Compare commits

..

17 Commits

Author SHA1 Message Date
sommermorgentraum
9aff3f4121
Fix bug that created by PR "Added menu group for transceivers" (#2630) 2025-04-17 07:42:05 +02:00
sommermorgentraum
6b6a00d511
Support IPS screen & brightness set for IPS screen (#2629)
* _

* format

* format

* format
2025-04-17 07:40:28 +02:00
Oleg Belousov
806219f46e
Added menu group for transceivers (#2623)
* Added menu group for transceivers

* Reorder apps icons
2025-04-15 16:46:49 +08:00
Luca
a153cbf30b
Adding 8.33KHz spacing (#2628)
* Adding 8.33KHz spacing
* remove extern options_db_t freqman_steps which is now brought by ui_receiver.hpp
* use freqman db steps instead of static array
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-04-14 18:59:50 +00:00
sommermorgentraum
584af02dba
Touch on waterfall to set cursor pos (#2624)
* init

* fix typo that found by Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-14 20:47:41 +02:00
gullradriel
f344a1ca40
fix comments (#2622) 2025-04-11 11:48:07 +00:00
gullradriel
511675c9a1
Fix default waterfall file (#2621)
* correct default settings from file
* correct colors names
2025-04-11 19:30:18 +08:00
haruk
7bbd608c90
Delete dead code in ble_rx_app.cpp (#2620)
Duplicate include on line 24 removed.
2025-04-11 10:49:09 +00:00
Oleg Belousov
f941ae871a
Custom waterfall colors (#2617)
* Custom waterfall gradient
* Installing a custom waterfall gradient via fileman
* default file for user friendly swap
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-04-11 10:46:04 +00:00
RocketGod
eff214cc79
Enhance Graphic Equalizer Visualization with Improved Frequency Bands and Response (#2614) 2025-04-10 22:16:18 +02:00
Oleg Belousov
319ed3e7b4
Improved make_bitmap tool (#2615) 2025-04-10 12:21:19 +08:00
RocketGod
288f6bd517
GFX EQ App (#2607)
* Make the beginnings of rf3d
* Name change...
* Add mood button
* Remove forced amp settings and add persistent user settings
* Fix options bar layout and SettingsManager
* Make the background paint to black again after opening fq modal
* fix audio/mod/settings and cleaned unneeded parts
* Mapped bars to audio spectrum
* Improved frequency response... still needs work i think
* add on_freqchg to be able to answer to serial frequency change command
* Made calculations for 14 bars to fit screen and little adjustments
* Visual improvements
Co-authored-by: gullradriel
2025-04-09 14:27:51 +02:00
sommermorgentraum
809abb6842
user can disable battery change hint (#2612)
* theme fix again

* _

* _
2025-04-06 10:24:50 +08:00
sommermorgentraum
31082c63af
theme fix again (#2611)
* theme fix again

* _
2025-04-05 16:58:12 +08:00
gullradriel
ff14008b43
prevent long life var for audio app - AM (#2610)
* static vars so no external linkage is possible

* persistent settings and no more global living variables

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-04-04 21:43:26 +08:00
sommermorgentraum
1377516dce
make the ptext_prompt func can define which keyboard to enter (#2608)
* _

* format

* use define
2025-04-04 15:41:12 +02:00
Brumi-2021
4162820409
solving_Audio_App_AM_GUI_Problem_issue_2604 (#2609) 2025-04-04 14:04:06 +02:00
72 changed files with 6158 additions and 5605 deletions

View File

@ -191,6 +191,7 @@ set(CPPSRC
clock_manager.cpp
core_control.cpp
database.cpp
gradient.cpp
rfm69.cpp
event_m0.cpp
file_reader.cpp

View File

@ -52,20 +52,20 @@ AMOptionsView::AMOptionsView(
&zoom_config,
});
zoom_config.on_change = [this, view](size_t, OptionsField::value_t n) {
receiver_model.set_am_configuration(previous_filter_array_index + n);
zoom_config.on_change = [this, view](size_t, OptionsField::value_t n) { // n , has two option values. when GUI =zoom+1 => (0), when GUI=zoom+2 (6)
receiver_model.set_am_configuration(view->get_previous_AM_mode_option() + n); // n (0 or 6)
view->set_zoom_factor(AM_MODULATION, n);
view->set_previous_zoom_option(n);
};
// restore zoom selection
zoom_config.set_by_value(view->get_zoom_factor(AM_MODULATION));
freqman_set_bandwidth_option(AM_MODULATION, options_config); // adding the common message from freqman.cpp to the options_config
options_config.set_by_value(receiver_model.am_configuration());
freqman_set_bandwidth_option(AM_MODULATION, options_config); // freqman.cpp to the options_config, only allowing 5 modes freqman_bandwidths[AM] {"DSB 9k", 0}, {"DSB 6k", 1}, {"USB+3k", 2}, {"LSB-3k", 3}, {"CW", 4},
options_config.set_by_value(receiver_model.am_configuration() - view->get_previous_zoom_option()); // restore AM GUI option mode , AM FIR index filters (0..11) values , <baseband::AMConfig, 12> am_configs has 12 fir index elements.
options_config.on_change = [this, view](size_t, OptionsField::value_t n) {
receiver_model.set_am_configuration(n);
previous_filter_array_index = n;
zoom_config.set_by_value(view->get_zoom_factor(AM_MODULATION));
receiver_model.set_am_configuration(n + view->get_previous_zoom_option()); // we select proper FIR AM filter (0..11), = 0..4 GUI AM modes + offset +6 (if zoom+2)
view->set_previous_AM_mode_option(n); // (0..4) allowing 5 AM modes (DSB9K, DSB6K, USB,LSB, CW)
};
}
@ -282,6 +282,22 @@ void AnalogAudioView::set_zoom_factor(uint8_t mode, uint8_t zoom) { // define a
zoom_factor_amfm = zoom;
}
uint8_t AnalogAudioView::get_previous_AM_mode_option() {
return previous_AM_mode_option;
}
void AnalogAudioView::set_previous_AM_mode_option(uint8_t mode) {
previous_AM_mode_option = mode;
}
uint8_t AnalogAudioView::get_previous_zoom_option() {
return previous_zoom;
}
void AnalogAudioView::set_previous_zoom_option(uint8_t zoom) {
previous_zoom = zoom;
}
uint8_t AnalogAudioView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read iq_phase_calibration_value
return iq_phase_calibration_value;
}

View File

@ -40,7 +40,6 @@ class AnalogAudioView;
class AMOptionsView : public View {
public:
AMOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
int16_t previous_filter_array_index = 0;
private:
Text label_config{
@ -59,7 +58,7 @@ class AMOptionsView : public View {
{23 * 8, 0 * 16},
7,
{{"ZOOM x1", 0},
{"ZOOM x2", 6}} // offset index array filters.
{"ZOOM x2", 6}} // offset index AM modes array FIR filters.
};
};
@ -200,6 +199,12 @@ class AnalogAudioView : public View {
uint8_t get_zoom_factor(uint8_t mode);
void set_zoom_factor(uint8_t mode, uint8_t zoom);
uint8_t get_previous_AM_mode_option();
void set_previous_AM_mode_option(uint8_t mode);
uint8_t get_previous_zoom_option();
void set_previous_zoom_option(uint8_t zoom);
private:
static constexpr ui::Dim header_height = 3 * 16;
@ -208,6 +213,9 @@ class AnalogAudioView : public View {
uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839
uint8_t zoom_factor_am{0}; // initial zoom factor in AM mode
uint8_t zoom_factor_amfm{0}; // initial zoom factor in AMFM mode
uint8_t previous_AM_mode_option{0}; // GUI 5 AM modes : (0..4 ) (DSB9K, DSB6K, USB,LSB, CW). Used to select proper FIR filter (0..11) AM mode + offset 0 (zoom+1) or +6 (if zoom+2)
uint8_t previous_zoom{0}; // GUI ZOOM+1, ZOOM+2 , equivalent to two values offset 0 (zoom+1) or +6 (if zoom+2)
//
app_settings::SettingsManager settings_{
"rx_audio",
app_settings::Mode::RX,
@ -215,6 +223,8 @@ class AnalogAudioView : public View {
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
{"zoom_factor_am"sv, &zoom_factor_am}, // we are saving and restoring AM ZOOM factor from Settings.
{"zoom_factor_amfm"sv, &zoom_factor_amfm}, // we are saving and restoring AMFM ZOOM factor from Settings.
{"previous_AM_mode_option"sv, &previous_AM_mode_option}, // we are saving and restoring AMFM ZOOM factor from Settings.
{"previous_zoom"sv, &previous_zoom}, // we are saving and restoring AMFM ZOOM factor from Settings.
}};
const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16};

View File

@ -21,7 +21,6 @@
* Boston, MA 02110-1301, USA.
*/
#include "ble_rx_app.hpp"
#include "ble_rx_app.hpp"
#include "ui_modemsetup.hpp"
@ -203,6 +202,7 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
nav,
packetFileBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this, packetToSave](std::string& buffer) {
on_save_file(buffer, packetToSave);
});
@ -471,6 +471,7 @@ BLERxView::BLERxView(NavigationView& nav)
nav_,
filterBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
on_filter_change(buffer);
});
@ -493,6 +494,7 @@ BLERxView::BLERxView(NavigationView& nav)
nav,
listFileBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
on_save_file(buffer);
});

View File

@ -399,6 +399,7 @@ BLETxView::BLETxView(NavigationView& nav)
nav,
packetFileBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
on_save_file(buffer);
});

View File

@ -94,6 +94,7 @@ APRSTXView::APRSTXView(NavigationView& nav) {
nav,
payload,
30,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& s) {
text_payload.set(s);
});

View File

@ -92,6 +92,9 @@ void ExternalModuleView::on_tick_second() {
case app_location_t::TX:
btnText += " (TX)";
break;
case app_location_t::TRX:
btnText += " (TRX)";
break;
case app_location_t::SETTINGS:
btnText += " (Settings)";
break;

View File

@ -419,6 +419,17 @@ void FileManBaseView::reload_current(bool reset_pagination) {
refresh_list();
}
void FileManBaseView::copy_waterfall(std::filesystem::path path) {
nav_.push<ModalMessageView>(
"Install", " Use this gradient file\n for all waterfalls?", YESNO,
[this, path](bool choice) {
if (choice) {
delete_file(default_gradient_file);
copy_file(path, default_gradient_file);
}
});
}
const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
const fs::path& ext) const {
size_t index = 0;
@ -499,7 +510,7 @@ FileSaveView::FileSaveView(
button_edit_path.on_select = [this](Button&) {
buffer_ = path_.string();
text_prompt(nav_, buffer_, max_filename_length,
text_prompt(nav_, buffer_, max_filename_length,ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string&) {
path_ = buffer_;
refresh_widgets();
@ -508,7 +519,7 @@ FileSaveView::FileSaveView(
button_edit_name.on_select = [this](Button&) {
buffer_ = file_.string();
text_prompt(nav_, buffer_, max_filename_length,
text_prompt(nav_, buffer_, max_filename_length,ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string&) {
file_ = buffer_;
refresh_widgets();
@ -566,7 +577,7 @@ void FileManagerView::on_rename(std::string_view hint) {
cursor_pos = pos;
text_prompt(
nav_, name_buffer, cursor_pos, max_filename_length,
nav_, name_buffer, cursor_pos, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& renamed) {
auto renamed_path = fs::path{renamed};
rename_file(get_selected_full_path(), current_path / renamed_path);
@ -640,7 +651,7 @@ void FileManagerView::on_clean() {
void FileManagerView::on_new_dir() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) {
text_prompt(nav_, name_buffer, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& dir_name) {
make_new_directory(current_path / dir_name);
reload_current(true);
});
@ -671,7 +682,7 @@ void FileManagerView::on_paste() {
void FileManagerView::on_new_file() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) {
text_prompt(nav_, name_buffer, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& file_name) {
make_new_file(current_path / file_name);
reload_current(true);
});
@ -685,7 +696,11 @@ bool FileManagerView::handle_file_open() {
auto ext = path.extension();
if (path_iequal(txt_ext, ext)) {
nav_.push<TextEditorView>(path);
if (path_iequal(current_path, u"/" + waterfalls_dir)) {
copy_waterfall(path);
} else {
nav_.push<TextEditorView>(path);
}
return true;
} else if (is_cxx_capture_file(path) || path_iequal(ppl_ext, ext)) {
// TODO: Enough memory to push?

View File

@ -96,6 +96,7 @@ class FileManBaseView : public View {
void load_directory_contents(const std::filesystem::path& dir_path);
void load_directory_contents_unordered(const std::filesystem::path& dir_path, size_t file_cnt);
const file_assoc_t& get_assoc(const std::filesystem::path& ext) const;
void copy_waterfall(std::filesystem::path path);
NavigationView& nav_;

View File

@ -46,7 +46,7 @@ using options_db_t = std::vector<option_db_t>;
extern options_db_t freqman_modulations;
extern options_db_t freqman_bandwidths[5];
extern options_db_t freqman_steps;
// extern options_db_t freqman_steps; // now included via ui_receiver.hpp
extern options_db_t freqman_steps_short;
options_t dboptions_to_options(const options_db_t& dboptions) {
@ -240,7 +240,7 @@ void FrequencyManagerView::on_edit_freq() {
void FrequencyManagerView::on_edit_desc() {
temp_buffer_ = current_entry().description;
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, [this](std::string& new_desc) {
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_desc) {
auto entry = current_entry();
entry.description = std::move(new_desc);
db_.replace_entry(current_index(), entry);
@ -250,7 +250,7 @@ void FrequencyManagerView::on_edit_desc() {
void FrequencyManagerView::on_add_category() {
temp_buffer_.clear();
text_prompt(nav_, temp_buffer_, 20, [this](std::string& new_name) {
text_prompt(nav_, temp_buffer_, 20, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_name) {
if (!new_name.empty()) {
create_freqman_file(new_name);
refresh_categories();

View File

@ -126,7 +126,7 @@ void GlassView::reset_live_view() {
}
void GlassView::add_spectrum_pixel(uint8_t power) {
spectrum_row[pixel_index] = spectrum_rgb3_lut[power]; // row of colors
spectrum_row[pixel_index] = gradient.lut[power]; // row of colors
spectrum_data[pixel_index] = (live_frequency_integrate * spectrum_data[pixel_index] + power) / (live_frequency_integrate + 1); // smoothing
pixel_index++;
@ -359,6 +359,10 @@ GlassView::GlassView(
: nav_(nav) {
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
if (!gradient.load_file(default_gradient_file)) {
gradient.set_default();
}
add_children({&labels,
&field_frequency_min,
&field_frequency_max,

View File

@ -35,7 +35,7 @@
#include "ui_receiver.hpp"
#include "string_format.hpp"
#include "analog_audio_app.hpp"
#include "spectrum_color_lut.hpp"
#include "gradient.hpp"
namespace ui {
@ -74,6 +74,7 @@ class GlassView : public View {
private:
NavigationView& nav_;
Gradient gradient{};
RxRadioState radio_state_{ReceiverModel::Mode::SpectrumAnalysis};
// Settings
rf::Frequency f_min = 260 * MHZ_DIV; // Default to 315/433 remote range.

View File

@ -153,7 +153,7 @@ void POCSAGTXView::paint(Painter&) {
}
void POCSAGTXView::on_set_text(NavigationView& nav) {
text_prompt(nav, buffer, MAX_POCSAG_LENGTH);
text_prompt(nav, buffer, MAX_POCSAG_LENGTH, ENTER_KEYBOARD_MODE_ALPHA);
}
POCSAGTXView::POCSAGTXView(

View File

@ -65,6 +65,7 @@ RDSPSNView::RDSPSNView(
nav,
PSN,
8,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& s) {
text_psn.set(s);
});
@ -86,6 +87,7 @@ RDSRadioTextView::RDSRadioTextView(
nav,
radiotext,
28,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& s) {
text_radiotext.set(s);
});

View File

@ -82,7 +82,7 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
};
button_choose_output_name.on_select = [this, &nav](Button&) {
text_prompt(nav, _output_file, 28, [this](std::string& buffer) {
text_prompt(nav, _output_file, 28, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& buffer) {
_output_file = buffer;
button_choose_output_name.set_text(_output_file);
});

View File

@ -56,6 +56,10 @@ SearchView::SearchView(
: nav_(nav) {
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
if (!gradient.load_file(default_gradient_file)) {
gradient.set_default();
}
add_children({&labels,
&field_frequency_min,
&field_frequency_max,
@ -290,7 +294,7 @@ void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
power = spectrum.db[bin - 128];
}
add_spectrum_pixel(spectrum_rgb3_lut[power]);
add_spectrum_pixel(gradient.lut[power]);
mean_acc += power;
if (power > max_power) {

View File

@ -24,7 +24,7 @@
#include "receiver_model.hpp"
#include "recent_entries.hpp"
#include "radio_state.hpp"
#include "spectrum_color_lut.hpp"
#include "gradient.hpp"
#include "ui_receiver.hpp"
namespace ui {
@ -88,6 +88,7 @@ class SearchView : public View {
private:
NavigationView& nav_;
Gradient gradient{};
RxRadioState radio_state_{
100'000'000 /* frequency */,
2500000 /* bandwidth */,

View File

@ -810,30 +810,29 @@ SetDisplayView::SetDisplayView(NavigationView& nav) {
&field_fake_brightness,
&button_save,
&button_cancel,
&checkbox_invert_switch,
&checkbox_ips_screen_switch,
&checkbox_brightness_switch});
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
checkbox_ips_screen_switch.set_value(pmem::config_lcd_normally_black());
button_save.on_select = [&nav, this](Button&) {
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
if (checkbox_invert_switch.value() != pmem::config_lcd_inverted_mode()) {
display.set_inverted(checkbox_invert_switch.value());
pmem::set_lcd_inverted_mode(checkbox_invert_switch.value());
if (checkbox_ips_screen_switch.value() != pmem::config_lcd_normally_black()) {
pmem::set_lcd_normally_black(checkbox_ips_screen_switch.value());
}
send_system_refresh();
nav.pop();
};
// only enable invert OR fake brightness
checkbox_invert_switch.on_select = [this](Checkbox&, bool v) {
checkbox_ips_screen_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_brightness_switch.set_value(false);
};
checkbox_brightness_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_invert_switch.set_value(false);
if (v) checkbox_ips_screen_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {
@ -1044,12 +1043,14 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&checkbox_overridebatt});
&checkbox_overridebatt,
&checkbox_battery_charge_hint});
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) add_children({&button_reset, &labels2});
button_save.on_select = [&nav, this](Button&) {
pmem::set_ui_override_batt_calc(checkbox_overridebatt.value());
pmem::set_ui_battery_charge_hint(checkbox_battery_charge_hint.value());
battery::BatteryManagement::set_calc_override(checkbox_overridebatt.value());
send_system_refresh();
nav.pop();
@ -1064,6 +1065,7 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
};
checkbox_overridebatt.set_value(pmem::ui_override_batt_calc());
checkbox_battery_charge_hint.set_value(pmem::ui_battery_charge_hint());
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();

View File

@ -762,6 +762,8 @@ class SetDisplayView : public View {
{{1 * 8, 2 * 16}, "(has a small performance", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "impact when enabled).", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 8 * 16}, "Brightness:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 16}, "REBOOT TO APPLY SCREEN TYPE", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_fake_brightness{
@ -776,10 +778,10 @@ class SetDisplayView : public View {
16,
"Enable brightness adjust"};
Checkbox checkbox_invert_switch{
{1 * 8, 10 * 16},
Checkbox checkbox_ips_screen_switch{
{1 * 8, 12 * 16},
23,
"Invert colors (For IPS)"};
"IPS Screen"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
@ -978,9 +980,13 @@ class SetBatteryView : public View {
int32_t selected = 0;
Labels labels{
{{1 * 8, 1 * 16}, "Override batt calculation", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "method to voltage based", Theme::getInstance()->fg_light->foreground}};
Labels labels2{
{{1 * 8, 6 * 16}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}};
{{1 * 8, 2 * 16}, "method to voltage based", Theme::getInstance()->fg_light->foreground},
/**/
{{1 * 8, 6 * 16}, "Display a hint to remind you", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 7 * 16}, "when you charge", Theme::getInstance()->fg_light->foreground}};
Labels labels2{{{1 * 8, 11 * 16}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@ -990,13 +996,18 @@ class SetBatteryView : public View {
23,
"Override"};
Checkbox checkbox_battery_charge_hint{
{2 * 8, 9 * 16},
23,
"Charge hint"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
Button button_reset{
{2 * 8, 8 * 16, 12 * 8, 32},
{2 * 8, 13 * 16, 12 * 8, 32},
"Reset",
};
};

View File

@ -617,6 +617,7 @@ void TextEditorView::show_edit_line() {
edit_line_buffer_,
viewer.col(),
max_edit_length,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
auto range = file_->line_range(viewer.line());
if (!range)

View File

@ -25,6 +25,7 @@
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_widget.hpp"
#include "ui_textentry.hpp"
namespace ui {
@ -105,7 +106,7 @@ void bind(TextField& field, T& value, NavigationView& nav, Fn fn = Fn{}) {
// Capture a new string and make the lambda mutable so it can be modified.
field.on_select = [&nav, buf = std::string{}](TextField& tf) mutable {
buf = tf.get_text();
text_prompt(nav, buf, /*max_length*/ 255,
text_prompt(nav, buf, /*max_length*/ 255, ENTER_KEYBOARD_MODE_ALPHA,
[&tf](std::string& str) {
tf.set_text(str);
});

File diff suppressed because it is too large Load Diff

View File

@ -131,6 +131,7 @@ ADSBCallsignView::ADSBCallsignView(
nav,
callsign,
8,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& s) {
button_callsign.set_text(s);
});

View File

@ -80,7 +80,7 @@ bool DebugDumpView::debug_dump_func() {
pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(config_updown_converter()));
pmem_dump_file.write_line("updown_frequency_rx_correction: " + to_string_dec_int(config_freq_rx_correction_updown()));
pmem_dump_file.write_line("updown_frequency_tx_correction: " + to_string_dec_int(config_freq_tx_correction_updown()));
pmem_dump_file.write_line("lcd_inverted_mode: " + to_string_dec_uint(config_lcd_inverted_mode()));
pmem_dump_file.write_line("lcd_normally_black: " + to_string_dec_uint(config_lcd_normally_black()));
pmem_dump_file.write_line("converter_frequency_offset: " + to_string_dec_int(config_converter_freq()));
pmem_dump_file.write_line("frequency_rx_correction: " + to_string_dec_uint(config_freq_rx_correction()));
pmem_dump_file.write_line("frequency_tx_correction: " + to_string_dec_uint(config_freq_tx_correction()));
@ -128,6 +128,7 @@ bool DebugDumpView::debug_dump_func() {
pmem_dump_file.write_line("ui_config2 button_repeat_delay: " + to_string_dec_uint(ui_button_repeat_delay()));
pmem_dump_file.write_line("ui_config2 button_repeat_speed: " + to_string_dec_uint(ui_button_repeat_speed()));
pmem_dump_file.write_line("ui_config2 button_long_press_delay: " + to_string_dec_uint(ui_button_long_press_delay()));
pmem_dump_file.write_line("ui_config2 battery_charge_hint: " + to_string_dec_uint(ui_battery_charge_hint()));
// misc_config bits
pmem_dump_file.write_line("misc_config config_audio_mute: " + to_string_dec_int(config_audio_mute()));

View File

@ -203,6 +203,10 @@ set(EXTCPPSRC
#level
external/level/main.cpp
external/level/ui_level.cpp
#gfxEQ
external/gfxeq/main.cpp
external/gfxeq/ui_gfxeq.cpp
)
set(EXTAPPLIST
@ -255,4 +259,5 @@ set(EXTAPPLIST
debug_pmem
scanner
level
gfxeq
)

View File

@ -72,6 +72,7 @@ MEMORY
ram_external_app_debug_pmem (rwx) : org = 0xADDF0000, len = 32k
ram_external_app_scanner (rwx) : org = 0xADE00000, len = 32k
ram_external_app_level (rwx) : org = 0xADE10000, len = 32k
ram_external_app_gfxeq (rwx) : org = 0xADE20000, len = 32k
}
SECTIONS
@ -368,4 +369,10 @@ SECTIONS
KEEP(*(.external_app.app_level.application_information));
*(*ui*external_app*level*);
} > ram_external_app_level
.external_app_gfxeq : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_gfxeq.application_information));
*(*ui*external_app*gfxeq*);
} > ram_external_app_gfxeq
}

View File

@ -0,0 +1,36 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#include "ui_gfxeq.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::gfxeq {
void initialize_app(ui::NavigationView& nav) {
nav.push<gfxEQView>();
}
} // namespace ui::external_app::gfxeq
extern "C" {
__attribute__((section(".external_app.app_gfxeq.application_information"), used)) application_information_t _application_information_gfxeq = {
(uint8_t*)0x00000000,
ui::external_app::gfxeq::initialize_app,
CURRENT_HEADER_VERSION,
VERSION_MD5,
"gfxEQ",
{0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
ui::Color::green().v,
app_location_t::RX,
-1,
{'P', 'N', 'F', 'M'},
0x00000000,
};
} // namespace ui::external_app::gfxeq

View File

@ -0,0 +1,196 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#include "ui_gfxeq.hpp"
#include "ui.hpp"
#include "ui_freqman.hpp"
#include "tone_key.hpp"
#include "analog_audio_app.hpp"
#include "portapack.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "dsp_fir_taps.hpp"
using namespace portapack;
namespace ui::external_app::gfxeq {
gfxEQView::gfxEQView(NavigationView& nav)
: nav_{nav}, bar_heights(NUM_BARS, 0), prev_bar_heights(NUM_BARS, 0) {
add_children({&button_frequency, &field_rf_amp, &field_lna, &field_vga,
&button_mood, &field_volume});
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.set_wfm_configuration(1); // 200k => 0 , 180k => 1 , 40k => 2. Set to 1 or 2 for better reception
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
audio::set_rate(audio::Rate::Hz_48000);
audio::output::start();
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack
//
receiver_model.enable();
receiver_model.set_target_frequency(frequency_value); // Retune to actual freq
button_frequency.set_text("<" + to_string_short_freq(frequency_value) + ">");
button_frequency.on_select = [this, &nav](ButtonWithEncoder& button) {
auto new_view = nav_.push<FrequencyKeypadView>(frequency_value);
new_view->on_changed = [this, &button](rf::Frequency f) {
frequency_value = f;
receiver_model.set_target_frequency(f); // Retune to actual freq
button_frequency.set_text("<" + to_string_short_freq(frequency_value) + ">");
};
};
button_frequency.on_change = [this]() {
int64_t def_step = 25000;
frequency_value = frequency_value + (button_frequency.get_encoder_delta() * def_step);
if (frequency_value < 1) {
frequency_value = 1;
}
if (frequency_value > (MAX_UFREQ - def_step)) {
frequency_value = MAX_UFREQ;
}
button_frequency.set_encoder_delta(0);
receiver_model.set_target_frequency(frequency_value); // Retune to actual freq
button_frequency.set_text("<" + to_string_short_freq(frequency_value) + ">");
};
button_mood.on_select = [this](Button&) { this->cycle_theme(); };
}
// needed to answer usb serial frequency set
void gfxEQView::on_freqchg(int64_t freq) {
receiver_model.set_target_frequency(freq); // Retune to actual freq
button_frequency.set_text("<" + to_string_short_freq(freq) + ">");
}
gfxEQView::~gfxEQView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
void gfxEQView::focus() {
button_frequency.focus();
}
void gfxEQView::on_show() {
needs_background_redraw = true;
}
void gfxEQView::on_hide() {
needs_background_redraw = true;
}
void gfxEQView::update_audio_spectrum(const AudioSpectrum& spectrum) {
const float bin_frequency_size = 48000.0f / 128;
for (int bar = 0; bar < NUM_BARS; bar++) {
float start_freq = FREQUENCY_BANDS[bar];
float end_freq = FREQUENCY_BANDS[bar + 1];
int start_bin = std::max(1, (int)(start_freq / bin_frequency_size));
int end_bin = std::min(127, (int)(end_freq / bin_frequency_size));
if (start_bin >= end_bin) {
end_bin = start_bin + 1;
}
float total_energy = 0;
int bin_count = 0;
for (int bin = start_bin; bin <= end_bin; bin++) {
total_energy += spectrum.db[bin];
bin_count++;
}
float avg_db = bin_count > 0 ? (total_energy / bin_count) : 0;
// Manually boost highs for better visual balance
float treble_boost = 1.0f;
if (bar == 10)
treble_boost = 1.7f;
else if (bar >= 9)
treble_boost = 1.3f;
else if (bar >= 7)
treble_boost = 1.3f;
// Mid emphasis for a V-shape effect
float mid_boost = 1.0f;
if (bar == 4 || bar == 5 || bar == 6) mid_boost = 1.2f;
float amplified_db = avg_db * treble_boost * mid_boost;
if (amplified_db > 255) amplified_db = 255;
float band_scale = 1.0f;
int target_height = (amplified_db * RENDER_HEIGHT * band_scale) / 255;
if (target_height > RENDER_HEIGHT) {
target_height = RENDER_HEIGHT;
}
// Adjusted to look nice to my eyes
float rise_speed = 0.8f;
float fall_speed = 1.0f;
if (target_height > bar_heights[bar]) {
bar_heights[bar] = bar_heights[bar] * (1.0f - rise_speed) + target_height * rise_speed;
} else {
bar_heights[bar] = bar_heights[bar] * (1.0f - fall_speed) + target_height * fall_speed;
}
}
}
void gfxEQView::render_equalizer(Painter& painter) {
const int num_segments = RENDER_HEIGHT / SEGMENT_HEIGHT;
const ColorTheme& theme = themes[current_theme];
for (int bar = 0; bar < NUM_BARS; bar++) {
int x = HORIZONTAL_OFFSET + bar * (BAR_WIDTH + BAR_SPACING);
int active_segments = (bar_heights[bar] * num_segments) / RENDER_HEIGHT;
if (prev_bar_heights[bar] > active_segments) {
int clear_height = (prev_bar_heights[bar] - active_segments) * SEGMENT_HEIGHT;
int clear_y = SCREEN_HEIGHT - prev_bar_heights[bar] * SEGMENT_HEIGHT;
painter.fill_rectangle({x, clear_y, BAR_WIDTH, clear_height}, Color(0, 0, 0));
}
for (int seg = 0; seg < active_segments; seg++) {
int y = SCREEN_HEIGHT - (seg + 1) * SEGMENT_HEIGHT;
if (y < header_height) break;
Color segment_color = (seg >= active_segments - 2 && seg < active_segments) ? theme.peak_color : theme.base_color;
painter.fill_rectangle({x, y, BAR_WIDTH, SEGMENT_HEIGHT - 1}, segment_color);
}
prev_bar_heights[bar] = active_segments;
}
}
void gfxEQView::paint(Painter& painter) {
if (needs_background_redraw) {
painter.fill_rectangle({0, header_height, SCREEN_WIDTH, RENDER_HEIGHT}, Color(0, 0, 0));
needs_background_redraw = false;
}
render_equalizer(painter);
}
void gfxEQView::cycle_theme() {
current_theme = (current_theme + 1) % themes.size();
}
} // namespace ui::external_app::gfxeq

View File

@ -0,0 +1,138 @@
/*
* ------------------------------------------------------------
* | Made by RocketGod |
* | Find me at https://betaskynet.com |
* | Argh matey! |
* ------------------------------------------------------------
*/
#ifndef __UI_GFXEQ_HPP__
#define __UI_GFXEQ_HPP__
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "message.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "ui_spectrum.hpp"
#include "ui_freq_field.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
namespace ui::external_app::gfxeq {
class gfxEQView : public View {
public:
gfxEQView(NavigationView& nav);
~gfxEQView();
gfxEQView(const gfxEQView&) = delete;
gfxEQView& operator=(const gfxEQView&) = delete;
void focus() override;
std::string title() const override { return "gfxEQ"; }
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
void on_freqchg(int64_t freq);
private:
static constexpr ui::Dim header_height = 2 * 16;
static constexpr int SCREEN_WIDTH = 240;
static constexpr int SCREEN_HEIGHT = 320;
static constexpr int RENDER_HEIGHT = 288;
static constexpr int NUM_BARS = 11;
static constexpr int BAR_SPACING = 2;
static constexpr int BAR_WIDTH = (SCREEN_WIDTH - (BAR_SPACING * (NUM_BARS - 1))) / NUM_BARS;
static constexpr int HORIZONTAL_OFFSET = 2;
static constexpr int SEGMENT_HEIGHT = 10;
static constexpr std::array<int, NUM_BARS + 1> FREQUENCY_BANDS = {
375, // Bass warmth and low rumble (e.g., deep basslines, kick drum body)
750, // Upper bass punch (e.g., bass guitar punch, kick drum attack)
1500, // Lower midrange fullness (e.g., warmth in vocals, guitar body)
2250, // Midrange clarity (e.g., vocal presence, snare crack)
3375, // Upper midrange bite (e.g., instrument definition, vocal articulation)
4875, // Presence and edge (e.g., guitar bite, vocal sibilance start)
6750, // Lower brilliance (e.g., cymbal shimmer, vocal clarity)
9375, // Brilliance and air (e.g., hi-hat crispness, breathy vocals)
13125, // High treble sparkle (e.g., subtle overtones, synth shimmer)
16875, // Upper treble airiness (e.g., faint harmonics, room ambiance)
20625, // Top-end sheen (e.g., ultra-high harmonics, noise floor)
24375 // Extreme treble limit (e.g., inaudible overtones, signal cutoff, static)
};
struct ColorTheme {
Color base_color;
Color peak_color;
};
NavigationView& nav_;
bool needs_background_redraw{false};
std::vector<int> bar_heights;
std::vector<int> prev_bar_heights;
uint32_t current_theme{0};
const std::array<ColorTheme, 20> themes{
ColorTheme{Color(255, 0, 255), Color(255, 255, 255)},
ColorTheme{Color(0, 255, 0), Color(255, 0, 0)},
ColorTheme{Color(0, 0, 255), Color(255, 255, 0)},
ColorTheme{Color(255, 128, 0), Color(255, 0, 128)},
ColorTheme{Color(128, 0, 255), Color(0, 255, 255)},
ColorTheme{Color(255, 255, 0), Color(0, 255, 128)},
ColorTheme{Color(255, 0, 0), Color(0, 128, 255)},
ColorTheme{Color(0, 255, 128), Color(255, 128, 255)},
ColorTheme{Color(128, 128, 128), Color(255, 255, 255)},
ColorTheme{Color(255, 64, 0), Color(0, 255, 64)},
ColorTheme{Color(0, 128, 128), Color(255, 192, 0)},
ColorTheme{Color(0, 255, 0), Color(0, 128, 0)},
ColorTheme{Color(32, 64, 32), Color(0, 255, 0)},
ColorTheme{Color(64, 0, 128), Color(255, 0, 255)},
ColorTheme{Color(0, 64, 0), Color(0, 255, 128)},
ColorTheme{Color(255, 255, 255), Color(0, 0, 255)},
ColorTheme{Color(128, 0, 0), Color(255, 128, 0)},
ColorTheme{Color(0, 128, 255), Color(255, 255, 128)},
ColorTheme{Color(64, 64, 64), Color(255, 0, 0)},
ColorTheme{Color(255, 192, 0), Color(0, 64, 128)}};
ButtonWithEncoder button_frequency{{0 * 8, 0 * 16 + 4, 11 * 8, 1 * 8}, ""};
RFAmpField field_rf_amp{{13 * 8, 0 * 16}};
LNAGainField field_lna{{15 * 8, 0 * 16}};
VGAGainField field_vga{{18 * 8, 0 * 16}};
Button button_mood{{21 * 8, 0, 6 * 8, 16}, "MOOD"};
AudioVolumeField field_volume{{28 * 8, 0 * 16}};
rf::Frequency frequency_value{93100000};
RxRadioState rx_radio_state_{};
app_settings::SettingsManager settings_{
"rx_gfx_eq",
app_settings::Mode::RX,
{{"theme", &current_theme},
{"frequency", &frequency_value}}};
void update_audio_spectrum(const AudioSpectrum& spectrum);
void render_equalizer(Painter& painter);
void cycle_theme();
MessageHandlerRegistration message_handler_audio_spectrum{
Message::ID::AudioSpectrum,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
this->update_audio_spectrum(*message.data);
this->set_dirty();
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
};
} // namespace ui::external_app::gfxeq
#endif

View File

@ -227,6 +227,7 @@ void HopperView::save_list() {
nav_,
filename_buffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& value) {
auto path = hopper_dir / (value + ".PHOP");

View File

@ -189,6 +189,7 @@ void LCRView::on_button_set_am(NavigationView& nav, int16_t button_id) {
nav,
litteral[button_id],
7,
ENTER_KEYBOARD_MODE_ALPHA,
[this, button_id](std::string& buffer) {
texts[button_id].set(buffer);
});
@ -257,6 +258,7 @@ LCRView::LCRView(NavigationView& nav) {
nav,
rgsb,
4,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
button_set_rgsb.set_text(buffer);
});

View File

@ -311,6 +311,7 @@ LGEView::LGEView(NavigationView& nav) {
nav,
nickname,
15,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
button_text.set_text(buffer);
});

View File

@ -212,6 +212,7 @@ MetronomeTapTempoView::MetronomeTapTempoView(NavigationView& nav, uint16_t bpm)
nav_,
input_buffer,
3,
ENTER_KEYBOARD_MODE_DIGITS,
[this](std::string& buffer) {
if (buffer.empty()) {
return;

View File

@ -99,7 +99,7 @@ static msg_t loopthread_fn(void* arg) {
}
void MorseView::on_set_text(NavigationView& nav) {
text_prompt(nav, buffer, 28);
text_prompt(nav, buffer, 28, ENTER_KEYBOARD_MODE_ALPHA);
}
void MorseView::focus() {

View File

@ -210,6 +210,7 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
nav,
outputFileBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
on_save_file(buffer);
});
@ -263,6 +264,7 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
nav,
ook_data.payload,
100,
ENTER_KEYBOARD_MODE_DIGITS,
[this](std::string& s) {
text_payload.set(s);
draw_waveform();

View File

@ -83,6 +83,7 @@ OOKBruteView::OOKBruteView(NavigationView& nav)
nav_,
text_input_buffer,
8, // currently longest is princeton
ENTER_KEYBOARD_MODE_DIGITS,
[this](std::string& buffer) {
field_start.set_value(atoi(buffer.c_str()));
validate_start_stop();
@ -100,6 +101,7 @@ OOKBruteView::OOKBruteView(NavigationView& nav)
nav_,
text_input_buffer,
8, // currently longest is princeton
ENTER_KEYBOARD_MODE_DIGITS,
[this](std::string& buffer) {
field_stop.set_value(atoi(buffer.c_str()));
validate_start_stop();

View File

@ -122,6 +122,7 @@ bool PlaylistEditorView::on_create_ppl() {
nav_,
current_ppl_name_buffer,
100,
ENTER_KEYBOARD_MODE_ALPHA,
[&](std::string& s) {
current_ppl_name_buffer = s;
@ -307,6 +308,7 @@ PlaylistItemEditView::PlaylistItemEditView(
nav_,
delay_str,
100,
ENTER_KEYBOARD_MODE_ALPHA,
[&](std::string& s) {
delay_ = atoi(s.c_str());
field_delay.set_value(delay_);

View File

@ -75,7 +75,7 @@ __attribute__((section(".external_app.app_remote.application_information"), used
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::HOME,
/*.desired_menu_position = */ 4,
/*.desired_menu_position = */ 6,
/*.m4_app_tag = portapack::spi_flash::image_tag_replay */ {'P', 'R', 'E', 'P'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time

View File

@ -330,7 +330,7 @@ RemoteAppView::RemoteAppView(
field_title.on_select = [this, &nav](TextField&) {
temp_buffer_ = model_.name;
text_prompt(nav_, temp_buffer_, text_edit_max, [this](std::string& new_name) {
text_prompt(nav_, temp_buffer_, text_edit_max, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_name) {
model_.name = new_name;
refresh_ui();
set_needs_save();
@ -339,7 +339,7 @@ RemoteAppView::RemoteAppView(
field_filename.on_select = [this, &nav](TextField&) {
temp_buffer_ = remote_path_.stem().string();
text_prompt(nav_, temp_buffer_, text_edit_max, [this](std::string& new_name) {
text_prompt(nav_, temp_buffer_, text_edit_max, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_name) {
rename_remote(new_name);
refresh_ui();
});

View File

@ -56,7 +56,7 @@ SpectrumInputTextView::~SpectrumInputTextView() {
}
void SpectrumInputTextView::on_set_text(NavigationView& nav) {
text_prompt(nav, buffer, 300);
text_prompt(nav, buffer, 300, ENTER_KEYBOARD_MODE_DIGITS);
}
void SpectrumInputTextView::focus() {

View File

@ -51,3 +51,4 @@ const std::filesystem::path whipcalc_dir = u"WHIPCALC";
const std::filesystem::path ook_editor_dir = u"OOKFILES";
const std::filesystem::path hopper_dir = u"HOPPER";
const std::filesystem::path subghz_dir = u"SUBGHZ";
const std::filesystem::path waterfalls_dir = u"WATERFALLS";

View File

@ -53,5 +53,6 @@ extern const std::filesystem::path whipcalc_dir;
extern const std::filesystem::path ook_editor_dir;
extern const std::filesystem::path hopper_dir;
extern const std::filesystem::path subghz_dir;
extern const std::filesystem::path waterfalls_dir;
#endif /* __FILE_PATH_H__ */

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2025 Belousov Oleg
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "gradient.hpp"
#include "convert.hpp"
#include "file_reader.hpp"
namespace fs = std::filesystem;
const std::filesystem::path default_gradient_file = u"waterfall.txt";
Gradient::Gradient() {
prev_index = 0;
prev_r = 0;
prev_g = 0;
prev_b = 0;
}
void Gradient::set_default() {
step(86, 0, 0, 255);
step(171, 0, 255, 0);
step(255, 255, 0, 0);
}
bool Gradient::load_file(const std::filesystem::path& file_path) {
File gradient_file;
auto error = gradient_file.open(file_path.string());
if (error)
return false;
auto reader = FileLineReader(gradient_file);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue; // Empty or comment line.
auto cols = split_string(line, ',');
if (cols.size() == 4) {
int16_t index, r, g, b;
if (!parse_int(cols[0], index) || index < 0 || index > 255)
continue;
if (!parse_int(cols[1], r) || r < 0 || r > 255)
continue;
if (!parse_int(cols[2], g) || g < 0 || g > 255)
continue;
if (!parse_int(cols[3], b) || b < 0 || b > 255)
continue;
step(index, r, g, b);
}
}
return true;
}
void Gradient::step(int16_t index, int16_t r, int16_t g, int16_t b) {
for (int16_t i = prev_index; i <= index; i++) {
float x = (float)(i - prev_index) / (index - prev_index);
float y = 1.0f - x;
int16_t new_r = prev_r * y + r * x;
int16_t new_g = prev_g * y + g * x;
int16_t new_b = prev_b * y + b * x;
lut[i] = ui::Color(new_r, new_g, new_b);
}
prev_index = index;
prev_r = r;
prev_g = g;
prev_b = b;
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2025 Belousov Oleg
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __GRADIENT_H__
#define __GRADIENT_H__
#include "ui.hpp"
#include "file.hpp"
#include <array>
#include <cstdint>
extern const std::filesystem::path default_gradient_file;
class Gradient {
public:
std::array<ui::Color, 256> lut{};
Gradient();
void set_default();
bool load_file(const std::filesystem::path& file_path);
private:
int16_t prev_index = 0;
int16_t prev_r = 0;
int16_t prev_g = 0;
int16_t prev_b = 0;
void step(int16_t index, int16_t r, int16_t g, int16_t b);
};
#endif /* __GRADIENT_H__ */

View File

@ -538,7 +538,6 @@ init_status_t init() {
set_cpu_clock_speed();
if (persistent_memory::config_lcd_inverted_mode()) display.set_inverted(true);
/* sample max: 1023 sample_t AKA uint16_t
* touch_sensitivity: range: 1 to 128
* threshold range: 1023/1 to 1023/128 = 1023 to 8

View File

@ -280,265 +280,6 @@ const std::array<ui::Color, 256> spectrum_rgb2_lut{{
{132, 0, 0},
}};
const std::array<ui::Color, 256> spectrum_rgb3_lut{{
{0, 0, 0},
{0, 0, 3},
{0, 0, 6},
{0, 0, 9},
{0, 0, 12},
{0, 0, 15},
{0, 0, 18},
{0, 0, 21},
{0, 0, 24},
{0, 0, 27},
{0, 0, 30},
{0, 0, 33},
{0, 0, 36},
{0, 0, 39},
{0, 0, 42},
{0, 0, 45},
{0, 0, 48},
{0, 0, 51},
{0, 0, 54},
{0, 0, 57},
{0, 0, 60},
{0, 0, 63},
{0, 0, 66},
{0, 0, 69},
{0, 0, 72},
{0, 0, 75},
{0, 0, 78},
{0, 0, 81},
{0, 0, 84},
{0, 0, 87},
{0, 0, 90},
{0, 0, 93},
{0, 0, 96},
{0, 0, 99},
{0, 0, 102},
{0, 0, 105},
{0, 0, 108},
{0, 0, 111},
{0, 0, 114},
{0, 0, 117},
{0, 0, 120},
{0, 0, 123},
{0, 0, 126},
{0, 0, 129},
{0, 0, 132},
{0, 0, 135},
{0, 0, 138},
{0, 0, 141},
{0, 0, 144},
{0, 0, 147},
{0, 0, 150},
{0, 0, 153},
{0, 0, 156},
{0, 0, 159},
{0, 0, 162},
{0, 0, 165},
{0, 0, 168},
{0, 0, 171},
{0, 0, 174},
{0, 0, 177},
{0, 0, 180},
{0, 0, 183},
{0, 0, 186},
{0, 0, 189},
{0, 0, 192},
{0, 0, 195},
{0, 0, 198},
{0, 0, 201},
{0, 0, 204},
{0, 0, 207},
{0, 0, 210},
{0, 0, 213},
{0, 0, 216},
{0, 0, 219},
{0, 0, 222},
{0, 0, 225},
{0, 0, 228},
{0, 0, 231},
{0, 0, 234},
{0, 0, 237},
{0, 0, 240},
{0, 0, 243},
{0, 0, 246},
{0, 0, 249},
{0, 0, 252},
{0, 0, 255},
{0, 3, 252},
{0, 6, 249},
{0, 9, 246},
{0, 12, 243},
{0, 15, 240},
{0, 18, 237},
{0, 21, 234},
{0, 24, 231},
{0, 27, 228},
{0, 30, 225},
{0, 33, 222},
{0, 36, 219},
{0, 39, 216},
{0, 42, 213},
{0, 45, 210},
{0, 48, 207},
{0, 51, 204},
{0, 54, 201},
{0, 57, 198},
{0, 60, 195},
{0, 63, 192},
{0, 66, 189},
{0, 69, 186},
{0, 72, 183},
{0, 75, 180},
{0, 78, 177},
{0, 81, 174},
{0, 84, 171},
{0, 87, 168},
{0, 90, 165},
{0, 93, 162},
{0, 96, 159},
{0, 99, 156},
{0, 102, 153},
{0, 105, 150},
{0, 108, 147},
{0, 111, 144},
{0, 114, 141},
{0, 117, 138},
{0, 120, 135},
{0, 123, 132},
{0, 126, 129},
{0, 129, 126},
{0, 132, 123},
{0, 135, 120},
{0, 138, 117},
{0, 141, 114},
{0, 144, 111},
{0, 147, 108},
{0, 150, 105},
{0, 153, 102},
{0, 156, 99},
{0, 159, 96},
{0, 162, 93},
{0, 165, 90},
{0, 168, 87},
{0, 171, 84},
{0, 174, 81},
{0, 177, 78},
{0, 180, 75},
{0, 183, 72},
{0, 186, 69},
{0, 189, 66},
{0, 192, 63},
{0, 195, 60},
{0, 198, 57},
{0, 201, 54},
{0, 204, 51},
{0, 207, 48},
{0, 210, 45},
{0, 213, 42},
{0, 216, 39},
{0, 219, 36},
{0, 222, 33},
{0, 225, 30},
{0, 228, 27},
{0, 231, 24},
{0, 234, 21},
{0, 237, 18},
{0, 240, 15},
{0, 243, 12},
{0, 246, 9},
{0, 249, 6},
{0, 252, 3},
{0, 255, 0},
{3, 252, 0},
{6, 249, 0},
{9, 246, 0},
{12, 243, 0},
{15, 240, 0},
{18, 237, 0},
{21, 234, 0},
{24, 231, 0},
{27, 228, 0},
{30, 225, 0},
{33, 222, 0},
{36, 219, 0},
{39, 216, 0},
{42, 213, 0},
{45, 210, 0},
{48, 207, 0},
{51, 204, 0},
{54, 201, 0},
{57, 198, 0},
{60, 195, 0},
{63, 192, 0},
{66, 189, 0},
{69, 186, 0},
{72, 183, 0},
{75, 180, 0},
{78, 177, 0},
{81, 174, 0},
{84, 171, 0},
{87, 168, 0},
{90, 165, 0},
{93, 162, 0},
{96, 159, 0},
{99, 156, 0},
{102, 153, 0},
{105, 150, 0},
{108, 147, 0},
{111, 144, 0},
{114, 141, 0},
{117, 138, 0},
{120, 135, 0},
{123, 132, 0},
{126, 129, 0},
{129, 126, 0},
{132, 123, 0},
{135, 120, 0},
{138, 117, 0},
{141, 114, 0},
{144, 111, 0},
{147, 108, 0},
{150, 105, 0},
{153, 102, 0},
{156, 99, 0},
{159, 96, 0},
{162, 93, 0},
{165, 90, 0},
{168, 87, 0},
{171, 84, 0},
{174, 81, 0},
{177, 78, 0},
{180, 75, 0},
{183, 72, 0},
{186, 69, 0},
{189, 66, 0},
{192, 63, 0},
{195, 60, 0},
{198, 57, 0},
{201, 54, 0},
{204, 51, 0},
{207, 48, 0},
{210, 45, 0},
{213, 42, 0},
{216, 39, 0},
{219, 36, 0},
{222, 33, 0},
{225, 30, 0},
{228, 27, 0},
{231, 24, 0},
{234, 21, 0},
{237, 18, 0},
{240, 15, 0},
{243, 12, 0},
{246, 9, 0},
{249, 6, 0},
{252, 3, 0},
{255, 0, 0},
}};
const std::array<ui::Color, 256> spectrum_rgb4_lut{{
{0, 0, 0},
{1, 1, 1},

View File

@ -27,7 +27,6 @@
#include <array>
extern const std::array<ui::Color, 256> spectrum_rgb2_lut;
extern const std::array<ui::Color, 256> spectrum_rgb3_lut;
extern const std::array<ui::Color, 256> spectrum_rgb4_lut;
#endif /*__SPECTRUM_COLOR_LUT_H__*/

View File

@ -32,7 +32,8 @@ namespace ui {
AlphanumView::AlphanumView(
NavigationView& nav,
std::string& str,
size_t max_length)
size_t max_length,
uint8_t enter_mode)
: TextEntryView(nav, str, max_length) {
size_t n;
@ -76,7 +77,7 @@ AlphanumView::AlphanumView(
n++;
}
set_mode(mode);
set_mode(enter_mode);
button_mode.on_select = [this](Button&) {
set_mode(mode + 1);

View File

@ -37,7 +37,7 @@ namespace ui {
class AlphanumView : public TextEntryView {
public:
AlphanumView(NavigationView& nav, std::string& str, size_t max_length);
AlphanumView(NavigationView& nav, std::string& str, size_t max_length, uint8_t enter_mode);
AlphanumView(const AlphanumView&) = delete;
AlphanumView(AlphanumView&&) = delete;

View File

@ -29,6 +29,7 @@
#include "irq_controls.hpp"
#include "rf_path.hpp"
#include "freqman_db.hpp"
#include <cstddef>
#include <cstdint>
@ -37,6 +38,10 @@
#define MAX_UFREQ 7200000000 // maximum usable frequency
using option_db_t = std::pair<std::string_view, int32_t>;
using options_db_t = std::vector<option_db_t>;
extern options_db_t freqman_steps;
namespace ui {
class FrequencyField : public Widget {
@ -263,22 +268,12 @@ class FrequencyStepView : public OptionsField {
: OptionsField{
parent_pos,
5,
{
{" 10", 10}, /* Fine tuning SSB voice pitch,in HF and QO-100 sat #669 */
{" 50", 50}, /* added 50Hz/10Hz,but we do not increase GUI digit decimal */
{" 100", 100},
{" 1k ", 1000},
{" 3k ", 3000}, /* Approximate SSB bandwidth */
{" 5k ", 5000},
{" 6k3", 6250},
{" 9k ", 9000}, /* channel spacing for LF, MF in some regions */
{" 10k ", 10000},
{" 12k5", 12500},
{" 25k ", 25000},
{"100k ", 100000},
{" 1M ", 1000000},
{" 10M ", 10000000},
}} {
{}} {
options_t options;
for (const auto& step : freqman_steps) {
options.emplace_back(step.first, step.second);
}
set_options(options);
}
};

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyleft Mr. Robot 2025
*
* This file is part of PortaPack.
*
@ -21,8 +22,6 @@
#include "ui_spectrum.hpp"
#include "spectrum_color_lut.hpp"
#include "portapack.hpp"
using namespace portapack;
@ -102,6 +101,15 @@ void FrequencyScale::set_channel_filter(
}
}
void FrequencyScale::set_cursor_position(const int32_t position) {
cursor_position = position;
cursor_position = std::min<int32_t>(cursor_position, 119);
cursor_position = std::max<int32_t>(cursor_position, -120);
set_dirty();
}
void FrequencyScale::paint(Painter& painter) {
const auto r = screen_rect();
@ -242,6 +250,15 @@ bool FrequencyScale::on_key(const KeyEvent key) {
return false;
}
bool FrequencyScale::on_touch(const TouchEvent touch) {
if (touch.type == TouchEvent::Type::Start) {
if (on_select) {
on_select((touch.point.x() * spectrum_sampling_rate) / 240);
}
}
return true;
}
void FrequencyScale::on_tick_second() {
set_dirty();
_blink = !_blink;
@ -271,12 +288,12 @@ void WaterfallWidget::on_channel_spectrum(
std::array<Color, 240> pixel_row;
for (size_t i = 0; i < 120; i++) {
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]];
const auto pixel_color = gradient.lut[spectrum.db[256 - 120 + i]];
pixel_row[i] = pixel_color;
}
for (size_t i = 120; i < 240; i++) {
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]];
const auto pixel_color = gradient.lut[spectrum.db[i - 120]];
pixel_row[i] = pixel_color;
}
@ -287,6 +304,15 @@ void WaterfallWidget::on_channel_spectrum(
pixel_row);
}
bool WaterfallWidget::on_touch(const TouchEvent event) {
if (event.type == TouchEvent::Type::Start) {
if (on_touch_select) {
on_touch_select(event.point.x(), event.point.y());
}
}
return true;
}
void WaterfallWidget::clear() {
display.fill_rectangle(
screen_rect(),
@ -305,6 +331,22 @@ WaterfallView::WaterfallView(const bool cursor) {
frequency_scale.on_select = [this](int32_t offset) {
if (on_select) on_select(offset);
};
waterfall_widget.on_touch_select = [this](int32_t x, int32_t y) {
if (y > screen_height - screen_height * 0.1) return; // prevent ghost touch
frequency_scale.focus(); // focus on frequency scale to show cursor
if (sampling_rate) {
// screen x to frequency scale x, NB we need two widgets align
int32_t cursor_position = x - (screen_width / 2);
frequency_scale.set_cursor_position(cursor_position);
}
};
if (!waterfall_widget.gradient.load_file(default_gradient_file)) {
waterfall_widget.gradient.set_default();
}
}
void WaterfallView::on_show() {

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyleft Mr. Robot 2025
*
* This file is part of PortaPack.
*
@ -24,6 +25,7 @@
#include "ui.hpp"
#include "ui_widget.hpp"
#include "gradient.hpp"
#include "event_m0.hpp"
@ -78,9 +80,11 @@ class FrequencyScale : public Widget {
bool on_encoder(const EncoderEvent delta) override;
bool on_key(const KeyEvent key) override;
bool on_touch(const TouchEvent touch) override;
void set_spectrum_sampling_rate(const int new_sampling_rate);
void set_channel_filter(const int low_frequency, const int high_frequency, const int transition);
void set_cursor_position(const int32_t position);
void paint(Painter& painter) override;
@ -111,9 +115,14 @@ class FrequencyScale : public Widget {
class WaterfallWidget : public Widget {
public:
std::function<void(int32_t offset, int32_t y)> on_touch_select{};
Gradient gradient{};
void on_show() override;
void on_hide() override;
void paint(Painter&) override {}
bool on_touch(const TouchEvent event) override;
void on_channel_spectrum(const ChannelSpectrum& spectrum);

View File

@ -31,8 +31,9 @@ void text_prompt(
NavigationView& nav,
std::string& str,
size_t max_length,
uint8_t mode,
std::function<void(std::string&)> on_done) {
text_prompt(nav, str, str.length(), max_length, on_done);
text_prompt(nav, str, str.length(), max_length, mode, on_done);
}
void text_prompt(
@ -40,8 +41,9 @@ void text_prompt(
std::string& str,
uint32_t cursor_pos,
size_t max_length,
uint8_t mode,
std::function<void(std::string&)> on_done) {
auto te_view = nav.push<AlphanumView>(str, max_length);
auto te_view = nav.push<AlphanumView>(str, max_length, mode);
te_view->set_cursor(cursor_pos);
te_view->on_changed = [on_done](std::string& value) {
if (on_done)

View File

@ -26,6 +26,11 @@
#include "ui.hpp"
#include "ui_navigation.hpp"
#define ENTER_KEYBOARD_MODE_ALPHA 0
#define ENTER_KEYBOARD_MODE_DIGITS 1
#define ENTER_KEYBOARD_MODE_SYMBOLS 2
#define ENTER_KEYBOARD_MODE_HEX 3
namespace ui {
class TextEntryView : public View {
@ -62,6 +67,7 @@ void text_prompt(
NavigationView& nav,
std::string& str,
size_t max_length,
uint8_t mode,
std::function<void(std::string&)> on_done = nullptr);
void text_prompt(
@ -69,6 +75,7 @@ void text_prompt(
std::string& str,
uint32_t cursor_pos,
size_t max_length,
uint8_t mode, // enter mode: 123 abc etc
std::function<void(std::string&)> on_done = nullptr);
} /* namespace ui */

View File

@ -117,10 +117,10 @@ const NavigationView::AppList NavigationView::appList = {
/* HOME ******************************************************************/
{nullptr, "Receive", HOME, Color::cyan(), &bitmap_icon_receivers, new ViewFactory<ReceiversMenuView>()},
{nullptr, "Transmit", HOME, Color::cyan(), &bitmap_icon_transmit, new ViewFactory<TransmittersMenuView>()},
{nullptr, "Tranceiver", HOME, Color::cyan(), &bitmap_icon_tranceivers, new ViewFactory<TranceiversMenuView>()},
{"recon", "Recon", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory<ReconView>()},
{"capture", "Capture", HOME, Color::red(), &bitmap_icon_capture, new ViewFactory<CaptureAppView>()},
{"replay", "Replay", HOME, Color::green(), &bitmap_icon_replay, new ViewFactory<PlaylistView>()},
{"recon", "Recon", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory<ReconView>()},
{"microphone", "Microphone", HOME, Color::green(), &bitmap_icon_microphone, new ViewFactory<MicTXView>()},
{"lookingglass", "Looking Glass", HOME, Color::green(), &bitmap_icon_looking, new ViewFactory<GlassView>()},
{nullptr, "Utilities", HOME, Color::cyan(), &bitmap_icon_utilities, new ViewFactory<UtilitiesMenuView>()},
{nullptr, "Games", HOME, Color::cyan(), &bitmap_icon_games, new ViewFactory<GamesMenuView>()},
@ -146,7 +146,9 @@ const NavigationView::AppList NavigationView::appList = {
{"rdstx", "RDS", TX, ui::Color::green(), &bitmap_icon_rds, new ViewFactory<RDSView>()},
{"soundbrd", "Soundbrd", TX, ui::Color::green(), &bitmap_icon_soundboard, new ViewFactory<SoundBoardView>()},
{"touchtune", "TouchTune", TX, ui::Color::green(), &bitmap_icon_touchtunes, new ViewFactory<TouchTunesView>()},
{"signalgen", "Signal Gen", TX, Color::green(), &bitmap_icon_cwgen, new ViewFactory<SigGenView>()},
{"signalgen", "SignalGen", TX, Color::green(), &bitmap_icon_cwgen, new ViewFactory<SigGenView>()},
/* TRX ********************************************************************/
{"microphone", "Mic", TRX, Color::green(), &bitmap_icon_microphone, new ViewFactory<MicTXView>()},
/* UTILITIES *************************************************************/
{"filemanager", "File Manager", UTILITIES, Color::green(), &bitmap_icon_dir, new ViewFactory<FileManagerView>()},
{"freqman", "Freq. Manager", UTILITIES, Color::green(), &bitmap_icon_freqman, new ViewFactory<FrequencyManagerView>()},
@ -343,7 +345,7 @@ void SystemStatusView::on_battery_data(const BatteryStateMessage* msg) {
// Check if charging state changed to charging
static bool was_charging = false;
if (msg->on_charger && !was_charging) {
if (msg->on_charger && !was_charging && pmem::ui_battery_charge_hint()) {
// Only show charging modal when transitioning to charging state
nav_.display_modal(
"CHARGING",
@ -380,7 +382,7 @@ void SystemStatusView::refresh() {
// 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(&toggle_speaker);
if (!pmem::ui_hide_fake_brightness() && !pmem::config_lcd_inverted_mode()) status_icons.add(&button_fake_brightness);
if (!pmem::ui_hide_fake_brightness()) status_icons.add(&button_fake_brightness);
if (battery::BatteryManagement::isDetected()) {
batt_was_inited = true;
if (!pmem::ui_hide_battery_icon()) {
@ -414,7 +416,7 @@ void SystemStatusView::refresh() {
button_converter.set_foreground(pmem::config_converter() ? Theme::getInstance()->fg_red->foreground : Theme::getInstance()->fg_light->foreground);
// Fake Brightness
button_fake_brightness.set_foreground((pmem::apply_fake_brightness() & (!pmem::config_lcd_inverted_mode())) ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
button_fake_brightness.set_foreground(pmem::apply_fake_brightness() ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
set_dirty();
}
@ -841,6 +843,22 @@ void TransmittersMenuView::on_populate() {
add_external_items(nav_, app_location_t::TX, *this, return_icon ? 1 : 0);
}
/* TranceiversMenuView **************************************************/
TranceiversMenuView::TranceiversMenuView(NavigationView& nav)
: nav_(nav) {}
void TranceiversMenuView::on_populate() {
bool return_icon = pmem::show_gui_return_icon();
if (return_icon) {
add_items({{"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }}});
}
add_apps(nav_, *this, TRX);
// add_external_items(nav_, app_location_t::TRX, *this, return_icon ? 1 : 0);
// this folder doesn't have external apps, comment to prevent pop the err msg.
// NB: when has external app someday, uncomment this.
}
/* UtilitiesMenuView *****************************************************/
UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav)

View File

@ -379,6 +379,16 @@ class TransmittersMenuView : public BtnGridView {
void on_populate() override;
};
class TranceiversMenuView : public BtnGridView {
public:
TranceiversMenuView(NavigationView& nav);
std::string title() const override { return "Tranceiver"; };
private:
NavigationView& nav_;
void on_populate() override;
};
class UtilitiesMenuView : public BtnGridView {
public:
UtilitiesMenuView(NavigationView& nav);

View File

@ -691,6 +691,9 @@ static void printAppInfo(BaseSequentialStream* chp, ui::AppInfoConsole& element)
case TX:
chprintf(chp, "[TX]\r\n");
break;
case TRX:
chprintf(chp, "[TRX]\r\n");
break;
case UTILITIES:
chprintf(chp, "[UTIL]\r\n");
break;
@ -715,6 +718,9 @@ static void printAppInfo(BaseSequentialStream* chp, const ui::AppInfo& element)
case TX:
chprintf(chp, "[TX]\r\n");
break;
case TRX:
chprintf(chp, "[TRX]\r\n");
break;
case UTILITIES:
chprintf(chp, "[UTIL]\r\n");
break;

View File

@ -33,6 +33,8 @@ using namespace portapack;
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
#include <complex>
#include <cstring>
@ -143,7 +145,13 @@ void lcd_init() {
// REV = 1 (normally white)
// NL = 0b100111 (default)
// PCDIV = 0b000000 (default?)
io.lcd_data_write_command_and_data(0xB6, {0x0A, 0xA2, 0x27, 0x00});
/*as per the datasheet chapter 8.3.7, addr B6h,
data "REV" bit, liquid crystal type:*/
if (portapack::persistent_memory::config_lcd_normally_black())
io.lcd_data_write_command_and_data(0xB6, {0x0A, 0x22, 0x27, 0x00}); // IPS : normally black : 0
else
io.lcd_data_write_command_and_data(0xB6, {0x0A, 0xA2, 0x27, 0x00}); // TFT : normally white : 1
// Power Control 1
// VRH[5:0]
@ -309,14 +317,6 @@ void ILI9341::wake() {
lcd_wake();
}
void ILI9341::set_inverted(bool invert) {
if (invert) {
io.lcd_data_write_command_and_data(0x21, {});
} else {
io.lcd_data_write_command_and_data(0x20, {});
}
}
void ILI9341::fill_rectangle(ui::Rect r, const ui::Color c) {
const auto r_clipped = r.intersect(screen_rect());
if (!r_clipped.is_empty()) {

View File

@ -50,8 +50,6 @@ class ILI9341 {
void sleep();
void wake();
void set_inverted(bool invert);
void fill_rectangle(ui::Rect r, const ui::Color c);
void fill_rectangle_unrolled8(ui::Rect r, const ui::Color c);
void draw_line(const ui::Point start, const ui::Point end, const ui::Color color);

View File

@ -78,11 +78,11 @@ void IO::reference_oscillator(const bool enable) {
}
bool IO::get_dark_cover() {
return portapack::persistent_memory::apply_fake_brightness() & (!portapack::persistent_memory::config_lcd_inverted_mode());
return portapack::persistent_memory::apply_fake_brightness();
}
bool IO::get_is_inverted() {
return portapack::persistent_memory::config_lcd_inverted_mode();
bool IO::get_is_normally_black() {
return portapack::persistent_memory::config_lcd_normally_black();
}
uint8_t IO::get_brightness() {
@ -90,7 +90,7 @@ uint8_t IO::get_brightness() {
}
void IO::update_cached_values() {
inverted_enabled = get_is_inverted();
lcd_normally_black = get_is_normally_black();
dark_cover_enabled = get_dark_cover();
brightness = get_brightness();
}

View File

@ -231,10 +231,10 @@ class IO {
return switches_raw;
}
bool inverted_enabled = false;
bool lcd_normally_black = false;
bool dark_cover_enabled = false;
uint8_t brightness = 0;
bool get_is_inverted();
bool get_is_normally_black();
bool get_dark_cover();
uint8_t get_brightness();
void update_cached_values();
@ -419,7 +419,7 @@ class IO {
const auto value_low = data_read();
uint32_t original_value = (value_high << 8) | value_low;
if (inverted_enabled) return original_value;
if (lcd_normally_black) return original_value;
if (dark_cover_enabled) {
// this is read data, so if the fake brightness is enabled AKA get_dark_cover() == true,

View File

@ -137,9 +137,9 @@ struct ui_config2_t {
bool button_repeat_delay : 1;
bool button_repeat_speed : 1;
bool button_long_press_delay : 1;
bool battery_charge_hint : 1;
uint8_t theme_id;
uint8_t PLACEHOLDER_3;
};
static_assert(sizeof(ui_config2_t) == sizeof(uint32_t));
@ -211,7 +211,7 @@ struct data_t {
bool updown_converter;
bool updown_frequency_rx_correction;
bool updown_frequency_tx_correction;
bool lcd_inverted_mode : 1;
bool lcd_normally_black : 1;
bool encoder_dial_direction : 1; // false = normal, true = reverse
bool UNUSED_6 : 1;
bool UNUSED_7 : 1;
@ -289,7 +289,7 @@ struct data_t {
updown_converter(false),
updown_frequency_rx_correction(false),
updown_frequency_tx_correction(false),
lcd_inverted_mode(false),
lcd_normally_black(false),
encoder_dial_direction(false),
UNUSED_6(false),
UNUSED_7(false),
@ -981,6 +981,9 @@ bool ui_button_repeat_speed() {
bool ui_button_long_press_delay() {
return data->ui_config2.button_long_press_delay;
}
bool ui_battery_charge_hint() {
return data->ui_config2.battery_charge_hint;
}
void set_ui_hide_speaker(bool v) {
data->ui_config2.hide_speaker = v;
@ -1035,6 +1038,9 @@ void set_ui_button_repeat_speed(bool v) {
void set_ui_button_long_press_delay(bool v) {
data->ui_config2.button_long_press_delay = v;
}
void set_ui_battery_charge_hint(bool v) {
data->ui_config2.battery_charge_hint = v;
}
/* Converter */
bool config_converter() {
@ -1084,12 +1090,12 @@ void set_config_freq_rx_correction(uint32_t v) {
data->frequency_rx_correction = v;
}
// LCD invert
bool config_lcd_inverted_mode() {
return data->lcd_inverted_mode;
// IPS vs TFT
bool config_lcd_normally_black() {
return data->lcd_normally_black;
}
void set_lcd_inverted_mode(bool v) {
data->lcd_inverted_mode = v;
void set_lcd_normally_black(bool v) {
data->lcd_normally_black = v;
}
// Rotary encoder dial settings
@ -1158,7 +1164,6 @@ void set_fake_brightness_level(uint8_t v) {
// Cycle through 4 brightness options: disabled -> enabled/50% -> enabled/25% -> enabled/12.5% -> disabled
void toggle_fake_brightness_level() {
bool fbe = apply_fake_brightness();
if (config_lcd_inverted_mode()) return; // for now only inverted mode OR fake brightness
if ((!fbe) || (data->fake_brightness_level >= BRIGHTNESS_12p5)) {
set_apply_fake_brightness(!fbe);
data->fake_brightness_level = BRIGHTNESS_50;

View File

@ -249,8 +249,8 @@ void set_config_audio_mute(bool v);
void set_config_speaker_disable(bool v);
void set_config_backlight_timer(const backlight_config_t& new_value);
void set_disable_touchscreen(bool v);
bool config_lcd_inverted_mode();
void set_lcd_inverted_mode(bool v);
bool config_lcd_normally_black();
void set_lcd_normally_black(bool v);
uint8_t encoder_dial_sensitivity();
void set_encoder_dial_sensitivity(uint8_t v);
@ -350,6 +350,8 @@ bool ui_override_batt_calc();
bool ui_button_repeat_delay();
bool ui_button_repeat_speed();
bool ui_button_long_press_delay();
bool ui_battery_charge_hint();
void set_ui_hide_speaker(bool v);
void set_ui_hide_mute(bool v);
void set_ui_hide_converter(bool v);
@ -367,6 +369,7 @@ void set_ui_override_batt_calc(bool v);
void set_ui_button_repeat_delay(bool v);
void set_ui_button_repeat_speed(bool v);
void set_ui_button_long_press_delay(bool v);
void set_ui_battery_charge_hint(bool v);
// sd persisting settings
bool should_use_sdcard_for_pmem();

View File

@ -73,7 +73,8 @@ enum app_location_t : uint32_t {
DEBUG,
HOME,
SETTINGS,
GAMES
GAMES,
TRX
};
struct standalone_application_information_t {

View File

@ -1136,7 +1136,7 @@ void ButtonWithEncoder::paint(Painter& painter) {
const Style paint_style = {style().font, bg, fg};
painter.draw_rectangle({r.location(), {r.size().width(), 1}}, Theme::getInstance()->fg_light->foreground);
painter.draw_rectangle({r.location(), {r.size().width(), 1}}, Theme::getInstance()->bg_light->background);
painter.draw_rectangle({r.location().x(), r.location().y() + r.size().height() - 1, r.size().width(), 1}, Theme::getInstance()->bg_dark->background);
painter.draw_rectangle({r.location().x() + r.size().width() - 1, r.location().y(), 1, r.size().height()}, Theme::getInstance()->bg_dark->background);

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

View File

@ -98,12 +98,12 @@ f.write("/*\n"
"#include \"ui.hpp\"\n\n"
"namespace ui {\n\n")
for file in listdir(graphics_path):
for file in sorted(listdir(graphics_path)):
if file.endswith(".png") and path.isfile(graphics_path + file):
convert_png(graphics_path + file)
count += 1
f.write("} /* namespace ui */\n\n")
f.write("#endif/*__BITMAP_HPP__*/\n")
f.write("#endif /*__BITMAP_HPP__*/\n")
print(("Converted " + str(count) + " files"))

View File

@ -0,0 +1,14 @@
# 0% black
0,0,0,0
# 25% blue
64,0,0,255
# 50% magenta
128,255,0,255
# 75% green
192,0,255,0
# 100% white
255,255,255,255

View File

@ -0,0 +1,12 @@
#lines are index,r,g,b
#if index 0 is not set, it's starting at 0,0,0,0
#all values are ranging from 0 to 255
#pure blue
86,0,0,255
#bright green
171,0,255,0
#pure red
255,255,0,0

View File

@ -0,0 +1,11 @@
# 0% black
0,0,0,0
# 33% red
84,255,0,0
# 66% yellow
168,255,255,0
# 100% white
255,255,255,255

View File

@ -0,0 +1,8 @@
# 0% black
0,0,0,0
# 50% green
128,0,255,0
# 100% white
255,255,255,255

View File

@ -0,0 +1,14 @@
# 0% black
0,0,0,0
# 25% blue
64,0,0,255
# 50% red
128,255,0,0
# 75% yellow
192,255,255,0
# 100% white
255,255,255,255