mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-21 23:19:02 +00:00
Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d4342e01a4 | |||
| 4342f5c0ee | |||
| bd781ce37b | |||
| c2e05dea48 | |||
| de3212cba5 | |||
| 9aff3f4121 | |||
| 6b6a00d511 | |||
| 806219f46e | |||
| a153cbf30b | |||
| 584af02dba | |||
| f344a1ca40 | |||
| 511675c9a1 | |||
| 7bbd608c90 | |||
| f941ae871a | |||
| eff214cc79 | |||
| 319ed3e7b4 | |||
| 288f6bd517 | |||
| 809abb6842 | |||
| 31082c63af | |||
| ff14008b43 | |||
| 1377516dce | |||
| 4162820409 | |||
| 4bbe1175c1 | |||
| ecd1a217d7 | |||
| 335cace137 | |||
| dbd708a1c7 | |||
| 4d842b5fae | |||
| e24f1b131d | |||
| 3f2c7351f1 | |||
| afc75f6cda | |||
| 69b17faddf | |||
| 2c118ae584 | |||
| 75c34747cb | |||
| 7754c0f37f | |||
| 37cc35d3c6 | |||
| 8a7aa9c0f8 | |||
| 22d6e34cd6 | |||
| 2229381ce3 | |||
| 4a63bdd7a0 | |||
| 21773cc3c3 | |||
| 4b000c8da6 | |||
| 20c64c98bd | |||
| 139ade0670 | |||
| c5b7326d4b | |||
| a60169c7f1 | |||
| 0ce6ea8318 | |||
| 4ecc9d04fe | |||
| d73db884f0 | |||
| ff2f27e297 | |||
| 443ffd8ad3 | |||
| 403c362702 | |||
| 6aa29e265b | |||
| 40cf2b3f8c | |||
| 4aa5fc1fbe | |||
| 717d615f4f | |||
| dce4e91e41 | |||
| dc2f3260f3 | |||
| 2d85e73f0d | |||
| 6ee7270db7 | |||
| b4112f0c04 | |||
| e920932886 | |||
| 081b11ad41 | |||
| adc23315a2 | |||
| 084b88564b | |||
| 24e9504688 | |||
| c670ca59a8 | |||
| 2ca8ea6342 | |||
| eb50b790c2 | |||
| 1df318355b | |||
| a17995fd2b | |||
| ee472e1ed2 | |||
| e933c8b498 | |||
| 18bebbfb6d | |||
| 52c3760e90 | |||
| b6e498a6d3 | |||
| ddf7f7ccb5 | |||
| fcdccdea85 | |||
| 5e55444f19 | |||
| 73f7f84718 | |||
| 7ad4ad99dd | |||
| 200f10397b | |||
| 9b352f45a2 | |||
| 51c445054e | |||
| 02586101e6 | |||
| b66d8b1aa1 | |||
| 8dc3851b55 | |||
| aef7c2be93 | |||
| 4e38dc4d3e | |||
| 520ad97f78 | |||
| 5bd208bca1 | |||
| a65ef3ce2e | |||
| b2bb37af74 | |||
| 54ad82a423 | |||
| e463405c77 | |||
| c413f925a9 | |||
| 9e1eb31150 | |||
| 7eb08f34cb | |||
| 2de1f8e6bf | |||
| 55db0e8c87 | |||
| dfa35b4290 | |||
| 0124051c4f | |||
| 65f301399f | |||
| 97889494cf | |||
| 32b35b1507 | |||
| fccff0484b | |||
| d94afe364b | |||
| 2e4db3dd27 | |||
| 97d3edbde7 | |||
| 4c18b80e42 | |||
| 01034af077 | |||
| 57ce978bab | |||
| 854ac514cb | |||
| b47a5fe0c8 | |||
| af362600ef | |||
| bad57d1391 | |||
| ef28e63a8c |
+2
-1
@@ -1 +1,2 @@
|
||||
build
|
||||
*
|
||||
!firmware/tools/docker-entrypoint.sh
|
||||
@@ -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.
|
||||
@@ -61,6 +61,7 @@ cmake-build-debug/
|
||||
*.sublime-workspace
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
|
||||
# VSCodium extensions
|
||||
.history
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1 @@
|
||||
Please see [How-to-collaborate](https://github.com/portapack-mayhem/mayhem-firmware/wiki/How-to-collaborate)
|
||||
@@ -5,11 +5,13 @@
|
||||
> The only legitimate link to our repositories is the [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware) organization on GitHub.--->
|
||||
# PortaPack Mayhem
|
||||
|
||||
[](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml) [](https://codescene.io/projects/8381) [](https://github.com/portapack-mayhem/mayhem-firmware/releases) [](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3)
|
||||
[](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml) [](https://codescene.io/projects/8381) [](https://github.com/portapack-mayhem/mayhem-firmware/releases) [](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3)
|
||||
|
||||
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
|
||||
|
||||
[<img src="https://github.com/user-attachments/assets/dea337ab-fb64-4a2a-b419-69afd272e815" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#new-h4m-mayhem-edition) [<img src="https://camo.githubusercontent.com/5c1f1da0688240ac7b2ccca0c8dbfd1d73f2540741ad8b1828ba4d5ea68af248/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f343339333937392f3239353533323731382d38653562363631632d663934362d346365652d386232642d3061363135663737313566342e706e67" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition)
|
||||
[<img src="https://github.com/user-attachments/assets/dea337ab-fb64-4a2a-b419-69afd272e815" height="310">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#new-h4m-mayhem-edition)
|
||||
[<img src="https://camo.githubusercontent.com/5c1f1da0688240ac7b2ccca0c8dbfd1d73f2540741ad8b1828ba4d5ea68af248/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f343339333937392f3239353533323731382d38653562363631632d663934362d346365652d386232642d3061363135663737313566342e706e67" height="310">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition)
|
||||
[<img src="https://camo.githubusercontent.com/c1f7dd1e7672324f60a513f0de23de76da6a669e63896a9de535d8c8093fc3c7/68747470733a2f2f7261772e6769746875622e636f6d2f7368617265627261696e65642f706f7274617061636b2d6861636b72662f6d61737465722f646f632f696d616765732f68617264776172652f706f7274617061636b5f68315f6f7065726174696e672e6a7067" height="310">]([https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h1r1r2))
|
||||
|
||||
# What is this?
|
||||
|
||||
@@ -26,11 +28,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:  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:  The fabulous H4M [complete](https://share.hackrf.app/9UMBN5) or [upgrade](https://share.hackrf.app/30CQ55), 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:
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Please check [Intended-Use-and-Legality](https://github.com/portapack-mayhem/mayhem-firmware/wiki/)
|
||||
+3
-3
@@ -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
@@ -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 "$@"
|
||||
@@ -18,7 +18,7 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(firmware)
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ set(CPPSRC
|
||||
clock_manager.cpp
|
||||
core_control.cpp
|
||||
database.cpp
|
||||
de_bruijn.cpp
|
||||
gradient.cpp
|
||||
rfm69.cpp
|
||||
event_m0.cpp
|
||||
file_reader.cpp
|
||||
@@ -294,7 +294,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 +302,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 +314,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 +372,7 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC
|
||||
hw
|
||||
apps
|
||||
protocols
|
||||
bitmaps
|
||||
bmps
|
||||
)
|
||||
|
||||
#
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace ui {
|
||||
/* AMOptionsView *********************************************************/
|
||||
|
||||
AMOptionsView::AMOptionsView(
|
||||
AnalogAudioView* view,
|
||||
Rect parent_rect,
|
||||
const Style* style)
|
||||
: View{parent_rect} {
|
||||
@@ -48,12 +49,23 @@ AMOptionsView::AMOptionsView(
|
||||
add_children({
|
||||
&label_config,
|
||||
&options_config,
|
||||
&zoom_config,
|
||||
});
|
||||
|
||||
freqman_set_bandwidth_option(AM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config
|
||||
options_config.set_by_value(receiver_model.am_configuration());
|
||||
options_config.on_change = [this](size_t, OptionsField::value_t n) {
|
||||
receiver_model.set_am_configuration(n);
|
||||
zoom_config.on_change = [this, view](size_t, OptionsField::value_t n) { // n , has two option values. when GUI =zoom+1 => (0), when GUI=zoom+2 (6)
|
||||
receiver_model.set_am_configuration(view->get_previous_AM_mode_option() + n); // n (0 or 6)
|
||||
view->set_zoom_factor(AM_MODULATION, n);
|
||||
view->set_previous_zoom_option(n);
|
||||
};
|
||||
|
||||
// restore zoom selection
|
||||
zoom_config.set_by_value(view->get_zoom_factor(AM_MODULATION));
|
||||
|
||||
freqman_set_bandwidth_option(AM_MODULATION, options_config); // freqman.cpp to the options_config, only allowing 5 modes freqman_bandwidths[AM] {"DSB 9k", 0}, {"DSB 6k", 1}, {"USB+3k", 2}, {"LSB-3k", 3}, {"CW", 4},
|
||||
options_config.set_by_value(receiver_model.am_configuration() - view->get_previous_zoom_option()); // restore AM GUI option mode , AM FIR index filters (0..11) values , <baseband::AMConfig, 12> am_configs has 12 fir index elements.
|
||||
options_config.on_change = [this, view](size_t, OptionsField::value_t n) {
|
||||
receiver_model.set_am_configuration(n + view->get_previous_zoom_option()); // we select proper FIR AM filter (0..11), = 0..4 GUI AM modes + offset +6 (if zoom+2)
|
||||
view->set_previous_AM_mode_option(n); // (0..4) allowing 5 AM modes (DSB9K, DSB6K, USB,LSB, CW)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -102,6 +114,54 @@ WFMOptionsView::WFMOptionsView(
|
||||
};
|
||||
}
|
||||
|
||||
/* WFMAMAptOptionsView *******************************************************/
|
||||
|
||||
WFMAMAptOptionsView::WFMAMAptOptionsView(
|
||||
Rect parent_rect,
|
||||
const Style* style)
|
||||
: View{parent_rect} {
|
||||
set_style(style);
|
||||
|
||||
add_children({
|
||||
&label_config,
|
||||
&options_config,
|
||||
});
|
||||
|
||||
freqman_set_bandwidth_option(WFMAM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config
|
||||
options_config.set_by_value(receiver_model.wfmam_configuration());
|
||||
options_config.on_change = [this](size_t, OptionsField::value_t n) {
|
||||
receiver_model.set_wfmam_configuration(n);
|
||||
};
|
||||
}
|
||||
|
||||
/* AMFMAptOptionsView *********************************************************/
|
||||
|
||||
AMFMAptOptionsView::AMFMAptOptionsView(
|
||||
AnalogAudioView* view,
|
||||
Rect parent_rect,
|
||||
const Style* style)
|
||||
: View{parent_rect} {
|
||||
set_style(style);
|
||||
|
||||
add_children({
|
||||
&label_config,
|
||||
&options_config,
|
||||
&zoom_config,
|
||||
});
|
||||
|
||||
freqman_set_bandwidth_option(AMFM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config
|
||||
receiver_model.set_amfm_configuration(5); // Fix index 5 manually, not from freqman: set to RX AM (USB+FM) mode to demod audio tone, and get Wefax_APT signal.
|
||||
options_config.set_by_value(receiver_model.amfm_configuration());
|
||||
|
||||
zoom_config.on_change = [this, view](size_t, OptionsField::value_t n) {
|
||||
receiver_model.set_amfm_configuration(5 + n);
|
||||
view->set_zoom_factor(AMFM_MODULATION, n);
|
||||
};
|
||||
|
||||
// restore zoom selection
|
||||
zoom_config.set_by_value(view->get_zoom_factor(AMFM_MODULATION));
|
||||
}
|
||||
|
||||
/* SPECOptionsView *******************************************************/
|
||||
|
||||
SPECOptionsView::SPECOptionsView(
|
||||
@@ -175,8 +235,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 +287,42 @@ void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) {
|
||||
receiver_model.set_baseband_bandwidth(bw / 2);
|
||||
}
|
||||
|
||||
uint8_t AnalogAudioView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value
|
||||
uint8_t AnalogAudioView::get_zoom_factor(uint8_t mode) { // define accessor functions inside AnalogAudioView to read zoom value
|
||||
if (mode == AM_MODULATION)
|
||||
return zoom_factor_am;
|
||||
else if (mode == AMFM_MODULATION)
|
||||
return zoom_factor_amfm;
|
||||
return 0; // default if unsupported mode
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_zoom_factor(uint8_t mode, uint8_t zoom) { // define accessor functions inside AnalogAudioView to write zoom value
|
||||
if (mode == AM_MODULATION)
|
||||
zoom_factor_am = zoom;
|
||||
else if (mode == AMFM_MODULATION)
|
||||
zoom_factor_amfm = zoom;
|
||||
}
|
||||
|
||||
uint8_t AnalogAudioView::get_previous_AM_mode_option() {
|
||||
return previous_AM_mode_option;
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_previous_AM_mode_option(uint8_t mode) {
|
||||
previous_AM_mode_option = mode;
|
||||
}
|
||||
|
||||
uint8_t AnalogAudioView::get_previous_zoom_option() {
|
||||
return previous_zoom;
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_previous_zoom_option(uint8_t zoom) {
|
||||
previous_zoom = zoom;
|
||||
}
|
||||
|
||||
uint8_t AnalogAudioView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read iq_phase_calibration_value
|
||||
return iq_phase_calibration_value;
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // define accessor functions
|
||||
void AnalogAudioView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // define accessor functions inside AnalogAudioView to write iq_phase_calibration_value
|
||||
iq_phase_calibration_value = cal_value;
|
||||
radio::set_rx_max283x_iq_phase_calibration(iq_phase_calibration_value);
|
||||
}
|
||||
@@ -246,6 +338,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 +360,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 +418,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 +435,18 @@ void AnalogAudioView::on_show_options_modulation() {
|
||||
text_ctcss.hidden(true);
|
||||
break;
|
||||
|
||||
case ReceiverModel::Mode::WFMAudioAMApt:
|
||||
widget = std::make_unique<WFMAMAptOptionsView>(options_view_rect, Theme::getInstance()->option_active);
|
||||
waterfall.show_audio_spectrum_view(true);
|
||||
text_ctcss.hidden(true);
|
||||
break;
|
||||
|
||||
case ReceiverModel::Mode::AMAudioFMApt:
|
||||
widget = std::make_unique<AMFMAptOptionsView>(this, options_view_rect, Theme::getInstance()->option_active);
|
||||
waterfall.show_audio_spectrum_view(false);
|
||||
text_ctcss.hidden(true);
|
||||
break;
|
||||
|
||||
case ReceiverModel::Mode::SpectrumAnalysis:
|
||||
widget = std::make_unique<SPECOptionsView>(this, nbfm_view_rect, Theme::getInstance()->option_active);
|
||||
waterfall.show_audio_spectrum_view(false);
|
||||
@@ -387,6 +488,12 @@ void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
|
||||
case ReceiverModel::Mode::WidebandFMAudio:
|
||||
image_tag = portapack::spi_flash::image_tag_wfm_audio;
|
||||
break;
|
||||
case ReceiverModel::Mode::WFMAudioAMApt:
|
||||
image_tag = portapack::spi_flash::image_tag_wfm_audio;
|
||||
break;
|
||||
case ReceiverModel::Mode::AMAudioFMApt:
|
||||
image_tag = portapack::spi_flash::image_tag_am_audio;
|
||||
break;
|
||||
case ReceiverModel::Mode::SpectrumAnalysis:
|
||||
image_tag = portapack::spi_flash::image_tag_wideband_spectrum;
|
||||
break;
|
||||
@@ -407,6 +514,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 +530,12 @@ void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
|
||||
case ReceiverModel::Mode::WidebandFMAudio:
|
||||
sampling_rate = 48000;
|
||||
break;
|
||||
case ReceiverModel::Mode::WFMAudioAMApt:
|
||||
sampling_rate = 12000;
|
||||
break;
|
||||
case ReceiverModel::Mode::AMAudioFMApt:
|
||||
sampling_rate = 12000;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -35,9 +35,11 @@
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
Text label_config{
|
||||
@@ -51,6 +53,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 AM modes array FIR 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 "USB+FM"
|
||||
{
|
||||
// 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,7 +132,22 @@ class WFMOptionsView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
class AnalogAudioView;
|
||||
class WFMAMAptOptionsView : public View {
|
||||
public:
|
||||
WFMAMAptOptionsView(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},
|
||||
10, // Max option char length "FM+AM(DSB)"
|
||||
{
|
||||
// Using common messages from freqman_ui.cpp
|
||||
}};
|
||||
};
|
||||
|
||||
class SPECOptionsView : public View {
|
||||
public:
|
||||
@@ -164,17 +213,35 @@ class AnalogAudioView : public View {
|
||||
uint8_t get_spec_iq_phase_calibration_value();
|
||||
void set_spec_iq_phase_calibration_value(uint8_t cal_value);
|
||||
|
||||
uint8_t get_zoom_factor(uint8_t mode);
|
||||
void set_zoom_factor(uint8_t mode, uint8_t zoom);
|
||||
|
||||
uint8_t get_previous_AM_mode_option();
|
||||
void set_previous_AM_mode_option(uint8_t mode);
|
||||
|
||||
uint8_t get_previous_zoom_option();
|
||||
void set_previous_zoom_option(uint8_t zoom);
|
||||
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839
|
||||
uint8_t zoom_factor_am{0}; // initial zoom factor in AM mode
|
||||
uint8_t zoom_factor_amfm{0}; // initial zoom factor in AMFM mode
|
||||
uint8_t previous_AM_mode_option{0}; // GUI 5 AM modes : (0..4 ) (DSB9K, DSB6K, USB,LSB, CW). Used to select proper FIR filter (0..11) AM mode + offset 0 (zoom+1) or +6 (if zoom+2)
|
||||
uint8_t previous_zoom{0}; // GUI ZOOM+1, ZOOM+2 , equivalent to two values offset 0 (zoom+1) or +6 (if zoom+2)
|
||||
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_audio",
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
|
||||
{"zoom_factor_am"sv, &zoom_factor_am}, // we are saving and restoring AM ZOOM factor from Settings.
|
||||
{"zoom_factor_amfm"sv, &zoom_factor_amfm}, // we are saving and restoring AMFM ZOOM factor from Settings.
|
||||
{"previous_AM_mode_option"sv, &previous_AM_mode_option}, // we are saving and restoring AMFM ZOOM factor from Settings.
|
||||
{"previous_zoom"sv, &previous_zoom}, // we are saving and restoring AMFM ZOOM factor from Settings.
|
||||
}};
|
||||
|
||||
const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16};
|
||||
@@ -211,6 +278,8 @@ 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
|
||||
{"FMAM", toUType(ReceiverModel::Mode::WFMAudioAMApt)} // Added to handle SAT NOAA APT
|
||||
}};
|
||||
|
||||
AudioVolumeField field_volume{
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ble_rx_app.hpp"
|
||||
#include "ble_rx_app.hpp"
|
||||
#include "ui_modemsetup.hpp"
|
||||
|
||||
@@ -39,6 +38,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);
|
||||
}
|
||||
@@ -195,6 +202,7 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
|
||||
nav,
|
||||
packetFileBuffer,
|
||||
64,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this, packetToSave](std::string& buffer) {
|
||||
on_save_file(buffer, packetToSave);
|
||||
});
|
||||
@@ -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() {
|
||||
@@ -459,6 +471,7 @@ BLERxView::BLERxView(NavigationView& nav)
|
||||
nav_,
|
||||
filterBuffer,
|
||||
64,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& buffer) {
|
||||
on_filter_change(buffer);
|
||||
});
|
||||
@@ -481,6 +494,7 @@ BLERxView::BLERxView(NavigationView& nav)
|
||||
nav,
|
||||
listFileBuffer,
|
||||
64,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& buffer) {
|
||||
on_save_file(buffer);
|
||||
});
|
||||
@@ -568,11 +582,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 +601,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 +632,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 +712,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 +848,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) {
|
||||
|
||||
@@ -399,6 +399,7 @@ BLETxView::BLETxView(NavigationView& nav)
|
||||
nav,
|
||||
packetFileBuffer,
|
||||
64,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& buffer) {
|
||||
on_save_file(buffer);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -91,10 +91,6 @@ void SoundBoardView::start_tx(const uint32_t id) {
|
||||
|
||||
auto reader = std::make_unique<WAVFileReader>();
|
||||
|
||||
uint32_t tone_key_index = options_tone_key.selected_index();
|
||||
uint32_t sample_rate;
|
||||
uint8_t bits_per_sample;
|
||||
|
||||
stop();
|
||||
|
||||
if (!reader->open(u"/WAV/" + file_list[id].native())) {
|
||||
@@ -108,7 +104,9 @@ void SoundBoardView::start_tx(const uint32_t id) {
|
||||
|
||||
// button_play.set_bitmap(&bitmap_stop);
|
||||
|
||||
sample_rate = reader->sample_rate();
|
||||
uint32_t sample_rate = reader->sample_rate();
|
||||
|
||||
tone_key_index = options_tone_key.selected_index();
|
||||
bits_per_sample = reader->bits_per_sample();
|
||||
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
@@ -155,6 +153,23 @@ void SoundBoardView::on_tx_progress(const uint32_t progress) {
|
||||
progressbar.set_value(progress);
|
||||
}
|
||||
|
||||
void SoundBoardView::update_config() {
|
||||
// NB: this were called by the on_bandwidth_changed() callback,
|
||||
// so other val would be updated too when bw changed. currently it's safe but be careful.
|
||||
baseband::set_audiotx_config(
|
||||
1536000 / 20, // Update vu-meter at 20Hz
|
||||
transmitter_model.channel_bandwidth(),
|
||||
0, // Gain is unused
|
||||
8, // shift_bits_s16, default 8 bits, but also unused
|
||||
bits_per_sample,
|
||||
TONES_F2D(tone_key_frequency(tone_key_index), TONES_SAMPLERATE),
|
||||
false, // AM
|
||||
false, // DSB
|
||||
false, // USB
|
||||
false // LSB
|
||||
);
|
||||
}
|
||||
|
||||
void SoundBoardView::on_select_entry() {
|
||||
tx_view.focus();
|
||||
}
|
||||
@@ -175,7 +190,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();
|
||||
@@ -288,6 +304,10 @@ SoundBoardView::SoundBoardView(
|
||||
};
|
||||
};
|
||||
|
||||
tx_view.on_bandwidth_changed = [this]() {
|
||||
update_config();
|
||||
};
|
||||
|
||||
tx_view.on_start = [this]() {
|
||||
start_tx(menu_view.highlighted_index());
|
||||
};
|
||||
|
||||
@@ -70,6 +70,8 @@ class SoundBoardView : public View {
|
||||
uint32_t playing_id{};
|
||||
uint32_t page = 1;
|
||||
uint32_t c_page = 1;
|
||||
uint32_t tone_key_index = 1;
|
||||
uint8_t bits_per_sample = 1;
|
||||
|
||||
std::vector<std::filesystem::path> file_list{};
|
||||
|
||||
@@ -90,6 +92,7 @@ class SoundBoardView : public View {
|
||||
void on_tx_progress(const uint32_t progress);
|
||||
void refresh_list();
|
||||
void on_select_entry();
|
||||
void update_config();
|
||||
|
||||
Labels labels{
|
||||
{{24 * 8, 180}, "Vol:", Theme::getInstance()->fg_light->foreground},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}};
|
||||
|
||||
@@ -94,6 +94,7 @@ APRSTXView::APRSTXView(NavigationView& nav) {
|
||||
nav,
|
||||
payload,
|
||||
30,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& s) {
|
||||
text_payload.set(s);
|
||||
});
|
||||
|
||||
@@ -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,21 @@ void ExternalModuleView::on_tick_second() {
|
||||
case app_location_t::TX:
|
||||
btnText += " (TX)";
|
||||
break;
|
||||
case app_location_t::TRX:
|
||||
btnText += " (TRX)";
|
||||
break;
|
||||
case app_location_t::SETTINGS:
|
||||
btnText += " (Settings)";
|
||||
break;
|
||||
case app_location_t::DEBUG:
|
||||
btnText += " (Debug)";
|
||||
break;
|
||||
case app_location_t::HOME:
|
||||
btnText += " (Home)";
|
||||
break;
|
||||
case app_location_t::GAMES:
|
||||
btnText += " (Games)";
|
||||
break;
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
@@ -119,4 +128,5 @@ void ExternalModuleView::on_tick_second() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -419,6 +419,17 @@ void FileManBaseView::reload_current(bool reset_pagination) {
|
||||
refresh_list();
|
||||
}
|
||||
|
||||
void FileManBaseView::copy_waterfall(std::filesystem::path path) {
|
||||
nav_.push<ModalMessageView>(
|
||||
"Install", " Use this gradient file\n for all waterfalls?", YESNO,
|
||||
[this, path](bool choice) {
|
||||
if (choice) {
|
||||
delete_file(default_gradient_file);
|
||||
copy_file(path, default_gradient_file);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
|
||||
const fs::path& ext) const {
|
||||
size_t index = 0;
|
||||
@@ -499,7 +510,7 @@ FileSaveView::FileSaveView(
|
||||
|
||||
button_edit_path.on_select = [this](Button&) {
|
||||
buffer_ = path_.string();
|
||||
text_prompt(nav_, buffer_, max_filename_length,
|
||||
text_prompt(nav_, buffer_, max_filename_length,ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string&) {
|
||||
path_ = buffer_;
|
||||
refresh_widgets();
|
||||
@@ -508,7 +519,7 @@ FileSaveView::FileSaveView(
|
||||
|
||||
button_edit_name.on_select = [this](Button&) {
|
||||
buffer_ = file_.string();
|
||||
text_prompt(nav_, buffer_, max_filename_length,
|
||||
text_prompt(nav_, buffer_, max_filename_length,ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string&) {
|
||||
file_ = buffer_;
|
||||
refresh_widgets();
|
||||
@@ -566,7 +577,7 @@ void FileManagerView::on_rename(std::string_view hint) {
|
||||
cursor_pos = pos;
|
||||
|
||||
text_prompt(
|
||||
nav_, name_buffer, cursor_pos, max_filename_length,
|
||||
nav_, name_buffer, cursor_pos, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& renamed) {
|
||||
auto renamed_path = fs::path{renamed};
|
||||
rename_file(get_selected_full_path(), current_path / renamed_path);
|
||||
@@ -640,7 +651,7 @@ void FileManagerView::on_clean() {
|
||||
|
||||
void FileManagerView::on_new_dir() {
|
||||
name_buffer = "";
|
||||
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) {
|
||||
text_prompt(nav_, name_buffer, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& dir_name) {
|
||||
make_new_directory(current_path / dir_name);
|
||||
reload_current(true);
|
||||
});
|
||||
@@ -671,7 +682,7 @@ void FileManagerView::on_paste() {
|
||||
|
||||
void FileManagerView::on_new_file() {
|
||||
name_buffer = "";
|
||||
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) {
|
||||
text_prompt(nav_, name_buffer, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& file_name) {
|
||||
make_new_file(current_path / file_name);
|
||||
reload_current(true);
|
||||
});
|
||||
@@ -685,7 +696,11 @@ bool FileManagerView::handle_file_open() {
|
||||
auto ext = path.extension();
|
||||
|
||||
if (path_iequal(txt_ext, ext)) {
|
||||
nav_.push<TextEditorView>(path);
|
||||
if (path_iequal(current_path, u"/" + waterfalls_dir)) {
|
||||
copy_waterfall(path);
|
||||
} else {
|
||||
nav_.push<TextEditorView>(path);
|
||||
}
|
||||
return true;
|
||||
} else if (is_cxx_capture_file(path) || path_iequal(ppl_ext, ext)) {
|
||||
// TODO: Enough memory to push?
|
||||
|
||||
@@ -96,6 +96,7 @@ class FileManBaseView : public View {
|
||||
void load_directory_contents(const std::filesystem::path& dir_path);
|
||||
void load_directory_contents_unordered(const std::filesystem::path& dir_path, size_t file_cnt);
|
||||
const file_assoc_t& get_assoc(const std::filesystem::path& ext) const;
|
||||
void copy_waterfall(std::filesystem::path path);
|
||||
|
||||
NavigationView& nav_;
|
||||
|
||||
|
||||
@@ -177,4 +177,4 @@ void FlashUtilityView::focus() {
|
||||
menu_view.focus();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -45,8 +45,8 @@ using option_db_t = std::pair<std::string_view, int32_t>;
|
||||
using options_db_t = std::vector<option_db_t>;
|
||||
|
||||
extern options_db_t freqman_modulations;
|
||||
extern options_db_t freqman_bandwidths[4];
|
||||
extern options_db_t freqman_steps;
|
||||
extern options_db_t freqman_bandwidths[6];
|
||||
// extern options_db_t freqman_steps; // now included via ui_receiver.hpp
|
||||
extern options_db_t freqman_steps_short;
|
||||
|
||||
options_t dboptions_to_options(const options_db_t& dboptions) {
|
||||
@@ -240,7 +240,7 @@ void FrequencyManagerView::on_edit_freq() {
|
||||
|
||||
void FrequencyManagerView::on_edit_desc() {
|
||||
temp_buffer_ = current_entry().description;
|
||||
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, [this](std::string& new_desc) {
|
||||
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_desc) {
|
||||
auto entry = current_entry();
|
||||
entry.description = std::move(new_desc);
|
||||
db_.replace_entry(current_index(), entry);
|
||||
@@ -250,7 +250,7 @@ void FrequencyManagerView::on_edit_desc() {
|
||||
|
||||
void FrequencyManagerView::on_add_category() {
|
||||
temp_buffer_.clear();
|
||||
text_prompt(nav_, temp_buffer_, 20, [this](std::string& new_name) {
|
||||
text_prompt(nav_, temp_buffer_, 20, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_name) {
|
||||
if (!new_name.empty()) {
|
||||
create_freqman_file(new_name);
|
||||
refresh_categories();
|
||||
|
||||
@@ -126,7 +126,7 @@ void GlassView::reset_live_view() {
|
||||
}
|
||||
|
||||
void GlassView::add_spectrum_pixel(uint8_t power) {
|
||||
spectrum_row[pixel_index] = spectrum_rgb3_lut[power]; // row of colors
|
||||
spectrum_row[pixel_index] = gradient.lut[power]; // row of colors
|
||||
spectrum_data[pixel_index] = (live_frequency_integrate * spectrum_data[pixel_index] + power) / (live_frequency_integrate + 1); // smoothing
|
||||
pixel_index++;
|
||||
|
||||
@@ -359,6 +359,10 @@ GlassView::GlassView(
|
||||
: nav_(nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
if (!gradient.load_file(default_gradient_file)) {
|
||||
gradient.set_default();
|
||||
}
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "spectrum_color_lut.hpp"
|
||||
#include "gradient.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -74,6 +74,7 @@ class GlassView : public View {
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
Gradient gradient{};
|
||||
RxRadioState radio_state_{ReceiverModel::Mode::SpectrumAnalysis};
|
||||
// Settings
|
||||
rf::Frequency f_min = 260 * MHZ_DIV; // Default to 315/433 remote range.
|
||||
|
||||
@@ -153,7 +153,7 @@ void POCSAGTXView::paint(Painter&) {
|
||||
}
|
||||
|
||||
void POCSAGTXView::on_set_text(NavigationView& nav) {
|
||||
text_prompt(nav, buffer, MAX_POCSAG_LENGTH);
|
||||
text_prompt(nav, buffer, MAX_POCSAG_LENGTH, ENTER_KEYBOARD_MODE_ALPHA);
|
||||
}
|
||||
|
||||
POCSAGTXView::POCSAGTXView(
|
||||
|
||||
@@ -65,6 +65,7 @@ RDSPSNView::RDSPSNView(
|
||||
nav,
|
||||
PSN,
|
||||
8,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& s) {
|
||||
text_psn.set(s);
|
||||
});
|
||||
@@ -86,6 +87,7 @@ RDSRadioTextView::RDSRadioTextView(
|
||||
nav,
|
||||
radiotext,
|
||||
28,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& s) {
|
||||
text_radiotext.set(s);
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -82,7 +82,7 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
|
||||
};
|
||||
|
||||
button_choose_output_name.on_select = [this, &nav](Button&) {
|
||||
text_prompt(nav, _output_file, 28, [this](std::string& buffer) {
|
||||
text_prompt(nav, _output_file, 28, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& buffer) {
|
||||
_output_file = buffer;
|
||||
button_choose_output_name.set_text(_output_file);
|
||||
});
|
||||
|
||||
@@ -56,6 +56,10 @@ SearchView::SearchView(
|
||||
: nav_(nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
if (!gradient.load_file(default_gradient_file)) {
|
||||
gradient.set_default();
|
||||
}
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
@@ -290,7 +294,7 @@ void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
power = spectrum.db[bin - 128];
|
||||
}
|
||||
|
||||
add_spectrum_pixel(spectrum_rgb3_lut[power]);
|
||||
add_spectrum_pixel(gradient.lut[power]);
|
||||
|
||||
mean_acc += power;
|
||||
if (power > max_power) {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "receiver_model.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "spectrum_color_lut.hpp"
|
||||
#include "gradient.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
|
||||
namespace ui {
|
||||
@@ -88,6 +88,7 @@ class SearchView : public View {
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
Gradient gradient{};
|
||||
RxRadioState radio_state_{
|
||||
100'000'000 /* frequency */,
|
||||
2500000 /* bandwidth */,
|
||||
|
||||
@@ -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,21 +806,35 @@ 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_ips_screen_switch,
|
||||
&checkbox_brightness_switch});
|
||||
|
||||
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
|
||||
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
|
||||
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
|
||||
checkbox_ips_screen_switch.set_value(pmem::config_lcd_normally_black());
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
if (checkbox_invert_switch.value() != pmem::config_lcd_inverted_mode()) {
|
||||
display.set_inverted(checkbox_invert_switch.value());
|
||||
pmem::set_lcd_inverted_mode(checkbox_invert_switch.value());
|
||||
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
|
||||
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
|
||||
if (checkbox_ips_screen_switch.value() != pmem::config_lcd_normally_black()) {
|
||||
pmem::set_lcd_normally_black(checkbox_ips_screen_switch.value());
|
||||
}
|
||||
send_system_refresh();
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
// only enable invert OR fake brightness
|
||||
checkbox_ips_screen_switch.on_select = [this](Checkbox&, bool v) {
|
||||
if (v) checkbox_brightness_switch.set_value(false);
|
||||
};
|
||||
checkbox_brightness_switch.on_select = [this](Checkbox&, bool v) {
|
||||
if (v) checkbox_ips_screen_switch.set_value(false);
|
||||
};
|
||||
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
@@ -957,67 +1000,6 @@ void SetMenuColorView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
/* SetAutoStartView ************************************/
|
||||
|
||||
SetAutostartView::SetAutostartView(NavigationView& nav) {
|
||||
add_children({&labels,
|
||||
&button_save,
|
||||
&button_cancel,
|
||||
&button_reset,
|
||||
&options});
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
autostart_app = "";
|
||||
if (selected != 0) {
|
||||
auto it = full_app_list.find(selected);
|
||||
if (it != full_app_list.end())
|
||||
autostart_app = it->second;
|
||||
}
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
button_reset.on_select = [this](Button&) {
|
||||
selected = 0;
|
||||
options.set_selected_index(0);
|
||||
autostart_app = "";
|
||||
};
|
||||
|
||||
// options
|
||||
i = 0;
|
||||
OptionsField::option_t o{"-none-", i};
|
||||
opts.emplace_back(o);
|
||||
for (auto& app : NavigationView::appList) {
|
||||
if (app.id == nullptr) continue;
|
||||
i++;
|
||||
o = {app.displayName, i};
|
||||
opts.emplace_back(o);
|
||||
full_app_list.emplace(i, app.id);
|
||||
if (autostart_app == app.id) selected = i;
|
||||
}
|
||||
ExternalItemsMenuLoader::load_all_external_items_callback([this](ui::AppInfoConsole& app) {
|
||||
if (app.appCallName == nullptr) return;
|
||||
i++;
|
||||
OptionsField::option_t o = {app.appFriendlyName, i};
|
||||
opts.emplace_back(o);
|
||||
full_app_list.emplace(i, app.appCallName);
|
||||
if (autostart_app == app.appCallName) selected = i;
|
||||
});
|
||||
|
||||
options.set_options(opts);
|
||||
options.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
selected = v;
|
||||
};
|
||||
options.set_selected_index(selected);
|
||||
}
|
||||
|
||||
void SetAutostartView::focus() {
|
||||
options.focus();
|
||||
}
|
||||
|
||||
/* SetThemeView ************************************/
|
||||
|
||||
SetThemeView::SetThemeView(NavigationView& nav) {
|
||||
@@ -1061,12 +1043,14 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
|
||||
add_children({&labels,
|
||||
&button_save,
|
||||
&button_cancel,
|
||||
&checkbox_overridebatt});
|
||||
&checkbox_overridebatt,
|
||||
&checkbox_battery_charge_hint});
|
||||
|
||||
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) add_children({&button_reset, &labels2});
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
pmem::set_ui_override_batt_calc(checkbox_overridebatt.value());
|
||||
pmem::set_ui_battery_charge_hint(checkbox_battery_charge_hint.value());
|
||||
battery::BatteryManagement::set_calc_override(checkbox_overridebatt.value());
|
||||
send_system_refresh();
|
||||
nav.pop();
|
||||
@@ -1081,6 +1065,7 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
|
||||
};
|
||||
|
||||
checkbox_overridebatt.set_value(pmem::ui_override_batt_calc());
|
||||
checkbox_battery_charge_hint.set_value(pmem::ui_battery_charge_hint());
|
||||
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
nav.pop();
|
||||
@@ -1099,9 +1084,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 +1099,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 */
|
||||
|
||||
@@ -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,10 +757,31 @@ class SetDisplayView : public View {
|
||||
std::string title() const override { return "Display"; };
|
||||
|
||||
private:
|
||||
Checkbox checkbox_invert_switch{
|
||||
{1 * 8, 2 * 16},
|
||||
Labels labels{
|
||||
{{1 * 8, 1 * 16}, "Limits screen brightness", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 2 * 16}, "(has a small performance", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 3 * 16}, "impact when enabled).", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 8 * 16}, "Brightness:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 10 * 16}, "REBOOT TO APPLY SCREEN TYPE", Theme::getInstance()->fg_light->foreground},
|
||||
|
||||
};
|
||||
|
||||
OptionsField field_fake_brightness{
|
||||
{20 * 8, 8 * 16},
|
||||
6,
|
||||
{{"12.5%", fake_brightness_level_options::BRIGHTNESS_12p5},
|
||||
{"25%", fake_brightness_level_options::BRIGHTNESS_25},
|
||||
{"50%", fake_brightness_level_options::BRIGHTNESS_50}}};
|
||||
|
||||
Checkbox checkbox_brightness_switch{
|
||||
{1 * 8, 5 * 16},
|
||||
16,
|
||||
"Enable brightness adjust"};
|
||||
|
||||
Checkbox checkbox_ips_screen_switch{
|
||||
{1 * 8, 12 * 16},
|
||||
23,
|
||||
"Invert colors (For IPS)"};
|
||||
"IPS Screen"};
|
||||
|
||||
Button button_save{
|
||||
{2 * 8, 16 * 16, 12 * 8, 32},
|
||||
@@ -864,47 +927,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);
|
||||
@@ -958,9 +980,13 @@ class SetBatteryView : public View {
|
||||
int32_t selected = 0;
|
||||
Labels labels{
|
||||
{{1 * 8, 1 * 16}, "Override batt calculation", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 2 * 16}, "method to voltage based", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels labels2{
|
||||
{{1 * 8, 6 * 16}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}};
|
||||
{{1 * 8, 2 * 16}, "method to voltage based", Theme::getInstance()->fg_light->foreground},
|
||||
/**/
|
||||
{{1 * 8, 6 * 16}, "Display a hint to remind you", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 7 * 16}, "when you charge", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
Labels labels2{{{1 * 8, 11 * 16}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
Button button_save{
|
||||
{2 * 8, 16 * 16, 12 * 8, 32},
|
||||
"Save"};
|
||||
@@ -970,13 +996,18 @@ class SetBatteryView : public View {
|
||||
23,
|
||||
"Override"};
|
||||
|
||||
Checkbox checkbox_battery_charge_hint{
|
||||
{2 * 8, 9 * 16},
|
||||
23,
|
||||
"Charge hint"};
|
||||
|
||||
Button button_cancel{
|
||||
{16 * 8, 16 * 16, 12 * 8, 32},
|
||||
"Cancel",
|
||||
};
|
||||
|
||||
Button button_reset{
|
||||
{2 * 8, 8 * 16, 12 * 8, 32},
|
||||
{2 * 8, 13 * 16, 12 * 8, 32},
|
||||
"Reset",
|
||||
};
|
||||
};
|
||||
@@ -988,6 +1019,7 @@ class SettingsMenuView : public BtnGridView {
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
void on_populate() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -127,6 +154,13 @@ SigGenView::SigGenView(
|
||||
};
|
||||
};
|
||||
|
||||
tx_view.on_bandwidth_changed = [this]() {
|
||||
// we don't protect here with auto_update because other field of tx_view obj isn't protected too
|
||||
// to remains the design logic same
|
||||
|
||||
update_config();
|
||||
};
|
||||
|
||||
tx_view.on_start = [this]() {
|
||||
start_tx();
|
||||
tx_view.set_transmitting(true);
|
||||
@@ -138,4 +172,4 @@ SigGenView::SigGenView(
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui */
|
||||
@@ -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,
|
||||
|
||||
@@ -617,6 +617,7 @@ void TextEditorView::show_edit_line() {
|
||||
edit_line_buffer_,
|
||||
viewer.col(),
|
||||
max_edit_length,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& buffer) {
|
||||
auto range = file_->line_range(viewer.line());
|
||||
if (!range)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -92,9 +93,9 @@ void NBFMConfig::apply(const uint8_t squelch_level) const {
|
||||
|
||||
void WFMConfig::apply() const {
|
||||
const WFMConfigureMessage message{
|
||||
decim_0, // taps_200k_decim_0 , taps_180k_wfm_decim_0, taps_40k_wfm_decim_0
|
||||
decim_1, // taps_200k_decim_1 or taps_180k_wfm_decim_1, taps_40k_wfm_decim_1
|
||||
taps_64_lp_156_198,
|
||||
decim_0, // Dynamic array 24 taps : taps_200k_decim_0 , taps_180k_wfm_decim_0, taps_40k_wfm_decim_0
|
||||
decim_1, // Dynamic array 16 taps : taps_200k_decim_1 or taps_180k_wfm_decim_1, taps_40k_wfm_decim_1
|
||||
taps_64_lp_156_198, // Fixed channel audio filter 15khz
|
||||
75000,
|
||||
audio_48k_hpf_30hz_config,
|
||||
audio_48k_deemph_2122_6_config};
|
||||
@@ -102,6 +103,18 @@ void WFMConfig::apply() const {
|
||||
audio::set_rate(audio::Rate::Hz_48000);
|
||||
}
|
||||
|
||||
void WFMAMConfig::apply() const {
|
||||
const WFMAMConfigureMessage message{
|
||||
decim_0, // Fixed 24 taps array : taps_16k0_decim_0
|
||||
decim_1, // Fixed 32 taps array : taps_84k_wfm_decim_1
|
||||
taps_64_lp_1875_2166, // Fixed channel audio filter , 64 taps array , to filter DSB AM 2k4 carrier before demod. AM .
|
||||
17000, // NOAA satellite tx , FM deviation = +-17Khz.
|
||||
apt_audio_12k_notch_2k4_config,
|
||||
apt_audio_12k_lpf_2000hz_config};
|
||||
send_message(&message);
|
||||
audio::set_rate(audio::Rate::Hz_12000);
|
||||
}
|
||||
|
||||
void set_tone(const uint32_t index, const uint32_t delta, const uint32_t duration) {
|
||||
shared_memory.bb_data.tones_data.tone_defs[index].delta = delta;
|
||||
shared_memory.bb_data.tones_data.tone_defs[index].duration = duration;
|
||||
@@ -304,6 +317,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)};
|
||||
|
||||
@@ -36,9 +36,12 @@
|
||||
namespace baseband {
|
||||
|
||||
struct AMConfig {
|
||||
const fir_taps_real<32> decim_2; // added to handle two types decim_2 9k, 6k
|
||||
const fir_taps_real<32> decim_1; // added to handle two var LPF in AMFM to avoid aliasing when spectrum zoom factor 2.
|
||||
const fir_taps_real<32> decim_2; // added to handle two var types decim_2 9k, 6k
|
||||
const fir_taps_complex<64> channel;
|
||||
const AMConfigureMessage::Modulation modulation;
|
||||
const iir_biquad_config_t audio_12k_iir_filter_config; // added to handle two var IIR filter types : 300 hpf(as before) , 1500Hz lpf for Wefax.
|
||||
const size_t spectrum_decimation_factor; // used to handle LCD AM waterfall zoom x1 / zoom x2.
|
||||
|
||||
void apply() const;
|
||||
};
|
||||
@@ -53,12 +56,19 @@ struct NBFMConfig {
|
||||
};
|
||||
|
||||
struct WFMConfig {
|
||||
const fir_taps_real<24> decim_0; // To handle both WFM filters , 200k and 40K for NOAA APT
|
||||
const fir_taps_real<24> decim_0; // To handle all 3 WFM filters , 200k, 180k and 40K-
|
||||
const fir_taps_real<16> decim_1;
|
||||
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
struct WFMAMConfig {
|
||||
const fir_taps_real<24> decim_0; // To handle WFM filter BW=40K for NOAA APT
|
||||
const fir_taps_real<32> decim_1;
|
||||
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
void set_tone(const uint32_t index, const uint32_t delta, const uint32_t duration);
|
||||
void set_tones_config(const uint32_t bw, const uint32_t pre_silence, const uint16_t tone_count, const bool dual_tone, const bool audio_out);
|
||||
void kill_tone();
|
||||
@@ -89,6 +99,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();
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -105,7 +106,7 @@ void bind(TextField& field, T& value, NavigationView& nav, Fn fn = Fn{}) {
|
||||
// Capture a new string and make the lambda mutable so it can be modified.
|
||||
field.on_select = [&nav, buf = std::string{}](TextField& tf) mutable {
|
||||
buf = tf.get_text();
|
||||
text_prompt(nav, buf, /*max_length*/ 255,
|
||||
text_prompt(nav, buf, /*max_length*/ 255, ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[&tf](std::string& str) {
|
||||
tf.set_text(str);
|
||||
});
|
||||
|
||||
+5434
-5092
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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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__*/
|
||||
@@ -131,6 +131,7 @@ ADSBCallsignView::ADSBCallsignView(
|
||||
nav,
|
||||
callsign,
|
||||
8,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& s) {
|
||||
button_callsign.set_text(s);
|
||||
});
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
+3
-2
@@ -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
|
||||
+5
-2
@@ -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__*/
|
||||
@@ -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([¤t_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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,213 @@
|
||||
/*
|
||||
* 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_normally_black: " + to_string_dec_uint(config_lcd_normally_black()));
|
||||
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()));
|
||||
pmem_dump_file.write_line("ui_config2 battery_charge_hint: " + to_string_dec_uint(ui_battery_charge_hint()));
|
||||
|
||||
// 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::WFMAudioAMApt:
|
||||
pmem_dump_file.write_line("modulation: Mode::WFMAudioAMApt");
|
||||
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
@@ -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,
|
||||
};
|
||||
}
|
||||
+1136
File diff suppressed because it is too large
Load Diff
+84
@@ -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__*/
|
||||
+86
-10
@@ -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,74 @@ 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
|
||||
|
||||
#gfxEQ
|
||||
external/gfxeq/main.cpp
|
||||
external/gfxeq/ui_gfxeq.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -177,6 +238,7 @@ set(EXTAPPLIST
|
||||
#acars_rx
|
||||
ookbrute
|
||||
ook_editor
|
||||
wefax_rx
|
||||
shoppingcart_lock
|
||||
flippertx
|
||||
remote
|
||||
@@ -184,4 +246,18 @@ set(EXTAPPLIST
|
||||
fmradio
|
||||
tuner
|
||||
metronome
|
||||
app_manager
|
||||
hopper
|
||||
antenna_length
|
||||
view_wav
|
||||
sd_wipe
|
||||
playlist_editor
|
||||
snake
|
||||
stopwatch
|
||||
breakout
|
||||
doom
|
||||
debug_pmem
|
||||
scanner
|
||||
level
|
||||
gfxeq
|
||||
)
|
||||
|
||||
+137
-34
@@ -26,38 +26,53 @@ 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
|
||||
ram_external_app_gfxeq (rwx) : org = 0xADE20000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -188,7 +203,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 +268,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 +286,93 @@ 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
|
||||
|
||||
.external_app_gfxeq : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_gfxeq.application_information));
|
||||
*(*ui*external_app*gfxeq*);
|
||||
} > ram_external_app_gfxeq
|
||||
}
|
||||
|
||||
@@ -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
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
* | Made by RocketGod |
|
||||
* | Find me at https://betaskynet.com |
|
||||
* | Argh matey! |
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "ui_gfxeq.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::gfxeq {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<gfxEQView>();
|
||||
}
|
||||
} // namespace ui::external_app::gfxeq
|
||||
|
||||
extern "C" {
|
||||
__attribute__((section(".external_app.app_gfxeq.application_information"), used)) application_information_t _application_information_gfxeq = {
|
||||
(uint8_t*)0x00000000,
|
||||
ui::external_app::gfxeq::initialize_app,
|
||||
CURRENT_HEADER_VERSION,
|
||||
VERSION_MD5,
|
||||
"gfxEQ",
|
||||
{0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
ui::Color::green().v,
|
||||
app_location_t::RX,
|
||||
-1,
|
||||
{'P', 'W', 'F', 'M'},
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::gfxeq
|
||||
+196
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
* | Made by RocketGod |
|
||||
* | Find me at https://betaskynet.com |
|
||||
* | Argh matey! |
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "ui_gfxeq.hpp"
|
||||
#include "ui.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
#include "tone_key.hpp"
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui::external_app::gfxeq {
|
||||
|
||||
gfxEQView::gfxEQView(NavigationView& nav)
|
||||
: nav_{nav}, bar_heights(NUM_BARS, 0), prev_bar_heights(NUM_BARS, 0) {
|
||||
add_children({&button_frequency, &field_rf_amp, &field_lna, &field_vga,
|
||||
&button_mood, &field_volume});
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
||||
receiver_model.set_wfm_configuration(1); // 200k => 0 , 180k => 1 , 40k => 2. Set to 1 or 2 for better reception
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_48000);
|
||||
audio::output::start();
|
||||
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack
|
||||
//
|
||||
receiver_model.enable();
|
||||
|
||||
receiver_model.set_target_frequency(frequency_value); // Retune to actual freq
|
||||
button_frequency.set_text("<" + to_string_short_freq(frequency_value) + ">");
|
||||
|
||||
button_frequency.on_select = [this, &nav](ButtonWithEncoder& button) {
|
||||
auto new_view = nav_.push<FrequencyKeypadView>(frequency_value);
|
||||
new_view->on_changed = [this, &button](rf::Frequency f) {
|
||||
frequency_value = f;
|
||||
receiver_model.set_target_frequency(f); // Retune to actual freq
|
||||
button_frequency.set_text("<" + to_string_short_freq(frequency_value) + ">");
|
||||
};
|
||||
};
|
||||
|
||||
button_frequency.on_change = [this]() {
|
||||
int64_t def_step = 25000;
|
||||
frequency_value = frequency_value + (button_frequency.get_encoder_delta() * def_step);
|
||||
if (frequency_value < 1) {
|
||||
frequency_value = 1;
|
||||
}
|
||||
if (frequency_value > (MAX_UFREQ - def_step)) {
|
||||
frequency_value = MAX_UFREQ;
|
||||
}
|
||||
button_frequency.set_encoder_delta(0);
|
||||
receiver_model.set_target_frequency(frequency_value); // Retune to actual freq
|
||||
button_frequency.set_text("<" + to_string_short_freq(frequency_value) + ">");
|
||||
};
|
||||
|
||||
button_mood.on_select = [this](Button&) { this->cycle_theme(); };
|
||||
}
|
||||
|
||||
// needed to answer usb serial frequency set
|
||||
void gfxEQView::on_freqchg(int64_t freq) {
|
||||
receiver_model.set_target_frequency(freq); // Retune to actual freq
|
||||
button_frequency.set_text("<" + to_string_short_freq(freq) + ">");
|
||||
}
|
||||
|
||||
gfxEQView::~gfxEQView() {
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void gfxEQView::focus() {
|
||||
button_frequency.focus();
|
||||
}
|
||||
|
||||
void gfxEQView::on_show() {
|
||||
needs_background_redraw = true;
|
||||
}
|
||||
|
||||
void gfxEQView::on_hide() {
|
||||
needs_background_redraw = true;
|
||||
}
|
||||
|
||||
void gfxEQView::update_audio_spectrum(const AudioSpectrum& spectrum) {
|
||||
const float bin_frequency_size = 48000.0f / 128;
|
||||
|
||||
for (int bar = 0; bar < NUM_BARS; bar++) {
|
||||
float start_freq = FREQUENCY_BANDS[bar];
|
||||
float end_freq = FREQUENCY_BANDS[bar + 1];
|
||||
|
||||
int start_bin = std::max(1, (int)(start_freq / bin_frequency_size));
|
||||
int end_bin = std::min(127, (int)(end_freq / bin_frequency_size));
|
||||
|
||||
if (start_bin >= end_bin) {
|
||||
end_bin = start_bin + 1;
|
||||
}
|
||||
|
||||
float total_energy = 0;
|
||||
int bin_count = 0;
|
||||
|
||||
for (int bin = start_bin; bin <= end_bin; bin++) {
|
||||
total_energy += spectrum.db[bin];
|
||||
bin_count++;
|
||||
}
|
||||
|
||||
float avg_db = bin_count > 0 ? (total_energy / bin_count) : 0;
|
||||
|
||||
// Manually boost highs for better visual balance
|
||||
float treble_boost = 1.0f;
|
||||
if (bar == 10)
|
||||
treble_boost = 1.7f;
|
||||
else if (bar >= 9)
|
||||
treble_boost = 1.3f;
|
||||
else if (bar >= 7)
|
||||
treble_boost = 1.3f;
|
||||
|
||||
// Mid emphasis for a V-shape effect
|
||||
float mid_boost = 1.0f;
|
||||
if (bar == 4 || bar == 5 || bar == 6) mid_boost = 1.2f;
|
||||
|
||||
float amplified_db = avg_db * treble_boost * mid_boost;
|
||||
|
||||
if (amplified_db > 255) amplified_db = 255;
|
||||
|
||||
float band_scale = 1.0f;
|
||||
int target_height = (amplified_db * RENDER_HEIGHT * band_scale) / 255;
|
||||
|
||||
if (target_height > RENDER_HEIGHT) {
|
||||
target_height = RENDER_HEIGHT;
|
||||
}
|
||||
|
||||
// Adjusted to look nice to my eyes
|
||||
float rise_speed = 0.8f;
|
||||
float fall_speed = 1.0f;
|
||||
|
||||
if (target_height > bar_heights[bar]) {
|
||||
bar_heights[bar] = bar_heights[bar] * (1.0f - rise_speed) + target_height * rise_speed;
|
||||
} else {
|
||||
bar_heights[bar] = bar_heights[bar] * (1.0f - fall_speed) + target_height * fall_speed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfxEQView::render_equalizer(Painter& painter) {
|
||||
const int num_segments = RENDER_HEIGHT / SEGMENT_HEIGHT;
|
||||
const ColorTheme& theme = themes[current_theme];
|
||||
|
||||
for (int bar = 0; bar < NUM_BARS; bar++) {
|
||||
int x = HORIZONTAL_OFFSET + bar * (BAR_WIDTH + BAR_SPACING);
|
||||
int active_segments = (bar_heights[bar] * num_segments) / RENDER_HEIGHT;
|
||||
|
||||
if (prev_bar_heights[bar] > active_segments) {
|
||||
int clear_height = (prev_bar_heights[bar] - active_segments) * SEGMENT_HEIGHT;
|
||||
int clear_y = SCREEN_HEIGHT - prev_bar_heights[bar] * SEGMENT_HEIGHT;
|
||||
painter.fill_rectangle({x, clear_y, BAR_WIDTH, clear_height}, Color(0, 0, 0));
|
||||
}
|
||||
|
||||
for (int seg = 0; seg < active_segments; seg++) {
|
||||
int y = SCREEN_HEIGHT - (seg + 1) * SEGMENT_HEIGHT;
|
||||
if (y < header_height) break;
|
||||
|
||||
Color segment_color = (seg >= active_segments - 2 && seg < active_segments) ? theme.peak_color : theme.base_color;
|
||||
painter.fill_rectangle({x, y, BAR_WIDTH, SEGMENT_HEIGHT - 1}, segment_color);
|
||||
}
|
||||
|
||||
prev_bar_heights[bar] = active_segments;
|
||||
}
|
||||
}
|
||||
|
||||
void gfxEQView::paint(Painter& painter) {
|
||||
if (needs_background_redraw) {
|
||||
painter.fill_rectangle({0, header_height, SCREEN_WIDTH, RENDER_HEIGHT}, Color(0, 0, 0));
|
||||
needs_background_redraw = false;
|
||||
}
|
||||
render_equalizer(painter);
|
||||
}
|
||||
|
||||
void gfxEQView::cycle_theme() {
|
||||
current_theme = (current_theme + 1) % themes.size();
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::gfxeq
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* ------------------------------------------------------------
|
||||
* | Made by RocketGod |
|
||||
* | Find me at https://betaskynet.com |
|
||||
* | Argh matey! |
|
||||
* ------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#ifndef __UI_GFXEQ_HPP__
|
||||
#define __UI_GFXEQ_HPP__
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "message.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
namespace ui::external_app::gfxeq {
|
||||
|
||||
class gfxEQView : public View {
|
||||
public:
|
||||
gfxEQView(NavigationView& nav);
|
||||
~gfxEQView();
|
||||
|
||||
gfxEQView(const gfxEQView&) = delete;
|
||||
gfxEQView& operator=(const gfxEQView&) = delete;
|
||||
|
||||
void focus() override;
|
||||
std::string title() const override { return "gfxEQ"; }
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void on_freqchg(int64_t freq);
|
||||
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 2 * 16;
|
||||
static constexpr int SCREEN_WIDTH = 240;
|
||||
static constexpr int SCREEN_HEIGHT = 320;
|
||||
static constexpr int RENDER_HEIGHT = 288;
|
||||
static constexpr int NUM_BARS = 11;
|
||||
static constexpr int BAR_SPACING = 2;
|
||||
static constexpr int BAR_WIDTH = (SCREEN_WIDTH - (BAR_SPACING * (NUM_BARS - 1))) / NUM_BARS;
|
||||
static constexpr int HORIZONTAL_OFFSET = 2;
|
||||
static constexpr int SEGMENT_HEIGHT = 10;
|
||||
|
||||
static constexpr std::array<int, NUM_BARS + 1> FREQUENCY_BANDS = {
|
||||
375, // Bass warmth and low rumble (e.g., deep basslines, kick drum body)
|
||||
750, // Upper bass punch (e.g., bass guitar punch, kick drum attack)
|
||||
1500, // Lower midrange fullness (e.g., warmth in vocals, guitar body)
|
||||
2250, // Midrange clarity (e.g., vocal presence, snare crack)
|
||||
3375, // Upper midrange bite (e.g., instrument definition, vocal articulation)
|
||||
4875, // Presence and edge (e.g., guitar bite, vocal sibilance start)
|
||||
6750, // Lower brilliance (e.g., cymbal shimmer, vocal clarity)
|
||||
9375, // Brilliance and air (e.g., hi-hat crispness, breathy vocals)
|
||||
13125, // High treble sparkle (e.g., subtle overtones, synth shimmer)
|
||||
16875, // Upper treble airiness (e.g., faint harmonics, room ambiance)
|
||||
20625, // Top-end sheen (e.g., ultra-high harmonics, noise floor)
|
||||
24375 // Extreme treble limit (e.g., inaudible overtones, signal cutoff, static)
|
||||
};
|
||||
|
||||
struct ColorTheme {
|
||||
Color base_color;
|
||||
Color peak_color;
|
||||
};
|
||||
|
||||
NavigationView& nav_;
|
||||
bool needs_background_redraw{false};
|
||||
std::vector<int> bar_heights;
|
||||
std::vector<int> prev_bar_heights;
|
||||
uint32_t current_theme{0};
|
||||
const std::array<ColorTheme, 20> themes{
|
||||
ColorTheme{Color(255, 0, 255), Color(255, 255, 255)},
|
||||
ColorTheme{Color(0, 255, 0), Color(255, 0, 0)},
|
||||
ColorTheme{Color(0, 0, 255), Color(255, 255, 0)},
|
||||
ColorTheme{Color(255, 128, 0), Color(255, 0, 128)},
|
||||
ColorTheme{Color(128, 0, 255), Color(0, 255, 255)},
|
||||
ColorTheme{Color(255, 255, 0), Color(0, 255, 128)},
|
||||
ColorTheme{Color(255, 0, 0), Color(0, 128, 255)},
|
||||
ColorTheme{Color(0, 255, 128), Color(255, 128, 255)},
|
||||
ColorTheme{Color(128, 128, 128), Color(255, 255, 255)},
|
||||
ColorTheme{Color(255, 64, 0), Color(0, 255, 64)},
|
||||
ColorTheme{Color(0, 128, 128), Color(255, 192, 0)},
|
||||
ColorTheme{Color(0, 255, 0), Color(0, 128, 0)},
|
||||
ColorTheme{Color(32, 64, 32), Color(0, 255, 0)},
|
||||
ColorTheme{Color(64, 0, 128), Color(255, 0, 255)},
|
||||
ColorTheme{Color(0, 64, 0), Color(0, 255, 128)},
|
||||
ColorTheme{Color(255, 255, 255), Color(0, 0, 255)},
|
||||
ColorTheme{Color(128, 0, 0), Color(255, 128, 0)},
|
||||
ColorTheme{Color(0, 128, 255), Color(255, 255, 128)},
|
||||
ColorTheme{Color(64, 64, 64), Color(255, 0, 0)},
|
||||
ColorTheme{Color(255, 192, 0), Color(0, 64, 128)}};
|
||||
|
||||
ButtonWithEncoder button_frequency{{0 * 8, 0 * 16 + 4, 11 * 8, 1 * 8}, ""};
|
||||
RFAmpField field_rf_amp{{13 * 8, 0 * 16}};
|
||||
LNAGainField field_lna{{15 * 8, 0 * 16}};
|
||||
VGAGainField field_vga{{18 * 8, 0 * 16}};
|
||||
Button button_mood{{21 * 8, 0, 6 * 8, 16}, "MOOD"};
|
||||
AudioVolumeField field_volume{{28 * 8, 0 * 16}};
|
||||
|
||||
rf::Frequency frequency_value{93100000};
|
||||
|
||||
RxRadioState rx_radio_state_{};
|
||||
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_gfx_eq",
|
||||
app_settings::Mode::RX,
|
||||
{{"theme", ¤t_theme},
|
||||
{"frequency", &frequency_value}}};
|
||||
|
||||
void update_audio_spectrum(const AudioSpectrum& spectrum);
|
||||
void render_equalizer(Painter& painter);
|
||||
void cycle_theme();
|
||||
|
||||
MessageHandlerRegistration message_handler_audio_spectrum{
|
||||
Message::ID::AudioSpectrum,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
|
||||
this->update_audio_spectrum(*message.data);
|
||||
this->set_dirty();
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_freqchg{
|
||||
Message::ID::FreqChangeCommand,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
|
||||
this->on_freqchg(message->freq);
|
||||
}};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::gfxeq
|
||||
|
||||
#endif
|
||||
+83
@@ -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
|
||||
};
|
||||
}
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* 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,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[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
@@ -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
|
||||
@@ -189,6 +189,7 @@ void LCRView::on_button_set_am(NavigationView& nav, int16_t button_id) {
|
||||
nav,
|
||||
litteral[button_id],
|
||||
7,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this, button_id](std::string& buffer) {
|
||||
texts[button_id].set(buffer);
|
||||
});
|
||||
@@ -257,6 +258,7 @@ LCRView::LCRView(NavigationView& nav) {
|
||||
nav,
|
||||
rgsb,
|
||||
4,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& buffer) {
|
||||
button_set_rgsb.set_text(buffer);
|
||||
});
|
||||
|
||||
+84
@@ -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 WFM is the biggest
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_nfm */ {'P', 'W', 'F', 'M'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+11
-4
@@ -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
|
||||
+2
-2
@@ -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
|
||||
@@ -311,6 +311,7 @@ LGEView::LGEView(NavigationView& nav) {
|
||||
nav,
|
||||
nickname,
|
||||
15,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& buffer) {
|
||||
button_text.set_text(buffer);
|
||||
});
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -42,6 +43,7 @@ MetronomeView::MetronomeView(NavigationView& nav)
|
||||
&field_unaccent_beep_tune,
|
||||
&field_beep_flash_duration,
|
||||
&field_bpm,
|
||||
&button_enter_tap_tempo,
|
||||
&progressbar,
|
||||
});
|
||||
|
||||
@@ -59,6 +61,14 @@ MetronomeView::MetronomeView(NavigationView& nav)
|
||||
}
|
||||
};
|
||||
|
||||
button_enter_tap_tempo.on_select = [this](Button&) {
|
||||
auto tap_tempo_view = nav_.push<MetronomeTapTempoView>(field_bpm.value());
|
||||
|
||||
tap_tempo_view->on_apply = [this](uint16_t bpm) {
|
||||
field_bpm.set_value(bpm);
|
||||
};
|
||||
};
|
||||
|
||||
field_volume.set_value(0); // seems that a change is required to force update, so setting to 0 first
|
||||
field_volume.set_value(99);
|
||||
|
||||
@@ -174,4 +184,116 @@ void MetronomeView::run() {
|
||||
}
|
||||
}
|
||||
|
||||
MetronomeTapTempoView::MetronomeTapTempoView(NavigationView& nav, uint16_t bpm)
|
||||
: nav_{nav},
|
||||
bpm_{bpm} {
|
||||
add_children({
|
||||
&button_input,
|
||||
&button_tap,
|
||||
&button_cancel,
|
||||
&button_apply,
|
||||
});
|
||||
|
||||
bpm_when_entered_ = bpm; // save for if user cancel
|
||||
|
||||
// im aware that we have duplicated painter which means in this app, weo have two painter instances
|
||||
// here is the reason why this is necessary:
|
||||
// We need to draw the bpm big font once when enter, which would be at bad timing in constructor,
|
||||
// cuz it happened before the view is pushed to nav, which casued it actually didn't draw
|
||||
// which leads me have to override the paint func from father and draw inside of it.
|
||||
//
|
||||
// BUT I can't completely package the draw logic inside of the paint func,
|
||||
// cuz set_dirty has flaw and cause screen flicker during the char changes, if i just package there and use set_dirty()
|
||||
Painter painter_instance_2;
|
||||
|
||||
button_input.on_select = [this](Button&) {
|
||||
input_buffer = to_string_dec_uint(bpm_);
|
||||
text_prompt(
|
||||
nav_,
|
||||
input_buffer,
|
||||
3,
|
||||
ENTER_KEYBOARD_MODE_DIGITS,
|
||||
[this](std::string& buffer) {
|
||||
if (buffer.empty()) {
|
||||
return;
|
||||
}
|
||||
bpm_ = atoi(buffer.c_str());
|
||||
|
||||
if (on_apply) {
|
||||
on_apply(bpm_);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
button_tap.on_select = [&](Button&) {
|
||||
on_tap(painter_instance_2);
|
||||
};
|
||||
|
||||
button_apply.on_select = [this](Button&) {
|
||||
// it's dynamically applied in tap handler
|
||||
// the design allow user to hear changes before apply
|
||||
nav_.pop();
|
||||
};
|
||||
|
||||
button_cancel.on_select = [this](Button&) {
|
||||
bpm_ = bpm_when_entered_;
|
||||
if (on_apply) {
|
||||
on_apply(bpm_);
|
||||
}
|
||||
nav_.pop();
|
||||
};
|
||||
}
|
||||
|
||||
void MetronomeTapTempoView::focus() {
|
||||
button_tap.focus();
|
||||
}
|
||||
|
||||
void MetronomeTapTempoView::paint(Painter& painter) {
|
||||
View::paint(painter);
|
||||
painter.draw_char({(0 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + bpm_ / 100, 4);
|
||||
painter.draw_char({(1 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + (bpm_ / 10) % 10, 4);
|
||||
painter.draw_char({(2 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + bpm_ % 10, 4);
|
||||
}
|
||||
|
||||
/*
|
||||
NB: i don't really know if the cpu clock is 1000Hz AKA 1ms per tick for chTimeNow()
|
||||
but it should be, refering to the stop watch app.
|
||||
and also i compared with my real metronome and it's very close
|
||||
so I assume it's 1ms per tick
|
||||
*/
|
||||
void MetronomeTapTempoView::on_tap(Painter& painter) {
|
||||
/* ^ NB: this painter accepted from painter_instance_2*/
|
||||
systime_t current_time = chTimeNow();
|
||||
if (last_tap_time > 0) {
|
||||
uint32_t interval_ms = current_time - last_tap_time;
|
||||
|
||||
if (interval_ms > 100) {
|
||||
uint16_t this_time_bpm = 60000 / interval_ms;
|
||||
|
||||
if (this_time_bpm > 0 && this_time_bpm < 400) {
|
||||
bpms_deque.push_back(this_time_bpm);
|
||||
if (bpms_deque.size() > 4) { // one bar length cuz most music tempo is quarter note as 1 beat
|
||||
bpms_deque.pop_front();
|
||||
}
|
||||
|
||||
// avg
|
||||
uint32_t sum = 0;
|
||||
for (auto& bpm : bpms_deque) {
|
||||
sum += bpm;
|
||||
}
|
||||
bpm_ = sum / bpms_deque.size();
|
||||
|
||||
if (on_apply) {
|
||||
on_apply(bpm_);
|
||||
}
|
||||
|
||||
painter.draw_char({(0 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + bpm_ / 100, 4);
|
||||
painter.draw_char({(1 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + (bpm_ / 10) % 10, 4);
|
||||
painter.draw_char({(2 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + bpm_ % 10, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
last_tap_time = current_time;
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::metronome
|
||||
@@ -27,6 +27,8 @@
|
||||
#include "audio.hpp"
|
||||
#include "ch.h"
|
||||
|
||||
#include <deque>
|
||||
|
||||
namespace ui::external_app::metronome {
|
||||
|
||||
class MetronomeView : public View {
|
||||
@@ -73,6 +75,11 @@ class MetronomeView : public View {
|
||||
1,
|
||||
' '};
|
||||
|
||||
Button button_enter_tap_tempo{
|
||||
{(sizeof("BPM:") + 6) * 8, 1 * 16, (sizeof("Tap Tempo") + 3) * 8, 16},
|
||||
"Tap Tempo",
|
||||
};
|
||||
|
||||
NumberField field_rythm_unaccent_time{// e.g. 3 in 3/4 beat
|
||||
{(sizeof("Rhythm:") + 1) * 8, 4 * 16},
|
||||
2,
|
||||
@@ -121,6 +128,45 @@ class MetronomeView : public View {
|
||||
{0 * 16, 8 * 16, screen_width, screen_height - 14 * 16}};
|
||||
};
|
||||
|
||||
class MetronomeTapTempoView : public View {
|
||||
public:
|
||||
std::function<void(uint16_t)> on_apply{};
|
||||
|
||||
MetronomeTapTempoView(NavigationView& nav, uint16_t bpm);
|
||||
|
||||
std::string title() const override { return "Tap.T"; };
|
||||
void focus() override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
void on_tap(Painter& painter);
|
||||
|
||||
NavigationView& nav_;
|
||||
|
||||
uint16_t bpm_{0};
|
||||
uint16_t bpm_when_entered_{0}; // this pass from MetronomeView and need to restore if user cancel
|
||||
std::deque<uint16_t> bpms_deque = {0}; // take average for recent taps to debounce
|
||||
uint32_t last_tap_time{0};
|
||||
|
||||
std::string input_buffer{""}; // needed by text_prompt
|
||||
|
||||
Button button_input{
|
||||
{0, 0, screen_width, 2 * 16},
|
||||
"Input BPM"};
|
||||
|
||||
Button button_tap{
|
||||
{0, 8 * 16, screen_width, 7 * 16},
|
||||
"Tap BPM"};
|
||||
|
||||
Button button_cancel{
|
||||
{1, 17 * 16, screen_width / 2 - 4, 2 * 16},
|
||||
"Cancel"};
|
||||
|
||||
Button button_apply{
|
||||
{1 + screen_width / 2 + 1, 17 * 16, screen_width / 2 - 4, 2 * 16},
|
||||
"Apply"};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::metronome
|
||||
|
||||
#endif /*__UI_METRONOME_H__*/
|
||||
+1
-1
@@ -99,7 +99,7 @@ static msg_t loopthread_fn(void* arg) {
|
||||
}
|
||||
|
||||
void MorseView::on_set_text(NavigationView& nav) {
|
||||
text_prompt(nav, buffer, 28);
|
||||
text_prompt(nav, buffer, 28, ENTER_KEYBOARD_MODE_ALPHA);
|
||||
}
|
||||
|
||||
void MorseView::focus() {
|
||||
|
||||
+157
-1
@@ -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);
|
||||
@@ -207,6 +210,7 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
|
||||
nav,
|
||||
outputFileBuffer,
|
||||
64,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[this](std::string& buffer) {
|
||||
on_save_file(buffer);
|
||||
});
|
||||
@@ -260,6 +264,7 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
|
||||
nav,
|
||||
ook_data.payload,
|
||||
100,
|
||||
ENTER_KEYBOARD_MODE_DIGITS,
|
||||
[this](std::string& s) {
|
||||
text_payload.set(s);
|
||||
draw_waveform();
|
||||
@@ -267,6 +272,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 +295,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__*/
|
||||
|
||||
@@ -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,42 @@ 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
|
||||
ENTER_KEYBOARD_MODE_DIGITS,
|
||||
[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
|
||||
ENTER_KEYBOARD_MODE_DIGITS,
|
||||
[this](std::string& buffer) {
|
||||
field_stop.set_value(atoi(buffer.c_str()));
|
||||
validate_start_stop();
|
||||
});
|
||||
};
|
||||
update_start_stop(0);
|
||||
}
|
||||
|
||||
|
||||
+21
-4
@@ -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();
|
||||
|
||||
|
||||
@@ -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,369 @@
|
||||
/*
|
||||
* 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,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[&](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,
|
||||
ENTER_KEYBOARD_MODE_ALPHA,
|
||||
[&](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__
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user