Compare commits

...

54 Commits

Author SHA1 Message Date
John Stockdale fe88760766 First version of rtcget and rtcset (#1727) 2024-01-06 09:15:59 +01:00
E.T 75ece38725 Dockerfile overhaul (#1726)
Create entrypoint to orchestrate the build steps
Supported commands: make, ninja
Passes additional arguments to the make / ninja command at the end (like -jNN)
There is a shortcut to make -jNN by just specifying -jNN
Anything else will be directly executed (like getting a shell into the container with bash -li is still possible)
2024-01-06 00:42:36 +01:00
E.T b53032a8a5 Ppfw docker build (#1725)
* Docker build ppfw by default

* Allow GH workflows to be triggered from any branch

* Fix asset path for github upload
2024-01-05 21:59:15 +01:00
Mark Thompson e7f8952ece Fix compile error in deprecated app (#1723) 2024-01-05 10:13:43 -06:00
Totoo 82a6ae0791 Geomap speed display (#1722)
* speed to geomap
* Add speed to map
* Fix hidden state
* UI fix on ADSB tx
* UI fixes
2024-01-05 13:44:30 +01:00
Totoo d303098e35 Moved SPainter and GPSSim to ext app (#1721)
* Moved GPSSim
* Moved spainter
2024-01-05 10:46:09 +01:00
Totoo 1a69ce2d97 Accessibility over serial (#1717)
* Initial accessibility support
* added it to some widgets to test
* More widget accessibility response
* More widgets, better output
* Mark selected widget on list
* typo
2024-01-05 07:43:30 +01:00
Erwin Ried eedebe1c52 Update your links (#1719) 2024-01-05 07:43:13 +01:00
E.T 9e61f80809 Create cmake target ppfw (#1716)
* Create cmake target ppfw and oci

It will build an OCI ppfw package to be flashed or shared on test-drive

Additionally some minor cmake cleanup
And updated gitignore to allow multiple build folders

* Make ppfw / oci build default
2024-01-04 22:59:25 +01:00
Mark Thompson ff591c68a0 Updated Flash message to say device will restart (#1715) 2024-01-04 11:47:45 -06:00
Totoo 8761b9d7e0 Send ASCII chars from USB serial to selected widget (#1708)
* Initial commit for keyboard emulation
* Added on_keyboard to some widgets
* TextEdit partly
* Multi key send at once
* Frequency control support
* Fix encoder emulation
* Add keyboard to geomap
* More widgets
2024-01-04 17:36:31 +01:00
Erwin Ried 1b5125b0a8 Adding the new TechMinds video (#1712)
I think it explains very well everything, even if he calls it "Portapak"
2024-01-04 11:28:33 +01:00
E.T 9ba885361d add _OCI to ppfw package name (#1711) 2024-01-04 20:52:42 +13:00
Mark Thompson afa34d83b2 Added Zoom-Out capability to Geomap (#1710) 2024-01-04 07:32:44 +01:00
E.T d93c914dd3 Create firmware ppfw.tar to flash firmware together with apps (#1709)
* Github actions test

* Action to create and upload ppfw.tar

+remove unused travis config

* Changelog token fix

* Also generate ppfw for stable releases

* Make tar verbose
2024-01-03 22:32:34 +01:00
Mark Thompson 715a2dd448 Add Tamper Flags to ERT RX display and meter ID to log file (#1707) 2024-01-03 20:17:33 +01:00
Totoo 58bf60695d Touch emulation from usb cdc (#1706) 2024-01-03 14:06:29 +01:00
Totoo fbe7954f2e Added support for extracting firmware from TAR file (with apps) (#1704)
* Added support for extracting firmware from TAR file (with apps)
* Added tar to usb cdc firmware command.
* Serial flash tar
* Show error on bad tar
* Check tar for valid filenames
2024-01-03 14:05:24 +01:00
tomvandermussele d122a8fc3b Created Belgian Airband file (#1705)
* Created file in FREQMAN for the Belgian Airband (Brussels Airport)

* Added Military to BE Airband
2024-01-03 12:02:04 +01:00
Bernd Herzog 3998dc124a added cpld info shell commands (#1703)
* added cpld info shell commands

* fixed reset
2024-01-02 00:18:53 +01:00
jLynx 3d2da9c0db Serial info command (#1700)
* WIP adding more info

* Added source output & R9 detection

* WIP

* WIP

* Moved hackrf back

* Fixed output error
2024-01-02 07:25:36 +13:00
Erwin Ried 3189d3af4c Update FUNDING.yml (#1702)
Removing bountysource, since they scammed everyone: https://github.com/bountysource/core/issues/1586
2024-01-01 15:14:37 +01:00
E.T 1b3a99cc5e Gcc warning onscreen (#1701)
* Fix GCC version detection for old CMAKE

* Yellow tint version number / hash on gcc mismatch
2023-12-31 15:46:26 -06:00
E.T 935c43e88f Gcc version warning (#1698)
* Compiler warning
* Fix warning message
2023-12-31 16:36:19 +01:00
gullradriel 2893c031ab Recon repeat delay (#1699)
- Recon repeat delay, range: from 0s to 254s, timer is played before each TX
- fixed missing '<' '>' around button names for DELETE and REMOVE name to indicate that they can also be used to skip in the list
- added missing pmem recon dump entries
2023-12-30 22:32:41 +01:00
E.T 94cdb16ca9 Mention external apps in stable release notes (#1694) 2023-12-29 15:11:40 -06:00
jLynx 84d4066986 V1.9.1 (#1691)
* Update past_version.txt

* Update version.txt
2023-12-29 22:07:43 +13:00
Mark Thompson 2153c2dd10 ERT app - Fix freq field overlapping amp setting (#1689) 2023-12-29 07:28:42 +01:00
gullradriel a7393c3492 fix for freq_b equal zero in repeater entries (#1686) 2023-12-28 20:10:38 +01:00
gullradriel 794fece8cc Recon raw repeat (#1658)
* UI for Recon Repeater config
* persistent recon repeat settings
* record_view: added possibility to force keep the provided record filename
* working auto record to same file
* Recon TX 
* added Repeater type
* adding yellow coloring on Repeater config+a modal, comments in the code
* default repeater values
2023-12-28 11:25:53 +01:00
Totoo 1bf95e85a0 Fixed #1670 (#1676) 2023-12-26 07:16:54 +01:00
Brumi-2021 a85357a8af Minor Improvement (Extending_min_CLK_out_from_10khz_to_4khz) (#1675) 2023-12-26 07:11:14 +01:00
Totoo 7bf3e02f6c Faster usb serial file download (#1674)
* Made file read much faster via USB serial
* Remove old code
2023-12-25 17:25:16 +01:00
Mark Thompson 459e8d0b24 Disallow CLKOUT freq changes on r9 (#1672) 2023-12-24 17:28:22 +01:00
Mark Thompson 93585d846a CLKOUT workaround for r9 boards (#1671) 2023-12-24 11:06:11 +01:00
Mark Thompson 4eb5c4603e Simplify CLKOUT frequency entry (#1668) 2023-12-24 08:55:15 +01:00
Mark Thompson f42ad5ffc6 Configure CLKOUT at power-up per PMEM setting (#1669) 2023-12-24 08:54:56 +01:00
gullradriel d0bd8e50be Added USB serial checkbox (#1664) 2023-12-22 11:00:27 +01:00
Mark Thompson 337e3f4449 Enhance Debug->Peripherals to view all registers of SI5351 chip (#1663) 2023-12-22 08:45:05 +01:00
jLynx 53bbf74f4d V1.9.0 (#1660)
* Update version.txt

* Update past_version.txt
2023-12-22 07:35:54 +13:00
Mark Thompson 19a66fac20 Enable Sample Rate to be spec'd in .ini file for Weather & SubGhzDecoder apps (#1659) 2023-12-21 19:33:06 +01:00
Totoo 36e1b9a36e Portapack USB screen tx (#1653)
* implemented 2 screen read functions, and haxfixed windows usb bug
* Support up to 64 byte per packet for faster tx
* Added filesize command
2023-12-19 22:25:22 +01:00
Netro 1ca9288bbe initial USB serial thread integrated to BLE RX (#1651) 2023-12-19 22:20:13 +01:00
Mark Thompson 4ca2d1e352 Fix compile error (#1650) 2023-12-18 07:35:00 +01:00
gullradriel 0b11730835 fix wrong direction in tx freq correction (#1649)
Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-12-17 18:47:20 -06:00
Bernd Herzog 6069145b68 Usb serial (#1648)
* enabled usb clock
* added usb stack to application
* fixed pll0usb clock setup
* implemented serial usb handshake
* implemented serial communication
* integrated chibios shell
* implemented device reset
* implemented enter dfu mode
* implemented hackrf mode command
* implemented flash command
* implemented memory manipulation
* implemented button control
* fixed mode change
* improved reset behavior
* implemented directory commands
* implemented file commands
* improved data communication
* refactorings
2023-12-17 17:20:35 +01:00
Totoo 2ccda5aebd Subghz decoder (#1646)
* Initial commit - wip

* Half part of the transition of baseband processor.

* More SGD

* WIP, Weather refactor, UI improv

* Rename

* Added 4msps, and fixes

* Fixes

* princeton working

* Renamed proc_weather, bc now multifunctional

* Proto: bett

* FPS_CAME = 4,
    FPS_PRASTEL = 5,
    FPS_AIRFORCE = 6,

* Came Atomo, fixes

* Separate weather and sgd, bc of baseband size limit

* Fix display

* Save space

* More protos

* Dooya proto added

* More protos

* add protos

* More protos

* Move weather to ext app

* nw

* Revert "Move weather to ext app"

This reverts commit 8a84aac2f5.

* revert

* Fix merge

* Better naming

* More protos

* More protos

* Add protos

* Fix warning

* Add NeroRadio

* more protos

* more protos

* More protos

* Shrink a bit

* fixes

* More protos

* Nicer code

* Fix naming

* Fix format

* Remove unused

* Fix some protos, that needs a LOOOONG part with the same lo/high

* Modify key calculation
2023-12-16 16:37:51 -06:00
Totoo 02810bf527 Move Jammer ext app (with baseband image too) (#1647) 2023-12-15 20:18:21 +01:00
Totoo b58ee761a1 Move more apps + language module (#1643)
* CoasterP to ext (Burger pager)
* LGE to ext app
* Solve compiler literal removal with centralizing common string literals.
2023-12-12 16:55:50 +01:00
Totoo 5b9d898202 Fix oregon2 weather protocol (#1642) 2023-12-12 06:45:56 +01:00
Totoo 5f8e44e7b7 NRF RX to ext app (#1641)
* NFR Rx to ext app
2023-12-11 11:57:04 +01:00
Totoo ec80859d93 AnalogTV to ext (#1638) 2023-12-11 08:55:07 +01:00
Totoo 5e9d92d65c Set sr to 4m (#1636)
* Set sr to 4m

* Rename variable
2023-12-10 23:40:11 +01:00
Mark Thompson c3bb9c0a16 Weather display beautification (#1635) 2023-12-10 17:57:21 +01:00
213 changed files with 11553 additions and 1404 deletions
+1 -1
View File
@@ -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
+17 -2
View File
@@ -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: |
@@ -64,7 +69,7 @@ jobs:
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
- 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
+20 -4
View File
@@ -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: |
@@ -48,7 +53,7 @@ jobs:
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
- 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'}"
@@ -74,13 +79,24 @@ jobs:
**Full Changelog**: https://github.com/eried/portapack-mayhem/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/eried/portapack-havoc/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
View File
@@ -1 +1 @@
v1.7.4
v1.9.0
+1 -1
View File
@@ -1 +1 @@
v1.8.0
v1.9.1
+1 -1
View File
@@ -49,7 +49,7 @@
*.map
*.lst
.dep/
build/
/build*
CMakeFiles/
# Debugging
-55
View File
@@ -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
View File
@@ -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)
+6 -1
View File
@@ -1,3 +1,6 @@
> [!IMPORTANT]
> *Shortly*, this repository **will be moved** from my personal GitHub [eried/portapack-mayhem](https://github.com/eried/portapack-mayhem) to an organization: [portapack-mayhem/portapack-mayhem](https://github.com/portapack-mayhem/portapack-mayhem). Please keep this in mind to **update your links** accordingly.
# PortaPack Mayhem
[![Build Status](https://travis-ci.com/eried/portapack-mayhem.svg?branch=master)](https://travis-ci.com/eried/portapack-mayhem) [![Nightly Release](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml/badge.svg?branch=next)](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/total)](https://github.com/eried/portapack-mayhem/releases) [![GitHub Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/latest/total)](https://github.com/eried/portapack-mayhem/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://dcbadge.vercel.app/api/server/tuwVMv3?style=flat)](https://discord.gg/tuwVMv3)
@@ -12,7 +15,9 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
If you are new to *HackRF+PortaPack+Mayhem*, check this video:
[![What is?](https://img.youtube.com/vi/alrFbY5vxt4/0.jpg)](https://www.youtube.com/watch?v=alrFbY5vxt4)
[![Beginner's Guide To The HackRF & Portapak With Mayhem](https://img.youtube.com/vi/H-bqdWfbhpg/0.jpg)](https://www.youtube.com/watch?v=H-bqdWfbhpg)
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
+22 -14
View File
@@ -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"]
+1 -1
View File
@@ -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
View File
@@ -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"]
+50 -1
View File
@@ -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,12 +61,22 @@ 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
COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_DFU_IMAGE} || (exit 0) # We need to add it for dfu-utils v.011 , (in v.09 it is not necessary)
COMMAND sleep 3s
COMMAND hackrf_spiflash -i -R -w ${FIRMWARE_FILENAME}
@@ -67,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
@@ -86,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
)
+32 -13
View File
@@ -113,12 +113,23 @@ set(CSRC
${BOARDSRC}
${FATFSSRC}
firmware_info.c
usb_serial_cdc.c
usb_serial_descriptor.c
usb_serial_endpoints.c
usb_serial_io.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/chprintf.c
)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
set(CPPSRC
main.cpp
shell.cpp
${COMMON}/acars_packet.cpp
${COMMON}/adsb.cpp
${COMMON}/adsb_frame.cpp
@@ -162,6 +173,7 @@ set(CPPSRC
${COMMON}/ui_painter.cpp
${COMMON}/ui_text.cpp
${COMMON}/ui_widget.cpp
${COMMON}/ui_language.cpp
${COMMON}/utility.cpp
${COMMON}/wm8731.cpp
${COMMON}/performance_counter.cpp
@@ -190,6 +202,10 @@ set(CPPSRC
log_file.cpp
metadata_file.cpp
portapack.cpp
usb_serial_shell.cpp
usb_serial_event.cpp
usb_serial_thread.cpp
usb_serial.cpp
qrcodegen.cpp
radio.cpp
receiver_model.cpp
@@ -243,15 +259,14 @@ set(CPPSRC
apps/acars_app.cpp
apps/ais_app.cpp
apps/analog_audio_app.cpp
apps/analog_tv_app.cpp
# apps/analog_tv_app.cpp
apps/ble_comm_app.cpp
apps/ble_rx_app.cpp
apps/ble_tx_app.cpp
apps/capture_app.cpp
apps/ert_app.cpp
apps/gps_sim_app.cpp
apps/lge_app.cpp
apps/lge_app.cpp
# apps/gps_sim_app.cpp
# apps/lge_app.cpp
apps/pocsag_app.cpp
# apps/replay_app.cpp
apps/soundboard_app.cpp
@@ -264,7 +279,7 @@ set(CPPSRC
apps/ui_aprs_tx.cpp
apps/ui_bht_tx.cpp
apps/ui_btle_rx.cpp
apps/ui_coasterp.cpp
# apps/ui_coasterp.cpp
apps/ui_debug.cpp
apps/ui_dfu_menu.cpp
apps/ui_encoders.cpp
@@ -273,15 +288,15 @@ set(CPPSRC
apps/ui_freqman.cpp
apps/ui_fsk_rx.cpp
apps/ui_iq_trim.cpp
apps/ui_jammer.cpp
# apps/ui_jammer.cpp
# apps/ui_keyfob.cpp
apps/ui_lcr.cpp
# apps/ui_lcr.cpp
apps/ui_level.cpp
apps/ui_looking_glass_app.cpp
apps/ui_mictx.cpp
apps/ui_modemsetup.cpp
apps/ui_morse.cpp
apps/ui_nrf_rx.cpp
# apps/ui_nrf_rx.cpp
# apps/ui_nuoptix.cpp
apps/ui_playlist.cpp
apps/ui_pocsag_tx.cpp
@@ -296,11 +311,12 @@ 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
# apps/ui_test.cpp
apps/ui_text_editor.cpp
apps/ui_tone_search.cpp
@@ -314,7 +330,7 @@ set(CPPSRC
protocols/bht.cpp
protocols/dcs.cpp
protocols/encoders.cpp
protocols/lcr.cpp
# protocols/lcr.cpp
protocols/modems.cpp
protocols/rds.cpp
# ui_handwrite.cpp
@@ -362,6 +378,9 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC
${HALINC} ${PLATFORMINC} ${BOARDINC}
${FATFSINC}
${CHIBIOS}/os/various
${HACKRF_PATH}/firmware/libopencm3/include
${HACKRF_PATH}/firmware/common
${HACKRF_PATH}/firmware
ui
hw
apps
@@ -405,7 +424,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)
+2 -1
View File
@@ -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() {
+21 -15
View File
@@ -426,6 +426,7 @@ BLERxView::BLERxView(NavigationView& nav)
&options_sort,
&label_found,
&text_found_count,
&check_serial_log,
&button_filter,
&button_save_list,
&button_clear_list,
@@ -436,6 +437,16 @@ BLERxView::BLERxView(NavigationView& nav)
nav_.push<BleRecentEntryDetailView>(entry);
};
check_serial_log.on_select = [this](Checkbox&, bool v) {
serial_logging = v;
if (v) {
usb_serial_thread = std::make_unique<UsbSerialThread>();
} else {
usb_serial_thread.reset();
}
};
check_serial_log.set_value(serial_logging);
ensure_directory(find_packet_path);
ensure_directory(log_packets_path);
ensure_directory(packet_save_path);
@@ -454,8 +465,6 @@ BLERxView::BLERxView(NavigationView& nav)
logger = std::make_unique<BLELogger>();
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
str_log = "";
logging = v;
@@ -466,6 +475,7 @@ BLERxView::BLERxView(NavigationView& nav)
"/BLELOG_" +
to_string_timestamp(rtc_time::now()) + ".TXT");
};
check_log.set_value(logging);
button_save_list.on_select = [this, &nav](const ui::Button&) {
listFileBuffer = "";
@@ -684,24 +694,16 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
}
void BLERxView::on_data(BlePacketData* packet) {
std::string str_console = "";
if (!logging) {
str_log = "";
}
str_console += pdu_type_to_string((ADV_PDU_TYPE)packet->type);
str_console += " Len:";
str_console += to_string_dec_uint(packet->size);
str_console += "\n";
str_console += "Mac:";
str_console += " Mac:";
str_console += to_string_mac_address(packet->macAddress, 6, false);
str_console += "\n";
str_console += "Data:";
str_console += " Data:";
int i;
@@ -709,8 +711,6 @@ void BLERxView::on_data(BlePacketData* packet) {
str_console += to_string_hex(packet->data[i], 2);
}
str_console += "\n";
uint64_t macAddressEncoded = copy_mac_address_to_uint64(packet->macAddress);
// Start of Packet stuffing.
@@ -728,9 +728,15 @@ void BLERxView::on_data(BlePacketData* packet) {
// Log at End of Packet.
if (logger && logging) {
logger->log_raw_data(str_console);
logger->log_raw_data(str_console + "\r\n");
}
if (serial_logging) {
usb_serial_thread->serial_str = str_console + "\r\n";
usb_serial_thread->str_ready = true;
}
str_console = "";
if (!searchList.empty()) {
auto it = searchList.begin();
+13
View File
@@ -35,6 +35,7 @@
#include "radio_state.hpp"
#include "log_file.hpp"
#include "utility.hpp"
#include "usb_serial_thread.hpp"
#include "recent_entries.hpp"
@@ -194,6 +195,7 @@ class BLERxView : public View {
std::string build_line_str(BleRecentEntry entry);
void on_save_file(const std::string value);
bool saveFile(const std::filesystem::path& path);
std::unique_ptr<UsbSerialThread> usb_serial_thread{};
void on_data(BlePacketData* packetData);
void on_filter_change(std::string value);
void on_file_changed(const std::filesystem::path& new_file_path);
@@ -213,6 +215,8 @@ class BLERxView : public View {
uint8_t sort_index{0};
std::string filter{};
bool logging{false};
bool serial_logging{false};
bool name_enable{true};
app_settings::SettingsManager settings_{
"rx_ble",
@@ -222,9 +226,12 @@ class BLERxView : public View {
{"sort_index"sv, &sort_index},
{"filter"sv, &filter},
{"log"sv, &logging},
// disabled to always start without USB serial activated until we can make it non blocking if not connected
// {"serial_log"sv, &serial_logging},
{"name"sv, &name_enable},
}};
std::string str_console = "";
uint8_t console_color{0};
uint32_t prev_value{0};
uint8_t channel_number = 37;
@@ -316,6 +323,12 @@ class BLERxView : public View {
{11 * 8, 3 * 16, 20 * 8, 16},
"0/0"};
Checkbox check_serial_log{
{17 * 8, 3 * 16 - 2},
7,
"USB Log",
true};
Console console{
{0, 4 * 16, 240, 240}};
+16 -10
View File
@@ -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);
+8 -6
View File
@@ -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,16 +138,17 @@ 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};
static constexpr auto header_height = 1 * 16;
RxFrequencyField field_frequency{
{5 * 8, 0 * 16},
{0 * 8, 0 * 16},
nav_};
RFAmpField field_rf_amp{
+2 -1
View File
@@ -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();
+3 -1
View File
@@ -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);
});
+2 -1
View File
@@ -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},
+2 -1
View File
@@ -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,*/
+25 -14
View File
@@ -176,12 +176,15 @@ void RegistersWidget::paint(Painter& painter) {
void RegistersWidget::draw_legend(const Coord left, Painter& painter) {
const auto pos = screen_pos();
const std::string spaces(config.legend_length(), ' ');
for (uint32_t i = 0; i < config.registers_per_page; i += config.registers_per_row()) {
uint32_t r = page_number * config.registers_per_page + i;
for (uint32_t i = 0; i < config.registers_count; i += config.registers_per_row()) {
const Point offset{
left, static_cast<int>((i / config.registers_per_row()) * row_height)};
const auto text = to_string_hex(i, config.legend_length());
const auto text = (r >= config.registers_count) ? spaces : to_string_hex(r, config.legend_length());
painter.draw_string(
pos + offset,
style().invert(),
@@ -193,15 +196,16 @@ void RegistersWidget::draw_values(
const Coord left,
Painter& painter) {
const auto pos = screen_pos();
const std::string spaces(config.value_length(), ' ');
for (uint32_t i = 0; i < config.registers_per_page; i++) {
uint32_t r = page_number * config.registers_per_page + i;
for (uint32_t i = 0; i < config.registers_count; i++) {
const Point offset = {
static_cast<int>(left + config.legend_width() + 8 + (i % config.registers_per_row()) * (config.value_width() + 8)),
static_cast<int>((i / config.registers_per_row()) * row_height)};
const auto value = reg_read(i);
const auto text = to_string_hex(value, config.value_length());
const auto text = (r >= config.registers_count) ? spaces : to_string_hex(reg_read(r), config.value_length());
painter.draw_string(
pos + offset,
style(),
@@ -213,7 +217,7 @@ uint32_t RegistersWidget::reg_read(const uint32_t register_number) {
if (register_number < config.registers_count) {
switch (config.chip_type) {
case CT_PMEM:
return portapack::persistent_memory::pmem_data_word((page_number * config.registers_count + register_number) / 4) >> (register_number % 4 * 8);
return portapack::persistent_memory::pmem_data_word(register_number / 4) >> (register_number % 4 * 8);
case CT_RFFC5072:
return radio::debug::first_if::register_read(register_number);
case CT_MAX283X:
@@ -272,6 +276,7 @@ RegistersView::RegistersView(
button_done.on_select = [&nav](Button&) { nav.pop(); };
registers_widget.set_parent_rect({0, 48, 240, 192});
registers_widget.set_page(0);
text_title.set_parent_rect({(240 - static_cast<int>(title.size()) * 8) / 2, 16,
static_cast<int>(title.size()) * 8, 16});
@@ -296,6 +301,13 @@ void RegistersView::focus() {
button_done.focus();
}
bool RegistersView::on_encoder(const EncoderEvent delta) {
registers_widget.set_page(std::max(0ul, std::min(registers_widget.page_count() - 1, registers_widget.page() + delta)));
registers_widget.update();
return true;
}
/* ControlsSwitchesWidget ************************************************/
void ControlsSwitchesWidget::on_show() {
@@ -429,10 +441,10 @@ DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
const char* max283x = hackrf_r9 ? "MAX2839" : "MAX2837";
const char* si5351x = hackrf_r9 ? "Si5351A" : "Si5351C";
add_items({
{"RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>("RFFC5072", RegistersWidgetConfig{CT_RFFC5072, 31, 16}); }},
{max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x]() { nav.push<RegistersView>(max283x, RegistersWidgetConfig{CT_MAX283X, 32, 10}); }},
{si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x]() { nav.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 96, 8}); }},
{audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_bits()}); }},
{"RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>("RFFC5072", RegistersWidgetConfig{CT_RFFC5072, 31, 31, 16}); }},
{max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x]() { nav.push<RegistersView>(max283x, RegistersWidgetConfig{CT_MAX283X, 32, 32, 10}); }},
{si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x]() { nav.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
{audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
});
set_max_rows(2); // allow wider buttons
}
@@ -504,8 +516,8 @@ void DebugMemoryDumpView::focus() {
/* DebugPmemView *********************************************************/
DebugPmemView::DebugPmemView(NavigationView& nav)
: registers_widget(RegistersWidgetConfig{CT_PMEM, page_size, 8}) {
add_children({&text_page, &registers_widget, &text_checksum, &text_checksum2, &button_ok});
: registers_widget(RegistersWidgetConfig{CT_PMEM, PMEM_SIZE_BYTES, page_size, 8}) {
add_children({&registers_widget, &text_checksum, &text_checksum2, &button_ok});
registers_widget.set_parent_rect({0, 32, 240, 192});
@@ -532,7 +544,6 @@ void DebugPmemView::focus() {
}
void DebugPmemView::update() {
text_page.set(to_string_hex(registers_widget.page() * page_size, 2) + "+");
registers_widget.update();
}
+4 -4
View File
@@ -142,6 +142,7 @@ typedef enum {
struct RegistersWidgetConfig {
chip_type_t chip_type;
uint32_t registers_count;
uint32_t registers_per_page;
uint32_t register_bits;
constexpr size_t legend_length() const {
@@ -194,6 +195,7 @@ class RegistersWidget : public Widget {
void set_page(int32_t value) { page_number = value; }
uint32_t page(void) { return page_number; }
uint32_t page_count(void) { return (config.registers_count + config.registers_per_page - 1) / config.registers_per_page; }
private:
const RegistersWidgetConfig config;
@@ -208,8 +210,8 @@ class RegistersWidget : public Widget {
class RegistersView : public View {
public:
RegistersView(NavigationView& nav, const std::string& title, RegistersWidgetConfig&& config);
void focus();
void focus() override;
bool on_encoder(const EncoderEvent delta) override;
private:
Text text_title{};
@@ -364,8 +366,6 @@ class DebugPmemView : public View {
static constexpr uint8_t page_size{96}; // Must be multiply of 4 otherwise bit shifting for register view wont work properly
static constexpr uint8_t page_count{(portapack::memory::map::backup_ram.size() + page_size - 1) / page_size};
Text text_page{{16, 16, 208, 16}};
RegistersWidget registers_widget;
Text text_checksum{{16, 232, 208, 16}};
+48 -1
View File
@@ -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 */
+2 -1
View File
@@ -412,7 +412,8 @@ void FrequencyEditView::refresh_ui() {
auto is_range = entry_.type == freqman_type::Range;
auto is_ham = entry_.type == freqman_type::HamRadio;
auto has_freq_b = is_range || is_ham;
auto is_repeater = entry_.type == freqman_type::Repeater;
auto has_freq_b = is_range || is_ham || is_repeater;
field_freq_b.set_style(has_freq_b ? &Styles::white : &Styles::grey);
field_step.set_style(is_range ? &Styles::white : &Styles::grey);
+2 -1
View File
@@ -220,7 +220,8 @@ class FrequencyEditView : public View {
{"Single", 0},
{"Range", 1},
{"HamRadio", 2},
{"Raw", 3},
{"Repeater", 3},
{"Raw", 4},
}};
FrequencyField field_freq_a{{13 * 8, 4 * 16}};
+2 -3
View File
@@ -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);
};
};
+277 -50
View File
@@ -22,13 +22,24 @@
*/
#include "ui_recon.hpp"
#include "ui_fileman.hpp"
#include "ui_freqman.hpp"
#include "capture_app.hpp"
#include "convert.hpp"
#include "file.hpp"
#include "file_reader.hpp"
#include "tone_key.hpp"
#include "replay_app.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "io_convert.hpp"
#include "oversample.hpp"
#include "baseband_api.hpp"
#include "metadata_file.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include "utility.hpp"
#include "replay_thread.hpp"
using namespace portapack;
using namespace tonekey;
@@ -37,6 +48,30 @@ namespace fs = std::filesystem;
namespace ui {
void ReconView::reload_restart_recon() {
frequency_file_load();
if (frequency_list.size() > 0) {
if (fwd) {
button_dir.set_text("FW>");
} else {
button_dir.set_text("<RW");
}
recon_resume();
}
if (scanner_mode) {
file_name.set_style(&Styles::red);
button_scanner_mode.set_style(&Styles::red);
button_scanner_mode.set_text("SCAN");
} else {
file_name.set_style(&Styles::blue);
button_scanner_mode.set_style(&Styles::blue);
button_scanner_mode.set_text("RECON");
}
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
file_name.set_style(&Styles::yellow);
}
}
void ReconView::check_update_ranges_from_current() {
if (frequency_list.size() && current_is_valid() && current_entry().type == freqman_type::Range) {
if (update_ranges && !manual_mode) {
@@ -67,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");
@@ -75,13 +110,17 @@ void ReconView::recon_stop_recording() {
button_audio_app.set_text("AUDIO");
button_audio_app.set_style(&Styles::white);
record_view->stop();
button_config.set_style(&Styles::white); // disable config while recording as it's causing an IO error pop up at exit
button_config.set_style(&Styles::white);
is_recording = false;
// repeater mode
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
@@ -111,6 +150,9 @@ void ReconView::update_description() {
case freqman_type::HamRadio:
description = "H: ";
break;
case freqman_type::Repeater:
description = "L: ";
break;
default:
description = "S: ";
}
@@ -182,6 +224,7 @@ void ReconView::load_persisted_settings() {
load_freqs = persistent_memory::recon_load_freqs();
load_ranges = persistent_memory::recon_load_ranges();
load_hamradios = persistent_memory::recon_load_hamradios();
load_repeaters = persistent_memory::recon_load_repeaters();
update_ranges = persistent_memory::recon_update_ranges_when_recon();
auto_record_locked = persistent_memory::recon_auto_record_locked();
}
@@ -265,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();
}
@@ -275,9 +325,20 @@ ReconView::~ReconView() {
ReconView::ReconView(NavigationView& nav)
: nav_{nav} {
chrono_start = chTimeNow();
tx_view.hidden(true);
// set record View
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO_", u"AUDIO",
u"AUTO_AUDIO", u"AUDIO",
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
record_view->set_auto_trim(false);
record_view->hidden(true);
record_view->on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
add_children({&labels,
&field_lna,
&field_vga,
@@ -314,13 +375,9 @@ ReconView::ReconView(NavigationView& nav)
&button_restart,
&button_mic_app,
&button_remove,
record_view.get()});
record_view->hidden(true);
record_view->set_filename_date_frequency(true);
record_view->on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
record_view.get(),
&progressbar,
&tx_view});
def_step = 0;
load_persisted_settings();
@@ -492,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;
@@ -566,27 +623,7 @@ ReconView::ReconView(NavigationView& nav)
};
button_restart.on_select = [this](Button&) {
frequency_file_load();
if (frequency_list.size() > 0) {
if (fwd) {
button_dir.set_text("FW>");
} else {
button_dir.set_text("<RW");
}
recon_resume();
}
if (scanner_mode) {
file_name.set_style(&Styles::red);
button_scanner_mode.set_style(&Styles::red);
button_scanner_mode.set_text("SCAN");
} else {
file_name.set_style(&Styles::blue);
button_scanner_mode.set_style(&Styles::blue);
button_scanner_mode.set_text("RECON");
}
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
file_name.set_style(&Styles::yellow);
}
reload_restart_recon();
};
button_add.on_select = [this](ButtonWithEncoder&) {
@@ -606,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) {
@@ -704,6 +741,9 @@ ReconView::ReconView(NavigationView& nav)
change_mode(AM_MODULATION); // start on AM.
field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector
// tx progress bar
progressbar.hidden(true);
if (filedelete) {
delete_file(freq_file_path);
}
@@ -741,7 +781,8 @@ void ReconView::frequency_file_load() {
freqman_load_options options{
.load_freqs = load_freqs,
.load_ranges = load_ranges,
.load_hamradios = load_hamradios};
.load_hamradios = load_hamradios,
.load_repeaters = load_repeaters};
if (!load_freqman_file(file_input, frequency_list, options) || frequency_list.empty()) {
file_name.set_style(&Styles::red);
desc_cycle.set("...empty file...");
@@ -776,6 +817,12 @@ void ReconView::frequency_file_load() {
}
void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (recon_tx)
return;
if (is_repeat_active())
return;
chrono_end = chTimeNow();
systime_t time_interval = chrono_end - chrono_start;
chrono_start = chrono_end;
@@ -804,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();
}
@@ -835,9 +882,9 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
// contents of a possible recon_start_recording(), but not yet since it's only called once
if (auto_record_locked && !is_recording) {
button_audio_app.set_style(&Styles::red);
if (field_mode.selected_index_value() == SPEC_MODULATION)
if (field_mode.selected_index_value() == SPEC_MODULATION) {
button_audio_app.set_text("RAW REC");
else
} else
button_audio_app.set_text("WAV REC");
record_view->start();
button_config.set_style(&Styles::light_grey); // disable config while recording as it's causing an IO error pop up at exit
@@ -956,6 +1003,26 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
}
}
}
} else if (current_entry().type == freqman_type::Repeater) {
// repeater is like single, we only listen on frequency_a and then jump to next entry
if ((fwd && stepper == 0) || stepper > 0) { // forward
current_index++;
entry_has_changed = true;
// looping
if ((uint32_t)current_index >= frequency_list.size()) {
has_looped = true;
current_index = 0;
}
} else if ((!fwd && stepper == 0) || stepper < 0) {
// reverse
current_index--;
entry_has_changed = true;
// if previous if under the list => go back from end
if (current_index < 0) {
has_looped = true;
current_index = frequency_list.size() - 1;
}
}
}
// set index to boundary if !continuous
if (has_looped && !continuous) {
@@ -984,6 +1051,7 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (entry_has_changed) {
timer = 0;
switch (current_entry().type) {
case freqman_type::Repeater:
case freqman_type::Single:
freq = current_entry().frequency_a;
break;
@@ -1017,7 +1085,7 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (stepper > 0) stepper--;
} // if( recon || stepper != 0 || index_stepper != 0 )
} // if (frequency_list.size() > 0 )
} /* on_statistic_updates */
} /* on_statistics_updates */
}
handle_retune();
recon_redraw();
@@ -1080,28 +1148,39 @@ void ReconView::on_stepper_delta(int32_t v) {
}
size_t ReconView::change_mode(freqman_index_t new_mod) {
if (recon_tx || is_repeat_active())
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();
remove_child(record_view.get());
record_view.reset();
if (new_mod == SPEC_MODULATION) {
recon_stop_recording(false);
if (record_view != nullptr) {
remove_child(record_view.get());
record_view.reset();
}
if (persistent_memory::recon_repeat_recorded()) {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"RECON_REPEAT.C16", u"CAPTURES",
RecordView::FileType::RawS16, 16384, 3);
record_view->set_filename_as_is(true);
} else if (new_mod == SPEC_MODULATION) {
audio::output::stop();
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_RAW_", u"CAPTURES",
u"AUTO_RAW", u"CAPTURES",
RecordView::FileType::RawS16, 16384, 3);
} else {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO_", u"AUDIO",
u"AUTO_AUDIO", u"AUDIO",
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
}
record_view->set_auto_trim(false);
add_child(record_view.get());
record_view->hidden(true);
record_view->set_filename_date_frequency(true);
record_view->on_error = [this](std::string message) {
nav_.display_modal("Error", message);
};
receiver_model.disable();
transmitter_model.disable();
baseband::shutdown();
size_t recording_sampling_rate = 0;
switch (new_mod) {
@@ -1144,7 +1223,6 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
field_bw.on_change = [this](size_t, OptionsField::value_t sampling_rate) {
// record_view determines the correct oversampling to apply and returns the actual sample rate.
auto actual_sampling_rate = record_view->set_sampling_rate(sampling_rate);
// The radio needs to know the effective sampling rate.
receiver_model.set_sampling_rate(actual_sampling_rate);
receiver_model.set_baseband_bandwidth(filter_bandwidth_for_sampling_rate(actual_sampling_rate));
@@ -1222,4 +1300,153 @@ void ReconView::handle_remove_current_item() {
update_description();
}
void ReconView::on_repeat_tx_progress(const uint32_t progress) {
progressbar.set_value(progress);
}
void ReconView::repeat_file_error(const std::filesystem::path& path, const std::string& message) {
nav_.display_modal("Error", "Error opening file \n" + path.string() + "\n" + message);
}
bool ReconView::is_repeat_active() const {
return replay_thread != nullptr;
}
void ReconView::start_repeat() {
// Prepare to send a file.
std::filesystem::path rawfile = u"/" + repeat_rec_path + u"/" + repeat_rec_file;
std::filesystem::path rawmeta = u"/" + repeat_rec_path + u"/" + repeat_rec_meta;
if (recon_tx == false) {
recon_tx = true;
if (record_view != nullptr) {
record_view->stop();
remove_child(record_view.get());
record_view.reset();
}
receiver_model.disable();
transmitter_model.disable();
baseband::shutdown();
baseband::run_image(portapack::spi_flash::image_tag_replay);
size_t rawsize = 0;
{
File capture_file;
auto error = capture_file.open(rawfile);
if (error) {
repeat_file_error(rawfile, "Can't open file to send for size");
return;
}
rawsize = capture_file.size();
}
// Reset the transmit progress bar.
uint8_t sample_size = std::filesystem::capture_file_sample_size(rawfile);
progressbar.set_value(0);
progressbar.set_max(rawsize * sizeof(complex16_t) / sample_size);
progressbar.hidden(false);
auto metadata = read_metadata_file(rawmeta);
// If no metadata found, fallback to the TX frequency.
if (!metadata) {
metadata = {freq, 500'000};
repeat_file_error(rawmeta, "Can't open file to read meta, using default rx_freq,500'000");
}
// Update the sample rate in proc_replay baseband.
baseband::set_sample_rate(metadata->sample_rate,
get_oversample_rate(metadata->sample_rate));
transmitter_model.set_sampling_rate(get_actual_sample_rate(metadata->sample_rate));
transmitter_model.set_baseband_bandwidth(metadata->sample_rate <= 500'000 ? 1'750'000 : 2'500'000); // TX LPF min 1M75 for SR <=500K, and 2M5 (by experimental test) for SR >500K
// set TX to repeater TX freq if entry is Repeater and have a valid freq, else use recorded one
if (current_entry().type == freqman_type::Repeater && current_entry().frequency_b != 0) {
transmitter_model.set_target_frequency(current_entry().frequency_b);
} else {
transmitter_model.set_target_frequency(metadata->center_frequency);
}
// set TX powers and enable transmitter
transmitter_model.set_tx_gain(persistent_memory::recon_repeat_gain());
transmitter_model.set_rf_amp(persistent_memory::recon_repeat_amp());
transmitter_model.enable();
}
// clear replay thread and set reader
replay_thread.reset();
auto reader = std::make_unique<FileConvertReader>();
auto error = reader->open(rawfile);
if (error) {
repeat_file_error(rawfile, "Can't open file to send to thread");
return;
}
// 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,
/* buffer_count */ repeat_buffer_count,
&repeat_ready_signal,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});
}
void ReconView::stop_repeat(const bool do_loop) {
repeat_ready_signal = false;
if (is_repeat_active()) {
replay_thread.reset();
transmitter_model.disable();
}
// repeat transmit if current number of repetitions (repeat_cur_rep) is < recon configured number of repetitions (recon_repeat_nb)
if (do_loop && repeat_cur_rep < persistent_memory::recon_repeat_nb()) {
start_repeat();
} else {
repeat_cur_rep = 0;
recon_tx = false;
reload_restart_recon();
progressbar.hidden(true);
set_dirty(); // fix progressbar no hiding
}
}
void ReconView::handle_repeat_thread_done(const uint32_t return_code) {
if (return_code == ReplayThread::END_OF_FILE) {
stop_repeat(true);
} else if (return_code == ReplayThread::READ_ERROR) {
stop_repeat(false);
repeat_file_error(u"/" + repeat_rec_path + u"/" + repeat_rec_file, "Can't open file to send.");
}
}
} /* namespace ui */
+73 -6
View File
@@ -41,10 +41,20 @@
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_recon_settings.hpp"
#include "ui_transmitter.hpp"
#include "replay_thread.hpp"
#include "metadata_file.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_freq_field.hpp"
#include "ui_spectrum.hpp"
#include <string>
#include <memory>
namespace ui {
#define RECON_CFG_FILE "SETTINGS/recon.cfg"
#define RECON_CFG_FILE u"SETTINGS/recon.cfg"
enum class recon_mode : uint8_t {
Recon,
@@ -66,10 +76,17 @@ class ReconView : public View {
private:
NavigationView& nav_;
RxRadioState radio_state_{};
RxRadioState rx_radio_state_{};
TxRadioState tx_radio_state_{
0 /* frequency */,
1750000 /* bandwidth */,
500000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_recon"sv, app_settings::Mode::RX};
"rx_tx_recon"sv, app_settings::Mode::RX_TX};
;
void reload_restart_recon();
void check_update_ranges_from_current();
void set_loop_config(bool v);
void clear_freqlist_for_ui_action();
@@ -93,7 +110,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();
@@ -125,6 +142,7 @@ class ReconView : public View {
bool load_freqs{true};
bool load_ranges{true};
bool load_hamradios{true};
bool load_repeaters{true};
bool update_ranges{true};
bool fwd{true};
bool recon{true};
@@ -163,6 +181,25 @@ class ReconView : public View {
systime_t chrono_start{};
systime_t chrono_end{};
const std::filesystem::path repeat_rec_file = u"RECON_REPEAT.C16";
const std::filesystem::path repeat_rec_meta = u"RECON_REPEAT.TXT";
const std::filesystem::path repeat_rec_path = u"CAPTURES";
const size_t repeat_read_size{16384};
const size_t repeat_buffer_count{3};
int8_t repeat_cur_rep = 0;
int64_t repeat_sample_rate = 0;
static constexpr uint32_t repeat_bandwidth = 2500000;
void on_repeat_tx_progress(const uint32_t progress);
void start_repeat();
void stop_repeat(const bool do_loop);
bool is_repeat_active() const;
void handle_repeat_thread_done(const uint32_t return_code);
void repeat_file_error(const std::filesystem::path& path, const std::string& message);
std::filesystem::path repeat_file_path{};
std::unique_ptr<ReplayThread> replay_thread{};
bool repeat_ready_signal{false};
bool recon_tx{false};
// Persisted settings.
SettingsStore ui_settings{
"recon"sv,
@@ -354,17 +391,47 @@ class ReconView : public View {
{168, (35 * 8) - 4, 72, 28},
"<REMOVE>"};
ProgressBar progressbar{
{0 * 8, SCREEN_H / 2 - 16, SCREEN_W, 32}};
TransmitterView2 tx_view{
{11 * 8, 2 * 16},
/*short_ui*/ true};
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value);
handle_coded_squelch(message.value);
}};
MessageHandlerRegistration message_handler_stats{
Message::ID::ChannelStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}};
MessageHandlerRegistration message_handler_replay_thread_error{
Message::ID::ReplayThreadDone,
[this](const Message* p) {
auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
handle_repeat_thread_done(message.return_code);
}};
MessageHandlerRegistration message_handler_fifo_signal{
Message::ID::RequestSignal,
[this](const Message* p) {
auto message = static_cast<const RequestSignalMessage*>(p);
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
repeat_ready_signal = true;
}
}};
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* p) {
auto message = *reinterpret_cast<const TXProgressMessage*>(p);
on_repeat_tx_progress(message.progress);
}};
};
@@ -99,8 +99,14 @@ void ReconSetupViewMore::save() {
persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value());
persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value());
persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value());
persistent_memory::set_recon_load_repeaters(checkbox_load_repeaters.value());
persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value());
persistent_memory::set_recon_auto_record_locked(checkbox_auto_record_locked.value());
persistent_memory::set_recon_repeat_recorded(checkbox_repeat_recorded.value());
persistent_memory::set_recon_repeat_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() {
@@ -113,16 +119,55 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
hidden(true);
add_children({&checkbox_load_freqs,
&checkbox_load_repeaters,
&checkbox_load_ranges,
&checkbox_load_hamradios,
&checkbox_update_ranges_when_recon,
&checkbox_auto_record_locked});
&checkbox_auto_record_locked,
&checkbox_repeat_recorded,
&text_repeat_nb,
&field_repeat_nb,
&checkbox_repeat_amp,
&text_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);
text_repeat_nb.set_style(&Styles::yellow);
field_repeat_nb.set_style(&Styles::yellow);
checkbox_repeat_amp.set_style(&Styles::yellow);
text_repeat_gain.set_style(&Styles::yellow);
field_repeat_gain.set_style(&Styles::yellow);
text_repeat_delay.set_style(&Styles::yellow);
field_repeat_delay.set_style(&Styles::yellow);
checkbox_load_freqs.set_value(persistent_memory::recon_load_freqs());
checkbox_load_repeaters.set_value(persistent_memory::recon_load_repeaters());
checkbox_load_ranges.set_value(persistent_memory::recon_load_ranges());
checkbox_load_hamradios.set_value(persistent_memory::recon_load_hamradios());
checkbox_update_ranges_when_recon.set_value(persistent_memory::recon_update_ranges_when_recon());
checkbox_auto_record_locked.set_value(persistent_memory::recon_auto_record_locked());
checkbox_repeat_recorded.set_value(persistent_memory::recon_repeat_recorded());
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) {
if (v) {
nav.display_modal(
"TX WARNING",
"This activate TX ability\nin Recon !",
YESNO,
[this, &nav](bool choice) {
if (!choice)
checkbox_repeat_recorded.set_value(choice);
});
}
};
};
void ReconSetupViewMore::focus() {
@@ -30,6 +30,7 @@
#include "ui_tabview.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include "ui_styles.hpp"
// 1Mhz helper
#ifdef OneMHz
@@ -116,26 +117,82 @@ class ReconSetupViewMore : public View {
Checkbox checkbox_load_freqs{
{1 * 8, 12},
3,
"input: load freqs"};
"load freq",
true};
Checkbox checkbox_load_repeaters{
{14 * 8, 12},
3,
"load repeater",
true};
Checkbox checkbox_load_ranges{
{1 * 8, 42},
3,
"input: load ranges"};
"load range",
true};
Checkbox checkbox_load_hamradios{
{1 * 8, 72},
3,
"input: load hamradios"};
"load hamradio",
true};
Checkbox checkbox_update_ranges_when_recon{
{1 * 8, 102},
3,
"auto update m-ranges"};
Checkbox checkbox_auto_record_locked{
{1 * 8, 132},
3,
"record locked periods"};
Checkbox checkbox_repeat_recorded{
{1 * 8, 162},
3,
"repeater,"};
Text text_repeat_nb{
{14 * 8, 165, 3 * 8, 22},
"nb:"};
NumberField field_repeat_nb{
{17 * 8, 165},
2,
{1, 99},
1,
' ',
};
Checkbox checkbox_repeat_amp{
{1 * 8, 192},
3,
"AMP,"};
Text text_repeat_gain{
{9 * 8, 196, 5 * 8, 22},
"GAIN:"};
NumberField field_repeat_gain{
{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 {
+1
View File
@@ -564,6 +564,7 @@ void ScannerView::frequency_file_load(const fs::path& path) {
def_step_index = entry.step;
switch (entry.type) {
case freqman_type::Repeater:
case freqman_type::Single:
entries.push_back({entry.frequency_a, entry.description});
break;
+17 -40
View File
@@ -133,7 +133,6 @@ SetRadioView::SetRadioView(
&check_clkout,
&field_clkout_freq,
&labels_clkout_khz,
&value_freq_step,
&labels_bias,
&check_bias,
&disable_external_tcxo, // TODO: always show?
@@ -150,23 +149,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);
@@ -185,29 +171,20 @@ SetRadioView::SetRadioView(
send_system_refresh();
};
field_clkout_freq.set_value(pmem::clkout_freq());
value_freq_step.set_style(&Styles::light_grey);
// Disallow CLKOUT freq change on hackrf_r9 due to dependencies on GP_CLKIN (same Si5351A clock);
// see comments in ClockManager::enable_clock_output()
if (hackrf_r9) {
if (pmem::clkout_freq() != 10000)
pmem::set_clkout_freq(10000);
field_clkout_freq.set_focusable(false);
}
field_clkout_freq.on_select = [this](NumberField&) {
freq_step_khz++;
if (freq_step_khz > 3) {
freq_step_khz = 0;
}
switch (freq_step_khz) {
case 0:
value_freq_step.set(" |");
break;
case 1:
value_freq_step.set(" | ");
break;
case 2:
value_freq_step.set(" | ");
break;
case 3:
value_freq_step.set("| ");
break;
}
field_clkout_freq.set_step(pow(10, freq_step_khz));
field_clkout_freq.set_value(pmem::clkout_freq());
field_clkout_freq.on_change = [this](SymField&) {
if (field_clkout_freq.to_integer() < 4) // Min. CLK out of Si5351A/B/C-B is 2.5khz , but in our application -intermediate freq 800Mhz-,Min working CLK=4khz.
field_clkout_freq.set_value(4);
if (field_clkout_freq.to_integer() > 60000)
field_clkout_freq.set_value(60000);
};
check_bias.set_value(get_antenna_bias());
@@ -248,7 +225,7 @@ void SetRadioView::form_init(const SetFrequencyCorrectionModel& model) {
SetFrequencyCorrectionModel SetRadioView::form_collect() {
return {
.ppm = static_cast<int8_t>(field_ppm.value()),
.freq = static_cast<uint32_t>(field_clkout_freq.value()),
.freq = static_cast<uint32_t>(field_clkout_freq.to_integer()),
};
}
@@ -459,7 +436,7 @@ SetFrequencyCorrectionView::SetFrequencyCorrectionView(NavigationView& nav) {
pmem::set_freq_rx_correction_updown(v);
};
opt_tx_correction_mode.set_by_value(pmem::config_freq_rx_correction_updown());
opt_tx_correction_mode.set_by_value(pmem::config_freq_tx_correction_updown());
opt_tx_correction_mode.on_change = [this](size_t, OptionsField::value_t v) {
pmem::set_freq_tx_correction_updown(v);
};
+2 -8
View File
@@ -163,20 +163,14 @@ class SetRadioView : public View {
13,
"Enable CLKOUT"};
NumberField field_clkout_freq{
SymField field_clkout_freq{
{20 * 8, 6 * 16},
5,
{10, 60000},
1000,
' '};
SymField::Type::Dec};
Labels labels_clkout_khz{
{{26 * 8, 6 * 16}, "kHz", Color::light_grey()}};
Text value_freq_step{
{21 * 8, (7 * 16), 4 * 8, 16},
"| "};
Labels labels_bias{
{{4 * 8 + 4, 8 * 16}, "CAUTION: Ensure that all", Color::red()},
{{5 * 8 + 0, 9 * 16}, "devices attached to the", Color::red()},
+1
View File
@@ -94,6 +94,7 @@ SondeView::SondeView(NavigationView& nav)
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?
+2 -1
View File
@@ -162,7 +162,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},
+242
View File
@@ -0,0 +1,242 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_subghzd.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace ui;
namespace ui {
void SubGhzDRecentEntryDetailView::update_data() {
// set text elements
text_type.set(SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry_.sensorType));
text_id.set("0x" + to_string_hex(entry_.serial));
if (entry_.bits > 0) console.writeln("Bits: " + to_string_dec_uint(entry_.bits));
if (entry_.btn != SD_NO_BTN) console.writeln("Btn: " + to_string_dec_uint(entry_.btn));
if (entry_.cnt != SD_NO_CNT) console.writeln("Cnt: " + to_string_dec_uint(entry_.cnt));
if (entry_.data != 0) console.writeln("Data: " + to_string_hex(entry_.data));
}
SubGhzDRecentEntryDetailView::SubGhzDRecentEntryDetailView(NavigationView& nav, const SubGhzDRecentEntry& entry)
: nav_{nav},
entry_{entry} {
add_children({&button_done,
&text_type,
&text_id,
&console,
&labels});
button_done.on_select = [&nav](const ui::Button&) {
nav.pop();
};
update_data();
}
void SubGhzDRecentEntryDetailView::focus() {
button_done.focus();
}
void SubGhzDView::focus() {
field_frequency.focus();
}
SubGhzDView::SubGhzDView(NavigationView& nav)
: nav_{nav} {
add_children({&rssi,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&button_clear_list,
&recent_entries_view});
baseband::run_image(portapack::spi_flash::image_tag_subghzd);
button_clear_list.on_select = [this](Button&) {
recent.clear();
recent_entries_view.set_dirty();
};
field_frequency.set_step(100000);
const Rect content_rect{0, header_height, screen_width, screen_height - header_height};
recent_entries_view.set_parent_rect(content_rect);
recent_entries_view.on_select = [this](const SubGhzDRecentEntry& entry) {
nav_.push<SubGhzDRecentEntryDetailView>(entry);
};
baseband::set_subghzd_config(0, receiver_model.sampling_rate()); // 0=am
receiver_model.enable();
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
on_tick_second();
};
}
void SubGhzDView::on_tick_second() {
for (auto& entry : recent) {
entry.inc_age(1);
}
recent_entries_view.set_dirty();
}
void SubGhzDView::on_data(const SubGhzDDataMessage* data) {
SubGhzDRecentEntry key{data->sensorType, data->serial, data->bits, data->data, data->btn, data->cnt};
auto matching_recent = find(recent, key.key());
if (matching_recent != std::end(recent)) {
// Found within. Move to front of list, increment counter.
(*matching_recent).reset_age();
recent.push_front(*matching_recent);
recent.erase(matching_recent);
} else {
recent.emplace_front(key);
truncate_entries(recent, 64);
}
recent_entries_view.set_dirty();
}
SubGhzDView::~SubGhzDView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
receiver_model.disable();
baseband::shutdown();
}
const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
switch (type) {
case FPS_PRINCETON:
return "Princeton";
case FPS_BETT:
return "Bett";
case FPS_CAME:
return "Came";
case FPS_PRASTEL:
return "Prastel";
case FPS_AIRFORCE:
return "Airforce";
case FPS_CAMEATOMO:
return "Came Atomo";
case FPS_CAMETWEE:
return "Came Twee";
case FPS_CHAMBCODE:
return "Chamb Code";
case FPS_CLEMSA:
return "Clemsa";
case FPS_DOITRAND:
return "Doitrand";
case FPS_DOOYA:
return "Dooya";
case FPS_FAAC:
return "Faac";
case FPS_GATETX:
return "Gate TX";
case FPS_HOLTEK:
return "Holtek";
case FPS_HOLTEKHT12X:
return "Holtek HT12X";
case FPS_HONEYWELL:
return "Honeywell";
case FPS_HONEYWELLWDB:
return "Honeywell Wdb";
case FPS_HORMANN:
return "Hormann";
case FPS_IDO:
return "Ido 11x";
case FPS_INTERTECHNOV3:
return "InterTehcno v3";
case FPS_KEELOQ:
return "KeeLoq";
case FPS_KINGGATESSTYLO4K:
return "Kinggate Stylo4K";
case FPS_LINEAR:
return "Linear";
case FPS_LINEARDELTA3:
return "Linear Delta3";
case FPS_MAGELLAN:
return "Magellan";
case FPS_MARANTEC:
return "Marantec";
case FPS_MASTERCODE:
return "Mastercode";
case FPS_MEGACODE:
return "Megacode";
case FPS_NERORADIO:
return "Nero Radio";
case FPS_NERO_SKETCH:
return "Nero Sketch";
case FPS_NICEFLO:
return "Nice Flo";
case FPS_NICEFLORS:
return "Nice Flor S";
case FPS_PHOENIXV2:
return "Phoenix V2";
case FPS_POWERSMART:
return "PowerSmart";
case FPS_SECPLUSV1:
return "SecPlus V1";
case FPS_SECPLUSV2:
return "SecPlus V2";
case FPS_SMC5326:
return "SMC5326";
case FPS_STARLINE:
return "Star Line";
case FPS_X10:
return "X10";
case FPS_Invalid:
default:
return "Unknown";
}
}
std::string SubGhzDView::pad_string_with_spaces(int snakes) {
std::string paddedStr(snakes, ' ');
return paddedStr;
}
template <>
void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
std::string line{};
line.reserve(30);
line = SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry.sensorType);
line = line + " " + to_string_hex(entry.serial);
if (line.length() < 19) {
line += SubGhzDView::pad_string_with_spaces(19 - line.length());
} else {
line = truncate(line, 19);
}
std::string ageStr = to_string_dec_uint(entry.age);
std::string bitsStr = to_string_dec_uint(entry.bits);
line += SubGhzDView::pad_string_with_spaces(5 - bitsStr.length()) + bitsStr;
line += SubGhzDView::pad_string_with_spaces(4 - ageStr.length()) + ageStr;
line.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.location(), style, line);
}
} // namespace ui
+171
View File
@@ -0,0 +1,171 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_SUBGHZD_H__
#define __UI_SUBGHZD_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "utility.hpp"
#include "recent_entries.hpp"
#include "../baseband/fprotos/subghztypes.hpp"
using namespace ui;
namespace ui {
struct SubGhzDRecentEntry {
using Key = uint64_t;
static constexpr Key invalid_key = 0x0fffffff;
uint8_t sensorType = FPS_Invalid;
uint8_t btn = SD_NO_BTN;
uint32_t serial = SD_NO_SERIAL;
uint16_t bits = 0;
uint16_t age = 0; // updated on each seconds, show how long the signal was last seen
uint32_t cnt = SD_NO_CNT;
uint64_t data = 0;
SubGhzDRecentEntry() {}
SubGhzDRecentEntry(
uint8_t sensorType,
uint32_t serial,
uint16_t bits = 0,
uint64_t data = 0,
uint8_t btn = SD_NO_BTN,
uint32_t cnt = SD_NO_CNT)
: sensorType{sensorType},
btn{btn},
serial{serial},
bits{bits},
cnt{cnt},
data{data} {
}
Key key() const {
return (data ^ ((static_cast<uint64_t>(serial) << 32) | (static_cast<uint64_t>(sensorType) & 0xFF) << 0));
}
void inc_age(int delta) {
if (UINT16_MAX - delta > age) age += delta;
}
void reset_age() {
age = 0;
}
};
using SubGhzDRecentEntries = RecentEntries<SubGhzDRecentEntry>;
using SubGhzDRecentEntriesView = RecentEntriesView<SubGhzDRecentEntries>;
class SubGhzDView : public View {
public:
SubGhzDView(NavigationView& nav);
~SubGhzDView();
void focus() override;
std::string title() const override { return "SubGhzD"; };
static const char* getSensorTypeName(FPROTO_SUBGHZD_SENSOR type);
static std::string pad_string_with_spaces(int snakes);
private:
void on_tick_second();
void on_data(const SubGhzDDataMessage* data);
NavigationView& nav_;
RxRadioState radio_state_{
433'920'000 /* frequency */,
1'750'000 /* bandwidth */,
4'000'000 /* sampling rate */,
ReceiverModel::Mode::AMAudio};
app_settings::SettingsManager settings_{
"rx_subghzd",
app_settings::Mode::RX,
{}};
SubGhzDRecentEntries recent{};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
SignalToken signal_token_tick_second{};
Button button_clear_list{
{0, 16, 7 * 8, 32},
"Clear"};
static constexpr auto header_height = 3 * 16;
const RecentEntriesColumns columns{{
{"Type", 19},
{"Bits", 4},
{"Age", 3},
}};
SubGhzDRecentEntriesView recent_entries_view{columns, recent};
MessageHandlerRegistration message_handler_packet{
Message::ID::SubGhzDData,
[this](Message* const p) {
const auto message = static_cast<const SubGhzDDataMessage*>(p);
this->on_data(message);
}};
};
class SubGhzDRecentEntryDetailView : public View {
public:
SubGhzDRecentEntryDetailView(NavigationView& nav, const SubGhzDRecentEntry& entry);
void update_data();
void focus() override;
private:
NavigationView& nav_;
SubGhzDRecentEntry entry_{};
Text text_type{{0 * 8, 1 * 16, 15 * 8, 16}, "?"};
Text text_id{{6 * 8, 2 * 16, 10 * 8, 16}, "?"};
Console console{
{0, 4 * 16, 240, screen_height - (4 * 16) - 36}};
Labels labels{
{{0 * 8, 0 * 16}, "Type:", Color::light_grey()},
{{0 * 8, 2 * 16}, "Serial: ", Color::light_grey()},
{{0 * 8, 3 * 16}, "Data:", Color::light_grey()},
};
Button button_done{
{screen_width - 96 - 4, screen_height - 32 - 12, 96, 32},
"Done"};
};
} // namespace ui
#endif /*__UI_SUBGHZD_H__*/
+25 -10
View File
@@ -35,11 +35,26 @@ namespace ui {
void WeatherRecentEntryDetailView::update_data() {
// set text elements
text_type.set(WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)entry_.sensorType));
text_id.set("0x" + to_string_hex(entry_.id));
text_temp.set(weather_units_fahr ? to_string_decimal((entry_.temp * 9 / 5) + 32, 1) + STR_DEGREES_F : to_string_decimal(entry_.temp, 2) + STR_DEGREES_C);
text_hum.set(to_string_dec_uint(entry_.humidity) + "%");
text_ch.set(to_string_dec_uint(entry_.channel));
text_batt.set(to_string_dec_uint(entry_.battery_low) + " " + ((entry_.battery_low == 0) ? "OK" : "LOW"));
if (entry_.id != WS_NO_ID)
text_id.set("0x" + to_string_hex(entry_.id));
else
text_id.set("-");
if (entry_.temp != WS_NO_TEMPERATURE)
text_temp.set(weather_units_fahr ? to_string_decimal((entry_.temp * 9 / 5) + 32, 1) + STR_DEGREES_F : to_string_decimal(entry_.temp, 2) + STR_DEGREES_C);
else
text_temp.set("-");
if (entry_.humidity != WS_NO_HUMIDITY)
text_hum.set(to_string_dec_uint(entry_.humidity) + "%");
else
text_hum.set("-");
if (entry_.channel != WS_NO_CHANNEL)
text_ch.set(to_string_dec_uint(entry_.channel));
else
text_ch.set("-");
if (entry_.battery_low != WS_NO_BATT)
text_batt.set(to_string_dec_uint(entry_.battery_low) + " " + ((entry_.battery_low == 0) ? "OK" : "LOW"));
else
text_batt.set("-");
text_age.set(to_string_dec_uint(entry_.age) + " sec");
}
@@ -100,7 +115,7 @@ WeatherView::WeatherView(NavigationView& nav)
recent_entries_view.on_select = [this](const WeatherRecentEntry& entry) {
nav_.push<WeatherRecentEntryDetailView>(entry);
};
baseband::set_weather();
baseband::set_subghzd_config(0, receiver_model.sampling_rate()); // 0=am
receiver_model.enable();
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
on_tick_second();
@@ -204,17 +219,17 @@ void RecentEntriesTable<ui::WeatherRecentEntries>::draw(
}
std::string temp = (weather_units_fahr ? to_string_decimal((entry.temp * 9 / 5) + 32, 1) : to_string_decimal(entry.temp, 1));
std::string humStr = to_string_dec_uint(entry.humidity) + "%";
std::string chStr = to_string_dec_uint(entry.channel);
std::string humStr = (entry.humidity != WS_NO_HUMIDITY) ? to_string_dec_uint(entry.humidity) + "%" : "-";
std::string chStr = (entry.channel != WS_NO_CHANNEL) ? to_string_dec_uint(entry.channel) : "-";
std::string ageStr = to_string_dec_uint(entry.age);
line += WeatherView::pad_string_with_spaces(6 - temp.length()) + temp;
line += WeatherView::pad_string_with_spaces(5 - humStr.length()) + humStr;
line += WeatherView::pad_string_with_spaces(4 - chStr.length()) + chStr;
line += WeatherView::pad_string_with_spaces(4 - ageStr.length()) + ageStr;
line += WeatherView::pad_string_with_spaces(5 - ageStr.length()) + ageStr;
line.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.location(), style, line);
}
} // namespace ui
} // namespace ui
+18 -17
View File
@@ -43,11 +43,11 @@ struct WeatherRecentEntry {
using Key = uint64_t;
static constexpr Key invalid_key = 0x0fffffff; // todo calc the invalid all
uint8_t sensorType = FPW_Invalid;
uint32_t id = 0xFFFFFFFF;
float temp = -273.0f;
uint8_t humidity = 0xFF;
uint8_t battery_low = 0xFF;
uint8_t channel = 0xFF;
uint32_t id = WS_NO_ID;
float temp = WS_NO_TEMPERATURE;
uint8_t humidity = WS_NO_HUMIDITY;
uint8_t battery_low = WS_NO_BATT;
uint8_t channel = WS_NO_CHANNEL;
uint16_t age = 0; // updated on each seconds, show how long the signal was last seen
WeatherRecentEntry() {}
@@ -57,7 +57,7 @@ struct WeatherRecentEntry {
float temp,
uint8_t humidity,
uint8_t channel,
uint8_t battery_low = 0xff)
uint8_t battery_low = WS_NO_BATT)
: sensorType{sensorType},
id{id},
temp{temp},
@@ -100,8 +100,8 @@ class WeatherView : public View {
NavigationView& nav_;
RxRadioState radio_state_{
433'920'000 /* frequency */,
2'500'000 /* bandwidth max283x*/,
4'000'000 /* sampling rate */,
1'750'000 /* bandwidth */,
2'000'000 /* sampling rate */,
ReceiverModel::Mode::AMAudio};
app_settings::SettingsManager settings_{
"rx_weather",
@@ -143,7 +143,7 @@ class WeatherView : public View {
{"Temp", 5},
{"Hum", 4},
{"Ch", 3},
{"Age", 3},
{"Age", 4},
}};
WeatherRecentEntriesView recent_entries_view{columns, recent};
@@ -165,16 +165,17 @@ class WeatherRecentEntryDetailView : public View {
private:
NavigationView& nav_;
WeatherRecentEntry entry_{};
Text text_type{{0 * 8, 1 * 16, 15 * 8, 16}, "?"};
Text text_id{{6 * 8, 2 * 16, 10 * 8, 16}, "?"};
Text text_temp{{6 * 8, 3 * 16, 8 * 8, 16}, "?"};
Text text_hum{{11 * 8, 4 * 16, 6 * 8, 16}, "?"};
Text text_ch{{11 * 8, 5 * 16, 6 * 8, 16}, "?"};
Text text_batt{{11 * 8, 6 * 16, 6 * 8, 16}, "?"};
Text text_age{{11 * 8, 7 * 16, 6 * 8, 16}, "?"};
Text text_type{{10 * 8, 1 * 16, 15 * 8, 16}, "?"};
Text text_id{{10 * 8, 2 * 16, 10 * 8, 16}, "?"};
Text text_temp{{10 * 8, 3 * 16, 8 * 8, 16}, "?"};
Text text_hum{{10 * 8, 4 * 16, 6 * 8, 16}, "?"};
Text text_ch{{10 * 8, 5 * 16, 6 * 8, 16}, "?"};
Text text_batt{{10 * 8, 6 * 16, 6 * 8, 16}, "?"};
Text text_age{{10 * 8, 7 * 16, 10 * 8, 16}, "?"};
Labels labels{
{{0 * 8, 0 * 16}, "Weather station type:", Color::light_grey()},
{{0 * 8, 0 * 16}, "Weather Station", Color::light_grey()},
{{0 * 8, 1 * 16}, "Type:", Color::light_grey()},
{{0 * 8, 2 * 16}, "Id: ", Color::light_grey()},
{{0 * 8, 3 * 16}, "Temp:", Color::light_grey()},
{{0 * 8, 4 * 16}, "Humidity:", Color::light_grey()},
+2 -2
View File
@@ -319,8 +319,8 @@ void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bo
send_message(&message);
}
void set_weather() {
const WeatherRxConfigureMessage message{};
void set_subghzd_config(uint8_t modulation = 0, uint32_t sampling_rate = 0) {
const SubGhzFPRxConfigureMessage message{modulation, sampling_rate};
send_message(&message);
}
+1 -1
View File
@@ -88,7 +88,7 @@ void set_spectrum(const size_t sampling_rate, const size_t trigger);
void set_siggen_tone(const uint32_t tone);
void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t duration);
void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw);
void set_weather();
void set_subghzd_config(uint8_t modulation, uint32_t sampling_rate);
void request_beep();
void run_image(const portapack::spi_flash::image_tag_t image_tag);
+46 -19
View File
@@ -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);
@@ -561,26 +582,32 @@ void ClockManager::stop_audio_pll() {
}
void ClockManager::enable_clock_output(bool enable) {
if (enable) {
clock_generator.enable_output(clock_generator_output_og_clkout);
if (portapack::persistent_memory::clkout_freq() < 1000) {
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
} else {
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
}
} else {
clock_generator.disable_output(clock_generator_output_og_clkout);
if (hackrf_r9) {
gpio_r9_clkout_en.output();
gpio_r9_clkout_en.write(enable);
// NOTE: RETURNING HERE IF HACKRF_R9 TO PREVENT CLK2 FROM BEING DISABLED OR FREQ MODIFIED SINCE CLK2 ON R9 IS
// USED FOR BOTH CLKOUT AND FOR THE MCU_CLOCK (== GP_CLKIN) WHICH OTHER LP43XX CLOCKS CURRENTLY RELY ON.
// FUTURE TBD: REMOVE OTHER LP43XX CLOCK DEPENDENCIES ON GP_CLKIN, THEN DELETE THE return LINE BELOW TO ALLOW
// CLKOUT FREQ CHANGES ON R9 BOARDS.
return;
}
auto si5351_clock_control_common = hackrf_r9
? si5351a_clock_control_common
: si5351c_clock_control_common;
const auto ref_pll = hackrf_r9
? ClockControl::MultiSynthSource::PLLA
: get_si5351c_reference_clock_generator_pll(reference.source);
auto clkout_select = hackrf_r9 ? clock_generator_output_r9_clkout : clock_generator_output_og_clkout;
if (enable)
clock_generator.set_clock_control(clock_generator_output_og_clkout, si5351_clock_control_common[clock_generator_output_og_clkout].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On));
else
clock_generator.set_clock_control(clock_generator_output_og_clkout, ClockControl::power_off());
if (enable) {
clock_generator.enable_output(clkout_select);
if (portapack::persistent_memory::clkout_freq() < 1000) {
clock_generator.set_ms_frequency(clkout_select, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
} else {
clock_generator.set_ms_frequency(clkout_select, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
}
auto si5351_clock_control_common = hackrf_r9 ? si5351a_clock_control_common : si5351c_clock_control_common;
const auto ref_pll = hackrf_r9 ? ClockControl::MultiSynthSource::PLLA : get_si5351c_reference_clock_generator_pll(reference.source);
clock_generator.set_clock_control(clkout_select, si5351_clock_control_common[clkout_select].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On));
} else {
clock_generator.disable_output(clkout_select);
clock_generator.set_clock_control(clkout_select, ClockControl::power_off());
}
}
+3
View File
@@ -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);
+25
View File
@@ -113,6 +113,7 @@ void EventDispatcher::run() {
while (is_running) {
const auto events = wait();
dispatch(events);
portapack::usb_serial.dispatch();
}
}
@@ -237,6 +238,22 @@ ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent ev
return nullptr;
}
void EventDispatcher::emulateTouch(ui::TouchEvent event) {
on_touch_event(event);
}
void EventDispatcher::emulateKeyboard(ui::KeyboardEvent event) {
on_keyboard_event(event);
}
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.
@@ -257,6 +274,14 @@ 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() {
DisplayFrameSyncMessage message;
message_map.send(&message);
+7
View File
@@ -86,6 +86,12 @@ class EventDispatcher {
events_flag(EVT_MASK_LOCAL);
}
void emulateTouch(ui::TouchEvent event);
void emulateKeyboard(ui::KeyboardEvent event);
ui::Widget* getTopWidget();
ui::Widget* getFocusedWidget();
private:
static Thread* thread_event_loop;
@@ -111,6 +117,7 @@ class EventDispatcher {
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();
+4 -3
View File
@@ -24,6 +24,7 @@
#define __UI_AFSK_RX_H__
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
@@ -92,16 +93,16 @@ class AFSKRxView : public View {
Checkbox check_log{
{0 * 8, 1 * 16},
3,
"LOG",
LanguageHelper::currentMessages[LANG_LOG],
false};
Text text_debug{
{0 * 8, 12 + 2 * 16, screen_width, 16},
"DEBUG"};
LanguageHelper::currentMessages[LANG_DEBUG]};
Button button_modem_setup{
{screen_width - 12 * 8, 1 * 16, 96, 24},
"Modem setup"};
LanguageHelper::currentMessages[LANG_MODEM_SETUP]};
Console console{
{0, 4 * 16, 240, screen_width}};
@@ -37,7 +37,7 @@ using namespace tonekey;
#include "string_format.hpp"
namespace ui {
namespace ui::external_app::analogtv {
/* AnalogTvView *******************************************************/
@@ -192,10 +192,11 @@ void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) {
baseband::shutdown();
portapack::spi_flash::image_tag_t image_tag;
image_tag = portapack::spi_flash::image_tag_am_tv;
// portapack::spi_flash::image_tag_t image_tag; //moved to ext app, disabled
// image_tag = portapack::spi_flash::image_tag_am_tv;
baseband::run_image(image_tag);
// baseband::run_image(image_tag);
baseband::run_prepared_image(portapack::memory::map::m4_code.base()); // moved the baseband too
receiver_model.set_modulation(modulation);
receiver_model.set_sampling_rate(2000000);
@@ -203,4 +204,4 @@ void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) {
receiver_model.enable();
}
} /* namespace ui */
} // namespace ui::external_app::analogtv
@@ -36,7 +36,7 @@
#include "tone_key.hpp"
namespace ui {
namespace ui::external_app::analogtv {
class AnalogTvView : public View {
public:
@@ -111,6 +111,6 @@ class AnalogTvView : public View {
void update_modulation(const ReceiverModel::Mode modulation);
};
} /* namespace ui */
} // namespace ui::external_app::analogtv
#endif /*__ANALOG_TV_APP_H__*/
+82
View File
@@ -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 "analog_tv_app.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::analogtv {
void initialize_app(ui::NavigationView& nav) {
nav.push<AnalogTvView>();
}
} // namespace ui::external_app::analogtv
extern "C" {
__attribute__((section(".external_app.app_analogtv.application_information"), used)) application_information_t _application_information_analogtv = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::analogtv::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Analog TV",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0xFE,
0x7F,
0x03,
0xC0,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0x03,
0xC0,
0xFF,
0xFF,
0xFB,
0xD7,
0xFE,
0x7F,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::RX,
/*.m4_app_tag = portapack::spi_flash::image_tag_am_tv */ {'P', 'A', 'M', 'T'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
File diff suppressed because it is too large Load Diff
+10 -4
View File
@@ -27,7 +27,10 @@
#ifndef __UI_BLESPAM_H__
#define __UI_BLESPAM_H__
#define BLESPMUSECONSOLE 1
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_transmitter.hpp"
#include "ui_freq_field.hpp"
@@ -107,12 +110,13 @@ class BLESpamView : public View {
Button button_startstop{
{0, 3 * 16, 96, 24},
"Start"};
LanguageHelper::currentMessages[LANG_START]};
Checkbox chk_randdev{{100, 16}, 10, "Rnd device", true};
#ifdef BLESPMUSECONSOLE
Console console{
{0, 70, 240, 220}};
#endif
OptionsField options_atkmode{
{0 * 8, 2 * 8},
10,
@@ -133,7 +137,7 @@ class BLESpamView : public View {
bool randomDev{true};
uint8_t channel_number = 37;
char mac[13] = "010203040507";
char mac[13] = "010203040407";
char advertisementData[63] = {"03032CFE06162CFED5A59E020AB4\0"};
PKT_TYPE pduType = {PKT_TYPE_DISCOVERY};
@@ -160,6 +164,8 @@ class BLESpamView : public View {
this->on_tx_progress(message.done);
}};
uint8_t packet[80];
// continuity
typedef enum {
@@ -226,7 +232,7 @@ class BLESpamView : public View {
static const uint16_t fastpairModels_count;
typedef struct {
uint32_t value;
const char* name; // could be moved too
// const char* name; // could be moved too
} fpUi32;
static const fpUi32 fastpairModels[];
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_coasterp.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::coasterp {
void initialize_app(ui::NavigationView& nav) {
nav.push<CoasterPagerView>();
}
} // namespace ui::external_app::coasterp
extern "C" {
__attribute__((section(".external_app.app_coasterp.application_information"), used)) application_information_t _application_information_coasterp = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::coasterp::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "BurgerPgr",
/*.bitmap_data = */ {
0x00,
0x00,
0xE0,
0x07,
0xF8,
0x1F,
0xFC,
0x3F,
0xFE,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x55,
0x55,
0xAA,
0xAA,
0x55,
0x55,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x7F,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_fsktx */ {'P', 'F', 'S', 'K'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -30,7 +30,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::coasterp {
void CoasterPagerView::focus() {
sym_data.focus();
@@ -131,4 +131,4 @@ CoasterPagerView::CoasterPagerView(NavigationView& nav) {
};
}
} /* namespace ui */
} /* namespace ui::external_app::coasterp */
@@ -21,6 +21,7 @@
*/
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_transmitter.hpp"
@@ -31,7 +32,7 @@
#include "radio_state.hpp"
#include "portapack.hpp"
namespace ui {
namespace ui::external_app::coasterp {
class CoasterPagerView : public View {
public:
@@ -65,7 +66,7 @@ class CoasterPagerView : public View {
Labels labels{
{{1 * 8, 3 * 8}, "Syscall pager TX beta", Color::light_grey()},
{{1 * 8, 8 * 8}, "Data:", Color::light_grey()}};
{{1 * 8, 8 * 8}, LanguageHelper::currentMessages[LANG_DATADP], Color::light_grey()}};
SymField sym_data{
{7 * 8, 8 * 8},
@@ -75,7 +76,7 @@ class CoasterPagerView : public View {
Checkbox checkbox_scan{
{10 * 8, 14 * 8},
4,
"Scan"};
LanguageHelper::currentMessages[LANG_SCAN]};
/*
ProgressBar progressbar {
@@ -99,4 +100,4 @@ class CoasterPagerView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::coasterp */
+42
View File
@@ -19,7 +19,41 @@ set(EXTCPPSRC
#blespam
external/blespam/main.cpp
external/blespam/ui_blespam.cpp
#analogtv
external/analogtv/main.cpp
external/analogtv/analog_tv_app.cpp
#nrf_rx
external/nrf_rx/main.cpp
external/nrf_rx/ui_nrf_rx.cpp
#coasterp
external/coasterp/main.cpp
external/coasterp/ui_coasterp.cpp
#lge
external/lge/main.cpp
external/lge/lge_app.cpp
#lcr
external/lcr/main.cpp
external/lcr/ui_lcr.cpp
#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
@@ -28,4 +62,12 @@ set(EXTAPPLIST
calculator
font_viewer
blespam
nrf_rx
analogtv
coasterp
lge
lcr
jammer
gpssim
spainter
)
+60
View File
@@ -22,6 +22,14 @@ MEMORY
ram_external_app_calculator (rwx) : org = 0xEEEB0000, len = 32k
ram_external_app_font_viewer(rwx) : org = 0xEEEC0000, len = 32k
ram_external_app_blespam(rwx) : org = 0xEEED0000, len = 32k
ram_external_app_analogtv(rwx) : org = 0xEEEE0000, len = 32k
ram_external_app_nrf_rx(rwx) : org = 0xEEEF0000, len = 32k
ram_external_app_coasterp(rwx) : org = 0xEEF00000, len = 32k
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
@@ -56,4 +64,56 @@ SECTIONS
KEEP(*(.external_app.app_blespam.application_information));
*(*ui*external_app*blespam*);
} > ram_external_app_blespam
.external_app_analogtv : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_analogtv.application_information));
*(*ui*external_app*analogtv*);
} > ram_external_app_analogtv
.external_app_nrf_rx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_nrf_rx.application_information));
*(*ui*external_app*nrf_rx*);
} > ram_external_app_nrf_rx
.external_app_coasterp : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_coasterp.application_information));
*(*ui*external_app*coasterp*);
} > ram_external_app_coasterp
.external_app_lge : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_lge.application_information));
*(*ui*external_app*lge*);
} > ram_external_app_lge
.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)
{
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
}
@@ -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 */
@@ -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
View File
@@ -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
};
}
+82
View File
@@ -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_jammer.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::jammer {
void initialize_app(ui::NavigationView& nav) {
nav.push<JammerView>();
}
} // namespace ui::external_app::jammer
extern "C" {
__attribute__((section(".external_app.app_jammer.application_information"), used)) application_information_t _application_information_jammer = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::jammer::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Jammer",
/*.bitmap_data = */ {
0xE0,
0x07,
0xF8,
0x1F,
0x1C,
0x38,
0x0E,
0x78,
0x06,
0x7C,
0x03,
0xCE,
0x03,
0xC7,
0x83,
0xC3,
0xC3,
0xC1,
0xE3,
0xC0,
0x73,
0xC0,
0x3E,
0x60,
0x1E,
0x70,
0x1C,
0x38,
0xF8,
0x1F,
0xE0,
0x07,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_jammer */ {'P', 'J', 'A', 'M'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -29,7 +29,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::jammer {
void RangeView::focus() {
check_enabled.focus();
@@ -331,7 +331,8 @@ JammerView::JammerView(
NavigationView& nav)
: nav_{nav} {
Rect view_rect = {0, 3 * 8, 240, 80};
baseband::run_image(portapack::spi_flash::image_tag_jammer);
// baseband::run_image(portapack::spi_flash::image_tag_jammer);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&tab_view,
&view_range_a,
@@ -372,4 +373,4 @@ JammerView::JammerView(
};
}
} /* namespace ui */
} // namespace ui::external_app::jammer
@@ -21,6 +21,7 @@
*/
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_widget.hpp"
#include "ui_styles.hpp"
#include "ui_navigation.hpp"
@@ -33,7 +34,7 @@
using namespace jammer;
namespace ui {
namespace ui::external_app::jammer {
class RangeView : public View {
public:
@@ -56,8 +57,8 @@ class RangeView : public View {
const Style& style_info = Styles::grey;
Labels labels{
{{2 * 8, 8 * 8 + 4}, "Start", Color::light_grey()},
{{23 * 8, 8 * 8 + 4}, "Stop", Color::light_grey()},
{{2 * 8, 8 * 8 + 4}, LanguageHelper::currentMessages[LANG_START], Color::light_grey()},
{{23 * 8, 8 * 8 + 4}, LanguageHelper::currentMessages[LANG_STOP], Color::light_grey()},
{{12 * 8, 5 * 8 - 4}, "Center", Color::light_grey()},
{{12 * 8 + 4, 13 * 8}, "Width", Color::light_grey()}};
@@ -226,7 +227,7 @@ class JammerView : public View {
Button button_transmit{
{148, 216, 80, 80},
"START"};
LanguageHelper::currentMessages[LANG_START]};
MessageHandlerRegistration message_handler_retune{
Message::ID::Retune,
@@ -242,4 +243,4 @@ class JammerView : public View {
}};
};
} /* namespace ui */
} // namespace ui::external_app::jammer
+82
View File
@@ -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_lcr.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::lcr {
void initialize_app(ui::NavigationView& nav) {
nav.push<LCRView>();
}
} // namespace ui::external_app::lcr
extern "C" {
__attribute__((section(".external_app.app_lcr.application_information"), used)) application_information_t _application_information_lcr = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::lcr::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "TEDI/LCR",
/*.bitmap_data = */ {
0x0C,
0x00,
0xFF,
0x7F,
0x01,
0x80,
0xC1,
0x9B,
0xFF,
0x7F,
0x0C,
0x00,
0xFF,
0x7F,
0x01,
0x80,
0xC1,
0x9D,
0xFF,
0x7F,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk */ {'P', 'A', 'F', 'T'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -23,7 +23,6 @@
#include "ui_lcr.hpp"
#include "ui_modemsetup.hpp"
#include "lcr.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
@@ -31,7 +30,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::lcr {
void LCRView::focus() {
button_set_rgsb.focus();
@@ -42,6 +41,49 @@ LCRView::~LCRView() {
baseband::shutdown();
}
std::string LCRView::generate_message(std::string rgsb, std::vector<std::string> litterals, size_t option_ec) {
const std::string ec_lut[4] = {"A", "J", "N", "S"}; // Eclairage (Auto, Jour, Nuit)
char eom[3] = {3, 0, 0}; // EOM and space for checksum
uint8_t i;
std::string lcr_message{127, 127, 127, 127, 127, 127, 127, 5}; // 5/15 ? Modem sync and SOM
char checksum = 0;
// Pad litterals to 7 chars (not required ?)
for (auto& litteral : litterals)
while (litteral.length() < 7)
litteral += ' ';
// Compose LCR message
lcr_message += rgsb;
lcr_message += "PA ";
i = 1;
for (auto& litteral : litterals) {
lcr_message += "AM=";
lcr_message += to_string_dec_uint(i, 1);
lcr_message += " AF=\"";
lcr_message += litteral;
lcr_message += "\" CL=0 ";
i++;
}
lcr_message += "EC=";
lcr_message += ec_lut[option_ec];
lcr_message += " SAB=0";
// Checksum
i = 7; // Skip modem sync
while (lcr_message[i])
checksum ^= lcr_message[i++];
checksum ^= eom[0]; // EOM char
checksum &= 0x7F; // Trim
eom[1] = checksum;
lcr_message += eom;
return lcr_message;
}
/*
// Recap: frequency @ baudrate
final_str = to_string_short_freq(persistent_memory::tuned_frequency());
@@ -53,7 +95,7 @@ text_recap.set(final_str);*/
void LCRView::update_progress() {
if (tx_mode == IDLE) {
text_status.set("Ready");
text_status.set(LanguageHelper::currentMessages[LANG_READY]);
progress.set_value(0);
} else {
std::string progress_str = to_string_dec_uint(repeat_index) + "/" + to_string_dec_uint(persistent_memory::modem_repeat()) +
@@ -125,7 +167,7 @@ void LCRView::start_tx(const bool scan) {
litterals_list.push_back(litteral[i]);
}
modems::generate_data(lcr::generate_message(rgsb, litterals_list, options_ec.selected_index()), lcr_message_data);
modems::generate_data(generate_message(rgsb, litterals_list, options_ec.selected_index()), lcr_message_data);
/* It is AFSK modulation , measuring original fw 1.7.4 spectrum BW is just around 30khz , NBFM */
transmitter_model.set_baseband_bandwidth(1'750'000); // Min TX LPF 1M75, same spectrum as previous fw 1.7.4
@@ -269,4 +311,4 @@ LCRView::LCRView(NavigationView& nav) {
};
}
} /* namespace ui */
} /* namespace ui::external_app::lcr */
@@ -21,6 +21,7 @@
*/
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_widget.hpp"
#include "ui_textentry.hpp"
#include "ui_transmitter.hpp"
@@ -31,7 +32,7 @@
#include "app_settings.hpp"
#include "radio_state.hpp"
namespace ui {
namespace ui::external_app::lcr {
#define LCR_MAX_AM 5
@@ -101,6 +102,7 @@ class LCRView : public View {
void start_tx(const bool scan);
void on_tx_progress(const uint32_t progress, const bool done);
void on_button_set_am(NavigationView& nav, int16_t button_id);
std::string generate_message(std::string rgsb, std::vector<std::string> litterals, size_t option_ec);
Labels labels{
{{0, 8}, "EC: RGSB:", Color::light_grey()},
@@ -125,11 +127,11 @@ class LCRView : public View {
Checkbox check_scan{
{22 * 8, 4},
4,
"Scan"};
LanguageHelper::currentMessages[LANG_SCAN]};
Button button_modem_setup{
{1 * 8, 4 * 8 + 2, 14 * 8, 24},
"Modem setup"};
LanguageHelper::currentMessages[LANG_MODEM_SETUP]};
OptionsField options_scanlist{
{22 * 8, 4 * 8},
6,
@@ -137,11 +139,11 @@ class LCRView : public View {
Button button_clear{
{22 * 8, 8 * 8, 7 * 8, 19 * 8},
"CLEAR"};
LanguageHelper::currentMessages[LANG_CLEAR]};
Text text_status{
{2 * 8, 27 * 8 + 4, 26 * 8, 16},
"Ready"};
LanguageHelper::currentMessages[LANG_READY]};
ProgressBar progress{
{2 * 8, 29 * 8 + 4, 26 * 8, 16}};
@@ -158,4 +160,4 @@ class LCRView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::lcr */
@@ -36,7 +36,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::lge {
void LGEView::focus() {
options_frame.focus();
@@ -354,4 +354,4 @@ LGEView::LGEView(NavigationView& nav) {
};
}
} /* namespace ui */
} /* namespace ui::external_app::lge */
@@ -21,6 +21,7 @@
*/
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_transmitter.hpp"
@@ -32,7 +33,7 @@
#include "app_settings.hpp"
#include "radio_state.hpp"
namespace ui {
namespace ui::external_app::lge {
class LGEView : public View {
public:
@@ -105,7 +106,7 @@ class LGEView : public View {
{"Set nickname", 1},
{"Set team", 2},
{"Brdcst nick", 3},
{"Start", 4},
{LanguageHelper::currentMessages[LANG_START], 4},
{"Game over", 5},
{"Set vest", 6}}};
@@ -185,4 +186,4 @@ class LGEView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::lge */
+82
View File
@@ -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 "lge_app.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::lge {
void initialize_app(ui::NavigationView& nav) {
nav.push<LGEView>();
}
} // namespace ui::external_app::lge
extern "C" {
__attribute__((section(".external_app.app_lge.application_information"), used)) application_information_t _application_information_lge = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::lge::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "LGE",
/*.bitmap_data = */ {
0x00,
0x00,
0x80,
0x00,
0xA4,
0x12,
0xA8,
0x0A,
0xD0,
0x05,
0xEC,
0x1B,
0xF0,
0x07,
0xFE,
0xFF,
0xF0,
0x07,
0xEC,
0x1B,
0xD0,
0x05,
0xA8,
0x0A,
0xA4,
0x12,
0x80,
0x00,
0x00,
0x00,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_fsktx */ {'P', 'F', 'S', 'K'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+83
View File
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_nrf_rx.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::nrf_rx {
void initialize_app(ui::NavigationView& nav) {
nav.push<NRFRxView>();
}
} // namespace ui::external_app::nrf_rx
extern "C" {
__attribute__((section(".external_app.app_nrf_rx.application_information"), used)) application_information_t _application_information_nrf_rx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::nrf_rx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "NRF",
/*.bitmap_data = */ {
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0xF8,
0x3F,
0xFC,
0x7F,
0xFC,
0x7F,
0xDC,
0x7F,
0x8C,
0x6B,
0xDC,
0x7F,
0xFC,
0x7F,
0xFC,
0x7F,
0xF8,
0x3F,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::RX,
/*.m4_app_tag = portapack::spi_flash::image_tag_nrf_rx */ {'P', 'N', 'R', 'R'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -34,7 +34,7 @@
using namespace portapack;
using namespace modems;
namespace ui {
namespace ui::external_app::nrf_rx {
void NRFRxView::focus() {
field_frequency.focus();
@@ -42,7 +42,8 @@ void NRFRxView::focus() {
NRFRxView::NRFRxView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_nrf_rx);
// baseband::run_image(portapack::spi_flash::image_tag_nrf_rx);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&rssi,
&channel,
@@ -130,4 +131,4 @@ NRFRxView::~NRFRxView() {
baseband::shutdown();
}
} /* namespace ui */
} // namespace ui::external_app::nrf_rx
@@ -25,6 +25,7 @@
#define __UI_NRF_RX_H__
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
@@ -34,7 +35,7 @@
#include "utility.hpp"
namespace ui {
namespace ui::external_app::nrf_rx {
class NRFRxView : public View {
public:
@@ -77,7 +78,7 @@ class NRFRxView : public View {
Button button_modem_setup{
{240 - 12 * 8, 1 * 16, 96, 24},
"Modem setup"};
LanguageHelper::currentMessages[LANG_MODEM_SETUP]};
Console console{
{0, 4 * 16, 240, 240}};
@@ -90,6 +91,6 @@ class NRFRxView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::nrf_rx */
#endif /*__UI_NRF_RX_H__*/
+82
View File
@@ -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
};
}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
+19 -6
View File
@@ -185,6 +185,8 @@ bool operator==(const freqman_entry& lhs, const freqman_entry& rhs) {
} else if (lhs.type == freqman_type::HamRadio) {
equal = lhs.frequency_b == rhs.frequency_b &&
lhs.tone == rhs.tone;
} else if (lhs.type == freqman_type::Repeater) {
equal = lhs.frequency_b == rhs.frequency_b;
}
return equal;
@@ -248,6 +250,10 @@ std::string pretty_string(const freqman_entry& entry, size_t max_length) {
str = "R:" + to_string_rounded_freq(entry.frequency_a, 1) + "M,T:" +
to_string_rounded_freq(entry.frequency_b, 1) + "M: " + entry.description;
break;
case freqman_type::Repeater:
str = "L:" + to_string_rounded_freq(entry.frequency_a, 1) + "M,T:" +
to_string_rounded_freq(entry.frequency_b, 1) + "M: " + entry.description;
break;
case freqman_type::Raw:
str = entry.description;
break;
@@ -292,6 +298,10 @@ std::string to_freqman_string(const freqman_entry& entry) {
if (is_valid(entry.tone))
append_field("c", tonekey::tone_key_value_string(entry.tone));
break;
case freqman_type::Repeater:
append_field("l", to_string_dec_uint(entry.frequency_a));
append_field("t", to_string_dec_uint(entry.frequency_b));
break;
case freqman_type::Raw:
return entry.description;
default:
@@ -372,16 +382,18 @@ bool parse_freqman_entry(std::string_view str, freqman_entry& entry) {
parse_int(value, entry.frequency_a);
} else if (key == "m") {
entry.modulation = find_by_name(freqman_modulations, value);
} else if (key == "r") {
} else if (key == "r") { // HamRadio relay receive freq
entry.type = freqman_type::HamRadio;
parse_int(value, entry.frequency_a);
} else if (key == "l") { // Portapack Repeater mode listen freq. Used as a single freq if Repeater mode isn't active
entry.type = freqman_type::Repeater;
parse_int(value, entry.frequency_a);
} else if (key == "s") {
entry.step = find_by_name(freqman_steps_short, value);
} else if (key == "t") {
} else if (key == "t") { // Tx freq: scanned as a single freq in HamRadio mode, used as TX freq in Repeater mode and ignored by the scanner
parse_int(value, entry.frequency_b);
}
}
return is_valid(entry);
}
@@ -400,7 +412,8 @@ bool parse_freqman_file(const fs::path& path, freqman_db& db, freqman_load_optio
if (entry.type == freqman_type::Unknown ||
(entry.type == freqman_type::Single && !options.load_freqs) ||
(entry.type == freqman_type::Range && !options.load_ranges) ||
(entry.type == freqman_type::HamRadio && !options.load_hamradios)) {
(entry.type == freqman_type::HamRadio && !options.load_hamradios) ||
(entry.type == freqman_type::Repeater && !options.load_repeaters)) {
continue;
}
@@ -433,8 +446,8 @@ bool is_valid(const freqman_entry& entry) {
if (entry.frequency_a == 0)
return false;
// Frequency B must be set for type Range or Ham Radio
if (entry.type == freqman_type::Range || entry.type == freqman_type::HamRadio) {
// Frequency B must be set for type Range or HamRadio or Repeater
if (entry.type == freqman_type::Range || entry.type == freqman_type::HamRadio || entry.type == freqman_type::Repeater) {
if (entry.frequency_b == 0)
return false;
}
+3 -1
View File
@@ -56,6 +56,7 @@ enum class freqman_type : uint8_t {
Single, // f=
Range, // a=,b=
HamRadio, // r=,t=
Repeater, // l=,t=
Raw, // line content in description
Unknown,
};
@@ -167,6 +168,7 @@ struct freqman_load_options {
bool load_freqs{true};
bool load_ranges{true};
bool load_hamradios{true};
bool load_repeaters{true};
};
using freqman_entry_ptr = std::unique_ptr<freqman_entry>;
@@ -277,4 +279,4 @@ class FreqmanDB {
bool read_raw_{true};
};
#endif /* __FREQMAN_DB_H__ */
#endif /* __FREQMAN_DB_H__ */
+1 -1
View File
@@ -285,7 +285,7 @@
* buffers.
*/
#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
#define SERIAL_BUFFERS_SIZE 16
#define SERIAL_BUFFERS_SIZE 64
#endif
/*===========================================================================*/
+25 -5
View File
@@ -122,6 +122,8 @@ static bool touch_update() {
}
static uint8_t switches_raw = 0;
static uint8_t injected_switch = 0;
static uint8_t injected_encoder = 0;
/* The raw data is not packed in a way that makes looping over it easy.
* One option would be an accessor helper (RawSwitch). Another option
@@ -169,9 +171,13 @@ static bool encoder_update(const uint8_t raw) {
}
static bool encoder_read() {
const auto delta = encoder.update(
encoder_debounce[0].state(),
encoder_debounce[1].state());
auto delta = encoder.update(encoder_debounce[0].state(), encoder_debounce[1].state());
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;
@@ -186,10 +192,10 @@ void timer0_callback(GPTDriver* const) {
if (touch_update()) event_mask |= EVT_MASK_TOUCH;
switches_raw = swizzled_switches();
if (switches_update(switches_raw))
if (switches_update(switches_raw) || (injected_switch > 0))
event_mask |= EVT_MASK_SWITCHES;
if (encoder_update(switches_raw) && encoder_read())
if (encoder_update(switches_raw) || encoder_read())
event_mask |= EVT_MASK_ENCODER;
/* Signal event loop */
@@ -238,6 +244,13 @@ SwitchesState get_switches_state() {
for (size_t i = 0; i < result.size(); i++)
result[i] = switch_debounce[i].state();
if (injected_switch > 0 && injected_switch <= 6) {
result[injected_switch - 1] = 1;
injected_switch = 0xff;
} else if (injected_switch == 0xff) {
injected_switch = 0x00;
}
return result;
}
@@ -277,5 +290,12 @@ uint8_t switches() {
return switches_raw;
}
void inject_switch(uint8_t button) {
if (button <= 6)
injected_switch = button;
else if (button > 6)
injected_encoder = button - 6;
}
} // namespace debug
} // namespace control
+1
View File
@@ -56,6 +56,7 @@ namespace control {
namespace debug {
uint8_t switches();
void inject_switch(uint8_t);
} // namespace debug
} // namespace control
+1 -1
View File
@@ -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();
}
+8 -1
View File
@@ -70,6 +70,7 @@ lcd::ILI9341 display;
I2C i2c0(&I2CD0);
SPI ssp1(&SPID2);
portapack::USBSerial usb_serial;
si5351::Si5351 clock_generator{
i2c0, hackrf::one::si5351_i2c_address};
@@ -368,7 +369,7 @@ static void shutdown_base() {
*
* XTAL_OSC = powered down
*
* PLL0USB = powered down
* PLL0USB = XTAL, 480 MHz
* PLL0AUDIO = GP_CLKIN, Fcco=491.52 MHz, Fout=12.288 MHz
* PLL1 =
* OG: GP_CLKIN * 10 = 200 MHz
@@ -464,6 +465,8 @@ bool init() {
/* Remove /2P divider from PLL1 output to achieve full speed */
cgu::pll1::direct();
usb_serial.initialize();
i2c0.start(i2c_config_fast_clock);
chThdSleepMilliseconds(10);
@@ -539,4 +542,8 @@ void shutdown(const bool leave_screen_on) {
shutdown_base();
}
void setEventDispatcherToUSBSerial(EventDispatcher* evt) {
usb_serial.setEventDispatcher(evt);
}
} /* namespace portapack */
+4
View File
@@ -31,6 +31,7 @@
#include "si5351.hpp"
#include "lcd_ili9341.hpp"
#include "backlight.hpp"
#include "usb_serial.hpp"
#include "radio.hpp"
#include "clock_manager.hpp"
@@ -46,6 +47,7 @@ extern lcd::ILI9341 display;
extern I2C i2c0;
extern SPI ssp1;
extern portapack::USBSerial usb_serial;
extern si5351::Si5351 clock_generator;
extern ClockManager clock_manager;
@@ -66,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 */
-71
View File
@@ -1,71 +0,0 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "lcr.hpp"
#include "string_format.hpp"
namespace lcr {
std::string generate_message(std::string rgsb, std::vector<std::string> litterals, size_t option_ec) {
const std::string ec_lut[4] = {"A", "J", "N", "S"}; // Eclairage (Auto, Jour, Nuit)
char eom[3] = {3, 0, 0}; // EOM and space for checksum
uint8_t i;
std::string lcr_message{127, 127, 127, 127, 127, 127, 127, 5}; // 5/15 ? Modem sync and SOM
char checksum = 0;
// Pad litterals to 7 chars (not required ?)
for (auto& litteral : litterals)
while (litteral.length() < 7)
litteral += ' ';
// Compose LCR message
lcr_message += rgsb;
lcr_message += "PA ";
i = 1;
for (auto& litteral : litterals) {
lcr_message += "AM=";
lcr_message += to_string_dec_uint(i, 1);
lcr_message += " AF=\"";
lcr_message += litteral;
lcr_message += "\" CL=0 ";
i++;
}
lcr_message += "EC=";
lcr_message += ec_lut[option_ec];
lcr_message += " SAB=0";
// Checksum
i = 7; // Skip modem sync
while (lcr_message[i])
checksum ^= lcr_message[i++];
checksum ^= eom[0]; // EOM char
checksum &= 0x7F; // Trim
eom[1] = checksum;
lcr_message += eom;
return lcr_message;
}
} /* namespace lcr */
+305
View File
@@ -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;
}
}
}
/** @} */
+83
View File
@@ -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_ */
/** @} */
+22
View File
@@ -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;
+2
View File
@@ -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);
+117 -43
View File
@@ -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;
@@ -255,6 +315,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 +390,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;
@@ -413,7 +480,7 @@ 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_position(float lat, float lon, uint16_t angle, int32_t altitude, int32_t speed) {
if (geomap.manual_panning()) {
geomap.set_dirty();
return;
@@ -422,12 +489,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 +515,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 +552,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 +560,7 @@ GeoMapView::GeoMapView(
: nav_(nav),
altitude_(altitude),
altitude_unit_(altitude_unit),
speed_unit_(speed_unit),
lat_(lat),
lon_(lon),
angle_(angle),
@@ -516,12 +588,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 +614,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();
};
}
+39 -9
View File
@@ -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,7 @@ 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;
bool init();
void set_mode(GeoMapMode mode);
@@ -187,7 +212,7 @@ 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 map_read_line(ui::Color* buffer, uint16_t pixels);
bool manual_panning_{false};
GeoMapMode mode_{};
@@ -220,6 +245,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 +253,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 +266,7 @@ 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);
std::string title() const override { return "Map view"; };
@@ -253,10 +280,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 +295,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}};
+13
View File
@@ -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;
+1
View File
@@ -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();
+33 -1
View File
@@ -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) {

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