mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-16 03:22:59 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d5c8525afc | |||
| d9bbd1b9ff | |||
| b47cee435e | |||
| c078bac0e7 | |||
| 6177b08632 | |||
| 53a1bacd46 | |||
| 4c012a9493 | |||
| abb9b6c8ad | |||
| 6e2507d609 | |||
| 536981998b | |||
| 1a87f2d701 | |||
| 81e24d582d | |||
| 28a5fc5915 | |||
| ba4290cf0d | |||
| 692644d214 | |||
| 4aeaf94d2d | |||
| 8391ca8052 | |||
| 807c76346b | |||
| 308573918c | |||
| 74442f197d | |||
| b53c8e1f80 | |||
| c5f73cf8f4 | |||
| c1bf2620c7 | |||
| 8383363e74 | |||
| 6e34343bde | |||
| 01e4ff65a2 | |||
| 9e40e38f07 | |||
| 181624daf1 | |||
| f0614c882b | |||
| 14f42d1fe8 | |||
| 999f9e2ded | |||
| 61dc8a0225 | |||
| 0b2d5f75cc | |||
| 3c489e1a81 | |||
| 910fd82051 |
@@ -1,10 +1,6 @@
|
||||
> [!WARNING]
|
||||
> Recently, we have become aware of certain individuals distributing prepackaged packages of Mayhem for a fee, and we want to bring this to your attention to ensure the integrity and security of our project.
|
||||
>
|
||||
> We would like to clarify that none of the contributors to Mayhem receive any monetary compensation for their efforts. All the work put into this project is driven by passion and hobby, and we are committed to providing the community with an open-source solution that remains free of charge.
|
||||
>
|
||||
> __IF YOU'VE PAID FOR MAYHEM OR ANY PREPACKAGED PACKAGES, YOU'RE BEING SCAMMED.__ Seek a refund from your card company and report the issue immediately.
|
||||
> The only legitimate link leading to our repositories is the organization [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware). Please ensure that any download or access to our software is done exclusively through these trusted sources.
|
||||
> __IF YOU'VE PAID FOR MAYHEM OR ANY PREPACKAGED PACKAGES, YOU'RE BEING SCAMMED.__
|
||||
> The only legitimate link leading to our repositories is the organization [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware).
|
||||
|
||||
# PortaPack Mayhem
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ set(CPPSRC
|
||||
event_m0.cpp
|
||||
file_reader.cpp
|
||||
file.cpp
|
||||
file_path.cpp
|
||||
freqman_db.cpp
|
||||
freqman.cpp
|
||||
io_convert.cpp
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@@ -40,7 +41,7 @@ using namespace portapack;
|
||||
|
||||
namespace {
|
||||
fs::path get_settings_path(const std::string& app_name) {
|
||||
return fs::path{SETTINGS_DIR} / app_name + u".ini";
|
||||
return settings_dir / app_name + u".ini";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -156,7 +157,7 @@ bool save_settings(std::string_view store_name, const SettingBindings& bindings)
|
||||
File f;
|
||||
auto path = get_settings_path(std::string{store_name});
|
||||
|
||||
ensure_directory(SETTINGS_DIR);
|
||||
ensure_directory(settings_dir);
|
||||
auto error = f.create(path);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
@@ -36,9 +36,6 @@
|
||||
#include "max283x.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
// Folder to store app settings, pmem_fileflag, and date_fileflag
|
||||
#define SETTINGS_DIR u"/SETTINGS"
|
||||
|
||||
// Bring in the string_view literal.
|
||||
using std::literals::operator""sv;
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace acars;
|
||||
@@ -77,7 +78,7 @@ ACARSAppView::ACARSAppView(NavigationView& nav)
|
||||
|
||||
logger = std::make_unique<ACARSLogger>();
|
||||
if (logger)
|
||||
logger->append(LOG_ROOT_DIR "/ACARS.TXT");
|
||||
logger->append(logs_dir / u"ACARS.TXT");
|
||||
}
|
||||
|
||||
ACARSAppView::~ACARSAppView() {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
class ACARSLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "database.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
@@ -399,7 +400,7 @@ AISAppView::AISAppView(NavigationView& nav)
|
||||
|
||||
logger = std::make_unique<AISLogger>();
|
||||
if (logger) {
|
||||
logger->append(LOG_ROOT_DIR "/AIS.TXT");
|
||||
logger->append(logs_dir / u"AIS.TXT");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "ui_text.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
@@ -100,7 +101,7 @@ BLECommView::BLECommView(NavigationView& nav)
|
||||
logging = v;
|
||||
|
||||
if (logger && logging)
|
||||
logger->append(LOG_ROOT_DIR "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
logger->append(logs_dir.string() + "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
};
|
||||
|
||||
options_channel.on_change = [this](size_t, int32_t i) {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
class BLECommLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -470,10 +470,7 @@ BLERxView::BLERxView(NavigationView& nav)
|
||||
logging = v;
|
||||
|
||||
if (logger && logging)
|
||||
logger->append(
|
||||
"BLERX/Logs"
|
||||
"/BLELOG_" +
|
||||
to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
logger->append(blerx_dir.string() + "/Logs/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
};
|
||||
check_log.set_value(logging);
|
||||
|
||||
|
||||
@@ -36,12 +36,13 @@
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "usb_serial_thread.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
class BLELogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
@@ -133,7 +134,7 @@ class BleRecentEntryDetailView : public View {
|
||||
void on_save_file(const std::string value, BLETxPacket packetToSave);
|
||||
bool saveFile(const std::filesystem::path& path, BLETxPacket packetToSave);
|
||||
std::string packetFileBuffer{};
|
||||
std::filesystem::path packet_save_path{u"BLERX/Lists/????.csv"};
|
||||
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
|
||||
|
||||
static constexpr uint8_t total_data_lines{5};
|
||||
|
||||
@@ -250,9 +251,9 @@ class BLERxView : public View {
|
||||
uint64_t total_count = 0;
|
||||
std::vector<std::string> searchList{};
|
||||
|
||||
std::filesystem::path find_packet_path{u"BLERX/Find/????.TXT"};
|
||||
std::filesystem::path log_packets_path{u"BLERX/Logs/????.TXT"};
|
||||
std::filesystem::path packet_save_path{u"BLERX/Lists/????.csv"};
|
||||
std::filesystem::path find_packet_path{blerx_dir / u"Find/????.TXT"};
|
||||
std::filesystem::path log_packets_path{blerx_dir / u"Logs/????.TXT"};
|
||||
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
|
||||
|
||||
static constexpr auto header_height = 4 * 16;
|
||||
static constexpr auto switch_button_height = 3 * 16;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "replay_thread.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
@@ -44,7 +45,7 @@
|
||||
|
||||
class BLELoggerTx {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
@@ -138,7 +139,7 @@ class BLETxView : public View {
|
||||
uint32_t prev_value{0};
|
||||
|
||||
std::filesystem::path file_path{};
|
||||
std::filesystem::path packet_save_path{u"BLETX/BLETX_????.TXT"};
|
||||
std::filesystem::path packet_save_path{bletx_dir / u"BLETX_????.TXT"};
|
||||
uint8_t channel_number = 37;
|
||||
bool auto_channel = false;
|
||||
|
||||
@@ -165,7 +166,7 @@ class BLETxView : public View {
|
||||
|
||||
std::unique_ptr<FileWrapper> dataFileWrapper{};
|
||||
File dataFile{};
|
||||
std::filesystem::path dataTempFilePath{u"BLETX/dataFileTemp.TXT"};
|
||||
std::filesystem::path dataTempFilePath{bletx_dir / u"dataFileTemp.TXT"};
|
||||
std::vector<uint16_t> markedBytes{};
|
||||
CursorPos cursor_pos{};
|
||||
uint8_t marked_counter = 0;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -101,7 +102,7 @@ class CaptureAppView : public View {
|
||||
RecordView record_view{
|
||||
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
|
||||
u"BBD_????.*",
|
||||
u"CAPTURES",
|
||||
captures_dir,
|
||||
RecordView::FileType::RawS16,
|
||||
16384,
|
||||
3};
|
||||
|
||||
@@ -31,6 +31,7 @@ using namespace portapack;
|
||||
|
||||
#include "crc.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace ert {
|
||||
|
||||
@@ -129,7 +130,7 @@ ERTAppView::ERTAppView(NavigationView& nav)
|
||||
|
||||
logger = std::make_unique<ERTLogger>();
|
||||
if (logger) {
|
||||
logger->append(LOG_ROOT_DIR "/ERT.TXT");
|
||||
logger->append(logs_dir / u"ERT.TXT");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace pocsag;
|
||||
@@ -115,7 +116,7 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
|
||||
: FILTER_DROP;
|
||||
}
|
||||
|
||||
logger.append(LOG_ROOT_DIR "/POCSAG.TXT");
|
||||
logger.append(logs_dir / u"POCSAG.TXT");
|
||||
|
||||
field_squelch.set_value(receiver_model.squelch_level());
|
||||
field_squelch.on_change = [this](int32_t v) {
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
class POCSAGLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "tonesets.hpp"
|
||||
#include "ui_tone_key.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace tonekey;
|
||||
using namespace portapack;
|
||||
@@ -164,7 +165,7 @@ void SoundBoardView::refresh_list() {
|
||||
|
||||
// List directories and files, put directories up top
|
||||
uint32_t count = 0;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(u"WAV", u"*")) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(wav_dir, u"*")) {
|
||||
if (std::filesystem::is_regular_file(entry.status())) {
|
||||
if (entry.path().string().length()) {
|
||||
auto entry_extension = entry.path().extension().string();
|
||||
@@ -173,7 +174,7 @@ void SoundBoardView::refresh_list() {
|
||||
c = toupper(c);
|
||||
|
||||
if (entry_extension == ".WAV") {
|
||||
if (reader->open(u"/WAV/" + entry.path().native())) {
|
||||
if (reader->open(wav_dir / entry.path())) {
|
||||
if ((reader->channels() == 1) && ((reader->bits_per_sample() == 8) || (reader->bits_per_sample() == 16))) {
|
||||
// sounds[c].ms_duration = reader->ms_duration();
|
||||
// sounds[c].path = u"WAV/" + entry.path().native();
|
||||
|
||||
@@ -30,6 +30,7 @@ using namespace portapack;
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace tpms {
|
||||
|
||||
@@ -176,7 +177,7 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
|
||||
logger = std::make_unique<TPMSLogger>();
|
||||
if (logger) {
|
||||
logger->append(LOG_ROOT_DIR "/TPMS.TXT");
|
||||
logger->append(logs_dir / u"TPMS.TXT");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ AboutView::AboutView(NavigationView& nav) {
|
||||
}
|
||||
|
||||
void AboutView::update() {
|
||||
if (++timer > 200) {
|
||||
if (++timer > 400) {
|
||||
timer = 0;
|
||||
|
||||
switch (++frame) {
|
||||
@@ -23,7 +23,7 @@ void AboutView::update() {
|
||||
console.writeln(STR_COLOR_DARK_YELLOW "Mayhem:");
|
||||
console.writeln("eried,euquiq,gregoryfenton");
|
||||
console.writeln("johnelder,jwetzell,nnemanjan00");
|
||||
console.writeln("N0vaPixel,klockee,GullCode");
|
||||
console.writeln("N0vaPixel,klockee,gullradriel");
|
||||
console.writeln("jamesshao8,ITAxReal,rascafr");
|
||||
console.writeln("mcules,dqs105,strijar");
|
||||
console.writeln("zhang00963,RedFox-Fr,aldude999");
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -390,7 +391,7 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
|
||||
};
|
||||
|
||||
logger = std::make_unique<ADSBLogger>();
|
||||
logger->append(LOG_ROOT_DIR "/ADSB.TXT");
|
||||
logger->append(logs_dir / u"ADSB.TXT");
|
||||
|
||||
receiver_model.enable();
|
||||
baseband::set_adsb();
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -111,7 +112,7 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
|
||||
|
||||
logger = std::make_unique<APRSLogger>();
|
||||
if (logger)
|
||||
logger->append(LOG_ROOT_DIR "/APRS.TXT");
|
||||
logger->append(logs_dir / u"APRS.TXT");
|
||||
|
||||
baseband::set_aprs(1200);
|
||||
|
||||
|
||||
@@ -36,10 +36,11 @@
|
||||
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
class APRSLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
@@ -232,7 +233,7 @@ class APRSRxView : public View {
|
||||
RecordView record_view{
|
||||
{0 * 8, 1 * 16, 30 * 8, 1 * 16},
|
||||
u"AFS_????.WAV",
|
||||
u"APRS",
|
||||
aprs_dir,
|
||||
RecordView::FileType::WAV,
|
||||
4096,
|
||||
4};
|
||||
|
||||
@@ -402,6 +402,7 @@ void FileSaveView::refresh_widgets() {
|
||||
void FileManagerView::refresh_widgets(const bool v) {
|
||||
button_rename.hidden(v);
|
||||
button_delete.hidden(v);
|
||||
button_clean.hidden(v);
|
||||
button_cut.hidden(v);
|
||||
button_copy.hidden(v);
|
||||
button_paste.hidden(v);
|
||||
@@ -449,7 +450,7 @@ void FileManagerView::on_rename(std::string_view hint) {
|
||||
|
||||
void FileManagerView::on_delete() {
|
||||
if (is_directory(get_selected_full_path()) && !is_empty_directory(get_selected_full_path())) {
|
||||
nav_.display_modal("Delete", "Directory not empty!");
|
||||
nav_.display_modal("Delete", " Folder is not empty;\n Use \"Clean\" button to\n empty it first.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -474,6 +475,31 @@ void FileManagerView::on_delete() {
|
||||
});
|
||||
}
|
||||
|
||||
void FileManagerView::on_clean() {
|
||||
if (is_empty_directory(get_selected_full_path())) {
|
||||
nav_.display_modal("Clean", "Folder is Empty;\nUse \"Delete\" button instead\nof \"Clean\" button to delete\nit.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto path_name = is_directory(get_selected_full_path()) ? get_selected_full_path() : get_selected_full_path().parent_path();
|
||||
|
||||
// selected either a single file (delete files in this directory) or a directory that is not empty (del sub files)
|
||||
nav_.push<ModalMessageView>(
|
||||
"Clean", " ALL FILES in this folder\n (excluding sub-folders)\n will be DELETED!\n\n Delete all files?", YESNO,
|
||||
[this, path_name](bool choice) {
|
||||
if (choice) {
|
||||
std::vector<std::filesystem::path> file_list;
|
||||
file_list = scan_root_files(path_name, u"*");
|
||||
|
||||
for (const auto& file_name : file_list) {
|
||||
std::filesystem::path current_full_path = path_name / file_name;
|
||||
delete_file(current_full_path);
|
||||
}
|
||||
reload_current();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FileManagerView::on_new_dir() {
|
||||
name_buffer = "";
|
||||
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) {
|
||||
@@ -560,6 +586,7 @@ FileManagerView::FileManagerView(
|
||||
&text_date,
|
||||
&button_rename,
|
||||
&button_delete,
|
||||
&button_clean,
|
||||
&button_cut,
|
||||
&button_copy,
|
||||
&button_paste,
|
||||
@@ -606,6 +633,11 @@ FileManagerView::FileManagerView(
|
||||
on_delete();
|
||||
};
|
||||
|
||||
button_clean.on_select = [this]() {
|
||||
if (selected_is_valid())
|
||||
on_clean();
|
||||
};
|
||||
|
||||
button_cut.on_select = [this]() {
|
||||
if (selected_is_valid() && !get_selected_entry().is_directory) {
|
||||
clipboard_path = get_selected_full_path();
|
||||
|
||||
@@ -207,6 +207,7 @@ class FileManagerView : public FileManBaseView {
|
||||
void refresh_widgets(const bool v);
|
||||
void on_rename(std::string_view hint);
|
||||
void on_delete();
|
||||
void on_clean();
|
||||
void on_paste();
|
||||
void on_new_dir();
|
||||
void on_new_file();
|
||||
@@ -227,11 +228,17 @@ class FileManagerView : public FileManBaseView {
|
||||
Color::dark_blue()};
|
||||
|
||||
NewButton button_delete{
|
||||
{4 * 8, 29 * 8, 4 * 8, 32},
|
||||
{9 * 8, 34 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_trash,
|
||||
Color::red()};
|
||||
|
||||
NewButton button_clean{
|
||||
{13 * 8, 34 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_clean,
|
||||
Color::red()};
|
||||
|
||||
NewButton button_cut{
|
||||
{9 * 8, 29 * 8, 4 * 8, 32},
|
||||
{},
|
||||
@@ -269,20 +276,22 @@ class FileManagerView : public FileManBaseView {
|
||||
Color::orange()};
|
||||
|
||||
NewButton button_rename_timestamp{
|
||||
{4 * 8, 34 * 8, 4 * 8, 32},
|
||||
|
||||
{4 * 8, 29 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_options_datetime,
|
||||
Color::orange(),
|
||||
Color::dark_blue(),
|
||||
/*vcenter*/ true};
|
||||
|
||||
NewButton button_open_iq_trim{
|
||||
{9 * 8, 34 * 8, 4 * 8, 32},
|
||||
|
||||
{4 * 8, 34 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_trim,
|
||||
Color::orange()};
|
||||
|
||||
NewButton button_show_hidden_files{
|
||||
{13 * 8, 34 * 8, 4 * 8, 32},
|
||||
{17 * 8, 34 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_hide,
|
||||
Color::dark_grey()};
|
||||
|
||||
@@ -23,11 +23,10 @@
|
||||
#include "ui_flash_utility.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
static const char16_t* firmware_folder = u"/FIRMWARE";
|
||||
|
||||
// Firmware image validation
|
||||
static const char* hackrf_magic = "HACKRFFW";
|
||||
#define FIRMWARE_INFO_AREA_OFFSET 0x400
|
||||
@@ -84,30 +83,29 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
|
||||
|
||||
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
|
||||
|
||||
ensure_directory(firmware_folder);
|
||||
ensure_directory(firmware_dir);
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.bin")) {
|
||||
auto filename = entry.path().filename();
|
||||
auto path = entry.path().native();
|
||||
auto add_firmware_items = [&](
|
||||
const std::filesystem::path& folder_path,
|
||||
const std::filesystem::path& wild,
|
||||
ui::Color color) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(folder_path, wild)) {
|
||||
auto filename = entry.path().filename();
|
||||
auto path = entry.path().native();
|
||||
|
||||
menu_view.add_item({filename.string().substr(0, max_filename_length),
|
||||
ui::Color::red(),
|
||||
&bitmap_icon_temperature,
|
||||
[this, path](KeyEvent) {
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.tar")) {
|
||||
auto filename = entry.path().filename();
|
||||
auto path = entry.path().native();
|
||||
menu_view.add_item({filename.string().substr(0, max_filename_length),
|
||||
color,
|
||||
&bitmap_icon_temperature,
|
||||
[this, path](KeyEvent) {
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
};
|
||||
|
||||
menu_view.add_item({filename.string().substr(0, max_filename_length),
|
||||
ui::Color::purple(),
|
||||
&bitmap_icon_temperature,
|
||||
[this, path](KeyEvent) {
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
add_firmware_items(firmware_dir, u"*.bin", ui::Color::red());
|
||||
add_firmware_items(firmware_dir, u"*.tar", ui::Color::purple());
|
||||
|
||||
// add_firmware_items(user_firmware_folder,u"*.bin", ui::Color::purple());
|
||||
}
|
||||
|
||||
void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) {
|
||||
@@ -117,7 +115,7 @@ void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path
|
||||
YESNO,
|
||||
[this, path](bool choice) {
|
||||
if (choice) {
|
||||
std::u16string full_path = std::u16string(u"FIRMWARE/") + path;
|
||||
std::filesystem::path::string_type full_path = firmware_dir.native() + u"/" + path;
|
||||
this->flash_firmware(full_path);
|
||||
}
|
||||
});
|
||||
@@ -179,4 +177,4 @@ void FlashUtilityView::focus() {
|
||||
menu_view.focus();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui */
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -228,7 +229,7 @@ void FrequencyManagerView::on_edit_freq() {
|
||||
|
||||
void FrequencyManagerView::on_edit_desc() {
|
||||
temp_buffer_ = current_entry().description;
|
||||
text_prompt(nav_, temp_buffer_, desc_edit_max, [this](std::string& new_desc) {
|
||||
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, [this](std::string& new_desc) {
|
||||
auto entry = current_entry();
|
||||
entry.description = std::move(new_desc);
|
||||
db_.replace_entry(current_index(), entry);
|
||||
|
||||
@@ -40,8 +40,6 @@ class FreqManBaseView : public View {
|
||||
|
||||
void focus() override;
|
||||
|
||||
static constexpr size_t desc_edit_max = 0x80;
|
||||
|
||||
protected:
|
||||
using options_t = OptionsField::options_t;
|
||||
|
||||
@@ -63,11 +61,11 @@ class FreqManBaseView : public View {
|
||||
|
||||
/* The top section (category) is 20px tall. */
|
||||
Labels label_category{
|
||||
{{0, 2}, "Category:", Color::light_grey()}};
|
||||
{{0, 2}, "F:", Color::light_grey()}};
|
||||
|
||||
OptionsField options_category{
|
||||
{9 * 8, 2},
|
||||
14 /* length */,
|
||||
{3 * 8, 2},
|
||||
20 /* length */,
|
||||
{}};
|
||||
|
||||
FreqManUIList freqlist_view{
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
@@ -134,7 +135,7 @@ FskxRxMainView::FskxRxMainView(NavigationView& nav)
|
||||
field_frequency.set_value(initial_target_frequency);
|
||||
deviation_frequency.set_value(initial_deviation);
|
||||
|
||||
logger.append(LOG_ROOT_DIR "/FSKRX.TXT");
|
||||
logger.append(logs_dir / u"FSKRX.TXT");
|
||||
|
||||
baseband::set_fsk(initial_deviation);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
class FskRxLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "complex.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
@@ -47,7 +48,7 @@ IQTrimView::IQTrimView(NavigationView& nav)
|
||||
|
||||
field_path.on_select = [this](TextField&) {
|
||||
auto open_view = nav_.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(u"CAPTURES");
|
||||
open_view->push_dir(captures_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
open_file(path);
|
||||
};
|
||||
|
||||
@@ -35,6 +35,30 @@ using portapack::memory::map::backup_ram;
|
||||
|
||||
namespace ui {
|
||||
|
||||
// Function to map the value from one range to another
|
||||
int32_t LevelView::map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh) {
|
||||
return toLow + (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow);
|
||||
}
|
||||
|
||||
void LevelView::m4_manage_stat_update() {
|
||||
if (audio_mode) {
|
||||
if (radio_mode == WFM_MODULATION || radio_mode == SPEC_MODULATION) {
|
||||
shared_memory.request_m4_performance_counter = 1;
|
||||
} else {
|
||||
shared_memory.request_m4_performance_counter = 2;
|
||||
}
|
||||
if (radio_mode == SPEC_MODULATION) {
|
||||
beep = true;
|
||||
}
|
||||
} else {
|
||||
shared_memory.request_m4_performance_counter = 2;
|
||||
if (radio_mode == SPEC_MODULATION) {
|
||||
beep = false;
|
||||
baseband::request_beep_stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LevelView::focus() {
|
||||
button_frequency.focus();
|
||||
}
|
||||
@@ -62,18 +86,15 @@ LevelView::LevelView(NavigationView& nav)
|
||||
&freq_stats_rssi,
|
||||
&freq_stats_db,
|
||||
&freq_stats_rx,
|
||||
&audio_mode,
|
||||
&text_beep_squelch,
|
||||
&field_beep_squelch,
|
||||
&field_audio_mode,
|
||||
&peak_mode,
|
||||
&rssi,
|
||||
&rssi_graph});
|
||||
|
||||
// activate vertical bar mode
|
||||
rssi.set_vertical_rssi(true);
|
||||
// activate counters for RxSat
|
||||
shared_memory.request_m4_performance_counter = 2;
|
||||
|
||||
change_mode(NFM_MODULATION); // Start on AM
|
||||
field_mode.set_by_value(NFM_MODULATION); // Reflect the mode into the manual selector
|
||||
|
||||
freq_ = receiver_model.target_frequency();
|
||||
button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
|
||||
@@ -87,6 +108,11 @@ LevelView::LevelView(NavigationView& nav)
|
||||
};
|
||||
};
|
||||
|
||||
field_beep_squelch.set_value(beep_squelch);
|
||||
field_beep_squelch.on_change = [this](int32_t v) {
|
||||
beep_squelch = v;
|
||||
};
|
||||
|
||||
button_frequency.on_change = [this]() {
|
||||
int64_t def_step = freqman_entry_get_step_value(step_mode.selected_index());
|
||||
freq_ = freq_ + (button_frequency.get_encoder_delta() * def_step);
|
||||
@@ -102,17 +128,14 @@ LevelView::LevelView(NavigationView& nav)
|
||||
button_frequency.set_text("<" + to_string_short_freq(freq_) + " MHz>");
|
||||
};
|
||||
|
||||
freqman_set_modulation_option(field_mode);
|
||||
field_mode.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
if (v != -1) {
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
change_mode(v);
|
||||
if (audio_mode.selected_index() != 0) {
|
||||
audio::output::start();
|
||||
}
|
||||
receiver_model.enable();
|
||||
}
|
||||
};
|
||||
field_mode.set_by_value(radio_mode); // Reflect the mode into the manual selector
|
||||
field_bw.set_selected_index(radio_bw);
|
||||
|
||||
rssi_resolution.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
if (v != -1) {
|
||||
@@ -120,15 +143,18 @@ LevelView::LevelView(NavigationView& nav)
|
||||
}
|
||||
};
|
||||
|
||||
audio_mode.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
field_audio_mode.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
audio_mode = v;
|
||||
if (v == 0) {
|
||||
audio::output::stop();
|
||||
} else if (v == 1) {
|
||||
audio::set_rate(audio_sampling_rate);
|
||||
audio::output::start();
|
||||
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
|
||||
} else {
|
||||
}
|
||||
m4_manage_stat_update(); // rx_sat hack
|
||||
};
|
||||
field_audio_mode.set_selected_index(audio_mode);
|
||||
|
||||
peak_mode.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
if (v == 0) {
|
||||
@@ -142,7 +168,6 @@ LevelView::LevelView(NavigationView& nav)
|
||||
peak_mode.set_selected_index(2);
|
||||
rssi_resolution.set_selected_index(1);
|
||||
// FILL STEP OPTIONS
|
||||
freqman_set_modulation_option(field_mode);
|
||||
freqman_set_step_option_short(step_mode);
|
||||
freq_stats_rssi.set_style(&Styles::white);
|
||||
freq_stats_db.set_style(&Styles::white);
|
||||
@@ -168,13 +193,28 @@ void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
last_min_rssi = rssi_graph.get_graph_min();
|
||||
last_avg_rssi = rssi_graph.get_graph_avg();
|
||||
last_max_rssi = rssi_graph.get_graph_max();
|
||||
freq_stats_rssi.set("RSSI: " + to_string_dec_uint(last_min_rssi) + "/" + to_string_dec_uint(last_avg_rssi) + "/" + to_string_dec_uint(last_max_rssi) + ", dt: " + to_string_dec_uint(rssi_graph.get_graph_delta()));
|
||||
freq_stats_rssi.set("RSSI: " + to_string_dec_uint(last_min_rssi) + "/" + to_string_dec_uint(last_avg_rssi) + "/" + to_string_dec_uint(last_max_rssi));
|
||||
}
|
||||
|
||||
if (beep && statistics.max_db > beep_squelch) {
|
||||
baseband::request_audio_beep(map(statistics.max_db, -100, 20, 400, 2600), 24000, 150);
|
||||
}
|
||||
|
||||
// refresh sat
|
||||
if (radio_mode == SPEC_MODULATION || (radio_mode == WFM_MODULATION && audio_mode == 1)) {
|
||||
Style style_freq_stats_rx{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {55, 55, 55},
|
||||
.foreground = {155, 155, 155},
|
||||
};
|
||||
freq_stats_rx.set_style(&style_freq_stats_rx);
|
||||
freq_stats_rx.set("RxSat off");
|
||||
return;
|
||||
}
|
||||
uint8_t rx_sat = ((uint32_t)shared_memory.m4_performance_counter) * 100 / 127;
|
||||
if (last_rx_sat != rx_sat) {
|
||||
last_rx_sat = rx_sat;
|
||||
freq_stats_rx.set("RxSat: " + to_string_dec_uint(rx_sat) + "%");
|
||||
|
||||
uint8_t br = 0;
|
||||
uint8_t bg = 0;
|
||||
uint8_t bb = 0;
|
||||
@@ -191,6 +231,7 @@ void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
.foreground = {255, 255, 255},
|
||||
};
|
||||
freq_stats_rx.set_style(&style_freq_stats_rx);
|
||||
freq_stats_rx.set("RxSat: " + to_string_dec_uint(rx_sat) + "%");
|
||||
}
|
||||
|
||||
} /* on_statistic_updates */
|
||||
@@ -198,44 +239,53 @@ void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
size_t LevelView::change_mode(freqman_index_t new_mod) {
|
||||
field_bw.on_change = [this](size_t n, OptionsField::value_t) { (void)n; };
|
||||
|
||||
radio_mode = new_mod;
|
||||
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
|
||||
switch (new_mod) {
|
||||
case AM_MODULATION:
|
||||
audio_sampling_rate = audio::Rate::Hz_12000;
|
||||
freqman_set_bandwidth_option(new_mod, field_bw);
|
||||
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
|
||||
receiver_model.set_am_configuration(field_bw.selected_index_value());
|
||||
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_am_configuration(n); };
|
||||
// bw DSB (0) default
|
||||
field_bw.set_by_value(0);
|
||||
text_ctcss.set(" ");
|
||||
receiver_model.set_am_configuration(0);
|
||||
field_bw.on_change = [this](size_t index, OptionsField::value_t n) { radio_bw = index ; receiver_model.set_am_configuration(n); };
|
||||
break;
|
||||
case NFM_MODULATION:
|
||||
audio_sampling_rate = audio::Rate::Hz_24000;
|
||||
freqman_set_bandwidth_option(new_mod, field_bw);
|
||||
baseband::run_image(portapack::spi_flash::image_tag_nfm_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
||||
receiver_model.set_nbfm_configuration(field_bw.selected_index_value());
|
||||
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_nbfm_configuration(n); };
|
||||
// bw 16k (2) default
|
||||
field_bw.set_by_value(2);
|
||||
field_bw.on_change = [this](size_t index, OptionsField::value_t n) { radio_bw = index ; receiver_model.set_nbfm_configuration(n); };
|
||||
break;
|
||||
case WFM_MODULATION:
|
||||
audio_sampling_rate = audio::Rate::Hz_48000;
|
||||
freqman_set_bandwidth_option(new_mod, field_bw);
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
|
||||
receiver_model.set_wfm_configuration(field_bw.selected_index_value());
|
||||
field_bw.on_change = [this](size_t, OptionsField::value_t n) { receiver_model.set_wfm_configuration(n); };
|
||||
// bw 200k (0) only/default
|
||||
// bw 200k (0) default
|
||||
field_bw.set_by_value(0);
|
||||
text_ctcss.set(" ");
|
||||
field_bw.on_change = [this](size_t index, OptionsField::value_t n) { radio_bw = index ; receiver_model.set_wfm_configuration(n); };
|
||||
break;
|
||||
case SPEC_MODULATION:
|
||||
audio_sampling_rate = audio::Rate::Hz_24000;
|
||||
freqman_set_bandwidth_option(new_mod, field_bw);
|
||||
baseband::run_image(portapack::spi_flash::image_tag_capture);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::Capture);
|
||||
field_bw.on_change = [this](size_t, OptionsField::value_t sampling_rate) {
|
||||
// 12k5 (0) default
|
||||
field_bw.on_change = [this](size_t index, OptionsField::value_t sampling_rate) {
|
||||
radio_bw = index;
|
||||
// Baseband needs to know the desired sampling and oversampling rates.
|
||||
baseband::set_sample_rate(sampling_rate, get_oversample_rate(sampling_rate));
|
||||
|
||||
// The radio needs to know the effective sampling rate.
|
||||
auto actual_sampling_rate = get_actual_sample_rate(sampling_rate);
|
||||
receiver_model.set_sampling_rate(actual_sampling_rate);
|
||||
@@ -250,6 +300,18 @@ size_t LevelView::change_mode(freqman_index_t new_mod) {
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
}
|
||||
if (new_mod != NFM_MODULATION) {
|
||||
text_ctcss.set(" ");
|
||||
}
|
||||
|
||||
m4_manage_stat_update(); // rx_sat hack
|
||||
|
||||
if (audio_mode) {
|
||||
audio::set_rate(audio_sampling_rate);
|
||||
audio::output::start();
|
||||
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
|
||||
}
|
||||
receiver_model.enable();
|
||||
|
||||
return step_mode.selected_index();
|
||||
}
|
||||
|
||||
@@ -55,14 +55,30 @@ class LevelView : public View {
|
||||
NavigationView& nav_;
|
||||
|
||||
RxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_level", app_settings::Mode::RX};
|
||||
|
||||
int32_t map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh);
|
||||
size_t change_mode(freqman_index_t mod_type);
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
void set_display_freq(int64_t freq);
|
||||
void m4_manage_stat_update(); // to finely adjust the RxSaturation usage
|
||||
|
||||
rf::Frequency freq_ = {0};
|
||||
bool beep = false;
|
||||
uint8_t radio_mode = 0;
|
||||
uint8_t radio_bw = 0;
|
||||
uint8_t audio_mode = 0;
|
||||
int32_t beep_squelch = 0;
|
||||
audio::Rate audio_sampling_rate = audio::Rate::Hz_48000;
|
||||
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_level",
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"beep_squelch"sv, &beep_squelch},
|
||||
{"audio_mode"sv, &audio_mode},
|
||||
{"radio_mode"sv, &radio_mode},
|
||||
{"radio_bw"sv, &radio_bw},
|
||||
}};
|
||||
|
||||
Labels labels{
|
||||
{{0 * 8, 0 * 16}, "LNA: VGA: AMP: VOL: ", Color::light_grey()},
|
||||
@@ -100,23 +116,27 @@ class LevelView : public View {
|
||||
{0 * 8, 2 * 16 + 8, 15 * 8, 1 * 8},
|
||||
""};
|
||||
|
||||
OptionsField audio_mode{
|
||||
OptionsField field_audio_mode{
|
||||
{21 * 8, 1 * 16},
|
||||
9,
|
||||
{
|
||||
{"audio off", 0},
|
||||
{"audio on", 1}
|
||||
//{"tone on", 2},
|
||||
//{"tone off", 3},
|
||||
}};
|
||||
{{"audio off", 0},
|
||||
{"audio on", 1}}};
|
||||
|
||||
Text text_ctcss{
|
||||
{22 * 8, 3 * 16 + 4, 8 * 8, 1 * 8},
|
||||
""};
|
||||
Text text_beep_squelch{
|
||||
{21 * 8, 3 * 16 + 4, 4 * 8, 1 * 8},
|
||||
"Bip>"};
|
||||
|
||||
// RSSI: XX/XX/XXX,dt: XX
|
||||
NumberField field_beep_squelch{
|
||||
{25 * 8, 3 * 16 + 4},
|
||||
4,
|
||||
{-100, 20},
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
// RSSI: XX/XX/XXX
|
||||
Text freq_stats_rssi{
|
||||
{0 * 8, 3 * 16 + 4, 22 * 8, 1 * 16},
|
||||
{0 * 8, 3 * 16 + 4, 15 * 8, 1 * 16},
|
||||
};
|
||||
|
||||
// Power: -XXX db
|
||||
@@ -153,14 +173,18 @@ class LevelView : public View {
|
||||
{0 * 8, 5 * 16 + 4, 10 * 8, 1 * 16},
|
||||
};
|
||||
|
||||
Text text_ctcss{
|
||||
{12 * 8, 5 * 16 + 4, 8 * 8, 1 * 8},
|
||||
""};
|
||||
|
||||
RSSIGraph rssi_graph{
|
||||
// 240x320 =>
|
||||
{0, 6 * 16 + 4, 240 - 5 * 8, 320 - (6 * 16 + 4)},
|
||||
{0, 6 * 16 + 8, 240 - 5 * 8, 320 - (6 * 16)},
|
||||
};
|
||||
|
||||
RSSI rssi{
|
||||
// 240x320 =>
|
||||
{240 - 5 * 8, 6 * 16 + 4, 5 * 8, 320 - (6 * 16 + 4)},
|
||||
{240 - 5 * 8, 6 * 16 + 8, 5 * 8, 320 - (6 * 16)},
|
||||
};
|
||||
|
||||
void handle_coded_squelch(const uint32_t value);
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#include "convert.hpp"
|
||||
#include "file_reader.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -34,11 +36,39 @@ void GlassView::focus() {
|
||||
}
|
||||
|
||||
GlassView::~GlassView() {
|
||||
audio::output::stop();
|
||||
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
// Function to map the value from one range to another
|
||||
int32_t GlassView::map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh) {
|
||||
return toLow + (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow);
|
||||
}
|
||||
|
||||
void GlassView::update_display_beep() {
|
||||
if (beep_enabled) {
|
||||
button_beep_squelch.set_style(&Styles::green);
|
||||
// <bip:-XXXdb>
|
||||
button_beep_squelch.set_text("[bip>" + to_string_dec_int(beep_squelch, 4) + "db]");
|
||||
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
|
||||
} else {
|
||||
button_beep_squelch.set_style(&Styles::white);
|
||||
button_beep_squelch.set_text("[ beep OFF ]");
|
||||
}
|
||||
}
|
||||
|
||||
void GlassView::manage_beep_audio() {
|
||||
if (beep_enabled) {
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::output::start();
|
||||
} else {
|
||||
baseband::request_beep_stop();
|
||||
audio::output::stop();
|
||||
}
|
||||
}
|
||||
|
||||
void GlassView::get_max_power(const ChannelSpectrum& spectrum, uint8_t bin, uint8_t& max_power) {
|
||||
if (mode == LOOKING_GLASS_SINGLEPASS) {
|
||||
// <20MHz spectrum mode
|
||||
@@ -173,6 +203,8 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
// we actually need SCREEN_W (240) of those bins
|
||||
for (uint8_t bin = 0; bin < bin_length; bin++) {
|
||||
get_max_power(spectrum, bin, max_power);
|
||||
if (max_power > range_max_power)
|
||||
range_max_power = max_power;
|
||||
// process dc spike if enable
|
||||
if (bin == 119) {
|
||||
uint8_t next_max_power = 0;
|
||||
@@ -184,14 +216,21 @@ void GlassView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
}
|
||||
}
|
||||
// process actual bin
|
||||
if (process_bins(&max_power) == true)
|
||||
if (process_bins(&max_power)) {
|
||||
int8_t power = map(range_max_power, 0, 255, -100, 20);
|
||||
if (power >= beep_squelch) {
|
||||
baseband::request_audio_beep(map(range_max_power, 0, 256, 400, 2600), 24000, 250);
|
||||
}
|
||||
range_max_power = 0;
|
||||
return; // new line signaled, return
|
||||
}
|
||||
}
|
||||
if (mode != LOOKING_GLASS_SINGLEPASS) {
|
||||
f_center += looking_glass_step;
|
||||
retune();
|
||||
} else
|
||||
} else {
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
}
|
||||
|
||||
void GlassView::on_hide() {
|
||||
@@ -327,13 +366,15 @@ GlassView::GlassView(
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&field_range,
|
||||
&steps_config,
|
||||
//&steps_config,
|
||||
&scan_type,
|
||||
&view_config,
|
||||
&level_integration,
|
||||
&field_volume,
|
||||
&filter_config,
|
||||
&field_rf_amp,
|
||||
&range_presets,
|
||||
&button_beep_squelch,
|
||||
&field_marker,
|
||||
&field_trigger,
|
||||
&button_jump,
|
||||
@@ -370,12 +411,12 @@ GlassView::GlassView(
|
||||
};
|
||||
};
|
||||
|
||||
steps_config.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
/*steps_config.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
field_frequency_min.set_step(v);
|
||||
field_frequency_max.set_step(v);
|
||||
steps = v;
|
||||
};
|
||||
steps_config.set_selected_index(0); // 1 Mhz step.
|
||||
steps_config.set_selected_index(0); // 1 Mhz step.*/
|
||||
|
||||
scan_type.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
mode = v;
|
||||
@@ -497,6 +538,27 @@ GlassView::GlassView(
|
||||
receiver_model.set_baseband_bandwidth(looking_glass_bandwidth); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
|
||||
receiver_model.set_squelch_level(0);
|
||||
receiver_model.enable();
|
||||
|
||||
button_beep_squelch.on_select = [this](ButtonWithEncoder& button) {
|
||||
(void)button;
|
||||
beep_enabled = 1 - beep_enabled;
|
||||
manage_beep_audio();
|
||||
update_display_beep();
|
||||
};
|
||||
|
||||
button_beep_squelch.on_change = [this]() {
|
||||
int new_beep_squelch = beep_squelch + button_beep_squelch.get_encoder_delta();
|
||||
if (new_beep_squelch < -100)
|
||||
new_beep_squelch = -100;
|
||||
if (new_beep_squelch > 20)
|
||||
new_beep_squelch = 20;
|
||||
beep_squelch = new_beep_squelch;
|
||||
button_beep_squelch.set_encoder_delta(0);
|
||||
update_display_beep();
|
||||
};
|
||||
|
||||
manage_beep_audio();
|
||||
update_display_beep();
|
||||
}
|
||||
|
||||
uint8_t GlassView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value
|
||||
@@ -510,7 +572,7 @@ void GlassView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // def
|
||||
|
||||
void GlassView::load_presets() {
|
||||
File presets_file;
|
||||
auto error = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
auto error = presets_file.open(looking_glass_dir / u"PRESETS.TXT");
|
||||
presets_db.clear();
|
||||
|
||||
// Add the "Manual" entry.
|
||||
|
||||
@@ -85,6 +85,8 @@ class GlassView : public View {
|
||||
uint8_t live_frequency_view = 0; // Spectrum
|
||||
uint8_t live_frequency_integrate = 3; // Default (3 * old value + new_value) / 4
|
||||
uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839
|
||||
int32_t beep_squelch = 20; // range from -100 to +20, >=20 disabled
|
||||
bool beep_enabled = false; // activate on bip button click
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_glass"sv,
|
||||
app_settings::Mode::RX,
|
||||
@@ -98,6 +100,8 @@ class GlassView : public View {
|
||||
{"freq_view"sv, &live_frequency_view},
|
||||
{"freq_integrate"sv, &live_frequency_integrate},
|
||||
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
|
||||
{"beep_squelch"sv, &beep_squelch},
|
||||
{"beep_enabled"sv, &beep_enabled},
|
||||
}};
|
||||
|
||||
struct preset_entry {
|
||||
@@ -106,7 +110,10 @@ class GlassView : public View {
|
||||
std::string label{};
|
||||
};
|
||||
|
||||
int32_t map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh);
|
||||
std::vector<preset_entry> presets_db{};
|
||||
void manage_beep_audio();
|
||||
void update_display_beep();
|
||||
void update_min(int32_t v);
|
||||
void update_max(int32_t v);
|
||||
void update_range_field();
|
||||
@@ -153,6 +160,8 @@ class GlassView : public View {
|
||||
int32_t steps = 1;
|
||||
bool locked_range = false;
|
||||
|
||||
uint8_t range_max_power = 0;
|
||||
uint8_t range_max_power_counter = 0;
|
||||
uint8_t max_power = 0;
|
||||
rf::Frequency max_freq_hold = 0;
|
||||
rf::Frequency last_max_freq = 0;
|
||||
@@ -166,7 +175,8 @@ class GlassView : public View {
|
||||
{{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()},
|
||||
{{0, 2 * 16}, "PRESET:", Color::light_grey()},
|
||||
{{0, 3 * 16}, "MARKER: MHz RXIQCAL", Color::light_grey()},
|
||||
{{0, 4 * 16}, "RES: STEP:", Color::light_grey()}};
|
||||
//{{0, 4 * 16}, "RES: STEPS:", Color::light_grey()}};
|
||||
{{0, 4 * 16}, "RES: VOL:", Color::light_grey()}};
|
||||
|
||||
NumberField field_frequency_min{
|
||||
{4 * 8, 0 * 16},
|
||||
@@ -206,9 +216,13 @@ class GlassView : public View {
|
||||
|
||||
OptionsField range_presets{
|
||||
{7 * 8, 2 * 16},
|
||||
20,
|
||||
10,
|
||||
{}};
|
||||
|
||||
ButtonWithEncoder button_beep_squelch{
|
||||
{18 * 8, 2 * 16 + 4, 12 * 8, 1 * 8},
|
||||
""};
|
||||
|
||||
TextField field_marker{
|
||||
{7 * 8, 3 * 16, 9 * 8, 16},
|
||||
""};
|
||||
@@ -228,7 +242,10 @@ class GlassView : public View {
|
||||
2,
|
||||
' '};
|
||||
|
||||
OptionsField steps_config{
|
||||
AudioVolumeField field_volume{
|
||||
{13 * 8, 4 * 16}};
|
||||
|
||||
/*OptionsField steps_config{
|
||||
{13 * 8, 4 * 16},
|
||||
3,
|
||||
{
|
||||
@@ -238,7 +255,7 @@ class GlassView : public View {
|
||||
{"100", 100},
|
||||
{"250", 250},
|
||||
{"500", 500},
|
||||
}};
|
||||
}};*/
|
||||
|
||||
OptionsField scan_type{
|
||||
{17 * 8, 4 * 16},
|
||||
|
||||
@@ -130,9 +130,9 @@ void MicTXView::set_tx(bool enable) {
|
||||
portapack::pin_i2s0_rx_sda.mode(3); // This is already done in audio::init but gets changed by the CPLD overlay reprogramming
|
||||
} else {
|
||||
if (transmitting && rogerbeep_enabled) {
|
||||
baseband::request_beep(); // Transmit the roger beep
|
||||
transmitting = false; // And flag the end of the transmission so ...
|
||||
} else { // (if roger beep was enabled, this will be executed after the beep ends transmitting.
|
||||
baseband::request_roger_beep(); // Transmit the roger beep
|
||||
transmitting = false; // Flag the end of the transmission (transmitter will be disabled after the beep)
|
||||
} else {
|
||||
transmitting = false;
|
||||
configure_baseband();
|
||||
transmitter_model.disable();
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fstream>
|
||||
@@ -130,7 +131,7 @@ void PlaylistView::open_file(bool prompt_save) {
|
||||
}
|
||||
|
||||
auto open_view = nav_.push<FileLoadView>(".PPL");
|
||||
open_view->push_dir(u"PLAYLIST");
|
||||
open_view->push_dir(playlist_dir);
|
||||
open_view->on_changed = [this](fs::path new_file_path) {
|
||||
on_file_changed(new_file_path);
|
||||
};
|
||||
@@ -169,7 +170,7 @@ void PlaylistView::save_file(bool show_dialogs) {
|
||||
|
||||
void PlaylistView::add_entry(fs::path&& path) {
|
||||
if (playlist_path_.empty()) {
|
||||
playlist_path_ = next_filename_matching_pattern(u"/PLAYLIST/PLAY_????.PPL");
|
||||
playlist_path_ = next_filename_matching_pattern(playlist_dir / u"PLAY_????.PPL");
|
||||
|
||||
// Hack around focus getting called by ctor before parent is set.
|
||||
if (parent())
|
||||
@@ -387,7 +388,7 @@ PlaylistView::PlaylistView(
|
||||
&waterfall,
|
||||
});
|
||||
|
||||
ensure_directory(u"PLAYLIST");
|
||||
ensure_directory(playlist_dir);
|
||||
waterfall.show_audio_spectrum_view(false);
|
||||
|
||||
field_frequency.set_value(transmitter_model.target_frequency());
|
||||
@@ -410,7 +411,7 @@ PlaylistView::PlaylistView(
|
||||
if (is_active())
|
||||
return;
|
||||
auto open_view = nav_.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(u"CAPTURES");
|
||||
open_view->push_dir(captures_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
add_entry(std::move(path));
|
||||
};
|
||||
|
||||
@@ -337,7 +337,7 @@ ReconView::ReconView(NavigationView& nav)
|
||||
|
||||
// set record View
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"AUTO_AUDIO", u"AUDIO",
|
||||
u"AUTO_AUDIO", audio_dir,
|
||||
RecordView::FileType::WAV, 4096, 4);
|
||||
record_view->set_filename_date_frequency(true);
|
||||
record_view->set_auto_trim(false);
|
||||
@@ -1170,18 +1170,18 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
|
||||
if (new_mod == SPEC_MODULATION) {
|
||||
if (persistent_memory::recon_repeat_recorded()) {
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"RECON_REPEAT.C16", u"CAPTURES",
|
||||
u"RECON_REPEAT.C16", captures_dir,
|
||||
RecordView::FileType::RawS16, 16384, 3);
|
||||
record_view->set_filename_as_is(true);
|
||||
} else {
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"AUTO_RAW", u"CAPTURES",
|
||||
u"AUTO_RAW", captures_dir,
|
||||
RecordView::FileType::RawS16, 16384, 3);
|
||||
record_view->set_filename_date_frequency(true);
|
||||
}
|
||||
} else {
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"AUTO_AUDIO", u"AUDIO",
|
||||
u"AUTO_AUDIO", audio_dir,
|
||||
RecordView::FileType::WAV, 4096, 4);
|
||||
record_view->set_filename_date_frequency(true);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_recon_settings.hpp"
|
||||
@@ -181,7 +182,6 @@ class ReconView : public View {
|
||||
|
||||
const std::filesystem::path repeat_rec_file = u"RECON_REPEAT.C16";
|
||||
const std::filesystem::path repeat_rec_meta = u"RECON_REPEAT.TXT";
|
||||
const std::filesystem::path repeat_rec_path = u"CAPTURES";
|
||||
const size_t repeat_read_size{16384};
|
||||
const size_t repeat_buffer_count{3};
|
||||
int8_t repeat_cur_rep = 0;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "freqman_db.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace portapack;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
@@ -246,7 +247,7 @@ RemoteEntryEditView::RemoteEntryEditView(
|
||||
|
||||
field_path.on_select = [this, &nav](TextField&) {
|
||||
auto open_view = nav.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(u"CAPTURES");
|
||||
open_view->push_dir(captures_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
load_path(std::move(path));
|
||||
refresh_ui();
|
||||
@@ -355,7 +356,7 @@ RemoteView::RemoteView(
|
||||
Dim waterfall_height = waterfall_bottom - waterfall_top;
|
||||
waterfall.set_parent_rect({0, waterfall_top, screen_width, waterfall_height});
|
||||
|
||||
ensure_directory(u"REMOTES");
|
||||
ensure_directory(remotes_dir);
|
||||
|
||||
// Load the previously loaded remote if exists.
|
||||
if (!load_remote(settings_.remote_path))
|
||||
@@ -527,7 +528,7 @@ void RemoteView::new_remote() {
|
||||
|
||||
void RemoteView::open_remote() {
|
||||
auto open_view = nav_.push<FileLoadView>(".REM");
|
||||
open_view->push_dir(u"REMOTES");
|
||||
open_view->push_dir(remotes_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
save_remote();
|
||||
load_remote(std::move(path));
|
||||
@@ -538,7 +539,7 @@ void RemoteView::open_remote() {
|
||||
void RemoteView::init_remote() {
|
||||
model_ = {"<Unnamed Remote>", {}};
|
||||
reset_buttons();
|
||||
set_remote_path(next_filename_matching_pattern(u"/REMOTES/REMOTE_????.REM"));
|
||||
set_remote_path(next_filename_matching_pattern(remotes_dir / u"REMOTE_????.REM"));
|
||||
set_needs_save(false);
|
||||
|
||||
if (remote_path_.empty())
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "optional.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -41,6 +41,7 @@ using namespace lpc43xx;
|
||||
using namespace portapack;
|
||||
|
||||
#include "file.hpp"
|
||||
#include "file_path.hpp"
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#include "string_format.hpp"
|
||||
@@ -571,17 +572,17 @@ SetPersistentMemoryView::SetPersistentMemoryView(NavigationView& nav) {
|
||||
check_use_sdcard_for_pmem.on_select = [this](Checkbox&, bool v) {
|
||||
File pmem_flag_file_handle;
|
||||
if (v) {
|
||||
if (fs::file_exists(PMEM_FILEFLAG)) {
|
||||
if (fs::file_exists(settings_dir / PMEM_FILEFLAG)) {
|
||||
text_pmem_status.set("P.Mem flag file present.");
|
||||
} else {
|
||||
auto error = pmem_flag_file_handle.create(PMEM_FILEFLAG);
|
||||
auto error = pmem_flag_file_handle.create(settings_dir / PMEM_FILEFLAG);
|
||||
if (error)
|
||||
text_pmem_status.set("Error creating P.Mem File!");
|
||||
else
|
||||
text_pmem_status.set("P.Mem flag file created.");
|
||||
}
|
||||
} else {
|
||||
auto result = delete_file(PMEM_FILEFLAG);
|
||||
auto result = delete_file(settings_dir / PMEM_FILEFLAG);
|
||||
if (result.code() != FR_OK)
|
||||
text_pmem_status.set("Error deleting P.Mem flag!");
|
||||
else
|
||||
@@ -717,10 +718,10 @@ AppSettingsView::AppSettingsView(
|
||||
|
||||
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
|
||||
|
||||
ensure_directory(SETTINGS_DIR);
|
||||
ensure_directory(settings_dir);
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(SETTINGS_DIR, u"*.ini")) {
|
||||
auto path = (std::filesystem::path)SETTINGS_DIR / entry.path();
|
||||
for (const auto& entry : std::filesystem::directory_iterator(settings_dir, u"*.ini")) {
|
||||
auto path = settings_dir / entry.path();
|
||||
|
||||
menu_view.add_item({path.filename().string().substr(0, 26),
|
||||
ui::Color::dark_cyan(),
|
||||
@@ -858,7 +859,7 @@ void SettingsMenuView::on_populate() {
|
||||
{"Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetRadioView>(); }},
|
||||
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [this]() { nav_.push<SetSDCardView>(); }},
|
||||
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [this]() { nav_.push<SetUIView>(); }},
|
||||
{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [this]() { nav_.push<SetQRCodeView>(); }},
|
||||
//{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [this]() { nav_.push<SetQRCodeView>(); }},
|
||||
{"Brightness", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetFakeBrightnessView>(); }},
|
||||
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetMenuColorView>(); }},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include <cstring>
|
||||
@@ -70,14 +72,19 @@ SondeView::SondeView(NavigationView& nav)
|
||||
|
||||
geopos.set_read_only(true);
|
||||
|
||||
check_beep.set_value(beep);
|
||||
check_beep.on_select = [this](Checkbox&, bool v) {
|
||||
beep = v;
|
||||
if (beep)
|
||||
baseband::request_audio_beep(1000, 24000, 60); // 1khz tone for 60ms to acknowledge enablement
|
||||
};
|
||||
|
||||
check_log.set_value(logging);
|
||||
check_log.on_select = [this](Checkbox&, bool v) {
|
||||
logging = v;
|
||||
};
|
||||
|
||||
check_crc.set_value(use_crc);
|
||||
check_crc.on_select = [this](Checkbox&, bool v) {
|
||||
use_crc = v;
|
||||
};
|
||||
@@ -86,17 +93,18 @@ SondeView::SondeView(NavigationView& nav)
|
||||
|
||||
// QR code with geo URI
|
||||
button_see_qr.on_select = [this, &nav](Button&) {
|
||||
nav.push<QRCodeView>(geo_uri);
|
||||
std::string geo_uri = "geo:" + to_string_decimal(geopos.lat(), 5) + "," + to_string_decimal(geopos.lon(), 5); // 5 decimal digits for ~1 meter accuracy
|
||||
nav.push<QRCodeView>(geo_uri.data());
|
||||
};
|
||||
|
||||
button_see_map.on_select = [this, &nav](Button&) {
|
||||
geomap_view_ = nav.push<GeoMapView>(
|
||||
sonde_id,
|
||||
gps_info.alt,
|
||||
geopos.altitude(),
|
||||
GeoPos::alt_unit::METERS,
|
||||
GeoPos::spd_unit::HIDDEN,
|
||||
gps_info.lat,
|
||||
gps_info.lon,
|
||||
geopos.lat(),
|
||||
geopos.lon(),
|
||||
999); // set a dummy heading out of range to draw a cross...probably not ideal?
|
||||
nav.set_on_pop([this]() {
|
||||
geomap_view_ = nullptr;
|
||||
@@ -105,7 +113,7 @@ SondeView::SondeView(NavigationView& nav)
|
||||
|
||||
logger = std::make_unique<SondeLogger>();
|
||||
if (logger)
|
||||
logger->append(LOG_ROOT_DIR "/SONDE.TXT");
|
||||
logger->append(logs_dir / u"SONDE.TXT");
|
||||
|
||||
audio::output::start();
|
||||
|
||||
@@ -142,51 +150,9 @@ void SondeView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
// used to convert float to character pointer, since unfortunately function like
|
||||
// sprintf and c_str aren't supported.
|
||||
char* SondeView::float_to_char(float x, char* p) {
|
||||
char* s = p + 9; // go to end of buffer
|
||||
uint16_t decimals; // variable to store the decimals
|
||||
int units; // variable to store the units (part to left of decimal place)
|
||||
if (x < 0) { // take care of negative numbers
|
||||
decimals = (int)(x * -100000) % 100000; // make 1000 for 3 decimals etc.
|
||||
units = (int)(-1 * x);
|
||||
} else { // positive numbers
|
||||
decimals = (int)(x * 100000) % 100000;
|
||||
units = (int)x;
|
||||
}
|
||||
|
||||
// TODO: more elegant solution (loop?)
|
||||
*--s = (decimals % 10) + '0';
|
||||
decimals /= 10;
|
||||
*--s = (decimals % 10) + '0';
|
||||
decimals /= 10;
|
||||
*--s = (decimals % 10) + '0';
|
||||
decimals /= 10;
|
||||
*--s = (decimals % 10) + '0';
|
||||
decimals /= 10;
|
||||
*--s = (decimals % 10) + '0';
|
||||
*--s = '.';
|
||||
|
||||
while (units > 0) {
|
||||
*--s = (units % 10) + '0';
|
||||
units /= 10;
|
||||
}
|
||||
if (x < 0) *--s = '-'; // unary minus sign for negative numbers
|
||||
return s;
|
||||
}
|
||||
|
||||
void SondeView::on_packet(const sonde::Packet& packet) {
|
||||
if (!use_crc || packet.crc_ok()) // euquiq: Reject bad packet if crc is on
|
||||
{
|
||||
char buffer_lat[10] = {};
|
||||
char buffer_lon[10] = {};
|
||||
|
||||
strcpy(geo_uri, "geo:");
|
||||
strcat(geo_uri, float_to_char(gps_info.lat, buffer_lat));
|
||||
strcat(geo_uri, ",");
|
||||
strcat(geo_uri, float_to_char(gps_info.lon, buffer_lon));
|
||||
|
||||
text_signature.set(packet.type_string());
|
||||
|
||||
sonde_id = packet.serial_number(); // used also as tag on the geomap
|
||||
@@ -220,7 +186,7 @@ void SondeView::on_packet(const sonde::Packet& packet) {
|
||||
}
|
||||
|
||||
if (beep) {
|
||||
baseband::request_beep();
|
||||
baseband::request_rssi_beep();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -74,15 +75,19 @@ class SondeView : public View {
|
||||
1750000 /* bandwidth */,
|
||||
2457600 /* sampling rate */
|
||||
};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_sonde", app_settings::Mode::RX};
|
||||
|
||||
std::unique_ptr<SondeLogger> logger{};
|
||||
bool beep{false};
|
||||
bool logging{false};
|
||||
bool use_crc{false};
|
||||
bool beep{false};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_sonde",
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"beep"sv, &beep},
|
||||
{"logging"sv, &logging},
|
||||
{"use_crc"sv, &use_crc},
|
||||
}};
|
||||
|
||||
char geo_uri[32] = {};
|
||||
std::unique_ptr<SondeLogger> logger{};
|
||||
|
||||
sonde::GPS_data gps_info{};
|
||||
sonde::temp_humid temp_humid_info{};
|
||||
@@ -201,7 +206,6 @@ class SondeView : public View {
|
||||
void on_gps(const GPSPosDataMessage* msg);
|
||||
void on_orientation(const OrientationDataMessage* msg);
|
||||
void on_packet(const sonde::Packet& packet);
|
||||
char* float_to_char(float x, char* p);
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
class TestLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "event_m0.hpp"
|
||||
#include "file_reader.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
@@ -142,7 +143,7 @@ WhipCalcView::WhipCalcView(NavigationView& nav)
|
||||
|
||||
void WhipCalcView::load_antenna_db() {
|
||||
File antennas_file;
|
||||
auto error = antennas_file.open("/WHIPCALC/ANTENNAS.TXT");
|
||||
auto error = antennas_file.open(whipcalc_dir / u"ANTENNAS.TXT");
|
||||
|
||||
if (error)
|
||||
return;
|
||||
|
||||
@@ -428,8 +428,25 @@ void replay_stop() {
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void request_beep() {
|
||||
RequestSignalMessage message{RequestSignalMessage::Signal::BeepRequest};
|
||||
void request_beep(RequestSignalMessage::Signal beep_type) {
|
||||
RequestSignalMessage message{beep_type};
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void request_roger_beep() {
|
||||
request_beep(RequestSignalMessage::Signal::RogerBeepRequest);
|
||||
}
|
||||
|
||||
void request_rssi_beep() {
|
||||
request_beep(RequestSignalMessage::Signal::RSSIBeepRequest);
|
||||
}
|
||||
|
||||
void request_beep_stop() {
|
||||
request_beep(RequestSignalMessage::Signal::BeepStopRequest);
|
||||
}
|
||||
|
||||
void request_audio_beep(uint32_t freq, uint32_t sample_rate, uint32_t duration_ms) {
|
||||
AudioBeepMessage message{freq, sample_rate, duration_ms};
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,11 @@ void set_siggen_tone(const uint32_t tone);
|
||||
void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t duration);
|
||||
void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw);
|
||||
void set_subghzd_config(uint8_t modulation, uint32_t sampling_rate);
|
||||
void request_beep();
|
||||
|
||||
void request_roger_beep();
|
||||
void request_rssi_beep();
|
||||
void request_beep_stop();
|
||||
void request_audio_beep(uint32_t freq, uint32_t sample_rate, uint32_t duration_ms);
|
||||
|
||||
void run_image(const portapack::spi_flash::image_tag_t image_tag);
|
||||
void run_prepared_image(const uint32_t m4_code);
|
||||
|
||||
@@ -5797,6 +5797,44 @@ static constexpr Bitmap bitmap_icon_brightness{
|
||||
{16, 16},
|
||||
bitmap_icon_brightness_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_clean_data[] = {
|
||||
0x00,
|
||||
0x00,
|
||||
0xC0,
|
||||
0x01,
|
||||
0x20,
|
||||
0x02,
|
||||
0xFC,
|
||||
0x1F,
|
||||
0x00,
|
||||
0x00,
|
||||
0x08,
|
||||
0x08,
|
||||
0xE8,
|
||||
0x08,
|
||||
0xA8,
|
||||
0x09,
|
||||
0xA8,
|
||||
0x0B,
|
||||
0x28,
|
||||
0x0A,
|
||||
0x28,
|
||||
0x0A,
|
||||
0x28,
|
||||
0x0A,
|
||||
0xE8,
|
||||
0x0B,
|
||||
0x08,
|
||||
0x08,
|
||||
0xF0,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_clean{
|
||||
{16, 16},
|
||||
bitmap_icon_clean_data};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif /*__BITMAP_HPP__*/
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
|
||||
#include "database.hpp"
|
||||
#include "file.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include <cstring>
|
||||
|
||||
int database::retrieve_mid_record(MidDBRecord* record, std::string search_term) {
|
||||
file_path = "AIS/mids.db";
|
||||
file_path = ais_dir / u"mids.db";
|
||||
index_item_length = 4;
|
||||
record_length = 32;
|
||||
|
||||
@@ -36,7 +37,7 @@ int database::retrieve_mid_record(MidDBRecord* record, std::string search_term)
|
||||
}
|
||||
|
||||
int database::retrieve_airline_record(AirlinesDBRecord* record, std::string search_term) {
|
||||
file_path = "ADSB/airlines.db";
|
||||
file_path = adsb_dir / u"airlines.db";
|
||||
index_item_length = 4;
|
||||
record_length = 64;
|
||||
|
||||
@@ -46,7 +47,7 @@ int database::retrieve_airline_record(AirlinesDBRecord* record, std::string sear
|
||||
}
|
||||
|
||||
int database::retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term) {
|
||||
file_path = "ADSB/icao24.db";
|
||||
file_path = adsb_dir / u"icao24.db";
|
||||
index_item_length = 7;
|
||||
record_length = 146;
|
||||
|
||||
@@ -55,7 +56,7 @@ int database::retrieve_aircraft_record(AircraftDBRecord* record, std::string sea
|
||||
return (result);
|
||||
}
|
||||
|
||||
int database::retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term) {
|
||||
int database::retrieve_record(std::filesystem::path file_path, int index_item_length, int record_length, void* record, std::string search_term) {
|
||||
if (search_term.empty())
|
||||
return DATABASE_RECORD_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@ class database {
|
||||
int retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term);
|
||||
|
||||
private:
|
||||
std::string file_path = ""; // path inclusing filename
|
||||
int index_item_length = 0; // length of index item
|
||||
int record_length = 0; // length of record
|
||||
std::filesystem::path file_path = ""; // path including filename
|
||||
int index_item_length = 0; // length of index item
|
||||
int record_length = 0; // length of record
|
||||
|
||||
File db_file{};
|
||||
int number_of_records = 0;
|
||||
@@ -74,7 +74,7 @@ class database {
|
||||
|
||||
int result = 0;
|
||||
|
||||
int retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term);
|
||||
int retrieve_record(std::filesystem::path file_path, int index_item_length, int record_length, void* record, std::string search_term);
|
||||
};
|
||||
|
||||
#endif /*__DATABASE_H__*/
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
@@ -266,7 +267,6 @@ bool stack_dump() {
|
||||
|
||||
bool memory_dump(uint32_t* addr_start, uint32_t num_words, bool stack_flag) {
|
||||
Painter painter;
|
||||
std::string debug_dir = "DEBUG";
|
||||
std::filesystem::path filename{};
|
||||
File dump_file{};
|
||||
bool error;
|
||||
@@ -277,7 +277,7 @@ bool memory_dump(uint32_t* addr_start, uint32_t num_words, bool stack_flag) {
|
||||
bool data_found{false};
|
||||
|
||||
ensure_directory(debug_dir);
|
||||
filename = next_filename_matching_pattern(debug_dir + "/" + (stack_flag ? "STACK" : "MEMORY") + "_DUMP_????.TXT");
|
||||
filename = next_filename_matching_pattern(debug_dir + (stack_flag ? u"/STACK" : u"/MEMORY") + u"_DUMP_????.TXT");
|
||||
error = filename.empty();
|
||||
if (!error)
|
||||
error = dump_file.create(filename) != 0;
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
@@ -84,7 +85,7 @@ AFSKRxView::AFSKRxView(NavigationView& nav)
|
||||
|
||||
logger = std::make_unique<AFSKLogger>();
|
||||
if (logger)
|
||||
logger->append(LOG_ROOT_DIR "/AFSK.TXT");
|
||||
logger->append(logs_dir / u"AFSK.TXT");
|
||||
|
||||
// Auto-configure modem for LCR RX (will be removed later)
|
||||
baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false);
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ namespace ui::external_app::afsk_rx {
|
||||
|
||||
class AFSKLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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_audio_test.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::audio_test {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<AudioTestView>();
|
||||
}
|
||||
} // namespace ui::external_app::audio_test
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_audio_test.application_information"), used)) application_information_t _application_information_audio_test = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::audio_test::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "Audio Test",
|
||||
/*.bitmap_data = */ {
|
||||
0x00,
|
||||
0x00,
|
||||
0x40,
|
||||
0x10,
|
||||
0x60,
|
||||
0x20,
|
||||
0x70,
|
||||
0x44,
|
||||
0x78,
|
||||
0x48,
|
||||
0x7F,
|
||||
0x91,
|
||||
0x7F,
|
||||
0x92,
|
||||
0x7F,
|
||||
0x92,
|
||||
0x7F,
|
||||
0x92,
|
||||
0x7F,
|
||||
0x92,
|
||||
0x7F,
|
||||
0x92,
|
||||
0x7F,
|
||||
0x91,
|
||||
0x78,
|
||||
0x48,
|
||||
0x70,
|
||||
0x44,
|
||||
0x60,
|
||||
0x20,
|
||||
0x40,
|
||||
0x10,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::cyan().v,
|
||||
/*.menu_location = */ app_location_t::DEBUG,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {'P', 'A', 'B', 'P'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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_audio_test.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
AudioTestView::AudioTestView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base()); // proc_audio_beep baseband is external too
|
||||
|
||||
add_children({&labels,
|
||||
&options_sample_rate,
|
||||
&field_frequency,
|
||||
&options_step,
|
||||
&field_duration,
|
||||
&field_volume,
|
||||
&toggle_speaker});
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
options_sample_rate.on_change = [this](size_t, int32_t v) {
|
||||
switch (v) {
|
||||
case 12000:
|
||||
audio::set_rate(audio::Rate::Hz_12000);
|
||||
break;
|
||||
case 24000:
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
break;
|
||||
case 48000:
|
||||
audio::set_rate(audio::Rate::Hz_48000);
|
||||
break;
|
||||
}
|
||||
field_frequency.set_range(v / 128, v / 2);
|
||||
update_audio_beep();
|
||||
};
|
||||
options_sample_rate.set_selected_index(0, 1);
|
||||
|
||||
field_frequency.on_change = [this](int32_t) {
|
||||
update_audio_beep();
|
||||
};
|
||||
field_frequency.set_value(1000);
|
||||
|
||||
options_step.on_change = [this](size_t, int32_t v) {
|
||||
field_frequency.set_step(v);
|
||||
};
|
||||
options_step.set_by_value(100);
|
||||
|
||||
field_duration.on_change = [this](int32_t v) {
|
||||
(void)v;
|
||||
update_audio_beep();
|
||||
};
|
||||
field_duration.set_value(100);
|
||||
|
||||
toggle_speaker.on_change = [this](bool v) {
|
||||
beep = v;
|
||||
update_audio_beep();
|
||||
};
|
||||
|
||||
field_volume.set_value(0); // seems that a change is required to force update, so setting to 0 first
|
||||
field_volume.set_value(80);
|
||||
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
audio::output::start();
|
||||
}
|
||||
|
||||
AudioTestView::~AudioTestView() {
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
audio::output::stop();
|
||||
}
|
||||
|
||||
void AudioTestView::focus() {
|
||||
toggle_speaker.focus();
|
||||
}
|
||||
|
||||
void AudioTestView::update_audio_beep() {
|
||||
if (beep)
|
||||
baseband::request_audio_beep(field_frequency.value(), options_sample_rate.selected_index_value(), field_duration.value());
|
||||
else
|
||||
baseband::request_beep_stop();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
|
||||
#ifndef __UI_AUDIO_TEST_H__
|
||||
#define __UI_AUDIO_TEST_H__
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AudioTestView : public View {
|
||||
public:
|
||||
AudioTestView(NavigationView& nav);
|
||||
~AudioTestView();
|
||||
AudioTestView(const AudioTestView& other) = delete;
|
||||
AudioTestView& operator=(const AudioTestView& other) = delete;
|
||||
|
||||
void focus() override;
|
||||
void update_audio_beep();
|
||||
|
||||
std::string title() const override { return "Audio Test"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
bool beep{false};
|
||||
|
||||
Labels labels{
|
||||
{{7 * 8, 3 * 16}, "Audio Beep Test", Color::light_grey()},
|
||||
{{0 * 8, 6 * 16}, "Sample Rate (Hz):", Color::light_grey()},
|
||||
{{25 * 8, 7 * 16}, "Step:", Color::light_grey()},
|
||||
{{0 * 8, 8 * 16}, "Frequency (Hz):", Color::light_grey()},
|
||||
{{0 * 8, 10 * 16}, "Duration (ms):", Color::light_grey()},
|
||||
{{25 * 8, 10 * 16}, "0=con", Color::light_grey()},
|
||||
{{0 * 8, 12 * 16}, "Volume:", Color::light_grey()}};
|
||||
|
||||
OptionsField options_sample_rate{
|
||||
{18 * 8, 6 * 16},
|
||||
5,
|
||||
{{"24000", 24000},
|
||||
{"48000", 48000},
|
||||
{"12000", 12000}}};
|
||||
|
||||
OptionsField options_step{
|
||||
{26 * 8, 8 * 16},
|
||||
4,
|
||||
{{" 1", 1},
|
||||
{" 10", 10},
|
||||
{" 100", 100},
|
||||
{"1000", 1000}}};
|
||||
|
||||
NumberField field_frequency{
|
||||
{18 * 8, 8 * 16},
|
||||
5,
|
||||
{},
|
||||
100,
|
||||
' ',
|
||||
true};
|
||||
|
||||
NumberField field_duration{
|
||||
{18 * 8, 10 * 16},
|
||||
5,
|
||||
{0, 60000},
|
||||
50,
|
||||
' ',
|
||||
true};
|
||||
|
||||
AudioVolumeField field_volume{
|
||||
{21 * 8, 12 * 16}};
|
||||
|
||||
ImageToggle toggle_speaker{
|
||||
{21 * 8, 14 * 16, 2 * 8, 1 * 16},
|
||||
&bitmap_icon_speaker_mute,
|
||||
&bitmap_icon_speaker,
|
||||
Color::light_grey(),
|
||||
Color::dark_grey(),
|
||||
Color::green(),
|
||||
Color::dark_grey()};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif /*__UI_AUDIO_TEST_H__*/
|
||||
+79
-20
@@ -20,7 +20,7 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
// Code from https://github.com/Flipper-XFW/Xtreme-Apps/tree/04c3a60093e2c2378e79498b4505aa8072980a42/ble_spam/protocols
|
||||
// Code from https://github.com/Next-Flip/Momentum-Apps/blob/dev/ble_spam/
|
||||
// Thanks for the work of the original creators!
|
||||
|
||||
#include "ui_blespam.hpp"
|
||||
@@ -109,7 +109,8 @@ BLESpamView::BLESpamView(NavigationView& nav)
|
||||
console.writeln("Based on work of:");
|
||||
console.writeln("@Willy-JL, @ECTO-1A,");
|
||||
console.writeln("@Spooks4576, @iNetro");
|
||||
console.writeln("");
|
||||
console.writeln("---");
|
||||
console.writeln("iOS crash + Android\nattacks are patched\non new devices.");
|
||||
#endif
|
||||
changePacket(true); // init
|
||||
}
|
||||
@@ -293,6 +294,39 @@ void BLESpamView::createWindowsPacket() {
|
||||
std::copy(res.begin(), res.end(), advertisementData);
|
||||
}
|
||||
|
||||
void BLESpamView::createNameSpamPacket() {
|
||||
const char* names[] = {"PortaHack", "PwnBt", "iSpam", "GenericFoodVagon", "SignalSnoop", "ByteBandit", "RadioRogue", "RadioRebel", "ByteBlast"};
|
||||
|
||||
const char* name = names[rand() % 9]; //"PortaHack";
|
||||
uint8_t name_len = strlen(name);
|
||||
|
||||
uint8_t size = 12 + name_len;
|
||||
uint8_t i = 0;
|
||||
|
||||
packet[i++] = 2; // Size
|
||||
packet[i++] = 0x01; // AD Type (Flags)
|
||||
packet[i++] = 0x06; // Flags
|
||||
|
||||
packet[i++] = name_len + 1; // Size
|
||||
packet[i++] = 0x09; // AD Type (Complete Local Name)
|
||||
memcpy(&packet[i], name, name_len); // Device Name
|
||||
i += name_len;
|
||||
|
||||
packet[i++] = 3; // Size
|
||||
packet[i++] = 0x02; // AD Type (Incomplete Service UUID List)
|
||||
packet[i++] = 0x12; // Service UUID (Human Interface Device)
|
||||
packet[i++] = 0x18; // ...
|
||||
|
||||
packet[i++] = 2; // Size
|
||||
packet[i++] = 0x0A; // AD Type (Tx Power Level)
|
||||
packet[i++] = 0x00; // 0dBm
|
||||
|
||||
// size, packet
|
||||
std::string res = to_string_hex_array(packet, size);
|
||||
memset(advertisementData, 0, sizeof(advertisementData));
|
||||
std::copy(res.begin(), res.end(), advertisementData);
|
||||
}
|
||||
|
||||
void BLESpamView::createIosPacket(bool crash = false) {
|
||||
uint8_t ios_packet_sizes[18] = {0, 0, 0, 0, 0, 24, 0, 31, 0, 12, 0, 0, 20, 0, 12, 11, 11, 17};
|
||||
ContinuityType type;
|
||||
@@ -509,6 +543,48 @@ void BLESpamView::createFastPairPacket() {
|
||||
std::copy(res.begin(), res.end(), advertisementData);
|
||||
}
|
||||
|
||||
void BLESpamView::createAnyPacket(bool safe) {
|
||||
ATK_TYPE type[] = {
|
||||
ATK_ANDROID,
|
||||
ATK_IOS,
|
||||
ATK_WINDOWS,
|
||||
ATK_SAMSUNG,
|
||||
ATK_NAMESPAM,
|
||||
ATK_IOS_CRASH};
|
||||
ATK_TYPE attackType = type[rand() % (COUNT_OF(type) - (1 ? safe : 0))];
|
||||
createPacket(attackType);
|
||||
}
|
||||
|
||||
void BLESpamView::createPacket(ATK_TYPE attackType) {
|
||||
switch (attackType) {
|
||||
case ATK_IOS_CRASH:
|
||||
createIosPacket(true);
|
||||
break;
|
||||
case ATK_IOS:
|
||||
createIosPacket(false);
|
||||
break;
|
||||
case ATK_SAMSUNG:
|
||||
createSamsungPacket();
|
||||
break;
|
||||
case ATK_WINDOWS:
|
||||
createWindowsPacket();
|
||||
break;
|
||||
case ATK_NAMESPAM:
|
||||
createNameSpamPacket();
|
||||
break;
|
||||
case ATK_ALL_SAFE:
|
||||
createAnyPacket(true);
|
||||
break;
|
||||
case ATK_ALL:
|
||||
createAnyPacket(false);
|
||||
break;
|
||||
default:
|
||||
case ATK_ANDROID:
|
||||
createFastPairPacket();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BLESpamView::changePacket(bool forced = false) {
|
||||
counter++; // need to send it multiple times to be accepted
|
||||
if (counter >= 4 || forced) {
|
||||
@@ -517,24 +593,7 @@ void BLESpamView::changePacket(bool forced = false) {
|
||||
randomizeMac();
|
||||
randomChn();
|
||||
if (randomDev || forced) {
|
||||
switch (attackType) {
|
||||
case ATK_IOS_CRASH:
|
||||
createIosPacket(true);
|
||||
break;
|
||||
case ATK_IOS:
|
||||
createIosPacket(false);
|
||||
break;
|
||||
case ATK_SAMSUNG:
|
||||
createSamsungPacket();
|
||||
break;
|
||||
case ATK_WINDOWS:
|
||||
createWindowsPacket();
|
||||
break;
|
||||
default:
|
||||
case ATK_ANDROID:
|
||||
createFastPairPacket();
|
||||
break;
|
||||
}
|
||||
createPacket(attackType);
|
||||
}
|
||||
// rate limit console display
|
||||
#ifdef BLESPMUSECONSOLE
|
||||
|
||||
+11
-2
@@ -49,7 +49,10 @@ enum ATK_TYPE {
|
||||
ATK_IOS,
|
||||
ATK_IOS_CRASH,
|
||||
ATK_WINDOWS,
|
||||
ATK_SAMSUNG
|
||||
ATK_SAMSUNG,
|
||||
ATK_NAMESPAM,
|
||||
ATK_ALL_SAFE,
|
||||
ATK_ALL
|
||||
};
|
||||
enum PKT_TYPE {
|
||||
PKT_TYPE_INVALID_TYPE,
|
||||
@@ -124,7 +127,10 @@ class BLESpamView : public View {
|
||||
{"iOs", 1},
|
||||
{"iOs crash", 2},
|
||||
{"Windows", 3},
|
||||
{"Samsung", 4}}};
|
||||
{"Samsung", 4},
|
||||
{"NameSpam", 5},
|
||||
{"All-Safe", 6},
|
||||
{"All", 7}}};
|
||||
|
||||
bool is_running{false};
|
||||
|
||||
@@ -148,6 +154,9 @@ class BLESpamView : public View {
|
||||
void createIosPacket(bool crash);
|
||||
void createSamsungPacket();
|
||||
void createWindowsPacket();
|
||||
void createNameSpamPacket();
|
||||
void createAnyPacket(bool safe);
|
||||
void createPacket(ATK_TYPE attackType);
|
||||
void changePacket(bool forced);
|
||||
void on_tx_progress(const bool done);
|
||||
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_calculator.application_information"),
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'}, // optional
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
|
||||
+11
@@ -67,6 +67,15 @@ set(EXTCPPSRC
|
||||
external/extsensors/main.cpp
|
||||
external/extsensors/ui_extsensors.cpp
|
||||
external/extsensors/ui_extsensors.hpp
|
||||
|
||||
#foxhunt
|
||||
external/foxhunt/main.cpp
|
||||
external/foxhunt/ui_foxhunt_rx.cpp
|
||||
external/foxhunt/ui_foxhunt_rx.hpp
|
||||
|
||||
#audio_test
|
||||
external/audio_test/main.cpp
|
||||
external/audio_test/ui_audio_test.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -86,4 +95,6 @@ set(EXTAPPLIST
|
||||
keyfob
|
||||
tetris
|
||||
extsensors
|
||||
foxhunt_rx
|
||||
audio_test
|
||||
)
|
||||
|
||||
+12
@@ -39,6 +39,8 @@ MEMORY
|
||||
ram_external_app_keyfob(rwx) : org = 0xADBD0000, len = 32k
|
||||
ram_external_app_tetris(rwx) : org = 0xADBE0000, len = 32k
|
||||
ram_external_app_extsensors(rwx) : org = 0xADBF0000, len = 32k
|
||||
ram_external_app_foxhunt_rx(rwx) : org = 0xADC00000, len = 32k
|
||||
ram_external_app_audio_test(rwx) : org = 0xADC10000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -139,6 +141,16 @@ SECTIONS
|
||||
*(*ui*external_app*extsensors*);
|
||||
} > ram_external_app_extsensors
|
||||
|
||||
.external_app_foxhunt_rx : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_foxhunt_rx.application_information));
|
||||
*(*ui*external_app*foxhunt_rx*);
|
||||
} > ram_external_app_foxhunt_rx
|
||||
|
||||
.external_app_audio_test : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_audio_test.application_information));
|
||||
*(*ui*external_app*audio_test*);
|
||||
} > ram_external_app_audio_test
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_font_viewer.application_information"),
|
||||
/*.icon_color = */ ui::Color::cyan().v,
|
||||
/*.menu_location = */ app_location_t::DEBUG,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'},
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* 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_foxhunt_rx.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::foxhunt_rx {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<FoxhuntRxView>();
|
||||
}
|
||||
} // namespace ui::external_app::foxhunt_rx
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_foxhunt_rx.application_information"), used)) application_information_t _application_information_foxhunt_rx = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::foxhunt_rx::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "Fox hunt",
|
||||
/*.bitmap_data = */ {
|
||||
0x18,
|
||||
0x18,
|
||||
0x28,
|
||||
0x14,
|
||||
0x68,
|
||||
0x16,
|
||||
0x68,
|
||||
0x16,
|
||||
0xC8,
|
||||
0x13,
|
||||
0x88,
|
||||
0x11,
|
||||
0x04,
|
||||
0x20,
|
||||
0x24,
|
||||
0x24,
|
||||
0x22,
|
||||
0x44,
|
||||
0x01,
|
||||
0x80,
|
||||
0x06,
|
||||
0x60,
|
||||
0x98,
|
||||
0x19,
|
||||
0x20,
|
||||
0x04,
|
||||
0x40,
|
||||
0x02,
|
||||
0x80,
|
||||
0x01,
|
||||
0x00,
|
||||
0x00,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_am_audio */ {'P', 'A', 'M', 'A'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* 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_foxhunt_rx.hpp"
|
||||
|
||||
#include "audio.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::foxhunt_rx {
|
||||
|
||||
void FoxhuntRxView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
FoxhuntRxView::FoxhuntRxView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_am_audio);
|
||||
|
||||
add_children({&rssi,
|
||||
&field_rf_amp,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&field_volume,
|
||||
&field_frequency,
|
||||
&freq_stats_db,
|
||||
&rssi_graph,
|
||||
&geomap,
|
||||
&clear_markers,
|
||||
&add_current_marker});
|
||||
|
||||
clear_markers.on_select = [this](Button&) {
|
||||
geomap.clear_markers();
|
||||
};
|
||||
add_current_marker.on_select = [this](Button&) {
|
||||
GeoMarker tmp{my_lat, my_lon, my_orientation};
|
||||
geomap.store_marker(tmp);
|
||||
};
|
||||
geomap.set_mode(DISPLAY);
|
||||
geomap.set_manual_panning(false);
|
||||
// geomap.set_enable_additional_zoom(true);
|
||||
// geomap.set_hide_center_marker(true); //todo hide again after testing
|
||||
geomap.set_focusable(true);
|
||||
geomap.clear_markers();
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
|
||||
field_frequency.set_step(100);
|
||||
receiver_model.enable();
|
||||
audio::output::start();
|
||||
rssi_graph.set_nb_columns(64);
|
||||
geomap.init();
|
||||
}
|
||||
|
||||
FoxhuntRxView::~FoxhuntRxView() {
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
audio::output::stop();
|
||||
}
|
||||
|
||||
void FoxhuntRxView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
static int16_t last_max_db = -1000;
|
||||
rssi_graph.add_values(rssi.get_min(), rssi.get_avg(), rssi.get_max(), statistics.max_db);
|
||||
// refresh db
|
||||
if (last_max_db != statistics.max_db) {
|
||||
last_max_db = statistics.max_db;
|
||||
freq_stats_db.set("Power: " + to_string_dec_int(statistics.max_db) + " db");
|
||||
}
|
||||
|
||||
} /* on_statistic_updates */
|
||||
|
||||
void FoxhuntRxView::on_gps(const GPSPosDataMessage* msg) {
|
||||
my_lat = msg->lat;
|
||||
my_lon = msg->lon;
|
||||
geomap.update_my_position(msg->lat, msg->lon, msg->altitude);
|
||||
geomap.move(my_lon, my_lat);
|
||||
geomap.set_dirty();
|
||||
}
|
||||
void FoxhuntRxView::on_orientation(const OrientationDataMessage* msg) {
|
||||
my_orientation = msg->angle;
|
||||
geomap.set_angle(msg->angle);
|
||||
geomap.update_my_orientation(msg->angle, true);
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::foxhunt_rx
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* 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 __UI_FOXHUNT_RX_H__
|
||||
#define __UI_FOXHUNT_RX_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_geomap.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::foxhunt_rx {
|
||||
|
||||
class FoxhuntRxView : public View {
|
||||
public:
|
||||
FoxhuntRxView(NavigationView& nav);
|
||||
~FoxhuntRxView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Fox hunt"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_foxhunt", app_settings::Mode::RX};
|
||||
|
||||
RFAmpField field_rf_amp{
|
||||
{13 * 8, 0 * 16}};
|
||||
LNAGainField field_lna{
|
||||
{15 * 8, 0 * 16}};
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, 0 * 16}};
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, 6 * 8, 4}};
|
||||
AudioVolumeField field_volume{
|
||||
{28 * 8, 0 * 16}};
|
||||
|
||||
RxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
nav_};
|
||||
|
||||
// Power: -XXX db
|
||||
Text freq_stats_db{
|
||||
{0 * 8, 2 * 16 + 4, 14 * 8, 14},
|
||||
};
|
||||
RSSIGraph rssi_graph{
|
||||
{0, 50, 240, 30},
|
||||
};
|
||||
|
||||
Button clear_markers{
|
||||
{10 * 8, 18, 8 * 8, 16},
|
||||
LanguageHelper::currentMessages[LANG_CLEAR]};
|
||||
|
||||
Button add_current_marker{
|
||||
{2, 18, 7 * 8, 16},
|
||||
"Mark"};
|
||||
|
||||
GeoMap geomap{{0, 80, 240, 240}};
|
||||
|
||||
MessageHandlerRegistration message_handler_gps{
|
||||
Message::ID::GPSPosData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const GPSPosDataMessage*>(p);
|
||||
this->on_gps(message);
|
||||
}};
|
||||
MessageHandlerRegistration message_handler_orientation{
|
||||
Message::ID::OrientationData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const OrientationDataMessage*>(p);
|
||||
this->on_orientation(message);
|
||||
}};
|
||||
MessageHandlerRegistration message_handler_stats{
|
||||
Message::ID::ChannelStatistics,
|
||||
[this](const Message* const p) {
|
||||
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
|
||||
}};
|
||||
|
||||
float my_lat = 200;
|
||||
float my_lon = 200;
|
||||
uint16_t my_orientation = 400;
|
||||
|
||||
void on_gps(const GPSPosDataMessage* msg);
|
||||
void on_orientation(const OrientationDataMessage* msg);
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::foxhunt_rx
|
||||
|
||||
#endif /*__UI_FOXHUNT_RX_H__*/
|
||||
+2
-2
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_pacman.application_information"), used
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'}, // optional
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
|
||||
@@ -76,15 +76,15 @@ SpectrumPainterView::SpectrumPainterView(
|
||||
transmitter_model.set_rf_amp(rf_amp);
|
||||
};
|
||||
|
||||
input_image.on_input_avaliable = [this]() {
|
||||
image_input_avaliable = true;
|
||||
input_image.on_input_available = [this]() {
|
||||
image_input_available = true;
|
||||
};
|
||||
|
||||
button_play.on_select = [this](ImageButton&) {
|
||||
if (tx_active == false) {
|
||||
tx_mode = tab_view.selected();
|
||||
|
||||
if (tx_mode == 0 && image_input_avaliable == false)
|
||||
if (tx_mode == 0 && image_input_available == false)
|
||||
return;
|
||||
/* By experimental test measurement, we got a good painted spectrum quality when selecting
|
||||
a BW GUI App range from 100k ... 2M aprox. In that range , the best TX LPF filter = 1M75 (the min)
|
||||
|
||||
@@ -61,7 +61,7 @@ class SpectrumPainterView : public View {
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_painter", app_settings::Mode::TX};
|
||||
|
||||
bool image_input_avaliable{false};
|
||||
bool image_input_available{false};
|
||||
bool tx_active{false};
|
||||
uint32_t tx_mode{0};
|
||||
uint16_t tx_current_line{0};
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "io_file.hpp"
|
||||
#include "file.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace ui::external_app::spainter {
|
||||
|
||||
@@ -39,15 +40,14 @@ SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
|
||||
button_load_image.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".bmp");
|
||||
|
||||
constexpr auto data_directory = u"SPECTRUM";
|
||||
ensure_directory(data_directory);
|
||||
open_view->push_dir(data_directory);
|
||||
ensure_directory(spectrum_dir);
|
||||
open_view->push_dir(spectrum_dir);
|
||||
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
this->file = new_file_path.string();
|
||||
painted = false;
|
||||
this->set_dirty();
|
||||
this->on_input_avaliable();
|
||||
this->on_input_available();
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class SpectrumInputImageView : public View {
|
||||
uint16_t get_height();
|
||||
std::vector<uint8_t> get_line(uint16_t);
|
||||
|
||||
std::function<void()> on_input_avaliable{};
|
||||
std::function<void()> on_input_available{};
|
||||
|
||||
private:
|
||||
bool painted{false};
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_tetris.application_information"), used
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'}, // optional
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
|
||||
@@ -461,6 +461,15 @@ std::string path::string() const {
|
||||
return conv.to_bytes(native());
|
||||
}
|
||||
|
||||
// appends a string to the end of filename, but leaves the extension asd.txt + "fg" -> asdfg.txt
|
||||
path& path::append_filename(const string_type& str) {
|
||||
const auto t = extension().native();
|
||||
_s.erase(_s.size() - t.size()); // remove extension
|
||||
_s += str; // append string
|
||||
_s += t; // add back extension
|
||||
return *this;
|
||||
}
|
||||
|
||||
path& path::replace_extension(const path& replacement) {
|
||||
const auto t = extension().native();
|
||||
_s.erase(_s.size() - t.size());
|
||||
|
||||
@@ -155,6 +155,8 @@ struct path {
|
||||
|
||||
path& replace_extension(const path& replacement = path());
|
||||
|
||||
path& append_filename(const string_type& str);
|
||||
|
||||
private:
|
||||
string_type _s;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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 "file_path.hpp"
|
||||
#include "file.hpp"
|
||||
|
||||
const std::filesystem::path adsb_dir = u"ADSB";
|
||||
const std::filesystem::path ais_dir = u"AIS";
|
||||
const std::filesystem::path aprs_dir = u"APRS";
|
||||
const std::filesystem::path audio_dir = u"AUDIO";
|
||||
const std::filesystem::path blerx_dir = u"BLERX";
|
||||
const std::filesystem::path bletx_dir = u"BLETX";
|
||||
const std::filesystem::path captures_dir = u"CAPTURES";
|
||||
const std::filesystem::path debug_dir = u"DEBUG";
|
||||
const std::filesystem::path firmware_dir = u"FIRMWARE";
|
||||
const std::filesystem::path freqman_dir = u"FREQMAN";
|
||||
const std::filesystem::path logs_dir = u"LOGS";
|
||||
const std::filesystem::path looking_glass_dir = u"LOOKINGGLASS";
|
||||
const std::filesystem::path playlist_dir = u"PLAYLIST";
|
||||
const std::filesystem::path remotes_dir = u"REMOTES";
|
||||
const std::filesystem::path repeat_rec_path = u"CAPTURES";
|
||||
const std::filesystem::path screenshots_dir = u"SCREENSHOTS";
|
||||
const std::filesystem::path settings_dir = u"SETTINGS";
|
||||
const std::filesystem::path spectrum_dir = u"SPECTRUM";
|
||||
const std::filesystem::path splash_dir = u"SPLASH";
|
||||
const std::filesystem::path sstv_dir = u"SSTV";
|
||||
const std::filesystem::path wav_dir = u"WAV";
|
||||
const std::filesystem::path whipcalc_dir = u"WHIPCALC";
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
|
||||
#ifndef __FILE_PATH_H__
|
||||
#define __FILE_PATH_H__
|
||||
|
||||
#include "file.hpp"
|
||||
|
||||
extern const std::filesystem::path adsb_dir;
|
||||
extern const std::filesystem::path ais_dir;
|
||||
extern const std::filesystem::path aprs_dir;
|
||||
extern const std::filesystem::path audio_dir;
|
||||
extern const std::filesystem::path blerx_dir;
|
||||
extern const std::filesystem::path bletx_dir;
|
||||
extern const std::filesystem::path captures_dir;
|
||||
extern const std::filesystem::path debug_dir;
|
||||
extern const std::filesystem::path firmware_dir;
|
||||
extern const std::filesystem::path freqman_dir;
|
||||
extern const std::filesystem::path logs_dir;
|
||||
extern const std::filesystem::path looking_glass_dir;
|
||||
extern const std::filesystem::path playlist_dir;
|
||||
extern const std::filesystem::path remotes_dir;
|
||||
extern const std::filesystem::path repeat_rec_path;
|
||||
extern const std::filesystem::path screenshots_dir;
|
||||
extern const std::filesystem::path settings_dir;
|
||||
extern const std::filesystem::path spectrum_dir;
|
||||
extern const std::filesystem::path splash_dir;
|
||||
extern const std::filesystem::path sstv_dir;
|
||||
extern const std::filesystem::path wav_dir;
|
||||
extern const std::filesystem::path whipcalc_dir;
|
||||
|
||||
#endif /* __FILE_PATH_H__ */
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "tone_key.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cctype>
|
||||
@@ -37,7 +38,6 @@
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const std::filesystem::path freqman_dir{u"/FREQMAN"};
|
||||
const std::filesystem::path freqman_extension{u".TXT"};
|
||||
|
||||
// NB: Don't include UI headers to keep this code unit testable.
|
||||
@@ -376,7 +376,7 @@ bool parse_freqman_entry(std::string_view str, freqman_entry& entry) {
|
||||
} else if (key == "c") {
|
||||
entry.tone = parse_tone_key(value);
|
||||
} else if (key == "d") {
|
||||
entry.description = trim(value);
|
||||
entry.description = trim(value).substr(0, freqman_max_desc_size);
|
||||
} else if (key == "f") {
|
||||
entry.type = freqman_type::Single;
|
||||
parse_int(value, entry.frequency_a);
|
||||
@@ -492,7 +492,7 @@ freqman_entry FreqmanDB::operator[](Index index) const {
|
||||
return entry;
|
||||
else if (read_raw_) {
|
||||
entry.type = freqman_type::Raw;
|
||||
entry.description = trim(*line_text);
|
||||
entry.description = trim(*line_text).substr(0, freqman_max_desc_size);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include <vector>
|
||||
|
||||
/* Defined in freqman_db.cpp */
|
||||
extern const std::filesystem::path freqman_dir;
|
||||
extern const std::filesystem::path freqman_extension;
|
||||
|
||||
using freqman_index_t = uint8_t;
|
||||
@@ -162,6 +161,9 @@ std::string freqman_entry_get_step_string_short(freqman_index_t step);
|
||||
* ensure app memory stability. */
|
||||
constexpr size_t freqman_default_max_entries = 150;
|
||||
|
||||
/* Limiting description to 30 as specified by the format */
|
||||
constexpr size_t freqman_max_desc_size = 30;
|
||||
|
||||
struct freqman_load_options {
|
||||
/* Loads all entries when set to 0. */
|
||||
size_t max_entries{freqman_default_max_entries};
|
||||
|
||||
@@ -150,6 +150,8 @@ void MAX2837::init() {
|
||||
}
|
||||
|
||||
void MAX2837::set_tx_LO_iq_phase_calibration(const size_t v) {
|
||||
Mode saved_mode = get_mode();
|
||||
|
||||
/* TX IQ phase deg CAL adj (+4 ...-4) in 32 steps (5 bits), 00000 = +4deg (Q lags I by 94degs, default), 01111 = +0deg, 11111 = -4deg (Q lags I by 86degs) */
|
||||
|
||||
// TX calibration , Logic pins , ENABLE, RXENABLE, TXENABLE = 1,0,1 (5dec), and Reg address 16, D1 (CAL mode 1):DO (CHIP ENABLE 1)
|
||||
@@ -172,7 +174,7 @@ void MAX2837::set_tx_LO_iq_phase_calibration(const size_t v) {
|
||||
_map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (1 = Chip select enable )
|
||||
flush_one(Register::SPI_EN);
|
||||
|
||||
set_mode(Mode::Standby); // Back 3 logic pins CALIBRATION mode -> Standby.
|
||||
set_mode(saved_mode); // restore original mode
|
||||
}
|
||||
|
||||
enum class Mask { // There are class Mask ,and class mode with same names, but they are not same.
|
||||
@@ -205,12 +207,18 @@ Mask mode_mask(const Mode mode) { // based on enum Mode cases, we set up the co
|
||||
}
|
||||
|
||||
void MAX2837::set_mode(const Mode mode) { // We set up the 3 Logic Pins ENABLE, RXENABLE, TXENABLE accordingly to the max2837 mode case, that we want to set up .
|
||||
_mode = mode;
|
||||
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable));
|
||||
gpio_max2837_txenable.write(toUType(mask) & toUType(Mask::TxEnable));
|
||||
}
|
||||
|
||||
Mode MAX2837::get_mode() {
|
||||
return _mode;
|
||||
}
|
||||
|
||||
void MAX2837::flush() {
|
||||
if (_dirty) {
|
||||
for (size_t n = 0; n < reg_count; n++) {
|
||||
|
||||
@@ -841,6 +841,7 @@ class MAX2837 : public MAX283x {
|
||||
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
Mode _mode{Mode::Standby};
|
||||
|
||||
RegisterMap _map{initial_register_values};
|
||||
DirtyRegisters<Register, reg_count> _dirty{};
|
||||
@@ -851,6 +852,7 @@ class MAX2837 : public MAX283x {
|
||||
reg_t read(const Register reg);
|
||||
|
||||
void flush();
|
||||
Mode get_mode();
|
||||
};
|
||||
|
||||
} // namespace max2837
|
||||
|
||||
@@ -143,6 +143,8 @@ void MAX2839::init() {
|
||||
}
|
||||
|
||||
void MAX2839::set_tx_LO_iq_phase_calibration(const size_t v) {
|
||||
Mode saved_mode = get_mode();
|
||||
|
||||
/* IQ phase deg CAL adj (+4 ...-4) This IC in 64 steps (6 bits), 000000 = +4deg (Q lags I by 94degs, default), 011111 = +0deg, 111111 = -4deg (Q lags I by 86degs) */
|
||||
|
||||
// TX calibration , 2 x Logic pins , ENABLE, RXENABLE = 1,0, (2dec), and Reg address 16, D1 (CAL mode 1):DO (CHIP ENABLE 1)
|
||||
@@ -164,7 +166,7 @@ void MAX2839::set_tx_LO_iq_phase_calibration(const size_t v) {
|
||||
_map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (1 = Chip select enable )
|
||||
flush_one(Register::SPI_EN);
|
||||
|
||||
set_mode(Mode::Standby); // Back 3 logic pins CALIBRATION mode -> Standby.
|
||||
set_mode(saved_mode); // restore original mode
|
||||
}
|
||||
|
||||
enum class Mask {
|
||||
@@ -196,11 +198,17 @@ Mask mode_mask(const Mode mode) {
|
||||
}
|
||||
|
||||
void MAX2839::set_mode(const Mode mode) {
|
||||
_mode = mode;
|
||||
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx));
|
||||
}
|
||||
|
||||
Mode MAX2839::get_mode() {
|
||||
return _mode;
|
||||
}
|
||||
|
||||
void MAX2839::flush() {
|
||||
if (_dirty) {
|
||||
for (size_t n = 0; n < reg_count; n++) {
|
||||
|
||||
@@ -700,6 +700,7 @@ class MAX2839 : public MAX283x {
|
||||
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
Mode _mode{Mode::Standby};
|
||||
|
||||
RegisterMap _map{initial_register_values};
|
||||
DirtyRegisters<Register, reg_count> _dirty{};
|
||||
@@ -712,6 +713,7 @@ class MAX2839 : public MAX283x {
|
||||
void flush();
|
||||
|
||||
void configure_rx_gain();
|
||||
Mode get_mode();
|
||||
};
|
||||
|
||||
} // namespace max2839
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#include "file.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
|
||||
#define LOG_ROOT_DIR "LOGS"
|
||||
|
||||
class LogFile {
|
||||
public:
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
|
||||
@@ -31,6 +31,9 @@ using namespace std::literals;
|
||||
|
||||
const std::string_view center_freq_name = "center_frequency"sv;
|
||||
const std::string_view sample_rate_name = "sample_rate"sv;
|
||||
const std::string_view latitude_name = "latitude"sv;
|
||||
const std::string_view longitude_name = "longitude"sv;
|
||||
const std::string_view satinuse_name = "satinuse"sv;
|
||||
|
||||
fs::path get_metadata_path(const fs::path& capture_path) {
|
||||
auto temp = capture_path;
|
||||
@@ -54,6 +57,23 @@ Optional<File::Error> write_metadata_file(const fs::path& path, capture_metadata
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
// add gps data if available
|
||||
if (metadata.latitude != 0 && metadata.longitude != 0 && metadata.latitude < 200 && metadata.longitude < 200) {
|
||||
error = f.write_line(std::string{latitude_name} + "=" +
|
||||
to_string_decimal(metadata.latitude, 7));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = f.write_line(std::string{longitude_name} + "=" +
|
||||
to_string_decimal(metadata.longitude, 7));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = f.write_line(std::string{satinuse_name} + "=" +
|
||||
to_string_dec_uint(metadata.satinuse));
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -77,6 +97,12 @@ Optional<capture_metadata> read_metadata_file(const fs::path& path) {
|
||||
parse_int(cols[1], metadata.center_frequency);
|
||||
else if (cols[0] == sample_rate_name)
|
||||
parse_int(cols[1], metadata.sample_rate);
|
||||
else if (cols[0] == latitude_name)
|
||||
parse_float_meta(cols[1], metadata.latitude);
|
||||
else if (cols[0] == longitude_name)
|
||||
parse_float_meta(cols[1], metadata.longitude);
|
||||
else if (cols[0] == satinuse_name)
|
||||
parse_int(cols[1], metadata.satinuse);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
@@ -86,3 +112,18 @@ Optional<capture_metadata> read_metadata_file(const fs::path& path) {
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
bool parse_float_meta(std::string_view str, float& out_val) {
|
||||
out_val = {};
|
||||
|
||||
if (str.size() > max_parse_int_length)
|
||||
return false;
|
||||
|
||||
// Copy onto the stack and null terminate.
|
||||
char zstr[max_parse_int_length + 1];
|
||||
std::memcpy(zstr, str.data(), str.size());
|
||||
zstr[str.size()] = '\0';
|
||||
errno = 0;
|
||||
out_val = strtod(zstr, nullptr);
|
||||
return (errno == 0);
|
||||
}
|
||||
@@ -29,6 +29,9 @@
|
||||
struct capture_metadata {
|
||||
rf::Frequency center_frequency;
|
||||
uint32_t sample_rate;
|
||||
float latitude = 0;
|
||||
float longitude = 0;
|
||||
uint8_t satinuse = 0;
|
||||
};
|
||||
|
||||
std::filesystem::path get_metadata_path(const std::filesystem::path& capture_path);
|
||||
@@ -36,4 +39,5 @@ std::filesystem::path get_metadata_path(const std::filesystem::path& capture_pat
|
||||
Optional<File::Error> write_metadata_file(const std::filesystem::path& path, capture_metadata metadata);
|
||||
Optional<capture_metadata> read_metadata_file(const std::filesystem::path& path);
|
||||
|
||||
bool parse_float_meta(std::string_view str, float& out_val);
|
||||
#endif // __METADATA_FILE_HPP__
|
||||
@@ -107,12 +107,14 @@ void BtnGridView::on_tick_second() {
|
||||
}
|
||||
|
||||
void BtnGridView::clear() {
|
||||
std::vector<GridItem>().swap(menu_items); // clear vector and release memory
|
||||
// clear vector and release memory, not using swap since it's causing capture to glitch/fault
|
||||
menu_items.clear();
|
||||
|
||||
for (auto& item : menu_item_views)
|
||||
remove_child(item.get());
|
||||
|
||||
std::vector<std::unique_ptr<NewButton>>().swap(menu_item_views); // clear vector and release memory
|
||||
// clear vector and release memory, not using swap since it's causing capture to glitch/fault
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
|
||||
|
||||
@@ -34,6 +34,7 @@ using namespace portapack;
|
||||
#include "complex.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "ui_font_fixed_5x8.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@@ -447,7 +448,7 @@ void GeoMap::move(const float lon, const float lat) {
|
||||
}
|
||||
|
||||
bool GeoMap::init() {
|
||||
auto result = map_file.open("ADSB/world_map.bin");
|
||||
auto result = map_file.open(adsb_dir / u"world_map.bin");
|
||||
map_opened = !result.is_valid();
|
||||
|
||||
if (map_opened) {
|
||||
@@ -613,7 +614,7 @@ void GeoMapView::focus() {
|
||||
geopos.focus();
|
||||
|
||||
if (!geomap.map_file_opened())
|
||||
nav_.display_modal("No map", "No world_map.bin file in\n/ADSB/ directory", ABORT);
|
||||
nav_.display_modal("No map", "No world_map.bin file in\n/" + adsb_dir.string() + "/ directory", ABORT);
|
||||
}
|
||||
|
||||
void GeoMapView::update_my_position(float lat, float lon, int32_t altitude) {
|
||||
|
||||
@@ -59,43 +59,19 @@ void QRCodeImage::paint(Painter& painter) {
|
||||
// The structure to manage the QR code
|
||||
QRCode qrcode;
|
||||
|
||||
// Either small or large QR code can be shown..
|
||||
int qr_version = 10;
|
||||
|
||||
if (portapack::persistent_memory::show_bigger_qr_code()) { // show large QR code
|
||||
int qr_version = 2;
|
||||
// Allocate a chunk of memory to store the QR code
|
||||
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
|
||||
|
||||
// Allocate a chunk of memory to store the QR code
|
||||
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
|
||||
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
|
||||
|
||||
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
|
||||
display.fill_rectangle(Rect(57, 65, 126, 127), Color::white());
|
||||
|
||||
display.fill_rectangle(Rect(10, 30, 220, 220), Color::white());
|
||||
|
||||
for (uint8_t y = 0; y < qrcode.size; y++) {
|
||||
for (uint8_t x = 0; x < qrcode.size; x++) {
|
||||
if (qrcode_getModule(&qrcode, x, y)) {
|
||||
display.fill_rectangle(Rect(20 + (x * 8), 40 + (y * 8), 8, 8), Color::black());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else { // show small QR code
|
||||
int qr_version = 10;
|
||||
|
||||
// Allocate a chunk of memory to store the QR code
|
||||
uint8_t qrcodeBytes[qrcode_getBufferSize(qr_version)];
|
||||
|
||||
qrcode_initText(&qrcode, qrcodeBytes, qr_version, ECC_HIGH, qr_text_);
|
||||
|
||||
display.fill_rectangle(Rect(92, 97, 63, 63), Color::white());
|
||||
|
||||
for (uint8_t y = 0; y < qrcode.size; y++) {
|
||||
for (uint8_t x = 0; x < qrcode.size; x++) {
|
||||
if (qrcode_getModule(&qrcode, x, y)) {
|
||||
display.draw_pixel(Point(95 + x, 100 + y), Color::black());
|
||||
}
|
||||
for (uint8_t y = 0; y < qrcode.size; y++) {
|
||||
for (uint8_t x = 0; x < qrcode.size; x++) {
|
||||
if (qrcode_getModule(&qrcode, x, y)) {
|
||||
display.fill_rectangle(Rect(63 + (x * 2), 71 + (y * 2), 2, 2), Color::black());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
#include "file.hpp"
|
||||
#include "file_reader.hpp"
|
||||
#include "png_writer.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using portapack::receiver_model;
|
||||
using portapack::transmitter_model;
|
||||
@@ -473,8 +474,8 @@ void SystemStatusView::on_bias_tee() {
|
||||
}
|
||||
|
||||
void SystemStatusView::on_camera() {
|
||||
ensure_directory("SCREENSHOTS");
|
||||
auto path = next_filename_matching_pattern(u"SCREENSHOTS/SCR_????.PNG");
|
||||
ensure_directory(screenshots_dir);
|
||||
auto path = next_filename_matching_pattern(screenshots_dir / u"SCR_????.PNG");
|
||||
|
||||
if (path.empty())
|
||||
return;
|
||||
@@ -535,7 +536,7 @@ void SystemStatusView::rtc_battery_workaround() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ensure_directory(SETTINGS_DIR);
|
||||
ensure_directory(settings_dir);
|
||||
make_new_file(DATE_FILEFLAG);
|
||||
|
||||
year = 1980;
|
||||
|
||||
@@ -205,6 +205,11 @@ void RecordView::start() {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for geo data, if present append filename with _GEO
|
||||
if (latitude != 0 && longitude != 0 && latitude < 200 && longitude < 200) {
|
||||
base_path.append_filename(u"_GEO");
|
||||
}
|
||||
|
||||
std::unique_ptr<stream::Writer> writer;
|
||||
switch (file_type) {
|
||||
case FileType::WAV: {
|
||||
@@ -223,7 +228,7 @@ void RecordView::start() {
|
||||
case FileType::RawS8:
|
||||
case FileType::RawS16: {
|
||||
const auto metadata_file_error = write_metadata_file(
|
||||
get_metadata_path(base_path), {receiver_model.target_frequency(), sampling_rate});
|
||||
get_metadata_path(base_path), {receiver_model.target_frequency(), sampling_rate, latitude, longitude, satinuse});
|
||||
if (metadata_file_error.is_valid()) {
|
||||
handle_error(metadata_file_error.value());
|
||||
return;
|
||||
@@ -342,6 +347,12 @@ void RecordView::trim_capture() {
|
||||
trim_path = {};
|
||||
}
|
||||
|
||||
void RecordView::on_gps(const GPSPosDataMessage* msg) {
|
||||
latitude = msg->lat;
|
||||
longitude = msg->lon;
|
||||
satinuse = msg->satinuse;
|
||||
}
|
||||
|
||||
void RecordView::handle_capture_thread_done(const File::Error error) {
|
||||
stop();
|
||||
if (error.code()) {
|
||||
|
||||
@@ -88,6 +88,7 @@ class RecordView : public View {
|
||||
|
||||
OversampleRate get_oversample_rate(uint32_t sample_rate);
|
||||
|
||||
void on_gps(const GPSPosDataMessage* msg);
|
||||
// bool pitch_rssi_enabled = false;
|
||||
|
||||
// Time Stamp
|
||||
@@ -95,6 +96,10 @@ class RecordView : public View {
|
||||
bool filename_as_is = false;
|
||||
rtc::RTC datetime{};
|
||||
|
||||
float latitude = 0; // for wardriwing with ext module
|
||||
float longitude = 0;
|
||||
uint8_t satinuse = 0; // to see if there was enough sats used or not
|
||||
|
||||
const std::filesystem::path filename_stem_pattern;
|
||||
const std::filesystem::path folder;
|
||||
FileType file_type;
|
||||
@@ -147,6 +152,13 @@ class RecordView : public View {
|
||||
const auto message = *reinterpret_cast<const CaptureThreadDoneMessage*>(p);
|
||||
this->handle_capture_thread_done(message.error);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_gps{
|
||||
Message::ID::GPSPosData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const GPSPosDataMessage*>(p);
|
||||
this->on_gps(message);
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "ui_flash_utility.hpp"
|
||||
#include "untar.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
#include "usb_serial_shell_filesystem.hpp"
|
||||
@@ -167,8 +168,8 @@ static void cmd_screenshot(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
ensure_directory("SCREENSHOTS");
|
||||
auto path = next_filename_matching_pattern(u"SCREENSHOTS/SCR_????.PNG");
|
||||
ensure_directory(screenshots_dir);
|
||||
auto path = next_filename_matching_pattern(screenshots_dir / u"SCR_????.PNG");
|
||||
|
||||
if (path.empty())
|
||||
return;
|
||||
@@ -905,8 +906,8 @@ static void cmd_cpld_read(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
static void cmd_gotgps(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage = "usage: gotgps <lat> <lon> [altitude] [speed]\r\n";
|
||||
if (argc < 2 || argc > 4) {
|
||||
const char* usage = "usage: gotgps <lat> <lon> [altitude] [speed] [satinuse]\r\n";
|
||||
if (argc < 2 || argc > 5) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
@@ -914,9 +915,11 @@ static void cmd_gotgps(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
float lon = atof(argv[1]);
|
||||
int32_t altitude = 0;
|
||||
int32_t speed = 0;
|
||||
uint8_t satinuse = 0;
|
||||
if (argc >= 3) altitude = strtol(argv[2], NULL, 10);
|
||||
if (argc >= 4) speed = strtol(argv[3], NULL, 10);
|
||||
GPSPosDataMessage msg{lat, lon, altitude, speed};
|
||||
if (argc >= 5) satinuse = strtol(argv[4], NULL, 10);
|
||||
GPSPosDataMessage msg{lat, lon, altitude, speed, satinuse};
|
||||
EventDispatcher::send_message(msg);
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
@@ -1035,9 +1038,9 @@ static void cmd_settingsreset(BaseSequentialStream* chp, int argc, char* argv[])
|
||||
if (!nav) return;
|
||||
nav->home(true); // to exit all running apps
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(SETTINGS_DIR, u"*.ini")) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(settings_dir, u"*.ini")) {
|
||||
if (std::filesystem::is_regular_file(entry.status())) {
|
||||
std::filesystem::path pth = SETTINGS_DIR;
|
||||
std::filesystem::path pth = settings_dir;
|
||||
pth += u"/" + entry.path();
|
||||
chprintf(chp, pth.string().c_str());
|
||||
chprintf(chp, "\r\n");
|
||||
|
||||
@@ -290,7 +290,7 @@ macro(DeclareTargets chunk_tag name)
|
||||
DEPENDS ${PROJECT_NAME}.elf ${MAKE_IMAGE_CHUNK}
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
|
||||
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
|
||||
|
||||
else()
|
||||
@@ -434,12 +434,12 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PNFM nfm_audio)
|
||||
|
||||
### No op
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_noop.cpp
|
||||
)
|
||||
DeclareTargets(PNOP no_operation)
|
||||
#### No op
|
||||
#
|
||||
#set(MODE_CPPSRC
|
||||
# proc_noop.cpp
|
||||
#)
|
||||
#DeclareTargets(PNOP no_operation)
|
||||
|
||||
### OOK
|
||||
|
||||
@@ -519,7 +519,7 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PWFM wfm_audio)
|
||||
|
||||
### SubGhz Decoders
|
||||
### SubGhz Decoders
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_subghzd.cpp
|
||||
@@ -554,7 +554,7 @@ set(MODE_INCDIR
|
||||
)
|
||||
set(MODE_CPPSRC
|
||||
sd_over_usb/proc_sd_over_usb.cpp
|
||||
|
||||
|
||||
sd_over_usb/scsi.c
|
||||
sd_over_usb/diskio.c
|
||||
sd_over_usb/sd_over_usb.c
|
||||
@@ -609,7 +609,6 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PNRR nrfrx)
|
||||
|
||||
|
||||
### Jammer
|
||||
|
||||
set(MODE_CPPSRC
|
||||
@@ -645,6 +644,12 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PTST test)
|
||||
|
||||
### Audio Beep
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_audio_beep.cpp
|
||||
)
|
||||
DeclareTargets(PABP audio_beep)
|
||||
|
||||
### HackRF "factory" firmware
|
||||
|
||||
@@ -672,7 +677,7 @@ set(BASEBAND_IMAGES ${BASEBAND_IMAGES} terminator.img)
|
||||
#######################################################################
|
||||
|
||||
project(baseband)
|
||||
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_NAME}.img
|
||||
COMMAND cat ${BASEBAND_IMAGES} > ${PROJECT_NAME}.img
|
||||
|
||||
@@ -25,9 +25,11 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#include "hal.h"
|
||||
#include "gpdma.hpp"
|
||||
#include "tone_gen.hpp"
|
||||
|
||||
using namespace lpc43xx;
|
||||
|
||||
@@ -36,6 +38,8 @@ using namespace lpc43xx;
|
||||
namespace audio {
|
||||
namespace dma {
|
||||
|
||||
ToneGen tone_gen{};
|
||||
|
||||
constexpr uint32_t gpdma_ahb_master_peripheral = 1;
|
||||
constexpr uint32_t gpdma_ahb_master_memory = 0;
|
||||
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
|
||||
@@ -145,9 +149,14 @@ static volatile const gpdma::channel::LLI* tx_next_lli = nullptr;
|
||||
static volatile const gpdma::channel::LLI* rx_next_lli = nullptr;
|
||||
|
||||
static bool single_tx_buffer = false;
|
||||
static uint32_t beep_duration_downcounter = 0;
|
||||
|
||||
static void tx_transfer_complete() {
|
||||
tx_next_lli = gpdma_channel_i2s0_tx.next_lli();
|
||||
|
||||
if (beep_duration_downcounter != 0)
|
||||
if (--beep_duration_downcounter == 0)
|
||||
beep_stop();
|
||||
}
|
||||
|
||||
static void tx_error() {
|
||||
@@ -162,14 +171,6 @@ static void rx_error() {
|
||||
disable();
|
||||
}
|
||||
|
||||
void init() {
|
||||
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
|
||||
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
|
||||
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
|
||||
}
|
||||
|
||||
static void configure_tx() {
|
||||
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->TXFIFO);
|
||||
const auto control_value = control_tx(transfer_bytes);
|
||||
@@ -194,22 +195,32 @@ static void configure_rx() {
|
||||
}
|
||||
}
|
||||
|
||||
void configure() {
|
||||
configure_tx();
|
||||
configure_rx();
|
||||
static void enable_tx() {
|
||||
const auto gpdma_config_tx = config_tx();
|
||||
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
|
||||
gpdma_channel_i2s0_tx.enable();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
const auto gpdma_config_tx = config_tx();
|
||||
static void enable_rx() {
|
||||
const auto gpdma_config_rx = config_rx();
|
||||
|
||||
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
|
||||
gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx);
|
||||
|
||||
gpdma_channel_i2s0_tx.enable();
|
||||
gpdma_channel_i2s0_rx.enable();
|
||||
}
|
||||
|
||||
void init_audio_out() {
|
||||
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
|
||||
configure_tx();
|
||||
enable_tx();
|
||||
}
|
||||
|
||||
void init_audio_in() {
|
||||
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
|
||||
configure_rx();
|
||||
enable_rx();
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel_i2s0_tx.disable();
|
||||
gpdma_channel_i2s0_rx.disable();
|
||||
@@ -217,12 +228,54 @@ void disable() {
|
||||
|
||||
void shrink_tx_buffer(bool shrink) {
|
||||
single_tx_buffer = shrink;
|
||||
|
||||
if (transfers_per_buffer == 1)
|
||||
return;
|
||||
|
||||
if (single_tx_buffer)
|
||||
lli_tx_loop[0].lli = lli_pointer(&lli_tx_loop[0]);
|
||||
else
|
||||
lli_tx_loop[0].lli = lli_pointer(&lli_tx_loop[1]);
|
||||
}
|
||||
|
||||
void beep_start(uint32_t freq, uint32_t sample_rate, uint32_t beep_duration_ms) {
|
||||
// Prevent divide-by-0
|
||||
if (freq == 0 || sample_rate == 0)
|
||||
return;
|
||||
|
||||
// Fill entire buffer with sine waves
|
||||
tone_gen.configure_beep(freq, sample_rate);
|
||||
for (size_t i = 0; i < buffer_samples; i++)
|
||||
buffer_tx[i].left = buffer_tx[i].right = tone_gen.process_beep();
|
||||
|
||||
// Try to adjust DMA transfer count to align with full sine waves for a better tone
|
||||
float samples_per_sine_wave = float(sample_rate) / freq;
|
||||
uint32_t sine_waves_per_buffer = buffer_samples / samples_per_sine_wave;
|
||||
size_t sample_count = (sine_waves_per_buffer == 0) ? buffer_samples : sine_waves_per_buffer * samples_per_sine_wave + 0.5;
|
||||
|
||||
// Use single larger transfer buffer with sample count determined above
|
||||
lli_tx_loop[0].lli = lli_pointer(&lli_tx_loop[0]);
|
||||
lli_tx_loop[0].control = control_tx(sample_count * sizeof(sample_t));
|
||||
|
||||
// Convert duration ms to number of buffers to send before stopping
|
||||
// NB: beep_duration_ms==0 means beep continuously until stopped
|
||||
uint32_t beep_interrupt_count = beep_duration_ms * sample_rate / (1000 * sample_count);
|
||||
if ((beep_duration_ms != 0) && (beep_interrupt_count == 0))
|
||||
beep_interrupt_count = 1;
|
||||
beep_duration_downcounter = beep_interrupt_count;
|
||||
}
|
||||
|
||||
void beep_stop() {
|
||||
// Clear audio DMA buffer
|
||||
memset(&buffer_tx, 0, buffer_bytes);
|
||||
|
||||
// Restore DMA linked list to use multiple smaller buffers
|
||||
lli_tx_loop[0].control = control_tx(transfer_bytes);
|
||||
if (!single_tx_buffer && (transfers_per_buffer > 1)) {
|
||||
lli_tx_loop[0].lli = lli_pointer(&lli_tx_loop[1]);
|
||||
}
|
||||
}
|
||||
|
||||
buffer_t tx_empty_buffer() {
|
||||
const auto next_lli = tx_next_lli;
|
||||
if (next_lli) {
|
||||
|
||||
@@ -43,11 +43,12 @@ using buffer_t = buffer_t<sample_t>;
|
||||
|
||||
namespace dma {
|
||||
|
||||
void init();
|
||||
void configure();
|
||||
void enable();
|
||||
void init_audio_in();
|
||||
void init_audio_out();
|
||||
void disable();
|
||||
void shrink_tx_buffer(bool shrink);
|
||||
void beep_start(uint32_t freq, uint32_t sample_rate, uint32_t beep_duration_ms);
|
||||
void beep_stop();
|
||||
|
||||
audio::buffer_t tx_empty_buffer();
|
||||
audio::buffer_t rx_empty_buffer();
|
||||
|
||||
@@ -27,14 +27,10 @@
|
||||
#include "portapack_dma.hpp"
|
||||
|
||||
#include "gpdma.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
static void init() {
|
||||
audio::dma::init();
|
||||
audio::dma::configure();
|
||||
audio::dma::enable();
|
||||
|
||||
// Audio DMA initialization was moved to baseband proc's that actually use DMA audio, to save memory.
|
||||
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
|
||||
}
|
||||
|
||||
@@ -70,6 +66,8 @@ void __late_init(void) {
|
||||
void _default_exit(void) {
|
||||
// TODO: Is this complete?
|
||||
|
||||
audio::dma::disable();
|
||||
|
||||
nvicDisableVector(DMA_IRQn);
|
||||
|
||||
chSysDisable();
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "proc_afskrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
void AFSKRxProcessor::execute(const buffer_c8_t& buffer) {
|
||||
@@ -181,6 +183,8 @@ void AFSKRxProcessor::configure(const AFSKRxConfigureMessage& message) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<AFSKRxProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "proc_am_audio.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
@@ -112,6 +113,8 @@ void NarrowbandAMAudio::capture_config(const CaptureConfigMessage& message) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<NarrowbandAMAudio>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user