Compare commits

...

50 Commits

Author SHA1 Message Date
jLynx 8e90c65a62 v2.0.1 (#2090)
* Update version.txt

* Update past_version.txt
2024-04-07 16:24:01 +12:00
gullradriel bfe67a13b7 removed dead link (#2089)
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-04-07 00:59:40 +08:00
gullradriel f091db3270 Mic audio ak4951 fix + SSTV description (#2088)
* fixed too long description, thanks to ImDroided

* fixed ak4951 RxAudio not working on startup when enabled

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-04-05 16:29:18 -05:00
Mark Thompson a602abf1d8 Fix Cancel button in Settings->Autostart (#2087) 2024-04-05 16:04:39 +02:00
ImDroided 689224fd8d Added CubeSats to Presets 2024-04-05 08:52:14 +02:00
gullradriel 23cf1c2737 fix build warning in settings (#2081)
* fix warning ui::SetAutostartView::opts' should be initialized in the member initialization list

* clang

---------

Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-04-03 14:17:48 -05:00
Totoo c48cbb7e55 Autostart option (#2079)
* Autostart app on boot
* Settings page
* ext app support
2024-04-03 16:27:13 +02:00
Totoo 3665b3c607 Added sil value ASDB (#2078)
* Added sil value. resolves #2005

* readibility
2024-04-03 15:57:39 +08:00
Totoo 804b7c87b7 allow to load more files but unordered (#2077) 2024-04-02 20:19:28 +02:00
Totoo a30875b582 Fileman memory opt (#2076)
* paginator
* fixes
* string instead of path, so saves ram.
* fix FileLoadView
* pagination list number
* pagination "size" test
* added detection of special folder name to display page numbers instead of (0 or a number of byte)
* order of things: only one directory '..', only on first page
Co-authored-by: gullradriel <gullradriel@no-mail.com>
2024-04-02 17:02:02 +02:00
Mark Thompson 56303eb385 Use freq from AppSettings unless passed from another app (#2073) 2024-03-30 12:57:43 -05:00
Totoo d29172f41e Add GPS icon, fix overwrite bug (#2072) 2024-03-30 16:48:45 +01:00
sommermorgentraum 6d9d5ad1af fix read file (#2070) 2024-03-30 13:03:27 +01:00
gullradriel 2acefee4d2 adjust GUI for more preset name room (#2068) 2024-03-30 10:34:37 +01:00
Mark Thompson cb9fee8f08 Modify warning text (#2066) 2024-03-29 18:50:28 -05:00
Mark Thompson b473930f94 Beep-on-packet support in ADSB RX app (#2065) 2024-03-29 16:34:18 -05:00
Mark Thompson 6e5eadd25c Beep-on-packet support in AIS app (#2064)
* Beep-on-packet support in AIS app
2024-03-29 22:08:32 +01:00
Dumpster-Diver 76017c91a9 Create RADIOS_FM_FR_PARIS.TXT with FM Radios in Paris (#2049)
* Create RADIOS_FM_FR.TXT with FM Radios in Paris

This new file will add FM frequencies for France. As a start it covers Paris FM radio frequencies. It is based on https://www.csa.fr/maradiofm/liste_ville?recherche=3&commune=Paris

* Update and rename RADIOS_FM_FR.TXT to RADIOS_FM_FR_PARIS.TXT

Reduce frequency description length to fit under the 30 chars limit
2024-03-29 15:52:00 -05:00
Mark Thompson f1ebb1a6e3 Beep-on-packet support in Weather app (#2063) 2024-03-29 21:29:50 +01:00
Mark Thompson 746bf1c15f Beep-on-packet support in TPMS app (#2062)
* Beep-on-packet support in TPMS app
* audio::output::stop when exiting
2024-03-29 21:28:27 +01:00
Mark Thompson 0db65ccb6b Beep-on-packet support in POCSAG app (#2061) 2024-03-29 21:27:53 +01:00
Mark Thompson fe2fbb847f Beep-on-packets option in Settings and updated ERT & Sonde apps (#2058)
* Beep-on-packets option in Settings
* Add beep to ERT app
2024-03-29 19:26:45 +01:00
Mark Thompson ba36680a7b FileMan support for folder Cut & Paste and Timestamp Rename (#2055) 2024-03-29 10:44:37 +01:00
set sail into the sea of life 90ff1e62ce move the no ext app tile to forward (#2047)
* strong notice of missing ext app

* textual

* move the notice tile to front and remove the painter string

* remove notice icon

* format

* for back button

* remove <>
2024-03-28 21:05:09 -05:00
Mark Thompson 2e1e02baac Removed special font characters (#2056) 2024-03-28 21:04:19 -05:00
Totoo 2dca408e45 Wardrive Map app (#2050)
* WardriveMap app

* better txt

* Count only displayed markers
2024-03-28 19:25:23 -05:00
Daniel fa56cdd015 Adding Australian UHF channels and Brisbane Airband to FREQMAN, adding Australian UHF to Looking Glass (#2048) 2024-03-27 19:57:40 -05:00
Mark Thompson 264fa9a28e Continued folder names consolidation & GPS default folder (#2042)
* Continued folder names consolidation

* Clang
2024-03-26 11:32:24 -05:00
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
141 changed files with 2622 additions and 614 deletions
+1 -1
View File
@@ -1 +1 @@
v1.9.1
v2.0.0
+1 -1
View File
@@ -1 +1 @@
v2.0.0
v2.0.1
-2
View File
@@ -30,8 +30,6 @@ This repository expands upon the previous work by many people and aims to consta
:heavy_check_mark: Our friends at OpenSourceSDRLab give away five units every three months in our discord (check the badge on top) of their [PortaPack H2](https://www.aliexpress.com/item/4000247041639.html?gatewayAdapt=4itemAdapt), you can support them too by ordering.
:heavy_check_mark: Another popular option is the clone of the [PortaPack H1](https://s.click.aliexpress.com/e/_Dkbqs2X).
:warning: Be cautious , *ask* the seller about compatibility with the latest releases. Look out for the description of the item, if they provide the firmware files for an older version or they have custom setup instructions, this means it might be **NOT compatible**, for example:
![image](https://user-images.githubusercontent.com/1091420/214579017-9ad970b9-0917-48f6-a550-588226d3f89b.png)
+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
+5 -8
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;
@@ -174,16 +175,12 @@ void copy_to_radio_model(const AppSettings& settings) {
// Specifically 'modulation' which requires a running baseband.
if (flags_enabled(settings.mode, Mode::TX)) {
if (!flags_enabled(settings.options, Options::UseGlobalTargetFrequency))
persistent_memory::set_target_frequency(settings.tx_frequency);
persistent_memory::set_target_frequency(settings.tx_frequency);
transmitter_model.configure_from_app_settings(settings);
}
if (flags_enabled(settings.mode, Mode::RX)) {
if (!flags_enabled(settings.options, Options::UseGlobalTargetFrequency))
persistent_memory::set_target_frequency(settings.rx_frequency);
persistent_memory::set_target_frequency(settings.rx_frequency);
receiver_model.configure_from_app_settings(settings);
receiver_model.set_configuration_without_update(
static_cast<ReceiverModel::Mode>(settings.modulation),
-6
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;
@@ -121,9 +118,6 @@ enum class Mode : uint8_t {
enum class Options {
None = 0x0000,
/* Don't use target frequency from app settings. */
UseGlobalTargetFrequency = 0x0001,
};
/* NB: See RX/TX model headers for default values. */
+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);
}
+16 -1
View File
@@ -20,9 +20,11 @@
*/
#include "ais_app.hpp"
#include "audio.hpp"
#include "string_format.hpp"
#include "database.hpp"
#include "file_path.hpp"
#include "baseband_api.hpp"
@@ -31,6 +33,8 @@ using namespace portapack;
#include <algorithm>
namespace pmem = portapack::persistent_memory;
namespace ais {
namespace format {
@@ -189,6 +193,10 @@ void AISLogger::on_packet(const ais::Packet& packet) {
}
log_file.write_entry(packet.received_at(), entry);
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
void AISRecentEntry::update(const ais::Packet& packet) {
@@ -376,6 +384,7 @@ AISAppView::AISAppView(NavigationView& nav)
&field_lna,
&field_vga,
&rssi,
&field_volume,
&channel,
&recent_entries_view,
&recent_entry_detail_view,
@@ -399,11 +408,17 @@ 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");
}
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
}
AISAppView::~AISAppView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
+3
View File
@@ -209,6 +209,9 @@ class AISAppView : public View {
{21 * 8, 0, 6 * 8, 4},
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
@@ -208,7 +208,9 @@ AnalogAudioView::AnalogAudioView(
NavigationView& nav,
ReceiverModel::settings_t override)
: AnalogAudioView(nav) {
// Settings to override when launched from another app (versus from AppSettings .ini file)
// TODO: Which other settings make sense to override?
field_frequency.set_value(override.frequency_app_override);
on_frequency_step_changed(override.frequency_step);
options_modulation.set_by_value(toUType(override.mode));
}
@@ -174,7 +174,6 @@ class AnalogAudioView : public View {
app_settings::SettingsManager settings_{
"rx_audio",
app_settings::Mode::RX,
app_settings::Options::UseGlobalTargetFrequency,
{
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
}};
+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);
}
+2 -5
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);
@@ -768,7 +765,7 @@ void BLERxView::on_filter_change(std::string value) {
}
void BLERxView::on_file_changed(const std::filesystem::path& new_file_path) {
file_path = fs::path(u"/") + new_file_path;
file_path = new_file_path;
found_count = 0;
total_count = 0;
searchList.clear();
+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;
+2 -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;
@@ -448,7 +449,7 @@ BLETxView::BLETxView(
}
void BLETxView::on_file_changed(const fs::path& new_file_path) {
file_path = fs::path(u"/") + new_file_path;
file_path = new_file_path;
num_packets = 0;
{ // Get the size of the data file.
+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;
@@ -104,6 +104,14 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
};
}
CaptureAppView::CaptureAppView(
NavigationView& nav,
ReceiverModel::settings_t override)
: CaptureAppView(nav) {
// Settings to override when launched from another app (versus from AppSettings .ini file)
field_frequency.set_value(override.frequency_app_override);
}
CaptureAppView::~CaptureAppView() {
receiver_model.disable();
baseband::shutdown();
+4 -3
View File
@@ -31,12 +31,14 @@
#include "ui_spectrum.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "file_path.hpp"
namespace ui {
class CaptureAppView : public View {
public:
CaptureAppView(NavigationView& nav);
CaptureAppView(NavigationView& nav, ReceiverModel::settings_t override);
~CaptureAppView();
void focus() override;
@@ -51,8 +53,7 @@ class CaptureAppView : public View {
NavigationView& nav_;
RxRadioState radio_state_{ReceiverModel::Mode::Capture};
app_settings::SettingsManager settings_{
"rx_capture", app_settings::Mode::RX,
app_settings::Options::UseGlobalTargetFrequency};
"rx_capture", app_settings::Mode::RX};
Labels labels{
{{0 * 8, 1 * 16}, "Rate:", Color::light_grey()},
@@ -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};
+16 -2
View File
@@ -23,7 +23,7 @@
#include "ert_app.hpp"
#include "baseband_api.hpp"
#include "audio.hpp"
#include "portapack.hpp"
using namespace portapack;
@@ -31,6 +31,9 @@ using namespace portapack;
#include "crc.hpp"
#include "string_format.hpp"
#include "file_path.hpp"
namespace pmem = portapack::persistent_memory;
namespace ert {
@@ -120,6 +123,7 @@ ERTAppView::ERTAppView(NavigationView& nav)
&field_lna,
&field_vga,
&rssi,
&field_volume,
&recent_entries_view,
});
@@ -129,11 +133,17 @@ 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");
}
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
}
ERTAppView::~ERTAppView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
@@ -157,6 +167,10 @@ void ERTAppView::on_packet(const ert::Packet& packet) {
entry.update(packet);
recent_entries_view.set_dirty();
}
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
void ERTAppView::on_show_list() {
+3
View File
@@ -165,6 +165,9 @@ class ERTAppView : public View {
{21 * 8, 0, 6 * 8, 4},
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
MessageHandlerRegistration message_handler_packet{
Message::ID::ERTPacket,
[this](Message* const p) {
+10 -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) {
@@ -135,6 +136,10 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
};
refresh_ui();
if (pmem::beep_on_packets())
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
receiver_model.enable();
baseband::set_pocsag();
@@ -303,6 +308,10 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
// Set status icon color to indicate state machine state.
image_status.set_foreground(get_status_color(pocsag_state));
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
void POCSAGAppView::on_stats(const POCSAGStatsMessage* stats) {
+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);
}
+1 -1
View File
@@ -44,7 +44,7 @@ void ReplayAppView::set_ready() {
}
void ReplayAppView::on_file_changed(const fs::path& new_file_path) {
file_path = fs::path(u"/") + new_file_path;
file_path = new_file_path;
File::Size file_size{};
{ // Get the size of the data file.
+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();
+16 -2
View File
@@ -23,13 +23,16 @@
#include "tpms_app.hpp"
#include "baseband_api.hpp"
#include "audio.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "string_format.hpp"
#include "utility.hpp"
#include "file_path.hpp"
namespace pmem = portapack::persistent_memory;
namespace tpms {
@@ -146,6 +149,7 @@ TPMSAppView::TPMSAppView(NavigationView&) {
baseband::run_image(portapack::spi_flash::image_tag_tpms);
add_children({&rssi,
&field_volume,
&channel,
&options_band,
&options_pressure,
@@ -176,11 +180,17 @@ TPMSAppView::TPMSAppView(NavigationView&) {
logger = std::make_unique<TPMSLogger>();
if (logger) {
logger->append(LOG_ROOT_DIR "/TPMS.TXT");
logger->append(logs_dir / u"TPMS.TXT");
}
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
}
TPMSAppView::~TPMSAppView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
@@ -213,6 +223,10 @@ void TPMSAppView::on_packet(const tpms::Packet& packet) {
entry.update(reading);
recent_entries_view.set_dirty();
}
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
void TPMSAppView::on_show_list() {
+3
View File
@@ -143,6 +143,9 @@ class TPMSAppView : public View {
{21 * 8, 0, 6 * 8, 4},
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
+24 -6
View File
@@ -31,9 +31,13 @@
#include "portapack_persistent_memory.hpp"
#include "rtc_time.hpp"
#include "string_format.hpp"
#include "file_path.hpp"
#include "audio.hpp"
using namespace portapack;
namespace pmem = portapack::persistent_memory;
namespace ui {
static std::string get_map_tag(const AircraftRecentEntry& entry) {
@@ -103,7 +107,8 @@ void ADSBLogger::log(const ADSBLogEntry& log_entry) {
log_line += " Type:" + to_string_dec_uint(log_entry.vel_type) +
" Hdg:" + to_string_dec_uint(log_entry.vel.heading) +
" Spd: " + to_string_dec_int(log_entry.vel.speed);
if (log_entry.sil != 0)
log_line += " Sil:" + to_string_dec_uint(log_entry.sil);
log_file.write_entry(log_line);
}
@@ -350,11 +355,12 @@ void ADSBRxDetailsView::refresh_ui() {
text_callsign.set(entry_.callsign);
text_infos.set(entry_.info_string);
std::string str_sil = (entry_.sil > 0) ? " Sil:" + to_string_dec_uint(entry_.sil) : "";
if (entry_.velo.heading < 360 && entry_.velo.speed >= 0)
text_info2.set("Hdg:" + to_string_dec_uint(entry_.velo.heading) +
" Spd:" + to_string_dec_int(entry_.velo.speed));
" Spd:" + to_string_dec_int(entry_.velo.speed) + str_sil);
else
text_info2.set("");
text_info2.set(str_sil);
text_frame_pos_even.set(to_string_hex_array(entry_.frame_pos_even.get_raw_data(), 14));
text_frame_pos_odd.set(to_string_hex_array(entry_.frame_pos_odd.get_raw_data(), 14));
@@ -372,7 +378,8 @@ ADSBRxView::ADSBRxView(NavigationView& nav) {
&rssi,
&recent_entries_view,
&status_frame,
&status_good_frame});
&status_good_frame,
&field_volume});
recent_entries_view.set_parent_rect({0, 16, 240, 272});
recent_entries_view.on_select = [this, &nav](const AircraftRecentEntry& entry) {
@@ -390,14 +397,20 @@ 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();
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
}
ADSBRxView::~ADSBRxView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
@@ -460,7 +473,6 @@ void ADSBRxView::on_frame(const ADSBFrameMessage* message) {
"Alt:" + to_string_dec_int(entry.pos.altitude) +
" Lat:" + to_string_decimal(entry.pos.latitude, 2) +
" Lon:" + to_string_decimal(entry.pos.longitude, 2);
entry.set_info_string(std::move(str_info));
}
@@ -468,10 +480,16 @@ void ADSBRxView::on_frame(const ADSBFrameMessage* message) {
entry.set_frame_velo(frame);
log_entry.vel = entry.velo;
log_entry.vel_type = msg_sub;
} else if (msg_type == AIRBORNE_OP_STATUS) { // for ver 1
entry.sil = frame.get_sil_value();
}
}
logger->log(log_entry);
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
void ADSBRxView::on_tick_second() {
+11 -3
View File
@@ -54,6 +54,8 @@ namespace ui {
#define AIRBORNE_POS_GPS_L 20 // airborne position (lowest type id)
#define AIRBORNE_POS_GPS_H 22 // airborne position (highest type id)
#define AIRBORNE_OP_STATUS 31 // Aircraft operation status
#define RESERVED_L 23 // reserved for other uses
#define RESERVED_H 31 // reserved for other uses
@@ -102,6 +104,8 @@ struct AircraftRecentEntry {
std::string callsign{};
std::string info_string{};
uint8_t sil{0}; // Surveillance integrity level
AircraftRecentEntry(const uint32_t ICAO_address)
: ICAO_address{ICAO_address} {
this->icao_str = to_string_hex(ICAO_address, 6);
@@ -173,6 +177,7 @@ struct ADSBLogEntry {
adsb_pos pos{};
adsb_vel vel{};
uint8_t vel_type{};
uint8_t sil{};
};
// TODO: Make logging optional.
@@ -421,19 +426,22 @@ class ADSBRxView : public View {
{18 * 8, 0 * 16}};
RSSI rssi{
{20 * 8, 4, 10 * 7, 8},
{20 * 8, 4, 7 * 8, 8},
};
ActivityDot status_frame{
{screen_width - 3, 5, 2, 2},
{27 * 8 + 2, 5, 2, 2},
Color::white(),
};
ActivityDot status_good_frame{
{screen_width - 3, 9, 2, 2},
{27 * 8 + 2, 9, 2, 2},
Color::green(),
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
MessageHandlerRegistration message_handler_frame{
Message::ID::ADSBFrame,
[this](Message* const p) {
+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};
+205 -48
View File
@@ -62,7 +62,7 @@ std::string truncate(const fs::path& path, size_t max_length) {
}
// Inserts the entry into the entry list sorted directories first then by file name.
void insert_sorted(std::vector<fileman_entry>& entries, fileman_entry&& entry) {
void insert_sorted(std::list<fileman_entry>& entries, fileman_entry&& entry) {
auto it = std::lower_bound(
std::begin(entries), std::end(entries), entry,
[](const fileman_entry& lhs, const fileman_entry& rhs) {
@@ -146,9 +146,82 @@ namespace ui {
/* FileManBaseView ***********************************************************/
void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
void FileManBaseView::load_directory_contents_unordered(const fs::path& dir_path, size_t file_cnt) {
current_path = dir_path;
entry_list.clear();
menu_view.clear();
auto filtering = !extension_filter.empty();
bool cxx_file = path_iequal(cxx_ext, extension_filter);
text_current.set(dir_path.empty() ? "(sd root)" : truncate(dir_path, 24));
nb_pages = 1 + (file_cnt / items_per_page);
size_t start = pagination * items_per_page;
size_t stop = start + items_per_page;
if (file_cnt < stop) stop = file_cnt;
if (start > file_cnt) start = 0; // shouldn't hapen but check against it won't hurt
size_t curr = 0;
for (const auto& entry : fs::directory_iterator(dir_path, u"*")) {
if (entry_list.size() >= items_per_page) {
break;
}
// Hide files starting with '.' (hidden / tmp).
if (!show_hidden_files && is_hidden_file(entry.path()))
continue;
if (fs::is_regular_file(entry.status())) {
if (!filtering || path_iequal(entry.path().extension(), extension_filter) || (cxx_file && is_cxx_capture_file(entry.path()))) {
curr++;
if (curr >= start) insert_sorted(entry_list, {entry.path().string(), (uint32_t)entry.size(), false});
}
} else if (fs::is_directory(entry.status())) {
curr++;
if (curr >= start) insert_sorted(entry_list, {entry.path().string(), 0, true});
}
}
// Add "parent" directory if not at the root.
if (!dir_path.empty() && pagination == 0)
entry_list.insert(entry_list.begin(), {parent_dir_path.string(), 0, true});
// add next page
if (file_cnt > start + items_per_page) {
entry_list.push_back({str_next, (uint32_t)pagination + 1, true});
}
// add prev page
if (pagination > 0) {
entry_list.insert(entry_list.begin(), {str_back, (uint32_t)pagination - 1, true});
}
}
int FileManBaseView::file_count_filtered(const fs::path& directory) {
int count{0};
auto filtering = !extension_filter.empty();
bool cxx_file = path_iequal(cxx_ext, extension_filter);
for (auto& entry : std::filesystem::directory_iterator(directory, (const TCHAR*)u"*")) {
if (fs::is_regular_file(entry.status())) {
if (!filtering || path_iequal(entry.path().extension(), extension_filter) || (cxx_file && is_cxx_capture_file(entry.path())))
++count;
} else
++count;
}
return count;
}
void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
size_t file_cnt = file_count_filtered(dir_path);
if (file_cnt >= max_items_loaded) {
load_directory_contents_unordered(dir_path, file_cnt);
return;
}
current_path = dir_path;
entry_list.clear();
menu_view.clear();
auto filtering = !extension_filter.empty();
bool cxx_file = path_iequal(cxx_ext, extension_filter);
@@ -161,19 +234,41 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
if (fs::is_regular_file(entry.status())) {
if (!filtering || path_iequal(entry.path().extension(), extension_filter) || (cxx_file && is_cxx_capture_file(entry.path())))
insert_sorted(entry_list, {entry.path(), (uint32_t)entry.size(), false});
insert_sorted(entry_list, {entry.path().string(), (uint32_t)entry.size(), false});
} else if (fs::is_directory(entry.status())) {
insert_sorted(entry_list, {entry.path(), 0, true});
insert_sorted(entry_list, {entry.path().string(), 0, true});
}
}
// paginating
auto list_size = entry_list.size();
nb_pages = 1 + (list_size / items_per_page);
size_t start = pagination * items_per_page;
size_t stop = start + items_per_page;
if (list_size > start) {
if (list_size < stop)
stop = list_size;
entry_list.erase(std::next(entry_list.begin(), stop), entry_list.end());
entry_list.erase(entry_list.begin(), std::next(entry_list.begin(), start));
}
// Add "parent" directory if not at the root.
if (!dir_path.empty())
entry_list.insert(entry_list.begin(), {parent_dir_path, 0, true});
if (!dir_path.empty() && pagination == 0)
entry_list.insert(entry_list.begin(), {parent_dir_path.string(), 0, true});
// add next page
if (list_size > start + items_per_page) {
entry_list.push_back({str_next, (uint32_t)pagination + 1, true});
}
// add prev page
if (pagination > 0) {
entry_list.insert(entry_list.begin(), {str_back, (uint32_t)pagination - 1, true});
}
}
fs::path FileManBaseView::get_selected_full_path() const {
if (get_selected_entry().path == parent_dir_path)
if (get_selected_entry().path == parent_dir_path.string())
return current_path.parent_path();
return current_path / get_selected_entry().path;
@@ -181,7 +276,10 @@ fs::path FileManBaseView::get_selected_full_path() const {
const fileman_entry& FileManBaseView::get_selected_entry() const {
// TODO: return reference to an "empty" entry on OOB?
return entry_list[menu_view.highlighted_index()];
auto it = entry_list.begin();
if (menu_view.highlighted_index() >= 1) std::advance(it, menu_view.highlighted_index());
return *it;
// return entry_list[menu_view.highlighted_index()];
}
FileManBaseView::FileManBaseView(
@@ -230,7 +328,7 @@ void FileManBaseView::push_dir(const fs::path& path) {
current_path /= path;
saved_index_stack.push_back(menu_view.highlighted_index());
menu_view.set_highlighted(0);
reload_current();
reload_current(true);
}
}
@@ -239,29 +337,56 @@ void FileManBaseView::pop_dir() {
return;
current_path = current_path.parent_path();
reload_current();
reload_current(true);
menu_view.set_highlighted(saved_index_stack.back());
saved_index_stack.pop_back();
}
std::string get_extension(std::string t) {
const auto index = t.find_last_of(u'.');
if (index == t.npos) {
return {};
} else {
return t.substr(index);
}
}
std::string get_stem(std::string t) {
const auto index = t.find_last_of(u'.');
if (index == t.npos) {
return t;
} else {
return t.substr(0, index);
}
}
std::string get_filename(std::string _s) {
const auto index = _s.find_last_of("/");
if (index == _s.npos) {
return _s;
} else {
return _s.substr(index + 1);
}
}
void FileManBaseView::refresh_list() {
if (on_refresh_widgets)
on_refresh_widgets(false);
auto prev_highlight = menu_view.highlighted_index();
prev_highlight = menu_view.highlighted_index();
menu_view.clear();
for (const auto& entry : entry_list) {
auto entry_name = truncate(entry.path, 20);
auto entry_name = std::string{entry.path.length() <= 20 ? entry.path : entry.path.substr(0, 20)};
if (entry.is_directory) {
auto size_str =
(entry.path == parent_dir_path)
? ""
: to_string_dec_uint(file_count(current_path / entry.path));
std::string size_str{};
if (entry.path == str_next || entry.path == str_back) {
size_str = to_string_dec_uint(1 + entry.size) + "/" + to_string_dec_uint(nb_pages); // show computed number of pages
} else {
size_str = (entry.path == parent_dir_path.string()) ? "" : to_string_dec_uint(file_count(current_path / entry.path));
}
menu_view.add_item(
{entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
ui::Color::yellow(),
&bitmap_icon_dir,
[this](KeyEvent key) {
@@ -270,11 +395,11 @@ void FileManBaseView::refresh_list() {
}});
} else {
const auto& assoc = get_assoc(entry.path.extension());
const auto& assoc = get_assoc(get_extension(entry.path));
auto size_str = to_string_file_size(entry.size);
menu_view.add_item(
{entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
assoc.color,
assoc.icon,
[this](KeyEvent key) {
@@ -282,16 +407,13 @@ void FileManBaseView::refresh_list() {
on_select_entry(key);
}});
}
// HACK: Should page menu items instead of limiting the number.
if (menu_view.item_count() >= max_items_shown)
break;
}
menu_view.set_highlighted(prev_highlight);
}
void FileManBaseView::reload_current() {
void FileManBaseView::reload_current(bool reset_pagination) {
if (reset_pagination) pagination = 0;
load_directory_contents(current_path);
refresh_list();
}
@@ -327,6 +449,21 @@ FileLoadView::FileLoadView(
on_select_entry = [this](KeyEvent) {
if (get_selected_entry().is_directory) {
if (get_selected_entry().path == str_full) {
return;
}
if (get_selected_entry().path == str_back) {
pagination--;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
if (get_selected_entry().path == str_next) {
pagination++;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
push_dir(get_selected_entry().path);
} else {
if (on_changed)
@@ -416,10 +553,10 @@ void FileManagerView::on_rename(std::string_view hint) {
auto& entry = get_selected_entry();
// Append the hint to the filename stem as a rename suggestion.
name_buffer = entry.path.stem().string();
name_buffer = get_stem(entry.path);
if (!hint.empty())
name_buffer += "_" + std::string{hint};
name_buffer += entry.path.extension().string();
name_buffer += get_extension(entry.path);
// Set the rename cursor to before the extension to make renaming simpler.
uint32_t cursor_pos = (uint32_t)name_buffer.length();
@@ -440,11 +577,11 @@ void FileManagerView::on_rename(std::string_view hint) {
auto new_name = renamed_path.replace_extension(partner.extension());
rename_file(partner, current_path / new_name);
}
reload_current();
reload_current(false);
});
if (!has_partner)
reload_current();
reload_current(false);
});
}
@@ -454,7 +591,7 @@ void FileManagerView::on_delete() {
return;
}
auto name = get_selected_entry().path.filename().string();
auto name = get_filename(get_selected_entry().path);
nav_.push<ModalMessageView>(
"Delete", "Delete " + name + "\nAre you sure?", YESNO,
[this](bool choice) {
@@ -466,11 +603,11 @@ void FileManagerView::on_delete() {
[this](const fs::path& partner, bool should_delete) {
if (should_delete)
delete_file(partner);
reload_current();
reload_current(true);
});
if (!has_partner)
reload_current();
reload_current(true);
}
});
}
@@ -495,7 +632,7 @@ void FileManagerView::on_clean() {
std::filesystem::path current_full_path = path_name / file_name;
delete_file(current_full_path);
}
reload_current();
reload_current(true);
}
});
}
@@ -504,7 +641,7 @@ void FileManagerView::on_new_dir() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) {
make_new_directory(current_path / dir_name);
reload_current();
reload_current(true);
});
}
@@ -514,7 +651,10 @@ void FileManagerView::on_paste() {
fs::filesystem_error result;
if (clipboard_mode == ClipboardMode::Cut)
result = rename_file(clipboard_path, current_path / new_name);
if ((current_path / clipboard_path.filename()) == clipboard_path)
result = FR_OK; // Skip paste to avoid renaming if path is unchanged
else
result = rename_file(clipboard_path, current_path / new_name);
else if (clipboard_mode == ClipboardMode::Copy)
result = copy_file(clipboard_path, current_path / new_name);
@@ -525,14 +665,14 @@ void FileManagerView::on_paste() {
clipboard_path = fs::path{};
clipboard_mode = ClipboardMode::None;
menu_view.focus();
reload_current();
reload_current(true);
}
void FileManagerView::on_new_file() {
name_buffer = "";
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& file_name) {
make_new_file(current_path / file_name);
reload_current();
reload_current(true);
});
}
@@ -555,7 +695,7 @@ bool FileManagerView::handle_file_open() {
return true;
} else if (path_iequal(bmp_ext, ext)) {
nav_.push<SplashViewer>(path);
reload_current();
reload_current(false);
return true;
} else if (path_iequal(rem_ext, ext)) {
nav_.push<RemoteView>(path);
@@ -567,7 +707,7 @@ bool FileManagerView::handle_file_open() {
bool FileManagerView::selected_is_valid() const {
return !entry_list.empty() &&
get_selected_entry().path != parent_dir_path;
get_selected_entry().path != parent_dir_path.string();
}
FileManagerView::FileManagerView(
@@ -599,7 +739,7 @@ FileManagerView::FileManagerView(
});
menu_view.on_highlight = [this]() {
if (menu_view.highlighted_index() >= max_items_shown - 1) {
if (menu_view.highlighted_index() >= max_items_loaded - 1) { // todo check this if correct
text_date.set_style(&Styles::red);
text_date.set("Too many files!");
} else {
@@ -614,13 +754,30 @@ FileManagerView::FileManagerView(
refresh_list();
on_select_entry = [this](KeyEvent key) {
if (key == KeyEvent::Select && get_selected_entry().is_directory) {
push_dir(get_selected_entry().path);
} else if (key == KeyEvent::Select && handle_file_open()) {
return;
} else {
button_rename.focus();
if (key == KeyEvent::Select) {
if (get_selected_entry().is_directory) {
if (get_selected_entry().path == str_full) {
return;
}
if (get_selected_entry().path == str_back) {
pagination--;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
if (get_selected_entry().path == str_next) {
pagination++;
menu_view.set_highlighted(0);
reload_current(false);
return;
}
push_dir(get_selected_entry().path);
return;
} else if (handle_file_open()) {
return;
}
}
button_rename.focus();
};
button_rename.on_select = [this]() {
@@ -639,7 +796,7 @@ FileManagerView::FileManagerView(
};
button_cut.on_select = [this]() {
if (selected_is_valid() && !get_selected_entry().is_directory) {
if (selected_is_valid()) {
clipboard_path = get_selected_full_path();
clipboard_mode = ClipboardMode::Cut;
} else
@@ -658,7 +815,7 @@ FileManagerView::FileManagerView(
if (clipboard_mode != ClipboardMode::None)
on_paste();
else
nav_.display_modal("Paste", "Cut or copy a file first.");
nav_.display_modal("Paste", " Cut or copy a file,\n or cut a folder, first.");
};
button_new_dir.on_select = [this]() {
@@ -678,7 +835,7 @@ FileManagerView::FileManagerView(
};
button_rename_timestamp.on_select = [this]() {
if (selected_is_valid() && !get_selected_entry().is_directory) {
if (selected_is_valid()) {
on_rename(::truncate(to_string_timestamp(rtc_time::now()), 8));
} else
nav_.display_modal("Timestamp Rename", "Can't rename that.");
+15 -6
View File
@@ -31,7 +31,7 @@
namespace ui {
struct fileman_entry {
std::filesystem::path path{};
std::string path{};
uint32_t size{};
bool is_directory{};
};
@@ -61,8 +61,12 @@ class FileManBaseView : public View {
void push_dir(const std::filesystem::path& path);
protected:
static constexpr size_t max_filename_length = 64;
static constexpr size_t max_items_shown = 100;
uint32_t prev_highlight = 0;
uint8_t pagination = 0;
uint8_t nb_pages = 1;
static constexpr size_t max_filename_length = 20;
static constexpr size_t max_items_loaded = 75; // too memory hungry, so won't sort it
static constexpr size_t items_per_page = 20;
struct file_assoc_t {
std::filesystem::path extension;
@@ -85,10 +89,12 @@ class FileManBaseView : public View {
std::filesystem::path get_selected_full_path() const;
const fileman_entry& get_selected_entry() const;
int file_count_filtered(const std::filesystem::path& directory);
void pop_dir();
void refresh_list();
void reload_current();
void reload_current(bool reset_pagination = false);
void load_directory_contents(const std::filesystem::path& dir_path);
void load_directory_contents_unordered(const std::filesystem::path& dir_path, size_t file_cnt);
const file_assoc_t& get_assoc(const std::filesystem::path& ext) const;
NavigationView& nav_;
@@ -97,11 +103,14 @@ class FileManBaseView : public View {
std::function<void(KeyEvent)> on_select_entry{nullptr};
std::function<void(bool)> on_refresh_widgets{nullptr};
const std::string str_back{"<--"};
const std::string str_next{"-->"};
const std::string str_full{"Can't load more.."};
const std::filesystem::path parent_dir_path{u".."};
std::filesystem::path current_path{u""};
std::filesystem::path extension_filter{u""};
std::vector<fileman_entry> entry_list{};
std::list<fileman_entry> entry_list{};
std::vector<uint32_t> saved_index_stack{};
bool show_hidden_files{false};
@@ -280,7 +289,7 @@ class FileManagerView : public FileManBaseView {
{4 * 8, 29 * 8, 4 * 8, 32},
{},
&bitmap_icon_options_datetime,
Color::orange(),
Color::dark_blue(),
/*vcenter*/ true};
NewButton button_open_iq_trim{
@@ -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 std::filesystem::path firmware_path = u"/FIRMWARE";
// Firmware image validation
static const char* hackrf_magic = "HACKRFFW";
#define FIRMWARE_INFO_AREA_OFFSET 0x400
@@ -84,7 +83,7 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
ensure_directory(firmware_path);
ensure_directory(firmware_dir);
auto add_firmware_items = [&](
const std::filesystem::path& folder_path,
@@ -103,8 +102,8 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
}
};
add_firmware_items(firmware_path, u"*.bin", ui::Color::red());
add_firmware_items(firmware_path, u"*.tar", ui::Color::purple());
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());
}
@@ -116,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);
}
});
+1
View File
@@ -31,6 +31,7 @@
#include "ui_receiver.hpp"
#include "ui_styles.hpp"
#include "utility.hpp"
#include "file_path.hpp"
#include <memory>
+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-XXdb
button_beep_squelch.set_text("bip" + to_string_dec_int(beep_squelch, 3) + "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("bip 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 < -99)
new_beep_squelch = -99;
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;
@@ -164,9 +173,10 @@ class GlassView : public View {
Labels labels{
{{0, 0 * 16}, "MIN: MAX: LNA VGA ", Color::light_grey()},
{{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()},
{{0, 2 * 16}, "PRESET:", Color::light_grey()},
{{0, 2 * 16}, "P:", 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},
@@ -205,10 +215,14 @@ class GlassView : public View {
{28 * 8, 1 * 16}};
OptionsField range_presets{
{7 * 8, 2 * 16},
{2 * 8, 2 * 16},
20,
{}};
ButtonWithEncoder button_beep_squelch{
{240 - 8 * 8, 2 * 16 + 4, 8 * 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},
+20 -18
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();
@@ -523,21 +523,6 @@ MicTXView::MicTXView(
};
check_mic_to_HP.set_value(mic_to_HP_enabled);
check_rxactive.on_select = [this](Checkbox&, bool v) {
// vumeter.set_value(0); //Start with a clean vumeter
rx_enabled = v;
check_mic_to_HP.hidden(rx_enabled); // Toggle Hide / show "Hear Mic" checkbox depending if we activate or not the receiver. (if RX on => no visible "Mic Hear" option)
if ((rx_enabled) && (transmitting))
check_mic_to_HP.set_value(transmitting); // Once we activate the "Rx audio" in reception time we disable "Hear Mic", but we allow it again in TX time.
if (rx_enabled)
check_va.set_value(false); // Disallow voice activation during RX audio (for now) - Future TODO: Should allow VOX during RX audio
rxaudio(v); // Activate-Deactivate audio RX (receiver) accordingly
set_dirty(); // Refresh interface
};
check_rxactive.set_value(rx_enabled);
tx_button.on_select = [this](Button&) {
if (!transmitting) {
set_tx(true);
@@ -590,12 +575,28 @@ MicTXView::MicTXView(
// Trigger receiver to update modulation.
if (rx_enabled)
receiver_model.set_squelch_level(receiver_model.squelch_level());
check_rxactive.on_select = [this](Checkbox&, bool v) {
// vumeter.set_value(0); //Start with a clean vumeter
rx_enabled = v;
check_mic_to_HP.hidden(rx_enabled); // Toggle Hide / show "Hear Mic" checkbox depending if we activate or not the receiver. (if RX on => no visible "Mic Hear" option)
if ((rx_enabled) && (transmitting))
check_mic_to_HP.set_value(transmitting); // Once we activate the "Rx audio" in reception time we disable "Hear Mic", but we allow it again in TX time.
if (rx_enabled)
check_va.set_value(false); // Disallow voice activation during RX audio (for now) - Future TODO: Should allow VOX during RX audio
rxaudio(v); // Activate-Deactivate audio RX (receiver) accordingly
set_dirty(); // Refresh interface
};
check_rxactive.set_value(rx_enabled);
}
MicTXView::MicTXView(
NavigationView& nav,
ReceiverModel::settings_t override)
: MicTXView(nav) {
// Settings to override when launched from another app (versus from AppSettings .ini file)
// Try to use the modulation/bandwidth from RX settings.
// TODO: These concepts should be merged so there's only one.
switch (override.mode) {
@@ -616,6 +617,7 @@ MicTXView::MicTXView(
break;
}
field_frequency.set_value(override.frequency_app_override);
check_common_freq_tx_rx.set_value(true); // freq passed from other app is in tx_frequency, so set rx_frequency=tx_frequency
// TODO: bandwidth selection is tied too tightly to the UI
-1
View File
@@ -118,7 +118,6 @@ class MicTXView : public View {
app_settings::SettingsManager settings_{
"tx_mic",
app_settings::Mode::RX_TX,
app_settings::Options::UseGlobalTargetFrequency,
{
{"mic_mod_index"sv, &mic_mod_index},
{"rxbw_index"sv, &rxbw_index},
+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));
};
+7 -7
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);
@@ -508,7 +508,7 @@ ReconView::ReconView(NavigationView& nav)
auto settings = receiver_model.settings();
settings.frequency_step = step_mode.selected_index_value();
if (field_mode.selected_index_value() == SPEC_MODULATION)
nav_.replace<CaptureAppView>();
nav_.replace<CaptureAppView>(settings);
else
nav_.replace<AnalogAudioView>(settings);
};
@@ -539,7 +539,7 @@ ReconView::ReconView(NavigationView& nav)
}
}
// MicTX wants Modulation and Bandwidth overrides, but that's only stored on the RX model.
// MicTX wants Frequency, Modulation and Bandwidth overrides, but that's only stored on the RX model.
nav_.replace<MicTXView>(receiver_model.settings());
};
@@ -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);
}
@@ -1460,7 +1460,7 @@ void ReconView::handle_repeat_thread_done(const uint32_t return_code) {
stop_repeat(true);
} else if (return_code == ReplayThread::READ_ERROR) {
stop_repeat(false);
repeat_file_error(u"/" + repeat_rec_path + u"/" + repeat_rec_file, "Can't open file to send.");
repeat_file_error(rawfile, "Can't open file to send.");
}
}
+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;
@@ -58,7 +59,7 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir);
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find(freqman_dir.native()) == 0) {
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
_input_file = new_file_path.stem().string();
text_input_file.set(_input_file);
} else {
@@ -71,7 +72,7 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir);
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find(freqman_dir.native()) == 0) {
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
_output_file = new_file_path.stem().string();
button_choose_output_name.set_text(_output_file);
} else {
+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())
+3 -2
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;
@@ -337,7 +338,7 @@ ScannerView::ScannerView(
auto open_view = nav.push<FileLoadView>(".TXT");
open_view->push_dir(freqman_dir);
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
if (new_file_path.native().find(freqman_dir.native()) == 0) {
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
scan_pause();
frequency_file_load(new_file_path);
} else {
@@ -412,7 +413,7 @@ ScannerView::ScannerView(
button_mic_app.on_select = [this](Button&) {
if (scan_thread)
scan_thread->stop();
// MicTX wants Modulation and Bandwidth overrides, but that's only stored on the RX model.
// MicTX wants Frequency, Modulation and Bandwidth overrides, but that's only stored on the RX model.
nav_.replace<MicTXView>(receiver_model.settings());
};
+68 -7
View File
@@ -31,6 +31,7 @@
#include "ui_receiver.hpp"
#include "ui_touch_calibration.hpp"
#include "ui_text_editor.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "portapack_persistent_memory.hpp"
#include "lpc43xx_cpp.hpp"
@@ -41,6 +42,7 @@ using namespace lpc43xx;
using namespace portapack;
#include "file.hpp"
#include "file_path.hpp"
namespace fs = std::filesystem;
#include "string_format.hpp"
@@ -571,17 +573,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
@@ -634,13 +636,17 @@ void SetPersistentMemoryView::focus() {
SetAudioView::SetAudioView(NavigationView& nav) {
add_children({&labels,
&field_tone_mix,
&checkbox_beep_on_packets,
&button_save,
&button_cancel});
field_tone_mix.set_value(pmem::tone_mix());
checkbox_beep_on_packets.set_value(pmem::beep_on_packets());
button_save.on_select = [&nav, this](Button&) {
pmem::set_tone_mix(field_tone_mix.value());
pmem::set_beep_on_packets(checkbox_beep_on_packets.value());
audio::output::update_audio_mute();
nav.pop();
};
@@ -717,10 +723,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(),
@@ -834,6 +840,60 @@ void SetMenuColorView::focus() {
button_save.focus();
}
/* SetAutoStartView ************************************/
SetAutostartView::SetAutostartView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&options});
button_save.on_select = [&nav, this](Button&) {
autostart_app = "";
if (selected != 0) {
auto it = full_app_list.find(selected);
if (it != full_app_list.end())
autostart_app = it->second;
}
nav.pop();
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
// options
i = 0;
OptionsField::option_t o{"-none-", i};
opts.emplace_back(o);
for (auto& app : NavigationView::appList) {
if (app.id == nullptr) continue;
i++;
o = {app.displayName, i};
opts.emplace_back(o);
full_app_list.emplace(i, app.id);
if (autostart_app == app.id) selected = i;
}
ExternalItemsMenuLoader::load_all_external_items_callback([this](ui::AppInfoConsole& app) {
if (app.appCallName == nullptr) return;
i++;
OptionsField::option_t o = {app.appFriendlyName, i};
opts.emplace_back(o);
full_app_list.emplace(i, app.appCallName);
if (autostart_app == app.appCallName) selected = i;
});
options.set_options(opts);
options.on_change = [this](size_t, OptionsField::value_t v) {
selected = v;
};
options.set_selected_index(selected);
}
void SetAutostartView::focus() {
options.focus();
}
/* SettingsMenuView **************************************/
SettingsMenuView::SettingsMenuView(NavigationView& nav)
@@ -858,9 +918,10 @@ 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>(); }},
{"Autostart", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetAutostartView>(); }},
});
}
+46 -1
View File
@@ -506,8 +506,12 @@ class SetAudioView : public View {
Labels labels{
{{1 * 8, 1 * 16}, "Controls the volume of the", Color::light_grey()},
{{1 * 8, 2 * 16}, "tone when transmitting in", Color::light_grey()},
{{1 * 8, 3 * 16}, "Soundboard or Mic apps.", Color::light_grey()},
{{1 * 8, 3 * 16}, "Soundboard or Mic apps:", Color::light_grey()},
{{2 * 8, 5 * 16}, "Tone key mix: %", Color::light_grey()},
{{1 * 8, 8 * 16}, "Controls whether apps should", Color::light_grey()},
{{1 * 8, 9 * 16}, "beep on speaker & headphone", Color::light_grey()},
{{1 * 8, 10 * 16}, "when a packet is received", Color::light_grey()},
{{1 * 8, 11 * 16}, "(not all apps support this):", Color::light_grey()},
};
NumberField field_tone_mix{
@@ -517,6 +521,11 @@ class SetAudioView : public View {
1,
'0'};
Checkbox checkbox_beep_on_packets{
{3 * 8, 13 * 16},
16,
"Beep on RX packets"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -790,6 +799,42 @@ class SetMenuColorView : public View {
};
};
class SetAutostartView : public View {
public:
SetAutostartView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Autostart"; };
private:
int32_t i = 0;
std::string autostart_app{""};
OptionsField::options_t opts{};
std::map<int32_t, std::string> full_app_list{}; // looking table
int32_t selected = 0;
SettingsStore nav_setting{
"nav"sv,
{{"autostart_app"sv, &autostart_app}}};
Labels labels{
{{1 * 8, 1 * 16}, "Select app to start on boot", Color::light_grey()},
{{2 * 8, 2 * 16}, "(an SD Card is required)", Color::light_grey()}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
OptionsField options{
{8 * 8, 4 * 16},
30,
{}};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
};
class SettingsMenuView : public BtnGridView {
public:
SettingsMenuView(NavigationView& nav);
+17 -55
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,12 +25,14 @@
#include "baseband_api.hpp"
#include "audio.hpp"
#include "app_settings.hpp"
#include "file_path.hpp"
#include "portapack.hpp"
#include <cstring>
#include <stdio.h>
using namespace portapack;
namespace pmem = portapack::persistent_memory;
#include "string_format.hpp"
#include "complex.hpp"
@@ -52,7 +55,6 @@ SondeView::SondeView(NavigationView& nav)
&field_vga,
&rssi,
&field_volume,
&check_beep,
&check_log,
&check_crc,
&text_signature,
@@ -70,14 +72,12 @@ SondeView::SondeView(NavigationView& nav)
geopos.set_read_only(true);
check_beep.on_select = [this](Checkbox&, bool v) {
beep = v;
};
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 +86,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,9 +106,12 @@ 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();
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
// inject a PitchRSSIConfigureMessage in order to arm
// the pitch rssi events that will be used by the
@@ -142,51 +146,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
@@ -219,8 +181,8 @@ void SondeView::on_packet(const sonde::Packet& packet) {
logger->on_packet(packet);
}
if (beep) {
baseband::request_beep();
if (pmem::beep_on_packets()) {
baseband::request_rssi_beep();
}
}
}
+9 -12
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,17 @@ 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 logging{false};
bool use_crc{false};
bool beep{false};
app_settings::SettingsManager settings_{
"rx_sonde",
app_settings::Mode::RX,
{
{"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{};
@@ -119,11 +122,6 @@ class SondeView : public View {
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
Checkbox check_beep{
{22 * 8, 6 * 16},
3,
"Beep"};
Checkbox check_log{
{22 * 8, 8 * 16},
3,
@@ -201,7 +199,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);
}
@@ -30,6 +30,8 @@
using namespace portapack;
using namespace ui;
namespace pmem = portapack::persistent_memory;
namespace ui {
void WeatherRecentEntryDetailView::update_data() {
@@ -91,6 +93,7 @@ WeatherView::WeatherView(NavigationView& nav)
&field_rf_amp,
&field_lna,
&field_vga,
&field_volume,
&field_frequency,
&options_temperature,
&button_clear_list,
@@ -120,6 +123,11 @@ WeatherView::WeatherView(NavigationView& nav)
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
on_tick_second();
};
if (pmem::beep_on_packets()) {
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
}
void WeatherView::on_tick_second() {
@@ -142,10 +150,15 @@ void WeatherView::on_data(const WeatherDataMessage* data) {
truncate_entries(recent, 64);
}
recent_entries_view.set_dirty();
if (pmem::beep_on_packets()) {
baseband::request_audio_beep(1000, 24000, 60);
}
}
WeatherView::~WeatherView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
@@ -126,6 +126,10 @@ class WeatherView : public View {
{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_};
+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);
+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__*/
+16
View File
@@ -67,6 +67,19 @@ 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
#wardrivemap
external/wardrivemap/main.cpp
external/wardrivemap/ui_wardrivemap.cpp
)
set(EXTAPPLIST
@@ -86,4 +99,7 @@ set(EXTAPPLIST
keyfob
tetris
extsensors
foxhunt_rx
audio_test
wardrivemap
)
+19
View File
@@ -39,6 +39,9 @@ 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
ram_external_app_wardrivemap(rwx) : org = 0xADC20000, len = 32k
}
SECTIONS
@@ -139,6 +142,22 @@ 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
.external_app_wardrivemap : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_wardrivemap.application_information));
*(*ui*external_app*wardrivemap*);
} > ram_external_app_wardrivemap
}
+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
};
}
+106
View File
@@ -0,0 +1,106 @@
/*
* 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_hide_center_marker(true); //todo test if needed
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__*/
+4 -1
View File
@@ -28,6 +28,7 @@
#include "io_file.hpp"
#include "metadata_file.hpp"
#include "utility.hpp"
#include "file_path.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
@@ -43,7 +44,7 @@ void GpsSimAppView::set_ready() {
}
void GpsSimAppView::on_file_changed(const fs::path& new_file_path) {
file_path = fs::path(u"/") + new_file_path;
file_path = new_file_path;
File::Size file_size{};
{ // Get the size of the data file.
@@ -185,6 +186,8 @@ GpsSimAppView::GpsSimAppView(
button_open.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".C8");
ensure_directory(gps_dir);
open_view->push_dir(gps_dir);
open_view->on_changed = [this](std::filesystem::path new_file_path) {
on_file_changed(new_file_path);
};
@@ -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};
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* 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_wardrivemap.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::wardrivemap {
void initialize_app(ui::NavigationView& nav) {
nav.push<WardriveMapView>();
}
} // namespace ui::external_app::wardrivemap
extern "C" {
__attribute__((section(".external_app.app_wardrivemap.application_information"), used)) application_information_t _application_information_wardrivemap = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::wardrivemap::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "WardriveMap",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0x04,
0x20,
0x12,
0x48,
0x8A,
0x51,
0xCA,
0x53,
0xCA,
0x53,
0x8A,
0x51,
0x12,
0x48,
0x84,
0x21,
0xC0,
0x03,
0x40,
0x02,
0x60,
0x06,
0x20,
0x04,
0x30,
0x0C,
0xF0,
0x0F,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -0,0 +1,143 @@
/*
* 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_wardrivemap.hpp"
#include "rtc_time.hpp"
#include "string_format.hpp"
#include "file_path.hpp"
#include "metadata_file.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace ui;
namespace ui::external_app::wardrivemap {
void WardriveMapView::focus() {
geopos.focus();
}
// needs to load on every map change, because won't store or draw all while marker is not in the current view.
// todo optimize this somehow
bool WardriveMapView::load_markers() {
uint16_t cnt = 0;
uint16_t displayed_cnt = 0;
geomap.clear_markers();
// serach for files with geotag, and add it to geomap as marker with tag. limit to N bc of mem limit.
for (const auto& entry : std::filesystem::directory_iterator(captures_dir, u"*.txt")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (displayed_cnt > 30) break;
std::filesystem::path pth = captures_dir;
pth += u"/" + entry.path();
auto metadata_path = get_metadata_path(pth);
auto metadata = read_metadata_file(metadata_path);
if (metadata) {
if (metadata.value().latitude != 0 && metadata.value().longitude != 0 && metadata.value().latitude < 400 && metadata.value().longitude < 400) {
if (first_init == false) {
// move map there before add, so will display this.
geopos.set_report_change(false);
geopos.set_lat(metadata.value().latitude);
geopos.set_lon(metadata.value().longitude);
geopos.set_report_change(true);
geomap.move(metadata.value().longitude, metadata.value().latitude);
first_init = true;
}
GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()};
if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++;
cnt++;
}
}
}
}
return (cnt > 0);
}
WardriveMapView::WardriveMapView(NavigationView& nav)
: nav_{nav} {
add_children({&text_info,
&geomap,
&geopos,
&text_notfound});
geomap.set_mode(DISPLAY);
geomap.set_manual_panning(false);
geomap.set_focusable(true);
geomap.set_hide_center_marker(true);
geomap.clear_markers();
geopos.set_report_change(false);
geopos.set_lat(0);
geopos.set_lon(0);
geopos.set_altitude(0);
geopos.set_speed(0);
geopos.set_read_only(true);
geopos.hide_altandspeed();
geopos.set_report_change(true);
geopos.on_change = [this](int32_t altitude, float lat, float lon, int32_t speed) {
(void)altitude;
(void)speed;
geomap.set_manual_panning(true);
geomap.move(lon, lat);
load_markers();
geomap.set_dirty();
};
text_notfound.hidden(true);
geomap.init();
// load markers
if (load_markers()) {
text_notfound.hidden(true);
geomap.set_dirty();
} else {
geomap.hidden(true);
geopos.hidden(true);
}
geomap.on_move = [this](float lon, float lat) {
(void)lon;
(void)lat;
load_markers();
};
}
WardriveMapView::~WardriveMapView() {
}
void WardriveMapView::on_gps(const GPSPosDataMessage* msg) {
geomap.update_my_position(msg->lat, msg->lon, msg->altitude);
if (geomap.manual_panning() == false) {
geopos.set_report_change(false);
geopos.set_lat(msg->lat);
geopos.set_lon(msg->lon);
geopos.set_altitude(msg->altitude);
geopos.set_speed(msg->speed);
geopos.set_report_change(true);
geomap.move(msg->lon, msg->lat);
load_markers();
}
geomap.set_dirty();
}
void WardriveMapView::on_orientation(const OrientationDataMessage* msg) {
geomap.set_angle(msg->angle);
geomap.update_my_orientation(msg->angle, true);
}
} // namespace ui::external_app::wardrivemap
@@ -0,0 +1,85 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
// Code from https://github.com/Flipper-XFW/Xtreme-Apps/tree/04c3a60093e2c2378e79498b4505aa8072980a42/ble_spam/protocols
// Thanks for the work of the original creators!
// Saying thanks in the main view!
#ifndef __UI_WARDRIVEMAP_H__
#define __UI_WARDRIVEMAP_H__
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_geomap.hpp"
#include "app_settings.hpp"
#include "utility.hpp"
using namespace ui;
namespace ui::external_app::wardrivemap {
class WardriveMapView : public View {
public:
WardriveMapView(NavigationView& nav);
~WardriveMapView();
void focus() override;
std::string title() const override {
return "WardriveMap";
};
private:
NavigationView& nav_;
Text text_info{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "All GEOTAG from CAPTURES"};
Text text_notfound{{0 * 8, 3 * 8, 30 * 8, 16 * 1}, "No GeoTagged captures found"};
GeoPos geopos{
{0, 20},
GeoPos::alt_unit::METERS,
GeoPos::spd_unit::HIDDEN};
GeoMap geomap{{0, 75, 240, 320 - 75}};
void on_gps(const GPSPosDataMessage* msg);
void on_orientation(const OrientationDataMessage* msg);
bool load_markers(); // returns true if any exists, false if none.
bool first_init = false;
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);
}};
};
}; // namespace ui::external_app::wardrivemap
#endif /*__UI_WARDRIVEMAP_H__*/
+49
View File
@@ -0,0 +1,49 @@
/*
* 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 apps_dir = u"APPS";
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 gps_dir = u"GPS";
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 samples_dir = u"SAMPLES";
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";
+53
View File
@@ -0,0 +1,53 @@
/*
* 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 apps_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 gps_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 samples_dir;
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__ */
+1 -1
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.
-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;
-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) {
+1
View File
@@ -158,6 +158,7 @@ static void event_loop() {
event_dispatcher.set_display_sleep(true);
}};
portapack::setEventDispatcherToUSBSerial(&event_dispatcher);
system_view.get_navigation_view()->handle_autostart();
event_dispatcher.run();
}
+1
View File
@@ -68,6 +68,7 @@ rf::Frequency ReceiverModel::target_frequency() const {
void ReceiverModel::set_target_frequency(rf::Frequency f) {
persistent_memory::set_target_frequency(f);
settings_.frequency_app_override = f;
update_tuning_frequency();
}
+1
View File
@@ -48,6 +48,7 @@ class ReceiverModel {
uint32_t baseband_bandwidth = max283x::filter::bandwidth_minimum;
uint32_t sampling_rate = 3'072'000;
rf::Frequency frequency_step = 25'000;
rf::Frequency frequency_app_override = 0;
uint8_t lna_gain_db = 32;
uint8_t vga_gain_db = 32;
bool rf_amp = false;
+14
View File
@@ -4,6 +4,7 @@
* Copyright (C) 2019 Elia Yehuda (z4ziggy)
* Copyright (C) 2023 Mark Thompson
* Copyright (C) 2024 u-foka
* Copyleft () 2024 zxkmm with the GPL license
*
* This file is part of PortaPack.
*
@@ -133,6 +134,19 @@ void BtnGridView::add_item(GridItem new_item) {
}
}
void BtnGridView::insert_item(GridItem new_item, uint8_t position) {
if (!blacklisted_app(new_item)) {
if (position < menu_items.size()) {
auto pos_iter = menu_items.begin() + position;
menu_items.insert(pos_iter, new_item);
update_items();
} else {
menu_items.push_back(new_item);
update_items();
}
}
}
void BtnGridView::update_items() {
size_t i = 0;
Color bg_color = portapack::persistent_memory::menu_color();
+1
View File
@@ -61,6 +61,7 @@ class BtnGridView : public View {
void add_items(std::initializer_list<GridItem> new_items);
void add_item(GridItem new_item);
void insert_item(GridItem new_item, uint8_t position);
void set_max_rows(int rows);
int rows();
void clear();
+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());
}
}
}
@@ -1,6 +1,7 @@
#include "ui_external_items_menu_loader.hpp"
#include "sd_card.hpp"
#include "file_path.hpp"
namespace ui {
@@ -11,8 +12,8 @@ namespace ui {
if (!callback) return;
if (sd_card::status() != sd_card::Status::Mounted)
return;
for (const auto& entry : std::filesystem::directory_iterator(u"APPS", u"*.ppma")) {
auto filePath = u"/APPS/" + entry.path();
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppma")) {
auto filePath = apps_dir / entry.path();
File app;
auto openError = app.open(filePath);
@@ -52,8 +53,8 @@ namespace ui {
if (sd_card::status() != sd_card::Status::Mounted)
return external_apps;
for (const auto& entry : std::filesystem::directory_iterator(u"APPS", u"*.ppma")) {
auto filePath = u"/APPS/" + entry.path();
for (const auto& entry : std::filesystem::directory_iterator(apps_dir, u"*.ppma")) {
auto filePath = apps_dir / entry.path();
File app;
auto openError = app.open(filePath);
@@ -86,7 +87,7 @@ namespace ui {
gridItem.on_select = [&nav, app_location, filePath]() {
if (!run_external_app(nav, filePath)) {
nav.display_modal("Error", "The .ppma file in your APPS\nfolder can't be read. Please\nupdate your SD Card content.");
nav.display_modal("Error", "The .ppma file in your " + apps_dir.string() + "\nfolder can't be read. Please\nupdate your SD Card content.");
}
};
} else {
@@ -95,7 +96,7 @@ namespace ui {
gridItem.bitmap = &bitmap_sd_card_error;
gridItem.on_select = [&nav]() {
nav.display_modal("Error", "The .ppma file in your APPS\nfolder is outdated. Please\nupdate your SD Card content.");
nav.display_modal("Error", "The .ppma file in your " + apps_dir.string() + "\nfolder is outdated. Please\nupdate your SD Card content.");
};
}
+38 -13
View File
@@ -106,6 +106,11 @@
#include "file.hpp"
#include "file_reader.hpp"
#include "png_writer.hpp"
#include "file_path.hpp"
#include "ff.h"
#include <locale>
#include <codecvt>
using portapack::receiver_model;
using portapack::transmitter_model;
@@ -473,8 +478,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 +540,7 @@ void SystemStatusView::rtc_battery_workaround() {
}
}
} else {
ensure_directory(SETTINGS_DIR);
ensure_directory(settings_dir);
make_new_file(DATE_FILEFLAG);
year = 1980;
@@ -707,6 +712,23 @@ bool NavigationView::set_on_pop(std::function<void()> on_pop) {
return true;
}
void NavigationView::handle_autostart() {
std::string autostart_app{""};
SettingsStore nav_setting{
"nav"sv,
{{"autostart_app"sv, &autostart_app}}};
if (!autostart_app.empty()) {
if (StartAppByName(autostart_app.c_str())) return;
// if returned false, check for external apps by that name, and try to start it
std::string appwithpath = "/" + apps_dir.string() + "/";
appwithpath += autostart_app;
appwithpath += ".ppma";
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conv;
std::filesystem::path pth = conv.from_bytes(appwithpath.c_str());
ui::ExternalItemsMenuLoader::run_external_app(*this, pth);
}
}
/* Helpers **************************************************************/
static void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc) {
@@ -718,25 +740,28 @@ static void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc)
};
}
// clang-format off
void addExternalItems(NavigationView& nav, app_location_t location, BtnGridView& grid) {
auto externalItems = ExternalItemsMenuLoader::load_external_items(location, nav);
if (externalItems.empty()) {
grid.add_item({"Notice",
Color::red(),
&bitmap_icon_debug,
[&nav]() {
nav.display_modal(
"Notice",
"External app directory empty;\n"
"see Mayhem wiki and copy apps\n"
"to APPS folder of SD card.");
}});
grid.insert_item({"Notice!",
Color::red(),
nullptr,
[&nav]() {
nav.display_modal(
"Notice",
"External app directory empty;\n"
"see Mayhem wiki and copy apps\n"
"to " + apps_dir.string() + " folder of SD card.");
}},
pmem::show_gui_return_icon() ? 1 : 0);
} else {
for (auto const& gridItem : externalItems) {
grid.add_item(gridItem);
}
}
}
// clang-format on
/* ReceiversMenuView *****************************************************/
+2
View File
@@ -136,6 +136,8 @@ class NavigationView : public View {
static const AppList appList;
bool StartAppByName(const char* name); // Starts a View (app) by name stored in appListFC. This is to start apps from console
void handle_autostart();
private:
struct ViewState {
std::unique_ptr<View> view;
+14 -8
View File
@@ -75,6 +75,7 @@ RecordView::RecordView(
&rect_background,
//&button_pitch_rssi,
&button_record,
&gps_icon,
&text_record_filename,
&text_record_dropped,
&text_time_available,
@@ -93,6 +94,7 @@ RecordView::RecordView(
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
this->on_tick_second();
};
gps_icon.hidden(true);
}
RecordView::~RecordView() {
@@ -180,6 +182,13 @@ void RecordView::start() {
}
std::filesystem::path base_path;
auto tmp_path = filename_stem_pattern; // store it, to be able to modify without causing permanent change
// check for geo data, if present append filename with _GEO
if (latitude != 0 && longitude != 0 && latitude < 200 && longitude < 200) {
tmp_path.append_filename(u"_GEO");
}
if (filename_date_frequency) {
rtc_time::now(datetime);
@@ -192,24 +201,19 @@ void RecordView::start() {
to_string_dec_uint(datetime.minute()) +
to_string_dec_uint(datetime.second());
base_path = filename_stem_pattern.string() + "_" + date_time + "_" +
base_path = tmp_path.string() + "_" + date_time + "_" +
trim(to_string_freq(receiver_model.target_frequency())) + "Hz";
base_path = folder / base_path;
} else if (filename_as_is) {
base_path = filename_stem_pattern.string();
base_path = tmp_path.string();
base_path = folder / base_path;
} else
base_path = next_filename_matching_pattern(folder / filename_stem_pattern);
base_path = next_filename_matching_pattern(folder / tmp_path);
if (base_path.empty()) {
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: {
@@ -348,6 +352,8 @@ void RecordView::trim_capture() {
}
void RecordView::on_gps(const GPSPosDataMessage* msg) {
if (msg->lat == 0 || msg->lat > 399) return; // not valid one
if (latitude == 0) gps_icon.hidden(false); // prev was 0, so not shown already
latitude = msg->lat;
longitude = msg->lon;
satinuse = msg->satinuse;
+6
View File
@@ -144,6 +144,12 @@ class RecordView : public View {
"",
};
Image gps_icon{
{2 * 8 + 1, 0 * 16, 2 * 8, 1 * 16},
&bitmap_target,
Color::white(),
Color::black()};
std::unique_ptr<CaptureThread> capture_thread{};
MessageHandlerRegistration message_handler_capture_thread_error{
+6 -5
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;
@@ -664,7 +665,7 @@ static void cmd_appstart(BaseSequentialStream* chp, int argc, char* argv[]) {
return;
}
// since ext app loader changed, we can just pass the string to it, and it"ll return if started or not.
std::string appwithpath = "/APPS/";
std::string appwithpath = "/" + apps_dir.string() + "/";
appwithpath += argv[0];
appwithpath += ".ppma";
bool ret = ui::ExternalItemsMenuLoader::run_external_app(*nav, path_from_string8((char*)appwithpath.c_str()));
@@ -1037,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");

Some files were not shown because too many files have changed in this diff Show More