Compare commits

..

35 Commits

Author SHA1 Message Date
Mark Thompson d5c8525afc Declare all folder names in a single source file (#2039) 2024-03-25 08:44:49 +01:00
ImDroided d9bbd1b9ff SSTV Freqs (#2038)
* Added and fixed freqs

Figured I would clean these up and add a bit.

* Create SSTV.TXT

SSTV Frequencies for SSTV app
2024-03-25 01:21:05 -05:00
gullradriel b47cee435e fix typo (#2037) 2024-03-24 18:50:42 -05:00
gullradriel c078bac0e7 Looking glass beep (#2036)
* first draft of looking beep
* fixed beep squelch range in percent
* took out steps 
* gui adjustements
* uniformize calculation and beep squelch in db
* uniformisation, fix 24/48 error
2024-03-24 22:32:13 +01:00
gullradriel 6177b08632 fixing level beep, bad copy paste on special SPEC mode (#2035)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-03-24 18:47:15 +08:00
Mark Thompson 53a1bacd46 Revert "Add cmake dependency on baseband for external APPS" (#2034)
This reverts commit abb9b6c8ad.
2024-03-24 10:39:20 +08:00
ImDroided 4c012a9493 Added and fixed freqs (#2033)
Figured I would clean these up and add a bit.
2024-03-23 21:16:27 -05:00
Mark Thompson abb9b6c8ad Add make dependency on baseband for external APPS (#2031) 2024-03-23 21:34:15 +01:00
gullradriel 6e2507d609 fix gui glitch on beep squelch (#2030) 2024-03-23 21:01:48 +01:00
gullradriel 536981998b Level fix and beep, RSSI avg fix (#2027)
* fix average value being overflow'd
* fix audio and mod changes, preps for beep mode
* fixed beep_freq range, added a stop and set a variable sooner
* added support for audio beep messages
* better scaler for beep
* added bip squelch and saving of bip squelch and audio mode
* saving modulation, fixing audio
* added save and restore of bandwidth
* simpler ctcss clean on change mode
2024-03-23 19:27:05 +01:00
Mark Thompson 1a87f2d701 Revert accidental newbutton bg color change (#2029) 2024-03-23 13:26:46 -05:00
Mark Thompson 81e24d582d More debug Audio Beep Test tweaks (#2028) 2024-03-23 13:25:36 -05:00
Mark Thompson 28a5fc5915 Improved Audio Beep Test (#2026) 2024-03-23 08:21:35 +01:00
Mark Thompson ba4290cf0d Audio Beep Test (Debug) external app (#2023) 2024-03-22 11:10:16 +01:00
Mark Thompson 692644d214 Radiosonde QR code fixes (#2021)
* Increase size of small QR Code

* Fixed QR code lat/lon for iPhone

* Show current geopos location on map

* Deprecate large QR code
2024-03-21 10:56:06 -05:00
Mark Thompson 4aeaf94d2d Radiosonde beep tone tweaks (#2020)
* Tweak RSSI audio beep frequency range
* Clang & changed min freq
* Save beep/log/crc settings in .ini file
* Update copyright string
* Added generic audio_beep message
2024-03-21 10:14:54 +01:00
Totoo 8391ca8052 Fox hunt (#2015)
* foxhunt_rx
2024-03-19 21:33:53 +01:00
Mark Thompson 807c76346b Improved audio beep tone (#2014)
* Improved sine wave beep tone

* Prevent divide-by-zero
2024-03-19 15:30:05 -05:00
Mark Thompson 308573918c Added a beep duration parameter (#2013) 2024-03-19 18:23:06 +01:00
Mark Thompson 74442f197d Fixed Sonde RSSI "Beep" (#2012) 2024-03-19 10:53:55 +01:00
Mark Thompson b53c8e1f80 Stop audio DMA in baseband::shutdown() (#2011) 2024-03-19 05:58:23 +01:00
Mark Thompson c5f73cf8f4 Darken FileMan button colors for visibility (#2010) 2024-03-19 05:56:23 +01:00
Totoo c1bf2620c7 Bt namespam (#2009)
* Add random names
2024-03-18 21:45:45 +01:00
Mark Thompson 8383363e74 Preserve radio mode when updating iq_tx_phase_cal (#2008) 2024-03-18 21:45:02 +01:00
Alexandros Andreou 6e34343bde added all and all-safe attack types to blespam (#2003) 2024-03-18 09:13:10 +01:00
gullradriel 01e4ff65a2 fix butngrid/capture and fix contributor name (#1995)
* fixing contributor name

* used clear instead of swap

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2024-03-16 00:39:34 +08:00
Totoo 9e40e38f07 Record gps lat, lon, satinuse from recorder app if there was gps signal. (#1992)
* Record gps lat, lon, satinuse from recorder app if there was gps signal.

* Add _GEO to filename that has geotag
2024-03-15 21:56:20 +08:00
not tre mann 181624daf1 lambda the flash util menu add item code (#1993)
* lambda the flash util menu add code

* clean up
2024-03-14 22:56:55 -05:00
Mark Thompson f0614c882b File manager "Clean" button tweaks (#1990)
* File Manager "clean" button tweaks

* Add files via upload
2024-03-13 22:42:40 +01:00
not tre mann 14f42d1fe8 support "clean" files (#1983)
* init for clean files
* textual
* icons position
2024-03-13 18:06:14 +01:00
Mark Thompson 999f9e2ded Fix #1982 (enable DMA interrupt for all apps) (#1987) 2024-03-13 16:57:35 +01:00
Mark Thompson 61dc8a0225 Disabled unused baseband "no-op" process (#1986) 2024-03-13 16:08:52 +01:00
Mark Thompson 0b2d5f75cc Start audio DMA only in apps that use audio (#1982)
* Start audio DMA only in apps that use audio
* Rename main.cpp to main.cpp.unuse
* shrink_tx_buffer fix for transfers_per_buffer==1 scenario
2024-03-13 16:07:44 +01:00
Erwin Ried 3c489e1a81 Steamlining the warning (#1984)
I think it is enough to note nobody should pay for the download
2024-03-13 19:30:05 +08:00
gullradriel 910fd82051 freqman: limiting description size to 30, and minor fix (#1977)
* Limiting description size to 30 as it was documented before

* cosmetic adjustement

---------

Co-authored-by: GullCode <gullradriel@hotmail.com>
2024-03-11 16:40:47 +01:00
127 changed files with 1857 additions and 556 deletions
+2 -6
View File
@@ -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
+1
View File
@@ -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
+3 -2
View File
@@ -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;
-3
View File
@@ -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;
+2 -1
View File
@@ -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() {
+1 -1
View File
@@ -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);
}
+2 -1
View File
@@ -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");
}
}
+2 -1
View File
@@ -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) {
+1 -1
View File
@@ -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);
}
+1 -4
View File
@@ -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);
+6 -5
View File
@@ -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;
+1
View File
@@ -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;
+4 -3
View File
@@ -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;
+2 -1
View File
@@ -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};
+2 -1
View File
@@ -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");
}
}
+2 -1
View File
@@ -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) {
+1 -1
View File
@@ -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);
}
+3 -2
View File
@@ -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();
+2 -1
View File
@@ -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");
+2 -1
View File
@@ -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();
+2 -1
View File
@@ -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);
+3 -2
View File
@@ -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};
+33 -1
View File
@@ -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();
+14 -5
View File
@@ -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 -25
View File
@@ -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 */
+2 -1
View File
@@ -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);
+3 -5
View File
@@ -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{
+2 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
}
+2 -1
View File
@@ -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);
};
+88 -26
View File
@@ -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();
}
+40 -16
View File
@@ -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},
+3 -3
View File
@@ -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();
+5 -4
View File
@@ -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));
};
+4 -4
View File
@@ -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);
}
+1 -1
View File
@@ -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;
+5 -4
View File
@@ -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())
+1
View File
@@ -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;
+8 -7
View File
@@ -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>(); }},
});
+14 -48
View File
@@ -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();
}
}
}
+11 -7
View File
@@ -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 */
+1 -1
View File
@@ -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);
}
+2 -1
View File
@@ -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;
+19 -2
View File
@@ -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);
}
+5 -1
View File
@@ -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);
+38
View File
@@ -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__*/
+5 -4
View File
@@ -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;
+4 -4
View File
@@ -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__*/
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+82
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
};
}
+107
View File
@@ -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
+118
View File
@@ -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
View File
@@ -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
View File
@@ -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
};
}
+9
View File
@@ -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());
+2
View File
@@ -155,6 +155,8 @@ struct path {
path& replace_extension(const path& replacement = path());
path& append_filename(const string_type& str);
private:
string_type _s;
};
+46
View File
@@ -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";
+50
View File
@@ -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__ */
+3 -3
View File
@@ -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;
}
}
+3 -1
View File
@@ -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};
+9 -1
View File
@@ -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++) {
+2
View File
@@ -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
+9 -1
View File
@@ -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++) {
+2
View File
@@ -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
-2
View File
@@ -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) {
+41
View File
@@ -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);
}
+4
View File
@@ -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__
+4 -2
View File
@@ -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) {
+3 -2
View File
@@ -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) {
+9 -33
View File
@@ -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());
}
}
}
+4 -3
View File
@@ -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;
+12 -1
View File
@@ -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()) {
+12
View File
@@ -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 */
+10 -7
View File
@@ -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");
+16 -11
View File
@@ -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
+70 -17
View File
@@ -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) {
+4 -3
View File
@@ -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();
+3 -5
View File
@@ -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();
+4
View File
@@ -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;
+3
View File
@@ -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