mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-27 09:59:03 +00:00
Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 |
+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.8.0
|
||||
v1.9.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.9.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"]
|
||||
|
||||
+49
-1
@@ -18,6 +18,8 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(firmware)
|
||||
|
||||
set(BASEBAND ${PROJECT_SOURCE_DIR}/baseband)
|
||||
@@ -34,6 +36,17 @@ set(LZ4 lz4)
|
||||
set(FIRMWARE_NAME portapack-h1_h2-mayhem)
|
||||
set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
|
||||
|
||||
# In our current build container cmake need a little help to get the version :)
|
||||
if(NOT DEFINED ${CMAKE_CXX_COMPILER_VERSION})
|
||||
execute_process(COMMAND bash "-c" "arm-none-eabi-g++ -v 2>&1 | grep 'gcc version' | awk '{print $3}'" OUTPUT_VARIABLE CMAKE_CXX_COMPILER_VERSION)
|
||||
string(STRIP ${CMAKE_CXX_COMPILER_VERSION} CMAKE_CXX_COMPILER_VERSION)
|
||||
endif()
|
||||
|
||||
set(GCC_VERSION_MISMATCH 0)
|
||||
if(NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_EQUAL ${EXPECTED_GCC_VERSION})
|
||||
set(GCC_VERSION_MISMATCH 1)
|
||||
endif()
|
||||
|
||||
add_subdirectory(application)
|
||||
add_subdirectory(baseband)
|
||||
add_subdirectory(test)
|
||||
@@ -48,10 +61,19 @@ add_custom_command(
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
firmware ALL
|
||||
firmware
|
||||
DEPENDS ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_DFU_FILENAME}
|
||||
)
|
||||
|
||||
if(${GCC_VERSION_MISMATCH})
|
||||
set(COMPILER_MISMATCH_MESSAGE "WARNING: Compiler version mismatch, please use the official compiler version ${EXPECTED_GCC_VERSION} when sharing builds! Current compiler version: ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
message(${COMPILER_MISMATCH_MESSAGE})
|
||||
add_custom_command(
|
||||
TARGET firmware POST_BUILD
|
||||
COMMAND echo ${COMPILER_MISMATCH_MESSAGE}
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
program
|
||||
COMMAND ${PROJECT_SOURCE_DIR}/tools/enter_mode.sh hackrf
|
||||
@@ -68,6 +90,21 @@ add_custom_target(
|
||||
DEPENDS program
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PPFW_FILENAME}
|
||||
|
||||
COMMAND rm -rf firmware_tar
|
||||
COMMAND mkdir -p firmware_tar/FIRMWARE
|
||||
# Using VERSION_NOHASH to avoid dev builds piling up in the FIRMWARE folder of the sd card of testers in #test-drive
|
||||
COMMAND cp ${FIRMWARE_FILENAME} firmware_tar/FIRMWARE/portapack-mayhem_${VERSION_NOHASH}.bin
|
||||
COMMAND mkdir -p firmware_tar/APPS
|
||||
COMMAND cp application/*.ppma firmware_tar/APPS
|
||||
COMMAND cd firmware_tar && tar -cvaf ../${PPFW_FILENAME} *
|
||||
DEPENDS firmware
|
||||
# Dont use VERBATIM here as it prevents usage of globbing (*)
|
||||
# There shouldnt be any funny business in the filenames above :)
|
||||
)
|
||||
|
||||
# TODO: Bad hack to fix location of LICENSE file for tar.
|
||||
add_custom_command(
|
||||
OUTPUT ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip
|
||||
@@ -87,7 +124,18 @@ add_custom_command(
|
||||
DEPENDS ${FIRMWARE_NAME}-${VERSION}.tar.bz2 ${FIRMWARE_NAME}-${VERSION}.zip
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
ppfw ALL
|
||||
DEPENDS ${PPFW_FILENAME}
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
oci
|
||||
DEPENDS ${PPFW_FILENAME}
|
||||
)
|
||||
|
||||
add_custom_target(
|
||||
release
|
||||
DEPENDS MD5SUMS SHA256SUMS
|
||||
)
|
||||
|
||||
|
||||
@@ -122,7 +122,6 @@ set(CSRC
|
||||
${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c
|
||||
${HACKRF_PATH}/firmware/common/usb_request.c
|
||||
${HACKRF_PATH}/firmware/common/usb_standard_request.c
|
||||
${CHIBIOS}/os/various/shell.c
|
||||
${CHIBIOS}/os/various/chprintf.c
|
||||
)
|
||||
|
||||
@@ -130,6 +129,7 @@ set(CSRC
|
||||
# setting.
|
||||
set(CPPSRC
|
||||
main.cpp
|
||||
shell.cpp
|
||||
${COMMON}/acars_packet.cpp
|
||||
${COMMON}/adsb.cpp
|
||||
${COMMON}/adsb_frame.cpp
|
||||
@@ -265,7 +265,7 @@ set(CPPSRC
|
||||
apps/ble_tx_app.cpp
|
||||
apps/capture_app.cpp
|
||||
apps/ert_app.cpp
|
||||
apps/gps_sim_app.cpp
|
||||
# apps/gps_sim_app.cpp
|
||||
# apps/lge_app.cpp
|
||||
apps/pocsag_app.cpp
|
||||
# apps/replay_app.cpp
|
||||
@@ -311,9 +311,9 @@ set(CPPSRC
|
||||
apps/ui_settings.cpp
|
||||
apps/ui_siggen.cpp
|
||||
apps/ui_sonde.cpp
|
||||
apps/ui_spectrum_painter_image.cpp
|
||||
apps/ui_spectrum_painter_text.cpp
|
||||
apps/ui_spectrum_painter.cpp
|
||||
# apps/ui_spectrum_painter_image.cpp
|
||||
# apps/ui_spectrum_painter_text.cpp
|
||||
# apps/ui_spectrum_painter.cpp
|
||||
apps/ui_ss_viewer.cpp
|
||||
apps/ui_sstvtx.cpp
|
||||
apps/ui_subghzd.cpp
|
||||
@@ -424,7 +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,7 +437,15 @@ BLERxView::BLERxView(NavigationView& nav)
|
||||
nav_.push<BleRecentEntryDetailView>(entry);
|
||||
};
|
||||
|
||||
usb_serial_thread = std::make_unique<UsbSerialThread>();
|
||||
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);
|
||||
@@ -456,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;
|
||||
@@ -468,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 = "";
|
||||
@@ -723,8 +731,10 @@ void BLERxView::on_data(BlePacketData* packet) {
|
||||
logger->log_raw_data(str_console + "\r\n");
|
||||
}
|
||||
|
||||
usb_serial_thread->serial_str = str_console + "\r\n";
|
||||
usb_serial_thread->str_ready = true;
|
||||
if (serial_logging) {
|
||||
usb_serial_thread->serial_str = str_console + "\r\n";
|
||||
usb_serial_thread->str_ready = true;
|
||||
}
|
||||
str_console = "";
|
||||
|
||||
if (!searchList.empty()) {
|
||||
|
||||
@@ -215,6 +215,7 @@ 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_{
|
||||
@@ -225,6 +226,8 @@ 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},
|
||||
}};
|
||||
|
||||
@@ -320,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()),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -238,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.
|
||||
@@ -258,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();
|
||||
|
||||
+528
-530
File diff suppressed because it is too large
Load Diff
+8
-3
@@ -27,6 +27,8 @@
|
||||
#ifndef __UI_BLESPAM_H__
|
||||
#define __UI_BLESPAM_H__
|
||||
|
||||
#define BLESPMUSECONSOLE 1
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -111,9 +113,10 @@ class BLESpamView : public View {
|
||||
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,
|
||||
@@ -134,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};
|
||||
|
||||
@@ -161,6 +164,8 @@ class BLESpamView : public View {
|
||||
this->on_tx_progress(message.done);
|
||||
}};
|
||||
|
||||
uint8_t packet[80];
|
||||
|
||||
// continuity
|
||||
|
||||
typedef enum {
|
||||
@@ -227,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[];
|
||||
|
||||
|
||||
+12
@@ -44,6 +44,16 @@ set(EXTCPPSRC
|
||||
#lcr
|
||||
external/jammer/main.cpp
|
||||
external/jammer/ui_jammer.cpp
|
||||
|
||||
#gpssim
|
||||
external/gpssim/main.cpp
|
||||
external/gpssim/gps_sim_app.cpp
|
||||
|
||||
#spainter
|
||||
external/spainter/main.cpp
|
||||
external/spainter/ui_spectrum_painter.cpp
|
||||
external/spainter/ui_spectrum_painter_text.cpp
|
||||
external/spainter/ui_spectrum_painter_image.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -58,4 +68,6 @@ set(EXTAPPLIST
|
||||
lge
|
||||
lcr
|
||||
jammer
|
||||
gpssim
|
||||
spainter
|
||||
)
|
||||
|
||||
+16
-2
@@ -28,6 +28,8 @@ MEMORY
|
||||
ram_external_app_lge(rwx) : org = 0xEEF10000, len = 32k
|
||||
ram_external_app_lcr(rwx) : org = 0xEEF20000, len = 32k
|
||||
ram_external_app_jammer(rwx) : org = 0xEEF30000, len = 32k
|
||||
ram_external_app_gpssim(rwx) : org = 0xEEF40000, len = 32k
|
||||
ram_external_app_spainter(rwx) : org = 0xEEF50000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -88,18 +90,30 @@ SECTIONS
|
||||
} > ram_external_app_lge
|
||||
|
||||
|
||||
.external_app_lcr : ALIGN(4) SUBALIGN(4)
|
||||
.external_app_lcr : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_lcr.application_information));
|
||||
*(*ui*external_app*lcr*);
|
||||
} > ram_external_app_lcr
|
||||
|
||||
|
||||
.external_app_jammer : ALIGN(4) SUBALIGN(4)
|
||||
.external_app_jammer : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_jammer.application_information));
|
||||
*(*ui*external_app*jammer*);
|
||||
} > ram_external_app_jammer
|
||||
|
||||
.external_app_gpssim : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_gpssim.application_information));
|
||||
*(*ui*external_app*gpssim*);
|
||||
} > ram_external_app_gpssim
|
||||
|
||||
|
||||
.external_app_spainter : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_spainter.application_information));
|
||||
*(*ui*external_app*spainter*);
|
||||
} > ram_external_app_spainter
|
||||
|
||||
}
|
||||
|
||||
Vendored
+4
-3
@@ -36,7 +36,7 @@
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::gpssim {
|
||||
|
||||
void GpsSimAppView::set_ready() {
|
||||
ready_signal = true;
|
||||
@@ -161,7 +161,8 @@ void GpsSimAppView::handle_replay_thread_done(const uint32_t return_code) {
|
||||
GpsSimAppView::GpsSimAppView(
|
||||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_gps);
|
||||
// baseband::run_image(portapack::spi_flash::image_tag_gps);
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({
|
||||
&button_open,
|
||||
@@ -211,4 +212,4 @@ void GpsSimAppView::set_parent_rect(const Rect new_parent_rect) {
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui::external_app::gpssim */
|
||||
Vendored
+4
-3
@@ -25,6 +25,7 @@
|
||||
#define __GPS_SIM_APP_HPP__
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -37,7 +38,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::gpssim {
|
||||
|
||||
class GpsSimAppView : public View {
|
||||
public:
|
||||
@@ -108,7 +109,7 @@ class GpsSimAppView : public View {
|
||||
Checkbox check_loop{
|
||||
{21 * 8, 2 * 16},
|
||||
4,
|
||||
"Loop",
|
||||
LanguageHelper::currentMessages[LANG_LOOP],
|
||||
true};
|
||||
ImageButton button_play{
|
||||
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
|
||||
@@ -142,6 +143,6 @@ class GpsSimAppView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui::external_app::gpssim */
|
||||
|
||||
#endif /*__GPS_SIM_APP_HPP__*/
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "gps_sim_app.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::gpssim {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<GpsSimAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::gpssim
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_gpssim.application_information"), used)) application_information_t _application_information_gpssim = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::gpssim::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "GPSSim",
|
||||
/*.bitmap_data = */ {
|
||||
0xC0,
|
||||
0x07,
|
||||
0xE0,
|
||||
0x0F,
|
||||
0x70,
|
||||
0x1F,
|
||||
0x78,
|
||||
0x3E,
|
||||
0x78,
|
||||
0x3C,
|
||||
0x78,
|
||||
0x38,
|
||||
0x78,
|
||||
0x30,
|
||||
0x78,
|
||||
0x38,
|
||||
0x78,
|
||||
0x3C,
|
||||
0x70,
|
||||
0x1E,
|
||||
0x70,
|
||||
0x1F,
|
||||
0xE0,
|
||||
0x0F,
|
||||
0xC0,
|
||||
0x07,
|
||||
0x80,
|
||||
0x03,
|
||||
0x20,
|
||||
0x09,
|
||||
0x50,
|
||||
0x14,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_gpssim */ {'P', 'G', 'P', 'S'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+4
-3
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -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,
|
||||
|
||||
+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__ */
|
||||
|
||||
@@ -171,12 +171,13 @@ static bool encoder_update(const uint8_t raw) {
|
||||
}
|
||||
|
||||
static bool encoder_read() {
|
||||
const auto delta = encoder.update(
|
||||
encoder_debounce[0].state() | (injected_encoder == 1),
|
||||
encoder_debounce[1].state() | (injected_encoder == 2));
|
||||
auto delta = encoder.update(encoder_debounce[0].state(), encoder_debounce[1].state());
|
||||
|
||||
if (injected_encoder > 0)
|
||||
if (injected_encoder > 0) {
|
||||
if (injected_encoder == 1) delta = -1;
|
||||
if (injected_encoder == 2) delta = 1;
|
||||
injected_encoder = 0;
|
||||
}
|
||||
|
||||
if (delta != 0) {
|
||||
encoder_position += delta;
|
||||
|
||||
@@ -157,7 +157,7 @@ static void event_loop() {
|
||||
[&event_dispatcher](const Message* const) {
|
||||
event_dispatcher.set_display_sleep(true);
|
||||
}};
|
||||
|
||||
portapack::setEventDispatcherToUSBSerial(&event_dispatcher);
|
||||
event_dispatcher.run();
|
||||
}
|
||||
|
||||
|
||||
@@ -542,4 +542,8 @@ void shutdown(const bool leave_screen_on) {
|
||||
shutdown_base();
|
||||
}
|
||||
|
||||
void setEventDispatcherToUSBSerial(EventDispatcher* evt) {
|
||||
usb_serial.setEventDispatcher(evt);
|
||||
}
|
||||
|
||||
} /* namespace portapack */
|
||||
|
||||
@@ -68,6 +68,8 @@ bool get_antenna_bias();
|
||||
bool init();
|
||||
void shutdown(const bool leave_screen_on = false);
|
||||
|
||||
void setEventDispatcherToUSBSerial(EventDispatcher* evt);
|
||||
|
||||
Backlight* backlight();
|
||||
|
||||
} /* namespace portapack */
|
||||
|
||||
@@ -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 4
|
||||
#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) {
|
||||
|
||||
@@ -61,10 +61,14 @@ class FrequencyField : public Widget {
|
||||
|
||||
bool on_key(KeyEvent event) override;
|
||||
bool on_encoder(EncoderEvent delta) override;
|
||||
bool on_keyboard(KeyboardEvent key) override;
|
||||
bool on_touch(TouchEvent event) override;
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
const size_t length_;
|
||||
range_t range_;
|
||||
@@ -202,6 +206,7 @@ class FrequencyKeypadView : public View {
|
||||
rf::Frequency value() const;
|
||||
void set_value(const rf::Frequency new_value);
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_keyboard(const KeyboardEvent key) override;
|
||||
|
||||
private:
|
||||
int16_t focused_button = 0;
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
#include "ui_settings.hpp"
|
||||
#include "ui_siggen.hpp"
|
||||
#include "ui_sonde.hpp"
|
||||
#include "ui_spectrum_painter.hpp"
|
||||
// #include "ui_spectrum_painter.hpp" //moved to ext app
|
||||
#include "ui_ss_viewer.hpp"
|
||||
#include "ui_sstvtx.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
@@ -93,7 +93,7 @@
|
||||
#include "ble_tx_app.hpp"
|
||||
#include "capture_app.hpp"
|
||||
#include "ert_app.hpp"
|
||||
#include "gps_sim_app.hpp"
|
||||
// #include "gps_sim_app.hpp" //moved to ext
|
||||
// #include "lge_app.hpp" //moved to ext
|
||||
#include "pocsag_app.hpp"
|
||||
#include "replay_app.hpp"
|
||||
@@ -173,6 +173,9 @@ SystemStatusView::SystemStatusView(
|
||||
pmem::load_persistent_settings_from_file();
|
||||
}
|
||||
|
||||
// configure CLKOUT per pmem setting
|
||||
portapack::clock_manager.enable_clock_output(pmem::clkout_enabled());
|
||||
|
||||
// force apply of selected sdcard speed override at UI startup
|
||||
pmem::set_config_sdcard_high_speed_io(pmem::config_sdcard_high_speed_io(), false);
|
||||
|
||||
@@ -438,7 +441,17 @@ InformationView::InformationView(
|
||||
&version,
|
||||
<ime});
|
||||
|
||||
#if GCC_VERSION_MISMATCH
|
||||
static constexpr Style style_gcc_warning{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 33, 33},
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
version.set_style(&style_gcc_warning);
|
||||
#else
|
||||
version.set_style(&style_infobar);
|
||||
#endif
|
||||
|
||||
ltime.set_style(&style_infobar);
|
||||
refresh();
|
||||
set_dirty();
|
||||
@@ -594,7 +607,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
||||
{"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push<BHTView>(); }},
|
||||
{"BLE Tx", ui::Color::green(), &bitmap_icon_btle, [&nav]() { nav.push<BLETxView>(); }},
|
||||
// {"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push<CoasterPagerView>(); }}, //moved to ext
|
||||
{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }},
|
||||
//{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }}, //moved to ext
|
||||
//{"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push<JammerView>(); }}, //moved to ext
|
||||
// { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push<KeyfobView>(); }},
|
||||
// {"LGE", ui::Color::yellow(), &bitmap_icon_lge, [&nav]() { nav.push<LGEView>(); }}, //moved to ext
|
||||
@@ -604,7 +617,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
||||
{"POCSAG TX", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push<POCSAGTXView>(); }},
|
||||
{"RDS", ui::Color::green(), &bitmap_icon_rds, [&nav]() { nav.push<RDSView>(); }},
|
||||
{"Soundbrd", ui::Color::green(), &bitmap_icon_soundboard, [&nav]() { nav.push<SoundBoardView>(); }},
|
||||
{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
|
||||
//{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
|
||||
{"SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav]() { nav.push<SSTVTXView>(); }},
|
||||
// {"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }}, //moved to ext
|
||||
{"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push<TouchTunesView>(); }},
|
||||
|
||||
@@ -145,6 +145,15 @@ OversampleRate RecordView::get_oversample_rate(uint32_t sample_rate) {
|
||||
// Setter for datetime and frequency filename
|
||||
void RecordView::set_filename_date_frequency(bool set) {
|
||||
filename_date_frequency = set;
|
||||
if (set)
|
||||
filename_as_is = false;
|
||||
}
|
||||
|
||||
// Setter for leaving the filename untouched
|
||||
void RecordView::set_filename_as_is(bool set) {
|
||||
filename_as_is = set;
|
||||
if (set)
|
||||
filename_date_frequency = false;
|
||||
}
|
||||
|
||||
bool RecordView::is_active() const {
|
||||
@@ -186,9 +195,11 @@ void RecordView::start() {
|
||||
base_path = filename_stem_pattern.string() + "_" + date_time + "_" +
|
||||
trim(to_string_freq(receiver_model.target_frequency())) + "Hz";
|
||||
base_path = folder / base_path;
|
||||
} else {
|
||||
} else if (filename_as_is) {
|
||||
base_path = filename_stem_pattern.string();
|
||||
base_path = folder / base_path;
|
||||
} else
|
||||
base_path = next_filename_matching_pattern(folder / filename_stem_pattern);
|
||||
}
|
||||
|
||||
if (base_path.empty()) {
|
||||
return;
|
||||
|
||||
@@ -73,6 +73,7 @@ class RecordView : public View {
|
||||
bool is_active() const;
|
||||
|
||||
void set_filename_date_frequency(bool set);
|
||||
void set_filename_as_is(bool set);
|
||||
|
||||
private:
|
||||
void toggle();
|
||||
@@ -91,6 +92,7 @@ class RecordView : public View {
|
||||
|
||||
// Time Stamp
|
||||
bool filename_date_frequency = false;
|
||||
bool filename_as_is = false;
|
||||
rtc::RTC datetime{};
|
||||
|
||||
const std::filesystem::path filename_stem_pattern;
|
||||
|
||||
@@ -32,7 +32,7 @@ void USBSerial::dispatch() {
|
||||
|
||||
if (shell_created == false) {
|
||||
shell_created = true;
|
||||
create_shell();
|
||||
create_shell(_eventDispatcher);
|
||||
}
|
||||
|
||||
bulk_out_receive();
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
class EventDispatcher;
|
||||
|
||||
namespace portapack {
|
||||
|
||||
class USBSerial {
|
||||
@@ -32,6 +34,7 @@ class USBSerial {
|
||||
void dispatch();
|
||||
void on_channel_opened();
|
||||
void on_channel_closed();
|
||||
void setEventDispatcher(EventDispatcher* ed) { _eventDispatcher = ed; }
|
||||
|
||||
private:
|
||||
void enable_xtal();
|
||||
@@ -43,6 +46,8 @@ class USBSerial {
|
||||
|
||||
bool connected{false};
|
||||
bool shell_created{false};
|
||||
|
||||
EventDispatcher* _eventDispatcher = NULL;
|
||||
};
|
||||
|
||||
} // namespace portapack
|
||||
@@ -27,19 +27,36 @@
|
||||
#include "png_writer.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_hal.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "jtag_target_gpio.hpp"
|
||||
#include "cpld_max5.hpp"
|
||||
#include "portapack_cpld_data.hpp"
|
||||
#include "crc.hpp"
|
||||
#include "hackrf_cpld_data.hpp"
|
||||
|
||||
#include "usb_serial_io.h"
|
||||
#include "ff.h"
|
||||
#include "chprintf.h"
|
||||
#include "chqueues.h"
|
||||
#include "untar.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <codecvt>
|
||||
#include <cstring>
|
||||
#include <locale>
|
||||
#include <libopencm3/lpc43xx/wwdt.h>
|
||||
|
||||
#define SHELL_WA_SIZE THD_WA_SIZE(1024 * 3)
|
||||
#define palOutputPad(port, pad) (LPC_GPIO->DIR[(port)] |= 1 << (pad))
|
||||
|
||||
static EventDispatcher* _eventDispatcherInstance = NULL;
|
||||
static EventDispatcher* getEventDispatcherInstance() {
|
||||
return _eventDispatcherInstance;
|
||||
}
|
||||
|
||||
// queue handler from ch
|
||||
static msg_t qwait(GenericQueue* qp, systime_t time) {
|
||||
if (TIME_IMMEDIATE == time)
|
||||
@@ -146,6 +163,15 @@ std::filesystem::path path_from_string8(char* path) {
|
||||
return conv.from_bytes(path);
|
||||
}
|
||||
|
||||
bool strEndsWith(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;
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "Usage: flash /FIRMWARE/portapack-h1_h2-mayhem.bin\r\n");
|
||||
@@ -153,15 +179,37 @@ static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
size_t filename_length = strlen(argv[0]);
|
||||
|
||||
if (!std::filesystem::file_exists(path)) {
|
||||
chprintf(chp, "file not found.\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (filename_length + 1) * 2);
|
||||
|
||||
// check file extensions
|
||||
if (strEndsWith(path.native(), u".ppfw.tar")) {
|
||||
// extract tar
|
||||
chprintf(chp, "Extracting TAR file.\r\n");
|
||||
auto res = UnTar::untar(
|
||||
path.native(), [chp](const std::string fileName) {
|
||||
chprintf(chp, fileName.c_str());
|
||||
chprintf(chp, "\r\n");
|
||||
});
|
||||
if (res.empty()) {
|
||||
chprintf(chp, "error bad TAR file.\r\n");
|
||||
return;
|
||||
}
|
||||
path = res; // it will contain the last bin file in tar
|
||||
} else if (strEndsWith(path.native(), u".bin")) {
|
||||
// nothing to do for this case yet.
|
||||
} else {
|
||||
chprintf(chp, "error only .bin or .ppfw.tar files canbe flashed.\r\n");
|
||||
return;
|
||||
}
|
||||
chprintf(chp, "Flashing: ");
|
||||
chprintf(chp, path.string().c_str());
|
||||
chprintf(chp, "\r\n");
|
||||
chThdSleepMilliseconds(50);
|
||||
std::memcpy(&shared_memory.bb_data.data[0], path.native().c_str(), (path.native().length() + 1) * 2);
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(50);
|
||||
m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false);
|
||||
@@ -296,6 +344,66 @@ static void cmd_button(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
static void cmd_touch(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
chprintf(chp, "usage: touch x y\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
int x = (int)strtol(argv[0], NULL, 10);
|
||||
int y = (int)strtol(argv[1], NULL, 10);
|
||||
if (x < 0 || x > ui::screen_width || y < 0 || y > ui::screen_height) {
|
||||
chprintf(chp, "usage: touch x y\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto evtd = getEventDispatcherInstance();
|
||||
if (evtd == NULL) {
|
||||
chprintf(chp, "error\r\n");
|
||||
}
|
||||
evtd->emulateTouch({{x, y}, ui::TouchEvent::Type::Start});
|
||||
evtd->emulateTouch({{x, y}, ui::TouchEvent::Type::End});
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
// send ascii keys in 2 char hex representation. Can send multiple keys at once like: keyboard 414243 (this will be ABC)
|
||||
static void cmd_keyboard(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: keyboard XX\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto evtd = getEventDispatcherInstance();
|
||||
if (evtd == NULL) {
|
||||
chprintf(chp, "error\r\n");
|
||||
}
|
||||
|
||||
size_t data_string_len = strlen(argv[0]);
|
||||
if (data_string_len % 2 != 0) {
|
||||
chprintf(chp, "usage: keyboard XXXX\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < data_string_len; i++) {
|
||||
char c = argv[0][i];
|
||||
if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
|
||||
chprintf(chp, "usage: keyboard XX\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[3] = {0, 0, 0};
|
||||
|
||||
for (size_t i = 0; i < data_string_len / 2; i++) {
|
||||
buffer[0] = argv[0][i * 2];
|
||||
buffer[1] = argv[0][i * 2 + 1];
|
||||
uint8_t chr = (uint8_t)strtol(buffer, NULL, 16);
|
||||
evtd->emulateKeyboard(chr);
|
||||
}
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
static void cmd_sd_list_dir(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: ls /\r\n");
|
||||
@@ -418,21 +526,18 @@ static void cmd_sd_read(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
|
||||
int size = (int)strtol(argv[0], NULL, 10);
|
||||
|
||||
uint8_t buffer[16];
|
||||
uint8_t buffer[62];
|
||||
|
||||
do {
|
||||
File::Size bytes_to_read = size > 16 ? 16 : size;
|
||||
File::Size bytes_to_read = size > 62 ? 62 : size;
|
||||
auto bytes_read = shell_file->read(buffer, bytes_to_read);
|
||||
if (bytes_read.is_error()) {
|
||||
chprintf(chp, "error %d\r\n", bytes_read.error());
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < bytes_read.value(); i++)
|
||||
chprintf(chp, "%02X", buffer[i]);
|
||||
|
||||
chprintf(chp, "\r\n");
|
||||
|
||||
std::string res = to_string_hex_array(buffer, bytes_read.value());
|
||||
res += "\r\n";
|
||||
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)res.c_str(), res.size());
|
||||
if (bytes_to_read != bytes_read.value())
|
||||
return;
|
||||
|
||||
@@ -479,6 +584,395 @@ static void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
static void cpld_info(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage =
|
||||
"usage: cpld_info <device>\r\n"
|
||||
" supported modes:\r\n"
|
||||
" cpld_info hackrf\r\n"
|
||||
" cpld_info portapack\r\n";
|
||||
if (argc != 1) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp(argv[0], "hackrf", 5) == 0) {
|
||||
jtag::GPIOTarget jtag_target_hackrf_cpld{
|
||||
hackrf::one::gpio_cpld_tck,
|
||||
hackrf::one::gpio_cpld_tms,
|
||||
hackrf::one::gpio_cpld_tdi,
|
||||
hackrf::one::gpio_cpld_tdo,
|
||||
};
|
||||
|
||||
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
|
||||
{
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
|
||||
hackrf_cpld.prepare_read_eeprom();
|
||||
|
||||
for (const auto& block : hackrf::one::cpld::verify_blocks) {
|
||||
auto from_device = hackrf_cpld.read_block_eeprom(block.id);
|
||||
|
||||
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
|
||||
auto bit = *i;
|
||||
crc.process_bit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
hackrf_cpld.finalize_read_eeprom();
|
||||
chprintf(chp, "CPLD eeprom firmware checksum: 0x%08X\r\n", crc.checksum());
|
||||
}
|
||||
|
||||
{
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
|
||||
hackrf_cpld.prepare_read_sram();
|
||||
|
||||
for (const auto& block : hackrf::one::cpld::verify_blocks) {
|
||||
auto from_device = hackrf_cpld.read_block_sram(block);
|
||||
|
||||
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
|
||||
auto bit = *i;
|
||||
crc.process_bit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
hackrf_cpld.finalize_read_sram(hackrf::one::cpld::verify_blocks[0].id);
|
||||
chprintf(chp, "CPLD sram firmware checksum: 0x%08X\r\n", crc.checksum());
|
||||
}
|
||||
|
||||
} else if (strncmp(argv[0], "portapack", 5) == 0) {
|
||||
jtag::GPIOTarget target{
|
||||
portapack::gpio_cpld_tck,
|
||||
portapack::gpio_cpld_tms,
|
||||
portapack::gpio_cpld_tdi,
|
||||
portapack::gpio_cpld_tdo};
|
||||
jtag::JTAG jtag{target};
|
||||
portapack::cpld::CPLD cpld{jtag};
|
||||
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
uint32_t idcode = cpld.get_idcode();
|
||||
|
||||
chprintf(chp, "CPLD IDCODE: 0x%08X\r\n", idcode);
|
||||
|
||||
if (idcode == 0x20A50DD) {
|
||||
chprintf(chp, "CPLD Model: Altera MAX V 5M40Z\r\n");
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
cpld.sample();
|
||||
cpld.bypass();
|
||||
cpld.enable();
|
||||
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
cpld.prepare_read(0x0000);
|
||||
|
||||
for (size_t i = 0; i < 3328; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
crc.process_byte((data >> 0) & 0xff);
|
||||
crc.process_byte((data >> 8) & 0xff);
|
||||
crc.process_byte((data >> 16) & 0xff);
|
||||
crc.process_byte((data >> 24) & 0xff);
|
||||
}
|
||||
|
||||
cpld.prepare_read(0x0001);
|
||||
|
||||
for (size_t i = 0; i < 512; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
crc.process_byte((data >> 0) & 0xff);
|
||||
crc.process_byte((data >> 8) & 0xff);
|
||||
crc.process_byte((data >> 16) & 0xff);
|
||||
crc.process_byte((data >> 24) & 0xff);
|
||||
}
|
||||
|
||||
chprintf(chp, "CPLD firmware checksum: 0x%08X\r\n", crc.checksum());
|
||||
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
|
||||
WWDT_TC = 100000 & 0xFFFFFF;
|
||||
WWDT_FEED_SEQUENCE;
|
||||
|
||||
} else if (idcode == 0x00025610) {
|
||||
chprintf(chp, "CPLD Model: AGM AG256SL100\r\n");
|
||||
|
||||
if (cpld.AGM_enter_maintenance_mode() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
cpld.AGM_enter_read_mode();
|
||||
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
for (size_t i = 0; i < 2048; i++) {
|
||||
uint32_t data = cpld.AGM_read(i);
|
||||
crc.process_byte((data >> 0) & 0xff);
|
||||
crc.process_byte((data >> 8) & 0xff);
|
||||
crc.process_byte((data >> 16) & 0xff);
|
||||
crc.process_byte((data >> 24) & 0xff);
|
||||
}
|
||||
|
||||
cpld.AGM_exit_maintenance_mode();
|
||||
|
||||
chprintf(chp, "CPLD firmware checksum: 0x%08X\r\n", crc.checksum());
|
||||
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
|
||||
WWDT_TC = 100000 & 0xFFFFFF;
|
||||
WWDT_FEED_SEQUENCE;
|
||||
|
||||
} else {
|
||||
chprintf(chp, "CPLD Model: unknown\r\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
chprintf(chp, usage);
|
||||
}
|
||||
}
|
||||
|
||||
// walks throught the given widget's childs in recurse to get all support text and pass it to a callback function
|
||||
static void widget_collect_accessibility(BaseSequentialStream* chp, ui::Widget* w, void (*callback)(BaseSequentialStream*, const std::string&, const std::string&), ui::Widget* focusedWidget) {
|
||||
for (auto child : w->children()) {
|
||||
if (!child->hidden()) {
|
||||
std::string res = "";
|
||||
child->getAccessibilityText(res);
|
||||
std::string strtype = "";
|
||||
child->getWidgetName(strtype);
|
||||
if (child == focusedWidget) strtype += "*";
|
||||
if (callback != NULL && !res.empty()) callback(chp, res, strtype);
|
||||
widget_collect_accessibility(chp, child, callback, focusedWidget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// callback when it found any response from a widget
|
||||
static void accessibility_callback(BaseSequentialStream* chp, const std::string& strResult, const std::string& wgType) {
|
||||
if (!wgType.empty()) {
|
||||
chprintf(chp, "[");
|
||||
chprintf(chp, wgType.c_str());
|
||||
chprintf(chp, "] ");
|
||||
}
|
||||
chprintf(chp, "%s\r\n", strResult.c_str());
|
||||
}
|
||||
|
||||
// gets all widget's accessibility helper text
|
||||
static void cmd_accessibility_readall(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
auto evtd = getEventDispatcherInstance();
|
||||
if (evtd == NULL) {
|
||||
chprintf(chp, "error Can't get Event Dispatcherr\n");
|
||||
return;
|
||||
}
|
||||
auto wg = evtd->getTopWidget();
|
||||
if (wg == NULL) {
|
||||
chprintf(chp, "error Can't get top Widget\r\n");
|
||||
return;
|
||||
}
|
||||
auto focused = evtd->getFocusedWidget();
|
||||
widget_collect_accessibility(chp, wg, accessibility_callback, focused);
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
// gets focused widget's accessibility helper text
|
||||
static void cmd_accessibility_readcurr(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
auto evtd = getEventDispatcherInstance();
|
||||
if (evtd == NULL) {
|
||||
chprintf(chp, "error Can't get Event Dispatcher\r\n");
|
||||
return;
|
||||
}
|
||||
auto wg = evtd->getFocusedWidget();
|
||||
if (wg == NULL) {
|
||||
chprintf(chp, "error Can't get focused Widget\r\n");
|
||||
return;
|
||||
}
|
||||
std::string res = "";
|
||||
wg->getAccessibilityText(res);
|
||||
if (res.empty()) {
|
||||
// try with parent
|
||||
wg = wg->parent();
|
||||
if (wg == NULL) {
|
||||
chprintf(chp, "error Widget not providing accessibility info\r\n");
|
||||
return;
|
||||
}
|
||||
wg->getAccessibilityText(res);
|
||||
if (res.empty()) {
|
||||
chprintf(chp, "error Widget not providing accessibility info\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
std::string strtype = "";
|
||||
wg->getWidgetName(strtype);
|
||||
accessibility_callback(chp, res, strtype);
|
||||
chprintf(chp, "\r\nok\r\n");
|
||||
}
|
||||
|
||||
static void cmd_cpld_read(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage =
|
||||
"usage: cpld_read <device> <target>\r\n"
|
||||
" device can be: hackrf, portapack\r\n"
|
||||
" target can be: sram (hackrf only), eeprom\r\n";
|
||||
|
||||
if (argc != 2) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp(argv[0], "hackrf", 5) == 0) {
|
||||
if (strncmp(argv[1], "eeprom", 5) == 0) {
|
||||
jtag::GPIOTarget jtag_target_hackrf_cpld{
|
||||
hackrf::one::gpio_cpld_tck,
|
||||
hackrf::one::gpio_cpld_tms,
|
||||
hackrf::one::gpio_cpld_tdi,
|
||||
hackrf::one::gpio_cpld_tdo,
|
||||
};
|
||||
|
||||
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
|
||||
|
||||
hackrf_cpld.prepare_read_eeprom();
|
||||
|
||||
for (const auto& block : hackrf::one::cpld::verify_blocks) {
|
||||
auto from_device = hackrf_cpld.read_block_eeprom(block.id);
|
||||
|
||||
chprintf(chp, "bank %04X: ", block.id);
|
||||
uint32_t n = 6;
|
||||
uint8_t byte = 0;
|
||||
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
|
||||
auto bit = *i;
|
||||
byte |= bit << (7 - (n % 8));
|
||||
if (n % 8 == 7) {
|
||||
chprintf(chp, "%02X ", byte);
|
||||
byte = 0;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
chprintf(chp, "\r\n");
|
||||
}
|
||||
|
||||
hackrf_cpld.finalize_read_eeprom();
|
||||
}
|
||||
|
||||
else if (strncmp(argv[1], "sram", 5) == 0) {
|
||||
jtag::GPIOTarget jtag_target_hackrf_cpld{
|
||||
hackrf::one::gpio_cpld_tck,
|
||||
hackrf::one::gpio_cpld_tms,
|
||||
hackrf::one::gpio_cpld_tdi,
|
||||
hackrf::one::gpio_cpld_tdo,
|
||||
};
|
||||
|
||||
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
|
||||
|
||||
hackrf_cpld.prepare_read_sram();
|
||||
|
||||
for (const auto& block : hackrf::one::cpld::verify_blocks) {
|
||||
auto from_device = hackrf_cpld.read_block_sram(block);
|
||||
|
||||
chprintf(chp, "bank %04X: ", block.id);
|
||||
uint32_t n = 6;
|
||||
uint8_t byte = 0;
|
||||
for (std::array<bool, 274UL>::reverse_iterator i = from_device.rbegin(); i != from_device.rend(); ++i) {
|
||||
auto bit = *i;
|
||||
byte |= bit << (7 - (n % 8));
|
||||
if (n % 8 == 7) {
|
||||
chprintf(chp, "%02X ", byte);
|
||||
byte = 0;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
chprintf(chp, "\r\n");
|
||||
}
|
||||
|
||||
hackrf_cpld.finalize_read_sram(hackrf::one::cpld::verify_blocks[0].id);
|
||||
}
|
||||
} else if (strncmp(argv[0], "portapack", 5) == 0) {
|
||||
jtag::GPIOTarget target{
|
||||
portapack::gpio_cpld_tck,
|
||||
portapack::gpio_cpld_tms,
|
||||
portapack::gpio_cpld_tdi,
|
||||
portapack::gpio_cpld_tdo};
|
||||
jtag::JTAG jtag{target};
|
||||
portapack::cpld::CPLD cpld{jtag};
|
||||
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
uint32_t idcode = cpld.get_idcode();
|
||||
|
||||
chprintf(chp, "CPLD IDCODE: 0x%08X\r\n", idcode);
|
||||
|
||||
if (idcode == 0x20A50DD) {
|
||||
chprintf(chp, "CPLD Model: Altera MAX V 5M40Z\r\n");
|
||||
cpld.reset();
|
||||
cpld.run_test_idle();
|
||||
cpld.sample();
|
||||
cpld.bypass();
|
||||
cpld.enable();
|
||||
|
||||
cpld.prepare_read(0x0000);
|
||||
|
||||
for (size_t i = 0; i < 3328; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
chprintf(chp, "%d: 0x%04X\r\n", i, data);
|
||||
}
|
||||
|
||||
cpld.prepare_read(0x0001);
|
||||
|
||||
for (size_t i = 0; i < 512; i++) {
|
||||
uint16_t data = cpld.read();
|
||||
chprintf(chp, "%d: 0x%04X\r\n", i, data);
|
||||
}
|
||||
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
|
||||
WWDT_TC = 100000 & 0xFFFFFF;
|
||||
WWDT_FEED_SEQUENCE;
|
||||
|
||||
} else if (idcode == 0x00025610) {
|
||||
chprintf(chp, "CPLD Model: AGM AG256SL100\r\n");
|
||||
|
||||
if (cpld.AGM_enter_maintenance_mode() == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
cpld.AGM_enter_read_mode();
|
||||
|
||||
for (size_t i = 0; i < 2048; i++) {
|
||||
uint32_t data = cpld.AGM_read(i);
|
||||
if (i % 4 == 0)
|
||||
chprintf(chp, "%5d: ", i * 4);
|
||||
|
||||
chprintf(chp, "0x%08X", data);
|
||||
|
||||
if (i % 4 == 3)
|
||||
chprintf(chp, "\r\n");
|
||||
else
|
||||
chprintf(chp, " ");
|
||||
}
|
||||
|
||||
cpld.AGM_exit_maintenance_mode();
|
||||
|
||||
m4_request_shutdown();
|
||||
chThdSleepMilliseconds(1000);
|
||||
|
||||
WWDT_MOD = WWDT_MOD_WDEN | WWDT_MOD_WDRESET;
|
||||
WWDT_TC = 100000 & 0xFFFFFF;
|
||||
WWDT_FEED_SEQUENCE;
|
||||
|
||||
} else {
|
||||
chprintf(chp, "CPLD Model: unknown\r\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
chprintf(chp, usage);
|
||||
}
|
||||
}
|
||||
|
||||
static const ShellCommand commands[] = {
|
||||
{"reboot", cmd_reboot},
|
||||
{"dfu", cmd_dfu},
|
||||
@@ -491,6 +985,8 @@ static const ShellCommand commands[] = {
|
||||
{"write_memory", cmd_write_memory},
|
||||
{"read_memory", cmd_read_memory},
|
||||
{"button", cmd_button},
|
||||
{"touch", cmd_touch},
|
||||
{"keyboard", cmd_keyboard},
|
||||
{"ls", cmd_sd_list_dir},
|
||||
{"rm", cmd_sd_delete},
|
||||
{"open", cmd_sd_open},
|
||||
@@ -499,12 +995,17 @@ static const ShellCommand commands[] = {
|
||||
{"read", cmd_sd_read},
|
||||
{"write", cmd_sd_write},
|
||||
{"filesize", cmd_sd_filesize},
|
||||
{"cpld_info", cpld_info},
|
||||
{"cpld_read", cmd_cpld_read},
|
||||
{"accessibility_readall", cmd_accessibility_readall},
|
||||
{"accessibility_readcurr", cmd_accessibility_readcurr},
|
||||
{NULL, NULL}};
|
||||
|
||||
static const ShellConfig shell_cfg1 = {
|
||||
(BaseSequentialStream*)&SUSBD1,
|
||||
commands};
|
||||
|
||||
void create_shell() {
|
||||
void create_shell(EventDispatcher* evtd) {
|
||||
_eventDispatcherInstance = evtd;
|
||||
shellCreate(&shell_cfg1, SHELL_WA_SIZE, NORMALPRIO);
|
||||
}
|
||||
|
||||
@@ -26,4 +26,6 @@
|
||||
|
||||
#include "shell.h"
|
||||
|
||||
void create_shell(void);
|
||||
class EventDispatcher;
|
||||
|
||||
void create_shell(EventDispatcher* evtd);
|
||||
|
||||
@@ -476,13 +476,6 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PREP replay)
|
||||
|
||||
### GPS Simulator
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_gps_sim.cpp
|
||||
)
|
||||
DeclareTargets(PGPS gps_sim)
|
||||
|
||||
### Signal generator
|
||||
|
||||
set(MODE_CPPSRC
|
||||
@@ -490,12 +483,6 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PSIG siggen)
|
||||
|
||||
### Spectrum painter
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_spectrum_painter.cpp
|
||||
)
|
||||
DeclareTargets(PSPT spectrum_painter)
|
||||
|
||||
### SSTV TX
|
||||
|
||||
@@ -637,6 +624,20 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PAMT am_tv)
|
||||
|
||||
### GPS Simulator
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_gps_sim.cpp
|
||||
)
|
||||
DeclareTargets(PGPS gps_sim)
|
||||
|
||||
### Spectrum painter
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_spectrum_painter.cpp
|
||||
)
|
||||
DeclareTargets(PSPT spectrum_painter)
|
||||
|
||||
### Test
|
||||
|
||||
set(MODE_CPPSRC
|
||||
|
||||
@@ -147,9 +147,12 @@ void CPLD::sector_select(const uint16_t id) {
|
||||
}
|
||||
|
||||
bool CPLD::idcode_ok() {
|
||||
return (get_idcode() == idcode);
|
||||
}
|
||||
|
||||
uint32_t CPLD::get_idcode() {
|
||||
shift_ir(instruction_t::IDCODE);
|
||||
const auto idcode_read = jtag.shift_dr(idcode_length, 0);
|
||||
return (idcode_read == idcode);
|
||||
return jtag.shift_dr(idcode_length, 0);
|
||||
}
|
||||
|
||||
std::array<uint16_t, 5> CPLD::read_silicon_id() {
|
||||
@@ -208,6 +211,16 @@ void CPLD::program_block(
|
||||
}
|
||||
}
|
||||
|
||||
void CPLD::prepare_read(uint16_t block) {
|
||||
sector_select(block);
|
||||
shift_ir(instruction_t::ISC_READ);
|
||||
jtag.runtest_tck(93); // 5us
|
||||
}
|
||||
|
||||
uint32_t CPLD::read() {
|
||||
return jtag.shift_dr(16, 0xffff) & 0xfbff;
|
||||
}
|
||||
|
||||
bool CPLD::verify_block(
|
||||
const uint16_t id,
|
||||
const uint16_t* const data,
|
||||
@@ -265,5 +278,59 @@ bool CPLD::is_blank() {
|
||||
return block_0_blank && block_1_blank;
|
||||
}
|
||||
|
||||
bool CPLD::AGM_enter_maintenance_mode() {
|
||||
shift_ir(instruction_t::AGM_STAGE_1);
|
||||
jtag.runtest_tck(100);
|
||||
shift_ir(instruction_t::AGM_STAGE_2);
|
||||
jtag.runtest_tck(100);
|
||||
shift_ir(instruction_t::AGM_STAGE_1);
|
||||
jtag.runtest_tck(100);
|
||||
|
||||
shift_ir(instruction_t::AGM_SET_REGISTER);
|
||||
jtag.runtest_tck(100);
|
||||
jtag.shift_dr(8, 0x0);
|
||||
jtag.runtest_tck(100);
|
||||
|
||||
shift_ir(instruction_t::AGM_PROGRAM);
|
||||
jtag.runtest_tck(100);
|
||||
jtag.shift_dr(32, 0x203f0044uL, 0x80000000);
|
||||
|
||||
shift_ir(instruction_t::IDCODE);
|
||||
jtag.runtest_tck(100);
|
||||
auto idcode = jtag.shift_dr(idcode_length, 0);
|
||||
|
||||
return idcode == 0x00025610;
|
||||
}
|
||||
|
||||
void CPLD::AGM_exit_maintenance_mode() {
|
||||
shift_ir(instruction_t::AGM_RESET);
|
||||
jtag.runtest_tck(100);
|
||||
}
|
||||
|
||||
void CPLD::AGM_enter_read_mode() {
|
||||
shift_ir(instruction_t::AGM_SET_REGISTER);
|
||||
jtag.runtest_tck(100);
|
||||
jtag.shift_dr(8, 0xf0);
|
||||
jtag.runtest_tck(100);
|
||||
|
||||
shift_ir(instruction_t::AGM_READ);
|
||||
jtag.runtest_tck(100);
|
||||
}
|
||||
|
||||
uint32_t CPLD::AGM_encode_address(uint32_t address, uint32_t trailer) {
|
||||
uint32_t p = trailer;
|
||||
for (size_t i = 0; i < 18; i++) {
|
||||
auto address_bit = (address >> i) & 0x01;
|
||||
p |= address_bit << (31 - i);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
uint32_t CPLD::AGM_read(uint32_t address) {
|
||||
auto encoded_address = AGM_encode_address(address * 4, 0xC0);
|
||||
return jtag.shift_dr(32, encoded_address, 0x0);
|
||||
}
|
||||
|
||||
} /* namespace max5 */
|
||||
} /* namespace cpld */
|
||||
|
||||
@@ -60,6 +60,7 @@ class CPLD {
|
||||
}
|
||||
|
||||
bool idcode_ok();
|
||||
uint32_t get_idcode();
|
||||
|
||||
void enable();
|
||||
|
||||
@@ -90,6 +91,15 @@ class CPLD {
|
||||
|
||||
std::pair<bool, uint8_t> boundary_scan();
|
||||
|
||||
void prepare_read(uint16_t block);
|
||||
uint32_t read();
|
||||
|
||||
bool AGM_enter_maintenance_mode();
|
||||
void AGM_exit_maintenance_mode();
|
||||
void AGM_enter_read_mode();
|
||||
uint32_t AGM_encode_address(uint32_t address, uint32_t trailer);
|
||||
uint32_t AGM_read(uint32_t address);
|
||||
|
||||
private:
|
||||
using idcode_t = uint32_t;
|
||||
static constexpr size_t idcode_length = 32;
|
||||
@@ -115,6 +125,13 @@ class CPLD {
|
||||
ISC_ADDRESS_SHIFT = 0b1000000011, // 0x203
|
||||
ISC_READ = 0b1000000101, // 0x205
|
||||
ISC_NOOP = 0b1000010000, // 0x210
|
||||
AGM_RESET = 0x3f7,
|
||||
AGM_STAGE_1 = 0x3f8,
|
||||
AGM_STAGE_2 = 0x3f9,
|
||||
AGM_PROGRAM = 0x3fa,
|
||||
AGM_SET_REGISTER = 0x3fc,
|
||||
AGM_READ = 0x3fd,
|
||||
AGM_ERASE = 0x3fe,
|
||||
};
|
||||
|
||||
void shift_ir(const instruction_t instruction) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ch.h"
|
||||
#include "cpld_xilinx.hpp"
|
||||
|
||||
namespace cpld {
|
||||
@@ -47,6 +48,25 @@ void XC2C64A::write_sram(const verify_blocks_t& blocks) {
|
||||
}
|
||||
|
||||
bool XC2C64A::verify_sram(const verify_blocks_t& blocks) {
|
||||
prepare_read_sram();
|
||||
|
||||
const jtag::tap::bits_t empty_row{block_length};
|
||||
|
||||
auto error = false;
|
||||
for (const auto& block : blocks) {
|
||||
tap.shift({&block.id, block_id_length}, true);
|
||||
tap.state(state_t::run_test_idle);
|
||||
|
||||
tap.state(state_t::shift_dr);
|
||||
error |= tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false, nullptr);
|
||||
}
|
||||
|
||||
finalize_read_sram(blocks[0].id);
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
void XC2C64A::prepare_read_sram() {
|
||||
tap.set_repeat(0);
|
||||
tap.set_end_ir(state_t::run_test_idle);
|
||||
tap.set_end_dr(state_t::run_test_idle);
|
||||
@@ -61,17 +81,25 @@ bool XC2C64A::verify_sram(const verify_blocks_t& blocks) {
|
||||
|
||||
tap.state(state_t::shift_dr);
|
||||
tap.shift(empty_row, false);
|
||||
}
|
||||
|
||||
auto error = false;
|
||||
for (const auto& block : blocks) {
|
||||
tap.shift({&block.id, block_id_length}, true);
|
||||
tap.state(state_t::run_test_idle);
|
||||
std::array<bool, 274> XC2C64A::read_block_sram(verify_block_t block) {
|
||||
tap.shift({&block.id, block_id_length}, true);
|
||||
tap.state(state_t::run_test_idle);
|
||||
|
||||
tap.state(state_t::shift_dr);
|
||||
error |= tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false);
|
||||
}
|
||||
// Redundant operation to finish the row.
|
||||
tap.shift({&blocks[0].id, block_id_length}, true);
|
||||
tap.state(state_t::shift_dr);
|
||||
const jtag::tap::bits_t empty_row{block_length};
|
||||
std::vector<bool> from_device;
|
||||
|
||||
tap.shift(empty_row, {block.data.data(), block_length}, {block.mask.data(), block_length}, false, &from_device);
|
||||
|
||||
std::array<bool, block_length> ret;
|
||||
std::copy_n(std::make_move_iterator(from_device.begin()), block_length, ret.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void XC2C64A::finalize_read_sram(block_id_t id) {
|
||||
tap.shift({&id, block_id_length}, true);
|
||||
tap.state(state_t::run_test_idle);
|
||||
tap.set_end_dr(state_t::run_test_idle);
|
||||
|
||||
@@ -79,8 +107,6 @@ bool XC2C64A::verify_sram(const verify_blocks_t& blocks) {
|
||||
bypass();
|
||||
|
||||
tap.state(state_t::test_logic_reset);
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool XC2C64A::verify_eeprom(const verify_blocks_t& blocks) {
|
||||
@@ -133,6 +159,44 @@ void XC2C64A::init_from_eeprom() {
|
||||
tap.state(state_t::test_logic_reset);
|
||||
}
|
||||
|
||||
void XC2C64A::prepare_read_eeprom() {
|
||||
tap.set_repeat(0);
|
||||
tap.set_end_ir(state_t::run_test_idle);
|
||||
tap.set_end_dr(state_t::run_test_idle);
|
||||
|
||||
reset();
|
||||
bypass();
|
||||
enable();
|
||||
|
||||
shift_ir(instruction_t::ISC_READ);
|
||||
}
|
||||
|
||||
std::array<bool, 274> XC2C64A::read_block_eeprom(block_id_t id) {
|
||||
const jtag::tap::bits_t empty_row{block_length};
|
||||
|
||||
tap.set_end_dr(state_t::pause_dr);
|
||||
tap.shift_dr({&id, block_id_length});
|
||||
tap.set_end_ir(state_t::run_test_idle);
|
||||
tap.wait(state_t::pause_dr, state_t::pause_dr, 20);
|
||||
tap.set_end_ir(state_t::run_test_idle);
|
||||
tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100);
|
||||
|
||||
std::vector<bool> from_device = tap.shift_dr_read(empty_row);
|
||||
|
||||
tap.wait(state_t::run_test_idle, state_t::run_test_idle, 100);
|
||||
|
||||
std::array<bool, block_length> ret;
|
||||
std::copy_n(std::make_move_iterator(from_device.begin()), block_length, ret.begin());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void XC2C64A::finalize_read_eeprom() {
|
||||
disable();
|
||||
bypass();
|
||||
|
||||
tap.state(state_t::test_logic_reset);
|
||||
}
|
||||
|
||||
bool XC2C64A::shift_ir(const instruction_t instruction) {
|
||||
const ir_t ir_buffer = toUType(instruction);
|
||||
const jtag::tap::bits_t bits{&ir_buffer, ir_length};
|
||||
|
||||
@@ -68,6 +68,13 @@ class XC2C64A {
|
||||
bool verify_eeprom(const verify_blocks_t& blocks);
|
||||
void init_from_eeprom();
|
||||
|
||||
void prepare_read_eeprom();
|
||||
void prepare_read_sram();
|
||||
std::array<bool, block_length> read_block_eeprom(block_id_t id);
|
||||
std::array<bool, block_length> read_block_sram(verify_block_t block);
|
||||
void finalize_read_eeprom();
|
||||
void finalize_read_sram(block_id_t id);
|
||||
|
||||
private:
|
||||
static constexpr size_t idcode_length = 32;
|
||||
using idcode_t = uint32_t;
|
||||
|
||||
@@ -82,6 +82,19 @@ CommodityType Packet::commodity_type() const {
|
||||
return invalid_commodity_type;
|
||||
}
|
||||
|
||||
TamperFlags Packet::tamper_flags() const {
|
||||
if (type() == Type::SCM) {
|
||||
return (reader_.read(9, 2) << 4) | reader_.read(3, 2); // Physical/Encoder tamper flags in lower/upper nibbles
|
||||
}
|
||||
if (type() == Type::SCMPLUS) {
|
||||
return reader_.read(10 * 8, 16);
|
||||
}
|
||||
if (type() == Type::IDM) {
|
||||
return reader_.read(11 * 8, 48);
|
||||
}
|
||||
return invalid_tamper_flags;
|
||||
}
|
||||
|
||||
FormattedSymbols Packet::symbols_formatted() const {
|
||||
return format_symbols(decoder_);
|
||||
}
|
||||
|
||||
@@ -34,10 +34,12 @@ namespace ert {
|
||||
using ID = uint32_t;
|
||||
using Consumption = uint32_t;
|
||||
using CommodityType = uint32_t;
|
||||
using TamperFlags = uint32_t;
|
||||
|
||||
constexpr ID invalid_id = 0;
|
||||
constexpr CommodityType invalid_commodity_type = -1;
|
||||
constexpr Consumption invalid_consumption = 0;
|
||||
constexpr TamperFlags invalid_tamper_flags = 0;
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
@@ -67,6 +69,7 @@ class Packet {
|
||||
ID id() const;
|
||||
CommodityType commodity_type() const;
|
||||
Consumption consumption() const;
|
||||
TamperFlags tamper_flags() const;
|
||||
|
||||
FormattedSymbols symbols_formatted() const;
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ constexpr size_t clock_generator_output_og_mcu_clkin = 7;
|
||||
|
||||
constexpr size_t clock_generator_output_r9_if = 0;
|
||||
constexpr size_t clock_generator_output_r9_sgpio = 1;
|
||||
constexpr size_t clock_generator_output_r9_clkout = 2;
|
||||
constexpr size_t clock_generator_output_r9_mcu_clkin = 2;
|
||||
|
||||
/* ADC0 */
|
||||
|
||||
@@ -37,4 +37,15 @@ uint32_t JTAG::shift(const size_t count, uint32_t value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t JTAG::shift_header(const size_t count, uint32_t value) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
const auto tdo = target.clock(
|
||||
0,
|
||||
value & 1);
|
||||
value >>= 1;
|
||||
value |= tdo << (count - 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} /* namespace jtag */
|
||||
|
||||
@@ -89,10 +89,29 @@ class JTAG {
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t shift_dr(const size_t count, const uint32_t address, const uint32_t value) {
|
||||
/* Run-Test/Idle -> Select-DR-Scan */
|
||||
target.clock(1, 0);
|
||||
/* Scan -> Capture -> Shift */
|
||||
target.clock(0, 0);
|
||||
target.clock(0, 0);
|
||||
|
||||
shift_header(count, address);
|
||||
const auto result = shift(count, value);
|
||||
|
||||
/* Exit1 -> Update */
|
||||
target.clock(1, 0);
|
||||
/* Update -> Run-Test/Idle */
|
||||
target.clock(0, 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
Target& target;
|
||||
|
||||
uint32_t shift(const size_t count, uint32_t value);
|
||||
uint32_t shift_header(const size_t count, uint32_t value);
|
||||
};
|
||||
|
||||
} /* namespace jtag */
|
||||
|
||||
@@ -191,6 +191,10 @@ bool TAPMachine::shift_dr(const bits_t& tdi_value, const bits_t& tdo_expected, c
|
||||
return shift_data(tdi_value, tdo_expected, tdo_mask, state_t::shift_dr, _end_dr, _run_test);
|
||||
}
|
||||
|
||||
std::vector<bool> TAPMachine::shift_dr_read(const bits_t& tdi_value) {
|
||||
return shift_data_read(tdi_value, state_t::shift_dr, _end_dr, _run_test);
|
||||
}
|
||||
|
||||
void TAPMachine::state(const state_t state) {
|
||||
if (state == state_t::test_logic_reset) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
@@ -227,7 +231,7 @@ void TAPMachine::shift_start(const state_t state) {
|
||||
advance_to_state(state);
|
||||
}
|
||||
|
||||
bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms) {
|
||||
bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms, std::vector<bool>* from_device) {
|
||||
if (tdo_expected.length() != tdo_mask.length()) {
|
||||
return false;
|
||||
}
|
||||
@@ -239,6 +243,10 @@ bool TAPMachine::shift(const bits_t& tdi, const bits_t& tdo_expected, const bits
|
||||
for (uint32_t i = 0; i < tdi.length(); i++) {
|
||||
const auto tms = end_tms & (i == (tdi.length() - 1));
|
||||
const auto tdo = clock(tms, tdi[i]);
|
||||
|
||||
if (from_device != nullptr)
|
||||
from_device->push_back(tdo);
|
||||
|
||||
if (tdo_expected && tdo_mask) {
|
||||
tdo_error |= (tdo & tdo_mask[i]) != (tdo_expected[i] & tdo_mask[i]);
|
||||
}
|
||||
@@ -258,7 +266,15 @@ void TAPMachine::shift_end(const state_t end_state, const uint32_t end_delay) {
|
||||
|
||||
bool TAPMachine::shift_data(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const state_t state, const state_t end_state, const uint32_t end_delay) {
|
||||
shift_start(state);
|
||||
const auto result = shift(tdi, tdo_expected, tdo_mask, true);
|
||||
const auto result = shift(tdi, tdo_expected, tdo_mask, true, nullptr);
|
||||
shift_end(end_state, end_delay);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<bool> TAPMachine::shift_data_read(const bits_t& tdi, const state_t state, const state_t end_state, const uint32_t end_delay) {
|
||||
shift_start(state);
|
||||
std::vector<bool> result;
|
||||
shift(tdi, {}, {}, true, &result);
|
||||
shift_end(end_state, end_delay);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace jtag {
|
||||
namespace tap {
|
||||
@@ -113,13 +114,14 @@ class TAPMachine {
|
||||
void set_end_dr(const state_t state);
|
||||
|
||||
bool shift(const bits_t& tdi, const bool end_tms) {
|
||||
return shift(tdi, {}, {}, end_tms);
|
||||
return shift(tdi, {}, {}, end_tms, nullptr);
|
||||
}
|
||||
|
||||
bool shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms);
|
||||
bool shift(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const bool end_tms, std::vector<bool>* from_device);
|
||||
|
||||
bool shift_ir(const bits_t& tdi_value, const bits_t& tdo_expected = {}, const bits_t& tdo_mask = {});
|
||||
bool shift_dr(const bits_t& tdi_value, const bits_t& tdo_expected = {}, const bits_t& tdo_mask = {});
|
||||
std::vector<bool> shift_dr_read(const bits_t& tdi_value);
|
||||
|
||||
void state(const state_t state);
|
||||
|
||||
@@ -142,6 +144,7 @@ class TAPMachine {
|
||||
void shift_end(const state_t end_state, const uint32_t end_delay);
|
||||
|
||||
bool shift_data(const bits_t& tdi, const bits_t& tdo_expected, const bits_t& tdo_mask, const state_t state, const state_t end_state, const uint32_t end_delay);
|
||||
std::vector<bool> shift_data_read(const bits_t& tdi, const state_t state, const state_t end_state, const uint32_t end_delay);
|
||||
};
|
||||
|
||||
} /* namespace tap */
|
||||
|
||||
@@ -77,7 +77,7 @@ constexpr modem_repeat_range_t modem_repeat_range{1, 99};
|
||||
constexpr int32_t modem_repeat_reset_value{5};
|
||||
|
||||
using clkout_freq_range_t = range_t<uint32_t>;
|
||||
constexpr clkout_freq_range_t clkout_freq_range{10, 60000};
|
||||
constexpr clkout_freq_range_t clkout_freq_range{4, 60000}; // Min. CLK out of Si5351A/B/C-B is 2.5khz , but in our application -intermediate freq 800Mhz-,Min working CLK=4khz.
|
||||
constexpr uint16_t clkout_freq_reset_value{10000};
|
||||
|
||||
enum data_structure_version_enum : uint32_t {
|
||||
@@ -195,6 +195,9 @@ struct data_t {
|
||||
|
||||
// Recon App
|
||||
uint64_t recon_config;
|
||||
int8_t recon_repeat_nb;
|
||||
int8_t recon_repeat_gain;
|
||||
uint8_t recon_repeat_delay;
|
||||
|
||||
// enable or disable converter
|
||||
bool converter;
|
||||
@@ -257,7 +260,11 @@ struct data_t {
|
||||
tone_mix(tone_mix_reset_value),
|
||||
|
||||
hardware_config(0),
|
||||
|
||||
recon_config(0),
|
||||
recon_repeat_nb(0),
|
||||
recon_repeat_gain(0),
|
||||
recon_repeat_delay(0),
|
||||
|
||||
converter(false),
|
||||
updown_converter(false),
|
||||
@@ -284,11 +291,11 @@ struct data_t {
|
||||
|
||||
struct backup_ram_t {
|
||||
private:
|
||||
volatile uint32_t regfile[63];
|
||||
volatile uint32_t regfile[PMEM_SIZE_WORDS - 1];
|
||||
volatile uint32_t check_value;
|
||||
|
||||
static void copy(const backup_ram_t& src, backup_ram_t& dst) {
|
||||
for (size_t i = 0; i < 63; i++) {
|
||||
for (size_t i = 0; i < PMEM_SIZE_WORDS - 1; i++) {
|
||||
dst.regfile[i] = src.regfile[i];
|
||||
}
|
||||
dst.check_value = src.check_value;
|
||||
@@ -297,7 +304,7 @@ struct backup_ram_t {
|
||||
static void copy_from_data_t(const data_t& src, backup_ram_t& dst) {
|
||||
const uint32_t* const src_words = (uint32_t*)&src;
|
||||
const size_t word_count = (sizeof(data_t) + 3) / 4;
|
||||
for (size_t i = 0; i < 63; i++) {
|
||||
for (size_t i = 0; i < PMEM_SIZE_WORDS - 1; i++) {
|
||||
if (i < word_count) {
|
||||
dst.regfile[i] = src_words[i];
|
||||
} else {
|
||||
@@ -308,7 +315,7 @@ struct backup_ram_t {
|
||||
|
||||
uint32_t compute_check_value() {
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
for (size_t i = 0; i < 63; i++) {
|
||||
for (size_t i = 0; i < PMEM_SIZE_WORDS - 1; i++) {
|
||||
const auto word = regfile[i];
|
||||
crc.process_byte((word >> 0) & 0xff);
|
||||
crc.process_byte((word >> 8) & 0xff);
|
||||
@@ -391,10 +398,16 @@ void defaults() {
|
||||
set_recon_continuous(true);
|
||||
set_recon_clear_output(false);
|
||||
set_recon_load_freqs(true);
|
||||
set_recon_load_repeaters(true);
|
||||
set_recon_load_ranges(true);
|
||||
set_recon_update_ranges_when_recon(true);
|
||||
set_recon_load_hamradios(true);
|
||||
set_recon_match_mode(0);
|
||||
set_recon_repeat_recorded(false);
|
||||
set_recon_repeat_amp(false);
|
||||
set_recon_repeat_gain(35);
|
||||
set_recon_repeat_nb(3);
|
||||
set_recon_repeat_delay(1);
|
||||
|
||||
set_config_sdcard_high_speed_io(false, true);
|
||||
}
|
||||
@@ -747,6 +760,24 @@ bool recon_match_mode() {
|
||||
bool recon_auto_record_locked() {
|
||||
return (data->recon_config & 0x00400000UL) ? true : false;
|
||||
}
|
||||
bool recon_repeat_recorded() {
|
||||
return (data->recon_config & 0x00200000UL) ? true : false;
|
||||
}
|
||||
int8_t recon_repeat_nb() {
|
||||
return data->recon_repeat_nb;
|
||||
}
|
||||
int8_t recon_repeat_gain() {
|
||||
return data->recon_repeat_gain;
|
||||
}
|
||||
uint8_t recon_repeat_delay() {
|
||||
return data->recon_repeat_delay;
|
||||
}
|
||||
bool recon_repeat_amp() {
|
||||
return (data->recon_config & 0x00100000UL) ? true : false;
|
||||
}
|
||||
bool recon_load_repeaters() {
|
||||
return (data->recon_config & 0x00080000UL) ? true : false;
|
||||
}
|
||||
|
||||
void set_recon_autosave_freqs(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x80000000UL) | (v << 31);
|
||||
@@ -778,6 +809,24 @@ void set_recon_match_mode(const bool v) {
|
||||
void set_recon_auto_record_locked(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00400000UL) | (v << 22);
|
||||
}
|
||||
void set_recon_repeat_recorded(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00200000UL) | (v << 21);
|
||||
}
|
||||
void set_recon_repeat_nb(const int8_t v) {
|
||||
data->recon_repeat_nb = v;
|
||||
}
|
||||
void set_recon_repeat_gain(const int8_t v) {
|
||||
data->recon_repeat_gain = v;
|
||||
}
|
||||
void set_recon_repeat_delay(const uint8_t v) {
|
||||
data->recon_repeat_delay = v;
|
||||
}
|
||||
void set_recon_repeat_amp(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00100000UL) | (v << 20);
|
||||
}
|
||||
void set_recon_load_repeaters(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00080000UL) | (v << 19);
|
||||
}
|
||||
|
||||
/* UI Config 2 */
|
||||
bool ui_hide_speaker() {
|
||||
@@ -988,6 +1037,9 @@ bool debug_dump() {
|
||||
pmem_dump_file.write_line("tone_mix: " + to_string_dec_uint(data->tone_mix));
|
||||
pmem_dump_file.write_line("hardware_config: " + to_string_dec_uint(data->hardware_config));
|
||||
pmem_dump_file.write_line("recon_config: 0x" + to_string_hex(data->recon_config, 16));
|
||||
pmem_dump_file.write_line("recon_repeat_nb: " + to_string_dec_int(data->recon_repeat_nb, 16));
|
||||
pmem_dump_file.write_line("recon_repeat_gain:" + to_string_hex(data->recon_config, 16));
|
||||
pmem_dump_file.write_line("recon_repeat_delay:" + to_string_hex(data->recon_config, 16));
|
||||
pmem_dump_file.write_line("converter: " + to_string_dec_int(data->converter));
|
||||
pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(data->updown_converter));
|
||||
pmem_dump_file.write_line("updown_frequency_rx_correction: " + to_string_dec_int(data->updown_frequency_rx_correction));
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
// persistant memory from/to sdcard flag file
|
||||
#define PMEM_SETTING_FILE u"/SETTINGS/pmem_settings"
|
||||
|
||||
#define PMEM_SIZE_BYTES 256 // total amount of pmem space in bytes, including checksum
|
||||
#define PMEM_SIZE_WORDS (PMEM_SIZE_BYTES / 4)
|
||||
|
||||
using namespace modems;
|
||||
using namespace serializer;
|
||||
|
||||
@@ -242,11 +245,17 @@ bool recon_autostart_recon();
|
||||
bool recon_continuous();
|
||||
bool recon_clear_output();
|
||||
bool recon_load_freqs();
|
||||
bool recon_load_repeaters();
|
||||
bool recon_load_ranges();
|
||||
bool recon_update_ranges_when_recon();
|
||||
bool recon_auto_record_locked();
|
||||
bool recon_repeat_recorded();
|
||||
int8_t recon_repeat_nb();
|
||||
int8_t recon_repeat_gain();
|
||||
bool recon_repeat_amp();
|
||||
bool recon_load_hamradios();
|
||||
bool recon_match_mode();
|
||||
uint8_t recon_repeat_delay();
|
||||
void set_recon_autosave_freqs(const bool v);
|
||||
void set_recon_autostart_recon(const bool v);
|
||||
void set_recon_continuous(const bool v);
|
||||
@@ -255,8 +264,14 @@ void set_recon_load_freqs(const bool v);
|
||||
void set_recon_load_ranges(const bool v);
|
||||
void set_recon_update_ranges_when_recon(const bool v);
|
||||
void set_recon_auto_record_locked(const bool v);
|
||||
void set_recon_repeat_recorded(const bool v);
|
||||
void set_recon_repeat_nb(const int8_t v);
|
||||
void set_recon_repeat_gain(const int8_t v);
|
||||
void set_recon_repeat_amp(const bool v);
|
||||
void set_recon_load_hamradios(const bool v);
|
||||
void set_recon_load_repeaters(const bool v);
|
||||
void set_recon_match_mode(const bool v);
|
||||
void set_recon_repeat_delay(const uint8_t v);
|
||||
|
||||
/* UI Config 2 */
|
||||
bool ui_hide_speaker();
|
||||
|
||||
@@ -371,6 +371,7 @@ enum class KeyEvent : uint8_t {
|
||||
};
|
||||
|
||||
using EncoderEvent = int32_t;
|
||||
using KeyboardEvent = uint8_t;
|
||||
|
||||
struct TouchEvent {
|
||||
enum class Type : uint32_t {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ui_language.hpp"
|
||||
|
||||
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:"};
|
||||
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:", "Loop"};
|
||||
|
||||
const char** LanguageHelper::currentMessages = englishMessages;
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ enum LangConsts {
|
||||
LANG_SCAN,
|
||||
LANG_CLEAR,
|
||||
LANG_READY,
|
||||
LANG_DATADP
|
||||
LANG_DATADP,
|
||||
LANG_LOOP
|
||||
};
|
||||
|
||||
class LanguageHelper {
|
||||
|
||||
@@ -172,6 +172,10 @@ bool Widget::on_touch(const TouchEvent event) {
|
||||
(void)event;
|
||||
return false;
|
||||
}
|
||||
bool Widget::on_keyboard(const KeyboardEvent event) {
|
||||
(void)event;
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<Widget*>& Widget::children() const {
|
||||
return no_children;
|
||||
@@ -233,6 +237,12 @@ void Widget::dirty_overlapping_children_in_rect(const Rect& child_rect) {
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::getAccessibilityText(std::string& result) {
|
||||
result = "";
|
||||
}
|
||||
void Widget::getWidgetName(std::string& result) {
|
||||
result = "";
|
||||
}
|
||||
/* View ******************************************************************/
|
||||
|
||||
void View::paint(Painter& painter) {
|
||||
@@ -363,6 +373,12 @@ void Text::set(std::string_view value) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void Text::getAccessibilityText(std::string& result) {
|
||||
result = text;
|
||||
}
|
||||
void Text::getWidgetName(std::string& result) {
|
||||
result = "Text";
|
||||
}
|
||||
void Text::paint(Painter& painter) {
|
||||
const auto rect = screen_rect();
|
||||
auto s = has_focus() ? style().invert() : style();
|
||||
@@ -403,6 +419,17 @@ void Labels::paint(Painter& painter) {
|
||||
}
|
||||
}
|
||||
|
||||
void Labels::getAccessibilityText(std::string& result) {
|
||||
result = "";
|
||||
for (auto& label : labels_) {
|
||||
result += label.text;
|
||||
result += ", ";
|
||||
}
|
||||
}
|
||||
void Labels::getWidgetName(std::string& result) {
|
||||
result = "Labels";
|
||||
}
|
||||
|
||||
/* LiveDateTime **********************************************************/
|
||||
|
||||
void LiveDateTime::on_tick_second() {
|
||||
@@ -575,6 +602,13 @@ void ProgressBar::set_value(const uint32_t value) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void ProgressBar::getAccessibilityText(std::string& result) {
|
||||
result = to_string_dec_uint(_value) + " / " + to_string_dec_uint(_max);
|
||||
}
|
||||
void ProgressBar::getWidgetName(std::string& result) {
|
||||
result = "ProgressBar";
|
||||
}
|
||||
|
||||
void ProgressBar::paint(Painter& painter) {
|
||||
int v_scaled;
|
||||
|
||||
@@ -675,6 +709,13 @@ void Console::write(std::string message) {
|
||||
}
|
||||
}
|
||||
|
||||
void Console::getAccessibilityText(std::string& result) {
|
||||
result = "{" + buffer + "}";
|
||||
}
|
||||
void Console::getWidgetName(std::string& result) {
|
||||
result = "Console";
|
||||
}
|
||||
|
||||
void Console::writeln(std::string message) {
|
||||
write(message + "\n");
|
||||
}
|
||||
@@ -783,6 +824,13 @@ bool Checkbox::set_value(const bool value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Checkbox::getAccessibilityText(std::string& result) {
|
||||
result = text_ + ((value_) ? " checked" : " unchecked");
|
||||
}
|
||||
void Checkbox::getWidgetName(std::string& result) {
|
||||
result = "Checkbox";
|
||||
}
|
||||
|
||||
bool Checkbox::value() const {
|
||||
return value_;
|
||||
}
|
||||
@@ -853,6 +901,11 @@ bool Checkbox::on_key(const KeyEvent key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Checkbox::on_keyboard(const KeyboardEvent event) {
|
||||
if (event == 10 || event == 32) return set_value(not value_);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Checkbox::on_touch(const TouchEvent event) {
|
||||
switch (event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
@@ -895,6 +948,13 @@ std::string Button::text() const {
|
||||
return text_;
|
||||
}
|
||||
|
||||
void Button::getAccessibilityText(std::string& result) {
|
||||
result = text_;
|
||||
}
|
||||
void Button::getWidgetName(std::string& result) {
|
||||
result = "Button";
|
||||
}
|
||||
|
||||
void Button::paint(Painter& painter) {
|
||||
Color bg, fg;
|
||||
const auto r = screen_rect();
|
||||
@@ -944,6 +1004,16 @@ bool Button::on_key(const KeyEvent key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Button::on_keyboard(const KeyboardEvent event) {
|
||||
if (event == 10 || event == 32) {
|
||||
if (on_select) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Button::on_touch(const TouchEvent event) {
|
||||
switch (event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
@@ -1031,6 +1101,13 @@ std::string ButtonWithEncoder::text() const {
|
||||
return text_;
|
||||
}
|
||||
|
||||
void ButtonWithEncoder::getAccessibilityText(std::string& result) {
|
||||
result = text_;
|
||||
}
|
||||
void ButtonWithEncoder::getWidgetName(std::string& result) {
|
||||
result = "ButtonWithEncoder";
|
||||
}
|
||||
|
||||
void ButtonWithEncoder::paint(Painter& painter) {
|
||||
Color bg, fg;
|
||||
const auto r = screen_rect();
|
||||
@@ -1080,6 +1157,16 @@ bool ButtonWithEncoder::on_key(const KeyEvent key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ButtonWithEncoder::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == 32 || key == 10) {
|
||||
if (on_select) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ButtonWithEncoder::on_touch(const TouchEvent event) {
|
||||
switch (event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
@@ -1176,6 +1263,13 @@ void NewButton::set_text(const std::string value) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void NewButton::getAccessibilityText(std::string& result) {
|
||||
result = text_;
|
||||
}
|
||||
void NewButton::getWidgetName(std::string& result) {
|
||||
result = "NewButton";
|
||||
}
|
||||
|
||||
std::string NewButton::text() const {
|
||||
return text_;
|
||||
}
|
||||
@@ -1274,6 +1368,16 @@ bool NewButton::on_key(const KeyEvent key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NewButton::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == 32 || key == 10) {
|
||||
if (on_select) {
|
||||
on_select();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NewButton::on_touch(const TouchEvent event) {
|
||||
switch (event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
@@ -1360,6 +1464,13 @@ ImageButton::ImageButton(
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
void ImageButton::getAccessibilityText(std::string& result) {
|
||||
result = "image";
|
||||
}
|
||||
void ImageButton::getWidgetName(std::string& result) {
|
||||
result = "ImageButton";
|
||||
}
|
||||
|
||||
bool ImageButton::on_key(const KeyEvent key) {
|
||||
if (key == KeyEvent::Select) {
|
||||
if (on_select) {
|
||||
@@ -1371,6 +1482,16 @@ bool ImageButton::on_key(const KeyEvent key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImageButton::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == 32 || key == 10) {
|
||||
if (on_select) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImageButton::on_touch(const TouchEvent event) {
|
||||
switch (event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
@@ -1440,6 +1561,13 @@ bool ImageToggle::value() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void ImageToggle::getAccessibilityText(std::string& result) {
|
||||
result = value_ ? "checked" : "unchecked";
|
||||
}
|
||||
void ImageToggle::getWidgetName(std::string& result) {
|
||||
result = "ImageToggle";
|
||||
}
|
||||
|
||||
void ImageToggle::set_value(bool b) {
|
||||
if (b == value_)
|
||||
return;
|
||||
@@ -1475,6 +1603,13 @@ size_t ImageOptionsField::selected_index_value() const {
|
||||
return options[selected_index_].second;
|
||||
}
|
||||
|
||||
void ImageOptionsField::getAccessibilityText(std::string& result) {
|
||||
result = "selected index: " + to_string_dec_uint(selected_index_);
|
||||
}
|
||||
void ImageOptionsField::getWidgetName(std::string& result) {
|
||||
result = "ImageOptionsField";
|
||||
}
|
||||
|
||||
void ImageOptionsField::set_selected_index(const size_t new_index) {
|
||||
if (new_index < options.size()) {
|
||||
if (new_index != selected_index()) {
|
||||
@@ -1537,6 +1672,12 @@ bool ImageOptionsField::on_encoder(const EncoderEvent delta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImageOptionsField::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == '+' || key == ' ' || key == 10) return on_encoder(1);
|
||||
if (key == '-' || key == 8) return on_encoder(-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImageOptionsField::on_touch(const TouchEvent event) {
|
||||
if (event.type == TouchEvent::Type::Start) {
|
||||
focus();
|
||||
@@ -1568,6 +1709,20 @@ const OptionsField::value_t& OptionsField::selected_index_value() const {
|
||||
return options_[selected_index_].second;
|
||||
}
|
||||
|
||||
void OptionsField::getAccessibilityText(std::string& result) {
|
||||
result = "options: ";
|
||||
bool first = true;
|
||||
for (const auto& option : options_) {
|
||||
if (!first) result += " ,";
|
||||
first = false;
|
||||
result += option.first;
|
||||
}
|
||||
result += "; selected: " + selected_index_name();
|
||||
}
|
||||
void OptionsField::getWidgetName(std::string& result) {
|
||||
result = "OptionsField";
|
||||
}
|
||||
|
||||
void OptionsField::set_selected_index(const size_t new_index, bool trigger_change) {
|
||||
if (new_index < options_.size()) {
|
||||
if (new_index != selected_index() || trigger_change) {
|
||||
@@ -1653,6 +1808,11 @@ bool OptionsField::on_encoder(const EncoderEvent delta) {
|
||||
set_selected_index(new_value);
|
||||
return true;
|
||||
}
|
||||
bool OptionsField::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == '+' || key == ' ' || key == 10) return on_encoder(1);
|
||||
if (key == '-' || key == 8) return on_encoder(-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OptionsField::on_touch(const TouchEvent event) {
|
||||
if (event.type == TouchEvent::Type::Start) {
|
||||
@@ -1681,6 +1841,13 @@ const std::string& TextEdit::value() const {
|
||||
return text_;
|
||||
}
|
||||
|
||||
void TextEdit::getAccessibilityText(std::string& result) {
|
||||
result = text_;
|
||||
}
|
||||
void TextEdit::getWidgetName(std::string& result) {
|
||||
result = "TextEdit";
|
||||
}
|
||||
|
||||
void TextEdit::set_cursor(uint32_t pos) {
|
||||
cursor_pos_ = std::min<size_t>(pos, text_.length());
|
||||
set_dirty();
|
||||
@@ -1772,6 +1939,19 @@ bool TextEdit::on_key(const KeyEvent key) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextEdit::on_keyboard(const KeyboardEvent key) {
|
||||
// if ascii printable
|
||||
if (key >= 0x20 && key <= 0x7e) {
|
||||
char_add(key);
|
||||
return true;
|
||||
}
|
||||
if (key == 8) {
|
||||
char_delete();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TextEdit::on_encoder(const EncoderEvent delta) {
|
||||
int32_t new_pos = cursor_pos_ + delta;
|
||||
|
||||
@@ -1817,6 +1997,13 @@ const std::string& TextField::get_text() const {
|
||||
return text;
|
||||
}
|
||||
|
||||
void TextField::getAccessibilityText(std::string& result) {
|
||||
result = text;
|
||||
}
|
||||
void TextField::getWidgetName(std::string& result) {
|
||||
result = "TextField";
|
||||
}
|
||||
|
||||
void TextField::set_text(std::string_view value) {
|
||||
set(value);
|
||||
if (on_change)
|
||||
@@ -1872,6 +2059,13 @@ int32_t NumberField::value() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void NumberField::getAccessibilityText(std::string& result) {
|
||||
result = to_string_dec_int(value_);
|
||||
}
|
||||
void NumberField::getWidgetName(std::string& result) {
|
||||
result = "NumberField";
|
||||
}
|
||||
|
||||
void NumberField::set_value(int32_t new_value, bool trigger_change) {
|
||||
if (can_loop) {
|
||||
if (new_value >= range.first)
|
||||
@@ -1928,6 +2122,22 @@ bool NumberField::on_encoder(const EncoderEvent delta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NumberField::on_keyboard(const KeyboardEvent key) {
|
||||
if (key == 10) {
|
||||
if (on_select) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (key == '+' || key == ' ') {
|
||||
return on_encoder(1);
|
||||
}
|
||||
if (key == '-' || key == 8) {
|
||||
return on_encoder(-1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NumberField::on_touch(const TouchEvent event) {
|
||||
if (event.type == TouchEvent::Type::Start) {
|
||||
focus();
|
||||
@@ -2066,6 +2276,12 @@ const std::string& SymField::to_string() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void SymField::getAccessibilityText(std::string& result) {
|
||||
result = value_;
|
||||
}
|
||||
void SymField::getWidgetName(std::string& result) {
|
||||
result = "SymField";
|
||||
}
|
||||
void SymField::paint(Painter& painter) {
|
||||
Point p = screen_pos();
|
||||
|
||||
|
||||
@@ -101,10 +101,14 @@ class Widget {
|
||||
virtual bool on_key(const KeyEvent event);
|
||||
virtual bool on_encoder(const EncoderEvent event);
|
||||
virtual bool on_touch(const TouchEvent event);
|
||||
virtual bool on_keyboard(const KeyboardEvent event);
|
||||
virtual const std::vector<Widget*>& children() const;
|
||||
|
||||
virtual Context& context() const;
|
||||
|
||||
virtual void getAccessibilityText(std::string& result);
|
||||
virtual void getWidgetName(std::string& result);
|
||||
|
||||
void set_style(const Style* new_style);
|
||||
|
||||
const Style& style() const;
|
||||
@@ -206,6 +210,8 @@ class Text : public Widget {
|
||||
void set(std::string_view value);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
protected:
|
||||
// NB: Don't truncate this string. The UI will only render
|
||||
@@ -233,6 +239,8 @@ class Labels : public Widget {
|
||||
void set_labels(std::initializer_list<Label> labels);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
std::vector<Label> labels_;
|
||||
@@ -311,6 +319,8 @@ class ProgressBar : public Widget {
|
||||
void set_value(const uint32_t value);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
uint32_t _value = 0;
|
||||
@@ -344,6 +354,8 @@ class Console : public Widget {
|
||||
void enable_scrolling(bool enable);
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
Point pos{0, 0};
|
||||
@@ -382,7 +394,10 @@ class Checkbox : public Widget {
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_keyboard(const KeyboardEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
@@ -418,6 +433,9 @@ class Button : public Widget {
|
||||
void on_focus() override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
@@ -456,6 +474,10 @@ class ButtonWithEncoder : public Widget {
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
@@ -490,6 +512,10 @@ class NewButton : public Widget {
|
||||
void on_focus() override;
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
@@ -544,6 +570,9 @@ class ImageButton : public Image {
|
||||
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
};
|
||||
|
||||
/* A button that toggles between two images when set. */
|
||||
@@ -577,6 +606,9 @@ class ImageToggle : public ImageButton {
|
||||
bool value() const;
|
||||
void set_value(bool b);
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
// Hide this field.
|
||||
using ImageButton::on_select;
|
||||
@@ -621,6 +653,9 @@ class ImageOptionsField : public Widget {
|
||||
void on_focus() override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
options_t options;
|
||||
@@ -658,6 +693,10 @@ class OptionsField : public Widget {
|
||||
void on_focus() override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
const size_t length_;
|
||||
@@ -702,6 +741,10 @@ class TextEdit : public Widget {
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
@@ -729,6 +772,9 @@ class TextField : public Text {
|
||||
bool on_encoder(EncoderEvent delta) override;
|
||||
bool on_touch(TouchEvent event) override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
using Text::set;
|
||||
};
|
||||
@@ -763,6 +809,10 @@ class NumberField : public Widget {
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
bool on_keyboard(const KeyboardEvent event) override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
range_t range;
|
||||
@@ -839,6 +889,9 @@ class SymField : public Widget {
|
||||
bool on_encoder(EncoderEvent delta) override;
|
||||
bool on_touch(TouchEvent event) override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
/* Ensure the specified symbol is in the symbol list. */
|
||||
char ensure_valid(char symbol) const;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user