Compare commits

..

476 Commits

Author SHA1 Message Date
Oleg Belousov b048b8f4f1 Feature/sliding freq (#3280)
* Add sliding-frequency audio receiver tuning
* Move ADS-B receiver to external app
* Move AIS receiver to external app
* Move APRS receiver to external app
* Move APRS transmitter to external app
* Isolate filtered spectrum collection from WFM
* Fixed the issues #3280
* fpga_bridge.c: init registers per reference fpga_init, add quarter-shift mode setter
* radio.hpp: expose cached FPGA quarter-rate shift
* radio.cpp: program FPGA quarter-rate shift together with tuning offset
* tuning.hpp: pass AFE rate and direction to tuning config, add quarter_shift field
* tuning.cpp: import full PRALINE RX/TX tuning tables with quarter-shift offsets
* clock_manager: stop writing bogus RX digital gain, keep quarter shift across rate changes
* receiver_model: port LPF bandwidth from reference auto_bandwidth, use cached quarter shift
* adsb_rx: enable RF amp by default on first run
* ui_geomap.hpp: add hemisphere fields, degrees become magnitude
* ui_geomap: use hemisphere selector for lat/lon sign, fix minute/second wrap carry
* ui_menu: guard select against empty menu
* waterfall_designer: header updates for profile file handling
* waterfall_designer: exception-free profile parsing, CRLF handling, deferred nav callbacks, backup cleanup
* external.ld: scope app section globs to their own object directories
* CMakeLists: relink when external.ld changes
* tools: add external app symbol placement checker
* tools: add guru meditation address lookup script
* tools: add per-function stack usage report script
Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-08-08 21:47:54 +02:00
MsfPablo 9067e007b6 Fix GeoPos losing the sign of coordinates between 0 and -1 (#3283)
GeoPos stored a coordinate's sign only in its degrees NumberField, which
is an int32_t and therefore has no negative zero. Any coordinate whose
integer degrees part is 0 but which is negative (i.e. strictly between
0.0 and -1.0) could not be represented at all:

  - set_lat()/set_lon() passed the raw float to the degrees field, so
    -0.2933 truncated to 0 and the sign was gone before it reached the
    widget.
  - lat()/lon() then decided the sign with `field_lon_degrees.value() < 0`,
    which is false for 0, so the value came back positive.

The reporter's airport is at longitude -0.2933, which was impossible to
enter in the ADSB Tx app.

Give each coordinate an explicit hemisphere field (N/S and E/W) and make
the degrees field an unsigned magnitude:

  - The hemisphere OptionsField is the single source of the sign, so
    "negative with zero degrees" is now representable.
  - set_lat()/set_lon() derive the hemisphere from the sign of the input
    and feed the fields the magnitude.
  - lat()/lon() read the hemisphere instead of inferring the sign from
    the degrees value.
  - The minutes on_wrap handlers no longer need to flip the carry
    direction based on the degrees sign, since degrees is now a
    magnitude; the carry is the same in both hemispheres.
  - The degrees fields no longer loop, so carrying below 0 clamps at 0
    instead of wrapping round to 90/180.

The hemisphere indicator occupies the column that the 4-wide signed
degrees field used for its minus sign, so the row layout, the degree
symbol and the decimal readout all stay where they were.

Fixes #3234
2026-08-08 21:18:18 +02:00
Copilot 367eaf54c0 Fix GeoPos DMS edge carry rollback at clamped degree limits (#3284)
* Initial plan

* Fix geomap DMS wrap carry at clamped bounds

Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
2026-08-08 00:40:35 +02:00
qwer123 de30cbfe1b fix ADSB RX weak signal on pro (#3282) 2026-08-05 10:09:55 +02:00
未来方舟 a4d91f12ef Fix waterfall designer crash and also some linker script fixes (#3281)
* init

* _
2026-08-01 12:57:45 +02:00
gullradriel c4f32ac436 update submodule (#3277) 2026-07-25 18:54:33 +02:00
Frederic BORRY efe214167d Added sn0ren's SPECTRUM BMP files (#3276) 2026-07-24 18:37:55 +08:00
Copilot 483a100100 Fix ADSB altitude integer overflow and add on-ground indicator (#3275)
* Initial plan

* Fix ADSB altitude integer overflow and add on-ground indicator

- Fix display overflow: use to_string_dec_int (signed) instead of
  to_string_dec_uint (unsigned) for altitude display - per ICAO Annex 10
  Vol IV 3.1.2.6.5.4, altitude = 25N-1000 can be as low as -1000ft
- Remove incorrect altitude clamp (altitude < 0 => 0) in DF 0/4/20 decoder
- Add on_ground flag to AircraftRecentEntry; set from TC 5-8 (surface
  position) messages, cleared on TC 9-22 (airborne position) messages
- Display 'GND' in altitude column when aircraft is on ground
- Clear altitude to 0 when on_ground is set to prevent stale data in map
  color calculation

Closes #3274

* Revert on-ground indicator; keep only the overflow and clamp fixes

Remove on_ground field, TC 5-8 handling, and "GND" display per review
feedback. Only keep the two minimal bug fixes:
- Signed altitude display (to_string_dec_int vs to_string_dec_uint)
- Remove erroneous altitude < 0 clamp; update comment to show the math
2026-07-23 01:16:21 +02:00
Pezsma 8561ff32b3 Gpio modify v4 (#3266)
* Refactor GPIO handling for RFFC5072: update pin mappings and simplify initialization

* Refactor GPIO handling: replace portapack::gpio_dfu with dfu_button and clean up unused GPIOs

* Refactor MAX283x GPIO handling: update pin mappings and simplify initialization

* Refactor GPIO handling: update MAX2831 shutdown pin configuration

* Refactor GPIO handling: add MAX2831 RXHP and RXTX control, update related configurations

* Refactor MAX2831 frequency setting to include reference divider parameter and improve validation checks

* Refactor MAX2831 frequency setting to remove reference divider parameter and improve frequency validation

* copilot
2026-07-10 21:37:41 +02:00
gullradriel 87eb8e5863 update submodule (#3267) 2026-07-09 23:11:39 +02:00
Frederic BORRY d11669f711 EPIRB TX SGB support (#3263)
* Update BEACONS.TXT
Added test beacon with 3 bch1 errors and 2 bch2 errors to test multiple bit error correction.
* Fist step to SGB format
* Added SGB to manual mode.
* SGB support
- Added SGB frame to beacons file
- Fixed hex display for SGB
- Fixed frame type option display
- Fixed self-test mode activation logic
- Fixed PRN for self-test mode
2026-07-09 10:36:26 +02:00
Totoo 7513cd46fc Pause on the looking glass (#3261) 2026-07-09 09:43:45 +02:00
Totoo d671455f79 Added tetra rx ext app (#3257) 2026-07-09 09:10:08 +02:00
Totoo 42122a739c remove const temp measurement from core to the ext app. we don't need it (#3259) 2026-07-08 10:09:53 +08:00
未来方舟 03fe508aae fix looking glass frequency cast error (#3253)
* fix looking glass frequency cast error

* add useless protecter back
2026-07-08 10:08:24 +08:00
未来方舟 e3eafbeb66 Add benchmark function for mbt (#3255) 2026-07-07 11:32:00 +02:00
未来方舟 e3b3fa2fc8 fine tune app search ui (#3256) 2026-07-07 11:06:31 +02:00
未来方舟 5f0ae22f66 remove my dumb nicknames in files (#3254) 2026-07-07 11:04:34 +02:00
Totoo f9dd28f31d fix dfu button (#3252) 2026-07-07 00:31:44 +02:00
Matej Sochan b99cec5111 Feature/signal hunter (#3245) 2026-07-06 11:05:49 +08:00
Pezsma fe2ba80a10 refactor: simplify audio volume handling in AudioVolumeField (#3246) 2026-07-04 08:53:28 +02:00
Luca b5d84fcc4d Airband VOR receiver and trasmitter (#3244)
* First test

* Reorder includes for consistency and fix string dereference in VorCdiIndicator

* Add memory section and linker script entry for vor_rx application

* Testing VOR TX

* Moving VOR TX to SD

* Code refactoring to align with guidelines

* Add TODO comment to verify radial sign convention in vor_tx_config

* Remove unnecessary set_dirty() calls in VorRxView methods

* Update Course Deviation Indicator label and adjust UI element positions

* Update VorTxView to run prepared image from memory map

* Update build condition in nightly release workflow to include workflow_dispatch event

* Reverting action changes

* Adjust Course Deviation Indicator layout and refine painting logic

* Refactor VorRxView UI elements for improved layout and clarity

* Add calibration field and update radial calculation logic in VorRxView

* Enhance VOR processing: add renormalization to phase oscillator, improve signal handling, and update configuration parameters

* Implement radial smoothing and TO/FROM state handling in VorRxView; update VOR processing logic

* Setting comments inline

* Refactor VorCdiIndicator and VorRxView: change normalize_signed_degrees to use int32_t, update radial smoothing to use fixed point arithmetic to reduce flash usage

* Update external app address end to 0xAE0B0000

* Update set_vor_tx_config to include enabled parameter for consistency

* Refactor VorRx to omit decim_2 stage and update comments for clarity on VOR processing

* Add RSSI, Channel, and Audio fields to VorRxView to mimic existing apps

* Refactor VorRxView labels and status messages

* Update VorRxView calibration field range to allow full circle input

* Completing Ident transmission logic

* Fixing TX gain and moving log label

* Refactor VOR phase calculations and update radial offset handling for accurate bearing representation

* Add low-pass filters to strip 9960 Hz subcarrier from audio output
2026-07-04 08:49:21 +02:00
Sandbox 7481aefe9f customizable name attack option "CUSTOM" for the BLESPAM App (#3239) 2026-07-02 16:14:17 +02:00
未来方舟 6b641c8a5d You can search app now (#3241) 2026-07-02 10:19:53 +02:00
Pezsma 2ad34bbc09 Gpio modify v3 (#3238)
* Refactor GPIO mappings and definitions for improved clarity and functionality

- Updated SCU_ARRAY_SIZE definitions in pal_lld.h for PRALINE and non-PRALINE configurations.
- Modified pin mappings in gpio.hpp to reflect new hardware configurations, including additional control pins for RF and audio components.
- Removed obsolete GPIO definitions from hackrf_gpio.hpp and streamlined the code for better maintainability.
- Added new GPIO definitions for mix bypass, amplifier control, and other RF-related functionalities to enhance the system's capabilities.

* Refactor GPIO mappings and update SCU_ARRAY_SIZE for LPC43xx platform

- Updated SCU_ARRAY_SIZE to 80 for PRALINE configuration and 58 for others.
- Added new GPIO mappings for auxiliary power control, antenna bias, and R9 clock enable signals in gpio.hpp.
- Removed commented-out PPS output mapping in hackrf_gpio.hpp.
- Adjusted GPIO definitions for R9 clock signals to ensure proper functionality.

* Refactor RF path initialization and configuration for improved clarity and functionality

* Refactor RF path configuration and GPIO mappings for improved clarity and functionality

* Potential fix for pull request finding

* comment

* aux power polarrity

* Fix formatting of pin_aux_power_enable declaration
2026-07-02 10:05:34 +02:00
Totoo 9c74a1f392 fix mbt build on rare errors (#3242) 2026-07-01 14:42:25 +02:00
gullradriel abadf35e06 Lna investigations and fix (#3240)
* The LNA gain field 'L' lives in LPF_VGA_2 (reg 6), not RXRF_2 (reg 2). RXRF_2 only holds LNAgain_SPI_EN. The configure_rx_gain() function wrote 'L' into lpf_vga_2 but marked RXRF_2 dirty => the LNA value only got flushed incidentally because the following VGA write marks LPF_VGA_2 dirty. Drop the stray RXRF_2 dirty flag and rely on the LPF_VGA_2 flag, which carries both L and VGA.

* The baseband build defined IS_H1_R9 as the compile-time constant 0, on the (now incorrect) assumption that detected_platform() isn't linked. The sd_over_usb image is the only baseband target that compiles the IS_H1_R9-gated upstream files (rf_path.c, si5351c.c, sgpio.c, platform_gpio.c, platform_scu.c), and it *does* link platform_detect.c and call detect_hardware_platform() at startup. Forcing IS_H1_R9=0 dead-stripped every 'if (IS_H1_R9)' branch, so a real HackRF One r9 was set up with the OG clock/GPIO/RF-path config in that image.
Define IS_H1_R9 as the runtime '(detected_platform()==BOARD_ID_HACKRF1_R9)', mirroring upstream platform-detect.cmake for HACKRF_ONE. All usages are '#ifdef IS_H1_R9' + 'if (IS_H1_R9)' (no arithmetic '#if'), so a function-call expression is safe, single-quoted -D with no internal spaces keeps it intact through the shell-split DDEFS blob.

---------

Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-07-01 08:47:07 +02:00
Totoo 288e87537a MAX 17055 sleep bug fixes. 2026-06-25 09:08:44 +02:00
gullradriel d25899de10 Upgrade subm plus warning fix (#3235) 2026-06-25 08:46:36 +02:00
Totoo 8a16ec829c fix clock source change on praline (#3233)
* fix clock source change on praline

* whoops
2026-06-22 16:50:16 +02:00
Totoo f6d6e3be4c Fix battery display bug, and the flash time (#3232)
* battfix

* por again, but nicer
2026-06-22 16:20:49 +02:00
Pezsma c16bac24c7 Add RSSI input configuration for VGA output measurement (#3231) 2026-06-21 11:17:09 +02:00
Payalo64 4e6a2cc934 Add 3.3GHz Analog FPV Channels to FPV_ANALOG.TXT (#3230) 2026-06-20 18:28:02 +02:00
Pezsma cafe62564e Gpio modify (#3229)
* Refactor GPIO configuration for PRALINE and non-PRALINE setups

- Updated PinMap structure to include gpio_mode for better flexibility.
- Added new GPIO mappings for SGPIO pins with appropriate configurations.
- Commented out unused GPIO definitions in hackrf_gpio.hpp to improve code clarity.
- Adjusted GPIO initialization for control pins to utilize the new PinMap structure.
- Ensured compatibility for both PRALINE and non-PRALINE configurations by using preprocessor directives.

* Refactor GPIO handling and remove LED abstraction

- Updated GPIO class to support logical polarity, enabling/disabling features based on their configured state.
- Replaced direct GPIO manipulation in power control functions with new GPIO methods for better readability and maintainability.
- Removed the LED class and its associated functionality, as it was deemed unnecessary for the current implementation.
- Adjusted GPIO initialization for various components, ensuring correct polarity settings for VAA and power enable pins.
- Cleaned up unused includes and commented-out code in hackrf_gpio.hpp.

* Refactor GPIO LED control methods to use setActive() and setInactive() for improved clarity

* Refactor GPIO control methods to use setActive() and setInactive() for improved clarity and consistency

* copilot

* Update GPIO control logic and pin definitions for clarity and consistency

* Refactor GPIO methods for improved naming consistency and clarity
2026-06-19 07:48:55 +02:00
gullradriel 960e5b8329 Upgrade subm plus warning fix (#3227)
* fix compilation warning
* update submodule
2026-06-17 11:34:00 +02:00
Totoo f5a5e8e5e8 Map improv. (#3226) 2026-06-16 17:54:32 +02:00
Synray d8a1e2530a Add Security+ 2.0 TX App (#3217)
* Add U64 BoundSetting
* Add secplus-tx
* Add secplus license
* Update icon
* UI update
* Add filesystem::path BoundSetting
* Add file UI
* Rename file
* Add save button
* Fix warnings, add integer conversions to UI macros
* UI update
* Copilot suggestions, name edit UI
* Fix buffer length
* Style update
* Avoid recursion in Widget::style()
* Apply suggestions from code review
* Move secplustx.cpp to external
* Shorten title
* Space optimizations, remove FilePath setting
* Remove assertions
* Change namespace
* Change title to match
2026-06-15 19:28:47 +02:00
Totoo b84a424875 MBT update 2026-06-13 09:50:25 +08:00
gullradriel 62d0e598d4 Update submodule (#3222)
* update submodule to latest GSG modifications
* build: fix delay function
* update submodule to include praline fix
* fix dep to rebuild/reinclude hackrf_usb_ram.bin
2026-06-12 22:15:44 +02:00
Synray a93f2ff583 Remove sine table lookup from proc_ook (#3201) 2026-06-12 16:26:13 +02:00
Totoo e2586ceb22 proper fix for flasher (#3223) 2026-06-12 16:25:43 +02:00
未来方舟 5a2b0cdc2a build tool and agent.md (#2993)
* init

* note

* detect arch

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix typo

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update mbt

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* protect rm -rf

* remove wrong ai code

* Update mbt

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* add agent file

* remove empty file

* agent.md rewrite

* agent.md rewrite

* fix on ubuntu

* err check

* add make

* add python3-yaml

* add binutils

* fix the build order to avoid some compile bugs

* mg

* remove trash

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
Co-authored-by: HTotoo <ttotoo@gmail.com>
2026-06-12 16:52:13 +08:00
Totoo f852900cd0 Epirbrx sizefix (#3221) 2026-06-11 17:18:05 +02:00
Pezsma 9ab367f2e0 Add praline gpio (#3218)
* Implement antenna bias control for Praline and update GPIO configurations
* Add GPIO power management functions and update board initialization for Praline
* Refactor power management functions for Praline and update GPIO configurations
* Update GPIO configurations for Praline and enable power control pins. AA_EN enabled
* Refactor GPIO configuration for Praline: update array size definitions and clean up comments
* Add P1 and P2 control functions for PRALINE
* Introduced P1_Function and P2_Function enums in ClockManager to manage multiplexer control.
* mplemented set_p1_control and set_p2_control methods for configuring P1 and P2 control pins based on specified functions.
* Updated clock_manager.hpp and clock_manager.cpp to include new functionality.
* Modified MAX2837 and MAX2839 initialization to conditionally configure GPIO based on PRALINE.
* Adjusted RFFC507x and RFFC507x_SPI initialization to include GPIO setup for PRALINE.
- Refactored RF path initialization to remove unnecessary GPIO setup.
- Updated board configuration for PRALINE to reflect new GPIO settings and pin configurations.
- Cleaned up FPGA bridge code by removing unused pin configuration functions.
- Adjusted SCU array size in pal_lld.h for PRALINE.
- Enhanced hackrf_gpio.hpp to define multiplexer control pins for PRALINE.
- Modified LED setup to accommodate active-low configuration for PRALINE.

* Refactor GPIO and power control functions

- Removed unused power control function declarations from board.h.
- Enhanced gpio.hpp with new pin setup functions and GPIO control structures for PRALINE.
- Consolidated multiplexer control pin definitions into gpio_control namespace.
- Moved power control function implementations to gpio.cpp, including detailed VAA power management logic.
- Updated power control functions to handle both PRALINE and HackRF R9 configurations.
- Cleaned up hackrf_gpio.hpp by removing redundant multiplexer control definitions.

* Refactor GPIO control for PRALINE: update MAX2831 and RFFC507x configurations, add new GPIO mappings

* Refactor GPIO configurations for PRALINE: update anti-aliasing filter pin mapping, enhance LED setup logic, and add placeholder GPIO entries.

* Refactor GPIO configurations for PRALINE: update LED mappings and remove unused anti-aliasing filter GPIO entry.
2026-06-11 13:24:42 +02:00
Arne Luehrs c7a0846645 EPIRB RX: add receiver-overload indicator on channel bar (#3212)
* EPIRB RX: add receiver-overload indicator on channel bar

Tint the channel-power bar red when the front-end overloads (ADC near
full scale). EPIRB demodulation relies on the constant-envelope carrier
and the +/-1.1 rad biphase phase jumps; ADC clipping distorts both, so a
visible cue to reduce RF-amp/LNA/VGA gain is useful.

Implemented in the shared ui::Channel widget via an opt-in
set_overload_threshold(): when the last ChannelStatistics max_db reaches
the threshold the bar is drawn red instead of blue. Default threshold is
above the 0 dBFS ceiling, so all existing users are unaffected. The
EPIRB view enables it at -3 dBFS.

Done in the widget (main firmware) rather than the EPIRB app because the
epirb_rx external app is already at the 32 KB flash limit; the app only
pays for a single setter call.
2026-06-11 11:10:23 +02:00
Arne Luehrs 569bcaad22 Epirb afc wide capture (#3213)
* EPIRB RX: widen AFC capture range to +/-5 kHz

The AFC estimate was only applied after carrier lock, so carrier
acquisition ran on raw phase deltas and could only tolerate offsets of
a few hundred Hz before the 0.6/0.7/1.6 rad detection thresholds (on
the 12-sample accumulator) broke down.

Track the carrier offset continuously in the IDLE state with a
first-order loop (AFC_TRACK_ALPHA), so the de-biased accumulator
self-centers for any offset within the discriminator Nyquist (~24 kHz)
*before* the thresholds run. ALPHA = 0.005 pulls a +/-5 kHz offset
under the 0.6 rad lock threshold in ~11 ms, well inside the 160 ms
preamble / 80 ms stability window. The IDLE rise-detect threshold is
also made symmetric (fabsf) now that the bias is removed.

Also add the missing <cstdint> include to test_convert.cpp so the
application_test suite compiles under the current toolchain.

Verified: baseband_epirb_rx.elf builds (flash 53%, RAM 12%) and
baseband_test passes.
2026-06-11 11:01:54 +02:00
Totoo 6b5955e0b5 Enhance battery detection and configuration handling (#3197)
* indicate batt changed event
* added sleep config, and use it.
* reinit if new batt detected.
* more detect
* repl batt setting
* disabled some things
* copilot fixes
2026-06-11 10:52:20 +02:00
Synray 9042d1c433 Use UI_ macros in looking glass (#3208) 2026-06-09 20:46:35 +02:00
未来方舟 378b2808ba Looking Glass frequency list now supports muiti file loads (#3215) 2026-06-08 15:22:29 +02:00
Erwin Ried 5b363b79aa Update links and images in README.md 2026-05-31 22:56:20 +02:00
Brumi-2021 0d51787099 Clean_gcc_14_2_1_compiler_errors (#3206)
* Clean_gcc_14_2_1_compiler_errors

* format issues
2026-05-31 17:34:30 +02:00
gullradriel 8f5b25b10d PRALINE Tx and clock fix (Pr 3192 takeover (#3198) )
* fix clock init and add mayhem tx capacity
* aligned external clock and RX config
* fix(clock): cap TX interpolation to x16 to prevent doubled waveform speed

  The TX gateware only implements interpolation factors x1..x16 (tx_intrp
  0..4), but the rate search could select n==5 (x32) for very low TX
  sample rates. That left the AFE/CLK0/CLK1 running at the x32 rate while
  the FPGA only interpolated by x16, clocking the TX datapath at twice the
  gateware output rate. Cap afe_rate and _resampling_n to the x16 limit in
  TX so the clocks stay aligned with the interpolation setting.

* fix(praline): source PLL1 from 40MHz GP_CLKIN instead of 12MHz XTAL

  For the intermediate 90-110MHz PLL1 stepping stage, source PLL1 from
  GP_CLKIN (Si5351 CLK2/MCU_CLK at 40MHz) giving N=2, M=10, Fclk=100MHz,
  rather than the on-chip 12MHz XTAL bootstrap path. This matches the
  non-PRALINE path. Remove the dead commented-out XTAL enable/PLL code.

* docs(fpga_bridge): correct register 0x03 description for PRALINE gateware

  Register 0x03 is RX_DIGITAL_GAIN in RX and TX_NCO_CTRL in TX, not
  RX_PSTEP.

* Fix fpga_tx_set_nco_enable to use FPGA_REG3_TX_NCO_CTRL (0x03)
2026-05-31 12:44:40 +02:00
Synray 4e56d0f420 Add RSSI toggle button to looking glass (#3204) 2026-05-31 11:24:24 +02:00
gullradriel 2bb3b415d1 update submodule (#3199) 2026-05-30 20:31:01 +02:00
Pezsma 6e0394fb6b Filename scroll (#3193) 2026-05-30 08:24:20 +02:00
Totoo da21ea7866 Fix flashing (#3188)
Fixed some bugs
2026-05-30 01:49:49 +02:00
未来方舟 2c434946cd Display if IQ inverted in DFU overlay menu while the algo decides to (#3196)
* init

* expose more info

* copilot suggest
2026-05-29 12:06:17 +02:00
gullradriel f3aee44e3b update submodule (#3187)
* update submodule
* fix iwyu CI errors
* fix sd over usb with hackrf submodule si5351c refactored functions
2026-05-21 17:48:53 +02:00
未来方舟 165c190d92 Use html comments to make som text only visible by PR creator (#3186)
* Use html comments to make som text only visible by PR creator

* reformat a bit
2026-05-21 15:51:56 +02:00
Totoo 8f9246c1ea Enhance transmitter model and UI for better handling of TX and RF ampstates (#3185) 2026-05-21 15:50:47 +02:00
Pezsma f1d52ae78f expanding channels (#3183) 2026-05-21 15:50:10 +02:00
qwer123 865bdee9b3 add external clock detect method (#3180) 2026-05-20 22:40:39 +02:00
Totoo 314e910cbc make battleship smaller (#3181) 2026-05-20 18:39:21 +02:00
Pezsma ec6c7eff86 Hard reset app (#3182) 2026-05-20 18:38:13 +02:00
Pezsma 95b2e9fbc3 Charge sleep bug (#3177) 2026-05-15 09:24:30 +00:00
未来方舟 52ff442fcc Tune pull_request_template a bit (#3176) 2026-05-14 00:48:27 +08:00
gullradriel 95d246a020 Update hackrf submodule (#3174)
* updated submodule

* usb_serial: populate usb_device before usb_run. Upstream now leaves the global usb_device zero-initialized and expects hackrf_usb.c to memcpy a per-board const into it. We don't link hackrf_usb.c, so do the memcpy here. Also flip control_in to enable_zlp=true to match the new endpoint API.

* baseband: derive IS_* board defines from BOARD. Upstream moved platform IS_* macros from platform_detect.h to cmake. Set them per BOARD here since we don't include platform-detect.cmake. Drop the duplicate clkin.c entry.

* sd_over_usb: drop duplicate driver objects, fix setup callback. Upstream now defines i2c0/i2c1, spi_bus_ssp0/1, sgpio_config and rf_path in the common driver files, so stop redefining them here and patch the runtime fields in pin_setup() instead. Update si5351c_reset_pll() call to the two-arg form. Replace the no-op transceiver_usb_setup_complete stub with one that delegates to usb_setup_complete.

* sd_over_usb: populate usb_device before usb_run. Same fix as the application side: memcpy a const template into usb_device before usb_device_init/usb_run, otherwise the descriptor pointer is NULL. Also pass enable_zlp=true on control_in to match the new endpoint API.

* fix unicode in comment

* remove lz4 bufs reservation in M0
2026-05-13 14:53:54 +02:00
Frederic BORRY c34a6940ca EPIRB RX app rework + EPIRB TX app fixes (#3172)
* First step to EPIRB rx app fix
* Added spike filtering on carrier detection for more robustness.
* Created dedicated packet builder
* First step to fixing UI.
* First step to epirb-rx ui rework.
* Made static methods static members
* Code optim
* Code cleanup + first step to new UI
* Fixed display
* Added BeaconList widget + BeaconDB
* Epirb TX app memory optim
* Fixed OOM when opening Map Display or Locator editor on EPIRB TX app
* First step to tabs + code cleanup to free some space...
* Added spectrum analyzer + fixed display
* Fixed display refresh issue after leaving spectrum.
* More code optim + first step to detail view.
* Fixed float coord display.
* Fixed protocol names
* Fixed location display
* Externalized country base to sd card + more code optim
* Fixed country display
* Fixed path
* Added protocol description and resource manager
* Added main/aux loc device
* Removed specan to save space
* First step to adding QR code
* Used a custom TextArea component instead of Console
* Fixed QR code display for map URL
* Added beacon selection management + fixes
* First step to map/data qr.
* QR tab : added data + detail/map toggle
* Removed sqhelch to save space
* Removed audio to save space
* Removed freq label to save space
* Removed packet timestamp to save space
* Moved frequency values to resource file
* Fixed detection parameters to suite real beacons (slower phase shift time).
* Fixed countdown field size
* Fixed frequency list.
* Fixed detail view on select.
* First step to fix settings.
* Fixed squelch for a 0-99 range.
* Added bch code correction (single bit error).
* More code optim, disable country cache, removed inline from location to reduce code size.
* Removed inline, code formatting.
* Added bch code correction display.
* Fixed focus bug, fixed packet size check. Added BCH1/BCH2 correction test cases in BEACONS.TXT file for EPIRB TX application.
* Fixed merge
* Added countdown setting.
* Added selection display to beacon list
* Restored squelch control. Added countdown reset.
* Code optim on formatSummary
* More code optim
* Made ResourceManager methods static
* Externalized beacon strings to res file
*Removed frame parameter from methods
* Added test beacons for emergency. Removed old res file.
* Fixed resource manager
* Fixed resources. Added emergency display.
* BCH + res fixes
* Fixed beacons
* Fixed app color. Fixed HexId and Serial calculation.
* Fixed frequency list. Comments.
* Code cleanup and comments
* Added slideshow mode to EPIRB TX application. Code comments / cleanup.
* Fixed frame end after wrong code cleanup
* More code optim and comments. Removed touch from beacon list.
* Added beacon selection with encoder on detail view.
* Added beacon paging in header. Fixed timeout display
* Fixed build for PRALINE
* Fixed formatting
* Potential fix for pull request finding
* stop on carriage return
* empty string if unknown
* zeroing data buffer before use
* fix typo
* add include
* fix case if pair == 0
* fix scpectrum_on to spectrum_on
* fix typos
* replaced 3 inlined offset blocks with apply_offset calls
* set_beacon consolidation
2026-05-12 14:00:47 +02:00
Synray 33330e4c34 Use gradient for level view, fix height scaling (#3173)
* Use gradient for level view, fix height scaling
* Add RSSI indicators
2026-05-12 12:48:17 +02:00
Pezsma 66f1835232 Wait tx complete (#3165)
* Implement radio transmission drain mechanism and update shared memory structure
* don't wait for timeout if baseband already in shut down state
2026-05-12 12:45:02 +02:00
Synray 4dfbf666d0 Add more waterfalls (#3170) 2026-05-10 09:37:08 +02:00
Synray 245a36459f UI and bug fixes (#3164)
* Fix BLE RX RSSI and Channel scale

* Properly reset gradient index 0

* Use move constructor in MenuView::add_item

* Avoid erasing during iteration

* Fix gradient rounding. Extend to end of table

* Add comment to add_item
2026-05-10 00:08:05 +02:00
janhaverkamp 590f19062b Add support for larger battery capacities (#3168) 2026-05-09 20:54:25 +02:00
Totoo f38ef4ebc8 Added CU8 playback support (#3166) 2026-05-09 12:23:43 +02:00
gullradriel 900d2f2482 fix missing header (#3167) 2026-05-09 12:23:10 +02:00
Totoo 5aff5ac277 Fix text color multiline handling (#3161) 2026-05-08 09:57:06 +02:00
Totoo cf4d245964 Fixes #3159 (#3160) 2026-05-07 23:54:30 +08:00
Pezsma c7f7173958 sleep warning fix (#3162) 2026-05-07 15:26:34 +02:00
Totoo 02c0f67a8d fix menu handling again (#3157) 2026-05-04 20:59:48 +02:00
Totoo 2bacbc80a8 Memory management improv. 2026-04-30 15:00:19 +02:00
Pezsma ab986a0378 Change charge display off to sleep (#3153) 2026-04-29 19:57:17 +02:00
Totoo c9f310f548 Add power off command to I2cDev_PPmod class (#3152) 2026-04-23 13:18:56 +02:00
Synray 1dafdd5a26 Increase text length to avoid cutoff (#3150) 2026-04-20 15:47:59 +08:00
VasylSamoilov fc5beb0c09 FLEX RX: fix long-address numeric decode, improve short message format (#3136)
- Fix long-address numeric message decoding: read body[0] from Vy (j+1)
  for multi-word messages, from w1 for single-word. Fix MF word count
  for long addresses (n_field words, not n_field+1).
- Fix long-address short message: read 5 additional BCD digits from Vy
  for 8-digit decode.
- Fix BCD table: index 10 is '.' (dot) not ' ' (space) in both RX and TX.
- Clean up address decode: identify address type by value range per
  Table 3.8.1-1. Remove is_group/is_temp_group from packet structs.
- Rename SMSG to SHORT. Format message payload directly in baseband:
  TONE, NUM abc, SRC N, SRC N N=M R=R, RESERVED hex.
- Add BIW1 heartbeat packet (biw_field=0xFF) on every decoded frame.
- Move timezone table to file scope, simplify console log_message,
  add No signal initial status, simplify serial BIW output format.
2026-04-18 21:15:05 +02:00
gullradriel 3f10663bc9 Pull request guidelines (#3149)
* update PR template
* wiki mention
* added trusted contributors guidelines, compile proof simplification
2026-04-18 21:13:30 +02:00
VasylSamoilov 396f8aef41 POCSAG: show '?' for characters from uncorrectable codewords (#3138)
Use per-character error tracking to substitute '?' when a
character's bits came from an uncorrectable codeword (BCH error_count >= 3). 
Previously these displayed as garbage.
2026-04-18 18:56:33 +02:00
VasylSamoilov c095a88cb3 FLEX TX: fix stop button not stopping transmission (fixes #3135) (#3137)
Reset tx_phase_ to 0 on stop so on_tx_progress does not restart
the next phase. Reset progress bar and repaint capcode info.
2026-04-13 15:57:31 +08:00
Totoo 900c80549f externalize basebands, not needed, and remove too high level function (#3134)
* externalize basebands, not needed, and remove too high level function

* fix ert
2026-04-10 21:08:46 +02:00
VasylSamoilov 4dce613172 FLEX TX: pager transmitter with long address support (1600/2FSK) (#3132)
External app for encoding and transmitting FLEX pager frames at
1600bps/2FSK. Supports short (1-1,933,312) and long (2,101,249-
4,297,068,542) addresses across all encoding sets (1-2, 1-3/1-4, 2-3).

Message types:
- Alphanumeric (vector type 5): 7-bit chars, signature, K checksum
- Standard Numeric (vector type 3): 4-bit BCD, K checksum
- Short/tone-only (vector type 2): space-filled BCD in vector word
- Short numeric (vector type 2): BCD digits in vector word (3/8 digits)

Frame structure:
- FIW (Frame Information Word): cycle, frame, roaming flag
- BIW (Block Information Word) support with up to 3 extra words:
  Date, Time, SysInfo/timezone+DST, SSID1 (Simulcast System ID:
  Local ID + Coverage Zone), SSID2 (Country Code)
- BCH(31,21) encoding with bit reversal, block interleaving
- Two frames per TX: message R=1 (new) then R=0 (duplicate)
- Message number N (0-63): random seed from RTC, auto-increments
- ERS (Emergency Re-Sync) burst: configurable N x 42-cycle bursts
  with 8-cycle preamble per frame

UI:
- Capcode info line: address type, computed frame/phase (reference)
- TX status: step counter with ERS/frame progress
- Set params view: Date/Time/TZ/DST, LocID/CovZone, CountryCode,
  Roaming, message number, ERS count
- Date/time auto-populated from RTC with calendar-equivalent year
  mapping for years beyond 2025 (per ARIB STD-43A)
- Year validation with equiv-year popup for out-of-range values
- Capcode and BIW flags persisted via SettingsManager

Serial interface:
- sendflex <capcode> <type> <msglen> + payload
- Types: 0=alpha, 1=numeric, 2=short/tone, 3=short numeric
- FlexTosendMessage (ID 103) for serial-to-app communication
- App must be open to receive serial commands
2026-04-10 11:17:52 +02:00
VasylSamoilov cf44076a34 FLEX RX: comprehensive decoder rewrite (#3131)
Baseband (proc_flex.cpp):
- Fix numeric vector field width (3-bit n+1 for types 3,4,7)
- Fix capcode range classification for long address components
- Add long address decode (Set 1-2, 1-3/1-4, 2-3)
- Add vector checksum validation (4-bit nibble sum)
- Add tone-only boundary detection via vector pre-scan
- Add idle phase detection (skip all-zeros/all-ones before BCH)
- Skip spurious TON for capcode 1 (BCH-corrected idle artifact)
- Add BIW parsing: SSID1/SSID2, DATE, TIME, TZ, SYSMSG, CHAN
- Add alpha fragment flags: frag, more_frag, seq, new, maildrop, sig
- Add secure message enc= field (alpha/binary/separate)
- Add numbered numeric flags: seq, new, fmt
- Add HEX/Binary header decode: block_bits, new, maildrop, rtl
- Add short instruction decode: temp group slot/target, sys events
- Add short message (SMSG) decode: tone/numeric/source/numbered
- Add group/temp-group/priority flags per address
- Add S2 C-pattern detection with ±1 symbol boundary correction
- Extract FIW roaming, repeat, traffic fields
- Replace snprintf with lightweight str_* helpers (no heap/_sbrk)
- Mark uncorrectable BCH words as ? instead of aborting phase
- ETX/NUL handling: trim trailing padding, show mid-message as ?

App (ui_flex_rx.cpp):
- Add status bar: cycle/frame, bitrate, polarity, time, timezone
- Add network info bar: LID, CZ, CC, roaming flag
- Pipe-delimited serial output for all message types
- BIW events to serial and status bar (not console)
- Human-readable console: +GRP for temp group, G/TG/P flags
- USB serial via UsbSerialAsyncmsg

Packet (flex_defs.hpp):
- uint64_t capcode (long address support)
- 256-byte message buffer
- Fragment flags, BIW raw values, address type, group/priority

Known limitation: phase label unreliable on multi-phase modes
(3200/2FSK, 6400/4FSK). Data always decodes correctly.
2026-04-10 08:37:38 +02:00
VasylSamoilov dd006b4830 POCSAG: decoder fixes, serial output, TX polarity cleanup (#3130)
RX: fix address/batch-boundary handling, add numeric continuation,
add heuristic type detection toggle, pass inverted polarity flag,
stream decoded messages over USB serial.

TX: rename phase to polarity (Standard/Inverted), fix function
validation (&&→||), fix serial shell payload read (offset tracking,
31-byte cap, preserve msglen).

Decoder: remove color escapes from output (plain text for all
consumers), guard count_alpha_fill against empty string, keep
numeric_len across continuation batches.

Serial format: POCSAG <baud> <addr> <func> <pol> <type> "<msg>" hex:<hex>
Quote escaping for embedded " and \\ in alpha messages.
2026-04-10 08:35:43 +02:00
qwer123 fc18992442 Add retry mechanism for volume write (#3121) 2026-04-10 08:33:48 +02:00
gullradriel ef2ae9874b fix build (#3129)
Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-04-08 08:28:44 +02:00
Pezsma 5deeaed5f0 Praline rssi (#3127) 2026-04-07 19:42:21 +02:00
Ryan Harden d5f94398ce Add external 2-Tone TX/RX Apps (#3128) 2026-04-07 19:32:41 +02:00
Totoo fba15c48e5 Battery capacity options added (#3125) 2026-04-04 09:19:17 +13:00
Totoo 888634ce35 praline without the much fixed ram (#3123) 2026-04-04 09:17:21 +13:00
Frederic BORRY 03131c8a0c EPIRB TX application update (#3120) 2026-04-03 11:35:46 +02:00
gullradriel 605c9efa0c Make detector use freqman (#3119)
* make detector list a freqman file
* add file loading ability, autoscan/pause, step in on pause on range, frequency value display
* Update firmware/application/external/detector_rx/ui_detector_rx.hpp
* Update firmware/application/external/detector_rx/ui_detector_rx.cpp
* add 750 kHz step
* revamp UI and add auto advance, auto scan, and only display 'RANGE SCAN...' when auto scan is on on ranges to avoid flicker of the hell.
* adjust as I can the ranges as they where before
* Update sdcard/FREQMAN/DETECTOR.TXT
* fix instance related copilot remark
2026-04-02 08:26:45 +02:00
gullradriel aee54e5b2d Merge updated submodule (#3116)
* update hackrf submodule
* add platform_gpio.c and platform_scu.c to baseband build
    -New source files required by the updated hackrf submodule.
* adapt scsi.c to updated hackrf API
    -Rename struct gpio_t to struct gpio and add gpio_lpc.h include.
* adapt hackrf_core.c to updated hackrf API
    - Rename struct gpio_t to struct gpio throughout
    - Add platform_gpio.h, platform_scu.h, and fixed_point.h includes
    - Update pin_setup() to use platform_scu() accessor instead of SCU_ macros
    - Rewrite sample_rate_set() to use new fixed-point frequency API
    - Update ssp_config_w25q80bv for changed struct layout
    - Update si5351c and max283x function call signatures
2026-03-30 23:47:39 +08:00
zxkmm e33e697cca remove some bluffing text from fpv detect app (#3114) 2026-03-29 17:24:29 +08:00
zxkmm d7d27d99e7 edit security policy (#3113) 2026-03-28 18:14:22 +01:00
Pezsma 78d3480b68 Morse tx update (#3112)
* loop message, progress bar
2026-03-27 18:29:20 +01:00
Totoo 70b42f4a56 fix audio mode change crash (#3111) 2026-03-26 22:40:27 +01:00
jLynx 5c719645d6 Update version.txt 2026-03-24 22:31:30 +13:00
jLynx b0eb776bdd Update past_version.txt 2026-03-24 22:31:18 +13:00
jLynx ce87161f9b Revert "Switched build to use ninja by default (#3072)" (#3102)
This reverts commit 22932e636d.
2026-03-20 12:16:14 +13:00
Totoo 8883f2decc fix sd test app mem usage (#3101) 2026-03-17 15:42:10 +01:00
Totoo e06d23ea3e app color 2026-03-17 15:41:31 +01:00
Totoo 0da90c7c5e modified app colors based on wiki (#3099) 2026-03-17 15:00:25 +01:00
Totoo e7572e5d41 transparent bg on map marker text (#3098) 2026-03-17 21:46:05 +08:00
Totoo 50f1e51d7e pocsag fix (#3097) 2026-03-17 21:35:37 +08:00
Sarah Rose 55a15958ec Add P25 Phase 1 TSCC TX external app (p25_tx) (#3090)
* Add P25 TX external app (UI shell)

- Register p25_tx in external.cmake and external.ld at 0xAE010000
- UI with NAC, SYSID, WACN, RFSSID, SITEID, TG, VCH fields
- 2.4 Msps TX, 155 MHz default, no baseband (m4_app_tag={0,0,0,0})

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Add P25 TX baseband processor (M4)

- P25TxConfigure = 102 in message.hpp + P25TxConfigureMessage
- image_tag_p25_tx in spi_image.hpp
- set_p25tx_data() in baseband_api (copies dibits to bb_data, sends msg)
- proc_p25_tx registered in baseband CMakeLists (tag P25T)
- m4_app_tag updated to {'P','2','5','T'}
- TX wired: transmits P25 FSW test pattern (512 dibits) via M4 C4FM proc
- on_tx_progress handler stops TX when frame complete

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* p25_tx: implement correct P25 TSBK encoding from ccemu

- BCH(63,16) via generator matrix (not polynomial division)
- CRC-CCITT bit-by-bit init=0 over 80 bits
- 1/2-rate trellis encoder (4 states)
- DataInterleave per TIA-102
- Status symbol insertion (SS=0x02 every 35 dibits)
- TSBK rotation: IDEN_UP -> NET_STS -> RFSS_STS -> NET_STS
- tsbk_idx_ persists across buffer refills, resets on stop_tx

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* p25_tx: apply clang-format

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* p25_tx: fix HackRF lockup - zero output buffer when idle

When configured=false, execute() returned without writing to the
output buffer. The DMA would re-use stale IQ samples from the
previous cycle, sending garbage to the HackRF and causing it to
lock up mid-transmission.

Fix: explicitly zero the output buffer when the proc is idle.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* p25_tx: address PR review feedback and fix TX restart lockup

- Replace NumberField with SymField::Type::Hex for NAC, SYSID, WACN,
  RFSSID, SITEID, TG, VCH (hex values should display as hex)
- Add voice grant TSBK (OpcodeGrpVChGrant 0x00) to rotation cycle;
  rotation is now 5-step: IDEN_UP->NET_STS->RFSS_STS->GRP_V_GRANT->NET_STS
- Wire TG and VCH fields into fill_tx_buffer for voice grant encoding
- Reset txprogress_message.done=false on new P25TxConfigure in baseband
- Add #include <cstring> to baseband_api.cpp (needed for memcpy)
- Fix TX restart lockup: remove baseband::shutdown() from stop_tx() so
  the M4 stays loaded between sessions; only shut down in destructor.
  Previously, the stale ShutdownMessage in the queue would be picked up
  by the restarted M4 on second TX, causing it to exit immediately and
  leaving the HackRF stuck in TX mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* ifndef guard instead of pragma
2026-03-17 08:41:01 +01:00
stafur 395e20d17b Next praline clean up (add SW: RSSI for hackrf pro praline) (#3091)
* Cleaned up #ifndef PRALINE and updated logic to being with #ifdef PRALINE entries where possible to make logic flow for PRALINE code execution pipeline clearer. Cleaned up compiletime warnings for PRALINE related codebase updates.

* Addressed comments provided by copilot during PR review. Combed through frequency definitions for consistency between PLL A and PLL B register definitions for CLKs 0-7. Ensured CLK3/LK6 <- SMA PORTs and CLK7 <- not utiliized are disabled during core development phase to support root cause analysis of any spectral artifacts. Updated MCU frequency to 40MHz to ensure audio harmonics are outside FM radio band range (< 80 MHz, >120MHz) and added comments clarifying choice of 40 over 10 MHz for potential future root cause analysis in other bands where audio may be expected as needed. Added CLK6 and CLK7 to Clocks Status View Debug display. Moved CLK defintions and PLL instantiations for components that are most RF sensitive to PLL A. Left others in PLL B. That is move FPGA CLK1 to PLL B, while moving CLK2, CLK4, and CLK5 to PLL A.

* Cleaned up PLL A and B XTAL reference checks relative to 800 MHz.

* Encapsulated HackRF Pro Praline debug and status vies into a single Pro Debug submenu as part of clean up.

* Fixed BLE RX Out of Memory error. Updated LPC43xx ld scripts to accouint for additional HackRF Pro praline memory.

* Addressed copilot comments for ble_rx_app by adding recent_entries_view.set_dirty. Updated ble_rx_app for easier use with heap limit set to one less than recent entries max limit.

* Addressed copilot comments by updating comment clarity in source files. Updated ui_debug to allow for return reference if set for PRO debug menu item.

* Improved readability of intialization parameters for the FPGA registers, and addressed 20Mhz nulls by initializing DC Notch width with standard setting, and DC Adaptiation rate with a balanced setting.

* Ran format-code.sh

* Added option to allow for user to set number if entries in recent list. Default is set to a relatively stable 32.

* Removed #ifdef PRALINE pragmas from ble_rx_app such that HackRF One can also use the updated UI widget to allow for user to set number of entries in recent list.

* Ran format-code.sh

* Improved RSSI for praline. Values are now being counted correctly by statistics methods after selecting ADC = 0 0 for RSSI. This was different than for the hackrf one which uses ADC = 1.

* Implemented SW RSSI calculation since HW RSSI not currently available in hackrf pro (praline) via fpga pass through. Calibrated RSSI power sensistivity to mid level signal intensitities.

* In preparation for WIP allocated full 128k to M4 limiting heap for M0 to 0k, and leabing it only with 64k from bank 0. That is, bank1 and bank2 are fully allocated to M4. Cleaned up #ifdef PRALINE conditionals such taht max2837/39 are in #else conditionals, as well as continuing to clean up hackrf_r9 booleans by placing them within #else conditionals.

* fix typo

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Addressed co-pilot comments. Clarified use of 25 recent samples in ble app for stability, udpated comments to reflect use of avg power over pwak pwer detection, improved safety of sample packing while relying on intrinstics for execute the implementation in a single machine instruction (PKHBT) for computational and memory efficiency.
2026-03-17 08:13:09 +01:00
zxkmm 9bf1f2175e fix bad color (#3096) 2026-03-17 08:10:07 +01:00
zxkmm ec57f99a2c Contribution template (#3088) 2026-03-16 19:41:43 +01:00
Sarah Rose 5c2d1d3dfb Add MDC-1200 TX external app (#3083)
Implements MDC-1200 single and double-packet TX as a PPMA external app.

Features:
- Supports PTT ID, Emergency, Radio Check, Stun/Unstun, Call Alert,
  Selective Call (1 & 2), Status Request/Response, Remote Monitor, Message
- Auto-fills Arg byte on Op selection
- AFSK FSK modulation: 1800 Hz mark / 1200 Hz space, 1200 baud
- NRZI encoding with Golay FEC per MDC-1200 spec
- Tap-to-edit frequency via keypad
- Default TX gain of 30 dB
- Uses AFSK baseband (same as POCSAG/SAME TX)
- All buffers kept off the stack to avoid hard faults in ext app environment

* mdc_tx: address PR review feedback

- Move MDC helpers into ui::external_app::mdc_tx namespace (drop anonymous namespace)
- Use AFSK_TX_SAMPLERATE macro instead of hardcoded 1536000
- Add #include "modems.hpp" explicitly for AFSK_TX_SAMPLERATE
- Replace n*8/n*16 pixel positions with UI_POS_X/UI_POS_Y macros
- Fix field comments: 'hex bytes' -> 'decimal bytes (0-255)'
- Fix TransmitterView frequency_step: 1750000 -> 12500 (12.5kHz NFM step)
- Fix MDC preamble: replace leading 0x00 bytes with 0xAA
  after NRZI encoding stayed 0x00, terminating TX on the first word.
  0xAA bytes NRZI-encode to 0xFF (continuous 1800Hz mark tone), which
  is the standard MDC-1200 preamble and does not trigger the sentinel.
2026-03-16 19:28:07 +01:00
berkeozkir 52dd2ea2bb Add FPV DETECT external app with Auto-scan FPV Presence Receiver (#3084)
* fpv_rx external app implemented

* FPV DETECT: PR feedback and Copilot suggestions

- Rename app from 'FPV RX' to 'FPV DETECT' (menu and title)
- Add fpv_rx to EXTAPPLIST in external.cmake so app is included in build
- Set external_apps_address_end = 0xAE010000 in external_app_info.py
- Fix clang-format in ui_fpv_rx.cpp (update_visual_state block)
- Re-sample adjacent channels during candidate verification so
  neighbor_margin lock decision uses current measurements instead of
  stale channel_memory_ from scan (reduces false/missed locks)

Made-with: Cursor

* Rename fpv_rx to fpv_detect (files, dir, app id)

- Rename directory external/fpv_rx -> external/fpv_detect
- Rename ui_fpv_rx.{cpp,hpp} -> ui_fpv_detect.{cpp,hpp}
- Update external.cmake and external.ld for fpv_detect
- Namespace fpv_rx -> fpv_detect, class FpvRxView -> FpvDetectView
- Section/symbol .app_fpv_rx -> .app_fpv_detect

Made-with: Cursor
2026-03-16 10:28:07 +08:00
zxkmm b273165a0c fix warning and ub (#3089) 2026-03-14 00:51:15 +08:00
Frederic BORRY 5aecd4097e EPIRB TX Application (#3081)
* First step to epirb-tx app

* Next step to epirb_tx app

* Fixed merge

* EPIRB TX app progress

- Fixed UI
- Added automatic frame resend after delay
- Added load of beacons from EPIRB/BEACONS.TXT file

* First step to adding 121.5 MHz signal

* Refactored 121.5 signal generation code

* Added frequency change when switching from BPSK to AM

* Added checkbox for AM signal.

Added START/STOP button
Added AM frequency
First step to editor mode.

* Next step to frame editor

* Fixed location bits.

* Refactored code + fixed location

* Added location fields

* UI fixes + settings backup

Fixed lat/long display
Added send on change checkbox
Added settings backup
Fixed locator edition
Fixed BEACONS.TXT to move to Self test beacons.

* Update ui_epirb_tx.cpp

Fixed timeout reset

* Added national location protocol.

Added national location protocol.
Fixed send on change.

* UI fixes

Added country selection
Separated beacon protocol and beacon type selectors

* Standard protocol

Added standard protocol
Added internal checkbox

* Fixed fomratting

* Code cleanup

* Comments / doc

* More comments / doc

* More comments / doc

Fixed pre count duration.
Added frequency restore after leaving

* Prepare merge

* Prepare merge

* Finished merge

* More comments and doc

* Update ui_epirb_tx.cpp

Fixed out of memory error when using AlphanumView

* Update proc_epirb_tx.hpp

Fixed comment

* Fixed copilot PR comments.

* Update proc_epirb_tx.hpp

Fixed formatting

* Update external.ld

Merge with kiss_tnc app
2026-03-12 22:05:42 +13:00
qwer123 2c2959df30 change pin mux (#3085)
Co-authored-by: fuwenbo <16166717+fuwenbo95@user.noreply.gitee.com>
2026-03-12 20:24:42 +13:00
stafur 9a3c2cc6ac Next praline clean up (fixed BLE RX crashes) (#3080)
* Cleaned up #ifndef PRALINE and updated logic to being with #ifdef PRALINE entries where possible to make logic flow for PRALINE code execution pipeline clearer. Cleaned up compiletime warnings for PRALINE related codebase updates.

* Addressed comments provided by copilot during PR review. Combed through frequency definitions for consistency between PLL A and PLL B register definitions for CLKs 0-7. Ensured CLK3/LK6 <- SMA PORTs and CLK7 <- not utiliized are disabled during core development phase to support root cause analysis of any spectral artifacts. Updated MCU frequency to 40MHz to ensure audio harmonics are outside FM radio band range (< 80 MHz, >120MHz) and added comments clarifying choice of 40 over 10 MHz for potential future root cause analysis in other bands where audio may be expected as needed. Added CLK6 and CLK7 to Clocks Status View Debug display. Moved CLK defintions and PLL instantiations for components that are most RF sensitive to PLL A. Left others in PLL B. That is move FPGA CLK1 to PLL B, while moving CLK2, CLK4, and CLK5 to PLL A.

* Cleaned up PLL A and B XTAL reference checks relative to 800 MHz.

* Encapsulated HackRF Pro Praline debug and status vies into a single Pro Debug submenu as part of clean up.

* Fixed BLE RX Out of Memory error. Updated LPC43xx ld scripts to accouint for additional HackRF Pro praline memory.

* Addressed copilot comments for ble_rx_app by adding recent_entries_view.set_dirty. Updated ble_rx_app for easier use with heap limit set to one less than recent entries max limit.

* Addressed copilot comments by updating comment clarity in source files. Updated ui_debug to allow for return reference if set for PRO debug menu item.

* Improved readability of intialization parameters for the FPGA registers, and addressed 20Mhz nulls by initializing DC Notch width with standard setting, and DC Adaptiation rate with a balanced setting.

* Ran format-code.sh

* Added option to allow for user to set number if entries in recent list. Default is set to a relatively stable 32.

* Removed #ifdef PRALINE pragmas from ble_rx_app such that HackRF One can also use the updated UI widget to allow for user to set number of entries in recent list.

* Ran format-code.sh
2026-03-10 21:40:43 -04:00
zxkmm c91a6db165 remove redundant code from #3078 (#3082) 2026-03-10 16:13:10 +01:00
Sarah Rose bf5ea0a004 Add KISS TNC external app for APRS RX/TX over USB serial (#3078)
* Add KISS TNC external app for APRS RX/TX over USB serial

Adds a KISS TNC (Terminal Node Controller) app that bridges APRS
packets between the HackRF radio and a connected PC over USB CDC
serial using the KISS protocol.

Features:
- APRS receive at 144.390 MHz (configurable), decoding packets via
  the existing APRS RX baseband (PAPR.bin)
- Received AX.25 frames forwarded to host as KISS-framed data over
  USB serial
- KISS frames received from host transmitted as AFSK audio using the
  existing AFSK TX baseband (PAFT.bin)
- Clean RX->TX->RX baseband switching with proper timing delays
- USB connection status indicator with periodic refresh
- Compatible with standard KISS TNC clients (Xastir, APRSISCE/32,
  Dire Wolf, etc.)

Firmware changes required to support the external app:
- baseband_api.cpp: mark set_aprs() __attribute__((used)) so it is
  retained by the linker for external app use
- protocols/ax25: add make_frame_from_raw() to build NRZI bitstream
  from raw AX.25 bytes, marked __attribute__((used))
- usb_serial_host_to_device: add kiss_raw_handler hook to route
  incoming USB bytes directly to a registered callback, bypassing
  the shell; marked __attribute__((used))
- baseband/proc_aprsrx: add constructor that auto-configures at
  1200 baud so external apps do not need to call set_aprs() at init

* Address PR review: generic RAII USB handler, fix TX cutoff and buffer overflow

- Replace KISS-specific set_kiss_raw_handler() in shared USB code with a
  generic UsbSerialInputHandler RAII class; any app can now register a
  handler via the ctor and it auto-clears in the dtor — no app-specific
  'if' checks in shared code, no __attribute__((used)) needed
- Remove __attribute__((used)) from set_aprs() — it is referenced directly
  by ui_aprs_rx.cpp so it will never be dead-stripped
- Fix send_kiss_frame() buffer overflow: per-byte bounds check prevents
  2-byte escape sequences from writing past the end of the output buffer
- Replace fillOBuffer(TIME_INFINITE) with chOQWriteTimeout(TIME_IMMEDIATE)
  to drop bytes instead of blocking the event loop
- Remove chThdSleepMilliseconds() calls from start_tx() and finish_tx()
- Fix TX cutoff bug: call start_tx() directly from process_kiss_frame()
  instead of deferring via tx_pending_ flag (deferral caused kiss_idx_
  to be 0 by the time start_tx() fired, producing an empty frame)

* Move USB write into UsbSerialInputHandler class

Add a write() method to UsbSerialInputHandler so all USB I/O
(both input callback and output) is encapsulated in the class.
KISS TNC now uses usb_input_handler_->write() instead of calling
chOQWriteTimeout(&SUSBD1.oqueue, ...) directly, keeping raw USB
details out of app code.

* Address PR review: remove auto-configure from APRSRxProcessor, add timeout param to write()

- Remove default_config from APRSRxProcessor() constructor; baseband should
  start unconfigured and wait for APRSRxConfigureMessage from the app side
  (already sent via baseband::set_aprs() in KISS TNC and APRS RX app)
- Remove stray #include "stdio.h" from proc_aprsrx.cpp
- Add systime_t timeout parameter with default TIME_IMMEDIATE to
  UsbSerialInputHandler::write() so callers can choose blocking behaviour
2026-03-10 10:40:27 +01:00
Erwin Ried bf166bb2fc Update main video 2026-03-08 21:32:39 +01:00
stafur 52239d320a Next praline clean up (#3077)
* Cleaned up #ifndef PRALINE and updated logic to being with #ifdef PRALINE entries where possible to make logic flow for PRALINE code execution pipeline clearer. Cleaned up compiletime warnings for PRALINE related codebase updates.

* Addressed comments provided by copilot during PR review. Combed through frequency definitions for consistency between PLL A and PLL B register definitions for CLKs 0-7. Ensured CLK3/LK6 <- SMA PORTs and CLK7 <- not utiliized are disabled during core development phase to support root cause analysis of any spectral artifacts. Updated MCU frequency to 40MHz to ensure audio harmonics are outside FM radio band range (< 80 MHz, >120MHz) and added comments clarifying choice of 40 over 10 MHz for potential future root cause analysis in other bands where audio may be expected as needed. Added CLK6 and CLK7 to Clocks Status View Debug display. Moved CLK defintions and PLL instantiations for components that are most RF sensitive to PLL A. Left others in PLL B. That is move FPGA CLK1 to PLL B, while moving CLK2, CLK4, and CLK5 to PLL A.

* Cleaned up PLL A and B XTAL reference checks relative to 800 MHz.
2026-03-08 13:39:53 +13:00
E.T. 561143cfb0 Improve SD Card Status Display and Error Handling (#3079)
Summary:
Fixed SD card mount error detection by enabling immediate mounting, made the status icon clickable to access settings, and added real-time status display (including filesystem type) in the SD card settings page.

Changes:
- Changed f_mount() to immediate mounting for error detection
- Made SD card status icon clickable, linking to an enhanced sd card settings page that now includes live display of card status and filesystem type
- Removed "SDCard Error" menu item
2026-03-06 20:05:44 +01:00
jLynx 678ee2963e PortaRF & Windows tool update (#3075)
* Remove outdated DLL files and update HackRF submodule to latest commit

* Remove outdated msvcp120.dll and msvcr120.dll files
2026-03-06 09:41:56 +13:00
Sarah Rose bae3854d27 Add SAME TX external app (#3069)
* Add SAME TX external app

Adds a Specific Area Message Encoding (SAME/EAS) transmitter as an
external app (.ppma) for HackRF PortaPack.

SAME is the digital protocol used by NOAA Weather Radio and the
Emergency Alert System in the US and Canada for broadcasting
emergency alerts (tornado warnings, flash floods, EAN, etc.)
on 162.400-162.550 MHz and AM/FM broadcast stations.

Features:
- Originator code selector (WXR, EAS, CIV, PEP)
- Event code selector: 26 standard SAME event codes
  (RWT, TOR, SVR, FFW, EAN, etc.) with live name display
- FIPS location code entry (state 00-99 + county 000-999)
- Purge time entry (hours + minutes)
- Live message preview showing the encoded header
- AFSK encoding: mark=2083Hz, space=1563Hz, 520.833 baud
- FM deviation: 5 kHz, repeated 3x per SAME spec
- Default frequency: 162.550 MHz (WX channel 7)

Protocol details:
- 16-byte 0xAB preamble (bit-reversed for LSB-first AFSK)
- Header: ZCZC-ORG-EVT-SSPCCC+HHMM-JJJHHMM-STATION-
- Uses existing PAFT (proc_afsk) baseband image

* Apply clang-format via format-code.sh

* Add GPL license headers, guard re-entry, call set_transmitting(true)

- Add standard PortaPack GPL-2.0 license header to ui_same_tx.cpp and .hpp
- Guard start_tx() with tx_active_ to prevent re-entrant calls
- Call tx_view.set_transmitting(true) after starting TX so the
  TransmitterView button switches to stop mode
2026-03-05 19:49:17 +13:00
jLynx 0645f1897a Flashing: overhaul mayhem_flasher.bat with serial fallback and improved UX (#3074)
* Refactor mayhem_flasher.bat to streamline firmware flashing process and add serial fallback for device connection

* Improve COM port selection logic in mayhem_flasher.bat for better user input handling

* Enhance error handling in firmware flashing process for improved user feedback
2026-03-05 16:20:41 +13:00
jLynx da0a5c3e73 Fix on-device flash utility hanging on HackRF Pro (Praline) (#3073)
* Add support for W25Q32JV device ID in wait_for_device function

* Enhance mayhem_flasher.bat: Add DFU unbrick option and update firmware links
2026-03-05 14:49:32 +13:00
jLynx 22932e636d Switched build to use ninja by default (#3072)
* Initial plan

* Enable Ninja parallel builds for nightly and stable release pipelines

Co-authored-by: jLynx <4393979+jLynx@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-03-05 13:25:39 +13:00
E.T. 2b7302e659 rename Widget::visible to Widget::drawn (#3066)
* rename Widget::visible to Widget::drawn

* Update firmware/standalone/common/ui/ui_widget.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update firmware/standalone/common/ui/ui_widget.hpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix fm_radio usage of drawn()

---------

Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-04 21:30:43 +01:00
jLynx 60ce89021a Combine per-device firmware ZIPs into a single unified flasher (#3070)
* Add combined firmware ZIP creation and update flashing script for device selection

* Refactor firmware flashing scripts and update README for improved device selection and actions

* Remove outdated flash script for PortaPack Mayhem

* Add write permissions to release jobs in nightly and stable release workflows

* Rename HackRF firmware files for consistency in nightly and stable release workflows

* Refactor release workflows to streamline asset preparation and upload process

* Fix file paths for HackRF firmware in nightly and stable release workflows

* Updated labels

* Refactor mayhem_flasher.bat for improved clarity

* Added reset after flash

Updated the flashing script to include error handling and improved user prompts.

* Remove --reset from DFU as it is known to cause issues
2026-03-05 08:40:39 +13:00
jLynx 25556428a7 Update HackRF flashing utilities for Windows (#3067)
* Update flasher for windows

* Updated scripts

* Fix echo statement formatting in flash script

* Fix echo statement formatting in flash script
2026-03-04 07:22:19 +00:00
stafur 584587d814 Resolve "crackling" sounds in analog_audio_app (#3068)
* Updated PRALINE audio processing methods to account for potential slip in 25MHz reference clock. Moved all clocks to PLLB except for CLK0 to isolate the ADC from potential EMI and clock spills from the other clocks to CLK0. Added PLL reference to Clock ui_debug display and added debug view for PLLA and PLLB. Updated PRO menu in analog_audio_app to allow for 100Hz resolution in sample rate for finer control of samplerate debugging of crackling noise artifacts. Updated ppb method and set it for account for a potential 49 Hz shift in the PRALINE crystal. Started instatiating methods for synchronized DMA transfers for the spectrogram since the FFT calculations and the LCD data updates appear to be related to the crackling noise.

* Added firmware/flash.h to .gitignore.

* Changed reference clock for start_audio_pll to 40MHz instead of ~12MHz xtal. Moved clocks around to reflect best audio quality in analog_audio_app at 3.072 MHz sample frequency.

* Ran format-code.sh.

* Removed ppb in set_sampling_frequency since it created aliasing in analog_audio_app.

* Expanded baseband_bandwidth back to 1750000 after testing since audio quality was retained.

* Updated comments to address copilot recommendations where acceptable.

* Removed firmware/flashsize.h from .gitignore as coordinated with @gullradriel.
2026-03-04 02:11:19 -05:00
Erwin Ried a0179117b1 Update README
Broken photo and links updated
2026-03-04 00:48:17 +01:00
gullradriel ad577a1c66 Delete firmware/flashsize.h (#3063)
Remove uneeded file
2026-03-02 23:43:23 +01:00
E.T. b9958a689b Btngrid better paging (#3061)
* improved paging for ui_btngrid

* move pgup and down to their own funcs in btngrid

* one line minus (format code)

* visible check before focus
2026-03-02 23:33:09 +01:00
E.T. 3d8bc308ee add checks to prevent crashes when showing an empty menu (#3060)
* add checks to prevent crashes when showing an empty menu

* Update firmware/application/ui/ui_btngrid.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-02 21:04:35 +01:00
zxkmm 9ca36394d0 add oscilloscope (time sink) app (#3059)
* POC

* submodule

* seperate bb

* seperate bb

* seperate bb

* seperate bb

* seperate bb

* sadly have to reduce history

* icon

* bring it back

* bring it back

* bb

* format and move bb outside
2026-03-02 19:17:02 +01:00
E.T. f489d43259 Auto update ext apps (#3046)
* first naive impl

* Unregister sdcard notification

* btngrid reload_items fixed

* format-code

* Avoid some unnecessary updates

* clear empty space after removing items

* Fix displaying sdcard error after inserting card while running

* format code

* Eliminate multiple update_items() calls from on_populate()
And call it only once after on_populate()

* update_items() only once when showing btngrid

* normalize offset when menu item count decreases
2026-03-02 12:35:45 +01:00
stafur 6b663894b0 Addresses comments from PR #3057 (#3058)
* Addressed clock inversion and fractiona/interger states. Created ui_debug display that checks expected clock frequencies for expected 3.072 MHz sampling frequency commonly utilizaed in portapack apps.

* Ran format-code.sh

* Addressed comments from PR. Ran format-code.sh. Updated definition of a, b, and c in comments and introduced x, y, and z for debug display. Changed default color for P2/P3 conditional debub display for MS_DIV. Removed implied recalibration since this did not address crackling sound.
2026-03-01 21:51:28 +01:00
stafur b8ca356ce3 Addressed clock inversion and fractional/interger states for 3.072 MHz sampling rate (#3057)
* Addressed clock inversion and fractiona/interger states. Created ui_debug display that checks expected clock frequencies for expected 3.072 MHz sampling frequency commonly utilizaed in portapack apps.

* Ran format-code.sh
2026-03-01 21:28:36 +01:00
stafur 3a54b112ec Imrpoved sound quality in analog_audio_app, and added Nyquist protection option to BBW filter for praline. (#3054) 2026-02-28 23:36:01 +01:00
stafur b013e68b17 Sampling frequency and Receiver Model tweaks. (#3053) 2026-02-28 10:11:04 +01:00
stafur b2bf0f2459 Legacy Initialization, Analog Audio App, and Aliasing fixes. (#3052)
* Added PRO button to analog_audio.cpp for debugging metallic ringing sounds in Audio app. Ensured consitency to 4MHz in analog_audio.cpp

* Restored blutooth after loss.

* Added several sampling rates to analog_audio for testing.

* Ran format-code.sh

* Fixed 0x03 mode for DC/Q-INV/Q-SHFT being set at every tuning and sample rate change. This was the root cause for loss of bluetooth before. Sample rate, and frequency can now be changed without need for manually resetting DC/Q-INV/Q-SHIFT settings. Updated method for setting frequncies in praline so that we have more testing options.

* Ran format-code.sh and cleaned up stale comments.

* Addressed comments, and removed commented line, opting for higher register values, 5E and 5D. Added WFM Debug View to support testing demodulation ringing.

* Addressed comments in PR conversation to clean comments and ensure consistency at initializtion accross updated methods and displays.

* Ran format-code.sh

* Set initial legacy state. Improved readability of clocking initialization settings. Updated set_sampling_frequency, and udpate_bandwidth to set decimation values in the fpga_registers to avoid aliasing in low band frequencies.

* Updated clock_manager to use correct clock and clock parameters for audio pll. Ran format-code.sh.
2026-02-27 23:43:19 -05:00
stafur 56adca3bf6 Resolved root cause for loss of bluetooth functionality and added more testing options (#3049)
* Added PRO button to analog_audio.cpp for debugging metallic ringing sounds in Audio app. Ensured consitency to 4MHz in analog_audio.cpp

* Restored blutooth after loss.

* Added several sampling rates to analog_audio for testing.

* Ran format-code.sh

* Fixed 0x03 mode for DC/Q-INV/Q-SHFT being set at every tuning and sample rate change. This was the root cause for loss of bluetooth before. Sample rate, and frequency can now be changed without need for manually resetting DC/Q-INV/Q-SHIFT settings. Updated method for setting frequncies in praline so that we have more testing options.

* Ran format-code.sh and cleaned up stale comments.

* Addressed comments, and removed commented line, opting for higher register values, 5E and 5D. Added WFM Debug View to support testing demodulation ringing.

* Addressed comments in PR conversation to clean comments and ensure consistency at initializtion accross updated methods and displays.

* Ran format-code.sh
2026-02-26 11:16:07 -05:00
stafur cd7c412aa4 Improve low band signal reception (FM Radio Station Audio tests) (#3048)
* Added PRO button to analog_audio.cpp for debugging metallic ringing sounds in Audio app. Ensured consitency to 4MHz in analog_audio.cpp

* Restored blutooth after loss.

* Added several sampling rates to analog_audio for testing.

* Ran format-code.sh
2026-02-25 03:29:21 -05:00
gullradriel 16c4c28584 fix guru in level / scanner / recon app (#3047)
* fix guru
* fix potential guru
* added information for the sudden return
2026-02-25 01:28:16 +01:00
Totoo 10d17fa7ce Fix device locking in grid item selection callback (#3044) 2026-02-24 16:32:05 +01:00
Totoo e63f54c0a2 Fixes (#3032) 2026-02-24 11:23:41 +01:00
stafur b09efb4d3c Mixer lock:: update clocks and move fpga_bridge_init to portapack.cpp (#3039)
* Updated harckrf_gpio methods to more closely reflect hackrf_usb. Added some ui debug updates to RFFC5072 Status View.

* Updated tuning tables for tuning.cpp. Remnoved 15MHz lower limit in max2831.cpp since lower bandwidths don't seem to be causing lower band issues. Added more opportunities for clocks to stabalize at startup in board.cpp. Added/amended UI to help with addresssing low band tuning issues. Cleaned up stale comments in radio.cpp.

* Ran format-code.sh

* Updated to remove commented lines as part of clean up addressing review comments.

* Fixed clock_manager.cpp configruation for praline. Praline hackrf pro should have: CLK4: 6mA, Invert RFFC5072 40MHz ref and CLK5: 4mA, Invert MAX2831 40MHz ref. Improvements should be visible in the watefall at 2.4GHz, and between 2.311 and 2.599GHz.

* After reviewin clock config, updated values to reflect: CLK0 MAX5864 (ADC) 40 MHz 4mA Normal Integer Minimizes sampling jitter for SNR. CLK1 iCE40 FPGA 40 MHz 6mA Normal Integer Stable timing for the SPI bridge. CLK2 LPC43xx MCU 40 MHz 4mA Normal Integer Standard reference for MCU PLL. CLK3 SMA Port P1 10 MHz 8mA Normal Integer Cleanest square wave for Ext Ref. CLK4 RFFC5072 (Mixer) 40 MHz 6mA Inverted Integer Crucial for PLL Lock stability. CLK5 MAX2831 (TRX) 40 MHz 4mA Inverted Integer Reduces phase noise in the 2.4GHz LO. CLK6 SMA Port P2 10 MHz 8mA Normal Integer Sync output for external gear. CLK7 Reserved Off N/A N/A N/A Disabled to save power/reduce EMI.

* Ran format-code.sh

* Moved fpga_brdige_init to portapack.cpp. This more closely resembles how hackrf_usb starts initializes the fpga, and allows time for the clocks to stabilize and facilitate locking to support low band tuning.

* Ran format-code.sh

* Added ProRadio Debug display to check for / debug locking.

* Moved fpga_bridge_init before radio_init in portapack.cpp

* Low band can now see signals. Updated clock_manager, rffc507 and portapack.cpp to ensure fpga intialized correctly. Ensures rffc507x.cpp has correct 40MHz reference frequency, prescaler, and synth configuration.

* Updated radio to add q-invert on, dc-block on, and deimation=3 (8x, for 20 -> 2.5 MHz decimation for testing cleaner low band signals.

* Ran format-code.sh
2026-02-24 03:36:55 -05:00
gullradriel bfbbacd43c Update hackrf submodule (#3034)
* fix warnings

* update hackrf submodule
2026-02-24 00:41:59 +01:00
Sandbox 065d01b591 Move POCSAG Tx to Ext. (#3033)
* Disable POCSAG TX in menu

Comment out POCSAG TX related code in ui_navigation.cpp

* Comment out ui_pocsag_tx.cpp in CMakeLists

Comment out the ui_pocsag_tx.cpp file in CMakeLists.

* Move POCSAG Tx to Ext.

* Update external.cmake

* Delete firmware/application/apps/ui_pocsag_tx.hpp

* Delete firmware/application/apps/ui_pocsag_tx.cpp

* Update main.cpp

* Update main.cpp

* Update external.ld

* fix namespace

* deleted commented out entry

* delete commented entries

* fix: correct baseband for pocsag tx

* run prepared

---------

Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-02-23 21:37:03 +01:00
stafur b197a4b1de Update to CLK4 and CLK5 configuration for PRALINE. (#3031)
* Updated harckrf_gpio methods to more closely reflect hackrf_usb. Added some ui debug updates to RFFC5072 Status View.

* Updated tuning tables for tuning.cpp. Remnoved 15MHz lower limit in max2831.cpp since lower bandwidths don't seem to be causing lower band issues. Added more opportunities for clocks to stabalize at startup in board.cpp. Added/amended UI to help with addresssing low band tuning issues. Cleaned up stale comments in radio.cpp.

* Ran format-code.sh

* Updated to remove commented lines as part of clean up addressing review comments.

* Fixed clock_manager.cpp configruation for praline. Praline hackrf pro should have: CLK4: 6mA, Invert RFFC5072 40MHz ref and CLK5: 4mA, Invert MAX2831 40MHz ref. Improvements should be visible in the watefall at 2.4GHz, and between 2.311 and 2.599GHz.

* After reviewin clock config, updated values to reflect: CLK0 MAX5864 (ADC) 40 MHz 4mA Normal Integer Minimizes sampling jitter for SNR. CLK1 iCE40 FPGA 40 MHz 6mA Normal Integer Stable timing for the SPI bridge. CLK2 LPC43xx MCU 40 MHz 4mA Normal Integer Standard reference for MCU PLL. CLK3 SMA Port P1 10 MHz 8mA Normal Integer Cleanest square wave for Ext Ref. CLK4 RFFC5072 (Mixer) 40 MHz 6mA Inverted Integer Crucial for PLL Lock stability. CLK5 MAX2831 (TRX) 40 MHz 4mA Inverted Integer Reduces phase noise in the 2.4GHz LO. CLK6 SMA Port P2 10 MHz 8mA Normal Integer Sync output for external gear. CLK7 Reserved Off N/A N/A N/A Disabled to save power/reduce EMI.

* Ran format-code.sh
2026-02-22 21:31:46 -05:00
stafur 85eaa9d800 Updated praline low and high band tuning (#3030)
* Updated harckrf_gpio methods to more closely reflect hackrf_usb. Added some ui debug updates to RFFC5072 Status View.

* Updated tuning tables for tuning.cpp. Remnoved 15MHz lower limit in max2831.cpp since lower bandwidths don't seem to be causing lower band issues. Added more opportunities for clocks to stabalize at startup in board.cpp. Added/amended UI to help with addresssing low band tuning issues. Cleaned up stale comments in radio.cpp.

* Ran format-code.sh

* Updated to remove commented lines as part of clean up addressing review comments.
2026-02-23 13:40:49 +13:00
Sandbox 1ad092f341 ACARS Rx App using libacars (#3029)
* Using same decode as libacrars
* Enable ACARS RX
* indent + fix warning
* fixed CRC calculations, length validation, message print
Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-02-22 21:29:20 +01:00
E.T. 0fe51fa7e3 Move flashsize.h to build directory to avoid conflicts when switching between different builds (#3021)
* Move flashsize.h to build directory to avoid conflicts when switching builds

plus small fix in praline image generation

* Update hackrf submodule to latest next

* fix fpga append comments

* Update firmware/CMakeLists.txt

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update firmware/CMakeLists.txt

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* remove ls dependency from build

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-02-22 21:21:03 +01:00
Sandbox 4cc2ba690b Add 12k5 RXBW to NFM Mode (#3023)
* Add 12k5 option to NFM
* Updated freqman_db
* Add 12k5 RXBW to NFM
* Add 12k5 RXBW
* formating
2026-02-22 20:53:26 +01:00
gullradriel abee022000 update submodule, revert lz4 bufs M0/M4 allocation (#3022) 2026-02-21 19:54:48 +01:00
Totoo 430c8e5e79 Enhance ADSB details view with map trail functionality and update gro… (#3020)
* Enhance ADSB details view with map trail functionality and update ground speed calculation
2026-02-21 14:32:20 +01:00
stafur 7ea3eec016 Update to additional gpio definitions for praline (#3018)
* Initial commit and pr for HackRF Pro (praline) arch-port to mayhem-firmware. Please see https://github.com/portapack-mayhem/mayhem-firmware/issues/2957. Added flash specifics for -DBOARD=PRALINE. This firmware only builds with toolchain v9.2.1 if hackrf codebase has -B arm in firmware/hackrf_usb/CMakeLists.txt.

* Updated CMakeLists.txt per coordination with @HtoToo. For -DBOARD=PRALINE FLASH_MB_SIZE and FLASH_MB_LIMIT_SIZE are now 4. Removed praline specific variable for FLASH limits.

* Updated chibios-portapack's board.cpp to support initialization of the HachRF-Pro (praline) FPGA. Added append_fpga_bitstream.py tool to ensure that praline_fgpa.bin bitstream can be appended to -DBOARD=PRALINE produced firmware. In order to ensure successful execution of append_fpga_bitstream.py to append the fpga bitstream we should expect that the bistsream will be located at 0x180000 in flash. This requires that FLASH_MB_LIMIT_SIZE must be 1.5, and FLASH_BYTES_LIMIT_SIZE must be 1535 * 1024. If we want to allow more or less space for the base firmware image sans the fpga bitstream the location of the bistream must be moved to a location other than 0x180000.

* Updated location of praline_fpga.bin bitstream to 0x380000 to allow more room for firmware. Firmware now has 3.5MB, or 2MB more available than before as coordinated with @HTotoo.

* Expanded #ifndef PRALINE to include og and r9 gpio and pin setup as coordinated with @HTotoo.

* Added note for PRALINE FLASH_MB_LIMIT_SIZE and FLASH_BYTES_LIMIT_SIZE to explain why we are using the 3.5 and 3584 values respectively as coordinated with @HTotoo.

* Next round of modifications derived heavily, if not entirely  from work done by @banandana at https://github.com/Banandana/mayhem-firmware. This commit should power on the HackRF Pro (praline) display, power on the fpga, and enable gpio, and provide debug utilties. There is still a lot of work to be done to fully enable the new praline board with this build and firmware architectural porting effort. However, hackrf-one boards do not seem to be adversely impacted by the #ifdef PRALINE statements, and CMakeLists updates, as far as I have been able to test.

* Ran format-code.sh. Updates for this commit are only due to formatting. Tested builds and they seem to work as exptected.

* Addressed fixes in firmware/application and firmware/baseband. Stream now flows to capture and looking glass. Issues were related to thread management. Issues were originally addressed by @banandana.

* Ran format-code.sh to allow for consistency with autoamted clang checks.

* Update hackrf ref repo to mayhem-portapack-hackrf next from https://github.com/portapack-mayhem/hackrf

* Addressed format edits necessary to pass clang-format check.

* Starting addressing Si5351 Clocks for radio sampling. These updates correctly set the Si5351 clock at start up. There appears to be an issue during runtime when testing with RX Test Init, Capture and Looking glass.

* Updated clock_manager.cpp to restore correct function introduced by @banandana when testing with Rx Test Init.

* Switched to using decimation for setting the sample rate without changing the Si5351 clock. This assumes that for the praline board Si5351 CLK0 runs at fixed 8 MHz (constant) and the FPGA decimates to get the desired sample rate. For example, for a 1 MHz sample rate -> Si5351 outputs 8 MHz, FPGA decimates by 8. There is still more work needed here, and potential verification that this is the correct way to operate with this new archteitecture.

* After deliberating on hackrf_usb hackrf_core.c and radio.c, and reviewing firmware/application/hw/si5351.cpp the original approach of using the aproach detailed in hackrf_core.c sample_rate_frac_set() lines 580-582, via the implementation in firmware/application/hw/si5351.cpp seems like the best place to continue testing efforts.

* Tested at ~2.4GHz (2.3 - 2.5) with lookgin glass and was able to receive signals. Added a Signal Path debug app to test gains, and readio mode (receive/transmit).

* Added two debug apps for the RFFC507x. Status View and Tuning View. This helped debug some of the potential issues with tuning.

* update submodule

* format code

* Small touch up merging latest next and ensuring build for HackRF One.

* Fix ui addition of max2831 debug display.

* Reverted edits to re: firmware/baseband/sd_over_usb/scsi.c and firmware/application/portapack.cpp. Source now builds, had to pull latest hackrf submodule.

* Skipped detect hardware for praline board to avoid backscreen in HackRF Pro praline board.

* Added UI debug display for max2831 like the rffc507x. Added Filter Band display and Mixer status to Signal Path Status debug display.

* Added ability to set Q: INV, or Q:NOR in Signal Path Status Debug view for testing. Also added display entries for the FGPA Ctrl register fir debugging.

* Added System Diagnostics which allows testing sample rate, q-inv, and dc blk

* Corrected LP - GPIO4[8], and GPIO4[9] issue which was preventing RF Amp, and LPF from chaning state. HackRF Pro (praline) now correctly changes state of LPF and RF Amp as expected.

* Corrected LP - GPIO4[8], and GPIO4[9] issue which was preventing RF Amp, and LPF from chaning state. HackRF Pro (praline) now correctly changes state of LPF and RF Amp as expected.

* Added consistency for testing.

* Added ui to debug whether rffc5072 is locking correctly. Also addressed rf_amp fixed in on state, such that it may now be able to be toggled in HackRF Pro praline setups.

* Updated format to support automated tests.

* format code

* Updated rffc5072 status view in ui_debug to help with debugging. Made updated recommendtions for merge from PR comments. Made updates to ensure resetx isn't reconfigured for output in rffc507x.cpp.

* Ran ./format-code.sh

* Added reference comment that addresses debugging in PRALINE to detail that when the capture app was requesting 15 MHz, it matched 15100000 which resulted in the 8.5M setting. To get the actual 15M setting (22.6 MHz bandwidth), I had to force a request at least 22 MHz while debugging.

* Debugging tunning ENX and frequency locking in low band.

* Addressed pin differences between HackRF One and HackRF Pro (praline), resolved ENX configuration.

* Cirrected pins, and debugging tuning for low band. High band now goes past 2.7GHz into 3.3 GHz.

* Updated ui_debug to track ENX updates and support tracing which calls may be changing ENX.

* Removed clkin reference in ui_debug.cpp to allow space for more information about CLK0, CLK1, CLK4, and CLK5.

* Removed clkin reference in ui_debug.cpp to allow space for more information about CLK0, CLK1, CLK4, and CLK5.

* Ran format-code.sh

* Updated rffc507xx.cpp, and ensured hackrf submodule at f1dcd554.

---------

Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-02-21 16:48:23 +13:00
Totoo eb524f7110 fix dino hiscore position (#3017) 2026-02-20 22:09:55 +01:00
Totoo 3413d71bb3 Refactor marker addition logic in ADSBRxView to eliminate shadow markers and optimize RAM usage (#3013) 2026-02-20 15:18:15 +01:00
E.T. c64dddf5cc Dockerize docu update (#3014) 2026-02-20 14:49:35 +01:00
Totoo 3d56bccd49 Fix sd_over_usb serial command (#3015) 2026-02-20 14:43:20 +01:00
gullradriel 705e4f125f Update submodule, fix warnings for PRALINE (#3012)
* update submodule
* remove pragma, remove unused variable
* fix bad external decl, signed vs unsigned comparison
* fix unused: display computed flags
* fix max2881 info mess
* code format
* fix gui positions
* use constexpr for min and max lo_frequencies
2026-02-20 11:08:41 +00:00
qwer123 363b27efe8 fix RX AUDIO baseband async error for PRALINE (#3010) 2026-02-20 10:35:19 +01:00
E.T. f8ed7489c9 update docker entrypoint to support setting build directory via -B or --workdir arguments (#3011) 2026-02-20 10:29:22 +01:00
Totoo 0685fb7a9f Add altitude-based color coding for map markers in adsb (#3004)
* Add altitude-based color coding for map markers in adsb
2026-02-19 20:50:49 +01:00
Totoo 6498f5a1b2 Notifications (#3002) 2026-02-19 14:33:01 +01:00
jLynx 77f747aab2 Add HackRF Pro (PRALINE) Board Detection and Info Display (#3006) 2026-02-19 08:56:22 +01:00
Jeff Laflamme 819b4d0fed Add restaurant pager (EV1527-variant) to SubGhzD (#2998)
* Add restaurant pager protocol to SubGhzD receiver

EV1527-variant 25-bit OOK PWM decoder for restaurant pager
systems (JianTao JT-913 and similar). Decodes system ID,
pager address, and function code (buzz/sync).

* Show friendly pager details in SubGhzD view
2026-02-18 07:47:41 +01:00
jLynx 15df51f976 TPMS TX Application with Enhanced RX Integration (#3001)
* Add TPMS transmit application with editable fields and update linker script

* Fix formatting in application information structure for TPMS TX

* Implement TPMS recent entry detail view and enhance TPMS TX encoding logic

* Refactor TPMS TX UI layout and streamline packet display updates

* Fix formatting by removing unnecessary blank line in TPMSTXView constructor

* Enhance TPMS transmission logic with signal type handling and update UI components

* Add advanced mode toggle and auto-select signal type based on packet type

* Enhance packet display and encoding logic with protocol-specific notes for temperature and ID handling

* Implement baseband switching logic for TPMS transmission based on signal type

* Implement application-level FSK repeat handling and initialize repeat counter

* Add FSK termination function and improve transmission handling

* Remove unused kill_fsk function and clean up whitespace in TPMS transmission logic

* Add pressure and temperature unit conversion functionality in TPMSTXView

* Add temperature and function field visibility handling in TPMSTXView

* Add signal type handling and improve data loading in TPMSTXView

* Fix formatting of pressure unit string in TPMSRecentEntryDetailView

* Add pressure overflow checks and enhance transponder ID handling in TPMSTXView

* Update application information and change icon color to green in TPMS TX

* Update desired menu position for TPMS TX application
2026-02-17 22:02:27 -08:00
zxkmm e9e78609b7 init (#3000) 2026-02-17 16:06:38 +08:00
Totoo 785bd06938 fix random splash file selection logic in SplashScreenView (#2999) 2026-02-17 20:52:08 +13:00
zxkmm 300a4b03c6 fix the FLASH ERR warn on hackrf pro plus PP H1/2/3/4 (#2997)
* init

* remove mbt
2026-02-17 12:31:03 +08:00
jLynx eaa07f8369 Fix HackRF Pro firmware compatibility check failure (#2996)
* fix: update SUPPORTED_PLATFORM definition to align with official HackRF firmware logic

* fix: align SUPPORTED_PLATFORM macro formatting with coding standards
2026-02-17 11:35:12 +08:00
stafur c31a978e6f Next pralinetcv9.2.1 archport (#2991)
* Initial commit and pr for HackRF Pro (praline) arch-port to mayhem-firmware. Please see https://github.com/portapack-mayhem/mayhem-firmware/issues/2957. Added flash specifics for -DBOARD=PRALINE. This firmware only builds with toolchain v9.2.1 if hackrf codebase has -B arm in firmware/hackrf_usb/CMakeLists.txt.

* Updated CMakeLists.txt per coordination with @HtoToo. For -DBOARD=PRALINE FLASH_MB_SIZE and FLASH_MB_LIMIT_SIZE are now 4. Removed praline specific variable for FLASH limits.

* Updated chibios-portapack's board.cpp to support initialization of the HachRF-Pro (praline) FPGA. Added append_fpga_bitstream.py tool to ensure that praline_fgpa.bin bitstream can be appended to -DBOARD=PRALINE produced firmware. In order to ensure successful execution of append_fpga_bitstream.py to append the fpga bitstream we should expect that the bistsream will be located at 0x180000 in flash. This requires that FLASH_MB_LIMIT_SIZE must be 1.5, and FLASH_BYTES_LIMIT_SIZE must be 1535 * 1024. If we want to allow more or less space for the base firmware image sans the fpga bitstream the location of the bistream must be moved to a location other than 0x180000.

* Updated location of praline_fpga.bin bitstream to 0x380000 to allow more room for firmware. Firmware now has 3.5MB, or 2MB more available than before as coordinated with @HTotoo.

* Expanded #ifndef PRALINE to include og and r9 gpio and pin setup as coordinated with @HTotoo.

* Added note for PRALINE FLASH_MB_LIMIT_SIZE and FLASH_BYTES_LIMIT_SIZE to explain why we are using the 3.5 and 3584 values respectively as coordinated with @HTotoo.

* Next round of modifications derived heavily, if not entirely  from work done by @banandana at https://github.com/Banandana/mayhem-firmware. This commit should power on the HackRF Pro (praline) display, power on the fpga, and enable gpio, and provide debug utilties. There is still a lot of work to be done to fully enable the new praline board with this build and firmware architectural porting effort. However, hackrf-one boards do not seem to be adversely impacted by the #ifdef PRALINE statements, and CMakeLists updates, as far as I have been able to test.

* Ran format-code.sh. Updates for this commit are only due to formatting. Tested builds and they seem to work as exptected.

* Addressed fixes in firmware/application and firmware/baseband. Stream now flows to capture and looking glass. Issues were related to thread management. Issues were originally addressed by @banandana.

* Ran format-code.sh to allow for consistency with autoamted clang checks.

* Update hackrf ref repo to mayhem-portapack-hackrf next from https://github.com/portapack-mayhem/hackrf

* Addressed format edits necessary to pass clang-format check.

* Starting addressing Si5351 Clocks for radio sampling. These updates correctly set the Si5351 clock at start up. There appears to be an issue during runtime when testing with RX Test Init, Capture and Looking glass.

* Updated clock_manager.cpp to restore correct function introduced by @banandana when testing with Rx Test Init.

* Switched to using decimation for setting the sample rate without changing the Si5351 clock. This assumes that for the praline board Si5351 CLK0 runs at fixed 8 MHz (constant) and the FPGA decimates to get the desired sample rate. For example, for a 1 MHz sample rate -> Si5351 outputs 8 MHz, FPGA decimates by 8. There is still more work needed here, and potential verification that this is the correct way to operate with this new archteitecture.

* After deliberating on hackrf_usb hackrf_core.c and radio.c, and reviewing firmware/application/hw/si5351.cpp the original approach of using the aproach detailed in hackrf_core.c sample_rate_frac_set() lines 580-582, via the implementation in firmware/application/hw/si5351.cpp seems like the best place to continue testing efforts.

* Tested at ~2.4GHz (2.3 - 2.5) with lookgin glass and was able to receive signals. Added a Signal Path debug app to test gains, and readio mode (receive/transmit).

* Added two debug apps for the RFFC507x. Status View and Tuning View. This helped debug some of the potential issues with tuning.

* update submodule

* format code

* Small touch up merging latest next and ensuring build for HackRF One.

* Fix ui addition of max2831 debug display.

* Reverted edits to re: firmware/baseband/sd_over_usb/scsi.c and firmware/application/portapack.cpp. Source now builds, had to pull latest hackrf submodule.

* Skipped detect hardware for praline board to avoid backscreen in HackRF Pro praline board.

* Added UI debug display for max2831 like the rffc507x. Added Filter Band display and Mixer status to Signal Path Status debug display.

* Added ability to set Q: INV, or Q:NOR in Signal Path Status Debug view for testing. Also added display entries for the FGPA Ctrl register fir debugging.

* Added System Diagnostics which allows testing sample rate, q-inv, and dc blk

* Corrected LP - GPIO4[8], and GPIO4[9] issue which was preventing RF Amp, and LPF from chaning state. HackRF Pro (praline) now correctly changes state of LPF and RF Amp as expected.

* Corrected LP - GPIO4[8], and GPIO4[9] issue which was preventing RF Amp, and LPF from chaning state. HackRF Pro (praline) now correctly changes state of LPF and RF Amp as expected.

* Added consistency for testing.

* Added ui to debug whether rffc5072 is locking correctly. Also addressed rf_amp fixed in on state, such that it may now be able to be toggled in HackRF Pro praline setups.

* Updated format to support automated tests.

* format code

* Updated rffc5072 status view in ui_debug to help with debugging. Made updated recommendtions for merge from PR comments. Made updates to ensure resetx isn't reconfigured for output in rffc507x.cpp.

* Ran ./format-code.sh

* Added reference comment that addresses debugging in PRALINE to detail that when the capture app was requesting 15 MHz, it matched 15100000 which resulted in the 8.5M setting. To get the actual 15M setting (22.6 MHz bandwidth), I had to force a request at least 22 MHz while debugging.

---------

Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-02-17 15:27:29 +13:00
Pezsma 266db4b158 Random splash on boot (#2961) 2026-02-16 17:52:47 +01:00
gullradriel 290bf543a1 fix restore settings (#2995) 2026-02-16 17:06:57 +01:00
gullradriel e17f4c4ab4 Fix units tpms not compiling (#2994)
* add defines for unit types
* fix compilation, units usage and variables names
2026-02-16 15:50:27 +01:00
lifegame1lu111 83a4580353 Refactor KeeLoq crypto functions and add new KeeLoq TX app (#2990) 2026-02-16 15:19:50 +01:00
Hoite Prins 00f40bf171 feat: add BAR unit support to TPMS RX app (#2988)
- Add bar() conversion method to Pressure class
- Convert pressure units system from boolean to enum (kPa, PSI, BAR)
- Add BAR option to pressure unit selector in UI
- Update settings manager to persist pressure unit preference
- Use ternary operators for compact pressure formatting
2026-02-16 14:55:12 +01:00
Totoo 794daf5257 Rtty tx and rx (#2977) 2026-02-16 14:34:55 +01:00
gullradriel 95eafeb812 fix clang format workflow, update hackrf submodule, fix subghzd compilation warning, remove flashsize.h (#2992)
* trying to fix clang format workflow
* try without docker repo, classic apt install
* find what installed clang format 18 version is
* removed auto generated flashsize
* comment on flashsize.h
* modified to use repo owned clang-format binaries
* code format for windows hosts
* add clang-format binaries for linux x86_64/arm64, macos-arm64, windows x86_64
* add helper script to download specific llvm clang-format version
* add helper to download missing libraries (use docker)
* use custom clang-format
* fix ui_subghzd.cpp:85:29: warning: narrowing conversion of 'this->ui::SubGhzDRecentEntryDetailView::cnt' from 'uint32_t' {aka 'long unsigned int'} to 'uint16_t' {aka 'short unsigned int'} [-Wnarrowing]
* remove auto generated file
* Update tools/clang-format.sh
* removed fallback
* updated hackrf submodule
2026-02-15 21:29:26 +00:00
gullradriel 94d46036f8 up to latest (#2984) 2026-02-12 12:00:22 +00:00
Totoo 68000f0b94 fix compile (#2983) 2026-02-12 12:41:12 +01:00
lifegame1lu111 eff68693ad Add support for decrypting and saving KeeLoq signals for SubGhzD (#2982) 2026-02-12 10:41:56 +01:00
jLynx 2f468fd37c Add Multi-Device Build Support to Release Workflows (#2978) 2026-02-11 10:44:07 +01:00
stafur 39424632bb HackRF Pro (praline) arch-port initial PR (#2958)
* Initial commit and pr for HackRF Pro (praline) arch-port to mayhem-firmware. Please see https://github.com/portapack-mayhem/mayhem-firmware/issues/2957. Added flash specifics for -DBOARD=PRALINE. This firmware only builds with toolchain v9.2.1 if hackrf codebase has -B arm in firmware/hackrf_usb/CMakeLists.txt.

* Updated CMakeLists.txt per coordination with @HtoToo. For -DBOARD=PRALINE FLASH_MB_SIZE and FLASH_MB_LIMIT_SIZE are now 4. Removed praline specific variable for FLASH limits.

* Updated chibios-portapack's board.cpp to support initialization of the HachRF-Pro (praline) FPGA. Added append_fpga_bitstream.py tool to ensure that praline_fgpa.bin bitstream can be appended to -DBOARD=PRALINE produced firmware. In order to ensure successful execution of append_fpga_bitstream.py to append the fpga bitstream we should expect that the bistsream will be located at 0x180000 in flash. This requires that FLASH_MB_LIMIT_SIZE must be 1.5, and FLASH_BYTES_LIMIT_SIZE must be 1535 * 1024. If we want to allow more or less space for the base firmware image sans the fpga bitstream the location of the bistream must be moved to a location other than 0x180000.

* Updated location of praline_fpga.bin bitstream to 0x380000 to allow more room for firmware. Firmware now has 3.5MB, or 2MB more available than before as coordinated with @HTotoo.

* Expanded #ifndef PRALINE to include og and r9 gpio and pin setup as coordinated with @HTotoo.

* Added note for PRALINE FLASH_MB_LIMIT_SIZE and FLASH_BYTES_LIMIT_SIZE to explain why we are using the 3.5 and 3584 values respectively as coordinated with @HTotoo.

* Next round of modifications derived heavily, if not entirely  from work done by @banandana at https://github.com/Banandana/mayhem-firmware. This commit should power on the HackRF Pro (praline) display, power on the fpga, and enable gpio, and provide debug utilties. There is still a lot of work to be done to fully enable the new praline board with this build and firmware architectural porting effort. However, hackrf-one boards do not seem to be adversely impacted by the #ifdef PRALINE statements, and CMakeLists updates, as far as I have been able to test.

* Ran format-code.sh. Updates for this commit are only due to formatting. Tested builds and they seem to work as exptected.

* Addressed fixes in firmware/application and firmware/baseband. Stream now flows to capture and looking glass. Issues were related to thread management. Issues were originally addressed by @banandana.

* Ran format-code.sh to allow for consistency with autoamted clang checks.

* Update hackrf ref repo to mayhem-portapack-hackrf next from https://github.com/portapack-mayhem/hackrf

* Addressed format edits necessary to pass clang-format check.

* Starting addressing Si5351 Clocks for radio sampling. These updates correctly set the Si5351 clock at start up. There appears to be an issue during runtime when testing with RX Test Init, Capture and Looking glass.

* Updated clock_manager.cpp to restore correct function introduced by @banandana when testing with Rx Test Init.

* Switched to using decimation for setting the sample rate without changing the Si5351 clock. This assumes that for the praline board Si5351 CLK0 runs at fixed 8 MHz (constant) and the FPGA decimates to get the desired sample rate. For example, for a 1 MHz sample rate -> Si5351 outputs 8 MHz, FPGA decimates by 8. There is still more work needed here, and potential verification that this is the correct way to operate with this new archteitecture.

* After deliberating on hackrf_usb hackrf_core.c and radio.c, and reviewing firmware/application/hw/si5351.cpp the original approach of using the aproach detailed in hackrf_core.c sample_rate_frac_set() lines 580-582, via the implementation in firmware/application/hw/si5351.cpp seems like the best place to continue testing efforts.

* Tested at ~2.4GHz (2.3 - 2.5) with lookgin glass and was able to receive signals. Added a Signal Path debug app to test gains, and readio mode (receive/transmit).

* Added two debug apps for the RFFC507x. Status View and Tuning View. This helped debug some of the potential issues with tuning.

* update submodule

* format code

* Small touch up merging latest next and ensuring build for HackRF One.

* Reverted edits to re: firmware/baseband/sd_over_usb/scsi.c and firmware/application/portapack.cpp. Source now builds, had to pull latest hackrf submodule.

* Skipped detect hardware for praline board to avoid backscreen in HackRF Pro praline board.

---------

Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-02-11 09:15:11 +01:00
E.T. cd30022b61 Do hardware detection on init for shell info (#2975)
and reset gpio state afterward
2026-02-09 21:49:16 +01:00
gullradriel be872003ec Update hackrf submodule (#2973)
* update submodule
* fix warning missing delay declaration
* update: fix get board messing up radio/waterfalls
2026-02-09 20:25:40 +00:00
gullradriel 9e7758718c update submodule (#2971) 2026-02-08 20:44:14 +01:00
E.T. 89b878d3bb Better hackrf board detection for the serial info command (#2967) 2026-02-07 15:45:26 +01:00
gullradriel 56d965f3ae update submodule to latest (#2968) 2026-02-06 19:55:53 +01:00
Totoo fe9f0c228f SubCar fm mode (#2965) 2026-02-06 17:20:14 +01:00
Totoo 6562e596e8 OSM change detection 2026-02-06 15:07:07 +01:00
Totoo c27192bdbb Flipper fsk tx support (#2964) 2026-02-06 09:48:20 +01:00
gullradriel 927e3b11e7 Morse rx cleanings (#2962)
* fix ui overlap, align with log box
* bring back enum inside the class as a simple enum
* correctly restore saved setting whatever the modulation was at exit
* set squelch / field_squelch / receiver model in one go, add audio/receiver model stop and start
2026-02-04 14:01:29 +01:00
Pezsma a44a00acf0 Morse rx further development (#2959)
* filters OK, radio configurations set

* all modes are working, noise still needs work, especially in FM. A baseband enum is also needed

* audio and decoding fixed, clipping and the baseband enum are still missing
2026-02-03 22:25:52 +01:00
gullradriel 6739b43914 disable select key repeat in Mic TX (#2956) 2026-02-01 19:44:56 +01:00
gullradriel b306a0d490 Revert "Update Map view labels to match marine navigation standards (#2951)" (#2955)
This reverts commit c6130c6e8d.
2026-01-31 21:15:12 +01:00
Herbenderbler c6130c6e8d Update Map view labels to match marine navigation standards (#2951)
Replace Alt: with CoG: and Spd: with SoG: in the Map view to better align with standard marine navigation terminology and the AIS Rx screen.

- Alt (Altitude) → CoG (Course over Ground)
- Spd (Speed) → SoG (Speed over Ground)

Rationale: AIS is primarily used for marine navigation where altitude is typically zero (sea level), while Course over Ground is essential navigation information. This change provides consistency with the AIS Rx screen which already displays CoG and SoG.

Modified files:
- firmware/application/ui/ui_geomap.hpp
- firmware/standalone/common/ui/ui_geomap.hpp
2026-01-31 20:53:58 +01:00
Herbenderbler 97f2ca42f1 Fix aviation 8.33kHz step size to reduce frequency drift (#2950)
Update aviation radio step size from 8330 Hz to 8333 Hz in freqman_steps
and freqman_steps_short arrays. This better represents the actual 8⅓ kHz
(25000/3 Hz) aviation channel spacing and significantly reduces frequency
drift during dial tuning.
With this fix, stepping 24 times from 123.000 MHz now correctly reaches
123.200 MHz instead of 123.199 MHz.
2026-01-31 19:35:14 +01:00
Pezsma e2b642ae25 New Morse TX app (#2948)
* morse tx
* global button repeat function improvement
2026-01-30 17:18:13 +01:00
gullradriel 106d9b1207 updated submodule (#2947) 2026-01-30 10:14:08 +01:00
gullradriel c8ae419e0b updated submodule (#2946) 2026-01-30 00:21:36 +01:00
Totoo 2319dcd0d6 Aprs tx gps added, and save config added, path added (#2937) 2026-01-25 10:01:33 +01:00
lifegame1lu111 44d9ce00cd Holtek HT6P20B support for SubGhzD (#2941) 2026-01-25 09:41:06 +01:00
Pezsma 5cc5f8faa6 Morserx added ssb modes (#2934)
* USB receive good
* LSB receive good
* all mode receive good, log is dead
* frequency measurement good, decode dead
* total killed
* szutyok, gerjed
* frequenty meas only.
* ui ok
* Add frequency measurement handling and update Morse processing logic
* fix ssb speed
2026-01-22 18:49:21 +01:00
gullradriel 3a0ca480b0 conform to clang spec (#2925)
* conform to clang spec
* proper find call
* Update format-code.sh
2026-01-22 15:26:31 +01:00
Totoo 7a2b432bfa Dyn flash size (#2933) 2026-01-22 15:24:42 +01:00
zxkmm 86d7b345d0 waterfall color designer app (#2625) 2026-01-19 21:46:25 +01:00
Pezsma 877ede5f86 Added morse receiver app. (#2923)
* Adder morse receiver app. Works with cw and fm mode. Adaptive speed learning. Logging.
2026-01-16 09:57:45 +01:00
gullradriel 19eb1c40ab fix NumberField wrapping when steps is not '1' and minimum can be negative (#2920) 2026-01-11 22:49:18 +01:00
gullradriel 9c03a18893 Looking glass marker no encoder (#2919)
* enable repeat mode for select button too
* added marker + and marker - buttons, removed non effective iqcalc tuning
2026-01-10 20:19:10 +01:00
gullradriel cd504eb4a7 Udpdate hackrf submodule (#2906)
* add missing parameter to usb endpoint init
* make firmware compile with latest hackrf
* updated submodule
* format code
2026-01-10 16:19:53 +01:00
Totoo dc976c7f38 make sd over usb external (#2918) 2026-01-10 15:10:23 +01:00
Totoo 266d90a600 Updated subcar processors (#2913) 2026-01-10 14:47:33 +01:00
Totoo 2884bb9ab7 make siggen ext (#2917) 2026-01-10 21:25:41 +08:00
Totoo 133b2d2065 fix for #2903 (#2914) 2026-01-10 20:25:30 +08:00
Totoo 2f2671f76c Some tweaks to support some usecases without encoder, also maximize the NewButton's text size (#2915) 2026-01-10 20:21:57 +08:00
Totoo f5c2c78733 fix looking glass marker on portarf (#2912) 2026-01-09 20:32:31 +00:00
Harin Lee e4ab7227fc Fix typos of 'transceiver' (#2910) 2026-01-08 10:34:05 +01:00
Harin Lee 93799bde6d New approach to support RX-only mode more cleanly (#2908)
* Add enabled checks in ReceiverModel and TransmitterModel

* Move TX limit logic to TransmitterModel from radio API

* Add TX disable functionality and UI
2026-01-07 12:29:56 +08:00
RML 3d979a613a remove github pages CNAME (#2905) 2026-01-04 09:25:45 +01:00
Harin Lee ee97b51eb3 Add some features to support RX-only mode (#2904)
* Remove duplicate LED control code
* Add TX limit settings and UI
* Fix App Manager to handle non-application menu items
2026-01-03 22:10:34 +01:00
gullradriel 70ac1a03a1 Make stable release (#2895)
* updated submodule
* updated versions
2025-12-21 11:39:47 +01:00
Totoo 2261e53981 fix bit counts (#2894) 2025-12-21 11:38:35 +01:00
Totoo f86d3e51f1 Subcar (#2893) 2025-12-21 11:21:55 +01:00
StarVore Labs f71f19e719 Addition of SSTV RX application (#2888) 2025-12-17 13:14:19 +01:00
RocketGod c53adfc765 Refactor FLEX RX UI to use console for messages (#2890) 2025-12-14 10:08:39 -08:00
gullradriel 7e8ad83537 Update clang format to 18 (#2891)
* move to clang-format-18
* clang-format-18 indendation
2025-12-14 11:26:21 +01:00
RocketGod 472571b1ed Refactor FLEX RX UI to add color cycling and message log (#2885)
Replaces the console with a scrollable menu view for displaying FLEX messages, adds support for cycling text colors, and implements message logging with line wrapping and persistence. Also refactors frequency handling for persistence and updates UI element layout and initialization.
2025-12-12 19:40:15 +01:00
Tim Elfelt 106e56abc3 FLEX pager app (#2883)
* Add FLEX pager support

- Introduced a new FLEX configuration function in baseband_api.
- Added FLEX application view and associated UI elements.
- Implemented FLEX processing logic in proc_flex, including demodulation and message handling.
- Updated CMakeLists to include new FLEX source files and headers.
- Enhanced message system to support FLEX-specific messages and statistics.

This commit lays the groundwork for FLEX pager functionality, allowing for the reception and processing of FLEX messages.

* Fixed baseband and moved app to external with some other fixes.

* Format code

---------

Co-authored-by: RocketGod <57732082+RocketGod-git@users.noreply.github.com>
2025-12-12 17:43:51 +01:00
zxkmm 4129d57c09 fix dead link (#2882)
* fix dead link

* submodule
2025-12-06 07:34:10 -08:00
Totoo caac5e1041 Sonde + map (#2879)
* Update geomap view position on GPS data reception

* Refactor timestamp and temperature/humidity display formatting in SondeView

* Implement battery voltage reading for Meteomodem M20

* Enhance GPS data validation and update OSM zoom handling

- Introduced a new method to validate GPS data in the Packet structure.
- Updated the SondeView to use the new GPS validation method.
- Modified GeoMap to improve handling of OSM zoom levels and ensure consistent usage of real zoom values.
- Adjusted battery voltage calculation for Meteomodem M20 to ensure correct scaling.

* Add serial number extraction for Meteomodem M20 support

* Add support for Meteomodem M20 temperature and humidity readings

* Update log file naming to include timestamp in SondeView

* Add pressure reading support for Meteomodem M20 and update UI

* Update SondeView UI layout and enhance Meteomodem M20 packet handling

* Add vertical speed calculation and display to SondeView

* Fix set_fsk function parameter type for samplesPerSymbol
2025-11-26 19:13:21 +01:00
Totoo 6b02ba6e5d Faster osm (#2874)
Much faster OSM map handler
2025-11-21 09:30:01 +01:00
Totoo c01597baf2 Pocsag manual baud option (#2870)
* baseband part of manual baud control of pocsag

* added manual pocsag baud to ui, to fix #2546

* fix adult toys settings filename
2025-11-18 14:05:27 +01:00
Totoo 43a5163b77 prevent sonde pos to change to 0 when the frame is invalid. fixes #2862 (#2869) 2025-11-18 19:07:40 +08:00
jLynx c46cc431c9 Revert "resolve conflicts merge" (#2860)
This reverts commit 344aa0c741.
2025-11-08 10:21:33 +13:00
jLynx a4d23768c1 Update version.txt (#2858) 2025-11-08 10:13:35 +13:00
gullradriel 344aa0c741 resolve conflicts merge 2025-11-07 21:36:41 +01:00
gullradriel fd7637187a updated version number (#2855)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-11-07 20:52:28 +01:00
Totoo 674b865ec0 Refactor OnTouchEvent functions to return boolean values for better event handling (#2854) 2025-11-05 20:53:23 +01:00
Totoo 00c9c6b40e Fix shift key position (#2850) 2025-10-31 18:25:23 +01:00
RocketGod 113cbc42c9 Add sound effects (#2849)
* Add sound effects

Adding sound effects and volume adjuster in start menu. Sounds are a little clicky but I tinkered a long time and couldn't solve that. Maybe someone else wants to tinker a bit with me.

* Format code
2025-10-30 07:38:45 +01:00
RocketGod 454b8776b6 Refactor gfxEQView UI layout for consistency (#2848)
* Refactor gfxEQView UI layout for consistency

Updated control and graph positioning in gfxEQView to use a standard layout approach, aligning with other applications. Screen dimensions are now used for dynamic placement, and hardcoded values have been replaced for improved maintainability and consistency.

Basically, centered for smaller and larger screens with other visual improvements.

* Fixed screen to Macros - looks good now

Better methods for dynamic screen size
2025-10-30 07:33:38 +01:00
Matej Sochan efdf043d62 fix typo: wps -> wpm in morse_tx (#2847) 2025-10-28 20:55:21 +01:00
gullradriel 0f9d9e589d Revert "Additional debounce control parameters for rotary encoder settings (f…" (#2846)
This reverts commit bf18851b6b.
2025-10-28 22:34:14 +13:00
Totoo 3b57672dda sht4x support,, typo fix (#2843) 2025-10-28 10:14:11 +08:00
Totoo b739dd883c Free up some fw space (#2844) 2025-10-28 08:10:09 +08:00
rmckay bf18851b6b Additional debounce control parameters for rotary encoder settings (for bad/noisy encoders) (#2841)
* Added debounce control options for rotary encoder settings

* ran format-code.sh to adjust whitespace

---------

Co-authored-by: Robert McKay <robert.mckay@ubermorgen.land>
2025-10-27 23:56:27 +01:00
Pezsma 81afec9ea4 Added Adult toy controller external app (#2840) 2025-10-23 13:09:48 +02:00
Totoo 60a5222058 Pacman update (#2837) 2025-10-21 07:37:26 +02:00
Totoo 7041d507ca gracefully handle i2c error (#2838) 2025-10-21 07:37:14 +02:00
Totoo d3373734e9 battleship resizes based on screen size (#2836) 2025-10-18 17:00:50 +02:00
Totoo 200831ad67 fix some bugs due to framesync not fired on stealth mode (#2834) 2025-10-18 13:13:51 +02:00
gullradriel dafe3b7641 update hackrf submodule (#2832) 2025-10-17 19:28:18 +02:00
gullradriel 63634407b6 fix ctrl click link (#2833) 2025-10-17 17:09:57 +00:00
Totoo 9f06ccf9ae Support some space chars in serial commands (fs) (#2828) 2025-10-17 17:05:27 +02:00
Totoo 6782e12a15 Fix morse tx cw+loop (#2827) 2025-10-17 17:03:34 +02:00
Totoo cbbd8103d3 Touch for MenuView and Freqlist (#2829)
* freqman add touch support. solves #1919

* menuview touch support
2025-10-17 21:59:35 +08:00
Pezsma e88ab44f49 Modify Morse code table (#2825)
Fixed morse code table
2025-10-17 10:32:01 +02:00
Pezsma 545dead1ca Added new game: Morse trainer (#2820)
* Added new game: Morse trainer

* code format error fix, and ui alignment
2025-10-16 13:01:28 +02:00
Totoo e3b2a65b39 Oregon fixes (#2822) 2025-10-16 13:00:40 +02:00
Totoo 2f4d222403 New memory address for i2c dev loaded apps (#2817)
* New memory address for i2c dev loaded apps

* better download handling on error

* new hackrf version (added PortapackRf support)

* lock i2cdev while loading apps
2025-10-14 09:45:52 +02:00
Totoo 54d99945f2 fix touch calib on prf (#2816) 2025-10-14 09:03:28 +08:00
RocketGod 65a3c797c6 Blackjack - Dynamic screen for PortaRF (#2814)
Blackjack - Dynamic screen for PortaRF
2025-10-10 22:48:26 +02:00
Totoo 6a81a981aa Fix standalont touch_release event (#2813) 2025-10-10 22:47:17 +02:00
RocketGod 193ac73b34 Doom UI improvements for dynamic screen (#2812)
* Doom dynamic screen and other improvements

Doom dynamic screen and other improvements

* Res divider fix for big screens

* Format code
2025-10-09 22:54:54 +02:00
RocketGod 477d035dce Dino Game Dynamic Screen for PortaRF plus sprite animation improvements (#2811)
* Dino Game Dynamic Screen for PortaRF plus sprite animation improvements

Dino Game Dynamic Screen for PortaRF plus sprite animation improvements

* Format code per usual
2025-10-09 22:31:26 +02:00
RocketGod 1d8f53b49e Space invaders UI improvements for all device screens including new PortaRF (#2810)
* Dynamic screen for PortaRF

Dynamic screen size for new PortaRF device.

* Format code

* Space Invaders improvements for all devices including new PortaRF

Space Invaders improvements for all devices including new PortaRF
2025-10-09 22:30:30 +02:00
Totoo db324a5225 PortaRF battery fix (#2809) 2025-10-09 22:29:53 +02:00
Totoo b2331bf818 clarify battery settings (#2805) 2025-10-06 10:29:17 +02:00
Totoo 4b18ac42e9 full blue batt when charging (#2804) 2025-10-05 17:04:30 +02:00
RocketGod 41dd0c4674 Dynamic screen for PortaRF Tetris (#2803)
* Dynamic screen for PortaRF
* Format code
2025-10-04 19:19:54 +02:00
Totoo 371b6b5079 Multi screen support, with dyn alignment (#2801) 2025-10-03 19:10:10 +02:00
RocketGod 23cabb8b8a Make dynamic screen for PortaRF (#2800) 2025-10-02 11:31:33 -07:00
Totoo b3edaf3cd0 AIS name fix (#2793)
* AIS name fix
2025-09-17 22:40:46 +02:00
Totoo 9afd3a9dca Standalone imp (#2791)
* Moved ui elements to common folder in standalone, and add screen parameters to the api v4

* pass it as a pointer, so if standalone app supports it can change
2025-09-16 20:29:05 +02:00
Totoo d80f41e9d7 Beep on search find something.
Beep on search find something. Solves #2643
2025-09-14 07:22:48 +00:00
Totoo 46d87d95ae Set MIC app's freq step a bit lower to 12.5k. Solves #2728 (#2788) 2025-09-13 12:38:29 +02:00
Totoo 7626dff1ef Add blue bar (Channel) to sonde app. 2025-09-13 12:37:34 +02:00
Totoo f101680521 fixes osm marker display bug #2783 (#2786) 2025-09-13 12:37:01 +02:00
Totoo 325f295ee8 Log function for search app (#2782) 2025-09-07 22:44:04 +08:00
Totoo b15bb59678 Standalone app api v3 (#2772)
Added file io, and updated some ui elements.
Also added Digital Rain standalone app for an example.
2025-09-01 11:50:46 +02:00
Totoo 776c9bc7c9 Fix jammer ranges, clean code (#2776) 2025-08-30 22:22:14 +02:00
Totoo b9ebefc7c7 AIS map speed fix (#2777) 2025-08-30 15:01:29 +02:00
Netro 70ace4be8f BLE RX Lag Improvements (#2770)
* Better handling on filtering to improve lag.
2025-08-27 15:46:53 +02:00
Totoo fe826d5d98 Fix bmp16bit with nocomp header (#2769) 2025-08-27 19:07:08 +08:00
Totoo 4dda7cfaa5 Openstreetmap support (#2765) 2025-08-27 06:37:57 +00:00
zxkmm e6d4081c06 Add game 2048 (#2767)
* _

* rename my name

* fix warnings

* fix warnings
2025-08-26 07:26:53 -07:00
zxkmm 4f738adc70 rename my names in comments (#2768)
* rename my name

* remove wrong line
2025-08-26 19:56:39 +08:00
Totoo d516191ccb Update external.cmake (#2764)
Disable acars ext app, since not working
2025-08-22 10:44:55 +02:00
gullradriel 29bdc539a3 Fix BLE TX warning, regenerate bitmap.hpp (#2762)
* fix 'progress' variable warning, added a TODO
* up to date bitmap.hpp file, soundboard icon
2025-08-19 07:19:46 +02:00
Arne Luehrs 29bba4d0ea Epirb 406 v2 (#2761)
* Adding channel selection to EPIRB receiver

* UI enhancement, Packet error check and color display of error

* code formating
2025-08-18 09:09:37 +02:00
zxkmm a6f886ad0a - (#2759) 2025-08-16 10:50:52 +02:00
Totoo 2d2d9223f2 better rs41 (#2757) 2025-08-15 16:24:07 +02:00
zxkmm 6e33007cb6 move soundbrd app external (#2756) 2025-08-15 13:31:18 +02:00
zxkmm 3f7b59f27e frequency keypad opt (#2755)
* init

* revert changes that caused by batch replace

* revert changes that caused by batch replace

* revert changes that caused by batch replace

* revert submodu;e
2025-08-13 14:25:21 +02:00
Arne Luehrs 375d1ad54e Implementation of EPIRB receiver (#2754)
* Implementation of EPIRB receiver
* Baseband processing of EPIRB signal
* UI to ddecode and display EPIRB message with display on a map
* External application
* External proc element
* Delete CLAUDE.md
2025-08-13 14:24:18 +02:00
Netro 6b05878532 BLE RX/TX Changes (#2752)
* Work on BLE Rx Tx improvements.
* Working on compile size.
* cleanup
* Formatting
* Fixes
* More Improvements + Custom Parsing for Tags
* Moving ERT to external apps.
* Fix Icon.
2025-08-11 07:42:58 +02:00
RocketGod 3983749f11 Update ui_jammer.cpp (#2747)
Update warning to reflect actual range limit of bandwidth.
2025-07-30 20:08:00 +02:00
Totoo 28e2f770e5 fix miss (#2738) 2025-07-12 13:33:22 +02:00
gullradriel 4c5275247e update version (#2735)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-07-11 15:57:04 +02:00
gullradriel a0185d28eb update submodule (#2734)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-07-09 21:33:38 +02:00
RocketGod 2500df310f Add radio settings, new app icon, and other UI improvements (#2732) 2025-07-03 11:03:51 -07:00
Totoo 54f9ff116b AIS map improv (#2725)
* AIS map improv

* format code mismatch with vc
2025-06-29 22:47:18 +02:00
Totoo 47f231ad63 Add blue bar to subghzd+weather (#2724) 2025-06-29 19:31:05 +08:00
Netro f90d3fabce Adding simple FSK Rx Processor. Can be used with New Apps. (#2716)
* Work to allow for unique beacon parsing functions.

* Fixing pull.

* Changes.

* Formatting.

* Fix Copyright

* Update firmware/application/apps/ble_rx_app.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update firmware/baseband/proc_btlerx.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* PR suggestions.

* Fix String.

* FSK Rx Improvements. Works for my custom protocol.

* Fix buffer size.

* Refactor

* Formatting.

* Formatting.

* Fixing compiling, and BLE Rx UI/Performance.

* More improvements.

* Fixing stuck state.

* More stuck parsing fix.

* Combining PR changes.

* Improvements from previous PR.

* Fix dbM calculation relative to device RSSI.

* Formatting.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: TJ <tj.baginski@cognosos.com>
2025-06-29 01:02:12 +02:00
RocketGod 4e276cdc71 Battleship (#2720)
* Made the Battleship 2P 2PP game - FSK is wip
* Using POCSAG
2025-06-28 20:02:29 +02:00
plomek 0eb03373b1 Added 3d printed cases for the H4M (#2715) 2025-06-25 19:16:43 +02:00
Netro d5ea0f0369 BLE Rx Improvements (#2710)
* Work to allow for unique beacon parsing functions.
* Fix Copyright
* Update firmware/application/apps/ble_rx_app.cpp
* Update firmware/baseband/proc_btlerx.cpp
* PR suggestions.
* Fix String.
* Refactor
2025-06-25 19:14:04 +02:00
RocketGod 22cc311447 Update app icons for Space Invaders and Dino Game (#2713) 2025-06-25 16:08:48 +02:00
RocketGod 4fbba205ad Made the Blackjack game (#2712)
* Made the Blackjack game
* Format Blackjack main.cpp
* Changed spade to diamond for dark mode visibility
* Format code
2025-06-25 16:07:56 +02:00
RocketGod 7b91103118 Made the Space Invaders game. Argh matey! (#2709)
* Made the Space Invaders game. Argh matey!
* Format code, sigh.
2025-06-21 08:35:52 +02:00
Tommaso Ventafridda c0aa4a1738 ADSB database update (tools, db) (#2701)
* enhance make_airlines_db tool
* enhance make_icao24_db tool
* update airlinescode (.txt, .db), aircraftdatabase/icao24 (.csv, .db)
2025-06-19 17:28:49 +02:00
Tommaso Ventafridda e26f77ee77 Filemanager: go to parent directory keep track of the right selected … (#2702)
* Filemanager: go to parent directory keep track of the right selected item and page number
* review: avoid unnecessary copies in get_extension
2025-06-19 17:24:38 +02:00
plomek f53262783a Improved FPV_ANALOG.txt FREQMAN file (#2700)
* Improved FPV_ANALOG.txt FREQMAN file
Removed unused or super rare analog fpv bands: U, O, H, D
Added 1.2GHz -1.3GHz channels sometimes used for long range analog fpv


* Corrected and updated the labels to be more consistent.

Corrected the labels to be more consistent.

I also somehow messed up the correct channels because 1.3GHz FPV is not fully standardized, but these channels seem to be the most common.
It should be all correct now.

Example transmitters using those channels:
https://greenchip.com.ua/0-0-1615-2.html
https://flymod.net/en/item/walksnail_vtx_9ch
https://pl.aliexpress.com/item/1005006505365351.html
2025-06-19 05:26:02 +00:00
Netro b456c18008 Modified Text Editor to handle long presses. (#2698) 2025-06-19 07:21:54 +02:00
Tommaso Ventafridda fa4b74fd6f Add vendor name in bluetooth rx app (#2696)
* add macaddress db, add vendor name in bluetooth rx app

* show "missing macaddress.db" instead of unknown if db not found

* bluetooth rx list with colors based on mac vendor

* bug fix
2025-06-16 10:57:58 -07:00
RocketGod 18bc2cf11c Made the Dino Game (#2697) 2025-06-15 09:25:10 -07:00
Tommaso Ventafridda 20f28c8331 add dark theme (#2695) 2025-06-13 18:34:20 +02:00
Tommaso Ventafridda ea38a0fe48 Add 1ms hop option to hopper app + 0ms (freeze UI) (#2692) 2025-06-12 11:54:37 +02:00
RocketGod fb2e576b34 Super secret dont look (#2690)
* Add new jammer modes

Overview

This PR enhances the PortaPack Jammer app by introducing eight new signal types, ported from my Flipper Zero RF Jammer app (https://github.com/RocketGod-git/flipper-zero-rf-jammer). These modes expand the app's capability to disrupt a wide range of RF communication protocols, from analog radios to modern digital systems. The implementation preserves the original app structure, resolves namespace conflicts, and ensures compatibility with the Mayhem firmware.

New Modes

The following modes have been added to the options_type in ui_jammer.hpp, with corresponding signal generation in proc_jammer.cpp:

Noise: Generates broadband white noise to interfere with analog and digital signals (e.g., Wi-Fi, Bluetooth, key fobs). Highly effective for overwhelming receivers across a frequency range.

Sine: Produces a continuous, unmodulated sine wave to jam narrowband receivers, ideal for analog FM/AM radios or telemetry systems.

Square: Emits a harmonic-rich square wave, disrupting digital protocols (e.g., OOK, ASK) and systems sensitive to sharp transitions, such as remote keyless entry.

Sawtooth (Experimental): Generates a sawtooth wave with a unique harmonic profile, useful for testing interference against PWM-based or niche analog systems.

Triangle (Experimental): Creates a triangle wave with minimal harmonics, suitable for exploratory jamming of narrowband systems or receiver linearity testing.

Chirp: Outputs a rapid frequency-sweeping chirp signal, effective against frequency-hopping and spread-spectrum systems (e.g., some Wi-Fi, Bluetooth, or military radios).

Gauss: Generates Gaussian noise to mimic natural interference, targeting digital systems like GPS or data links by degrading signal-to-noise ratios.

Brute (Experimental): Transmits a constant maximum-amplitude signal to saturate simple receiver front-ends, useful for brute-force jamming of basic analog devices.

* Super secret

* You gotta get (Get) that (That) dirt off your shoulder
2025-06-10 13:36:26 -07:00
Tommaso Ventafridda 1d79e30d4b Add all jammer modes in hopper app (#2691)
Added all modes that jammer app supports in hopper app.
2025-06-10 06:54:59 -07:00
Totoo be372e12bc Externalize widget (#2688) 2025-06-09 12:52:40 +02:00
Totoo 00853f526a Gfx widget and Radio (#2685)
* widgetize
* gfx and Radio improvement
* format + handle not wfm visual states
* wf or gf
2025-06-07 11:43:07 +02:00
Petro Danylevskyi 37ca7a601c Rename looking glass preset for clarity and consistency (#2686) 2025-06-07 11:27:25 +02:00
RocketGod b50d18eafc Radio app improvements (#2680) 2025-06-05 21:25:09 +02:00
Totoo 1070d951e6 80mhz jammer range (#2682)
Looks great 😎🤘🚀
2025-06-05 07:08:03 -07:00
Totoo a980fe21ac removed some std stuff only used here (#2681) 2025-06-05 11:32:26 +02:00
Alien 9e96715176 Added ability to enter custom tone values in Morse app (#2679)
* Added ability to enter custom tone values in Morse app

Added the ability to type in a custom tone value in the morse TX app (issue#2582)

*Click on the tone field to open a keyboard for entering in a desired value between 100hz - 9999hz.

*Maintains original step value of 20 when scrolling the rotary wheel.

* Update ui_morse.cpp

Replaced std::to_string with to_string_dec_uint

* Moved tone_input_buffer init to in-class
2025-06-05 05:21:44 +00:00
Brumi-2021 fecfe8b8fc Clean_LCD_beat_in_NOAA_Rx_App (#2678) 2025-06-03 21:10:58 +02:00
zix dfdd52c667 trivial apps folder movement (#2677) 2025-06-03 12:13:44 +02:00
Brumi-2021 695e6d19f4 Adding_BPF_selection_to_the_NOAA_APT_signal (#2675)
* Adding_BPF_selection_to_the_NOAA_APT_signal
* comments, spell mistake .
2025-06-02 14:40:13 +02:00
Totoo 6e9ecf8e7b jammer fix (#2676)
* jammer fix
2025-06-02 19:58:53 +08:00
RocketGod fd158e873a Jammer improvements (#2674)
* Add new jammer modes
Overview:
This PR enhances the PortaPack Jammer app by introducing eight new signal types, ported from my Flipper Zero RF Jammer app (https://github.com/RocketGod-git/flipper-zero-rf-jammer). These modes expand the app's capability to disrupt a wide range of RF communication protocols, from analog radios to modern digital systems. The implementation preserves the original app structure, resolves namespace conflicts, and ensures compatibility with the Mayhem firmware.

New Modes

The following modes have been added to the options_type in ui_jammer.hpp, with corresponding signal generation in proc_jammer.cpp:

Noise: Generates broadband white noise to interfere with analog and digital signals (e.g., Wi-Fi, Bluetooth, key fobs). Highly effective for overwhelming receivers across a frequency range.

Sine: Produces a continuous, unmodulated sine wave to jam narrowband receivers, ideal for analog FM/AM radios or telemetry systems.

Square: Emits a harmonic-rich square wave, disrupting digital protocols (e.g., OOK, ASK) and systems sensitive to sharp transitions, such as remote keyless entry.

Sawtooth (Experimental): Generates a sawtooth wave with a unique harmonic profile, useful for testing interference against PWM-based or niche analog systems.

Triangle (Experimental): Creates a triangle wave with minimal harmonics, suitable for exploratory jamming of narrowband systems or receiver linearity testing.

Chirp: Outputs a rapid frequency-sweeping chirp signal, effective against frequency-hopping and spread-spectrum systems (e.g., some Wi-Fi, Bluetooth, or military radios).

Gauss: Generates Gaussian noise to mimic natural interference, targeting digital systems like GPS or data links by degrading signal-to-noise ratios.

Brute (Experimental): Transmits a constant maximum-amplitude signal to saturate simple receiver front-ends, useful for brute-force jamming of basic analog devices.

* Fixed and made brutal.

This PR introduces user-focused improvements to the Jammer App in the HackRF PortaPack Mayhem Firmware, enhancing usability and flexibility. The changes address specific user requirements for a more intuitive default configuration, continuous waveform support, and dynamic setting adjustments during transmission.
2025-05-31 22:16:57 +02:00
Totoo f90bd44617 Detector RX ext app (#2673) 2025-05-28 19:19:47 +08:00
Totoo 2602c417be ui new coord system examples and macros (#2672) 2025-05-27 17:37:41 +08:00
Totoo aa1264cf91 getres cmd (#2671) 2025-05-27 15:18:33 +08:00
Brumi-2021 c79abccffd wfm_filters_GUI_name_std (#2668) 2025-05-25 20:04:08 +02:00
Brumi-2021 d3c9dc1f10 Storing_selected_NOAA_filter_in_settings_file (#2665)
* Storing_selected_NOAA_filter_in_settings_file

* format_issues
2025-05-24 18:18:24 +02:00
Totoo 3c8335c942 Audio to right (#2664)
* r.align
2025-05-23 12:27:57 +02:00
Totoo ac31f4781c screen max width fixes (#2663)
* max width fixes

* format
2025-05-23 08:42:19 +02:00
Totoo c80cae3638 fix png part 2025-05-23 00:23:35 +08:00
Totoo a1d7cf2b86 Prepare for display orientation part 1 (#2661) 2025-05-22 23:24:53 +08:00
zix 6f6d863a14 refactor the serial log logic of BLE Rx (#2660) 2025-05-21 07:07:26 -07:00
RocketGod 43a1bc0445 Jammer app add modes (#2659)
* Add new jammer modes

Overview

This PR enhances the PortaPack Jammer app by introducing eight new signal types, ported from my Flipper Zero RF Jammer app (https://github.com/RocketGod-git/flipper-zero-rf-jammer). These modes expand the app's capability to disrupt a wide range of RF communication protocols, from analog radios to modern digital systems. The implementation preserves the original app structure, resolves namespace conflicts, and ensures compatibility with the Mayhem firmware.

New Modes

The following modes have been added to the options_type in ui_jammer.hpp, with corresponding signal generation in proc_jammer.cpp:

Noise: Generates broadband white noise to interfere with analog and digital signals (e.g., Wi-Fi, Bluetooth, key fobs). Highly effective for overwhelming receivers across a frequency range.

Sine: Produces a continuous, unmodulated sine wave to jam narrowband receivers, ideal for analog FM/AM radios or telemetry systems.

Square: Emits a harmonic-rich square wave, disrupting digital protocols (e.g., OOK, ASK) and systems sensitive to sharp transitions, such as remote keyless entry.

Sawtooth (Experimental): Generates a sawtooth wave with a unique harmonic profile, useful for testing interference against PWM-based or niche analog systems.

Triangle (Experimental): Creates a triangle wave with minimal harmonics, suitable for exploratory jamming of narrowband systems or receiver linearity testing.

Chirp: Outputs a rapid frequency-sweeping chirp signal, effective against frequency-hopping and spread-spectrum systems (e.g., some Wi-Fi, Bluetooth, or military radios).

Gauss: Generates Gaussian noise to mimic natural interference, targeting digital systems like GPS or data links by degrading signal-to-noise ratios.

Brute (Experimental): Transmits a constant maximum-amplitude signal to saturate simple receiver front-ends, useful for brute-force jamming of basic analog devices.

* Add new jammer modes

Overview

This PR enhances the PortaPack Jammer app by introducing eight new signal types, ported from my Flipper Zero RF Jammer app (https://github.com/RocketGod-git/flipper-zero-rf-jammer). These modes expand the app's capability to disrupt a wide range of RF communication protocols, from analog radios to modern digital systems. The implementation preserves the original app structure, resolves namespace conflicts, and ensures compatibility with the Mayhem firmware.

New Modes

The following modes have been added to the options_type in ui_jammer.hpp, with corresponding signal generation in proc_jammer.cpp:

Noise: Generates broadband white noise to interfere with analog and digital signals (e.g., Wi-Fi, Bluetooth, key fobs). Highly effective for overwhelming receivers across a frequency range.

Sine: Produces a continuous, unmodulated sine wave to jam narrowband receivers, ideal for analog FM/AM radios or telemetry systems.

Square: Emits a harmonic-rich square wave, disrupting digital protocols (e.g., OOK, ASK) and systems sensitive to sharp transitions, such as remote keyless entry.

Sawtooth (Experimental): Generates a sawtooth wave with a unique harmonic profile, useful for testing interference against PWM-based or niche analog systems.

Triangle (Experimental): Creates a triangle wave with minimal harmonics, suitable for exploratory jamming of narrowband systems or receiver linearity testing.

Chirp: Outputs a rapid frequency-sweeping chirp signal, effective against frequency-hopping and spread-spectrum systems (e.g., some Wi-Fi, Bluetooth, or military radios).

Gauss: Generates Gaussian noise to mimic natural interference, targeting digital systems like GPS or data links by degrading signal-to-noise ratios.

Brute (Experimental): Transmits a constant maximum-amplitude signal to saturate simple receiver front-ends, useful for brute-force jamming of basic analog devices.
2025-05-19 19:16:28 +02:00
OpenSourceSDRLab 0d02f431c7 Upload the PCB file of PortaPack H4 and update the schematic file (#2657)
* Create README.txt

* Update README.txt

* Add files via upload

The gerber files of the portapack h4.

* delete

* Upload the gerber files for H4

* Create README.txt

This is the V1.0 version PCB file of PortaPack H4.

* Upload the pcb file for H4

* Update README.txt

* Update LCD_TF_Schematic.pdf
2025-05-19 23:40:02 +12:00
Brumi-2021 322b06d670 Improve_RF_sensitivity_NOAA_signal (#2654) 2025-05-18 12:49:24 +02:00
gullradriel db187301ab adding 10Hz and 50Hz to freqman_steps (#2652) 2025-05-11 21:36:47 +02:00
horrordash 84cb32ee14 slightly improved ads-b receiver module (#2649)
* slightly improved ads-b receiver module:
* fix Heading, Speed and Vrate decoders
* decode more ModeS messages
* log all ModeS messages (except DF11)
* fix formatting (clang-style); advice on data alignment taken into account
* ADS-B module: convert Indicated AirSpeed to True AirSpeed if altitute is known
* ADS-B rx module: replacing floating point with integer arithmetic
2025-05-11 21:11:15 +02:00
Brumi-2021 61bd696b69 Explicit_naming_wefax_NOAA_and_small_addition (#2651) 2025-05-11 21:10:48 +02:00
Totoo db65ae192a Noaa apt decoder (#2648) 2025-05-11 20:24:52 +02:00
RocketGod 7553dafffc Update ui_tetris.cpp (#2650)
Start "I" tetromino a bit higher and block rotate if it will cause out of bounds collision.
2025-05-10 13:08:41 +02:00
sommermorgentraum cfdd1a4c22 remove dead code (#2647) 2025-05-07 06:17:12 +02:00
gullradriel d4342e01a4 going back to WFM as main baseband in main.cpp as NFM is now making the apps crashing. Looks like last additions to WFM made it bigger. (#2646) 2025-05-06 16:50:59 +08:00
Brumi-2021 4342f5c0ee Adding_WFM_AM_mode_to_Audio_App (#2644)
* Adding_WFM_AM_mode_to_Audio_App
* more precise values for cos and sin theta, fix sen_theta to sin_theta
* fix sen_theta to sin_theta
2025-05-05 17:48:01 +02:00
Richard bd781ce37b Fix for #2538 (#2635)
* Fix for #2538

Fix for #2538
Added on_bandwidth_changed Callback to ui_transmitter.hpp
Modified the field_bw.on_change lambda in the TransmitterView constructor to trigger the on_bandwidth_changed callback
Connected the Callback in ui_siggen.cpp

I am not a C++ programmer so this change was proposed by Gemini AI.

I have built and tested the App and it works as expected and I don't think the change will have any unexpected side effects.

* Fix clang issues

Fix clang issues

* Update ui_transmitter.cpp

typo

* Revised change

The proposed change mirrors the way a change to the frequency (on_edit_frequency) is triggered in ui_siggen by the tx_view.
The bw parameter is not passed because it is stored in _setting in the tx_view and will be read by update_config.
A change to the bw is not checked against auto_update to keep its behaviour consistent with a change to the gain, amplitude or frequency.

* Make changes to the channel_bandwidth dynamic whist playing

Behaviour of channel bandwidth is now consistent with frequency, amp and gain.

* comment edit

* revert hackrf submodule checkpoint to the repo

* comment

---------

Co-authored-by: zxkmm <zxkmm@hotmail.com>
2025-05-02 04:19:41 +00:00
Erwin Ried c2e05dea48 Update README.md
Links were dead for opensourcesdrlab
2025-04-25 13:01:54 +02:00
sommermorgentraum de3212cba5 spectrum cursor opt again (#2634)
* spectrum cursor opt

* fmt

* remove blink

* remove End event

* cleanup
2025-04-22 21:14:31 +08:00
sommermorgentraum 9aff3f4121 Fix bug that created by PR "Added menu group for transceivers" (#2630) 2025-04-17 07:42:05 +02:00
sommermorgentraum 6b6a00d511 Support IPS screen & brightness set for IPS screen (#2629)
* _

* format

* format

* format
2025-04-17 07:40:28 +02:00
Oleg Belousov 806219f46e Added menu group for transceivers (#2623)
* Added menu group for transceivers

* Reorder apps icons
2025-04-15 16:46:49 +08:00
Luca a153cbf30b Adding 8.33KHz spacing (#2628)
* Adding 8.33KHz spacing
* remove extern options_db_t freqman_steps which is now brought by ui_receiver.hpp
* use freqman db steps instead of static array
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-04-14 18:59:50 +00:00
sommermorgentraum 584af02dba Touch on waterfall to set cursor pos (#2624)
* init

* fix typo that found by Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-14 20:47:41 +02:00
gullradriel f344a1ca40 fix comments (#2622) 2025-04-11 11:48:07 +00:00
gullradriel 511675c9a1 Fix default waterfall file (#2621)
* correct default settings from file
* correct colors names
2025-04-11 19:30:18 +08:00
haruk 7bbd608c90 Delete dead code in ble_rx_app.cpp (#2620)
Duplicate include on line 24 removed.
2025-04-11 10:49:09 +00:00
Oleg Belousov f941ae871a Custom waterfall colors (#2617)
* Custom waterfall gradient
* Installing a custom waterfall gradient via fileman
* default file for user friendly swap
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-04-11 10:46:04 +00:00
RocketGod eff214cc79 Enhance Graphic Equalizer Visualization with Improved Frequency Bands and Response (#2614) 2025-04-10 22:16:18 +02:00
Oleg Belousov 319ed3e7b4 Improved make_bitmap tool (#2615) 2025-04-10 12:21:19 +08:00
RocketGod 288f6bd517 GFX EQ App (#2607)
* Make the beginnings of rf3d
* Name change...
* Add mood button
* Remove forced amp settings and add persistent user settings
* Fix options bar layout and SettingsManager
* Make the background paint to black again after opening fq modal
* fix audio/mod/settings and cleaned unneeded parts
* Mapped bars to audio spectrum
* Improved frequency response... still needs work i think
* add on_freqchg to be able to answer to serial frequency change command
* Made calculations for 14 bars to fit screen and little adjustments
* Visual improvements
Co-authored-by: gullradriel
2025-04-09 14:27:51 +02:00
sommermorgentraum 809abb6842 user can disable battery change hint (#2612)
* theme fix again

* _

* _
2025-04-06 10:24:50 +08:00
sommermorgentraum 31082c63af theme fix again (#2611)
* theme fix again

* _
2025-04-05 16:58:12 +08:00
gullradriel ff14008b43 prevent long life var for audio app - AM (#2610)
* static vars so no external linkage is possible

* persistent settings and no more global living variables

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-04-04 21:43:26 +08:00
sommermorgentraum 1377516dce make the ptext_prompt func can define which keyboard to enter (#2608)
* _

* format

* use define
2025-04-04 15:41:12 +02:00
Brumi-2021 4162820409 solving_Audio_App_AM_GUI_Problem_issue_2604 (#2609) 2025-04-04 14:04:06 +02:00
sommermorgentraum 4bbe1175c1 add tap tempo to metronomic app (#2605)
* _

* format

* fix new tree in Arch
2025-04-02 22:15:12 +02:00
sommermorgentraum ecd1a217d7 Issue template again (#2602)
* test1

* test2
2025-03-27 10:12:43 +01:00
RocketGod 335cace137 Remove RF TX and use PATX baseband for audio --> speaker out only (#2601)
* Force 433.92 and remove metadata check

We already know the frequency for all files so don't need a million metadata files to match.

* Remove RF TX. Improve PATX baseband.

* code formatting of course
2025-03-27 12:33:24 +08:00
Mark Thompson dbd708a1c7 Added more Wefax stations (#2600)
* Delete sdcard/FREQMAN/WEFAX.TXT

Replace with 2 files

* Add more WeFax frequencies

* Update comments

* Oops wrong folder

* Oops wrong folder

* Change comments
2025-03-26 11:43:17 -05:00
RocketGod 4d842b5fae Force 433.92 and remove metadata check (#2599)
* Force 433.92 and remove metadata check: we already know the frequency for all files so don't need a million metadata files to match.
* Variable fixes and move to header
2025-03-26 15:56:27 +01:00
Erwin Ried e24f1b131d Create CONTRIBUTING.md (#2598) 2025-03-26 19:53:41 +08:00
Erwin Ried 3f2c7351f1 Create SECURITY.md 2025-03-26 12:22:18 +01:00
Erwin Ried afc75f6cda Create pull_request_template.md 2025-03-26 12:20:54 +01:00
Erwin Ried 69b17faddf Create CODE_OF_CONDUCT.md 2025-03-26 12:09:51 +01:00
gullradriel 2c118ae584 Remove unneeded AMFM support in those apps (#2597) 2025-03-26 11:58:11 +01:00
gullradriel 75c34747cb Externalize level (#2596)
* removing ability to focus on RSSI bars and to launch level app, until a solution to launch external apps from internal ones is given
* externalize Level app
2025-03-26 18:20:01 +08:00
gullradriel 7754c0f37f Externalize scanner (#2589)
* externalize scanner
* NFM as main baseband as it's the biggest used one
* fix modulation bug introduced with AMFM
2025-03-26 10:42:24 +01:00
sommermorgentraum 37cc35d3c6 move default splash into sdcard (#2595)
* move bmps to sdcard
* remove unrelated files
* gitignore
* credit
* format
2025-03-26 10:34:00 +01:00
gullradriel 8a7aa9c0f8 remove not yet enabled screening app (#2594) 2025-03-25 20:20:52 +08:00
Totoo 22d6e34cd6 remove dead code (#2593) 2025-03-25 12:33:28 +01:00
gullradriel 2229381ce3 Externalize dump pmem (#2590)
* initial commit

* clang

* memory icon

* text output and exit button, FOCUS OVERRIDE TO AVOID COMPILATION ERROR

* modem and data_structure_version accessor

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-03-24 18:14:08 -05:00
RocketGod 4a63bdd7a0 Fixed the I Tetromino rotation using SRS (Super Rotation System) (#2588) 2025-03-24 14:43:12 +01:00
Brumi-2021 21773cc3c3 Adding_Waterfall_ZOOM_x2_in_AM_modes_Audio_App (#2586)
* adding zoom_factor to app settings
* separated zoom_factor settings for AM and AMFM
* fix order so zoom factor is also applied correctly on modulation change
* fix zoom not applied when changing bandwidth
* temporary disable the Scanner so we are not breaking the nightly. Until we are choosing to finally remove it or find a better solution

---------

Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-03-23 22:08:12 +00:00
RocketGod 4b000c8da6 Tetris: Combined cpp files. Helper files into hpp. Dark mode. Encoder on. (#2587) 2025-03-23 21:43:30 +01:00
sommermorgentraum 20c64c98bd naming space (#2585) 2025-03-23 10:44:45 +01:00
RocketGod 139ade0670 Combine cpp, move helpers to hpp (#2584) 2025-03-23 14:04:01 +08:00
RocketGod c5b7326d4b Combined cpp files, stuffed helper files in hpp, updates start and game over screens (#2583) 2025-03-23 13:29:05 +08:00
sommermorgentraum a60169c7f1 issue template fine tune (#2579) 2025-03-21 17:43:43 +01:00
sommermorgentraum 0ce6ea8318 stopwatch opt (#2578)
* stopwatch opt

* comments

* format

* fxi ms display when user tune display level
2025-03-21 10:34:10 +08:00
RocketGod 4ecc9d04fe Put ticker class and pp_colors in hpp file in namespace and remove helper files (#2577) 2025-03-20 08:08:21 +01:00
Totoo d73db884f0 Added wefax offset to audio app too. (#2572)
* added wefax offset to audio app too.

* moved from head to cpp
2025-03-19 23:03:04 +01:00
Benjamin Møller ff2f27e297 Add icon for the doom game (#2574)
* Add icon for the doom game
* Add b/w .png to convert as doom icon
2025-03-19 17:58:34 +00:00
gullradriel 443ffd8ad3 Add WEFAX freqman file (#2567)
* Add WEFAX freqman file
* fix bad escaped spaces and unicode characters
2025-03-19 18:45:35 +01:00
sommermorgentraum 403c362702 fix external app address list (#2573) 2025-03-19 18:45:11 +01:00
RocketGod 6aa29e265b Update Doom main.cpp for better icon (#2575) 2025-03-19 18:30:11 +01:00
RocketGod 40cf2b3f8c Doom - Mayhem Edition (#2570)
* Doom - Mini Mayhem version

* Update ui_doom.cpp

* Update ui_doom.cpp

* Update ui_doom.cpp

* Update ui_doom.cpp

* Update ui_doom.cpp

* Update ui_doom.cpp

* Update ui_doom.cpp

* Update ui_doom.cpp

* Doom - Mayhem Edition

Made the Doom - Mayhem Edition game. Some little bugs but good enough for nightly.

* Code formatting. Forgot like always.

* give more initial ammo until i put ammo around the maze to collect
2025-03-19 14:16:12 +08:00
Totoo 4aa5fc1fbe WeFax rx ext app (#2566)
* wf3

* Ookbrute (#2354)

* Revert "Ookbrute (#2354)"

This reverts commit abb8143eec.

* fix

* test edition

* re enable ble

* re enable ert

* steal amfm stuff

* something happens

* save bmp on start btn

* kinda works

* exit crash fixed

* redline, remove some hardcoded

* removed cpu killer red line, and some fixes

* simplify #1

* seems ok. time to improve

* added hidden freq offset to receiver model, so wefax can be set to the "correct" freq without users needs to substract 300 hz

* badly implemented sync detection, and disabled it.

* fix for fix

* fixes

* fix offset to real life off

* no line on freq enter

* fixes
2025-03-18 18:31:40 -05:00
Erwin Ried 717d615f4f Update README.md
Other link broken
2025-03-18 10:46:19 +01:00
Erwin Ried dce4e91e41 Update README.md
Fixing opesourcesdrlab link
2025-03-18 10:36:34 +01:00
Brumi-2021 dc2f3260f3 zooming_spectrum_AMFM_mode (#2565) 2025-03-16 18:35:03 +01:00
sommermorgentraum 2d85e73f0d OokBrute app opt (#2561) 2025-03-11 21:17:47 +01:00
Mark Thompson 6ee7270db7 Setting for faster Button Repeat delays (#2559)
* Setting for faster Button Repeat delays

* Tweak fast delay times

* Tweak delay times

* Added description line and tweaked delay again
2025-03-11 09:13:41 -05:00
Mark Thompson b4112f0c04 Remove deprecated QR Code code (#2558) 2025-03-10 19:13:57 +00:00
Mark Thompson e920932886 Snake icon change (#2557) 2025-03-10 12:37:30 -05:00
Mark Thompson 081b11ad41 Breakout icon change (#2556)
- Changed Breakout icon
- Changed Tetris icon color to green
2025-03-10 17:25:49 +01:00
gullradriel adc23315a2 updated bitmaps for speaker icon (#2555)
* updated bitmaps for speaker icon
* removed opera cake icon that was added by mistake
* adding missing Game menu source png
* cyan for Game menu
* regenerated icons
2025-03-10 12:33:33 +01:00
Mark Thompson 084b88564b Stopwatch external app (#2553) 2025-03-09 22:03:41 +01:00
dark-juju 24e9504688 Moved speaker 1px to the right to match muted icon variant. (#2554) 2025-03-09 21:02:48 +00:00
sommermorgentraum c670ca59a8 rename rand pwd app (#2552)
* _

* remove wip code that forgot to add in old branch
2025-03-08 21:38:12 -06:00
RocketGod 2ca8ea6342 Nested Debug menu into Utilities menu to tidy Home screen (#2551) 2025-03-08 18:05:33 -06:00
RocketGod eb50b790c2 Snake (#2549)
* Create the Shopping Cart Lock app

Will demonstrate tomorrow. Don't merge until I do 😁

* Fixes for HTotoo's comments 😎

* Improved audio the best I can.

If nobody has any ideas to further improve high frequencies of the audio, the hardware may not be capable. I still need to check with line-out to better speaker to make sure it's not just the speaker, but it shouldn't be.

* Compared against baseband_api.cpp - matched some things better but still playback seems to be missing higher fq sounds

* renamed wav files to a more specific / less generic name

* indentation + using variables instead of litteral names for wav files to use

* indentation

* Made a Snake game - enjoy

* Code formatting. I always forget.

* move to keep sort order

* Update external.ld

Sorry I should have also asked if there was any reason that address ranges 0xADDA0000--0xADDD0000 were skipped in external.ld.  I assumed there wasn't so I changed it to be consecutive using the same 0x10000 step as the other modules.  If there is any reason to skip them then we should add a comment to note it.  Of course these are all just temporary address values used for linking and get overwritten by a kludgy "search & replace" during the build process.

Resolves enhancement request #764

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
Co-authored-by: Mark Thompson <129641948+NotherNgineer@users.noreply.github.com>
2025-03-08 08:33:27 -06:00
gullradriel 1df318355b Wefax warning fix modulation fix (#2543)
* changed order of modulations, changed case to avoid capture
* added missing AMAudioFMApt mode to dump pmem
* reorder demod, adding missing ones, fix warnings
* removed uneeded 'previous_modulation', renamed WFAX to AMFM to match other places
* removing uneeded 'previous_modulation' uneeded check in change_modulation
* move capture at the end so AMAUdioFMApt is matching the other arrays for position 4
* added AMFM to Recon Level and Scanner

* clang + more details in some comments

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-03-07 17:28:11 +01:00
RocketGod a17995fd2b Moved games to new game menu (#2544)
* Moved games to new game menu
* There's enough games to have a menu now and I plan to make more. Having them in "Utilities" made no sense.
2025-03-06 22:27:25 +01:00
sommermorgentraum ee472e1ed2 waveform fix 3 and trivial change (#2540) 2025-03-05 17:53:41 +01:00
sommermorgentraum e933c8b498 Trivial textual change about missing SD content (#2542)
* _

* _
2025-03-05 15:37:53 +01:00
RocketGod 18bebbfb6d Breakout - The Portapack remake game rises from the pirate's lair (#2541)
* Breakout - The Portapack remake game rises from the pirate's lair

* Fixes

* Added a signature
2025-03-05 14:43:30 +08:00
Brumi-2021 52c3760e90 Adding Wefax demodulation mode inside Audio App (#2539)
* Adding_new_WFAX_GUI_mode_Audio_App

* Wefax_APT_demodulation_structure

* Solving REC Apt signal.wav from WFAX

* clang format issues

* correcting comments
2025-03-05 11:27:18 +08:00
Davide Rovelli b6e498a6d3 Added different modulations in signal generator
* Added DSB, AM 100% mod index and AM 50% mod index. 
* Changed UI.
* Added pulsed CW
2025-03-03 22:17:56 +01:00
sommermorgentraum ddf7f7ccb5 Allow disable/enable waveform in Audio app to remove decoding problem on some frequencies 2025-03-03 22:16:03 +01:00
sommermorgentraum fcdccdea85 add cursor to font viewer app (#2528) 2025-02-22 13:37:06 +01:00
sommermorgentraum 5e55444f19 Support Bug Key AKA Auto Key for OOK Editor app (#2523)
* _

* some final tune

* textual

* rename following gull's suggestion
2025-02-19 13:51:09 +08:00
sommermorgentraum 73f7f84718 Playlist editor (#2506)
* make both exist
* format
* fix focusing issue
* add example hopper payload
* fix compiler err
* clean up
* correct linker script addr
* lint
* PoC
* unknown: write_line issue
* clean up
* merge
* fix read line
* remove debug code
* fix english
* support new file
* support enter delay
* fix crash
* remove debug code
* some final tune
2025-02-18 22:05:40 +01:00
sommermorgentraum 7ad4ad99dd Add widget preview tool (#2520)
* PoC

* opt

* opt
2025-02-16 23:42:06 +08:00
sommermorgentraum 200f10397b Add new app "hopper" app. (#2482)
* make both jammer and hopper exist
* add example hopper payload
* example files
* swap scanner and recon app location
2025-02-14 20:10:01 +01:00
E.T. 9b352f45a2 fix docker build warnings in dockerfile-nogit (#2518) 2025-02-13 07:29:20 +01:00
Lerold 51c445054e Moved country-specific FREQMAN files to the separate repository set up for it. (#2517) 2025-02-10 11:02:55 +01:00
RocketGod 02586101e6 Add modal to turn off screen when charging is detected (#2514) 2025-02-08 22:09:40 +01:00
Gaurav Chaturvedi b66d8b1aa1 Removing vim swap files (#2503)
* Removing vim swap files
* Added vim swap file to .gitignore
2025-01-28 16:31:11 +01:00
gullradriel 8dc3851b55 externalize wipe sdcard (#2502) 2025-01-27 22:10:33 +01:00
gullradriel aef7c2be93 fix baseband (#2501) 2025-01-26 22:23:29 +01:00
gullradriel 4e38dc4d3e put back app in 'Utilities' (#2500)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-01-26 22:15:02 +01:00
Davide Rovelli 520ad97f78 Added different modulations in signal generator (#2492)
* Added DSB, AM 100% mod index and AM 50% mod index. Changed UI.
2025-01-26 21:41:11 +01:00
Lucas C. Villa Real 5bd208bca1 APRS: add frequency settings for Brazil (#2494)
* Add frequency settings for Brazil, named 'BR' that tunes to 145.570 MHz.
* Also added: Japan: 144.640 MHz (JAP), Thailand: 144.900 MHz (THA), Philippines: 144.740 MHz (PHI)
* Reordered list by increasing frequency
Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
2025-01-26 21:39:38 +01:00
gullradriel a65ef3ce2e Externalize antenna calc and wav view (#2498)
* externalize antenna calc and wav view
* Added a tool to check if all the pictures in graphics are used in internal apps
2025-01-26 21:36:21 +01:00
gullradriel b2bb37af74 Flipper tx: use file_path, example file (#2496)
* added subghz_dir
* use subghz_dir from file_path
* example file
2025-01-26 17:46:43 +00:00
gullradriel 54ad82a423 fix fallthrough warning (#2497) 2025-01-26 16:27:29 +01:00
gullradriel e463405c77 fix cmake_minimum_required to 3.16 (#2499) 2025-01-26 16:26:02 +01:00
sommermorgentraum c413f925a9 Add fast flash script for sdcard switch hardware (#2480) 2025-01-20 12:43:57 +01:00
Erwin Ried 9e1eb31150 Update README.md
Discord badge was broken, switching to shields.io
2025-01-20 09:22:40 +01:00
sommermorgentraum 7eb08f34cb Fix encoder setting p.mem issue (#2475) 2025-01-09 15:56:14 +01:00
Erwin Ried 2de1f8e6bf Update README.md
Lab401 link with our redirection
2025-01-09 10:05:27 +01:00
sommermorgentraum 55db0e8c87 Add the feature to decide rotate direction of encoder (#2472) 2025-01-08 21:02:05 +01:00
gullradriel dfa35b4290 added discord server information (#2471) 2025-01-07 14:56:08 +01:00
gullradriel 0124051c4f fix for empty text prompt crash (#2468) 2025-01-07 13:46:51 +01:00
gullradriel 65f301399f disabling button on main menu, change labels and add 'icons' (#2466)
* disabling button on main menu, change labels and add 'icons'

* fix reverse order of buttons

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-01-04 10:50:53 +08:00
sommermorgentraum 97889494cf rename bitmaps into bmp 2025-01-03 14:33:30 +01:00
Erwin Ried 32b35b1507 Update README.md 2025-01-02 15:27:13 +01:00
quantum-x fccff0484b Update README.md (#2456)
Added a link to Lab401.com as a purchase option for EU customers.
(Lab401 was added as the EU exclusive distributor for the H4M - https://opensourcesdrlab.com/pages/distributors)
2025-01-02 15:22:58 +01:00
OpenSourceSDRLab d94afe364b The gerber files of the portapack H4 (#2463)
* Create README.txt

* Update README.txt

* Add files via upload

The gerber files of the portapack h4.

* delete

* Upload the gerber files for H4
2025-01-02 09:31:28 +13:00
Erwin Ried 2e4db3dd27 Update README.md
New metal case link
2024-12-31 22:39:35 +01:00
E.T. 97d3edbde7 docker improvements (#2455) 2024-12-31 11:27:38 +01:00
gullradriel 4c18b80e42 Navigation buttons (#2458)
* regenerate bitmap data
* pagination in submenu
* using little font so we are not eating menu buttons
2024-12-31 10:01:03 +08:00
Mark Thompson 01034af077 Renamed parameters in rx_capture.ini file (#2452)
* Rename settings in file to match screen
* Renamed variables for hopefully better clarity
2024-12-29 18:29:31 +01:00
Mark Thompson 57ce978bab Remember previous capture settings (#2450) 2024-12-29 09:21:08 +01:00
Totoo 854ac514cb M10 additional parser (#2448) 2024-12-28 12:47:58 +01:00
sommermorgentraum b47a5fe0c8 Add + - buttons in Encoder dial settings (#2447) 2024-12-27 22:04:11 +01:00
sommermorgentraum af362600ef app manager (#2442)
* fix unset autostart
* clean up - add comments to prevent misleading
* move the app to external and with necessary changes
* replace autostart app
2024-12-27 12:01:33 +01:00
hackrfstuff bad57d1391 Fake brightness reimplementation (#2444)
* Fake brightness reimplementation
* indentation
* added call to the function which is caching the display settings values
* use cached values instead of pmem
2024-12-26 23:23:02 +01:00
Erwin Ried ef28e63a8c Update README.md
Actually valid complete ali h4m link
2024-12-21 01:36:49 +01:00
979 changed files with 154623 additions and 15763 deletions
+6 -7
View File
@@ -1,14 +1,13 @@
---
BasedOnStyle: Chromium
IndentWidth: '4'
SortIncludes: 'false'
AllowAllArgumentsOnNextLine: 'true'
IndentWidth: 4
SortIncludes: false
AllowAllArgumentsOnNextLine: true
ColumnLimit: 0
Cpp11BracedListStyle: 'true'
AllowShortLoopsOnASingleLine: 'true'
AllowShortIfStatementsOnASingleLine: 'true'
Cpp11BracedListStyle: true
AllowShortLoopsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: 1
...
+2 -1
View File
@@ -1 +1,2 @@
build
*
!firmware/tools/docker-entrypoint.sh
+6
View File
@@ -8,6 +8,12 @@ body:
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
> [!NOTE]
For hardware related issue, please open as a hardware issue instead.
For feature requests, please open as a feature request instead.
If you have questions, please join our Discord server to ask.
For your information, they are options you can choose when you open an issue.
Unrelated issues will be closed without notice.
- type: textarea
id: description
attributes:
@@ -0,0 +1,38 @@
name: Hardware / QC / QA / selling
description: Issues that related with hardware, quality control, quality assurance, selling, etc.
labels:
- question
- hardware problem
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out an issue.
We would like to inform you that we can't provide much help regarding hardware/QC/QA/selling issues because we are not controlling various vendors/manufacturers, and also we are not profiting from you buying your device.
You are still welcome to ask, but please be aware that for this kind of issue, ask the seller would be the best option.
- type: textarea
id: description
attributes:
label: Describe the problem
description: "A clear and concise description of what the problem is."
validations:
required: true
- type: textarea
id: hardware-info
attributes:
label: Hardware information
description: "Please provide the hardware information here."
placeholder: |
1. HackrfRF revision
2. Portapack revision
3. (if it's audio/mic/display/touchscreen/buttons related) audio chip/ CPLD/ screen info...
4. (if hardware is visually damaged) please provide a photo
validations:
required: true
- type: textarea
id: anything-else
attributes:
label: Anything else?
description: Let us know if you have anything else to share.
+91
View File
@@ -0,0 +1,91 @@
<!--
👋 Thank you for your contribution!
⚠️ Before you start, please review our Contributing Guidelines:
https://github.com/portapack-mayhem/mayhem-firmware/wiki/Contributing-Guidelines
-->
## Brief description of what you did
<!--
Replace this line with a description of your changes. What problem does this PR solve? What does it add or fix?
Don't forget to describe how maintainers can test your modifications!
-->
---
## Proof that your changes work
<!--
1. PRs without proof will not be merged. Please provide evidence for every section below that applies to your change.
2. Trusted contributors: If you have a track record of merged PRs that were properly tested and documented, maintainers may waive one or more of the proof sections below at their discretion. You are still encouraged to include whatever evidence you have, but sections may be marked `N/A — trusted contributor` with a short note.
3. First-time contributors must provide full proof. See the Contributing Guidelines(https://github.com/portapack-mayhem/mayhem-firmware/wiki/Contributing-Guidelines) and Trusted Contributors (https://github.com/portapack-mayhem/mayhem-firmware/wiki/Trusted-Contributors) articles for details.
-->
### 🖥️ Proof it compiles
<!--
Attach a log snippet showing a successful build from the final firmware size/space summary in the build output. Example: `Space remaining in flash ROM: 19552 bytes ( 1.9 % )`.
Paste log below.
-->
### 📱 Proof of testing on a real device
<!--
Attach photos, screenshots, or a short video of your changes running on actual PortaPack hardware. Emulator or simulator output is not a substitute for real-device testing.
Paste photo / video / screenshot below
-->
### 📡 Proof against a real emitter/receiver (if applicable)
<!--
1. If your PR involves RX/TX, protocol decoding, signal generation, or any RF-related functionality, you **must** demonstrate it working against a real emitter or receiver. Include photos, videos, logs, or waterfall/spectrum screenshots.
2. If this section does not apply to your PR, write `N/A` below and briefly explain why.
3. Paste RF evidence, or write N/A with a short justification
-->
---
## 📚 Wiki documentation commitment
<!--
By submitting this PR, you acknowledge that once it is merged you are responsible for creating or updating the corresponding page on the project wiki(https://github.com/portapack-mayhem/mayhem-firmware/wiki).
Your wiki article should include:
- A screenshot of the main app screen (strongly recommended)
- A clear description of what the app does
- An explanation of the controls and UI elements
- Any known limitations, quirks, or caveats
- Anything else a user should know to use the app effectively (dependencies, required hardware, file formats, etc.)
- If you are modifying an existing app, make sure the wiki page reflects your changes.
-->
- [ ] I will (or already) create(d) wiki document for my newly added feature
---
## Checklist
- [ ] Kept changes minimal and limited to necessary files
- [ ] Verified functionality remains intact and code compiles
- [ ] Attached proof that the code compiles successfully *(or marked N/A as a trusted contributor)*
- [ ] Attached proof of testing on real PortaPack hardware *(or marked N/A as a trusted contributor)*
- [ ] Attached proof of testing against a real emitter/receiver (if RF-related), or marked N/A with justification
- [ ] I understand that by getting this PR merged, I am implicitly agreeing to create or update the corresponding wiki page (including a main-screen screenshot, description, controls, and limitations)
- [ ] I own all rights to this code (i.e., all code contained in this PR), including compliant usage rights for third-party libraries, and I agree that this code is licensed under the license of this project (GPL-3.0).
- [ ] If any third-party libraries are used, I confirm that their licenses comply with the requirements for contributing to this repository.
- [ ] Reviewed the [Contributing Guidelines](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Contributing-Guidelines)
+18 -9
View File
@@ -3,7 +3,7 @@ name: Check formatting
on: [pull_request]
jobs:
check_date:
check_format:
runs-on: ubuntu-latest
name: Check formatting
strategy:
@@ -13,11 +13,20 @@ jobs:
- "firmware/application"
- "firmware/baseband"
steps:
- uses: actions/checkout@v2
- name: print latest_commit
run: echo ${{ github.sha }}
- name: clang-format Check
uses: jidicula/clang-format-action@v4.11.0
with:
check-path: ${{ matrix.path }}
fallback-style: Chromium
- uses: actions/checkout@v4
- name: clang-format check (repo pinned)
run: |
set -euo pipefail
CF="./tools/clang-format.sh"
chmod +x "$CF" || true
"$CF" --version
ROOT="${{ matrix.path }}"
find "$ROOT" -type f \( \
-name '*.c' -o -name '*.cpp' -o \
-name '*.h' -o -name '*.hpp' \
\) -print0 \
| xargs -0 -r "$CF" --dry-run --Werror --style=file
+100 -60
View File
@@ -3,7 +3,7 @@ name: Nightly Release
on:
schedule:
- cron: "0 0 * * *"
workflow_dispatch:
jobs:
@@ -21,27 +21,36 @@ jobs:
id: should_run
continue-on-error: true
run: test -z $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "should_run=false" >> $GITHUB_OUTPUT
build:
needs: check_date
if: ${{ needs.check_date.outputs.should_run != 'false' }}
runs-on: ubuntu-latest
name: build (${{ matrix.device_name }})
strategy:
fail-fast: false
matrix:
include:
- device: default
device_name: HackRF
cmake_args: ""
artifact_suffix: "_hackrf"
- device: portarf
device_name: PortaRF
cmake_args: "-DFLASH_MB_SIZE=2 -DFLASH_MB_LIMIT_SIZE=2"
artifact_suffix: "_portarf"
- device: hpro
device_name: HackRF Pro
cmake_args: "-DBOARD=PRALINE"
artifact_suffix: "_hpro"
steps:
- name: Get current date
id: date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Get version date
id: version_date
run: echo "date=n_$(date +'%y%m%d')" >> $GITHUB_OUTPUT
- name: Checkout
- name: Checkout
uses: actions/checkout@master
with:
fetch-depth: 0
#ref: next
# The branch, tag or SHA to checkout. When checking out the repository that
# triggered a workflow, this defaults to the reference or SHA for that event.
# Otherwise, uses the default branch.
# https://github.com/actions/checkout
# So scheduled runs will use the default branch (next) but its now possible to trigger a workflow from another branch
submodules: true
- name: Git Sumbodule Update
run: |
@@ -51,10 +60,10 @@ jobs:
- name: Make build folder
run: mkdir ${{ github.workspace }}/build
- name: Run the Docker image
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev ${{ matrix.cmake_args }}
- name: Create Small SD Card ZIP - No World Map
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_nightly_${{ steps.version_date.outputs.date }}_OCI.ppfw.tar && 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 ..
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_nightly_${{ steps.version_date.outputs.date }}${{ matrix.artifact_suffix }}_OCI.ppfw.tar && 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
@@ -63,13 +72,55 @@ jobs:
unzip world_map.zip -d sdcard/ADSB
- name: Prepare Firmware ZIP
run: |
cp build/hackrf/firmware/hackrf_usb/hackrf_usb.dfu flashing/utils/hackrf_one_usb.dfu && cp build/hackrf/firmware/hackrf_usb/hackrf_usb.bin flashing/utils/hackrf_one_usb.bin
mkdir -p flashing/firmware && cp build/hackrf/firmware/hackrf_usb/hackrf_usb.dfu flashing/firmware/hackrf_usb.dfu && cp build/hackrf/firmware/hackrf_usb/hackrf_usb.bin flashing/firmware/hackrf_usb.bin
- name: Create Firmware ZIP
run: |
zip -j firmware.zip build/firmware/portapack-mayhem-firmware.bin && cd flashing && zip -r ../firmware.zip *
- name: Create SD Card ZIP
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_nightly_${{ steps.version_date.outputs.date }}_OCI.ppfw.tar && 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 ..
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_nightly_${{ steps.version_date.outputs.date }}${{ matrix.artifact_suffix }}_OCI.ppfw.tar && 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: Upload firmware zip artifact
uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.device }}
path: ./firmware.zip
- name: Upload SD card artifact
uses: actions/upload-artifact@v4
with:
name: sdcard-${{ matrix.device }}
path: ./sdcard.zip
- name: Upload SD card no-map artifact
uses: actions/upload-artifact@v4
with:
name: sdcard-no-map-${{ matrix.device }}
path: ./sdcard-no-map.zip
- name: Upload OCI tar artifact
uses: actions/upload-artifact@v4
with:
name: oci-tar-${{ matrix.device }}
path: build/firmware/portapack-mayhem_OCI.ppfw.tar
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Get current date
id: date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Get version date
id: version_date
run: echo "date=n_$(date +'%y%m%d')" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@master
with:
fetch-depth: 0
submodules: true
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create changelog
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -80,14 +131,42 @@ jobs:
echo EOF
} >> "$GITHUB_OUTPUT"
id: changelog
# Combine all 3 device firmware bins into a single zip with device selector bat
- name: Create Combined Firmware ZIP
run: |
mkdir -p firmware_combined
unzip artifacts/firmware-default/firmware.zip -d firmware_combined
mv firmware_combined/portapack-mayhem-firmware.bin firmware_combined/firmware/firmware_hackrf.bin
unzip -j artifacts/firmware-portarf/firmware.zip portapack-mayhem-firmware.bin -d firmware_combined/firmware
mv firmware_combined/firmware/portapack-mayhem-firmware.bin firmware_combined/firmware/firmware_portarf.bin
unzip -j artifacts/firmware-hpro/firmware.zip portapack-mayhem-firmware.bin -d firmware_combined/firmware
mv firmware_combined/firmware/portapack-mayhem-firmware.bin firmware_combined/firmware/firmware_hpro.bin
unzip -j artifacts/firmware-hpro/firmware.zip firmware/hackrf_usb.dfu firmware/hackrf_usb.bin -d firmware_combined_hpro
mv firmware_combined_hpro/hackrf_usb.dfu firmware_combined/firmware/hackrf_hpro_usb.dfu
mv firmware_combined_hpro/hackrf_usb.bin firmware_combined/firmware/hackrf_hpro_usb.bin
cd firmware_combined && zip -r ../firmware_all_devices.zip . && cd ..
- name: Prepare release assets
run: |
VER="${{ steps.version_date.outputs.date }}"
mkdir -p release_assets
# OCI tar assets
cp artifacts/oci-tar-default/portapack-mayhem_OCI.ppfw.tar "release_assets/OCI_hackrf_mayhem_nightly_${VER}.ppfw.tar"
cp artifacts/oci-tar-portarf/portapack-mayhem_OCI.ppfw.tar "release_assets/OCI_portarf_mayhem_nightly_${VER}.ppfw.tar"
cp artifacts/oci-tar-hpro/portapack-mayhem_OCI.ppfw.tar "release_assets/OCI_hpro_mayhem_nightly_${VER}.ppfw.tar"
# Combined firmware
cp firmware_all_devices.zip "release_assets/FIRMWARE_mayhem_nightly_${VER}.zip"
# SD card assets
cp artifacts/sdcard-default/sdcard.zip "release_assets/COPY_TO_SDCARD_hackrf_mayhem_nightly_${VER}.zip"
cp artifacts/sdcard-no-map-default/sdcard-no-map.zip "release_assets/COPY_TO_SDCARD_hackrf_mayhem_nightly_${VER}-no-world-map.zip"
cp artifacts/sdcard-portarf/sdcard.zip "release_assets/COPY_TO_SDCARD_portarf_mayhem_nightly_${VER}.zip"
cp artifacts/sdcard-no-map-portarf/sdcard-no-map.zip "release_assets/COPY_TO_SDCARD_portarf_mayhem_nightly_${VER}-no-world-map.zip"
cp artifacts/sdcard-hpro/sdcard.zip "release_assets/COPY_TO_SDCARD_hpro_mayhem_nightly_${VER}.zip"
cp artifacts/sdcard-no-map-hpro/sdcard-no-map.zip "release_assets/COPY_TO_SDCARD_hpro_mayhem_nightly_${VER}-no-world-map.zip"
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: softprops/action-gh-release@v2
with:
tag_name: nightly-tag-${{ steps.date.outputs.date }}
release_name: Nightly Release - ${{ steps.date.outputs.date }}
name: Nightly Release - ${{ steps.date.outputs.date }}
body: |
**Nightly release - ${{ steps.date.outputs.date }}**
This build is the latest and greatest, although may not be the most stable as this is a nightly release.
@@ -96,43 +175,4 @@ jobs:
${{ steps.changelog.outputs.content }}
draft: false
prerelease: true
- name: Upload Firmware TAR Asset
id: upload-firmware-tar-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: build/firmware/portapack-mayhem_OCI.ppfw.tar
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}_OCI.ppfw.tar
asset_content_type: application/x-tar
- name: Upload Firmware Asset
id: upload-firmware-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./firmware.zip
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}_FIRMWARE.zip
asset_content_type: application/zip
- name: Upload SD Card Assets
id: upload-sd-card-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./sdcard.zip
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}_COPY_TO_SDCARD.zip
asset_content_type: application/zip
- name: Upload SD Card Assets - No Map
id: upload-sd-card-asset-no-map
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./sdcard-no-map.zip
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}_COPY_TO_SDCARD-no-world-map.zip
asset_content_type: application/zip
files: release_assets/*
+103 -62
View File
@@ -1,25 +1,36 @@
name: Stable Release
on:
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
name: build (${{ matrix.device_name }})
strategy:
fail-fast: false
matrix:
include:
- device: default
device_name: HackRF
cmake_args: ""
artifact_suffix: "_hackrf"
- device: portarf
device_name: PortaRF
cmake_args: "-DFLASH_MB_SIZE=2 -DFLASH_MB_LIMIT_SIZE=2"
artifact_suffix: "_portarf"
- device: hpro
device_name: HackRF Pro
cmake_args: "-DBOARD=PRALINE"
artifact_suffix: "_hpro"
steps:
- name: Get current date
id: date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Checkout
- name: Checkout
uses: actions/checkout@master
with:
fetch-depth: 0
#ref: next
# The branch, tag or SHA to checkout. When checking out the repository that
# triggered a workflow, this defaults to the reference or SHA for that event.
# Otherwise, uses the default branch.
# https://github.com/actions/checkout
# So scheduled runs will use the default branch (next) but its now possible to trigger a workflow from another branch
submodules: true
- name: Git Sumbodule Update
run: |
@@ -27,18 +38,15 @@ jobs:
- name: Get version
id: version
run: echo "version=$(cat .github/workflows/version.txt)" >> $GITHUB_OUTPUT
- name: Get past version
id: past_version
run: echo "past_version=$(cat .github/workflows/past_version.txt)" >> $GITHUB_OUTPUT
- name: Build the Docker image
run: docker build -t portapack-dev -f dockerfile-nogit . --tag my-image-name:$(date +%s)
- name: Make build folder
run: mkdir ${{ github.workspace }}/build
- name: Run the Docker image
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev ${{ matrix.cmake_args }}
- name: Create Small SD Card ZIP - No World Map
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_${{ steps.version.outputs.version }}_OCI.ppfw.tar && 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 ..
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_${{ steps.version.outputs.version }}${{ matrix.artifact_suffix }}_OCI.ppfw.tar && 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
@@ -47,13 +55,58 @@ jobs:
unzip world_map.zip -d sdcard/ADSB
- name: Prepare Firmware ZIP
run: |
cp build/hackrf/firmware/hackrf_usb/hackrf_usb.dfu flashing/utils/hackrf_one_usb.dfu && cp build/hackrf/firmware/hackrf_usb/hackrf_usb.bin flashing/utils/hackrf_one_usb.bin
mkdir -p flashing/firmware && cp build/hackrf/firmware/hackrf_usb/hackrf_usb.dfu flashing/firmware/hackrf_usb.dfu && cp build/hackrf/firmware/hackrf_usb/hackrf_usb.bin flashing/firmware/hackrf_usb.bin
- name: Create Firmware ZIP
run: |
zip -j firmware.zip build/firmware/portapack-mayhem-firmware.bin && cd flashing && zip -r ../firmware.zip *
- name: Create SD Card ZIP
run: |
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_${{ steps.version.outputs.version }}_OCI.ppfw.tar && 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 ..
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-mayhem-firmware.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && cp build/firmware/portapack-mayhem_OCI.ppfw.tar sdcard/FIRMWARE/mayhem_${{ steps.version.outputs.version }}${{ matrix.artifact_suffix }}_OCI.ppfw.tar && 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: Upload firmware zip artifact
uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.device }}
path: ./firmware.zip
- name: Upload SD card artifact
uses: actions/upload-artifact@v4
with:
name: sdcard-${{ matrix.device }}
path: ./sdcard.zip
- name: Upload SD card no-map artifact
uses: actions/upload-artifact@v4
with:
name: sdcard-no-map-${{ matrix.device }}
path: ./sdcard-no-map.zip
- name: Upload OCI tar artifact
uses: actions/upload-artifact@v4
with:
name: oci-tar-${{ matrix.device }}
path: build/firmware/portapack-mayhem_OCI.ppfw.tar
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Get current date
id: date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@master
with:
fetch-depth: 0
submodules: true
- name: Get version
id: version
run: echo "version=$(cat .github/workflows/version.txt)" >> $GITHUB_OUTPUT
- name: Get past version
id: past_version
run: echo "past_version=$(cat .github/workflows/past_version.txt)" >> $GITHUB_OUTPUT
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create changelog
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -64,21 +117,49 @@ jobs:
echo EOF
} >> "$GITHUB_OUTPUT"
id: changelog
# Combine all 3 device firmware bins into a single zip with device selector bat
- name: Create Combined Firmware ZIP
run: |
mkdir -p firmware_combined
unzip artifacts/firmware-default/firmware.zip -d firmware_combined
mv firmware_combined/portapack-mayhem-firmware.bin firmware_combined/firmware/firmware_hackrf.bin
unzip -j artifacts/firmware-portarf/firmware.zip portapack-mayhem-firmware.bin -d firmware_combined/firmware
mv firmware_combined/firmware/portapack-mayhem-firmware.bin firmware_combined/firmware/firmware_portarf.bin
unzip -j artifacts/firmware-hpro/firmware.zip portapack-mayhem-firmware.bin -d firmware_combined/firmware
mv firmware_combined/firmware/portapack-mayhem-firmware.bin firmware_combined/firmware/firmware_hpro.bin
unzip -j artifacts/firmware-hpro/firmware.zip firmware/hackrf_usb.dfu firmware/hackrf_usb.bin -d firmware_combined_hpro
mv firmware_combined_hpro/hackrf_usb.dfu firmware_combined/firmware/hackrf_hpro_usb.dfu
mv firmware_combined_hpro/hackrf_usb.bin firmware_combined/firmware/hackrf_hpro_usb.bin
cd firmware_combined && zip -r ../firmware_all_devices.zip . && cd ..
- name: Prepare release assets
run: |
VER="${{ steps.version.outputs.version }}"
mkdir -p release_assets
# OCI tar assets
cp artifacts/oci-tar-default/portapack-mayhem_OCI.ppfw.tar "release_assets/OCI_hackrf_mayhem_${VER}.ppfw.tar"
cp artifacts/oci-tar-portarf/portapack-mayhem_OCI.ppfw.tar "release_assets/OCI_portarf_mayhem_${VER}.ppfw.tar"
cp artifacts/oci-tar-hpro/portapack-mayhem_OCI.ppfw.tar "release_assets/OCI_hpro_mayhem_${VER}.ppfw.tar"
# Combined firmware
cp firmware_all_devices.zip "release_assets/FIRMWARE_mayhem_${VER}.zip"
# SD card assets
cp artifacts/sdcard-default/sdcard.zip "release_assets/COPY_TO_SDCARD_hackrf_mayhem_${VER}.zip"
cp artifacts/sdcard-no-map-default/sdcard-no-map.zip "release_assets/COPY_TO_SDCARD_hackrf_mayhem_${VER}-no-world-map.zip"
cp artifacts/sdcard-portarf/sdcard.zip "release_assets/COPY_TO_SDCARD_portarf_mayhem_${VER}.zip"
cp artifacts/sdcard-no-map-portarf/sdcard-no-map.zip "release_assets/COPY_TO_SDCARD_portarf_mayhem_${VER}-no-world-map.zip"
cp artifacts/sdcard-hpro/sdcard.zip "release_assets/COPY_TO_SDCARD_hpro_mayhem_${VER}.zip"
cp artifacts/sdcard-no-map-hpro/sdcard-no-map.zip "release_assets/COPY_TO_SDCARD_hpro_mayhem_${VER}-no-world-map.zip"
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.version }}
release_name: Mayhem firmware ${{ steps.version.outputs.version }}
name: Mayhem firmware ${{ steps.version.outputs.version }}
body: |
**Stable release - ${{ steps.version.outputs.version }}**
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). Please check the [readme](https://github.com/portapack-mayhem/mayhem-firmware/blob/master/README.md) for details.
## Release notes
### Revision (${{ steps.version.outputs.version }}):
${{ steps.changelog.outputs.content }}
**Full Changelog**: https://github.com/portapack-mayhem/mayhem-firmware/compare/${{ steps.past_version.outputs.past_version }}...${{ steps.version.outputs.version }}
## Installation
@@ -90,44 +171,4 @@ jobs:
For certain functionality, like external apps, the world map, GPS simulator, and others you need to uncompress (using [7-zip](https://www.7-zip.org/download.html)) the files from `mayhem_vX.Y.Z_COPY_TO_SDCARD.zip` to a FAT32 formatted MicroSD card.
draft: true
prerelease: false
- name: Upload Firmware TAR Asset
id: upload-firmware-tar-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: build/firmware/portapack-mayhem_OCI.ppfw.tar
asset_name: mayhem_${{ steps.version.outputs.version }}_OCI.ppfw.tar
asset_content_type: application/x-tar
- name: Upload Firmware Asset
id: upload-firmware-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./firmware.zip
asset_name: mayhem_${{ steps.version.outputs.version }}_FIRMWARE.zip
asset_content_type: application/zip
- name: Upload SD Card Assets
id: upload-sd-card-asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./sdcard.zip
asset_name: mayhem_${{ steps.version.outputs.version }}_COPY_TO_SDCARD.zip
asset_content_type: application/zip
- name: Upload SD Card Assets - No Map
id: upload-sd-card-asset-no-map
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./sdcard-no-map.zip
asset_name: mayhem_${{ steps.version.outputs.version }}_COPY_TO_SDCARD-no-world-map.zip
asset_content_type: application/zip
files: release_assets/*
@@ -0,0 +1,20 @@
name: Hardware Issue Warning
on:
issues:
types: [labeled]
jobs:
add-comment:
if: github.event.label.name == 'hardware problem'
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add warning comment
uses: peter-evans/create-or-update-comment@v3
with:
issue-number: ${{ github.event.issue.number }}
body: |
> [!IMPORTANT]
If you are a seller/vendor and your customer has directed you to this page as a reference/evidence of a "broken device" or similar issue, please be aware that we are not providing any "official guarantee of a faulty device." We are only offering suggestions based on what the submitter has stated. We do not guarantee that the device is broken or anything else.
+1 -1
View File
@@ -1 +1 @@
v2.0.2
v2.3.2
+1 -1
View File
@@ -1 +1 @@
v2.1.0
v2.4.0
+3
View File
@@ -61,6 +61,8 @@ cmake-build-debug/
*.sublime-workspace
.vscode
.idea
*.swp
.claude
# VSCodium extensions
.history
@@ -81,3 +83,4 @@ venv/
# 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
tmp/*
+65
View File
@@ -0,0 +1,65 @@
# PortaPack Mayhem Agent Guidelines
Welcome to the PortaPack Mayhem repository. This document sets mandatory rules and best practices for human developers and AI coding assistants. It exists to prevent unverified, untested code from overwhelming the maintainers, while still enabling AI to accelerate development.
## 🚫 STRICTLY PROHIBITED
1. **No Fully Automated Pull Requests**
Autonomous agents (bots, crawlers, automated workflows) must **never** open a Pull Request without direct human supervision, review, and explicit approval.
2. **No “Blind” AI-Generated Submissions**
If you cannot explain, debug, or maintain the C++/DSP code an AI produced for you, do **not** submit it. PRs that appear to be untested, AI-only “compile-and-pray” attempts will be closed immediately.
3. **No Unverified Hardware Interactions**
AI cannot test on real hardware. Do **not** submit code that has not been compiled and physically tested on a PortaPack device (or a highly accurate emulator). This includes UI rendering, RF transmission quality, and baseband stability.
## ✅ ENCOURAGED AI USE CASES
We strongly encourage using AI assistants (Copilot, ChatGPT, Claude, local LLMs) as pair-programmers. Excellent use cases include:
- **Bug Triage & Root-Causing:** Analyze crash logs, stack traces, and complex memory issues to identify the root cause of an open issue.
- **Code Comprehension:** Map Mayhems architecture, understand M0M4 IPC, or break down complex DSP algorithms.
- **Development Assistance:** Generate boilerplate UI code, write unit tests, optimize math functions, or draft documentation provided a human reviews and refines the output.
- **Targeted Refactoring:** Suggest cleaner, modern C++ for small legacy code segments. **Do not attempt to rewrite the entire codebase.**
## 📋 MANDATORY RULES FOR AIASSISTED CONTRIBUTIONS
When submitting AIassisted work, you must:
1. **Take Full Ownership**
You are strictly responsible for every line of code you submit, regardless of origin. Be prepared to answer detailed technical questions during review.
2. **Hardware Verification Required**
In your PR, explicitly state: *“I have compiled this code and tested it on physical PortaPack hardware.”* PRs without this statement will be rejected.
3. **Transparency (Recommended)**
If AI significantly helped architect a feature or fix a complex bug, mention it briefly in the PR description (e.g., *“Used an LLM to help optimize the DSP filter loop”*).
4. **Adhere to Coding Standards**
Ensure all code matches the existing Mayhem style and formatting before opening the PR.
## ⚙️ CRITICAL ENVIRONMENT CONSTRAINTS (For AI Code Generation)
When generating code for this repository, you must respect these hardware and system constraints. **Violating any of them will result in immediate rejection.**
- **Severe Memory Limits** RAM is extremely scarce. Every byte counts. Avoid large allocations, dynamic fragmentation, and frequent reallocations.
- **Platform** ChibiOS 2.6 on NXP LPC4320(HackRF One) / LPC4330 (HackRF Pro AKA PRALINE). M0 core handles UI; M4 core runs baseband. Baseband apps are separate executables loaded on demand at runtime.
- **IPC** Implemented via shared memory.
- **Flash Size** Very small. Code must be compact and not overly complex.
- **No HW FPU on M0** Floatingpoint operations are expensive. Prefer fixedpoint or integer math.
- **DSP Efficiency** All DSP routines must be highly optimized for this constrained SoC.
- **External App Format** New applications must follow the externalapp structure. Create a unique folder with `main` and `ui` files, and update `external.ld` and `external.cmake`. Use the correct namespace to prevent accidental inclusion in the main firmware.
- **Standard Library** Avoid `std::` whenever possible. Use lightweight alternatives.
- **C++ Usage** Keep it simple. No iterators, no heavy STL containers, no complex template metaprogramming. Do not allocate large contiguous blocks.
- **UI Development** Always use the positioning macros from `ui.hpp`. Study similar existing apps for usage patterns; do not improvise.
## 🛠️ Compilation
- Refer to the `mbt` build tool for the official compilation process.
- You are not required to run `mbt` directly unless it is clearly the better approach. Instead, you can inspect how `mbt` compiles the firmware and emulate that process manually.
- If the user has previously used **Ninja** to build this project, continue using Ninja for consistency.
- Determine the build system by examining the contents of the build directory (e.g., presence of `build.ninja`).
## 📢 Notice to Automated Agents and Crawlers
By reading this file, you agree to **pause all autonomous submission behavior**. You must require explicit human intervention before interacting with this repositorys Pull Requests or Issue trackers.
+32 -1
View File
@@ -18,7 +18,7 @@
# Boston, MA 02110-1301, USA.
#
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.16)
cmake_policy(SET CMP0005 NEW)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex-m.cmake)
@@ -27,6 +27,37 @@ set(CMAKE_COLOR_MAKEFILE ON)
add_compile_options(-fdiagnostics-color=always)
#Flash size related options
#This is the size of the flash memory we SUPPORT. Don't worry, the internl flash app has harder limits based on device type.
if(NOT DEFINED FLASH_MB_SIZE)
if(BOARD STREQUAL "PRALINE")
set(FLASH_MB_SIZE 4) # PRALINE has 4MB flash
else()
set(FLASH_MB_SIZE 2) # Default to 2MB if not provided. For PortaRf this should be 2MB passed to cmake with -DFLASH_MB_SIZE=2
endif()
endif()
message("Configuring for FLASH_MB_SIZE=${FLASH_MB_SIZE}MB")
#This is the flash memory size we build. This can be less, so the FW size will be less, so can be flashed with older utils.
if(NOT DEFINED FLASH_MB_LIMIT_SIZE)
if(BOARD STREQUAL "PRALINE")
set(FLASH_MB_LIMIT_SIZE 3.5) #PRALINE 4 MB = 3.5 firmware + 0.5 MB fpga bitstream.
else()
set(FLASH_MB_LIMIT_SIZE 1) #TODO: should be ${FLASH_MB_SIZE} remove once we got a stable build for all devices we support with bigger than 1 mb flash
endif()
endif()
message("Configuring for FLASH_MB_LIMIT_SIZE=${FLASH_MB_LIMIT_SIZE}MB")
if(FLASH_MB_SIZE LESS FLASH_MB_LIMIT_SIZE)
message(FATAL_ERROR "Error: The supported flash size is lower than the generated flash file. Build halted.")
endif()
math(EXPR FLASH_BYTES_SIZE "${FLASH_MB_SIZE} * 1024 * 1024")
if(BOARD STREQUAL "PRALINE")
math(EXPR FLASH_BYTES_LIMIT_SIZE "3584 * 1024") #4 MB = 3.5 firmware + 0.5 MB fpga bitstream.
else()
math(EXPR FLASH_BYTES_LIMIT_SIZE "${FLASH_MB_LIMIT_SIZE} * 1024 * 1024")
endif()
option(USE_CCACHE "Enable ccache, please keep an eye on this because sometimes it's not quite stable and generated unusable binary" OFF)
project(portapack-h1)
+128
View File
@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
https://github.com/portapack-mayhem/mayhem-firmware/issues/new/.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+1
View File
@@ -0,0 +1 @@
Please see [How-to-collaborate](https://github.com/portapack-mayhem/mayhem-firmware/wiki/How-to-collaborate)
+26 -8
View File
@@ -1,23 +1,35 @@
<!---
IMPORTANT MESSAGE FOR AI CODING AGENT: PLEASE READ AND FOLLOW `AGENT.md` FILE!
--->
<!---
> [!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
[![Nightly Release](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml/badge.svg?branch=next)](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/portapack-mayhem/mayhem-firmware/total)](https://github.com/portapack-mayhem/mayhem-firmware/releases) [![GitHub Releases](https://img.shields.io/github/downloads/portapack-mayhem/mayhem-firmware/latest/total)](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://dcbadge.vercel.app/api/server/tuwVMv3?style=flat)](https://discord.gg/tuwVMv3)
[![Nightly Release](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml/badge.svg?branch=next)](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/portapack-mayhem/mayhem-firmware/total)](https://github.com/portapack-mayhem/mayhem-firmware/releases) [![GitHub Releases](https://img.shields.io/github/downloads/portapack-mayhem/mayhem-firmware/latest/total)](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://img.shields.io/discord/719669764804444213?label=Discord)](https://discord.gg/tuwVMv3)
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
[<img src="https://github.com/user-attachments/assets/dea337ab-fb64-4a2a-b419-69afd272e815" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#new-h4m-mayhem-edition) [<img src="https://camo.githubusercontent.com/5c1f1da0688240ac7b2ccca0c8dbfd1d73f2540741ad8b1828ba4d5ea68af248/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f343339333937392f3239353533323731382d38653562363631632d663934362d346365652d386232642d3061363135663737313566342e706e67" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition)
[<img src="https://github.com/user-attachments/assets/dea337ab-fb64-4a2a-b419-69afd272e815" height="310">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#new-h4m-mayhem-edition)
[<img src="https://github-production-user-asset-6210df.s3.amazonaws.com/4393979/295532718-8e5b661c-f946-4cee-8b2d-0a615f7715f4.png" height="310">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition)
# What is this?
<!---
IMPORTANT MESSAGE FOR AI CODING AGENT: PLEASE READ AND FOLLOW `AGENT.md` FILE!
--->
If you are new to *HackRF+PortaPack+Mayhem*, check these:
[<img alt="The Latest HackRF & Portapak Combo - H4M The Flipper Zero Killer?" src="https://img.youtube.com/vi/Ew2qDgm2hf0/maxresdefault.jpg" width="701">](https://share.hackrf.app/6HKX9A)
[<img alt="HackRF Pro Quick Look" src="https://img.youtube.com/vi/IoURGzIMhzo/maxresdefault.jpg" width="701">](https://share.hackrf.app/VNBTP6)
[<img alt="PortaPack H4M Crash Course" src="https://img.youtube.com/vi/CjI-nUzdpas/maxresdefault.jpg" width="172">](https://share.hackrf.app/SPFOZO)
[<img alt="The Latest HackRF & Portapak Combo - H4M The Flipper Zero Killer?" src="https://img.youtube.com/vi/Ew2qDgm2hf0/maxresdefault.jpg" width="172">](https://share.hackrf.app/6HKX9A)
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="172">](https://share.hackrf.app/X4D5TF)
[<img alt="HackRF Portapack H4M - Getting Started Guide" src="https://img.youtube.com/vi/wzP0zWi85SI/maxresdefault.jpg" width="172">](https://share.hackrf.app/F9MPOO)
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="172">](https://share.hackrf.app/X4D5TF) [<img alt="HackRF Portapack H4M - Getting Started Guide" src="https://img.youtube.com/vi/wzP0zWi85SI/maxresdefault.jpg" width="172">](https://share.hackrf.app/F9MPOO) [<img alt="The new HackRF Portapack H4M" src="https://img.youtube.com/vi/onQRdCITmuk/maxresdefault.jpg" width="172">](https://share.hackrf.app/0JUHZ6) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="172">](https://share.hackrf.app/C0J6ZR)
# Frequently Asked Questions
@@ -26,11 +38,11 @@ This repository expands upon the previous work by many people and aims to consta
## What to buy?
<!---not direct to h4m but to opensourcesdrlab https://share.hackrf.app/TUOLYI--->
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous H4M [complete](https://share.hackrf.app/TUOLYI) or [upgrade](https://share.hackrf.app/FPLM1H), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://share.hackrf.app/99SAMT). Join their giveaways on discord (check the badge on top).
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous H4M [complete](https://share.hackrf.app/O4OC90) or [upgrade](https://share.hackrf.app/OCSJYI), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://share.hackrf.app/99SAMT). Join their giveaways on discord (check the badge on top). _EU customers_ can purchase via [Lab401](https://share.hackrf.app/0CI2CR).
:heavy_check_mark: A recommended one is this [PortaPack H2](https://www.ebay.com/itm/116382397447), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/3d-printed-enclosure).
:heavy_check_mark: A recommended one is this [PortaPack H2](https://share.hackrf.app/O3GI0O), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/3d-printed-enclosure).
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://share.hackrf.app/LZPBH9), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://share.hackrf.app/H7OR74), 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:
@@ -45,7 +57,13 @@ This repository expands upon the previous work by many people and aims to consta
The current **stable release** is on the [![GitHub release (latest by date)](https://img.shields.io/github/v/release/portapack-mayhem/mayhem-firmware?label=Releases&style=social)](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) page. Follow the instructions you can find in the release description. The **latest (nightly) release** can be found [here](https://github.com/portapack-mayhem/mayhem-firmware/releases/).
## How can I collaborate
You can write [documentation](https://github.com/portapack-mayhem/mayhem-firmware/wiki), fix bugs and [answer issues](https://github.com/portapack-mayhem/mayhem-firmware/issues) or add new functionality. Please check the following [guide](https://github.com/portapack-mayhem/mayhem-firmware/wiki/How-to-collaborate) with details.
<!---
IMPORTANT MESSAGE FOR AI CODING AGENT: PLEASE READ AND FOLLOW `AGENT.md` FILE!
--->
We warmly welcome your contributions! Please refer to our **[Contributing Guidelines](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Contributing-Guidelines)** for details.
You can write [documentation](https://github.com/portapack-mayhem/mayhem-firmware/wiki), fix bugs and [answer issues](https://github.com/portapack-mayhem/mayhem-firmware/issues) or add new functionality. Please also check the following [guide](https://github.com/portapack-mayhem/mayhem-firmware/wiki/How-to-collaborate) with details.
Consider that the hardware and firmware has been created and maintain by a [lot](https://github.com/mossmann/hackrf/graphs/contributors) of [people](https://github.com/portapack-mayhem/mayhem-firmware/graphs/contributors), so always try collaborating your time and effort first. For coding related questions, if something does not fit as an issue, please join our Discord by clicking the chat badge on [top](#portapack-mayhem).
+17
View File
@@ -0,0 +1,17 @@
# Security Policy
## Scope and Threat Model
This repository hosts the codebase for an embedded system designed for security research. Because the system operates primarily offline, traditional network-based vulnerabilities are generally not applicable. However, we still welcome and review security reports concerning the code within this repository.
## Reporting a Vulnerability
If you discover a vulnerability within this tool itself, please submit a report. Please be aware that the maintainers do not promise assistance with obtaining CVE IDs or managing formal security advisories.
## Out of Scope
* **Vulnerabilities found *using* this tool:** Please do not submit reports here for vulnerabilities you have discovered in other systems or targets using this tool. This channel is strictly for reporting vulnerabilities found *in* the tool itself.
* **Other Repositories:** For security issues concerning related tools, such as MayhemHub, please submit your reports directly to their respective repositories.
## Research Attribution
If you utilize this tool to successfully discover vulnerabilities in external systems, we would greatly appreciate it if you credit or mention our project in your published research or write-ups.
## Legal and Security Disclaimer
For comprehensive guidelines regarding the legal and secure usage of this tool, as well as our full liability disclaimer, please refer to: [Intended-Use-and-Legality](https://github.com/portapack-mayhem/mayhem-firmware/wiki/). Users are expected to comply with all applicable laws and regulations when using this software and/or hardware.
+3 -3
View File
@@ -3,7 +3,7 @@ FROM ubuntu:noble
# Set location to download ARM toolkit from.
# This will need to be changed over time or replaced with a static link to the latest release.
ENV ARMBINURL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2?revision=108bd959-44bd-4619-9c19-26187abf5225&la=en&hash=E788CE92E5DFD64B2A8C246BBA91A249CB8E2D2D \
PATH=$HOME/bin:$PATH:/opt/build/armbin/bin
PATH=$PATH:/opt/build/armbin/bin
#Create volume /havoc/bin for compiled firmware binaries
VOLUME /havoc
@@ -15,8 +15,8 @@ RUN apt-get update \
&& apt-get -qy autoremove \
&& rm -rf /var/lib/apt/lists/*
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
# Grab the GNU ARM toolchain from arm.com
# Then extract contents to /opt/build/armbin/
Executable
+86
View File
@@ -0,0 +1,86 @@
#!/bin/bash
#################################################
# This script aids building mayhem inside docker
#
# Basic usage:
# - Build dev container: ./dockerize.sh build
# - Build mayhem: ./dockerize.sh
#
# The image will be automatically build if it
# does not exist, but if the dockerfile changes
# it need to be rebuilt manually.
#
# Advanced parameters:
# - Get a shell inside the build image to
# inspect problems: ./dockerize.sh shell
# - Give additional parameters to the container:
# Additional parameters to make: ./dockerize.sh -j10
# Use ninja instead of make: ./dockerize.sh ninja -j10
# Addotopnal CMake parameters: ./dockerize.sh -DBOARD=PRALINE
# Change the default build directory: ./dockerize.sh -B build-alt
# (Alternatively --workdir works as well)
# - Use a different dockerfile:
# ./dockerize.sh build dockerfile-other
# - Use a different cpu architecture:
# ./dockerize.sh build dockerfile-nogit-arm arm64
#
# Environment variables:
# - It is possible to override the default image
# name that is being used to build and run the
# build container using an environment variable.
# The default is 'portapack-dev'
# Override by setting the following environment
# variable: MAYHEM_DEV_DOCKER_IMAGE
#
#
# Copyright (C) 2024 u-foka
#
# 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.
#
set -e # exit immediatelly on any failure
DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
IMAGE="${MAYHEM_DEV_DOCKER_IMAGE:-portapack-dev}"
build_image() {
DOCKERFILE=${1:-dockerfile-nogit}
PLATFORM=${2:-amd64}
docker build --platform "linux/${PLATFORM}" -t "${IMAGE}" -f "${DOCKERFILE}" .
}
start_docker() {
if [ -z "$(docker images -q ${IMAGE} 2> /dev/null)" ]; then
build_image
fi
exec docker run -v "${DIR}:/havoc" -u "$(id -u):$(id -g)" -ti --rm "${IMAGE}" "$@"
}
if [ "$1" = 'shell' ]; then # open a shell into the container
start_docker "bash -li"
elif [ "$1" = 'build' ]; then # build the default (or specified) target with ninja
shift # remove the first item from $@ as we consumed it, we can then pass the rest on to make
build_image "$@"
exit $?
fi
start_docker "$@"
-1
View File
@@ -1 +0,0 @@
portapack.ried.cl
+36 -9
View File
@@ -18,7 +18,7 @@
# Boston, MA 02110-1301, USA.
#
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.16)
project(firmware)
@@ -48,19 +48,46 @@ if(NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL ${EXPECTED_GCC_VERSION})
set(GCC_VERSION_MISMATCH 1)
endif()
#generate h files
configure_file(flashsize.h.in flashsize.h)
add_compile_options(-include ${CMAKE_CURRENT_BINARY_DIR}/flashsize.h)
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?
add_custom_command(
OUTPUT ${FIRMWARE_FILENAME}
COMMAND ${MAKE_SPI_IMAGE} ${application_BINARY_DIR}/application.bin ${baseband_BINARY_DIR}/baseband.img ${FIRMWARE_FILENAME}
DEPENDS baseband application ${MAKE_SPI_IMAGE}
${baseband_BINARY_DIR}/baseband.img ${application_BINARY_DIR}/application.bin
VERBATIM
)
add_dependencies(application.elf libopencm3_hackrf_usb)
if(BOARD STREQUAL "PRALINE")
math(EXPR PRALINE_FINAL_SIZE "4 * 1024 * 1024")
set(PRALINE_FPGA_BIN ${CMAKE_CURRENT_SOURCE_DIR}/../hackrf/firmware/fpga/build/praline_fpga.bin)
set(APPEND_FPGA_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/tools/append_fpga_bitstream.py)
# NOTE: Dependencies break if the .bin files aren't included in DEPENDS. WTF, CMake?
add_custom_command(
OUTPUT ${FIRMWARE_FILENAME}
COMMAND ${MAKE_SPI_IMAGE} ${application_BINARY_DIR}/application.bin ${baseband_BINARY_DIR}/baseband.img ${FIRMWARE_FILENAME} ${FLASH_BYTES_LIMIT_SIZE}
# PRALINE: Append FPGA bitstream to firmware at offset 0x380000
# The base firmware is 3.5MB, FPGA bitstream gets appended at the end of it, final size is 4MB
COMMAND ${CMAKE_COMMAND} -E echo "Appending PRALINE FPGA bitstream to firmware..."
COMMAND python3 ${APPEND_FPGA_SCRIPT} ${FIRMWARE_FILENAME} ${PRALINE_FPGA_BIN} ${FIRMWARE_FILENAME}.tmp ${PRALINE_FINAL_SIZE}
COMMAND ${CMAKE_COMMAND} -E rename ${FIRMWARE_FILENAME}.tmp ${FIRMWARE_FILENAME}
DEPENDS baseband application ${MAKE_SPI_IMAGE}
${baseband_BINARY_DIR}/baseband.img ${application_BINARY_DIR}/application.bin
${PRALINE_FPGA_BIN} ${APPEND_FPGA_SCRIPT}
VERBATIM
)
else() # Sadly this part had to be duplicated (or at least I couldn't find a better way to do it)
# NOTE: Dependencies break if the .bin files aren't included in DEPENDS. WTF, CMake?
add_custom_command(
OUTPUT ${FIRMWARE_FILENAME}
COMMAND ${MAKE_SPI_IMAGE} ${application_BINARY_DIR}/application.bin ${baseband_BINARY_DIR}/baseband.img ${FIRMWARE_FILENAME} ${FLASH_BYTES_LIMIT_SIZE}
DEPENDS baseband application ${MAKE_SPI_IMAGE}
${baseband_BINARY_DIR}/baseband.img ${application_BINARY_DIR}/application.bin
VERBATIM
)
endif()
add_custom_target(
firmware
+37 -24
View File
@@ -86,6 +86,9 @@ set(CPLD_20150901_DATA_CPP ${CMAKE_CURRENT_BINARY_DIR}/portapack_cpld_20150901_d
set(CPLD_20170522_SVF_PATH ${HARDWARE_PATH}/portapack_h1/cpld/20170522/output_files/portapack_h1_cpld.svf)
set(CPLD_20170522_DATA_CPP ${CMAKE_CURRENT_BINARY_DIR}/portapack_cpld_20170522_data.cpp)
set(CPLD_H4M_SVF_PATH ${HARDWARE_PATH}/portapack_h4m/CPLD/AG256SL100/output_files/portapack_h4m_cpld.svf)
set(CPLD_H4M_DATA_CPP ${CMAKE_CURRENT_BINARY_DIR}/portapack_cpld_h4m_data.cpp)
set(HACKRF_CPLD_DATA_HPP ${CMAKE_CURRENT_BINARY_DIR}/hackrf_cpld_data.hpp)
set(HACKRF_CPLD_DATA_CPP ${CMAKE_CURRENT_BINARY_DIR}/hackrf_cpld_data.cpp)
@@ -118,11 +121,16 @@ set(CSRC
usb_serial_endpoints.c
usb_serial_device_to_host.c
i2c_device_to_host.c
hackrf_core_mini.c
${HACKRF_PATH}/firmware/common/adc.c
${HACKRF_PATH}/firmware/common/usb.c
${HACKRF_PATH}/firmware/common/usb_queue.c
${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c
${HACKRF_PATH}/firmware/common/usb_request.c
${HACKRF_PATH}/firmware/common/usb_standard_request.c
${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/scu.c
${HACKRF_PATH}/firmware/common/gpio_lpc.c
${HACKRF_PATH}/firmware/common/platform_detect.c
${CHIBIOS}/os/various/chprintf.c
)
@@ -184,6 +192,7 @@ set(CPPSRC
${COMMON}/battery.cpp
${COMMON}/performance_counter.cpp
${COMMON}/bmpfile.cpp
${COMMON}/gpio.cpp
app_settings.cpp
audio.cpp
baseband_api.cpp
@@ -191,8 +200,7 @@ set(CPPSRC
clock_manager.cpp
core_control.cpp
database.cpp
de_bruijn.cpp
rfm69.cpp
gradient.cpp
event_m0.cpp
file_reader.cpp
file.cpp
@@ -206,6 +214,9 @@ set(CPPSRC
irq_controls.cpp
irq_lcd_frame.cpp
irq_rtc.cpp
keeloq_common.cpp
keeloq_file.cpp
keeloq_keystore.cpp
log_file.cpp
metadata_file.cpp
flipper_subfile.cpp
@@ -228,7 +239,6 @@ set(CPPSRC
serializer.cpp
spectrum_color_lut.cpp
string_format.cpp
temperature_logger.cpp
theme.cpp
touch.cpp
tone_key.cpp
@@ -238,6 +248,7 @@ set(CPPSRC
hw/encoder.cpp
hw/max2837.cpp
hw/max2839.cpp
hw/max2831.cpp
hw/max5864.cpp
hw/rffc507x.cpp
hw/rffc507x_spi.cpp
@@ -247,6 +258,7 @@ set(CPPSRC
ook_file.cpp
ui_baseband_stats_view.cpp
ui_navigation.cpp
ui_notifications.cpp
ui_record_view.cpp
ui_sd_card_status_view.cpp
ui/ui_alphanum.cpp
@@ -261,28 +273,19 @@ set(CPPSRC
ui/ui_receiver.cpp
ui/ui_rssi.cpp
ui/ui_freqlist.cpp
ui/ui_tv.cpp
ui/ui_spectrum.cpp
ui/ui_tabview.cpp
ui/ui_textentry.cpp
ui/ui_tone_key.cpp
ui/ui_transmitter.cpp
ui/ui_bmpview.cpp
apps/ais_app.cpp
apps/analog_audio_app.cpp
apps/ble_comm_app.cpp
apps/ble_rx_app.cpp
apps/ble_tx_app.cpp
apps/capture_app.cpp
apps/ert_app.cpp
apps/pocsag_app.cpp
apps/soundboard_app.cpp
apps/ui_about_simple.cpp
apps/ui_adsb_rx.cpp
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_debug.cpp
@@ -294,21 +297,15 @@ set(CPPSRC
apps/ui_flash_utility.cpp
apps/ui_freqman.cpp
apps/ui_iq_trim.cpp
apps/ui_level.cpp
apps/ui_looking_glass_app.cpp
apps/ui_mictx.cpp
apps/ui_modemsetup.cpp
apps/ui_playlist.cpp
apps/ui_pocsag_tx.cpp
apps/ui_rds.cpp
apps/ui_recon_settings.cpp
apps/ui_recon.cpp
apps/ui_scanner.cpp
apps/ui_sd_over_usb.cpp
apps/ui_sd_wipe.cpp
apps/ui_search.cpp
apps/ui_settings.cpp
apps/ui_siggen.cpp
apps/ui_sonde.cpp
apps/ui_ss_viewer.cpp
apps/ui_standalone_view.cpp
@@ -317,12 +314,9 @@ set(CPPSRC
apps/ui_text_editor.cpp
apps/ui_touch_calibration.cpp
apps/ui_touchtunes.cpp
apps/ui_view_wav.cpp
apps/ui_weatherstation.cpp
apps/ui_whipcalc.cpp
protocols/aprs.cpp
protocols/ax25.cpp
protocols/bht.cpp
protocols/dcs.cpp
protocols/encoders.cpp
protocols/modems.cpp
@@ -331,6 +325,7 @@ set(CPPSRC
config_mode.cpp
${CPLD_20150901_DATA_CPP}
${CPLD_20170522_DATA_CPP}
${CPLD_H4M_DATA_CPP}
${HACKRF_CPLD_DATA_CPP}
ui_external_items_menu_loader.cpp
view_factory_base.cpp
@@ -377,7 +372,7 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC
hw
apps
protocols
bitmaps
bmps
)
#
@@ -416,7 +411,11 @@ set(CPPWARN "-Wall -Wextra -Wno-psabi")
# List all default C defines here, like -D_DEBUG=1
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"' -DVERSION_MD5=${VERSION_MD5} -D'GCC_VERSION_MISMATCH=${GCC_VERSION_MISMATCH}'")
# Use BOARD variable if set, otherwise default to HACKRF_ONE
if(NOT DEFINED BOARD)
set(BOARD "HACKRF_ONE")
endif()
set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -D${BOARD} -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"' -DVERSION_MD5=${VERSION_MD5} -D'GCC_VERSION_MISMATCH=${GCC_VERSION_MISMATCH}'")
# List all default ASM defines here, like -D_DEBUG=1
set(DADEFS)
@@ -474,6 +473,12 @@ add_custom_command(
DEPENDS ${EXTRACT_CPLD_DATA} ${CPLD_20170522_SVF_PATH}
)
add_custom_command(
OUTPUT ${CPLD_H4M_DATA_CPP}
COMMAND ${EXTRACT_CPLD_DATA} ${CPLD_H4M_SVF_PATH} rev_h4m >${CPLD_H4M_DATA_CPP}
DEPENDS ${EXTRACT_CPLD_DATA} ${CPLD_H4M_SVF_PATH}
)
add_custom_command(
OUTPUT ${HACKRF_CPLD_DATA_CPP}
COMMAND ${HACKRF_CPLD_TOOL} --xsvf ${HACKRF_CPLD_XSVF_PATH} --portapack-data ${HACKRF_CPLD_DATA_CPP}
@@ -481,7 +486,13 @@ add_custom_command(
)
add_executable(${PROJECT_NAME}.elf ${CSRC} ${CPPSRC} ${ASMSRC})
set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
target_link_options(${PROJECT_NAME}.elf PRIVATE
"LINKER:--defsym,LD_FLASH_SIZE=${FLASH_BYTES_LIMIT_SIZE}"
)
# NB: LPC43xx_M0.ld does INCLUDE external.ld, so that one has to be listed too -
# otherwise editing the external app section rules silently does not relink.
set_target_properties(${PROJECT_NAME}.elf PROPERTIES
LINK_DEPENDS "${LDSCRIPT};${CMAKE_CURRENT_SOURCE_DIR}/external/external.ld")
add_definitions(${DEFS})
include_directories(. ${INCDIR})
link_directories(${LLIBDIR})
@@ -500,3 +511,5 @@ add_custom_target(
${PROJECT_NAME}
DEPENDS ${PROJECT_NAME}.bin
)
add_dependencies(${PROJECT_NAME} baseband)
+19 -3
View File
@@ -50,6 +50,9 @@ void BoundSetting::parse(std::string_view value) {
case SettingType::I64:
parse_int(value, as<int64_t>());
break;
case SettingType::U64:
parse_int(value, as<uint64_t>());
break;
case SettingType::I32:
parse_int(value, as<int32_t>());
break;
@@ -68,6 +71,9 @@ void BoundSetting::parse(std::string_view value) {
as<bool>() = (parsed != 0);
break;
}
case SettingType::Float:
as<float>() = atof(value.data());
break;
};
}
@@ -84,6 +90,9 @@ void BoundSetting::write(File& file) const {
case SettingType::I64:
file.write(to_string_dec_int(as<int64_t>(), buffer, length), length);
break;
case SettingType::U64:
file.write(to_string_dec_uint(as<uint64_t>(), buffer, length), length);
break;
case SettingType::I32:
file.write(to_string_dec_int(as<int32_t>(), buffer, length), length);
break;
@@ -94,13 +103,17 @@ void BoundSetting::write(File& file) const {
file.write(to_string_dec_uint(as<uint8_t>(), buffer, length), length);
break;
case SettingType::String: {
const auto& str = as<std::string>();
file.write(str.data(), str.length());
break;
file.write_line(as<std::string>());
return;
}
case SettingType::Bool:
file.write(as<bool>() ? "1" : "0", 1);
break;
case SettingType::Float: {
auto float_str = to_string_decimal(as<float>(), 6);
file.write(float_str.c_str(), float_str.length());
}
}
file.write("\r\n", 2);
@@ -188,6 +201,7 @@ void copy_to_radio_model(const AppSettings& settings) {
settings.am_config_index,
settings.nbfm_config_index,
settings.wfm_config_index,
settings.wfmam_config_index,
settings.squelch);
}
@@ -218,6 +232,7 @@ void copy_from_radio_model(AppSettings& settings) {
settings.am_config_index = receiver_model.am_configuration();
settings.nbfm_config_index = receiver_model.nbfm_configuration();
settings.wfm_config_index = receiver_model.wfm_configuration();
settings.wfmam_config_index = receiver_model.wfmam_configuration();
}
settings.step = receiver_model.frequency_step();
@@ -271,6 +286,7 @@ SettingsManager::SettingsManager(
bindings_.emplace_back("am_config_index"sv, &settings_.am_config_index);
bindings_.emplace_back("nbfm_config_index"sv, &settings_.nbfm_config_index);
bindings_.emplace_back("wfm_config_index"sv, &settings_.wfm_config_index);
bindings_.emplace_back("wfmam_config_index"sv, &settings_.wfmam_config_index);
bindings_.emplace_back("squelch"sv, &settings_.squelch);
}
+11 -1
View File
@@ -33,6 +33,7 @@
#include <variant>
#include "file.hpp"
#include "hackrf_hal.hpp"
#include "max283x.hpp"
#include "string_format.hpp"
@@ -47,17 +48,22 @@ class BoundSetting {
/* The type of bound setting. */
enum class SettingType : uint8_t {
I64,
U64,
I32,
U32,
U8,
String,
Bool,
Float
};
public:
BoundSetting(std::string_view name, int64_t* target)
: name_{name}, target_{target}, type_{SettingType::I64} {}
BoundSetting(std::string_view name, uint64_t* target)
: name_{name}, target_{target}, type_{SettingType::U64} {}
BoundSetting(std::string_view name, int32_t* target)
: name_{name}, target_{target}, type_{SettingType::I32} {}
@@ -73,6 +79,9 @@ class BoundSetting {
BoundSetting(std::string_view name, bool* target)
: name_{name}, target_{target}, type_{SettingType::Bool} {}
BoundSetting(std::string_view name, float* target)
: name_{name}, target_{target}, type_{SettingType::Float} {}
std::string_view name() const { return name_; }
void parse(std::string_view value);
void write(File& file) const;
@@ -130,7 +139,7 @@ struct AppSettings {
uint8_t vga = 32;
uint8_t rx_amp = 0;
uint8_t tx_amp = 0;
uint8_t tx_gain = 35;
uint8_t tx_gain = hackrf::one::default_tx_gain_db;
uint32_t channel_bandwidth = 1;
uint32_t rx_frequency;
uint32_t tx_frequency;
@@ -139,6 +148,7 @@ struct AppSettings {
uint8_t am_config_index = 0;
uint8_t nbfm_config_index = 0;
uint8_t wfm_config_index = 0;
uint8_t wfmam_config_index = 0;
uint8_t squelch = 80;
uint8_t volume;
// NOTE: update COMMON_APP_SETTINGS_COUNT when adding to this
+269 -18
View File
@@ -40,6 +40,7 @@ namespace ui {
/* AMOptionsView *********************************************************/
AMOptionsView::AMOptionsView(
AnalogAudioView* view,
Rect parent_rect,
const Style* style)
: View{parent_rect} {
@@ -48,12 +49,23 @@ AMOptionsView::AMOptionsView(
add_children({
&label_config,
&options_config,
&zoom_config,
});
freqman_set_bandwidth_option(AM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config
options_config.set_by_value(receiver_model.am_configuration());
options_config.on_change = [this](size_t, OptionsField::value_t n) {
receiver_model.set_am_configuration(n);
zoom_config.on_change = [this, view](size_t, OptionsField::value_t n) { // n , has two option values. when GUI =zoom+1 => (0), when GUI=zoom+2 (6)
receiver_model.set_am_configuration(view->get_previous_AM_mode_option() + n); // n (0 or 6)
view->set_zoom_factor(AM_MODULATION, n);
view->set_previous_zoom_option(n);
};
// restore zoom selection
zoom_config.set_by_value(view->get_zoom_factor(AM_MODULATION));
freqman_set_bandwidth_option(AM_MODULATION, options_config); // freqman.cpp to the options_config, only allowing 5 modes freqman_bandwidths[AM] {"DSB 9k", 0}, {"DSB 6k", 1}, {"USB+3k", 2}, {"LSB-3k", 3}, {"CW", 4},
options_config.set_by_value(receiver_model.am_configuration() - view->get_previous_zoom_option()); // restore AM GUI option mode , AM FIR index filters (0..11) values , <baseband::AMConfig, 12> am_configs has 12 fir index elements.
options_config.on_change = [this, view](size_t, OptionsField::value_t n) {
receiver_model.set_am_configuration(n + view->get_previous_zoom_option()); // we select proper FIR AM filter (0..11), = 0..4 GUI AM modes + offset +6 (if zoom+2)
view->set_previous_AM_mode_option(n); // (0..4) allowing 5 AM modes (DSB9K, DSB6K, USB,LSB, CW)
};
}
@@ -102,6 +114,54 @@ WFMOptionsView::WFMOptionsView(
};
}
/* WFMAMAptOptionsView *******************************************************/
WFMAMAptOptionsView::WFMAMAptOptionsView(
Rect parent_rect,
const Style* style)
: View{parent_rect} {
set_style(style);
add_children({
&label_config,
&options_config,
});
freqman_set_bandwidth_option(WFMAM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config
options_config.set_by_value(receiver_model.wfmam_configuration());
options_config.on_change = [this](size_t, OptionsField::value_t n) {
receiver_model.set_wfmam_configuration(n);
};
}
/* AMFMAptOptionsView *********************************************************/
AMFMAptOptionsView::AMFMAptOptionsView(
AnalogAudioView* view,
Rect parent_rect,
const Style* style)
: View{parent_rect} {
set_style(style);
add_children({
&label_config,
&options_config,
&zoom_config,
});
freqman_set_bandwidth_option(AMFM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config
receiver_model.set_amfm_configuration(5); // Fix index 5 manually, not from freqman: set to RX AM (USB+FM) mode to demod audio tone, and get Wefax_APT signal.
options_config.set_by_value(receiver_model.amfm_configuration());
zoom_config.on_change = [this, view](size_t, OptionsField::value_t n) {
receiver_model.set_amfm_configuration(5 + n);
view->set_zoom_factor(AMFM_MODULATION, n);
};
// restore zoom selection
zoom_config.set_by_value(view->get_zoom_factor(AMFM_MODULATION));
}
/* SPECOptionsView *******************************************************/
SPECOptionsView::SPECOptionsView(
@@ -139,6 +199,55 @@ SPECOptionsView::SPECOptionsView(
view->set_spec_iq_phase_calibration_value(view->get_spec_iq_phase_calibration_value()); // initialize iq_phase_calibration in radio
}
/* PralineOptionsView *******************************************************/
#ifdef PRALINE
PralineOptionsView::PralineOptionsView(Rect parent_rect, const Style* style) {
set_style(style);
set_parent_rect(parent_rect);
add_children({&label_sr, &options_sr, &label_dc, &options_dc,
&label_qi, &options_qi, &label_qs, &options_qs});
// READ the current state from hardware
fpga_reg_1 = radio::debug::fpga::register_read(1);
// INITIALIZE UI WIDGETS based on read bits
options_dc.set_by_value((fpga_reg_1 & 0x01) ? 1 : 0); // Bit 0
options_qi.set_by_value((fpga_reg_1 & 0x02) ? 1 : 0); // Bit 1
options_qs.set_by_value((fpga_reg_1 & 0x04) ? 1 : 0); // Bit 2
options_sr.set_value(receiver_model.sampling_rate());
options_sr.on_change = [this](int32_t v) { receiver_model.set_sampling_rate(static_cast<uint32_t>(v)); };
options_dc.on_change = [this](size_t, OptionsField::value_t v) {
if (v)
fpga_reg_1 |= 0x01;
else
fpga_reg_1 &= ~0x01;
update_fpga_ctrl();
};
options_qi.on_change = [this](size_t, OptionsField::value_t v) {
if (v)
fpga_reg_1 |= 0x02;
else
fpga_reg_1 &= ~0x02;
update_fpga_ctrl();
};
options_qs.on_change = [this](size_t, OptionsField::value_t v) {
if (v)
fpga_reg_1 |= 0x04;
else
fpga_reg_1 &= ~0x04;
update_fpga_ctrl();
};
}
void PralineOptionsView::update_fpga_ctrl() {
radio::debug::fpga::register_write(1, fpga_reg_1);
}
#endif
/* AnalogAudioView *******************************************************/
AnalogAudioView::AnalogAudioView(
@@ -157,7 +266,13 @@ AnalogAudioView::AnalogAudioView(
&field_volume,
&text_ctcss,
&record_view,
&waterfall});
#ifdef PRALINE
&waterfall,
&button_pro
#else
&waterfall
#endif
});
// Filename Datetime and Frequency
record_view.set_filename_date_frequency(true);
@@ -165,6 +280,12 @@ AnalogAudioView::AnalogAudioView(
field_frequency.on_show_options = [this]() {
this->on_show_options_frequency();
};
field_frequency.changing = [this](rf::Frequency frequency) {
return this->on_frequency_changed(frequency);
};
field_frequency.entered = [this](rf::Frequency frequency) {
this->set_frequency_absolute(frequency);
};
field_lna.on_show_options = [this]() {
this->on_show_options_rf_gain();
@@ -175,8 +296,9 @@ AnalogAudioView::AnalogAudioView(
};
auto modulation = receiver_model.modulation();
// This app doesn't handle "Capture" mode.
if (modulation > ReceiverModel::Mode::SpectrumAnalysis)
if (modulation == ReceiverModel::Mode::Capture)
modulation = ReceiverModel::Mode::SpectrumAnalysis;
options_modulation.set_by_value(toUType(modulation));
@@ -192,13 +314,20 @@ AnalogAudioView::AnalogAudioView(
};
waterfall.on_select = [this](int32_t offset) {
field_frequency.set_value(receiver_model.target_frequency() + offset);
field_frequency.set_value(
field_frequency.value() + offset * receiver_model.frequency_step());
};
waterfall.set_live_tuning(true);
#ifdef PRALINE
button_pro.on_select = [this](Button&) { this->on_show_options_praline(); };
#endif
audio::output::start();
// This call starts the correct baseband image to run
// and sets the radio up as necessary for the given modulation.
sliding_center_frequency = receiver_model.target_frequency();
on_modulation_changed(modulation);
}
@@ -208,7 +337,7 @@ AnalogAudioView::AnalogAudioView(
: 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);
set_frequency_absolute(override.frequency_app_override);
on_frequency_step_changed(override.frequency_step);
options_modulation.set_by_value(toUType(override.mode));
}
@@ -226,11 +355,42 @@ 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
uint8_t AnalogAudioView::get_zoom_factor(uint8_t mode) { // define accessor functions inside AnalogAudioView to read zoom value
if (mode == AM_MODULATION)
return zoom_factor_am;
else if (mode == AMFM_MODULATION)
return zoom_factor_amfm;
return 0; // default if unsupported mode
}
void AnalogAudioView::set_zoom_factor(uint8_t mode, uint8_t zoom) { // define accessor functions inside AnalogAudioView to write zoom value
if (mode == AM_MODULATION)
zoom_factor_am = zoom;
else if (mode == AMFM_MODULATION)
zoom_factor_amfm = zoom;
}
uint8_t AnalogAudioView::get_previous_AM_mode_option() {
return previous_AM_mode_option;
}
void AnalogAudioView::set_previous_AM_mode_option(uint8_t mode) {
previous_AM_mode_option = mode;
}
uint8_t AnalogAudioView::get_previous_zoom_option() {
return previous_zoom;
}
void AnalogAudioView::set_previous_zoom_option(uint8_t zoom) {
previous_zoom = zoom;
}
uint8_t AnalogAudioView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read iq_phase_calibration_value
return iq_phase_calibration_value;
}
void AnalogAudioView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // define accessor functions
void AnalogAudioView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // define accessor functions inside AnalogAudioView to write iq_phase_calibration_value
iq_phase_calibration_value = cal_value;
radio::set_rx_max283x_iq_phase_calibration(iq_phase_calibration_value);
}
@@ -246,6 +406,7 @@ void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
}
AnalogAudioView::~AnalogAudioView() {
receiver_model.set_hidden_offset(0);
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
@@ -267,14 +428,10 @@ void AnalogAudioView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
}
void AnalogAudioView::on_modulation_changed(ReceiverModel::Mode modulation) {
// This app doesn't know what to do with "Capture" mode.
if (modulation > ReceiverModel::Mode::SpectrumAnalysis)
modulation = ReceiverModel::Mode::SpectrumAnalysis;
baseband::spectrum_streaming_stop();
waterfall.stop();
update_modulation(modulation);
on_show_options_modulation();
baseband::spectrum_streaming_start();
waterfall.start();
}
void AnalogAudioView::remove_options_widget() {
@@ -329,7 +486,7 @@ 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, Theme::getInstance()->option_active);
widget = std::make_unique<AMOptionsView>(this, options_view_rect, Theme::getInstance()->option_active);
waterfall.show_audio_spectrum_view(false);
text_ctcss.hidden(true);
break;
@@ -346,6 +503,18 @@ void AnalogAudioView::on_show_options_modulation() {
text_ctcss.hidden(true);
break;
case ReceiverModel::Mode::WFMAudioAMApt:
widget = std::make_unique<WFMAMAptOptionsView>(options_view_rect, Theme::getInstance()->option_active);
waterfall.show_audio_spectrum_view(true);
text_ctcss.hidden(true);
break;
case ReceiverModel::Mode::AMAudioFMApt:
widget = std::make_unique<AMFMAptOptionsView>(this, options_view_rect, Theme::getInstance()->option_active);
waterfall.show_audio_spectrum_view(false);
text_ctcss.hidden(true);
break;
case ReceiverModel::Mode::SpectrumAnalysis:
widget = std::make_unique<SPECOptionsView>(this, nbfm_view_rect, Theme::getInstance()->option_active);
waterfall.show_audio_spectrum_view(false);
@@ -387,6 +556,12 @@ void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
case ReceiverModel::Mode::WidebandFMAudio:
image_tag = portapack::spi_flash::image_tag_wfm_audio;
break;
case ReceiverModel::Mode::WFMAudioAMApt:
image_tag = portapack::spi_flash::image_tag_wfm_audio;
break;
case ReceiverModel::Mode::AMAudioFMApt:
image_tag = portapack::spi_flash::image_tag_am_audio;
break;
case ReceiverModel::Mode::SpectrumAnalysis:
image_tag = portapack::spi_flash::image_tag_wideband_spectrum;
break;
@@ -407,8 +582,14 @@ void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
receiver_model.set_sampling_rate(is_wideband_spectrum_mode ? spec_bw : 3072000);
receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? spec_bw / 2 : 1750000);
reset_sliding_frequency(modulation);
receiver_model.enable();
if (sliding_enabled) {
baseband::set_audio_ddc_frequency(0);
}
// TODO: This doesn't belong here! There's a better way.
size_t sampling_rate = 0;
switch (modulation) {
@@ -421,6 +602,12 @@ void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
case ReceiverModel::Mode::WidebandFMAudio:
sampling_rate = 48000;
break;
case ReceiverModel::Mode::WFMAudioAMApt:
sampling_rate = 12000;
break;
case ReceiverModel::Mode::AMAudioFMApt:
sampling_rate = 12000;
break;
default:
break;
}
@@ -436,6 +623,70 @@ void AnalogAudioView::handle_coded_squelch(uint32_t value) {
}
void AnalogAudioView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
set_frequency_absolute(freq);
}
void AnalogAudioView::set_frequency_absolute(rf::Frequency frequency) {
if (!sliding_enabled) {
field_frequency.set_value(frequency);
return;
}
sliding_center_frequency = frequency;
/* set_value() does not call on_change when the displayed frequency is
* already equal, so reset the hardware and DDC explicitly in that case. */
if (field_frequency.value() == frequency) {
receiver_model.set_target_frequency_with_hidden_offset(frequency, 0);
baseband::set_audio_ddc_frequency(0);
} else {
field_frequency.set_value(frequency);
}
}
int32_t AnalogAudioView::sliding_limit() const {
const bool zoom_x2 =
receiver_model.modulation() == ReceiverModel::Mode::AMAudio &&
previous_zoom != 0;
return zoom_x2 ? sliding_limit_zoom_x2 : sliding_limit_zoom_x1;
}
void AnalogAudioView::reset_sliding_frequency(ReceiverModel::Mode modulation) {
sliding_enabled =
modulation == ReceiverModel::Mode::AMAudio ||
modulation == ReceiverModel::Mode::NarrowbandFMAudio;
sliding_center_frequency = receiver_model.target_frequency();
/* AMFM keeps its existing Wefax offset; sliding applies to AM and NFM. */
receiver_model.set_hidden_offset(
modulation == ReceiverModel::Mode::AMAudioFMApt ? -2200 : 0);
}
bool AnalogAudioView::on_frequency_changed(rf::Frequency frequency) {
if (!sliding_enabled)
return false;
const auto limit = sliding_limit();
int64_t offset = frequency - sliding_center_frequency;
if (offset > limit) {
sliding_center_frequency = frequency - limit;
offset = limit;
} else if (offset < -limit) {
sliding_center_frequency = frequency + limit;
offset = -limit;
}
/* Store the displayed frequency and retune the hardware centre atomically. */
receiver_model.set_target_frequency_with_hidden_offset(
frequency, sliding_center_frequency - frequency);
baseband::set_audio_ddc_frequency(static_cast<int32_t>(offset));
return true;
}
#ifdef PRALINE
void AnalogAudioView::on_show_options_praline() {
set_options_widget(std::make_unique<PralineOptionsView>(options_view_rect, Theme::getInstance()->option_active));
}
#endif
} /* namespace ui */
+142 -28
View File
@@ -35,22 +35,88 @@
namespace ui {
class AnalogAudioView;
#ifdef PRALINE
class PralineOptionsView : public View {
public:
PralineOptionsView(Rect parent_rect, const Style* style);
private:
// Layout: SR (Sample Rate), DC (DC Block), QI (Q-Invert), QS (Quarter Shift), D (Decim)
Text label_sr{{UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "SR"};
// Parameters: Position, Length (in chars), Range, Step, and default value
NumberField options_sr{
{UI_POS_X(3), UI_POS_Y(0)},
8, // Number of digits to show
{0, 40000000}, // Range: 0 to 40,000 kHz
100, // Step: 100 Hz
' ' // Fix: Single quotes for char
};
Text label_dc{{UI_POS_X(14), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "DC"};
OptionsField options_dc{{UI_POS_X(16), UI_POS_Y(0)}, 2, {{"Of", 0}, {"On", 1}}};
Text label_qi{{UI_POS_X(20), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "QI"};
OptionsField options_qi{{UI_POS_X(22), UI_POS_Y(0)}, 2, {{"Of", 0}, {"On", 1}}};
Text label_qs{{UI_POS_X(26), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)}, "QS"};
OptionsField options_qs{{UI_POS_X(28), UI_POS_Y(0)}, 2, {{"Of", 0}, {"On", 1}}};
uint8_t fpga_reg_1{0x00}; // Tracks DC, QI, QS bits
void update_fpga_ctrl();
};
#endif
class AMOptionsView : public View {
public:
AMOptionsView(Rect parent_rect, const Style* style);
AMOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
private:
Text label_config{
{0 * 8, 0 * 16, 2 * 8, 1 * 16},
{UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)},
"BW",
};
OptionsField options_config{
{3 * 8, 0 * 16},
{UI_POS_X(3), UI_POS_Y(0)},
6, // Max option length
{
// Using common messages from freqman_ui.cpp
}};
OptionsField zoom_config{
{UI_POS_X_RIGHT(7), UI_POS_Y(0)},
7,
{{"ZOOM x1", 0},
{"ZOOM x2", 6}} // offset index AM modes array FIR filters.
};
};
class AMFMAptOptionsView : public View {
public:
AMFMAptOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
private:
Text label_config{
{UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)},
"BW",
};
OptionsField options_config{
{UI_POS_X(3), UI_POS_Y(0)},
17, // Max option length chars "USB+FM(Wefax Apt)"
{
// Using common messages from freqman_ui.cpp In HF USB , Here we only need USB Audio demod, + post-FM demod fsubcarrier FM tone to get APT signal.
}};
OptionsField zoom_config{
{UI_POS_X_RIGHT(7), UI_POS_Y(0)},
7,
{{"ZOOM x1", 0},
{"ZOOM x2", 6}} // offset index array filters.
};
};
class NBFMOptionsView : public View {
@@ -59,21 +125,21 @@ class NBFMOptionsView : public View {
private:
Text label_config{
{0 * 8, 0 * 16, 2 * 8, 1 * 16},
{UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)},
"BW",
};
OptionsField options_config{
{3 * 8, 0 * 16},
{UI_POS_X(3), UI_POS_Y(0)},
3, // Max option length
{
// Using common messages from freqman_ui.cpp
}};
Text text_squelch{
{7 * 8, 0 * 16, 8 * 8, 1 * 16},
{UI_POS_X(7), UI_POS_Y(0), UI_POS_WIDTH(8), UI_POS_HEIGHT(1)},
"SQ /99"};
NumberField field_squelch{
{10 * 8, 0 * 16},
{UI_POS_X(10), UI_POS_Y(0)},
2,
{0, 99},
1,
@@ -87,18 +153,33 @@ class WFMOptionsView : public View {
private:
Text label_config{
{0 * 8, 0 * 16, 2 * 8, 1 * 16},
{UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)},
"BW",
};
OptionsField options_config{
{3 * 8, 0 * 16},
{UI_POS_X(3), UI_POS_Y(0)},
4, // Max option length
{
// Using common messages from freqman_ui.cpp
}};
};
class AnalogAudioView;
class WFMAMAptOptionsView : public View {
public:
WFMAMAptOptionsView(Rect parent_rect, const Style* style);
private:
Text label_config{
{UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)},
"BW",
};
OptionsField options_config{
{UI_POS_X(3), UI_POS_Y(0)},
16, // Max option char length "80k-NOAA Apt LPF" , example.
{
// Using common messages from freqman_ui.cpp
}};
};
class SPECOptionsView : public View {
public:
@@ -106,11 +187,11 @@ class SPECOptionsView : public View {
private:
Text label_config{
{0 * 8, 0 * 16, 2 * 8, 1 * 16},
{UI_POS_X(0), UI_POS_Y(0), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)},
"BW",
};
OptionsField options_config{
{3 * 8, 0 * 16},
{UI_POS_X(3), UI_POS_Y(0)},
4,
{
{"20m ", 20000000},
@@ -123,20 +204,20 @@ class SPECOptionsView : public View {
}};
Text text_speed{
{9 * 8, 0 * 16, 8 * 8, 1 * 16},
{UI_POS_X(9), UI_POS_Y(0), UI_POS_WIDTH(8), UI_POS_HEIGHT(1)},
"SP /63"};
NumberField field_speed{
{12 * 8, 0 * 16},
{UI_POS_X(12), UI_POS_Y(0)},
2,
{0, 63},
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.
{UI_POS_X(19), UI_POS_Y(0), UI_POS_WIDTH(11), UI_POS_HEIGHT(1)}, // 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},
{screen_width - 2 * 8, UI_POS_Y(0)},
2,
{0, 63}, // 5 or 6 bits IQ CAL phase adjustment (range updated later)
1,
@@ -164,66 +245,95 @@ class AnalogAudioView : public View {
uint8_t get_spec_iq_phase_calibration_value();
void set_spec_iq_phase_calibration_value(uint8_t cal_value);
uint8_t get_zoom_factor(uint8_t mode);
void set_zoom_factor(uint8_t mode, uint8_t zoom);
uint8_t get_previous_AM_mode_option();
void set_previous_AM_mode_option(uint8_t mode);
uint8_t get_previous_zoom_option();
void set_previous_zoom_option(uint8_t zoom);
#ifdef PRALINE
Button button_pro{{UI_POS_X(12), UI_POS_Y(2), UI_POS_WIDTH(3), UI_POS_HEIGHT(1)}, "PRO"};
void on_show_options_praline();
#endif
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
uint8_t zoom_factor_am{0}; // initial zoom factor in AM mode
uint8_t zoom_factor_amfm{0}; // initial zoom factor in AMFM mode
uint8_t previous_AM_mode_option{0}; // GUI 5 AM modes : (0..4 ) (DSB9K, DSB6K, USB,LSB, CW). Used to select proper FIR filter (0..11) AM mode + offset 0 (zoom+1) or +6 (if zoom+2)
uint8_t previous_zoom{0}; // GUI ZOOM+1, ZOOM+2 , equivalent to two values offset 0 (zoom+1) or +6 (if zoom+2)
static constexpr int32_t sliding_limit_zoom_x1 = 50000;
static constexpr int32_t sliding_limit_zoom_x2 = 30000;
rf::Frequency sliding_center_frequency{0};
bool sliding_enabled{false};
app_settings::SettingsManager settings_{
"rx_audio",
app_settings::Mode::RX,
{
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
{"zoom_factor_am"sv, &zoom_factor_am}, // we are saving and restoring AM ZOOM factor from Settings.
{"zoom_factor_amfm"sv, &zoom_factor_amfm}, // we are saving and restoring AMFM ZOOM factor from Settings.
{"previous_AM_mode_option"sv, &previous_AM_mode_option}, // we are saving and restoring AMFM ZOOM factor from Settings.
{"previous_zoom"sv, &previous_zoom}, // we are saving and restoring AMFM ZOOM factor 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};
const Rect options_view_rect{UI_POS_X(0), UI_POS_Y(1), UI_POS_MAXWIDTH, UI_POS_HEIGHT(1)};
const Rect nbfm_view_rect{UI_POS_X(0), UI_POS_Y(1), UI_POS_WIDTH(18), UI_POS_HEIGHT(1)};
size_t spec_bw_index = 0;
uint32_t spec_bw = 20000000;
uint16_t spec_trigger = 63;
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(21) - UI_POS_WIDTH(2), 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4}};
{UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(21) - UI_POS_WIDTH(2), 4}};
Audio audio{
{21 * 8, 10, 6 * 8, 4}};
{UI_POS_X(21), 10, UI_POS_WIDTH_REMAINING(21) - UI_POS_WIDTH(2), 4}};
RxFrequencyField field_frequency{
{5 * 8, 0 * 16},
{UI_POS_X(5), UI_POS_Y(0)},
nav_};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{UI_POS_X(15), UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{UI_POS_X(18), UI_POS_Y(0)}};
OptionsField options_modulation{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
4,
{
{" AM ", toUType(ReceiverModel::Mode::AMAudio)},
{"NFM ", toUType(ReceiverModel::Mode::NarrowbandFMAudio)},
{"WFM ", toUType(ReceiverModel::Mode::WidebandFMAudio)},
{"SPEC", toUType(ReceiverModel::Mode::SpectrumAnalysis)},
{"AMFM", toUType(ReceiverModel::Mode::AMAudioFMApt)}, // Added to handle HF WeatherFax , SSB (USB demod) + Tone_Subcarrier FM demod
{"FMAM", toUType(ReceiverModel::Mode::WFMAudioAMApt)} // Added to handle SAT NOAA APT
}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, UI_POS_Y(0)}};
Text text_ctcss{
{16 * 8, 1 * 16, 14 * 8, 1 * 16},
{UI_POS_X(16), UI_POS_Y(1), UI_POS_WIDTH(14), UI_POS_HEIGHT(1)},
""};
std::unique_ptr<Widget> options_widget{};
RecordView record_view{
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
{UI_POS_X(0), UI_POS_Y(2), UI_POS_MAXWIDTH, UI_POS_HEIGHT(1)},
u"AUD",
u"AUDIO",
RecordView::FileType::WAV,
@@ -248,6 +358,10 @@ class AnalogAudioView : public View {
void handle_coded_squelch(uint32_t value);
void on_freqchg(int64_t freq);
bool on_frequency_changed(rf::Frequency frequency);
void set_frequency_absolute(rf::Frequency frequency);
int32_t sliding_limit() const;
void reset_sliding_frequency(ReceiverModel::Mode modulation);
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
+63 -105
View File
@@ -69,7 +69,7 @@ static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) {
}
void BLECommView::focus() {
options_channel.focus();
field_frequency.focus();
}
BLECommView::BLECommView(NavigationView& nav)
@@ -79,10 +79,7 @@ BLECommView::BLECommView(NavigationView& nav)
&field_rf_amp,
&field_lna,
&field_vga,
&options_channel,
&field_frequency,
&label_send_adv,
&button_send_adv,
&check_log,
&label_packets_sent,
&text_packets_sent,
@@ -90,10 +87,6 @@ BLECommView::BLECommView(NavigationView& nav)
field_frequency.set_step(0);
button_send_adv.on_select = [this](ImageButton&) {
this->toggle();
};
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
@@ -104,27 +97,8 @@ BLECommView::BLECommView(NavigationView& nav)
logger->append(logs_dir.string() + "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
};
options_channel.on_change = [this](size_t, int32_t i) {
// If we selected Auto don't do anything and Auto will handle changing.
if (i == 40) {
auto_channel = true;
return;
} else {
auto_channel = false;
}
field_frequency.set_value(get_freq_by_channel_number(i));
channel_number_rx = i;
channel_number_tx = i;
};
options_channel.set_selected_index(3, true);
logger = std::make_unique<BLECommLogger>();
// Generate new random Mac Address upon each new startup.
generateRandomMacAddress(randomMac);
// Setup Initial Advertise Packet.
advertisePacket = build_adv_packet();
}
@@ -145,86 +119,66 @@ bool BLECommView::in_tx_mode() const {
return (bool)is_running_tx;
}
void BLECommView::toggle() {
if (in_tx_mode()) {
sendAdvertisement(false);
} else {
sendAdvertisement(true);
}
}
BLETxPacket BLECommView::build_adv_packet() {
BLETxPacket bleTxPacket;
memset(&bleTxPacket, 0, sizeof(BLETxPacket));
std::string dataString = "11094861636b524620506f7274617061636b";
strncpy(bleTxPacket.macAddress, randomMac, 12);
std::string dataString = "02010603030F1807084861636B5246";
strncpy(bleTxPacket.advertisementData, dataString.c_str(), dataString.length());
// Duty cycle of 40% per 100ms advertisment periods.
strncpy(bleTxPacket.packetCount, "80", 3);
bleTxPacket.packet_count = 80;
strncpy(bleTxPacket.packetCount, "1", 2);
bleTxPacket.packet_count = 1;
bleTxPacket.packetType = PKT_TYPE_DISCOVERY;
bleTxPacket.pduType = PKT_TYPE_ADV_IND;
return bleTxPacket;
}
void BLECommView::sendAdvertisement(bool enable) {
if (enable) {
startTx(advertisePacket);
is_adv = true;
} else {
is_adv = false;
stopTx();
}
void BLECommView::sendAdvertisement(void) {
startTx(advertisePacket);
ble_state = Ble_State_Advertising;
}
void BLECommView::startTx(BLETxPacket packetToSend) {
int randomChannel = channel_number_tx;
switch_rx_tx(false);
if (auto_channel) {
int min = 37;
int max = 39;
currentPacket = packetToSend;
packet_counter = currentPacket.packet_count;
randomChannel = min + std::rand() % (max - min + 1);
field_frequency.set_value(get_freq_by_channel_number(randomChannel));
switch (advCount) {
case 0:
channel_number_tx = 37;
break;
case 1:
channel_number_tx = 38;
break;
case 2:
channel_number_tx = 39;
break;
}
if (!in_tx_mode()) {
switch_rx_tx(false);
field_frequency.set_value(get_freq_by_channel_number(37));
currentPacket = packetToSend;
packet_counter = currentPacket.packet_count;
advCount++;
button_send_adv.set_bitmap(&bitmap_stop);
baseband::set_btletx(randomChannel, currentPacket.macAddress, currentPacket.advertisementData, currentPacket.packetType);
transmitter_model.set_tx_gain(47);
transmitter_model.enable();
is_running_tx = true;
} else {
baseband::set_btletx(randomChannel, currentPacket.macAddress, currentPacket.advertisementData, currentPacket.packetType);
if (advCount == 3) {
advCount = 0;
}
if ((packet_counter % 10) == 0) {
text_packets_sent.set(to_string_dec_uint(packet_counter));
}
baseband::set_btletx(37, deviceMAC, currentPacket.advertisementData, currentPacket.pduType);
transmitter_model.set_tx_gain(47);
transmitter_model.enable();
is_sending = true;
packet_counter--;
is_running_tx = true;
}
void BLECommView::stopTx() {
button_send_adv.set_bitmap(&bitmap_play);
text_packets_sent.set(to_string_dec_uint(packet_counter));
switch_rx_tx(true);
baseband::set_btlerx(channel_number_rx);
field_frequency.set_value(get_freq_by_channel_number(channel_number_rx));
receiver_model.enable();
is_running_tx = false;
@@ -248,14 +202,12 @@ void BLECommView::on_data(BlePacketData* packet) {
parse_received_packet(packet, (ADV_PDU_TYPE)packet->type);
}
// called each 1/60th of second, so 6 = 100ms
void BLECommView::on_timer() {
// Send advertise burst only once every 100ms
if (++timer_counter == timer_period) {
timer_counter = 0;
if (!is_adv) {
sendAdvertisement(true);
if (ble_state == Ble_State_Receiving || ble_state == Ble_State_Idle) {
sendAdvertisement();
}
}
}
@@ -263,17 +215,18 @@ void BLECommView::on_timer() {
void BLECommView::on_tx_progress(const bool done) {
if (done) {
if (in_tx_mode()) {
is_sending = false;
ble_state = Ble_State_Receiving;
if (packet_counter == 0) {
if (is_adv) {
sendAdvertisement(false);
} else {
stopTx();
}
} else {
startTx(currentPacket);
}
timer_counter = 0;
timer_period = 12;
stopTx();
// else
// {
// startTx(advertisePacket);
// advCount++;
// }
}
}
}
@@ -309,23 +262,28 @@ void BLECommView::parse_received_packet(const BlePacketData* packet, ADV_PDU_TYP
}
if (pdu_type == SCAN_REQ || pdu_type == CONNECT_REQ) {
ADV_PDU_PAYLOAD_TYPE_1_3* directed_mac_data = (ADV_PDU_PAYLOAD_TYPE_1_3*)packet->data;
std::reverse(directed_mac_data->A1, directed_mac_data->A1 + 6);
console.clear(true);
std::string str_console = "";
std::string pduTypeStr = "";
if (pdu_type == SCAN_REQ) {
pduTypeStr += "SCAN_REQ";
} else if (pdu_type == CONNECT_REQ) {
pduTypeStr += "CONNECT_REQ";
}
ADV_PDU_PAYLOAD_TYPE_1_3* directed_mac_data = (ADV_PDU_PAYLOAD_TYPE_1_3*)packet->data;
str_console += "PACKET TYPE:" + pduTypeStr + "\n";
str_console += "MY MAC:" + to_string_formatted_mac_address(randomMac) + "\n";
str_console += "SCAN MAC:" + to_string_mac_address(directed_mac_data->A1, 6, false) + "\n";
console.write(str_console);
std::reverse(directed_mac_data->A1, directed_mac_data->A1 + 6);
std::string directedMAC = to_string_mac_address(directed_mac_data->A1, 6, false);
// Compare directed MAC Hex with the device MAC.
if (1) {
// std::string str_console = "Received SCAN_REQ from directed MAC: " + directedMAC + "\n";
// console.clear(true);
// console.writeln(str_console);
}
} else if (pdu_type == CONNECT_REQ) {
ADV_PDU_PAYLOAD_TYPE_5* connectReq = (ADV_PDU_PAYLOAD_TYPE_5*)packet->data;
std::reverse(connectReq->AdvA, connectReq->AdvA + 6);
std::string directedMAC = to_string_mac_address(connectReq->AdvA, 6, false);
std::string str_console = "Received CONNECT_REQ from directed MAC: " + directedMAC + "\n";
console.clear(true);
console.writeln(str_console);
}
}
}
+21 -20
View File
@@ -59,7 +59,7 @@ class BLECommView : public View {
~BLECommView();
void set_parent_rect(const Rect new_parent_rect) override;
void paint(Painter&) override{};
void paint(Painter&) override {};
void focus() override;
@@ -76,7 +76,16 @@ class BLECommView : public View {
void on_data(BlePacketData* packetData);
void on_tx_progress(const bool done);
void parse_received_packet(const BlePacketData* packet, ADV_PDU_TYPE pdu_type);
void sendAdvertisement(bool enable);
void sendAdvertisement(void);
typedef enum {
Ble_State_Idle = 0,
Ble_State_Advertising = 1,
Ble_State_Receiving = 2,
Ble_State_Sending = 3
} BleState;
BleState ble_state{Ble_State_Idle};
NavigationView& nav_;
@@ -101,8 +110,9 @@ class BLECommView : public View {
uint8_t channel_number_tx = 37;
uint8_t channel_number_rx = 37;
bool auto_channel = false;
uint8_t advCount = 0;
char randomMac[13] = "010203040506";
char deviceMAC[13] = "C23456789ABC";
bool is_running_tx = false;
bool is_sending = false;
@@ -111,7 +121,7 @@ class BLECommView : public View {
int16_t timer_period{6}; // Delay each packet by 16ms.
int16_t timer_counter = 0;
int16_t timer_rx_counter = 0;
int16_t timer_rx_period{12}; // Poll Rx for at least 200ms. (TBD)
int16_t timer_rx_period{12}; // Poll Rx for at least 150ms. (TBD)
uint32_t packet_counter{0};
BLETxPacket advertisePacket{};
@@ -121,7 +131,7 @@ class BLECommView : public View {
static constexpr auto header_height = 5 * 16;
OptionsField options_channel{
{0 * 8, 0 * 8},
{UI_POS_X(0), 0 * 8},
5,
{{"Ch.37 ", 37},
{"Ch.38", 38},
@@ -129,17 +139,17 @@ class BLECommView : public View {
{"Auto", 40}}};
RxFrequencyField field_frequency{
{6 * 8, 0 * 16},
{6 * 8, UI_POS_Y(0)},
nav_};
RFAmpField field_rf_amp{
{16 * 8, 0 * 16}};
{16 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{21 * 8, 0 * 16}};
{21 * 8, UI_POS_Y(0)}};
RSSI rssi{
{24 * 8, 0, 6 * 8, 4}};
@@ -147,15 +157,6 @@ class BLECommView : public View {
Channel channel{
{24 * 8, 5, 6 * 8, 4}};
Labels label_send_adv{
{{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,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
Checkbox check_log{
{24 * 8, 2 * 8},
3,
@@ -163,14 +164,14 @@ class BLECommView : public View {
true};
Labels label_packets_sent{
{{0 * 8, 4 * 8}, "Packets Left:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 4 * 8}, "Packets Left:", Theme::getInstance()->fg_light->foreground}};
Text text_packets_sent{
{13 * 8, 2 * 16, 12 * 8, 16},
"-"};
Console console{
{0, 4 * 16, 240, 240}};
{0, 4 * 16, ui::screen_width, ui::screen_height - 80}};
std::string str_log{""};
bool logging{false};
File diff suppressed because it is too large Load Diff
+123 -56
View File
@@ -2,6 +2,7 @@
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2023 TJ Baginski
* Copyright (C) 2025 Tommaso Ventafridda
*
* This file is part of PortaPack.
*
@@ -33,6 +34,7 @@
#include "ui_record_view.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "database.hpp"
#include "log_file.hpp"
#include "utility.hpp"
#include "usb_serial_thread.hpp"
@@ -40,6 +42,12 @@
#include "recent_entries.hpp"
// Add for heap debugging
// For ChibiOS core functions
#include "ch.h"
#include "chcore.h"
#include "chheap.h"
class BLELogger {
public:
Optional<File::Error> append(const std::filesystem::path& filename) {
@@ -69,24 +77,36 @@ typedef enum {
RESERVED5 = 12,
RESERVED6 = 13,
RESERVED7 = 14,
RESERVED8 = 15
RESERVED8 = 15,
UNKNOWN = 16
} ADV_PDU_TYPE;
typedef enum {
MAC_VENDOR_UNKNOWN = 0,
MAC_VENDOR_FOUND = 1,
MAC_VENDOR_NOT_FOUND = 2,
MAC_DB_NOT_FOUND = 3
} MAC_VENDOR_STATUS;
struct BleRecentEntry {
using Key = uint64_t;
static constexpr Key invalid_key = 0xffffffff;
static constexpr Key invalid_key = 0xFFFFFFFFFFFFF;
uint64_t macAddress;
uint64_t uniqueKey;
int dbValue;
BlePacketData packetData;
std::string timestamp;
std::string dataString;
std::string nameString;
std::string informationString;
bool include_name;
uint16_t numHits;
ADV_PDU_TYPE pduType;
uint8_t channelNumber;
MAC_VENDOR_STATUS vendor_status;
std::string vendor_name;
bool entryFound;
BleRecentEntry()
@@ -94,22 +114,25 @@ struct BleRecentEntry {
}
BleRecentEntry(
const uint64_t macAddress)
: macAddress{macAddress},
const uint64_t uniqueKey)
: uniqueKey{uniqueKey},
dbValue{},
packetData{},
timestamp{},
dataString{},
nameString{},
informationString{},
include_name{},
numHits{},
pduType{},
channelNumber{},
vendor_status{MAC_VENDOR_UNKNOWN},
vendor_name{},
entryFound{} {
}
Key key() const {
return macAddress;
return uniqueKey;
}
};
@@ -126,11 +149,11 @@ class BleRecentEntryDetailView : public View {
void update_data();
void focus() override;
void paint(Painter&) override;
static BLETxPacket build_packet(BleRecentEntry entry_);
private:
NavigationView& nav_;
BleRecentEntry entry_{};
BLETxPacket build_packet();
void on_save_file(const std::string value, BLETxPacket packetToSave);
bool saveFile(const std::filesystem::path& path, BLETxPacket packetToSave);
std::string packetFileBuffer{};
@@ -139,35 +162,42 @@ class BleRecentEntryDetailView : public View {
static constexpr uint8_t total_data_lines{5};
Labels label_mac_address{
{{0 * 8, 0 * 16}, "Mac Address:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), UI_POS_Y(0)}, "Mac Address:", Theme::getInstance()->fg_light->foreground}};
Text text_mac_address{
{12 * 8, 0 * 16, 17 * 8, 16},
{UI_POS_X(12), UI_POS_Y(0), UI_POS_WIDTH(17), UI_POS_HEIGHT(1)},
"-"};
Labels label_pdu_type{
{{0 * 8, 1 * 16}, "PDU Type:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), UI_POS_Y(1)}, "PDU Type:", Theme::getInstance()->fg_light->foreground}};
Text text_pdu_type{
{9 * 8, 1 * 16, 17 * 8, 16},
{9 * 8, UI_POS_Y(1), 17 * 8, UI_POS_HEIGHT(1)},
"-"};
Labels label_vendor{
{{UI_POS_X(0), UI_POS_Y(2)}, "Vendor:", Theme::getInstance()->fg_light->foreground}};
Text text_vendor{
{7 * 8, UI_POS_Y(2), 23 * 8, UI_POS_HEIGHT(1)},
"-"};
Labels labels{
{{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},
{{UI_POS_X(0), UI_POS_Y(3)}, "Len", Theme::getInstance()->fg_light->foreground},
{{5 * 8, UI_POS_Y(3)}, "Type", Theme::getInstance()->fg_light->foreground},
{{10 * 8, UI_POS_Y(3)}, "Value", Theme::getInstance()->fg_light->foreground},
};
Button button_send{
{19, 224, 96, 24},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), 224, 96, 24},
"Send"};
Button button_done{
{125, 224, 96, 24},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), 224, 96, 24},
"Done"};
Button button_save{
{72, 264, 96, 24},
{UI_POS_X_CENTER(12), 264, 96, 24},
"Save"};
bool send_updates{false};
@@ -186,7 +216,7 @@ class BLERxView : public View {
~BLERxView();
void set_parent_rect(const Rect new_parent_rect) override;
void paint(Painter&) override{};
void paint(Painter&) override {};
void focus() override;
@@ -198,15 +228,19 @@ class BLERxView : public View {
bool saveFile(const std::filesystem::path& path);
std::unique_ptr<UsbSerialThread> usb_serial_thread{};
void on_data(BlePacketData* packetData);
void log_ble_packet(BlePacketData* packet);
void on_filter_change(std::string value);
void on_file_changed(const std::filesystem::path& new_file_path);
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);
bool handle_filter_options(uint8_t index, const BleRecentEntry& entry);
bool updateEntry(const BlePacketData* packet, BleRecentEntry& entry, ADV_PDU_TYPE pdu_type);
bool parse_beacon_data(const uint8_t* data, uint8_t length, std::string& nameString, std::string& informationString);
bool parse_tracking_beacon_data(const uint8_t* data, uint8_t length, std::string& nameString, std::string& informationString);
NavigationView& nav_;
RxRadioState radio_state_{
2402000000 /* frequency */,
4000000 /* bandwidth */,
@@ -216,9 +250,12 @@ class BLERxView : public View {
uint8_t channel_index{0};
uint8_t sort_index{0};
uint8_t filter_index{0};
bool uniqueParsing = false;
bool duplicatePackets = false;
std::string filter{};
bool logging{false};
bool serial_logging{false};
bool async_tx_states_when_entered{false};
bool name_enable{true};
app_settings::SettingsManager settings_{
@@ -233,6 +270,8 @@ class BLERxView : public View {
// 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},
{"unique_parsing"sv, &uniqueParsing},
{"duplicate_packets"sv, &duplicatePackets},
}};
std::string str_console = "";
@@ -242,7 +281,7 @@ class BLERxView : public View {
bool auto_channel = false;
int16_t timer_count{0};
int16_t timer_period{6}; // 100ms
int16_t timer_period{1}; // 25ms
std::string filterBuffer{};
std::string listFileBuffer{};
@@ -258,11 +297,11 @@ class BLERxView : public View {
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 = 9 * 8;
static constexpr auto header_height = 12 * 8;
static constexpr auto switch_button_height = 3 * 16;
OptionsField options_channel{
{0 * 8, 0 * 8},
{UI_POS_X(0), UI_POS_Y(0)},
5,
{{"Ch.37", 37},
{"Ch.38", 38},
@@ -270,45 +309,50 @@ class BLERxView : public View {
{"Auto", 40}}};
RxFrequencyField field_frequency{
{6 * 8, 0 * 16},
{UI_POS_X(6), UI_POS_Y(0)},
nav_};
RFAmpField field_rf_amp{
{16 * 8, 0 * 16}};
{UI_POS_X(16), UI_POS_Y(0)}};
LNAGainField field_lna{
{18 * 8, 0 * 16}};
{UI_POS_X(18), UI_POS_Y(0)}};
VGAGainField field_vga{
{21 * 8, 0 * 16}};
{UI_POS_X(21), UI_POS_Y(0)}};
RSSI rssi{
{24 * 8, 0, 6 * 8, 4}};
{UI_POS_X(24), 0, UI_POS_WIDTH_REMAINING(24), 4}};
Channel channel{
{24 * 8, 5, 6 * 8, 4}};
{UI_POS_X(24), 5, UI_POS_WIDTH_REMAINING(24), 4}};
Labels label_sort{
{{0 * 8, 2 * 8}, "Sort:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), UI_POS_Y(1)}, "Sort:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_sort{
{5 * 8, 2 * 8},
{5 * 8, UI_POS_Y(1)},
4,
{{"MAC", 0},
{"Hits", 1},
{"dB", 2},
{"Time", 3},
{"Name", 4}}};
{"Name", 4},
{"Info", 5}}};
Button button_filter{
{11 * 8, 2 * 8, 7 * 8, 16},
{11 * 8, UI_POS_Y(1), 7 * 8, 16},
"Filter:"};
OptionsField options_filter{
{18 * 8 + 2, 2 * 8},
4,
{18 * 8 + 2, UI_POS_Y(1)},
7,
{{"Data", 0},
{"MAC", 1}}};
{"MAC", 1},
{"Name", 2},
{"Info", 3},
{"Vendor", 4},
{"Channel", 5}}};
Checkbox check_log{
{10 * 8, 4 * 8 + 2},
@@ -317,41 +361,50 @@ class BLERxView : public View {
true};
Checkbox check_name{
{0 * 8, 4 * 8 + 2},
{UI_POS_X(0), 4 * 8 + 2},
3,
"Name",
true};
Button button_find{
{0 * 8, 7 * 8 - 2, 4 * 8, 16},
"Find"};
Labels label_found{
{{5 * 8, 7 * 8 - 2}, "Found:", Theme::getInstance()->fg_light->foreground}};
Text text_found_count{
{11 * 8, 7 * 8 - 2, 20 * 8, 16},
"0/0"};
Checkbox check_serial_log{
{18 * 8 + 2, 4 * 8 + 2},
7,
"USB Log",
true};
// Console console{
// {0, 10 * 8, 240, 240}};
Checkbox check_unique{
{0 * 8 + 2, 7 * 8 + 2},
7,
"Unique",
true};
Checkbox check_duplicate_packets{
{10 * 8 + 2, 7 * 8 + 2},
7,
"Duplicate",
true};
Button button_find{
{UI_POS_X(0), 10 * 8 - 2, 4 * 8, 16},
"Find"};
Labels label_found{
{{5 * 8, 10 * 8 - 2}, "Found:", Theme::getInstance()->fg_light->foreground}};
Text text_found_count{
{11 * 8, 10 * 8 - 2, 20 * 8, 16},
"0/0"};
Button button_clear_list{
{2 * 8, 320 - (16 + 32), 7 * 8, 32},
{2 * 8, screen_height - (16 + 32), 7 * 8, 32},
"Clear"};
Button button_save_list{
{11 * 8, 320 - (16 + 32), 11 * 8, 32},
{UI_POS_X_CENTER(11), screen_height - (16 + 32), 11 * 8, 32},
"Export CSV"};
Button button_switch{
{240 - 6 * 8, 320 - (16 + 32), 4 * 8, 32},
{UI_POS_X_RIGHT(6), screen_height - (16 + 32), 4 * 8, 32},
"Tx"};
std::string str_log{""};
@@ -360,10 +413,10 @@ class BLERxView : public View {
BleRecentEntries recent{};
BleRecentEntries tempList{};
const RecentEntriesColumns columns{{
{"Mac Address", 17},
RecentEntriesColumns columns{{
{"Name", 0},
{"Hits", 7},
{"dB", 4},
{"dBm", 4},
}};
BleRecentEntriesView recent_entries_view{columns, recent};
@@ -380,6 +433,20 @@ class BLERxView : public View {
[this](const Message* const) {
this->on_timer();
}};
// Widget to control the list limit
Labels label_max_entries{
{{UI_POS_X(22), 10 * 8 - 2}, "List:", Theme::getInstance()->fg_light->foreground}};
NumberField field_max_entries{
{UI_POS_X(28), 10 * 8 - 2}, // Correct: Position (x, y)
2, // Number of digits
{5, 64}, // Range (min, max) <- Coincides with max_entries
1, // Step size
' ' // Filler character
};
// Variable to store the current limit
size_t max_recent_entries = 25;
}; /* BLERxView */
} /* namespace ui */
+166 -101
View File
@@ -35,6 +35,7 @@
#include "rtc_time.hpp"
#include "string_format.hpp"
#include "file_path.hpp"
#include "usb_serial_asyncmsg.hpp"
using namespace portapack;
using namespace modems;
@@ -115,7 +116,7 @@ static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) {
freq_hz = 2'428'000'000ull + (channel_number - 11) * 2'000'000ull;
break;
default:
freq_hz = UINT64_MAX;
freq_hz = 2'402'000'000ull; // Default to channel 37 frequency if invalid channel number
}
return freq_hz;
@@ -123,6 +124,31 @@ static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) {
namespace ui {
PKT_TYPE get_pkt_type_from_string(const std::string& type_str) {
if (type_str == "RAW") {
return PKT_TYPE_RAW;
} else if (type_str == "DISCOVERY") {
return PKT_TYPE_DISCOVERY;
} else if (type_str == "IBEACON") {
return PKT_TYPE_IBEACON;
} else if (type_str == "ADV_IND") {
return PKT_TYPE_ADV_IND;
} else if (type_str == "ADV_DIRECT_IND") {
return PKT_TYPE_ADV_DIRECT_IND;
} else if (type_str == "ADV_NONCONN_IND") {
return PKT_TYPE_ADV_NONCONN_IND;
} else if (type_str == "ADV_SCAN_IND") {
return PKT_TYPE_ADV_SCAN_IND;
} else if (type_str == "SCAN_REQ") {
return PKT_TYPE_SCAN_REQ;
} else if (type_str == "SCAN_RSP") {
return PKT_TYPE_SCAN_RSP;
} else if (type_str == "CONNECT_REQ") {
return PKT_TYPE_CONNECT_REQ;
}
return PKT_TYPE_INVALID_TYPE;
}
void BLETxView::focus() {
button_open.focus();
}
@@ -167,29 +193,13 @@ void BLETxView::toggle() {
}
}
void BLETxView::start() {
baseband::run_image(portapack::spi_flash::image_tag_btle_tx);
transmitter_model.enable();
void BLETxView::send_packet() {
// Generate new random Mac Address.
transmitter_model.enable();
generateRandomMacAddress(randomMac);
// If this is our first run, check file.
if (!is_active()) {
File data_file;
auto error = data_file.open(file_path);
if (error && !file_override) {
file_error();
check_loop.set_value(false);
return;
}
button_play.set_bitmap(&bitmap_stop);
is_running = true;
}
char advertisementData[63] = {0};
packets[current_packet].advertisementData[62] = '\0';
strcpy(advertisementData, packets[current_packet].advertisementData);
// TODO: Make this a checkbox.
@@ -216,7 +226,7 @@ void BLETxView::start() {
uint8_t min = 0x00;
uint8_t max = 0x0F;
hexDigit = min + std::rand() % (max - min + 1);
hexDigit = min + rand() % (max - min + 1);
} break;
default:
hexDigit = 0;
@@ -235,9 +245,30 @@ void BLETxView::start() {
}
}
// Setup next packet configuration.
progressbar.set_max(packets[current_packet].packet_count);
baseband::set_btletx(channel_number, random_mac ? randomMac : packets[current_packet].macAddress, advertisementData, pduType);
progressbar.set_value(packets[current_packet].packet_count - packet_counter);
text_packets_sent.set(to_string_dec_uint(packet_counter));
baseband::set_btletx(channel_number, random_mac ? randomMac : packets[current_packet].macAddress, advertisementData, packets[current_packet].pduType);
packetDone = false;
}
void BLETxView::start() {
if (file_path.empty() || num_packets == 0) {
file_error();
check_loop.set_value(false);
return;
}
baseband::run_image(portapack::spi_flash::image_tag_btle_tx);
transmitter_model.enable();
button_play.set_bitmap(&bitmap_stop);
is_running = true;
advCount = 0;
send_packet();
}
void BLETxView::stop() {
@@ -253,73 +284,83 @@ void BLETxView::stop() {
is_running = false;
}
void BLETxView::reset() {
transmitter_model.disable();
baseband::shutdown();
start();
}
// called each 1/60th of second, so 6 = 100ms
void BLETxView::on_timer() {
if (++timer_count == timer_period) {
timer_count = 0;
if (is_active()) {
// Reached end of current packet repeats.
if (packet_counter == 0) {
// Done sending all packets.
if (current_packet == (num_packets - 1)) {
current_packet = 0;
// If looping, restart from beginning.
if (check_loop.value()) {
update_current_packet(packets[current_packet], current_packet);
reset();
} else {
stop();
}
} else {
current_packet++;
update_current_packet(packets[current_packet], current_packet);
reset();
}
} else {
reset();
}
}
}
if (++auto_channel_counter == auto_channel_period) {
auto_channel_counter = 0;
if (auto_channel) {
int min = 37;
int max = 39;
channel_number = min + std::rand() % (max - min + 1);
field_frequency.set_value(get_freq_by_channel_number(channel_number));
if (is_active() && packetDone) {
send_packet();
}
}
}
void BLETxView::on_tx_progress(const bool done) {
void BLETxView::on_tx_progress(const bool done, uint32_t progress) {
// TODO: make use of progress variable
(void)progress;
if (!is_active() || num_packets == 0) {
return;
}
if (done) {
if (is_active()) {
if ((packet_counter % 10) == 0) {
text_packets_sent.set(to_string_dec_uint(packet_counter));
}
transmitter_model.disable();
packet_counter--;
progressbar.set_value(packets[current_packet].packet_count - packet_counter);
if (auto_channel) {
advCount++;
if (advCount == 3) {
channel_number = 37;
field_frequency.set_value(get_freq_by_channel_number(channel_number));
if (packet_counter > 0) packet_counter--;
packetDone = true;
advCount = 0;
} else {
channel_number = 37 + advCount;
field_frequency.set_value(get_freq_by_channel_number(channel_number));
send_packet();
}
} else if (all_channels) {
advCount++;
if (advCount == 40) {
channel_number = 0;
field_frequency.set_value(get_freq_by_channel_number(channel_number));
if (packet_counter > 0) packet_counter--;
packetDone = true;
advCount = 0;
} else {
channel_number = advCount;
field_frequency.set_value(get_freq_by_channel_number(channel_number));
send_packet();
}
} else {
if (packet_counter > 0) packet_counter--;
packetDone = true;
}
}
}
// Reached end of current packet repeats.
if (packet_counter == 0) {
// Done sending all packets.
current_packet++;
if (current_packet >= num_packets) {
current_packet = 0;
// If looping, restart from beginning.
if (check_loop.value()) {
update_current_packet(packets[current_packet], current_packet);
} else {
stop();
}
} else {
update_current_packet(packets[current_packet], current_packet);
}
}
}
BLETxView::BLETxView(NavigationView& nav)
: nav_{nav} {
add_children({&button_open,
add_children({&dataEditView,
&button_open,
&text_filename,
&progressbar,
&check_rand_mac,
@@ -330,7 +371,6 @@ BLETxView::BLETxView(NavigationView& nav)
&label_speed,
&options_speed,
&options_channel,
&options_adv_type,
&label_marked_data,
&marked_data_sequence,
&label_packet_index,
@@ -340,7 +380,6 @@ BLETxView::BLETxView(NavigationView& nav)
&label_mac_address,
&text_mac_address,
&label_data_packet,
&dataEditView,
&button_clear_marked,
&button_save_packet,
&button_switch});
@@ -354,16 +393,20 @@ BLETxView::BLETxView(NavigationView& nav)
};
options_channel.on_change = [this](size_t, int32_t i) {
// If we selected Auto don't do anything and Auto will handle changing.
if (i == 40) {
auto_channel = true;
return;
auto_channel = (i == BLT_CHAN_AUTO);
all_channels = (i == BLT_CHAN_ALL);
advCount = 0;
if (auto_channel) {
channel_number = 37;
} else if (all_channels) {
channel_number = 0;
} else {
auto_channel = false;
channel_number = i;
}
field_frequency.set_value(get_freq_by_channel_number(i));
channel_number = i;
field_frequency.set_value(get_freq_by_channel_number(channel_number));
};
options_speed.on_change = [this](size_t, int32_t i) {
@@ -371,13 +414,8 @@ BLETxView::BLETxView(NavigationView& nav)
timer_count = 0;
};
options_adv_type.on_change = [this](size_t, int32_t i) {
pduType = (PKT_TYPE)i;
};
options_speed.set_selected_index(0);
options_channel.set_selected_index(0);
options_adv_type.set_selected_index(0);
options_channel.set_by_value(BLT_CHAN_AUTO);
check_rand_mac.set_value(false);
check_rand_mac.on_select = [this](Checkbox&, bool v) {
@@ -387,8 +425,7 @@ BLETxView::BLETxView(NavigationView& nav)
button_open.on_select = [this](Button&) {
auto open_view = nav_.push<FileLoadView>(".TXT");
open_view->on_changed = [this](std::filesystem::path new_file_path) {
on_file_changed(new_file_path);
this->on_file_changed(new_file_path);
nav_.set_on_pop([this]() { button_play.focus(); });
};
};
@@ -399,8 +436,9 @@ BLETxView::BLETxView(NavigationView& nav)
nav,
packetFileBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
on_save_file(buffer);
this->on_save_file(buffer);
});
};
@@ -429,6 +467,23 @@ BLETxView::BLETxView(NavigationView& nav)
}
};
dataEditView.on_change = [this](uint8_t value) {
// Reject setting newline at index 29.
if (cursor_pos.col != 29) {
uint16_t dataBytePos = (cursor_pos.line * 29) + cursor_pos.col;
packets[current_packet].advertisementData[dataBytePos] = uint_to_char(value, 16);
update_current_packet(packets[current_packet], current_packet);
}
};
dataEditView.on_cursor_moved = [this]() {
// Save last selected cursor.
cursor_pos.line = dataEditView.line();
cursor_pos.col = dataEditView.col();
};
button_clear_marked.on_select = [this](Button&) {
marked_counter = 0;
markedBytes.clear();
@@ -462,19 +517,27 @@ void BLETxView::on_file_changed(const fs::path& new_file_path) {
}
do {
readUntil(data_file, packets[num_packets].macAddress, mac_address_size_str, ' ');
readUntil(data_file, packets[num_packets].advertisementData, max_packet_size_str, ' ');
readUntil(data_file, packets[num_packets].packetCount, max_packet_repeat_str, '\n');
readUntil(data_file, packets[num_packets].macAddress, sizeof(packets[num_packets].macAddress), ' ');
readUntil(data_file, packets[num_packets].advertisementData, sizeof(packets[num_packets].advertisementData), ' ');
readUntil(data_file, packets[num_packets].packetType, sizeof(packets[num_packets].packetType), ' ');
readUntil(data_file, packets[num_packets].packetCount, sizeof(packets[num_packets].packetCount), '\n');
uint64_t macAddressSize = strlen(packets[num_packets].macAddress);
uint64_t advertisementDataSize = strlen(packets[num_packets].advertisementData);
uint64_t packetCountSize = strlen(packets[num_packets].packetCount);
uint64_t packetTypeSize = strlen(packets[num_packets].packetType);
packets[num_packets].packet_count = stringToUint32(packets[num_packets].packetCount);
packets[num_packets].pduType = (PKT_TYPE)get_pkt_type_from_string(packets[num_packets].packetType);
// Verify Data.
if ((macAddressSize == mac_address_size_str) && (advertisementDataSize < max_packet_size_str) && (packetCountSize < max_packet_repeat_str) &&
hasValidHexPairs(packets[num_packets].macAddress, macAddressSize / 2) && hasValidHexPairs(packets[num_packets].advertisementData, advertisementDataSize / 2) && (packets[num_packets].packet_count >= 1) && (packets[num_packets].packet_count < max_packet_repeat_count)) {
if ((macAddressSize == mac_address_size_str) &&
(advertisementDataSize < max_packet_size_str) &&
(packetCountSize < max_packet_repeat_str) &&
hasValidHexPairs(packets[num_packets].macAddress, macAddressSize / 2) &&
hasValidHexPairs(packets[num_packets].advertisementData, advertisementDataSize / 2) &&
(packets[num_packets].packet_count >= 1) && (packets[num_packets].packet_count < max_packet_repeat_count) &&
(packetTypeSize <= max_packet_type_str)) {
text_filename.set(truncate(file_path.filename().string(), 12));
} else {
@@ -490,7 +553,7 @@ void BLETxView::on_file_changed(const fs::path& new_file_path) {
num_packets++;
} while (num_packets < max_num_packets);
data_file.close();
update_current_packet(packets[0], 0);
}
}
@@ -516,24 +579,25 @@ void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex)
std::vector<std::string> strings = splitIntoStrings(packet.advertisementData);
packet_counter = packet.packet_count;
current_packet = currentIndex;
text_packet_index.set(to_string_dec_uint(current_packet));
text_packets_sent.set(to_string_dec_uint(packet.packet_count));
text_mac_address.set(formattedMacAddress);
packet_counter = packet.packet_count;
current_packet = currentIndex;
progressbar.set_max(packet.packet_count);
dataFile.create(dataTempFilePath);
for (const std::string& str : strings) {
dataFile.write(str.c_str(), str.size());
dataFile.write("\n", 1);
;
}
dataFile.~File();
dataFile.close();
auto result = FileWrapper::open(dataTempFilePath);
@@ -545,6 +609,7 @@ void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex)
dataEditView.set_font_zoom(true);
dataEditView.set_file(*dataFileWrapper);
dataEditView.redraw(true, true);
dataEditView.cursor_set(cursor_pos.line, cursor_pos.col);
}
void BLETxView::set_parent_rect(const Rect new_parent_rect) {
+81 -37
View File
@@ -38,11 +38,16 @@
#include "utility.hpp"
#include "file_path.hpp"
#include "ble_tx_app.hpp"
#include "recent_entries.hpp"
#include <string>
#include <memory>
#define BLT_CHAN_AUTO 40
#define BLT_CHAN_ALL 41
class BLELoggerTx {
public:
Optional<File::Error> append(const std::filesystem::path& filename) {
@@ -91,8 +96,9 @@ struct BLETxPacket {
char macAddress[13];
char advertisementData[63];
char packetCount[11];
uint32_t packet_count;
PKT_TYPE packetType;
char packetType[17];
uint32_t packet_count; // repeat the same packet count times
PKT_TYPE pduType;
};
class BLETxView : public View {
@@ -102,15 +108,15 @@ class BLETxView : public View {
~BLETxView();
void set_parent_rect(const Rect new_parent_rect) override;
void paint(Painter&) override{};
void paint(Painter&) override {};
void focus() override;
bool is_active() const;
void toggle();
void start();
void send_packet();
void stop();
void reset();
void handle_replay_thread_done(const uint32_t return_code);
void file_error();
bool saveFile(const std::filesystem::path& path);
@@ -122,7 +128,7 @@ class BLETxView : public View {
void on_data(uint32_t value, bool is_data);
void on_file_changed(const std::filesystem::path& new_file_path);
void on_save_file(const std::string value);
void on_tx_progress(const bool done);
void on_tx_progress(const bool done, uint32_t progress);
void on_random_data_change(std::string value);
void update_current_packet(BLETxPacket packet, uint32_t currentIndex);
@@ -142,6 +148,7 @@ class BLETxView : public View {
std::filesystem::path packet_save_path{bletx_dir / u"BLETX_????.TXT"};
uint8_t channel_number = 37;
bool auto_channel = false;
bool all_channels = false;
char randomMac[13] = "010203040506";
@@ -153,9 +160,11 @@ class BLETxView : public View {
int16_t auto_channel_period{6};
bool repeatLoop = false;
uint32_t packet_counter{0};
uint32_t num_packets{0};
uint32_t current_packet{0};
uint32_t packet_counter{0}; // the counter (decr) of the actual packet as it can be sent multiple times
uint32_t num_packets{0}; // how many packets in the file
uint32_t current_packet{0}; // the actual packet's num, i'm sending if i have multiple packets
uint8_t packetTxCount{0}; // not used
bool packetDone = false;
bool random_mac = false;
bool file_override = false;
@@ -170,39 +179,39 @@ class BLETxView : public View {
std::vector<uint16_t> markedBytes{};
CursorPos cursor_pos{};
uint8_t marked_counter = 0;
uint8_t advCount = 0;
static constexpr uint8_t mac_address_size_str{12};
static constexpr uint8_t max_packet_size_str{62};
static constexpr uint8_t max_packet_repeat_str{10};
static constexpr uint8_t max_packet_type_str{16};
static constexpr uint32_t max_packet_repeat_count{UINT32_MAX};
static constexpr uint32_t max_num_packets{32};
BLETxPacket packets[max_num_packets];
PKT_TYPE pduType = {PKT_TYPE_DISCOVERY};
static constexpr auto header_height = 10 * 16;
static constexpr auto switch_button_height = 6 * 16;
Button button_open{
{0 * 8, 0 * 16, 10 * 8, 2 * 16},
{UI_POS_X(0), UI_POS_Y(0), 10 * 8, 2 * 16},
"Open file"};
Text text_filename{
{11 * 8, 0 * 16, 12 * 8, 16},
{11 * 8, UI_POS_Y(0), UI_POS_WIDTH_REMAINING(11), 16},
"-"};
ProgressBar progressbar{
{11 * 8, 1 * 16, 9 * 8, 16}};
{11 * 8, 1 * 16, UI_POS_WIDTH_REMAINING(11 + 10), 16}};
Checkbox check_rand_mac{
{21 * 8, 1 * 16},
{UI_POS_X_RIGHT(9), 1 * 16},
6,
"?? Mac",
true};
TxFrequencyField field_frequency{
{0 * 8, 2 * 16},
{UI_POS_X(0), 2 * 16},
nav_};
TransmitterView2 tx_view{
@@ -210,36 +219,74 @@ class BLETxView : public View {
/*short_ui*/ true};
Checkbox check_loop{
{21 * 8, 2 * 16},
{UI_POS_X_RIGHT(9), 2 * 16},
4,
"Loop",
true};
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
{UI_POS_X_RIGHT(2), 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
Labels label_speed{
{{0 * 8, 6 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 6 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_speed{
{7 * 8, 6 * 8},
3,
{{"1 ", 1}, // 16ms
{"2 ", 2}, // 32ms
{"3 ", 3}, // 48ms
{"4 ", 6}, // 100ms
{"5 ", 12}}}; // 200ms
{{"1 ", 2}, // 25ms
{"2 ", 4}, // 50ms
{"3 ", 6}, // 75ms
{"4 ", 8}, // 100ms
{"5 ", 12}}}; // 150ms
OptionsField options_channel{
{11 * 8, 6 * 8},
5,
{{"Ch.37 ", 37},
{{"Ch.0", 0},
{"Ch.1", 1},
{"Ch.2", 2},
{"Ch.3", 3},
{"Ch.4", 4},
{"Ch.5", 5},
{"Ch.6", 6},
{"Ch.7", 7},
{"Ch.8", 8},
{"Ch.9", 9},
{"Ch.10", 10},
{"Ch.11", 11},
{"Ch.12", 12},
{"Ch.13", 13},
{"Ch.14", 14},
{"Ch.15", 15},
{"Ch.16", 16},
{"Ch.17", 17},
{"Ch.18", 18},
{"Ch.19", 19},
{"Ch.20", 20},
{"Ch.21", 21},
{"Ch.22", 22},
{"Ch.23", 23},
{"Ch.24", 24},
{"Ch.25", 25},
{"Ch.26", 26},
{"Ch.27", 27},
{"Ch.28", 28},
{"Ch.29", 29},
{"Ch.30", 30},
{"Ch.31", 31},
{"Ch.32", 32},
{"Ch.33", 33},
{"Ch.34", 34},
{"Ch.35", 35},
{"Ch.36", 36},
{"Ch.37", 37},
{"Ch.38", 38},
{"Ch.39", 39},
{"Auto", 40}}};
{"Auto", BLT_CHAN_AUTO},
{"All", BLT_CHAN_ALL}}};
OptionsField options_adv_type{
{17 * 8, 6 * 8},
@@ -254,7 +301,7 @@ class BLETxView : public View {
{"CONNECT_REQ", PKT_TYPE_CONNECT_REQ}}};
Labels label_marked_data{
{{0 * 8, 4 * 16}, "Marked Data:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 4 * 16}, "Marked Data:", Theme::getInstance()->fg_light->foreground}};
OptionsField marked_data_sequence{
{12 * 8, 8 * 8},
@@ -264,45 +311,42 @@ class BLETxView : public View {
{"Random", 2}}};
Labels label_packet_index{
{{0 * 8, 12 * 8}, "Packet Index:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 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:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 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:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 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:", Theme::getInstance()->fg_light->foreground}};
Console console{
{0, 9 * 18, 240, 240}};
{{UI_POS_X(0), 9 * 16}, "Packet Data:", Theme::getInstance()->fg_light->foreground}};
TextViewer dataEditView{
{0, 9 * 18, 240, 240}};
Button button_clear_marked{
{1 * 8, 14 * 16, 13 * 8, 3 * 8},
{UI_POS_X(1), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(13), UI_POS_HEIGHT(1.5)},
"Clear Marked"};
Button button_save_packet{
{1 * 8, 16 * 16, 13 * 8, 2 * 16},
{UI_POS_X(1), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(13), UI_POS_HEIGHT(2)},
"Save Packet"};
Button button_switch{
{16 * 8, 16 * 16, 13 * 8, 2 * 16},
{UI_POS_X_RIGHT(14), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(13), UI_POS_HEIGHT(2)},
"Switch to Rx"};
std::string str_log{""};
@@ -323,7 +367,7 @@ class BLETxView : public View {
Message::ID::TXProgress,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
this->on_tx_progress(message.done);
this->on_tx_progress(message.done, message.progress);
}};
MessageHandlerRegistration message_handler_frame_sync{
+11 -9
View File
@@ -55,22 +55,24 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
this->field_frequency.set_step(v);
};
option_format.set_selected_index(0); // Default to C16
option_format.set_selected_index(file_format);
option_format.on_change = [this](size_t, uint32_t file_type) {
file_format = file_type;
record_view.set_file_type((RecordView::FileType)file_type);
};
check_trim.set_value(trim);
check_trim.on_select = [this](Checkbox&, bool v) {
trim = v;
record_view.set_auto_trim(v);
};
freqman_set_bandwidth_option(SPEC_MODULATION, option_bandwidth);
option_bandwidth.on_change = [this](size_t, uint32_t bandwidth) {
option_bandwidth.on_change = [this](size_t, uint32_t new_capture_rate) {
/* Nyquist would imply a sample rate of 2x bandwidth, but because the ADC
* provides 2 values (I,Q), the sample_rate is equal to bandwidth here. */
auto sample_rate = bandwidth;
/* base_rate (bandwidth) is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* capture_rate (bandwidth) is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz, when selected 1 Mhz BW ... */
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card. */
@@ -78,7 +80,7 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
// record_view determines the correct oversampling to apply and returns the actual sample rate.
// NB: record_view is what actually updates proc_capture baseband settings.
auto actual_sample_rate = record_view.set_sampling_rate(sample_rate);
auto actual_sample_rate = record_view.set_sampling_rate(new_capture_rate);
// Update the radio model with the actual sampling rate.
receiver_model.set_sampling_rate(actual_sample_rate);
@@ -88,19 +90,19 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
receiver_model.set_baseband_bandwidth(anti_alias_filter_bandwidth);
// Automatically switch default capture format to C8 when bandwidth setting is increased to >=1.5MHz anb back to C16 for <=1,25Mhz
if ((bandwidth >= 1500000) && (previous_bandwidth < 1500000)) {
if ((new_capture_rate >= 1500000) && (capture_rate < 1500000)) {
option_format.set_selected_index(1); // Default C8 format for REC, 1500K ... 5500k
}
if ((bandwidth <= 1250000) && (previous_bandwidth > 1250000)) {
if ((new_capture_rate <= 1250000) && (capture_rate > 1250000)) {
option_format.set_selected_index(0); // Default C16 format for REC , 12k5 ... 1250K
}
previous_bandwidth = bandwidth;
capture_rate = new_capture_rate;
waterfall.start();
};
receiver_model.enable();
option_bandwidth.set_by_value(500000);
option_bandwidth.set_by_value(capture_rate);
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
+20 -11
View File
@@ -48,39 +48,48 @@ class CaptureAppView : public View {
private:
static constexpr ui::Dim header_height = 3 * 16;
uint32_t previous_bandwidth{500000};
uint32_t capture_rate{500000};
uint32_t file_format{0};
bool trim{false};
NavigationView& nav_;
RxRadioState radio_state_{ReceiverModel::Mode::Capture};
app_settings::SettingsManager settings_{
"rx_capture", app_settings::Mode::RX};
"rx_capture",
app_settings::Mode::RX,
{
{"capture_rate"sv, &capture_rate},
{"file_format"sv, &file_format},
{"trim"sv, &trim},
}};
Labels labels{
{{0 * 8, 1 * 16}, "Rate:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 1 * 16}, "Rate:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 1 * 16}, "Format:", Theme::getInstance()->fg_light->foreground},
};
RSSI rssi{
{24 * 8, 0, 6 * 8, 4}};
{UI_POS_X(24), 0, UI_POS_WIDTH_REMAINING(24), 4}};
Channel channel{
{24 * 8, 5, 6 * 8, 4}};
{UI_POS_X(24), 5, UI_POS_WIDTH_REMAINING(24), 4}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
FrequencyStepView field_frequency_step{
{10 * 8, 0 * 16}};
{10 * 8, UI_POS_Y(0)}};
RFAmpField field_rf_amp{
{16 * 8, 0 * 16}};
{16 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{21 * 8, 0 * 16}};
{21 * 8, UI_POS_Y(0)}};
OptionsField option_bandwidth{
{5 * 8, 1 * 16},
@@ -100,7 +109,7 @@ class CaptureAppView : public View {
/*small*/ true};
RecordView record_view{
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
{UI_POS_X(0), 2 * 16, screen_width, 1 * 16},
u"BBD_????.*",
captures_dir,
RecordView::FileType::RawS16,
+131 -18
View File
@@ -28,6 +28,7 @@
#include "string_format.hpp"
#include "utility.hpp"
#include "file_path.hpp"
#include "usb_serial_asyncmsg.hpp"
using namespace portapack;
using namespace pocsag;
@@ -56,20 +57,24 @@ POCSAGSettingsView::POCSAGSettingsView(
: settings_{settings} {
add_children(
{&labels,
&opt_baud_rate,
&check_log,
&check_log_raw,
&check_small_font,
&check_hide_bad,
&check_hide_addr_only,
&check_numeric_detect,
&opt_filter_mode,
&field_filter_address,
&button_save});
opt_baud_rate.set_by_value(settings_.baud_rate);
check_log.set_value(settings_.enable_logging);
check_log_raw.set_value(settings_.enable_raw_log);
check_small_font.set_value(settings_.enable_small_font);
check_hide_bad.set_value(settings_.hide_bad_data);
check_hide_addr_only.set_value(settings_.hide_addr_only);
check_numeric_detect.set_value(settings_.enable_numeric_detect);
opt_filter_mode.set_by_value(settings_.filter_mode);
field_filter_address.set_value(settings_.filter_address);
@@ -79,9 +84,10 @@ POCSAGSettingsView::POCSAGSettingsView(
settings_.enable_small_font = check_small_font.value();
settings_.hide_bad_data = check_hide_bad.value();
settings_.hide_addr_only = check_hide_addr_only.value();
settings_.enable_numeric_detect = check_numeric_detect.value();
settings_.filter_mode = opt_filter_mode.selected_index_value();
settings_.filter_address = field_filter_address.to_integer();
settings_.baud_rate = opt_baud_rate.selected_index_value();
nav.pop();
};
}
@@ -142,7 +148,7 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
audio::output::start();
receiver_model.enable();
baseband::set_pocsag();
baseband::set_pocsag((int8_t)settings_.baud_rate);
}
void POCSAGAppView::focus() {
@@ -182,6 +188,7 @@ void POCSAGAppView::refresh_ui() {
btn_text = "Filter Last";
break;
}
baseband::set_pocsag((int8_t)settings_.baud_rate);
button_filter_last.set_text(btn_text);
}
@@ -216,12 +223,24 @@ void POCSAGAppView::handle_decoded(Timestamp timestamp, const std::string& prefi
return;
}
// Color indicates the message has a lot of decoding errors.
std::string color = bad_data ? STR_COLOR_MAGENTA : STR_COLOR_WHITE;
// Type indicator with its own color.
std::string type_str;
bool numeric_detect = settings_.enable_numeric_detect;
if (numeric_detect && pocsag_state.detected == pocsag::DET_NUMERIC)
type_str = STR_COLOR_GREEN "n";
else if (numeric_detect && pocsag_state.detected == pocsag::DET_ALPHA)
type_str = STR_COLOR_LIGHT_GREY "a";
else if (pocsag_state.out_type == ADDRESS)
type_str = STR_COLOR_DARK_YELLOW "t";
else
type_str = "";
std::string console_info = "\n" + color + prefix;
console_info += " #" + to_string_dec_uint(pocsag_state.address);
// Header: timestamp+baud in light grey, capcode+function in white.
std::string console_info = "\n" STR_COLOR_LIGHT_GREY + prefix;
console_info += STR_COLOR_WHITE " #" + to_string_dec_uint(pocsag_state.address);
console_info += " F" + to_string_dec_uint(pocsag_state.function);
if (!type_str.empty())
console_info += " " + type_str;
if (pocsag_state.out_type == ADDRESS) {
last_address = pocsag_state.address;
@@ -237,23 +256,111 @@ void POCSAGAppView::handle_decoded(Timestamp timestamp, const std::string& prefi
}
}
/* Serial: tone-only page */
if (portapack::usb_serial.serial_connected()) {
std::string s = "\r\nPOCSAG " + to_string_dec_uint(current_bitrate) + " " + to_string_dec_uint(pocsag_state.address) + " " + std::string(1, 'A' + pocsag_state.function) + (current_inverted ? " I" : " S") + " tone";
UsbSerialAsyncmsg::asyncmsg(s);
}
} else if (pocsag_state.out_type == MESSAGE) {
if (pocsag_state.address != last_address) {
// New message
if (pocsag_state.new_message) {
last_address = pocsag_state.address;
serial_numeric_sent = 0;
console.writeln(console_info);
console.write(color + pocsag_state.output);
// If heuristic chose numeric, show numeric line first (green).
if (numeric_detect &&
pocsag_state.detected == pocsag::DET_NUMERIC &&
pocsag_state.numeric_len > 0) {
std::string num_str(pocsag_state.numeric_buf, pocsag_state.numeric_len);
console.write(STR_COLOR_GREEN + num_str);
console.writeln("");
}
// Alpha decode (already has per-char color escapes from decoder).
console.write(pocsag_state.output);
/* Serial: header + first chunk.
* hex field contains rendered alpha as hex bytes (color escapes
* stripped). Non-printable chars show as '.' (0x2E),
* uncorrectable chars show as '?' (0x3F) original 7-bit
* values are not preserved.
* Numeric decode goes in the quoted message field only. */
if (portapack::usb_serial.serial_connected()) {
/* Build hex representation of decoded alpha characters. */
std::string raw;
for (size_t i = 0; i < pocsag_state.output.size(); ++i)
raw += to_string_hex((uint8_t)pocsag_state.output[i], 2);
std::string s = "\r\nPOCSAG " + to_string_dec_uint(current_bitrate) + " " + to_string_dec_uint(pocsag_state.address) + " " + std::string(1, 'A' + pocsag_state.function) + (current_inverted ? " I" : " S");
if (numeric_detect && pocsag_state.detected == pocsag::DET_NUMERIC) {
s += " numeric \"";
if (pocsag_state.numeric_len > 0)
s += std::string(pocsag_state.numeric_buf, pocsag_state.numeric_len);
s += "\"";
serial_numeric_sent = pocsag_state.numeric_len;
} else {
/* For alpha, the quoted text is the same chars as raw but as ASCII.
* Escape " and \ to avoid breaking the quoted field. */
s += " alpha \"";
for (size_t i = 0; i < pocsag_state.output.size(); ++i) {
if (pocsag_state.output[i] == '"' || pocsag_state.output[i] == '\\') {
s += '\\';
}
s += pocsag_state.output[i];
}
s += "\"";
}
s += " hex:" + raw;
UsbSerialAsyncmsg::asyncmsg(s);
}
} else {
// Message continues...
console.write(color + pocsag_state.output);
// Message continues from previous batch.
bool is_numeric = numeric_detect && pocsag_state.detected == pocsag::DET_NUMERIC;
// GUI: show numeric continuation if applicable.
if (is_numeric && pocsag_state.numeric_len > serial_numeric_sent) {
std::string num_str(pocsag_state.numeric_buf + serial_numeric_sent,
pocsag_state.numeric_len - serial_numeric_sent);
console.write(STR_COLOR_GREEN + num_str);
}
console.write(pocsag_state.output);
/* Serial: continuation chunk with full header.
* Type label carries over from first batch (numeric+ or alpha+). */
if (portapack::usb_serial.serial_connected()) {
std::string raw;
std::string decoded;
for (size_t i = 0; i < pocsag_state.output.size(); ++i) {
if (pocsag_state.output[i] == '"' || pocsag_state.output[i] == '\\')
decoded += '\\';
decoded += pocsag_state.output[i];
raw += to_string_hex((uint8_t)pocsag_state.output[i], 2);
}
if (!decoded.empty() || pocsag_state.numeric_len > serial_numeric_sent) {
std::string s = "\r\nPOCSAG " + to_string_dec_uint(current_bitrate) + " " + to_string_dec_uint(pocsag_state.address) + " " + std::string(1, 'A' + pocsag_state.function) + (current_inverted ? " I" : " S") + (is_numeric ? " numeric+" : " alpha+") + " \"";
if (is_numeric && pocsag_state.numeric_len > serial_numeric_sent)
s += std::string(pocsag_state.numeric_buf + serial_numeric_sent,
pocsag_state.numeric_len - serial_numeric_sent);
else
s += decoded;
s += "\" hex:" + raw;
UsbSerialAsyncmsg::asyncmsg(s);
}
}
serial_numeric_sent = pocsag_state.numeric_len;
}
if (logging()) {
logger.log_decoded(
timestamp,
to_string_dec_uint(pocsag_state.address) +
" F" + to_string_dec_uint(pocsag_state.function) +
" " + pocsag_state.output);
std::string log_entry = to_string_dec_uint(pocsag_state.address) +
" F" + to_string_dec_uint(pocsag_state.function);
if (numeric_detect &&
pocsag_state.detected == pocsag::DET_NUMERIC &&
pocsag_state.numeric_len > 0)
log_entry += " N:" + std::string(pocsag_state.numeric_buf, pocsag_state.numeric_len);
log_entry += " " + pocsag_state.output;
logger.log_decoded(timestamp, log_entry);
}
}
}
@@ -297,13 +404,19 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
image_status.set_foreground(Theme::getInstance()->fg_magenta->foreground);
pocsag_state.codeword_index = 0;
pocsag_state.errors = 0;
current_bitrate = message->packet.bitrate();
current_inverted = message->packet.inverted();
// Handle multiple messages (if any).
while (pocsag_decode_batch(message->packet, pocsag_state))
handle_decoded(message->packet.timestamp(), prefix);
// Handle the remainder.
handle_decoded(message->packet.timestamp(), prefix);
// Handle the remainder. Skip if decoder is still in
// STATE_HAVE_ADDRESS — the address was at the end of this
// batch and we can't yet tell if it's tone-only or has
// message data in the next batch.
if (pocsag_state.mode != STATE_HAVE_ADDRESS)
handle_decoded(message->packet.timestamp(), prefix);
}
// Set status icon color to indicate state machine state.
+35 -13
View File
@@ -125,7 +125,9 @@ struct POCSAGSettings {
bool enable_raw_log = false;
bool hide_bad_data = false;
bool hide_addr_only = false;
bool enable_numeric_detect = true; /* heuristic alpha/numeric type detection */
uint8_t filter_mode = false;
int32_t baud_rate = -1;
uint32_t filter_address = 0;
};
@@ -139,9 +141,18 @@ class POCSAGSettingsView : public View {
private:
POCSAGSettings& settings_;
OptionsField opt_baud_rate{
{8 * 8, 0 * 16},
4,
{{"Auto", -1},
{" 512", 0},
{"1200", 1},
{"2400", 2}}};
Labels labels{
{{2 * 8, 12 * 16}, "Filter Mode:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 13 * 16}, "Filter Addr:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 0 * 16}, "Baud:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 14 * 16}, "Filter Mode:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 15 * 16}, "Filter Addr:", Theme::getInstance()->fg_light->foreground},
};
Checkbox check_log{
@@ -169,21 +180,26 @@ class POCSAGSettingsView : public View {
22,
"Hide Addr Only"};
Checkbox check_numeric_detect{
{2 * 8, 12 * 16},
22,
"Detect Numeric"};
OptionsField opt_filter_mode{
{15 * 8, 12 * 16},
{15 * 8, 14 * 16},
4,
{{"None", FILTER_NONE},
{"Drop", FILTER_DROP},
{"Keep", FILTER_KEEP}}};
SymField field_filter_address{
{15 * 8, 13 * 16},
{15 * 8, 15 * 16},
7,
SymField::Type::Dec,
true /*explicit_edit*/};
Button button_save{
{11 * 8, 16 * 16, 10 * 8, 2 * 16},
{UI_POS_X_CENTER(10), UI_POS_Y(17), 10 * 8, 2 * 16},
"Save"};
};
@@ -221,6 +237,8 @@ class POCSAGAppView : public View {
{"filter_address"sv, &settings_.filter_address},
{"hide_bad_data"sv, &settings_.hide_bad_data},
{"hide_addr_only"sv, &settings_.hide_addr_only},
{"numeric_detect"sv, &settings_.enable_numeric_detect},
{"baud_rate"sv, &settings_.baud_rate},
}};
void refresh_ui();
@@ -230,36 +248,40 @@ class POCSAGAppView : public View {
void on_stats(const POCSAGStatsMessage* stats);
uint32_t last_address = 0;
uint16_t current_bitrate = 0; /* bitrate of current packet being decoded */
bool current_inverted = false; /* polarity of current packet being decoded */
uint8_t serial_numeric_sent = 0; /* numeric chars already sent to serial/GUI */
pocsag::EccContainer ecc{};
pocsag::POCSAGState pocsag_state{&ecc};
POCSAGLogger logger{};
uint16_t packet_count = 0;
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
{UI_POS_X(0), 0 * 8},
nav_};
RFAmpField field_rf_amp{
{11 * 8, 0 * 16}};
{11 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{16 * 8, 0 * 16}};
{16 * 8, UI_POS_Y(0)}};
RSSI rssi{
{19 * 8 - 4, 3, 6 * 8, 4}};
{19 * 8 - 4, 3, UI_POS_WIDTH_REMAINING(26), 4}};
Audio audio{
{19 * 8 - 4, 8, 6 * 8, 4}};
{19 * 8 - 4, 8, UI_POS_WIDTH_REMAINING(26), 4}};
NumberField field_squelch{
{25 * 8, 0 * 16},
{UI_POS_X_RIGHT(6), UI_POS_Y(0)},
2,
{0, 99},
1,
' ',
true /*wrap*/};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
Image image_status{
{0 * 8 + 4, 1 * 16 + 2, 16, 16},
+15 -13
View File
@@ -7,30 +7,34 @@
namespace ui {
// Information: a line starting with a '#' will be yellow coloured
constexpr std::string_view authors_list[] = {
"# * List of contributors * ",
constexpr std::string_view mayhem_information_list[] = {
"#****** Mayhem Community ******",
" ",
" https://discord.hackrf.app",
" ",
"#**** List of contributors ****",
" ",
"#Mayhem-Firmware:",
"jboone,eried,furrtek,",
"NotherNgineer,gullradriel,",
"jLynx,kallanreed,Brumi-2021,",
"htotoo,bernd-herzog,zxkmm,",
"htotoo,zxkmm,bernd-herzog,",
"ArjanOnwezen,euquiq,u-foka,",
"iNetro,heurist1,dqs105,",
"teixeluis,jwetzell,",
"jimilinuxguy,gregoryfenton,",
"notpike,strijar,BehleZebub,",
"arneluehrs,rascafr,joyel24,",
"ImDroided,zigad,johnelder,",
"klockee,nnesetto,LupusE,",
"argilo,dc2dc,formtapez,",
"arneluehrs,mcules,rascafr,",
"joyel24,ImDroided,zigad,",
"johnelder,klockee,nnesetto,",
"LupusE,argilo,dc2dc,formtapez,",
"RocketGod-git,mrmookie,",
"ITAxReal,F33RNI,F4GEV,",
"rusty-labs,mjwaxios,andrej-mk,",
"RedFox-Fr,nemanjan00,",
"MichalLeonBorsuk,",
"MatiasFernandez,Giorgiofox,",
"ckuethe",
"MatiasFernandez,Giorgiofox",
"TommasoVentafridda",
" ",
"#Havoc:",
"jboone,furrtek,eried,argilo,",
@@ -72,7 +76,7 @@ AboutView::AboutView(NavigationView& nav) {
button_ok.focus();
};
for (auto& authors_line : authors_list) {
for (auto& authors_line : mayhem_information_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] == '#') {
@@ -103,15 +107,13 @@ void AboutView::on_frame_sync() {
menu_view.set_highlighted(current + 1);
} else {
menu_view.set_highlighted(0);
// ^ to go back to the REAL top instead of make the 2 at the top to make the title disappeares
menu_view.set_highlighted(2); // the first line that has human name
}
}
}
void AboutView::focus() {
button_ok.focus();
menu_view.set_highlighted(2); // the first line that has human name
menu_view.set_highlighted(3); // contributors block starting line
}
bool AboutView::on_touch(const TouchEvent) {
@@ -6,6 +6,10 @@
#include <cstdint>
/*
TODO: Now it is dyn width. There should be an algorithm to fill the menu based on it's size.
*/
namespace ui {
class AboutView : public View {
public:
@@ -22,11 +26,11 @@ class AboutView : public View {
uint16_t frame_sync_count{0};
void on_frame_sync();
MenuView menu_view{
{0, 0, 240, 264},
{0, 0, screen_width, screen_height - 56},
true};
Button button_ok{
{240 / 3, 270, 240 / 3, 24},
{screen_width / 3, screen_height - 50, screen_width / 3, 24},
"OK",
};
-120
View File
@@ -1,120 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 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_aprs_tx.hpp"
#include "ui_alphanum.hpp"
#include "aprs.hpp"
#include "string_format.hpp"
#include "portapack.hpp"
#include "baseband_api.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
#include <stdio.h>
using namespace aprs;
using namespace portapack;
namespace ui {
void APRSTXView::focus() {
tx_view.focus();
}
APRSTXView::~APRSTXView() {
transmitter_model.disable();
baseband::shutdown();
}
void APRSTXView::start_tx() {
// TODO: Clean up this API to take string_views to avoid allocations.
make_aprs_frame(
sym_source.to_string().c_str(), num_ssid_source.value(),
sym_dest.to_string().c_str(), num_ssid_dest.value(),
payload);
// uint8_t * bb_data_ptr = shared_memory.bb_data.data;
// text_payload.set(to_string_hex_array(bb_data_ptr + 56, 15));
transmitter_model.enable();
baseband::set_afsk_data(
AFSK_TX_SAMPLERATE / 1200,
1200,
2200,
1,
10000, // APRS uses fixed 10k bandwidth
8);
}
void APRSTXView::on_tx_progress(const uint32_t progress, const bool done) {
(void)progress;
if (done) {
transmitter_model.disable();
tx_view.set_transmitting(false);
}
}
APRSTXView::APRSTXView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_afsk);
add_children({&labels,
&sym_source,
&num_ssid_source,
&sym_dest,
&num_ssid_dest,
&text_payload,
&button_set,
&tx_view});
button_set.on_select = [this, &nav](Button&) {
text_prompt(
nav,
payload,
30,
[this](std::string& s) {
text_payload.set(s);
});
};
tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
};
tx_view.on_start = [this]() {
start_tx();
tx_view.set_transmitting(true);
};
tx_view.on_stop = [this]() {
tx_view.set_transmitting(false);
transmitter_model.disable();
};
}
} /* namespace ui */
-114
View File
@@ -1,114 +0,0 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 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.hpp"
#include "ui_textentry.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_transmitter.hpp"
#include "message.hpp"
#include "modems.hpp"
#include "transmitter_model.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "portapack.hpp"
namespace ui {
class APRSTXView : public View {
public:
APRSTXView(NavigationView& nav);
~APRSTXView();
void focus() override;
std::string title() const override { return "APRS TX"; };
private:
TxRadioState radio_state_{
144390000 /* frequency */,
1750000 /* bandwidth */,
AFSK_TX_SAMPLERATE /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_aprs", app_settings::Mode::TX};
std::string payload{""};
void start_tx();
void generate_frame();
void generate_frame_pos();
void on_tx_progress(const uint32_t progress, const bool done);
Labels labels{
{{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{
{7 * 8, 1 * 16},
6,
SymField::Type::Alpha};
NumberField num_ssid_source{
{19 * 8, 1 * 16},
2,
{0, 15},
1,
' '};
SymField sym_dest{
{7 * 8, 2 * 16},
6,
SymField::Type::Alpha};
NumberField num_ssid_dest{
{19 * 8, 2 * 16},
2,
{0, 15},
1,
' '};
Text text_payload{
{0 * 8, 5 * 16, 30 * 8, 16},
"-"};
Button button_set{
{0 * 8, 6 * 16, 80, 32},
"Set"};
TransmitterView tx_view{
16 * 16,
5000,
0 // disable setting bandwith, since APRS used fixed 10k bandwidth
};
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
this->on_tx_progress(message.progress, message.done);
}};
};
} /* namespace ui */
+16 -18
View File
@@ -27,6 +27,8 @@
#include "portapack.hpp"
#include "battery.hpp"
#include <cstring>
#include "ui_settings.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
@@ -50,7 +52,6 @@ void BattinfoView::update_result() {
text_voltage.set("UNKNOWN");
text_current.set("-");
text_charge.set("-");
// text_cycles.set("-");
text_ttef.set("-");
text_method.set("-");
// text_warn.set("");
@@ -58,7 +59,9 @@ void BattinfoView::update_result() {
}
bool uichg = false;
uint8_t valid_mask = 0;
battery::BatteryManagement::getBatteryInfo(valid_mask, percent, voltage, current);
bool battMayChanged = false;
battery::BatteryManagement::getBatteryInfo(valid_mask, percent, voltage, current, battMayChanged);
text_state.set((battMayChanged || !persistent_memory::battery_cap_valid()) ? "Needs config." : "ok");
// 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) + " %");
@@ -74,6 +77,7 @@ void BattinfoView::update_result() {
labels_opt.hidden(false);
text_current.hidden(false);
text_charge.hidden(false);
text_state.hidden(false);
text_current.set(to_string_dec_int(current) + " mA");
if (current >= 25) // when >25mA it is charging in any scenario
text_charge.set("Charging");
@@ -96,7 +100,7 @@ void BattinfoView::update_result() {
labels_opt.hidden(true);
text_current.hidden(true);
text_charge.hidden(true);
// text_cycles.hidden(true);
text_state.hidden(true);
text_ttef.hidden(true);
// text_warn.set("");
}
@@ -138,14 +142,13 @@ void BattinfoView::update_result() {
}
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 (battMayChanged) text_capacity.set_style(Theme::getInstance()->fg_red);
if (uichg) set_dirty();
// to update status bar too, send message in behalf of batt manager
BatteryStateMessage msg{valid_mask, percent, current >= 25, voltage};
BatteryStateMessage msg{valid_mask, percent, current >= 25, voltage, battMayChanged};
EventDispatcher::send_message(msg);
}
@@ -158,26 +161,21 @@ BattinfoView::BattinfoView(NavigationView& nav)
&text_current,
&text_charge,
&text_method,
&button_mode,
&button_settings,
&button_exit,
// &text_cycles,
&text_capacity,
//&text_state, //disabled for now
// &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");
}
button_settings.on_select = [this, &nav](Button&) {
nav.replace<SetBatteryView>();
};
text_capacity.set(to_string_dec_uint(persistent_memory::battery_cap_mah()) + " mAh");
if (!persistent_memory::battery_cap_valid()) text_capacity.set_style(Theme::getInstance()->fg_red);
update_result();
if (thread == nullptr) thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, BattinfoView::static_fn, this);
}
+30 -24
View File
@@ -54,51 +54,57 @@ class BattinfoView : public View {
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},
{{UI_POS_X(2), UI_POS_Y(1)}, "Percent:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(2), UI_POS_Y(2)}, "Voltage:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(2), UI_POS_Y(3)}, "Method:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(2), UI_POS_Y(4)}, "Capacity:", 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},
{{UI_POS_X(2), UI_POS_Y(5)}, "Current:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(2), UI_POS_Y(6)}, "Charge:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(2), UI_POS_Y(7)}, "TTF/E:", Theme::getInstance()->fg_light->foreground},
//{{UI_POS_X(2), UI_POS_Y(8)}, "State:", Theme::getInstance()->fg_light->foreground}, //for now, this is disabled. see max ic implementation
{{UI_POS_X(2), UI_POS_Y(10)}, "Change settings:", Theme::getInstance()->fg_light->foreground},
};
Text text_percent{
{13 * 8, 1 * 16, 10 * 16, 16},
{UI_POS_X(13), UI_POS_Y(1), UI_POS_WIDTH(11), UI_POS_HEIGHT(1)},
"-"};
Text text_voltage{
{13 * 8, 2 * 16, 10 * 16, 16},
{UI_POS_X(13), UI_POS_Y(2), UI_POS_WIDTH(11), UI_POS_HEIGHT(1)},
"-"};
Text text_method{
{13 * 8, 3 * 16, 10 * 16, 16},
{UI_POS_X(13), UI_POS_Y(3), UI_POS_WIDTH(11), UI_POS_HEIGHT(1)},
"-"};
Text text_capacity{
{UI_POS_X(13), UI_POS_Y(4), UI_POS_WIDTH(11), UI_POS_HEIGHT(1)},
"-"};
Text text_current{
{13 * 8, 4 * 16, 10 * 16, 16},
{UI_POS_X(13), UI_POS_Y(5), UI_POS_WIDTH(11), UI_POS_HEIGHT(1)},
"-"};
Text text_charge{
{13 * 8, 5 * 16, 10 * 16, 16},
{UI_POS_X(13), UI_POS_Y(6), UI_POS_WIDTH(11), UI_POS_HEIGHT(1)},
"-"};
Text text_ttef{
{13 * 8, 6 * 16, 10 * 16, 16},
"-"};
/* Text text_cycles{
{13 * 8, 7 * 16, 10 * 16, 16},
{UI_POS_X(13), UI_POS_Y(7), UI_POS_WIDTH(11), UI_POS_HEIGHT(1)},
"-"};
Text text_state{
{UI_POS_X(13), UI_POS_Y(8), UI_POS_WIDTH_REMAINING(13), UI_POS_HEIGHT(1)},
"ok"};
/*
Text text_warn{
{1 * 8, 8 * 16, 30 * 8, 2 * 16},
""}; */
{UI_POS_X(1), UI_POS_Y(9), screen_width, UI_POS_HEIGHT(2)},
""};
*/
Button button_mode{
{2 * 8, 11 * 16 + 5, 5 * 16, 32},
"Volt"};
Button button_settings{
{UI_POS_X(2), UI_POS_Y(11) + 5, UI_POS_WIDTH(10), UI_POS_HEIGHT(2)},
"Settings"};
Button button_exit{
{72, 17 * 16, 96, 32},
{UI_POS_X_CENTER(12), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Back"};
static msg_t static_fn(void* arg);
Thread* thread{nullptr};
@@ -106,4 +112,4 @@ class BattinfoView : public View {
} /* namespace ui */
#endif /*__UI_BATTINFO__*/
#endif /*__UI_BATTINFO__*/
@@ -42,7 +42,7 @@ class BMPFileViewer : public View {
private:
NavigationView& nav_;
std::filesystem::path path_{};
BMPViewer bmp{{0, 0, 240, 320}};
BMPViewer bmp{{0, 0, screen_width, screen_height}};
};
} // namespace ui
+8 -8
View File
@@ -63,26 +63,26 @@ class BTLERxView : public View {
static constexpr uint8_t channel_number = 37;
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{UI_POS_X(13), UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{UI_POS_X(15), UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{UI_POS_X(18), UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(21), 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4}};
{UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(21), 4}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
Button button_modem_setup{
{240 - 12 * 8, 1 * 16, 96, 24},
{screen_width - 12 * 8, UI_POS_Y(1), 96, 24},
"Modem setup"};
Console console{
{0, 4 * 16, 240, 240}};
{0, 4 * 16, screen_width, screen_height - 80}};
MessageHandlerRegistration message_handler_packet{
Message::ID::AFSKData,
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -12,7 +12,7 @@ BatteryCapacityView::RegisterEntry BatteryCapacityView::get_entry(size_t index)
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});
name_texts[i].set_parent_rect({UI_POS_X(0), 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});
@@ -24,18 +24,18 @@ class BatteryCapacityView : public View {
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},
{{UI_POS_X(0), UI_POS_Y(0)}, "Reg", Theme::getInstance()->fg_yellow->foreground},
{{9 * 8, UI_POS_Y(0)}, "Addr", Theme::getInstance()->fg_yellow->foreground},
{{14 * 8, UI_POS_Y(0)}, "Hex", Theme::getInstance()->fg_yellow->foreground},
{{21 * 8, UI_POS_Y(0)}, "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"};
Text page_text{{144, UI_POS_Y_BOTTOM(3), 80, 16}, "Page 1/1"};
Button button_done{{16, UI_POS_Y_BOTTOM(3), 96, 24}, "Done"};
void update_values();
void populate_page(int start_index);
+6 -2
View File
@@ -103,7 +103,8 @@ DfuMenu2::DfuMenu2(NavigationView& nav)
&text_info_line_8,
&text_info_line_9,
&text_info_line_10,
&text_info_line_11});
&text_info_line_11,
&text_info_line_12});
}
void DfuMenu2::paint(Painter& painter) {
@@ -118,9 +119,12 @@ void DfuMenu2::paint(Painter& painter) {
text_info_line_9.set(to_string_dec_uint(portapack::transmitter_model.target_frequency(), 10));
text_info_line_10.set(to_string_dec_uint(portapack::transmitter_model.baseband_bandwidth(), 10));
text_info_line_11.set(to_string_dec_uint(portapack::transmitter_model.sampling_rate(), 10));
text_info_line_12.set(std::string("M") + (radio::get_mixer_invert() ? "Y" : "N") +
" B" + (radio::get_baseband_invert() ? "Y" : "N") +
" IQ" + ((radio::get_mixer_invert() ^ radio::get_baseband_invert()) ? "Y" : "N"));
constexpr auto margin = 5;
constexpr auto lines = 11 + 2;
constexpr auto lines = 12 + 2;
painter.fill_rectangle(
{{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
@@ -96,6 +96,7 @@ class DfuMenu2 : public View {
{{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},
{{5 * CHARACTER_WIDTH, 16 * LINE_HEIGHT}, "IQ Inv:", Theme::getInstance()->fg_darkcyan->foreground},
};
Text text_info_line_1{{14 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
@@ -109,6 +110,7 @@ class DfuMenu2 : public View {
Text text_info_line_9{{14 * CHARACTER_WIDTH, 13 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_10{{14 * CHARACTER_WIDTH, 14 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_11{{14 * CHARACTER_WIDTH, 15 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_12{{(14 + 1) * CHARACTER_WIDTH, 16 * LINE_HEIGHT, (10 - 1) * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
};
} /* namespace ui */
+4 -5
View File
@@ -25,7 +25,6 @@
#include "ui_transmitter.hpp"
#include "transmitter_model.hpp"
#include "encoders.hpp"
#include "de_bruijn.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
@@ -125,7 +124,7 @@ class EncodersConfigView : public View {
""};
Waveform waveform{
{0, 17 * 8, 240, 32},
{0, 17 * 8, screen_width, 32},
waveform_buffer,
0,
0,
@@ -202,7 +201,7 @@ class EncodersView : public View {
void start_tx(const bool scan);
void on_tx_progress(const uint32_t progress, const bool done);
Rect view_rect = {0, 4 * 8, 240, 168};
Rect view_rect = {0, 4 * 8, screen_width, 168};
EncodersConfigView view_config{nav_, view_rect};
EncodersScanView view_scan{nav_, view_rect};
@@ -217,10 +216,10 @@ class EncodersView : public View {
"Ready"};
ProgressBar progressbar{
{2 * 8, 13 * 16 + 20, 208, 16}};
{2 * 8, 13 * 16 + 20, UI_POS_WIDTH_REMAINING(4), 16}};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
50000,
9};
@@ -92,12 +92,21 @@ void ExternalModuleView::on_tick_second() {
case app_location_t::TX:
btnText += " (TX)";
break;
case app_location_t::TRX:
btnText += " (TRX)";
break;
case app_location_t::SETTINGS:
btnText += " (Settings)";
break;
case app_location_t::DEBUG:
btnText += " (Debug)";
break;
case app_location_t::HOME:
btnText += " (Home)";
break;
case app_location_t::GAMES:
btnText += " (Games)";
break;
}
switch (i) {
@@ -119,4 +128,5 @@ void ExternalModuleView::on_tick_second() {
}
}
}
} // namespace ui
@@ -84,7 +84,7 @@ class ExternalModuleView : public View {
Text text_app5_name{{24, 160, 200, 16}};
Button dummy{
{240, 0, 0, 0},
{screen_width, 0, 0, 0},
""};
SignalToken signal_token_tick_second{};
+96 -99
View File
@@ -43,6 +43,7 @@ namespace ui {
static const fs::path txt_ext{u".TXT"};
static const fs::path ppl_ext{u".PPL"};
static const fs::path c8_ext{u".C8"};
static const fs::path cu8_ext{u".CU8"};
static const fs::path c16_ext{u".C16"};
static const fs::path cxx_ext{u".C*"};
static const fs::path png_ext{u".PNG"};
@@ -88,8 +89,11 @@ fs::path get_partner_file(fs::path path) {
path.replace_extension(txt_ext);
else if (path_iequal(ext, txt_ext)) {
path.replace_extension(c8_ext);
if (!fs::file_exists(path))
path.replace_extension(cu8_ext);
if (!fs::file_exists(path))
path.replace_extension(c16_ext);
} else
return {};
@@ -228,6 +232,8 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
text_current.set(dir_path.empty() ? "(sd root)" : truncate(dir_path, 24));
// Collect all entries first
std::list<fileman_entry> all_entries;
for (const auto& entry : fs::directory_iterator(dir_path, u"*")) {
// Hide files starting with '.' (hidden / tmp).
if (!show_hidden_files && is_hidden_file(entry.path()))
@@ -235,36 +241,40 @@ 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().string(), (uint32_t)entry.size(), false});
insert_sorted(all_entries, {entry.path().string(), (uint32_t)entry.size(), false});
} else if (fs::is_directory(entry.status())) {
insert_sorted(entry_list, {entry.path().string(), 0, true});
insert_sorted(all_entries, {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));
// Calculate pagination
nb_pages = (all_entries.size() + items_per_page - 1) / items_per_page;
if (nb_pages == 0) nb_pages = 1;
size_t start_idx = pagination * items_per_page;
size_t end_idx = std::min(start_idx + items_per_page, all_entries.size());
// Add "parent" directory if not at the root and on first page
if (!dir_path.empty() && pagination == 0) {
entry_list.push_back({parent_dir_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 (list_size > start + items_per_page) {
entry_list.push_back({str_next, (uint32_t)pagination + 1, true});
}
// add prev page
// Add prev page navigation if not on first page
if (pagination > 0) {
entry_list.insert(entry_list.begin(), {str_back, (uint32_t)pagination - 1, true});
entry_list.push_back({str_back, (uint32_t)pagination - 1, true});
}
// Add entries for current page
auto it = all_entries.begin();
std::advance(it, start_idx);
for (size_t i = start_idx; i < end_idx && it != all_entries.end(); i++, ++it) {
entry_list.push_back(*it);
}
// Add next page navigation if not on last page
if (end_idx < all_entries.size()) {
entry_list.push_back({str_next, (uint32_t)pagination + 1, true});
}
}
@@ -288,6 +298,7 @@ FileManBaseView::FileManBaseView(
std::string filter)
: nav_{nav},
extension_filter{filter} {
max_filename_length = screen_width / 8 - 10;
add_children({&labels,
&text_current,
&button_exit});
@@ -320,30 +331,49 @@ void FileManBaseView::focus() {
} else {
menu_view.focus();
}
// Set menu to the correct page and select the correct item
menu_view.set_highlighted(prev_highlight % items_per_page);
}
// Push directory - store the global index (page * items_per_page + local_index)
void FileManBaseView::push_dir(const fs::path& path) {
if (path == parent_dir_path) {
pop_dir();
} else {
// Save global index (combines page number and item position)
saved_index_stack.push_back(menu_view.highlighted_index() + (pagination * items_per_page));
current_path /= path;
saved_index_stack.push_back(menu_view.highlighted_index());
menu_view.set_highlighted(0);
reload_current(true);
reload_current(true); // Reset pagination when entering new directory
}
}
void FileManBaseView::pop_dir() {
if (saved_index_stack.empty())
if (current_path.empty()) {
return;
}
// Move to parent directory
current_path = current_path.parent_path();
reload_current(true);
menu_view.set_highlighted(saved_index_stack.back());
saved_index_stack.pop_back();
// Restore the previous global index if available
if (!saved_index_stack.empty()) {
uint32_t global_index = saved_index_stack.back();
saved_index_stack.pop_back();
// Calculate pagination from global index
pagination = global_index / items_per_page;
// Calculate local index within the page
prev_highlight = global_index % items_per_page;
restoring_navigation = true;
}
reload_current(false); // Important: don't reset pagination
}
std::string get_extension(std::string t) {
std::string FileManBaseView::get_extension(const std::string& t) const {
const auto index = t.find_last_of(u'.');
if (index == t.npos) {
return {};
@@ -360,6 +390,7 @@ std::string get_stem(std::string t) {
return t.substr(0, index);
}
}
std::string get_filename(std::string _s) {
const auto index = _s.find_last_of("/");
if (index == _s.npos) {
@@ -368,16 +399,17 @@ std::string get_filename(std::string _s) {
return _s.substr(index + 1);
}
}
void FileManBaseView::refresh_list() {
if (on_refresh_widgets)
on_refresh_widgets(false);
prev_highlight = menu_view.highlighted_index();
if (!restoring_navigation) {
prev_highlight = menu_view.highlighted_index();
}
menu_view.clear();
for (const auto& entry : entry_list) {
auto entry_name = std::string{entry.path.length() <= 20 ? entry.path : entry.path.substr(0, 20)};
if (entry.is_directory) {
std::string size_str{};
if (entry.path == str_next || entry.path == str_back) {
@@ -386,8 +418,10 @@ void FileManBaseView::refresh_list() {
size_str = (entry.path == parent_dir_path.string()) ? "" : to_string_dec_uint(file_count(current_path / entry.path));
}
// CRITICAL: Use "\t" (Tab) to separate the path and the size!
// Do not use spaces or padding here. The menu will handle the alignment.
menu_view.add_item(
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
{entry.path + "\t" + size_str,
Theme::getInstance()->fg_yellow->foreground,
&bitmap_icon_dir,
[this](KeyEvent key) {
@@ -399,8 +433,9 @@ void FileManBaseView::refresh_list() {
const auto& assoc = get_assoc(get_extension(entry.path));
auto size_str = to_string_file_size(entry.size);
// CRITICAL: Use "\t" (Tab) to separate the path and the size!
menu_view.add_item(
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
{entry.path + "\t" + size_str,
assoc.color,
assoc.icon,
[this](KeyEvent key) {
@@ -411,14 +446,29 @@ void FileManBaseView::refresh_list() {
}
menu_view.set_highlighted(prev_highlight);
restoring_navigation = false;
}
void FileManBaseView::reload_current(bool reset_pagination) {
if (reset_pagination) pagination = 0;
// Only reset pagination if explicitly requested
if (reset_pagination) {
pagination = 0;
}
load_directory_contents(current_path);
refresh_list();
}
void FileManBaseView::copy_waterfall(std::filesystem::path path) {
nav_.push<ModalMessageView>(
"Install", " Use this gradient file\n for all waterfalls?", YESNO,
[this, path](bool choice) {
if (choice) {
delete_file(default_gradient_file);
copy_file(path, default_gradient_file);
}
});
}
const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
const fs::path& ext) const {
size_t index = 0;
@@ -444,7 +494,7 @@ FileLoadView::FileLoadView(
add_children({&menu_view});
// Resize menu view to fill screen
menu_view.set_parent_rect({0, 3 * 8, 240, 29 * 8});
menu_view.set_parent_rect({0, 3 * 8, screen_width, UI_POS_HEIGHT_REMAINING(4.5)});
refresh_list();
@@ -478,63 +528,6 @@ void FileLoadView::refresh_widgets(const bool) {
set_dirty();
}
/* FileSaveView **************************************************************/
/*
FileSaveView::FileSaveView(
NavigationView& nav,
const fs::path& path,
const fs::path& file
) : nav_{ nav },
path_{ path },
file_{ file }
{
add_children({
&text_path,
&button_edit_path,
&text_name,
&button_edit_name,
&button_save,
&button_cancel,
});
button_edit_path.on_select = [this](Button&) {
buffer_ = path_.string();
text_prompt(nav_, buffer_, max_filename_length,
[this](std::string&) {
path_ = buffer_;
refresh_widgets();
});
};
button_edit_name.on_select = [this](Button&) {
buffer_ = file_.string();
text_prompt(nav_, buffer_, max_filename_length,
[this](std::string&) {
file_ = buffer_;
refresh_widgets();
});
};
button_save.on_select = [this](Button&) {
if (on_save)
on_save(path_ / file_);
else
nav_.pop();
};
button_cancel.on_select = [this](Button&) {
nav_.pop();
};
refresh_widgets();
}
void FileSaveView::refresh_widgets() {
text_path.set(truncate(path_, 30));
text_name.set(truncate(file_, 30));
set_dirty();
}
*/
/* FileManagerView ***********************************************************/
void FileManagerView::refresh_widgets(const bool v) {
@@ -566,7 +559,7 @@ void FileManagerView::on_rename(std::string_view hint) {
cursor_pos = pos;
text_prompt(
nav_, name_buffer, cursor_pos, max_filename_length,
nav_, name_buffer, cursor_pos, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& renamed) {
auto renamed_path = fs::path{renamed};
rename_file(get_selected_full_path(), current_path / renamed_path);
@@ -640,7 +633,7 @@ void FileManagerView::on_clean() {
void FileManagerView::on_new_dir() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) {
text_prompt(nav_, name_buffer, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& dir_name) {
make_new_directory(current_path / dir_name);
reload_current(true);
});
@@ -671,7 +664,7 @@ void FileManagerView::on_paste() {
void FileManagerView::on_new_file() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) {
text_prompt(nav_, name_buffer, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& file_name) {
make_new_file(current_path / file_name);
reload_current(true);
});
@@ -685,7 +678,11 @@ bool FileManagerView::handle_file_open() {
auto ext = path.extension();
if (path_iequal(txt_ext, ext)) {
nav_.push<TextEditorView>(path);
if (path_iequal(current_path, u"/" + waterfalls_dir)) {
copy_waterfall(path);
} else {
nav_.push<TextEditorView>(path);
}
return true;
} else if (is_cxx_capture_file(path) || path_iequal(ppl_ext, ext)) {
// TODO: Enough memory to push?
+23 -19
View File
@@ -64,7 +64,8 @@ class FileManBaseView : public View {
uint32_t prev_highlight = 0;
uint8_t pagination = 0;
uint8_t nb_pages = 1;
static constexpr size_t max_filename_length = 20;
bool restoring_navigation = false;
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;
@@ -79,6 +80,7 @@ class FileManBaseView : public View {
{u".PNG", &bitmap_icon_file_image, ui::Color::green()},
{u".BMP", &bitmap_icon_file_image, ui::Color::green()},
{u".C8", &bitmap_icon_file_iq, ui::Color::dark_cyan()},
{u".CU8", &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
@@ -96,6 +98,8 @@ class FileManBaseView : public View {
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;
std::string get_extension(const std::string& t) const;
void copy_waterfall(std::filesystem::path path);
NavigationView& nav_;
@@ -119,16 +123,16 @@ class FileManBaseView : public View {
{{0, 0}, "Path:", Theme::getInstance()->fg_light->foreground}};
Text text_current{
{6 * 8, 0 * 8, 24 * 8, 16},
{UI_POS_X(6), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(6), UI_POS_HEIGHT(1)},
"",
};
MenuView menu_view{
{0, 2 * 8, 240, 26 * 8},
{0, UI_POS_Y(1), UI_POS_MAXWIDTH, UI_POS_HEIGHT_REMAINING(7)},
true};
Button button_exit{
{22 * 8, 34 * 8, 8 * 8, 32},
{UI_POS_X_RIGHT(8), UI_POS_Y_BOTTOM(3), 8 * 8, UI_POS_HEIGHT(2)},
"Exit"};
};
@@ -171,7 +175,7 @@ private:
};
Text text_path {
{ 0 * 8, 2 * 16, 30 * 8, 16 },
{ 0 * 8, 2 * 16, screen_width, 16 },
"",
};
@@ -181,7 +185,7 @@ private:
};
Text text_name {
{ 0 * 8, 7 * 16, 30 * 8, 16 },
{ 0 * 8, 7 * 16, screen_width, 16 },
"",
};
@@ -227,66 +231,66 @@ class FileManagerView : public FileManBaseView {
bool selected_is_valid() const;
Text text_date{
{0 * 8, 26 * 8, 28 * 8, 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(6), 28 * 8, UI_POS_HEIGHT(1)},
""};
NewButton button_rename{
{0 * 8, 29 * 8, 4 * 8, 32},
{UI_POS_X(0), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_rename,
Theme::getInstance()->fg_blue->foreground};
NewButton button_delete{
{9 * 8, 34 * 8, 4 * 8, 32},
{UI_POS_X(9), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_trash,
Theme::getInstance()->fg_red->foreground};
NewButton button_clean{
{13 * 8, 34 * 8, 4 * 8, 32},
{UI_POS_X(13), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_clean,
Theme::getInstance()->fg_red->foreground};
NewButton button_cut{
{9 * 8, 29 * 8, 4 * 8, 32},
{UI_POS_X(9), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_cut,
Theme::getInstance()->fg_dark->foreground};
NewButton button_copy{
{13 * 8, 29 * 8, 4 * 8, 32},
{UI_POS_X(13), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_copy,
Theme::getInstance()->fg_dark->foreground};
NewButton button_paste{
{17 * 8, 29 * 8, 4 * 8, 32},
{UI_POS_X(17), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_paste,
Theme::getInstance()->fg_dark->foreground};
NewButton button_new_dir{
{22 * 8, 29 * 8, 4 * 8, 32},
{UI_POS_X(22), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_new_dir,
Theme::getInstance()->fg_green->foreground};
NewButton button_new_file{
{26 * 8, 29 * 8, 4 * 8, 32},
{UI_POS_X(26), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_new_file,
Theme::getInstance()->fg_green->foreground};
NewButton button_open_notepad{
{0 * 8, 34 * 8, 4 * 8, 32},
{UI_POS_X(0), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_notepad,
Theme::getInstance()->fg_orange->foreground};
NewButton button_rename_timestamp{
{4 * 8, 29 * 8, 4 * 8, 32},
{UI_POS_X(4), UI_POS_Y_BOTTOM(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_options_datetime,
Theme::getInstance()->fg_blue->foreground,
@@ -294,13 +298,13 @@ class FileManagerView : public FileManBaseView {
NewButton button_open_iq_trim{
{4 * 8, 34 * 8, 4 * 8, 32},
{UI_POS_X(4), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_trim,
Theme::getInstance()->fg_orange->foreground};
NewButton button_show_hidden_files{
{17 * 8, 34 * 8, 4 * 8, 32},
{UI_POS_X(17), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_hide,
Theme::getInstance()->fg_dark->foreground};
+12 -6
View File
@@ -32,7 +32,7 @@ static const char* hackrf_magic = "HACKRFFW";
#define FIRST_CHECKSUM_NIGHTLY 240125
Thread* FlashUtilityView::thread{nullptr};
static constexpr size_t max_filename_length = 26;
static constexpr size_t max_filename_length = 60; // max length of filename
bool valid_firmware_file(std::filesystem::path::string_type path) {
File firmware_file;
@@ -45,10 +45,14 @@ bool valid_firmware_file(std::filesystem::path::string_type path) {
// 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()) {
uint64_t file_size = firmware_file.size();
if (file_size > FLASH_ROM_SIZE) {
// Firmware file is larger than the flash size for this device
return false;
}
checksum = 0;
for (uint32_t offset = 0; offset < FLASH_ROM_SIZE; offset += sizeof(read_buffer)) {
for (uint64_t offset = 0; offset < FLASH_ROM_SIZE && offset < file_size; offset += sizeof(read_buffer)) {
auto readResult = firmware_file.read(&read_buffer, sizeof(read_buffer));
if ((!readResult) || (readResult.value() != sizeof(read_buffer))) {
// 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).
@@ -80,7 +84,7 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
add_children({&labels,
&menu_view});
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
menu_view.set_parent_rect({0, 3 * 8, screen_width, 33 * 8});
ensure_directory(apps_dir);
ensure_directory(firmware_dir);
@@ -147,6 +151,7 @@ std::filesystem::path FlashUtilityView::extract_tar(std::filesystem::path::strin
bool FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
ui::Painter painter;
menu_view.hidden(true);
if (endsWith(path, u".tar")) {
// extract, then update
path = extract_tar(u'/' + path, painter).native();
@@ -156,13 +161,14 @@ bool FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
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);
menu_view.hidden(false);
return false;
}
painter.fill_rectangle(
{0, 0, portapack::display.width(), portapack::display.height()},
Theme::getInstance()->bg_darkest->background);
painter.draw_string({12, 24}, this->nav_.style(), "This will take 15 seconds.");
painter.draw_string({12, 24}, this->nav_.style(), "This will take 15-45 sec.");
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.");
@@ -177,4 +183,4 @@ void FlashUtilityView::focus() {
menu_view.focus();
}
} /* namespace ui */
} /* namespace ui */
@@ -32,7 +32,7 @@
#include "untar.hpp"
#include <cstdint>
#define FLASH_ROM_SIZE 1048576
#define FLASH_ROM_SIZE (FLASH_SIZE_MB * 1024 * 1024)
#define FLASH_STARTING_ADDRESS 0x00000000
#define FLASH_EXPECTED_CHECKSUM 0x00000000
#define FLASH_CHECKSUM_ERROR 0xFFFFFFFF
@@ -60,7 +60,7 @@ class FlashUtilityView : public View {
{{4, 4}, "Select firmware to flash:", Theme::getInstance()->bg_darkest->foreground}};
MenuView menu_view{
{0, 2 * 8, 240, 26 * 8},
{0, UI_POS_Y(1), screen_width, UI_POS_HEIGHT_REMAINING(2)},
true};
std::filesystem::path extract_tar(std::filesystem::path::string_type path, ui::Painter& painter); // extracts the tar file, and returns the firmware.bin path from it. empty string if no fw
+4 -4
View File
@@ -45,8 +45,8 @@ using option_db_t = std::pair<std::string_view, int32_t>;
using options_db_t = std::vector<option_db_t>;
extern options_db_t freqman_modulations;
extern options_db_t freqman_bandwidths[4];
extern options_db_t freqman_steps;
extern options_db_t freqman_bandwidths[6];
// extern options_db_t freqman_steps; // now included via ui_receiver.hpp
extern options_db_t freqman_steps_short;
options_t dboptions_to_options(const options_db_t& dboptions) {
@@ -240,7 +240,7 @@ void FrequencyManagerView::on_edit_freq() {
void FrequencyManagerView::on_edit_desc() {
temp_buffer_ = current_entry().description;
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, [this](std::string& new_desc) {
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_desc) {
auto entry = current_entry();
entry.description = std::move(new_desc);
db_.replace_entry(current_index(), entry);
@@ -250,7 +250,7 @@ void FrequencyManagerView::on_edit_desc() {
void FrequencyManagerView::on_add_category() {
temp_buffer_.clear();
text_prompt(nav_, temp_buffer_, 20, [this](std::string& new_name) {
text_prompt(nav_, temp_buffer_, 20, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_name) {
if (!new_name.empty()) {
create_freqman_file(new_name);
refresh_categories();
+23 -23
View File
@@ -65,14 +65,14 @@ class FreqManBaseView : public View {
OptionsField options_category{
{3 * 8, 2},
20 /* length */,
19 /* length */,
{}};
FreqManUIList freqlist_view{
{0, 3 * 8, screen_width, 12 * 16 + 2 /* 2 Keeps text out of border. */}};
Button button_exit{
{15 * 8, 17 * 16, 15 * 8, 2 * 16},
{UI_POS_X_RIGHT(14), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(14), UI_POS_HEIGHT(2)},
"Exit"};
protected:
@@ -97,14 +97,14 @@ class FrequencySaveView : public FreqManBaseView {
0};
Labels labels{
{{0 * 8, 6 * 16}, "Description:", Theme::getInstance()->bg_darkest->foreground}};
{{UI_POS_X(0), 6 * 16}, "Description:", Theme::getInstance()->bg_darkest->foreground}};
TextField field_description{
{0 * 8, 7 * 16, 30 * 8, 1 * 16},
{UI_POS_X(0), 7 * 16, screen_width, 1 * 16},
""};
Button button_save{
{0 * 8, 17 * 16, 15 * 8, 2 * 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(14), UI_POS_HEIGHT(2)},
"Save"};
};
@@ -134,44 +134,44 @@ class FrequencyManagerView : public FreqManBaseView {
void on_del_entry();
NewButton button_add_category{
{23 * 8, 0 * 16, 7 * 4, 20},
{UI_POS_X_RIGHT(8), UI_POS_Y(0), 7 * 4, 20},
{},
&bitmap_icon_new_file,
Theme::getInstance()->bg_darkest->foreground,
true};
NewButton button_del_category{
{26 * 8 + 4, 0 * 16, 7 * 4, 20},
{UI_POS_X_RIGHT(4), UI_POS_Y(0), 7 * 4, 20},
{},
&bitmap_icon_trash,
Theme::getInstance()->fg_red->foreground,
true};
Button button_edit_entry{
{0 * 8, 14 * 16 - 4, 15 * 8, 1 * 16 + 4},
{UI_POS_X(0), UI_POS_Y_BOTTOM(6.5) + 4, 15 * 8, 1 * 16 + 4},
"Edit"};
Rectangle rect_padding{
{15 * 8, 14 * 16 - 4, 15 * 8, 1 * 16 + 4},
{15 * 8, UI_POS_Y_BOTTOM(6.5), 15 * 8, 1 * 16 + 4},
Theme::getInstance()->fg_medium->background};
Button button_edit_freq{
{0 * 8, 15 * 16, 15 * 8, 2 * 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(5), 15 * 8, 2 * 16},
"Frequency"};
Button button_edit_desc{
{0 * 8, 17 * 16, 15 * 8, 2 * 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(3), 15 * 8, 2 * 16},
"Description"};
NewButton button_add_entry{
{15 * 8, 15 * 16, 7 * 8 + 4, 2 * 16},
{15 * 8, UI_POS_Y_BOTTOM(5), 7 * 8 + 4, 2 * 16},
{},
&bitmap_icon_add,
Theme::getInstance()->bg_darkest->foreground,
true};
NewButton button_del_entry{
{22 * 8 + 4, 15 * 16, 7 * 8 + 4, 2 * 16},
{22 * 8 + 4, UI_POS_Y_BOTTOM(5), 7 * 8 + 4, 2 * 16},
{},
&bitmap_icon_delete,
Theme::getInstance()->fg_red->foreground,
@@ -201,14 +201,14 @@ class FrequencyEditView : public View {
Labels labels{
{{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},
{{UI_POS_X(0), 3 * 16}, "Entry Type :", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 4 * 16}, "Frequency A:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 5 * 16}, "Frequency B:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 6 * 16}, "Modulation :", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 7 * 16}, "Bandwidth :", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 8 * 16}, "Step :", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 9 * 16}, "Tone Freq :", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 10 * 16}, "Description:", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_type{
@@ -243,11 +243,11 @@ class FrequencyEditView : public View {
{}};
Button button_save{
{0 * 8, 17 * 16, 15 * 8, 2 * 16},
{UI_POS_X(0), UI_POS_Y_BOTTOM(3), 15 * 8, 2 * 16},
"Save"};
Button button_cancel{
{15 * 8, 17 * 16, 15 * 8, 2 * 16},
{UI_POS_X_RIGHT(15), UI_POS_Y_BOTTOM(3), 15 * 8, 2 * 16},
"Cancel"};
};
+3 -1
View File
@@ -34,6 +34,7 @@ namespace ui {
IQTrimView::IQTrimView(NavigationView& nav)
: nav_{nav} {
power_buckets_.resize(screen_width);
add_children({
&labels,
&field_path,
@@ -170,7 +171,8 @@ void IQTrimView::update_range_controls(iq::TrimRange trim_range) {
}
void IQTrimView::profile_capture() {
power_buckets_ = {};
power_buckets_.clear();
power_buckets_.resize(screen_width);
iq::PowerBuckets buckets{
.p = power_buckets_.data(),
.size = power_buckets_.size()};
+12 -12
View File
@@ -54,7 +54,7 @@ class TrimProgressUI {
}
void clear() {
p.fill_rectangle({0 * 8, 4 * 16, screen_width, 3 * 16}, Theme::getInstance()->bg_darkest->background);
p.fill_rectangle({UI_POS_X(0), 4 * 16, screen_width, 3 * 16}, Theme::getInstance()->bg_darkest->background);
}
auto get_callback() {
@@ -96,26 +96,26 @@ class IQTrimView : public View {
std::filesystem::path path_{};
Optional<iq::CaptureInfo> info_{};
std::array<iq::PowerBuckets::Bucket, screen_width> power_buckets_{};
std::vector<iq::PowerBuckets::Bucket> power_buckets_{};
TrimProgressUI progress_ui{};
Labels labels{
{{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},
{{UI_POS_X(0), UI_POS_Y(0)}, "Capture File:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 6 * 16}, "Start :", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 7 * 16}, "End :", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 8 * 16}, "Samples:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 9 * 16}, "Max Pwr:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 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},
{{UI_POS_X(0), 12 * 16}, "Amplify:", Theme::getInstance()->fg_light->foreground},
{{10 * 8, 12 * 16}, "x", Theme::getInstance()->fg_light->foreground},
};
TextField field_path{
{0 * 8, 1 * 16, 30 * 8, 1 * 16},
{UI_POS_X(0), 1 * 16, screen_width, 1 * 16},
"Open File..."};
Point pos_lines{0 * 8, 4 * 16};
Point pos_lines{UI_POS_X(0), 4 * 16};
Dim height_lines{2 * 16};
NumberField field_start{
@@ -155,7 +155,7 @@ class IQTrimView : public View {
' '};
Button button_trim{
{20 * 8, 16 * 16, 8 * 8, 2 * 16},
{UI_POS_X_CENTER(8), UI_POS_Y_BOTTOM(3), 8 * 8, 2 * 16},
"Trim"};
};
@@ -24,6 +24,7 @@
#include "ui_looking_glass_app.hpp"
#include "convert.hpp"
#include "file_reader.hpp"
#include "file.hpp"
#include "string_format.hpp"
#include "audio.hpp"
#include "file_path.hpp"
@@ -31,6 +32,7 @@
using namespace portapack;
namespace ui {
void GlassView::focus() {
range_presets.focus();
}
@@ -44,7 +46,7 @@ GlassView::~GlassView() {
// 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);
return toLow + ((value - fromLow) * (toHigh - toLow) + (fromHigh - fromLow) / 2) / (fromHigh - fromLow);
}
void GlassView::update_display_beep() {
@@ -69,7 +71,7 @@ void GlassView::manage_beep_audio() {
}
}
void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power) {
void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint16_t bin, uint8_t& max_power) {
if (mode == LOOKING_GLASS_SINGLEPASS) {
// <20MHz spectrum mode
if (bin < 120) {
@@ -91,17 +93,24 @@ void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint
}
}
rf::Frequency GlassView::get_freq_from_bin_pos(uint8_t pos) {
rf::Frequency GlassView::get_freq_from_bin_pos(uint16_t pos) {
rf::Frequency freq_at_pos = 0;
if (mode == LOOKING_GLASS_SINGLEPASS) {
// starting from the middle, minus 8 ignored bin on each side. Since pos is [-120,120] after the (pos - 120), it's divided by SCREEN_W(240)/2 => 120
freq_at_pos = f_center_ini + ((pos - 120) * ((looking_glass_range - ((16 * looking_glass_range) / SPEC_NB_BINS)) / 2)) / (SCREEN_W / 2);
// starting from the middle, minus 8 ignored bin on each side. Since pos is [-120,120] after the (pos - 120), it's divided by screen_width(240)/2 => 120
freq_at_pos = f_center_ini + ((pos - 120) * ((looking_glass_range - ((16 * looking_glass_range) / SPEC_NB_BINS)) / 2)) / (screen_width / 2);
} else
freq_at_pos = f_min + (2 * offset * each_bin_size) + (pos * looking_glass_range) / SCREEN_W;
freq_at_pos = f_min + (2 * offset * each_bin_size) + (pos * looking_glass_range) / screen_width;
return freq_at_pos;
}
void GlassView::on_marker_change() {
void GlassView::on_marker_change(int8_t delta) {
if ((marker_pixel_index + delta) < 0)
marker_pixel_index = marker_pixel_index + delta + screen_width;
else if ((marker_pixel_index + delta) > screen_width)
marker_pixel_index = marker_pixel_index + delta - screen_width;
else
marker_pixel_index = marker_pixel_index + delta;
marker = get_freq_from_bin_pos(marker_pixel_index);
field_marker.set_text(to_string_short_freq(marker));
plot_marker(marker_pixel_index); // Refresh marker on screen
@@ -121,49 +130,52 @@ void GlassView::reset_live_view() {
max_freq_power = -1000;
// Clear screen in peak mode.
const int spectrum_height = screen_height - spectrum_y;
if (live_frequency_view == 2)
display.fill_rectangle({{0, 108 + 16}, {SCREEN_W, SCREEN_H - (108 + 16)}}, {0, 0, 0});
display.fill_rectangle({{0, spectrum_y}, {screen_width, spectrum_height}}, {0, 0, 0});
}
void GlassView::add_spectrum_pixel(uint8_t power) {
spectrum_row[pixel_index] = spectrum_rgb3_lut[power]; // row of colors
spectrum_row[pixel_index] = gradient.lut[power]; // row of colors
spectrum_data[pixel_index] = (live_frequency_integrate * spectrum_data[pixel_index] + power) / (live_frequency_integrate + 1); // smoothing
pixel_index++;
if (pixel_index == SCREEN_W) // got an entire waterfall line
if (pixel_index == screen_width) // got an entire waterfall line
{
if (live_frequency_view > 0) {
constexpr int rssi_sample_range = SPEC_NB_BINS;
constexpr float rssi_voltage_min = 0.4;
constexpr float rssi_voltage_max = 2.2;
constexpr float adc_voltage_max = 3.3;
constexpr int raw_min = rssi_sample_range * rssi_voltage_min / adc_voltage_max;
constexpr int raw_max = rssi_sample_range * rssi_voltage_max / adc_voltage_max;
constexpr int raw_delta = raw_max - raw_min;
const range_t<int> y_max_range{0, 320 - (108 + 16)};
constexpr int raw_min = 0.5f + rssi_sample_range * rssi_voltage_min / adc_voltage_max;
constexpr int raw_max = 0.5f + rssi_sample_range * rssi_voltage_max / adc_voltage_max;
const int spectrum_height = screen_height - spectrum_y;
// drawing and keeping track of max freq
for (uint16_t xpos = 0; xpos < SCREEN_W; xpos++) {
for (uint16_t xpos = 0; xpos < screen_width; xpos++) {
// save max powerwull freq
if (spectrum_data[xpos] > max_freq_power) {
max_freq_power = spectrum_data[xpos];
max_freq_hold = get_freq_from_bin_pos(xpos);
}
int16_t point = y_max_range.clip(((spectrum_data[xpos] - raw_min) * (320 - (108 + 16))) / raw_delta);
uint8_t color_gradient = (point * 255) / 212;
int16_t point = map(spectrum_data[xpos], 0, 255, 0, spectrum_height);
// clear if not in peak view
if (live_frequency_view != 2) {
display.fill_rectangle({{xpos, 108 + 16}, {1, SCREEN_H - point}}, {0, 0, 0});
display.fill_rectangle({{xpos, spectrum_y}, {1, screen_height - point}}, {0, 0, 0});
}
display.fill_rectangle({{xpos, SCREEN_H - point}, {1, point}}, {color_gradient, 0, uint8_t(255 - color_gradient)});
display.fill_rectangle({{xpos, screen_height - point}, {1, point}}, gradient.lut[spectrum_data[xpos]]);
}
// indicate RSSI min and max power
if (show_rssi_guides) {
display.fill_rectangle({{0, screen_height - map(raw_min, 0, 255, 0, spectrum_height)}, {screen_width, 1}}, -gradient.lut[raw_min]);
display.fill_rectangle({{0, screen_height - map(raw_max, 0, 255, 0, spectrum_height)}, {screen_width, 1}}, -gradient.lut[raw_max]);
}
if (last_max_freq != max_freq_hold) {
last_max_freq = max_freq_hold;
freq_stats.set("MAX HOLD: " + to_string_short_freq(max_freq_hold));
freq_stats.set("MAX: " + to_string_short_freq(max_freq_hold));
}
plot_marker(marker_pixel_index);
} else {
display.draw_pixels({{0, display.scroll(1)}, {SCREEN_W, 1}}, spectrum_row); // new line at top, one less var, speedier
display.draw_pixels({{0, display.scroll(1)}, {screen_width, 1}}, spectrum_row); // new line at top, one less var, speedier
}
pixel_index = 0; // Start New cascade line
}
@@ -199,8 +211,8 @@ bool GlassView::process_bins(uint8_t* powerlevel) {
void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
baseband::spectrum_streaming_stop();
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
// we actually need SCREEN_W (240) of those bins
for (uint8_t bin = 0; bin < bin_length; bin++) {
// we actually need screen_width (240) of those bins
for (uint16_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;
@@ -208,7 +220,7 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
if (bin == 119) {
uint8_t next_max_power = 0;
get_max_power(spectrum, bin + 1, next_max_power);
for (uint8_t it = 0; it < ignore_dc; it++) {
for (uint16_t it = 0; it < ignore_dc; it++) {
uint8_t med_max_power = (max_power + next_max_power) / 2; // due to the way process_bins works we have to keep resetting the color
if (process_bins(&med_max_power) == true)
return; // new line signaled, return
@@ -238,7 +250,7 @@ void GlassView::on_hide() {
}
void GlassView::on_show() {
display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates
display.scroll_set_area(109, screen_height - 1); // Restart scroll on the correct coordinates
baseband::spectrum_streaming_start();
}
@@ -253,11 +265,11 @@ void GlassView::on_range_changed() {
// if the view is done in one pass, show it like in analog_audio_app
mode = LOOKING_GLASS_SINGLEPASS;
offset = 2;
bin_length = SCREEN_W;
bin_length = screen_width;
ignore_dc = 0;
looking_glass_bandwidth = looking_glass_range;
looking_glass_sampling_rate = looking_glass_range;
each_bin_size = looking_glass_bandwidth / SCREEN_W;
each_bin_size = looking_glass_bandwidth / screen_width;
looking_glass_step = looking_glass_bandwidth;
f_center_ini = f_min + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
} else {
@@ -269,7 +281,7 @@ void GlassView::on_range_changed() {
if (mode == LOOKING_GLASS_FASTSCAN) {
offset = 2;
ignore_dc = 4;
bin_length = SCREEN_W;
bin_length = screen_width;
} else { // if( mode == LOOKING_GLASS_SLOWSCAN )
offset = 2;
bin_length = 80;
@@ -279,13 +291,13 @@ void GlassView::on_range_changed() {
f_center_ini = f_min - (offset * each_bin_size) + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
}
search_span = looking_glass_range / MHZ_DIV;
marker_pixel_step = looking_glass_range / SCREEN_W; // Each pixel value in Hz
marker_pixel_step = looking_glass_range / screen_width; // Each pixel value in Hz
pixel_index = 0;
max_power = 0;
bins_hz_size = 0;
on_marker_change();
on_marker_change(0);
update_range_field();
// set the sample rate and bandwidth
@@ -298,16 +310,16 @@ void GlassView::on_range_changed() {
receiver_model.set_target_frequency(f_center); // tune rx for this slice
}
void GlassView::plot_marker(uint8_t pos) {
uint8_t shift_y = 0;
void GlassView::plot_marker(uint16_t pos) {
uint16_t shift_y = 0;
if (live_frequency_view > 0) // plot one line down when in live view
{
shift_y = 16;
}
portapack::display.fill_rectangle({0, 100 + shift_y, 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
portapack::display.fill_rectangle({0, 100 + shift_y, screen_width, 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) {
@@ -316,8 +328,8 @@ void GlassView::update_min(int32_t v) {
min_size = search_span;
if (min_size < 1)
min_size = 1;
if (v > 7200 - min_size) {
v = 7200 - min_size;
if (v > LOOKING_GLASS_MAX_FREQ_MHZ - min_size) {
v = LOOKING_GLASS_MAX_FREQ_MHZ - min_size;
}
if (v > (field_frequency_max.value() - min_size))
field_frequency_max.set_value(v + min_size, false);
@@ -336,6 +348,9 @@ void GlassView::update_max(int32_t v) {
if (v < min_size) {
v = min_size;
}
if (v > LOOKING_GLASS_MAX_FREQ_MHZ) {
v = LOOKING_GLASS_MAX_FREQ_MHZ;
}
if (v < (field_frequency_min.value() + min_size))
field_frequency_min.set_value(v - min_size, false);
if (locked_range)
@@ -358,6 +373,11 @@ GlassView::GlassView(
NavigationView& nav)
: nav_(nav) {
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
spectrum_row.resize(screen_width);
spectrum_data.resize(screen_width);
if (!gradient.load_file(default_gradient_file)) {
gradient.set_default();
}
add_children({&labels,
&field_frequency_min,
@@ -376,10 +396,12 @@ GlassView::GlassView(
&button_beep_squelch,
&field_marker,
&field_trigger,
&button_marker_minus,
&button_marker_plus,
&button_jump,
&button_rst,
&field_rx_iq_phase_cal,
&freq_stats});
&button_rssi,
&freq_stats, &button_play});
load_presets(); // Load available presets from TXT files (or default).
preset_index = clip<uint8_t>(preset_index, 0, presets_db.size());
@@ -433,26 +455,29 @@ GlassView::GlassView(
freq_stats.hidden(true);
button_jump.hidden(true);
button_rst.hidden(true);
display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates.
button_rssi.hidden(true);
display.scroll_set_area(109, screen_height - 1); // Restart scroll on the correct coordinates.
break;
case 1: // LEVEL
display.fill_rectangle({{0, 108}, {SCREEN_W, 24}}, {0, 0, 0});
display.fill_rectangle({{0, 108}, {screen_width, 24}}, {0, 0, 0});
display.scroll_disable();
level_integration.hidden(false);
freq_stats.hidden(false);
button_jump.hidden(false);
button_rst.hidden(false);
button_rssi.hidden(false);
break;
case 2: // PEAK
default:
display.fill_rectangle({{0, 108}, {SCREEN_W, 24}}, {0, 0, 0});
display.fill_rectangle({{0, 108}, {screen_width, 24}}, {0, 0, 0});
display.scroll_disable();
level_integration.hidden(false);
freq_stats.hidden(false);
button_jump.hidden(false);
button_rst.hidden(false);
button_rssi.hidden(false);
break;
}
@@ -486,13 +511,7 @@ GlassView::GlassView(
range_presets.set_selected_index(preset_index);
field_marker.on_encoder_change = [this](TextField&, EncoderEvent delta) {
if ((marker_pixel_index + delta) < 0)
marker_pixel_index = marker_pixel_index + delta + SCREEN_W;
else if ((marker_pixel_index + delta) > SCREEN_W)
marker_pixel_index = marker_pixel_index + delta - SCREEN_W;
else
marker_pixel_index = marker_pixel_index + delta;
on_marker_change();
on_marker_change(delta);
};
field_marker.on_select = [this](TextField&) {
@@ -511,6 +530,14 @@ GlassView::GlassView(
update_range_field();
};
button_marker_minus.on_select = [this](Button&) {
on_marker_change(-1);
};
button_marker_plus.on_select = [this](Button&) {
on_marker_change(1);
};
button_jump.on_select = [this](Button&) {
// Launch Audio with peak frequency.
launch_audio(max_freq_hold);
@@ -520,14 +547,32 @@ 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
button_rssi.on_select = [this](Button& button) {
show_rssi_guides = !show_rssi_guides;
if (show_rssi_guides) {
button.set_style(Theme::getInstance()->fg_green);
} else {
button.set_style(Theme::getInstance()->bg_darkest);
}
reset_live_view();
};
set_spec_iq_phase_calibration_value(get_spec_iq_phase_calibration_value()); // initialize iq_phase_calibration in radio
display.scroll_set_area(109, 319);
button_play.on_select = [this](ImageButton&) {
paused = !paused;
if (paused) {
button_play.set_foreground(Theme::getInstance()->fg_red->foreground);
button_play.set_background(Theme::getInstance()->fg_red->background);
baseband::spectrum_streaming_stop();
button_play.set_bitmap(&bitmap_play);
} else {
button_play.set_foreground(Theme::getInstance()->fg_green->foreground);
button_play.set_background(Theme::getInstance()->fg_green->background);
baseband::spectrum_streaming_start();
button_play.set_bitmap(&bitmap_stop);
}
};
display.scroll_set_area(109, screen_height - 1); // Restart scroll on the correct coordinates
// trigger:
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute,
@@ -535,7 +580,7 @@ GlassView::GlassView(
// at which time it pushes the buffer up with channel_spectrum.feed
baseband::set_spectrum(looking_glass_bandwidth, trigger);
marker_pixel_index = SCREEN_W / 2;
marker_pixel_index = screen_width / 2;
on_range_changed(); // Force a UI update.
receiver_model.set_sampling_rate(looking_glass_sampling_rate); // 20mhz
@@ -576,44 +621,40 @@ void GlassView::on_freqchg(int64_t freq) {
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(looking_glass_dir / u"PRESETS.TXT");
presets_db.clear();
// Add the "Manual" entry.
presets_db.push_back({0, 0, "Manual"});
if (!error) {
auto reader = FileLineReader(presets_file);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue;
scan_root_files(looking_glass_dir, u"*.TXT", [this](const std::filesystem::path& path) {
if (path.empty() || path.native()[0] == u'.')
return;
auto cols = split_string(line, ',');
if (cols.size() != 3)
continue;
File presets_file;
auto error = presets_file.open(looking_glass_dir / path);
if (!error) {
auto reader = FileLineReader(presets_file);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue;
preset_entry entry{};
parse_int(cols[0], entry.min);
parse_int(cols[1], entry.max);
entry.label = trimr(cols[2]);
auto cols = split_string(line, ',');
if (cols.size() != 3)
continue;
if (entry.min == 0 || entry.max == 0 || entry.min >= entry.max)
continue; // Invalid line.
preset_entry entry{};
parse_int(cols[0], entry.min);
parse_int(cols[1], entry.max);
entry.label = trimr(cols[2]);
presets_db.emplace_back(std::move(entry));
if (entry.max == 0 || entry.min >= entry.max)
continue; // Invalid line.
presets_db.emplace_back(std::move(entry));
}
}
}
});
populate_presets();
}
@@ -35,13 +35,14 @@
#include "ui_receiver.hpp"
#include "string_format.hpp"
#include "analog_audio_app.hpp"
#include "spectrum_color_lut.hpp"
#include "gradient.hpp"
namespace ui {
#define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000
#define LOOKING_GLASS_MAX_SAMPLERATE 20000000
#define MHZ_DIV 1000000
#define LOOKING_GLASS_MAX_FREQ_MHZ 7250 // HackRF upper tuning limit (band_high top), in MHz
// blanked DC (16 centered bins ignored ) and top left and right (2 bins ignored on each side )
#define LOOKING_GLASS_FASTSCAN 0
@@ -51,9 +52,6 @@ namespace ui {
#define LOOKING_GLASS_SINGLEPASS 2
// one spectrum line number of bins
#define SPEC_NB_BINS 256
// screen dimensions
#define SCREEN_W 240
#define SCREEN_H 320
class GlassView : public View {
public:
@@ -69,11 +67,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_;
Gradient gradient{};
RxRadioState radio_state_{ReceiverModel::Mode::SpectrumAnalysis};
// Settings
rf::Frequency f_min = 260 * MHZ_DIV; // Default to 315/433 remote range.
@@ -82,11 +78,10 @@ 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 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
uint8_t live_frequency_view = 0; // Spectrum
uint8_t live_frequency_integrate = 3; // Default (3 * old value + new_value) / 4
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,
@@ -99,7 +94,6 @@ 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},
}};
@@ -111,6 +105,7 @@ class GlassView : public View {
};
void on_freqchg(int64_t freq);
// Function to map the value from one range to another
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();
@@ -118,9 +113,9 @@ class GlassView : public View {
void update_min(int32_t v);
void update_max(int32_t v);
void update_range_field();
void get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power);
rf::Frequency get_freq_from_bin_pos(uint8_t pos);
void on_marker_change();
void get_max_power(const ChannelSpectrum& spectrum, uint16_t bin, uint8_t& max_power);
rf::Frequency get_freq_from_bin_pos(uint16_t pos);
void on_marker_change(int8_t delta);
void retune();
bool process_bins(uint8_t* powerlevel);
void on_channel_spectrum(const ChannelSpectrum& spectrum);
@@ -130,17 +125,19 @@ class GlassView : public View {
void on_range_changed();
void reset_live_view();
void add_spectrum_pixel(uint8_t power);
void plot_marker(uint8_t pos);
void plot_marker(uint16_t pos);
void load_presets();
void populate_presets();
void launch_audio(rf::Frequency center_freq);
bool paused{false};
rf::Frequency search_span{0};
rf::Frequency f_center{0};
rf::Frequency f_center_ini{0};
rf::Frequency marker{0};
uint8_t marker_pixel_index{0};
uint16_t marker_pixel_index{0};
rf::Frequency marker_pixel_step{0};
// Size of one spectrum bin in Hz.
@@ -154,8 +151,8 @@ class GlassView : public View {
uint8_t min_color_power{0}; // Filter cutoff level.
uint32_t pixel_index{0};
std::array<Color, SCREEN_W> spectrum_row{};
std::array<uint8_t, SCREEN_W> spectrum_data{};
std::vector<Color> spectrum_row{};
std::vector<uint8_t> spectrum_data{};
ChannelSpectrumFIFO* fifo{};
int32_t steps = 1;
@@ -167,44 +164,47 @@ class GlassView : public View {
rf::Frequency max_freq_hold = 0;
rf::Frequency last_max_freq = 0;
int16_t max_freq_power = -1000;
uint8_t bin_length = SCREEN_W;
uint8_t bin_length = screen_width;
uint8_t offset = 0;
uint8_t ignore_dc = 0;
bool show_rssi_guides = false;
// start of spectrum area
static constexpr int spectrum_y = (108 + 16);
Labels labels{
{{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}};
{{0, UI_POS_Y(0)}, "MIN: MAX: LNA VGA ", Theme::getInstance()->fg_light->foreground},
{{0, UI_POS_Y(1)}, "RANGE: FILTER: AMP:", Theme::getInstance()->fg_light->foreground},
{{0, UI_POS_Y(2)}, "P:", Theme::getInstance()->fg_light->foreground},
{{0, UI_POS_Y(3)}, "MARKER( / ): MHz", Theme::getInstance()->fg_light->foreground},
{{0, UI_POS_Y(4)}, "RES: VOL:", Theme::getInstance()->fg_light->foreground}};
NumberField field_frequency_min{
{4 * 8, 0 * 16},
{UI_POS_X(4), UI_POS_Y(0)},
4,
{0, 7199},
{0, LOOKING_GLASS_MAX_FREQ_MHZ - 1},
1, // number of steps by encoder delta
' '};
NumberField field_frequency_max{
{13 * 8, 0 * 16},
{UI_POS_X(13), UI_POS_Y(0)},
4,
{1, 7200},
{1, LOOKING_GLASS_MAX_FREQ_MHZ},
1, // number of steps by encoder delta
' '};
LNAGainField field_lna{
{21 * 8, 0 * 16}};
{UI_POS_X(21), UI_POS_Y(0)}};
VGAGainField field_vga{
{27 * 8, 0 * 16}};
{UI_POS_X(27), UI_POS_Y(0)}};
TextField field_range{
{6 * 8, 1 * 16, 6 * 8, 16},
{UI_POS_X(6), UI_POS_Y(1), UI_POS_WIDTH(6), UI_POS_HEIGHT(1)},
""};
OptionsField filter_config{
{20 * 8, 1 * 16},
{UI_POS_X(20), UI_POS_Y(1)},
4,
{
{"OFF ", 0},
@@ -213,41 +213,47 @@ class GlassView : public View {
}};
RFAmpField field_rf_amp{
{28 * 8, 1 * 16}};
{UI_POS_X(28), UI_POS_Y(1)}};
OptionsField range_presets{
{2 * 8, 2 * 16},
{UI_POS_X(2), UI_POS_Y(2)},
20,
{}};
ButtonWithEncoder button_beep_squelch{
{240 - 8 * 8, 2 * 16 + 4, 8 * 8, 1 * 8},
{UI_POS_X_RIGHT(8), UI_POS_Y(2.25), UI_POS_WIDTH(8), UI_POS_HEIGHT(0.5)},
""};
ImageButton button_play{
{UI_POS_X_RIGHT(2), UI_POS_Y(3.25) - 2, UI_POS_WIDTH(2), UI_POS_HEIGHT(0.5)},
&bitmap_stop,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
TextField field_marker{
{7 * 8, 3 * 16, 9 * 8, 16},
{UI_POS_X(12), UI_POS_Y(3), UI_POS_WIDTH(9), UI_POS_HEIGHT(1)},
""};
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,
' ',
};
Button button_marker_minus{
{UI_POS_X(7), UI_POS_Y(3.25), UI_POS_WIDTH(1), UI_POS_HEIGHT(0.5)},
"-"};
Button button_marker_plus{
{UI_POS_X(9), UI_POS_Y(3.25), UI_POS_WIDTH(1), UI_POS_HEIGHT(0.5)},
"+"};
NumberField field_trigger{
{4 * 8, 4 * 16},
{UI_POS_X(4), UI_POS_Y(4)},
3,
{2, 128},
2,
' '};
AudioVolumeField field_volume{
{13 * 8, 4 * 16}};
{UI_POS_X(13), UI_POS_Y(4)}};
/*OptionsField steps_config{
{13 * 8, 4 * 16},
{UI_POS_X(13), UI_POS_Y(4)},
3,
{
{"1", 1},
@@ -259,7 +265,7 @@ class GlassView : public View {
}};*/
OptionsField scan_type{
{17 * 8, 4 * 16},
{UI_POS_X(17), UI_POS_Y(4)},
2,
{
{"F-", LOOKING_GLASS_FASTSCAN},
@@ -267,7 +273,7 @@ class GlassView : public View {
}};
OptionsField view_config{
{19 * 8, 4 * 16},
{UI_POS_X(19), UI_POS_Y(4)},
7,
{
{"SPCTR-V", 0},
@@ -276,7 +282,7 @@ class GlassView : public View {
}};
OptionsField level_integration{
{27 * 8, 4 * 16},
{UI_POS_X(27), UI_POS_Y(4)},
2,
{
{"x0", 0},
@@ -292,15 +298,19 @@ class GlassView : public View {
}};
Button button_jump{
{SCREEN_W - 4 * 8, 5 * 16, 4 * 8, 16},
{UI_POS_X_RIGHT(4), UI_POS_Y(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(1)},
"JMP"};
Button button_rst{
{SCREEN_W - 9 * 8, 5 * 16, 4 * 8, 16},
{UI_POS_X_RIGHT(9), UI_POS_Y(5), UI_POS_WIDTH(4), UI_POS_HEIGHT(1)},
"RST"};
Button button_rssi{
{UI_POS_X_RIGHT(15), UI_POS_Y(5), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)},
"RSSI"};
Text freq_stats{
{0 * 8, 5 * 16, SCREEN_W - 10 * 8, 8},
{UI_POS_X(0), UI_POS_Y(5), UI_POS_WIDTH(14), UI_POS_HEIGHT(1)},
""};
MessageHandlerRegistration message_handler_spectrum_config{
+13 -2
View File
@@ -202,7 +202,7 @@ void MicTXView::rxaudio(bool enable) {
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
// receiver_model.set_nbfm_configuration(n); is called above, depending user's selection (8k5, 11k, 16k).
break;
case MIC_MOD_WFM: // WFM, BW 200Khz aprox, or the two new addional BW filters (180k, 40k)
case MIC_MOD_WFM: // WFM, BW 200Khz aprox, or the two new addional BW filters (180k, 80k)
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
// receiver_model.set_wfm_configuration(n); is called above, depending user's selection (200k, 180k, 0k).
@@ -300,7 +300,7 @@ void MicTXView::update_receiver_rxbw(void) {
receiver_model.set_nbfm_configuration(rxbw_index); // we are in NFM/FM case, we need to select proper NFM/FM RX channel filter, NFM BW 8K5(0), NFM BW 11K(1), FM BW 16K (2)
break;
case MIC_MOD_WFM:
receiver_model.set_wfm_configuration(rxbw_index); // we are in WFM case, we need to select proper WFB RX BW filter, WFM BW 200K(0), WFM BW 180K(1), WFM BW 40K(2)
receiver_model.set_wfm_configuration(rxbw_index); // we are in WFM case, we need to select proper WFB RX BW filter, WFM BW 200K(0), WFM BW 180K(1), WFM BW 80K(2)
break;
case MIC_MOD_AM:
receiver_model.set_am_configuration(rxbw_index); // we are in AM TX mode, we need to select proper AM full path config AM-9K filter. 0+0 =>AM-9K(0), 0+1=1 =>AM-6K(1),
@@ -350,6 +350,12 @@ MicTXView::MicTXView(
&tx_button,
&tx_icon});
// disable key repeat on select
initial_switch_config_ = get_switches_repeat_config();
SwitchesState config = initial_switch_config_;
config[toUType(Switch::Sel)] = false;
set_switches_repeat_config(config);
set_rxbw_options();
set_rxbw_defaults(settings_.loaded());
@@ -446,6 +452,8 @@ MicTXView::MicTXView(
field_frequency.set_value(tx_frequency);
// TODO: would be nice if frequency step was configurable in this app
// but now, make it a bit lower
receiver_model.set_frequency_step(12'500); // set freq step to 12.5kHz
field_frequency.set_step(receiver_model.frequency_step());
// WARNING: transmitter_model.set_target_frequency() and receiver_model.set_target_frequency() both update the same tuning freq, but one has an offset!
@@ -638,6 +646,9 @@ MicTXView::MicTXView(
}
MicTXView::~MicTXView() {
// restore select key repeat mode
set_switches_repeat_config(initial_switch_config_);
audio::input::stop();
if (rx_enabled) { // Also turn off both (audio rx if enabled, and disable mic_loop to HP)
rxaudio(false);
+7 -3
View File
@@ -90,6 +90,9 @@ class MicTXView : public View {
sampling_rate /* sampling rate */
};
// structure used to control key repeat
SwitchesState initial_switch_config_{};
enum Mic_Modulation : uint32_t {
MIC_MOD_NFM = 0,
MIC_MOD_WFM = 1,
@@ -170,7 +173,7 @@ class MicTXView : public View {
{{17 * 8, 1 * 8}, "ALC", Theme::getInstance()->fg_light->foreground}};
VuMeter vumeter{
{0 * 8, 1 * 8, 2 * 8, 33 * 8},
{UI_POS_X(0), 1 * 8, 2 * 8, 33 * 8},
12,
true};
@@ -301,7 +304,8 @@ class MicTXView : public View {
{
{" 8k5 ", 0}, // Initial dynamic values when we start Mic App.
{" 11k ", 1},
{" 16k ", 2},
{" 12k5 ", 2},
{" 16k ", 3},
}};
NumberField field_squelch{
@@ -350,7 +354,7 @@ class MicTXView : public View {
};
Button tx_button{
{10 * 8, 30 * 8, 10 * 8, 5 * 8},
{10 * 8, screen_width, 10 * 8, 5 * 8},
"PTT TX",
true};
+1 -1
View File
@@ -104,7 +104,7 @@ class ModemSetupView : public View {
"SET"};
Button button_save{
{9 * 8, 250, 96, 40},
{UI_POS_X_CENTER(12), 250, UI_POS_WIDTH(12), 40},
"Save"};
};
@@ -387,7 +387,6 @@ PlaylistView::PlaylistView(
&button_next,
&waterfall,
});
ensure_directory(playlist_dir);
waterfall.show_audio_spectrum_view(false);
+8 -8
View File
@@ -107,24 +107,24 @@ class PlaylistView : public View {
void handle_replay_thread_done(uint32_t return_code);
Text text_filename{
{0 * 8, 0 * 16, 30 * 8, 16}};
{UI_POS_X(0), UI_POS_Y(0), screen_width, 16}};
FrequencyField field_frequency{
{0 * 8, 1 * 16}};
{UI_POS_X(0), 1 * 16}};
Text text_sample_rate{
{10 * 8, 1 * 16, 7 * 8, 16}};
ProgressBar progressbar_track{
{18 * 8, 1 * 16, 12 * 8, 8 + 1}};
{18 * 8, 1 * 16, UI_POS_WIDTH_REMAINING(19), 8 + 1}};
// (-1) to overlap with progressbar_track so there's
// only 1 pixel between them instead of 2.
ProgressBar progressbar_transmit{
{18 * 8, 3 * 8 - 1, 12 * 8, 8}};
{18 * 8, 3 * 8 - 1, UI_POS_WIDTH_REMAINING(19), 8}};
Text text_duration{
{0 * 8, 2 * 16, 5 * 8, 16}};
{UI_POS_X(0), 2 * 16, 5 * 8, 16}};
// TODO: delay duration field.
@@ -133,19 +133,19 @@ class PlaylistView : public View {
/*short_ui*/ true};
Checkbox check_loop{
{21 * 8, 2 * 16},
{UI_POS_X_RIGHT(9), 2 * 16},
4,
"Loop",
true};
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
{screen_width - 2 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
Text text_track{
{0 * 8, 3 * 16, 30 * 8, 16}};
{UI_POS_X(0), 3 * 16, screen_width, 16}};
NewButton button_prev{
{2 * 8, 4 * 16, 4 * 8, 2 * 16},
+2
View File
@@ -65,6 +65,7 @@ RDSPSNView::RDSPSNView(
nav,
PSN,
8,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& s) {
text_psn.set(s);
});
@@ -86,6 +87,7 @@ RDSRadioTextView::RDSRadioTextView(
nav,
radiotext,
28,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& s) {
text_radiotext.set(s);
});
+6 -6
View File
@@ -82,7 +82,7 @@ class RDSRadioTextView : public OptionTabView {
{1 * 8, 4 * 16, 28 * 8, 16},
"-"};
Button button_set{
{88, 6 * 16, 64, 32},
{UI_POS_X_CENTER(8), 6 * 16, UI_POS_WIDTH(8), 32},
"Set"};
};
@@ -92,7 +92,7 @@ class RDSDateTimeView : public OptionTabView {
private:
Labels labels{
{{44, 5 * 16}, "Not yet implemented", Theme::getInstance()->error_dark->foreground}};
{{UI_POS_X_CENTER(19), 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", Theme::getInstance()->error_dark->foreground}};
{{UI_POS_X_CENTER(19), 5 * 16}, "Not yet implemented", Theme::getInstance()->error_dark->foreground}};
};
class RDSThread {
@@ -160,7 +160,7 @@ class RDSView : public View {
void start_tx();
Rect view_rect = {0, 8 * 8, 240, 192};
Rect view_rect = {0, 8 * 8, screen_width, 192};
RDSPSNView view_PSN{nav_, view_rect};
RDSRadioTextView view_radiotext{nav_, view_rect};
@@ -174,7 +174,7 @@ class RDSView : public View {
{"Audio", Theme::getInstance()->fg_orange->foreground, &view_audio}};
Labels labels{
{{0 * 8, 28}, "Program type:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 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 },
@@ -319,7 +319,7 @@ class RDSView : public View {
"TP"};
TransmitterView tx_view{
16 * 16,
(int16_t)UI_POS_Y_BOTTOM(4),
50000,
9};
+19 -14
View File
@@ -335,7 +335,7 @@ ReconView::ReconView(NavigationView& nav)
tx_view.hidden(true);
// set record View
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
record_view = std::make_unique<RecordView>(Rect{0, 0, screen_width, 1 * 16},
u"AUTO_AUDIO", audio_dir,
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
@@ -517,11 +517,7 @@ ReconView::ReconView(NavigationView& nav)
};
set_loop_config(continuous);
rssi.set_focusable(true);
rssi.set_peak(true, 500);
rssi.on_select = [this](RSSI&) {
nav_.replace<LevelView>();
};
// TODO: *BUG* Both transmitter_model and receiver_model share the same pmem setting for target_frequency.
button_mic_app.on_select = [this](Button&) {
@@ -1090,8 +1086,8 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (stepper < 0) stepper++;
if (stepper > 0) stepper--;
} // if( recon || stepper != 0 || index_stepper != 0 )
} // if (frequency_list.size() > 0 )
} /* on_statistics_updates */
} // if (frequency_list.size() > 0 )
} /* on_statistics_updates */
}
handle_retune();
recon_redraw();
@@ -1168,18 +1164,18 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
}
if (new_mod == SPEC_MODULATION) {
if (persistent_memory::recon_repeat_recorded()) {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
record_view = std::make_unique<RecordView>(Rect{0, 0, screen_width, 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},
record_view = std::make_unique<RecordView>(Rect{0, 0, screen_width, 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},
record_view = std::make_unique<RecordView>(Rect{0, 0, screen_width, 1 * 16},
u"AUTO_AUDIO", audio_dir,
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
@@ -1257,9 +1253,18 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
field_mode.set_selected_index(new_mod);
field_mode.on_change = [this](size_t, OptionsField::value_t v) {
if (v != -1) {
change_mode(v);
// initialize to a value under SPEC
static freqman_index_t last_mode = WFM_MODULATION;
if (v > SPEC_MODULATION) {
if (last_mode == SPEC_MODULATION)
v = AM_MODULATION;
else
v = SPEC_MODULATION;
field_mode.set_selected_index(v);
return; // Important ! Guru will occur if you don't return when modifying field from within on_change !
}
last_mode = v;
change_mode(v);
};
// for some motive, audio output gets stopped.
if (!recon && field_mode.selected_index_value() != SPEC_MODULATION)
@@ -1400,8 +1405,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}, Theme::getInstance()->fg_light->foreground);
p.draw_string({(SCREEN_W / 2) - 7 * 8, SCREEN_H / 2}, *Theme::getInstance()->fg_red, delay_message);
p.fill_rectangle({0, (screen_height / 2) - 16, screen_width, 64}, Theme::getInstance()->fg_light->foreground);
p.draw_string({(screen_width / 2) - 7 * 8, screen_height / 2}, *Theme::getInstance()->fg_red, delay_message);
// sleep 1 second
chThdSleepMilliseconds(1000);
+23 -23
View File
@@ -31,7 +31,6 @@
#include "analog_audio_app.hpp"
#include "audio.hpp"
#include "ui_mictx.hpp"
#include "ui_level.hpp"
#include "ui_looking_glass_app.hpp"
#include "portapack_persistent_memory.hpp"
#include "baseband_api.hpp"
@@ -218,35 +217,36 @@ class ReconView : public View {
std::unique_ptr<RecordView> record_view{};
Labels labels{
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP: ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(6), UI_POS_Y(0)}, "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}};
{{UI_POS_X(0), (22 * 8)}, " S: ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), (24 * 8) + 4}, "NBLCKS:x W,L: , ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), (26 * 8) + 4}, "MODE: , SQUELCH: ", Theme::getInstance()->fg_light->foreground}};
LNAGainField field_lna{
{4 * 8, 0 * 16}};
{4 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{11 * 8, 0 * 16}};
{11 * 8, UI_POS_Y(0)}};
RFAmpField field_rf_amp{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
AudioVolumeField field_volume{
{24 * 8, 0 * 16}};
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
Text file_name{
// show file used
{0, 1 * 16, SCREEN_W, 16},
{0, 1 * 16, screen_width, 16},
};
Text desc_cycle{
{0, 2 * 16, SCREEN_W, 16},
{0, 2 * 16, screen_width, 16},
};
RSSI rssi{
{0 * 16, 3 * 16 + 2, SCREEN_W - 8 * 8 + 4, 12},
{0 * 16, 3 * 16 + 2, screen_width - 8 * 8 + 4, 12},
};
ButtonWithEncoder text_cycle{
@@ -286,19 +286,19 @@ class ReconView : public View {
// Button can be RECON or SCANNER
Button button_scanner_mode{
{SCREEN_W - 7 * 8, 3 * 16, 7 * 8, 28},
{screen_width - 7 * 8, 3 * 16, 7 * 8, 28},
"RECON"};
Button button_loop_config{
{SCREEN_W - 7 * 8, 5 * 16, 7 * 8, 28},
{screen_width - 7 * 8, 5 * 16, 7 * 8, 28},
"[LOOP]"};
Button button_config{
{SCREEN_W - 7 * 8, 7 * 16, 7 * 8, 28},
{screen_width - 7 * 8, 7 * 16, 7 * 8, 28},
"CONFIG"};
ButtonWithEncoder button_manual_start{
{0 * 8, 9 * 16, 11 * 8, 28},
{UI_POS_X(0), 9 * 16, 11 * 8, 28},
""};
ButtonWithEncoder button_manual_end{
@@ -306,7 +306,7 @@ class ReconView : public View {
""};
OptionsField field_recon_match_mode{
{0 * 8, 11 * 16},
{UI_POS_X(0), 11 * 16},
16, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE
{
{"MATCH:CONTINOUS", 0},
@@ -364,15 +364,15 @@ class ReconView : public View {
};
ButtonWithEncoder button_pause{
{0, (15 * 16) - 4, 72, 28},
{0, (15 * 16) - 4, UI_POS_WIDTH(9), 28},
"PAUSE"};
Button button_audio_app{
{84, (15 * 16) - 4, 72, 28},
{UI_POS_X_CENTER(9), (15 * 16) - 4, UI_POS_WIDTH(9), 28},
"AUDIO"};
ButtonWithEncoder button_add{
{168, (15 * 16) - 4, 72, 28},
{UI_POS_X_RIGHT(9), (15 * 16) - 4, UI_POS_WIDTH(9), 28},
"<STORE>"};
Button button_dir{
@@ -384,15 +384,15 @@ class ReconView : public View {
"RST"};
Button button_mic_app{
{84, (35 * 8) - 4, 72, 28},
{UI_POS_X_CENTER(9), (35 * 8) - 4, UI_POS_WIDTH(9), 28},
"MIC TX"};
ButtonWithEncoder button_remove{
{168, (35 * 8) - 4, 72, 28},
{UI_POS_X_RIGHT(9), (35 * 8) - 4, UI_POS_WIDTH(9), 28},
"<REMOVE>"};
ProgressBar progressbar{
{0 * 8, SCREEN_H / 2 - 16, SCREEN_W, 32}};
{UI_POS_X(0), screen_height / 2 - 16, screen_width, 32}};
TransmitterView2 tx_view{
{11 * 8, 2 * 16},
@@ -82,7 +82,7 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
};
button_choose_output_name.on_select = [this, &nav](Button&) {
text_prompt(nav, _output_file, 28, [this](std::string& buffer) {
text_prompt(nav, _output_file, 28, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& buffer) {
_output_file = buffer;
button_choose_output_name.set_text(_output_file);
});
@@ -57,10 +57,6 @@
#define RECON_MIN_LOCK_DURATION 100 // have to be >= and a multiple of STATS_UPDATE_INTERVAL
#define RECON_DEF_WAIT_DURATION 1000 // will be incremented/decremented by STATS_UPDATE_INTERVAL steps
// screen size helper
#define SCREEN_W 240
// #define SCREEN_H 320
// recon settings nb params
#define RECON_SETTINGS_NB_PARAMS 7
@@ -219,7 +215,7 @@ class ReconSetupView : public View {
std::string input_file{"RECON"};
std::string output_file{"RECON_RESULTS"};
Rect view_rect{0, 3 * 8, SCREEN_W, 230};
Rect view_rect{0, 3 * 8, screen_width, 230};
ReconSetupViewMain viewMain{nav_, view_rect, input_file, output_file};
ReconSetupViewMore viewMore{nav_, view_rect};
@@ -229,7 +225,7 @@ class ReconSetupView : public View {
{"More", Theme::getInstance()->fg_green->foreground, &viewMore}};
Button button_save{
{9 * 8, 255, 14 * 8, 40},
{UI_POS_X_CENTER(18), 255, UI_POS_WIDTH(18), 40},
"SAVE"};
};
+42 -12
View File
@@ -26,17 +26,24 @@
#include "binder.hpp"
#include "string_format.hpp"
#include "ui_freqman.hpp"
#include "audio.hpp"
using namespace portapack;
namespace pmem = portapack::persistent_memory;
namespace ui {
void SearchLogger::log_data(SearchRecentEntry& data) {
log_file.write_entry(";" + to_string_short_freq(data.frequency) + ";" + std::to_string(data.duration));
}
template <>
void RecentEntriesTable<SearchRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
const Style& style,
RecentEntriesColumns& columns) {
std::string str_duration = "";
if (entry.duration < 600)
@@ -44,9 +51,10 @@ void RecentEntriesTable<SearchRecentEntries>::draw(
else
str_duration = to_string_dec_uint(entry.duration / 600) + "m" + to_string_dec_uint((entry.duration / 10) % 60) + "s";
str_duration.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.location(), style, to_string_short_freq(entry.frequency) + " " + entry.time + " " + str_duration);
str_duration.resize(11, ' ');
std::string freq = to_string_short_freq(entry.frequency);
freq.resize(columns.at(0).second, ' ');
painter.draw_string(target_rect.location(), style, freq + " " + entry.time + " " + str_duration);
}
/* SearchView ********************************************/
@@ -54,8 +62,13 @@ void RecentEntriesTable<SearchRecentEntries>::draw(
SearchView::SearchView(
NavigationView& nav)
: nav_(nav) {
spectrum_row.resize(240);
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
if (!gradient.load_file(default_gradient_file)) {
gradient.set_default();
}
add_children({&labels,
&field_frequency_min,
&field_frequency_max,
@@ -71,11 +84,12 @@ SearchView::SearchView(
&check_snap,
&options_snap,
&big_display,
&check_log,
&recent_entries_view});
baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31);
recent_entries_view.set_parent_rect({0, 28 * 8, screen_width, 12 * 8});
recent_entries_view.set_parent_rect({0, 28 * 8, screen_width, screen_height - 28 * 8});
recent_entries_view.on_select = [this, &nav](const SearchRecentEntry& entry) {
nav.push<FrequencySaveView>(entry.frequency);
};
@@ -96,6 +110,14 @@ SearchView::SearchView(
on_range_changed();
});
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
if (logging) {
logger.append(logs_dir.string() + "/SEARCH_" + to_string_timestamp(rtc_time::now()) + ".CSV");
logger.write_header();
}
};
bind(field_threshold, settings_.power_threshold);
bind(check_snap, settings_.snap_search);
bind(options_snap, settings_.snap_step);
@@ -104,9 +126,15 @@ SearchView::SearchView(
on_range_changed();
receiver_model.enable();
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
}
SearchView::~SearchView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
@@ -135,9 +163,8 @@ void SearchView::do_detection() {
// Display spectrum
bin_skip_acc = 0;
pixel_index = 0;
display.draw_pixels(
{{0, 88}, {(Dim)spectrum_row.size(), 1}},
spectrum_row);
uint16_t center_align_start = (screen_width - spectrum_row.size()) / 2;
display.draw_pixels({{center_align_start, 88}, {(Dim)spectrum_row.size(), 1}}, spectrum_row);
mean_power = mean_acc / (SEARCH_BIN_NB_NO_DC * slices_nb);
mean_acc = 0;
@@ -188,7 +215,9 @@ void SearchView::do_detection() {
locked = true;
locked_bin = bin_max;
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
// TODO: open Audio.
} else
text_infos.set("Out of range");
@@ -206,6 +235,7 @@ void SearchView::do_detection() {
auto& entry = ::on_packet(recent, resolved_frequency);
entry.set_duration(duration);
if (logging) logger.log_data(entry);
recent_entries_view.set_dirty();
text_infos.set("Listening");
@@ -221,7 +251,7 @@ void SearchView::do_detection() {
// Refresh red tick
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);
last_tick_pos = (Coord)(bin_max / slices_nb) + center_align_start;
portapack::display.fill_rectangle({last_tick_pos, 90, 1, 6}, Theme::getInstance()->fg_red->foreground);
}
}
@@ -290,7 +320,7 @@ void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
power = spectrum.db[bin - 128];
}
add_spectrum_pixel(spectrum_rgb3_lut[power]);
add_spectrum_pixel(gradient.lut[power]);
mean_acc += power;
if (power > max_power) {
@@ -370,7 +400,7 @@ void SearchView::add_spectrum_pixel(Color color) {
bin_skip_acc -= 0x10000;
if (pixel_index < screen_width)
if (pixel_index < spectrum_row.size())
spectrum_row[pixel_index++] = color;
}
+56 -29
View File
@@ -24,8 +24,10 @@
#include "receiver_model.hpp"
#include "recent_entries.hpp"
#include "radio_state.hpp"
#include "spectrum_color_lut.hpp"
#include "gradient.hpp"
#include "ui_receiver.hpp"
#include "log_file.hpp"
#include "file_path.hpp"
namespace ui {
@@ -70,6 +72,21 @@ struct SearchRecentEntry {
using SearchRecentEntries = RecentEntries<SearchRecentEntry>;
class SearchLogger {
public:
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
void log_data(SearchRecentEntry& data);
void write_header() {
log_file.write_raw("Time;Freq;Duration;");
}
private:
LogFile log_file{};
};
class SearchView : public View {
public:
SearchView(NavigationView& nav);
@@ -88,6 +105,7 @@ class SearchView : public View {
private:
NavigationView& nav_;
Gradient gradient{};
RxRadioState radio_state_{
100'000'000 /* frequency */,
2500000 /* bandwidth */,
@@ -125,7 +143,7 @@ class SearchView : public View {
uint32_t bin_skip_acc = 0;
uint32_t bin_skip_frac = 0;
uint32_t pixel_index = 0;
std::array<Color, 240> spectrum_row{};
std::vector<Color> spectrum_row{};
ChannelSpectrumFIFO* fifo = nullptr;
uint8_t detect_timer = 0;
@@ -147,6 +165,8 @@ class SearchView : public View {
uint16_t locked_bin = 0;
uint8_t search_counter = 0;
bool locked = false;
bool logging = false;
SearchLogger logger{};
void do_detection();
void do_timers();
@@ -154,75 +174,82 @@ class SearchView : public View {
void on_range_changed();
void add_spectrum_pixel(Color color);
const RecentEntriesColumns columns{{{"Frequency", 9},
{"Time", 8},
{"Duration", 11}}};
RecentEntriesColumns columns{{{"Frequency", 0},
{"Time", 8},
{"Duration", 11}}};
SearchRecentEntries recent{};
RecentEntriesView<RecentEntries<SearchRecentEntry>> recent_entries_view{columns, recent};
Labels labels{
{{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}};
{{UI_POS_X(1), UI_POS_Y(0)}, "Min: Max: ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(7), UI_POS_Y(0)}, "LNA VGA", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(1), UI_POS_Y(2)}, "Trig: /255", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(12), UI_POS_Y(2)}, "Mean: /255", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(1), UI_POS_Y(3)}, "Slices: /32", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(10), UI_POS_Y(3)}, "Rate: Hz", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(6), UI_POS_Y(5)}, "Timer Status", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(1), 25 * 8}, "Accuracy +/-4.9kHz", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_RIGHT(4), 25 * 8}, "MHz", Theme::getInstance()->fg_light->foreground}};
Checkbox check_log{
{UI_POS_X_RIGHT(6), UI_POS_Y(5)},
3,
"LOG",
true};
FrequencyField field_frequency_min{
{1 * 8, 1 * 16}};
{UI_POS_X(1), UI_POS_Y(1)}};
FrequencyField field_frequency_max{
{11 * 8, 1 * 16}};
{UI_POS_X(11), UI_POS_Y(1)}};
LNAGainField field_lna{
{22 * 8, 1 * 16}};
{UI_POS_X_RIGHT(7), UI_POS_Y(1)}};
VGAGainField field_vga{
{26 * 8, 1 * 16}};
{UI_POS_X_RIGHT(3), UI_POS_Y(1)}};
NumberField field_threshold{
{6 * 8, 2 * 16},
{UI_POS_X(6), UI_POS_Y(2)},
3,
{5, 255},
5,
' '};
Text text_mean{
{22 * 8, 2 * 16, 3 * 8, 16},
{UI_POS_X_RIGHT(7), UI_POS_Y(2), UI_POS_WIDTH(3), UI_POS_HEIGHT(1)},
"---"};
Text text_slices{
{8 * 8, 3 * 16, 2 * 8, 16},
{UI_POS_X(8), UI_POS_Y(3), UI_POS_WIDTH(2), UI_POS_HEIGHT(1)},
"--"};
Text text_rate{
{24 * 8, 3 * 16, 3 * 8, 16},
{UI_POS_X_RIGHT(5), UI_POS_Y(3), UI_POS_WIDTH(3), UI_POS_HEIGHT(1)},
"---"};
VuMeter vu_max{
{1 * 8, 11 * 8 - 4, 3 * 8, 48},
{UI_POS_X(1), 11 * 8 - 4, 3 * 8, 48},
18,
false};
ProgressBar progress_timers{
{6 * 8, 12 * 8, 6 * 8, 16}};
{UI_POS_X(6), UI_POS_Y(6), UI_POS_WIDTH(6), UI_POS_HEIGHT(1)}};
Text text_infos{
{13 * 8, 12 * 8, 15 * 8, 16},
{UI_POS_X(13), UI_POS_Y(6), UI_POS_WIDTH(15), UI_POS_HEIGHT(1)},
"Listening"};
Checkbox check_snap{
{6 * 8, 15 * 8},
{UI_POS_X(6), 15 * 8},
7,
"Snap to:",
true};
OptionsField options_snap{
{17 * 8, 15 * 8}, // Position
7, // Length
{ // Options
{UI_POS_X(17), 15 * 8}, // Position
7, // Length
{ // Options
{"25kHz ", 25'000},
{"12.5kHz", 12'500},
{"8.33kHz", 8'333},
{"2.5kHz", 2'500},
{"500Hz", 500}}};
BigFrequency big_display{
{4, 9 * 16, 28 * 8, 52},
0};
BigFrequency big_display{{UI_POS_X_CENTER(28), UI_POS_Y(9), UI_POS_WIDTH(28), 52}, 0};
MessageHandlerRegistration message_handler_spectrum_config{
Message::ID::ChannelSpectrumConfig,
+288 -106
View File
@@ -6,7 +6,7 @@
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
* Copyright (C) 2024 HTotoo
* Copyleft (ɔ) 2024 zxkmm under GPL license
* copyleft 2024 zxkmm
*
* This file is part of PortaPack.
*
@@ -33,6 +33,9 @@
#include "ui_touch_calibration.hpp"
#include "ui_text_editor.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "ui_ss_viewer.hpp"
#include "ui_fileman.hpp"
#include "ui_sd_card_debug.hpp"
#include "portapack_persistent_memory.hpp"
#include "lpc43xx_cpp.hpp"
@@ -40,6 +43,7 @@ using namespace lpc43xx;
#include "audio.hpp"
#include "portapack.hpp"
#include "portapack_io.hpp"
using namespace portapack;
#include "file.hpp"
@@ -53,6 +57,8 @@ namespace fs = std::filesystem;
#include "i2cdevmanager.hpp"
#include "i2cdev_max17055.hpp"
#include "file_reader.hpp"
extern ui::SystemView* system_view_ptr;
namespace pmem = portapack::persistent_memory;
@@ -306,13 +312,45 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() {
};
}
/* SetTXLimitView ************************************/
SetTXLimitView::SetTXLimitView(NavigationView& nav) {
add_children({
&labels,
&tx_gain_max_db,
&tx_disable_switch,
&tx_amp_disable_switch,
&button_save,
&button_cancel,
});
tx_disable_switch.set_value(pmem::config_tx_disabled());
tx_amp_disable_switch.set_value(pmem::config_tx_amp_disabled());
tx_gain_max_db.set_value(pmem::config_tx_gain_max_db());
button_save.on_select = [&nav, this](Button&) {
pmem::set_config_tx_disabled(tx_disable_switch.value());
pmem::set_config_tx_amp_disabled(tx_amp_disable_switch.value());
pmem::set_config_tx_gain_max_db(tx_gain_max_db.value());
send_system_refresh();
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetTXLimitView::focus() {
button_save.focus();
}
/* SetUIView *********************************************/
SetUIView::SetUIView(NavigationView& nav) {
add_children({&checkbox_disable_touchscreen,
&checkbox_bloff,
&options_bloff,
&checkbox_showsplash,
&checkbox_showclock,
&options_clockformat,
&checkbox_guireturnflag,
@@ -324,6 +362,7 @@ SetUIView::SetUIView(NavigationView& nav) {
&toggle_bias_tee,
&toggle_clock,
&toggle_mute,
&toggle_fake_brightness,
&toggle_sd_card,
&button_save,
&button_cancel});
@@ -338,7 +377,6 @@ SetUIView::SetUIView(NavigationView& nav) {
}
checkbox_disable_touchscreen.set_value(pmem::disable_touchscreen());
checkbox_showsplash.set_value(pmem::config_splash());
checkbox_showclock.set_value(!pmem::hide_clock());
checkbox_guireturnflag.set_value(pmem::show_gui_return_icon());
@@ -361,6 +399,7 @@ SetUIView::SetUIView(NavigationView& nav) {
toggle_clock.set_value(!pmem::ui_hide_clock());
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());
@@ -376,7 +415,6 @@ SetUIView::SetUIView(NavigationView& nav) {
pmem::set_clock_with_date(false);
}
pmem::set_config_splash(checkbox_showsplash.value());
pmem::set_clock_hidden(!checkbox_showclock.value());
pmem::set_gui_return_icon(checkbox_guireturnflag.value());
pmem::set_disable_touchscreen(checkbox_disable_touchscreen.value());
@@ -389,6 +427,7 @@ SetUIView::SetUIView(NavigationView& nav) {
pmem::set_ui_hide_clock(!toggle_clock.value());
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());
@@ -408,7 +447,11 @@ void SetUIView::focus() {
/* SetSDCardView *********************************************/
SetSDCardView::SetSDCardView(NavigationView& nav) {
add_children({&labels,
add_children({&status_labels,
&text_card_status,
&text_filesystem_type,
&button_more_info,
&labels,
&checkbox_sdcard_speed,
&button_test_sdcard_high_speed,
&text_sdcard_test_status,
@@ -417,6 +460,10 @@ SetSDCardView::SetSDCardView(NavigationView& nav) {
checkbox_sdcard_speed.set_value(pmem::config_sdcard_high_speed_io());
button_more_info.on_select = [&nav, this](Button&) {
nav.push<SDCardDebugView>();
};
button_test_sdcard_high_speed.on_select = [&nav, this](Button&) {
pmem::set_config_sdcard_high_speed_io(true, false);
text_sdcard_test_status.set("!! HIGH SPEED MODE ON !!");
@@ -434,7 +481,79 @@ SetSDCardView::SetSDCardView(NavigationView& nav) {
}
void SetSDCardView::focus() {
button_save.focus();
button_cancel.focus();
}
void SetSDCardView::on_show() {
sd_card_status_signal_token = sd_card::status_signal += [this](const sd_card::Status) {
update_sd_card_status();
};
update_sd_card_status();
}
void SetSDCardView::on_hide() {
sd_card::status_signal -= sd_card_status_signal_token;
}
void SetSDCardView::update_sd_card_status() {
using sd_card::Status;
const auto status = sd_card::status();
// Update card status text
switch (status) {
case Status::NotPresent:
text_card_status.set("Not Inserted");
text_filesystem_type.set("---");
break;
case Status::Present:
text_card_status.set("Inserted");
text_filesystem_type.set("---");
break;
case Status::Mounted:
text_card_status.set("Mounted");
// Determine filesystem type
{
const auto fs_type = sd_card::fs.fs_type;
std::string fs_name;
switch (fs_type) {
case FS_FAT12:
fs_name = "FAT12";
break;
case FS_FAT16:
fs_name = "FAT16";
break;
case FS_FAT32:
fs_name = "FAT32";
break;
case FS_EXFAT:
fs_name = "exFAT";
break;
default:
fs_name = "Unknown";
break;
}
text_filesystem_type.set(fs_name);
}
break;
case Status::ConnectError:
text_card_status.set("Connect Error");
text_filesystem_type.set("---");
break;
case Status::MountError:
text_card_status.set("Mount Error");
text_filesystem_type.set("---");
break;
case Status::IOError:
text_card_status.set("I/O Error");
text_filesystem_type.set("---");
break;
default:
text_card_status.set("Unknown");
text_filesystem_type.set("---");
break;
}
}
/* SetConverterSettingsView ******************************/
@@ -669,32 +788,6 @@ void SetAudioView::focus() {
button_save.focus();
}
/* SetQRCodeView *****************************************/
SetQRCodeView::SetQRCodeView(NavigationView& nav) {
add_children({
&labels,
&checkbox_bigger_qr,
&button_save,
&button_cancel,
});
checkbox_bigger_qr.set_value(pmem::show_bigger_qr_code());
button_save.on_select = [&nav, this](Button&) {
pmem::set_show_bigger_qr_code(checkbox_bigger_qr.value());
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetQRCodeView::focus() {
button_save.focus();
}
/* SetEncoderDialView ************************************/
SetEncoderDialView::SetEncoderDialView(NavigationView& nav) {
@@ -702,14 +795,34 @@ SetEncoderDialView::SetEncoderDialView(NavigationView& nav) {
&field_encoder_dial_sensitivity,
&field_encoder_rate_multiplier,
&button_save,
&button_cancel});
&button_cancel,
&button_dial_sensitivity_plus,
&button_dial_sensitivity_minus,
&button_rate_multiplier_plus,
&button_rate_multiplier_minus,
&field_encoder_dial_direction});
field_encoder_dial_sensitivity.set_by_value(pmem::encoder_dial_sensitivity());
field_encoder_rate_multiplier.set_value(pmem::encoder_rate_multiplier());
field_encoder_dial_direction.set_by_value(pmem::encoder_dial_direction());
button_dial_sensitivity_plus.on_select = [this](Button&) {
field_encoder_dial_sensitivity.on_encoder(1);
};
button_dial_sensitivity_minus.on_select = [this](Button&) {
field_encoder_dial_sensitivity.on_encoder(-1);
};
button_rate_multiplier_plus.on_select = [this](Button&) {
field_encoder_rate_multiplier.on_encoder(1);
};
button_rate_multiplier_minus.on_select = [this](Button&) {
field_encoder_rate_multiplier.on_encoder(-1);
};
button_save.on_select = [&nav, this](Button&) {
pmem::set_encoder_dial_sensitivity(field_encoder_dial_sensitivity.selected_index_value());
pmem::set_encoder_rate_multiplier(field_encoder_rate_multiplier.value());
pmem::set_encoder_dial_direction(field_encoder_dial_direction.selected_index_value());
nav.pop();
};
@@ -722,6 +835,36 @@ void SetEncoderDialView::focus() {
button_save.focus();
}
/* SetButtonsView ************************************/
SetButtonsView::SetButtonsView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&field_repeat_delay,
&field_repeat_speed,
&field_long_press_delay});
field_repeat_delay.set_by_value(pmem::ui_button_repeat_delay());
field_repeat_speed.set_by_value(pmem::ui_button_repeat_speed());
field_long_press_delay.set_by_value(pmem::ui_button_long_press_delay());
button_save.on_select = [&nav, this](Button&) {
pmem::set_ui_button_repeat_delay(field_repeat_delay.selected_index_value());
pmem::set_ui_button_repeat_speed(field_repeat_speed.selected_index_value());
pmem::set_ui_button_long_press_delay(field_long_press_delay.selected_index_value());
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetButtonsView::focus() {
button_save.focus();
}
/* AppSettingsView ************************************/
AppSettingsView::AppSettingsView(
@@ -730,7 +873,7 @@ AppSettingsView::AppSettingsView(
add_children({&labels,
&menu_view});
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
menu_view.set_parent_rect({0, 3 * 8, screen_width, UI_POS_HEIGHT_REMAINING(3)});
ensure_directory(settings_dir);
@@ -777,21 +920,38 @@ void SetConfigModeView::focus() {
/* SetDisplayView ************************************/
SetDisplayView::SetDisplayView(NavigationView& nav) {
add_children({&button_save,
add_children({&labels,
&field_fake_brightness,
&button_save,
&button_cancel,
&checkbox_invert_switch});
&checkbox_brightness_switch});
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
if (portapack::device_type == portapack::DeviceType::DEV_PORTAPACK) {
add_child(&checkbox_ips_screen_switch);
}
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_ips_screen_switch.set_value(pmem::config_lcd_normally_black());
button_save.on_select = [&nav, this](Button&) {
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());
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
if (checkbox_ips_screen_switch.value() != pmem::config_lcd_normally_black()) {
pmem::set_lcd_normally_black(checkbox_ips_screen_switch.value());
}
send_system_refresh();
nav.pop();
};
// only enable invert OR fake brightness
checkbox_ips_screen_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_ips_screen_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
@@ -957,67 +1117,6 @@ void SetMenuColorView::focus() {
button_save.focus();
}
/* SetAutoStartView ************************************/
SetAutostartView::SetAutostartView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&button_reset,
&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();
};
button_reset.on_select = [this](Button&) {
selected = 0;
options.set_selected_index(0);
autostart_app = "";
};
// 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) {
@@ -1061,26 +1160,62 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&checkbox_overridebatt});
&field_battcap,
&button_help_cap,
&checkbox_overridebatt,
&checkbox_battery_replaceable,
&checkbox_battery_charge_hint});
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) add_children({&button_reset, &labels2});
button_save.on_select = [&nav, this](Button&) {
pmem::set_ui_override_batt_calc(checkbox_overridebatt.value());
pmem::set_ui_battery_charge_hint(checkbox_battery_charge_hint.value());
pmem::set_battery_replaceable(checkbox_battery_replaceable.value());
battery::BatteryManagement::set_calc_override(checkbox_overridebatt.value());
i2cdev::I2cDev_MAX17055* dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
if (dev) dev->resetChangedFlag();
if ((((uint32_t)field_battcap.value() != pmem::battery_cap_mah()) || (!pmem::battery_cap_valid())) || (dev && dev->getIsBattChanged())) {
pmem::set_battery_cap_mah(field_battcap.value());
if (dev && !dev->reInit()) {
nav.display_modal("Error", "Battery gauge re-init failed");
return;
}
if (dev) {
dev->resetChangedFlag();
dev->update(); // resend the new data
}
}
send_system_refresh();
nav.pop();
};
button_reset.on_select = [&nav, this](Button&) {
auto dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
if (dev->reset_learned())
if (dev && dev->reset_learned())
nav.display_modal("Reset", "Battery parameters reset");
else
nav.display_modal("Error", "Error parameter reset");
};
button_help_cap.on_select = [&nav, this](Button&) {
nav.display_modal("Battery Capacity",
"Only change default, if you\n"
" changed the battery!\n"
"Defaults:\n"
"H4 + Hackrf One: 2500\n"
"H4 + Hackrf Pro: 2000\n"
"H4Pro + Hackrf Pro: custom\n"
"PortaRf: 3000\n"
);
};
checkbox_overridebatt.set_value(pmem::ui_override_batt_calc());
checkbox_battery_charge_hint.set_value(pmem::ui_battery_charge_hint());
checkbox_battery_replaceable.set_value(pmem::battery_replaceable());
field_battcap.set_value(pmem::battery_cap_mah());
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
@@ -1091,6 +1226,46 @@ void SetBatteryView::focus() {
button_cancel.focus();
}
/* SetSlpash *********************************************/
SetSplash::SetSplash(NavigationView& nav) {
add_children({&checkbox_showsplash,
&checkbox_randomsplash,
&message,
&button_picture_select,
&button_save,
&button_cancel});
checkbox_showsplash.set_value(pmem::config_splash());
splash_bmp_exists = file_exists(splash_dot_bmp);
checkbox_randomsplash.set_value(!splash_bmp_exists);
message.hidden(splash_bmp_exists);
checkbox_randomsplash.on_select = [this](Checkbox&, bool v) {
random_enabled = v;
};
button_picture_select.on_select = [this, &nav](Button&) {
auto ret = nav.push<FileManagerView>();
ret->push_dir(splash_dir);
};
button_save.on_select = [&nav, this](Button&) {
if (random_enabled == true) delete_file(splash_dot_bmp);
pmem::set_config_splash(checkbox_showsplash.value());
send_system_refresh();
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetSplash::focus() {
button_save.focus();
}
/* SettingsMenuView **************************************/
SettingsMenuView::SettingsMenuView(NavigationView& nav)
@@ -1099,9 +1274,12 @@ SettingsMenuView::SettingsMenuView(NavigationView& nav)
}
void SettingsMenuView::on_populate() {
if (pmem::show_gui_return_icon()) {
const bool return_icon = pmem::show_gui_return_icon();
if (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>(); }},
@@ -1111,18 +1289,22 @@ void SettingsMenuView::on_populate() {
{"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>(); }},
{"Button Speed", ui::Color::dark_cyan(), &bitmap_icon_controls, [this]() { nav_.push<SetButtonsView>(); }},
{"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>(); }},
{"TX Limit", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetTXLimitView>(); }},
{"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>(); }},
{"Splash settings", ui::Color::dark_cyan(), &bitmap_icon_file_image, [this]() { nav_.push<SetSplash>(); }},
});
if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_batt_icon, [this]() { nav_.push<SetBatteryView>(); }});
add_external_items(nav_, app_location_t::SETTINGS, *this, return_icon ? 1 : 0);
}
} /* namespace ui */
+316 -161
View File
@@ -5,7 +5,7 @@
* Copyright (C) 2023 Kyle Reed
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
* Copyleft (ɔ) 2024 zxkmm under GPL license
* copyleft 2024 zxkmm
*
* This file is part of PortaPack.
*
@@ -34,6 +34,7 @@
#include "ui_navigation.hpp"
#include "bitmap.hpp"
#include "ff.h"
#include "sd_card.hpp"
#include "portapack_persistent_memory.hpp"
#include "irq_controls.hpp"
@@ -72,8 +73,8 @@ class SetDateTimeView : public View {
{{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}};
{{UI_POS_X(0), 12 * 16}, "Start: 0:00 on Nth DDD in", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 13 * 16}, "End: 1:00 on Nth DDD in", Theme::getInstance()->fg_light->foreground}};
NumberField field_year{
{1 * 8, 6 * 16},
@@ -170,10 +171,10 @@ class SetDateTimeView : public View {
{}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Cancel"};
void form_init(const SetDateTimeModel& model);
@@ -253,10 +254,10 @@ class SetRadioView : public View {
"Disable external TCXO"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Cancel",
};
@@ -264,6 +265,51 @@ class SetRadioView : public View {
SetFrequencyCorrectionModel form_collect();
};
class SetTXLimitView : public View {
public:
SetTXLimitView(NavigationView& nav);
void focus() override;
std::string title() const override { return "TX Limit"; };
private:
Labels labels{
{{1 * 8, 1 * 16}, "Limits RF TX Gain", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "(This may affect", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "all applications.)", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 12 * 16}, "TX Max Gain:", Theme::getInstance()->fg_light->foreground},
};
Checkbox tx_disable_switch{
{1 * 8, 6 * 16},
23,
"Disable TX"};
Checkbox tx_amp_disable_switch{
{1 * 8, 8 * 16},
23,
"Disable TX Amp"};
NumberField tx_gain_max_db{
{20 * 8, 12 * 16},
6,
{0, 47},
1,
' ',
};
Button button_save{
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Save"};
Button button_cancel{
{UI_POS_X_CENTER(16) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Cancel",
};
};
using portapack::persistent_memory::backlight_timeout_t;
class SetUIView : public View {
@@ -298,81 +344,80 @@ class SetUIView : public View {
{"1 hour", backlight_timeout_t::Timeout3600Sec},
}};
Checkbox checkbox_showsplash{
{3 * 8, 6 * 16},
20,
"Show splash"};
Checkbox checkbox_showclock{
{3 * 8, 8 * 16},
{3 * 8, 6 * 16},
20,
"Show clock with:"};
OptionsField options_clockformat{
{60, 9 * 16 + 8},
{60, 7 * 16 + 8},
20,
{{"time only", 0},
{"time and date", 1}}};
Checkbox checkbox_guireturnflag{
{3 * 8, 11 * 16},
{3 * 8, 9 * 16},
20,
"Back button in menu"};
Labels labels{
{{3 * 8, 13 * 16}, "Show/Hide Status Icons", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 11 * 16}, "Show/Hide Status Icons", Theme::getInstance()->fg_light->foreground},
};
ImageToggle toggle_camera{
{2 * 8, 14 * 16 + 2, 16, 16},
{2 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_camera};
ImageToggle toggle_sleep{
{4 * 8, 14 * 16 + 2, 16, 16},
{4 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_sleep};
ImageToggle toggle_stealth{
{6 * 8, 14 * 16 + 2, 16, 16},
{6 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_stealth};
ImageToggle toggle_converter{
{8 * 8, 14 * 16 + 2, 16, 16},
{8 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_upconvert};
ImageToggle toggle_bias_tee{
{10 * 8, 14 * 16 + 2, 16, 16},
{10 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_biast_off};
ImageToggle toggle_clock{
{12 * 8, 14 * 16 + 2, 8, 16},
{12 * 8, 12 * 16 + 2, 8, 16},
&bitmap_icon_clk_ext};
ImageToggle toggle_mute{
{13 * 8, 14 * 16 + 2, 16, 16},
{13 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_speaker_and_headphones_mute};
ImageToggle toggle_speaker{
{15 * 8, 14 * 16 + 2, 16, 16},
{15 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_speaker_mute};
ImageToggle toggle_battery_icon{
{17 * 8, 14 * 16 + 2, 16, 16},
{17 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_batt_icon};
ImageToggle toggle_battery_text{
{19 * 8, 14 * 16 + 2, 16, 16},
{19 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_batt_text};
ImageToggle toggle_fake_brightness{
{21 * 8, 12 * 16 + 2, 16, 16},
&bitmap_icon_brightness};
ImageToggle toggle_sd_card{
{21 * 8, 14 * 16 + 2, 16, 16},
{23 * 8, 12 * 16 + 2, 16, 16},
&bitmap_sd_card_ok};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(16) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Cancel"};
};
@@ -380,36 +425,60 @@ class SetSDCardView : public View {
public:
SetSDCardView(NavigationView& nav);
void on_show() override;
void on_hide() override;
void focus() override;
std::string title() const override { return "SD Card"; };
private:
SignalToken sd_card_status_signal_token{};
// Status section (top half)
Labels status_labels{
{{10, 24}, "Card Status:", Theme::getInstance()->fg_light->foreground},
{{10, 48}, "Filesystem:", Theme::getInstance()->fg_light->foreground}};
Text text_card_status{
{120, 24, 110, 16},
""};
Text text_filesystem_type{
{120, 48, 110, 16},
""};
Button button_more_info{
{UI_POS_X_CENTER(20), 90, UI_POS_WIDTH(20), UI_POS_HEIGHT(2)},
"More Info"};
// Settings section (bottom half)
Labels labels{
// 01234567890123456789012345678
{{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}};
{{UI_POS_X_CENTER(26), 140}, " HIGH SPEED SDCARD IO ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(26), 156}, " May or may not work !! ", Theme::getInstance()->fg_light->foreground}};
Checkbox checkbox_sdcard_speed{
{2 * 8, 120},
{UI_POS_X_CENTER(26), 180},
20,
"enable high speed IO"};
Button button_test_sdcard_high_speed{
{2 * 8, 152, 27 * 8, 32},
{UI_POS_X_CENTER(27), 210, UI_POS_WIDTH(27), UI_POS_HEIGHT(2)},
"TEST BUTTON (NO PMEM SAVE)"};
Text text_sdcard_test_status{
{2 * 8, 198, 28 * 8, 16},
{UI_POS_X_CENTER(28), 256, UI_POS_WIDTH(28), UI_POS_HEIGHT(1)},
""};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Cancel"};
void update_sd_card_status();
};
class SetConverterSettingsView : public View {
@@ -451,7 +520,7 @@ class SetConverterSettingsView : public View {
{8 * 8, 10 * 16}};
Button button_return{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Return",
};
};
@@ -494,7 +563,7 @@ class SetFrequencyCorrectionView : public View {
{8 * 8, 10 * 16}};
Button button_return{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12), UI_POS_Y_BOTTOM(4), 12 * 8, UI_POS_HEIGHT(2)},
"Return",
};
};
@@ -532,44 +601,16 @@ class SetAudioView : public View {
"Beep on RX packets"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
};
class SetQRCodeView : public View {
public:
SetQRCodeView(NavigationView& nav);
void focus() override;
std::string title() const override { return "QR Code"; };
private:
Labels labels{
{{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{
{3 * 8, 4 * 16},
20,
"Show large QR code"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Cancel",
};
};
using portapack::persistent_memory::encoder_dial_direction;
using portapack::persistent_memory::encoder_dial_sensitivity;
using portapack::persistent_memory::encoder_rate_multiplier;
@@ -583,36 +624,101 @@ class SetEncoderDialView : public View {
private:
Labels labels{
{{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},
{{UI_POS_X(0), UI_POS_Y(0)}, "Sensitivity to dial rotation", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 1 * 16}, "position (x steps per 360):", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "Sensitivity:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 7 * 16}, "Rotation rate (default 1", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 8 * 16}, "means no rate dependency):", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 16}, "Rate multiplier:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 14 * 16}, "Direction:", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_encoder_dial_sensitivity{
{20 * 8, 5 * 16},
{20 * 8, 3 * 16},
6,
{{"LOW", encoder_dial_sensitivity::DIAL_SENSITIVITY_LOW},
{"NORMAL", encoder_dial_sensitivity::DIAL_SENSITIVITY_NORMAL},
{"HIGH", encoder_dial_sensitivity::DIAL_SENSITIVITY_HIGH}}};
NumberField field_encoder_rate_multiplier{
{20 * 8, 12 * 16},
{20 * 8, 10 * 16},
2,
{1, 15},
1,
' '};
OptionsField field_encoder_dial_direction{
{18 * 8, 14 * 16},
7,
{{"NORMAL", false},
{"REVERSE", true}}};
Button button_dial_sensitivity_plus{
{20 * 8, 2 * 16, 16, 16},
"+"};
Button button_dial_sensitivity_minus{
{20 * 8, 4 * 16, 16, 16},
"-"};
Button button_rate_multiplier_plus{
{20 * 8, 9 * 16, 16, 16},
"+"};
Button button_rate_multiplier_minus{
{20 * 8, 11 * 16, 16, 16},
"-"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Cancel",
};
};
class SetButtonsView : public View {
public:
SetButtonsView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Button Speed"; };
private:
Labels labels{
{{1 * 8, 1 * 16}, "Adjusts response time when a", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "button is held down.", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Repeat delay:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 16}, "Repeat speed:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 9 * 16}, "Long press delay:", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_repeat_delay{
{20 * 8, 5 * 16},
6,
{{"NORMAL", false},
{"FAST", true}}};
OptionsField field_repeat_speed{
{20 * 8, 7 * 16},
6,
{{"NORMAL", false},
{"FAST", true}}};
OptionsField field_long_press_delay{
{20 * 8, 9 * 16},
6,
{{"NORMAL", false},
{"FAST", true}}};
Button button_save{
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Save"};
Button button_cancel{
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Cancel",
};
};
@@ -633,7 +739,7 @@ class SetPersistentMemoryView : public View {
};
Text text_pmem_status{
{1 * 8, 4 * 16 + 8, 28 * 8, 16},
{UI_POS_X_CENTER(28), 4 * 16 + 8, 28 * 8, 16},
""};
Checkbox check_use_sdcard_for_pmem{
@@ -642,19 +748,19 @@ class SetPersistentMemoryView : public View {
"Use SD card for P.Mem"};
Button button_save_mem_to_file{
{1 * 8, 8 * 16, 28 * 8, 2 * 16},
{UI_POS_X_CENTER(28), 8 * 16, 28 * 8, 2 * 16},
"Save P.Mem to SD card"};
Button button_load_mem_from_file{
{1 * 8, 10 * 16 + 2, 28 * 8, 2 * 16},
{UI_POS_X_CENTER(28), 10 * 16 + 2, 28 * 8, 2 * 16},
"Load P.Mem from SD Card"};
Button button_load_mem_defaults{
{1 * 8, 12 * 16 + 4, 28 * 8, 2 * 16},
{UI_POS_X_CENTER(28), 12 * 16 + 4, 28 * 8, 2 * 16},
"Reset P.Mem to defaults"};
Button button_return{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Return",
};
};
@@ -672,7 +778,7 @@ class AppSettingsView : public View {
{{0, 4}, "Select file to edit:", Theme::getInstance()->bg_darkest->foreground}};
MenuView menu_view{
{0, 2 * 8, 240, 26 * 8},
{0, 2 * 8, screen_width, UI_POS_HEIGHT_REMAINING(3)},
true};
};
@@ -697,14 +803,15 @@ class SetConfigModeView : public View {
"Config Mode enable"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), 12 * 8, 32},
"Cancel",
};
};
using portapack::persistent_memory::fake_brightness_level_options;
class SetDisplayView : public View {
public:
@@ -715,17 +822,38 @@ class SetDisplayView : public View {
std::string title() const override { return "Display"; };
private:
Checkbox checkbox_invert_switch{
{1 * 8, 2 * 16},
Labels labels{
{{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},
{{2 * 8, 10 * 16}, "REBOOT TO APPLY SCREEN TYPE", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_fake_brightness{
{20 * 8, 8 * 16},
6,
{{"12.5%", fake_brightness_level_options::BRIGHTNESS_12p5},
{"25%", fake_brightness_level_options::BRIGHTNESS_25},
{"50%", fake_brightness_level_options::BRIGHTNESS_50}}};
Checkbox checkbox_brightness_switch{
{1 * 8, 5 * 16},
16,
"Enable brightness adjust"};
Checkbox checkbox_ips_screen_switch{
{1 * 8, 12 * 16},
23,
"Invert colors (For IPS)"};
"IPS Screen"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(16) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Cancel",
};
};
@@ -778,19 +906,19 @@ class SetTouchscreenThresholdView : public View {
};
Button button_autodetect{
{2 * 8, 13 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), 13 * 16, 12 * 8, 32},
"Auto Detect"};
Button button_reset{
{16 * 8, 13 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), 13 * 16, 12 * 8, 32},
"Reset",
};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), 16 * 16, 12 * 8, 32},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), 16 * 16, 12 * 8, 32},
"Cancel",
};
@@ -813,14 +941,14 @@ class SetMenuColorView : public View {
void paint_sample();
Labels labels{
{{3 * 8, 1 * 16}, "Menu Button Color Scheme", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(25), 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{
{8 * 8, 4 * 16, 14 * 8, 3 * 16},
{UI_POS_X_CENTER(14), 4 * 16, 14 * 8, 3 * 16},
"New Color",
&bitmap_icon_brightness,
};
@@ -850,61 +978,20 @@ class SetMenuColorView : public View {
};
Button button_reset{
{2 * 8, 13 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Reset",
};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Cancel",
};
};
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{
{0 * 8, 4 * 16},
screen_width / 8,
{},
true};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
Button button_reset{
{2 * 8, 6 * 16, screen_width - 4 * 8, 32},
"Reset"};
};
class SetThemeView : public View {
public:
SetThemeView(NavigationView& nav);
@@ -919,19 +1006,16 @@ class SetThemeView : public View {
{{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{
{0 * 8, 4 * 16},
screen_width / 8,
{UI_POS_X(0), 4 * 16},
(size_t)(screen_width / 8),
{
{"Default - Grey", 0},
{"Yellow", 1},
{"Aqua", 2},
{"Green", 3},
{"Red", 4},
{"Dark", 5},
},
true};
@@ -940,8 +1024,12 @@ class SetThemeView : public View {
23,
"Set Menu color too"};
Button button_save{
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Cancel",
};
};
@@ -957,26 +1045,55 @@ class SetBatteryView : public View {
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"};
{{UI_POS_X_CENTER(26), UI_POS_Y(0)}, "Override batt calculation", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(24), UI_POS_Y(1)}, "method to voltage based", Theme::getInstance()->fg_light->foreground},
/**/
{{UI_POS_X_CENTER(29), UI_POS_Y(4)}, "Display a hint to remind you", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(16), UI_POS_Y(5)}, "when you charge", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X_CENTER(17), UI_POS_Y(8)}, "Battery capacity", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(7), UI_POS_Y(9)}, "mAh", Theme::getInstance()->fg_light->foreground}};
Labels labels2{{{UI_POS_X(1), UI_POS_Y_BOTTOM(7)}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}};
NumberField field_battcap{
{UI_POS_X(1), UI_POS_Y(9)},
5,
{BATT_18650_MIN_MAH, BATT_18650_MAX_MAH},
100,
' ',
};
Button button_help_cap{
{UI_POS_X(12), UI_POS_Y(9), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)},
"Help",
};
Checkbox checkbox_overridebatt{
{2 * 8, 4 * 16},
{UI_POS_X(2), UI_POS_Y(2)},
23,
"Override"};
Checkbox checkbox_battery_charge_hint{
{UI_POS_X(2), UI_POS_Y(6)},
23,
"Charge hint"};
Checkbox checkbox_battery_replaceable{
{UI_POS_X(2), UI_POS_Y(11)},
23,
"Replaceable battery"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Cancel",
};
Button button_save{
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Save"};
Button button_reset{
{2 * 8, 8 * 16, 12 * 8, 32},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(6), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Reset",
};
};
@@ -988,9 +1105,47 @@ class SettingsMenuView : public BtnGridView {
private:
NavigationView& nav_;
void on_populate() override;
};
class SetSplash : public View {
public:
SetSplash(NavigationView& nav);
void focus() override;
std::string title() const override { return "Splash"; };
private:
bool splash_bmp_exists = false;
bool random_enabled = false;
Checkbox checkbox_showsplash{
{3 * 8, 1 * 16},
20,
"Show splash"};
Checkbox checkbox_randomsplash{
{3 * 8, 3 * 16},
20,
"Random splash on boot"};
Text message{{UI_POS_X(1), UI_POS_Y(6), UI_POS_WIDTH(26), UI_POS_HEIGHT(1)}, "Pick image to stop random."};
Button button_picture_select{
{UI_POS_X_CENTER(8) - UI_POS_WIDTH(4), UI_POS_Y_BOTTOM(7), UI_POS_WIDTH(13), UI_POS_HEIGHT(2)},
"Select pict."};
Button button_save{
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Save"};
Button button_cancel{
{UI_POS_X_CENTER(16) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(2)},
"Cancel"};
};
} /* namespace ui */
#endif /*__UI_SETTINGS_H__*/
+2 -2
View File
@@ -57,7 +57,7 @@ void SIGFRXView::paint(Painter& painter) {
uint8_t i, xp;
// portapack::display.draw_bmp_from_bmp_hex_arr({0, 302-160}, fox_bmp);
portapack::display.fill_rectangle({0, 16, 240, 160 - 16}, Theme::getInstance()->bg_darkest->foreground);
portapack::display.fill_rectangle({0, 16, screen_width, 160 - 16}, Theme::getInstance()->bg_darkest->foreground);
for (i = 0; i < 6; i++) {
xp = sigfrx_marks[i * 3];
painter.draw_string({(ui::Coord)sigfrx_marks[(i * 3) + 1], 144 - 20}, style_white, to_string_dec_uint(sigfrx_marks[(i * 3) + 2]));
@@ -66,7 +66,7 @@ void SIGFRXView::paint(Painter& painter) {
}
void SIGFRXView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
portapack::display.fill_rectangle({0, 144, 240, 4}, Theme::getInstance()->bg_darkest->foreground);
portapack::display.fill_rectangle({0, 144, screen_width, 4}, Theme::getInstance()->bg_darkest->foreground);
uint8_t xmax = 0, imax = 0;
size_t i;
+35 -12
View File
@@ -30,11 +30,10 @@
#include "portapack.hpp"
#include <cstring>
#include <stdio.h>
#include "rtc_time.hpp"
using namespace portapack;
namespace pmem = portapack::persistent_memory;
#include "string_format.hpp"
#include "complex.hpp"
void SondeLogger::on_packet(const sonde::Packet& packet) {
@@ -54,6 +53,7 @@ SondeView::SondeView(NavigationView& nav)
&field_lna,
&field_vga,
&rssi,
&channel,
&field_volume,
&check_log,
&check_crc,
@@ -64,6 +64,8 @@ SondeView::SondeView(NavigationView& nav)
&text_frame,
&text_temp,
&text_humid,
&text_press,
&text_vspeed,
&geopos,
&button_see_qr,
&button_see_map});
@@ -106,7 +108,7 @@ SondeView::SondeView(NavigationView& nav)
logger = std::make_unique<SondeLogger>();
if (logger)
logger->append(logs_dir / u"SONDE.TXT");
logger->append(logs_dir / u"SONDE_" + to_string_timestamp(rtc_time::now()) + u".TXT");
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
@@ -154,7 +156,7 @@ void SondeView::on_packet(const sonde::Packet& packet) {
sonde_id = packet.serial_number(); // used also as tag on the geomap
text_serial.set(sonde_id);
text_timestamp.set(to_string_timestamp(packet.received_at()));
text_timestamp.set(to_string_datetime(packet.received_at(), TimeFormat::YMDHMS));
text_voltage.set(unit_auto_scale(packet.battery_voltage(), 2, 2) + "V");
@@ -162,21 +164,42 @@ void SondeView::on_packet(const sonde::Packet& packet) {
temp_humid_info = packet.get_temp_humid();
if (temp_humid_info.humid != 0) {
double decimals = abs(get_decimals(temp_humid_info.humid, 10, true));
text_humid.set(to_string_dec_int((int)temp_humid_info.humid) + "." + to_string_dec_uint(decimals, 1) + "%");
text_humid.set(to_string_decimal(temp_humid_info.humid, 1) + "%");
}
if (temp_humid_info.temp != 0) {
double decimals = abs(get_decimals(temp_humid_info.temp, 10, true));
text_temp.set(to_string_dec_int((int)temp_humid_info.temp) + "." + to_string_dec_uint(decimals, 1) + STR_DEGREES_C);
text_temp.set(to_string_decimal(temp_humid_info.temp, 1) + STR_DEGREES_C);
}
if (packet.get_pressure() != 0) {
text_press.set(to_string_decimal(packet.get_pressure(), 1) + " hPa");
}
gps_info = packet.get_GPS_data();
geopos.set_altitude(gps_info.alt);
geopos.set_lat(gps_info.lat);
geopos.set_lon(gps_info.lon);
if (last_timestamp_update_ != 0 && last_altitude_ != 0) {
// calculate speeds
float vspeed = 0;
time_t currpackettime = rtc_time::rtcToUnixUTC(packet.received_at());
int32_t time_diff = (currpackettime - last_timestamp_update_);
if (time_diff >= 10) { // update only every 10 seconds
vspeed = (static_cast<int>(gps_info.alt) - static_cast<int>(last_altitude_)) / (float)time_diff;
last_timestamp_update_ = currpackettime;
last_altitude_ = gps_info.alt;
text_vspeed.set(to_string_decimal(vspeed, 1) + " m/s");
}
} else { // save first valid packet time + altitude
last_timestamp_update_ = rtc_time::rtcToUnixUTC(packet.received_at());
last_altitude_ = geopos.altitude();
}
if (gps_info.is_valid()) { // only update when valid, to prevent flashing
geopos.set_altitude(gps_info.alt);
geopos.set_lat(gps_info.lat);
geopos.set_lon(gps_info.lon);
if (geomap_view_) {
geomap_view_->update_position(gps_info.lat, gps_info.lon, 400, gps_info.alt, 0);
}
}
if (logger && logging) {
logger->on_packet(packet);
}
+41 -26
View File
@@ -30,6 +30,7 @@
#include "ui_rssi.hpp"
#include "ui_qrcode.hpp"
#include "ui_geomap.hpp"
#include "string_format.hpp"
#include "event_m0.hpp"
@@ -94,86 +95,100 @@ class SondeView : public View {
// AudioOutput audio_output { };
Labels labels{
{{4 * 8, 2 * 16}, "Type:", Theme::getInstance()->fg_light->foreground},
{{6 * 8, 3 * 16}, "ID:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "DateTime:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(4), UI_POS_Y(2)}, "Type:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(6), UI_POS_Y(3)}, "ID:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(4)}, "DateTime:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 5 * 16}, "Vbatt:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 6 * 16}, "Frame:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 7 * 16}, "Temp:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 8 * 16}, "Humidity:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(3), UI_POS_Y(5)}, "Vbatt:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(3), UI_POS_Y(6)}, "Frame:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(4), UI_POS_Y(7)}, "Temp:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(8)}, "Humidity:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(9)}, "Pressure:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(2), UI_POS_Y(10)}, "VSpeed:", Theme::getInstance()->fg_light->foreground}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{UI_POS_X(13), UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{UI_POS_X(15), UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{UI_POS_X(18), UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH_REMAINING(24), 4}};
Channel channel{
{UI_POS_X(21), UI_POS_Y(0) + 5, UI_POS_WIDTH_REMAINING(24), 4},
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
Checkbox check_log{
{22 * 8, 8 * 16},
{UI_POS_X_RIGHT(8), UI_POS_Y(8)},
3,
"Log"};
Checkbox check_crc{
{22 * 8, 10 * 16},
{UI_POS_X_RIGHT(8), UI_POS_Y(10)},
3,
"CRC"};
Text text_signature{
{9 * 8, 2 * 16, 10 * 8, 16},
{UI_POS_X(9), UI_POS_Y(2), UI_POS_WIDTH_REMAINING(10), UI_POS_HEIGHT(1)},
"..."};
Text text_serial{
{9 * 8, 3 * 16, 11 * 8, 16},
{UI_POS_X(9), UI_POS_Y(3), UI_POS_WIDTH_REMAINING(10), UI_POS_HEIGHT(1)},
"..."};
Text text_timestamp{
{9 * 8, 4 * 16, 11 * 8, 16},
{UI_POS_X(9), UI_POS_Y(4), UI_POS_WIDTH_REMAINING(9), UI_POS_HEIGHT(1)},
"..."};
Text text_voltage{
{9 * 8, 5 * 16, 10 * 8, 16},
{UI_POS_X(9), UI_POS_Y(5), UI_POS_WIDTH(10), UI_POS_HEIGHT(1)},
"..."};
Text text_frame{
{9 * 8, 6 * 16, 10 * 8, 16},
{UI_POS_X(9), UI_POS_Y(6), UI_POS_WIDTH(10), UI_POS_HEIGHT(1)},
"..."};
Text text_temp{
{9 * 8, 7 * 16, 10 * 8, 16},
{UI_POS_X(9), UI_POS_Y(7), UI_POS_WIDTH(10), UI_POS_HEIGHT(1)},
"..."};
Text text_humid{
{9 * 8, 8 * 16, 10 * 8, 16},
{UI_POS_X(9), UI_POS_Y(8), UI_POS_WIDTH(10), UI_POS_HEIGHT(1)},
"..."};
Text text_press{
{UI_POS_X(9), UI_POS_Y(9), UI_POS_WIDTH(10), UI_POS_HEIGHT(1)},
"..."};
Text text_vspeed{
{UI_POS_X(9), UI_POS_Y(10), UI_POS_WIDTH(10), UI_POS_HEIGHT(1)},
"..."};
GeoPos geopos{
{0, 12 * 16},
{UI_POS_X(0), UI_POS_Y(12)},
GeoPos::alt_unit::METERS,
GeoPos::spd_unit::HIDDEN};
Button button_see_qr{
{2 * 8, 15 * 16, 12 * 8, 3 * 16},
{UI_POS_X_CENTER(12) - UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(3)},
"See QR"};
Button button_see_map{
{16 * 8, 15 * 16, 12 * 8, 3 * 16},
{UI_POS_X_CENTER(12) + UI_POS_WIDTH(8), UI_POS_Y_BOTTOM(4), UI_POS_WIDTH(12), UI_POS_HEIGHT(3)},
"See on map"};
GeoMapView* geomap_view_{nullptr};
time_t last_timestamp_update_{0};
uint32_t last_altitude_{0};
MessageHandlerRegistration message_handler_packet{
Message::ID::SondePacket,
+2 -5
View File
@@ -27,8 +27,6 @@ namespace fs = std::filesystem;
namespace ui {
const std::filesystem::path splash_dot_bmp{u"/splash.bmp"};
ScreenshotViewer::ScreenshotViewer(
NavigationView& nav,
const std::filesystem::path& path)
@@ -44,7 +42,6 @@ bool ScreenshotViewer::on_key(KeyEvent) {
void ScreenshotViewer::paint(Painter& painter) {
File file{};
painter.fill_rectangle({0, 0, screen_width, screen_height}, Color::black());
auto show_invalid = [&]() {
@@ -58,7 +55,7 @@ void ScreenshotViewer::paint(Painter& painter) {
}
// Screenshots from PNGWriter are all this size.
if (file.size() != 232383) {
if ((file.size() != 232383 && screen_width == 240) || (screen_width == 320 && file.size() != 463743)) {
show_invalid();
return;
}
@@ -66,7 +63,7 @@ void ScreenshotViewer::paint(Painter& painter) {
constexpr size_t read_chunk = 80; // NB: must be a factor of pixel_width.
constexpr size_t buffer_size = sizeof(ColorRGB888) * read_chunk;
uint8_t buffer[buffer_size];
std::array<Color, screen_width> pixel_data;
std::vector<Color> pixel_data(screen_width);
// Seek past all the headers.
file.seek(43);
+1 -3
View File
@@ -28,11 +28,10 @@
#include "ui_painter.hpp"
#include "ui_widget.hpp"
#include "file.hpp"
#include "file_path.hpp"
namespace ui {
extern const std::filesystem::path splash_dot_bmp;
class ScreenshotViewer : public View {
public:
ScreenshotViewer(NavigationView& nav, const std::filesystem::path& path);
@@ -49,7 +48,6 @@ class SplashViewer : public View {
SplashViewer(NavigationView& nav, const std::filesystem::path& path);
bool on_key(KeyEvent key) override;
void paint(Painter& painter) override;
void update_ss(void);
private:
NavigationView& nav_;
+144 -26
View File
@@ -27,11 +27,14 @@
#include "ui_font_fixed_5x8.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "portapack.hpp"
#include "file.hpp"
namespace ui {
void create_thread(int32_t (*fn)(void*), void* arg, size_t stack_size, int priority) {
// TODO: collect memory on terminate, once this is used
// TODO: collect memory on terminate, once this is used. This is a HUGE TODO! need to call chThdWait on all of them!
chThdCreateFromHeap(NULL, stack_size, priority, fn, arg);
}
@@ -93,34 +96,146 @@ bool i2c_read(uint8_t* cmd, size_t cmd_len, uint8_t* data, size_t data_len) {
return dev->i2c_read(cmd, cmd_len, data, data_len);
}
// v3
// Version 3
FRESULT ext_f_open(FIL* fp, const TCHAR* path, BYTE mode) {
return f_open(fp, path, mode);
}
FRESULT ext_f_close(FIL* fp) {
return f_close(fp);
}
FRESULT ext_f_read(FIL* fp, void* buff, UINT btr, UINT* br) {
return f_read(fp, buff, btr, br);
}
FRESULT ext_f_write(FIL* fp, const void* buff, UINT btw, UINT* bw) {
return f_write(fp, buff, btw, bw);
}
FRESULT ext_f_lseek(FIL* fp, FSIZE_t ofs) {
return f_lseek(fp, ofs);
}
FRESULT ext_f_truncate(FIL* fp) {
return f_truncate(fp);
}
FRESULT ext_f_sync(FIL* fp) {
return f_sync(fp);
}
FRESULT ext_f_opendir(DIR* dp, const TCHAR* path) {
return f_opendir(dp, path);
}
FRESULT ext_f_closedir(DIR* dp) {
return f_closedir(dp);
}
FRESULT ext_f_readdir(DIR* dp, FILINFO* fno) {
return f_readdir(dp, fno);
}
FRESULT ext_f_findfirst(DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern) {
return f_findfirst(dp, fno, path, pattern);
}
FRESULT ext_f_findnext(DIR* dp, FILINFO* fno) {
return f_findnext(dp, fno);
}
FRESULT ext_f_mkdir(const TCHAR* path) {
return f_mkdir(path);
}
FRESULT ext_f_unlink(const TCHAR* path) {
return f_unlink(path);
}
FRESULT ext_f_rename(const TCHAR* path_old, const TCHAR* path_new) {
return f_rename(path_old, path_new);
}
FRESULT ext_f_stat(const TCHAR* path, FILINFO* fno) {
return f_stat(path, fno);
}
FRESULT ext_f_utime(const TCHAR* path, const FILINFO* fno) {
return f_utime(path, fno);
}
FRESULT ext_f_getfree(const TCHAR* path, DWORD* nclst, FATFS** fatfs) {
return f_getfree(path, nclst, fatfs);
}
FRESULT ext_f_mount(FATFS* fs, const TCHAR* path, BYTE opt) {
return f_mount(fs, path, opt);
}
int ext_f_putc(TCHAR c, FIL* fp) {
return f_putc(c, fp);
}
int ext_f_puts(const TCHAR* str, FIL* cp) {
return f_puts(str, cp);
}
int ext_f_printf(FIL* fp, const TCHAR* str, ...) {
return f_printf(fp, str);
}
TCHAR* ext_f_gets(TCHAR* buff, int len, FIL* fp) {
return f_gets(buff, len, fp);
}
void ext_draw_pixels(const ui::Rect r, const ui::Color* const colors, const size_t count) {
portapack::display.draw_pixels(r, colors, count);
}
void ext_draw_pixel(const ui::Point p, const ui::Color color) {
portapack::display.draw_pixel(p, color);
}
StandaloneView* standaloneView = nullptr;
void exit_app() {
if (standaloneView) standaloneView->exit();
}
void set_dirty() {
if (standaloneView != nullptr)
standaloneView->set_dirty();
}
standalone_application_api_t api = {
/* .malloc = */ &alloc,
/* .calloc = */ &calloc,
/* .realloc = */ &realloc,
/* .free = */ &chHeapFree,
/* .create_thread = */ &create_thread,
/* .fill_rectangle = */ &fill_rectangle,
/* .swizzled_switches = */ &swizzled_switches,
/* .get_switches_state = */ &get_switches_state_ulong,
/* .fixed_5x8_glyph_data = */ ui::font::fixed_5x8.get_data(),
/* .fixed_8x16_glyph_data = */ ui::font::fixed_8x16.get_data(),
/* .fill_rectangle_unrolled8 = */ &fill_rectangle_unrolled8,
/* .draw_bitmap = */ &draw_bitmap,
/* .scroll_area_y = */ &scroll_area_y,
/* .scroll_set_area = */ &scroll_set_area,
/* .scroll_disable = */ &scroll_disable,
/* .scroll_set_position = */ &scroll_set_position,
/* .scroll = */ &scroll,
/* .i2c_read = */ &i2c_read,
/* .panic = */ &chDbgPanic,
/* .set_dirty = */ &set_dirty,
.malloc = &alloc,
.calloc = &calloc,
.realloc = &realloc,
.free = &chHeapFree,
.create_thread = &create_thread,
.fill_rectangle = &fill_rectangle,
.swizzled_switches = &swizzled_switches,
.get_switches_state = &get_switches_state_ulong,
.fixed_5x8_glyph_data = ui::font::fixed_5x8.get_data(),
.fixed_8x16_glyph_data = ui::font::fixed_8x16.get_data(),
.fill_rectangle_unrolled8 = &fill_rectangle_unrolled8,
.draw_bitmap = &draw_bitmap,
.scroll_area_y = &scroll_area_y,
.scroll_set_area = &scroll_set_area,
.scroll_disable = &scroll_disable,
.scroll_set_position = &scroll_set_position,
.scroll = &scroll,
.i2c_read = &i2c_read,
.panic = &chDbgPanic,
.set_dirty = &set_dirty,
// Version 3
.f_open = &ext_f_open,
.f_close = &ext_f_close,
.f_read = &ext_f_read,
.f_write = &ext_f_write,
.f_lseek = &ext_f_lseek,
.f_truncate = &ext_f_truncate,
.f_sync = &ext_f_sync,
.f_opendir = &ext_f_opendir,
.f_closedir = &ext_f_closedir,
.f_readdir = &ext_f_readdir,
.f_findfirst = &ext_f_findfirst,
.f_findnext = &ext_f_findnext,
.f_mkdir = &ext_f_mkdir,
.f_unlink = &ext_f_unlink,
.f_rename = &ext_f_rename,
.f_stat = &ext_f_stat,
.f_utime = &ext_f_utime,
.f_getfree = &ext_f_getfree,
.f_mount = &ext_f_mount,
.f_putc = &ext_f_putc,
.f_puts = &ext_f_puts,
.f_printf = &ext_f_printf,
.f_gets = &ext_f_gets,
.draw_pixels = &ext_draw_pixels,
.draw_pixel = &ext_draw_pixel,
.exit_app = &exit_app,
// version 4
.screen_height = &screen_height,
.screen_width = &screen_width,
};
StandaloneView::StandaloneView(NavigationView& nav, uint8_t* app_image)
@@ -158,22 +273,21 @@ bool StandaloneView::on_key(const KeyEvent key) {
if (get_application_information()->header_version > 1) {
return get_application_information()->OnKeyEvent((uint8_t)key);
}
return false;
return true;
}
bool StandaloneView::on_encoder(const EncoderEvent event) {
if (get_application_information()->header_version > 1) {
return get_application_information()->OnEncoder((int32_t)event);
}
return false;
return true;
}
bool StandaloneView::on_touch(const TouchEvent event) {
if (get_application_information()->header_version > 1) {
get_application_information()->OnTouchEvent(event.point.x(), event.point.y(), (uint32_t)event.type);
return get_application_information()->OnTouchEvent(event.point.x(), event.point.y(), (uint32_t)event.type);
}
return true;
return false;
}
bool StandaloneView::on_keyboard(const KeyboardEvent event) {
@@ -202,4 +316,8 @@ void StandaloneView::on_before_detach() {
context().focus_manager().clearMirror();
}
void StandaloneView::exit() {
nav_.pop();
}
} // namespace ui
@@ -51,6 +51,8 @@ class StandaloneView : public View {
void frame_sync();
void exit();
private:
bool initialized = false;
NavigationView& nav_;
+152 -8
View File
@@ -25,6 +25,10 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "file_path.hpp"
#include "ui_textentry.hpp"
#include "../keeloq_keystore.hpp"
#include "../keeloq_file.hpp"
#include "../keeloq_common.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
@@ -56,6 +60,51 @@ void SubGhzDRecentEntryDetailView::update_data() {
if (cnt != SD_NO_CNT) console.writeln("Cnt: " + to_string_dec_uint(cnt));
if (entry_.data != 0) console.writeln("Data: " + to_string_hex(entry_.data));
if (entry_.sensorType == FPS_RESTAURANT_PAGER) {
uint8_t pager_addr = (entry_.data >> 5) & 0x0F;
uint8_t func_code = (entry_.data >> 1) & 0x0F;
console.writeln("Station: 0x" + to_string_hex(serial) + " (" + to_string_dec_uint(serial) + ")");
console.writeln("Pager: " + to_string_dec_uint(pager_addr));
if (func_code == 0x0D)
console.writeln("Action: Buzz");
else if (func_code == 0x0F)
console.writeln("Action: Sync");
else
console.writeln("Action: 0x" + to_string_hex(func_code));
}
if (entry_.sensorType == FPS_KEELOQ) {
console.writeln("Fix: " + to_string_hex(fix));
console.writeln("Encrypted: " + to_string_hex(encrypted));
console.writeln("Manufacturer: " + mf_name);
if (hop != SD_NO_HOP) {
console.writeln("Hop: " + to_string_hex(hop));
add_children({&button_save});
button_save.on_select = [this](const Button&) {
keeloq_file_buffer.clear();
text_prompt(
nav_,
keeloq_file_buffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
KeeloqData params{
mf_name,
serial,
(uint16_t)cnt,
btn};
ensure_directory(keeloq_remotes_dir);
write_keeloq_file(keeloq_remotes_dir / buffer + ".KEELOQ", params);
});
};
}
}
}
SubGhzDRecentEntryDetailView::SubGhzDRecentEntryDetailView(NavigationView& nav, const SubGhzDRecentEntry& entry)
@@ -84,6 +133,7 @@ void SubGhzDView::focus() {
SubGhzDView::SubGhzDView(NavigationView& nav)
: nav_{nav} {
add_children({&rssi,
&channel,
&field_rf_amp,
&field_lna,
&field_vga,
@@ -241,6 +291,10 @@ const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
return "GangQi";
case FPS_MARANTEC24:
return "Marantec24";
case FPS_HOLTEKHT6P20B:
return "Holtek HT6P20B";
case FPS_RESTAURANT_PAGER:
return "Rest. Pager";
case FPS_Invalid:
default:
return "Unknown";
@@ -261,17 +315,21 @@ void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
const Style& style,
RecentEntriesColumns& columns) {
std::string line{};
line.reserve(30);
line = SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry.sensorType);
line = line + " " + to_string_hex(entry.data << 32);
if (line.length() < 19) {
line += SubGhzDView::pad_string_with_spaces(19 - line.length());
if (entry.sensorType == FPS_RESTAURANT_PAGER) {
uint8_t pgr = (entry.data >> 5) & 0x0F;
uint8_t func = (entry.data >> 1) & 0x0F;
line += " P:" + to_string_dec_uint(pgr) + (func == 0x0D ? " Buzz" : func == 0x0F ? " Sync"
: "");
} else {
line = truncate(line, 19);
line = line + " " + to_string_hex(entry.data << 32);
}
line.resize(columns.at(0).second, ' ');
std::string ageStr = to_string_dec_uint(entry.age);
std::string bitsStr = to_string_dec_uint(entry.bits);
line += SubGhzDView::pad_string_with_spaces(5 - bitsStr.length()) + bitsStr;
@@ -303,6 +361,29 @@ void atomo_decrypt(uint8_t* buff) {
}
}
bool SubGhzDRecentEntryDetailView::keeloq_check_decrypt(uint32_t decrypt) {
uint16_t end_serial = serial & 0xFF;
if ((decrypt >> 28 == btn) && (((((uint16_t)(decrypt >> 16)) & 0xFF) == end_serial) ||
((((uint16_t)(decrypt >> 16)) & 0xFF) == 0))) {
cnt = decrypt & 0xFFFF;
return true;
}
return false;
}
bool SubGhzDRecentEntryDetailView::keeloq_check_decrypt_centurion(uint32_t decrypt) {
if ((decrypt >> 28 == btn) && ((((uint16_t)(decrypt >> 16)) & 0x3FF) == 0x1CE)) {
cnt = decrypt & 0xFFFF;
return true;
}
return false;
}
const uint32_t came_twee_magic_numbers_xor[15] = {
0x0E0E0E00,
0x1D1D1D11,
@@ -341,7 +422,7 @@ void SubGhzDRecentEntryDetailView::parseProtocol() {
if (entry_.sensorType == FPS_CAMEATOMO) {
entry_.data ^= 0xFFFFFFFFFFFFFFFF;
entry_.data <<= 4;
uint8_t pack[8] = {};
uint8_t pack[8];
pack[0] = (entry_.data >> 56);
pack[1] = ((entry_.data >> 48) & 0xFF);
pack[2] = ((entry_.data >> 40) & 0xFF);
@@ -591,7 +672,57 @@ void SubGhzDRecentEntryDetailView::parseProtocol() {
}
if (entry_.sensorType == FPS_KEELOQ) {
// too many sub protocol versions, skipping. maybe in future when we'll have much more fw space
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, 64);
btn = data_rev >> 60;
serial = (data_rev >> 32) & 0xFFFFFFF;
fix = data_rev >> 32;
encrypted = data_rev & 0xFFFFFFFF;
KeeloqKeystore keystore{};
const auto& keys = keystore.get_keys();
if (keys.empty()) {
return;
}
for (const auto& key : keys) {
switch (key.type) {
case KEELOQ_SIMPLE_LEARNING: {
uint32_t decrypted = keeloq_decrypt(encrypted, key.key);
if (keeloq_check_decrypt(decrypted)) {
mf_name = key.mf_name;
hop = decrypted;
return;
}
break;
}
case KEELOQ_NORMAL_LEARNING: {
uint64_t man = keeloq_normal_learning(fix, key.key);
uint32_t decrypted = keeloq_decrypt(encrypted, man);
if (key.mf_name == "Centurion" && keeloq_check_decrypt_centurion(decrypted)) {
mf_name = "Centurion";
hop = decrypted;
return;
} else if (keeloq_check_decrypt(decrypted)) {
mf_name = key.mf_name;
hop = decrypted;
return;
}
break;
}
}
}
return;
}
@@ -758,5 +889,18 @@ void SubGhzDRecentEntryDetailView::parseProtocol() {
btn = entry_.data & 0xf;
return;
}
if (entry_.sensorType == FPS_HOLTEKHT6P20B) {
serial = entry_.data >> 8;
btn = (entry_.data >> 4) & 0xF;
return;
}
if (entry_.sensorType == FPS_RESTAURANT_PAGER) {
// 25-bit EV1527-variant: [sysid:16][pager:4][func:4][stop:1]
serial = (entry_.data >> 9) & 0xFFFF; // System ID
// btn/cnt left as SD_NO values; friendly output in update_data()
return;
}
}
} // namespace ui
} // namespace ui
+37 -13
View File
@@ -25,7 +25,11 @@
#define SD_NO_SERIAL 0xFFFFFFFF
#define SD_NO_BTN 0xFF
#define SD_NO_CNT 0xFF
#define SD_NO_CNT 0xFFFFFFFF
#define SD_NO_HOP 0xFFFFFFFF
#define SD_NO_FIX 0xFFFFFFFF
#define SD_NO_ENCRYPTED 0xFFFFFFFF
#include "ui.hpp"
#include "ui_navigation.hpp"
@@ -122,15 +126,18 @@ class SubGhzDView : public View {
SubGhzDRecentEntries recent{};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{21 * 8, 0, UI_POS_WIDTH_REMAINING(24), 4}};
Channel channel{
{21 * 8, 5, UI_POS_WIDTH_REMAINING(24), 4},
};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
SignalToken signal_token_tick_second{};
@@ -149,8 +156,8 @@ class SubGhzDView : public View {
std::unique_ptr<SubGhzDLogger> logger{};
const RecentEntriesColumns columns{{
{"Type", 19},
RecentEntriesColumns columns{{
{"Type", 0},
{"Bits", 4},
{"Age", 3},
}};
@@ -188,22 +195,39 @@ class SubGhzDRecentEntryDetailView : public View {
uint32_t cnt = SD_NO_CNT;
uint32_t seed = 0;
Text text_type{{0 * 8, 1 * 16, 15 * 8, 16}, "?"};
// keeloq specific
std::string keeloq_file_buffer{};
uint32_t hop = SD_NO_HOP;
uint32_t fix = SD_NO_FIX;
uint32_t encrypted = SD_NO_ENCRYPTED;
std::string mf_name = "Unknown";
bool keeloq_check_decrypt(uint32_t decrypt);
bool keeloq_check_decrypt_centurion(uint32_t decrypt);
// end keeloq specific
Text text_type{{UI_POS_X(0), 1 * 16, 15 * 8, 16}, "?"};
Text text_id{{6 * 8, 2 * 16, 10 * 8, 16}, "?"};
Console console{
{0, 4 * 16, 240, screen_height - (4 * 16) - 36}};
{0, 4 * 16, screen_width, screen_height - (4 * 16) - 36}};
Labels labels{
{{0 * 8, 0 * 16}, "Type:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Serial: ", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Data:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(0)}, "Type:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 2 * 16}, "Serial: ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 3 * 16}, "Data:", Theme::getInstance()->fg_light->foreground},
};
Button button_done{
{screen_width - 96 - 4, screen_height - 32 - 12, 96, 32},
"Done"};
Button button_save{
{4, screen_height - 32 - 12, 96, 32},
"Save"};
void parseProtocol();
};
+2 -2
View File
@@ -99,8 +99,8 @@ void TestView::on_packet(const testapp::Packet& packet) {
display.draw_pixel(Point(cur_x, 4 * 16 + (256 - ((raw_alt - cal_value) / 4))), Color::white());
cur_x++;
if (cur_x >= 240) {
display.fill_rectangle(Rect(0, 5 * 16, 240, 256), Color::black());
if (cur_x >= screen_width) {
display.fill_rectangle(Rect(0, 5 * 16, screen_width, 256), Color::black());
cur_x = 0;
}
+7 -7
View File
@@ -76,29 +76,29 @@ class TestView : public View {
bool logging{false};
Labels labels{
{{0 * 8, 1 * 16}, "Data:", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), 1 * 16}, "Data:", Theme::getInstance()->fg_light->foreground}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
{UI_POS_X(0), 0 * 8},
nav_};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
Text text_debug_a{
{0 * 8, 4 * 16, 30 * 8, 16},
{UI_POS_X(0), 4 * 16, screen_width, 16},
"..."};
Text text_debug_b{
{0 * 8, 5 * 16, 30 * 8, 16},
{UI_POS_X(0), 5 * 16, screen_width, 16},
"..."};
Button button_cal{
+100 -21
View File
@@ -26,6 +26,7 @@
#include "log_file.hpp"
#include "string_format.hpp"
#include "irq_controls.hpp"
using namespace portapack;
namespace fs = std::filesystem;
@@ -81,21 +82,75 @@ void TextViewer::paint(Painter& painter) {
paint_cursor(painter);
}
void TextViewer::enable_long_press() {
// Enable long press on "Select".
SwitchesState config;
config[toUType(Switch::Sel)] = true;
set_switches_long_press_config(config);
}
void TextViewer::on_focus() {
enable_long_press();
}
bool TextViewer::on_key(const KeyEvent key) {
int16_t delta_col = 0;
int16_t delta_line = 0;
if (key == KeyEvent::Left)
delta_col = -1;
else if (key == KeyEvent::Right)
delta_col = 1;
else if (key == KeyEvent::Up)
delta_line = -1;
else if (key == KeyEvent::Down)
delta_line = 1;
else if (key == KeyEvent::Select && on_select) {
on_select();
return true;
if (key == KeyEvent::Select) {
// Toggle 'digit' mode with long-press.
if (digit_mode_ || key_is_long_pressed(key)) {
digit_mode_ = !digit_mode_;
set_dirty();
return true;
}
}
if (digit_mode_) {
switch (key) {
case KeyEvent::Left:
delta_col = -1;
break;
case KeyEvent::Right:
delta_col = 1;
break;
case KeyEvent::Up:
set_value(value_ == 0x0F ? 0x00 : value_ + 1);
break;
case KeyEvent::Down:
set_value(value_ == 0x00 ? 0x0F : value_ - 1);
break;
default:
return false;
}
if (delta_col == 0 && delta_line == 0) {
return true;
}
} else {
switch (key) {
case KeyEvent::Left:
delta_col = -1;
break;
case KeyEvent::Right:
delta_col = 1;
break;
case KeyEvent::Up:
delta_line = -1;
break;
case KeyEvent::Down:
delta_line = 1;
break;
case KeyEvent::Select: {
if (on_select) {
on_select();
return true;
}
break;
}
default:
return false;
}
}
// Always allow cursor direction to be updated.
@@ -111,19 +166,42 @@ bool TextViewer::on_key(const KeyEvent key) {
bool TextViewer::on_encoder(EncoderEvent delta) {
bool updated = false;
if (cursor_.dir == ScrollDirection::Horizontal)
updated = apply_scrolling_constraints(0, delta);
else {
delta *= 16;
updated = apply_scrolling_constraints(delta, 0);
if (digit_mode_) {
if (delta > 0) {
set_value(value_ == 0x0F ? 0x00 : value_ + 1);
} else if (delta < 0) {
set_value(value_ == 0x00 ? 0x0F : value_ - 1);
}
updated = true;
} else {
if (cursor_.dir == ScrollDirection::Horizontal) {
updated = apply_scrolling_constraints(0, delta);
} else {
delta *= 16;
updated = apply_scrolling_constraints(delta, 0);
}
if (updated) {
redraw();
}
}
if (updated)
redraw();
return updated;
}
void TextViewer::set_value(uint8_t new_value) {
if (new_value != value_) {
value_ = new_value;
if (on_change) {
on_change(value_);
}
set_dirty();
}
}
void TextViewer::redraw(bool redraw_text, bool redraw_marked) {
paint_state_.redraw_text = redraw_text;
paint_state_.redraw_marked = redraw_marked;
@@ -349,7 +427,7 @@ void TextViewer::set_font_zoom(bool zoom) {
/* TextEditorMenu ***************************************************/
TextEditorMenu::TextEditorMenu()
: View{{7 * 4, 9 * 4, 25 * 8, 25 * 8}} {
: View{{UI_POS_X_CENTER(25), 9 * 4, 25 * 8, 25 * 8}} {
add_children(
{
&rect_frame,
@@ -534,7 +612,7 @@ void TextEditorView::open_file(const fs::path& path) {
viewer.clear_file();
delete_temp_file(path_);
path_ = {};
path_ = "";
file_dirty_ = false;
has_temp_file_ = false;
auto result = FileWrapper::open(
@@ -617,6 +695,7 @@ void TextEditorView::show_edit_line() {
edit_line_buffer_,
viewer.col(),
max_edit_length,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
auto range = file_->line_range(viewer.line());
if (!range)
+12 -5
View File
@@ -54,10 +54,13 @@ class TextViewer : public Widget {
std::function<void()> on_select{};
std::function<void()> on_cursor_moved{};
std::function<void(uint8_t)> on_change{};
void paint(Painter& painter) override;
bool on_key(KeyEvent key) override;
bool on_encoder(EncoderEvent delta) override;
void set_value(uint8_t new_value);
void on_focus() override;
void redraw(bool redraw_text = false, bool redraw_marked = false);
@@ -74,6 +77,7 @@ class TextViewer : public Widget {
void cursor_set(uint16_t line, uint16_t col);
void cursor_mark_selected();
void cursor_clear_marked();
void enable_long_press();
typedef std::pair<uint16_t, uint16_t> LineColPair;
std::vector<LineColPair> lineColPair{};
@@ -95,6 +99,9 @@ class TextViewer : public Widget {
int8_t char_height{};
uint8_t max_line{};
uint8_t max_col{};
bool digit_mode_{false};
uint8_t value_{0};
bool allow_digit_mode_{true};
/* Returns true if the cursor was updated. */
bool apply_scrolling_constraints(
@@ -157,7 +164,7 @@ class TextEditorMenu : public View {
void hide_children(bool hidden);
Rectangle rect_frame{
{0 * 8, 0 * 8, 23 * 8, 23 * 8},
{UI_POS_X(0), 0 * 8, 23 * 8, 23 * 8},
Theme::getInstance()->fg_dark->foreground};
NewButton button_home{
@@ -260,23 +267,23 @@ class TextEditorView : public View {
TextViewer viewer{
/* 272 = screen_height - 16 (top bar) - 32 (bottom controls) */
{0, 0, screen_width, 272}};
{0, 0, screen_width, UI_POS_HEIGHT_REMAINING(4)}};
TextEditorMenu menu{};
NewButton button_menu{
{26 * 8, 34 * 8, 4 * 8, 4 * 8},
{UI_POS_X_RIGHT(4), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH(4), UI_POS_HEIGHT(2)},
{},
&bitmap_icon_controls,
Theme::getInstance()->bg_dark->background,
/*vcenter*/ true};
Text text_position{
{0 * 8, 34 * 8, 26 * 8, 2 * 8},
{UI_POS_X(0), UI_POS_Y_BOTTOM(3), UI_POS_WIDTH_REMAINING(5), UI_POS_HEIGHT(1)},
""};
Text text_size{
{0 * 8, 36 * 8, 26 * 8, 2 * 8},
{UI_POS_X(0), UI_POS_Y_BOTTOM(2), UI_POS_WIDTH_REMAINING(5), UI_POS_HEIGHT(1)},
""};
};
@@ -81,13 +81,13 @@ class TouchCalibrationView : public View {
Color::black()};
Image image_calibrate_1{
{240 - 32 - 16, (320 - 16) / 2 - 16, 32, 32},
{screen_width - 32 - 16, (screen_height - 16) / 2 - 16, 32, 32},
&bitmap_target_calibrate,
Color::white(),
Color::black()};
Image image_calibrate_2{
{240 / 2 - 16, (320 - 16) - 32 - 16, 32, 32},
{screen_width / 2 - 16, (screen_height - 16) - 32 - 16, 32, 32},
&bitmap_target_calibrate,
Color::white(),
Color::black()};
@@ -99,13 +99,13 @@ class TouchCalibrationView : public View {
Color::black()};
Image image_verify_1{
{240 - 32 - 16, (320 - 16) / 2 - 16, 32, 32},
{screen_width - 32 - 16, (screen_height - 16) / 2 - 16, 32, 32},
&bitmap_target_verify,
Color::white(),
Color::black()};
Image image_verify_2{
{240 / 2 - 16, (320 - 16) - 32 - 16, 32, 32},
{screen_width / 2 - 16, (screen_height - 16) - 32 - 16, 32, 32},
&bitmap_target_verify,
Color::white(),
Color::black()};
@@ -131,11 +131,11 @@ class TouchCalibrationView : public View {
"Calibration failed. Retry?"};
Button button_cancel{
{40, 200, 64, 24},
{40, 200, UI_POS_WIDTH(8), 24},
"Cancel"};
Button button_ok{
{136, 200, 64, 24},
{136, 200, UI_POS_WIDTH(8), 24},
"OK"};
void on_frame_sync();
+4 -4
View File
@@ -162,16 +162,16 @@ class TouchTunesView : public View {
{{18 * 8 + 4, 18 * 8}, "V"},
{{22 * 8, 18 * 8}, "F4"},
{{0 * 8, 5 * 8}, "1"},
{{UI_POS_X(0), 5 * 8}, "1"},
{{4 * 8, 5 * 8}, "2"},
{{8 * 8, 5 * 8}, "3"},
{{0 * 8, 10 * 8}, "4"},
{{UI_POS_X(0), 10 * 8}, "4"},
{{4 * 8, 10 * 8}, "5"},
{{8 * 8, 10 * 8}, "6"},
{{0 * 8, 15 * 8}, "7"},
{{UI_POS_X(0), 15 * 8}, "7"},
{{4 * 8, 15 * 8}, "8"},
{{8 * 8, 15 * 8}, "9"},
{{0 * 8, 20 * 8}, "*"},
{{UI_POS_X(0), 20 * 8}, "*"},
{{4 * 8, 20 * 8}, "0"},
{{8 * 8, 20 * 8}, "#"},
+40 -12
View File
@@ -107,6 +107,7 @@ void WeatherView::focus() {
WeatherView::WeatherView(NavigationView& nav)
: nav_{nav} {
add_children({&rssi,
&channel,
&field_rf_amp,
&field_lna,
&field_vga,
@@ -196,6 +197,10 @@ WeatherView::~WeatherView() {
baseband::shutdown();
}
uint16_t WeatherView::bcd_decode_short(uint32_t data) {
return (data & 0xF) * 10 + ((data >> 4) & 0xF);
}
const char* WeatherView::getWeatherSensorTypeName(FPROTO_WEATHER_SENSOR type) {
switch (type) {
case FPW_NexusTH:
@@ -224,6 +229,8 @@ const char* WeatherView::getWeatherSensorTypeName(FPROTO_WEATHER_SENSOR type) {
return "LaCrosse TX141THBv2";
case FPW_OREGON2:
return "Oregon2";
case FPW_OREGON2B:
return "Oregon2B";
case FPW_OREGON3:
return "Oregon3";
case FPW_OREGONv1:
@@ -269,16 +276,13 @@ void RecentEntriesTable<ui::WeatherRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
const Style& style,
RecentEntriesColumns& columns) {
std::string line{};
line.reserve(30);
line = WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)entry.sensorType);
if (line.length() < 10) {
line += WeatherView::pad_string_with_spaces(10 - line.length());
} else {
line = truncate(line, 10);
}
line.resize(columns.at(0).second, ' ');
std::string temp = (weather_units_fahr ? to_string_decimal((entry.temp * 9 / 5) + 32, 1) : to_string_decimal(entry.temp, 1));
std::string humStr = (entry.humidity != WS_NO_HUMIDITY) ? to_string_dec_uint(entry.humidity) + "%" : "-";
@@ -301,7 +305,9 @@ WeatherRecentEntry WeatherView::process_data(const WeatherDataMessage* data) {
WeatherRecentEntry ret = {};
ret.sensorType = data->sensorType;
int16_t i16 = 0;
int32_t i32 = 0;
uint16_t u16 = 0;
uint64_t u64 = 0;
uint8_t u8 = 0;
uint8_t channel_3021[] = {3, 0, 2, 1};
@@ -444,12 +450,34 @@ WeatherRecentEntry WeatherView::process_data(const WeatherDataMessage* data) {
ret.humidity = (data->decode_data >> 8) & 0xFF;
break;
case FPW_OREGON2:
i16 = ((data->decode_data >> 4) & 0xF) * 10 + (((data->decode_data >> 4) >> 4) & 0xF);
i16 *= 10;
i16 += (data->decode_data >> 12) & 0xF;
if (data->decode_data & 0xF) i16 = -i16;
ret.temp = (float)i16 / 10.0;
// todo fix missing parts.
i32 = bcd_decode_short(data->decode_data >> 4);
i32 *= 10;
i32 += (data->decode_data >> 12) & 0xf;
if (data->decode_data & 0xF) i32 = -i32;
ret.temp = (float)i32 / 10.0;
ret.battery_low = ((data->decode_data >> 32) & 0x4) ? 1 : 0;
u8 = (data->decode_data >> 44) & 0xF;
ret.channel = 1;
while (u8 > 1) {
ret.channel++;
u8 >>= 1;
}
break;
case FPW_OREGON2B:
ret.humidity = bcd_decode_short(data->decode_data);
u64 = data->decode_data >> 8;
i32 = bcd_decode_short(u64 >> 4);
i32 *= 10;
i32 += (u64 >> 12) & 0xf;
if (u64 & 0xF) i32 = -i32;
ret.temp = (float)i32 / 10.0;
ret.battery_low = ((data->decode_data >> 32) & 0x4) ? 1 : 0;
u8 = (data->decode_data >> 44) & 0xF;
ret.channel = 1;
while (u8 > 1) {
ret.channel++;
u8 >>= 1;
}
break;
case FPW_OREGON3:
// todo check
+21 -17
View File
@@ -114,6 +114,7 @@ class WeatherView : public View {
private:
void on_tick_second();
uint16_t bcd_decode_short(uint32_t data);
void on_data(const WeatherDataMessage* data);
WeatherRecentEntry process_data(const WeatherDataMessage* data);
NavigationView& nav_;
@@ -134,25 +135,28 @@ class WeatherView : public View {
WeatherRecentEntries recent{};
OptionsField options_temperature{
{10 * 8, 0 * 16},
{10 * 8, UI_POS_Y(0)},
2,
{{STR_DEGREES_C, 0},
{STR_DEGREES_F, 1}}};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{13 * 8, UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{15 * 8, UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{18 * 8, UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{21 * 8, 0, UI_POS_WIDTH_REMAINING(24), 4}};
Channel channel{
{21 * 8, 5, UI_POS_WIDTH_REMAINING(24), 4},
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
SignalToken signal_token_tick_second{};
@@ -171,8 +175,8 @@ class WeatherView : public View {
std::unique_ptr<WeatherLogger> logger{};
const RecentEntriesColumns columns{{
{"Type", 10},
RecentEntriesColumns columns{{
{"Type", 0},
{"Temp", 5},
{"Hum", 4},
{"Ch", 3},
@@ -215,14 +219,14 @@ class WeatherRecentEntryDetailView : public View {
Text text_age{{10 * 8, 7 * 16, 10 * 8, 16}, "?"};
Labels labels{
{{0 * 8, 0 * 16}, "Weather Station", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 1 * 16}, "Type:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Id: ", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "Temp:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Humidity:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 5 * 16}, "Channel:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 6 * 16}, "Battery:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "Age:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), UI_POS_Y(0)}, "Weather Station", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 1 * 16}, "Type:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 2 * 16}, "Id: ", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 3 * 16}, "Temp:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 4 * 16}, "Humidity:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 5 * 16}, "Channel:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 6 * 16}, "Battery:", Theme::getInstance()->fg_light->foreground},
{{UI_POS_X(0), 7 * 16}, "Age:", Theme::getInstance()->fg_light->foreground},
};
Button button_done{
+9 -2
View File
@@ -225,8 +225,15 @@ volume_range_t volume_range() {
return audio_codec->headphone_gain_range();
}
void set_volume(const volume_t volume) {
audio_codec->set_headphone_volume(volume);
bool set_volume(const volume_t volume) {
// add retry method
for (int i = 0; i < 100; ++i) {
if (audio_codec->set_headphone_volume(volume)) {
return true;
}
chThdSleepMilliseconds(2);
}
return false;
}
} /* namespace headphone */
+2 -2
View File
@@ -49,7 +49,7 @@ class Codec {
virtual void headphone_enable() = 0;
virtual void headphone_disable() = 0;
virtual volume_range_t headphone_gain_range() const = 0;
virtual void set_headphone_volume(const volume_t volume) = 0;
virtual bool set_headphone_volume(const volume_t volume) = 0;
virtual void microphone_enable(int8_t alc_mode, bool mic_to_HP_enabled) = 0; // added user-GUI AK4951 ,selected ALC mode.
virtual void microphone_disable() = 0;
@@ -93,7 +93,7 @@ namespace headphone {
volume_range_t volume_range();
void set_volume(const volume_t volume);
bool set_volume(const volume_t volume);
} /* namespace headphone */
+170 -19
View File
@@ -21,6 +21,7 @@
*/
#include "baseband_api.hpp"
#include <cstring>
#include "audio.hpp"
#include "tonesets.hpp"
@@ -52,12 +53,21 @@ static void send_message(const Message* const message) {
creg::m0apptxevent::assert_event();
if constexpr (check_for_message_hang) {
#ifdef PRALINE
/* Timeout: ~3 seconds at typical clock speeds */
auto count = 200'000'000u;
#else
auto count = UINT32_MAX;
#endif
while (shared_memory.baseband_message && --count)
/* spin */;
if (count == 0)
#ifdef PRALINE
chDbgPanic("BB Msg Timeout");
#else
chDbgPanic("Baseband Send Fail");
#endif
} else {
while (shared_memory.baseband_message)
/* spin */;
@@ -66,12 +76,13 @@ static void send_message(const Message* const message) {
void AMConfig::apply() const {
const AMConfigureMessage message{
taps_6k0_decim_0, // common FIR filter taps pre-decim_0 to all 5 x AM mod types.(AM-9K, AM-6K, USB, LSB, CW)
taps_6k0_decim_1, // common FIR filter taps pre-decim_1 to all 5 x AM mod. types.
decim_2, // var decim_2 FIR taps filter , variable values, depending selected AM mod(AM 9k / 6k all rest AM modes)
channel, // var channel FIR taps filter , variable values, depending selected AM mode, each one different (DSB-9K, DSB-6K, USB-3K, LSB-3K,CW)
modulation, // var parameter .
audio_12k_hpf_300hz_config};
taps_6k0_decim_0, // common FIR filter taps pre-decim_0 to all 6 x AM mod types.(AM-9K, AM-6K, USB, LSB, CW, AMFM-WFAX)
decim_1, // var decim_1 FIR taps filter , variable values , to handle two spectrum decim factor 1 and 2 (zoom) and more APT LPF filtered .
decim_2, // var decim_2 FIR taps filter , variable values, depending selected AM mod(AM 9k / 6k and all rest AM modes)
channel, // var channel FIR taps filter , variable values, depending selected AM mode, each one different (DSB-9K, DSB-6K, USB-3K, LSB-3K,CW,AMFM-WFAX)
modulation, // var parameter . enum class Modulation : int32_t {DSB = 0, SSB = 1, SSB_FM = 2}
audio_12k_iir_filter_config, // var parameter , 300 Hz hpf all except Wefax (1.500Hz lpf)
spectrum_decimation_factor}; // var parameter , waterfall no zoom : 1 ,for zoom x 2 : 2
send_message(&message);
audio::set_rate(audio::Rate::Hz_12000);
}
@@ -92,9 +103,9 @@ void NBFMConfig::apply(const uint8_t squelch_level) const {
void WFMConfig::apply() const {
const WFMConfigureMessage message{
decim_0, // taps_200k_decim_0 , taps_180k_wfm_decim_0, taps_40k_wfm_decim_0
decim_1, // taps_200k_decim_1 or taps_180k_wfm_decim_1, taps_40k_wfm_decim_1
taps_64_lp_156_198,
decim_0, // Dynamic array 24 taps : taps_200k_decim_0 , taps_180k_wfm_decim_0, taps_80k_wfm_decim_0
decim_1, // Dynamic array 16 taps : taps_200k_decim_1 or taps_180k_wfm_decim_1, taps_80k_wfm_decim_1
taps_64_lp_156_198, // Fixed channel audio filter 15khz
75000,
audio_48k_hpf_30hz_config,
audio_48k_deemph_2122_6_config};
@@ -102,6 +113,18 @@ void WFMConfig::apply() const {
audio::set_rate(audio::Rate::Hz_48000);
}
void WFMAMConfig::apply() const {
const WFMAMConfigureMessage message{
decim_0, // Fixed 24 taps array : taps_16k0_decim_0
decim_1, // Dynamic 32 taps array : taps_80k_wfmam_decim_1, 38k_wfmam
taps_64_lp_bpf, // Dynamic 64 taps array , to filter modulated DSB AM 2k4 carrier before demod. AM .(LPF / BPF)
17000, // NOAA satellite tx , FM deviation = +-17Khz.
apt_audio_12k_notch_2k4_config,
apt_audio_12k_lpf_2000hz_config};
send_message(&message);
audio::set_rate(audio::Rate::Hz_12000);
}
void set_tone(const uint32_t index, const uint32_t delta, const uint32_t duration) {
shared_memory.bb_data.tones_data.tone_defs[index].delta = delta;
shared_memory.bb_data.tones_data.tone_defs[index].duration = duration;
@@ -134,6 +157,19 @@ void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration) {
send_message(&message);
}
void set_sstvrx_data(const uint8_t code) {
const SSTVRXConfigureMessage message{
code};
send_message(&message);
}
void set_sstvrx_phase_slant(const int16_t phase, const int16_t slant) {
const SSTVRXPhaseSlantMessage message{
phase,
slant};
send_message(&message);
}
void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) {
const AFSKRxConfigureMessage message{
baudrate,
@@ -173,13 +209,14 @@ void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t
send_message(&message);
}
void set_fsk(const size_t deviation) {
void set_fsk(const uint8_t samplesPerSymbol, const uint32_t syncWord, const uint8_t syncWordLength, const uint32_t preamble, const uint8_t preambleLength, uint16_t numDataBytes) {
const FSKRxConfigureMessage message{
taps_200k_decim_0,
taps_16k0_decim_1,
taps_11k0_channel,
2,
deviation};
samplesPerSymbol,
syncWord,
syncWordLength,
preamble,
preambleLength,
numDataBytes};
send_message(&message);
}
@@ -274,8 +311,8 @@ void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit,
send_message(&message);
}
void set_pocsag() {
const POCSAGConfigureMessage message{};
void set_pocsag(int8_t baud_config) {
const POCSAGConfigureMessage message{baud_config};
send_message(&message);
}
@@ -298,12 +335,52 @@ void set_rds_data(const uint16_t message_length) {
send_message(&message);
}
void set_spectrum(const size_t sampling_rate, const size_t trigger) {
void set_spectrum(
const size_t sampling_rate,
const size_t trigger) {
const WidebandSpectrumConfigMessage message{
sampling_rate, trigger};
send_message(&message);
}
void set_audio_ddc_frequency(int32_t frequency) {
const AudioDDCConfigMessage message{frequency};
send_message(&message);
}
void set_time_sink(
const size_t sampling_rate,
const size_t trigger) {
const TimeSinkConfigMessage message{
sampling_rate, trigger};
send_message(&message);
}
void set_wefax_config(uint8_t lpm = 120, uint8_t ioc = 0) {
const WeFaxRxConfigureMessage message{lpm, ioc};
send_message(&message);
}
void set_noaaapt_config() {
const NoaaAptRxConfigureMessage message{};
send_message(&message);
}
void set_vor_config(bool enabled) {
const VorRxConfigureMessage message{enabled};
send_message(&message);
}
void set_vor_tx_config(uint16_t radial_deg, bool ident_enabled, const std::string& ident, bool enabled) {
const VorTxConfigureMessage message{radial_deg, ident_enabled, ident.c_str(), enabled};
send_message(&message);
}
void set_flex_config() {
const FlexConfigureMessage message{};
send_message(&message);
}
void set_siggen_tone(const uint32_t tone) {
const SigGenToneMessage message{
TONES_F2D(tone, TONES_SAMPLERATE)};
@@ -326,8 +403,77 @@ void set_subghzd_config(uint8_t modulation = 0, uint32_t sampling_rate = 0) {
send_message(&message);
}
void set_moreserx_config(uint8_t mode) {
const MorseRXConfigureMessage message{mode};
send_message(&message);
}
void set_tonedetect_config(uint8_t squelch, uint32_t ctcss_freq_x10) {
const ToneDetectConfigureMessage message{squelch, ctcss_freq_x10};
send_message(&message);
}
void set_morsetx_config(uint8_t mode, uint32_t tone, float fm_delta) {
const MorseTXConfigureMessage message{mode, tone, fm_delta};
send_message(&message);
}
void set_morsetx_key(bool key_down) {
const MorseTXkeyMessage message{key_down};
send_message(&message);
}
void set_bitstream_config(uint32_t deviation, uint8_t mode) {
const StreamTXConfigurationMessage message{deviation, mode};
send_message(&message);
}
void set_rtty_config(uint16_t baud, uint16_t shift, uint8_t* payload, uint16_t payload_length) {
RTTYDataMessage message{baud, shift};
if (payload && payload_length > 0) {
message.data_len = payload_length > message.max_len ? message.max_len : payload_length;
for (size_t i = 0; i < message.data_len; ++i) {
message.data[i] = payload[i];
}
}
send_message(&message);
}
void set_rtty_config(RTTYDataMessage& message) {
send_message(&message);
}
void set_epirb_tx_config(EPIRBTXDataMessage& message) {
send_message(&message);
}
void set_epirb_rx_config(EPIRBRXConfig& message) {
send_message(&message);
}
void set_p25tx_data(const uint8_t* dibits, uint16_t frame_length) {
const size_t max_len = sizeof(shared_memory.bb_data.data);
if (frame_length > max_len) frame_length = max_len;
memcpy(shared_memory.bb_data.data, dibits, frame_length);
P25TxConfigureMessage msg{};
msg.frame_length = frame_length;
send_message(&msg);
}
void set_hunter_config(uint32_t threshold, uint32_t hangtime_ms, bool start) {
HunterConfigMessage message{};
message.energy_threshold = threshold;
message.hangtime_ms = hangtime_ms;
message.start = start;
send_message(&message);
}
static bool baseband_image_running = false;
bool is_image_running() {
return baseband_image_running;
}
void run_image(const spi_flash::image_tag_t image_tag) {
if (baseband_image_running) {
chDbgPanic("BBRunning");
@@ -387,7 +533,12 @@ void shutdown() {
send_message(&message);
shared_memory.application_queue.reset();
// Allow time for the shutdown message to be processed and for the baseband
// core to stop before starting another image. Otherwise, the M4 may still be
// running and cause a crash when the next image is started.
#ifdef PRALINE
chThdSleepMilliseconds(20);
#endif
baseband_image_running = false;
}
+41 -5
View File
@@ -36,9 +36,12 @@
namespace baseband {
struct AMConfig {
const fir_taps_real<32> decim_2; // added to handle two types decim_2 9k, 6k
const fir_taps_real<32> decim_1; // added to handle two var LPF in AMFM to avoid aliasing when spectrum zoom factor 2.
const fir_taps_real<32> decim_2; // added to handle two var types decim_2 9k, 6k
const fir_taps_complex<64> channel;
const AMConfigureMessage::Modulation modulation;
const iir_biquad_config_t audio_12k_iir_filter_config; // added to handle two var IIR filter types : 300 hpf(as before) , 1500Hz lpf for Wefax.
const size_t spectrum_decimation_factor; // used to handle LCD AM waterfall zoom x1 / zoom x2.
void apply() const;
};
@@ -53,23 +56,33 @@ struct NBFMConfig {
};
struct WFMConfig {
const fir_taps_real<24> decim_0; // To handle both WFM filters , 200k and 40K for NOAA APT
const fir_taps_real<24> decim_0; // To handle all 3 WFM filters , 200k, 180k and 80K-
const fir_taps_real<16> decim_1;
void apply() const;
};
struct WFMAMConfig {
const fir_taps_real<24> decim_0; // To handle WFM filter BW=40K for NOAA APT
const fir_taps_real<32> decim_1;
const fir_taps_real<64> taps_64_lp_bpf; // to handle dynamically LPF / BPF .
void apply() const;
};
void set_tone(const uint32_t index, const uint32_t delta, const uint32_t duration);
void set_tones_config(const uint32_t bw, const uint32_t pre_silence, const uint16_t tone_count, const bool dual_tone, const bool audio_out);
void kill_tone();
void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration);
void set_sstvrx_data(const uint8_t code);
void set_sstvrx_phase_slant(const int16_t phase, const int16_t slant);
void set_audiotx_config(const uint32_t divider, const float deviation_hz, const float audio_gain, uint8_t audio_shift_bits_s16, uint8_t bits_per_sample, const uint32_t tone_key_delta, const bool am_enabled, const bool dsb_enabled, const bool usb_enabled, const bool lsb_enabled);
void set_fifo_data(const int8_t* data);
void set_pitch_rssi(int32_t avg, bool enabled);
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space, const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count);
void kill_afsk();
void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
void set_fsk(const size_t deviation);
void set_fsk(const uint8_t samplesPerSymbol, const uint32_t syncWord, const uint8_t syncWordLength, const uint32_t preamble, const uint8_t preambleLength, uint16_t numDataBytes);
void set_aprs(const uint32_t baudrate);
void set_btlerx(uint8_t channel_number);
@@ -80,21 +93,44 @@ void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t
void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat, const uint32_t pause_symbols, const uint8_t de_bruijn_length = 0);
void kill_ook();
void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift, const uint32_t progress_notice);
void set_pocsag();
void set_pocsag(int8_t baud_config = -1);
void set_adsb();
void set_jammer(const bool run, const jammer::JammerType type, const uint32_t speed);
void set_rds_data(const uint16_t message_length);
void set_spectrum(const size_t sampling_rate, const size_t trigger);
void set_spectrum(
const size_t sampling_rate,
const size_t trigger);
void set_audio_ddc_frequency(int32_t frequency);
void set_time_sink(
const size_t sampling_rate,
const size_t trigger);
void set_siggen_tone(const uint32_t tone);
void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t duration);
void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw);
void set_subghzd_config(uint8_t modulation, uint32_t sampling_rate);
void set_moreserx_config(uint8_t mode);
void set_tonedetect_config(uint8_t squelch, uint32_t ctcss_freq_x10 = 0);
void set_morsetx_config(uint8_t mode, uint32_t tone, float fm_delta);
void set_morsetx_key(bool key_down);
void set_wefax_config(uint8_t lpm, uint8_t ioc);
void set_noaaapt_config();
void set_vor_config(bool enabled = true);
void set_vor_tx_config(uint16_t radial_deg, bool ident_enabled = true, const std::string& ident = "", bool enabled = true);
void set_flex_config();
void set_bitstream_config(uint32_t deviation, uint8_t mode); // mode 0 for am, 1 for 2fsk
void set_rtty_config(uint16_t baud, uint16_t shift, uint8_t* payload = nullptr, uint16_t payload_length = 0); // baud*100
void set_rtty_config(RTTYDataMessage& message);
void set_epirb_tx_config(EPIRBTXDataMessage& message);
void set_epirb_rx_config(EPIRBRXConfig& message);
void set_p25tx_data(const uint8_t* dibits, uint16_t frame_length);
void set_hunter_config(uint32_t threshold, uint32_t hangtime_ms, bool start);
void request_roger_beep();
void request_rssi_beep();
void request_beep_stop();
void request_audio_beep(uint32_t freq, uint32_t sample_rate, uint32_t duration_ms);
bool is_image_running();
void run_image(const portapack::spi_flash::image_tag_t image_tag);
void run_prepared_image(const uint32_t m4_code);
void shutdown();

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