mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-01 04:19:03 +00:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae9d6de093 | |||
| 7a4c3184a1 | |||
| 58307aee9c | |||
| 409242507c | |||
| 0a8194fa10 | |||
| eaac40df3a | |||
| 58b488b840 | |||
| 1676702a09 | |||
| 3943848add | |||
| 831dbeaab5 | |||
| 49e719ded8 | |||
| c2d2409bff | |||
| 485f57d86b | |||
| 0e6f303eec | |||
| ad94ae05c1 | |||
| 89dc8c0463 | |||
| 77640c5cc0 | |||
| cdc8cfeb7d | |||
| 4740df2e2c | |||
| ec0f45a488 | |||
| 9d7e06c255 | |||
| 4b93e78dd9 | |||
| 5ced8c90d3 | |||
| 9d22711368 | |||
| 23e6295dd2 | |||
| bbd75ab7ef | |||
| 7508fee62c | |||
| fe88760766 | |||
| 75ece38725 | |||
| b53032a8a5 | |||
| e7f8952ece | |||
| 82a6ae0791 | |||
| d303098e35 | |||
| 1a69ce2d97 | |||
| eedebe1c52 | |||
| 9e61f80809 | |||
| ff591c68a0 | |||
| 8761b9d7e0 | |||
| 1b5125b0a8 | |||
| 9ba885361d | |||
| afa34d83b2 | |||
| d93c914dd3 | |||
| 715a2dd448 | |||
| 58bf60695d | |||
| fbe7954f2e | |||
| d122a8fc3b | |||
| 3998dc124a | |||
| 3d2da9c0db | |||
| 3189d3af4c | |||
| 1b3a99cc5e | |||
| 935c43e88f | |||
| 2893c031ab | |||
| 94cdb16ca9 |
+1
-1
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://salt.bountysource.com/checkout/amount?team=portapack-mayhem
|
||||
custom: # https://salt.bountysource.com/checkout/amount?team=portapack-mayhem
|
||||
|
||||
@@ -9,7 +9,7 @@ assignees: ''
|
||||
|
||||
----
|
||||
|
||||
(Please try the latest nightly release before submitting this. You can find the latest nightly version here: https://github.com/eried/portapack-mayhem/releases)
|
||||
(Please try the latest nightly release before submitting this. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases)
|
||||
|
||||
----
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ assignees: ''
|
||||
|
||||
----
|
||||
Before creating this issue, **do the following**:
|
||||
* Read the Wiki on booting: https://github.com/eried/portapack-mayhem/wiki/Won't-boot
|
||||
* Read: https://github.com/eried/portapack-havoc/wiki/Update-firmware
|
||||
* Read the Wiki on booting: https://github.com/portapack-mayhem/mayhem-firmware/wiki/Won't-boot
|
||||
* Read: https://github.com/portapack-mayhem/mayhem-firmware/wiki/Update-firmware
|
||||
* Watch carefully: https://www.youtube.com/watch?v=_zx4ZvurgOs
|
||||
* (if you are not in Windows) also check: https://www.youtube.com/watch?v=kjFB58Y1TAo
|
||||
|
||||
@@ -27,12 +27,12 @@ Steps to reproduce the behavior:
|
||||
|
||||
**My Hardware**
|
||||
Please specify what PortaPack hardware version you are using.
|
||||
You can find the list of versions here: https://github.com/eried/portapack-mayhem/wiki/PortaPack-Versions
|
||||
You can find the list of versions here: https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions
|
||||
|
||||
**Affected versions**
|
||||
Please tell us what version you are running.
|
||||
If your device is still functional, try the latest nightly release before submitting this.
|
||||
You can find the latest nightly version here https://github.com/eried/portapack-mayhem/releases
|
||||
You can find the latest nightly version here https://github.com/portapack-mayhem/mayhem-firmware/releases
|
||||
|
||||
**Were you able to update the firmware before?**
|
||||
Things might be confusing the first time, please check the video available on the link above.
|
||||
|
||||
@@ -6,8 +6,8 @@ from datetime import datetime, timedelta, timezone
|
||||
|
||||
# Set up your personal access token and the repository details
|
||||
token = os.environ.get('GH_TOKEN')
|
||||
repo_owner = "eried"
|
||||
repo_name = "portapack-mayhem"
|
||||
repo_owner = "portapack-mayhem"
|
||||
repo_name = "mayhem-firmware"
|
||||
|
||||
|
||||
def print_stable_changelog(previous_sha):
|
||||
|
||||
@@ -36,7 +36,12 @@ jobs:
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: next
|
||||
#ref: next
|
||||
# The branch, tag or SHA to checkout. When checking out the repository that
|
||||
# triggered a workflow, this defaults to the reference or SHA for that event.
|
||||
# Otherwise, uses the default branch.
|
||||
# https://github.com/actions/checkout
|
||||
# So scheduled runs will use the default branch (next) but its now possible to trigger a workflow from another branch
|
||||
submodules: true
|
||||
- name: Git Sumbodule Update
|
||||
run: |
|
||||
@@ -49,10 +54,10 @@ jobs:
|
||||
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Small SD Card ZIP - No World Map
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard-no-map.zip sdcard
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
|
||||
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
|
||||
- name: Unzip world map
|
||||
run: |
|
||||
unzip world_map.zip -d sdcard/ADSB
|
||||
@@ -61,10 +66,10 @@ jobs:
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard.zip sdcard
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
CHANGELOG=$(python3 .github/workflows/changelog.py)
|
||||
CHANGELOG="${CHANGELOG//'%'/'%25'}"
|
||||
@@ -88,6 +93,16 @@ jobs:
|
||||
${{ steps.changelog.outputs.content }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
- name: Upload Firmware TAR Asset
|
||||
id: upload-firmware-tar-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: build/firmware/portapack-mayhem.ppfw.tar
|
||||
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}_OCI.ppfw.tar
|
||||
asset_content_type: application/x-tar
|
||||
- name: Upload Firmware Asset
|
||||
id: upload-firmware-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
|
||||
@@ -14,7 +14,12 @@ jobs:
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: next
|
||||
#ref: next
|
||||
# The branch, tag or SHA to checkout. When checking out the repository that
|
||||
# triggered a workflow, this defaults to the reference or SHA for that event.
|
||||
# Otherwise, uses the default branch.
|
||||
# https://github.com/actions/checkout
|
||||
# So scheduled runs will use the default branch (next) but its now possible to trigger a workflow from another branch
|
||||
submodules: true
|
||||
- name: Git Sumbodule Update
|
||||
run: |
|
||||
@@ -33,10 +38,10 @@ jobs:
|
||||
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Small SD Card ZIP - No World Map
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard-no-map.zip sdcard
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/eried/portapack-mayhem/releases/download/world_map/world_map.zip
|
||||
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
|
||||
- name: Unzip world map
|
||||
run: |
|
||||
unzip world_map.zip -d sdcard/ADSB
|
||||
@@ -45,10 +50,10 @@ jobs:
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && zip -r sdcard.zip sdcard
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
CHANGELOG=$(python3 .github/workflows/changelog.py ${{ steps.past_version.outputs.past_version }})
|
||||
CHANGELOG="${CHANGELOG//'%'/'%25'}"
|
||||
@@ -66,21 +71,32 @@ jobs:
|
||||
release_name: Mayhem firmware ${{ steps.version.outputs.version }}
|
||||
body: |
|
||||
**Stable release - ${{ steps.version.outputs.version }}**
|
||||
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). Please check the [readme](https://github.com/eried/portapack-mayhem/blob/master/README.md) for details.
|
||||
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). Please check the [readme](https://github.com/portapack-mayhem/mayhem-firmware/blob/master/README.md) for details.
|
||||
## Release notes
|
||||
### Revision (${{ steps.version.outputs.version }}):
|
||||
${{ steps.changelog.outputs.content }}
|
||||
|
||||
**Full Changelog**: https://github.com/eried/portapack-mayhem/compare/${{ steps.past_version.outputs.past_version }}...${{ steps.version.outputs.version }}
|
||||
**Full Changelog**: https://github.com/portapack-mayhem/mayhem-firmware/compare/${{ steps.past_version.outputs.past_version }}...${{ steps.version.outputs.version }}
|
||||
|
||||
## Installation
|
||||
Check the [wiki](https://github.com/eried/portapack-havoc/wiki/Update-firmware) for details how to upgrade.
|
||||
Check the [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Update-firmware) for details how to upgrade.
|
||||
__Warning:__ Since release 1.8.0, some applications has been moved to the SD card as we ran out of flash space.
|
||||
|
||||
### MicroSD card files
|
||||
|
||||
For certain functionality, like the world map, GPS simulator, and others you need to uncompress (using [7-zip](https://www.7-zip.org/download.html)) the files from `mayhem_vX.Y.Z_COPY_TO_SDCARD.zip` to a FAT32 formatted MicroSD card.
|
||||
For certain functionality, like external apps, the world map, GPS simulator, and others you need to uncompress (using [7-zip](https://www.7-zip.org/download.html)) the files from `mayhem_vX.Y.Z_COPY_TO_SDCARD.zip` to a FAT32 formatted MicroSD card.
|
||||
draft: true
|
||||
prerelease: false
|
||||
- name: Upload Firmware TAR Asset
|
||||
id: upload-firmware-tar-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: build/firmware/portapack-mayhem.ppfw.tar
|
||||
asset_name: mayhem_${{ steps.version.outputs.version }}_OCI.ppfw.tar
|
||||
asset_content_type: application/x-tar
|
||||
- name: Upload Firmware Asset
|
||||
id: upload-firmware-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@
|
||||
*.map
|
||||
*.lst
|
||||
.dep/
|
||||
build/
|
||||
/build*
|
||||
CMakeFiles/
|
||||
|
||||
# Debugging
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
cache: apt
|
||||
dist: xenial
|
||||
|
||||
env:
|
||||
global:
|
||||
- PROJECT_NAME=PortaPack-HAVOC
|
||||
- SHORT_COMMIT_HASH=`git rev-parse --short HEAD`
|
||||
- VERSION_STRING=nightly-$SHORT_COMMIT_HASH
|
||||
- BUILD_DATE="`date +%Y-%m-%d`"
|
||||
- BUILD_NAME="$PROJECT_NAME-$BUILD_DATE-$SHORT_COMMIT_HASH"
|
||||
- ARTEFACT_BASE=$TRAVIS_BUILD_DIR/artefacts/
|
||||
- ARTEFACT_PATH=$ARTEFACT_BASE/$BUILD_NAME
|
||||
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa -y
|
||||
- sudo apt-get update -q
|
||||
- sudo apt-get install gcc-arm-embedded -y
|
||||
|
||||
script:
|
||||
# TODO: Introduce top-level Makefile, this is lame.
|
||||
- sed -e "s/\#set(VERSION.*/set(VERSION \"$VERSION_STRING\")/" -i".bak" CMakeLists.txt
|
||||
- mkdir build/
|
||||
- pushd build/
|
||||
- cmake ..
|
||||
- make firmware
|
||||
- popd
|
||||
|
||||
after_success:
|
||||
- mkdir -p $ARTEFACT_PATH
|
||||
# Copy firmware to firmware-bin directory
|
||||
- cd $TRAVIS_BUILD_DIR/build
|
||||
- cp firmware/portapack-h1-havoc.bin $ARTEFACT_PATH/
|
||||
- cp hackrf/firmware/hackrf_usb/hackrf_usb.dfu $ARTEFACT_PATH/
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
- cp LICENSE $ARTEFACT_PATH/
|
||||
# Build the archive
|
||||
- cd $ARTEFACT_BASE
|
||||
- tar -cJvf $ARTEFACT_BASE/$BUILD_NAME.tar.xz $BUILD_NAME
|
||||
- md5sum --binary $BUILD_NAME.tar.xz >MD5SUMS
|
||||
- sha256sum --binary $BUILD_NAME.tar.xz >SHA256SUMS
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- coreutils
|
||||
- tar
|
||||
- sed
|
||||
- cmake
|
||||
- dfu-util
|
||||
+21
-2
@@ -18,15 +18,17 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_policy(SET CMP0005 NEW)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex-m.cmake)
|
||||
|
||||
project(portapack-h1)
|
||||
|
||||
set(EXPECTED_GCC_VERSION "9.2.1")
|
||||
|
||||
set(VERSION "$ENV{VERSION_STRING}")
|
||||
if ("$ENV{VERSION_STRING}" STREQUAL "")
|
||||
if ("${VERSION}" STREQUAL "")
|
||||
execute_process(
|
||||
COMMAND git log -n 1 --format=%h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
@@ -40,6 +42,11 @@ if ("$ENV{VERSION_STRING}" STREQUAL "")
|
||||
else (GIT_VERSION_FOUND)
|
||||
set(VERSION "${GIT_VERSION}")
|
||||
endif (GIT_VERSION_FOUND)
|
||||
|
||||
set(VERSION_NOHASH "dev")
|
||||
else()
|
||||
set(VERSION_NOHASH "${VERSION}")
|
||||
set(PPFW_FILENAME "portapack-mayhem.ppfw.tar")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
@@ -48,6 +55,18 @@ execute_process(
|
||||
)
|
||||
set(VERSION_MD5 "0x${VERSION_MD5}")
|
||||
|
||||
if (NOT DEFINED PPFW_FILENAME)
|
||||
# Funny business here: trying to generate unique names to prevent renaming of shared test-drive builds by the browser on download to .ppfw-42.tar as it wont be recognized by the flasher
|
||||
execute_process(
|
||||
COMMAND date "+%y%m%d-%H%M"
|
||||
OUTPUT_VARIABLE PPFW_FILENAME
|
||||
)
|
||||
string(STRIP ${PPFW_FILENAME} PPFW_FILENAME)
|
||||
set(PPFW_FILENAME "portapack-mayhem_${PPFW_FILENAME}_OCI.ppfw.tar")
|
||||
endif()
|
||||
|
||||
message("Building version: ${VERSION} MD5: ${VERSION_MD5}")
|
||||
|
||||
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
|
||||
set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
|
||||
|
||||
|
||||
@@ -1,26 +1,31 @@
|
||||
> [!IMPORTANT]
|
||||
> this repository **has just been moved** from my personal GitHub [eried/portapack-mayhem](https://github.com/eried/portapack-mayhem) to an organization: [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware). Please keep this in mind to **update your links** accordingly.
|
||||
|
||||
# PortaPack Mayhem
|
||||
|
||||
[](https://travis-ci.com/eried/portapack-mayhem) [](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml) [](https://codescene.io/projects/8381) [](https://github.com/eried/portapack-mayhem/releases) [](https://github.com/eried/portapack-mayhem/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://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_front.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_inside.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview#portapack-internals)
|
||||
[<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_front.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_inside.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview#portapack-internals)
|
||||
|
||||
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_DmU7GQX) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
|
||||
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_DmU7GQX) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure)*
|
||||
|
||||
# What is this?
|
||||
|
||||
If you are new to *HackRF+PortaPack+Mayhem*, check this video:
|
||||
|
||||
[](https://www.youtube.com/watch?v=alrFbY5vxt4)
|
||||
[](https://www.youtube.com/watch?v=H-bqdWfbhpg)
|
||||
|
||||
For people familiar with the [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware), this one might be interesting:<br>[What is the HackRF One Portapack H2+?](https://www.youtube.com/watch?v=alrFbY5vxt4)
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
This repository expands upon the previous work by many people and aims to constantly add new features, bugfixes and generate documentation to make further development easier. [Collaboration](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) is always welcomed and appreciated.
|
||||
This repository expands upon the previous work by many people and aims to constantly add new features, bugfixes and generate documentation to make further development easier. [Collaboration](https://github.com/portapack-mayhem/mayhem-firmware/wiki/How-to-collaborate) is always welcomed and appreciated.
|
||||
|
||||
## What to buy?
|
||||
|
||||
:heavy_check_mark: A recommended one is this [PortaPack H2](https://s.click.aliexpress.com/e/_DmU7GQX), that includes everything you need with the plastic case "inspired" on [this](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure).
|
||||
:heavy_check_mark: A recommended one is this [PortaPack H2](https://s.click.aliexpress.com/e/_DmU7GQX), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure).
|
||||
|
||||
:heavy_check_mark: Our friends at OpenSourceSDRLab give away five units every three months in our discord (check the badge on top) of their [PortaPack H2](https://www.aliexpress.com/item/4000247041639.html?gatewayAdapt=4itemAdapt), you can support them too by ordering.
|
||||
|
||||
@@ -30,33 +35,33 @@ This repository expands upon the previous work by many people and aims to consta
|
||||
|
||||

|
||||
|
||||
:warning: If it looks **too different**, this might mean that they are using their own recipe, check the [different models](https://github.com/eried/portapack-mayhem/wiki/PortaPack-Versions) in our wiki. For example all the H3 and clones of that version use their own version of the firmware. They do not contribute the changes back and eventually you will be left with a device that nobody maintains:
|
||||
:warning: If it looks **too different**, this might mean that they are using their own recipe, check the [different models](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions) in our wiki. For example all the H3 and clones of that version use their own version of the firmware. They do not contribute the changes back and eventually you will be left with a device that nobody maintains:
|
||||
|
||||

|
||||
|
||||
## Where is the latest version?
|
||||
|
||||
The current **stable release** is on the [](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description. The **latest (nightly) release** can be found [here](https://github.com/eried/portapack-mayhem/releases/).
|
||||
The current **stable release** is on the [](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) page. Follow the instructions you can find in the release description. The **latest (nightly) release** can be found [here](https://github.com/portapack-mayhem/mayhem-firmware/releases/).
|
||||
|
||||
## How can I collaborate
|
||||
You can write [documentation](https://github.com/eried/portapack-mayhem/wiki), fix bugs and [answer issues](https://github.com/eried/portapack-mayhem/issues) or add new functionality. Please check the following [guide](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) with details.
|
||||
You can write [documentation](https://github.com/portapack-mayhem/mayhem-firmware/wiki), fix bugs and [answer issues](https://github.com/portapack-mayhem/mayhem-firmware/issues) or add new functionality. Please check the following [guide](https://github.com/portapack-mayhem/mayhem-firmware/wiki/How-to-collaborate) with details.
|
||||
|
||||
Consider that the hardware and firmware has been created and maintain by a [lot](https://github.com/mossmann/hackrf/graphs/contributors) of [people](https://github.com/eried/portapack-mayhem/graphs/contributors), so always try collaborating your time and effort first. For coding related questions, if something does not fit as an issue, please join our Discord by clicking the chat badge on [top](#portapack-mayhem).
|
||||
Consider that the hardware and firmware has been created and maintain by a [lot](https://github.com/mossmann/hackrf/graphs/contributors) of [people](https://github.com/portapack-mayhem/mayhem-firmware/graphs/contributors), so always try collaborating your time and effort first. For coding related questions, if something does not fit as an issue, please join our Discord by clicking the chat badge on [top](#portapack-mayhem).
|
||||
|
||||
[](https://github.com/eried/portapack-mayhem/graphs/contributors)
|
||||
[](https://github.com/portapack-mayhem/mayhem-firmware/graphs/contributors)
|
||||
|
||||
To support the people behind the hardware, please buy a genuine [HackRF](https://greatscottgadgets.com/hackrf/) and [PortaPack](https://store.sharebrained.com/products/portapack-for-hackrf-one-kit).
|
||||
|
||||
## What if I really want something specific?
|
||||
If what you need can be relevant in general, you can [request a feature](https://github.com/eried/portapack-mayhem/issues/new?labels=enhancement&template=feature_request.md).
|
||||
If what you need can be relevant in general, you can [request a feature](https://github.com/portapack-mayhem/mayhem-firmware/issues/new?labels=enhancement&template=feature_request.md).
|
||||
|
||||
<del>You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/eried/portapack-mayhem/issues/new/choose) or [choosing](https://github.com/eried/portapack-mayhem/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).</del>
|
||||
<del>You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/portapack-mayhem/mayhem-firmware/issues/new/choose) or [choosing](https://github.com/portapack-mayhem/mayhem-firmware/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).</del>
|
||||
|
||||
<del>Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).</del>
|
||||
|
||||
Bountysource has not been reliable lately, so until this changes, please **DO NOT** post a bounty there. Go to our Discord by clicking the chat badge on [top](#portapack-mayhem) and discuss there.
|
||||
|
||||
## What if I need help?
|
||||
First, check the [documentation](https://github.com/eried/portapack-mayhem/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
|
||||
First, check the [documentation](https://github.com/portapack-mayhem/mayhem-firmware/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/portapack-mayhem/mayhem-firmware/issues/new/choose).
|
||||
|
||||
You can reach the [official community](https://www.facebook.com/groups/177623356165819) in Facebook, and our Discord by clicking the chat badge on [top](#portapack-mayhem).
|
||||
|
||||
+22
-14
@@ -10,32 +10,40 @@ VOLUME /havoc
|
||||
WORKDIR /havoc/firmware
|
||||
|
||||
# Fetch dependencies from APT
|
||||
RUN apt-get update && \
|
||||
apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl && \
|
||||
apt-get -qy autoremove
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl ninja-build \
|
||||
&& apt-get -qy autoremove \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
#Install current pip from PyPa
|
||||
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
|
||||
python3 get-pip.py
|
||||
|
||||
#Fetch additional dependencies from Python 3.x pip
|
||||
RUN pip install pyyaml
|
||||
RUN ln -s /usr/bin/python3 /usr/bin/python && \
|
||||
ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
RUN pip install pyyaml \
|
||||
&& ln -s /usr/bin/python3 /usr/bin/python \
|
||||
&& ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
# Grab the GNU ARM toolchain from arm.com
|
||||
# Then extract contents to /opt/build/armbin/
|
||||
RUN mkdir /opt/build && cd /opt/build && \
|
||||
wget -O gcc-arm-none-eabi $ARMBINURL && \
|
||||
mkdir armbin && \
|
||||
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
RUN mkdir /opt/build \
|
||||
&& cd /opt/build \
|
||||
&& wget -O gcc-arm-none-eabi $ARMBINURL \
|
||||
&& mkdir armbin \
|
||||
&& tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
|
||||
# Configure CCACHE
|
||||
RUN mkdir ~/bin && cd ~/bin && \
|
||||
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
|
||||
RUN mkdir ~/bin \
|
||||
&& cd ~/bin \
|
||||
&& for tool in gcc g++ cpp c++; do \
|
||||
ln -s $(which ccache) arm-none-eabi-$tool \
|
||||
; done
|
||||
|
||||
CMD cd .. && cd build && \
|
||||
cmake .. && make firmware
|
||||
ADD firmware/tools/docker-entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
|
||||
# replace make with ninja temporarily while building your image if you prefer to use that by default
|
||||
CMD ["make"]
|
||||
|
||||
@@ -24,4 +24,4 @@ RUN mkdir ~/bin && cd ~/bin && \
|
||||
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
|
||||
|
||||
CMD cd .. && cd build && \
|
||||
cmake .. && make firmware
|
||||
cmake .. && make ppfw
|
||||
|
||||
+22
-14
@@ -10,32 +10,40 @@ VOLUME /havoc
|
||||
WORKDIR /havoc/firmware
|
||||
|
||||
# Fetch dependencies from APT
|
||||
RUN apt-get update && \
|
||||
apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl && \
|
||||
apt-get -qy autoremove
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl ninja-build \
|
||||
&& apt-get -qy autoremove \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
#Install current pip from PyPa
|
||||
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
|
||||
python3 get-pip.py
|
||||
|
||||
#Fetch additional dependencies from Python 3.x pip
|
||||
RUN pip install pyyaml
|
||||
RUN ln -s /usr/bin/python3 /usr/bin/python && \
|
||||
ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
RUN pip install pyyaml \
|
||||
&& ln -s /usr/bin/python3 /usr/bin/python \
|
||||
&& ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
# Grab the GNU ARM toolchain from arm.com
|
||||
# Then extract contents to /opt/build/armbin/
|
||||
RUN mkdir /opt/build && cd /opt/build && \
|
||||
wget -O gcc-arm-none-eabi $ARMBINURL && \
|
||||
mkdir armbin && \
|
||||
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
RUN mkdir /opt/build \
|
||||
&& cd /opt/build \
|
||||
&& wget -O gcc-arm-none-eabi $ARMBINURL \
|
||||
&& mkdir armbin \
|
||||
&& tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
|
||||
# Configure CCACHE
|
||||
RUN mkdir ~/bin && cd ~/bin && \
|
||||
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
|
||||
RUN mkdir ~/bin \
|
||||
&& cd ~/bin \
|
||||
&& for tool in gcc g++ cpp c++; do \
|
||||
ln -s $(which ccache) arm-none-eabi-$tool \
|
||||
; done
|
||||
|
||||
CMD cd .. && cd build && \
|
||||
cmake .. && make firmware
|
||||
ADD firmware/tools/docker-entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
|
||||
# replace make with ninja temporarily while building your image if you prefer to use that by default
|
||||
CMD ["make"]
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; URL=https://github.com/eried/portapack-mayhem/" />
|
||||
<meta http-equiv="refresh" content="0; URL=https://github.com/portapack-mayhem/mayhem-firmware/" />
|
||||
</head>
|
||||
<body>
|
||||
<p>If you are not redirected, <a href="https://github.com/eried/portapack-mayhem/">click here</a>.</p>
|
||||
<p>If you are not redirected, <a href="https://github.com/portapack-mayhem/mayhem-firmware/">click here</a>.</p>
|
||||
</body>
|
||||
|
||||
+49
-1
@@ -18,6 +18,8 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(firmware)
|
||||
|
||||
set(BASEBAND ${PROJECT_SOURCE_DIR}/baseband)
|
||||
@@ -34,6 +36,17 @@ set(LZ4 lz4)
|
||||
set(FIRMWARE_NAME portapack-h1_h2-mayhem)
|
||||
set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
|
||||
|
||||
# In our current build container cmake need a little help to get the version :)
|
||||
if(NOT DEFINED ${CMAKE_CXX_COMPILER_VERSION})
|
||||
execute_process(COMMAND bash "-c" "arm-none-eabi-g++ -v 2>&1 | grep 'gcc version' | awk '{print $3}'" OUTPUT_VARIABLE CMAKE_CXX_COMPILER_VERSION)
|
||||
string(STRIP ${CMAKE_CXX_COMPILER_VERSION} CMAKE_CXX_COMPILER_VERSION)
|
||||
endif()
|
||||
|
||||
set(GCC_VERSION_MISMATCH 0)
|
||||
if(NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL ${EXPECTED_GCC_VERSION})
|
||||
set(GCC_VERSION_MISMATCH 1)
|
||||
endif()
|
||||
|
||||
add_subdirectory(application)
|
||||
add_subdirectory(baseband)
|
||||
add_subdirectory(test)
|
||||
@@ -48,10 +61,19 @@ add_custom_command(
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
firmware ALL
|
||||
firmware
|
||||
DEPENDS ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME}
|
||||
)
|
||||
|
||||
if(${GCC_VERSION_MISMATCH})
|
||||
set(COMPILER_MISMATCH_MESSAGE "WARNING: Compiler version mismatch, please use the official compiler version ${EXPECTED_GCC_VERSION} when sharing builds! Current compiler version: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
message(${COMPILER_MISMATCH_MESSAGE})
|
||||
add_custom_command(
|
||||
TARGET firmware POST_BUILD
|
||||
COMMAND echo ${COMPILER_MISMATCH_MESSAGE}
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
program
|
||||
COMMAND ${PROJECT_SOURCE_DIR}/tools/enter_mode.sh hackrf
|
||||
@@ -68,6 +90,21 @@ add_custom_target(
|
||||
DEPENDS program
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PPFW_FILENAME}
|
||||
|
||||
COMMAND rm -rf firmware_tar
|
||||
COMMAND mkdir -p firmware_tar/FIRMWARE
|
||||
# Using VERSION_NOHASH to avoid dev builds piling up in the FIRMWARE folder of the sd card of testers in #test-drive
|
||||
COMMAND cp ${FIRMWARE_FILENAME} firmware_tar/FIRMWARE/portapack-mayhem_${VERSION_NOHASH}.bin
|
||||
COMMAND mkdir -p firmware_tar/APPS
|
||||
COMMAND cp application/*.ppma firmware_tar/APPS
|
||||
COMMAND cd firmware_tar && tar -cvaf ../${PPFW_FILENAME} *
|
||||
DEPENDS firmware
|
||||
# Dont use VERBATIM here as it prevents usage of globbing (*)
|
||||
# There shouldnt be any funny business in the filenames above :)
|
||||
)
|
||||
|
||||
# TODO: Bad hack to fix location of LICENSE file for tar.
|
||||
add_custom_command(
|
||||
OUTPUT ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip
|
||||
@@ -87,7 +124,18 @@ add_custom_command(
|
||||
DEPENDS ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
ppfw ALL
|
||||
DEPENDS ${PPFW_FILENAME}
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
oci
|
||||
DEPENDS ${PPFW_FILENAME}
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
release
|
||||
DEPENDS MD5SUMS SHA256SUMS
|
||||
)
|
||||
|
||||
|
||||
@@ -116,13 +116,12 @@ set(CSRC
|
||||
usb_serial_cdc.c
|
||||
usb_serial_descriptor.c
|
||||
usb_serial_endpoints.c
|
||||
usb_serial_io.c
|
||||
usb_serial_device_to_host.c
|
||||
${HACKRF_PATH}/firmware/common/usb.c
|
||||
${HACKRF_PATH}/firmware/common/usb_queue.c
|
||||
${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c
|
||||
${HACKRF_PATH}/firmware/common/usb_request.c
|
||||
${HACKRF_PATH}/firmware/common/usb_standard_request.c
|
||||
${CHIBIOS}/os/various/shell.c
|
||||
${CHIBIOS}/os/various/chprintf.c
|
||||
)
|
||||
|
||||
@@ -130,6 +129,7 @@ set(CSRC
|
||||
# setting.
|
||||
set(CPPSRC
|
||||
main.cpp
|
||||
shell.cpp
|
||||
${COMMON}/acars_packet.cpp
|
||||
${COMMON}/adsb.cpp
|
||||
${COMMON}/adsb_frame.cpp
|
||||
@@ -203,9 +203,11 @@ set(CPPSRC
|
||||
metadata_file.cpp
|
||||
portapack.cpp
|
||||
usb_serial_shell.cpp
|
||||
usb_serial_shell_filesystem.cpp
|
||||
usb_serial_event.cpp
|
||||
usb_serial_thread.cpp
|
||||
usb_serial.cpp
|
||||
usb_serial_host_to_device.cpp
|
||||
qrcodegen.cpp
|
||||
radio.cpp
|
||||
receiver_model.cpp
|
||||
@@ -265,7 +267,7 @@ set(CPPSRC
|
||||
apps/ble_tx_app.cpp
|
||||
apps/capture_app.cpp
|
||||
apps/ert_app.cpp
|
||||
apps/gps_sim_app.cpp
|
||||
# apps/gps_sim_app.cpp
|
||||
# apps/lge_app.cpp
|
||||
apps/pocsag_app.cpp
|
||||
# apps/replay_app.cpp
|
||||
@@ -311,9 +313,9 @@ set(CPPSRC
|
||||
apps/ui_settings.cpp
|
||||
apps/ui_siggen.cpp
|
||||
apps/ui_sonde.cpp
|
||||
apps/ui_spectrum_painter_image.cpp
|
||||
apps/ui_spectrum_painter_text.cpp
|
||||
apps/ui_spectrum_painter.cpp
|
||||
# apps/ui_spectrum_painter_image.cpp
|
||||
# apps/ui_spectrum_painter_text.cpp
|
||||
# apps/ui_spectrum_painter.cpp
|
||||
apps/ui_ss_viewer.cpp
|
||||
apps/ui_sstvtx.cpp
|
||||
apps/ui_subghzd.cpp
|
||||
@@ -424,7 +426,7 @@ set(CPPWARN "-Wall -Wextra -Wno-psabi")
|
||||
# List all default C defines here, like -D_DEBUG=1
|
||||
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
|
||||
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
|
||||
set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"' -DVERSION_MD5=${VERSION_MD5}")
|
||||
set(DDEFS "-DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -D'VERSION_STRING=\"${VERSION}\"' -DVERSION_MD5=${VERSION_MD5} -D'GCC_VERSION_MISMATCH=${GCC_VERSION_MISMATCH}'")
|
||||
|
||||
# List all default ASM defines here, like -D_DEBUG=1
|
||||
set(DADEFS)
|
||||
|
||||
@@ -40,7 +40,7 @@ using namespace portapack;
|
||||
|
||||
namespace {
|
||||
fs::path get_settings_path(const std::string& app_name) {
|
||||
return fs::path{u"/SETTINGS"} / app_name + u".ini";
|
||||
return fs::path{SETTINGS_DIR} / app_name + u".ini";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -156,7 +156,7 @@ bool save_settings(std::string_view store_name, const SettingBindings& bindings)
|
||||
File f;
|
||||
auto path = get_settings_path(std::string{store_name});
|
||||
|
||||
make_new_directory(SETTINGS_DIR);
|
||||
ensure_directory(SETTINGS_DIR);
|
||||
auto error = f.create(path);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
@@ -293,6 +293,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
|
||||
ais::format::text(entry_.name),
|
||||
0,
|
||||
GeoPos::alt_unit::METERS,
|
||||
GeoPos::spd_unit::NONE,
|
||||
ais::format::latlon_float(entry_.last_position.latitude.normalized()),
|
||||
ais::format::latlon_float(entry_.last_position.longitude.normalized()),
|
||||
entry_.last_position.true_heading,
|
||||
@@ -315,7 +316,7 @@ AISRecentEntryDetailView& AISRecentEntryDetailView::operator=(const AISRecentEnt
|
||||
|
||||
void AISRecentEntryDetailView::update_position() {
|
||||
if (send_updates)
|
||||
geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading, 0);
|
||||
geomap_view->update_position(ais::format::latlon_float(entry_.last_position.latitude.normalized()), ais::format::latlon_float(entry_.last_position.longitude.normalized()), (float)entry_.last_position.true_heading, 0, entry_.last_position.speed_over_ground);
|
||||
}
|
||||
|
||||
void AISRecentEntryDetailView::focus() {
|
||||
|
||||
@@ -54,24 +54,31 @@ std::string id(ID value) {
|
||||
}
|
||||
|
||||
std::string consumption(Consumption value) {
|
||||
return to_string_dec_uint(value, 10);
|
||||
return to_string_dec_uint(value, 8);
|
||||
}
|
||||
|
||||
std::string commodity_type(CommodityType value) {
|
||||
return to_string_dec_uint(value, 2);
|
||||
}
|
||||
|
||||
std::string tamper_flags(TamperFlags value) {
|
||||
return to_string_hex(value & 0xFFFF, 4); // Note: ignoring bits 32-47 of tamper flags in IDM type due to screen width
|
||||
}
|
||||
|
||||
std::string tamper_flags_scm(TamperFlags value) {
|
||||
return " " + to_string_hex(value & 0x0F, 1) + "/" + to_string_hex(value >> 4, 1); // Physical/Encoder flags
|
||||
}
|
||||
|
||||
} /* namespace format */
|
||||
|
||||
} /* namespace ert */
|
||||
|
||||
void ERTLogger::on_packet(const ert::Packet& packet, const uint32_t target_frequency) {
|
||||
const auto formatted = packet.symbols_formatted();
|
||||
|
||||
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
|
||||
const auto target_frequency_str = to_string_dec_uint(target_frequency, 10);
|
||||
|
||||
std::string entry = target_frequency_str + " " + ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors;
|
||||
std::string entry = target_frequency_str + " " + ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors + " ID:" + to_string_dec_uint(packet.id(), 1);
|
||||
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
}
|
||||
|
||||
@@ -81,6 +88,8 @@ void ERTRecentEntry::update(const ert::Packet& packet) {
|
||||
received_count++;
|
||||
|
||||
last_consumption = packet.consumption();
|
||||
last_tamper_flags = packet.tamper_flags();
|
||||
packet_type = packet.type();
|
||||
}
|
||||
|
||||
namespace ui {
|
||||
@@ -91,13 +100,10 @@ void RecentEntriesTable<ERTRecentEntries>::draw(
|
||||
const Rect& target_rect,
|
||||
Painter& painter,
|
||||
const Style& style) {
|
||||
std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption);
|
||||
std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption) + " ";
|
||||
|
||||
if (entry.received_count > 999) {
|
||||
line += " +++";
|
||||
} else {
|
||||
line += " " + to_string_dec_uint(entry.received_count, 3);
|
||||
}
|
||||
line += (entry.packet_type == ert::Packet::Type::SCM) ? ert::format::tamper_flags_scm(entry.last_tamper_flags) : ert::format::tamper_flags(entry.last_tamper_flags);
|
||||
line += (entry.received_count > 99) ? " ++" : to_string_dec_uint(entry.received_count, 3);
|
||||
|
||||
line.resize(target_rect.width() / 8, ' ');
|
||||
painter.draw_string(target_rect.location(), style, line);
|
||||
|
||||
@@ -72,11 +72,12 @@ struct ERTRecentEntry {
|
||||
|
||||
ert::ID id{ert::invalid_id};
|
||||
ert::CommodityType commodity_type{ert::invalid_commodity_type};
|
||||
ert::Consumption last_consumption{};
|
||||
ert::TamperFlags last_tamper_flags{};
|
||||
ert::Packet::Type packet_type{};
|
||||
|
||||
size_t received_count{0};
|
||||
|
||||
ert::Consumption last_consumption{};
|
||||
|
||||
ERTRecentEntry(
|
||||
const Key& key)
|
||||
: id{key.id},
|
||||
@@ -137,9 +138,10 @@ class ERTAppView : public View {
|
||||
|
||||
const RecentEntriesColumns columns{{
|
||||
{"ID", 10},
|
||||
{"Tp", 2},
|
||||
{"Consumpt", 10},
|
||||
{"Cnt", 3},
|
||||
{"Ty", 2},
|
||||
{"Consumpt", 8},
|
||||
{"Tamp", 4},
|
||||
{"Ct", 2},
|
||||
}};
|
||||
ERTRecentEntriesView recent_entries_view{columns, recent};
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "tonesets.hpp"
|
||||
#include "ui_tone_key.hpp"
|
||||
#include "audio.hpp"
|
||||
|
||||
using namespace tonekey;
|
||||
using namespace portapack;
|
||||
@@ -40,6 +41,7 @@ void SoundBoardView::stop() {
|
||||
if (is_active())
|
||||
replay_thread.reset();
|
||||
|
||||
audio::output::stop();
|
||||
transmitter_model.disable();
|
||||
tx_view.set_transmitting(false);
|
||||
|
||||
@@ -123,6 +125,8 @@ void SoundBoardView::start_tx(const uint32_t id) {
|
||||
transmitter_model.enable();
|
||||
|
||||
tx_view.set_transmitting(true);
|
||||
|
||||
audio::output::start();
|
||||
}
|
||||
|
||||
/*void SoundBoardView::show_infos() {
|
||||
@@ -227,6 +231,7 @@ SoundBoardView::SoundBoardView(
|
||||
//&text_title,
|
||||
//&text_duration,
|
||||
//&progressbar,
|
||||
&field_volume,
|
||||
&page_info,
|
||||
&check_loop,
|
||||
&check_random,
|
||||
@@ -274,7 +279,6 @@ SoundBoardView::SoundBoardView(
|
||||
|
||||
SoundBoardView::~SoundBoardView() {
|
||||
stop();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class SoundBoardView : public View {
|
||||
void on_select_entry();
|
||||
|
||||
Labels labels{
|
||||
//{ { 0, 20 * 8 + 4 }, "Title:", Color::light_grey() },
|
||||
{{24 * 8, 180}, "Vol:", Color::light_grey()},
|
||||
{{0, 180}, "Key:", Color::light_grey()}};
|
||||
|
||||
Button button_next_page{
|
||||
@@ -122,10 +122,13 @@ class SoundBoardView : public View {
|
||||
};*/
|
||||
|
||||
OptionsField options_tone_key{
|
||||
{32, 180},
|
||||
{4 * 8, 180},
|
||||
18,
|
||||
{}};
|
||||
|
||||
AudioVolumeField field_volume{
|
||||
{28 * 8, 180}};
|
||||
|
||||
Checkbox check_loop{
|
||||
{0, 25 * 8 + 4},
|
||||
4,
|
||||
|
||||
@@ -19,7 +19,7 @@ void AboutView::update() {
|
||||
switch (++frame) {
|
||||
case 1:
|
||||
// TODO: Generate this automatically from github
|
||||
// https://github.com/eried/portapack-mayhem/graphs/contributors?to=2022-01-01&from=2020-04-12&type=c
|
||||
// https://github.com/portapack-mayhem/mayhem-firmware/graphs/contributors?to=2022-01-01&from=2020-04-12&type=c
|
||||
console.writeln(STR_COLOR_DARK_YELLOW "Mayhem:");
|
||||
console.writeln("eried,euquiq,gregoryfenton");
|
||||
console.writeln("johnelder,jwetzell,nnemanjan00");
|
||||
@@ -42,7 +42,7 @@ void AboutView::update() {
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// https://github.com/eried/portapack-mayhem/graphs/contributors?to=2020-04-12&from=2015-07-31&type=c
|
||||
// https://github.com/portapack-mayhem/mayhem-firmware/graphs/contributors?to=2020-04-12&from=2015-07-31&type=c
|
||||
console.writeln(STR_COLOR_DARK_YELLOW "Havoc:");
|
||||
console.writeln("furrtek,mrmookie,NotPike");
|
||||
console.writeln("mjwaxios,ImDroided,Giorgiofox");
|
||||
@@ -55,7 +55,7 @@ void AboutView::update() {
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// https://github.com/eried/portapack-mayhem/graphs/contributors?from=2014-07-05&to=2015-07-31&type=c
|
||||
// https://github.com/portapack-mayhem/mayhem-firmware/graphs/contributors?from=2014-07-05&to=2015-07-31&type=c
|
||||
console.writeln(STR_COLOR_DARK_YELLOW "PortaPack:");
|
||||
console.writeln("jboone,argilo");
|
||||
console.writeln("");
|
||||
|
||||
@@ -266,6 +266,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
|
||||
get_map_tag(entry_),
|
||||
entry_.pos.altitude,
|
||||
GeoPos::alt_unit::FEET,
|
||||
GeoPos::spd_unit::MPH,
|
||||
entry_.pos.latitude,
|
||||
entry_.pos.longitude,
|
||||
entry_.velo.heading);
|
||||
@@ -290,7 +291,7 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) {
|
||||
} else if (geomap_view_) {
|
||||
// Map is showing, update the current item.
|
||||
geomap_view_->update_tag(get_map_tag(entry_));
|
||||
geomap_view_->update_position(entry.pos.latitude, entry.pos.longitude, entry.velo.heading, entry.pos.altitude);
|
||||
geomap_view_->update_position(entry.pos.latitude, entry.pos.longitude, entry.velo.heading, entry.pos.altitude, entry.velo.speed);
|
||||
} else {
|
||||
// Details is showing, update details.
|
||||
refresh_ui();
|
||||
@@ -317,6 +318,17 @@ bool ADSBRxDetailsView::add_map_marker(const AircraftRecentEntry& entry) {
|
||||
return markerStored == MARKER_STORED;
|
||||
}
|
||||
|
||||
void ADSBRxDetailsView::on_gps(const GPSPosDataMessage* msg) {
|
||||
if (!geomap_view_)
|
||||
return;
|
||||
geomap_view_->update_my_position(msg->lat, msg->lon, msg->altitude);
|
||||
}
|
||||
void ADSBRxDetailsView::on_orientation(const OrientationDataMessage* msg) {
|
||||
if (!geomap_view_)
|
||||
return;
|
||||
geomap_view_->update_my_orientation(msg->angle);
|
||||
}
|
||||
|
||||
void ADSBRxDetailsView::refresh_ui() {
|
||||
auto age = entry_.age;
|
||||
if (age < 60)
|
||||
|
||||
@@ -271,6 +271,8 @@ class ADSBRxDetailsView : public View {
|
||||
|
||||
private:
|
||||
void refresh_ui();
|
||||
void on_gps(const GPSPosDataMessage* msg);
|
||||
void on_orientation(const OrientationDataMessage* msg);
|
||||
|
||||
GeoMapView* geomap_view_{nullptr};
|
||||
ADSBRxAircraftDetailsView* aircraft_details_view_{nullptr};
|
||||
@@ -330,6 +332,19 @@ class ADSBRxDetailsView : public View {
|
||||
Button button_see_map{
|
||||
{16 * 8, 9 * 16, 12 * 8, 3 * 16},
|
||||
"See on map"};
|
||||
|
||||
MessageHandlerRegistration message_handler_gps{
|
||||
Message::ID::GPSPosData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const GPSPosDataMessage*>(p);
|
||||
this->on_gps(message);
|
||||
}};
|
||||
MessageHandlerRegistration message_handler_orientation{
|
||||
Message::ID::OrientationData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const OrientationDataMessage*>(p);
|
||||
this->on_orientation(message);
|
||||
}};
|
||||
};
|
||||
|
||||
/* Main ADSB application view and message dispatch. */
|
||||
|
||||
@@ -85,10 +85,12 @@ ADSBPositionView::ADSBPositionView(
|
||||
nav.push<GeoMapView>(
|
||||
geopos.altitude(),
|
||||
GeoPos::alt_unit::FEET,
|
||||
GeoPos::spd_unit::HIDDEN,
|
||||
geopos.lat(),
|
||||
geopos.lon(),
|
||||
[this](int32_t altitude, float lat, float lon) {
|
||||
[this](int32_t altitude, float lat, float lon, int32_t speed) {
|
||||
geopos.set_altitude(altitude);
|
||||
geopos.set_speed(speed);
|
||||
geopos.set_lat(lat);
|
||||
geopos.set_lon(lon);
|
||||
});
|
||||
|
||||
@@ -58,7 +58,8 @@ class ADSBPositionView : public OptionTabView {
|
||||
private:
|
||||
GeoPos geopos{
|
||||
{0, 2 * 16},
|
||||
GeoPos::FEET};
|
||||
GeoPos::FEET,
|
||||
GeoPos::HIDDEN};
|
||||
|
||||
Button button_set_map{
|
||||
{8 * 8, 6 * 16, 14 * 8, 2 * 16},
|
||||
|
||||
@@ -317,7 +317,7 @@ void APRSDetailsView::update() {
|
||||
}
|
||||
|
||||
if (send_updates)
|
||||
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0);
|
||||
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0, 0);
|
||||
}
|
||||
|
||||
APRSDetailsView::~APRSDetailsView() {
|
||||
@@ -339,6 +339,7 @@ APRSDetailsView::APRSDetailsView(
|
||||
entry_copy.source_formatted,
|
||||
0, // entry_copy.pos.altitude,
|
||||
GeoPos::alt_unit::FEET,
|
||||
GeoPos::spd_unit::HIDDEN,
|
||||
entry_copy.pos.latitude,
|
||||
entry_copy.pos.longitude,
|
||||
0, /*entry_copy.velo.heading,*/
|
||||
|
||||
@@ -48,7 +48,7 @@ void DfuMenu::paint(Painter& painter) {
|
||||
text_info_line_3.set(to_string_dec_uint(utilisation, 6));
|
||||
text_info_line_4.set(to_string_dec_uint(shared_memory.m4_heap_usage, 6));
|
||||
text_info_line_5.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6));
|
||||
text_info_line_6.set(to_string_dec_uint(shared_memory.m4_cpu_usage, 6));
|
||||
text_info_line_6.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6));
|
||||
text_info_line_7.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
|
||||
text_info_line_8.set(to_string_dec_uint(chTimeNow() / 1000, 6));
|
||||
|
||||
@@ -94,23 +94,25 @@ DfuMenu2::DfuMenu2(NavigationView& nav)
|
||||
&text_info_line_7,
|
||||
&text_info_line_8,
|
||||
&text_info_line_9,
|
||||
&text_info_line_10});
|
||||
&text_info_line_10,
|
||||
&text_info_line_11});
|
||||
}
|
||||
|
||||
void DfuMenu2::paint(Painter& painter) {
|
||||
text_info_line_1.set(to_string_dec_uint(portapack::receiver_model.target_frequency(), 10));
|
||||
text_info_line_2.set(to_string_dec_uint(portapack::receiver_model.baseband_bandwidth(), 10));
|
||||
text_info_line_3.set(to_string_dec_uint(portapack::receiver_model.sampling_rate(), 10));
|
||||
text_info_line_4.set(to_string_dec_uint((uint32_t)portapack::receiver_model.modulation(), 10));
|
||||
text_info_line_5.set(to_string_dec_uint(portapack::receiver_model.am_configuration(), 10));
|
||||
text_info_line_6.set(to_string_dec_uint(portapack::receiver_model.nbfm_configuration(), 10));
|
||||
text_info_line_7.set(to_string_dec_uint(portapack::receiver_model.wfm_configuration(), 10));
|
||||
text_info_line_8.set(to_string_dec_uint(portapack::transmitter_model.target_frequency(), 10));
|
||||
text_info_line_9.set(to_string_dec_uint(portapack::transmitter_model.baseband_bandwidth(), 10));
|
||||
text_info_line_10.set(to_string_dec_uint(portapack::transmitter_model.sampling_rate(), 10));
|
||||
text_info_line_4.set(to_string_dec_uint(((uint32_t)shared_memory.m4_performance_counter) * 100 / 127, 10));
|
||||
text_info_line_5.set(to_string_dec_uint((uint32_t)portapack::receiver_model.modulation(), 10));
|
||||
text_info_line_6.set(to_string_dec_uint(portapack::receiver_model.am_configuration(), 10));
|
||||
text_info_line_7.set(to_string_dec_uint(portapack::receiver_model.nbfm_configuration(), 10));
|
||||
text_info_line_8.set(to_string_dec_uint(portapack::receiver_model.wfm_configuration(), 10));
|
||||
text_info_line_9.set(to_string_dec_uint(portapack::transmitter_model.target_frequency(), 10));
|
||||
text_info_line_10.set(to_string_dec_uint(portapack::transmitter_model.baseband_bandwidth(), 10));
|
||||
text_info_line_11.set(to_string_dec_uint(portapack::transmitter_model.sampling_rate(), 10));
|
||||
|
||||
constexpr auto margin = 5;
|
||||
constexpr auto lines = 10 + 2;
|
||||
constexpr auto lines = 11 + 2;
|
||||
|
||||
painter.fill_rectangle(
|
||||
{{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
|
||||
|
||||
@@ -83,13 +83,15 @@ class DfuMenu2 : public View {
|
||||
{{5 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "RX Freq:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "RX BW:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "RX SampR:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "Modulatn:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "AM cfg:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "NBFM cfg:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "WFM cfg:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "TX Freq:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "TX BW:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "TX SampR:", Color::dark_cyan()}};
|
||||
{{5 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "RX Satu%:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "Modulatn:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "AM cfg:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "NBFM cfg:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "WFM cfg:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "TX Freq:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "TX BW:", Color::dark_cyan()},
|
||||
{{5 * CHARACTER_WIDTH, 15 * LINE_HEIGHT}, "TX SampR:", Color::dark_cyan()},
|
||||
};
|
||||
|
||||
Text text_info_line_1{{14 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_2{{14 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
@@ -101,6 +103,7 @@ class DfuMenu2 : public View {
|
||||
Text text_info_line_8{{14 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_9{{14 * CHARACTER_WIDTH, 13 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_10{{14 * CHARACTER_WIDTH, 14 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_11{{14 * CHARACTER_WIDTH, 15 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -50,6 +50,17 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.ppfw.tar")) {
|
||||
auto filename = entry.path().filename();
|
||||
auto path = entry.path().native();
|
||||
|
||||
menu_view.add_item({filename.string().substr(0, max_filename_length),
|
||||
ui::Color::purple(),
|
||||
&bitmap_icon_temperature,
|
||||
[this, path](KeyEvent) {
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) {
|
||||
@@ -65,7 +76,43 @@ void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path
|
||||
});
|
||||
}
|
||||
|
||||
bool FlashUtilityView::endsWith(const std::u16string& str, const std::u16string& suffix) {
|
||||
if (str.length() >= suffix.length()) {
|
||||
std::u16string endOfString = str.substr(str.length() - suffix.length());
|
||||
return endOfString == suffix;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path FlashUtilityView::extract_tar(std::filesystem::path::string_type path) {
|
||||
//
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle(
|
||||
{0, 0, portapack::display.width(), portapack::display.height()},
|
||||
ui::Color::black());
|
||||
painter.draw_string({12, 24}, this->nav_.style(), "Unpacking TAR file...");
|
||||
|
||||
auto res = UnTar::untar(path, [this](const std::string fileName) {
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, ui::Color::black());
|
||||
painter.draw_string({0, 60}, this->nav_.style(), fileName);
|
||||
});
|
||||
if (res.string().empty()) {
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle({0, 50, portapack::display.width(), 90}, ui::Color::black());
|
||||
painter.draw_string({0, 60}, this->nav_.style(), "BAD TAR FILE");
|
||||
chThdSleepMilliseconds(5000);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
|
||||
if (endsWith(path, u".ppfw.tar")) {
|
||||
// extract, then update
|
||||
path = extract_tar(u'/' + path).native();
|
||||
if (path.empty()) return;
|
||||
}
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle(
|
||||
{0, 0, portapack::display.width(), portapack::display.height()},
|
||||
@@ -74,7 +121,7 @@ void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
|
||||
painter.draw_string({12, 24}, this->nav_.style(), "This will take 15 seconds.");
|
||||
painter.draw_string({12, 64}, this->nav_.style(), "Please wait while LEDs RX");
|
||||
painter.draw_string({12, 84}, this->nav_.style(), "and TX are flashing.");
|
||||
painter.draw_string({12, 124}, this->nav_.style(), "Then restart the device.");
|
||||
painter.draw_string({12, 124}, this->nav_.style(), "Device will then restart.");
|
||||
|
||||
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (path.length() + 1) * 2);
|
||||
m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false);
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "ff.h"
|
||||
#include "baseband_api.hpp"
|
||||
#include "core_control.hpp"
|
||||
|
||||
#include "untar.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
@@ -55,8 +55,10 @@ class FlashUtilityView : public View {
|
||||
{0, 2 * 8, 240, 26 * 8},
|
||||
true};
|
||||
|
||||
std::filesystem::path extract_tar(std::filesystem::path::string_type path); // extracts the tar file, and returns the firmware.bin path from it. empty string if no fw
|
||||
void firmware_selected(std::filesystem::path::string_type path);
|
||||
void flash_firmware(std::filesystem::path::string_type path);
|
||||
bool endsWith(const std::u16string& str, const std::u16string& suffix);
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -78,8 +78,6 @@ void NuoptixView::transmit(bool setup) {
|
||||
}
|
||||
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_lna(40);
|
||||
transmitter_model.set_vga(40);
|
||||
transmitter_model.enable();
|
||||
|
||||
dtmf_message[0] = '*'; // "Pre-tone for restart" method #1
|
||||
@@ -136,6 +134,7 @@ void NuoptixView::transmit(bool setup) {
|
||||
shared_memory.bb_data.tones_data.silence = NUOPTIX_TONE_LENGTH; // 49ms tone, 49ms space
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
|
||||
baseband::set_tones_config(transmitter_model.channel_bandwidth(), 0, 6 * 2, true, true);
|
||||
|
||||
timecode++;
|
||||
@@ -156,7 +155,7 @@ NuoptixView::NuoptixView(
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
transmitter_model.target_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ void ReconView::set_loop_config(bool v) {
|
||||
persistent_memory::set_recon_continuous(continuous);
|
||||
}
|
||||
|
||||
void ReconView::recon_stop_recording() {
|
||||
void ReconView::recon_stop_recording(bool exiting) {
|
||||
if (is_recording) {
|
||||
if (field_mode.selected_index_value() == SPEC_MODULATION)
|
||||
button_audio_app.set_text("RAW");
|
||||
@@ -113,14 +113,14 @@ void ReconView::recon_stop_recording() {
|
||||
button_config.set_style(&Styles::white);
|
||||
is_recording = false;
|
||||
// repeater mode
|
||||
if (persistent_memory::recon_repeat_recorded()) {
|
||||
if (!exiting && persistent_memory::recon_repeat_recorded()) {
|
||||
start_repeat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ReconView::clear_freqlist_for_ui_action() {
|
||||
recon_stop_recording();
|
||||
recon_stop_recording(false);
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio::output::stop();
|
||||
// flag to detect and reload frequency_list
|
||||
@@ -308,9 +308,16 @@ void ReconView::focus() {
|
||||
}
|
||||
|
||||
ReconView::~ReconView() {
|
||||
recon_stop_recording();
|
||||
if (recon_tx) {
|
||||
replay_thread.reset();
|
||||
}
|
||||
|
||||
recon_stop_recording(true);
|
||||
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio::output::stop();
|
||||
|
||||
transmitter_model.disable();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -542,7 +549,7 @@ ReconView::ReconView(NavigationView& nav)
|
||||
};
|
||||
|
||||
button_manual_recon.on_select = [this](Button&) {
|
||||
button_remove.set_text("DELETE");
|
||||
button_remove.set_text("<DELETE>");
|
||||
button_add.hidden(false);
|
||||
scanner_mode = false;
|
||||
manual_mode = true;
|
||||
@@ -636,12 +643,12 @@ ReconView::ReconView(NavigationView& nav)
|
||||
scanner_mode = false;
|
||||
button_scanner_mode.set_style(&Styles::blue);
|
||||
button_scanner_mode.set_text("RECON");
|
||||
button_remove.set_text("REMOVE");
|
||||
button_remove.set_text("<REMOVE>");
|
||||
} else {
|
||||
scanner_mode = true;
|
||||
button_scanner_mode.set_style(&Styles::red);
|
||||
button_scanner_mode.set_text("SCAN");
|
||||
button_remove.set_text("DELETE");
|
||||
button_remove.set_text("<DELETE>");
|
||||
}
|
||||
frequency_file_load();
|
||||
if (autostart) {
|
||||
@@ -844,7 +851,7 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
if (status != 1) {
|
||||
status = 1;
|
||||
if (wait != 0) {
|
||||
recon_stop_recording();
|
||||
recon_stop_recording(false);
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio::output::stop();
|
||||
}
|
||||
@@ -1145,7 +1152,7 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
|
||||
return 0;
|
||||
field_mode.on_change = [this](size_t, OptionsField::value_t) {};
|
||||
field_bw.on_change = [this](size_t, OptionsField::value_t) {};
|
||||
recon_stop_recording();
|
||||
recon_stop_recording(false);
|
||||
if (record_view != nullptr) {
|
||||
remove_child(record_view.get());
|
||||
record_view.reset();
|
||||
@@ -1377,10 +1384,31 @@ void ReconView::start_repeat() {
|
||||
repeat_file_error(rawfile, "Can't open file to send to thread");
|
||||
return;
|
||||
}
|
||||
repeat_ready_signal = true;
|
||||
repeat_cur_rep++;
|
||||
// wait for TX if needed (hackish, direct screen update since the UI will be blocked)
|
||||
if (persistent_memory::recon_repeat_delay() > 0) {
|
||||
uint8_t delay = persistent_memory::recon_repeat_delay();
|
||||
Painter p;
|
||||
while (delay > 0) {
|
||||
std::string delay_message = "TX DELAY: " + to_string_dec_uint(delay) + "s";
|
||||
|
||||
// update display information
|
||||
p.fill_rectangle({0, (SCREEN_H / 2) - 16, SCREEN_W, 64}, Color::light_grey());
|
||||
p.draw_string({(SCREEN_W / 2) - 7 * 8, SCREEN_H / 2}, Styles::red, delay_message);
|
||||
|
||||
// sleep 1 second
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
// decre delay
|
||||
if (delay > 0)
|
||||
delay = delay - 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ReplayThread starts immediately on construction; must be set before creating.
|
||||
repeat_ready_signal = true;
|
||||
repeat_cur_rep++;
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
/* read_size */ repeat_read_size,
|
||||
|
||||
@@ -54,8 +54,6 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
#define RECON_CFG_FILE u"SETTINGS/recon.cfg"
|
||||
|
||||
enum class recon_mode : uint8_t {
|
||||
Recon,
|
||||
Scanner,
|
||||
@@ -110,7 +108,7 @@ class ReconView : public View {
|
||||
void load_persisted_settings();
|
||||
bool recon_save_freq(const std::filesystem::path& path, size_t index, bool warn_if_exists);
|
||||
// placeholder for possible void recon_start_recording();
|
||||
void recon_stop_recording();
|
||||
void recon_stop_recording(bool exiting);
|
||||
|
||||
// Returns true if 'current_index' is in bounds of frequency_list.
|
||||
bool current_is_valid();
|
||||
|
||||
@@ -106,6 +106,7 @@ void ReconSetupViewMore::save() {
|
||||
persistent_memory::set_recon_repeat_nb(field_repeat_nb.value());
|
||||
persistent_memory::set_recon_repeat_amp(checkbox_repeat_amp.value());
|
||||
persistent_memory::set_recon_repeat_gain(field_repeat_gain.value());
|
||||
persistent_memory::set_recon_repeat_delay(field_repeat_delay.value());
|
||||
};
|
||||
|
||||
void ReconSetupViewMain::focus() {
|
||||
@@ -128,7 +129,9 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
&field_repeat_nb,
|
||||
&checkbox_repeat_amp,
|
||||
&text_repeat_gain,
|
||||
&field_repeat_gain});
|
||||
&field_repeat_gain,
|
||||
&text_repeat_delay,
|
||||
&field_repeat_delay});
|
||||
|
||||
// tx options have to be in yellow to inform the users that activating them will make the device transmit
|
||||
checkbox_repeat_recorded.set_style(&Styles::yellow);
|
||||
@@ -137,6 +140,8 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
checkbox_repeat_amp.set_style(&Styles::yellow);
|
||||
text_repeat_gain.set_style(&Styles::yellow);
|
||||
field_repeat_gain.set_style(&Styles::yellow);
|
||||
text_repeat_delay.set_style(&Styles::yellow);
|
||||
field_repeat_delay.set_style(&Styles::yellow);
|
||||
|
||||
checkbox_load_freqs.set_value(persistent_memory::recon_load_freqs());
|
||||
checkbox_load_repeaters.set_value(persistent_memory::recon_load_repeaters());
|
||||
@@ -148,6 +153,7 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
checkbox_repeat_amp.set_value(persistent_memory::recon_repeat_amp());
|
||||
field_repeat_nb.set_value(persistent_memory::recon_repeat_nb());
|
||||
field_repeat_gain.set_value(persistent_memory::recon_repeat_gain());
|
||||
field_repeat_delay.set_value(persistent_memory::recon_repeat_delay());
|
||||
|
||||
// tx warning modal
|
||||
checkbox_repeat_recorded.on_select = [this, &nav](Checkbox&, bool v) {
|
||||
|
||||
@@ -158,7 +158,7 @@ class ReconSetupViewMore : public View {
|
||||
"nb:"};
|
||||
|
||||
NumberField field_repeat_nb{
|
||||
{18 * 8, 165},
|
||||
{17 * 8, 165},
|
||||
2,
|
||||
{1, 99},
|
||||
1,
|
||||
@@ -171,16 +171,28 @@ class ReconSetupViewMore : public View {
|
||||
"AMP,"};
|
||||
|
||||
Text text_repeat_gain{
|
||||
{10 * 8, 196, 5 * 8, 22},
|
||||
{9 * 8, 196, 5 * 8, 22},
|
||||
"GAIN:"};
|
||||
|
||||
NumberField field_repeat_gain{
|
||||
{16 * 8, 196},
|
||||
{14 * 8, 196},
|
||||
2,
|
||||
{0, 47},
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
Text text_repeat_delay{
|
||||
{16 * 8, 196, 8 * 8, 22},
|
||||
", delay:"};
|
||||
|
||||
NumberField field_repeat_delay{
|
||||
{24 * 8, 196},
|
||||
3,
|
||||
{0, 254},
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
};
|
||||
|
||||
class ReconSetupView : public View {
|
||||
|
||||
@@ -275,7 +275,7 @@ ScannerView::~ScannerView() {
|
||||
}
|
||||
|
||||
void ScannerView::show_max_index() { // show total number of freqs to scan
|
||||
field_current_index.set_text("---");
|
||||
field_current_index.set_text("<->");
|
||||
|
||||
if (entries.size() == FREQMAN_MAX_PER_FILE) {
|
||||
text_max_index.set_style(&Styles::red);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_touch_calibration.hpp"
|
||||
#include "ui_text_editor.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
@@ -149,23 +150,10 @@ SetRadioView::SetRadioView(
|
||||
});
|
||||
}
|
||||
|
||||
std::string source_name("---");
|
||||
switch (reference.source) {
|
||||
case ClockManager::ReferenceSource::Xtal:
|
||||
source_name = "HackRF";
|
||||
break;
|
||||
case ClockManager::ReferenceSource::PortaPack:
|
||||
source_name = "PortaPack";
|
||||
break;
|
||||
case ClockManager::ReferenceSource::External:
|
||||
source_name = "External";
|
||||
break;
|
||||
}
|
||||
std::string source_name = clock_manager.get_source();
|
||||
|
||||
value_source.set(source_name);
|
||||
value_source_frequency.set(
|
||||
to_string_dec_uint(reference.frequency / 1000000, 2) + "." +
|
||||
to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz");
|
||||
value_source_frequency.set(clock_manager.get_freq());
|
||||
|
||||
// Make these Text controls look like Labels.
|
||||
label_source.set_style(&Styles::light_grey);
|
||||
@@ -641,6 +629,34 @@ void SetEncoderDialView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
/* AppSettingsView ************************************/
|
||||
|
||||
AppSettingsView::AppSettingsView(
|
||||
NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
add_children({&labels,
|
||||
&menu_view});
|
||||
|
||||
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
|
||||
|
||||
ensure_directory(SETTINGS_DIR);
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(SETTINGS_DIR, u"*.ini")) {
|
||||
auto path = (std::filesystem::path)SETTINGS_DIR / entry.path();
|
||||
|
||||
menu_view.add_item({path.filename().string().substr(0, 26),
|
||||
ui::Color::dark_cyan(),
|
||||
&bitmap_icon_file_text,
|
||||
[this, path](KeyEvent) {
|
||||
nav_.push<TextEditorView>(path);
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
void AppSettingsView::focus() {
|
||||
menu_view.focus();
|
||||
}
|
||||
|
||||
/* SettingsMenuView **************************************/
|
||||
|
||||
SettingsMenuView::SettingsMenuView(NavigationView& nav) {
|
||||
@@ -648,6 +664,7 @@ SettingsMenuView::SettingsMenuView(NavigationView& nav) {
|
||||
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
|
||||
}
|
||||
add_items({
|
||||
{"App Settings", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<AppSettingsView>(); }},
|
||||
{"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav]() { nav.push<SetAudioView>(); }},
|
||||
{"Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav]() { nav.push<TouchCalibrationView>(); }},
|
||||
{"Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetConverterSettingsView>(); }},
|
||||
|
||||
@@ -564,6 +564,23 @@ class SetPersistentMemoryView : public View {
|
||||
};
|
||||
};
|
||||
|
||||
class AppSettingsView : public View {
|
||||
public:
|
||||
AppSettingsView(NavigationView& nav);
|
||||
std::string title() const override { return "App Settings"; };
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
Labels labels{
|
||||
{{0, 4}, "Select file to edit:", Color::white()}};
|
||||
|
||||
MenuView menu_view{
|
||||
{0, 2 * 8, 240, 26 * 8},
|
||||
true};
|
||||
};
|
||||
|
||||
class SettingsMenuView : public BtnGridView {
|
||||
public:
|
||||
SettingsMenuView(NavigationView& nav);
|
||||
|
||||
@@ -90,13 +90,17 @@ SondeView::SondeView(NavigationView& nav)
|
||||
};
|
||||
|
||||
button_see_map.on_select = [this, &nav](Button&) {
|
||||
nav.push<GeoMapView>(
|
||||
geomap_view_ = nav.push<GeoMapView>(
|
||||
sonde_id,
|
||||
gps_info.alt,
|
||||
GeoPos::alt_unit::METERS,
|
||||
GeoPos::spd_unit::HIDDEN,
|
||||
gps_info.lat,
|
||||
gps_info.lon,
|
||||
999); // set a dummy heading out of range to draw a cross...probably not ideal?
|
||||
nav.set_on_pop([this]() {
|
||||
geomap_view_ = nullptr;
|
||||
});
|
||||
};
|
||||
|
||||
logger = std::make_unique<SondeLogger>();
|
||||
@@ -123,6 +127,17 @@ SondeView::~SondeView() {
|
||||
audio::output::stop();
|
||||
}
|
||||
|
||||
void SondeView::on_gps(const GPSPosDataMessage* msg) {
|
||||
if (!geomap_view_)
|
||||
return;
|
||||
geomap_view_->update_my_position(msg->lat, msg->lon, msg->altitude);
|
||||
}
|
||||
void SondeView::on_orientation(const OrientationDataMessage* msg) {
|
||||
if (!geomap_view_)
|
||||
return;
|
||||
geomap_view_->update_my_orientation(msg->angle, true);
|
||||
}
|
||||
|
||||
void SondeView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ class SondeView : public View {
|
||||
|
||||
SondeView(NavigationView& nav);
|
||||
~SondeView();
|
||||
SondeView(const SondeView& other) = delete;
|
||||
SondeView& operator=(const SondeView& other) = delete;
|
||||
|
||||
void focus() override;
|
||||
|
||||
@@ -162,7 +164,8 @@ class SondeView : public View {
|
||||
|
||||
GeoPos geopos{
|
||||
{0, 12 * 16},
|
||||
GeoPos::alt_unit::METERS};
|
||||
GeoPos::alt_unit::METERS,
|
||||
GeoPos::spd_unit::HIDDEN};
|
||||
|
||||
Button button_see_qr{
|
||||
{2 * 8, 15 * 16, 12 * 8, 3 * 16},
|
||||
@@ -172,6 +175,8 @@ class SondeView : public View {
|
||||
{16 * 8, 15 * 16, 12 * 8, 3 * 16},
|
||||
"See on map"};
|
||||
|
||||
GeoMapView* geomap_view_{nullptr};
|
||||
|
||||
MessageHandlerRegistration message_handler_packet{
|
||||
Message::ID::SondePacket,
|
||||
[this](Message* const p) {
|
||||
@@ -180,6 +185,21 @@ class SondeView : public View {
|
||||
this->on_packet(packet);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_gps{
|
||||
Message::ID::GPSPosData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const GPSPosDataMessage*>(p);
|
||||
this->on_gps(message);
|
||||
}};
|
||||
MessageHandlerRegistration message_handler_orientation{
|
||||
Message::ID::OrientationData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const OrientationDataMessage*>(p);
|
||||
this->on_orientation(message);
|
||||
}};
|
||||
|
||||
void on_gps(const GPSPosDataMessage* msg);
|
||||
void on_orientation(const OrientationDataMessage* msg);
|
||||
void on_packet(const sonde::Packet& packet);
|
||||
char* float_to_char(float x, char* p);
|
||||
};
|
||||
|
||||
@@ -217,6 +217,27 @@ ClockManager::Reference ClockManager::get_reference() const {
|
||||
return reference;
|
||||
}
|
||||
|
||||
std::string ClockManager::get_source() {
|
||||
std::string source_name("---");
|
||||
switch (reference.source) {
|
||||
case ClockManager::ReferenceSource::Xtal:
|
||||
source_name = "HackRF";
|
||||
break;
|
||||
case ClockManager::ReferenceSource::PortaPack:
|
||||
source_name = "PortaPack";
|
||||
break;
|
||||
case ClockManager::ReferenceSource::External:
|
||||
source_name = "External";
|
||||
break;
|
||||
}
|
||||
return source_name;
|
||||
}
|
||||
|
||||
std::string ClockManager::get_freq() {
|
||||
return to_string_dec_uint(reference.frequency / 1000000, 2) + "." +
|
||||
to_string_dec_uint((reference.frequency % 1000000) / 100, 4, '0') + " MHz";
|
||||
}
|
||||
|
||||
static void portapack_tcxo_enable() {
|
||||
portapack::io.reference_oscillator(true);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#ifndef __CLOCK_MANAGER_H__
|
||||
#define __CLOCK_MANAGER_H__
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
@@ -74,6 +75,8 @@ class ClockManager {
|
||||
uint32_t get_frequency_monitor_measurement_in_hertz();
|
||||
|
||||
Reference get_reference() const;
|
||||
std::string get_source();
|
||||
std::string get_freq();
|
||||
|
||||
void enable_clock_output(bool enable);
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ bool memory_dump(uint32_t* addr_start, uint32_t num_words, bool stack_flag) {
|
||||
int n{0};
|
||||
bool data_found{false};
|
||||
|
||||
make_new_directory(debug_dir);
|
||||
ensure_directory(debug_dir);
|
||||
filename = next_filename_matching_pattern(debug_dir + "/" + (stack_flag ? "STACK" : "MEMORY") + "_DUMP_????.TXT");
|
||||
error = filename.empty();
|
||||
if (!error)
|
||||
|
||||
@@ -113,7 +113,6 @@ void EventDispatcher::run() {
|
||||
while (is_running) {
|
||||
const auto events = wait();
|
||||
dispatch(events);
|
||||
portapack::usb_serial.dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +150,8 @@ void EventDispatcher::dispatch(const eventmask_t events) {
|
||||
*(uint32_t*)&shared_memory.bb_data.data[4]);
|
||||
}
|
||||
|
||||
handle_shell();
|
||||
|
||||
if (events & EVT_MASK_APPLICATION) {
|
||||
handle_application_queue();
|
||||
}
|
||||
@@ -163,6 +164,9 @@ void EventDispatcher::dispatch(const eventmask_t events) {
|
||||
handle_rtc_tick();
|
||||
}
|
||||
|
||||
handle_usb_transfer();
|
||||
handle_usb();
|
||||
|
||||
if (events & EVT_MASK_SWITCHES) {
|
||||
handle_switches();
|
||||
}
|
||||
@@ -216,6 +220,34 @@ void EventDispatcher::handle_rtc_tick() {
|
||||
portapack::persistent_memory::cache::persist();
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_usb() {
|
||||
portapack::usb_serial.dispatch();
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_usb_transfer() {
|
||||
portapack::usb_serial.dispatch_transfer();
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_shell() {
|
||||
if (waiting_for_shellmode) {
|
||||
waiting_for_shellmode = false;
|
||||
shellmode_active = true;
|
||||
while (shellmode_active) {
|
||||
chThdSleepMilliseconds(5);
|
||||
}
|
||||
}
|
||||
|
||||
if (injected_touch_event != nullptr) {
|
||||
on_touch_event(*injected_touch_event);
|
||||
injected_touch_event = nullptr;
|
||||
}
|
||||
|
||||
if (injected_keyboard_event != nullptr) {
|
||||
on_keyboard_event(*injected_keyboard_event);
|
||||
injected_keyboard_event = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent event) {
|
||||
if (!w->hidden()) {
|
||||
// To achieve reverse depth ordering (last object drawn is
|
||||
@@ -238,6 +270,28 @@ ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent ev
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EventDispatcher::emulateTouch(ui::TouchEvent event) {
|
||||
injected_touch_event = &event;
|
||||
while (injected_touch_event != nullptr) {
|
||||
chThdSleepMilliseconds(5);
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::emulateKeyboard(ui::KeyboardEvent event) {
|
||||
injected_keyboard_event = &event;
|
||||
while (injected_keyboard_event != nullptr) {
|
||||
chThdSleepMilliseconds(5);
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::on_keyboard_event(ui::KeyboardEvent event) {
|
||||
// send the key to focused widget, or parent if not accepts it
|
||||
auto target = context.focus_manager().focus_widget();
|
||||
while ((target != nullptr) && !target->on_keyboard(event)) {
|
||||
target = target->parent();
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::on_touch_event(ui::TouchEvent event) {
|
||||
/* TODO: Capture widget receiving the Start event, send Move and
|
||||
* End events to the same widget.
|
||||
@@ -258,7 +312,17 @@ void EventDispatcher::on_touch_event(ui::TouchEvent event) {
|
||||
}
|
||||
}
|
||||
|
||||
ui::Widget* EventDispatcher::getTopWidget() {
|
||||
return top_widget;
|
||||
}
|
||||
|
||||
ui::Widget* EventDispatcher::getFocusedWidget() {
|
||||
return context.focus_manager().focus_widget();
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_lcd_frame_sync() {
|
||||
bool waiting_for_frame = this->waiting_for_frame;
|
||||
|
||||
DisplayFrameSyncMessage message;
|
||||
message_map.send(&message);
|
||||
|
||||
@@ -266,6 +330,28 @@ void EventDispatcher::handle_lcd_frame_sync() {
|
||||
painter.paint_widget_tree(top_widget);
|
||||
|
||||
portapack::backlight()->on();
|
||||
|
||||
if (waiting_for_frame)
|
||||
this->waiting_for_frame = false;
|
||||
}
|
||||
|
||||
void EventDispatcher::wait_finish_frame() {
|
||||
waiting_for_frame = true;
|
||||
while (waiting_for_frame) {
|
||||
chThdSleepMilliseconds(5);
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::enter_shell_working_mode() {
|
||||
waiting_for_shellmode = true;
|
||||
|
||||
while (waiting_for_shellmode) {
|
||||
chThdSleepMilliseconds(5);
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::exit_shell_working_mode() {
|
||||
shellmode_active = false;
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_switches() {
|
||||
@@ -327,6 +413,9 @@ void EventDispatcher::handle_encoder() {
|
||||
|
||||
const uint32_t encoder_now = get_encoder_position();
|
||||
const int32_t delta = static_cast<int32_t>(encoder_now - encoder_last);
|
||||
if (delta == 0)
|
||||
return;
|
||||
|
||||
encoder_last = encoder_now;
|
||||
const auto event = static_cast<ui::EncoderEvent>(delta);
|
||||
event_bubble_encoder(event);
|
||||
|
||||
@@ -45,6 +45,7 @@ constexpr auto EVT_MASK_ENCODER = EVENT_MASK(4);
|
||||
constexpr auto EVT_MASK_TOUCH = EVENT_MASK(5);
|
||||
constexpr auto EVT_MASK_APPLICATION = EVENT_MASK(6);
|
||||
constexpr auto EVT_MASK_LOCAL = EVENT_MASK(7);
|
||||
constexpr auto EVT_MASK_USB = EVENT_MASK(8);
|
||||
|
||||
class EventDispatcher {
|
||||
public:
|
||||
@@ -86,6 +87,16 @@ class EventDispatcher {
|
||||
events_flag(EVT_MASK_LOCAL);
|
||||
}
|
||||
|
||||
void emulateTouch(ui::TouchEvent event);
|
||||
void emulateKeyboard(ui::KeyboardEvent event);
|
||||
|
||||
void wait_finish_frame();
|
||||
void enter_shell_working_mode();
|
||||
void exit_shell_working_mode();
|
||||
|
||||
ui::Widget* getTopWidget();
|
||||
ui::Widget* getFocusedWidget();
|
||||
|
||||
private:
|
||||
static Thread* thread_event_loop;
|
||||
|
||||
@@ -98,6 +109,11 @@ class EventDispatcher {
|
||||
bool sd_card_present = false;
|
||||
static bool display_sleep;
|
||||
bool in_key_event = false;
|
||||
volatile bool waiting_for_frame = false;
|
||||
volatile bool waiting_for_shellmode = false;
|
||||
volatile bool shellmode_active = false;
|
||||
ui::TouchEvent* volatile injected_touch_event = nullptr;
|
||||
ui::KeyboardEvent* volatile injected_keyboard_event = nullptr;
|
||||
|
||||
eventmask_t wait();
|
||||
void dispatch(const eventmask_t events);
|
||||
@@ -105,12 +121,16 @@ class EventDispatcher {
|
||||
void handle_application_queue();
|
||||
void handle_local_queue();
|
||||
void handle_rtc_tick();
|
||||
void handle_usb();
|
||||
void handle_usb_transfer();
|
||||
void handle_shell();
|
||||
|
||||
static ui::Widget* touch_widget(ui::Widget* const w, ui::TouchEvent event);
|
||||
|
||||
ui::Widget* captured_widget{nullptr};
|
||||
|
||||
void on_touch_event(ui::TouchEvent event);
|
||||
void on_keyboard_event(ui::KeyboardEvent event);
|
||||
|
||||
// void blink_timer();
|
||||
void handle_lcd_frame_sync();
|
||||
|
||||
+12
@@ -44,6 +44,16 @@ set(EXTCPPSRC
|
||||
#lcr
|
||||
external/jammer/main.cpp
|
||||
external/jammer/ui_jammer.cpp
|
||||
|
||||
#gpssim
|
||||
external/gpssim/main.cpp
|
||||
external/gpssim/gps_sim_app.cpp
|
||||
|
||||
#spainter
|
||||
external/spainter/main.cpp
|
||||
external/spainter/ui_spectrum_painter.cpp
|
||||
external/spainter/ui_spectrum_painter_text.cpp
|
||||
external/spainter/ui_spectrum_painter_image.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -58,4 +68,6 @@ set(EXTAPPLIST
|
||||
lge
|
||||
lcr
|
||||
jammer
|
||||
gpssim
|
||||
spainter
|
||||
)
|
||||
|
||||
+16
-2
@@ -28,6 +28,8 @@ MEMORY
|
||||
ram_external_app_lge(rwx) : org = 0xEEF10000, len = 32k
|
||||
ram_external_app_lcr(rwx) : org = 0xEEF20000, len = 32k
|
||||
ram_external_app_jammer(rwx) : org = 0xEEF30000, len = 32k
|
||||
ram_external_app_gpssim(rwx) : org = 0xEEF40000, len = 32k
|
||||
ram_external_app_spainter(rwx) : org = 0xEEF50000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -88,18 +90,30 @@ SECTIONS
|
||||
} > ram_external_app_lge
|
||||
|
||||
|
||||
.external_app_lcr : ALIGN(4) SUBALIGN(4)
|
||||
.external_app_lcr : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_lcr.application_information));
|
||||
*(*ui*external_app*lcr*);
|
||||
} > ram_external_app_lcr
|
||||
|
||||
|
||||
.external_app_jammer : ALIGN(4) SUBALIGN(4)
|
||||
.external_app_jammer : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_jammer.application_information));
|
||||
*(*ui*external_app*jammer*);
|
||||
} > ram_external_app_jammer
|
||||
|
||||
.external_app_gpssim : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_gpssim.application_information));
|
||||
*(*ui*external_app*gpssim*);
|
||||
} > ram_external_app_gpssim
|
||||
|
||||
|
||||
.external_app_spainter : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_spainter.application_information));
|
||||
*(*ui*external_app*spainter*);
|
||||
} > ram_external_app_spainter
|
||||
|
||||
}
|
||||
|
||||
Vendored
+4
-3
@@ -36,7 +36,7 @@
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::gpssim {
|
||||
|
||||
void GpsSimAppView::set_ready() {
|
||||
ready_signal = true;
|
||||
@@ -161,7 +161,8 @@ void GpsSimAppView::handle_replay_thread_done(const uint32_t return_code) {
|
||||
GpsSimAppView::GpsSimAppView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_gps);
|
||||
// baseband::run_image(portapack::spi_flash::image_tag_gps);
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({
|
||||
&button_open,
|
||||
@@ -211,4 +212,4 @@ void GpsSimAppView::set_parent_rect(const Rect new_parent_rect) {
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui::external_app::gpssim */
|
||||
Vendored
+4
-3
@@ -25,6 +25,7 @@
|
||||
#define __GPS_SIM_APP_HPP__
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -37,7 +38,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::gpssim {
|
||||
|
||||
class GpsSimAppView : public View {
|
||||
public:
|
||||
@@ -108,7 +109,7 @@ class GpsSimAppView : public View {
|
||||
Checkbox check_loop{
|
||||
{21 * 8, 2 * 16},
|
||||
4,
|
||||
"Loop",
|
||||
LanguageHelper::currentMessages[LANG_LOOP],
|
||||
true};
|
||||
ImageButton button_play{
|
||||
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
|
||||
@@ -142,6 +143,6 @@ class GpsSimAppView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui::external_app::gpssim */
|
||||
|
||||
#endif /*__GPS_SIM_APP_HPP__*/
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 "gps_sim_app.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::gpssim {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<GpsSimAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::gpssim
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_gpssim.application_information"), used)) application_information_t _application_information_gpssim = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::gpssim::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "GPSSim",
|
||||
/*.bitmap_data = */ {
|
||||
0xC0,
|
||||
0x07,
|
||||
0xE0,
|
||||
0x0F,
|
||||
0x70,
|
||||
0x1F,
|
||||
0x78,
|
||||
0x3E,
|
||||
0x78,
|
||||
0x3C,
|
||||
0x78,
|
||||
0x38,
|
||||
0x78,
|
||||
0x30,
|
||||
0x78,
|
||||
0x38,
|
||||
0x78,
|
||||
0x3C,
|
||||
0x70,
|
||||
0x1E,
|
||||
0x70,
|
||||
0x1F,
|
||||
0xE0,
|
||||
0x0F,
|
||||
0xC0,
|
||||
0x07,
|
||||
0x80,
|
||||
0x03,
|
||||
0x20,
|
||||
0x09,
|
||||
0x50,
|
||||
0x14,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_gpssim */ {'P', 'G', 'P', 'S'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+21
-7
@@ -205,11 +205,13 @@ class Sprite {
|
||||
byte who;
|
||||
byte _speed;
|
||||
byte dir;
|
||||
byte lastDir;
|
||||
byte phase;
|
||||
|
||||
// Sprite bits
|
||||
byte palette2; // 4->16 color map index
|
||||
byte bits; // index of sprite bits
|
||||
|
||||
signed char sy;
|
||||
|
||||
void Init(const byte* s) {
|
||||
@@ -274,9 +276,17 @@ class Sprite {
|
||||
f = pgm_read_byte(_pacLeftAnim + f);
|
||||
else if (dir == MRight)
|
||||
f = pgm_read_byte(_pacRightAnim + f);
|
||||
else
|
||||
else if (dir == MDown || dir == MUp)
|
||||
f = pgm_read_byte(_pacVAnim + f);
|
||||
if (dir == MUp)
|
||||
else if (dir == MStopped) {
|
||||
if (lastDir == MLeft)
|
||||
f = pgm_read_byte(_pacLeftAnim + f);
|
||||
else if (lastDir == MRight)
|
||||
f = pgm_read_byte(_pacRightAnim + f);
|
||||
else if (lastDir == MDown || lastDir == MUp)
|
||||
f = pgm_read_byte(_pacVAnim + f);
|
||||
}
|
||||
if ((dir == MUp) || (dir == MStopped && lastDir == MUp))
|
||||
sy = -1;
|
||||
bits = f + PACMANSPRITE;
|
||||
}
|
||||
@@ -838,15 +848,19 @@ class Playfield {
|
||||
else if (DEMO == 0 && s->who == PACMAN && choice[3] < 0x7FFF && but_RIGHT)
|
||||
dir = MRight;
|
||||
|
||||
else if (DEMO == 0 && choice[0] < 0x7FFF && s->who == PACMAN && dir == MUp)
|
||||
else if (DEMO == 0 && choice[0] < 0x7FFF && s->who == PACMAN && dir == MUp) {
|
||||
dir = MUp;
|
||||
else if (DEMO == 0 && choice[1] < 0x7FFF && s->who == PACMAN && dir == MLeft)
|
||||
s->lastDir = MUp;
|
||||
} else if (DEMO == 0 && choice[1] < 0x7FFF && s->who == PACMAN && dir == MLeft) {
|
||||
dir = MLeft;
|
||||
else if (DEMO == 0 && choice[2] < 0x7FFF && s->who == PACMAN && dir == MDown)
|
||||
s->lastDir = MLeft;
|
||||
} else if (DEMO == 0 && choice[2] < 0x7FFF && s->who == PACMAN && dir == MDown) {
|
||||
dir = MDown;
|
||||
else if (DEMO == 0 && choice[3] < 0x7FFF && s->who == PACMAN && dir == MRight)
|
||||
s->lastDir = MDown;
|
||||
} else if (DEMO == 0 && choice[3] < 0x7FFF && s->who == PACMAN && dir == MRight) {
|
||||
dir = MRight;
|
||||
else if ((DEMO == 0 && s->who != PACMAN) || DEMO == 1) {
|
||||
s->lastDir = MRight;
|
||||
} else if ((DEMO == 0 && s->who != PACMAN) || DEMO == 1) {
|
||||
// Don't choose opposite of current direction?
|
||||
|
||||
int16_t dist = choice[4 - dir]; // favor current direction
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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_spectrum_painter.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<SpectrumPainterView>();
|
||||
}
|
||||
} // namespace ui::external_app::spainter
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_spainter.application_information"), used)) application_information_t _application_information_spainter = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::spainter::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "S.Painter",
|
||||
/*.bitmap_data = */ {
|
||||
0xFE,
|
||||
0x3F,
|
||||
0xFF,
|
||||
0x3F,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xBF,
|
||||
0xFE,
|
||||
0xBF,
|
||||
0x00,
|
||||
0x80,
|
||||
0x80,
|
||||
0xFF,
|
||||
0x80,
|
||||
0x00,
|
||||
0x80,
|
||||
0x00,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_spainter */ {'P', 'S', 'P', 'T'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+4
-3
@@ -31,12 +31,13 @@
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
SpectrumPainterView::SpectrumPainterView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
baseband::run_image(spi_flash::image_tag_spectrum_painter);
|
||||
// baseband::run_image(spi_flash::image_tag_spectrum_painter);
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
@@ -198,4 +199,4 @@ void SpectrumPainterView::paint(Painter& painter) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+4
-3
@@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -37,7 +38,7 @@
|
||||
#include "ui_spectrum_painter_image.hpp"
|
||||
#include "ui_spectrum_painter_text.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
class SpectrumPainterView : public View {
|
||||
public:
|
||||
@@ -115,7 +116,7 @@ class SpectrumPainterView : public View {
|
||||
Checkbox check_loop{
|
||||
{21 * 8, footer_location + 1 * 16},
|
||||
4,
|
||||
"Loop",
|
||||
LanguageHelper::currentMessages[LANG_LOOP],
|
||||
true};
|
||||
|
||||
ImageButton button_play{
|
||||
@@ -163,4 +164,4 @@ class SpectrumPainterView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+2
-2
@@ -29,7 +29,7 @@
|
||||
#include "file.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
|
||||
hidden(true);
|
||||
@@ -254,4 +254,4 @@ void SpectrumInputImageView::paint(Painter& painter) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+2
-2
@@ -32,7 +32,7 @@
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
class SpectrumInputImageView : public View {
|
||||
public:
|
||||
@@ -63,4 +63,4 @@ class SpectrumInputImageView : public View {
|
||||
bool drawBMP_scaled(const ui::Rect r, const std::string file);
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+2
-2
@@ -30,7 +30,7 @@
|
||||
#include "file.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
SpectrumInputTextView::SpectrumInputTextView(NavigationView& nav) {
|
||||
hidden(true);
|
||||
@@ -105,4 +105,4 @@ std::vector<uint8_t> SpectrumInputTextView::get_line(uint16_t y) {
|
||||
|
||||
return data;
|
||||
}
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+2
-2
@@ -32,7 +32,7 @@
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
class SpectrumInputTextView : public View {
|
||||
public:
|
||||
@@ -109,4 +109,4 @@ class SpectrumInputTextView : public View {
|
||||
"Set message"};
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
@@ -171,12 +171,13 @@ static bool encoder_update(const uint8_t raw) {
|
||||
}
|
||||
|
||||
static bool encoder_read() {
|
||||
const auto delta = encoder.update(
|
||||
encoder_debounce[0].state() | (injected_encoder == 1),
|
||||
encoder_debounce[1].state() | (injected_encoder == 2));
|
||||
auto delta = encoder.update(encoder_debounce[0].state(), encoder_debounce[1].state());
|
||||
|
||||
if (injected_encoder > 0)
|
||||
if (injected_encoder > 0) {
|
||||
if (injected_encoder == 1) delta = -1;
|
||||
if (injected_encoder == 2) delta = 1;
|
||||
injected_encoder = 0;
|
||||
}
|
||||
|
||||
if (delta != 0) {
|
||||
encoder_position += delta;
|
||||
|
||||
@@ -157,7 +157,7 @@ static void event_loop() {
|
||||
[&event_dispatcher](const Message* const) {
|
||||
event_dispatcher.set_display_sleep(true);
|
||||
}};
|
||||
|
||||
portapack::setEventDispatcherToUSBSerial(&event_dispatcher);
|
||||
event_dispatcher.run();
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ enum class PortaPackModel {
|
||||
static bool save_config(int8_t value) {
|
||||
persistent_memory::set_config_cpld(value);
|
||||
if (sd_card::status() == sd_card::Status::Mounted) {
|
||||
make_new_directory("/hardware");
|
||||
ensure_directory("/hardware");
|
||||
File file;
|
||||
auto sucess = file.create("/hardware/settings.txt");
|
||||
if (!sucess.is_valid()) {
|
||||
@@ -542,4 +542,8 @@ void shutdown(const bool leave_screen_on) {
|
||||
shutdown_base();
|
||||
}
|
||||
|
||||
void setEventDispatcherToUSBSerial(EventDispatcher* evt) {
|
||||
usb_serial.setEventDispatcher(evt);
|
||||
}
|
||||
|
||||
} /* namespace portapack */
|
||||
|
||||
@@ -68,6 +68,8 @@ bool get_antenna_bias();
|
||||
bool init();
|
||||
void shutdown(const bool leave_screen_on = false);
|
||||
|
||||
void setEventDispatcherToUSBSerial(EventDispatcher* evt);
|
||||
|
||||
Backlight* backlight();
|
||||
|
||||
} /* namespace portapack */
|
||||
|
||||
@@ -116,16 +116,8 @@ void set_direction(const rf::Direction new_direction) {
|
||||
/* TODO: Refactor all the various "Direction" enumerations into one. */
|
||||
/* TODO: Only make changes if direction changes, but beware of clock enabling. */
|
||||
|
||||
// Hack to fix the CPLD (clocking ?) bug: toggle CPLD SRAM overlay depending on new direction.
|
||||
// Use CPLD's EEPROM config when transmitting
|
||||
// Use the SRAM overlay when receiving
|
||||
if (direction != new_direction) {
|
||||
if (new_direction == rf::Direction::Transmit)
|
||||
hackrf::cpld::init_from_eeprom();
|
||||
else
|
||||
// Prevents ghosting when switching back to RX from TX mode.
|
||||
hackrf::cpld::load_sram_no_verify();
|
||||
}
|
||||
// Prevents ghosting when switching back to RX from TX mode.
|
||||
hackrf::cpld::load_sram_no_verify();
|
||||
|
||||
direction = new_direction;
|
||||
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file shell.c
|
||||
* @brief Simple CLI shell code.
|
||||
*
|
||||
* @addtogroup SHELL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include "shell.hpp"
|
||||
#include "chprintf.h"
|
||||
#include "portapack.hpp"
|
||||
|
||||
/**
|
||||
* @brief Shell termination event source.
|
||||
*/
|
||||
EventSource shell_terminated;
|
||||
|
||||
static char* _strtok(char* str, const char* delim, char** saveptr) {
|
||||
char* token;
|
||||
if (str)
|
||||
*saveptr = str;
|
||||
token = *saveptr;
|
||||
|
||||
if (!token)
|
||||
return NULL;
|
||||
|
||||
token += strspn(token, delim);
|
||||
*saveptr = strpbrk(token, delim);
|
||||
if (*saveptr)
|
||||
*(*saveptr)++ = '\0';
|
||||
|
||||
return *token ? token : NULL;
|
||||
}
|
||||
|
||||
static void usage(BaseSequentialStream* chp, char* p) {
|
||||
chprintf(chp, "Usage: %s\r\n", p);
|
||||
}
|
||||
|
||||
static void list_commands(BaseSequentialStream* chp, const ShellCommand* scp) {
|
||||
while (scp->sc_name != NULL) {
|
||||
chprintf(chp, "%s ", scp->sc_name);
|
||||
scp++;
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_info(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
if (argc > 0) {
|
||||
usage(chp, const_cast<char*>("info"));
|
||||
return;
|
||||
}
|
||||
|
||||
chprintf(chp, "Kernel: %s\r\n", CH_KERNEL_VERSION);
|
||||
#ifdef CH_COMPILER_NAME
|
||||
chprintf(chp, "Compiler: %s\r\n", CH_COMPILER_NAME);
|
||||
#endif
|
||||
chprintf(chp, "Architecture: %s\r\n", CH_ARCHITECTURE_NAME);
|
||||
#ifdef CH_CORE_VARIANT_NAME
|
||||
chprintf(chp, "Core Variant: %s\r\n", CH_CORE_VARIANT_NAME);
|
||||
#endif
|
||||
#ifdef CH_PORT_INFO
|
||||
chprintf(chp, "Port Info: %s\r\n", CH_PORT_INFO);
|
||||
#endif
|
||||
#ifdef PLATFORM_NAME
|
||||
chprintf(chp, "Platform: %s\r\n", PLATFORM_NAME);
|
||||
#endif
|
||||
#ifdef BOARD_NAME
|
||||
chprintf(chp, "Board: %s\r\n", BOARD_NAME);
|
||||
#endif
|
||||
#ifdef VERSION_STRING
|
||||
chprintf(chp, "Mayhem Version: %s\r\n", VERSION_STRING);
|
||||
#endif
|
||||
chprintf(chp, "HackRF Board Rev: %s\r\n", hackrf_r9 ? "R9" : "R1-R8");
|
||||
chprintf(chp, "Reference Source: %s\r\n", portapack::clock_manager.get_source().c_str());
|
||||
chprintf(chp, "Reference Freq: %s\r\n", portapack::clock_manager.get_freq().c_str());
|
||||
#ifdef __DATE__
|
||||
#ifdef __TIME__
|
||||
chprintf(chp, "Build time: %s%s%s\r\n", __DATE__, " - ", __TIME__);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cmd_systime(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
if (argc > 0) {
|
||||
usage(chp, const_cast<char*>("systime"));
|
||||
return;
|
||||
}
|
||||
chprintf(chp, "%lu\r\n", (unsigned long)chTimeNow());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Array of the default commands.
|
||||
*/
|
||||
static ShellCommand local_commands[] = {
|
||||
{"info", cmd_info},
|
||||
{"systime", cmd_systime},
|
||||
{NULL, NULL}};
|
||||
|
||||
static bool_t cmdexec(const ShellCommand* scp, BaseSequentialStream* chp, char* name, int argc, char* argv[]) {
|
||||
while (scp->sc_name != NULL) {
|
||||
if (strcmp(scp->sc_name, name) == 0) {
|
||||
scp->sc_function(chp, argc, argv);
|
||||
return FALSE;
|
||||
}
|
||||
scp++;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell thread function.
|
||||
*
|
||||
* @param[in] p pointer to a @p BaseSequentialStream object
|
||||
* @return Termination reason.
|
||||
* @retval RDY_OK terminated by command.
|
||||
* @retval RDY_RESET terminated by reset condition on the I/O channel.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static msg_t shell_thread(void* p) {
|
||||
int n;
|
||||
BaseSequentialStream* chp = ((ShellConfig*)p)->sc_channel;
|
||||
const ShellCommand* scp = ((ShellConfig*)p)->sc_commands;
|
||||
char *lp, *cmd, *tokp, line[SHELL_MAX_LINE_LENGTH];
|
||||
char* args[SHELL_MAX_ARGUMENTS + 1];
|
||||
|
||||
chRegSetThreadName("shell");
|
||||
chprintf(chp, "\r\nChibiOS/RT Shell\r\n");
|
||||
while (TRUE) {
|
||||
chprintf(chp, "ch> ");
|
||||
if (shellGetLine(chp, line, sizeof(line))) {
|
||||
chprintf(chp, "\r\nlogout");
|
||||
break;
|
||||
}
|
||||
lp = _strtok(line, " \t", &tokp);
|
||||
cmd = lp;
|
||||
n = 0;
|
||||
while ((lp = _strtok(NULL, " \t", &tokp)) != NULL) {
|
||||
if (n >= SHELL_MAX_ARGUMENTS) {
|
||||
chprintf(chp, "too many arguments\r\n");
|
||||
cmd = NULL;
|
||||
break;
|
||||
}
|
||||
args[n++] = lp;
|
||||
}
|
||||
args[n] = NULL;
|
||||
if (cmd != NULL) {
|
||||
if (strcmp(cmd, "exit") == 0) {
|
||||
if (n > 0) {
|
||||
usage(chp, const_cast<char*>("exit"));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
} else if (strcmp(cmd, "help") == 0) {
|
||||
if (n > 0) {
|
||||
usage(chp, const_cast<char*>("help"));
|
||||
continue;
|
||||
}
|
||||
chprintf(chp, "Commands: help exit ");
|
||||
list_commands(chp, local_commands);
|
||||
if (scp != NULL)
|
||||
list_commands(chp, scp);
|
||||
chprintf(chp, "\r\n");
|
||||
} else if (cmdexec(local_commands, chp, cmd, n, args) &&
|
||||
((scp == NULL) || cmdexec(scp, chp, cmd, n, args))) {
|
||||
chprintf(chp, "%s", cmd);
|
||||
chprintf(chp, " ?\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
shellExit(RDY_OK);
|
||||
/* Never executed, silencing a warning.*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell manager initialization.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void shellInit(void) {
|
||||
chEvtInit(&shell_terminated);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Terminates the shell.
|
||||
* @note Must be invoked from the command handlers.
|
||||
* @note Does not return.
|
||||
*
|
||||
* @param[in] msg shell exit code
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void shellExit(msg_t msg) {
|
||||
/* Atomically broadcasting the event source and terminating the thread,
|
||||
there is not a chSysUnlock() because the thread terminates upon return.*/
|
||||
chSysLock();
|
||||
chEvtBroadcastI(&shell_terminated);
|
||||
chThdExitS(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Spawns a new shell.
|
||||
* @pre @p CH_USE_HEAP and @p CH_USE_DYNAMIC must be enabled.
|
||||
*
|
||||
* @param[in] scp pointer to a @p ShellConfig object
|
||||
* @param[in] size size of the shell working area to be allocated
|
||||
* @param[in] prio priority level for the new shell
|
||||
* @return A pointer to the shell thread.
|
||||
* @retval NULL thread creation failed because memory allocation.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
#if CH_USE_HEAP && CH_USE_DYNAMIC
|
||||
Thread* shellCreate(const ShellConfig* scp, size_t size, tprio_t prio) {
|
||||
return chThdCreateFromHeap(NULL, size, prio, shell_thread, (void*)scp);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create statically allocated shell thread.
|
||||
*
|
||||
* @param[in] scp pointer to a @p ShellConfig object
|
||||
* @param[in] wsp pointer to a working area dedicated to the shell thread stack
|
||||
* @param[in] size size of the shell working area
|
||||
* @param[in] prio priority level for the new shell
|
||||
* @return A pointer to the shell thread.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
Thread* shellCreateStatic(const ShellConfig* scp, void* wsp, size_t size, tprio_t prio) {
|
||||
return chThdCreateStatic(wsp, size, prio, shell_thread, (void*)scp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads a whole line from the input channel.
|
||||
*
|
||||
* @param[in] chp pointer to a @p BaseSequentialStream object
|
||||
* @param[in] line pointer to the line buffer
|
||||
* @param[in] size buffer maximum length
|
||||
* @return The operation status.
|
||||
* @retval TRUE the channel was reset or CTRL-D pressed.
|
||||
* @retval FALSE operation successful.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t shellGetLine(BaseSequentialStream* chp, char* line, unsigned size) {
|
||||
char* p = line;
|
||||
|
||||
while (TRUE) {
|
||||
char c;
|
||||
|
||||
if (chSequentialStreamRead(chp, (uint8_t*)&c, 1) == 0)
|
||||
return TRUE;
|
||||
if (c == 4) {
|
||||
chprintf(chp, "^D");
|
||||
return TRUE;
|
||||
}
|
||||
if ((c == 8) || (c == 127)) {
|
||||
if (p != line) {
|
||||
chSequentialStreamPut(chp, c);
|
||||
chSequentialStreamPut(chp, 0x20);
|
||||
chSequentialStreamPut(chp, c);
|
||||
p--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c == '\r') {
|
||||
chprintf(chp, "\r\n");
|
||||
*p = 0;
|
||||
return FALSE;
|
||||
}
|
||||
if (c < 0x20)
|
||||
continue;
|
||||
if (p < line + size - 1) {
|
||||
chSequentialStreamPut(chp, c);
|
||||
*p++ = (char)c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file shell.h
|
||||
* @brief Simple CLI shell header.
|
||||
*
|
||||
* @addtogroup SHELL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _SHELL_H_
|
||||
#define _SHELL_H_
|
||||
|
||||
/**
|
||||
* @brief Shell maximum input line length.
|
||||
*/
|
||||
#if !defined(SHELL_MAX_LINE_LENGTH) || defined(__DOXYGEN__)
|
||||
#define SHELL_MAX_LINE_LENGTH 64
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Shell maximum arguments per command.
|
||||
*/
|
||||
#if !defined(SHELL_MAX_ARGUMENTS) || defined(__DOXYGEN__)
|
||||
#define SHELL_MAX_ARGUMENTS 6
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Command handler function type.
|
||||
*/
|
||||
typedef void (*shellcmd_t)(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
|
||||
/**
|
||||
* @brief Custom command entry type.
|
||||
*/
|
||||
typedef struct {
|
||||
const char* sc_name; /**< @brief Command name. */
|
||||
shellcmd_t sc_function; /**< @brief Command function. */
|
||||
} ShellCommand;
|
||||
|
||||
/**
|
||||
* @brief Shell descriptor type.
|
||||
*/
|
||||
typedef struct {
|
||||
BaseSequentialStream* sc_channel; /**< @brief I/O channel associated
|
||||
to the shell. */
|
||||
const ShellCommand* sc_commands; /**< @brief Shell extra commands
|
||||
table. */
|
||||
} ShellConfig;
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
extern EventSource shell_terminated;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void shellInit(void);
|
||||
void shellExit(msg_t msg);
|
||||
Thread* shellCreate(const ShellConfig* scp, size_t size, tprio_t prio);
|
||||
Thread* shellCreateStatic(const ShellConfig* scp, void* wsp, size_t size, tprio_t prio);
|
||||
bool_t shellGetLine(BaseSequentialStream* chp, char* line, unsigned size);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SHELL_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -92,6 +92,28 @@ void FreqManUIList::on_blur() {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
bool FreqManUIList::on_keyboard(const KeyboardEvent key) {
|
||||
if (!db_ || db_->empty())
|
||||
return false;
|
||||
|
||||
auto delta = 0;
|
||||
if (key == '-' && get_index() > 0) delta = -1;
|
||||
if (key == '+' && get_index() < db_->entry_count() - 1) delta = 1;
|
||||
if (delta != 0) {
|
||||
adjust_selected_index(delta);
|
||||
set_dirty();
|
||||
return true;
|
||||
}
|
||||
if (key == 10) {
|
||||
if (on_select) {
|
||||
on_select(get_index());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FreqManUIList::on_key(const KeyEvent key) {
|
||||
if (!db_ || db_->empty())
|
||||
return false;
|
||||
|
||||
@@ -50,6 +50,8 @@ class FreqManUIList : public Widget {
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_encoder(EncoderEvent delta) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
void set_parent_rect(Rect new_parent_rect) override;
|
||||
|
||||
void set_index(size_t index);
|
||||
|
||||
@@ -38,13 +38,17 @@ namespace ui {
|
||||
|
||||
GeoPos::GeoPos(
|
||||
const Point pos,
|
||||
const alt_unit altitude_unit)
|
||||
: altitude_unit_(altitude_unit) {
|
||||
const alt_unit altitude_unit,
|
||||
const spd_unit speed_unit)
|
||||
: altitude_unit_(altitude_unit), speed_unit_(speed_unit) {
|
||||
set_parent_rect({pos, {30 * 8, 3 * 16}});
|
||||
|
||||
add_children({&labels_position,
|
||||
&label_spd_position,
|
||||
&field_altitude,
|
||||
&field_speed,
|
||||
&text_alt_unit,
|
||||
&text_speed_unit,
|
||||
&field_lat_degrees,
|
||||
&field_lat_minutes,
|
||||
&field_lat_seconds,
|
||||
@@ -56,6 +60,7 @@ GeoPos::GeoPos(
|
||||
|
||||
// Defaults
|
||||
set_altitude(0);
|
||||
set_speed(0);
|
||||
set_lat(0);
|
||||
set_lon(0);
|
||||
|
||||
@@ -67,10 +72,11 @@ GeoPos::GeoPos(
|
||||
text_lon_decimal.set(to_string_decimal(lon_value, 5));
|
||||
|
||||
if (on_change && report_change)
|
||||
on_change(altitude(), lat_value, lon_value);
|
||||
on_change(altitude(), lat_value, lon_value, speed());
|
||||
};
|
||||
|
||||
field_altitude.on_change = changed_fn;
|
||||
field_speed.on_change = changed_fn;
|
||||
field_lat_degrees.on_change = changed_fn;
|
||||
field_lat_minutes.on_change = changed_fn;
|
||||
field_lat_seconds.on_change = changed_fn;
|
||||
@@ -79,11 +85,19 @@ GeoPos::GeoPos(
|
||||
field_lon_seconds.on_change = changed_fn;
|
||||
|
||||
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
|
||||
if (speed_unit_ == KMPH) text_speed_unit.set("kmph");
|
||||
if (speed_unit_ == MPH) text_speed_unit.set("mph");
|
||||
if (speed_unit_ == HIDDEN) {
|
||||
text_speed_unit.hidden(true);
|
||||
label_spd_position.hidden(true);
|
||||
field_speed.hidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
void GeoPos::set_read_only(bool v) {
|
||||
// only setting altitude to read-only (allow manual panning via lat/lon fields)
|
||||
field_altitude.set_focusable(!v);
|
||||
field_speed.set_focusable(!v);
|
||||
}
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
@@ -98,14 +112,18 @@ void GeoPos::focus() {
|
||||
field_lat_degrees.focus();
|
||||
}
|
||||
|
||||
void GeoPos::hide_altitude() {
|
||||
void GeoPos::hide_altandspeed() {
|
||||
// Color altitude grey to indicate it's not updated in manual panning mode
|
||||
field_altitude.set_style(&Styles::grey);
|
||||
field_speed.set_style(&Styles::grey);
|
||||
}
|
||||
|
||||
void GeoPos::set_altitude(int32_t altitude) {
|
||||
field_altitude.set_value(altitude);
|
||||
}
|
||||
void GeoPos::set_speed(int32_t speed) {
|
||||
field_speed.set_value(speed);
|
||||
}
|
||||
|
||||
void GeoPos::set_lat(float lat) {
|
||||
field_lat_degrees.set_value(lat);
|
||||
@@ -139,21 +157,38 @@ int32_t GeoPos::altitude() {
|
||||
return field_altitude.value();
|
||||
};
|
||||
|
||||
int32_t GeoPos::speed() {
|
||||
return field_speed.value();
|
||||
};
|
||||
|
||||
GeoMap::GeoMap(
|
||||
Rect parent_rect)
|
||||
: Widget{parent_rect}, markerListLen(0) {
|
||||
}
|
||||
|
||||
bool GeoMap::on_encoder(const EncoderEvent delta) {
|
||||
if ((delta > 0) && (map_zoom < 5)) {
|
||||
map_zoom++;
|
||||
// Valid map_zoom values are -2 to -MAX_MAP_ZOOM_OUT, and +1 to +MAX_MAP_ZOOM_IN (values of 0 and -1 are not permitted)
|
||||
if (delta > 0) {
|
||||
if (map_zoom < MAX_MAP_ZOOM_IN) {
|
||||
if (map_zoom == -2) {
|
||||
map_zoom = 1;
|
||||
} else {
|
||||
map_zoom++;
|
||||
|
||||
// Ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function
|
||||
if (geomap_rect_width % map_zoom != 0) {
|
||||
map_zoom--;
|
||||
// When zooming in, ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function
|
||||
if ((map_zoom > 1) && (geomap_rect_width % map_zoom != 0)) {
|
||||
map_zoom--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (delta < 0) {
|
||||
if (map_zoom > -MAX_MAP_ZOOM_OUT) {
|
||||
if (map_zoom == 1) {
|
||||
map_zoom = -2;
|
||||
} else {
|
||||
map_zoom--;
|
||||
}
|
||||
}
|
||||
} else if ((delta < 0) && (map_zoom > 1)) {
|
||||
map_zoom--;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -164,17 +199,31 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GeoMap::map_zoom_line(ui::Color* buffer) {
|
||||
if (map_zoom <= 1) {
|
||||
return;
|
||||
}
|
||||
void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) {
|
||||
if (map_zoom == 1) {
|
||||
map_file.read(buffer, pixels << 1);
|
||||
} else if (map_zoom > 1) {
|
||||
map_file.read(buffer, (pixels / map_zoom) << 1);
|
||||
|
||||
// As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel;
|
||||
// For 240 width, than means no check is needed for map_zoom values up to 6
|
||||
for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) {
|
||||
for (int j = 0; j < map_zoom; j++) {
|
||||
buffer[(i * map_zoom) + j] = buffer[i];
|
||||
// Zoom in: Expand each pixel to "map_zoom" number of pixels.
|
||||
// Future TODO: Add dithering to smooth out the pixelation.
|
||||
// As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel;
|
||||
// For 240 width, than means no check is needed for map_zoom values up to 6
|
||||
for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) {
|
||||
for (int j = 0; j < map_zoom; j++) {
|
||||
buffer[(i * map_zoom) + j] = buffer[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ui::Color* zoom_out_buffer = new ui::Color[(pixels * (-map_zoom))];
|
||||
map_file.read(zoom_out_buffer, (pixels * (-map_zoom)) << 1);
|
||||
|
||||
// Zoom out: Collapse each group of "-map_zoom" pixels into one pixel.
|
||||
// Future TODO: Average each group of pixels (in both X & Y directions if possible).
|
||||
for (int i = 0; i < geomap_rect_width; i++) {
|
||||
buffer[i] = zoom_out_buffer[i * (-map_zoom)];
|
||||
}
|
||||
delete zoom_out_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,9 +236,12 @@ void GeoMap::draw_markers(Painter& painter) {
|
||||
int x = (map_width * (item.lon + 180) / 360) - x_pos;
|
||||
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
|
||||
|
||||
if (map_zoom != 1) {
|
||||
if (map_zoom > 1) {
|
||||
x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 2);
|
||||
y = ((y - (r.height() / 2)) * map_zoom) + (r.height() / 2);
|
||||
} else if (map_zoom < 0) {
|
||||
x = ((x - (r.width() / 2)) / (-map_zoom)) + (r.width() / 2);
|
||||
y = ((y - (r.height() / 2)) / (-map_zoom)) + (r.height() / 2);
|
||||
}
|
||||
|
||||
if ((x >= 0) && (x < r.width()) &&
|
||||
@@ -208,31 +260,39 @@ void GeoMap::draw_markers(Painter& painter) {
|
||||
}
|
||||
|
||||
void GeoMap::paint(Painter& painter) {
|
||||
uint16_t line, j;
|
||||
uint32_t zoom_seek_x, zoom_seek_y;
|
||||
std::array<ui::Color, 240> map_line_buffer;
|
||||
const auto r = screen_rect();
|
||||
std::array<ui::Color, 240> map_line_buffer;
|
||||
|
||||
// Ony redraw map if it moved by at least 1 pixel
|
||||
// or the markers list was updated
|
||||
int x_diff = abs(x_pos - prev_x_pos);
|
||||
int y_diff = abs(y_pos - prev_y_pos);
|
||||
if (markerListUpdated || (x_diff >= map_zoom * 3) || (y_diff >= map_zoom * 3)) {
|
||||
if (map_zoom == 1) {
|
||||
zoom_seek_x = zoom_seek_y = 0;
|
||||
} else {
|
||||
zoom_seek_x = (r.width() - (r.width() / map_zoom)) / 2;
|
||||
zoom_seek_y = (r.height() - (r.height() / map_zoom)) / 2;
|
||||
|
||||
if (markerListUpdated || (x_diff >= 3) || (y_diff >= 3)) {
|
||||
int32_t zoom_seek_x = x_pos;
|
||||
int32_t zoom_seek_y = y_pos;
|
||||
|
||||
// Adjust starting corner position of map per zoom setting
|
||||
if (map_zoom > 1) {
|
||||
zoom_seek_x += (r.width() - (r.width() / map_zoom)) / 2;
|
||||
zoom_seek_y += (r.height() - (r.height() / map_zoom)) / 2;
|
||||
} else if (map_zoom < 0) {
|
||||
zoom_seek_x += (r.width() - (r.width() * (-map_zoom))) / 2;
|
||||
zoom_seek_y += (r.height() - (r.height() * (-map_zoom))) / 2;
|
||||
}
|
||||
|
||||
for (line = 0; line < (r.height() / map_zoom); line++) {
|
||||
map_file.seek(4 + ((x_pos + zoom_seek_x + (map_width * (y_pos + line + zoom_seek_y))) << 1));
|
||||
map_file.read(map_line_buffer.data(), (r.width() / map_zoom) << 1);
|
||||
map_zoom_line(map_line_buffer.data());
|
||||
for (j = 0; j < map_zoom; j++) {
|
||||
display.draw_pixels({0, r.top() + (line * map_zoom) + j, r.width(), 1}, map_line_buffer);
|
||||
// Read from map file and display to zoomed scale
|
||||
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
|
||||
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
|
||||
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
|
||||
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
|
||||
map_read_line(map_line_buffer.data(), r.width());
|
||||
|
||||
for (uint16_t j = 0; j < duplicate_lines; j++) {
|
||||
display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
prev_x_pos = x_pos;
|
||||
prev_y_pos = y_pos;
|
||||
|
||||
@@ -245,6 +305,7 @@ void GeoMap::paint(Painter& painter) {
|
||||
// Draw the other markers
|
||||
draw_markers(painter);
|
||||
draw_scale(painter);
|
||||
draw_mypos();
|
||||
markerListUpdated = false;
|
||||
set_clean();
|
||||
}
|
||||
@@ -255,6 +316,13 @@ void GeoMap::paint(Painter& painter) {
|
||||
}
|
||||
}
|
||||
|
||||
bool GeoMap::on_keyboard(KeyboardEvent key) {
|
||||
if (key == '+' || key == ' ') return on_encoder(1);
|
||||
if (key == '-') return on_encoder(-1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GeoMap::on_touch(const TouchEvent event) {
|
||||
if ((event.type == TouchEvent::Type::Start) && (mode_ == PROMPT)) {
|
||||
set_highlighted(true);
|
||||
@@ -323,8 +391,8 @@ bool GeoMap::manual_panning() {
|
||||
}
|
||||
|
||||
void GeoMap::draw_scale(Painter& painter) {
|
||||
uint16_t km = 100;
|
||||
uint16_t scale_width = km * pixels_per_km * map_zoom;
|
||||
uint16_t km = 800;
|
||||
uint16_t scale_width = (map_zoom > 0) ? km * pixels_per_km * map_zoom : km * pixels_per_km / (-map_zoom);
|
||||
|
||||
while (scale_width > screen_width / 2) {
|
||||
scale_width /= 2;
|
||||
@@ -380,6 +448,41 @@ void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint
|
||||
}
|
||||
}
|
||||
|
||||
void GeoMap::draw_mypos() {
|
||||
const auto r = screen_rect();
|
||||
if (my_lat >= 200 || my_lon >= 200) return; // invalid
|
||||
int x = (map_width * (my_lon + 180) / 360) - x_pos;
|
||||
double lat_rad = sin(my_lat * pi / 180);
|
||||
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
|
||||
auto color = Color::yellow();
|
||||
|
||||
if (map_zoom > 1) {
|
||||
x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 2);
|
||||
y = ((y - (r.height() / 2)) * map_zoom) + (r.height() / 2);
|
||||
} else if (map_zoom < 0) {
|
||||
x = ((x - (r.width() / 2)) / (-map_zoom)) + (r.width() / 2);
|
||||
y = ((y - (r.height() / 2)) / (-map_zoom)) + (r.height() / 2);
|
||||
}
|
||||
|
||||
if ((x >= 0) && (x < r.width()) &&
|
||||
(y > 10) && (y < r.height())) // Dont draw within symbol size of top
|
||||
{
|
||||
ui::Point itemPoint(x, y + r.top());
|
||||
if (mode_ == PROMPT) {
|
||||
// Cross
|
||||
display.fill_rectangle({itemPoint - Point(16, 1), {32, 2}}, color);
|
||||
display.fill_rectangle({itemPoint - Point(1, 16), {2, 32}}, color);
|
||||
} else if (my_angle < 360) {
|
||||
// if we have a valid angle draw bearing
|
||||
draw_bearing(itemPoint, my_angle, 10, color);
|
||||
} else {
|
||||
// draw a small cross
|
||||
display.fill_rectangle({itemPoint - Point(8, 1), {16, 2}}, color);
|
||||
display.fill_rectangle({itemPoint - Point(1, 8), {2, 16}}, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GeoMap::clear_markers() {
|
||||
markerListLen = 0;
|
||||
}
|
||||
@@ -406,6 +509,21 @@ MapMarkerStored GeoMap::store_marker(GeoMarker& marker) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GeoMap::update_my_position(float lat, float lon, int32_t altitude) {
|
||||
my_lat = lat;
|
||||
my_lon = lon;
|
||||
my_altitude = altitude;
|
||||
markerListUpdated = true;
|
||||
set_dirty();
|
||||
}
|
||||
void GeoMap::update_my_orientation(uint16_t angle, bool refresh) {
|
||||
my_angle = angle;
|
||||
if (refresh) {
|
||||
markerListUpdated = true;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
|
||||
void GeoMapView::focus() {
|
||||
geopos.focus();
|
||||
|
||||
@@ -413,7 +531,14 @@ void GeoMapView::focus() {
|
||||
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT);
|
||||
}
|
||||
|
||||
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude) {
|
||||
void GeoMapView::update_my_position(float lat, float lon, int32_t altitude) {
|
||||
geomap.update_my_position(lat, lon, altitude);
|
||||
}
|
||||
void GeoMapView::update_my_orientation(uint16_t angle, bool refresh) {
|
||||
geomap.update_my_orientation(angle, refresh);
|
||||
}
|
||||
|
||||
void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t altitude, int32_t speed) {
|
||||
if (geomap.manual_panning()) {
|
||||
geomap.set_dirty();
|
||||
return;
|
||||
@@ -422,12 +547,14 @@ void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t a
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
altitude_ = altitude;
|
||||
speed_ = speed;
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
geopos.set_report_change(false);
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
geopos.set_altitude(altitude_);
|
||||
geopos.set_speed(speed_);
|
||||
geopos.set_report_change(true);
|
||||
|
||||
geomap.set_angle(angle);
|
||||
@@ -446,11 +573,12 @@ void GeoMapView::setup() {
|
||||
geopos.set_lat(lat_);
|
||||
geopos.set_lon(lon_);
|
||||
|
||||
geopos.on_change = [this](int32_t altitude, float lat, float lon) {
|
||||
geopos.on_change = [this](int32_t altitude, float lat, float lon, int32_t speed) {
|
||||
altitude_ = altitude;
|
||||
lat_ = lat;
|
||||
lon_ = lon;
|
||||
geopos.hide_altitude();
|
||||
speed_ = speed;
|
||||
geopos.hide_altandspeed();
|
||||
geomap.set_manual_panning(true);
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
@@ -482,6 +610,7 @@ GeoMapView::GeoMapView(
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
GeoPos::spd_unit speed_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
uint16_t angle,
|
||||
@@ -489,6 +618,7 @@ GeoMapView::GeoMapView(
|
||||
: nav_(nav),
|
||||
altitude_(altitude),
|
||||
altitude_unit_(altitude_unit),
|
||||
speed_unit_(speed_unit),
|
||||
lat_(lat),
|
||||
lon_(lon),
|
||||
angle_(angle),
|
||||
@@ -516,12 +646,14 @@ GeoMapView::GeoMapView(
|
||||
NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
GeoPos::spd_unit speed_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done)
|
||||
const std::function<void(int32_t, float, float, int32_t)> on_done)
|
||||
: nav_(nav),
|
||||
altitude_(altitude),
|
||||
altitude_unit_(altitude_unit),
|
||||
speed_unit_(speed_unit),
|
||||
lat_(lat),
|
||||
lon_(lon) {
|
||||
mode_ = PROMPT;
|
||||
@@ -540,7 +672,7 @@ GeoMapView::GeoMapView(
|
||||
|
||||
button_ok.on_select = [this, on_done, &nav](Button&) {
|
||||
if (on_done)
|
||||
on_done(altitude_, lat_, lon_);
|
||||
on_done(altitude_, lat_, lon_, speed_);
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
|
||||
#include "portapack.hpp"
|
||||
|
||||
#define MAX_MAP_ZOOM_IN 5
|
||||
#define MAX_MAP_ZOOM_OUT 10
|
||||
|
||||
namespace ui {
|
||||
|
||||
enum GeoMapMode {
|
||||
@@ -59,19 +62,27 @@ class GeoPos : public View {
|
||||
FEET = 0,
|
||||
METERS
|
||||
};
|
||||
enum spd_unit {
|
||||
NONE = 0,
|
||||
MPH,
|
||||
KMPH,
|
||||
HIDDEN = 255
|
||||
};
|
||||
|
||||
std::function<void(int32_t, float, float)> on_change{};
|
||||
std::function<void(int32_t, float, float, int32_t)> on_change{};
|
||||
|
||||
GeoPos(const Point pos, const alt_unit altitude_unit);
|
||||
GeoPos(const Point pos, const alt_unit altitude_unit, const spd_unit speed_unit);
|
||||
|
||||
void focus() override;
|
||||
|
||||
void set_read_only(bool v);
|
||||
void set_altitude(int32_t altitude);
|
||||
void set_speed(int32_t speed);
|
||||
void set_lat(float lat);
|
||||
void set_lon(float lon);
|
||||
int32_t altitude();
|
||||
void hide_altitude();
|
||||
int32_t speed();
|
||||
void hide_altandspeed();
|
||||
float lat();
|
||||
float lon();
|
||||
|
||||
@@ -81,22 +92,35 @@ class GeoPos : public View {
|
||||
bool read_only{false};
|
||||
bool report_change{true};
|
||||
alt_unit altitude_unit_{};
|
||||
spd_unit speed_unit_{};
|
||||
|
||||
Labels labels_position{
|
||||
{{1 * 8, 0 * 16}, "Alt:", Color::light_grey()},
|
||||
{{1 * 8, 1 * 16}, "Lat: \xB0 ' \"", Color::light_grey()}, // 0xB0 is degree ° symbol in our 8x16 font
|
||||
{{1 * 8, 2 * 16}, "Lon: \xB0 ' \"", Color::light_grey()},
|
||||
};
|
||||
|
||||
Labels label_spd_position{
|
||||
{{15 * 8, 0 * 16}, "Spd:", Color::light_grey()},
|
||||
};
|
||||
NumberField field_altitude{
|
||||
{6 * 8, 0 * 16},
|
||||
5,
|
||||
{-1000, 50000},
|
||||
250,
|
||||
' '};
|
||||
|
||||
NumberField field_speed{
|
||||
{19 * 8, 0 * 16},
|
||||
4,
|
||||
{0, 5000},
|
||||
1,
|
||||
' '};
|
||||
Text text_alt_unit{
|
||||
{12 * 8, 0 * 16, 2 * 8, 16},
|
||||
""};
|
||||
Text text_speed_unit{
|
||||
{25 * 8, 0 * 16, 4 * 8, 16},
|
||||
""};
|
||||
|
||||
NumberField field_lat_degrees{
|
||||
{5 * 8, 1 * 16},
|
||||
@@ -159,6 +183,10 @@ class GeoMap : public Widget {
|
||||
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
void update_my_position(float lat, float lon, int32_t altitude);
|
||||
void update_my_orientation(uint16_t angle, bool refresh = false);
|
||||
|
||||
bool init();
|
||||
void set_mode(GeoMapMode mode);
|
||||
@@ -187,7 +215,8 @@ class GeoMap : public Widget {
|
||||
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
|
||||
void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag, const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black());
|
||||
void draw_markers(Painter& painter);
|
||||
void map_zoom_line(ui::Color* buffer);
|
||||
void draw_mypos();
|
||||
void map_read_line(ui::Color* buffer, uint16_t pixels);
|
||||
|
||||
bool manual_panning_{false};
|
||||
GeoMapMode mode_{};
|
||||
@@ -208,6 +237,12 @@ class GeoMap : public Widget {
|
||||
uint16_t angle_{};
|
||||
std::string tag_{};
|
||||
|
||||
// the portapack's position data ( for example injected from serial )
|
||||
float my_lat{200};
|
||||
float my_lon{200};
|
||||
int32_t my_altitude{0};
|
||||
uint16_t my_angle{400};
|
||||
|
||||
int markerListLen{0};
|
||||
GeoMarker markerList[NumMarkerListElements];
|
||||
bool markerListUpdated{false};
|
||||
@@ -220,6 +255,7 @@ class GeoMapView : public View {
|
||||
const std::string& tag,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
GeoPos::spd_unit speed_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
uint16_t angle,
|
||||
@@ -227,9 +263,10 @@ class GeoMapView : public View {
|
||||
GeoMapView(NavigationView& nav,
|
||||
int32_t altitude,
|
||||
GeoPos::alt_unit altitude_unit,
|
||||
GeoPos::spd_unit speed_unit,
|
||||
float lat,
|
||||
float lon,
|
||||
const std::function<void(int32_t, float, float)> on_done);
|
||||
const std::function<void(int32_t, float, float, int32_t)> on_done);
|
||||
~GeoMapView();
|
||||
|
||||
GeoMapView(const GeoMapView&) = delete;
|
||||
@@ -239,7 +276,9 @@ class GeoMapView : public View {
|
||||
|
||||
void focus() override;
|
||||
|
||||
void update_position(float lat, float lon, uint16_t angle, int32_t altitude);
|
||||
void update_position(float lat, float lon, uint16_t angle, int32_t altitude, int32_t speed = 0);
|
||||
void update_my_position(float lat, float lon, int32_t altitude);
|
||||
void update_my_orientation(uint16_t angle, bool refresh = false);
|
||||
|
||||
std::string title() const override { return "Map view"; };
|
||||
|
||||
@@ -253,10 +292,12 @@ class GeoMapView : public View {
|
||||
|
||||
void setup();
|
||||
|
||||
const std::function<void(int32_t, float, float)> on_done{};
|
||||
const std::function<void(int32_t, float, float, int32_t)> on_done{};
|
||||
GeoMapMode mode_{};
|
||||
int32_t altitude_{};
|
||||
int32_t speed_{};
|
||||
GeoPos::alt_unit altitude_unit_{};
|
||||
GeoPos::spd_unit speed_unit_{};
|
||||
float lat_{};
|
||||
float lon_{};
|
||||
uint16_t angle_{};
|
||||
@@ -266,7 +307,8 @@ class GeoMapView : public View {
|
||||
|
||||
GeoPos geopos{
|
||||
{0, 0},
|
||||
altitude_unit_};
|
||||
altitude_unit_,
|
||||
speed_unit_};
|
||||
|
||||
GeoMap geomap{
|
||||
{0, GeoMap::banner_height, GeoMap::geomap_rect_width, GeoMap::geomap_rect_height}};
|
||||
|
||||
@@ -269,6 +269,19 @@ bool MenuView::on_key(const KeyEvent key) {
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuView::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == '-') return set_highlighted(highlighted_item - 1);
|
||||
if (key == '+') return set_highlighted(highlighted_item + 1);
|
||||
if (key == 10) {
|
||||
if (menu_items[highlighted_item].on_select) {
|
||||
menu_items[highlighted_item].on_select(KeyEvent::Right);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MenuView::on_encoder(const EncoderEvent event) {
|
||||
set_highlighted(highlighted_item + event);
|
||||
return true;
|
||||
|
||||
@@ -97,6 +97,7 @@ class MenuView : public View {
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
private:
|
||||
void update_items();
|
||||
|
||||
@@ -93,6 +93,13 @@ void FrequencyField::set_allow_digit_mode(bool allowed) {
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyField::getAccessibilityText(std::string& result) {
|
||||
result = to_string_dec_int(value_);
|
||||
}
|
||||
void FrequencyField::getWidgetName(std::string& result) {
|
||||
result = "FrequencyField";
|
||||
}
|
||||
|
||||
void FrequencyField::paint(Painter& painter) {
|
||||
const auto str_value = to_string_short_freq(value_);
|
||||
const auto paint_style = has_focus() ? style().invert() : style();
|
||||
@@ -165,12 +172,18 @@ bool FrequencyField::on_key(KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_keyboard(KeyboardEvent key) {
|
||||
if (key == '+' || key == ' ') return on_encoder(1);
|
||||
if (key == '-') return on_encoder(-1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_encoder(const EncoderEvent delta) {
|
||||
if (digit_mode_)
|
||||
set_value(value_ + (delta * digit_step()));
|
||||
else
|
||||
set_value(value_ + (delta * step_));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -321,6 +334,25 @@ void FrequencyKeypadView::on_button(Button& button) {
|
||||
update_text();
|
||||
}
|
||||
|
||||
bool FrequencyKeypadView::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == 8) {
|
||||
digit_delete();
|
||||
update_text();
|
||||
return true;
|
||||
}
|
||||
if (key >= '0' && key <= '9') {
|
||||
digit_add(key);
|
||||
update_text();
|
||||
return true;
|
||||
}
|
||||
if (key == '.') {
|
||||
field_toggle();
|
||||
update_text();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::digit_add(const char c) {
|
||||
if (state == State::DigitMHz) {
|
||||
if (clear_field_if_digits_entered) {
|
||||
|
||||
@@ -61,10 +61,14 @@ class FrequencyField : public Widget {
|
||||
|
||||
bool on_key(KeyEvent event) override;
|
||||
bool on_encoder(EncoderEvent delta) override;
|
||||
bool on_keyboard(KeyboardEvent key) override;
|
||||
bool on_touch(TouchEvent event) override;
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
const size_t length_;
|
||||
range_t range_;
|
||||
@@ -202,6 +206,7 @@ class FrequencyKeypadView : public View {
|
||||
rf::Frequency value() const;
|
||||
void set_value(const rf::Frequency new_value);
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_keyboard(const KeyboardEvent key) override;
|
||||
|
||||
private:
|
||||
int16_t focused_button = 0;
|
||||
|
||||
@@ -6,6 +6,44 @@ namespace ui {
|
||||
|
||||
/* static */ std::vector<DynamicBitmap<16, 16>> ExternalItemsMenuLoader::bitmaps;
|
||||
|
||||
// iterates over all ppma-s, and if it is runnable on the current system, it'll call the callback, and pass info.
|
||||
/* static */ void ExternalItemsMenuLoader::load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback) {
|
||||
if (!callback) return;
|
||||
if (sd_card::status() != sd_card::Status::Mounted)
|
||||
return;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(u"APPS", u"*.ppma")) {
|
||||
auto filePath = u"/APPS/" + entry.path();
|
||||
File app;
|
||||
|
||||
auto openError = app.open(filePath);
|
||||
if (openError)
|
||||
continue;
|
||||
|
||||
application_information_t application_information = {};
|
||||
|
||||
auto readResult = app.read(&application_information, sizeof(application_information_t));
|
||||
if (!readResult)
|
||||
continue;
|
||||
|
||||
if (application_information.header_version != CURRENT_HEADER_VERSION)
|
||||
continue;
|
||||
|
||||
bool versionMatches = VERSION_MD5 == application_information.app_version;
|
||||
if (!versionMatches) continue;
|
||||
// here the app is startable and good.
|
||||
std::string appshortname = filePath.filename().string();
|
||||
if (appshortname.size() >= 5 && appshortname.substr(appshortname.size() - 5) == ".ppma") {
|
||||
// Remove the ".ppma" suffix
|
||||
appshortname = appshortname.substr(0, appshortname.size() - 5);
|
||||
}
|
||||
AppInfoConsole info{
|
||||
.appCallName = appshortname.c_str(),
|
||||
.appFriendlyName = reinterpret_cast<char*>(&application_information.app_name[0]),
|
||||
.appLocation = application_information.menu_location};
|
||||
callback(info);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ std::vector<GridItem> ExternalItemsMenuLoader::load_external_items(app_location_t app_location, NavigationView& nav) {
|
||||
bitmaps.clear();
|
||||
|
||||
@@ -47,7 +85,9 @@ namespace ui {
|
||||
bitmaps.push_back(std::move(dyn_bmp));
|
||||
|
||||
gridItem.on_select = [&nav, app_location, filePath]() {
|
||||
run_external_app(nav, filePath);
|
||||
if (!run_external_app(nav, filePath)) {
|
||||
nav.display_modal("Error", "The .ppma file in your APPS\nfolder can't be read. Please\nupdate your SD Card content.");
|
||||
}
|
||||
};
|
||||
} else {
|
||||
gridItem.color = Color::light_grey();
|
||||
@@ -65,19 +105,19 @@ namespace ui {
|
||||
return external_apps;
|
||||
}
|
||||
|
||||
/* static */ void ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
|
||||
/* static */ bool ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
|
||||
File app;
|
||||
|
||||
auto openError = app.open(filePath);
|
||||
if (openError)
|
||||
chDbgPanic("file gone");
|
||||
return false;
|
||||
|
||||
application_information_t application_information = {};
|
||||
|
||||
auto readResult = app.read(&application_information, sizeof(application_information_t));
|
||||
|
||||
if (!readResult)
|
||||
chDbgPanic("no data");
|
||||
return false;
|
||||
|
||||
app.seek(0);
|
||||
|
||||
@@ -93,7 +133,7 @@ namespace ui {
|
||||
|
||||
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
|
||||
if (!readResult)
|
||||
chDbgPanic("read error");
|
||||
return false;
|
||||
|
||||
if (readResult.value() < std::filesystem::max_file_block_size)
|
||||
break;
|
||||
@@ -114,7 +154,7 @@ namespace ui {
|
||||
|
||||
readResult = app.read(target_memory, bytes_to_read);
|
||||
if (!readResult)
|
||||
chDbgPanic("read error #2");
|
||||
return false;
|
||||
|
||||
if (readResult.value() != bytes_to_read)
|
||||
break;
|
||||
@@ -126,7 +166,7 @@ namespace ui {
|
||||
|
||||
readResult = app.read(&application_information.memory_location[file_read_index], bytes_to_read);
|
||||
if (!readResult)
|
||||
chDbgPanic("read error #3");
|
||||
return false;
|
||||
|
||||
if (readResult.value() < std::filesystem::max_file_block_size)
|
||||
break;
|
||||
@@ -134,6 +174,7 @@ namespace ui {
|
||||
}
|
||||
|
||||
application_information.externalAppEntry(nav);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -54,11 +54,11 @@ class ExternalItemsMenuLoader {
|
||||
public:
|
||||
static std::vector<GridItem> load_external_items(app_location_t, NavigationView&);
|
||||
ExternalItemsMenuLoader() = delete;
|
||||
static bool run_external_app(ui::NavigationView&, std::filesystem::path);
|
||||
static void load_all_external_items_callback(std::function<void(AppInfoConsole&)> callback);
|
||||
|
||||
private:
|
||||
static std::vector<DynamicBitmap<16, 16>> bitmaps;
|
||||
|
||||
static void run_external_app(ui::NavigationView&, std::filesystem::path);
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
#include "ui_settings.hpp"
|
||||
#include "ui_siggen.hpp"
|
||||
#include "ui_sonde.hpp"
|
||||
#include "ui_spectrum_painter.hpp"
|
||||
// #include "ui_spectrum_painter.hpp" //moved to ext app
|
||||
#include "ui_ss_viewer.hpp"
|
||||
#include "ui_sstvtx.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
@@ -93,7 +93,7 @@
|
||||
#include "ble_tx_app.hpp"
|
||||
#include "capture_app.hpp"
|
||||
#include "ert_app.hpp"
|
||||
#include "gps_sim_app.hpp"
|
||||
// #include "gps_sim_app.hpp" //moved to ext
|
||||
// #include "lge_app.hpp" //moved to ext
|
||||
#include "pocsag_app.hpp"
|
||||
#include "replay_app.hpp"
|
||||
@@ -111,6 +111,169 @@ namespace pmem = portapack::persistent_memory;
|
||||
|
||||
namespace ui {
|
||||
|
||||
// When adding or removing apps from the Menu, please update it here:
|
||||
std::vector<AppInfoConsole> NavigationView::fixedAppListFC = {
|
||||
{"adsbrx", "ADS-B", RX},
|
||||
{"ais", "AIS Boats", RX},
|
||||
{"aprsrx", "APRS", RX},
|
||||
{"audio", "Audio", RX},
|
||||
{"blerx", "BLE Rx", RX},
|
||||
{"ert", "ERT Meter", RX},
|
||||
{"level", "Level", RX},
|
||||
{"pocsagrx", "POCSAG", RX},
|
||||
{"radiosnde", "Radiosnde", RX},
|
||||
{"recon", "Recon", RX},
|
||||
{"search", "Search", RX},
|
||||
{"tpms", "TPMS Cars", RX},
|
||||
{"weather", "Weather", RX},
|
||||
{"subghzd", "SubGhzD", RX},
|
||||
{"adsbtx", "ADS-B", TX},
|
||||
{"aprstx", "APRS TX", TX},
|
||||
{"bht", "BHT Xy/EP", TX},
|
||||
{"bletx", "BLE Tx", TX},
|
||||
{"morsetx", "Morse", TX},
|
||||
{"ooktx", "OOK", TX},
|
||||
{"pocsatx", "POCSAG TX", TX},
|
||||
{"rdstx", "RDS TX", TX},
|
||||
{"soundbrd", "Soundbrd", TX},
|
||||
{"sstvtx", "SSTV", TX},
|
||||
{"touchtune", "TouchTune", TX},
|
||||
{"capture", "Capture", RX},
|
||||
{"replay", "Replay", TX},
|
||||
{"remote", "Remote", TX},
|
||||
{"scanner", "Scanner", RX},
|
||||
{"microphone", "Microphone", TX},
|
||||
{"lookingglass", "Looking Glass", RX}};
|
||||
|
||||
bool NavigationView::StartAppByName(const char* name) {
|
||||
home(false);
|
||||
if (strcmp(name, "adsbrx") == 0) {
|
||||
push<ADSBRxView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "ais") == 0) {
|
||||
push<AISAppView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "aprsrx") == 0) {
|
||||
push<APRSRXView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "audio") == 0) {
|
||||
push<AnalogAudioView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "blerx") == 0) {
|
||||
push<BLERxView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "ert") == 0) {
|
||||
push<ERTAppView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "level") == 0) {
|
||||
push<LevelView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "pocsagrx") == 0) {
|
||||
push<POCSAGAppView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "radiosnode") == 0) {
|
||||
push<SondeView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "recon") == 0) {
|
||||
push<ReconView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "search") == 0) {
|
||||
push<SearchView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "tpms") == 0) {
|
||||
push<TPMSAppView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "weather") == 0) {
|
||||
push<WeatherView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "subghzd") == 0) {
|
||||
push<SubGhzDView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "adsbtx") == 0) {
|
||||
push<ADSBTxView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "aprstx") == 0) {
|
||||
push<APRSTXView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "bht") == 0) {
|
||||
push<BHTView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "bletx") == 0) {
|
||||
push<BLETxView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "morsetx") == 0) {
|
||||
push<MorseView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "ooktx") == 0) {
|
||||
push<EncodersView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "pocsatx") == 0) {
|
||||
push<POCSAGTXView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "rdstx") == 0) {
|
||||
push<RDSView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "soundbrd") == 0) {
|
||||
push<SoundBoardView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "sstvtx") == 0) {
|
||||
push<SSTVTXView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "touchtune") == 0) {
|
||||
push<TouchTunesView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "capture") == 0) {
|
||||
push<CaptureAppView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "replay") == 0) {
|
||||
push<PlaylistView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "remote") == 0) {
|
||||
push<RemoteView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "scanner") == 0) {
|
||||
push<ScannerView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "microphone") == 0) {
|
||||
push<MicTXView>();
|
||||
return true;
|
||||
}
|
||||
if (strcmp(name, "lookingglass") == 0) {
|
||||
push<GlassView>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* StatusTray ************************************************************/
|
||||
|
||||
StatusTray::StatusTray(Point pos)
|
||||
@@ -407,7 +570,7 @@ void SystemStatusView::rtc_battery_workaround() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
make_new_directory(SETTINGS_DIR);
|
||||
ensure_directory(SETTINGS_DIR);
|
||||
make_new_file(DATE_FILEFLAG);
|
||||
|
||||
year = 1980;
|
||||
@@ -441,7 +604,17 @@ InformationView::InformationView(
|
||||
&version,
|
||||
<ime});
|
||||
|
||||
#if GCC_VERSION_MISMATCH
|
||||
static constexpr Style style_gcc_warning{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 33, 33},
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
version.set_style(&style_gcc_warning);
|
||||
#else
|
||||
version.set_style(&style_infobar);
|
||||
#endif
|
||||
|
||||
ltime.set_style(&style_infobar);
|
||||
refresh();
|
||||
set_dirty();
|
||||
@@ -487,6 +660,14 @@ void NavigationView::pop(bool trigger_update) {
|
||||
if (on_pop) on_pop();
|
||||
}
|
||||
|
||||
void NavigationView::home(bool trigger_update) {
|
||||
while (view_stack.size() > 1) {
|
||||
pop(false);
|
||||
}
|
||||
|
||||
if (trigger_update) update_view();
|
||||
}
|
||||
|
||||
void NavigationView::display_modal(
|
||||
const std::string& title,
|
||||
const std::string& message) {
|
||||
@@ -597,7 +778,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
||||
{"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push<BHTView>(); }},
|
||||
{"BLE Tx", ui::Color::green(), &bitmap_icon_btle, [&nav]() { nav.push<BLETxView>(); }},
|
||||
// {"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push<CoasterPagerView>(); }}, //moved to ext
|
||||
{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }},
|
||||
//{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }}, //moved to ext
|
||||
//{"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push<JammerView>(); }}, //moved to ext
|
||||
// { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push<KeyfobView>(); }},
|
||||
// {"LGE", ui::Color::yellow(), &bitmap_icon_lge, [&nav]() { nav.push<LGEView>(); }}, //moved to ext
|
||||
@@ -607,7 +788,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
||||
{"POCSAG TX", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push<POCSAGTXView>(); }},
|
||||
{"RDS", ui::Color::green(), &bitmap_icon_rds, [&nav]() { nav.push<RDSView>(); }},
|
||||
{"Soundbrd", ui::Color::green(), &bitmap_icon_soundboard, [&nav]() { nav.push<SoundBoardView>(); }},
|
||||
{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
|
||||
//{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
|
||||
{"SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav]() { nav.push<SSTVTXView>(); }},
|
||||
// {"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }}, //moved to ext
|
||||
{"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push<TouchTunesView>(); }},
|
||||
@@ -741,6 +922,9 @@ SystemView::SystemView(
|
||||
Context& SystemView::context() const {
|
||||
return context_;
|
||||
}
|
||||
NavigationView* SystemView::get_navigation_view() {
|
||||
return &navigation_view;
|
||||
}
|
||||
|
||||
void SystemView::toggle_overlay() {
|
||||
switch (++overlay_active) {
|
||||
@@ -748,7 +932,7 @@ void SystemView::toggle_overlay() {
|
||||
this->add_child(&this->overlay);
|
||||
this->set_dirty();
|
||||
shared_memory.request_m4_performance_counter = 1;
|
||||
shared_memory.m4_cpu_usage = 0;
|
||||
shared_memory.m4_performance_counter = 0;
|
||||
shared_memory.m4_heap_usage = 0;
|
||||
shared_memory.m4_stack_usage = 0;
|
||||
break;
|
||||
@@ -756,11 +940,12 @@ void SystemView::toggle_overlay() {
|
||||
this->remove_child(&this->overlay);
|
||||
this->add_child(&this->overlay2);
|
||||
this->set_dirty();
|
||||
shared_memory.request_m4_performance_counter = 0;
|
||||
shared_memory.request_m4_performance_counter = 2;
|
||||
break;
|
||||
case 3:
|
||||
this->remove_child(&this->overlay2);
|
||||
this->set_dirty();
|
||||
shared_memory.request_m4_performance_counter = 0;
|
||||
overlay_active = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
#include "diskio.h"
|
||||
#include "lfsr_random.hpp"
|
||||
#include "sd_card.hpp"
|
||||
|
||||
#include "external_app.hpp"
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
@@ -57,6 +57,12 @@ enum modal_t {
|
||||
ABORT
|
||||
};
|
||||
|
||||
struct AppInfoConsole {
|
||||
const char* appCallName;
|
||||
const char* appFriendlyName;
|
||||
const app_location_t appLocation;
|
||||
};
|
||||
|
||||
class NavigationView : public View {
|
||||
public:
|
||||
std::function<void(const View&)> on_view_changed{};
|
||||
@@ -85,6 +91,7 @@ class NavigationView : public View {
|
||||
View* push_view(std::unique_ptr<View> new_view);
|
||||
void replace(View* v);
|
||||
void pop(bool trigger_update = true);
|
||||
void home(bool trigger_update);
|
||||
|
||||
void display_modal(const std::string& title, const std::string& message);
|
||||
void display_modal(
|
||||
@@ -99,6 +106,8 @@ class NavigationView : public View {
|
||||
* Returns true if the handler was bound successfully. */
|
||||
bool set_on_pop(std::function<void()> on_pop);
|
||||
|
||||
static std::vector<AppInfoConsole> fixedAppListFC; // fixed app list for console. vector, so can be incomplete and still iterateable
|
||||
bool StartAppByName(const char* name); // Starts a View (app) by name stored in fixedAppListFC. This is to start apps from console
|
||||
private:
|
||||
struct ViewState {
|
||||
std::unique_ptr<View> view;
|
||||
@@ -321,6 +330,8 @@ class SystemView : public View {
|
||||
void toggle_overlay();
|
||||
void paint_overlay();
|
||||
|
||||
NavigationView* get_navigation_view();
|
||||
|
||||
private:
|
||||
uint8_t overlay_active{0};
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
extern "C" {
|
||||
#include "usb_serial_io.h"
|
||||
#include "usb_serial_device_to_host.h"
|
||||
#include "usb_serial_cdc.h"
|
||||
}
|
||||
|
||||
#include "usb_serial_shell.hpp"
|
||||
#include "usb_serial.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "usb_serial_host_to_device.hpp"
|
||||
|
||||
#include <libopencm3/cm3/common.h>
|
||||
#include <libopencm3/lpc43xx/usb.h>
|
||||
@@ -24,6 +25,7 @@ void USBSerial::initialize() {
|
||||
|
||||
init_serial_usb_driver(&SUSBD1);
|
||||
shellInit();
|
||||
init_host_to_device();
|
||||
}
|
||||
|
||||
void USBSerial::dispatch() {
|
||||
@@ -32,10 +34,14 @@ void USBSerial::dispatch() {
|
||||
|
||||
if (shell_created == false) {
|
||||
shell_created = true;
|
||||
create_shell();
|
||||
create_shell(_eventDispatcher);
|
||||
}
|
||||
|
||||
bulk_out_receive();
|
||||
schedule_host_to_device_transfer();
|
||||
}
|
||||
|
||||
void USBSerial::dispatch_transfer() {
|
||||
complete_host_to_device_transfer();
|
||||
}
|
||||
|
||||
void USBSerial::on_channel_opened() {
|
||||
|
||||
@@ -24,14 +24,18 @@
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
class EventDispatcher;
|
||||
|
||||
namespace portapack {
|
||||
|
||||
class USBSerial {
|
||||
public:
|
||||
void initialize();
|
||||
void dispatch();
|
||||
void dispatch_transfer();
|
||||
void on_channel_opened();
|
||||
void on_channel_closed();
|
||||
void setEventDispatcher(EventDispatcher* ed) { _eventDispatcher = ed; }
|
||||
|
||||
private:
|
||||
void enable_xtal();
|
||||
@@ -43,6 +47,8 @@ class USBSerial {
|
||||
|
||||
bool connected{false};
|
||||
bool shell_created{false};
|
||||
|
||||
EventDispatcher* _eventDispatcher = NULL;
|
||||
};
|
||||
|
||||
} // namespace portapack
|
||||
@@ -23,10 +23,25 @@
|
||||
#include "usb_serial_endpoints.h"
|
||||
#include "usb_serial_event.hpp"
|
||||
|
||||
uint32_t EVT_MASK_USB = EVENT_MASK(8);
|
||||
|
||||
extern void usb0_isr(void);
|
||||
|
||||
static Thread* thread_usb_event = NULL;
|
||||
|
||||
CH_IRQ_HANDLER(USB0_IRQHandler) {
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
const uint32_t status = USB0_USBSTS_D & USB0_USBINTR_D;
|
||||
|
||||
usb0_isr();
|
||||
|
||||
if (status & USB0_USBSTS_D_UI) {
|
||||
chSysLockFromIsr();
|
||||
chEvtSignalI(thread_usb_event, EVT_MASK_USB);
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
@@ -46,6 +61,7 @@ uint32_t __strex(uint32_t val, volatile uint32_t* addr) {
|
||||
|
||||
void nvic_enable_irq(uint8_t irqn) {
|
||||
NVIC_ISER(irqn / 32) = (1 << (irqn % 32));
|
||||
thread_usb_event = chThdSelf();
|
||||
}
|
||||
|
||||
void usb_configuration_changed(usb_device_t* const device) {
|
||||
|
||||
@@ -264,7 +264,7 @@ uint8_t usb_descriptor_string_manufacturer[] = {
|
||||
};
|
||||
|
||||
uint8_t usb_descriptor_string_product[] = {
|
||||
43, // bLength
|
||||
34, // bLength
|
||||
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
|
||||
'P', 0x00,
|
||||
'o', 0x00,
|
||||
|
||||
+44
-36
@@ -19,7 +19,7 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "usb_serial_io.h"
|
||||
#include "usb_serial_device_to_host.h"
|
||||
|
||||
#include "usb_serial_endpoints.h"
|
||||
|
||||
@@ -40,42 +40,11 @@
|
||||
|
||||
SerialUSBDriver SUSBD1;
|
||||
|
||||
void bulk_out_receive(void) {
|
||||
int ret;
|
||||
do {
|
||||
ret = usb_transfer_schedule(
|
||||
&usb_endpoint_bulk_out,
|
||||
&usb_endpoint_bulk_out.buffer[0],
|
||||
32,
|
||||
serial_bulk_transfer_complete,
|
||||
NULL);
|
||||
|
||||
} while (ret != -1);
|
||||
}
|
||||
|
||||
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) {
|
||||
(void)user_data;
|
||||
|
||||
chSysLockFromIsr();
|
||||
|
||||
for (unsigned int i = 0; i < bytes_transferred; i++) {
|
||||
msg_t ret;
|
||||
do {
|
||||
ret = chIQPutI(&SUSBD1.iqueue, usb_endpoint_bulk_out.buffer[i]);
|
||||
if (ret == Q_FULL)
|
||||
chThdSleepMilliseconds(1);
|
||||
|
||||
} while (ret == Q_FULL);
|
||||
}
|
||||
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
static void onotify(GenericQueue* qp) {
|
||||
SerialUSBDriver* sdp = chQGetLink(qp);
|
||||
uint8_t buff[64];
|
||||
uint8_t buff[USBSERIAL_BUFFERS_SIZE];
|
||||
int n = chOQGetFullI(&sdp->oqueue);
|
||||
if (n > 64) n = 64; // don't overflow
|
||||
if (n > USBSERIAL_BUFFERS_SIZE) n = USBSERIAL_BUFFERS_SIZE; // don't overflow
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
buff[i] = chOQGetI(&sdp->oqueue);
|
||||
@@ -139,6 +108,45 @@ static const struct SerialUSBDriverVMT vmt = {
|
||||
|
||||
void init_serial_usb_driver(SerialUSBDriver* sdp) {
|
||||
sdp->vmt = &vmt;
|
||||
chIQInit(&sdp->iqueue, sdp->ib, SERIAL_BUFFERS_SIZE, NULL, sdp);
|
||||
chOQInit(&sdp->oqueue, sdp->ob, SERIAL_BUFFERS_SIZE, onotify, sdp);
|
||||
chIQInit(&sdp->iqueue, sdp->ib, USBSERIAL_BUFFERS_SIZE, NULL, sdp);
|
||||
chOQInit(&sdp->oqueue, sdp->ob, USBSERIAL_BUFFERS_SIZE, onotify, sdp);
|
||||
}
|
||||
|
||||
// queue handler from ch
|
||||
static msg_t qwait(GenericQueue* qp, systime_t time) {
|
||||
if (TIME_IMMEDIATE == time)
|
||||
return Q_TIMEOUT;
|
||||
currp->p_u.wtobjp = qp;
|
||||
queue_insert(currp, &qp->q_waiting);
|
||||
return chSchGoSleepTimeoutS(THD_STATE_WTQUEUE, time);
|
||||
}
|
||||
|
||||
// This function fills the output buffer, and sends all data in 1 packet
|
||||
size_t fillOBuffer(OutputQueue* oqp, const uint8_t* bp, size_t n) {
|
||||
qnotify_t nfy = oqp->q_notify;
|
||||
size_t w = 0;
|
||||
|
||||
chDbgCheck(n > 0, "chOQWriteTimeout");
|
||||
chSysLock();
|
||||
while (TRUE) {
|
||||
while (chOQIsFullI(oqp)) {
|
||||
if (qwait((GenericQueue*)oqp, TIME_INFINITE) != Q_OK) {
|
||||
chSysUnlock();
|
||||
return w;
|
||||
}
|
||||
}
|
||||
while (!chOQIsFullI(oqp) && n > 0) {
|
||||
oqp->q_counter--;
|
||||
*oqp->q_wrptr++ = *bp++;
|
||||
if (oqp->q_wrptr >= oqp->q_top)
|
||||
oqp->q_wrptr = oqp->q_buffer;
|
||||
w++;
|
||||
--n;
|
||||
}
|
||||
if (nfy) nfy(oqp);
|
||||
|
||||
chSysUnlock(); /* Gives a preemption chance in a controlled point.*/
|
||||
if (n == 0) return w;
|
||||
chSysLock();
|
||||
}
|
||||
}
|
||||
+18
-6
@@ -24,6 +24,12 @@
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#define USB_BULK_BUFFER_SIZE 64
|
||||
|
||||
#ifndef USBSERIAL_BUFFERS_SIZE
|
||||
#define USBSERIAL_BUFFERS_SIZE 128
|
||||
#endif
|
||||
|
||||
struct SerialUSBDriverVMT {
|
||||
_base_asynchronous_channel_methods
|
||||
};
|
||||
@@ -31,10 +37,10 @@ struct SerialUSBDriverVMT {
|
||||
struct SerialUSBDriver {
|
||||
/** @brief Virtual Methods Table.*/
|
||||
const struct SerialUSBDriverVMT* vmt;
|
||||
InputQueue iqueue; /* Output queue.*/
|
||||
OutputQueue oqueue; /* Input circular buffer.*/
|
||||
uint8_t ib[SERIAL_BUFFERS_SIZE]; /* Output circular buffer.*/
|
||||
uint8_t ob[SERIAL_BUFFERS_SIZE];
|
||||
InputQueue iqueue; /* Output queue.*/
|
||||
OutputQueue oqueue; /* Input circular buffer.*/
|
||||
uint8_t ib[USBSERIAL_BUFFERS_SIZE]; /* Output circular buffer.*/
|
||||
uint8_t ob[USBSERIAL_BUFFERS_SIZE];
|
||||
};
|
||||
|
||||
typedef struct SerialUSBDriver SerialUSBDriver;
|
||||
@@ -42,5 +48,11 @@ typedef struct SerialUSBDriver SerialUSBDriver;
|
||||
extern SerialUSBDriver SUSBD1;
|
||||
|
||||
void init_serial_usb_driver(SerialUSBDriver* sdp);
|
||||
void bulk_out_receive(void);
|
||||
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
size_t fillOBuffer(OutputQueue* oqp, const uint8_t* bp, size_t n);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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 "usb_serial_host_to_device.hpp"
|
||||
#include "event_m0.hpp"
|
||||
#include "usb_serial_device_to_host.h"
|
||||
|
||||
extern "C" {
|
||||
#include <common/usb.h>
|
||||
#include <hackrf_usb/usb_device.h>
|
||||
#include <hackrf_usb/usb_endpoint.h>
|
||||
}
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
static Thread* thread_usb_event = NULL;
|
||||
|
||||
struct usb_bulk_buffer_t {
|
||||
uint8_t* data;
|
||||
size_t length;
|
||||
bool completed;
|
||||
};
|
||||
|
||||
std::queue<usb_bulk_buffer_t*> usb_bulk_buffer_queue;
|
||||
std::queue<usb_bulk_buffer_t*> usb_bulk_buffer_spare;
|
||||
|
||||
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) {
|
||||
usb_bulk_buffer_t* transfer_data = (usb_bulk_buffer_t*)user_data;
|
||||
|
||||
transfer_data->length = bytes_transferred;
|
||||
transfer_data->completed = true;
|
||||
}
|
||||
|
||||
void init_host_to_device() {
|
||||
thread_usb_event = chThdSelf();
|
||||
}
|
||||
|
||||
void schedule_host_to_device_transfer() {
|
||||
if (usb_bulk_buffer_queue.size() >= 8)
|
||||
return;
|
||||
|
||||
static usb_bulk_buffer_t* transfer_data = nullptr;
|
||||
|
||||
int ret;
|
||||
|
||||
do {
|
||||
if (transfer_data == nullptr) {
|
||||
if (usb_bulk_buffer_spare.empty() == false) {
|
||||
transfer_data = usb_bulk_buffer_spare.front();
|
||||
transfer_data->length = 0;
|
||||
transfer_data->completed = false;
|
||||
usb_bulk_buffer_spare.pop();
|
||||
} else {
|
||||
transfer_data = new usb_bulk_buffer_t{
|
||||
.data = new uint8_t[USB_BULK_BUFFER_SIZE],
|
||||
.length = 0,
|
||||
.completed = false};
|
||||
}
|
||||
}
|
||||
|
||||
ret = usb_transfer_schedule(
|
||||
&usb_endpoint_bulk_out,
|
||||
transfer_data->data,
|
||||
USB_BULK_BUFFER_SIZE,
|
||||
serial_bulk_transfer_complete,
|
||||
transfer_data);
|
||||
|
||||
if (ret != -1) {
|
||||
usb_bulk_buffer_queue.push(transfer_data);
|
||||
transfer_data = nullptr;
|
||||
|
||||
if (usb_bulk_buffer_queue.size() >= 8)
|
||||
return;
|
||||
}
|
||||
} while (ret != -1);
|
||||
}
|
||||
|
||||
void complete_host_to_device_transfer() {
|
||||
for (; !usb_bulk_buffer_queue.empty(); usb_bulk_buffer_queue.pop()) {
|
||||
usb_bulk_buffer_t* transfer_data = usb_bulk_buffer_queue.front();
|
||||
|
||||
while (transfer_data->completed == false)
|
||||
return;
|
||||
|
||||
chSysLock();
|
||||
for (unsigned int i = 0; i < transfer_data->length; i++) {
|
||||
msg_t ret;
|
||||
do {
|
||||
ret = chIQPutI(&SUSBD1.iqueue, transfer_data->data[i]);
|
||||
|
||||
if (ret == Q_FULL) {
|
||||
chSysUnlock();
|
||||
chThdSleepMilliseconds(1); // wait for shell thread when buffer is full
|
||||
chSysLock();
|
||||
}
|
||||
|
||||
} while (ret == Q_FULL);
|
||||
}
|
||||
chSysUnlock();
|
||||
|
||||
usb_bulk_buffer_spare.push(transfer_data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#define USB_BULK_BUFFER_SIZE 64
|
||||
|
||||
void init_host_to_device();
|
||||
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred);
|
||||
void schedule_host_to_device_transfer();
|
||||
void complete_host_to_device_transfer();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,4 +26,6 @@
|
||||
|
||||
#include "shell.h"
|
||||
|
||||
void create_shell(void);
|
||||
class EventDispatcher;
|
||||
|
||||
void create_shell(EventDispatcher* evtd);
|
||||
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* 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 "usb_serial_shell_filesystem.hpp"
|
||||
#include "usb_serial_device_to_host.h"
|
||||
|
||||
#include "chprintf.h"
|
||||
#include "string_format.hpp"
|
||||
#include <cstring>
|
||||
|
||||
static File* shell_file = nullptr;
|
||||
|
||||
static bool report_on_error(BaseSequentialStream* chp, File::Error& error) {
|
||||
if (error.ok() == false) {
|
||||
chprintf(chp, "Error calling delete_file: %d %s\r\n", error.code(), error.what().c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool report_on_error(BaseSequentialStream* chp, FRESULT error_code) {
|
||||
std::filesystem::filesystem_error error = error_code;
|
||||
return report_on_error(chp, error);
|
||||
}
|
||||
|
||||
static bool report_on_error(BaseSequentialStream* chp, Optional<File::Error>& error) {
|
||||
if (error.is_valid())
|
||||
return report_on_error(chp, error.value());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool report_on_error(BaseSequentialStream* chp, File::Result<uint64_t> error) {
|
||||
if (error.is_error()) {
|
||||
return report_on_error(chp, error.error());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void cmd_sd_list_dir(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: ls /\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(path, "*")) {
|
||||
if (std::filesystem::is_directory(entry.status())) {
|
||||
chprintf(chp, "%s/\r\n", entry.path().string().c_str());
|
||||
} else if (std::filesystem::is_regular_file(entry.status())) {
|
||||
chprintf(chp, "%s\r\n", entry.path().string().c_str());
|
||||
} else {
|
||||
chprintf(chp, "%s *\r\n", entry.path().string().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_sd_unlink(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: unlink <path>\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
auto error = delete_file(path);
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_mkdir(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: mkdir <path>\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
|
||||
if (!std::filesystem::is_directory(path)) {
|
||||
chprintf(chp, "directory already exists.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = make_new_directory(path);
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_filesize(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: filesize <path>\r\n");
|
||||
return;
|
||||
}
|
||||
auto path = path_from_string8(argv[0]);
|
||||
FILINFO res;
|
||||
auto stat = f_stat(path.tchar(), &res);
|
||||
if (report_on_error(chp, stat)) return;
|
||||
|
||||
chprintf(chp, "%lu\r\n", res.fsize);
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_open(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: fopen <path>\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file != nullptr) {
|
||||
chprintf(chp, "file already open\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
shell_file = new File();
|
||||
auto error = shell_file->open(path, false, true);
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_seek(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: fseek <offset>\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file == nullptr) {
|
||||
chprintf(chp, "no open file\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int address = (int)strtol(argv[0], NULL, 10);
|
||||
auto error = shell_file->seek(address);
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_close(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
|
||||
if (argc != 0) {
|
||||
chprintf(chp, "usage: fclose\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file == nullptr) {
|
||||
chprintf(chp, "no open file\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = shell_file->sync();
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
delete shell_file;
|
||||
shell_file = nullptr;
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_truncate(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
|
||||
if (argc != 0) {
|
||||
chprintf(chp, "usage: ftruncate\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file == nullptr) {
|
||||
chprintf(chp, "no open file\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = shell_file->truncate();
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_sync(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
|
||||
if (argc != 0) {
|
||||
chprintf(chp, "usage: fsync\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file == nullptr) {
|
||||
chprintf(chp, "no open file\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto error = shell_file->sync();
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_tell(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
|
||||
if (argc != 0) {
|
||||
chprintf(chp, "usage: ftell\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file == nullptr) {
|
||||
chprintf(chp, "no open file\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto current_position = shell_file->tell();
|
||||
|
||||
chprintf(chp, "%d\r\n", current_position);
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_read(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: fread <number of bytes>\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file == nullptr) {
|
||||
chprintf(chp, "no open file\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int size = (int)strtol(argv[0], NULL, 10);
|
||||
|
||||
uint8_t buffer[62];
|
||||
|
||||
do {
|
||||
File::Size bytes_to_read = size > 62 ? 62 : size;
|
||||
auto bytes_read = shell_file->read(buffer, bytes_to_read);
|
||||
if (report_on_error(chp, bytes_read)) return;
|
||||
|
||||
std::string res = to_string_hex_array(buffer, bytes_read.value());
|
||||
res += "\r\n";
|
||||
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)res.c_str(), res.size());
|
||||
if (bytes_to_read != bytes_read.value())
|
||||
return;
|
||||
|
||||
size -= bytes_to_read;
|
||||
} while (size > 0);
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_read_binary(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: frb <number of bytes>\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file == nullptr) {
|
||||
chprintf(chp, "no open file\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int size = (int)strtol(argv[0], NULL, 10);
|
||||
|
||||
uint8_t buffer[64];
|
||||
|
||||
do {
|
||||
File::Size bytes_to_read = size > 64 ? 64 : size;
|
||||
auto bytes_read = shell_file->read(buffer, bytes_to_read);
|
||||
if (report_on_error(chp, bytes_read)) return;
|
||||
|
||||
if (bytes_read.value() > 0)
|
||||
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, buffer, bytes_read.value());
|
||||
|
||||
if (bytes_to_read != bytes_read.value())
|
||||
return;
|
||||
|
||||
size -= bytes_to_read;
|
||||
} while (size > 0);
|
||||
chprintf(chp, "\r\nok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage = "usage: fwrite 0123456789ABCDEF\r\n";
|
||||
if (argc != 1) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file == nullptr) {
|
||||
chprintf(chp, "no open file\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t data_string_len = strlen(argv[0]);
|
||||
if (data_string_len % 2 != 0) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < data_string_len; i++) {
|
||||
char c = argv[0][i];
|
||||
if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[3] = {0, 0, 0};
|
||||
|
||||
for (size_t i = 0; i < data_string_len / 2; i++) {
|
||||
buffer[0] = argv[0][i * 2];
|
||||
buffer[1] = argv[0][i * 2 + 1];
|
||||
uint8_t value = (uint8_t)strtol(buffer, NULL, 16);
|
||||
auto error = shell_file->write(&value, 1);
|
||||
if (report_on_error(chp, error)) return;
|
||||
}
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_write_binary(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage = "usage: fwb <number of bytes>\r\nfollowed by <number of bytes> of data";
|
||||
if (argc != 1) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell_file == nullptr) {
|
||||
chprintf(chp, "no open file\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = (size_t)strtol(argv[0], NULL, 10);
|
||||
|
||||
chprintf(chp, "send %d bytes\r\n", size);
|
||||
|
||||
uint8_t buffer[USB_BULK_BUFFER_SIZE];
|
||||
|
||||
do {
|
||||
size_t bytes_to_read = size > USB_BULK_BUFFER_SIZE ? USB_BULK_BUFFER_SIZE : size;
|
||||
size_t bytes_read = chSequentialStreamRead(chp, &buffer[0], bytes_to_read);
|
||||
if (bytes_read != bytes_to_read)
|
||||
return;
|
||||
|
||||
auto error = shell_file->write(&buffer[0], bytes_read);
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
size -= bytes_read;
|
||||
} while (size > 0);
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
|
||||
#include "ff.h"
|
||||
#include "file.hpp"
|
||||
|
||||
void cmd_sd_list_dir(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_unlink(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_mkdir(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_filesize(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_open(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_seek(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_close(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_truncate(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_sync(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_tell(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_read(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_read_binary(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_write_binary(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
|
||||
static std::filesystem::path path_from_string8(char* path) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conv;
|
||||
return conv.from_bytes(path);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
#define USB_SERIAL_SHELL_SD_COMMANDS \
|
||||
{"ls", cmd_sd_list_dir}, \
|
||||
{"unlink", cmd_sd_unlink}, \
|
||||
{"mkdir", cmd_sd_mkdir}, \
|
||||
{"filesize", cmd_sd_filesize}, \
|
||||
{"fopen", cmd_sd_open}, \
|
||||
{"fseek", cmd_sd_seek}, \
|
||||
{"fclose", cmd_sd_close}, \
|
||||
{"ftruncate", cmd_sd_truncate}, \
|
||||
{"fsync", cmd_sd_sync}, \
|
||||
{"ftell", cmd_sd_tell}, \
|
||||
{"fread", cmd_sd_read}, \
|
||||
{"frb", cmd_sd_read_binary}, \
|
||||
{"fwrite", cmd_sd_write}, \
|
||||
{"fwb", cmd_sd_write_binary}
|
||||
// clang-format on
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "optional.hpp"
|
||||
|
||||
#include "chprintf.h"
|
||||
#include "usb_serial_io.h"
|
||||
#include "usb_serial_device_to_host.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
@@ -476,13 +476,6 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PREP replay)
|
||||
|
||||
### GPS Simulator
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_gps_sim.cpp
|
||||
)
|
||||
DeclareTargets(PGPS gps_sim)
|
||||
|
||||
### Signal generator
|
||||
|
||||
set(MODE_CPPSRC
|
||||
@@ -490,12 +483,6 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PSIG siggen)
|
||||
|
||||
### Spectrum painter
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_spectrum_painter.cpp
|
||||
)
|
||||
DeclareTargets(PSPT spectrum_painter)
|
||||
|
||||
### SSTV TX
|
||||
|
||||
@@ -637,6 +624,20 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PAMT am_tv)
|
||||
|
||||
### GPS Simulator
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_gps_sim.cpp
|
||||
)
|
||||
DeclareTargets(PGPS gps_sim)
|
||||
|
||||
### Spectrum painter
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_spectrum_painter.cpp
|
||||
)
|
||||
DeclareTargets(PSPT spectrum_painter)
|
||||
|
||||
### Test
|
||||
|
||||
set(MODE_CPPSRC
|
||||
|
||||
@@ -123,10 +123,10 @@ constexpr gpdma::channel::Config config_rx() {
|
||||
/* TODO: Clean up terminology around "buffer", "transfer", "samples" */
|
||||
|
||||
constexpr size_t buffer_samples_log2n = 7;
|
||||
constexpr size_t buffer_samples = (1 << buffer_samples_log2n);
|
||||
constexpr size_t buffer_samples = (1 << buffer_samples_log2n); // 2^7 = 128 byte circular DMA buffer
|
||||
constexpr size_t transfers_per_buffer_log2n = 2;
|
||||
constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n);
|
||||
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer;
|
||||
constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n); // 2^2 = 4 transfer buffers in the circular buffer
|
||||
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer; // 128/4 = 32 samples in each transfer buffer
|
||||
constexpr size_t transfers_mask = transfers_per_buffer - 1;
|
||||
|
||||
constexpr size_t buffer_bytes = buffer_samples * sizeof(sample_t);
|
||||
@@ -144,6 +144,8 @@ static constexpr auto& gpdma_channel_i2s0_rx = gpdma::channels[portapack::i2s0_r
|
||||
static volatile const gpdma::channel::LLI* tx_next_lli = nullptr;
|
||||
static volatile const gpdma::channel::LLI* rx_next_lli = nullptr;
|
||||
|
||||
static bool single_tx_buffer = false;
|
||||
|
||||
static void tx_transfer_complete() {
|
||||
tx_next_lli = gpdma_channel_i2s0_tx.next_lli();
|
||||
}
|
||||
@@ -213,11 +215,16 @@ void disable() {
|
||||
gpdma_channel_i2s0_rx.disable();
|
||||
}
|
||||
|
||||
void shrink_tx_buffer() {
|
||||
single_tx_buffer = true;
|
||||
lli_tx_loop[0].lli = lli_pointer(&lli_tx_loop[0]);
|
||||
}
|
||||
|
||||
buffer_t tx_empty_buffer() {
|
||||
const auto next_lli = tx_next_lli;
|
||||
if (next_lli) {
|
||||
const size_t next_index = next_lli - &lli_tx_loop[0];
|
||||
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
|
||||
const size_t free_index = (single_tx_buffer) ? 0 : (next_index + transfers_per_buffer - 2) & transfers_mask;
|
||||
return {reinterpret_cast<sample_t*>(lli_tx_loop[free_index].srcaddr), transfer_samples};
|
||||
} else {
|
||||
return {nullptr, 0};
|
||||
|
||||
@@ -47,6 +47,7 @@ void init();
|
||||
void configure();
|
||||
void enable();
|
||||
void disable();
|
||||
void shrink_tx_buffer();
|
||||
|
||||
audio::buffer_t tx_empty_buffer();
|
||||
audio::buffer_t rx_empty_buffer();
|
||||
|
||||
@@ -95,6 +95,20 @@ void BasebandThread::run() {
|
||||
buffer_c8_t buffer{
|
||||
buffer_tmp.p, buffer_tmp.count, sampling_rate_};
|
||||
|
||||
if (shared_memory.request_m4_performance_counter == 0x02) {
|
||||
uint8_t max = shared_memory.m4_performance_counter;
|
||||
for (size_t i = 0; i < buffer_tmp.count; i++) {
|
||||
int8_t a = buffer_tmp.p[i].real();
|
||||
if (a < 0)
|
||||
a = -a;
|
||||
|
||||
if (a > max)
|
||||
max = a;
|
||||
}
|
||||
|
||||
shared_memory.m4_performance_counter = max;
|
||||
}
|
||||
|
||||
if (baseband_processor_) {
|
||||
baseband_processor_->execute(buffer);
|
||||
}
|
||||
|
||||
@@ -135,13 +135,17 @@ void update_performance_counters() {
|
||||
|
||||
last_paint_state = !last_paint_state;
|
||||
|
||||
auto utilisation = get_cpu_utilisation_in_percent();
|
||||
auto free_stack = (uint32_t)get_free_stack_space();
|
||||
auto free_heap = chCoreStatus();
|
||||
if (performance_counter_active == 0x01) {
|
||||
auto utilisation = get_cpu_utilisation_in_percent();
|
||||
auto free_stack = (uint32_t)get_free_stack_space();
|
||||
auto free_heap = chCoreStatus();
|
||||
|
||||
shared_memory.m4_cpu_usage = utilisation;
|
||||
shared_memory.m4_stack_usage = free_stack;
|
||||
shared_memory.m4_heap_usage = free_heap;
|
||||
shared_memory.m4_performance_counter = utilisation;
|
||||
shared_memory.m4_stack_usage = free_stack;
|
||||
shared_memory.m4_heap_usage = free_heap;
|
||||
} else if (performance_counter_active == 0x02) {
|
||||
shared_memory.m4_performance_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -41,6 +41,12 @@ void AudioTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
// Output to speaker too
|
||||
uint32_t imod32 = i & (AUDIO_OUTPUT_BUFFER_SIZE - 1);
|
||||
audio_data[imod32] = ((int16_t)audio_sample - 0x80) * (1.0f / 128.0f);
|
||||
if (imod32 == (AUDIO_OUTPUT_BUFFER_SIZE - 1))
|
||||
audio_output.write(audio_buffer);
|
||||
|
||||
sample = tone_gen.process(audio_sample - 0x80);
|
||||
|
||||
// FM
|
||||
@@ -95,6 +101,8 @@ void AudioTXProcessor::audio_config(const AudioTXConfigMessage& message) {
|
||||
tone_gen.configure(message.tone_key_delta, message.tone_key_mix_weight);
|
||||
progress_interval_samples = message.divider;
|
||||
resample_acc = 0;
|
||||
audio_output.configure(false);
|
||||
audio::dma::shrink_tx_buffer();
|
||||
}
|
||||
|
||||
void AudioTXProcessor::replay_config(const ReplayConfigMessage& message) {
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
#include "baseband_thread.hpp"
|
||||
#include "tone_gen.hpp"
|
||||
#include "stream_output.hpp"
|
||||
#include "audio_output.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#define AUDIO_OUTPUT_BUFFER_SIZE 32
|
||||
|
||||
class AudioTXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
@@ -48,6 +52,10 @@ class AudioTXProcessor : public BasebandProcessor {
|
||||
int32_t sample{0}, delta{};
|
||||
int8_t re{0}, im{0};
|
||||
|
||||
float audio_data[AUDIO_OUTPUT_BUFFER_SIZE];
|
||||
buffer_f32_t audio_buffer{audio_data, AUDIO_OUTPUT_BUFFER_SIZE, 48000};
|
||||
AudioOutput audio_output{};
|
||||
|
||||
size_t progress_interval_samples = 0, progress_samples = 0;
|
||||
|
||||
bool configured{false};
|
||||
|
||||
@@ -186,7 +186,7 @@ uint8_t usb_descriptor_string_manufacturer[] = {
|
||||
|
||||
uint8_t usb_descriptor_string_product[] = {
|
||||
#ifdef HACKRF_ONE
|
||||
43, // bLength
|
||||
34, // bLength
|
||||
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
|
||||
'P', 0x00,
|
||||
'o', 0x00,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user