Compare commits

..

9 Commits

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

* format

* format

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

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

* fix typo that found by Copilot

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-04-14 20:47:41 +02:00
gullradriel
f344a1ca40
fix comments (#2622) 2025-04-11 11:48:07 +00:00
gullradriel
511675c9a1
Fix default waterfall file (#2621)
* correct default settings from file
* correct colors names
2025-04-11 19:30:18 +08:00
haruk
7bbd608c90
Delete dead code in ble_rx_app.cpp (#2620)
Duplicate include on line 24 removed.
2025-04-11 10:49:09 +00:00
Oleg Belousov
f941ae871a
Custom waterfall colors (#2617)
* Custom waterfall gradient
* Installing a custom waterfall gradient via fileman
* default file for user friendly swap
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2025-04-11 10:46:04 +00:00
41 changed files with 425 additions and 333 deletions

View File

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

View File

@ -21,7 +21,6 @@
* Boston, MA 02110-1301, USA.
*/
#include "ble_rx_app.hpp"
#include "ble_rx_app.hpp"
#include "ui_modemsetup.hpp"

View File

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

View File

@ -419,6 +419,17 @@ void FileManBaseView::reload_current(bool reset_pagination) {
refresh_list();
}
void FileManBaseView::copy_waterfall(std::filesystem::path path) {
nav_.push<ModalMessageView>(
"Install", " Use this gradient file\n for all waterfalls?", YESNO,
[this, path](bool choice) {
if (choice) {
delete_file(default_gradient_file);
copy_file(path, default_gradient_file);
}
});
}
const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
const fs::path& ext) const {
size_t index = 0;
@ -685,7 +696,11 @@ bool FileManagerView::handle_file_open() {
auto ext = path.extension();
if (path_iequal(txt_ext, ext)) {
nav_.push<TextEditorView>(path);
if (path_iequal(current_path, u"/" + waterfalls_dir)) {
copy_waterfall(path);
} else {
nav_.push<TextEditorView>(path);
}
return true;
} else if (is_cxx_capture_file(path) || path_iequal(ppl_ext, ext)) {
// TODO: Enough memory to push?

View File

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

View File

@ -46,7 +46,7 @@ using options_db_t = std::vector<option_db_t>;
extern options_db_t freqman_modulations;
extern options_db_t freqman_bandwidths[5];
extern options_db_t freqman_steps;
// extern options_db_t freqman_steps; // now included via ui_receiver.hpp
extern options_db_t freqman_steps_short;
options_t dboptions_to_options(const options_db_t& dboptions) {

View File

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

View File

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

View File

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

View File

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

View File

@ -810,30 +810,29 @@ SetDisplayView::SetDisplayView(NavigationView& nav) {
&field_fake_brightness,
&button_save,
&button_cancel,
&checkbox_invert_switch,
&checkbox_ips_screen_switch,
&checkbox_brightness_switch});
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
checkbox_ips_screen_switch.set_value(pmem::config_lcd_normally_black());
button_save.on_select = [&nav, this](Button&) {
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
if (checkbox_invert_switch.value() != pmem::config_lcd_inverted_mode()) {
display.set_inverted(checkbox_invert_switch.value());
pmem::set_lcd_inverted_mode(checkbox_invert_switch.value());
if (checkbox_ips_screen_switch.value() != pmem::config_lcd_normally_black()) {
pmem::set_lcd_normally_black(checkbox_ips_screen_switch.value());
}
send_system_refresh();
nav.pop();
};
// only enable invert OR fake brightness
checkbox_invert_switch.on_select = [this](Checkbox&, bool v) {
checkbox_ips_screen_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_brightness_switch.set_value(false);
};
checkbox_brightness_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_invert_switch.set_value(false);
if (v) checkbox_ips_screen_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {

View File

@ -762,6 +762,8 @@ class SetDisplayView : public View {
{{1 * 8, 2 * 16}, "(has a small performance", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 3 * 16}, "impact when enabled).", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 8 * 16}, "Brightness:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 16}, "REBOOT TO APPLY SCREEN TYPE", Theme::getInstance()->fg_light->foreground},
};
OptionsField field_fake_brightness{
@ -776,10 +778,10 @@ class SetDisplayView : public View {
16,
"Enable brightness adjust"};
Checkbox checkbox_invert_switch{
{1 * 8, 10 * 16},
Checkbox checkbox_ips_screen_switch{
{1 * 8, 12 * 16},
23,
"Invert colors (For IPS)"};
"IPS Screen"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},

View File

@ -4221,6 +4221,44 @@ static constexpr Bitmap bitmap_icon_tpms{
{16, 16},
bitmap_icon_tpms_data};
static constexpr uint8_t bitmap_icon_tranceivers_data[] = {
0x80,
0x01,
0xC0,
0x03,
0xE0,
0x07,
0xB0,
0x0D,
0x98,
0x19,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x98,
0x19,
0xB0,
0x0D,
0xE0,
0x07,
0xC0,
0x03,
0x83,
0xC1,
0x03,
0xC0,
0xFF,
0xFF,
0xFF,
0xFF,
};
static constexpr Bitmap bitmap_icon_tranceivers{
{16, 16},
bitmap_icon_tranceivers_data};
static constexpr uint8_t bitmap_icon_transmit_data[] = {
0x80,
0x01,

View File

@ -80,7 +80,7 @@ bool DebugDumpView::debug_dump_func() {
pmem_dump_file.write_line("updown_converter: " + to_string_dec_int(config_updown_converter()));
pmem_dump_file.write_line("updown_frequency_rx_correction: " + to_string_dec_int(config_freq_rx_correction_updown()));
pmem_dump_file.write_line("updown_frequency_tx_correction: " + to_string_dec_int(config_freq_tx_correction_updown()));
pmem_dump_file.write_line("lcd_inverted_mode: " + to_string_dec_uint(config_lcd_inverted_mode()));
pmem_dump_file.write_line("lcd_normally_black: " + to_string_dec_uint(config_lcd_normally_black()));
pmem_dump_file.write_line("converter_frequency_offset: " + to_string_dec_int(config_converter_freq()));
pmem_dump_file.write_line("frequency_rx_correction: " + to_string_dec_uint(config_freq_rx_correction()));
pmem_dump_file.write_line("frequency_tx_correction: " + to_string_dec_uint(config_freq_tx_correction()));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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>()},
@ -380,7 +382,7 @@ void SystemStatusView::refresh() {
// Display "Disable speaker" icon only if AK4951 Codec which has separate speaker/headphone control
if (audio::speaker_disable_supported() && !pmem::ui_hide_speaker()) status_icons.add(&toggle_speaker);
if (!pmem::ui_hide_fake_brightness() && !pmem::config_lcd_inverted_mode()) status_icons.add(&button_fake_brightness);
if (!pmem::ui_hide_fake_brightness()) status_icons.add(&button_fake_brightness);
if (battery::BatteryManagement::isDetected()) {
batt_was_inited = true;
if (!pmem::ui_hide_battery_icon()) {
@ -414,7 +416,7 @@ void SystemStatusView::refresh() {
button_converter.set_foreground(pmem::config_converter() ? Theme::getInstance()->fg_red->foreground : Theme::getInstance()->fg_light->foreground);
// Fake Brightness
button_fake_brightness.set_foreground((pmem::apply_fake_brightness() & (!pmem::config_lcd_inverted_mode())) ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
button_fake_brightness.set_foreground(pmem::apply_fake_brightness() ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
set_dirty();
}
@ -841,6 +843,22 @@ void TransmittersMenuView::on_populate() {
add_external_items(nav_, app_location_t::TX, *this, return_icon ? 1 : 0);
}
/* TranceiversMenuView **************************************************/
TranceiversMenuView::TranceiversMenuView(NavigationView& nav)
: nav_(nav) {}
void TranceiversMenuView::on_populate() {
bool return_icon = pmem::show_gui_return_icon();
if (return_icon) {
add_items({{"..", Theme::getInstance()->fg_light->foreground, &bitmap_icon_previous, [this]() { nav_.pop(); }}});
}
add_apps(nav_, *this, TRX);
// add_external_items(nav_, app_location_t::TRX, *this, return_icon ? 1 : 0);
// this folder doesn't have external apps, comment to prevent pop the err msg.
// NB: when has external app someday, uncomment this.
}
/* UtilitiesMenuView *****************************************************/
UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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),
@ -1090,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
@ -1164,7 +1164,6 @@ void set_fake_brightness_level(uint8_t v) {
// Cycle through 4 brightness options: disabled -> enabled/50% -> enabled/25% -> enabled/12.5% -> disabled
void toggle_fake_brightness_level() {
bool fbe = apply_fake_brightness();
if (config_lcd_inverted_mode()) return; // for now only inverted mode OR fake brightness
if ((!fbe) || (data->fake_brightness_level >= BRIGHTNESS_12p5)) {
set_apply_fake_brightness(!fbe);
data->fake_brightness_level = BRIGHTNESS_50;

View File

@ -249,8 +249,8 @@ void set_config_audio_mute(bool v);
void set_config_speaker_disable(bool v);
void set_config_backlight_timer(const backlight_config_t& new_value);
void set_disable_touchscreen(bool v);
bool config_lcd_inverted_mode();
void set_lcd_inverted_mode(bool v);
bool config_lcd_normally_black();
void set_lcd_normally_black(bool v);
uint8_t encoder_dial_sensitivity();
void set_encoder_dial_sensitivity(uint8_t v);

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

View File

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

View File

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

View File

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

View File

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

View File

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