Compare commits

..

73 Commits

Author SHA1 Message Date
jLynx c2279e297c Stable v1.7.3 (#1224)
* Update past_version.txt

* Update version.txt
2023-06-30 20:45:20 +12:00
Mark Thompson cdd524b9f3 Use a common function for finding Tone Key index from received Tone Frequency (#1218)
* Common function for finding CTCSS tone index from freq
2023-06-30 07:37:43 +02:00
Kyle Reed 99809c7919 Consolidate anti_alias BW selection into function (#1217) 2023-06-30 07:35:35 +02:00
Kyle Reed 2390d79111 Fix up Waterfall control names (#1219) 2023-06-30 07:34:19 +02:00
Kyle Reed 3b41d73ddd Remove LTO, minor UI tweaks (#1216) 2023-06-30 07:33:33 +02:00
gullradriel 3d46c28911 fix build (#1215)
* fix build

* fixing build integrationg zoom

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-06-29 13:58:10 -07:00
Kyle Reed ca7840557b Change how scrolling works, add home/end (#1212)
Conflicts fixed by gull
2023-06-29 22:17:31 +02:00
Mark Thompson ceaa115025 Revert color of "SD over USB" icon back to yellow (#1210) 2023-06-29 22:13:33 +02:00
Mark Thompson 8dbcefbc75 Move initializer from .hpp -> .cpp file (#1213) 2023-06-29 22:07:39 +02:00
Mark Thompson b28b96fb4a Notepad text zoom support 5x8 to 8x16 font (#1211)
* Zoom button 5/8 to 8/16 font

* Zoom button 5/8 to 8/16 font

* Clang & revert unintended change

* Suggested improvements

* Suggested code changes

* Added char_width function

* Added char_width function

* Oops copy-paste error

* Delete added blank line
2023-06-29 10:55:25 -07:00
Stupid retard noob attention king e15a8ed2d8 improve keyboard (#1209)
* init commit for raw input in keyboard

* clean up

* clean up - 1

* clean up - 2

* can input underline and < now

* format fix

* textual change

* textual change - 2

* textual change - 3

* textual change - 4

* edit for PR comment

* edit for PR comment - 2

* edit for PR comment - 3

* edit for PR comment - 4

* edit for PR comment - 5
2023-06-29 09:24:18 -07:00
gullradriel 19b77bf03b fix recon start and mode change (#1208) 2023-06-28 22:21:13 +02:00
Kyle Reed 7b1541d2e8 work around calling focus from ctor path. (#1207) 2023-06-28 21:50:11 +02:00
gullradriel 830fea63ed Optimize compilation flags (#1206)
* restoring -O3 on baseband, activating lto on cpp parts
2023-06-28 21:46:49 +02:00
Kyle Reed 3d59de55f8 Remove raw new/delete from Recon (#1203) 2023-06-28 19:14:30 +02:00
Mark Thompson 5c1f5a7d2e Second DFU Overlay Screen with Receiver Info (#1204)
* 2nd DFU overlay with receiver info for debug
2023-06-28 19:04:16 +02:00
Kyle Reed 4e985420d4 Replace Replay with Playlist (#1202)
* Replace Replay with Playlist
* Suggestions from test-drive. Launch from Fileman
2023-06-28 19:02:06 +02:00
Mark Thompson 3e584a9652 Proposed debug dump formatting tweaks (#1200)
* Debug dump formatting 
* Added FW version to debug dump
2023-06-28 19:01:09 +02:00
Mark Thompson 39acaf846a Revised title bar truncation code & removed title widget shading (#1201)
* Revised title truncate & undo widget shading

* Revised title truncation & undo widget shading
2023-06-27 19:34:25 -07:00
Mark Thompson 76948cffa8 Improve visibility of green icons on white background (#1196) 2023-06-27 10:19:34 +02:00
Mark Thompson 4ea8abb53b Limit title length when too many status icons & shade background of every other icon (#1195)
* Shading alternate icon backgrounds on title bar

* Shading alternate icons on title bar

* Shading alternate icons on title bar

* Clang
2023-06-27 10:15:58 +02:00
gullradriel 32085f317b adding one more character to modulation widget for spec (#1193)
Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-06-27 10:02:04 +02:00
Kyle Reed faa4367295 Allow frequency override in Playlist, Folder fixes (#1194)
* Allow frequency to be changed per-entry

* Folder fixes, use TX freq

---------

Co-authored-by: kallanreed <kallanreed@noreply.github.com>
2023-06-26 19:44:01 +02:00
Mark Thompson 3f8a4957af Moved AK Speaker Disable back to a title bar, and modified Mute Icon to differentiate (#1192)
* Added fn to indicate support for disabling speaker
* Support both mute & speaker disable icons
* Byte grouping for ui_config2_t bitmap
* Fix disabling AK speaker to take effect immediately
2023-06-26 08:08:09 +02:00
gullradriel b5ea81cf96 fix SPEC mode in app (#1191)
Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-06-25 16:52:40 -07:00
Kyle Reed e14fa5eab0 New playlist (#1185)
* updated playlist file format
* fix trim and delay part of item
* playlist add/remove tracks
* UI Tweak
2023-06-25 20:40:19 +02:00
Mark Thompson 960ecf616c Update menu icons colors per app state & add contrast to bright icons for improved visibility when Selected (#1189)
* Orange icons vs yellow

* Update app icon colors per current development states

* Darken bright icons on white backgrounds

* Darken bright icons on white backgrounds
2023-06-25 19:48:51 +02:00
gullradriel 996ccb0e40 Recon capture mod (#1190)
* Recon: disabling config while recording
* Capture and Spectre Painter: using freqman set bandwidth option instead of static define
* Recon: light_grey for disable so we can still see the text
2023-06-25 11:33:44 +02:00
Mark Thompson 407fee23b9 Long button press support (#1188) 2023-06-25 11:32:37 +02:00
gullradriel 199570d4a5 flipper inspired freqlists for recon (#1187) 2023-06-25 11:30:09 +02:00
gullradriel 5cd9c377d2 Recon record (#1182)
* added auto record checbox and functions (audio and raw)
* Changed baseband compile option from -O3 to -O2. Trying lower gave unexpeted and crashing results.
* added 650k SPEC bw
* fix hang if wait is -100
* fixing no SPEC support in scanner
2023-06-25 08:16:49 +02:00
Kyle Reed e08273b8b8 Auto layout of top status icons (#1178)
* WIP - updated pmem

* WIP status tray auto layout

* Add settings page for icons

* Add ui_config2 to pmem dump

---------

Co-authored-by: kallanreed <kallanreed@noreply.github.com>
2023-06-23 16:13:39 -07:00
Kyle Reed 6b0f90c321 Update string_format.cpp (#1184) 2023-06-24 00:08:11 +02:00
Mark Thompson 0dea488e98 Added TouchTunes jukebox icon (#1183)
* Added TouchTunes jukebox icon

* Added TouchTunes jukebox icon

* Added TouchTunes jukebox icon
2023-06-23 15:01:19 -07:00
Mark Thompson e60c4dc34b Paint Roller icon for S.Painter & fix make_bitmap.py to generate Clang-compliant code (#1181)
* Generate Clang-compliant output file

* Paint roller icon for Screen Painter app

* Paint roller icon for Screen Painter app

* Paint roller icon for Spectrum Painter
2023-06-23 07:57:35 -07:00
gullradriel 0c75713820 using emplace_back instead of push_back
* using emplace_back instead of push_back is giving better results in regards of the memory management and crashes
* we tried with all our best ideas, we don't know why it's better but it outperformed every other allocations method tested
2023-06-22 22:26:15 +02:00
gullradriel 6a28987a36 Debug utilities (#1171)
-pmem, receiver and transmitter model dumps
2023-06-22 17:35:21 +02:00
gullradriel 7331979107 Freqman memory fix (#1177)
* Cleanings and setting the limit to a working for all apps one
* fixing a guru memory errors, renaming a leftover variable
* removed unneeded set_dirty
2023-06-22 17:34:20 +02:00
Mark Thompson f22808f8ca Pan & Zoom-in support for GeoMap (#1172)
* Zoom-in support

* Zoom-in support

* Clang formatting

* Clang formatting

* Zoom-in support

* Zoom-in support

* Zoom-in support

* Roll-back wrong file edited

* Zoom-in support

* Clang formatting

* Clang formatting

* Zoom-in support

* Zoom-in support

* Zoom-in support

* Oops copy/paste error
2023-06-21 21:56:07 +02:00
E.T e1c519d71e Frequman widget gets a non owning reference to the database instead of copying (#1175)
-Get rid of -Weffc++ pointer member warnings by restricting copy of FreqManUIList
-Add null checks
-Remove unused macro
2023-06-21 12:05:07 +02:00
Kyle Reed 909f37f89b whipcalc parsing, bug fixes (#1174)
* whipcalc parsing, bug fixes
2023-06-20 20:22:58 +02:00
Kyle Reed 63b0093321 Consolidate metadata file parsing (#1173)
* Add metadata file helper and replace old code
* Finish updating metadata read/write
2023-06-20 09:32:37 +02:00
Mark Thompson fa06df1400 Persistent audio mute support revisions (#1169)
* Don't disable DAC when other audio output is using it
* Persistent audio mute revisions
* Moved persistent audio mute code to audio.cpp
* Make "Disable AK speaker amp" take effect immediately
2023-06-19 21:06:07 +02:00
Kyle Reed f83027d451 Re-add MicTX workaround (#1170) 2023-06-19 21:05:08 +02:00
Kyle Reed 7ebf3d3cdd Fix hang in Audio when switching from Capture app, & bug fixes. (#1167)
* Fix hang in Audio when switching from Capture app
* Bug fixes
2023-06-19 07:48:29 +02:00
Kyle Reed 5daa0dfbb1 Consolidate frequency field on_edit (#1166) 2023-06-18 21:11:04 +02:00
Mark Thompson 7b669d7001 Proposed default focus changes (#1165)
* Changed default focus of pocsag, glass, scanner, sonde
2023-06-18 08:14:29 +02:00
Mark Thompson ba2407d691 Persistent audio mute support (#1161)
* Persistent speaker mute support #1100
* Moving persistent mute support from WM8731/AK4951 codec files to audio.cpp module for simplification
2023-06-17 23:15:21 +02:00
Kyle Reed 5743d3a3b9 Playlist cleanup (#1163)
* Functional rewrite of playlist
2023-06-17 22:01:46 +02:00
Kyle Reed 7f1c0f6f7e Explicitly set tx_model to make MicTX happy (#1162)
Co-authored-by: kallanreed <kallanreed@noreply.github.com>
2023-06-17 19:15:46 +02:00
Kyle Reed 650d299c99 Don't update radio directly when saving/restoring tx/rx model settings. (#1160)
* Set rf direction in RadioState

* centralize cpld load for TX->RX

* Remove calls to load_sram from all apps

* re-add CPLD hack, don't set radio values directly.

---------

Co-authored-by: kallanreed <kallanreed@noreply.github.com>
2023-06-17 07:54:52 -07:00
gullradriel 3a5c2da66c fixing bandwidth application, changed UI a bit (#1158)
* fixxing bandwidth application, moved 'continuous' from config page to main page in form of a 'loop' button. Removed looking glass button as it's not configurable to position on a range/signal
2023-06-16 07:50:04 +02:00
Kyle Reed d29826e6f2 Update playlist file parsing, reduce allocs (#1157)
* Update playlist file parsing, reduce allocs
* Cleanup, move impl to cpp
2023-06-15 22:15:26 +02:00
Kyle Reed 34fefd1cad Add file reader (#1155)
* Add file reader
* Add a simple test example of parsing settings.
* Use new FileLineReader to parse Glass presets
* Trim CRLF from Glass preset name
2023-06-15 09:45:13 +02:00
Mark Thompson a5c7eb2fbc Starting freq fixes when no App Settings, and support for App Settings in Scanner App (Mode/BW/Step only) (#1153)
* Fix initial freq when no App Settings tpms
* Default POCSAG freq when no App Settings
* Enable Scanner App Settings for Modulation/BW/Step
* Default SONDE freq when no App Settings
2023-06-14 16:54:19 +02:00
Kyle Reed 6298388fe1 Stash radio settings per app (#1151)
* add a little Stash utility.
* add radio state RAII helper.
* first part of radio_state changes
* add radio_state_ to rest of apps
* fix freq_step and format
* fix unused ui_sigfrx: corrected sample rate, added back freq setting
2023-06-14 09:57:20 +02:00
Kyle Reed 19491ce3f7 Add debug_log, fix tx_bht (#1149) 2023-06-13 08:14:26 +02:00
Kyle Reed 3db2053c21 Allows apps using app settings to use global frequency (#1148)
* Allow apps to not use persisted freq.
* use rx_freq for RX mode
* remove direct call to persistant_memory in tx_model
* app_setting defaults, and tx_view to use ctor value
2023-06-12 19:40:32 +02:00
gullradriel ccd7bd6fc2 Fixing txt_line_processing logic, adding back track number and plalist name (#1146) 2023-06-12 07:25:25 +02:00
Kyle Reed fff78ce00f Faster app_settings save (#1145)
* No-allocation settings save

* remove fixed TODO

---------

Co-authored-by: kallanreed <kallanreed@outlook.com>
2023-06-11 13:35:45 -07:00
Kyle Reed 8bd3d6249d App settings revamp (#1139)
* WIP AppSetting overhaul

* WIP migrating apps to new settings.

* remove settings, rename tuned => target

* formatting

* Minor fixes

* Fix hang on app load

* run formatter

* PR comment fixes

* Load modulation into receiver model in app_settings

* Run formatter

---------

Co-authored-by: kallanreed <kallanreed@outlook.com>
2023-06-11 11:47:13 -07:00
gullradriel f65e743c4c Recon revamp (#1144)
* fixing recon/scanner restarting while on pause and setting a manual start or end
* gui revamping, duplicated options suppressed from config menu
* recon_match_mode set at start
2023-06-11 18:49:28 +02:00
zxkmm 843880d22e playlist: fix track_number out of range and a little fix and alittle ui change (#1142)
* fix playlist track_number out of range
* implement pause
* fix for review: compatibility for old PPL grammar
2023-06-11 18:22:15 +02:00
gullradriel ec3cc7a859 Looking spectrum tune (#1143)
* finely tuned spectrum and marker precision, all modes

* hidding buttons when mode is not supported

* removing on show hide since widget implementation is buggy. Fine tuned values using signal generator to get around the corners

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-06-10 17:17:02 +02:00
Erwin Ried b1733cbd2a Merge pull request #1141 from u-foka/pmem-debug-rename
improve P.Memory naming consistency
2023-06-10 02:44:31 +02:00
Eisenberger Tamas 4ec2d64cb7 improve P.Memory naming consistency
I swear I wont touch these strings again :D
2023-06-10 00:34:27 +02:00
E.T d370dae170 increase touch sensitivity a little more (#1140)
and make the code a little more descriptive :)

Co-authored-by: Eisenberger Tamas <e.tamas@iwstudio.hu>
2023-06-09 23:45:26 +02:00
E.T 72f0a1be54 add dockerfile-nogit-arm (#1136)
works on aarc64 docker hosts

Co-authored-by: Eisenberger Tamas <e.tamas@iwstudio.hu>
2023-06-10 07:52:14 +12:00
E.T a2e5e03f07 Gcc12 related fixes (#1138)
* GCC12 related fixes but still compiles on GCC9

* Suppress warnings about volatile +=

* Enable c++20 if supported by the compiler

On gcc12 we need to use -std=c++20 since constexpr .c_str() on std::string is only officially available since c++20 and the new gcc wouldnt let us use it with older standard

* code format

---------

Co-authored-by: Eisenberger Tamas <e.tamas@iwstudio.hu>
2023-06-10 07:50:42 +12:00
E.T 47eda54d6a Format script (#1123)
* Add code formatting script

* dont forma the test framework

* use clang-format-13

---------

Co-authored-by: Eisenberger Tamas <e.tamas@iwstudio.hu>
2023-06-10 07:50:21 +12:00
gullradriel db81b9b1e1 code cleaning, fast and slow mode adjustement, dc pixels displayed (#1137)
* code cleaning, fast and slow mode adjustement, dc pixels displayed

* factorisation, comments

* adjusting offsets for alignement

* indent

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-06-09 17:13:44 +02:00
E.T b9de1918b4 fix pmem -> make backup_ram_t data members volatile (#1135)
* fix pmem -> make backup_ram_t data members volatile

plus add calculated crc to the pmem debug screen

* rename pmem debug menu to p.mem

As this is how its refered to in the wiki and other screens

* p.mem looked strange with capital P

---------

Co-authored-by: Eisenberger Tamas <e.tamas@iwstudio.hu>
2023-06-09 15:52:09 +02:00
dc2dc b87d26f271 Update HAM_70CM_BAND.TXT (#1134)
420-450 max Amateur Radio Band worldwide
2023-06-08 10:05:10 +02:00
211 changed files with 5772 additions and 3740 deletions
+1 -1
View File
@@ -1 +1 @@
v1.7.1 v1.7.2
+1 -1
View File
@@ -1 +1 @@
v1.7.2 v1.7.3
+41
View File
@@ -0,0 +1,41 @@
FROM ubuntu:xenial
# 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.
ENV ARMBINURL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-aarch64-linux.tar.bz2?revision=4583ce78-e7e7-459a-ad9f-bff8e94839f1&rev=4583ce78e7e7459aad9fbff8e94839f1&hash=C9B942D74CEA05FF9DE28380F267BB7D3F3B17A6 \
PATH=$HOME/bin:$PATH:/opt/build/armbin/bin
#Create volume /havoc/bin for compiled firmware binaries
VOLUME /havoc
WORKDIR /havoc/firmware
# Fetch dependencies from APT
RUN apt-get update && \
apt-get install -y git tar wget dfu-util cmake python3 ccache bzip2 liblz4-tool curl && \
apt-get -qy autoremove
#Install current pip from PyPa
RUN curl https://bootstrap.pypa.io/pip/3.4/get-pip.py -o get-pip.py && \
python3 get-pip.py
#Fetch additional dependencies from Python 3.x pip
RUN pip install pyyaml
RUN ln -s /usr/bin/python3 /usr/bin/python && \
ln -s /usr/bin/pip3 /usr/bin/pip
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
# Grab the GNU ARM toolchain from arm.com
# Then extract contents to /opt/build/armbin/
RUN mkdir /opt/build && cd /opt/build && \
wget -O gcc-arm-none-eabi $ARMBINURL && \
mkdir armbin && \
tar --strip=1 -xjvf gcc-arm-none-eabi -C armbin
# 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
+12 -2
View File
@@ -27,6 +27,8 @@
enable_language(C CXX ASM) enable_language(C CXX ASM)
include(CheckCXXCompilerFlag)
project(application) project(application)
# Compiler options here. # Compiler options here.
@@ -36,7 +38,13 @@ set(USE_OPT "-Os -g --specs=nano.specs")
set(USE_COPT "-std=gnu99") set(USE_COPT "-std=gnu99")
# C++ specific options here (added to USE_OPT). # C++ specific options here (added to USE_OPT).
set(USE_CPPOPT "-std=c++17 -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized") check_cxx_compiler_flag("-std=c++20" cpp20_supported)
if(cpp20_supported)
set(USE_CPPOPT "-std=c++20")
else()
set(USE_CPPOPT "-std=c++17")
endif()
set(USE_CPPOPT "${USE_CPPOPT} -fno-rtti -fno-exceptions -Weffc++ -Wuninitialized -Wno-volatile")
# Enable this if you want the linker to remove unused code and data # Enable this if you want the linker to remove unused code and data
set(USE_LINK_GC yes) set(USE_LINK_GC yes)
@@ -167,6 +175,7 @@ set(CPPSRC
#emu_cc1101.cpp #emu_cc1101.cpp
rfm69.cpp rfm69.cpp
event_m0.cpp event_m0.cpp
file_reader.cpp
file.cpp file.cpp
freqman.cpp freqman.cpp
io_file.cpp io_file.cpp
@@ -175,6 +184,7 @@ set(CPPSRC
irq_lcd_frame.cpp irq_lcd_frame.cpp
irq_rtc.cpp irq_rtc.cpp
log_file.cpp log_file.cpp
metadata_file.cpp
portapack.cpp portapack.cpp
qrcodegen.cpp qrcodegen.cpp
radio.cpp radio.cpp
@@ -277,7 +287,7 @@ set(CPPSRC
apps/ert_app.cpp apps/ert_app.cpp
apps/lge_app.cpp apps/lge_app.cpp
apps/pocsag_app.cpp apps/pocsag_app.cpp
apps/replay_app.cpp # apps/replay_app.cpp
apps/ui_playlist.cpp apps/ui_playlist.cpp
apps/gps_sim_app.cpp apps/gps_sim_app.cpp
apps/soundboard_app.cpp apps/soundboard_app.cpp
+224 -75
View File
@@ -26,86 +26,235 @@
#include "file.hpp" #include "file.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
#include <cstring> #include "utility.hpp"
#include <algorithm> #include <algorithm>
#include <cstring>
#include <string_view>
namespace std { namespace fs = std::filesystem;
using namespace portapack;
using namespace std::literals;
int app_settings::load(std::string application, AppSettings* settings) { namespace app_settings {
if (portapack::persistent_memory::load_app_settings()) {
file_path = folder + "/" + application + ".ini";
auto error = settings_file.open(file_path); template <typename T>
if (!error.is_valid()) { static void read_setting(
auto error = settings_file.read(file_content, std::min((int)settings_file.size(), MAX_FILE_CONTENT_SIZE)); std::string_view file_content,
std::string_view setting_name,
T& value) {
auto pos = file_content.find(setting_name);
settings->baseband_bandwidth = std::app_settings::read_long_long(file_content, "baseband_bandwidth="); if (pos != file_content.npos) {
settings->channel_bandwidth = std::app_settings::read_long_long(file_content, "channel_bandwidth="); pos += setting_name.length();
settings->lna = std::app_settings::read_long_long(file_content, "lna="); value = strtoll(&file_content[pos], nullptr, 10);
settings->modulation = std::app_settings::read_long_long(file_content, "modulation=");
settings->rx_amp = std::app_settings::read_long_long(file_content, "rx_amp=");
settings->rx_frequency = std::app_settings::read_long_long(file_content, "rx_frequency=");
settings->sampling_rate = std::app_settings::read_long_long(file_content, "sampling_rate=");
settings->vga = std::app_settings::read_long_long(file_content, "vga=");
settings->tx_amp = std::app_settings::read_long_long(file_content, "tx_amp=");
settings->tx_frequency = std::app_settings::read_long_long(file_content, "tx_frequency=");
settings->tx_gain = std::app_settings::read_long_long(file_content, "tx_gain=");
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;
} else
rc = SETTINGS_DISABLED;
return (rc);
}
int app_settings::save(std::string application, AppSettings* settings) {
if (portapack::persistent_memory::save_app_settings()) {
file_path = folder + "/" + application + ".ini";
make_new_directory(folder);
auto error = settings_file.create(file_path);
if (!error.is_valid()) {
// Save common setting
settings_file.write_line("baseband_bandwidth=" + to_string_dec_uint(portapack::receiver_model.baseband_bandwidth()));
settings_file.write_line("channel_bandwidth=" + to_string_dec_uint(portapack::transmitter_model.channel_bandwidth()));
settings_file.write_line("lna=" + to_string_dec_uint(portapack::receiver_model.lna()));
settings_file.write_line("rx_amp=" + to_string_dec_uint(portapack::receiver_model.rf_amp()));
settings_file.write_line("sampling_rate=" + to_string_dec_uint(portapack::receiver_model.sampling_rate()));
settings_file.write_line("tx_amp=" + to_string_dec_uint(portapack::transmitter_model.rf_amp()));
settings_file.write_line("tx_gain=" + to_string_dec_uint(portapack::transmitter_model.tx_gain()));
settings_file.write_line("vga=" + to_string_dec_uint(portapack::receiver_model.vga()));
// Save other settings from struct
settings_file.write_line("rx_frequency=" + to_string_dec_uint(settings->rx_frequency));
settings_file.write_line("tx_frequency=" + to_string_dec_uint(settings->tx_frequency));
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;
} else
rc = SETTINGS_UNABLE_TO_SAVE;
} else
rc = SETTINGS_DISABLED;
return (rc);
}
long long int app_settings::read_long_long(char* file_content, const char* setting_text) {
auto position = strstr(file_content, (char*)setting_text);
if (position) {
position += strlen((char*)setting_text);
setting_value = strtoll(position, nullptr, 10);
} }
return (setting_value);
} }
} /* namespace std */ template <typename T>
static void write_setting(File& file, std::string_view setting_name, const T& value) {
// NB: Not using file.write_line speed this up. This happens on every
// app exit when enabled so should be fast to keep the UX responsive.
StringFormatBuffer buffer;
size_t length = 0;
auto value_str = to_string_dec_uint(value, buffer, length);
file.write(setting_name.data(), setting_name.length());
file.write(value_str, length);
file.write("\r\n", 2);
}
static fs::path get_settings_path(const std::string& app_name) {
return fs::path{u"/SETTINGS"} / app_name + u".ini";
}
namespace setting {
constexpr std::string_view baseband_bandwidth = "baseband_bandwidth="sv;
constexpr std::string_view sampling_rate = "sampling_rate="sv;
constexpr std::string_view lna = "lna="sv;
constexpr std::string_view vga = "vga="sv;
constexpr std::string_view rx_amp = "rx_amp="sv;
constexpr std::string_view tx_amp = "tx_amp="sv;
constexpr std::string_view tx_gain = "tx_gain="sv;
constexpr std::string_view channel_bandwidth = "channel_bandwidth="sv;
constexpr std::string_view rx_frequency = "rx_frequency="sv;
constexpr std::string_view tx_frequency = "tx_frequency="sv;
constexpr std::string_view step = "step="sv;
constexpr std::string_view modulation = "modulation="sv;
constexpr std::string_view am_config_index = "am_config_index="sv;
constexpr std::string_view nbfm_config_index = "nbfm_config_index="sv;
constexpr std::string_view wfm_config_index = "wfm_config_index="sv;
constexpr std::string_view squelch = "squelch="sv;
constexpr std::string_view volume = "volume="sv;
} // namespace setting
// TODO: Only load/save values that are declared used.
// This will prevent switching apps from changing setting unnecessarily.
// TODO: Track which values are actually read.
// TODO: Maybe just use a dictionary which would allow for custom settings.
// TODO: Create a control value binding which will allow controls to
// be declaratively bound to a setting and persistence will be magic.
ResultCode load_settings(const std::string& app_name, AppSettings& settings) {
if (!portapack::persistent_memory::load_app_settings())
return ResultCode::SettingsDisabled;
auto file_path = get_settings_path(app_name);
auto data = File::read_file(file_path);
if (!data)
return ResultCode::LoadFailed;
if (flags_enabled(settings.mode, Mode::TX)) {
read_setting(*data, setting::tx_frequency, settings.tx_frequency);
read_setting(*data, setting::tx_amp, settings.tx_amp);
read_setting(*data, setting::tx_gain, settings.tx_gain);
read_setting(*data, setting::channel_bandwidth, settings.channel_bandwidth);
}
if (flags_enabled(settings.mode, Mode::RX)) {
read_setting(*data, setting::rx_frequency, settings.rx_frequency);
read_setting(*data, setting::rx_amp, settings.rx_amp);
read_setting(*data, setting::modulation, settings.modulation);
read_setting(*data, setting::am_config_index, settings.am_config_index);
read_setting(*data, setting::nbfm_config_index, settings.nbfm_config_index);
read_setting(*data, setting::wfm_config_index, settings.wfm_config_index);
read_setting(*data, setting::squelch, settings.squelch);
}
read_setting(*data, setting::baseband_bandwidth, settings.baseband_bandwidth);
read_setting(*data, setting::sampling_rate, settings.sampling_rate);
read_setting(*data, setting::lna, settings.lna);
read_setting(*data, setting::vga, settings.vga);
read_setting(*data, setting::step, settings.step);
read_setting(*data, setting::volume, settings.volume);
return ResultCode::Ok;
}
ResultCode save_settings(const std::string& app_name, AppSettings& settings) {
if (!portapack::persistent_memory::save_app_settings())
return ResultCode::SettingsDisabled;
File settings_file;
auto file_path = get_settings_path(app_name);
ensure_directory(file_path.parent_path());
auto error = settings_file.create(file_path);
if (error)
return ResultCode::SaveFailed;
if (flags_enabled(settings.mode, Mode::TX)) {
write_setting(settings_file, setting::tx_frequency, settings.tx_frequency);
write_setting(settings_file, setting::tx_amp, settings.tx_amp);
write_setting(settings_file, setting::tx_gain, settings.tx_gain);
write_setting(settings_file, setting::channel_bandwidth, settings.channel_bandwidth);
}
if (flags_enabled(settings.mode, Mode::RX)) {
write_setting(settings_file, setting::rx_frequency, settings.rx_frequency);
write_setting(settings_file, setting::rx_amp, settings.rx_amp);
write_setting(settings_file, setting::modulation, settings.modulation);
write_setting(settings_file, setting::am_config_index, settings.am_config_index);
write_setting(settings_file, setting::nbfm_config_index, settings.nbfm_config_index);
write_setting(settings_file, setting::wfm_config_index, settings.wfm_config_index);
write_setting(settings_file, setting::squelch, settings.squelch);
}
write_setting(settings_file, setting::baseband_bandwidth, settings.baseband_bandwidth);
write_setting(settings_file, setting::sampling_rate, settings.sampling_rate);
write_setting(settings_file, setting::lna, settings.lna);
write_setting(settings_file, setting::vga, settings.vga);
write_setting(settings_file, setting::step, settings.step);
write_setting(settings_file, setting::volume, settings.volume);
return ResultCode::Ok;
}
void copy_to_radio_model(const AppSettings& settings) {
// NB: Don't actually adjust the radio here or it will hang.
if (flags_enabled(settings.mode, Mode::TX)) {
if (!flags_enabled(settings.options, Options::UseGlobalTargetFrequency))
persistent_memory::set_target_frequency(settings.tx_frequency);
transmitter_model.configure_from_app_settings(settings);
}
if (flags_enabled(settings.mode, Mode::RX)) {
if (!flags_enabled(settings.options, Options::UseGlobalTargetFrequency))
persistent_memory::set_target_frequency(settings.rx_frequency);
receiver_model.configure_from_app_settings(settings);
receiver_model.set_configuration_without_update(
static_cast<ReceiverModel::Mode>(settings.modulation),
settings.step,
settings.am_config_index,
settings.nbfm_config_index,
settings.wfm_config_index,
settings.squelch);
}
receiver_model.set_frequency_step(settings.step);
receiver_model.set_normalized_headphone_volume(settings.volume);
}
void copy_from_radio_model(AppSettings& settings) {
if (flags_enabled(settings.mode, Mode::TX)) {
settings.tx_frequency = transmitter_model.target_frequency();
settings.baseband_bandwidth = transmitter_model.baseband_bandwidth();
settings.tx_amp = transmitter_model.rf_amp();
settings.tx_gain = transmitter_model.tx_gain();
settings.channel_bandwidth = transmitter_model.channel_bandwidth();
// TODO: Do these make sense for TX?
settings.sampling_rate = transmitter_model.sampling_rate();
settings.lna = transmitter_model.lna();
settings.vga = transmitter_model.vga();
}
if (flags_enabled(settings.mode, Mode::RX)) {
settings.rx_frequency = receiver_model.target_frequency();
settings.baseband_bandwidth = receiver_model.baseband_bandwidth();
settings.sampling_rate = receiver_model.sampling_rate();
settings.lna = receiver_model.lna();
settings.vga = receiver_model.vga();
settings.rx_amp = receiver_model.rf_amp();
settings.squelch = receiver_model.squelch_level();
settings.modulation = static_cast<uint8_t>(receiver_model.modulation());
settings.am_config_index = receiver_model.am_configuration();
settings.nbfm_config_index = receiver_model.nbfm_configuration();
settings.wfm_config_index = receiver_model.wfm_configuration();
}
settings.step = receiver_model.frequency_step();
settings.volume = receiver_model.normalized_headphone_volume();
}
/* SettingsManager *************************************************/
SettingsManager::SettingsManager(
std::string app_name,
Mode mode,
Options options)
: app_name_{std::move(app_name)},
settings_{},
loaded_{false} {
settings_.mode = mode;
settings_.options = options;
auto result = load_settings(app_name_, settings_);
if (result == ResultCode::Ok) {
loaded_ = true;
copy_to_radio_model(settings_);
}
}
SettingsManager::~SettingsManager() {
if (portapack::persistent_memory::save_app_settings()) {
copy_from_radio_model(settings_);
save_settings(app_name_, settings_);
}
}
} // namespace app_settings
+80 -37
View File
@@ -27,53 +27,96 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <utility>
#include "file.hpp" #include "file.hpp"
#include "max283x.hpp"
#include "string_format.hpp" #include "string_format.hpp"
namespace std { namespace app_settings {
class app_settings {
enum class ResultCode : uint8_t {
Ok, // settings found
LoadFailed, // settings (file) not found
SaveFailed, // unable to save settings
SettingsDisabled, // load/save disabled in settings
};
enum class Mode : uint8_t {
RX = 0x01,
TX = 0x02,
RX_TX = 0x03, // Both TX/RX
};
enum class Options {
None = 0x0000,
/* Don't use target frequency from app settings. */
UseGlobalTargetFrequency = 0x0001,
};
// TODO: separate types for TX/RX or union?
/* NB: See RX/TX model headers for default values. */
struct AppSettings {
Mode mode = Mode::RX;
Options options = Options::None;
uint32_t baseband_bandwidth = max283x::filter::bandwidth_minimum;
uint32_t sampling_rate = 3072000; // Good for 48k audio.
uint8_t lna = 32;
uint8_t vga = 32;
uint8_t rx_amp = 0;
uint8_t tx_amp = 0;
uint8_t tx_gain = 35;
uint32_t channel_bandwidth = 1;
uint32_t rx_frequency;
uint32_t tx_frequency;
uint32_t step = 25000;
uint8_t modulation = 1; // NFM
uint8_t am_config_index = 0;
uint8_t nbfm_config_index = 0;
uint8_t wfm_config_index = 0;
uint8_t squelch = 80;
uint8_t volume;
};
ResultCode load_settings(const std::string& app_name, AppSettings& settings);
ResultCode save_settings(const std::string& app_name, AppSettings& settings);
/* Copies common values to the receiver/transmitter models. */
void copy_to_radio_model(const AppSettings& settings);
/* Copies common values from the receiver/transmitter models. */
void copy_from_radio_model(AppSettings& settings);
/* RAII wrapper for automatically loading and saving settings for an app.
* NB: This should be added to a class before any LNA/VGA controls so that
* the receiver/transmitter models are set before the control ctors run. */
class SettingsManager {
public: public:
#define SETTINGS_OK 0 // settings found SettingsManager(std::string app_name, Mode mode, Options options = Options::None);
#define SETTINGS_UNABLE_TO_LOAD -1 // settings (file) not found ~SettingsManager();
#define SETTINGS_UNABLE_TO_SAVE -2 // unable to save settings
#define SETTINGS_DISABLED -3 // load/save settings disabled in settings
struct AppSettings { SettingsManager(const SettingsManager&) = delete;
uint32_t baseband_bandwidth; SettingsManager& operator=(const SettingsManager&) = delete;
uint32_t channel_bandwidth; SettingsManager(SettingsManager&&) = delete;
uint8_t lna; SettingsManager& operator=(SettingsManager&&) = delete;
uint8_t modulation;
uint8_t rx_amp;
uint32_t rx_frequency;
uint32_t sampling_rate;
uint8_t tx_amp;
uint32_t tx_frequency;
uint8_t tx_gain;
uint8_t vga;
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); /* True if settings were successfully loaded from file. */
int save(std::string application, AppSettings* settings); bool loaded() const { return loaded_; }
Mode mode() const { return settings_.mode; }
AppSettings& raw() { return settings_; }
private: private:
#define MAX_FILE_CONTENT_SIZE 1000 std::string app_name_;
AppSettings settings_;
bool loaded_;
};
char file_content[MAX_FILE_CONTENT_SIZE] = {}; } // namespace app_settings
std::string file_path = "";
std::string folder = "SETTINGS";
int rc = SETTINGS_OK;
File settings_file{};
long long int setting_value{};
long long int read_long_long(char* file_content, const char* setting_text); ENABLE_FLAGS_OPERATORS(app_settings::Mode);
ENABLE_FLAGS_OPERATORS(app_settings::Options);
}; // class app_settings
} // namespace std
#endif /*__APP_SETTINGS_H__*/ #endif /*__APP_SETTINGS_H__*/
+3 -33
View File
@@ -55,12 +55,8 @@ void ACARSLogger::log_raw_data(const acars::Packet& packet, const uint32_t frequ
namespace ui { namespace ui {
void ACARSAppView::update_freq(rf::Frequency f) { ACARSAppView::ACARSAppView(NavigationView& nav)
set_target_frequency(f); : nav_{nav} {
portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ?
}
ACARSAppView::ACARSAppView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_acars); baseband::run_image(portapack::spi_flash::image_tag_acars);
add_children({&rssi, add_children({&rssi,
@@ -72,25 +68,8 @@ ACARSAppView::ACARSAppView(NavigationView& nav) {
&check_log, &check_log,
&console}); &console});
receiver_model.set_sampling_rate(2457600);
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.enable(); receiver_model.enable();
field_frequency.set_value(receiver_model.tuning_frequency());
update_freq(receiver_model.tuning_frequency());
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());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
check_log.set_value(logging); check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) { check_log.on_select = [this](Checkbox&, bool v) {
logging = v; logging = v;
@@ -132,16 +111,7 @@ void ACARSAppView::on_packet(const acars::Packet& packet) {
// Log raw data whatever it contains // Log raw data whatever it contains
if (logger && logging) if (logger && logging)
logger->log_raw_data(packet, target_frequency()); logger->log_raw_data(packet, receiver_model.target_frequency());
}
void ACARSAppView::set_target_frequency(const uint32_t new_value) {
target_frequency_ = new_value;
receiver_model.set_tuning_frequency(new_value);
}
uint32_t ACARSAppView::target_frequency() const {
return target_frequency_;
} }
} /* namespace ui */ } /* namespace ui */
+15 -14
View File
@@ -23,10 +23,12 @@
#ifndef __ACARS_APP_H__ #ifndef __ACARS_APP_H__
#define __ACARS_APP_H__ #define __ACARS_APP_H__
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_rssi.hpp" #include "ui_rssi.hpp"
#include "log_file.hpp" #include "log_file.hpp"
#include "acars_packet.hpp" #include "acars_packet.hpp"
@@ -56,6 +58,14 @@ class ACARSAppView : public View {
std::string title() const override { return "ACARS (WIP)"; }; std::string title() const override { return "ACARS (WIP)"; };
private: private:
NavigationView& nav_;
RxRadioState radio_state_{
1750000 /* bandwidth */,
2457600 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_acars.hpp", app_settings::Mode::RX};
bool logging{false}; bool logging{false};
uint32_t packet_counter{0}; uint32_t packet_counter{0};
@@ -66,15 +76,13 @@ class ACARSAppView : public View {
VGAGainField field_vga{ VGAGainField field_vga{
{18 * 8, 0 * 16}}; {18 * 8, 0 * 16}};
RSSI rssi{ RSSI rssi{
{21 * 8, 0, 6 * 8, 4}, {21 * 8, 0, 6 * 8, 4}};
};
Channel channel{ Channel channel{
{21 * 8, 5, 6 * 8, 4}, {21 * 8, 5, 6 * 8, 4}};
};
FrequencyField field_frequency{ RxFrequencyField field_frequency{
{0 * 8, 0 * 8}, {0 * 8, 0 * 8},
}; nav_};
Checkbox check_log{ Checkbox check_log{
{22 * 8, 21}, {22 * 8, 21},
3, 3,
@@ -86,15 +94,8 @@ class ACARSAppView : public View {
std::unique_ptr<ACARSLogger> logger{}; std::unique_ptr<ACARSLogger> logger{};
uint32_t target_frequency_{};
void update_freq(rf::Frequency f);
void on_packet(const acars::Packet& packet); void on_packet(const acars::Packet& packet);
uint32_t target_frequency() const;
void set_target_frequency(const uint32_t new_value);
MessageHandlerRegistration message_handler_packet{ MessageHandlerRegistration message_handler_packet{
Message::ID::ACARSPacket, Message::ID::ACARSPacket,
[this](Message* const p) { [this](Message* const p) {
+9 -41
View File
@@ -283,8 +283,8 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
}); });
button_done.on_select = [this](const ui::Button&) { button_done.on_select = [this](const ui::Button&) {
if (this->on_close) { if (on_close) {
this->on_close(); on_close();
} }
}; };
@@ -380,33 +380,23 @@ AISAppView::AISAppView(NavigationView& nav)
&recent_entry_detail_view, &recent_entry_detail_view,
}); });
// load app settings
auto rc = settings.load("rx_ais", &app_settings);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
target_frequency_ = app_settings.rx_frequency;
} else
target_frequency_ = initial_target_frequency;
recent_entry_detail_view.hidden(true); recent_entry_detail_view.hidden(true);
receiver_model.set_tuning_frequency(tuning_frequency()); if (!settings_.loaded())
receiver_model.set_sampling_rate(sampling_rate); receiver_model.set_target_frequency(initial_target_frequency);
receiver_model.set_baseband_bandwidth(baseband_bandwidth);
receiver_model.enable(); receiver_model.enable();
options_channel.on_change = [this](size_t, OptionsField::value_t v) { options_channel.on_change = [this](size_t, OptionsField::value_t v) {
this->on_frequency_changed(v); receiver_model.set_target_frequency(v);
}; };
options_channel.set_by_value(target_frequency()); options_channel.set_by_value(receiver_model.target_frequency());
recent_entries_view.on_select = [this](const AISRecentEntry& entry) { recent_entries_view.on_select = [this](const AISRecentEntry& entry) {
this->on_show_detail(entry); on_show_detail(entry);
}; };
recent_entry_detail_view.on_close = [this]() { recent_entry_detail_view.on_close = [this]() {
this->on_show_list(); on_show_list();
}; };
logger = std::make_unique<AISLogger>(); logger = std::make_unique<AISLogger>();
@@ -416,12 +406,7 @@ AISAppView::AISAppView(NavigationView& nav)
} }
AISAppView::~AISAppView() { AISAppView::~AISAppView() {
// save app settings
app_settings.rx_frequency = target_frequency_;
settings.save("rx_ais", &app_settings);
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }
@@ -465,21 +450,4 @@ void AISAppView::on_show_detail(const AISRecentEntry& entry) {
recent_entry_detail_view.focus(); recent_entry_detail_view.focus();
} }
void AISAppView::on_frequency_changed(const uint32_t new_target_frequency) {
set_target_frequency(new_target_frequency);
}
void AISAppView::set_target_frequency(const uint32_t new_value) {
target_frequency_ = new_value;
receiver_model.set_tuning_frequency(tuning_frequency());
}
uint32_t AISAppView::target_frequency() const {
return target_frequency_;
}
uint32_t AISAppView::tuning_frequency() const {
return target_frequency() - (sampling_rate / 4);
}
} /* namespace ui */ } /* namespace ui */
+7 -17
View File
@@ -34,6 +34,7 @@
#include "log_file.hpp" #include "log_file.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "ais_packet.hpp" #include "ais_packet.hpp"
#include "lpc43xx_cpp.hpp" #include "lpc43xx_cpp.hpp"
@@ -154,9 +155,6 @@ class AISAppView : public View {
~AISAppView(); ~AISAppView();
void set_parent_rect(const Rect new_parent_rect) override; void set_parent_rect(const Rect new_parent_rect) override;
// Prevent painting of region covered entirely by a child.
// TODO: Add flag to View that specifies view does not need to be cleared before painting.
void paint(Painter&) override{}; void paint(Painter&) override{};
void focus() override; void focus() override;
@@ -165,12 +163,13 @@ class AISAppView : public View {
private: private:
static constexpr uint32_t initial_target_frequency = 162025000; static constexpr uint32_t initial_target_frequency = 162025000;
static constexpr uint32_t sampling_rate = 2457600;
static constexpr uint32_t baseband_bandwidth = 1750000;
// app save settings RxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; 2457600 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_ais", app_settings::Mode::RX};
NavigationView& nav_; NavigationView& nav_;
@@ -225,18 +224,9 @@ class AISAppView : public View {
} }
}}; }};
uint32_t target_frequency_ = initial_target_frequency;
void on_packet(const ais::Packet& packet); void on_packet(const ais::Packet& packet);
void on_show_list(); void on_show_list();
void on_show_detail(const AISRecentEntry& entry); void on_show_detail(const AISRecentEntry& entry);
void on_frequency_changed(const uint32_t new_target_frequency);
uint32_t target_frequency() const;
void set_target_frequency(const uint32_t new_value);
uint32_t tuning_frequency() const;
}; };
} /* namespace ui */ } /* namespace ui */
+46 -104
View File
@@ -22,21 +22,19 @@
#include "analog_audio_app.hpp" #include "analog_audio_app.hpp"
#include "audio.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "file.hpp"
#include "freqman.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
using namespace portapack; #include "string_format.hpp"
using namespace tonekey;
#include "audio.hpp"
#include "file.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "string_format.hpp" #include "debug.hpp"
#include "freqman.hpp" using namespace portapack;
using namespace tonekey;
namespace ui { namespace ui {
@@ -45,8 +43,8 @@ namespace ui {
static const Style& style_options_group = Styles::bg_blue; static const Style& style_options_group = Styles::bg_blue;
AMOptionsView::AMOptionsView( AMOptionsView::AMOptionsView(
const Rect parent_rect, Rect parent_rect,
const Style* const style) const Style* style)
: View{parent_rect} { : View{parent_rect} {
set_style(style); set_style(style);
@@ -65,8 +63,8 @@ AMOptionsView::AMOptionsView(
/* NBFMOptionsView *******************************************************/ /* NBFMOptionsView *******************************************************/
NBFMOptionsView::NBFMOptionsView( NBFMOptionsView::NBFMOptionsView(
const Rect parent_rect, Rect parent_rect,
const Style* const style) const Style* style)
: View{parent_rect} { : View{parent_rect} {
set_style(style); set_style(style);
@@ -90,8 +88,8 @@ NBFMOptionsView::NBFMOptionsView(
/* WFMOptionsView *******************************************************/ /* WFMOptionsView *******************************************************/
WFMOptionsView::WFMOptionsView( WFMOptionsView::WFMOptionsView(
const Rect parent_rect, Rect parent_rect,
const Style* const style) const Style* style)
: View{parent_rect} { : View{parent_rect} {
set_style(style); set_style(style);
@@ -111,8 +109,8 @@ WFMOptionsView::WFMOptionsView(
SPECOptionsView::SPECOptionsView( SPECOptionsView::SPECOptionsView(
AnalogAudioView* view, AnalogAudioView* view,
const Rect parent_rect, Rect parent_rect,
const Style* const style) const Style* style)
: View{parent_rect} { : View{parent_rect} {
set_style(style); set_style(style);
@@ -137,6 +135,10 @@ SPECOptionsView::SPECOptionsView(
AnalogAudioView::AnalogAudioView( AnalogAudioView::AnalogAudioView(
NavigationView& nav) NavigationView& nav)
: nav_(nav) { : nav_(nav) {
// A baseband image _must_ be running before
// interacting with the waterfall view.
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
add_children({&rssi, add_children({&rssi,
&channel, &channel,
&audio, &audio,
@@ -149,35 +151,9 @@ AnalogAudioView::AnalogAudioView(
&record_view, &record_view,
&waterfall}); &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) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
receiver_model.set_rf_amp(app_settings.rx_amp);
// field_frequency.set_value(app_settings.rx_frequency);
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);
}
field_frequency.set_value(receiver_model.tuning_frequency());
// Filename Datetime and Frequency // Filename Datetime and Frequency
record_view.set_filename_date_frequency(true); record_view.set_filename_date_frequency(true);
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
field_frequency.on_show_options = [this]() { field_frequency.on_show_options = [this]() {
this->on_show_options_frequency(); this->on_show_options_frequency();
}; };
@@ -190,9 +166,12 @@ AnalogAudioView::AnalogAudioView(
this->on_show_options_rf_gain(); this->on_show_options_rf_gain();
}; };
const auto modulation = receiver_model.modulation(); auto modulation = receiver_model.modulation();
options_modulation.set_by_value(toUType(modulation)); // This app doesn't handle "Capture" mode.
if (modulation > ReceiverModel::Mode::SpectrumAnalysis)
modulation = ReceiverModel::Mode::SpectrumAnalysis;
options_modulation.set_by_value(toUType(modulation));
options_modulation.on_change = [this](size_t, OptionsField::value_t v) { options_modulation.on_change = [this](size_t, OptionsField::value_t v) {
this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v)); this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v));
}; };
@@ -205,13 +184,14 @@ AnalogAudioView::AnalogAudioView(
}; };
waterfall.on_select = [this](int32_t offset) { waterfall.on_select = [this](int32_t offset) {
field_frequency.set_value(receiver_model.tuning_frequency() + offset); field_frequency.set_value(receiver_model.target_frequency() + offset);
}; };
audio::output::start(); audio::output::start();
update_modulation(static_cast<ReceiverModel::Mode>(modulation)); // This call starts the correct baseband image to run
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation)); // and sets the radio up as necessary for the given modulation.
on_modulation_changed(modulation);
} }
size_t AnalogAudioView::get_spec_bw_index() { size_t AnalogAudioView::get_spec_bw_index() {
@@ -238,40 +218,15 @@ void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
} }
AnalogAudioView::~AnalogAudioView() { 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
// both?
audio::output::stop(); audio::output::stop();
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps if the last modulation was SPEC
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }
void AnalogAudioView::on_hide() { void AnalogAudioView::set_parent_rect(Rect new_parent_rect) {
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
waterfall.on_hide();
View::on_hide();
}
void AnalogAudioView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_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}; ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
waterfall.set_parent_rect(waterfall_rect); waterfall.set_parent_rect(waterfall_rect);
} }
@@ -279,21 +234,19 @@ void AnalogAudioView::focus() {
field_frequency.focus(); field_frequency.focus();
} }
void AnalogAudioView::on_tuning_frequency_changed(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
void AnalogAudioView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) { void AnalogAudioView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
receiver_model.set_baseband_bandwidth(bandwidth_hz); receiver_model.set_baseband_bandwidth(bandwidth_hz);
} }
void AnalogAudioView::on_modulation_changed(const ReceiverModel::Mode modulation) { void AnalogAudioView::on_modulation_changed(ReceiverModel::Mode modulation) {
// TODO: Terrible kludge because widget system doesn't notify Waterfall that // This app doesn't know what to do with "Capture" mode.
// it's being shown or hidden. if (modulation > ReceiverModel::Mode::SpectrumAnalysis)
waterfall.on_hide(); modulation = ReceiverModel::Mode::SpectrumAnalysis;
baseband::spectrum_streaming_stop();
update_modulation(modulation); update_modulation(modulation);
on_show_options_modulation(); on_show_options_modulation();
waterfall.on_show(); baseband::spectrum_streaming_start();
} }
void AnalogAudioView::remove_options_widget() { void AnalogAudioView::remove_options_widget() {
@@ -345,7 +298,7 @@ void AnalogAudioView::on_show_options_rf_gain() {
void AnalogAudioView::on_show_options_modulation() { void AnalogAudioView::on_show_options_modulation() {
std::unique_ptr<Widget> widget; std::unique_ptr<Widget> widget;
const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation()); const auto modulation = receiver_model.modulation();
switch (modulation) { switch (modulation) {
case ReceiverModel::Mode::AMAudio: case ReceiverModel::Mode::AMAudio:
widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group); widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group);
@@ -372,6 +325,7 @@ void AnalogAudioView::on_show_options_modulation() {
break; break;
default: default:
chDbgPanic("Unhandled Mode");
break; break;
} }
@@ -388,7 +342,7 @@ void AnalogAudioView::on_reference_ppm_correction_changed(int32_t v) {
persistent_memory::set_correction_ppb(v * 1000); persistent_memory::set_correction_ppb(v * 1000);
} }
void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) { void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
audio::output::mute(); audio::output::mute();
record_view.stop(); record_view.stop();
@@ -409,7 +363,8 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
image_tag = portapack::spi_flash::image_tag_wideband_spectrum; image_tag = portapack::spi_flash::image_tag_wideband_spectrum;
break; break;
default: default:
return; chDbgPanic("Unhandled Mode");
break;
} }
baseband::run_image(image_tag); baseband::run_image(image_tag);
@@ -448,23 +403,10 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
} }
} }
void AnalogAudioView::handle_coded_squelch(const uint32_t value) { void AnalogAudioView::handle_coded_squelch(uint32_t value) {
float diff, min_diff = value; tone_index idx = tone_key_index_by_value(value);
size_t min_idx{0}; if (idx >= 0)
size_t c; text_ctcss.set("CTCSS " + tone_key_string(idx));
// Find nearest match
for (c = 0; c < tone_keys.size(); c++) {
diff = abs(((float)value / 100.0) - tone_keys[c].second);
if (diff < min_diff) {
min_idx = c;
min_diff = diff;
}
}
// Arbitrary confidence threshold
if (min_diff < 40)
text_ctcss.set("CTCSS " + tone_keys[min_idx].first);
else else
text_ctcss.set("???"); text_ctcss.set("???");
} }
+22 -38
View File
@@ -26,17 +26,19 @@
#include "receiver_model.hpp" #include "receiver_model.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_spectrum.hpp" #include "ui_spectrum.hpp"
#include "ui_record_view.hpp" #include "ui_record_view.hpp"
#include "ui_styles.hpp" #include "ui_styles.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "tone_key.hpp" #include "tone_key.hpp"
namespace ui { namespace ui {
class AMOptionsView : public View { class AMOptionsView : public View {
public: public:
AMOptionsView(const Rect parent_rect, const Style* const style); AMOptionsView(Rect parent_rect, const Style* style);
private: private:
Text label_config{ Text label_config{
@@ -54,7 +56,7 @@ class AMOptionsView : public View {
class NBFMOptionsView : public View { class NBFMOptionsView : public View {
public: public:
NBFMOptionsView(const Rect parent_rect, const Style* const style); NBFMOptionsView(Rect parent_rect, const Style* style);
private: private:
Text label_config{ Text label_config{
@@ -82,7 +84,7 @@ class NBFMOptionsView : public View {
class WFMOptionsView : public View { class WFMOptionsView : public View {
public: public:
WFMOptionsView(const Rect parent_rect, const Style* const style); WFMOptionsView(Rect parent_rect, const Style* style);
private: private:
Text label_config{ Text label_config{
@@ -101,7 +103,7 @@ class AnalogAudioView;
class SPECOptionsView : public View { class SPECOptionsView : public View {
public: public:
SPECOptionsView(AnalogAudioView* view, const Rect parent_rect, const Style* const style); SPECOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
private: private:
Text label_config{ Text label_config{
@@ -138,10 +140,7 @@ class AnalogAudioView : public View {
AnalogAudioView(NavigationView& nav); AnalogAudioView(NavigationView& nav);
~AnalogAudioView(); ~AnalogAudioView();
void on_hide() override; void set_parent_rect(Rect new_parent_rect) override;
void set_parent_rect(const Rect new_parent_rect) override;
void focus() override; void focus() override;
std::string title() const override { return "Audio RX"; }; std::string title() const override { return "Audio RX"; };
@@ -155,9 +154,11 @@ class AnalogAudioView : public View {
private: private:
static constexpr ui::Dim header_height = 3 * 16; static constexpr ui::Dim header_height = 3 * 16;
// app save settings NavigationView& nav_;
std::app_settings settings{}; RxRadioState radio_state_{};
std::app_settings::AppSettings app_settings{}; app_settings::SettingsManager settings_{
"rx_audio", app_settings::Mode::RX,
app_settings::Options::UseGlobalTargetFrequency};
const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16}; const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16};
const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16}; const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16};
@@ -166,24 +167,18 @@ class AnalogAudioView : public View {
uint32_t spec_bw = 20000000; uint32_t spec_bw = 20000000;
uint16_t spec_trigger = 63; uint16_t spec_trigger = 63;
NavigationView& nav_;
// bool exit_on_squelch { false };
RSSI rssi{ RSSI rssi{
{21 * 8, 0, 6 * 8, 4}, {21 * 8, 0, 6 * 8, 4}};
};
Channel channel{ Channel channel{
{21 * 8, 5, 6 * 8, 4}, {21 * 8, 5, 6 * 8, 4}};
};
Audio audio{ Audio audio{
{21 * 8, 10, 6 * 8, 4}, {21 * 8, 10, 6 * 8, 4}};
};
FrequencyField field_frequency{ RxFrequencyField field_frequency{
{5 * 8, 0 * 16}, {5 * 8, 0 * 16},
}; nav_};
LNAGainField field_lna{ LNAGainField field_lna{
{15 * 8, 0 * 16}}; {15 * 8, 0 * 16}};
@@ -218,37 +213,26 @@ class AnalogAudioView : public View {
4096, 4096,
4}; 4};
spectrum::WaterfallWidget waterfall{true}; spectrum::WaterfallView waterfall{true};
void on_tuning_frequency_changed(rf::Frequency f);
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
void on_modulation_changed(const ReceiverModel::Mode modulation); void on_modulation_changed(ReceiverModel::Mode modulation);
void on_show_options_frequency(); void on_show_options_frequency();
void on_show_options_rf_gain(); void on_show_options_rf_gain();
void on_show_options_modulation(); void on_show_options_modulation();
void on_frequency_step_changed(rf::Frequency f); void on_frequency_step_changed(rf::Frequency f);
void on_reference_ppm_correction_changed(int32_t v); void on_reference_ppm_correction_changed(int32_t v);
void on_edit_frequency();
void remove_options_widget(); void remove_options_widget();
void set_options_widget(std::unique_ptr<Widget> new_widget); void set_options_widget(std::unique_ptr<Widget> new_widget);
void update_modulation(const ReceiverModel::Mode modulation); void update_modulation(ReceiverModel::Mode modulation);
// void squelched(); void handle_coded_squelch(uint32_t value);
void handle_coded_squelch(const uint32_t value);
/*MessageHandlerRegistration message_handler_squelch_signal {
Message::ID::RequestSignal,
[this](const Message* const p) {
(void)p;
this->squelched();
}
};*/
MessageHandlerRegistration message_handler_coded_squelch{ MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch, Message::ID::CodedSquelch,
[this](const Message* const p) { [this](const Message* p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p); const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value); this->handle_coded_squelch(message.value);
}}; }};
+1 -36
View File
@@ -56,31 +56,6 @@ AnalogTvView::AnalogTvView(
&field_volume, &field_volume,
&tv}); &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);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
receiver_model.set_rf_amp(app_settings.rx_amp);
field_frequency.set_value(app_settings.rx_frequency);
} else
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
field_frequency.on_show_options = [this]() { field_frequency.on_show_options = [this]() {
this->on_show_options_frequency(); this->on_show_options_frequency();
}; };
@@ -103,7 +78,7 @@ AnalogTvView::AnalogTvView(
}; };
tv.on_select = [this](int32_t offset) { tv.on_select = [this](int32_t offset) {
field_frequency.set_value(receiver_model.tuning_frequency() + offset); field_frequency.set_value(receiver_model.target_frequency() + offset);
}; };
update_modulation(static_cast<ReceiverModel::Mode>(modulation)); update_modulation(static_cast<ReceiverModel::Mode>(modulation));
@@ -111,12 +86,6 @@ AnalogTvView::AnalogTvView(
} }
AnalogTvView::~AnalogTvView() { AnalogTvView::~AnalogTvView() {
// save app settings
app_settings.rx_frequency = field_frequency.value();
settings.save("rx_tv", &app_settings);
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both?
audio::output::stop(); audio::output::stop();
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
@@ -140,10 +109,6 @@ void AnalogTvView::focus() {
field_frequency.focus(); field_frequency.focus();
} }
void AnalogTvView::on_tuning_frequency_changed(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
void AnalogTvView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) { void AnalogTvView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
receiver_model.set_baseband_bandwidth(bandwidth_hz); receiver_model.set_baseband_bandwidth(bandwidth_hz);
} }
+11 -15
View File
@@ -27,10 +27,12 @@
#include "receiver_model.hpp" #include "receiver_model.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_tv.hpp" #include "ui_tv.hpp"
#include "ui_record_view.hpp" #include "ui_record_view.hpp"
#include "ui_styles.hpp" #include "ui_styles.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "tone_key.hpp" #include "tone_key.hpp"
@@ -52,30 +54,26 @@ class AnalogTvView : public View {
private: private:
static constexpr ui::Dim header_height = 3 * 16; static constexpr ui::Dim header_height = 3 * 16;
// app save settings NavigationView& nav_;
std::app_settings settings{}; RxRadioState radio_state_{};
std::app_settings::AppSettings app_settings{}; app_settings::SettingsManager settings_{
"rx_tv", app_settings::Mode::RX};
const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16}; const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16};
const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16}; const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16};
NavigationView& nav_;
RSSI rssi{ RSSI rssi{
{21 * 8, 0, 6 * 8, 4}, {21 * 8, 0, 6 * 8, 4}};
};
Channel channel{ Channel channel{
{21 * 8, 5, 6 * 8, 4}, {21 * 8, 5, 6 * 8, 4}};
};
Audio audio{ Audio audio{
{21 * 8, 10, 6 * 8, 4}, {21 * 8, 10, 6 * 8, 4}};
};
FrequencyField field_frequency{ RxFrequencyField field_frequency{
{5 * 8, 0 * 16}, {5 * 8, 0 * 16},
}; nav_};
LNAGainField field_lna{ LNAGainField field_lna{
{15 * 8, 0 * 16}}; {15 * 8, 0 * 16}};
@@ -99,7 +97,6 @@ class AnalogTvView : public View {
tv::TVWidget tv{}; tv::TVWidget tv{};
void on_tuning_frequency_changed(rf::Frequency f);
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
void on_modulation_changed(const ReceiverModel::Mode modulation); void on_modulation_changed(const ReceiverModel::Mode modulation);
void on_show_options_frequency(); void on_show_options_frequency();
@@ -107,7 +104,6 @@ class AnalogTvView : public View {
void on_show_options_modulation(); void on_show_options_modulation();
void on_frequency_step_changed(rf::Frequency f); void on_frequency_step_changed(rf::Frequency f);
void on_reference_ppm_correction_changed(int32_t v); void on_reference_ppm_correction_changed(int32_t v);
void on_edit_frequency();
void remove_options_widget(); void remove_options_widget();
void set_options_widget(std::unique_ptr<Widget> new_widget); void set_options_widget(std::unique_ptr<Widget> new_widget);
+14 -77
View File
@@ -21,15 +21,16 @@
*/ */
#include "capture_app.hpp" #include "capture_app.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "portapack.hpp" #include "portapack.hpp"
using namespace portapack; using namespace portapack;
namespace ui { namespace ui {
CaptureAppView::CaptureAppView(NavigationView& nav) { CaptureAppView::CaptureAppView(NavigationView& nav)
: nav_{nav} {
freqman_set_bandwidth_option(SPEC_MODULATION, option_bandwidth);
baseband::run_image(portapack::spi_flash::image_tag_capture); baseband::run_image(portapack::spi_flash::image_tag_capture);
add_children({ add_children({
@@ -46,26 +47,6 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
&waterfall, &waterfall,
}); });
// Hack for initialization
// TODO: This should be included in a more global section so apps dont need to do it
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
//-------------------
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
field_frequency_step.set_by_value(receiver_model.frequency_step()); field_frequency_step.set_by_value(receiver_model.frequency_step());
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) { field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
receiver_model.set_frequency_step(v); receiver_model.set_frequency_step(v);
@@ -74,55 +55,22 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
option_bandwidth.on_change = [this](size_t, uint32_t base_rate) { option_bandwidth.on_change = [this](size_t, uint32_t base_rate) {
sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side
/* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */ /* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/ /* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz, when selected 1 Mhz BW ... */
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */ /* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
waterfall.on_hide(); waterfall.on_hide();
/* Set up proper anti aliasing BPF bandwith in MAX2837 before ADC sampling according to the new added BW Options. */
auto anti_alias_baseband_bandwidth_filter = filter_bandwidth_for_sampling_rate(sampling_rate);
record_view.set_sampling_rate(sampling_rate); record_view.set_sampling_rate(sampling_rate);
receiver_model.set_sampling_rate(sampling_rate); receiver_model.set_sampling_rate(sampling_rate);
/* Set up proper anti aliasing BPF bandwith in MAX2837 before ADC sampling according to the new added BW Options . */
switch (sampling_rate) { // we use the var fs (sampling_rate) , to set up BPF aprox < fs_max/2 by Nyquist theorem.
case 0 ... 2000000: // BW Captured range (0 <= 250kHz max ) fs = 8 x 250 kHz
anti_alias_baseband_bandwidth_filter = 1750000; // Minimum BPF MAX2837 for all those lower BW options.
break;
case 4000000 ... 6000000: // BW capture range (500k ... 750kHz max ) fs_max = 8 x 750kHz = 6Mhz
// BW 500k ... 750kHz , ex. 500kHz (fs = 8*BW = 4Mhz) , BW 600kHz (fs = 4,8Mhz) , BW 750 kHz (fs = 6Mhz)
anti_alias_baseband_bandwidth_filter = 2500000; // in some IC MAX2837 appear 2250000 , but both works similar.
break;
case 8800000: // BW capture 1,1Mhz fs = 8 x 1,1Mhz = 8,8Mhz . (1Mhz showed slightly higher noise background).
anti_alias_baseband_bandwidth_filter = 3500000;
break;
case 14000000: // BW capture 1,75Mhz , fs = 8 x 1,75Mhz = 14Mhz
// good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
anti_alias_baseband_bandwidth_filter = 5000000;
break;
case 16000000: // BW capture 2Mhz , fs = 8 x 2Mhz = 16Mhz
// good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
anti_alias_baseband_bandwidth_filter = 6000000;
break;
case 20000000: // BW capture 2,5Mhz , fs= 8 x 2,5 Mhz = 20Mhz
// good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
anti_alias_baseband_bandwidth_filter = 7000000;
break;
default: // BW capture 2,75Mhz, fs = 8 x 2,75Mhz= 22Mhz max ADC sampling) and others.
// We tested also 9Mhz FPB stightly too much noise floor , better 8Mhz
anti_alias_baseband_bandwidth_filter = 8000000;
}
receiver_model.set_baseband_bandwidth(anti_alias_baseband_bandwidth_filter); receiver_model.set_baseband_bandwidth(anti_alias_baseband_bandwidth_filter);
waterfall.on_show(); waterfall.on_show();
}; };
option_bandwidth.set_selected_index(7); // 500k, Preselected starting default option 500kHz option_bandwidth.set_selected_index(7); // Preselected default option 500kHz.
receiver_model.set_modulation(ReceiverModel::Mode::Capture); receiver_model.set_modulation(ReceiverModel::Mode::Capture);
receiver_model.enable(); receiver_model.enable();
@@ -133,20 +81,13 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
} }
CaptureAppView::~CaptureAppView() { CaptureAppView::~CaptureAppView() {
// Hack for preventing halting other apps // Most other apps can't handle "Capture" mode, set to something standard.
// TODO: This should be also part of something global
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
// ----------------------------
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }
void CaptureAppView::on_hide() { void CaptureAppView::on_hide() {
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
waterfall.on_hide(); waterfall.on_hide();
View::on_hide(); View::on_hide();
} }
@@ -154,7 +95,7 @@ void CaptureAppView::on_hide() {
void CaptureAppView::set_parent_rect(const Rect new_parent_rect) { void CaptureAppView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_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}; ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
waterfall.set_parent_rect(waterfall_rect); waterfall.set_parent_rect(waterfall_rect);
} }
@@ -162,8 +103,4 @@ void CaptureAppView::focus() {
record_view.focus(); record_view.focus();
} }
void CaptureAppView::on_tuning_frequency_changed(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
} /* namespace ui */ } /* namespace ui */
+18 -33
View File
@@ -26,25 +26,12 @@
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_record_view.hpp" #include "ui_record_view.hpp"
#include "ui_spectrum.hpp" #include "ui_spectrum.hpp"
#include "app_settings.hpp"
#define BW_OPTIONS \ #include "radio_state.hpp"
{" 8k5", 8500}, \ #include "freqman.hpp"
{" 11k", 11000}, \
{" 16k", 16000}, \
{" 25k", 25000}, \
{" 50k", 50000}, \
{" 100k", 100000}, \
{" 250k", 250000}, \
{" 500k", 500000}, /* Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.*/ \
{" 600k", 600000}, /* That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec) */ \
{" 750k", 750000}, /* From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size) */ \
{"1100k", 1100000}, \
{"1750k", 1750000}, \
{"2000k", 2000000}, \
{"2500k", 2500000}, \
{"2750k", 2750000}, // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC)
namespace ui { namespace ui {
@@ -54,40 +41,38 @@ class CaptureAppView : public View {
~CaptureAppView(); ~CaptureAppView();
void on_hide() override; void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override;
void focus() override; void focus() override;
void set_parent_rect(const Rect new_parent_rect) override;
std::string title() const override { return "Capture"; }; std::string title() const override { return "Capture"; };
private: private:
static constexpr ui::Dim header_height = 3 * 16; static constexpr ui::Dim header_height = 3 * 16;
uint32_t sampling_rate = 0; NavigationView& nav_;
uint32_t anti_alias_baseband_bandwidth_filter = 2500000; // we rename the previous var , and change type static constexpr to normal var. RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_capture", app_settings::Mode::RX,
app_settings::Options::UseGlobalTargetFrequency};
void on_tuning_frequency_changed(rf::Frequency f); uint32_t sampling_rate = 0;
Labels labels{ Labels labels{
{{0 * 8, 1 * 16}, "Rate:", Color::light_grey()}, {{0 * 8, 1 * 16}, "Rate:", Color::light_grey()},
}; };
RSSI rssi{ RSSI rssi{
{24 * 8, 0, 6 * 8, 4}, {24 * 8, 0, 6 * 8, 4}};
};
Channel channel{ Channel channel{
{24 * 8, 5, 6 * 8, 4}, {24 * 8, 5, 6 * 8, 4}};
};
FrequencyField field_frequency{ RxFrequencyField field_frequency{
{0 * 8, 0 * 16}, {0 * 8, 0 * 16},
}; nav_};
FrequencyStepView field_frequency_step{ FrequencyStepView field_frequency_step{
{10 * 8, 0 * 16}, {10 * 8, 0 * 16}};
};
RFAmpField field_rf_amp{ RFAmpField field_rf_amp{
{16 * 8, 0 * 16}}; {16 * 8, 0 * 16}};
@@ -101,7 +86,7 @@ class CaptureAppView : public View {
OptionsField option_bandwidth{ OptionsField option_bandwidth{
{5 * 8, 1 * 16}, {5 * 8, 1 * 16},
5, 5,
{BW_OPTIONS}}; {}};
RecordView record_view{ RecordView record_view{
{0 * 8, 2 * 16, 30 * 8, 1 * 16}, {0 * 8, 2 * 16, 30 * 8, 1 * 16},
@@ -111,7 +96,7 @@ class CaptureAppView : public View {
16384, 16384,
3}; 3};
spectrum::WaterfallWidget waterfall{}; spectrum::WaterfallView waterfall{};
}; };
} /* namespace ui */ } /* namespace ui */
+2 -14
View File
@@ -110,17 +110,9 @@ ERTAppView::ERTAppView(NavigationView&) {
&recent_entries_view, &recent_entries_view,
}); });
// load app settings if (!settings_.loaded())
auto rc = settings.load("rx_ert", &app_settings); receiver_model.set_target_frequency(initial_target_frequency);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
}
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(); receiver_model.enable();
logger = std::make_unique<ERTLogger>(); logger = std::make_unique<ERTLogger>();
@@ -130,11 +122,7 @@ ERTAppView::ERTAppView(NavigationView&) {
} }
ERTAppView::~ERTAppView() { ERTAppView::~ERTAppView() {
// save app settings
settings.save("rx_ert", &app_settings);
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }
+9 -7
View File
@@ -29,6 +29,7 @@
#include "event_m0.hpp" #include "event_m0.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "log_file.hpp" #include "log_file.hpp"
#include "ert_packet.hpp" #include "ert_packet.hpp"
@@ -108,10 +109,6 @@ using ERTRecentEntriesView = RecentEntriesView<ERTRecentEntries>;
class ERTAppView : public View { class ERTAppView : public View {
public: public:
static constexpr uint32_t initial_target_frequency = 911600000;
static constexpr uint32_t sampling_rate = 4194304;
static constexpr uint32_t baseband_bandwidth = 2500000;
ERTAppView(NavigationView& nav); ERTAppView(NavigationView& nav);
~ERTAppView(); ~ERTAppView();
@@ -126,12 +123,17 @@ class ERTAppView : public View {
std::string title() const override { return "ERT RX"; }; std::string title() const override { return "ERT RX"; };
private: private:
static constexpr uint32_t initial_target_frequency = 911600000;
ERTRecentEntries recent{}; ERTRecentEntries recent{};
std::unique_ptr<ERTLogger> logger{}; std::unique_ptr<ERTLogger> logger{};
// app save settings RxRadioState radio_state_{
std::app_settings settings{}; 2500000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; 4194304 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_ert", app_settings::Mode::RX};
const RecentEntriesColumns columns{{ const RecentEntriesColumns columns{{
{"ID", 10}, {"ID", 10},
+31 -72
View File
@@ -26,13 +26,15 @@
#include "ui_fileman.hpp" #include "ui_fileman.hpp"
#include "io_file.hpp" #include "io_file.hpp"
#include "metadata_file.hpp"
#include "utility.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "cpld_update.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
using namespace portapack; using namespace portapack;
namespace fs = std::filesystem;
namespace ui { namespace ui {
@@ -40,51 +42,38 @@ void GpsSimAppView::set_ready() {
ready_signal = true; ready_signal = true;
} }
void GpsSimAppView::on_file_changed(std::filesystem::path new_file_path) { void GpsSimAppView::on_file_changed(const fs::path& new_file_path) {
File data_file, info_file; file_path = fs::path(u"/") + new_file_path;
char file_data[257]; File::Size file_size{};
// Get file size { // Get the size of the data file.
auto data_open_error = data_file.open("/" + new_file_path.string()); File data_file;
if (data_open_error.is_valid()) { auto error = data_file.open(file_path);
file_error(); if (error) {
return; file_error();
} return;
file_path = new_file_path;
// Get original record frequency if available
std::filesystem::path info_file_path = file_path;
info_file_path.replace_extension(u".TXT");
sample_rate = 500000;
auto info_open_error = info_file.open("/" + info_file_path.string());
if (!info_open_error.is_valid()) {
memset(file_data, 0, 257);
auto read_size = info_file.read(file_data, 256);
if (!read_size.is_error()) {
auto pos1 = strstr(file_data, "center_frequency=");
if (pos1) {
pos1 += 17;
field_frequency.set_value(strtoll(pos1, nullptr, 10));
}
auto pos2 = strstr(file_data, "sample_rate=");
if (pos2) {
pos2 += 12;
sample_rate = strtoll(pos2, nullptr, 10);
}
} }
file_size = data_file.size();
} }
// Get original record frequency if available.
auto metadata_path = get_metadata_path(file_path);
auto metadata = read_metadata_file(metadata_path);
if (metadata) {
field_frequency.set_value(metadata->center_frequency);
sample_rate = metadata->sample_rate;
} else {
sample_rate = 500000;
}
// UI Fixup.
text_sample_rate.set(unit_auto_scale(sample_rate, 3, 1) + "Hz"); text_sample_rate.set(unit_auto_scale(sample_rate, 3, 1) + "Hz");
auto file_size = data_file.size();
auto duration = (file_size * 1000) / (1 * 2 * sample_rate);
progressbar.set_max(file_size / 1024); progressbar.set_max(file_size / 1024);
text_filename.set(file_path.filename().string().substr(0, 12)); text_filename.set(truncate(file_path.filename().string(), 12));
auto duration = ms_duration(file_size, sample_rate, 2);
text_duration.set(to_string_time_ms(duration)); text_duration.set(to_string_time_ms(duration));
button_play.focus(); button_play.focus();
@@ -129,7 +118,6 @@ void GpsSimAppView::start() {
if (reader) { if (reader) {
button_play.set_bitmap(&bitmap_stop); button_play.set_bitmap(&bitmap_stop);
baseband::set_sample_rate(sample_rate);
replay_thread = std::make_unique<ReplayThread>( replay_thread = std::make_unique<ReplayThread>(
std::move(reader), std::move(reader),
@@ -142,7 +130,6 @@ void GpsSimAppView::start() {
} }
transmitter_model.set_sampling_rate(sample_rate); transmitter_model.set_sampling_rate(sample_rate);
transmitter_model.set_baseband_bandwidth(baseband_bandwidth);
transmitter_model.enable(); transmitter_model.enable();
} }
@@ -183,26 +170,12 @@ GpsSimAppView::GpsSimAppView(
&text_duration, &text_duration,
&progressbar, &progressbar,
&field_frequency, &field_frequency,
&tx_view, // now it handles previous rfgain , rfamp. &tx_view, // now it handles previous rfgain, rfamp.
&check_loop, &check_loop,
&button_play, &button_play,
&waterfall, &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); field_frequency.set_step(5000);
button_play.on_select = [this](ImageButton&) { button_play.on_select = [this](ImageButton&) {
@@ -219,8 +192,7 @@ GpsSimAppView::GpsSimAppView(
GpsSimAppView::~GpsSimAppView() { GpsSimAppView::~GpsSimAppView() {
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
void GpsSimAppView::on_hide() { void GpsSimAppView::on_hide() {
@@ -239,17 +211,4 @@ void GpsSimAppView::set_parent_rect(const Rect new_parent_rect) {
waterfall.set_parent_rect(waterfall_rect); waterfall.set_parent_rect(waterfall_rect);
} }
void GpsSimAppView::on_target_frequency_changed(rf::Frequency f) {
set_target_frequency(f);
}
void GpsSimAppView::set_target_frequency(const rf::Frequency new_value) {
persistent_memory::set_tuned_frequency(new_value);
;
}
rf::Frequency GpsSimAppView::target_frequency() const {
return persistent_memory::tuned_frequency();
}
} /* namespace ui */ } /* namespace ui */
+14 -9
View File
@@ -27,15 +27,18 @@
#define SHORT_UI true #define SHORT_UI true
#define NORMAL_UI false #define NORMAL_UI false
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "replay_thread.hpp" #include "replay_thread.hpp"
#include "ui_spectrum.hpp" #include "ui_spectrum.hpp"
#include "ui_transmitter.hpp"
#include <string> #include <string>
#include <memory> #include <memory>
#include "ui_transmitter.hpp"
namespace ui { namespace ui {
@@ -52,6 +55,12 @@ class GpsSimAppView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
RxRadioState radio_state_{
3000000 /* bandwidth */,
500000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_gps", app_settings::Mode::TX};
static constexpr ui::Dim header_height = 3 * 16; static constexpr ui::Dim header_height = 3 * 16;
@@ -62,13 +71,9 @@ class GpsSimAppView : public View {
const size_t read_size{16384}; const size_t read_size{16384};
const size_t buffer_count{3}; const size_t buffer_count{3};
void on_file_changed(std::filesystem::path new_file_path); void on_file_changed(const std::filesystem::path& new_file_path);
void on_target_frequency_changed(rf::Frequency f);
void on_tx_progress(const uint32_t progress); 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 toggle();
void start(); void start();
void stop(const bool do_loop); void stop(const bool do_loop);
@@ -98,9 +103,9 @@ class GpsSimAppView : public View {
ProgressBar progressbar{ ProgressBar progressbar{
{18 * 8, 1 * 16, 12 * 8, 16}}; {18 * 8, 1 * 16, 12 * 8, 16}};
FrequencyField field_frequency{ TxFrequencyField field_frequency{
{0 * 8, 2 * 16}, {0 * 8, 2 * 16},
}; nav_};
TransmitterView2 tx_view{ TransmitterView2 tx_view{
// new handling of NumberField field_rfgain, NumberField field_rfamp // new handling of NumberField field_rfgain, NumberField field_rfamp
@@ -119,7 +124,7 @@ class GpsSimAppView : public View {
Color::green(), Color::green(),
Color::black()}; Color::black()};
spectrum::WaterfallWidget waterfall{}; spectrum::WaterfallView waterfall{};
MessageHandlerRegistration message_handler_replay_thread_error{ MessageHandlerRegistration message_handler_replay_thread_error{
Message::ID::ReplayThreadDone, Message::ID::ReplayThreadDone,
+5 -21
View File
@@ -27,7 +27,6 @@
#include "lge_app.hpp" #include "lge_app.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "ui_textentry.hpp" #include "ui_textentry.hpp"
#include "string_format.hpp" #include "string_format.hpp"
@@ -44,13 +43,8 @@ void LGEView::focus() {
} }
LGEView::~LGEView() { LGEView::~LGEView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_lge", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . baseband::shutdown();
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) { void LGEView::generate_lge_frame(const uint8_t command, const uint16_t address_a, const uint16_t address_b, std::vector<uint8_t>& data) {
@@ -243,12 +237,11 @@ void LGEView::generate_frame_collier() {
void LGEView::start_tx() { void LGEView::start_tx() {
if (tx_mode == ALL) { if (tx_mode == ALL) {
transmitter_model.set_tuning_frequency(channels[channel_index]); transmitter_model.set_target_frequency(channels[channel_index]);
tx_view.on_show(); // Refresh tuning frequency display tx_view.on_show(); // Refresh tuning frequency display
tx_view.set_dirty(); tx_view.set_dirty();
} }
transmitter_model.set_sampling_rate(2280000);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
chThdSleep(100); chThdSleep(100);
@@ -306,15 +299,6 @@ LGEView::LGEView(NavigationView& nav) {
&console, &console,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_lge", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
field_room.set_value(1); field_room.set_value(1);
field_team.set_value(1); field_team.set_value(1);
field_player.set_value(1); field_player.set_value(1);
@@ -333,9 +317,9 @@ LGEView::LGEView(NavigationView& nav) {
}; };
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
receiver_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+7 -3
View File
@@ -30,6 +30,7 @@
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
namespace ui { namespace ui {
@@ -49,9 +50,12 @@ class LGEView : public View {
ALL ALL
}; };
// app save settings TxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; 2280000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_lge", app_settings::Mode::TX};
tx_modes tx_mode = IDLE; tx_modes tx_mode = IDLE;
+21 -62
View File
@@ -51,14 +51,8 @@ void POCSAGLogger::log_decoded(
namespace ui { namespace ui {
void POCSAGAppView::update_freq(rf::Frequency f) { POCSAGAppView::POCSAGAppView(NavigationView& nav)
set_target_frequency(f); : nav_{nav} {
portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ?
}
POCSAGAppView::POCSAGAppView(NavigationView& nav) {
uint32_t ignore_address;
baseband::run_image(portapack::spi_flash::image_tag_pocsag); baseband::run_image(portapack::spi_flash::image_tag_pocsag);
add_children({&rssi, add_children({&rssi,
@@ -74,44 +68,17 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
&sym_ignore, &sym_ignore,
&console}); &console});
// Set on_change before initialising the field if (!settings_.loaded())
field_frequency.on_change = [this](rf::Frequency f) { field_frequency.set_value(initial_target_frequency);
update_freq(f);
};
// load app settings TODO: needed?
auto rc = settings.load("rx_pocsag", &app_settings);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
field_frequency.set_value(app_settings.rx_frequency);
} else {
field_lna.set_value(receiver_model.lna());
field_vga.set_value(receiver_model.vga());
field_rf_amp.set_value(receiver_model.rf_amp());
field_frequency.set_value(receiver_model.tuning_frequency());
}
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.enable(); receiver_model.enable();
field_frequency.set_step(receiver_model.frequency_step()); // TODO: app setting instead?
field_frequency.on_edit = [this, &nav]() { uint32_t ignore_address;
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
// TODO app setting instead?
ignore_address = persistent_memory::pocsag_ignore_address(); ignore_address = persistent_memory::pocsag_ignore_address();
// TODO is this common enough for a helper? // TODO: is this common enough for a helper?
for (size_t c = 0; c < 7; c++) { for (size_t c = 0; c < 7; c++) {
sym_ignore.set_sym(6 - c, ignore_address % 10); sym_ignore.set_sym(6 - c, ignore_address % 10);
ignore_address /= 10; ignore_address /= 10;
@@ -122,16 +89,15 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
logger->append(LOG_ROOT_DIR "/POCSAG.TXT"); logger->append(LOG_ROOT_DIR "/POCSAG.TXT");
audio::output::start(); audio::output::start();
audio::output::unmute();
baseband::set_pocsag(); baseband::set_pocsag();
} }
POCSAGAppView::~POCSAGAppView() { void POCSAGAppView::focus() {
// save app settings field_frequency.focus();
app_settings.rx_frequency = field_frequency.value(); }
settings.save("rx_pocsag", &app_settings);
POCSAGAppView::~POCSAGAppView() {
audio::output::stop(); audio::output::stop();
// Save ignored address // Save ignored address
@@ -177,9 +143,10 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
console.write(console_info); console.write(console_info);
if (logger && logging()) { if (logger && logging()) {
logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) + logger->log_decoded(
" F" + to_string_dec_uint(pocsag_state.function) + message->packet, to_string_dec_uint(pocsag_state.address) +
" Address only"); " F" + to_string_dec_uint(pocsag_state.function) +
" Address only");
} }
last_address = pocsag_state.address; last_address = pocsag_state.address;
@@ -196,25 +163,17 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
} }
if (logger && logging()) if (logger && logging())
logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) + logger->log_decoded(
" F" + to_string_dec_uint(pocsag_state.function) + message->packet, to_string_dec_uint(pocsag_state.address) +
" Alpha: " + pocsag_state.output); " F" + to_string_dec_uint(pocsag_state.function) +
" Alpha: " + pocsag_state.output);
} }
} }
// TODO: make setting. // TODO: make setting.
// Log raw data whatever it contains // Log raw data whatever it contains
if (logger && logging()) /*if (logger && logging())
logger->log_raw_data(message->packet, target_frequency()); logger->log_raw_data(message->packet, receiver_model.target_frequency());*/
}
void POCSAGAppView::set_target_frequency(const uint32_t new_value) {
target_frequency_ = new_value;
receiver_model.set_tuning_frequency(new_value);
}
uint32_t POCSAGAppView::target_frequency() const {
return target_frequency_;
} }
} /* namespace ui */ } /* namespace ui */
+12 -19
View File
@@ -24,11 +24,13 @@
#define __POCSAG_APP_H__ #define __POCSAG_APP_H__
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_freq_field.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_rssi.hpp" #include "ui_rssi.hpp"
#include "log_file.hpp" #include "log_file.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "pocsag.hpp" #include "pocsag.hpp"
#include "pocsag_packet.hpp" #include "pocsag_packet.hpp"
@@ -53,16 +55,17 @@ class POCSAGAppView : public View {
~POCSAGAppView(); ~POCSAGAppView();
std::string title() const override { return "POCSAG RX"; }; std::string title() const override { return "POCSAG RX"; };
void focus() override;
private: private:
static constexpr uint32_t initial_target_frequency = 466175000; static constexpr uint32_t initial_target_frequency = 466175000;
bool logging() const { return check_log.value(); }; bool logging() const { return check_log.value(); };
bool ignore() const { return check_ignore.value(); }; bool ignore() const { return check_ignore.value(); };
// app save settings NavigationView& nav_;
std::app_settings settings{}; RxRadioState radio_state_{};
std::app_settings::AppSettings app_settings{}; app_settings::SettingsManager settings_{
"rx_pocsag", app_settings::Mode::RX};
uint32_t last_address = 0xFFFFFFFF; uint32_t last_address = 0xFFFFFFFF;
pocsag::POCSAGState pocsag_state{}; pocsag::POCSAGState pocsag_state{};
@@ -74,18 +77,15 @@ class POCSAGAppView : public View {
VGAGainField field_vga{ VGAGainField field_vga{
{18 * 8, 0 * 16}}; {18 * 8, 0 * 16}};
RSSI rssi{ RSSI rssi{
{21 * 8, 0, 6 * 8, 4}, {21 * 8, 0, 6 * 8, 4}};
};
Channel channel{ Channel channel{
{21 * 8, 5, 6 * 8, 4}, {21 * 8, 5, 6 * 8, 4}};
};
Audio audio{ Audio audio{
{21 * 8, 10, 6 * 8, 4}, {21 * 8, 10, 6 * 8, 4}};
};
FrequencyField field_frequency{ RxFrequencyField field_frequency{
{0 * 8, 0 * 8}, {0 * 8, 0 * 8},
}; nav_};
AudioVolumeField field_volume{ AudioVolumeField field_volume{
{28 * 8, 0 * 16}}; {28 * 8, 0 * 16}};
@@ -111,15 +111,8 @@ class POCSAGAppView : public View {
std::unique_ptr<POCSAGLogger> logger{}; std::unique_ptr<POCSAGLogger> logger{};
uint32_t target_frequency_ = initial_target_frequency;
void update_freq(rf::Frequency f);
void on_packet(const POCSAGPacketMessage* message); void on_packet(const POCSAGPacketMessage* message);
uint32_t target_frequency() const;
void set_target_frequency(const uint32_t new_value);
MessageHandlerRegistration message_handler_packet{ MessageHandlerRegistration message_handler_packet{
Message::ID::POCSAGPacket, Message::ID::POCSAGPacket,
[this](Message* const p) { [this](Message* const p) {
+35 -74
View File
@@ -26,12 +26,14 @@
#include "ui_fileman.hpp" #include "ui_fileman.hpp"
#include "io_file.hpp" #include "io_file.hpp"
#include "cpld_update.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "metadata_file.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
#include "utility.hpp"
using namespace portapack; using namespace portapack;
namespace fs = std::filesystem;
namespace ui { namespace ui {
@@ -39,51 +41,41 @@ void ReplayAppView::set_ready() {
ready_signal = true; ready_signal = true;
} }
void ReplayAppView::on_file_changed(std::filesystem::path new_file_path) { void ReplayAppView::on_file_changed(const fs::path& new_file_path) {
File data_file, info_file; file_path = fs::path(u"/") + new_file_path;
char file_data[257]; File::Size file_size{};
// Get file size { // Get the size of the data file.
auto data_open_error = data_file.open("/" + new_file_path.string()); File data_file;
if (data_open_error.is_valid()) { auto error = data_file.open(file_path);
file_error(); if (error) {
return; file_error();
} return;
file_path = new_file_path;
// Get original record frequency if available
std::filesystem::path info_file_path = file_path;
info_file_path.replace_extension(u".TXT");
sample_rate = 500000;
auto info_open_error = info_file.open("/" + info_file_path.string());
if (!info_open_error.is_valid()) {
memset(file_data, 0, 257);
auto read_size = info_file.read(file_data, 256);
if (!read_size.is_error()) {
auto pos1 = strstr(file_data, "center_frequency=");
if (pos1) {
pos1 += 17;
field_frequency.set_value(strtoll(pos1, nullptr, 10));
}
auto pos2 = strstr(file_data, "sample_rate=");
if (pos2) {
pos2 += 12;
sample_rate = strtoll(pos2, nullptr, 10);
}
} }
file_size = data_file.size();
} }
// Get original record frequency if available.
auto metadata_path = get_metadata_path(file_path);
auto metadata = read_metadata_file(metadata_path);
if (metadata) {
field_frequency.set_value(metadata->center_frequency);
sample_rate = metadata->sample_rate;
} else {
// TODO: This is interesting because it implies that the
// The capture will just be replayed at the freq set on the
// FrequencyField. Is that an intentional behavior?
sample_rate = 500000;
}
// UI Fixup.
text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz"); 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); progressbar.set_max(file_size);
text_filename.set(file_path.filename().string().substr(0, 12)); text_filename.set(truncate(file_path.filename().string(), 12));
auto duration = ms_duration(file_size, sample_rate, 4);
text_duration.set(to_string_time_ms(duration)); text_duration.set(to_string_time_ms(duration));
button_play.focus(); button_play.focus();
@@ -188,35 +180,19 @@ ReplayAppView::ReplayAppView(
&text_duration, &text_duration,
&progressbar, &progressbar,
&field_frequency, &field_frequency,
&tx_view, // now it handles previous rfgain , rfamp. &tx_view, // now it handles previous rfgain, rfamp.
&check_loop, &check_loop,
&button_play, &button_play,
&waterfall, &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&) { button_play.on_select = [this](ImageButton&) {
this->toggle(); this->toggle();
}; };
button_open.on_select = [this, &nav](Button&) { button_open.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".C16"); auto open_view = nav.push<FileLoadView>(".C16");
open_view->on_changed = [this](std::filesystem::path new_file_path) { open_view->on_changed = [this](fs::path new_file_path) {
on_file_changed(new_file_path); on_file_changed(new_file_path);
}; };
}; };
@@ -227,9 +203,7 @@ ReplayAppView::~ReplayAppView() {
display.fill_rectangle({0, 0, 240, 320}, Color::black()); // Solving sometimes visible bottom waterfall artifacts, clearing all LCD pixels. 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 ) 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();
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() { void ReplayAppView::on_hide() {
@@ -247,17 +221,4 @@ void ReplayAppView::set_parent_rect(const Rect new_parent_rect) {
waterfall.set_parent_rect(waterfall_rect); waterfall.set_parent_rect(waterfall_rect);
} }
void ReplayAppView::on_target_frequency_changed(rf::Frequency f) {
set_target_frequency(f);
}
void ReplayAppView::set_target_frequency(const rf::Frequency new_value) {
persistent_memory::set_tuned_frequency(new_value);
;
}
rf::Frequency ReplayAppView::target_frequency() const {
return persistent_memory::tuned_frequency();
}
} /* namespace ui */ } /* namespace ui */
+12 -9
View File
@@ -26,15 +26,18 @@
#define SHORT_UI true #define SHORT_UI true
#define NORMAL_UI false #define NORMAL_UI false
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "replay_thread.hpp" #include "replay_thread.hpp"
#include "ui_spectrum.hpp" #include "ui_spectrum.hpp"
#include "ui_transmitter.hpp"
#include <string> #include <string>
#include <memory> #include <memory>
#include "ui_transmitter.hpp"
namespace ui { namespace ui {
@@ -51,6 +54,9 @@ class ReplayAppView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
TxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"tx_replay", app_settings::Mode::TX};
static constexpr ui::Dim header_height = 3 * 16; static constexpr ui::Dim header_height = 3 * 16;
@@ -61,13 +67,9 @@ class ReplayAppView : public View {
const size_t read_size{16384}; const size_t read_size{16384};
const size_t buffer_count{3}; const size_t buffer_count{3};
void on_file_changed(std::filesystem::path new_file_path); void on_file_changed(const std::filesystem::path& new_file_path);
void on_target_frequency_changed(rf::Frequency f);
void on_tx_progress(const uint32_t progress); 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 toggle();
void start(); void start();
void stop(const bool do_loop); void stop(const bool do_loop);
@@ -97,9 +99,10 @@ class ReplayAppView : public View {
ProgressBar progressbar{ ProgressBar progressbar{
{18 * 8, 1 * 16, 12 * 8, 16}}; {18 * 8, 1 * 16, 12 * 8, 16}};
FrequencyField field_frequency{ // TODO: Does this need to be a frequency field at all?
TxFrequencyField field_frequency{
{0 * 8, 2 * 16}, {0 * 8, 2 * 16},
}; nav_};
TransmitterView2 tx_view{ TransmitterView2 tx_view{
// new handling of NumberField field_rfgain, NumberField field_rfamp // new handling of NumberField field_rfgain, NumberField field_rfamp
@@ -118,7 +121,7 @@ class ReplayAppView : public View {
Color::green(), Color::green(),
Color::black()}; Color::black()};
spectrum::WaterfallWidget waterfall{}; spectrum::WaterfallView waterfall{};
MessageHandlerRegistration message_handler_replay_thread_error{ MessageHandlerRegistration message_handler_replay_thread_error{
Message::ID::ReplayThreadDone, Message::ID::ReplayThreadDone,
+4 -20
View File
@@ -25,7 +25,6 @@
#include "soundboard_app.hpp" #include "soundboard_app.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "tonesets.hpp" #include "tonesets.hpp"
#include "cpld_update.hpp"
using namespace tonekey; using namespace tonekey;
using namespace portapack; using namespace portapack;
@@ -106,6 +105,7 @@ void SoundBoardView::start_tx(const uint32_t id) {
EventDispatcher::send_message(message); EventDispatcher::send_message(message);
}); });
// TODO: Delete all this and use tx model.
baseband::set_audiotx_config( baseband::set_audiotx_config(
1536000 / 20, // Update vu-meter at 20Hz 1536000 / 20, // Update vu-meter at 20Hz
transmitter_model.channel_bandwidth(), transmitter_model.channel_bandwidth(),
@@ -119,8 +119,6 @@ void SoundBoardView::start_tx(const uint32_t id) {
); );
baseband::set_sample_rate(sample_rate); baseband::set_sample_rate(sample_rate);
transmitter_model.set_sampling_rate(1536000);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
tx_view.set_transmitting(true); tx_view.set_transmitting(true);
@@ -235,15 +233,6 @@ SoundBoardView::SoundBoardView(
&button_next_page, &button_next_page,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_soundboard", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
refresh_list(); refresh_list();
button_next_page.on_select = [this](Button&) { button_next_page.on_select = [this](Button&) {
@@ -266,9 +255,9 @@ SoundBoardView::SoundBoardView(
check_random.set_value(false); check_random.set_value(false);
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
@@ -283,14 +272,9 @@ SoundBoardView::SoundBoardView(
} }
SoundBoardView::~SoundBoardView() { SoundBoardView::~SoundBoardView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_soundboard", &app_settings);
stop(); stop();
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
} // namespace ui } // namespace ui
+8 -4
View File
@@ -31,6 +31,7 @@
#include "io_wave.hpp" #include "io_wave.hpp"
#include "tone_key.hpp" #include "tone_key.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
namespace ui { namespace ui {
@@ -49,11 +50,14 @@ class SoundBoardView : public View {
std::string title() const override { return "Soundbrd TX"; }; std::string title() const override { return "Soundbrd TX"; };
private: private:
NavigationView& nav_; TxRadioState radio_state_{
1750000 /* bandwidth */,
1536000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_soundboard", app_settings::Mode::TX};
// app save settings NavigationView& nav_;
std::app_settings settings{};
std::app_settings::AppSettings app_settings{};
enum tx_modes { enum tx_modes {
NORMAL = 0, NORMAL = 0,
@@ -32,7 +32,7 @@ class SpectrumAnalysisModel {
namespace ui { namespace ui {
class SpectrumAnalysisView : public spectrum::WaterfallWidget { class SpectrumAnalysisView : public spectrum::WaterfallView {
public: public:
private: private:
SpectrumAnalysisModel model; SpectrumAnalysisModel model;
+7 -38
View File
@@ -78,9 +78,9 @@ void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_fre
const auto hex_formatted = packet.symbols_formatted(); const auto hex_formatted = packet.symbols_formatted();
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue! // TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10); const auto target_frequency_str = to_string_dec_uint(target_frequency, 10);
std::string entry = tuning_frequency_str + " " + tpms::format::signal_type(packet.signal_type()) + " " + hex_formatted.data + "/" + hex_formatted.errors; std::string entry = target_frequency_str + " " + tpms::format::signal_type(packet.signal_type()) + " " + hex_formatted.data + "/" + hex_formatted.errors;
log_file.write_entry(packet.received_at(), entry); log_file.write_entry(packet.received_at(), entry);
} }
@@ -157,25 +157,15 @@ TPMSAppView::TPMSAppView(NavigationView&) {
&field_vga, &field_vga,
&recent_entries_view}); &recent_entries_view});
// load app settings if (!settings_.loaded())
auto rc = settings.load("rx_tpms", &app_settings); receiver_model.set_target_frequency(initial_target_frequency);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
options_band.set_by_value(app_settings.rx_frequency);
} else
options_band.set_by_value(receiver_model.tuning_frequency());
receiver_model.set_tuning_frequency(tuning_frequency());
receiver_model.set_sampling_rate(sampling_rate);
receiver_model.set_baseband_bandwidth(baseband_bandwidth);
receiver_model.enable(); receiver_model.enable();
options_band.on_change = [this](size_t, OptionsField::value_t v) { options_band.on_change = [this](size_t, OptionsField::value_t v) {
this->on_band_changed(v); receiver_model.set_target_frequency(v);
}; };
options_band.set_by_value(target_frequency()); options_band.set_by_value(receiver_model.target_frequency());
options_pressure.on_change = [this](size_t, int32_t i) { options_pressure.on_change = [this](size_t, int32_t i) {
tpms::format::use_kpa = !i; tpms::format::use_kpa = !i;
@@ -198,10 +188,6 @@ TPMSAppView::TPMSAppView(NavigationView&) {
} }
TPMSAppView::~TPMSAppView() { TPMSAppView::~TPMSAppView() {
// save app settings
app_settings.rx_frequency = target_frequency_;
settings.save("rx_tpms", &app_settings);
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }
@@ -224,7 +210,7 @@ void TPMSAppView::set_parent_rect(const Rect new_parent_rect) {
void TPMSAppView::on_packet(const tpms::Packet& packet) { void TPMSAppView::on_packet(const tpms::Packet& packet) {
if (logger) { if (logger) {
logger->on_packet(packet, target_frequency()); logger->on_packet(packet, receiver_model.target_frequency());
} }
const auto reading_opt = packet.reading(); const auto reading_opt = packet.reading();
@@ -241,21 +227,4 @@ void TPMSAppView::on_show_list() {
recent_entries_view.focus(); recent_entries_view.focus();
} }
void TPMSAppView::on_band_changed(const uint32_t new_band_frequency) {
set_target_frequency(new_band_frequency);
}
void TPMSAppView::set_target_frequency(const uint32_t new_value) {
target_frequency_ = new_value;
receiver_model.set_tuning_frequency(tuning_frequency());
}
uint32_t TPMSAppView::target_frequency() const {
return target_frequency_;
}
uint32_t TPMSAppView::tuning_frequency() const {
return target_frequency() - (sampling_rate / 4);
}
} /* namespace ui */ } /* namespace ui */
+7 -18
View File
@@ -28,6 +28,7 @@
#include "ui_rssi.hpp" #include "ui_rssi.hpp"
#include "ui_channel.hpp" #include "ui_channel.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "event_m0.hpp" #include "event_m0.hpp"
#include "log_file.hpp" #include "log_file.hpp"
@@ -38,10 +39,6 @@
namespace std { namespace std {
constexpr bool operator==(const tpms::TransponderID& lhs, const tpms::TransponderID& rhs) {
return (lhs.value() == rhs.value());
}
} /* namespace std */ } /* namespace std */
struct TPMSRecentEntry { struct TPMSRecentEntry {
@@ -106,12 +103,13 @@ class TPMSAppView : public View {
private: private:
static constexpr uint32_t initial_target_frequency = 315000000; static constexpr uint32_t initial_target_frequency = 315000000;
static constexpr uint32_t sampling_rate = 2457600;
static constexpr uint32_t baseband_bandwidth = 1750000;
// app save settings RxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; 2457600 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_tpms", app_settings::Mode::RX};
MessageHandlerRegistration message_handler_packet{ MessageHandlerRegistration message_handler_packet{
Message::ID::TPMSPacket, Message::ID::TPMSPacket,
@@ -175,18 +173,9 @@ class TPMSAppView : public View {
}}; }};
TPMSRecentEntriesView recent_entries_view{columns, recent}; TPMSRecentEntriesView recent_entries_view{columns, recent};
uint32_t target_frequency_ = initial_target_frequency;
void on_packet(const tpms::Packet& packet); void on_packet(const tpms::Packet& packet);
void on_show_list(); void on_show_list();
void update_view(); void update_view();
void on_band_changed(const uint32_t new_band_frequency);
uint32_t target_frequency() const;
void set_target_frequency(const uint32_t new_value);
uint32_t tuning_frequency() const;
}; };
} /* namespace ui */ } /* namespace ui */
+1 -2
View File
@@ -47,7 +47,7 @@ using namespace portapack;
namespace ui { namespace ui {
void AboutView::on_show() { void AboutView::on_show() {
transmitter_model.set_tuning_frequency(1337000000); // TODO: Change transmitter_model.set_target_frequency(1337000000); // TODO: Change
transmitter_model.set_baseband_configuration({ transmitter_model.set_baseband_configuration({
.mode = 0, .mode = 0,
.sampling_rate = 1536000, .sampling_rate = 1536000,
@@ -56,7 +56,6 @@ void AboutView::on_show() {
transmitter_model.set_rf_amp(true); transmitter_model.set_rf_amp(true);
transmitter_model.set_lna(40); transmitter_model.set_lna(40);
transmitter_model.set_vga(40); transmitter_model.set_vga(40);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
baseband::set_audiotx_data(32, 50, false, 0); baseband::set_audiotx_data(32, 50, false, 0);
+3 -24
View File
@@ -288,13 +288,6 @@ void ADSBRxView::focus() {
} }
ADSBRxView::~ADSBRxView() { ADSBRxView::~ADSBRxView() {
receiver_model.set_tuning_frequency(prevFreq); // Restore previous frequency on exit
// save app settings
settings.save("rx_adsb", &app_settings);
// TODO: once all apps keep there own settin previous frequency logic can be removed
receiver_model.set_tuning_frequency(prevFreq);
rtc_time::signal_tick_second -= signal_token_tick_second; rtc_time::signal_tick_second -= signal_token_tick_second;
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
@@ -475,7 +468,8 @@ void ADSBRxView::updateDetailsAndMap(int ageStep) {
details_view->update(entry); details_view->update(entry);
} }
// Store if the view is present and the list isn't full // 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)) { // Note -- Storing the selected entry too, in case map panning occurs
if (otherMarkersCanBeSent && (markerStored != MARKER_LIST_FULL) && entry.pos.valid && (entry.age_state <= 2)) {
marker.lon = entry.pos.longitude; marker.lon = entry.pos.longitude;
marker.lat = entry.pos.latitude; marker.lat = entry.pos.latitude;
marker.angle = entry.velo.heading; marker.angle = entry.velo.heading;
@@ -502,17 +496,6 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
&rssi, &rssi,
&recent_entries_view}); &recent_entries_view});
// load app settings
auto rc = settings.load("rx_adsb", &app_settings);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
} else {
field_lna.set_value(40);
field_vga.set_value(40);
}
recent_entries_view.set_parent_rect({0, 16, 240, 272}); recent_entries_view.set_parent_rect({0, 16, 240, 272});
recent_entries_view.on_select = [this, &nav](const AircraftRecentEntry& entry) { recent_entries_view.on_select = [this, &nav](const AircraftRecentEntry& entry) {
detailed_entry_key = entry.key(); detailed_entry_key = entry.key();
@@ -528,14 +511,10 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
on_tick_second(); on_tick_second();
}; };
prevFreq = receiver_model.tuning_frequency(); // Store previous frequency on creation
baseband::set_adsb(); baseband::set_adsb();
receiver_model.set_tuning_frequency(1090000000); receiver_model.set_target_frequency(1090000000);
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis); receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_sampling_rate(2000000);
receiver_model.set_baseband_bandwidth(2500000);
receiver_model.enable(); receiver_model.enable();
} }
+8 -4
View File
@@ -33,6 +33,7 @@
#include "adsb.hpp" #include "adsb.hpp"
#include "message.hpp" #include "message.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "crc.hpp" #include "crc.hpp"
using namespace adsb; using namespace adsb;
@@ -340,7 +341,13 @@ class ADSBRxView : public View {
void sort_entries_by_state(); void sort_entries_by_state();
private: private:
rf::Frequency prevFreq = {0}; RxRadioState radio_state_{
2500000 /* bandwidth */,
2000000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_adsb", app_settings::Mode::RX};
std::unique_ptr<ADSBLogger> logger{}; std::unique_ptr<ADSBLogger> logger{};
void on_frame(const ADSBFrameMessage* message); void on_frame(const ADSBFrameMessage* message);
void on_tick_second(); void on_tick_second();
@@ -350,9 +357,6 @@ class ADSBRxView : public View {
#define MARKER_UPDATE_SECONDS (5) #define MARKER_UPDATE_SECONDS (5)
int ticksSinceMarkerRefresh{MARKER_UPDATE_SECONDS - 1}; int ticksSinceMarkerRefresh{MARKER_UPDATE_SECONDS - 1};
// app save settings
std::app_settings settings{};
std::app_settings::AppSettings app_settings{};
const RecentEntriesColumns columns{{{"ICAO/Call", 9}, const RecentEntriesColumns columns{{{"ICAO/Call", 9},
{"Lvl", 3}, {"Lvl", 3},
+3 -19
View File
@@ -26,7 +26,6 @@
#include "manchester.hpp" #include "manchester.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "cpld_update.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include <cstring> #include <cstring>
@@ -273,13 +272,8 @@ void ADSBTxView::focus() {
} }
ADSBTxView::~ADSBTxView() { ADSBTxView::~ADSBTxView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_adsb", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, withouth ghost signal problem at the exit . baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
void ADSBTxView::generate_frames() { void ADSBTxView::generate_frames() {
@@ -303,8 +297,6 @@ void ADSBTxView::generate_frames() {
void ADSBTxView::start_tx() { void ADSBTxView::start_tx() {
generate_frames(); generate_frames();
transmitter_model.set_sampling_rate(4000000U);
transmitter_model.set_baseband_bandwidth(10000000);
transmitter_model.enable(); transmitter_model.enable();
baseband::set_adsb(); baseband::set_adsb();
@@ -327,18 +319,10 @@ ADSBTxView::ADSBTxView(
&text_frame, &text_frame,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_adsb", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+7 -3
View File
@@ -29,6 +29,7 @@
#include "message.hpp" #include "message.hpp"
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "portapack.hpp" #include "portapack.hpp"
using namespace adsb; using namespace adsb;
@@ -201,9 +202,12 @@ class ADSBTxView : public View {
-1 -1
};*/ };*/
// app save settings TxRadioState radio_state_{
std::app_settings settings{}; 10000000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; 4000000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_adsb", app_settings::Mode::TX};
// tx_modes tx_mode = IDLE; // tx_modes tx_mode = IDLE;
NavigationView& nav_; NavigationView& nav_;
+4 -32
View File
@@ -43,11 +43,8 @@ void AFSKRxView::focus() {
field_frequency.focus(); field_frequency.focus();
} }
void AFSKRxView::update_freq(rf::Frequency f) { AFSKRxView::AFSKRxView(NavigationView& nav)
receiver_model.set_tuning_frequency(f); : nav_{nav} {
}
AFSKRxView::AFSKRxView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_afsk_rx); baseband::run_image(portapack::spi_flash::image_tag_afsk_rx);
add_children({&rssi, add_children({&rssi,
@@ -62,16 +59,8 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
&button_modem_setup, &button_modem_setup,
&console}); &console});
// load app settings // Auto-configure modem for LCR RX (TODO remove)
auto rc = settings.load("rx_afsk", &app_settings); field_frequency.set_value(467225500);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
}
// Auto-configure modem for LCR RX (will be removed later)
update_freq(467225500); // 462713300
auto def_bell202 = &modem_defs[0]; auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate); persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format; serial_format_t serial_format;
@@ -81,18 +70,7 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
serial_format.bit_order = LSB_FIRST; serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format); persistent_memory::set_serial_format(serial_format);
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(100); field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
check_log.set_value(logging); check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) { check_log.on_select = [this](Checkbox&, bool v) {
@@ -113,8 +91,6 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
audio::set_rate(audio::Rate::Hz_24000); audio::set_rate(audio::Rate::Hz_24000);
audio::output::start(); audio::output::start();
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.enable(); receiver_model.enable();
} }
@@ -167,10 +143,6 @@ void AFSKRxView::on_data(uint32_t value, bool is_data) {
} }
AFSKRxView::~AFSKRxView() { AFSKRxView::~AFSKRxView() {
// save app settings
app_settings.rx_frequency = field_frequency.value();
settings.save("rx_afsk", &app_settings);
audio::output::stop(); audio::output::stop();
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
+11 -11
View File
@@ -26,8 +26,10 @@
#include "ui.hpp" #include "ui.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_record_view.hpp" // DEBUG #include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "log_file.hpp" #include "log_file.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -57,9 +59,10 @@ class AFSKRxView : public View {
private: private:
void on_data(uint32_t value, bool is_data); void on_data(uint32_t value, bool is_data);
// app save settings NavigationView& nav_;
std::app_settings settings{}; RxRadioState radio_state_{};
std::app_settings::AppSettings app_settings{}; app_settings::SettingsManager settings_{
"rx_afsk", app_settings::Mode::RX};
uint8_t console_color{0}; uint8_t console_color{0};
uint32_t prev_value{0}; uint32_t prev_value{0};
@@ -73,18 +76,16 @@ class AFSKRxView : public View {
VGAGainField field_vga{ VGAGainField field_vga{
{18 * 8, 0 * 16}}; {18 * 8, 0 * 16}};
RSSI rssi{ RSSI rssi{
{21 * 8, 0, 6 * 8, 4}, {21 * 8, 0, 6 * 8, 4}};
};
Channel channel{ Channel channel{
{21 * 8, 5, 6 * 8, 4}, {21 * 8, 5, 6 * 8, 4}};
};
AudioVolumeField field_volume{ AudioVolumeField field_volume{
{28 * 8, 0 * 16}}; {28 * 8, 0 * 16}};
FrequencyField field_frequency{ RxFrequencyField field_frequency{
{0 * 8, 0 * 16}, {0 * 8, 0 * 16},
}; nav_};
Checkbox check_log{ Checkbox check_log{
{0 * 8, 1 * 16}, {0 * 8, 1 * 16},
@@ -103,7 +104,6 @@ class AFSKRxView : public View {
Console console{ Console console{
{0, 4 * 16, 240, 240}}; {0, 4 * 16, 240, 240}};
void update_freq(rf::Frequency f);
void on_data_afsk(const AFSKDataMessage& message); void on_data_afsk(const AFSKDataMessage& message);
std::unique_ptr<AFSKLogger> logger{}; std::unique_ptr<AFSKLogger> logger{};
+1 -30
View File
@@ -73,12 +73,8 @@ void APRSRxView::focus() {
options_region.focus(); options_region.focus();
} }
void APRSRxView::update_freq(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect) APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
: View(parent_rect) { : View(parent_rect), nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_aprs_rx); baseband::run_image(portapack::spi_flash::image_tag_aprs_rx);
add_children({&rssi, add_children({&rssi,
@@ -92,14 +88,6 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
&record_view, &record_view,
&console}); &console});
// load app settings
auto rc = settings.load("rx_aprs", &app_settings);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
}
// DEBUG // DEBUG
record_view.on_error = [&nav](std::string message) { record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message); nav.display_modal("Error", message);
@@ -119,19 +107,7 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
} }
}; };
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(100); field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
options_region.set_selected_index(0, true); options_region.set_selected_index(0, true);
logger = std::make_unique<APRSLogger>(); logger = std::make_unique<APRSLogger>();
@@ -143,8 +119,6 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
audio::set_rate(audio::Rate::Hz_24000); audio::set_rate(audio::Rate::Hz_24000);
audio::output::start(); audio::output::start();
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.enable(); receiver_model.enable();
} }
@@ -211,9 +185,6 @@ void APRSRxView::on_show() {
} }
APRSRxView::~APRSRxView() { APRSRxView::~APRSRxView() {
// save app settings
settings.save("rx_aprs", &app_settings);
audio::output::stop(); audio::output::stop();
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
+11 -12
View File
@@ -26,9 +26,11 @@
#include "ui.hpp" #include "ui.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_record_view.hpp" // DEBUG #include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "ui_geomap.hpp" #include "ui_geomap.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "recent_entries.hpp" #include "recent_entries.hpp"
#include "ui_tabview.hpp" #include "ui_tabview.hpp"
@@ -187,9 +189,10 @@ class APRSRxView : public View {
void on_data(uint32_t value, bool is_data); void on_data(uint32_t value, bool is_data);
bool reset_console = false; bool reset_console = false;
// app save settings NavigationView& nav_;
std::app_settings settings{}; RxRadioState radio_state_{};
std::app_settings::AppSettings app_settings{}; app_settings::SettingsManager settings_{
"rx_aprs", app_settings::Mode::RX};
uint8_t console_color{0}; uint8_t console_color{0};
std::string str_log{""}; std::string str_log{""};
@@ -201,11 +204,9 @@ class APRSRxView : public View {
VGAGainField field_vga{ VGAGainField field_vga{
{18 * 8, 0 * 16}}; {18 * 8, 0 * 16}};
RSSI rssi{ RSSI rssi{
{21 * 8, 0, 6 * 8, 4}, {21 * 8, 0, 6 * 8, 4}};
};
Channel channel{ Channel channel{
{21 * 8, 5, 6 * 8, 4}, {21 * 8, 5, 6 * 8, 4}};
};
AudioVolumeField field_volume{ AudioVolumeField field_volume{
{28 * 8, 0 * 16}}; {28 * 8, 0 * 16}};
@@ -218,9 +219,9 @@ class APRSRxView : public View {
{"AUS", 2}, {"AUS", 2},
{"NZ ", 3}}}; {"NZ ", 3}}};
FrequencyField field_frequency{ RxFrequencyField field_frequency{
{3 * 8, 0 * 16}, {3 * 8, 0 * 16},
}; nav_};
// DEBUG // DEBUG
RecordView record_view{ RecordView record_view{
@@ -234,8 +235,6 @@ class APRSRxView : public View {
Console console{ Console console{
{0, 2 * 16, 240, 240}}; {0, 2 * 16, 240, 240}};
void update_freq(rf::Frequency f);
std::unique_ptr<APRSLogger> logger{}; std::unique_ptr<APRSLogger> logger{};
}; };
+3 -20
View File
@@ -26,7 +26,6 @@
#include "aprs.hpp" #include "aprs.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "cpld_update.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "portapack_shared_memory.hpp" #include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
@@ -44,13 +43,8 @@ void APRSTXView::focus() {
} }
APRSTXView::~APRSTXView() { APRSTXView::~APRSTXView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_aprs", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
void APRSTXView::start_tx() { void APRSTXView::start_tx() {
@@ -62,9 +56,6 @@ void APRSTXView::start_tx() {
// uint8_t * bb_data_ptr = shared_memory.bb_data.data; // uint8_t * bb_data_ptr = shared_memory.bb_data.data;
// text_payload.set(to_string_hex_array(bb_data_ptr + 56, 15)); // text_payload.set(to_string_hex_array(bb_data_ptr + 56, 15));
transmitter_model.set_tuning_frequency(persistent_memory::tuned_frequency());
transmitter_model.set_sampling_rate(AFSK_TX_SAMPLERATE);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
baseband::set_afsk_data( baseband::set_afsk_data(
@@ -97,14 +88,6 @@ APRSTXView::APRSTXView(NavigationView& nav) {
&button_set, &button_set,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_aprs", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
button_set.on_select = [this, &nav](Button&) { button_set.on_select = [this, &nav](Button&) {
text_prompt( text_prompt(
nav, nav,
@@ -116,9 +99,9 @@ APRSTXView::APRSTXView(NavigationView& nav) {
}; };
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
receiver_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+8 -3
View File
@@ -27,8 +27,10 @@
#include "ui_transmitter.hpp" #include "ui_transmitter.hpp"
#include "message.hpp" #include "message.hpp"
#include "modems.hpp"
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "portapack.hpp" #include "portapack.hpp"
namespace ui { namespace ui {
@@ -43,9 +45,12 @@ class APRSTXView : public View {
std::string title() const override { return "APRS TX"; }; std::string title() const override { return "APRS TX"; };
private: private:
// app save settings TxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; AFSK_TX_SAMPLERATE /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_aprs", app_settings::Mode::TX};
std::string payload{""}; std::string payload{""};
+10 -26
View File
@@ -24,7 +24,6 @@
#include "string_format.hpp" #include "string_format.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
using namespace portapack; using namespace portapack;
@@ -137,13 +136,8 @@ void BHTView::on_tx_progress(const uint32_t progress, const bool done) {
} }
BHTView::~BHTView() { BHTView::~BHTView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_bht", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
BHTView::BHTView(NavigationView& nav) { BHTView::BHTView(NavigationView& nav) {
@@ -157,21 +151,12 @@ BHTView::BHTView(NavigationView& nav) {
&progressbar, &progressbar,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_bht", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
field_speed.set_value(1); field_speed.set_value(1);
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
@@ -233,7 +218,6 @@ EPARView::EPARView(
field_city.set_value(0); field_city.set_value(0);
field_group.set_selected_index(2); field_group.set_selected_index(2);
field_city.on_change = [this](int32_t) { generate_message(); }; field_city.on_change = [this](int32_t) { generate_message(); };
field_group.on_change = [this](size_t, int32_t) { generate_message(); }; field_group.on_change = [this](size_t, int32_t) { generate_message(); };
@@ -243,11 +227,11 @@ EPARView::EPARView(
size_t n = 0; size_t n = 0;
for (auto& relay_state : relay_states) { for (auto& relay_state : relay_states) {
relay_state.on_change = relay_state_fn;
relay_state.set_parent_rect({static_cast<Coord>(90 + (n * 36)), relay_state.set_parent_rect({static_cast<Coord>(90 + (n * 36)),
80, 80,
24, 24}); 24, 24});
relay_state.set_options(relay_options); relay_state.set_options(relay_options);
relay_state.on_change = relay_state_fn; // NB: set after set_options to avoid startup call.
add_child(&relay_state); add_child(&relay_state);
n++; n++;
} }
@@ -316,11 +300,11 @@ XylosView::XylosView(
}; };
field_header_a.on_change = field_fn; field_header_a.on_change = field_fn;
field_header_b.on_change = [this](int32_t) { generate_message(); }; field_header_b.on_change = field_fn;
field_city.on_change = [this](int32_t) { generate_message(); }; field_city.on_change = field_fn;
field_family.on_change = [this](int32_t) { generate_message(); }; field_family.on_change = field_fn;
field_subfamily.on_change = [this](int32_t) { generate_message(); }; field_subfamily.on_change = field_fn;
field_receiver.on_change = [this](int32_t) { generate_message(); }; field_receiver.on_change = field_fn;
checkbox_wcsubfamily.on_select = [this](Checkbox&, bool v) { checkbox_wcsubfamily.on_select = [this](Checkbox&, bool v) {
field_subfamily.set_focusable(!v); field_subfamily.set_focusable(!v);
@@ -341,11 +325,11 @@ XylosView::XylosView(
size_t n = 0; size_t n = 0;
for (auto& relay_state : relay_states) { for (auto& relay_state : relay_states) {
relay_state.on_change = relay_state_fn;
relay_state.set_parent_rect({static_cast<Coord>(54 + (n * 36)), relay_state.set_parent_rect({static_cast<Coord>(54 + (n * 36)),
134, 134,
24, 24}); 24, 24});
relay_state.set_options(relay_options); relay_state.set_options(relay_options);
relay_state.on_change = relay_state_fn; // NB: set after set_options to avoid startup call.
add_child(&relay_state); add_child(&relay_state);
n++; n++;
} }
+4 -3
View File
@@ -32,6 +32,7 @@
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "encoders.hpp" #include "encoders.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "portapack.hpp" #include "portapack.hpp"
namespace ui { namespace ui {
@@ -165,9 +166,9 @@ class BHTView : public View {
std::string title() const override { return "BHT TX"; }; std::string title() const override { return "BHT TX"; };
private: private:
// app save settings TxRadioState radio_state_{};
std::app_settings settings{}; app_settings::SettingsManager settings_{
std::app_settings::AppSettings app_settings{}; "tx_bht", app_settings::Mode::TX};
void on_tx_progress(const uint32_t progress, const bool done); void on_tx_progress(const uint32_t progress, const bool done);
void start_tx(); void start_tx();
+4 -32
View File
@@ -40,11 +40,8 @@ void BTLERxView::focus() {
field_frequency.focus(); field_frequency.focus();
} }
void BTLERxView::update_freq(rf::Frequency f) { BTLERxView::BTLERxView(NavigationView& nav)
receiver_model.set_tuning_frequency(f); : nav_{nav} {
}
BTLERxView::BTLERxView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_btle_rx); baseband::run_image(portapack::spi_flash::image_tag_btle_rx);
add_children({&rssi, add_children({&rssi,
@@ -56,16 +53,8 @@ BTLERxView::BTLERxView(NavigationView& nav) {
&button_modem_setup, &button_modem_setup,
&console}); &console});
// load app settings // Auto-configure modem for LCR RX (TODO: remove later)
auto rc = settings.load("rx_btle", &app_settings); field_frequency.set_value(2426000000);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
}
// Auto-configure modem for LCR RX (will be removed later)
update_freq(2426000000);
auto def_bell202 = &modem_defs[0]; auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate); persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format; serial_format_t serial_format;
@@ -75,18 +64,7 @@ BTLERxView::BTLERxView(NavigationView& nav) {
serial_format.bit_order = LSB_FIRST; serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format); persistent_memory::set_serial_format(serial_format);
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(100); field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
button_modem_setup.on_select = [&nav](Button&) { button_modem_setup.on_select = [&nav](Button&) {
nav.push<ModemSetupView>(); nav.push<ModemSetupView>();
@@ -98,8 +76,6 @@ BTLERxView::BTLERxView(NavigationView& nav) {
audio::set_rate(audio::Rate::Hz_24000); audio::set_rate(audio::Rate::Hz_24000);
audio::output::start(); audio::output::start();
receiver_model.set_sampling_rate(4000000);
receiver_model.set_baseband_bandwidth(4000000);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.enable(); receiver_model.enable();
} }
@@ -148,10 +124,6 @@ void BTLERxView::on_data(uint32_t value, bool is_data) {
} }
BTLERxView::~BTLERxView() { BTLERxView::~BTLERxView() {
// save app settings
app_settings.rx_frequency = field_frequency.value();
settings.save("rx_btle", &app_settings);
audio::output::stop(); audio::output::stop();
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
+14 -13
View File
@@ -27,8 +27,10 @@
#include "ui.hpp" #include "ui.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "ui_record_view.hpp" // DEBUG #include "radio_state.hpp"
#include "ui_record_view.hpp"
#include "utility.hpp" #include "utility.hpp"
@@ -46,9 +48,13 @@ class BTLERxView : public View {
private: private:
void on_data(uint32_t value, bool is_data); void on_data(uint32_t value, bool is_data);
// app save settings NavigationView& nav_;
std::app_settings settings{}; RxRadioState radio_state_{
std::app_settings::AppSettings app_settings{}; 4000000 /* bandwidth */,
4000000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_btle", app_settings::Mode::RX};
uint8_t console_color{0}; uint8_t console_color{0};
uint32_t prev_value{0}; uint32_t prev_value{0};
@@ -60,15 +66,13 @@ class BTLERxView : public View {
VGAGainField field_vga{ VGAGainField field_vga{
{18 * 8, 0 * 16}}; {18 * 8, 0 * 16}};
RSSI rssi{ RSSI rssi{
{21 * 8, 0, 6 * 8, 4}, {21 * 8, 0, 6 * 8, 4}};
};
Channel channel{ Channel channel{
{21 * 8, 5, 6 * 8, 4}, {21 * 8, 5, 6 * 8, 4}};
};
FrequencyField field_frequency{ RxFrequencyField field_frequency{
{0 * 8, 0 * 16}, {0 * 8, 0 * 16},
}; nav_};
Button button_modem_setup{ Button button_modem_setup{
{240 - 12 * 8, 1 * 16, 96, 24}, {240 - 12 * 8, 1 * 16, 96, 24},
@@ -77,9 +81,6 @@ class BTLERxView : public View {
Console console{ Console console{
{0, 4 * 16, 240, 240}}; {0, 4 * 16, 240, 240}};
void update_freq(rf::Frequency f);
// void on_data_afsk(const AFSKDataMessage& message);
MessageHandlerRegistration message_handler_packet{ MessageHandlerRegistration message_handler_packet{
Message::ID::AFSKData, Message::ID::AFSKData,
[this](Message* const p) { [this](Message* const p) {
+3 -20
View File
@@ -23,7 +23,6 @@
#include "ui_coasterp.hpp" #include "ui_coasterp.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
#include <cstring> #include <cstring>
@@ -38,13 +37,8 @@ void CoasterPagerView::focus() {
} }
CoasterPagerView::~CoasterPagerView() { CoasterPagerView::~CoasterPagerView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_coaster", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
void CoasterPagerView::generate_frame() { void CoasterPagerView::generate_frame() {
@@ -73,8 +67,6 @@ void CoasterPagerView::generate_frame() {
void CoasterPagerView::start_tx() { void CoasterPagerView::start_tx() {
generate_frame(); generate_frame();
transmitter_model.set_sampling_rate(2280000);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
baseband::set_fsk_data(19 * 8, 2280000 / 1000, 5000, 32); baseband::set_fsk_data(19 * 8, 2280000 / 1000, 5000, 32);
@@ -123,15 +115,6 @@ CoasterPagerView::CoasterPagerView(NavigationView& nav) {
&text_message, &text_message,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_coaster", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
// Bytes to nibbles // Bytes to nibbles
for (c = 0; c < 16; c++) for (c = 0; c < 16; c++)
sym_data.set_sym(c, (data_init[c >> 1] >> ((c & 1) ? 0 : 4)) & 0x0F); sym_data.set_sym(c, (data_init[c >> 1] >> ((c & 1) ? 0 : 4)) & 0x0F);
@@ -141,9 +124,9 @@ CoasterPagerView::CoasterPagerView(NavigationView& nav) {
generate_frame(); generate_frame();
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
receiver_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+7 -3
View File
@@ -28,6 +28,7 @@
#include "message.hpp" #include "message.hpp"
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "portapack.hpp" #include "portapack.hpp"
namespace ui { namespace ui {
@@ -50,9 +51,12 @@ class CoasterPagerView : public View {
tx_modes tx_mode = IDLE; tx_modes tx_mode = IDLE;
// app save settings TxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; 2280000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_coaster", app_settings::Mode::TX};
void start_tx(); void start_tx();
void generate_frame(); void generate_frame();
+38 -5
View File
@@ -25,6 +25,7 @@
#include "radio.hpp" #include "radio.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "crc.hpp"
#include "audio.hpp" #include "audio.hpp"
@@ -247,6 +248,7 @@ void ControlsSwitchesWidget::on_show() {
bool ControlsSwitchesWidget::on_key(const KeyEvent key) { bool ControlsSwitchesWidget::on_key(const KeyEvent key) {
key_event_mask = 1 << toUType(key); key_event_mask = 1 << toUType(key);
long_press_key_event_mask = switch_long_press_occurred((size_t)key) ? key_event_mask : 0;
return true; return true;
} }
@@ -324,6 +326,14 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
switches_event >>= 1; switches_event >>= 1;
} }
switches_event = long_press_key_event_mask;
for (const auto r : events_rects) {
if (switches_event & 1)
painter.fill_rectangle(r + pos, Color::cyan());
switches_event >>= 1;
}
} }
void ControlsSwitchesWidget::on_frame_sync() { void ControlsSwitchesWidget::on_frame_sync() {
@@ -334,12 +344,21 @@ void ControlsSwitchesWidget::on_frame_sync() {
DebugControlsView::DebugControlsView(NavigationView& nav) { DebugControlsView::DebugControlsView(NavigationView& nav) {
add_children({ add_children({
&text_title, &labels,
&switches_widget, &switches_widget,
&options_switches_mode,
&button_done, &button_done,
}); });
button_done.on_select = [&nav](Button&) { nav.pop(); }; button_done.on_select = [&nav](Button&) {
switches_long_press_enable(0);
nav.pop();
};
options_switches_mode.on_change = [this](size_t, OptionsField::value_t v) {
(void)v;
switches_long_press_enable(options_switches_mode.selected_index_value());
};
} }
void DebugControlsView::focus() { void DebugControlsView::focus() {
@@ -381,22 +400,36 @@ DebugMenuView::DebugMenuView(NavigationView& nav) {
{"Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [&nav]() { nav.push<DebugPeripheralsMenuView>(); }}, {"Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [&nav]() { nav.push<DebugPeripheralsMenuView>(); }},
{"Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav]() { nav.push<TemperatureView>(); }}, {"Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav]() { nav.push<TemperatureView>(); }},
{"Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav]() { nav.push<DebugControlsView>(); }}, {"Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav]() { nav.push<DebugControlsView>(); }},
{"Pmem", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugPmemView>(); }}, {"P.Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugPmemView>(); }},
{"Debug Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { portapack::persistent_memory::debug_dump(); }},
}); });
set_max_rows(2); // allow wider buttons set_max_rows(2); // allow wider buttons
} }
/* DebugPmemView *********************************************************/ /* DebugPmemView *********************************************************/
uint32_t pmem_checksum(volatile const uint32_t data[63]) {
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
for (size_t i = 0; i < 63; i++) {
const auto word = data[i];
crc.process_byte((word >> 0) & 0xff);
crc.process_byte((word >> 8) & 0xff);
crc.process_byte((word >> 16) & 0xff);
crc.process_byte((word >> 24) & 0xff);
}
return crc.checksum();
}
DebugPmemView::DebugPmemView(NavigationView& nav) DebugPmemView::DebugPmemView(NavigationView& nav)
: data{*reinterpret_cast<pmem_data*>(memory::map::backup_ram.base())}, registers_widget(RegistersWidgetConfig{page_size, 8}, std::bind(&DebugPmemView::registers_widget_feed, this, std::placeholders::_1)) { : data{*reinterpret_cast<pmem_data*>(memory::map::backup_ram.base())}, registers_widget(RegistersWidgetConfig{page_size, 8}, std::bind(&DebugPmemView::registers_widget_feed, this, std::placeholders::_1)) {
static_assert(sizeof(pmem_data) == memory::map::backup_ram.size()); static_assert(sizeof(pmem_data) == memory::map::backup_ram.size());
add_children({&text_page, &registers_widget, &text_checksum, &button_ok}); add_children({&text_page, &registers_widget, &text_checksum, &text_checksum2, &button_ok});
registers_widget.set_parent_rect({0, 32, 240, 192}); registers_widget.set_parent_rect({0, 32, 240, 192});
text_checksum.set("Size: " + to_string_dec_uint(portapack::persistent_memory::data_size()) + " CRC: " + to_string_hex(data.check_value, 8)); text_checksum.set("Size: " + to_string_dec_uint(portapack::persistent_memory::data_size(), 3) + " CRC: " + to_string_hex(data.check_value, 8));
text_checksum2.set("Calculated CRC: " + to_string_hex(pmem_checksum(data.regfile), 8));
button_ok.on_select = [&nav](Button&) { button_ok.on_select = [&nav](Button&) {
nav.pop(); nav.pop();
+19 -8
View File
@@ -31,6 +31,7 @@
#include "rffc507x.hpp" #include "rffc507x.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "memory_map.hpp" #include "memory_map.hpp"
#include "irq_controls.hpp"
#include <functional> #include <functional>
#include <utility> #include <utility>
@@ -220,7 +221,8 @@ class ControlsSwitchesWidget : public Widget {
ControlsSwitchesWidget( ControlsSwitchesWidget(
Rect parent_rect) Rect parent_rect)
: Widget{parent_rect}, : Widget{parent_rect},
key_event_mask(0) { key_event_mask(0),
long_press_key_event_mask{0} {
set_focusable(true); set_focusable(true);
} }
@@ -231,6 +233,7 @@ class ControlsSwitchesWidget : public Widget {
private: private:
uint8_t key_event_mask; uint8_t key_event_mask;
uint8_t long_press_key_event_mask;
MessageHandlerRegistration message_handler_frame_sync{ MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync, Message::ID::DisplayFrameSync,
@@ -250,15 +253,22 @@ class DebugControlsView : public View {
std::string title() const override { return "Buttons Test"; }; std::string title() const override { return "Buttons Test"; };
private: private:
Text text_title{ Labels labels{
{64, 16, 184, 16}, {{8 * 8, 1 * 16}, "Controls State", Color::white()},
"Controls State", {{0 * 8, 14 * 16}, "Long-Press Mode:", Color::grey()}};
};
ControlsSwitchesWidget switches_widget{ ControlsSwitchesWidget switches_widget{
{80, 80, 80, 112}, {80, 80, 80, 112},
}; };
OptionsField options_switches_mode{
{17 * 8, 14 * 16},
8,
{
{"Disabled", 0},
{"Enabled", 0xFF}, // all KeyEvent bits to long-press mode
}};
Button button_done{ Button button_done{
{72, 264, 96, 24}, {72, 264, 96, 24},
"Done"}; "Done"};
@@ -269,7 +279,7 @@ class DebugPmemView : public View {
DebugPmemView(NavigationView& nav); DebugPmemView(NavigationView& nav);
void focus() override; void focus() override;
bool on_encoder(const EncoderEvent delta) override; bool on_encoder(const EncoderEvent delta) override;
std::string title() const override { return "Pmem"; } std::string title() const override { return "P.Mem Debug"; }
private: private:
struct pmem_data { struct pmem_data {
@@ -282,13 +292,14 @@ class DebugPmemView : public View {
int32_t page{0}; int32_t page{0};
const pmem_data& data; volatile const pmem_data& data;
Text text_page{{16, 16, 208, 16}}; Text text_page{{16, 16, 208, 16}};
RegistersWidget registers_widget; RegistersWidget registers_widget;
Text text_checksum{{16, 240, 208, 16}}; Text text_checksum{{16, 232, 208, 16}};
Text text_checksum2{{16, 248, 208, 16}};
Button button_ok{ Button button_ok{
{240 / 3, 270, 240 / 3, 24}, {240 / 3, 270, 240 / 3, 24},
+54
View File
@@ -22,6 +22,7 @@
#include "ui_dfu_menu.hpp" #include "ui_dfu_menu.hpp"
#include "portapack_shared_memory.hpp" #include "portapack_shared_memory.hpp"
#include "performance_counter.hpp" #include "performance_counter.hpp"
#include "portapack.hpp"
namespace ui { namespace ui {
@@ -80,4 +81,57 @@ void DfuMenu::paint(Painter& painter) {
ui::Color::dark_cyan()); ui::Color::dark_cyan());
} }
DfuMenu2::DfuMenu2(NavigationView& nav)
: nav_(nav) {
add_children({&text_head,
&labels,
&text_info_line_1,
&text_info_line_2,
&text_info_line_3,
&text_info_line_4,
&text_info_line_5,
&text_info_line_6,
&text_info_line_7,
&text_info_line_8});
}
void DfuMenu2::paint(Painter& painter) {
text_info_line_1.set(to_string_dec_uint(portapack::receiver_model.target_frequency(), 10));
text_info_line_2.set(to_string_dec_uint(portapack::receiver_model.baseband_bandwidth(), 10));
text_info_line_3.set(to_string_dec_uint(portapack::receiver_model.sampling_rate(), 10));
text_info_line_4.set(to_string_dec_uint((uint32_t)portapack::receiver_model.modulation(), 10));
text_info_line_5.set(to_string_dec_uint(portapack::receiver_model.am_configuration(), 10));
text_info_line_6.set(to_string_dec_uint(portapack::receiver_model.nbfm_configuration(), 10));
text_info_line_7.set(to_string_dec_uint(portapack::receiver_model.wfm_configuration(), 10));
text_info_line_8.set("");
constexpr auto margin = 5;
constexpr auto lines = 8 + 2;
painter.fill_rectangle(
{{5 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
{19 * CHARACTER_WIDTH + margin * 2, lines * LINE_HEIGHT + margin * 2}},
ui::Color::black());
painter.fill_rectangle(
{{4 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}},
ui::Color::dark_cyan());
painter.fill_rectangle(
{{24 * CHARACTER_WIDTH + margin, 3 * LINE_HEIGHT - margin},
{CHARACTER_WIDTH, lines * LINE_HEIGHT + margin * 2}},
ui::Color::dark_cyan());
painter.fill_rectangle(
{{4 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin - 8},
{21 * CHARACTER_WIDTH + margin * 2, 8}},
ui::Color::dark_cyan());
painter.fill_rectangle(
{{4 * CHARACTER_WIDTH - margin, (lines + 3) * LINE_HEIGHT + margin},
{21 * CHARACTER_WIDTH + margin * 2, 8}},
ui::Color::dark_cyan());
}
} /* namespace ui */ } /* namespace ui */
+32
View File
@@ -67,6 +67,38 @@ class DfuMenu : public View {
Text text_info_line_8{{15 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; Text text_info_line_8{{15 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
}; };
class DfuMenu2 : public View {
public:
DfuMenu2(NavigationView& nav);
~DfuMenu2() = default;
void paint(Painter& painter) override;
private:
NavigationView& nav_;
Text text_head{{6 * CHARACTER_WIDTH, 3 * LINE_HEIGHT, 8 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, "Receiver"};
Labels labels{
{{5 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "Tgt freq:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "Bandwidt:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "Sampl Rt:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "Modulatn:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "AM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "NBFM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "WFM cfg:", Color::dark_cyan()},
{{5 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "", Color::dark_cyan()}};
Text text_info_line_1{{14 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_2{{14 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_3{{14 * CHARACTER_WIDTH, 7 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_4{{14 * CHARACTER_WIDTH, 8 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_5{{14 * CHARACTER_WIDTH, 9 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_6{{14 * CHARACTER_WIDTH, 10 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_7{{14 * CHARACTER_WIDTH, 11 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_8{{14 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 10 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
};
} /* namespace ui */ } /* namespace ui */
#endif /*__UI_DFU_MENU_H__*/ #endif /*__UI_DFU_MENU_H__*/
+3 -20
View File
@@ -24,7 +24,6 @@
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "cpld_update.hpp"
using namespace portapack; using namespace portapack;
@@ -200,13 +199,8 @@ void EncodersView::focus() {
} }
EncodersView::~EncodersView() { EncodersView::~EncodersView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_ook", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // ghost signal c/m to the problem at the exit . baseband::shutdown();
baseband::shutdown(); // better this function after load_sram()
} }
void EncodersView::update_progress() { void EncodersView::update_progress() {
@@ -266,8 +260,6 @@ void EncodersView::start_tx(const bool scan) {
repeat_index = 1; repeat_index = 1;
update_progress(); update_progress();
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
baseband::set_ook_data( baseband::set_ook_data(
@@ -290,19 +282,10 @@ EncodersView::EncodersView(
&progressbar, &progressbar,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_ook", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+7 -3
View File
@@ -27,6 +27,7 @@
#include "encoders.hpp" #include "encoders.hpp"
#include "de_bruijn.hpp" #include "de_bruijn.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
using namespace encoders; using namespace encoders;
@@ -183,9 +184,12 @@ class EncodersView : public View {
SCAN SCAN
}; };
// app save settings TxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; OOK_SAMPLERATE /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_ook", app_settings::Mode::TX};
tx_modes tx_mode = IDLE; tx_modes tx_mode = IDLE;
uint32_t repeat_index{0}; uint32_t repeat_index{0};
+20 -30
View File
@@ -26,6 +26,7 @@
#include <algorithm> #include <algorithm>
#include "ui_fileman.hpp" #include "ui_fileman.hpp"
#include "ui_playlist.hpp"
#include "ui_ss_viewer.hpp" #include "ui_ss_viewer.hpp"
#include "ui_text_editor.hpp" #include "ui_text_editor.hpp"
#include "string_format.hpp" #include "string_format.hpp"
@@ -35,6 +36,13 @@
using namespace portapack; using namespace portapack;
namespace fs = std::filesystem; namespace fs = std::filesystem;
namespace ui {
static const fs::path txt_ext{u".TXT"};
static const fs::path ppl_ext{u".PPL"};
static const fs::path c16_ext{u".C16"};
static const fs::path png_ext{u".PNG"};
} // namespace ui
namespace { namespace {
using namespace ui; using namespace ui;
@@ -47,25 +55,6 @@ std::string truncate(const fs::path& path, size_t max_length) {
return ::truncate(path.string(), max_length); return ::truncate(path.string(), max_length);
} }
// Case insensitive path equality on underlying "native" string.
bool iequal(
const fs::path& lhs,
const fs::path& rhs) {
const auto& lhs_str = lhs.native();
const auto& rhs_str = rhs.native();
// NB: Not correct for Unicode/locales.
if (lhs_str.length() == rhs_str.length()) {
for (size_t i = 0; i < lhs_str.length(); ++i)
if (towupper(lhs_str[i]) != towupper(rhs_str[i]))
return false;
return true;
}
return false;
}
// Inserts the entry into the entry list sorted directories first then by file name. // Inserts the entry into the entry list sorted directories first then by file name.
void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) { void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) {
auto it = std::lower_bound( auto it = std::lower_bound(
@@ -86,15 +75,12 @@ void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) {
fs::path get_partner_file(fs::path path) { fs::path get_partner_file(fs::path path) {
if (fs::is_directory(path)) if (fs::is_directory(path))
return {}; return {};
const fs::path txt_path{u".TXT"};
const fs::path c16_path{u".C16"};
auto ext = path.extension(); auto ext = path.extension();
if (iequal(ext, txt_path)) if (path_iequal(ext, txt_ext))
ext = c16_path; ext = c16_ext;
else if (iequal(ext, c16_path)) else if (path_iequal(ext, c16_ext))
ext = txt_path; ext = txt_ext;
else else
return {}; return {};
@@ -163,7 +149,7 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
continue; continue;
if (fs::is_regular_file(entry.status())) { if (fs::is_regular_file(entry.status())) {
if (!filtering || iequal(entry.path().extension(), extension_filter)) if (!filtering || path_iequal(entry.path().extension(), extension_filter))
insert_sorted(entry_list, {entry.path(), (uint32_t)entry.size(), false}); insert_sorted(entry_list, {entry.path(), (uint32_t)entry.size(), false});
} else if (fs::is_directory(entry.status())) { } else if (fs::is_directory(entry.status())) {
insert_sorted(entry_list, {entry.path(), 0, true}); insert_sorted(entry_list, {entry.path(), 0, true});
@@ -301,7 +287,7 @@ const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
size_t index = 0; size_t index = 0;
for (; index < file_types.size() - 1; ++index) for (; index < file_types.size() - 1; ++index)
if (iequal(ext, file_types[index].extension)) if (path_iequal(ext, file_types[index].extension))
return file_types[index]; return file_types[index];
// Default to last entry in the list. // Default to last entry in the list.
@@ -507,10 +493,14 @@ bool FileManagerView::handle_file_open() {
auto path = get_selected_full_path(); auto path = get_selected_full_path();
auto ext = path.extension(); auto ext = path.extension();
if (iequal(u".TXT", ext) || iequal(u".PPL", ext)) { if (path_iequal(txt_ext, ext)) {
nav_.push<TextEditorView>(path); nav_.push<TextEditorView>(path);
return true; return true;
} else if (iequal(u".PNG", ext)) { } else if (path_iequal(c16_ext, ext) || path_iequal(ppl_ext, ext)) {
// TODO: Enough memory to push?
nav_.push<PlaylistView>(path);
return true;
} else if (path_iequal(png_ext, ext)) {
nav_.push<ScreenshotViewer>(path); nav_.push<ScreenshotViewer>(path);
return true; return true;
} }
@@ -25,6 +25,8 @@
namespace ui { namespace ui {
static const char16_t* firmware_folder = u"/FIRMWARE";
Thread* FlashUtilityView::thread{nullptr}; Thread* FlashUtilityView::thread{nullptr};
static constexpr size_t max_filename_length = 26; static constexpr size_t max_filename_length = 26;
@@ -35,7 +37,9 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8}); menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
for (const auto& entry : std::filesystem::directory_iterator(u"FIRMWARE", u"*.bin")) { ensure_directory(firmware_folder);
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.bin")) {
auto filename = entry.path().filename(); auto filename = entry.path().filename();
auto path = entry.path().native(); auto path = entry.path().native();
@@ -40,7 +40,7 @@ class FlashUtilityView : public View {
void focus() override; void focus() override;
std::string title() const override { return "FlashUtility"; }; // Removed the space because the title and speaker icon overlapped. std::string title() const override { return "Flash Utility"; };
private: private:
NavigationView& nav_; NavigationView& nav_;
+23 -23
View File
@@ -81,14 +81,11 @@ void FreqManBaseView::change_category(int32_t category_id) {
if (file_list.empty()) return; if (file_list.empty()) return;
std::vector<freqman_entry>().swap(database);
if (!load_freqman_file(file_list[categories[category_id].second], database)) { if (!load_freqman_file(file_list[categories[category_id].second], database)) {
error_ = ERROR_ACCESS; error_ = ERROR_ACCESS;
} }
menu_view.set_db(database); freqlist_view.set_db(database);
text_empty.hidden(!database.empty()); text_empty.hidden(!database.empty());
menu_view.set_dirty();
set_dirty(); set_dirty();
} }
@@ -162,10 +159,14 @@ FrequencySaveView::FrequencySaveView(
}; };
} }
FrequencyLoadView::~FrequencyLoadView() {
std::vector<freqman_entry>().swap(database);
}
void FrequencyLoadView::refresh_widgets(const bool v) { void FrequencyLoadView::refresh_widgets(const bool v) {
menu_view.hidden(v); freqlist_view.hidden(v);
text_empty.hidden(!v); text_empty.hidden(!v);
// display.fill_rectangle(menu_view.screen_rect(), Color::black()); // display.fill_rectangle(freqlist_view.screen_rect(), Color::black());
set_dirty(); set_dirty();
} }
@@ -176,17 +177,14 @@ FrequencyLoadView::FrequencyLoadView(
refresh_widgets(v); refresh_widgets(v);
}; };
add_children({&menu_view, add_children({&freqlist_view,
&text_empty}); &text_empty});
// Resize menu view to fill screen // Resize menu view to fill screen
menu_view.set_parent_rect({0, 3 * 8, 240, 30 * 8}); freqlist_view.set_parent_rect({0, 3 * 8, 240, 30 * 8});
menu_view.on_select = [&nav, this](FreqManUIList&) {
nav_.pop();
auto& entry = database[menu_view.get_index()];
freqlist_view.on_select = [&nav, this](FreqManUIList&) {
auto& entry = database[freqlist_view.get_index()];
if (entry.type == RANGE) { if (entry.type == RANGE) {
// User chose a frequency range entry // User chose a frequency range entry
if (on_range_loaded) if (on_range_loaded)
@@ -199,18 +197,21 @@ FrequencyLoadView::FrequencyLoadView(
if (on_frequency_loaded) if (on_frequency_loaded)
on_frequency_loaded(entry.frequency_a); on_frequency_loaded(entry.frequency_a);
} }
// swap with empty vector to ensure memory is immediately released
std::vector<freqman_entry>().swap(database);
nav_.pop();
}; };
} }
void FrequencyManagerView::on_edit_freq(rf::Frequency f) { void FrequencyManagerView::on_edit_freq(rf::Frequency f) {
database[menu_view.get_index()].frequency_a = f; database[freqlist_view.get_index()].frequency_a = f;
save_freqman_file(file_list[categories[current_category_id].second], database); save_freqman_file(file_list[categories[current_category_id].second], database);
change_category(current_category_id); change_category(current_category_id);
} }
void FrequencyManagerView::on_edit_desc(NavigationView& nav) { void FrequencyManagerView::on_edit_desc(NavigationView& nav) {
text_prompt(nav, desc_buffer, 28, [this](std::string& buffer) { text_prompt(nav, desc_buffer, 28, [this](std::string& buffer) {
database[menu_view.get_index()].description = buffer; database[freqlist_view.get_index()].description = buffer;
save_freqman_file(file_list[categories[current_category_id].second], database); save_freqman_file(file_list[categories[current_category_id].second], database);
change_category(current_category_id); change_category(current_category_id);
}); });
@@ -230,7 +231,7 @@ void FrequencyManagerView::on_delete() {
delete_freqman_file(file_list[categories[current_category_id].second]); delete_freqman_file(file_list[categories[current_category_id].second]);
refresh_list(); refresh_list();
} else { } else {
database.erase(database.begin() + menu_view.get_index()); database.erase(database.begin() + freqlist_view.get_index());
save_freqman_file(file_list[categories[current_category_id].second], database); save_freqman_file(file_list[categories[current_category_id].second], database);
} }
change_category(current_category_id); change_category(current_category_id);
@@ -241,10 +242,9 @@ void FrequencyManagerView::refresh_widgets(const bool v) {
button_edit_desc.hidden(v); button_edit_desc.hidden(v);
button_delete.hidden(v); button_delete.hidden(v);
text_empty.hidden(!v); text_empty.hidden(!v);
menu_view.hidden(v); freqlist_view.hidden(v);
menu_view.set_dirty();
labels.hidden(v); labels.hidden(v);
// display.fill_rectangle(menu_view.screen_rect(), Color::black()); // display.fill_rectangle(freqlist_view.screen_rect(), Color::black());
set_dirty(); set_dirty();
} }
@@ -261,13 +261,13 @@ FrequencyManagerView::FrequencyManagerView(
add_children({&labels, add_children({&labels,
&button_new_category, &button_new_category,
&menu_view, &freqlist_view,
&text_empty, &text_empty,
&button_edit_freq, &button_edit_freq,
&button_edit_desc, &button_edit_desc,
&button_delete}); &button_delete});
menu_view.on_select = [this](FreqManUIList&) { freqlist_view.on_select = [this](FreqManUIList&) {
button_edit_freq.focus(); button_edit_freq.focus();
}; };
@@ -280,7 +280,7 @@ FrequencyManagerView::FrequencyManagerView(
if (database.empty()) { if (database.empty()) {
database.push_back({0, 0, "", SINGLE}); database.push_back({0, 0, "", SINGLE});
} }
auto new_view = nav.push<FrequencyKeypadView>(database[menu_view.get_index()].frequency_a); auto new_view = nav.push<FrequencyKeypadView>(database[freqlist_view.get_index()].frequency_a);
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
on_edit_freq(f); on_edit_freq(f);
}; };
@@ -290,7 +290,7 @@ FrequencyManagerView::FrequencyManagerView(
if (database.empty()) { if (database.empty()) {
database.push_back({0, 0, "", SINGLE}); database.push_back({0, 0, "", SINGLE});
} }
desc_buffer = database[menu_view.get_index()].description; desc_buffer = database[freqlist_view.get_index()].description;
on_edit_desc(nav); on_edit_desc(nav);
}; };
+2 -1
View File
@@ -64,7 +64,7 @@ class FreqManBaseView : public View {
14, 14,
{}}; {}};
FreqManUIList menu_view{ FreqManUIList freqlist_view{
{0, 3 * 8, 240, 23 * 8}}; {0, 3 * 8, 240, 23 * 8}};
Text text_empty{ Text text_empty{
@@ -116,6 +116,7 @@ class FrequencyLoadView : public FreqManBaseView {
std::function<void(rf::Frequency, rf::Frequency)> on_range_loaded{}; std::function<void(rf::Frequency, rf::Frequency)> on_range_loaded{};
FrequencyLoadView(NavigationView& nav); FrequencyLoadView(NavigationView& nav);
~FrequencyLoadView();
std::string title() const override { return "Load freq."; }; std::string title() const override { return "Load freq."; };
+2 -6
View File
@@ -25,7 +25,6 @@
#include "ui_freqman.hpp" #include "ui_freqman.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "string_format.hpp" #include "string_format.hpp"
using namespace portapack; using namespace portapack;
@@ -180,13 +179,12 @@ void JammerView::focus() {
JammerView::~JammerView() { JammerView::~JammerView() {
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . baseband::shutdown();
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) { void JammerView::on_retune(const rf::Frequency freq, const uint32_t range) {
if (freq) { if (freq) {
transmitter_model.set_tuning_frequency(freq); transmitter_model.set_target_frequency(freq);
text_range_number.set(to_string_dec_uint(range, 2)); text_range_number.set(to_string_dec_uint(range, 2));
} }
} }
@@ -261,9 +259,7 @@ void JammerView::start_tx() {
button_transmit.set_style(&style_cancel); button_transmit.set_style(&style_cancel);
button_transmit.set_text("STOP"); button_transmit.set_text("STOP");
transmitter_model.set_sampling_rate(3072000U);
transmitter_model.set_rf_amp(field_amp.value()); transmitter_model.set_rf_amp(field_amp.value());
transmitter_model.set_baseband_bandwidth(3500000U);
transmitter_model.set_tx_gain(field_gain.value()); transmitter_model.set_tx_gain(field_gain.value());
transmitter_model.enable(); transmitter_model.enable();
+5
View File
@@ -29,6 +29,7 @@
#include "message.hpp" #include "message.hpp"
#include "jammer.hpp" #include "jammer.hpp"
#include "lfsr_random.hpp" #include "lfsr_random.hpp"
#include "radio_state.hpp"
using namespace jammer; using namespace jammer;
@@ -99,6 +100,10 @@ class JammerView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
TxRadioState radio_state_{
3500000 /* bandwidth */,
3072000 /* sampling rate */
};
void start_tx(); void start_tx();
void on_timer(); void on_timer();
+4 -8
View File
@@ -23,7 +23,6 @@
#include "ui_keyfob.hpp" #include "ui_keyfob.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "string_format.hpp" #include "string_format.hpp"
using namespace portapack; using namespace portapack;
@@ -141,8 +140,7 @@ KeyfobView::~KeyfobView() {
settings.save("tx_keyfob", &app_settings); settings.save("tx_keyfob", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
void KeyfobView::update_progress(const uint32_t progress) { void KeyfobView::update_progress(const uint32_t progress) {
@@ -190,8 +188,6 @@ void KeyfobView::start_tx() {
size_t bitstream_length = generate_frame(); size_t bitstream_length = generate_frame();
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
baseband::set_ook_data( baseband::set_ook_data(
@@ -236,12 +232,12 @@ KeyfobView::KeyfobView(
options_make.set_selected_index(0); options_make.set_selected_index(0);
transmitter_model.set_tuning_frequency(433920000); // Fixed 433.92MHz transmitter_model.set_target_frequency(433920000); // Fixed 433.92MHz
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+7 -3
View File
@@ -24,6 +24,7 @@
#include "ui_transmitter.hpp" #include "ui_transmitter.hpp"
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "encoders.hpp" #include "encoders.hpp"
using namespace encoders; using namespace encoders;
@@ -42,9 +43,12 @@ class KeyfobView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
// app save settings TxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; OOK_SAMPLERATE /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_keyfob", , app_settings::Mode::TX};
// 1013210ns / bit // 1013210ns / bit
static constexpr uint32_t subaru_samples_per_bit = (OOK_SAMPLERATE * 0.00101321); static constexpr uint32_t subaru_samples_per_bit = (OOK_SAMPLERATE * 0.00101321);
+3 -22
View File
@@ -24,10 +24,8 @@
#include "ui_modemsetup.hpp" #include "ui_modemsetup.hpp"
#include "lcr.hpp" #include "lcr.hpp"
#include "modems.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "cpld_update.hpp"
#include "serializer.hpp" #include "serializer.hpp"
@@ -40,13 +38,8 @@ void LCRView::focus() {
} }
LCRView::~LCRView() { LCRView::~LCRView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_lcr", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
/* /*
@@ -134,9 +127,6 @@ void LCRView::start_tx(const bool scan) {
modems::generate_data(lcr::generate_message(rgsb, litterals_list, options_ec.selected_index()), lcr_message_data); modems::generate_data(lcr::generate_message(rgsb, litterals_list, options_ec.selected_index()), lcr_message_data);
transmitter_model.set_tuning_frequency(persistent_memory::tuned_frequency());
transmitter_model.set_sampling_rate(AFSK_TX_SAMPLERATE);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
memcpy(shared_memory.bb_data.data, lcr_message_data, sizeof(lcr_message_data)); memcpy(shared_memory.bb_data.data, lcr_message_data, sizeof(lcr_message_data));
@@ -176,15 +166,6 @@ LCRView::LCRView(NavigationView& nav) {
&button_clear, &button_clear,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_lcr", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
options_scanlist.set_selected_index(0); options_scanlist.set_selected_index(0);
const auto button_set_am_fn = [this, &nav](Button& button) { const auto button_set_am_fn = [this, &nav](Button& button) {
@@ -251,9 +232,9 @@ LCRView::LCRView(NavigationView& nav) {
}; };
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+8 -3
View File
@@ -26,8 +26,10 @@
#include "ui_transmitter.hpp" #include "ui_transmitter.hpp"
#include "message.hpp" #include "message.hpp"
#include "modems.hpp"
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
namespace ui { namespace ui {
@@ -79,9 +81,12 @@ class LCRView : public View {
SCAN SCAN
}; };
// app save settings TxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; AFSK_TX_SAMPLERATE /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_lcr", app_settings::Mode::TX};
tx_modes tx_mode = IDLE; tx_modes tx_mode = IDLE;
uint8_t scan_count{0}, scan_index{0}; uint8_t scan_count{0}, scan_index{0};
+35 -60
View File
@@ -35,13 +35,6 @@ void LevelView::focus() {
} }
LevelView::~LevelView() { LevelView::~LevelView() {
// save app settings
app_settings.lna = field_lna.value();
app_settings.vga = field_vga.value();
app_settings.rx_amp = field_rf_amp.value();
settings.save("level", &app_settings);
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }
@@ -71,42 +64,31 @@ LevelView::LevelView(NavigationView& nav)
change_mode(NFM_MODULATION); // Start on AM change_mode(NFM_MODULATION); // Start on AM
field_mode.set_by_value(NFM_MODULATION); // Reflect the mode into the manual selector field_mode.set_by_value(NFM_MODULATION); // Reflect the mode into the manual selector
// HELPER: Pre-setting a manual range, based on stored frequency freq_ = receiver_model.target_frequency();
freq = persistent_memory::tuned_frequency(); button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
receiver_model.set_tuning_frequency(freq);
button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>");
// load auto common app settings
auto rc = settings.load("level", &app_settings);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
receiver_model.set_rf_amp(app_settings.rx_amp);
}
button_frequency.on_select = [this, &nav](ButtonWithEncoder& button) { button_frequency.on_select = [this, &nav](ButtonWithEncoder& button) {
auto new_view = nav_.push<FrequencyKeypadView>(freq); auto new_view = nav_.push<FrequencyKeypadView>(freq_);
new_view->on_changed = [this, &button](rf::Frequency f) { new_view->on_changed = [this, &button](rf::Frequency f) {
freq = f; freq_ = f;
receiver_model.set_tuning_frequency(f); // Retune to actual freq receiver_model.set_target_frequency(f); // Retune to actual freq
button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>"); button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
}; };
}; };
button_frequency.on_change = [this]() { button_frequency.on_change = [this]() {
int64_t def_step = freqman_entry_get_step_value(step_mode.selected_index()); int64_t def_step = freqman_entry_get_step_value(step_mode.selected_index());
freq = freq + (button_frequency.get_encoder_delta() * def_step); freq_ = freq_ + (button_frequency.get_encoder_delta() * def_step);
if (freq < 1) { if (freq_ < 1) {
freq = 1; freq_ = 1;
} }
if (freq > (MAX_UFREQ - def_step)) { if (freq_ > (MAX_UFREQ - def_step)) {
freq = MAX_UFREQ; freq_ = MAX_UFREQ;
} }
button_frequency.set_encoder_delta(0); button_frequency.set_encoder_delta(0);
receiver_model.set_tuning_frequency(freq); // Retune to actual freq receiver_model.set_target_frequency(freq_); // Retune to actual freq
button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>"); button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
}; };
field_mode.on_change = [this](size_t, OptionsField::value_t v) { field_mode.on_change = [this](size_t, OptionsField::value_t v) {
@@ -196,8 +178,6 @@ size_t LevelView::change_mode(freqman_index_t new_mod) {
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
receiver_model.set_am_configuration(field_bw.selected_index_value()); receiver_model.set_am_configuration(field_bw.selected_index_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_am_configuration(n); }; field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_am_configuration(n); };
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
text_ctcss.set(" "); text_ctcss.set(" ");
break; break;
case NFM_MODULATION: case NFM_MODULATION:
@@ -208,8 +188,6 @@ size_t LevelView::change_mode(freqman_index_t new_mod) {
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.set_nbfm_configuration(field_bw.selected_index_value()); receiver_model.set_nbfm_configuration(field_bw.selected_index_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_nbfm_configuration(n); }; field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_nbfm_configuration(n); };
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
break; break;
case WFM_MODULATION: case WFM_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw); freqman_set_bandwidth_option(new_mod, field_bw);
@@ -219,10 +197,18 @@ size_t LevelView::change_mode(freqman_index_t new_mod) {
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.set_wfm_configuration(field_bw.selected_index_value()); receiver_model.set_wfm_configuration(field_bw.selected_index_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_wfm_configuration(n); }; field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_wfm_configuration(n); };
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
text_ctcss.set(" "); text_ctcss.set(" ");
break; break;
case SPEC_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw);
baseband::run_image(portapack::spi_flash::image_tag_capture);
receiver_model.set_modulation(ReceiverModel::Mode::Capture);
field_bw.set_by_value(0);
field_bw.on_change = [this](size_t, OptionsField::value_t sampling_rate) {
auto anti_alias_baseband_bandwidth_filter = filter_bandwidth_for_sampling_rate(sampling_rate);
receiver_model.set_sampling_rate(sampling_rate);
receiver_model.set_baseband_bandwidth(anti_alias_baseband_bandwidth_filter);
};
default: default:
break; break;
} }
@@ -230,34 +216,23 @@ size_t LevelView::change_mode(freqman_index_t new_mod) {
} }
void LevelView::handle_coded_squelch(const uint32_t value) { void LevelView::handle_coded_squelch(const uint32_t value) {
static int32_t last_idx = -1; static tone_index last_squelch_index = -1;
float diff, min_diff = value; if (field_mode.selected_index() == NFM_MODULATION) {
size_t min_idx{0}; tone_index idx = tone_key_index_by_value(value);
size_t c;
if (field_mode.selected_index() != NFM_MODULATION) { if ((last_squelch_index < 0) || (last_squelch_index != idx)) {
text_ctcss.set(" "); last_squelch_index = idx;
return; if (idx >= 0) {
} text_ctcss.set("T: " + tone_key_string(idx));
return;
// Find nearest match }
for (c = 0; c < tone_keys.size(); c++) { } else {
diff = abs(((float)value / 100.0) - tone_keys[c].second); return;
if (diff < min_diff) {
min_idx = c;
min_diff = diff;
} }
} }
// Arbitrary confidence threshold text_ctcss.set(" ");
if (last_idx < 0 || (unsigned)last_idx != min_idx) {
last_idx = min_idx;
if (min_diff < 40)
text_ctcss.set("T: " + tone_keys[min_idx].first);
else
text_ctcss.set(" ");
}
} }
} /* namespace ui */ } /* namespace ui */
+9 -6
View File
@@ -38,6 +38,7 @@
#include "string_format.hpp" #include "string_format.hpp"
#include "file.hpp" #include "file.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
namespace ui { namespace ui {
@@ -53,13 +54,18 @@ class LevelView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_level", app_settings::Mode::RX};
size_t change_mode(freqman_index_t mod_type); size_t change_mode(freqman_index_t mod_type);
void on_statistics_update(const ChannelStatistics& statistics); void on_statistics_update(const ChannelStatistics& statistics);
void set_display_freq(int64_t freq); void set_display_freq(int64_t freq);
// TODO: needed?
int32_t db{0}; int32_t db{0};
long long int MAX_UFREQ = {7200000000}; // maximum usable freq long long int MAX_UFREQ = {7200000000}; // maximum usable freq
rf::Frequency freq = {0}; rf::Frequency freq_ = {0};
Labels labels{ Labels labels{
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()}, {{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()},
@@ -85,7 +91,7 @@ class LevelView : public View {
OptionsField field_mode{ OptionsField field_mode{
{15 * 8, 1 * 16}, {15 * 8, 1 * 16},
3, 4,
{}}; {}};
OptionsField step_mode{ OptionsField step_mode{
@@ -104,7 +110,7 @@ class LevelView : public View {
{"audio off", 0}, {"audio off", 0},
{"audio on", 1} {"audio on", 1}
//{"tone on", 2}, //{"tone on", 2},
//{"tone off", 2}, //{"tone off", 3},
}}; }};
Text text_ctcss{ Text text_ctcss{
@@ -168,9 +174,6 @@ class LevelView : public View {
[this](const Message* const p) { [this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics); 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 */ } /* namespace ui */
@@ -22,12 +22,15 @@
*/ */
#include "ui_looking_glass_app.hpp" #include "ui_looking_glass_app.hpp"
#include "convert.hpp"
#include "file_reader.hpp"
#include "string_format.hpp"
using namespace portapack; using namespace portapack;
namespace ui { namespace ui {
void GlassView::focus() { void GlassView::focus() {
button_marker.focus(); range_presets.focus();
} }
GlassView::~GlassView() { GlassView::~GlassView() {
@@ -38,7 +41,7 @@ GlassView::~GlassView() {
void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power) { void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power) {
if (mode == LOOKING_GLASS_SINGLEPASS) { if (mode == LOOKING_GLASS_SINGLEPASS) {
// analog audio app like view // <20MHz spectrum mode
if (bin < 120) { if (bin < 120) {
if (spectrum.db[SPEC_NB_BINS - 120 + bin] > max_power) if (spectrum.db[SPEC_NB_BINS - 120 + bin] > max_power)
max_power = spectrum.db[SPEC_NB_BINS - 120 + bin]; max_power = spectrum.db[SPEC_NB_BINS - 120 + bin];
@@ -47,15 +50,13 @@ void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint
max_power = spectrum.db[bin - 120]; max_power = spectrum.db[bin - 120];
} }
} else { } else {
// view is made in multiple pass, use original bin picking // FAST or SLOW mode
// FAST mode: center 12 bins are ignored in fast mode , (DC spike is blanked) leftmost and rightmost 2 bins are ignored
// SLOW mode: leftmost 'offset' bins are ignored
if (bin < 120) { if (bin < 120) {
if (spectrum.db[SPEC_NB_BINS - offset - 120 + bin] > max_power) if (spectrum.db[134 + bin] > max_power)
max_power = spectrum.db[SPEC_NB_BINS - offset - 120 + bin]; max_power = spectrum.db[134 + bin];
} else { } else {
if (spectrum.db[offset + bin - 120] > max_power) if (spectrum.db[bin - 118] > max_power)
max_power = spectrum.db[offset + bin - 120]; max_power = spectrum.db[bin - 118];
} }
} }
} }
@@ -66,7 +67,7 @@ rf::Frequency GlassView::get_freq_from_bin_pos(uint8_t pos) {
// starting from the middle, minus 8 ignored bin on each side. Since pos is [-120,120] after the (pos - 120), it's divided by SCREEN_W(240)/2 => 120 // starting from the middle, minus 8 ignored bin on each side. Since pos is [-120,120] after the (pos - 120), it's divided by SCREEN_W(240)/2 => 120
freq_at_pos = f_center_ini + ((pos - 120) * ((looking_glass_range - ((16 * looking_glass_range) / SPEC_NB_BINS)) / 2)) / (SCREEN_W / 2); freq_at_pos = f_center_ini + ((pos - 120) * ((looking_glass_range - ((16 * looking_glass_range) / SPEC_NB_BINS)) / 2)) / (SCREEN_W / 2);
} else } else
freq_at_pos = f_min - (offset * each_bin_size) + (pos * looking_glass_range) / SCREEN_W; freq_at_pos = f_min + (2 * offset * each_bin_size) + (pos * looking_glass_range) / SCREEN_W;
return freq_at_pos; return freq_at_pos;
} }
@@ -77,23 +78,6 @@ void GlassView::on_marker_change() {
PlotMarker(marker_pixel_index); // Refresh marker on screen PlotMarker(marker_pixel_index); // Refresh marker on screen
} }
// Returns the next multiple of num that is a multiple of multiplier
int64_t GlassView::next_mult_of(int64_t num, int64_t multiplier) {
return ((num / multiplier) + 1) * multiplier;
}
void GlassView::adjust_range(int64_t* f_min, int64_t* f_max, int64_t width) {
int64_t span = *f_max - *f_min;
int64_t num_intervals = span / width;
if (span % width != 0) {
num_intervals++;
}
int64_t new_span = num_intervals * width;
int64_t delta_span = (new_span - span) / 2;
*f_min -= delta_span;
*f_max += delta_span;
}
void GlassView::retune() { void GlassView::retune() {
// Start a new sweep. // Start a new sweep.
// Tune rx for this new slice directly because the model // Tune rx for this new slice directly because the model
@@ -166,6 +150,31 @@ void GlassView::add_spectrum_pixel(uint8_t power) {
} }
} }
bool GlassView::process_bins(uint8_t* powerlevel) {
bins_Hz_size += each_bin_size; // add pixel to fulfilled bag of Hz
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
{
if (*powerlevel > min_color_power)
add_spectrum_pixel(*powerlevel); // Pixel will represent max_power
else
add_spectrum_pixel(0); // Filtered out, show black
*powerlevel = 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"
if (mode != LOOKING_GLASS_SINGLEPASS) {
f_center = f_center_ini;
retune();
} else
baseband::spectrum_streaming_start();
return true; // signal a new line
}
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
}
return false;
}
// Apparently, the spectrum object returns an array of SPEC_NB_BINS (256) bins // Apparently, the spectrum object returns an array of SPEC_NB_BINS (256) bins
// Each having the radio signal power for it's corresponding frequency slot // Each having the radio signal power for it's corresponding frequency slot
void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) { void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
@@ -174,31 +183,19 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
// we actually need SCREEN_W (240) of those bins // we actually need SCREEN_W (240) of those bins
for (bin = 0; bin < bin_length; bin++) { for (bin = 0; bin < bin_length; bin++) {
get_max_power(spectrum, bin, max_power); get_max_power(spectrum, bin, max_power);
if (ignore_dc && bin == 119) { // process dc spike if enable
bins_Hz_size += 12 * each_bin_size; // add the ignored DC spike to "pixel fulfilled bag of Hz" if (bin == 119) {
} uint8_t next_max_power = 0;
bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz" get_max_power(spectrum, bin + 1, next_max_power);
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled for (uint8_t it = 0; it < ignore_dc; it++) {
{ uint8_t med_max_power = (max_power + next_max_power) / 2; // due to the way process_bins works we have to keep resetting the color
if (min_color_power < max_power) if (process_bins(&med_max_power) == true)
add_spectrum_pixel(max_power); // Pixel will represent max_power return; // new line signaled, return
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"
if (mode != LOOKING_GLASS_SINGLEPASS) {
f_center = f_center_ini;
retune();
} else
baseband::spectrum_streaming_start();
return; // signal a new line
} }
bins_Hz_size -= marker_pixel_step; // reset bins size, but carrying the eventual excess Hz into next pixel
} }
// process actual bin
if (process_bins(&max_power) == true)
return; // new line signaled, return
} }
if (mode != LOOKING_GLASS_SINGLEPASS) { if (mode != LOOKING_GLASS_SINGLEPASS) {
f_center += looking_glass_step; f_center += looking_glass_step;
@@ -225,36 +222,33 @@ void GlassView::on_range_changed() {
f_max = f_max * MHZ_DIV; f_max = f_max * MHZ_DIV;
looking_glass_range = f_max - f_min; looking_glass_range = f_max - f_min;
if (looking_glass_range <= LOOKING_GLASS_SLICE_WIDTH_MAX) { if (looking_glass_range <= LOOKING_GLASS_SLICE_WIDTH_MAX) {
adjust_range(&f_min, &f_max, SCREEN_W);
looking_glass_range = f_max - f_min;
// if the view is done in one pass, show it like in analog_audio_app // if the view is done in one pass, show it like in analog_audio_app
mode = LOOKING_GLASS_SINGLEPASS; mode = LOOKING_GLASS_SINGLEPASS;
offset = 0; offset = 2;
bin_length = SCREEN_W; bin_length = SCREEN_W;
ignore_dc = 0; ignore_dc = 0;
looking_glass_bandwidth = looking_glass_range; looking_glass_bandwidth = looking_glass_range;
looking_glass_sampling_rate = looking_glass_bandwidth; looking_glass_sampling_rate = looking_glass_bandwidth;
each_bin_size = looking_glass_bandwidth / SCREEN_W; each_bin_size = looking_glass_bandwidth / SCREEN_W;
looking_glass_step = looking_glass_bandwidth; looking_glass_step = looking_glass_bandwidth;
f_center_ini = f_min + (looking_glass_bandwidth / 2); // Initial center frequency for sweep f_center_ini = f_min + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
} else { // if ( mode == LOOKING_GLASS_SLOWSCAN || mode == LOOKING_GLASS_FASTSCAN ) portapack::display.fill_rectangle({17 * 8, 4 * 16, 2 * 8, 16}, Color::black()); // Clear old marker and whole marker rectangle btw
} else {
// view is made in multiple pass, use original bin picking // view is made in multiple pass, use original bin picking
mode = scan_type.selected_index_value(); mode = scan_type.selected_index_value();
if (mode == LOOKING_GLASS_FASTSCAN) {
offset = 2;
bin_length = SCREEN_W;
ignore_dc = 1;
} else { // if( mode == LOOKING_GLASS_SLOWSCAN )
offset = 16;
bin_length = 80;
ignore_dc = 0;
}
adjust_range(&f_min, &f_max, SCREEN_W);
looking_glass_range = f_max - f_min;
looking_glass_bandwidth = LOOKING_GLASS_SLICE_WIDTH_MAX; looking_glass_bandwidth = LOOKING_GLASS_SLICE_WIDTH_MAX;
looking_glass_sampling_rate = LOOKING_GLASS_SLICE_WIDTH_MAX; looking_glass_sampling_rate = LOOKING_GLASS_SLICE_WIDTH_MAX;
each_bin_size = LOOKING_GLASS_SLICE_WIDTH_MAX / SPEC_NB_BINS; each_bin_size = LOOKING_GLASS_SLICE_WIDTH_MAX / SPEC_NB_BINS;
looking_glass_step = (bin_length + (ignore_dc * 12)) * each_bin_size; if (mode == LOOKING_GLASS_FASTSCAN) {
offset = 2;
ignore_dc = 4;
bin_length = SCREEN_W;
} else { // if( mode == LOOKING_GLASS_SLOWSCAN )
offset = 2;
bin_length = 80;
ignore_dc = 0;
}
looking_glass_step = (bin_length + ignore_dc) * each_bin_size;
f_center_ini = f_min - (offset * each_bin_size) + (looking_glass_bandwidth / 2); // Initial center frequency for sweep f_center_ini = f_min - (offset * each_bin_size) + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
} }
search_span = looking_glass_range / MHZ_DIV; search_span = looking_glass_range / MHZ_DIV;
@@ -280,7 +274,7 @@ void GlassView::on_range_changed() {
receiver_model.set_squelch_level(0); receiver_model.set_squelch_level(0);
f_center = f_center_ini; // Reset sweep into first slice f_center = f_center_ini; // Reset sweep into first slice
baseband::set_spectrum(looking_glass_bandwidth, field_trigger.value()); baseband::set_spectrum(looking_glass_bandwidth, field_trigger.value());
receiver_model.set_tuning_frequency(f_center); // tune rx for this slice receiver_model.set_target_frequency(f_center); // tune rx for this slice
} }
void GlassView::PlotMarker(uint8_t pos) { void GlassView::PlotMarker(uint8_t pos) {
@@ -476,7 +470,7 @@ GlassView::GlassView(
}; };
button_marker.on_select = [this](ButtonWithEncoder&) { button_marker.on_select = [this](ButtonWithEncoder&) {
receiver_model.set_tuning_frequency(marker); // Center tune rx in marker freq. receiver_model.set_target_frequency(marker); // Center tune rx in marker freq.
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
nav_.pop(); nav_.pop();
nav_.push<AnalogAudioView>(); // Jump into audio view nav_.push<AnalogAudioView>(); // Jump into audio view
@@ -500,7 +494,7 @@ GlassView::GlassView(
}; };
button_jump.on_select = [this](Button&) { button_jump.on_select = [this](Button&) {
receiver_model.set_tuning_frequency(max_freq_hold); // Center tune rx in marker freq. receiver_model.set_target_frequency(max_freq_hold); // Center tune rx in marker freq.
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
nav_.pop(); nav_.pop();
nav_.push<AnalogAudioView>(); // Jump into audio view nav_.push<AnalogAudioView>(); // Jump into audio view
@@ -527,71 +521,47 @@ GlassView::GlassView(
} }
void GlassView::load_Presets() { void GlassView::load_Presets() {
File presets_file; // LOAD /WHIPCALC/ANTENNAS.TXT from microSD File presets_file;
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT"); auto error = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
presets_db.clear(); // Start with fresh db presets_db.clear();
if (result.is_valid()) {
presets_Default(); // There is no txt, store a default range if (!error) {
} else { auto reader = FileLineReader(presets_file);
std::string line; // There is a txt file for (const auto& line : reader) {
char one_char[1]; // Read it char by char if (line.length() == 0 || line[0] == '#')
for (size_t pointer = 0; pointer < presets_file.size(); pointer++) { continue;
presets_file.seek(pointer);
presets_file.read(one_char, 1); auto cols = split_string(line, ',');
if ((int)one_char[0] > 31) { // ascii space upwards if (cols.size() != 3)
line += one_char[0]; // Add it to the textline continue;
} else if (one_char[0] == '\n') { // New Line
txtline_process(line); // make sense of this textline preset_entry entry{};
line.clear(); // Ready for next textline parse_int(cols[0], entry.min);
} parse_int(cols[1], entry.max);
entry.label = trimr(cols[2]);
if (entry.min == 0 || entry.max == 0 || entry.min >= entry.max)
continue; // Invalid line.
presets_db.emplace_back(std::move(entry));
} }
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
} }
// Couldn't load any from the file, load a default instead.
if (presets_db.empty())
presets_Default();
populate_Presets(); populate_Presets();
} }
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() { void GlassView::populate_Presets() {
using option_t = std::pair<std::string, int32_t>; using option_t = std::pair<std::string, int32_t>;
using options_t = std::vector<option_t>; using options_t = std::vector<option_t>;
options_t entries; options_t entries;
for (preset_entry preset : presets_db) { // go thru all available presets for (const auto& preset : presets_db)
entries.emplace_back(preset.label, entries.size()); entries.emplace_back(preset.label, entries.size());
}
range_presets.set_options(entries); range_presets.set_options(entries);
} }
@@ -26,7 +26,9 @@
#include "ui.hpp" #include "ui.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "app_settings.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "radio_state.hpp"
#include "receiver_model.hpp" #include "receiver_model.hpp"
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
@@ -37,6 +39,7 @@
#include "spectrum_color_lut.hpp" #include "spectrum_color_lut.hpp"
namespace ui { namespace ui {
#define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000 #define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000
#define MHZ_DIV 1000000 #define MHZ_DIV 1000000
@@ -60,7 +63,7 @@ class GlassView : public View {
GlassView& operator=(const GlassView& nav); GlassView& operator=(const GlassView& nav);
~GlassView(); ~GlassView();
std::string title() const override { return "LookingGlass"; }; std::string title() const override { return "Looking Glass"; };
void on_show() override; void on_show() override;
void on_hide() override; void on_hide() override;
@@ -68,6 +71,9 @@ class GlassView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_glass", app_settings::Mode::RX};
struct preset_entry { struct preset_entry {
rf::Frequency min{}; rf::Frequency min{};
@@ -81,12 +87,12 @@ class GlassView : public View {
void get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power); void get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power);
rf::Frequency get_freq_from_bin_pos(uint8_t pos); rf::Frequency get_freq_from_bin_pos(uint8_t pos);
void on_marker_change(); void on_marker_change();
int64_t next_mult_of(int64_t num, int64_t multiplier);
void adjust_range(int64_t* f_min, int64_t* f_max, int64_t width);
void retune(); void retune();
bool move_to_next_position(); bool process_bins(uint8_t* powerlevel);
void on_channel_spectrum(const ChannelSpectrum& spectrum); void on_channel_spectrum(const ChannelSpectrum& spectrum);
void do_timers(); void do_timers();
int64_t next_mult_of(int64_t num, int64_t multiplier);
void adjust_range(int64_t* f_min, int64_t* f_max, int64_t width);
void on_range_changed(); void on_range_changed();
void on_lna_changed(int32_t v_db); void on_lna_changed(int32_t v_db);
void on_vga_changed(int32_t v_db); void on_vga_changed(int32_t v_db);
+22 -31
View File
@@ -30,7 +30,6 @@ using wolfson::wm8731::WM8731;
#include "tonesets.hpp" #include "tonesets.hpp"
#include "portapack_hal.hpp" #include "portapack_hal.hpp"
#include "cpld_update.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "irq_controls.hpp" #include "irq_controls.hpp"
@@ -68,6 +67,7 @@ void MicTXView::on_tx_progress(const bool done) {
} }
void MicTXView::configure_baseband() { void MicTXView::configure_baseband() {
// TODO: Can this use the transmitter model instead?
baseband::set_audiotx_config( baseband::set_audiotx_config(
sampling_rate / 20, // Update vu-meter at 20Hz sampling_rate / 20, // Update vu-meter at 20Hz
transmitting ? transmitter_model.channel_bandwidth() : 0, transmitting ? transmitter_model.channel_bandwidth() : 0,
@@ -86,7 +86,7 @@ void MicTXView::set_tx(bool enable) {
rxaudio(false); // Then turn off audio RX rxaudio(false); // Then turn off audio RX
transmitting = true; transmitting = true;
configure_baseband(); configure_baseband();
transmitter_model.set_tuning_frequency(tx_frequency); // Now,no need: transmitter_model.set_tx_gain(tx_gain), nor (rf_amp); transmitter_model.set_target_frequency(tx_frequency); // Now, no need: transmitter_model.set_tx_gain(tx_gain), nor (rf_amp);
transmitter_model.enable(); transmitter_model.enable();
portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming
} else { } else {
@@ -140,14 +140,6 @@ void MicTXView::do_timing() {
} }
} }
/* Hmmmm. Maybe useless now.
void MicTXView::on_tuning_frequency_changed(rf::Frequency f) {
transmitter_model.set_tuning_frequency(f);
//if ( rx_enabled )
receiver_model.set_tuning_frequency(f); //Update freq also for RX
}
*/
void MicTXView::rxaudio(bool is_on) { void MicTXView::rxaudio(bool is_on) {
if (is_on) { if (is_on) {
audio::input::stop(); audio::input::stop();
@@ -170,18 +162,15 @@ void MicTXView::rxaudio(bool is_on) {
// receiver_model.set_nbfm_configuration(n); is called above , depending user's selection (8k5, 11k, 16k). // receiver_model.set_nbfm_configuration(n); is called above , depending user's selection (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.
if (bool_same_F_tx_rx_enabled) // when stop TX ,define to which freq RX we return if (bool_same_F_tx_rx_enabled) // when stop TX ,define to which freq RX we return
receiver_model.set_tuning_frequency(tx_frequency); // Update freq also for RX = TX receiver_model.set_target_frequency(tx_frequency); // Update freq also for RX = TX
else else
receiver_model.set_tuning_frequency(rx_frequency); // Now with seperate freq controls! receiver_model.set_target_frequency(rx_frequency); // Now with separate freq controls!
receiver_model.set_lna(rx_lna); receiver_model.set_lna(rx_lna);
receiver_model.set_vga(rx_vga); receiver_model.set_vga(rx_vga);
receiver_model.set_rf_amp(rx_amp); receiver_model.set_rf_amp(rx_amp);
receiver_model.enable(); receiver_model.enable();
hackrf::cpld::load_sram_no_verify(); // to have a good RX without any ghost inside Mic App
audio::output::start(); audio::output::start();
} else { // These incredibly convoluted steps are required for the vumeter to reappear when stopping RX. } 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... receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); // This fixes something with AM RX...
@@ -294,11 +283,11 @@ MicTXView::MicTXView(
} }
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) 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 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 - configure_baseband(); // to update in real time, 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 . options_wm8731_boost_mode.set_selected_index(3); // preset GUI index 3 as default WM -> -02 dB's .
} else { } 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) 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) { 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, 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 audio::input::start(ak4951_alc_and_wm8731_boost_GUI); // Detected (AK4951) ==> Set up proper ALC mode from 0..11 options
@@ -308,16 +297,16 @@ MicTXView::MicTXView(
// options_ak4951_alc_mode.set_selected_index(0); // options_ak4951_alc_mode.set_selected_index(0);
tx_frequency = transmitter_model.tuning_frequency(); tx_frequency = transmitter_model.target_frequency();
field_frequency.set_value(transmitter_model.tuning_frequency()); field_frequency.set_value(transmitter_model.target_frequency());
field_frequency.set_step(receiver_model.frequency_step()); field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) { field_frequency.on_change = [this](rf::Frequency f) {
tx_frequency = f; tx_frequency = f;
if (!rx_enabled) { // not activated receiver. just update freq TX if (!rx_enabled) { // not activated receiver. just update freq TX
transmitter_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
} else { // activated receiver. } else { // activated receiver.
if (bool_same_F_tx_rx_enabled) // user selected common freq- TX = RX if (bool_same_F_tx_rx_enabled) // user selected common freq- TX = RX
receiver_model.set_tuning_frequency(f); // Update common freq also for RX receiver_model.set_target_frequency(f); // Update common freq also for RX
} }
}; };
field_frequency.on_edit = [this, &nav]() { field_frequency.on_edit = [this, &nav]() {
@@ -327,10 +316,10 @@ MicTXView::MicTXView(
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
tx_frequency = f; tx_frequency = f;
if (!rx_enabled) { if (!rx_enabled) {
transmitter_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
} else { } else {
if (bool_same_F_tx_rx_enabled) if (bool_same_F_tx_rx_enabled)
receiver_model.set_tuning_frequency(f); // Update freq also for RX receiver_model.set_target_frequency(f); // Update freq also for RX
} }
this->field_frequency.set_value(f); this->field_frequency.set_value(f);
set_dirty(); set_dirty();
@@ -492,7 +481,7 @@ MicTXView::MicTXView(
bool_same_F_tx_rx_enabled = v; bool_same_F_tx_rx_enabled = v;
field_rxfrequency.hidden(v); // Hide or show separated freq RX field . (When no hide user can enter down indep. freq for RX) field_rxfrequency.hidden(v); // Hide or show separated freq RX field . (When no hide user can enter down indep. freq for RX)
set_dirty(); // Refresh GUI interface set_dirty(); // Refresh GUI interface
receiver_model.set_tuning_frequency(v ? tx_frequency : rx_frequency); // To go to the proper tuned freq. when toggling it receiver_model.set_target_frequency(v ? tx_frequency : rx_frequency); // To go to the proper tuned freq. when toggling it
}; };
field_va_level.on_change = [this](int32_t v) { field_va_level.on_change = [this](int32_t v) {
@@ -542,13 +531,13 @@ MicTXView::MicTXView(
field_squelch.set_value(0); field_squelch.set_value(0);
receiver_model.set_squelch_level(0); receiver_model.set_squelch_level(0);
rx_frequency = receiver_model.tuning_frequency(); rx_frequency = receiver_model.target_frequency();
field_rxfrequency.set_value(rx_frequency); field_rxfrequency.set_value(rx_frequency);
field_rxfrequency.set_step(receiver_model.frequency_step()); field_rxfrequency.set_step(receiver_model.frequency_step());
field_rxfrequency.on_change = [this](rf::Frequency f) { // available when field rxfrequency not hidden => user selected separated freq RX/TX- field_rxfrequency.on_change = [this](rf::Frequency f) { // available when field rxfrequency not hidden => user selected separated freq RX/TX-
rx_frequency = f; rx_frequency = f;
if (rx_enabled) if (rx_enabled)
receiver_model.set_tuning_frequency(f); receiver_model.set_target_frequency(f);
}; };
field_rxfrequency.on_edit = [this, &nav]() { // available when field rxfrequency not hidden => user selected separated freq RX/TX- field_rxfrequency.on_edit = [this, &nav]() { // available when field rxfrequency not hidden => user selected separated freq RX/TX-
focused_ui = 1; focused_ui = 1;
@@ -557,7 +546,7 @@ MicTXView::MicTXView(
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
rx_frequency = f; rx_frequency = f;
if (rx_enabled) if (rx_enabled)
receiver_model.set_tuning_frequency(f); receiver_model.set_target_frequency(f);
this->field_rxfrequency.set_value(f); this->field_rxfrequency.set_value(f);
set_dirty(); set_dirty();
}; };
@@ -606,6 +595,9 @@ MicTXView::MicTXView(
} }
}; };
// These shouldn't be necessary, but because
// this app uses both transmitter_model and directly
// configures the baseband, these end up being required.
transmitter_model.set_sampling_rate(sampling_rate); transmitter_model.set_sampling_rate(sampling_rate);
transmitter_model.set_baseband_bandwidth(1750000); transmitter_model.set_baseband_bandwidth(1750000);
@@ -617,12 +609,11 @@ MicTXView::MicTXView(
MicTXView::~MicTXView() { MicTXView::~MicTXView() {
audio::input::stop(); audio::input::stop();
transmitter_model.set_tuning_frequency(tx_frequency); // Save Tx frequency instead of Rx. Or maybe we need some "System Wide" changes to seperate Tx and Rx frequency. transmitter_model.set_target_frequency(tx_frequency); // Save Tx frequency instead of Rx. Or maybe we need some "System Wide" changes to seperate Tx and Rx frequency.
transmitter_model.disable(); transmitter_model.disable();
if (rx_enabled) // Also turn off audio rx if enabled if (rx_enabled) // Also turn off audio rx if enabled
rxaudio(false); rxaudio(false);
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
} // namespace ui } // namespace ui
+14 -1
View File
@@ -26,6 +26,8 @@
#define SHORT_UI true #define SHORT_UI true
#define NORMAL_UI false #define NORMAL_UI false
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui.hpp" #include "ui.hpp"
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
@@ -71,7 +73,7 @@ class MicTXView : public View {
void update_vumeter(); void update_vumeter();
void do_timing(); void do_timing();
void set_tx(bool enable); void set_tx(bool enable);
// void on_tuning_frequency_changed(rf::Frequency f); // void on_target_frequency_changed(rf::Frequency f);
void on_tx_progress(const bool done); void on_tx_progress(const bool done);
void configure_baseband(); void configure_baseband();
@@ -79,6 +81,15 @@ class MicTXView : public View {
void set_ptt_visibility(bool v); void set_ptt_visibility(bool v);
RxRadioState rx_radio_state_{};
TxRadioState tx_radio_state_{
1750000 /* bandwidth */,
sampling_rate /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_mic", app_settings::Mode::RX_TX,
app_settings::Options::UseGlobalTargetFrequency};
bool transmitting{false}; bool transmitting{false};
bool va_enabled{false}; bool va_enabled{false};
bool ptt_enabled{true}; bool ptt_enabled{true};
@@ -193,6 +204,7 @@ class MicTXView : public View {
{"OFF-08dB", 4}, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = -12 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)
}}; }};
// TODO: Use TxFrequencyField
FrequencyField field_frequency{ FrequencyField field_frequency{
{5 * 8, 3 * 8}, {5 * 8, 3 * 8},
}; };
@@ -298,6 +310,7 @@ class MicTXView : public View {
' ', ' ',
}; };
// TODO: Use RxFrequencyField
FrequencyField field_rxfrequency{ FrequencyField field_rxfrequency{
{10 * 8, (25 * 8) + 2}, {10 * 8, (25 * 8) + 2},
}; };
@@ -38,8 +38,6 @@ class ModemSetupView : public View {
std::string title() const override { return "Modem setup"; }; std::string title() const override { return "Modem setup"; };
private: private:
void update_freq(rf::Frequency f);
Labels labels{ Labels labels{
{{2 * 8, 11 * 8}, "Baudrate:", Color::light_grey()}, {{2 * 8, 11 * 8}, "Baudrate:", Color::light_grey()},
{{2 * 8, 13 * 8}, "Mark: Hz", Color::light_grey()}, {{2 * 8, 13 * 8}, "Mark: Hz", Color::light_grey()},
+3 -21
View File
@@ -26,7 +26,6 @@
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "hackrf_gpio.hpp" #include "hackrf_gpio.hpp"
#include "portapack_shared_memory.hpp" #include "portapack_shared_memory.hpp"
#include "cpld_update.hpp"
#include "ui_textentry.hpp" #include "ui_textentry.hpp"
#include "string_format.hpp" #include "string_format.hpp"
@@ -98,13 +97,8 @@ void MorseView::focus() {
} }
MorseView::~MorseView() { MorseView::~MorseView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_morse", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit . baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
void MorseView::paint(Painter&) { void MorseView::paint(Painter&) {
@@ -123,9 +117,6 @@ bool MorseView::start_tx() {
} }
progressbar.set_max(symbol_count); progressbar.set_max(symbol_count);
transmitter_model.set_sampling_rate(1536000U);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
if (modulation == CW) { if (modulation == CW) {
@@ -206,15 +197,6 @@ MorseView::MorseView(
&progressbar, &progressbar,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_morse", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
// Default settings // Default settings
field_speed.set_value(15); // 15wps field_speed.set_value(15); // 15wps
field_tone.set_value(700); // 700Hz FM tone field_tone.set_value(700); // 700Hz FM tone
@@ -251,9 +233,9 @@ MorseView::MorseView(
}; };
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
receiver_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+7 -3
View File
@@ -28,6 +28,7 @@
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_transmitter.hpp" #include "ui_transmitter.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "message.hpp" #include "message.hpp"
#include "volume.hpp" #include "volume.hpp"
@@ -68,9 +69,12 @@ class MorseView : public View {
std::string message{}; std::string message{};
uint32_t time_units{0}; uint32_t time_units{0};
// app save settings TxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; 1536000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_morse", app_settings::Mode::TX};
enum modulation_t { enum modulation_t {
CW = 0, CW = 0,
+3 -31
View File
@@ -40,11 +40,8 @@ void NRFRxView::focus() {
field_frequency.focus(); field_frequency.focus();
} }
void NRFRxView::update_freq(rf::Frequency f) { NRFRxView::NRFRxView(NavigationView& nav)
receiver_model.set_tuning_frequency(f); : nav_{nav} {
}
NRFRxView::NRFRxView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_nrf_rx); baseband::run_image(portapack::spi_flash::image_tag_nrf_rx);
add_children({&rssi, add_children({&rssi,
@@ -56,16 +53,8 @@ NRFRxView::NRFRxView(NavigationView& nav) {
&button_modem_setup, &button_modem_setup,
&console}); &console});
// load app settings
auto rc = settings.load("rx_nrf", &app_settings);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
}
// Auto-configure modem for LCR RX (will be removed later) // Auto-configure modem for LCR RX (will be removed later)
update_freq(2480000000); field_frequency.set_value(2480000000);
auto def_bell202 = &modem_defs[0]; auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate); persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format; serial_format_t serial_format;
@@ -75,18 +64,7 @@ NRFRxView::NRFRxView(NavigationView& nav) {
serial_format.bit_order = LSB_FIRST; serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format); persistent_memory::set_serial_format(serial_format);
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(100); field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
button_modem_setup.on_select = [&nav](Button&) { button_modem_setup.on_select = [&nav](Button&) {
nav.push<ModemSetupView>(); nav.push<ModemSetupView>();
@@ -98,8 +76,6 @@ NRFRxView::NRFRxView(NavigationView& nav) {
audio::set_rate(audio::Rate::Hz_24000); audio::set_rate(audio::Rate::Hz_24000);
audio::output::start(); audio::output::start();
receiver_model.set_sampling_rate(4000000);
receiver_model.set_baseband_bandwidth(4000000);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.enable(); receiver_model.enable();
} }
@@ -151,10 +127,6 @@ void NRFRxView::on_data(uint32_t value, bool is_data) {
} }
NRFRxView::~NRFRxView() { NRFRxView::~NRFRxView() {
// save app settings
app_settings.rx_frequency = field_frequency.value();
settings.save("rx_nrf", &app_settings);
audio::output::stop(); audio::output::stop();
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
+13 -12
View File
@@ -27,7 +27,9 @@
#include "ui.hpp" #include "ui.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_record_view.hpp" // DEBUG #include "ui_record_view.hpp" // DEBUG
#include "utility.hpp" #include "utility.hpp"
@@ -46,9 +48,13 @@ class NRFRxView : public View {
private: private:
void on_data(uint32_t value, bool is_data); void on_data(uint32_t value, bool is_data);
// app save settings NavigationView& nav_;
std::app_settings settings{}; RxRadioState radio_state_{
std::app_settings::AppSettings app_settings{}; 4000000 /* bandwidth */,
4000000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_nrf", app_settings::Mode::RX};
uint8_t console_color{0}; uint8_t console_color{0};
uint32_t prev_value{0}; uint32_t prev_value{0};
@@ -60,15 +66,13 @@ class NRFRxView : public View {
VGAGainField field_vga{ VGAGainField field_vga{
{18 * 8, 0 * 16}}; {18 * 8, 0 * 16}};
RSSI rssi{ RSSI rssi{
{21 * 8, 0, 6 * 8, 4}, {21 * 8, 0, 6 * 8, 4}};
};
Channel channel{ Channel channel{
{21 * 8, 5, 6 * 8, 4}, {21 * 8, 5, 6 * 8, 4}};
};
FrequencyField field_frequency{ RxFrequencyField field_frequency{
{0 * 8, 0 * 16}, {0 * 8, 0 * 16},
}; nav_};
Button button_modem_setup{ Button button_modem_setup{
{240 - 12 * 8, 1 * 16, 96, 24}, {240 - 12 * 8, 1 * 16, 96, 24},
@@ -77,9 +81,6 @@ class NRFRxView : public View {
Console console{ Console console{
{0, 4 * 16, 240, 240}}; {0, 4 * 16, 240, 240}};
void update_freq(rf::Frequency f);
// void on_data_afsk(const AFSKDataMessage& message);
MessageHandlerRegistration message_handler_packet{ MessageHandlerRegistration message_handler_packet{
Message::ID::AFSKData, Message::ID::AFSKData,
[this](Message* const p) { [this](Message* const p) {
-2
View File
@@ -129,9 +129,7 @@ void NumbersStationView::start_tx() {
prepare_audio(); prepare_audio();
transmitter_model.set_sampling_rate(1536000U);
transmitter_model.set_rf_amp(true); transmitter_model.set_rf_amp(true);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
baseband::set_audiotx_data( baseband::set_audiotx_data(
+6
View File
@@ -34,6 +34,7 @@
#include "message.hpp" #include "message.hpp"
#include "file.hpp" #include "file.hpp"
#include "io_wave.hpp" #include "io_wave.hpp"
#include "radio_state.hpp"
namespace ui { namespace ui {
@@ -54,6 +55,11 @@ class NumbersStationView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
TxRadioState radio_state_{
1750000 /* bandwidth */,
1536000 /* sampling rate */
};
// Sequencing state machine // Sequencing state machine
enum segments { enum segments {
IDLE = 0, IDLE = 0,
+2 -4
View File
@@ -77,11 +77,9 @@ void NuoptixView::transmit(bool setup) {
timecode = number_timecode.value(); timecode = number_timecode.value();
} }
transmitter_model.set_sampling_rate(1536000U);
transmitter_model.set_rf_amp(true); transmitter_model.set_rf_amp(true);
transmitter_model.set_lna(40); transmitter_model.set_lna(40);
transmitter_model.set_vga(40); transmitter_model.set_vga(40);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
dtmf_message[0] = '*'; // "Pre-tone for restart" method #1 dtmf_message[0] = '*'; // "Pre-tone for restart" method #1
@@ -156,9 +154,9 @@ NuoptixView::NuoptixView(
number_timecode.set_value(1); number_timecode.set_value(1);
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
receiver_model.set_tuning_frequency(f); transmitter_model.target_frequency(f);
}; };
}; };
+6 -1
View File
@@ -33,6 +33,7 @@
#include "message.hpp" #include "message.hpp"
#include "volume.hpp" #include "volume.hpp"
#include "audio.hpp" #include "audio.hpp"
#include "radio_state.hpp"
#define NUOPTIX_TONE_LENGTH ((TONES_SAMPLERATE * 0.049) - 1) // 49ms #define NUOPTIX_TONE_LENGTH ((TONES_SAMPLERATE * 0.049) - 1) // 49ms
@@ -54,9 +55,13 @@ class NuoptixView : public View {
IMPROVISE IMPROVISE
}; };
TxRadioState radio_state_{
1750000 /* bandwidth */,
1536000 /* sampling rate */
};
tx_modes tx_mode{IDLE}; tx_modes tx_mode{IDLE};
void on_tuning_frequency_changed(rf::Frequency f);
void transmit(bool setup); void transmit(bool setup);
void on_tx_progress(const uint32_t progress, const bool done); void on_tx_progress(const uint32_t progress, const bool done);
+368 -214
View File
@@ -2,6 +2,7 @@
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek * Copyright (C) 2016 Furrtek
* Copyleft () 2022 NotPike * Copyleft () 2022 NotPike
* Copyright (C) 2023 Kyle Reed, zxkmm
* *
* This file is part of PortaPack. * This file is part of PortaPack.
* *
@@ -22,10 +23,13 @@
*/ */
#include "ui_playlist.hpp" #include "ui_playlist.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp" #include "convert.hpp"
#include "file_reader.hpp"
#include "io_file.hpp" #include "io_file.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "utility.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "portapack.hpp" #include "portapack.hpp"
@@ -34,223 +38,326 @@
#include <fstream> #include <fstream>
using namespace portapack; using namespace portapack;
namespace fs = std::filesystem;
/* TODO
* - Should frequency overrides be saved in the playlist?
* - Start playing from current track (vs start over)?
*/
namespace ui { namespace ui {
void PlaylistView::set_ready() { // TODO: consolidate extesions into a shared header?
ready_signal = true; static const fs::path c16_ext = u".C16";
} static const fs::path ppl_ext = u".PPL";
void PlaylistView::load_file(std::filesystem::path playlist_path) { void PlaylistView::load_file(const fs::path& playlist_path) {
File playlist_file; File playlist_file;
auto error = playlist_file.open(playlist_path.string()); auto error = playlist_file.open(playlist_path.string());
if (!error.is_valid()) {
std::string line; if (error)
char one_char[1]; return;
for (size_t pointer = 0; pointer < playlist_file.size(); pointer++) {
playlist_file.seek(pointer); auto reader = FileLineReader(playlist_file);
playlist_file.read(one_char, 1); for (const auto& line : reader) {
if ((int)one_char[0] >= ' ') { if (line.length() == 0 || line[0] == '#')
line += one_char[0]; continue; // Empty or comment line.
} else if (one_char[0] == '\n') {
total_tracks++; auto cols = split_string(line, ',');
txtline_process(line); auto entry = load_entry(trim(cols[0]));
line.clear();
} if (!entry)
} continue;
if (line.length() > 0) {
total_tracks++; // Read optional delay value.
txtline_process(line); if (cols.size() > 1)
} parse_int(cols[1], entry->ms_delay);
playlist_db_.emplace_back(*std::move(entry));
} }
playlist_masterdb = playlist_db;
return;
} }
void PlaylistView::txtline_process(std::string& line) { Optional<PlaylistView::playlist_entry> PlaylistView::load_entry(fs::path&& path) {
playlist_entry new_item; File capture_file;
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); auto error = capture_file.open(path);
new_item.replay_frequency = f; if (error)
new_item.replay_file = "/" + file; return {};
new_item.sample_rate = sample_rate;
new_item.next_delay = 0;
playlist_db.push_back(std::move(new_item)); // Read metafile if it exists.
auto metadata_path = get_metadata_path(path);
auto metadata = read_metadata_file(metadata_path);
// If no metadata found, fallback to the TX frequency.
if (!metadata)
metadata = {transmitter_model.target_frequency(), 500'000};
return playlist_entry{
std::move(path),
*metadata,
capture_file.size(),
0u};
} }
void PlaylistView::on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate) { void PlaylistView::on_file_changed(const fs::path& new_file_path) {
File data_file; stop();
// Get file size
auto data_open_error = data_file.open("/" + new_file_path.string()); current_index_ = 0;
if (!data_open_error.is_valid()) { playlist_path_ = new_file_path;
track_number++; playlist_db_.clear();
} else if (data_open_error.is_valid()) { load_file(playlist_path_);
file_error("C16 file\n" + new_file_path.string() + "\nread error.");
update_ui();
// TODO: fix in UI framework with 'try_focus()'?
// Hack around focus getting called by ctor before parent is set.
if (parent())
button_play.focus();
}
void PlaylistView::open_file(bool prompt_save) {
if (playlist_dirty_ && prompt_save) {
nav_.display_modal(
"Save?", "Save changes?", YESNO,
[this](bool choice) {
if (choice)
save_file(false);
});
nav_.set_on_pop([this]() { open_file(false); });
return; return;
} }
file_path = new_file_path; auto open_view = nav_.push<FileLoadView>(".PPL");
field_frequency.set_value(replay_frequency); open_view->push_dir(u"PLAYLIST");
open_view->on_changed = [this](fs::path new_file_path) {
sample_rate = replay_sample_rate; on_file_changed(new_file_path);
};
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));
// text_track.set(std::to_string(track_number) + "/" + std::to_string(total_tracks));
button_play.focus();
} }
void PlaylistView::on_tx_progress(const uint32_t progress) { void PlaylistView::save_file(bool show_dialogs) {
progressbar.set_value(progress); if (!playlist_dirty_ || playlist_path_.empty())
return;
ensure_directory(playlist_path_.parent_path());
File playlist_file;
auto error = playlist_file.create(playlist_path_.string());
if (error) {
if (show_dialogs)
nav_.display_modal(
"Save Error",
"Could not save file\n" + playlist_path_.string());
return;
}
for (const auto& entry : playlist_db_) {
playlist_file.write_line(
entry.path.string() + "," +
to_string_dec_uint(entry.ms_delay));
}
playlist_dirty_ = false;
if (show_dialogs)
nav_.display_modal(
"Save",
"Saved playlist\n" + playlist_path_.string());
} }
void PlaylistView::focus() { void PlaylistView::add_entry(fs::path&& path) {
button_open.focus(); if (playlist_path_.empty()) {
playlist_path_ = next_filename_matching_pattern(u"/PLAYLIST/PLAY_????.PPL");
// Hack around focus getting called by ctor before parent is set.
if (parent())
button_play.focus();
}
auto entry = load_entry(std::move(path));
if (entry) {
playlist_db_.emplace_back(*std::move(entry));
current_index_ = playlist_db_.size() - 1;
playlist_dirty_ = true;
}
update_ui();
} }
void PlaylistView::file_error(std::string error_message) { void PlaylistView::delete_entry() {
nav_.display_modal("Error", "Error for \n" + file_path.string() + "\n" + error_message); if (playlist_db_.empty())
return;
// Ugh, the STL is gross.
playlist_db_.erase(playlist_db_.begin() + current_index_);
if (current_index_ > 0)
--current_index_;
playlist_dirty_ = true;
update_ui();
}
void PlaylistView::show_file_error(const fs::path& path, const std::string& message) {
nav_.display_modal("Error", "Error opening file \n" + path.string() + "\n" + message);
}
PlaylistView::playlist_entry* PlaylistView::current() {
return playlist_db_.empty() ? nullptr : &playlist_db_[current_index_];
} }
bool PlaylistView::is_active() const { bool PlaylistView::is_active() const {
return (bool)replay_thread; return replay_thread_ != nullptr;
} }
bool PlaylistView::loop() const { bool PlaylistView::at_end() const {
return (bool)playlist_db.size(); return current_index_ >= playlist_db_.size();
} }
void PlaylistView::toggle() { void PlaylistView::toggle() {
if (is_active()) { if (is_active())
stop(false); stop();
total_tracks = 0; else
track_number = 0; start();
playlist_db.clear();
playlist_masterdb.clear();
} else {
total_tracks = 0;
track_number = 0;
playlist_db.clear();
playlist_masterdb.clear();
load_file(now_play_list_file);
if (!playlist_db.empty()) {
start();
}
}
} }
void PlaylistView::start() { void PlaylistView::start() {
stop(false); if (playlist_db_.empty())
return; // Nothing to do.
playlist_entry item = playlist_db.front(); current_index_ = 0u;
playlist_db.pop_front(); send_current_track();
// 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; /* Advance to the next track in the playlist. */
bool PlaylistView::next_track() {
++current_index_;
auto p = std::make_unique<FileReader>(); // Reset to the 0 once at the end to prevent current_index_
auto open_error = p->open(file_path); // from being outside the bounds of playlist_db_ when painting.
if (open_error.is_valid()) { if (at_end()) {
file_error("Illegal grammar"); current_index_ = 0;
return; // Fixes TX bug if there's a file error return check_loop.value(); // Keep going if looping.
} else {
reader = std::move(p);
} }
if (reader) { return true;
button_play.set_bitmap(&bitmap_stop); }
baseband::set_sample_rate(sample_rate * 8);
replay_thread = std::make_unique<ReplayThread>( /* Transmits the current_entry_ */
std::move(reader), void PlaylistView::send_current_track() {
read_size, buffer_count, // Prepare to send a file.
&ready_signal, replay_thread_.reset();
[](uint32_t return_code) { transmitter_model.disable();
ReplayThreadDoneMessage message{return_code}; ready_signal_ = false;
EventDispatcher::send_message(message);
}); if (!current())
return;
// TODO: Use a timer so the UI isn't frozen.
if (current()->ms_delay > 0)
chThdSleepMilliseconds(current()->ms_delay);
// Open the sample file to send.
auto reader = std::make_unique<FileReader>();
auto error = reader->open(current()->path);
if (error) {
show_file_error(current()->path, "Can't open file to send.");
return;
} }
transmitter_model.set_sampling_rate(sample_rate * 8); // ReplayThread starts immediately on construction so
// these need to be set before creating the ReplayThread.
transmitter_model.set_target_frequency(current()->metadata.center_frequency);
transmitter_model.set_sampling_rate(current()->metadata.sample_rate * 8);
transmitter_model.set_baseband_bandwidth(baseband_bandwidth); transmitter_model.set_baseband_bandwidth(baseband_bandwidth);
transmitter_model.enable(); transmitter_model.enable();
// Set baseband sample rate too for waterfall to be correct.
// TODO: Why doesn't the transmitter_model just handle this?
baseband::set_sample_rate(transmitter_model.sampling_rate());
// Reset the transmit progress bar.
progressbar_transmit.set_value(0);
// Use the ReplayThread class to send the data.
replay_thread_ = std::make_unique<ReplayThread>(
std::move(reader),
/* read_size */ 0x4000,
/* buffer_count */ 3,
&ready_signal_,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});
// Now it's sending, update the UI.
update_ui();
} }
void PlaylistView::stop(const bool do_loop) { void PlaylistView::stop() {
if (is_active()) { // This terminates the underlying chThread.
replay_thread.reset(); replay_thread_.reset();
} transmitter_model.disable();
// TODO: the logic here could be more beautiful but maybe they are all same for compiler anyway.... // Reset the transmit progress bar.
// Notes of the logic here in case if it needed to be changed in the future: progressbar_transmit.set_value(0);
// 1. check_loop.value() is for the LOOP checkbox update_ui();
// 2. do_loop is a part of the replay thread, not a user - control thing.
// 3. when (total_tracks > track_number) is true, it means that the current track is not the last track.
// Thus, (do_loop && (total_tracks != track_number)) is for the case when the start() func were called with true AND not the last track.
// Which means it do loop until the last track.
if (check_loop.value()) {
if (do_loop) {
if (playlist_db.size() > 0) {
start();
} else {
playlist_db = playlist_masterdb;
start();
}
} else {
transmitter_model.disable();
button_play.set_bitmap(&bitmap_play);
}
} else if (!check_loop.value()) {
if (do_loop && (total_tracks > track_number)) {
if (playlist_db.size() > 0) {
start();
} else {
playlist_db = playlist_masterdb;
start();
}
} else {
transmitter_model.disable();
button_play.set_bitmap(&bitmap_play);
}
}
ready_signal = false;
} }
void PlaylistView::handle_replay_thread_done(const uint32_t return_code) { void PlaylistView::update_ui() {
if (playlist_db_.empty()) {
text_filename.set("-");
text_sample_rate.set("-");
text_duration.set("-");
if (playlist_path_.empty())
text_track.set("Open playlist or add capture.");
else
text_track.set(playlist_path_.filename().string());
progressbar_track.set_value(0);
progressbar_track.set_max(0);
progressbar_transmit.set_max(0);
} else {
chDbgAssert(!at_end(), "update_ui #1", "current_index_ invalid");
text_filename.set(current()->path.filename().string());
text_sample_rate.set(unit_auto_scale(current()->metadata.sample_rate, 3, 0) + "Hz");
auto duration = ms_duration(current()->file_size, current()->metadata.sample_rate, 4);
text_duration.set(to_string_time_ms(duration));
field_frequency.set_value(current()->metadata.center_frequency);
text_track.set(
to_string_dec_uint(current_index_ + 1) + "/" +
to_string_dec_uint(playlist_db_.size()) + " " +
playlist_path_.filename().string());
progressbar_track.set_max(playlist_db_.size() - 1);
progressbar_track.set_value(current_index_);
progressbar_transmit.set_max(current()->file_size);
}
button_play.set_bitmap(is_active() ? &bitmap_stop : &bitmap_play);
}
void PlaylistView::on_tx_progress(uint32_t progress) {
progressbar_transmit.set_value(progress);
}
void PlaylistView::handle_replay_thread_done(uint32_t return_code) {
if (return_code == ReplayThread::END_OF_FILE) { if (return_code == ReplayThread::END_OF_FILE) {
stop(true); if (next_track()) {
} else if (return_code == ReplayThread::READ_ERROR) { send_current_track();
stop(false); return;
file_error("Replay thread read error"); }
} }
progressbar.set_value(0); if (return_code == ReplayThread::READ_ERROR)
show_file_error(current()->path, "Replay read failed.");
stop();
} }
PlaylistView::PlaylistView( PlaylistView::PlaylistView(
@@ -259,47 +366,101 @@ PlaylistView::PlaylistView(
baseband::run_image(portapack::spi_flash::image_tag_replay); baseband::run_image(portapack::spi_flash::image_tag_replay);
add_children({ add_children({
&button_open,
&text_filename, &text_filename,
&text_sample_rate, &text_sample_rate,
&text_duration, &text_duration,
&progressbar, &progressbar_track,
&progressbar_transmit,
&field_frequency, &field_frequency,
&tx_view, // this handles now the previous rfgain, rfamp &tx_view,
&check_loop, &check_loop,
&button_play, &button_play,
// &text_track, &text_track,
// TODO: add track number &button_prev,
&button_add,
&button_delete,
&button_open,
&button_save,
&button_next,
&waterfall, &waterfall,
}); });
field_frequency.set_value(target_frequency()); waterfall.show_audio_spectrum_view(false);
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.set_value(100'000'000);
field_frequency.on_change = [this](rf::Frequency f) { field_frequency.on_change = [this](rf::Frequency f) {
this->on_target_frequency_changed(f); if (current())
current()->metadata.center_frequency = f;
}; };
field_frequency.on_edit = [this, &nav]() { field_frequency.on_edit = [this]() {
// TODO: Provide separate modal method/scheme? auto freq_view = nav_.push<FrequencyKeypadView>(field_frequency.value());
auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency()); freq_view->on_changed = [this](rf::Frequency f) {
new_view->on_changed = [this](rf::Frequency f) { field_frequency.set_value(f);
this->on_target_frequency_changed(f);
this->field_frequency.set_value(f);
}; };
}; };
field_frequency.set_step(5000);
button_play.on_select = [this](ImageButton&) { button_play.on_select = [this](ImageButton&) {
this->toggle(); toggle();
}; };
button_open.on_select = [this, &nav](Button&) { button_add.on_select = [this, &nav]() {
auto open_view = nav.push<FileLoadView>(".PPL"); if (is_active())
open_view->on_changed = [this](std::filesystem::path new_file_path) { return;
now_play_list_file = new_file_path; auto open_view = nav_.push<FileLoadView>(".C16");
load_file(new_file_path); open_view->push_dir(u"CAPTURES");
open_view->on_changed = [this](fs::path path) {
add_entry(std::move(path));
}; };
}; };
button_delete.on_select = [this, &nav]() {
if (is_active())
return;
delete_entry();
};
button_open.on_select = [this, &nav]() {
if (is_active())
stop();
open_file();
};
button_save.on_select = [this]() {
if (is_active())
stop();
save_file();
};
button_prev.on_select = [this]() {
if (is_active())
return;
--current_index_;
if (at_end())
current_index_ = playlist_db_.size() - 1;
update_ui();
};
button_next.on_select = [this]() {
if (is_active())
return;
++current_index_;
if (at_end())
current_index_ = 0;
update_ui();
};
update_ui();
}
PlaylistView::PlaylistView(
NavigationView& nav,
const fs::path& path)
: PlaylistView(nav) {
auto ext = path.extension();
if (path_iequal(ext, ppl_ext))
on_file_changed(path);
else if (path_iequal(ext, c16_ext))
add_entry(fs::path{path});
} }
PlaylistView::~PlaylistView() { PlaylistView::~PlaylistView() {
@@ -307,33 +468,26 @@ PlaylistView::~PlaylistView() {
baseband::shutdown(); baseband::shutdown();
} }
void PlaylistView::set_parent_rect(Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
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::focus() {
if (playlist_path_.empty())
button_add.focus();
else
button_play.focus();
}
void PlaylistView::on_hide() { void PlaylistView::on_hide() {
stop(false); stop();
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
waterfall.on_hide(); waterfall.on_hide();
View::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 */ } /* namespace ui */
+129 -76
View File
@@ -1,6 +1,7 @@
/* /*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek * Copyright (C) 2016 Furrtek
* Copyright (C) 2023 Kyle Reed, zxkmm
* *
* This file is part of PortaPack. * This file is part of PortaPack.
* *
@@ -23,141 +24,193 @@
#define SHORT_UI true #define SHORT_UI true
#define NORMAL_UI false #define NORMAL_UI false
#include "ui_widget.hpp" #include "app_settings.hpp"
#include "bitmap.hpp"
#include "file.hpp"
#include "metadata_file.hpp"
#include "radio_state.hpp"
#include "replay_thread.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "replay_thread.hpp"
#include "ui_spectrum.hpp" #include "ui_spectrum.hpp"
#include "ui_transmitter.hpp"
#include "ui_widget.hpp"
#include <string> #include <string>
#include <memory> #include <memory>
#include <deque> #include <vector>
#include "ui_transmitter.hpp"
namespace ui { namespace ui {
class PlaylistView : public View { class PlaylistView : public View {
public: public:
PlaylistView(NavigationView& nav); PlaylistView(NavigationView& nav);
PlaylistView(NavigationView& nav, const std::filesystem::path& path);
~PlaylistView(); ~PlaylistView();
// Following 2 called by 'NavigationView::update_view' after view is created.
void set_parent_rect(Rect new_parent_rect) override;
void focus() override;
void on_hide() override; void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override; std::string title() const override { return "Replay"; };
void focus() override;
std::string title() const override { return "Playlist"; };
private: private:
NavigationView& nav_; NavigationView& nav_;
TxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"tx_playlist", app_settings::Mode::TX};
static constexpr ui::Dim header_height = 3 * 16; // More header == less spectrum view.
static constexpr ui::Dim header_height = 6 * 16;
static constexpr uint32_t baseband_bandwidth = 2500000;
struct playlist_entry { struct playlist_entry {
rf::Frequency replay_frequency{0}; std::filesystem::path path{};
std::string replay_file{}; capture_metadata metadata{};
uint32_t sample_rate{}; File::Size file_size{};
uint32_t next_delay{}; uint32_t ms_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); std::unique_ptr<ReplayThread> replay_thread_{};
void txtline_process(std::string&); bool ready_signal_{}; // Used to signal the ReplayThread.
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); size_t current_index_{0};
void on_tx_progress(const uint32_t progress); bool playlist_dirty_{};
void set_target_frequency(const rf::Frequency new_value); std::vector<playlist_entry> playlist_db_{};
rf::Frequency target_frequency() const; std::filesystem::path playlist_path_{};
void load_file(const std::filesystem::path& path);
Optional<playlist_entry> load_entry(std::filesystem::path&& path);
void on_file_changed(const std::filesystem::path& path);
void open_file(bool prompt_save = true);
void save_file(bool show_dialogs = true);
void add_entry(std::filesystem::path&& path);
void delete_entry();
void reset();
void show_file_error(
const std::filesystem::path& path,
const std::string& message);
playlist_entry* current();
bool is_active() const;
bool at_end() const;
void toggle(); void toggle();
void start(); void start();
void stop(const bool do_loop); bool next_track();
bool is_active() const; void send_current_track();
bool loop() const; void stop();
void set_ready();
void handle_replay_thread_done(const uint32_t return_code);
void file_error(std::string error_message);
std::filesystem::path file_path{}; void update_ui();
std::unique_ptr<ReplayThread> replay_thread{};
bool ready_signal{false};
size_t track_number{0};
size_t total_tracks{0};
std::filesystem::path now_play_list_file{};
Button button_open{ /* There are called by Message handlers. */
{0 * 8, 0 * 16, 10 * 8, 2 * 16}, void on_tx_progress(uint32_t progress);
"Open file"}; void handle_replay_thread_done(uint32_t return_code);
Text text_filename{ Text text_filename{
{11 * 8, 0 * 16, 12 * 8, 16}, {0 * 8, 0 * 16, 30 * 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{ FrequencyField field_frequency{
{0 * 8, 2 * 16}, {0 * 8, 1 * 16}};
};
Text text_sample_rate{
{10 * 8, 1 * 16, 7 * 8, 16}};
/*v making there's 1px line (instead of two) between two progress bars,
* by letting 1px overlapped.
* So, since they overlapped 1px, they are visually same, and looks better.
*/
ProgressBar progressbar_track{
{18 * 8, 1 * 16, 12 * 8, 8 + 1}};
ProgressBar progressbar_transmit{
{18 * 8, 3 * 8 - 1, 12 * 8, 8}};
Text text_duration{
{0 * 8, 2 * 16, 5 * 8, 16}};
// TODO: delay duration field.
TransmitterView2 tx_view{ TransmitterView2 tx_view{
// new handling of NumberField field_rfgain, NumberField field_rfamp 9 * 8, 1 * 8, SHORT_UI};
74, 1 * 8, SHORT_UI // x(columns), y (line) position. (Used in Repay / GPS Simul / Play list App)
// 10*8, 2*8, NORMAL_UI // x(columns), y (line) position. (Used in Mic App)
};
Checkbox check_loop{ Checkbox check_loop{
{21 * 8, 2 * 16}, {21 * 8, 2 * 16},
4, 4,
"Loop", "Loop",
true}; true};
ImageButton button_play{ ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16}, {28 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play, &bitmap_play,
Color::green(), Color::green(),
Color::black()}; Color::black()};
// TODO: add track number
// Text text_track{
// {18 * 8, 1 * 16, 12 * 8, 16},
// "0/0"};
spectrum::WaterfallWidget waterfall{}; Text text_track{
{0 * 8, 3 * 16, 30 * 8, 16}};
NewButton button_prev{
{2 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_arrow_left,
Color::dark_grey()};
NewButton button_next{
{6 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_arrow_right,
Color::dark_grey()};
NewButton button_add{
{11 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_icon_new_file,
Color::orange()};
NewButton button_delete{
{15 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_icon_delete,
Color::orange()};
NewButton button_open{
{20 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_icon_load,
Color::dark_blue()};
NewButton button_save{
{24 * 8, 4 * 16, 4 * 8, 2 * 16},
"",
&bitmap_icon_save,
Color::dark_blue()};
spectrum::WaterfallView waterfall{};
MessageHandlerRegistration message_handler_replay_thread_error{ MessageHandlerRegistration message_handler_replay_thread_error{
Message::ID::ReplayThreadDone, Message::ID::ReplayThreadDone,
[this](const Message* const p) { [this](const Message* p) {
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p); auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
this->handle_replay_thread_done(message.return_code); handle_replay_thread_done(message.return_code);
}}; }};
MessageHandlerRegistration message_handler_fifo_signal{ MessageHandlerRegistration message_handler_fifo_signal{
Message::ID::RequestSignal, Message::ID::RequestSignal,
[this](const Message* const p) { [this](const Message* p) {
const auto message = static_cast<const RequestSignalMessage*>(p); auto message = static_cast<const RequestSignalMessage*>(p);
if (message->signal == RequestSignalMessage::Signal::FillRequest) { if (message->signal == RequestSignalMessage::Signal::FillRequest) {
this->set_ready(); ready_signal_ = true;
} }
}}; }};
MessageHandlerRegistration message_handler_tx_progress{ MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress, Message::ID::TXProgress,
[this](const Message* const p) { [this](const Message* p) {
const auto message = *reinterpret_cast<const TXProgressMessage*>(p); auto message = *reinterpret_cast<const TXProgressMessage*>(p);
this->on_tx_progress(message.progress); on_tx_progress(message.progress);
}}; }};
}; };
+3 -20
View File
@@ -23,7 +23,6 @@
#include "ui_pocsag_tx.hpp" #include "ui_pocsag_tx.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "ui_textentry.hpp" #include "ui_textentry.hpp"
@@ -39,13 +38,8 @@ void POCSAGTXView::focus() {
} }
POCSAGTXView::~POCSAGTXView() { POCSAGTXView::~POCSAGTXView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_pocsag", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit baseband::shutdown();
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) { void POCSAGTXView::on_tx_progress(const uint32_t progress, const bool done) {
@@ -85,11 +79,9 @@ bool POCSAGTXView::start_tx() {
progressbar.set_max(total_frames); progressbar.set_max(total_frames);
transmitter_model.set_sampling_rate(2280000);
transmitter_model.set_rf_amp(true); transmitter_model.set_rf_amp(true);
transmitter_model.set_lna(40); transmitter_model.set_lna(40);
transmitter_model.set_vga(40); transmitter_model.set_vga(40);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
uint8_t* data_ptr = shared_memory.bb_data.data; uint8_t* data_ptr = shared_memory.bb_data.data;
@@ -143,15 +135,6 @@ POCSAGTXView::POCSAGTXView(
&progressbar, &progressbar,
&tx_view}); &tx_view});
// load app settings
auto rc = settings.load("tx_pocsag", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
options_bitrate.set_selected_index(1); // 1200bps options_bitrate.set_selected_index(1); // 1200bps
options_type.set_selected_index(0); // Address only options_type.set_selected_index(0); // Address only
@@ -172,9 +155,9 @@ POCSAGTXView::POCSAGTXView(
}; };
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
transmitter_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+8 -4
View File
@@ -32,6 +32,7 @@
#include "message.hpp" #include "message.hpp"
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "pocsag.hpp" #include "pocsag.hpp"
using namespace pocsag; using namespace pocsag;
@@ -58,6 +59,13 @@ class POCSAGTXView : public View {
std::string message{}; std::string message{};
NavigationView& nav_; NavigationView& nav_;
TxRadioState radio_state_{
1750000 /* bandwidth */,
2280000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_pocsag", app_settings::Mode::TX};
BCHCode BCH_code{ BCHCode BCH_code{
{1, 0, 1, 0, 0, 1}, {1, 0, 1, 0, 0, 1},
5, 5,
@@ -65,10 +73,6 @@ class POCSAGTXView : public View {
21, 21,
2}; 2};
// app save settings
std::app_settings settings{};
std::app_settings::AppSettings app_settings{};
void on_set_text(NavigationView& nav); void on_set_text(NavigationView& nav);
void on_tx_progress(const uint32_t progress, const bool done); void on_tx_progress(const uint32_t progress, const bool done);
bool start_tx(); bool start_tx();
+3 -19
View File
@@ -24,7 +24,6 @@
#include "portapack.hpp" #include "portapack.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "portapack_shared_memory.hpp" #include "portapack_shared_memory.hpp"
#include <cstring> #include <cstring>
@@ -160,13 +159,8 @@ void RDSView::focus() {
} }
RDSView::~RDSView() { RDSView::~RDSView() {
// save app settings
app_settings.tx_frequency = transmitter_model.tuning_frequency();
settings.save("tx_rds", &app_settings);
transmitter_model.disable(); transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit. baseband::shutdown();
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
} }
void RDSView::start_tx() { void RDSView::start_tx() {
@@ -193,8 +187,6 @@ void RDSView::start_tx() {
else else
frame_datetime.clear(); frame_datetime.clear();
transmitter_model.set_sampling_rate(2280000U);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
tx_thread = std::make_unique<RDSThread>(frames); tx_thread = std::make_unique<RDSThread>(frames);
@@ -218,14 +210,6 @@ RDSView::RDSView(
&tx_view, &tx_view,
}); });
// load app settings
auto rc = settings.load("tx_rds", &app_settings);
if (rc == SETTINGS_OK) {
transmitter_model.set_rf_amp(app_settings.tx_amp);
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
transmitter_model.set_tx_gain(app_settings.tx_gain);
}
check_TP.set_value(true); check_TP.set_value(true);
sym_pi_code.set_sym(0, 0xF); sym_pi_code.set_sym(0, 0xF);
@@ -239,9 +223,9 @@ RDSView::RDSView(
options_pty.set_selected_index(0); // None options_pty.set_selected_index(0); // None
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
receiver_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+7 -3
View File
@@ -25,6 +25,7 @@
#include "ui_textentry.hpp" #include "ui_textentry.hpp"
#include "ui_tabview.hpp" #include "ui_tabview.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "rds.hpp" #include "rds.hpp"
using namespace rds; using namespace rds;
@@ -140,9 +141,12 @@ class RDSView : public View {
NavigationView& nav_; NavigationView& nav_;
RDS_flags rds_flags{}; RDS_flags rds_flags{};
// app save settings TxRadioState radio_state_{
std::app_settings settings{}; 1750000 /* bandwidth */,
std::app_settings::AppSettings app_settings{}; 2280000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_rds", app_settings::Mode::TX};
std::vector<RDSGroup> frame_psn{}; std::vector<RDSGroup> frame_psn{};
std::vector<RDSGroup> frame_radiotext{}; std::vector<RDSGroup> frame_radiotext{};
+212 -145
View File
@@ -30,18 +30,33 @@ using portapack::memory::map::backup_ram;
namespace ui { namespace ui {
void ReconView::set_loop_config(bool v) {
continuous = v;
button_loop_config.set_style(v ? &Styles::green : &Styles::white);
persistent_memory::set_recon_continuous(continuous);
}
void ReconView::recon_stop_recording() {
if (is_recording) {
button_audio_app.set_style(&Styles::white);
record_view->stop();
button_config.set_style(&Styles::white); // disable config while recording as it's causing an IO error pop up at exit
is_recording = false;
}
}
void ReconView::clear_freqlist_for_ui_action() { void ReconView::clear_freqlist_for_ui_action() {
audio::output::stop(); recon_stop_recording();
if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::stop();
// flag to detect and reload frequency_list // flag to detect and reload frequency_list
freqlist_cleared_for_ui_action = true;
// if in manual mode, there is enough memory to load freqman files, else we have to unload/reload
if (!manual_mode) { if (!manual_mode) {
// clear and shrink_to_fit are not enough to really start with a new, clean, empty vector // clear and shrink_to_fit are not enough to really start with a new, clean, empty vector
// swap is the only way to achieve a perfect memory liberation // swap is the only way to achieve a perfect memory liberation
std::vector<freqman_entry>().swap(frequency_list); std::vector<freqman_entry>().swap(frequency_list);
} else { } else
frequency_list.shrink_to_fit(); frequency_list.shrink_to_fit();
} freqlist_cleared_for_ui_action = true;
} }
void ReconView::reset_indexes() { void ReconView::reset_indexes() {
@@ -93,9 +108,9 @@ bool ReconView::recon_save_freq(const std::string& freq_file_path, size_t freq_i
auto result = recon_file.open(freq_file_path); // First recon if freq is already in txt auto result = recon_file.open(freq_file_path); // First recon if freq is already in txt
if (!result.is_valid()) { if (!result.is_valid()) {
char one_char[1]; // Read it char by char char one_char[1]{}; // Read it char by char
std::string line; // and put read line in here std::string line{}; // and put read line in here
bool found = false; bool found{false};
for (size_t pointer = 0; pointer < recon_file.size(); pointer++) { for (size_t pointer = 0; pointer < recon_file.size(); pointer++) {
recon_file.seek(pointer); recon_file.seek(pointer);
recon_file.read(one_char, 1); recon_file.read(one_char, 1);
@@ -128,16 +143,16 @@ bool ReconView::recon_save_freq(const std::string& freq_file_path, size_t freq_i
} }
bool ReconView::recon_load_config_from_sd() { bool ReconView::recon_load_config_from_sd() {
File settings_file; File settings_file{};
size_t length, file_position = 0; size_t length{0};
char* pos; size_t file_position{0};
char* line_start; char* pos{NULL};
char* line_end; char* line_start{NULL};
char file_data[257]; char* line_end{NULL};
char file_data[257]{};
uint32_t it = 0; uint32_t it{0};
uint32_t nb_params = RECON_SETTINGS_NB_PARAMS; uint32_t nb_params{RECON_SETTINGS_NB_PARAMS};
std::string params[RECON_SETTINGS_NB_PARAMS]; std::string params[RECON_SETTINGS_NB_PARAMS]{};
make_new_directory(u"SETTINGS"); make_new_directory(u"SETTINGS");
@@ -228,8 +243,8 @@ bool ReconView::recon_save_config_to_sd() {
make_new_directory(u"SETTINGS"); make_new_directory(u"SETTINGS");
auto result = settings_file.create(RECON_CFG_FILE); auto error = settings_file.create(RECON_CFG_FILE);
if (result.is_valid()) if (error)
return false; return false;
settings_file.write_line(input_file); settings_file.write_line(input_file);
settings_file.write_line(output_file); settings_file.write_line(output_file);
@@ -242,7 +257,8 @@ bool ReconView::recon_save_config_to_sd() {
} }
void ReconView::audio_output_start() { void ReconView::audio_output_start() {
audio::output::start(); if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::start();
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack. receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
} }
@@ -275,10 +291,6 @@ void ReconView::recon_redraw() {
} else if (freq_lock >= recon_lock_nb_match) { } else if (freq_lock >= recon_lock_nb_match) {
big_display.set_style(&Styles::green); big_display.set_style(&Styles::green);
button_pause.set_text("<UNLOCK>"); button_pause.set_text("<UNLOCK>");
// FREQ IS STRONG: GREEN and recon will pause when on_statistics_update()
if ((!scanner_mode) && autosave && frequency_list.size() > 0) {
recon_save_freq(freq_file_path, current_index, false);
}
} }
} }
if (last_db != db || last_list_size != frequency_list.size()) { if (last_db != db || last_list_size != frequency_list.size()) {
@@ -291,29 +303,18 @@ void ReconView::recon_redraw() {
void ReconView::handle_retune() { void ReconView::handle_retune() {
if (last_freq != freq) { if (last_freq != freq) {
last_freq = freq; last_freq = freq;
receiver_model.set_tuning_frequency(freq); // Retune receiver_model.set_target_frequency(freq); // Retune
} }
if (frequency_list.size() > 0) { if (frequency_list.size() > 0) {
if (last_entry.modulation != frequency_list[current_index].modulation && frequency_list[current_index].modulation >= 0) { if (last_entry.modulation != frequency_list[current_index].modulation && frequency_list[current_index].modulation >= 0) {
last_entry.modulation = frequency_list[current_index].modulation; last_entry.modulation = frequency_list[current_index].modulation;
field_mode.set_selected_index(frequency_list[current_index].modulation); field_mode.set_selected_index(frequency_list[current_index].modulation);
last_entry.bandwidth = -1;
} }
// Set bandwidth if any // Set bandwidth if any
if (last_entry.bandwidth != frequency_list[current_index].bandwidth && frequency_list[current_index].bandwidth >= 0) { if (last_entry.bandwidth != frequency_list[current_index].bandwidth && frequency_list[current_index].bandwidth >= 0) {
last_entry.bandwidth = frequency_list[current_index].bandwidth; last_entry.bandwidth = frequency_list[current_index].bandwidth;
switch (frequency_list[current_index].modulation) { field_bw.set_selected_index(frequency_list[current_index].bandwidth);
case AM_MODULATION:
receiver_model.set_am_configuration(freq);
break;
case NFM_MODULATION:
receiver_model.set_nbfm_configuration(freq);
break;
case WFM_MODULATION:
receiver_model.set_wfm_configuration(freq);
default:
break;
}
field_bw.set_selected_index(freq);
} }
if (last_entry.step != frequency_list[current_index].step && frequency_list[current_index].step >= 0) { if (last_entry.step != frequency_list[current_index].step && frequency_list[current_index].step >= 0) {
last_entry.step = frequency_list[current_index].step; last_entry.step = frequency_list[current_index].step;
@@ -361,18 +362,21 @@ void ReconView::focus() {
} }
ReconView::~ReconView() { ReconView::~ReconView() {
// save app config recon_stop_recording();
recon_save_config_to_sd(); recon_save_config_to_sd();
// save app common settings if (field_mode.selected_index_value() != SPEC_MODULATION)
settings.save("recon", &app_settings); audio::output::stop();
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
audio::output::stop();
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }
ReconView::ReconView(NavigationView& nav) ReconView::ReconView(NavigationView& nav)
: nav_{nav} { : nav_{nav} {
chrono_start = chTimeNow();
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO_", u"AUDIO",
RecordView::FileType::WAV, 4096, 4);
add_children({&labels, add_children({&labels,
&field_lna, &field_lna,
&field_vga, &field_vga,
@@ -380,11 +384,12 @@ ReconView::ReconView(NavigationView& nav)
&field_volume, &field_volume,
&field_bw, &field_bw,
&field_squelch, &field_squelch,
&field_nblocks,
&field_wait, &field_wait,
&field_lock_wait, &field_lock_wait,
&button_config, &button_config,
&button_scanner_mode, &button_scanner_mode,
&button_looking_glass, &button_loop_config,
&file_name, &file_name,
&rssi, &rssi,
&text_cycle, &text_cycle,
@@ -399,6 +404,7 @@ ReconView::ReconView(NavigationView& nav)
&button_manual_end, &button_manual_end,
&button_manual_recon, &button_manual_recon,
&field_mode, &field_mode,
&field_recon_match_mode,
&step_mode, &step_mode,
&button_pause, &button_pause,
&button_audio_app, &button_audio_app,
@@ -406,12 +412,18 @@ ReconView::ReconView(NavigationView& nav)
&button_dir, &button_dir,
&button_restart, &button_restart,
&button_mic_app, &button_mic_app,
&button_remove}); &button_remove,
record_view.get()});
record_view->hidden(true);
record_view->set_filename_date_frequency(true);
record_view->on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
def_step = 0; def_step = 0;
// HELPER: Pre-setting a manual range, based on stored frequency // HELPER: Pre-setting a manual range, based on stored frequency
rf::Frequency stored_freq = persistent_memory::tuned_frequency(); rf::Frequency stored_freq = receiver_model.target_frequency();
receiver_model.set_tuning_frequency(stored_freq);
if (stored_freq - OneMHz > 0) if (stored_freq - OneMHz > 0)
frequency_range.min = stored_freq - OneMHz; frequency_range.min = stored_freq - OneMHz;
else else
@@ -422,6 +434,7 @@ ReconView::ReconView(NavigationView& nav)
else else
frequency_range.max = MAX_UFREQ; frequency_range.max = MAX_UFREQ;
button_manual_end.set_text(to_string_short_freq(frequency_range.max)); button_manual_end.set_text(to_string_short_freq(frequency_range.max));
// Loading settings // Loading settings
autostart = persistent_memory::recon_autostart_recon(); autostart = persistent_memory::recon_autostart_recon();
autosave = persistent_memory::recon_autosave_freqs(); autosave = persistent_memory::recon_autosave_freqs();
@@ -431,16 +444,7 @@ ReconView::ReconView(NavigationView& nav)
load_ranges = persistent_memory::recon_load_ranges(); load_ranges = persistent_memory::recon_load_ranges();
load_hamradios = persistent_memory::recon_load_hamradios(); load_hamradios = persistent_memory::recon_load_hamradios();
update_ranges = persistent_memory::recon_update_ranges_when_recon(); update_ranges = persistent_memory::recon_update_ranges_when_recon();
if (sd_card_mounted) { auto_record_locked = persistent_memory::recon_auto_record_locked();
// load auto common app settings
auto rc = settings.load("recon", &app_settings);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
receiver_model.set_rf_amp(app_settings.rx_amp);
}
}
button_manual_start.on_select = [this, &nav](ButtonWithEncoder& button) { button_manual_start.on_select = [this, &nav](ButtonWithEncoder& button) {
clear_freqlist_for_ui_action(); clear_freqlist_for_ui_action();
@@ -530,8 +534,10 @@ ReconView::ReconView(NavigationView& nav)
} else { } else {
if (!recon) { if (!recon) {
recon_resume(); recon_resume();
user_pause = false;
} else { } else {
recon_pause(); recon_pause();
user_pause = true;
} }
} }
} }
@@ -547,10 +553,10 @@ ReconView::ReconView(NavigationView& nav)
nav_.push<AnalogAudioView>(); nav_.push<AnalogAudioView>();
}; };
button_looking_glass.on_select = [this](Button&) { button_loop_config.on_select = [this](Button&) {
nav_.pop(); set_loop_config(!continuous);
nav_.push<GlassView>();
}; };
set_loop_config(continuous);
rssi.set_focusable(true); rssi.set_focusable(true);
rssi.set_peak(true, 500); rssi.set_peak(true, 500);
@@ -559,17 +565,18 @@ ReconView::ReconView(NavigationView& nav)
nav_.push<LevelView>(); nav_.push<LevelView>();
}; };
// TODO: *BUG* Both transmitter_model and receiver_model share the same pmem setting for target_frequency.
button_mic_app.on_select = [this](Button&) { button_mic_app.on_select = [this](Button&) {
if (frequency_list.size() > 0 && current_index >= 0 && (unsigned)current_index < frequency_list.size()) { if (frequency_list.size() > 0 && current_index >= 0 && (unsigned)current_index < frequency_list.size()) {
if (frequency_list[current_index].type == HAMRADIO) { if (frequency_list[current_index].type == HAMRADIO) {
// if it's a HAMRADIO entry, then frequency_a is the freq at which the repeater reveive, so we have to set it in transmit in mic app // if it's a HAMRADIO entry, then frequency_a is the freq at which the repeater reveive, so we have to set it in transmit in mic app
transmitter_model.set_tuning_frequency(frequency_list[current_index].frequency_a); transmitter_model.set_target_frequency(frequency_list[current_index].frequency_a);
// if it's a HAMRADIO entry, then frequency_b is the freq at which the repeater transmit, so we have to set it in receive in mic app // if it's a HAMRADIO entry, then frequency_b is the freq at which the repeater transmit, so we have to set it in receive in mic app
receiver_model.set_tuning_frequency(frequency_list[current_index].frequency_b); receiver_model.set_target_frequency(frequency_list[current_index].frequency_b);
} else { } else {
// it's single or range so we us actual tuned frequency // it's single or range so we us actual tuned frequency
transmitter_model.set_tuning_frequency(freq); transmitter_model.set_target_frequency(freq);
receiver_model.set_tuning_frequency(freq); receiver_model.set_target_frequency(freq);
} }
} }
// there is no way yet to set modulation and bandwidth from Recon to MicApp // there is no way yet to set modulation and bandwidth from Recon to MicApp
@@ -610,7 +617,7 @@ ReconView::ReconView(NavigationView& nav)
} }
// also remove from output file if in scanner mode // also remove from output file if in scanner mode
if (scanner_mode) { if (scanner_mode) {
File freqman_file; File freqman_file{};
delete_file(freq_file_path); delete_file(freq_file_path);
auto result = freqman_file.create(freq_file_path); auto result = freqman_file.create(freq_file_path);
if (!result.is_valid()) { if (!result.is_valid()) {
@@ -623,10 +630,10 @@ ReconView::ReconView(NavigationView& nav)
} }
} else if (manual_mode) // only remove from output } else if (manual_mode) // only remove from output
{ {
File recon_file; File recon_file{};
File tmp_recon_file; File tmp_recon_file{};
std::string tmp_freq_file_path = freq_file_path + ".TMP"; std::string tmp_freq_file_path{freq_file_path + ".TMP"};
std::string frequency_to_add; std::string frequency_to_add{};
freqman_entry entry = frequency_list[current_index]; freqman_entry entry = frequency_list[current_index];
entry.frequency_a = freq; entry.frequency_a = freq;
@@ -643,8 +650,8 @@ ReconView::ReconView(NavigationView& nav)
bool found = false; bool found = false;
result = recon_file.open(freq_file_path); // First recon if freq is already in txt result = recon_file.open(freq_file_path); // First recon if freq is already in txt
if (!result.is_valid()) { if (!result.is_valid()) {
char one_char[1]; // Read it char by char char one_char[1]{}; // Read it char by char
std::string line; // and put read line in here std::string line{}; // and put read line in here
for (size_t pointer = 0; pointer < recon_file.size(); pointer++) { for (size_t pointer = 0; pointer < recon_file.size(); pointer++) {
recon_file.seek(pointer); recon_file.seek(pointer);
recon_file.read(one_char, 1); recon_file.read(one_char, 1);
@@ -668,7 +675,7 @@ ReconView::ReconView(NavigationView& nav)
} }
} }
} }
receiver_model.set_tuning_frequency(frequency_list[current_index].frequency_a); // retune receiver_model.set_target_frequency(frequency_list[current_index].frequency_a); // retune
} }
if (frequency_list.size() == 0) { if (frequency_list.size() == 0) {
text_cycle.set_text(" "); text_cycle.set_text(" ");
@@ -694,8 +701,8 @@ ReconView::ReconView(NavigationView& nav)
} else if (frequency_range.min > frequency_range.max) { } else if (frequency_range.min > frequency_range.max) {
nav_.display_modal("Error", "END freq\nis lower than START"); nav_.display_modal("Error", "END freq\nis lower than START");
} else { } else {
audio::output::stop(); if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::stop();
// clear and shrink_to_fit are not enough to really start with a new, clean, empty vector // clear and shrink_to_fit are not enough to really start with a new, clean, empty vector
// swap is the only way to achieve a perfect memory liberation // swap is the only way to achieve a perfect memory liberation
std::vector<freqman_entry>().swap(frequency_list); std::vector<freqman_entry>().swap(frequency_list);
@@ -775,6 +782,9 @@ ReconView::ReconView(NavigationView& nav)
button_scanner_mode.set_style(&Styles::blue); button_scanner_mode.set_style(&Styles::blue);
button_scanner_mode.set_text("RECON"); button_scanner_mode.set_text("RECON");
} }
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
file_name.set_style(&Styles::yellow);
}
}; };
button_add.on_select = [this](ButtonWithEncoder&) { // frequency_list[current_index] button_add.on_select = [this](ButtonWithEncoder&) { // frequency_list[current_index]
@@ -810,33 +820,29 @@ ReconView::ReconView(NavigationView& nav)
}; };
button_config.on_select = [this, &nav](Button&) { button_config.on_select = [this, &nav](Button&) {
if (is_recording) // disabling config while recording
return;
clear_freqlist_for_ui_action(); clear_freqlist_for_ui_action();
freq_lock = 0;
auto open_view = nav.push<ReconSetupView>(input_file, output_file, recon_lock_duration, recon_lock_nb_match, recon_match_mode); timer = 0;
auto open_view = nav.push<ReconSetupView>(input_file, output_file);
open_view->on_changed = [this](std::vector<std::string> result) { open_view->on_changed = [this](std::vector<std::string> result) {
freqlist_cleared_for_ui_action = false;
input_file = result[0]; input_file = result[0];
output_file = result[1]; output_file = result[1];
freq_file_path = "/FREQMAN/" + output_file + ".TXT"; freq_file_path = "/FREQMAN/" + output_file + ".TXT";
recon_lock_duration = strtol(result[2].c_str(), nullptr, 10);
recon_lock_nb_match = strtol(result[3].c_str(), nullptr, 10);
recon_match_mode = strtol(result[4].c_str(), nullptr, 10);
recon_save_config_to_sd(); recon_save_config_to_sd();
autosave = persistent_memory::recon_autosave_freqs(); autosave = persistent_memory::recon_autosave_freqs();
autostart = persistent_memory::recon_autostart_recon(); autostart = persistent_memory::recon_autostart_recon();
continuous = persistent_memory::recon_continuous();
filedelete = persistent_memory::recon_clear_output(); filedelete = persistent_memory::recon_clear_output();
load_freqs = persistent_memory::recon_load_freqs(); load_freqs = persistent_memory::recon_load_freqs();
load_ranges = persistent_memory::recon_load_ranges(); load_ranges = persistent_memory::recon_load_ranges();
load_hamradios = persistent_memory::recon_load_hamradios(); load_hamradios = persistent_memory::recon_load_hamradios();
update_ranges = persistent_memory::recon_update_ranges_when_recon(); update_ranges = persistent_memory::recon_update_ranges_when_recon();
auto_record_locked = persistent_memory::recon_auto_record_locked();
field_wait.set_value(wait);
field_lock_wait.set_value(recon_lock_duration);
colorize_waits();
frequency_file_load(false); frequency_file_load(false);
freqlist_cleared_for_ui_action = false;
if (autostart) { if (autostart) {
recon_resume(); recon_resume();
@@ -846,8 +852,23 @@ ReconView::ReconView(NavigationView& nav)
}; };
}; };
field_recon_match_mode.on_change = [this](size_t, OptionsField::value_t v) {
recon_match_mode = v;
colorize_waits();
};
field_wait.on_change = [this](int32_t v) { field_wait.on_change = [this](int32_t v) {
wait = v; wait = v;
// replacing -100 by 200 else it's freezing the device
if (wait == -100)
wait = -200;
colorize_waits();
};
field_nblocks.on_change = [this](int32_t v) {
recon_lock_nb_match = v;
if ((unsigned)v < freq_lock)
freq_lock = v;
colorize_waits(); colorize_waits();
}; };
@@ -869,9 +890,11 @@ ReconView::ReconView(NavigationView& nav)
recon_load_config_from_sd(); recon_load_config_from_sd();
freq_file_path = "/FREQMAN/" + output_file + ".TXT"; freq_file_path = "/FREQMAN/" + output_file + ".TXT";
field_recon_match_mode.set_selected_index(recon_match_mode);
field_squelch.set_value(squelch); field_squelch.set_value(squelch);
field_wait.set_value(wait); field_wait.set_value(wait);
field_lock_wait.set_value(recon_lock_duration); field_lock_wait.set_value(recon_lock_duration);
field_nblocks.set_value(recon_lock_nb_match);
colorize_waits(); colorize_waits();
// fill modulation and step options // fill modulation and step options
@@ -879,9 +902,8 @@ ReconView::ReconView(NavigationView& nav)
freqman_set_step_option(step_mode); freqman_set_step_option(step_mode);
// set radio // set radio
change_mode(AM_MODULATION); // start on AM. change_mode(AM_MODULATION); // start on AM.
field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector
receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); // first tune
if (filedelete) { if (filedelete) {
delete_file(freq_file_path); delete_file(freq_file_path);
@@ -898,40 +920,36 @@ ReconView::ReconView(NavigationView& nav)
void ReconView::frequency_file_load(bool stop_all_before) { void ReconView::frequency_file_load(bool stop_all_before) {
(void)(stop_all_before); (void)(stop_all_before);
audio::output::stop(); if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::stop();
def_step = step_mode.selected_index(); // use def_step from manual selector def_step = step_mode.selected_index(); // use def_step from manual selector
// clear and shrink_to_fit are not enough to really start with a new, clean, empty vector std::string file_input = input_file; // default recon mode
// swap is the only way to achieve a perfect memory liberation
std::vector<freqman_entry>().swap(frequency_list); // clear the existing frequency list (expected behavior)
std::string file_input = input_file; // default recon mode
if (scanner_mode) { if (scanner_mode) {
file_input = output_file; file_input = output_file;
file_name.set_style(&Styles::red); file_name.set_style(&Styles::red);
button_scanner_mode.set_style(&Styles::red); button_scanner_mode.set_style(&Styles::red);
desc_cycle.set_style(&Styles::red);
button_scanner_mode.set_text("SCANNER"); button_scanner_mode.set_text("SCANNER");
} else { } else {
file_name.set_style(&Styles::blue); file_name.set_style(&Styles::blue);
button_scanner_mode.set_style(&Styles::blue); button_scanner_mode.set_style(&Styles::blue);
desc_cycle.set_style(&Styles::blue);
button_scanner_mode.set_text("RECON"); button_scanner_mode.set_text("RECON");
} }
desc_cycle.set_style(&Styles::white); if (!load_freqman_file(file_input, frequency_list, load_freqs, load_ranges, load_hamradios)) {
if (!load_freqman_file_ex(file_input, frequency_list, load_freqs, load_ranges, load_hamradios, RECON_FREQMAN_MAX_PER_FILE)) {
file_name.set_style(&Styles::red); file_name.set_style(&Styles::red);
desc_cycle.set_style(&Styles::red);
desc_cycle.set(" NO " + file_input + ".TXT FILE ..."); desc_cycle.set(" NO " + file_input + ".TXT FILE ...");
file_name.set("=> NO DATA"); file_name.set("=> NO DATA");
} else { } else {
file_name.set("=> " + file_input); file_name.set(file_input + "=>" + output_file);
if (frequency_list.size() == 0) { if (frequency_list.size() == 0) {
file_name.set_style(&Styles::red); file_name.set_style(&Styles::red);
desc_cycle.set_style(&Styles::red);
desc_cycle.set("/0 no entries in list"); desc_cycle.set("/0 no entries in list");
file_name.set("BadOrEmpty " + file_input); file_name.set("BadOrEmpty " + file_input);
} else { } else {
if (frequency_list.size() > RECON_FREQMAN_MAX_PER_FILE) { if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
file_name.set_style(&Styles::yellow); file_name.set_style(&Styles::yellow);
desc_cycle.set_style(&Styles::yellow);
} }
} }
} }
@@ -1005,12 +1023,20 @@ void ReconView::frequency_file_load(bool stop_all_before) {
} }
void ReconView::on_statistics_update(const ChannelStatistics& statistics) { void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
systime_t time_interval = 100;
if (field_mode.selected_index_value() == SPEC_MODULATION) {
chrono_end = chTimeNow();
time_interval = chrono_end - chrono_start;
chrono_start = chrono_end;
}
// hack to reload the list if it was cleared by going into CONFIG // hack to reload the list if it was cleared by going into CONFIG
if (freqlist_cleared_for_ui_action) { if (freqlist_cleared_for_ui_action) {
if (!manual_mode) { if (!manual_mode) {
frequency_file_load(false); frequency_file_load(false);
} }
if (autostart) { if (autostart && !user_pause) {
recon_resume(); recon_resume();
} else { } else {
recon_pause(); recon_pause();
@@ -1031,7 +1057,9 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (status != 1) { if (status != 1) {
status = 1; status = 1;
if (wait != 0) { if (wait != 0) {
audio::output::stop(); recon_stop_recording();
if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::stop();
} }
} }
if (db > squelch) // MATCHING LEVEL if (db > squelch) // MATCHING LEVEL
@@ -1053,8 +1081,24 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (status != 2) { if (status != 2) {
continuous_lock = false; continuous_lock = false;
status = 2; status = 2;
// FREQ IS STRONG: GREEN and recon will pause when on_statistics_update()
if ((!scanner_mode) && autosave && frequency_list.size() > 0) {
recon_save_freq(freq_file_path, current_index, false);
}
if (wait != 0) { if (wait != 0) {
audio_output_start(); if (field_mode.selected_index_value() != SPEC_MODULATION)
audio_output_start();
// contents of a possible recon_start_recording(), but not yet since it's only called once
if (auto_record_locked && !is_recording) {
button_audio_app.set_style(&Styles::red);
record_view->start();
button_config.set_style(&Styles::light_grey); // disable config while recording as it's causing an IO error pop up at exit
is_recording = true;
}
// FREQ IS STRONG: GREEN and recon will pause when on_statistics_update()
if ((!scanner_mode) && autosave && frequency_list.size() > 0) {
recon_save_freq(freq_file_path, current_index, false);
}
} }
if (wait >= 0) { if (wait >= 0) {
timer = wait; timer = wait;
@@ -1073,8 +1117,9 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
text_timer.set("TIMER: " + to_string_dec_int(timer)); text_timer.set("TIMER: " + to_string_dec_int(timer));
} }
if (timer) { if (timer) {
if (!continuous_lock) if (!continuous_lock || recon_match_mode == RECON_MATCH_SPARSE) {
timer -= STATS_UPDATE_INTERVAL; timer -= time_interval;
}
if (timer < 0) { if (timer < 0) {
timer = 0; timer = 0;
} }
@@ -1085,7 +1130,6 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
if (frequency_list.size() > 0) { if (frequency_list.size() > 0) {
has_looped = false; has_looped = false;
entry_has_changed = false; entry_has_changed = false;
if (recon || stepper != 0 || index_stepper != 0) { if (recon || stepper != 0 || index_stepper != 0) {
if (index_stepper == 0) { if (index_stepper == 0) {
/* we are doing a range */ /* we are doing a range */
@@ -1185,7 +1229,8 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
entry_has_changed = true; entry_has_changed = true;
if (!recon) // for some motive, audio output gets stopped. // for some motive, audio output gets stopped.
if (!recon && field_mode.selected_index_value() != SPEC_MODULATION)
audio_output_start(); audio_output_start();
} }
// reload entry if changed // reload entry if changed
@@ -1237,7 +1282,8 @@ void ReconView::recon_pause() {
continuous_lock = false; continuous_lock = false;
recon = false; recon = false;
audio_output_start(); if (field_mode.selected_index_value() != SPEC_MODULATION)
audio_output_start();
big_display.set_style(&Styles::white); big_display.set_style(&Styles::white);
button_pause.set_text("<RESUME>"); // PAUSED, show resume button_pause.set_text("<RESUME>"); // PAUSED, show resume
@@ -1249,7 +1295,8 @@ void ReconView::recon_resume() {
continuous_lock = false; continuous_lock = false;
recon = true; recon = true;
audio::output::stop(); if (field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::stop();
big_display.set_style(&Styles::white); big_display.set_style(&Styles::white);
button_pause.set_text("<PAUSE>"); button_pause.set_text("<PAUSE>");
@@ -1290,10 +1337,28 @@ void ReconView::on_stepper_delta(int32_t v) {
size_t ReconView::change_mode(freqman_index_t new_mod) { size_t ReconView::change_mode(freqman_index_t new_mod) {
field_mode.on_change = [this](size_t, OptionsField::value_t) {}; field_mode.on_change = [this](size_t, OptionsField::value_t) {};
field_bw.on_change = [this](size_t, OptionsField::value_t) {}; field_bw.on_change = [this](size_t, OptionsField::value_t) {};
recon_stop_recording();
remove_child(record_view.get());
record_view.reset();
if (new_mod == SPEC_MODULATION) {
audio::output::stop();
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_RAW_", u"CAPTURES",
RecordView::FileType::RawS16, 16384, 3);
} else {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO_", u"AUDIO",
RecordView::FileType::WAV, 4096, 4);
}
add_child(record_view.get());
record_view->hidden(true);
record_view->set_filename_date_frequency(true);
record_view->on_error = [this](std::string message) {
nav_.display_modal("Error", message);
};
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
size_t recording_sampling_rate = 0;
switch (new_mod) { switch (new_mod) {
case AM_MODULATION: case AM_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw); freqman_set_bandwidth_option(new_mod, field_bw);
@@ -1303,9 +1368,8 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
receiver_model.set_am_configuration(field_bw.selected_index_value()); receiver_model.set_am_configuration(field_bw.selected_index_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_am_configuration(n); }; field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_am_configuration(n); };
receiver_model.set_sampling_rate(3072000); text_ctcss.set(" ");
receiver_model.set_baseband_bandwidth(1750000); recording_sampling_rate = 12000;
text_ctcss.set(" ");
break; break;
case NFM_MODULATION: case NFM_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw); freqman_set_bandwidth_option(new_mod, field_bw);
@@ -1315,8 +1379,7 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.set_nbfm_configuration(field_bw.selected_index_value()); receiver_model.set_nbfm_configuration(field_bw.selected_index_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_nbfm_configuration(n); }; field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_nbfm_configuration(n); };
receiver_model.set_sampling_rate(3072000); recording_sampling_rate = 24000;
receiver_model.set_baseband_bandwidth(1750000);
break; break;
case WFM_MODULATION: case WFM_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw); freqman_set_bandwidth_option(new_mod, field_bw);
@@ -1326,21 +1389,36 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.set_wfm_configuration(field_bw.selected_index_value()); receiver_model.set_wfm_configuration(field_bw.selected_index_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_wfm_configuration(n); }; field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_wfm_configuration(n); };
receiver_model.set_sampling_rate(3072000); text_ctcss.set(" ");
receiver_model.set_baseband_bandwidth(1750000); recording_sampling_rate = 48000;
text_ctcss.set(" "); break;
case SPEC_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw);
// bw 200k (0) default
baseband::run_image(portapack::spi_flash::image_tag_capture);
receiver_model.set_modulation(ReceiverModel::Mode::Capture);
field_bw.set_by_value(0);
field_bw.on_change = [this](size_t, OptionsField::value_t sampling_rate) {
auto anti_alias_baseband_bandwidth_filter = filter_bandwidth_for_sampling_rate(sampling_rate);
record_view->set_sampling_rate(sampling_rate);
receiver_model.set_sampling_rate(sampling_rate);
receiver_model.set_baseband_bandwidth(anti_alias_baseband_bandwidth_filter);
};
text_ctcss.set(" ");
break; break;
default: default:
break; break;
} }
if (new_mod != SPEC_MODULATION)
record_view->set_sampling_rate(recording_sampling_rate);
field_mode.set_selected_index(new_mod); field_mode.set_selected_index(new_mod);
field_mode.on_change = [this](size_t, OptionsField::value_t v) { field_mode.on_change = [this](size_t, OptionsField::value_t v) {
if (v != -1) { if (v != -1) {
change_mode(v); change_mode(v);
} }
}; };
// for some motive, audio output gets stopped.
if (!recon) // for some motive, audio output gets stopped. if (!recon && field_mode.selected_index_value() != SPEC_MODULATION)
audio::output::start(); // so if recon was stopped we resume audio audio::output::start(); // so if recon was stopped we resume audio
receiver_model.enable(); receiver_model.enable();
@@ -1348,31 +1426,20 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
} }
void ReconView::handle_coded_squelch(const uint32_t value) { void ReconView::handle_coded_squelch(const uint32_t value) {
float diff, min_diff = value; if (field_mode.selected_index() == NFM_MODULATION) {
size_t min_idx{0}; tone_index idx = tone_key_index_by_value(value);
size_t c;
if (field_mode.selected_index() != NFM_MODULATION) { if ((last_squelch_index < 0) || (last_squelch_index != idx)) {
text_ctcss.set(" "); last_squelch_index = idx;
return; if (idx >= 0) {
} text_ctcss.set("T: " + tone_key_string(idx));
return;
// Find nearest match }
for (c = 0; c < tone_keys.size(); c++) { } else {
diff = abs(((float)value / 100.0) - tone_keys[c].second); return;
if (diff < min_diff) {
min_idx = c;
min_diff = diff;
} }
} }
text_ctcss.set(" ");
// Arbitrary confidence threshold
if (last_squelch_index < 0 || (unsigned)last_squelch_index != min_idx) {
last_squelch_index = min_idx;
if (min_diff < 40)
text_ctcss.set("T: " + tone_keys[min_idx].first);
else
text_ctcss.set(" ");
}
} }
} /* namespace ui */ } /* namespace ui */
+133 -110
View File
@@ -39,12 +39,12 @@
#include "string_format.hpp" #include "string_format.hpp"
#include "file.hpp" #include "file.hpp"
#include "app_settings.hpp" #include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_recon_settings.hpp" #include "ui_recon_settings.hpp"
namespace ui { namespace ui {
#define RECON_CFG_FILE "SETTINGS/recon.cfg" #define RECON_CFG_FILE "SETTINGS/recon.cfg"
#define RECON_FREQMAN_MAX_PER_FILE 99 // maximum tested limit for load frequency from freqman to work
class ReconView : public View { class ReconView : public View {
public: public:
@@ -60,6 +60,11 @@ class ReconView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_recon", app_settings::Mode::RX};
void set_loop_config(bool v);
void clear_freqlist_for_ui_action(); void clear_freqlist_for_ui_action();
void reset_indexes(); void reset_indexes();
void audio_output_start(); void audio_output_start();
@@ -79,69 +84,75 @@ class ReconView : public View {
bool recon_load_config_from_sd(); bool recon_load_config_from_sd();
bool recon_save_config_to_sd(); bool recon_save_config_to_sd();
bool recon_save_freq(const std::string& freq_file_path, size_t index, bool warn_if_exists); bool recon_save_freq(const std::string& freq_file_path, size_t index, bool warn_if_exists);
// placeholder for possible void recon_start_recording();
void recon_stop_recording();
jammer::jammer_range_t frequency_range{false, 0, MAX_UFREQ}; // perfect for manual recon task too... jammer::jammer_range_t frequency_range{false, 0, MAX_UFREQ}; // perfect for manual recon task too...
int32_t squelch{0}; int32_t squelch{0};
int32_t db{0}; int32_t db{0};
int32_t timer{0}; int32_t timer{0};
int32_t wait{RECON_DEF_WAIT_DURATION}; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity int32_t wait{RECON_DEF_WAIT_DURATION}; // in msec. if > 0 wait duration after a lock, if < 0 duration is set to 'wait' unless there is no more activity
freqman_db frequency_list = {}; freqman_db frequency_list{};
int32_t current_index{0}; int32_t current_index{0};
bool continuous_lock{false}; bool continuous_lock{false};
bool freqlist_cleared_for_ui_action{false}; // flag positioned by ui widgets to manage freqlist unload/load bool freqlist_cleared_for_ui_action{false}; // flag positioned by ui widgets to manage freqlist unload/load
std::string input_file = {"RECON"}; std::string input_file{"RECON"};
std::string output_file = {"RECON_RESULTS"}; std::string output_file{"RECON_RESULTS"};
std::string description = {"...no description..."}; std::string description{"...no description..."};
bool autosave = {true}; bool autosave{true};
bool autostart = {true}; bool autostart{true};
bool continuous = {true}; bool continuous{true};
bool filedelete = {true}; bool filedelete{true};
bool load_freqs = {true}; bool load_freqs{true};
bool load_ranges = {true}; bool load_ranges{true};
bool load_hamradios = {true}; bool load_hamradios{true};
bool update_ranges = {true}; bool update_ranges{true};
bool fwd = {true}; bool fwd{true};
bool recon = true; bool recon{true};
uint32_t recon_lock_nb_match = {3}; bool user_pause{false};
uint32_t recon_lock_duration = {RECON_MIN_LOCK_DURATION}; bool auto_record_locked{false};
uint32_t recon_match_mode = {RECON_MATCH_CONTINUOUS}; bool is_recording{false};
uint32_t recon_lock_nb_match{3};
uint32_t recon_lock_duration{RECON_MIN_LOCK_DURATION};
uint32_t recon_match_mode{RECON_MATCH_CONTINUOUS};
bool scanner_mode{false}; bool scanner_mode{false};
bool manual_mode{false}; bool manual_mode{false};
bool sd_card_mounted = false; bool sd_card_mounted{false};
int32_t stepper = 0; int32_t stepper{0};
int32_t index_stepper = 0; int32_t index_stepper{0};
int64_t freq = 0; int64_t freq{0};
uint32_t step = 0; uint32_t step{0};
freqman_index_t def_modulation = 0; freqman_index_t def_step{0};
freqman_index_t def_bandwidth = 0; freqman_entry last_entry{};
freqman_index_t def_step = 0; bool entry_has_changed{false};
tone_index tone = 0;
freqman_entry last_entry = {};
bool entry_has_changed = false;
uint32_t freq_lock{0}; uint32_t freq_lock{0};
int64_t minfreq = 0; int64_t minfreq{0};
int64_t maxfreq = 0; int64_t maxfreq{0};
bool has_looped = false; bool has_looped{false};
int8_t status = -1; // 0 recon , 1 locking , 2 locked int8_t status{-1}; // 0 recon , 1 locking , 2 locked
int32_t last_timer = -1; int32_t last_timer{-1};
int8_t last_db = -127; int8_t last_db{-127};
uint16_t last_nb_match = 999; uint16_t last_nb_match{999};
uint16_t last_freq_lock = 999; uint16_t last_freq_lock{999};
size_t last_list_size = 0; size_t last_list_size{0};
int8_t last_rssi_min = -127; int8_t last_rssi_min{-127};
int8_t last_rssi_med = -127; int8_t last_rssi_med{-127};
int8_t last_rssi_max = -127; int8_t last_rssi_max{-127};
int32_t last_index = -1; int32_t last_index{-1};
int32_t last_squelch_index = -1; tone_index last_squelch_index{-1};
int64_t last_freq = 0; int64_t last_freq{0};
std::string freq_file_path = {}; std::string freq_file_path{};
systime_t chrono_start{};
systime_t chrono_end{};
std::unique_ptr<RecordView> record_view{};
Labels labels{ Labels labels{
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()}, {{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()},
{{0 * 8, 1 * 16}, "BW: SQ: W,L: , ", Color::light_grey()}, {{3 * 8, 8 * 16}, "START END", Color::light_grey()},
{{3 * 8, 10 * 16}, "START END MANUAL", Color::light_grey()}, {{0 * 8, (22 * 8)}, " S: ", Color::light_grey()},
{{0 * 8, (26 * 8) + 4}, "MODE:", Color::light_grey()}, {{0 * 8, (24 * 8) + 4}, "NBLCKS:x W,L: , ", Color::light_grey()},
{{11 * 8, (26 * 8) + 4}, "STEP:", Color::light_grey()}, {{0 * 8, (26 * 8) + 4}, "MODE: , SQUELCH: ", Color::light_grey()}};
};
LNAGainField field_lna{ LNAGainField field_lna{
{4 * 8, 0 * 16}}; {4 * 8, 0 * 16}};
@@ -155,65 +166,41 @@ class ReconView : public View {
AudioVolumeField field_volume{ AudioVolumeField field_volume{
{24 * 8, 0 * 16}}; {24 * 8, 0 * 16}};
OptionsField field_bw{ Text file_name{
{3 * 8, 1 * 16}, // show file used
6, {0, 1 * 16, SCREEN_W, 16},
{}};
NumberField field_squelch{
{12 * 8, 1 * 16},
3,
{-90, 20},
1,
' ',
}; };
NumberField field_wait{ Text desc_cycle{
{20 * 8, 1 * 16}, {0, 2 * 16, SCREEN_W, 16},
5,
{-RECON_MAX_LOCK_DURATION, RECON_MAX_LOCK_DURATION},
STATS_UPDATE_INTERVAL,
' ',
};
NumberField field_lock_wait{
{26 * 8, 1 * 16},
4,
{RECON_MIN_LOCK_DURATION, RECON_MAX_LOCK_DURATION},
STATS_UPDATE_INTERVAL,
' ',
}; };
RSSI rssi{ RSSI rssi{
{0 * 16, 2 * 16, SCREEN_W - 8 * 8 + 4, 14}, {0 * 16, 3 * 16 + 2, SCREEN_W - 8 * 8 + 4, 12},
}; };
ButtonWithEncoder text_cycle{ ButtonWithEncoder text_cycle{
{0, 3 * 16, 4 * 8, 16}, {0, 4 * 16, 4 * 8, 16},
""}; ""};
// "/XXX -XXX db" => 12 chars max // "/XXX -XXX db" => 12 chars max
Text text_max{ Text text_max{
{4 * 8, 3 * 16, 12 * 8, 16}, {4 * 8, 4 * 16, 12 * 8, 16},
}; };
// "XX/XX" => 5 chars max // "XX/XX" => 5 chars max
Text text_nb_locks{ Text text_nb_locks{
{16 * 8, 3 * 16, 5 * 8, 16}, {16 * 8, 4 * 16, 5 * 8, 16},
};
Text desc_cycle{
{0, 4 * 16, SCREEN_W, 16},
}; };
Text big_display{ Text big_display{
// Show frequency in text mode // Show frequency in text mode
{0, 5 * 16, 23 * 8, 16}, {0, 5 * 16, 21 * 8, 16},
}; };
Text freq_stats{ Text freq_stats{
// Show frequency stats in text mode // Show frequency stats in text mode
{0, 6 * 16, 23 * 8, 16}, {0, 6 * 16, 21 * 8, 16},
}; };
// TIMER: 9999 // TIMER: 9999
@@ -224,48 +211,87 @@ class ReconView : public View {
// T: Senn. 32.000k // T: Senn. 32.000k
Text text_ctcss{ Text text_ctcss{
{12 * 8 + 4, 7 * 16, 14 * 8, 1 * 8}, {14 * 8, 7 * 16, 8 * 8, 1 * 8},
""}; ""};
Button button_config{
{SCREEN_W - 7 * 8, 2 * 16, 7 * 8, 28},
"CONFIG"};
Button button_looking_glass{
{SCREEN_W - 7 * 8, 5 * 16, 7 * 8, 28},
"GLASS"};
// Button can be RECON or SCANNER // Button can be RECON or SCANNER
Button button_scanner_mode{ Button button_scanner_mode{
{SCREEN_W - 7 * 8, 8 * 16, 7 * 8, 28}, {SCREEN_W - 7 * 8, 3 * 16, 7 * 8, 28},
"RECON"}; "RECON"};
Text file_name{ Button button_loop_config{
// show file used {SCREEN_W - 7 * 8, 5 * 16, 7 * 8, 28},
{0, 8 * 16 + 6, SCREEN_W - 7 * 8, 16}, "[LOOP]"};
};
Button button_config{
{SCREEN_W - 7 * 8, 7 * 16, 7 * 8, 28},
"CONFIG"};
ButtonWithEncoder button_manual_start{ ButtonWithEncoder button_manual_start{
{0 * 8, 11 * 16, 11 * 8, 28}, {0 * 8, 9 * 16, 11 * 8, 28},
""}; ""};
ButtonWithEncoder button_manual_end{ ButtonWithEncoder button_manual_end{
{12 * 8 - 6, 11 * 16, 11 * 8, 28}, {12 * 8 - 6, 9 * 16, 11 * 8, 28},
""}; ""};
OptionsField field_recon_match_mode{
{0 * 8, 11 * 16},
16, // CONTINUOUS MATCH MODE / SPARSE TIMED MATCH MODE
{
{"MATCH:CONTINOUS", 0},
{"MATCH:SPARSE", 1}}};
OptionsField step_mode{
{18 * 8, 11 * 16},
12,
{}};
Button button_manual_recon{ Button button_manual_recon{
{23 * 8 - 3, 11 * 16, 7 * 8, 28}, {23 * 8, 9 * 16, 7 * 8, 28},
"SEARCH"}; "SEARCH"};
NumberField field_nblocks{
{8 * 8, 24 * 8 + 4},
2,
{1, 99},
1,
' ',
};
NumberField field_wait{
{19 * 8, 24 * 8 + 4},
5,
{-RECON_MAX_LOCK_DURATION, RECON_MAX_LOCK_DURATION},
STATS_UPDATE_INTERVAL,
' ',
};
NumberField field_lock_wait{
{25 * 8, 24 * 8 + 4},
4,
{RECON_MIN_LOCK_DURATION, RECON_MAX_LOCK_DURATION},
STATS_UPDATE_INTERVAL,
' ',
};
OptionsField field_mode{ OptionsField field_mode{
{5 * 8, (26 * 8) + 4}, {6 * 8, (26 * 8) + 4},
4,
{}};
OptionsField field_bw{
{11 * 8, (26 * 8) + 4},
6, 6,
{}}; {}};
OptionsField step_mode{ NumberField field_squelch{
{17 * 8, (26 * 8) + 4}, {26 * 8, (26 * 8) + 4},
12, 3,
{}}; {-90, 20},
1,
' ',
};
ButtonWithEncoder button_pause{ ButtonWithEncoder button_pause{
{0, (15 * 16) - 4, 72, 28}, {0, (15 * 16) - 4, 72, 28},
@@ -307,9 +333,6 @@ class ReconView : public View {
[this](const Message* const p) { [this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics); 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 */ } /* namespace ui */
+13 -35
View File
@@ -44,12 +44,10 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
&button_output_file, &button_output_file,
&checkbox_autosave_freqs, &checkbox_autosave_freqs,
&checkbox_autostart_recon, &checkbox_autostart_recon,
&checkbox_continuous,
&checkbox_clear_output}); &checkbox_clear_output});
checkbox_autosave_freqs.set_value(persistent_memory::recon_autosave_freqs()); checkbox_autosave_freqs.set_value(persistent_memory::recon_autosave_freqs());
checkbox_autostart_recon.set_value(persistent_memory::recon_autostart_recon()); 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()); checkbox_clear_output.set_value(persistent_memory::recon_clear_output());
text_input_file.set(_input_file); text_input_file.set(_input_file);
@@ -96,49 +94,38 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
void ReconSetupViewMain::save(std::string& input_file, std::string& 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_autosave_freqs(checkbox_autosave_freqs.value());
persistent_memory::set_recon_autostart_recon(checkbox_autostart_recon.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()); persistent_memory::set_recon_clear_output(checkbox_clear_output.value());
input_file = _input_file; input_file = _input_file;
output_file = _output_file; output_file = _output_file;
}; };
void ReconSetupViewMore::save(uint32_t& recon_lock_duration, uint32_t& recon_lock_nb_match, uint32_t& recon_match_mode) { void ReconSetupViewMore::save() {
persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value()); 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_ranges(checkbox_load_ranges.value());
persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.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()); persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value());
recon_lock_duration = field_recon_lock_duration.value(); persistent_memory::set_recon_auto_record_locked(checkbox_auto_record_locked.value());
recon_lock_nb_match = field_recon_lock_nb_match.value();
recon_match_mode = field_recon_match_mode.selected_index_value();
}; };
void ReconSetupViewMain::focus() { void ReconSetupViewMain::focus() {
button_load_freqs.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) ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
: View(parent_rect), _recon_lock_duration{recon_lock_duration}, _recon_lock_nb_match{recon_lock_nb_match}, _recon_match_mode{recon_match_mode} { : View(parent_rect) {
(void)nav; (void)nav;
hidden(true); hidden(true);
add_children({ add_children({&checkbox_load_freqs,
&checkbox_load_freqs, &checkbox_load_ranges,
&checkbox_load_ranges, &checkbox_load_hamradios,
&checkbox_load_hamradios, &checkbox_update_ranges_when_recon,
&checkbox_update_ranges_when_recon, &checkbox_auto_record_locked});
&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_freqs.set_value(persistent_memory::recon_load_freqs());
checkbox_load_ranges.set_value(persistent_memory::recon_load_ranges()); checkbox_load_ranges.set_value(persistent_memory::recon_load_ranges());
checkbox_load_hamradios.set_value(persistent_memory::recon_load_hamradios()); checkbox_load_hamradios.set_value(persistent_memory::recon_load_hamradios());
checkbox_update_ranges_when_recon.set_value(persistent_memory::recon_update_ranges_when_recon()); checkbox_update_ranges_when_recon.set_value(persistent_memory::recon_update_ranges_when_recon());
field_recon_lock_duration.set_value(_recon_lock_duration); checkbox_auto_record_locked.set_value(persistent_memory::recon_auto_record_locked());
field_recon_lock_nb_match.set_value(_recon_lock_nb_match);
field_recon_match_mode.set_by_value(_recon_match_mode);
}; };
void ReconSetupViewMore::focus() { void ReconSetupViewMore::focus() {
@@ -149,14 +136,8 @@ void ReconSetupView::focus() {
viewMain.focus(); viewMain.focus();
} }
ReconSetupView::ReconSetupView( ReconSetupView::ReconSetupView(NavigationView& nav, std::string _input_file, std::string _output_file)
NavigationView& nav, : nav_{nav}, input_file{_input_file}, output_file{_output_file} {
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, add_children({&tab_view,
&viewMain, &viewMain,
&viewMore, &viewMore,
@@ -164,13 +145,10 @@ ReconSetupView::ReconSetupView(
button_save.on_select = [this, &nav](Button&) { button_save.on_select = [this, &nav](Button&) {
viewMain.save(input_file, output_file); viewMain.save(input_file, output_file);
viewMore.save(recon_lock_duration, recon_lock_nb_match, recon_match_mode); viewMore.save();
std::vector<std::string> messages; std::vector<std::string> messages;
messages.push_back(input_file); messages.push_back(input_file);
messages.push_back(output_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); on_changed(messages);
nav.pop(); nav.pop();
}; };
+14 -53
View File
@@ -71,8 +71,8 @@ class ReconSetupViewMain : public View {
void focus() override; void focus() override;
private: private:
std::string _input_file = {"RECON"}; std::string _input_file{"RECON"};
std::string _output_file = {"RECON_RESULTS"}; std::string _output_file{"RECON_RESULTS"};
Button button_load_freqs{ Button button_load_freqs{
{1 * 8, 12, 18 * 8, 22}, {1 * 8, 12, 18 * 8, 22},
@@ -98,29 +98,21 @@ class ReconSetupViewMain : public View {
3, 3,
"autostart recon"}; "autostart recon"};
Checkbox checkbox_continuous{
{1 * 8, 11 * 16 - 4},
3,
"continuous"};
Checkbox checkbox_clear_output{ Checkbox checkbox_clear_output{
{1 * 8, 13 * 16 - 4}, {1 * 8, 11 * 16 - 4},
3, 3,
"clear output at start"}; "clear output at start"};
}; };
class ReconSetupViewMore : public View { class ReconSetupViewMore : public View {
public: public:
ReconSetupViewMore(NavigationView& nav, Rect parent_rect, uint32_t _recon_lock_duration, uint32_t _recon_lock_nb_match, uint32_t _recon_match_mode); ReconSetupViewMore(NavigationView& nav, Rect parent_rect);
void save(uint32_t& recon_lock_duration, uint32_t& recon_lock_nb_match, uint32_t& recon_match_mode); void save();
void focus() override; void focus() override;
private: private:
uint32_t _recon_lock_duration = STATS_UPDATE_INTERVAL;
uint32_t _recon_lock_nb_match = RECON_DEF_NB_MATCH;
uint32_t _recon_match_mode = RECON_MATCH_CONTINUOUS;
Checkbox checkbox_load_freqs{ Checkbox checkbox_load_freqs{
{1 * 8, 12}, {1 * 8, 12},
3, 3,
@@ -140,43 +132,15 @@ class ReconSetupViewMore : public View {
{1 * 8, 102}, {1 * 8, 102},
3, 3,
"auto update m-ranges"}; "auto update m-ranges"};
Checkbox checkbox_auto_record_locked{
NumberField field_recon_lock_duration{ {1 * 8, 132},
{1 * 8, 132}, // position X , Y 3,
4, // number of displayed digits (even empty) "auto record locked periods"};
{STATS_UPDATE_INTERVAL, RECON_MAX_LOCK_DURATION}, // range of number
STATS_UPDATE_INTERVAL, // rotary encoder increment
' ', // filling character
false // can loop
};
Text text_recon_lock_duration{
{1 * 8, 132, 22 * 8, 22},
" ms (lock duration)"};
NumberField field_recon_lock_nb_match{
{1 * 8, 162},
4,
{1, 99},
1,
' ',
false};
Text text_recon_lock_nb{
{1 * 8, 162, 25 * 8, 22},
" x (nb lock to match freq)"};
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 { class ReconSetupView : public View {
public: public:
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); ReconSetupView(NavigationView& nav, std::string _input_file, std::string _output_file);
std::function<void(std::vector<std::string> messages)> on_changed{}; std::function<void(std::vector<std::string> messages)> on_changed{};
@@ -187,16 +151,13 @@ class ReconSetupView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
std::string input_file = {"RECON"}; std::string input_file{"RECON"};
std::string output_file = {"RECON_RESULTS"}; std::string output_file{"RECON_RESULTS"};
uint32_t recon_lock_duration = STATS_UPDATE_INTERVAL;
uint32_t recon_lock_nb_match = RECON_DEF_NB_MATCH;
uint32_t recon_match_mode = RECON_MATCH_CONTINUOUS;
Rect view_rect = {0, 3 * 8, SCREEN_W, 230}; Rect view_rect{0, 3 * 8, SCREEN_W, 230};
ReconSetupViewMain viewMain{nav_, view_rect, input_file, output_file}; ReconSetupViewMain viewMain{nav_, view_rect, input_file, output_file};
ReconSetupViewMore viewMore{nav_, view_rect, recon_lock_duration, recon_lock_nb_match, recon_match_mode}; ReconSetupViewMore viewMore{nav_, view_rect};
TabView tab_view{ TabView tab_view{
{"Main", Color::cyan(), &viewMain}, {"Main", Color::cyan(), &viewMain},
+23 -23
View File
@@ -122,7 +122,7 @@ void ScannerThread::run() {
if (force_one_step) if (force_one_step)
_index_stepper = 0; _index_stepper = 0;
receiver_model.set_tuning_frequency(frequency_list_[frequency_index]); // Retune receiver_model.set_target_frequency(frequency_list_[frequency_index]); // Retune
} }
message.freq = frequency_list_[frequency_index]; message.freq = frequency_list_[frequency_index];
message.range = frequency_index; // Inform freq (for coloring purposes also!) message.range = frequency_index; // Inform freq (for coloring purposes also!)
@@ -160,7 +160,7 @@ void ScannerThread::run() {
if (force_one_step) if (force_one_step)
_index_stepper = 0; _index_stepper = 0;
receiver_model.set_tuning_frequency(frequency_range_.min + frequency_index * def_step_hz_); // Retune receiver_model.set_target_frequency(frequency_range_.min + frequency_index * def_step_hz_); // Retune
} }
message.freq = frequency_range_.min + frequency_index * def_step_hz_; message.freq = frequency_range_.min + frequency_index * def_step_hz_;
message.range = 0; // Inform freq (for coloring purposes also!) message.range = 0; // Inform freq (for coloring purposes also!)
@@ -239,7 +239,7 @@ void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
} }
void ScannerView::focus() { void ScannerView::focus() {
field_mode.focus(); button_load.focus();
} }
ScannerView::~ScannerView() { ScannerView::~ScannerView() {
@@ -299,14 +299,13 @@ ScannerView::ScannerView(
freqman_set_modulation_option(field_mode); freqman_set_modulation_option(field_mode);
freqman_set_step_option(field_step); freqman_set_step_option(field_step);
// Default starting modulation (these may be overridden in SCANNER.TXT) // Default starting modulation (from saved App Settings if enabled, and may be overridden in SCANNER.TXT)
change_mode(AM_MODULATION); // Default modulation field_mode.set_by_value((OptionsField::value_t)receiver_model.modulation()); // Reflect the mode into the manual selector
field_mode.set_by_value(AM_MODULATION); // Reflect the mode into the manual selector field_step.set_by_value(receiver_model.frequency_step()); // Default step interval (Hz)
field_step.set_by_value(9000); // Default step interval (Hz) change_mode((freqman_index_t)field_mode.selected_index_value());
// FUTURE: perhaps additional settings should be stored in persistent memory vs using defaults // FUTURE: perhaps additional settings should be stored in persistent memory vs using defaults
// HELPER: Pre-setting a manual range, based on stored frequency rf::Frequency stored_freq = receiver_model.target_frequency();
rf::Frequency stored_freq = persistent_memory::tuned_frequency();
frequency_range.min = stored_freq - 1000000; frequency_range.min = stored_freq - 1000000;
button_manual_start.set_text(to_string_short_freq(frequency_range.min)); button_manual_start.set_text(to_string_short_freq(frequency_range.min));
frequency_range.max = stored_freq + 1000000; frequency_range.max = stored_freq + 1000000;
@@ -448,6 +447,16 @@ ScannerView::ScannerView(
// Mode field was changed (AM/NFM/WFM) // Mode field was changed (AM/NFM/WFM)
field_mode.on_change = [this](size_t, OptionsField::value_t v) { field_mode.on_change = [this](size_t, OptionsField::value_t v) {
static freqman_index_t last_mode = AM_MODULATION;
// unsupported SPEC mode fix
if (v == SPEC_MODULATION) {
if (last_mode == AM_MODULATION)
v = WFM_MODULATION;
else
v = AM_MODULATION;
field_mode.set_selected_index(v);
}
last_mode = v;
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
change_mode((freqman_index_t)v); change_mode((freqman_index_t)v);
@@ -458,7 +467,7 @@ ScannerView::ScannerView(
// Step field was changed (Hz) -- only affects manual Search mode // Step field was changed (Hz) -- only affects manual Search mode
field_step.on_change = [this](size_t, OptionsField::value_t v) { field_step.on_change = [this](size_t, OptionsField::value_t v) {
(void)v; // prevent compiler Unused warning receiver_model.set_frequency_step(v);
if (manual_search && scan_thread) { if (manual_search && scan_thread) {
// Restart scan thread with new step value // Restart scan thread with new step value
@@ -759,31 +768,22 @@ void ScannerView::change_mode(freqman_index_t new_mod) { // Before this, do a s
freqman_set_bandwidth_option(new_mod, field_bw); freqman_set_bandwidth_option(new_mod, field_bw);
baseband::run_image(portapack::spi_flash::image_tag_am_audio); baseband::run_image(portapack::spi_flash::image_tag_am_audio);
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio); receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
field_bw.set_by_value(0); field_bw.set_by_value(receiver_model.am_configuration());
receiver_model.set_am_configuration(field_bw.selected_index_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_am_configuration(n); }; field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_am_configuration(n); };
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
break; break;
case NFM_MODULATION: // bw 16k (2) default case NFM_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw); freqman_set_bandwidth_option(new_mod, field_bw);
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio); baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
field_bw.set_by_value(2); field_bw.set_by_value(receiver_model.nbfm_configuration());
receiver_model.set_nbfm_configuration(field_bw.selected_index_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_nbfm_configuration(n); }; field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_nbfm_configuration(n); };
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
break; break;
case WFM_MODULATION: case WFM_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw); freqman_set_bandwidth_option(new_mod, field_bw);
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio); baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
field_bw.set_by_value(0); field_bw.set_by_value(receiver_model.wfm_configuration());
receiver_model.set_wfm_configuration(field_bw.selected_index_value());
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_wfm_configuration(n); }; field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_wfm_configuration(n); };
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(2000000);
break; break;
default: default:
break; break;
+5
View File
@@ -22,6 +22,7 @@
#include "ui.hpp" #include "ui.hpp"
#include "receiver_model.hpp" #include "receiver_model.hpp"
#include "radio_state.hpp"
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_styles.hpp" #include "ui_styles.hpp"
#include "freqman.hpp" #include "freqman.hpp"
@@ -94,7 +95,11 @@ class ScannerView : public View {
// void set_parent_rect(const Rect new_parent_rect) override; // void set_parent_rect(const Rect new_parent_rect) override;
private: private:
app_settings::SettingsManager settings_{
"rx_scanner", app_settings::Mode::RX};
NavigationView& nav_; NavigationView& nav_;
RxRadioState radio_state_{};
void start_scan_thread(); void start_scan_thread();
void change_mode(freqman_index_t mod_type); void change_mode(freqman_index_t mod_type);
+6 -8
View File
@@ -221,7 +221,7 @@ void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
slice_counter = 0; slice_counter = 0;
} else } else
slice_counter++; slice_counter++;
receiver_model.set_tuning_frequency(slices[slice_counter].center_frequency); receiver_model.set_target_frequency(slices[slice_counter].center_frequency);
baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); // Clear baseband::set_spectrum(SEARCH_SLICE_WIDTH, 31); // Clear
} else { } else {
// Unique slice // Unique slice
@@ -271,7 +271,7 @@ void SearchView::on_range_changed() {
} }
} else { } else {
slices[0].center_frequency = (f_max + f_min) / 2; slices[0].center_frequency = (f_max + f_min) / 2;
receiver_model.set_tuning_frequency(slices[0].center_frequency); receiver_model.set_target_frequency(slices[0].center_frequency);
slices_nb = 1; slices_nb = 1;
text_slices.set(" 1"); text_slices.set(" 1");
@@ -376,25 +376,25 @@ SearchView::SearchView(
power_threshold = value; power_threshold = value;
}; };
field_frequency_min.set_value(receiver_model.tuning_frequency() - 1000000); field_frequency_min.set_value(receiver_model.target_frequency() - 1000000);
field_frequency_min.set_step(100000); field_frequency_min.set_step(100000);
field_frequency_min.on_change = [this](rf::Frequency) { field_frequency_min.on_change = [this](rf::Frequency) {
this->on_range_changed(); this->on_range_changed();
}; };
field_frequency_min.on_edit = [this, &nav]() { field_frequency_min.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency() - 1000000);
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency_min.set_value(f); this->field_frequency_min.set_value(f);
}; };
}; };
field_frequency_max.set_value(receiver_model.tuning_frequency() + 1000000); field_frequency_max.set_value(receiver_model.target_frequency() + 1000000);
field_frequency_max.set_step(100000); field_frequency_max.set_step(100000);
field_frequency_max.on_change = [this](rf::Frequency) { field_frequency_max.on_change = [this](rf::Frequency) {
this->on_range_changed(); this->on_range_changed();
}; };
field_frequency_max.on_edit = [this, &nav]() { field_frequency_max.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency() + 1000000);
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency_max.set_value(f); this->field_frequency_max.set_value(f);
}; };
@@ -415,8 +415,6 @@ SearchView::SearchView(
on_range_changed(); on_range_changed();
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis); receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_sampling_rate(SEARCH_SLICE_WIDTH);
receiver_model.set_baseband_bandwidth(2500000);
receiver_model.enable(); receiver_model.enable();
} }
+7 -4
View File
@@ -21,6 +21,7 @@
*/ */
#include "receiver_model.hpp" #include "receiver_model.hpp"
#include "radio_state.hpp"
#include "spectrum_color_lut.hpp" #include "spectrum_color_lut.hpp"
@@ -89,6 +90,10 @@ class SearchView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
RxRadioState radio_state_{
2500000 /* bandwidth */,
SEARCH_SLICE_WIDTH /* sampling rate */
};
struct slice_t { struct slice_t {
rf::Frequency center_frequency; rf::Frequency center_frequency;
@@ -142,11 +147,9 @@ class SearchView : public View {
{{26 * 8, 25 * 8}, "MHz", Color::light_grey()}}; {{26 * 8, 25 * 8}, "MHz", Color::light_grey()}};
FrequencyField field_frequency_min{ FrequencyField field_frequency_min{
{1 * 8, 1 * 16}, {1 * 8, 1 * 16}};
};
FrequencyField field_frequency_max{ FrequencyField field_frequency_max{
{11 * 8, 1 * 16}, {11 * 8, 1 * 16}};
};
LNAGainField field_lna{ LNAGainField field_lna{
{22 * 8, 1 * 16}}; {22 * 8, 1 * 16}};
VGAGainField field_vga{ VGAGainField field_vga{
+139 -109
View File
@@ -24,6 +24,7 @@
#include "ui_settings.hpp" #include "ui_settings.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_touch_calibration.hpp" #include "ui_touch_calibration.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
@@ -32,16 +33,24 @@ using namespace lpc43xx;
#include "audio.hpp" #include "audio.hpp"
#include "portapack.hpp" #include "portapack.hpp"
using portapack::receiver_model;
using namespace portapack; using namespace portapack;
#include "string_format.hpp" #include "string_format.hpp"
#include "ui_styles.hpp" #include "ui_styles.hpp"
#include "cpld_update.hpp" #include "cpld_update.hpp"
#include "freqman.hpp" namespace pmem = portapack::persistent_memory;
namespace ui { namespace ui {
/* Sends a UI refresh message to cause the status bar to redraw. */
static void send_system_refresh() {
StatusRefreshMessage message{};
EventDispatcher::send_message(message);
}
/* SetDateTimeView ***************************************/
SetDateTimeView::SetDateTimeView( SetDateTimeView::SetDateTimeView(
NavigationView& nav) { NavigationView& nav) {
button_save.on_select = [&nav, this](Button&) { button_save.on_select = [&nav, this](Button&) {
@@ -105,13 +114,15 @@ SetDateTimeModel SetDateTimeView::form_collect() {
.second = static_cast<uint8_t>(field_second.value())}; .second = static_cast<uint8_t>(field_second.value())};
} }
/* SetRadioView ******************************************/
SetRadioView::SetRadioView( SetRadioView::SetRadioView(
NavigationView& nav) { NavigationView& nav) {
button_cancel.on_select = [&nav](Button&) { button_cancel.on_select = [&nav](Button&) {
nav.pop(); nav.pop();
}; };
const auto reference = portapack::clock_manager.get_reference(); const auto reference = clock_manager.get_reference();
std::string source_name("---"); std::string source_name("---");
switch (reference.source) { switch (reference.source) {
@@ -156,19 +167,18 @@ SetRadioView::SetRadioView(
&button_cancel}); &button_cancel});
SetFrequencyCorrectionModel model{ SetFrequencyCorrectionModel model{
static_cast<int8_t>(portapack::persistent_memory::correction_ppb() / 1000), 0}; static_cast<int8_t>(pmem::correction_ppb() / 1000), 0};
form_init(model); form_init(model);
check_clkout.set_value(portapack::persistent_memory::clkout_enabled()); check_clkout.set_value(pmem::clkout_enabled());
check_clkout.on_select = [this](Checkbox&, bool v) { check_clkout.on_select = [this](Checkbox&, bool v) {
clock_manager.enable_clock_output(v); clock_manager.enable_clock_output(v);
portapack::persistent_memory::set_clkout_enabled(v); pmem::set_clkout_enabled(v);
StatusRefreshMessage message{}; send_system_refresh();
EventDispatcher::send_message(message);
}; };
field_clkout_freq.set_value(portapack::persistent_memory::clkout_freq()); field_clkout_freq.set_value(pmem::clkout_freq());
value_freq_step.set_style(&Styles::light_grey); value_freq_step.set_style(&Styles::light_grey);
field_clkout_freq.on_select = [this](NumberField&) { field_clkout_freq.on_select = [this](NumberField&) {
@@ -193,9 +203,9 @@ SetRadioView::SetRadioView(
field_clkout_freq.set_step(pow(10, freq_step_khz)); field_clkout_freq.set_step(pow(10, freq_step_khz));
}; };
check_bias.set_value(portapack::get_antenna_bias()); check_bias.set_value(get_antenna_bias());
check_bias.on_select = [this](Checkbox&, bool v) { check_bias.on_select = [this](Checkbox&, bool v) {
portapack::set_antenna_bias(v); set_antenna_bias(v);
// Update the radio. // Update the radio.
receiver_model.set_antenna_bias(); receiver_model.set_antenna_bias();
@@ -205,15 +215,14 @@ SetRadioView::SetRadioView(
if (!v) if (!v)
radio::set_antenna_bias(false); radio::set_antenna_bias(false);
StatusRefreshMessage message{}; send_system_refresh();
EventDispatcher::send_message(message);
}; };
button_save.on_select = [this, &nav](Button&) { button_save.on_select = [this, &nav](Button&) {
const auto model = this->form_collect(); const auto model = this->form_collect();
portapack::persistent_memory::set_correction_ppb(model.ppm * 1000); pmem::set_correction_ppb(model.ppm * 1000);
portapack::persistent_memory::set_clkout_freq(model.freq); pmem::set_clkout_freq(model.freq);
clock_manager.enable_clock_output(portapack::persistent_memory::clkout_enabled()); clock_manager.enable_clock_output(pmem::clkout_enabled());
nav.pop(); nav.pop();
}; };
} }
@@ -233,54 +242,86 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() {
}; };
} }
/* SetUIView *********************************************/
SetUIView::SetUIView(NavigationView& nav) { SetUIView::SetUIView(NavigationView& nav) {
add_children({&checkbox_disable_touchscreen, add_children({&checkbox_disable_touchscreen,
&checkbox_speaker,
&checkbox_bloff, &checkbox_bloff,
&options_bloff, &options_bloff,
&checkbox_showsplash, &checkbox_showsplash,
&checkbox_showclock, &checkbox_showclock,
&options_clockformat, &options_clockformat,
&checkbox_guireturnflag, &checkbox_guireturnflag,
&labels,
&toggle_camera,
&toggle_sleep,
&toggle_stealth,
&toggle_converter,
&toggle_bias_tee,
&toggle_clock,
&toggle_mute,
&toggle_sd_card,
&button_save, &button_save,
&button_cancel}); &button_cancel});
checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen()); // Display "Disable speaker" option only if AK4951 Codec which has separate speaker/headphone control
checkbox_speaker.set_value(persistent_memory::config_speaker()); if (audio::speaker_disable_supported()) {
checkbox_showsplash.set_value(persistent_memory::config_splash()); add_child(&toggle_speaker);
checkbox_showclock.set_value(!persistent_memory::hide_clock()); }
checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon());
const auto backlight_config = persistent_memory::config_backlight_timer(); checkbox_disable_touchscreen.set_value(pmem::disable_touchscreen());
checkbox_showsplash.set_value(pmem::config_splash());
checkbox_showclock.set_value(!pmem::hide_clock());
checkbox_guireturnflag.set_value(pmem::show_gui_return_icon());
const auto backlight_config = pmem::config_backlight_timer();
checkbox_bloff.set_value(backlight_config.timeout_enabled()); checkbox_bloff.set_value(backlight_config.timeout_enabled());
options_bloff.set_by_value(backlight_config.timeout_enum()); options_bloff.set_by_value(backlight_config.timeout_enum());
if (persistent_memory::clock_with_date()) { if (pmem::clock_with_date()) {
options_clockformat.set_selected_index(1); options_clockformat.set_selected_index(1);
} else { } else {
options_clockformat.set_selected_index(0); options_clockformat.set_selected_index(0);
} }
// NB: Invert so "active" == "not hidden"
toggle_camera.set_value(!pmem::ui_hide_camera());
toggle_sleep.set_value(!pmem::ui_hide_sleep());
toggle_stealth.set_value(!pmem::ui_hide_stealth());
toggle_converter.set_value(!pmem::ui_hide_converter());
toggle_bias_tee.set_value(!pmem::ui_hide_bias_tee());
toggle_clock.set_value(!pmem::ui_hide_clock());
toggle_speaker.set_value(!pmem::ui_hide_speaker());
toggle_mute.set_value(!pmem::ui_hide_mute());
toggle_sd_card.set_value(!pmem::ui_hide_sd_card());
button_save.on_select = [&nav, this](Button&) { button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_config_backlight_timer({(persistent_memory::backlight_timeout_t)options_bloff.selected_index_value(), pmem::set_config_backlight_timer({(pmem::backlight_timeout_t)options_bloff.selected_index_value(),
checkbox_bloff.value()}); checkbox_bloff.value()});
if (checkbox_showclock.value()) { if (checkbox_showclock.value()) {
if (options_clockformat.selected_index() == 1) if (options_clockformat.selected_index() == 1)
persistent_memory::set_clock_with_date(true); pmem::set_clock_with_date(true);
else else
persistent_memory::set_clock_with_date(false); pmem::set_clock_with_date(false);
} }
if (checkbox_speaker.value()) audio::output::speaker_mute(); // Just mute audio if speaker is disabled pmem::set_config_splash(checkbox_showsplash.value());
persistent_memory::set_config_speaker(checkbox_speaker.value()); // Store Speaker status pmem::set_clock_hidden(!checkbox_showclock.value());
StatusRefreshMessage message{}; // Refresh status bar with/out speaker pmem::set_gui_return_icon(checkbox_guireturnflag.value());
EventDispatcher::send_message(message); pmem::set_disable_touchscreen(checkbox_disable_touchscreen.value());
pmem::set_ui_hide_camera(!toggle_camera.value());
pmem::set_ui_hide_sleep(!toggle_sleep.value());
pmem::set_ui_hide_stealth(!toggle_stealth.value());
pmem::set_ui_hide_converter(!toggle_converter.value());
pmem::set_ui_hide_bias_tee(!toggle_bias_tee.value());
pmem::set_ui_hide_clock(!toggle_clock.value());
pmem::set_ui_hide_speaker(!toggle_speaker.value());
pmem::set_ui_hide_mute(!toggle_mute.value());
pmem::set_ui_hide_sd_card(!toggle_sd_card.value());
send_system_refresh();
persistent_memory::set_config_splash(checkbox_showsplash.value());
persistent_memory::set_clock_hidden(!checkbox_showclock.value());
persistent_memory::set_gui_return_icon(checkbox_guireturnflag.value());
persistent_memory::set_disable_touchscreen(checkbox_disable_touchscreen.value());
nav.pop(); nav.pop();
}; };
button_cancel.on_select = [&nav, this](Button&) { button_cancel.on_select = [&nav, this](Button&) {
@@ -292,21 +333,20 @@ void SetUIView::focus() {
button_save.focus(); button_save.focus();
} }
// --------------------------------------------------------- /* SetAppSettingsView ************************************/
// Appl. Settings
// ---------------------------------------------------------
SetAppSettingsView::SetAppSettingsView(NavigationView& nav) { SetAppSettingsView::SetAppSettingsView(NavigationView& nav) {
add_children({&checkbox_load_app_settings, add_children({&checkbox_load_app_settings,
&checkbox_save_app_settings, &checkbox_save_app_settings,
&button_save, &button_save,
&button_cancel}); &button_cancel});
checkbox_load_app_settings.set_value(persistent_memory::load_app_settings()); checkbox_load_app_settings.set_value(pmem::load_app_settings());
checkbox_save_app_settings.set_value(persistent_memory::save_app_settings()); checkbox_save_app_settings.set_value(pmem::save_app_settings());
button_save.on_select = [&nav, this](Button&) { button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_load_app_settings(checkbox_load_app_settings.value()); pmem::set_load_app_settings(checkbox_load_app_settings.value());
persistent_memory::set_save_app_settings(checkbox_save_app_settings.value()); pmem::set_save_app_settings(checkbox_save_app_settings.value());
nav.pop(); nav.pop();
}; };
button_cancel.on_select = [&nav, this](Button&) { button_cancel.on_select = [&nav, this](Button&) {
@@ -318,9 +358,8 @@ void SetAppSettingsView::focus() {
button_save.focus(); button_save.focus();
} }
// --------------------------------------------------------- /* SetConverterSettingsView ******************************/
// Converter Settings
// ---------------------------------------------------------
SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) { SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) {
add_children({&check_show_converter, add_children({&check_show_converter,
&check_converter, &check_converter,
@@ -328,48 +367,45 @@ SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) {
&button_converter_freq, &button_converter_freq,
&button_return}); &button_return});
check_show_converter.set_value(!portapack::persistent_memory::config_hide_converter()); check_show_converter.set_value(!pmem::ui_hide_converter());
check_show_converter.on_select = [this](Checkbox&, bool v) { check_show_converter.on_select = [this](Checkbox&, bool v) {
portapack::persistent_memory::set_config_hide_converter(!v); pmem::set_ui_hide_converter(!v);
if (!v) { if (!v) {
check_converter.set_value(false); check_converter.set_value(false);
} }
// Retune to take converter change in account // Retune to take converter change in account.
receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); receiver_model.set_target_frequency(receiver_model.target_frequency());
// Refresh status bar with/out converter // Refresh status bar with/out converter
StatusRefreshMessage message{}; send_system_refresh();
EventDispatcher::send_message(message);
}; };
check_converter.set_value(portapack::persistent_memory::config_converter()); check_converter.set_value(pmem::config_converter());
check_converter.on_select = [this](Checkbox&, bool v) { check_converter.on_select = [this](Checkbox&, bool v) {
if (v) { if (v) {
check_show_converter.set_value(true); check_show_converter.set_value(true);
portapack::persistent_memory::set_config_hide_converter(false); pmem::set_ui_hide_converter(false);
} }
portapack::persistent_memory::set_config_converter(v); pmem::set_config_converter(v);
// Retune to take converter change in account // Retune to take converter change in account
receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); receiver_model.set_target_frequency(receiver_model.target_frequency());
// Refresh status bar with/out converter // Refresh status bar with/out converter
StatusRefreshMessage message{}; send_system_refresh();
EventDispatcher::send_message(message);
}; };
converter_mode.set_by_value(portapack::persistent_memory::config_updown_converter()); converter_mode.set_by_value(pmem::config_updown_converter());
converter_mode.on_change = [this](size_t, OptionsField::value_t v) { converter_mode.on_change = [this](size_t, OptionsField::value_t v) {
portapack::persistent_memory::set_config_updown_converter(v); pmem::set_config_updown_converter(v);
// Refresh status bar with icon up or down // Refresh status bar with icon up or down
StatusRefreshMessage message{}; send_system_refresh();
EventDispatcher::send_message(message);
}; };
button_converter_freq.set_text(to_string_short_freq(portapack::persistent_memory::config_converter_freq()) + "MHz"); button_converter_freq.set_text(to_string_short_freq(pmem::config_converter_freq()) + "MHz");
button_converter_freq.on_select = [this, &nav](Button& button) { button_converter_freq.on_select = [this, &nav](Button& button) {
auto new_view = nav.push<FrequencyKeypadView>(portapack::persistent_memory::config_converter_freq()); auto new_view = nav.push<FrequencyKeypadView>(pmem::config_converter_freq());
new_view->on_changed = [this, &button](rf::Frequency f) { new_view->on_changed = [this, &button](rf::Frequency f) {
portapack::persistent_memory::set_config_converter_freq(f); pmem::set_config_converter_freq(f);
// Retune to take converter change in account // Retune to take converter change in account
receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); receiver_model.set_target_frequency(receiver_model.target_frequency());
button_converter_freq.set_text("<" + to_string_short_freq(f) + " MHz>"); button_converter_freq.set_text("<" + to_string_short_freq(f) + " MHz>");
}; };
}; };
@@ -383,9 +419,8 @@ void SetConverterSettingsView::focus() {
button_return.focus(); button_return.focus();
} }
// --------------------------------------------------------- /* SetFrequencyCorrectionView ****************************/
// Frequency Correction Settings
// ---------------------------------------------------------
SetFrequencyCorrectionView::SetFrequencyCorrectionView(NavigationView& nav) { SetFrequencyCorrectionView::SetFrequencyCorrectionView(NavigationView& nav) {
add_children({&text_freqCorrection_about, add_children({&text_freqCorrection_about,
&frequency_rx_correction_mode, &frequency_rx_correction_mode,
@@ -394,38 +429,38 @@ SetFrequencyCorrectionView::SetFrequencyCorrectionView(NavigationView& nav) {
&button_freq_tx_correction, &button_freq_tx_correction,
&button_return}); &button_return});
frequency_rx_correction_mode.set_by_value(portapack::persistent_memory::config_freq_rx_correction_updown()); frequency_rx_correction_mode.set_by_value(pmem::config_freq_rx_correction_updown());
frequency_rx_correction_mode.on_change = [this](size_t, OptionsField::value_t v) { frequency_rx_correction_mode.on_change = [this](size_t, OptionsField::value_t v) {
portapack::persistent_memory::set_freq_rx_correction_updown(v); pmem::set_freq_rx_correction_updown(v);
}; };
frequency_tx_correction_mode.set_by_value(portapack::persistent_memory::config_freq_rx_correction_updown()); frequency_tx_correction_mode.set_by_value(pmem::config_freq_rx_correction_updown());
frequency_tx_correction_mode.on_change = [this](size_t, OptionsField::value_t v) { frequency_tx_correction_mode.on_change = [this](size_t, OptionsField::value_t v) {
portapack::persistent_memory::set_freq_tx_correction_updown(v); pmem::set_freq_tx_correction_updown(v);
}; };
button_freq_rx_correction.set_text(to_string_short_freq(portapack::persistent_memory::config_freq_rx_correction()) + "MHz (Rx)"); button_freq_rx_correction.set_text(to_string_short_freq(pmem::config_freq_rx_correction()) + "MHz (Rx)");
button_freq_rx_correction.on_select = [this, &nav](Button& button) { button_freq_rx_correction.on_select = [this, &nav](Button& button) {
auto new_view = nav.push<FrequencyKeypadView>(portapack::persistent_memory::config_converter_freq()); auto new_view = nav.push<FrequencyKeypadView>(pmem::config_converter_freq());
new_view->on_changed = [this, &button](rf::Frequency f) { new_view->on_changed = [this, &button](rf::Frequency f) {
if (f >= MAX_FREQ_CORRECTION) if (f >= MAX_FREQ_CORRECTION)
f = MAX_FREQ_CORRECTION; f = MAX_FREQ_CORRECTION;
portapack::persistent_memory::set_config_freq_rx_correction(f); pmem::set_config_freq_rx_correction(f);
// Retune to take converter change in account // Retune to take converter change in account
receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); receiver_model.set_target_frequency(receiver_model.target_frequency());
button_freq_rx_correction.set_text("<" + to_string_short_freq(f) + " MHz>"); button_freq_rx_correction.set_text("<" + to_string_short_freq(f) + " MHz>");
}; };
}; };
button_freq_tx_correction.set_text(to_string_short_freq(portapack::persistent_memory::config_freq_tx_correction()) + "MHz (Tx)"); button_freq_tx_correction.set_text(to_string_short_freq(pmem::config_freq_tx_correction()) + "MHz (Tx)");
button_freq_tx_correction.on_select = [this, &nav](Button& button) { button_freq_tx_correction.on_select = [this, &nav](Button& button) {
auto new_view = nav.push<FrequencyKeypadView>(portapack::persistent_memory::config_converter_freq()); auto new_view = nav.push<FrequencyKeypadView>(pmem::config_converter_freq());
new_view->on_changed = [this, &button](rf::Frequency f) { new_view->on_changed = [this, &button](rf::Frequency f) {
if (f >= MAX_FREQ_CORRECTION) if (f >= MAX_FREQ_CORRECTION)
f = MAX_FREQ_CORRECTION; f = MAX_FREQ_CORRECTION;
portapack::persistent_memory::set_config_freq_tx_correction(f); pmem::set_config_freq_tx_correction(f);
// Retune to take converter change in account // Retune to take converter change in account
receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency()); receiver_model.set_target_frequency(receiver_model.target_frequency());
button_freq_tx_correction.set_text("<" + to_string_short_freq(f) + " MHz>"); button_freq_tx_correction.set_text("<" + to_string_short_freq(f) + " MHz>");
}; };
}; };
@@ -439,9 +474,8 @@ void SetFrequencyCorrectionView::focus() {
button_return.focus(); button_return.focus();
} }
// --------------------------------------------------------- /* SetPersistentMemoryView *******************************/
// Persistent Memory Settings
// ---------------------------------------------------------
SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) { SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
add_children({&text_pmem_about, add_children({&text_pmem_about,
&text_pmem_informations, &text_pmem_informations,
@@ -452,7 +486,7 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
&button_load_mem_defaults, &button_load_mem_defaults,
&button_return}); &button_return});
check_use_sdcard_for_pmem.set_value(portapack::persistent_memory::should_use_sdcard_for_pmem()); check_use_sdcard_for_pmem.set_value(pmem::should_use_sdcard_for_pmem());
check_use_sdcard_for_pmem.on_select = [this](Checkbox&, bool v) { check_use_sdcard_for_pmem.on_select = [this](Checkbox&, bool v) {
File pmem_flag_file_handle; File pmem_flag_file_handle;
std::string pmem_flag_file = PMEM_FILEFLAG; std::string pmem_flag_file = PMEM_FILEFLAG;
@@ -479,7 +513,7 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
}; };
button_save_mem_to_file.on_select = [&nav, this](Button&) { button_save_mem_to_file.on_select = [&nav, this](Button&) {
if (!portapack::persistent_memory::save_persistent_settings_to_file()) { if (!pmem::save_persistent_settings_to_file()) {
text_pmem_status.set("!problem saving settings!"); text_pmem_status.set("!problem saving settings!");
} else { } else {
text_pmem_status.set("settings saved"); text_pmem_status.set("settings saved");
@@ -487,13 +521,12 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
}; };
button_load_mem_from_file.on_select = [&nav, this](Button&) { button_load_mem_from_file.on_select = [&nav, this](Button&) {
if (!portapack::persistent_memory::load_persistent_settings_from_file()) { if (!pmem::load_persistent_settings_from_file()) {
text_pmem_status.set("!problem loading settings!"); text_pmem_status.set("!problem loading settings!");
} else { } else {
text_pmem_status.set("settings loaded"); text_pmem_status.set("settings loaded");
// Refresh status bar with icon up or down // Refresh status bar with icon up or down
StatusRefreshMessage message{}; send_system_refresh();
EventDispatcher::send_message(message);
} }
}; };
@@ -504,7 +537,7 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
YESNO, YESNO,
[this](bool choice) { [this](bool choice) {
if (choice) { if (choice) {
portapack::persistent_memory::cache::defaults(); pmem::cache::defaults();
} }
}); });
}; };
@@ -518,19 +551,19 @@ void SetPersistentMemoryView::focus() {
button_return.focus(); button_return.focus();
} }
// --------------------------------------------------------- /* SetAudioView ******************************************/
// Audio Settings
// ---------------------------------------------------------
SetAudioView::SetAudioView(NavigationView& nav) { SetAudioView::SetAudioView(NavigationView& nav) {
add_children({&labels, add_children({&labels,
&field_tone_mix, &field_tone_mix,
&button_save, &button_save,
&button_cancel}); &button_cancel});
field_tone_mix.set_value(persistent_memory::tone_mix()); field_tone_mix.set_value(pmem::tone_mix());
button_save.on_select = [&nav, this](Button&) { button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_tone_mix(field_tone_mix.value()); pmem::set_tone_mix(field_tone_mix.value());
audio::output::update_audio_mute();
nav.pop(); nav.pop();
}; };
@@ -543,18 +576,17 @@ void SetAudioView::focus() {
button_save.focus(); button_save.focus();
} }
// --------------------------------------------------------- /* SetQRCodeView *****************************************/
// QR Code Settings
// ------------------------------------------------------
SetQRCodeView::SetQRCodeView(NavigationView& nav) { SetQRCodeView::SetQRCodeView(NavigationView& nav) {
add_children({&checkbox_bigger_qr, add_children({&checkbox_bigger_qr,
&button_save, &button_save,
&button_cancel}); &button_cancel});
checkbox_bigger_qr.set_value(persistent_memory::show_bigger_qr_code()); checkbox_bigger_qr.set_value(pmem::show_bigger_qr_code());
button_save.on_select = [&nav, this](Button&) { button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_show_bigger_qr_code(checkbox_bigger_qr.value()); pmem::set_show_bigger_qr_code(checkbox_bigger_qr.value());
nav.pop(); nav.pop();
}; };
@@ -567,19 +599,18 @@ void SetQRCodeView::focus() {
button_save.focus(); button_save.focus();
} }
// --------------------------------------------------------- /* SetEncoderDialView ************************************/
// Rotary Encoder Dial Settings
// ---------------------------------------------------------
SetEncoderDialView::SetEncoderDialView(NavigationView& nav) { SetEncoderDialView::SetEncoderDialView(NavigationView& nav) {
add_children({&labels, add_children({&labels,
&field_encoder_dial_sensitivity, &field_encoder_dial_sensitivity,
&button_save, &button_save,
&button_cancel}); &button_cancel});
field_encoder_dial_sensitivity.set_by_value(persistent_memory::config_encoder_dial_sensitivity()); field_encoder_dial_sensitivity.set_by_value(pmem::config_encoder_dial_sensitivity());
button_save.on_select = [&nav, this](Button&) { button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_encoder_dial_sensitivity(field_encoder_dial_sensitivity.selected_index_value()); pmem::set_encoder_dial_sensitivity(field_encoder_dial_sensitivity.selected_index_value());
nav.pop(); nav.pop();
}; };
@@ -592,11 +623,10 @@ void SetEncoderDialView::focus() {
button_save.focus(); button_save.focus();
} }
// --------------------------------------------------------- /* SettingsMenuView **************************************/
// Settings main menu
// ---------------------------------------------------------
SettingsMenuView::SettingsMenuView(NavigationView& nav) { SettingsMenuView::SettingsMenuView(NavigationView& nav) {
if (portapack::persistent_memory::show_gui_return_icon()) { if (pmem::show_gui_return_icon()) {
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
} }
add_items({ add_items({
+52 -16
View File
@@ -27,6 +27,7 @@
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_menu.hpp" #include "ui_menu.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "bitmap.hpp"
#include "ff.h" #include "ff.h"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
@@ -34,7 +35,7 @@
namespace ui { namespace ui {
#define MAX_FREQ_CORRECTION 4294967295 // maximum possible for an uint32_t #define MAX_FREQ_CORRECTION INT32_MAX
struct SetDateTimeModel { struct SetDateTimeModel {
uint16_t year; uint16_t year;
@@ -208,21 +209,16 @@ class SetUIView : public View {
private: private:
Checkbox checkbox_disable_touchscreen{ Checkbox checkbox_disable_touchscreen{
{3 * 8, 2 * 16}, {3 * 8, 1 * 16},
20, 20,
"Disable touchscreen"}; "Disable touchscreen"};
Checkbox checkbox_speaker{
{3 * 8, 4 * 16},
20,
"Hide H1 Speaker option"};
Checkbox checkbox_bloff{ Checkbox checkbox_bloff{
{3 * 8, 6 * 16}, {3 * 8, 3 * 16},
20, 20,
"Backlight off after:"}; "Backlight off after:"};
OptionsField options_bloff{ OptionsField options_bloff{
{52, 7 * 16 + 8}, {60, 4 * 16 + 8},
20, 20,
{ {
{"5 seconds", backlight_timeout_t::Timeout5Sec}, {"5 seconds", backlight_timeout_t::Timeout5Sec},
@@ -236,25 +232,65 @@ class SetUIView : public View {
}}; }};
Checkbox checkbox_showsplash{ Checkbox checkbox_showsplash{
{3 * 8, 9 * 16}, {3 * 8, 6 * 16},
20, 20,
"Show splash"}; "Show splash"};
Checkbox checkbox_showclock{ Checkbox checkbox_showclock{
{3 * 8, 11 * 16}, {3 * 8, 8 * 16},
20, 20,
"Show clock with:"}; "Show clock with:"};
OptionsField options_clockformat{ OptionsField options_clockformat{
{52, 12 * 16 + 8}, {60, 9 * 16 + 8},
20, 20,
{{"time only", 0}, {{"time only", 0},
{"time and date", 1}}}; {"time and date", 1}}};
Checkbox checkbox_guireturnflag{ Checkbox checkbox_guireturnflag{
{3 * 8, 14 * 16}, {3 * 8, 11 * 16},
25, 20,
"add return icon in GUI"}; "Back button in menu"};
Labels labels{
{{3 * 8, 13 * 16}, "Show/Hide Status Icons", Color::light_grey()},
};
ImageToggle toggle_camera{
{7 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_camera};
ImageToggle toggle_sleep{
{9 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_sleep};
ImageToggle toggle_stealth{
{11 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_stealth};
ImageToggle toggle_converter{
{13 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_upconvert};
ImageToggle toggle_bias_tee{
{15 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_biast_off};
ImageToggle toggle_clock{
{17 * 8, 14 * 16 + 2, 8, 16},
&bitmap_icon_clk_ext};
ImageToggle toggle_mute{
{18 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_speaker_and_headphones_mute};
ImageToggle toggle_speaker{
{20 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_speaker_mute};
ImageToggle toggle_sd_card{
{22 * 8, 14 * 16 + 2, 16, 16},
&bitmap_sd_card_ok};
Button button_save{ Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32}, {2 * 8, 16 * 16, 12 * 8, 32},
@@ -470,7 +506,7 @@ class SetPersistentMemoryView : public View {
private: private:
Text text_pmem_about{ Text text_pmem_about{
{0, 1 * 16, 240, 16}, {0, 1 * 16, 240, 16},
"PersistentMemory from/to SD"}; "Persistent Memory from/to SD"};
Text text_pmem_informations{ Text text_pmem_informations{
{0, 2 * 16, 240, 16}, {0, 2 * 16, 240, 16},
+2 -4
View File
@@ -119,12 +119,10 @@ SIGFRXView::SIGFRXView(
.sampling_rate = 3072000, .sampling_rate = 3072000,
.decimation_factor = 4, .decimation_factor = 4,
}); });
receiver_model.set_baseband_bandwidth(1750000); // TODO: use settings.
receiver_model.set_tuning_frequency(868110000);
receiver_model.set_lna(0); receiver_model.set_lna(0);
receiver_model.set_vga(0); receiver_model.set_vga(0);
receiver_model.set_target_frequency(868110000);
add_children({&text_type, add_children({&text_type,
&text_channel, &text_channel,
+3
View File
@@ -32,6 +32,7 @@
#include "max2837.hpp" #include "max2837.hpp"
#include "volume.hpp" #include "volume.hpp"
#include "receiver_model.hpp" #include "receiver_model.hpp"
#include "radio_state.hpp"
namespace ui { namespace ui {
@@ -50,6 +51,8 @@ class SIGFRXView : public View {
uint8_t last_channel; uint8_t last_channel;
uint8_t detect_counter = 0; uint8_t detect_counter = 0;
RxRadioState radio_state_{};
const uint16_t sigfrx_marks[18] = { const uint16_t sigfrx_marks[18] = {
10, 8, 0, 10, 8, 0,
60, 52, 90, 60, 52, 90,
+2 -5
View File
@@ -54,9 +54,6 @@ void SigGenView::update_tone() {
} }
void SigGenView::start_tx() { void SigGenView::start_tx() {
transmitter_model.set_sampling_rate(1536000);
// transmitter_model.set_rf_amp(true);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable(); transmitter_model.enable();
update_tone(); update_tone();
@@ -116,9 +113,9 @@ SigGenView::SigGenView(
}; };
tx_view.on_edit_frequency = [this, &nav]() { tx_view.on_edit_frequency = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency()); auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) { new_view->on_changed = [this](rf::Frequency f) {
receiver_model.set_tuning_frequency(f); transmitter_model.set_target_frequency(f);
}; };
}; };
+9
View File
@@ -23,6 +23,8 @@
#ifndef __SIGGEN_H__ #ifndef __SIGGEN_H__
#define __SIGGEN_H__ #define __SIGGEN_H__
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui.hpp" #include "ui.hpp"
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
@@ -48,6 +50,13 @@ class SigGenView : public View {
void update_tone(); void update_tone();
void on_tx_progress(const uint32_t progress, const bool done); void on_tx_progress(const uint32_t progress, const bool done);
TxRadioState radio_state_{
1750000 /* bandwidth */,
1536000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_siggen", app_settings::Mode::TX};
const std::string shape_strings[7] = { const std::string shape_strings[7] = {
"CW-just carrier", "CW-just carrier",
"Sine signal ", "Sine signal ",

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