mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-08 08:53:48 +00:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c2279e297c | |||
| cdd524b9f3 | |||
| 99809c7919 | |||
| 2390d79111 | |||
| 3b41d73ddd | |||
| 3d46c28911 | |||
| ca7840557b | |||
| ceaa115025 | |||
| 8dbcefbc75 | |||
| b28b96fb4a | |||
| e15a8ed2d8 | |||
| 19b77bf03b | |||
| 7b1541d2e8 | |||
| 830fea63ed | |||
| 3d59de55f8 | |||
| 5c1f5a7d2e | |||
| 4e985420d4 | |||
| 3e584a9652 | |||
| 39acaf846a | |||
| 76948cffa8 | |||
| 4ea8abb53b | |||
| 32085f317b | |||
| faa4367295 | |||
| 3f8a4957af | |||
| b5ea81cf96 | |||
| e14fa5eab0 | |||
| 960ecf616c | |||
| 996ccb0e40 | |||
| 407fee23b9 | |||
| 199570d4a5 | |||
| 5cd9c377d2 | |||
| e08273b8b8 | |||
| 6b0f90c321 | |||
| 0dea488e98 | |||
| e60c4dc34b | |||
| 0c75713820 | |||
| 6a28987a36 | |||
| 7331979107 | |||
| f22808f8ca | |||
| e1c519d71e | |||
| 909f37f89b | |||
| 63b0093321 | |||
| fa06df1400 | |||
| f83027d451 | |||
| 7ebf3d3cdd | |||
| 5daa0dfbb1 | |||
| 7b669d7001 | |||
| ba2407d691 | |||
| 5743d3a3b9 | |||
| 7f1c0f6f7e | |||
| 650d299c99 | |||
| 3a5c2da66c | |||
| d29826e6f2 | |||
| 34fefd1cad | |||
| a5c7eb2fbc | |||
| 6298388fe1 | |||
| 19491ce3f7 | |||
| 3db2053c21 | |||
| ccd7bd6fc2 | |||
| fff78ce00f | |||
| 8bd3d6249d | |||
| f65e743c4c | |||
| 843880d22e | |||
| ec3cc7a859 | |||
| b1733cbd2a | |||
| 4ec2d64cb7 | |||
| d370dae170 | |||
| 72f0a1be54 | |||
| a2e5e03f07 | |||
| 47eda54d6a | |||
| db81b9b1e1 | |||
| b9de1918b4 | |||
| b87d26f271 |
@@ -1 +1 @@
|
||||
v1.7.1
|
||||
v1.7.2
|
||||
|
||||
@@ -1 +1 @@
|
||||
v1.7.2
|
||||
v1.7.3
|
||||
|
||||
@@ -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
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
enable_language(C CXX ASM)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
||||
project(application)
|
||||
|
||||
# Compiler options here.
|
||||
@@ -36,7 +38,13 @@ set(USE_OPT "-Os -g --specs=nano.specs")
|
||||
set(USE_COPT "-std=gnu99")
|
||||
|
||||
# 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
|
||||
set(USE_LINK_GC yes)
|
||||
@@ -167,6 +175,7 @@ set(CPPSRC
|
||||
#emu_cc1101.cpp
|
||||
rfm69.cpp
|
||||
event_m0.cpp
|
||||
file_reader.cpp
|
||||
file.cpp
|
||||
freqman.cpp
|
||||
io_file.cpp
|
||||
@@ -175,6 +184,7 @@ set(CPPSRC
|
||||
irq_lcd_frame.cpp
|
||||
irq_rtc.cpp
|
||||
log_file.cpp
|
||||
metadata_file.cpp
|
||||
portapack.cpp
|
||||
qrcodegen.cpp
|
||||
radio.cpp
|
||||
@@ -277,7 +287,7 @@ set(CPPSRC
|
||||
apps/ert_app.cpp
|
||||
apps/lge_app.cpp
|
||||
apps/pocsag_app.cpp
|
||||
apps/replay_app.cpp
|
||||
# apps/replay_app.cpp
|
||||
apps/ui_playlist.cpp
|
||||
apps/gps_sim_app.cpp
|
||||
apps/soundboard_app.cpp
|
||||
|
||||
@@ -26,86 +26,235 @@
|
||||
#include "file.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include <cstring>
|
||||
#include "utility.hpp"
|
||||
|
||||
#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) {
|
||||
if (portapack::persistent_memory::load_app_settings()) {
|
||||
file_path = folder + "/" + application + ".ini";
|
||||
namespace app_settings {
|
||||
|
||||
auto error = settings_file.open(file_path);
|
||||
if (!error.is_valid()) {
|
||||
auto error = settings_file.read(file_content, std::min((int)settings_file.size(), MAX_FILE_CONTENT_SIZE));
|
||||
template <typename T>
|
||||
static void read_setting(
|
||||
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=");
|
||||
settings->channel_bandwidth = std::app_settings::read_long_long(file_content, "channel_bandwidth=");
|
||||
settings->lna = std::app_settings::read_long_long(file_content, "lna=");
|
||||
settings->modulation = std::app_settings::read_long_long(file_content, "modulation=");
|
||||
settings->rx_amp = std::app_settings::read_long_long(file_content, "rx_amp=");
|
||||
settings->rx_frequency = std::app_settings::read_long_long(file_content, "rx_frequency=");
|
||||
settings->sampling_rate = std::app_settings::read_long_long(file_content, "sampling_rate=");
|
||||
settings->vga = std::app_settings::read_long_long(file_content, "vga=");
|
||||
settings->tx_amp = std::app_settings::read_long_long(file_content, "tx_amp=");
|
||||
settings->tx_frequency = std::app_settings::read_long_long(file_content, "tx_frequency=");
|
||||
settings->tx_gain = std::app_settings::read_long_long(file_content, "tx_gain=");
|
||||
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);
|
||||
if (pos != file_content.npos) {
|
||||
pos += setting_name.length();
|
||||
value = strtoll(&file_content[pos], 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
|
||||
|
||||
@@ -27,53 +27,96 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "file.hpp"
|
||||
#include "max283x.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
namespace std {
|
||||
class app_settings {
|
||||
namespace 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:
|
||||
#define SETTINGS_OK 0 // settings found
|
||||
#define SETTINGS_UNABLE_TO_LOAD -1 // settings (file) not found
|
||||
#define SETTINGS_UNABLE_TO_SAVE -2 // unable to save settings
|
||||
#define SETTINGS_DISABLED -3 // load/save settings disabled in settings
|
||||
SettingsManager(std::string app_name, Mode mode, Options options = Options::None);
|
||||
~SettingsManager();
|
||||
|
||||
struct AppSettings {
|
||||
uint32_t baseband_bandwidth;
|
||||
uint32_t channel_bandwidth;
|
||||
uint8_t lna;
|
||||
uint8_t modulation;
|
||||
uint8_t rx_amp;
|
||||
uint32_t rx_frequency;
|
||||
uint32_t sampling_rate;
|
||||
uint8_t tx_amp;
|
||||
uint32_t tx_frequency;
|
||||
uint8_t tx_gain;
|
||||
uint8_t vga;
|
||||
uint32_t step;
|
||||
uint8_t am_config_index;
|
||||
uint8_t nbfm_config_index;
|
||||
uint8_t wfm_config_index;
|
||||
uint8_t squelch;
|
||||
};
|
||||
SettingsManager(const SettingsManager&) = delete;
|
||||
SettingsManager& operator=(const SettingsManager&) = delete;
|
||||
SettingsManager(SettingsManager&&) = delete;
|
||||
SettingsManager& operator=(SettingsManager&&) = delete;
|
||||
|
||||
int load(std::string application, AppSettings* settings);
|
||||
int save(std::string application, AppSettings* settings);
|
||||
/* True if settings were successfully loaded from file. */
|
||||
bool loaded() const { return loaded_; }
|
||||
Mode mode() const { return settings_.mode; }
|
||||
|
||||
AppSettings& raw() { return settings_; }
|
||||
|
||||
private:
|
||||
#define MAX_FILE_CONTENT_SIZE 1000
|
||||
std::string app_name_;
|
||||
AppSettings settings_;
|
||||
bool loaded_;
|
||||
};
|
||||
|
||||
char file_content[MAX_FILE_CONTENT_SIZE] = {};
|
||||
std::string file_path = "";
|
||||
std::string folder = "SETTINGS";
|
||||
int rc = SETTINGS_OK;
|
||||
File settings_file{};
|
||||
long long int setting_value{};
|
||||
} // namespace app_settings
|
||||
|
||||
long long int read_long_long(char* file_content, const char* setting_text);
|
||||
|
||||
}; // class app_settings
|
||||
} // namespace std
|
||||
ENABLE_FLAGS_OPERATORS(app_settings::Mode);
|
||||
ENABLE_FLAGS_OPERATORS(app_settings::Options);
|
||||
|
||||
#endif /*__APP_SETTINGS_H__*/
|
||||
|
||||
@@ -55,12 +55,8 @@ void ACARSLogger::log_raw_data(const acars::Packet& packet, const uint32_t frequ
|
||||
|
||||
namespace ui {
|
||||
|
||||
void ACARSAppView::update_freq(rf::Frequency f) {
|
||||
set_target_frequency(f);
|
||||
portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ?
|
||||
}
|
||||
|
||||
ACARSAppView::ACARSAppView(NavigationView& nav) {
|
||||
ACARSAppView::ACARSAppView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_acars);
|
||||
|
||||
add_children({&rssi,
|
||||
@@ -72,25 +68,8 @@ ACARSAppView::ACARSAppView(NavigationView& nav) {
|
||||
&check_log,
|
||||
&console});
|
||||
|
||||
receiver_model.set_sampling_rate(2457600);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
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.on_select = [this](Checkbox&, bool v) {
|
||||
logging = v;
|
||||
@@ -132,16 +111,7 @@ void ACARSAppView::on_packet(const acars::Packet& packet) {
|
||||
|
||||
// Log raw data whatever it contains
|
||||
if (logger && logging)
|
||||
logger->log_raw_data(packet, 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_;
|
||||
logger->log_raw_data(packet, receiver_model.target_frequency());
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -23,10 +23,12 @@
|
||||
#ifndef __ACARS_APP_H__
|
||||
#define __ACARS_APP_H__
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_rssi.hpp"
|
||||
|
||||
#include "log_file.hpp"
|
||||
|
||||
#include "acars_packet.hpp"
|
||||
@@ -56,6 +58,14 @@ class ACARSAppView : public View {
|
||||
std::string title() const override { return "ACARS (WIP)"; };
|
||||
|
||||
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};
|
||||
uint32_t packet_counter{0};
|
||||
|
||||
@@ -66,15 +76,13 @@ class ACARSAppView : public View {
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, 0 * 16}};
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
Channel channel{
|
||||
{21 * 8, 5, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 5, 6 * 8, 4}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
RxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 8},
|
||||
};
|
||||
nav_};
|
||||
Checkbox check_log{
|
||||
{22 * 8, 21},
|
||||
3,
|
||||
@@ -86,15 +94,8 @@ class ACARSAppView : public View {
|
||||
|
||||
std::unique_ptr<ACARSLogger> logger{};
|
||||
|
||||
uint32_t target_frequency_{};
|
||||
|
||||
void update_freq(rf::Frequency f);
|
||||
|
||||
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{
|
||||
Message::ID::ACARSPacket,
|
||||
[this](Message* const p) {
|
||||
|
||||
@@ -283,8 +283,8 @@ AISRecentEntryDetailView::AISRecentEntryDetailView(NavigationView& nav) {
|
||||
});
|
||||
|
||||
button_done.on_select = [this](const ui::Button&) {
|
||||
if (this->on_close) {
|
||||
this->on_close();
|
||||
if (on_close) {
|
||||
on_close();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -380,33 +380,23 @@ AISAppView::AISAppView(NavigationView& nav)
|
||||
&recent_entry_detail_view,
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_ais", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
target_frequency_ = app_settings.rx_frequency;
|
||||
} else
|
||||
target_frequency_ = initial_target_frequency;
|
||||
|
||||
recent_entry_detail_view.hidden(true);
|
||||
|
||||
receiver_model.set_tuning_frequency(tuning_frequency());
|
||||
receiver_model.set_sampling_rate(sampling_rate);
|
||||
receiver_model.set_baseband_bandwidth(baseband_bandwidth);
|
||||
if (!settings_.loaded())
|
||||
receiver_model.set_target_frequency(initial_target_frequency);
|
||||
|
||||
receiver_model.enable();
|
||||
|
||||
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) {
|
||||
this->on_show_detail(entry);
|
||||
on_show_detail(entry);
|
||||
};
|
||||
recent_entry_detail_view.on_close = [this]() {
|
||||
this->on_show_list();
|
||||
on_show_list();
|
||||
};
|
||||
|
||||
logger = std::make_unique<AISLogger>();
|
||||
@@ -416,12 +406,7 @@ AISAppView::AISAppView(NavigationView& nav)
|
||||
}
|
||||
|
||||
AISAppView::~AISAppView() {
|
||||
// save app settings
|
||||
app_settings.rx_frequency = target_frequency_;
|
||||
settings.save("rx_ais", &app_settings);
|
||||
|
||||
receiver_model.disable();
|
||||
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
@@ -465,21 +450,4 @@ void AISAppView::on_show_detail(const AISRecentEntry& entry) {
|
||||
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 */
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include "log_file.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ais_packet.hpp"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
@@ -154,9 +155,6 @@ class AISAppView : public View {
|
||||
~AISAppView();
|
||||
|
||||
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 focus() override;
|
||||
@@ -165,12 +163,13 @@ class AISAppView : public View {
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 162025000;
|
||||
static constexpr uint32_t sampling_rate = 2457600;
|
||||
static constexpr uint32_t baseband_bandwidth = 1750000;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
RxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
2457600 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_ais", app_settings::Mode::RX};
|
||||
|
||||
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_show_list();
|
||||
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 */
|
||||
|
||||
@@ -22,21 +22,19 @@
|
||||
|
||||
#include "analog_audio_app.hpp"
|
||||
|
||||
#include "audio.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
#include "freqman.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
using namespace portapack;
|
||||
using namespace tonekey;
|
||||
|
||||
#include "audio.hpp"
|
||||
#include "file.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "debug.hpp"
|
||||
|
||||
#include "freqman.hpp"
|
||||
using namespace portapack;
|
||||
using namespace tonekey;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -45,8 +43,8 @@ namespace ui {
|
||||
static const Style& style_options_group = Styles::bg_blue;
|
||||
|
||||
AMOptionsView::AMOptionsView(
|
||||
const Rect parent_rect,
|
||||
const Style* const style)
|
||||
Rect parent_rect,
|
||||
const Style* style)
|
||||
: View{parent_rect} {
|
||||
set_style(style);
|
||||
|
||||
@@ -65,8 +63,8 @@ AMOptionsView::AMOptionsView(
|
||||
/* NBFMOptionsView *******************************************************/
|
||||
|
||||
NBFMOptionsView::NBFMOptionsView(
|
||||
const Rect parent_rect,
|
||||
const Style* const style)
|
||||
Rect parent_rect,
|
||||
const Style* style)
|
||||
: View{parent_rect} {
|
||||
set_style(style);
|
||||
|
||||
@@ -90,8 +88,8 @@ NBFMOptionsView::NBFMOptionsView(
|
||||
/* WFMOptionsView *******************************************************/
|
||||
|
||||
WFMOptionsView::WFMOptionsView(
|
||||
const Rect parent_rect,
|
||||
const Style* const style)
|
||||
Rect parent_rect,
|
||||
const Style* style)
|
||||
: View{parent_rect} {
|
||||
set_style(style);
|
||||
|
||||
@@ -111,8 +109,8 @@ WFMOptionsView::WFMOptionsView(
|
||||
|
||||
SPECOptionsView::SPECOptionsView(
|
||||
AnalogAudioView* view,
|
||||
const Rect parent_rect,
|
||||
const Style* const style)
|
||||
Rect parent_rect,
|
||||
const Style* style)
|
||||
: View{parent_rect} {
|
||||
set_style(style);
|
||||
|
||||
@@ -137,6 +135,10 @@ SPECOptionsView::SPECOptionsView(
|
||||
AnalogAudioView::AnalogAudioView(
|
||||
NavigationView& 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,
|
||||
&channel,
|
||||
&audio,
|
||||
@@ -149,35 +151,9 @@ AnalogAudioView::AnalogAudioView(
|
||||
&record_view,
|
||||
&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
|
||||
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]() {
|
||||
this->on_show_options_frequency();
|
||||
};
|
||||
@@ -190,9 +166,12 @@ AnalogAudioView::AnalogAudioView(
|
||||
this->on_show_options_rf_gain();
|
||||
};
|
||||
|
||||
const auto modulation = receiver_model.modulation();
|
||||
options_modulation.set_by_value(toUType(modulation));
|
||||
auto modulation = receiver_model.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) {
|
||||
this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v));
|
||||
};
|
||||
@@ -205,13 +184,14 @@ AnalogAudioView::AnalogAudioView(
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
|
||||
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation));
|
||||
// This call starts the correct baseband image to run
|
||||
// and sets the radio up as necessary for the given modulation.
|
||||
on_modulation_changed(modulation);
|
||||
}
|
||||
|
||||
size_t AnalogAudioView::get_spec_bw_index() {
|
||||
@@ -238,40 +218,15 @@ void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
|
||||
}
|
||||
|
||||
AnalogAudioView::~AnalogAudioView() {
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
app_settings.lna = receiver_model.lna();
|
||||
app_settings.vga = receiver_model.vga();
|
||||
app_settings.rx_amp = receiver_model.rf_amp();
|
||||
app_settings.step = receiver_model.frequency_step();
|
||||
app_settings.modulation = (uint8_t)receiver_model.modulation();
|
||||
app_settings.am_config_index = receiver_model.am_configuration();
|
||||
app_settings.nbfm_config_index = receiver_model.nbfm_configuration();
|
||||
app_settings.wfm_config_index = receiver_model.wfm_configuration();
|
||||
app_settings.squelch = receiver_model.squelch_level();
|
||||
settings.save("rx_audio", &app_settings);
|
||||
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
// both?
|
||||
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();
|
||||
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_hide() {
|
||||
// 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) {
|
||||
void AnalogAudioView::set_parent_rect(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);
|
||||
}
|
||||
|
||||
@@ -279,21 +234,19 @@ void AnalogAudioView::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) {
|
||||
receiver_model.set_baseband_bandwidth(bandwidth_hz);
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_modulation_changed(const ReceiverModel::Mode modulation) {
|
||||
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
|
||||
// it's being shown or hidden.
|
||||
waterfall.on_hide();
|
||||
void AnalogAudioView::on_modulation_changed(ReceiverModel::Mode modulation) {
|
||||
// This app doesn't know what to do with "Capture" mode.
|
||||
if (modulation > ReceiverModel::Mode::SpectrumAnalysis)
|
||||
modulation = ReceiverModel::Mode::SpectrumAnalysis;
|
||||
|
||||
baseband::spectrum_streaming_stop();
|
||||
update_modulation(modulation);
|
||||
on_show_options_modulation();
|
||||
waterfall.on_show();
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void AnalogAudioView::remove_options_widget() {
|
||||
@@ -345,7 +298,7 @@ void AnalogAudioView::on_show_options_rf_gain() {
|
||||
void AnalogAudioView::on_show_options_modulation() {
|
||||
std::unique_ptr<Widget> widget;
|
||||
|
||||
const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation());
|
||||
const auto modulation = receiver_model.modulation();
|
||||
switch (modulation) {
|
||||
case ReceiverModel::Mode::AMAudio:
|
||||
widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group);
|
||||
@@ -372,6 +325,7 @@ void AnalogAudioView::on_show_options_modulation() {
|
||||
break;
|
||||
|
||||
default:
|
||||
chDbgPanic("Unhandled Mode");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -388,7 +342,7 @@ void AnalogAudioView::on_reference_ppm_correction_changed(int32_t v) {
|
||||
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();
|
||||
record_view.stop();
|
||||
|
||||
@@ -409,7 +363,8 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
|
||||
image_tag = portapack::spi_flash::image_tag_wideband_spectrum;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
chDbgPanic("Unhandled Mode");
|
||||
break;
|
||||
}
|
||||
|
||||
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) {
|
||||
float diff, min_diff = value;
|
||||
size_t min_idx{0};
|
||||
size_t c;
|
||||
|
||||
// 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);
|
||||
void AnalogAudioView::handle_coded_squelch(uint32_t value) {
|
||||
tone_index idx = tone_key_index_by_value(value);
|
||||
if (idx >= 0)
|
||||
text_ctcss.set("CTCSS " + tone_key_string(idx));
|
||||
else
|
||||
text_ctcss.set("???");
|
||||
}
|
||||
|
||||
@@ -26,17 +26,19 @@
|
||||
#include "receiver_model.hpp"
|
||||
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "tone_key.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AMOptionsView : public View {
|
||||
public:
|
||||
AMOptionsView(const Rect parent_rect, const Style* const style);
|
||||
AMOptionsView(Rect parent_rect, const Style* style);
|
||||
|
||||
private:
|
||||
Text label_config{
|
||||
@@ -54,7 +56,7 @@ class AMOptionsView : public View {
|
||||
|
||||
class NBFMOptionsView : public View {
|
||||
public:
|
||||
NBFMOptionsView(const Rect parent_rect, const Style* const style);
|
||||
NBFMOptionsView(Rect parent_rect, const Style* style);
|
||||
|
||||
private:
|
||||
Text label_config{
|
||||
@@ -82,7 +84,7 @@ class NBFMOptionsView : public View {
|
||||
|
||||
class WFMOptionsView : public View {
|
||||
public:
|
||||
WFMOptionsView(const Rect parent_rect, const Style* const style);
|
||||
WFMOptionsView(Rect parent_rect, const Style* style);
|
||||
|
||||
private:
|
||||
Text label_config{
|
||||
@@ -101,7 +103,7 @@ class AnalogAudioView;
|
||||
|
||||
class SPECOptionsView : public View {
|
||||
public:
|
||||
SPECOptionsView(AnalogAudioView* view, const Rect parent_rect, const Style* const style);
|
||||
SPECOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
|
||||
|
||||
private:
|
||||
Text label_config{
|
||||
@@ -138,10 +140,7 @@ class AnalogAudioView : public View {
|
||||
AnalogAudioView(NavigationView& nav);
|
||||
~AnalogAudioView();
|
||||
|
||||
void on_hide() override;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void set_parent_rect(Rect new_parent_rect) override;
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Audio RX"; };
|
||||
@@ -155,9 +154,11 @@ class AnalogAudioView : public View {
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
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 nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16};
|
||||
@@ -166,24 +167,18 @@ class AnalogAudioView : public View {
|
||||
uint32_t spec_bw = 20000000;
|
||||
uint16_t spec_trigger = 63;
|
||||
|
||||
NavigationView& nav_;
|
||||
// bool exit_on_squelch { false };
|
||||
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
|
||||
Channel channel{
|
||||
{21 * 8, 5, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 5, 6 * 8, 4}};
|
||||
|
||||
Audio audio{
|
||||
{21 * 8, 10, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 10, 6 * 8, 4}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
RxFrequencyField field_frequency{
|
||||
{5 * 8, 0 * 16},
|
||||
};
|
||||
nav_};
|
||||
|
||||
LNAGainField field_lna{
|
||||
{15 * 8, 0 * 16}};
|
||||
@@ -218,37 +213,26 @@ class AnalogAudioView : public View {
|
||||
4096,
|
||||
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_modulation_changed(const ReceiverModel::Mode modulation);
|
||||
void on_modulation_changed(ReceiverModel::Mode modulation);
|
||||
void on_show_options_frequency();
|
||||
void on_show_options_rf_gain();
|
||||
void on_show_options_modulation();
|
||||
void on_frequency_step_changed(rf::Frequency f);
|
||||
void on_reference_ppm_correction_changed(int32_t v);
|
||||
void on_edit_frequency();
|
||||
|
||||
void remove_options_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(const uint32_t value);
|
||||
|
||||
/*MessageHandlerRegistration message_handler_squelch_signal {
|
||||
Message::ID::RequestSignal,
|
||||
[this](const Message* const p) {
|
||||
(void)p;
|
||||
this->squelched();
|
||||
}
|
||||
};*/
|
||||
void handle_coded_squelch(uint32_t value);
|
||||
|
||||
MessageHandlerRegistration message_handler_coded_squelch{
|
||||
Message::ID::CodedSquelch,
|
||||
[this](const Message* const p) {
|
||||
[this](const Message* p) {
|
||||
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
|
||||
this->handle_coded_squelch(message.value);
|
||||
}};
|
||||
|
||||
@@ -56,31 +56,6 @@ AnalogTvView::AnalogTvView(
|
||||
&field_volume,
|
||||
&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]() {
|
||||
this->on_show_options_frequency();
|
||||
};
|
||||
@@ -103,7 +78,7 @@ AnalogTvView::AnalogTvView(
|
||||
};
|
||||
|
||||
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));
|
||||
@@ -111,12 +86,6 @@ AnalogTvView::AnalogTvView(
|
||||
}
|
||||
|
||||
AnalogTvView::~AnalogTvView() {
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_tv", &app_settings);
|
||||
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
// both?
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
@@ -140,10 +109,6 @@ void AnalogTvView::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) {
|
||||
receiver_model.set_baseband_bandwidth(bandwidth_hz);
|
||||
}
|
||||
|
||||
@@ -27,10 +27,12 @@
|
||||
#include "receiver_model.hpp"
|
||||
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_tv.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
#include "tone_key.hpp"
|
||||
|
||||
@@ -52,30 +54,26 @@ class AnalogTvView : public View {
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
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 nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16};
|
||||
|
||||
NavigationView& nav_;
|
||||
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
|
||||
Channel channel{
|
||||
{21 * 8, 5, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 5, 6 * 8, 4}};
|
||||
|
||||
Audio audio{
|
||||
{21 * 8, 10, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 10, 6 * 8, 4}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
RxFrequencyField field_frequency{
|
||||
{5 * 8, 0 * 16},
|
||||
};
|
||||
nav_};
|
||||
|
||||
LNAGainField field_lna{
|
||||
{15 * 8, 0 * 16}};
|
||||
@@ -99,7 +97,6 @@ class AnalogTvView : public View {
|
||||
|
||||
tv::TVWidget tv{};
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
|
||||
void on_modulation_changed(const ReceiverModel::Mode modulation);
|
||||
void on_show_options_frequency();
|
||||
@@ -107,7 +104,6 @@ class AnalogTvView : public View {
|
||||
void on_show_options_modulation();
|
||||
void on_frequency_step_changed(rf::Frequency f);
|
||||
void on_reference_ppm_correction_changed(int32_t v);
|
||||
void on_edit_frequency();
|
||||
|
||||
void remove_options_widget();
|
||||
void set_options_widget(std::unique_ptr<Widget> new_widget);
|
||||
|
||||
@@ -21,15 +21,16 @@
|
||||
*/
|
||||
|
||||
#include "capture_app.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
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);
|
||||
|
||||
add_children({
|
||||
@@ -46,26 +47,6 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
|
||||
&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.on_change = [this](size_t, OptionsField::value_t 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) {
|
||||
sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side
|
||||
/* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
|
||||
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/
|
||||
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
|
||||
|
||||
/* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
|
||||
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz, when selected 1 Mhz BW ... */
|
||||
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
|
||||
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);
|
||||
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);
|
||||
|
||||
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.enable();
|
||||
@@ -133,20 +81,13 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
|
||||
}
|
||||
|
||||
CaptureAppView::~CaptureAppView() {
|
||||
// Hack for preventing halting other apps
|
||||
// TODO: This should be also part of something global
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
// Most other apps can't handle "Capture" mode, set to something standard.
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
||||
// ----------------------------
|
||||
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void CaptureAppView::on_hide() {
|
||||
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
|
||||
// it's being shown or hidden.
|
||||
waterfall.on_hide();
|
||||
View::on_hide();
|
||||
}
|
||||
@@ -154,7 +95,7 @@ void CaptureAppView::on_hide() {
|
||||
void CaptureAppView::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};
|
||||
ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
@@ -162,8 +103,4 @@ void CaptureAppView::focus() {
|
||||
record_view.focus();
|
||||
}
|
||||
|
||||
void CaptureAppView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -26,25 +26,12 @@
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
|
||||
#define BW_OPTIONS \
|
||||
{" 8k5", 8500}, \
|
||||
{" 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)
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "freqman.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -54,40 +41,38 @@ class CaptureAppView : public View {
|
||||
~CaptureAppView();
|
||||
|
||||
void on_hide() override;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void focus() override;
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
std::string title() const override { return "Capture"; };
|
||||
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
uint32_t sampling_rate = 0;
|
||||
uint32_t anti_alias_baseband_bandwidth_filter = 2500000; // we rename the previous var , and change type static constexpr to normal var.
|
||||
NavigationView& nav_;
|
||||
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{
|
||||
{{0 * 8, 1 * 16}, "Rate:", Color::light_grey()},
|
||||
};
|
||||
|
||||
RSSI rssi{
|
||||
{24 * 8, 0, 6 * 8, 4},
|
||||
};
|
||||
{24 * 8, 0, 6 * 8, 4}};
|
||||
|
||||
Channel channel{
|
||||
{24 * 8, 5, 6 * 8, 4},
|
||||
};
|
||||
{24 * 8, 5, 6 * 8, 4}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
RxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
};
|
||||
nav_};
|
||||
|
||||
FrequencyStepView field_frequency_step{
|
||||
{10 * 8, 0 * 16},
|
||||
};
|
||||
{10 * 8, 0 * 16}};
|
||||
|
||||
RFAmpField field_rf_amp{
|
||||
{16 * 8, 0 * 16}};
|
||||
@@ -101,7 +86,7 @@ class CaptureAppView : public View {
|
||||
OptionsField option_bandwidth{
|
||||
{5 * 8, 1 * 16},
|
||||
5,
|
||||
{BW_OPTIONS}};
|
||||
{}};
|
||||
|
||||
RecordView record_view{
|
||||
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
|
||||
@@ -111,7 +96,7 @@ class CaptureAppView : public View {
|
||||
16384,
|
||||
3};
|
||||
|
||||
spectrum::WaterfallWidget waterfall{};
|
||||
spectrum::WaterfallView waterfall{};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -110,17 +110,9 @@ ERTAppView::ERTAppView(NavigationView&) {
|
||||
&recent_entries_view,
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_ert", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
if (!settings_.loaded())
|
||||
receiver_model.set_target_frequency(initial_target_frequency);
|
||||
|
||||
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();
|
||||
|
||||
logger = std::make_unique<ERTLogger>();
|
||||
@@ -130,11 +122,7 @@ ERTAppView::ERTAppView(NavigationView&) {
|
||||
}
|
||||
|
||||
ERTAppView::~ERTAppView() {
|
||||
// save app settings
|
||||
settings.save("rx_ert", &app_settings);
|
||||
|
||||
receiver_model.disable();
|
||||
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "event_m0.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "log_file.hpp"
|
||||
|
||||
#include "ert_packet.hpp"
|
||||
@@ -108,10 +109,6 @@ using ERTRecentEntriesView = RecentEntriesView<ERTRecentEntries>;
|
||||
|
||||
class ERTAppView : public View {
|
||||
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();
|
||||
|
||||
@@ -126,12 +123,17 @@ class ERTAppView : public View {
|
||||
std::string title() const override { return "ERT RX"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 911600000;
|
||||
|
||||
ERTRecentEntries recent{};
|
||||
std::unique_ptr<ERTLogger> logger{};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
RxRadioState radio_state_{
|
||||
2500000 /* bandwidth */,
|
||||
4194304 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_ert", app_settings::Mode::RX};
|
||||
|
||||
const RecentEntriesColumns columns{{
|
||||
{"ID", 10},
|
||||
|
||||
@@ -26,13 +26,15 @@
|
||||
|
||||
#include "ui_fileman.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "metadata_file.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -40,51 +42,38 @@ void GpsSimAppView::set_ready() {
|
||||
ready_signal = true;
|
||||
}
|
||||
|
||||
void GpsSimAppView::on_file_changed(std::filesystem::path new_file_path) {
|
||||
File data_file, info_file;
|
||||
char file_data[257];
|
||||
void GpsSimAppView::on_file_changed(const fs::path& new_file_path) {
|
||||
file_path = fs::path(u"/") + new_file_path;
|
||||
File::Size file_size{};
|
||||
|
||||
// Get file size
|
||||
auto data_open_error = data_file.open("/" + new_file_path.string());
|
||||
if (data_open_error.is_valid()) {
|
||||
file_error();
|
||||
return;
|
||||
}
|
||||
|
||||
file_path = new_file_path;
|
||||
|
||||
// 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);
|
||||
}
|
||||
{ // Get the size of the data file.
|
||||
File data_file;
|
||||
auto error = data_file.open(file_path);
|
||||
if (error) {
|
||||
file_error();
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
auto file_size = data_file.size();
|
||||
auto duration = (file_size * 1000) / (1 * 2 * sample_rate);
|
||||
|
||||
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));
|
||||
|
||||
button_play.focus();
|
||||
@@ -129,7 +118,6 @@ void GpsSimAppView::start() {
|
||||
|
||||
if (reader) {
|
||||
button_play.set_bitmap(&bitmap_stop);
|
||||
baseband::set_sample_rate(sample_rate);
|
||||
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
@@ -142,7 +130,6 @@ void GpsSimAppView::start() {
|
||||
}
|
||||
|
||||
transmitter_model.set_sampling_rate(sample_rate);
|
||||
transmitter_model.set_baseband_bandwidth(baseband_bandwidth);
|
||||
transmitter_model.enable();
|
||||
}
|
||||
|
||||
@@ -183,26 +170,12 @@ GpsSimAppView::GpsSimAppView(
|
||||
&text_duration,
|
||||
&progressbar,
|
||||
&field_frequency,
|
||||
&tx_view, // now it handles previous rfgain , rfamp.
|
||||
&tx_view, // now it handles previous rfgain, rfamp.
|
||||
&check_loop,
|
||||
&button_play,
|
||||
&waterfall,
|
||||
});
|
||||
|
||||
field_frequency.set_value(target_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_frequency.set_step(5000);
|
||||
|
||||
button_play.on_select = [this](ImageButton&) {
|
||||
@@ -219,8 +192,7 @@ GpsSimAppView::GpsSimAppView(
|
||||
|
||||
GpsSimAppView::~GpsSimAppView() {
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void GpsSimAppView::on_hide() {
|
||||
@@ -239,17 +211,4 @@ void GpsSimAppView::set_parent_rect(const Rect new_parent_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 */
|
||||
|
||||
@@ -27,15 +27,18 @@
|
||||
#define SHORT_UI true
|
||||
#define NORMAL_UI false
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "replay_thread.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -52,6 +55,12 @@ class GpsSimAppView : public View {
|
||||
|
||||
private:
|
||||
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;
|
||||
|
||||
@@ -62,13 +71,9 @@ class GpsSimAppView : public View {
|
||||
const size_t read_size{16384};
|
||||
const size_t buffer_count{3};
|
||||
|
||||
void on_file_changed(std::filesystem::path new_file_path);
|
||||
void on_target_frequency_changed(rf::Frequency f);
|
||||
void on_file_changed(const std::filesystem::path& new_file_path);
|
||||
void on_tx_progress(const uint32_t progress);
|
||||
|
||||
void set_target_frequency(const rf::Frequency new_value);
|
||||
rf::Frequency target_frequency() const;
|
||||
|
||||
void toggle();
|
||||
void start();
|
||||
void stop(const bool do_loop);
|
||||
@@ -98,9 +103,9 @@ class GpsSimAppView : public View {
|
||||
ProgressBar progressbar{
|
||||
{18 * 8, 1 * 16, 12 * 8, 16}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
TxFrequencyField field_frequency{
|
||||
{0 * 8, 2 * 16},
|
||||
};
|
||||
nav_};
|
||||
|
||||
TransmitterView2 tx_view{
|
||||
// new handling of NumberField field_rfgain, NumberField field_rfamp
|
||||
@@ -119,7 +124,7 @@ class GpsSimAppView : public View {
|
||||
Color::green(),
|
||||
Color::black()};
|
||||
|
||||
spectrum::WaterfallWidget waterfall{};
|
||||
spectrum::WaterfallView waterfall{};
|
||||
|
||||
MessageHandlerRegistration message_handler_replay_thread_error{
|
||||
Message::ID::ReplayThreadDone,
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "lge_app.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
@@ -44,13 +43,8 @@ void LGEView::focus() {
|
||||
}
|
||||
|
||||
LGEView::~LGEView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_lge", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
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() {
|
||||
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.set_dirty();
|
||||
}
|
||||
transmitter_model.set_sampling_rate(2280000);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
|
||||
transmitter_model.enable();
|
||||
|
||||
chThdSleep(100);
|
||||
@@ -306,15 +299,6 @@ LGEView::LGEView(NavigationView& nav) {
|
||||
&console,
|
||||
&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_team.set_value(1);
|
||||
field_player.set_value(1);
|
||||
@@ -333,9 +317,9 @@ LGEView::LGEView(NavigationView& 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) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "transmitter_model.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -49,9 +50,12 @@ class LGEView : public View {
|
||||
ALL
|
||||
};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
2280000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_lge", app_settings::Mode::TX};
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
|
||||
@@ -51,14 +51,8 @@ void POCSAGLogger::log_decoded(
|
||||
|
||||
namespace ui {
|
||||
|
||||
void POCSAGAppView::update_freq(rf::Frequency f) {
|
||||
set_target_frequency(f);
|
||||
portapack::persistent_memory::set_tuned_frequency(f); // Maybe not ?
|
||||
}
|
||||
|
||||
POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
uint32_t ignore_address;
|
||||
|
||||
POCSAGAppView::POCSAGAppView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_pocsag);
|
||||
|
||||
add_children({&rssi,
|
||||
@@ -74,44 +68,17 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
&sym_ignore,
|
||||
&console});
|
||||
|
||||
// Set on_change before initialising the field
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
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());
|
||||
}
|
||||
if (!settings_.loaded())
|
||||
field_frequency.set_value(initial_target_frequency);
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.enable();
|
||||
|
||||
field_frequency.set_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) {
|
||||
update_freq(f);
|
||||
field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
// TODO app setting instead?
|
||||
// TODO: app setting instead?
|
||||
uint32_t 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++) {
|
||||
sym_ignore.set_sym(6 - c, ignore_address % 10);
|
||||
ignore_address /= 10;
|
||||
@@ -122,16 +89,15 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
|
||||
logger->append(LOG_ROOT_DIR "/POCSAG.TXT");
|
||||
|
||||
audio::output::start();
|
||||
audio::output::unmute();
|
||||
|
||||
baseband::set_pocsag();
|
||||
}
|
||||
|
||||
POCSAGAppView::~POCSAGAppView() {
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_pocsag", &app_settings);
|
||||
void POCSAGAppView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
POCSAGAppView::~POCSAGAppView() {
|
||||
audio::output::stop();
|
||||
|
||||
// Save ignored address
|
||||
@@ -177,9 +143,10 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
|
||||
console.write(console_info);
|
||||
|
||||
if (logger && logging()) {
|
||||
logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) +
|
||||
" F" + to_string_dec_uint(pocsag_state.function) +
|
||||
" Address only");
|
||||
logger->log_decoded(
|
||||
message->packet, to_string_dec_uint(pocsag_state.address) +
|
||||
" F" + to_string_dec_uint(pocsag_state.function) +
|
||||
" Address only");
|
||||
}
|
||||
|
||||
last_address = pocsag_state.address;
|
||||
@@ -196,25 +163,17 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
|
||||
}
|
||||
|
||||
if (logger && logging())
|
||||
logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) +
|
||||
" F" + to_string_dec_uint(pocsag_state.function) +
|
||||
" Alpha: " + pocsag_state.output);
|
||||
logger->log_decoded(
|
||||
message->packet, to_string_dec_uint(pocsag_state.address) +
|
||||
" F" + to_string_dec_uint(pocsag_state.function) +
|
||||
" Alpha: " + pocsag_state.output);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: make setting.
|
||||
// Log raw data whatever it contains
|
||||
if (logger && logging())
|
||||
logger->log_raw_data(message->packet, 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_;
|
||||
/*if (logger && logging())
|
||||
logger->log_raw_data(message->packet, receiver_model.target_frequency());*/
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -24,11 +24,13 @@
|
||||
#define __POCSAG_APP_H__
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_rssi.hpp"
|
||||
|
||||
#include "log_file.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "pocsag.hpp"
|
||||
#include "pocsag_packet.hpp"
|
||||
|
||||
@@ -53,16 +55,17 @@ class POCSAGAppView : public View {
|
||||
~POCSAGAppView();
|
||||
|
||||
std::string title() const override { return "POCSAG RX"; };
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 466175000;
|
||||
|
||||
bool logging() const { return check_log.value(); };
|
||||
bool ignore() const { return check_ignore.value(); };
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_pocsag", app_settings::Mode::RX};
|
||||
|
||||
uint32_t last_address = 0xFFFFFFFF;
|
||||
pocsag::POCSAGState pocsag_state{};
|
||||
@@ -74,18 +77,15 @@ class POCSAGAppView : public View {
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, 0 * 16}};
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
Channel channel{
|
||||
{21 * 8, 5, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 5, 6 * 8, 4}};
|
||||
Audio audio{
|
||||
{21 * 8, 10, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 10, 6 * 8, 4}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
RxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 8},
|
||||
};
|
||||
nav_};
|
||||
|
||||
AudioVolumeField field_volume{
|
||||
{28 * 8, 0 * 16}};
|
||||
@@ -111,15 +111,8 @@ class POCSAGAppView : public View {
|
||||
|
||||
std::unique_ptr<POCSAGLogger> logger{};
|
||||
|
||||
uint32_t target_frequency_ = initial_target_frequency;
|
||||
|
||||
void update_freq(rf::Frequency f);
|
||||
|
||||
void on_packet(const POCSAGPacketMessage* message);
|
||||
|
||||
uint32_t target_frequency() const;
|
||||
void set_target_frequency(const uint32_t new_value);
|
||||
|
||||
MessageHandlerRegistration message_handler_packet{
|
||||
Message::ID::POCSAGPacket,
|
||||
[this](Message* const p) {
|
||||
|
||||
@@ -26,12 +26,14 @@
|
||||
|
||||
#include "ui_fileman.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "metadata_file.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -39,51 +41,41 @@ void ReplayAppView::set_ready() {
|
||||
ready_signal = true;
|
||||
}
|
||||
|
||||
void ReplayAppView::on_file_changed(std::filesystem::path new_file_path) {
|
||||
File data_file, info_file;
|
||||
char file_data[257];
|
||||
void ReplayAppView::on_file_changed(const fs::path& new_file_path) {
|
||||
file_path = fs::path(u"/") + new_file_path;
|
||||
File::Size file_size{};
|
||||
|
||||
// Get file size
|
||||
auto data_open_error = data_file.open("/" + new_file_path.string());
|
||||
if (data_open_error.is_valid()) {
|
||||
file_error();
|
||||
return;
|
||||
}
|
||||
|
||||
file_path = new_file_path;
|
||||
|
||||
// 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);
|
||||
}
|
||||
{ // Get the size of the data file.
|
||||
File data_file;
|
||||
auto error = data_file.open(file_path);
|
||||
if (error) {
|
||||
file_error();
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
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_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));
|
||||
|
||||
button_play.focus();
|
||||
@@ -188,35 +180,19 @@ ReplayAppView::ReplayAppView(
|
||||
&text_duration,
|
||||
&progressbar,
|
||||
&field_frequency,
|
||||
&tx_view, // now it handles previous rfgain , rfamp.
|
||||
&tx_view, // now it handles previous rfgain, rfamp.
|
||||
&check_loop,
|
||||
&button_play,
|
||||
&waterfall,
|
||||
});
|
||||
|
||||
field_frequency.set_value(target_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_frequency.set_step(5000);
|
||||
|
||||
button_play.on_select = [this](ImageButton&) {
|
||||
this->toggle();
|
||||
};
|
||||
|
||||
button_open.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".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);
|
||||
};
|
||||
};
|
||||
@@ -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.
|
||||
chThdSleepMilliseconds(40); // (that happened sometimes if we interrupt the waterfall play at the beggining of the play around 25% and exit )
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX reception ok, without "ghost interference signal problem" at the exit .
|
||||
|
||||
baseband::shutdown(); // better this function at the end, after load_sram(). If not , sometimes produced hang up (now not , it is ok).
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void ReplayAppView::on_hide() {
|
||||
@@ -247,17 +221,4 @@ void ReplayAppView::set_parent_rect(const Rect new_parent_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 */
|
||||
|
||||
@@ -26,15 +26,18 @@
|
||||
#define SHORT_UI true
|
||||
#define NORMAL_UI false
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "replay_thread.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -51,6 +54,9 @@ class ReplayAppView : public View {
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
TxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_replay", app_settings::Mode::TX};
|
||||
|
||||
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 buffer_count{3};
|
||||
|
||||
void on_file_changed(std::filesystem::path new_file_path);
|
||||
void on_target_frequency_changed(rf::Frequency f);
|
||||
void on_file_changed(const std::filesystem::path& new_file_path);
|
||||
void on_tx_progress(const uint32_t progress);
|
||||
|
||||
void set_target_frequency(const rf::Frequency new_value);
|
||||
rf::Frequency target_frequency() const;
|
||||
|
||||
void toggle();
|
||||
void start();
|
||||
void stop(const bool do_loop);
|
||||
@@ -97,9 +99,10 @@ class ReplayAppView : public View {
|
||||
ProgressBar progressbar{
|
||||
{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},
|
||||
};
|
||||
nav_};
|
||||
|
||||
TransmitterView2 tx_view{
|
||||
// new handling of NumberField field_rfgain, NumberField field_rfamp
|
||||
@@ -118,7 +121,7 @@ class ReplayAppView : public View {
|
||||
Color::green(),
|
||||
Color::black()};
|
||||
|
||||
spectrum::WaterfallWidget waterfall{};
|
||||
spectrum::WaterfallView waterfall{};
|
||||
|
||||
MessageHandlerRegistration message_handler_replay_thread_error{
|
||||
Message::ID::ReplayThreadDone,
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "soundboard_app.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "tonesets.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
using namespace tonekey;
|
||||
using namespace portapack;
|
||||
@@ -106,6 +105,7 @@ void SoundBoardView::start_tx(const uint32_t id) {
|
||||
EventDispatcher::send_message(message);
|
||||
});
|
||||
|
||||
// TODO: Delete all this and use tx model.
|
||||
baseband::set_audiotx_config(
|
||||
1536000 / 20, // Update vu-meter at 20Hz
|
||||
transmitter_model.channel_bandwidth(),
|
||||
@@ -119,8 +119,6 @@ void SoundBoardView::start_tx(const uint32_t id) {
|
||||
);
|
||||
baseband::set_sample_rate(sample_rate);
|
||||
|
||||
transmitter_model.set_sampling_rate(1536000);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
tx_view.set_transmitting(true);
|
||||
@@ -235,15 +233,6 @@ SoundBoardView::SoundBoardView(
|
||||
&button_next_page,
|
||||
&tx_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_soundboard", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
refresh_list();
|
||||
|
||||
button_next_page.on_select = [this](Button&) {
|
||||
@@ -266,9 +255,9 @@ SoundBoardView::SoundBoardView(
|
||||
check_random.set_value(false);
|
||||
|
||||
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) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -283,14 +272,9 @@ SoundBoardView::SoundBoardView(
|
||||
}
|
||||
|
||||
SoundBoardView::~SoundBoardView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_soundboard", &app_settings);
|
||||
|
||||
stop();
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "io_wave.hpp"
|
||||
#include "tone_key.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -49,11 +50,14 @@ class SoundBoardView : public View {
|
||||
std::string title() const override { return "Soundbrd TX"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_soundboard", app_settings::Mode::TX};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
NavigationView& nav_;
|
||||
|
||||
enum tx_modes {
|
||||
NORMAL = 0,
|
||||
|
||||
@@ -32,7 +32,7 @@ class SpectrumAnalysisModel {
|
||||
|
||||
namespace ui {
|
||||
|
||||
class SpectrumAnalysisView : public spectrum::WaterfallWidget {
|
||||
class SpectrumAnalysisView : public spectrum::WaterfallView {
|
||||
public:
|
||||
private:
|
||||
SpectrumAnalysisModel model;
|
||||
|
||||
@@ -78,9 +78,9 @@ void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_fre
|
||||
const auto hex_formatted = packet.symbols_formatted();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -157,25 +157,15 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
&field_vga,
|
||||
&recent_entries_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_tpms", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
options_band.set_by_value(app_settings.rx_frequency);
|
||||
} else
|
||||
options_band.set_by_value(receiver_model.tuning_frequency());
|
||||
if (!settings_.loaded())
|
||||
receiver_model.set_target_frequency(initial_target_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();
|
||||
|
||||
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) {
|
||||
tpms::format::use_kpa = !i;
|
||||
@@ -198,10 +188,6 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
}
|
||||
|
||||
TPMSAppView::~TPMSAppView() {
|
||||
// save app settings
|
||||
app_settings.rx_frequency = target_frequency_;
|
||||
settings.save("rx_tpms", &app_settings);
|
||||
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -224,7 +210,7 @@ void TPMSAppView::set_parent_rect(const Rect new_parent_rect) {
|
||||
|
||||
void TPMSAppView::on_packet(const tpms::Packet& packet) {
|
||||
if (logger) {
|
||||
logger->on_packet(packet, target_frequency());
|
||||
logger->on_packet(packet, receiver_model.target_frequency());
|
||||
}
|
||||
|
||||
const auto reading_opt = packet.reading();
|
||||
@@ -241,21 +227,4 @@ void TPMSAppView::on_show_list() {
|
||||
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 */
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "ui_rssi.hpp"
|
||||
#include "ui_channel.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "log_file.hpp"
|
||||
@@ -38,10 +39,6 @@
|
||||
|
||||
namespace std {
|
||||
|
||||
constexpr bool operator==(const tpms::TransponderID& lhs, const tpms::TransponderID& rhs) {
|
||||
return (lhs.value() == rhs.value());
|
||||
}
|
||||
|
||||
} /* namespace std */
|
||||
|
||||
struct TPMSRecentEntry {
|
||||
@@ -106,12 +103,13 @@ class TPMSAppView : public View {
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 315000000;
|
||||
static constexpr uint32_t sampling_rate = 2457600;
|
||||
static constexpr uint32_t baseband_bandwidth = 1750000;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
RxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
2457600 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_tpms", app_settings::Mode::RX};
|
||||
|
||||
MessageHandlerRegistration message_handler_packet{
|
||||
Message::ID::TPMSPacket,
|
||||
@@ -175,18 +173,9 @@ class TPMSAppView : public View {
|
||||
}};
|
||||
TPMSRecentEntriesView recent_entries_view{columns, recent};
|
||||
|
||||
uint32_t target_frequency_ = initial_target_frequency;
|
||||
|
||||
void on_packet(const tpms::Packet& packet);
|
||||
void on_show_list();
|
||||
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 */
|
||||
|
||||
@@ -47,7 +47,7 @@ using namespace portapack;
|
||||
namespace ui {
|
||||
|
||||
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({
|
||||
.mode = 0,
|
||||
.sampling_rate = 1536000,
|
||||
@@ -56,7 +56,6 @@ void AboutView::on_show() {
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_lna(40);
|
||||
transmitter_model.set_vga(40);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_audiotx_data(32, 50, false, 0);
|
||||
|
||||
@@ -288,13 +288,6 @@ void ADSBRxView::focus() {
|
||||
}
|
||||
|
||||
ADSBRxView::~ADSBRxView() {
|
||||
receiver_model.set_tuning_frequency(prevFreq); // Restore previous frequency on exit
|
||||
|
||||
// save app settings
|
||||
settings.save("rx_adsb", &app_settings);
|
||||
|
||||
// TODO: once all apps keep there own settin previous frequency logic can be removed
|
||||
receiver_model.set_tuning_frequency(prevFreq);
|
||||
rtc_time::signal_tick_second -= signal_token_tick_second;
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
@@ -475,7 +468,8 @@ void ADSBRxView::updateDetailsAndMap(int ageStep) {
|
||||
details_view->update(entry);
|
||||
}
|
||||
// Store if the view is present and the list isn't full
|
||||
else if (otherMarkersCanBeSent && (markerStored != MARKER_LIST_FULL) && entry.pos.valid && (entry.age_state <= 2)) {
|
||||
// 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.lat = entry.pos.latitude;
|
||||
marker.angle = entry.velo.heading;
|
||||
@@ -502,17 +496,6 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
|
||||
&rssi,
|
||||
&recent_entries_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_adsb", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
} else {
|
||||
field_lna.set_value(40);
|
||||
field_vga.set_value(40);
|
||||
}
|
||||
|
||||
recent_entries_view.set_parent_rect({0, 16, 240, 272});
|
||||
recent_entries_view.on_select = [this, &nav](const AircraftRecentEntry& entry) {
|
||||
detailed_entry_key = entry.key();
|
||||
@@ -528,14 +511,10 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
|
||||
on_tick_second();
|
||||
};
|
||||
|
||||
prevFreq = receiver_model.tuning_frequency(); // Store previous frequency on creation
|
||||
|
||||
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_sampling_rate(2000000);
|
||||
receiver_model.set_baseband_bandwidth(2500000);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "adsb.hpp"
|
||||
#include "message.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "crc.hpp"
|
||||
|
||||
using namespace adsb;
|
||||
@@ -340,7 +341,13 @@ class ADSBRxView : public View {
|
||||
void sort_entries_by_state();
|
||||
|
||||
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{};
|
||||
void on_frame(const ADSBFrameMessage* message);
|
||||
void on_tick_second();
|
||||
@@ -350,9 +357,6 @@ class ADSBRxView : public View {
|
||||
|
||||
#define MARKER_UPDATE_SECONDS (5)
|
||||
int ticksSinceMarkerRefresh{MARKER_UPDATE_SECONDS - 1};
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
|
||||
const RecentEntriesColumns columns{{{"ICAO/Call", 9},
|
||||
{"Lvl", 3},
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "manchester.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include <cstring>
|
||||
@@ -273,13 +272,8 @@ void ADSBTxView::focus() {
|
||||
}
|
||||
|
||||
ADSBTxView::~ADSBTxView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_adsb", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, withouth ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void ADSBTxView::generate_frames() {
|
||||
@@ -303,8 +297,6 @@ void ADSBTxView::generate_frames() {
|
||||
void ADSBTxView::start_tx() {
|
||||
generate_frames();
|
||||
|
||||
transmitter_model.set_sampling_rate(4000000U);
|
||||
transmitter_model.set_baseband_bandwidth(10000000);
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_adsb();
|
||||
@@ -327,18 +319,10 @@ ADSBTxView::ADSBTxView(
|
||||
&text_frame,
|
||||
&tx_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_adsb", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
using namespace adsb;
|
||||
@@ -201,9 +202,12 @@ class ADSBTxView : public View {
|
||||
-1
|
||||
};*/
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{
|
||||
10000000 /* bandwidth */,
|
||||
4000000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_adsb", app_settings::Mode::TX};
|
||||
|
||||
// tx_modes tx_mode = IDLE;
|
||||
NavigationView& nav_;
|
||||
|
||||
@@ -43,11 +43,8 @@ void AFSKRxView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
void AFSKRxView::update_freq(rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
AFSKRxView::AFSKRxView(NavigationView& nav) {
|
||||
AFSKRxView::AFSKRxView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_afsk_rx);
|
||||
|
||||
add_children({&rssi,
|
||||
@@ -62,16 +59,8 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
|
||||
&button_modem_setup,
|
||||
&console});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_afsk", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
|
||||
// Auto-configure modem for LCR RX (will be removed later)
|
||||
update_freq(467225500); // 462713300
|
||||
// Auto-configure modem for LCR RX (TODO remove)
|
||||
field_frequency.set_value(467225500);
|
||||
auto def_bell202 = &modem_defs[0];
|
||||
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
|
||||
serial_format_t serial_format;
|
||||
@@ -81,18 +70,7 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
|
||||
serial_format.bit_order = LSB_FIRST;
|
||||
persistent_memory::set_serial_format(serial_format);
|
||||
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
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.on_select = [this](Checkbox&, bool v) {
|
||||
@@ -113,8 +91,6 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::output::start();
|
||||
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
||||
receiver_model.enable();
|
||||
}
|
||||
@@ -167,10 +143,6 @@ void AFSKRxView::on_data(uint32_t value, bool is_data) {
|
||||
}
|
||||
|
||||
AFSKRxView::~AFSKRxView() {
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_afsk", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
@@ -26,8 +26,10 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.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 "radio_state.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
@@ -57,9 +59,10 @@ class AFSKRxView : public View {
|
||||
private:
|
||||
void on_data(uint32_t value, bool is_data);
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_afsk", app_settings::Mode::RX};
|
||||
|
||||
uint8_t console_color{0};
|
||||
uint32_t prev_value{0};
|
||||
@@ -73,18 +76,16 @@ class AFSKRxView : public View {
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, 0 * 16}};
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
Channel channel{
|
||||
{21 * 8, 5, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 5, 6 * 8, 4}};
|
||||
|
||||
AudioVolumeField field_volume{
|
||||
{28 * 8, 0 * 16}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
RxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
};
|
||||
nav_};
|
||||
|
||||
Checkbox check_log{
|
||||
{0 * 8, 1 * 16},
|
||||
@@ -103,7 +104,6 @@ class AFSKRxView : public View {
|
||||
Console console{
|
||||
{0, 4 * 16, 240, 240}};
|
||||
|
||||
void update_freq(rf::Frequency f);
|
||||
void on_data_afsk(const AFSKDataMessage& message);
|
||||
|
||||
std::unique_ptr<AFSKLogger> logger{};
|
||||
|
||||
@@ -73,12 +73,8 @@ void APRSRxView::focus() {
|
||||
options_region.focus();
|
||||
}
|
||||
|
||||
void APRSRxView::update_freq(rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
add_children({&rssi,
|
||||
@@ -92,14 +88,6 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
|
||||
&record_view,
|
||||
&console});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_aprs", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
|
||||
// DEBUG
|
||||
record_view.on_error = [&nav](std::string message) {
|
||||
nav.display_modal("Error", message);
|
||||
@@ -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.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);
|
||||
|
||||
logger = std::make_unique<APRSLogger>();
|
||||
@@ -143,8 +119,6 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::output::start();
|
||||
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
||||
receiver_model.enable();
|
||||
}
|
||||
@@ -211,9 +185,6 @@ void APRSRxView::on_show() {
|
||||
}
|
||||
|
||||
APRSRxView::~APRSRxView() {
|
||||
// save app settings
|
||||
settings.save("rx_aprs", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
@@ -26,9 +26,11 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.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 "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
|
||||
@@ -187,9 +189,10 @@ class APRSRxView : public View {
|
||||
void on_data(uint32_t value, bool is_data);
|
||||
bool reset_console = false;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_aprs", app_settings::Mode::RX};
|
||||
|
||||
uint8_t console_color{0};
|
||||
std::string str_log{""};
|
||||
@@ -201,11 +204,9 @@ class APRSRxView : public View {
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, 0 * 16}};
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
Channel channel{
|
||||
{21 * 8, 5, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 5, 6 * 8, 4}};
|
||||
|
||||
AudioVolumeField field_volume{
|
||||
{28 * 8, 0 * 16}};
|
||||
@@ -218,9 +219,9 @@ class APRSRxView : public View {
|
||||
{"AUS", 2},
|
||||
{"NZ ", 3}}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
RxFrequencyField field_frequency{
|
||||
{3 * 8, 0 * 16},
|
||||
};
|
||||
nav_};
|
||||
|
||||
// DEBUG
|
||||
RecordView record_view{
|
||||
@@ -234,8 +235,6 @@ class APRSRxView : public View {
|
||||
Console console{
|
||||
{0, 2 * 16, 240, 240}};
|
||||
|
||||
void update_freq(rf::Frequency f);
|
||||
|
||||
std::unique_ptr<APRSLogger> logger{};
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "aprs.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
@@ -44,13 +43,8 @@ void APRSTXView::focus() {
|
||||
}
|
||||
|
||||
APRSTXView::~APRSTXView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_aprs", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void APRSTXView::start_tx() {
|
||||
@@ -62,9 +56,6 @@ void APRSTXView::start_tx() {
|
||||
// uint8_t * bb_data_ptr = shared_memory.bb_data.data;
|
||||
// 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();
|
||||
|
||||
baseband::set_afsk_data(
|
||||
@@ -97,14 +88,6 @@ APRSTXView::APRSTXView(NavigationView& nav) {
|
||||
&button_set,
|
||||
&tx_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_aprs", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
button_set.on_select = [this, &nav](Button&) {
|
||||
text_prompt(
|
||||
nav,
|
||||
@@ -116,9 +99,9 @@ APRSTXView::APRSTXView(NavigationView& 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) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
#include "modems.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
@@ -43,9 +45,12 @@ class APRSTXView : public View {
|
||||
std::string title() const override { return "APRS TX"; };
|
||||
|
||||
private:
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
AFSK_TX_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_aprs", app_settings::Mode::TX};
|
||||
|
||||
std::string payload{""};
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@@ -137,13 +136,8 @@ void BHTView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
}
|
||||
|
||||
BHTView::~BHTView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_bht", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
BHTView::BHTView(NavigationView& nav) {
|
||||
@@ -157,21 +151,12 @@ BHTView::BHTView(NavigationView& nav) {
|
||||
&progressbar,
|
||||
&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);
|
||||
|
||||
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) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -233,7 +218,6 @@ EPARView::EPARView(
|
||||
|
||||
field_city.set_value(0);
|
||||
field_group.set_selected_index(2);
|
||||
|
||||
field_city.on_change = [this](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;
|
||||
for (auto& relay_state : relay_states) {
|
||||
relay_state.on_change = relay_state_fn;
|
||||
relay_state.set_parent_rect({static_cast<Coord>(90 + (n * 36)),
|
||||
80,
|
||||
24, 24});
|
||||
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);
|
||||
n++;
|
||||
}
|
||||
@@ -316,11 +300,11 @@ XylosView::XylosView(
|
||||
};
|
||||
|
||||
field_header_a.on_change = field_fn;
|
||||
field_header_b.on_change = [this](int32_t) { generate_message(); };
|
||||
field_city.on_change = [this](int32_t) { generate_message(); };
|
||||
field_family.on_change = [this](int32_t) { generate_message(); };
|
||||
field_subfamily.on_change = [this](int32_t) { generate_message(); };
|
||||
field_receiver.on_change = [this](int32_t) { generate_message(); };
|
||||
field_header_b.on_change = field_fn;
|
||||
field_city.on_change = field_fn;
|
||||
field_family.on_change = field_fn;
|
||||
field_subfamily.on_change = field_fn;
|
||||
field_receiver.on_change = field_fn;
|
||||
|
||||
checkbox_wcsubfamily.on_select = [this](Checkbox&, bool v) {
|
||||
field_subfamily.set_focusable(!v);
|
||||
@@ -341,11 +325,11 @@ XylosView::XylosView(
|
||||
|
||||
size_t n = 0;
|
||||
for (auto& relay_state : relay_states) {
|
||||
relay_state.on_change = relay_state_fn;
|
||||
relay_state.set_parent_rect({static_cast<Coord>(54 + (n * 36)),
|
||||
134,
|
||||
24, 24});
|
||||
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);
|
||||
n++;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "transmitter_model.hpp"
|
||||
#include "encoders.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
@@ -165,9 +166,9 @@ class BHTView : public View {
|
||||
std::string title() const override { return "BHT TX"; };
|
||||
|
||||
private:
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_bht", app_settings::Mode::TX};
|
||||
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
void start_tx();
|
||||
|
||||
@@ -40,11 +40,8 @@ void BTLERxView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
void BTLERxView::update_freq(rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
BTLERxView::BTLERxView(NavigationView& nav) {
|
||||
BTLERxView::BTLERxView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_btle_rx);
|
||||
|
||||
add_children({&rssi,
|
||||
@@ -56,16 +53,8 @@ BTLERxView::BTLERxView(NavigationView& nav) {
|
||||
&button_modem_setup,
|
||||
&console});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("rx_btle", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
field_lna.set_value(app_settings.lna);
|
||||
field_vga.set_value(app_settings.vga);
|
||||
field_rf_amp.set_value(app_settings.rx_amp);
|
||||
}
|
||||
|
||||
// Auto-configure modem for LCR RX (will be removed later)
|
||||
update_freq(2426000000);
|
||||
// Auto-configure modem for LCR RX (TODO: remove later)
|
||||
field_frequency.set_value(2426000000);
|
||||
auto def_bell202 = &modem_defs[0];
|
||||
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
|
||||
serial_format_t serial_format;
|
||||
@@ -75,18 +64,7 @@ BTLERxView::BTLERxView(NavigationView& nav) {
|
||||
serial_format.bit_order = LSB_FIRST;
|
||||
persistent_memory::set_serial_format(serial_format);
|
||||
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
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&) {
|
||||
nav.push<ModemSetupView>();
|
||||
@@ -98,8 +76,6 @@ BTLERxView::BTLERxView(NavigationView& nav) {
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::output::start();
|
||||
|
||||
receiver_model.set_sampling_rate(4000000);
|
||||
receiver_model.set_baseband_bandwidth(4000000);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
||||
receiver_model.enable();
|
||||
}
|
||||
@@ -148,10 +124,6 @@ void BTLERxView::on_data(uint32_t value, bool is_data) {
|
||||
}
|
||||
|
||||
BTLERxView::~BTLERxView() {
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_btle", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "ui_record_view.hpp" // DEBUG
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
@@ -46,9 +48,13 @@ class BTLERxView : public View {
|
||||
private:
|
||||
void on_data(uint32_t value, bool is_data);
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
4000000 /* bandwidth */,
|
||||
4000000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_btle", app_settings::Mode::RX};
|
||||
|
||||
uint8_t console_color{0};
|
||||
uint32_t prev_value{0};
|
||||
@@ -60,15 +66,13 @@ class BTLERxView : public View {
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, 0 * 16}};
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
Channel channel{
|
||||
{21 * 8, 5, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 5, 6 * 8, 4}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
RxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
};
|
||||
nav_};
|
||||
|
||||
Button button_modem_setup{
|
||||
{240 - 12 * 8, 1 * 16, 96, 24},
|
||||
@@ -77,9 +81,6 @@ class BTLERxView : public View {
|
||||
Console console{
|
||||
{0, 4 * 16, 240, 240}};
|
||||
|
||||
void update_freq(rf::Frequency f);
|
||||
// void on_data_afsk(const AFSKDataMessage& message);
|
||||
|
||||
MessageHandlerRegistration message_handler_packet{
|
||||
Message::ID::AFSKData,
|
||||
[this](Message* const p) {
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "ui_coasterp.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
@@ -38,13 +37,8 @@ void CoasterPagerView::focus() {
|
||||
}
|
||||
|
||||
CoasterPagerView::~CoasterPagerView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_coaster", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void CoasterPagerView::generate_frame() {
|
||||
@@ -73,8 +67,6 @@ void CoasterPagerView::generate_frame() {
|
||||
void CoasterPagerView::start_tx() {
|
||||
generate_frame();
|
||||
|
||||
transmitter_model.set_sampling_rate(2280000);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_fsk_data(19 * 8, 2280000 / 1000, 5000, 32);
|
||||
@@ -123,15 +115,6 @@ CoasterPagerView::CoasterPagerView(NavigationView& nav) {
|
||||
&text_message,
|
||||
&tx_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_coaster", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
// Bytes to nibbles
|
||||
for (c = 0; c < 16; c++)
|
||||
sym_data.set_sym(c, (data_init[c >> 1] >> ((c & 1) ? 0 : 4)) & 0x0F);
|
||||
@@ -141,9 +124,9 @@ CoasterPagerView::CoasterPagerView(NavigationView& nav) {
|
||||
generate_frame();
|
||||
|
||||
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) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
@@ -50,9 +51,12 @@ class CoasterPagerView : public View {
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
2280000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_coaster", app_settings::Mode::TX};
|
||||
|
||||
void start_tx();
|
||||
void generate_frame();
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "radio.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "crc.hpp"
|
||||
|
||||
#include "audio.hpp"
|
||||
|
||||
@@ -247,6 +248,7 @@ void ControlsSwitchesWidget::on_show() {
|
||||
|
||||
bool ControlsSwitchesWidget::on_key(const KeyEvent 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;
|
||||
}
|
||||
|
||||
@@ -324,6 +326,14 @@ void ControlsSwitchesWidget::paint(Painter& painter) {
|
||||
|
||||
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() {
|
||||
@@ -334,12 +344,21 @@ void ControlsSwitchesWidget::on_frame_sync() {
|
||||
|
||||
DebugControlsView::DebugControlsView(NavigationView& nav) {
|
||||
add_children({
|
||||
&text_title,
|
||||
&labels,
|
||||
&switches_widget,
|
||||
&options_switches_mode,
|
||||
&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() {
|
||||
@@ -381,22 +400,36 @@ DebugMenuView::DebugMenuView(NavigationView& nav) {
|
||||
{"Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [&nav]() { nav.push<DebugPeripheralsMenuView>(); }},
|
||||
{"Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav]() { nav.push<TemperatureView>(); }},
|
||||
{"Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav]() { nav.push<DebugControlsView>(); }},
|
||||
{"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
|
||||
}
|
||||
|
||||
/* 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)
|
||||
: 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());
|
||||
|
||||
add_children({&text_page, ®isters_widget, &text_checksum, &button_ok});
|
||||
add_children({&text_page, ®isters_widget, &text_checksum, &text_checksum2, &button_ok});
|
||||
|
||||
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&) {
|
||||
nav.pop();
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "rffc507x.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "memory_map.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
@@ -220,7 +221,8 @@ class ControlsSwitchesWidget : public Widget {
|
||||
ControlsSwitchesWidget(
|
||||
Rect parent_rect)
|
||||
: Widget{parent_rect},
|
||||
key_event_mask(0) {
|
||||
key_event_mask(0),
|
||||
long_press_key_event_mask{0} {
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
@@ -231,6 +233,7 @@ class ControlsSwitchesWidget : public Widget {
|
||||
|
||||
private:
|
||||
uint8_t key_event_mask;
|
||||
uint8_t long_press_key_event_mask;
|
||||
|
||||
MessageHandlerRegistration message_handler_frame_sync{
|
||||
Message::ID::DisplayFrameSync,
|
||||
@@ -250,15 +253,22 @@ class DebugControlsView : public View {
|
||||
std::string title() const override { return "Buttons Test"; };
|
||||
|
||||
private:
|
||||
Text text_title{
|
||||
{64, 16, 184, 16},
|
||||
"Controls State",
|
||||
};
|
||||
Labels labels{
|
||||
{{8 * 8, 1 * 16}, "Controls State", Color::white()},
|
||||
{{0 * 8, 14 * 16}, "Long-Press Mode:", Color::grey()}};
|
||||
|
||||
ControlsSwitchesWidget switches_widget{
|
||||
{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{
|
||||
{72, 264, 96, 24},
|
||||
"Done"};
|
||||
@@ -269,7 +279,7 @@ class DebugPmemView : public View {
|
||||
DebugPmemView(NavigationView& nav);
|
||||
void focus() 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:
|
||||
struct pmem_data {
|
||||
@@ -282,13 +292,14 @@ class DebugPmemView : public View {
|
||||
|
||||
int32_t page{0};
|
||||
|
||||
const pmem_data& data;
|
||||
volatile const pmem_data& data;
|
||||
|
||||
Text text_page{{16, 16, 208, 16}};
|
||||
|
||||
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{
|
||||
{240 / 3, 270, 240 / 3, 24},
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "ui_dfu_menu.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "performance_counter.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -80,4 +81,57 @@ void DfuMenu::paint(Painter& painter) {
|
||||
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 */
|
||||
|
||||
@@ -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}, ""};
|
||||
};
|
||||
|
||||
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 */
|
||||
|
||||
#endif /*__UI_DFU_MENU_H__*/
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -200,13 +199,8 @@ void EncodersView::focus() {
|
||||
}
|
||||
|
||||
EncodersView::~EncodersView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_ook", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // ghost signal c/m to the problem at the exit .
|
||||
baseband::shutdown(); // better this function after load_sram()
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void EncodersView::update_progress() {
|
||||
@@ -266,8 +260,6 @@ void EncodersView::start_tx(const bool scan) {
|
||||
repeat_index = 1;
|
||||
update_progress();
|
||||
|
||||
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_ook_data(
|
||||
@@ -290,19 +282,10 @@ EncodersView::EncodersView(
|
||||
&progressbar,
|
||||
&tx_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_ook", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
tx_view.on_edit_frequency = [this, &nav]() {
|
||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency());
|
||||
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "encoders.hpp"
|
||||
#include "de_bruijn.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
using namespace encoders;
|
||||
|
||||
@@ -183,9 +184,12 @@ class EncodersView : public View {
|
||||
SCAN
|
||||
};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
OOK_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_ook", app_settings::Mode::TX};
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
uint32_t repeat_index{0};
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include "ui_fileman.hpp"
|
||||
#include "ui_playlist.hpp"
|
||||
#include "ui_ss_viewer.hpp"
|
||||
#include "ui_text_editor.hpp"
|
||||
#include "string_format.hpp"
|
||||
@@ -35,6 +36,13 @@
|
||||
using namespace portapack;
|
||||
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 {
|
||||
using namespace ui;
|
||||
|
||||
@@ -47,25 +55,6 @@ std::string truncate(const fs::path& path, size_t 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.
|
||||
void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) {
|
||||
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) {
|
||||
if (fs::is_directory(path))
|
||||
return {};
|
||||
|
||||
const fs::path txt_path{u".TXT"};
|
||||
const fs::path c16_path{u".C16"};
|
||||
auto ext = path.extension();
|
||||
|
||||
if (iequal(ext, txt_path))
|
||||
ext = c16_path;
|
||||
else if (iequal(ext, c16_path))
|
||||
ext = txt_path;
|
||||
if (path_iequal(ext, txt_ext))
|
||||
ext = c16_ext;
|
||||
else if (path_iequal(ext, c16_ext))
|
||||
ext = txt_ext;
|
||||
else
|
||||
return {};
|
||||
|
||||
@@ -163,7 +149,7 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
|
||||
continue;
|
||||
|
||||
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});
|
||||
} else if (fs::is_directory(entry.status())) {
|
||||
insert_sorted(entry_list, {entry.path(), 0, true});
|
||||
@@ -301,7 +287,7 @@ const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
|
||||
size_t index = 0;
|
||||
|
||||
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];
|
||||
|
||||
// Default to last entry in the list.
|
||||
@@ -507,10 +493,14 @@ bool FileManagerView::handle_file_open() {
|
||||
auto path = get_selected_full_path();
|
||||
auto ext = path.extension();
|
||||
|
||||
if (iequal(u".TXT", ext) || iequal(u".PPL", ext)) {
|
||||
if (path_iequal(txt_ext, ext)) {
|
||||
nav_.push<TextEditorView>(path);
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
static const char16_t* firmware_folder = u"/FIRMWARE";
|
||||
|
||||
Thread* FlashUtilityView::thread{nullptr};
|
||||
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});
|
||||
|
||||
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 path = entry.path().native();
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ class FlashUtilityView : public View {
|
||||
|
||||
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:
|
||||
NavigationView& nav_;
|
||||
|
||||
@@ -81,14 +81,11 @@ void FreqManBaseView::change_category(int32_t category_id) {
|
||||
|
||||
if (file_list.empty()) return;
|
||||
|
||||
std::vector<freqman_entry>().swap(database);
|
||||
|
||||
if (!load_freqman_file(file_list[categories[category_id].second], database)) {
|
||||
error_ = ERROR_ACCESS;
|
||||
}
|
||||
menu_view.set_db(database);
|
||||
freqlist_view.set_db(database);
|
||||
text_empty.hidden(!database.empty());
|
||||
menu_view.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) {
|
||||
menu_view.hidden(v);
|
||||
freqlist_view.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();
|
||||
}
|
||||
|
||||
@@ -176,17 +177,14 @@ FrequencyLoadView::FrequencyLoadView(
|
||||
refresh_widgets(v);
|
||||
};
|
||||
|
||||
add_children({&menu_view,
|
||||
add_children({&freqlist_view,
|
||||
&text_empty});
|
||||
|
||||
// Resize menu view to fill screen
|
||||
menu_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.set_parent_rect({0, 3 * 8, 240, 30 * 8});
|
||||
|
||||
freqlist_view.on_select = [&nav, this](FreqManUIList&) {
|
||||
auto& entry = database[freqlist_view.get_index()];
|
||||
if (entry.type == RANGE) {
|
||||
// User chose a frequency range entry
|
||||
if (on_range_loaded)
|
||||
@@ -199,18 +197,21 @@ FrequencyLoadView::FrequencyLoadView(
|
||||
if (on_frequency_loaded)
|
||||
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) {
|
||||
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);
|
||||
change_category(current_category_id);
|
||||
}
|
||||
|
||||
void FrequencyManagerView::on_edit_desc(NavigationView& nav) {
|
||||
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);
|
||||
change_category(current_category_id);
|
||||
});
|
||||
@@ -230,7 +231,7 @@ void FrequencyManagerView::on_delete() {
|
||||
delete_freqman_file(file_list[categories[current_category_id].second]);
|
||||
refresh_list();
|
||||
} 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);
|
||||
}
|
||||
change_category(current_category_id);
|
||||
@@ -241,10 +242,9 @@ void FrequencyManagerView::refresh_widgets(const bool v) {
|
||||
button_edit_desc.hidden(v);
|
||||
button_delete.hidden(v);
|
||||
text_empty.hidden(!v);
|
||||
menu_view.hidden(v);
|
||||
menu_view.set_dirty();
|
||||
freqlist_view.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();
|
||||
}
|
||||
|
||||
@@ -261,13 +261,13 @@ FrequencyManagerView::FrequencyManagerView(
|
||||
|
||||
add_children({&labels,
|
||||
&button_new_category,
|
||||
&menu_view,
|
||||
&freqlist_view,
|
||||
&text_empty,
|
||||
&button_edit_freq,
|
||||
&button_edit_desc,
|
||||
&button_delete});
|
||||
|
||||
menu_view.on_select = [this](FreqManUIList&) {
|
||||
freqlist_view.on_select = [this](FreqManUIList&) {
|
||||
button_edit_freq.focus();
|
||||
};
|
||||
|
||||
@@ -280,7 +280,7 @@ FrequencyManagerView::FrequencyManagerView(
|
||||
if (database.empty()) {
|
||||
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) {
|
||||
on_edit_freq(f);
|
||||
};
|
||||
@@ -290,7 +290,7 @@ FrequencyManagerView::FrequencyManagerView(
|
||||
if (database.empty()) {
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class FreqManBaseView : public View {
|
||||
14,
|
||||
{}};
|
||||
|
||||
FreqManUIList menu_view{
|
||||
FreqManUIList freqlist_view{
|
||||
{0, 3 * 8, 240, 23 * 8}};
|
||||
|
||||
Text text_empty{
|
||||
@@ -116,6 +116,7 @@ class FrequencyLoadView : public FreqManBaseView {
|
||||
std::function<void(rf::Frequency, rf::Frequency)> on_range_loaded{};
|
||||
|
||||
FrequencyLoadView(NavigationView& nav);
|
||||
~FrequencyLoadView();
|
||||
|
||||
std::string title() const override { return "Load freq."; };
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@@ -180,13 +179,12 @@ void JammerView::focus() {
|
||||
|
||||
JammerView::~JammerView() {
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void JammerView::on_retune(const rf::Frequency freq, const uint32_t range) {
|
||||
if (freq) {
|
||||
transmitter_model.set_tuning_frequency(freq);
|
||||
transmitter_model.set_target_frequency(freq);
|
||||
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_text("STOP");
|
||||
|
||||
transmitter_model.set_sampling_rate(3072000U);
|
||||
transmitter_model.set_rf_amp(field_amp.value());
|
||||
transmitter_model.set_baseband_bandwidth(3500000U);
|
||||
transmitter_model.set_tx_gain(field_gain.value());
|
||||
transmitter_model.enable();
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "message.hpp"
|
||||
#include "jammer.hpp"
|
||||
#include "lfsr_random.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
using namespace jammer;
|
||||
|
||||
@@ -99,6 +100,10 @@ class JammerView : public View {
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
TxRadioState radio_state_{
|
||||
3500000 /* bandwidth */,
|
||||
3072000 /* sampling rate */
|
||||
};
|
||||
|
||||
void start_tx();
|
||||
void on_timer();
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "ui_keyfob.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@@ -141,8 +140,7 @@ KeyfobView::~KeyfobView() {
|
||||
settings.save("tx_keyfob", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void KeyfobView::update_progress(const uint32_t progress) {
|
||||
@@ -190,8 +188,6 @@ void KeyfobView::start_tx() {
|
||||
|
||||
size_t bitstream_length = generate_frame();
|
||||
|
||||
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_ook_data(
|
||||
@@ -236,12 +232,12 @@ KeyfobView::KeyfobView(
|
||||
|
||||
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]() {
|
||||
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) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "encoders.hpp"
|
||||
|
||||
using namespace encoders;
|
||||
@@ -42,9 +43,12 @@ class KeyfobView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
OOK_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_keyfob", , app_settings::Mode::TX};
|
||||
|
||||
// 1013210ns / bit
|
||||
static constexpr uint32_t subaru_samples_per_bit = (OOK_SAMPLERATE * 0.00101321);
|
||||
|
||||
@@ -24,10 +24,8 @@
|
||||
#include "ui_modemsetup.hpp"
|
||||
|
||||
#include "lcr.hpp"
|
||||
#include "modems.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
#include "serializer.hpp"
|
||||
|
||||
@@ -40,13 +38,8 @@ void LCRView::focus() {
|
||||
}
|
||||
|
||||
LCRView::~LCRView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_lcr", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -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);
|
||||
|
||||
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();
|
||||
|
||||
memcpy(shared_memory.bb_data.data, lcr_message_data, sizeof(lcr_message_data));
|
||||
@@ -176,15 +166,6 @@ LCRView::LCRView(NavigationView& nav) {
|
||||
&button_clear,
|
||||
&tx_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_lcr", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
options_scanlist.set_selected_index(0);
|
||||
|
||||
const auto button_set_am_fn = [this, &nav](Button& button) {
|
||||
@@ -251,9 +232,9 @@ LCRView::LCRView(NavigationView& 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) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -26,8 +26,10 @@
|
||||
#include "ui_transmitter.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
#include "modems.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -79,9 +81,12 @@ class LCRView : public View {
|
||||
SCAN
|
||||
};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
AFSK_TX_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_lcr", app_settings::Mode::TX};
|
||||
|
||||
tx_modes tx_mode = IDLE;
|
||||
uint8_t scan_count{0}, scan_index{0};
|
||||
|
||||
@@ -35,13 +35,6 @@ void LevelView::focus() {
|
||||
}
|
||||
|
||||
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();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -71,42 +64,31 @@ LevelView::LevelView(NavigationView& nav)
|
||||
change_mode(NFM_MODULATION); // Start on AM
|
||||
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 = persistent_memory::tuned_frequency();
|
||||
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);
|
||||
}
|
||||
freq_ = receiver_model.target_frequency();
|
||||
button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
|
||||
|
||||
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) {
|
||||
freq = f;
|
||||
receiver_model.set_tuning_frequency(f); // Retune to actual freq
|
||||
button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>");
|
||||
freq_ = f;
|
||||
receiver_model.set_target_frequency(f); // Retune to actual freq
|
||||
button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
|
||||
};
|
||||
};
|
||||
|
||||
button_frequency.on_change = [this]() {
|
||||
int64_t def_step = freqman_entry_get_step_value(step_mode.selected_index());
|
||||
freq = freq + (button_frequency.get_encoder_delta() * def_step);
|
||||
if (freq < 1) {
|
||||
freq = 1;
|
||||
freq_ = freq_ + (button_frequency.get_encoder_delta() * def_step);
|
||||
if (freq_ < 1) {
|
||||
freq_ = 1;
|
||||
}
|
||||
if (freq > (MAX_UFREQ - def_step)) {
|
||||
freq = MAX_UFREQ;
|
||||
if (freq_ > (MAX_UFREQ - def_step)) {
|
||||
freq_ = MAX_UFREQ;
|
||||
}
|
||||
button_frequency.set_encoder_delta(0);
|
||||
|
||||
receiver_model.set_tuning_frequency(freq); // Retune to actual freq
|
||||
button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>");
|
||||
receiver_model.set_target_frequency(freq_); // Retune to actual freq
|
||||
button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
|
||||
};
|
||||
|
||||
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_am_configuration(field_bw.selected_index_value());
|
||||
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(" ");
|
||||
break;
|
||||
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_nbfm_configuration(field_bw.selected_index_value());
|
||||
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;
|
||||
case WFM_MODULATION:
|
||||
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_wfm_configuration(field_bw.selected_index_value());
|
||||
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(" ");
|
||||
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:
|
||||
break;
|
||||
}
|
||||
@@ -230,34 +216,23 @@ size_t LevelView::change_mode(freqman_index_t new_mod) {
|
||||
}
|
||||
|
||||
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;
|
||||
size_t min_idx{0};
|
||||
size_t c;
|
||||
if (field_mode.selected_index() == NFM_MODULATION) {
|
||||
tone_index idx = tone_key_index_by_value(value);
|
||||
|
||||
if (field_mode.selected_index() != NFM_MODULATION) {
|
||||
text_ctcss.set(" ");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
if ((last_squelch_index < 0) || (last_squelch_index != idx)) {
|
||||
last_squelch_index = idx;
|
||||
if (idx >= 0) {
|
||||
text_ctcss.set("T: " + tone_key_string(idx));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Arbitrary confidence threshold
|
||||
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(" ");
|
||||
}
|
||||
text_ctcss.set(" ");
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "file.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -53,13 +54,18 @@ class LevelView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
RxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_level", app_settings::Mode::RX};
|
||||
|
||||
size_t change_mode(freqman_index_t mod_type);
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
void set_display_freq(int64_t freq);
|
||||
|
||||
// TODO: needed?
|
||||
int32_t db{0};
|
||||
long long int MAX_UFREQ = {7200000000}; // maximum usable freq
|
||||
rf::Frequency freq = {0};
|
||||
rf::Frequency freq_ = {0};
|
||||
|
||||
Labels labels{
|
||||
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()},
|
||||
@@ -85,7 +91,7 @@ class LevelView : public View {
|
||||
|
||||
OptionsField field_mode{
|
||||
{15 * 8, 1 * 16},
|
||||
3,
|
||||
4,
|
||||
{}};
|
||||
|
||||
OptionsField step_mode{
|
||||
@@ -104,7 +110,7 @@ class LevelView : public View {
|
||||
{"audio off", 0},
|
||||
{"audio on", 1}
|
||||
//{"tone on", 2},
|
||||
//{"tone off", 2},
|
||||
//{"tone off", 3},
|
||||
}};
|
||||
|
||||
Text text_ctcss{
|
||||
@@ -168,9 +174,6 @@ class LevelView : public View {
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
|
||||
}};
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -22,12 +22,15 @@
|
||||
*/
|
||||
|
||||
#include "ui_looking_glass_app.hpp"
|
||||
#include "convert.hpp"
|
||||
#include "file_reader.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
void GlassView::focus() {
|
||||
button_marker.focus();
|
||||
range_presets.focus();
|
||||
}
|
||||
|
||||
GlassView::~GlassView() {
|
||||
@@ -38,7 +41,7 @@ GlassView::~GlassView() {
|
||||
|
||||
void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power) {
|
||||
if (mode == LOOKING_GLASS_SINGLEPASS) {
|
||||
// analog audio app like view
|
||||
// <20MHz spectrum mode
|
||||
if (bin < 120) {
|
||||
if (spectrum.db[SPEC_NB_BINS - 120 + bin] > max_power)
|
||||
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];
|
||||
}
|
||||
} else {
|
||||
// view is made in multiple pass, use original bin picking
|
||||
// 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
|
||||
// FAST or SLOW mode
|
||||
if (bin < 120) {
|
||||
if (spectrum.db[SPEC_NB_BINS - offset - 120 + bin] > max_power)
|
||||
max_power = spectrum.db[SPEC_NB_BINS - offset - 120 + bin];
|
||||
if (spectrum.db[134 + bin] > max_power)
|
||||
max_power = spectrum.db[134 + bin];
|
||||
} else {
|
||||
if (spectrum.db[offset + bin - 120] > max_power)
|
||||
max_power = spectrum.db[offset + bin - 120];
|
||||
if (spectrum.db[bin - 118] > max_power)
|
||||
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
|
||||
freq_at_pos = f_center_ini + ((pos - 120) * ((looking_glass_range - ((16 * looking_glass_range) / SPEC_NB_BINS)) / 2)) / (SCREEN_W / 2);
|
||||
} 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;
|
||||
}
|
||||
@@ -77,23 +78,6 @@ void GlassView::on_marker_change() {
|
||||
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() {
|
||||
// Start a new sweep.
|
||||
// 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
|
||||
// Each having the radio signal power for it's corresponding frequency slot
|
||||
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
|
||||
for (bin = 0; bin < bin_length; bin++) {
|
||||
get_max_power(spectrum, bin, max_power);
|
||||
if (ignore_dc && bin == 119) {
|
||||
bins_Hz_size += 12 * each_bin_size; // add the ignored DC spike to "pixel fulfilled bag of Hz"
|
||||
}
|
||||
bins_Hz_size += each_bin_size; // add this bin Hz count into the "pixel fulfilled bag of Hz"
|
||||
if (bins_Hz_size >= marker_pixel_step) // new pixel fullfilled
|
||||
{
|
||||
if (min_color_power < max_power)
|
||||
add_spectrum_pixel(max_power); // Pixel will represent max_power
|
||||
else
|
||||
add_spectrum_pixel(0); // Filtered out, show black
|
||||
|
||||
max_power = 0;
|
||||
|
||||
if (!pixel_index) // Received indication that a waterfall line has been completed
|
||||
{
|
||||
bins_Hz_size = 0; // Since this is an entire pixel line, we don't carry "Pixels into next bin"
|
||||
if (mode != LOOKING_GLASS_SINGLEPASS) {
|
||||
f_center = f_center_ini;
|
||||
retune();
|
||||
} else
|
||||
baseband::spectrum_streaming_start();
|
||||
return; // signal a new line
|
||||
// process dc spike if enable
|
||||
if (bin == 119) {
|
||||
uint8_t next_max_power = 0;
|
||||
get_max_power(spectrum, bin + 1, next_max_power);
|
||||
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 (process_bins(&med_max_power) == true)
|
||||
return; // new line signaled, return
|
||||
}
|
||||
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) {
|
||||
f_center += looking_glass_step;
|
||||
@@ -225,36 +222,33 @@ void GlassView::on_range_changed() {
|
||||
f_max = f_max * MHZ_DIV;
|
||||
looking_glass_range = f_max - f_min;
|
||||
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
|
||||
mode = LOOKING_GLASS_SINGLEPASS;
|
||||
offset = 0;
|
||||
offset = 2;
|
||||
bin_length = SCREEN_W;
|
||||
ignore_dc = 0;
|
||||
looking_glass_bandwidth = looking_glass_range;
|
||||
looking_glass_sampling_rate = looking_glass_bandwidth;
|
||||
each_bin_size = looking_glass_bandwidth / SCREEN_W;
|
||||
looking_glass_step = looking_glass_bandwidth;
|
||||
f_center_ini = f_min + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
|
||||
} else { // if ( mode == LOOKING_GLASS_SLOWSCAN || mode == LOOKING_GLASS_FASTSCAN )
|
||||
f_center_ini = f_min + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
|
||||
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
|
||||
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_sampling_rate = LOOKING_GLASS_SLICE_WIDTH_MAX;
|
||||
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
|
||||
}
|
||||
search_span = looking_glass_range / MHZ_DIV;
|
||||
@@ -280,7 +274,7 @@ void GlassView::on_range_changed() {
|
||||
receiver_model.set_squelch_level(0);
|
||||
f_center = f_center_ini; // Reset sweep into first slice
|
||||
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) {
|
||||
@@ -476,7 +470,7 @@ GlassView::GlassView(
|
||||
};
|
||||
|
||||
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
|
||||
nav_.pop();
|
||||
nav_.push<AnalogAudioView>(); // Jump into audio view
|
||||
@@ -500,7 +494,7 @@ GlassView::GlassView(
|
||||
};
|
||||
|
||||
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
|
||||
nav_.pop();
|
||||
nav_.push<AnalogAudioView>(); // Jump into audio view
|
||||
@@ -527,71 +521,47 @@ GlassView::GlassView(
|
||||
}
|
||||
|
||||
void GlassView::load_Presets() {
|
||||
File presets_file; // LOAD /WHIPCALC/ANTENNAS.TXT from microSD
|
||||
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear(); // Start with fresh db
|
||||
if (result.is_valid()) {
|
||||
presets_Default(); // There is no txt, store a default range
|
||||
} else {
|
||||
std::string line; // There is a txt file
|
||||
char one_char[1]; // Read it char by char
|
||||
for (size_t pointer = 0; pointer < presets_file.size(); pointer++) {
|
||||
presets_file.seek(pointer);
|
||||
presets_file.read(one_char, 1);
|
||||
if ((int)one_char[0] > 31) { // ascii space upwards
|
||||
line += one_char[0]; // Add it to the textline
|
||||
} else if (one_char[0] == '\n') { // New Line
|
||||
txtline_process(line); // make sense of this textline
|
||||
line.clear(); // Ready for next textline
|
||||
}
|
||||
File presets_file;
|
||||
auto error = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear();
|
||||
|
||||
if (!error) {
|
||||
auto reader = FileLineReader(presets_file);
|
||||
for (const auto& line : reader) {
|
||||
if (line.length() == 0 || line[0] == '#')
|
||||
continue;
|
||||
|
||||
auto cols = split_string(line, ',');
|
||||
if (cols.size() != 3)
|
||||
continue;
|
||||
|
||||
preset_entry entry{};
|
||||
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();
|
||||
}
|
||||
|
||||
void GlassView::txtline_process(std::string& line) {
|
||||
if (line.find("#") != std::string::npos)
|
||||
return; // Line is just a comment
|
||||
|
||||
size_t comma = line.find(","); // Get first comma position
|
||||
if (comma == std::string::npos)
|
||||
return; // No comma at all
|
||||
|
||||
size_t previous = 0;
|
||||
preset_entry new_preset;
|
||||
|
||||
new_preset.min = std::stoi(line.substr(0, comma));
|
||||
if (!new_preset.min)
|
||||
return; // No frequency!
|
||||
|
||||
previous = comma + 1;
|
||||
comma = line.find(",", previous); // Search for next delimiter
|
||||
if (comma == std::string::npos)
|
||||
return; // No comma at all
|
||||
|
||||
new_preset.max = std::stoi(line.substr(previous, comma - previous));
|
||||
if (!new_preset.max)
|
||||
return; // No frequency!
|
||||
|
||||
new_preset.label = line.substr(comma + 1);
|
||||
if (new_preset.label.size() == 0)
|
||||
return; // No label ?
|
||||
|
||||
presets_db.push_back(new_preset); // Add this preset.
|
||||
}
|
||||
|
||||
void GlassView::populate_Presets() {
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t entries;
|
||||
|
||||
for (preset_entry preset : presets_db) { // go thru all available presets
|
||||
for (const auto& preset : presets_db)
|
||||
entries.emplace_back(preset.label, entries.size());
|
||||
}
|
||||
|
||||
range_presets.set_options(entries);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "receiver_model.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -37,6 +39,7 @@
|
||||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
#define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000
|
||||
#define MHZ_DIV 1000000
|
||||
|
||||
@@ -60,7 +63,7 @@ class GlassView : public View {
|
||||
GlassView& operator=(const GlassView& nav);
|
||||
|
||||
~GlassView();
|
||||
std::string title() const override { return "LookingGlass"; };
|
||||
std::string title() const override { return "Looking Glass"; };
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
@@ -68,6 +71,9 @@ class GlassView : public View {
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_glass", app_settings::Mode::RX};
|
||||
|
||||
struct preset_entry {
|
||||
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);
|
||||
rf::Frequency get_freq_from_bin_pos(uint8_t pos);
|
||||
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();
|
||||
bool move_to_next_position();
|
||||
bool process_bins(uint8_t* powerlevel);
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
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_lna_changed(int32_t v_db);
|
||||
void on_vga_changed(int32_t v_db);
|
||||
|
||||
@@ -30,7 +30,6 @@ using wolfson::wm8731::WM8731;
|
||||
|
||||
#include "tonesets.hpp"
|
||||
#include "portapack_hal.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
@@ -68,6 +67,7 @@ void MicTXView::on_tx_progress(const bool done) {
|
||||
}
|
||||
|
||||
void MicTXView::configure_baseband() {
|
||||
// TODO: Can this use the transmitter model instead?
|
||||
baseband::set_audiotx_config(
|
||||
sampling_rate / 20, // Update vu-meter at 20Hz
|
||||
transmitting ? transmitter_model.channel_bandwidth() : 0,
|
||||
@@ -86,7 +86,7 @@ void MicTXView::set_tx(bool enable) {
|
||||
rxaudio(false); // Then turn off audio RX
|
||||
transmitting = true;
|
||||
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();
|
||||
portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming
|
||||
} 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) {
|
||||
if (is_on) {
|
||||
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_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
|
||||
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
|
||||
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_vga(rx_vga);
|
||||
receiver_model.set_rf_amp(rx_amp);
|
||||
receiver_model.enable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to have a good RX without any ghost inside Mic App
|
||||
audio::output::start();
|
||||
} else { // These incredibly convoluted steps are required for the vumeter to reappear when stopping RX.
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); // This fixes something with AM RX...
|
||||
@@ -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)
|
||||
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 .
|
||||
} 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) {
|
||||
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
|
||||
@@ -308,16 +297,16 @@ MicTXView::MicTXView(
|
||||
|
||||
// options_ak4951_alc_mode.set_selected_index(0);
|
||||
|
||||
tx_frequency = transmitter_model.tuning_frequency();
|
||||
field_frequency.set_value(transmitter_model.tuning_frequency());
|
||||
tx_frequency = transmitter_model.target_frequency();
|
||||
field_frequency.set_value(transmitter_model.target_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
tx_frequency = f;
|
||||
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.
|
||||
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]() {
|
||||
@@ -327,10 +316,10 @@ MicTXView::MicTXView(
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
tx_frequency = f;
|
||||
if (!rx_enabled) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
} else {
|
||||
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);
|
||||
set_dirty();
|
||||
@@ -492,7 +481,7 @@ MicTXView::MicTXView(
|
||||
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)
|
||||
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) {
|
||||
@@ -542,13 +531,13 @@ MicTXView::MicTXView(
|
||||
field_squelch.set_value(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_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-
|
||||
rx_frequency = f;
|
||||
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-
|
||||
focused_ui = 1;
|
||||
@@ -557,7 +546,7 @@ MicTXView::MicTXView(
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
rx_frequency = f;
|
||||
if (rx_enabled)
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
receiver_model.set_target_frequency(f);
|
||||
this->field_rxfrequency.set_value(f);
|
||||
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_baseband_bandwidth(1750000);
|
||||
|
||||
@@ -617,12 +609,11 @@ MicTXView::MicTXView(
|
||||
|
||||
MicTXView::~MicTXView() {
|
||||
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();
|
||||
if (rx_enabled) // Also turn off audio rx if enabled
|
||||
rxaudio(false);
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#define SHORT_UI true
|
||||
#define NORMAL_UI false
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -71,7 +73,7 @@ class MicTXView : public View {
|
||||
void update_vumeter();
|
||||
void do_timing();
|
||||
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 configure_baseband();
|
||||
|
||||
@@ -79,6 +81,15 @@ class MicTXView : public View {
|
||||
|
||||
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 va_enabled{false};
|
||||
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)
|
||||
}};
|
||||
|
||||
// TODO: Use TxFrequencyField
|
||||
FrequencyField field_frequency{
|
||||
{5 * 8, 3 * 8},
|
||||
};
|
||||
@@ -298,6 +310,7 @@ class MicTXView : public View {
|
||||
' ',
|
||||
};
|
||||
|
||||
// TODO: Use RxFrequencyField
|
||||
FrequencyField field_rxfrequency{
|
||||
{10 * 8, (25 * 8) + 2},
|
||||
};
|
||||
|
||||
@@ -38,8 +38,6 @@ class ModemSetupView : public View {
|
||||
std::string title() const override { return "Modem setup"; };
|
||||
|
||||
private:
|
||||
void update_freq(rf::Frequency f);
|
||||
|
||||
Labels labels{
|
||||
{{2 * 8, 11 * 8}, "Baudrate:", Color::light_grey()},
|
||||
{{2 * 8, 13 * 8}, "Mark: Hz", Color::light_grey()},
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
@@ -98,13 +97,8 @@ void MorseView::focus() {
|
||||
}
|
||||
|
||||
MorseView::~MorseView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_morse", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void MorseView::paint(Painter&) {
|
||||
@@ -123,9 +117,6 @@ bool MorseView::start_tx() {
|
||||
}
|
||||
|
||||
progressbar.set_max(symbol_count);
|
||||
|
||||
transmitter_model.set_sampling_rate(1536000U);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
if (modulation == CW) {
|
||||
@@ -206,15 +197,6 @@ MorseView::MorseView(
|
||||
&progressbar,
|
||||
&tx_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_morse", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
// Default settings
|
||||
field_speed.set_value(15); // 15wps
|
||||
field_tone.set_value(700); // 700Hz FM tone
|
||||
@@ -251,9 +233,9 @@ MorseView::MorseView(
|
||||
};
|
||||
|
||||
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) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
#include "volume.hpp"
|
||||
@@ -68,9 +69,12 @@ class MorseView : public View {
|
||||
std::string message{};
|
||||
uint32_t time_units{0};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_morse", app_settings::Mode::TX};
|
||||
|
||||
enum modulation_t {
|
||||
CW = 0,
|
||||
|
||||
@@ -40,11 +40,8 @@ void NRFRxView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
void NRFRxView::update_freq(rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
NRFRxView::NRFRxView(NavigationView& nav) {
|
||||
NRFRxView::NRFRxView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_nrf_rx);
|
||||
|
||||
add_children({&rssi,
|
||||
@@ -56,16 +53,8 @@ NRFRxView::NRFRxView(NavigationView& nav) {
|
||||
&button_modem_setup,
|
||||
&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)
|
||||
update_freq(2480000000);
|
||||
field_frequency.set_value(2480000000);
|
||||
auto def_bell202 = &modem_defs[0];
|
||||
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
|
||||
serial_format_t serial_format;
|
||||
@@ -75,18 +64,7 @@ NRFRxView::NRFRxView(NavigationView& nav) {
|
||||
serial_format.bit_order = LSB_FIRST;
|
||||
persistent_memory::set_serial_format(serial_format);
|
||||
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
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&) {
|
||||
nav.push<ModemSetupView>();
|
||||
@@ -98,8 +76,6 @@ NRFRxView::NRFRxView(NavigationView& nav) {
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::output::start();
|
||||
|
||||
receiver_model.set_sampling_rate(4000000);
|
||||
receiver_model.set_baseband_bandwidth(4000000);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
||||
receiver_model.enable();
|
||||
}
|
||||
@@ -151,10 +127,6 @@ void NRFRxView::on_data(uint32_t value, bool is_data) {
|
||||
}
|
||||
|
||||
NRFRxView::~NRFRxView() {
|
||||
// save app settings
|
||||
app_settings.rx_frequency = field_frequency.value();
|
||||
settings.save("rx_nrf", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
#include "ui.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_record_view.hpp" // DEBUG
|
||||
|
||||
#include "utility.hpp"
|
||||
@@ -46,9 +48,13 @@ class NRFRxView : public View {
|
||||
private:
|
||||
void on_data(uint32_t value, bool is_data);
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
4000000 /* bandwidth */,
|
||||
4000000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_nrf", app_settings::Mode::RX};
|
||||
|
||||
uint8_t console_color{0};
|
||||
uint32_t prev_value{0};
|
||||
@@ -60,15 +66,13 @@ class NRFRxView : public View {
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, 0 * 16}};
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
Channel channel{
|
||||
{21 * 8, 5, 6 * 8, 4},
|
||||
};
|
||||
{21 * 8, 5, 6 * 8, 4}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
RxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
};
|
||||
nav_};
|
||||
|
||||
Button button_modem_setup{
|
||||
{240 - 12 * 8, 1 * 16, 96, 24},
|
||||
@@ -77,9 +81,6 @@ class NRFRxView : public View {
|
||||
Console console{
|
||||
{0, 4 * 16, 240, 240}};
|
||||
|
||||
void update_freq(rf::Frequency f);
|
||||
// void on_data_afsk(const AFSKDataMessage& message);
|
||||
|
||||
MessageHandlerRegistration message_handler_packet{
|
||||
Message::ID::AFSKData,
|
||||
[this](Message* const p) {
|
||||
|
||||
@@ -129,9 +129,7 @@ void NumbersStationView::start_tx() {
|
||||
|
||||
prepare_audio();
|
||||
|
||||
transmitter_model.set_sampling_rate(1536000U);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_audiotx_data(
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "message.hpp"
|
||||
#include "file.hpp"
|
||||
#include "io_wave.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -54,6 +55,11 @@ class NumbersStationView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
|
||||
// Sequencing state machine
|
||||
enum segments {
|
||||
IDLE = 0,
|
||||
|
||||
@@ -77,11 +77,9 @@ void NuoptixView::transmit(bool setup) {
|
||||
timecode = number_timecode.value();
|
||||
}
|
||||
|
||||
transmitter_model.set_sampling_rate(1536000U);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_lna(40);
|
||||
transmitter_model.set_vga(40);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
dtmf_message[0] = '*'; // "Pre-tone for restart" method #1
|
||||
@@ -156,9 +154,9 @@ NuoptixView::NuoptixView(
|
||||
number_timecode.set_value(1);
|
||||
|
||||
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) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
transmitter_model.target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "message.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
#define NUOPTIX_TONE_LENGTH ((TONES_SAMPLERATE * 0.049) - 1) // 49ms
|
||||
|
||||
@@ -54,9 +55,13 @@ class NuoptixView : public View {
|
||||
IMPROVISE
|
||||
};
|
||||
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
1536000 /* sampling rate */
|
||||
};
|
||||
|
||||
tx_modes tx_mode{IDLE};
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void transmit(bool setup);
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyleft (ↄ) 2022 NotPike
|
||||
* Copyright (C) 2023 Kyle Reed, zxkmm
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -22,10 +23,13 @@
|
||||
*/
|
||||
|
||||
#include "ui_playlist.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "ui_fileman.hpp"
|
||||
#include "convert.hpp"
|
||||
#include "file_reader.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack.hpp"
|
||||
@@ -34,223 +38,326 @@
|
||||
#include <fstream>
|
||||
|
||||
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 {
|
||||
|
||||
void PlaylistView::set_ready() {
|
||||
ready_signal = true;
|
||||
}
|
||||
// TODO: consolidate extesions into a shared header?
|
||||
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;
|
||||
|
||||
auto error = playlist_file.open(playlist_path.string());
|
||||
if (!error.is_valid()) {
|
||||
std::string line;
|
||||
char one_char[1];
|
||||
for (size_t pointer = 0; pointer < playlist_file.size(); pointer++) {
|
||||
playlist_file.seek(pointer);
|
||||
playlist_file.read(one_char, 1);
|
||||
if ((int)one_char[0] >= ' ') {
|
||||
line += one_char[0];
|
||||
} else if (one_char[0] == '\n') {
|
||||
total_tracks++;
|
||||
txtline_process(line);
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
if (line.length() > 0) {
|
||||
total_tracks++;
|
||||
txtline_process(line);
|
||||
}
|
||||
|
||||
if (error)
|
||||
return;
|
||||
|
||||
auto reader = FileLineReader(playlist_file);
|
||||
for (const auto& line : reader) {
|
||||
if (line.length() == 0 || line[0] == '#')
|
||||
continue; // Empty or comment line.
|
||||
|
||||
auto cols = split_string(line, ',');
|
||||
auto entry = load_entry(trim(cols[0]));
|
||||
|
||||
if (!entry)
|
||||
continue;
|
||||
|
||||
// Read optional delay value.
|
||||
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) {
|
||||
playlist_entry new_item;
|
||||
rf::Frequency f;
|
||||
size_t previous = 0;
|
||||
size_t current = line.find(',');
|
||||
std::string freqs = line.substr(0, current);
|
||||
previous = current + 1;
|
||||
current = line.find(',', previous);
|
||||
std::string file = line.substr(previous, current - previous);
|
||||
previous = current + 1;
|
||||
current = line.find(',', previous);
|
||||
uint32_t sample_rate = strtoll(line.substr(previous).c_str(), nullptr, 10);
|
||||
Optional<PlaylistView::playlist_entry> PlaylistView::load_entry(fs::path&& path) {
|
||||
File capture_file;
|
||||
|
||||
f = strtoll(freqs.c_str(), nullptr, 0);
|
||||
new_item.replay_frequency = f;
|
||||
new_item.replay_file = "/" + file;
|
||||
new_item.sample_rate = sample_rate;
|
||||
new_item.next_delay = 0;
|
||||
auto error = capture_file.open(path);
|
||||
if (error)
|
||||
return {};
|
||||
|
||||
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) {
|
||||
File data_file;
|
||||
// Get file size
|
||||
auto data_open_error = data_file.open("/" + new_file_path.string());
|
||||
if (!data_open_error.is_valid()) {
|
||||
track_number++;
|
||||
} else if (data_open_error.is_valid()) {
|
||||
file_error("C16 file\n" + new_file_path.string() + "\nread error.");
|
||||
void PlaylistView::on_file_changed(const fs::path& new_file_path) {
|
||||
stop();
|
||||
|
||||
current_index_ = 0;
|
||||
playlist_path_ = new_file_path;
|
||||
playlist_db_.clear();
|
||||
load_file(playlist_path_);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
file_path = new_file_path;
|
||||
field_frequency.set_value(replay_frequency);
|
||||
|
||||
sample_rate = replay_sample_rate;
|
||||
|
||||
text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz");
|
||||
|
||||
auto file_size = data_file.size();
|
||||
auto duration = (file_size * 1000) / (2 * 2 * sample_rate);
|
||||
|
||||
progressbar.set_max(file_size);
|
||||
text_filename.set(file_path.filename().string().substr(0, 12));
|
||||
text_duration.set(to_string_time_ms(duration));
|
||||
// text_track.set(std::to_string(track_number) + "/" + std::to_string(total_tracks));
|
||||
|
||||
button_play.focus();
|
||||
auto open_view = nav_.push<FileLoadView>(".PPL");
|
||||
open_view->push_dir(u"PLAYLIST");
|
||||
open_view->on_changed = [this](fs::path new_file_path) {
|
||||
on_file_changed(new_file_path);
|
||||
};
|
||||
}
|
||||
|
||||
void PlaylistView::on_tx_progress(const uint32_t progress) {
|
||||
progressbar.set_value(progress);
|
||||
void PlaylistView::save_file(bool show_dialogs) {
|
||||
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() {
|
||||
button_open.focus();
|
||||
void PlaylistView::add_entry(fs::path&& path) {
|
||||
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) {
|
||||
nav_.display_modal("Error", "Error for \n" + file_path.string() + "\n" + error_message);
|
||||
void PlaylistView::delete_entry() {
|
||||
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 {
|
||||
return (bool)replay_thread;
|
||||
return replay_thread_ != nullptr;
|
||||
}
|
||||
|
||||
bool PlaylistView::loop() const {
|
||||
return (bool)playlist_db.size();
|
||||
bool PlaylistView::at_end() const {
|
||||
return current_index_ >= playlist_db_.size();
|
||||
}
|
||||
|
||||
void PlaylistView::toggle() {
|
||||
if (is_active()) {
|
||||
stop(false);
|
||||
total_tracks = 0;
|
||||
track_number = 0;
|
||||
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();
|
||||
}
|
||||
}
|
||||
if (is_active())
|
||||
stop();
|
||||
else
|
||||
start();
|
||||
}
|
||||
|
||||
void PlaylistView::start() {
|
||||
stop(false);
|
||||
if (playlist_db_.empty())
|
||||
return; // Nothing to do.
|
||||
|
||||
playlist_entry item = playlist_db.front();
|
||||
playlist_db.pop_front();
|
||||
// playlist_entry item = playlist_db[0];
|
||||
// for (playlist_entry item : playlist_db) {
|
||||
// file_path = item.replay_file;
|
||||
// rf::Frequency replay_frequency = strtoll(item.replay_frequency.c_str(),nullptr,10);
|
||||
on_file_changed(item.replay_file, item.replay_frequency, item.sample_rate);
|
||||
on_target_frequency_changed(item.replay_frequency);
|
||||
current_index_ = 0u;
|
||||
send_current_track();
|
||||
}
|
||||
|
||||
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>();
|
||||
auto open_error = p->open(file_path);
|
||||
if (open_error.is_valid()) {
|
||||
file_error("Illegal grammar");
|
||||
return; // Fixes TX bug if there's a file error
|
||||
} else {
|
||||
reader = std::move(p);
|
||||
// Reset to the 0 once at the end to prevent current_index_
|
||||
// from being outside the bounds of playlist_db_ when painting.
|
||||
if (at_end()) {
|
||||
current_index_ = 0;
|
||||
return check_loop.value(); // Keep going if looping.
|
||||
}
|
||||
|
||||
if (reader) {
|
||||
button_play.set_bitmap(&bitmap_stop);
|
||||
baseband::set_sample_rate(sample_rate * 8);
|
||||
return true;
|
||||
}
|
||||
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
read_size, buffer_count,
|
||||
&ready_signal,
|
||||
[](uint32_t return_code) {
|
||||
ReplayThreadDoneMessage message{return_code};
|
||||
EventDispatcher::send_message(message);
|
||||
});
|
||||
/* Transmits the current_entry_ */
|
||||
void PlaylistView::send_current_track() {
|
||||
// Prepare to send a file.
|
||||
replay_thread_.reset();
|
||||
transmitter_model.disable();
|
||||
ready_signal_ = false;
|
||||
|
||||
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.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) {
|
||||
if (is_active()) {
|
||||
replay_thread.reset();
|
||||
}
|
||||
void PlaylistView::stop() {
|
||||
// This terminates the underlying chThread.
|
||||
replay_thread_.reset();
|
||||
transmitter_model.disable();
|
||||
|
||||
// TODO: the logic here could be more beautiful but maybe they are all same for compiler anyway....
|
||||
// Notes of the logic here in case if it needed to be changed in the future:
|
||||
// 1. check_loop.value() is for the LOOP checkbox
|
||||
// 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;
|
||||
// Reset the transmit progress bar.
|
||||
progressbar_transmit.set_value(0);
|
||||
update_ui();
|
||||
}
|
||||
|
||||
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) {
|
||||
stop(true);
|
||||
} else if (return_code == ReplayThread::READ_ERROR) {
|
||||
stop(false);
|
||||
file_error("Replay thread read error");
|
||||
if (next_track()) {
|
||||
send_current_track();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
progressbar.set_value(0);
|
||||
if (return_code == ReplayThread::READ_ERROR)
|
||||
show_file_error(current()->path, "Replay read failed.");
|
||||
|
||||
stop();
|
||||
}
|
||||
|
||||
PlaylistView::PlaylistView(
|
||||
@@ -259,47 +366,101 @@ PlaylistView::PlaylistView(
|
||||
baseband::run_image(portapack::spi_flash::image_tag_replay);
|
||||
|
||||
add_children({
|
||||
&button_open,
|
||||
&text_filename,
|
||||
&text_sample_rate,
|
||||
&text_duration,
|
||||
&progressbar,
|
||||
&progressbar_track,
|
||||
&progressbar_transmit,
|
||||
&field_frequency,
|
||||
&tx_view, // this handles now the previous rfgain, rfamp
|
||||
&tx_view,
|
||||
&check_loop,
|
||||
&button_play,
|
||||
// &text_track,
|
||||
// TODO: add track number
|
||||
&text_track,
|
||||
&button_prev,
|
||||
&button_add,
|
||||
&button_delete,
|
||||
&button_open,
|
||||
&button_save,
|
||||
&button_next,
|
||||
&waterfall,
|
||||
});
|
||||
|
||||
field_frequency.set_value(target_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
waterfall.show_audio_spectrum_view(false);
|
||||
|
||||
field_frequency.set_value(100'000'000);
|
||||
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]() {
|
||||
// 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.on_edit = [this]() {
|
||||
auto freq_view = nav_.push<FrequencyKeypadView>(field_frequency.value());
|
||||
freq_view->on_changed = [this](rf::Frequency f) {
|
||||
field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_frequency.set_step(5000);
|
||||
|
||||
button_play.on_select = [this](ImageButton&) {
|
||||
this->toggle();
|
||||
toggle();
|
||||
};
|
||||
|
||||
button_open.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".PPL");
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
now_play_list_file = new_file_path;
|
||||
load_file(new_file_path);
|
||||
button_add.on_select = [this, &nav]() {
|
||||
if (is_active())
|
||||
return;
|
||||
auto open_view = nav_.push<FileLoadView>(".C16");
|
||||
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() {
|
||||
@@ -307,33 +468,26 @@ PlaylistView::~PlaylistView() {
|
||||
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() {
|
||||
stop(false);
|
||||
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
|
||||
// it's being shown or hidden.
|
||||
stop();
|
||||
waterfall.on_hide();
|
||||
View::on_hide();
|
||||
}
|
||||
|
||||
void PlaylistView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(),
|
||||
new_parent_rect.height() - header_height};
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
void PlaylistView::on_target_frequency_changed(rf::Frequency f) {
|
||||
set_target_frequency(f);
|
||||
}
|
||||
|
||||
void PlaylistView::set_target_frequency(const rf::Frequency new_value) {
|
||||
persistent_memory::set_tuned_frequency(new_value);
|
||||
;
|
||||
}
|
||||
|
||||
rf::Frequency PlaylistView::target_frequency() const {
|
||||
return persistent_memory::tuned_frequency();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2023 Kyle Reed, zxkmm
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -23,141 +24,193 @@
|
||||
#define SHORT_UI true
|
||||
#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_receiver.hpp"
|
||||
#include "replay_thread.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <deque>
|
||||
#include "ui_transmitter.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class PlaylistView : public View {
|
||||
public:
|
||||
PlaylistView(NavigationView& nav);
|
||||
|
||||
PlaylistView(NavigationView& nav, const std::filesystem::path& path);
|
||||
~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 set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Playlist"; };
|
||||
std::string title() const override { return "Replay"; };
|
||||
|
||||
private:
|
||||
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 {
|
||||
rf::Frequency replay_frequency{0};
|
||||
std::string replay_file{};
|
||||
uint32_t sample_rate{};
|
||||
uint32_t next_delay{};
|
||||
std::filesystem::path path{};
|
||||
capture_metadata metadata{};
|
||||
File::Size file_size{};
|
||||
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);
|
||||
void txtline_process(std::string&);
|
||||
void on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate);
|
||||
void on_target_frequency_changed(rf::Frequency f);
|
||||
void on_tx_progress(const uint32_t progress);
|
||||
void set_target_frequency(const rf::Frequency new_value);
|
||||
rf::Frequency target_frequency() const;
|
||||
std::unique_ptr<ReplayThread> replay_thread_{};
|
||||
bool ready_signal_{}; // Used to signal the ReplayThread.
|
||||
|
||||
size_t current_index_{0};
|
||||
bool playlist_dirty_{};
|
||||
std::vector<playlist_entry> playlist_db_{};
|
||||
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 start();
|
||||
void stop(const bool do_loop);
|
||||
bool is_active() const;
|
||||
bool loop() const;
|
||||
void set_ready();
|
||||
void handle_replay_thread_done(const uint32_t return_code);
|
||||
void file_error(std::string error_message);
|
||||
bool next_track();
|
||||
void send_current_track();
|
||||
void stop();
|
||||
|
||||
std::filesystem::path file_path{};
|
||||
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{};
|
||||
void update_ui();
|
||||
|
||||
Button button_open{
|
||||
{0 * 8, 0 * 16, 10 * 8, 2 * 16},
|
||||
"Open file"};
|
||||
/* There are called by Message handlers. */
|
||||
void on_tx_progress(uint32_t progress);
|
||||
void handle_replay_thread_done(uint32_t return_code);
|
||||
|
||||
Text text_filename{
|
||||
{11 * 8, 0 * 16, 12 * 8, 16},
|
||||
"-"};
|
||||
Text text_sample_rate{
|
||||
{24 * 8, 0 * 16, 6 * 8, 16},
|
||||
"-"};
|
||||
|
||||
Text text_duration{
|
||||
{11 * 8, 1 * 16, 6 * 8, 16},
|
||||
"-"};
|
||||
ProgressBar progressbar{
|
||||
{18 * 8, 1 * 16, 12 * 8, 16}};
|
||||
{0 * 8, 0 * 16, 30 * 8, 16}};
|
||||
|
||||
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{
|
||||
// new handling of NumberField field_rfgain, NumberField field_rfamp
|
||||
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)
|
||||
};
|
||||
9 * 8, 1 * 8, SHORT_UI};
|
||||
|
||||
Checkbox check_loop{
|
||||
{21 * 8, 2 * 16},
|
||||
4,
|
||||
"Loop",
|
||||
true};
|
||||
|
||||
ImageButton button_play{
|
||||
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
|
||||
&bitmap_play,
|
||||
Color::green(),
|
||||
Color::black()};
|
||||
// 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{
|
||||
Message::ID::ReplayThreadDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
this->handle_replay_thread_done(message.return_code);
|
||||
[this](const Message* p) {
|
||||
auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
handle_replay_thread_done(message.return_code);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal{
|
||||
Message::ID::RequestSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const RequestSignalMessage*>(p);
|
||||
[this](const Message* p) {
|
||||
auto message = static_cast<const RequestSignalMessage*>(p);
|
||||
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
|
||||
this->set_ready();
|
||||
ready_signal_ = true;
|
||||
}
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress);
|
||||
[this](const Message* p) {
|
||||
auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
on_tx_progress(message.progress);
|
||||
}};
|
||||
};
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "ui_pocsag_tx.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
|
||||
@@ -39,13 +38,8 @@ void POCSAGTXView::focus() {
|
||||
}
|
||||
|
||||
POCSAGTXView::~POCSAGTXView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_pocsag", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
transmitter_model.set_sampling_rate(2280000);
|
||||
transmitter_model.set_rf_amp(true);
|
||||
transmitter_model.set_lna(40);
|
||||
transmitter_model.set_vga(40);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
uint8_t* data_ptr = shared_memory.bb_data.data;
|
||||
@@ -143,15 +135,6 @@ POCSAGTXView::POCSAGTXView(
|
||||
&progressbar,
|
||||
&tx_view});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_pocsag", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
|
||||
options_bitrate.set_selected_index(1); // 1200bps
|
||||
options_type.set_selected_index(0); // Address only
|
||||
|
||||
@@ -172,9 +155,9 @@ POCSAGTXView::POCSAGTXView(
|
||||
};
|
||||
|
||||
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) {
|
||||
transmitter_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "message.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "pocsag.hpp"
|
||||
|
||||
using namespace pocsag;
|
||||
@@ -58,6 +59,13 @@ class POCSAGTXView : public View {
|
||||
std::string message{};
|
||||
NavigationView& nav_;
|
||||
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
2280000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_pocsag", app_settings::Mode::TX};
|
||||
|
||||
BCHCode BCH_code{
|
||||
{1, 0, 1, 0, 0, 1},
|
||||
5,
|
||||
@@ -65,10 +73,6 @@ class POCSAGTXView : public View {
|
||||
21,
|
||||
2};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
|
||||
void on_set_text(NavigationView& nav);
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
bool start_tx();
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
@@ -160,13 +159,8 @@ void RDSView::focus() {
|
||||
}
|
||||
|
||||
RDSView::~RDSView() {
|
||||
// save app settings
|
||||
app_settings.tx_frequency = transmitter_model.tuning_frequency();
|
||||
settings.save("tx_rds", &app_settings);
|
||||
|
||||
transmitter_model.disable();
|
||||
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
|
||||
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void RDSView::start_tx() {
|
||||
@@ -193,8 +187,6 @@ void RDSView::start_tx() {
|
||||
else
|
||||
frame_datetime.clear();
|
||||
|
||||
transmitter_model.set_sampling_rate(2280000U);
|
||||
transmitter_model.set_baseband_bandwidth(1750000);
|
||||
transmitter_model.enable();
|
||||
|
||||
tx_thread = std::make_unique<RDSThread>(frames);
|
||||
@@ -218,14 +210,6 @@ RDSView::RDSView(
|
||||
&tx_view,
|
||||
});
|
||||
|
||||
// load app settings
|
||||
auto rc = settings.load("tx_rds", &app_settings);
|
||||
if (rc == SETTINGS_OK) {
|
||||
transmitter_model.set_rf_amp(app_settings.tx_amp);
|
||||
transmitter_model.set_channel_bandwidth(app_settings.channel_bandwidth);
|
||||
transmitter_model.set_tuning_frequency(app_settings.tx_frequency);
|
||||
transmitter_model.set_tx_gain(app_settings.tx_gain);
|
||||
}
|
||||
check_TP.set_value(true);
|
||||
|
||||
sym_pi_code.set_sym(0, 0xF);
|
||||
@@ -239,9 +223,9 @@ RDSView::RDSView(
|
||||
options_pty.set_selected_index(0); // None
|
||||
|
||||
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) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ui_textentry.hpp"
|
||||
#include "ui_tabview.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "rds.hpp"
|
||||
|
||||
using namespace rds;
|
||||
@@ -140,9 +141,12 @@ class RDSView : public View {
|
||||
NavigationView& nav_;
|
||||
RDS_flags rds_flags{};
|
||||
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
TxRadioState radio_state_{
|
||||
1750000 /* bandwidth */,
|
||||
2280000 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_rds", app_settings::Mode::TX};
|
||||
|
||||
std::vector<RDSGroup> frame_psn{};
|
||||
std::vector<RDSGroup> frame_radiotext{};
|
||||
|
||||
@@ -30,18 +30,33 @@ using portapack::memory::map::backup_ram;
|
||||
|
||||
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() {
|
||||
audio::output::stop();
|
||||
recon_stop_recording();
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio::output::stop();
|
||||
// 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) {
|
||||
// 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
|
||||
std::vector<freqman_entry>().swap(frequency_list);
|
||||
} else {
|
||||
} else
|
||||
frequency_list.shrink_to_fit();
|
||||
}
|
||||
freqlist_cleared_for_ui_action = true;
|
||||
}
|
||||
|
||||
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
|
||||
if (!result.is_valid()) {
|
||||
char one_char[1]; // Read it char by char
|
||||
std::string line; // and put read line in here
|
||||
bool found = false;
|
||||
char one_char[1]{}; // Read it char by char
|
||||
std::string line{}; // and put read line in here
|
||||
bool found{false};
|
||||
for (size_t pointer = 0; pointer < recon_file.size(); pointer++) {
|
||||
recon_file.seek(pointer);
|
||||
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() {
|
||||
File settings_file;
|
||||
size_t length, file_position = 0;
|
||||
char* pos;
|
||||
char* line_start;
|
||||
char* line_end;
|
||||
char file_data[257];
|
||||
|
||||
uint32_t it = 0;
|
||||
uint32_t nb_params = RECON_SETTINGS_NB_PARAMS;
|
||||
std::string params[RECON_SETTINGS_NB_PARAMS];
|
||||
File settings_file{};
|
||||
size_t length{0};
|
||||
size_t file_position{0};
|
||||
char* pos{NULL};
|
||||
char* line_start{NULL};
|
||||
char* line_end{NULL};
|
||||
char file_data[257]{};
|
||||
uint32_t it{0};
|
||||
uint32_t nb_params{RECON_SETTINGS_NB_PARAMS};
|
||||
std::string params[RECON_SETTINGS_NB_PARAMS]{};
|
||||
|
||||
make_new_directory(u"SETTINGS");
|
||||
|
||||
@@ -228,8 +243,8 @@ bool ReconView::recon_save_config_to_sd() {
|
||||
|
||||
make_new_directory(u"SETTINGS");
|
||||
|
||||
auto result = settings_file.create(RECON_CFG_FILE);
|
||||
if (result.is_valid())
|
||||
auto error = settings_file.create(RECON_CFG_FILE);
|
||||
if (error)
|
||||
return false;
|
||||
settings_file.write_line(input_file);
|
||||
settings_file.write_line(output_file);
|
||||
@@ -242,7 +257,8 @@ bool ReconView::recon_save_config_to_sd() {
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
@@ -275,10 +291,6 @@ void ReconView::recon_redraw() {
|
||||
} else if (freq_lock >= recon_lock_nb_match) {
|
||||
big_display.set_style(&Styles::green);
|
||||
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()) {
|
||||
@@ -291,29 +303,18 @@ void ReconView::recon_redraw() {
|
||||
void ReconView::handle_retune() {
|
||||
if (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 (last_entry.modulation != frequency_list[current_index].modulation && frequency_list[current_index].modulation >= 0) {
|
||||
last_entry.modulation = frequency_list[current_index].modulation;
|
||||
field_mode.set_selected_index(frequency_list[current_index].modulation);
|
||||
last_entry.bandwidth = -1;
|
||||
}
|
||||
// Set bandwidth if any
|
||||
if (last_entry.bandwidth != frequency_list[current_index].bandwidth && frequency_list[current_index].bandwidth >= 0) {
|
||||
last_entry.bandwidth = frequency_list[current_index].bandwidth;
|
||||
switch (frequency_list[current_index].modulation) {
|
||||
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);
|
||||
field_bw.set_selected_index(frequency_list[current_index].bandwidth);
|
||||
}
|
||||
if (last_entry.step != frequency_list[current_index].step && frequency_list[current_index].step >= 0) {
|
||||
last_entry.step = frequency_list[current_index].step;
|
||||
@@ -361,18 +362,21 @@ void ReconView::focus() {
|
||||
}
|
||||
|
||||
ReconView::~ReconView() {
|
||||
// save app config
|
||||
recon_stop_recording();
|
||||
recon_save_config_to_sd();
|
||||
// save app common settings
|
||||
settings.save("recon", &app_settings);
|
||||
|
||||
audio::output::stop();
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio::output::stop();
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
ReconView::ReconView(NavigationView& 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,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
@@ -380,11 +384,12 @@ ReconView::ReconView(NavigationView& nav)
|
||||
&field_volume,
|
||||
&field_bw,
|
||||
&field_squelch,
|
||||
&field_nblocks,
|
||||
&field_wait,
|
||||
&field_lock_wait,
|
||||
&button_config,
|
||||
&button_scanner_mode,
|
||||
&button_looking_glass,
|
||||
&button_loop_config,
|
||||
&file_name,
|
||||
&rssi,
|
||||
&text_cycle,
|
||||
@@ -399,6 +404,7 @@ ReconView::ReconView(NavigationView& nav)
|
||||
&button_manual_end,
|
||||
&button_manual_recon,
|
||||
&field_mode,
|
||||
&field_recon_match_mode,
|
||||
&step_mode,
|
||||
&button_pause,
|
||||
&button_audio_app,
|
||||
@@ -406,12 +412,18 @@ ReconView::ReconView(NavigationView& nav)
|
||||
&button_dir,
|
||||
&button_restart,
|
||||
&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;
|
||||
// HELPER: Pre-setting a manual range, based on stored frequency
|
||||
rf::Frequency stored_freq = persistent_memory::tuned_frequency();
|
||||
receiver_model.set_tuning_frequency(stored_freq);
|
||||
rf::Frequency stored_freq = receiver_model.target_frequency();
|
||||
if (stored_freq - OneMHz > 0)
|
||||
frequency_range.min = stored_freq - OneMHz;
|
||||
else
|
||||
@@ -422,6 +434,7 @@ ReconView::ReconView(NavigationView& nav)
|
||||
else
|
||||
frequency_range.max = MAX_UFREQ;
|
||||
button_manual_end.set_text(to_string_short_freq(frequency_range.max));
|
||||
|
||||
// Loading settings
|
||||
autostart = persistent_memory::recon_autostart_recon();
|
||||
autosave = persistent_memory::recon_autosave_freqs();
|
||||
@@ -431,16 +444,7 @@ ReconView::ReconView(NavigationView& nav)
|
||||
load_ranges = persistent_memory::recon_load_ranges();
|
||||
load_hamradios = persistent_memory::recon_load_hamradios();
|
||||
update_ranges = persistent_memory::recon_update_ranges_when_recon();
|
||||
if (sd_card_mounted) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
auto_record_locked = persistent_memory::recon_auto_record_locked();
|
||||
|
||||
button_manual_start.on_select = [this, &nav](ButtonWithEncoder& button) {
|
||||
clear_freqlist_for_ui_action();
|
||||
@@ -530,8 +534,10 @@ ReconView::ReconView(NavigationView& nav)
|
||||
} else {
|
||||
if (!recon) {
|
||||
recon_resume();
|
||||
user_pause = false;
|
||||
} else {
|
||||
recon_pause();
|
||||
user_pause = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -547,10 +553,10 @@ ReconView::ReconView(NavigationView& nav)
|
||||
nav_.push<AnalogAudioView>();
|
||||
};
|
||||
|
||||
button_looking_glass.on_select = [this](Button&) {
|
||||
nav_.pop();
|
||||
nav_.push<GlassView>();
|
||||
button_loop_config.on_select = [this](Button&) {
|
||||
set_loop_config(!continuous);
|
||||
};
|
||||
set_loop_config(continuous);
|
||||
|
||||
rssi.set_focusable(true);
|
||||
rssi.set_peak(true, 500);
|
||||
@@ -559,17 +565,18 @@ ReconView::ReconView(NavigationView& nav)
|
||||
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&) {
|
||||
if (frequency_list.size() > 0 && current_index >= 0 && (unsigned)current_index < frequency_list.size()) {
|
||||
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
|
||||
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
|
||||
receiver_model.set_tuning_frequency(frequency_list[current_index].frequency_b);
|
||||
receiver_model.set_target_frequency(frequency_list[current_index].frequency_b);
|
||||
} else {
|
||||
// it's single or range so we us actual tuned frequency
|
||||
transmitter_model.set_tuning_frequency(freq);
|
||||
receiver_model.set_tuning_frequency(freq);
|
||||
transmitter_model.set_target_frequency(freq);
|
||||
receiver_model.set_target_frequency(freq);
|
||||
}
|
||||
}
|
||||
// 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
|
||||
if (scanner_mode) {
|
||||
File freqman_file;
|
||||
File freqman_file{};
|
||||
delete_file(freq_file_path);
|
||||
auto result = freqman_file.create(freq_file_path);
|
||||
if (!result.is_valid()) {
|
||||
@@ -623,10 +630,10 @@ ReconView::ReconView(NavigationView& nav)
|
||||
}
|
||||
} else if (manual_mode) // only remove from output
|
||||
{
|
||||
File recon_file;
|
||||
File tmp_recon_file;
|
||||
std::string tmp_freq_file_path = freq_file_path + ".TMP";
|
||||
std::string frequency_to_add;
|
||||
File recon_file{};
|
||||
File tmp_recon_file{};
|
||||
std::string tmp_freq_file_path{freq_file_path + ".TMP"};
|
||||
std::string frequency_to_add{};
|
||||
|
||||
freqman_entry entry = frequency_list[current_index];
|
||||
entry.frequency_a = freq;
|
||||
@@ -643,8 +650,8 @@ ReconView::ReconView(NavigationView& nav)
|
||||
bool found = false;
|
||||
result = recon_file.open(freq_file_path); // First recon if freq is already in txt
|
||||
if (!result.is_valid()) {
|
||||
char one_char[1]; // Read it char by char
|
||||
std::string line; // and put read line in here
|
||||
char one_char[1]{}; // Read it char by char
|
||||
std::string line{}; // and put read line in here
|
||||
for (size_t pointer = 0; pointer < recon_file.size(); pointer++) {
|
||||
recon_file.seek(pointer);
|
||||
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) {
|
||||
text_cycle.set_text(" ");
|
||||
@@ -694,8 +701,8 @@ ReconView::ReconView(NavigationView& nav)
|
||||
} else if (frequency_range.min > frequency_range.max) {
|
||||
nav_.display_modal("Error", "END freq\nis lower than START");
|
||||
} 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
|
||||
// swap is the only way to achieve a perfect memory liberation
|
||||
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_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]
|
||||
@@ -810,33 +820,29 @@ ReconView::ReconView(NavigationView& nav)
|
||||
};
|
||||
|
||||
button_config.on_select = [this, &nav](Button&) {
|
||||
if (is_recording) // disabling config while recording
|
||||
return;
|
||||
clear_freqlist_for_ui_action();
|
||||
|
||||
auto open_view = nav.push<ReconSetupView>(input_file, output_file, recon_lock_duration, recon_lock_nb_match, recon_match_mode);
|
||||
freq_lock = 0;
|
||||
timer = 0;
|
||||
auto open_view = nav.push<ReconSetupView>(input_file, output_file);
|
||||
open_view->on_changed = [this](std::vector<std::string> result) {
|
||||
freqlist_cleared_for_ui_action = false;
|
||||
input_file = result[0];
|
||||
output_file = result[1];
|
||||
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();
|
||||
|
||||
autosave = persistent_memory::recon_autosave_freqs();
|
||||
autostart = persistent_memory::recon_autostart_recon();
|
||||
continuous = persistent_memory::recon_continuous();
|
||||
filedelete = persistent_memory::recon_clear_output();
|
||||
load_freqs = persistent_memory::recon_load_freqs();
|
||||
load_ranges = persistent_memory::recon_load_ranges();
|
||||
load_hamradios = persistent_memory::recon_load_hamradios();
|
||||
update_ranges = persistent_memory::recon_update_ranges_when_recon();
|
||||
|
||||
field_wait.set_value(wait);
|
||||
field_lock_wait.set_value(recon_lock_duration);
|
||||
colorize_waits();
|
||||
auto_record_locked = persistent_memory::recon_auto_record_locked();
|
||||
|
||||
frequency_file_load(false);
|
||||
freqlist_cleared_for_ui_action = false;
|
||||
|
||||
if (autostart) {
|
||||
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) {
|
||||
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();
|
||||
};
|
||||
|
||||
@@ -869,9 +890,11 @@ ReconView::ReconView(NavigationView& nav)
|
||||
recon_load_config_from_sd();
|
||||
freq_file_path = "/FREQMAN/" + output_file + ".TXT";
|
||||
|
||||
field_recon_match_mode.set_selected_index(recon_match_mode);
|
||||
field_squelch.set_value(squelch);
|
||||
field_wait.set_value(wait);
|
||||
field_lock_wait.set_value(recon_lock_duration);
|
||||
field_nblocks.set_value(recon_lock_nb_match);
|
||||
colorize_waits();
|
||||
|
||||
// fill modulation and step options
|
||||
@@ -879,9 +902,8 @@ ReconView::ReconView(NavigationView& nav)
|
||||
freqman_set_step_option(step_mode);
|
||||
|
||||
// set radio
|
||||
change_mode(AM_MODULATION); // start on AM.
|
||||
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
|
||||
change_mode(AM_MODULATION); // start on AM.
|
||||
field_mode.set_by_value(AM_MODULATION); // reflect the mode into the manual selector
|
||||
|
||||
if (filedelete) {
|
||||
delete_file(freq_file_path);
|
||||
@@ -898,40 +920,36 @@ ReconView::ReconView(NavigationView& nav)
|
||||
|
||||
void ReconView::frequency_file_load(bool 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
|
||||
// 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
|
||||
std::vector<freqman_entry>().swap(frequency_list); // clear the existing frequency list (expected behavior)
|
||||
std::string file_input = input_file; // default recon mode
|
||||
std::string file_input = input_file; // default recon mode
|
||||
if (scanner_mode) {
|
||||
file_input = output_file;
|
||||
file_name.set_style(&Styles::red);
|
||||
button_scanner_mode.set_style(&Styles::red);
|
||||
desc_cycle.set_style(&Styles::red);
|
||||
button_scanner_mode.set_text("SCANNER");
|
||||
} else {
|
||||
file_name.set_style(&Styles::blue);
|
||||
button_scanner_mode.set_style(&Styles::blue);
|
||||
desc_cycle.set_style(&Styles::blue);
|
||||
button_scanner_mode.set_text("RECON");
|
||||
}
|
||||
desc_cycle.set_style(&Styles::white);
|
||||
if (!load_freqman_file_ex(file_input, frequency_list, load_freqs, load_ranges, load_hamradios, RECON_FREQMAN_MAX_PER_FILE)) {
|
||||
if (!load_freqman_file(file_input, frequency_list, load_freqs, load_ranges, load_hamradios)) {
|
||||
file_name.set_style(&Styles::red);
|
||||
desc_cycle.set_style(&Styles::red);
|
||||
desc_cycle.set(" NO " + file_input + ".TXT FILE ...");
|
||||
file_name.set("=> NO DATA");
|
||||
} else {
|
||||
file_name.set("=> " + file_input);
|
||||
file_name.set(file_input + "=>" + output_file);
|
||||
if (frequency_list.size() == 0) {
|
||||
file_name.set_style(&Styles::red);
|
||||
desc_cycle.set_style(&Styles::red);
|
||||
desc_cycle.set("/0 no entries in list");
|
||||
file_name.set("BadOrEmpty " + file_input);
|
||||
} else {
|
||||
if (frequency_list.size() > RECON_FREQMAN_MAX_PER_FILE) {
|
||||
if (frequency_list.size() > FREQMAN_MAX_PER_FILE) {
|
||||
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) {
|
||||
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
|
||||
if (freqlist_cleared_for_ui_action) {
|
||||
if (!manual_mode) {
|
||||
frequency_file_load(false);
|
||||
}
|
||||
if (autostart) {
|
||||
if (autostart && !user_pause) {
|
||||
recon_resume();
|
||||
} else {
|
||||
recon_pause();
|
||||
@@ -1031,7 +1057,9 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
if (status != 1) {
|
||||
status = 1;
|
||||
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
|
||||
@@ -1053,8 +1081,24 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
if (status != 2) {
|
||||
continuous_lock = false;
|
||||
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) {
|
||||
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) {
|
||||
timer = wait;
|
||||
@@ -1073,8 +1117,9 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
text_timer.set("TIMER: " + to_string_dec_int(timer));
|
||||
}
|
||||
if (timer) {
|
||||
if (!continuous_lock)
|
||||
timer -= STATS_UPDATE_INTERVAL;
|
||||
if (!continuous_lock || recon_match_mode == RECON_MATCH_SPARSE) {
|
||||
timer -= time_interval;
|
||||
}
|
||||
if (timer < 0) {
|
||||
timer = 0;
|
||||
}
|
||||
@@ -1085,7 +1130,6 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
if (frequency_list.size() > 0) {
|
||||
has_looped = false;
|
||||
entry_has_changed = false;
|
||||
|
||||
if (recon || stepper != 0 || index_stepper != 0) {
|
||||
if (index_stepper == 0) {
|
||||
/* we are doing a range */
|
||||
@@ -1185,7 +1229,8 @@ void ReconView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
|
||||
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();
|
||||
}
|
||||
// reload entry if changed
|
||||
@@ -1237,7 +1282,8 @@ void ReconView::recon_pause() {
|
||||
continuous_lock = false;
|
||||
recon = false;
|
||||
|
||||
audio_output_start();
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio_output_start();
|
||||
|
||||
big_display.set_style(&Styles::white);
|
||||
button_pause.set_text("<RESUME>"); // PAUSED, show resume
|
||||
@@ -1249,7 +1295,8 @@ void ReconView::recon_resume() {
|
||||
continuous_lock = false;
|
||||
recon = true;
|
||||
|
||||
audio::output::stop();
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION)
|
||||
audio::output::stop();
|
||||
|
||||
big_display.set_style(&Styles::white);
|
||||
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) {
|
||||
field_mode.on_change = [this](size_t, OptionsField::value_t) {};
|
||||
field_bw.on_change = [this](size_t, OptionsField::value_t) {};
|
||||
|
||||
recon_stop_recording();
|
||||
remove_child(record_view.get());
|
||||
record_view.reset();
|
||||
if (new_mod == SPEC_MODULATION) {
|
||||
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();
|
||||
baseband::shutdown();
|
||||
|
||||
size_t recording_sampling_rate = 0;
|
||||
switch (new_mod) {
|
||||
case AM_MODULATION:
|
||||
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_am_configuration(field_bw.selected_index_value());
|
||||
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(" ");
|
||||
recording_sampling_rate = 12000;
|
||||
break;
|
||||
case NFM_MODULATION:
|
||||
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_nbfm_configuration(field_bw.selected_index_value());
|
||||
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);
|
||||
recording_sampling_rate = 24000;
|
||||
break;
|
||||
case WFM_MODULATION:
|
||||
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_wfm_configuration(field_bw.selected_index_value());
|
||||
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(" ");
|
||||
recording_sampling_rate = 48000;
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (new_mod != SPEC_MODULATION)
|
||||
record_view->set_sampling_rate(recording_sampling_rate);
|
||||
field_mode.set_selected_index(new_mod);
|
||||
field_mode.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
if (v != -1) {
|
||||
change_mode(v);
|
||||
}
|
||||
};
|
||||
|
||||
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(); // so if recon was stopped we resume audio
|
||||
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) {
|
||||
float diff, min_diff = value;
|
||||
size_t min_idx{0};
|
||||
size_t c;
|
||||
if (field_mode.selected_index() == NFM_MODULATION) {
|
||||
tone_index idx = tone_key_index_by_value(value);
|
||||
|
||||
if (field_mode.selected_index() != NFM_MODULATION) {
|
||||
text_ctcss.set(" ");
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
if ((last_squelch_index < 0) || (last_squelch_index != idx)) {
|
||||
last_squelch_index = idx;
|
||||
if (idx >= 0) {
|
||||
text_ctcss.set("T: " + tone_key_string(idx));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 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(" ");
|
||||
}
|
||||
text_ctcss.set(" ");
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -39,12 +39,12 @@
|
||||
#include "string_format.hpp"
|
||||
#include "file.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_recon_settings.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
#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 {
|
||||
public:
|
||||
@@ -60,6 +60,11 @@ class ReconView : public View {
|
||||
private:
|
||||
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 reset_indexes();
|
||||
void audio_output_start();
|
||||
@@ -79,69 +84,75 @@ class ReconView : public View {
|
||||
bool recon_load_config_from_sd();
|
||||
bool recon_save_config_to_sd();
|
||||
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...
|
||||
int32_t squelch{0};
|
||||
int32_t db{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
|
||||
freqman_db frequency_list = {};
|
||||
freqman_db frequency_list{};
|
||||
int32_t current_index{0};
|
||||
bool continuous_lock{false};
|
||||
bool freqlist_cleared_for_ui_action{false}; // flag positioned by ui widgets to manage freqlist unload/load
|
||||
std::string input_file = {"RECON"};
|
||||
std::string output_file = {"RECON_RESULTS"};
|
||||
std::string description = {"...no description..."};
|
||||
bool autosave = {true};
|
||||
bool autostart = {true};
|
||||
bool continuous = {true};
|
||||
bool filedelete = {true};
|
||||
bool load_freqs = {true};
|
||||
bool load_ranges = {true};
|
||||
bool load_hamradios = {true};
|
||||
bool update_ranges = {true};
|
||||
bool fwd = {true};
|
||||
bool recon = true;
|
||||
uint32_t recon_lock_nb_match = {3};
|
||||
uint32_t recon_lock_duration = {RECON_MIN_LOCK_DURATION};
|
||||
uint32_t recon_match_mode = {RECON_MATCH_CONTINUOUS};
|
||||
std::string input_file{"RECON"};
|
||||
std::string output_file{"RECON_RESULTS"};
|
||||
std::string description{"...no description..."};
|
||||
bool autosave{true};
|
||||
bool autostart{true};
|
||||
bool continuous{true};
|
||||
bool filedelete{true};
|
||||
bool load_freqs{true};
|
||||
bool load_ranges{true};
|
||||
bool load_hamradios{true};
|
||||
bool update_ranges{true};
|
||||
bool fwd{true};
|
||||
bool recon{true};
|
||||
bool user_pause{false};
|
||||
bool auto_record_locked{false};
|
||||
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 manual_mode{false};
|
||||
bool sd_card_mounted = false;
|
||||
int32_t stepper = 0;
|
||||
int32_t index_stepper = 0;
|
||||
int64_t freq = 0;
|
||||
uint32_t step = 0;
|
||||
freqman_index_t def_modulation = 0;
|
||||
freqman_index_t def_bandwidth = 0;
|
||||
freqman_index_t def_step = 0;
|
||||
tone_index tone = 0;
|
||||
freqman_entry last_entry = {};
|
||||
bool entry_has_changed = false;
|
||||
bool sd_card_mounted{false};
|
||||
int32_t stepper{0};
|
||||
int32_t index_stepper{0};
|
||||
int64_t freq{0};
|
||||
uint32_t step{0};
|
||||
freqman_index_t def_step{0};
|
||||
freqman_entry last_entry{};
|
||||
bool entry_has_changed{false};
|
||||
uint32_t freq_lock{0};
|
||||
int64_t minfreq = 0;
|
||||
int64_t maxfreq = 0;
|
||||
bool has_looped = false;
|
||||
int8_t status = -1; // 0 recon , 1 locking , 2 locked
|
||||
int32_t last_timer = -1;
|
||||
int8_t last_db = -127;
|
||||
uint16_t last_nb_match = 999;
|
||||
uint16_t last_freq_lock = 999;
|
||||
size_t last_list_size = 0;
|
||||
int8_t last_rssi_min = -127;
|
||||
int8_t last_rssi_med = -127;
|
||||
int8_t last_rssi_max = -127;
|
||||
int32_t last_index = -1;
|
||||
int32_t last_squelch_index = -1;
|
||||
int64_t last_freq = 0;
|
||||
std::string freq_file_path = {};
|
||||
int64_t minfreq{0};
|
||||
int64_t maxfreq{0};
|
||||
bool has_looped{false};
|
||||
int8_t status{-1}; // 0 recon , 1 locking , 2 locked
|
||||
int32_t last_timer{-1};
|
||||
int8_t last_db{-127};
|
||||
uint16_t last_nb_match{999};
|
||||
uint16_t last_freq_lock{999};
|
||||
size_t last_list_size{0};
|
||||
int8_t last_rssi_min{-127};
|
||||
int8_t last_rssi_med{-127};
|
||||
int8_t last_rssi_max{-127};
|
||||
int32_t last_index{-1};
|
||||
tone_index last_squelch_index{-1};
|
||||
int64_t last_freq{0};
|
||||
std::string freq_file_path{};
|
||||
systime_t chrono_start{};
|
||||
systime_t chrono_end{};
|
||||
|
||||
std::unique_ptr<RecordView> record_view{};
|
||||
|
||||
Labels labels{
|
||||
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()},
|
||||
{{0 * 8, 1 * 16}, "BW: SQ: W,L: , ", Color::light_grey()},
|
||||
{{3 * 8, 10 * 16}, "START END MANUAL", Color::light_grey()},
|
||||
{{0 * 8, (26 * 8) + 4}, "MODE:", Color::light_grey()},
|
||||
{{11 * 8, (26 * 8) + 4}, "STEP:", Color::light_grey()},
|
||||
};
|
||||
{{3 * 8, 8 * 16}, "START END", Color::light_grey()},
|
||||
{{0 * 8, (22 * 8)}, " S: ", Color::light_grey()},
|
||||
{{0 * 8, (24 * 8) + 4}, "NBLCKS:x W,L: , ", Color::light_grey()},
|
||||
{{0 * 8, (26 * 8) + 4}, "MODE: , SQUELCH: ", Color::light_grey()}};
|
||||
|
||||
LNAGainField field_lna{
|
||||
{4 * 8, 0 * 16}};
|
||||
@@ -155,65 +166,41 @@ class ReconView : public View {
|
||||
AudioVolumeField field_volume{
|
||||
{24 * 8, 0 * 16}};
|
||||
|
||||
OptionsField field_bw{
|
||||
{3 * 8, 1 * 16},
|
||||
6,
|
||||
{}};
|
||||
|
||||
NumberField field_squelch{
|
||||
{12 * 8, 1 * 16},
|
||||
3,
|
||||
{-90, 20},
|
||||
1,
|
||||
' ',
|
||||
Text file_name{
|
||||
// show file used
|
||||
{0, 1 * 16, SCREEN_W, 16},
|
||||
};
|
||||
|
||||
NumberField field_wait{
|
||||
{20 * 8, 1 * 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,
|
||||
' ',
|
||||
Text desc_cycle{
|
||||
{0, 2 * 16, SCREEN_W, 16},
|
||||
};
|
||||
|
||||
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{
|
||||
{0, 3 * 16, 4 * 8, 16},
|
||||
{0, 4 * 16, 4 * 8, 16},
|
||||
""};
|
||||
|
||||
// "/XXX -XXX db" => 12 chars max
|
||||
Text text_max{
|
||||
{4 * 8, 3 * 16, 12 * 8, 16},
|
||||
{4 * 8, 4 * 16, 12 * 8, 16},
|
||||
};
|
||||
|
||||
// "XX/XX" => 5 chars max
|
||||
Text text_nb_locks{
|
||||
{16 * 8, 3 * 16, 5 * 8, 16},
|
||||
};
|
||||
|
||||
Text desc_cycle{
|
||||
{0, 4 * 16, SCREEN_W, 16},
|
||||
{16 * 8, 4 * 16, 5 * 8, 16},
|
||||
};
|
||||
|
||||
Text big_display{
|
||||
// Show frequency in text mode
|
||||
{0, 5 * 16, 23 * 8, 16},
|
||||
{0, 5 * 16, 21 * 8, 16},
|
||||
};
|
||||
|
||||
Text freq_stats{
|
||||
// Show frequency stats in text mode
|
||||
{0, 6 * 16, 23 * 8, 16},
|
||||
{0, 6 * 16, 21 * 8, 16},
|
||||
};
|
||||
|
||||
// TIMER: 9999
|
||||
@@ -224,48 +211,87 @@ class ReconView : public View {
|
||||
|
||||
// T: Senn. 32.000k
|
||||
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 button_scanner_mode{
|
||||
{SCREEN_W - 7 * 8, 8 * 16, 7 * 8, 28},
|
||||
{SCREEN_W - 7 * 8, 3 * 16, 7 * 8, 28},
|
||||
"RECON"};
|
||||
|
||||
Text file_name{
|
||||
// show file used
|
||||
{0, 8 * 16 + 6, SCREEN_W - 7 * 8, 16},
|
||||
};
|
||||
Button button_loop_config{
|
||||
{SCREEN_W - 7 * 8, 5 * 16, 7 * 8, 28},
|
||||
"[LOOP]"};
|
||||
|
||||
Button button_config{
|
||||
{SCREEN_W - 7 * 8, 7 * 16, 7 * 8, 28},
|
||||
"CONFIG"};
|
||||
|
||||
ButtonWithEncoder button_manual_start{
|
||||
{0 * 8, 11 * 16, 11 * 8, 28},
|
||||
{0 * 8, 9 * 16, 11 * 8, 28},
|
||||
""};
|
||||
|
||||
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{
|
||||
{23 * 8 - 3, 11 * 16, 7 * 8, 28},
|
||||
{23 * 8, 9 * 16, 7 * 8, 28},
|
||||
"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{
|
||||
{5 * 8, (26 * 8) + 4},
|
||||
{6 * 8, (26 * 8) + 4},
|
||||
4,
|
||||
{}};
|
||||
|
||||
OptionsField field_bw{
|
||||
{11 * 8, (26 * 8) + 4},
|
||||
6,
|
||||
{}};
|
||||
|
||||
OptionsField step_mode{
|
||||
{17 * 8, (26 * 8) + 4},
|
||||
12,
|
||||
{}};
|
||||
NumberField field_squelch{
|
||||
{26 * 8, (26 * 8) + 4},
|
||||
3,
|
||||
{-90, 20},
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
ButtonWithEncoder button_pause{
|
||||
{0, (15 * 16) - 4, 72, 28},
|
||||
@@ -307,9 +333,6 @@ class ReconView : public View {
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
|
||||
}};
|
||||
// app save settings
|
||||
std::app_settings settings{};
|
||||
std::app_settings::AppSettings app_settings{};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -44,12 +44,10 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
|
||||
&button_output_file,
|
||||
&checkbox_autosave_freqs,
|
||||
&checkbox_autostart_recon,
|
||||
&checkbox_continuous,
|
||||
&checkbox_clear_output});
|
||||
|
||||
checkbox_autosave_freqs.set_value(persistent_memory::recon_autosave_freqs());
|
||||
checkbox_autostart_recon.set_value(persistent_memory::recon_autostart_recon());
|
||||
checkbox_continuous.set_value(persistent_memory::recon_continuous());
|
||||
checkbox_clear_output.set_value(persistent_memory::recon_clear_output());
|
||||
|
||||
text_input_file.set(_input_file);
|
||||
@@ -96,49 +94,38 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
|
||||
void ReconSetupViewMain::save(std::string& input_file, std::string& output_file) {
|
||||
persistent_memory::set_recon_autosave_freqs(checkbox_autosave_freqs.value());
|
||||
persistent_memory::set_recon_autostart_recon(checkbox_autostart_recon.value());
|
||||
persistent_memory::set_recon_continuous(checkbox_continuous.value());
|
||||
persistent_memory::set_recon_clear_output(checkbox_clear_output.value());
|
||||
input_file = _input_file;
|
||||
output_file = _output_file;
|
||||
};
|
||||
void ReconSetupViewMore::save(uint32_t& recon_lock_duration, uint32_t& recon_lock_nb_match, uint32_t& recon_match_mode) {
|
||||
void ReconSetupViewMore::save() {
|
||||
persistent_memory::set_recon_load_freqs(checkbox_load_freqs.value());
|
||||
persistent_memory::set_recon_load_ranges(checkbox_load_ranges.value());
|
||||
persistent_memory::set_recon_load_hamradios(checkbox_load_hamradios.value());
|
||||
persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value());
|
||||
recon_lock_duration = field_recon_lock_duration.value();
|
||||
recon_lock_nb_match = field_recon_lock_nb_match.value();
|
||||
recon_match_mode = field_recon_match_mode.selected_index_value();
|
||||
persistent_memory::set_recon_auto_record_locked(checkbox_auto_record_locked.value());
|
||||
};
|
||||
|
||||
void ReconSetupViewMain::focus() {
|
||||
button_load_freqs.focus();
|
||||
}
|
||||
|
||||
ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect, uint32_t recon_lock_duration, uint32_t recon_lock_nb_match, uint32_t recon_match_mode)
|
||||
: View(parent_rect), _recon_lock_duration{recon_lock_duration}, _recon_lock_nb_match{recon_lock_nb_match}, _recon_match_mode{recon_match_mode} {
|
||||
ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
: View(parent_rect) {
|
||||
(void)nav;
|
||||
hidden(true);
|
||||
|
||||
add_children({
|
||||
&checkbox_load_freqs,
|
||||
&checkbox_load_ranges,
|
||||
&checkbox_load_hamradios,
|
||||
&checkbox_update_ranges_when_recon,
|
||||
&text_recon_lock_duration,
|
||||
&field_recon_lock_duration,
|
||||
&text_recon_lock_nb,
|
||||
&field_recon_lock_nb_match,
|
||||
&field_recon_match_mode,
|
||||
});
|
||||
add_children({&checkbox_load_freqs,
|
||||
&checkbox_load_ranges,
|
||||
&checkbox_load_hamradios,
|
||||
&checkbox_update_ranges_when_recon,
|
||||
&checkbox_auto_record_locked});
|
||||
|
||||
checkbox_load_freqs.set_value(persistent_memory::recon_load_freqs());
|
||||
checkbox_load_ranges.set_value(persistent_memory::recon_load_ranges());
|
||||
checkbox_load_hamradios.set_value(persistent_memory::recon_load_hamradios());
|
||||
checkbox_update_ranges_when_recon.set_value(persistent_memory::recon_update_ranges_when_recon());
|
||||
field_recon_lock_duration.set_value(_recon_lock_duration);
|
||||
field_recon_lock_nb_match.set_value(_recon_lock_nb_match);
|
||||
field_recon_match_mode.set_by_value(_recon_match_mode);
|
||||
checkbox_auto_record_locked.set_value(persistent_memory::recon_auto_record_locked());
|
||||
};
|
||||
|
||||
void ReconSetupViewMore::focus() {
|
||||
@@ -149,14 +136,8 @@ void ReconSetupView::focus() {
|
||||
viewMain.focus();
|
||||
}
|
||||
|
||||
ReconSetupView::ReconSetupView(
|
||||
NavigationView& nav,
|
||||
std::string _input_file,
|
||||
std::string _output_file,
|
||||
uint32_t _recon_lock_duration,
|
||||
uint32_t _recon_lock_nb_match,
|
||||
uint32_t _recon_match_mode)
|
||||
: nav_{nav}, input_file{_input_file}, output_file{_output_file}, recon_lock_duration{_recon_lock_duration}, recon_lock_nb_match{_recon_lock_nb_match}, recon_match_mode{_recon_match_mode} {
|
||||
ReconSetupView::ReconSetupView(NavigationView& nav, std::string _input_file, std::string _output_file)
|
||||
: nav_{nav}, input_file{_input_file}, output_file{_output_file} {
|
||||
add_children({&tab_view,
|
||||
&viewMain,
|
||||
&viewMore,
|
||||
@@ -164,13 +145,10 @@ ReconSetupView::ReconSetupView(
|
||||
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
viewMain.save(input_file, output_file);
|
||||
viewMore.save(recon_lock_duration, recon_lock_nb_match, recon_match_mode);
|
||||
viewMore.save();
|
||||
std::vector<std::string> messages;
|
||||
messages.push_back(input_file);
|
||||
messages.push_back(output_file);
|
||||
messages.push_back(to_string_dec_uint(recon_lock_duration));
|
||||
messages.push_back(to_string_dec_uint(recon_lock_nb_match));
|
||||
messages.push_back(to_string_dec_uint(recon_match_mode));
|
||||
on_changed(messages);
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
@@ -71,8 +71,8 @@ class ReconSetupViewMain : public View {
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
std::string _input_file = {"RECON"};
|
||||
std::string _output_file = {"RECON_RESULTS"};
|
||||
std::string _input_file{"RECON"};
|
||||
std::string _output_file{"RECON_RESULTS"};
|
||||
|
||||
Button button_load_freqs{
|
||||
{1 * 8, 12, 18 * 8, 22},
|
||||
@@ -98,29 +98,21 @@ class ReconSetupViewMain : public View {
|
||||
3,
|
||||
"autostart recon"};
|
||||
|
||||
Checkbox checkbox_continuous{
|
||||
{1 * 8, 11 * 16 - 4},
|
||||
3,
|
||||
"continuous"};
|
||||
Checkbox checkbox_clear_output{
|
||||
{1 * 8, 13 * 16 - 4},
|
||||
{1 * 8, 11 * 16 - 4},
|
||||
3,
|
||||
"clear output at start"};
|
||||
};
|
||||
|
||||
class ReconSetupViewMore : public View {
|
||||
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;
|
||||
|
||||
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{
|
||||
{1 * 8, 12},
|
||||
3,
|
||||
@@ -140,43 +132,15 @@ class ReconSetupViewMore : public View {
|
||||
{1 * 8, 102},
|
||||
3,
|
||||
"auto update m-ranges"};
|
||||
|
||||
NumberField field_recon_lock_duration{
|
||||
{1 * 8, 132}, // position X , Y
|
||||
4, // number of displayed digits (even empty)
|
||||
{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}}};
|
||||
Checkbox checkbox_auto_record_locked{
|
||||
{1 * 8, 132},
|
||||
3,
|
||||
"auto record locked periods"};
|
||||
};
|
||||
|
||||
class ReconSetupView : public View {
|
||||
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{};
|
||||
|
||||
@@ -187,16 +151,13 @@ class ReconSetupView : public View {
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
std::string input_file = {"RECON"};
|
||||
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;
|
||||
std::string input_file{"RECON"};
|
||||
std::string output_file{"RECON_RESULTS"};
|
||||
|
||||
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};
|
||||
ReconSetupViewMore viewMore{nav_, view_rect, recon_lock_duration, recon_lock_nb_match, recon_match_mode};
|
||||
ReconSetupViewMore viewMore{nav_, view_rect};
|
||||
|
||||
TabView tab_view{
|
||||
{"Main", Color::cyan(), &viewMain},
|
||||
|
||||
@@ -122,7 +122,7 @@ void ScannerThread::run() {
|
||||
if (force_one_step)
|
||||
_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.range = frequency_index; // Inform freq (for coloring purposes also!)
|
||||
@@ -160,7 +160,7 @@ void ScannerThread::run() {
|
||||
if (force_one_step)
|
||||
_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.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() {
|
||||
field_mode.focus();
|
||||
button_load.focus();
|
||||
}
|
||||
|
||||
ScannerView::~ScannerView() {
|
||||
@@ -299,14 +299,13 @@ ScannerView::ScannerView(
|
||||
freqman_set_modulation_option(field_mode);
|
||||
freqman_set_step_option(field_step);
|
||||
|
||||
// Default starting modulation (these may be overridden in SCANNER.TXT)
|
||||
change_mode(AM_MODULATION); // Default modulation
|
||||
field_mode.set_by_value(AM_MODULATION); // Reflect the mode into the manual selector
|
||||
field_step.set_by_value(9000); // Default step interval (Hz)
|
||||
// Default starting modulation (from saved App Settings if enabled, and may be overridden in SCANNER.TXT)
|
||||
field_mode.set_by_value((OptionsField::value_t)receiver_model.modulation()); // Reflect the mode into the manual selector
|
||||
field_step.set_by_value(receiver_model.frequency_step()); // 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
|
||||
// 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();
|
||||
frequency_range.min = stored_freq - 1000000;
|
||||
button_manual_start.set_text(to_string_short_freq(frequency_range.min));
|
||||
frequency_range.max = stored_freq + 1000000;
|
||||
@@ -448,6 +447,16 @@ ScannerView::ScannerView(
|
||||
|
||||
// Mode field was changed (AM/NFM/WFM)
|
||||
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();
|
||||
baseband::shutdown();
|
||||
change_mode((freqman_index_t)v);
|
||||
@@ -458,7 +467,7 @@ ScannerView::ScannerView(
|
||||
|
||||
// Step field was changed (Hz) -- only affects manual Search mode
|
||||
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) {
|
||||
// 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);
|
||||
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
|
||||
field_bw.set_by_value(0);
|
||||
receiver_model.set_am_configuration(field_bw.selected_index_value());
|
||||
field_bw.set_by_value(receiver_model.am_configuration());
|
||||
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;
|
||||
case NFM_MODULATION: // bw 16k (2) default
|
||||
case NFM_MODULATION:
|
||||
freqman_set_bandwidth_option(new_mod, field_bw);
|
||||
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
||||
field_bw.set_by_value(2);
|
||||
receiver_model.set_nbfm_configuration(field_bw.selected_index_value());
|
||||
field_bw.set_by_value(receiver_model.nbfm_configuration());
|
||||
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;
|
||||
case WFM_MODULATION:
|
||||
freqman_set_bandwidth_option(new_mod, field_bw);
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
||||
field_bw.set_by_value(0);
|
||||
receiver_model.set_wfm_configuration(field_bw.selected_index_value());
|
||||
field_bw.set_by_value(receiver_model.wfm_configuration());
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "receiver_model.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "freqman.hpp"
|
||||
@@ -94,7 +95,11 @@ class ScannerView : public View {
|
||||
// void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
private:
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_scanner", app_settings::Mode::RX};
|
||||
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
|
||||
void start_scan_thread();
|
||||
void change_mode(freqman_index_t mod_type);
|
||||
|
||||
@@ -221,7 +221,7 @@ void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
slice_counter = 0;
|
||||
} else
|
||||
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
|
||||
} else {
|
||||
// Unique slice
|
||||
@@ -271,7 +271,7 @@ void SearchView::on_range_changed() {
|
||||
}
|
||||
} else {
|
||||
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;
|
||||
text_slices.set(" 1");
|
||||
@@ -376,25 +376,25 @@ SearchView::SearchView(
|
||||
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.on_change = [this](rf::Frequency) {
|
||||
this->on_range_changed();
|
||||
};
|
||||
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) {
|
||||
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.on_change = [this](rf::Frequency) {
|
||||
this->on_range_changed();
|
||||
};
|
||||
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) {
|
||||
this->field_frequency_max.set_value(f);
|
||||
};
|
||||
@@ -415,8 +415,6 @@ SearchView::SearchView(
|
||||
on_range_changed();
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_sampling_rate(SEARCH_SLICE_WIDTH);
|
||||
receiver_model.set_baseband_bandwidth(2500000);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "receiver_model.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
@@ -89,6 +90,10 @@ class SearchView : public View {
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{
|
||||
2500000 /* bandwidth */,
|
||||
SEARCH_SLICE_WIDTH /* sampling rate */
|
||||
};
|
||||
|
||||
struct slice_t {
|
||||
rf::Frequency center_frequency;
|
||||
@@ -142,11 +147,9 @@ class SearchView : public View {
|
||||
{{26 * 8, 25 * 8}, "MHz", Color::light_grey()}};
|
||||
|
||||
FrequencyField field_frequency_min{
|
||||
{1 * 8, 1 * 16},
|
||||
};
|
||||
{1 * 8, 1 * 16}};
|
||||
FrequencyField field_frequency_max{
|
||||
{11 * 8, 1 * 16},
|
||||
};
|
||||
{11 * 8, 1 * 16}};
|
||||
LNAGainField field_lna{
|
||||
{22 * 8, 1 * 16}};
|
||||
VGAGainField field_vga{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "ui_settings.hpp"
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_touch_calibration.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
@@ -32,16 +33,24 @@ using namespace lpc43xx;
|
||||
|
||||
#include "audio.hpp"
|
||||
#include "portapack.hpp"
|
||||
using portapack::receiver_model;
|
||||
using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "cpld_update.hpp"
|
||||
|
||||
#include "freqman.hpp"
|
||||
namespace pmem = portapack::persistent_memory;
|
||||
|
||||
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(
|
||||
NavigationView& nav) {
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
@@ -105,13 +114,15 @@ SetDateTimeModel SetDateTimeView::form_collect() {
|
||||
.second = static_cast<uint8_t>(field_second.value())};
|
||||
}
|
||||
|
||||
/* SetRadioView ******************************************/
|
||||
|
||||
SetRadioView::SetRadioView(
|
||||
NavigationView& nav) {
|
||||
button_cancel.on_select = [&nav](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
const auto reference = portapack::clock_manager.get_reference();
|
||||
const auto reference = clock_manager.get_reference();
|
||||
|
||||
std::string source_name("---");
|
||||
switch (reference.source) {
|
||||
@@ -156,19 +167,18 @@ SetRadioView::SetRadioView(
|
||||
&button_cancel});
|
||||
|
||||
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);
|
||||
|
||||
check_clkout.set_value(portapack::persistent_memory::clkout_enabled());
|
||||
check_clkout.set_value(pmem::clkout_enabled());
|
||||
check_clkout.on_select = [this](Checkbox&, bool v) {
|
||||
clock_manager.enable_clock_output(v);
|
||||
portapack::persistent_memory::set_clkout_enabled(v);
|
||||
StatusRefreshMessage message{};
|
||||
EventDispatcher::send_message(message);
|
||||
pmem::set_clkout_enabled(v);
|
||||
send_system_refresh();
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
field_clkout_freq.on_select = [this](NumberField&) {
|
||||
@@ -193,9 +203,9 @@ SetRadioView::SetRadioView(
|
||||
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) {
|
||||
portapack::set_antenna_bias(v);
|
||||
set_antenna_bias(v);
|
||||
|
||||
// Update the radio.
|
||||
receiver_model.set_antenna_bias();
|
||||
@@ -205,15 +215,14 @@ SetRadioView::SetRadioView(
|
||||
if (!v)
|
||||
radio::set_antenna_bias(false);
|
||||
|
||||
StatusRefreshMessage message{};
|
||||
EventDispatcher::send_message(message);
|
||||
send_system_refresh();
|
||||
};
|
||||
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
const auto model = this->form_collect();
|
||||
portapack::persistent_memory::set_correction_ppb(model.ppm * 1000);
|
||||
portapack::persistent_memory::set_clkout_freq(model.freq);
|
||||
clock_manager.enable_clock_output(portapack::persistent_memory::clkout_enabled());
|
||||
pmem::set_correction_ppb(model.ppm * 1000);
|
||||
pmem::set_clkout_freq(model.freq);
|
||||
clock_manager.enable_clock_output(pmem::clkout_enabled());
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
@@ -233,54 +242,86 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() {
|
||||
};
|
||||
}
|
||||
|
||||
/* SetUIView *********************************************/
|
||||
|
||||
SetUIView::SetUIView(NavigationView& nav) {
|
||||
add_children({&checkbox_disable_touchscreen,
|
||||
&checkbox_speaker,
|
||||
&checkbox_bloff,
|
||||
&options_bloff,
|
||||
&checkbox_showsplash,
|
||||
&checkbox_showclock,
|
||||
&options_clockformat,
|
||||
&checkbox_guireturnflag,
|
||||
&labels,
|
||||
&toggle_camera,
|
||||
&toggle_sleep,
|
||||
&toggle_stealth,
|
||||
&toggle_converter,
|
||||
&toggle_bias_tee,
|
||||
&toggle_clock,
|
||||
&toggle_mute,
|
||||
&toggle_sd_card,
|
||||
&button_save,
|
||||
&button_cancel});
|
||||
|
||||
checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen());
|
||||
checkbox_speaker.set_value(persistent_memory::config_speaker());
|
||||
checkbox_showsplash.set_value(persistent_memory::config_splash());
|
||||
checkbox_showclock.set_value(!persistent_memory::hide_clock());
|
||||
checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon());
|
||||
// Display "Disable speaker" option only if AK4951 Codec which has separate speaker/headphone control
|
||||
if (audio::speaker_disable_supported()) {
|
||||
add_child(&toggle_speaker);
|
||||
}
|
||||
|
||||
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());
|
||||
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);
|
||||
} else {
|
||||
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&) {
|
||||
persistent_memory::set_config_backlight_timer({(persistent_memory::backlight_timeout_t)options_bloff.selected_index_value(),
|
||||
checkbox_bloff.value()});
|
||||
pmem::set_config_backlight_timer({(pmem::backlight_timeout_t)options_bloff.selected_index_value(),
|
||||
checkbox_bloff.value()});
|
||||
|
||||
if (checkbox_showclock.value()) {
|
||||
if (options_clockformat.selected_index() == 1)
|
||||
persistent_memory::set_clock_with_date(true);
|
||||
pmem::set_clock_with_date(true);
|
||||
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
|
||||
persistent_memory::set_config_speaker(checkbox_speaker.value()); // Store Speaker status
|
||||
StatusRefreshMessage message{}; // Refresh status bar with/out speaker
|
||||
EventDispatcher::send_message(message);
|
||||
pmem::set_config_splash(checkbox_showsplash.value());
|
||||
pmem::set_clock_hidden(!checkbox_showclock.value());
|
||||
pmem::set_gui_return_icon(checkbox_guireturnflag.value());
|
||||
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();
|
||||
};
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
@@ -292,21 +333,20 @@ void SetUIView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Appl. Settings
|
||||
// ---------------------------------------------------------
|
||||
/* SetAppSettingsView ************************************/
|
||||
|
||||
SetAppSettingsView::SetAppSettingsView(NavigationView& nav) {
|
||||
add_children({&checkbox_load_app_settings,
|
||||
&checkbox_save_app_settings,
|
||||
&button_save,
|
||||
&button_cancel});
|
||||
|
||||
checkbox_load_app_settings.set_value(persistent_memory::load_app_settings());
|
||||
checkbox_save_app_settings.set_value(persistent_memory::save_app_settings());
|
||||
checkbox_load_app_settings.set_value(pmem::load_app_settings());
|
||||
checkbox_save_app_settings.set_value(pmem::save_app_settings());
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
persistent_memory::set_load_app_settings(checkbox_load_app_settings.value());
|
||||
persistent_memory::set_save_app_settings(checkbox_save_app_settings.value());
|
||||
pmem::set_load_app_settings(checkbox_load_app_settings.value());
|
||||
pmem::set_save_app_settings(checkbox_save_app_settings.value());
|
||||
nav.pop();
|
||||
};
|
||||
button_cancel.on_select = [&nav, this](Button&) {
|
||||
@@ -318,9 +358,8 @@ void SetAppSettingsView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Converter Settings
|
||||
// ---------------------------------------------------------
|
||||
/* SetConverterSettingsView ******************************/
|
||||
|
||||
SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) {
|
||||
add_children({&check_show_converter,
|
||||
&check_converter,
|
||||
@@ -328,48 +367,45 @@ SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) {
|
||||
&button_converter_freq,
|
||||
&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) {
|
||||
portapack::persistent_memory::set_config_hide_converter(!v);
|
||||
pmem::set_ui_hide_converter(!v);
|
||||
if (!v) {
|
||||
check_converter.set_value(false);
|
||||
}
|
||||
// Retune to take converter change in account
|
||||
receiver_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency());
|
||||
// Retune to take converter change in account.
|
||||
receiver_model.set_target_frequency(receiver_model.target_frequency());
|
||||
// Refresh status bar with/out converter
|
||||
StatusRefreshMessage message{};
|
||||
EventDispatcher::send_message(message);
|
||||
send_system_refresh();
|
||||
};
|
||||
|
||||
check_converter.set_value(portapack::persistent_memory::config_converter());
|
||||
check_converter.set_value(pmem::config_converter());
|
||||
check_converter.on_select = [this](Checkbox&, bool v) {
|
||||
if (v) {
|
||||
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
|
||||
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
|
||||
StatusRefreshMessage message{};
|
||||
EventDispatcher::send_message(message);
|
||||
send_system_refresh();
|
||||
};
|
||||
|
||||
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) {
|
||||
portapack::persistent_memory::set_config_updown_converter(v);
|
||||
pmem::set_config_updown_converter(v);
|
||||
// Refresh status bar with icon up or down
|
||||
StatusRefreshMessage message{};
|
||||
EventDispatcher::send_message(message);
|
||||
send_system_refresh();
|
||||
};
|
||||
|
||||
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) {
|
||||
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) {
|
||||
portapack::persistent_memory::set_config_converter_freq(f);
|
||||
pmem::set_config_converter_freq(f);
|
||||
// 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>");
|
||||
};
|
||||
};
|
||||
@@ -383,9 +419,8 @@ void SetConverterSettingsView::focus() {
|
||||
button_return.focus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Frequency Correction Settings
|
||||
// ---------------------------------------------------------
|
||||
/* SetFrequencyCorrectionView ****************************/
|
||||
|
||||
SetFrequencyCorrectionView::SetFrequencyCorrectionView(NavigationView& nav) {
|
||||
add_children({&text_freqCorrection_about,
|
||||
&frequency_rx_correction_mode,
|
||||
@@ -394,38 +429,38 @@ SetFrequencyCorrectionView::SetFrequencyCorrectionView(NavigationView& nav) {
|
||||
&button_freq_tx_correction,
|
||||
&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) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
if (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
|
||||
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_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) {
|
||||
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) {
|
||||
if (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
|
||||
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>");
|
||||
};
|
||||
};
|
||||
@@ -439,9 +474,8 @@ void SetFrequencyCorrectionView::focus() {
|
||||
button_return.focus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Persistent Memory Settings
|
||||
// ---------------------------------------------------------
|
||||
/* SetPersistentMemoryView *******************************/
|
||||
|
||||
SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
|
||||
add_children({&text_pmem_about,
|
||||
&text_pmem_informations,
|
||||
@@ -452,7 +486,7 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
|
||||
&button_load_mem_defaults,
|
||||
&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) {
|
||||
File pmem_flag_file_handle;
|
||||
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&) {
|
||||
if (!portapack::persistent_memory::save_persistent_settings_to_file()) {
|
||||
if (!pmem::save_persistent_settings_to_file()) {
|
||||
text_pmem_status.set("!problem saving settings!");
|
||||
} else {
|
||||
text_pmem_status.set("settings saved");
|
||||
@@ -487,13 +521,12 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
|
||||
};
|
||||
|
||||
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!");
|
||||
} else {
|
||||
text_pmem_status.set("settings loaded");
|
||||
// Refresh status bar with icon up or down
|
||||
StatusRefreshMessage message{};
|
||||
EventDispatcher::send_message(message);
|
||||
send_system_refresh();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -504,7 +537,7 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
|
||||
YESNO,
|
||||
[this](bool choice) {
|
||||
if (choice) {
|
||||
portapack::persistent_memory::cache::defaults();
|
||||
pmem::cache::defaults();
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -518,19 +551,19 @@ void SetPersistentMemoryView::focus() {
|
||||
button_return.focus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Audio Settings
|
||||
// ---------------------------------------------------------
|
||||
/* SetAudioView ******************************************/
|
||||
|
||||
SetAudioView::SetAudioView(NavigationView& nav) {
|
||||
add_children({&labels,
|
||||
&field_tone_mix,
|
||||
&button_save,
|
||||
&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&) {
|
||||
persistent_memory::set_tone_mix(field_tone_mix.value());
|
||||
pmem::set_tone_mix(field_tone_mix.value());
|
||||
audio::output::update_audio_mute();
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
@@ -543,18 +576,17 @@ void SetAudioView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// QR Code Settings
|
||||
// ------------------------------------------------------
|
||||
/* SetQRCodeView *****************************************/
|
||||
|
||||
SetQRCodeView::SetQRCodeView(NavigationView& nav) {
|
||||
add_children({&checkbox_bigger_qr,
|
||||
&button_save,
|
||||
&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&) {
|
||||
persistent_memory::set_show_bigger_qr_code(checkbox_bigger_qr.value());
|
||||
pmem::set_show_bigger_qr_code(checkbox_bigger_qr.value());
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
@@ -567,19 +599,18 @@ void SetQRCodeView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Rotary Encoder Dial Settings
|
||||
// ---------------------------------------------------------
|
||||
/* SetEncoderDialView ************************************/
|
||||
|
||||
SetEncoderDialView::SetEncoderDialView(NavigationView& nav) {
|
||||
add_children({&labels,
|
||||
&field_encoder_dial_sensitivity,
|
||||
&button_save,
|
||||
&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&) {
|
||||
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();
|
||||
};
|
||||
|
||||
@@ -592,11 +623,10 @@ void SetEncoderDialView::focus() {
|
||||
button_save.focus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Settings main menu
|
||||
// ---------------------------------------------------------
|
||||
/* SettingsMenuView **************************************/
|
||||
|
||||
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({
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "bitmap.hpp"
|
||||
#include "ff.h"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
@@ -34,7 +35,7 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
#define MAX_FREQ_CORRECTION 4294967295 // maximum possible for an uint32_t
|
||||
#define MAX_FREQ_CORRECTION INT32_MAX
|
||||
|
||||
struct SetDateTimeModel {
|
||||
uint16_t year;
|
||||
@@ -208,21 +209,16 @@ class SetUIView : public View {
|
||||
|
||||
private:
|
||||
Checkbox checkbox_disable_touchscreen{
|
||||
{3 * 8, 2 * 16},
|
||||
{3 * 8, 1 * 16},
|
||||
20,
|
||||
"Disable touchscreen"};
|
||||
|
||||
Checkbox checkbox_speaker{
|
||||
{3 * 8, 4 * 16},
|
||||
20,
|
||||
"Hide H1 Speaker option"};
|
||||
|
||||
Checkbox checkbox_bloff{
|
||||
{3 * 8, 6 * 16},
|
||||
{3 * 8, 3 * 16},
|
||||
20,
|
||||
"Backlight off after:"};
|
||||
OptionsField options_bloff{
|
||||
{52, 7 * 16 + 8},
|
||||
{60, 4 * 16 + 8},
|
||||
20,
|
||||
{
|
||||
{"5 seconds", backlight_timeout_t::Timeout5Sec},
|
||||
@@ -236,25 +232,65 @@ class SetUIView : public View {
|
||||
}};
|
||||
|
||||
Checkbox checkbox_showsplash{
|
||||
{3 * 8, 9 * 16},
|
||||
{3 * 8, 6 * 16},
|
||||
20,
|
||||
"Show splash"};
|
||||
|
||||
Checkbox checkbox_showclock{
|
||||
{3 * 8, 11 * 16},
|
||||
{3 * 8, 8 * 16},
|
||||
20,
|
||||
"Show clock with:"};
|
||||
|
||||
OptionsField options_clockformat{
|
||||
{52, 12 * 16 + 8},
|
||||
{60, 9 * 16 + 8},
|
||||
20,
|
||||
{{"time only", 0},
|
||||
{"time and date", 1}}};
|
||||
|
||||
Checkbox checkbox_guireturnflag{
|
||||
{3 * 8, 14 * 16},
|
||||
25,
|
||||
"add return icon in GUI"};
|
||||
{3 * 8, 11 * 16},
|
||||
20,
|
||||
"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{
|
||||
{2 * 8, 16 * 16, 12 * 8, 32},
|
||||
@@ -470,7 +506,7 @@ class SetPersistentMemoryView : public View {
|
||||
private:
|
||||
Text text_pmem_about{
|
||||
{0, 1 * 16, 240, 16},
|
||||
"PersistentMemory from/to SD"};
|
||||
"Persistent Memory from/to SD"};
|
||||
|
||||
Text text_pmem_informations{
|
||||
{0, 2 * 16, 240, 16},
|
||||
|
||||
@@ -119,12 +119,10 @@ SIGFRXView::SIGFRXView(
|
||||
.sampling_rate = 3072000,
|
||||
.decimation_factor = 4,
|
||||
});
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
|
||||
receiver_model.set_tuning_frequency(868110000);
|
||||
|
||||
// TODO: use settings.
|
||||
receiver_model.set_lna(0);
|
||||
receiver_model.set_vga(0);
|
||||
receiver_model.set_target_frequency(868110000);
|
||||
|
||||
add_children({&text_type,
|
||||
&text_channel,
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "max2837.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "receiver_model.hpp"
|
||||
#include "radio_state.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -50,6 +51,8 @@ class SIGFRXView : public View {
|
||||
uint8_t last_channel;
|
||||
uint8_t detect_counter = 0;
|
||||
|
||||
RxRadioState radio_state_{};
|
||||
|
||||
const uint16_t sigfrx_marks[18] = {
|
||||
10, 8, 0,
|
||||
60, 52, 90,
|
||||
|
||||
@@ -54,9 +54,6 @@ void SigGenView::update_tone() {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
update_tone();
|
||||
@@ -116,9 +113,9 @@ SigGenView::SigGenView(
|
||||
};
|
||||
|
||||
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) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
transmitter_model.set_target_frequency(f);
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#ifndef __SIGGEN_H__
|
||||
#define __SIGGEN_H__
|
||||
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
@@ -48,6 +50,13 @@ class SigGenView : public View {
|
||||
void update_tone();
|
||||
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] = {
|
||||
"CW-just carrier",
|
||||
"Sine signal ",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user