Compare commits

...

52 Commits

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

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

* fixing build integrationg zoom

---------

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

* Zoom button 5/8 to 8/16 font

* Clang & revert unintended change

* Suggested improvements

* Suggested code changes

* Added char_width function

* Added char_width function

* Oops copy-paste error

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

* clean up

* clean up - 1

* clean up - 2

* can input underline and < now

* format fix

* textual change

* textual change - 2

* textual change - 3

* textual change - 4

* edit for PR comment

* edit for PR comment - 2

* edit for PR comment - 3

* edit for PR comment - 4

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

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

* Shading alternate icons on title bar

* Shading alternate icons on title bar

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

* Folder fixes, use TX freq

---------

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

* Update app icon colors per current development states

* Darken bright icons on white backgrounds

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

* WIP status tray auto layout

* Add settings page for icons

* Add ui_config2 to pmem dump

---------

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

* Added TouchTunes jukebox icon

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

* Paint roller icon for Screen Painter app

* Paint roller icon for Screen Painter app

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

* Zoom-in support

* Clang formatting

* Clang formatting

* Zoom-in support

* Zoom-in support

* Zoom-in support

* Roll-back wrong file edited

* Zoom-in support

* Clang formatting

* Clang formatting

* Zoom-in support

* Zoom-in support

* Zoom-in support

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

* centralize cpld load for TX->RX

* Remove calls to load_sram from all apps

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

---------

Co-authored-by: kallanreed <kallanreed@noreply.github.com>
2023-06-17 07:54:52 -07:00
gullradriel 3a5c2da66c fixing bandwidth application, changed UI a bit (#1158)
* fixxing bandwidth application, moved 'continuous' from config page to main page in form of a 'loop' button. Removed looking glass button as it's not configurable to position on a range/signal
2023-06-16 07:50:04 +02:00
150 changed files with 3502 additions and 2042 deletions
+1 -1
View File
@@ -1 +1 @@
v1.7.1
v1.7.2
+1 -1
View File
@@ -1 +1 @@
v1.7.2
v1.7.3
+2 -1
View File
@@ -184,6 +184,7 @@ set(CPPSRC
irq_lcd_frame.cpp
irq_rtc.cpp
log_file.cpp
metadata_file.cpp
portapack.cpp
qrcodegen.cpp
radio.cpp
@@ -286,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
+3 -5
View File
@@ -94,8 +94,6 @@ constexpr std::string_view volume = "volume="sv;
// 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.
// TODO: radio settings should be pushed and popped to prevent cross-app
// radio bugs caused by sharing a global model.
ResultCode load_settings(const std::string& app_name, AppSettings& settings) {
if (!portapack::persistent_memory::load_app_settings())
@@ -178,17 +176,17 @@ void copy_to_radio_model(const AppSettings& settings) {
if (flags_enabled(settings.mode, Mode::TX)) {
if (!flags_enabled(settings.options, Options::UseGlobalTargetFrequency))
transmitter_model.set_target_frequency(settings.tx_frequency);
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))
transmitter_model.set_target_frequency(settings.rx_frequency);
persistent_memory::set_target_frequency(settings.rx_frequency);
receiver_model.configure_from_app_settings(settings);
receiver_model.set_configuration_without_init(
receiver_model.set_configuration_without_update(
static_cast<ReceiverModel::Mode>(settings.modulation),
settings.step,
settings.am_config_index,
+2 -14
View File
@@ -55,7 +55,8 @@ void ACARSLogger::log_raw_data(const acars::Packet& packet, const uint32_t frequ
namespace ui {
ACARSAppView::ACARSAppView(NavigationView& nav) {
ACARSAppView::ACARSAppView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_acars);
add_children({&rssi,
@@ -69,19 +70,6 @@ ACARSAppView::ACARSAppView(NavigationView& nav) {
receiver_model.enable();
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
+6 -6
View File
@@ -27,6 +27,7 @@
#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"
@@ -57,6 +58,7 @@ class ACARSAppView : public View {
std::string title() const override { return "ACARS (WIP)"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{
1750000 /* bandwidth */,
2457600 /* sampling rate */
@@ -74,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,
+45 -86
View File
@@ -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,
@@ -152,19 +154,6 @@ AnalogAudioView::AnalogAudioView(
// Filename Datetime and Frequency
record_view.set_filename_date_frequency(true);
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
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.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
field_frequency.on_show_options = [this]() {
this->on_show_options_frequency();
};
@@ -177,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));
};
@@ -197,8 +189,9 @@ AnalogAudioView::AnalogAudioView(
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() {
@@ -225,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);
}
@@ -270,13 +238,15 @@ 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() {
@@ -328,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);
@@ -355,6 +325,7 @@ void AnalogAudioView::on_show_options_modulation() {
break;
default:
chDbgPanic("Unhandled Mode");
break;
}
@@ -371,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();
@@ -392,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);
@@ -431,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("???");
}
+17 -34
View File
@@ -26,6 +26,7 @@
#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"
@@ -37,7 +38,7 @@ 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{
@@ -55,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{
@@ -83,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{
@@ -102,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{
@@ -139,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"; };
@@ -156,6 +154,7 @@ class AnalogAudioView : public View {
private:
static constexpr ui::Dim header_height = 3 * 16;
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_audio", app_settings::Mode::RX,
@@ -168,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}};
@@ -220,36 +213,26 @@ class AnalogAudioView : public View {
4096,
4};
spectrum::WaterfallWidget waterfall{true};
spectrum::WaterfallView waterfall{true};
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,22 +56,6 @@ AnalogTvView::AnalogTvView(
&field_volume,
&tv});
// Set on_change before initialising the field
field_frequency.on_change = [this](rf::Frequency f) {
this->on_target_frequency_changed(f);
};
field_frequency.set_value(receiver_model.target_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.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_show_options = [this]() {
this->on_show_options_frequency();
};
@@ -125,10 +109,6 @@ void AnalogTvView::focus() {
field_frequency.focus();
}
void AnalogTvView::on_target_frequency_changed(rf::Frequency f) {
receiver_model.set_target_frequency(f);
}
void AnalogTvView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
receiver_model.set_baseband_bandwidth(bandwidth_hz);
}
+7 -12
View File
@@ -27,6 +27,7 @@
#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"
@@ -53,6 +54,7 @@ class AnalogTvView : public View {
private:
static constexpr ui::Dim header_height = 3 * 16;
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_tv", app_settings::Mode::RX};
@@ -60,23 +62,18 @@ class AnalogTvView : public View {
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}};
@@ -100,7 +97,6 @@ class AnalogTvView : public View {
tv::TVWidget tv{};
void on_target_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();
@@ -108,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);
+15 -65
View File
@@ -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,20 +47,6 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
&waterfall,
});
field_frequency.set_value(receiver_model.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>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_target_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);
@@ -68,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();
@@ -127,13 +81,13 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
}
CaptureAppView::~CaptureAppView() {
// 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();
}
@@ -141,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);
}
@@ -149,8 +103,4 @@ void CaptureAppView::focus() {
record_view.focus();
}
void CaptureAppView::on_target_frequency_changed(rf::Frequency f) {
receiver_model.set_target_frequency(f);
}
} /* namespace ui */
+15 -33
View File
@@ -26,26 +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"
#include "app_settings.hpp"
#include "radio_state.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 "freqman.hpp"
namespace ui {
@@ -55,42 +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;
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_capture", app_settings::Mode::RX,
app_settings::Options::UseGlobalTargetFrequency};
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.
void on_target_frequency_changed(rf::Frequency f);
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}};
@@ -104,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},
@@ -114,7 +96,7 @@ class CaptureAppView : public View {
16384,
3};
spectrum::WaterfallWidget waterfall{};
spectrum::WaterfallView waterfall{};
};
} /* namespace ui */
+30 -53
View File
@@ -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();
@@ -187,18 +176,7 @@ GpsSimAppView::GpsSimAppView(
&waterfall,
});
field_frequency.set_value(transmitter_model.target_frequency());
field_frequency.set_step(5000);
field_frequency.on_change = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency.set_value(f);
};
};
button_play.on_select = [this](ImageButton&) {
this->toggle();
@@ -214,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() {
+5 -4
View File
@@ -32,6 +32,7 @@
#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"
@@ -70,7 +71,7 @@ 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_file_changed(const std::filesystem::path& new_file_path);
void on_tx_progress(const uint32_t progress);
void toggle();
@@ -102,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
@@ -123,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,
+1 -3
View File
@@ -27,7 +27,6 @@
#include "lge_app.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "ui_textentry.hpp"
#include "string_format.hpp"
@@ -45,8 +44,7 @@ void LGEView::focus() {
LGEView::~LGEView() {
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) {
+17 -29
View File
@@ -51,9 +51,8 @@ void POCSAGLogger::log_decoded(
namespace ui {
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,
@@ -70,28 +69,16 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
&console});
if (!settings_.loaded())
receiver_model.set_target_frequency(initial_target_frequency);
field_frequency.set_value(initial_target_frequency);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.enable();
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
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.target_frequency());
new_view->on_changed = [this](rf::Frequency 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;
@@ -102,13 +89,12 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
logger->append(LOG_ROOT_DIR "/POCSAG.TXT");
audio::output::start();
audio::output::unmute();
baseband::set_pocsag();
}
void POCSAGAppView::focus() {
check_log.focus();
field_frequency.focus();
}
POCSAGAppView::~POCSAGAppView() {
@@ -157,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;
@@ -176,16 +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, receiver_model.target_frequency());
/*if (logger && logging())
logger->log_raw_data(message->packet, receiver_model.target_frequency());*/
}
} /* namespace ui */
+7 -8
View File
@@ -24,6 +24,7 @@
#define __POCSAG_APP_H__
#include "ui_widget.hpp"
#include "ui_freq_field.hpp"
#include "ui_receiver.hpp"
#include "ui_rssi.hpp"
@@ -61,6 +62,7 @@ class POCSAGAppView : public View {
bool logging() const { return check_log.value(); };
bool ignore() const { return check_ignore.value(); };
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_pocsag", app_settings::Mode::RX};
@@ -75,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}};
+35 -60
View File
@@ -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,34 +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(transmitter_model.target_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_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();
};
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);
};
};
@@ -226,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() {
+6 -4
View File
@@ -31,6 +31,7 @@
#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"
@@ -66,7 +67,7 @@ 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_file_changed(const std::filesystem::path& new_file_path);
void on_tx_progress(const uint32_t progress);
void toggle();
@@ -98,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
@@ -119,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,
+1 -3
View File
@@ -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;
@@ -275,8 +274,7 @@ SoundBoardView::SoundBoardView(
SoundBoardView::~SoundBoardView() {
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
@@ -32,7 +32,7 @@ class SpectrumAnalysisModel {
namespace ui {
class SpectrumAnalysisView : public spectrum::WaterfallWidget {
class SpectrumAnalysisView : public spectrum::WaterfallView {
public:
private:
SpectrumAnalysisModel model;
+2 -1
View File
@@ -468,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;
+1 -3
View File
@@ -26,7 +26,6 @@
#include "manchester.hpp"
#include "string_format.hpp"
#include "portapack.hpp"
#include "cpld_update.hpp"
#include "baseband_api.hpp"
#include <cstring>
@@ -274,8 +273,7 @@ void ADSBTxView::focus() {
ADSBTxView::~ADSBTxView() {
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() {
+4 -17
View File
@@ -43,11 +43,8 @@ void AFSKRxView::focus() {
field_frequency.focus();
}
void AFSKRxView::update_freq(rf::Frequency f) {
receiver_model.set_target_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,8 +59,8 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
&button_modem_setup,
&console});
// 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;
@@ -73,17 +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.target_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.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
+7 -8
View File
@@ -26,7 +26,8 @@
#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"
@@ -58,6 +59,7 @@ class AFSKRxView : public View {
private:
void on_data(uint32_t value, bool is_data);
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_afsk", app_settings::Mode::RX};
@@ -74,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},
@@ -104,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{};
+1 -16
View File
@@ -73,12 +73,8 @@ void APRSRxView::focus() {
options_region.focus();
}
void APRSRxView::update_freq(rf::Frequency f) {
receiver_model.set_target_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,
@@ -111,18 +107,7 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
}
};
field_frequency.set_value(receiver_model.target_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.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
options_region.set_selected_index(0, true);
logger = std::make_unique<APRSLogger>();
+7 -9
View File
@@ -26,7 +26,8 @@
#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"
@@ -188,6 +189,7 @@ class APRSRxView : public View {
void on_data(uint32_t value, bool is_data);
bool reset_console = false;
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_aprs", app_settings::Mode::RX};
@@ -202,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}};
@@ -219,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{
@@ -235,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{};
};
+1 -3
View File
@@ -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"
@@ -45,8 +44,7 @@ void APRSTXView::focus() {
APRSTXView::~APRSTXView() {
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() {
+1 -3
View File
@@ -24,7 +24,6 @@
#include "string_format.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
@@ -138,8 +137,7 @@ void BHTView::on_tx_progress(const uint32_t progress, const bool done) {
BHTView::~BHTView() {
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) {
+4 -17
View File
@@ -40,11 +40,8 @@ void BTLERxView::focus() {
field_frequency.focus();
}
void BTLERxView::update_freq(rf::Frequency f) {
receiver_model.set_target_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,8 +53,8 @@ BTLERxView::BTLERxView(NavigationView& nav) {
&button_modem_setup,
&console});
// 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;
@@ -67,17 +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.target_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.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
button_modem_setup.on_select = [&nav](Button&) {
nav.push<ModemSetupView>();
+7 -10
View File
@@ -27,9 +27,10 @@
#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 "ui_record_view.hpp"
#include "utility.hpp"
@@ -47,6 +48,7 @@ class BTLERxView : public View {
private:
void on_data(uint32_t value, bool is_data);
NavigationView& nav_;
RxRadioState radio_state_{
4000000 /* bandwidth */,
4000000 /* sampling rate */
@@ -64,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},
@@ -81,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) {
+1 -3
View File
@@ -23,7 +23,6 @@
#include "ui_coasterp.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
@@ -39,8 +38,7 @@ void CoasterPagerView::focus() {
CoasterPagerView::~CoasterPagerView() {
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() {
+21 -2
View File
@@ -248,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;
}
@@ -325,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() {
@@ -335,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() {
@@ -383,6 +401,7 @@ DebugMenuView::DebugMenuView(NavigationView& nav) {
{"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>(); }},
{"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
}
+15 -5
View File
@@ -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"};
+54
View File
@@ -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 */
+32
View File
@@ -67,6 +67,38 @@ class DfuMenu : public View {
Text text_info_line_8{{15 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 5 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
};
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__*/
+1 -3
View File
@@ -24,7 +24,6 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "cpld_update.hpp"
using namespace portapack;
@@ -201,8 +200,7 @@ void EncodersView::focus() {
EncodersView::~EncodersView() {
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() {
+20 -30
View File
@@ -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_;
+23 -23
View File
@@ -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);
};
+2 -1
View File
@@ -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."; };
+1 -3
View File
@@ -25,7 +25,6 @@
#include "ui_freqman.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "string_format.hpp"
using namespace portapack;
@@ -180,8 +179,7 @@ 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) {
+1 -3
View File
@@ -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) {
+1 -3
View File
@@ -26,7 +26,6 @@
#include "lcr.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "cpld_update.hpp"
#include "serializer.hpp"
@@ -40,8 +39,7 @@ void LCRView::focus() {
LCRView::~LCRView() {
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();
}
/*
+22 -23
View File
@@ -199,6 +199,16 @@ size_t LevelView::change_mode(freqman_index_t new_mod) {
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_wfm_configuration(n); };
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;
}
@@ -206,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 */
+1 -1
View File
@@ -91,7 +91,7 @@ class LevelView : public View {
OptionsField field_mode{
{15 * 8, 1 * 16},
3,
4,
{}};
OptionsField step_mode{
@@ -30,7 +30,7 @@ using namespace portapack;
namespace ui {
void GlassView::focus() {
button_marker.focus();
range_presets.focus();
}
GlassView::~GlassView() {
@@ -26,6 +26,7 @@
#include "ui.hpp"
#include "portapack.hpp"
#include "app_settings.hpp"
#include "baseband_api.hpp"
#include "radio_state.hpp"
#include "receiver_model.hpp"
@@ -62,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;
@@ -71,6 +72,8 @@ 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{};
+8 -5
View File
@@ -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"
@@ -163,7 +162,7 @@ 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_target_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_target_frequency(tx_frequency); // Update freq also for RX = TX
else
@@ -172,7 +171,6 @@ void MicTXView::rxaudio(bool is_on) {
receiver_model.set_vga(rx_vga);
receiver_model.set_rf_amp(rx_amp);
receiver_model.enable();
hackrf::cpld::load_sram_no_verify(); // to have a good RX without any ghost inside Mic App
audio::output::start();
} else { // These incredibly convoluted steps are required for the vumeter to reappear when stopping RX.
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); // This fixes something with AM RX...
@@ -597,6 +595,12 @@ 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);
set_tx(false);
audio::set_rate(audio::Rate::Hz_24000);
@@ -609,8 +613,7 @@ MicTXView::~MicTXView() {
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
+2
View File
@@ -204,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},
};
@@ -309,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()},
+1 -3
View File
@@ -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"
@@ -99,8 +98,7 @@ void MorseView::focus() {
MorseView::~MorseView() {
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&) {
+3 -16
View File
@@ -40,11 +40,8 @@ void NRFRxView::focus() {
field_frequency.focus();
}
void NRFRxView::update_freq(rf::Frequency f) {
receiver_model.set_target_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,
@@ -57,7 +54,7 @@ NRFRxView::NRFRxView(NavigationView& nav) {
&console});
// 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;
@@ -67,17 +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.target_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.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
button_modem_setup.on_select = [&nav](Button&) {
nav.push<ModemSetupView>();
+6 -9
View File
@@ -27,6 +27,7 @@
#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
@@ -47,6 +48,7 @@ class NRFRxView : public View {
private:
void on_data(uint32_t value, bool is_data);
NavigationView& nav_;
RxRadioState radio_state_{
4000000 /* bandwidth */,
4000000 /* sampling rate */
@@ -64,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},
@@ -81,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) {
+366 -202
View File
@@ -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,12 +23,13 @@
*/
#include "ui_playlist.hpp"
#include "convert.hpp"
#include "file_reader.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "utility.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
@@ -36,219 +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) {
auto reader = FileLineReader(playlist_file);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue; // Empty or comment line.
if (error)
return;
auto cols = split_string(line, ',');
if (cols.size() < 3)
continue; // Line doesn't have enough columns.
auto reader = FileLineReader(playlist_file);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue; // Empty or comment line.
playlist_entry entry{};
auto cols = split_string(line, ',');
auto entry = load_entry(trim(cols[0]));
parse_int(cols[0], entry.replay_frequency);
parse_int(cols[2], entry.sample_rate);
if (entry.replay_frequency == 0 || entry.sample_rate == 0)
continue; // Invalid freq or rate.
if (!entry)
continue;
entry.replay_file = std::string{"/"} + std::string{cols[1]};
// Read optional delay value.
if (cols.size() > 1)
parse_int(cols[1], entry->ms_delay);
if (cols.size() == 4) // Optional delay value.
parse_int(cols[3], entry.next_delay);
playlist_db.emplace_back(std::move(entry));
}
playlist_db_.emplace_back(*std::move(entry));
}
total_tracks = playlist_db.size();
playlist_masterdb = playlist_db;
text_track.set(to_string_dec_uint(track_number) + "/" + to_string_dec_uint(total_tracks) + " " + now_play_list_file.string());
tracks_progressbar.set_max(total_tracks);
button_play.focus();
}
void PlaylistView::on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate, uint32_t next_delay) {
File data_file;
Optional<PlaylistView::playlist_entry> PlaylistView::load_entry(fs::path&& path) {
File capture_file;
// Get file size
auto error = data_file.open("/" + new_file_path.string());
auto error = capture_file.open(path);
if (error)
return {};
if (error) {
file_error("C16 file\n" + new_file_path.string() + "\nread error.");
// 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(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;
}
track_number = track_number >= total_tracks ? 1 : track_number + 1; // prevent track_number out of range
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");
now_delay = next_delay;
auto file_size = data_file.size();
auto duration = (file_size * 1000) / (2 * 2 * sample_rate);
on_track_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(to_string_dec_uint(track_number) + "/" + to_string_dec_uint(total_tracks) + " " + now_play_list_file.string());
tracks_progressbar.set_value(track_number);
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) {
on_track_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) {
clean_playlist();
stop(false);
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::clean_playlist() {
total_tracks = 0;
track_number = 0;
playlist_db.clear();
playlist_masterdb.clear();
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);
clean_playlist();
} else {
clean_playlist();
if (std::filesystem::file_exists(now_play_list_file.string())) {
load_file(now_play_list_file);
if (!playlist_db.empty()) {
start();
}
} else {
text_track.set("0/0 no input playlist file");
}
}
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();
on_file_changed(item.replay_file, item.replay_frequency, item.sample_rate, item.next_delay);
transmitter_model.set_target_frequency(item.replay_frequency);
std::unique_ptr<stream::Reader> reader;
auto p = std::make_unique<FileReader>();
auto open_error = p->open(file_path);
if (open_error.is_valid()) {
file_error("Illegal grammar");
return; // Fixes TX bug if there's a file error
} else {
reader = std::move(p);
}
if (reader) {
button_play.set_bitmap(&bitmap_stop);
if (now_delay) { // this `if` is because, if the delay is 0, it will sleep forever
chThdSleepMilliseconds(now_delay);
}
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);
});
}
transmitter_model.set_sampling_rate(sample_rate * 8);
transmitter_model.set_baseband_bandwidth(baseband_bandwidth);
transmitter_model.enable();
current_index_ = 0u;
send_current_track();
}
void PlaylistView::stop(const bool do_loop) {
if (is_active()) {
replay_thread.reset();
/* Advance to the next track in the playlist. */
bool PlaylistView::next_track() {
++current_index_;
// 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.
}
// 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;
return true;
}
void PlaylistView::handle_replay_thread_done(const uint32_t return_code) {
if (return_code == ReplayThread::END_OF_FILE) {
stop(true);
} else if (return_code == ReplayThread::READ_ERROR) {
file_error("Replay thread read error");
/* 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;
}
on_track_progressbar.set_value(0);
// 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() {
// This terminates the underlying chThread.
replay_thread_.reset();
transmitter_model.disable();
// Reset the transmit progress bar.
progressbar_transmit.set_value(0);
update_ui();
}
void PlaylistView::update_ui() {
if (playlist_db_.empty()) {
text_filename.set("-");
text_sample_rate.set("-");
text_duration.set("-");
if (playlist_path_.empty())
text_track.set("Open playlist or add capture.");
else
text_track.set(playlist_path_.filename().string());
progressbar_track.set_value(0);
progressbar_track.set_max(0);
progressbar_transmit.set_max(0);
} else {
chDbgAssert(!at_end(), "update_ui #1", "current_index_ invalid");
text_filename.set(current()->path.filename().string());
text_sample_rate.set(unit_auto_scale(current()->metadata.sample_rate, 3, 0) + "Hz");
auto duration = ms_duration(current()->file_size, current()->metadata.sample_rate, 4);
text_duration.set(to_string_time_ms(duration));
field_frequency.set_value(current()->metadata.center_frequency);
text_track.set(
to_string_dec_uint(current_index_ + 1) + "/" +
to_string_dec_uint(playlist_db_.size()) + " " +
playlist_path_.filename().string());
progressbar_track.set_max(playlist_db_.size() - 1);
progressbar_track.set_value(current_index_);
progressbar_transmit.set_max(current()->file_size);
}
button_play.set_bitmap(is_active() ? &bitmap_stop : &bitmap_play);
}
void PlaylistView::on_tx_progress(uint32_t progress) {
progressbar_transmit.set_value(progress);
}
void PlaylistView::handle_replay_thread_done(uint32_t return_code) {
if (return_code == ReplayThread::END_OF_FILE) {
if (next_track()) {
send_current_track();
return;
}
}
if (return_code == ReplayThread::READ_ERROR)
show_file_error(current()->path, "Replay read failed.");
stop();
}
PlaylistView::PlaylistView(
@@ -257,52 +366,101 @@ PlaylistView::PlaylistView(
baseband::run_image(portapack::spi_flash::image_tag_replay);
add_children({
&button_open,
&text_filename,
&text_sample_rate,
&text_duration,
&tracks_progressbar,
&on_track_progressbar,
&progressbar_track,
&progressbar_transmit,
&field_frequency,
&tx_view, // this handles now the previous rfgain, rfamp
&tx_view,
&check_loop,
&button_play,
&text_track,
&button_prev,
&button_add,
&button_delete,
&button_open,
&button_save,
&button_next,
&waterfall,
});
waterfall.show_audio_spectrum_view(false);
field_frequency.set_value(transmitter_model.target_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.set_value(100'000'000);
field_frequency.on_change = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(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>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency 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;
if (std::filesystem::file_exists(now_play_list_file.string())) {
load_file(now_play_list_file);
} else {
text_track.set("0/0 no input playlist file");
}
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() {
@@ -310,20 +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);
}
} /* namespace ui */
+121 -77
View File
@@ -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.
*
@@ -24,33 +25,35 @@
#define NORMAL_UI false
#include "app_settings.hpp"
#include "bitmap.hpp"
#include "file.hpp"
#include "metadata_file.hpp"
#include "radio_state.hpp"
#include "ui_widget.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 <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_;
@@ -58,82 +61,88 @@ class PlaylistView : public View {
app_settings::SettingsManager settings_{
"tx_playlist", app_settings::Mode::TX};
// add or remove lines here to allow more header and less spectrum view, & vice versa
static constexpr ui::Dim header_height = 4 * 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 on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate, uint32_t next_delay);
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);
void clean_playlist();
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};
uint32_t now_delay{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 tracks_progressbar{
{18 * 8, 1 * 16, 12 * 8, 8}};
ProgressBar on_track_progressbar{
{18 * 8, 3 * 8, 12 * 8, 8}};
{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,
@@ -141,32 +150,67 @@ class PlaylistView : public View {
Color::black()};
Text text_track{
{0 * 8, 3 * 16, 16 * 8, 16},
"0/0 no input playlist file"};
{0 * 8, 3 * 16, 30 * 8, 16}};
spectrum::WaterfallWidget waterfall{};
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);
}};
};
+1 -3
View File
@@ -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"
@@ -40,8 +39,7 @@ void POCSAGTXView::focus() {
POCSAGTXView::~POCSAGTXView() {
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) {
+1 -3
View File
@@ -24,7 +24,6 @@
#include "portapack.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "portapack_shared_memory.hpp"
#include <cstring>
@@ -161,8 +160,7 @@ void RDSView::focus() {
RDSView::~RDSView() {
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() {
+178 -104
View File
@@ -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()) {
@@ -297,23 +309,12 @@ void ReconView::handle_retune() {
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,16 +362,21 @@ void ReconView::focus() {
}
ReconView::~ReconView() {
// Save recon config.
recon_stop_recording();
recon_save_config_to_sd();
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,
@@ -383,7 +389,7 @@ ReconView::ReconView(NavigationView& nav)
&field_lock_wait,
&button_config,
&button_scanner_mode,
&button_looking_glass,
&button_loop_config,
&file_name,
&rssi,
&text_cycle,
@@ -406,7 +412,14 @@ 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
@@ -431,6 +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();
auto_record_locked = persistent_memory::recon_auto_record_locked();
button_manual_start.on_select = [this, &nav](ButtonWithEncoder& button) {
clear_freqlist_for_ui_action();
@@ -539,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);
@@ -603,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()) {
@@ -616,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;
@@ -636,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);
@@ -687,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);
@@ -768,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]
@@ -803,11 +820,13 @@ ReconView::ReconView(NavigationView& nav)
};
button_config.on_select = [this, &nav](Button&) {
if (is_recording) // disabling config while recording
return;
clear_freqlist_for_ui_action();
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";
@@ -815,14 +834,15 @@ ReconView::ReconView(NavigationView& nav)
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();
auto_record_locked = persistent_memory::recon_auto_record_locked();
frequency_file_load(false);
freqlist_cleared_for_ui_action = false;
if (autostart) {
recon_resume();
@@ -839,6 +859,9 @@ ReconView::ReconView(NavigationView& nav)
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();
};
@@ -897,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 + "=>" + 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);
}
}
}
@@ -1004,6 +1023,14 @@ 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) {
@@ -1030,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
@@ -1052,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;
@@ -1072,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 || recon_match_mode == RECON_MATCH_SPARSE)
timer -= STATS_UPDATE_INTERVAL;
if (!continuous_lock || recon_match_mode == RECON_MATCH_SPARSE) {
timer -= time_interval;
}
if (timer < 0) {
timer = 0;
}
@@ -1084,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 */
@@ -1184,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
@@ -1236,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
@@ -1248,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>");
@@ -1289,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,6 +1369,7 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
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); };
text_ctcss.set(" ");
recording_sampling_rate = 12000;
break;
case NFM_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw);
@@ -1312,6 +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); };
recording_sampling_rate = 24000;
break;
case WFM_MODULATION:
freqman_set_bandwidth_option(new_mod, field_bw);
@@ -1322,18 +1390,35 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
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); };
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();
@@ -1341,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 */
+77 -71
View File
@@ -45,7 +45,6 @@
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:
@@ -65,6 +64,7 @@ class ReconView : public View {
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();
@@ -84,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;
bool user_pause = 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};
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()},
{{3 * 8, 8 * 16}, "START END", Color::light_grey()},
{{0 * 8, (22 * 8)}, " S: ", Color::light_grey()},
{{0 * 8, (24 * 8) + 4}, "NBLCK: x W,L: , ", Color::light_grey()},
{{0 * 8, (26 * 8) + 4}, "MODE: , SQUELCH: ", 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}};
@@ -160,26 +166,31 @@ class ReconView : public View {
AudioVolumeField field_volume{
{24 * 8, 0 * 16}};
Text file_name{
// show file used
{0, 1 * 16, SCREEN_W, 16},
};
Text desc_cycle{
{0, 2 * 16, SCREEN_W, 16},
};
RSSI rssi{
{0 * 16, 2 * 16 + 2, SCREEN_W - 8 * 8 + 4, 12},
{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{
@@ -203,23 +214,18 @@ class ReconView : public View {
{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, 7 * 16, 7 * 8, 28},
{SCREEN_W - 7 * 8, 3 * 16, 7 * 8, 28},
"RECON"};
Text file_name{
// show file used
{0, 1 * 16, SCREEN_W, 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, 9 * 16, 11 * 8, 28},
@@ -271,11 +277,11 @@ class ReconView : public View {
OptionsField field_mode{
{6 * 8, (26 * 8) + 4},
3,
4,
{}};
OptionsField field_bw{
{10 * 8, (26 * 8) + 4},
{11 * 8, (26 * 8) + 4},
6,
{}};
@@ -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,7 +94,6 @@ 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;
@@ -106,6 +103,7 @@ void ReconSetupViewMore::save() {
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());
persistent_memory::set_recon_auto_record_locked(checkbox_auto_record_locked.value());
};
void ReconSetupViewMain::focus() {
@@ -120,12 +118,14 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
add_children({&checkbox_load_freqs,
&checkbox_load_ranges,
&checkbox_load_hamradios,
&checkbox_update_ranges_when_recon});
&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());
checkbox_auto_record_locked.set_value(persistent_memory::recon_auto_record_locked());
};
void ReconSetupViewMore::focus() {
+10 -10
View File
@@ -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,12 +98,8 @@ 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"};
};
@@ -136,6 +132,10 @@ class ReconSetupViewMore : public View {
{1 * 8, 102},
3,
"auto update m-ranges"};
Checkbox checkbox_auto_record_locked{
{1 * 8, 132},
3,
"auto record locked periods"};
};
class ReconSetupView : public View {
@@ -151,10 +151,10 @@ class ReconSetupView : public View {
private:
NavigationView& nav_;
std::string input_file = {"RECON"};
std::string output_file = {"RECON_RESULTS"};
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};
+11 -1
View File
@@ -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() {
@@ -447,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);
+1 -1
View File
@@ -96,7 +96,7 @@ class ScannerView : public View {
private:
app_settings::SettingsManager settings_{
"scanner", app_settings::Mode::RX};
"rx_scanner", app_settings::Mode::RX};
NavigationView& nav_;
RxRadioState radio_state_{};
+2 -2
View File
@@ -382,7 +382,7 @@ SearchView::SearchView(
this->on_range_changed();
};
field_frequency_min.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_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);
};
@@ -394,7 +394,7 @@ SearchView::SearchView(
this->on_range_changed();
};
field_frequency_max.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_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);
};
+2 -4
View File
@@ -147,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{
+133 -103
View File
@@ -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,46 +367,43 @@ 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_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_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_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,36 +429,36 @@ 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_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_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({
+51 -15
View File
@@ -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},
+4 -15
View File
@@ -41,7 +41,8 @@ void SondeLogger::on_packet(const sonde::Packet& packet) {
namespace ui {
SondeView::SondeView(NavigationView& nav) {
SondeView::SondeView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_sonde);
add_children({&labels,
@@ -66,20 +67,9 @@ SondeView::SondeView(NavigationView& nav) {
&button_see_map});
if (!settings_.loaded())
receiver_model.set_target_frequency(initial_target_frequency);
field_frequency.set_value(initial_target_frequency);
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(500); // euquiq: was 10000, but we are using this for fine-tunning
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
geopos.set_read_only(true);
@@ -117,7 +107,6 @@ SondeView::SondeView(NavigationView& nav) {
logger->append(LOG_ROOT_DIR "/SONDE.TXT");
audio::output::start();
audio::output::speaker_unmute();
// inject a PitchRSSIConfigureMessage in order to arm
// the pitch rssi events that will be used by the
@@ -138,7 +127,7 @@ SondeView::~SondeView() {
}
void SondeView::focus() {
field_vga.focus();
field_frequency.focus();
}
// used to convert float to character pointer, since unfortunately function like
+5 -4
View File
@@ -25,6 +25,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_rssi.hpp"
#include "ui_qrcode.hpp"
#include "ui_geomap.hpp"
@@ -65,6 +66,7 @@ class SondeView : public View {
std::string title() const override { return "Radiosnd RX"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{
1750000 /* bandwidth */,
2457600 /* sampling rate */
@@ -96,9 +98,9 @@ class SondeView : public View {
{{4 * 8, 7 * 16}, "Temp:", Color::light_grey()},
{{0 * 8, 8 * 16}, "Humidity:", Color::light_grey()}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
};
nav_};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
@@ -110,8 +112,7 @@ class SondeView : public View {
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
@@ -20,7 +20,6 @@
*/
#include "ui_spectrum_painter.hpp"
#include "cpld_update.hpp"
#include "bmp.hpp"
#include "baseband_api.hpp"
@@ -58,17 +57,9 @@ SpectrumPainterView::SpectrumPainterView(
input_image.set_parent_rect(view_rect);
input_text.set_parent_rect(view_rect);
field_frequency.set_value(transmitter_model.target_frequency());
freqman_set_bandwidth_option(SPEC_MODULATION, option_bandwidth);
field_frequency.set_step(5000);
field_frequency.on_change = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
tx_gain = transmitter_model.tx_gain();
field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max ).
@@ -176,7 +167,6 @@ void SpectrumPainterView::frame_sync() {
SpectrumPainterView::~SpectrumPainterView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify();
baseband::shutdown();
}
@@ -94,9 +94,9 @@ class SpectrumPainterView : public View {
{{1 * 8, footer_location + 2 * 16}, "BW: Du: P:", Color::light_grey()},
};
FrequencyField field_frequency{
TxFrequencyField field_frequency{
{0 * 8, footer_location + 1 * 16},
};
nav_};
NumberField field_rfgain{
{14 * 8, footer_location + 1 * 16},
@@ -127,7 +127,7 @@ class SpectrumPainterView : public View {
OptionsField option_bandwidth{
{4 * 8, footer_location + 2 * 16},
5,
{BW_OPTIONS}};
{}};
NumberField field_duration{
{13 * 8, footer_location + 2 * 16},
+1 -3
View File
@@ -25,7 +25,6 @@
#include "portapack.hpp"
#include "hackrf_hal.hpp"
#include "cpld_update.hpp"
#include <cstring>
#include <stdio.h>
@@ -89,8 +88,7 @@ void SSTVTXView::paint(Painter&) {
SSTVTXView::~SSTVTXView() {
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 SSTVTXView::prepare_scanline() {
+2 -12
View File
@@ -40,7 +40,8 @@ void TestLogger::log_raw_data(const testapp::Packet& packet, const int32_t alt)
namespace ui {
TestView::TestView(NavigationView& nav) {
TestView::TestView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_test);
add_children({&labels,
@@ -54,18 +55,7 @@ TestView::TestView(NavigationView& nav) {
&button_cal,
&check_log});
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(10000);
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
+4 -2
View File
@@ -25,6 +25,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_rssi.hpp"
#include "event_m0.hpp"
@@ -59,6 +60,7 @@ class TestView : public View {
std::string title() const override { return "Test app"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{
1750000 /* bandwidth */,
2457600 * 2 /* sampling rate */
@@ -75,9 +77,9 @@ class TestView : public View {
Labels labels{
{{0 * 8, 1 * 16}, "Data:", Color::light_grey()}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
};
nav_};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
+43 -28
View File
@@ -42,10 +42,9 @@ namespace ui {
/* TextViewer *******************************************************/
TextViewer::TextViewer(Rect parent_rect)
: Widget(parent_rect),
max_line{static_cast<uint8_t>(parent_rect.height() / char_height)},
max_col{static_cast<uint8_t>(parent_rect.width() / char_width)} {
: Widget(parent_rect) {
set_focusable(true);
set_font_zoom(false);
}
void TextViewer::paint(Painter& painter) {
@@ -138,6 +137,16 @@ uint32_t TextViewer::offset() const {
return 0;
}
void TextViewer::cursor_home() {
cursor_.col = 0;
redraw();
}
void TextViewer::cursor_end() {
cursor_.col = line_length() - 1;
redraw();
}
uint16_t TextViewer::line_length() {
return file_->line_length(cursor_.line);
}
@@ -158,18 +167,14 @@ bool TextViewer::apply_scrolling_constraints(int16_t delta_line, int16_t delta_c
++new_line;
}
// Snap to first/last to make navigating easier.
if (new_line < 0 && new_col > 0) {
// Snap to first/last line to make navigating easier.
if (new_line < 0 && cursor_.line > 0) {
new_line = 0;
new_col = 0;
} else if (new_line >= (int32_t)file_->line_count()) {
auto last_line = file_->line_count() - 1;
int32_t last_col = file_->line_length(last_line) - 1;
if (new_col < last_col) {
if (cursor_.line < last_line)
new_line = last_line;
new_col = last_col;
}
}
if (new_line < 0 || (uint32_t)new_line >= file_->line_count())
@@ -177,7 +182,6 @@ bool TextViewer::apply_scrolling_constraints(int16_t delta_line, int16_t delta_c
new_line_length = file_->line_length(new_line);
// TODO: don't wrap with encoder?
// Wrap or clamp column.
if (new_line_length == 0)
new_col = 0;
@@ -207,7 +211,7 @@ void TextViewer::paint_text(Painter& painter, uint32_t line, uint16_t col) {
if (result && *result > 0)
painter.draw_string(
{0, r.top() + (int)i * char_height},
Styles::white_small, {buffer, *result});
style(), {buffer, *result});
// Clear empty line sections. This is less visually jarring than full clear.
int32_t clear_width = max_col - (result ? *result : 0);
@@ -216,7 +220,7 @@ void TextViewer::paint_text(Painter& painter, uint32_t line, uint16_t col) {
{(max_col - clear_width) * char_width,
r.top() + (int)i * char_height,
clear_width * char_width, char_height},
Styles::white_small.background);
style().background);
}
}
@@ -237,8 +241,8 @@ void TextViewer::paint_cursor(Painter& painter) {
};
// Clear old cursor. CONSIDER: XOR cursor?
draw_cursor(paint_state_.line, paint_state_.col, Styles::white_small.background);
draw_cursor(cursor_.line, cursor_.col, Styles::white_small.foreground);
draw_cursor(paint_state_.line, paint_state_.col, style().background);
draw_cursor(cursor_.line, cursor_.col, style().foreground);
paint_state_.line = cursor_.line;
paint_state_.col = cursor_.col;
}
@@ -252,6 +256,15 @@ void TextViewer::reset_file(FileWrapper* file) {
redraw(true);
}
void TextViewer::set_font_zoom(bool zoom) {
font_zoom = zoom;
font_style = font_zoom ? &Styles::white : &Styles::white_small;
char_height = style().font.line_height();
char_width = style().font.char_width();
max_line = (uint8_t)(parent_rect().height() / char_height);
max_col = (uint8_t)(parent_rect().width() / char_width);
}
/* TextEditorMenu ***************************************************/
TextEditorMenu::TextEditorMenu()
@@ -259,9 +272,9 @@ TextEditorMenu::TextEditorMenu()
add_children(
{
&rect_frame,
&button_cut,
&button_paste,
&button_copy,
&button_home,
&button_end,
&button_zoom,
&button_delline,
&button_edit,
&button_addline,
@@ -310,14 +323,20 @@ TextEditorView::TextEditorView(NavigationView& nav)
};
menu.hidden(true);
menu.on_cut() = [this]() {
show_nyi();
menu.on_home() = [this]() {
viewer.cursor_home();
hide_menu(true);
};
menu.on_paste() = [this]() {
show_nyi();
menu.on_end() = [this]() {
viewer.cursor_end();
hide_menu(true);
};
menu.on_copy() = [this]() {
show_nyi();
menu.on_zoom() = [this]() {
viewer.toggle_font_zoom();
refresh_ui();
hide_menu(true);
};
menu.on_delete_line() = [this]() {
@@ -490,10 +509,6 @@ void TextEditorView::show_edit_line() {
});
}
void TextEditorView::show_nyi() {
nav_.display_modal("Soon...", "Coming soon.");
}
void TextEditorView::show_save_prompt(std::function<void()> continuation) {
if (!file_dirty_) {
if (continuation)
+27 -22
View File
@@ -72,15 +72,23 @@ class TextViewer : public Widget {
uint32_t col() const { return cursor_.col; }
uint32_t offset() const;
void cursor_home();
void cursor_end();
// Gets the length of the current line.
uint16_t line_length();
private:
static constexpr int8_t char_width = 5;
static constexpr int8_t char_height = 8;
const Style& style() { return *font_style; }
void set_font_zoom(bool zoom);
void toggle_font_zoom() { set_font_zoom(!font_zoom); };
const uint8_t max_line = 32;
const uint8_t max_col = 48;
private:
bool font_zoom{};
const Style* font_style{};
int8_t char_width{};
int8_t char_height{};
uint8_t max_line{};
uint8_t max_col{};
/* Returns true if the cursor was updated. */
bool apply_scrolling_constraints(
@@ -120,14 +128,12 @@ class TextEditorMenu : public View {
void on_show() override;
void on_hide() override;
std::function<void()>& on_cut() { return button_cut.on_select; }
std::function<void()>& on_paste() { return button_paste.on_select; }
std::function<void()>& on_copy() { return button_copy.on_select; }
std::function<void()>& on_home() { return button_home.on_select; }
std::function<void()>& on_end() { return button_end.on_select; }
std::function<void()>& on_zoom() { return button_zoom.on_select; }
std::function<void()>& on_delete_line() { return button_delline.on_select; }
std::function<void()>& on_edit_line() { return button_edit.on_select; }
std::function<void()>& on_add_line() { return button_addline.on_select; }
std::function<void()>& on_open() { return button_open.on_select; }
std::function<void()>& on_save() { return button_save.on_select; }
std::function<void()>& on_exit() { return button_exit.on_select; }
@@ -139,23 +145,23 @@ class TextEditorMenu : public View {
{0 * 8, 0 * 8, 23 * 8, 23 * 8},
Color::dark_grey()};
NewButton button_cut{
NewButton button_home{
{1 * 8, 1 * 8, 7 * 8, 7 * 8},
"Cut",
&bitmap_icon_cut,
"Home",
&bitmap_arrow_left,
Color::dark_grey()};
NewButton button_paste{
NewButton button_end{
{8 * 8, 1 * 8, 7 * 8, 7 * 8},
"Paste",
&bitmap_icon_paste,
"End",
&bitmap_arrow_right,
Color::dark_grey()};
NewButton button_copy{
NewButton button_zoom{
{15 * 8, 1 * 8, 7 * 8, 7 * 8},
"Copy",
&bitmap_icon_copy,
Color::dark_grey()};
"Zoom",
&bitmap_icon_search,
Color::orange()};
NewButton button_delline{
{1 * 8, 8 * 8, 7 * 8, 7 * 8},
@@ -220,7 +226,6 @@ class TextEditorView : public View {
void hide_menu(bool hidden = true);
void show_file_picker(bool immediate = true);
void show_edit_line();
void show_nyi();
void show_save_prompt(std::function<void()> continuation);
void prepare_for_write();
@@ -258,4 +263,4 @@ class TextEditorView : public View {
} // namespace ui
#endif // __UI_TEXT_EDITOR_H__
#endif // __UI_TEXT_EDITOR_H__
+1 -3
View File
@@ -26,7 +26,6 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "cpld_update.hpp"
using namespace portapack;
using namespace encoders;
@@ -39,8 +38,7 @@ void TouchTunesView::focus() {
TouchTunesView::~TouchTunesView() {
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 TouchTunesView::stop_tx() {
+38 -57
View File
@@ -23,8 +23,10 @@
#include "ui_whipcalc.hpp"
#include "ch.h"
#include "portapack.hpp"
#include "convert.hpp"
#include "event_m0.hpp"
#include "file_reader.hpp"
#include "portapack.hpp"
#include <cstring>
@@ -102,7 +104,8 @@ void WhipCalcView::update_result() {
}
}
WhipCalcView::WhipCalcView(NavigationView& nav) {
WhipCalcView::WhipCalcView(NavigationView& nav)
: nav_{nav} {
add_children({&labels,
//&antennas_on_memory,
&field_frequency,
@@ -112,28 +115,12 @@ WhipCalcView::WhipCalcView(NavigationView& nav) {
&console,
&button_exit});
File antennas_file;
auto error = antennas_file.open("WHIPCALC/ANTENNAS.TXT");
if (!error.is_valid()) {
std::string line;
char one_char[1];
for (size_t pointer = 0; pointer < antennas_file.size(); pointer++) {
antennas_file.seek(pointer);
antennas_file.read(one_char, 1);
if ((int)one_char[0] >= ' ')
line += one_char[0]; // Add it to the textline
else if (one_char[0] == '\n') { // New Line
txtline_process(line); // make sense of this textline
line.clear(); // Ready for next textline
}
}
if (line.length() > 0)
txtline_process(line); // Last line had no newline at end ?
}
// Try loading antennas from file.
load_antenna_db();
if (!antenna_db.size())
add_default_antenna();
// antennas_on_memory.set(to_string_dec_int(antenna_db.size(),0) + " antennas"); //tell user
options_type.set_selected_index(2); // Quarter wave
@@ -142,56 +129,50 @@ WhipCalcView::WhipCalcView(NavigationView& nav) {
};
field_frequency.set_step(1000000); // 1MHz step
field_frequency.on_change = [this](rf::Frequency) {
this->update_result();
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency.set_value(f);
this->update_result();
};
field_frequency.updated = [this](rf::Frequency) {
update_result();
};
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
};
field_frequency.set_value(transmitter_model.target_frequency());
update_result();
}
void ui::WhipCalcView::txtline_process(std::string& line) {
if (line.find("#") != std::string::npos)
return; // Line is just a comment
void WhipCalcView::load_antenna_db() {
File antennas_file;
auto error = antennas_file.open("/WHIPCALC/ANTENNAS.TXT");
char separator = ',';
size_t previous = 0;
uint16_t value = 0;
antenna_entry new_antenna;
size_t current = line.find(separator);
if (error)
return;
while (current != std::string::npos) {
if (!previous)
new_antenna.label.assign(line, 0, current); // antenna label
else {
value = std::stoi(line.substr(previous, current - previous));
if (!value) return; // No element length? abort antenna
new_antenna.elements.push_back(value); // Store this new element
auto reader = FileLineReader(antennas_file);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue; // Empty or comment line.
auto cols = split_string(line, ',');
if (cols.size() < 2)
continue; // Line doesn't have enough columns.
antenna_entry new_antenna{
std::string{cols[0]}};
// Add antenna elements.
for (auto i = 1ul; i < cols.size(); ++i) {
uint16_t length = 0;
if (parse_int(cols[i], length)) {
new_antenna.elements.push_back(length);
}
}
previous = current + 1;
current = line.find(separator, previous); // Search for next space delimiter
if (!new_antenna.elements.empty())
antenna_db.push_back(std::move(new_antenna));
}
if (!previous) return; // Not even a label ? drop this antenna!
value = std::stoi(line.substr(previous, current - previous)); // Last element
if (!value) return;
new_antenna.elements.push_back(value);
antenna_db.push_back(new_antenna); // Add this antenna
}
void ui::WhipCalcView::add_default_antenna() {
void WhipCalcView::add_default_antenna() {
antenna_db.push_back({"ANT500", {185, 315, 450, 586, 724, 862}}); // store a default ant500
}
} // namespace ui
+5 -4
View File
@@ -26,6 +26,7 @@
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include <vector>
@@ -47,10 +48,10 @@ class WhipCalcView : public View {
std::vector<uint16_t> elements{};
};
NavigationView& nav_;
std::vector<antenna_entry> antenna_db{};
void update_result();
uint16_t string_to_number(std::string);
void txtline_process(std::string&);
void load_antenna_db();
void add_default_antenna();
Labels labels{
@@ -59,9 +60,9 @@ class WhipCalcView : public View {
{{5 * 8, 3 * 16}, "Metric:", Color::light_grey()},
{{3 * 8, 4 * 16}, "Imperial:", Color::light_grey()}};
FrequencyField field_frequency{
TxFrequencyField field_frequency{
{13 * 8, 1 * 16},
};
nav_};
OptionsField options_type{
{13 * 8, 2 * 16},
+45 -11
View File
@@ -134,6 +134,10 @@ static audio::Codec* audio_codec = nullptr;
namespace output {
static bool cfg_speaker_disable = false;
static bool nav_requested_mute = false;
static bool app_requested_mute = false;
void start() {
i2s::i2s0::tx_start();
unmute();
@@ -145,23 +149,49 @@ void stop() {
}
void mute() {
app_requested_mute = true;
i2s::i2s0::tx_mute();
audio_codec->headphone_disable();
}
void unmute() {
i2s::i2s0::tx_unmute();
audio_codec->headphone_enable();
}
void speaker_mute() {
i2s::i2s0::tx_mute();
audio_codec->speaker_disable();
}
void unmute() {
app_requested_mute = false;
if (!nav_requested_mute) {
i2s::i2s0::tx_unmute();
audio_codec->headphone_enable();
if (!cfg_speaker_disable) {
audio_codec->speaker_enable();
}
}
}
// The following functions are used by the navigation-bar Speaker Mute only,
// and override all other audio mute/unmute requests from apps
void speaker_mute() {
nav_requested_mute = true;
i2s::i2s0::tx_mute();
audio_codec->speaker_disable();
audio_codec->headphone_disable();
}
void speaker_unmute() {
i2s::i2s0::tx_unmute();
audio_codec->speaker_enable();
nav_requested_mute = false;
if (!app_requested_mute) {
unmute();
}
}
void update_audio_mute() {
cfg_speaker_disable = portapack::persistent_memory::config_speaker_disable();
if (cfg_speaker_disable) {
audio_codec->speaker_disable();
}
if (portapack::persistent_memory::config_audio_mute())
speaker_mute();
else
speaker_unmute();
}
} /* namespace output */
@@ -242,4 +272,8 @@ void set_rate(const Rate rate) {
clock_manager.set_base_audio_clock_divider(toUType(rate));
}
bool speaker_disable_supported() {
return audio_codec->speaker_disable_supported();
}
} /* namespace audio */
+5
View File
@@ -43,6 +43,7 @@ class Codec {
virtual void speaker_enable() = 0;
virtual void speaker_disable() = 0;
virtual bool speaker_disable_supported() const = 0;
virtual void headphone_enable() = 0;
virtual void headphone_disable() = 0;
@@ -65,8 +66,11 @@ void stop();
void mute();
void unmute();
void speaker_disable();
void speaker_enable();
void speaker_mute();
void speaker_unmute();
void update_audio_mute();
} /* namespace output */
@@ -104,6 +108,7 @@ size_t reg_bits();
void init(audio::Codec* const codec);
void shutdown();
bool speaker_disable_supported();
enum class Rate {
Hz_12000 = 4,
+228
View File
@@ -5341,6 +5341,234 @@ static constexpr Bitmap bitmap_icon_new_dir{
{16, 16},
bitmap_icon_new_dir_data};
static constexpr uint8_t bitmap_icon_paint_data[] = {
0xFE,
0x3F,
0xFF,
0x3F,
0xFF,
0xFF,
0xFF,
0xBF,
0xFE,
0xBF,
0x00,
0x80,
0x80,
0xFF,
0x80,
0x00,
0x80,
0x00,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
0xC0,
0x01,
};
static constexpr Bitmap bitmap_icon_paint{
{16, 16},
bitmap_icon_paint_data};
static constexpr uint8_t bitmap_icon_touchtunes_data[] = {
0xE0,
0x07,
0x30,
0x0C,
0x7C,
0x3E,
0xC4,
0x23,
0x26,
0x64,
0x12,
0x48,
0x0F,
0xF3,
0x09,
0x95,
0x0F,
0xF1,
0x09,
0x91,
0x0F,
0xF1,
0xC9,
0x91,
0xE9,
0x91,
0xC9,
0x90,
0x0F,
0xF0,
0xFF,
0xFF,
};
static constexpr Bitmap bitmap_icon_touchtunes{
{16, 16},
bitmap_icon_touchtunes_data};
static constexpr uint8_t bitmap_arrow_left_data[] = {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x20,
0x00,
0x30,
0x00,
0x38,
0x00,
0xFC,
0x7F,
0xFE,
0x7F,
0xFC,
0x7F,
0x38,
0x00,
0x30,
0x00,
0x20,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
static constexpr Bitmap bitmap_arrow_left{
{16, 16},
bitmap_arrow_left_data};
static constexpr uint8_t bitmap_arrow_right_data[] = {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x04,
0x00,
0x0C,
0x00,
0x1C,
0xFE,
0x3F,
0xFE,
0x7F,
0xFE,
0x3F,
0x00,
0x1C,
0x00,
0x0C,
0x00,
0x04,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
static constexpr Bitmap bitmap_arrow_right{
{16, 16},
bitmap_arrow_right_data};
static constexpr uint8_t bitmap_icon_speaker_and_headphones_data[] = {
0x20,
0x10,
0x30,
0x20,
0x38,
0x44,
0x3E,
0x48,
0x3E,
0x91,
0x3E,
0x92,
0x38,
0x92,
0x30,
0x92,
0x20,
0x92,
0x00,
0x92,
0x30,
0x91,
0x48,
0x48,
0x84,
0x44,
0x84,
0x20,
0x86,
0x11,
0x86,
0x01,
};
static constexpr Bitmap bitmap_icon_speaker_and_headphones{
{16, 16},
bitmap_icon_speaker_and_headphones_data};
static constexpr uint8_t bitmap_icon_speaker_and_headphones_mute_data[] = {
0x40,
0x00,
0x60,
0x44,
0x70,
0x6C,
0x7C,
0x38,
0x7C,
0x10,
0x7C,
0x38,
0x70,
0x6C,
0x60,
0x44,
0x40,
0x00,
0x00,
0x44,
0x30,
0x6C,
0x48,
0x38,
0x84,
0x10,
0x84,
0x38,
0x86,
0x6D,
0x86,
0x45,
};
static constexpr Bitmap bitmap_icon_speaker_and_headphones_mute{
{16, 16},
bitmap_icon_speaker_and_headphones_mute_data};
} /* namespace ui */
#endif /*__BITMAP_HPP__*/
+18
View File
@@ -488,6 +488,24 @@ path operator/(const path& lhs, const path& rhs) {
return result;
}
bool path_iequal(
const path& lhs,
const 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;
}
directory_iterator::directory_iterator(
std::filesystem::path path,
std::filesystem::path wild)
+3
View File
@@ -168,6 +168,9 @@ bool operator>(const path& lhs, const path& rhs);
path operator+(const path& lhs, const path& rhs);
path operator/(const path& lhs, const path& rhs);
/* Case insensitive path equality on underlying "native" string. */
bool path_iequal(const path& lhs, const path& rhs);
using file_status = BYTE;
static_assert(sizeof(path::value_type) == 2, "sizeof(std::filesystem::path::value_type) != 2");
+40 -22
View File
@@ -30,7 +30,8 @@ using options_t = std::vector<option_t>;
options_t freqman_entry_modulations = {
{"AM", 0},
{"NFM", 1},
{"WFM", 2}};
{"WFM", 2},
{"SPEC", 3}};
options_t freqman_entry_bandwidths[4] = {
{// AM
@@ -48,6 +49,25 @@ options_t freqman_entry_bandwidths[4] = {
{"40k", 2},
{"180k", 1},
{"200k", 0},
},
{
// SPEC
{"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) */
{"650k", 650000},
{"750k", 750000}, /* From this BW onwards, the LCD is ok, but the recorded file is 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)
}};
options_t freqman_entry_steps = {
@@ -86,27 +106,22 @@ options_t freqman_entry_steps_short = {
{"500kHz", 500000},
{"1MHz", 1000000}};
bool load_freqman_file(std::string& file_stem, freqman_db& db) {
return load_freqman_file_ex(file_stem, db, true, true, true, FREQMAN_MAX_PER_FILE);
}
bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freqs, bool load_ranges, bool load_hamradios, uint8_t max_num_freqs = FREQMAN_MAX_PER_FILE) {
bool load_freqman_file(std::string& file_stem, freqman_db& db, bool load_freqs, bool load_ranges, bool load_hamradios, uint8_t max_num_freqs) {
// swap with empty vector to ensure memory is immediately released
std::vector<freqman_entry>().swap(db);
File freqman_file;
size_t length, n = 0, file_position = 0;
char* pos;
char* line_start;
char* line_end;
std::string description;
rf::Frequency frequency_a, frequency_b;
char file_data[FREQMAN_READ_BUF_SIZE + 1];
freqman_entry_type type;
freqman_index_t modulation = 0;
freqman_index_t bandwidth = 0;
freqman_index_t step = 0;
freqman_index_t tone = 0;
File freqman_file{};
size_t length = 0, n = 0, file_position = 0;
char* pos = NULL;
char* line_start = NULL;
char* line_end = NULL;
std::string description{NULL};
rf::Frequency frequency_a = 0, frequency_b = 0;
char file_data[FREQMAN_READ_BUF_SIZE + 1] = {0};
freqman_entry_type type = NOTYPE;
freqman_index_t modulation = -1;
freqman_index_t bandwidth = -1;
freqman_index_t step = -1;
freqman_index_t tone = -1;
auto result = freqman_file.open("FREQMAN/" + file_stem + ".TXT");
if (result.is_valid())
@@ -137,7 +152,7 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq
bandwidth = -1;
step = -1;
tone = -1;
type = ERROR_TYPE;
type = NOTYPE;
frequency_a = frequency_b = 0;
// Read frequency
@@ -207,9 +222,11 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq
pos += 2;
length = std::min(strcspn(pos, ",\x0A"), (size_t)FREQMAN_DESC_MAX_LEN);
description = string(pos, length);
description.shrink_to_fit();
}
if ((type == SINGLE && load_freqs) || (type == RANGE && load_ranges) || (type == HAMRADIO && load_hamradios)) {
db.push_back({frequency_a, frequency_b, description, type, modulation, bandwidth, step, tone});
freqman_entry entry = {frequency_a, frequency_b, std::move(description), type, modulation, bandwidth, step, tone};
db.emplace_back(entry);
n++;
if (n > max_num_freqs) return true;
}
@@ -243,6 +260,7 @@ bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freq
}
}
}
db.shrink_to_fit();
return true;
}
+29 -33
View File
@@ -32,11 +32,11 @@
#include "string_format.hpp"
#include "ui_widget.hpp"
#define FREQMAN_DESC_MAX_LEN 24 // This is the number of characters that can be drawn in front of "R: TEXT..." before taking a full screen line
#define FREQMAN_MAX_PER_FILE 60 // Maximum of entries we can read. This is a hardware limit
// It was tested and lowered to leave a bit of space to the caller
#define FREQMAN_MAX_PER_FILE_STR "60" // STRING OF FREQMAN_MAX_PER_FILE
#define FREQMAN_READ_BUF_SIZE 96 // max freqman line size including desc is 90, +6 for a bit of space
#define FREQMAN_DESC_MAX_LEN 24 // This is the number of characters that can be drawn in front of "R: TEXT..." before taking a full screen line
#define FREQMAN_MAX_PER_FILE 90 // Maximum of entries we can read. This is a hardware limit
// It was tested and lowered to leave a bit of space to the caller
#define FREQMAN_READ_BUF_SIZE 96 // max freqman line size including desc is 90, + a bit of space
using namespace ui;
using namespace std;
@@ -45,58 +45,54 @@ using namespace tonekey;
// needs to be signed as -1 means not set
typedef int8_t freqman_index_t;
enum freqman_error {
enum freqman_error : int8_t {
NO_ERROR = 0,
ERROR_ACCESS,
ERROR_NOFILES,
ERROR_DUPLICATE
};
enum freqman_entry_type {
enum freqman_entry_type : int8_t {
SINGLE = 0, // f=
RANGE, // a=,b=
HAMRADIO, // r=,t=
ERROR_TYPE
NOTYPE // undetected
};
enum freqman_entry_modulation {
enum freqman_entry_modulation : uint8_t {
AM_MODULATION = 0,
NFM_MODULATION,
WFM_MODULATION,
MODULATION_DEF,
ERROR_MODULATION
SPEC_MODULATION,
};
// Entry step placed for AlainD freqman version (or any other enhanced version)
enum freqman_entry_step {
STEP_DEF = -1, // default
AM_US, // 10 kHz AM/CB
AM_EUR, // 9 kHz LW/MW
NFM_1, // 12,5 kHz (Analogic PMR 446)
NFM_2, // 6,25 kHz (Digital PMR 446)
FM_1, // 100 kHz
FM_2, // 50 kHz
N_1, // 25 kHz
N_2, // 250 kHz
AIRBAND, // AIRBAND 8,33 kHz
ERROR_STEP
enum freqman_entry_step : int8_t {
AM_US, // 10 kHz AM/CB
AM_EUR, // 9 kHz LW/MW
NFM_1, // 12,5 kHz (Analogic PMR 446)
NFM_2, // 6,25 kHz (Digital PMR 446)
FM_1, // 100 kHz
FM_2, // 50 kHz
N_1, // 25 kHz
N_2, // 250 kHz
AIRBAND, // AIRBAND 8,33 kHz
};
struct freqman_entry {
rf::Frequency frequency_a{0}; // 'f=freq' or 'a=freq_start' or 'r=recv_freq'
rf::Frequency frequency_b{0}; // 'b=freq_end' or 't=tx_freq'
std::string description{}; // 'd=desc'
freqman_entry_type type{}; // SINGLE,RANGE,HAMRADIO
freqman_index_t modulation{}; // AM,NFM,WFM
freqman_index_t bandwidth{}; // AM_DSB, ...
freqman_index_t step{}; // 5khz (SA AM,...
tone_index tone{}; // 0XZ, 11 1ZB,...
rf::Frequency frequency_a{0}; // 'f=freq' or 'a=freq_start' or 'r=recv_freq'
rf::Frequency frequency_b{0}; // 'b=freq_end' or 't=tx_freq'
std::string description{NULL}; // 'd=desc'
freqman_entry_type type{SINGLE}; // SINGLE,RANGE,HAMRADIO
freqman_index_t modulation{AM_MODULATION}; // AM,NFM,WFM
freqman_index_t bandwidth{0}; // AM_DSB, ...
freqman_index_t step{0}; // 5khz (SA AM,...
tone_index tone{0}; // 0XZ, 11 1ZB,...
};
using freqman_db = std::vector<freqman_entry>;
bool load_freqman_file(std::string& file_stem, freqman_db& db);
bool load_freqman_file_ex(std::string& file_stem, freqman_db& db, bool load_freqs, bool load_ranges, bool load_hamradios, uint8_t limit);
bool load_freqman_file(std::string& file_stem, freqman_db& db, bool load_freqs = true, bool load_ranges = true, bool load_hamradios = true, uint8_t max_num_freqs = FREQMAN_MAX_PER_FILE);
bool get_freq_string(freqman_entry& entry, std::string& item_string);
bool delete_freqman_file(std::string& file_stem);
bool save_freqman_file(std::string& file_stem, freqman_db& db);
+40 -10
View File
@@ -30,7 +30,7 @@ bool Debounce::feed(const uint8_t bit) {
// "Repeat" handling - simulated button release
if (repeat_ctr_) {
// Make sure the button is still being held continuously
if (history_ == 0xFF) {
if ((history_ == 0xFF) && !long_press_enabled_) {
// Simulate button press every REPEAT_SUBSEQUENT_DELAY ticks
if (--repeat_ctr_ == 0) {
state_ = !state_;
@@ -49,29 +49,59 @@ bool Debounce::feed(const uint8_t bit) {
if ((history_ & DEBOUNCE_MASK) == DEBOUNCE_MASK) {
state_ = 1;
held_time_ = 0;
// If long_press_enabled_, state() function masks the button press until it's released
// or until LONG_PRESS_DELAY is reached
if (long_press_enabled_) {
pulse_upon_release_ = true;
return false;
}
return true;
}
} else {
// Previous button state was 1 (pressed);
// Has button been released for DEBOUNCE_COUNT ticks?
if ((history_ & DEBOUNCE_MASK) == 0) {
state_ = 0;
// Button has been released when long_press_enabled_ and before LONG_PRESS_DELAY was reached;
// allow state() function to finally return a single press indication
// (in long press mode, apps won't see button press until the button is released)
if (pulse_upon_release_) {
// leaving state_==1 for one cycle
pulse_upon_release_ = 0;
} else {
state_ = 0;
}
// Reset long_press_occurred_ flag after button is released
long_press_occurred_ = false;
return true;
}
// Repeat support is limited to the 4 directional buttons
if (repeat_enabled_) {
// Has button been held continuously for DEBOUNCE_REPEAT_DELAY?
if (history_ == 0xFF) {
if (++held_time_ == REPEAT_INITIAL_DELAY) {
// Has button been held continuously?
if (history_ == 0xFF) {
held_time_++;
if (pulse_upon_release_) {
// Button is being held down and long_press support is enabled for this key:
// if LONG_PRESS_DELAY is reached then finally report that switch is pressed and set flag
// indicating it was a LONG press
// (note that repease_support and long_press support are mutually exclusive)
if (held_time_ == LONG_PRESS_DELAY) {
long_press_occurred_ = true;
pulse_upon_release_ = 0;
held_time_ = 0;
return true;
}
} else if (repeat_enabled_ && !long_press_enabled_) {
// Repeat support -- 4 directional buttons only (unless long_press is enabled)
if (held_time_ == REPEAT_INITIAL_DELAY) {
// Delay reached; trigger repeat code on NEXT tick
repeat_ctr_ = 1;
held_time_ = 0;
}
} else {
// Button not continuously pressed; reset counter
held_time_ = 0;
}
} else {
// Button not continuously pressed; reset counter
held_time_ = 0;
}
}
return false;
+15 -1
View File
@@ -31,25 +31,39 @@
// # of timer0 ticks before a held button starts being counted as repeated presses
#define REPEAT_INITIAL_DELAY 250
#define REPEAT_SUBSEQUENT_DELAY 92
#define LONG_PRESS_DELAY 1000
class Debounce {
public:
bool feed(const uint8_t bit);
uint8_t state() const {
return state_;
return (pulse_upon_release_) ? 0 : state_;
}
void enable_repeat() {
repeat_enabled_ = true;
}
void set_long_press_support(bool v) {
long_press_enabled_ = v;
}
bool long_press_occurred() {
bool v = long_press_occurred_;
long_press_occurred_ = false;
return v;
}
private:
uint8_t history_{0};
uint8_t state_{0};
bool repeat_enabled_{0};
uint16_t repeat_ctr_{0};
uint16_t held_time_{0};
bool pulse_upon_release_{0};
bool long_press_enabled_{0};
bool long_press_occurred_{0};
};
#endif /*__DEBOUNCE_H__*/
+4 -4
View File
@@ -22,17 +22,17 @@
#include "io_file.hpp"
File::Result<File::Size> FileReader::read(void* const buffer, const File::Size bytes) {
auto read_result = file.read(buffer, bytes);
auto read_result = file_.read(buffer, bytes);
if (read_result.is_ok()) {
bytes_read += read_result.value();
bytes_read_ += read_result.value();
}
return read_result;
}
File::Result<File::Size> FileWriter::write(const void* const buffer, const File::Size bytes) {
auto write_result = file.write(buffer, bytes);
auto write_result = file_.write(buffer, bytes);
if (write_result.is_ok()) {
bytes_written += write_result.value();
bytes_written_ += write_result.value();
}
return write_result;
}
+8 -6
View File
@@ -38,14 +38,15 @@ class FileReader : public stream::Reader {
FileReader& operator=(FileReader&&) = delete;
Optional<File::Error> open(const std::filesystem::path& filename) {
return file.open(filename);
return file_.open(filename);
}
File::Result<File::Size> read(void* const buffer, const File::Size bytes) override;
const File& file() const& { return file_; }
protected:
File file{};
uint64_t bytes_read{0};
File file_{};
uint64_t bytes_read_{0};
};
class FileWriter : public stream::Writer {
@@ -58,14 +59,15 @@ class FileWriter : public stream::Writer {
FileWriter& operator=(FileWriter&&) = delete;
Optional<File::Error> create(const std::filesystem::path& filename) {
return file.create(filename);
return file_.create(filename);
}
File::Result<File::Size> write(const void* const buffer, const File::Size bytes) override;
const File& file() const& { return file_; }
protected:
File file{};
uint64_t bytes_written{0};
File file_{};
uint64_t bytes_written_{0};
};
using RawFileWriter = FileWriter;
+16 -15
View File
@@ -21,6 +21,7 @@
*/
#include "io_wave.hpp"
#include "utility.hpp"
bool WAVFileReader::open(const std::filesystem::path& path) {
size_t i = 0;
@@ -36,10 +37,10 @@ bool WAVFileReader::open(const std::filesystem::path& path) {
return true;
}
auto error = file.open(path);
auto error = file_.open(path);
if (!error.is_valid()) {
file.read((void*)&header, sizeof(header)); // Read header (RIFF and WAVE)
file_.read((void*)&header, sizeof(header)); // Read header (RIFF and WAVE)
riff_size = header.cksize + 8;
data_start = header.fmt.cksize + 28;
@@ -48,14 +49,14 @@ bool WAVFileReader::open(const std::filesystem::path& path) {
// Look for INAM (title) tag
if (data_end < riff_size) {
file.seek(data_end);
while (file.read((void*)&ch, 1).is_ok()) {
file_.seek(data_end);
while (file_.read((void*)&ch, 1).is_ok()) {
if (ch == tag_INAM[i++]) {
if (i == 4) {
// Tag found, copy title
file.read((void*)&title_size, sizeof(uint32_t));
file_.read((void*)&title_size, sizeof(uint32_t));
if (title_size > 32) title_size = 32;
file.read((void*)&title_buffer, title_size);
file_.read((void*)&title_buffer, title_size);
title_string = title_buffer;
break;
}
@@ -86,7 +87,7 @@ bool WAVFileReader::open(const std::filesystem::path& path) {
}
void WAVFileReader::rewind() {
file.seek(data_start);
file_.seek(data_start);
}
std::string WAVFileReader::title() {
@@ -94,15 +95,15 @@ std::string WAVFileReader::title() {
}
uint32_t WAVFileReader::ms_duration() {
return ((data_size_ * 1000) / sample_rate_) / bytes_per_sample;
return ::ms_duration(data_size_, sample_rate_, bytes_per_sample);
}
void WAVFileReader::data_seek(const uint64_t Offset) {
file.seek(data_start + (Offset * bytes_per_sample));
file_.seek(data_start + (Offset * bytes_per_sample));
}
/*int WAVFileReader::seek_mss(const uint16_t minutes, const uint8_t seconds, const uint32_t samples) {
const auto result = file.seek(data_start + ((((minutes * 60) + seconds) * sample_rate_) + samples) * bytes_per_sample);
const auto result = file_.seek(data_start + ((((minutes * 60) + seconds) * sample_rate_) + samples) * bytes_per_sample);
if (result.is_error())
return 0;
@@ -145,21 +146,21 @@ Optional<File::Error> WAVFileWriter::create(
}
Optional<File::Error> WAVFileWriter::update_header() {
header_t header{sampling_rate, (uint32_t)bytes_written - sizeof(header_t), info_chunk_size};
header_t header{sampling_rate, (uint32_t)bytes_written_ - sizeof(header_t), info_chunk_size};
const auto seek_0_result = file.seek(0);
const auto seek_0_result = file_.seek(0);
if (seek_0_result.is_error()) {
return seek_0_result.error();
}
const auto old_position = seek_0_result.value();
const auto write_result = file.write(&header, sizeof(header));
const auto write_result = file_.write(&header, sizeof(header));
if (write_result.is_error()) {
return write_result.error();
}
const auto seek_old_result = file.seek(old_position);
const auto seek_old_result = file_.seek(old_position);
if (seek_old_result.is_error()) {
return seek_old_result.error();
}
@@ -170,7 +171,7 @@ Optional<File::Error> WAVFileWriter::update_header() {
Optional<File::Error> WAVFileWriter::write_tags() {
tags_t tags{title};
const auto write_result = file.write(&tags, sizeof(tags));
const auto write_result = file_.write(&tags, sizeof(tags));
if (write_result.is_error()) {
return write_result.error();
}
+19 -2
View File
@@ -195,16 +195,33 @@ void controls_init() {
SwitchesState get_switches_state() {
SwitchesState result;
// Right, Left, Down, Up, & Select switches
for (size_t i = 0; i < result.size() - 1; i++) {
// TODO: Ignore multiple keys at the same time?
result[i] = switch_debounce[i].state();
}
result[result.size() - 1] = switch_debounce[switch_debounce.size() - 1].state();
// Grab Dfu switch from bit 7 and return in bit 5 so that all switches are grouped together in this 6-bit result
result[(size_t)Switch::Dfu] = switch_debounce[7].state();
return result;
}
// Configure which switches support long press (note those switches will not support Repeat function)
void switches_long_press_enable(SwitchesState switches_long_press_enabled) {
// Right, Left, Down, Up, & Select switches
for (size_t i = 0; i < switches_long_press_enabled.size() - 1; i++) {
switch_debounce[i].set_long_press_support(switches_long_press_enabled[i]);
}
// Dfu switch
switch_debounce[7].set_long_press_support(switches_long_press_enabled[(size_t)Switch::Dfu]);
}
bool switch_long_press_occurred(size_t v) {
return (v == (size_t)Switch::Dfu) ? switch_debounce[7].long_press_occurred() : switch_debounce[v].long_press_occurred();
}
EncoderPosition get_encoder_position() {
return encoder_position;
}
+3
View File
@@ -27,6 +27,7 @@
#include "touch.hpp"
// Must be same values as in ui::KeyEvent
enum class Switch {
Right = 0,
Left = 1,
@@ -44,6 +45,8 @@ void controls_init();
SwitchesState get_switches_state();
EncoderPosition get_encoder_position();
touch::Frame get_touch_frame();
void switches_long_press_enable(SwitchesState v);
bool switch_long_press_occurred(size_t v);
namespace control {
namespace debug {
+88
View File
@@ -0,0 +1,88 @@
/*
* Copyright (C) 2023 Kyle Reed
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "metadata_file.hpp"
#include "convert.hpp"
#include "file_reader.hpp"
#include "string_format.hpp"
#include <string_view>
namespace fs = std::filesystem;
using namespace std::literals;
const std::string_view center_freq_name = "center_frequency"sv;
const std::string_view sample_rate_name = "sample_rate"sv;
fs::path get_metadata_path(const fs::path& capture_path) {
auto temp = capture_path;
return temp.replace_extension(u".TXT");
}
Optional<File::Error> write_metadata_file(const fs::path& path, capture_metadata metadata) {
File f;
auto error = f.create(path);
if (error)
return error;
error = f.write_line(std::string{center_freq_name} + "=" +
to_string_dec_uint(metadata.center_frequency));
if (error)
return error;
error = f.write_line(std::string{sample_rate_name} + "=" +
to_string_dec_uint(metadata.sample_rate));
if (error)
return error;
return {};
}
Optional<capture_metadata> read_metadata_file(const fs::path& path) {
File f;
auto error = f.open(path);
if (error)
return {};
capture_metadata metadata{};
auto reader = FileLineReader(f);
for (const auto& line : reader) {
auto cols = split_string(line, '=');
if (cols.size() != 2)
continue; // Bad line.
if (cols[0] == center_freq_name)
parse_int(cols[1], metadata.center_frequency);
else if (cols[0] == sample_rate_name)
parse_int(cols[1], metadata.sample_rate);
else
continue;
}
if (metadata.center_frequency == 0 || metadata.sample_rate == 0)
return {}; // Parse failed.
return metadata;
}
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2023 Kyle Reed
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __METADATA_FILE_HPP__
#define __METADATA_FILE_HPP__
#include "file.hpp"
#include "optional.hpp"
#include "rf_path.hpp"
struct capture_metadata {
rf::Frequency center_frequency;
uint32_t sample_rate;
};
std::filesystem::path get_metadata_path(const std::filesystem::path& capture_path);
Optional<File::Error> write_metadata_file(const std::filesystem::path& path, capture_metadata metadata);
Optional<capture_metadata> read_metadata_file(const std::filesystem::path& path);
#endif // __METADATA_FILE_HPP__
-9
View File
@@ -96,15 +96,6 @@ bool get_antenna_bias() {
return antenna_bias;
}
bool speaker_mode{false};
void set_speaker_mode(const bool v) {
speaker_mode = v;
if (speaker_mode)
audio::output::speaker_unmute();
else
audio::output::speaker_mute();
}
static constexpr uint32_t systick_count(const uint32_t clock_source_f) {
return clock_source_f / CH_FREQUENCY;
}
-3
View File
@@ -53,9 +53,6 @@ extern ClockManager clock_manager;
extern ReceiverModel receiver_model;
extern TransmitterModel transmitter_model;
extern bool speaker_mode;
void set_speaker_mode(const bool v);
extern uint32_t bl_tick_counter;
extern bool antenna_bias;
+9 -8
View File
@@ -92,7 +92,8 @@ max2839::MAX2839 second_if_max2839{ssp1_target_max283x};
static max5864::MAX5864 baseband_codec{ssp1_target_max5864};
static baseband::CPLD baseband_cpld;
static rf::Direction direction{rf::Direction::Receive};
// Set invalid to force the set_direction CPLD workaround to run.
static rf::Direction direction{-1};
static bool baseband_invert = false;
static bool mixer_invert = false;
@@ -115,15 +116,15 @@ void set_direction(const rf::Direction new_direction) {
/* TODO: Refactor all the various "Direction" enumerations into one. */
/* TODO: Only make changes if direction changes, but beware of clock enabling. */
// Hack to fix the CPLD (clocking ?) bug: toggle CPLD SRAM overlay depending on new direction
// Hack to fix the CPLD (clocking ?) bug: toggle CPLD SRAM overlay depending on new direction.
// Use CPLD's EEPROM config when transmitting
// Use the SRAM overlay when receiving
// teixeluis: undone "Hack to fix the CPLD (clocking ?) bug".
// Apparently with current CPLD code from the hackrf repo,
// toggling CPLD overlay should no longer be necessary:
if (direction != new_direction && new_direction == rf::Direction::Transmit) {
hackrf::cpld::init_from_eeprom();
if (direction != new_direction) {
if (new_direction == rf::Direction::Transmit)
hackrf::cpld::init_from_eeprom();
else
// Prevents ghosting when switching back to RX from TX mode.
hackrf::cpld::load_sram_no_verify();
}
direction = new_direction;

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