Compare commits

..

No commits in common. "next" and "nightly-tag-2025-03-27" have entirely different histories.

80 changed files with 5991 additions and 6736 deletions

View File

@ -2,7 +2,6 @@ name: Hardware / QC / QA / selling
description: Issues that related with hardware, quality control, quality assurance, selling, etc.
labels:
- question
- hardware problem
body:
- type: markdown
attributes:
@ -12,6 +11,9 @@ body:
We would like to inform you that we can't provide much help regarding hardware/QC/QA/selling issues because we are not controlling various vendors/manufacturers, and also we are not profiting from you buying your device.
You are still welcome to ask, but please be aware that for this kind of issue, ask the seller would be the best option.
> [!IMPORTANT]
If you are a seller/vendor and your customer has directed you to this page as a reference/evidence of a "broken device" or similar issue, please be aware that we are not providing any "official guarantee of a faulty device." We are only offering suggestions based on what the submitter has stated. We do not guarantee that the device is broken or anything else.
- type: textarea
id: description
attributes:

View File

@ -1,20 +0,0 @@
name: Hardware Issue Warning
on:
issues:
types: [labeled]
jobs:
add-comment:
if: github.event.label.name == 'hardware problem'
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- name: Add warning comment
uses: peter-evans/create-or-update-comment@v3
with:
issue-number: ${{ github.event.issue.number }}
body: |
> [!IMPORTANT]
If you are a seller/vendor and your customer has directed you to this page as a reference/evidence of a "broken device" or similar issue, please be aware that we are not providing any "official guarantee of a faulty device." We are only offering suggestions based on what the submitter has stated. We do not guarantee that the device is broken or anything else.

View File

@ -9,9 +9,7 @@
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
[<img src="https://github.com/user-attachments/assets/dea337ab-fb64-4a2a-b419-69afd272e815" height="310">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#new-h4m-mayhem-edition)
[<img src="https://camo.githubusercontent.com/5c1f1da0688240ac7b2ccca0c8dbfd1d73f2540741ad8b1828ba4d5ea68af248/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f343339333937392f3239353533323731382d38653562363631632d663934362d346365652d386232642d3061363135663737313566342e706e67" height="310">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition)
[<img src="https://camo.githubusercontent.com/c1f7dd1e7672324f60a513f0de23de76da6a669e63896a9de535d8c8093fc3c7/68747470733a2f2f7261772e6769746875622e636f6d2f7368617265627261696e65642f706f7274617061636b2d6861636b72662f6d61737465722f646f632f696d616765732f68617264776172652f706f7274617061636b5f68315f6f7065726174696e672e6a7067" height="310">]([https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h1r1r2))
[<img src="https://github.com/user-attachments/assets/dea337ab-fb64-4a2a-b419-69afd272e815" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#new-h4m-mayhem-edition) [<img src="https://camo.githubusercontent.com/5c1f1da0688240ac7b2ccca0c8dbfd1d73f2540741ad8b1828ba4d5ea68af248/68747470733a2f2f6769746875622d70726f64756374696f6e2d757365722d61737365742d3632313064662e73332e616d617a6f6e6177732e636f6d2f343339333937392f3239353533323731382d38653562363631632d663934362d346365652d386232642d3061363135663737313566342e706e67" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/PortaPack-Versions#h2m-mayhem-edition)
# What is this?

View File

@ -191,7 +191,6 @@ 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) { // 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)
zoom_config.on_change = [this, view](size_t, OptionsField::value_t n) {
receiver_model.set_am_configuration(previous_filter_array_index + n);
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); // 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.
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());
options_config.on_change = [this, view](size_t, OptionsField::value_t n) {
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)
receiver_model.set_am_configuration(n);
previous_filter_array_index = n;
zoom_config.set_by_value(view->get_zoom_factor(AM_MODULATION));
};
}
@ -282,22 +282,6 @@ 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,6 +40,7 @@ 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{
@ -58,7 +59,7 @@ class AMOptionsView : public View {
{23 * 8, 0 * 16},
7,
{{"ZOOM x1", 0},
{"ZOOM x2", 6}} // offset index AM modes array FIR filters.
{"ZOOM x2", 6}} // offset index array filters.
};
};
@ -199,12 +200,6 @@ 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;
@ -213,9 +208,6 @@ 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,
@ -223,8 +215,6 @@ 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,6 +21,7 @@
* Boston, MA 02110-1301, USA.
*/
#include "ble_rx_app.hpp"
#include "ble_rx_app.hpp"
#include "ui_modemsetup.hpp"
@ -202,7 +203,6 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
nav,
packetFileBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this, packetToSave](std::string& buffer) {
on_save_file(buffer, packetToSave);
});
@ -471,7 +471,6 @@ BLERxView::BLERxView(NavigationView& nav)
nav_,
filterBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
on_filter_change(buffer);
});
@ -494,7 +493,6 @@ BLERxView::BLERxView(NavigationView& nav)
nav,
listFileBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
on_save_file(buffer);
});

View File

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

View File

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

View File

@ -92,9 +92,6 @@ 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,17 +419,6 @@ 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;
@ -510,7 +499,7 @@ FileSaveView::FileSaveView(
button_edit_path.on_select = [this](Button&) {
buffer_ = path_.string();
text_prompt(nav_, buffer_, max_filename_length,ENTER_KEYBOARD_MODE_ALPHA,
text_prompt(nav_, buffer_, max_filename_length,
[this](std::string&) {
path_ = buffer_;
refresh_widgets();
@ -519,7 +508,7 @@ FileSaveView::FileSaveView(
button_edit_name.on_select = [this](Button&) {
buffer_ = file_.string();
text_prompt(nav_, buffer_, max_filename_length,ENTER_KEYBOARD_MODE_ALPHA,
text_prompt(nav_, buffer_, max_filename_length,
[this](std::string&) {
file_ = buffer_;
refresh_widgets();
@ -577,7 +566,7 @@ void FileManagerView::on_rename(std::string_view hint) {
cursor_pos = pos;
text_prompt(
nav_, name_buffer, cursor_pos, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA,
nav_, name_buffer, cursor_pos, max_filename_length,
[this](std::string& renamed) {
auto renamed_path = fs::path{renamed};
rename_file(get_selected_full_path(), current_path / renamed_path);
@ -651,7 +640,7 @@ void FileManagerView::on_clean() {
void FileManagerView::on_new_dir() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& dir_name) {
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) {
make_new_directory(current_path / dir_name);
reload_current(true);
});
@ -682,7 +671,7 @@ void FileManagerView::on_paste() {
void FileManagerView::on_new_file() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& file_name) {
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) {
make_new_file(current_path / file_name);
reload_current(true);
});
@ -696,11 +685,7 @@ bool FileManagerView::handle_file_open() {
auto ext = path.extension();
if (path_iequal(txt_ext, ext)) {
if (path_iequal(current_path, u"/" + waterfalls_dir)) {
copy_waterfall(path);
} else {
nav_.push<TextEditorView>(path);
}
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,7 +96,6 @@ 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; // now included via ui_receiver.hpp
extern options_db_t freqman_steps;
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, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_desc) {
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, [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, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_name) {
text_prompt(nav_, temp_buffer_, 20, [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] = gradient.lut[power]; // row of colors
spectrum_row[pixel_index] = spectrum_rgb3_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,10 +359,6 @@ 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 "gradient.hpp"
#include "spectrum_color_lut.hpp"
namespace ui {
@ -74,7 +74,6 @@ 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, ENTER_KEYBOARD_MODE_ALPHA);
text_prompt(nav, buffer, MAX_POCSAG_LENGTH);
}
POCSAGTXView::POCSAGTXView(

View File

@ -65,7 +65,6 @@ RDSPSNView::RDSPSNView(
nav,
PSN,
8,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& s) {
text_psn.set(s);
});
@ -87,7 +86,6 @@ 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, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& buffer) {
text_prompt(nav, _output_file, 28, [this](std::string& buffer) {
_output_file = buffer;
button_choose_output_name.set_text(_output_file);
});

View File

@ -56,10 +56,6 @@ 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,
@ -294,7 +290,7 @@ void SearchView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
power = spectrum.db[bin - 128];
}
add_spectrum_pixel(gradient.lut[power]);
add_spectrum_pixel(spectrum_rgb3_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 "gradient.hpp"
#include "spectrum_color_lut.hpp"
#include "ui_receiver.hpp"
namespace ui {
@ -88,7 +88,6 @@ class SearchView : public View {
private:
NavigationView& nav_;
Gradient gradient{};
RxRadioState radio_state_{
100'000'000 /* frequency */,
2500000 /* bandwidth */,

View File

@ -810,29 +810,30 @@ SetDisplayView::SetDisplayView(NavigationView& nav) {
&field_fake_brightness,
&button_save,
&button_cancel,
&checkbox_ips_screen_switch,
&checkbox_invert_switch,
&checkbox_brightness_switch});
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_ips_screen_switch.set_value(pmem::config_lcd_normally_black());
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
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_ips_screen_switch.value() != pmem::config_lcd_normally_black()) {
pmem::set_lcd_normally_black(checkbox_ips_screen_switch.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());
}
send_system_refresh();
nav.pop();
};
// only enable invert OR fake brightness
checkbox_ips_screen_switch.on_select = [this](Checkbox&, bool v) {
checkbox_invert_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_ips_screen_switch.set_value(false);
if (v) checkbox_invert_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {
@ -1043,14 +1044,12 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&checkbox_overridebatt,
&checkbox_battery_charge_hint});
&checkbox_overridebatt});
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();
@ -1065,7 +1064,6 @@ 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,8 +762,6 @@ 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{
@ -778,10 +776,10 @@ class SetDisplayView : public View {
16,
"Enable brightness adjust"};
Checkbox checkbox_ips_screen_switch{
{1 * 8, 12 * 16},
Checkbox checkbox_invert_switch{
{1 * 8, 10 * 16},
23,
"IPS Screen"};
"Invert colors (For IPS)"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
@ -980,13 +978,9 @@ 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},
/**/
{{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}};
{{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}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@ -996,18 +990,13 @@ 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, 13 * 16, 12 * 8, 32},
{2 * 8, 8 * 16, 12 * 8, 32},
"Reset",
};
};

View File

@ -617,7 +617,6 @@ 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,7 +25,6 @@
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_widget.hpp"
#include "ui_textentry.hpp"
namespace ui {
@ -106,7 +105,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, ENTER_KEYBOARD_MODE_ALPHA,
text_prompt(nav, buf, /*max_length*/ 255,
[&tf](std::string& str) {
tf.set_text(str);
});

File diff suppressed because it is too large Load Diff

View File

@ -131,7 +131,6 @@ 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_normally_black: " + to_string_dec_uint(config_lcd_normally_black()));
pmem_dump_file.write_line("lcd_inverted_mode: " + to_string_dec_uint(config_lcd_inverted_mode()));
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,7 +128,6 @@ 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,10 +203,6 @@ set(EXTCPPSRC
#level
external/level/main.cpp
external/level/ui_level.cpp
#gfxEQ
external/gfxeq/main.cpp
external/gfxeq/ui_gfxeq.cpp
)
set(EXTAPPLIST
@ -259,5 +255,4 @@ set(EXTAPPLIST
debug_pmem
scanner
level
gfxeq
)

View File

@ -72,7 +72,6 @@ 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
@ -369,10 +368,4 @@ 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

@ -1,36 +0,0 @@
/*
* ------------------------------------------------------------
* | 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

@ -1,196 +0,0 @@
/*
* ------------------------------------------------------------
* | 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

@ -1,138 +0,0 @@
/*
* ------------------------------------------------------------
* | 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,7 +227,6 @@ 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,7 +189,6 @@ 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);
});
@ -258,7 +257,6 @@ LCRView::LCRView(NavigationView& nav) {
nav,
rgsb,
4,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
button_set_rgsb.set_text(buffer);
});

View File

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

View File

@ -23,7 +23,6 @@
#include "baseband_api.hpp"
#include "audio.hpp"
#include "portapack.hpp"
#include "ui_textentry.hpp"
using namespace portapack;
@ -43,7 +42,6 @@ MetronomeView::MetronomeView(NavigationView& nav)
&field_unaccent_beep_tune,
&field_beep_flash_duration,
&field_bpm,
&button_enter_tap_tempo,
&progressbar,
});
@ -61,14 +59,6 @@ MetronomeView::MetronomeView(NavigationView& nav)
}
};
button_enter_tap_tempo.on_select = [this](Button&) {
auto tap_tempo_view = nav_.push<MetronomeTapTempoView>(field_bpm.value());
tap_tempo_view->on_apply = [this](uint16_t bpm) {
field_bpm.set_value(bpm);
};
};
field_volume.set_value(0); // seems that a change is required to force update, so setting to 0 first
field_volume.set_value(99);
@ -184,116 +174,4 @@ void MetronomeView::run() {
}
}
MetronomeTapTempoView::MetronomeTapTempoView(NavigationView& nav, uint16_t bpm)
: nav_{nav},
bpm_{bpm} {
add_children({
&button_input,
&button_tap,
&button_cancel,
&button_apply,
});
bpm_when_entered_ = bpm; // save for if user cancel
// im aware that we have duplicated painter which means in this app, weo have two painter instances
// here is the reason why this is necessary:
// We need to draw the bpm big font once when enter, which would be at bad timing in constructor,
// cuz it happened before the view is pushed to nav, which casued it actually didn't draw
// which leads me have to override the paint func from father and draw inside of it.
//
// BUT I can't completely package the draw logic inside of the paint func,
// cuz set_dirty has flaw and cause screen flicker during the char changes, if i just package there and use set_dirty()
Painter painter_instance_2;
button_input.on_select = [this](Button&) {
input_buffer = to_string_dec_uint(bpm_);
text_prompt(
nav_,
input_buffer,
3,
ENTER_KEYBOARD_MODE_DIGITS,
[this](std::string& buffer) {
if (buffer.empty()) {
return;
}
bpm_ = atoi(buffer.c_str());
if (on_apply) {
on_apply(bpm_);
}
});
};
button_tap.on_select = [&](Button&) {
on_tap(painter_instance_2);
};
button_apply.on_select = [this](Button&) {
// it's dynamically applied in tap handler
// the design allow user to hear changes before apply
nav_.pop();
};
button_cancel.on_select = [this](Button&) {
bpm_ = bpm_when_entered_;
if (on_apply) {
on_apply(bpm_);
}
nav_.pop();
};
}
void MetronomeTapTempoView::focus() {
button_tap.focus();
}
void MetronomeTapTempoView::paint(Painter& painter) {
View::paint(painter);
painter.draw_char({(0 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + bpm_ / 100, 4);
painter.draw_char({(1 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + (bpm_ / 10) % 10, 4);
painter.draw_char({(2 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + bpm_ % 10, 4);
}
/*
NB: i don't really know if the cpu clock is 1000Hz AKA 1ms per tick for chTimeNow()
but it should be, refering to the stop watch app.
and also i compared with my real metronome and it's very close
so I assume it's 1ms per tick
*/
void MetronomeTapTempoView::on_tap(Painter& painter) {
/* ^ NB: this painter accepted from painter_instance_2*/
systime_t current_time = chTimeNow();
if (last_tap_time > 0) {
uint32_t interval_ms = current_time - last_tap_time;
if (interval_ms > 100) {
uint16_t this_time_bpm = 60000 / interval_ms;
if (this_time_bpm > 0 && this_time_bpm < 400) {
bpms_deque.push_back(this_time_bpm);
if (bpms_deque.size() > 4) { // one bar length cuz most music tempo is quarter note as 1 beat
bpms_deque.pop_front();
}
// avg
uint32_t sum = 0;
for (auto& bpm : bpms_deque) {
sum += bpm;
}
bpm_ = sum / bpms_deque.size();
if (on_apply) {
on_apply(bpm_);
}
painter.draw_char({(0 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + bpm_ / 100, 4);
painter.draw_char({(1 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + (bpm_ / 10) % 10, 4);
painter.draw_char({(2 * 16) * 4 + 2 * 16, 3 * 16}, *Theme::getInstance()->fg_light, '0' + bpm_ % 10, 4);
}
}
}
last_tap_time = current_time;
}
} // namespace ui::external_app::metronome

View File

@ -27,8 +27,6 @@
#include "audio.hpp"
#include "ch.h"
#include <deque>
namespace ui::external_app::metronome {
class MetronomeView : public View {
@ -75,11 +73,6 @@ class MetronomeView : public View {
1,
' '};
Button button_enter_tap_tempo{
{(sizeof("BPM:") + 6) * 8, 1 * 16, (sizeof("Tap Tempo") + 3) * 8, 16},
"Tap Tempo",
};
NumberField field_rythm_unaccent_time{// e.g. 3 in 3/4 beat
{(sizeof("Rhythm:") + 1) * 8, 4 * 16},
2,
@ -128,45 +121,6 @@ class MetronomeView : public View {
{0 * 16, 8 * 16, screen_width, screen_height - 14 * 16}};
};
class MetronomeTapTempoView : public View {
public:
std::function<void(uint16_t)> on_apply{};
MetronomeTapTempoView(NavigationView& nav, uint16_t bpm);
std::string title() const override { return "Tap.T"; };
void focus() override;
void paint(Painter& painter) override;
private:
void on_tap(Painter& painter);
NavigationView& nav_;
uint16_t bpm_{0};
uint16_t bpm_when_entered_{0}; // this pass from MetronomeView and need to restore if user cancel
std::deque<uint16_t> bpms_deque = {0}; // take average for recent taps to debounce
uint32_t last_tap_time{0};
std::string input_buffer{""}; // needed by text_prompt
Button button_input{
{0, 0, screen_width, 2 * 16},
"Input BPM"};
Button button_tap{
{0, 8 * 16, screen_width, 7 * 16},
"Tap BPM"};
Button button_cancel{
{1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"Cancel"};
Button button_apply{
{1 + screen_width / 2 + 1, 17 * 16, screen_width / 2 - 4, 2 * 16},
"Apply"};
};
} // namespace ui::external_app::metronome
#endif /*__UI_METRONOME_H__*/

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, ENTER_KEYBOARD_MODE_ALPHA);
text_prompt(nav, buffer, 28);
}
void MorseView::focus() {

View File

@ -210,7 +210,6 @@ OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
nav,
outputFileBuffer,
64,
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
on_save_file(buffer);
});
@ -264,7 +263,6 @@ 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,7 +83,6 @@ 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();
@ -101,7 +100,6 @@ 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,7 +122,6 @@ bool PlaylistEditorView::on_create_ppl() {
nav_,
current_ppl_name_buffer,
100,
ENTER_KEYBOARD_MODE_ALPHA,
[&](std::string& s) {
current_ppl_name_buffer = s;
@ -308,7 +307,6 @@ 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 = */ 6,
/*.desired_menu_position = */ 4,
/*.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, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_name) {
text_prompt(nav_, temp_buffer_, text_edit_max, [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, ENTER_KEYBOARD_MODE_ALPHA, [this](std::string& new_name) {
text_prompt(nav_, temp_buffer_, text_edit_max, [this](std::string& new_name) {
rename_remote(new_name);
refresh_ui();
});

View File

@ -1,8 +1,6 @@
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
// If you can read this, you're a nerd. :P
// Come join us at https://discord.gg/thepiratesreborn
// RocketGod's Shopping Cart Lock app
// https://betaskynet.com
#include "ui.hpp"
#include "shoppingcart_lock.hpp"
#include "ui_navigation.hpp"
@ -59,10 +57,10 @@ __attribute__((section(".external_app.app_shoppingcart_lock.application_informat
0x00,
},
/*.icon_color = */ ui::Color::red().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.menu_location = */ app_location_t::TX,
/*.desired_menu_position = */ -1,
/*.m4_app_tag = portapack::spi_flash::image_tag_audio_tx */ {'P', 'A', 'T', 'X'},
/*.m4_app_offset = */ 0x00000000,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'T', 'X'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}

View File

@ -1,8 +1,5 @@
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
// If you can read this, you're a nerd. :P
// Come join us at https://discord.gg/thepiratesreborn
// RocketGod's Shopping Cart Lock app
// https://betaskynet.com
#include "shoppingcart_lock.hpp"
using namespace portapack;
@ -51,6 +48,7 @@ void ShoppingCartLock::stop() {
audio::output::stop();
log_event("... Resetting State Variables");
transmitter_model.disable();
ready_signal = false;
thread_sync_complete = false;
looping = false;
@ -112,24 +110,18 @@ std::string ShoppingCartLock::list_wav_files() {
}
void ShoppingCartLock::wait_for_thread() {
uint32_t timeout = 1000;
uint32_t timeout = 100;
while (!ready_signal && timeout > 0) {
chThdYield();
timeout--;
}
if (!ready_signal) {
log_event("!!! Timeout waiting for ReplayThread");
}
}
void ShoppingCartLock::restart_playback() {
auto reader = std::make_unique<WAVFileReader>();
std::string file_path = (wav_dir / current_file).string();
if (!reader->open(file_path)) {
log_event("!!! Failed to reopen " + current_file + " for restart");
return;
}
if (!reader->open(file_path)) return;
replay_thread = std::make_unique<ReplayThread>(
std::move(reader),
@ -141,8 +133,9 @@ void ShoppingCartLock::restart_playback() {
EventDispatcher::send_message(message);
});
log_event(">> RESTARTING AUDIO <<");
log_event(">> SENDING <<");
audio::output::start();
transmitter_model.enable();
}
void ShoppingCartLock::play_audio(const std::string& filename, bool loop) {
@ -173,24 +166,33 @@ void ShoppingCartLock::play_audio(const std::string& filename, bool loop) {
wait_for_thread();
baseband::set_sample_rate(wav_sample_rate);
audio::set_rate(wav_sample_rate <= 12000 ? audio::Rate::Hz_12000 : wav_sample_rate <= 24000 ? audio::Rate::Hz_24000
: audio::Rate::Hz_48000);
log_event("... Configuring Baseband");
const uint32_t bb_sample_rate = 1536000;
const uint32_t decimation = bb_sample_rate / wav_sample_rate;
baseband::set_audiotx_config(
wav_sample_rate,
0.0f,
bb_sample_rate / decimation,
0.0f,
5.0f,
wav_bits_per_sample,
wav_bits_per_sample,
0,
false,
true,
false,
false,
false);
baseband::set_sample_rate(wav_sample_rate);
log_event("... Starting Audio Output");
audio::output::start();
volume_t max_volume = audio::headphone::volume_range().max;
audio::headphone::set_volume(max_volume);
log_event("... Setting Max Volume");
audio::headphone::set_volume(audio::headphone::volume_range().max);
transmitter_model.enable();
log_event(">>> Playback Started <<<");
}
ShoppingCartLock::ShoppingCartLock(NavigationView& nav)
@ -223,6 +225,9 @@ ShoppingCartLock::ShoppingCartLock(NavigationView& nav)
log_event("[+] INITIALIZATION COMPLETE");
log_event("[+] PORTAPACK ARMED");
log_event("[*] STATUS: READY");
log_event("This app use speaker to");
log_event("produce LF signal, but");
log_event("also trigger radio TX");
}
ShoppingCartLock::~ShoppingCartLock() {
@ -230,4 +235,4 @@ ShoppingCartLock::~ShoppingCartLock() {
baseband::shutdown();
}
} // namespace ui::external_app::shoppingcart_lock
} // namespace ui::external_app::shoppingcart_lock

View File

@ -1,8 +1,5 @@
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
// If you can read this, you're a nerd. :P
// Come join us at https://discord.gg/thepiratesreborn
// RocketGod's Shopping Cart Lock app
// https://betaskynet.com
#pragma once
#include "ui_widget.hpp"
@ -30,8 +27,8 @@ class ShoppingCartLock : public View {
void focus() override;
private:
static constexpr size_t BUFFER_SIZE = 512;
static constexpr size_t NUM_BUFFERS = 2;
static constexpr size_t BUFFER_SIZE = 8192;
static constexpr size_t NUM_BUFFERS = 8;
const std::string shoppingcart_lock_file{"shopping_cart_lock.wav"};
const std::string shoppingcart_unlock_file{"shopping_cart_unlock.wav"};

View File

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

View File

@ -51,4 +51,3 @@ 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,6 +53,5 @@ 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

@ -1,95 +0,0 @@
/*
* 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

@ -1,51 +0,0 @@
/*
* 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,6 +538,7 @@ 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,6 +280,265 @@ 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,6 +27,7 @@
#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,8 +32,7 @@ namespace ui {
AlphanumView::AlphanumView(
NavigationView& nav,
std::string& str,
size_t max_length,
uint8_t enter_mode)
size_t max_length)
: TextEntryView(nav, str, max_length) {
size_t n;
@ -77,7 +76,7 @@ AlphanumView::AlphanumView(
n++;
}
set_mode(enter_mode);
set_mode(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, uint8_t enter_mode);
AlphanumView(NavigationView& nav, std::string& str, size_t max_length);
AlphanumView(const AlphanumView&) = delete;
AlphanumView(AlphanumView&&) = delete;

View File

@ -29,7 +29,6 @@
#include "irq_controls.hpp"
#include "rf_path.hpp"
#include "freqman_db.hpp"
#include <cstddef>
#include <cstdint>
@ -38,10 +37,6 @@
#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 {
@ -268,12 +263,22 @@ class FrequencyStepView : public OptionsField {
: OptionsField{
parent_pos,
5,
{}} {
options_t options;
for (const auto& step : freqman_steps) {
options.emplace_back(step.first, step.second);
}
set_options(options);
{
{" 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},
}} {
}
};

View File

@ -1,6 +1,5 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyleft Mr. Robot 2025
*
* This file is part of PortaPack.
*
@ -22,6 +21,8 @@
#include "ui_spectrum.hpp"
#include "spectrum_color_lut.hpp"
#include "portapack.hpp"
using namespace portapack;
@ -101,15 +102,6 @@ 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();
@ -250,15 +242,6 @@ 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;
@ -288,12 +271,12 @@ void WaterfallWidget::on_channel_spectrum(
std::array<Color, 240> pixel_row;
for (size_t i = 0; i < 120; i++) {
const auto pixel_color = gradient.lut[spectrum.db[256 - 120 + i]];
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[256 - 120 + i]];
pixel_row[i] = pixel_color;
}
for (size_t i = 120; i < 240; i++) {
const auto pixel_color = gradient.lut[spectrum.db[i - 120]];
const auto pixel_color = spectrum_rgb3_lut[spectrum.db[i - 120]];
pixel_row[i] = pixel_color;
}
@ -304,15 +287,6 @@ 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(),
@ -331,22 +305,6 @@ 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,6 +1,5 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyleft Mr. Robot 2025
*
* This file is part of PortaPack.
*
@ -25,7 +24,6 @@
#include "ui.hpp"
#include "ui_widget.hpp"
#include "gradient.hpp"
#include "event_m0.hpp"
@ -80,11 +78,9 @@ 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;
@ -115,14 +111,9 @@ 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,9 +31,8 @@ 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, mode, on_done);
text_prompt(nav, str, str.length(), max_length, on_done);
}
void text_prompt(
@ -41,9 +40,8 @@ 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, mode);
auto te_view = nav.push<AlphanumView>(str, max_length);
te_view->set_cursor(cursor_pos);
te_view->on_changed = [on_done](std::string& value) {
if (on_done)

View File

@ -26,11 +26,6 @@
#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 {
@ -67,7 +62,6 @@ 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(
@ -75,7 +69,6 @@ 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,9 +146,7 @@ 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", "SignalGen", TX, Color::green(), &bitmap_icon_cwgen, new ViewFactory<SigGenView>()},
/* TRX ********************************************************************/
{"microphone", "Mic", TRX, Color::green(), &bitmap_icon_microphone, new ViewFactory<MicTXView>()},
{"signalgen", "Signal Gen", TX, Color::green(), &bitmap_icon_cwgen, new ViewFactory<SigGenView>()},
/* 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>()},
@ -345,7 +343,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 && pmem::ui_battery_charge_hint()) {
if (msg->on_charger && !was_charging) {
// Only show charging modal when transitioning to charging state
nav_.display_modal(
"CHARGING",
@ -382,7 +380,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()) status_icons.add(&button_fake_brightness);
if (!pmem::ui_hide_fake_brightness() && !pmem::config_lcd_inverted_mode()) status_icons.add(&button_fake_brightness);
if (battery::BatteryManagement::isDetected()) {
batt_was_inited = true;
if (!pmem::ui_hide_battery_icon()) {
@ -416,7 +414,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() ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
button_fake_brightness.set_foreground((pmem::apply_fake_brightness() & (!pmem::config_lcd_inverted_mode())) ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
set_dirty();
}
@ -843,22 +841,6 @@ 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,16 +379,6 @@ 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,9 +691,6 @@ 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;
@ -718,9 +715,6 @@ 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,8 +33,6 @@ using namespace portapack;
#include "file.hpp"
#include "portapack_persistent_memory.hpp"
#include <complex>
#include <cstring>
@ -145,13 +143,7 @@ void lcd_init() {
// REV = 1 (normally white)
// NL = 0b100111 (default)
// PCDIV = 0b000000 (default?)
/*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
io.lcd_data_write_command_and_data(0xB6, {0x0A, 0xA2, 0x27, 0x00});
// Power Control 1
// VRH[5:0]
@ -317,6 +309,14 @@ 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,6 +50,8 @@ 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();
return portapack::persistent_memory::apply_fake_brightness() & (!portapack::persistent_memory::config_lcd_inverted_mode());
}
bool IO::get_is_normally_black() {
return portapack::persistent_memory::config_lcd_normally_black();
bool IO::get_is_inverted() {
return portapack::persistent_memory::config_lcd_inverted_mode();
}
uint8_t IO::get_brightness() {
@ -90,7 +90,7 @@ uint8_t IO::get_brightness() {
}
void IO::update_cached_values() {
lcd_normally_black = get_is_normally_black();
inverted_enabled = get_is_inverted();
dark_cover_enabled = get_dark_cover();
brightness = get_brightness();
}

View File

@ -231,10 +231,10 @@ class IO {
return switches_raw;
}
bool lcd_normally_black = false;
bool inverted_enabled = false;
bool dark_cover_enabled = false;
uint8_t brightness = 0;
bool get_is_normally_black();
bool get_is_inverted();
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 (lcd_normally_black) return original_value;
if (inverted_enabled) 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_normally_black : 1;
bool lcd_inverted_mode : 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_normally_black(false),
lcd_inverted_mode(false),
encoder_dial_direction(false),
UNUSED_6(false),
UNUSED_7(false),
@ -981,9 +981,6 @@ 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;
@ -1038,9 +1035,6 @@ 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() {
@ -1090,12 +1084,12 @@ void set_config_freq_rx_correction(uint32_t v) {
data->frequency_rx_correction = v;
}
// IPS vs TFT
bool config_lcd_normally_black() {
return data->lcd_normally_black;
// LCD invert
bool config_lcd_inverted_mode() {
return data->lcd_inverted_mode;
}
void set_lcd_normally_black(bool v) {
data->lcd_normally_black = v;
void set_lcd_inverted_mode(bool v) {
data->lcd_inverted_mode = v;
}
// Rotary encoder dial settings
@ -1164,6 +1158,7 @@ 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_normally_black();
void set_lcd_normally_black(bool v);
bool config_lcd_inverted_mode();
void set_lcd_inverted_mode(bool v);
uint8_t encoder_dial_sensitivity();
void set_encoder_dial_sensitivity(uint8_t v);
@ -350,8 +350,6 @@ 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);
@ -369,7 +367,6 @@ 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,8 +73,7 @@ enum app_location_t : uint32_t {
DEBUG,
HOME,
SETTINGS,
GAMES,
TRX
GAMES
};
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()->bg_light->background);
painter.draw_rectangle({r.location(), {r.size().width(), 1}}, Theme::getInstance()->fg_light->foreground);
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.

Before

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 sorted(listdir(graphics_path)):
for file in 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

@ -137,12 +137,7 @@ def get_pp_device_linux():
parts = clean_line.split()
if len(parts) >= 2 and SDCARD_LABEL in parts[1]: # checker
print("found pp sd:")
device_path = parts[0]
#remove `- in it
device_path = device_path.replace('-', '')
device_path = device_path.replace('`', '')
print(device_path)
# if path valid
if not os.path.exists(device_path):
continue

View File

@ -1,14 +0,0 @@
# 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

@ -1,12 +0,0 @@
#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

@ -1,11 +0,0 @@
# 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

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

View File

@ -1,14 +0,0 @@
# 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