mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-04-19 07:25:56 -04:00
Compare commits
13 Commits
nightly-ta
...
next
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9aff3f4121 | ||
![]() |
6b6a00d511 | ||
![]() |
806219f46e | ||
![]() |
a153cbf30b | ||
![]() |
584af02dba | ||
![]() |
f344a1ca40 | ||
![]() |
511675c9a1 | ||
![]() |
7bbd608c90 | ||
![]() |
f941ae871a | ||
![]() |
eff214cc79 | ||
![]() |
319ed3e7b4 | ||
![]() |
288f6bd517 | ||
![]() |
809abb6842 |
@ -191,6 +191,7 @@ set(CPPSRC
|
||||
clock_manager.cpp
|
||||
core_control.cpp
|
||||
database.cpp
|
||||
gradient.cpp
|
||||
rfm69.cpp
|
||||
event_m0.cpp
|
||||
file_reader.cpp
|
||||
|
@ -21,7 +21,6 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ble_rx_app.hpp"
|
||||
#include "ble_rx_app.hpp"
|
||||
#include "ui_modemsetup.hpp"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
@ -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?
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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 */,
|
||||
|
@ -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();
|
||||
|
@ -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",
|
||||
};
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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()));
|
||||
|
5
firmware/application/external/external.cmake
vendored
5
firmware/application/external/external.cmake
vendored
@ -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
|
||||
)
|
||||
|
7
firmware/application/external/external.ld
vendored
7
firmware/application/external/external.ld
vendored
@ -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
|
||||
}
|
||||
|
36
firmware/application/external/gfxeq/main.cpp
vendored
Normal file
36
firmware/application/external/gfxeq/main.cpp
vendored
Normal 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
|
196
firmware/application/external/gfxeq/ui_gfxeq.cpp
vendored
Normal file
196
firmware/application/external/gfxeq/ui_gfxeq.cpp
vendored
Normal 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
|
138
firmware/application/external/gfxeq/ui_gfxeq.hpp
vendored
Normal file
138
firmware/application/external/gfxeq/ui_gfxeq.hpp
vendored
Normal 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", ¤t_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
|
@ -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
|
||||
|
@ -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";
|
||||
|
@ -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__ */
|
||||
|
95
firmware/application/gradient.cpp
Normal file
95
firmware/application/gradient.cpp
Normal 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;
|
||||
}
|
51
firmware/application/gradient.hpp
Normal file
51
firmware/application/gradient.hpp
Normal 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__ */
|
@ -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
|
||||
|
@ -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},
|
||||
|
@ -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__*/
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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>()},
|
||||
@ -147,6 +147,8 @@ const NavigationView::AppList NavigationView::appList = {
|
||||
{"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>()},
|
||||
/* 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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -73,7 +73,8 @@ enum app_location_t : uint32_t {
|
||||
DEBUG,
|
||||
HOME,
|
||||
SETTINGS,
|
||||
GAMES
|
||||
GAMES,
|
||||
TRX
|
||||
};
|
||||
|
||||
struct standalone_application_information_t {
|
||||
|
BIN
firmware/graphics/icon_tranceivers.png
Normal file
BIN
firmware/graphics/icon_tranceivers.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 185 B |
@ -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"))
|
||||
|
14
sdcard/WATERFALLS/aurora.txt
Normal file
14
sdcard/WATERFALLS/aurora.txt
Normal 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
|
12
sdcard/WATERFALLS/default.txt
Normal file
12
sdcard/WATERFALLS/default.txt
Normal 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
|
11
sdcard/WATERFALLS/flame.txt
Normal file
11
sdcard/WATERFALLS/flame.txt
Normal 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
|
8
sdcard/WATERFALLS/matrix.txt
Normal file
8
sdcard/WATERFALLS/matrix.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# 0% black
|
||||
0,0,0,0
|
||||
|
||||
# 50% green
|
||||
128,0,255,0
|
||||
|
||||
# 100% white
|
||||
255,255,255,255
|
14
sdcard/WATERFALLS/sunset.txt
Normal file
14
sdcard/WATERFALLS/sunset.txt
Normal 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
|
Loading…
x
Reference in New Issue
Block a user