mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-08 17:03:49 +00:00
Compare commits
219 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 15f3d81798 | |||
| fa2791004f | |||
| 50bd08d9ff | |||
| 39134b62c3 | |||
| e5a30b4309 | |||
| 2031a79b34 | |||
| deff646e36 | |||
| 4aa3314906 | |||
| e3da9d4cb2 | |||
| 2f10fe2fe4 | |||
| 5a7b12f920 | |||
| f8a4134973 | |||
| a3ee160e27 | |||
| ade0d6c68f | |||
| 8e9355b57a | |||
| e97ffd42ef | |||
| cccc92cc34 | |||
| 3387b59eab | |||
| 6c5f8a38e0 | |||
| de1570d8af | |||
| 1027e80d53 | |||
| c9db1aab30 | |||
| 367cb318cd | |||
| fe33c12111 | |||
| 8957d692ea | |||
| 5551c6a676 | |||
| 2b7b0d028a | |||
| 7b42d9ec94 | |||
| 0580a528e4 | |||
| c75c4685cd | |||
| da49743cc4 | |||
| 799a473b36 | |||
| dccc68a4e0 | |||
| ee9b4c89bd | |||
| a91bbe6a2e | |||
| a5a9bc85f8 | |||
| b23addd452 | |||
| 6467e5e7e6 | |||
| 35ed394b37 | |||
| 4c98cf16a1 | |||
| 18be6750e5 | |||
| 4f58be737a | |||
| 3fa190324b | |||
| de042df7eb | |||
| 2fbf96425a | |||
| 85d5a8fdba | |||
| f13d1f8b9c | |||
| f804d7997c | |||
| 2108ea53fa | |||
| 90d38af705 | |||
| d617618dcd | |||
| c37df0c6fd | |||
| cdba4fa5bf | |||
| 1537b1eccb | |||
| 048b254354 | |||
| 4b87a8a7dd | |||
| 5cdaac6b82 | |||
| 17aa58bbf8 | |||
| e93a5820a2 | |||
| 0e9c7448c0 | |||
| 9f54f4b5eb | |||
| 178528ef96 | |||
| 5d9a2e560b | |||
| b02329019c | |||
| 8c1593fab9 | |||
| 1f27af660d | |||
| a9d2b2ff8a | |||
| a81d630d93 | |||
| efdefa85be | |||
| f25ce9ed45 | |||
| 6697d2533c | |||
| 6cf5990b0e | |||
| c97e1042b1 | |||
| 9d5ed65f6b | |||
| 2ba74d0413 | |||
| a824e30420 | |||
| 59d08a4203 | |||
| 51bc0f0100 | |||
| d2e185a1fb | |||
| 2d3cb426ba | |||
| dd2fdecb21 | |||
| a37ba1ee2b | |||
| 9fbcab4224 | |||
| 505f26c98a | |||
| dc18e340ab | |||
| 3e86f83099 | |||
| d98f2b40df | |||
| e26d026216 | |||
| 0c0d47b0d2 | |||
| 50821bab55 | |||
| 796d9ca854 | |||
| fd8bc177ad | |||
| eab832396b | |||
| 3bb7ba59ae | |||
| f6c496d1d3 | |||
| b4a6b958e5 | |||
| 43e9ce4704 | |||
| b4e5fb7483 | |||
| 29d3a0f1dd | |||
| 1e413d034f | |||
| 5627634da1 | |||
| 719e7d42fd | |||
| 71d9fd1c87 | |||
| 58bdd358c6 | |||
| 27c93e144b | |||
| 04f781ff52 | |||
| 7a8637fb96 | |||
| 054a931967 | |||
| 0a660aa5c0 | |||
| 9c5bf8dcc0 | |||
| 57df9dd2b7 | |||
| b33ef608ee | |||
| a17eb47657 | |||
| 250553e4ba | |||
| 9200db24be | |||
| 03057546e1 | |||
| 486b064e08 | |||
| e4836a5e5c | |||
| ad742a84e3 | |||
| eec4cd8778 | |||
| d7a795b8f2 | |||
| 579caa93b2 | |||
| 3601545712 | |||
| f7b219fc2b | |||
| 85cfdd75cc | |||
| deef2ebdc2 | |||
| 79a2c3a372 | |||
| 759c4d9d51 | |||
| 77a0d255dd | |||
| 671fc12cf7 | |||
| e1446598a8 | |||
| 7066f931a8 | |||
| aa2c5a4c84 | |||
| d70e1a6fac | |||
| 477e6d65df | |||
| a55e5d4fc0 | |||
| 8da0dbe36b | |||
| 8688ac0e05 | |||
| 82732a3f73 | |||
| be0427889f | |||
| 37a40cbeda | |||
| 4ef98e2928 | |||
| 434c559861 | |||
| 3cb3c95349 | |||
| 979b5126a6 | |||
| 8e3fa0b9ea | |||
| 00b43299ad | |||
| 1f31e9b983 | |||
| 5c97c24b48 | |||
| 4a8458d812 | |||
| 165efd7355 | |||
| d1d1198b1b | |||
| 0025ad2507 | |||
| 32f957cead | |||
| 2030118d9f | |||
| 0523aabe91 | |||
| 1c81c7a8ba | |||
| 9df1cfa670 | |||
| 68d40ebce8 | |||
| 901af22de5 | |||
| d9c1b6b61f | |||
| 8774ee0acd | |||
| c2856ce089 | |||
| d5a1733c1c | |||
| 98c0ff1e47 | |||
| e00d91e1d2 | |||
| 459b8efc52 | |||
| db3f645131 | |||
| b84820963b | |||
| ec23dd8edf | |||
| 4de25a2039 | |||
| a11b9c492f | |||
| 867539ce9c | |||
| 799a701fc3 | |||
| cebbc12084 | |||
| 4a0d42ed34 | |||
| 0d5af740a5 | |||
| 0da0431e5e | |||
| 96879d3664 | |||
| ea251a87c0 | |||
| 7fa64a3f76 | |||
| 7af7279ea4 | |||
| 9726a50533 | |||
| f42479c190 | |||
| dcb67049af | |||
| 4a57af64ce | |||
| 30449e0adf | |||
| c9a813fbd6 | |||
| 0da7d2d0ac | |||
| 7c8f02d05a | |||
| e5015cc67b | |||
| 03c9812487 | |||
| fc08d0ba2d | |||
| dfa7dfb024 | |||
| c713d996ad | |||
| c2ff37a1c7 | |||
| 8ee650503c | |||
| 47014b35ad | |||
| 0648e0d9bd | |||
| 094fa4c905 | |||
| 0a05c63c47 | |||
| f4403121a1 | |||
| 9792e13cbf | |||
| 6c4dec59bb | |||
| 455389f1d1 | |||
| 4a0a243530 | |||
| 3cb9e1849b | |||
| d0f9073b67 | |||
| e2a2f7a448 | |||
| 708111066e | |||
| 2a2abac244 | |||
| c3da2863d5 | |||
| 96db26e884 | |||
| 9f48199a96 | |||
| 834b65d833 | |||
| 662eb0a7f9 | |||
| f60aab68a1 | |||
| 05a09bc988 | |||
| 55fac01ab5 |
@@ -1,12 +1,18 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
about: Create a report to help us improve the software
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
|
||||
(Please try the latest nightly release before submitting this. You can find the latest nightly verison here: https://github.com/eried/portapack-mayhem/releases)
|
||||
|
||||
----
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
@@ -21,9 +27,9 @@ A clear and concise description of what you expected to happen.
|
||||
|
||||
**Affected versions**
|
||||
Please write any difference related with the Expected behavior, on the following versions:
|
||||
* Latest Stable Release
|
||||
* Previous one (if any) that did not presented the issue
|
||||
* Old versions available here: https://github.com/eried/Research/tree/master/HackRF/PortaPack/old_legacy_firmware
|
||||
* Latest Stable release:
|
||||
* Latest Nightly release:
|
||||
* Previous working release:
|
||||
|
||||
**Additional**
|
||||
If the bug is difficult to explain, additionally to the text please include images and videos.
|
||||
|
||||
@@ -1,18 +1,39 @@
|
||||
---
|
||||
name: Problem upgrading the firmware
|
||||
about: Deal with the firmware upgrade problems
|
||||
title: Problem upgrading the firmware
|
||||
labels: ''
|
||||
name: Problem upgrading the firmware or booting
|
||||
about: If you are having firmware upgrade or booting problems
|
||||
title: ''
|
||||
labels: 'firmware'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**What is happening?**
|
||||
Describe here why you are unable to upgrade the firmware. Before describing your problems, **do the following**:
|
||||
----
|
||||
Before creating this issue, **do the following**:
|
||||
* Read the Wiki on booting: https://github.com/eried/portapack-mayhem/wiki/Won't-boot
|
||||
* Read: https://github.com/eried/portapack-havoc/wiki/Update-firmware
|
||||
* Watch carefully: https://www.youtube.com/watch?v=_zx4ZvurgOs
|
||||
* (if you are not in Windows) also check: https://www.youtube.com/watch?v=kjFB58Y1TAo
|
||||
|
||||
----
|
||||
|
||||
**Describe the issue**
|
||||
A clear and concise description of what the issue you are facing is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Tap on '....'
|
||||
|
||||
|
||||
**My Hardware**
|
||||
Please specify what PortaPack hardware version you are using.
|
||||
You can find the list of versions here: https://github.com/eried/portapack-mayhem/wiki/PortaPack-Versions
|
||||
|
||||
**Affected versions**
|
||||
Please tell us what version you are running.
|
||||
Also please try the latest nightly release before submitting this.
|
||||
You can find the latest nightly version here https://github.com/eried/portapack-mayhem/releases
|
||||
|
||||
**Were you able to update the firmware before?**
|
||||
Things might be confusing the first time, please check the video available on the link above.
|
||||
|
||||
@@ -20,4 +41,4 @@ Things might be confusing the first time, please check the video available on th
|
||||
If is possible, swap hardware and try again. Also, try different USB cables, even if the one you are using works fine for other purposes.
|
||||
|
||||
**Additional**
|
||||
Add photos and videos of your procedure.
|
||||
If the issue is difficult to explain, additionally to the text please include images and videos.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
raw_git = os.popen('git log next --since="24 hours" --pretty=format:"- %h - {USERNAME}*+%al-%an*: %s"').read()
|
||||
|
||||
|
||||
def compute_username(line):
|
||||
stripped = re.search(r'(?<=\*)(.*?)(?=\*)', line).group(0)
|
||||
|
||||
pattern = re.compile("[$@+&?].*[$@+&?]")
|
||||
if pattern.match(stripped):
|
||||
stripped = re.sub("[$@+&?].*[$@+&?]", "", stripped)
|
||||
stripped = re.match(r'.+?(?=-)', stripped).group(0)
|
||||
else:
|
||||
stripped = re.sub(r'^.*?-', "", stripped)
|
||||
return "@" + stripped
|
||||
|
||||
|
||||
def compile_line(line):
|
||||
username = compute_username(line)
|
||||
line = re.sub(r'[*].*[*]', "", line)
|
||||
line = line.replace("{USERNAME}", username)
|
||||
return line
|
||||
|
||||
|
||||
for row in raw_git.splitlines():
|
||||
print(compile_line(row))
|
||||
@@ -0,0 +1,99 @@
|
||||
name: Nightly Release
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
check_date:
|
||||
runs-on: ubuntu-latest
|
||||
name: Check latest commit
|
||||
outputs:
|
||||
should_run: ${{ steps.should_run.outputs.should_run }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: print latest_commit
|
||||
run: echo ${{ github.sha }}
|
||||
|
||||
- name: check latest commit is less than a day
|
||||
id: should_run
|
||||
continue-on-error: true
|
||||
run: test -z $(git rev-list --after="24 hours" ${{ github.sha }}) && echo "::set-output name=should_run::false"
|
||||
build:
|
||||
needs: check_date
|
||||
if: ${{ needs.check_date.outputs.should_run != 'false' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
|
||||
- name: Get version date
|
||||
id: version_date
|
||||
run: echo "::set-output name=date::n_$(date +'%y%m%d')"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: next
|
||||
submodules: true
|
||||
- name: Git Sumbodule Update
|
||||
run: |
|
||||
git submodule update --init --recursive
|
||||
- name: Build the Docker image
|
||||
run: docker build -t portapack-dev -f dockerfile-nogit . --tag my-image-name:$(date +%s)
|
||||
- name: Make build folder
|
||||
run: mkdir ${{ github.workspace }}/build
|
||||
- name: Run the Docker image
|
||||
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Firmware ZIP
|
||||
run: |
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
zip -r sdcard.zip sdcard
|
||||
- name: Create changelog
|
||||
run: |
|
||||
CHANGELOG=$(python3 .github/workflows/changelog.py)
|
||||
CHANGELOG="${CHANGELOG//'%'/'%25'}"
|
||||
CHANGELOG="${CHANGELOG//$'\n'/'%0A'}"
|
||||
CHANGELOG="${CHANGELOG//$'\r'/'%0D'}"
|
||||
echo "::set-output name=content::$CHANGELOG"
|
||||
id: changelog
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: nightly-tag-${{ steps.date.outputs.date }}
|
||||
release_name: Nightly Release - ${{ steps.date.outputs.date }}
|
||||
body: |
|
||||
**Nightly release - ${{ steps.date.outputs.date }}**
|
||||
This build is the latest and greatest, although may not be the most stable as this is a nightly release.
|
||||
## Release notes
|
||||
### Revision (${{ steps.version_date.outputs.date }}):
|
||||
${{ steps.changelog.outputs.content }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
- name: Upload Firmware Asset
|
||||
id: upload-firmware-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./firmware.zip
|
||||
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}_FIRMWARE.zip
|
||||
asset_content_type: application/zip
|
||||
- name: Upload SD Card Assets
|
||||
id: upload-sd-card-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./sdcard.zip
|
||||
asset_name: mayhem_nightly_${{ steps.version_date.outputs.date }}_COPY_TO_SDCARD.zip
|
||||
asset_content_type: application/zip
|
||||
@@ -0,0 +1,93 @@
|
||||
name: Stable Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
|
||||
- name: Checkout
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: next
|
||||
submodules: true
|
||||
- name: Git Sumbodule Update
|
||||
run: |
|
||||
git submodule update --init --recursive
|
||||
- name: Get version
|
||||
id: version
|
||||
run: echo "::set-output name=version::$(cat .github/workflows/version.txt)"
|
||||
- name: Get past version
|
||||
id: past_version
|
||||
run: echo "::set-output name=past_version::$(cat .github/workflows/past_version.txt)"
|
||||
- name: Build the Docker image
|
||||
run: docker build -t portapack-dev -f dockerfile-nogit . --tag my-image-name:$(date +%s)
|
||||
- name: Make build folder
|
||||
run: mkdir ${{ github.workspace }}/build
|
||||
- name: Run the Docker image
|
||||
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Firmware ZIP
|
||||
run: |
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
zip -r sdcard.zip sdcard
|
||||
- name: Create changelog
|
||||
run: |
|
||||
CHANGELOG=$(python3 .github/workflows/stable_changelog.py ${{ steps.past_version.outputs.past_version }})
|
||||
CHANGELOG="${CHANGELOG//'%'/'%25'}"
|
||||
CHANGELOG="${CHANGELOG//$'\n'/'%0A'}"
|
||||
CHANGELOG="${CHANGELOG//$'\r'/'%0D'}"
|
||||
echo "::set-output name=content::$CHANGELOG"
|
||||
id: changelog
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ steps.version.outputs.version }}
|
||||
release_name: Mayhem firmware ${{ steps.version.outputs.version }}
|
||||
body: |
|
||||
**Stable release - ${{ steps.version.outputs.version }}**
|
||||
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). Please check the [readme](https://github.com/eried/portapack-mayhem/blob/master/README.md) for details.
|
||||
## Release notes
|
||||
### Revision (${{ steps.version.outputs.version }}):
|
||||
${{ steps.changelog.outputs.content }}
|
||||
|
||||
**Full Changelog**: https://github.com/eried/portapack-mayhem/compare/${{ steps.past_version.outputs.past_version }}...${{ steps.version.outputs.version }}
|
||||
|
||||
## Installation
|
||||
Check the [wiki](https://github.com/eried/portapack-havoc/wiki/Update-firmware) for details how to upgrade.
|
||||
|
||||
### 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.
|
||||
draft: true
|
||||
prerelease: false
|
||||
- name: Upload Firmware Asset
|
||||
id: upload-firmware-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./firmware.zip
|
||||
asset_name: mayhem_${{ steps.version.outputs.version }}_FIRMWARE.zip
|
||||
asset_content_type: application/zip
|
||||
- name: Upload SD Card Assets
|
||||
id: upload-sd-card-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./sdcard.zip
|
||||
asset_name: mayhem_${{ steps.version.outputs.version }}_COPY_TO_SDCARD.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
v1.5.3
|
||||
@@ -0,0 +1,30 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
past_version = sys.argv[1]
|
||||
|
||||
raw_git = os.popen('git log ' + past_version + '..next --pretty=format:"- %h - {USERNAME}*+%al-%an*: %s"').read()
|
||||
|
||||
|
||||
def compute_username(line):
|
||||
stripped = re.search(r'(?<=\*)(.*?)(?=\*)', line).group(0)
|
||||
|
||||
pattern = re.compile("[$@+&?].*[$@+&?]")
|
||||
if pattern.match(stripped):
|
||||
stripped = re.sub("[$@+&?].*[$@+&?]", "", stripped)
|
||||
stripped = re.match(r'.+?(?=-)', stripped).group(0)
|
||||
else:
|
||||
stripped = re.sub(r'^.*?-', "", stripped)
|
||||
return "@" + stripped
|
||||
|
||||
|
||||
def compile_line(line):
|
||||
username = compute_username(line)
|
||||
line = re.sub(r'[*].*[*]', "", line)
|
||||
line = line.replace("{USERNAME}", username)
|
||||
return line
|
||||
|
||||
|
||||
for row in raw_git.splitlines():
|
||||
print(compile_line(row))
|
||||
@@ -0,0 +1 @@
|
||||
v1.5.4
|
||||
@@ -27,7 +27,6 @@
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
@@ -39,7 +38,6 @@
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
/firmware/baseband/*.bin
|
||||
|
||||
+3
-3
@@ -25,8 +25,8 @@ set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex
|
||||
|
||||
project(portapack-h1)
|
||||
|
||||
#set(VERSION "")
|
||||
if (NOT DEFINED VERSION)
|
||||
set(VERSION "$ENV{VERSION_STRING}")
|
||||
if ("$ENV{VERSION_STRING}" STREQUAL "")
|
||||
execute_process(
|
||||
COMMAND git log -n 1 --format=%h
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
|
||||
@@ -38,7 +38,7 @@ if (NOT DEFINED VERSION)
|
||||
if (GIT_VERSION_FOUND)
|
||||
set(VERSION "unknown")
|
||||
else (GIT_VERSION_FOUND)
|
||||
set(VERSION "local-${GIT_VERSION}")
|
||||
set(VERSION "${GIT_VERSION}")
|
||||
endif (GIT_VERSION_FOUND)
|
||||
endif()
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# License
|
||||
Portapack Mayhem is distributed under GPL v3.0, however some sub projects might have different (GPL v3.0 compatible) licenses.
|
||||
Portapack Mayhem is distributed under [GPL v3.0](LICENSE), however some sub projects might have different (GPL v3.0 compatible) licenses.
|
||||
|
||||
## Other Licenses (compatible with GPL v3.0)
|
||||
- Most of Portapack Mayhem is distributed under GPL-2.0-or-later. [license](LICENSE.GPL-2.0-or-later)
|
||||
|
||||
@@ -6,7 +6,7 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
|
||||
|
||||
[<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_front.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_inside.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview#portapack-internals)
|
||||
|
||||
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_dZ7lA96) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
|
||||
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_9zbXMk) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
|
||||
|
||||
# Quick overview
|
||||
|
||||
@@ -20,13 +20,15 @@ This repository expands upon the previous work by many people and aims to consta
|
||||
|
||||
## Does it work on H1/H2 PortaPack?
|
||||
|
||||
Yes, both devices are the [same](https://github.com/eried/portapack-mayhem/wiki/First-steps). The one I am using to test all changes is this [PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_dZ7lA96), which is a kit that includes everything you need. Sadly, the people making the H2 never made the updated schematics available, which is not ideal (and goes against the terms of the license). Most members of the community are using a clone of the [PortaPack H1+HackRF+metal case](https://s.click.aliexpress.com/e/_dS6liw4), which does not include any battery functionality, but it is a cheaper alternative.
|
||||
Yes, both devices are the [same](https://github.com/eried/portapack-mayhem/wiki/First-steps). The one I am using to test all changes is this [PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_9zbXMk), which is a kit that includes everything you need. Sadly, the people making the H2 never made the updated schematics available, which is not ideal (and goes against the terms of the license). Most members of the community are using a clone of the [PortaPack H1+HackRF+metal case](https://s.click.aliexpress.com/e/_dS6liw4), which does not include any battery functionality, but it is a cheaper alternative.
|
||||
|
||||
To support the people behind the hardware, please buy a genuine [HackRF](https://greatscottgadgets.com/hackrf/) and [PortaPack](https://store.sharebrained.com/products/portapack-for-hackrf-one-kit).
|
||||
|
||||
## Where is the latest firmware?
|
||||
|
||||
The current stable release is on the [](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description.
|
||||
The **latest (nightly) release** can be found [here](https://github.com/eried/portapack-mayhem/releases/).
|
||||
|
||||
The current **stable release** is on the [](https://github.com/eried/portapack-mayhem/releases/latest) page. Follow the instructions you can find in the release description.
|
||||
|
||||
## Is this the newest firmware for my PortaPack?
|
||||
Most probably: **YES**. *If you find new features somewhere else, please [suggest](https://github.com/eried/portapack-mayhem/issues/new/choose) them*.
|
||||
|
||||
@@ -154,11 +154,13 @@ set(CPPSRC
|
||||
${COMMON}/ui_widget.cpp
|
||||
${COMMON}/utility.cpp
|
||||
${COMMON}/wm8731.cpp
|
||||
app_settings.cpp
|
||||
audio.cpp
|
||||
baseband_api.cpp
|
||||
capture_thread.cpp
|
||||
clock_manager.cpp
|
||||
core_control.cpp
|
||||
database.cpp
|
||||
de_bruijn.cpp
|
||||
#emu_cc1101.cpp
|
||||
rfm69.cpp
|
||||
@@ -172,6 +174,7 @@ set(CPPSRC
|
||||
irq_rtc.cpp
|
||||
log_file.cpp
|
||||
portapack.cpp
|
||||
qrcodegen.cpp
|
||||
radio.cpp
|
||||
receiver_model.cpp
|
||||
recent_entries.cpp
|
||||
@@ -206,6 +209,7 @@ set(CPPSRC
|
||||
ui/ui_channel.cpp
|
||||
ui/ui_font_fixed_8x16.cpp
|
||||
ui/ui_geomap.cpp
|
||||
ui/ui_qrcode.cpp
|
||||
ui/ui_menu.cpp
|
||||
ui/ui_btngrid.cpp
|
||||
ui/ui_receiver.cpp
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2022 Arjan Onwezen
|
||||
*
|
||||
* 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 "app_settings.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
namespace std {
|
||||
|
||||
int app_settings::load(std::string application, AppSettings* settings) {
|
||||
|
||||
if (portapack::persistent_memory::load_app_settings()) {
|
||||
file_path = folder+"/"+application+".ini";
|
||||
|
||||
auto error = settings_file.open(file_path);
|
||||
if (!error.is_valid()) {
|
||||
auto error = settings_file.read(file_content, std::min((int)settings_file.size(), MAX_FILE_CONTENT_SIZE));
|
||||
|
||||
settings->baseband_bandwidth=std::app_settings::read_long_long(file_content, "baseband_bandwidth=");
|
||||
settings->channel_bandwidth=std::app_settings::read_long_long(file_content, "channel_bandwidth=");
|
||||
settings->lna=std::app_settings::read_long_long(file_content, "lna=");
|
||||
settings->modulation=std::app_settings::read_long_long(file_content, "modulation=");
|
||||
settings->rx_amp=std::app_settings::read_long_long(file_content, "rx_amp=");
|
||||
settings->rx_frequency=std::app_settings::read_long_long(file_content, "rx_frequency=");
|
||||
settings->sampling_rate=std::app_settings::read_long_long(file_content, "sampling_rate=");
|
||||
settings->vga=std::app_settings::read_long_long(file_content, "vga=");
|
||||
settings->tx_amp=std::app_settings::read_long_long(file_content, "tx_amp=");
|
||||
settings->tx_frequency=std::app_settings::read_long_long(file_content, "tx_frequency=");
|
||||
settings->tx_gain=std::app_settings::read_long_long(file_content, "tx_gain=");
|
||||
rc = SETTINGS_OK;
|
||||
}
|
||||
else rc = SETTINGS_UNABLE_TO_LOAD;
|
||||
}
|
||||
else rc = SETTINGS_DISABLED;
|
||||
return(rc);
|
||||
}
|
||||
|
||||
int app_settings::save(std::string application, AppSettings* settings) {
|
||||
|
||||
if (portapack::persistent_memory::save_app_settings()) {
|
||||
file_path = folder+"/"+application+".ini";
|
||||
make_new_directory(folder);
|
||||
|
||||
auto error = settings_file.create(file_path);
|
||||
if (!error.is_valid()) {
|
||||
// Save common setting
|
||||
settings_file.write_line("baseband_bandwidth="+to_string_dec_uint(portapack::receiver_model.baseband_bandwidth()));
|
||||
settings_file.write_line("channel_bandwidth="+to_string_dec_uint(portapack::transmitter_model.channel_bandwidth()));
|
||||
settings_file.write_line("lna="+to_string_dec_uint(portapack::receiver_model.lna()));
|
||||
settings_file.write_line("rx_amp="+to_string_dec_uint(portapack::receiver_model.rf_amp()));
|
||||
settings_file.write_line("sampling_rate="+to_string_dec_uint(portapack::receiver_model.sampling_rate()));
|
||||
settings_file.write_line("tx_amp="+to_string_dec_uint(portapack::transmitter_model.rf_amp()));
|
||||
settings_file.write_line("tx_gain="+to_string_dec_uint(portapack::transmitter_model.tx_gain()));
|
||||
settings_file.write_line("vga="+to_string_dec_uint(portapack::receiver_model.vga()));
|
||||
// Save other settings from struct
|
||||
settings_file.write_line("rx_frequency="+to_string_dec_uint(settings->rx_frequency));
|
||||
settings_file.write_line("tx_frequency="+to_string_dec_uint(settings->tx_frequency));
|
||||
|
||||
rc = SETTINGS_OK;
|
||||
}
|
||||
else rc = SETTINGS_UNABLE_TO_SAVE;
|
||||
}
|
||||
else rc = SETTINGS_DISABLED;
|
||||
return(rc);
|
||||
}
|
||||
|
||||
|
||||
long long int app_settings::read_long_long(char* file_content, const char* setting_text) {
|
||||
auto position = strstr(file_content, (char *)setting_text);
|
||||
if (position) {
|
||||
position += strlen((char *)setting_text);
|
||||
setting_value = strtoll(position, nullptr, 10);
|
||||
}
|
||||
return(setting_value);
|
||||
}
|
||||
|
||||
|
||||
} /* namespace std */
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2022 Arjan Onwezen
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __APP_SETTINGS_H__
|
||||
#define __APP_SETTINGS_H__
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "file.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
|
||||
namespace std {
|
||||
class app_settings {
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
#define SETTINGS_OK 0 // settings found
|
||||
#define SETTINGS_UNABLE_TO_LOAD -1 // settings (file) not found
|
||||
#define SETTINGS_UNABLE_TO_SAVE -2 // unable to save settings
|
||||
#define SETTINGS_DISABLED -3 // load/save settings disabled in settings
|
||||
|
||||
|
||||
|
||||
struct AppSettings {
|
||||
uint32_t baseband_bandwidth;
|
||||
uint32_t channel_bandwidth;
|
||||
uint8_t lna;
|
||||
uint8_t modulation;
|
||||
uint8_t rx_amp;
|
||||
uint32_t rx_frequency;
|
||||
uint32_t sampling_rate;
|
||||
uint8_t tx_amp;
|
||||
uint32_t tx_frequency;
|
||||
uint8_t tx_gain;
|
||||
uint8_t vga;
|
||||
};
|
||||
|
||||
int load(std::string application, AppSettings* settings);
|
||||
int save(std::string application, AppSettings* settings);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
#define MAX_FILE_CONTENT_SIZE 1000
|
||||
|
||||
char file_content[MAX_FILE_CONTENT_SIZE] = {};
|
||||
std::string file_path = "";
|
||||
std::string folder = "SETTINGS";
|
||||
int rc = SETTINGS_OK;
|
||||
File settings_file { };
|
||||
long long int setting_value {} ;
|
||||
|
||||
long long int read_long_long(char* file_content, const char* setting_text);
|
||||
|
||||
|
||||
}; // class app_settings
|
||||
} // namespace std
|
||||
|
||||
|
||||
|
||||
#endif/*__APP_SETTINGS_H__*/
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "ais_app.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "database.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
@@ -59,7 +60,26 @@ static float latlon_float(const int32_t normalized) {
|
||||
static std::string mmsi(
|
||||
const ais::MMSI& mmsi
|
||||
) {
|
||||
return to_string_dec_uint(mmsi, 9);
|
||||
return to_string_dec_uint(mmsi, 9, '0'); // MMSI is always is always 9 characters pre-padded with zeros
|
||||
}
|
||||
|
||||
|
||||
static std::string mid(
|
||||
const ais::MMSI& mmsi
|
||||
) {
|
||||
std::database db;
|
||||
std::string mid_code = "";
|
||||
std::database::MidDBRecord mid_record = {};
|
||||
int return_code = 0;
|
||||
|
||||
// Try getting the country name from mids.db using MID code for given MMSI
|
||||
mid_code = to_string_dec_uint(mmsi, 9, ' ').substr(0, 3);
|
||||
return_code = db.retrieve_mid_record(&mid_record, mid_code);
|
||||
switch(return_code) {
|
||||
case DATABASE_RECORD_FOUND: return mid_record.country;
|
||||
case DATABASE_NOT_FOUND: return "No mids.db file";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string navigational_status(const unsigned int value) {
|
||||
@@ -234,13 +254,23 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
AISRecentEntryDetailView::AISRecentEntryDetailView(const AISRecentEntryDetailView&Entry) : View()
|
||||
{
|
||||
(void)Entry;
|
||||
}
|
||||
|
||||
AISRecentEntryDetailView & AISRecentEntryDetailView::operator=(const AISRecentEntryDetailView&Entry)
|
||||
{
|
||||
(void)Entry;
|
||||
return *this;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
void AISRecentEntryDetailView::focus() {
|
||||
@@ -271,6 +301,7 @@ void AISRecentEntryDetailView::paint(Painter& painter) {
|
||||
auto field_rect = Rect { rect.left(), rect.top() + 16, rect.width(), 16 };
|
||||
|
||||
field_rect = draw_field(painter, field_rect, s, "MMSI", ais::format::mmsi(entry_.mmsi));
|
||||
field_rect = draw_field(painter, field_rect, s, "Ctry", ais::format::mid(entry_.mmsi));
|
||||
field_rect = draw_field(painter, field_rect, s, "Name", entry_.name);
|
||||
field_rect = draw_field(painter, field_rect, s, "Call", entry_.call_sign);
|
||||
field_rect = draw_field(painter, field_rect, s, "Dest", entry_.destination);
|
||||
@@ -304,20 +335,24 @@ AISAppView::AISAppView(NavigationView& nav) : nav_ { nav } {
|
||||
&recent_entry_detail_view,
|
||||
});
|
||||
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_ais", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
target_frequency_ = app_settings.rx_frequency;
|
||||
}
|
||||
else target_frequency_ = initial_target_frequency;
|
||||
|
||||
recent_entry_detail_view.hidden(true);
|
||||
|
||||
target_frequency_ = initial_target_frequency;
|
||||
|
||||
radio::enable({
|
||||
tuning_frequency(),
|
||||
sampling_rate,
|
||||
baseband_bandwidth,
|
||||
rf::Direction::Receive,
|
||||
receiver_model.rf_amp(),
|
||||
static_cast<int8_t>(receiver_model.lna()),
|
||||
static_cast<int8_t>(receiver_model.vga()),
|
||||
});
|
||||
|
||||
receiver_model.set_tuning_frequency(tuning_frequency());
|
||||
receiver_model.set_sampling_rate(sampling_rate);
|
||||
receiver_model.set_baseband_bandwidth(baseband_bandwidth);
|
||||
receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias.
|
||||
|
||||
options_channel.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->on_frequency_changed(v);
|
||||
};
|
||||
@@ -337,7 +372,12 @@ AISAppView::AISAppView(NavigationView& nav) : nav_ { nav } {
|
||||
}
|
||||
|
||||
AISAppView::~AISAppView() {
|
||||
radio::disable();
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = target_frequency_;
|
||||
settings.save("rx_ais", &app_settings);
|
||||
|
||||
receiver_model.disable(); // to switch off all, including DC bias.
|
||||
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "log_file.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "ais_packet.hpp"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
@@ -127,15 +127,18 @@ public:
|
||||
void focus() override;
|
||||
void paint(Painter&) override;
|
||||
|
||||
AISRecentEntryDetailView(const AISRecentEntryDetailView&Entry);
|
||||
AISRecentEntryDetailView &operator=(const AISRecentEntryDetailView&Entry);
|
||||
|
||||
private:
|
||||
AISRecentEntry entry_ { };
|
||||
|
||||
Button button_done {
|
||||
{ 125, 216, 96, 24 },
|
||||
{ 125, 224, 96, 24 },
|
||||
"Done"
|
||||
};
|
||||
Button button_see_map {
|
||||
{ 19, 216, 96, 24 },
|
||||
{ 19, 224, 96, 24 },
|
||||
"See on map"
|
||||
};
|
||||
GeoMapView* geomap_view { nullptr };
|
||||
@@ -163,12 +166,18 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "AIS"; };
|
||||
std::string title() const override { return "AIS Boats RX"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 162025000;
|
||||
static constexpr uint32_t sampling_rate = 2457600;
|
||||
static constexpr uint32_t baseband_bandwidth = 1750000;
|
||||
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
NavigationView& nav_;
|
||||
|
||||
AISRecentEntries recent { };
|
||||
|
||||
@@ -129,7 +129,19 @@ AnalogAudioView::AnalogAudioView(
|
||||
&waterfall
|
||||
});
|
||||
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_audio", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
receiver_model.set_rf_amp(app_settings.rx_amp);
|
||||
field_frequency.set_value(app_settings.rx_frequency);
|
||||
}
|
||||
else field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
|
||||
//Filename Datetime and Frequency
|
||||
record_view.set_filename_date_frequency(true);
|
||||
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
@@ -157,6 +169,7 @@ AnalogAudioView::AnalogAudioView(
|
||||
|
||||
const auto modulation = receiver_model.modulation();
|
||||
options_modulation.set_by_value(toUType(modulation));
|
||||
|
||||
options_modulation.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v));
|
||||
};
|
||||
@@ -180,7 +193,7 @@ AnalogAudioView::AnalogAudioView(
|
||||
audio::output::start();
|
||||
|
||||
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
|
||||
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation));
|
||||
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation));
|
||||
}
|
||||
|
||||
size_t AnalogAudioView::get_spec_bw_index() {
|
||||
@@ -207,6 +220,11 @@ void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
|
||||
}
|
||||
|
||||
AnalogAudioView::~AnalogAudioView() {
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_audio", &app_settings);
|
||||
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
// both?
|
||||
audio::output::stop();
|
||||
@@ -394,9 +412,6 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
|
||||
}
|
||||
}
|
||||
|
||||
/*void AnalogAudioView::squelched() {
|
||||
if (exit_on_squelch) nav_.pop();
|
||||
}*/
|
||||
|
||||
void AnalogAudioView::handle_coded_squelch(const uint32_t value) {
|
||||
float diff, min_diff = value;
|
||||
|
||||
@@ -28,11 +28,11 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "tone_key.hpp"
|
||||
|
||||
|
||||
namespace ui {
|
||||
|
||||
constexpr Style style_options_group {
|
||||
@@ -143,7 +143,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Analog audio"; };
|
||||
std::string title() const override { return "Audio RX"; };
|
||||
|
||||
size_t get_spec_bw_index();
|
||||
void set_spec_bw(size_t index, uint32_t bw);
|
||||
@@ -154,6 +154,10 @@ public:
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 };
|
||||
const Rect nbfm_view_rect { 0 * 8, 1 * 16, 18 * 8, 1 * 16 };
|
||||
|
||||
@@ -216,7 +220,10 @@ private:
|
||||
|
||||
RecordView record_view {
|
||||
{ 0 * 8, 2 * 16, 30 * 8, 1 * 16 },
|
||||
u"AUD_????", RecordView::FileType::WAV, 4096, 4
|
||||
u"AUD",
|
||||
RecordView::FileType::WAV,
|
||||
4096,
|
||||
4
|
||||
};
|
||||
|
||||
spectrum::WaterfallWidget waterfall { true };
|
||||
|
||||
@@ -57,7 +57,18 @@ AnalogTvView::AnalogTvView(
|
||||
&tv
|
||||
});
|
||||
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_tv", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
receiver_model.set_rf_amp(app_settings.rx_amp);
|
||||
field_frequency.set_value(app_settings.rx_frequency);
|
||||
}
|
||||
else field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
|
||||
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
@@ -106,12 +117,15 @@ AnalogTvView::AnalogTvView(
|
||||
}
|
||||
|
||||
AnalogTvView::~AnalogTvView() {
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_tv", &app_settings);
|
||||
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
// both?
|
||||
audio::output::stop();
|
||||
|
||||
receiver_model.disable();
|
||||
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_tv.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "tone_key.hpp"
|
||||
@@ -53,11 +53,15 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Analog TV"; };
|
||||
std::string title() const override { return "Analog TV RX"; };
|
||||
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 };
|
||||
const Rect nbfm_view_rect { 0 * 8, 1 * 16, 18 * 8, 1 * 16 };
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
|
||||
option_bandwidth.on_change = [this](size_t, uint32_t base_rate) {
|
||||
sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side
|
||||
/* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
|
||||
/* ex. sampling_rate values, 4Mhz, when recording 500 khz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/
|
||||
/* ex. recording 500khz BW to .C16 file, base_rate clock 500khz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
|
||||
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/
|
||||
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
|
||||
|
||||
waterfall.on_hide();
|
||||
record_view.set_sampling_rate(sampling_rate);
|
||||
@@ -85,12 +85,12 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
|
||||
|
||||
switch(sampling_rate) { // we use the var fs (sampling_rate) , to set up BPF aprox < fs_max/2 by Nyquist theorem.
|
||||
|
||||
case 0 ... 2000000: // BW Captured range (0 <= 250Khz max ) fs = 8 x 250 Khz
|
||||
case 0 ... 2000000: // BW Captured range (0 <= 250kHz max ) fs = 8 x 250 kHz
|
||||
anti_alias_baseband_bandwidth_filter = 1750000; // Minimum BPF MAX2837 for all those lower BW options.
|
||||
break;
|
||||
|
||||
case 4000000 ... 6000000: // BW capture range (500k ... 750Khz max ) fs_max = 8 x 750Khz = 6Mhz
|
||||
// BW 500k ... 750khz , ex. 500khz (fs = 8*BW = 4Mhz) , BW 600Khz (fs = 4,8Mhz) , BW 750 Khz (fs = 6Mhz)
|
||||
case 4000000 ... 6000000: // BW capture range (500k ... 750kHz max ) fs_max = 8 x 750kHz = 6Mhz
|
||||
// BW 500k ... 750kHz , ex. 500kHz (fs = 8*BW = 4Mhz) , BW 600kHz (fs = 4,8Mhz) , BW 750 kHz (fs = 6Mhz)
|
||||
anti_alias_baseband_bandwidth_filter = 2500000; // in some IC MAX2837 appear 2250000 , but both works similar.
|
||||
break;
|
||||
|
||||
@@ -123,7 +123,7 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
|
||||
waterfall.on_show();
|
||||
};
|
||||
|
||||
option_bandwidth.set_selected_index(7); // 500k, Preselected starting default option 500khz
|
||||
option_bandwidth.set_selected_index(7); // 500k, Preselected starting default option 500kHz
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::Capture);
|
||||
receiver_model.enable();
|
||||
|
||||
@@ -105,6 +105,15 @@ ERTAppView::ERTAppView(NavigationView&) {
|
||||
&recent_entries_view,
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_ert", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
|
||||
|
||||
radio::enable({
|
||||
initial_target_frequency,
|
||||
sampling_rate,
|
||||
@@ -122,6 +131,10 @@ ERTAppView::ERTAppView(NavigationView&) {
|
||||
}
|
||||
|
||||
ERTAppView::~ERTAppView() {
|
||||
|
||||
// save app settings
|
||||
settings.save("rx_ert", &app_settings);
|
||||
|
||||
radio::disable();
|
||||
|
||||
baseband::shutdown();
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "ui_channel.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "log_file.hpp"
|
||||
|
||||
#include "ert_packet.hpp"
|
||||
@@ -125,12 +125,17 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "ERT"; };
|
||||
std::string title() const override { return "ERT Meter RX"; };
|
||||
|
||||
private:
|
||||
ERTRecentEntries recent { };
|
||||
std::unique_ptr<ERTLogger> logger { };
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
|
||||
const RecentEntriesColumns columns { {
|
||||
{ "ID", 10 },
|
||||
{ "Tp", 2 },
|
||||
|
||||
@@ -140,13 +140,24 @@ void GpsSimAppView::start() {
|
||||
}
|
||||
);
|
||||
}
|
||||
field_rfgain.on_change = [this](int32_t v) {
|
||||
tx_gain = v;
|
||||
};
|
||||
field_rfgain.set_value(tx_gain);
|
||||
receiver_model.set_tx_gain(tx_gain);
|
||||
|
||||
|
||||
field_rfamp.on_change = [this](int32_t v) {
|
||||
rf_amp = (bool)v;
|
||||
};
|
||||
field_rfamp.set_value(rf_amp ? 14 : 0);
|
||||
|
||||
radio::enable({
|
||||
receiver_model.tuning_frequency(),
|
||||
sample_rate ,
|
||||
baseband_bandwidth,
|
||||
rf::Direction::Transmit,
|
||||
receiver_model.rf_amp(),
|
||||
rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps
|
||||
static_cast<int8_t>(receiver_model.lna()),
|
||||
static_cast<int8_t>(receiver_model.vga())
|
||||
});
|
||||
@@ -181,6 +192,9 @@ GpsSimAppView::GpsSimAppView(
|
||||
NavigationView& nav
|
||||
) : nav_ (nav)
|
||||
{
|
||||
tx_gain = 35;field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max 47dBs ).
|
||||
field_rfamp.set_value(rf_amp ? 14 : 0); // Initial default value True. (TX RF amp on , +14dB's)
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_gps);
|
||||
|
||||
add_children({
|
||||
@@ -191,8 +205,8 @@ GpsSimAppView::GpsSimAppView(
|
||||
&text_duration,
|
||||
&progressbar,
|
||||
&field_frequency,
|
||||
&field_lna,
|
||||
&field_rf_amp,
|
||||
&field_rfgain,
|
||||
&field_rfamp, // let's not use common persistent rf_amp , local rfamp is enough
|
||||
&check_loop,
|
||||
&button_play,
|
||||
&waterfall,
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "GPS Simulator"; };
|
||||
std::string title() const override { return "GPS Sim TX"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
@@ -52,6 +52,8 @@ private:
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
uint32_t sample_rate = 0;
|
||||
int32_t tx_gain { 47 };
|
||||
bool rf_amp { true }; // aux private var to store temporal, same as Replay App rf_amp user selection.
|
||||
static constexpr uint32_t baseband_bandwidth = 3000000; //filter bandwidth
|
||||
const size_t read_size { 16384 };
|
||||
const size_t buffer_count { 3 };
|
||||
@@ -76,7 +78,7 @@ private:
|
||||
bool ready_signal { false };
|
||||
|
||||
Labels labels {
|
||||
{ { 10 * 8, 2 * 16 }, "LNA: A:", Color::light_grey() }
|
||||
{ { 10 * 8, 2 * 16 }, "GAIN A:", Color::light_grey() }
|
||||
};
|
||||
|
||||
Button button_open {
|
||||
@@ -104,11 +106,19 @@ private:
|
||||
FrequencyField field_frequency {
|
||||
{ 0 * 8, 2 * 16 },
|
||||
};
|
||||
LNAGainField field_lna {
|
||||
{ 14 * 8, 2 * 16 }
|
||||
NumberField field_rfgain {
|
||||
{ 14 * 8, 2 * 16 },
|
||||
2,
|
||||
{ 0, 47 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
RFAmpField field_rf_amp {
|
||||
{ 19 * 8, 2 * 16 }
|
||||
NumberField field_rfamp { // previously we were using "RFAmpField field_rf_amp" but that is general Receiver amp setting.
|
||||
{ 19 * 8, 2 * 16 },
|
||||
2,
|
||||
{ 0, 14 }, // this time we will display GUI , 0 or 14 dBs same as Mic and Replay App
|
||||
14,
|
||||
' '
|
||||
};
|
||||
Checkbox check_loop {
|
||||
{ 21 * 8, 2 * 16 },
|
||||
|
||||
@@ -39,10 +39,14 @@ using namespace portapack;
|
||||
namespace ui {
|
||||
|
||||
void LGEView::focus() {
|
||||
options_trame.focus();
|
||||
options_frame.focus();
|
||||
}
|
||||
|
||||
LGEView::~LGEView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_lge", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -70,10 +74,10 @@ void LGEView::generate_frame_touche() {
|
||||
std::vector<uint8_t> data { 0x46, 0x28, 0x01, 0x45, 0x27, 0x01, 0x44, 0x23 };
|
||||
|
||||
console.write("\n\x1B\x07Touche:\x1B\x10");
|
||||
generate_lge_frame(0x96, (field_joueur.value() << 8) | field_salle.value(), 0x0001, data);
|
||||
generate_lge_frame(0x96, (field_player.value() << 8) | field_room.value(), 0x0001, data);
|
||||
}
|
||||
|
||||
void LGEView::generate_frame_pseudo() {
|
||||
void LGEView::generate_frame_nickname() {
|
||||
// 0040.48s:
|
||||
// 30 02 1A 00 19 00 FF 00 02 19 42 52 45 42 49 53 20 00 00 00 00 00 00 00 00 00
|
||||
// 04 01 B0 04 7F 1F 11 33 40 1F 22 01 07 00 00 01 07 00 00 63 05 00 00 99 A2
|
||||
@@ -90,15 +94,15 @@ void LGEView::generate_frame_pseudo() {
|
||||
};
|
||||
uint32_t c;
|
||||
|
||||
//data_header[2] = field_salle.value(); // ?
|
||||
//data_footer[0] = field_salle.value(); // ?
|
||||
//data_header[2] = field_room.value(); // ?
|
||||
//data_footer[0] = field_room.value(); // ?
|
||||
|
||||
data.insert(data.begin(), data_header.begin(), data_header.end());
|
||||
|
||||
data.push_back(field_joueur.value());
|
||||
data.push_back(field_player.value());
|
||||
|
||||
c = 0;
|
||||
for (auto &ch : pseudo) {
|
||||
for (auto &ch : nickname) {
|
||||
data.push_back(ch);
|
||||
c++;
|
||||
}
|
||||
@@ -108,16 +112,16 @@ void LGEView::generate_frame_pseudo() {
|
||||
while (++c < 16)
|
||||
data.push_back(0x00);
|
||||
|
||||
data.push_back(field_equipe.value());
|
||||
data.push_back(field_team.value());
|
||||
|
||||
data.insert(data.end(), data_footer.begin(), data_footer.end());
|
||||
|
||||
console.write("\n\x1B\x0ESet pseudo:\x1B\x10");
|
||||
console.write("\n\x1B\x0ESet nickname:\x1B\x10");
|
||||
|
||||
generate_lge_frame(0x02, 0x001A, field_joueur.value(), data);
|
||||
generate_lge_frame(0x02, 0x001A, field_player.value(), data);
|
||||
}
|
||||
|
||||
void LGEView::generate_frame_equipe() {
|
||||
void LGEView::generate_frame_team() {
|
||||
// 0041.83s:
|
||||
// 3D 03 FF FF FF FF 02 03 01 52 4F 55 47 45 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
// 02 56 45 52 54 45 00 00 00 00 00 00 00 00 00 00 00 01 03 42 4C 45 55 45 00 00
|
||||
@@ -129,10 +133,10 @@ void LGEView::generate_frame_equipe() {
|
||||
|
||||
data.insert(data.begin(), data_header.begin(), data_header.end());
|
||||
|
||||
data.push_back(field_equipe.value());
|
||||
data.push_back(field_team.value());
|
||||
|
||||
c = 0;
|
||||
for (auto &ch : pseudo) {
|
||||
for (auto &ch : nickname) {
|
||||
data.push_back(ch);
|
||||
c++;
|
||||
}
|
||||
@@ -140,14 +144,14 @@ void LGEView::generate_frame_equipe() {
|
||||
while (c++ < 16)
|
||||
data.push_back(0x00);
|
||||
|
||||
data.push_back(field_equipe.value() - 1); // Color ?
|
||||
data.push_back(field_team.value() - 1); // Color ?
|
||||
|
||||
console.write("\n\x1B\x0ASet equipe:\x1B\x10");
|
||||
console.write("\n\x1B\x0ASet team:\x1B\x10");
|
||||
|
||||
generate_lge_frame(0x03, data);
|
||||
}
|
||||
|
||||
void LGEView::generate_frame_broadcast_pseudo() {
|
||||
void LGEView::generate_frame_broadcast_nickname() {
|
||||
// 0043.86s:
|
||||
// 3D 04 FF FF FF FF 02 03 19 42 52 45 42 49 53 20 00 00 00 00 00 00 00 00 00 04
|
||||
// 07 50 4F 4E 45 59 20 00 00 00 00 00 00 00 00 00 00 05 1B 41 42 42 59 20 00 00
|
||||
@@ -159,10 +163,10 @@ void LGEView::generate_frame_broadcast_pseudo() {
|
||||
|
||||
data.insert(data.begin(), data_header.begin(), data_header.end());
|
||||
|
||||
data.push_back(field_joueur.value());
|
||||
data.push_back(field_player.value());
|
||||
|
||||
c = 0;
|
||||
for (auto &ch : pseudo) {
|
||||
for (auto &ch : nickname) {
|
||||
data.push_back(ch);
|
||||
c++;
|
||||
}
|
||||
@@ -172,9 +176,9 @@ void LGEView::generate_frame_broadcast_pseudo() {
|
||||
while (++c < 16)
|
||||
data.push_back(0x00);
|
||||
|
||||
data.push_back(field_equipe.value());
|
||||
data.push_back(field_team.value());
|
||||
|
||||
console.write("\n\x1B\x09" "Broadcast pseudo:\x1B\x10");
|
||||
console.write("\n\x1B\x09" "Broadcast nickname:\x1B\x10");
|
||||
|
||||
generate_lge_frame(0x04, data);
|
||||
}
|
||||
@@ -184,14 +188,14 @@ void LGEView::generate_frame_start() {
|
||||
// 0A 05 FF FF FF FF 02 EC FF FF FF A3 35
|
||||
std::vector<uint8_t> data { 0x02, 0xEC, 0xFF, 0xFF, 0xFF };
|
||||
|
||||
//data[0] = field_salle.value(); // ?
|
||||
//data[0] = field_room.value(); // ?
|
||||
|
||||
console.write("\n\x1B\x0DStart:\x1B\x10");
|
||||
generate_lge_frame(0x05, data);
|
||||
}
|
||||
|
||||
void LGEView::generate_frame_gameover() {
|
||||
std::vector<uint8_t> data { (uint8_t)field_salle.value() };
|
||||
std::vector<uint8_t> data { (uint8_t)field_room.value() };
|
||||
|
||||
console.write("\n\x1B\x0CGameover:\x1B\x10");
|
||||
generate_lge_frame(0x0D, data);
|
||||
@@ -203,7 +207,7 @@ void LGEView::generate_frame_collier() {
|
||||
// Custom
|
||||
// 0C 00 13 37 13 37 id flags channel playerid zapduty zaptime checksum CRC CRC
|
||||
// channel: field_channel
|
||||
// playerid: field_joueur
|
||||
// playerid: field_player
|
||||
// zapduty: field_power
|
||||
// zaptime: field_duration
|
||||
|
||||
@@ -218,8 +222,8 @@ void LGEView::generate_frame_collier() {
|
||||
std::vector<uint8_t> data {
|
||||
id,
|
||||
flags,
|
||||
(uint8_t)field_salle.value(),
|
||||
(uint8_t)field_joueur.value(),
|
||||
(uint8_t)field_room.value(),
|
||||
(uint8_t)field_player.value(),
|
||||
(uint8_t)field_power.value(),
|
||||
(uint8_t)(field_duration.value() * 10)
|
||||
};
|
||||
@@ -285,11 +289,11 @@ LGEView::LGEView(NavigationView& nav) {
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&options_trame,
|
||||
&field_salle,
|
||||
&button_texte,
|
||||
&field_equipe,
|
||||
&field_joueur,
|
||||
&options_frame,
|
||||
&field_room,
|
||||
&button_text,
|
||||
&field_team,
|
||||
&field_player,
|
||||
&field_id,
|
||||
&field_power,
|
||||
&field_duration,
|
||||
@@ -300,20 +304,29 @@ LGEView::LGEView(NavigationView& nav) {
|
||||
&tx_view
|
||||
});
|
||||
|
||||
field_salle.set_value(1);
|
||||
field_equipe.set_value(1);
|
||||
field_joueur.set_value(1);
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_lge", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
field_room.set_value(1);
|
||||
field_team.set_value(1);
|
||||
field_player.set_value(1);
|
||||
field_id.set_value(1);
|
||||
field_power.set_value(1);
|
||||
field_duration.set_value(2);
|
||||
|
||||
button_texte.on_select = [this, &nav](Button&) {
|
||||
button_text.on_select = [this, &nav](Button&) {
|
||||
text_prompt(
|
||||
nav,
|
||||
pseudo,
|
||||
nickname,
|
||||
15,
|
||||
[this](std::string& buffer) {
|
||||
button_texte.set_text(buffer);
|
||||
button_text.set_text(buffer);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -326,15 +339,15 @@ LGEView::LGEView(NavigationView& nav) {
|
||||
|
||||
tx_view.on_start = [this]() {
|
||||
if (tx_mode == IDLE) {
|
||||
auto i = options_trame.selected_index_value();
|
||||
auto i = options_frame.selected_index_value();
|
||||
if (i == 0)
|
||||
generate_frame_touche();
|
||||
else if (i == 1)
|
||||
generate_frame_pseudo();
|
||||
generate_frame_nickname();
|
||||
else if (i == 2)
|
||||
generate_frame_equipe();
|
||||
generate_frame_team();
|
||||
else if (i == 3)
|
||||
generate_frame_broadcast_pseudo();
|
||||
generate_frame_broadcast_nickname();
|
||||
else if (i == 4)
|
||||
generate_frame_start();
|
||||
else if (i == 5)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "app_settings.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -40,7 +41,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "LGE tool"; };
|
||||
std::string title() const override { return "LGE tool TX"; };
|
||||
|
||||
private:
|
||||
enum tx_modes {
|
||||
@@ -48,15 +49,19 @@ private:
|
||||
SINGLE,
|
||||
ALL
|
||||
};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
RFM69 rfm69 { 5, 0x2DD4, true, true };
|
||||
RFM69 rfm69 { 5, 0x2DD4, true, true };
|
||||
|
||||
uint32_t frame_size { 0 };
|
||||
uint32_t repeats { 0 };
|
||||
uint32_t channel_index { 0 };
|
||||
std::string pseudo { "ABCDEF" };
|
||||
std::string nickname { "ABCDEF" };
|
||||
|
||||
rf::Frequency channels[3] = { 868067000, 868183000, 868295000 };
|
||||
|
||||
@@ -68,9 +73,9 @@ private:
|
||||
}
|
||||
void generate_lge_frame(const uint8_t command, const uint16_t address_a, const uint16_t address_b, std::vector<uint8_t>& data);
|
||||
void generate_frame_touche();
|
||||
void generate_frame_pseudo();
|
||||
void generate_frame_equipe();
|
||||
void generate_frame_broadcast_pseudo();
|
||||
void generate_frame_nickname();
|
||||
void generate_frame_team();
|
||||
void generate_frame_broadcast_nickname();
|
||||
void generate_frame_start();
|
||||
void generate_frame_gameover();
|
||||
void generate_frame_collier();
|
||||
@@ -79,28 +84,28 @@ private:
|
||||
|
||||
Labels labels {
|
||||
//{ { 7 * 8, 1 * 8 }, "NO FUN ALLOWED !", Color::red() },
|
||||
{ { 1 * 8, 1 * 8 }, "Trame:", Color::light_grey() },
|
||||
{ { 1 * 8, 3 * 8 }, "Salle:", Color::light_grey() },
|
||||
{ { 14 * 8, 3 * 8 }, "Texte:", Color::light_grey() },
|
||||
{ { 0 * 8, 5 * 8 }, "Equipe:", Color::light_grey() },
|
||||
{ { 0 * 8, 7 * 8 }, "Joueur:", Color::light_grey() },
|
||||
{ { 0 * 8, 10 * 8 }, "Collier:", Color::light_grey() },
|
||||
{ { 1 * 8, 1 * 8 }, "Frame:", Color::light_grey() },
|
||||
{ { 2 * 8, 3 * 8 }, "Room:", Color::light_grey() },
|
||||
{ { 14 * 8, 3 * 8 }, "Text:", Color::light_grey() },
|
||||
{ { 2 * 8, 5 * 8 }, "Team:", Color::light_grey() },
|
||||
{ { 0 * 8, 7 * 8 }, "Player:", Color::light_grey() },
|
||||
{ { 0 * 8, 10 * 8 }, "Vest:", Color::light_grey() },
|
||||
{ { 4 * 8, 12 * 8 }, "ID:", Color::light_grey() },
|
||||
{ { 3 * 8, 14 * 8 }, "Pow: /10", Color::light_grey() },
|
||||
{ { 1 * 8, 16 * 8 }, "Duree: x100ms", Color::light_grey() }
|
||||
{ { 2 * 8, 16 * 8 }, "Time: x100ms", Color::light_grey() }
|
||||
};
|
||||
|
||||
OptionsField options_trame {
|
||||
OptionsField options_frame {
|
||||
{ 7 * 8, 1 * 8 },
|
||||
13,
|
||||
{
|
||||
{ "Touche", 0 },
|
||||
{ "Set pseudo", 1 },
|
||||
{ "Set equipe", 2 },
|
||||
{ "Brdcst pseudo", 3 },
|
||||
{ "Key", 0 },
|
||||
{ "Set nickname", 1 },
|
||||
{ "Set team", 2 },
|
||||
{ "Brdcst nick", 3 },
|
||||
{ "Start", 4 },
|
||||
{ "Game over", 5 },
|
||||
{ "Set collier", 6 }
|
||||
{ "Set vest", 6 }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -111,7 +116,7 @@ private:
|
||||
true
|
||||
};
|
||||
|
||||
NumberField field_salle {
|
||||
NumberField field_room {
|
||||
{ 7 * 8, 3 * 8 },
|
||||
1,
|
||||
{ 1, 2 },
|
||||
@@ -119,12 +124,12 @@ private:
|
||||
'0'
|
||||
};
|
||||
|
||||
Button button_texte {
|
||||
Button button_text {
|
||||
{ 14 * 8, 5 * 8, 16 * 8, 3 * 8 },
|
||||
"ABCDEF"
|
||||
};
|
||||
|
||||
NumberField field_equipe {
|
||||
NumberField field_team {
|
||||
{ 7 * 8, 5 * 8 },
|
||||
1,
|
||||
{ 1, 6 },
|
||||
@@ -132,7 +137,7 @@ private:
|
||||
'0'
|
||||
};
|
||||
|
||||
NumberField field_joueur {
|
||||
NumberField field_player {
|
||||
{ 7 * 8, 7 * 8 },
|
||||
2,
|
||||
{ 1, 50 },
|
||||
|
||||
@@ -77,11 +77,22 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
&console
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_pocsag", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
field_frequency.set_value(app_settings.rx_frequency);
|
||||
}
|
||||
else field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
||||
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.enable();
|
||||
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
update_freq(f);
|
||||
@@ -127,6 +138,11 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
}
|
||||
|
||||
POCSAGAppView::~POCSAGAppView() {
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_pocsag", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
|
||||
// Save ignored address
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "ui_rssi.hpp"
|
||||
|
||||
#include "log_file.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "pocsag.hpp"
|
||||
#include "pocsag_packet.hpp"
|
||||
|
||||
@@ -60,6 +60,10 @@ public:
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 466175000;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
bool logging { true };
|
||||
bool ignore { true };
|
||||
uint32_t last_address = 0xFFFFFFFF;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyleft (ↄ) 2022 NotPike
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -121,6 +122,7 @@ void ReplayAppView::start() {
|
||||
auto open_error = p->open(file_path);
|
||||
if( open_error.is_valid() ) {
|
||||
file_error();
|
||||
return; // Fixes TX bug if there's a file error
|
||||
} else {
|
||||
reader = std::move(p);
|
||||
}
|
||||
@@ -145,16 +147,25 @@ void ReplayAppView::start() {
|
||||
field_rfgain.set_value(tx_gain);
|
||||
receiver_model.set_tx_gain(tx_gain);
|
||||
|
||||
|
||||
field_rfamp.on_change = [this](int32_t v) {
|
||||
rf_amp = (bool)v;
|
||||
};
|
||||
field_rfamp.set_value(rf_amp ? 14 : 0);
|
||||
|
||||
//Enable Bias Tee if selected
|
||||
radio::set_antenna_bias(portapack::get_antenna_bias());
|
||||
|
||||
radio::enable({
|
||||
receiver_model.tuning_frequency(),
|
||||
sample_rate * 8 ,
|
||||
sample_rate * 8,
|
||||
baseband_bandwidth,
|
||||
rf::Direction::Transmit,
|
||||
receiver_model.rf_amp(),
|
||||
rf_amp, // previous code line : "receiver_model.rf_amp()," was passing the same rf_amp of all Receiver Apps
|
||||
static_cast<int8_t>(receiver_model.lna()),
|
||||
static_cast<int8_t>(receiver_model.vga())
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ReplayAppView::stop(const bool do_loop) {
|
||||
if( is_active() )
|
||||
@@ -163,6 +174,7 @@ void ReplayAppView::stop(const bool do_loop) {
|
||||
if (do_loop && check_loop.value()) {
|
||||
start();
|
||||
} else {
|
||||
radio::set_antenna_bias(false); //Turn off Bias Tee
|
||||
radio::disable();
|
||||
button_play.set_bitmap(&bitmap_play);
|
||||
}
|
||||
@@ -186,7 +198,19 @@ ReplayAppView::ReplayAppView(
|
||||
) : nav_ (nav)
|
||||
{
|
||||
|
||||
tx_gain = 35;field_rfgain.set_value(tx_gain);
|
||||
tx_gain = 35;field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max ).
|
||||
field_rfamp.set_value(rf_amp ? 14 : 0); // Initial default value True. (TX RF amp on , +14dB's)
|
||||
|
||||
field_rfamp.on_change = [this](int32_t v) { // allow initial value change just after opened file.
|
||||
rf_amp = (bool)v;
|
||||
};
|
||||
field_rfamp.set_value(rf_amp ? 14 : 0);
|
||||
|
||||
field_rfgain.on_change = [this](int32_t v) { // allow initial value change just after opened file.
|
||||
tx_gain = v;
|
||||
};
|
||||
field_rfgain.set_value(tx_gain);
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_replay);
|
||||
|
||||
add_children({
|
||||
@@ -198,7 +222,7 @@ ReplayAppView::ReplayAppView(
|
||||
&progressbar,
|
||||
&field_frequency,
|
||||
&field_rfgain,
|
||||
&field_rf_amp,
|
||||
&field_rfamp, // let's not use common rf_amp
|
||||
&check_loop,
|
||||
&button_play,
|
||||
&waterfall,
|
||||
|
||||
@@ -52,6 +52,7 @@ private:
|
||||
|
||||
uint32_t sample_rate = 0;
|
||||
int32_t tx_gain { 47 };
|
||||
bool rf_amp { true }; // aux private var to store temporal, Replay App rf_amp user selection.
|
||||
static constexpr uint32_t baseband_bandwidth = 2500000;
|
||||
const size_t read_size { 16384 };
|
||||
const size_t buffer_count { 3 };
|
||||
@@ -112,8 +113,12 @@ private:
|
||||
1,
|
||||
' '
|
||||
};
|
||||
RFAmpField field_rf_amp {
|
||||
{ 19 * 8, 2 * 16 }
|
||||
NumberField field_rfamp { // previously I was using "RFAmpField field_rf_amp" but that is general Receiver amp setting.
|
||||
{ 19 * 8, 2 * 16 },
|
||||
2,
|
||||
{ 0, 14 }, // this time we will display GUI , 0 or 14 dBs same as Mic App
|
||||
14,
|
||||
' '
|
||||
};
|
||||
Checkbox check_loop {
|
||||
{ 21 * 8, 2 * 16 },
|
||||
|
||||
@@ -240,6 +240,15 @@ SoundBoardView::SoundBoardView(
|
||||
&button_next_page,
|
||||
&tx_view
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_soundboard", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
refresh_list();
|
||||
|
||||
@@ -280,6 +289,10 @@ SoundBoardView::SoundBoardView(
|
||||
}
|
||||
|
||||
SoundBoardView::~SoundBoardView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_soundboard", &app_settings);
|
||||
|
||||
stop();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "lfsr_random.hpp"
|
||||
#include "io_wave.hpp"
|
||||
#include "tone_key.hpp"
|
||||
#include "app_settings.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -45,10 +46,14 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Soundboard"; };
|
||||
std::string title() const override { return "Soundboard TX"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
enum tx_modes {
|
||||
NORMAL = 0,
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace tpms {
|
||||
|
||||
namespace format {
|
||||
|
||||
static bool use_kpa = true;
|
||||
|
||||
std::string type(Reading::Type type) {
|
||||
return to_string_dec_uint(toUType(type), 2);
|
||||
}
|
||||
@@ -43,7 +45,11 @@ std::string id(TransponderID id) {
|
||||
}
|
||||
|
||||
std::string pressure(Pressure pressure) {
|
||||
return to_string_dec_int(pressure.kilopascal(), 3);
|
||||
if(use_kpa){
|
||||
return to_string_dec_int(pressure.kilopascal(), 3);
|
||||
}
|
||||
return to_string_dec_int(pressure.psi(), 3);
|
||||
|
||||
}
|
||||
|
||||
std::string temperature(Temperature temperature) {
|
||||
@@ -142,9 +148,19 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
&field_rf_amp,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&recent_entries_view,
|
||||
&options_type,
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_tpms", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
options_band.set_by_value(app_settings.rx_frequency);
|
||||
}
|
||||
else options_band.set_by_value(receiver_model.tuning_frequency());
|
||||
|
||||
radio::enable({
|
||||
tuning_frequency(),
|
||||
sampling_rate,
|
||||
@@ -160,6 +176,17 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
};
|
||||
options_band.set_by_value(target_frequency());
|
||||
|
||||
options_type.on_change = [this](size_t, int32_t i) {
|
||||
if (i == 0){
|
||||
tpms::format::use_kpa = true;
|
||||
} else if (i == 1){
|
||||
tpms::format::use_kpa = false;
|
||||
}
|
||||
update_type();
|
||||
};
|
||||
|
||||
options_type.set_selected_index(0, true);
|
||||
|
||||
logger = std::make_unique<TPMSLogger>();
|
||||
if( logger ) {
|
||||
logger->append(u"tpms.txt");
|
||||
@@ -167,6 +194,12 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
}
|
||||
|
||||
TPMSAppView::~TPMSAppView() {
|
||||
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = target_frequency_;
|
||||
settings.save("rx_tpms", &app_settings);
|
||||
|
||||
radio::disable();
|
||||
|
||||
baseband::shutdown();
|
||||
@@ -176,9 +209,24 @@ void TPMSAppView::focus() {
|
||||
options_band.focus();
|
||||
}
|
||||
|
||||
void TPMSAppView::update_type() {
|
||||
if (tpms::format::use_kpa){
|
||||
remove_child(&recent_entries_view_psi);
|
||||
add_child(&recent_entries_view_kpa);
|
||||
recent_entries_view_kpa.set_parent_rect(view_normal_rect);
|
||||
} else {
|
||||
remove_child(&recent_entries_view_kpa);
|
||||
add_child(&recent_entries_view_psi);
|
||||
recent_entries_view_psi.set_parent_rect(view_normal_rect);
|
||||
}
|
||||
}
|
||||
|
||||
void TPMSAppView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
recent_entries_view.set_parent_rect({ 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height });
|
||||
|
||||
view_normal_rect = { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height };
|
||||
|
||||
update_type();
|
||||
}
|
||||
|
||||
void TPMSAppView::on_packet(const tpms::Packet& packet) {
|
||||
@@ -191,13 +239,23 @@ void TPMSAppView::on_packet(const tpms::Packet& packet) {
|
||||
const auto reading = reading_opt.value();
|
||||
auto& entry = ::on_packet(recent, TPMSRecentEntry::Key { reading.type(), reading.id() });
|
||||
entry.update(reading);
|
||||
recent_entries_view.set_dirty();
|
||||
|
||||
if(tpms::format::use_kpa){
|
||||
recent_entries_view_kpa.set_dirty();
|
||||
} else {
|
||||
recent_entries_view_psi.set_dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TPMSAppView::on_show_list() {
|
||||
recent_entries_view.hidden(false);
|
||||
recent_entries_view.focus();
|
||||
if(tpms::format::use_kpa){
|
||||
recent_entries_view_kpa.hidden(false);
|
||||
recent_entries_view_kpa.focus();
|
||||
} else {
|
||||
recent_entries_view_psi.hidden(false);
|
||||
recent_entries_view_psi.focus();
|
||||
}
|
||||
}
|
||||
|
||||
void TPMSAppView::on_band_changed(const uint32_t new_band_frequency) {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_rssi.hpp"
|
||||
#include "ui_channel.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "log_file.hpp"
|
||||
@@ -103,13 +103,17 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "TPMS"; };
|
||||
std::string title() const override { return "TPMS Cars RX"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 315000000;
|
||||
static constexpr uint32_t sampling_rate = 2457600;
|
||||
static constexpr uint32_t baseband_bandwidth = 1750000;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
MessageHandlerRegistration message_handler_packet {
|
||||
Message::ID::TPMSPacket,
|
||||
[this](Message* const p) {
|
||||
@@ -121,6 +125,8 @@ private:
|
||||
|
||||
static constexpr ui::Dim header_height = 1 * 16;
|
||||
|
||||
ui::Rect view_normal_rect { };
|
||||
|
||||
RSSI rssi {
|
||||
{ 21 * 8, 0, 6 * 8, 4 },
|
||||
};
|
||||
@@ -134,7 +140,16 @@ private:
|
||||
3,
|
||||
{
|
||||
{ "315", 315000000 },
|
||||
{ "434", 433920000 },
|
||||
{ "433", 433920000 },
|
||||
}
|
||||
};
|
||||
|
||||
OptionsField options_type {
|
||||
{ 5 * 8, 0 * 16 },
|
||||
3,
|
||||
{
|
||||
{ "kPa", 0 },
|
||||
{ "PSI", 1 }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -153,7 +168,7 @@ private:
|
||||
TPMSRecentEntries recent { };
|
||||
std::unique_ptr<TPMSLogger> logger { };
|
||||
|
||||
const RecentEntriesColumns columns { {
|
||||
const RecentEntriesColumns columns_kpa { {
|
||||
{ "Tp", 2 },
|
||||
{ "ID", 8 },
|
||||
{ "kPa", 3 },
|
||||
@@ -161,12 +176,23 @@ private:
|
||||
{ "Cnt", 3 },
|
||||
{ "Fl", 2 },
|
||||
} };
|
||||
TPMSRecentEntriesView recent_entries_view { columns, recent };
|
||||
TPMSRecentEntriesView recent_entries_view_kpa { columns_kpa, recent };
|
||||
|
||||
const RecentEntriesColumns columns_psi { {
|
||||
{ "Tp", 2 },
|
||||
{ "ID", 8 },
|
||||
{ "PSI", 3 },
|
||||
{ "C", 3 },
|
||||
{ "Cnt", 3 },
|
||||
{ "Fl", 2 },
|
||||
} };
|
||||
TPMSRecentEntriesView recent_entries_view_psi { columns_psi, recent };
|
||||
|
||||
uint32_t target_frequency_ = initial_target_frequency;
|
||||
|
||||
void on_packet(const tpms::Packet& packet);
|
||||
void on_show_list();
|
||||
void update_type();
|
||||
|
||||
void on_band_changed(const uint32_t new_band_frequency);
|
||||
|
||||
|
||||
@@ -36,14 +36,16 @@ namespace ui
|
||||
console.writeln("East2West,fossum,ArjanOnwezen");
|
||||
console.writeln("vXxOinvizioNxX,teixeluis");
|
||||
console.writeln("Brumi-2021,texasyojimbo");
|
||||
console.writeln("heurist1,intoxsick");
|
||||
console.writeln("heurist1,intoxsick,ckuethe");
|
||||
console.writeln("notpike,jLynx,zigad");
|
||||
console.writeln("MichalLeonBorsuk");
|
||||
console.writeln("");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// https://github.com/eried/portapack-mayhem/graphs/contributors?to=2020-04-12&from=2015-07-31&type=c
|
||||
console.writeln("\x1B\x06Havoc:\x1B\x10");
|
||||
console.writeln("furrtek,mrmookie,notpike");
|
||||
console.writeln("furrtek,mrmookie,NotPike");
|
||||
console.writeln("mjwaxios,ImDroided,Giorgiofox");
|
||||
console.writeln("F4GEV,z4ziggy,xmycroftx");
|
||||
console.writeln("troussos,silascutler");
|
||||
|
||||
@@ -72,7 +72,7 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
|
||||
to_string_dec_uint((unsigned int)entry.velo.speed,4) +
|
||||
to_string_dec_uint((unsigned int)(entry.amp>>9),4) + " " +
|
||||
(entry.hits <= 999 ? to_string_dec_uint(entry.hits, 3) + " " : "1k+ ") +
|
||||
to_string_dec_uint(entry.age, 3);
|
||||
to_string_dec_uint(entry.age, 4);
|
||||
#endif
|
||||
|
||||
painter.draw_string(
|
||||
@@ -91,6 +91,117 @@ void ADSBLogger::log_str(std::string& logline) {
|
||||
log_file.write_entry(datetime,logline);
|
||||
}
|
||||
|
||||
|
||||
// Aircraft Details
|
||||
void ADSBRxAircraftDetailsView::focus() {
|
||||
button_close.focus();
|
||||
}
|
||||
|
||||
ADSBRxAircraftDetailsView::~ADSBRxAircraftDetailsView() {
|
||||
on_close_();
|
||||
}
|
||||
|
||||
ADSBRxAircraftDetailsView::ADSBRxAircraftDetailsView(
|
||||
NavigationView& nav,
|
||||
const AircraftRecentEntry& entry,
|
||||
const std::function<void(void)> on_close
|
||||
) : entry_copy(entry),
|
||||
on_close_(on_close)
|
||||
{
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&text_icao_address,
|
||||
&text_registration,
|
||||
&text_manufacturer,
|
||||
&text_model,
|
||||
&text_type,
|
||||
&text_number_of_engines,
|
||||
&text_engine_type,
|
||||
&text_owner,
|
||||
&text_operator,
|
||||
&button_close
|
||||
});
|
||||
|
||||
std::unique_ptr<ADSBLogger> logger { };
|
||||
//update(entry_copy);
|
||||
|
||||
|
||||
icao_code = to_string_hex(entry_copy.ICAO_address, 6);
|
||||
text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6));
|
||||
|
||||
// Try getting the aircraft information from icao24.db
|
||||
return_code = db.retrieve_aircraft_record(&aircraft_record, icao_code);
|
||||
switch(return_code) {
|
||||
case DATABASE_RECORD_FOUND:
|
||||
text_registration.set(aircraft_record.aircraft_registration);
|
||||
text_manufacturer.set(aircraft_record.aircraft_manufacturer);
|
||||
text_model.set(aircraft_record.aircraft_model);
|
||||
text_owner.set(aircraft_record.aircraft_owner);
|
||||
text_operator.set(aircraft_record.aircraft_operator);
|
||||
// Check for ICAO type, e.g. L2J
|
||||
if(strlen(aircraft_record.icao_type) == 3) {
|
||||
switch(aircraft_record.icao_type[0]) {
|
||||
case 'L':
|
||||
text_type.set("Landplane");
|
||||
break;
|
||||
case 'S':
|
||||
text_type.set("Seaplane");
|
||||
break;
|
||||
case 'A':
|
||||
text_type.set("Amphibian");
|
||||
break;
|
||||
case 'H':
|
||||
text_type.set("Helicopter");
|
||||
break;
|
||||
case 'G':
|
||||
text_type.set("Gyrocopter");
|
||||
break;
|
||||
case 'T':
|
||||
text_type.set("Tilt-wing aircraft");
|
||||
break;
|
||||
}
|
||||
text_number_of_engines.set(std::string(1, aircraft_record.icao_type[1]));
|
||||
switch(aircraft_record.icao_type[2]) {
|
||||
case 'P':
|
||||
text_engine_type.set("Piston engine");
|
||||
break;
|
||||
case 'T':
|
||||
text_engine_type.set("Turboprop/Turboshaft engine");
|
||||
break;
|
||||
case 'J':
|
||||
text_engine_type.set("Jet engine");
|
||||
break;
|
||||
case 'E':
|
||||
text_engine_type.set("Electric engine");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
// Check for ICAO type designator
|
||||
else if(strlen(aircraft_record.icao_type) == 4) {
|
||||
if(strcmp(aircraft_record.icao_type,"SHIP") == 0) text_type.set("Airship");
|
||||
else if(strcmp(aircraft_record.icao_type,"BALL") == 0) text_type.set("Balloon");
|
||||
else if(strcmp(aircraft_record.icao_type,"GLID") == 0) text_type.set("Glider / sailplane");
|
||||
else if(strcmp(aircraft_record.icao_type,"ULAC") == 0) text_type.set("Micro/ultralight aircraft");
|
||||
else if(strcmp(aircraft_record.icao_type,"GYRO") == 0) text_type.set("Micro/ultralight autogyro");
|
||||
else if(strcmp(aircraft_record.icao_type,"UHEL") == 0) text_type.set("Micro/ultralight helicopter");
|
||||
else if(strcmp(aircraft_record.icao_type,"SHIP") == 0) text_type.set("Airship");
|
||||
else if(strcmp(aircraft_record.icao_type,"PARA") == 0) text_type.set("Powered parachute/paraplane");
|
||||
}
|
||||
break;
|
||||
case DATABASE_NOT_FOUND:
|
||||
text_manufacturer.set("No icao24.db file");
|
||||
break;
|
||||
}
|
||||
button_close.on_select = [&nav](Button&){
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// End of Aicraft details
|
||||
|
||||
void ADSBRxDetailsView::focus() {
|
||||
button_see_map.focus();
|
||||
}
|
||||
@@ -114,7 +225,7 @@ void ADSBRxDetailsView::update(const AircraftRecentEntry& entry) {
|
||||
text_frame_pos_odd.set(to_string_hex_array(entry_copy.frame_pos_odd.get_raw_data(), 14));
|
||||
|
||||
if (send_updates)
|
||||
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading);
|
||||
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading, entry_copy.pos.altitude);
|
||||
}
|
||||
|
||||
ADSBRxDetailsView::~ADSBRxDetailsView() {
|
||||
@@ -128,14 +239,10 @@ ADSBRxDetailsView::ADSBRxDetailsView(
|
||||
) : entry_copy(entry),
|
||||
on_close_(on_close)
|
||||
{
|
||||
char file_buffer[32] { 0 };
|
||||
bool found = false;
|
||||
int number_of_airlines = 0;
|
||||
std::string airline_code;
|
||||
size_t c;
|
||||
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&text_icao_address,
|
||||
&text_callsign,
|
||||
&text_last_seen,
|
||||
&text_airline,
|
||||
@@ -144,6 +251,7 @@ ADSBRxDetailsView::ADSBRxDetailsView(
|
||||
&text_info2,
|
||||
&text_frame_pos_even,
|
||||
&text_frame_pos_odd,
|
||||
&button_aircraft_details,
|
||||
&button_see_map
|
||||
});
|
||||
|
||||
@@ -152,41 +260,34 @@ ADSBRxDetailsView::ADSBRxDetailsView(
|
||||
|
||||
// The following won't (shouldn't !) change for a given airborne aircraft
|
||||
// Try getting the airline's name from airlines.db
|
||||
auto result = db_file.open("ADSB/airlines.db");
|
||||
if (!result.is_valid()) {
|
||||
// Search for 3-letter code
|
||||
number_of_airlines = (db_file.size() / 68); // determine number of airlines in file
|
||||
airline_code = entry_copy.callsign.substr(0, 3);
|
||||
c = 0;
|
||||
do {
|
||||
db_file.read(file_buffer, 4);
|
||||
if (!file_buffer[0])
|
||||
break;
|
||||
if (!airline_code.compare(0, 4, file_buffer))
|
||||
found = true;
|
||||
else
|
||||
c++;
|
||||
} while (!found && (c < number_of_airlines));
|
||||
|
||||
if (found) {
|
||||
db_file.seek((number_of_airlines * 4) + (c << 6)); // seek starting after index
|
||||
db_file.read(file_buffer, 32);
|
||||
text_airline.set(file_buffer);
|
||||
db_file.read(file_buffer, 32);
|
||||
text_country.set(file_buffer);
|
||||
} else {
|
||||
text_airline.set("Unknown");
|
||||
text_country.set("Unknown");
|
||||
}
|
||||
} else {
|
||||
text_airline.set("No airlines.db file");
|
||||
text_country.set("No airlines.db file");
|
||||
airline_code = entry_copy.callsign.substr(0, 3);
|
||||
return_code = db.retrieve_airline_record(&airline_record, airline_code);
|
||||
switch(return_code) {
|
||||
case DATABASE_RECORD_FOUND:
|
||||
text_airline.set(airline_record.airline);
|
||||
text_country.set(airline_record.country);
|
||||
break;
|
||||
case DATABASE_NOT_FOUND:
|
||||
text_airline.set("No airlines.db file");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
text_callsign.set(entry_copy.callsign);
|
||||
|
||||
text_icao_address.set(to_string_hex(entry_copy.ICAO_address, 6));
|
||||
|
||||
button_aircraft_details.on_select = [this, &nav](Button&) {
|
||||
//detailed_entry_key = entry.key();
|
||||
aircraft_details_view = nav.push<ADSBRxAircraftDetailsView>(
|
||||
entry_copy,
|
||||
[this]() {
|
||||
send_updates = false;
|
||||
});
|
||||
send_updates = false;
|
||||
};
|
||||
|
||||
button_see_map.on_select = [this, &nav](Button&) {
|
||||
if (!send_updates) { // Prevent recursivley launching the map
|
||||
if (!send_updates) { // Prevent recursively launching the map
|
||||
geomap_view = nav.push<GeoMapView>(
|
||||
entry_copy.callsign,
|
||||
entry_copy.pos.altitude,
|
||||
@@ -202,13 +303,18 @@ ADSBRxDetailsView::ADSBRxDetailsView(
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
void ADSBRxView::focus() {
|
||||
field_vga.focus();
|
||||
}
|
||||
|
||||
ADSBRxView::~ADSBRxView() {
|
||||
receiver_model.set_tuning_frequency(prevFreq);
|
||||
|
||||
// save app settings
|
||||
settings.save("rx_adsb", &app_settings);
|
||||
|
||||
//TODO: once all apps keep there own settin previous frequency logic can be removed
|
||||
receiver_model.set_tuning_frequency(prevFreq);
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
@@ -356,6 +462,21 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
|
||||
&recent_entries_view
|
||||
});
|
||||
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_adsb", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
else
|
||||
{
|
||||
field_lna.set_value(40);
|
||||
field_vga.set_value(40);
|
||||
}
|
||||
|
||||
|
||||
recent_entries_view.set_parent_rect({ 0, 16, 240, 272 });
|
||||
recent_entries_view.on_select = [this, &nav](const AircraftRecentEntry& entry) {
|
||||
detailed_entry_key = entry.key();
|
||||
@@ -376,8 +497,6 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
|
||||
baseband::set_adsb();
|
||||
|
||||
receiver_model.set_tuning_frequency(1090000000);
|
||||
field_lna.set_value(40);
|
||||
field_vga.set_value(40);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_sampling_rate(2000000);
|
||||
receiver_model.set_baseband_bandwidth(2500000);
|
||||
|
||||
@@ -27,11 +27,12 @@
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
#include "database.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "adsb.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "crc.hpp"
|
||||
|
||||
using namespace adsb;
|
||||
@@ -159,6 +160,98 @@ private:
|
||||
};
|
||||
|
||||
|
||||
class ADSBRxAircraftDetailsView : public View {
|
||||
public:
|
||||
ADSBRxAircraftDetailsView(NavigationView&, const AircraftRecentEntry& entry, const std::function<void(void)> on_close);
|
||||
~ADSBRxAircraftDetailsView();
|
||||
|
||||
ADSBRxAircraftDetailsView(const ADSBRxAircraftDetailsView&) = delete;
|
||||
ADSBRxAircraftDetailsView(ADSBRxAircraftDetailsView&&) = delete;
|
||||
ADSBRxAircraftDetailsView& operator=(const ADSBRxAircraftDetailsView&) = delete;
|
||||
ADSBRxAircraftDetailsView& operator=(ADSBRxAircraftDetailsView&&) = delete;
|
||||
|
||||
void focus() override;
|
||||
|
||||
void update(const AircraftRecentEntry& entry);
|
||||
|
||||
std::string title() const override { return "AC Details"; };
|
||||
|
||||
AircraftRecentEntry get_current_entry() { return entry_copy; }
|
||||
|
||||
std::database::AircraftDBRecord aircraft_record = {};
|
||||
|
||||
private:
|
||||
AircraftRecentEntry entry_copy { 0 };
|
||||
std::function<void(void)> on_close_ { };
|
||||
bool send_updates { false };
|
||||
std::database db = { };
|
||||
std::string icao_code = "";
|
||||
int return_code = 0;
|
||||
|
||||
Labels labels {
|
||||
{ { 0 * 8, 1 * 16 }, "ICAO:", Color::light_grey() },
|
||||
{ { 0 * 8, 2 * 16 }, "Registration:", Color::light_grey() },
|
||||
{ { 0 * 8, 3 * 16 }, "Manufacturer:", Color::light_grey() },
|
||||
{ { 0 * 8, 5 * 16 }, "Model:", Color::light_grey() },
|
||||
{ { 0 * 8, 7 * 16 }, "Type:", Color::light_grey() },
|
||||
{ { 0 * 8, 8 * 16 }, "Number of engines:", Color::light_grey() },
|
||||
{ { 0 * 8, 9 * 16 }, "Engine type:", Color::light_grey() },
|
||||
{ { 0 * 8, 11 * 16 }, "Owner:", Color::light_grey() },
|
||||
{ { 0 * 8, 13 * 16 }, "Operator:", Color::light_grey() }
|
||||
};
|
||||
|
||||
Text text_icao_address {
|
||||
{ 5 * 8, 1 * 16, 6 * 8, 16},
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_registration {
|
||||
{ 13 * 8, 2 * 16, 8 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_manufacturer {
|
||||
{ 0 * 8, 4 * 16, 19 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_model {
|
||||
{ 0 * 8, 6 * 16, 30 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_type {
|
||||
{ 5 * 8, 7 * 16, 22 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_number_of_engines {
|
||||
{ 18 * 8, 8 * 16, 30 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_engine_type {
|
||||
{ 0 * 8, 10 * 16, 30 * 8, 16},
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_owner {
|
||||
{ 0 * 8, 12 * 16, 30 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_operator {
|
||||
{ 0 * 8, 14 * 16, 30 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Button button_close {
|
||||
{ 9 * 8, 16 * 16, 12 * 8, 3 * 16 },
|
||||
"Back"
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class ADSBRxDetailsView : public View {
|
||||
public:
|
||||
ADSBRxDetailsView(NavigationView&, const AircraftRecentEntry& entry, const std::function<void(void)> on_close);
|
||||
@@ -176,16 +269,23 @@ public:
|
||||
std::string title() const override { return "Details"; };
|
||||
|
||||
AircraftRecentEntry get_current_entry() { return entry_copy; }
|
||||
|
||||
|
||||
std::database::AirlinesDBRecord airline_record = {};
|
||||
|
||||
private:
|
||||
AircraftRecentEntry entry_copy { 0 };
|
||||
std::function<void(void)> on_close_ { };
|
||||
GeoMapView* geomap_view { nullptr };
|
||||
bool send_updates { false };
|
||||
File db_file { };
|
||||
AircraftRecentEntry entry_copy { 0 };
|
||||
std::function<void(void)> on_close_ { };
|
||||
GeoMapView* geomap_view { nullptr };
|
||||
ADSBRxAircraftDetailsView* aircraft_details_view { nullptr };
|
||||
bool send_updates { false };
|
||||
std::database db = { };
|
||||
std::string airline_code = "";
|
||||
int return_code = 0;
|
||||
|
||||
Labels labels {
|
||||
{ { 0 * 8, 1 * 16 }, "Callsign:", Color::light_grey() },
|
||||
{ { 0 * 8, 1 * 16 }, "ICAO:", Color::light_grey() },
|
||||
{ { 13 * 8, 1 * 16 }, "Callsign:", Color::light_grey() },
|
||||
{ { 0 * 8, 2 * 16 }, "Last seen:", Color::light_grey() },
|
||||
{ { 0 * 8, 3 * 16 }, "Airline:", Color::light_grey() },
|
||||
{ { 0 * 8, 5 * 16 }, "Country:", Color::light_grey() },
|
||||
@@ -193,8 +293,13 @@ private:
|
||||
{ { 0 * 8, 15 * 16 }, "Odd position frame:", Color::light_grey() }
|
||||
};
|
||||
|
||||
Text text_icao_address {
|
||||
{ 5 * 8, 1 * 16, 6 * 8, 16},
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_callsign {
|
||||
{ 9 * 8, 1 * 16, 8 * 8, 16 },
|
||||
{ 22 * 8, 1 * 16, 8 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
@@ -232,11 +337,17 @@ private:
|
||||
"-"
|
||||
};
|
||||
|
||||
Button button_aircraft_details {
|
||||
{ 2 * 8, 9 * 16, 12 * 8, 3 * 16 },
|
||||
"A/C details"
|
||||
};
|
||||
|
||||
Button button_see_map {
|
||||
{ 8 * 8, 9 * 16, 14 * 8, 3 * 16 },
|
||||
{ 16 * 8, 9 * 16, 12 * 8, 3 * 16 },
|
||||
"See on map"
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class ADSBRxView : public View {
|
||||
public:
|
||||
@@ -250,17 +361,20 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "ADS-B receive"; };
|
||||
std::string title() const override { return "ADS-B RX"; };
|
||||
|
||||
void replace_entry(AircraftRecentEntry & entry);
|
||||
AircraftRecentEntry find_or_create_entry(uint32_t ICAO_address);
|
||||
void sort_entries_by_state();
|
||||
|
||||
private:
|
||||
rf::Frequency prevFreq;
|
||||
rf::Frequency prevFreq = { 0 };
|
||||
std::unique_ptr<ADSBLogger> logger { };
|
||||
void on_frame(const ADSBFrameMessage * message);
|
||||
void on_tick_second();
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
const RecentEntriesColumns columns { {
|
||||
#if false
|
||||
@@ -274,7 +388,7 @@ private:
|
||||
{ "Spd", 3 },
|
||||
{ "Amp", 3 },
|
||||
{ "Hit", 3 },
|
||||
{ "Age", 3 }
|
||||
{ "Age", 4 }
|
||||
#endif
|
||||
} };
|
||||
AircraftRecentEntries recent { };
|
||||
|
||||
@@ -284,6 +284,11 @@ void ADSBTxView::focus() {
|
||||
}
|
||||
|
||||
ADSBTxView::~ADSBTxView() {
|
||||
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_adsb", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -334,8 +339,16 @@ ADSBTxView::ADSBTxView(
|
||||
&view_squawk,
|
||||
&text_frame,
|
||||
&tx_view
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_adsb", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
using namespace adsb;
|
||||
@@ -152,7 +153,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "ADS-B transmit"; };
|
||||
std::string title() const override { return "ADS-B TX"; };
|
||||
|
||||
private:
|
||||
/*enum tx_modes {
|
||||
@@ -189,6 +190,10 @@ private:
|
||||
-1,
|
||||
-1
|
||||
};*/
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
//tx_modes tx_mode = IDLE;
|
||||
NavigationView& nav_;
|
||||
|
||||
@@ -66,6 +66,15 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
|
||||
&console
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_afsk", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
|
||||
|
||||
// DEBUG
|
||||
record_view.on_error = [&nav](std::string message) {
|
||||
nav.display_modal("Error", message);
|
||||
@@ -164,6 +173,11 @@ void AFSKRxView::on_data(uint32_t value, bool is_data) {
|
||||
}
|
||||
|
||||
AFSKRxView::~AFSKRxView() {
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_afsk", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_record_view.hpp" // DEBUG
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
@@ -57,6 +57,10 @@ public:
|
||||
private:
|
||||
void on_data(uint32_t value, bool is_data);
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
uint8_t console_color { 0 };
|
||||
uint32_t prev_value { 0 };
|
||||
std::string str_log { "" };
|
||||
|
||||
@@ -98,6 +98,15 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect) : View(parent_rect
|
||||
&console
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_aprs", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
|
||||
|
||||
// DEBUG
|
||||
record_view.on_error = [&nav](std::string message) {
|
||||
nav.display_modal("Error", message);
|
||||
@@ -108,12 +117,12 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect) : View(parent_rect
|
||||
options_region.on_change = [this](size_t, int32_t i) {
|
||||
if (i == 0){
|
||||
field_frequency.set_value(144390000);
|
||||
}
|
||||
if(i == 1){
|
||||
} else if(i == 1){
|
||||
field_frequency.set_value(144800000);
|
||||
}
|
||||
if(i == 2){
|
||||
} else if(i == 2){
|
||||
field_frequency.set_value(145175000);
|
||||
} else if(i == 3){
|
||||
field_frequency.set_value(144575000);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -211,6 +220,10 @@ void APRSRxView::on_show(){
|
||||
}
|
||||
|
||||
APRSRxView::~APRSRxView() {
|
||||
|
||||
// save app settings
|
||||
settings.save("rx_aprs", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
@@ -351,7 +364,7 @@ void APRSDetailsView::update() {
|
||||
}
|
||||
|
||||
if (send_updates)
|
||||
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0);
|
||||
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, 0, 0);
|
||||
}
|
||||
|
||||
APRSDetailsView::~APRSDetailsView() {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_record_view.hpp" // DEBUG
|
||||
#include "ui_geomap.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
|
||||
@@ -57,12 +57,12 @@ struct APRSRecentEntry {
|
||||
uint16_t hits { 0 };
|
||||
uint32_t age { 0 };
|
||||
|
||||
uint64_t source;
|
||||
uint64_t source { 0 };
|
||||
std::string source_formatted { " " };
|
||||
std::string time_string { "" };
|
||||
std::string info_string { "" };
|
||||
|
||||
aprs::aprs_pos pos;
|
||||
aprs::aprs_pos pos { 0 , 0 , 0 , 0 };
|
||||
bool has_position = false;
|
||||
APRSRecentEntry(uint64_t src)
|
||||
{
|
||||
@@ -131,6 +131,7 @@ private:
|
||||
GeoMapView* geomap_view { nullptr };
|
||||
bool send_updates { false };
|
||||
|
||||
|
||||
Console console {
|
||||
{ 0, 0 * 16, 240, 224 }
|
||||
};
|
||||
@@ -192,6 +193,11 @@ private:
|
||||
void on_data(uint32_t value, bool is_data);
|
||||
bool reset_console = false;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
|
||||
uint8_t console_color { 0 };
|
||||
std::string str_log { "" };
|
||||
|
||||
@@ -217,7 +223,8 @@ private:
|
||||
{
|
||||
{ "NA ", 0 },
|
||||
{ "EUR", 1 },
|
||||
{ "AUS", 2 }
|
||||
{ "AUS", 2 },
|
||||
{ "NZ ", 3 }
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ void APRSTXView::focus() {
|
||||
}
|
||||
|
||||
APRSTXView::~APRSTXView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_aprs", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -66,7 +70,7 @@ void APRSTXView::start_tx() {
|
||||
1200,
|
||||
2200,
|
||||
1,
|
||||
10000, //transmitter_model.channel_bandwidth(),
|
||||
10000, //APRS uses fixed 10k bandwidth
|
||||
8
|
||||
);
|
||||
}
|
||||
@@ -95,6 +99,15 @@ APRSTXView::APRSTXView(NavigationView& nav) {
|
||||
&tx_view
|
||||
});
|
||||
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_aprs", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
button_set.on_select = [this, &nav](Button&) {
|
||||
text_prompt(
|
||||
nav,
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
@@ -40,17 +41,14 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "APRS TX (beta)"; };
|
||||
std::string title() const override { return "APRS TX"; };
|
||||
|
||||
private:
|
||||
/*enum tx_modes {
|
||||
IDLE = 0,
|
||||
SINGLE,
|
||||
SEQUENCE
|
||||
};
|
||||
|
||||
tx_modes tx_mode = IDLE;*/
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
std::string payload { "" };
|
||||
|
||||
void start_tx();
|
||||
@@ -103,7 +101,7 @@ private:
|
||||
TransmitterView tx_view {
|
||||
16 * 16,
|
||||
5000,
|
||||
10
|
||||
0 // disable setting bandwith, since APRS used fixed 10k bandwidth
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress {
|
||||
|
||||
@@ -96,9 +96,9 @@ void BHTView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
if (target_system == XYLOS) {
|
||||
if (done) {
|
||||
if (tx_mode == SINGLE) {
|
||||
if (checkbox_cligno.value()) {
|
||||
if (checkbox_flashing.value()) {
|
||||
// TODO: Thread !
|
||||
chThdSleepMilliseconds(field_tempo.value() * 1000); // Dirty :(
|
||||
chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :(
|
||||
view_xylos.flip_relays();
|
||||
start_tx();
|
||||
} else
|
||||
@@ -120,9 +120,9 @@ void BHTView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
} else {
|
||||
view_EPAR.half = false;
|
||||
if (tx_mode == SINGLE) {
|
||||
if (checkbox_cligno.value()) {
|
||||
if (checkbox_flashing.value()) {
|
||||
// TODO: Thread !
|
||||
chThdSleepMilliseconds(field_tempo.value() * 1000); // Dirty :(
|
||||
chThdSleepMilliseconds(field_speed.value() * 1000); // Dirty :(
|
||||
view_EPAR.flip_relays();
|
||||
start_tx();
|
||||
} else
|
||||
@@ -140,6 +140,10 @@ void BHTView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
}
|
||||
|
||||
BHTView::~BHTView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_bht", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
}
|
||||
|
||||
@@ -150,13 +154,22 @@ BHTView::BHTView(NavigationView& nav) {
|
||||
&view_xylos,
|
||||
&view_EPAR,
|
||||
&checkbox_scan,
|
||||
&checkbox_cligno,
|
||||
&field_tempo,
|
||||
&checkbox_flashing,
|
||||
&field_speed,
|
||||
&progressbar,
|
||||
&tx_view
|
||||
});
|
||||
|
||||
field_tempo.set_value(1);
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_bht", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
field_speed.set_value(1);
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "encoders.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
@@ -50,11 +51,11 @@ public:
|
||||
private:
|
||||
Labels labels {
|
||||
{ { 8 * 8, 1 * 8 }, "Header:", Color::light_grey() },
|
||||
{ { 4 * 8, 3 * 8 }, "Code ville:", Color::light_grey() },
|
||||
{ { 7 * 8, 5 * 8 }, "Famille:", Color::light_grey() },
|
||||
{ { 2 * 8, 7 * 8 + 2 }, "Sous-famille:", Color::light_grey() },
|
||||
{ { 2 * 8, 11 * 8 }, "ID recepteur:", Color::light_grey() },
|
||||
{ { 2 * 8, 14 * 8 }, "Relais:", Color::light_grey() }
|
||||
{ { 4 * 8, 3 * 8 }, "City code:", Color::light_grey() },
|
||||
{ { 7 * 8, 5 * 8 }, "Family:", Color::light_grey() },
|
||||
{ { 2 * 8, 7 * 8 + 2 }, "Subfamily:", Color::light_grey() },
|
||||
{ { 2 * 8, 11 * 8 }, "Receiver ID:", Color::light_grey() },
|
||||
{ { 2 * 8, 14 * 8 }, "Relay:", Color::light_grey() }
|
||||
};
|
||||
|
||||
NumberField field_header_a {
|
||||
@@ -98,8 +99,8 @@ private:
|
||||
|
||||
Checkbox checkbox_wcsubfamily {
|
||||
{ 20 * 8, 6 * 8 + 6 },
|
||||
6,
|
||||
"Toutes"
|
||||
3,
|
||||
"All"
|
||||
};
|
||||
|
||||
NumberField field_receiver {
|
||||
@@ -111,8 +112,8 @@ private:
|
||||
};
|
||||
Checkbox checkbox_wcid {
|
||||
{ 20 * 8, 10 * 8 + 4 },
|
||||
4,
|
||||
"Tous"
|
||||
3,
|
||||
"All"
|
||||
};
|
||||
|
||||
std::array<ImageOptionsField, 4> relay_states { };
|
||||
@@ -139,9 +140,9 @@ public:
|
||||
|
||||
private:
|
||||
Labels labels {
|
||||
{ { 4 * 8, 1 * 8 }, "Code ville:", Color::light_grey() },
|
||||
{ { 8 * 8, 3 * 8 }, "Groupe:", Color::light_grey() },
|
||||
{ { 8 * 8, 7 * 8 }, "Relais:", Color::light_grey() }
|
||||
{ { 4 * 8, 1 * 8 }, "City code:", Color::light_grey() },
|
||||
{ { 8 * 8, 3 * 8 }, "Group:", Color::light_grey() },
|
||||
{ { 8 * 8, 7 * 8 }, "Relay:", Color::light_grey() }
|
||||
};
|
||||
|
||||
NumberField field_city {
|
||||
@@ -178,9 +179,13 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "BHT transmit"; };
|
||||
std::string title() const override { return "BHT Xy/EP TX"; };
|
||||
|
||||
private:
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
void start_tx();
|
||||
void stop_tx();
|
||||
@@ -220,12 +225,12 @@ private:
|
||||
"Scan"
|
||||
};
|
||||
|
||||
Checkbox checkbox_cligno {
|
||||
Checkbox checkbox_flashing {
|
||||
{ 16 * 8, 25 * 8 },
|
||||
6,
|
||||
"Cligno"
|
||||
8,
|
||||
"Flashing"
|
||||
};
|
||||
NumberField field_tempo {
|
||||
NumberField field_speed {
|
||||
{ 26 * 8, 25 * 8 + 4 },
|
||||
2,
|
||||
{ 1, 99 },
|
||||
|
||||
@@ -60,6 +60,15 @@ BTLERxView::BTLERxView(NavigationView& nav) {
|
||||
&console
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_btle", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
|
||||
|
||||
// DEBUG
|
||||
record_view.on_error = [&nav](std::string message) {
|
||||
nav.display_modal("Error", message);
|
||||
@@ -152,6 +161,11 @@ void BTLERxView::on_data(uint32_t value, bool is_data) {
|
||||
}
|
||||
|
||||
BTLERxView::~BTLERxView() {
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_btle", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "ui_record_view.hpp" // DEBUG
|
||||
|
||||
#include "utility.hpp"
|
||||
@@ -45,6 +46,11 @@ public:
|
||||
private:
|
||||
void on_data(uint32_t value, bool is_data);
|
||||
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
uint8_t console_color { 0 };
|
||||
uint32_t prev_value { 0 };
|
||||
std::string str_log { "" };
|
||||
|
||||
@@ -37,6 +37,10 @@ void CoasterPagerView::focus() {
|
||||
}
|
||||
|
||||
CoasterPagerView::~CoasterPagerView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_coaster", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -119,6 +123,15 @@ CoasterPagerView::CoasterPagerView(NavigationView& nav) {
|
||||
&tx_view
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_coaster", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
// Bytes to nibbles
|
||||
for (c = 0; c < 16; c++)
|
||||
sym_data.set_sym(c, (data_init[c >> 1] >> ((c & 1) ? 0 : 4)) & 0x0F);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
@@ -39,7 +40,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Si44xx TX"; };
|
||||
std::string title() const override { return "BurgerPgr TX"; };
|
||||
|
||||
private:
|
||||
enum tx_modes {
|
||||
@@ -50,6 +51,10 @@ private:
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
void start_tx();
|
||||
void generate_frame();
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "ui_sd_card_debug.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "irq_controls.hpp"
|
||||
@@ -345,14 +346,17 @@ DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
|
||||
/* DebugMenuView *********************************************************/
|
||||
|
||||
DebugMenuView::DebugMenuView(NavigationView& nav) {
|
||||
if( portapack::persistent_memory::show_gui_return_icon() )
|
||||
{
|
||||
add_items( { { "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } } } );
|
||||
}
|
||||
add_items({
|
||||
//{ "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } },
|
||||
{ "Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav](){ nav.push<DebugMemoryView>(); } },
|
||||
//{ "Radio State", ui::Color::white(), nullptr, [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [&nav](){ nav.push<SDCardDebugView>(); } },
|
||||
{ "Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [&nav](){ nav.push<DebugPeripheralsMenuView>(); } },
|
||||
{ "Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav](){ nav.push<TemperatureView>(); } },
|
||||
{ "Buttons test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav](){ nav.push<DebugControlsView>(); } },
|
||||
{ "Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav](){ nav.push<DebugControlsView>(); } },
|
||||
});
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Memory"; };
|
||||
|
||||
private:
|
||||
Text text_title {
|
||||
{ 96, 96, 48, 16 },
|
||||
@@ -113,6 +115,8 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Temperature"; };
|
||||
|
||||
private:
|
||||
Text text_title {
|
||||
{ 76, 16, 240, 16 },
|
||||
@@ -252,6 +256,8 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Buttons Test"; };
|
||||
|
||||
private:
|
||||
Text text_title {
|
||||
{ 64, 16, 184, 16 },
|
||||
|
||||
@@ -203,6 +203,10 @@ void EncodersView::focus() {
|
||||
}
|
||||
|
||||
EncodersView::~EncodersView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_ook", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -335,6 +339,15 @@ EncodersView::EncodersView(
|
||||
&tx_view
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_ook", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "transmitter_model.hpp"
|
||||
#include "encoders.hpp"
|
||||
#include "de_bruijn.hpp"
|
||||
#include "app_settings.hpp"
|
||||
|
||||
using namespace encoders;
|
||||
|
||||
@@ -158,7 +159,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "OOK transmit"; };
|
||||
std::string title() const override { return "OOK TX"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
@@ -169,6 +170,10 @@ private:
|
||||
SCAN
|
||||
};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
uint8_t repeat_index { 0 };
|
||||
uint8_t repeat_min { 0 };
|
||||
|
||||
@@ -106,7 +106,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Jammer"; };
|
||||
std::string title() const override { return "Jammer TX"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
@@ -136,6 +136,9 @@ void KeyfobView::focus() {
|
||||
}
|
||||
|
||||
KeyfobView::~KeyfobView() {
|
||||
// save app settings
|
||||
settings.save("tx_keyfob", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -214,6 +217,13 @@ KeyfobView::KeyfobView(
|
||||
&tx_view
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_keyfob", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
frame[0] = 0x55;
|
||||
update_symfields();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "encoders.hpp"
|
||||
|
||||
using namespace encoders;
|
||||
@@ -36,11 +37,15 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Keyfob (BETA)"; };
|
||||
std::string title() const override { return "Key fob TX"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
// 1013210ns / bit
|
||||
static constexpr uint32_t subaru_samples_per_bit = (OOK_SAMPLERATE * 0.00101321);
|
||||
static constexpr uint32_t repeats = 4;
|
||||
|
||||
@@ -39,6 +39,10 @@ void LCRView::focus() {
|
||||
}
|
||||
|
||||
LCRView::~LCRView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_lcr", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -173,6 +177,15 @@ LCRView::LCRView(NavigationView& nav) {
|
||||
&tx_view
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_lcr", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
options_scanlist.set_selected_index(0);
|
||||
|
||||
const auto button_set_am_fn = [this, &nav](Button& button) {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -39,7 +40,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "LCR transmit"; };
|
||||
std::string title() const override { return "TEDI/LCR TX"; };
|
||||
|
||||
private:
|
||||
struct scan_list_t {
|
||||
@@ -81,6 +82,10 @@ private:
|
||||
SCAN
|
||||
};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
uint8_t scan_count { 0 }, scan_index { 0 };
|
||||
uint32_t scan_progress { 0 };
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "audio.hpp"
|
||||
|
||||
#include "wm8731.hpp"
|
||||
using wolfson::wm8731::WM8731;
|
||||
|
||||
#include "tonesets.hpp"
|
||||
#include "portapack_hal.hpp"
|
||||
#include "string_format.hpp"
|
||||
@@ -34,6 +38,10 @@
|
||||
using namespace tonekey;
|
||||
using namespace portapack;
|
||||
|
||||
|
||||
WM8731 audio_codec_wm8731 { i2c0, 0x1a };
|
||||
|
||||
|
||||
namespace ui {
|
||||
|
||||
void MicTXView::focus() {
|
||||
@@ -178,7 +186,7 @@ void MicTXView::rxaudio(bool is_on) {
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_mic_tx);
|
||||
audio::output::stop();
|
||||
audio::input::start();
|
||||
audio::input::start(); // set up audio input = mic config of any audio coded AK4951/WM8731, (in WM8731 parameter will be ignored)
|
||||
portapack::pin_i2s0_rx_sda.mode(3);
|
||||
configure_baseband();
|
||||
}
|
||||
@@ -203,10 +211,12 @@ MicTXView::MicTXView(
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_mic_tx);
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
|
||||
if (true ) { // Temporary , disabling ALC feature , (pending to solve -No Audio in Mic app ,in some H2/H2+ WM /QFP100 CPLS users- if ( audio_codec_wm8731.detected() ) {
|
||||
add_children({
|
||||
&labels_WM8731, // we have audio codec WM8731, same MIC menu as original.
|
||||
&vumeter,
|
||||
&options_gain,
|
||||
&options_gain, // MIC GAIN float factor on the GUI.
|
||||
// &check_va,
|
||||
&field_va,
|
||||
&field_va_level,
|
||||
@@ -229,6 +239,37 @@ MicTXView::MicTXView(
|
||||
&field_rxamp,
|
||||
&tx_button
|
||||
});
|
||||
|
||||
} else {
|
||||
add_children({
|
||||
&labels_AK4951, // we have audio codec AK4951, enable Automatic Level Control
|
||||
&vumeter,
|
||||
&options_gain,
|
||||
&options_ak4951_alc_mode,
|
||||
// &check_va,
|
||||
&field_va,
|
||||
&field_va_level,
|
||||
&field_va_attack,
|
||||
&field_va_decay,
|
||||
&field_bw,
|
||||
&field_rfgain,
|
||||
&field_rfamp,
|
||||
&options_mode,
|
||||
&field_frequency,
|
||||
&options_tone_key,
|
||||
&check_rogerbeep,
|
||||
&check_rxactive,
|
||||
&field_volume,
|
||||
&field_rxbw,
|
||||
&field_squelch,
|
||||
&field_rxfrequency,
|
||||
&field_rxlna,
|
||||
&field_rxvga,
|
||||
&field_rxamp,
|
||||
&tx_button
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
tone_keys_populate(options_tone_key);
|
||||
options_tone_key.on_change = [this](size_t i, int32_t) {
|
||||
@@ -242,6 +283,12 @@ MicTXView::MicTXView(
|
||||
};
|
||||
options_gain.set_selected_index(1); // x1.0
|
||||
|
||||
options_ak4951_alc_mode.on_change = [this](size_t, int8_t v) {
|
||||
ak4951_alc_GUI_selected = v;
|
||||
audio::input::start();
|
||||
};
|
||||
// options_ak4951_alc_mode.set_selected_index(0);
|
||||
|
||||
tx_frequency = transmitter_model.tuning_frequency();
|
||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
@@ -294,23 +341,37 @@ MicTXView::MicTXView(
|
||||
enable_dsb = false;
|
||||
field_bw.set_value(transmitter_model.channel_bandwidth() / 1000);
|
||||
//if (rx_enabled)
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
options_tone_key.hidden(0); // we are in FM mode , we should have active the Key-tones & CTCSS option.
|
||||
field_rxbw.hidden(0); // we are in FM mode, we need to allow the user set up of the RX NFM BW selection (8K5, 11K, 16K)
|
||||
break;
|
||||
case 1:
|
||||
enable_am = true;
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
options_tone_key.set_selected_index(0); // we are NOT in FM mode , we reset the possible previous key-tones &CTCSS selection.
|
||||
set_dirty(); // Refresh display
|
||||
options_tone_key.hidden(1); // we hide that Key-tones & CTCSS input selecction, (no meaning in AM/DSB/SSB).
|
||||
field_rxbw.hidden(1); // we hide the NFM BW selection in other modes non_FM (no meaning in AM/DSB/SSB)
|
||||
check_rogerbeep.hidden(0); // make visible again the "rogerbeep" selection.
|
||||
break;
|
||||
case 2:
|
||||
enable_usb = true;
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now.
|
||||
check_rogerbeep.hidden(1); // hide that roger beep selection.
|
||||
set_dirty(); // Refresh display
|
||||
break;
|
||||
case 3:
|
||||
enable_lsb = true;
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
check_rogerbeep.set_value(false); // reset the possible activation of roger beep, because it is not compatible with SSB , by now.
|
||||
check_rogerbeep.hidden(1); // hide that roger beep selection.
|
||||
set_dirty(); // Refresh display
|
||||
break;
|
||||
case 4:
|
||||
enable_dsb = true;
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
check_rogerbeep.hidden(0); // make visible again the "rogerbeep" selection.
|
||||
break;
|
||||
}
|
||||
//configure_baseband();
|
||||
@@ -478,7 +539,7 @@ MicTXView::MicTXView(
|
||||
set_tx(false);
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::input::start();
|
||||
audio::input::start(); // originally , audio::input::start(); (we added parameter)
|
||||
}
|
||||
|
||||
MicTXView::~MicTXView() {
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
};
|
||||
*/
|
||||
|
||||
std::string title() const override { return "Mic TX RX"; };
|
||||
std::string title() const override { return "Microphone"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t sampling_rate = 1536000U;
|
||||
@@ -83,6 +83,7 @@ private:
|
||||
bool rx_enabled { false };
|
||||
uint32_t tone_key_index { };
|
||||
float mic_gain { 1.0 };
|
||||
uint8_t ak4951_alc_GUI_selected { 0 };
|
||||
uint32_t audio_level { 0 };
|
||||
uint32_t va_level { };
|
||||
uint32_t attack_ms { };
|
||||
@@ -106,8 +107,8 @@ private:
|
||||
bool enable_lsb { false };
|
||||
|
||||
|
||||
Labels labels {
|
||||
{ { 3 * 8, 1 * 8 }, "MIC. GAIN:", Color::light_grey() },
|
||||
Labels labels_WM8731 {
|
||||
{ { 3 * 8, 1 * 8 }, "MIC-GAIN:", Color::light_grey() },
|
||||
{ { 3 * 8, 3 * 8 }, "F:", Color::light_grey() },
|
||||
{ { 15 * 8, 3 * 8 }, "BW: FM kHz", Color::light_grey() },
|
||||
{ { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() },
|
||||
@@ -118,15 +119,36 @@ private:
|
||||
{ {12 * 8, 10 * 8 }, "ATT:", Color::light_grey() },
|
||||
{ {20 * 8, 10 * 8 }, "DEC:", Color::light_grey() },
|
||||
{ { 4 * 8, ( 13 * 8 ) - 2 }, "TONE KEY:", Color::light_grey() },
|
||||
{ { 9 * 8, 23 * 8 }, "VOL:", Color::light_grey() },
|
||||
{ {17 * 8, 23 * 8 }, "FM RXBW:", Color::light_grey() },
|
||||
{ { 7 * 8, 23 * 8 }, "VOL:", Color::light_grey() },
|
||||
{ {15 * 8, 23 * 8 }, "FM RXBW:", Color::light_grey() },
|
||||
{ {17 * 8, 25 * 8 }, "SQ:", Color::light_grey() },
|
||||
{ { 5 * 8, 25 * 8 }, "F:", Color::light_grey() },
|
||||
{ { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()},
|
||||
{ {12 * 8, 27 * 8 }, "VGA:", Color::light_grey()},
|
||||
{ {19 * 8, 27 * 8 }, "AMP:", Color::light_grey()}
|
||||
};
|
||||
|
||||
Labels labels_AK4951 {
|
||||
{ { 3 * 8, 1 * 8 }, "MIC-GAIN:", Color::light_grey() },
|
||||
{ { 17 * 8, 1 * 8 }, "ALC", Color::light_grey() },
|
||||
{ { 3 * 8, 3 * 8 }, "F:", Color::light_grey() },
|
||||
{ { 15 * 8, 3 * 8 }, "BW: FM kHz", Color::light_grey() },
|
||||
{ { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() },
|
||||
{ {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() },
|
||||
{ { 18 * 8, (5 * 8) }, "Mode:", Color::light_grey() },
|
||||
{ { 3 * 8, 8 * 8 }, "TX Activation:", Color::light_grey() },
|
||||
{ { 4 * 8, 10 * 8 }, "LVL:", Color::light_grey() },
|
||||
{ {12 * 8, 10 * 8 }, "ATT:", Color::light_grey() },
|
||||
{ {20 * 8, 10 * 8 }, "DEC:", Color::light_grey() },
|
||||
{ { 4 * 8, ( 13 * 8 ) - 2 }, "TONE KEY:", Color::light_grey() },
|
||||
{ { 7 * 8, 23 * 8 }, "VOL:", Color::light_grey() },
|
||||
{ {15 * 8, 23 * 8 }, "FM RXBW:", Color::light_grey() },
|
||||
{ {17 * 8, 25 * 8 }, "SQ:", Color::light_grey() },
|
||||
{ { 5 * 8, 25 * 8 }, "F:", Color::light_grey() },
|
||||
{ { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()},
|
||||
{ {12 * 8, 27 * 8 }, "VGA:", Color::light_grey()},
|
||||
{ {19 * 8, 27 * 8 }, "AMP:", Color::light_grey()}
|
||||
};
|
||||
|
||||
VuMeter vumeter {
|
||||
{ 0 * 8, 1 * 8, 2 * 8, 33 * 8 },
|
||||
12,
|
||||
@@ -135,7 +157,7 @@ private:
|
||||
|
||||
|
||||
OptionsField options_gain {
|
||||
{ 13 * 8, 1 * 8 },
|
||||
{ 12 * 8, 1 * 8 },
|
||||
4,
|
||||
{
|
||||
{ "x0.5", 5 },
|
||||
@@ -145,6 +167,25 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
OptionsField options_ak4951_alc_mode {
|
||||
{ 20 * 8, 1 * 8 }, // Coordinates are: int:x (px), int:y (px)
|
||||
11,
|
||||
{
|
||||
{ " OFF-20kHz", 0 }, // Nothing changed from ORIGINAL,keeping ALL programm. AK4951 Dig. block->OFF)
|
||||
{ "+12dB-6kHz", 1 }, // ALC-> on, (+12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original)
|
||||
{ "+09dB-6kHz", 2 }, // ALC-> on, (+09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original)
|
||||
{ "+06dB-6kHz", 3 }, // ALC-> on, (+06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original)
|
||||
{ "+03dB-2kHz", 4 }, // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 3,5k + Pre-amp Mic (+21dB=original)+ EQ boosting ~<2kHz (f0~1k1,fb:1,7K, k=1,8)
|
||||
{ "+03dB-4kHz", 5 }, // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 4kHz + Pre-amp Mic (+21dB=original)+ EQ boosting ~<3kHz (f0~1k4,fb~2,4k, k=1,8)
|
||||
{ "+03dB-6kHz", 6 }, // ALC-> on, (+03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original)
|
||||
{ "+00dB-6kHz", 7 }, // ALC-> on, (+00dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original)
|
||||
{ "-03dB-6kHz", 8 }, // ALC-> on, (-03dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original)
|
||||
{ "-06dB-6kHz", 9 }, // ALC-> on, (-06dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz + Pre-amp Mic (+21dB=original)
|
||||
{ "-09dB-6kHz", 10 }, // ALC-> on, (-09dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -3dB (18dB's)
|
||||
{ "-12dB-6kHz", 11 }, // ALC-> on, (-12dB's) Auto Vol max + Wind Noise cancel + LPF 6kHz - Pre-amp MIC -6dB (15dB's)
|
||||
}
|
||||
};
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 5 * 8, 3 * 8 },
|
||||
};
|
||||
@@ -244,7 +285,7 @@ private:
|
||||
};
|
||||
|
||||
NumberField field_volume {
|
||||
{ 13 * 8, 23 * 8 },
|
||||
{ 11* 8, 23 * 8 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
@@ -252,7 +293,7 @@ private:
|
||||
};
|
||||
|
||||
OptionsField field_rxbw {
|
||||
{ 25 * 8, 23 * 8},
|
||||
{ 23* 8, 23 * 8},
|
||||
3,
|
||||
{
|
||||
{"8k5", 0},
|
||||
|
||||
@@ -97,6 +97,10 @@ void MorseView::focus() {
|
||||
}
|
||||
|
||||
MorseView::~MorseView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_morse", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -203,6 +207,15 @@ MorseView::MorseView(
|
||||
&tx_view
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_morse", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
// Default settings
|
||||
field_speed.set_value(15); // 15wps
|
||||
field_tone.set_value(700); // 700Hz FM tone
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
#include "volume.hpp"
|
||||
@@ -67,6 +67,10 @@ private:
|
||||
std::string message { };
|
||||
uint32_t time_units { 0 };
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
enum modulation_t {
|
||||
CW = 0,
|
||||
FM = 1
|
||||
|
||||
@@ -60,6 +60,14 @@ NRFRxView::NRFRxView(NavigationView& nav) {
|
||||
&console
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_nrf", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
record_view.on_error = [&nav](std::string message) {
|
||||
nav.display_modal("Error", message);
|
||||
@@ -157,6 +165,11 @@ void NRFRxView::on_data(uint32_t value, bool is_data) {
|
||||
}
|
||||
|
||||
NRFRxView::~NRFRxView() {
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_nrf", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "ui_record_view.hpp" // DEBUG
|
||||
|
||||
#include "utility.hpp"
|
||||
@@ -45,6 +46,10 @@ public:
|
||||
private:
|
||||
void on_data(uint32_t value, bool is_data);
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
uint8_t console_color { 0 };
|
||||
uint32_t prev_value { 0 };
|
||||
std::string str_log { "" };
|
||||
|
||||
@@ -38,6 +38,10 @@ void POCSAGTXView::focus() {
|
||||
}
|
||||
|
||||
POCSAGTXView::~POCSAGTXView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_pocsag", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -141,6 +145,15 @@ POCSAGTXView::POCSAGTXView(
|
||||
&tx_view
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_pocsag", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
options_bitrate.set_selected_index(1); // 1200bps
|
||||
options_type.set_selected_index(0); // Address only
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "bch_code.hpp"
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "pocsag.hpp"
|
||||
|
||||
using namespace pocsag;
|
||||
@@ -62,6 +63,10 @@ private:
|
||||
5, 31, 21, 2
|
||||
};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
void on_set_text(NavigationView& nav);
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
bool start_tx();
|
||||
|
||||
@@ -175,6 +175,10 @@ void RDSView::focus() {
|
||||
}
|
||||
|
||||
RDSView::~RDSView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_rds", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -226,11 +230,17 @@ RDSView::RDSView(
|
||||
&view_radiotext,
|
||||
&view_datetime,
|
||||
&view_audio,
|
||||
//&options_countrycode,
|
||||
//&options_coverage,
|
||||
&tx_view,
|
||||
});
|
||||
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_rds", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
check_TP.set_value(true);
|
||||
|
||||
sym_pi_code.set_sym(0, 0xF);
|
||||
@@ -242,8 +252,6 @@ RDSView::RDSView(
|
||||
};
|
||||
|
||||
options_pty.set_selected_index(0); // None
|
||||
//options_countrycode.set_selected_index(18); // Baguette du fromage
|
||||
//options_coverage.set_selected_index(0); // Local
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "rds.hpp"
|
||||
|
||||
using namespace rds;
|
||||
@@ -144,12 +144,17 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "RDS transmit"; };
|
||||
std::string title() const override { return "RDS TX"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RDS_flags rds_flags { };
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
|
||||
std::vector<RDSGroup> frame_psn { };
|
||||
std::vector<RDSGroup> frame_radiotext { };
|
||||
std::vector<RDSGroup> frame_datetime { };
|
||||
|
||||
@@ -484,6 +484,7 @@ void ScannerView::scan_pause() {
|
||||
scan_thread->set_freq_lock(0); //Reset the scanner lock (because user paused, or MAX_FREQ_LOCK reached) for next freq scan
|
||||
scan_thread->set_scanning(false); // WE STOP SCANNING
|
||||
audio::output::start();
|
||||
on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,7 +540,7 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
|
||||
field_bw.set_selected_index(0);
|
||||
receiver_model.set_am_configuration(field_bw.selected_index());
|
||||
field_bw.on_change = [this](size_t n, OptionsField::value_t) { receiver_model.set_am_configuration(n); };
|
||||
receiver_model.set_sampling_rate(2000000);receiver_model.set_baseband_bandwidth(2000000);
|
||||
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
|
||||
break;
|
||||
case WFM:
|
||||
bw.emplace_back("16k", 0);
|
||||
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
|
||||
std::string title() const override { return "SCANNER"; };
|
||||
std::string title() const override { return "Scanner"; };
|
||||
std::vector<rf::Frequency> frequency_list{ };
|
||||
std::vector<string> description_list { };
|
||||
|
||||
@@ -242,16 +242,16 @@ private:
|
||||
{ 17 * 8, (26 * 8) + 4 },
|
||||
12,
|
||||
{
|
||||
{ "5Khz (SA AM)", 5000 },
|
||||
{ "9Khz (EU AM)", 9000 },
|
||||
{ "10Khz(US AM)", 10000 },
|
||||
{ "50Khz (FM1)", 50000 },
|
||||
{ "100Khz(FM2)", 100000 },
|
||||
{ "6.25khz(NFM)", 6250 },
|
||||
{ "12.5khz(NFM)", 12500 },
|
||||
{ "25khz (N1)", 25000 },
|
||||
{ "250khz (N2)", 250000 },
|
||||
{ "8.33khz(AIR)", 8330 }
|
||||
{ "5kHz (SA AM)", 5000 },
|
||||
{ "9kHz (EU AM)", 9000 },
|
||||
{ "10kHz(US AM)", 10000 },
|
||||
{ "50kHz (FM1)", 50000 },
|
||||
{ "100kHz(FM2)", 100000 },
|
||||
{ "6.25kHz(NFM)", 6250 },
|
||||
{ "12.5kHz(NFM)", 12500 },
|
||||
{ "25kHz (N1)", 25000 },
|
||||
{ "250kHz (N2)", 250000 },
|
||||
{ "8.33kHz(AIR)", 8330 }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -308,4 +308,4 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
~WipeSDView();
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Wipe FAT"; };
|
||||
std::string title() const override { return "Wipe SD Card"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace ui {
|
||||
SetDateTimeView::SetDateTimeView(
|
||||
NavigationView& nav
|
||||
) {
|
||||
button_done.on_select = [&nav, this](Button&){
|
||||
button_save.on_select = [&nav, this](Button&){
|
||||
const auto model = this->form_collect();
|
||||
const rtc::RTC new_datetime {
|
||||
model.year, model.month, model.day,
|
||||
@@ -65,7 +65,7 @@ SetDateTimeView::SetDateTimeView(
|
||||
&field_hour,
|
||||
&field_minute,
|
||||
&field_second,
|
||||
&button_done,
|
||||
&button_save,
|
||||
&button_cancel,
|
||||
});
|
||||
|
||||
@@ -150,7 +150,7 @@ SetRadioView::SetRadioView(
|
||||
&value_freq_step,
|
||||
&labels_bias,
|
||||
&check_bias,
|
||||
&button_done,
|
||||
&button_save,
|
||||
&button_cancel
|
||||
});
|
||||
|
||||
@@ -200,7 +200,7 @@ SetRadioView::SetRadioView(
|
||||
EventDispatcher::send_message(message);
|
||||
};
|
||||
|
||||
button_done.on_select = [this, &nav](Button&){
|
||||
button_save.on_select = [this, &nav](Button&){
|
||||
const auto model = this->form_collect();
|
||||
portapack::persistent_memory::set_correction_ppb(model.ppm * 1000);
|
||||
portapack::persistent_memory::set_clkout_freq(model.freq);
|
||||
@@ -210,7 +210,7 @@ SetRadioView::SetRadioView(
|
||||
}
|
||||
|
||||
void SetRadioView::focus() {
|
||||
button_done.focus();
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
void SetRadioView::form_init(const SetFrequencyCorrectionModel& model) {
|
||||
@@ -224,84 +224,30 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() {
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
SetPlayDeadView::SetPlayDeadView(NavigationView& nav) {
|
||||
add_children({
|
||||
&text_sequence,
|
||||
&button_enter,
|
||||
&button_cancel
|
||||
});
|
||||
|
||||
button_enter.on_select = [this, &nav](Button&){
|
||||
if (!entermode) {
|
||||
sequence = 0;
|
||||
keycount = 0;
|
||||
memset(sequence_txt, '-', 10);
|
||||
text_sequence.set(sequence_txt);
|
||||
entermode = true;
|
||||
button_cancel.hidden(true);
|
||||
set_dirty();
|
||||
} else {
|
||||
if (sequence == 0x8D1) // U D L R
|
||||
nav.display_modal("Warning", "Default sequence entered !", ABORT, nullptr);
|
||||
else {
|
||||
persistent_memory::set_playdead_sequence(sequence);
|
||||
nav.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
button_enter.on_dir = [this](Button&, KeyEvent key){
|
||||
if ((entermode == true) && (keycount < 10)) {
|
||||
key_code = static_cast<std::underlying_type<KeyEvent>::type>(key);
|
||||
if (key_code == 0)
|
||||
sequence_txt[keycount] = 'R';
|
||||
else if (key_code == 1)
|
||||
sequence_txt[keycount] = 'L';
|
||||
else if (key_code == 2)
|
||||
sequence_txt[keycount] = 'D';
|
||||
else if (key_code == 3)
|
||||
sequence_txt[keycount] = 'U';
|
||||
text_sequence.set(sequence_txt);
|
||||
sequence = (sequence << 3) | (key_code + 1);
|
||||
keycount++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
button_cancel.on_select = [&nav](Button&){ nav.pop(); };
|
||||
}
|
||||
|
||||
void SetPlayDeadView::focus() {
|
||||
button_cancel.focus();
|
||||
}
|
||||
*/
|
||||
|
||||
SetUIView::SetUIView(NavigationView& nav) {
|
||||
add_children({
|
||||
//&checkbox_login,
|
||||
&checkbox_disable_touchscreen,
|
||||
&checkbox_speaker,
|
||||
&checkbox_bloff,
|
||||
&options_bloff,
|
||||
&checkbox_showsplash,
|
||||
&checkbox_showclock,
|
||||
&options_clockformat,
|
||||
&button_ok
|
||||
&options_clockformat,
|
||||
&checkbox_guireturnflag,
|
||||
&button_save,
|
||||
&button_cancel
|
||||
});
|
||||
|
||||
checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen());
|
||||
checkbox_speaker.set_value(persistent_memory::config_speaker());
|
||||
checkbox_showsplash.set_value(persistent_memory::config_splash());
|
||||
checkbox_showclock.set_value(!persistent_memory::hide_clock());
|
||||
//checkbox_login.set_value(persistent_memory::config_login());
|
||||
checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon());
|
||||
|
||||
uint32_t backlight_timer = persistent_memory::config_backlight_timer();
|
||||
if (backlight_timer) {
|
||||
checkbox_bloff.set_value(true);
|
||||
options_bloff.set_by_value(backlight_timer);
|
||||
} else {
|
||||
options_bloff.set_selected_index(0);
|
||||
}
|
||||
const auto backlight_config = persistent_memory::config_backlight_timer();
|
||||
checkbox_bloff.set_value(backlight_config.timeout_enabled());
|
||||
options_bloff.set_by_value(backlight_config.timeout_enum());
|
||||
|
||||
if (persistent_memory::clock_with_date()) {
|
||||
options_clockformat.set_selected_index(1);
|
||||
@@ -309,245 +255,134 @@ SetUIView::SetUIView(NavigationView& nav) {
|
||||
options_clockformat.set_selected_index(0);
|
||||
}
|
||||
|
||||
checkbox_speaker.on_select = [this](Checkbox&, bool v) {
|
||||
if (v) audio::output::speaker_mute(); //Just mute audio if speaker is disabled
|
||||
|
||||
persistent_memory::set_config_speaker(v); //Store Speaker status
|
||||
|
||||
StatusRefreshMessage message { }; //Refresh status bar with/out speaker
|
||||
EventDispatcher::send_message(message);
|
||||
};
|
||||
|
||||
button_ok.on_select = [&nav, this](Button&) {
|
||||
if (checkbox_bloff.value())
|
||||
persistent_memory::set_config_backlight_timer(options_bloff.selected_index() + 1);
|
||||
else
|
||||
persistent_memory::set_config_backlight_timer(0);
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
persistent_memory::set_config_backlight_timer({
|
||||
(persistent_memory::backlight_timeout_t)options_bloff.selected_index_value(),
|
||||
checkbox_bloff.value()
|
||||
});
|
||||
|
||||
if (checkbox_showclock.value()){
|
||||
if (options_clockformat.selected_index() == 1)
|
||||
persistent_memory::set_clock_with_date(true);
|
||||
else
|
||||
persistent_memory::set_clock_with_date(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (checkbox_speaker.value()) audio::output::speaker_mute(); //Just mute audio if speaker is disabled
|
||||
persistent_memory::set_config_speaker(checkbox_speaker.value()); //Store Speaker status
|
||||
StatusRefreshMessage message { }; //Refresh status bar with/out speaker
|
||||
EventDispatcher::send_message(message);
|
||||
|
||||
persistent_memory::set_config_splash(checkbox_showsplash.value());
|
||||
persistent_memory::set_clock_hidden(!checkbox_showclock.value());
|
||||
//persistent_memory::set_config_login(checkbox_login.value());
|
||||
persistent_memory::set_gui_return_icon(checkbox_guireturnflag.value());
|
||||
persistent_memory::set_disable_touchscreen(checkbox_disable_touchscreen.value());
|
||||
nav.pop();
|
||||
};
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
void SetUIView::focus() {
|
||||
button_ok.focus();
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Appl. Settings
|
||||
// ---------------------------------------------------------
|
||||
SetAppSettingsView::SetAppSettingsView(NavigationView& nav) {
|
||||
add_children({
|
||||
&checkbox_load_app_settings,
|
||||
&checkbox_save_app_settings,
|
||||
&button_save,
|
||||
&button_cancel
|
||||
});
|
||||
|
||||
checkbox_load_app_settings.set_value(persistent_memory::load_app_settings());
|
||||
checkbox_save_app_settings.set_value(persistent_memory::save_app_settings());
|
||||
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
persistent_memory::set_load_app_settings(checkbox_load_app_settings.value());
|
||||
persistent_memory::set_save_app_settings(checkbox_save_app_settings.value());
|
||||
nav.pop();
|
||||
};
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
void SetAppSettingsView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
SetAudioView::SetAudioView(NavigationView& nav) {
|
||||
add_children({
|
||||
&labels,
|
||||
&field_tone_mix,
|
||||
&button_ok
|
||||
&button_save,
|
||||
&button_cancel
|
||||
});
|
||||
|
||||
field_tone_mix.set_value(persistent_memory::tone_mix());
|
||||
|
||||
button_ok.on_select = [&nav, this](Button&) {
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
persistent_memory::set_tone_mix(field_tone_mix.value());
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
|
||||
void SetAudioView::focus() {
|
||||
field_tone_mix.focus();
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
/*void ModInfoView::on_show() {
|
||||
if (modules_nb) update_infos(0);
|
||||
}
|
||||
SetQRCodeView::SetQRCodeView(NavigationView& nav) {
|
||||
add_children({
|
||||
&checkbox_bigger_qr,
|
||||
&button_save,
|
||||
&button_cancel
|
||||
});
|
||||
|
||||
void ModInfoView::update_infos(uint8_t modn) {
|
||||
char info_str[27];
|
||||
char ch;
|
||||
uint8_t c;
|
||||
Point pos = { 0, 0 };
|
||||
Rect rect = { { 16, 144 }, { 208, 144 } };
|
||||
checkbox_bigger_qr.set_value(persistent_memory::show_bigger_qr_code());
|
||||
|
||||
info_str[0] = 0;
|
||||
strcat(info_str, module_list[modn].name);
|
||||
text_namestr.set(info_str);
|
||||
|
||||
info_str[0] = 0;
|
||||
strcat(info_str, to_string_dec_uint(module_list[modn].size).c_str());
|
||||
strcat(info_str, " bytes");
|
||||
text_sizestr.set(info_str);
|
||||
|
||||
info_str[0] = 0;
|
||||
for (c = 0; c < 8; c++)
|
||||
strcat(info_str, to_string_hex(module_list[modn].md5[c], 2).c_str());
|
||||
text_md5_a.set(info_str);
|
||||
|
||||
info_str[0] = 0;
|
||||
for (c = 8; c < 16; c++)
|
||||
strcat(info_str, to_string_hex(module_list[modn].md5[c], 2).c_str());
|
||||
text_md5_b.set(info_str);
|
||||
|
||||
// TODO: Use ui_console
|
||||
display.fill_rectangle(rect, Color::black());
|
||||
|
||||
const Font& font = font::fixed_8x16;
|
||||
const auto line_height = font.line_height();
|
||||
c = 0;
|
||||
while((ch = module_list[modn].description[c++])) {
|
||||
const auto glyph = font.glyph(ch);
|
||||
const auto advance = glyph.advance();
|
||||
if((pos.x + advance.x) > rect.width()) {
|
||||
pos.x = 0;
|
||||
pos.y += line_height;
|
||||
}
|
||||
const Point pos_glyph {
|
||||
static_cast<Coord>(rect.pos.x + pos.x),
|
||||
static_cast<Coord>(rect.pos.y + pos.y)
|
||||
};
|
||||
display.draw_glyph(pos_glyph, glyph, Color::white(), Color::black());
|
||||
pos.x += advance.x;
|
||||
}
|
||||
}
|
||||
|
||||
ModInfoView::ModInfoView(NavigationView& nav) {
|
||||
const char magic[4] = {'P', 'P', 'M', ' '};
|
||||
UINT bw;
|
||||
uint8_t i;
|
||||
char read_buf[16];
|
||||
char info_str[25];
|
||||
FILINFO modinfo;
|
||||
FIL modfile;
|
||||
DIR rootdir;
|
||||
FRESULT res;
|
||||
uint8_t c;
|
||||
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
option_t opt;
|
||||
options_t opts;
|
||||
|
||||
static constexpr Style style_orange {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::orange(),
|
||||
};
|
||||
|
||||
add_children({{
|
||||
&text_modcount,
|
||||
&text_name,
|
||||
&text_namestr,
|
||||
&text_size,
|
||||
&text_sizestr,
|
||||
&text_md5,
|
||||
&text_md5_a,
|
||||
&text_md5_b,
|
||||
&button_ok
|
||||
}});
|
||||
|
||||
text_name.set_style(&style_orange);
|
||||
text_size.set_style(&style_orange);
|
||||
text_md5.set_style(&style_orange);
|
||||
|
||||
// TODO: Find a way to merge this with m4_load_image() in m4_startup.cpp
|
||||
|
||||
// Scan SD card root directory for files starting with the magic bytes
|
||||
c = 0;
|
||||
if (f_opendir(&rootdir, "/") == FR_OK) {
|
||||
for (;;) {
|
||||
res = f_readdir(&rootdir, &modinfo);
|
||||
if (res != FR_OK || modinfo.fname[0] == 0) break; // Reached last file, abort
|
||||
// Only care about files with .bin extension
|
||||
if ((!(modinfo.fattrib & AM_DIR)) && (modinfo.fname[9] == 'B') && (modinfo.fname[10] == 'I') && (modinfo.fname[11] == 'N')) {
|
||||
f_open(&modfile, modinfo.fname, FA_OPEN_EXISTING | FA_READ);
|
||||
// Magic bytes check
|
||||
f_read(&modfile, &read_buf, 4, &bw);
|
||||
for (i = 0; i < 4; i++)
|
||||
if (read_buf[i] != magic[i]) break;
|
||||
if (i == 4) {
|
||||
memcpy(&module_list[c].filename, modinfo.fname, 8);
|
||||
module_list[c].filename[8] = 0;
|
||||
|
||||
f_lseek(&modfile, 4);
|
||||
f_read(&modfile, &module_list[c].version, 2, &bw);
|
||||
f_lseek(&modfile, 6);
|
||||
f_read(&modfile, &module_list[c].size, 4, &bw);
|
||||
f_lseek(&modfile, 10);
|
||||
f_read(&modfile, &module_list[c].name, 16, &bw);
|
||||
f_lseek(&modfile, 26);
|
||||
f_read(&modfile, &module_list[c].md5, 16, &bw);
|
||||
f_lseek(&modfile, 42);
|
||||
f_read(&modfile, &module_list[c].description, 214, &bw);
|
||||
f_lseek(&modfile, 256);
|
||||
|
||||
// Sanitize
|
||||
module_list[c].name[15] = 0;
|
||||
module_list[c].description[213] = 0;
|
||||
|
||||
memcpy(info_str, module_list[c].filename, 16);
|
||||
strcat(info_str, "(V");
|
||||
strcat(info_str, to_string_dec_uint(module_list[c].version, 4, '0').c_str());
|
||||
strcat(info_str, ")");
|
||||
while(strlen(info_str) < 24)
|
||||
strcat(info_str, " ");
|
||||
|
||||
opt = std::make_pair(info_str, c);
|
||||
opts.insert(opts.end(), opt);
|
||||
|
||||
c++;
|
||||
}
|
||||
f_close(&modfile);
|
||||
}
|
||||
if (c == 8) break;
|
||||
}
|
||||
}
|
||||
f_closedir(&rootdir);
|
||||
|
||||
modules_nb = c;
|
||||
|
||||
if (modules_nb) {
|
||||
strcpy(info_str, "Found ");
|
||||
strcat(info_str, to_string_dec_uint(modules_nb, 1).c_str());
|
||||
strcat(info_str, " module(s)");
|
||||
|
||||
text_modcount.set(info_str);
|
||||
option_modules.set_options(opts);
|
||||
|
||||
add_child(&option_modules);
|
||||
|
||||
option_modules.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
update_infos(v);
|
||||
};
|
||||
} else {
|
||||
strcpy(info_str, "No modules found");
|
||||
text_modcount.set(info_str);
|
||||
}
|
||||
|
||||
button_ok.on_select = [&nav,this](Button&){
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
persistent_memory::set_show_bigger_qr_code(checkbox_bigger_qr.value());
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void ModInfoView::focus() {
|
||||
if (modules_nb)
|
||||
option_modules.focus();
|
||||
else
|
||||
button_ok.focus();
|
||||
}*/
|
||||
void SetQRCodeView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Settings main menu
|
||||
// ---------------------------------------------------------
|
||||
SettingsMenuView::SettingsMenuView(NavigationView& nav) {
|
||||
if( portapack::persistent_memory::show_gui_return_icon() )
|
||||
{
|
||||
add_items( { { "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } } } );
|
||||
}
|
||||
add_items({
|
||||
//{ "..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav](){ nav.pop(); } },
|
||||
{ "Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav](){ nav.push<SetAudioView>(); } },
|
||||
{ "Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav](){ nav.push<SetRadioView>(); } },
|
||||
{ "Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav](){ nav.push<SetUIView>(); } },
|
||||
//{ "SD card modules", ui::Color::dark_cyan(), [&nav](){ nav.push<ModInfoView>(); } },
|
||||
{ "Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav](){ nav.push<SetDateTimeView>(); } },
|
||||
{ "Touchscreen", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav](){ nav.push<TouchCalibrationView>(); } },
|
||||
//{ "Play dead", ui::Color::dark_cyan(), &bitmap_icon_playdead, [&nav](){ nav.push<SetPlayDeadView>(); } }
|
||||
{ "Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav](){ nav.push<SetAudioView>(); } },
|
||||
{ "Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav](){ nav.push<SetRadioView>(); } },
|
||||
{ "User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav](){ nav.push<SetUIView>(); } },
|
||||
{ "Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav](){ nav.push<SetDateTimeView>(); } },
|
||||
{ "Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav](){ nav.push<TouchCalibrationView>(); } },
|
||||
{ "App Settings", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav](){ nav.push<SetAppSettingsView>(); } },
|
||||
{ "QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav](){ nav.push<SetQRCodeView>(); } }
|
||||
});
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ff.h"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -47,7 +48,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Set Date/Time"; };
|
||||
std::string title() const override { return "Date/Time"; };
|
||||
|
||||
private:
|
||||
Labels labels {
|
||||
@@ -99,7 +100,7 @@ private:
|
||||
'0',
|
||||
};
|
||||
|
||||
Button button_done {
|
||||
Button button_save {
|
||||
{ 2 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Save"
|
||||
};
|
||||
@@ -197,7 +198,7 @@ private:
|
||||
"Turn on bias voltage"
|
||||
};
|
||||
|
||||
Button button_done {
|
||||
Button button_save {
|
||||
{ 2 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Save"
|
||||
};
|
||||
@@ -210,6 +211,8 @@ private:
|
||||
SetFrequencyCorrectionModel form_collect();
|
||||
};
|
||||
|
||||
using portapack::persistent_memory::backlight_timeout_t;
|
||||
|
||||
class SetUIView : public View {
|
||||
public:
|
||||
SetUIView(NavigationView& nav);
|
||||
@@ -219,11 +222,12 @@ public:
|
||||
std::string title() const override { return "UI settings"; };
|
||||
|
||||
private:
|
||||
/*Checkbox checkbox_login {
|
||||
|
||||
Checkbox checkbox_disable_touchscreen {
|
||||
{ 3 * 8, 2 * 16 },
|
||||
20,
|
||||
"Login with play dead"
|
||||
};*/
|
||||
"Disable touchscreen"
|
||||
};
|
||||
|
||||
Checkbox checkbox_speaker {
|
||||
{ 3 * 8, 4 * 16 },
|
||||
@@ -240,13 +244,14 @@ private:
|
||||
{ 52, 7 * 16 + 8 },
|
||||
20,
|
||||
{
|
||||
{ "5 seconds", 5 },
|
||||
{ "15 seconds", 15 },
|
||||
{ "30 seconds", 30 },
|
||||
{ "1 minute", 60 },
|
||||
{ "3 minutes", 180 },
|
||||
{ "5 minutes", 300 },
|
||||
{ "10 minutes", 600 }
|
||||
{ "5 seconds", backlight_timeout_t::Timeout5Sec },
|
||||
{ "15 seconds", backlight_timeout_t::Timeout15Sec },
|
||||
{ "30 seconds", backlight_timeout_t::Timeout30Sec },
|
||||
{ "1 minute", backlight_timeout_t::Timeout60Sec },
|
||||
{ "3 minutes", backlight_timeout_t::Timeout180Sec },
|
||||
{ "5 minutes", backlight_timeout_t::Timeout300Sec },
|
||||
{ "10 minutes", backlight_timeout_t::Timeout600Sec },
|
||||
{ "1 hour", backlight_timeout_t::Timeout3600Sec },
|
||||
}
|
||||
};
|
||||
|
||||
@@ -270,11 +275,55 @@ private:
|
||||
{ "time and date", 1 }
|
||||
}
|
||||
};
|
||||
|
||||
Checkbox checkbox_guireturnflag {
|
||||
{ 3 * 8, 14 * 16 },
|
||||
25,
|
||||
"add return icon in GUI"
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
Button button_save {
|
||||
{ 2 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Save"
|
||||
};
|
||||
|
||||
Button button_cancel {
|
||||
{ 16 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Cancel",
|
||||
};
|
||||
};
|
||||
|
||||
class SetAppSettingsView : public View {
|
||||
public:
|
||||
SetAppSettingsView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "App Settings"; };
|
||||
|
||||
private:
|
||||
|
||||
Checkbox checkbox_load_app_settings {
|
||||
{ 3 * 8, 2 * 16 },
|
||||
25,
|
||||
"Load app settings"
|
||||
};
|
||||
|
||||
Checkbox checkbox_save_app_settings {
|
||||
{ 3 * 8, 4 * 16 },
|
||||
25,
|
||||
"Save app settings"
|
||||
};
|
||||
|
||||
Button button_save {
|
||||
{ 2 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Save"
|
||||
};
|
||||
|
||||
Button button_cancel {
|
||||
{ 16 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Cancel",
|
||||
};
|
||||
};
|
||||
|
||||
class SetAudioView : public View {
|
||||
@@ -298,111 +347,43 @@ private:
|
||||
'0'
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
Button button_save {
|
||||
{ 2 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Save"
|
||||
};
|
||||
|
||||
Button button_cancel {
|
||||
{ 16 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Cancel",
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
class SetPlayDeadView : public View {
|
||||
public:
|
||||
SetPlayDeadView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Playdead settings"; };
|
||||
|
||||
private:
|
||||
bool entermode = false;
|
||||
uint32_t sequence { 0 };
|
||||
uint8_t keycount { 0 }, key_code { };
|
||||
char sequence_txt[11] = { 0 };
|
||||
|
||||
Text text_sequence {
|
||||
{ 64, 32, 14 * 8, 16 },
|
||||
"Enter sequence",
|
||||
};
|
||||
|
||||
Button button_enter {
|
||||
{ 16, 192, 96, 24 },
|
||||
"Enter"
|
||||
};
|
||||
Button button_cancel {
|
||||
{ 128, 192, 96, 24 },
|
||||
"Cancel"
|
||||
};
|
||||
};*/
|
||||
|
||||
/*class ModInfoView : public View {
|
||||
class SetQRCodeView : public View {
|
||||
public:
|
||||
ModInfoView(NavigationView& nav);
|
||||
SetQRCodeView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
void on_show() override;
|
||||
|
||||
std::string title() const override { return "QR Code"; };
|
||||
|
||||
private:
|
||||
void update_infos(uint8_t modn);
|
||||
|
||||
typedef struct moduleinfo_t{
|
||||
char filename[9];
|
||||
uint16_t version;
|
||||
uint32_t size;
|
||||
char md5[16];
|
||||
char name[16];
|
||||
char description[214];
|
||||
} moduleinfo_t;
|
||||
|
||||
moduleinfo_t module_list[8]; // 8 max for now
|
||||
|
||||
uint8_t modules_nb;
|
||||
|
||||
Text text_modcount {
|
||||
{ 2 * 8, 1 * 16, 18 * 8, 16 },
|
||||
"Searching..."
|
||||
Checkbox checkbox_bigger_qr {
|
||||
{ 3 * 8, 9 * 16 },
|
||||
20,
|
||||
"Show large QR code"
|
||||
};
|
||||
|
||||
OptionsField option_modules {
|
||||
{ 2 * 8, 2 * 16 },
|
||||
24,
|
||||
{
|
||||
{ "-", 0 }
|
||||
}
|
||||
Button button_save {
|
||||
{ 2 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Save"
|
||||
};
|
||||
|
||||
Text text_name {
|
||||
{ 2 * 8, 4 * 16, 5 * 8, 16 },
|
||||
"Name:"
|
||||
Button button_cancel {
|
||||
{ 16 * 8, 16 * 16, 12 * 8, 32 },
|
||||
"Cancel",
|
||||
};
|
||||
Text text_namestr {
|
||||
{ 8 * 8, 4 * 16, 16 * 8, 16 },
|
||||
"..."
|
||||
};
|
||||
Text text_size {
|
||||
{ 2 * 8, 5 * 16, 5 * 8, 16 },
|
||||
"Size:"
|
||||
};
|
||||
Text text_sizestr {
|
||||
{ 8 * 8, 5 * 16, 16 * 8, 16 },
|
||||
"..."
|
||||
};
|
||||
Text text_md5 {
|
||||
{ 2 * 8, 6 * 16, 4 * 8, 16 },
|
||||
"MD5:"
|
||||
};
|
||||
Text text_md5_a {
|
||||
{ 7 * 8, 6 * 16, 16 * 8, 16 },
|
||||
"..."
|
||||
};
|
||||
Text text_md5_b {
|
||||
{ 7 * 8, 7 * 16, 16 * 8, 16 },
|
||||
"..."
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 4 * 8, 272, 64, 24 },
|
||||
"Ok"
|
||||
};
|
||||
};*/
|
||||
};
|
||||
|
||||
class SettingsMenuView : public BtnGridView {
|
||||
public:
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Generator"; };
|
||||
std::string title() const override { return "Signal gen"; };
|
||||
|
||||
private:
|
||||
void start_tx();
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "ui_sonde.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "app_settings.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include <cstring>
|
||||
@@ -41,7 +42,10 @@ void SondeLogger::on_packet(const sonde::Packet& packet) {
|
||||
|
||||
namespace ui {
|
||||
|
||||
|
||||
SondeView::SondeView(NavigationView& nav) {
|
||||
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_sonde);
|
||||
|
||||
add_children({
|
||||
@@ -63,11 +67,21 @@ SondeView::SondeView(NavigationView& nav) {
|
||||
&text_temp,
|
||||
&text_humid,
|
||||
&geopos,
|
||||
&button_see_qr,
|
||||
&button_see_map
|
||||
});
|
||||
|
||||
// start from the frequency currently stored in the receiver_model:
|
||||
target_frequency_ = receiver_model.tuning_frequency();
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_sonde", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
target_frequency_ = app_settings.rx_frequency;
|
||||
}
|
||||
else target_frequency_ = receiver_model.tuning_frequency();
|
||||
|
||||
|
||||
field_frequency.set_value(target_frequency_);
|
||||
field_frequency.set_step(500); //euquiq: was 10000, but we are using this for fine-tunning
|
||||
@@ -98,15 +112,16 @@ SondeView::SondeView(NavigationView& nav) {
|
||||
use_crc = v;
|
||||
};
|
||||
|
||||
radio::enable({
|
||||
tuning_frequency(),
|
||||
sampling_rate,
|
||||
baseband_bandwidth,
|
||||
rf::Direction::Receive,
|
||||
receiver_model.rf_amp(),
|
||||
static_cast<int8_t>(receiver_model.lna()),
|
||||
static_cast<int8_t>(receiver_model.vga()),
|
||||
});
|
||||
receiver_model.set_tuning_frequency(tuning_frequency());
|
||||
receiver_model.set_sampling_rate(sampling_rate);
|
||||
receiver_model.set_baseband_bandwidth(baseband_bandwidth);
|
||||
receiver_model.enable(); // Before using radio::enable(), but not updating Ant.DC-Bias.
|
||||
|
||||
|
||||
// QR code with geo URI
|
||||
button_see_qr.on_select = [this, &nav](Button&) {
|
||||
nav.push<QRCodeView>(geo_uri);
|
||||
};
|
||||
|
||||
button_see_map.on_select = [this, &nav](Button&) {
|
||||
nav.push<GeoMapView>(
|
||||
@@ -144,8 +159,13 @@ SondeView::SondeView(NavigationView& nav) {
|
||||
}
|
||||
|
||||
SondeView::~SondeView() {
|
||||
// save app settings
|
||||
app_settings.rx_frequency = target_frequency_;
|
||||
settings.save("rx_sonde", &app_settings);
|
||||
|
||||
baseband::set_pitch_rssi(0, false);
|
||||
radio::disable();
|
||||
|
||||
receiver_model.disable(); // to switch off all, including DC bias.
|
||||
baseband::shutdown();
|
||||
audio::output::stop();
|
||||
}
|
||||
@@ -154,10 +174,56 @@ void SondeView::focus() {
|
||||
field_vga.focus();
|
||||
}
|
||||
|
||||
|
||||
// used to convert float to character pointer, since unfortunately function like
|
||||
// sprintf and c_str aren't supported.
|
||||
char * SondeView::float_to_char(float x, char *p)
|
||||
{
|
||||
|
||||
char *s = p + 9; // go to end of buffer
|
||||
uint16_t decimals; // variable to store the decimals
|
||||
int units; // variable to store the units (part to left of decimal place)
|
||||
if (x < 0) { // take care of negative numbers
|
||||
decimals = (int)(x * -100000) % 100000; // make 1000 for 3 decimals etc.
|
||||
units = (int)(-1 * x);
|
||||
} else { // positive numbers
|
||||
decimals = (int)(x * 100000) % 100000;
|
||||
units = (int)x;
|
||||
}
|
||||
|
||||
// TODO: more elegant solution (loop?)
|
||||
*--s = (decimals % 10) + '0';
|
||||
decimals /= 10;
|
||||
*--s = (decimals % 10) + '0';
|
||||
decimals /= 10;
|
||||
*--s = (decimals % 10) + '0';
|
||||
decimals /= 10;
|
||||
*--s = (decimals % 10) + '0';
|
||||
decimals /= 10;
|
||||
*--s = (decimals % 10) + '0';
|
||||
*--s = '.';
|
||||
|
||||
while (units > 0) {
|
||||
*--s = (units % 10) + '0';
|
||||
units /= 10;
|
||||
}
|
||||
if (x < 0) *--s = '-'; // unary minus sign for negative numbers
|
||||
return s;
|
||||
}
|
||||
|
||||
void SondeView::on_packet(const sonde::Packet &packet)
|
||||
{
|
||||
if (!use_crc || packet.crc_ok()) //euquiq: Reject bad packet if crc is on
|
||||
{
|
||||
|
||||
char buffer_lat[10] = {};
|
||||
char buffer_lon[10] = {};
|
||||
|
||||
strcpy(geo_uri, "geo:");
|
||||
strcat(geo_uri, float_to_char(gps_info.lat, buffer_lat));
|
||||
strcat(geo_uri, ",");
|
||||
strcat(geo_uri, float_to_char(gps_info.lon, buffer_lon));
|
||||
|
||||
text_signature.set(packet.type_string());
|
||||
|
||||
sonde_id = packet.serial_number(); //used also as tag on the geomap
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_rssi.hpp"
|
||||
#include "ui_qrcode.hpp"
|
||||
#include "ui_geomap.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
@@ -33,7 +34,7 @@
|
||||
#include "log_file.hpp"
|
||||
|
||||
#include "sonde_packet.hpp"
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
@@ -63,6 +64,8 @@ public:
|
||||
|
||||
std::string title() const override { return "Radiosonde RX"; };
|
||||
|
||||
|
||||
|
||||
private:
|
||||
std::unique_ptr<SondeLogger> logger { };
|
||||
uint32_t target_frequency_ { 402700000 };
|
||||
@@ -70,6 +73,11 @@ private:
|
||||
bool use_crc { false };
|
||||
bool beep { false };
|
||||
|
||||
char geo_uri[32] = {};
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
sonde::GPS_data gps_info { };
|
||||
sonde::temp_humid temp_humid_info { };
|
||||
std::string sonde_id { };
|
||||
@@ -173,9 +181,15 @@ private:
|
||||
{ 0, 12 * 16 },
|
||||
GeoPos::alt_unit::METERS
|
||||
};
|
||||
|
||||
|
||||
|
||||
Button button_see_qr {
|
||||
{ 2 * 8, 15 * 16, 12 * 8, 3 * 16 },
|
||||
"See QR"
|
||||
};
|
||||
|
||||
Button button_see_map {
|
||||
{ 8 * 8, 16 * 16, 14 * 8, 3 * 16 },
|
||||
{ 16 * 8, 15 * 16, 12 * 8, 3 * 16 },
|
||||
"See on map"
|
||||
};
|
||||
|
||||
@@ -190,7 +204,7 @@ private:
|
||||
|
||||
void on_packet(const sonde::Packet& packet);
|
||||
void on_headphone_volume_changed(int32_t v);
|
||||
|
||||
char * float_to_char(float x, char *p);
|
||||
void set_target_frequency(const uint32_t new_value);
|
||||
|
||||
uint32_t tuning_frequency() const;
|
||||
|
||||
@@ -88,6 +88,10 @@ void SSTVTXView::paint(Painter&) {
|
||||
}
|
||||
|
||||
SSTVTXView::~SSTVTXView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_sstv", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -215,6 +219,15 @@ SSTVTXView::SSTVTXView(
|
||||
options_t mode_options;
|
||||
uint32_t c;
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_sstv", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
// Search for valid bitmaps
|
||||
file_list = scan_root_files(u"/sstv", u"*.bmp");
|
||||
if (!file_list.size()) {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "sstv.hpp"
|
||||
#include "file.hpp"
|
||||
#include "bmp.hpp"
|
||||
#include "app_settings.hpp"
|
||||
|
||||
using namespace sstv;
|
||||
|
||||
@@ -58,7 +59,10 @@ private:
|
||||
NavigationView& nav_;
|
||||
|
||||
sstv_scanline scanline_buffer { };
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
bool file_error { false };
|
||||
File bmp_file { };
|
||||
bmp_header_t bmp_header { };
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace ui {
|
||||
TouchCalibrationView::TouchCalibrationView(
|
||||
NavigationView& nav
|
||||
) : nav { nav },
|
||||
calibration { touch::default_calibration() }
|
||||
calibration { touch::Calibration() }
|
||||
{
|
||||
add_children({
|
||||
&image_calibrate_0,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
* Copyright (C) 2017 NotPike
|
||||
* Copyright (C) 2022 NotPike
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
|
||||
using namespace portapack;
|
||||
using namespace encoders;
|
||||
|
||||
@@ -45,7 +46,13 @@ void TouchTunesView::stop_tx() {
|
||||
transmitter_model.disable();
|
||||
tx_mode = IDLE;
|
||||
progressbar.set_value(0);
|
||||
text_status.set("Ready");
|
||||
|
||||
// EW Mode Check
|
||||
if(check_ew.value()) {
|
||||
start_ew();
|
||||
} else {
|
||||
text_status.set("Ready");
|
||||
}
|
||||
}
|
||||
|
||||
void TouchTunesView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
@@ -72,7 +79,43 @@ void TouchTunesView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
}
|
||||
}
|
||||
|
||||
// EW (Electronic Warfare) Mode will jam the receiving jukebox
|
||||
// while still alowing you (the hacker) to send commands
|
||||
// to the target jukebox.
|
||||
// EW Mode works by transmitting a CW on 433.92MHz inbetween
|
||||
// transmission events.
|
||||
void TouchTunesView::start_ew() {
|
||||
// Radio
|
||||
transmitter_model.set_tuning_frequency(433920000);
|
||||
transmitter_model.set_sampling_rate(3072000U);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_baseband_bandwidth(3500000U);
|
||||
transmitter_model.set_tx_gain(47);
|
||||
transmitter_model.enable();
|
||||
|
||||
//UI
|
||||
text_status.set("Jamming...");
|
||||
progressbar.set_max(1);
|
||||
progressbar.set_value(1);
|
||||
|
||||
}
|
||||
|
||||
void TouchTunesView::stop_ew() {
|
||||
// Radio
|
||||
transmitter_model.disable();
|
||||
|
||||
// UI
|
||||
text_status.set("Ready");
|
||||
progressbar.set_value(0);
|
||||
}
|
||||
|
||||
void TouchTunesView::start_tx(const uint32_t button_index) {
|
||||
|
||||
// Check EW Mode
|
||||
if(check_ew.value()) {
|
||||
stop_ew();
|
||||
}
|
||||
|
||||
std::string fragments = { "" };
|
||||
size_t bit;
|
||||
uint64_t frame_data;
|
||||
@@ -136,6 +179,7 @@ TouchTunesView::TouchTunesView(
|
||||
&labels,
|
||||
&field_pin,
|
||||
&check_scan,
|
||||
&check_ew,
|
||||
&text_status,
|
||||
&progressbar
|
||||
});
|
||||
@@ -145,6 +189,15 @@ TouchTunesView::TouchTunesView(
|
||||
field_pin.on_change = [this](int32_t v) {
|
||||
pin = v;
|
||||
};
|
||||
|
||||
// EW Mode
|
||||
check_ew.on_select = [this](Checkbox&, bool v) {
|
||||
if(v){
|
||||
start_ew();
|
||||
} else {
|
||||
stop_ew();
|
||||
}
|
||||
};
|
||||
|
||||
const auto button_fn = [this](Button& button) {
|
||||
start_tx(button.id);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
* Copyright (C) 2018 NotPike
|
||||
* Copyright (C) 2022 NotPike
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -127,6 +127,8 @@ private:
|
||||
void start_tx(const uint32_t button_index);
|
||||
void stop_tx();
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
void start_ew();
|
||||
void stop_ew();
|
||||
|
||||
struct remote_layout_t {
|
||||
Point position;
|
||||
@@ -191,11 +193,17 @@ private:
|
||||
};
|
||||
|
||||
Checkbox check_scan {
|
||||
{ 2 * 8, 27 * 8 },
|
||||
{ 2 * 8, 25 * 8 },
|
||||
4,
|
||||
"Scan"
|
||||
};
|
||||
|
||||
Checkbox check_ew {
|
||||
{ 2 * 8, 29 * 8 },
|
||||
4,
|
||||
"EW Mode"
|
||||
};
|
||||
|
||||
Text text_status {
|
||||
{ 2 * 8, 33 * 8, 128, 16 },
|
||||
"Ready"
|
||||
|
||||
@@ -1285,6 +1285,28 @@ static constexpr Bitmap bitmap_icon_previous {
|
||||
{ 16, 16 }, bitmap_icon_previous_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_qr_code_data[] = {
|
||||
0x00, 0x00,
|
||||
0xFE, 0x7E,
|
||||
0xC6, 0x62,
|
||||
0xFA, 0x5A,
|
||||
0xFA, 0x5A,
|
||||
0xDA, 0x5A,
|
||||
0xFE, 0x7E,
|
||||
0x7E, 0x7E,
|
||||
0x00, 0x00,
|
||||
0xFE, 0x46,
|
||||
0xC2, 0x06,
|
||||
0xFA, 0x18,
|
||||
0xFA, 0x18,
|
||||
0xC6, 0x60,
|
||||
0xFE, 0x62,
|
||||
0x00, 0x00,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_qr_code {
|
||||
{ 16, 16 }, bitmap_icon_qr_code_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_rds_data[] = {
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2022 Arjan Onwezen
|
||||
*
|
||||
* 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 "database.hpp"
|
||||
#include "file.hpp"
|
||||
#include <cstring>
|
||||
|
||||
namespace std {
|
||||
|
||||
int database::retrieve_mid_record(MidDBRecord* record, std::string search_term){
|
||||
|
||||
file_path = "AIS/mids.db";
|
||||
index_item_length = 4;
|
||||
record_length = 32;
|
||||
|
||||
result = std::database::retrieve_record(file_path, index_item_length, record_length, record, search_term);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
int database::retrieve_airline_record(AirlinesDBRecord* record, std::string search_term){
|
||||
|
||||
file_path = "ADSB/airlines.db";
|
||||
index_item_length = 4;
|
||||
record_length = 64;
|
||||
|
||||
result = std::database::retrieve_record(file_path, index_item_length, record_length, record, search_term);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
int database::retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term){
|
||||
|
||||
file_path = "ADSB/icao24.db";
|
||||
index_item_length = 7;
|
||||
record_length = 146;
|
||||
|
||||
result = std::database::retrieve_record(file_path, index_item_length, record_length, record, search_term);
|
||||
|
||||
return(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int database::retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term)
|
||||
{
|
||||
|
||||
auto result = db_file.open(file_path);
|
||||
if (!result.is_valid()) {
|
||||
number_of_records = (db_file.size() / (index_item_length + record_length)); // determine number of records in file
|
||||
// binary search tree
|
||||
int first = 0, // First search element
|
||||
last = number_of_records - 1, // Last search element
|
||||
middle, // Mid point of search
|
||||
position = -1; // Position of search value
|
||||
|
||||
while (!found && first <= last) {
|
||||
middle = (first + last) / 2; // Calculate mid point
|
||||
db_file.seek(middle * index_item_length);
|
||||
db_file.read(file_buffer, search_term.length());
|
||||
if (file_buffer == search_term) { // If value is found at mid
|
||||
found = true;
|
||||
position = middle;
|
||||
}
|
||||
else if (file_buffer > search_term) // If value is in lower half
|
||||
last = middle - 1;
|
||||
else
|
||||
first = middle + 1; // If value is in upper half
|
||||
}
|
||||
|
||||
if(found == true) {
|
||||
|
||||
db_file.seek((number_of_records * index_item_length) + (position * record_length)); // seek starting after index
|
||||
db_file.read(record, record_length);
|
||||
return(DATABASE_RECORD_FOUND);
|
||||
}
|
||||
else {
|
||||
return(DATABASE_RECORD_NOT_FOUND);
|
||||
}
|
||||
|
||||
}
|
||||
else return(DATABASE_NOT_FOUND);
|
||||
|
||||
}
|
||||
|
||||
|
||||
} /* namespace std */
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2022 Arjan Onwezen
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __DATABASE_H__
|
||||
#define __DATABASE_H__
|
||||
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "file.hpp"
|
||||
|
||||
|
||||
|
||||
namespace std {
|
||||
class database {
|
||||
|
||||
|
||||
|
||||
public:
|
||||
|
||||
#define DATABASE_RECORD_FOUND 0 // record found in database
|
||||
#define DATABASE_NOT_FOUND -1 // database not found / could not be opened
|
||||
#define DATABASE_RECORD_NOT_FOUND -2 // record could not be found in database
|
||||
|
||||
struct MidDBRecord {
|
||||
char country[32]; // country name
|
||||
};
|
||||
|
||||
int retrieve_mid_record(MidDBRecord* record, std::string search_term);
|
||||
|
||||
struct AirlinesDBRecord {
|
||||
char airline[32]; // airline name
|
||||
char country[32]; // country name
|
||||
};
|
||||
|
||||
int retrieve_airline_record(AirlinesDBRecord* record, std::string search_term);
|
||||
|
||||
struct AircraftDBRecord {
|
||||
char aircraft_registration[9]; // aircraft registration
|
||||
char aircraft_manufacturer[33]; // aircraft manufacturer
|
||||
char aircraft_model[33]; // aircraft model
|
||||
char icao_type[5]; // ICAO type descripton or when unavailable ICAO type designator
|
||||
char aircraft_owner[33]; // aircraft owner
|
||||
char aircraft_operator[33]; // aircraft operator
|
||||
|
||||
};
|
||||
|
||||
int retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term);
|
||||
|
||||
private:
|
||||
string file_path = ""; // path inclusing filename
|
||||
int index_item_length = 0; // length of index item
|
||||
int record_length = 0; // length of record
|
||||
|
||||
File db_file { };
|
||||
int number_of_records = 0;
|
||||
int position = 0;
|
||||
|
||||
char file_buffer[32] { 0 };
|
||||
bool found = false;
|
||||
|
||||
int result = 0;
|
||||
|
||||
int retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term);
|
||||
|
||||
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
|
||||
|
||||
#endif/*__DATABASE_H__*/
|
||||
@@ -226,15 +226,17 @@ void EventDispatcher::handle_rtc_tick() {
|
||||
|
||||
portapack::temperature_logger.second_tick();
|
||||
|
||||
uint32_t backlight_timer = portapack::persistent_memory::config_backlight_timer();
|
||||
if (backlight_timer) {
|
||||
if (portapack::bl_tick_counter == backlight_timer)
|
||||
const auto backlight_timer = portapack::persistent_memory::config_backlight_timer();
|
||||
if (backlight_timer.timeout_enabled()) {
|
||||
if (portapack::bl_tick_counter == backlight_timer.timeout_seconds())
|
||||
set_display_sleep(true);
|
||||
else
|
||||
portapack::bl_tick_counter++;
|
||||
}
|
||||
|
||||
rtc_time::on_tick_second();
|
||||
|
||||
portapack::persistent_memory::cache::persist();
|
||||
}
|
||||
|
||||
ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent event) {
|
||||
|
||||
@@ -197,12 +197,12 @@ std::vector<std::filesystem::path> scan_root_directories(const std::filesystem::
|
||||
return directory_list;
|
||||
}
|
||||
|
||||
void delete_file(const std::filesystem::path& file_path) {
|
||||
f_unlink(reinterpret_cast<const TCHAR*>(file_path.c_str()));
|
||||
uint32_t delete_file(const std::filesystem::path& file_path) {
|
||||
return f_unlink(reinterpret_cast<const TCHAR*>(file_path.c_str()));
|
||||
}
|
||||
|
||||
void rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name) {
|
||||
f_rename(reinterpret_cast<const TCHAR*>(file_path.c_str()), reinterpret_cast<const TCHAR*>(new_name.c_str()));
|
||||
uint32_t rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name) {
|
||||
return f_rename(reinterpret_cast<const TCHAR*>(file_path.c_str()), reinterpret_cast<const TCHAR*>(new_name.c_str()));
|
||||
}
|
||||
|
||||
FATTimestamp file_created_date(const std::filesystem::path& file_path) {
|
||||
|
||||
@@ -238,8 +238,8 @@ struct FATTimestamp {
|
||||
uint16_t FAT_time;
|
||||
};
|
||||
|
||||
void delete_file(const std::filesystem::path& file_path);
|
||||
void rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name);
|
||||
uint32_t delete_file(const std::filesystem::path& file_path);
|
||||
uint32_t rename_file(const std::filesystem::path& file_path, const std::filesystem::path& new_name);
|
||||
FATTimestamp file_created_date(const std::filesystem::path& file_path);
|
||||
uint32_t make_new_directory(const std::filesystem::path& dir_path);
|
||||
|
||||
|
||||
@@ -51,15 +51,15 @@ enum freqman_entry_type {
|
||||
//Entry step placed for AlainD freqman version (or any other enhanced version)
|
||||
enum freqman_entry_step {
|
||||
STEP_DEF = 0, // default
|
||||
AM_US, // 10 Khz AM/CB
|
||||
AM_EUR, // 9 Khz LW/MW
|
||||
NFM_1, // 12,5 Khz (Analogic PMR 446)
|
||||
NFM_2, // 6,25 Khz (Digital PMR 446)
|
||||
FM_1, // 100 Khz
|
||||
FM_2, // 50 Khz
|
||||
N_1, // 25 Khz
|
||||
N_2, // 250 Khz
|
||||
AIRBAND, // AIRBAND 8,33 Khz
|
||||
AM_US, // 10 kHz AM/CB
|
||||
AM_EUR, // 9 kHz LW/MW
|
||||
NFM_1, // 12,5 kHz (Analogic PMR 446)
|
||||
NFM_2, // 6,25 kHz (Digital PMR 446)
|
||||
FM_1, // 100 kHz
|
||||
FM_2, // 50 kHz
|
||||
N_1, // 25 kHz
|
||||
N_2, // 250 kHz
|
||||
AIRBAND, // AIRBAND 8,33 kHz
|
||||
ERROR_STEP
|
||||
};
|
||||
|
||||
|
||||
@@ -167,14 +167,15 @@ int main(void) {
|
||||
if( portapack::init() ) {
|
||||
portapack::display.init();
|
||||
|
||||
sdcStart(&SDCD1, nullptr);
|
||||
// sdcStart(&SDCD1, nullptr); // Commented out as now happens in portapack.cpp
|
||||
|
||||
controls_init();
|
||||
// controls_init(); // Commented out as now happens in portapack.cpp
|
||||
lcd_frame_sync_configure();
|
||||
rtc_interrupt_enable();
|
||||
|
||||
event_loop();
|
||||
|
||||
|
||||
sdcDisconnect(&SDCD1);
|
||||
sdcStop(&SDCD1);
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "hackrf_gpio.hpp"
|
||||
using namespace hackrf::one;
|
||||
|
||||
|
||||
#include "clock_manager.hpp"
|
||||
#include "event_m0.hpp"
|
||||
|
||||
@@ -45,6 +46,11 @@ using asahi_kasei::ak4951::AK4951;
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
#include "optional.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
#include "sd_card.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
namespace portapack {
|
||||
|
||||
@@ -175,22 +181,102 @@ enum class PortaPackModel {
|
||||
R2_20170522,
|
||||
};
|
||||
|
||||
static bool save_config(int8_t value){
|
||||
persistent_memory::set_config_cpld(value);
|
||||
if(sd_card::status() == sd_card::Status::Mounted){
|
||||
make_new_directory("/hardware");
|
||||
File file;
|
||||
auto sucess = file.create("/hardware/settings.txt");
|
||||
if(!sucess.is_valid()) {
|
||||
file.write_line(to_string_dec_uint(value));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int read_file(std::string name) {
|
||||
std::string return_string = "";
|
||||
File file;
|
||||
auto success = file.open(name);
|
||||
|
||||
if(!success.is_valid()) {
|
||||
char one_char[1];
|
||||
for(size_t pointer = 0; pointer < file.size() ; pointer++) {
|
||||
file.seek(pointer);
|
||||
file.read(one_char, 1);
|
||||
return_string += one_char[0];
|
||||
}
|
||||
return std::stoi(return_string);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int load_config(){
|
||||
static Optional<int> config_value;
|
||||
if(!config_value.is_valid()){
|
||||
int8_t value = portapack::persistent_memory::config_cpld();
|
||||
if((value <= 0 || value >= 5) && sd_card::status() == sd_card::Status::Mounted){
|
||||
int data = read_file("/hardware/settings.txt");
|
||||
if(data != -1) {
|
||||
config_value = data;
|
||||
}
|
||||
} else {
|
||||
config_value = value;
|
||||
}
|
||||
}
|
||||
return config_value.value();
|
||||
}
|
||||
|
||||
|
||||
static PortaPackModel portapack_model() {
|
||||
static Optional<PortaPackModel> model;
|
||||
|
||||
if( !model.is_valid() ) {
|
||||
/*For the time being, it is impossible to distinguish the hardware of R1 and R2 from the software level*/
|
||||
/*At this point, I2c is not ready.*/
|
||||
//if( audio_codec_wm8731.detected() ) {
|
||||
// model = PortaPackModel::R1_20150901;
|
||||
//} else {
|
||||
const auto switches_state = get_switches_state();
|
||||
if (switches_state[(size_t)ui::KeyEvent::Up]){
|
||||
save_config(1);
|
||||
// model = PortaPackModel::R2_20170522; // Commented these out as they should be set down below anyway
|
||||
}
|
||||
else if (switches_state[(size_t)ui::KeyEvent::Down]){
|
||||
save_config(2);
|
||||
// model = PortaPackModel::R1_20150901;
|
||||
}
|
||||
else if (switches_state[(size_t)ui::KeyEvent::Left]){
|
||||
save_config(3);
|
||||
// model = PortaPackModel::R1_20150901;
|
||||
}
|
||||
else if (switches_state[(size_t)ui::KeyEvent::Right]){
|
||||
save_config(4);
|
||||
// model = PortaPackModel::R2_20170522;
|
||||
}
|
||||
else if (switches_state[(size_t)ui::KeyEvent::Select]){
|
||||
save_config(0);
|
||||
}
|
||||
|
||||
|
||||
if (load_config() == 1) {
|
||||
model = PortaPackModel::R2_20170522;
|
||||
//}
|
||||
} else if (load_config() == 2) {
|
||||
model = PortaPackModel::R1_20150901;
|
||||
} else if (load_config() == 3) {
|
||||
model = PortaPackModel::R1_20150901;
|
||||
} else if (load_config() == 4) {
|
||||
model = PortaPackModel::R2_20170522;
|
||||
} else {
|
||||
if( audio_codec_wm8731.detected() ) {
|
||||
model = PortaPackModel::R1_20150901; // H1R1
|
||||
} else {
|
||||
model = PortaPackModel::R2_20170522; // H1R2, H2, H2+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model.value();
|
||||
}
|
||||
|
||||
//audio_codec_wm8731 = H1R1 & H2+
|
||||
//audio_codec_ak4951 = H1R2
|
||||
|
||||
static audio::Codec* portapack_audio_codec() {
|
||||
/* I2C ready OK, Automatic recognition of audio chip */
|
||||
return (audio_codec_wm8731.detected())
|
||||
@@ -201,15 +287,14 @@ static audio::Codec* portapack_audio_codec() {
|
||||
|
||||
static const portapack::cpld::Config& portapack_cpld_config() {
|
||||
return (portapack_model() == PortaPackModel::R2_20170522)
|
||||
? portapack::cpld::rev_20170522::config
|
||||
: portapack::cpld::rev_20150901::config
|
||||
;
|
||||
? portapack::cpld::rev_20170522::config
|
||||
: portapack::cpld::rev_20150901::config;
|
||||
}
|
||||
|
||||
Backlight* backlight() {
|
||||
return (portapack_model() == PortaPackModel::R2_20170522)
|
||||
? static_cast<portapack::Backlight*>(&backlight_cat4004)
|
||||
: static_cast<portapack::Backlight*>(&backlight_on_off);
|
||||
? static_cast<portapack::Backlight*>(&backlight_cat4004) // R2_20170522
|
||||
: static_cast<portapack::Backlight*>(&backlight_on_off); // R1_20150901
|
||||
}
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
@@ -318,14 +403,16 @@ bool init() {
|
||||
|
||||
i2c0.start(i2c_config_boot_clock);
|
||||
|
||||
if( !portapack::cpld::update_if_necessary(portapack_cpld_config()) ) {
|
||||
shutdown_base();
|
||||
return false;
|
||||
}
|
||||
// Keeping this here for now incase we need to revert
|
||||
// if( !portapack::cpld::update_if_necessary(portapack_cpld_config()) ) {
|
||||
// shutdown_base();
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if( !hackrf::cpld::load_sram() ) {
|
||||
chSysHalt();
|
||||
}
|
||||
// if( !hackrf::cpld::load_sram() ) {
|
||||
// chSysHalt();
|
||||
// }
|
||||
chThdSleepMilliseconds(100);
|
||||
|
||||
configure_pins_portapack();
|
||||
|
||||
@@ -335,8 +422,9 @@ bool init() {
|
||||
|
||||
i2c0.stop();
|
||||
|
||||
set_clock_config(clock_config_irc);
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
set_clock_config(clock_config_irc);
|
||||
cgu::pll1::disable();
|
||||
|
||||
/* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4
|
||||
@@ -376,21 +464,49 @@ bool init() {
|
||||
cgu::pll1::direct();
|
||||
|
||||
i2c0.start(i2c_config_fast_clock);
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
/* Cache some configuration data from persistent memory. */
|
||||
persistent_memory::cache::init();
|
||||
|
||||
touch::adc::init();
|
||||
controls_init();
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
clock_manager.set_reference_ppb(persistent_memory::correction_ppb());
|
||||
|
||||
audio::init(portapack_audio_codec());
|
||||
|
||||
clock_manager.enable_first_if_clock();
|
||||
clock_manager.enable_second_if_clock();
|
||||
clock_manager.enable_codec_clocks();
|
||||
radio::init();
|
||||
|
||||
touch::adc::init();
|
||||
sdcStart(&SDCD1, nullptr);
|
||||
sd_card::poll_inserted();
|
||||
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
if( !portapack::cpld::update_if_necessary(portapack_cpld_config()) ) {
|
||||
chThdSleepMilliseconds(10);
|
||||
// If using a "2021/12 QFP100", press and hold the left button while booting. Should only need to do once.
|
||||
if (load_config() != 3 && load_config() != 4){
|
||||
shutdown_base();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( !hackrf::cpld::load_sram() ) {
|
||||
chSysHalt();
|
||||
}
|
||||
|
||||
chThdSleepMilliseconds(10); // This delay seems to solve white noise audio issues
|
||||
|
||||
LPC_CREG->DMAMUX = portapack::gpdma_mux;
|
||||
gpdma::controller.enable();
|
||||
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
audio::init(portapack_audio_codec());
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,876 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* This library is written and maintained by Richard Moore.
|
||||
* Major parts were derived from Project Nayuki's library.
|
||||
*
|
||||
* Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode)
|
||||
* Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
|
||||
* heavily inspired and compared against.
|
||||
*
|
||||
* See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
|
||||
*/
|
||||
|
||||
#include "qrcodegen.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* #pragma mark - Error Correction Lookup tables */
|
||||
|
||||
#if LOCK_VERSION == 0
|
||||
|
||||
static const uint16_t NUM_ERROR_CORRECTION_CODEWORDS[4][40] = {
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
|
||||
{ 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium
|
||||
{ 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low
|
||||
{ 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High
|
||||
{ 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile
|
||||
};
|
||||
|
||||
static const uint8_t NUM_ERROR_CORRECTION_BLOCKS[4][40] = {
|
||||
// Version: (note that index 0 is for padding, and is set to an illegal value)
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
|
||||
{ 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium
|
||||
{ 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low
|
||||
{ 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High
|
||||
{ 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile
|
||||
};
|
||||
|
||||
static const uint16_t NUM_RAW_DATA_MODULES[40] = {
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||
208, 359, 567, 807, 1079, 1383, 1568, 1936, 2336, 2768, 3232, 3728, 4256, 4651, 5243, 5867, 6523,
|
||||
// 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
7211, 7931, 8683, 9252, 10068, 10916, 11796, 12708, 13652, 14628, 15371, 16411, 17483, 18587,
|
||||
// 32, 33, 34, 35, 36, 37, 38, 39, 40
|
||||
19723, 20891, 22091, 23008, 24272, 25568, 26896, 28256, 29648
|
||||
};
|
||||
|
||||
// @TODO: Put other LOCK_VERSIONS here
|
||||
#elif LOCK_VERSION == 3
|
||||
|
||||
static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4] = {
|
||||
26, 15, 44, 36
|
||||
};
|
||||
|
||||
static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4] = {
|
||||
1, 1, 2, 2
|
||||
};
|
||||
|
||||
static const uint16_t NUM_RAW_DATA_MODULES = 567;
|
||||
|
||||
#else
|
||||
|
||||
#error Unsupported LOCK_VERSION (add it...)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static int max(int a, int b) {
|
||||
if (a > b) { return a; }
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
static int abs(int value) {
|
||||
if (value < 0) { return -value; }
|
||||
return value;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* #pragma mark - Mode testing and conversion */
|
||||
|
||||
static int8_t getAlphanumeric(char c) {
|
||||
|
||||
if (c >= '0' && c <= '9') { return (c - '0'); }
|
||||
if (c >= 'A' && c <= 'Z') { return (c - 'A' + 10); }
|
||||
|
||||
switch (c) {
|
||||
case ' ': return 36;
|
||||
case '$': return 37;
|
||||
case '%': return 38;
|
||||
case '*': return 39;
|
||||
case '+': return 40;
|
||||
case '-': return 41;
|
||||
case '.': return 42;
|
||||
case '/': return 43;
|
||||
case ':': return 44;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool isAlphanumeric(const char *text, uint16_t length) {
|
||||
while (length != 0) {
|
||||
if (getAlphanumeric(text[--length]) == -1) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool isNumeric(const char *text, uint16_t length) {
|
||||
while (length != 0) {
|
||||
char c = text[--length];
|
||||
if (c < '0' || c > '9') { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* #pragma mark - Counting */
|
||||
|
||||
// We store the following tightly packed (less 8) in modeInfo
|
||||
// <=9 <=26 <= 40
|
||||
// NUMERIC ( 10, 12, 14);
|
||||
// ALPHANUMERIC ( 9, 11, 13);
|
||||
// BYTE ( 8, 16, 16);
|
||||
static char getModeBits(uint8_t version, uint8_t mode) {
|
||||
// Note: We use 15 instead of 16; since 15 doesn't exist and we cannot store 16 (8 + 8) in 3 bits
|
||||
// hex(int("".join(reversed([('00' + bin(x - 8)[2:])[-3:] for x in [10, 9, 8, 12, 11, 15, 14, 13, 15]])), 2))
|
||||
unsigned int modeInfo = 0x7bbb80a;
|
||||
|
||||
#if LOCK_VERSION == 0 || LOCK_VERSION > 9
|
||||
if (version > 9) { modeInfo >>= 9; }
|
||||
#endif
|
||||
|
||||
#if LOCK_VERSION == 0 || LOCK_VERSION > 26
|
||||
if (version > 26) { modeInfo >>= 9; }
|
||||
#endif
|
||||
|
||||
char result = 8 + ((modeInfo >> (3 * mode)) & 0x07);
|
||||
if (result == 15) { result = 16; }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* #pragma mark - BitBucket */
|
||||
|
||||
typedef struct BitBucket {
|
||||
uint32_t bitOffsetOrWidth;
|
||||
uint16_t capacityBytes;
|
||||
uint8_t *data;
|
||||
} BitBucket;
|
||||
|
||||
/*
|
||||
void bb_dump(BitBucket *bitBuffer) {
|
||||
printf("Buffer: ");
|
||||
for (uint32_t i = 0; i < bitBuffer->capacityBytes; i++) {
|
||||
printf("%02x", bitBuffer->data[i]);
|
||||
if ((i % 4) == 3) { printf(" "); }
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
*/
|
||||
|
||||
static uint16_t bb_getGridSizeBytes(uint8_t size) {
|
||||
return (((size * size) + 7) / 8);
|
||||
}
|
||||
|
||||
static uint16_t bb_getBufferSizeBytes(uint32_t bits) {
|
||||
return ((bits + 7) / 8);
|
||||
}
|
||||
|
||||
static void bb_initBuffer(BitBucket *bitBuffer, uint8_t *data, int32_t capacityBytes) {
|
||||
bitBuffer->bitOffsetOrWidth = 0;
|
||||
bitBuffer->capacityBytes = capacityBytes;
|
||||
bitBuffer->data = data;
|
||||
|
||||
memset(data, 0, bitBuffer->capacityBytes);
|
||||
}
|
||||
|
||||
static void bb_initGrid(BitBucket *bitGrid, uint8_t *data, uint8_t size) {
|
||||
bitGrid->bitOffsetOrWidth = size;
|
||||
bitGrid->capacityBytes = bb_getGridSizeBytes(size);
|
||||
bitGrid->data = data;
|
||||
|
||||
memset(data, 0, bitGrid->capacityBytes);
|
||||
}
|
||||
|
||||
static void bb_appendBits(BitBucket *bitBuffer, uint32_t val, uint8_t length) {
|
||||
uint32_t offset = bitBuffer->bitOffsetOrWidth;
|
||||
for (int8_t i = length - 1; i >= 0; i--, offset++) {
|
||||
bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
|
||||
}
|
||||
bitBuffer->bitOffsetOrWidth = offset;
|
||||
}
|
||||
/*
|
||||
void bb_setBits(BitBucket *bitBuffer, uint32_t val, int offset, uint8_t length) {
|
||||
for (int8_t i = length - 1; i >= 0; i--, offset++) {
|
||||
bitBuffer->data[offset >> 3] |= ((val >> i) & 1) << (7 - (offset & 7));
|
||||
}
|
||||
}
|
||||
*/
|
||||
static void bb_setBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool on) {
|
||||
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
|
||||
uint8_t mask = 1 << (7 - (offset & 0x07));
|
||||
if (on) {
|
||||
bitGrid->data[offset >> 3] |= mask;
|
||||
} else {
|
||||
bitGrid->data[offset >> 3] &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
static void bb_invertBit(BitBucket *bitGrid, uint8_t x, uint8_t y, bool invert) {
|
||||
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
|
||||
uint8_t mask = 1 << (7 - (offset & 0x07));
|
||||
bool on = ((bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0);
|
||||
if (on ^ invert) {
|
||||
bitGrid->data[offset >> 3] |= mask;
|
||||
} else {
|
||||
bitGrid->data[offset >> 3] &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
static bool bb_getBit(BitBucket *bitGrid, uint8_t x, uint8_t y) {
|
||||
uint32_t offset = y * bitGrid->bitOffsetOrWidth + x;
|
||||
return (bitGrid->data[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
|
||||
}
|
||||
|
||||
|
||||
/* #pragma mark - Drawing Patterns */
|
||||
|
||||
// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical
|
||||
// properties, calling applyMask(m) twice with the same value is equivalent to no change at all.
|
||||
// This means it is possible to apply a mask, undo it, and try another mask. Note that a final
|
||||
// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).
|
||||
static void applyMask(BitBucket *modules, BitBucket *isFunction, uint8_t mask) {
|
||||
uint8_t size = modules->bitOffsetOrWidth;
|
||||
|
||||
for (uint8_t y = 0; y < size; y++) {
|
||||
for (uint8_t x = 0; x < size; x++) {
|
||||
if (bb_getBit(isFunction, x, y)) { continue; }
|
||||
|
||||
bool invert = 0;
|
||||
switch (mask) {
|
||||
case 0: invert = (x + y) % 2 == 0; break;
|
||||
case 1: invert = y % 2 == 0; break;
|
||||
case 2: invert = x % 3 == 0; break;
|
||||
case 3: invert = (x + y) % 3 == 0; break;
|
||||
case 4: invert = (x / 3 + y / 2) % 2 == 0; break;
|
||||
case 5: invert = x * y % 2 + x * y % 3 == 0; break;
|
||||
case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break;
|
||||
case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break;
|
||||
}
|
||||
bb_invertBit(modules, x, y, invert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void setFunctionModule(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y, bool on) {
|
||||
bb_setBit(modules, x, y, on);
|
||||
bb_setBit(isFunction, x, y, true);
|
||||
}
|
||||
|
||||
// Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).
|
||||
static void drawFinderPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
|
||||
uint8_t size = modules->bitOffsetOrWidth;
|
||||
|
||||
for (int8_t i = -4; i <= 4; i++) {
|
||||
for (int8_t j = -4; j <= 4; j++) {
|
||||
uint8_t dist = max(abs(i), abs(j)); // Chebyshev/infinity norm
|
||||
int16_t xx = x + j, yy = y + i;
|
||||
if (0 <= xx && xx < size && 0 <= yy && yy < size) {
|
||||
setFunctionModule(modules, isFunction, xx, yy, dist != 2 && dist != 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draws a 5*5 alignment pattern, with the center module at (x, y).
|
||||
static void drawAlignmentPattern(BitBucket *modules, BitBucket *isFunction, uint8_t x, uint8_t y) {
|
||||
for (int8_t i = -2; i <= 2; i++) {
|
||||
for (int8_t j = -2; j <= 2; j++) {
|
||||
setFunctionModule(modules, isFunction, x + j, y + i, max(abs(i), abs(j)) != 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draws two copies of the format bits (with its own error correction code)
|
||||
// based on the given mask and this object's error correction level field.
|
||||
static void drawFormatBits(BitBucket *modules, BitBucket *isFunction, uint8_t ecc, uint8_t mask) {
|
||||
|
||||
uint8_t size = modules->bitOffsetOrWidth;
|
||||
|
||||
// Calculate error correction code and pack bits
|
||||
uint32_t data = ecc << 3 | mask; // errCorrLvl is uint2, mask is uint3
|
||||
uint32_t rem = data;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
rem = (rem << 1) ^ ((rem >> 9) * 0x537);
|
||||
}
|
||||
|
||||
data = data << 10 | rem;
|
||||
data ^= 0x5412; // uint15
|
||||
|
||||
// Draw first copy
|
||||
for (uint8_t i = 0; i <= 5; i++) {
|
||||
setFunctionModule(modules, isFunction, 8, i, ((data >> i) & 1) != 0);
|
||||
}
|
||||
|
||||
setFunctionModule(modules, isFunction, 8, 7, ((data >> 6) & 1) != 0);
|
||||
setFunctionModule(modules, isFunction, 8, 8, ((data >> 7) & 1) != 0);
|
||||
setFunctionModule(modules, isFunction, 7, 8, ((data >> 8) & 1) != 0);
|
||||
|
||||
for (int8_t i = 9; i < 15; i++) {
|
||||
setFunctionModule(modules, isFunction, 14 - i, 8, ((data >> i) & 1) != 0);
|
||||
}
|
||||
|
||||
// Draw second copy
|
||||
for (int8_t i = 0; i <= 7; i++) {
|
||||
setFunctionModule(modules, isFunction, size - 1 - i, 8, ((data >> i) & 1) != 0);
|
||||
}
|
||||
|
||||
for (int8_t i = 8; i < 15; i++) {
|
||||
setFunctionModule(modules, isFunction, 8, size - 15 + i, ((data >> i) & 1) != 0);
|
||||
}
|
||||
|
||||
setFunctionModule(modules, isFunction, 8, size - 8, true);
|
||||
}
|
||||
|
||||
|
||||
// Draws two copies of the version bits (with its own error correction code),
|
||||
// based on this object's version field (which only has an effect for 7 <= version <= 40).
|
||||
static void drawVersion(BitBucket *modules, BitBucket *isFunction, uint8_t version) {
|
||||
|
||||
int8_t size = modules->bitOffsetOrWidth;
|
||||
|
||||
#if LOCK_VERSION != 0 && LOCK_VERSION < 7
|
||||
return;
|
||||
|
||||
#else
|
||||
if (version < 7) { return; }
|
||||
|
||||
// Calculate error correction code and pack bits
|
||||
uint32_t rem = version; // version is uint6, in the range [7, 40]
|
||||
for (uint8_t i = 0; i < 12; i++) {
|
||||
rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
|
||||
}
|
||||
|
||||
uint32_t data = version << 12 | rem; // uint18
|
||||
|
||||
// Draw two copies
|
||||
for (uint8_t i = 0; i < 18; i++) {
|
||||
bool bit = ((data >> i) & 1) != 0;
|
||||
uint8_t a = size - 11 + i % 3, b = i / 3;
|
||||
setFunctionModule(modules, isFunction, a, b, bit);
|
||||
setFunctionModule(modules, isFunction, b, a, bit);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
static void drawFunctionPatterns(BitBucket *modules, BitBucket *isFunction, uint8_t version, uint8_t ecc) {
|
||||
|
||||
uint8_t size = modules->bitOffsetOrWidth;
|
||||
|
||||
// Draw the horizontal and vertical timing patterns
|
||||
for (uint8_t i = 0; i < size; i++) {
|
||||
setFunctionModule(modules, isFunction, 6, i, i % 2 == 0);
|
||||
setFunctionModule(modules, isFunction, i, 6, i % 2 == 0);
|
||||
}
|
||||
|
||||
// Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules)
|
||||
drawFinderPattern(modules, isFunction, 3, 3);
|
||||
drawFinderPattern(modules, isFunction, size - 4, 3);
|
||||
drawFinderPattern(modules, isFunction, 3, size - 4);
|
||||
|
||||
#if LOCK_VERSION == 0 || LOCK_VERSION > 1
|
||||
|
||||
if (version > 1) {
|
||||
|
||||
// Draw the numerous alignment patterns
|
||||
|
||||
uint8_t alignCount = version / 7 + 2;
|
||||
uint8_t step;
|
||||
if (version != 32) {
|
||||
step = (version * 4 + alignCount * 2 + 1) / (2 * alignCount - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2
|
||||
} else { // C-C-C-Combo breaker!
|
||||
step = 26;
|
||||
}
|
||||
|
||||
uint8_t alignPositionIndex = alignCount - 1;
|
||||
uint8_t alignPosition[alignCount];
|
||||
|
||||
alignPosition[0] = 6;
|
||||
|
||||
uint8_t size = version * 4 + 17;
|
||||
for (uint8_t i = 0, pos = size - 7; i < alignCount - 1; i++, pos -= step) {
|
||||
alignPosition[alignPositionIndex--] = pos;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < alignCount; i++) {
|
||||
for (uint8_t j = 0; j < alignCount; j++) {
|
||||
if ((i == 0 && j == 0) || (i == 0 && j == alignCount - 1) || (i == alignCount - 1 && j == 0)) {
|
||||
continue; // Skip the three finder corners
|
||||
} else {
|
||||
drawAlignmentPattern(modules, isFunction, alignPosition[i], alignPosition[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Draw configuration data
|
||||
drawFormatBits(modules, isFunction, ecc, 0); // Dummy mask value; overwritten later in the constructor
|
||||
drawVersion(modules, isFunction, version);
|
||||
}
|
||||
|
||||
|
||||
// Draws the given sequence of 8-bit codewords (data and error correction) onto the entire
|
||||
// data area of this QR Code symbol. Function modules need to be marked off before this is called.
|
||||
static void drawCodewords(BitBucket *modules, BitBucket *isFunction, BitBucket *codewords) {
|
||||
|
||||
uint32_t bitLength = codewords->bitOffsetOrWidth;
|
||||
uint8_t *data = codewords->data;
|
||||
|
||||
uint8_t size = modules->bitOffsetOrWidth;
|
||||
|
||||
// Bit index into the data
|
||||
uint32_t i = 0;
|
||||
|
||||
// Do the funny zigzag scan
|
||||
for (int16_t right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair
|
||||
if (right == 6) { right = 5; }
|
||||
|
||||
for (uint8_t vert = 0; vert < size; vert++) { // Vertical counter
|
||||
for (int j = 0; j < 2; j++) {
|
||||
uint8_t x = right - j; // Actual x coordinate
|
||||
bool upwards = ((right & 2) == 0) ^ (x < 6);
|
||||
uint8_t y = upwards ? size - 1 - vert : vert; // Actual y coordinate
|
||||
if (!bb_getBit(isFunction, x, y) && i < bitLength) {
|
||||
bb_setBit(modules, x, y, ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0);
|
||||
i++;
|
||||
}
|
||||
// If there are any remainder bits (0 to 7), they are already
|
||||
// set to 0/false/white when the grid of modules was initialized
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* #pragma mark - Penalty Calculation */
|
||||
|
||||
#define PENALTY_N1 3
|
||||
#define PENALTY_N2 3
|
||||
#define PENALTY_N3 40
|
||||
#define PENALTY_N4 10
|
||||
|
||||
// Calculates and returns the penalty score based on state of this QR Code's current modules.
|
||||
// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score.
|
||||
// @TODO: This can be optimized by working with the bytes instead of bits.
|
||||
static uint32_t getPenaltyScore(BitBucket *modules) {
|
||||
uint32_t result = 0;
|
||||
|
||||
uint8_t size = modules->bitOffsetOrWidth;
|
||||
|
||||
// Adjacent modules in row having same color
|
||||
for (uint8_t y = 0; y < size; y++) {
|
||||
|
||||
bool colorX = bb_getBit(modules, 0, y);
|
||||
for (uint8_t x = 1, runX = 1; x < size; x++) {
|
||||
bool cx = bb_getBit(modules, x, y);
|
||||
if (cx != colorX) {
|
||||
colorX = cx;
|
||||
runX = 1;
|
||||
|
||||
} else {
|
||||
runX++;
|
||||
if (runX == 5) {
|
||||
result += PENALTY_N1;
|
||||
} else if (runX > 5) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjacent modules in column having same color
|
||||
for (uint8_t x = 0; x < size; x++) {
|
||||
bool colorY = bb_getBit(modules, x, 0);
|
||||
for (uint8_t y = 1, runY = 1; y < size; y++) {
|
||||
bool cy = bb_getBit(modules, x, y);
|
||||
if (cy != colorY) {
|
||||
colorY = cy;
|
||||
runY = 1;
|
||||
} else {
|
||||
runY++;
|
||||
if (runY == 5) {
|
||||
result += PENALTY_N1;
|
||||
} else if (runY > 5) {
|
||||
result++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t black = 0;
|
||||
for (uint8_t y = 0; y < size; y++) {
|
||||
uint16_t bitsRow = 0, bitsCol = 0;
|
||||
for (uint8_t x = 0; x < size; x++) {
|
||||
bool color = bb_getBit(modules, x, y);
|
||||
|
||||
// 2*2 blocks of modules having same color
|
||||
if (x > 0 && y > 0) {
|
||||
bool colorUL = bb_getBit(modules, x - 1, y - 1);
|
||||
bool colorUR = bb_getBit(modules, x, y - 1);
|
||||
bool colorL = bb_getBit(modules, x - 1, y);
|
||||
if (color == colorUL && color == colorUR && color == colorL) {
|
||||
result += PENALTY_N2;
|
||||
}
|
||||
}
|
||||
|
||||
// Finder-like pattern in rows and columns
|
||||
bitsRow = ((bitsRow << 1) & 0x7FF) | color;
|
||||
bitsCol = ((bitsCol << 1) & 0x7FF) | bb_getBit(modules, y, x);
|
||||
|
||||
// Needs 11 bits accumulated
|
||||
if (x >= 10) {
|
||||
if (bitsRow == 0x05D || bitsRow == 0x5D0) {
|
||||
result += PENALTY_N3;
|
||||
}
|
||||
if (bitsCol == 0x05D || bitsCol == 0x5D0) {
|
||||
result += PENALTY_N3;
|
||||
}
|
||||
}
|
||||
|
||||
// Balance of black and white modules
|
||||
if (color) { black++; }
|
||||
}
|
||||
}
|
||||
|
||||
// Find smallest k such that (45-5k)% <= dark/total <= (55+5k)%
|
||||
uint16_t total = size * size;
|
||||
for (uint16_t k = 0; black * 20 < (9 - k) * total || black * 20 > (11 + k) * total; k++) {
|
||||
result += PENALTY_N4;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* #pragma mark - Reed-Solomon Generator */
|
||||
|
||||
static uint8_t rs_multiply(uint8_t x, uint8_t y) {
|
||||
// Russian peasant multiplication
|
||||
// See: https://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication
|
||||
uint16_t z = 0;
|
||||
for (int8_t i = 7; i >= 0; i--) {
|
||||
z = (z << 1) ^ ((z >> 7) * 0x11D);
|
||||
z ^= ((y >> i) & 1) * x;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
static void rs_init(uint8_t degree, uint8_t *coeff) {
|
||||
memset(coeff, 0, degree);
|
||||
coeff[degree - 1] = 1;
|
||||
|
||||
// Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}),
|
||||
// drop the highest term, and store the rest of the coefficients in order of descending powers.
|
||||
// Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D).
|
||||
uint16_t root = 1;
|
||||
for (uint8_t i = 0; i < degree; i++) {
|
||||
// Multiply the current product by (x - r^i)
|
||||
for (uint8_t j = 0; j < degree; j++) {
|
||||
coeff[j] = rs_multiply(coeff[j], root);
|
||||
if (j + 1 < degree) {
|
||||
coeff[j] ^= coeff[j + 1];
|
||||
}
|
||||
}
|
||||
root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D)
|
||||
}
|
||||
}
|
||||
|
||||
static void rs_getRemainder(uint8_t degree, uint8_t *coeff, uint8_t *data, uint8_t length, uint8_t *result, uint8_t stride) {
|
||||
// Compute the remainder by performing polynomial division
|
||||
|
||||
//for (uint8_t i = 0; i < degree; i++) { result[] = 0; }
|
||||
//memset(result, 0, degree);
|
||||
|
||||
for (uint8_t i = 0; i < length; i++) {
|
||||
uint8_t factor = data[i] ^ result[0];
|
||||
for (uint8_t j = 1; j < degree; j++) {
|
||||
result[(j - 1) * stride] = result[j * stride];
|
||||
}
|
||||
result[(degree - 1) * stride] = 0;
|
||||
|
||||
for (uint8_t j = 0; j < degree; j++) {
|
||||
result[j * stride] ^= rs_multiply(coeff[j], factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* #pragma mark - QrCode */
|
||||
|
||||
static int8_t encodeDataCodewords(BitBucket *dataCodewords, const uint8_t *text, uint16_t length, uint8_t version) {
|
||||
int8_t mode = MODE_BYTE;
|
||||
|
||||
if (isNumeric((char*)text, length)) {
|
||||
mode = MODE_NUMERIC;
|
||||
bb_appendBits(dataCodewords, 1 << MODE_NUMERIC, 4);
|
||||
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_NUMERIC));
|
||||
|
||||
uint16_t accumData = 0;
|
||||
uint8_t accumCount = 0;
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
accumData = accumData * 10 + ((char)(text[i]) - '0');
|
||||
accumCount++;
|
||||
if (accumCount == 3) {
|
||||
bb_appendBits(dataCodewords, accumData, 10);
|
||||
accumData = 0;
|
||||
accumCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 1 or 2 digits remaining
|
||||
if (accumCount > 0) {
|
||||
bb_appendBits(dataCodewords, accumData, accumCount * 3 + 1);
|
||||
}
|
||||
|
||||
} else if (isAlphanumeric((char*)text, length)) {
|
||||
mode = MODE_ALPHANUMERIC;
|
||||
bb_appendBits(dataCodewords, 1 << MODE_ALPHANUMERIC, 4);
|
||||
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_ALPHANUMERIC));
|
||||
|
||||
uint16_t accumData = 0;
|
||||
uint8_t accumCount = 0;
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
accumData = accumData * 45 + getAlphanumeric((char)(text[i]));
|
||||
accumCount++;
|
||||
if (accumCount == 2) {
|
||||
bb_appendBits(dataCodewords, accumData, 11);
|
||||
accumData = 0;
|
||||
accumCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// 1 character remaining
|
||||
if (accumCount > 0) {
|
||||
bb_appendBits(dataCodewords, accumData, 6);
|
||||
}
|
||||
|
||||
} else {
|
||||
bb_appendBits(dataCodewords, 1 << MODE_BYTE, 4);
|
||||
bb_appendBits(dataCodewords, length, getModeBits(version, MODE_BYTE));
|
||||
for (uint16_t i = 0; i < length; i++) {
|
||||
bb_appendBits(dataCodewords, (char)(text[i]), 8);
|
||||
}
|
||||
}
|
||||
|
||||
//bb_setBits(dataCodewords, length, 4, getModeBits(version, mode));
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void performErrorCorrection(uint8_t version, uint8_t ecc, BitBucket *data) {
|
||||
|
||||
// See: http://www.thonky.com/qr-code-tutorial/structure-final-message
|
||||
|
||||
#if LOCK_VERSION == 0
|
||||
uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc][version - 1];
|
||||
uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc][version - 1];
|
||||
uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
|
||||
#else
|
||||
uint8_t numBlocks = NUM_ERROR_CORRECTION_BLOCKS[ecc];
|
||||
uint16_t totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[ecc];
|
||||
uint16_t moduleCount = NUM_RAW_DATA_MODULES;
|
||||
#endif
|
||||
|
||||
uint8_t blockEccLen = totalEcc / numBlocks;
|
||||
uint8_t numShortBlocks = numBlocks - moduleCount / 8 % numBlocks;
|
||||
uint8_t shortBlockLen = moduleCount / 8 / numBlocks;
|
||||
|
||||
uint8_t shortDataBlockLen = shortBlockLen - blockEccLen;
|
||||
|
||||
uint8_t result[data->capacityBytes];
|
||||
memset(result, 0, sizeof(result));
|
||||
|
||||
uint8_t coeff[blockEccLen];
|
||||
rs_init(blockEccLen, coeff);
|
||||
|
||||
uint16_t offset = 0;
|
||||
uint8_t *dataBytes = data->data;
|
||||
|
||||
|
||||
// Interleave all short blocks
|
||||
for (uint8_t i = 0; i < shortDataBlockLen; i++) {
|
||||
uint16_t index = i;
|
||||
uint8_t stride = shortDataBlockLen;
|
||||
for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
|
||||
result[offset++] = dataBytes[index];
|
||||
|
||||
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
|
||||
if (blockNum == numShortBlocks) { stride++; }
|
||||
#endif
|
||||
index += stride;
|
||||
}
|
||||
}
|
||||
|
||||
// Version less than 5 only have short blocks
|
||||
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
|
||||
{
|
||||
// Interleave long blocks
|
||||
uint16_t index = shortDataBlockLen * (numShortBlocks + 1);
|
||||
uint8_t stride = shortDataBlockLen;
|
||||
for (uint8_t blockNum = 0; blockNum < numBlocks - numShortBlocks; blockNum++) {
|
||||
result[offset++] = dataBytes[index];
|
||||
|
||||
if (blockNum == 0) { stride++; }
|
||||
index += stride;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add all ecc blocks, interleaved
|
||||
uint8_t blockSize = shortDataBlockLen;
|
||||
for (uint8_t blockNum = 0; blockNum < numBlocks; blockNum++) {
|
||||
|
||||
#if LOCK_VERSION == 0 || LOCK_VERSION >= 5
|
||||
if (blockNum == numShortBlocks) { blockSize++; }
|
||||
#endif
|
||||
rs_getRemainder(blockEccLen, coeff, dataBytes, blockSize, &result[offset + blockNum], numBlocks);
|
||||
dataBytes += blockSize;
|
||||
}
|
||||
|
||||
memcpy(data->data, result, data->capacityBytes);
|
||||
data->bitOffsetOrWidth = moduleCount;
|
||||
}
|
||||
|
||||
// We store the Format bits tightly packed into a single byte (each of the 4 modes is 2 bits)
|
||||
// The format bits can be determined by ECC_FORMAT_BITS >> (2 * ecc)
|
||||
static const uint8_t ECC_FORMAT_BITS = (0x02 << 6) | (0x03 << 4) | (0x00 << 2) | (0x01 << 0);
|
||||
|
||||
|
||||
/* #pragma mark - Public QRCode functions */
|
||||
|
||||
uint16_t qrcode_getBufferSize(uint8_t version) {
|
||||
return bb_getGridSizeBytes(4 * version + 17);
|
||||
}
|
||||
|
||||
// @TODO: Return error if data is too big.
|
||||
int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length) {
|
||||
uint8_t size = version * 4 + 17;
|
||||
qrcode->version = version;
|
||||
qrcode->size = size;
|
||||
qrcode->ecc = ecc;
|
||||
qrcode->modules = modules;
|
||||
|
||||
uint8_t eccFormatBits = (ECC_FORMAT_BITS >> (2 * ecc)) & 0x03;
|
||||
|
||||
#if LOCK_VERSION == 0
|
||||
uint16_t moduleCount = NUM_RAW_DATA_MODULES[version - 1];
|
||||
uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits][version - 1];
|
||||
#else
|
||||
version = LOCK_VERSION;
|
||||
uint16_t moduleCount = NUM_RAW_DATA_MODULES;
|
||||
uint16_t dataCapacity = moduleCount / 8 - NUM_ERROR_CORRECTION_CODEWORDS[eccFormatBits];
|
||||
#endif
|
||||
|
||||
struct BitBucket codewords;
|
||||
uint8_t codewordBytes[bb_getBufferSizeBytes(moduleCount)];
|
||||
bb_initBuffer(&codewords, codewordBytes, (int32_t)sizeof(codewordBytes));
|
||||
|
||||
// Place the data code words into the buffer
|
||||
int8_t mode = encodeDataCodewords(&codewords, data, length, version);
|
||||
|
||||
if (mode < 0) { return -1; }
|
||||
qrcode->mode = mode;
|
||||
|
||||
// Add terminator and pad up to a byte if applicable
|
||||
uint32_t padding = (dataCapacity * 8) - codewords.bitOffsetOrWidth;
|
||||
if (padding > 4) { padding = 4; }
|
||||
bb_appendBits(&codewords, 0, padding);
|
||||
bb_appendBits(&codewords, 0, (8 - codewords.bitOffsetOrWidth % 8) % 8);
|
||||
|
||||
// Pad with alternate bytes until data capacity is reached
|
||||
for (uint8_t padByte = 0xEC; codewords.bitOffsetOrWidth < (dataCapacity * 8); padByte ^= 0xEC ^ 0x11) {
|
||||
bb_appendBits(&codewords, padByte, 8);
|
||||
}
|
||||
|
||||
BitBucket modulesGrid;
|
||||
bb_initGrid(&modulesGrid, modules, size);
|
||||
|
||||
BitBucket isFunctionGrid;
|
||||
uint8_t isFunctionGridBytes[bb_getGridSizeBytes(size)];
|
||||
bb_initGrid(&isFunctionGrid, isFunctionGridBytes, size);
|
||||
|
||||
// Draw function patterns, draw all codewords, do masking
|
||||
drawFunctionPatterns(&modulesGrid, &isFunctionGrid, version, eccFormatBits);
|
||||
performErrorCorrection(version, eccFormatBits, &codewords);
|
||||
drawCodewords(&modulesGrid, &isFunctionGrid, &codewords);
|
||||
|
||||
// Find the best (lowest penalty) mask
|
||||
uint8_t mask = 0;
|
||||
int32_t minPenalty = INT32_MAX;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, i);
|
||||
applyMask(&modulesGrid, &isFunctionGrid, i);
|
||||
int penalty = getPenaltyScore(&modulesGrid);
|
||||
if (penalty < minPenalty) {
|
||||
mask = i;
|
||||
minPenalty = penalty;
|
||||
}
|
||||
applyMask(&modulesGrid, &isFunctionGrid, i); // Undoes the mask due to XOR
|
||||
}
|
||||
|
||||
qrcode->mask = mask;
|
||||
|
||||
// Overwrite old format bits
|
||||
drawFormatBits(&modulesGrid, &isFunctionGrid, eccFormatBits, mask);
|
||||
|
||||
// Apply the final choice of mask
|
||||
applyMask(&modulesGrid, &isFunctionGrid, mask);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data) {
|
||||
return qrcode_initBytes(qrcode, modules, version, ecc, (uint8_t*)data, strlen(data));
|
||||
}
|
||||
|
||||
bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y) {
|
||||
if ( x >= qrcode->size || y >= qrcode->size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t offset = y * qrcode->size + x;
|
||||
return (qrcode->modules[offset >> 3] & (1 << (7 - (offset & 0x07)))) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
uint8_t qrcode_getHexLength(QRCode *qrcode) {
|
||||
return ((qrcode->size * qrcode->size) + 7) / 4;
|
||||
}
|
||||
|
||||
void qrcode_getHex(QRCode *qrcode, char *result) {
|
||||
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* This library is written and maintained by Richard Moore.
|
||||
* Major parts were derived from Project Nayuki's library.
|
||||
*
|
||||
* Copyright (c) 2017 Richard Moore (https://github.com/ricmoo/QRCode)
|
||||
* Copyright (c) 2017 Project Nayuki (https://www.nayuki.io/page/qr-code-generator-library)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Special thanks to Nayuki (https://www.nayuki.io/) from which this library was
|
||||
* heavily inspired and compared against.
|
||||
*
|
||||
* See: https://github.com/nayuki/QR-Code-generator/tree/master/cpp
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __QRCODEGEN_H_
|
||||
#define __QRCODEGEN_H_
|
||||
|
||||
#ifndef __cplusplus
|
||||
typedef unsigned char bool;
|
||||
static const bool false = 0;
|
||||
static const bool true = 1;
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// QR Code Format Encoding
|
||||
#define MODE_NUMERIC 0
|
||||
#define MODE_ALPHANUMERIC 1
|
||||
#define MODE_BYTE 2
|
||||
|
||||
|
||||
// Error Correction Code Levels
|
||||
#define ECC_LOW 0
|
||||
#define ECC_MEDIUM 1
|
||||
#define ECC_QUARTILE 2
|
||||
#define ECC_HIGH 3
|
||||
|
||||
|
||||
// If set to non-zero, this library can ONLY produce QR codes at that version
|
||||
// This saves a lot of dynamic memory, as the codeword tables are skipped
|
||||
#ifndef LOCK_VERSION
|
||||
#define LOCK_VERSION 0
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct QRCode {
|
||||
uint8_t version;
|
||||
uint8_t size;
|
||||
uint8_t ecc;
|
||||
uint8_t mode;
|
||||
uint8_t mask;
|
||||
uint8_t *modules;
|
||||
} QRCode;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
|
||||
uint16_t qrcode_getBufferSize(uint8_t version);
|
||||
|
||||
int8_t qrcode_initText(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, const char *data);
|
||||
int8_t qrcode_initBytes(QRCode *qrcode, uint8_t *modules, uint8_t version, uint8_t ecc, uint8_t *data, uint16_t length);
|
||||
|
||||
bool qrcode_getModule(QRCode *qrcode, uint8_t x, uint8_t y);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#endif /* __QRCODEGEN_H_ */
|
||||
@@ -45,6 +45,14 @@ struct serial_format_t {
|
||||
parity_enum parity;
|
||||
uint8_t stop_bits;
|
||||
order_enum bit_order;
|
||||
|
||||
constexpr serial_format_t() :
|
||||
data_bits(7),
|
||||
parity(parity_enum::EVEN),
|
||||
stop_bits(1),
|
||||
bit_order(order_enum::LSB_FIRST)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
size_t symbol_count(const serial_format_t& serial_format);
|
||||
|
||||
@@ -129,6 +129,11 @@ std::string to_string_decimal(float decimal, int8_t precision) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string to_string_freq(const uint64_t f) {
|
||||
auto final_str = to_string_dec_int(f / 1000000,4) + to_string_dec_int(f % 1000000, 6, '0');
|
||||
return final_str;
|
||||
}
|
||||
|
||||
std::string to_string_short_freq(const uint64_t f) {
|
||||
auto final_str = to_string_dec_int(f / 1000000,4) + "." + to_string_dec_int((f / 100) % 10000, 4, '0');
|
||||
return final_str;
|
||||
|
||||
@@ -48,6 +48,7 @@ std::string to_string_decimal(float decimal, int8_t precision);
|
||||
std::string to_string_hex(const uint64_t n, const int32_t l = 0);
|
||||
std::string to_string_hex_array(uint8_t * const array, const int32_t l = 0);
|
||||
|
||||
std::string to_string_freq(const uint64_t f);
|
||||
std::string to_string_short_freq(const uint64_t f);
|
||||
std::string to_string_time_ms(const uint32_t ms);
|
||||
|
||||
|
||||
@@ -76,14 +76,6 @@ ui::Point Calibration::translate(const DigitizerPoint& p) const {
|
||||
return { x_clipped, y_clipped };
|
||||
}
|
||||
|
||||
const Calibration default_calibration() {
|
||||
/* Values derived from one PortaPack H1 unit. */
|
||||
return {
|
||||
{ { { 256, 731 }, { 880, 432 }, { 568, 146 } } },
|
||||
{ { { 32, 48 }, { 208, 168 }, { 120, 288 } } }
|
||||
};
|
||||
};
|
||||
|
||||
void Manager::feed(const Frame& frame) {
|
||||
// touch_debounce.feed(touch_raw);
|
||||
const auto touch_raw = frame.touch;
|
||||
@@ -109,7 +101,7 @@ void Manager::feed(const Frame& frame) {
|
||||
|
||||
switch(state) {
|
||||
case State::NoTouch:
|
||||
if( touch_stable && touch_pressure ) {
|
||||
if( touch_stable && touch_pressure && !persistent_memory::disable_touchscreen()) {
|
||||
if( point_stable() ) {
|
||||
state = State::TouchDetected;
|
||||
touch_started();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user