Compare commits

...

35 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
76 changed files with 2141 additions and 896 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
+1 -1
View File
@@ -287,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 -16
View File
@@ -404,22 +404,9 @@ void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
}
void AnalogAudioView::handle_coded_squelch(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);
tone_index idx = tone_key_index_by_value(value);
if (idx >= 0)
text_ctcss.set("CTCSS " + tone_key_string(idx));
else
text_ctcss.set("???");
}
@@ -213,7 +213,7 @@ 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(ReceiverModel::Mode modulation);
+3 -35
View File
@@ -30,6 +30,7 @@ namespace ui {
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({
@@ -59,41 +60,8 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
/* 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 . */
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;
}
/* 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);
+3 -20
View File
@@ -31,23 +31,7 @@
#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 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)
#include "freqman.hpp"
namespace ui {
@@ -72,7 +56,6 @@ class CaptureAppView : public View {
app_settings::Options::UseGlobalTargetFrequency};
uint32_t sampling_rate = 0;
uint32_t anti_alias_baseband_bandwidth_filter = 2500000;
Labels labels{
{{0 * 8, 1 * 16}, "Rate:", Color::light_grey()},
@@ -103,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},
@@ -113,7 +96,7 @@ class CaptureAppView : public View {
16384,
3};
spectrum::WaterfallWidget waterfall{};
spectrum::WaterfallView waterfall{};
};
} /* namespace ui */
+1 -1
View File
@@ -124,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 -1
View File
@@ -121,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,
@@ -32,7 +32,7 @@ class SpectrumAnalysisModel {
namespace ui {
class SpectrumAnalysisView : public spectrum::WaterfallWidget {
class SpectrumAnalysisView : public spectrum::WaterfallView {
public:
private:
SpectrumAnalysisModel model;
+20 -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() {
+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__*/
+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_;
+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{
@@ -63,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;
+270 -90
View File
@@ -23,11 +23,12 @@
*/
#include "ui_playlist.hpp"
#include "convert.hpp"
#include "file_reader.hpp"
#include "io_file.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "utility.hpp"
#include "baseband_api.hpp"
@@ -39,13 +40,17 @@
using namespace portapack;
namespace fs = std::filesystem;
/* TODO: wouldn't it be easier if the playlist were just a list of C16 files
* (and maybe delays) and then read the metadata file next to the C16 file?
* TODO: use metadata_file.hpp to read the metadata.
* TODO: change PPL format to only allow paths, and !<number> for delay. */
/* TODO
* - Should frequency overrides be saved in the playlist?
* - Start playing from current track (vs start over)?
*/
namespace ui {
// 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(const fs::path& playlist_path) {
File playlist_file;
auto error = playlist_file.open(playlist_path.string());
@@ -59,57 +64,154 @@ void PlaylistView::load_file(const fs::path& playlist_path) {
continue; // Empty or comment line.
auto cols = split_string(line, ',');
if (cols.size() < 3)
continue; // Line doesn't have enough columns.
auto entry = load_entry(trim(cols[0]));
playlist_entry entry{};
if (!entry)
continue;
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.
// Read optional delay value.
if (cols.size() > 1)
parse_int(cols[1], entry->ms_delay);
entry.replay_file = fs::path{u"/"} + std::string{cols[1]};
if (cols.size() == 4) // Optional delay value.
parse_int(cols[3], entry.initial_delay);
playlist_db_.emplace_back(std::move(entry));
playlist_db_.emplace_back(*std::move(entry));
}
}
Optional<PlaylistView::playlist_entry> PlaylistView::load_entry(fs::path&& path) {
File capture_file;
auto error = capture_file.open(path);
if (error)
return {};
// 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();
reset();
current_index_ = 0;
playlist_path_ = new_file_path;
playlist_db_.clear();
load_file(playlist_path_);
update_ui();
button_play.focus();
// 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::reset() {
current_track_ = 0;
current_entry_ = nullptr;
current_entry_size_ = 0;
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;
}
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::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::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::delete_entry() {
if (playlist_db_.empty())
return;
// Ugh, the STL is gross.
playlist_db_.erase(playlist_db_.begin() + current_index_);
if (current_index_ > 0)
--current_index_;
playlist_dirty_ = true;
update_ui();
}
void PlaylistView::show_file_error(const fs::path& path, const std::string& message) {
nav_.display_modal("Error", "Error opening file \n" + path.string() + "\n" + message);
}
PlaylistView::playlist_entry* PlaylistView::current() {
return playlist_db_.empty() ? nullptr : &playlist_db_[current_index_];
}
bool PlaylistView::is_active() const {
return replay_thread_ != nullptr;
}
bool PlaylistView::loop() const {
return check_loop.value();
}
bool PlaylistView::is_done() const {
return current_track_ >= playlist_db_.size();
bool PlaylistView::at_end() const {
return current_index_ >= playlist_db_.size();
}
void PlaylistView::toggle() {
@@ -120,25 +222,24 @@ void PlaylistView::toggle() {
}
void PlaylistView::start() {
reset();
if (playlist_db_.empty())
return; // Nothing to do.
if (next_track())
send_current_track();
current_index_ = 0u;
send_current_track();
}
/* Advance to the next track in the playlist. */
bool PlaylistView::next_track() {
if (is_done()) {
if (loop())
current_track_ = 0;
else
return false; // All done.
++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.
}
current_entry_ = &playlist_db_[current_track_];
current_track_++;
return true;
}
@@ -149,30 +250,25 @@ void PlaylistView::send_current_track() {
transmitter_model.disable();
ready_signal_ = false;
if (!current_entry_)
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_entry_->replay_file);
auto error = reader->open(current()->path);
if (error) {
show_file_error(current_entry_->replay_file, "Can't open file to send.");
show_file_error(current()->path, "Can't open file to send.");
return;
}
// Get the sample file size for the progress bar.
current_entry_size_ = reader->file().size();
progressbar_transmit.set_value(0);
// Wait a bit before sending if specified.
// TODO: this pauses the main thread, move into ReplayThread instead.
if (current_entry_->initial_delay > 0)
chThdSleepMilliseconds(current_entry_->initial_delay);
// ReplayThread starts immediately on contruction so
// ReplayThread starts immediately on construction so
// these need to be set before creating the ReplayThread.
transmitter_model.set_target_frequency(current_entry_->replay_frequency);
transmitter_model.set_sampling_rate(current_entry_->sample_rate * 8);
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();
@@ -180,6 +276,9 @@ void PlaylistView::send_current_track() {
// 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),
@@ -199,37 +298,48 @@ 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_track.set("Open a playlist file.");
} else {
text_track.set(
to_string_dec_uint(current_track_) + "/" +
to_string_dec_uint(playlist_db_.size()) + " " +
playlist_path_.filename().string());
}
button_play.set_bitmap(is_active() ? &bitmap_stop : &bitmap_play);
progressbar_track.set_value(current_track_);
progressbar_track.set_max(playlist_db_.size());
progressbar_transmit.set_max(current_entry_size_);
if (current_entry_) {
auto duration = ms_duration(current_entry_size_, current_entry_->sample_rate, 4);
text_filename.set(truncate(current_entry_->replay_file.filename().string(), 12));
text_sample_rate.set(unit_auto_scale(current_entry_->sample_rate, 3, 0) + "Hz");
text_duration.set(to_string_time_ms(duration));
text_frequency.set(to_string_short_freq(current_entry_->replay_frequency));
} else {
text_filename.set("-");
text_sample_rate.set("-");
text_duration.set("-");
text_frequency.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) {
@@ -245,7 +355,7 @@ void PlaylistView::handle_replay_thread_done(uint32_t return_code) {
}
if (return_code == ReplayThread::READ_ERROR)
show_file_error(current_entry_->replay_file, "Replay read failed.");
show_file_error(current()->path, "Replay read failed.");
stop();
}
@@ -256,36 +366,103 @@ PlaylistView::PlaylistView(
baseband::run_image(portapack::spi_flash::image_tag_replay);
add_children({
&button_open,
&text_filename,
&text_sample_rate,
&text_duration,
&progressbar_track,
&progressbar_transmit,
&text_frequency,
&field_frequency,
&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(100'000'000);
field_frequency.on_change = [this](rf::Frequency f) {
if (current())
current()->metadata.center_frequency = 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);
};
};
button_play.on_select = [this](ImageButton&) {
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) {
on_file_changed(new_file_path);
button_add.on_select = [this, &nav]() {
if (is_active())
return;
auto open_view = nav_.push<FileLoadView>(".C16");
open_view->push_dir(u"CAPTURES");
open_view->on_changed = [this](fs::path path) {
add_entry(std::move(path));
};
};
button_delete.on_select = [this, &nav]() {
if (is_active())
return;
delete_entry();
};
button_open.on_select = [this, &nav]() {
if (is_active())
stop();
open_file();
};
button_save.on_select = [this]() {
if (is_active())
stop();
save_file();
};
button_prev.on_select = [this]() {
if (is_active())
return;
--current_index_;
if (at_end())
current_index_ = playlist_db_.size() - 1;
update_ui();
};
button_next.on_select = [this]() {
if (is_active())
return;
++current_index_;
if (at_end())
current_index_ = 0;
update_ui();
};
update_ui();
}
PlaylistView::PlaylistView(
NavigationView& nav,
const fs::path& path)
: PlaylistView(nav) {
auto ext = path.extension();
if (path_iequal(ext, ppl_ext))
on_file_changed(path);
else if (path_iequal(ext, c16_ext))
add_entry(fs::path{path});
}
PlaylistView::~PlaylistView() {
transmitter_model.disable();
baseband::shutdown();
@@ -294,20 +471,23 @@ PlaylistView::~PlaylistView() {
void PlaylistView::set_parent_rect(Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
const ui::Rect waterfall_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();
waterfall.on_hide();
View::on_hide();
}
void PlaylistView::focus() {
button_open.focus();
}
} /* namespace ui */
+77 -33
View File
@@ -25,13 +25,16 @@
#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>
@@ -42,17 +45,15 @@ namespace ui {
class PlaylistView : public View {
public:
PlaylistView(NavigationView& nav);
PlaylistView(NavigationView& nav, const std::filesystem::path& path);
~PlaylistView();
// Disable copy to make -Weffc++ happy.
PlaylistView(const PlaylistView&) = delete;
PlaylistView& operator=(const PlaylistView&) = delete;
// Following 2 called by 'NavigationView::update_view' after view is created.
void set_parent_rect(Rect new_parent_rect) override;
void on_hide() override;
void focus() override;
void on_hide() override;
std::string title() const override { return "Playlist"; };
std::string title() const override { return "Replay"; };
private:
NavigationView& nav_;
@@ -61,35 +62,40 @@ class PlaylistView : public View {
"tx_playlist", app_settings::Mode::TX};
// More header == less spectrum view.
static constexpr ui::Dim header_height = 4 * 16;
static constexpr ui::Dim header_height = 6 * 16;
static constexpr uint32_t baseband_bandwidth = 2500000;
struct playlist_entry {
rf::Frequency replay_frequency{0};
std::filesystem::path replay_file{};
uint32_t sample_rate{};
uint32_t initial_delay{};
std::filesystem::path path{};
capture_metadata metadata{};
File::Size file_size{};
uint32_t ms_delay{};
};
std::unique_ptr<ReplayThread> replay_thread_{};
bool ready_signal_{}; // Used to signal the ReplayThread.
size_t current_track_{0};
const playlist_entry* current_entry_{};
size_t current_entry_size_{0};
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 loop() const;
bool is_done() const;
bool at_end() const;
void toggle();
void start();
@@ -103,31 +109,33 @@ class PlaylistView : public View {
void on_tx_progress(uint32_t progress);
void handle_replay_thread_done(uint32_t return_code);
Button button_open{
{0 * 8, 0 * 16, 10 * 8, 2 * 16},
"Open PPL"};
Text text_filename{
{11 * 8, 0 * 16, 12 * 8, 16}};
{0 * 8, 0 * 16, 30 * 8, 16}};
FrequencyField field_frequency{
{0 * 8, 1 * 16}};
Text text_sample_rate{
{24 * 8, 0 * 16, 6 * 8, 16}};
{10 * 8, 1 * 16, 7 * 8, 16}};
Text text_duration{
{11 * 8, 1 * 16, 6 * 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}};
{18 * 8, 1 * 16, 12 * 8, 8 + 1}};
ProgressBar progressbar_transmit{
{18 * 8, 3 * 8, 12 * 8, 8}};
{18 * 8, 3 * 8 - 1, 12 * 8, 8}};
Text text_frequency{
{0 * 8, 2 * 16, 9 * 8, 16}};
Text text_duration{
{0 * 8, 2 * 16, 5 * 8, 16}};
// TODO: delay duration field.
TransmitterView2 tx_view{
74, 1 * 8, SHORT_UI // x(columns), y (line) position.
};
9 * 8, 1 * 8, SHORT_UI};
Checkbox check_loop{
{21 * 8, 2 * 16},
@@ -144,7 +152,43 @@ class PlaylistView : public View {
Text text_track{
{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,
+140 -55
View File
@@ -36,8 +36,19 @@ void ReconView::set_loop_config(bool v) {
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
if (!manual_mode) {
// clear and shrink_to_fit are not enough to really start with a new, clean, empty vector
@@ -232,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);
@@ -246,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.
}
@@ -279,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()) {
@@ -354,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,
@@ -399,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
@@ -424,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();
@@ -680,7 +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);
@@ -760,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]
@@ -795,8 +820,11 @@ 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) {
input_file = result[0];
@@ -811,6 +839,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();
frequency_file_load(false);
freqlist_cleared_for_ui_action = false;
@@ -830,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();
};
@@ -888,7 +920,8 @@ 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
std::string file_input = input_file; // default recon mode
@@ -896,29 +929,27 @@ void ReconView::frequency_file_load(bool stop_all_before) {
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(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() > FREQMAN_MAX_PER_FILE) {
file_name.set_style(&Styles::yellow);
desc_cycle.set_style(&Styles::yellow);
}
}
}
@@ -992,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) {
@@ -1018,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
@@ -1040,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;
@@ -1060,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;
}
@@ -1072,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 */
@@ -1172,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
@@ -1224,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
@@ -1236,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>");
@@ -1277,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);
@@ -1291,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);
@@ -1300,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);
@@ -1310,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();
@@ -1329,32 +1426,20 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
}
void ReconView::handle_coded_squelch(const uint32_t value) {
float diff{0.0};
float min_diff{(float)value};
size_t min_idx{0};
size_t c{0};
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 */
+12 -7
View File
@@ -84,6 +84,8 @@ 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};
@@ -108,6 +110,8 @@ class ReconView : public View {
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};
@@ -118,10 +122,7 @@ class ReconView : public View {
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};
uint32_t freq_lock{0};
@@ -138,16 +139,20 @@ class ReconView : public View {
int8_t last_rssi_med{-127};
int8_t last_rssi_max{-127};
int32_t last_index{-1};
int32_t last_squelch_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}, "NBLCKS:x W,L: , ", Color::light_grey()},
{{0 * 8, (26 * 8) + 4}, "MODE: , SQUELCH: ", Color::light_grey()}};
{{0 * 8, (26 * 8) + 4}, "MODE: , SQUELCH: ", Color::light_grey()}};
LNAGainField field_lna{
{4 * 8, 0 * 16}};
@@ -272,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,
{}};
@@ -103,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() {
@@ -117,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() {
@@ -132,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 {
+10
View File
@@ -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);
+133 -100
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,6 +242,8 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() {
};
}
/* SetUIView *********************************************/
SetUIView::SetUIView(NavigationView& nav) {
add_children({&checkbox_disable_touchscreen,
&checkbox_bloff,
@@ -241,39 +252,76 @@ SetUIView::SetUIView(NavigationView& nav) {
&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_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);
}
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());
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();
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
@@ -285,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&) {
@@ -311,9 +358,8 @@ void SetAppSettingsView::focus() {
button_save.focus();
}
// ---------------------------------------------------------
// Converter Settings
// ---------------------------------------------------------
/* SetConverterSettingsView ******************************/
SetConverterSettingsView::SetConverterSettingsView(NavigationView& nav) {
add_children({&check_show_converter,
&check_converter,
@@ -321,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>");
@@ -376,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,
@@ -387,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>");
@@ -432,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,
@@ -445,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;
@@ -472,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");
@@ -480,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();
}
};
@@ -497,7 +537,7 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
YESNO,
[this](bool choice) {
if (choice) {
portapack::persistent_memory::cache::defaults();
pmem::cache::defaults();
}
});
};
@@ -511,22 +551,18 @@ void SetPersistentMemoryView::focus() {
button_return.focus();
}
// ---------------------------------------------------------
// Audio Settings
// ---------------------------------------------------------
/* SetAudioView ******************************************/
SetAudioView::SetAudioView(NavigationView& nav) {
add_children({&labels,
&field_tone_mix,
&checkbox_speaker_disable,
&button_save,
&button_cancel});
field_tone_mix.set_value(persistent_memory::tone_mix());
checkbox_speaker_disable.set_value(persistent_memory::config_speaker_disable());
field_tone_mix.set_value(pmem::tone_mix());
button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_tone_mix(field_tone_mix.value());
persistent_memory::set_config_speaker_disable(checkbox_speaker_disable.value());
pmem::set_tone_mix(field_tone_mix.value());
audio::output::update_audio_mute();
nav.pop();
};
@@ -540,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();
};
@@ -564,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();
};
@@ -589,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,16 +209,16 @@ class SetUIView : public View {
private:
Checkbox checkbox_disable_touchscreen{
{3 * 8, 2 * 16},
{3 * 8, 1 * 16},
20,
"Disable touchscreen"};
Checkbox checkbox_bloff{
{3 * 8, 4 * 16},
{3 * 8, 3 * 16},
20,
"Backlight off after:"};
OptionsField options_bloff{
{60, 5 * 16 + 8},
{60, 4 * 16 + 8},
20,
{
{"5 seconds", backlight_timeout_t::Timeout5Sec},
@@ -231,25 +232,65 @@ class SetUIView : public View {
}};
Checkbox checkbox_showsplash{
{3 * 8, 7 * 16},
{3 * 8, 6 * 16},
20,
"Show splash"};
Checkbox checkbox_showclock{
{3 * 8, 9 * 16},
{3 * 8, 8 * 16},
20,
"Show clock with:"};
OptionsField options_clockformat{
{60, 10 * 16 + 8},
{60, 9 * 16 + 8},
20,
{{"time only", 0},
{"time and date", 1}}};
Checkbox checkbox_guireturnflag{
{3 * 8, 12 * 16},
25,
"Show 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},
@@ -388,11 +429,6 @@ class SetAudioView : public View {
1,
'0'};
Checkbox checkbox_speaker_disable{
{2 * 8, 6 * 16},
25,
"Disable AK speaker amp"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -57,6 +57,8 @@ SpectrumPainterView::SpectrumPainterView(
input_image.set_parent_rect(view_rect);
input_text.set_parent_rect(view_rect);
freqman_set_bandwidth_option(SPEC_MODULATION, option_bandwidth);
field_frequency.set_step(5000);
tx_gain = transmitter_model.tx_gain();
@@ -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},
+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__
+7
View File
@@ -184,6 +184,9 @@ void speaker_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();
@@ -269,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 */
+2
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;
@@ -107,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");
+21 -1
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 = {
+2 -1
View File
@@ -59,10 +59,11 @@ enum freqman_entry_type : int8_t {
NOTYPE // undetected
};
enum freqman_entry_modulation : int8_t {
enum freqman_entry_modulation : uint8_t {
AM_MODULATION = 0,
NFM_MODULATION,
WFM_MODULATION,
SPEC_MODULATION,
};
// Entry step placed for AlainD freqman version (or any other enhanced version)
+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__*/
+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 {
+2 -2
View File
@@ -286,7 +286,7 @@ static const char* whitespace_str = " \t\r\n";
std::string trim(std::string_view str) {
auto first = str.find_first_not_of(whitespace_str);
auto last = str.find_last_not_of(whitespace_str);
return std::string{str.substr(first, last - first)};
return std::string{str.substr(first, last - first + 1)};
}
std::string trimr(std::string_view str) {
@@ -296,4 +296,4 @@ std::string trimr(std::string_view str) {
std::string truncate(std::string_view str, size_t length) {
return std::string{str.length() <= length ? str : str.substr(0, length)};
}
}
+33 -8
View File
@@ -73,9 +73,9 @@ const tone_key_t tone_keys = {
{"34 M3", 218.100},
{"35 M4", 225.700},
{"49 9Z", 229.100},
{"36 --", 233.600},
{"37 --", 241.800},
{"38 --", 250.300},
{"36 M5", 233.600},
{"37 M6", 241.800},
{"38 M7", 250.300},
{"50 0Z", 254.100},
{"Axient 28kHz", 28000.0},
{"Senn. 32.768k", 32768.0},
@@ -113,6 +113,36 @@ std::string tone_key_string(tone_index index) {
return tone_keys[index].first;
}
std::string tone_key_string_by_value(uint32_t value) {
return tone_key_string(tone_key_index_by_value(value));
}
tone_index tone_key_index_by_value(uint32_t value) {
float diff;
float min_diff{(float)value};
float fvalue{(float)((min_diff + 50.0) / 100.0)};
tone_index min_idx{-1};
tone_index idx;
// Find nearest match
for (idx = 0; idx < (tone_index)tone_keys.size(); idx++) {
diff = abs(fvalue - tone_keys[idx].second);
if (diff < min_diff) {
min_idx = idx;
min_diff = diff;
} else {
// list is sorted in frequency order; if diff is getting larger than we've passed it
break;
}
}
// Arbitrary confidence threshold
if (min_diff < 40.0)
return min_idx;
else
return -1;
}
tone_index tone_key_index_by_string(char* str) {
if (!str)
return -1;
@@ -123,9 +153,4 @@ tone_index tone_key_index_by_string(char* str) {
return -1;
}
/* tone_index tone_key_index_by_value( int32_t freq )
{
return -1 ;
} */
} // namespace tonekey
+3 -3
View File
@@ -30,7 +30,7 @@ using namespace ui;
namespace tonekey {
typedef int16_t tone_index;
typedef int32_t tone_index;
using tone_key_t = std::vector<std::pair<std::string, float>>;
@@ -40,8 +40,8 @@ void tone_keys_populate(OptionsField& field);
float tone_key_frequency(const tone_index index);
std::string tone_key_string(const tone_index index);
tone_index tone_key_index_by_string(char* str);
// tone_index tone_key_index_by_value( int32_t freq );
std::string tone_key_string_by_value(uint32_t value);
tone_index tone_key_index_by_value(uint32_t value);
} // namespace tonekey
+17 -8
View File
@@ -37,9 +37,13 @@ AlphanumView::AlphanumView(
: TextEntryView(nav, str, max_length) {
size_t n;
add_children({&button_mode,
&text_raw,
&field_raw});
add_children({
&labels,
&field_raw,
&text_raw_to_char,
&button_delete,
&button_mode,
});
const auto button_fn = [this](Button& button) {
this->on_button(button);
@@ -65,10 +69,19 @@ AlphanumView::AlphanumView(
set_mode(mode + 1);
};
button_delete.on_select = [this](Button&) {
char_delete();
};
field_raw.set_value('0');
field_raw.on_select = [this](NumberField&) {
char_add(field_raw.value());
};
// make text_raw_to_char widget display the char value from field_raw
field_raw.on_change = [this](auto) {
text_raw_to_char.set(std::string{static_cast<char>(field_raw.value())});
};
}
void AlphanumView::set_mode(const uint32_t new_mode) {
@@ -96,11 +109,7 @@ void AlphanumView::set_mode(const uint32_t new_mode) {
void AlphanumView::on_button(Button& button) {
const auto c = button.text()[0];
if (c == '<')
char_delete();
else
char_add(c);
char_add(c);
}
bool AlphanumView::on_encoder(const EncoderEvent delta) {
+20 -15
View File
@@ -43,14 +43,14 @@ class AlphanumView : public TextEntryView {
bool on_encoder(const EncoderEvent delta) override;
private:
const char* const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ, .<";
const char* const keys_lower = "abcdefghijklmnopqrstuvwxyz, .<";
const char* const keys_digit = "0123456789!\"#'()*+-/:;=>?@[\\]<";
const char* const keys_upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ, ._";
const char* const keys_lower = "abcdefghijklmnopqrstuvwxyz, ._";
const char* const keys_digit = "0123456789!\"#'()*+-/:;=<>@[\\]?";
const std::pair<std::string, const char*> key_sets[3] = {
{"Upper", keys_upper},
{"Lower", keys_lower},
{"Digit", keys_digit}};
{"ABC", keys_upper},
{"abc", keys_lower},
{"123", keys_digit}};
int16_t focused_button = 0;
uint32_t mode = 0; // Uppercase
@@ -60,13 +60,10 @@ class AlphanumView : public TextEntryView {
std::array<Button, 30> buttons{};
Button button_mode{
{21 * 8, 33 * 8, 8 * 8, 32},
""};
Labels labels{
{{1 * 8, 33 * 8}, "Raw:", Color::light_grey()},
{{1 * 8, 35 * 8}, "AKA:", Color::light_grey()}};
Text text_raw{
{1 * 8, 33 * 8, 4 * 8, 16},
"Raw:"};
NumberField field_raw{
{5 * 8, 33 * 8},
3,
@@ -74,9 +71,17 @@ class AlphanumView : public TextEntryView {
1,
'0'};
Button button_ok{
{10 * 8, 33 * 8, 9 * 8, 32},
"OK"};
Text text_raw_to_char{
{5 * 8, 35 * 8, 4 * 8, 16},
"0"};
Button button_delete{
{9 * 8, 33 * 8, 6 * 8, 32},
"<DEL"};
Button button_mode{
{16 * 8, 33 * 8, 5 * 8, 32},
""};
};
} /* namespace ui */
+53 -30
View File
@@ -36,7 +36,7 @@ using namespace portapack;
namespace ui {
namespace spectrum {
/* AudioSpectrumView******************************************************/
/* AudioSpectrumView ******************************************************/
AudioSpectrumView::AudioSpectrumView(
const Rect parent_rect)
@@ -247,28 +247,25 @@ void FrequencyScale::on_tick_second() {
_blink = !_blink;
}
/* WaterfallView *********************************************************/
/* WaterfallWidget *********************************************************/
// TODO: buffer and use "paint" instead of immediate drawing would help with
// preventing flicker from drawing. Would use more RAM however.
void WaterfallView::on_show() {
void WaterfallWidget::on_show() {
clear();
const auto screen_r = screen_rect();
display.scroll_set_area(screen_r.top(), screen_r.bottom());
}
void WaterfallView::on_hide() {
void WaterfallWidget::on_hide() {
/* TODO: Clear region to eliminate brief flash of content at un-shifted
* position?
*/
display.scroll_disable();
}
void WaterfallView::paint(Painter& painter) {
// Do nothing.
(void)painter;
}
void WaterfallView::on_channel_spectrum(
void WaterfallWidget::on_channel_spectrum(
const ChannelSpectrum& spectrum) {
/* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */
@@ -290,16 +287,16 @@ void WaterfallView::on_channel_spectrum(
pixel_row);
}
void WaterfallView::clear() {
void WaterfallWidget::clear() {
display.fill_rectangle(
screen_rect(),
Color::black());
}
/* WaterfallWidget *******************************************************/
/* WaterfallView *******************************************************/
WaterfallWidget::WaterfallWidget(const bool cursor) {
add_children({&waterfall_view,
WaterfallView::WaterfallView(const bool cursor) {
add_children({&waterfall_widget,
&frequency_scale});
frequency_scale.set_focusable(cursor);
@@ -310,17 +307,17 @@ WaterfallWidget::WaterfallWidget(const bool cursor) {
};
}
void WaterfallWidget::on_show() {
void WaterfallView::on_show() {
// TODO: Assert that baseband is not shutdown.
baseband::spectrum_streaming_start();
}
void WaterfallWidget::on_hide() {
void WaterfallView::on_hide() {
// TODO: Assert that baseband is not shutdown.
baseband::spectrum_streaming_stop();
}
void WaterfallWidget::show_audio_spectrum_view(const bool show) {
void WaterfallView::show_audio_spectrum_view(const bool show) {
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
if (show) {
@@ -335,18 +332,18 @@ void WaterfallWidget::show_audio_spectrum_view(const bool show) {
}
}
void WaterfallWidget::update_widgets_rect() {
void WaterfallView::update_widgets_rect() {
if (audio_spectrum_view) {
frequency_scale.set_parent_rect({0, audio_spectrum_height, screen_rect().width(), scale_height});
waterfall_view.set_parent_rect(waterfall_reduced_rect);
waterfall_widget.set_parent_rect(waterfall_reduced_rect);
} else {
frequency_scale.set_parent_rect({0, 0, screen_rect().width(), scale_height});
waterfall_view.set_parent_rect(waterfall_normal_rect);
waterfall_widget.set_parent_rect(waterfall_normal_rect);
}
waterfall_view.on_show();
waterfall_widget.on_show();
}
void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) {
void WaterfallView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
waterfall_normal_rect = {0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
@@ -355,13 +352,8 @@ void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) {
update_widgets_rect();
}
void WaterfallWidget::paint(Painter& painter) {
// TODO:
(void)painter;
}
void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
waterfall_view.on_channel_spectrum(spectrum);
void WaterfallView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
waterfall_widget.on_channel_spectrum(spectrum);
sampling_rate = spectrum.sampling_rate;
frequency_scale.set_spectrum_sampling_rate(sampling_rate);
frequency_scale.set_channel_filter(
@@ -370,9 +362,40 @@ void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
spectrum.channel_filter_transition);
}
void WaterfallWidget::on_audio_spectrum() {
void WaterfallView::on_audio_spectrum() {
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
}
} /* namespace spectrum */
uint32_t filter_bandwidth_for_sampling_rate(int32_t sampling_rate) {
switch (sampling_rate) { // 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.
return 1750000; // Minimum BPF MAX2837 for all those lower BW options.
case 4000000 ... 6000000: // BW capture range (500k...750kHz max) fs_max = 8 x 750kHz = 6Mhz
// BW 500k...750kHz, ex. 500kHz (fs = 8 x BW = 4Mhz), BW 600kHz (fs = 4,8Mhz), BW 750 kHz (fs = 6Mhz).
return 2500000; // In some IC, MAX2837 appears as 2250000, but both work similarly.
case 8800000: // BW capture 1,1Mhz fs = 8 x 1,1Mhz = 8,8Mhz. (1Mhz showed slightly higher noise background).
return 3500000;
case 14000000: // BW capture 1,75Mhz, fs = 8 x 1,75Mhz = 14Mhz
// Good BPF, good matching, but LCD flickers, refresh rate should be < 20 Hz, reasonable picture.
return 5000000;
case 16000000: // BW capture 2Mhz, fs = 8 x 2Mhz = 16Mhz
// Good BPF, good matching, but LCD flickers, refresh rate should be < 20 Hz, reasonable picture.
return 6000000;
case 20000000: // BW capture 2,5Mhz, fs = 8 x 2,5 Mhz = 20Mhz
// Good BPF, good matching, but LCD flickers, refresh rate should be < 20 Hz, reasonable picture.
return 7000000;
default: // BW capture 2,75Mhz, fs = 8 x 2,75Mhz = 22Mhz max ADC sampling and others.
// We tested also 9Mhz FPB slightly too much noise floor, better at 8Mhz.
return 8000000;
}
}
} /* namespace ui */
+13 -12
View File
@@ -108,12 +108,11 @@ class FrequencyScale : public Widget {
* If the baseband is shutdown or otherwise not running when interacting
* with these, they will almost certainly hang the device. */
class WaterfallView : public Widget {
class WaterfallWidget : public Widget {
public:
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
void paint(Painter&) override {}
void on_channel_spectrum(const ChannelSpectrum& spectrum);
@@ -121,16 +120,16 @@ class WaterfallView : public Widget {
void clear();
};
class WaterfallWidget : public View {
class WaterfallView : public View {
public:
std::function<void(int32_t offset)> on_select{};
WaterfallWidget(const bool cursor = false);
WaterfallView(const bool cursor = false);
WaterfallWidget(const WaterfallWidget&) = delete;
WaterfallWidget(WaterfallWidget&&) = delete;
WaterfallWidget& operator=(const WaterfallWidget&) = delete;
WaterfallWidget& operator=(WaterfallWidget&&) = delete;
WaterfallView(const WaterfallView&) = delete;
WaterfallView(WaterfallView&&) = delete;
WaterfallView& operator=(const WaterfallView&) = delete;
WaterfallView& operator=(WaterfallView&&) = delete;
void on_show() override;
void on_hide() override;
@@ -139,8 +138,6 @@ class WaterfallWidget : public View {
void show_audio_spectrum_view(const bool show);
void paint(Painter& painter) override;
private:
void update_widgets_rect();
@@ -148,7 +145,7 @@ class WaterfallWidget : public View {
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
static constexpr Dim scale_height = 20;
WaterfallView waterfall_view{};
WaterfallWidget waterfall_widget{};
FrequencyScale frequency_scale{};
ChannelSpectrumFIFO* channel_fifo{nullptr};
@@ -195,6 +192,10 @@ class WaterfallWidget : public View {
};
} /* namespace spectrum */
/* Calculates the best anti_alias_baseband_bandwidth_filter for the given sampling rate. */
uint32_t filter_bandwidth_for_sampling_rate(int32_t sampling_rate);
} /* namespace ui */
#endif /*__UI_SPECTRUM_H__*/
+1 -1
View File
@@ -50,7 +50,7 @@ class TextEntryView : public View {
TextField text_input;
Button button_ok{
{10 * 8, 33 * 8, 9 * 8, 32},
{22 * 8, 33 * 8, 7 * 8, 32},
"OK"};
};
+223 -198
View File
@@ -98,9 +98,51 @@
using portapack::receiver_model;
using portapack::transmitter_model;
namespace pmem = portapack::persistent_memory;
namespace ui {
/* StatusTray ************************************************************/
StatusTray::StatusTray(Point pos)
: View{{pos, {0, height}}},
pos_(pos) {
set_focusable(false);
}
void StatusTray::add(Widget* child) {
width_ += child->parent_rect().width();
add_child(child);
}
void StatusTray::update_layout() {
// Widen the tray's parent rect.
auto rect = parent_rect();
set_parent_rect({{rect.left() - width_, rect.top()}, {rect.right() + width_, height}});
// Update the children.
auto x = 0;
for (auto child : children()) {
auto size = child->parent_rect().size();
child->set_parent_rect({{x, 0}, size});
x += size.width();
}
set_dirty();
}
void StatusTray::clear() {
// More efficient than 'remove_children'.
for (auto child : children())
child->set_parent(nullptr);
children_.clear();
width_ = 0;
set_parent_rect({pos_, {width_, height}});
set_dirty();
}
void StatusTray::paint(Painter&) {
}
/* SystemStatusView ******************************************************/
SystemStatusView::SystemStatusView(
@@ -111,48 +153,19 @@ SystemStatusView::SystemStatusView(
&button_back,
&title,
&button_title,
&button_converter,
&button_speaker,
&button_stealth,
//&button_textentry,
&button_camera,
&button_sleep,
&button_bias_tee,
&button_clock_status,
&sd_card_status_view,
&status_icons,
});
if (portapack::persistent_memory::should_use_sdcard_for_pmem()) {
portapack::persistent_memory::load_persistent_settings_from_file();
}
if (portapack::persistent_memory::config_hide_converter()) {
button_converter.hidden(true);
} else {
button_converter.hidden(false);
if (portapack::persistent_memory::config_converter()) {
button_converter.set_foreground(Color::red());
} else {
button_converter.set_foreground(Color::light_grey());
}
if (pmem::should_use_sdcard_for_pmem()) {
pmem::load_persistent_settings_from_file();
}
button_back.id = -1; // Special ID used by FocusManager
title.set_style(&Styles::bg_dark_grey);
if (portapack::persistent_memory::stealth_mode())
button_stealth.set_foreground(ui::Color::green());
/*if (!portapack::persistent_memory::ui_config_textentry())
button_textentry.set_bitmap(&bitmap_icon_keyboard);
else
button_textentry.set_bitmap(&bitmap_icon_unistroke);*/
refresh();
button_back.on_select = [this](ImageButton&) {
if (portapack::persistent_memory::should_use_sdcard_for_pmem()) {
portapack::persistent_memory::save_persistent_settings_to_file();
if (pmem::should_use_sdcard_for_pmem()) {
pmem::save_persistent_settings_to_file();
}
if (this->on_back)
this->on_back();
@@ -170,6 +183,10 @@ SystemStatusView::SystemStatusView(
this->on_speaker();
};
button_mute.on_select = [this](ImageButton&) {
this->on_mute();
};
button_stealth.on_select = [this](ImageButton&) {
this->on_stealth();
};
@@ -178,10 +195,6 @@ SystemStatusView::SystemStatusView(
this->on_bias_tee();
};
/*button_textentry.on_select = [this](ImageButton&) {
this->on_textentry();
};*/
button_camera.on_select = [this](ImageButton&) {
this->on_camera();
};
@@ -196,26 +209,31 @@ SystemStatusView::SystemStatusView(
};
audio::output::update_audio_mute();
refresh();
}
void SystemStatusView::refresh() {
if (portapack::persistent_memory::config_hide_converter()) {
button_converter.hidden(true);
} else {
if (portapack::persistent_memory::config_updown_converter()) {
button_converter.set_bitmap(&bitmap_icon_downconvert);
} else {
button_converter.set_bitmap(&bitmap_icon_upconvert);
}
button_converter.hidden(false);
if (portapack::persistent_memory::config_converter()) {
button_converter.set_foreground(Color::red());
} else {
button_converter.set_foreground(Color::light_grey());
}
}
// NB: Order of insertion is the display order Left->Right.
// TODO: Might be better to support hide and only add once.
status_icons.clear();
if (!pmem::ui_hide_camera()) status_icons.add(&button_camera);
if (!pmem::ui_hide_sleep()) status_icons.add(&button_sleep);
if (!pmem::ui_hide_stealth()) status_icons.add(&button_stealth);
if (!pmem::ui_hide_converter()) status_icons.add(&button_converter);
if (!pmem::ui_hide_bias_tee()) status_icons.add(&button_bias_tee);
if (!pmem::ui_hide_clock()) status_icons.add(&button_clock_status);
if (!pmem::ui_hide_mute()) status_icons.add(&button_mute);
if (portapack::persistent_memory::config_audio_mute()) {
// Display "Disable speaker" icon only if AK4951 Codec which has separate speaker/headphone control
if (audio::speaker_disable_supported() && !pmem::ui_hide_speaker())
status_icons.add(&button_speaker);
if (!pmem::ui_hide_sd_card()) status_icons.add(&sd_card_status_view);
status_icons.update_layout();
// Update icon display (try to keep all in on place).
// Speaker Enable/Disable (AK4951 boards only)
if (pmem::config_speaker_disable()) {
button_speaker.set_foreground(Color::light_grey());
button_speaker.set_bitmap(&bitmap_icon_speaker_mute);
} else {
@@ -223,25 +241,38 @@ void SystemStatusView::refresh() {
button_speaker.set_bitmap(&bitmap_icon_speaker);
}
// Audio Mute (both headphones & speaker)
if (pmem::config_audio_mute()) {
button_mute.set_foreground(Color::light_grey());
button_mute.set_bitmap(&bitmap_icon_speaker_and_headphones_mute);
} else {
button_mute.set_foreground(Color::green());
button_mute.set_bitmap(&bitmap_icon_speaker_and_headphones);
}
// Clock status
bool external_clk = portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::External;
button_clock_status.set_bitmap(external_clk ? &bitmap_icon_clk_ext : &bitmap_icon_clk_int);
button_clock_status.set_foreground(
pmem::clkout_enabled() ? Color::green() : Color::light_grey());
// Antenna DC Bias
if (portapack::get_antenna_bias()) {
button_bias_tee.set_bitmap(&bitmap_icon_biast_on);
button_bias_tee.set_foreground(ui::Color::yellow());
button_bias_tee.set_foreground(Color::yellow());
} else {
button_bias_tee.set_bitmap(&bitmap_icon_biast_off);
button_bias_tee.set_foreground(ui::Color::light_grey());
button_bias_tee.set_foreground(Color::light_grey());
}
if (portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::External) {
button_clock_status.set_bitmap(&bitmap_icon_clk_ext);
} else {
button_clock_status.set_bitmap(&bitmap_icon_clk_int);
}
// Converter
button_converter.set_bitmap(
pmem::config_updown_converter() ? &bitmap_icon_downconvert : &bitmap_icon_upconvert);
button_converter.set_foreground(pmem::config_converter() ? Color::red() : Color::light_grey());
if (portapack::persistent_memory::clkout_enabled()) {
button_clock_status.set_foreground(ui::Color::green());
} else {
button_clock_status.set_foreground(ui::Color::light_grey());
}
// Stealth
button_stealth.set_foreground(
pmem::stealth_mode() ? Color::green() : Color::light_grey());
set_dirty();
}
@@ -266,49 +297,53 @@ void SystemStatusView::set_title(const std::string new_value) {
if (new_value.empty()) {
title.set(default_title);
} else {
title.set(new_value);
// Limit length of title string to prevent partial characters if too many StatusView icons
size_t max_len = (status_icons.parent_rect().left() - title.parent_rect().left()) / 8;
title.set(truncate(new_value, max_len));
}
}
void SystemStatusView::on_converter() {
if (!portapack::persistent_memory::config_converter()) {
portapack::persistent_memory::set_config_converter(true);
button_converter.set_foreground(Color::red());
} else {
portapack::persistent_memory::set_config_converter(false);
button_converter.set_foreground(Color::light_grey());
}
pmem::set_config_converter(!pmem::config_converter());
// Poke to update tuning
// NOTE: Code assumes here that a TX app isn't active, since RX & TX have diff tuning offsets
// (and there's only one tuner in the radio so can't update tuner for both).
// TODO: Maybe expose the 'enabled_' flag on models.
receiver_model.set_target_frequency(receiver_model.target_frequency());
refresh();
}
void SystemStatusView::on_speaker() {
portapack::persistent_memory::set_config_audio_mute(!portapack::persistent_memory::config_audio_mute());
pmem::set_config_speaker_disable(!pmem::config_speaker_disable());
audio::output::update_audio_mute();
refresh();
}
void SystemStatusView::on_mute() {
pmem::set_config_audio_mute(!pmem::config_audio_mute());
audio::output::update_audio_mute();
refresh();
}
void SystemStatusView::on_stealth() {
bool mode = not portapack::persistent_memory::stealth_mode();
portapack::persistent_memory::set_stealth_mode(mode);
button_stealth.set_foreground(mode ? Color::green() : Color::light_grey());
pmem::set_stealth_mode(!pmem::stealth_mode());
refresh();
}
void SystemStatusView::on_bias_tee() {
if (!portapack::get_antenna_bias()) {
nav_.display_modal("Bias voltage", "Enable DC voltage on\nantenna connector?", YESNO, [this](bool v) {
if (v) {
portapack::set_antenna_bias(true);
receiver_model.set_antenna_bias();
transmitter_model.set_antenna_bias();
refresh();
}
});
nav_.display_modal("Bias voltage",
"Enable DC voltage on\nantenna connector?",
YESNO,
[this](bool v) {
if (v) {
portapack::set_antenna_bias(true);
receiver_model.set_antenna_bias();
transmitter_model.set_antenna_bias();
refresh();
}
});
} else {
portapack::set_antenna_bias(false);
receiver_model.set_antenna_bias();
@@ -321,48 +356,28 @@ void SystemStatusView::on_bias_tee() {
}
}
/*void SystemStatusView::on_textentry() {
uint8_t cfg;
cfg = portapack::persistent_memory::ui_config_textentry();
portapack::persistent_memory::set_config_textentry(cfg ^ 1);
if (!cfg)
button_textentry.set_bitmap(&bitmap_icon_unistroke);
else
button_textentry.set_bitmap(&bitmap_icon_keyboard);
}*/
void SystemStatusView::on_camera() {
ensure_directory("SCREENSHOTS");
auto path = next_filename_matching_pattern(u"SCREENSHOTS/SCR_????.PNG");
if (path.empty()) {
if (path.empty())
return;
}
PNGWriter png;
auto create_error = png.create(path);
if (create_error.is_valid()) {
auto error = png.create(path);
if (error)
return;
}
for (int i = 0; i < 320; i++) {
std::array<ColorRGB888, 240> row;
portapack::display.read_pixels({0, i, 240, 1}, row);
for (int i = 0; i < screen_height; i++) {
std::array<ColorRGB888, screen_width> row;
portapack::display.read_pixels({0, i, screen_width, 1}, row);
png.write_scanline(row);
}
}
void SystemStatusView::on_clk() {
bool v = portapack::persistent_memory::clkout_enabled();
if (v) {
v = false;
} else {
v = true;
}
portapack::clock_manager.enable_clock_output(v);
portapack::persistent_memory::set_clkout_enabled(v);
pmem::set_clkout_enabled(!pmem::clkout_enabled());
portapack::clock_manager.enable_clock_output(pmem::clkout_enabled());
refresh();
}
@@ -390,17 +405,17 @@ InformationView::InformationView(
version.set_style(&style_infobar);
ltime.set_hide_clock(portapack::persistent_memory::hide_clock());
ltime.set_hide_clock(pmem::hide_clock());
ltime.set_style(&style_infobar);
ltime.set_seconds_enabled(true);
ltime.set_date_enabled(portapack::persistent_memory::clock_with_date());
ltime.set_date_enabled(pmem::clock_with_date());
set_dirty();
}
void InformationView::refresh() {
ltime.set_hide_clock(portapack::persistent_memory::hide_clock());
ltime.set_hide_clock(pmem::hide_clock());
ltime.set_seconds_enabled(true);
ltime.set_date_enabled(portapack::persistent_memory::clock_with_date());
ltime.set_date_enabled(pmem::clock_with_date());
}
/* Navigation ************************************************************/
@@ -510,36 +525,36 @@ bool NavigationView::set_on_pop(std::function<void()> on_pop) {
/* ReceiversMenuView *****************************************************/
ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
if (portapack::persistent_memory::show_gui_return_icon()) {
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
if (pmem::show_gui_return_icon()) {
add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
}
add_items({
{
"ADS-B",
ui::Color::green(),
Color::green(),
&bitmap_icon_adsb,
[&nav]() { nav.push<ADSBRxView>(); },
},
//{ "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }, },
{"AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav]() { nav.push<AISAppView>(); }},
{"AFSK", ui::Color::yellow(), &bitmap_icon_modem, [&nav]() { nav.push<AFSKRxView>(); }},
{"BTLE", ui::Color::yellow(), &bitmap_icon_btle, [&nav]() { nav.push<BTLERxView>(); }},
{"NRF", ui::Color::yellow(), &bitmap_icon_nrf, [&nav]() { nav.push<NRFRxView>(); }},
{"Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav]() { nav.push<AnalogAudioView>(); }},
{"Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push<AnalogTvView>(); }},
{"ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav]() { nav.push<ERTAppView>(); }},
{"POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push<POCSAGAppView>(); }},
{"Radiosnde", ui::Color::green(), &bitmap_icon_sonde, [&nav]() { nav.push<SondeView>(); }},
{"TPMS Cars", ui::Color::green(), &bitmap_icon_tpms, [&nav]() { nav.push<TPMSAppView>(); }},
{"Recon", ui::Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<ReconView>(); }},
{"Level", ui::Color::green(), &bitmap_icon_options_radio, [&nav]() { nav.push<LevelView>(); }},
{"APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSRXView>(); }}
//{ "ACARS", Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }, },
{"AIS Boats", Color::green(), &bitmap_icon_ais, [&nav]() { nav.push<AISAppView>(); }},
{"AFSK", Color::yellow(), &bitmap_icon_modem, [&nav]() { nav.push<AFSKRxView>(); }},
{"BTLE", Color::yellow(), &bitmap_icon_btle, [&nav]() { nav.push<BTLERxView>(); }},
{"NRF", Color::yellow(), &bitmap_icon_nrf, [&nav]() { nav.push<NRFRxView>(); }},
{"Audio", Color::green(), &bitmap_icon_speaker, [&nav]() { nav.push<AnalogAudioView>(); }},
{"Analog TV", Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push<AnalogTvView>(); }},
{"ERT Meter", Color::green(), &bitmap_icon_ert, [&nav]() { nav.push<ERTAppView>(); }},
{"POCSAG", Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push<POCSAGAppView>(); }},
{"Radiosnde", Color::green(), &bitmap_icon_sonde, [&nav]() { nav.push<SondeView>(); }},
{"TPMS Cars", Color::green(), &bitmap_icon_tpms, [&nav]() { nav.push<TPMSAppView>(); }},
{"Recon", Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<ReconView>(); }},
{"Level", Color::green(), &bitmap_icon_options_radio, [&nav]() { nav.push<LevelView>(); }},
{"APRS", Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSRXView>(); }}
/*
{ "DMR", ui::Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push<NotImplementedView>(); } },
{ "SIGFOX", ui::Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push<NotImplementedView>(); } }, // SIGFRXView
{ "LoRa", ui::Color::dark_grey(), &bitmap_icon_lora, [&nav](){ nav.push<NotImplementedView>(); } },
{ "SSTV", ui::Color::dark_grey(), &bitmap_icon_sstv, [&nav](){ nav.push<NotImplementedView>(); } },
{ "TETRA", ui::Color::dark_grey(), &bitmap_icon_tetra, [&nav](){ nav.push<NotImplementedView>(); } },*/
{ "DMR", Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push<NotImplementedView>(); } },
{ "SIGFOX", Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push<NotImplementedView>(); } }, // SIGFRXView
{ "LoRa", Color::dark_grey(), &bitmap_icon_lora, [&nav](){ nav.push<NotImplementedView>(); } },
{ "SSTV", Color::dark_grey(), &bitmap_icon_sstv, [&nav](){ nav.push<NotImplementedView>(); } },
{ "TETRA", Color::dark_grey(), &bitmap_icon_tetra, [&nav](){ nav.push<NotImplementedView>(); } },*/
});
// set_highlighted(0); // Default selection is "Audio"
@@ -548,14 +563,14 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
/* TransmittersMenuView **************************************************/
TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
if (portapack::persistent_memory::show_gui_return_icon()) {
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
if (pmem::show_gui_return_icon()) {
add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
}
add_items({
{"ADS-B [S]", ui::Color::yellow(), &bitmap_icon_adsb, [&nav]() { nav.push<ADSBTxView>(); }},
{"ADS-B [S]", ui::Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push<ADSBTxView>(); }},
{"APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSTXView>(); }},
{"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push<BHTView>(); }},
{"GPS Sim", ui::Color::yellow(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }},
{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }},
{"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push<JammerView>(); }},
//{ "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push<KeyfobView>(); } },
{"LGE tool", ui::Color::yellow(), &bitmap_icon_lge, [&nav]() { nav.push<LGEView>(); }},
@@ -568,9 +583,9 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
{"Soundbrd", ui::Color::green(), &bitmap_icon_soundboard, [&nav]() { nav.push<SoundBoardView>(); }},
{"SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav]() { nav.push<SSTVTXView>(); }},
{"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }},
{"TouchTune", ui::Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push<TouchTunesView>(); }},
{"Playlist", ui::Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<PlaylistView>(); }},
{"S.Painter", ui::Color::orange(), &bitmap_icon_morse, [&nav]() { nav.push<SpectrumPainterView>(); }},
{"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push<TouchTunesView>(); }},
//{"Playlist", ui::Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<PlaylistView>(); }},
{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
//{ "Remote", ui::Color::dark_grey(), &bitmap_icon_remote, [&nav](){ nav.push<RemoteView>(); } },
});
}
@@ -578,22 +593,22 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
/* UtilitiesMenuView *****************************************************/
UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) {
if (portapack::persistent_memory::show_gui_return_icon()) {
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
if (pmem::show_gui_return_icon()) {
add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
}
add_items({
//{ "Test app", ui::Color::dark_grey(), nullptr, [&nav](){ nav.push<TestView>(); } },
{"Freq. manager", ui::Color::green(), &bitmap_icon_freqman, [&nav]() { nav.push<FrequencyManagerView>(); }},
{"File manager", ui::Color::yellow(), &bitmap_icon_dir, [&nav]() { nav.push<FileManagerView>(); }},
{"Notepad", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<TextEditorView>(); }},
{"Signal gen", ui::Color::green(), &bitmap_icon_cwgen, [&nav]() { nav.push<SigGenView>(); }},
//{ "Tone search", ui::Color::dark_grey(), nullptr, [&nav](){ nav.push<ToneSearchView>(); } },
{"Wav viewer", ui::Color::yellow(), &bitmap_icon_soundboard, [&nav]() { nav.push<ViewWavView>(); }},
{"Antenna length", ui::Color::green(), &bitmap_icon_tools_antenna, [&nav]() { nav.push<WhipCalcView>(); }},
//{ "Test app", Color::dark_grey(), nullptr, [&nav](){ nav.push<TestView>(); } },
{"Freq. manager", Color::green(), &bitmap_icon_freqman, [&nav]() { nav.push<FrequencyManagerView>(); }},
{"File manager", Color::green(), &bitmap_icon_dir, [&nav]() { nav.push<FileManagerView>(); }},
{"Notepad", Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<TextEditorView>(); }},
{"Signal gen", Color::green(), &bitmap_icon_cwgen, [&nav]() { nav.push<SigGenView>(); }},
//{ "Tone search", Color::dark_grey(), nullptr, [&nav](){ nav.push<ToneSearchView>(); } },
{"Wav viewer", Color::yellow(), &bitmap_icon_soundboard, [&nav]() { nav.push<ViewWavView>(); }},
{"Antenna length", Color::green(), &bitmap_icon_tools_antenna, [&nav]() { nav.push<WhipCalcView>(); }},
{"Wipe SD card", ui::Color::red(), &bitmap_icon_tools_wipesd, [&nav]() { nav.push<WipeSDView>(); }},
{"Flash Utility", ui::Color::red(), &bitmap_icon_temperature, [&nav]() { nav.push<FlashUtilityView>(); }},
{"SD over USB", ui::Color::yellow(), &bitmap_icon_hackrf, [&nav]() { nav.push<SdOverUsbView>(); }},
{"Wipe SD card", Color::red(), &bitmap_icon_tools_wipesd, [&nav]() { nav.push<WipeSDView>(); }},
{"Flash Utility", Color::red(), &bitmap_icon_temperature, [&nav]() { nav.push<FlashUtilityView>(); }},
{"SD over USB", Color::yellow(), &bitmap_icon_hackrf, [&nav]() { nav.push<SdOverUsbView>(); }},
});
set_max_rows(2); // allow wider buttons
}
@@ -611,20 +626,20 @@ void SystemMenuView::hackrf_mode(NavigationView& nav) {
SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items({
//{ "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push<PlayDeadView>(); } },
{"Receive", ui::Color::cyan(), &bitmap_icon_receivers, [&nav]() { nav.push<ReceiversMenuView>(); }},
{"Transmit", ui::Color::cyan(), &bitmap_icon_transmit, [&nav]() { nav.push<TransmittersMenuView>(); }},
{"Capture", ui::Color::red(), &bitmap_icon_capture, [&nav]() { nav.push<CaptureAppView>(); }},
{"Replay", ui::Color::green(), &bitmap_icon_replay, [&nav]() { nav.push<ReplayAppView>(); }},
{"Search", ui::Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push<SearchView>(); }},
{"Scanner", ui::Color::yellow(), &bitmap_icon_scanner, [&nav]() { nav.push<ScannerView>(); }},
{"Microphone", ui::Color::yellow(), &bitmap_icon_microphone, [&nav]() { nav.push<MicTXView>(); }},
{"Looking Glass", ui::Color::yellow(), &bitmap_icon_looking, [&nav]() { nav.push<GlassView>(); }},
{"Utilities", ui::Color::cyan(), &bitmap_icon_utilities, [&nav]() { nav.push<UtilitiesMenuView>(); }},
{"Settings", ui::Color::cyan(), &bitmap_icon_setup, [&nav]() { nav.push<SettingsMenuView>(); }},
{"Debug", ui::Color::light_grey(), &bitmap_icon_debug, [&nav]() { nav.push<DebugMenuView>(); }},
{"HackRF", ui::Color::cyan(), &bitmap_icon_hackrf, [this, &nav]() { hackrf_mode(nav); }},
//{ "About", ui::Color::cyan(), nullptr, [&nav](){ nav.push<AboutView>(); } }
//{ "Play dead", Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push<PlayDeadView>(); } },
{"Receive", Color::cyan(), &bitmap_icon_receivers, [&nav]() { nav.push<ReceiversMenuView>(); }},
{"Transmit", Color::cyan(), &bitmap_icon_transmit, [&nav]() { nav.push<TransmittersMenuView>(); }},
{"Capture", Color::red(), &bitmap_icon_capture, [&nav]() { nav.push<CaptureAppView>(); }},
{"Replay", Color::green(), &bitmap_icon_replay, [&nav]() { nav.push<PlaylistView>(); }},
{"Search", Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push<SearchView>(); }},
{"Scanner", Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<ScannerView>(); }},
{"Microphone", Color::green(), &bitmap_icon_microphone, [&nav]() { nav.push<MicTXView>(); }},
{"Looking Glass", Color::green(), &bitmap_icon_looking, [&nav]() { nav.push<GlassView>(); }},
{"Utilities", Color::cyan(), &bitmap_icon_utilities, [&nav]() { nav.push<UtilitiesMenuView>(); }},
{"Settings", Color::cyan(), &bitmap_icon_setup, [&nav]() { nav.push<SettingsMenuView>(); }},
{"Debug", Color::light_grey(), &bitmap_icon_debug, [&nav]() { nav.push<DebugMenuView>(); }},
{"HackRF", Color::cyan(), &bitmap_icon_hackrf, [this, &nav]() { hackrf_mode(nav); }},
//{ "About", Color::cyan(), nullptr, [&nav](){ nav.push<AboutView>(); } }
});
set_max_rows(2); // allow wider buttons
set_arrow_enabled(false);
@@ -638,10 +653,10 @@ SystemView::SystemView(
const Rect parent_rect)
: View{parent_rect},
context_(context) {
set_style(&ui::Styles::white);
set_style(&Styles::white);
constexpr ui::Dim status_view_height = 16;
constexpr ui::Dim info_view_height = 16;
constexpr Dim status_view_height = 16;
constexpr Dim info_view_height = 16;
add_child(&status_view);
status_view.set_parent_rect({{0, 0},
@@ -652,7 +667,7 @@ SystemView::SystemView(
add_child(&navigation_view);
navigation_view.set_parent_rect({{0, status_view_height},
{parent_rect.width(), static_cast<ui::Dim>(parent_rect.height() - status_view_height)}});
{parent_rect.width(), static_cast<Dim>(parent_rect.height() - status_view_height)}});
add_child(&info_view);
info_view.set_parent_rect({{0, 19 * 16},
@@ -672,17 +687,17 @@ SystemView::SystemView(
this->status_view.set_dirty();
};
// portapack::persistent_memory::set_playdead_sequence(0x8D1);
// pmem::set_playdead_sequence(0x8D1);
// Initial view
/*if ((portapack::persistent_memory::playing_dead() == 0x5920C1DF) || // Enable code
(portapack::persistent_memory::ui_config() & 16)) { // Login option
/*if ((pmem::playing_dead() == 0x5920C1DF) || // Enable code
(pmem::ui_config() & 16)) { // Login option
navigation_view.push<PlayDeadView>();
} else {*/
navigation_view.push<SystemMenuView>();
if (portapack::persistent_memory::config_splash()) {
if (pmem::config_splash()) {
navigation_view.push<BMPView>();
}
status_view.set_back_enabled(false);
@@ -699,20 +714,27 @@ Context& SystemView::context() const {
}
void SystemView::toggle_overlay() {
if (overlay_active) {
this->remove_child(&this->overlay);
this->set_dirty();
shared_memory.request_m4_performance_counter = 0;
} else {
this->add_child(&this->overlay);
this->set_dirty();
shared_memory.request_m4_performance_counter = 1;
shared_memory.m4_cpu_usage = 0;
shared_memory.m4_heap_usage = 0;
shared_memory.m4_stack_usage = 0;
switch (++overlay_active) {
case 1:
this->add_child(&this->overlay);
this->set_dirty();
shared_memory.request_m4_performance_counter = 1;
shared_memory.m4_cpu_usage = 0;
shared_memory.m4_heap_usage = 0;
shared_memory.m4_stack_usage = 0;
break;
case 2:
this->remove_child(&this->overlay);
this->add_child(&this->overlay2);
this->set_dirty();
shared_memory.request_m4_performance_counter = 0;
break;
case 3:
this->remove_child(&this->overlay2);
this->set_dirty();
overlay_active = 0;
break;
}
overlay_active = !overlay_active;
}
void SystemView::paint_overlay() {
@@ -723,7 +745,10 @@ void SystemView::paint_overlay() {
return;
last_paint_state = !last_paint_state;
this->overlay.set_dirty();
if (overlay_active == 1)
this->overlay.set_dirty();
else
this->overlay2.set_dirty();
}
}
+48 -19
View File
@@ -120,6 +120,32 @@ class NavigationView : public View {
View* push_view(std::unique_ptr<View> new_view);
};
/* Holds widgets and grows dynamically toward the left.
* 16px tall fixed and right-aligns all children in the
* order in which they were added. */
// TODO: Could make this a generic "StackPanel" control.
class StatusTray : public View {
public:
StatusTray(Point pos);
StatusTray(const StatusTray&) = delete;
StatusTray& operator=(const StatusTray&) = delete;
void add_button(ImageButton* child);
void add(Widget* child);
void update_layout();
void clear();
void paint(Painter& painter) override;
uint8_t width() { return width_; };
private:
static constexpr uint8_t height = 16;
// This control grow to the left, so keep
// track of the right edge.
const Point pos_{};
uint8_t width_{};
};
class SystemStatusView : public View {
public:
std::function<void(void)> on_back{};
@@ -136,7 +162,7 @@ class SystemStatusView : public View {
NavigationView& nav_;
Rectangle backdrop{
{0 * 8, 0 * 16, 240, 16},
{0 * 8, 0 * 16, ui::screen_width, 16},
Color::dark_grey()};
ImageButton button_back{
@@ -156,63 +182,65 @@ class SystemStatusView : public View {
Color::white(),
Color::dark_grey()};
StatusTray status_icons{{screen_width, 0}};
// TODO: Convert to ImageToggle buttons.
ImageButton button_speaker{
{15 * 8, 0, 2 * 8, 1 * 16},
&bitmap_icon_speaker_mute,
{0, 0, 2 * 8, 1 * 16},
&bitmap_icon_speaker,
Color::light_grey(),
Color::dark_grey()};
ImageButton button_mute{
{0, 0, 2 * 8, 1 * 16},
&bitmap_icon_speaker_and_headphones,
Color::light_grey(),
Color::dark_grey()};
ImageButton button_converter{
{17 * 8, 0, 2 * 8, 1 * 16},
{0, 0, 2 * 8, 1 * 16},
&bitmap_icon_upconvert,
Color::light_grey(),
Color::dark_grey()};
ImageButton button_stealth{
{19 * 8, 0, 2 * 8, 1 * 16},
{0, 0, 2 * 8, 1 * 16},
&bitmap_icon_stealth,
Color::light_grey(),
Color::dark_grey()};
/*ImageButton button_textentry {
{ 170, 0, 2 * 8, 1 * 16 },
&bitmap_icon_unistroke,
Color::white(),
Color::dark_grey()
};*/
ImageButton button_camera{
{21 * 8, 0, 2 * 8, 1 * 16},
{0, 0, 2 * 8, 1 * 16},
&bitmap_icon_camera,
Color::white(),
Color::dark_grey()};
ImageButton button_sleep{
{23 * 8, 0, 2 * 8, 1 * 16},
{0, 0, 2 * 8, 1 * 16},
&bitmap_icon_sleep,
Color::white(),
Color::dark_grey()};
ImageButton button_bias_tee{
{25 * 8, 0, 12, 1 * 16},
{0, 0, 2 * 8, 1 * 16},
&bitmap_icon_biast_off,
Color::light_grey(),
Color::dark_grey()};
ImageButton button_clock_status{
{27 * 8, 0 * 16, 2 * 8, 1 * 16},
{0, 0 * 16, 8, 1 * 16},
&bitmap_icon_clk_int,
Color::light_grey(),
Color::dark_grey()};
SDCardStatusView sd_card_status_view{
{28 * 8, 0 * 16, 2 * 8, 1 * 16}};
{0, 0 * 16, 2 * 8, 1 * 16}};
void on_converter();
void on_speaker();
void on_mute();
void on_stealth();
void on_bias_tee();
// void on_textentry();
void on_camera();
void on_title();
void refresh();
@@ -300,11 +328,12 @@ class SystemView : public View {
void paint_overlay();
private:
bool overlay_active{false};
uint8_t overlay_active{0};
SystemStatusView status_view{navigation_view};
InformationView info_view{navigation_view};
DfuMenu overlay{navigation_view};
DfuMenu2 overlay2{navigation_view};
NavigationView navigation_view{};
Context& context_;
};
+4
View File
@@ -837,6 +837,10 @@ class AK4951 : public audio::Codec {
void speaker_enable();
void speaker_disable();
bool speaker_disable_supported() const override {
return true;
}
void set_headphone_volume(const volume_t volume) override;
void headphone_mute();
+11
View File
@@ -34,6 +34,17 @@ class Optional {
constexpr Optional(T&& value)
: value_{std::move(value)}, valid_{true} {}
constexpr Optional& operator=(const T& value) {
value_ = value;
valid_ = true;
return *this;
}
constexpr Optional& operator=(T&& value) {
value_ = std::move(value);
valid_ = true;
return *this;
}
bool is_valid() const { return valid_; }
operator bool() const { return valid_; }
+166 -68
View File
@@ -89,26 +89,26 @@ enum data_structure_version_enum : uint32_t {
static const uint32_t TOUCH_CALIBRATION_MAGIC = 0x074af82f;
enum bits_t {
BacklightTimeoutLSB = 0,
BacklightTimeoutEnable = 3,
ClkoutFreqLSB = 4,
ShowGUIReturnIcon = 20,
LoadAppSettings = 21,
SaveAppSettings = 22,
ShowBiggerQRCode = 23,
DisableTouchscreen = 24,
HideClock = 25,
ClockWithDate = 26,
ClkOutEnabled = 27,
ConfigSpeakerHidden = 28, // unused since Speaker icon modifications
StealthMode = 29,
ConfigLogin = 30,
ConfigSplash = 31,
};
struct ui_config_t {
private:
enum bits_t {
BacklightTimeoutLSB = 0,
BacklightTimeoutEnable = 3,
ClkoutFreqLSB = 4,
ShowGUIReturnIcon = 20,
LoadAppSettings = 21,
SaveAppSettings = 22,
ShowBiggerQRCode = 23,
DisableTouchscreen = 24,
HideClock = 25,
ClockWithDate = 26,
ClkOutEnabled = 27,
UNUSED = 28,
StealthMode = 29,
ConfigLogin = 30,
ConfigSplash = 31,
};
enum bits_mask_t : uint32_t {
BacklightTimeoutMask = ((1 << 3) - 1) << bits_t::BacklightTimeoutLSB,
ClkoutFreqMask = ((1 << 16) - 1) << bits_t::ClkoutFreqLSB,
@@ -252,6 +252,27 @@ struct ui_config_t {
}
};
/* Additional UI config.
* NB: Will be default init - override in defaults(). */
struct ui_config2_t {
/* Top icon bar */
bool hide_speaker : 1;
bool hide_converter : 1;
bool hide_stealth : 1;
bool hide_camera : 1;
bool hide_sleep : 1;
bool hide_bias_tee : 1;
bool hide_clock : 1;
bool hide_sd_card : 1;
bool hide_mute : 1;
bool UNUSED : 7;
uint8_t placeholder_1;
uint8_t placeholder_2;
};
static_assert(sizeof(ui_config2_t) == sizeof(uint32_t));
struct misc_config_t {
private:
enum bits_t {
@@ -321,7 +342,7 @@ struct data_t {
uint32_t playing_dead;
uint32_t playdead_sequence;
// UI
// UI Config
ui_config_t ui_config;
uint32_t pocsag_last_address;
@@ -335,8 +356,8 @@ struct data_t {
// Recon App
uint64_t recon_config;
// converter: show or hide icon. Hiding cause auto disable to avoid mistakes
bool hide_converter;
bool placeholder_0;
// enable or disable converter
bool converter;
// set up converter (false) or down converter (true) converter
@@ -353,12 +374,15 @@ struct data_t {
// Rotary encoder dial sensitivity (encoder.cpp/hpp)
uint8_t encoder_dial_sensitivity;
// Headphone volume in centibels.
// Headphone volume in centibels. (Only really needs 10 bits)
int32_t headphone_volume_cb;
// Misc flags
misc_config_t misc_config;
// Additional UI settings.
ui_config2_t ui_config2;
constexpr data_t()
: structure_version(data_structure_version_enum::VERSION_CURRENT),
target_frequency(target_frequency_reset_value),
@@ -387,7 +411,7 @@ struct data_t {
hardware_config(0),
recon_config(0),
hide_converter(0),
placeholder_0(0),
converter(0),
updown_converter(0),
converter_frequency_offset(0),
@@ -397,7 +421,8 @@ struct data_t {
updown_frequency_tx_correction(0),
encoder_dial_sensitivity(0),
headphone_volume_cb(-600),
misc_config() {
misc_config(),
ui_config2() {
}
};
@@ -470,6 +495,11 @@ struct backup_ram_t {
static_assert(sizeof(backup_ram_t) == memory::map::backup_ram.size());
static_assert(sizeof(data_t) <= sizeof(backup_ram_t) - sizeof(uint32_t));
/* Uncomment to get a compiler error with the data_t size. */
// template <size_t N>
// struct ShowSize;
// ShowSize<sizeof(data_t)> __data_t_size;
static backup_ram_t* const backup_ram = reinterpret_cast<backup_ram_t*>(memory::map::backup_ram.base());
static backup_ram_t cached_backup_ram;
@@ -479,9 +509,6 @@ namespace cache {
void defaults() {
cached_backup_ram = backup_ram_t();
*data = data_t(); // This is a workaround for apparently alignment issue
// that is causing backup_ram_t's block copy to be
// misaligned. This force sets values through the struct.
// defaults values for recon app
set_recon_autosave_freqs(false);
@@ -745,18 +772,6 @@ void set_config_backlight_timer(const backlight_config_t& new_value) {
data->ui_config.set_config_backlight_timer(new_value);
}
/*void set_config_textentry(uint8_t new_value) {
data->ui_config = (data->ui_config & ~0b100) | ((new_value & 1) << 2);
}
uint8_t ui_config_textentry() {
return ((data->ui_config >> 2) & 1);
}*/
/*void set_ui_config(const uint32_t new_value) {
data->ui_config = new_value;
}*/
uint32_t pocsag_last_address() {
return data->pocsag_last_address;
}
@@ -781,6 +796,7 @@ void set_clkout_freq(uint32_t freq) {
data->ui_config.set_clkout_freq(freq);
}
/* Recon app */
bool recon_autosave_freqs() {
return (data->recon_config & 0x80000000UL) ? true : false;
}
@@ -808,6 +824,9 @@ bool recon_load_hamradios() {
bool recon_match_mode() {
return (data->recon_config & 0x00800000UL) ? true : false;
}
bool recon_auto_record_locked() {
return (data->recon_config & 0x00400000UL) ? true : false;
}
void set_recon_autosave_freqs(const bool v) {
data->recon_config = (data->recon_config & ~0x80000000UL) | (v << 31);
@@ -836,9 +855,70 @@ void set_recon_load_hamradios(const bool v) {
void set_recon_match_mode(const bool v) {
data->recon_config = (data->recon_config & ~0x00800000UL) | (v << 23);
}
bool config_hide_converter() {
return data->hide_converter;
void set_recon_auto_record_locked(const bool v) {
data->recon_config = (data->recon_config & ~0x00400000UL) | (v << 22);
}
/* UI Config 2 */
bool ui_hide_speaker() {
return data->ui_config2.hide_speaker;
}
bool ui_hide_mute() {
return data->ui_config2.hide_mute;
}
bool ui_hide_converter() {
return data->ui_config2.hide_converter;
}
bool ui_hide_stealth() {
return data->ui_config2.hide_stealth;
}
bool ui_hide_camera() {
return data->ui_config2.hide_camera;
}
bool ui_hide_sleep() {
return data->ui_config2.hide_sleep;
}
bool ui_hide_bias_tee() {
return data->ui_config2.hide_bias_tee;
}
bool ui_hide_clock() {
return data->ui_config2.hide_clock;
}
bool ui_hide_sd_card() {
return data->ui_config2.hide_sd_card;
}
void set_ui_hide_speaker(bool v) {
data->ui_config2.hide_speaker = v;
}
void set_ui_hide_mute(bool v) {
data->ui_config2.hide_mute = v;
}
void set_ui_hide_converter(bool v) {
data->ui_config2.hide_converter = v;
if (v)
data->converter = false;
}
void set_ui_hide_stealth(bool v) {
data->ui_config2.hide_stealth = v;
}
void set_ui_hide_camera(bool v) {
data->ui_config2.hide_camera = v;
}
void set_ui_hide_sleep(bool v) {
data->ui_config2.hide_sleep = v;
}
void set_ui_hide_bias_tee(bool v) {
data->ui_config2.hide_bias_tee = v;
}
void set_ui_hide_clock(bool v) {
data->ui_config2.hide_clock = v;
}
void set_ui_hide_sd_card(bool v) {
data->ui_config2.hide_sd_card = v;
}
/* Converter */
bool config_converter() {
return data->converter;
}
@@ -849,12 +929,6 @@ int64_t config_converter_freq() {
return data->converter_frequency_offset;
}
void set_config_hide_converter(bool v) {
data->hide_converter = v;
if (v) {
data->converter = false;
}
}
void set_config_converter(bool v) {
data->converter = v;
}
@@ -865,25 +939,26 @@ void set_config_converter_freq(int64_t v) {
data->converter_frequency_offset = v;
}
// frequency correction settings
// Frequency correction settings
bool config_freq_tx_correction_updown() {
return data->updown_frequency_tx_correction;
}
void set_freq_tx_correction_updown(bool v) {
data->updown_frequency_tx_correction = v;
}
bool config_freq_rx_correction_updown() {
return data->updown_frequency_rx_correction;
}
void set_freq_rx_correction_updown(bool v) {
data->updown_frequency_rx_correction = v;
}
uint32_t config_freq_tx_correction() {
return data->frequency_tx_correction;
}
uint32_t config_freq_rx_correction() {
return data->frequency_rx_correction;
}
void set_freq_tx_correction_updown(bool v) {
data->updown_frequency_tx_correction = v;
}
void set_freq_rx_correction_updown(bool v) {
data->updown_frequency_rx_correction = v;
}
void set_config_freq_tx_correction(uint32_t v) {
data->frequency_tx_correction = v;
}
@@ -891,7 +966,8 @@ void set_config_freq_rx_correction(uint32_t v) {
data->frequency_rx_correction = v;
}
// rotary encoder dial settings
// Rotary encoder dial settings
uint8_t config_encoder_dial_sensitivity() {
return data->encoder_dial_sensitivity;
}
@@ -899,11 +975,12 @@ void set_encoder_dial_sensitivity(uint8_t v) {
data->encoder_dial_sensitivity = v;
}
// PMem to sdcard settings
bool should_use_sdcard_for_pmem() {
return std::filesystem::file_exists(PMEM_FILEFLAG);
}
// sd persisting settings
int save_persistent_settings_to_file() {
std::string filename = PMEM_SETTING_FILE;
delete_file(filename);
@@ -927,10 +1004,14 @@ int load_persistent_settings_from_file() {
return false;
}
// Pmem size helper
size_t data_size() {
return sizeof(data_t);
}
// Dump pmem, receiver and transmitter models internals in human readable format
bool debug_dump() {
ui::Painter painter{};
std::string debug_dir = "DEBUG";
@@ -940,6 +1021,7 @@ bool debug_dump() {
make_new_directory(debug_dir);
filename = next_filename_matching_pattern(debug_dir + "/DEBUG_DUMP_????.TXT");
if (filename.empty()) {
painter.draw_string({0, 320 - 16}, ui::Styles::red, "COULD NOT GET DUMP NAME !");
return false;
}
// dump data fo filename
@@ -948,11 +1030,13 @@ bool debug_dump() {
painter.draw_string({0, 320 - 16}, ui::Styles::red, "ERROR DUMPING " + filename.filename().string() + " !");
return false;
}
pmem_dump_file.write_line("FW version " VERSION_STRING);
// write persistent memory
pmem_dump_file.write_line("[Persistent Memory]");
pmem_dump_file.write_line("\n[Persistent Memory]");
// full variables
pmem_dump_file.write_line("structure_version: " + to_string_dec_uint(data->structure_version));
pmem_dump_file.write_line("structure_version: 0x" + to_string_hex(data->structure_version, 8));
pmem_dump_file.write_line("target_frequency: " + to_string_dec_int(data->target_frequency));
pmem_dump_file.write_line("correction_ppb: " + to_string_dec_int(data->correction_ppb));
pmem_dump_file.write_line("modem_def_index: " + to_string_dec_uint(data->modem_def_index));
@@ -970,17 +1054,20 @@ bool debug_dump() {
pmem_dump_file.write_line("playdead_sequence: " + to_string_dec_uint(data->playdead_sequence));
pmem_dump_file.write_line("pocsag_last_address: " + to_string_dec_uint(data->pocsag_last_address));
pmem_dump_file.write_line("pocsag_ignore_address: " + to_string_dec_uint(data->pocsag_ignore_address));
pmem_dump_file.write_line("tone_mix: " + to_string_dec_uint(data->tone_mix));
pmem_dump_file.write_line("hardware_config: " + to_string_dec_uint(data->hardware_config));
pmem_dump_file.write_line("recon_config: " + to_string_dec_uint(data->recon_config));
pmem_dump_file.write_line("hide_converter: " + to_string_dec_int(data->tone_mix));
pmem_dump_file.write_line("converter: " + to_string_dec_int(data->tone_mix));
pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(data->tone_mix));
pmem_dump_file.write_line("recon_config: 0x" + to_string_hex(data->recon_config, 16));
pmem_dump_file.write_line("placeholder_0: " + to_string_dec_int(data->placeholder_0));
pmem_dump_file.write_line("converter: " + to_string_dec_int(data->converter));
pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(data->updown_converter));
pmem_dump_file.write_line("converter_frequency_offset: " + to_string_dec_int(data->converter_frequency_offset));
pmem_dump_file.write_line("frequency_rx_correction: " + to_string_dec_uint(data->frequency_rx_correction));
pmem_dump_file.write_line("updown_frequency_rx_correction: " + to_string_dec_int(data->updown_frequency_rx_correction));
pmem_dump_file.write_line("frequency_tx_correction: " + to_string_dec_uint(data->frequency_tx_correction));
pmem_dump_file.write_line("updown_frequency_tx_correction: " + to_string_dec_int(data->updown_frequency_tx_correction));
pmem_dump_file.write_line("encoder_dial_sensitivity: " + to_string_dec_uint(data->encoder_dial_sensitivity));
pmem_dump_file.write_line("headphone_volume_cb: " + to_string_dec_int(data->headphone_volume_cb));
// ui_config bits
const auto backlight_timer = portapack::persistent_memory::config_backlight_timer();
pmem_dump_file.write_line("ui_config backlight_timer.timeout_enabled: " + to_string_dec_uint(backlight_timer.timeout_enabled()));
@@ -997,12 +1084,23 @@ bool debug_dump() {
pmem_dump_file.write_line("ui_config stealth_mode: " + to_string_dec_uint(data->ui_config.stealth_mode()));
pmem_dump_file.write_line("ui_config config_login: " + to_string_dec_uint(data->ui_config.config_login()));
pmem_dump_file.write_line("ui_config config_splash: " + to_string_dec_uint(data->ui_config.config_splash()));
// ui_config2 bits
pmem_dump_file.write_line("ui_config2 hide_speaker: " + to_string_dec_uint(data->ui_config2.hide_speaker));
pmem_dump_file.write_line("ui_config2 hide_converter: " + to_string_dec_uint(data->ui_config2.hide_converter));
pmem_dump_file.write_line("ui_config2 hide_stealth: " + to_string_dec_uint(data->ui_config2.hide_stealth));
pmem_dump_file.write_line("ui_config2 hide_camera: " + to_string_dec_uint(data->ui_config2.hide_camera));
pmem_dump_file.write_line("ui_config2 hide_sleep: " + to_string_dec_uint(data->ui_config2.hide_sleep));
pmem_dump_file.write_line("ui_config2 hide_bias_tee: " + to_string_dec_uint(data->ui_config2.hide_bias_tee));
pmem_dump_file.write_line("ui_config2 hide_clock: " + to_string_dec_uint(data->ui_config2.hide_clock));
pmem_dump_file.write_line("ui_config2 hide_sd_card: " + to_string_dec_uint(data->ui_config2.hide_sd_card));
pmem_dump_file.write_line("ui_config2 hide_mute: " + to_string_dec_uint(data->ui_config2.hide_mute));
// misc_config bits
pmem_dump_file.write_line("misc_config config_audio_mute: " + to_string_dec_int(config_audio_mute()));
pmem_dump_file.write_line("misc_config config_speaker_disable: " + to_string_dec_int(config_speaker_disable()));
// receiver_model
pmem_dump_file.write_line("[Receiver Model]");
pmem_dump_file.write_line("\n[Receiver Model]");
pmem_dump_file.write_line("target_frequency: " + to_string_dec_uint(receiver_model.target_frequency()));
pmem_dump_file.write_line("frequency_step: " + to_string_dec_uint(receiver_model.frequency_step()));
pmem_dump_file.write_line("lna: " + to_string_dec_int(receiver_model.lna()));
@@ -1030,13 +1128,14 @@ bool debug_dump() {
pmem_dump_file.write_line("modulation: !!unknown mode!!");
break;
}
pmem_dump_file.write_line("headphone_volume.centibel: " + to_string_dec_uint(receiver_model.headphone_volume().centibel()));
pmem_dump_file.write_line("headphone_volume.centibel: " + to_string_dec_int(receiver_model.headphone_volume().centibel()));
pmem_dump_file.write_line("normalized_headphone_volume: " + to_string_dec_uint(receiver_model.normalized_headphone_volume()));
pmem_dump_file.write_line("am_configuration: " + to_string_dec_uint(receiver_model.am_configuration()));
pmem_dump_file.write_line("nbfm_configuration: " + to_string_dec_uint(receiver_model.nbfm_configuration()));
pmem_dump_file.write_line("wfm_configuration: " + to_string_dec_uint(receiver_model.wfm_configuration()));
// transmitter_model
pmem_dump_file.write_line("[Transmitter Model]");
pmem_dump_file.write_line("\n[Transmitter Model]");
pmem_dump_file.write_line("target_frequency: " + to_string_dec_uint(transmitter_model.target_frequency()));
pmem_dump_file.write_line("lna: " + to_string_dec_int(transmitter_model.lna()));
pmem_dump_file.write_line("vga: " + to_string_dec_int(transmitter_model.vga()));
@@ -1045,9 +1144,8 @@ bool debug_dump() {
pmem_dump_file.write_line("sampling_rate: " + to_string_dec_uint(transmitter_model.sampling_rate()));
pmem_dump_file.write_line("tx_gain: " + to_string_dec_int(transmitter_model.tx_gain()));
pmem_dump_file.write_line("channel_bandwidth: " + to_string_dec_uint(transmitter_model.channel_bandwidth()));
// on screen information
painter.draw_string({0, 320 - 16}, ui::Styles::green, filename.filename().string() + " DUMPED !");
return true;
}
@@ -173,7 +173,6 @@ uint8_t config_cpld();
void set_config_cpld(uint8_t i);
bool config_splash();
bool config_hide_converter();
bool config_converter();
bool config_updown_converter();
int64_t config_converter_freq();
@@ -194,11 +193,9 @@ void set_load_app_settings(bool v);
void set_save_app_settings(bool v);
void set_show_bigger_qr_code(bool v);
void set_config_splash(bool v);
bool config_hide_converter();
bool config_converter();
bool config_updown_converter();
int64_t config_converter_freq();
void set_config_hide_converter(bool v);
void set_config_converter(bool v);
void set_config_updown_converter(bool v);
void set_config_converter_freq(int64_t v);
@@ -220,9 +217,6 @@ void set_disable_touchscreen(bool v);
uint8_t config_encoder_dial_sensitivity();
void set_encoder_dial_sensitivity(uint8_t v);
// uint8_t ui_config_textentry();
// void set_config_textentry(uint8_t new_value);
uint32_t pocsag_last_address();
void set_pocsag_last_address(uint32_t address);
@@ -242,6 +236,7 @@ bool recon_clear_output();
bool recon_load_freqs();
bool recon_load_ranges();
bool recon_update_ranges_when_recon();
bool recon_auto_record_locked();
bool recon_load_hamradios();
bool recon_match_mode();
void set_recon_autosave_freqs(const bool v);
@@ -251,9 +246,30 @@ void set_recon_clear_output(const bool v);
void set_recon_load_freqs(const bool v);
void set_recon_load_ranges(const bool v);
void set_recon_update_ranges_when_recon(const bool v);
void set_recon_auto_record_locked(const bool v);
void set_recon_load_hamradios(const bool v);
void set_recon_match_mode(const bool v);
/* UI Config 2 */
bool ui_hide_speaker();
bool ui_hide_mute();
bool ui_hide_converter();
bool ui_hide_stealth();
bool ui_hide_camera();
bool ui_hide_sleep();
bool ui_hide_bias_tee();
bool ui_hide_clock();
bool ui_hide_sd_card();
void set_ui_hide_speaker(bool v);
void set_ui_hide_mute(bool v);
void set_ui_hide_converter(bool v);
void set_ui_hide_stealth(bool v);
void set_ui_hide_camera(bool v);
void set_ui_hide_sleep(bool v);
void set_ui_hide_bias_tee(bool v);
void set_ui_hide_clock(bool v);
void set_ui_hide_sd_card(bool v);
// sd persisting settings
bool should_use_sdcard_for_pmem();
int save_persistent_settings_to_file();
+15 -4
View File
@@ -31,6 +31,9 @@ namespace ui {
using Coord = int16_t;
using Dim = int16_t;
constexpr uint16_t screen_width = 240;
constexpr uint16_t screen_height = 320;
struct Color {
uint16_t v; // rrrrrGGGGGGbbbbb
@@ -53,15 +56,20 @@ struct Color {
}
uint8_t to_greyscale() {
uint32_t r = ((v >> 11) & 31U) << 3;
uint32_t g = ((v >> 5) & 63U) << 2;
uint32_t b = (v & 31U) << 3;
uint32_t r = (v >> 8) & 0xf8;
uint32_t g = (v >> 3) & 0xfc;
uint32_t b = (v << 3) & 0xf8;
uint32_t grey = (r * 299 + g * 587 + b * 114) / 1000;
uint32_t grey = ((r * 306) + (g * 601) + (b * 117)) >> 10;
return (uint8_t)grey;
}
uint16_t dark() {
// stripping bits 4 & 5 from each of the colors R/G/B
return (v & ((0xc8 << 8) | (0xcc << 3) | (0xc8 >> 3)));
}
Color operator-() const {
return (v ^ 0xffff);
}
@@ -132,6 +140,9 @@ struct Color {
static constexpr Color dark_grey() {
return {63, 63, 63};
}
static constexpr Color darker_grey() {
return {31, 31, 31};
}
static constexpr Color purple() {
return {204, 0, 102};
+4
View File
@@ -79,6 +79,10 @@ int Painter::draw_string(
}
void Painter::draw_bitmap(Point p, const Bitmap& bitmap, Color foreground, Color background) {
// If bright foreground colors on white background, darken the foreground color to improve visibility
if ((background.v == ui::Color::white().v) && (foreground.to_greyscale() > 146))
foreground = foreground.dark();
display.draw_bitmap(p, bitmap.size, bitmap.data, foreground, background);
}
+4
View File
@@ -39,6 +39,10 @@ Dim Font::line_height() const {
return h;
}
Dim Font::char_width() const {
return w;
}
Size Font::size_of(const std::string s) const {
Size size;
+2
View File
@@ -86,6 +86,8 @@ class Font {
Glyph glyph(const char c) const;
Dim line_height() const;
Dim char_width() const;
Size size_of(const std::string s) const;
private:
+64
View File
@@ -177,6 +177,8 @@ const std::vector<Widget*>& Widget::children() const {
}
Context& Widget::context() const {
chDbgAssert(parent_, "parent_ is null",
"Check that parent isn't null before deref.");
return parent()->context();
}
@@ -1323,6 +1325,68 @@ bool ImageButton::on_touch(const TouchEvent event) {
}
}
/* ImageToggle ***********************************************************/
ImageToggle::ImageToggle(
Rect parent_rect,
const Bitmap* bitmap_)
: ImageToggle{parent_rect,
bitmap_,
Color::green(),
Color::light_grey(),
Color::dark_grey()} {}
ImageToggle::ImageToggle(
Rect parent_rect,
const Bitmap* bitmap_,
Color foreground_true,
Color foreground_false,
Color background_)
: ImageToggle{parent_rect,
bitmap_,
bitmap_,
foreground_true,
background_,
foreground_false,
background_} {}
ImageToggle::ImageToggle(
Rect parent_rect,
const Bitmap* bitmap_true,
const Bitmap* bitmap_false,
Color foreground_true,
Color background_true,
Color foreground_false,
Color background_false)
: ImageButton{parent_rect, bitmap_false, foreground_false, background_false},
bitmap_true_{bitmap_true},
bitmap_false_{bitmap_false},
foreground_true_{foreground_true},
background_true_{background_true},
foreground_false_{foreground_false},
background_false_{background_false},
value_{false} {
ImageButton::on_select = [this](ImageButton&) {
set_value(!value());
};
}
bool ImageToggle::value() const {
return value_;
}
void ImageToggle::set_value(bool b) {
if (b == value_)
return;
value_ = b;
set_bitmap(b ? bitmap_true_ : bitmap_false_);
set_foreground(b ? foreground_true_ : foreground_false_);
set_background(b ? background_true_ : background_false_);
if (on_change)
on_change(b);
}
/* ImageOptionsField *****************************************************/
ImageOptionsField::ImageOptionsField(
+46 -1
View File
@@ -448,7 +448,7 @@ class ButtonWithEncoder : public Widget {
class NewButton : public Widget {
public:
std::function<void(void)> on_select{};
// std::function<void(NewButton&)> on_select { };
// std::function<void(NewButton&)> on_select{};
std::function<bool(NewButton&, KeyEvent)> on_dir{};
std::function<void(NewButton&)> on_highlight{};
@@ -500,6 +500,8 @@ class Image : public Widget {
void paint(Painter& painter) override;
const Bitmap& bitmap() & { return *bitmap_; }
private:
const Bitmap* bitmap_;
Color foreground_;
@@ -520,6 +522,49 @@ class ImageButton : public Image {
bool on_touch(const TouchEvent event) override;
};
/* A button that toggles between two images when set. */
class ImageToggle : public ImageButton {
public:
std::function<void(bool value)> on_change{};
ImageToggle(
Rect parent_rect,
const Bitmap* bitmap_);
ImageToggle(
Rect parent_rect,
const Bitmap* bitmap_,
Color foreground_true,
Color foreground_false,
Color background_);
ImageToggle(
Rect parent_rect,
const Bitmap* bitmap_true,
const Bitmap* bitmap_false,
Color foreground_true,
Color background_true,
Color foreground_false,
Color background_false);
ImageToggle(const ImageToggle&) = delete;
ImageToggle& operator=(const ImageToggle&) = delete;
bool value() const;
void set_value(bool b);
private:
// Hide this field.
using ImageButton::on_select;
const Bitmap* bitmap_true_;
const Bitmap* bitmap_false_;
const Color foreground_true_;
const Color background_true_;
const Color foreground_false_;
const Color background_false_;
bool value_;
};
class ImageOptionsField : public Widget {
public:
using value_t = int32_t;
+3
View File
@@ -345,6 +345,9 @@ class WM8731 : public audio::Codec {
void speaker_enable(){};
void speaker_disable(){};
bool speaker_disable_supported() const override {
return false;
}
void microphone_enable(int8_t wm8731_boost_GUI) override {
microphone_mute(true); // c/m to reduce "plop noise" when changing wm8731_boost_GUI.
Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

+3 -10
View File
@@ -48,7 +48,6 @@ def convert_png(file):
name = path.basename(file).split(".")[0].lower();
f.write("static constexpr uint8_t bitmap_" + name + "_data[] = {\n")
f.write(' ') # Tab
for i in range(rgb_im.size[1]):
for j in range(rgb_im.size[0]):
@@ -60,17 +59,12 @@ def convert_png(file):
data += 128
if j % 8 == 7:
f.write("0x%0.2X, " % data)
f.write(" 0x%0.2X,\n" % data)
data = 0
f.write("\n")
if i < rgb_im.size[1] - 1:
f.write(' ') # Tab
f.write("};\n")
f.write("static constexpr Bitmap bitmap_" + name + " {\n")
f.write(" { " + str(rgb_im.size[0]) + ", " + str(rgb_im.size[1]) + " }, bitmap_" + name+ "_data\n")
f.write("};\n\n")
f.write("static constexpr Bitmap bitmap_" + name + "{\n")
f.write(" {" + str(rgb_im.size[0]) + ", " + str(rgb_im.size[1]) + "},\n bitmap_" + name+ "_data};\n\n")
return
count = 0
@@ -108,7 +102,6 @@ for file in listdir(sys.argv[1]):
convert_png(sys.argv[1] + file)
count += 1
f.write("\n")
f.write("} /* namespace ui */\n\n")
f.write("#endif/*__BITMAP_HPP__*/\n")
+6
View File
@@ -0,0 +1,6 @@
f=310000000,m=SPEC,bw=650k
f=315000000,m=SPEC,bw=650k
f=318000000,m=SPEC,bw=650k
f=418000000,m=SPEC,bw=650k
f=433920000,m=SPEC,bw=650k
f=868350000,m=SPEC,bw=650k
+55
View File
@@ -0,0 +1,55 @@
f=300000000,m=SPEC,bw=650k,d=300 - 348 01
f=302757000,m=SPEC,bw=650k,d=300 - 348 02
f=303875000,m=SPEC,bw=650k,d=300 - 348 03
f=304250000,m=SPEC,bw=650k,d=300 - 348 04
f=307000000,m=SPEC,bw=650k,d=300 - 348 05
f=307500000,m=SPEC,bw=650k,d=300 - 348 06
f=307800000,m=SPEC,bw=650k,d=300 - 348 07
f=309000000,m=SPEC,bw=650k,d=300 - 348 08
f=310000000,m=SPEC,bw=650k,d=300 - 348 09
f=312000000,m=SPEC,bw=650k,d=300 - 348 10
f=312100000,m=SPEC,bw=650k,d=300 - 348 11
f=312200000,m=SPEC,bw=650k,d=300 - 348 12
f=313000000,m=SPEC,bw=650k,d=300 - 348 13
f=313850000,m=SPEC,bw=650k,d=300 - 348 14
f=314000000,m=SPEC,bw=650k,d=300 - 348 15
f=314350000,m=SPEC,bw=650k,d=300 - 348 16
f=314980000,m=SPEC,bw=650k,d=300 - 348 17
f=315000000,m=SPEC,bw=650k,d=300 - 348 18
f=318000000,m=SPEC,bw=650k,d=300 - 348 19
f=330000000,m=SPEC,bw=650k,d=300 - 348 20
f=345000000,m=SPEC,bw=650k,d=300 - 348 21
f=348000000,m=SPEC,bw=650k,d=300 - 348 22
f=350000000,m=SPEC,bw=650k,d=300 - 348 23
f=387000000,m=SPEC,bw=650k,d=387 - 464 24
f=390000000,m=SPEC,bw=650k,d=387 - 464 25
f=418000000,m=SPEC,bw=650k,d=387 - 464 26
f=430000000,m=SPEC,bw=650k,d=387 - 464 27
f=431000000,m=SPEC,bw=650k,d=387 - 464 28
f=431500000,m=SPEC,bw=650k,d=387 - 464 29
f=433075000,m=SPEC,bw=650k,d=LPD433 01 FIRST
f=433220000,m=SPEC,bw=650k,d=LPD433 02
f=433420000,m=SPEC,bw=650k,d=LPD433 03
f=433657070,m=SPEC,bw=650k,d=LPD433 04
f=433889000,m=SPEC,bw=650k,d=LPD433 05
f=433920000,m=SPEC,bw=650k,d=LPD433 06 MIDDLE
f=434075000,m=SPEC,bw=650k,d=LPD433 07
f=434176948,m=SPEC,bw=650k,d=LPD433 08
f=434190000,m=SPEC,bw=650k,d=LPD433 09
f=434390000,m=SPEC,bw=650k,d=LPD433 10
f=434420000,m=SPEC,bw=650k,d=LPD433 11
f=434620000,m=SPEC,bw=650k,d=LPD433 12
f=434775000,m=SPEC,bw=650k,d=LPD433 13 LAST CHANS
f=438900000,m=SPEC,bw=650k,d=LPD433 14 LAST CHANS
f=440175000,m=SPEC,bw=650k,d=LPD433 15 LAST CHANS
f=464000000,m=SPEC,bw=650k,d=LPD433 16 LAST CHANS
f=467750000,m=SPEC,bw=650k,d=LPD433 17 LAST CHANS
f=779000000,m=SPEC,bw=650k,d=779 - 928 01
f=868350000,m=SPEC,bw=650k,d=779 - 928 02
f=868400000,m=SPEC,bw=650k,d=779 - 928 03
f=868800000,m=SPEC,bw=650k,d=779 - 928 04
f=868950000,m=SPEC,bw=650k,d=779 - 928 05
f=906400000,m=SPEC,bw=650k,d=779 - 928 06
f=915000000,m=SPEC,bw=650k,d=779 - 928 07
f=925000000,m=SPEC,bw=650k,d=779 - 928 08
f=928000000,m=SPEC,bw=650k,d=779 - 928 09
-2
View File
@@ -1,2 +0,0 @@
315000000,SAMPLES/TeslaChargePort_US.C16,500000,0
433920000,SAMPLES/TeslaChargePort_EU_AU.C16,500000,10
+4
View File
@@ -0,0 +1,4 @@
# Playlist file example
# capture path, pre-delay milliseconds (optional)
/SAMPLES/TeslaChargePort_US.C16
/SAMPLES/TeslaChargePort_EU_AU.C16,100