Merge 61378c98aaca1c795e7ed0e1d78c5f642339e531 into 806219f46e2a9de4f8d058ee0dc793b3e1a9f319

This commit is contained in:
sommermorgentraum 2025-04-15 16:51:12 +08:00 committed by GitHub
commit 11cb55ae14
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 914 additions and 2 deletions

View File

@ -207,6 +207,10 @@ set(EXTCPPSRC
#gfxEQ
external/gfxeq/main.cpp
external/gfxeq/ui_gfxeq.cpp
#waterfall designer
external/waterfall_designer/main.cpp
external/waterfall_designer/ui_waterfall_designer.cpp
)
set(EXTAPPLIST
@ -260,4 +264,5 @@ set(EXTAPPLIST
scanner
level
gfxeq
waterfall_designer
)

View File

@ -73,6 +73,7 @@ MEMORY
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
ram_external_app_waterfall_designer (rwx) : org = 0xADE30000, len = 32k
}
SECTIONS
@ -375,4 +376,10 @@ SECTIONS
KEEP(*(.external_app.app_gfxeq.application_information));
*(*ui*external_app*gfxeq*);
} > ram_external_app_gfxeq
.external_app_waterfall_designer : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_waterfall_designer.application_information));
*(*ui*external_app*waterfall_designer*);
} > ram_external_app_waterfall_designer
}

View File

@ -0,0 +1,83 @@
/*
* Copyright (C) 2025 Mark Thompson
*
* 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 "ui.hpp"
#include "ui_waterfall_designer.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::waterfall_designer {
void initialize_app(ui::NavigationView& nav) {
nav.push<WaterfallDesignerView>();
}
} // namespace ui::external_app::waterfall_designer
extern "C" {
__attribute__((section(".external_app.app_waterfall_designer.application_information"), used)) application_information_t _application_information_waterfall_designer = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::waterfall_designer::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Wt Design",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0xFE,
0x7F,
0x03,
0xC0,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0x03,
0xC0,
0xFF,
0xFF,
0xFB,
0xD7,
0xFE,
0x7F,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::cyan().v,
/*.menu_location = */ app_location_t::RX,
/*.desired_menu_position = */ 0,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {'P', 'C', 'A', 'P'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}

View File

@ -0,0 +1,553 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
*
* 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 "ui_waterfall_designer.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "ui_freqman.hpp"
#include "file_path.hpp"
#include "ui_fileman.hpp"
#include "file_reader.hpp"
#include "ui_textentry.hpp"
#include "usb_serial_asyncmsg.hpp"
using namespace portapack;
namespace ui::external_app::waterfall_designer {
WaterfallDesignerView::WaterfallDesignerView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_capture);
add_children({
&labels,
&field_frequency,
&field_frequency_step,
&field_rf_amp,
&field_lna,
&field_vga,
&option_bandwidth,
&record_view,
&menu_view,
&button_new,
&button_open,
&button_save,
&button_add_level,
&button_remove_level,
&button_edit_color,
&button_apply_setting,
});
waterfall = std::make_unique<spectrum::WaterfallView>();
add_child(waterfall.get());
menu_view.set_parent_rect({0, 1 * 16, screen_width, 7 * 16});
field_frequency_step.set_by_value(receiver_model.frequency_step());
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
receiver_model.set_frequency_step(v);
this->field_frequency.set_step(v);
};
freqman_set_bandwidth_option(SPEC_MODULATION, option_bandwidth);
option_bandwidth.on_change = [this](size_t, uint32_t new_capture_rate) {
/* Nyquist would imply a sample rate of 2x bandwidth, but because the ADC
* provides 2 values (I,Q), the sample_rate is equal to bandwidth here. */
/* capture_rate (bandwidth) is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz, when selected 1 Mhz BW ... */
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card. */
waterfall->stop();
// record_view determines the correct oversampling to apply and returns the actual sample rate.
// NB: record_view is what actually updates proc_capture baseband settings.
auto actual_sample_rate = record_view.set_sampling_rate(new_capture_rate);
// Update the radio model with the actual sampling rate.
receiver_model.set_sampling_rate(actual_sample_rate);
// Get suitable anti-aliasing BPF bandwidth for MAX2837 given the actual sample rate.
auto anti_alias_filter_bandwidth = filter_bandwidth_for_sampling_rate(actual_sample_rate);
receiver_model.set_baseband_bandwidth(anti_alias_filter_bandwidth);
capture_rate = new_capture_rate;
waterfall->start();
};
button_new.on_select = [this]() {
on_create_new_profile();
};
button_open.on_select = [this]() {
on_open_profile();
};
button_save.on_select = [this]() {
on_save_profile();
};
button_add_level.on_select = [this]() {
portapack::async_tx_enabled = true;
UsbSerialAsyncmsg::asyncmsg("-------- pl");
for (auto& line : profile_levels) {
UsbSerialAsyncmsg::asyncmsg(line);
}
UsbSerialAsyncmsg::asyncmsg("-------- pl end");
UsbSerialAsyncmsg::asyncmsg("-------- index");
UsbSerialAsyncmsg::asyncmsg(to_string_dec_uint(menu_view.highlighted_index()));
UsbSerialAsyncmsg::asyncmsg("-------- index end");
UsbSerialAsyncmsg::asyncmsg("\n\n\n\n\n");
on_add_level();
};
button_remove_level.on_select = [this]() {
portapack::async_tx_enabled = true;
UsbSerialAsyncmsg::asyncmsg("-------- pl");
for (auto& line : profile_levels) {
UsbSerialAsyncmsg::asyncmsg(line);
}
UsbSerialAsyncmsg::asyncmsg("-------- pl end");
UsbSerialAsyncmsg::asyncmsg("-------- index");
UsbSerialAsyncmsg::asyncmsg(to_string_dec_uint(menu_view.highlighted_index()));
UsbSerialAsyncmsg::asyncmsg("-------- index end");
UsbSerialAsyncmsg::asyncmsg("\n\n\n\n\n");
on_remove_level();
};
button_edit_color.on_select = [this]() {
on_edit_color();
};
button_apply_setting.on_select = [this]() {
if_apply_setting = true;
on_apply_current_to_wtf();
nav_.pop();
};
menu_view.on_highlight = [this]() {
portapack::async_tx_enabled = true;
UsbSerialAsyncmsg::asyncmsg("-------- pl");
for (auto& line : profile_levels) {
UsbSerialAsyncmsg::asyncmsg(line);
}
UsbSerialAsyncmsg::asyncmsg("-------- pl end");
UsbSerialAsyncmsg::asyncmsg("-------- index");
UsbSerialAsyncmsg::asyncmsg(to_string_dec_uint(menu_view.highlighted_index()));
UsbSerialAsyncmsg::asyncmsg("-------- index end");
UsbSerialAsyncmsg::asyncmsg("\n\n\n\n\n");
};
receiver_model.enable();
option_bandwidth.set_by_value(capture_rate);
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
button_save.hidden(true);
button_add_level.hidden(true);
button_remove_level.hidden(true);
button_edit_color.hidden(true);
button_apply_setting.hidden(true);
refresh_menu_view();
}
WaterfallDesignerView::WaterfallDesignerView(
NavigationView& nav,
ReceiverModel::settings_t override)
: WaterfallDesignerView(nav) {
// Settings to override when launched from another app (versus from AppSettings .ini file)
field_frequency.set_value(override.frequency_app_override);
}
WaterfallDesignerView::~WaterfallDesignerView() {
if (!if_apply_setting) restore_current_profile();
receiver_model.disable();
baseband::shutdown();
}
void WaterfallDesignerView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
waterfall->set_parent_rect(waterfall_rect);
}
void WaterfallDesignerView::focus() {
button_open.focus();
}
void WaterfallDesignerView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void WaterfallDesignerView::on_open_profile() {
auto open_view = nav_.push<FileLoadView>(".txt");
open_view->push_dir(waterfalls_dir);
open_view->on_changed = [this](std::filesystem::path new_file_path) {
on_profile_changed(new_file_path);
};
}
void WaterfallDesignerView::on_profile_changed(std::filesystem::path new_profile_path) {
current_profile_path = new_profile_path;
profile_levels.clear();
File playlist_file;
auto error = playlist_file.open(new_profile_path.string());
if (error) return;
menu_view.clear();
auto reader = FileLineReader(playlist_file);
for (const auto& line : reader) {
profile_levels.push_back(line);
}
for (auto& line : profile_levels) {
// remove empty lines
if (line == "\n" || line == "\r\n" || line == "\r") {
profile_levels.erase(std::remove(profile_levels.begin(), profile_levels.end(), line), profile_levels.end());
}
// remove line end \n etc
if (line.length() > 0 && (line[line.length() - 1] == '\n' || line[line.length() - 1] == '\r')) {
line = line.substr(0, line.length() - 1);
}
}
button_save.hidden(false);
button_add_level.hidden(false);
button_remove_level.hidden(false);
button_edit_color.hidden(false);
button_apply_setting.hidden(false);
refresh_menu_view();
on_apply_current_to_wtf();
}
void WaterfallDesignerView::refresh_menu_view() {
menu_view.clear();
for (const auto& line : profile_levels) {
if (line.length() == 0 || line[0] == '#') {
menu_view.add_item({line,
ui::Color::grey(),
&bitmap_icon_notepad,
[this](KeyEvent) {
button_add_level.focus();
}});
} else {
// index,R,G,B
size_t pos = 0;
size_t next_pos = 0;
// pass index
next_pos = line.find(',', pos);
if (next_pos == std::string::npos) continue;
pos = next_pos + 1;
// r
next_pos = line.find(',', pos);
if (next_pos == std::string::npos) continue;
uint8_t r = static_cast<uint8_t>(std::stoi(line.substr(pos, next_pos - pos)));
pos = next_pos + 1;
// g
next_pos = line.find(',', pos);
if (next_pos == std::string::npos) continue;
uint8_t g = static_cast<uint8_t>(std::stoi(line.substr(pos, next_pos - pos)));
pos = next_pos + 1;
// b
uint8_t b = static_cast<uint8_t>(std::stoi(line.substr(pos)));
ui::Color color = ui::Color(r, g, b);
menu_view.add_item({line,
color,
&bitmap_icon_cwgen,
[this](KeyEvent) {
button_remove_level.focus();
}});
}
}
set_dirty();
}
void WaterfallDesignerView::on_apply_current_to_wtf() {
std::filesystem::path system_read_path = "waterfall.txt";
copy_file(current_profile_path, system_read_path);
remove_child(waterfall.get());
waterfall.reset();
waterfall = std::make_unique<spectrum::WaterfallView>();
add_child(waterfall.get());
ui::Rect waterfall_rect{0, header_height, screen_rect().width(), screen_rect().height() - header_height};
waterfall->set_parent_rect(waterfall_rect);
set_dirty();
}
void WaterfallDesignerView::on_save_profile() {
if (current_profile_path.empty()) {
nav_.display_modal("Err", "No profile file loaded");
return;
} else if (profile_levels.empty()) {
nav_.display_modal("Err", "List is empty");
return;
}
File profile_file;
auto error = profile_file.open(current_profile_path.string(), false, false);
if (error) {
nav_.display_modal("Err", "open err");
return;
}
// clear file
profile_file.seek(0);
profile_file.truncate();
// write new data
for (const auto& entry : profile_levels) {
profile_file.write_line(entry);
}
nav_.display_modal("Save", "Saved profile\n" + current_profile_path.string());
}
void WaterfallDesignerView::on_add_level() {
if (menu_view.highlighted_index() >= profile_levels.size()) return;
if (profile_levels[menu_view.highlighted_index()].empty()) return;
if (profile_levels[menu_view.highlighted_index()][0] == '#') return;
if (profile_levels[menu_view.highlighted_index()].find(',') == std::string::npos) return;
size_t insert_pos = menu_view.highlighted_index();
std::string new_entry = "0,128,128,128";
profile_levels.insert(profile_levels.begin() + insert_pos, new_entry);
refresh_menu_view();
on_edit_color();
}
void WaterfallDesignerView::on_remove_level() {
if (menu_view.highlighted_index() >= profile_levels.size()) return;
if (profile_levels[menu_view.highlighted_index()].empty()) return;
if (profile_levels[menu_view.highlighted_index()][0] == '#') return;
if (profile_levels[menu_view.highlighted_index()].find(',') == std::string::npos) return;
profile_levels.erase(profile_levels.begin() + menu_view.highlighted_index());
refresh_menu_view();
}
void WaterfallDesignerView::on_edit_color() {
if (menu_view.highlighted_index() >= profile_levels.size()) return;
if (profile_levels[menu_view.highlighted_index()].empty()) return;
if (profile_levels[menu_view.highlighted_index()][0] == '#') return;
if (profile_levels[menu_view.highlighted_index()].find(',') == std::string::npos) return;
auto color_picker_view = nav_.push<WaterfallDesignerColorPickerView>(profile_levels[menu_view.highlighted_index()]);
color_picker_view->on_save = [this](std::string new_color) {
profile_levels[menu_view.highlighted_index()] = new_color;
refresh_menu_view();
on_apply_current_to_wtf();
};
}
void WaterfallDesignerView::backup_current_profile() {
std::filesystem::path curren_wtf_path = "waterfall.txt";
std::filesystem::path backup_path = waterfalls_dir / "wtf_des_bk.bk";
copy_file(curren_wtf_path, backup_path);
}
void WaterfallDesignerView::restore_current_profile() {
std::filesystem::path backup_path = waterfalls_dir / "wtf_des_bk.bk";
std::filesystem::path put_back_path = "waterfall.txt";
copy_file(backup_path, put_back_path);
delete_file(backup_path);
}
void WaterfallDesignerView::on_apply_setting() {
}
void WaterfallDesignerView::on_create_new_profile() {
text_prompt(
nav_,
file_name_buffer,
32 - 4, // fat32
ENTER_KEYBOARD_MODE_ALPHA,
[this](std::string& buffer) {
if (buffer.empty()) return;
if (buffer.length() < 4 || buffer.substr(buffer.length() - 4) != ".txt") {
buffer += ".txt";
}
File new_file;
auto error = new_file.create(waterfalls_dir / buffer);
if (error) {
nav_.display_modal("Err", "create file err");
return;
}
profile_levels.clear();
current_profile_path = waterfalls_dir / buffer;
on_profile_changed(current_profile_path);
});
}
WaterfallDesignerColorPickerView::WaterfallDesignerColorPickerView(NavigationView& nav, std::string color_str)
: nav_{nav},
color_str_{color_str} {
add_children({&labels,
&field_red,
&field_green,
&field_blue,
&field_step,
&field_index,
&progressbar,
&button_save});
progressbar.set_max(UINT8_MAX);
size_t pos = 0;
size_t next_pos = 0;
// index
next_pos = color_str.find(',', pos);
if (next_pos != std::string::npos) {
index_ = static_cast<uint8_t>(std::stoi(color_str.substr(pos, next_pos - pos)));
pos = next_pos + 1;
}
// r
next_pos = color_str.find(',', pos);
if (next_pos != std::string::npos) {
red_ = static_cast<uint8_t>(std::stoi(color_str.substr(pos, next_pos - pos)));
pos = next_pos + 1;
}
// g
next_pos = color_str.find(',', pos);
if (next_pos != std::string::npos) {
green_ = static_cast<uint8_t>(std::stoi(color_str.substr(pos, next_pos - pos)));
pos = next_pos + 1;
}
// b
blue_ = static_cast<uint8_t>(std::stoi(color_str.substr(pos)));
field_red.set_value(red_);
field_green.set_value(green_);
field_blue.set_value(blue_);
field_index.set_value(index_);
// cb
field_red.on_change = [this](int32_t) {
update_color_index();
};
field_green.on_change = [this](int32_t) {
update_color_index();
};
field_blue.on_change = [this](int32_t) {
update_color_index();
};
button_save.on_select = [this](Button&) {
if (on_save) on_save(build_color_str());
nav_.pop();
};
field_index.on_change = [this](int32_t) {
update_color_index();
};
field_step.on_change = [this](int32_t) {
field_red.set_step(field_step.value());
field_green.set_step(field_step.value());
field_blue.set_step(field_step.value());
field_index.set_step(field_step.value());
};
update_color_index();
}
void WaterfallDesignerColorPickerView::focus() {
button_save.focus();
}
void WaterfallDesignerColorPickerView::update_color_index() {
index_ = static_cast<uint8_t>(field_index.value());
red_ = static_cast<uint8_t>(field_red.value());
green_ = static_cast<uint8_t>(field_green.value());
blue_ = static_cast<uint8_t>(field_blue.value());
const Rect preview_rect{screen_width - 48, 1 * 16, 40, 40};
Painter painter_instance_2;
painter_instance_2.fill_rectangle(
{preview_rect.left(), preview_rect.top(), preview_rect.width(), preview_rect.height()},
ui::Color(red_, green_, blue_));
}
void WaterfallDesignerColorPickerView::paint(Painter& painter) {
// this is not duplicated code.
// because need to display color when enter,
// but it is too early to call update_color() in the constructor.
const Rect preview_rect{screen_width - 48, 1 * 16, 40, 40};
painter.fill_rectangle(
{preview_rect.left(), preview_rect.top(), preview_rect.width(), preview_rect.height()},
ui::Color(red_, green_, blue_));
}
std::string WaterfallDesignerColorPickerView::build_color_str() {
size_t index_pos = color_str_.find(',');
if (index_pos != std::string::npos) {
return color_str_.substr(0, index_pos + 1) +
to_string_dec_uint(red_) + "," +
to_string_dec_uint(green_) + "," +
to_string_dec_uint(blue_);
}
return to_string_dec_uint(index_) + "," + to_string_dec_uint(red_) + "," + to_string_dec_uint(green_) + "," + to_string_dec_uint(blue_);
}
} /* namespace ui::external_app::waterfall_designer */

View File

@ -0,0 +1,264 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
*
* 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 __WATERFALL_DESIGNER_APP_HPP__
#define __WATERFALL_DESIGNER_APP_HPP__
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "ui_spectrum.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "file_path.hpp"
namespace ui::external_app::waterfall_designer {
enum class ColorComponent {
RED,
GREEN,
BLUE
};
class WaterfallDesignerColorPickerView : public View {
public:
std::function<void(std::string)> on_save{};
WaterfallDesignerColorPickerView(NavigationView& nav, std::string color_str);
std::string title() const override { return "Color Picker"; };
void focus() override;
void paint(Painter& painter) override;
private:
NavigationView& nav_;
std::string color_str_;
uint8_t index_{0};
uint8_t red_{0};
uint8_t green_{0};
uint8_t blue_{0};
void update_color_index();
std::string build_color_str();
Labels labels{
{{0 * 8, 0 * 16}, "Index", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 2 * 16}, "Red", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 4 * 16}, "Green", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 6 * 16}, "Blue", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 8 * 16}, "Step", Theme::getInstance()->fg_light->foreground}};
NumberField field_index{
{0 * 8, 1 * 16},
3,
{0, 255},
1,
' '};
NumberField field_red{
{0 * 8, 3 * 16},
3,
{0, 255},
1,
' '};
NumberField field_green{
{0 * 8, 5 * 16},
3,
{0, 255},
1,
' '};
NumberField field_blue{
{0 * 8, 7 * 16},
3,
{0, 255},
1,
' '};
NumberField field_step{
{0 * 8, 9 * 16},
3,
{0, 255},
1,
' '};
ProgressBar progressbar{
{0 * 8,
screen_height - 4 * 16 - 2 * 16 - 1 * 16,
screen_width,
2 * 16}};
Button button_save{
{0, screen_height - 4 * 16, screen_width, 4 * 16},
"Save"};
};
class WaterfallDesignerView : public View {
public:
WaterfallDesignerView(NavigationView& nav);
WaterfallDesignerView(NavigationView& nav, ReceiverModel::settings_t override);
~WaterfallDesignerView();
void focus() override;
void set_parent_rect(const Rect new_parent_rect) override;
std::string title() const override { return "Wtf Design"; };
private:
uint32_t capture_rate{500000};
uint32_t file_format{0};
bool trim{false};
std::filesystem::path current_profile_path = "";
NavigationView& nav_;
RxRadioState radio_state_{ReceiverModel::Mode::Capture};
app_settings::SettingsManager settings_{
"rx_capture",
app_settings::Mode::RX,
{
{"capture_rate"sv, &capture_rate},
{"file_format"sv, &file_format},
{"trim"sv, &trim},
}};
Labels labels{
{{0 * 8, 1 * 16}, "Rate:", Theme::getInstance()->fg_light->foreground},
{{11 * 8, 1 * 16}, "Format:", Theme::getInstance()->fg_light->foreground},
};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
FrequencyStepView field_frequency_step{
{10 * 8, 0 * 16}};
RFAmpField field_rf_amp{
{16 * 8, 0 * 16}};
LNAGainField field_lna{
{18 * 8, 0 * 16}};
VGAGainField field_vga{
{21 * 8, 0 * 16}};
OptionsField option_bandwidth{
{24 * 8, 0 * 16},
5,
{}};
MenuView menu_view{};
NewButton button_new{
{0 * 8, 8 * 16, 4 * 8, 32},
{},
&bitmap_icon_file,
Theme::getInstance()->fg_blue->foreground};
NewButton button_open{
{4 * 8, 8 * 16, 4 * 8, 32},
{},
&bitmap_icon_load,
Theme::getInstance()->fg_blue->foreground};
NewButton button_save{
{8 * 8, 8 * 16, 4 * 8, 32},
{},
&bitmap_icon_save,
Theme::getInstance()->fg_blue->foreground};
NewButton button_add_level{
{12 * 8, 8 * 16, 4 * 8, 32},
{},
&bitmap_icon_add,
Theme::getInstance()->fg_blue->foreground};
NewButton button_remove_level{
{16 * 8, 8 * 16, 4 * 8, 32},
{},
&bitmap_icon_delete,
Theme::getInstance()->fg_blue->foreground};
NewButton button_edit_color{
{20 * 8, 8 * 16, 4 * 8, 32},
{},
&bitmap_icon_notepad,
Theme::getInstance()->fg_blue->foreground};
NewButton button_apply_setting{
{24 * 8, 8 * 16, 4 * 8, 32},
{},
&bitmap_icon_replay,
Theme::getInstance()->fg_blue->foreground};
void backup_current_profile();
void restore_current_profile();
void on_create_new_profile();
void on_open_profile();
void on_profile_changed(std::filesystem::path new_profile_path);
void on_save_profile();
void on_add_level();
void on_remove_level();
void on_edit_color();
void refresh_menu_view();
void on_apply_current_to_wtf(); // will restore if didn't apple, when distruct
void on_apply_setting(); // apply set
bool if_apply_setting{false};
/*NB:
this works as:
each time you change color, it apply as file realtime
however if you don't push the apply (play) btn, it would resotore in distructor,
if you push apply, it would apply and exit*/
std::vector<std::string> profile_levels{};
static constexpr ui::Dim header_height = 10 * 16;
RecordView record_view{// we still need it cuz it make waterfall correct
{screen_width, screen_height, 30 * 8, 1 * 16},
u"BBD_????.*",
captures_dir,
RecordView::FileType::RawS16,
16384,
3};
std::unique_ptr<spectrum::WaterfallView> waterfall{};
std::string file_name_buffer{}; // needed by text_prompy
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} /* namespace ui::external_app::waterfall_designer */
#endif /*__WATERFALL_DESIGNER_APP_HPP__*/

View File

@ -60,7 +60,7 @@
#include "ui_text_editor.hpp"
#include "ui_touchtunes.hpp"
#include "ui_weatherstation.hpp"
#include "ui_subghzd.hpp"
// #include "ui_subghzd.hpp"
#include "ui_battinfo.hpp"
#include "ui_external_items_menu_loader.hpp"
@ -135,7 +135,7 @@ const NavigationView::AppList NavigationView::appList = {
{"pocsag", "POCSAG", RX, Color::green(), &bitmap_icon_pocsag, new ViewFactory<POCSAGAppView>()},
{"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory<SondeView>()},
{"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory<SearchView>()},
{"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<SubGhzDView>()},
// {"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<SubGhzDView>()},
{"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory<WeatherView>()},
/* TX ********************************************************************/
{"aprstx", "APRS TX", TX, ui::Color::green(), &bitmap_icon_aprs, new ViewFactory<APRSTXView>()},