mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-30 19:39:03 +00:00
Compare commits
197 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e2620f1a33 | |||
| f6a4cc26ee | |||
| 3d248647c9 | |||
| 3c13161273 | |||
| f0b4914186 | |||
| 037fc098ef | |||
| 988404df9c | |||
| f5deae56cf | |||
| e526061d13 | |||
| daf63de55f | |||
| 778111d466 | |||
| 00b75eacda | |||
| ef151e243b | |||
| 567fee1d98 | |||
| b549d3a4f1 | |||
| 86203f090f | |||
| 19fed78b7f | |||
| c0532aeafa | |||
| c8774e82d9 | |||
| 62ee23a314 | |||
| 70551ef155 | |||
| 91074e5b4e | |||
| 5f95cec5f4 | |||
| 66ba6442b1 | |||
| 7e20cea6d7 | |||
| 65217856c0 | |||
| 5117538b49 | |||
| 362c208078 | |||
| 4f9e8e9642 | |||
| de53626a46 | |||
| 2cf6849985 | |||
| 1cde3ab189 | |||
| bdb70a3231 | |||
| 49e63e2728 | |||
| 1beca57e56 | |||
| 27632825a3 | |||
| e02e58b366 | |||
| 37912df085 | |||
| a20a08c630 | |||
| a8885282cd | |||
| 2d9090862b | |||
| 6e5bb7911c | |||
| 46f98ddc8b | |||
| 2902365b80 | |||
| c0f368c978 | |||
| 75c2b69f19 | |||
| 3291c7e2a0 | |||
| 64ca9f6a68 | |||
| 297063873f | |||
| 2a38f386ab | |||
| 079a0506a4 | |||
| e8ad86cc94 | |||
| e69c9bbc7b | |||
| 1f7b800c2a | |||
| 25c267a3d9 | |||
| efbcd2aa9b | |||
| f32b55f66f | |||
| e9adc32421 | |||
| a120a6344d | |||
| e3cea4c68b | |||
| 0631f5d5ab | |||
| 8986b65f94 | |||
| 7f64606fb2 | |||
| 7bfb54eee9 | |||
| e0c1f7b45e | |||
| 734f315266 | |||
| 7f6aebbe53 | |||
| 5f62926af3 | |||
| 17f6475506 | |||
| 17d3cb387e | |||
| 1e4c93b979 | |||
| 25dfa96257 | |||
| 400cccdcc1 | |||
| f59437cf71 | |||
| 5577ec82ed | |||
| da98e7739e | |||
| b5b5bf42af | |||
| 587fbfbbb1 | |||
| c54e382db2 | |||
| 22de89c879 | |||
| aa778e5797 | |||
| e8e98475e2 | |||
| 95d1bca9fd | |||
| 1a5c360efe | |||
| 560c7143bf | |||
| 6fc03a2651 | |||
| f31ee1172d | |||
| 996e648a64 | |||
| ca4f68d24a | |||
| 2c3f8e9b3d | |||
| a591ba572f | |||
| c9657bc92b | |||
| b8d98e4f45 | |||
| e0309b13e5 | |||
| c2117b2c5b | |||
| 2b78f9e907 | |||
| 6cd4eb0358 | |||
| 8ec69d7c95 | |||
| efe8d82fae | |||
| 32697d1c73 | |||
| 11558b9843 | |||
| e5d1308560 | |||
| 62968e6009 | |||
| a9b4abc07e | |||
| 05f3793213 | |||
| ae8633c569 | |||
| 480f492740 | |||
| dd2e23f901 | |||
| 3ca57f634c | |||
| 29ac3192cb | |||
| e76faf6edd | |||
| 5474487751 | |||
| e6cf04e511 | |||
| 1d521819ff | |||
| 83f7c9ae63 | |||
| 310fbd53ec | |||
| 6b75249b0a | |||
| 745574d3c4 | |||
| c80595db7e | |||
| 040dded2a6 | |||
| 958c9a1bfa | |||
| 76a137924c | |||
| 861db600e8 | |||
| 4993a6abcb | |||
| 9086f32a63 | |||
| 9d59c07855 | |||
| 0d18aa47f3 | |||
| cf40f5eb14 | |||
| fe4bc7902f | |||
| 5d5865eefb | |||
| a7370ee95d | |||
| 223cd7c20d | |||
| 1be3167748 | |||
| 1bbd889005 | |||
| d87e9853a1 | |||
| 36109964a7 | |||
| 6087c658e9 | |||
| 08a108d3e4 | |||
| c78659db38 | |||
| 5021a03572 | |||
| 961a4c4588 | |||
| a89d418202 | |||
| 1dcc80ba59 | |||
| 1e14ca4df1 | |||
| 5096f91986 | |||
| 65be02c5b1 | |||
| f5a604a0a5 | |||
| 87418703e0 | |||
| e33c98cbc7 | |||
| 823c69eb42 | |||
| 15f3d81798 | |||
| fa2791004f | |||
| 50bd08d9ff | |||
| a26ee84ff8 | |||
| 39134b62c3 | |||
| e5a30b4309 | |||
| 379ad6dbf0 | |||
| 840da4c685 | |||
| 2031a79b34 | |||
| deff646e36 | |||
| 4aa3314906 | |||
| e3da9d4cb2 | |||
| c389ce7c0e | |||
| dda543f7f3 | |||
| 92844a7528 | |||
| ef52339b1d | |||
| 5e0780839c | |||
| 85602d145a | |||
| 2efab19bf6 | |||
| 4562228b55 | |||
| b56acb0c9d | |||
| 6af0d4e751 | |||
| 0fed174a80 | |||
| d71c759480 | |||
| c1e568f62c | |||
| db1b7ffddf | |||
| f48980e381 | |||
| cdaa6683ae | |||
| d2dbad73c7 | |||
| 9412dd8bec | |||
| d624b92e85 | |||
| 584c46af64 | |||
| 1976c3648b | |||
| c4eb02422b | |||
| 95ac4a2d1e | |||
| a7c7495428 | |||
| 8f311ea1f2 | |||
| d0643421a8 | |||
| adb0cdf8fb | |||
| 1b93a042a3 | |||
| 52c6d0e4ed | |||
| 22a5ca7df7 | |||
| d0b356a7ef | |||
| 28ee69baae | |||
| bfd614b51f | |||
| 4a6fc35384 | |||
| 35cece1cb0 |
@@ -0,0 +1 @@
|
||||
build
|
||||
@@ -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.
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.5.1
|
||||
v1.5.4
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.5.3
|
||||
v1.6.0
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
|
||||
@@ -53,4 +53,3 @@ addons:
|
||||
- sed
|
||||
- cmake
|
||||
- dfu-util
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# PortaPack Mayhem
|
||||
|
||||
[](https://travis-ci.com/eried/portapack-mayhem) [](https://codescene.io/projects/8381) [](https://github.com/eried/portapack-mayhem/releases) [](https://github.com/eried/portapack-mayhem/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3) [](https://www.bountysource.com/teams/portapack-mayhem/issues)
|
||||
[](https://travis-ci.com/eried/portapack-mayhem) [](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml) [](https://codescene.io/projects/8381) [](https://github.com/eried/portapack-mayhem/releases) [](https://github.com/eried/portapack-mayhem/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3) [](https://www.bountysource.com/teams/portapack-mayhem/issues)
|
||||
|
||||
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
|
||||
|
||||
[<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_front.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/eried/portapack-mayhem/img/hw_overview_h2_inside.png" height="400">](https://github.com/eried/portapack-mayhem/wiki/Hardware-overview#portapack-internals)
|
||||
|
||||
*[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)*
|
||||
*[PortaPack H2+HackRF+battery](https://s.click.aliexpress.com/e/_DmU7GQX) (clone) with a custom [3d printed case](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure)*
|
||||
|
||||
# Quick overview
|
||||
|
||||
@@ -18,42 +18,40 @@ If you are new to *HackRF+PortaPack+Mayhem*, there is an awesome introductory vi
|
||||
|
||||
This repository expands upon the previous work by many people and aims to constantly add new features, bugfixes and generate documentation to make further development easier. [Collaboration](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) is always welcomed and appreciated.
|
||||
|
||||
## Does it work on H1/H2 PortaPack?
|
||||
## What to buy?
|
||||
|
||||
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.
|
||||
:heavy_check_mark: A recommended one is this [PortaPack H2](https://s.click.aliexpress.com/e/_DmU7GQX) pack, that includes everything you need. Sadly, the people making the H2 never made the updated schematics available (against the terms of the license).
|
||||
|
||||
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).
|
||||
:heavy_check_mark: Another popular option is the clone of the [PortaPack H1](https://s.click.aliexpress.com/e/_Dkbqs2X).
|
||||
|
||||
## Where is the latest firmware?
|
||||
:warning: Be cautious , *ask* the seller about compatibility with the latest releases. Look out for the description of the item, if they provide the firmware files for an older version or they have custom setup instructions, this means it might be **NOT compatible**, for example:
|
||||
|
||||
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.
|
||||
:warning: If it looks **too different**, this might mean that they are using their own recipe, check the [different models](https://github.com/eried/portapack-mayhem/wiki/PortaPack-Versions) in our wiki. For example all the H3 and clones of that version use their own version of the firmware. They do not contribute the changes back and eventually you will be left with a device that nobody maintains:
|
||||
|
||||
## 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*.
|
||||

|
||||
|
||||
## Which one is actually the newest?
|
||||
There is a lot of confusion of which is the latest version because no old version used any actual "version number". Additionally, since the files were distributed on facebook groups, github issue links and similar temporal sources, then there was no central location for them.
|
||||
## Where is the latest version?
|
||||
|
||||
This fork (**Mayhem**) uses *major.minor.release* [semantic versioning](https://en.wikipedia.org/wiki/Software_versioning), so you can always compare your current version with the latest from [Releases](https://github.com/eried/portapack-mayhem/releases/latest).
|
||||
|
||||
## What about Havoc/GridRF/jamesshao8/jboone's?
|
||||
* jboone's PortaPack: the [vanilla](https://en.wikipedia.org/wiki/Vanilla_software) experience
|
||||
* Havoc: It was the most popular fork of jboone's PortaPack, currrently, it is not being maintained nor updated
|
||||
* jamesshao8: He keeps his own version of the fork, while not attached as a fork to anything. Latest functions do not follow the license and are not being published with source code, so keep this in mind
|
||||
* GridRF: They sell PortaPack clones with their own firmware based on a old version, which has no sourcecode available
|
||||
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/).
|
||||
|
||||
## How can I collaborate
|
||||
You can write [documentation](https://github.com/eried/portapack-mayhem/wiki), fix bugs and [answer issues](https://github.com/eried/portapack-mayhem/issues) or add new functionality. Please check the following [guide](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate) with details.
|
||||
|
||||
Consider that the hardware and firmware has been created and maintain by a [lot](https://github.com/mossmann/hackrf/graphs/contributors) of [people](https://github.com/eried/portapack-mayhem/graphs/contributors), so always try colaborating your time and effort first. For coding related questions, if something does not fit as an issue, please join our Discord by clicking the chat badge on [top](#portapack-mayhem).
|
||||
|
||||
[](https://github.com/eried/portapack-mayhem/graphs/contributors)
|
||||
|
||||
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).
|
||||
|
||||
As a last option, if you want to send money directly to me for getting more boards, antennas and such:
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CBPQA4HRRPJQ6&source=url)
|
||||
|
||||
## What if I really want to pay for something?
|
||||
## What if I really want something specific?
|
||||
If what you need can be relevant in general, you can [request a feature](https://github.com/eried/portapack-mayhem/issues/new?labels=enhancement&template=feature_request.md).
|
||||
|
||||
You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/eried/portapack-mayhem/issues/new/choose) or [choosing](https://github.com/eried/portapack-mayhem/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).
|
||||
|
||||
Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).
|
||||
@@ -62,6 +60,3 @@ Promote your bounty over our Discord by clicking the chat badge on [top](#portap
|
||||
First, check the [documentation](https://github.com/eried/portapack-mayhem/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
|
||||
|
||||
You can reach the [official community](https://www.facebook.com/groups/177623356165819) in Facebook, and our Discord by clicking the chat badge on [top](#portapack-mayhem).
|
||||
|
||||
## What if I find incongruencies, or grammatical errors in the text?
|
||||
If is on the [Wiki](https://github.com/eried/portapack-mayhem/wiki), you can modify it directly. If is on files of the repository, you can send corrections as [pull requests](https://github.com/eried/portapack-mayhem/wiki/How-to-collaborate#coding-new-stuff-or-fixing-bugs). As a last resource, open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
portapack-mayhem-dev:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: dockerfile-nogit-alpine
|
||||
volumes:
|
||||
- ".:/havoc"
|
||||
@@ -0,0 +1,9 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
portapack-mayhem-dev:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: dockerfile-alpine
|
||||
volumes:
|
||||
- "./bin:/havocbin"
|
||||
+17
-14
@@ -1,6 +1,6 @@
|
||||
#Download base image.
|
||||
#The ubuntu:latest tag points to the "latest LTS"
|
||||
FROM ubuntu:latest
|
||||
#The ubuntu:20.04 tag points to the "20.04 LTS"
|
||||
FROM ubuntu:20.04
|
||||
|
||||
#Set location to download ARM toolkit from.
|
||||
# This will need to be changed over time or replaced with a static link to the latest release.
|
||||
@@ -14,17 +14,19 @@ COPY ./ /havocsrc
|
||||
|
||||
#Fetch dependencies from APT
|
||||
RUN apt-get update && \
|
||||
apt-get install -y tar wget dfu-util cmake python bzip2 curl && \
|
||||
apt-get -qy autoremove
|
||||
apt-get install -y tar wget dfu-util cmake python bzip2 curl python3 python3-yaml && \
|
||||
apt-get -qy autoremove
|
||||
|
||||
#Install current pip from PyPa
|
||||
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
|
||||
python get-pip.py
|
||||
python get-pip.py
|
||||
|
||||
#Fetch additional dependencies from Python 2.x pip
|
||||
RUN pip install pyyaml
|
||||
RUN ln -s /usr/bin/python3 /usr/bin/python && \
|
||||
ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
RUN rm -rf /usr/bin/python && \
|
||||
rm -rf /usr/bin/pip && \
|
||||
ln -s /usr/bin/python3 /usr/bin/python && \
|
||||
ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
@@ -32,14 +34,15 @@ ENV LC_ALL C.UTF-8
|
||||
#Grab the GNU ARM toolchain from arm.com
|
||||
#Then extract contents to /opt/build/armbin/
|
||||
RUN mkdir /opt/build && cd /opt/build && \
|
||||
wget -O gcc-arm-none-eabi $ARMBINURL && \
|
||||
mkdir armbin && \
|
||||
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
|
||||
wget -O gcc-arm-none-eabi $ARMBINURL && \
|
||||
mkdir armbin && \
|
||||
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
|
||||
|
||||
#Set environment variable so compiler knows where the toolchain lives
|
||||
ENV PATH=$PATH:/opt/build/armbin/bin
|
||||
|
||||
CMD cd /havocsrc && \
|
||||
mkdir build && cd build && \
|
||||
cmake .. && make firmware && \
|
||||
cp /portapack-havoc/firmware/portapack-h1-havoc.bin /havocbin
|
||||
mkdir build && cd build && \
|
||||
cmake .. && make firmware && \
|
||||
cp /portapack-havoc/firmware/portapack-h1-havoc.bin /havocbin
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Download base image.
|
||||
FROM alpine:edge
|
||||
|
||||
# Download ARM toolkit.
|
||||
RUN apk add --no-cache gcc-arm-none-eabi gcc
|
||||
|
||||
# Create volume /havocbin for compiled firmware binaries
|
||||
VOLUME /havocbin
|
||||
|
||||
# Copy build context (repository root) to /havocsrc
|
||||
COPY ./ /havocsrc
|
||||
|
||||
# Fetch dependencies from APK
|
||||
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
||||
RUN apk update -U
|
||||
RUN apk add --no-cache git tar wget cmake curl bzip2 make
|
||||
RUN apk add --no-cache dfu-util ccache icu-data-full
|
||||
RUN apk add --no-cache python3 py3-pip py3-yaml
|
||||
RUN apk add --no-cache py3-pyyaml-env-tag
|
||||
RUN apk add --no-cache g++ gcc clang clang-static clang-dev llvm-dev llvm-static
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
CMD cd /havocsrc && \
|
||||
mkdir build && cd build && \
|
||||
cmake .. && make firmware && \
|
||||
cp /portapack-havoc/firmware/portapack-h1-havoc.bin /havocbin
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
|
||||
#Fetch additional dependencies from Python 3.x pip
|
||||
RUN pip install pyyaml
|
||||
RUN ln -s /usr/bin/python3 /usr/bin/python && \
|
||||
ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
ln -s /usr/bin/pip3 /usr/bin/pip
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
@@ -38,4 +38,4 @@ RUN mkdir ~/bin && cd ~/bin && \
|
||||
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
|
||||
|
||||
CMD cd .. && cd build && \
|
||||
cmake .. && make firmware
|
||||
cmake .. && make firmware
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
FROM alpine:edge
|
||||
|
||||
# Download ARM toolkit.
|
||||
RUN apk add --no-cache gcc-arm-none-eabi gcc
|
||||
|
||||
# Create volume /havoc/bin for compiled firmware binaries
|
||||
VOLUME /havoc
|
||||
WORKDIR /havoc/firmware
|
||||
|
||||
# Fetch dependencies from APK
|
||||
RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
|
||||
RUN apk update -U
|
||||
RUN apk add --no-cache git tar wget cmake curl bzip2 make
|
||||
RUN apk add --no-cache dfu-util ccache icu-data-full
|
||||
RUN apk add --no-cache python3 py3-pip py3-yaml
|
||||
RUN apk add --no-cache py3-pyyaml-env-tag
|
||||
RUN apk add --no-cache g++ gcc clang clang-static clang-dev llvm-dev llvm-static
|
||||
|
||||
ENV LANG C.UTF-8
|
||||
ENV LC_ALL C.UTF-8
|
||||
|
||||
# Configure CCACHE
|
||||
RUN mkdir ~/bin && cd ~/bin && \
|
||||
for tool in gcc g++ cpp c++;do ln -s $(which ccache) arm-none-eabi-$tool;done
|
||||
|
||||
CMD cd .. && cd build && \
|
||||
cmake .. && make firmware
|
||||
@@ -103,6 +103,7 @@ set(CSRC
|
||||
${PLATFORMSRC}
|
||||
${BOARDSRC}
|
||||
${FATFSSRC}
|
||||
firmware_info.c
|
||||
)
|
||||
|
||||
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
|
||||
@@ -193,6 +194,7 @@ set(CPPSRC
|
||||
hw/debounce.cpp
|
||||
hw/encoder.cpp
|
||||
hw/max2837.cpp
|
||||
hw/max2839.cpp
|
||||
hw/max5864.cpp
|
||||
hw/rffc507x.cpp
|
||||
hw/rffc507x_spi.cpp
|
||||
@@ -267,8 +269,12 @@ set(CPPSRC
|
||||
apps/lge_app.cpp
|
||||
apps/pocsag_app.cpp
|
||||
apps/replay_app.cpp
|
||||
apps/ui_playlist.cpp
|
||||
apps/gps_sim_app.cpp
|
||||
apps/soundboard_app.cpp
|
||||
apps/ui_recon.cpp
|
||||
apps/ui_recon_settings.cpp
|
||||
apps/tpms_app.cpp
|
||||
apps/tpms_app.cpp
|
||||
protocols/aprs.cpp
|
||||
protocols/ax25.cpp
|
||||
|
||||
@@ -51,6 +51,13 @@ int app_settings::load(std::string application, AppSettings* settings) {
|
||||
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=");
|
||||
settings->step=std::app_settings::read_long_long(file_content, "step=");
|
||||
settings->modulation=std::app_settings::read_long_long(file_content, "modulation=");
|
||||
settings->am_config_index=std::app_settings::read_long_long(file_content, "am_config_index=");
|
||||
settings->nbfm_config_index=std::app_settings::read_long_long(file_content, "nbfm_config_index=");
|
||||
settings->wfm_config_index=std::app_settings::read_long_long(file_content, "wfm_config_index=");
|
||||
settings->squelch=std::app_settings::read_long_long(file_content, "squelch=");
|
||||
|
||||
rc = SETTINGS_OK;
|
||||
}
|
||||
else rc = SETTINGS_UNABLE_TO_LOAD;
|
||||
@@ -61,7 +68,7 @@ int app_settings::load(std::string application, AppSettings* settings) {
|
||||
|
||||
int app_settings::save(std::string application, AppSettings* settings) {
|
||||
|
||||
if (portapack::persistent_memory::save_app_settings()) {
|
||||
if (portapack::persistent_memory::save_app_settings()) {
|
||||
file_path = folder+"/"+application+".ini";
|
||||
make_new_directory(folder);
|
||||
|
||||
@@ -79,6 +86,12 @@ int app_settings::save(std::string application, AppSettings* settings) {
|
||||
// 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));
|
||||
settings_file.write_line("step="+to_string_dec_uint(settings->step));
|
||||
settings_file.write_line("modulation="+to_string_dec_uint(settings->modulation));
|
||||
settings_file.write_line("am_config_index="+to_string_dec_uint(settings->am_config_index));
|
||||
settings_file.write_line("nbfm_config_index="+to_string_dec_uint(settings->nbfm_config_index));
|
||||
settings_file.write_line("wfm_config_index="+to_string_dec_uint(settings->wfm_config_index));
|
||||
settings_file.write_line("squelch="+to_string_dec_uint(settings->squelch));
|
||||
|
||||
rc = SETTINGS_OK;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,11 @@ public:
|
||||
uint32_t tx_frequency;
|
||||
uint8_t tx_gain;
|
||||
uint8_t vga;
|
||||
uint32_t step;
|
||||
uint8_t am_config_index;
|
||||
uint8_t nbfm_config_index;
|
||||
uint8_t wfm_config_index;
|
||||
uint8_t squelch;
|
||||
};
|
||||
|
||||
int load(std::string application, AppSettings* settings);
|
||||
|
||||
@@ -76,12 +76,18 @@ static std::string mid(
|
||||
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_RECORD_FOUND: return mid_record.country;
|
||||
case DATABASE_NOT_FOUND: return "No mids.db file";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string text(const std::string &text) {
|
||||
size_t end = text.find_last_not_of("@");
|
||||
return (end == std::string::npos) ? "" : text.substr(0, end + 1);
|
||||
|
||||
}
|
||||
|
||||
static std::string navigational_status(const unsigned int value) {
|
||||
switch(value) {
|
||||
case 0: return "under way w/engine";
|
||||
@@ -164,7 +170,7 @@ void AISLogger::on_packet(const ais::Packet& packet) {
|
||||
}
|
||||
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
}
|
||||
}
|
||||
|
||||
void AISRecentEntry::update(const ais::Packet& packet) {
|
||||
received_count++;
|
||||
@@ -195,6 +201,15 @@ void AISRecentEntry::update(const ais::Packet& packet) {
|
||||
destination = packet.text(302, 20);
|
||||
break;
|
||||
|
||||
case 18:
|
||||
last_position.timestamp = packet.received_at();
|
||||
last_position.speed_over_ground = packet.read(46, 10);
|
||||
last_position.latitude = packet.latitude(85);
|
||||
last_position.longitude = packet.longitude(57);
|
||||
last_position.course_over_ground = packet.read(112, 12);
|
||||
last_position.true_heading = packet.read(124, 9);
|
||||
break;
|
||||
|
||||
case 21:
|
||||
name = packet.text(43, 20);
|
||||
last_position.timestamp = packet.received_at();
|
||||
@@ -202,6 +217,23 @@ void AISRecentEntry::update(const ais::Packet& packet) {
|
||||
last_position.longitude = packet.longitude(164);
|
||||
break;
|
||||
|
||||
case 24:
|
||||
switch (packet.read(38, 2))
|
||||
{
|
||||
case 0:
|
||||
name = packet.text(40, 20);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
call_sign = packet.text(90, 7);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -218,9 +250,9 @@ void RecentEntriesTable<AISRecentEntries>::draw(
|
||||
) {
|
||||
std::string line = ais::format::mmsi(entry.mmsi) + " ";
|
||||
if( !entry.name.empty() ) {
|
||||
line += entry.name;
|
||||
line += ais::format::text(entry.name);
|
||||
} else {
|
||||
line += entry.call_sign;
|
||||
line += ais::format::text(entry.call_sign);
|
||||
}
|
||||
|
||||
line.resize(target_rect.width() / 8, ' ');
|
||||
@@ -241,7 +273,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
|
||||
|
||||
button_see_map.on_select = [this, &nav](Button&) {
|
||||
geomap_view = nav.push<GeoMapView>(
|
||||
entry_.name,
|
||||
ais::format::text(entry_.name),
|
||||
0,
|
||||
GeoPos::alt_unit::METERS,
|
||||
ais::format::latlon_float(entry_.last_position.latitude.normalized()),
|
||||
@@ -252,7 +284,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
|
||||
});
|
||||
send_updates = true;
|
||||
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -262,7 +294,7 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(const AISRecentEntryDetailVie
|
||||
(void)Entry;
|
||||
}
|
||||
|
||||
AISRecentEntryDetailView & AISRecentEntryDetailView::operator=(const AISRecentEntryDetailView&Entry)
|
||||
AISRecentEntryDetailView & AISRecentEntryDetailView::operator=(const AISRecentEntryDetailView&Entry)
|
||||
{
|
||||
(void)Entry;
|
||||
return *this;
|
||||
@@ -302,9 +334,9 @@ void AISRecentEntryDetailView::paint(Painter& painter) {
|
||||
|
||||
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);
|
||||
field_rect = draw_field(painter, field_rect, s, "Name", ais::format::text(entry_.name));
|
||||
field_rect = draw_field(painter, field_rect, s, "Call", ais::format::text(entry_.call_sign));
|
||||
field_rect = draw_field(painter, field_rect, s, "Dest", ais::format::text(entry_.destination));
|
||||
field_rect = draw_field(painter, field_rect, s, "Last", to_string_datetime(entry_.last_position.timestamp));
|
||||
field_rect = draw_field(painter, field_rect, s, "Pos ", ais::format::latlon(entry_.last_position.latitude, entry_.last_position.longitude));
|
||||
field_rect = draw_field(painter, field_rect, s, "Stat", ais::format::navigational_status(entry_.navigational_status));
|
||||
@@ -352,7 +384,7 @@ AISAppView::AISAppView(NavigationView& nav) : nav_ { nav } {
|
||||
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);
|
||||
};
|
||||
|
||||
@@ -129,6 +129,11 @@ AnalogAudioView::AnalogAudioView(
|
||||
&waterfall
|
||||
});
|
||||
|
||||
// Set on_change before initialising the field
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_audio", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
@@ -136,6 +141,7 @@ AnalogAudioView::AnalogAudioView(
|
||||
field_vga.set_value(app_settings.vga);
|
||||
receiver_model.set_rf_amp(app_settings.rx_amp);
|
||||
field_frequency.set_value(app_settings.rx_frequency);
|
||||
receiver_model.set_configuration_without_init(static_cast<ReceiverModel::Mode>(app_settings.modulation), app_settings.step, app_settings.am_config_index, app_settings.nbfm_config_index, app_settings.wfm_config_index, app_settings.squelch);
|
||||
}
|
||||
else field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
|
||||
@@ -143,9 +149,6 @@ AnalogAudioView::AnalogAudioView(
|
||||
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);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
@@ -193,36 +196,45 @@ 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() {
|
||||
return spec_bw_index;
|
||||
return spec_bw_index;
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) {
|
||||
spec_bw_index = index;
|
||||
spec_bw = bw;
|
||||
spec_bw_index = index;
|
||||
spec_bw = bw;
|
||||
|
||||
baseband::set_spectrum(bw, spec_trigger);
|
||||
receiver_model.set_sampling_rate(bw);
|
||||
receiver_model.set_baseband_bandwidth(bw/2);
|
||||
baseband::set_spectrum(bw, spec_trigger);
|
||||
receiver_model.set_sampling_rate(bw);
|
||||
receiver_model.set_baseband_bandwidth(bw/2);
|
||||
}
|
||||
|
||||
uint16_t AnalogAudioView::get_spec_trigger() {
|
||||
return spec_trigger;
|
||||
return spec_trigger;
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
|
||||
spec_trigger = trigger;
|
||||
spec_trigger = trigger;
|
||||
|
||||
baseband::set_spectrum(spec_bw, spec_trigger);
|
||||
baseband::set_spectrum(spec_bw, spec_trigger);
|
||||
}
|
||||
|
||||
AnalogAudioView::~AnalogAudioView() {
|
||||
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
app_settings.lna = receiver_model.lna();
|
||||
app_settings.vga = receiver_model.vga();
|
||||
app_settings.rx_amp = receiver_model.rf_amp();
|
||||
app_settings.step = receiver_model.frequency_step();
|
||||
app_settings.modulation = (uint8_t)receiver_model.modulation();
|
||||
app_settings.am_config_index = receiver_model.am_configuration();
|
||||
app_settings.nbfm_config_index = receiver_model.nbfm_configuration();
|
||||
app_settings.wfm_config_index = receiver_model.wfm_configuration();
|
||||
app_settings.squelch = receiver_model.squelch_level();
|
||||
settings.save("rx_audio", &app_settings);
|
||||
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
|
||||
@@ -53,12 +53,13 @@ private:
|
||||
|
||||
OptionsField options_config {
|
||||
{ 3 * 8, 0 * 16 },
|
||||
4,
|
||||
5,
|
||||
{
|
||||
{ "DSB ", 0 },
|
||||
{ "USB ", 0 },
|
||||
{ "LSB ", 0 },
|
||||
{ "CW ", 0 },
|
||||
{ "DSB 9k ", 0 },
|
||||
{ "DSB 6k ", 0 },
|
||||
{ "USB+3k ", 0 },
|
||||
{ "LSB-3k ", 0 },
|
||||
{ "CW ", 0 },
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -57,6 +57,10 @@ AnalogTvView::AnalogTvView(
|
||||
&tv
|
||||
});
|
||||
|
||||
// Set on_change before initialising the field
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_tv", &app_settings);
|
||||
@@ -70,9 +74,6 @@ AnalogTvView::AnalogTvView(
|
||||
|
||||
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
|
||||
@@ -113,8 +113,12 @@ ERTAppView::ERTAppView(NavigationView&) {
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
|
||||
receiver_model.set_tuning_frequency(initial_target_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.
|
||||
|
||||
radio::enable({
|
||||
/* radio::enable({
|
||||
initial_target_frequency,
|
||||
sampling_rate,
|
||||
baseband_bandwidth,
|
||||
@@ -122,7 +126,7 @@ ERTAppView::ERTAppView(NavigationView&) {
|
||||
receiver_model.rf_amp(),
|
||||
static_cast<int8_t>(receiver_model.lna()),
|
||||
static_cast<int8_t>(receiver_model.vga()),
|
||||
});
|
||||
}); */
|
||||
|
||||
logger = std::make_unique<ERTLogger>();
|
||||
if( logger ) {
|
||||
@@ -135,7 +139,7 @@ ERTAppView::~ERTAppView() {
|
||||
// save app settings
|
||||
settings.save("rx_ert", &app_settings);
|
||||
|
||||
radio::disable();
|
||||
receiver_model.disable(); // to switch off all, including DC bias and change flag enabled_
|
||||
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@@ -242,7 +243,8 @@ GpsSimAppView::GpsSimAppView(
|
||||
|
||||
GpsSimAppView::~GpsSimAppView() {
|
||||
radio::disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void GpsSimAppView::on_hide() {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "lge_app.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
@@ -48,7 +49,8 @@ LGEView::~LGEView() {
|
||||
settings.save("tx_lge", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown();// better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void LGEView::generate_lge_frame(const uint8_t command, const uint16_t address_a, const uint16_t address_b, std::vector<uint8_t>& data) {
|
||||
|
||||
@@ -77,6 +77,11 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
&console
|
||||
});
|
||||
|
||||
// Set on_change before initialising the field
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
update_freq(f);
|
||||
};
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_pocsag", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
@@ -94,9 +99,6 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
receiver_model.enable();
|
||||
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
update_freq(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
#include "ui_fileman.hpp"
|
||||
#include "io_file.hpp"
|
||||
|
||||
#include "cpld_update.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
@@ -258,7 +258,12 @@ ReplayAppView::ReplayAppView(
|
||||
|
||||
ReplayAppView::~ReplayAppView() {
|
||||
radio::disable();
|
||||
baseband::shutdown();
|
||||
|
||||
display.fill_rectangle({ 0,0,240, 320 },Color::black()); //Solving sometimes visible bottom waterfall artifacts, clearing all LCD pixels.
|
||||
chThdSleepMilliseconds(40); // (that happened sometimes if we interrupt the waterfall play at the beggining of the play around 25% and exit )
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX reception ok, without "ghost interference signal problem" at the exit .
|
||||
|
||||
baseband::shutdown(); // better this function at the end, after load_sram(). If not , sometimes produced hang up (now not , it is ok).
|
||||
}
|
||||
|
||||
void ReplayAppView::on_hide() {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "soundboard_app.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "tonesets.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
using namespace tonekey;
|
||||
using namespace portapack;
|
||||
@@ -110,6 +111,7 @@ void SoundBoardView::start_tx(const uint32_t id) {
|
||||
1536000 / 20, // Update vu-meter at 20Hz
|
||||
transmitter_model.channel_bandwidth(),
|
||||
0, // Gain is unused
|
||||
8, // shift_bits_s16, default 8 bits, but also unused
|
||||
TONES_F2D(tone_key_frequency(tone_key_index), 1536000),
|
||||
0, //AM
|
||||
0, //DSB
|
||||
@@ -295,7 +297,8 @@ SoundBoardView::~SoundBoardView() {
|
||||
|
||||
stop();
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -161,7 +161,12 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
}
|
||||
else options_band.set_by_value(receiver_model.tuning_frequency());
|
||||
|
||||
radio::enable({
|
||||
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.
|
||||
|
||||
/* radio::enable({
|
||||
tuning_frequency(),
|
||||
sampling_rate,
|
||||
baseband_bandwidth,
|
||||
@@ -169,7 +174,7 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
receiver_model.rf_amp(),
|
||||
static_cast<int8_t>(receiver_model.lna()),
|
||||
static_cast<int8_t>(receiver_model.vga()),
|
||||
});
|
||||
}); */
|
||||
|
||||
options_band.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->on_band_changed(v);
|
||||
@@ -200,7 +205,7 @@ TPMSAppView::~TPMSAppView() {
|
||||
app_settings.rx_frequency = target_frequency_;
|
||||
settings.save("rx_tpms", &app_settings);
|
||||
|
||||
radio::disable();
|
||||
receiver_model.disable(); // to switch off all, including DC bias and change flag enabled_
|
||||
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace ui
|
||||
console.writeln("Brumi-2021,texasyojimbo");
|
||||
console.writeln("heurist1,intoxsick,ckuethe");
|
||||
console.writeln("notpike,jLynx,zigad");
|
||||
console.writeln("MichalLeonBorsuk");
|
||||
console.writeln("MichalLeonBorsuk,jimilinuxguy");
|
||||
console.writeln("");
|
||||
break;
|
||||
|
||||
|
||||
@@ -46,10 +46,10 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
|
||||
auto entry_age = entry.age;
|
||||
|
||||
// Color decay for flights not being updated anymore
|
||||
if (entry_age < ADSB_DECAY_A) {
|
||||
if (entry_age < ADSB_CURRENT) {
|
||||
aged_color = 0x10;
|
||||
target_color = Color::green();
|
||||
} else if (entry_age < ADSB_DECAY_B) {
|
||||
} else if (entry_age < ADSB_RECENT) {
|
||||
aged_color = 0x07;
|
||||
target_color = Color::light_grey();
|
||||
} else {
|
||||
@@ -59,21 +59,13 @@ void RecentEntriesTable<AircraftRecentEntries>::draw(
|
||||
|
||||
std::string entry_string = "\x1B";
|
||||
entry_string += aged_color;
|
||||
#if false
|
||||
entry_string += to_string_hex(entry.ICAO_address, 6) + " " +
|
||||
entry.callsign + " " +
|
||||
(entry.hits <= 999 ? to_string_dec_uint(entry.hits, 4) : "999+") + " " +
|
||||
entry.time_string;
|
||||
#else
|
||||
// SBT
|
||||
entry_string +=
|
||||
(entry.callsign[0]!=' ' ? entry.callsign + " " : to_string_hex(entry.ICAO_address, 6) + " ") +
|
||||
to_string_dec_uint((unsigned int)((entry.pos.altitude+50)/100),4) +
|
||||
(entry.callsign[0]!=' ' ? entry.callsign + " " : entry.icaoStr + " ") +
|
||||
to_string_dec_uint((unsigned int)(entry.pos.altitude/100),4) +
|
||||
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, 4);
|
||||
#endif
|
||||
|
||||
painter.draw_string(
|
||||
target_rect.location(),
|
||||
@@ -124,8 +116,6 @@ ADSBRxAircraftDetailsView::ADSBRxAircraftDetailsView(
|
||||
});
|
||||
|
||||
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));
|
||||
@@ -225,7 +215,10 @@ 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_tag(trimr(entry.callsign[0]!=' ' ? entry.callsign : to_string_hex(entry.ICAO_address, 6)));
|
||||
geomap_view->update_position(entry_copy.pos.latitude, entry_copy.pos.longitude, entry_copy.velo.heading, entry_copy.pos.altitude);
|
||||
}
|
||||
}
|
||||
|
||||
ADSBRxDetailsView::~ADSBRxDetailsView() {
|
||||
@@ -277,19 +270,14 @@ ADSBRxDetailsView::ADSBRxDetailsView(
|
||||
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;
|
||||
});
|
||||
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 recursively launching the map
|
||||
geomap_view = nav.push<GeoMapView>(
|
||||
entry_copy.callsign,
|
||||
trimr(entry_copy.callsign[0]!=' ' ? entry_copy.callsign : entry_copy.icaoStr),
|
||||
entry_copy.pos.altitude,
|
||||
GeoPos::alt_unit::FEET,
|
||||
entry_copy.pos.latitude,
|
||||
@@ -309,6 +297,7 @@ void ADSBRxView::focus() {
|
||||
}
|
||||
|
||||
ADSBRxView::~ADSBRxView() {
|
||||
receiver_model.set_tuning_frequency(prevFreq); // Restore previous frequency on exit
|
||||
|
||||
// save app settings
|
||||
settings.save("rx_adsb", &app_settings);
|
||||
@@ -326,8 +315,6 @@ AircraftRecentEntry ADSBRxView::find_or_create_entry(uint32_t ICAO_address) {
|
||||
// If not found
|
||||
if (it == std::end(recent)){
|
||||
recent.emplace_front(ICAO_address); // Add it
|
||||
truncate_entries(recent); // Truncate the list
|
||||
sort_entries_by_state();
|
||||
it = find(recent, ICAO_address); // Find it again
|
||||
}
|
||||
return *it;
|
||||
@@ -342,6 +329,21 @@ void ADSBRxView::replace_entry(AircraftRecentEntry & entry)
|
||||
entry);
|
||||
}
|
||||
|
||||
void ADSBRxView::remove_old_entries()
|
||||
{
|
||||
auto it = recent.rbegin();
|
||||
auto end = recent.rend();
|
||||
while (it != end)
|
||||
{
|
||||
if (it->age_state>=4) {
|
||||
std::advance(it, 1);
|
||||
recent.erase( it.base() );
|
||||
} else {
|
||||
break; // stop looking because the list is sorted
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBRxView::sort_entries_by_state()
|
||||
{
|
||||
// Sorting List pn age_state using lambda function as comparator
|
||||
@@ -349,8 +351,8 @@ void ADSBRxView::sort_entries_by_state()
|
||||
}
|
||||
|
||||
void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
|
||||
logger = std::make_unique<ADSBLogger>();
|
||||
rtc::RTC datetime;
|
||||
std::string str_timestamp;
|
||||
std::string callsign;
|
||||
std::string str_info;
|
||||
std::string logentry;
|
||||
@@ -365,16 +367,16 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
|
||||
entry.reset_age();
|
||||
if (entry.hits==0)
|
||||
{
|
||||
entry.amp = message->amp;
|
||||
entry.amp = message->amp; // Store amplitude on first hit
|
||||
} else {
|
||||
entry.amp = ((entry.amp*15)+message->amp)>>4;
|
||||
entry.amp = ((entry.amp*15)+message->amp)>>4; // Update smoothed amplitude on updates
|
||||
}
|
||||
str_timestamp = to_string_datetime(datetime, HMS);
|
||||
entry.set_time_string(str_timestamp);
|
||||
|
||||
entry.inc_hit();
|
||||
logentry += to_string_hex_array(frame.get_raw_data(), 14) + " ";
|
||||
logentry += "ICAO:" + to_string_hex(ICAO_address, 6) + " ";
|
||||
if (logger) {
|
||||
logentry += to_string_hex_array(frame.get_raw_data(), 14) + " ";
|
||||
logentry += "ICAO:" + entry.icaoStr + " ";
|
||||
}
|
||||
|
||||
if (frame.get_DF() == DF_ADSB) {
|
||||
uint8_t msg_type = frame.get_msg_type();
|
||||
@@ -385,7 +387,9 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
|
||||
if ((msg_type >= AIRCRAFT_ID_L) && (msg_type <= AIRCRAFT_ID_H)) {
|
||||
callsign = decode_frame_id(frame);
|
||||
entry.set_callsign(callsign);
|
||||
logentry+=callsign+" ";
|
||||
if (logger) {
|
||||
logentry+=callsign+" ";
|
||||
}
|
||||
}
|
||||
// 9:
|
||||
// 18: { // Extended squitter/non-transponder
|
||||
@@ -400,30 +404,33 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
|
||||
" Lat:" + to_string_decimal(entry.pos.latitude, 2) +
|
||||
" Lon:" + to_string_decimal(entry.pos.longitude, 2);
|
||||
|
||||
// printing the coordinates in the log file with more
|
||||
// resolution, as we are not constrained by screen
|
||||
// real estate there:
|
||||
|
||||
std::string log_info = "Alt:" + to_string_dec_int(entry.pos.altitude) +
|
||||
" Lat:" + to_string_decimal(entry.pos.latitude, 7) +
|
||||
" Lon:" + to_string_decimal(entry.pos.longitude, 7);
|
||||
|
||||
entry.set_info_string(str_info);
|
||||
logentry+=log_info + " ";
|
||||
|
||||
if (logger) {
|
||||
// printing the coordinates in the log file with more
|
||||
// resolution, as we are not constrained by screen
|
||||
// real estate there:
|
||||
|
||||
std::string log_info = "Alt:" + to_string_dec_int(entry.pos.altitude) +
|
||||
" Lat:" + to_string_decimal(entry.pos.latitude, 7) +
|
||||
" Lon:" + to_string_decimal(entry.pos.longitude, 7);
|
||||
logentry+=log_info + " ";
|
||||
}
|
||||
|
||||
}
|
||||
} else if(msg_type == AIRBORNE_VEL && msg_sub >= VEL_GND_SUBSONIC && msg_sub <= VEL_AIR_SUPERSONIC){
|
||||
entry.set_frame_velo(frame);
|
||||
logentry += "Type:" + to_string_dec_uint(msg_sub) +
|
||||
" Hdg:" + to_string_dec_uint(entry.velo.heading) +
|
||||
" Spd: "+ to_string_dec_int(entry.velo.speed);
|
||||
if (logger) {
|
||||
logentry += "Type:" + to_string_dec_uint(msg_sub) +
|
||||
" Hdg:" + to_string_dec_uint(entry.velo.heading) +
|
||||
" Spd: "+ to_string_dec_int(entry.velo.speed);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
replace_entry(entry);
|
||||
} // frame.get_DF() == DF_ADSB
|
||||
|
||||
replace_entry(entry);
|
||||
|
||||
logger = std::make_unique<ADSBLogger>();
|
||||
if (logger) {
|
||||
logger->append(u"adsb.txt");
|
||||
// will log each frame in format:
|
||||
@@ -433,20 +440,69 @@ void ADSBRxView::on_frame(const ADSBFrameMessage * message) {
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBRxView::on_tick_second() {
|
||||
// Decay and refresh if needed
|
||||
for (auto& entry : recent) {
|
||||
entry.inc_age();
|
||||
|
||||
if (details_view) {
|
||||
if (send_updates && (entry.key() == detailed_entry_key)) // Check if the ICAO address match
|
||||
details_view->update(entry);
|
||||
void ADSBRxView::on_tick_second() {
|
||||
if (recent.size() <= 16){ // Not many entries update everything (16 is one screen full)
|
||||
updateDetailsAndMap(1);
|
||||
updateRecentEntries();
|
||||
} else if (updateState==0) { // Even second
|
||||
updateState = 1;
|
||||
updateDetailsAndMap(2);
|
||||
} else { // Odd second only performed when there are many entries
|
||||
updateState = 0;
|
||||
updateRecentEntries();
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBRxView::updateDetailsAndMap(int ageStep) {
|
||||
ui::GeoMarker marker;
|
||||
bool storeNewMarkers = false;
|
||||
|
||||
// Sort and truncate the entries, grouped, newest group first
|
||||
sort_entries_by_state();
|
||||
truncate_entries(recent);
|
||||
remove_old_entries();
|
||||
|
||||
// Calculate if it is time to update markers
|
||||
if (send_updates && details_view && details_view->geomap_view) {
|
||||
ticksSinceMarkerRefresh += ageStep;
|
||||
if (ticksSinceMarkerRefresh >= MARKER_UPDATE_SECONDS) { // Update other aircraft every few seconds
|
||||
storeNewMarkers = true;
|
||||
ticksSinceMarkerRefresh=0;
|
||||
}
|
||||
} else {
|
||||
ticksSinceMarkerRefresh = MARKER_UPDATE_SECONDS; // Send the markers as soon as the geoview exists
|
||||
}
|
||||
|
||||
// Sort the list if it is being displayed
|
||||
if (!send_updates) {
|
||||
sort_entries_by_state();
|
||||
// Increment age, and pass updates to the details and map
|
||||
const bool otherMarkersCanBeSent = send_updates && storeNewMarkers && details_view && details_view->geomap_view; // Save retesting all of this
|
||||
MapMarkerStored markerStored = MARKER_NOT_STORED;
|
||||
if (otherMarkersCanBeSent) {details_view->geomap_view->clear_markers();}
|
||||
// Loop through all entries
|
||||
for (auto& entry : recent) {
|
||||
entry.inc_age(ageStep);
|
||||
|
||||
// Only if there is a details view
|
||||
if (send_updates && details_view) {
|
||||
if (entry.key() == detailed_entry_key) // Check if the ICAO address match
|
||||
{
|
||||
details_view->update(entry);
|
||||
}
|
||||
// Store if the view is present and the list isn't full
|
||||
else if (otherMarkersCanBeSent && (markerStored != MARKER_LIST_FULL) && entry.pos.valid && (entry.age_state<=2))
|
||||
{
|
||||
marker.lon = entry.pos.longitude;
|
||||
marker.lat = entry.pos.latitude;
|
||||
marker.angle = entry.velo.heading;
|
||||
marker.tag = trimr(entry.callsign[0]!=' ' ? entry.callsign : entry.icaoStr);
|
||||
markerStored = details_view->geomap_view->store_marker(marker);
|
||||
}
|
||||
}
|
||||
} // Loop through all entries, if only to update the age
|
||||
}
|
||||
|
||||
void ADSBRxView::updateRecentEntries() {
|
||||
// Redraw the list of aircraft
|
||||
if (!send_updates){
|
||||
recent_entries_view.set_dirty();
|
||||
}
|
||||
}
|
||||
@@ -492,7 +548,7 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
|
||||
on_tick_second();
|
||||
};
|
||||
|
||||
prevFreq = receiver_model.tuning_frequency();
|
||||
prevFreq = receiver_model.tuning_frequency(); // Store previous frequency on creation
|
||||
|
||||
baseband::set_adsb();
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_geomap.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
#include "database.hpp"
|
||||
@@ -39,9 +40,9 @@ using namespace adsb;
|
||||
|
||||
namespace ui {
|
||||
|
||||
#define ADSB_DECAY_A 10 // In seconds
|
||||
#define ADSB_DECAY_B 30
|
||||
#define ADSB_DECAY_C 60 // Can be used for removing old entries, RecentEntries already caps to 64
|
||||
#define ADSB_CURRENT 10 // Seconds
|
||||
#define ADSB_RECENT 30 // Seconds
|
||||
#define ADSB_REMOVE 300 // Used for removing old entries
|
||||
|
||||
#define AIRCRAFT_ID_L 1 // aircraft ID message type (lowest type id)
|
||||
#define AIRCRAFT_ID_H 4 // aircraft ID message type (highest type id)
|
||||
@@ -83,14 +84,15 @@ struct AircraftRecentEntry {
|
||||
ADSBFrame frame_pos_even { };
|
||||
ADSBFrame frame_pos_odd { };
|
||||
|
||||
std::string icaoStr {" "};
|
||||
std::string callsign { " " };
|
||||
std::string time_string { "" };
|
||||
std::string info_string { "" };
|
||||
|
||||
AircraftRecentEntry(
|
||||
const uint32_t ICAO_address
|
||||
) : ICAO_address { ICAO_address }
|
||||
{
|
||||
this->icaoStr = to_string_hex(ICAO_address, 6);
|
||||
}
|
||||
|
||||
Key key() const {
|
||||
@@ -125,23 +127,20 @@ struct AircraftRecentEntry {
|
||||
info_string = new_info_string;
|
||||
}
|
||||
|
||||
void set_time_string(std::string& new_time_string) {
|
||||
time_string = new_time_string;
|
||||
}
|
||||
|
||||
void reset_age() {
|
||||
age = 0;
|
||||
}
|
||||
|
||||
void inc_age() {
|
||||
age++;
|
||||
if (age < ADSB_DECAY_A)
|
||||
{
|
||||
void inc_age(int delta) {
|
||||
age+=delta;
|
||||
if (age < ADSB_CURRENT){
|
||||
age_state = pos.valid ? 0 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
age_state = (age < ADSB_DECAY_B) ? 2 : 3;
|
||||
} else if(age < ADSB_RECENT){
|
||||
age_state = 2;
|
||||
} else if(age < ADSB_REMOVE){
|
||||
age_state = 3;
|
||||
} else{
|
||||
age_state = 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -273,10 +272,10 @@ public:
|
||||
|
||||
std::database::AirlinesDBRecord airline_record = {};
|
||||
|
||||
GeoMapView* geomap_view { nullptr };
|
||||
private:
|
||||
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 = { };
|
||||
@@ -364,6 +363,7 @@ public:
|
||||
std::string title() const override { return "ADS-B RX"; };
|
||||
|
||||
void replace_entry(AircraftRecentEntry & entry);
|
||||
void remove_old_entries();
|
||||
AircraftRecentEntry find_or_create_entry(uint32_t ICAO_address);
|
||||
void sort_entries_by_state();
|
||||
|
||||
@@ -372,24 +372,23 @@ private:
|
||||
std::unique_ptr<ADSBLogger> logger { };
|
||||
void on_frame(const ADSBFrameMessage * message);
|
||||
void on_tick_second();
|
||||
int updateState = { 0 };
|
||||
void updateRecentEntries();
|
||||
void updateDetailsAndMap(int ageStep);
|
||||
|
||||
#define MARKER_UPDATE_SECONDS (5)
|
||||
int ticksSinceMarkerRefresh { MARKER_UPDATE_SECONDS-1 };
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
|
||||
const RecentEntriesColumns columns { {
|
||||
#if false
|
||||
{ "ICAO", 6 },
|
||||
{ "Callsign", 9 },
|
||||
{ "Hits", 4 },
|
||||
{ "Time", 8 }
|
||||
#else
|
||||
{ "ICAO/Call", 9 },
|
||||
{ "Lvl", 3 },
|
||||
{ "Spd", 3 },
|
||||
{ "Amp", 3 },
|
||||
{ "Hit", 3 },
|
||||
{ "Age", 4 }
|
||||
#endif
|
||||
} };
|
||||
AircraftRecentEntries recent { };
|
||||
RecentEntriesView<RecentEntries<AircraftRecentEntry>> recent_entries_view { columns, recent };
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "manchester.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include <cstring>
|
||||
@@ -161,13 +162,16 @@ ADSBSpeedView::ADSBSpeedView(
|
||||
|
||||
add_children({
|
||||
&labels_speed,
|
||||
&labels_vert_rate,
|
||||
&compass,
|
||||
&field_angle,
|
||||
&field_speed
|
||||
&field_speed,
|
||||
&field_vert_rate
|
||||
});
|
||||
|
||||
field_angle.set_value(0);
|
||||
field_speed.set_value(400);
|
||||
field_vert_rate.set_value(0);
|
||||
|
||||
field_angle.on_change = [this](int32_t v) {
|
||||
compass.set_value(v);
|
||||
@@ -180,7 +184,7 @@ void ADSBSpeedView::collect_frames(const uint32_t ICAO_address, std::vector<ADSB
|
||||
ADSBFrame temp_frame;
|
||||
|
||||
encode_frame_velo(temp_frame, ICAO_address, field_speed.value(),
|
||||
field_angle.value(), 0); // TODO: v_rate
|
||||
field_angle.value(), field_vert_rate.value()); // Added v_rate , ft/min , (+) climb , (-) descend .
|
||||
|
||||
frame_list.emplace_back(temp_frame);
|
||||
}
|
||||
@@ -203,7 +207,7 @@ void ADSBSquawkView::collect_frames(const uint32_t ICAO_address, std::vector<ADS
|
||||
ADSBFrame temp_frame;
|
||||
(void)ICAO_address;
|
||||
|
||||
encode_frame_squawk(temp_frame, field_squawk.value_dec_u32());
|
||||
encode_frame_squawk(temp_frame, field_squawk.concatenate_4_octal_u16());
|
||||
|
||||
frame_list.emplace_back(temp_frame);
|
||||
}
|
||||
@@ -290,8 +294,9 @@ ADSBTxView::~ADSBTxView() {
|
||||
settings.save("tx_adsb", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, withouth ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void ADSBTxView::generate_frames() {
|
||||
const uint32_t ICAO_address = sym_icao.value_hex_u64();
|
||||
|
||||
@@ -96,6 +96,10 @@ private:
|
||||
{ { 1 * 8, 6 * 16 }, "Speed: kn Bearing: *", Color::light_grey() }
|
||||
};
|
||||
|
||||
Labels labels_vert_rate {
|
||||
{ { 1 * 8, 8 * 16 }, "Vert. rate: ft/min, (+/-)", Color::light_grey() }
|
||||
};
|
||||
|
||||
Compass compass {
|
||||
{ 21 * 8, 2 * 16 }
|
||||
};
|
||||
@@ -107,6 +111,10 @@ private:
|
||||
NumberField field_speed {
|
||||
{ 8 * 8, 6 * 16 }, 3, { 0, 999 }, 5, ' '
|
||||
};
|
||||
|
||||
NumberField field_vert_rate {
|
||||
{ 11 * 8, 8 * 16 }, 5, { -4096, 4096 }, 64, ' ' // Let's limit to +/-5k aprox , Ex. max safe descent vertical rate aprox -1000 ft/min on an instrument approach. , std step is 64
|
||||
};
|
||||
};
|
||||
|
||||
class ADSBSquawkView : public OptionTabView {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "aprs.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
@@ -48,7 +49,8 @@ APRSTXView::~APRSTXView() {
|
||||
settings.save("tx_aprs", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void APRSTXView::start_tx() {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@@ -145,6 +146,8 @@ BHTView::~BHTView() {
|
||||
settings.save("tx_bht", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
BHTView::BHTView(NavigationView& nav) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "ui_coasterp.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
@@ -42,7 +43,8 @@ CoasterPagerView::~CoasterPagerView() {
|
||||
settings.save("tx_coaster", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void CoasterPagerView::generate_frame() {
|
||||
|
||||
@@ -322,17 +322,19 @@ void DebugControlsView::focus() {
|
||||
/* DebugPeripheralsMenuView **********************************************/
|
||||
|
||||
DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
|
||||
const char * max283x = hackrf_r9 ? "MAX2839" : "MAX2837";
|
||||
const char * si5351x = hackrf_r9 ? "Si5351A" : "Si5351C";
|
||||
add_items({
|
||||
{ "RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
|
||||
"RFFC5072", RegistersWidgetConfig { 31, 16 },
|
||||
[](const size_t register_number) { return radio::debug::first_if::register_read(register_number); }
|
||||
); } },
|
||||
{ "MAX2837", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
|
||||
"MAX2837", RegistersWidgetConfig { 32, 10 },
|
||||
{ max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x](){ nav.push<RegistersView>(
|
||||
max283x, RegistersWidgetConfig { 32, 10 },
|
||||
[](const size_t register_number) { return radio::debug::second_if::register_read(register_number); }
|
||||
); } },
|
||||
{ "Si5351C", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
|
||||
"Si5351C", RegistersWidgetConfig { 96, 8 },
|
||||
{ si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x](){ nav.push<RegistersView>(
|
||||
si5351x, RegistersWidgetConfig { 96, 8 },
|
||||
[](const size_t register_number) { return portapack::clock_generator.read_register(register_number); }
|
||||
); } },
|
||||
{ audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav](){ nav.push<RegistersView>(
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
#include "rffc507x.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -35,18 +36,21 @@ EncodersConfigView::EncodersConfigView(
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
std::vector<option_t> enc_options;
|
||||
size_t i;
|
||||
|
||||
|
||||
set_parent_rect(parent_rect);
|
||||
hidden(true);
|
||||
|
||||
|
||||
// Default encoder def
|
||||
encoder_def = &encoder_defs[0];
|
||||
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&options_enctype,
|
||||
&field_repeat_min,
|
||||
&field_clk,
|
||||
&field_clk_step,
|
||||
&field_frameduration,
|
||||
&field_frameduration_step,
|
||||
&symfield_word,
|
||||
&text_format,
|
||||
&waveform
|
||||
@@ -55,18 +59,18 @@ EncodersConfigView::EncodersConfigView(
|
||||
// Load encoder types in option field
|
||||
for (i = 0; i < ENC_TYPES_COUNT; i++)
|
||||
enc_options.emplace_back(std::make_pair(encoder_defs[i].name, i));
|
||||
|
||||
|
||||
options_enctype.on_change = [this](size_t index, int32_t) {
|
||||
on_type_change(index);
|
||||
};
|
||||
|
||||
|
||||
options_enctype.set_options(enc_options);
|
||||
options_enctype.set_selected_index(0);
|
||||
|
||||
|
||||
symfield_word.on_change = [this]() {
|
||||
generate_frame();
|
||||
};
|
||||
|
||||
|
||||
// Selecting input clock changes symbol and word duration
|
||||
field_clk.on_change = [this](int32_t value) {
|
||||
// value is in kHz, new_value is in us
|
||||
@@ -74,7 +78,11 @@ EncodersConfigView::EncodersConfigView(
|
||||
if (new_value != field_frameduration.value())
|
||||
field_frameduration.set_value(new_value * encoder_def->word_length, false);
|
||||
};
|
||||
|
||||
|
||||
field_clk_step.on_change = [this](size_t, int32_t value) {
|
||||
field_clk.set_step(value);
|
||||
};
|
||||
|
||||
// Selecting word duration changes input clock and symbol duration
|
||||
field_frameduration.on_change = [this](int32_t value) {
|
||||
// value is in us, new_value is in kHz
|
||||
@@ -82,6 +90,10 @@ EncodersConfigView::EncodersConfigView(
|
||||
if (new_value != field_clk.value())
|
||||
field_clk.set_value(1000000 / new_value, false);
|
||||
};
|
||||
|
||||
field_frameduration_step.on_change = [this](size_t, int32_t value) {
|
||||
field_frameduration.set_step(value);
|
||||
};
|
||||
}
|
||||
|
||||
void EncodersConfigView::focus() {
|
||||
@@ -96,7 +108,8 @@ void EncodersConfigView::on_type_change(size_t index) {
|
||||
encoder_def = &encoder_defs[index];
|
||||
|
||||
field_clk.set_value(encoder_def->default_speed / 1000);
|
||||
|
||||
field_repeat_min.set_value(encoder_def->repeat_min);
|
||||
|
||||
// SymField setup
|
||||
word_length = encoder_def->word_length;
|
||||
symfield_word.set_length(word_length);
|
||||
@@ -111,10 +124,10 @@ void EncodersConfigView::on_type_change(size_t index) {
|
||||
format_string += 'D';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Ugly :( Pad to erase
|
||||
format_string.append(24 - format_string.size(), ' ');
|
||||
|
||||
|
||||
text_format.set(format_string);
|
||||
|
||||
generate_frame();
|
||||
@@ -130,28 +143,30 @@ void EncodersConfigView::draw_waveform() {
|
||||
|
||||
for (size_t n = 0; n < length; n++)
|
||||
waveform_buffer[n] = (frame_fragments[n] == '0') ? 0 : 1;
|
||||
|
||||
|
||||
waveform.set_length(length);
|
||||
waveform.set_dirty();
|
||||
}
|
||||
|
||||
void EncodersConfigView::generate_frame() {
|
||||
size_t i = 0;
|
||||
|
||||
|
||||
frame_fragments.clear();
|
||||
|
||||
|
||||
for (auto c : encoder_def->word_format) {
|
||||
if (c == 'S')
|
||||
frame_fragments += encoder_def->sync;
|
||||
else if (!c)
|
||||
break;
|
||||
else
|
||||
frame_fragments += encoder_def->bit_format[symfield_word.get_sym(i++)];
|
||||
}
|
||||
|
||||
|
||||
draw_waveform();
|
||||
}
|
||||
|
||||
uint8_t EncodersConfigView::repeat_min() {
|
||||
return encoder_def->repeat_min;
|
||||
return field_repeat_min.value();
|
||||
}
|
||||
|
||||
uint32_t EncodersConfigView::samples_per_bit() {
|
||||
@@ -163,7 +178,7 @@ uint32_t EncodersConfigView::pause_symbols() {
|
||||
}
|
||||
|
||||
void EncodersScanView::focus() {
|
||||
field_debug.focus();
|
||||
field_length.focus();
|
||||
}
|
||||
|
||||
EncodersScanView::EncodersScanView(
|
||||
@@ -171,31 +186,17 @@ EncodersScanView::EncodersScanView(
|
||||
) {
|
||||
set_parent_rect(parent_rect);
|
||||
hidden(true);
|
||||
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&field_debug,
|
||||
&text_debug,
|
||||
&text_length
|
||||
&field_length,
|
||||
&bit_length_10,
|
||||
&bit_length
|
||||
});
|
||||
|
||||
// DEBUG
|
||||
field_debug.on_change = [this](int32_t value) {
|
||||
uint32_t l;
|
||||
size_t length;
|
||||
|
||||
de_bruijn debruijn_seq;
|
||||
length = debruijn_seq.init(value);
|
||||
|
||||
l = 1;
|
||||
l <<= value;
|
||||
l--;
|
||||
if (l > 25)
|
||||
l = 25;
|
||||
text_debug.set(to_string_bin(debruijn_seq.compute(l), 25));
|
||||
|
||||
text_length.set(to_string_dec_uint(length));
|
||||
};
|
||||
|
||||
field_length.set_value(8);
|
||||
bit_length_10.set_value(40);
|
||||
bit_length.set_value(0);
|
||||
}
|
||||
|
||||
void EncodersView::focus() {
|
||||
@@ -204,33 +205,21 @@ void EncodersView::focus() {
|
||||
|
||||
EncodersView::~EncodersView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_ook", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // ghost signal c/m to the problem at the exit .
|
||||
baseband::shutdown(); // better this function after load_sram()
|
||||
}
|
||||
|
||||
void EncodersView::update_progress() {
|
||||
std::string str_buffer;
|
||||
|
||||
// text_status.set(" ");
|
||||
|
||||
if (tx_mode == SINGLE) {
|
||||
|
||||
if (tx_mode == SINGLE || tx_mode == SCAN) {
|
||||
str_buffer = to_string_dec_uint(repeat_index) + "/" + to_string_dec_uint(repeat_min);
|
||||
text_status.set(str_buffer);
|
||||
progressbar.set_value(repeat_index);
|
||||
|
||||
/*} else if (tx_mode == SCAN) {
|
||||
strcpy(str, to_string_dec_uint(repeat_index).c_str());
|
||||
strcat(str, "/");
|
||||
strcat(str, to_string_dec_uint(portapack::persistent_memory::afsk_repeats()).c_str());
|
||||
strcat(str, " ");
|
||||
strcat(str, to_string_dec_uint(scan_index + 1).c_str());
|
||||
strcat(str, "/");
|
||||
strcat(str, to_string_dec_uint(scan_count).c_str());
|
||||
text_status.set(str);
|
||||
progress.set_value(scan_progress);*/
|
||||
} else {
|
||||
text_status.set("Ready");
|
||||
progressbar.set_value(0);
|
||||
@@ -238,89 +227,59 @@ void EncodersView::update_progress() {
|
||||
}
|
||||
|
||||
void EncodersView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
//char str[16];
|
||||
|
||||
if (!done) {
|
||||
// Repeating...
|
||||
repeat_index = progress + 1;
|
||||
|
||||
/*if (tx_mode == SCAN) {
|
||||
scan_progress++;
|
||||
update_progress();
|
||||
} else {*/
|
||||
update_progress();
|
||||
//}
|
||||
update_progress();
|
||||
} else {
|
||||
// make sure all samples are transmitted before disabling radio
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
// Done transmitting
|
||||
/*if ((tx_mode == SCAN) && (scan_index < (scan_count - 1))) {
|
||||
transmitter_model.disable();
|
||||
if (abort_scan) {
|
||||
// Kill scan process
|
||||
strcpy(str, "Abort @");
|
||||
strcat(str, rgsb);
|
||||
text_status.set(str);
|
||||
progress.set_value(0);
|
||||
tx_mode = IDLE;
|
||||
abort_scan = false;
|
||||
button_scan.set_style(&style_val);
|
||||
button_scan.set_text("SCAN");
|
||||
} else {
|
||||
// Next address
|
||||
scan_index++;
|
||||
strcpy(rgsb, &scan_list[options_scanlist.selected_index()].addresses[scan_index * 5]);
|
||||
scan_progress++;
|
||||
repeat_index = 1;
|
||||
update_progress();
|
||||
start_tx(true);
|
||||
}
|
||||
} else {*/
|
||||
transmitter_model.disable();
|
||||
tx_mode = IDLE;
|
||||
text_status.set("Done");
|
||||
progressbar.set_value(0);
|
||||
tx_view.set_transmitting(false);
|
||||
//}
|
||||
transmitter_model.disable();
|
||||
tx_mode = IDLE;
|
||||
text_status.set("Done");
|
||||
progressbar.set_value(0);
|
||||
tx_view.set_transmitting(false);
|
||||
}
|
||||
}
|
||||
|
||||
void EncodersView::start_tx(const bool scan) {
|
||||
(void)scan;
|
||||
size_t bitstream_length = 0;
|
||||
|
||||
repeat_min = view_config.repeat_min();
|
||||
|
||||
/*if (scan) {
|
||||
if (tx_mode != SCAN) {
|
||||
scan_index = 0;
|
||||
scan_count = scan_list[options_scanlist.selected_index()].count;
|
||||
scan_progress = 1;
|
||||
repeat_index = 1;
|
||||
tx_mode = SCAN;
|
||||
strcpy(rgsb, &scan_list[options_scanlist.selected_index()].addresses[0]);
|
||||
progress.set_max(scan_count * afsk_repeats);
|
||||
update_progress();
|
||||
}
|
||||
} else {*/
|
||||
int scan_width = 0;
|
||||
uint32_t samples_per_bit;
|
||||
|
||||
if (scan) {
|
||||
tx_mode = SCAN;
|
||||
scan_width = view_scan.field_length.value();
|
||||
samples_per_bit =
|
||||
((view_scan.bit_length_10.value() * 10 + view_scan.bit_length.value()) * OOK_SAMPLERATE) / 1000000UL;
|
||||
constexpr auto sym_len = 4;
|
||||
const uint32_t seq_len = ((1 << (scan_width - 1)) * 2) * ((uint64_t) samples_per_bit) * sym_len / 2048UL;
|
||||
progressbar.set_max(seq_len);
|
||||
repeat_min = seq_len;
|
||||
} else {
|
||||
tx_mode = SINGLE;
|
||||
repeat_index = 1;
|
||||
samples_per_bit = view_config.samples_per_bit();
|
||||
view_config.generate_frame();
|
||||
bitstream_length = make_bitstream(view_config.frame_fragments);
|
||||
progressbar.set_max(repeat_min);
|
||||
update_progress();
|
||||
//}
|
||||
|
||||
view_config.generate_frame();
|
||||
|
||||
bitstream_length = make_bitstream(view_config.frame_fragments);
|
||||
repeat_min = view_config.repeat_min();
|
||||
}
|
||||
|
||||
repeat_index = 1;
|
||||
update_progress();
|
||||
|
||||
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
|
||||
baseband::set_ook_data(
|
||||
bitstream_length,
|
||||
view_config.samples_per_bit(),
|
||||
samples_per_bit,
|
||||
repeat_min,
|
||||
view_config.pause_symbols()
|
||||
view_config.pause_symbols(),
|
||||
scan_width
|
||||
);
|
||||
}
|
||||
|
||||
@@ -338,14 +297,14 @@ EncodersView::EncodersView(
|
||||
&progressbar,
|
||||
&tx_view
|
||||
});
|
||||
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_ook", &app_settings);
|
||||
if(rc == SETTINGS_OK) {
|
||||
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);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
@@ -354,14 +313,16 @@ EncodersView::EncodersView(
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
tx_view.on_start = [this]() {
|
||||
tx_view.set_transmitting(true);
|
||||
start_tx(false);
|
||||
start_tx(tab_view.selected());
|
||||
};
|
||||
|
||||
|
||||
tx_view.on_stop = [this]() {
|
||||
tx_view.set_transmitting(false);
|
||||
baseband::kill_ook();
|
||||
transmitter_model.disable();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -35,43 +35,41 @@ namespace ui {
|
||||
class EncodersConfigView : public View {
|
||||
public:
|
||||
EncodersConfigView(NavigationView& nav, Rect parent_rect);
|
||||
|
||||
|
||||
EncodersConfigView(const EncodersConfigView&) = delete;
|
||||
EncodersConfigView(EncodersConfigView&&) = delete;
|
||||
EncodersConfigView& operator=(const EncodersConfigView&) = delete;
|
||||
EncodersConfigView& operator=(EncodersConfigView&&) = delete;
|
||||
|
||||
|
||||
void focus() override;
|
||||
void on_show() override;
|
||||
|
||||
|
||||
uint8_t repeat_min();
|
||||
uint32_t samples_per_bit();
|
||||
uint32_t pause_symbols();
|
||||
void generate_frame();
|
||||
|
||||
|
||||
std::string frame_fragments = "0";
|
||||
|
||||
private:
|
||||
//bool abort_scan = false;
|
||||
//uint8_t scan_count;
|
||||
//double scan_progress;
|
||||
//unsigned int scan_index;
|
||||
int16_t waveform_buffer[550];
|
||||
const encoder_def_t * encoder_def { };
|
||||
//uint8_t enc_type = 0;
|
||||
|
||||
void draw_waveform();
|
||||
void on_bitfield();
|
||||
void on_type_change(size_t index);
|
||||
|
||||
|
||||
Labels labels {
|
||||
{ { 1 * 8, 0 }, "Type:", Color::light_grey() },
|
||||
{ { 16 * 8, 0 }, "Clk:", Color::light_grey() },
|
||||
{ { 24 * 8, 0 }, "kHz", Color::light_grey() },
|
||||
{ { 14 * 8, 2 * 8 }, "Frame:", Color::light_grey() },
|
||||
{ { 26 * 8, 2 * 8 }, "us", Color::light_grey() },
|
||||
{ { 2 * 8, 4 * 8 }, "Symbols:", Color::light_grey() },
|
||||
{ { 1 * 8, 11 * 8 }, "Waveform:", Color::light_grey() }
|
||||
{ { 17 * 8, 0 }, "Repeat:", Color::light_grey() },
|
||||
{ { 1 * 8, 2 * 8 }, "Clk:", Color::light_grey() },
|
||||
{ { 10 * 8, 2 * 8 }, "kHz", Color::light_grey() },
|
||||
{ { 17 * 8, 2 * 8 }, "Step:", Color::light_grey() },
|
||||
{ { 1 * 8, 4 * 8 }, "Frame:", Color::light_grey() },
|
||||
{ { 13 * 8, 4 * 8 }, "us", Color::light_grey() },
|
||||
{ { 17 * 8, 4 * 8 }, "Step:", Color::light_grey() },
|
||||
{ { 2 * 8, 7 * 8 }, "Symbols:", Color::light_grey() },
|
||||
{ { 1 * 8, 14 * 8 }, "Waveform:", Color::light_grey() }
|
||||
};
|
||||
|
||||
OptionsField options_enctype { // Options are loaded at runtime
|
||||
@@ -82,34 +80,63 @@ private:
|
||||
};
|
||||
|
||||
NumberField field_clk {
|
||||
{ 21 * 8, 0 },
|
||||
3,
|
||||
{ 1, 500 },
|
||||
{ 5 * 8, 2 * 8 },
|
||||
4,
|
||||
{ 1, 1000 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
NumberField field_repeat_min {
|
||||
{ 24 * 8, 0 },
|
||||
2,
|
||||
{ 1, 99 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
OptionsField field_clk_step {
|
||||
{ 22 * 8, 2 * 8 },
|
||||
7,
|
||||
{
|
||||
{ "1", 1 },
|
||||
{ "10", 10 },
|
||||
{ "100", 100 }
|
||||
}
|
||||
};
|
||||
|
||||
NumberField field_frameduration {
|
||||
{ 21 * 8, 2 * 8 },
|
||||
{ 7 * 8, 4 * 8 },
|
||||
5,
|
||||
{ 300, 99999 },
|
||||
100,
|
||||
' '
|
||||
};
|
||||
|
||||
|
||||
OptionsField field_frameduration_step {
|
||||
{ 22 * 8, 4 * 8 },
|
||||
7,
|
||||
{
|
||||
{ "1", 1 },
|
||||
{ "10", 10 },
|
||||
{ "100", 100 },
|
||||
{ "1000", 1000 }
|
||||
}
|
||||
};
|
||||
|
||||
SymField symfield_word {
|
||||
{ 2 * 8, 6 * 8 },
|
||||
{ 2 * 8, 9 * 8 },
|
||||
20,
|
||||
SymField::SYMFIELD_DEF
|
||||
};
|
||||
|
||||
|
||||
Text text_format {
|
||||
{ 2 * 8, 8 * 8, 24 * 8, 16 },
|
||||
{ 2 * 8, 11 * 8, 24 * 8, 16 },
|
||||
""
|
||||
};
|
||||
|
||||
|
||||
Waveform waveform {
|
||||
{ 0, 14 * 8, 240, 32 },
|
||||
{ 0, 17 * 8, 240, 32 },
|
||||
waveform_buffer,
|
||||
0,
|
||||
0,
|
||||
@@ -122,33 +149,38 @@ private:
|
||||
class EncodersScanView : public View {
|
||||
public:
|
||||
EncodersScanView(NavigationView& nav, Rect parent_rect);
|
||||
|
||||
|
||||
NumberField field_length {
|
||||
{ 8 * 8, 0 },
|
||||
2,
|
||||
{ 3, 24 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
NumberField bit_length_10 {
|
||||
{ 12 * 8, 2 * 8 },
|
||||
2,
|
||||
{ 1, 88 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
NumberField bit_length {
|
||||
{ 14 * 8, 2 * 8 },
|
||||
1,
|
||||
{ 0, 9 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
Labels labels {
|
||||
{ { 1 * 8, 1 * 8 }, "Coming soon...", Color::light_grey() }
|
||||
};
|
||||
|
||||
// DEBUG
|
||||
NumberField field_debug {
|
||||
{ 1 * 8, 6 * 8 },
|
||||
2,
|
||||
{ 3, 16 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
|
||||
// DEBUG
|
||||
Text text_debug {
|
||||
{ 1 * 8, 8 * 8, 24 * 8, 16 },
|
||||
""
|
||||
};
|
||||
|
||||
// DEBUG
|
||||
Text text_length {
|
||||
{ 1 * 8, 10 * 8, 24 * 8, 16 },
|
||||
""
|
||||
{ { 1 * 8, 0 * 8 }, "Length:", Color::light_grey() },
|
||||
{ { 1 * 8, 2 * 8 }, "Bit length:", Color::light_grey() },
|
||||
{ { 16 * 8, 2 * 8 }, "us", Color::light_grey() },
|
||||
};
|
||||
};
|
||||
|
||||
@@ -156,9 +188,9 @@ class EncodersView : public View {
|
||||
public:
|
||||
EncodersView(NavigationView& nav);
|
||||
~EncodersView();
|
||||
|
||||
|
||||
void focus() override;
|
||||
|
||||
|
||||
std::string title() const override { return "OOK TX"; };
|
||||
|
||||
private:
|
||||
@@ -169,19 +201,19 @@ private:
|
||||
SINGLE,
|
||||
SCAN
|
||||
};
|
||||
|
||||
|
||||
// app save settings
|
||||
std::app_settings 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 };
|
||||
|
||||
uint32_t repeat_index { 0 };
|
||||
uint32_t repeat_min { 0 };
|
||||
|
||||
void update_progress();
|
||||
void start_tx(const bool scan);
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
|
||||
/*const Style style_address {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
@@ -192,32 +224,32 @@ private:
|
||||
.background = Color::black(),
|
||||
.foreground = Color::blue(),
|
||||
};*/
|
||||
|
||||
|
||||
Rect view_rect = { 0, 4 * 8, 240, 168 };
|
||||
|
||||
|
||||
EncodersConfigView view_config { nav_, view_rect };
|
||||
EncodersScanView view_scan { nav_, view_rect };
|
||||
|
||||
|
||||
TabView tab_view {
|
||||
{ "Config", Color::cyan(), &view_config },
|
||||
{ "Scan", Color::green(), &view_scan },
|
||||
{ "de Bruijn", Color::green(), &view_scan },
|
||||
};
|
||||
|
||||
Text text_status {
|
||||
{ 2 * 8, 13 * 16, 128, 16 },
|
||||
"Ready"
|
||||
};
|
||||
|
||||
|
||||
ProgressBar progressbar {
|
||||
{ 2 * 8, 13 * 16 + 20, 208, 16 }
|
||||
};
|
||||
|
||||
|
||||
TransmitterView tx_view {
|
||||
16 * 16,
|
||||
50000,
|
||||
9
|
||||
};
|
||||
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress {
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@@ -183,7 +184,8 @@ void JammerView::focus() {
|
||||
|
||||
JammerView::~JammerView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void JammerView::on_retune(const rf::Frequency freq, const uint32_t range) {
|
||||
@@ -268,9 +270,9 @@ void JammerView::start_tx() {
|
||||
button_transmit.set_text("STOP");
|
||||
|
||||
transmitter_model.set_sampling_rate(3072000U);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_rf_amp(field_amp.value());
|
||||
transmitter_model.set_baseband_bandwidth(3500000U);
|
||||
transmitter_model.set_tx_gain(47);
|
||||
transmitter_model.set_tx_gain(field_gain.value());
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
|
||||
@@ -366,6 +368,8 @@ JammerView::JammerView(
|
||||
&field_timetx,
|
||||
&field_timepause,
|
||||
&field_jitter,
|
||||
&field_gain,
|
||||
&field_amp,
|
||||
&button_transmit
|
||||
});
|
||||
|
||||
@@ -380,6 +384,8 @@ JammerView::JammerView(
|
||||
|
||||
field_timetx.set_value(30);
|
||||
field_timepause.set_value(1);
|
||||
field_gain.set_value(transmitter_model.tx_gain());
|
||||
field_amp.set_value(transmitter_model.rf_amp());
|
||||
|
||||
button_transmit.on_select = [this](Button&) {
|
||||
if (jamming || cooling)
|
||||
|
||||
@@ -156,7 +156,9 @@ private:
|
||||
{ { 0 * 8, 33 * 8 }, "Jitter:", Color::light_grey() }, //Maybe the repository curator can keep the "mystype" for some versions.
|
||||
{ { 11 * 8, 29 * 8 }, "Secs.", Color::light_grey() },
|
||||
{ { 11 * 8, 31 * 8 }, "Secs.", Color::light_grey() },
|
||||
{ { 11 * 8, 33 * 8 }, "/60", Color::light_grey() }
|
||||
{ { 11 * 8, 33 * 8 }, "/60", Color::light_grey() },
|
||||
{ { 2 * 8, 35 * 8 }, "Gain:", Color::light_grey() },
|
||||
{ { 11 * 8, 35 * 8 }, "A:", Color::light_grey() }
|
||||
};
|
||||
|
||||
OptionsField options_type {
|
||||
@@ -229,8 +231,24 @@ private:
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_gain {
|
||||
{ 8 * 8, 35 * 8 },
|
||||
2,
|
||||
{ 0, 47 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_amp {
|
||||
{ 13 * 8, 35 * 8 },
|
||||
1,
|
||||
{ 0, 1 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
Button button_transmit {
|
||||
{ 148, 212, 80, 80},
|
||||
{ 148, 216, 80, 80},
|
||||
"START"
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "ui_keyfob.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@@ -140,7 +141,8 @@ KeyfobView::~KeyfobView() {
|
||||
settings.save("tx_keyfob", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void KeyfobView::update_progress(const uint32_t progress) {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "modems.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
#include "serializer.hpp"
|
||||
|
||||
@@ -44,7 +45,8 @@ LCRView::~LCRView() {
|
||||
settings.save("tx_lcr", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -22,315 +22,340 @@
|
||||
|
||||
#include "ui_looking_glass_app.hpp"
|
||||
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui
|
||||
{
|
||||
|
||||
void GlassView::focus() {
|
||||
field_marker.focus();
|
||||
}
|
||||
|
||||
GlassView::~GlassView() {
|
||||
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void GlassView::on_lna_changed(int32_t v_db) {
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
|
||||
void GlassView::on_vga_changed(int32_t v_db) {
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
|
||||
void GlassView::add_spectrum_pixel(Color color)
|
||||
{
|
||||
spectrum_row[pixel_index++] = color;
|
||||
if (pixel_index == 240) //got an entire waterfall line
|
||||
{
|
||||
const auto draw_y = display.scroll(1); //Scroll 1 pixel down
|
||||
display.draw_pixels( {{0, draw_y}, {240, 1}}, spectrum_row); //new line at top
|
||||
pixel_index = 0; //Start New cascade line
|
||||
}
|
||||
}
|
||||
|
||||
//Apparently, the spectrum object returns an array of 256 bins
|
||||
//Each having the radio signal power for it's corresponding frequency slot
|
||||
void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum)
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
|
||||
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
|
||||
// Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored
|
||||
// All things said and done, we actually need 240 of those bins:
|
||||
for(uint8_t bin = 0; bin < 240; bin++)
|
||||
void GlassView::focus()
|
||||
{
|
||||
if (bin < 120) {
|
||||
if (spectrum.db[134 + bin] > max_power)
|
||||
max_power = spectrum.db[134 + bin];
|
||||
}
|
||||
else
|
||||
field_marker.focus();
|
||||
}
|
||||
|
||||
GlassView::~GlassView()
|
||||
{
|
||||
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void GlassView::on_lna_changed(int32_t v_db)
|
||||
{
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
|
||||
void GlassView::on_vga_changed(int32_t v_db)
|
||||
{
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
|
||||
void GlassView::add_spectrum_pixel(Color color)
|
||||
{
|
||||
spectrum_row[pixel_index++] = color;
|
||||
if (pixel_index == 240) // got an entire waterfall line
|
||||
{
|
||||
if (spectrum.db[bin - 118] > max_power)
|
||||
max_power = spectrum.db[bin - 118];
|
||||
display.draw_pixels({{0, display.scroll(1)}, {240, 1}}, spectrum_row); // new line at top, one less var, speedier
|
||||
pixel_index = 0; // Start New cascade line
|
||||
}
|
||||
}
|
||||
|
||||
bins_Hz_size += each_bin_size; //add this bin Hz count into the "pixel fulfilled bag of Hz"
|
||||
// Apparently, the spectrum object returns an array of 256 bins
|
||||
// Each having the radio signal power for it's corresponding frequency slot
|
||||
void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum)
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
|
||||
if (bins_Hz_size >= marker_pixel_step) //new pixel fullfilled
|
||||
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
|
||||
// Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored
|
||||
// All things said and done, we actually need 240 of those bins:
|
||||
for (uint8_t bin = 0; bin < 240; bin++)
|
||||
{
|
||||
if (min_color_power < max_power)
|
||||
add_spectrum_pixel(spectrum_rgb3_lut[max_power]); //Pixel will represent max_power
|
||||
else
|
||||
add_spectrum_pixel(0); //Filtered out, show black
|
||||
|
||||
max_power = 0;
|
||||
|
||||
if (!pixel_index) //Received indication that a waterfall line has been completed
|
||||
if (bin < 120)
|
||||
{
|
||||
bins_Hz_size = 0; //Since this is an entire pixel line, we don't carry "Pixels into next bin"
|
||||
break;
|
||||
} else {
|
||||
bins_Hz_size -= marker_pixel_step; //reset bins size, but carrying the eventual excess Hz into next pixel
|
||||
if (spectrum.db[134 + bin] > max_power) // 134
|
||||
max_power = spectrum.db[134 + bin];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spectrum.db[bin - 118] > max_power) // 118
|
||||
max_power = spectrum.db[bin - 118];
|
||||
}
|
||||
|
||||
bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz"
|
||||
|
||||
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
|
||||
{
|
||||
if (min_color_power < max_power)
|
||||
add_spectrum_pixel(spectrum_rgb3_lut[max_power]); // Pixel will represent max_power
|
||||
else
|
||||
add_spectrum_pixel(0); // Filtered out, show black
|
||||
|
||||
max_power = 0;
|
||||
|
||||
if (!pixel_index) // Received indication that a waterfall line has been completed
|
||||
{
|
||||
bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin"
|
||||
f_center = f_center_ini; // Start a new sweep
|
||||
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
|
||||
chThdSleepMilliseconds(10);
|
||||
baseband::spectrum_streaming_start(); // Do the RX
|
||||
return;
|
||||
}
|
||||
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
|
||||
}
|
||||
}
|
||||
|
||||
f_center += LOOKING_GLASS_SLICE_WIDTH; // Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH
|
||||
radio::set_tuning_frequency(f_center); // tune rx for this new slice directly, faster than using persistent memory saving
|
||||
chThdSleepMilliseconds(5);
|
||||
|
||||
// receiver_model.set_tuning_frequency(f_center); //tune rx for this slice
|
||||
baseband::spectrum_streaming_start(); // Do the RX
|
||||
}
|
||||
|
||||
if (pixel_index)
|
||||
f_center += LOOKING_GLASS_SLICE_WIDTH; //Move into the next bandwidth slice NOTE: spectrum.sampling_rate = LOOKING_GLASS_SLICE_WIDTH
|
||||
else
|
||||
f_center = f_center_ini; //Start a new sweep
|
||||
|
||||
receiver_model.set_tuning_frequency(f_center); //tune rx for this slice
|
||||
baseband::spectrum_streaming_start(); //Do the RX
|
||||
}
|
||||
|
||||
void GlassView::on_hide()
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
void GlassView::on_show()
|
||||
{
|
||||
display.scroll_set_area( 109, 319); //Restart scroll on the correct coordinates
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void GlassView::on_range_changed()
|
||||
{
|
||||
f_min = field_frequency_min.value();
|
||||
f_max = field_frequency_max.value();
|
||||
search_span = f_max - f_min;
|
||||
|
||||
field_marker.set_range(f_min, f_max); //Move the marker between range
|
||||
field_marker.set_value(f_min + (search_span / 2)); //Put MARKER AT MIDDLE RANGE
|
||||
text_range.set(to_string_dec_uint(search_span));
|
||||
|
||||
f_min = (f_min)*MHZ_DIV; //Transpose into full frequency realm
|
||||
f_max = (f_max)*MHZ_DIV;
|
||||
search_span = search_span * MHZ_DIV;
|
||||
|
||||
marker_pixel_step = search_span / 240; //Each pixel value in Hz
|
||||
text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision
|
||||
|
||||
int32_t marker_step = marker_pixel_step / MHZ_DIV;
|
||||
if (!marker_step)
|
||||
field_marker.set_step(1); //in case selected range is less than 240 (pixels)
|
||||
else
|
||||
field_marker.set_step(marker_step); //step needs to be a pixel wide.
|
||||
|
||||
f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2); //Initial center frequency for sweep
|
||||
f_center_ini += LOOKING_GLASS_SLICE_WIDTH; //euquiq: Why do I need to move the center ???!!! (shift needed for marker accuracy)
|
||||
|
||||
PlotMarker(field_marker.value()); //Refresh marker on screen
|
||||
|
||||
f_center = f_center_ini; //Reset sweep into first slice
|
||||
pixel_index = 0; //reset pixel counter
|
||||
max_power = 0;
|
||||
bins_Hz_size = 0; //reset amount of Hz filled up by pixels
|
||||
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value());
|
||||
receiver_model.set_tuning_frequency(f_center_ini); //tune rx for this slice
|
||||
}
|
||||
|
||||
void GlassView::PlotMarker(rf::Frequency fpos)
|
||||
{
|
||||
int64_t pos = fpos * MHZ_DIV;
|
||||
pos -= f_min;
|
||||
pos = pos / marker_pixel_step; //Real pixel
|
||||
|
||||
portapack::display.fill_rectangle({0, 100, 240, 8}, Color::black()); //Clear old marker and whole marker rectangle btw
|
||||
portapack::display.fill_rectangle({(int16_t)pos - 2, 100, 5, 3}, Color::red()); //Red marker middle
|
||||
portapack::display.fill_rectangle({(int16_t)pos - 1, 103, 3, 3}, Color::red()); //Red marker middle
|
||||
portapack::display.fill_rectangle({(int16_t)pos, 106, 1, 2}, Color::red()); //Red marker middle
|
||||
}
|
||||
|
||||
GlassView::GlassView(
|
||||
NavigationView &nav) : nav_(nav)
|
||||
{
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&text_range,
|
||||
&filter_config,
|
||||
&field_rf_amp,
|
||||
&range_presets,
|
||||
&field_marker,
|
||||
&text_marker_pm,
|
||||
&field_trigger
|
||||
});
|
||||
|
||||
load_Presets(); //Load available presets from TXT files (or default)
|
||||
|
||||
field_frequency_min.set_value(presets_db[0].min); //Defaults to first preset
|
||||
field_frequency_min.on_change = [this](int32_t v) {
|
||||
if (v >= field_frequency_max.value())
|
||||
field_frequency_max.set_value(v + 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_frequency_max.set_value(presets_db[0].max); //Defaults to first preset
|
||||
field_frequency_max.on_change = [this](int32_t v) {
|
||||
if (v <= field_frequency_min.value())
|
||||
field_frequency_min.set_value(v - 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v) {
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db) {
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
filter_config.set_selected_index(0);
|
||||
filter_config.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
min_color_power = v;
|
||||
};
|
||||
|
||||
range_presets.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
field_frequency_min.set_value(presets_db[v].min,false);
|
||||
field_frequency_max.set_value(presets_db[v].max,false);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_marker.on_change = [this](int32_t v) {
|
||||
PlotMarker(v); //Refresh marker on screen
|
||||
};
|
||||
|
||||
field_marker.on_select = [this](NumberField&) {
|
||||
f_center = field_marker.value();
|
||||
f_center = f_center * MHZ_DIV;
|
||||
receiver_model.set_tuning_frequency(f_center); //Center tune rx in marker freq.
|
||||
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
|
||||
nav_.pop();
|
||||
nav_.push<AnalogAudioView>(); //Jump into audio view
|
||||
};
|
||||
|
||||
field_trigger.set_value(32); //Defaults to 32, as normal triggering resolution
|
||||
field_trigger.on_change = [this](int32_t v) {
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v);
|
||||
};
|
||||
|
||||
display.scroll_set_area( 109, 319);
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); //trigger:
|
||||
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute ,
|
||||
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
|
||||
//at which time it pushes the buffer up with channel_spectrum.feed
|
||||
|
||||
on_range_changed();
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); //20mhz
|
||||
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
|
||||
receiver_model.set_squelch_level(0);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
void GlassView::load_Presets() {
|
||||
File presets_file; //LOAD /WHIPCALC/ANTENNAS.TXT from microSD
|
||||
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear(); //Start with fresh db
|
||||
if (result.is_valid()) {
|
||||
presets_Default(); //There is no txt, store a default range
|
||||
} else {
|
||||
|
||||
std::string line; //There is a txt file
|
||||
char one_char[1]; //Read it char by char
|
||||
for (size_t pointer=0; pointer < presets_file.size();pointer++) {
|
||||
presets_file.seek(pointer);
|
||||
presets_file.read(one_char, 1);
|
||||
if ((int)one_char[0] > 31) { //ascii space upwards
|
||||
line += one_char[0]; //Add it to the textline
|
||||
}
|
||||
else if (one_char[0] == '\n') { //New Line
|
||||
txtline_process(line); //make sense of this textline
|
||||
line.clear(); //Ready for next textline
|
||||
}
|
||||
}
|
||||
if (line.length() > 0) txtline_process(line); //Last line had no newline at end ?
|
||||
if (!presets_db.size()) presets_Default(); //no antenna on txt, use default
|
||||
void GlassView::on_hide()
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
populate_Presets();
|
||||
}
|
||||
void GlassView::on_show()
|
||||
{
|
||||
display.scroll_set_area(109, 319); // Restart scroll on the correct coordinates
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void GlassView::txtline_process(std::string& line)
|
||||
{
|
||||
if (line.find("#") != std::string::npos) return; //Line is just a comment
|
||||
void GlassView::on_range_changed()
|
||||
{
|
||||
f_min = field_frequency_min.value();
|
||||
f_max = field_frequency_max.value();
|
||||
search_span = f_max - f_min;
|
||||
|
||||
size_t comma = line.find(","); //Get first comma position
|
||||
if (comma == std::string::npos) return; //No comma at all
|
||||
field_marker.set_range(f_min, f_max); // Move the marker between range
|
||||
field_marker.set_value(f_min + (search_span / 2)); // Put MARKER AT MIDDLE RANGE
|
||||
text_range.set(to_string_dec_uint(search_span));
|
||||
|
||||
size_t previous = 0;
|
||||
preset_entry new_preset;
|
||||
f_min = (f_min)*MHZ_DIV; // Transpose into full frequency realm
|
||||
f_max = (f_max)*MHZ_DIV;
|
||||
search_span = search_span * MHZ_DIV;
|
||||
|
||||
new_preset.min = std::stoi(line.substr(0,comma));
|
||||
if (!new_preset.min) return; //No frequency!
|
||||
|
||||
previous = comma + 1;
|
||||
comma = line.find(",",previous); //Search for next delimiter
|
||||
if (comma == std::string::npos) return; //No comma at all
|
||||
marker_pixel_step = search_span / 240; // Each pixel value in Hz
|
||||
text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision
|
||||
|
||||
new_preset.max = std::stoi(line.substr(previous,comma - previous));
|
||||
if (!new_preset.max) return; //No frequency!
|
||||
int32_t marker_step = marker_pixel_step / MHZ_DIV;
|
||||
if (!marker_step)
|
||||
field_marker.set_step(1); // in case selected range is less than 240 (pixels)
|
||||
else
|
||||
field_marker.set_step(marker_step); // step needs to be a pixel wide.
|
||||
|
||||
new_preset.label = line.substr(comma + 1);
|
||||
if (new_preset.label.size() == 0) return; //No label ?
|
||||
|
||||
presets_db.push_back(new_preset); //Add this preset.
|
||||
}
|
||||
f_center_ini = f_min + (LOOKING_GLASS_SLICE_WIDTH / 2); // Initial center frequency for sweep
|
||||
|
||||
void GlassView::populate_Presets()
|
||||
{
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t entries;
|
||||
PlotMarker(field_marker.value()); // Refresh marker on screen
|
||||
|
||||
for (preset_entry preset : presets_db)
|
||||
{ //go thru all available presets
|
||||
entries.emplace_back(preset.label,entries.size());
|
||||
f_center = f_center_ini; // Reset sweep into first slice
|
||||
pixel_index = 0; // reset pixel counter
|
||||
max_power = 0;
|
||||
bins_Hz_size = 0; // reset amount of Hz filled up by pixels
|
||||
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value());
|
||||
receiver_model.set_tuning_frequency(f_center_ini); // tune rx for this slice
|
||||
}
|
||||
|
||||
void GlassView::PlotMarker(rf::Frequency pos)
|
||||
{
|
||||
pos = pos * MHZ_DIV;
|
||||
pos -= f_min;
|
||||
pos = pos / marker_pixel_step; // Real pixel
|
||||
|
||||
portapack::display.fill_rectangle({0, 100, 240, 8}, Color::black()); // Clear old marker and whole marker rectangle btw
|
||||
portapack::display.fill_rectangle({(int)pos - 2, 100, 5, 3}, Color::red()); // Red marker top
|
||||
portapack::display.fill_rectangle({(int)pos - 1, 103, 3, 3}, Color::red()); // Red marker middle
|
||||
portapack::display.fill_rectangle({(int)pos, 106, 1, 2}, Color::red()); // Red marker bottom
|
||||
}
|
||||
|
||||
GlassView::GlassView(
|
||||
NavigationView &nav) : nav_(nav)
|
||||
{
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&text_range,
|
||||
&filter_config,
|
||||
&field_rf_amp,
|
||||
&range_presets,
|
||||
&field_marker,
|
||||
&text_marker_pm,
|
||||
&field_trigger});
|
||||
|
||||
load_Presets(); // Load available presets from TXT files (or default)
|
||||
|
||||
field_frequency_min.set_value(presets_db[0].min); // Defaults to first preset
|
||||
field_frequency_min.on_change = [this](int32_t v)
|
||||
{
|
||||
if (v >= field_frequency_max.value())
|
||||
field_frequency_max.set_value(v + 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_frequency_max.set_value(presets_db[0].max); // Defaults to first preset
|
||||
field_frequency_max.on_change = [this](int32_t v)
|
||||
{
|
||||
if (v <= field_frequency_min.value())
|
||||
field_frequency_min.set_value(v - 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v)
|
||||
{
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db)
|
||||
{
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
filter_config.set_selected_index(0);
|
||||
filter_config.on_change = [this](size_t n, OptionsField::value_t v)
|
||||
{
|
||||
(void)n;
|
||||
min_color_power = v;
|
||||
};
|
||||
|
||||
range_presets.on_change = [this](size_t n, OptionsField::value_t v)
|
||||
{
|
||||
(void)n;
|
||||
field_frequency_min.set_value(presets_db[v].min, false);
|
||||
field_frequency_max.set_value(presets_db[v].max, false);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_marker.on_change = [this](int32_t v)
|
||||
{
|
||||
PlotMarker(v); // Refresh marker on screen
|
||||
};
|
||||
|
||||
field_marker.on_select = [this](NumberField &)
|
||||
{
|
||||
f_center = field_marker.value();
|
||||
f_center = f_center * MHZ_DIV;
|
||||
receiver_model.set_tuning_frequency(f_center); // Center tune rx in marker freq.
|
||||
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
|
||||
nav_.pop();
|
||||
nav_.push<AnalogAudioView>(); // Jump into audio view
|
||||
};
|
||||
|
||||
field_trigger.set_value(32); // Defaults to 32, as normal triggering resolution
|
||||
field_trigger.on_change = [this](int32_t v)
|
||||
{
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, v);
|
||||
};
|
||||
|
||||
display.scroll_set_area(109, 319);
|
||||
baseband::set_spectrum(LOOKING_GLASS_SLICE_WIDTH, field_trigger.value()); // trigger:
|
||||
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute ,
|
||||
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
|
||||
// at which time it pushes the buffer up with channel_spectrum.feed
|
||||
|
||||
on_range_changed();
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_sampling_rate(LOOKING_GLASS_SLICE_WIDTH); // 20mhz
|
||||
receiver_model.set_baseband_bandwidth(LOOKING_GLASS_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
|
||||
receiver_model.set_squelch_level(0);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
void GlassView::load_Presets()
|
||||
{
|
||||
File presets_file; // LOAD /WHIPCALC/ANTENNAS.TXT from microSD
|
||||
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear(); // Start with fresh db
|
||||
if (result.is_valid())
|
||||
{
|
||||
presets_Default(); // There is no txt, store a default range
|
||||
}
|
||||
range_presets.set_options(entries);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string line; // There is a txt file
|
||||
char one_char[1]; // Read it char by char
|
||||
for (size_t pointer = 0; pointer < presets_file.size(); pointer++)
|
||||
{
|
||||
presets_file.seek(pointer);
|
||||
presets_file.read(one_char, 1);
|
||||
if ((int)one_char[0] > 31)
|
||||
{ // ascii space upwards
|
||||
line += one_char[0]; // Add it to the textline
|
||||
}
|
||||
else if (one_char[0] == '\n')
|
||||
{ // New Line
|
||||
txtline_process(line); // make sense of this textline
|
||||
line.clear(); // Ready for next textline
|
||||
}
|
||||
}
|
||||
if (line.length() > 0)
|
||||
txtline_process(line); // Last line had no newline at end ?
|
||||
if (!presets_db.size())
|
||||
presets_Default(); // no antenna on txt, use default
|
||||
}
|
||||
populate_Presets();
|
||||
}
|
||||
|
||||
void GlassView::presets_Default()
|
||||
{
|
||||
void GlassView::txtline_process(std::string &line)
|
||||
{
|
||||
if (line.find("#") != std::string::npos)
|
||||
return; // Line is just a comment
|
||||
|
||||
size_t comma = line.find(","); // Get first comma position
|
||||
if (comma == std::string::npos)
|
||||
return; // No comma at all
|
||||
|
||||
size_t previous = 0;
|
||||
preset_entry new_preset;
|
||||
|
||||
new_preset.min = std::stoi(line.substr(0, comma));
|
||||
if (!new_preset.min)
|
||||
return; // No frequency!
|
||||
|
||||
previous = comma + 1;
|
||||
comma = line.find(",", previous); // Search for next delimiter
|
||||
if (comma == std::string::npos)
|
||||
return; // No comma at all
|
||||
|
||||
new_preset.max = std::stoi(line.substr(previous, comma - previous));
|
||||
if (!new_preset.max)
|
||||
return; // No frequency!
|
||||
|
||||
new_preset.label = line.substr(comma + 1);
|
||||
if (new_preset.label.size() == 0)
|
||||
return; // No label ?
|
||||
|
||||
presets_db.push_back(new_preset); // Add this preset.
|
||||
}
|
||||
|
||||
void GlassView::populate_Presets()
|
||||
{
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t entries;
|
||||
|
||||
for (preset_entry preset : presets_db)
|
||||
{ // go thru all available presets
|
||||
entries.emplace_back(preset.label, entries.size());
|
||||
}
|
||||
range_presets.set_options(entries);
|
||||
}
|
||||
|
||||
void GlassView::presets_Default()
|
||||
{
|
||||
presets_db.clear();
|
||||
presets_db.push_back({2320, 2560, "DEFAULT WIFI 2.4GHz"});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ using wolfson::wm8731::WM8731;
|
||||
|
||||
#include "tonesets.hpp"
|
||||
#include "portapack_hal.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
@@ -73,6 +74,7 @@ void MicTXView::configure_baseband() {
|
||||
sampling_rate / 20, // Update vu-meter at 20Hz
|
||||
transmitting ? transmitter_model.channel_bandwidth() : 0,
|
||||
mic_gain,
|
||||
shift_bits_s16, // to be used in dsp_modulate
|
||||
TONES_F2D(tone_key_frequency(tone_key_index), sampling_rate),
|
||||
enable_am,
|
||||
enable_dsb,
|
||||
@@ -157,19 +159,24 @@ void MicTXView::rxaudio(bool is_on) {
|
||||
audio::input::stop();
|
||||
baseband::shutdown();
|
||||
|
||||
if (enable_am || enable_usb || enable_lsb || enable_dsb) {
|
||||
if (enable_am || enable_usb || enable_lsb || enable_dsb) { // "NFM/FM",0 ," WFM ",1 , " AM ",2, " USB ", 3, " LSB ",4, " DSB-SC", 5
|
||||
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
|
||||
if (options_mode.selected_index() < 4)
|
||||
receiver_model.set_am_configuration(options_mode.selected_index() - 1);
|
||||
else
|
||||
receiver_model.set_am_configuration(0);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); // that AM demodulation engine is common to all Amplitude mod : AM/USB/LSB/DSB (2,3,4,5)
|
||||
if (options_mode.selected_index() < 5) // We will be called here with 2,3,4,5 . We treat here demod. filter 2,3,4; (excluding DSB-C case (5) it is treated more down).
|
||||
receiver_model.set_am_configuration(options_mode.selected_index() - 1); // selecting proper filter(2,3,4). 2-1=1=>6k-AM(1) , 3-1=2=>+3k-USB(2), 4-1=3=>-3K-LSB(3),
|
||||
}
|
||||
else {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
||||
else { // We are in NFM/FM or WFM (NFM BW:8k5 or 11k / FM BW 16k / WFM BW:200k)
|
||||
|
||||
}
|
||||
if (enable_wfm) { // WFM , BW 200Khz aprox ,
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
||||
receiver_model.set_wfm_configuration(0); // there are only 1 x config filters 200k WFM . (not like 8k5/11k/16k)
|
||||
} else { // NFM BW:8k5 or 11k / FM BW 16k
|
||||
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); //
|
||||
// receiver_model.set_nbfm_configuration(n); is called above , depending 8k5, 11k, 16k
|
||||
}
|
||||
}
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
// receiver_model.set_tuning_frequency(field_frequency.value()); //probably this too can be commented out.
|
||||
@@ -178,6 +185,7 @@ void MicTXView::rxaudio(bool is_on) {
|
||||
receiver_model.set_vga(rx_vga);
|
||||
receiver_model.set_rf_amp(rx_amp);
|
||||
receiver_model.enable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to have a good RX without any ghost inside Mic App
|
||||
audio::output::start();
|
||||
} else { //These incredibly convoluted steps are required for the vumeter to reappear when stopping RX.
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); //This fixes something with AM RX...
|
||||
@@ -186,7 +194,7 @@ void MicTXView::rxaudio(bool is_on) {
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_mic_tx);
|
||||
audio::output::stop();
|
||||
audio::input::start(); // set up audio input = mic config of any audio coded AK4951/WM8731, (in WM8731 parameter will be ignored)
|
||||
audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // When detected AK4951 => set up ALC mode; when detected WM8731 => set up mic_boost ON/OFF.
|
||||
portapack::pin_i2s0_rx_sda.mode(3);
|
||||
configure_baseband();
|
||||
}
|
||||
@@ -211,12 +219,12 @@ MicTXView::MicTXView(
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_mic_tx);
|
||||
|
||||
|
||||
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() ) {
|
||||
if (audio::debug::codec_name() =="WM8731" ) {
|
||||
add_children({
|
||||
&labels_WM8731, // we have audio codec WM8731, same MIC menu as original.
|
||||
&vumeter,
|
||||
&options_gain, // MIC GAIN float factor on the GUI.
|
||||
&options_wm8731_boost_mode,
|
||||
// &check_va,
|
||||
&field_va,
|
||||
&field_va_level,
|
||||
@@ -281,13 +289,43 @@ MicTXView::MicTXView(
|
||||
mic_gain = v / 10.0;
|
||||
configure_baseband();
|
||||
};
|
||||
options_gain.set_selected_index(1); // x1.0
|
||||
options_gain.set_selected_index(1); // x1.0 preselected default.
|
||||
|
||||
if (audio::debug::codec_name() =="WM8731") {
|
||||
options_wm8731_boost_mode.on_change = [this](size_t, int8_t v) {
|
||||
|
||||
switch(v) {
|
||||
case 0: // +12 dB’s respect reference level orig fw 1.5.x fw FM : when +20dB's boost ON) and shift bits (>>8),
|
||||
shift_bits_s16 = 6; // now mic-boost on (+20dBs) and shift bits (>>6), +20+12=32 dB’s (orig fw +20 dBs+ 0dBs)=> +12dB's respect ref.
|
||||
break;
|
||||
case 1: // +06 dB’s reference level , (when +20dB's boost ON)
|
||||
shift_bits_s16 = 7; // now mic-boost on (+20dBs) and shift bits (>>7), +20+06=26 dB’s (orig fw +20 dBs+ 0dBs) => +06dB's respect ref.
|
||||
break;
|
||||
case 2:
|
||||
shift_bits_s16 = 4; // +04 dB’s respect ref level , (when +20dB's boost OFF)
|
||||
break; // now mic-boost off (+00dBs) shift bits (4) (+0+24dB's)=24 dBs => +04dB's respect ref.
|
||||
case 3:
|
||||
shift_bits_s16 = 5; // -02 dB’s respect ref level , (when +20dB's boost OFF)
|
||||
break; // now mic-boost off (+00dBs) shift bits (5) (+0+18dB's)=18 dBs => -02dB's respect ref.
|
||||
case 4:
|
||||
shift_bits_s16 = 6; // -08 dB’s respect ref level , (when +20dB's boost OFF)
|
||||
break; // now mic-boost off (+00dBs) shift bits (6) (+0+12dB's)=12 dBs => -08dB's respect ref.
|
||||
}
|
||||
ak4951_alc_and_wm8731_boost_GUI = v; // 0,..4 WM8731_boost dB's options, (combination boost on/off , and effective gain in captured data >>x)
|
||||
audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // Detected (WM8731) , set up the proper wm_boost on/off , 0..4 (0,1) boost_on , (2,3,4) boost_0ff
|
||||
configure_baseband(); // to update in real timme,sending msg , var-parameters >>shift_bits FM msg ,to audio_tx from M0 to M4 Proc -
|
||||
};
|
||||
options_wm8731_boost_mode.set_selected_index(3); // preset GUI index 3 as default WM -> -02 dB's .
|
||||
} else {
|
||||
shift_bits_s16 = 8; // Initialized default fixed >>8_FM for FM tx mod , shift audio data for AK4951 ,using top 8 bits s16 data (>>8)
|
||||
options_ak4951_alc_mode.on_change = [this](size_t, int8_t v) {
|
||||
ak4951_alc_and_wm8731_boost_GUI = v; // 0,..11, AK4951 Mic -Automatic volume Level Control options,
|
||||
audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // Detected (AK4951) ==> Set up proper ALC mode from 0..11 options
|
||||
configure_baseband(); // sending fixed >>8_FM , var-parameters msg , to audiotx from this M0 to M4 process.
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
// options_ak4951_alc_mode.set_selected_index(0);
|
||||
|
||||
tx_frequency = transmitter_model.tuning_frequency();
|
||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
@@ -313,7 +351,8 @@ MicTXView::MicTXView(
|
||||
field_bw.on_change = [this](uint32_t v) {
|
||||
transmitter_model.set_channel_bandwidth(v * 1000);
|
||||
};
|
||||
field_bw.set_value(10);
|
||||
field_bw.set_value(10); // pre-default first time, TX deviation FM for NFM / FM
|
||||
|
||||
|
||||
tx_gain = transmitter_model.tx_gain();
|
||||
field_rfgain.on_change = [this](int32_t v) {
|
||||
@@ -328,50 +367,102 @@ MicTXView::MicTXView(
|
||||
};
|
||||
field_rfamp.set_value(rf_amp ? 14 : 0);
|
||||
|
||||
options_mode.on_change = [this](size_t, int32_t v) {
|
||||
enable_am = false;
|
||||
options_mode.on_change = [this](size_t, int32_t v) { //{ "NFM/FM", 0 }, { " WFM ", 1 },{ "AM", 2 },{ "USB", 3 },{ "LSB", 4 },{ "DSB", 5 }
|
||||
enable_am = false;
|
||||
enable_usb = false;
|
||||
enable_lsb = false;
|
||||
enable_dsb = false;
|
||||
enable_wfm = false;
|
||||
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t rxbw; // Aux structure to change dynamically field_rxbw contents,
|
||||
|
||||
switch(v) {
|
||||
case 0:
|
||||
enable_am = false;
|
||||
case 0: //{ "FM", 0 }
|
||||
enable_am = false;
|
||||
enable_usb = false;
|
||||
enable_lsb = false;
|
||||
enable_dsb = false;
|
||||
field_bw.set_value(transmitter_model.channel_bandwidth() / 1000);
|
||||
field_bw.set_value(10); // pre-default deviation FM for WFM
|
||||
// field_bw.set_value(transmitter_model.channel_bandwidth() / 1000);
|
||||
//if (rx_enabled)
|
||||
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.
|
||||
|
||||
rxbw.emplace_back(" NFM1:8k5 ", 0); // restore the original dynamic field_rxbw value.
|
||||
rxbw.emplace_back(" NFM2:11k ", 1);
|
||||
rxbw.emplace_back(" FM :16k ", 2);
|
||||
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
|
||||
|
||||
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)
|
||||
field_bw.hidden(0); // we are in FM mode, we need to allow FM deviation parameter , in non FM mode.
|
||||
break;
|
||||
case 1:
|
||||
case 1: //{ "WFM", 1 }
|
||||
enable_am = false;
|
||||
enable_usb = false;
|
||||
enable_lsb = false;
|
||||
enable_dsb = false;
|
||||
enable_wfm = true;
|
||||
field_bw.set_value(75); // pre-default deviation FM for WFM
|
||||
// field_bw.set_value(transmitter_model.channel_bandwidth() / 1000);
|
||||
//if (rx_enabled)
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
options_tone_key.hidden(0); // we are in WFM mode , we should have active the Key-tones & CTCSS option.
|
||||
|
||||
rxbw.emplace_back(" 200k-WFM ", 0); // locked a fixed option , to display it .
|
||||
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
|
||||
|
||||
field_rxbw.hidden(0); // we are in WFM mode, we need to show to the user the selected BW WFM filer .
|
||||
field_bw.hidden(0); // we are in WFM mode, we need to allow WFM deviation parameter , in non FM mode.
|
||||
break;
|
||||
case 2: //{ "AM", 2 }
|
||||
enable_am = true;
|
||||
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.
|
||||
|
||||
rxbw.emplace_back(" DSB1-9k ", 0); // we offer in AM DSB two audio BW 9k / 6k .
|
||||
rxbw.emplace_back(" DSB2-6k ", 1);
|
||||
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
|
||||
|
||||
field_rxbw.hidden(0); // we show fixed RX AM BW 6Khz
|
||||
field_bw.hidden(1); // we hide the FM TX deviation parameter , in non FM mode.
|
||||
check_rogerbeep.hidden(0); // make visible again the "rogerbeep" selection.
|
||||
break;
|
||||
case 2:
|
||||
case 3: //{ "USB", 3 }
|
||||
enable_usb = true;
|
||||
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.
|
||||
|
||||
rxbw.emplace_back(" USB+3k ", 0); // locked a fixed option , to display it .
|
||||
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
|
||||
|
||||
set_dirty(); // Refresh display
|
||||
break;
|
||||
case 3:
|
||||
case 4: //{ "LSB", 4 }
|
||||
enable_lsb = true;
|
||||
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.
|
||||
|
||||
rxbw.emplace_back(" LSB-3k ", 0); // locked a fixed option , to display it .
|
||||
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
|
||||
|
||||
set_dirty(); // Refresh display
|
||||
break;
|
||||
case 4:
|
||||
case 5: //{ "DSB-SC", 5 }
|
||||
enable_dsb = true;
|
||||
rxaudio(rx_enabled); //Update now if we have RX audio on
|
||||
check_rogerbeep.hidden(0); // make visible again the "rogerbeep" selection.
|
||||
|
||||
rxbw.emplace_back("SSB1:USB+3k", 0); // added dynamically two options (index 0,1) to that DSB-C case to the field_rxbw value.
|
||||
rxbw.emplace_back("SSB2:LSB-3k", 1);
|
||||
|
||||
field_rxbw.set_options(rxbw); // store that aux GUI option to the field_rxbw.
|
||||
|
||||
break;
|
||||
}
|
||||
//configure_baseband();
|
||||
@@ -448,19 +539,20 @@ MicTXView::MicTXView(
|
||||
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
|
||||
|
||||
field_rxbw.on_change = [this](size_t, int32_t v) {
|
||||
switch(v) {
|
||||
case 0:
|
||||
receiver_model.set_nbfm_configuration(0);
|
||||
break;
|
||||
case 1:
|
||||
receiver_model.set_nbfm_configuration(1);
|
||||
break;
|
||||
case 2:
|
||||
receiver_model.set_nbfm_configuration(2);
|
||||
break;
|
||||
if (!(enable_am || enable_usb || enable_lsb || enable_dsb || enable_wfm )) {
|
||||
// In Previous fw versions, that nbfm_configuration(n) was done in any mode (FM/AM/SSB/DSB)...strictly speaking only need it in (NFM/FM)
|
||||
receiver_model.set_nbfm_configuration(v ); // we are in NFM/FM case, we need to select proper NFM/FM RX channel filter , NFM BW 8K5(0), NFM BW 11K(1) , FM BW 16K (2)
|
||||
}
|
||||
else { // we are not in NFM/FM mode .(we could be in any of the rest : AM /USB/LSB/DSB-SC)
|
||||
if (enable_am) { // we are in AM TX mode , we will allow both independent RX audio BW : AM 9K (9K00AE3 / AM 6K (6K00AE3). (In AM option v can be 0 (9k) , 1 (6k)
|
||||
receiver_model.set_am_configuration(v ); // we are in AM TX mode , we need to select proper AM full path config AM-9K filter. 0+0 =>AM-9K(0), 0+1=1 =>AM-6K(1),
|
||||
}
|
||||
|
||||
if (enable_dsb) { // we are in DSB-SC in TX mode , we will allow both independent RX SSB demodulation (USB / LSB side band). in that submenu, v is 0 (SSB1 USB) or 1 (SSB2 LSB)
|
||||
receiver_model.set_am_configuration(v +2 ); // we are in DSB-SC TX mode , we need to select proper SSB filter. 0+2 =>usb(2), 1+2=3 =>lsb(3),
|
||||
}
|
||||
}
|
||||
};
|
||||
field_rxbw.set_selected_index(2);
|
||||
|
||||
field_squelch.on_change = [this](int32_t v) {
|
||||
receiver_model.set_squelch_level(100 - v);
|
||||
@@ -537,9 +629,9 @@ MicTXView::MicTXView(
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
|
||||
set_tx(false);
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::input::start(); // originally , audio::input::start(); (we added parameter)
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // When detected AK4951 => set up ALC mode; when detected WM8731 => set up mic_boost ON/OFF.
|
||||
}
|
||||
|
||||
MicTXView::~MicTXView() {
|
||||
@@ -548,7 +640,8 @@ MicTXView::~MicTXView() {
|
||||
transmitter_model.disable();
|
||||
if (rx_enabled) //Also turn off audio rx if enabled
|
||||
rxaudio(false);
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ private:
|
||||
bool rx_enabled { false };
|
||||
uint32_t tone_key_index { };
|
||||
float mic_gain { 1.0 };
|
||||
uint8_t ak4951_alc_GUI_selected { 0 };
|
||||
uint8_t ak4951_alc_and_wm8731_boost_GUI { 0 };
|
||||
uint32_t audio_level { 0 };
|
||||
uint32_t va_level { };
|
||||
uint32_t attack_ms { };
|
||||
@@ -99,18 +99,21 @@ private:
|
||||
rf::Frequency rx_frequency { 0 };
|
||||
int32_t focused_ui { 2 };
|
||||
bool button_touch { false };
|
||||
uint8_t shift_bits_s16 {4} ; // shift bits factor to the captured ADC S16 audio sample.
|
||||
|
||||
//AM TX Stuff
|
||||
bool enable_am { false };
|
||||
bool enable_dsb { false };
|
||||
bool enable_usb { false };
|
||||
bool enable_lsb { false };
|
||||
bool enable_wfm { false }; // added to distinguish in the FM mode , RX BW : NFM (8K5, 11K), FM (16K), WFM(200K)
|
||||
|
||||
|
||||
Labels labels_WM8731 {
|
||||
{ { 3 * 8, 1 * 8 }, "MIC-GAIN:", Color::light_grey() },
|
||||
{ { 17 * 8, 1 * 8 }, "Boost", Color::light_grey() },
|
||||
{ { 3 * 8, 3 * 8 }, "F:", Color::light_grey() },
|
||||
{ { 15 * 8, 3 * 8 }, "BW: FM kHz", Color::light_grey() },
|
||||
{ { 15 * 8, 3 * 8 }, "FM TXBW: kHz", Color::light_grey() }, // to be more symetric and consistent to the below FM RXBW
|
||||
{ { 3 * 8, 5 * 8 }, "GAIN:", Color::light_grey() },
|
||||
{ {11 * 8, 5 * 8 }, "Amp:", Color::light_grey() },
|
||||
{ { 18 * 8, (5 * 8) }, "Mode:", Color::light_grey() },
|
||||
@@ -120,7 +123,7 @@ private:
|
||||
{ {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() },
|
||||
{ {14 * 8, 23 * 8 }, "RXBW:", Color::light_grey() }, //we remove the label "FM" because we will display all MOD types RX_BW.
|
||||
{ {17 * 8, 25 * 8 }, "SQ:", Color::light_grey() },
|
||||
{ { 5 * 8, 25 * 8 }, "F:", Color::light_grey() },
|
||||
{ { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()},
|
||||
@@ -131,7 +134,7 @@ private:
|
||||
{ { 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() },
|
||||
{ { 15 * 8, 3 * 8 }, "FM TXBW: 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() },
|
||||
@@ -140,8 +143,8 @@ 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() },
|
||||
{ { 7 * 8, 23 * 8 }, "VOL:", Color::light_grey() },
|
||||
{ {15 * 8, 23 * 8 }, "FM RXBW:", Color::light_grey() },
|
||||
{ { (6 * 8)+4, 23 * 8 }, "VOL:", Color::light_grey() },
|
||||
{ {14 * 8, 23 * 8 }, "RXBW:", Color::light_grey() }, //we remove the label "FM" because we will display all MOD types RX_BW.
|
||||
{ {17 * 8, 25 * 8 }, "SQ:", Color::light_grey() },
|
||||
{ { 5 * 8, 25 * 8 }, "F:", Color::light_grey() },
|
||||
{ { 5 * 8, 27 * 8 }, "LNA:", Color::light_grey()},
|
||||
@@ -171,7 +174,7 @@ private:
|
||||
{ 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)
|
||||
{ " OFF-12kHz", 0 }, // Nothing changed from ORIGINAL,keeping ALL programmable AK4951 Digital Block->OFF, sampling 24Khz)
|
||||
{ "+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)
|
||||
@@ -186,11 +189,23 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
OptionsField options_wm8731_boost_mode {
|
||||
{ 22 * 8, 1 * 8 }, // Coordinates are: int:x (px), int:y (px)
|
||||
5,
|
||||
{
|
||||
{ "ON +12dB", 0 }, // WM8731 Mic Boost ON ,original+12dBs condition, easy to saturate ADC sat in high voice ,relative G = +12 dB's respect ref level
|
||||
{ "ON +06dB", 1 }, // WM8731 Mic Boost ON ,original+6 dBs condition, easy to saturate ADC sat in high voice ,relative G = +06 dB's respect ref level
|
||||
{ "OFF+04dB", 2 }, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = +04 dB's (respect ref level) , always effective sampling 24khz
|
||||
{ "OFF-02dB", 3 }, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = -02 dB's (respect ref level)
|
||||
{ "OFF-08dB", 4 }, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = -12 dB's (respect ref level)
|
||||
}
|
||||
};
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 5 * 8, 3 * 8 },
|
||||
};
|
||||
NumberField field_bw {
|
||||
{ 18 * 8, 3 * 8 },
|
||||
{ 23 * 8, 3 * 8 },
|
||||
3,
|
||||
{ 0, 150 },
|
||||
1,
|
||||
@@ -214,13 +229,14 @@ private:
|
||||
|
||||
OptionsField options_mode {
|
||||
{ 24 * 8, 5 * 8 },
|
||||
3,
|
||||
4,
|
||||
{
|
||||
{ "FM", 0 },
|
||||
{ "AM", 1 },
|
||||
{ "USB", 2 },
|
||||
{ "LSB", 3 },
|
||||
{ "DSB", 4 }
|
||||
{ "NFM/FM", 0 },
|
||||
{ " WFM ", 1 },
|
||||
{ " AM ", 2 }, // in fact that TX mode = AM -DSB with carrier .
|
||||
{ " USB ", 3 },
|
||||
{ " LSB ", 4 },
|
||||
{ "DSB-SC", 5 } // We are TX Double Side AM Band with suppressed carrier, and allowing in RX both indep SSB lateral band (USB/LSB).
|
||||
}
|
||||
};
|
||||
/*
|
||||
@@ -293,13 +309,13 @@ private:
|
||||
};
|
||||
|
||||
OptionsField field_rxbw {
|
||||
{ 23* 8, 23 * 8},
|
||||
{ 19* 8, 23 * 8},
|
||||
3,
|
||||
{
|
||||
{"8k5", 0},
|
||||
{"11k", 1},
|
||||
{"16k", 2}
|
||||
}
|
||||
{" NFM1:8k5 ", 0},
|
||||
{" NFM2:11k ", 1},
|
||||
{" FM :16k ", 2},
|
||||
}
|
||||
};
|
||||
|
||||
NumberField field_squelch {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
@@ -56,13 +57,13 @@ static msg_t ookthread_fn(void * arg) {
|
||||
v = (symbol < 2) ? 1 : 0; // TX on for dot or dash, off for pause
|
||||
delay = morse_symbols[symbol];
|
||||
|
||||
gpio_tx.write(v);
|
||||
gpio_og_tx.write(v);
|
||||
arg_c->on_tx_progress(i, false);
|
||||
|
||||
chThdSleepMilliseconds(delay * arg_c->time_unit_ms);
|
||||
}
|
||||
|
||||
gpio_tx.write(0); // Ensure TX is off
|
||||
gpio_og_tx.write(0); // Ensure TX is off
|
||||
arg_c->on_tx_progress(0, true);
|
||||
chThdExit(0);
|
||||
|
||||
@@ -102,7 +103,8 @@ MorseView::~MorseView() {
|
||||
settings.save("tx_morse", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void MorseView::paint(Painter&) {
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyleft (ↄ) 2022 NotPike
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui_playlist.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "ui_fileman.hpp"
|
||||
#include "io_file.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void PlaylistView::set_ready() {
|
||||
ready_signal = true;
|
||||
}
|
||||
|
||||
|
||||
void PlaylistView::load_file(std::filesystem::path playlist_path) {
|
||||
File playlist_file;
|
||||
|
||||
auto error = playlist_file.open(playlist_path.string());
|
||||
if (!error.is_valid()) {
|
||||
std::string line;
|
||||
char one_char[1];
|
||||
for (size_t pointer =0 ; pointer < playlist_file.size(); pointer++) {
|
||||
playlist_file.seek(pointer);
|
||||
playlist_file.read(one_char, 1);
|
||||
if ((int) one_char[0] >= ' ') {
|
||||
line += one_char[0];
|
||||
} else if (one_char[0] == '\n') {
|
||||
txtline_process(line);
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
if (line.length() > 0) {
|
||||
txtline_process(line);
|
||||
}
|
||||
}
|
||||
playlist_masterdb = playlist_db;
|
||||
return ;
|
||||
}
|
||||
|
||||
|
||||
void PlaylistView::txtline_process(std::string &line) {
|
||||
playlist_entry new_item;
|
||||
rf::Frequency f;
|
||||
size_t previous = 0;
|
||||
size_t current = line.find(',');
|
||||
std::string freqs = line.substr(0, current);
|
||||
previous = current +1;
|
||||
current = line.find(',', previous);
|
||||
std::string file = line.substr(previous, current - previous);
|
||||
previous = current +1;
|
||||
current = line.find(',', previous);
|
||||
uint32_t sample_rate = strtoll(line.substr(previous).c_str(), nullptr, 10);
|
||||
|
||||
f = strtoll(freqs.c_str(), nullptr, 0);
|
||||
new_item.replay_frequency = f;
|
||||
new_item.replay_file = "/" + file;
|
||||
new_item.sample_rate = sample_rate;
|
||||
new_item.next_delay = 0;
|
||||
|
||||
playlist_db.push_back(std::move(new_item));
|
||||
}
|
||||
|
||||
void PlaylistView::on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate) {
|
||||
File data_file;
|
||||
// Get file size
|
||||
auto data_open_error = data_file.open("/" + new_file_path.string());
|
||||
if (data_open_error.is_valid()) {
|
||||
file_error();
|
||||
return;
|
||||
}
|
||||
|
||||
file_path = new_file_path;
|
||||
field_frequency.set_value(replay_frequency);
|
||||
|
||||
sample_rate = replay_sample_rate;
|
||||
|
||||
text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz");
|
||||
|
||||
auto file_size = data_file.size();
|
||||
auto duration = (file_size * 1000) / (2 * 2 * sample_rate);
|
||||
|
||||
progressbar.set_max(file_size);
|
||||
text_filename.set(file_path.filename().string().substr(0, 12));
|
||||
text_duration.set(to_string_time_ms(duration));
|
||||
|
||||
button_play.focus();
|
||||
}
|
||||
|
||||
void PlaylistView::on_tx_progress(const uint32_t progress) {
|
||||
progressbar.set_value(progress);
|
||||
}
|
||||
|
||||
void PlaylistView::focus() {
|
||||
button_open.focus();
|
||||
}
|
||||
|
||||
void PlaylistView::file_error() {
|
||||
nav_.display_modal("Error", "File "+file_path.string() +" read error. " +file_path.string());
|
||||
}
|
||||
|
||||
bool PlaylistView::is_active() const {
|
||||
return (bool)replay_thread;
|
||||
}
|
||||
|
||||
bool PlaylistView::loop() const {
|
||||
return (bool) playlist_db.size();
|
||||
}
|
||||
|
||||
void PlaylistView::toggle() {
|
||||
if( is_active() ) {
|
||||
stop(false);
|
||||
} else {
|
||||
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
void PlaylistView::start() {
|
||||
stop(false);
|
||||
|
||||
playlist_entry item = playlist_db.front();
|
||||
playlist_db.pop_front();
|
||||
// playlist_entry item = playlist_db[0];
|
||||
// for (playlist_entry item : playlist_db) {
|
||||
// file_path = item.replay_file;
|
||||
// rf::Frequency replay_frequency = strtoll(item.replay_frequency.c_str(),nullptr,10);
|
||||
on_file_changed(item.replay_file, item.replay_frequency, item.sample_rate);
|
||||
on_target_frequency_changed(item.replay_frequency);
|
||||
|
||||
std::unique_ptr<stream::Reader> reader;
|
||||
|
||||
auto p = std::make_unique<FileReader>();
|
||||
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);
|
||||
}
|
||||
|
||||
if( reader ) {
|
||||
button_play.set_bitmap(&bitmap_stop);
|
||||
baseband::set_sample_rate(sample_rate * 8);
|
||||
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
read_size, buffer_count,
|
||||
&ready_signal,
|
||||
[](uint32_t return_code) {
|
||||
ReplayThreadDoneMessage message { return_code };
|
||||
EventDispatcher::send_message(message);
|
||||
}
|
||||
);
|
||||
}
|
||||
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);
|
||||
|
||||
//Enable Bias Tee if selected
|
||||
radio::set_antenna_bias(portapack::get_antenna_bias());
|
||||
|
||||
radio::enable({
|
||||
receiver_model.tuning_frequency(),
|
||||
sample_rate * 8,
|
||||
baseband_bandwidth,
|
||||
rf::Direction::Transmit,
|
||||
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 PlaylistView::stop(const bool do_loop) {
|
||||
if( is_active() ) {
|
||||
replay_thread.reset();
|
||||
}
|
||||
if (do_loop) {
|
||||
if (playlist_db.size() > 0 ) {
|
||||
start();
|
||||
} else {
|
||||
playlist_db = playlist_masterdb;
|
||||
start();
|
||||
}
|
||||
} else {
|
||||
radio::set_antenna_bias(false); //Turn off Bias Tee
|
||||
radio::disable();
|
||||
button_play.set_bitmap(&bitmap_play);
|
||||
}
|
||||
|
||||
ready_signal = false;
|
||||
}
|
||||
|
||||
void PlaylistView::handle_replay_thread_done(const uint32_t return_code) {
|
||||
if (return_code == ReplayThread::END_OF_FILE) {
|
||||
stop(true);
|
||||
} else if (return_code == ReplayThread::READ_ERROR) {
|
||||
stop(false);
|
||||
file_error();
|
||||
}
|
||||
|
||||
progressbar.set_value(0);
|
||||
}
|
||||
|
||||
PlaylistView::PlaylistView(
|
||||
NavigationView& nav
|
||||
) : nav_ (nav)
|
||||
{
|
||||
|
||||
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)
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_replay);
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&button_open,
|
||||
&text_filename,
|
||||
&text_sample_rate,
|
||||
&text_duration,
|
||||
&progressbar,
|
||||
&field_frequency,
|
||||
&field_rfgain,
|
||||
&field_rfamp, // let's not use common rf_amp
|
||||
&check_loop,
|
||||
&button_play,
|
||||
&waterfall,
|
||||
});
|
||||
|
||||
field_frequency.set_value(target_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_frequency.set_step(5000);
|
||||
|
||||
button_play.on_select = [this](ImageButton&) {
|
||||
this->toggle();
|
||||
};
|
||||
|
||||
button_open.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".TXT");
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
load_file(new_file_path);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
PlaylistView::~PlaylistView() {
|
||||
radio::disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void PlaylistView::on_hide() {
|
||||
stop(false);
|
||||
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
|
||||
// it's being shown or hidden.
|
||||
waterfall.on_hide();
|
||||
View::on_hide();
|
||||
}
|
||||
|
||||
void PlaylistView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height };
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
void PlaylistView::on_target_frequency_changed(rf::Frequency f) {
|
||||
set_target_frequency(f);
|
||||
}
|
||||
|
||||
void PlaylistView::set_target_frequency(const rf::Frequency new_value) {
|
||||
persistent_memory::set_tuned_frequency(new_value);;
|
||||
}
|
||||
|
||||
rf::Frequency PlaylistView::target_frequency() const {
|
||||
return persistent_memory::tuned_frequency();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "replay_thread.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <deque>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class PlaylistView : public View {
|
||||
public:
|
||||
PlaylistView(NavigationView& nav);
|
||||
~PlaylistView();
|
||||
|
||||
void on_hide() override;
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Playlist"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
struct playlist_entry {
|
||||
rf::Frequency replay_frequency { 0 };
|
||||
std::string replay_file{};
|
||||
uint32_t sample_rate{};
|
||||
uint32_t next_delay{};
|
||||
};
|
||||
std::deque<playlist_entry> playlist_db{};
|
||||
std::deque<playlist_entry> playlist_masterdb{};
|
||||
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 };
|
||||
void load_file(std::filesystem::path playlist_path);
|
||||
void txtline_process(std::string &);
|
||||
void on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate);
|
||||
void on_target_frequency_changed(rf::Frequency f);
|
||||
void on_tx_progress(const uint32_t progress);
|
||||
|
||||
void set_target_frequency(const rf::Frequency new_value);
|
||||
rf::Frequency target_frequency() const;
|
||||
|
||||
void toggle();
|
||||
void start();
|
||||
void stop(const bool do_loop);
|
||||
bool is_active() const;
|
||||
bool loop() const;
|
||||
void set_ready();
|
||||
void handle_replay_thread_done(const uint32_t return_code);
|
||||
void file_error();
|
||||
|
||||
std::filesystem::path file_path { };
|
||||
std::unique_ptr<ReplayThread> replay_thread { };
|
||||
bool ready_signal { false };
|
||||
|
||||
Labels labels {
|
||||
{ { 10 * 8, 2 * 16 }, "GAIN A:", Color::light_grey() }
|
||||
};
|
||||
|
||||
Button button_open {
|
||||
{ 0 * 8, 0 * 16, 10 * 8, 2 * 16 },
|
||||
"Open file"
|
||||
};
|
||||
|
||||
|
||||
Text text_filename {
|
||||
{ 11 * 8, 0 * 16, 12 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
Text text_sample_rate {
|
||||
{ 24 * 8, 0 * 16, 6 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Text text_duration {
|
||||
{ 11 * 8, 1 * 16, 6 * 8, 16 },
|
||||
"-"
|
||||
};
|
||||
ProgressBar progressbar {
|
||||
{ 18 * 8, 1 * 16, 12 * 8, 16 }
|
||||
};
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 0 * 8, 2 * 16 },
|
||||
};
|
||||
|
||||
NumberField field_rfgain {
|
||||
{ 14 * 8, 2 * 16 },
|
||||
2,
|
||||
{ 0, 47 },
|
||||
1,
|
||||
' '
|
||||
};
|
||||
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 },
|
||||
4,
|
||||
"Loop",
|
||||
true
|
||||
};
|
||||
ImageButton button_play {
|
||||
{ 28 * 8, 2 * 16, 2 * 8, 1 * 16 },
|
||||
&bitmap_play,
|
||||
Color::green(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
spectrum::WaterfallWidget waterfall { };
|
||||
|
||||
MessageHandlerRegistration message_handler_replay_thread_error {
|
||||
Message::ID::ReplayThreadDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
this->handle_replay_thread_done(message.return_code);
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal {
|
||||
Message::ID::RequestSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const RequestSignalMessage*>(p);
|
||||
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
|
||||
this->set_ready();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress {
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "ui_pocsag_tx.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
|
||||
@@ -43,7 +44,8 @@ POCSAGTXView::~POCSAGTXView() {
|
||||
settings.save("tx_pocsag", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void POCSAGTXView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
@@ -180,7 +181,8 @@ RDSView::~RDSView() {
|
||||
settings.save("tx_rds", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void RDSView::start_tx() {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2018 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _UI_RECON
|
||||
#define _UI_RECON
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "receiver_model.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "freqman.hpp"
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "ui_mictx.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file.hpp"
|
||||
#include "app_settings.hpp"
|
||||
|
||||
|
||||
namespace ui {
|
||||
|
||||
class ReconThread {
|
||||
public:
|
||||
ReconThread(freqman_db *database );
|
||||
~ReconThread();
|
||||
|
||||
void set_recon(const bool v);
|
||||
void set_freq_delete(const bool v);
|
||||
bool is_recon();
|
||||
|
||||
void set_lock_duration( const uint32_t v );
|
||||
uint32_t get_lock_duration();
|
||||
void set_lock_nb_match( const uint32_t v );
|
||||
void set_match_mode( const uint32_t v );
|
||||
uint32_t get_lock_nb_match();
|
||||
|
||||
void set_freq_lock(const uint32_t v);
|
||||
uint32_t is_freq_lock();
|
||||
int64_t get_current_freq();
|
||||
|
||||
void set_stepper(const int64_t v);
|
||||
|
||||
void change_recon_direction();
|
||||
bool get_recon_direction();
|
||||
void set_recon_direction( const bool v);
|
||||
|
||||
void set_continuous(const bool v);
|
||||
|
||||
void set_default_modulation( freqman_index_t index );
|
||||
freqman_index_t get_current_modulation();
|
||||
void set_default_bandwidth( freqman_index_t index );
|
||||
freqman_index_t get_current_bandwidth();
|
||||
void set_default_step( freqman_index_t index );
|
||||
void set_freq_index( int16_t index );
|
||||
int16_t get_freq_index();
|
||||
|
||||
void run();
|
||||
void stop();
|
||||
|
||||
ReconThread(const ReconThread&) = delete;
|
||||
ReconThread(ReconThread&&) = delete;
|
||||
ReconThread& operator=(const ReconThread&) = delete;
|
||||
ReconThread& operator=(ReconThread&&) = delete;
|
||||
|
||||
private:
|
||||
freqman_db &frequency_list_ ;
|
||||
Thread* thread { nullptr };
|
||||
int64_t freq = 0 ;
|
||||
uint32_t step = 0 ;
|
||||
freqman_index_t def_modulation = 0 ;
|
||||
freqman_index_t def_bandwidth = 0 ;
|
||||
freqman_index_t def_step = 0 ;
|
||||
tone_index tone = 0 ;
|
||||
freqman_entry last_entry = { } ;
|
||||
int16_t frequency_index = 0 ;
|
||||
|
||||
bool _recon { true };
|
||||
bool _freq_delete { false };
|
||||
bool _fwd { true };
|
||||
bool _continuous { true };
|
||||
int64_t _stepper { 0 };
|
||||
int32_t _freq_lock { 0 };
|
||||
uint32_t _lock_duration { 50 };
|
||||
uint32_t _lock_nb_match { 10 };
|
||||
static msg_t static_fn(void* arg);
|
||||
};
|
||||
|
||||
class ReconView : public View {
|
||||
public:
|
||||
ReconView(NavigationView& nav);
|
||||
~ReconView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
void big_display_freq( int64_t f );
|
||||
|
||||
const Style style_grey { // recon
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::grey(),
|
||||
};
|
||||
|
||||
const Style style_white { // recon
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
const Style style_yellow { //Found signal
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
|
||||
const Style style_green { //Found signal
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::green(),
|
||||
};
|
||||
|
||||
const Style style_red { //erasing freq
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::red(),
|
||||
};
|
||||
|
||||
const Style style_blue { // quick recon, wait == 0
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::black(),
|
||||
.foreground = Color::blue(),
|
||||
};
|
||||
|
||||
std::string title() const override { return "Recon"; };
|
||||
|
||||
//void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
void start_recon_thread();
|
||||
size_t change_mode( freqman_index_t mod_type);
|
||||
void show_max( bool refresh_display = false );
|
||||
void recon_pause();
|
||||
void recon_resume();
|
||||
void user_pause();
|
||||
void user_resume();
|
||||
void frequency_file_load( bool stop_all_before = false);
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
void on_headphone_volume_changed(int32_t v);
|
||||
void set_display_freq( int64_t freq );
|
||||
void handle_retune( int64_t freq , uint32_t index );
|
||||
bool check_sd_card();
|
||||
|
||||
jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual recon task too...
|
||||
int32_t squelch { 0 };
|
||||
int32_t db { 0 };
|
||||
int32_t timer { 0 };
|
||||
int32_t wait { 5000 }; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity
|
||||
uint32_t lock_wait { 500 }; // in msec. Represent the maximum amount of time we will wait for a lock to complete before switching to next
|
||||
int32_t def_step { 0 };
|
||||
freqman_db frequency_list = { };
|
||||
uint32_t current_index { 0 };
|
||||
bool userpause { false };
|
||||
bool continuous_lock { false };
|
||||
std::string input_file = { "RECON" };
|
||||
std::string output_file = { "RECON_RESULTS" };
|
||||
bool autosave = { true };
|
||||
bool autostart = { true };
|
||||
bool continuous = { true };
|
||||
bool filedelete = { true };
|
||||
bool load_freqs = { true };
|
||||
bool load_ranges = { true };
|
||||
bool load_hamradios = { true };
|
||||
bool update_ranges = { true };
|
||||
bool fwd = { true };
|
||||
// maximum usable freq
|
||||
long long int MAX_UFREQ = { 7200000000 };
|
||||
uint32_t recon_lock_nb_match = { 10 };
|
||||
uint32_t recon_lock_duration = { 50 };
|
||||
uint32_t recon_match_mode = { 0 };
|
||||
bool scanner_mode { false };
|
||||
bool manual_mode { false };
|
||||
bool sd_card_mounted = false ;
|
||||
int32_t volume = 40 ;
|
||||
|
||||
Labels labels
|
||||
{
|
||||
{ { 0 * 8 , 0 * 16 }, "LNA: VGA: AMP: VOL: ", Color::light_grey() },
|
||||
{ { 0 * 8 , 1 * 16 }, "BW: SQ: W,L: , ", Color::light_grey() },
|
||||
{ { 3 * 8 , 10 * 16 }, "START END MANUAL", Color::light_grey() },
|
||||
{ { 0 * 8 , (26 * 8) + 4 }, "MODE:", Color::light_grey() },
|
||||
{ { 11 * 8 , (26 * 8) + 4 }, "STEP:", Color::light_grey() },
|
||||
};
|
||||
|
||||
LNAGainField field_lna {
|
||||
{ 4 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
VGAGainField field_vga {
|
||||
{ 11 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
RFAmpField field_rf_amp {
|
||||
{ 18 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
NumberField field_volume {
|
||||
{ 24 * 8, 0 * 16 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
OptionsField field_bw {
|
||||
{ 3 * 8, 1 * 16 },
|
||||
6,
|
||||
{ }
|
||||
};
|
||||
|
||||
NumberField field_squelch {
|
||||
{ 12 * 8, 1 * 16 },
|
||||
3,
|
||||
{ -90, 20 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_wait {
|
||||
{ 20 * 8, 1 * 16 },
|
||||
5,
|
||||
{ -9000, 9000 },
|
||||
100,
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_lock_wait {
|
||||
{ 26 * 8, 1 * 16 },
|
||||
4,
|
||||
{ 100 , 9000 },
|
||||
100,
|
||||
' ',
|
||||
};
|
||||
|
||||
RSSI rssi {
|
||||
{ 0 * 16, 2 * 16, 15 * 16, 8 },
|
||||
};
|
||||
|
||||
Text text_cycle {
|
||||
{ 0, 3 * 16, 3 * 8, 16 },
|
||||
};
|
||||
|
||||
Text text_max {
|
||||
{ 3 * 8, 3 * 16, 20 * 8 , 16 },
|
||||
};
|
||||
|
||||
Text desc_cycle {
|
||||
{0, 4 * 16, 240, 16 },
|
||||
};
|
||||
|
||||
/* BigFrequency big_display { //Show frequency in glamour
|
||||
{ 4, 7 * 16 - 8 , 28 * 8, 52 },
|
||||
0
|
||||
}; */
|
||||
|
||||
Text big_display { //Show frequency in text mode
|
||||
{ 0, 5 * 16 , 28 * 8, 16 },
|
||||
};
|
||||
|
||||
Text freq_stats { //Show frequency stats in text mode
|
||||
{ 0, 6 * 16 , 28 * 8, 16 },
|
||||
};
|
||||
|
||||
Text text_timer { //Show frequency stats in text mode
|
||||
{ 0, 7 * 16 , 28 * 8, 16 },
|
||||
};
|
||||
|
||||
Button button_recon_setup {
|
||||
{ 25 * 8 , 2 * 16 + 8 , 4 * 8, 28 },
|
||||
"OPT"
|
||||
};
|
||||
|
||||
Button button_scanner_mode {
|
||||
{ 21 * 8 , 8 * 16 , 9 * 8, 28 },
|
||||
"RECON"
|
||||
};
|
||||
|
||||
Text file_name { //Show file used
|
||||
{ 0 , 8 * 16 + 4 , 20 * 8, 16 },
|
||||
};
|
||||
|
||||
|
||||
ButtonWithEncoder button_manual_start {
|
||||
{ 0 * 8, 11 * 16, 11 * 8, 28 },
|
||||
""
|
||||
};
|
||||
|
||||
ButtonWithEncoder button_manual_end {
|
||||
{ 12 * 8 - 6, 11 * 16, 11 * 8, 28 },
|
||||
""
|
||||
};
|
||||
|
||||
Button button_manual_recon {
|
||||
{ 23 * 8 - 3, 11 * 16, 7 * 8 , 28 },
|
||||
"SEARCH"
|
||||
};
|
||||
|
||||
OptionsField field_mode {
|
||||
{ 5 * 8, (26 * 8) + 4 },
|
||||
6,
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
OptionsField step_mode {
|
||||
{ 17 * 8, (26 * 8) + 4 },
|
||||
12,
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
ButtonWithEncoder button_pause {
|
||||
{ 0, (15 * 16) - 4, 72, 28 },
|
||||
"PAUSE"
|
||||
};
|
||||
|
||||
|
||||
Button button_audio_app {
|
||||
{ 84, (15 * 16) - 4, 72, 28 },
|
||||
"AUDIO"
|
||||
};
|
||||
|
||||
ButtonWithEncoder button_add {
|
||||
{ 168, (15 * 16) - 4, 72, 28 },
|
||||
"<STORE>"
|
||||
};
|
||||
|
||||
Button button_dir {
|
||||
{ 0, (35 * 8) - 4, 34, 28 },
|
||||
"FW>"
|
||||
};
|
||||
|
||||
Button button_restart {
|
||||
{ 38, (35 * 8) - 4, 34, 28 },
|
||||
"RST"
|
||||
};
|
||||
|
||||
|
||||
Button button_mic_app {
|
||||
{ 84, (35 * 8) - 4, 72, 28 },
|
||||
"MIC TX"
|
||||
};
|
||||
|
||||
ButtonWithEncoder button_remove {
|
||||
{ 168, (35 * 8) - 4, 72, 28 },
|
||||
"<REMOVE>"
|
||||
};
|
||||
|
||||
std::unique_ptr<ReconThread> recon_thread { };
|
||||
|
||||
MessageHandlerRegistration message_handler_retune {
|
||||
Message::ID::Retune,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const RetuneMessage*>(p);
|
||||
this->handle_retune(message.freq,message.range);
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_stats {
|
||||
Message::ID::ChannelStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
|
||||
}
|
||||
};
|
||||
// app save settings
|
||||
std::app_settings settings { };
|
||||
std::app_settings::AppSettings app_settings { };
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui_recon_settings.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
bool ReconSetupLoadStrings( std::string source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , uint32_t &lock_wait , int32_t &volume )
|
||||
{
|
||||
File settings_file;
|
||||
size_t length, file_position = 0;
|
||||
char * pos;
|
||||
char * line_start;
|
||||
char * line_end;
|
||||
char file_data[257];
|
||||
|
||||
uint32_t it = 0 ;
|
||||
uint32_t nb_params = 9 ;
|
||||
std::string params[ 9 ];
|
||||
|
||||
bool check_sd_card = (sd_card::status() == sd_card::Status::Mounted) ? true : false ;
|
||||
|
||||
if( check_sd_card )
|
||||
{
|
||||
auto result = settings_file.open( source );
|
||||
if( !result.is_valid() )
|
||||
{
|
||||
while( it < nb_params )
|
||||
{
|
||||
// Read a 256 bytes block from file
|
||||
settings_file.seek(file_position);
|
||||
memset(file_data, 0, 257);
|
||||
auto read_size = settings_file.read(file_data, 256);
|
||||
if (read_size.is_error())
|
||||
break ;
|
||||
file_position += 256;
|
||||
// Reset line_start to beginning of buffer
|
||||
line_start = file_data;
|
||||
pos=line_start;
|
||||
while ((line_end = strstr(line_start, "\x0A"))) {
|
||||
length = line_end - line_start - 1 ;
|
||||
params[ it ] = string( pos , length );
|
||||
it ++ ;
|
||||
line_start = line_end + 1;
|
||||
pos=line_start ;
|
||||
if (line_start - file_data >= 256)
|
||||
break;
|
||||
if( it >= nb_params )
|
||||
break ;
|
||||
}
|
||||
if (read_size.value() != 256)
|
||||
break; // End of file
|
||||
|
||||
// Restart at beginning of last incomplete line
|
||||
file_position -= (file_data + 256 - line_start);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if( it > 0 )
|
||||
input_file = params[ 0 ];
|
||||
else
|
||||
input_file = "RECON" ;
|
||||
|
||||
if( it > 1 )
|
||||
output_file= params[ 1 ];
|
||||
else
|
||||
output_file = "RECON_RESULTS" ;
|
||||
|
||||
if( it > 2 )
|
||||
recon_lock_duration = strtoll( params[ 2 ].c_str() , nullptr , 10 );
|
||||
else
|
||||
recon_lock_duration = 50 ;
|
||||
|
||||
if( it > 3 )
|
||||
recon_lock_nb_match = strtoll( params[ 3 ].c_str() , nullptr , 10 );
|
||||
else
|
||||
recon_lock_nb_match = 10 ;
|
||||
|
||||
if( it > 4 )
|
||||
recon_squelch_level = strtoll( params[ 4 ].c_str() , nullptr , 10 );
|
||||
else
|
||||
recon_squelch_level = -14 ;
|
||||
|
||||
if( it > 5 )
|
||||
recon_match_mode = strtoll( params[ 5 ].c_str() , nullptr , 10 );
|
||||
else
|
||||
recon_match_mode = 0 ;
|
||||
|
||||
if( it > 6 )
|
||||
wait = strtoll( params[ 6 ].c_str() , nullptr , 10 );
|
||||
else
|
||||
wait = 5000 ;
|
||||
|
||||
if( it > 7 )
|
||||
lock_wait = strtoll( params[ 7 ].c_str() , nullptr , 10 );
|
||||
else
|
||||
lock_wait = 1000 ;
|
||||
|
||||
if( it > 8 )
|
||||
volume = strtoll( params[ 8 ].c_str() , nullptr , 10 );
|
||||
else
|
||||
volume = 40 ;
|
||||
|
||||
if( it < nb_params )
|
||||
{
|
||||
/* bad number of params, signal defaults */
|
||||
return false ;
|
||||
}
|
||||
return true ;
|
||||
}
|
||||
|
||||
bool ReconSetupSaveStrings( std::string dest, std::string input_file , std::string output_file , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , uint32_t lock_wait , int32_t volume )
|
||||
{
|
||||
File settings_file;
|
||||
|
||||
auto result = settings_file.create( dest );
|
||||
if( result.is_valid() )
|
||||
return false ;
|
||||
settings_file.write_line( input_file );
|
||||
settings_file.write_line( output_file );
|
||||
settings_file.write_line( to_string_dec_uint( recon_lock_duration ) );
|
||||
settings_file.write_line( to_string_dec_uint( recon_lock_nb_match ) );
|
||||
settings_file.write_line( to_string_dec_int( recon_squelch_level ) );
|
||||
settings_file.write_line( to_string_dec_uint( recon_match_mode ) );
|
||||
settings_file.write_line( to_string_dec_int( wait ) );
|
||||
settings_file.write_line( to_string_dec_uint( lock_wait ) );
|
||||
settings_file.write_line( to_string_dec_int( volume ) );
|
||||
return true ;
|
||||
}
|
||||
|
||||
ReconSetupViewMain::ReconSetupViewMain( NavigationView &nav , Rect parent_rect , std::string input_file , std::string output_file ) : View( parent_rect ) , _input_file { input_file } , _output_file { output_file }
|
||||
{
|
||||
hidden(true);
|
||||
add_children({
|
||||
&button_load_freqs,
|
||||
&text_input_file,
|
||||
&button_save_freqs,
|
||||
&button_output_file,
|
||||
&checkbox_autosave_freqs,
|
||||
&checkbox_autostart_recon,
|
||||
&checkbox_continuous,
|
||||
&checkbox_clear_output
|
||||
});
|
||||
|
||||
checkbox_autosave_freqs.set_value( persistent_memory::recon_autosave_freqs() );
|
||||
checkbox_autostart_recon.set_value( persistent_memory::recon_autostart_recon() );
|
||||
checkbox_continuous.set_value( persistent_memory::recon_continuous() );
|
||||
checkbox_clear_output.set_value( persistent_memory::recon_clear_output() );
|
||||
|
||||
text_input_file.set( _input_file );
|
||||
button_output_file.set_text( _output_file );
|
||||
|
||||
button_load_freqs.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".TXT");
|
||||
open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) {
|
||||
std::string dir_filter = "FREQMAN/";
|
||||
std::string str_file_path = new_file_path.string();
|
||||
if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder
|
||||
// get the filename without txt extension so we can use load_freqman_file fcn
|
||||
_input_file = new_file_path.stem().string();
|
||||
text_input_file.set( _input_file );
|
||||
} else {
|
||||
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
button_save_freqs.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".TXT");
|
||||
open_view->on_changed = [this,&nav](std::filesystem::path new_file_path) {
|
||||
std::string dir_filter = "FREQMAN/";
|
||||
std::string str_file_path = new_file_path.string();
|
||||
if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder
|
||||
_output_file = new_file_path.stem().string();
|
||||
button_output_file.set_text( _output_file );
|
||||
} else {
|
||||
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
button_output_file.on_select =[this, &nav](Button&) {
|
||||
text_prompt( nav, _output_file , 28 ,
|
||||
[this](std::string& buffer) {
|
||||
_output_file = buffer ;
|
||||
button_output_file.set_text( _output_file );
|
||||
} );
|
||||
};
|
||||
};
|
||||
|
||||
void ReconSetupViewMain::Save( std::string &input_file , std::string &output_file ) {
|
||||
persistent_memory::set_recon_autosave_freqs(checkbox_autosave_freqs.value());
|
||||
persistent_memory::set_recon_autostart_recon(checkbox_autostart_recon.value());
|
||||
persistent_memory::set_recon_continuous(checkbox_continuous.value());
|
||||
persistent_memory::set_recon_clear_output(checkbox_clear_output.value());
|
||||
input_file=_input_file ;
|
||||
output_file=_output_file ;
|
||||
};
|
||||
void ReconSetupViewMore::Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode ) {
|
||||
persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value());
|
||||
persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value());
|
||||
persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value());
|
||||
persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value());
|
||||
recon_lock_duration = field_recon_lock_duration.value();
|
||||
recon_lock_nb_match = field_recon_lock_nb_match.value();
|
||||
recon_match_mode = field_recon_match_mode . selected_index_value() ;
|
||||
};
|
||||
|
||||
void ReconSetupViewMain::focus() {
|
||||
button_load_freqs.focus();
|
||||
}
|
||||
|
||||
ReconSetupViewMore::ReconSetupViewMore( NavigationView &nav , Rect parent_rect , uint32_t recon_lock_duration , uint32_t recon_lock_nb_match , uint32_t recon_match_mode ) : View( parent_rect ), _recon_lock_duration { recon_lock_duration } , _recon_lock_nb_match { recon_lock_nb_match } , _recon_match_mode { recon_match_mode }
|
||||
{
|
||||
(void)nav;
|
||||
hidden(true);
|
||||
|
||||
add_children({
|
||||
&checkbox_load_freqs,
|
||||
&checkbox_load_ranges,
|
||||
&checkbox_load_hamradios,
|
||||
&checkbox_update_ranges_when_recon,
|
||||
&text_recon_lock_duration,
|
||||
&field_recon_lock_duration,
|
||||
&text_recon_lock_nb,
|
||||
&field_recon_lock_nb_match,
|
||||
&field_recon_match_mode,
|
||||
});
|
||||
|
||||
checkbox_load_freqs.set_value( persistent_memory::recon_load_freqs() );
|
||||
checkbox_load_ranges.set_value( persistent_memory::recon_load_ranges() );
|
||||
checkbox_load_hamradios.set_value( persistent_memory::recon_load_hamradios() );
|
||||
checkbox_update_ranges_when_recon.set_value( persistent_memory::recon_update_ranges_when_recon() );
|
||||
field_recon_lock_duration.set_value( _recon_lock_duration );
|
||||
field_recon_lock_nb_match.set_value( _recon_lock_nb_match );
|
||||
field_recon_match_mode.set_by_value( _recon_match_mode );
|
||||
};
|
||||
|
||||
void ReconSetupViewMore::focus() {
|
||||
checkbox_load_freqs.focus();
|
||||
}
|
||||
|
||||
void ReconSetupView::focus() {
|
||||
viewMain.focus();
|
||||
}
|
||||
|
||||
ReconSetupView::ReconSetupView(
|
||||
NavigationView& nav , std::string _input_file , std::string _output_file , uint32_t _recon_lock_duration , uint32_t _recon_lock_nb_match , uint32_t _recon_match_mode ) : nav_ { nav } , input_file { _input_file } , output_file { _output_file } , recon_lock_duration { _recon_lock_duration } , recon_lock_nb_match { _recon_lock_nb_match } , recon_match_mode { _recon_match_mode }
|
||||
{
|
||||
add_children({
|
||||
&tab_view,
|
||||
&viewMain,
|
||||
&viewMore,
|
||||
&button_save
|
||||
});
|
||||
|
||||
button_save.on_select = [this,&nav](Button&) {
|
||||
viewMain.Save( input_file , output_file );
|
||||
viewMore.Save( recon_lock_duration , recon_lock_nb_match , recon_match_mode );
|
||||
std::vector<std::string> messages ;
|
||||
messages.push_back( input_file );
|
||||
messages.push_back( output_file );
|
||||
messages.push_back( to_string_dec_uint( recon_lock_duration ) );
|
||||
messages.push_back( to_string_dec_uint( recon_lock_nb_match ) );
|
||||
messages.push_back( to_string_dec_uint( recon_match_mode ) );
|
||||
on_changed( messages );
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
} /* namespace ui */
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "serializer.hpp"
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
bool ReconSetupLoadStrings( std::string source, std::string &input_file , std::string &output_file , uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , int32_t &recon_squelch_level , uint32_t &recon_match_mode , int32_t &wait , uint32_t &lock_wait , int32_t &volume );
|
||||
bool ReconSetupSaveStrings( std::string dest, const std::string input_file , const std::string output_file , const uint32_t recon_lock_duration , const uint32_t recon_lock_nb_match , int32_t recon_squelch_level , uint32_t recon_match_mode , int32_t wait , uint32_t lock_wait , int32_t volume );
|
||||
|
||||
class ReconSetupViewMain : public View {
|
||||
public:
|
||||
ReconSetupViewMain( NavigationView& nav, Rect parent_rect , std::string input_file , std::string output_file );
|
||||
void Save( std::string &input_file , std::string &output_file );
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
std::string _input_file = { "RECON" };
|
||||
std::string _output_file = { "RECON_RESULTS" };
|
||||
|
||||
Button button_load_freqs {
|
||||
{ 1 * 8 , 12 , 18 * 8 , 22 },
|
||||
"select input file"
|
||||
};
|
||||
Text text_input_file {
|
||||
{ 1 * 8 , 4 + 2 * 16, 18 * 8, 22 },
|
||||
"RECON"
|
||||
};
|
||||
|
||||
Button button_save_freqs {
|
||||
{ 1 * 8 , 4 * 16 - 8 , 18 * 8 , 22 },
|
||||
"select output file"
|
||||
};
|
||||
Button button_output_file {
|
||||
{ 1 * 8 , 5 * 16 - 2, 18 * 8, 22 },
|
||||
"RECON_RESULTS"
|
||||
};
|
||||
|
||||
Checkbox checkbox_autosave_freqs {
|
||||
{ 1 * 8, 7 * 16 - 4 },
|
||||
3,
|
||||
"autosave freqs"
|
||||
};
|
||||
|
||||
Checkbox checkbox_autostart_recon {
|
||||
{ 1 * 8, 9 * 16 - 4 },
|
||||
3,
|
||||
"autostart recon"
|
||||
};
|
||||
|
||||
Checkbox checkbox_continuous {
|
||||
{ 1 * 8, 11 * 16 - 4 },
|
||||
3,
|
||||
"continuous"
|
||||
};
|
||||
Checkbox checkbox_clear_output {
|
||||
{ 1 * 8, 13 * 16 - 4 },
|
||||
3,
|
||||
"clear output at start"
|
||||
};
|
||||
};
|
||||
|
||||
class ReconSetupViewMore : public View {
|
||||
public:
|
||||
ReconSetupViewMore( NavigationView& nav, Rect parent_rect , const uint32_t _recon_lock_duration , const uint32_t _recon_lock_nb_match , const uint32_t _recon_match_mode );
|
||||
|
||||
void Save( uint32_t &recon_lock_duration , uint32_t &recon_lock_nb_match , uint32_t &recon_match_mode );
|
||||
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
|
||||
const uint32_t _recon_lock_duration = 50 ;
|
||||
const uint32_t _recon_lock_nb_match = 10 ;
|
||||
const uint32_t _recon_match_mode = 0 ;
|
||||
|
||||
Checkbox checkbox_load_freqs {
|
||||
{ 1 * 8, 12 },
|
||||
3,
|
||||
"input: load freqs"
|
||||
};
|
||||
|
||||
Checkbox checkbox_load_ranges {
|
||||
{ 1 * 8, 42 },
|
||||
3,
|
||||
"input: load ranges"
|
||||
};
|
||||
|
||||
Checkbox checkbox_load_hamradios {
|
||||
{ 1 * 8, 72 },
|
||||
3,
|
||||
"input: load hamradios"
|
||||
};
|
||||
|
||||
Checkbox checkbox_update_ranges_when_recon {
|
||||
{ 1 * 8, 102 },
|
||||
3,
|
||||
"auto update m-ranges"
|
||||
};
|
||||
Text text_recon_lock_duration {
|
||||
{ 1 * 8 , 132 , 22 * 8 , 22 },
|
||||
" ms (lock duration)"
|
||||
};
|
||||
NumberField field_recon_lock_duration {
|
||||
{ 1 * 8, 132 }, // position X , Y
|
||||
4, // number of displayed digits (even empty)
|
||||
{ 50 , 990 }, // range of number
|
||||
10, // rotary encoder increment
|
||||
' ', // filling character
|
||||
false // can loop
|
||||
};
|
||||
Text text_recon_lock_nb {
|
||||
{ 1 * 8 , 162 , 25 * 8 , 22 },
|
||||
" x (nb lock to match freq)"
|
||||
};
|
||||
NumberField field_recon_lock_nb_match {
|
||||
{ 1 * 8, 162 },
|
||||
4,
|
||||
{ 1, 99 },
|
||||
1,
|
||||
' ',
|
||||
false
|
||||
};
|
||||
OptionsField field_recon_match_mode {
|
||||
{ 1 * 8, 192 },
|
||||
20, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE
|
||||
{
|
||||
{ "SQL MATCH: CONTINOUS" , 0 },
|
||||
{ "SQL MATCH: SPARSE" , 1 }
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
class ReconSetupView : public View {
|
||||
public:
|
||||
ReconSetupView( NavigationView& nav , std::string _input_file , std::string _output_file , const uint32_t _recon_lock_duration , const uint32_t _recon_lock_nb_match , const uint32_t _recon_match_mode );
|
||||
|
||||
std::function<void( std::vector<std::string> messages )> on_changed { };
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Recon setup"; };
|
||||
|
||||
private:
|
||||
|
||||
NavigationView& nav_ ;
|
||||
|
||||
std::string input_file = { "RECON" };
|
||||
std::string output_file = { "RECON_RESULTS" };
|
||||
uint32_t recon_lock_duration = 50 ;
|
||||
uint32_t recon_lock_nb_match = 10 ;
|
||||
uint32_t recon_match_mode = 0 ;
|
||||
|
||||
Rect view_rect = { 0, 3 * 8, 240, 230 };
|
||||
|
||||
ReconSetupViewMain viewMain{ nav_ , view_rect , input_file , output_file };
|
||||
ReconSetupViewMore viewMore{ nav_ , view_rect , recon_lock_duration , recon_lock_nb_match , recon_match_mode };
|
||||
|
||||
TabView tab_view {
|
||||
{ "Main", Color::cyan() , &viewMain },
|
||||
{ "More", Color::green(), &viewMore }
|
||||
};
|
||||
Button button_save {
|
||||
{ 9 * 8, 255, 14 * 8 , 40 },
|
||||
"SAVE"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
@@ -31,13 +31,23 @@ ScannerThread::ScannerThread(
|
||||
std::vector<rf::Frequency> frequency_list
|
||||
) : frequency_list_ { std::move(frequency_list) }
|
||||
{
|
||||
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ScannerThread::static_fn, this);
|
||||
create_thread();
|
||||
}
|
||||
|
||||
ScannerThread::ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step) : frequency_range_(frequency_range), def_step_(def_step)
|
||||
{
|
||||
create_thread();
|
||||
}
|
||||
|
||||
ScannerThread::~ScannerThread() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void ScannerThread::create_thread()
|
||||
{
|
||||
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ScannerThread::static_fn, this);
|
||||
}
|
||||
|
||||
void ScannerThread::stop() {
|
||||
if( thread ) {
|
||||
chThdTerminate(thread);
|
||||
@@ -102,12 +112,13 @@ void ScannerThread::run() {
|
||||
else
|
||||
restart_scan=false; //Effectively skipping first retuning, giving system time
|
||||
}
|
||||
message.freq = frequency_list_[frequency_index];
|
||||
message.range = frequency_index; //Inform freq (for coloring purposes also!)
|
||||
EventDispatcher::send_message(message);
|
||||
}
|
||||
else { //NOT scanning
|
||||
if (_freq_del != 0) { //There is a frequency to delete
|
||||
for (uint16_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete
|
||||
for (uint32_t i = 0; i < frequency_list_.size(); i++) { //Search for the freq to delete
|
||||
if (frequency_list_[i] == _freq_del)
|
||||
{ //found: Erase it
|
||||
frequency_list_.erase(frequency_list_.begin() + i);
|
||||
@@ -126,16 +137,58 @@ void ScannerThread::run() {
|
||||
}
|
||||
chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq
|
||||
}
|
||||
}else if(def_step_ > 0) // manual mode
|
||||
{
|
||||
RetuneMessage message { };
|
||||
int64_t frequency_index = 0;
|
||||
int64_t size = (frequency_range_.max - frequency_range_.min) / def_step_;
|
||||
bool restart_scan = false; //Flag whenever scanning is restarting after a pause
|
||||
while( !chThdShouldTerminate() ) {
|
||||
if (_scanning) { //Scanning
|
||||
if (_freq_lock == 0) { //normal scanning (not performing freq_lock)
|
||||
if (!restart_scan) { //looping at full speed
|
||||
if (_fwd) { //forward
|
||||
frequency_index++;
|
||||
if (frequency_index >= size)
|
||||
frequency_index = 0;
|
||||
|
||||
} else { //reverse
|
||||
if (frequency_index < 1)
|
||||
frequency_index = size;
|
||||
frequency_index--;
|
||||
}
|
||||
receiver_model.set_tuning_frequency(frequency_range_.min + frequency_index * def_step_); // Retune
|
||||
}
|
||||
else
|
||||
restart_scan=false; //Effectively skipping first retuning, giving system time
|
||||
}
|
||||
message.freq = frequency_range_.min + frequency_index * def_step_;
|
||||
message.range = 0; //Inform freq (for coloring purposes also!)
|
||||
EventDispatcher::send_message(message);
|
||||
}
|
||||
else { //NOT scanning
|
||||
restart_scan=true; //Flag the need for skipping a cycle when restarting scan
|
||||
}
|
||||
chThdSleepMilliseconds(50); //Needed to (eventually) stabilize the receiver into new freq
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScannerView::handle_retune(uint32_t i) {
|
||||
void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
|
||||
|
||||
current_index = freq_idx; //since it is an ongoing scan, this is a new index
|
||||
current_frequency = freq;
|
||||
|
||||
switch (scan_thread->is_freq_lock())
|
||||
{
|
||||
case 0: //NO FREQ LOCK, ONGOING STANDARD SCANNING
|
||||
text_cycle.set( to_string_dec_uint(i + 1,3) );
|
||||
current_index = i; //since it is an ongoing scan, this is a new index
|
||||
if (description_list[current_index].size() > 0) desc_cycle.set( description_list[current_index] ); //Show new description
|
||||
if (frequency_list.size() > 0) {
|
||||
text_cycle.set( to_string_dec_uint(freq_idx + 1,3) );
|
||||
}
|
||||
|
||||
if (freq_idx < description_list.size() && description_list[freq_idx].size() > 0) {
|
||||
desc_cycle.set( description_list[freq_idx] ); //Show new description
|
||||
}
|
||||
break;
|
||||
case 1: //STARTING LOCK FREQ
|
||||
big_display.set_style(&style_yellow);
|
||||
@@ -146,7 +199,7 @@ void ScannerView::handle_retune(uint32_t i) {
|
||||
default: //freq lock is checking the signal, do not update display
|
||||
return;
|
||||
}
|
||||
big_display.set(frequency_list[current_index]); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching)
|
||||
big_display.set(freq); //UPDATE the big Freq after 0, 1 or MAX_FREQ_LOCK (at least, for color synching)
|
||||
}
|
||||
|
||||
void ScannerView::focus() {
|
||||
@@ -154,15 +207,17 @@ void ScannerView::focus() {
|
||||
}
|
||||
|
||||
ScannerView::~ScannerView() {
|
||||
// make sure to stop the thread before shutting down the receiver
|
||||
scan_thread.reset();
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void ScannerView::show_max() { //show total number of freqs to scan
|
||||
if (frequency_list.size() == MAX_DB_ENTRY) {
|
||||
if (frequency_list.size() == FREQMAN_MAX_PER_FILE ) {
|
||||
text_max.set_style(&style_red);
|
||||
text_max.set( "/ " + to_string_dec_uint(MAX_DB_ENTRY) + " (DB MAX!)");
|
||||
text_max.set( "/ " + to_string_dec_uint(FREQMAN_MAX_PER_FILE ) + " (DB MAX!)");
|
||||
}
|
||||
else {
|
||||
text_max.set_style(&style_grey);
|
||||
@@ -297,23 +352,15 @@ ScannerView::ScannerView(
|
||||
description_list.clear();
|
||||
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
|
||||
|
||||
description_list.push_back(
|
||||
"M" + to_string_short_freq(frequency_range.min) + ">"
|
||||
+ to_string_short_freq(frequency_range.max) + "S"
|
||||
+ to_string_short_freq(def_step).erase(0,1) //euquiq: lame kludge to reduce spacing in step freq
|
||||
);
|
||||
|
||||
rf::Frequency frequency = frequency_range.min;
|
||||
while (frequency_list.size() < MAX_DB_ENTRY && frequency <= frequency_range.max) { //add manual range
|
||||
frequency_list.push_back(frequency);
|
||||
description_list.push_back(""); //If empty, will keep showing the last description
|
||||
frequency+=def_step;
|
||||
}
|
||||
show_max();
|
||||
desc_cycle.set("");
|
||||
text_max.set("");
|
||||
text_cycle.set("");
|
||||
|
||||
if ( userpause ) //If user-paused, resume
|
||||
user_resume();
|
||||
big_display.set_style(&style_grey); //Back to grey color
|
||||
start_scan_thread(); //RESTART SCANNER THREAD
|
||||
start_scan_thread(frequency_range, def_step); //RESTART SCANNER THREAD
|
||||
}
|
||||
};
|
||||
|
||||
@@ -333,35 +380,56 @@ ScannerView::ScannerView(
|
||||
big_display.set_style(&style_grey); //Back to grey color
|
||||
};
|
||||
|
||||
button_add.on_select = [this](Button&) { //frequency_list[current_index]
|
||||
|
||||
button_add.on_select = [this](Button&) { // current_frequency
|
||||
|
||||
File scanner_file;
|
||||
std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
|
||||
const std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
|
||||
auto result = scanner_file.open(freq_file_path); //First search if freq is already in txt
|
||||
|
||||
if (!result.is_valid()) {
|
||||
std::string frequency_to_add = "f="
|
||||
+ to_string_dec_uint(frequency_list[current_index] / 1000)
|
||||
+ to_string_dec_uint(frequency_list[current_index] % 1000UL, 3, '0');
|
||||
char one_char[1]; //Read it char by char
|
||||
std::string line; //and put read line in here
|
||||
const std::string frequency_to_add = "f="
|
||||
+ to_string_dec_uint(current_frequency / 1000)
|
||||
+ to_string_dec_uint(current_frequency % 1000UL, 3, '0');
|
||||
|
||||
bool found=false;
|
||||
for (size_t pointer=0; pointer < scanner_file.size();pointer++) {
|
||||
constexpr size_t buffer_size = 1024;
|
||||
char buffer[buffer_size];
|
||||
|
||||
for (size_t pointer = 0, freq_str_idx = 0; pointer < scanner_file.size(); pointer += buffer_size) {
|
||||
size_t adjusted_buffer_size;
|
||||
if (pointer + buffer_size >= scanner_file.size()) {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
adjusted_buffer_size = scanner_file.size() - pointer;
|
||||
}
|
||||
else {
|
||||
adjusted_buffer_size = buffer_size;
|
||||
}
|
||||
|
||||
scanner_file.seek(pointer);
|
||||
scanner_file.read(one_char, 1);
|
||||
if ((int)one_char[0] > 31) { //ascii space upwards
|
||||
line += one_char[0]; //Add it to the textline
|
||||
}
|
||||
else if (one_char[0] == '\n') { //New Line
|
||||
if (line.compare(0, frequency_to_add.size(),frequency_to_add) == 0) {
|
||||
found=true;
|
||||
break;
|
||||
scanner_file.read(buffer, adjusted_buffer_size);
|
||||
|
||||
for (size_t i = 0; i < adjusted_buffer_size; ++i) {
|
||||
if (buffer[i] == frequency_to_add.data()[freq_str_idx]) {
|
||||
++freq_str_idx;
|
||||
if (freq_str_idx >= frequency_to_add.size()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
freq_str_idx = 0;
|
||||
}
|
||||
line.clear(); //Ready for next textline
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
nav_.display_modal("Error", "Frequency already exists");
|
||||
big_display.set(frequency_list[current_index]); //After showing an error
|
||||
big_display.set(current_frequency); //After showing an error
|
||||
}
|
||||
else {
|
||||
scanner_file.append(freq_file_path); //Second: append if it is not there
|
||||
@@ -370,7 +438,7 @@ ScannerView::ScannerView(
|
||||
} else
|
||||
{
|
||||
nav_.display_modal("Error", "Cannot open " + loaded_file_name + ".TXT\nfor appending freq.");
|
||||
big_display.set(frequency_list[current_index]); //After showing an error
|
||||
big_display.set(current_frequency); //After showing an error
|
||||
}
|
||||
};
|
||||
|
||||
@@ -398,7 +466,7 @@ void ScannerView::frequency_file_load(std::string file_name, bool stop_all_befor
|
||||
if ( load_freqman_file(file_name, database) ) {
|
||||
loaded_file_name = file_name; // keep loaded filename in memory
|
||||
for(auto& entry : database) { // READ LINE PER LINE
|
||||
if (frequency_list.size() < MAX_DB_ENTRY) { //We got space!
|
||||
if (frequency_list.size() < FREQMAN_MAX_PER_FILE ) { //We got space!
|
||||
if (entry.type == RANGE) { //RANGE
|
||||
switch (entry.step) {
|
||||
case AM_US: def_step = 10000; break ;
|
||||
@@ -421,7 +489,7 @@ void ScannerView::frequency_file_load(std::string file_name, bool stop_all_befor
|
||||
+ ">" + to_string_short_freq(entry.frequency_b)
|
||||
+ " S" + to_string_short_freq(def_step).erase(0,1) //euquiq: lame kludge to reduce spacing in step freq
|
||||
);
|
||||
while (frequency_list.size() < MAX_DB_ENTRY && entry.frequency_a <= entry.frequency_b) { //add the rest of the range
|
||||
while (frequency_list.size() < FREQMAN_MAX_PER_FILE && entry.frequency_a <= entry.frequency_b) { //add the rest of the range
|
||||
entry.frequency_a+=def_step;
|
||||
frequency_list.push_back(entry.frequency_a);
|
||||
description_list.push_back(""); //Token (keep showing the last description)
|
||||
@@ -516,9 +584,9 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
|
||||
|
||||
switch (new_mod) {
|
||||
case NFM: //bw 16k (2) default
|
||||
bw.emplace_back("8k5", 0);
|
||||
bw.emplace_back("11k", 0);
|
||||
bw.emplace_back("16k", 0);
|
||||
bw.emplace_back("8k5 ", 0);
|
||||
bw.emplace_back("11k ", 0);
|
||||
bw.emplace_back("16k ", 0);
|
||||
field_bw.set_options(bw);
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
|
||||
@@ -529,10 +597,12 @@ size_t ScannerView::change_mode(uint8_t new_mod) { //Before this, do a scan_thre
|
||||
receiver_model.set_sampling_rate(3072000); receiver_model.set_baseband_bandwidth(1750000);
|
||||
break;
|
||||
case AM:
|
||||
bw.emplace_back("DSB", 0);
|
||||
bw.emplace_back("USB", 0);
|
||||
bw.emplace_back("LSB", 0);
|
||||
bw.emplace_back("CW ", 0);
|
||||
bw.emplace_back("DSB 9k" , 0 );
|
||||
bw.emplace_back("DSB 6k" , 1 );
|
||||
bw.emplace_back("USB+3k" , 2 );
|
||||
bw.emplace_back("LSB-3k" , 3 );
|
||||
bw.emplace_back("CW " , 4 );
|
||||
|
||||
field_bw.set_options(bw);
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
|
||||
@@ -540,10 +610,10 @@ 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);
|
||||
bw.emplace_back("200k ", 0);
|
||||
field_bw.set_options(bw);
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
|
||||
@@ -564,4 +634,10 @@ void ScannerView::start_scan_thread() {
|
||||
scan_thread = std::make_unique<ScannerThread>(frequency_list);
|
||||
}
|
||||
|
||||
void ScannerView::start_scan_thread(const jammer::jammer_range_t& frequency_range, size_t def_step) {
|
||||
receiver_model.enable();
|
||||
receiver_model.set_squelch_level(0);
|
||||
scan_thread = std::make_unique<ScannerThread>(frequency_range, def_step);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include "file.hpp"
|
||||
|
||||
|
||||
#define MAX_DB_ENTRY 500
|
||||
#define MAX_FREQ_LOCK 10 //50ms cycles scanner locks into freq when signal detected, to verify signal is not spureous
|
||||
|
||||
namespace ui {
|
||||
@@ -47,6 +46,7 @@ size_t const mod_step[3] = {9000, 100000, 12500 };
|
||||
class ScannerThread {
|
||||
public:
|
||||
ScannerThread(std::vector<rf::Frequency> frequency_list);
|
||||
ScannerThread(const jammer::jammer_range_t& frequency_range, size_t def_step);
|
||||
~ScannerThread();
|
||||
|
||||
void set_scanning(const bool v);
|
||||
@@ -68,6 +68,8 @@ public:
|
||||
|
||||
private:
|
||||
std::vector<rf::Frequency> frequency_list_ { };
|
||||
jammer::jammer_range_t frequency_range_ {false, 0, 0};
|
||||
size_t def_step_ { 0 };
|
||||
Thread* thread { nullptr };
|
||||
|
||||
bool _scanning { true };
|
||||
@@ -76,6 +78,7 @@ private:
|
||||
uint32_t _freq_del { 0 };
|
||||
static msg_t static_fn(void* arg);
|
||||
void run();
|
||||
void create_thread();
|
||||
};
|
||||
|
||||
class ScannerView : public View {
|
||||
@@ -121,6 +124,7 @@ private:
|
||||
NavigationView& nav_;
|
||||
|
||||
void start_scan_thread();
|
||||
void start_scan_thread(const jammer::jammer_range_t& frequency_range, size_t def_step);
|
||||
size_t change_mode(uint8_t mod_type);
|
||||
void show_max();
|
||||
void scan_pause();
|
||||
@@ -130,7 +134,7 @@ private:
|
||||
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
void on_headphone_volume_changed(int32_t v);
|
||||
void handle_retune(uint32_t i);
|
||||
void handle_retune(int64_t freq, uint32_t freq_idx);
|
||||
|
||||
jammer::jammer_range_t frequency_range { false, 0, 0 }; //perfect for manual scan task too...
|
||||
int32_t squelch { 0 };
|
||||
@@ -140,11 +144,12 @@ private:
|
||||
freqman_db database { };
|
||||
std::string loaded_file_name;
|
||||
uint32_t current_index { 0 };
|
||||
rf::Frequency current_frequency { 0 };
|
||||
bool userpause { false };
|
||||
|
||||
Labels labels {
|
||||
{ { 0 * 8, 0 * 16 }, "LNA: VGA: AMP: VOL:", Color::light_grey() },
|
||||
{ { 0 * 8, 1* 16 }, "BW: SQUELCH: db WAIT:", Color::light_grey() },
|
||||
{ { 0 * 8, 1* 16 }, "BW: SQLCH: db WAIT:", Color::light_grey() },
|
||||
{ { 3 * 8, 10 * 16 }, "START END MANUAL", Color::light_grey() },
|
||||
{ { 0 * 8, (26 * 8) + 4 }, "MODE:", Color::light_grey() },
|
||||
{ { 11 * 8, (26 * 8) + 4 }, "STEP:", Color::light_grey() },
|
||||
@@ -177,7 +182,7 @@ private:
|
||||
};
|
||||
|
||||
NumberField field_squelch {
|
||||
{ 15 * 8, 1 * 16 },
|
||||
{ 18 * 8, 1 * 16 },
|
||||
3,
|
||||
{ -90, 20 },
|
||||
1,
|
||||
@@ -185,7 +190,7 @@ private:
|
||||
};
|
||||
|
||||
NumberField field_wait {
|
||||
{ 26 * 8, 1 * 16 },
|
||||
{ 27 * 8, 1 * 16 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
@@ -296,7 +301,7 @@ private:
|
||||
Message::ID::Retune,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const RetuneMessage*>(p);
|
||||
this->handle_retune(message.range);
|
||||
this->handle_retune(message.freq, message.range);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -245,13 +245,9 @@ SetUIView::SetUIView(NavigationView& nav) {
|
||||
checkbox_showclock.set_value(!persistent_memory::hide_clock());
|
||||
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);
|
||||
@@ -261,11 +257,11 @@ SetUIView::SetUIView(NavigationView& nav) {
|
||||
|
||||
|
||||
button_save.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);
|
||||
|
||||
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);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ff.h"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -210,6 +211,8 @@ private:
|
||||
SetFrequencyCorrectionModel form_collect();
|
||||
};
|
||||
|
||||
using portapack::persistent_memory::backlight_timeout_t;
|
||||
|
||||
class SetUIView : public View {
|
||||
public:
|
||||
SetUIView(NavigationView& nav);
|
||||
@@ -241,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 },
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
@@ -93,7 +94,8 @@ SSTVTXView::~SSTVTXView() {
|
||||
settings.save("tx_sstv", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void SSTVTXView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace ui {
|
||||
TouchCalibrationView::TouchCalibrationView(
|
||||
NavigationView& nav
|
||||
) : nav { nav },
|
||||
calibration { touch::default_calibration() }
|
||||
calibration { touch::Calibration() }
|
||||
{
|
||||
add_children({
|
||||
&image_calibrate_0,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
|
||||
using namespace portapack;
|
||||
@@ -39,7 +40,8 @@ void TouchTunesView::focus() {
|
||||
|
||||
TouchTunesView::~TouchTunesView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
}
|
||||
|
||||
void TouchTunesView::stop_tx() {
|
||||
|
||||
@@ -168,8 +168,8 @@ void speaker_mute() {
|
||||
|
||||
namespace input {
|
||||
|
||||
void start() {
|
||||
audio_codec->microphone_enable();
|
||||
void start(int8_t alc_mode) {
|
||||
audio_codec->microphone_enable(alc_mode); // added user-GUI selection for AK4951, ALC mode parameter.
|
||||
i2s::i2s0::rx_start();
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
virtual volume_range_t headphone_gain_range() const = 0;
|
||||
virtual void set_headphone_volume(const volume_t volume) = 0;
|
||||
|
||||
virtual void microphone_enable() = 0;
|
||||
virtual void microphone_enable(int8_t alc_mode) = 0; // added user-GUI AK4951 ,selected ALC mode.
|
||||
virtual void microphone_disable() = 0;
|
||||
|
||||
virtual size_t reg_count() const = 0;
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
|
||||
namespace output {
|
||||
|
||||
void start();
|
||||
void start(); // this other start(),no changed. ,in namespace output , used to config audio playback mode,
|
||||
void stop();
|
||||
|
||||
void mute();
|
||||
@@ -72,7 +72,7 @@ void speaker_unmute();
|
||||
|
||||
namespace input {
|
||||
|
||||
void start();
|
||||
void start(int8_t alc_mode); // added parameter user-GUI select AK4951-ALC mode for config mic path,(recording mode in datasheet),
|
||||
void stop();
|
||||
|
||||
} /* namespace input */
|
||||
|
||||
@@ -45,11 +45,11 @@ static void send_message(const Message* const message) {
|
||||
|
||||
void AMConfig::apply() const {
|
||||
const AMConfigureMessage message {
|
||||
taps_6k0_decim_0,
|
||||
taps_6k0_decim_1,
|
||||
taps_6k0_decim_2,
|
||||
channel,
|
||||
modulation,
|
||||
taps_6k0_decim_0, // common FIR filter taps pre-decim_0 to all 5 x AM mod types.(AM-9K, AM-6K, USB, LSB, CW)
|
||||
taps_6k0_decim_1, // common FIR filter taps pre-decim_1 to all 5 x AM mod. types.
|
||||
decim_2, // var decim_2 FIR taps filter , variable values, depending selected AM mod(AM 9k / 6k all rest AM modes)
|
||||
channel, // var channel FIR taps filter , variable values, depending selected AM mode, each one different (DSB-9K, DSB-6K, USB-3K, LSB-3K,CW)
|
||||
modulation, // var parameter .
|
||||
audio_12k_hpf_300hz_config
|
||||
};
|
||||
send_message(&message);
|
||||
@@ -146,7 +146,7 @@ void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_
|
||||
};
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
|
||||
void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) {
|
||||
const NRFRxConfigureMessage message {
|
||||
baudrate,
|
||||
@@ -156,7 +156,7 @@ void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t
|
||||
};
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
|
||||
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
|
||||
const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count) {
|
||||
const AFSKTxConfigureMessage message {
|
||||
@@ -183,12 +183,13 @@ void kill_afsk() {
|
||||
}
|
||||
|
||||
void set_audiotx_config(const uint32_t divider, const float deviation_hz, const float audio_gain,
|
||||
const uint32_t tone_key_delta, const bool am_enabled, const bool dsb_enabled,
|
||||
const bool usb_enabled, const bool lsb_enabled) {
|
||||
uint8_t audio_shift_bits_s16, const uint32_t tone_key_delta, const bool am_enabled,
|
||||
const bool dsb_enabled, const bool usb_enabled, const bool lsb_enabled) {
|
||||
const AudioTXConfigMessage message {
|
||||
divider,
|
||||
deviation_hz,
|
||||
audio_gain,
|
||||
audio_shift_bits_s16,
|
||||
tone_key_delta,
|
||||
(float)persistent_memory::tone_mix() / 100.0f,
|
||||
am_enabled,
|
||||
@@ -211,16 +212,28 @@ void set_pitch_rssi(int32_t avg, bool enabled) {
|
||||
enabled,
|
||||
avg
|
||||
};
|
||||
send_message(&message);
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat,
|
||||
const uint32_t pause_symbols) {
|
||||
const uint32_t pause_symbols, const uint8_t de_bruijn_length) {
|
||||
const OOKConfigureMessage message {
|
||||
stream_length,
|
||||
samples_per_bit,
|
||||
repeat,
|
||||
pause_symbols
|
||||
pause_symbols,
|
||||
de_bruijn_length
|
||||
};
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void kill_ook() {
|
||||
const OOKConfigureMessage message {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
send_message(&message);
|
||||
}
|
||||
@@ -250,7 +263,7 @@ void set_adsb() {
|
||||
|
||||
void set_jammer(const bool run, const jammer::JammerType type, const uint32_t speed) {
|
||||
const JammerConfigureMessage message {
|
||||
run,
|
||||
run,
|
||||
type,
|
||||
speed
|
||||
};
|
||||
@@ -294,7 +307,7 @@ void run_image(const portapack::spi_flash::image_tag_t image_tag) {
|
||||
|
||||
creg::m4txevent::clear();
|
||||
|
||||
m4_init(image_tag, portapack::memory::map::m4_code);
|
||||
m4_init(image_tag, portapack::memory::map::m4_code, false);
|
||||
baseband_image_running = true;
|
||||
|
||||
creg::m4txevent::enable();
|
||||
@@ -311,7 +324,7 @@ void shutdown() {
|
||||
send_message(&message);
|
||||
|
||||
shared_memory.application_queue.reset();
|
||||
|
||||
|
||||
baseband_image_running = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
namespace baseband {
|
||||
|
||||
struct AMConfig {
|
||||
const fir_taps_real<32> decim_2; // added to handle two types decim_2 9k, 6k
|
||||
const fir_taps_complex<64> channel;
|
||||
const AMConfigureMessage::Modulation modulation;
|
||||
|
||||
@@ -61,8 +62,8 @@ void set_tones_config(const uint32_t bw, const uint32_t pre_silence, const uint1
|
||||
void kill_tone();
|
||||
void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration);
|
||||
void set_audiotx_config(const uint32_t divider, const float deviation_hz, const float audio_gain,
|
||||
const uint32_t tone_key_delta, const bool am_enabled, const bool dsb_enabled,
|
||||
const bool usb_enabled, const bool lsb_enabled);
|
||||
uint8_t audio_shift_bits_s16, const uint32_t tone_key_delta, const bool am_enabled,
|
||||
const bool dsb_enabled, const bool usb_enabled, const bool lsb_enabled);
|
||||
void set_fifo_data(const int8_t * data);
|
||||
void set_pitch_rssi(int32_t avg, bool enabled);
|
||||
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
|
||||
@@ -76,7 +77,8 @@ void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_
|
||||
void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
|
||||
|
||||
void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat,
|
||||
const uint32_t pause_symbols);
|
||||
const uint32_t pause_symbols, const uint8_t de_bruijn_length = 0);
|
||||
void kill_ook();
|
||||
void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift,
|
||||
const uint32_t progress_notice);
|
||||
void set_pocsag();
|
||||
|
||||
@@ -41,12 +41,18 @@ constexpr si5351::Inputs si5351_inputs {
|
||||
static_assert(si5351_inputs.f_xtal == si5351_xtal_f, "XTAL output frequency wrong");
|
||||
static_assert(si5351_inputs.f_clkin_out() == si5351_clkin_f, "CLKIN output frequency wrong");
|
||||
|
||||
constexpr si5351::PLLInputSource::Type si5351_pll_input_sources {
|
||||
constexpr si5351::PLLInputSource::Type si5351c_pll_input_sources {
|
||||
si5351::PLLInputSource::PLLA_Source_XTAL
|
||||
| si5351::PLLInputSource::PLLB_Source_CLKIN
|
||||
| si5351::PLLInputSource::CLKIN_Div1
|
||||
};
|
||||
|
||||
constexpr si5351::PLLInputSource::Type si5351a_pll_input_sources {
|
||||
si5351::PLLInputSource::PLLA_Source_XTAL
|
||||
| si5351::PLLInputSource::PLLB_Source_XTAL
|
||||
| si5351::PLLInputSource::CLKIN_Div1
|
||||
};
|
||||
|
||||
constexpr si5351::PLL si5351_pll_xtal_25m {
|
||||
.f_in = si5351_inputs.f_xtal,
|
||||
.a = 32,
|
||||
@@ -61,7 +67,8 @@ constexpr si5351::PLL si5351_pll_clkin_10m {
|
||||
.b = 0,
|
||||
.c = 1,
|
||||
};
|
||||
constexpr auto si5351_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1);
|
||||
constexpr auto si5351c_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1);
|
||||
constexpr auto si5351a_pll_a_clkin_reg = si5351_pll_clkin_10m.reg(0);
|
||||
|
||||
static_assert(si5351_pll_xtal_25m.f_vco() == si5351_vco_f, "PLL XTAL frequency wrong");
|
||||
static_assert(si5351_pll_xtal_25m.p1() == 3584, "PLL XTAL P1 wrong");
|
||||
@@ -98,7 +105,7 @@ constexpr si5351::MultisynthFractional si5351_ms_0_8m {
|
||||
.c = 1,
|
||||
.r_div = 1,
|
||||
};
|
||||
constexpr auto si5351_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_codec);
|
||||
constexpr auto si5351c_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_og_codec);
|
||||
|
||||
constexpr si5351::MultisynthFractional si5351_ms_group {
|
||||
.f_src = si5351_vco_f,
|
||||
@@ -107,8 +114,17 @@ constexpr si5351::MultisynthFractional si5351_ms_group {
|
||||
.c = 1,
|
||||
.r_div = 0,
|
||||
};
|
||||
constexpr auto si5351_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_cpld);
|
||||
constexpr auto si5351_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_sgpio);
|
||||
constexpr auto si5351c_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_og_cpld);
|
||||
constexpr auto si5351c_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_og_sgpio);
|
||||
|
||||
constexpr si5351::MultisynthFractional si5351_ms_16m {
|
||||
.f_src = si5351_vco_f,
|
||||
.a = 50,
|
||||
.b = 0,
|
||||
.c = 1,
|
||||
.r_div = 0,
|
||||
};
|
||||
constexpr auto si5351a_ms_1_sgpio_16m_reg = si5351_ms_16m.reg(clock_generator_output_r9_sgpio);
|
||||
|
||||
constexpr si5351::MultisynthFractional si5351_ms_10m {
|
||||
.f_src = si5351_vco_f,
|
||||
@@ -117,7 +133,8 @@ constexpr si5351::MultisynthFractional si5351_ms_10m {
|
||||
.c = 1,
|
||||
.r_div = 0,
|
||||
};
|
||||
constexpr auto si5351_ms_3_10m_reg = si5351_ms_10m.reg(3);
|
||||
constexpr auto si5351c_ms_3_10m_reg = si5351_ms_10m.reg(3);
|
||||
constexpr auto si5351a_ms_2_mcu_10m_reg = si5351_ms_10m.reg(clock_generator_output_r9_mcu_clkin);
|
||||
|
||||
constexpr si5351::MultisynthFractional si5351_ms_40m {
|
||||
.f_src = si5351_vco_f,
|
||||
@@ -128,10 +145,11 @@ constexpr si5351::MultisynthFractional si5351_ms_40m {
|
||||
};
|
||||
|
||||
constexpr auto si5351_ms_rffc5072 = si5351_ms_40m;
|
||||
constexpr auto si5351_ms_max2837 = si5351_ms_40m;
|
||||
constexpr auto si5351_ms_max283x = si5351_ms_40m;
|
||||
|
||||
constexpr auto si5351_ms_4_reg = si5351_ms_rffc5072.reg(clock_generator_output_first_if);
|
||||
constexpr auto si5351_ms_5_reg = si5351_ms_max2837.reg(clock_generator_output_second_if);
|
||||
constexpr auto si5351c_ms_4_reg = si5351_ms_rffc5072.reg(clock_generator_output_og_first_if);
|
||||
constexpr auto si5351c_ms_5_reg = si5351_ms_max283x.reg(clock_generator_output_og_second_if);
|
||||
constexpr auto si5351a_ms_0_if_40m_reg = si5351_ms_40m.reg(clock_generator_output_r9_if);
|
||||
|
||||
static_assert(si5351_ms_10m.f_out() == 10000000, "MS 10MHz f_out wrong");
|
||||
static_assert(si5351_ms_10m.p1() == 9728, "MS 10MHz p1 wrong");
|
||||
@@ -139,14 +157,8 @@ static_assert(si5351_ms_10m.p2() == 0, "MS 10MHz p2 wrong");
|
||||
static_assert(si5351_ms_10m.p3() == 1, "MS 10MHz p3 wrong");
|
||||
|
||||
static_assert(si5351_ms_rffc5072.f_out() == rffc5072_reference_f, "RFFC5072 reference f_out wrong");
|
||||
// static_assert(si5351_ms_50m.p1() == 2048, "MS 50MHz P1 wrong");
|
||||
// static_assert(si5351_ms_50m.p2() == 0, "MS 50MHz P2 wrong");
|
||||
// static_assert(si5351_ms_50m.p3() == 1, "MS 50MHz P3 wrong");
|
||||
|
||||
static_assert(si5351_ms_max2837.f_out() == max2837_reference_f, "MAX2837 reference f_out wrong");
|
||||
// static_assert(si5351_ms_50m.p1() == 2048, "MS 40MHz P1 wrong");
|
||||
// static_assert(si5351_ms_50m.p2() == 0, "MS 40MHz P2 wrong");
|
||||
// static_assert(si5351_ms_50m.p3() == 1, "MS 40MHz P3 wrong");
|
||||
static_assert(si5351_ms_max283x.f_out() == max283x_reference_f, "MAX283x reference f_out wrong");
|
||||
|
||||
constexpr si5351::MultisynthInteger si5351_ms_int_off {
|
||||
.f_src = si5351_vco_f,
|
||||
@@ -154,40 +166,60 @@ constexpr si5351::MultisynthInteger si5351_ms_int_off {
|
||||
.r_div = 0,
|
||||
};
|
||||
|
||||
constexpr si5351::MultisynthInteger si5351_ms_int_mcu_clkin {
|
||||
constexpr si5351::MultisynthInteger si5351_ms_int_40m {
|
||||
.f_src = si5351_vco_f,
|
||||
.a = 20,
|
||||
.r_div = 0,
|
||||
};
|
||||
|
||||
constexpr auto si5351_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_mcu_clkin);
|
||||
constexpr si5351::MultisynthInteger si5351_ms_int_10m {
|
||||
.f_src = si5351_vco_f,
|
||||
.a = 80,
|
||||
.r_div = 0,
|
||||
};
|
||||
|
||||
constexpr auto si5351c_ms_int_mcu_clkin = si5351_ms_int_40m;
|
||||
constexpr auto si5351a_ms_int_mcu_clkin = si5351_ms_int_10m;
|
||||
|
||||
constexpr auto si5351c_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351c_ms_int_mcu_clkin);
|
||||
constexpr auto si5351a_ms6_7_off_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_off);
|
||||
|
||||
static_assert(si5351_ms_int_off.f_out() == 3137254, "MS int off f_out wrong");
|
||||
static_assert(si5351_ms_int_off.p1() == 255, "MS int off P1 wrong");
|
||||
|
||||
static_assert(si5351_ms_int_mcu_clkin.f_out() == mcu_clkin_f, "MS int MCU CLKIN f_out wrong");
|
||||
// static_assert(si5351_ms_int_mcu_clkin.p1() == 20, "MS int MCU CLKIN P1 wrong");
|
||||
static_assert(si5351c_ms_int_mcu_clkin.f_out() == mcu_clkin_og_f, "MS int MCU CLKIN OG f_out wrong");
|
||||
static_assert(si5351a_ms_int_mcu_clkin.f_out() == mcu_clkin_r9_f, "MS int MCU CLKIN r9 f_out wrong");
|
||||
|
||||
using namespace si5351;
|
||||
|
||||
static constexpr ClockControl::MultiSynthSource get_reference_clock_generator_pll(const ClockManager::ReferenceSource reference_source) {
|
||||
static constexpr ClockControl::MultiSynthSource get_si5351c_reference_clock_generator_pll(const ClockManager::ReferenceSource reference_source) {
|
||||
return (reference_source == ClockManager::ReferenceSource::Xtal)
|
||||
? ClockControl::MultiSynthSource::PLLA
|
||||
: ClockControl::MultiSynthSource::PLLB
|
||||
;
|
||||
}
|
||||
|
||||
constexpr ClockControls si5351_clock_control_common { {
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
constexpr ClockControls si5351c_clock_control_common { {
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Group, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Invert, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, get_si5351c_reference_clock_generator_pll(ClockManager::ReferenceSource::Xtal), ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
} };
|
||||
|
||||
constexpr ClockControls si5351a_clock_control_common { {
|
||||
{ ClockControl::ClockCurrentDrive::_6mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_4mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Fractional, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_8mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
{ ClockControl::ClockCurrentDrive::_2mA, ClockControl::ClockSource::MS_Self, ClockControl::ClockInvert::Normal, ClockControl::MultiSynthSource::PLLA, ClockControl::MultiSynthMode::Integer, ClockControl::ClockPowerDown::Power_Off },
|
||||
} };
|
||||
ClockManager::Reference ClockManager::get_reference() const {
|
||||
return reference;
|
||||
}
|
||||
@@ -212,11 +244,25 @@ void ClockManager::init_clock_generator() {
|
||||
clock_generator.reset();
|
||||
clock_generator.set_crystal_internal_load_capacitance(CrystalInternalLoadCapacitance::XTAL_CL_8pF);
|
||||
clock_generator.enable_fanout();
|
||||
clock_generator.set_pll_input_sources(si5351_pll_input_sources);
|
||||
clock_generator.set_pll_input_sources(hackrf_r9
|
||||
? si5351a_pll_input_sources
|
||||
: si5351c_pll_input_sources);
|
||||
|
||||
auto si5351_clock_control_common = hackrf_r9
|
||||
? si5351a_clock_control_common
|
||||
: si5351c_clock_control_common;
|
||||
|
||||
auto clock_generator_output_mcu_clkin = hackrf_r9
|
||||
? clock_generator_output_r9_mcu_clkin
|
||||
: clock_generator_output_og_mcu_clkin;
|
||||
|
||||
clock_generator.set_clock_control(
|
||||
clock_generator_output_mcu_clkin,
|
||||
si5351_clock_control_common[clock_generator_output_mcu_clkin].clk_src(ClockControl::ClockSource::CLKIN).clk_pdn(ClockControl::ClockPowerDown::Power_On)
|
||||
si5351_clock_control_common[clock_generator_output_mcu_clkin]
|
||||
.clk_src(hackrf_r9
|
||||
? ClockControl::ClockSource::Xtal
|
||||
: ClockControl::ClockSource::CLKIN)
|
||||
.clk_pdn(ClockControl::ClockPowerDown::Power_On)
|
||||
);
|
||||
clock_generator.enable_output(clock_generator_output_mcu_clkin);
|
||||
|
||||
@@ -224,7 +270,10 @@ void ClockManager::init_clock_generator() {
|
||||
|
||||
clock_generator.disable_output(clock_generator_output_mcu_clkin);
|
||||
|
||||
const auto ref_pll = get_reference_clock_generator_pll(reference.source);
|
||||
const auto ref_pll = hackrf_r9
|
||||
? ClockControl::MultiSynthSource::PLLA
|
||||
: get_si5351c_reference_clock_generator_pll(reference.source);
|
||||
|
||||
const ClockControls si5351_clock_control = ClockControls { {
|
||||
si5351_clock_control_common[0].ms_src(ref_pll),
|
||||
si5351_clock_control_common[1].ms_src(ref_pll),
|
||||
@@ -237,21 +286,35 @@ void ClockManager::init_clock_generator() {
|
||||
} };
|
||||
clock_generator.set_clock_control(si5351_clock_control);
|
||||
|
||||
clock_generator.write(si5351_pll_a_xtal_reg);
|
||||
clock_generator.write(si5351_pll_b_clkin_reg);
|
||||
clock_generator.write(si5351_ms_0_8m_reg);
|
||||
clock_generator.write(si5351_ms_1_group_reg);
|
||||
clock_generator.write(si5351_ms_2_group_reg);
|
||||
clock_generator.write(si5351_ms_3_10m_reg);
|
||||
clock_generator.write(si5351_ms_4_reg);
|
||||
clock_generator.write(si5351_ms_5_reg);
|
||||
clock_generator.write(si5351_ms6_7_off_mcu_clkin_reg);
|
||||
if (hackrf_r9) {
|
||||
const PLLReg pll_reg = (reference.source == ReferenceSource::Xtal)
|
||||
? si5351_pll_a_xtal_reg
|
||||
: si5351a_pll_a_clkin_reg;
|
||||
clock_generator.write(pll_reg);
|
||||
clock_generator.write(si5351a_ms_0_if_40m_reg);
|
||||
clock_generator.write(si5351a_ms_1_sgpio_16m_reg);
|
||||
clock_generator.write(si5351a_ms_2_mcu_10m_reg);
|
||||
clock_generator.write(si5351a_ms6_7_off_reg);
|
||||
} else {
|
||||
clock_generator.write(si5351_pll_a_xtal_reg);
|
||||
clock_generator.write(si5351c_pll_b_clkin_reg);
|
||||
clock_generator.write(si5351c_ms_0_8m_reg);
|
||||
clock_generator.write(si5351c_ms_1_group_reg);
|
||||
clock_generator.write(si5351c_ms_2_group_reg);
|
||||
clock_generator.write(si5351c_ms_3_10m_reg);
|
||||
clock_generator.write(si5351c_ms_4_reg);
|
||||
clock_generator.write(si5351c_ms_5_reg);
|
||||
clock_generator.write(si5351c_ms6_7_off_mcu_clkin_reg);
|
||||
}
|
||||
|
||||
clock_generator.reset_plls();
|
||||
|
||||
// Wait for both PLLs to lock.
|
||||
// TODO: Disable the unused PLL?
|
||||
const uint8_t device_status_mask = (ref_pll == ClockControl::MultiSynthSource::PLLB) ? 0x40 : 0x20;
|
||||
// Wait for PLL(s) to lock.
|
||||
uint8_t device_status_mask = hackrf_r9
|
||||
? 0x20
|
||||
: (ref_pll == ClockControl::MultiSynthSource::PLLB)
|
||||
? 0x40
|
||||
: 0x20;
|
||||
while((clock_generator.device_status() & device_status_mask) != 0);
|
||||
|
||||
clock_generator.set_clock_control(
|
||||
@@ -268,12 +331,18 @@ uint32_t ClockManager::measure_gp_clkin_frequency() {
|
||||
return get_frequency_monitor_measurement_in_hertz();
|
||||
}
|
||||
|
||||
bool ClockManager::loss_of_signal() {
|
||||
return hackrf_r9
|
||||
? clock_generator.plla_loss_of_signal()
|
||||
: clock_generator.clkin_loss_of_signal();
|
||||
}
|
||||
|
||||
ClockManager::ReferenceSource ClockManager::detect_reference_source() {
|
||||
if( clock_generator.clkin_loss_of_signal() ) {
|
||||
if(loss_of_signal()) {
|
||||
// No external reference. Turn on PortaPack reference (if present).
|
||||
portapack_tcxo_enable();
|
||||
|
||||
if( clock_generator.clkin_loss_of_signal() ) {
|
||||
if(loss_of_signal()) {
|
||||
// No PortaPack reference was detected. Choose the HackRF crystal as the reference.
|
||||
return ReferenceSource::Xtal;
|
||||
} else {
|
||||
@@ -285,6 +354,11 @@ ClockManager::ReferenceSource ClockManager::detect_reference_source() {
|
||||
}
|
||||
|
||||
ClockManager::Reference ClockManager::choose_reference() {
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_clkin_en.write(1);
|
||||
volatile uint32_t delay = 240000 + 24000;
|
||||
while(delay--);
|
||||
}
|
||||
const auto detected_reference = detect_reference_source();
|
||||
|
||||
if( (detected_reference == ReferenceSource::External) ||
|
||||
@@ -296,8 +370,12 @@ ClockManager::Reference ClockManager::choose_reference() {
|
||||
}
|
||||
}
|
||||
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_clkin_en.write(0);
|
||||
}
|
||||
|
||||
portapack_tcxo_disable();
|
||||
return { ReferenceSource::Xtal, 10000000 };
|
||||
return { ReferenceSource::Xtal, 25000000 };
|
||||
}
|
||||
|
||||
void ClockManager::shutdown() {
|
||||
@@ -305,18 +383,26 @@ void ClockManager::shutdown() {
|
||||
}
|
||||
|
||||
void ClockManager::enable_codec_clocks() {
|
||||
clock_generator.enable_clock(clock_generator_output_codec);
|
||||
clock_generator.enable_clock(clock_generator_output_cpld);
|
||||
clock_generator.enable_clock(clock_generator_output_sgpio);
|
||||
if (hackrf_r9) {
|
||||
clock_generator.enable_clock(clock_generator_output_r9_sgpio);
|
||||
} else {
|
||||
clock_generator.enable_clock(clock_generator_output_og_codec);
|
||||
clock_generator.enable_clock(clock_generator_output_og_cpld);
|
||||
clock_generator.enable_clock(clock_generator_output_og_sgpio);
|
||||
}
|
||||
/* Turn on all outputs at the same time. This probably doesn't ensure
|
||||
* their phase relationships. For example, clocks that output frequencies
|
||||
* in a 2:1 relationship may start with the slower clock high or low?
|
||||
*/
|
||||
clock_generator.enable_output_mask(
|
||||
(1U << clock_generator_output_codec)
|
||||
| (1U << clock_generator_output_cpld)
|
||||
| (1U << clock_generator_output_sgpio)
|
||||
);
|
||||
if (hackrf_r9) {
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_r9_sgpio);
|
||||
} else {
|
||||
clock_generator.enable_output_mask(
|
||||
(1U << clock_generator_output_og_codec)
|
||||
| (1U << clock_generator_output_og_cpld)
|
||||
| (1U << clock_generator_output_og_sgpio)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::disable_codec_clocks() {
|
||||
@@ -324,34 +410,45 @@ void ClockManager::disable_codec_clocks() {
|
||||
* be enabled for the output to come to rest at the state specified by
|
||||
* CLKx_DISABLE_STATE.
|
||||
*/
|
||||
clock_generator.disable_output_mask(
|
||||
(1U << clock_generator_output_codec)
|
||||
| (1U << clock_generator_output_cpld)
|
||||
| (1U << clock_generator_output_sgpio)
|
||||
);
|
||||
clock_generator.disable_clock(clock_generator_output_codec);
|
||||
clock_generator.disable_clock(clock_generator_output_cpld);
|
||||
clock_generator.disable_clock(clock_generator_output_sgpio);
|
||||
if (hackrf_r9) {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_r9_sgpio);
|
||||
clock_generator.disable_clock(clock_generator_output_r9_sgpio);
|
||||
} else {
|
||||
clock_generator.disable_output_mask(
|
||||
(1U << clock_generator_output_og_codec)
|
||||
| (1U << clock_generator_output_og_cpld)
|
||||
| (1U << clock_generator_output_og_sgpio)
|
||||
);
|
||||
clock_generator.disable_clock(clock_generator_output_og_codec);
|
||||
clock_generator.disable_clock(clock_generator_output_og_cpld);
|
||||
clock_generator.disable_clock(clock_generator_output_og_sgpio);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ClockManager::enable_first_if_clock() {
|
||||
clock_generator.enable_clock(clock_generator_output_first_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_first_if);
|
||||
void ClockManager::enable_if_clocks() {
|
||||
if (hackrf_r9) {
|
||||
clock_generator.enable_clock(clock_generator_output_r9_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_r9_if);
|
||||
} else {
|
||||
clock_generator.enable_clock(clock_generator_output_og_first_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_og_first_if);
|
||||
clock_generator.enable_clock(clock_generator_output_og_second_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_og_second_if);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::disable_first_if_clock() {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_first_if);
|
||||
clock_generator.disable_clock(clock_generator_output_first_if);
|
||||
}
|
||||
|
||||
void ClockManager::enable_second_if_clock() {
|
||||
clock_generator.enable_clock(clock_generator_output_second_if);
|
||||
clock_generator.enable_output_mask(1U << clock_generator_output_second_if);
|
||||
}
|
||||
|
||||
void ClockManager::disable_second_if_clock() {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_second_if);
|
||||
clock_generator.disable_clock(clock_generator_output_second_if);
|
||||
void ClockManager::disable_if_clocks() {
|
||||
if (hackrf_r9) {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_r9_if);
|
||||
clock_generator.disable_clock(clock_generator_output_r9_if);
|
||||
} else {
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_og_first_if);
|
||||
clock_generator.disable_clock(clock_generator_output_og_first_if);
|
||||
clock_generator.disable_output_mask(1U << clock_generator_output_og_second_if);
|
||||
clock_generator.disable_clock(clock_generator_output_og_second_if);
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::set_sampling_frequency(const uint32_t frequency) {
|
||||
@@ -360,14 +457,21 @@ void ClockManager::set_sampling_frequency(const uint32_t frequency) {
|
||||
* necessary to change the MS0 synth frequency, and ensure the output
|
||||
* is divided by two.
|
||||
*/
|
||||
clock_generator.set_ms_frequency(clock_generator_output_codec, frequency * 2, si5351_vco_f, 1);
|
||||
if (hackrf_r9) {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_r9_sgpio, frequency * 2, si5351_vco_f, 0);
|
||||
} else {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_og_codec, frequency * 2, si5351_vco_f, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::set_reference_ppb(const int32_t ppb) {
|
||||
/* NOTE: This adjustment only affects PLLA, which is derived from the 25MHz crystal.
|
||||
* It is assumed an external clock coming in to PLLB is sufficiently accurate as to not need adjustment.
|
||||
/* NOTE: This adjustment only affects PLLA when it is derived from the 25MHz crystal.
|
||||
* It is assumed an external clock coming in to CLKIN/PLLB is sufficiently accurate as to not need adjustment.
|
||||
* TODO: Revisit the above policy. It may be good to allow adjustment of the external reference too.
|
||||
*/
|
||||
if (hackrf_r9 && reference.source != ReferenceSource::Xtal) {
|
||||
return;
|
||||
}
|
||||
constexpr uint32_t pll_multiplier = si5351_pll_xtal_25m.a;
|
||||
constexpr uint32_t denominator = 1000000 / pll_multiplier;
|
||||
const uint32_t new_a = (ppb >= 0) ? pll_multiplier : (pll_multiplier - 1);
|
||||
@@ -425,11 +529,13 @@ void ClockManager::start_audio_pll() {
|
||||
|
||||
/* For 40MHz clock source, 48kHz audio rate, 256Fs MCLK:
|
||||
* Fout=12.288MHz, Fcco=491.52MHz
|
||||
* PSEL=20, NSEL=125, MSEL=768
|
||||
* OG: PSEL=20, NSEL=125, MSEL=768
|
||||
* PDEC=31, NDEC=45, MDEC=30542
|
||||
* r9: PSEL=20, NSEL=125, MSEL=3072
|
||||
* PDEC=31, NDEC=45, MDEC=8308
|
||||
*/
|
||||
cgu::pll0audio::mdiv({
|
||||
.mdec = 30542,
|
||||
.mdec = hackrf_r9 ? 8308UL : 30542UL,
|
||||
});
|
||||
cgu::pll0audio::np_div({
|
||||
.pdec = 31,
|
||||
@@ -467,18 +573,25 @@ void ClockManager::stop_audio_pll() {
|
||||
|
||||
void ClockManager::enable_clock_output(bool enable) {
|
||||
if(enable) {
|
||||
clock_generator.enable_output(clock_generator_output_clkout);
|
||||
clock_generator.enable_output(clock_generator_output_og_clkout);
|
||||
if(portapack::persistent_memory::clkout_freq() < 1000) {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
|
||||
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
|
||||
} else {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
|
||||
clock_generator.set_ms_frequency(clock_generator_output_og_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
|
||||
}
|
||||
} else {
|
||||
clock_generator.disable_output(clock_generator_output_clkout);
|
||||
clock_generator.disable_output(clock_generator_output_og_clkout);
|
||||
}
|
||||
|
||||
auto si5351_clock_control_common = hackrf_r9
|
||||
? si5351a_clock_control_common
|
||||
: si5351c_clock_control_common;
|
||||
const auto ref_pll = hackrf_r9
|
||||
? ClockControl::MultiSynthSource::PLLA
|
||||
: get_si5351c_reference_clock_generator_pll(reference.source);
|
||||
|
||||
if(enable)
|
||||
clock_generator.set_clock_control(clock_generator_output_clkout, si5351_clock_control_common[clock_generator_output_clkout].ms_src(get_reference_clock_generator_pll(reference.source)).clk_pdn(ClockControl::ClockPowerDown::Power_On));
|
||||
clock_generator.set_clock_control(clock_generator_output_og_clkout, si5351_clock_control_common[clock_generator_output_og_clkout].ms_src(ref_pll).clk_pdn(ClockControl::ClockPowerDown::Power_On));
|
||||
else
|
||||
clock_generator.set_clock_control(clock_generator_output_clkout, ClockControl::power_off());
|
||||
clock_generator.set_clock_control(clock_generator_output_og_clkout, ClockControl::power_off());
|
||||
}
|
||||
|
||||
@@ -65,11 +65,8 @@ public:
|
||||
void enable_codec_clocks();
|
||||
void disable_codec_clocks();
|
||||
|
||||
void enable_first_if_clock();
|
||||
void disable_first_if_clock();
|
||||
|
||||
void enable_second_if_clock();
|
||||
void disable_second_if_clock();
|
||||
void enable_if_clocks();
|
||||
void disable_if_clocks();
|
||||
|
||||
void set_sampling_frequency(const uint32_t frequency);
|
||||
|
||||
@@ -99,6 +96,7 @@ private:
|
||||
|
||||
ReferenceSource detect_reference_source();
|
||||
Reference choose_reference();
|
||||
bool loss_of_signal();
|
||||
};
|
||||
|
||||
#endif/*__CLOCK_MANAGER_H__*/
|
||||
|
||||
@@ -38,7 +38,7 @@ using namespace lpc43xx;
|
||||
* I suppose I could force M4MEMMAP to an invalid memory reason which would
|
||||
* cause an exception and effectively halt the M4. But that feels gross.
|
||||
*/
|
||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to) {
|
||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset) {
|
||||
const portapack::spi_flash::chunk_t* chunk = reinterpret_cast<const portapack::spi_flash::chunk_t*>(portapack::spi_flash::images.base());
|
||||
while(chunk->tag) {
|
||||
if( chunk->tag == image_tag ) {
|
||||
@@ -50,8 +50,11 @@ void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack:
|
||||
*/
|
||||
LPC_CREG->M4MEMMAP = to.base();
|
||||
|
||||
/* Reset M4 core */
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 13);
|
||||
/* Reset M4 core and optionally all peripherals */
|
||||
LPC_RGU->RESET_CTRL[0] = (full_reset) ?
|
||||
(1 << 1) // PERIPH_RST
|
||||
: (1 << 13) // M4_RST
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "memory_map.hpp"
|
||||
#include "spi_image.hpp"
|
||||
|
||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to);
|
||||
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to, const bool full_reset);
|
||||
void m4_request_shutdown();
|
||||
|
||||
void m0_halt();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "../../hackrf/firmware/common/firmware_info.h"
|
||||
#include "../../hackrf/firmware/common/platform_detect.h"
|
||||
|
||||
#define SUPPORTED_PLATFORM (PLATFORM_HACKRF1_OG | PLATFORM_HACKRF1_R9)
|
||||
#define DFU_MODE_VALUE 0
|
||||
|
||||
__attribute__((section(".firmware_info"))) const struct firmware_info_t firmware_info = {
|
||||
.magic = "HACKRFFW",
|
||||
.struct_version = 1,
|
||||
.dfu_mode = DFU_MODE_VALUE,
|
||||
.supported_platform = SUPPORTED_PLATFORM,
|
||||
.version_string = VERSION_STRING,
|
||||
};
|
||||
+474
-148
@@ -23,166 +23,492 @@
|
||||
#include "freqman.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
|
||||
options_t freqman_entry_modulations = {
|
||||
{ "AM", 0 },
|
||||
{ "NFM", 1 },
|
||||
{ "WFM", 2 }
|
||||
};
|
||||
|
||||
options_t freqman_entry_bandwidths[ 4 ] = {
|
||||
{ //AM
|
||||
{ "DSB 9k" , 0 },
|
||||
{ "DSB 6k" , 1 },
|
||||
{ "USB+3k" , 2 },
|
||||
{ "LSB-3k" , 3 },
|
||||
{ "CW" , 4 }
|
||||
},
|
||||
{ //NFM
|
||||
{ "8k5" , 0 },
|
||||
{ "11k" , 1 },
|
||||
{ "16k" , 2 }
|
||||
},
|
||||
{ //WFM
|
||||
{ "200k" , 0 },
|
||||
}
|
||||
};
|
||||
|
||||
options_t freqman_entry_steps = {
|
||||
{ "5KHz (SA AM)" , 5000 },
|
||||
{ "6.25KHz(NFM)" , 6250 },
|
||||
{ "8.33KHz(AIR)" , 8330 },
|
||||
{ "9KHz (EU AM)" , 9000 },
|
||||
{ "10KHz(US AM)" , 10000 },
|
||||
{ "12.5KHz(NFM)" , 12500 },
|
||||
{ "15KHz (HFM)" , 15000 },
|
||||
{ "25KHz (N1)" , 25000 },
|
||||
{ "30KHz (OIRT)" , 30000 },
|
||||
{ "50KHz (FM1)" , 50000 },
|
||||
{ "100KHz (FM2)" , 100000 },
|
||||
{ "250KHz (N2)" , 250000 },
|
||||
{ "500KHz (WFM)" , 500000 },
|
||||
{ "1MHz " , 1000000 }
|
||||
};
|
||||
|
||||
options_t freqman_entry_steps_short = {
|
||||
{ "5KHz" , 5000 },
|
||||
{ "6.25KHz" , 6250 },
|
||||
{ "8.33KHz" , 8330 },
|
||||
{ "9KHz" , 9000 },
|
||||
{ "10KHz" , 10000 },
|
||||
{ "12.5KHz" , 12500 },
|
||||
{ "15KHz" , 15000 },
|
||||
{ "25KHz" , 25000 },
|
||||
{ "30KHz" , 30000 },
|
||||
{ "50KHz" , 50000 },
|
||||
{ "100KHz" , 100000 },
|
||||
{ "250KHz" , 250000 },
|
||||
{ "500KHz" , 500000 },
|
||||
{ "1MHz" , 1000000 }
|
||||
};
|
||||
|
||||
std::vector<std::string> get_freqman_files() {
|
||||
std::vector<std::string> file_list;
|
||||
|
||||
auto files = scan_root_files(u"FREQMAN", u"*.TXT");
|
||||
|
||||
for (auto file : files) {
|
||||
std::string file_name = file.stem().string();
|
||||
// don't propose tmp / hidden files in freqman's list
|
||||
if (file_name.length() && file_name[0] != '.') {
|
||||
file_list.emplace_back(file_name);
|
||||
}
|
||||
}
|
||||
|
||||
return file_list;
|
||||
std::vector<std::string> file_list;
|
||||
|
||||
auto files = scan_root_files(u"FREQMAN", u"*.TXT");
|
||||
|
||||
for (auto file : files) {
|
||||
std::string file_name = file.stem().string();
|
||||
// don't propose tmp / hidden files in freqman's list
|
||||
if (file_name.length() && file_name[0] != '.') {
|
||||
file_list.emplace_back(file_name);
|
||||
}
|
||||
}
|
||||
|
||||
return file_list;
|
||||
};
|
||||
|
||||
bool load_freqman_file(std::string& file_stem, freqman_db &db) {
|
||||
File freqman_file;
|
||||
size_t length, n = 0, file_position = 0;
|
||||
char * pos;
|
||||
char * line_start;
|
||||
char * line_end;
|
||||
std::string description;
|
||||
rf::Frequency frequency_a, frequency_b;
|
||||
char file_data[257];
|
||||
freqman_entry_type type;
|
||||
|
||||
db.clear();
|
||||
|
||||
auto result = freqman_file.open("FREQMAN/" + file_stem + ".TXT");
|
||||
if (result.is_valid())
|
||||
return false;
|
||||
|
||||
while (1) {
|
||||
// Read a 256 bytes block from file
|
||||
freqman_file.seek(file_position);
|
||||
|
||||
memset(file_data, 0, 257);
|
||||
auto read_size = freqman_file.read(file_data, 256);
|
||||
if (read_size.is_error())
|
||||
return false; // Read error
|
||||
|
||||
file_position += 256;
|
||||
|
||||
// Reset line_start to beginning of buffer
|
||||
line_start = file_data;
|
||||
|
||||
if (!strstr(file_data, "f=") && !strstr(file_data, "a="))
|
||||
break;
|
||||
|
||||
// Look for complete lines in buffer
|
||||
while ((line_end = strstr(line_start, "\x0A"))) {
|
||||
// Read frequency
|
||||
pos = strstr(line_start, "f=");
|
||||
frequency_b = 0;
|
||||
type = SINGLE;
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
frequency_a = strtoll(pos, nullptr, 10);
|
||||
} else {
|
||||
// ...or range
|
||||
pos = strstr(line_start, "a=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
frequency_a = strtoll(pos, nullptr, 10);
|
||||
type = RANGE;
|
||||
pos = strstr(line_start, "b=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
frequency_b = strtoll(pos, nullptr, 10);
|
||||
} else
|
||||
frequency_b = 0;
|
||||
} else
|
||||
frequency_a = 0;
|
||||
}
|
||||
|
||||
// Read description until , or LF
|
||||
pos = strstr(line_start, "d=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
length = std::min(strcspn(pos, ",\x0A"), (size_t)FREQMAN_DESC_MAX_LEN);
|
||||
description = string(pos, length);
|
||||
} else
|
||||
description = "-";
|
||||
|
||||
db.push_back({ frequency_a, frequency_b, description, type });
|
||||
n++;
|
||||
|
||||
if (n >= FREQMAN_MAX_PER_FILE) return true;
|
||||
|
||||
line_start = line_end + 1;
|
||||
if (line_start - file_data >= 256) break;
|
||||
}
|
||||
|
||||
if (read_size.value() != 256)
|
||||
break; // End of file
|
||||
|
||||
// Restart at beginning of last incomplete line
|
||||
file_position -= (file_data + 256 - line_start);
|
||||
}
|
||||
|
||||
return true;
|
||||
return load_freqman_file_ex( file_stem , db , true , true , true );
|
||||
}
|
||||
|
||||
bool save_freqman_file(std::string& file_stem, freqman_db &db) {
|
||||
File freqman_file;
|
||||
std::string item_string;
|
||||
rf::Frequency frequency_a, frequency_b;
|
||||
|
||||
if (!create_freqman_file(file_stem, freqman_file))
|
||||
return false;
|
||||
|
||||
for (size_t n = 0; n < db.size(); n++) {
|
||||
auto& entry = db[n];
|
||||
bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freqs , bool load_ranges , bool load_hamradios ) {
|
||||
File freqman_file;
|
||||
size_t length, n = 0, file_position = 0;
|
||||
char * pos;
|
||||
char * line_start;
|
||||
char * line_end;
|
||||
std::string description;
|
||||
rf::Frequency frequency_a, frequency_b;
|
||||
char file_data[257];
|
||||
freqman_entry_type type;
|
||||
freqman_index_t modulation = 0 ;
|
||||
freqman_index_t bandwidth = 0 ;
|
||||
freqman_index_t step = 0 ;
|
||||
freqman_index_t tone = 0 ;
|
||||
|
||||
frequency_a = entry.frequency_a;
|
||||
|
||||
if (entry.type == SINGLE) {
|
||||
// Single
|
||||
|
||||
// TODO: Make to_string_dec_uint be able to return uint64_t's
|
||||
// Please forgive me...
|
||||
item_string = "f=" + to_string_dec_uint(frequency_a / 1000) + to_string_dec_uint(frequency_a % 1000UL, 3, '0');
|
||||
|
||||
} else {
|
||||
// Range
|
||||
frequency_b = entry.frequency_b;
|
||||
|
||||
item_string = "a=" + to_string_dec_uint(frequency_a / 1000) + to_string_dec_uint(frequency_a % 1000UL, 3, '0');
|
||||
item_string += ",b=" + to_string_dec_uint(frequency_b / 1000) + to_string_dec_uint(frequency_b % 1000UL, 3, '0');
|
||||
}
|
||||
|
||||
if (entry.description.size())
|
||||
item_string += ",d=" + entry.description;
|
||||
|
||||
freqman_file.write_line(item_string);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
db.clear();
|
||||
|
||||
auto result = freqman_file.open("FREQMAN/" + file_stem + ".TXT");
|
||||
if (result.is_valid())
|
||||
return false;
|
||||
|
||||
while (1) {
|
||||
// Read a 256 bytes block from file
|
||||
freqman_file.seek(file_position);
|
||||
|
||||
memset(file_data, 0, 257);
|
||||
auto read_size = freqman_file.read(file_data, 256);
|
||||
if (read_size.is_error())
|
||||
return false; // Read error
|
||||
|
||||
file_position += 256;
|
||||
|
||||
// Reset line_start to beginning of buffer
|
||||
line_start = file_data;
|
||||
|
||||
if (!strstr(file_data, "f=") && !strstr(file_data, "a=") && !strstr(file_data, "r=") )
|
||||
break;
|
||||
|
||||
// Look for complete lines in buffer
|
||||
while ((line_end = strstr(line_start, "\x0A"))) {
|
||||
|
||||
modulation = -1 ;
|
||||
bandwidth = -1 ;
|
||||
step = -1 ;
|
||||
tone = -1 ;
|
||||
type=SINGLE ;
|
||||
|
||||
frequency_a = frequency_b = 0;
|
||||
// Read frequency
|
||||
pos = strstr(line_start, "f=");
|
||||
if(pos) {
|
||||
pos += 2;
|
||||
frequency_a = strtoll(pos, nullptr, 10);
|
||||
} else {
|
||||
// ...or range
|
||||
pos = strstr(line_start, "a=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
frequency_a = strtoll(pos, nullptr, 10);
|
||||
type = RANGE;
|
||||
pos = strstr(line_start, "b=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
frequency_b = strtoll(pos, nullptr, 10);
|
||||
} else
|
||||
frequency_b = 0;
|
||||
}else {
|
||||
// ... or hamradio
|
||||
pos = strstr(line_start, "r=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
frequency_a = strtoll(pos, nullptr, 10);
|
||||
type = HAMRADIO;
|
||||
pos = strstr(line_start, "t=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
frequency_b = strtoll(pos, nullptr, 10);
|
||||
} else
|
||||
frequency_b = frequency_a ;
|
||||
} else
|
||||
frequency_a = 0;
|
||||
}
|
||||
}
|
||||
// modulation if any
|
||||
pos = strstr(line_start, "m=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
modulation = freqman_entry_get_modulation_from_str( pos );
|
||||
}
|
||||
// bandwidth if any
|
||||
pos = strstr(line_start, "bw=");
|
||||
if (pos) {
|
||||
pos += 3;
|
||||
bandwidth = freqman_entry_get_bandwidth_from_str( modulation , pos );
|
||||
}
|
||||
// step if any
|
||||
pos = strstr(line_start, "s=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
step = freqman_entry_get_step_from_str_short( pos );
|
||||
}
|
||||
// ctcss tone if any
|
||||
/* disabled until better form
|
||||
pos = strstr(line_start, "c=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
tone = tone_key_index_by_value( strtoll( pos , nullptr , 10 ) );
|
||||
} */
|
||||
// Read description until , or LF
|
||||
pos = strstr(line_start, "d=");
|
||||
if (pos) {
|
||||
pos += 2;
|
||||
length = std::min(strcspn(pos, ",\x0A"), (size_t)FREQMAN_DESC_MAX_LEN);
|
||||
description = string(pos, length);
|
||||
} else
|
||||
description = "-";
|
||||
if( (type == SINGLE && load_freqs) || (type == RANGE && load_ranges) || (type == HAMRADIO && load_hamradios) )
|
||||
{
|
||||
db.push_back({ frequency_a, frequency_b, description, type , modulation , bandwidth , step , tone });
|
||||
n++;
|
||||
if (n >= FREQMAN_MAX_PER_FILE) return true;
|
||||
}
|
||||
|
||||
line_start = line_end + 1;
|
||||
if (line_start - file_data >= 256) break;
|
||||
}
|
||||
|
||||
if (read_size.value() != 256)
|
||||
break; // End of file
|
||||
|
||||
// Restart at beginning of last incomplete line
|
||||
file_position -= (file_data + 256 - line_start);
|
||||
}
|
||||
|
||||
/* populate implicitly specified modulation / bandwidth */
|
||||
if( db.size() > 2 )
|
||||
{
|
||||
modulation = db[ 0 ] . modulation;
|
||||
bandwidth = db[ 0 ] . bandwidth;
|
||||
|
||||
for( unsigned int it = 1 ; it < db.size() ; it ++ )
|
||||
{
|
||||
if( db[ it ] . modulation < 0 )
|
||||
{
|
||||
db[ it ] . modulation = modulation ;
|
||||
}
|
||||
else
|
||||
{
|
||||
modulation = db[ it ] . modulation ;
|
||||
}
|
||||
if( db[ it ] . bandwidth < 0 )
|
||||
{
|
||||
db[ it ] . bandwidth = bandwidth ;
|
||||
}
|
||||
else
|
||||
{
|
||||
modulation = db[ it ] . bandwidth ;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool get_freq_string( freqman_entry &entry , std::string &item_string )
|
||||
{
|
||||
rf::Frequency frequency_a, frequency_b;
|
||||
|
||||
frequency_a = entry.frequency_a;
|
||||
if (entry.type == SINGLE) {
|
||||
// Single
|
||||
item_string = "f=" + to_string_dec_uint(frequency_a / 1000) + to_string_dec_uint(frequency_a % 1000UL, 3, '0');
|
||||
} else if( entry.type == RANGE ) {
|
||||
// Range
|
||||
frequency_b = entry.frequency_b;
|
||||
item_string = "a=" + to_string_dec_uint(frequency_a / 1000) + to_string_dec_uint(frequency_a % 1000UL, 3, '0');
|
||||
item_string += ",b=" + to_string_dec_uint(frequency_b / 1000) + to_string_dec_uint(frequency_b % 1000UL, 3, '0');
|
||||
if( entry.step >= 0 )
|
||||
{
|
||||
item_string += ",s=" + freqman_entry_get_step_string_short( entry.step );
|
||||
}
|
||||
} else if( entry.type == HAMRADIO ) {
|
||||
frequency_b = entry.frequency_b;
|
||||
item_string = "r=" + to_string_dec_uint(frequency_a / 1000) + to_string_dec_uint(frequency_a % 1000UL, 3, '0');
|
||||
item_string += ",t=" + to_string_dec_uint(frequency_b / 1000) + to_string_dec_uint(frequency_b % 1000UL, 3, '0');
|
||||
if( entry.tone >= 0 )
|
||||
{
|
||||
item_string += ",c=" + tone_key_string( entry.tone );
|
||||
}
|
||||
}
|
||||
if( entry.modulation >= 0 && (unsigned)entry.modulation < freqman_entry_modulations . size() )
|
||||
{
|
||||
item_string += ",m=" + freqman_entry_get_modulation_string( entry.modulation );
|
||||
if( entry.bandwidth >= 0 && (unsigned)entry.bandwidth < freqman_entry_bandwidths[ entry.modulation ] . size() )
|
||||
{
|
||||
item_string += ",bw=" + freqman_entry_get_bandwidth_string( entry.modulation , entry.bandwidth );
|
||||
}
|
||||
}
|
||||
if (entry.description.size())
|
||||
item_string += ",d=" + entry.description;
|
||||
|
||||
return true ;
|
||||
}
|
||||
|
||||
bool save_freqman_file(std::string &file_stem, freqman_db &db) {
|
||||
|
||||
File freqman_file;
|
||||
|
||||
std::string freq_file_path = "FREQMAN/" + file_stem + ".TXT";
|
||||
std::string tmp_freq_file_path = "FREQMAN/" + file_stem + ".TXT.TMP";
|
||||
|
||||
if( !db.size() )
|
||||
{
|
||||
delete_file( "FREQMAN/"+file_stem+".TXT" );
|
||||
return true ;
|
||||
}
|
||||
|
||||
delete_file( tmp_freq_file_path );
|
||||
auto result = freqman_file.open( tmp_freq_file_path );
|
||||
if ( !result.is_valid() ) {
|
||||
for (size_t n = 0; n < db.size(); n++) {
|
||||
std::string item_string;
|
||||
auto& entry = db[n];
|
||||
get_freq_string( entry , item_string );
|
||||
freqman_file.write_line( item_string );
|
||||
delete &item_string;
|
||||
}
|
||||
delete_file( freq_file_path );
|
||||
rename_file( tmp_freq_file_path , freq_file_path );
|
||||
return true;
|
||||
}
|
||||
return false ;
|
||||
}
|
||||
|
||||
bool create_freqman_file(std::string& file_stem, File& freqman_file) {
|
||||
auto result = freqman_file.create("FREQMAN/" + file_stem + ".TXT");
|
||||
if (result.is_valid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
auto result = freqman_file.create( "FREQMAN/" + file_stem + ".TXT" );
|
||||
|
||||
if (result.is_valid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string freqman_item_string(freqman_entry &entry, size_t max_length) {
|
||||
std::string item_string;
|
||||
std::string item_string;
|
||||
|
||||
if (entry.type == SINGLE) {
|
||||
item_string = to_string_short_freq(entry.frequency_a) + "M: " + entry.description;
|
||||
} else {
|
||||
item_string = "Range: " + entry.description;
|
||||
}
|
||||
|
||||
if (item_string.size() > max_length)
|
||||
return item_string.substr(0, max_length - 3) + "...";
|
||||
|
||||
return item_string;
|
||||
switch( entry.type ){
|
||||
case SINGLE:
|
||||
item_string = to_string_short_freq(entry.frequency_a) + "M: " + entry.description;
|
||||
break;
|
||||
case RANGE:
|
||||
item_string = "R: " + entry.description;
|
||||
break;
|
||||
case HAMRADIO:
|
||||
item_string = "H: " + entry.description;
|
||||
break;
|
||||
default:
|
||||
item_string = "!UNKNOW TYPE " + entry.description;
|
||||
break;
|
||||
}
|
||||
|
||||
if (item_string.size() > max_length)
|
||||
return item_string.substr(0, max_length - 3) + "...";
|
||||
|
||||
return item_string;
|
||||
}
|
||||
|
||||
void freqman_set_modulation_option( OptionsField &option )
|
||||
{
|
||||
option.set_options( freqman_entry_modulations );
|
||||
}
|
||||
|
||||
void freqman_set_bandwidth_option( freqman_index_t modulation , OptionsField &option )
|
||||
{
|
||||
option.set_options( freqman_entry_bandwidths[ modulation ] );
|
||||
}
|
||||
|
||||
void freqman_set_step_option( OptionsField &option )
|
||||
{
|
||||
option.set_options( freqman_entry_steps );
|
||||
}
|
||||
|
||||
void freqman_set_step_option_short( OptionsField &option )
|
||||
{
|
||||
option.set_options( freqman_entry_steps_short );
|
||||
}
|
||||
|
||||
std::string freqman_entry_get_modulation_string( freqman_index_t modulation )
|
||||
{
|
||||
if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() )
|
||||
{
|
||||
return std::string( "" ); // unknown modulation
|
||||
}
|
||||
return freqman_entry_modulations[ modulation ] . first ;
|
||||
}
|
||||
|
||||
std::string freqman_entry_get_bandwidth_string( freqman_index_t modulation , freqman_index_t bandwidth )
|
||||
{
|
||||
if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() )
|
||||
{
|
||||
return std::string( "" ); // unknown modulation
|
||||
}
|
||||
if( bandwidth < 0 || (unsigned)bandwidth > freqman_entry_bandwidths[ modulation ] . size() )
|
||||
{
|
||||
return std::string( "" ); // unknown modulation
|
||||
}
|
||||
return freqman_entry_bandwidths[ modulation ][ bandwidth ] . first ;
|
||||
}
|
||||
|
||||
std::string freqman_entry_get_step_string( freqman_index_t step )
|
||||
{
|
||||
if( step < 0 || (unsigned)step >= freqman_entry_steps . size() )
|
||||
{
|
||||
return std::string( "" ); // unknown modulation
|
||||
}
|
||||
return freqman_entry_steps[ step ] . first ;
|
||||
}
|
||||
|
||||
std::string freqman_entry_get_step_string_short( freqman_index_t step )
|
||||
{
|
||||
if( step < 0 || (unsigned)step >= freqman_entry_steps_short . size() )
|
||||
{
|
||||
return std::string( "" ); // unknown modulation
|
||||
}
|
||||
return freqman_entry_steps_short[ step ] . first ;
|
||||
}
|
||||
|
||||
int32_t freqman_entry_get_modulation_value( freqman_index_t modulation )
|
||||
{
|
||||
if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() )
|
||||
{
|
||||
return -1 ; // unknown modulation
|
||||
}
|
||||
return freqman_entry_modulations[ modulation ] . second ;
|
||||
}
|
||||
|
||||
int32_t freqman_entry_get_bandwidth_value( freqman_index_t modulation , freqman_index_t bandwidth )
|
||||
{
|
||||
if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() )
|
||||
{
|
||||
return -1 ; // unknown modulation
|
||||
}
|
||||
if( bandwidth < 0 || (unsigned)bandwidth > freqman_entry_bandwidths[ modulation ] . size() )
|
||||
{
|
||||
return -1 ; // unknown bandwidth for modulation
|
||||
}
|
||||
return freqman_entry_bandwidths[ modulation ][ bandwidth ] . second ;
|
||||
}
|
||||
|
||||
int32_t freqman_entry_get_step_value( freqman_index_t step )
|
||||
{
|
||||
if( step < 0 || (unsigned)step >= freqman_entry_steps . size() )
|
||||
{
|
||||
return -1 ; // unknown modulation
|
||||
}
|
||||
return freqman_entry_steps[ step ] . second ;
|
||||
}
|
||||
|
||||
freqman_index_t freqman_entry_get_modulation_from_str( char *str )
|
||||
{
|
||||
if( !str )
|
||||
return -1 ;
|
||||
for( freqman_index_t index = 0 ; (unsigned)index < freqman_entry_modulations . size() ; index ++ )
|
||||
{
|
||||
if( strncmp( freqman_entry_modulations[ index ] . first . c_str() , str , freqman_entry_modulations[ index ] . first . size() ) == 0 )
|
||||
return index ;
|
||||
}
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
freqman_index_t freqman_entry_get_bandwidth_from_str( freqman_index_t modulation , char *str )
|
||||
{
|
||||
if( !str )
|
||||
return -1 ;
|
||||
if( modulation < 0 || (unsigned)modulation >= freqman_entry_modulations . size() )
|
||||
return -1 ;
|
||||
for( freqman_index_t index = 0 ; (unsigned)index < freqman_entry_bandwidths[ modulation ] . size() ; index ++ )
|
||||
{
|
||||
if( strncmp( freqman_entry_bandwidths[ modulation ][ index ] . first . c_str() , str , freqman_entry_bandwidths[ modulation ][ index ] . first . size() ) == 0 )
|
||||
return index ;
|
||||
}
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
freqman_index_t freqman_entry_get_step_from_str( char *str )
|
||||
{
|
||||
if( !str )
|
||||
return -1 ;
|
||||
for( freqman_index_t index = 0 ; (unsigned)index < freqman_entry_steps . size() ; index ++ )
|
||||
{
|
||||
if( strncmp( freqman_entry_steps[ index ] . first . c_str() , str , freqman_entry_steps[ index ] . first . size() ) == 0 )
|
||||
return index ;
|
||||
}
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
freqman_index_t freqman_entry_get_step_from_str_short( char *str )
|
||||
{
|
||||
if( !str )
|
||||
return -1 ;
|
||||
for( freqman_index_t index = 0 ; (unsigned)index < freqman_entry_steps_short . size() ; index ++ )
|
||||
{
|
||||
if( strncmp( freqman_entry_steps_short[ index ] . first . c_str() , str , freqman_entry_steps_short[ index ] . first . size() ) == 0 )
|
||||
return index ;
|
||||
}
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
@@ -20,21 +20,27 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __FREQMAN_H__
|
||||
#define __FREQMAN_H__
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "file.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "tone_key.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#ifndef __FREQMAN_H__
|
||||
#define __FREQMAN_H__
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#define FREQMAN_DESC_MAX_LEN 30
|
||||
#define FREQMAN_MAX_PER_FILE 99
|
||||
#define FREQMAN_MAX_PER_FILE_STR "99"
|
||||
#define FREQMAN_MAX_PER_FILE 500 /* MAX PER FILES */
|
||||
#define FREQMAN_MAX_PER_FILE_STR "500" /*STRING OF FREQMAN_MAX_PER_FILE */
|
||||
|
||||
using namespace ui;
|
||||
using namespace std;
|
||||
using namespace tonekey;
|
||||
|
||||
// needs to be signed as -1 means not set
|
||||
typedef int8_t freqman_index_t ;
|
||||
|
||||
enum freqman_error {
|
||||
NO_ERROR = 0,
|
||||
@@ -44,13 +50,23 @@ enum freqman_error {
|
||||
};
|
||||
|
||||
enum freqman_entry_type {
|
||||
SINGLE = 0,
|
||||
RANGE
|
||||
SINGLE = 0, //f=
|
||||
RANGE, //a=,b=
|
||||
HAMRADIO, //r=,t=
|
||||
ERROR_TYPE
|
||||
};
|
||||
|
||||
enum freqman_entry_modulation {
|
||||
AM_MODULATION = 0 ,
|
||||
NFM_MODULATION ,
|
||||
WFM_MODULATION ,
|
||||
MODULATION_DEF ,
|
||||
ERROR_MODULATION
|
||||
};
|
||||
|
||||
//Entry step placed for AlainD freqman version (or any other enhanced version)
|
||||
enum freqman_entry_step {
|
||||
STEP_DEF = 0, // default
|
||||
STEP_DEF = -1, // default
|
||||
AM_US, // 10 kHz AM/CB
|
||||
AM_EUR, // 9 kHz LW/MW
|
||||
NFM_1, // 12,5 kHz (Analogic PMR 446)
|
||||
@@ -63,21 +79,46 @@ enum freqman_entry_step {
|
||||
ERROR_STEP
|
||||
};
|
||||
|
||||
// freqman_entry_step step added, as above, to provide compatibility / future enhancement.
|
||||
struct freqman_entry {
|
||||
rf::Frequency frequency_a { 0 };
|
||||
rf::Frequency frequency_b { 0 };
|
||||
std::string description { };
|
||||
freqman_entry_type type { };
|
||||
freqman_entry_step step { };
|
||||
rf::Frequency frequency_a { 0 }; // 'f=freq' or 'a=freq_start' or 'r=recv_freq'
|
||||
rf::Frequency frequency_b { 0 }; // 'b=freq_end' or 't=tx_freq'
|
||||
std::string description { }; // 'd=desc'
|
||||
freqman_entry_type type { }; // SINGLE,RANGE,HAMRADIO
|
||||
freqman_index_t modulation { }; // AM,NFM,WFM
|
||||
freqman_index_t bandwidth { }; // AM_DSB, ...
|
||||
freqman_index_t step { }; // 5Khz (SA AM,...
|
||||
tone_index tone { }; // 0XZ, 11 1ZB,...
|
||||
};
|
||||
|
||||
using freqman_db = std::vector<freqman_entry>;
|
||||
|
||||
std::vector<std::string> get_freqman_files();
|
||||
bool load_freqman_file(std::string& file_stem, freqman_db& db);
|
||||
bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freqs , bool load_ranges , bool load_hamradios );
|
||||
bool get_freq_string( freqman_entry &entry , std::string &item_string );
|
||||
bool save_freqman_file(std::string& file_stem, freqman_db& db);
|
||||
bool create_freqman_file(std::string& file_stem, File& freqman_file);
|
||||
|
||||
std::string freqman_item_string(freqman_entry &item, size_t max_length);
|
||||
|
||||
void freqman_set_bandwidth_option( freqman_index_t modulation , OptionsField &option );
|
||||
void freqman_set_modulation_option( OptionsField &option );
|
||||
void freqman_set_step_option( OptionsField &option );
|
||||
void freqman_set_step_option_short( OptionsField &option );
|
||||
void freqman_set_tone_option( OptionsField &option );
|
||||
|
||||
std::string freqman_entry_get_modulation_string( freqman_index_t modulation );
|
||||
std::string freqman_entry_get_bandwidth_string( freqman_index_t modulation , freqman_index_t bandwidth );
|
||||
std::string freqman_entry_get_step_string( freqman_index_t step );
|
||||
std::string freqman_entry_get_step_string_short( freqman_index_t step );
|
||||
|
||||
int32_t freqman_entry_get_modulation_value( freqman_index_t modulation );
|
||||
int32_t freqman_entry_get_bandwidth_value( freqman_index_t modulation , freqman_index_t bandwidth );
|
||||
int32_t freqman_entry_get_step_value( freqman_index_t step );
|
||||
|
||||
freqman_index_t freqman_entry_get_modulation_from_str( char *str );
|
||||
freqman_index_t freqman_entry_get_bandwidth_from_str( freqman_index_t modulation , char *str );
|
||||
freqman_index_t freqman_entry_get_step_from_str( char *str );
|
||||
freqman_index_t freqman_entry_get_step_from_str_short( char *str );
|
||||
|
||||
#endif/*__FREQMAN_H__*/
|
||||
|
||||
@@ -32,8 +32,12 @@ using namespace hackrf::one;
|
||||
|
||||
namespace max2837 {
|
||||
|
||||
using namespace max283x;
|
||||
|
||||
namespace lna {
|
||||
|
||||
using namespace max283x::lna;
|
||||
|
||||
constexpr std::array<uint8_t, 8> lookup_8db_steps {
|
||||
0b111, 0b011, 0b110, 0b010,
|
||||
0b100, 0b000, 0b000, 0b000
|
||||
@@ -48,6 +52,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
|
||||
namespace vga {
|
||||
|
||||
using namespace max283x::vga;
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return ((db_sat >> 1) & 0b11111) ^ 0b11111;
|
||||
@@ -57,6 +63,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
|
||||
namespace tx {
|
||||
|
||||
using namespace max283x::tx;
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
uint8_t value = db_sat & 0x0f;
|
||||
@@ -69,6 +77,8 @@ static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
|
||||
namespace filter {
|
||||
|
||||
using namespace max283x::filter;
|
||||
|
||||
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
|
||||
/* Determine filter setting that will provide bandwidth greater than or
|
||||
* equal to requested bandwidth.
|
||||
@@ -84,13 +94,13 @@ static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
|
||||
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
|
||||
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
|
||||
|
||||
constexpr uint32_t reference_frequency = max2837_reference_f;
|
||||
constexpr uint32_t reference_frequency = max283x_reference_f;
|
||||
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
|
||||
|
||||
void MAX2837::init() {
|
||||
set_mode(Mode::Shutdown);
|
||||
|
||||
gpio_max2837_enable.output();
|
||||
gpio_max283x_enable.output();
|
||||
gpio_max2837_rxenable.output();
|
||||
gpio_max2837_txenable.output();
|
||||
|
||||
@@ -140,10 +150,30 @@ void MAX2837::init() {
|
||||
set_mode(Mode::Standby);
|
||||
}
|
||||
|
||||
enum class Mask {
|
||||
Enable = 0b001,
|
||||
RxEnable = 0b010,
|
||||
TxEnable = 0b100,
|
||||
Shutdown = 0b000,
|
||||
Standby = Enable,
|
||||
Receive = Enable | RxEnable,
|
||||
Transmit = Enable | TxEnable,
|
||||
};
|
||||
|
||||
Mask mode_mask(const Mode mode) {
|
||||
switch (mode) {
|
||||
case Mode::Standby: return Mask::Standby;
|
||||
case Mode::Receive: return Mask::Receive;
|
||||
case Mode::Transmit: return Mask::Transmit;
|
||||
default: return Mask::Shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
void MAX2837::set_mode(const Mode mode) {
|
||||
gpio_max2837_enable.write(toUType(mode) & toUType(Mode::Mask_Enable));
|
||||
gpio_max2837_rxenable.write(toUType(mode) & toUType(Mode::Mask_RxEnable));
|
||||
gpio_max2837_txenable.write(toUType(mode) & toUType(Mode::Mask_TxEnable));
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable));
|
||||
gpio_max2837_txenable.write(toUType(mask) & toUType(Mask::TxEnable));
|
||||
}
|
||||
|
||||
void MAX2837::flush() {
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#ifndef __MAX2837_H__
|
||||
#define __MAX2837_H__
|
||||
|
||||
#include "max283x.hpp"
|
||||
#include "gpio.hpp"
|
||||
#include "spi_arbiter.hpp"
|
||||
|
||||
@@ -29,99 +30,11 @@
|
||||
#include <array>
|
||||
|
||||
#include "dirty_registers.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace max2837 {
|
||||
|
||||
enum class Mode {
|
||||
Mask_Enable = 0b001,
|
||||
Mask_RxEnable = 0b010,
|
||||
Mask_TxEnable = 0b100,
|
||||
|
||||
Shutdown = 0b000,
|
||||
Standby = Mask_Enable,
|
||||
Receive = Mask_Enable | Mask_RxEnable,
|
||||
Transmit = Mask_Enable | Mask_TxEnable,
|
||||
};
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace lo {
|
||||
|
||||
constexpr std::array<rf::FrequencyRange, 4> band { {
|
||||
{ 2300000000, 2400000000 },
|
||||
{ 2400000000, 2500000000 },
|
||||
{ 2500000000, 2600000000 },
|
||||
{ 2600000000, 2700000000 },
|
||||
} };
|
||||
|
||||
} /* namespace lo */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace lna {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 40 };
|
||||
constexpr int8_t gain_db_step = 8;
|
||||
|
||||
constexpr std::array<rf::FrequencyRange, 2> band { {
|
||||
{ 2300000000, 2500000000 },
|
||||
{ 2500000000, 2700000000 },
|
||||
} };
|
||||
|
||||
} /* namespace lna */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace vga {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 62 };
|
||||
constexpr int8_t gain_db_step = 2;
|
||||
|
||||
} /* namespace vga */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace tx {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 47 };
|
||||
constexpr int8_t gain_db_step = 1;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace filter {
|
||||
|
||||
constexpr std::array<uint32_t, 16> bandwidths {
|
||||
/* Assumption: these values are in ascending order */
|
||||
1750000,
|
||||
2500000, /* Some documentation says 2.25MHz */
|
||||
3500000,
|
||||
5000000,
|
||||
5500000,
|
||||
6000000,
|
||||
7000000,
|
||||
8000000,
|
||||
9000000,
|
||||
10000000,
|
||||
12000000,
|
||||
14000000,
|
||||
15000000,
|
||||
20000000,
|
||||
24000000,
|
||||
28000000,
|
||||
};
|
||||
|
||||
constexpr auto bandwidth_minimum = bandwidths[0];
|
||||
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
|
||||
|
||||
} /* namespace filter */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
using reg_t = uint16_t;
|
||||
using address_t = uint8_t;
|
||||
using namespace max283x;
|
||||
|
||||
constexpr size_t reg_count = 32;
|
||||
|
||||
@@ -826,7 +739,7 @@ constexpr RegisterMap initial_register_values { Register_Type {
|
||||
},
|
||||
} };
|
||||
|
||||
class MAX2837 {
|
||||
class MAX2837 : public MAX283x {
|
||||
public:
|
||||
constexpr MAX2837(
|
||||
spi::arbiter::Target& target
|
||||
@@ -834,13 +747,13 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void init();
|
||||
void set_mode(const Mode mode);
|
||||
void init() override;
|
||||
void set_mode(const Mode mode) override;
|
||||
|
||||
void set_tx_vga_gain(const int_fast8_t db);
|
||||
void set_lna_gain(const int_fast8_t db);
|
||||
void set_vga_gain(const int_fast8_t db);
|
||||
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
|
||||
void set_tx_vga_gain(const int_fast8_t db) override;
|
||||
void set_lna_gain(const int_fast8_t db) override;
|
||||
void set_vga_gain(const int_fast8_t db) override;
|
||||
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override;
|
||||
#if 0
|
||||
void rx_cal() {
|
||||
_map.r.spi_en.EN_SPI = 1;
|
||||
@@ -882,16 +795,16 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
bool set_frequency(const rf::Frequency lo_frequency);
|
||||
bool set_frequency(const rf::Frequency lo_frequency) override;
|
||||
|
||||
void set_rx_lo_iq_calibration(const size_t v);
|
||||
void set_rx_lo_iq_calibration(const size_t v) override;
|
||||
void set_rx_bias_trim(const size_t v);
|
||||
void set_vco_bias(const size_t v);
|
||||
void set_rx_buff_vcm(const size_t v);
|
||||
void set_rx_buff_vcm(const size_t v) override;
|
||||
|
||||
reg_t temp_sense();
|
||||
reg_t temp_sense() override;
|
||||
|
||||
reg_t read(const address_t reg_num);
|
||||
reg_t read(const address_t reg_num) override;
|
||||
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
|
||||
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2023 Great Scott Gadgets
|
||||
*
|
||||
* 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 "max2839.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
using namespace hackrf::one;
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace max2839 {
|
||||
|
||||
namespace lna {
|
||||
|
||||
using namespace max283x::lna;
|
||||
|
||||
constexpr std::array<uint8_t, 8> lookup_8db_steps {
|
||||
0b11, 0b11, 0b10, 0b10,
|
||||
0b01, 0b00, 0b00, 0b00
|
||||
};
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return lna::lookup_8db_steps[(db_sat >> 3) & 7];
|
||||
}
|
||||
|
||||
} /* namespace lna */
|
||||
|
||||
namespace vga {
|
||||
|
||||
using namespace max283x::vga;
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range_internal { 0, 63 };
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range_internal.clip(db);
|
||||
return (db_sat & 0b111111) ^ 0b111111;
|
||||
}
|
||||
|
||||
} /* namespace vga */
|
||||
|
||||
namespace tx {
|
||||
|
||||
using namespace max283x::tx;
|
||||
|
||||
static uint_fast8_t gain_ordinal(const int8_t db) {
|
||||
const auto db_sat = gain_db_range.clip(db);
|
||||
return 47 - db_sat;
|
||||
}
|
||||
|
||||
} /* namespace tx */
|
||||
|
||||
namespace filter {
|
||||
|
||||
using namespace max283x::filter;
|
||||
|
||||
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
|
||||
/* Determine filter setting that will provide bandwidth greater than or
|
||||
* equal to requested bandwidth.
|
||||
*/
|
||||
return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin();
|
||||
}
|
||||
|
||||
} /* namespace filter */
|
||||
|
||||
/* Empirical testing indicates about 25us is necessary to get a valid
|
||||
* temperature sense conversion from the ADC.
|
||||
*/
|
||||
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
|
||||
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
|
||||
|
||||
constexpr uint32_t reference_frequency = max283x_reference_f;
|
||||
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
|
||||
|
||||
static int_fast8_t requested_rx_lna_gain = 0;
|
||||
static int_fast8_t requested_rx_vga_gain = 0;
|
||||
|
||||
void MAX2839::init() {
|
||||
set_mode(Mode::Shutdown);
|
||||
|
||||
gpio_max283x_enable.output();
|
||||
gpio_max2839_rxtx.output();
|
||||
|
||||
_map.r.rxrf_1.MIMOmode = 1; /* enable RXINB */
|
||||
|
||||
_map.r.pa_drv.TXVGA_GAIN_SPI_EN = 1;
|
||||
_map.r.tx_gain.TXVGA_GAIN_SPI = 0x00;
|
||||
|
||||
_map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */
|
||||
|
||||
_map.r.rxrf_2.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */
|
||||
_map.r.lpf_vga_1.L = 0b000;
|
||||
_map.r.lpf_vga_2.L = 0b000;
|
||||
|
||||
_map.r.rx_top_1.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */
|
||||
_map.r.lpf_vga_1.VGA = 0b000000;
|
||||
_map.r.lpf_vga_2.VGA = 0b010101;
|
||||
|
||||
_map.r.lpf_vga_2.BUFF_VCM = 0b11; /* maximum RX output common-mode voltage */
|
||||
|
||||
_map.r.lpf_vga_1.ModeCtrl = 0b01; /* Rx LPF */
|
||||
_map.r.lpf.FT = 0b0000; /* 1.75 MHz LPF */
|
||||
|
||||
_map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */
|
||||
|
||||
_map.r.lo_gen.LOGEN_2GM = 0;
|
||||
|
||||
_map.r.rssi_vga.RSSI_MODE = 1; /* RSSI independent of RXHP */
|
||||
|
||||
/*
|
||||
* There are two LNA band settings, but we only use one of them.
|
||||
* Switching to the other one doesn't make the overall spectrum any
|
||||
* flatter but adds a surprise step in the middle.
|
||||
*/
|
||||
_map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */
|
||||
|
||||
_dirty.set();
|
||||
flush();
|
||||
|
||||
set_mode(Mode::Standby);
|
||||
}
|
||||
|
||||
enum class Mask {
|
||||
Enable = 0b01,
|
||||
RxTx = 0b10,
|
||||
Shutdown = 0b00,
|
||||
Standby = RxTx,
|
||||
Receive = Enable | RxTx,
|
||||
Transmit = Enable,
|
||||
};
|
||||
|
||||
Mask mode_mask(const Mode mode) {
|
||||
switch (mode) {
|
||||
case Mode::Standby: return Mask::Standby;
|
||||
case Mode::Receive: return Mask::Receive;
|
||||
case Mode::Transmit: return Mask::Transmit;
|
||||
default: return Mask::Shutdown;
|
||||
}
|
||||
}
|
||||
|
||||
void MAX2839::set_mode(const Mode mode) {
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx));
|
||||
}
|
||||
|
||||
void MAX2839::flush() {
|
||||
if( _dirty ) {
|
||||
for(size_t n=0; n<reg_count; n++) {
|
||||
if( _dirty[n] ) {
|
||||
write(n, _map.w[n]);
|
||||
}
|
||||
}
|
||||
_dirty.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void MAX2839::flush_one(const Register reg) {
|
||||
const auto reg_num = toUType(reg);
|
||||
write(reg_num, _map.w[reg_num]);
|
||||
_dirty.clear(reg_num);
|
||||
}
|
||||
|
||||
void MAX2839::write(const address_t reg_num, const reg_t value) {
|
||||
uint16_t t = (0U << 15) | (reg_num << 10) | (value & 0x3ffU);
|
||||
_target.transfer(&t, 1);
|
||||
}
|
||||
|
||||
reg_t MAX2839::read(const address_t reg_num) {
|
||||
uint16_t t = (1U << 15) | (reg_num << 10);
|
||||
_target.transfer(&t, 1U);
|
||||
return t & 0x3ffU;
|
||||
}
|
||||
|
||||
void MAX2839::write(const Register reg, const reg_t value) {
|
||||
write(toUType(reg), value);
|
||||
}
|
||||
|
||||
reg_t MAX2839::read(const Register reg) {
|
||||
return read(toUType(reg));
|
||||
}
|
||||
|
||||
void MAX2839::set_tx_vga_gain(const int_fast8_t db) {
|
||||
_map.r.tx_gain.TXVGA_GAIN_SPI = tx::gain_ordinal(db);
|
||||
_dirty[Register::TX_GAIN] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX2839 gain rain ranges differ slightly from MAX2837's but are close
|
||||
* enough that it makes sense to emulate MAX2837 gain ranges for a consistent
|
||||
* user experience.
|
||||
*/
|
||||
void MAX2839::configure_rx_gain() {
|
||||
/* Apply MAX2837 restrictions to requested gain settings. */
|
||||
int_fast8_t lna_gain = lna::gain_db_range.clip(requested_rx_lna_gain);
|
||||
lna_gain &= 0x38;
|
||||
int_fast8_t vga_gain = vga::gain_db_range.clip(requested_rx_vga_gain);
|
||||
vga_gain &= 0x3e;
|
||||
|
||||
/*
|
||||
* MAX2839 has lower full-scale RX output voltage than MAX2837, so we
|
||||
* adjust the VGA (baseband) gain to compensate.
|
||||
*/
|
||||
vga_gain += 3;
|
||||
|
||||
/*
|
||||
* If that adjustment puts VGA gain out of range, use LNA gain to
|
||||
* compensate. MAX2839 VGA gain can be any number from 0 through 63.
|
||||
*/
|
||||
if (vga_gain > 63) {
|
||||
if (lna_gain <= 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain = 63;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MAX2839 lacks max-24 dB (16 dB) and max-40 dB (0 dB) LNA gain
|
||||
* settings, so we use VGA gain to compensate.
|
||||
*/
|
||||
if (lna_gain == 0) {
|
||||
lna_gain = 8;
|
||||
vga_gain = (vga_gain >= 8) ? vga_gain - 8 : 0;
|
||||
}
|
||||
if (lna_gain == 16) {
|
||||
if (vga_gain > 32) {
|
||||
vga_gain -= 8;
|
||||
lna_gain += 8;
|
||||
} else {
|
||||
vga_gain += 8;
|
||||
lna_gain -= 8;
|
||||
}
|
||||
}
|
||||
|
||||
_map.r.lpf_vga_2.L = lna::gain_ordinal(lna_gain);
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
_map.r.lpf_vga_2.VGA = vga::gain_ordinal(vga_gain);
|
||||
_dirty[Register::LPF_VGA_2] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
void MAX2839::set_lna_gain(const int_fast8_t db) {
|
||||
requested_rx_lna_gain = db;
|
||||
configure_rx_gain();
|
||||
}
|
||||
|
||||
void MAX2839::set_vga_gain(const int_fast8_t db) {
|
||||
requested_rx_vga_gain = db;
|
||||
configure_rx_gain();
|
||||
}
|
||||
|
||||
void MAX2839::set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) {
|
||||
_map.r.lpf.FT = filter::bandwidth_ordinal(bandwidth_minimum);
|
||||
_dirty[Register::LPF] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
bool MAX2839::set_frequency(const rf::Frequency lo_frequency) {
|
||||
/* TODO: This is a sad implementation. Refactor. */
|
||||
if( lo::band[0].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b00; /* 2300 - 2399.99MHz */
|
||||
} else if( lo::band[1].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */
|
||||
} else if( lo::band[2].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */
|
||||
} else if( lo::band[3].contains(lo_frequency) ) {
|
||||
_map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
_dirty[Register::SYN_INT_DIV] = 1;
|
||||
|
||||
const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor;
|
||||
|
||||
_map.r.syn_int_div.SYN_INTDIV = div_q20 >> 20;
|
||||
_dirty[Register::SYN_INT_DIV] = 1;
|
||||
_map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff;
|
||||
_dirty[Register::SYN_FR_DIV_2] = 1;
|
||||
/* flush to commit high FRDIV first, as low FRDIV commits the change */
|
||||
flush();
|
||||
|
||||
_map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff);
|
||||
_dirty[Register::SYN_FR_DIV_1] = 1;
|
||||
flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MAX2839::set_rx_lo_iq_calibration(const size_t v) {
|
||||
_map.r.rxrf_2.RX_IQERR_SPI_EN = 1;
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
_map.r.rxrf_1.iqerr_trim = v;
|
||||
_dirty[Register::RXRF_1] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
void MAX2839::set_rx_buff_vcm(const size_t v) {
|
||||
_map.r.lpf_vga_2.BUFF_VCM = v;
|
||||
_dirty[Register::LPF_VGA_2] = 1;
|
||||
flush();
|
||||
}
|
||||
|
||||
reg_t MAX2839::temp_sense() {
|
||||
if( !_map.r.rx_top_2.ts_en ) {
|
||||
_map.r.rx_top_2.ts_en = 1;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
|
||||
chThdSleepMilliseconds(1);
|
||||
}
|
||||
|
||||
_map.r.rx_top_2.ts_adc_trigger = 1;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
|
||||
halPolledDelay(ticks_for_temperature_sense_adc_conversion);
|
||||
|
||||
/*
|
||||
* Things look very similar to MAX2837, so this probably works, but the
|
||||
* MAX2839 data sheet does not describe the TEMP_SENSE register contents.
|
||||
*/
|
||||
const auto value = read(Register::TEMP_SENSE);
|
||||
|
||||
_map.r.rx_top_2.ts_adc_trigger = 0;
|
||||
flush_one(Register::RX_TOP_2);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,688 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2023 Great Scott Gadgets
|
||||
*
|
||||
* 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 __MAX2839_H__
|
||||
#define __MAX2839_H__
|
||||
|
||||
#include "max283x.hpp"
|
||||
#include "gpio.hpp"
|
||||
#include "spi_arbiter.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "dirty_registers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace max2839 {
|
||||
|
||||
using namespace max283x;
|
||||
|
||||
constexpr size_t reg_count = 32;
|
||||
|
||||
enum class Register : address_t {
|
||||
RXENABLE = 0,
|
||||
RXRF_1 = 1,
|
||||
RXRF_2 = 2,
|
||||
RXRF_LPF = 3,
|
||||
LPF = 4,
|
||||
LPF_VGA_1 = 5,
|
||||
LPF_VGA_2 = 6,
|
||||
RSSI_VGA = 7,
|
||||
RX_TOP_1 = 8,
|
||||
RX_TOP_2 = 9,
|
||||
TX_TOP_1 = 10,
|
||||
TEMP_SENSE = 11,
|
||||
HPFSM_1 = 12,
|
||||
HPFSM_2 = 13,
|
||||
HPFSM_3 = 14,
|
||||
HPFSM_4 = 15,
|
||||
SPI_EN = 16,
|
||||
SYN_FR_DIV_1 = 17,
|
||||
SYN_FR_DIV_2 = 18,
|
||||
SYN_INT_DIV = 19,
|
||||
SYN_CFG_1 = 20,
|
||||
SYN_CFG_2 = 21,
|
||||
VAS_CFG = 22,
|
||||
LO_MISC = 23,
|
||||
XTAL_CFG = 24,
|
||||
VCO_CFG = 25,
|
||||
LO_GEN = 26,
|
||||
PA_DRV = 27,
|
||||
PA_DAC = 28,
|
||||
TX_GAIN = 29,
|
||||
TX_LO_IQ = 30,
|
||||
TX_DC_CORR = 31,
|
||||
};
|
||||
|
||||
struct RXENABLE_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RXENABLE_Type) == sizeof(reg_t), "RXENABLE_Type wrong size");
|
||||
|
||||
struct RXRF_1_Type {
|
||||
reg_t LNAband : 1;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t MIMOmode : 1;
|
||||
reg_t iqerr_trim : 5;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size");
|
||||
|
||||
struct RXRF_2_Type {
|
||||
reg_t LNAgain_SPI_EN : 1;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t RX_IQERR_SPI_EN : 1;
|
||||
reg_t RESERVED1 : 7;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RXRF_2_Type) == sizeof(reg_t), "RXRF_2_Type wrong size");
|
||||
|
||||
struct RXRF_LPF_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RXRF_LPF_Type) == sizeof(reg_t), "RXRF_LPF_Type wrong size");
|
||||
|
||||
struct LPF_Type {
|
||||
reg_t RESERVED0 : 2;
|
||||
reg_t dF : 2;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t FT : 4;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LPF_Type) == sizeof(reg_t), "LPF_Type wrong size");
|
||||
|
||||
struct LPF_VGA_1_Type {
|
||||
reg_t L : 2;
|
||||
reg_t VGA : 6;
|
||||
reg_t ModeCtrl : 2;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LPF_VGA_1_Type) == sizeof(reg_t), "LPF_VGA_1_Type wrong size");
|
||||
|
||||
struct LPF_VGA_2_Type {
|
||||
reg_t L : 2;
|
||||
reg_t VGA : 6;
|
||||
reg_t BUFF_VCM : 2;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LPF_VGA_2_Type) == sizeof(reg_t), "LPF_VGA_2_Type wrong size");
|
||||
|
||||
struct RSSI_VGA_Type {
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t RSSI_MUX : 1;
|
||||
reg_t RSSI_MODE : 1;
|
||||
reg_t RESERVED1 : 4;
|
||||
reg_t RXBB_OUT_SEL : 1;
|
||||
reg_t RESERVED2 : 1;
|
||||
reg_t RSSI_INPUT : 1;
|
||||
reg_t RESERVED3 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RSSI_VGA_Type) == sizeof(reg_t), "RSSI_VGA_Type wrong size");
|
||||
|
||||
struct RX_TOP_1_Type {
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t VGAgain_SPI_EN : 1;
|
||||
reg_t LPF_MODE_SEL : 1;
|
||||
reg_t RESERVED1 : 7;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RX_TOP_1_Type) == sizeof(reg_t), "RX_TOP_1_Type wrong size");
|
||||
|
||||
struct RX_TOP_2_Type {
|
||||
reg_t ts_adc_trigger : 1;
|
||||
reg_t ts_en : 1;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t DOUT_DRVH : 1;
|
||||
reg_t DOUT_CSB_SEL : 1;
|
||||
reg_t DOUT_SEL : 3;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(RX_TOP_2_Type) == sizeof(reg_t), "RX_TOP_2_Type wrong size");
|
||||
|
||||
struct TX_TOP_1_Type {
|
||||
reg_t TXCAL_GAIN : 2;
|
||||
reg_t TXCAL_V2I_FILT : 3;
|
||||
reg_t RESERVED1 : 5;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TX_TOP_1_Type) == sizeof(reg_t), "TX_TOP_1_Type wrong size");
|
||||
|
||||
struct TEMP_SENSE_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TEMP_SENSE_Type) == sizeof(reg_t), "TEMP_SENSE_Type wrong size");
|
||||
|
||||
struct HPFSM_1_Type {
|
||||
reg_t HPC_10M : 2;
|
||||
reg_t HPC_10M_GAIN : 2;
|
||||
reg_t HPC_600k : 3;
|
||||
reg_t HPC_600k_GAIN : 3;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(HPFSM_1_Type) == sizeof(reg_t), "HPFSM_1_Type wrong size");
|
||||
|
||||
struct HPFSM_2_Type {
|
||||
reg_t HPC_100k : 2;
|
||||
reg_t HPC_100k_GAIN : 2;
|
||||
reg_t HPC_30k : 2;
|
||||
reg_t HPC_30k_GAIN : 2;
|
||||
reg_t HPC_1k : 2;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(HPFSM_2_Type) == sizeof(reg_t), "HPFSM_2_Type wrong size");
|
||||
|
||||
struct HPFSM_3_Type {
|
||||
reg_t HPC_1k_B7B6 : 2;
|
||||
reg_t HPC_DELAY : 2;
|
||||
reg_t HPC_STOP : 2;
|
||||
reg_t HPC_STOP_M2 : 2;
|
||||
reg_t HPC_RXGAIN_EN : 1;
|
||||
reg_t TXGATE_EN : 1;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(HPFSM_3_Type) == sizeof(reg_t), "HPFSM_3_Type wrong size");
|
||||
|
||||
struct HPFSM_4_Type {
|
||||
reg_t HPC_DIVH : 1;
|
||||
reg_t RESERVED0 : 5;
|
||||
reg_t HPC_SEQ_BYP : 1;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t HPC_MODE : 1;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(HPFSM_4_Type) == sizeof(reg_t), "HPFSM_4_Type wrong size");
|
||||
|
||||
struct SPI_EN_Type {
|
||||
reg_t EN_SPI : 1;
|
||||
reg_t CAL_SPI : 1;
|
||||
reg_t RESERVED0 : 4;
|
||||
reg_t PADAC_SPI_EN : 1;
|
||||
reg_t PADAC_TX_EN : 1;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SPI_EN_Type) == sizeof(reg_t), "SPI_EN_Type wrong size");
|
||||
|
||||
struct SYN_FR_DIV_1_Type {
|
||||
reg_t SYN_FRDIV_9_0 : 10;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_FR_DIV_1_Type) == sizeof(reg_t), "SYN_FR_DIV_1_Type wrong size");
|
||||
|
||||
struct SYN_FR_DIV_2_Type {
|
||||
reg_t SYN_FRDIV_19_10 : 10;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_FR_DIV_2_Type) == sizeof(reg_t), "SYN_FR_DIV_2_Type wrong size");
|
||||
|
||||
struct SYN_INT_DIV_Type {
|
||||
reg_t SYN_INTDIV : 8;
|
||||
reg_t LOGEN_BSW : 2;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_INT_DIV_Type) == sizeof(reg_t), "SYN_INT_DIV_Type wrong size");
|
||||
|
||||
struct SYN_CFG_1_Type {
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t SYN_REF_DIV_RATIO : 2;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t SYN_CLOCKOUT_DRIVE : 1;
|
||||
reg_t RESERVED2 : 4;
|
||||
reg_t RESERVED3 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_CFG_1_Type) == sizeof(reg_t), "SYN_CFG_1_Type wrong size");
|
||||
|
||||
struct SYN_CFG_2_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(SYN_CFG_2_Type) == sizeof(reg_t), "SYN_CFG_2_Type wrong size");
|
||||
|
||||
struct VAS_CFG_Type {
|
||||
reg_t VAS_MODE : 1;
|
||||
reg_t VAS_RELOCK_SEL : 1;
|
||||
reg_t VAS_DIV : 3;
|
||||
reg_t VAS_DLY : 2;
|
||||
reg_t VAS_TRIG_EN : 1;
|
||||
reg_t RESERVED0 : 2;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VAS_CFG_Type) == sizeof(reg_t), "VAS_CFG_Type wrong size");
|
||||
|
||||
struct LO_MISC_Type {
|
||||
reg_t VAS_SPI : 5;
|
||||
reg_t XTAL_BIAS_SEL : 2;
|
||||
reg_t RESERVED0 : 3;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LO_MISC_Type) == sizeof(reg_t), "LO_MISC_Type wrong size");
|
||||
|
||||
struct XTAL_CFG_Type {
|
||||
reg_t XTAL_FTUNE : 7;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t XTAL_CLKOUT_DIV : 1;
|
||||
reg_t XTAL_CORE_EN : 1;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(XTAL_CFG_Type) == sizeof(reg_t), "XTAL_CFG_Type wrong size");
|
||||
|
||||
struct VCO_CFG_Type {
|
||||
reg_t RESERVED0 : 10;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VCO_CFG_Type) == sizeof(reg_t), "VCO_CFG_Type wrong size");
|
||||
|
||||
struct LO_GEN_Type {
|
||||
reg_t RESERVED0 : 3;
|
||||
reg_t LOGEN_2GM : 1;
|
||||
reg_t RESERVED1 : 2;
|
||||
reg_t VAS_TST : 4;
|
||||
reg_t RESERVED2 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LO_GEN_Type) == sizeof(reg_t), "LO_GEN_Type wrong size");
|
||||
|
||||
struct PA_DRV_Type {
|
||||
reg_t TXLO_IQ_SPI : 6;
|
||||
reg_t TXLO_IQ_SPI_EN : 1;
|
||||
reg_t TXVGA_GAIN_SPI_EN : 1;
|
||||
reg_t TX_DCCORR_SPI_EN : 1;
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PA_DRV_Type) == sizeof(reg_t), "PA_DRV_Type wrong size");
|
||||
|
||||
struct PA_DAC_Type {
|
||||
reg_t PADAC_BIAS : 6;
|
||||
reg_t PADAC_DLY : 4;
|
||||
reg_t RESERVED0 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PA_DAC_Type) == sizeof(reg_t), "PA_DAC_Type wrong size");
|
||||
|
||||
struct TX_GAIN_Type {
|
||||
reg_t TXVGA_GAIN_SPI : 6;
|
||||
reg_t RESERVED0 : 4;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TX_GAIN_Type) == sizeof(reg_t), "TX_GAIN_Type wrong size");
|
||||
|
||||
struct TX_LO_IQ_Type {
|
||||
reg_t TX_DCCORR_I : 6;
|
||||
reg_t RESERVED0 : 2;
|
||||
reg_t PADAC_IV : 1;
|
||||
reg_t PADAC_VMODE : 1;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TX_LO_IQ_Type) == sizeof(reg_t), "TX_LO_IQ_Type wrong size");
|
||||
|
||||
struct TX_DC_CORR_Type {
|
||||
reg_t TX_DCCORR_Q : 6;
|
||||
reg_t RESERVED0 : 3;
|
||||
reg_t PADAC_DIVH : 1;
|
||||
reg_t RESERVED1 : 6;
|
||||
};
|
||||
|
||||
static_assert(sizeof(TX_DC_CORR_Type) == sizeof(reg_t), "TX_DC_CORR_Type wrong size");
|
||||
|
||||
struct Register_Type {
|
||||
RXENABLE_Type rxenable; /* 0 */
|
||||
RXRF_1_Type rxrf_1;
|
||||
RXRF_2_Type rxrf_2;
|
||||
RXRF_LPF_Type rxrf_lpf_1;
|
||||
LPF_Type lpf; /* 4 */
|
||||
LPF_VGA_1_Type lpf_vga_1;
|
||||
LPF_VGA_2_Type lpf_vga_2;
|
||||
RSSI_VGA_Type rssi_vga;
|
||||
RX_TOP_1_Type rx_top_1; /* 8 */
|
||||
RX_TOP_2_Type rx_top_2;
|
||||
TX_TOP_1_Type tx_top_1;
|
||||
TEMP_SENSE_Type temp_sense;
|
||||
HPFSM_1_Type hpfsm_1; /* 12 */
|
||||
HPFSM_2_Type hpfsm_2;
|
||||
HPFSM_3_Type hpfsm_3;
|
||||
HPFSM_4_Type hpfsm_4;
|
||||
SPI_EN_Type spi_en; /* 16 */
|
||||
SYN_FR_DIV_1_Type syn_fr_div_1;
|
||||
SYN_FR_DIV_2_Type syn_fr_div_2;
|
||||
SYN_INT_DIV_Type syn_int_div;
|
||||
SYN_CFG_1_Type syn_cfg_1; /* 20 */
|
||||
SYN_CFG_2_Type syn_cfg_2;
|
||||
VAS_CFG_Type vas_cfg;
|
||||
LO_MISC_Type lo_misc;
|
||||
XTAL_CFG_Type xtal_cfg; /* 24 */
|
||||
VCO_CFG_Type vco_cfg;
|
||||
LO_GEN_Type lo_gen;
|
||||
PA_DRV_Type pa_drv;
|
||||
PA_DAC_Type pa_dac; /* 28 */
|
||||
TX_GAIN_Type tx_gain;
|
||||
TX_LO_IQ_Type tx_lo_iq;
|
||||
TX_DC_CORR_Type tx_dc_corr;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");
|
||||
|
||||
struct RegisterMap {
|
||||
constexpr RegisterMap(
|
||||
Register_Type values
|
||||
) : r(values)
|
||||
{
|
||||
}
|
||||
|
||||
union {
|
||||
Register_Type r;
|
||||
std::array<reg_t, reg_count> w;
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");
|
||||
|
||||
constexpr RegisterMap initial_register_values { Register_Type {
|
||||
/* settings recommended by MAX2839 data sheet */
|
||||
.rxenable = { /* 0 */
|
||||
.RESERVED0 = 0,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.rxrf_1 = { /* 1 */
|
||||
.LNAband = 0,
|
||||
.RESERVED0 = 0,
|
||||
.MIMOmode = 1,
|
||||
.iqerr_trim = 0b000001,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.rxrf_2 = { /* 2 */
|
||||
.LNAgain_SPI_EN = 0,
|
||||
.RESERVED0 = 0,
|
||||
.RX_IQERR_SPI_EN = 0,
|
||||
.RESERVED1 = 0b0010000,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.rxrf_lpf_1 = { /* 3 */
|
||||
.RESERVED0 = 0b0110111001,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.lpf = { /* 4 */
|
||||
.RESERVED0 = 0b10,
|
||||
.dF = 0b01,
|
||||
.RESERVED1 = 0b10,
|
||||
.FT = 0b1111,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.lpf_vga_1 = { /* 5 */
|
||||
.L = 0b00,
|
||||
.VGA = 0b000000,
|
||||
.ModeCtrl = 0b01,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.lpf_vga_2 = { /* 6 */
|
||||
.L = 0b00,
|
||||
.VGA = 0b000000,
|
||||
.BUFF_VCM = 0b00,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.rssi_vga = { /* 7 */
|
||||
.RESERVED0 = 0,
|
||||
.RSSI_MUX = 0,
|
||||
.RSSI_MODE = 0,
|
||||
.RESERVED1 = 0b0001,
|
||||
.RXBB_OUT_SEL = 0,
|
||||
.RESERVED2 = 0,
|
||||
.RSSI_INPUT = 1,
|
||||
.RESERVED3 = 0,
|
||||
},
|
||||
.rx_top_1 = { /* 8 */
|
||||
.RESERVED0 = 0,
|
||||
.VGAgain_SPI_EN = 0,
|
||||
.LPF_MODE_SEL = 0,
|
||||
.RESERVED1 = 0b1000100,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.rx_top_2 = { /* 9 */
|
||||
.ts_adc_trigger = 0,
|
||||
.ts_en = 0,
|
||||
.RESERVED0 = 0,
|
||||
.DOUT_DRVH = 1,
|
||||
.DOUT_CSB_SEL = 1,
|
||||
.DOUT_SEL = 0b000,
|
||||
.RESERVED1 = 0b00,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.tx_top_1 = { /* 10 */
|
||||
.TXCAL_GAIN = 0b00,
|
||||
.TXCAL_V2I_FILT = 0b011,
|
||||
.RESERVED1 = 0b00000,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.temp_sense = { /* 11 */
|
||||
.RESERVED0 = 0b0000000100,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.hpfsm_1 = { /* 12 */
|
||||
.HPC_10M = 0b11,
|
||||
.HPC_10M_GAIN = 0b11,
|
||||
.HPC_600k = 0b100,
|
||||
.HPC_600k_GAIN = 0b100,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.hpfsm_2 = { /* 13 */
|
||||
.HPC_100k = 0b00,
|
||||
.HPC_100k_GAIN = 0b00,
|
||||
.HPC_30k = 0b01,
|
||||
.HPC_30k_GAIN = 0b01,
|
||||
.HPC_1k = 0b01,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.hpfsm_3 = { /* 14 */
|
||||
.HPC_1k_B7B6 = 0b01,
|
||||
.HPC_DELAY = 0b01,
|
||||
.HPC_STOP = 0b00,
|
||||
.HPC_STOP_M2 = 0b11,
|
||||
.HPC_RXGAIN_EN = 1,
|
||||
.TXGATE_EN = 1,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.hpfsm_4 = { /* 15 */
|
||||
.HPC_DIVH = 1,
|
||||
.RESERVED0 = 0b00000,
|
||||
.HPC_SEQ_BYP = 0,
|
||||
.RESERVED1 = 0b00,
|
||||
.HPC_MODE = 1,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.spi_en = { /* 16 */
|
||||
.EN_SPI = 0,
|
||||
.CAL_SPI = 0,
|
||||
.RESERVED0 = 0b0111,
|
||||
.PADAC_SPI_EN = 0,
|
||||
.PADAC_TX_EN = 0,
|
||||
.RESERVED1 = 0b00,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.syn_fr_div_1 = { /* 17 */
|
||||
.SYN_FRDIV_9_0 = 0b0101010101,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.syn_fr_div_2 = { /* 18 */
|
||||
.SYN_FRDIV_19_10 = 0b0101010101,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.syn_int_div = { /* 19 */
|
||||
.SYN_INTDIV = 0b01010011,
|
||||
.LOGEN_BSW = 0b01,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.syn_cfg_1 = { /* 20 */
|
||||
.RESERVED0 = 1,
|
||||
.SYN_REF_DIV_RATIO = 0b00,
|
||||
.RESERVED1 = 0b01,
|
||||
.SYN_CLOCKOUT_DRIVE = 0,
|
||||
.RESERVED2 = 0b1001,
|
||||
.RESERVED3 = 0,
|
||||
},
|
||||
.syn_cfg_2 = { /* 21 */
|
||||
.RESERVED0 = 0b0000101101,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.vas_cfg = { /* 22 */
|
||||
.VAS_MODE = 1,
|
||||
.VAS_RELOCK_SEL = 0,
|
||||
.VAS_DIV = 0b010,
|
||||
.VAS_DLY = 0b01,
|
||||
.VAS_TRIG_EN = 1,
|
||||
.RESERVED0 = 0b01,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.lo_misc = { /* 23 */
|
||||
.VAS_SPI = 0b01111,
|
||||
.XTAL_BIAS_SEL = 0b10,
|
||||
.RESERVED0 = 0b100,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.xtal_cfg = { /* 24 */
|
||||
.XTAL_FTUNE = 0b0000000,
|
||||
.RESERVED0 = 1,
|
||||
.XTAL_CLKOUT_DIV = 1,
|
||||
.XTAL_CORE_EN = 0,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.vco_cfg = { /* 25 */
|
||||
.RESERVED0 = 0b0000000000,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.lo_gen = { /* 26 */
|
||||
.RESERVED0 = 0b000,
|
||||
.LOGEN_2GM = 0,
|
||||
.RESERVED1 = 0b00,
|
||||
.VAS_TST = 0b1111,
|
||||
.RESERVED2 = 0,
|
||||
},
|
||||
.pa_drv = { /* 27 */
|
||||
.TXLO_IQ_SPI = 0b000000,
|
||||
.TXLO_IQ_SPI_EN = 0,
|
||||
.TXVGA_GAIN_SPI_EN = 0,
|
||||
.TX_DCCORR_SPI_EN = 0,
|
||||
.RESERVED0 = 1,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.pa_dac = { /* 28 */
|
||||
.PADAC_BIAS = 0b000000,
|
||||
.PADAC_DLY = 0b0011,
|
||||
.RESERVED0 = 0,
|
||||
},
|
||||
.tx_gain = { /* 29 */
|
||||
.TXVGA_GAIN_SPI = 0b111111,
|
||||
.RESERVED0 = 0b0000,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.tx_lo_iq = { /* 30 */
|
||||
.TX_DCCORR_I = 0b000000,
|
||||
.RESERVED0 = 0b00,
|
||||
.PADAC_IV = 1,
|
||||
.PADAC_VMODE = 1,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
.tx_dc_corr = { /* 31 */
|
||||
.TX_DCCORR_Q = 0b000000,
|
||||
.RESERVED0 = 0b101,
|
||||
.PADAC_DIVH = 1,
|
||||
.RESERVED1 = 0,
|
||||
},
|
||||
} };
|
||||
|
||||
class MAX2839 : public MAX283x {
|
||||
public:
|
||||
constexpr MAX2839(
|
||||
spi::arbiter::Target& target
|
||||
) : _target(target)
|
||||
{
|
||||
}
|
||||
|
||||
void init() override;
|
||||
void set_mode(const Mode mode) override;
|
||||
|
||||
void set_tx_vga_gain(const int_fast8_t db) override;
|
||||
void set_lna_gain(const int_fast8_t db) override;
|
||||
void set_vga_gain(const int_fast8_t db) override;
|
||||
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) override;
|
||||
bool set_frequency(const rf::Frequency lo_frequency) override;
|
||||
void set_rx_lo_iq_calibration(const size_t v) override;
|
||||
void set_rx_buff_vcm(const size_t v) override;
|
||||
|
||||
reg_t temp_sense() override;
|
||||
|
||||
reg_t read(const address_t reg_num) override;
|
||||
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
|
||||
RegisterMap _map { initial_register_values };
|
||||
DirtyRegisters<Register, reg_count> _dirty { };
|
||||
|
||||
void flush_one(const Register reg);
|
||||
|
||||
void write(const address_t reg_num, const reg_t value);
|
||||
|
||||
void write(const Register reg, const reg_t value);
|
||||
reg_t read(const Register reg);
|
||||
|
||||
void flush();
|
||||
|
||||
void configure_rx_gain();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__MAX2839_H__*/
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Great Scott Gadgets
|
||||
*
|
||||
* 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 __MAX283X_H__
|
||||
#define __MAX283X_H__
|
||||
|
||||
#include "rf_path.hpp"
|
||||
|
||||
namespace max283x {
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace lo {
|
||||
|
||||
constexpr std::array<rf::FrequencyRange, 4> band { {
|
||||
{ 2300000000, 2400000000 },
|
||||
{ 2400000000, 2500000000 },
|
||||
{ 2500000000, 2600000000 },
|
||||
{ 2600000000, 2700000000 },
|
||||
} };
|
||||
|
||||
} /* namespace lo */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace lna {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 40 };
|
||||
constexpr int8_t gain_db_step = 8;
|
||||
|
||||
constexpr std::array<rf::FrequencyRange, 2> band { {
|
||||
{ 2300000000, 2500000000 },
|
||||
{ 2500000000, 2700000000 },
|
||||
} };
|
||||
|
||||
} /* namespace lna */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace vga {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 62 };
|
||||
constexpr int8_t gain_db_step = 2;
|
||||
|
||||
} /* namespace vga */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace tx {
|
||||
|
||||
constexpr range_t<int8_t> gain_db_range { 0, 47 };
|
||||
constexpr int8_t gain_db_step = 1;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
namespace filter {
|
||||
|
||||
constexpr std::array<uint32_t, 16> bandwidths {
|
||||
/* Assumption: these values are in ascending order */
|
||||
1750000,
|
||||
2500000, /* Some documentation says 2.25MHz */
|
||||
3500000,
|
||||
5000000,
|
||||
5500000,
|
||||
6000000,
|
||||
7000000,
|
||||
8000000,
|
||||
9000000,
|
||||
10000000,
|
||||
12000000,
|
||||
14000000,
|
||||
15000000,
|
||||
20000000,
|
||||
24000000,
|
||||
28000000,
|
||||
};
|
||||
|
||||
constexpr auto bandwidth_minimum = bandwidths[0];
|
||||
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
|
||||
|
||||
} /* namespace filter */
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
enum Mode {
|
||||
Shutdown,
|
||||
Standby,
|
||||
Receive,
|
||||
Transmit,
|
||||
};
|
||||
|
||||
using reg_t = uint16_t;
|
||||
using address_t = uint8_t;
|
||||
|
||||
class MAX283x {
|
||||
public:
|
||||
virtual ~MAX283x() = default;
|
||||
|
||||
virtual void init();
|
||||
virtual void set_mode(const Mode mode);
|
||||
|
||||
virtual void set_tx_vga_gain(const int_fast8_t db);
|
||||
virtual void set_lna_gain(const int_fast8_t db);
|
||||
virtual void set_vga_gain(const int_fast8_t db);
|
||||
virtual void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
|
||||
|
||||
virtual bool set_frequency(const rf::Frequency lo_frequency);
|
||||
|
||||
virtual void set_rx_lo_iq_calibration(const size_t v);
|
||||
virtual void set_rx_buff_vcm(const size_t v);
|
||||
|
||||
virtual reg_t temp_sense();
|
||||
|
||||
virtual reg_t read(const address_t reg_num);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif/*__MAX283X_H__*/
|
||||
@@ -372,6 +372,10 @@ public:
|
||||
while(device_status() & 0x80);
|
||||
}
|
||||
|
||||
bool plla_loss_of_signal() {
|
||||
return (device_status() >> 5) & 1;
|
||||
}
|
||||
|
||||
bool clkin_loss_of_signal() {
|
||||
return (device_status() >> 4) & 1;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ int main(void) {
|
||||
portapack::shutdown();
|
||||
}
|
||||
|
||||
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf);
|
||||
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf, true);
|
||||
m0_halt();
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -215,7 +215,7 @@ 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 >= 4) && sd_card::status() == sd_card::Status::Mounted){
|
||||
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;
|
||||
@@ -235,14 +235,19 @@ static PortaPackModel portapack_model() {
|
||||
const auto switches_state = get_switches_state();
|
||||
if (switches_state[(size_t)ui::KeyEvent::Up]){
|
||||
save_config(1);
|
||||
model = PortaPackModel::R2_20170522;
|
||||
// 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;
|
||||
// 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);
|
||||
@@ -253,6 +258,10 @@ static PortaPackModel portapack_model() {
|
||||
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
|
||||
@@ -372,8 +381,9 @@ static void shutdown_base() {
|
||||
*
|
||||
* PLL0USB = powered down
|
||||
* PLL0AUDIO = GP_CLKIN, Fcco=491.52 MHz, Fout=12.288 MHz
|
||||
* PLL1 = GP_CLKIN * 10 = 200 MHz
|
||||
*
|
||||
* PLL1 =
|
||||
* OG: GP_CLKIN * 10 = 200 MHz
|
||||
* r9: GP_CLKIN * 20 = 200 MHz
|
||||
* IDIVA = IRC / 1 = 12 MHz
|
||||
* IDIVB = PLL1 / 2 = 100 MHz
|
||||
* IDIVC = PLL1 / 1 = 200 MHz
|
||||
@@ -423,9 +433,14 @@ bool init() {
|
||||
*/
|
||||
|
||||
/* Step into the 90-110MHz M4 clock range */
|
||||
/* Fclkin = 40M
|
||||
* /N=2 = 20M = PFDin
|
||||
* Fcco = PFDin * (M=10) = 200M
|
||||
/* OG:
|
||||
* Fclkin = 40M
|
||||
* /N=2 = 20M = PFDin
|
||||
* Fcco = PFDin * (M=10) = 200M
|
||||
* r9:
|
||||
* Fclkin = 10M
|
||||
* /N=1 = 10M = PFDin
|
||||
* Fcco = PFDin * (M=20) = 200M
|
||||
* Fclk = Fcco / (2*(P=1)) = 100M
|
||||
*/
|
||||
cgu::pll1::ctrl({
|
||||
@@ -435,8 +450,8 @@ bool init() {
|
||||
.direct = 0,
|
||||
.psel = 0,
|
||||
.autoblock = 1,
|
||||
.nsel = 1,
|
||||
.msel = 9,
|
||||
.nsel = hackrf_r9 ? 0UL : 1UL,
|
||||
.msel = hackrf_r9 ? 19UL : 9UL,
|
||||
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
|
||||
});
|
||||
|
||||
@@ -457,13 +472,15 @@ bool init() {
|
||||
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());
|
||||
clock_manager.enable_first_if_clock();
|
||||
clock_manager.enable_second_if_clock();
|
||||
clock_manager.enable_if_clocks();
|
||||
clock_manager.enable_codec_clocks();
|
||||
radio::init();
|
||||
|
||||
@@ -475,7 +492,7 @@ bool init() {
|
||||
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){
|
||||
if (load_config() != 3 && load_config() != 4){
|
||||
shutdown_base();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@
|
||||
#define __ENCODERS_H__
|
||||
|
||||
namespace encoders {
|
||||
|
||||
|
||||
#define ENC_TYPES_COUNT 14
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
|
||||
|
||||
#define ENCODER_UM3750 8
|
||||
|
||||
|
||||
size_t make_bitstream(std::string& fragments);
|
||||
void bitstream_append(size_t& bitstream_length, uint32_t bit_count, uint32_t bits);
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace encoders {
|
||||
150000, 2,
|
||||
0
|
||||
},
|
||||
|
||||
|
||||
// PT2260-R4
|
||||
{
|
||||
"2260-R4",
|
||||
@@ -77,7 +77,7 @@ namespace encoders {
|
||||
150000, 2,
|
||||
0
|
||||
},
|
||||
|
||||
|
||||
// PT2262
|
||||
{
|
||||
"2262 ",
|
||||
@@ -89,7 +89,7 @@ namespace encoders {
|
||||
20000, 4,
|
||||
0
|
||||
},
|
||||
|
||||
|
||||
// 16-bit ?
|
||||
{
|
||||
"16-bit ",
|
||||
@@ -101,7 +101,7 @@ namespace encoders {
|
||||
25000, 50,
|
||||
0 // ?
|
||||
},
|
||||
|
||||
|
||||
// RT1527
|
||||
{
|
||||
"1527 ",
|
||||
@@ -111,9 +111,9 @@ namespace encoders {
|
||||
24, "SAAAAAAAAAAAAAAAAAAAADDDD",
|
||||
"10000000000000000000000000000000",
|
||||
100000, 4,
|
||||
10 // ?
|
||||
0
|
||||
},
|
||||
|
||||
|
||||
// HK526E
|
||||
{
|
||||
"526E ",
|
||||
@@ -125,7 +125,7 @@ namespace encoders {
|
||||
20000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// HT12E
|
||||
{
|
||||
"12E ",
|
||||
@@ -137,7 +137,7 @@ namespace encoders {
|
||||
3000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// VD5026 13 bits ?
|
||||
{
|
||||
"5026 ",
|
||||
@@ -149,7 +149,7 @@ namespace encoders {
|
||||
100000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// UM3750
|
||||
{
|
||||
"UM3750 ",
|
||||
@@ -161,7 +161,7 @@ namespace encoders {
|
||||
100000, 4,
|
||||
(3 * 12) - 6 // Compensates for pause delay bug in proc_ook
|
||||
},
|
||||
|
||||
|
||||
// UM3758
|
||||
{
|
||||
"UM3758 ",
|
||||
@@ -173,7 +173,7 @@ namespace encoders {
|
||||
160000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// BA5104
|
||||
{
|
||||
"BA5104 ",
|
||||
@@ -185,7 +185,7 @@ namespace encoders {
|
||||
455000, 4,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// MC145026
|
||||
{
|
||||
"145026 ",
|
||||
@@ -197,7 +197,7 @@ namespace encoders {
|
||||
455000, 2,
|
||||
2
|
||||
},
|
||||
|
||||
|
||||
// HT6*** TODO: Add individual variations
|
||||
{
|
||||
"HT6*** ",
|
||||
@@ -209,7 +209,7 @@ namespace encoders {
|
||||
80000, 3,
|
||||
10 // ?
|
||||
},
|
||||
|
||||
|
||||
// TC9148
|
||||
{
|
||||
"TC9148 ",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "rffc507x.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "max2839.hpp"
|
||||
#include "max5864.hpp"
|
||||
#include "baseband_cpld.hpp"
|
||||
|
||||
@@ -52,12 +53,12 @@ static constexpr uint32_t ssp_scr(
|
||||
return static_cast<uint8_t>(pclk_f / cpsr / spi_f - 1);
|
||||
}
|
||||
|
||||
static constexpr SPIConfig ssp_config_max2837 = {
|
||||
static constexpr SPIConfig ssp_config_max283x = {
|
||||
.end_cb = NULL,
|
||||
.ssport = gpio_max2837_select.port(),
|
||||
.sspad = gpio_max2837_select.pad(),
|
||||
.ssport = gpio_max283x_select.port(),
|
||||
.sspad = gpio_max283x_select.pad(),
|
||||
.cr0 =
|
||||
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max2837_spi_f))
|
||||
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max283x_spi_f))
|
||||
| CR0_FRFSPI
|
||||
| CR0_DSS16BIT
|
||||
,
|
||||
@@ -78,9 +79,9 @@ static constexpr SPIConfig ssp_config_max5864 = {
|
||||
|
||||
static spi::arbiter::Arbiter ssp1_arbiter(portapack::ssp1);
|
||||
|
||||
static spi::arbiter::Target ssp1_target_max2837 {
|
||||
static spi::arbiter::Target ssp1_target_max283x {
|
||||
ssp1_arbiter,
|
||||
ssp_config_max2837
|
||||
ssp_config_max283x
|
||||
};
|
||||
|
||||
static spi::arbiter::Target ssp1_target_max5864 {
|
||||
@@ -90,16 +91,27 @@ static spi::arbiter::Target ssp1_target_max5864 {
|
||||
|
||||
static rf::path::Path rf_path;
|
||||
rffc507x::RFFC507x first_if;
|
||||
max2837::MAX2837 second_if { ssp1_target_max2837 };
|
||||
max283x::MAX283x *second_if;
|
||||
max2837::MAX2837 second_if_max2837 { ssp1_target_max283x };
|
||||
max2839::MAX2839 second_if_max2839 { ssp1_target_max283x };
|
||||
static max5864::MAX5864 baseband_codec { ssp1_target_max5864 };
|
||||
static baseband::CPLD baseband_cpld;
|
||||
|
||||
static rf::Direction direction { rf::Direction::Receive };
|
||||
static bool baseband_invert = false;
|
||||
static bool mixer_invert = false;
|
||||
|
||||
void init() {
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_not_ant_pwr.write(1);
|
||||
gpio_r9_not_ant_pwr.output();
|
||||
}
|
||||
rf_path.init();
|
||||
first_if.init();
|
||||
second_if.init();
|
||||
second_if = hackrf_r9
|
||||
? (max283x::MAX283x *) &second_if_max2839
|
||||
: (max283x::MAX283x *) &second_if_max2837;
|
||||
second_if->init();
|
||||
baseband_codec.init();
|
||||
baseband_cpld.init();
|
||||
}
|
||||
@@ -121,7 +133,30 @@ void set_direction(const rf::Direction new_direction) {
|
||||
|
||||
direction = new_direction;
|
||||
|
||||
second_if.set_mode((direction == rf::Direction::Transmit) ? max2837::Mode::Transmit : max2837::Mode::Receive);
|
||||
if (hackrf_r9) {
|
||||
/*
|
||||
* HackRF One r9 inverts analog baseband only for RX. Previous hardware
|
||||
* revisions inverted analog baseband for neither direction because of
|
||||
* compensation in the CPLD. If we ever simplify the CPLD to handle RX
|
||||
* and TX the same way, we will need to update this baseband_invert
|
||||
* logic.
|
||||
*/
|
||||
baseband_invert = (direction == rf::Direction::Receive);
|
||||
} else {
|
||||
/*
|
||||
* Analog baseband is inverted in RX but not TX. The RX inversion is
|
||||
* corrected by the CPLD, but future hardware or CPLD changes may
|
||||
* change this for either or both directions. For a given hardware+CPLD
|
||||
* platform, baseband inversion is set here for RX and/or TX. Spectrum
|
||||
* inversion resulting from the mixer is tracked separately according
|
||||
* to the tuning configuration. We ask the CPLD to apply a correction
|
||||
* for the total inversion.
|
||||
*/
|
||||
baseband_invert = false;
|
||||
}
|
||||
baseband_cpld.set_invert(mixer_invert ^ baseband_invert);
|
||||
|
||||
second_if->set_mode((direction == rf::Direction::Transmit) ? max283x::Mode::Transmit : max283x::Mode::Receive);
|
||||
rf_path.set_direction(direction);
|
||||
|
||||
baseband_codec.set_mode((direction == rf::Direction::Transmit) ? max5864::Mode::Transmit : max5864::Mode::Receive);
|
||||
@@ -142,10 +177,11 @@ bool set_tuning_frequency(const rf::Frequency frequency) {
|
||||
first_if.enable();
|
||||
}
|
||||
|
||||
const auto result_second_if = second_if.set_frequency(tuning_config.second_lo_frequency);
|
||||
const auto result_second_if = second_if->set_frequency(tuning_config.second_lo_frequency);
|
||||
|
||||
rf_path.set_band(tuning_config.rf_path_band);
|
||||
baseband_cpld.set_invert(tuning_config.baseband_invert);
|
||||
mixer_invert = tuning_config.mixer_invert;
|
||||
baseband_cpld.set_invert(mixer_invert ^ baseband_invert);
|
||||
|
||||
return result_second_if;
|
||||
} else {
|
||||
@@ -165,19 +201,19 @@ void set_rf_amp(const bool rf_amp) {
|
||||
}
|
||||
|
||||
void set_lna_gain(const int_fast8_t db) {
|
||||
second_if.set_lna_gain(db);
|
||||
second_if->set_lna_gain(db);
|
||||
}
|
||||
|
||||
void set_vga_gain(const int_fast8_t db) {
|
||||
second_if.set_vga_gain(db);
|
||||
second_if->set_vga_gain(db);
|
||||
}
|
||||
|
||||
void set_tx_gain(const int_fast8_t db) {
|
||||
second_if.set_tx_vga_gain(db);
|
||||
second_if->set_tx_vga_gain(db);
|
||||
}
|
||||
|
||||
void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum) {
|
||||
second_if.set_lpf_rf_bandwidth(bandwidth_minimum);
|
||||
second_if->set_lpf_rf_bandwidth(bandwidth_minimum);
|
||||
}
|
||||
|
||||
void set_baseband_rate(const uint32_t rate) {
|
||||
@@ -186,13 +222,17 @@ void set_baseband_rate(const uint32_t rate) {
|
||||
|
||||
void set_antenna_bias(const bool on) {
|
||||
/* Pull MOSFET gate low to turn on antenna bias. */
|
||||
first_if.set_gpo1(on ? 0 : 1);
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_not_ant_pwr.write(on ? 0 : 1);
|
||||
} else {
|
||||
first_if.set_gpo1(on ? 0 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
void disable() {
|
||||
set_antenna_bias(false);
|
||||
baseband_codec.set_mode(max5864::Mode::Shutdown);
|
||||
second_if.set_mode(max2837::Mode::Standby);
|
||||
second_if->set_mode(max2837::Mode::Standby);
|
||||
first_if.disable();
|
||||
set_rf_amp(false);
|
||||
|
||||
@@ -227,11 +267,11 @@ uint32_t register_read(const size_t register_number) {
|
||||
namespace second_if {
|
||||
|
||||
uint32_t register_read(const size_t register_number) {
|
||||
return radio::second_if.read(register_number);
|
||||
return radio::second_if->read(register_number);
|
||||
}
|
||||
|
||||
uint8_t temp_sense() {
|
||||
return radio::second_if.temp_sense() & 0x1f;
|
||||
return radio::second_if->temp_sense() & 0x1f;
|
||||
}
|
||||
|
||||
} /* namespace second_if */
|
||||
|
||||
@@ -38,11 +38,12 @@ using namespace portapack;
|
||||
|
||||
namespace {
|
||||
|
||||
static constexpr std::array<baseband::AMConfig, 4> am_configs { {
|
||||
{ taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB },
|
||||
{ taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB },
|
||||
{ taps_2k8_lsb_channel, AMConfigureMessage::Modulation::SSB },
|
||||
{ taps_0k7_usb_channel, AMConfigureMessage::Modulation::SSB },
|
||||
static constexpr std::array<baseband::AMConfig, 5> am_configs { { // we config here all the non COMMON parameters to each AM modulation type in RX.
|
||||
{ taps_9k0_decim_2, taps_9k0_dsb_channel, AMConfigureMessage::Modulation::DSB }, // AM DSB-C BW 9khz (+-4k5) commercial EU bandwidth .
|
||||
{ taps_6k0_decim_2, taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB }, // AM DSB-C BW 6khz (+-3k0) narrow AM , ham equipments.
|
||||
{ taps_6k0_decim_2, taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB }, // SSB USB BW 2K8 (+ 2K8)
|
||||
{ taps_6k0_decim_2, taps_2k8_lsb_channel, AMConfigureMessage::Modulation::SSB }, // SSB LSB BW 2K8 (- 2K8)
|
||||
{ taps_6k0_decim_2, taps_0k7_usb_channel, AMConfigureMessage::Modulation::SSB }, // SSB USB BW 0K7 (+ 0K7) used to get audio tone from CW Morse, assuming tx shifted +700hz aprox
|
||||
} };
|
||||
|
||||
static constexpr std::array<baseband::NBFMConfig, 3> nbfm_configs { {
|
||||
@@ -312,3 +313,12 @@ size_t ReceiverModel::wfm_configuration() const {
|
||||
void ReceiverModel::update_wfm_configuration() {
|
||||
wfm_configs[wfm_config_index].apply();
|
||||
}
|
||||
|
||||
void ReceiverModel::set_configuration_without_init(const Mode new_mode, const rf::Frequency new_frequency_step, const size_t new_am_config_index, const size_t new_nbfm_config_index, const size_t new_wfm_config_index, uint8_t new_squelch_level) {
|
||||
mode_ = new_mode;
|
||||
frequency_step_ = new_frequency_step;
|
||||
am_config_index = new_am_config_index;
|
||||
nbfm_config_index = new_nbfm_config_index;
|
||||
wfm_config_index = new_wfm_config_index;
|
||||
squelch_level_ = new_squelch_level;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include "message.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "max283x.hpp"
|
||||
#include "volume.hpp"
|
||||
|
||||
class ReceiverModel {
|
||||
@@ -87,12 +87,14 @@ public:
|
||||
size_t wfm_configuration() const;
|
||||
void set_wfm_configuration(const size_t n);
|
||||
|
||||
void set_configuration_without_init(const Mode new_mode, const rf::Frequency new_frequency_step, const size_t new_am_config_index, const size_t new_nbfm_config_index, const size_t new_wfm_config_index, uint8_t new_squelch_level);
|
||||
|
||||
private:
|
||||
rf::Frequency frequency_step_ { 25000 };
|
||||
bool enabled_ { false };
|
||||
bool rf_amp_ { false };
|
||||
int32_t lna_gain_db_ { 32 };
|
||||
uint32_t baseband_bandwidth_ { max2837::filter::bandwidth_minimum };
|
||||
uint32_t baseband_bandwidth_ { max283x::filter::bandwidth_minimum };
|
||||
int32_t vga_gain_db_ { 32 };
|
||||
int32_t tx_gain_db_ { 47 };
|
||||
Mode mode_ { Mode::NarrowbandFMAudio };
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "rf_path.hpp"
|
||||
#include "platform.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
@@ -34,14 +35,12 @@ namespace path {
|
||||
|
||||
namespace {
|
||||
|
||||
using GPIOs = std::array<GPIO, 13>;
|
||||
using GPIOs = std::array<GPIO, 11>;
|
||||
|
||||
/* TODO: ARM GCC 4.8 2014q3 doesn't like this array inside struct Config.
|
||||
* No idea why.
|
||||
*/
|
||||
constexpr GPIOs gpios {
|
||||
gpio_tx,
|
||||
gpio_rx,
|
||||
gpio_mix_bypass,
|
||||
gpio_not_mix_bypass,
|
||||
gpio_tx_mix_bp,
|
||||
@@ -122,15 +121,39 @@ struct Config {
|
||||
}
|
||||
|
||||
static void gpio_init() {
|
||||
for(auto gpio : gpios) {
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_rx.output();
|
||||
} else {
|
||||
gpio_og_tx.output();
|
||||
gpio_og_rx.output();
|
||||
}
|
||||
for (auto gpio : gpios) {
|
||||
gpio.output();
|
||||
}
|
||||
}
|
||||
|
||||
void apply() const {
|
||||
/* NOTE: Assumes order in gpios[] and Config bitfield match. */
|
||||
for(size_t n=0; n<gpios.size(); n++) {
|
||||
gpios[n].write((*this)[n]);
|
||||
/* NOTE: Assumes order in gpios[] and Config bitfield match,
|
||||
* after the 'tx' and 'rx' fields which are handled specially. */
|
||||
for (size_t n = 0; n < gpios.size() + 2; n++) {
|
||||
bool value = (*this)[n];
|
||||
switch (n) {
|
||||
case 0:
|
||||
if (!hackrf_r9) {
|
||||
gpio_og_tx.write(value);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if (hackrf_r9) {
|
||||
gpio_r9_rx.write(value);
|
||||
} else {
|
||||
gpio_og_rx.write(value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
gpios[n - 2].write(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -39,8 +39,8 @@ enum class Direction {
|
||||
|
||||
namespace path {
|
||||
|
||||
constexpr FrequencyRange band_low { 0, 2150000000 };
|
||||
constexpr FrequencyRange band_high { 2750000000, 7250000000 };
|
||||
constexpr FrequencyRange band_low { 0, 2170000000 };
|
||||
constexpr FrequencyRange band_high { 2740000000, 7250000000 };
|
||||
constexpr FrequencyRange band_mid { band_low.maximum, band_high.minimum };
|
||||
|
||||
enum class Band {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -255,3 +255,9 @@ double get_decimals(double num, int16_t mult, bool round) {
|
||||
if (num > .5) intnum++; //Round up
|
||||
return intnum;
|
||||
}
|
||||
|
||||
std::string trimr(std::string str)
|
||||
{
|
||||
size_t last = str.find_last_not_of(' ');
|
||||
return (last!=std::string::npos) ? str.substr(0, last+1) : ""; // Remove the trailing spaces
|
||||
}
|
||||
@@ -58,4 +58,7 @@ std::string to_string_FAT_timestamp(const FATTimestamp& timestamp);
|
||||
|
||||
std::string unit_auto_scale(double n, const uint32_t base_nano, uint32_t precision);
|
||||
double get_decimals(double num, int16_t mult, bool round = false); //euquiq added
|
||||
|
||||
std::string trimr(std::string str); // Remove trailing spaces
|
||||
|
||||
#endif/*__STRING_FORMAT_H__*/
|
||||
|
||||
@@ -104,8 +104,30 @@ void tone_keys_populate(OptionsField& field) {
|
||||
field.set_options(tone_key_options);
|
||||
}
|
||||
|
||||
float tone_key_frequency(const uint32_t index) {
|
||||
float tone_key_frequency(const tone_index index) {
|
||||
return tone_keys[index].second;
|
||||
}
|
||||
|
||||
std::string tone_key_string( tone_index index ) {
|
||||
if( index < 0 || (unsigned)index >= tone_keys . size() )
|
||||
return std::string( "" );
|
||||
return tone_keys[ index ] .first ;
|
||||
}
|
||||
|
||||
tone_index tone_key_index_by_string( char *str ) {
|
||||
if( !str )
|
||||
return -1 ;
|
||||
for( tone_index index = 0 ; (unsigned)index < tone_keys . size() ; index ++ )
|
||||
{
|
||||
if( tone_keys[ index ] . first . compare( str ) >= 0 )
|
||||
return index ;
|
||||
}
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
/* tone_index tone_key_index_by_value( int32_t freq )
|
||||
{
|
||||
return -1 ;
|
||||
} */
|
||||
|
||||
}
|
||||
|
||||
@@ -30,12 +30,18 @@ using namespace ui;
|
||||
|
||||
namespace tonekey {
|
||||
|
||||
typedef int16_t tone_index ;
|
||||
|
||||
using tone_key_t = std::vector<std::pair<std::string, float>>;
|
||||
|
||||
extern const tone_key_t tone_keys;
|
||||
|
||||
void tone_keys_populate(OptionsField& field);
|
||||
float tone_key_frequency(const uint32_t index);
|
||||
float tone_key_frequency(const tone_index index);
|
||||
|
||||
std::string tone_key_string( const tone_index index );
|
||||
tone_index tone_key_index_by_string( char *str );
|
||||
// tone_index tone_key_index_by_value( int32_t freq );
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -137,6 +137,15 @@ struct Calibration {
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Calibration() :
|
||||
Calibration(
|
||||
/* Values derived from one PortaPack H1 unit. */
|
||||
{ { { 256, 731 }, { 880, 432 }, { 568, 146 } } },
|
||||
{ { { 32, 48 }, { 208, 168 }, { 120, 288 } } }
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
ui::Point translate(const DigitizerPoint& p) const;
|
||||
|
||||
private:
|
||||
@@ -149,8 +158,6 @@ private:
|
||||
int32_t f;
|
||||
};
|
||||
|
||||
const Calibration default_calibration();
|
||||
|
||||
template<size_t N>
|
||||
class Filter {
|
||||
public:
|
||||
|
||||
@@ -41,7 +41,7 @@ constexpr rf::Frequency high_band_second_lo_regions_2_and_3(const rf::Frequency
|
||||
|
||||
constexpr rf::Frequency high_band_second_lo_frequency(const rf::Frequency target_frequency) {
|
||||
return (target_frequency < 3600000000)
|
||||
? (2150000000 + (((target_frequency - 2750000000) * 60) / 85))
|
||||
? (2170000000 + (((target_frequency - 2740000000) * 57) / 86))
|
||||
: high_band_second_lo_regions_2_and_3(target_frequency)
|
||||
;
|
||||
}
|
||||
@@ -49,8 +49,8 @@ constexpr rf::Frequency high_band_second_lo_frequency(const rf::Frequency target
|
||||
Config low_band(const rf::Frequency target_frequency) {
|
||||
const rf::Frequency first_lo_frequency = target_frequency + low_band_second_lo_frequency(target_frequency);
|
||||
const rf::Frequency second_lo_frequency = first_lo_frequency - target_frequency;
|
||||
const bool baseband_invert = true;
|
||||
return { first_lo_frequency, second_lo_frequency, rf::path::Band::Low, baseband_invert };
|
||||
const bool mixer_invert = true;
|
||||
return { first_lo_frequency, second_lo_frequency, rf::path::Band::Low, mixer_invert };
|
||||
}
|
||||
|
||||
Config mid_band(const rf::Frequency target_frequency) {
|
||||
@@ -60,8 +60,8 @@ Config mid_band(const rf::Frequency target_frequency) {
|
||||
Config high_band(const rf::Frequency target_frequency) {
|
||||
const rf::Frequency first_lo_frequency = target_frequency - high_band_second_lo_frequency(target_frequency);
|
||||
const rf::Frequency second_lo_frequency = target_frequency - first_lo_frequency;
|
||||
const bool baseband_invert = false;
|
||||
return { first_lo_frequency, second_lo_frequency, rf::path::Band::High, baseband_invert };
|
||||
const bool mixer_invert = false;
|
||||
return { first_lo_frequency, second_lo_frequency, rf::path::Band::High, mixer_invert };
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
@@ -33,7 +33,7 @@ struct Config {
|
||||
) : first_lo_frequency(0),
|
||||
second_lo_frequency(0),
|
||||
rf_path_band(rf::path::Band::Mid),
|
||||
baseband_invert(false)
|
||||
mixer_invert(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -41,11 +41,11 @@ struct Config {
|
||||
rf::Frequency first_lo_frequency,
|
||||
rf::Frequency second_lo_frequency,
|
||||
rf::path::Band rf_path_band,
|
||||
bool baseband_invert
|
||||
bool mixer_invert
|
||||
) : first_lo_frequency(first_lo_frequency),
|
||||
second_lo_frequency(second_lo_frequency),
|
||||
rf_path_band(rf_path_band),
|
||||
baseband_invert(baseband_invert)
|
||||
mixer_invert(mixer_invert)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ struct Config {
|
||||
const rf::Frequency first_lo_frequency;
|
||||
const rf::Frequency second_lo_frequency;
|
||||
const rf::path::Band rf_path_band;
|
||||
const bool baseband_invert;
|
||||
const bool mixer_invert;
|
||||
};
|
||||
|
||||
Config create(const rf::Frequency target_frequency);
|
||||
|
||||
@@ -134,7 +134,7 @@ int32_t GeoPos::altitude() {
|
||||
|
||||
GeoMap::GeoMap(
|
||||
Rect parent_rect
|
||||
) : Widget { parent_rect }
|
||||
) : Widget { parent_rect }, markerListLen(0)
|
||||
{
|
||||
//set_focusable(true);
|
||||
}
|
||||
@@ -144,34 +144,44 @@ void GeoMap::paint(Painter& painter) {
|
||||
std::array<ui::Color, 240> map_line_buffer;
|
||||
const auto r = screen_rect();
|
||||
|
||||
// Ony redraw map if it moved by at least 1 pixel
|
||||
if ((x_pos != prev_x_pos) || (y_pos != prev_y_pos)) {
|
||||
// Ony redraw map if it moved by at least 1 pixel
|
||||
// or the markers list was updated
|
||||
int x_diff = abs(x_pos-prev_x_pos);
|
||||
int y_diff = abs(y_pos-prev_y_pos);
|
||||
if (markerListUpdated || (x_diff>=3) || (y_diff>=3)) {
|
||||
for (line = 0; line < r.height(); line++) {
|
||||
map_file.seek(4 + ((x_pos + (map_width * (y_pos + line))) << 1));
|
||||
map_file.read(map_line_buffer.data(), r.width() << 1);
|
||||
display.draw_pixels({ 0, r.top() + line, r.width(), 1 }, map_line_buffer);
|
||||
}
|
||||
|
||||
prev_x_pos = x_pos;
|
||||
prev_y_pos = y_pos;
|
||||
|
||||
// Draw the other markers
|
||||
for ( int i=0; i<markerListLen; ++i )
|
||||
{
|
||||
GeoMarker & item = markerList[i];
|
||||
double lat_rad = sin(item.lat * pi / 180);
|
||||
int x = (map_width * (item.lon+180)/360) - x_pos;
|
||||
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
|
||||
if ((x>=0) && (x<r.width()) &&
|
||||
(y>10) && (y<r.height()) ) // Dont draw within symbol size of top
|
||||
{
|
||||
ui::Point itemPoint(x,y+r.top());
|
||||
if(y>=32) { // Dont draw text if it would overlap top
|
||||
// Text and symbol
|
||||
draw_marker(painter, itemPoint, item.angle, item.tag, Color::blue(), Color::blue(), Color::magenta() );
|
||||
} else {
|
||||
// Only symbol
|
||||
draw_bearing( itemPoint, item.angle, 10, Color::blue());
|
||||
}
|
||||
}
|
||||
markerListUpdated = false;
|
||||
} // Draw the other markers
|
||||
}
|
||||
//center tag above point
|
||||
if(tag_.find_first_not_of(' ') != tag_.npos){ //only draw tag if we have something other than spaces
|
||||
painter.draw_string(r.center() - Point(((int)tag_.length() * 8 / 2), 2 * 16), style(), tag_);
|
||||
}
|
||||
if (mode_ == PROMPT) {
|
||||
// Cross
|
||||
display.fill_rectangle({ r.center() - Point(16, 1), { 32, 2 } }, Color::red());
|
||||
display.fill_rectangle({ r.center() - Point(1, 16), { 2, 32 } }, Color::red());
|
||||
} else if (angle_ < 360){
|
||||
//if we have a valid angle draw bearing
|
||||
draw_bearing(r.center(), angle_, 10, Color::red());
|
||||
}
|
||||
else {
|
||||
//draw a small cross
|
||||
display.fill_rectangle({ r.center() - Point(8, 1), { 16, 2 } }, Color::red());
|
||||
display.fill_rectangle({ r.center() - Point(1, 8), { 2, 16 } }, Color::red());
|
||||
}
|
||||
|
||||
//Draw the marker in the center
|
||||
draw_marker(painter, r.center(), angle_, tag_, Color::red(), Color::white(), Color::black() );
|
||||
}
|
||||
|
||||
bool GeoMap::on_touch(const TouchEvent event) {
|
||||
@@ -196,10 +206,7 @@ void GeoMap::move(const float lon, const float lat) {
|
||||
x_pos = map_width * (lon_+180)/360 - (map_rect.width() / 2);
|
||||
|
||||
// Latitude calculation based on https://stackoverflow.com/a/10401734/2278659
|
||||
double map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat
|
||||
double lat_rad = sin(lat * pi / 180);
|
||||
double map_world_lon = map_width / (2 * pi);
|
||||
double map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom)));
|
||||
y_pos = map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset) - 128; // Offset added for the GUI
|
||||
|
||||
// Cap position
|
||||
@@ -222,7 +229,11 @@ bool GeoMap::init() {
|
||||
|
||||
lon_ratio = 180.0 / map_center_x;
|
||||
lat_ratio = -90.0 / map_center_y;
|
||||
|
||||
|
||||
map_bottom = sin(-85.05 * pi / 180); // Map bitmap only goes from about -85 to 85 lat
|
||||
map_world_lon = map_width / (2 * pi);
|
||||
map_offset = (map_world_lon / 2 * log((1 + map_bottom) / (1 - map_bottom)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -234,9 +245,9 @@ void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t siz
|
||||
Point arrow_a, arrow_b, arrow_c;
|
||||
|
||||
for (size_t thickness = 0; thickness < 3; thickness++) {
|
||||
arrow_a = polar_to_point(angle, size) + origin;
|
||||
arrow_b = polar_to_point(angle + 180 - 35, size) + origin;
|
||||
arrow_c = polar_to_point(angle + 180 + 35, size) + origin;
|
||||
arrow_a = fast_polar_to_point((int)angle, size) + origin;
|
||||
arrow_b = fast_polar_to_point((int)(angle + 180 - 35), size) + origin;
|
||||
arrow_c = fast_polar_to_point((int)(angle + 180 + 35), size) + origin;
|
||||
|
||||
display.draw_line(arrow_a, arrow_b, color);
|
||||
display.draw_line(arrow_b, arrow_c, color);
|
||||
@@ -246,6 +257,65 @@ void GeoMap::draw_bearing(const Point origin, const uint16_t angle, uint32_t siz
|
||||
}
|
||||
}
|
||||
|
||||
void GeoMap::draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag,
|
||||
const Color color, const Color fontColor, const Color backColor )
|
||||
{
|
||||
int tagOffset = 10;
|
||||
if (mode_ == PROMPT) {
|
||||
// Cross
|
||||
display.fill_rectangle({ itemPoint - Point(16, 1), { 32, 2 } }, color);
|
||||
display.fill_rectangle({ itemPoint - Point(1, 16), { 2, 32 } }, color);
|
||||
tagOffset = 16;
|
||||
} else if (angle_ < 360){
|
||||
//if we have a valid angle draw bearing
|
||||
draw_bearing( itemPoint, itemAngle, 10, color);
|
||||
tagOffset = 10;
|
||||
}
|
||||
else {
|
||||
//draw a small cross
|
||||
display.fill_rectangle({ itemPoint - Point(8, 1), { 16, 2 } }, color);
|
||||
display.fill_rectangle({ itemPoint - Point(1, 8), { 2, 16 } }, color);
|
||||
tagOffset = 8;
|
||||
}
|
||||
//center tag above point
|
||||
if(itemTag.find_first_not_of(' ') != itemTag.npos){ //only draw tag if we have something other than spaces
|
||||
painter.draw_string( itemPoint - Point(((int)itemTag.length() * 8 / 2), 14 + tagOffset ),
|
||||
style().font, fontColor, backColor, itemTag);
|
||||
}
|
||||
}
|
||||
|
||||
void GeoMap::clear_markers()
|
||||
{
|
||||
markerListLen = 0;
|
||||
}
|
||||
|
||||
MapMarkerStored GeoMap::store_marker(GeoMarker & marker)
|
||||
{
|
||||
MapMarkerStored ret;
|
||||
|
||||
// Check if it could be on screen
|
||||
// Only checking one direction to reduce CPU
|
||||
const auto r = screen_rect();
|
||||
double lat_rad = sin(marker.lat * pi / 180);
|
||||
int x = (map_width * (marker.lon+180)/360) - x_pos;
|
||||
int y = (map_height - ((map_world_lon / 2 * log((1 + lat_rad) / (1 - lat_rad))) - map_offset)) - y_pos; // Offset added for the GUI
|
||||
if (false==((x>=0) && (x<r.width()) && (y>10) && (y<r.height()))) // Dont draw within symbol size of top
|
||||
{
|
||||
ret = MARKER_NOT_STORED;
|
||||
}
|
||||
else if (markerListLen<NumMarkerListElements)
|
||||
{
|
||||
markerList[markerListLen] = marker;
|
||||
markerListLen++;
|
||||
markerListUpdated = true;
|
||||
ret = MARKER_STORED;
|
||||
} else {
|
||||
ret = MARKER_LIST_FULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void GeoMapView::focus() {
|
||||
geopos.focus();
|
||||
|
||||
@@ -269,6 +339,10 @@ void GeoMapView::update_position(float lat, float lon, uint16_t angle, int32_t a
|
||||
geomap.move(lon_, lat_);
|
||||
geomap.set_dirty();
|
||||
}
|
||||
|
||||
void GeoMapView::update_tag(const std::string tag) {
|
||||
geomap.set_tag(tag);
|
||||
}
|
||||
|
||||
void GeoMapView::setup() {
|
||||
add_child(&geomap);
|
||||
@@ -375,4 +449,14 @@ GeoMapView::GeoMapView(
|
||||
};
|
||||
}
|
||||
|
||||
void GeoMapView::clear_markers()
|
||||
{
|
||||
geomap.clear_markers();
|
||||
}
|
||||
|
||||
MapMarkerStored GeoMapView::store_marker(GeoMarker & marker)
|
||||
{
|
||||
return geomap.store_marker(marker);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -37,6 +37,24 @@ enum GeoMapMode {
|
||||
PROMPT
|
||||
};
|
||||
|
||||
struct GeoMarker {
|
||||
public:
|
||||
float lat {0};
|
||||
float lon {0};
|
||||
uint16_t angle {0};
|
||||
std::string tag {""};
|
||||
|
||||
GeoMarker & operator=(GeoMarker & rhs){
|
||||
lat = rhs.lat;
|
||||
lon = rhs.lon;
|
||||
angle = rhs.angle;
|
||||
tag = rhs.tag;
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class GeoPos : public View {
|
||||
public:
|
||||
enum alt_unit {
|
||||
@@ -112,6 +130,12 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
enum MapMarkerStored {
|
||||
MARKER_NOT_STORED,
|
||||
MARKER_STORED,
|
||||
MARKER_LIST_FULL
|
||||
};
|
||||
|
||||
class GeoMap : public Widget {
|
||||
public:
|
||||
std::function<void(float, float)> on_move { };
|
||||
@@ -133,20 +157,35 @@ public:
|
||||
angle_ = new_angle;
|
||||
}
|
||||
|
||||
static const int NumMarkerListElements = 30;
|
||||
|
||||
void clear_markers();
|
||||
MapMarkerStored store_marker(GeoMarker & marker);
|
||||
|
||||
private:
|
||||
void draw_bearing(const Point origin, const uint16_t angle, uint32_t size, const Color color);
|
||||
|
||||
void draw_marker(Painter& painter, const ui::Point itemPoint, const uint16_t itemAngle, const std::string itemTag,
|
||||
const Color color = Color::red(), const Color fontColor = Color::white(), const Color backColor = Color::black() );
|
||||
|
||||
GeoMapMode mode_ { };
|
||||
File map_file { };
|
||||
uint16_t map_width { }, map_height { };
|
||||
int32_t map_center_x { }, map_center_y { };
|
||||
float lon_ratio { }, lat_ratio { };
|
||||
double map_bottom { };
|
||||
double map_world_lon { };
|
||||
double map_offset { };
|
||||
|
||||
int32_t x_pos { }, y_pos { };
|
||||
int32_t prev_x_pos { 0xFFFF }, prev_y_pos { 0xFFFF };
|
||||
float lat_ { };
|
||||
float lon_ { };
|
||||
uint16_t angle_ { };
|
||||
std::string tag_ { };
|
||||
|
||||
int markerListLen {0};
|
||||
GeoMarker markerList[NumMarkerListElements];
|
||||
bool markerListUpdated {false};
|
||||
};
|
||||
|
||||
class GeoMapView : public View {
|
||||
@@ -181,6 +220,12 @@ public:
|
||||
|
||||
std::string title() const override { return "Map view"; };
|
||||
|
||||
void clear_markers();
|
||||
MapMarkerStored store_marker(GeoMarker & marker);
|
||||
|
||||
void update_tag(const std::string tag);
|
||||
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user