Compare commits

...

93 Commits

Author SHA1 Message Date
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
206 changed files with 65891 additions and 7923 deletions
+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.
@@ -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
View File
@@ -61,6 +61,7 @@ cmake-build-debug/
*.sublime-workspace
.vscode
.idea
*.swp
# VSCodium extensions
.history
+1 -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)
+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)
+3 -3
View File
@@ -5,7 +5,7 @@
> 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.
@@ -26,11 +26,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/ABDO6H) 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). _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: 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/24T3TO), 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:
+1
View File
@@ -0,0 +1 @@
Please check [Intended-Use-and-Legality](https://github.com/portapack-mayhem/mayhem-firmware/wiki/)
+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
+83
View File
@@ -0,0 +1,83 @@
#!/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:
# ./dockerize.sh -j10
# ./dockerize.sh ninja -j10
# - 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 -1
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)
+1 -7
View File
@@ -191,7 +191,6 @@ set(CPPSRC
clock_manager.cpp
core_control.cpp
database.cpp
de_bruijn.cpp
rfm69.cpp
event_m0.cpp
file_reader.cpp
@@ -294,7 +293,6 @@ 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
@@ -303,9 +301,7 @@ set(CPPSRC
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
@@ -317,9 +313,7 @@ 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
@@ -377,7 +371,7 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC
hw
apps
protocols
bitmaps
bmps
)
#
+76 -9
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,
});
zoom_config.on_change = [this, view](size_t, OptionsField::value_t n) {
receiver_model.set_am_configuration(previous_filter_array_index + n);
view->set_zoom_factor(AM_MODULATION, n);
};
// restore zoom selection
zoom_config.set_by_value(view->get_zoom_factor(AM_MODULATION));
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) {
options_config.on_change = [this, view](size_t, OptionsField::value_t n) {
receiver_model.set_am_configuration(n);
previous_filter_array_index = n;
zoom_config.set_by_value(view->get_zoom_factor(AM_MODULATION));
};
}
@@ -102,6 +114,34 @@ WFMOptionsView::WFMOptionsView(
};
}
/* 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(
@@ -175,8 +215,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));
@@ -226,11 +267,26 @@ 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_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 +302,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,10 +324,6 @@ 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();
update_modulation(modulation);
on_show_options_modulation();
@@ -329,7 +382,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 +399,12 @@ void AnalogAudioView::on_show_options_modulation() {
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 +446,9 @@ void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
case ReceiverModel::Mode::WidebandFMAudio:
image_tag = portapack::spi_flash::image_tag_wfm_audio;
break;
case ReceiverModel::Mode::AMAudioFMApt: // TODO pending to update it.
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,6 +469,8 @@ 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);
receiver_model.set_hidden_offset(modulation == ReceiverModel::Mode::AMAudioFMApt ? -2200 : 0); // wefax needs to be shifted, see wefax rx app.
receiver_model.enable();
// TODO: This doesn't belong here! There's a better way.
@@ -421,6 +485,9 @@ void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
case ReceiverModel::Mode::WidebandFMAudio:
sampling_rate = 48000;
break;
case ReceiverModel::Mode::AMAudioFMApt: // TODO Wefax mode.
sampling_rate = 12000;
break;
default:
break;
}
+44 -3
View File
@@ -35,9 +35,12 @@
namespace ui {
class AnalogAudioView;
class AMOptionsView : public View {
public:
AMOptionsView(Rect parent_rect, const Style* style);
AMOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
int16_t previous_filter_array_index = 0;
private:
Text label_config{
@@ -51,6 +54,38 @@ class AMOptionsView : public View {
{
// Using common messages from freqman_ui.cpp
}};
OptionsField zoom_config{
{23 * 8, 0 * 16},
7,
{{"ZOOM x1", 0},
{"ZOOM x2", 6}} // offset index array filters.
};
};
class AMFMAptOptionsView : public View {
public:
AMFMAptOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
private:
Text label_config{
{0 * 8, 0 * 16, 2 * 8, 1 * 16},
"BW",
};
OptionsField options_config{
{3 * 8, 0 * 16},
6, // Max option length chars
{
// 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{
{23 * 8, 0 * 16},
7,
{{"ZOOM x1", 0},
{"ZOOM x2", 6}} // offset index array filters.
};
};
class NBFMOptionsView : public View {
@@ -98,8 +133,6 @@ class WFMOptionsView : public View {
}};
};
class AnalogAudioView;
class SPECOptionsView : public View {
public:
SPECOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
@@ -164,17 +197,24 @@ 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);
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
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.
}};
const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16};
@@ -211,6 +251,7 @@ class AnalogAudioView : public View {
{"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
}};
AudioVolumeField field_volume{
+43 -16
View File
@@ -39,6 +39,14 @@ using namespace portapack;
using namespace modems;
namespace fs = std::filesystem;
#define BLE_RX_NO_ERROR 0
#define BLE_RX_LIST_FILENAME_EMPTY_ERROR 1
#define BLE_RX_ENTRY_FILENAME_EMPTY_ERROR 2
#define BLE_RX_LIST_SAVE_ERROR 3
#define BLE_RX_ENTRY_SAVE_ERROR 4
static uint8_t ble_rx_error = BLE_RX_NO_ERROR;
void BLELogger::log_raw_data(const std::string& data) {
log_file.write_entry(data);
}
@@ -202,19 +210,23 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
}
void BleRecentEntryDetailView::on_save_file(const std::string value, BLETxPacket packetToSave) {
ensure_directory(packet_save_path);
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
auto new_path = folder / value + ext;
saveFile(new_path, packetToSave);
if (value.length() > 0) {
ensure_directory(packet_save_path);
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
auto new_path = folder / value + ext;
ble_rx_error = saveFile(new_path, packetToSave);
} else {
nav_.pop();
ble_rx_error = BLE_RX_ENTRY_FILENAME_EMPTY_ERROR;
}
}
bool BleRecentEntryDetailView::saveFile(const std::filesystem::path& path, BLETxPacket packetToSave) {
File f;
auto error = f.create(path);
if (error)
return false;
return BLE_RX_ENTRY_SAVE_ERROR;
std::string macAddressStr = packetToSave.macAddress;
std::string advertisementDataStr = packetToSave.advertisementData;
@@ -224,7 +236,7 @@ bool BleRecentEntryDetailView::saveFile(const std::filesystem::path& path, BLETx
f.write(packetString.c_str(), packetString.length());
return true;
return BLE_RX_NO_ERROR;
}
void BleRecentEntryDetailView::update_data() {
@@ -568,11 +580,14 @@ std::string BLERxView::build_line_str(BleRecentEntry entry) {
}
void BLERxView::on_save_file(const std::string value) {
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
auto new_path = folder / value + ext;
saveFile(new_path);
if (value.length() > 0) {
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
auto new_path = folder / value + ext;
ble_rx_error = saveFile(new_path);
} else {
ble_rx_error = BLE_RX_LIST_FILENAME_EMPTY_ERROR;
}
}
bool BLERxView::saveFile(const std::filesystem::path& path) {
@@ -584,7 +599,7 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
auto error = src->open(path, false, true);
if (error) {
return false;
return BLE_RX_LIST_SAVE_ERROR;
}
for (const auto& entry : recent) {
@@ -615,7 +630,7 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
auto error = dst->open(tempFilePath, false, true);
if (error) {
return false;
return BLE_RX_LIST_SAVE_ERROR;
}
dst->write_line(headerStr.c_str());
@@ -695,7 +710,7 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
tempList.clear();
return true;
return BLE_RX_NO_ERROR;
}
void BLERxView::on_data(BlePacketData* packet) {
@@ -831,6 +846,18 @@ void BLERxView::on_timer() {
baseband::set_btlerx(randomChannel);
}
}
if (ble_rx_error != BLE_RX_NO_ERROR) {
if (ble_rx_error == BLE_RX_LIST_FILENAME_EMPTY_ERROR) {
nav_.display_modal("Error", "List filename is empty !");
} else if (ble_rx_error == BLE_RX_ENTRY_FILENAME_EMPTY_ERROR) {
nav_.display_modal("Error", "Entry filename is empty !");
} else if (ble_rx_error == BLE_RX_LIST_SAVE_ERROR) {
nav_.display_modal("Error", "Couldn't save list !");
} else if (ble_rx_error == BLE_RX_ENTRY_SAVE_ERROR) {
nav_.display_modal("Error", "Couldn't save entry !");
}
ble_rx_error = BLE_RX_NO_ERROR;
}
}
void BLERxView::handle_entries_sort(uint8_t index) {
+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);
+11 -2
View File
@@ -48,12 +48,21 @@ 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},
+2 -1
View File
@@ -175,7 +175,8 @@ void SoundBoardView::refresh_list() {
for (auto& c : entry_extension)
c = toupper(c);
if (entry_extension == ".WAV") {
if (entry_extension == ".WAV" && entry.path().string().find("shopping_cart") == std::string::npos) {
/* ^ because the shopping cart lock app using the speaker to send the LF signal, it's meaningless to be here */
if (reader->open(wav_dir / entry.path())) {
if ((reader->channels() == 1) && ((reader->bits_per_sample() == 8) || (reader->bits_per_sample() == 16))) {
// sounds[c].ms_duration = reader->ms_duration();
+14 -13
View File
@@ -7,30 +7,33 @@
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.mayhem.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",
" ",
"#Havoc:",
"jboone,furrtek,eried,argilo,",
@@ -72,7 +75,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 +106,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) {
+14 -6
View File
@@ -99,13 +99,21 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
field_frequency.set_value(aprs_rx_freq);
} else if (i == 1) { // NA - North America - is also the default
field_frequency.set_value(144390000);
} else if (i == 2) { // EUR
field_frequency.set_value(144800000);
} else if (i == 3) { // AUS
field_frequency.set_value(145175000);
} else if (i == 4) { // NZ
} else if (i == 2) { // NZ
field_frequency.set_value(144575000);
} else if (i == 5) { // ISS
} else if (i == 3) { // JAP
field_frequency.set_value(144640000);
} else if (i == 4) { // PHI
field_frequency.set_value(144740000);
} else if (i == 5) { // EUR
field_frequency.set_value(144800000);
} else if (i == 6) { // THA
field_frequency.set_value(144900000);
} else if (i == 7) { // AUS
field_frequency.set_value(145175000);
} else if (i == 8) { // BR
field_frequency.set_value(145570000);
} else if (i == 9) { // ISS
field_frequency.set_value(145825000);
}
options_region_id = i;
+8 -4
View File
@@ -227,10 +227,14 @@ class APRSRxView : public View {
3,
{{"MAN", 0},
{"NA ", 1},
{"EUR", 2},
{"AUS", 3},
{"NZ ", 4},
{"ISS", 5}}};
{"NZ ", 2},
{"JAP", 3},
{"PHI", 4},
{"EUR", 5},
{"THA", 6},
{"AUS", 7},
{"BR ", 8},
{"ISS", 9}}};
FrequencyField field_frequency{
{3 * 8, 0 * 16}};
-1
View File
@@ -419,7 +419,6 @@ void DebugMenuView::on_populate() {
}
add_items({
{"Buttons Test", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_controls, [this]() { nav_.push<DebugControlsView>(); }},
{"Debug Dump", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { portapack::persistent_memory::debug_dump(); }},
{"M0 Stack Dump", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { stack_dump(); }},
{"Memory Dump", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { nav_.push<DebugMemoryDumpView>(); }},
{"Peripherals", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals, [this]() { nav_.push<DebugPeripheralsMenuView>(); }},
@@ -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"
@@ -92,12 +92,18 @@ void ExternalModuleView::on_tick_second() {
case app_location_t::TX:
btnText += " (TX)";
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 +125,5 @@ void ExternalModuleView::on_tick_second() {
}
}
}
} // namespace ui
@@ -177,4 +177,4 @@ void FlashUtilityView::focus() {
menu_view.focus();
}
} /* namespace ui */
} /* namespace ui */
+1 -1
View File
@@ -45,7 +45,7 @@ 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_bandwidths[5];
extern options_db_t freqman_steps;
extern options_db_t freqman_steps_short;
+10 -6
View File
@@ -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&) {
@@ -1257,9 +1253,17 @@ 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);
}
last_mode = v;
change_mode(v);
};
// for some motive, audio output gets stopped.
if (!recon && field_mode.selected_index_value() != SPEC_MODULATION)
-1
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"
+81 -93
View File
@@ -53,6 +53,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;
@@ -324,6 +326,7 @@ SetUIView::SetUIView(NavigationView& nav) {
&toggle_bias_tee,
&toggle_clock,
&toggle_mute,
&toggle_fake_brightness,
&toggle_sd_card,
&button_save,
&button_cancel});
@@ -361,6 +364,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());
@@ -389,6 +393,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());
@@ -669,32 +674,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 +681,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 +721,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(
@@ -777,13 +806,20 @@ 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_invert_switch,
&checkbox_brightness_switch});
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
button_save.on_select = [&nav, this](Button&) {
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
if (checkbox_invert_switch.value() != pmem::config_lcd_inverted_mode()) {
display.set_inverted(checkbox_invert_switch.value());
pmem::set_lcd_inverted_mode(checkbox_invert_switch.value());
@@ -792,6 +828,14 @@ SetDisplayView::SetDisplayView(NavigationView& nav) {
nav.pop();
};
// only enable invert OR fake brightness
checkbox_invert_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_brightness_switch.set_value(false);
};
checkbox_brightness_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_invert_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
@@ -957,67 +1001,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) {
@@ -1099,9 +1082,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 +1097,20 @@ 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>(); }},
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [this]() { nav_.push<SetSDCardView>(); }},
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [this]() { nav_.push<SetUIView>(); }},
//{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [this]() { nav_.push<SetQRCodeView>(); }},
{"Display", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetDisplayView>(); }},
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetMenuColorView>(); }},
{"Theme", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetThemeView>(); }},
{"Autostart", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetAutostartView>(); }},
});
if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_batt_icon, [this]() { nav_.push<SetBatteryView>(); }});
add_external_items(nav_, app_location_t::SETTINGS, *this, return_icon ? 1 : 0);
}
} /* namespace ui */
+103 -82
View File
@@ -363,8 +363,12 @@ class SetUIView : public View {
{19 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_batt_text};
ImageToggle toggle_sd_card{
ImageToggle toggle_fake_brightness{
{21 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_brightness};
ImageToggle toggle_sd_card{
{23 * 8, 14 * 16 + 2, 16, 16},
&bitmap_sd_card_ok};
Button button_save{
@@ -541,35 +545,7 @@ class SetAudioView : public View {
};
};
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},
"Cancel",
};
};
using portapack::persistent_memory::encoder_dial_direction;
using portapack::persistent_memory::encoder_dial_sensitivity;
using portapack::persistent_memory::encoder_rate_multiplier;
@@ -583,30 +559,95 @@ 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},
{{0 * 8, 0 * 16}, "Sensitivity to dial rotation", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 1 * 16}, "position (x steps per 360):", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "Sensitivity:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "Rotation rate (default 1", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 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},
"Save"};
Button button_cancel{
{16 * 8, 16 * 16, 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{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -705,6 +746,7 @@ class SetConfigModeView : public View {
"Cancel",
};
};
using portapack::persistent_memory::fake_brightness_level_options;
class SetDisplayView : public View {
public:
@@ -715,8 +757,27 @@ class SetDisplayView : public View {
std::string title() const override { return "Display"; };
private:
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},
};
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_invert_switch{
{1 * 8, 2 * 16},
{1 * 8, 10 * 16},
23,
"Invert colors (For IPS)"};
@@ -864,47 +925,6 @@ class SetMenuColorView : public View {
};
};
class SetAutostartView : public View {
public:
SetAutostartView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Autostart"; };
private:
int32_t i = 0;
std::string autostart_app{""};
OptionsField::options_t opts{};
std::map<int32_t, std::string> full_app_list{}; // looking table
int32_t selected = 0;
SettingsStore nav_setting{
"nav"sv,
{{"autostart_app"sv, &autostart_app}}};
Labels labels{
{{1 * 8, 1 * 16}, "Select app to start on boot", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 2 * 16}, "(an SD Card is required)", Theme::getInstance()->fg_light->foreground}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
OptionsField options{
{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);
@@ -988,6 +1008,7 @@ class SettingsMenuView : public BtnGridView {
private:
NavigationView& nav_;
void on_populate() override;
};
+33 -6
View File
@@ -44,9 +44,9 @@ SigGenView::~SigGenView() {
void SigGenView::update_config() {
if (checkbox_stop.value())
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), field_stop.value());
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), (options_mod.selected_index_value() << 4) + options_shape.selected_index_value(), field_stop.value());
else
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), 0);
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), (options_mod.selected_index_value() << 4) + options_shape.selected_index_value(), 0);
}
void SigGenView::update_tone() {
@@ -78,6 +78,7 @@ SigGenView::SigGenView(
baseband::run_image(portapack::spi_flash::image_tag_siggen);
add_children({&labels,
&options_mod,
&options_shape,
&text_shape,
&symfield_tone,
@@ -87,22 +88,48 @@ SigGenView::SigGenView(
&field_stop,
&tx_view});
symfield_tone.hidden(1); // At first launch , by default we are in CW Shape has NO MOD , we are not using Tone modulation.
symfield_tone.hidden(true); // At first launch , by default we are in CW: Shape ignored, we are not using Tone modulation.
options_shape.hidden(true);
text_shape.hidden(true);
symfield_tone.set_value(1000); // Default: 1000 Hz
options_shape.on_change = [this](size_t, OptionsField::value_t v) {
text_shape.set(shape_strings[v]);
if (auto_update)
update_config();
if ((v == 0) || (v == 6)) { // In Shapes Options (CW & Pseudo Random Noise) we are not using Tone modulation freq.
symfield_tone.hidden(1);
if (v == 5) { // In Shape Pseudo Random Noise we are not using Tone modulation freq.
symfield_tone.hidden(true);
} else {
symfield_tone.hidden(0);
symfield_tone.hidden(false);
}
set_dirty();
};
options_shape.set_selected_index(0);
text_shape.set(shape_strings[0]);
options_mod.on_change = [this](size_t, OptionsField::value_t v) {
if (auto_update)
update_config();
if (v == 0) { // In Modulation Options CW we are not using Tone modulation freq.
symfield_tone.hidden(true);
} else {
symfield_tone.hidden(false);
}
if ((v == 0) || (v == 2) || (v == 3) || (v == 7)) { // In Modulation Options CW, QPSK, BPSK, Pulsed CW we are not using Shapes.
options_shape.hidden(true);
text_shape.hidden(true);
} else {
options_shape.hidden(false);
text_shape.hidden(false);
}
set_dirty();
};
options_mod.set_selected_index(0);
field_stop.set_value(1);
symfield_tone.set_value(1000); // Default: 1000 Hz
+36 -30
View File
@@ -58,68 +58,74 @@ class SigGenView : public View {
app_settings::SettingsManager settings_{
"tx_siggen", app_settings::Mode::TX};
const std::string shape_strings[9] = {
"CW (No mod.) ",
"Sine mod. FM",
"Triangle mod.FM", // max 15 character text space.
"Saw up mod. FM",
"Saw down mod.FM",
"Square mod. FM",
"Pseudo Noise FM", // using 16 bits LFSR register, 16 order polynomial feedback.
"BPSK 0,1,0,1...",
"QPSK 00-01-10.."};
const std::string shape_strings[6] = {// max 15 character text space.
"Sine",
"Triangle",
"Saw up",
"Saw down",
"Square",
"Pseudo Noise"};
bool auto_update{false};
Labels labels{
{{3 * 8, 4 + 10}, "Shape:", Theme::getInstance()->fg_light->foreground},
{{6 * 8, 7 * 8}, "Tone: Hz", Theme::getInstance()->fg_light->foreground},
{{22 * 8, 15 * 8 + 4}, "s.", Theme::getInstance()->fg_light->foreground},
{{8 * 8, 20 * 8}, "Modulation: FM", Theme::getInstance()->fg_light->foreground}};
{{3 * 8, 2 * 8}, "Modulation:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 3 * 8 + 8 + 10}, "Shape:", Theme::getInstance()->fg_light->foreground},
{{6 * 8, 2 * 8 + 7 * 8}, "Tone: Hz", Theme::getInstance()->fg_light->foreground},
{{22 * 8, 2 * 8 + 15 * 8 + 4}, "s.", Theme::getInstance()->fg_light->foreground}};
ImageOptionsField options_shape{
{10 * 8, 4, 32, 32},
{10 * 8, 3 * 8 + 8, 32, 32},
Theme::getInstance()->bg_darkest->foreground,
Theme::getInstance()->bg_darkest->background,
{{&bitmap_sig_cw, 0},
{&bitmap_sig_sine, 1},
{&bitmap_sig_tri, 2},
{&bitmap_sig_saw_up, 3},
{&bitmap_sig_saw_down, 4},
{&bitmap_sig_square, 5},
{&bitmap_sig_noise, 6},
{&bitmap_sig_noise, 7}, // Pending to add a correct BPSK icon.
{&bitmap_sig_noise, 8}}}; // Pending to add a correct QPSK icon.
{{&bitmap_sig_sine, 0},
{&bitmap_sig_tri, 1},
{&bitmap_sig_saw_up, 2},
{&bitmap_sig_saw_down, 3},
{&bitmap_sig_square, 4},
{&bitmap_sig_noise, 5}}};
Text text_shape{
{15 * 8, 4 + 10, 15 * 8, 16},
{15 * 8, 3 * 8 + 8 + 10, 15 * 8, 16},
""};
SymField symfield_tone{
{12 * 8, 7 * 8},
{12 * 8, 2 * 8 + 7 * 8},
5};
Button button_update{
{5 * 8, 10 * 8, 8 * 8, 3 * 8},
{5 * 8, 2 * 8 + 10 * 8, 8 * 8, 3 * 8},
"Update"};
Checkbox checkbox_auto{
{15 * 8, 10 * 8},
{15 * 8, 2 * 8 + 10 * 8},
4,
"Auto"};
Checkbox checkbox_stop{
{5 * 8, 15 * 8},
{5 * 8, 2 * 8 + 15 * 8},
10,
"Stop after"};
NumberField field_stop{
{20 * 8, 15 * 8 + 4},
{20 * 8, 2 * 8 + 15 * 8 + 4},
2,
{1, 99},
1,
' '};
OptionsField options_mod{
{15 * 8, 2 * 8},
12,
{{"CW (No mod.)", 0},
{"FM", 1},
{"BPSK", 2},
{"QPSK", 3},
{"DSB", 4},
{"AM 100% dep.", 5},
{"AM 50% depth", 6},
{"Pulse CW 25%", 7}}};
TransmitterView tx_view{
16 * 16,
10000,
+12 -6
View File
@@ -66,12 +66,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);
}
@@ -304,6 +305,11 @@ void set_spectrum(const size_t sampling_rate, const size_t 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_siggen_tone(const uint32_t tone) {
const SigGenToneMessage message{
TONES_F2D(tone, TONES_SAMPLERATE)};
+5 -1
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;
};
@@ -89,6 +92,7 @@ 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_wefax_config(uint8_t lpm, uint8_t ioc);
void request_roger_beep();
void request_rssi_beep();
File diff suppressed because it is too large Load Diff
@@ -1,72 +0,0 @@
unsigned char modal_warning_bmp[] = {
0x42, 0x4d, 0xfa, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00,
0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0xa8, 0x02,
0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x07, 0x00,
0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0x00, 0x00, 0x5a,
0xff, 0x00, 0x78, 0x78, 0x78, 0x00, 0x00, 0x7c, 0xff, 0x00, 0x00, 0xa9,
0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x02, 0x03, 0x24, 0x66, 0x02, 0x30,
0x00, 0x00, 0x02, 0x36, 0x24, 0x66, 0x02, 0x63, 0x00, 0x00, 0x02, 0x66,
0x02, 0x63, 0x20, 0x00, 0x02, 0x36, 0x02, 0x66, 0x00, 0x00, 0x02, 0x36,
0x02, 0x60, 0x20, 0x00, 0x02, 0x06, 0x02, 0x63, 0x00, 0x00, 0x02, 0x06,
0x02, 0x60, 0x0c, 0x00, 0x00, 0x08, 0x03, 0x66, 0x66, 0x30, 0x0c, 0x00,
0x02, 0x06, 0x02, 0x60, 0x00, 0x00, 0x02, 0x03, 0x02, 0x66, 0x0c, 0x00,
0x00, 0x08, 0x06, 0x11, 0x11, 0x60, 0x0c, 0x00, 0x02, 0x66, 0x02, 0x30,
0x00, 0x00, 0x02, 0x00, 0x02, 0x66, 0x0c, 0x00, 0x00, 0x08, 0x06, 0x21,
0x21, 0x60, 0x0c, 0x00, 0x02, 0x66, 0x02, 0x00, 0x00, 0x00, 0x00, 0x06,
0x00, 0x36, 0x60, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x06, 0x42, 0x42, 0x60,
0x0a, 0x00, 0x00, 0x06, 0x06, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0x00, 0x06, 0x60, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x06, 0x54, 0x45, 0x60,
0x0a, 0x00, 0x00, 0x06, 0x06, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0x00, 0x03, 0x66, 0x00, 0x0a, 0x00, 0x00, 0x08, 0x03, 0x66, 0x66, 0x30,
0x0a, 0x00, 0x00, 0x06, 0x66, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
0x00, 0x00, 0x66, 0x00, 0x1c, 0x00, 0x00, 0x06, 0x66, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x36, 0x60, 0x18, 0x00, 0x00, 0x08,
0x06, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x60,
0x0a, 0x00, 0x02, 0x36, 0x02, 0x63, 0x0a, 0x00, 0x00, 0x08, 0x06, 0x60,
0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x66, 0x08, 0x00,
0x00, 0x08, 0x03, 0x61, 0x16, 0x30, 0x08, 0x00, 0x00, 0x08, 0x66, 0x30,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x02, 0x66, 0x08, 0x00, 0x00, 0x08,
0x06, 0x11, 0x11, 0x60, 0x08, 0x00, 0x02, 0x66, 0x06, 0x00, 0x00, 0x00,
0x06, 0x00, 0x02, 0x36, 0x02, 0x60, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11,
0x11, 0x60, 0x06, 0x00, 0x02, 0x06, 0x02, 0x63, 0x06, 0x00, 0x00, 0x00,
0x06, 0x00, 0x02, 0x06, 0x02, 0x60, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11,
0x11, 0x60, 0x06, 0x00, 0x02, 0x06, 0x02, 0x60, 0x06, 0x00, 0x00, 0x00,
0x06, 0x00, 0x02, 0x03, 0x02, 0x66, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11,
0x11, 0x60, 0x06, 0x00, 0x02, 0x66, 0x02, 0x30, 0x06, 0x00, 0x00, 0x00,
0x08, 0x00, 0x02, 0x66, 0x06, 0x00, 0x00, 0x08, 0x06, 0x11, 0x11, 0x60,
0x06, 0x00, 0x02, 0x66, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18,
0x36, 0x60, 0x00, 0x00, 0x06, 0x11, 0x11, 0x60, 0x00, 0x00, 0x06, 0x63,
0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x18, 0x06, 0x60, 0x00, 0x00,
0x06, 0x21, 0x21, 0x60, 0x00, 0x00, 0x06, 0x60, 0x08, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x18, 0x03, 0x66, 0x00, 0x00, 0x06, 0x12, 0x12, 0x60,
0x00, 0x00, 0x66, 0x30, 0x08, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14,
0x66, 0x00, 0x00, 0x06, 0x21, 0x21, 0x60, 0x00, 0x00, 0x66, 0x0a, 0x00,
0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, 0x36, 0x60, 0x00, 0x06, 0x22, 0x22,
0x60, 0x00, 0x06, 0x63, 0x0a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x14,
0x06, 0x60, 0x00, 0x06, 0x24, 0x24, 0x60, 0x00, 0x06, 0x60, 0x0a, 0x00,
0x00, 0x00, 0x0a, 0x00, 0x00, 0x14, 0x03, 0x66, 0x00, 0x06, 0x42, 0x42,
0x60, 0x00, 0x66, 0x30, 0x0a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10,
0x66, 0x00, 0x06, 0x54, 0x45, 0x60, 0x00, 0x66, 0x0c, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x00, 0x10, 0x36, 0x60, 0x03, 0x66, 0x66, 0x30, 0x06, 0x63,
0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x06, 0x02, 0x60, 0x08, 0x00,
0x02, 0x06, 0x02, 0x60, 0x0c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x03,
0x02, 0x66, 0x08, 0x00, 0x02, 0x66, 0x02, 0x30, 0x0c, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x02, 0x66, 0x08, 0x00, 0x02, 0x66, 0x0e, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x00, 0x0c, 0x36, 0x60, 0x00, 0x00, 0x06, 0x63, 0x0e, 0x00,
0x00, 0x00, 0x0e, 0x00, 0x00, 0x0c, 0x06, 0x60, 0x00, 0x00, 0x06, 0x60,
0x0e, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x0c, 0x03, 0x66, 0x00, 0x00,
0x66, 0x30, 0x0e, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x66, 0x00,
0x00, 0x66, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x36, 0x60,
0x06, 0x63, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x06, 0x60,
0x06, 0x60, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x08, 0x03, 0x66,
0x66, 0x30, 0x10, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x66, 0x02, 0x66,
0x12, 0x00, 0x00, 0x00, 0x12, 0x00, 0x02, 0x36, 0x02, 0x63, 0x12, 0x00,
0x00, 0x01};
unsigned int modal_warning_bmp_len = 830;
-327
View File
@@ -1,327 +0,0 @@
// converted by bmp_to_hex_cpp_arr.py at the firmware/tools dir
// fake transparent px should be the exact color rgb dec(41,24,22), then use true for the last arg when drawing bmp with the draw func
const unsigned char splash_bmp[] = {
0x42, 0x4d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0x32, 0x00,
0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9c, 0x0e,
0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x0f, 0x00,
0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf6, 0x00, 0x00, 0x14,
0xc9, 0x00, 0x00, 0x0c, 0x78, 0x00, 0x16, 0x18, 0x29, 0x00, 0xfd, 0xfd,
0xfd, 0x00, 0xf0, 0xf0, 0xf0, 0x00, 0xe7, 0xe7, 0xe7, 0x00, 0xda, 0xda,
0xda, 0x00, 0xcd, 0xcd, 0xcd, 0x00, 0xc7, 0xc7, 0xc7, 0x00, 0xba, 0xba,
0xba, 0x00, 0xad, 0xad, 0xad, 0x00, 0x96, 0x96, 0x96, 0x00, 0x5c, 0x5c,
0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0x00, 0x08,
0x33, 0x44, 0x44, 0x47, 0x0a, 0x33, 0x00, 0x06, 0xd5, 0x44, 0x7d, 0x00,
0x0a, 0x33, 0x00, 0x0a, 0x44, 0x44, 0x48, 0x33, 0x3a, 0x00, 0x06, 0x44,
0x02, 0xd3, 0x12, 0x33, 0x00, 0x08, 0xa4, 0x44, 0x45, 0x9d, 0x08, 0x33,
0x00, 0x08, 0xc4, 0x44, 0x45, 0xc3, 0x10, 0x33, 0x00, 0x08, 0x34, 0x44,
0x44, 0x6d, 0x16, 0x33, 0x00, 0x08, 0x34, 0x44, 0x44, 0x6d, 0x08, 0x33,
0x02, 0xd5, 0x1a, 0x44, 0x00, 0x0c, 0x45, 0xc3, 0x33, 0x44, 0x44, 0x47,
0x0a, 0x33, 0x00, 0x06, 0xd5, 0x44, 0x7d, 0x00, 0x0a, 0x33, 0x00, 0x08,
0x44, 0x44, 0x48, 0x33, 0x00, 0x00, 0x00, 0x0a, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x00, 0x06, 0x33, 0x00, 0x0a, 0xd4, 0x46, 0x89, 0x98, 0xb3, 0x00,
0x08, 0x33, 0x00, 0x12, 0x49, 0x99, 0x94, 0x33, 0x3d, 0x79, 0x99, 0x99,
0x73, 0x00, 0x10, 0x33, 0x00, 0x0a, 0x3d, 0x47, 0x99, 0x95, 0xa3, 0x00,
0x08, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c,
0x06, 0x33, 0x00, 0x06, 0xb4, 0x56, 0x89, 0x00, 0x18, 0x99, 0x00, 0x0e,
0x97, 0xa3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x00, 0x06, 0x33, 0x00, 0x0a,
0xd4, 0x46, 0x89, 0x98, 0xb3, 0x00, 0x08, 0x33, 0x00, 0x08, 0x49, 0x99,
0x94, 0x33, 0x00, 0x00, 0x00, 0x0a, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x00,
0x06, 0x33, 0x02, 0x55, 0x06, 0x99, 0x02, 0x9b, 0x08, 0x33, 0x00, 0x12,
0x49, 0x99, 0x94, 0x33, 0x33, 0xb9, 0x99, 0x99, 0x8d, 0x00, 0x10, 0x33,
0x00, 0x0a, 0x37, 0x69, 0x99, 0x74, 0xd3, 0x00, 0x08, 0x33, 0x00, 0x08,
0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c,
0x16, 0x33, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0x33, 0x33, 0x3a, 0x47,
0x1c, 0x99, 0x00, 0x0e, 0x97, 0xa3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x00,
0x06, 0x33, 0x02, 0x55, 0x06, 0x99, 0x02, 0x9b, 0x08, 0x33, 0x00, 0x08,
0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x33, 0x33, 0x3a, 0x48, 0x00, 0x06, 0x99, 0x02, 0x98, 0x02, 0xd3,
0x06, 0x33, 0x00, 0x12, 0x49, 0x99, 0x94, 0x33, 0x33, 0xd7, 0x99, 0x99,
0x98, 0x00, 0x10, 0x33, 0x00, 0x08, 0xd4, 0x89, 0x99, 0x5a, 0x0a, 0x33,
0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x7c, 0x16, 0x33, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0x33, 0x33,
0xb4, 0x79, 0x1c, 0x99, 0x00, 0x16, 0x97, 0xa3, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x33, 0x33, 0x3a, 0x48, 0x00, 0x06, 0x99, 0x02, 0x98, 0x02, 0xd3,
0x06, 0x33, 0x00, 0x08, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x12,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x33, 0xd4, 0x79, 0x00, 0x08, 0x99,
0x02, 0x73, 0x06, 0x33, 0x00, 0x0c, 0x49, 0x99, 0x94, 0x33, 0x33, 0x3b,
0x06, 0x99, 0x02, 0xc3, 0x0e, 0x33, 0x00, 0x08, 0x56, 0x99, 0x97, 0x4d,
0x0a, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0x33, 0x3d, 0x47, 0x00, 0x1e, 0x99, 0x00, 0x16, 0x97, 0xa3, 0x33, 0x49,
0x99, 0x95, 0xd3, 0x33, 0x33, 0xd4, 0x79, 0x00, 0x08, 0x99, 0x02, 0x73,
0x06, 0x33, 0x00, 0x08, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x10,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x33, 0xa5, 0x0a, 0x99, 0x02, 0x8d,
0x06, 0x33, 0x00, 0x14, 0x49, 0x99, 0x94, 0x33, 0x33, 0x3d, 0x79, 0x99,
0x99, 0x73, 0x0c, 0x33, 0x00, 0x0a, 0x3c, 0x48, 0x99, 0x95, 0xa3, 0x00,
0x0a, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0x33, 0x37, 0x69, 0x00, 0x1e, 0x99, 0x00, 0x14, 0x97, 0xa3, 0x33, 0x49,
0x99, 0x95, 0xd3, 0x33, 0x33, 0xa5, 0x0a, 0x99, 0x02, 0x8d, 0x06, 0x33,
0x00, 0x08, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x1c, 0x33, 0x49,
0x99, 0x95, 0xd3, 0x33, 0x3d, 0x47, 0x99, 0x97, 0x89, 0x99, 0x99, 0x97,
0x06, 0x33, 0x00, 0x06, 0x49, 0x99, 0x94, 0x00, 0x06, 0x33, 0x00, 0x08,
0xb9, 0x99, 0x99, 0x9d, 0x0c, 0x33, 0x00, 0x0a, 0x34, 0x69, 0x99, 0x74,
0xd3, 0x00, 0x0a, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x98, 0xa3, 0x10, 0x33,
0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x12, 0x34, 0x99,
0x99, 0x7c, 0x33, 0x34, 0x89, 0x99, 0x64, 0x00, 0x1c, 0x44, 0x00, 0x1e,
0xa3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x3d, 0x47, 0x99, 0x97, 0x89,
0x99, 0x99, 0x97, 0x00, 0x06, 0x33, 0x00, 0x08, 0x49, 0x99, 0x94, 0x33,
0x00, 0x00, 0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x3c, 0x59,
0x99, 0x95, 0xbc, 0x99, 0x99, 0x98, 0xd3, 0x33, 0x33, 0x49, 0x99, 0x94,
0x06, 0x33, 0x02, 0xd7, 0x06, 0x99, 0x02, 0x74, 0x0c, 0x44, 0x00, 0x06,
0x99, 0x99, 0x4b, 0x00, 0x0c, 0x33, 0x00, 0x08, 0xb7, 0x99, 0x99, 0xa3,
0x10, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0x33, 0xd5, 0x99, 0x99, 0x6d, 0x00, 0x20, 0x33,
0x00, 0x28, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x3c, 0x59, 0x99, 0x95, 0xbc,
0x99, 0x99, 0x98, 0xd3, 0x33, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00,
0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x34, 0x79, 0x99, 0x84,
0xdd, 0x89, 0x99, 0x99, 0x83, 0x33, 0x33, 0x49, 0x99, 0x94, 0x06, 0x33,
0x02, 0x3b, 0x16, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0x33, 0x00, 0x0a,
0x3d, 0x47, 0x99, 0x99, 0x9d, 0x00, 0x10, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x9d, 0x16, 0x33, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0x33, 0xd6,
0x99, 0x99, 0x6d, 0x00, 0x20, 0x33, 0x00, 0x28, 0x49, 0x99, 0x95, 0xd3,
0x33, 0x34, 0x79, 0x99, 0x84, 0xdd, 0x89, 0x99, 0x99, 0x83, 0x33, 0x33,
0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x28, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x33, 0xc4, 0x99, 0x99, 0x67, 0x33, 0x79, 0x99, 0x99, 0x8d, 0x33,
0x33, 0x49, 0x99, 0x94, 0x06, 0x33, 0x02, 0x3d, 0x02, 0x79, 0x14, 0x99,
0x02, 0x94, 0x02, 0xc3, 0x0a, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x99,
0x98, 0x00, 0x10, 0x33, 0x02, 0x34, 0x06, 0x99, 0x02, 0x74, 0x16, 0x44,
0x00, 0x10, 0x99, 0x99, 0x7c, 0x33, 0xd6, 0x99, 0x99, 0x9d, 0x20, 0x33,
0x00, 0x28, 0x49, 0x99, 0x95, 0xd3, 0x33, 0xc4, 0x99, 0x99, 0x67, 0x33,
0x79, 0x99, 0x99, 0x8d, 0x33, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00,
0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x57, 0x99, 0x99, 0x4d,
0x33, 0xd8, 0x99, 0x99, 0x98, 0x33, 0x33, 0x49, 0x99, 0x94, 0x08, 0x33,
0x02, 0xc9, 0x14, 0x99, 0x02, 0x74, 0x0c, 0x33, 0x02, 0x84, 0x02, 0x89,
0x06, 0x99, 0x02, 0xa3, 0x0e, 0x33, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06,
0x7c, 0x33, 0xd6, 0x00, 0x06, 0x99, 0x02, 0x64, 0x18, 0x44, 0x00, 0x2e,
0x45, 0xc3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x57, 0x99, 0x99, 0x4d,
0x33, 0xd8, 0x99, 0x99, 0x98, 0x33, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x3d, 0x48, 0x99,
0x97, 0x43, 0x33, 0x37, 0x99, 0x99, 0x97, 0xd3, 0x33, 0x49, 0x99, 0x94,
0x08, 0x33, 0x02, 0x37, 0x14, 0x99, 0x02, 0x4c, 0x0a, 0x33, 0x02, 0x3b,
0x02, 0x48, 0x08, 0x99, 0x02, 0x9b, 0x0e, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x3d, 0x48, 0x99, 0x97, 0x43, 0x33, 0x37,
0x99, 0x99, 0x97, 0xd3, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00,
0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x35, 0x69, 0x99, 0x95, 0xc3,
0x33, 0x3c, 0x89, 0x99, 0x99, 0xb3, 0x33, 0x49, 0x99, 0x94, 0x08, 0x33,
0x02, 0x3c, 0x12, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0x33, 0x02, 0xd4,
0x02, 0x79, 0x0a, 0x99, 0x02, 0xd3, 0x0c, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x35, 0x69, 0x99, 0x95, 0xc3, 0x33, 0x3c,
0x89, 0x99, 0x99, 0xb3, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00,
0x00, 0x12, 0x33, 0x49, 0x99, 0x95, 0xd3, 0xd4, 0x89, 0x99, 0x74, 0x00,
0x06, 0x33, 0x00, 0x10, 0x79, 0x99, 0x99, 0x7d, 0x33, 0x49, 0x99, 0x94,
0x0a, 0x33, 0x00, 0x16, 0x79, 0x99, 0x99, 0x64, 0x44, 0x44, 0x89, 0x99,
0x99, 0x94, 0xc3, 0x00, 0x08, 0x33, 0x00, 0x0a, 0x3d, 0x46, 0x99, 0x98,
0x59, 0x00, 0x06, 0x99, 0x02, 0x8d, 0x0c, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x16, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0xd4, 0x89, 0x99, 0x74, 0x00, 0x06, 0x33,
0x00, 0x12, 0x79, 0x99, 0x99, 0x7d, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x76, 0x99, 0x99,
0x6a, 0x00, 0x06, 0x33, 0x00, 0x10, 0xa9, 0x99, 0x99, 0x9b, 0x33, 0x49,
0x99, 0x94, 0x0a, 0x33, 0x00, 0x14, 0xc9, 0x99, 0x99, 0x9c, 0x33, 0x33,
0x3b, 0x99, 0x99, 0x74, 0x0a, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x95,
0x7d, 0x00, 0x06, 0x99, 0x02, 0x9a, 0x0c, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x16, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x76, 0x99, 0x99, 0x6a, 0x00, 0x06, 0x33,
0x00, 0x12, 0xa9, 0x99, 0x99, 0x9b, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98,
0x4d, 0x00, 0x06, 0x33, 0x00, 0x10, 0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49,
0x99, 0x94, 0x0a, 0x33, 0x00, 0x14, 0x37, 0x99, 0x99, 0x97, 0x33, 0x33,
0xc4, 0x89, 0x99, 0x4c, 0x0a, 0x33, 0x00, 0x0c, 0x84, 0x89, 0x99, 0x64,
0x33, 0xd9, 0x06, 0x99, 0x02, 0xa3, 0x0a, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x64, 0x16, 0x44, 0x06, 0x99, 0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00,
0x20, 0x99, 0x00, 0x16, 0x97, 0xa3, 0x33, 0x49, 0x99, 0x95, 0x3d, 0x48,
0x99, 0x98, 0x4d, 0x00, 0x06, 0x33, 0x00, 0x12, 0xd7, 0x99, 0x99, 0x97,
0xd3, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x00, 0x12, 0x33, 0x49,
0x99, 0x96, 0x3b, 0x69, 0x99, 0x96, 0x53, 0x00, 0x06, 0x33, 0x02, 0x38,
0x06, 0x99, 0x00, 0x08, 0xc3, 0x49, 0x99, 0x94, 0x0a, 0x33, 0x02, 0x3c,
0x06, 0x99, 0x00, 0x0c, 0xb3, 0x33, 0x46, 0x99, 0x96, 0x43, 0x08, 0x33,
0x00, 0x0e, 0x3b, 0x48, 0x99, 0x97, 0x4d, 0x33, 0x3d, 0x00, 0x06, 0x99,
0x02, 0x9c, 0x0a, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33,
0x00, 0x12, 0x39, 0x99, 0x99, 0x7c, 0x33, 0xd6, 0x99, 0x99, 0x54, 0x00,
0x1c, 0x44, 0x00, 0x14, 0xa3, 0x33, 0x49, 0x99, 0x96, 0x3b, 0x69, 0x99,
0x96, 0x53, 0x06, 0x33, 0x02, 0x38, 0x06, 0x99, 0x00, 0x0a, 0xc3, 0x49,
0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x98,
0xd5, 0x79, 0x99, 0x94, 0xd3, 0x00, 0x06, 0x33, 0x00, 0x10, 0x3d, 0x89,
0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x0c, 0x33, 0x00, 0x12, 0x79, 0x99,
0x99, 0x8d, 0x3a, 0x59, 0x99, 0x84, 0xd3, 0x00, 0x08, 0x33, 0x00, 0x10,
0xd4, 0x79, 0x99, 0x84, 0xb3, 0x33, 0x33, 0xb9, 0x06, 0x99, 0x02, 0xd3,
0x08, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0x33, 0xd6, 0x99, 0x99, 0x5d, 0x00, 0x20, 0x33,
0x00, 0x10, 0x49, 0x99, 0x98, 0xd5, 0x79, 0x99, 0x94, 0xd3, 0x06, 0x33,
0x00, 0x12, 0x3d, 0x89, 0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x10, 0x33, 0x49, 0x99, 0x99, 0xa6, 0x99, 0x99, 0x74,
0x0a, 0x33, 0x00, 0x0e, 0x79, 0x99, 0x99, 0x9c, 0x59, 0x99, 0x94, 0x00,
0x0c, 0x33, 0x00, 0x10, 0xc9, 0x99, 0x99, 0x9a, 0xd4, 0x79, 0x99, 0x65,
0x08, 0x33, 0x00, 0x0a, 0x3d, 0x46, 0x99, 0x98, 0x48, 0x00, 0x06, 0x33,
0x02, 0x3a, 0x06, 0x99, 0x02, 0x83, 0x08, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x6d, 0x16, 0x33, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0x33, 0xd6,
0x99, 0x99, 0x93, 0x00, 0x20, 0x33, 0x00, 0x0e, 0x49, 0x99, 0x99, 0xa6,
0x99, 0x99, 0x74, 0x00, 0x0a, 0x33, 0x00, 0x10, 0x79, 0x99, 0x99, 0x9c,
0x59, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x10, 0x33, 0x49, 0x99, 0x99,
0x98, 0x99, 0x99, 0x5c, 0x0a, 0x33, 0x02, 0xd8, 0x06, 0x99, 0x00, 0x06,
0x79, 0x99, 0x84, 0x00, 0x0c, 0x33, 0x02, 0x37, 0x06, 0x99, 0x00, 0x08,
0x86, 0x99, 0x98, 0x4d, 0x08, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x95,
0x53, 0x00, 0x08, 0x33, 0x00, 0x08, 0x89, 0x99, 0x99, 0x9a, 0x08, 0x33,
0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33, 0x00, 0x0c, 0x34, 0x99,
0x99, 0x7c, 0x33, 0x36, 0x06, 0x99, 0x02, 0x54, 0x18, 0x44, 0x00, 0x14,
0x45, 0xc3, 0x33, 0x49, 0x99, 0x99, 0x98, 0x99, 0x99, 0x5c, 0x0a, 0x33,
0x02, 0xd8, 0x06, 0x99, 0x00, 0x08, 0x79, 0x99, 0x84, 0x33, 0x00, 0x00,
0x02, 0x33, 0x02, 0x59, 0x08, 0x99, 0x02, 0x97, 0x02, 0x4d, 0x0a, 0x33,
0x02, 0x36, 0x0a, 0x99, 0x02, 0x75, 0x0c, 0x33, 0x02, 0x3c, 0x0a, 0x99,
0x02, 0x96, 0x02, 0x53, 0x08, 0x33, 0x00, 0x0a, 0x84, 0x89, 0x99, 0x64,
0xd3, 0x00, 0x08, 0x33, 0x02, 0xd9, 0x06, 0x99, 0x02, 0xb3, 0x06, 0x33,
0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33, 0x00, 0x0c, 0x34, 0x99,
0x99, 0x7c, 0x33, 0x36, 0x20, 0x99, 0x00, 0x08, 0x97, 0xa3, 0x33, 0x59,
0x08, 0x99, 0x02, 0x97, 0x02, 0x4d, 0x0a, 0x33, 0x02, 0x36, 0x0a, 0x99,
0x02, 0x75, 0x02, 0x33, 0x00, 0x00, 0x02, 0x33, 0x02, 0xa8, 0x08, 0x99,
0x02, 0x95, 0x02, 0xa3, 0x0a, 0x33, 0x02, 0x3c, 0x0a, 0x99, 0x02, 0x5b,
0x0e, 0x33, 0x02, 0x89, 0x08, 0x99, 0x02, 0x84, 0x02, 0xd3, 0x06, 0x33,
0x00, 0x0a, 0x3b, 0x48, 0x99, 0x97, 0x4c, 0x00, 0x0a, 0x33, 0x02, 0x3d,
0x06, 0x99, 0x02, 0x9d, 0x06, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d,
0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c, 0x33, 0x3d, 0x89, 0x00,
0x1e, 0x99, 0x00, 0x08, 0x97, 0xa3, 0x33, 0xa8, 0x08, 0x99, 0x02, 0x95,
0x02, 0xa3, 0x0a, 0x33, 0x02, 0x3c, 0x0a, 0x99, 0x02, 0x5b, 0x02, 0x33,
0x00, 0x00, 0x02, 0x33, 0x02, 0xd7, 0x08, 0x99, 0x02, 0x64, 0x0e, 0x33,
0x02, 0x79, 0x06, 0x99, 0x02, 0x96, 0x02, 0x4d, 0x0e, 0x33, 0x02, 0xd8,
0x06, 0x99, 0x02, 0x98, 0x02, 0x48, 0x08, 0x33, 0x00, 0x0a, 0xd4, 0x79,
0x99, 0x84, 0xb3, 0x00, 0x0c, 0x33, 0x00, 0x16, 0xb9, 0x99, 0x99, 0x98,
0xd3, 0x33, 0x33, 0x34, 0x99, 0x99, 0x6d, 0x00, 0x16, 0x33, 0x00, 0x0c,
0x34, 0x99, 0x99, 0x7c, 0x33, 0x33, 0x20, 0x99, 0x00, 0x08, 0x97, 0xa3,
0x33, 0xd7, 0x08, 0x99, 0x02, 0x64, 0x0e, 0x33, 0x02, 0x79, 0x06, 0x99,
0x00, 0x06, 0x96, 0x4d, 0x33, 0x00, 0x00, 0x00, 0x02, 0x33, 0x02, 0x3c,
0x06, 0x99, 0x02, 0x96, 0x02, 0x4d, 0x0e, 0x33, 0x02, 0xd9, 0x06, 0x99,
0x02, 0x74, 0x02, 0xc3, 0x0e, 0x33, 0x02, 0x3d, 0x06, 0x99, 0x02, 0x85,
0x02, 0x53, 0x06, 0x33, 0x00, 0x0a, 0x3d, 0x46, 0x99, 0x99, 0x57, 0x00,
0x0e, 0x33, 0x02, 0x39, 0x06, 0x99, 0x00, 0x0e, 0xa3, 0x33, 0x33, 0x34,
0x99, 0x99, 0x6d, 0x00, 0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0x33, 0x33, 0x3a, 0x00, 0x1e, 0x99, 0x00, 0x08, 0x97, 0xa3, 0x33, 0x3c,
0x06, 0x99, 0x02, 0x96, 0x02, 0x4d, 0x0e, 0x33, 0x02, 0xd9, 0x06, 0x99,
0x00, 0x06, 0x74, 0xc3, 0x33, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x33, 0x33,
0xc8, 0x99, 0x96, 0x44, 0xd3, 0x00, 0x0e, 0x33, 0x00, 0x0a, 0x3d, 0x79,
0x99, 0x76, 0x4b, 0x00, 0x12, 0x33, 0x00, 0x0a, 0xc7, 0x99, 0x96, 0x45,
0xd3, 0x00, 0x06, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x96, 0x43, 0x00,
0x10, 0x33, 0x00, 0x14, 0x89, 0x99, 0x99, 0x9a, 0x33, 0x33, 0x34, 0x99,
0x99, 0x6d, 0x16, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x06, 0x33,
0x02, 0xa6, 0x1c, 0x99, 0x00, 0x12, 0x97, 0xa3, 0x33, 0x33, 0xc8, 0x99,
0x96, 0x44, 0xd3, 0x00, 0x0e, 0x33, 0x00, 0x0e, 0x3d, 0x79, 0x99, 0x76,
0x4b, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x33, 0x33, 0x3d, 0x84,
0x44, 0xc3, 0x12, 0x33, 0x00, 0x08, 0xdc, 0x44, 0x4a, 0xd3, 0x12, 0x33,
0x00, 0x08, 0x3d, 0xa4, 0x44, 0xd3, 0x08, 0x33, 0x02, 0xc8, 0x06, 0x44,
0x02, 0xd3, 0x10, 0x33, 0x02, 0xd4, 0x06, 0x44, 0x00, 0x0c, 0xa3, 0x33,
0x38, 0x44, 0x44, 0x4d, 0x16, 0x33, 0x00, 0x08, 0x38, 0x44, 0x44, 0x4c,
0x06, 0x33, 0x02, 0x3d, 0x02, 0xc4, 0x1c, 0x44, 0x00, 0x0e, 0xa3, 0x33,
0x33, 0x3d, 0x84, 0x44, 0xc3, 0x00, 0x12, 0x33, 0x00, 0x0c, 0xdc, 0x44,
0x4a, 0xd3, 0x33, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0x00, 0x1e, 0x33, 0x20, 0x00, 0x23, 0x30, 0x00, 0x13, 0x31,
0x00, 0x02, 0x33, 0x00, 0x01, 0x33, 0x32, 0x00, 0x08, 0x00, 0x00, 0x26,
0x13, 0x31, 0x00, 0x02, 0x33, 0x00, 0x01, 0x33, 0x00, 0x00, 0x13, 0x33,
0x00, 0x00, 0x13, 0x32, 0x00, 0x00, 0x23, 0x00, 0x24, 0x33, 0x00, 0x06,
0x32, 0x00, 0x02, 0x00, 0x08, 0x33, 0x02, 0x32, 0x06, 0x00, 0x00, 0x30,
0x01, 0x33, 0x31, 0x00, 0x02, 0x33, 0x00, 0x01, 0x33, 0x33, 0x32, 0x00,
0x02, 0x33, 0x33, 0x30, 0x00, 0x13, 0x32, 0x00, 0x02, 0x33, 0x00, 0x01,
0x08, 0x33, 0x00, 0x10, 0x30, 0x00, 0x13, 0x31, 0x00, 0x02, 0x33, 0x30,
0x08, 0x00, 0x00, 0x10, 0x23, 0x30, 0x00, 0x13, 0x31, 0x00, 0x02, 0x33,
0x00, 0x00, 0x00, 0x20, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x10, 0x10, 0x06, 0x00, 0x00, 0x26,
0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01, 0x11, 0x03, 0x33,
0x01, 0x11, 0x13, 0x32, 0x01, 0x11, 0x23, 0x00, 0x24, 0x33, 0x00, 0x06,
0x32, 0x01, 0x11, 0x00, 0x08, 0x33, 0x00, 0x38, 0x10, 0x10, 0x00, 0x11,
0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x33, 0x32, 0x01,
0x11, 0x33, 0x33, 0x30, 0x11, 0x03, 0x32, 0x01, 0x11, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x12, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00,
0x10, 0x00, 0x06, 0x00, 0x00, 0x10, 0x23, 0x30, 0x11, 0x03, 0x31, 0x11,
0x02, 0x33, 0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11,
0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00,
0x06, 0x33, 0x00, 0x24, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01,
0x11, 0x13, 0x33, 0x01, 0x10, 0x13, 0x32, 0x01, 0x11, 0x23, 0x24, 0x33,
0x00, 0x06, 0x32, 0x01, 0x11, 0x00, 0x08, 0x33, 0x00, 0x38, 0x11, 0x10,
0x23, 0x31, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x33,
0x32, 0x01, 0x11, 0x33, 0x33, 0x30, 0x11, 0x03, 0x32, 0x01, 0x11, 0x33,
0x01, 0x10, 0x08, 0x33, 0x00, 0x12, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02,
0x33, 0x01, 0x10, 0x00, 0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x23,
0x30, 0x11, 0x03, 0x31, 0x11, 0x12, 0x33, 0x01, 0x10, 0x33, 0x11, 0x10,
0x23, 0x00, 0x06, 0x33, 0x00, 0x28, 0x31, 0x11, 0x12, 0x33, 0x01, 0x00,
0x33, 0x01, 0x11, 0x11, 0x00, 0x01, 0x12, 0x33, 0x32, 0x01, 0x11, 0x11,
0x00, 0x23, 0x20, 0x33, 0x00, 0x06, 0x32, 0x01, 0x12, 0x00, 0x08, 0x33,
0x00, 0x38, 0x11, 0x10, 0x23, 0x30, 0x11, 0x03, 0x31, 0x11, 0x12, 0x33,
0x01, 0x00, 0x33, 0x33, 0x32, 0x01, 0x11, 0x33, 0x33, 0x30, 0x11, 0x13,
0x32, 0x01, 0x11, 0x33, 0x01, 0x11, 0x08, 0x33, 0x00, 0x12, 0x30, 0x11,
0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x00, 0x08, 0x33, 0x00, 0x0e,
0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x22,
0x33, 0x20, 0x11, 0x11, 0x00, 0x11, 0x03, 0x31, 0x11, 0x11, 0x00, 0x01,
0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33, 0x00, 0x10, 0x31, 0x11,
0x11, 0x00, 0x01, 0x13, 0x33, 0x01, 0x08, 0x11, 0x00, 0x10, 0x12, 0x33,
0x32, 0x01, 0x11, 0x11, 0x10, 0x23, 0x20, 0x33, 0x00, 0x60, 0x32, 0x01,
0x11, 0x10, 0x00, 0x03, 0x33, 0x11, 0x10, 0x23, 0x30, 0x11, 0x03, 0x31,
0x11, 0x12, 0x22, 0x01, 0x02, 0x33, 0x33, 0x32, 0x01, 0x11, 0x33, 0x33,
0x30, 0x11, 0x12, 0x21, 0x01, 0x11, 0x33, 0x01, 0x11, 0x22, 0x22, 0x23,
0x33, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0x33, 0x01, 0x10, 0x08, 0x33,
0x00, 0x0e, 0x30, 0x11, 0x12, 0x21, 0x10, 0x13, 0x33, 0x00, 0x00, 0x00,
0x00, 0x22, 0x33, 0x20, 0x11, 0x00, 0x11, 0x11, 0x03, 0x31, 0x11, 0x00,
0x01, 0x11, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33, 0x00, 0x28,
0x31, 0x11, 0x00, 0x01, 0x11, 0x13, 0x33, 0x01, 0x11, 0x00, 0x01, 0x11,
0x12, 0x33, 0x32, 0x01, 0x11, 0x00, 0x00, 0x23, 0x20, 0x33, 0x00, 0x60,
0x32, 0x01, 0x10, 0x01, 0x11, 0x12, 0x33, 0x11, 0x10, 0x23, 0x30, 0x11,
0x03, 0x31, 0x11, 0x10, 0x10, 0x11, 0x13, 0x33, 0x33, 0x32, 0x01, 0x11,
0x33, 0x33, 0x30, 0x11, 0x10, 0x10, 0x11, 0x11, 0x33, 0x01, 0x11, 0x01,
0x00, 0x01, 0x23, 0x30, 0x11, 0x00, 0x10, 0x11, 0x02, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x23, 0x33, 0x00,
0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x31, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x11, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33,
0x00, 0x24, 0x31, 0x11, 0x02, 0x33, 0x11, 0x10, 0x33, 0x01, 0x11, 0x03,
0x33, 0x11, 0x11, 0x13, 0x32, 0x01, 0x11, 0x23, 0x24, 0x33, 0x00, 0x60,
0x32, 0x01, 0x11, 0x33, 0x11, 0x10, 0x33, 0x11, 0x10, 0x23, 0x30, 0x11,
0x03, 0x31, 0x11, 0x01, 0x22, 0x11, 0x12, 0x33, 0x33, 0x32, 0x01, 0x11,
0x33, 0x33, 0x30, 0x11, 0x02, 0x22, 0x11, 0x11, 0x33, 0x01, 0x10, 0x22,
0x21, 0x11, 0x13, 0x30, 0x11, 0x02, 0x21, 0x11, 0x02, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x23, 0x33, 0x00,
0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33,
0x00, 0x24, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01, 0x11, 0x13,
0x33, 0x01, 0x11, 0x13, 0x32, 0x01, 0x11, 0x23, 0x24, 0x33, 0x00, 0x60,
0x32, 0x01, 0x11, 0x33, 0x01, 0x10, 0x33, 0x11, 0x11, 0x23, 0x30, 0x11,
0x03, 0x31, 0x11, 0x12, 0x33, 0x11, 0x10, 0x33, 0x33, 0x32, 0x01, 0x11,
0x33, 0x33, 0x30, 0x11, 0x13, 0x32, 0x11, 0x11, 0x33, 0x01, 0x11, 0x33,
0x21, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00,
0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31,
0x11, 0x12, 0x33, 0x01, 0x00, 0x33, 0x11, 0x11, 0x23, 0x00, 0x06, 0x33,
0x00, 0x2c, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01, 0x11, 0x11,
0x00, 0x01, 0x10, 0x13, 0x32, 0x01, 0x11, 0x11, 0x00, 0x00, 0x01, 0x23,
0x1c, 0x33, 0x00, 0x60, 0x32, 0x01, 0x12, 0x33, 0x01, 0x00, 0x33, 0x11,
0x11, 0x22, 0x20, 0x11, 0x03, 0x31, 0x11, 0x12, 0x22, 0x01, 0x10, 0x33,
0x33, 0x32, 0x01, 0x12, 0x33, 0x33, 0x30, 0x11, 0x12, 0x21, 0x01, 0x11,
0x33, 0x01, 0x11, 0x22, 0x10, 0x11, 0x13, 0x30, 0x11, 0x12, 0x21, 0x11,
0x02, 0x33, 0x01, 0x11, 0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x38, 0x33, 0x20, 0x11, 0x13,
0x30, 0x11, 0x03, 0x32, 0x11, 0x11, 0x00, 0x01, 0x02, 0x33, 0x21, 0x11,
0x10, 0x00, 0x00, 0x13, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01,
0x08, 0x11, 0x00, 0x08, 0x12, 0x33, 0x33, 0x21, 0x08, 0x11, 0x02, 0x10,
0x02, 0x23, 0x1c, 0x33, 0x00, 0x0e, 0x32, 0x01, 0x11, 0x10, 0x01, 0x02,
0x33, 0x00, 0x06, 0x11, 0x00, 0x62, 0x01, 0x10, 0x03, 0x31, 0x11, 0x11,
0x10, 0x11, 0x00, 0x33, 0x10, 0x00, 0x01, 0x11, 0x10, 0x13, 0x30, 0x11,
0x11, 0x10, 0x11, 0x01, 0x33, 0x01, 0x11, 0x11, 0x01, 0x10, 0x13, 0x30,
0x11, 0x11, 0x00, 0x10, 0x02, 0x33, 0x01, 0x11, 0x10, 0x00, 0x00, 0x23,
0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x12,
0x33, 0x20, 0x00, 0x13, 0x31, 0x00, 0x03, 0x33, 0x20, 0x00, 0x06, 0x00,
0x00, 0x06, 0x13, 0x33, 0x32, 0x00, 0x08, 0x00, 0x00, 0x12, 0x03, 0x32,
0x00, 0x02, 0x33, 0x10, 0x00, 0x33, 0x10, 0x00, 0x08, 0x00, 0x00, 0x08,
0x02, 0x33, 0x33, 0x30, 0x0a, 0x00, 0x02, 0x23, 0x1c, 0x33, 0x02, 0x32,
0x08, 0x00, 0x00, 0x06, 0x03, 0x33, 0x32, 0x00, 0x06, 0x00, 0x00, 0x06,
0x01, 0x33, 0x32, 0x00, 0x08, 0x00, 0x00, 0x06, 0x13, 0x33, 0x20, 0x00,
0x08, 0x00, 0x00, 0x06, 0x03, 0x33, 0x10, 0x00, 0x06, 0x00, 0x00, 0x06,
0x23, 0x33, 0x10, 0x00, 0x06, 0x00, 0x00, 0x06, 0x02, 0x33, 0x33, 0x00,
0x08, 0x00, 0x00, 0x06, 0x23, 0x33, 0x30, 0x00, 0x08, 0x00, 0x00, 0x10,
0x23, 0x31, 0x00, 0x03, 0x32, 0x00, 0x02, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x01};
unsigned int splash_new__new_t_len = 3854;
-104
View File
@@ -1,104 +0,0 @@
/*
* Copyright (C) 2014 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 "de_bruijn.hpp"
/* How this works:
* B(2,4) means:
* Alphabet size = 2 (binary)
* Code length = 4
* The length of the bitstream is (2 ^ 4) + (4 - 1) = 19 bits
* The primitive polynomials come from the de_bruijn_polys[] array (one for each code length)
* The shift register is init with 1 and shifted left each step
* The polynomial is kept on the right, and used as a AND mask applied on the corresponding shift register bits
* The resulting bits are XORed together to produce the new bit pushed in the shift register
*
* 0001 (init)
* AND 1001 (polynomial)
* 0001 XOR'd -> 1
*
* 00011 (shift left)
* AND 1001
* 0001 XOR'd -> 1
*
* 000111 (shift left)
* AND 1001
* 0001 XOR'd -> 1
*
* 0001111 (shift left)
* AND 1001
* 1001 XOR'd -> 0
*
* 00011110 (shift left)
* AND 1001
* 1000 XOR'd -> 1
* ...
* After 16 steps: (0+) 000111101011001000
* Each of the 16 possible values appears in the sequence:
* -0000-111101011001000 0
* 0-0001-11101011001000 1
* 0000111101011-0010-00 2
* 00-0011-1101011001000 3
* 00001111010110-0100-0 4
* 00001111-0101-1001000 5
* 0000111101-0110-01000 6
* 000-0111-101011001000 7
* 000011110101100-1000- 8
* 000011110101-1001-000 9
* 0000111-1010-11001000 10
* 000011110-1011-001000 11
* 00001111010-1100-1000 12
* 000011-1101-011001000 13
* 00001-1110-1011001000 14
* 0000-1111-01011001000 15
*/
size_t de_bruijn::init(const uint32_t n) {
// Cap
if ((n < 3) || (n > 16))
length = 3;
else
length = n;
poly = de_bruijn_polys[length - 3];
shift_register = 1;
return (1U << length) + (length - 1);
}
uint32_t de_bruijn::compute(const uint32_t steps) {
uint32_t step, bits, masked;
uint8_t new_bit;
for (step = 0; step < steps; step++) {
masked = shift_register & poly;
new_bit = 0;
for (bits = 0; bits < length; bits++) {
new_bit ^= (masked & 1);
masked >>= 1;
}
shift_register <<= 1;
shift_register |= new_bit;
}
return shift_register;
}
-57
View File
@@ -1,57 +0,0 @@
/*
* Copyright (C) 2014 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 <string>
#include <cstdint>
#ifndef __DE_BRUIJN_H__
#define __DE_BRUIJN_H__
// n from 3 to 16
const uint32_t de_bruijn_polys[14]{
0b0000000000000101,
0b0000000000001001,
0b0000000000011011,
0b0000000000110011,
0b0000000001010011,
0b0000000010001101,
0b0000000100100001,
0b0000001000100001,
0b0000010001100001,
0b0000110101000011,
0b0001001001100101,
0b0010101100000011,
0b0101000000001001,
0b1010000101000101};
struct de_bruijn {
public:
size_t init(const uint32_t n);
uint32_t compute(const uint32_t steps);
private:
uint32_t length{};
uint32_t poly{};
uint32_t shift_register{};
};
#endif /*__DE_BRUIJN_H__*/
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2024 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_whipcalc.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::antenna_length {
void initialize_app(ui::NavigationView& nav) {
nav.push<WhipCalcView>();
}
} // namespace ui::external_app::antenna_length
extern "C" {
__attribute__((section(".external_app.app_antenna_length.application_information"), used)) application_information_t _application_information_antenna_length = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::antenna_length::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Antenna Length",
/*.bitmap_data = */ {0x38, 0x3E, 0x10, 0x22, 0x10, 0x26, 0x10, 0x22, 0x10, 0x2E, 0x10, 0x22, 0x10, 0x26, 0x10, 0x22, 0x38, 0x2E, 0x38, 0x22, 0x38, 0x26, 0x38, 0x22, 0x38, 0x2E, 0x38, 0x22, 0x38, 0x3E, 0x00, 0x00},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -32,8 +32,9 @@
#include <cstring>
using namespace portapack;
using namespace ui;
namespace ui {
namespace ui::external_app::antenna_length {
void WhipCalcView::focus() {
field_frequency.focus();
@@ -176,4 +177,4 @@ void WhipCalcView::load_antenna_db() {
void WhipCalcView::add_default_antenna() {
antenna_db.push_back({"ANT500", {185, 315, 450, 586, 724, 862}}); // store a default ant500
}
} // namespace ui
} // namespace ui::external_app::antenna_length
@@ -31,7 +31,10 @@
#include "string_format.hpp"
#include <vector>
namespace ui {
using namespace ui;
namespace ui::external_app::antenna_length {
class WhipCalcView : public View {
public:
WhipCalcView(NavigationView& nav);
@@ -90,6 +93,6 @@ class WhipCalcView : public View {
"Back"};
};
} /* namespace ui */
} // namespace ui::external_app::antenna_length
#endif /*__UI_WHIPCALC__*/
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2024 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_app_manager.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::app_manager {
void initialize_app(ui::NavigationView& nav) {
nav.push<AppManagerView>();
}
} // namespace ui::external_app::app_manager
extern "C" {
__attribute__((section(".external_app.app_app_manager.application_information"), used)) application_information_t _application_information_app_manager = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::app_manager::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "AppManager",
/*.bitmap_data = */ {
0xE0,
0x00,
0x10,
0x01,
0x10,
0x01,
0x1F,
0x1F,
0x01,
0x10,
0x01,
0x10,
0x01,
0x10,
0x01,
0x70,
0x01,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x70,
0xE1,
0x10,
0x11,
0x11,
0x11,
0x11,
0x1F,
0x1F,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::SETTINGS,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -0,0 +1,275 @@
/*
* copyleft spammingdramaqueen
*
* 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_app_manager.hpp"
#include "ui_navigation.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "file.hpp"
namespace fs = std::filesystem;
#include "string_format.hpp"
#include "file_reader.hpp"
using namespace portapack;
namespace ui::external_app::app_manager {
/* AppManagerView **************************************/
/* | inner apps | external apps |
* ------------------------------
* | id | appCallName |
* | displayName|appFriendlyName|
*/
AppManagerView::AppManagerView(NavigationView& nav)
: nav_{nav} {
add_children({&labels,
&menu_view,
&text_app_info,
&button_hide_unhide,
&button_clean_hide,
&button_set_cancel_autostart,
&button_clean_autostart});
menu_view.set_parent_rect({0, 2 * 8, screen_width, 24 * 8});
menu_view.on_highlight = [this]() {
if (menu_view.highlighted_index() >= app_list_index) {
text_app_info.set("");
return;
}
std::string info;
auto app_name = get_app_info(menu_view.highlighted_index(), true);
auto app_id = get_app_info(menu_view.highlighted_index(), false);
info += "Hidden:";
if (is_blacklisted(app_name)) {
info += "Yes ";
} else {
info += "No ";
}
info += "Autostart:";
if (is_autostart_app(app_id)) {
info += "Yes";
} else {
info += "No";
}
if (info.empty()) {
info = "Highlight an app";
}
text_app_info.set(info);
};
button_hide_unhide.on_select = [this](Button&) {
hide_unhide_app();
refresh_list();
};
button_clean_hide.on_select = [this](Button&) {
clean_blacklist();
refresh_list();
};
button_set_cancel_autostart.on_select = [this](Button&) {
set_unset_autostart_app();
refresh_list();
};
button_clean_autostart.on_select = [this](Button&) {
unset_auto_start();
refresh_list();
};
refresh_list();
}
void AppManagerView::refresh_list() {
auto previous_cursor_index = menu_view.highlighted_index();
app_list_index = 0;
menu_view.clear();
size_t index = 0;
auto padding = [](const std::string& str) {
return str.length() < 25 ? std::string(25 - str.length(), ' ') + '' : "";
// that weird char is a icon in portapack's font base that looks like a switch, which i use to indicate the auto start
};
for (auto& app : NavigationView::appList) {
if (app.id == nullptr) continue;
app_list_index++;
menu_view.add_item({app.displayName + std::string(is_autostart_app(app.id) ? padding(app.displayName) : ""),
app.iconColor,
app.icon,
[this, app_id = std::string(app.id)](KeyEvent) {
button_hide_unhide.focus();
}});
}
ExternalItemsMenuLoader::load_all_external_items_callback([this, &index, &padding](ui::AppInfoConsole& app) {
if (app.appCallName == nullptr) return;
app_list_index++;
menu_view.add_item({app.appFriendlyName + std::string(is_autostart_app(app.appCallName) ? padding(app.appFriendlyName) : ""),
ui::Color::light_grey(),
&bitmap_icon_sdcard,
[this, app_id = std::string(app.appCallName)](KeyEvent) {
button_hide_unhide.focus();
}});
});
menu_view.set_highlighted(previous_cursor_index);
}
void AppManagerView::focus() {
menu_view.focus();
}
void AppManagerView::hide_app() {
std::vector<std::string> blacklist;
get_blacklist(blacklist);
blacklist.push_back(get_app_info(menu_view.highlighted_index(), true));
write_blacklist(blacklist);
}
void AppManagerView::unhide_app() {
std::vector<std::string> blacklist;
get_blacklist(blacklist);
blacklist.erase(std::remove(blacklist.begin(), blacklist.end(), get_app_info(menu_view.highlighted_index(), true)), blacklist.end());
write_blacklist(blacklist);
}
void AppManagerView::hide_unhide_app() {
if (is_blacklisted(get_app_info(menu_view.highlighted_index(), true)))
unhide_app();
else
hide_app();
}
void AppManagerView::get_blacklist(std::vector<std::string>& blacklist) {
File f;
auto error = f.open(u"SETTINGS/blacklist");
if (error)
return;
auto reader = FileLineReader(f);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue;
blacklist.push_back(trim(line));
}
}
void AppManagerView::write_blacklist(const std::vector<std::string>& blacklist) {
File f;
auto error = f.create(u"SETTINGS/blacklist");
if (error)
return;
for (const auto& app_name : blacklist) {
auto line = app_name + "\n";
f.write(line.c_str(), line.length());
}
}
void AppManagerView::clean_blacklist() {
std::vector<std::string> blacklist = {};
write_blacklist(blacklist);
}
bool AppManagerView::is_blacklisted(const std::string& app_name) {
std::vector<std::string> blacklist;
get_blacklist(blacklist);
return std::find(blacklist.begin(), blacklist.end(), app_name) != blacklist.end();
}
void AppManagerView::set_auto_start() {
auto app_index = menu_view.highlighted_index();
if (app_index >= app_list_index) return;
auto id_aka_app_call_name = get_app_info(app_index, false);
autostart_app = id_aka_app_call_name;
refresh_list();
}
void AppManagerView::unset_auto_start() {
autostart_app = "";
refresh_list();
}
void AppManagerView::set_unset_autostart_app() {
auto app_index = menu_view.highlighted_index();
if (app_index >= app_list_index) return;
auto id_aka_friendly_name = get_app_info(app_index, false);
if (is_autostart_app(id_aka_friendly_name))
unset_auto_start();
else
set_auto_start();
}
bool AppManagerView::is_autostart_app(const char* id_aka_friendly_name) {
return autostart_app == std::string(id_aka_friendly_name);
}
bool AppManagerView::is_autostart_app(const std::string& id_aka_friendly_name) {
return autostart_app == id_aka_friendly_name;
}
std::string AppManagerView::get_app_info(uint16_t index, bool is_display_name) {
size_t current_index = 0;
std::string result;
for (auto& app : NavigationView::appList) {
if (app.id == nullptr) continue;
if (current_index == index) {
return is_display_name ? std::string(app.displayName) : std::string(app.id);
}
current_index++;
}
ExternalItemsMenuLoader::load_all_external_items_callback([&current_index, &index, &result, &is_display_name](ui::AppInfoConsole& app) {
if (app.appCallName == nullptr) return;
if (current_index == index) {
result = is_display_name ? app.appFriendlyName : app.appCallName;
}
current_index++;
});
return result;
}
} // namespace ui::external_app::app_manager
@@ -0,0 +1,87 @@
/*
* copyleft spammingdramaqueen
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_APP_MANAGER_H__
#define __UI_APP_MANAGER_H__
#include "ui_navigation.hpp"
namespace ui::external_app::app_manager {
class AppManagerView : public View {
public:
AppManagerView(NavigationView& nav);
std::string title() const override { return "AppMan"; };
private:
NavigationView& nav_;
std::string autostart_app{""};
SettingsStore nav_setting{
"nav"sv,
{{"autostart_app"sv, &autostart_app}}};
void focus() override;
void refresh_list();
uint16_t app_list_index{0};
Labels labels{
{{0 * 8, 0 * 16}, "App list:", Theme::getInstance()->fg_light->foreground}};
MenuView menu_view{};
Text text_app_info{
{0, 27 * 8, screen_width, 16},
"Highlight an app"};
Button button_hide_unhide{
{0, 29 * 8, screen_width / 2 - 1, 32},
"Hide/Show"};
Button button_clean_hide{
{screen_width / 2 + 2, 29 * 8, screen_width / 2 - 2, 32},
"Clean Hidden"};
Button button_set_cancel_autostart{
{0, screen_height - 32 - 16, screen_width / 2 - 1, 32},
"Set Autostart"};
Button button_clean_autostart{
{screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, 32},
"Del Autostart"};
std::string get_app_info(uint16_t index, bool is_display_name);
void get_blacklist(std::vector<std::string>& blacklist);
void write_blacklist(const std::vector<std::string>& blacklist);
bool is_blacklisted(const std::string& app_display_name);
void hide_app();
void unhide_app();
void hide_unhide_app();
void clean_blacklist();
bool is_autostart_app(const std::string& display_name);
void set_auto_start();
void unset_auto_start();
void set_unset_autostart_app();
bool is_autostart_app(const char* id_aka_app_call_name);
};
} // namespace ui::external_app::app_manager
#endif // __UI_APP_MANAGER_H__
+70
View File
@@ -0,0 +1,70 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#include "ui.hpp"
#include "ui_breakout.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::breakout {
void initialize_app(ui::NavigationView& nav) {
nav.push<BreakoutView>();
}
} // namespace ui::external_app::breakout
extern "C" {
__attribute__((section(".external_app.app_breakout.application_information"), used)) application_information_t _application_information_breakout = {
(uint8_t*)0x00000000,
ui::external_app::breakout::initialize_app,
CURRENT_HEADER_VERSION,
VERSION_MD5,
"Breakout",
{
0x00,
0x00,
0x7F,
0x7F,
0x7F,
0x7F,
0x7F,
0x7F,
0x00,
0x00,
0xF7,
0xF7,
0xF7,
0xF7,
0xF7,
0xF7,
0x00,
0x00,
0x7F,
0x7F,
0x7F,
0x7F,
0x7F,
0x7F,
0x00,
0x00,
0xF7,
0xF7,
0xF7,
0xF7,
0xF7,
0xF7,
},
ui::Color::green().v,
app_location_t::GAMES,
-1,
{0, 0, 0, 0},
0x00000000,
};
} // namespace ui::external_app::breakout
+550
View File
@@ -0,0 +1,550 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#include "ui_breakout.hpp"
namespace ui::external_app::breakout {
Ticker game_timer;
int paddle_x = (SCREEN_WIDTH - PADDLE_WIDTH) / 2;
float ball_x = SCREEN_WIDTH / 2;
float ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1;
float ball_dx = 1.5f;
float ball_dy = -2.0f;
int score = 0;
int lives = 3;
int level = 1;
int game_state = STATE_MENU;
bool initialized = false;
bool ball_attached = true;
unsigned int brick_count = 0;
bool menu_initialized = false;
bool blink_state = true;
uint32_t blink_counter = 0;
int16_t prompt_x = 0;
bool gameover_initialized = false;
bool gameover_blink_state = true;
uint32_t gameover_blink_counter = 0;
int16_t restart_x = 0;
bool bricks[BRICK_ROWS][BRICK_COLS];
int brick_colors[BRICK_ROWS];
const Color pp_colors[] = {
Color::white(),
Color::blue(),
Color::yellow(),
Color::purple(),
Color::green(),
Color::red(),
Color::magenta(),
Color::orange(),
Color::black(),
};
Painter painter;
bool but_RIGHT = false;
bool but_LEFT = false;
bool but_SELECT = false;
static Callback game_update_callback = nullptr;
static uint32_t game_update_timeout = 0;
static uint32_t game_update_counter = 0;
void cls() {
painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black());
}
void background(int color) {
(void)color;
}
void fillrect(int x1, int y1, int x2, int y2, int color) {
painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
}
void rect(int x1, int y1, int x2, int y2, int color) {
painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
}
void check_game_timer() {
if (game_update_callback) {
if (++game_update_counter >= game_update_timeout) {
game_update_counter = 0;
game_update_callback();
}
}
}
void Ticker::attach(Callback func, double delay_sec) {
game_update_callback = func;
game_update_timeout = delay_sec * 60;
}
void Ticker::detach() {
game_update_callback = nullptr;
}
void game_timer_check() {
if (game_state == STATE_PLAYING) {
update_game();
} else if (game_state == STATE_MENU) {
show_menu();
} else if (game_state == STATE_GAME_OVER) {
show_game_over();
}
}
void init_game() {
paddle_x = (SCREEN_WIDTH - PADDLE_WIDTH) / 2;
score = 0;
lives = 3;
level = 1;
brick_colors[0] = Red;
brick_colors[1] = Orange;
brick_colors[2] = Yellow;
brick_colors[3] = Green;
brick_colors[4] = Purple;
init_level();
game_state = STATE_MENU;
menu_initialized = false;
blink_state = true;
blink_counter = 0;
}
void init_level() {
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1;
float speed_multiplier = (level == 1) ? 1.0f : 1.0f + ((level - 1) * BALL_SPEED_INCREASE);
ball_dx = (ball_dx > 0 ? 1.5f : -1.5f) * speed_multiplier;
ball_dy = -2.0f * speed_multiplier;
ball_attached = true;
brick_count = 0;
for (int row = 0; row < BRICK_ROWS; row++) {
for (int col = 0; col < BRICK_COLS; col++) {
bricks[row][col] = true;
brick_count++;
}
}
}
void draw_screen() {
cls();
background(COLOR_BACKGROUND);
draw_borders();
draw_bricks();
draw_paddle();
draw_ball();
draw_score();
draw_lives();
draw_level();
}
void draw_borders() {
rect(0, GAME_AREA_TOP - 1, SCREEN_WIDTH, GAME_AREA_TOP, COLOR_BORDER);
}
void draw_bricks() {
for (int row = 0; row < BRICK_ROWS; row++) {
for (int col = 0; col < BRICK_COLS; col++) {
if (bricks[row][col]) {
int x = col * (BRICK_WIDTH + BRICK_GAP);
int y = GAME_AREA_TOP + row * (BRICK_HEIGHT + BRICK_GAP) + 5;
fillrect(x, y, x + BRICK_WIDTH, y + BRICK_HEIGHT, brick_colors[row]);
rect(x, y, x + BRICK_WIDTH, y + BRICK_HEIGHT, Black);
}
}
}
}
void draw_paddle() {
fillrect(paddle_x, PADDLE_Y, paddle_x + PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, COLOR_PADDLE);
}
void draw_ball() {
fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BALL);
}
void draw_score() {
auto style = *ui::Theme::getInstance()->fg_green;
painter.draw_string({5, 10}, style, "Score: " + std::to_string(score));
}
void draw_lives() {
auto style = *ui::Theme::getInstance()->fg_red;
painter.draw_string({5, 30}, style, "Lives: " + std::to_string(lives));
}
void draw_level() {
auto style = *ui::Theme::getInstance()->fg_yellow;
painter.draw_string({80, 30}, style, "Level: " + std::to_string(level));
}
void move_paddle_left() {
if (paddle_x > 0) {
fillrect(paddle_x, PADDLE_Y, paddle_x + PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, COLOR_BACKGROUND);
if (ball_attached) {
fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BACKGROUND);
}
paddle_x -= 10;
if (paddle_x < 0) paddle_x = 0;
if (ball_attached) {
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
}
draw_paddle();
if (ball_attached) {
draw_ball();
}
}
}
void move_paddle_right() {
if (paddle_x < SCREEN_WIDTH - PADDLE_WIDTH) {
fillrect(paddle_x, PADDLE_Y, paddle_x + PADDLE_WIDTH, PADDLE_Y + PADDLE_HEIGHT, COLOR_BACKGROUND);
if (ball_attached) {
fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BACKGROUND);
}
paddle_x += 10;
if (paddle_x > SCREEN_WIDTH - PADDLE_WIDTH) paddle_x = SCREEN_WIDTH - PADDLE_WIDTH;
if (ball_attached) {
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
}
draw_paddle();
if (ball_attached) {
draw_ball();
}
}
}
void launch_ball() {
if (ball_attached) {
ball_attached = false;
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1;
float speed_multiplier = (level == 1) ? 1.0f : 1.0f + ((level - 1) * BALL_SPEED_INCREASE);
ball_dx = 1.5f * speed_multiplier;
ball_dy = -2.0f * speed_multiplier;
}
}
void update_game() {
if (ball_attached) {
return;
}
fillrect(ball_x, ball_y, ball_x + BALL_SIZE, ball_y + BALL_SIZE, COLOR_BACKGROUND);
float next_ball_y = ball_y + ball_dy;
if (next_ball_y > GAME_AREA_BOTTOM) {
lives--;
draw_lives();
if (lives <= 0) {
handle_game_over();
} else {
ball_attached = true;
ball_x = paddle_x + (PADDLE_WIDTH / 2) - (BALL_SIZE / 2);
ball_y = GAME_AREA_BOTTOM - PADDLE_HEIGHT - BALL_SIZE - 1;
draw_ball();
}
return;
}
ball_x += ball_dx;
ball_y = next_ball_y;
if (ball_x < 0) {
ball_x = 0;
ball_dx = -ball_dx;
} else if (ball_x > SCREEN_WIDTH - BALL_SIZE) {
ball_x = SCREEN_WIDTH - BALL_SIZE;
ball_dx = -ball_dx;
}
if (ball_y < GAME_AREA_TOP) {
ball_y = GAME_AREA_TOP;
ball_dy = -ball_dy;
}
if (ball_y + BALL_SIZE >= PADDLE_Y && ball_y <= PADDLE_Y + PADDLE_HEIGHT) {
if (ball_x + BALL_SIZE >= paddle_x && ball_x <= paddle_x + PADDLE_WIDTH) {
ball_y = PADDLE_Y - BALL_SIZE;
float hit_position = (ball_x + (BALL_SIZE / 2)) - paddle_x;
float angle = (hit_position / PADDLE_WIDTH) - 0.5f;
ball_dx = angle * 4.0f;
if (ball_dx > -0.5f && ball_dx < 0.5f) {
ball_dx = (ball_dx > 0) ? 0.5f : -0.5f;
}
ball_dy = -ball_dy;
}
}
check_collisions();
draw_ball();
if (check_level_complete()) {
next_level();
}
}
void check_collisions() {
int grid_x = ball_x / (BRICK_WIDTH + BRICK_GAP);
int grid_y = (ball_y - GAME_AREA_TOP - 5) / (BRICK_HEIGHT + BRICK_GAP);
for (int row = grid_y - 1; row <= grid_y + 1; row++) {
for (int col = grid_x - 1; col <= grid_x + 1; col++) {
if (row >= 0 && row < BRICK_ROWS && col >= 0 && col < BRICK_COLS) {
if (bricks[row][col] && check_brick_collision(row, col)) {
return;
}
}
}
}
}
bool check_brick_collision(int row, int col) {
int brick_x = col * (BRICK_WIDTH + BRICK_GAP);
int brick_y = GAME_AREA_TOP + row * (BRICK_HEIGHT + BRICK_GAP) + 5;
if (ball_x + BALL_SIZE >= brick_x && ball_x <= brick_x + BRICK_WIDTH &&
ball_y + BALL_SIZE >= brick_y && ball_y <= brick_y + BRICK_HEIGHT) {
fillrect(brick_x, brick_y, brick_x + BRICK_WIDTH, brick_y + BRICK_HEIGHT, COLOR_BACKGROUND);
bricks[row][col] = false;
brick_count--;
score += (5 - row) * 10;
draw_score();
float center_x = brick_x + BRICK_WIDTH / 2;
float center_y = brick_y + BRICK_HEIGHT / 2;
float ball_center_x = ball_x + BALL_SIZE / 2;
float ball_center_y = ball_y + BALL_SIZE / 2;
float dx = std::abs(ball_center_x - center_x);
float dy = std::abs(ball_center_y - center_y);
if (dx * BRICK_HEIGHT > dy * BRICK_WIDTH) {
ball_dx = -ball_dx;
} else {
ball_dy = -ball_dy;
}
return true;
}
return false;
}
bool check_level_complete() {
return brick_count == 0;
}
void next_level() {
level++;
init_level();
draw_screen();
}
void handle_game_over() {
game_state = STATE_GAME_OVER;
gameover_initialized = false;
show_game_over();
}
void init_menu() {
cls();
background(COLOR_BACKGROUND);
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
auto style_blue = *ui::Theme::getInstance()->fg_blue;
auto style_cyan = *ui::Theme::getInstance()->fg_cyan;
int16_t screen_width = 240;
int16_t title_x = (screen_width - 17 * 8) / 2;
int16_t divider_width = 24 * 8;
int16_t divider_x = (screen_width - divider_width) / 2;
int16_t instruction_width = 22 * 8;
int16_t instruction_x = (screen_width - instruction_width) / 2;
int16_t prompt_width = 16 * 8;
prompt_x = (screen_width - prompt_width) / 2;
painter.fill_rectangle({0, 30, screen_width, 30}, Color::black());
painter.draw_string({title_x + 2, 42}, style_yellow, "*** BREAKOUT ***");
painter.draw_string({divider_x, 70}, style_blue, "========================");
painter.fill_rectangle({instruction_x - 5, 110, instruction_width + 10, 70}, Color::black());
painter.draw_rectangle({instruction_x - 5, 110, instruction_width + 10, 70}, Color::white());
painter.draw_string({instruction_x, 120}, style_cyan, " ROTARY: MOVE PADDLE");
painter.draw_string({instruction_x, 150}, style_cyan, " SELECT: START/LAUNCH");
painter.draw_string({divider_x, 190}, style_blue, "========================");
menu_initialized = true;
}
void show_menu() {
if (!menu_initialized) {
init_menu();
}
auto style_red = *ui::Theme::getInstance()->fg_red;
if (++blink_counter >= 30) {
blink_counter = 0;
blink_state = !blink_state;
painter.fill_rectangle({prompt_x - 2, 228, 16 * 8 + 4, 20}, Color::black());
if (blink_state) {
painter.draw_string({prompt_x, 230}, style_red, "* PRESS SELECT *");
}
}
}
void init_game_over() {
cls();
background(COLOR_BACKGROUND);
auto style_red = *ui::Theme::getInstance()->fg_red;
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
int16_t screen_width = 240;
int16_t title_width = 9 * 8;
int16_t title_x = (screen_width - title_width) / 2;
int16_t score_text_width = (16 + std::to_string(score).length()) * 8;
int16_t score_x = (screen_width - score_text_width) / 2;
painter.draw_rectangle({20, 80, screen_width - 40, 160}, Color::red());
painter.draw_rectangle({22, 82, screen_width - 44, 156}, Color::white());
painter.draw_string({title_x, 100}, style_red, "GAME OVER");
painter.fill_rectangle({40, 140, screen_width - 80, 30}, Color::black());
painter.draw_rectangle({40, 140, screen_width - 80, 30}, Color::yellow());
painter.draw_string({score_x, 150}, style_yellow, " FINAL SCORE: " + std::to_string(score));
int16_t restart_width = 12 * 8;
restart_x = (screen_width - restart_width) / 2;
gameover_initialized = true;
gameover_blink_state = true;
gameover_blink_counter = 0;
}
void show_game_over() {
if (!gameover_initialized) {
init_game_over();
}
auto style_green = *ui::Theme::getInstance()->fg_green;
if (++gameover_blink_counter >= 30) {
gameover_blink_counter = 0;
gameover_blink_state = !gameover_blink_state;
painter.fill_rectangle({restart_x - 2, 198, 12 * 8 + 4, 20}, Color::black());
if (gameover_blink_state) {
painter.draw_string({restart_x, 200}, style_green, "PRESS SELECT");
}
}
}
void reset_game() {
level = 1;
score = 0;
lives = 3;
game_state = STATE_PLAYING;
init_level();
draw_screen();
gameover_initialized = false;
gameover_blink_state = true;
gameover_blink_counter = 0;
}
BreakoutView::BreakoutView(NavigationView& nav)
: nav_{nav} {
add_children({&dummy});
game_timer.attach(&game_timer_check, 1.0 / 60.0);
}
void BreakoutView::on_show() {
}
void BreakoutView::paint(Painter& painter) {
(void)painter;
if (!initialized) {
initialized = true;
std::srand(LPC_RTC->CTIME0);
init_game();
}
}
void BreakoutView::frame_sync() {
check_game_timer();
set_dirty();
}
bool BreakoutView::on_encoder(const EncoderEvent delta) {
if (game_state == STATE_PLAYING) {
if (delta > 0) {
move_paddle_right();
set_dirty();
} else if (delta < 0) {
move_paddle_left();
set_dirty();
}
}
return true;
}
bool BreakoutView::on_key(const KeyEvent key) {
but_SELECT = (key == KeyEvent::Select);
but_LEFT = (key == KeyEvent::Left);
but_RIGHT = (key == KeyEvent::Right);
if (key == KeyEvent::Select) {
if (game_state == STATE_MENU) {
game_state = STATE_PLAYING;
reset_game();
} else if (game_state == STATE_PLAYING && ball_attached) {
launch_ball();
} else if (game_state == STATE_GAME_OVER) {
reset_game();
}
} else if (key == KeyEvent::Left) {
if (game_state == STATE_PLAYING) {
move_paddle_left();
}
} else if (key == KeyEvent::Right) {
if (game_state == STATE_PLAYING) {
move_paddle_right();
}
}
set_dirty();
return true;
}
} // namespace ui::external_app::breakout
+156
View File
@@ -0,0 +1,156 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#ifndef __UI_BREAKOUT_H__
#define __UI_BREAKOUT_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include "irq_controls.hpp"
#include "random.hpp"
#include "lpc43xx_cpp.hpp"
#include "ui_widget.hpp"
namespace ui::external_app::breakout {
enum {
White,
Blue,
Yellow,
Purple,
Green,
Red,
Maroon,
Orange,
Black,
};
extern const Color pp_colors[];
extern Painter painter;
extern bool but_RIGHT;
extern bool but_LEFT;
extern bool but_SELECT;
void cls();
void background(int color);
void fillrect(int x1, int y1, int x2, int y2, int color);
void rect(int x1, int y1, int x2, int y2, int color);
#define wait(x) chThdSleepMilliseconds(x * 1000)
using Callback = void (*)(void);
void check_game_timer();
class Ticker {
public:
Ticker() = default;
void attach(Callback func, double delay_sec);
void detach();
};
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
#define PADDLE_WIDTH 40
#define PADDLE_HEIGHT 10
#define BALL_SIZE 8
#define BRICK_WIDTH 20
#define BRICK_HEIGHT 10
#define BRICK_ROWS 5
#define BRICK_COLS 10
#define BRICK_GAP 2
#define GAME_AREA_TOP 50
#define GAME_AREA_BOTTOM 310
#define PADDLE_Y (GAME_AREA_BOTTOM - PADDLE_HEIGHT)
#define BALL_SPEED_INCREASE 0.1f
#define STATE_MENU 0
#define STATE_PLAYING 1
#define STATE_GAME_OVER 3
#define COLOR_BACKGROUND Black
#define COLOR_PADDLE Blue
#define COLOR_BALL White
#define COLOR_BORDER White
#define COLOR_BRICK_COLORS \
{ Red, Orange, Yellow, Green, Purple }
extern Ticker game_timer;
extern int paddle_x;
extern float ball_x;
extern float ball_y;
extern float ball_dx;
extern float ball_dy;
extern int score;
extern int lives;
extern int level;
extern int game_state;
extern bool initialized;
extern bool ball_attached;
extern unsigned int brick_count;
extern bool bricks[BRICK_ROWS][BRICK_COLS];
extern int brick_colors[BRICK_ROWS];
void game_timer_check();
void init_game();
void init_level();
void draw_screen();
void draw_bricks();
void draw_paddle();
void draw_ball();
void draw_score();
void draw_lives();
void draw_level();
void draw_borders();
void move_paddle_left();
void move_paddle_right();
void launch_ball();
void update_game();
void check_collisions();
bool check_brick_collision(int row, int col);
void handle_game_over();
void show_menu();
void show_game_over();
bool check_level_complete();
void next_level();
void reset_game();
class BreakoutView : public View {
public:
BreakoutView(NavigationView& nav);
void on_show() override;
std::string title() const override { return "Breakout"; }
void focus() override { dummy.focus(); }
void paint(Painter& painter) override;
void frame_sync();
bool on_encoder(const EncoderEvent event) override;
bool on_key(KeyEvent key) override;
private:
bool initialized = false;
NavigationView& nav_;
Button dummy{
{240, 0, 0, 0},
""};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->frame_sync();
}};
};
} // namespace ui::external_app::breakout
#endif /* __UI_BREAKOUT_H__ */
+9 -28
View File
@@ -81,8 +81,6 @@ void CVSSpamView::start_tx(const uint32_t id) {
return;
}
const uint32_t sample_rate = 250000;
current_file = cvsfiles_dir / file_list[id].filename();
File capture_file;
@@ -99,20 +97,13 @@ void CVSSpamView::start_tx(const uint32_t id) {
return;
}
auto metadata_path = get_metadata_path(current_file);
auto metadata = read_metadata_file(metadata_path);
if (!metadata) {
metadata = capture_metadata{transmitter_model.target_frequency(), sample_rate};
}
auto file_size = capture_file.size();
capture_file.close();
replay_thread.reset();
transmitter_model.disable();
ready_signal = false;
baseband::set_sample_rate(metadata->sample_rate, get_oversample_rate(metadata->sample_rate));
baseband::set_sample_rate(SAMPLE_RATE, get_oversample_rate(SAMPLE_RATE));
auto reader = std::make_unique<FileConvertReader>();
if (auto error = reader->open(current_file)) {
@@ -120,10 +111,7 @@ void CVSSpamView::start_tx(const uint32_t id) {
"Cannot read C16 data.\n"
"Check file format/perms.\n"
"Rate: " +
to_string_dec_uint(metadata->sample_rate) +
"\n"
"Size: " +
to_string_dec_uint(file_size) +
to_string_dec_uint(SAMPLE_RATE) +
"\n"
"Error: " +
std::to_string(static_cast<uint32_t>(error)));
@@ -132,9 +120,9 @@ void CVSSpamView::start_tx(const uint32_t id) {
progressbar.set_value(0);
transmitter_model.set_sampling_rate(get_actual_sample_rate(metadata->sample_rate));
transmitter_model.set_baseband_bandwidth(metadata->sample_rate <= 500000 ? 1750000 : 2500000);
transmitter_model.set_target_frequency(metadata->center_frequency);
transmitter_model.set_sampling_rate(get_actual_sample_rate(SAMPLE_RATE));
transmitter_model.set_baseband_bandwidth(SAMPLE_RATE <= 500000 ? 1750000 : 2500000);
transmitter_model.set_target_frequency(TARGET_FREQUENCY);
transmitter_model.enable();
chThdSleepMilliseconds(100);
@@ -164,7 +152,6 @@ void CVSSpamView::start_random_tx() {
lfsr_v = lfsr_iterate(lfsr_v);
size_t random_index = lfsr_v % file_list.size();
const uint32_t sample_rate = 250000;
current_file = cvsfiles_dir / file_list[random_index].filename();
File capture_file;
@@ -174,19 +161,13 @@ void CVSSpamView::start_random_tx() {
return;
}
auto metadata_path = get_metadata_path(current_file);
auto metadata = read_metadata_file(metadata_path);
if (!metadata) {
metadata = capture_metadata{transmitter_model.target_frequency(), sample_rate};
}
capture_file.close();
replay_thread.reset();
transmitter_model.disable();
ready_signal = false;
baseband::set_sample_rate(metadata->sample_rate, get_oversample_rate(metadata->sample_rate));
baseband::set_sample_rate(SAMPLE_RATE, get_oversample_rate(SAMPLE_RATE));
auto reader = std::make_unique<FileConvertReader>();
if (auto error = reader->open(current_file)) {
@@ -196,9 +177,9 @@ void CVSSpamView::start_random_tx() {
progressbar.set_value(0);
transmitter_model.set_sampling_rate(get_actual_sample_rate(metadata->sample_rate));
transmitter_model.set_baseband_bandwidth(metadata->sample_rate <= 500000 ? 1750000 : 2500000);
transmitter_model.set_target_frequency(metadata->center_frequency);
transmitter_model.set_sampling_rate(get_actual_sample_rate(SAMPLE_RATE));
transmitter_model.set_baseband_bandwidth(SAMPLE_RATE <= 500000 ? 1750000 : 2500000);
transmitter_model.set_target_frequency(TARGET_FREQUENCY);
transmitter_model.enable();
chThdSleepMilliseconds(100);
+3
View File
@@ -16,6 +16,9 @@ using namespace portapack;
namespace ui::external_app::cvs_spam {
constexpr uint32_t SAMPLE_RATE = 250000;
constexpr uint64_t TARGET_FREQUENCY = 433920000;
class CVSSpamView : public View {
public:
explicit CVSSpamView(NavigationView& nav);
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_debug_pmem.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::debug_pmem {
void initialize_app(ui::NavigationView& nav) {
nav.push<DebugDumpView>();
}
} // namespace ui::external_app::debug_pmem
extern "C" {
__attribute__((section(".external_app.app_debug_pmem.application_information"), used)) application_information_t _application_information_debug_pmem = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::debug_pmem::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "DebugPMem",
/*.bitmap_data = */ {
0x54,
0x15,
0x54,
0x15,
0xFF,
0x7F,
0xFC,
0x1F,
0xFF,
0x7F,
0xCC,
0x19,
0xAF,
0x7A,
0x6C,
0x1B,
0xEF,
0x7B,
0xEC,
0x1B,
0xFF,
0x7F,
0xFC,
0x1F,
0xFF,
0x7F,
0x54,
0x15,
0x54,
0x15,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::DEBUG,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -0,0 +1,209 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_debug_pmem.hpp"
namespace ui::external_app::debug_pmem {
// Dump pmem, receiver and transmitter models internals in human readable format
bool DebugDumpView::debug_dump_func() {
std::string debug_dir = "DEBUG";
std::filesystem::path filename{};
File pmem_dump_file{};
// create new dump file name and DEBUG directory
ensure_directory(debug_dir);
filename = next_filename_matching_pattern(debug_dir + "/DEBUG_DUMP_????.TXT");
if (filename.empty()) {
dump_output.set("COULD NOT GET DUMP NAME !");
dump_output.set_style(ui::Theme::getInstance()->fg_red);
return false;
}
// dump data fo filename
auto error = pmem_dump_file.create(filename);
if (error) {
dump_output.set("ERROR DUMPING " + filename.filename().string() + " !");
dump_output.set_style(ui::Theme::getInstance()->fg_red);
return false;
}
pmem_dump_file.write_line("FW version: " VERSION_STRING);
pmem_dump_file.write_line("Ext APPS version req'd: 0x" + to_string_hex(VERSION_MD5));
pmem_dump_file.write_line("GCC version: " + to_string_dec_int(__GNUC__) + "." + to_string_dec_int(__GNUC_MINOR__) + "." + to_string_dec_int(__GNUC_PATCHLEVEL__));
// firmware checksum
pmem_dump_file.write_line("Firmware calculated checksum: 0x" + to_string_hex(simple_checksum(FLASH_STARTING_ADDRESS, FLASH_ROM_SIZE), 8));
// write persistent memory
pmem_dump_file.write_line("\n[Persistent Memory]");
// full variables
pmem_dump_file.write_line("structure_version: 0x" + to_string_hex(get_data_structure_version(), 8));
pmem_dump_file.write_line("target_frequency: " + to_string_dec_int(target_frequency()));
pmem_dump_file.write_line("correction_ppb: " + to_string_dec_int(correction_ppb()));
pmem_dump_file.write_line("modem_def_index: " + to_string_dec_uint(get_modem_def_index()));
pmem_dump_file.write_line("serial_format.data_bit: " + to_string_dec_uint(serial_format().data_bits));
pmem_dump_file.write_line("serial_format.parity: " + to_string_dec_uint(serial_format().parity));
pmem_dump_file.write_line("serial_format.stop_bits: " + to_string_dec_uint(serial_format().stop_bits));
pmem_dump_file.write_line("serial_format.bit_order: " + to_string_dec_uint(serial_format().bit_order));
pmem_dump_file.write_line("modem_bw: " + to_string_dec_int(modem_bw()));
pmem_dump_file.write_line("afsk_mark_freq: " + to_string_dec_int(afsk_mark_freq()));
pmem_dump_file.write_line("afsk_space_freq: " + to_string_dec_int(afsk_space_freq()));
pmem_dump_file.write_line("modem_baudrate: " + to_string_dec_int(modem_baudrate()));
pmem_dump_file.write_line("modem_repeat: " + to_string_dec_int(modem_repeat()));
pmem_dump_file.write_line("pocsag_last_address: " + to_string_dec_uint(pocsag_last_address()));
pmem_dump_file.write_line("pocsag_ignore_address: " + to_string_dec_uint(pocsag_ignore_address()));
pmem_dump_file.write_line("tone_mix: " + to_string_dec_uint(tone_mix()));
pmem_dump_file.write_line("hardware_config: " + to_string_dec_uint(config_cpld()));
pmem_dump_file.write_line("recon_config: 0x" + to_string_hex(get_recon_config(), 16));
pmem_dump_file.write_line("recon_repeat_nb: " + to_string_dec_int(recon_repeat_nb()));
pmem_dump_file.write_line("recon_repeat_gain: " + to_string_dec_int(recon_repeat_gain()));
pmem_dump_file.write_line("recon_repeat_delay: " + to_string_dec_int(recon_repeat_delay()));
pmem_dump_file.write_line("converter: " + to_string_dec_int(config_converter()));
pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(config_updown_converter()));
pmem_dump_file.write_line("updown_frequency_rx_correction: " + to_string_dec_int(config_freq_rx_correction_updown()));
pmem_dump_file.write_line("updown_frequency_tx_correction: " + to_string_dec_int(config_freq_tx_correction_updown()));
pmem_dump_file.write_line("lcd_inverted_mode: " + to_string_dec_uint(config_lcd_inverted_mode()));
pmem_dump_file.write_line("converter_frequency_offset: " + to_string_dec_int(config_converter_freq()));
pmem_dump_file.write_line("frequency_rx_correction: " + to_string_dec_uint(config_freq_rx_correction()));
pmem_dump_file.write_line("frequency_tx_correction: " + to_string_dec_uint(config_freq_tx_correction()));
pmem_dump_file.write_line("encoder_dial_sensitivity: " + to_string_dec_uint(encoder_dial_sensitivity()));
pmem_dump_file.write_line("encoder_rate_multiplier: " + to_string_dec_uint(encoder_rate_multiplier()));
pmem_dump_file.write_line("encoder_dial_direction: " + to_string_dec_uint(encoder_dial_direction())); // 0 = normal, 1 = reverse
pmem_dump_file.write_line("config_mode_storage: 0x" + to_string_hex(config_mode_storage_direct(), 8));
pmem_dump_file.write_line("dst_config: 0x" + to_string_hex((uint32_t)config_dst().v, 8));
pmem_dump_file.write_line("fake_brightness_level: " + to_string_dec_uint(fake_brightness_level()));
pmem_dump_file.write_line("menu_color: 0x" + to_string_hex(menu_color().v, 4));
pmem_dump_file.write_line("touchscreen_threshold: " + to_string_dec_uint(touchscreen_threshold()));
// ui_config bits
const auto backlight_timer = portapack::persistent_memory::config_backlight_timer();
pmem_dump_file.write_line("ui_config clkout_freq: " + to_string_dec_uint(clkout_freq()));
pmem_dump_file.write_line("ui_config backlight_timer.timeout_enabled: " + to_string_dec_uint(backlight_timer.timeout_enabled()));
pmem_dump_file.write_line("ui_config backlight_timer.timeout_seconds: " + to_string_dec_uint(backlight_timer.timeout_seconds()));
pmem_dump_file.write_line("ui_config show_gui_return_icon: " + to_string_dec_uint(show_gui_return_icon()));
pmem_dump_file.write_line("ui_config load_app_settings: " + to_string_dec_uint(load_app_settings()));
pmem_dump_file.write_line("ui_config save_app_settings: " + to_string_dec_uint(save_app_settings()));
pmem_dump_file.write_line("ui_config disable_touchscreen: " + to_string_dec_uint(disable_touchscreen()));
pmem_dump_file.write_line("ui_config hide_clock: " + to_string_dec_uint(hide_clock()));
pmem_dump_file.write_line("ui_config clock_with_date: " + to_string_dec_uint(clock_with_date()));
pmem_dump_file.write_line("ui_config clkout_enabled: " + to_string_dec_uint(clkout_enabled()));
pmem_dump_file.write_line("ui_config apply_fake_brightness: " + to_string_dec_uint(apply_fake_brightness()));
pmem_dump_file.write_line("ui_config stealth_mode: " + to_string_dec_uint(stealth_mode()));
pmem_dump_file.write_line("ui_config config_login: " + to_string_dec_uint(config_login()));
pmem_dump_file.write_line("ui_config config_splash: " + to_string_dec_uint(config_splash()));
// ui_config2 bits
pmem_dump_file.write_line("ui_config2 hide_speaker: " + to_string_dec_uint(ui_hide_speaker()));
pmem_dump_file.write_line("ui_config2 hide_converter: " + to_string_dec_uint(ui_hide_converter()));
pmem_dump_file.write_line("ui_config2 hide_stealth: " + to_string_dec_uint(ui_hide_stealth()));
pmem_dump_file.write_line("ui_config2 hide_camera: " + to_string_dec_uint(ui_hide_camera()));
pmem_dump_file.write_line("ui_config2 hide_sleep: " + to_string_dec_uint(ui_hide_sleep()));
pmem_dump_file.write_line("ui_config2 hide_bias_tee: " + to_string_dec_uint(ui_hide_bias_tee()));
pmem_dump_file.write_line("ui_config2 hide_clock: " + to_string_dec_uint(ui_hide_clock()));
pmem_dump_file.write_line("ui_config2 hide_sd_card: " + to_string_dec_uint(ui_hide_sd_card()));
pmem_dump_file.write_line("ui_config2 hide_mute: " + to_string_dec_uint(ui_hide_mute()));
pmem_dump_file.write_line("ui_config2 hide_fake_brightness: " + to_string_dec_uint(ui_hide_fake_brightness()));
pmem_dump_file.write_line("ui_config2 hide_battery_icon: " + to_string_dec_uint(ui_hide_battery_icon()));
pmem_dump_file.write_line("ui_config2 hide_numeric_battery: " + to_string_dec_uint(ui_hide_numeric_battery()));
pmem_dump_file.write_line("ui_config2 theme_id: " + to_string_dec_uint(ui_theme_id()));
pmem_dump_file.write_line("ui_config2 override_batt_calc: " + to_string_dec_uint(ui_override_batt_calc()));
pmem_dump_file.write_line("ui_config2 button_repeat_delay: " + to_string_dec_uint(ui_button_repeat_delay()));
pmem_dump_file.write_line("ui_config2 button_repeat_speed: " + to_string_dec_uint(ui_button_repeat_speed()));
pmem_dump_file.write_line("ui_config2 button_long_press_delay: " + to_string_dec_uint(ui_button_long_press_delay()));
// misc_config bits
pmem_dump_file.write_line("misc_config config_audio_mute: " + to_string_dec_int(config_audio_mute()));
pmem_dump_file.write_line("misc_config config_speaker_disable: " + to_string_dec_int(config_speaker_disable()));
pmem_dump_file.write_line("misc_config config_disable_external_tcxo: " + to_string_dec_uint(config_disable_external_tcxo()));
pmem_dump_file.write_line("misc_config config_sdcard_high_speed_io: " + to_string_dec_uint(config_sdcard_high_speed_io()));
pmem_dump_file.write_line("misc_config config_disable_config_mode: " + to_string_dec_uint(config_disable_config_mode()));
pmem_dump_file.write_line("misc_config beep_on_packets: " + to_string_dec_int(beep_on_packets()));
// receiver_model
pmem_dump_file.write_line("\n[Receiver Model]");
pmem_dump_file.write_line("target_frequency: " + to_string_dec_uint(receiver_model.target_frequency()));
pmem_dump_file.write_line("frequency_step: " + to_string_dec_uint(receiver_model.frequency_step()));
pmem_dump_file.write_line("lna: " + to_string_dec_int(receiver_model.lna()));
pmem_dump_file.write_line("vga: " + to_string_dec_int(receiver_model.vga()));
pmem_dump_file.write_line("rf_amp: " + to_string_dec_int(receiver_model.rf_amp()));
pmem_dump_file.write_line("baseband_bandwidth: " + to_string_dec_uint(receiver_model.baseband_bandwidth()));
pmem_dump_file.write_line("sampling_rate: " + to_string_dec_uint(receiver_model.sampling_rate()));
switch (receiver_model.modulation()) {
case ReceiverModel::Mode::AMAudio:
pmem_dump_file.write_line("modulation: Mode::AMAudio");
break;
case ReceiverModel::Mode::NarrowbandFMAudio:
pmem_dump_file.write_line("modulation: Mode::NarrowbandFMAudio");
break;
case ReceiverModel::Mode::WidebandFMAudio:
pmem_dump_file.write_line("modulation: Mode::WidebandFMAudio");
break;
case ReceiverModel::Mode::SpectrumAnalysis:
pmem_dump_file.write_line("modulation: Mode::SpectrumAnalysis");
break;
case ReceiverModel::Mode::Capture:
pmem_dump_file.write_line("modulation: Mode::Capture");
break;
case ReceiverModel::Mode::AMAudioFMApt:
pmem_dump_file.write_line("modulation: Mode::AMAudioFMApt");
break;
default:
pmem_dump_file.write_line("modulation: !!unknown mode!!");
break;
}
pmem_dump_file.write_line("headphone_volume.centibel: " + to_string_dec_int(receiver_model.headphone_volume().centibel()));
pmem_dump_file.write_line("normalized_headphone_volume: " + to_string_dec_uint(receiver_model.normalized_headphone_volume()));
pmem_dump_file.write_line("am_configuration: " + to_string_dec_uint(receiver_model.am_configuration()));
pmem_dump_file.write_line("nbfm_configuration: " + to_string_dec_uint(receiver_model.nbfm_configuration()));
pmem_dump_file.write_line("wfm_configuration: " + to_string_dec_uint(receiver_model.wfm_configuration()));
// transmitter_model
pmem_dump_file.write_line("\n[Transmitter Model]");
pmem_dump_file.write_line("target_frequency: " + to_string_dec_uint(transmitter_model.target_frequency()));
pmem_dump_file.write_line("rf_amp: " + to_string_dec_int(transmitter_model.rf_amp()));
pmem_dump_file.write_line("baseband_bandwidth: " + to_string_dec_uint(transmitter_model.baseband_bandwidth()));
pmem_dump_file.write_line("sampling_rate: " + to_string_dec_uint(transmitter_model.sampling_rate()));
pmem_dump_file.write_line("tx_gain: " + to_string_dec_int(transmitter_model.tx_gain()));
pmem_dump_file.write_line("channel_bandwidth: " + to_string_dec_uint(transmitter_model.channel_bandwidth()));
// on screen information
dump_output.set(filename.filename().string() + " DUMPED !");
dump_output.set_style(ui::Theme::getInstance()->fg_green);
return true;
}
DebugDumpView::DebugDumpView(NavigationView& nav)
: nav_{nav} {
add_children({&dump_output,
&button_exit});
debug_dump_func();
button_exit.on_select = [this](Button&) {
nav_.pop();
};
}
void DebugDumpView::focus() {
button_exit.focus();
}
} // namespace ui::external_app::debug_pmem
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_DEBUG_PMEM_APP_H__
#define __UI_DEBUG_PMEM_APP_H__
#include "portapack_persistent_memory.hpp"
#include "ui_flash_utility.hpp"
using namespace portapack;
using namespace portapack::persistent_memory;
namespace ui::external_app::debug_pmem {
class DebugDumpView : public View {
public:
DebugDumpView(NavigationView& nav);
void focus() override;
std::string title() const override { return "DebugPMem"; };
bool debug_dump_func();
private:
NavigationView& nav_;
Text dump_output{
{0 * 8, 19 * 8, 30 * 8, 16},
""};
Button button_exit{
{22 * 8, 34 * 8, 8 * 8, 32},
"Exit"};
};
} // namespace ui::external_app::debug_pmem
#endif /*__UI_DEBUG_PMEM_APP_H__*/
+67
View File
@@ -0,0 +1,67 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#include "ui_doom.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::doom {
void initialize_app(ui::NavigationView& nav) {
nav.push<DoomView>();
}
} // namespace ui::external_app::doom
extern "C" {
__attribute__((section(".external_app.app_doom.application_information"), used)) application_information_t _application_information_doom = {
(uint8_t*)0x00000000,
ui::external_app::doom::initialize_app,
CURRENT_HEADER_VERSION,
VERSION_MD5,
"Doom",
{
0x00,
0x00,
0x00,
0x00,
0x77,
0xDF,
0xFF,
0xDF,
0xD9,
0xFD,
0x89,
0xF8,
0x89,
0xE8,
0x89,
0xA8,
0x89,
0xA8,
0xD9,
0xAD,
0x79,
0xAF,
0x2D,
0xAA,
0x07,
0xA8,
0x03,
0xA0,
0x01,
0x80,
0x00,
0x00,
},
ui::Color::red().v,
app_location_t::GAMES,
-1,
{0, 0, 0, 0},
0x00000000,
};
}
File diff suppressed because it is too large Load Diff
+84
View File
@@ -0,0 +1,84 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#ifndef __UI_DOOM_H__
#define __UI_DOOM_H__
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "message.hpp"
namespace ui::external_app::doom {
using Callback = void (*)();
class Ticker {
public:
Ticker() {}
void attach(Callback func, double delay_sec) {
game_update_callback = func;
game_update_timeout = delay_sec * 60;
}
void detach() {
game_update_callback = nullptr;
}
private:
Callback game_update_callback = nullptr;
uint32_t game_update_timeout = 0;
};
extern ui::Painter painter;
enum {
White,
Blue,
Yellow,
Purple,
Green,
Red,
Maroon,
Orange,
Black,
};
static const Color pp_colors[] = {
Color::white(),
Color::blue(),
Color::yellow(),
Color::purple(),
Color::green(),
Color::red(),
Color::magenta(),
Color::orange(),
Color::black(),
};
class DoomView : public View {
public:
DoomView(NavigationView& nav);
void on_show() override;
std::string title() const override { return "Doom"; }
void focus() override { dummy.focus(); }
void paint(Painter& painter) override;
void frame_sync();
bool on_key(const KeyEvent key) override;
private:
NavigationView& nav_;
Button dummy{{240, 0, 0, 0}, ""};
bool initialized{false};
bool prev_velocity_moving{false};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->frame_sync();
}};
};
} // namespace ui::external_app::doom
#endif /*__UI_DOOM_H__*/
+81 -10
View File
@@ -1,9 +1,5 @@
set(EXTCPPSRC
#tetris
external/tetris/main.cpp
external/tetris/ui_tetris.cpp
#afsk_rx
external/afsk_rx/main.cpp
external/afsk_rx/ui_afsk_rx.cpp
@@ -59,6 +55,10 @@ set(EXTCPPSRC
external/keyfob/ui_keyfob.cpp
external/keyfob/ui_keyfob.hpp
#tetris
external/tetris/main.cpp
external/tetris/ui_tetris.cpp
#extsensors
external/extsensors/main.cpp
external/extsensors/ui_extsensors.cpp
@@ -107,9 +107,14 @@ set(EXTCPPSRC
external/acars_rx/main.cpp
external/acars_rx/acars_app.cpp
#wefax_rx
external/wefax_rx/main.cpp
external/wefax_rx/ui_wefax_rx.cpp
#shoppingcart_lock
external/shoppingcart_lock/main.cpp
external/shoppingcart_lock/shoppingcart_lock.cpp
external/shoppingcart_lock/shoppingcart_lock.cpp
#ookbrute
external/ookbrute/main.cpp
@@ -121,8 +126,8 @@ set(EXTCPPSRC
#cvs_spam
external/cvs_spam/main.cpp
external/cvs_spam/cvs_spam.cpp
external/cvs_spam/cvs_spam.cpp
#flippertx
external/flippertx/main.cpp
external/flippertx/ui_flippertx.cpp
@@ -134,18 +139,70 @@ set(EXTCPPSRC
#mcu_temperature
external/mcu_temperature/main.cpp
external/mcu_temperature/mcu_temperature.cpp
#fmradio
external/fmradio/main.cpp
external/fmradio/ui_fmradio.cpp
#tuner
external/tuner/main.cpp
external/tuner/ui_tuner.cpp
#metronome
external/metronome/main.cpp
external/metronome/ui_metronome.cpp
#app_manager
external/app_manager/main.cpp
external/app_manager/ui_app_manager.cpp
#hopper
external/hopper/main.cpp
external/hopper/ui_hopper.cpp
# whip calculator
external/antenna_length/main.cpp
external/antenna_length/ui_whipcalc.cpp
# wav viewer
external/wav_view/main.cpp
external/wav_view/ui_view_wav.cpp
# wipe sdcard
external/sd_wipe/main.cpp
external/sd_wipe/ui_sd_wipe.cpp
# playlist editor
external/playlist_editor/main.cpp
external/playlist_editor/ui_playlist_editor.cpp
#snake
external/snake/main.cpp
external/snake/ui_snake.cpp
#stopwatch
external/stopwatch/main.cpp
external/stopwatch/ui_stopwatch.cpp
#breakout
external/breakout/main.cpp
external/breakout/ui_breakout.cpp
#doom
external/doom/main.cpp
external/doom/ui_doom.cpp
#debug_pmem
external/debug_pmem/main.cpp
external/debug_pmem/ui_debug_pmem.cpp
#scanner
external/scanner/main.cpp
external/scanner/ui_scanner.cpp
#level
external/level/main.cpp
external/level/ui_level.cpp
)
set(EXTAPPLIST
@@ -177,6 +234,7 @@ set(EXTAPPLIST
#acars_rx
ookbrute
ook_editor
wefax_rx
shoppingcart_lock
flippertx
remote
@@ -184,4 +242,17 @@ set(EXTAPPLIST
fmradio
tuner
metronome
app_manager
hopper
antenna_length
view_wav
sd_wipe
playlist_editor
snake
stopwatch
breakout
doom
debug_pmem
scanner
level
)
+130 -34
View File
@@ -26,38 +26,52 @@ MEMORY
ram_external_app_afsk_rx (rwx) : org = 0xADB10000, len = 32k
ram_external_app_calculator (rwx) : org = 0xADB20000, len = 32k
ram_external_app_font_viewer(rwx) : org = 0xADB30000, len = 32k
ram_external_app_blespam(rwx) : org = 0xADB40000, len = 32k
ram_external_app_analogtv(rwx) : org = 0xADB50000, len = 32k
ram_external_app_nrf_rx(rwx) : org = 0xADB60000, len = 32k
ram_external_app_coasterp(rwx) : org = 0xADB70000, len = 32k
ram_external_app_lge(rwx) : org = 0xADB80000, len = 32k
ram_external_app_lcr(rwx) : org = 0xADB90000, len = 32k
ram_external_app_jammer(rwx) : org = 0xADBA0000, len = 32k
ram_external_app_gpssim(rwx) : org = 0xADBB0000, len = 32k
ram_external_app_spainter(rwx) : org = 0xADBC0000, len = 32k
ram_external_app_keyfob(rwx) : org = 0xADBD0000, len = 32k
ram_external_app_tetris(rwx) : org = 0xADBE0000, len = 32k
ram_external_app_extsensors(rwx) : org = 0xADBF0000, len = 32k
ram_external_app_foxhunt_rx(rwx) : org = 0xADC00000, len = 32k
ram_external_app_audio_test(rwx) : org = 0xADC10000, len = 32k
ram_external_app_blespam (rwx) : org = 0xADB40000, len = 32k
ram_external_app_analogtv (rwx) : org = 0xADB50000, len = 32k
ram_external_app_nrf_rx (rwx) : org = 0xADB60000, len = 32k
ram_external_app_coasterp (rwx) : org = 0xADB70000, len = 32k
ram_external_app_lge (rwx) : org = 0xADB80000, len = 32k
ram_external_app_lcr (rwx) : org = 0xADB90000, len = 32k
ram_external_app_jammer (rwx) : org = 0xADBA0000, len = 32k
ram_external_app_gpssim (rwx) : org = 0xADBB0000, len = 32k
ram_external_app_spainter (rwx) : org = 0xADBC0000, len = 32k
ram_external_app_keyfob (rwx) : org = 0xADBD0000, len = 32k
ram_external_app_tetris (rwx) : org = 0xADBE0000, len = 32k
ram_external_app_extsensors (rwx) : org = 0xADBF0000, len = 32k
ram_external_app_foxhunt_rx (rwx) : org = 0xADC00000, len = 32k
ram_external_app_audio_test (rwx) : org = 0xADC10000, len = 32k
ram_external_app_wardrivemap(rwx) : org = 0xADC20000, len = 32k
ram_external_app_tpmsrx(rwx) : org = 0xADC30000, len = 32k
ram_external_app_protoview(rwx) : org = 0xADC40000, len = 32k
ram_external_app_adsbtx(rwx) : org = 0xADC50000, len = 32k
ram_external_app_morse_tx(rwx) : org = 0xADC60000, len = 32k
ram_external_app_sstvtx(rwx) : org = 0xADC70000, len = 32k
ram_external_app_random_password(rwx) : org = 0xADC80000, len = 32k
ram_external_app_acars_rx(rwx) : org = 0xADC90000, len = 32k
ram_external_app_shoppingcart_lock(rwx) : org = 0xADCA0000, len = 32k
ram_external_app_cvs_spam(rwx) : org = 0xADCB0000, len = 32k
ram_external_app_ookbrute(rwx) : org = 0xADCC0000, len = 32k
ram_external_app_flippertx(rwx) : org = 0xADCD0000, len = 32k
ram_external_app_ook_editor(rwx) : org = 0xADCE0000, len = 32k
ram_external_app_remote(rwx) : org = 0xADCF0000, len = 32k
ram_external_app_mcu_temperature(rwx) : org = 0xADD00000, len = 32k
ram_external_app_fmradio(rwx) : org = 0xADD10000, len = 32k
ram_external_app_tuner(rwx) : org = 0xADD20000, len = 32k
ram_external_app_metronome(rwx) : org = 0xADD30000, len = 32k
ram_external_app_tpmsrx (rwx) : org = 0xADC30000, len = 32k
ram_external_app_protoview (rwx) : org = 0xADC40000, len = 32k
ram_external_app_adsbtx (rwx) : org = 0xADC50000, len = 32k
ram_external_app_morse_tx (rwx) : org = 0xADC60000, len = 32k
ram_external_app_sstvtx (rwx) : org = 0xADC70000, len = 32k
ram_external_app_random_password(rwx): org = 0xADC80000, len = 32k
ram_external_app_acars_rx (rwx) : org = 0xADC90000, len = 32k
ram_external_app_shoppingcart_lock(rwx): org = 0xADCA0000, len = 32k
ram_external_app_cvs_spam (rwx) : org = 0xADCB0000, len = 32k
ram_external_app_ookbrute (rwx) : org = 0xADCC0000, len = 32k
ram_external_app_flippertx (rwx) : org = 0xADCD0000, len = 32k
ram_external_app_ook_editor (rwx) : org = 0xADCE0000, len = 32k
ram_external_app_remote (rwx) : org = 0xADCF0000, len = 32k
ram_external_app_mcu_temperature(rwx): org = 0xADD00000, len = 32k
ram_external_app_fmradio (rwx) : org = 0xADD10000, len = 32k
ram_external_app_tuner (rwx) : org = 0xADD20000, len = 32k
ram_external_app_metronome (rwx) : org = 0xADD30000, len = 32k
ram_external_app_app_manager(rwx) : org = 0xADD40000, len = 32k
ram_external_app_hopper (rwx) : org = 0xADD50000, len = 32k
ram_external_app_antenna_length(rwx): org = 0xADD60000, len = 32k
ram_external_app_view_wav (rwx) : org = 0xADD70000, len = 32k
ram_external_app_sd_wipe (rwx) : org = 0xADD80000, len = 32k
ram_external_app_playlist_editor(rwx): org = 0xADD90000, len = 32k
ram_external_app_snake (rwx) : org = 0xADDA0000, len = 32k
ram_external_app_stopwatch (rwx) : org = 0xADDB0000, len = 32k
ram_external_app_wefax_rx (rwx) : org = 0xADDC0000, len = 32k
ram_external_app_breakout (rwx) : org = 0xADDD0000, len = 32k
ram_external_app_doom (rwx) : org = 0xADDE0000, len = 32k
ram_external_app_debug_pmem (rwx) : org = 0xADDF0000, len = 32k
ram_external_app_scanner (rwx) : org = 0xADE00000, len = 32k
ram_external_app_level (rwx) : org = 0xADE10000, len = 32k
}
SECTIONS
@@ -188,7 +202,6 @@ SECTIONS
*(*ui*external_app*adsbtx*);
} > ram_external_app_adsbtx
.external_app_morse_tx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_morse_tx.application_information));
@@ -254,13 +267,13 @@ SECTIONS
KEEP(*(.external_app.app_mcu_temperature.application_information));
*(*ui*external_app*mcu_temperature*);
} > ram_external_app_mcu_temperature
.external_app_fmradio : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_fmradio.application_information));
*(*ui*external_app*fmradio*);
} > ram_external_app_fmradio
.external_app_tuner : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_tuner.application_information));
@@ -272,4 +285,87 @@ SECTIONS
KEEP(*(.external_app.app_metronome.application_information));
*(*ui*external_app*metronome*);
} > ram_external_app_metronome
.external_app_app_manager : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_app_manager.application_information));
*(*ui*external_app*app_manager*);
} > ram_external_app_app_manager
.external_app_hopper : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_hopper.application_information));
*(*ui*external_app*hopper*);
} > ram_external_app_hopper
.external_app_antenna_length : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_antenna_length.application_information));
*(*ui*external_app*antenna_length*);
} > ram_external_app_antenna_length
.external_app_view_wav : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_view_wav.application_information));
*(*ui*external_app*view_wav*);
} > ram_external_app_view_wav
.external_app_sd_wipe : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_sd_wipe.application_information));
*(*ui*external_app*sd_wipe*);
} > ram_external_app_sd_wipe
.external_app_playlist_editor : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_playlist_editor.application_information));
*(*ui*external_app*playlist_editor*);
} > ram_external_app_playlist_editor
.external_app_snake : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_snake.application_information));
*(*ui*external_app*snake*);
} > ram_external_app_snake
.external_app_stopwatch : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_stopwatch.application_information));
*(*ui*external_app*stopwatch*);
} > ram_external_app_stopwatch
.external_app_wefax_rx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_wefax_rx.application_information));
*(*ui*external_app*wefax_rx*);
} > ram_external_app_wefax_rx
.external_app_breakout : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_breakout.application_information));
*(*ui*external_app*breakout*);
} > ram_external_app_breakout
.external_app_doom : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_doom.application_information));
*(*ui*external_app*doom*);
} > ram_external_app_doom
.external_app_debug_pmem : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_debug_pmem.application_information));
*(*ui*external_app*debug_pmem*);
} > ram_external_app_debug_pmem
.external_app_scanner : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_scanner.application_information));
*(*ui*external_app*scanner*);
} > ram_external_app_scanner
.external_app_level : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_level.application_information));
*(*ui*external_app*level*);
} > ram_external_app_level
}
@@ -89,8 +89,6 @@ class FlipperTxView : public View {
std::unique_ptr<FlipperPlayThread> replay_thread{};
Optional<flippersub_metadata> submeta{};
const std::filesystem::path subghz_dir = u"subghz";
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* const p) {
+2 -1
View File
@@ -108,7 +108,8 @@ class FmRadioView : public View {
128,
0,
false,
Theme::getInstance()->bg_darkest->foreground};
Theme::getInstance()->bg_darkest->foreground,
true};
Button btn_fav_0{{2, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
Button btn_fav_1{{2 + 15 * 8, FMR_BTNGRID_TOP + 0 * 34, 10 * 8, 28}, "---"};
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2023 Mark Thompson
* copyleft Whiterose of the Dark Army
*
* This file is part of PortaPack.
*
@@ -32,7 +33,7 @@ namespace ui::external_app::font_viewer {
/* DebugFontsView *******************************************************/
uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name) {
uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name, bool is_big_font) {
auto char_width{font_style->font.char_width()};
auto char_height{font_style->font.line_height()};
auto cpl{((screen_width / char_width) - 6) & 0xF8}; // Display a multiple of 8 characters per line
@@ -47,7 +48,15 @@ uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const
if ((c % cpl) == 0)
painter.draw_string({0, line_pos}, *font_style, "Ox" + to_string_hex(c + 0x20, 2));
painter.draw_char({((c % cpl) + 5) * char_width, line_pos}, *font_style, (char)(c + 0x20));
Style highlight_style_big = *Theme::getInstance()->fg_red;
Style highlight_style_small = *Theme::getInstance()->bg_important_small;
if (c == field_cursor.value()) {
painter.draw_char({((c % cpl) + 5) * char_width, line_pos},
is_big_font ? highlight_style_big : highlight_style_small,
(char)(c + 0x20));
} else {
painter.draw_char({((c % cpl) + 5) * char_width, line_pos}, *font_style, (char)(c + 0x20));
}
}
return line_pos + char_height;
@@ -56,13 +65,43 @@ uint16_t DebugFontsView::display_font(Painter& painter, uint16_t y_offset, const
void DebugFontsView::paint(Painter& painter) {
int16_t line_pos;
line_pos = display_font(painter, 32, Theme::getInstance()->bg_darkest, "Fixed 8x16");
display_font(painter, line_pos + 16, Theme::getInstance()->bg_darkest_small, "Fixed 5x8");
line_pos = display_font(painter, 32, Theme::getInstance()->bg_darkest, "Fixed 8x16", true);
display_font(painter, line_pos + 16, Theme::getInstance()->bg_darkest_small, "Fixed 5x8", false);
paint_zoomed_text(painter);
}
void DebugFontsView::update_address_text() {
uint8_t ascii_value = field_cursor.value() + 0x20;
text_address.set("0x" + to_string_hex(ascii_value, 2));
}
void DebugFontsView::paint_zoomed_text(Painter& painter) {
if (field_zoom_level.value() == 0) return;
uint8_t cursor_pos = field_cursor.value();
painter.draw_char({screen_width / 2, screen_height / 2},
*Theme::getInstance()->bg_darkest,
(char)(cursor_pos + 0x20), field_zoom_level.value());
}
DebugFontsView::DebugFontsView(NavigationView& nav)
: nav_{nav} {
add_children({&field_cursor,
&text_address,
&field_zoom_level});
set_focusable(true);
field_cursor.on_change = [&](int32_t) {
update_address_text();
set_dirty();
};
field_zoom_level.on_change = [&](int32_t) {
set_dirty();
};
}
void DebugFontsView::focus() {
field_cursor.focus();
}
} /* namespace ui::external_app::font_viewer */
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2023 Mark Thompson
* copyleft Whiterose of the Dark Army
*
* This file is part of PortaPack.
*
@@ -39,10 +40,30 @@ class DebugFontsView : public View {
public:
DebugFontsView(NavigationView& nav);
void paint(Painter& painter) override;
void focus() override;
std::string title() const override { return "Fonts"; };
private:
uint16_t display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name);
uint16_t display_font(Painter& painter, uint16_t y_offset, const Style* font_style, std::string_view font_name, bool is_big_font);
void update_address_text();
void paint_zoomed_text(Painter& painter);
NumberField field_cursor{
{0 * 8, 0 * 8},
4,
{0, 1000},
1,
' '};
NumberField field_zoom_level{
{6 * 8, 0 * 8},
4,
{0, 1000},
1,
' '};
Text text_address{
{screen_width / 2, 0 * 16, screen_width / 2, 16},
"0x20",
};
NavigationView& nav_;
};
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_hopper.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::hopper {
void initialize_app(ui::NavigationView& nav) {
nav.push<HopperView>();
}
} // namespace ui::external_app::hopper
extern "C" {
__attribute__((section(".external_app.app_hopper.application_information"), used)) application_information_t _application_information_hopper = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::hopper::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Hopper",
/*.bitmap_data = */ {
0xE0,
0x07,
0xF8,
0x1F,
0x1C,
0x38,
0x0E,
0x78,
0x06,
0x7C,
0x03,
0xCE,
0x03,
0xC7,
0x83,
0xC3,
0xC3,
0xC1,
0xE3,
0xC0,
0x73,
0xC0,
0x3E,
0x60,
0x1E,
0x70,
0x1C,
0x38,
0xF8,
0x1F,
0xE0,
0x07,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_jammer */ {'P', 'J', 'A', 'M'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+346
View File
@@ -0,0 +1,346 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 euquiq
* copyleft mr.r0b0t from the F society
*
* 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_hopper.hpp"
#include "ui_receiver.hpp"
#include "ui_freqman.hpp"
#include "ui_fileman.hpp"
#include "file_path.hpp"
#include "file_reader.hpp"
#include "convert.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
using namespace portapack;
namespace ui::external_app::hopper {
void HopperView::focus() {
button_load_list.focus();
}
HopperView::~HopperView() {
transmitter_model.disable();
baseband::shutdown();
}
void HopperView::update_freq_list_menu_view() {
menu_freq_list.clear();
uint8_t list_count = freq_list.size();
if (list_count == 0) {
menu_freq_list.add_item({"Add freq or load list",
Theme::getInstance()->bg_darkest->foreground,
nullptr,
nullptr});
}
for (uint8_t i = 0; i < list_count; i++) {
menu_freq_list.add_item({to_string_rounded_freq(freq_list[i], INT8_MAX),
Theme::getInstance()->bg_darkest->foreground,
nullptr,
nullptr});
}
set_dirty();
}
void HopperView::on_retune(const rf::Frequency freq, const uint32_t range) {
if (freq) {
transmitter_model.set_target_frequency(freq);
text_range_number.set(to_string_dec_uint(range, 2));
}
}
void HopperView::set_hopper_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration) {
hopper_channels[i].enabled = true;
hopper_channels[i].width = (width * 0xFFFFFFULL) / 1536000;
hopper_channels[i].center = center;
hopper_channels[i].duration = 30720 * duration;
}
void HopperView::start_tx() {
uint32_t c, i = 0;
bool out_of_ranges = false;
size_t hop_value = options_hop.selected_index_value();
// Disable all channels by default
for (c = 0; c < JAMMER_MAX_CH; c++)
hopper_channels[c].enabled = false;
uint8_t channel_count = freq_list.size();
if (channel_count <= JAMMER_MAX_CH) {
for (c = 0; c < channel_count; c++) {
if (i >= JAMMER_MAX_CH) {
out_of_ranges = true;
break;
}
set_hopper_channel(i, JAMMER_CH_WIDTH, freq_list[c], hop_value);
i++;
}
} else {
out_of_ranges = true;
}
if (!out_of_ranges && i) {
text_range_total.set("/" + to_string_dec_uint(i, 2));
jamming = true;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("STOP");
transmitter_model.set_rf_amp(field_amp.value());
transmitter_model.set_tx_gain(field_gain.value());
transmitter_model.set_baseband_bandwidth(28'000'000); // Although tx is narrowband , let's use Max TX LPF .
transmitter_model.enable();
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
mscounter = 0; // euquiq: Reset internal ms counter for do_timer()
} else {
if (out_of_ranges)
nav_.display_modal("Error", "Jam freq too much.");
}
}
void HopperView::stop_tx() {
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
transmitter_model.disable();
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
jamming = false;
cooling = false;
}
// called each 1/60th of second
void HopperView::on_timer() {
if (++mscounter == 60) {
mscounter = 0;
if (jamming) {
if (cooling) {
if (++seconds >= field_timepause.value()) { // Re-start TX
transmitter_model.set_baseband_bandwidth(28'000'000); // Although tx is narrowband , let's use Max TX LPF .
transmitter_model.enable();
button_transmit.set_text("STOP");
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
int32_t jitter_amount = field_jitter.value();
if (jitter_amount) {
lfsr_v = lfsr_iterate(lfsr_v);
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
mscounter += jitter_amount;
}
cooling = false;
seconds = 0;
}
} else {
if (++seconds >= field_timetx.value()) // Start cooling period:
{
transmitter_model.disable();
button_transmit.set_text("PAUSED");
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
int32_t jitter_amount = field_jitter.value();
if (jitter_amount) {
lfsr_v = lfsr_iterate(lfsr_v);
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
mscounter += jitter_amount;
}
cooling = true;
seconds = 0;
}
}
}
}
}
void HopperView::load_list() {
freq_list.clear();
auto open_view = nav_.push<FileLoadView>(".PHOP");
open_view->push_dir(hopper_dir);
open_view->on_changed = [this](std::filesystem::path path) {
File f;
auto error = f.open(path);
if (error) {
nav_.display_modal("Err", "Can't open.");
update_freq_list_menu_view();
return;
}
freq_list.clear();
auto reader = FileLineReader(f);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue;
rf::Frequency freq;
if (parse_int(line, freq)) {
if (freq_list.size() >= JAMMER_MAX_CH) {
break;
}
freq_list.push_back(freq);
}
}
update_freq_list_menu_view();
};
update_freq_list_menu_view();
}
void HopperView::save_list() {
if (freq_list.empty()) {
nav_.display_modal("Err", "Nothing to save");
return;
}
ensure_directory(hopper_dir);
filename_buffer = "";
text_prompt(
nav_,
filename_buffer,
64,
[this](std::string& value) {
auto path = hopper_dir / (value + ".PHOP");
File f;
auto error = f.create(path);
if (error) {
nav_.display_modal("Err", "Create fail.");
return;
}
for (const auto& freq : freq_list) {
auto freq_str = to_string_dec_uint(freq) + "\n";
f.write(freq_str.c_str(), freq_str.length());
}
text_range_number.set("Saved: " + path.filename().string());
});
}
HopperView::HopperView(
NavigationView& nav)
: nav_{nav} {
// baseband::run_image(portapack::spi_flash::image_tag_jammer);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&menu_freq_list,
&button_load_list,
&button_add_freq,
&button_delete_freq,
&button_save_list,
&button_clear,
&labels,
&options_type,
&text_range_number,
&text_range_total,
&options_speed,
&options_hop,
&field_timetx,
&field_timepause,
&field_jitter,
&field_gain,
&field_amp,
&button_transmit});
options_type.set_selected_index(3); // Rand CW
options_speed.set_selected_index(3); // 10kHz
options_hop.set_selected_index(1); // 50ms
button_transmit.set_style(&style_val);
field_timetx.set_value(30);
field_timepause.set_value(1);
field_gain.set_value(transmitter_model.tx_gain());
field_amp.set_value(transmitter_model.rf_amp());
button_load_list.on_select = [this]() {
load_list();
update_freq_list_menu_view();
};
button_add_freq.on_select = [this]() {
if (freq_list.size() < JAMMER_MAX_CH) {
auto kb_view = nav_.push<FrequencyKeypadView>(0);
kb_view->on_changed = [this](rf::Frequency freq) {
freq_list.push_back(freq);
update_freq_list_menu_view();
};
} else {
nav_.display_modal("Err", "No more.");
}
update_freq_list_menu_view();
};
button_delete_freq.on_select = [this]() {
auto i = menu_freq_list.highlighted_index();
if (i < freq_list.size()) {
freq_list.erase(freq_list.begin() + i);
}
update_freq_list_menu_view();
};
button_save_list.on_select = [this]() {
save_list();
update_freq_list_menu_view();
};
button_clear.on_select = [this]() {
// clang-format off
nav_.display_modal("Del:", "Clean all?\n", YESNO, [this](bool choice) {
if (choice){
freq_list.clear();
update_freq_list_menu_view();
} }, TRUE);
// clang-format on
update_freq_list_menu_view();
};
button_transmit.on_select = [this](Button&) {
if (jamming || cooling)
stop_tx();
else
start_tx();
};
menu_freq_list.on_left = [this]() {
button_load_list.focus();
};
menu_freq_list.on_right = [this]() {
button_load_list.focus();
};
update_freq_list_menu_view();
}
} // namespace ui::external_app::hopper
+233
View File
@@ -0,0 +1,233 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 euquiq
* copyleft mr.r0b0t from the F society
*
* 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_language.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_tabview.hpp"
#include "transmitter_model.hpp"
#include "message.hpp"
#include "jammer.hpp"
#include "lfsr_random.hpp"
#include "radio_state.hpp"
using namespace jammer;
namespace ui::external_app::hopper {
class HopperView : public View {
public:
HopperView(NavigationView& nav);
~HopperView();
HopperView(const HopperView&) = delete;
HopperView(HopperView&&) = delete;
HopperView& operator=(const HopperView&) = delete;
HopperView& operator=(HopperView&&) = delete;
void focus() override;
void update_freq_list_menu_view();
std::string title() const override { return "Hopper"; };
std::vector<rf::Frequency> freq_list{315000000, 433920000};
private:
NavigationView& nav_;
TxRadioState radio_state_{
0 /* frequency */,
3500000 /* bandwidth */,
3072000 /* sampling rate */
};
void start_tx();
void on_timer();
void stop_tx();
void set_hopper_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration);
void on_retune(const rf::Frequency freq, const uint32_t range);
void load_list();
void save_list();
void on_file_save_write(const std::string& value);
HopperChannel* hopper_channels = (HopperChannel*)shared_memory.bb_data.data;
bool jamming{false};
bool cooling{false}; // euquiq: Indicates jammer in cooldown
uint16_t seconds = 0; // euquiq: seconds counter for toggling tx / cooldown
int16_t mscounter = 0; // euquiq: Internal ms counter for do_timer()
lfsr_word_t lfsr_v = 1; // euquiq: Used to generate "random" Jitter
std::string filename_buffer = "";
const Style& style_val = *Theme::getInstance()->fg_green;
const Style& style_cancel = *Theme::getInstance()->fg_red;
MenuView menu_freq_list{
{0, 0, screen_width, 8 * 16},
false};
NewButton button_load_list{
{0 * 8, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_load,
Color::dark_blue(),
/*vcenter*/ true};
NewButton button_save_list{
{4 * 8, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_save,
Color::dark_blue(),
/*vcenter*/ true};
NewButton button_add_freq{
{8 * 8 + 4, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_add,
Color::dark_green(),
/*vcenter*/ true};
NewButton button_delete_freq{
{12 * 8 + 4, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_trash,
Color::dark_red(),
/*vcenter*/ true};
NewButton button_clear{
{screen_width - 4 * 8, 9 * 16 + 4, 4 * 8, 32},
{},
&bitmap_icon_tools_wipesd,
Color::red(),
/*vcenter*/ true};
Labels labels{
{{2 * 8, 23 * 8}, "Type:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 25 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground},
{{3 * 8, 27 * 8}, "Hop:", Theme::getInstance()->fg_light->foreground},
{{4 * 8, 29 * 8}, "TX:", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 31 * 8}, "Sle3p:", Theme::getInstance()->fg_light->foreground}, // euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty.
{{0 * 8, 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground}, // Maybe the repository curator can keep the "mystype" for some versions.
{{11 * 8, 29 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 31 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 33 * 8}, "/60", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 35 * 8}, "Gain:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 35 * 8}, "A:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_type{
{7 * 8, 23 * 8},
8,
{
{"Rand FSK", 0},
{"FM tone", 1},
{"CW sweep", 2},
{"Rand CW", 3},
}};
Text text_range_number{
{16 * 8, 23 * 8, 2 * 8, 16},
"--"};
Text text_range_total{
{18 * 8, 23 * 8, 3 * 8, 16},
"/--"};
OptionsField options_speed{
{7 * 8, 25 * 8},
6,
{{"10Hz ", 10},
{"100Hz ", 100},
{"1kHz ", 1000},
{"10kHz ", 10000},
{"100kHz", 100000}}};
OptionsField options_hop{
{7 * 8, 27 * 8},
5,
{{"10ms ", 1},
{"50ms ", 5},
{"100ms", 10},
{"1s ", 100},
{"2s ", 200},
{"5s ", 500},
{"10s ", 1000}}};
NumberField field_timetx{
{7 * 8, 29 * 8},
3,
{1, 180},
1,
' ',
};
NumberField field_timepause{
{8 * 8, 31 * 8},
2,
{1, 60},
1,
' ',
};
NumberField field_jitter{
{8 * 8, 33 * 8},
2,
{1, 60},
1,
' ',
};
NumberField field_gain{
{8 * 8, 35 * 8},
2,
{0, 47},
1,
' ',
};
NumberField field_amp{
{13 * 8, 35 * 8},
1,
{0, 1},
1,
' ',
};
Button button_transmit{
{148, 216, 80, 80},
LanguageHelper::currentMessages[LANG_START]};
MessageHandlerRegistration message_handler_retune{
Message::ID::Retune,
[this](Message* const p) {
const auto message = static_cast<const RetuneMessage*>(p);
this->on_retune(message->freq, message->range);
}};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->on_timer();
}};
};
} // namespace ui::external_app::hopper
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_level.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::level {
void initialize_app(ui::NavigationView& nav) {
nav.push<LevelView>();
}
} // namespace ui::external_app::level
extern "C" {
__attribute__((section(".external_app.app_level.application_information"), used)) application_information_t _application_information_level = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::level::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Level",
/*.bitmap_data = */
{
0x00,
0x00,
0x00,
0x00,
0x04,
0x20,
0x12,
0x48,
0x8A,
0x51,
0xCA,
0x53,
0xCA,
0x53,
0x8A,
0x51,
0x12,
0x48,
0x84,
0x21,
0xC0,
0x03,
0x40,
0x02,
0x60,
0x06,
0x20,
0x04,
0x30,
0x0C,
0xF0,
0x0F},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::RX,
/*.desired_menu_position = */ -1,
// this has to be the biggest baseband used by the app. Level is using AM,WFM,NFM,AMFM,SPEC and NFM is the biggest
/*.m4_app_tag = portapack::spi_flash::image_tag_nfm */ {'P', 'N', 'F', 'M'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -33,7 +33,7 @@ using namespace portapack;
using namespace tonekey;
using portapack::memory::map::backup_ram;
namespace ui {
namespace ui::external_app::level {
// Function to map the value from one range to another
int32_t LevelView::map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh) {
@@ -130,9 +130,16 @@ LevelView::LevelView(NavigationView& nav)
freqman_set_modulation_option(field_mode);
field_mode.on_change = [this](size_t, OptionsField::value_t v) {
if (v != -1) {
change_mode(v);
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);
}
last_mode = v;
change_mode(v);
};
field_mode.set_by_value(radio_mode); // Reflect the mode into the manual selector
field_bw.set_selected_index(radio_bw);
@@ -329,4 +336,4 @@ void LevelView::on_freqchg(int64_t freq) {
button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>");
}
} /* namespace ui */
} // namespace ui::external_app::level
@@ -39,7 +39,7 @@
#include "ui_receiver.hpp"
#include "ui_spectrum.hpp"
namespace ui {
namespace ui::external_app::level {
class LevelView : public View {
public:
@@ -211,6 +211,6 @@ class LevelView : public View {
}};
};
} /* namespace ui */
} // namespace ui::external_app::level
#endif
+155 -1
View File
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
* copyleft Elliot Alderson from F society
* copyleft Darlene Alderson from F society
*
* This file is part of PortaPack.
*
@@ -178,7 +180,8 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
&label_waveform,
&waveform,
&button_open,
&button_save});
&button_save,
&button_bug_key});
// Initialize default values for controls
field_symbol_rate.set_value(100);
@@ -267,6 +270,16 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
});
};
button_bug_key.on_select = [&](Button&) {
auto bug_key_input_view = nav_.push<OOKEditorBugKeyView>(ook_data.payload);
bug_key_input_view->on_save = [this](std::string p) {
ook_data.payload = p;
text_payload.set(ook_data.payload);
draw_waveform();
};
};
// Configure button to start or stop the transmission
button_send_stop.on_select = [this](Button&) {
if (!is_transmitting) {
@@ -280,4 +293,145 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
// initial waveform drawing (should be a single line)
draw_waveform();
}
/*************** bug key view ****************/
OOKEditorBugKeyView::OOKEditorBugKeyView(NavigationView& nav, std::string payload)
: nav_{nav},
payload_{payload} {
add_children({&labels,
&field_primary_step,
&field_secondary_step,
&console,
&button_insert_high_level_long,
&button_insert_high_level_short,
&button_insert_low_level_long,
&button_insert_low_level_short,
&button_delete,
&button_save});
button_insert_low_level_short.on_select = [this](Button&) {
on_insert(InsertType::LOW_LEVEL_SHORT);
};
button_insert_low_level_long.on_select = [this](Button&) {
on_insert(InsertType::LOW_LEVEL_LONG);
};
button_insert_high_level_short.on_select = [this](Button&) {
on_insert(InsertType::HIGH_LEVEL_SHORT);
};
button_insert_high_level_long.on_select = [this](Button&) {
on_insert(InsertType::HIGH_LEVEL_LONG);
};
button_delete.on_select = [this](Button&) {
on_delete();
};
button_save.on_select = [this](Button&) {
if (on_save) on_save(build_payload());
nav_.pop();
};
auto update_step_buttons = [this](int32_t value, Button& btnLow, Button& btnHigh) {
std::string low_level_btn_str;
std::string high_level_btn_str;
if (value <= 14) { // the button width allow max 14 chars
for (int i = 0; i < value; i++) {
low_level_btn_str.push_back('0');
high_level_btn_str.push_back('1');
}
} else {
low_level_btn_str = to_string_dec_int(value) + " * \"0\"";
high_level_btn_str = to_string_dec_int(value) + " * \"1\"";
}
btnLow.set_text(" "); // set_dirty broken console. this is work around
btnHigh.set_text(" ");
btnLow.set_text(low_level_btn_str);
btnHigh.set_text(high_level_btn_str);
};
field_primary_step.on_change = [&](int32_t) {
update_step_buttons(field_primary_step.value(),
button_insert_low_level_short,
button_insert_high_level_short);
update_console();
};
field_secondary_step.on_change = [&](int32_t) {
update_step_buttons(field_secondary_step.value(),
button_insert_low_level_long,
button_insert_high_level_long);
update_console();
};
field_primary_step.set_value(1);
field_secondary_step.set_value(2);
update_step_buttons(field_primary_step.value(),
button_insert_low_level_short,
button_insert_high_level_short);
update_console();
}
void OOKEditorBugKeyView::on_insert(InsertType type) {
auto promise_length = 0;
std::string promose_level = "0";
switch (type) {
case InsertType::LOW_LEVEL_SHORT:
promise_length = field_primary_step.value();
promose_level = "0";
break;
case InsertType::LOW_LEVEL_LONG:
promise_length = field_secondary_step.value();
promose_level = "0";
break;
case InsertType::HIGH_LEVEL_SHORT:
promise_length = field_primary_step.value();
promose_level = "1";
break;
case InsertType::HIGH_LEVEL_LONG:
promise_length = field_secondary_step.value();
promose_level = "1";
break;
}
for (auto i = 0; i < promise_length; i++) {
payload_ += promose_level;
}
update_console();
}
void OOKEditorBugKeyView::on_delete() {
// I'm aware that if user inputted like: [long high][long high][short high], this will delete a pile of them
// but this doesnt matter because:
// 1. they should not do it, high or low shoudl cross each other, don't repeat
// 2. don't have too much RAM to trach the input trace
if (payload_.length() > 0) {
size_t len = payload_.length();
char last_char = payload_[len - 1];
size_t pos = len - 1;
while (pos > 0 && payload_[pos - 1] == last_char) {
pos--;
}
payload_.erase(pos);
update_console();
}
}
void OOKEditorBugKeyView::update_console() {
console.clear(true);
console.write(payload_);
}
std::string OOKEditorBugKeyView::build_payload() {
return payload_;
}
void OOKEditorBugKeyView::focus() {
button_save.focus();
}
} // namespace ui::external_app::ook_editor
@@ -1,5 +1,7 @@
/*
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
* copyleft Elliot Alderson from F society
* copyleft Darlene Alderson from F society
*
* This file is part of PortaPack.
*
@@ -29,6 +31,8 @@ namespace ui::external_app::ook_editor {
#define TRANSMISSION_FREQUENCY_DEFAULT 433920000U // Sets the default transmission frequency (27 MHz).
#define WAVEFORM_BUFFER_SIZE 550
/*****************Editor View ******************/
class OOKEditorAppView : public View {
public:
void focus() override;
@@ -123,6 +127,7 @@ class OOKEditorAppView : public View {
Button button_set{{0, 125, 60, 28}, LanguageHelper::currentMessages[LANG_SET]};
Button button_open{{68, 125, 80, 28}, LanguageHelper::currentMessages[LANG_OPEN_FILE]};
Button button_save{{154, 125, 80, 28}, LanguageHelper::currentMessages[LANG_SAVE_FILE]};
Button button_bug_key{{0, 125 + 28 + 3, screen_width, 28}, "Bug Key"};
Button button_send_stop{{80, 273, 80, 32}, LanguageHelper::currentMessages[LANG_SEND]};
// Progress bar to display transmission progress.
@@ -132,6 +137,82 @@ class OOKEditorAppView : public View {
Waveform waveform{{0, 208, 240, 32}, waveform_buffer, 0, 0, true, Theme::getInstance()->fg_yellow->foreground};
};
/******** bug key input view **********/
enum InsertType {
LOW_LEVEL_SHORT,
LOW_LEVEL_LONG,
HIGH_LEVEL_SHORT,
HIGH_LEVEL_LONG
};
class OOKEditorBugKeyView : public View {
public:
std::function<void(std::string)> on_save{};
OOKEditorBugKeyView(NavigationView& nav, std::string payload);
std::string title() const override { return "Bug.K"; };
void focus() override;
private:
NavigationView& nav_;
std::string payload_ = "";
std::string path_ = "";
uint32_t delay_{0};
std::string delay_str{""}; // needed by text_prompt
void on_insert(InsertType type);
void on_delete();
void update_console();
std::string build_payload();
Labels labels{
{{0 * 8, 0 * 16}, "Primary Step", Theme::getInstance()->fg_light->foreground},
{{(screen_width / 2), 0 * 16}, "Secondary Step", Theme::getInstance()->fg_light->foreground}};
NumberField field_primary_step{
{0 * 8, 1 * 16},
3,
{0, 550},
1,
' '};
NumberField field_secondary_step{
{(screen_width / 2), 1 * 16},
3,
{0, 550},
1,
' '};
Console console{
{0, 3 * 16, screen_width, screen_height - 10 * 16}};
Button button_insert_low_level_long{
{0 * 8, 13 * 16, screen_width / 2, 2 * 16},
"00"};
Button button_insert_low_level_short{
{0 * 8, 15 * 16, screen_width / 2, 2 * 16},
"0"};
Button button_insert_high_level_long{
{(screen_width / 2), 13 * 16, screen_width / 2, 2 * 16},
"11"};
Button button_insert_high_level_short{
{(screen_width / 2), 15 * 16, screen_width / 2, 2 * 16},
"1"};
Button button_delete{
{1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"<Backspace"};
Button button_save{
{1 + screen_width / 2 + 1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"Save"};
};
}; // namespace ui::external_app::ook_editor
#endif /*__UI_OOK_EDITOR_H__*/
+39
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2024 HTotoo
* copyleft Whiterose of the Dark Army
*
* This file is part of PortaPack.
*
@@ -25,6 +26,7 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "ui_textentry.hpp"
using namespace portapack;
using namespace ui;
@@ -39,6 +41,9 @@ OOKBruteView::OOKBruteView(NavigationView& nav)
: nav_{nav} {
add_children({
&button_startstop,
&button_input_stop_position,
&button_input_start_position,
&labels,
&field_frequency,
&tx_view,
&options_atkmode,
@@ -66,6 +71,40 @@ OOKBruteView::OOKBruteView(NavigationView& nav)
field_stop.on_change = [this](int32_t) {
validate_start_stop();
};
button_input_start_position.on_select = [this](Button&) {
stop(); // prevent mess count var up
text_input_buffer = to_string_dec_uint(field_start.value());
if (text_input_buffer == "0") {
text_input_buffer = "";
}
text_prompt(
nav_,
text_input_buffer,
8, // currently longest is princeton
[this](std::string& buffer) {
field_start.set_value(atoi(buffer.c_str()));
validate_start_stop();
});
};
button_input_stop_position.on_select = [this](Button&) {
stop(); // prevent mess count var up
text_input_buffer = to_string_dec_uint(field_stop.value());
if (text_input_buffer == "0") {
text_input_buffer = "";
}
text_prompt(
nav_,
text_input_buffer,
8, // currently longest is princeton
[this](std::string& buffer) {
field_stop.set_value(atoi(buffer.c_str()));
validate_start_stop();
});
};
update_start_stop(0);
}
+21 -4
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2024 HTotoo
* copyleft Whiterose of the Dark Army
*
* This file is part of PortaPack.
*
@@ -52,12 +53,25 @@ class OOKBruteView : public View {
app_settings::SettingsManager settings_{
"tx_ookbrute", app_settings::Mode::TX};
Labels labels{
{{0 * 8, 2 * 16}, "Start Position:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "Stop Position:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 13 * 16}, "Encoder Type:", Theme::getInstance()->fg_light->foreground}};
Button button_startstop{
{0, 3 * 16, 96, 24},
{8, screen_height - 48 - 16, screen_width - 2 * 8, 48},
LanguageHelper::currentMessages[LANG_START]};
Button button_input_start_position{
{8, 4 * 16, screen_width - 2 * 8, 24},
"Input Start Pos"};
Button button_input_stop_position{
{8, 9 * 16, screen_width - 2 * 8, 24},
"Input Stop Pos"};
NumberField field_start{
{0 * 8, 1 * 16},
{0 * 8, 3 * 16},
8,
{0, 2500},
1,
@@ -65,15 +79,16 @@ class OOKBruteView : public View {
true};
NumberField field_stop{
{11 * 8, 1 * 16},
{0, 8 * 16},
9,
{0, 2500},
1,
' ',
true};
// NB: when add new encoder here you should also change the char count limit for input range buttons by it's digits in DEC
OptionsField options_atkmode{
{0 * 8, 2 * 16},
{0, 14 * 16},
12,
{{"Came12", 0},
{"Came24", 1},
@@ -86,6 +101,8 @@ class OOKBruteView : public View {
uint32_t counter = 0; // for packet change
std::string text_input_buffer{}; // this is needed by the text_prompt func
void start();
void stop();
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright (C) 2024 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_playlist_editor.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::playlist_editor {
void initialize_app(ui::NavigationView& nav) {
nav.push<PlaylistEditorView>();
}
} // namespace ui::external_app::playlist_editor
extern "C" {
__attribute__((section(".external_app.app_playlist_editor.application_information"), used)) application_information_t _application_information_playlist_editor = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::playlist_editor::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "PlaylistEdit",
/*.bitmap_data = */ {
0x03,
0x00,
0x00,
0x00,
0x03,
0x00,
0x00,
0x00,
0x0F,
0x00,
0x00,
0x00,
0x03,
0x01,
0x80,
0x01,
0xC3,
0x00,
0xE0,
0xFF,
0xEF,
0xFF,
0xC0,
0x00,
0x83,
0x01,
0x00,
0x01,
0x03,
0x00,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -0,0 +1,367 @@
/*
* copyleft Elliot Alderson from F society
* copyleft Darlene Alderson from F society
*
* 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_playlist_editor.hpp"
#include "ui_navigation.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "file.hpp"
#include "ui_fileman.hpp"
#include "file_path.hpp"
#include "string_format.hpp"
namespace fs = std::filesystem;
#include "string_format.hpp"
#include "file_reader.hpp"
using namespace portapack;
namespace ui::external_app::playlist_editor {
/*********menu**********/
PlaylistEditorView::PlaylistEditorView(NavigationView& nav)
: nav_{nav} {
portapack::async_tx_enabled = true;
add_children({&labels,
&button_new,
&text_current_ppl_file,
&menu_view,
&text_hint,
&button_open_playlist,
&button_edit,
&button_insert,
&button_save_playlist});
menu_view.set_parent_rect({0, 2 * 8, screen_width, 24 * 8});
menu_view.on_highlight = [this]() {
text_hint.set("Edit:" +
playlist[menu_view.highlighted_index()].substr(playlist[menu_view.highlighted_index()].find_last_of('/') + 1,
playlist[menu_view.highlighted_index()].find(',') -
playlist[menu_view.highlighted_index()].find_last_of('/') - 1));
};
button_new.on_select = [this](Button&) {
if (on_create_ppl()) {
swap_opened_file_or_new_button(DisplayFilenameOrNewButton::DISPLAY_FILENAME);
refresh_interface();
}
};
button_open_playlist.on_select = [this](Button&) {
open_file();
};
button_edit.on_select = [this](Button&) {
on_edit_item();
};
button_insert.on_select = [this](Button&) {
on_insert_item();
};
button_save_playlist.on_select = [this](Button&) {
save_ppl();
};
swap_opened_file_or_new_button(DisplayFilenameOrNewButton::DISPLAY_NEW_BUTTON);
}
void PlaylistEditorView::focus() {
menu_view.focus();
}
void PlaylistEditorView::open_file() {
auto open_view = nav_.push<FileLoadView>(".PPL");
open_view->push_dir(playlist_dir);
open_view->on_changed = [this](fs::path new_file_path) {
current_ppl_path = new_file_path;
on_file_changed(new_file_path);
};
}
void PlaylistEditorView::swap_opened_file_or_new_button(DisplayFilenameOrNewButton d) {
if (d == DisplayFilenameOrNewButton::DISPLAY_NEW_BUTTON) {
button_new.hidden(false);
text_current_ppl_file.hidden(true);
} else {
button_new.hidden(true);
text_current_ppl_file.hidden(false);
text_current_ppl_file.set(current_ppl_name_buffer);
}
refresh_interface();
}
/*
NB: same name would became as "open file"
*/
bool PlaylistEditorView::on_create_ppl() {
bool success = false;
text_prompt(
nav_,
current_ppl_name_buffer,
100,
[&](std::string& s) {
current_ppl_name_buffer = s;
success = true;
current_ppl_name_buffer += ".PPL";
current_ppl_path = playlist_dir / std::filesystem::path(current_ppl_name_buffer);
File f;
f.open(current_ppl_path, true, true); // prob safer here as standalone obj as read only and then open again in process func
f.close();
on_file_changed(current_ppl_path);
});
return success;
}
void PlaylistEditorView::on_file_changed(const fs::path& new_file_path) {
File playlist_file;
auto error = playlist_file.open(new_file_path.string());
if (error) return;
menu_view.clear();
auto reader = FileLineReader(playlist_file);
for (const auto& line : reader) {
playlist.push_back(line);
}
for (auto& line : playlist) {
// remove empty lines
if (line == "\n" || line == "\r\n" || line == "\r") {
playlist.erase(std::remove(playlist.begin(), playlist.end(), line), playlist.end());
}
// remove line end \n etc
if (line.length() > 0 && (line[line.length() - 1] == '\n' || line[line.length() - 1] == '\r')) {
line = line.substr(0, line.length() - 1);
}
}
text_hint.set("Highlight an entry");
text_current_ppl_file.set(new_file_path.string());
ever_opened = true;
swap_opened_file_or_new_button(DisplayFilenameOrNewButton::DISPLAY_FILENAME);
refresh_menu_view();
}
void PlaylistEditorView::refresh_menu_view() {
menu_view.clear();
for (const auto& line : playlist) {
if (line.length() == 0 || line[0] == '#') {
menu_view.add_item({line,
ui::Color::grey(),
&bitmap_icon_notepad,
[this](KeyEvent) {
button_insert.focus();
}});
} else {
const auto filename = line.substr(line.find_last_of('/') + 1, line.find(',') - line.find_last_of('/') - 1);
menu_view.add_item({filename,
ui::Color::white(),
&bitmap_icon_cwgen,
[this](KeyEvent) {
button_edit.focus();
}});
}
}
}
void PlaylistEditorView::on_edit_item() {
if (!ever_opened || playlist.empty()) {
nav_.display_modal("Err", "No entry");
return;
}
auto edit_view = nav_.push<PlaylistItemEditView>(
playlist[menu_view.highlighted_index()]);
edit_view->set_on_delete([this]() {
playlist.erase(playlist.begin() + menu_view.highlighted_index());
refresh_interface();
});
edit_view->on_save = [this](std::string new_item) {
playlist[menu_view.highlighted_index()] = new_item;
refresh_interface();
};
}
void PlaylistEditorView::on_insert_item() {
// if (current_ppl_path.empty() || current_ppl_path.string().find_first_not_of(" \t\n\r") == std::string::npos) {
if (!ever_opened) { // TODO: this is a workaround because the above line is not working and I took one hour and didn't find the issue
nav_.display_modal("Err", "No playlist file loaded");
return;
}
auto edit_view = nav_.push<PlaylistItemEditView>(
"");
edit_view->on_save = [&](std::string new_item) {
if (playlist.empty()) {
playlist.push_back(new_item);
} else {
playlist.insert(playlist.begin() + menu_view.highlighted_index() + 1, new_item);
}
refresh_interface();
};
}
void PlaylistEditorView::refresh_interface() {
const auto previous_index = menu_view.highlighted_index();
refresh_menu_view();
set_dirty();
menu_view.set_highlighted(previous_index);
}
void PlaylistEditorView::save_ppl() {
if (current_ppl_path.empty()) {
nav_.display_modal("Err", "No playlist file loaded");
return;
} else if (playlist.empty()) {
nav_.display_modal("Err", "List is empty");
return;
}
File playlist_file;
auto error = playlist_file.open(current_ppl_path.string(), false, false);
if (error) {
nav_.display_modal("Err", "open err");
return;
}
// clear file
playlist_file.seek(0);
playlist_file.truncate();
// write new data
for (const auto& entry : playlist) {
playlist_file.write_line(entry);
}
nav_.display_modal("Save", "Saved playlist\n" + current_ppl_path.string());
}
/*********edit**********/
PlaylistItemEditView::PlaylistItemEditView(
NavigationView& nav,
std::string item)
: nav_{nav},
original_item_{item} {
add_children({&labels,
&field_path,
&field_delay,
&button_browse,
&button_input_delay,
&button_delete,
&button_save});
button_browse.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".C16");
open_view->push_dir(captures_dir);
open_view->on_changed = [this](fs::path path) {
field_path.set_text(path.string());
path_ = path.string();
};
field_delay.on_change = [&](auto) {
delay_ = field_delay.value();
};
};
button_input_delay.on_select = [this](Button&) {
delay_str = to_string_dec_uint(delay_);
if (delay_str == "0") {
delay_str = "";
}
text_prompt(
nav_,
delay_str,
100,
[&](std::string& s) {
delay_ = atoi(s.c_str());
field_delay.set_value(delay_);
refresh_ui();
});
};
button_delete.on_select = [this](Button&) {
if (on_delete) on_delete();
nav_.pop();
};
button_save.on_select = [&](Button&) {
if (path_.empty()) {
nav_.display_modal("Err", "Select a file\n or press back to cancel");
return;
}
if (on_save) on_save(build_item());
nav_.pop();
};
if (!on_delete) {
button_delete.hidden(true);
}
parse_item(item);
refresh_ui();
}
void PlaylistItemEditView::focus() {
button_save.focus();
}
void PlaylistItemEditView::refresh_ui() {
field_path.set_text(path_);
field_delay.set_value(delay_);
}
void PlaylistItemEditView::parse_item(std::string item) {
// Parse format: path,delay
if (item.empty()) {
return;
}
auto parts = split_string(item, ',');
if (parts.size() >= 1) {
path_ = std::string{parts[0]};
}
if (parts.size() >= 2) {
delay_ = atoi(std::string{parts[1]}.c_str());
}
}
std::string PlaylistItemEditView::build_item() const {
const auto v = path_ + "," + to_string_dec_uint(field_delay.value());
return path_ + "," + to_string_dec_uint(field_delay.value());
}
} // namespace ui::external_app::playlist_editor
@@ -0,0 +1,159 @@
/*
* copyleft Elliot Alderson from F society
* copyleft Darlene Alderson from F society
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_PLAYLIST_EDITOR_H__
#define __UI_PLAYLIST_EDITOR_H__
#include "ui_navigation.hpp"
namespace fs = std::filesystem;
namespace ui::external_app::playlist_editor {
enum DisplayFilenameOrNewButton {
DISPLAY_FILENAME,
DISPLAY_NEW_BUTTON
};
class PlaylistEditorView : public View {
public:
PlaylistEditorView(NavigationView& nav);
std::string title() const override { return "PPL Edit"; };
private:
NavigationView& nav_;
void focus() override;
std::vector<std::string> playlist = {};
fs::path current_ppl_path = "";
std::string current_ppl_name_buffer = ""; // this is because text_prompt needs it. TODO: this is so annoying, shoudl refactor that func
bool ever_opened = false;
Labels labels{
{{0 * 8, 0 * 16}, "PPL file:", Theme::getInstance()->fg_light->foreground}};
Button button_new{
{(sizeof("PPL file:") + 1) * 8, 0 * 16, 8 * 5, 16},
"New"};
Text text_current_ppl_file{
{sizeof("PPL file:") * 8, 0 * 16, screen_width - sizeof("PPL file:") * 8, 16},
""};
MenuView menu_view{};
Text text_hint{
{0, 27 * 8, screen_width, 16},
"Open a PPL file"};
Text text_ppl_name{
{0, 27 * 8, screen_width, 16},
"Highlight an app"};
Button button_open_playlist{
{0, 29 * 8, screen_width / 2 - 1, 32},
"Open PPL"};
Button button_edit{
{screen_width / 2 + 2, 29 * 8, screen_width / 2 - 2, 32},
"Edit Item"};
Button button_insert{
{0, screen_height - 32 - 16, screen_width / 2 - 1, 32},
"Ins. After"};
Button button_save_playlist{
{screen_width / 2 + 2, screen_height - 32 - 16, screen_width / 2 - 2, 32},
"Save PPL"};
void open_file();
void swap_opened_file_or_new_button(DisplayFilenameOrNewButton d);
void on_file_changed(const fs::path& path);
bool on_create_ppl();
void refresh_interface();
void on_edit_item();
void on_insert_item();
void refresh_menu_view();
void save_ppl();
};
class PlaylistItemEditView : public View {
public:
std::function<void(std::string)> on_save{};
std::function<void()> on_delete{};
PlaylistItemEditView(NavigationView& nav, std::string item);
std::string title() const override { return "Edit Item"; };
void focus() override;
void set_on_delete(std::function<void()> callback) {
on_delete = callback;
button_delete.hidden(false);
}
private:
NavigationView& nav_;
std::string original_item_ = "";
std::string path_ = "";
uint32_t delay_{0};
std::string delay_str{""}; // needed by text_prompt
void refresh_ui();
void parse_item(std::string item);
std::string build_item() const;
Labels labels{
{{0 * 8, 1 * 16}, "Path:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Delay(ms):", Theme::getInstance()->fg_light->foreground}};
TextField field_path{
{0, 2 * 16, screen_width, 16},
"empty"};
NumberField field_delay{
{11 * 8, 5 * 16},
5,
{0, 99999},
10,
' '};
Button button_browse{
{2 * 8, 8 * 16, 8 * 8, 3 * 16},
"Browse"};
Button button_input_delay{
{12 * 8, 8 * 16, sizeof("Input Delay") * 8, 3 * 16},
"Input Delay"};
Button button_delete{
{1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"Delete"};
Button button_save{
{1 + screen_width / 2 + 1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"Save"};
};
} // namespace ui::external_app::playlist_editor
#endif // __UI_PLAYLIST_EDITOR_H__
+2 -2
View File
@@ -38,7 +38,7 @@ __attribute__((section(".external_app.app_random_password.application_informatio
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Random passwd",
/*.app_name = */ "Rand Pwd",
/*.bitmap_data = */ {
0xC0,
0x03,
@@ -74,7 +74,7 @@ __attribute__((section(".external_app.app_random_password.application_informatio
0x01,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.menu_location = */ app_location_t::RX,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'F', 'R'},
+51
View File
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_scanner.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::scanner {
void initialize_app(ui::NavigationView& nav) {
nav.push<ScannerView>();
}
} // namespace ui::external_app::scanner
extern "C" {
__attribute__((section(".external_app.app_scanner.application_information"), used)) application_information_t _application_information_scanner = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::scanner::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Scanner",
/*.bitmap_data = */ {0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x03, 0x01, 0x80, 0x01, 0xC3, 0x00, 0xE0, 0xFF, 0xEF, 0xFF, 0xC0, 0x00, 0x83, 0x01, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::RX,
/*.desired_menu_position = */ -1,
// this has to be the biggest baseband used by the app. Scanner is using AM,WFM,NFM and NFM is the biggest
/*.m4_app_tag = portapack::spi_flash::image_tag_scanner */ {'P', 'N', 'F', 'M'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -28,10 +28,11 @@
#include "ui_freqman.hpp"
#include "file_path.hpp"
using namespace ui;
using namespace portapack;
namespace fs = std::filesystem;
namespace ui {
namespace ui::external_app::scanner {
ScannerThread::ScannerThread(std::vector<rf::Frequency> frequency_list)
: frequency_list_{std::move(frequency_list)} {
@@ -451,13 +452,14 @@ ScannerView::ScannerView(
// Mode field was changed (AM/NFM/WFM)
field_mode.on_change = [this](size_t, OptionsField::value_t v) {
static freqman_index_t last_mode = AM_MODULATION;
// initialize to a value under SPEC
static freqman_index_t last_mode = WFM_MODULATION;
// unsupported SPEC mode fix
if (v == SPEC_MODULATION) {
if (last_mode == AM_MODULATION)
v = WFM_MODULATION;
else
if (v >= SPEC_MODULATION) {
if (last_mode == WFM_MODULATION)
v = AM_MODULATION;
else
v = WFM_MODULATION;
field_mode.set_selected_index(v);
}
last_mode = v;
@@ -765,5 +767,4 @@ void ScannerView::restart_scan() {
start_scan_thread(); // RESTART SCANNER THREAD in selected mode
}
} /* namespace ui */
} // namespace ui::external_app::scanner
@@ -35,12 +35,14 @@
#include "ui_mictx.hpp"
#include "ui_receiver.hpp"
using namespace ui;
namespace ui::external_app::scanner {
#define SCANNER_SLEEP_MS 50 // ms that Scanner Thread sleeps per loop
#define STATISTICS_UPDATES_PER_SEC 10
#define MAX_FREQ_LOCK 10 // # of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spurious
namespace ui {
// TODO: There is too much duplicated data in these classes.
// ScannerThread should just use more from the View.
// Or perhaps ScannerThread should just be in the View.
@@ -314,5 +316,4 @@ class ScannerView : public View {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}};
};
} /* namespace ui */
} // namespace ui::external_app::scanner
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2024 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_sd_wipe.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::sd_wipe {
void initialize_app(ui::NavigationView& nav) {
nav.push<WipeSDView>();
}
} // namespace ui::external_app::sd_wipe
extern "C" {
__attribute__((section(".external_app.app_sd_wipe.application_information"), used)) application_information_t _application_information_sd_wipe = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::sd_wipe::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Wipe SD card",
/*.bitmap_data = */ {0xF0, 0x3F, 0x58, 0x35, 0x5C, 0x35, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0x3C, 0x1C, 0xBC, 0xC9, 0xBC, 0xE3, 0x2C, 0x77, 0x5C, 0x3E, 0xAC, 0x1C, 0x5C, 0x3E, 0x2C, 0x77, 0x9C, 0xE3, 0xAC, 0xC1},
/*.icon_color = */ ui::Color::red().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -22,7 +22,9 @@
#include "ui_sd_wipe.hpp"
namespace ui {
using namespace ui;
namespace ui::external_app::sd_wipe {
Thread* WipeSDView::thread{nullptr};
@@ -63,4 +65,4 @@ void WipeSDView::focus() {
}
}
} /* namespace ui */
} // namespace ui::external_app::sd_wipe
@@ -30,7 +30,9 @@
#include <cstdint>
namespace ui {
using namespace ui;
namespace ui::external_app::sd_wipe {
class WipeSDView : public View {
public:
@@ -87,6 +89,6 @@ class WipeSDView : public View {
""};
};
} /* namespace ui */
} // namespace ui::external_app::sd_wipe
#endif /*__UI_SD_WIPE_H__*/
+6 -4
View File
@@ -1,6 +1,8 @@
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
// If you can read this, you're a nerd. :P
// Come join us at https://discord.gg/thepiratesreborn
// RocketGod's Shopping Cart Lock app
// https://betaskynet.com
#include "ui.hpp"
#include "shoppingcart_lock.hpp"
#include "ui_navigation.hpp"
@@ -60,7 +62,7 @@ __attribute__((section(".external_app.app_shoppingcart_lock.application_informat
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'T', 'X'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
/*.m4_app_tag = portapack::spi_flash::image_tag_audio_tx */ {'P', 'A', 'T', 'X'},
/*.m4_app_offset = */ 0x00000000,
};
}
@@ -1,5 +1,8 @@
// RocketGod's Shopping Cart Lock app
// https://betaskynet.com
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
// If you can read this, you're a nerd. :P
// Come join us at https://discord.gg/thepiratesreborn
#include "shoppingcart_lock.hpp"
using namespace portapack;
@@ -48,7 +51,6 @@ void ShoppingCartLock::stop() {
audio::output::stop();
log_event("... Resetting State Variables");
transmitter_model.disable();
ready_signal = false;
thread_sync_complete = false;
looping = false;
@@ -110,18 +112,24 @@ std::string ShoppingCartLock::list_wav_files() {
}
void ShoppingCartLock::wait_for_thread() {
uint32_t timeout = 100;
uint32_t timeout = 1000;
while (!ready_signal && timeout > 0) {
chThdYield();
timeout--;
}
if (!ready_signal) {
log_event("!!! Timeout waiting for ReplayThread");
}
}
void ShoppingCartLock::restart_playback() {
auto reader = std::make_unique<WAVFileReader>();
std::string file_path = (wav_dir / current_file).string();
if (!reader->open(file_path)) return;
if (!reader->open(file_path)) {
log_event("!!! Failed to reopen " + current_file + " for restart");
return;
}
replay_thread = std::make_unique<ReplayThread>(
std::move(reader),
@@ -133,9 +141,8 @@ void ShoppingCartLock::restart_playback() {
EventDispatcher::send_message(message);
});
log_event(">> SENDING <<");
log_event(">> RESTARTING AUDIO <<");
audio::output::start();
transmitter_model.enable();
}
void ShoppingCartLock::play_audio(const std::string& filename, bool loop) {
@@ -166,33 +173,24 @@ void ShoppingCartLock::play_audio(const std::string& filename, bool loop) {
wait_for_thread();
log_event("... Configuring Baseband");
const uint32_t bb_sample_rate = 1536000;
const uint32_t decimation = bb_sample_rate / wav_sample_rate;
baseband::set_sample_rate(wav_sample_rate);
audio::set_rate(wav_sample_rate <= 12000 ? audio::Rate::Hz_12000 : wav_sample_rate <= 24000 ? audio::Rate::Hz_24000
: audio::Rate::Hz_48000);
baseband::set_audiotx_config(
bb_sample_rate / decimation,
wav_sample_rate,
0.0f,
0.0f,
5.0f,
wav_bits_per_sample,
wav_bits_per_sample,
0,
true,
false,
false,
false,
false);
baseband::set_sample_rate(wav_sample_rate);
log_event("... Starting Audio Output");
audio::output::start();
log_event("... Setting Max Volume");
audio::headphone::set_volume(audio::headphone::volume_range().max);
transmitter_model.enable();
log_event(">>> Playback Started <<<");
volume_t max_volume = audio::headphone::volume_range().max;
audio::headphone::set_volume(max_volume);
}
ShoppingCartLock::ShoppingCartLock(NavigationView& nav)
@@ -232,4 +230,4 @@ ShoppingCartLock::~ShoppingCartLock() {
baseband::shutdown();
}
} // namespace ui::external_app::shoppingcart_lock
} // namespace ui::external_app::shoppingcart_lock
@@ -1,5 +1,8 @@
// RocketGod's Shopping Cart Lock app
// https://betaskynet.com
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
// If you can read this, you're a nerd. :P
// Come join us at https://discord.gg/thepiratesreborn
#pragma once
#include "ui_widget.hpp"
@@ -27,8 +30,8 @@ class ShoppingCartLock : public View {
void focus() override;
private:
static constexpr size_t BUFFER_SIZE = 8192;
static constexpr size_t NUM_BUFFERS = 8;
static constexpr size_t BUFFER_SIZE = 512;
static constexpr size_t NUM_BUFFERS = 2;
const std::string shoppingcart_lock_file{"shopping_cart_lock.wav"};
const std::string shoppingcart_unlock_file{"shopping_cart_unlock.wav"};
+68
View File
@@ -0,0 +1,68 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#include "ui.hpp"
#include "ui_snake.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::snake {
void initialize_app(ui::NavigationView& nav) {
nav.push<SnakeView>();
}
} // namespace ui::external_app::snake
extern "C" {
__attribute__((section(".external_app.app_snake.application_information"), used)) application_information_t _application_information_snake = {
(uint8_t*)0x00000000,
ui::external_app::snake::initialize_app,
CURRENT_HEADER_VERSION,
VERSION_MD5,
"Snake",
{
0x00,
0x00,
0x00,
0x00,
0xE0,
0x09,
0x70,
0xC7,
0xFC,
0xC9,
0x06,
0x00,
0x06,
0x00,
0x0C,
0x00,
0xF0,
0x01,
0x00,
0x3E,
0x00,
0x40,
0xFC,
0x40,
0x02,
0x3F,
0x02,
0x00,
0x7C,
0x80,
0x80,
0x7F,
},
ui::Color::green().v,
app_location_t::GAMES,
-1,
{0, 0, 0, 0},
0x00000000,
};
} // namespace ui::external_app::snake
+294
View File
@@ -0,0 +1,294 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#include "ui_snake.hpp"
namespace ui::external_app::snake {
Ticker game_timer;
int snake_x[GRID_WIDTH * GRID_HEIGHT];
int snake_y[GRID_WIDTH * GRID_HEIGHT];
int snake_length = 1;
int snake_dx = 1;
int snake_dy = 0;
int food_x, food_y;
int score = 0;
int game_state = STATE_MENU;
bool initialized = false;
const Color pp_colors[] = {
Color::white(),
Color::blue(),
Color::yellow(),
Color::purple(),
Color::green(),
Color::red(),
Color::magenta(),
Color::orange(),
Color::black(),
};
Painter painter;
bool but_RIGHT = false;
bool but_LEFT = false;
bool but_SELECT = false;
static Callback game_update_callback = nullptr;
static uint32_t game_update_timeout = 0;
static uint32_t game_update_counter = 0;
void cls() {
painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black());
}
void background(int color) {
(void)color;
}
void fillrect(int x1, int y1, int x2, int y2, int color) {
painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
}
void rect(int x1, int y1, int x2, int y2, int color) {
painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
}
void check_game_timer() {
if (game_update_callback) {
if (++game_update_counter >= game_update_timeout) {
game_update_counter = 0;
game_update_callback();
}
}
}
void Ticker::attach(Callback func, double delay_sec) {
game_update_callback = func;
game_update_timeout = delay_sec * 60;
}
void Ticker::detach() {
game_update_callback = nullptr;
}
void game_timer_check() {
if (game_state == STATE_PLAYING) {
update_game();
}
}
void init_game() {
snake_x[0] = GRID_WIDTH / 2;
snake_y[0] = GRID_HEIGHT / 2;
snake_length = 1;
snake_dx = 1;
snake_dy = 0;
score = 0;
spawn_food();
if (game_state == STATE_MENU) {
show_menu();
} else if (game_state == STATE_PLAYING) {
draw_screen();
}
}
void spawn_food() {
bool valid;
do {
food_x = rand() % GRID_WIDTH;
food_y = rand() % GRID_HEIGHT;
valid = true;
for (int i = 0; i < snake_length; i++) {
if (snake_x[i] == food_x && snake_y[i] == food_y) {
valid = false;
break;
}
}
} while (!valid);
}
void update_game() {
int new_x = snake_x[0] + snake_dx;
int new_y = snake_y[0] + snake_dy;
bool ate_food = (new_x == food_x && new_y == food_y);
int tail_x = snake_x[snake_length - 1];
int tail_y = snake_y[snake_length - 1];
for (int i = snake_length - 1; i > 0; i--) {
snake_x[i] = snake_x[i - 1];
snake_y[i] = snake_y[i - 1];
}
snake_x[0] = new_x;
snake_y[0] = new_y;
if (ate_food) {
snake_x[snake_length] = tail_x;
snake_y[snake_length] = tail_y;
snake_length++;
score += 10;
spawn_food();
draw_food();
} else {
erase_tail(tail_x, tail_y);
}
draw_snake();
draw_score();
if (check_collision()) {
draw_borders();
game_state = STATE_GAME_OVER;
show_game_over();
return;
}
}
bool check_collision() {
if (snake_x[0] < 0 || snake_x[0] >= GRID_WIDTH || snake_y[0] < 0 || snake_y[0] >= GRID_HEIGHT) {
return true;
}
for (int i = 1; i < snake_length; i++) {
if (snake_x[0] == snake_x[i] && snake_y[0] == snake_y[i]) {
return true;
}
}
return false;
}
void draw_screen() {
cls();
background(COLOR_BACKGROUND);
draw_borders();
draw_full_snake();
draw_food();
draw_score();
}
void draw_snake() {
fillrect(1 + snake_x[0] * SNAKE_SIZE, GAME_AREA_TOP + snake_y[0] * SNAKE_SIZE,
1 + snake_x[0] * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + snake_y[0] * SNAKE_SIZE + SNAKE_SIZE, COLOR_SNAKE);
}
void draw_full_snake() {
for (int i = 0; i < snake_length; i++) {
fillrect(1 + snake_x[i] * SNAKE_SIZE, GAME_AREA_TOP + snake_y[i] * SNAKE_SIZE,
1 + snake_x[i] * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + snake_y[i] * SNAKE_SIZE + SNAKE_SIZE, COLOR_SNAKE);
}
}
void erase_tail(int x, int y) {
fillrect(1 + x * SNAKE_SIZE, GAME_AREA_TOP + y * SNAKE_SIZE,
1 + x * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + y * SNAKE_SIZE + SNAKE_SIZE, COLOR_BACKGROUND);
}
void draw_food() {
fillrect(1 + food_x * SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE,
1 + food_x * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE + SNAKE_SIZE, COLOR_FOOD);
}
void erase_food() {
fillrect(1 + food_x * SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE,
1 + food_x * SNAKE_SIZE + SNAKE_SIZE, GAME_AREA_TOP + food_y * SNAKE_SIZE + SNAKE_SIZE, COLOR_BACKGROUND);
}
void draw_score() {
auto style = *ui::Theme::getInstance()->fg_blue;
painter.draw_string({5, 5}, style, "Score: " + std::to_string(score));
}
void draw_borders() {
rect(0, GAME_AREA_TOP - 1, SCREEN_WIDTH, GAME_AREA_TOP, COLOR_BORDER);
rect(0, GAME_AREA_TOP, SCREEN_WIDTH, SCREEN_HEIGHT, COLOR_BORDER);
}
void show_menu() {
cls();
background(COLOR_BACKGROUND);
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
auto style_green = *ui::Theme::getInstance()->fg_green;
auto style_blue = *ui::Theme::getInstance()->fg_blue;
painter.draw_string({50, 40}, style_yellow, "* * * SNAKE * * *");
painter.draw_string({0, 120}, style_blue, "USE THE D-PAD TO MOVE");
painter.draw_string({0, 150}, style_blue, "EAT THE RED SQUARES TO GROW");
painter.draw_string({0, 180}, style_blue, "DON'T HIT THE WALLS OR SELF");
painter.draw_string({15, 240}, style_green, "** PRESS SELECT TO START **");
}
void show_game_over() {
cls();
background(COLOR_BACKGROUND);
auto style_red = *ui::Theme::getInstance()->fg_red;
auto style_yellow = *ui::Theme::getInstance()->fg_yellow;
auto style_green = *ui::Theme::getInstance()->fg_green;
painter.draw_string({75, 90}, style_red, "GAME OVER");
painter.draw_string({74, 150}, style_yellow, "SCORE: " + std::to_string(score));
painter.draw_string({20, 220}, style_green, "PRESS SELECT TO RESTART");
wait(1);
}
SnakeView::SnakeView(NavigationView& nav)
: nav_{nav} {
add_children({&dummy});
game_timer.attach(&game_timer_check, 1.0 / 5.0);
}
void SnakeView::on_show() {
}
void SnakeView::paint(Painter& painter) {
(void)painter;
if (!initialized) {
initialized = true;
std::srand(LPC_RTC->CTIME0);
init_game();
}
}
void SnakeView::frame_sync() {
check_game_timer();
set_dirty();
}
bool SnakeView::on_key(const KeyEvent key) {
if (key == KeyEvent::Select) {
if (game_state == STATE_MENU || game_state == STATE_GAME_OVER) {
game_state = STATE_PLAYING;
init_game();
}
} else if (game_state == STATE_PLAYING) {
if (key == KeyEvent::Left) {
if (snake_dx == 0) {
snake_dx = -1;
snake_dy = 0;
}
} else if (key == KeyEvent::Right) {
if (snake_dx == 0) {
snake_dx = 1;
snake_dy = 0;
}
} else if (key == KeyEvent::Up) {
if (snake_dy == 0) {
snake_dx = 0;
snake_dy = -1;
}
} else if (key == KeyEvent::Down) {
if (snake_dy == 0) {
snake_dx = 0;
snake_dy = 1;
}
}
}
set_dirty();
return true;
}
} // namespace ui::external_app::snake
+132
View File
@@ -0,0 +1,132 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#ifndef __UI_SNAKE_H__
#define __UI_SNAKE_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include "irq_controls.hpp"
#include "random.hpp"
#include "lpc43xx_cpp.hpp"
#include "ui_widget.hpp"
namespace ui::external_app::snake {
enum {
White,
Blue,
Yellow,
Purple,
Green,
Red,
Maroon,
Orange,
Black,
};
extern const Color pp_colors[];
extern Painter painter;
extern bool but_RIGHT;
extern bool but_LEFT;
extern bool but_SELECT;
void cls();
void background(int color);
void fillrect(int x1, int y1, int x2, int y2, int color);
void rect(int x1, int y1, int x2, int y2, int color);
#define wait(x) chThdSleepMilliseconds(x * 1000)
using Callback = void (*)(void);
class Ticker {
public:
Ticker() = default;
void attach(Callback func, double delay_sec);
void detach();
};
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
#define SNAKE_SIZE 10
#define INFO_BAR_HEIGHT 25
#define GAME_AREA_TOP (INFO_BAR_HEIGHT + 1)
#define GAME_AREA_HEIGHT (SCREEN_HEIGHT - INFO_BAR_HEIGHT - 2)
#define GRID_WIDTH ((SCREEN_WIDTH - 2) / SNAKE_SIZE)
#define GRID_HEIGHT (GAME_AREA_HEIGHT / SNAKE_SIZE)
#define STATE_MENU 0
#define STATE_PLAYING 1
#define STATE_GAME_OVER 2
#define COLOR_BACKGROUND Black
#define COLOR_SNAKE Green
#define COLOR_FOOD Red
#define COLOR_BORDER White
extern Ticker game_timer;
extern int snake_x[GRID_WIDTH * GRID_HEIGHT];
extern int snake_y[GRID_WIDTH * GRID_HEIGHT];
extern int snake_length;
extern int snake_dx;
extern int snake_dy;
extern int food_x;
extern int food_y;
extern int score;
extern int game_state;
extern bool initialized;
void game_timer_check();
void init_game();
void update_game();
void draw_screen();
void draw_snake();
void draw_full_snake();
void erase_tail(int x, int y);
void draw_food();
void erase_food();
void draw_score();
void draw_borders();
void spawn_food();
bool check_collision();
void show_menu();
void show_game_over();
class SnakeView : public View {
public:
SnakeView(NavigationView& nav);
void on_show() override;
std::string title() const override { return "Snake"; }
void focus() override { dummy.focus(); }
void paint(Painter& painter) override;
void frame_sync();
bool on_key(KeyEvent key) override;
private:
bool initialized = false;
NavigationView& nav_;
Button dummy{
{240, 0, 0, 0},
""};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->frame_sync();
}};
};
} // namespace ui::external_app::snake
#endif /* __UI_SNAKE_H__ */
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2025 Mark Thompson
*
* 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_stopwatch.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::stopwatch {
void initialize_app(ui::NavigationView& nav) {
nav.push<StopwatchView>();
}
} // namespace ui::external_app::stopwatch
extern "C" {
__attribute__((section(".external_app.app_stopwatch.application_information"), used)) application_information_t _application_information_stopwatch = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::stopwatch::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Stopwatch",
/*.bitmap_data = */ {
0x00,
0x00,
0xC0,
0x01,
0x80,
0x00,
0x80,
0x20,
0x60,
0x13,
0x10,
0x0C,
0x88,
0x08,
0x84,
0x10,
0x84,
0x10,
0xC2,
0x21,
0x84,
0x10,
0x04,
0x10,
0x08,
0x08,
0x10,
0x04,
0x60,
0x03,
0x80,
0x00,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+309
View File
@@ -0,0 +1,309 @@
/*
* Copyright 2025 Mark Thompson
* copyleft Mr. Robot of F.Society
*
* 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_stopwatch.hpp"
#include "portapack.hpp"
#include "ch.h"
using namespace portapack;
namespace ui::external_app::stopwatch {
// clang-format off
// clang-format doesn't allow use as this way but it's not worth to have const var for this thing. too expensive
// minute
#define TOTAL_M1_POS {0 * 8 * 4, 3 * 16}
#define TOTAL_M2_POS {1 * 8 * 4, 3 * 16}
// sec
#define TOTAL_S1_POS {2 * 8 * 4 + 24, 3 * 16}
#define TOTAL_S2_POS {3 * 8 * 4 + 24, 3 * 16}
// ms
#define TOTAL_MS1_POS {4 * 8 * 4 + 7 * 8, 3 * 16 + 24}
#define TOTAL_MS2_POS {5 * 8 * 4 + 5 * 8, 3 * 16 + 24}
#define TOTAL_MS3_POS {6 * 8 * 4 + 3 * 8, 3 * 16 + 24}
#define LAP_Y 9 * 16
//lap min
#define LAP_M1_POS {0 * 8 * 4, LAP_Y}
#define LAP_M2_POS {1 * 8 * 4, LAP_Y}
//lap sec
#define LAP_S1_POS {2 * 8 * 4 + 24, LAP_Y}
#define LAP_S2_POS {3 * 8 * 4 + 24, LAP_Y}
//lap ms
#define LAP_MS1_POS {4 * 8 * 4 + 7 * 8, LAP_Y + 24}
#define LAP_MS2_POS {5 * 8 * 4 + 5 * 8, LAP_Y + 24}
#define LAP_MS3_POS {6 * 8 * 4 + 3 * 8, LAP_Y + 24}
// clang-format on
StopwatchView::StopwatchView(NavigationView& nav)
: painter{} {
add_children({
&labels,
&button_run_stop,
&button_reset_lap,
&button_done,
&options_ms_display_level,
});
button_run_stop.on_select = [this](Button&) {
if (running)
stop();
else
run();
};
button_reset_lap.on_select = [this](Button&) {
if (running)
lap();
else
reset();
};
button_done.on_select = [&nav](Button&) {
nav.pop();
};
options_ms_display_level.on_change = [&](size_t, ui::OptionsField::value_t) {
clean_ms_display(options_ms_display_level.selected_index());
};
options_ms_display_level.set_selected_index(0);
refresh_painting();
}
void StopwatchView::focus() {
button_run_stop.focus();
}
void StopwatchView::run() {
options_ms_display_level.hidden(true);
// ^ this won't take efferts if don't set_dirty, but needed to let user can't change during ticking
if (!paused) refresh_painting();
paused = false;
running = true;
start_time = chTimeNow() - previously_elapsed;
button_run_stop.set_text("STOP");
button_reset_lap.set_text("LAP");
}
void StopwatchView::stop() {
options_ms_display_level.hidden(false);
running = false;
paused = true;
end_time = chTimeNow();
previously_elapsed = end_time - start_time;
button_run_stop.set_text("START");
button_reset_lap.set_text("RESET");
}
void StopwatchView::reset() {
lap_time = end_time = start_time = previously_elapsed = 0;
for (uint8_t i = 0; i < 7; i++) {
refresh_painting();
}
refresh_painting();
set_dirty();
}
void StopwatchView::lap() {
lap_time = chTimeNow();
}
void StopwatchView::paint(Painter&) {
}
void StopwatchView::refresh_painting() {
// minute
painter.draw_char(TOTAL_M1_POS, *Theme::getInstance()->fg_light, ' ', 4);
painter.draw_char(TOTAL_M2_POS, *Theme::getInstance()->fg_light, ' ', 4);
// sec
painter.draw_char(TOTAL_S1_POS, *Theme::getInstance()->fg_light, ' ', 4);
painter.draw_char(TOTAL_S2_POS, *Theme::getInstance()->fg_light, ' ', 4);
// ms
painter.draw_char(TOTAL_MS1_POS, *Theme::getInstance()->fg_light, ' ', 2);
painter.draw_char(TOTAL_MS2_POS, *Theme::getInstance()->fg_light, ' ', 2);
painter.draw_char(TOTAL_MS3_POS, *Theme::getInstance()->fg_light, ' ', 2);
// lap min
painter.draw_char(LAP_M1_POS, *Theme::getInstance()->fg_light, ' ', 4);
painter.draw_char(LAP_M2_POS, *Theme::getInstance()->fg_light, ' ', 4);
// lap sec
painter.draw_char(LAP_S1_POS, *Theme::getInstance()->fg_light, ' ', 4);
painter.draw_char(LAP_S2_POS, *Theme::getInstance()->fg_light, ' ', 4);
// lap ms
painter.draw_char(LAP_MS1_POS, *Theme::getInstance()->fg_light, ' ', 2);
painter.draw_char(LAP_MS2_POS, *Theme::getInstance()->fg_light, ' ', 2);
painter.draw_char(LAP_MS3_POS, *Theme::getInstance()->fg_light, ' ', 2);
}
/*when user seted it to display more, then display less later, this prevent the remain value exist on screen*/
void StopwatchView::clean_ms_display(uint8_t level) {
level++;
switch (level) {
case 0:
painter.draw_char(TOTAL_MS1_POS, *Theme::getInstance()->fg_light, ' ', 2);
painter.draw_char(TOTAL_MS2_POS, *Theme::getInstance()->fg_light, ' ', 2);
painter.draw_char(TOTAL_MS3_POS, *Theme::getInstance()->fg_light, ' ', 2);
break;
case 1:
painter.draw_char(TOTAL_MS2_POS, *Theme::getInstance()->fg_light, ' ', 2);
painter.draw_char(TOTAL_MS3_POS, *Theme::getInstance()->fg_light, ' ', 2);
break;
case 2:
painter.draw_char(TOTAL_MS3_POS, *Theme::getInstance()->fg_light, ' ', 2);
break;
}
}
void StopwatchView::resume_last() {
// minute
painter.draw_char(TOTAL_M1_POS, *Theme::getInstance()->fg_light, last_displayed[0], 4);
painter.draw_char(TOTAL_M2_POS, *Theme::getInstance()->fg_light, last_displayed[1], 4);
// sec
painter.draw_char(TOTAL_S1_POS, *Theme::getInstance()->fg_light, last_displayed[2], 4);
painter.draw_char(TOTAL_S2_POS, *Theme::getInstance()->fg_light, last_displayed[3], 4);
// ms
painter.draw_char(TOTAL_MS1_POS, *Theme::getInstance()->fg_light, last_displayed[4], 2);
painter.draw_char(TOTAL_MS2_POS, *Theme::getInstance()->fg_light, last_displayed[5], 2);
painter.draw_char(TOTAL_MS3_POS, *Theme::getInstance()->fg_light, last_displayed[6], 2);
}
/* NB:
* Due to the flaw of dirty management, it's using work around, to reduce screen flickering:
*
* for example when xx:15:xxx turn to xx:16:xxx, it actually only paint 6, the 1 is old,
* but not dirty, so we can see without flikering.
*
* So with these work around, it won't show false info, but bare in mind that it could be, if you add more things.
*/
void StopwatchView::frame_sync() {
uint32_t elapsed_ticks = 0;
if (running) {
end_time = chTimeNow();
elapsed_ticks = end_time - start_time;
} else if (previously_elapsed > 0) {
elapsed_ticks = previously_elapsed;
}
constexpr uint32_t TICKS_PER_SECOND = CH_FREQUENCY;
constexpr uint32_t TICKS_PER_MINUTE = TICKS_PER_SECOND * 60;
uint32_t minutes = elapsed_ticks / TICKS_PER_MINUTE;
uint32_t seconds = (elapsed_ticks % TICKS_PER_MINUTE) / TICKS_PER_SECOND;
uint32_t milliseconds = ((elapsed_ticks % TICKS_PER_SECOND) * 1000) / TICKS_PER_SECOND;
// minute
if (last_displayed[0] != (minutes / 10) % 10)
painter.draw_char(TOTAL_M1_POS, *Theme::getInstance()->fg_red, '0' + (minutes / 10) % 10, 4);
if (last_displayed[1] != minutes % 10)
painter.draw_char(TOTAL_M2_POS, *Theme::getInstance()->fg_red, '0' + minutes % 10, 4);
// sec
if (last_displayed[2] != (seconds / 10) % 10)
painter.draw_char(TOTAL_S1_POS, *Theme::getInstance()->fg_green, '0' + (seconds / 10) % 10, 4);
if (last_displayed[3] != seconds % 10)
painter.draw_char(TOTAL_S2_POS, *Theme::getInstance()->fg_green, '0' + seconds % 10, 4);
// ms
/* v place holder to aligh logic*/
if ((true) && last_displayed[4] != (milliseconds / 100) % 10)
painter.draw_char(TOTAL_MS1_POS, *Theme::getInstance()->fg_yellow, '0' + (milliseconds / 100) % 10, 2);
if ((options_ms_display_level.selected_index() >= 1) && last_displayed[5] != (milliseconds / 10) % 10)
painter.draw_char(TOTAL_MS2_POS, *Theme::getInstance()->fg_yellow, '0' + (milliseconds / 10) % 10, 2);
if ((options_ms_display_level.selected_index() >= 2) && last_displayed[6] != milliseconds % 10)
painter.draw_char(TOTAL_MS3_POS, *Theme::getInstance()->fg_yellow, '0' + milliseconds % 10, 2);
// min
last_displayed[0] = (minutes / 10) % 10;
last_displayed[1] = minutes % 10;
// sec
last_displayed[2] = (seconds / 10) % 10;
last_displayed[3] = seconds % 10;
// ms
last_displayed[4] = (milliseconds / 100) % 10;
last_displayed[5] = (milliseconds / 10) % 10;
last_displayed[6] = milliseconds % 10;
if (lap_time > 0) {
uint32_t lap_elapsed = lap_time - start_time;
uint32_t lap_minutes = lap_elapsed / TICKS_PER_MINUTE;
uint32_t lap_seconds = (lap_elapsed % TICKS_PER_MINUTE) / TICKS_PER_SECOND;
uint32_t lap_milliseconds = ((lap_elapsed % TICKS_PER_SECOND) * 1000) / TICKS_PER_SECOND;
// lap min
if (lap_last_displayed[0] == (lap_minutes / 10) % 10) {
painter.draw_char(LAP_M1_POS, *Theme::getInstance()->fg_light, '0' + (lap_minutes / 10) % 10, 4);
}
if (lap_last_displayed[1] == lap_minutes % 10) {
painter.draw_char(LAP_M2_POS, *Theme::getInstance()->fg_light, '0' + lap_minutes % 10, 4);
}
// lap sec
if (lap_last_displayed[2] == (lap_seconds / 10) % 10) {
painter.draw_char(LAP_S1_POS, *Theme::getInstance()->fg_light, '0' + (lap_seconds / 10) % 10, 4);
}
if (lap_last_displayed[3] == lap_seconds % 10) {
painter.draw_char(LAP_S2_POS, *Theme::getInstance()->fg_light, '0' + lap_seconds % 10, 4);
}
// lap ms
if (lap_last_displayed[4] == (lap_milliseconds / 100) % 10) {
painter.draw_char(LAP_MS1_POS, *Theme::getInstance()->fg_light, '0' + (lap_milliseconds / 100) % 10, 2);
}
if (lap_last_displayed[5] == (lap_milliseconds / 10) % 10) {
painter.draw_char(LAP_MS2_POS, *Theme::getInstance()->fg_light, '0' + (lap_milliseconds / 10) % 10, 2);
}
if (lap_last_displayed[6] == lap_milliseconds % 10) {
painter.draw_char(LAP_MS3_POS, *Theme::getInstance()->fg_light, '0' + lap_milliseconds % 10, 2);
}
// lp m
lap_last_displayed[0] = (lap_minutes / 10) % 10;
lap_last_displayed[1] = lap_minutes % 10;
// lp s
lap_last_displayed[2] = (lap_seconds / 10) % 10;
lap_last_displayed[3] = lap_seconds % 10;
// lp mss
lap_last_displayed[4] = (lap_milliseconds / 100) % 10;
lap_last_displayed[5] = (lap_milliseconds / 10) % 10;
lap_last_displayed[6] = lap_milliseconds % 10;
}
}
} // namespace ui::external_app::stopwatch
@@ -0,0 +1,94 @@
/*
* Copyright 2025 Mark Thompson
* copyleft Mr. Robot of F.Society
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_STOPWATCH_H__
#define __UI_STOPWATCH_H__
#include "ui_navigation.hpp"
namespace ui::external_app::stopwatch {
class StopwatchView : public View {
public:
StopwatchView(NavigationView& nav);
void focus() override;
void paint(Painter& painter) override;
void frame_sync();
std::string title() const override { return "Stopwatch"; };
private:
void run();
void stop();
void reset();
void lap();
void cover_display_area_with_0();
void refresh_painting();
void resume_last();
void clean_ms_display(uint8_t level = 0);
bool running{false};
bool paused{false};
long long start_time{0};
long long end_time{0};
long long lap_time{0};
long long previously_elapsed{0};
uint8_t last_displayed[7] = {0, 0, 0, 0, 0, 0, 0};
/* m m s s ms ms ms*/
uint8_t lap_last_displayed[7] = {0, 0, 0, 0, 0, 0, 0};
/* m m s s ms ms ms*/
Labels labels{
{{0 * 8, 1 * 16}, "TOTAL:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "LAP:", Theme::getInstance()->fg_light->foreground},
};
OptionsField options_ms_display_level{
{4 * 8 * 4 + 7 * 8 + 4, 2 * 16},
5,
{{"& - -", 0},
{"& & -", 1},
{"& & &", 2}}};
Painter painter;
Button button_run_stop{
{72, 210, 96, 24},
"START"};
Button button_reset_lap{
{72, 240, 96, 24},
"RESET"};
Button button_done{
{72, 270, 96, 24},
"EXIT"};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->frame_sync();
}};
};
} // namespace ui::external_app::stopwatch
#endif /*__UI_STOPWATCH_H__*/
-29
View File
@@ -1,29 +0,0 @@
/*
* Copyright (C) 2024 Mark Thompson
*
* 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.
*/
// dummy include file to avoid changing original source
#ifndef __UI_Arial12x12_H__
#define __UI_Arial12x12_H__
#define Arial12x12 (0)
#endif /*__UI_Arial12x12_H__*/
-110
View File
@@ -1,110 +0,0 @@
/*
* Copyright (C) 2024 Mark Thompson
*
* 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.
*/
// "HAL" display layer for Tetris code to run on PortaPack without its original ILI9341 functions
#ifndef __UI_SPI_TFT_ILI9341_H__
#define __UI_SPI_TFT_ILI9341_H__
ui::Painter painter;
static int x_pos{0};
static int y_pos{0};
static int fg_color;
static int bg_color;
enum {
White,
Blue,
Yellow,
Purple,
Green,
Red,
Maroon,
Orange,
Black,
};
// pp_colors must be in same order as enums above
static const Color pp_colors[] = {
Color::white(),
Color::blue(),
Color::yellow(),
Color::purple(),
Color::green(),
Color::red(),
Color::magenta(),
Color::orange(),
Color::black(),
};
// NB: ELIMINATED SPI_TFT_ILI9341 DISPLAY CLASS DUE TO GLOBAL OBJECT INITIALIZATION ISSUE WITH EXTERNAL APPS
static void claim(__FILE* x) {
(void)x;
};
static void cls() {
painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black());
};
static void background(int color) {
bg_color = color;
};
static void foreground(int color) {
fg_color = color;
};
static void locate(int x, int y) {
x_pos = x;
y_pos = y;
};
static void set_orientation(int x) {
(void)x;
};
static void set_font(unsigned char* x) {
(void)x;
};
static void fillrect(int x1, int y1, int x2, int y2, int color) {
painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
};
static void rect(int x1, int y1, int x2, int y2, int color) {
painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
};
static void printf(std::string str) {
auto style = (fg_color == White) ? *ui::Theme::getInstance()->bg_darkest : *ui::Theme::getInstance()->bg_lightest;
painter.draw_string({x_pos, y_pos - 1}, style, str);
};
static void printf(std::string str, int v) {
if (str.find_first_of("%") != std::string::npos) {
str.resize(str.find_first_of("%")); // remove %d from end of string
}
printf(str + to_string_dec_uint(v));
};
#endif /*__UI_SPI_TFT_ILI9341_H__*/
+2 -2
View File
@@ -73,8 +73,8 @@ __attribute__((section(".external_app.app_tetris.application_information"), used
0xFF,
0xF1,
},
/*.icon_color = */ ui::Color::orange().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::GAMES,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
-220
View File
@@ -1,220 +0,0 @@
/*
* Copyright (C) 2024 Mark Thompson
*
* 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.
*/
// "HAL" layer for Tetris code to run on PortaPack without its original mbed OS
// (the dream here was to avoid modifying the original code)
#ifndef __UI_mbed_H__
#define __UI_mbed_H__
using Callback = void (*)(void);
#define wait_us(x) (void)0
#define wait(x) chThdSleepMilliseconds(x * 1000)
#define PullUp 1
enum {
dp0,
dp1,
dp2,
dp3,
dp4,
dp5,
dp6,
dp7,
dp8,
dp9,
dp10,
dp11,
dp12,
dp13,
dp14,
dp15,
dp16,
dp17,
dp18,
dp19,
dp20,
dp21,
dp22,
dp23,
dp24,
dp25,
};
static bool but_RIGHT;
static bool but_LEFT;
static bool but_UP;
static bool but_DOWN;
static bool but_SELECT;
//
// AnalogIn Class -- DID NOT WORK DUE TO GLOBAL OBJECT INITIALIZER ISSUE WITH EXTERNAL APPS -- hacked original code module instead
//
// dp9 = joystick rotate button --> select button
// dp10 = joystick y --> up & down buttons
// dp11 = joystick x --> left & right buttons
// dp13 = random number generator
//
//
// class AnalogIn {
// public:
// AnalogIn(uint32_t analog_input) {
// // FIXME - THIS CODE NEVER GETS EXECUTED!
// analog_input_ = analog_input;
// };
//
// // Tetris code only uses this function for dp13 - supposed to be a random number
// uint16_t read_u16() {
// return std::rand();
// };
//
// // Tetris code uses read() function for direction buttons only
// float read() {
// float retval = 0.5;
// switch (analog_input_) {
// case dp11:
// if (but_LEFT)
// retval = 0.0;
// else if (but_RIGHT)
// retval = 1.0;
// break;
//
// case dp10:
// if (but_UP)
// retval = 0.0;
// else if (but_DOWN)
// retval = 1.0;
// break;
// }
// return retval;
// };
//
// operator float() {
// return read();
// };
//
// private:
// uint32_t analog_input_{INT_MAX};
// };
//
// Timer Class
// (Timer object was used for unneeded button debouncing, so just returning 1000ms to indicate we've waited long enough)
//
class Timer {
public:
// NOTE: INITIALIZER CODE WON'T RUN
Timer() { (void)0; };
void reset() { (void)0; };
void start() { (void)0; }
uint32_t read_ms() { return 1000; };
private:
};
//
// Ticker Class
// (Ticker is timed callback, used for checking "joystick" directional switches and when time to move piece down a row)
//
// NB: Only one callback is supported per Ticker class instantiation
static Callback fall_timer_callback;
static uint32_t fall_timer_timeout;
static uint32_t fall_timer_counter;
static Callback dir_button_callback;
static void check_fall_timer() {
if (fall_timer_callback) {
if (++fall_timer_counter >= fall_timer_timeout) {
fall_timer_counter = 0;
fall_timer_callback();
}
}
}
class Ticker {
public:
// NOTE: INITIALIZER CODE WON'T RUN
Ticker() { (void)0; };
void attach(Callback func, double delay_sec) {
// 0.3 sec is requested only for button check -- kludge to use on_key callback for this one instead of timer
if (delay_sec == 0.3) {
dir_button_callback = func;
} else {
fall_timer_callback = func;
fall_timer_timeout = delay_sec * 60; // timer interrupts at 60 Hz
}
}
void detach() {
// shouldn't detach both, but don't know how to tell which object is which
dir_button_callback = nullptr;
fall_timer_callback = nullptr;
}
private:
};
//
// InterruptIn Class
// (just used for the Select button)
//
static Callback sel_button_callback;
static bool check_encoder(const EncoderEvent delta) {
(void)delta;
// TODO: consider adding ability to rotate Tetronimo via encoder too
return false;
}
static bool check_key(const KeyEvent key) {
auto switches_debounced = get_switches_state().to_ulong();
but_RIGHT = (switches_debounced & 0x01) != 0;
but_LEFT = (switches_debounced & 0x02) != 0;
but_DOWN = (switches_debounced & 0x04) != 0;
but_UP = (switches_debounced & 0x08) != 0;
but_SELECT = (switches_debounced & 0x10) != 0;
if (key == KeyEvent::Select) {
if (sel_button_callback)
sel_button_callback();
} else {
if (dir_button_callback)
dir_button_callback();
}
return true;
}
class InterruptIn {
public:
InterruptIn(int reg) {
// NOTE: INITIALIZER CODE WON'T RUN
(void)reg;
};
void fall(Callback func) { sel_button_callback = func; };
void mode(int v) { (void)v; };
private:
};
#endif /*__UI_mbed_H__*/
-548
View File
@@ -1,548 +0,0 @@
// Projekat Tetris
// by Vrnjak Lamija & Selimović Denis
// Elektrotehnički fakultet Sarajevo
// clang-format off
//////// HACKED FOR PORTAPACK -- CHANGES HIGHLIGHTED
int main();
void pause_game();
void Initialize(unsigned char c);
void DeleteFigure();
void DrawFigure();
bool InCollisionDown(char delta);
bool InCollisionLeft();
bool InCollisionRight();
//////// PORTAPACK
#include "mbed.h"
#include "SPI_TFT_ILI9341.h"
#include "Arial12x12.h"
#define dp23 P0_0
//////// PORTAPACK - DISABLED MOST CLASSES DUE TO GLOBAL OBJECT INITIALIZER ISSUE WITH EXTERNAL APPS:
//deklaracija display-a
//SPI_TFT_ILI9341 display(dp2, dp1, dp6, dp24, dp23, dp25, "TFT");
//
//analogni ulazi za joystick
// AnalogIn VRx(dp11);
// AnalogIn VRy(dp10);
//
// AnalogIn random(dp13); //analogni ulaz za generisanje random vrijednosti
//////// PORTAPACK
//taster na joysticku za rotaciju
InterruptIn taster(dp9);
//ticker za spustanje figure
//timer za debouncing tastera na joysticku
Ticker game, joystick;
Timer debounceTaster;
unsigned char level = 0; //mora biti tipa usigned char jer inače se može desiti da level bude manji od 0, a i da ne trošimo memoriju
const float delays[4] = {1.2, 0.7, 0.4, 0.25}; //svakih koliko se spusti jedan red, ovo provjeriti da li je presporo ili prebrzo, ovisi o levelu
//////// PORTAPACK - DELETED UNNEEDED JOYSTICK HYSTERESIS VARIABLES
//char leftBoundary = 1, rightBoundary = 5, downBoundary = 1, upBoundary = 5;// sada je ovo tipa char
//////// PORTAPACK
unsigned int score = 0; //stavio sam ovo unsigned int za veći opseg, mada je jako teško da se i int premaši, ali nmvz
bool firstTime = true; //ako je prvi put, figura se crta u Tickeru
bool gameStarted = false;
unsigned char nextFigure = 1; //ovo je sad globalna varijabla, da bi mogli unaprijed generisati sljedeću figuru radi prikaza
//white - no figure
//I - BLUE
//O - YELLOW
//T - PURPLE
//S - GREEN
//Z - RED
//J - MAROON
//L - ORANGE
const int colors[8] = {White, Blue, Yellow, Purple, Green, Red, Maroon, Orange};
const short DIMENSION = 16;
const short DIMENSION_NEXT = 12;
short board[20][10] = {0}; //matrica boja, 0 - 7 indeksi boja
short figuresX[7][4] = {{0,0,0,0}, {0,0,1,1}, {0,1,1,1}, {1,1,0,0}, {0,1,0,1}, {1,1,1,0}, {1,1,1,0}};
short figuresY[7][4] = {{0,1,2,3}, {1,0,0,1}, {1,1,2,0}, {0,1,1,2}, {0,1,1,2}, {2,1,0,0}, {0,1,2,2}};
unsigned int GenerateRandomSeed() {
//////// PORTAPACK - USE RTC FOR SEED
return LPC_RTC->CTIME0;
//////// PORTAPACK
}
void Init() {
//ovo su zajedničke osobine za sve prikaze na display-u
//nikad se ne mijenjaju i pozvat ćemo je jednom prije petlje
claim(stdout);
set_orientation(2); // 2 ili 0, zavisi kako okrenemo display, provjerit ćemo na labu kako nam je najlakše povezat
set_font((unsigned char*) Arial12x12);
}
void ShowScore() {
//pomocna funkcija za prikazivanje score-a
fillrect(165, 20, 235, 50, White); //popunimo pravugaonik da obrišemo stari score
locate(200, 35); //valjda je na sredini pravougaonika
printf("%d", score);
}
void ShowNextFigure() {
//prikaz sljedeće figure koristeći pomoćnu varijablu nextFigure
fillrect(165, 70, 235, 120, White);
int upperLeftX = 176, upperLeftY = 83;
for(int i = 0; i < 4; i++) {
int x = upperLeftX + DIMENSION_NEXT * figuresY[nextFigure - 1][i], y = upperLeftY + DIMENSION_NEXT * figuresX[nextFigure - 1][i];
fillrect(x, y, x + DIMENSION_NEXT, y + DIMENSION_NEXT, colors[nextFigure]);
rect(x, y, x + DIMENSION_NEXT, y + DIMENSION_NEXT, Black);
}
}
//funkcija za crtanje cursora za odabir levela
void DrawCursor(int color, unsigned char lev) {
fillrect(60, lev * 70 + 50, 72, lev * 70 + 50 + 12, color);
}
// PORTAPACK - ADDED EXTRA LEVEL:
void ShowLevelMenu() {
//ovdje inicijalizujemo display
cls(); // brišemo prethodno
background(Black);
foreground(White);
locate(80, 50);
printf("LEVEL 1");
locate(80, 120);
printf("LEVEL 2");
locate(80, 190);
printf("LEVEL 3");
locate(80, 260);
printf("LEVEL 4");
DrawCursor(White, level);
}
//////// PORTAPACK
//////// PORTAPACK - USE BUTTONS VS JOYSTICK:
void ReadJoystickForLevel(){
unsigned char old = level;
if(but_UP){
(level == 0) ? level = 3 : level--;
}
else if(but_DOWN){
//ne radi ona prethodna varijanta jer % vraća i negastivni rezultat
//to što ne koristimo unsigned tip ne pomaže jer će doći do overflow-a
level = (level + 1) % 4;
}
DrawCursor(Black, old); //na prethodni level popunimo bojom pozadine
DrawCursor(White, level); //na novi level popunimo bijelom bojom - pozadina je crna
//koristio sam fillrect, jer njega svakako moramo koristiti, jer možda budemo morali da brišemo fillcircle iz biblioteke
}
//////// PORTAPACK
void EndPlay() {
joystick.detach();
score = 0;
firstTime = true;
gameStarted = false;
for(int i = 0; i < 20; i++) {
for(int j = 0; j < 10; j++) {
board[i][j] = 0;
}
}
//////// PORTAPACK - FIX TO REINITIALIZE SCREEN
// ShowLevelMenu();
// joystick.attach(&ReadJoystickForLevel, 0.3);
main();
//////// PORTAPACK
}
void StartGame()
{
cls(); // brišemo ShowLevelMenu
background(White);
foreground(Black);
fillrect(0, 0, 160, 320, White);
fillrect(160, 0, 240, 320, Black); //dio za prikazivanje rezultata će biti crni pravougaonik, a tabla je bijeli
ShowScore();
}
void copyCoordinates(short X[], short Y[], unsigned char index)
{
//umjesto one prošle metode za kopiranje, ova prima index, čisto da nam olakša život
for(int i = 0; i < 4; i++) {
X[i] = figuresX[index][i];
Y[i] = figuresY[index][i];
}
}
bool BottomEdge(int x){
return x > 19;
}
bool LeftEdge(int y){
return y < 0;
}
bool RightEdge(int y){
return y > 9;
}
bool OutOfBounds(int y, int x){
return y < 0 || y > 9 || x > 19;
}
void PutBorders(short x, short y) {
for(int i = x - 1; i <= x + 1; i++) {
for(int j = y - 1; j <= y + 1; j++) {
if(i < 0 || i > 9 || j < 0 || j > 19 || board[j][i] == 0) continue;
rect(i * DIMENSION, j * DIMENSION, (i + 1) * DIMENSION, (j + 1) * DIMENSION, Black);
}
}
}
//////// PORTAPACK - ELIMINATED CLASSES DUE TO GLOBAL OBJECT INITIALIZATION ISSUE WITH EXTERNAL APPS:
//class Tetromino{
//private:
short X[4];
short Y[4];
short boardX, boardY;
unsigned char colorIndex;//dodao sam colorIndex zasad, jer nema drugog načina da popunimo matricu sa indeksima boja
//ovo je najbezbolnija varijanta što se memorije tiče
//public:
// Tetromino(){
// unsigned char r = rand() % 7 + 1;
// Initialize(r);
// }
void Tetromino(unsigned char c) {
Initialize(c);
}
void Initialize(unsigned char c) {
colorIndex = c;
boardX = 0;
boardY = 4; //3,4 ili 5 najbolje da vidimo kad imamo display
copyCoordinates(X, Y, c - 1);
}
void Rotate(){
short pivotX = X[1], pivotY = Y[1];
//prvi elemnti u matrici su pivoti oko koje rotiramo
short newX[4]; //pozicije blokova nakon rotiranja ako sve bude ok
short newY[4];
for(int i = 0; i < 4; i++){
short tmp = X[i], tmp2 = Y[i];
newX[i] = pivotX + pivotY - tmp2;
newY[i] = tmp + pivotX - pivotY;
if(OutOfBounds(boardY + newY[i], boardX + newX[i]) || board[boardX + newX[i]][boardY + newY[i]] != 0) return;
}
DeleteFigure();
for(int i = 0; i < 4; i++){
X[i] = newX[i];
Y[i] = newY[i];
}
DrawFigure();
}
void DrawFigure() {
for(int i = 0; i < 4; i++) {
//display je deklarisani display logy iz biblioteke one
//računamo gornji lijevi pixel po x i y
//donji desni dobijemo kad dodamo DIMENZIJU
//stavio sam 16 za početak, možemo se opet skontati na labu
//ovo pretpostavlja da nema margina, mogu se lagano dodati uz neku konstantu kao offset
int upperLeftX = (boardX + X[i]) * DIMENSION, upperLeftY = (boardY + Y[i]) * DIMENSION;
fillrect( upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, colors[colorIndex]);
//ovo boji granice blokova u crno, možemo skloniti ako ti se ne sviđa
rect( upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, Black);
}
}
void DeleteFigure() {
for(int i = 0; i < 4; i++) {
//ista logika kao u DrawFigure, samo popunjavamo sve blokove sa bijelim pravougaonicima
short upperLeftX = (boardX + X[i]) * DIMENSION, upperLeftY = (boardY + Y[i]) * DIMENSION;
fillrect( upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, White);
PutBorders(upperLeftY, upperLeftX);
}
}
void OnAttached() {
//metoda se poziva kad figura prestanje da se krece
//popunimo matricu indeksom boje
for(int i = 0; i < 4; i++) {
board[boardX + X[i]][boardY + Y[i]] = colorIndex;
//btw board bi mogao biti niz tipa unsigned char, ali to ćemo vidjet kasnije
}
}
bool MoveDown(char delta = 1){
if(!InCollisionDown(delta)){
DeleteFigure();
boardX+=delta;
DrawFigure();
return true;
}
return false;
}
void MoveLeft(){
if(!InCollisionLeft()){
DeleteFigure();
boardY--;
DrawFigure();
}
}
void MoveRight(){
if(!InCollisionRight()){
DeleteFigure();
boardY++;
DrawFigure();
}
}
void SoftDrop() {
//ovo je funkcija za soft drop
//obrisemo figuru, postavimo novu poziciju dva reda ispod, nacrtamo figuru
DeleteFigure();
MoveDown(2);
DrawFigure();
//treba još vidjeti koje izmjene u tickeru trebaju
score += 2 * (level +1); //prema igrici koju smo igrali, dobije se 14 poena kad se uradi hard drop
ShowScore();
}
bool InCollisionDown(char delta = 1){
int newX, newY; //da bi bilo citljivije
for(int i = 0; i < 4; i++){
newX = boardX + X[i] + delta;
newY = boardY + Y[i];
if(BottomEdge(newX) || board[newX][newY] != 0){
return true;
}
//jedna figura je u koliziji ako
//pozicija na koju zelimo da pomjerimo bilo koji blok dotakne dno ili lijevu ili desnu ivicu ekrana
//ili ako je pozicija na koju zelimo da pomjerimo bilo koji od blokova vec zauzeta a da nije dio figure prije pomijeranja
}
return false;
}
bool InCollisionLeft(){
int newX, newY;
for(int i = 0; i < 4; i++){
newX = boardX + X[i];
newY = boardY + Y[i] - 1;
if(LeftEdge(newY) || board[newX][newY] != 0){
return true;
}
}
return false;
}
bool InCollisionRight(){
int newX, newY;
for(int i = 0; i < 4; i++){
newX = boardX + X[i];
newY = boardY + Y[i] + 1;
if(RightEdge(newY) || board[newX][newY] != 0){
return true;
}
}
return false;
}
// };
// Tetromino currentTetromino;
//////// PORTAPACK - USE BUTTONS VS JOYSTICK, AND ADDED PAUSE FEATURE:
void ReadJoystickForFigure() {
if(but_LEFT) {
MoveLeft();
}
else if(but_RIGHT) {
MoveRight();
}
else if(but_UP) {
pause_game();
}
else if(but_DOWN) {
SoftDrop();
}
}
//////// PORTAPACK
void CheckLines(short &firstLine, short &numberOfLines)
{
//vraća preko reference prvu liniju koja je popunjena do kraja, kao i takvih linija
firstLine = -1; //postavljen na -1 jer ako nema linija koje treba brisati ispod u UpdateBoard neće se ući u petlju
numberOfLines = 0;
for(int i = 19; i >= 0; i--) {
short temp = 0;
for(int j = 0; j < 10; j++) {
if(board[i][j] == 0) {
if(numberOfLines > 0) return;
break;
}//ako je makar jedna bijela, prekida se brojanje
temp++;
}
if(temp == 10) { //ako je temo došao do 10, niti jedna bijela - puna linija
numberOfLines++;
if(firstLine == -1) firstLine = i; //ovo mijenjamo samo prvi put
}
}
}
unsigned int UpdateScore (short numOfLines){
unsigned int newIncrement = 0;
switch(numOfLines) {
case 1 : newIncrement = 40; break;
case 2 : newIncrement = 100; break;
case 3 : newIncrement = 300; break;
case 4 : newIncrement = 1200; break;
default : newIncrement = 0; break; //update funkcije za score, još sam vratio ovo na 0
}
return newIncrement * (level + 1);
}
void UpdateBoard()
{
short firstLine, numberOfLines;
//pozivamo funkciju
do {
CheckLines(firstLine, numberOfLines);
for(int i = firstLine; i >= numberOfLines; i--) {
for(int j = 0; j < 10; j++) {
board[i][j] = board[i - numberOfLines][j];
board[i - numberOfLines][j] = 0;
short tmp = i - numberOfLines;
fillrect( j * DIMENSION,i * DIMENSION, (j + 1) * DIMENSION , (i + 1) * DIMENSION , colors[board[i][j]]); // bojimo novi blok
fillrect( j * DIMENSION, tmp * DIMENSION,(j + 1) * DIMENSION, (tmp + 1) * DIMENSION , White);
if(board[i][j] != 0)
rect( j * DIMENSION,i * DIMENSION, (j + 1) * DIMENSION , (i + 1) * DIMENSION , Black);
}
}
score += UpdateScore(numberOfLines);
}
while(numberOfLines != 0); //ovdje se mijenja globalna varijabla score
}
bool IsOver() {
for(int i = 0; i < 10; i++) {
if(board[0][i] != 0) return true;
}
return false;
}
void ShowGameOverScreen() {
//////// PORTAPACK - SKIP CLS
// cls();
// background(Black);
// foreground(White);
background(White);
foreground(Black);
//////// PORTAPACK
locate(60, 120);
printf("GAME OVER");
locate(40, 150);
printf("YOUR SCORE IS %d", score);
wait(5); //ovaj prikaz traje 3s (možemo mijenjati) a nakon toga se ponovo prikazuje meni sa levelima
}
void InitGame() {
if(firstTime) {
Tetromino(rand() % 7 + 1);
DrawFigure();
nextFigure = rand() % 7 + 1;
ShowNextFigure();
firstTime = false;
}
}
void PlayGame(){
InitGame();
if(!MoveDown()){
OnAttached();
UpdateBoard();
ShowScore();
Tetromino(nextFigure);
DrawFigure();
nextFigure = rand() % 7 + 1;
ShowNextFigure();
if(IsOver()) {
//ako je igra završena brišemo sve sa displey-a, prikazujemo poruku i score
//takođe moramo dettach-at ticker
game.detach();
ShowGameOverScreen(); //prikaz da je kraj igre, ima wait od 3s
EndPlay();
}
}
}
void OnTasterPressed(){
if(debounceTaster.read_ms() > 200) {
if(gameStarted){
Rotate();
}
else{
joystick.detach();
gameStarted = true;
StartGame(); //pocinje igra, prikazuje se tabla
joystick.attach(&ReadJoystickForFigure, 0.3);
game.attach(&PlayGame, delays[level]); //svakih nekoliko spusta figuru jedan red nize
}
debounceTaster.reset();
}
}
void SetTaster() {
taster.mode(PullUp); //mora se aktivirati pull up otpornik na tasteru joystick-a
taster.fall(&OnTasterPressed);
debounceTaster.reset();
debounceTaster.start();
}
int main() {
srand(GenerateRandomSeed());
Init();
ShowLevelMenu();
joystick.attach(&ReadJoystickForLevel, 0.3);
SetTaster();
//////// PORTAPACK: CAN'T HANG HERE IN MAYHEM OR WE WON'T GET ANY CALLBACKS
// while(1);
return 0;
//////// PORTAPACK
}
//////// PORTAPACK - ADDED PAUSE FEATURE:
void pause_game() {
game.detach();
joystick.detach();
locate(180, 200);
printf("PAUSED");
while ((get_switches_state().to_ulong() & 0x10) == 0); // wait for SELECT button to resume
printf(" ");
joystick.attach(&ReadJoystickForFigure, 0.3);
game.attach(&PlayGame, delays[level]); //svakih nekoliko spusta figuru jedan red nize
};
//////// PORTAPACK
+590 -11
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2024 Mark Thompson
* 2025 updates by RocketGod (https://betaskynet.com/)
*
* This file is part of PortaPack.
*
@@ -23,23 +24,567 @@
namespace ui::external_app::tetris {
#pragma GCC diagnostic push
// external code, so ignore warnings
#pragma GCC diagnostic ignored "-Weffc++"
#include "tetris.cpp"
#pragma GCC diagnostic pop
Ticker game;
Ticker joystick;
unsigned char level = 0;
const float delays[4] = {1.2, 0.7, 0.4, 0.25};
unsigned int score = 0;
bool firstTime = true;
bool gameStarted = false;
unsigned char nextFigure = 1;
short board[20][10] = {0};
const int colors[8] = {White, Blue, Yellow, Purple, Green, Red, Maroon, Orange};
const short DIMENSION = 16;
const short DIMENSION_NEXT = 12;
short figuresX[7][4] = {{0, 0, 0, 0}, {0, 0, 1, 1}, {0, 1, 1, 1}, {1, 1, 0, 0}, {0, 1, 0, 1}, {1, 1, 1, 0}, {1, 1, 1, 0}};
short figuresY[7][4] = {{0, 1, 2, 3}, {1, 0, 0, 1}, {1, 1, 2, 0}, {0, 1, 1, 2}, {0, 1, 1, 2}, {2, 1, 0, 0}, {0, 1, 2, 2}};
const Color pp_colors[] = {
Color::white(),
Color::blue(),
Color::yellow(),
Color::purple(),
Color::green(),
Color::red(),
Color::magenta(),
Color::orange(),
Color::black(),
};
Painter painter;
bool but_RIGHT = false;
bool but_LEFT = false;
bool but_UP = false;
bool but_DOWN = false;
bool but_SELECT = false;
static int x_pos{0};
static int y_pos{0};
static int rotation_state = 0;
static int fg_color;
static int bg_color;
static Callback fall_timer_callback = nullptr;
static uint32_t fall_timer_timeout = 0;
static uint32_t fall_timer_counter = 0;
static Callback dir_button_callback = nullptr;
void cls() {
painter.fill_rectangle({0, 0, portapack::display.width(), portapack::display.height()}, Color::black());
}
void background(int color) {
bg_color = color;
}
void foreground(int color) {
fg_color = color;
}
void locate(int x, int y) {
x_pos = x;
y_pos = y;
}
void fillrect(int x1, int y1, int x2, int y2, int color) {
painter.fill_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
}
void rect(int x1, int y1, int x2, int y2, int color) {
painter.draw_rectangle({x1, y1, x2 - x1, y2 - y1}, pp_colors[color]);
}
void printf(std::string str) {
auto style = (fg_color == White) ? *ui::Theme::getInstance()->bg_darkest : *ui::Theme::getInstance()->fg_light;
painter.draw_string({x_pos, y_pos - 1}, style, str);
}
void printf(std::string str, int v) {
if (str.find_first_of("%") != std::string::npos) {
str.resize(str.find_first_of("%"));
}
printf(str + to_string_dec_uint(v));
}
void check_fall_timer() {
if (fall_timer_callback) {
if (++fall_timer_counter >= fall_timer_timeout) {
fall_timer_counter = 0;
fall_timer_callback();
}
}
}
void Ticker::attach(Callback func, double delay_sec) {
if (delay_sec == 0.3) {
dir_button_callback = func;
} else {
fall_timer_callback = func;
fall_timer_timeout = delay_sec * 60;
}
}
void Ticker::detach() {
dir_button_callback = nullptr;
fall_timer_callback = nullptr;
}
unsigned int GenerateRandomSeed() {
return LPC_RTC->CTIME0;
}
void Init() {
}
void ShowScore() {
fillrect(165, 10, 235, 60, Black);
rect(165, 10, 235, 60, White);
locate(200, 35);
printf("%d", score);
}
void ShowNextFigure() {
fillrect(165, 70, 235, 130, Black);
rect(165, 70, 235, 130, White);
int upperLeftX = 176, upperLeftY = 83;
for (int i = 0; i < 4; i++) {
int x = upperLeftX + DIMENSION_NEXT * figuresY[nextFigure - 1][i], y = upperLeftY + DIMENSION_NEXT * figuresX[nextFigure - 1][i];
fillrect(x, y, x + DIMENSION_NEXT, y + DIMENSION_NEXT, colors[nextFigure]);
rect(x, y, x + DIMENSION_NEXT, y + DIMENSION_NEXT, Black);
}
}
void DrawCursor(int color, unsigned char lev) {
fillrect(60, lev * 70 + 50, 72, lev * 70 + 50 + 12, color);
}
void ShowLevelMenu() {
cls();
background(Black);
foreground(White);
locate(80, 50);
printf("LEVEL 1");
locate(80, 120);
printf("LEVEL 2");
locate(80, 190);
printf("LEVEL 3");
locate(80, 260);
printf("LEVEL 4");
DrawCursor(White, level);
}
void ReadJoystickForLevel() {
unsigned char old = level;
if (but_UP) {
(level == 0) ? level = 3 : level--;
} else if (but_DOWN) {
level = (level + 1) % 4;
}
if (old != level) {
DrawCursor(Black, old);
DrawCursor(White, level);
}
}
void EndPlay() {
joystick.detach();
game.detach();
score = 0;
firstTime = true;
gameStarted = false;
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 10; j++) {
board[i][j] = 0;
}
}
ShowLevelMenu();
joystick.attach(&ReadJoystickForLevel, 0.3);
}
void StartGame() {
cls();
background(Black);
foreground(White);
fillrect(0, 0, 162, 320, Black);
rect(162, 0, 164, 320, White);
fillrect(164, 0, 240, 320, Black);
ShowScore();
ShowNextFigure();
}
void copyCoordinates(short X[], short Y[], unsigned char index) {
for (int i = 0; i < 4; i++) {
X[i] = figuresX[index][i];
Y[i] = figuresY[index][i];
}
}
bool BottomEdge(int x) {
return x > 19;
}
bool LeftEdge(int y) {
return y < 0;
}
bool RightEdge(int y) {
return y > 9;
}
bool OutOfBounds(int y, int x) {
return y < 0 || y > 9 || x > 19;
}
void PutBorders(short x, short y) {
for (int i = x - 1; i <= x + 1; i++) {
for (int j = y - 1; j <= y + 1; j++) {
if (i < 0 || i > 9 || j < 0 || j > 19 || board[j][i] == 0) continue;
rect(i * DIMENSION, j * DIMENSION, (i + 1) * DIMENSION, (j + 1) * DIMENSION, Black);
}
}
}
short X[4];
short Y[4];
short boardX, boardY;
unsigned char colorIndex;
void Tetromino(unsigned char c) {
Initialize(c);
}
void Initialize(unsigned char c) {
colorIndex = c;
boardX = 0;
boardY = 4;
copyCoordinates(X, Y, c - 1);
rotation_state = 0;
}
void Rotate() {
short newX[4], newY[4];
int next_state = (rotation_state + 1) % 4;
if (colorIndex == 2) {
return;
}
const short kick_tests_I[4][5][2] = {
{{0, 0}, {-2, 0}, {1, 0}, {-2, -1}, {1, 2}},
{{0, 0}, {-1, 0}, {2, 0}, {-1, 2}, {2, -1}},
{{0, 0}, {2, 0}, {-1, 0}, {2, 1}, {-1, -2}},
{{0, 0}, {1, 0}, {-2, 0}, {1, -2}, {-2, 1}}};
const short kick_tests_other[4][5][2] = {
{{0, 0}, {-1, 0}, {-1, 1}, {0, -2}, {-1, -2}},
{{0, 0}, {1, 0}, {1, -1}, {0, 2}, {1, 2}},
{{0, 0}, {1, 0}, {1, 1}, {0, -2}, {1, -2}},
{{0, 0}, {-1, 0}, {-1, -1}, {0, 2}, {-1, 2}}};
bool is_I_tetromino = (colorIndex == 1);
const short(*kick_tests)[5][2] = is_I_tetromino ? kick_tests_I : kick_tests_other;
for (int test = 0; test < 5; test++) {
short kickX = kick_tests[rotation_state][test][0];
short kickY = kick_tests[rotation_state][test][1];
for (int i = 0; i < 4; i++) {
short tmpX = X[i] - X[1];
short tmpY = Y[i] - Y[1];
newX[i] = X[1] - tmpY;
newY[i] = Y[1] + tmpX;
int testX = boardX + newX[i] + kickX;
int testY = boardY + newY[i] + kickY;
if (OutOfBounds(testY, testX) || (testX >= 0 && board[testX][testY] != 0)) {
goto next_test;
}
}
DeleteFigure();
for (int i = 0; i < 4; i++) {
X[i] = newX[i];
Y[i] = newY[i];
}
boardX += kickX;
boardY += kickY;
rotation_state = next_state;
DrawFigure();
return;
next_test:
continue;
}
}
void DrawFigure() {
for (int i = 0; i < 4; i++) {
int upperLeftX = (boardX + X[i]) * DIMENSION, upperLeftY = (boardY + Y[i]) * DIMENSION;
fillrect(upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, colors[colorIndex]);
rect(upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, Black);
}
}
void DeleteFigure() {
for (int i = 0; i < 4; i++) {
short upperLeftX = (boardX + X[i]) * DIMENSION, upperLeftY = (boardY + Y[i]) * DIMENSION;
fillrect(upperLeftY, upperLeftX, upperLeftY + DIMENSION, upperLeftX + DIMENSION, Black);
PutBorders(upperLeftY, upperLeftX);
}
}
void OnAttached() {
for (int i = 0; i < 4; i++) {
board[boardX + X[i]][boardY + Y[i]] = colorIndex;
}
}
bool MoveDown(char delta) {
if (!InCollisionDown(delta)) {
DeleteFigure();
boardX += delta;
DrawFigure();
return true;
}
return false;
}
void MoveLeft() {
if (!InCollisionLeft()) {
DeleteFigure();
boardY--;
DrawFigure();
}
}
void MoveRight() {
if (!InCollisionRight()) {
DeleteFigure();
boardY++;
DrawFigure();
}
}
void SoftDrop() {
DeleteFigure();
MoveDown(2);
DrawFigure();
score += 2 * (level + 1);
ShowScore();
}
bool InCollisionDown(char delta) {
int newX, newY;
for (int i = 0; i < 4; i++) {
newX = boardX + X[i] + delta;
newY = boardY + Y[i];
if (BottomEdge(newX) || board[newX][newY] != 0) {
return true;
}
}
return false;
}
bool InCollisionLeft() {
int newX, newY;
for (int i = 0; i < 4; i++) {
newX = boardX + X[i];
newY = boardY + Y[i] - 1;
if (LeftEdge(newY) || board[newX][newY] != 0) {
return true;
}
}
return false;
}
bool InCollisionRight() {
int newX, newY;
for (int i = 0; i < 4; i++) {
newX = boardX + X[i];
newY = boardY + Y[i] + 1;
if (RightEdge(newY) || board[newX][newY] != 0) {
return true;
}
}
return false;
}
void ReadJoystickForFigure() {
if (but_LEFT) {
MoveLeft();
} else if (but_RIGHT) {
MoveRight();
} else if (but_UP) {
pause_game();
} else if (but_DOWN) {
SoftDrop();
} else if (but_SELECT) {
Rotate();
}
}
void CheckLines(short& firstLine, short& numberOfLines) {
firstLine = -1;
numberOfLines = 0;
for (int i = 19; i >= 0; i--) {
short temp = 0;
for (int j = 0; j < 10; j++) {
if (board[i][j] == 0) {
if (numberOfLines > 0) return;
break;
}
temp++;
}
if (temp == 10) {
numberOfLines++;
if (firstLine == -1) firstLine = i;
}
}
}
unsigned int UpdateScore(short numOfLines) {
unsigned int newIncrement = 0;
switch (numOfLines) {
case 1:
newIncrement = 40;
break;
case 2:
newIncrement = 100;
break;
case 3:
newIncrement = 300;
break;
case 4:
newIncrement = 1200;
break;
default:
newIncrement = 0;
break;
}
return newIncrement * (level + 1);
}
void UpdateBoard() {
short firstLine, numberOfLines;
do {
CheckLines(firstLine, numberOfLines);
for (int i = firstLine; i >= numberOfLines; i--) {
for (int j = 0; j < 10; j++) {
board[i][j] = board[i - numberOfLines][j];
board[i - numberOfLines][j] = 0;
}
}
fillrect(0, 0, 162, 320, Black);
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 10; j++) {
if (board[i][j] != 0) {
fillrect(j * DIMENSION, i * DIMENSION, (j + 1) * DIMENSION, (i + 1) * DIMENSION, colors[board[i][j]]);
rect(j * DIMENSION, i * DIMENSION, (j + 1) * DIMENSION, (i + 1) * DIMENSION, Black);
}
}
}
score += UpdateScore(numberOfLines);
DrawFigure();
} while (numberOfLines != 0);
}
bool IsOver() {
for (int i = 0; i < 10; i++) {
if (board[0][i] != 0) return true;
}
return false;
}
void ShowGameOverScreen() {
background(Black);
foreground(White);
locate(60, 120);
printf("GAME OVER");
locate(40, 150);
printf("YOUR SCORE IS %d", score);
wait(5);
}
void InitGame() {
if (firstTime) {
Tetromino(rand() % 7 + 1);
DrawFigure();
nextFigure = rand() % 7 + 1;
ShowNextFigure();
firstTime = false;
}
}
void PlayGame() {
InitGame();
if (!MoveDown(1)) {
OnAttached();
UpdateBoard();
ShowScore();
Tetromino(nextFigure);
DrawFigure();
nextFigure = rand() % 7 + 1;
ShowNextFigure();
if (IsOver()) {
game.detach();
ShowGameOverScreen();
EndPlay();
}
}
}
void OnTasterPressed() {
static uint32_t debounce_counter = 0;
const uint32_t debounce_threshold = 12;
if (debounce_counter == 0) {
if (!gameStarted) {
joystick.detach();
gameStarted = true;
StartGame();
joystick.attach(&ReadJoystickForFigure, 0.3);
game.attach(&PlayGame, delays[level]);
}
debounce_counter = debounce_threshold;
} else if (debounce_counter > 0) {
debounce_counter--;
}
}
void pause_game() {
game.detach();
joystick.detach();
locate(180, 200);
printf("PAUSED");
while ((get_switches_state().to_ulong() & 0x10) == 0)
;
printf(" ");
joystick.attach(&ReadJoystickForFigure, 0.3);
game.attach(&PlayGame, delays[level]);
}
int main() {
std::srand(GenerateRandomSeed());
Init();
ShowLevelMenu();
joystick.attach(&ReadJoystickForLevel, 0.3);
return 0;
}
TetrisView::TetrisView(NavigationView& nav)
: nav_(nav) {
: nav_{nav} {
add_children({&dummy});
}
void TetrisView::on_show() {
}
void TetrisView::paint(Painter& painter) {
(void)painter;
if (!initialized) {
initialized = true;
std::srand(LPC_RTC->CTIME0);
main();
}
}
@@ -50,11 +595,45 @@ void TetrisView::frame_sync() {
}
bool TetrisView::on_encoder(const EncoderEvent delta) {
return check_encoder(delta);
if (!gameStarted) {
unsigned char old = level;
if (delta > 0) {
level = (level + 1) % 4;
} else if (delta < 0) {
(level == 0) ? level = 3 : level--;
}
if (old != level) {
DrawCursor(Black, old);
DrawCursor(White, level);
}
} else if (gameStarted && delta != 0) {
Rotate();
}
set_dirty();
return true;
}
bool TetrisView::on_key(const KeyEvent key) {
return check_key(key);
auto switches_debounced = get_switches_state().to_ulong();
but_RIGHT = (switches_debounced & 0x01) != 0;
but_LEFT = (switches_debounced & 0x02) != 0;
but_DOWN = (switches_debounced & 0x04) != 0;
but_UP = (switches_debounced & 0x08) != 0;
but_SELECT = (switches_debounced & 0x10) != 0;
if (key == KeyEvent::Select) {
if (!gameStarted) {
OnTasterPressed();
} else {
Rotate();
}
} else if (gameStarted) {
ReadJoystickForFigure();
} else {
ReadJoystickForLevel();
}
set_dirty();
return true;
}
} // namespace ui::external_app::tetris
} // namespace ui::external_app::tetris
+102 -3
View File
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2024 Mark Thompson
* 2025 updates by RocketGod (https://betaskynet.com/)
*
* This file is part of PortaPack.
*
@@ -22,6 +23,7 @@
#ifndef __UI_TETRIS_H__
#define __UI_TETRIS_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "event_m0.hpp"
#include "message.hpp"
@@ -29,16 +31,113 @@
#include "random.hpp"
#include "lpc43xx_cpp.hpp"
#include "limits.h"
#include "ui_widget.hpp"
namespace ui::external_app::tetris {
enum {
White,
Blue,
Yellow,
Purple,
Green,
Red,
Maroon,
Orange,
Black,
};
extern const Color pp_colors[];
extern Painter painter;
extern bool but_RIGHT;
extern bool but_LEFT;
extern bool but_UP;
extern bool but_DOWN;
extern bool but_SELECT;
void cls();
void background(int color);
void foreground(int color);
void locate(int x, int y);
void fillrect(int x1, int y1, int x2, int y2, int color);
void rect(int x1, int y1, int x2, int y2, int color);
void printf(std::string str);
void printf(std::string str, int v);
#define wait(x) chThdSleepMilliseconds(x * 1000)
using Callback = void (*)(void);
class Ticker {
public:
Ticker() = default;
void attach(Callback func, double delay_sec);
void detach();
};
extern Ticker game;
extern Ticker joystick;
extern unsigned char level;
extern const float delays[4];
extern unsigned int score;
extern bool firstTime;
extern bool gameStarted;
extern unsigned char nextFigure;
extern short board[20][10];
extern const int colors[8];
extern const short DIMENSION;
extern const short DIMENSION_NEXT;
extern short figuresX[7][4];
extern short figuresY[7][4];
unsigned int GenerateRandomSeed();
void Init();
void ShowScore();
void ShowNextFigure();
void DrawCursor(int color, unsigned char lev);
void ShowLevelMenu();
void ReadJoystickForLevel();
void EndPlay();
void StartGame();
void copyCoordinates(short X[], short Y[], unsigned char index);
bool BottomEdge(int x);
bool LeftEdge(int y);
bool RightEdge(int y);
bool OutOfBounds(int y, int x);
void PutBorders(short x, short y);
void Tetromino(unsigned char c);
void Initialize(unsigned char c);
void Rotate();
void DrawFigure();
void DeleteFigure();
void OnAttached();
bool MoveDown(char delta);
void MoveLeft();
void MoveRight();
void SoftDrop();
bool InCollisionDown(char delta);
bool InCollisionLeft();
bool InCollisionRight();
void ReadJoystickForFigure();
void CheckLines(short& firstLine, short& numberOfLines);
unsigned int UpdateScore(short numOfLines);
void UpdateBoard();
bool IsOver();
void ShowGameOverScreen();
void InitGame();
void PlayGame();
void OnTasterPressed();
void pause_game();
class TetrisView : public View {
public:
TetrisView(NavigationView& nav);
void on_show() override;
std::string title() const override { return "Tetris"; };
std::string title() const override { return "Tetris"; }
void focus() override { dummy.focus(); };
void focus() override { dummy.focus(); }
void paint(Painter& painter) override;
void frame_sync();
bool on_encoder(const EncoderEvent event) override;
@@ -61,4 +160,4 @@ class TetrisView : public View {
} // namespace ui::external_app::tetris
#endif /*__UI_TETRIS_H__*/
#endif /* __UI_TETRIS_H__ */

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