mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-13 18:19:33 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fe88760766 | |||
| 75ece38725 | |||
| b53032a8a5 | |||
| e7f8952ece | |||
| 82a6ae0791 | |||
| d303098e35 | |||
| 1a69ce2d97 | |||
| eedebe1c52 | |||
| 9e61f80809 | |||
| ff591c68a0 | |||
| 8761b9d7e0 | |||
| 1b5125b0a8 | |||
| 9ba885361d | |||
| afa34d83b2 | |||
| d93c914dd3 | |||
| 715a2dd448 | |||
| 58bf60695d | |||
| fbe7954f2e | |||
| d122a8fc3b | |||
| 3998dc124a | |||
| 3d2da9c0db | |||
| 3189d3af4c | |||
| 1b3a99cc5e | |||
| 935c43e88f | |||
| 2893c031ab | |||
| 94cdb16ca9 | |||
| 84d4066986 | |||
| 2153c2dd10 | |||
| a7393c3492 | |||
| 794fece8cc | |||
| 1bf95e85a0 | |||
| a85357a8af | |||
| 7bf3e02f6c | |||
| 459e8d0b24 | |||
| 93585d846a | |||
| 4eb5c4603e | |||
| f42ad5ffc6 | |||
| d0bd8e50be | |||
| 337e3f4449 | |||
| 53bbf74f4d | |||
| 19a66fac20 | |||
| 36e1b9a36e | |||
| 1ca9288bbe | |||
| 4ca2d1e352 | |||
| 0b11730835 | |||
| 6069145b68 | |||
| 2ccda5aebd | |||
| 02810bf527 | |||
| b58ee761a1 | |||
| 5b9d898202 | |||
| 5f8e44e7b7 | |||
| ec80859d93 | |||
| 5e9d92d65c | |||
| c3bb9c0a16 |
+1
-1
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: https://salt.bountysource.com/checkout/amount?team=portapack-mayhem
|
||||
custom: # https://salt.bountysource.com/checkout/amount?team=portapack-mayhem
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 @@
|
||||
v1.7.4
|
||||
v1.9.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.8.0
|
||||
v1.9.1
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@
|
||||
*.map
|
||||
*.lst
|
||||
.dep/
|
||||
build/
|
||||
/build*
|
||||
CMakeFiles/
|
||||
|
||||
# Debugging
|
||||
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
cache: apt
|
||||
dist: xenial
|
||||
|
||||
env:
|
||||
global:
|
||||
- PROJECT_NAME=PortaPack-HAVOC
|
||||
- SHORT_COMMIT_HASH=`git rev-parse --short HEAD`
|
||||
- VERSION_STRING=nightly-$SHORT_COMMIT_HASH
|
||||
- BUILD_DATE="`date +%Y-%m-%d`"
|
||||
- BUILD_NAME="$PROJECT_NAME-$BUILD_DATE-$SHORT_COMMIT_HASH"
|
||||
- ARTEFACT_BASE=$TRAVIS_BUILD_DIR/artefacts/
|
||||
- ARTEFACT_PATH=$ARTEFACT_BASE/$BUILD_NAME
|
||||
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:team-gcc-arm-embedded/ppa -y
|
||||
- sudo apt-get update -q
|
||||
- sudo apt-get install gcc-arm-embedded -y
|
||||
|
||||
script:
|
||||
# TODO: Introduce top-level Makefile, this is lame.
|
||||
- sed -e "s/\#set(VERSION.*/set(VERSION \"$VERSION_STRING\")/" -i".bak" CMakeLists.txt
|
||||
- mkdir build/
|
||||
- pushd build/
|
||||
- cmake ..
|
||||
- make firmware
|
||||
- popd
|
||||
|
||||
after_success:
|
||||
- mkdir -p $ARTEFACT_PATH
|
||||
# Copy firmware to firmware-bin directory
|
||||
- cd $TRAVIS_BUILD_DIR/build
|
||||
- cp firmware/portapack-h1-havoc.bin $ARTEFACT_PATH/
|
||||
- cp hackrf/firmware/hackrf_usb/hackrf_usb.dfu $ARTEFACT_PATH/
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
- cp LICENSE $ARTEFACT_PATH/
|
||||
# Build the archive
|
||||
- cd $ARTEFACT_BASE
|
||||
- tar -cJvf $ARTEFACT_BASE/$BUILD_NAME.tar.xz $BUILD_NAME
|
||||
- md5sum --binary $BUILD_NAME.tar.xz >MD5SUMS
|
||||
- sha256sum --binary $BUILD_NAME.tar.xz >SHA256SUMS
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- coreutils
|
||||
- tar
|
||||
- sed
|
||||
- cmake
|
||||
- dfu-util
|
||||
+21
-2
@@ -18,15 +18,17 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_policy(SET CMP0005 NEW)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex-m.cmake)
|
||||
|
||||
project(portapack-h1)
|
||||
|
||||
set(EXPECTED_GCC_VERSION "9.2.1")
|
||||
|
||||
set(VERSION "$ENV{VERSION_STRING}")
|
||||
if ("$ENV{VERSION_STRING}" STREQUAL "")
|
||||
if ("${VERSION}" STREQUAL "")
|
||||
execute_process(
|
||||
COMMAND git log -n 1 --format=%h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
@@ -40,6 +42,11 @@ if ("$ENV{VERSION_STRING}" STREQUAL "")
|
||||
else (GIT_VERSION_FOUND)
|
||||
set(VERSION "${GIT_VERSION}")
|
||||
endif (GIT_VERSION_FOUND)
|
||||
|
||||
set(VERSION_NOHASH "dev")
|
||||
else()
|
||||
set(VERSION_NOHASH "${VERSION}")
|
||||
set(PPFW_FILENAME "portapack-mayhem.ppfw.tar")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
@@ -48,6 +55,18 @@ execute_process(
|
||||
)
|
||||
set(VERSION_MD5 "0x${VERSION_MD5}")
|
||||
|
||||
if (NOT DEFINED PPFW_FILENAME)
|
||||
# Funny business here: trying to generate unique names to prevent renaming of shared test-drive builds by the browser on download to .ppfw-42.tar as it wont be recognized by the flasher
|
||||
execute_process(
|
||||
COMMAND date "+%y%m%d-%H%M"
|
||||
OUTPUT_VARIABLE PPFW_FILENAME
|
||||
)
|
||||
string(STRIP ${PPFW_FILENAME} PPFW_FILENAME)
|
||||
set(PPFW_FILENAME "portapack-mayhem_${PPFW_FILENAME}_OCI.ppfw.tar")
|
||||
endif()
|
||||
|
||||
message("Building version: ${VERSION} MD5: ${VERSION_MD5}")
|
||||
|
||||
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
|
||||
set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
|
||||
|
||||
|
||||
@@ -1,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
|
||||
|
||||
[](https://travis-ci.com/eried/portapack-mayhem) [](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml) [](https://codescene.io/projects/8381) [](https://github.com/eried/portapack-mayhem/releases) [](https://github.com/eried/portapack-mayhem/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3)
|
||||
@@ -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:
|
||||
|
||||
[](https://www.youtube.com/watch?v=alrFbY5vxt4)
|
||||
[](https://www.youtube.com/watch?v=H-bqdWfbhpg)
|
||||
|
||||
For people familiar with the [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware), this one might be interesting:<br>[What is the HackRF One Portapack H2+?](https://www.youtube.com/watch?v=alrFbY5vxt4)
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
|
||||
+22
-14
@@ -10,32 +10,40 @@ VOLUME /havoc
|
||||
WORKDIR /havoc/firmware
|
||||
|
||||
# Fetch dependencies from APT
|
||||
RUN apt-get update && \
|
||||
apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl && \
|
||||
apt-get -qy autoremove
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl ninja-build \
|
||||
&& apt-get -qy autoremove \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
#Install current pip from PyPa
|
||||
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
|
||||
python3 get-pip.py
|
||||
|
||||
#Fetch additional dependencies from Python 3.x pip
|
||||
RUN pip install pyyaml
|
||||
RUN ln -s /usr/bin/python3 /usr/bin/python && \
|
||||
ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
RUN pip install pyyaml \
|
||||
&& ln -s /usr/bin/python3 /usr/bin/python \
|
||||
&& ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
# Grab the GNU ARM toolchain from arm.com
|
||||
# Then extract contents to /opt/build/armbin/
|
||||
RUN mkdir /opt/build && cd /opt/build && \
|
||||
wget -O gcc-arm-none-eabi $ARMBINURL && \
|
||||
mkdir armbin && \
|
||||
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
RUN mkdir /opt/build \
|
||||
&& cd /opt/build \
|
||||
&& wget -O gcc-arm-none-eabi $ARMBINURL \
|
||||
&& mkdir armbin \
|
||||
&& tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
|
||||
# Configure CCACHE
|
||||
RUN mkdir ~/bin && cd ~/bin && \
|
||||
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
|
||||
RUN mkdir ~/bin \
|
||||
&& cd ~/bin \
|
||||
&& for tool in gcc g++ cpp c++; do \
|
||||
ln -s $(which ccache) arm-none-eabi-$tool \
|
||||
; done
|
||||
|
||||
CMD cd .. && cd build && \
|
||||
cmake .. && make firmware
|
||||
ADD firmware/tools/docker-entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
|
||||
# replace make with ninja temporarily while building your image if you prefer to use that by default
|
||||
CMD ["make"]
|
||||
|
||||
@@ -24,4 +24,4 @@ RUN mkdir ~/bin && cd ~/bin && \
|
||||
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
|
||||
|
||||
CMD cd .. && cd build && \
|
||||
cmake .. && make firmware
|
||||
cmake .. && make ppfw
|
||||
|
||||
+22
-14
@@ -10,32 +10,40 @@ VOLUME /havoc
|
||||
WORKDIR /havoc/firmware
|
||||
|
||||
# Fetch dependencies from APT
|
||||
RUN apt-get update && \
|
||||
apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl && \
|
||||
apt-get -qy autoremove
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl ninja-build \
|
||||
&& apt-get -qy autoremove \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
#Install current pip from PyPa
|
||||
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
|
||||
python3 get-pip.py
|
||||
|
||||
#Fetch additional dependencies from Python 3.x pip
|
||||
RUN pip install pyyaml
|
||||
RUN ln -s /usr/bin/python3 /usr/bin/python && \
|
||||
ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
RUN pip install pyyaml \
|
||||
&& ln -s /usr/bin/python3 /usr/bin/python \
|
||||
&& ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
# Grab the GNU ARM toolchain from arm.com
|
||||
# Then extract contents to /opt/build/armbin/
|
||||
RUN mkdir /opt/build && cd /opt/build && \
|
||||
wget -O gcc-arm-none-eabi $ARMBINURL && \
|
||||
mkdir armbin && \
|
||||
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
RUN mkdir /opt/build \
|
||||
&& cd /opt/build \
|
||||
&& wget -O gcc-arm-none-eabi $ARMBINURL \
|
||||
&& mkdir armbin \
|
||||
&& tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
|
||||
# Configure CCACHE
|
||||
RUN mkdir ~/bin && cd ~/bin && \
|
||||
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
|
||||
RUN mkdir ~/bin \
|
||||
&& cd ~/bin \
|
||||
&& for tool in gcc g++ cpp c++; do \
|
||||
ln -s $(which ccache) arm-none-eabi-$tool \
|
||||
; done
|
||||
|
||||
CMD cd .. && cd build && \
|
||||
cmake .. && make firmware
|
||||
ADD firmware/tools/docker-entrypoint.sh /usr/local/bin/entrypoint.sh
|
||||
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
|
||||
|
||||
# replace make with ninja temporarily while building your image if you prefer to use that by default
|
||||
CMD ["make"]
|
||||
|
||||
+50
-1
@@ -18,6 +18,8 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(firmware)
|
||||
|
||||
set(BASEBAND ${PROJECT_SOURCE_DIR}/baseband)
|
||||
@@ -34,6 +36,17 @@ set(LZ4 lz4)
|
||||
set(FIRMWARE_NAME portapack-h1_h2-mayhem)
|
||||
set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
|
||||
|
||||
# In our current build container cmake need a little help to get the version :)
|
||||
if(NOT DEFINED ${CMAKE_CXX_COMPILER_VERSION})
|
||||
execute_process(COMMAND bash "-c" "arm-none-eabi-g++ -v 2>&1 | grep 'gcc version' | awk '{print $3}'" OUTPUT_VARIABLE CMAKE_CXX_COMPILER_VERSION)
|
||||
string(STRIP ${CMAKE_CXX_COMPILER_VERSION} CMAKE_CXX_COMPILER_VERSION)
|
||||
endif()
|
||||
|
||||
set(GCC_VERSION_MISMATCH 0)
|
||||
if(NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL ${EXPECTED_GCC_VERSION})
|
||||
set(GCC_VERSION_MISMATCH 1)
|
||||
endif()
|
||||
|
||||
add_subdirectory(application)
|
||||
add_subdirectory(baseband)
|
||||
add_subdirectory(test)
|
||||
@@ -48,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
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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}};
|
||||
|
||||
|
||||
@@ -54,24 +54,31 @@ std::string id(ID value) {
|
||||
}
|
||||
|
||||
std::string consumption(Consumption value) {
|
||||
return to_string_dec_uint(value, 10);
|
||||
return to_string_dec_uint(value, 8);
|
||||
}
|
||||
|
||||
std::string commodity_type(CommodityType value) {
|
||||
return to_string_dec_uint(value, 2);
|
||||
}
|
||||
|
||||
std::string tamper_flags(TamperFlags value) {
|
||||
return to_string_hex(value & 0xFFFF, 4); // Note: ignoring bits 32-47 of tamper flags in IDM type due to screen width
|
||||
}
|
||||
|
||||
std::string tamper_flags_scm(TamperFlags value) {
|
||||
return " " + to_string_hex(value & 0x0F, 1) + "/" + to_string_hex(value >> 4, 1); // Physical/Encoder flags
|
||||
}
|
||||
|
||||
} /* namespace format */
|
||||
|
||||
} /* namespace ert */
|
||||
|
||||
void ERTLogger::on_packet(const ert::Packet& packet, const uint32_t target_frequency) {
|
||||
const auto formatted = packet.symbols_formatted();
|
||||
|
||||
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
|
||||
const auto target_frequency_str = to_string_dec_uint(target_frequency, 10);
|
||||
|
||||
std::string entry = target_frequency_str + " " + ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors;
|
||||
std::string entry = target_frequency_str + " " + ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors + " ID:" + to_string_dec_uint(packet.id(), 1);
|
||||
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
}
|
||||
|
||||
@@ -81,6 +88,8 @@ void ERTRecentEntry::update(const ert::Packet& packet) {
|
||||
received_count++;
|
||||
|
||||
last_consumption = packet.consumption();
|
||||
last_tamper_flags = packet.tamper_flags();
|
||||
packet_type = packet.type();
|
||||
}
|
||||
|
||||
namespace ui {
|
||||
@@ -91,13 +100,10 @@ void RecentEntriesTable<ERTRecentEntries>::draw(
|
||||
const Rect& target_rect,
|
||||
Painter& painter,
|
||||
const Style& style) {
|
||||
std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption);
|
||||
std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption) + " ";
|
||||
|
||||
if (entry.received_count > 999) {
|
||||
line += " +++";
|
||||
} else {
|
||||
line += " " + to_string_dec_uint(entry.received_count, 3);
|
||||
}
|
||||
line += (entry.packet_type == ert::Packet::Type::SCM) ? ert::format::tamper_flags_scm(entry.last_tamper_flags) : ert::format::tamper_flags(entry.last_tamper_flags);
|
||||
line += (entry.received_count > 99) ? " ++" : to_string_dec_uint(entry.received_count, 3);
|
||||
|
||||
line.resize(target_rect.width() / 8, ' ');
|
||||
painter.draw_string(target_rect.location(), style, line);
|
||||
|
||||
@@ -72,11 +72,12 @@ struct ERTRecentEntry {
|
||||
|
||||
ert::ID id{ert::invalid_id};
|
||||
ert::CommodityType commodity_type{ert::invalid_commodity_type};
|
||||
ert::Consumption last_consumption{};
|
||||
ert::TamperFlags last_tamper_flags{};
|
||||
ert::Packet::Type packet_type{};
|
||||
|
||||
size_t received_count{0};
|
||||
|
||||
ert::Consumption last_consumption{};
|
||||
|
||||
ERTRecentEntry(
|
||||
const Key& key)
|
||||
: id{key.id},
|
||||
@@ -137,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{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -85,10 +85,12 @@ ADSBPositionView::ADSBPositionView(
|
||||
nav.push<GeoMapView>(
|
||||
geopos.altitude(),
|
||||
GeoPos::alt_unit::FEET,
|
||||
GeoPos::spd_unit::HIDDEN,
|
||||
geopos.lat(),
|
||||
geopos.lon(),
|
||||
[this](int32_t altitude, float lat, float lon) {
|
||||
[this](int32_t altitude, float lat, float lon, int32_t speed) {
|
||||
geopos.set_altitude(altitude);
|
||||
geopos.set_speed(speed);
|
||||
geopos.set_lat(lat);
|
||||
geopos.set_lon(lon);
|
||||
});
|
||||
|
||||
@@ -58,7 +58,8 @@ class ADSBPositionView : public OptionTabView {
|
||||
private:
|
||||
GeoPos geopos{
|
||||
{0, 2 * 16},
|
||||
GeoPos::FEET};
|
||||
GeoPos::FEET,
|
||||
GeoPos::HIDDEN};
|
||||
|
||||
Button button_set_map{
|
||||
{8 * 8, 6 * 16, 14 * 8, 2 * 16},
|
||||
|
||||
@@ -317,7 +317,7 @@ void APRSDetailsView::update() {
|
||||
}
|
||||
|
||||
if (send_updates)
|
||||
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0);
|
||||
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0, 0);
|
||||
}
|
||||
|
||||
APRSDetailsView::~APRSDetailsView() {
|
||||
@@ -339,6 +339,7 @@ APRSDetailsView::APRSDetailsView(
|
||||
entry_copy.source_formatted,
|
||||
0, // entry_copy.pos.altitude,
|
||||
GeoPos::alt_unit::FEET,
|
||||
GeoPos::spd_unit::HIDDEN,
|
||||
entry_copy.pos.latitude,
|
||||
entry_copy.pos.longitude,
|
||||
0, /*entry_copy.velo.heading,*/
|
||||
|
||||
@@ -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, ®isters_widget, &text_checksum, &text_checksum2, &button_ok});
|
||||
: registers_widget(RegistersWidgetConfig{CT_PMEM, PMEM_SIZE_BYTES, page_size, 8}) {
|
||||
add_children({®isters_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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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}};
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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}};
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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()},
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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
|
||||
@@ -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__*/
|
||||
@@ -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
|
||||
|
||||
@@ -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()},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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}};
|
||||
|
||||
+6
-5
@@ -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
|
||||
+2
-2
@@ -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
@@ -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
|
||||
};
|
||||
}
|
||||
+530
-532
File diff suppressed because it is too large
Load Diff
+10
-4
@@ -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
@@ -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
|
||||
};
|
||||
}
|
||||
Vendored
+2
-2
@@ -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 */
|
||||
Vendored
+5
-4
@@ -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
@@ -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
@@ -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
|
||||
|
||||
}
|
||||
|
||||
Vendored
+4
-3
@@ -36,7 +36,7 @@
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::gpssim {
|
||||
|
||||
void GpsSimAppView::set_ready() {
|
||||
ready_signal = true;
|
||||
@@ -161,7 +161,8 @@ void GpsSimAppView::handle_replay_thread_done(const uint32_t return_code) {
|
||||
GpsSimAppView::GpsSimAppView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_gps);
|
||||
// baseband::run_image(portapack::spi_flash::image_tag_gps);
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({
|
||||
&button_open,
|
||||
@@ -211,4 +212,4 @@ void GpsSimAppView::set_parent_rect(const Rect new_parent_rect) {
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui::external_app::gpssim */
|
||||
Vendored
+4
-3
@@ -25,6 +25,7 @@
|
||||
#define __GPS_SIM_APP_HPP__
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -37,7 +38,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::gpssim {
|
||||
|
||||
class GpsSimAppView : public View {
|
||||
public:
|
||||
@@ -108,7 +109,7 @@ class GpsSimAppView : public View {
|
||||
Checkbox check_loop{
|
||||
{21 * 8, 2 * 16},
|
||||
4,
|
||||
"Loop",
|
||||
LanguageHelper::currentMessages[LANG_LOOP],
|
||||
true};
|
||||
ImageButton button_play{
|
||||
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
|
||||
@@ -142,6 +143,6 @@ class GpsSimAppView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui::external_app::gpssim */
|
||||
|
||||
#endif /*__GPS_SIM_APP_HPP__*/
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "gps_sim_app.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::gpssim {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<GpsSimAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::gpssim
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_gpssim.application_information"), used)) application_information_t _application_information_gpssim = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::gpssim::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "GPSSim",
|
||||
/*.bitmap_data = */ {
|
||||
0xC0,
|
||||
0x07,
|
||||
0xE0,
|
||||
0x0F,
|
||||
0x70,
|
||||
0x1F,
|
||||
0x78,
|
||||
0x3E,
|
||||
0x78,
|
||||
0x3C,
|
||||
0x78,
|
||||
0x38,
|
||||
0x78,
|
||||
0x30,
|
||||
0x78,
|
||||
0x38,
|
||||
0x78,
|
||||
0x3C,
|
||||
0x70,
|
||||
0x1E,
|
||||
0x70,
|
||||
0x1F,
|
||||
0xE0,
|
||||
0x0F,
|
||||
0xC0,
|
||||
0x07,
|
||||
0x80,
|
||||
0x03,
|
||||
0x20,
|
||||
0x09,
|
||||
0x50,
|
||||
0x14,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_gpssim */ {'P', 'G', 'P', 'S'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_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
|
||||
};
|
||||
}
|
||||
Vendored
+4
-3
@@ -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
|
||||
Vendored
+6
-5
@@ -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
@@ -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
|
||||
};
|
||||
}
|
||||
+47
-5
@@ -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 */
|
||||
+8
-6
@@ -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 */
|
||||
+2
-2
@@ -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 */
|
||||
+4
-3
@@ -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
@@ -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
@@ -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
|
||||
};
|
||||
}
|
||||
Vendored
+4
-3
@@ -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
|
||||
Vendored
+4
-3
@@ -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
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_spectrum_painter.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<SpectrumPainterView>();
|
||||
}
|
||||
} // namespace ui::external_app::spainter
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_spainter.application_information"), used)) application_information_t _application_information_spainter = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::spainter::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "S.Painter",
|
||||
/*.bitmap_data = */ {
|
||||
0xFE,
|
||||
0x3F,
|
||||
0xFF,
|
||||
0x3F,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xFF,
|
||||
0xBF,
|
||||
0xFE,
|
||||
0xBF,
|
||||
0x00,
|
||||
0x80,
|
||||
0x80,
|
||||
0xFF,
|
||||
0x80,
|
||||
0x00,
|
||||
0x80,
|
||||
0x00,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
0xC0,
|
||||
0x01,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_spainter */ {'P', 'S', 'P', 'T'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+4
-3
@@ -31,12 +31,13 @@
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
SpectrumPainterView::SpectrumPainterView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
baseband::run_image(spi_flash::image_tag_spectrum_painter);
|
||||
// baseband::run_image(spi_flash::image_tag_spectrum_painter);
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
@@ -198,4 +199,4 @@ void SpectrumPainterView::paint(Painter& painter) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+4
-3
@@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -37,7 +38,7 @@
|
||||
#include "ui_spectrum_painter_image.hpp"
|
||||
#include "ui_spectrum_painter_text.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
class SpectrumPainterView : public View {
|
||||
public:
|
||||
@@ -115,7 +116,7 @@ class SpectrumPainterView : public View {
|
||||
Checkbox check_loop{
|
||||
{21 * 8, footer_location + 1 * 16},
|
||||
4,
|
||||
"Loop",
|
||||
LanguageHelper::currentMessages[LANG_LOOP],
|
||||
true};
|
||||
|
||||
ImageButton button_play{
|
||||
@@ -163,4 +164,4 @@ class SpectrumPainterView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+2
-2
@@ -29,7 +29,7 @@
|
||||
#include "file.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
|
||||
hidden(true);
|
||||
@@ -254,4 +254,4 @@ void SpectrumInputImageView::paint(Painter& painter) {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+2
-2
@@ -32,7 +32,7 @@
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
class SpectrumInputImageView : public View {
|
||||
public:
|
||||
@@ -63,4 +63,4 @@ class SpectrumInputImageView : public View {
|
||||
bool drawBMP_scaled(const ui::Rect r, const std::string file);
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+2
-2
@@ -30,7 +30,7 @@
|
||||
#include "file.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
SpectrumInputTextView::SpectrumInputTextView(NavigationView& nav) {
|
||||
hidden(true);
|
||||
@@ -105,4 +105,4 @@ std::vector<uint8_t> SpectrumInputTextView::get_line(uint16_t y) {
|
||||
|
||||
return data;
|
||||
}
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
+2
-2
@@ -32,7 +32,7 @@
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
class SpectrumInputTextView : public View {
|
||||
public:
|
||||
@@ -109,4 +109,4 @@ class SpectrumInputTextView : public View {
|
||||
"Set message"};
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::spainter
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -285,7 +285,7 @@
|
||||
* buffers.
|
||||
*/
|
||||
#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
|
||||
#define SERIAL_BUFFERS_SIZE 16
|
||||
#define SERIAL_BUFFERS_SIZE 64
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace control {
|
||||
namespace debug {
|
||||
|
||||
uint8_t switches();
|
||||
void inject_switch(uint8_t);
|
||||
|
||||
} // namespace debug
|
||||
} // namespace control
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file shell.c
|
||||
* @brief Simple CLI shell code.
|
||||
*
|
||||
* @addtogroup SHELL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include "shell.hpp"
|
||||
#include "chprintf.h"
|
||||
#include "portapack.hpp"
|
||||
|
||||
/**
|
||||
* @brief Shell termination event source.
|
||||
*/
|
||||
EventSource shell_terminated;
|
||||
|
||||
static char* _strtok(char* str, const char* delim, char** saveptr) {
|
||||
char* token;
|
||||
if (str)
|
||||
*saveptr = str;
|
||||
token = *saveptr;
|
||||
|
||||
if (!token)
|
||||
return NULL;
|
||||
|
||||
token += strspn(token, delim);
|
||||
*saveptr = strpbrk(token, delim);
|
||||
if (*saveptr)
|
||||
*(*saveptr)++ = '\0';
|
||||
|
||||
return *token ? token : NULL;
|
||||
}
|
||||
|
||||
static void usage(BaseSequentialStream* chp, char* p) {
|
||||
chprintf(chp, "Usage: %s\r\n", p);
|
||||
}
|
||||
|
||||
static void list_commands(BaseSequentialStream* chp, const ShellCommand* scp) {
|
||||
while (scp->sc_name != NULL) {
|
||||
chprintf(chp, "%s ", scp->sc_name);
|
||||
scp++;
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_info(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
if (argc > 0) {
|
||||
usage(chp, const_cast<char*>("info"));
|
||||
return;
|
||||
}
|
||||
|
||||
chprintf(chp, "Kernel: %s\r\n", CH_KERNEL_VERSION);
|
||||
#ifdef CH_COMPILER_NAME
|
||||
chprintf(chp, "Compiler: %s\r\n", CH_COMPILER_NAME);
|
||||
#endif
|
||||
chprintf(chp, "Architecture: %s\r\n", CH_ARCHITECTURE_NAME);
|
||||
#ifdef CH_CORE_VARIANT_NAME
|
||||
chprintf(chp, "Core Variant: %s\r\n", CH_CORE_VARIANT_NAME);
|
||||
#endif
|
||||
#ifdef CH_PORT_INFO
|
||||
chprintf(chp, "Port Info: %s\r\n", CH_PORT_INFO);
|
||||
#endif
|
||||
#ifdef PLATFORM_NAME
|
||||
chprintf(chp, "Platform: %s\r\n", PLATFORM_NAME);
|
||||
#endif
|
||||
#ifdef BOARD_NAME
|
||||
chprintf(chp, "Board: %s\r\n", BOARD_NAME);
|
||||
#endif
|
||||
#ifdef VERSION_STRING
|
||||
chprintf(chp, "Mayhem Version: %s\r\n", VERSION_STRING);
|
||||
#endif
|
||||
chprintf(chp, "HackRF Board Rev: %s\r\n", hackrf_r9 ? "R9" : "R1-R8");
|
||||
chprintf(chp, "Reference Source: %s\r\n", portapack::clock_manager.get_source().c_str());
|
||||
chprintf(chp, "Reference Freq: %s\r\n", portapack::clock_manager.get_freq().c_str());
|
||||
#ifdef __DATE__
|
||||
#ifdef __TIME__
|
||||
chprintf(chp, "Build time: %s%s%s\r\n", __DATE__, " - ", __TIME__);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cmd_systime(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
if (argc > 0) {
|
||||
usage(chp, const_cast<char*>("systime"));
|
||||
return;
|
||||
}
|
||||
chprintf(chp, "%lu\r\n", (unsigned long)chTimeNow());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Array of the default commands.
|
||||
*/
|
||||
static ShellCommand local_commands[] = {
|
||||
{"info", cmd_info},
|
||||
{"systime", cmd_systime},
|
||||
{NULL, NULL}};
|
||||
|
||||
static bool_t cmdexec(const ShellCommand* scp, BaseSequentialStream* chp, char* name, int argc, char* argv[]) {
|
||||
while (scp->sc_name != NULL) {
|
||||
if (strcmp(scp->sc_name, name) == 0) {
|
||||
scp->sc_function(chp, argc, argv);
|
||||
return FALSE;
|
||||
}
|
||||
scp++;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell thread function.
|
||||
*
|
||||
* @param[in] p pointer to a @p BaseSequentialStream object
|
||||
* @return Termination reason.
|
||||
* @retval RDY_OK terminated by command.
|
||||
* @retval RDY_RESET terminated by reset condition on the I/O channel.
|
||||
*
|
||||
* @notapi
|
||||
*/
|
||||
static msg_t shell_thread(void* p) {
|
||||
int n;
|
||||
BaseSequentialStream* chp = ((ShellConfig*)p)->sc_channel;
|
||||
const ShellCommand* scp = ((ShellConfig*)p)->sc_commands;
|
||||
char *lp, *cmd, *tokp, line[SHELL_MAX_LINE_LENGTH];
|
||||
char* args[SHELL_MAX_ARGUMENTS + 1];
|
||||
|
||||
chRegSetThreadName("shell");
|
||||
chprintf(chp, "\r\nChibiOS/RT Shell\r\n");
|
||||
while (TRUE) {
|
||||
chprintf(chp, "ch> ");
|
||||
if (shellGetLine(chp, line, sizeof(line))) {
|
||||
chprintf(chp, "\r\nlogout");
|
||||
break;
|
||||
}
|
||||
lp = _strtok(line, " \t", &tokp);
|
||||
cmd = lp;
|
||||
n = 0;
|
||||
while ((lp = _strtok(NULL, " \t", &tokp)) != NULL) {
|
||||
if (n >= SHELL_MAX_ARGUMENTS) {
|
||||
chprintf(chp, "too many arguments\r\n");
|
||||
cmd = NULL;
|
||||
break;
|
||||
}
|
||||
args[n++] = lp;
|
||||
}
|
||||
args[n] = NULL;
|
||||
if (cmd != NULL) {
|
||||
if (strcmp(cmd, "exit") == 0) {
|
||||
if (n > 0) {
|
||||
usage(chp, const_cast<char*>("exit"));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
} else if (strcmp(cmd, "help") == 0) {
|
||||
if (n > 0) {
|
||||
usage(chp, const_cast<char*>("help"));
|
||||
continue;
|
||||
}
|
||||
chprintf(chp, "Commands: help exit ");
|
||||
list_commands(chp, local_commands);
|
||||
if (scp != NULL)
|
||||
list_commands(chp, scp);
|
||||
chprintf(chp, "\r\n");
|
||||
} else if (cmdexec(local_commands, chp, cmd, n, args) &&
|
||||
((scp == NULL) || cmdexec(scp, chp, cmd, n, args))) {
|
||||
chprintf(chp, "%s", cmd);
|
||||
chprintf(chp, " ?\r\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
shellExit(RDY_OK);
|
||||
/* Never executed, silencing a warning.*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell manager initialization.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void shellInit(void) {
|
||||
chEvtInit(&shell_terminated);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Terminates the shell.
|
||||
* @note Must be invoked from the command handlers.
|
||||
* @note Does not return.
|
||||
*
|
||||
* @param[in] msg shell exit code
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
void shellExit(msg_t msg) {
|
||||
/* Atomically broadcasting the event source and terminating the thread,
|
||||
there is not a chSysUnlock() because the thread terminates upon return.*/
|
||||
chSysLock();
|
||||
chEvtBroadcastI(&shell_terminated);
|
||||
chThdExitS(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Spawns a new shell.
|
||||
* @pre @p CH_USE_HEAP and @p CH_USE_DYNAMIC must be enabled.
|
||||
*
|
||||
* @param[in] scp pointer to a @p ShellConfig object
|
||||
* @param[in] size size of the shell working area to be allocated
|
||||
* @param[in] prio priority level for the new shell
|
||||
* @return A pointer to the shell thread.
|
||||
* @retval NULL thread creation failed because memory allocation.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
#if CH_USE_HEAP && CH_USE_DYNAMIC
|
||||
Thread* shellCreate(const ShellConfig* scp, size_t size, tprio_t prio) {
|
||||
return chThdCreateFromHeap(NULL, size, prio, shell_thread, (void*)scp);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create statically allocated shell thread.
|
||||
*
|
||||
* @param[in] scp pointer to a @p ShellConfig object
|
||||
* @param[in] wsp pointer to a working area dedicated to the shell thread stack
|
||||
* @param[in] size size of the shell working area
|
||||
* @param[in] prio priority level for the new shell
|
||||
* @return A pointer to the shell thread.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
Thread* shellCreateStatic(const ShellConfig* scp, void* wsp, size_t size, tprio_t prio) {
|
||||
return chThdCreateStatic(wsp, size, prio, shell_thread, (void*)scp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads a whole line from the input channel.
|
||||
*
|
||||
* @param[in] chp pointer to a @p BaseSequentialStream object
|
||||
* @param[in] line pointer to the line buffer
|
||||
* @param[in] size buffer maximum length
|
||||
* @return The operation status.
|
||||
* @retval TRUE the channel was reset or CTRL-D pressed.
|
||||
* @retval FALSE operation successful.
|
||||
*
|
||||
* @api
|
||||
*/
|
||||
bool_t shellGetLine(BaseSequentialStream* chp, char* line, unsigned size) {
|
||||
char* p = line;
|
||||
|
||||
while (TRUE) {
|
||||
char c;
|
||||
|
||||
if (chSequentialStreamRead(chp, (uint8_t*)&c, 1) == 0)
|
||||
return TRUE;
|
||||
if (c == 4) {
|
||||
chprintf(chp, "^D");
|
||||
return TRUE;
|
||||
}
|
||||
if ((c == 8) || (c == 127)) {
|
||||
if (p != line) {
|
||||
chSequentialStreamPut(chp, c);
|
||||
chSequentialStreamPut(chp, 0x20);
|
||||
chSequentialStreamPut(chp, c);
|
||||
p--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (c == '\r') {
|
||||
chprintf(chp, "\r\n");
|
||||
*p = 0;
|
||||
return FALSE;
|
||||
}
|
||||
if (c < 0x20)
|
||||
continue;
|
||||
if (p < line + size - 1) {
|
||||
chSequentialStreamPut(chp, c);
|
||||
*p++ = (char)c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @} */
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file shell.h
|
||||
* @brief Simple CLI shell header.
|
||||
*
|
||||
* @addtogroup SHELL
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _SHELL_H_
|
||||
#define _SHELL_H_
|
||||
|
||||
/**
|
||||
* @brief Shell maximum input line length.
|
||||
*/
|
||||
#if !defined(SHELL_MAX_LINE_LENGTH) || defined(__DOXYGEN__)
|
||||
#define SHELL_MAX_LINE_LENGTH 64
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Shell maximum arguments per command.
|
||||
*/
|
||||
#if !defined(SHELL_MAX_ARGUMENTS) || defined(__DOXYGEN__)
|
||||
#define SHELL_MAX_ARGUMENTS 6
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Command handler function type.
|
||||
*/
|
||||
typedef void (*shellcmd_t)(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
|
||||
/**
|
||||
* @brief Custom command entry type.
|
||||
*/
|
||||
typedef struct {
|
||||
const char* sc_name; /**< @brief Command name. */
|
||||
shellcmd_t sc_function; /**< @brief Command function. */
|
||||
} ShellCommand;
|
||||
|
||||
/**
|
||||
* @brief Shell descriptor type.
|
||||
*/
|
||||
typedef struct {
|
||||
BaseSequentialStream* sc_channel; /**< @brief I/O channel associated
|
||||
to the shell. */
|
||||
const ShellCommand* sc_commands; /**< @brief Shell extra commands
|
||||
table. */
|
||||
} ShellConfig;
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
extern EventSource shell_terminated;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void shellInit(void);
|
||||
void shellExit(msg_t msg);
|
||||
Thread* shellCreate(const ShellConfig* scp, size_t size, tprio_t prio);
|
||||
Thread* shellCreateStatic(const ShellConfig* scp, void* wsp, size_t size, tprio_t prio);
|
||||
bool_t shellGetLine(BaseSequentialStream* chp, char* line, unsigned size);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SHELL_H_ */
|
||||
|
||||
/** @} */
|
||||
@@ -92,6 +92,28 @@ void FreqManUIList::on_blur() {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
bool FreqManUIList::on_keyboard(const KeyboardEvent key) {
|
||||
if (!db_ || db_->empty())
|
||||
return false;
|
||||
|
||||
auto delta = 0;
|
||||
if (key == '-' && get_index() > 0) delta = -1;
|
||||
if (key == '+' && get_index() < db_->entry_count() - 1) delta = 1;
|
||||
if (delta != 0) {
|
||||
adjust_selected_index(delta);
|
||||
set_dirty();
|
||||
return true;
|
||||
}
|
||||
if (key == 10) {
|
||||
if (on_select) {
|
||||
on_select(get_index());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FreqManUIList::on_key(const KeyEvent key) {
|
||||
if (!db_ || db_->empty())
|
||||
return false;
|
||||
|
||||
@@ -50,6 +50,8 @@ class FreqManUIList : public Widget {
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_encoder(EncoderEvent delta) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
void set_parent_rect(Rect new_parent_rect) override;
|
||||
|
||||
void set_index(size_t index);
|
||||
|
||||
@@ -38,13 +38,17 @@ namespace ui {
|
||||
|
||||
GeoPos::GeoPos(
|
||||
const Point pos,
|
||||
const alt_unit altitude_unit)
|
||||
: altitude_unit_(altitude_unit) {
|
||||
const alt_unit altitude_unit,
|
||||
const spd_unit speed_unit)
|
||||
: altitude_unit_(altitude_unit), speed_unit_(speed_unit) {
|
||||
set_parent_rect({pos, {30 * 8, 3 * 16}});
|
||||
|
||||
add_children({&labels_position,
|
||||
&label_spd_position,
|
||||
&field_altitude,
|
||||
&field_speed,
|
||||
&text_alt_unit,
|
||||
&text_speed_unit,
|
||||
&field_lat_degrees,
|
||||
&field_lat_minutes,
|
||||
&field_lat_seconds,
|
||||
@@ -56,6 +60,7 @@ GeoPos::GeoPos(
|
||||
|
||||
// Defaults
|
||||
set_altitude(0);
|
||||
set_speed(0);
|
||||
set_lat(0);
|
||||
set_lon(0);
|
||||
|
||||
@@ -67,10 +72,11 @@ GeoPos::GeoPos(
|
||||
text_lon_decimal.set(to_string_decimal(lon_value, 5));
|
||||
|
||||
if (on_change && report_change)
|
||||
on_change(altitude(), lat_value, lon_value);
|
||||
on_change(altitude(), lat_value, lon_value, speed());
|
||||
};
|
||||
|
||||
field_altitude.on_change = changed_fn;
|
||||
field_speed.on_change = changed_fn;
|
||||
field_lat_degrees.on_change = changed_fn;
|
||||
field_lat_minutes.on_change = changed_fn;
|
||||
field_lat_seconds.on_change = changed_fn;
|
||||
@@ -79,11 +85,19 @@ GeoPos::GeoPos(
|
||||
field_lon_seconds.on_change = changed_fn;
|
||||
|
||||
text_alt_unit.set(altitude_unit_ ? "m" : "ft");
|
||||
if (speed_unit_ == KMPH) text_speed_unit.set("kmph");
|
||||
if (speed_unit_ == MPH) text_speed_unit.set("mph");
|
||||
if (speed_unit_ == HIDDEN) {
|
||||
text_speed_unit.hidden(true);
|
||||
label_spd_position.hidden(true);
|
||||
field_speed.hidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
void GeoPos::set_read_only(bool v) {
|
||||
// only setting altitude to read-only (allow manual panning via lat/lon fields)
|
||||
field_altitude.set_focusable(!v);
|
||||
field_speed.set_focusable(!v);
|
||||
}
|
||||
|
||||
// Stupid hack to avoid an event loop
|
||||
@@ -98,14 +112,18 @@ void GeoPos::focus() {
|
||||
field_lat_degrees.focus();
|
||||
}
|
||||
|
||||
void GeoPos::hide_altitude() {
|
||||
void GeoPos::hide_altandspeed() {
|
||||
// Color altitude grey to indicate it's not updated in manual panning mode
|
||||
field_altitude.set_style(&Styles::grey);
|
||||
field_speed.set_style(&Styles::grey);
|
||||
}
|
||||
|
||||
void GeoPos::set_altitude(int32_t altitude) {
|
||||
field_altitude.set_value(altitude);
|
||||
}
|
||||
void GeoPos::set_speed(int32_t speed) {
|
||||
field_speed.set_value(speed);
|
||||
}
|
||||
|
||||
void GeoPos::set_lat(float lat) {
|
||||
field_lat_degrees.set_value(lat);
|
||||
@@ -139,21 +157,38 @@ int32_t GeoPos::altitude() {
|
||||
return field_altitude.value();
|
||||
};
|
||||
|
||||
int32_t GeoPos::speed() {
|
||||
return field_speed.value();
|
||||
};
|
||||
|
||||
GeoMap::GeoMap(
|
||||
Rect parent_rect)
|
||||
: Widget{parent_rect}, markerListLen(0) {
|
||||
}
|
||||
|
||||
bool GeoMap::on_encoder(const EncoderEvent delta) {
|
||||
if ((delta > 0) && (map_zoom < 5)) {
|
||||
map_zoom++;
|
||||
// Valid map_zoom values are -2 to -MAX_MAP_ZOOM_OUT, and +1 to +MAX_MAP_ZOOM_IN (values of 0 and -1 are not permitted)
|
||||
if (delta > 0) {
|
||||
if (map_zoom < MAX_MAP_ZOOM_IN) {
|
||||
if (map_zoom == -2) {
|
||||
map_zoom = 1;
|
||||
} else {
|
||||
map_zoom++;
|
||||
|
||||
// Ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function
|
||||
if (geomap_rect_width % map_zoom != 0) {
|
||||
map_zoom--;
|
||||
// When zooming in, ensure that MOD(240,map_zoom)==0 for the map_zoom_line() function
|
||||
if ((map_zoom > 1) && (geomap_rect_width % map_zoom != 0)) {
|
||||
map_zoom--;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (delta < 0) {
|
||||
if (map_zoom > -MAX_MAP_ZOOM_OUT) {
|
||||
if (map_zoom == 1) {
|
||||
map_zoom = -2;
|
||||
} else {
|
||||
map_zoom--;
|
||||
}
|
||||
}
|
||||
} else if ((delta < 0) && (map_zoom > 1)) {
|
||||
map_zoom--;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -164,17 +199,31 @@ bool GeoMap::on_encoder(const EncoderEvent delta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void GeoMap::map_zoom_line(ui::Color* buffer) {
|
||||
if (map_zoom <= 1) {
|
||||
return;
|
||||
}
|
||||
void GeoMap::map_read_line(ui::Color* buffer, uint16_t pixels) {
|
||||
if (map_zoom == 1) {
|
||||
map_file.read(buffer, pixels << 1);
|
||||
} else if (map_zoom > 1) {
|
||||
map_file.read(buffer, (pixels / map_zoom) << 1);
|
||||
|
||||
// As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel;
|
||||
// For 240 width, than means no check is needed for map_zoom values up to 6
|
||||
for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) {
|
||||
for (int j = 0; j < map_zoom; j++) {
|
||||
buffer[(i * map_zoom) + j] = buffer[i];
|
||||
// Zoom in: Expand each pixel to "map_zoom" number of pixels.
|
||||
// Future TODO: Add dithering to smooth out the pixelation.
|
||||
// As long as MOD(width,map_zoom)==0 then we don't need to check buffer overflow case when stretching last pixel;
|
||||
// For 240 width, than means no check is needed for map_zoom values up to 6
|
||||
for (int i = (geomap_rect_width / map_zoom) - 1; i >= 0; i--) {
|
||||
for (int j = 0; j < map_zoom; j++) {
|
||||
buffer[(i * map_zoom) + j] = buffer[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ui::Color* zoom_out_buffer = new ui::Color[(pixels * (-map_zoom))];
|
||||
map_file.read(zoom_out_buffer, (pixels * (-map_zoom)) << 1);
|
||||
|
||||
// Zoom out: Collapse each group of "-map_zoom" pixels into one pixel.
|
||||
// Future TODO: Average each group of pixels (in both X & Y directions if possible).
|
||||
for (int i = 0; i < geomap_rect_width; i++) {
|
||||
buffer[i] = zoom_out_buffer[i * (-map_zoom)];
|
||||
}
|
||||
delete zoom_out_buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,9 +236,12 @@ void GeoMap::draw_markers(Painter& painter) {
|
||||
int x = (map_width * (item.lon + 180) / 360) - x_pos;
|
||||
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
|
||||
|
||||
if (map_zoom != 1) {
|
||||
if (map_zoom > 1) {
|
||||
x = ((x - (r.width() / 2)) * map_zoom) + (r.width() / 2);
|
||||
y = ((y - (r.height() / 2)) * map_zoom) + (r.height() / 2);
|
||||
} else if (map_zoom < 0) {
|
||||
x = ((x - (r.width() / 2)) / (-map_zoom)) + (r.width() / 2);
|
||||
y = ((y - (r.height() / 2)) / (-map_zoom)) + (r.height() / 2);
|
||||
}
|
||||
|
||||
if ((x >= 0) && (x < r.width()) &&
|
||||
@@ -208,31 +260,39 @@ void GeoMap::draw_markers(Painter& painter) {
|
||||
}
|
||||
|
||||
void GeoMap::paint(Painter& painter) {
|
||||
uint16_t line, j;
|
||||
uint32_t zoom_seek_x, zoom_seek_y;
|
||||
std::array<ui::Color, 240> map_line_buffer;
|
||||
const auto r = screen_rect();
|
||||
std::array<ui::Color, 240> map_line_buffer;
|
||||
|
||||
// Ony redraw map if it moved by at least 1 pixel
|
||||
// or the markers list was updated
|
||||
int x_diff = abs(x_pos - prev_x_pos);
|
||||
int y_diff = abs(y_pos - prev_y_pos);
|
||||
if (markerListUpdated || (x_diff >= map_zoom * 3) || (y_diff >= map_zoom * 3)) {
|
||||
if (map_zoom == 1) {
|
||||
zoom_seek_x = zoom_seek_y = 0;
|
||||
} else {
|
||||
zoom_seek_x = (r.width() - (r.width() / map_zoom)) / 2;
|
||||
zoom_seek_y = (r.height() - (r.height() / map_zoom)) / 2;
|
||||
|
||||
if (markerListUpdated || (x_diff >= 3) || (y_diff >= 3)) {
|
||||
int32_t zoom_seek_x = x_pos;
|
||||
int32_t zoom_seek_y = y_pos;
|
||||
|
||||
// Adjust starting corner position of map per zoom setting
|
||||
if (map_zoom > 1) {
|
||||
zoom_seek_x += (r.width() - (r.width() / map_zoom)) / 2;
|
||||
zoom_seek_y += (r.height() - (r.height() / map_zoom)) / 2;
|
||||
} else if (map_zoom < 0) {
|
||||
zoom_seek_x += (r.width() - (r.width() * (-map_zoom))) / 2;
|
||||
zoom_seek_y += (r.height() - (r.height() * (-map_zoom))) / 2;
|
||||
}
|
||||
|
||||
for (line = 0; line < (r.height() / map_zoom); line++) {
|
||||
map_file.seek(4 + ((x_pos + zoom_seek_x + (map_width * (y_pos + line + zoom_seek_y))) << 1));
|
||||
map_file.read(map_line_buffer.data(), (r.width() / map_zoom) << 1);
|
||||
map_zoom_line(map_line_buffer.data());
|
||||
for (j = 0; j < map_zoom; j++) {
|
||||
display.draw_pixels({0, r.top() + (line * map_zoom) + j, r.width(), 1}, map_line_buffer);
|
||||
// Read from map file and display to zoomed scale
|
||||
int duplicate_lines = (map_zoom < 0) ? 1 : map_zoom;
|
||||
for (uint16_t line = 0; line < (r.height() / duplicate_lines); line++) {
|
||||
uint16_t seek_line = zoom_seek_y + ((map_zoom >= 0) ? line : line * (-map_zoom));
|
||||
map_file.seek(4 + ((zoom_seek_x + (map_width * seek_line)) << 1));
|
||||
map_read_line(map_line_buffer.data(), r.width());
|
||||
|
||||
for (uint16_t j = 0; j < duplicate_lines; j++) {
|
||||
display.draw_pixels({0, r.top() + (line * duplicate_lines) + j, r.width(), 1}, map_line_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
prev_x_pos = x_pos;
|
||||
prev_y_pos = y_pos;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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}};
|
||||
|
||||
@@ -269,6 +269,19 @@ bool MenuView::on_key(const KeyEvent key) {
|
||||
}
|
||||
}
|
||||
|
||||
bool MenuView::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == '-') return set_highlighted(highlighted_item - 1);
|
||||
if (key == '+') return set_highlighted(highlighted_item + 1);
|
||||
if (key == 10) {
|
||||
if (menu_items[highlighted_item].on_select) {
|
||||
menu_items[highlighted_item].on_select(KeyEvent::Right);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MenuView::on_encoder(const EncoderEvent event) {
|
||||
set_highlighted(highlighted_item + event);
|
||||
return true;
|
||||
|
||||
@@ -97,6 +97,7 @@ class MenuView : public View {
|
||||
void on_blur() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
private:
|
||||
void update_items();
|
||||
|
||||
@@ -93,6 +93,13 @@ void FrequencyField::set_allow_digit_mode(bool allowed) {
|
||||
}
|
||||
}
|
||||
|
||||
void FrequencyField::getAccessibilityText(std::string& result) {
|
||||
result = to_string_dec_int(value_);
|
||||
}
|
||||
void FrequencyField::getWidgetName(std::string& result) {
|
||||
result = "FrequencyField";
|
||||
}
|
||||
|
||||
void FrequencyField::paint(Painter& painter) {
|
||||
const auto str_value = to_string_short_freq(value_);
|
||||
const auto paint_style = has_focus() ? style().invert() : style();
|
||||
@@ -165,12 +172,18 @@ bool FrequencyField::on_key(KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_keyboard(KeyboardEvent key) {
|
||||
if (key == '+' || key == ' ') return on_encoder(1);
|
||||
if (key == '-') return on_encoder(-1);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FrequencyField::on_encoder(const EncoderEvent delta) {
|
||||
if (digit_mode_)
|
||||
set_value(value_ + (delta * digit_step()));
|
||||
else
|
||||
set_value(value_ + (delta * step_));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -321,6 +334,25 @@ void FrequencyKeypadView::on_button(Button& button) {
|
||||
update_text();
|
||||
}
|
||||
|
||||
bool FrequencyKeypadView::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == 8) {
|
||||
digit_delete();
|
||||
update_text();
|
||||
return true;
|
||||
}
|
||||
if (key >= '0' && key <= '9') {
|
||||
digit_add(key);
|
||||
update_text();
|
||||
return true;
|
||||
}
|
||||
if (key == '.') {
|
||||
field_toggle();
|
||||
update_text();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FrequencyKeypadView::digit_add(const char c) {
|
||||
if (state == State::DigitMHz) {
|
||||
if (clear_field_if_digits_entered) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user