mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-24 16:39:04 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e90c65a62 | |||
| bfe67a13b7 | |||
| f091db3270 | |||
| a602abf1d8 | |||
| 689224fd8d | |||
| 23cf1c2737 | |||
| c48cbb7e55 | |||
| 3665b3c607 | |||
| 804b7c87b7 | |||
| a30875b582 | |||
| 56303eb385 | |||
| d29172f41e | |||
| 6d9d5ad1af | |||
| 2acefee4d2 | |||
| cb9fee8f08 | |||
| b473930f94 | |||
| 6e5eadd25c | |||
| 76017c91a9 | |||
| f1ebb1a6e3 | |||
| 746bf1c15f | |||
| 0db65ccb6b | |||
| fe2fbb847f | |||
| ba36680a7b | |||
| 90ff1e62ce | |||
| 2e1e02baac | |||
| 2dca408e45 | |||
| fa56cdd015 | |||
| 264fa9a28e | |||
| d5c8525afc | |||
| d9bbd1b9ff |
@@ -1 +1 @@
|
||||
v1.9.1
|
||||
v2.0.0
|
||||
|
||||
@@ -1 +1 @@
|
||||
v2.0.0
|
||||
v2.0.1
|
||||
|
||||
@@ -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:
|
||||
|
||||

|
||||
|
||||
@@ -190,6 +190,7 @@ set(CPPSRC
|
||||
event_m0.cpp
|
||||
file_reader.cpp
|
||||
file.cpp
|
||||
file_path.cpp
|
||||
freqman_db.cpp
|
||||
freqman.cpp
|
||||
io_convert.cpp
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@@ -40,7 +41,7 @@ using namespace portapack;
|
||||
|
||||
namespace {
|
||||
fs::path get_settings_path(const std::string& app_name) {
|
||||
return fs::path{SETTINGS_DIR} / app_name + u".ini";
|
||||
return settings_dir / app_name + u".ini";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -156,7 +157,7 @@ bool save_settings(std::string_view store_name, const SettingBindings& bindings)
|
||||
File f;
|
||||
auto path = get_settings_path(std::string{store_name});
|
||||
|
||||
ensure_directory(SETTINGS_DIR);
|
||||
ensure_directory(settings_dir);
|
||||
auto error = f.create(path);
|
||||
if (error)
|
||||
return false;
|
||||
@@ -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),
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace acars;
|
||||
@@ -77,7 +78,7 @@ ACARSAppView::ACARSAppView(NavigationView& nav)
|
||||
|
||||
logger = std::make_unique<ACARSLogger>();
|
||||
if (logger)
|
||||
logger->append(LOG_ROOT_DIR "/ACARS.TXT");
|
||||
logger->append(logs_dir / u"ACARS.TXT");
|
||||
}
|
||||
|
||||
ACARSAppView::~ACARSAppView() {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
class ACARSLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
}};
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "ui_text.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
@@ -100,7 +101,7 @@ BLECommView::BLECommView(NavigationView& nav)
|
||||
logging = v;
|
||||
|
||||
if (logger && logging)
|
||||
logger->append(LOG_ROOT_DIR "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
logger->append(logs_dir.string() + "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
};
|
||||
|
||||
options_channel.on_change = [this](size_t, int32_t i) {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
class BLECommLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -470,10 +470,7 @@ BLERxView::BLERxView(NavigationView& nav)
|
||||
logging = v;
|
||||
|
||||
if (logger && logging)
|
||||
logger->append(
|
||||
"BLERX/Logs"
|
||||
"/BLELOG_" +
|
||||
to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
logger->append(blerx_dir.string() + "/Logs/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
};
|
||||
check_log.set_value(logging);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -36,12 +36,13 @@
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "usb_serial_thread.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
class BLELogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
@@ -133,7 +134,7 @@ class BleRecentEntryDetailView : public View {
|
||||
void on_save_file(const std::string value, BLETxPacket packetToSave);
|
||||
bool saveFile(const std::filesystem::path& path, BLETxPacket packetToSave);
|
||||
std::string packetFileBuffer{};
|
||||
std::filesystem::path packet_save_path{u"BLERX/Lists/????.csv"};
|
||||
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
|
||||
|
||||
static constexpr uint8_t total_data_lines{5};
|
||||
|
||||
@@ -250,9 +251,9 @@ class BLERxView : public View {
|
||||
uint64_t total_count = 0;
|
||||
std::vector<std::string> searchList{};
|
||||
|
||||
std::filesystem::path find_packet_path{u"BLERX/Find/????.TXT"};
|
||||
std::filesystem::path log_packets_path{u"BLERX/Logs/????.TXT"};
|
||||
std::filesystem::path packet_save_path{u"BLERX/Lists/????.csv"};
|
||||
std::filesystem::path find_packet_path{blerx_dir / u"Find/????.TXT"};
|
||||
std::filesystem::path log_packets_path{blerx_dir / u"Logs/????.TXT"};
|
||||
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
|
||||
|
||||
static constexpr auto header_height = 4 * 16;
|
||||
static constexpr auto switch_button_height = 3 * 16;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -111,7 +112,7 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
|
||||
|
||||
logger = std::make_unique<APRSLogger>();
|
||||
if (logger)
|
||||
logger->append(LOG_ROOT_DIR "/APRS.TXT");
|
||||
logger->append(logs_dir / u"APRS.TXT");
|
||||
|
||||
baseband::set_aprs(1200);
|
||||
|
||||
|
||||
@@ -36,10 +36,11 @@
|
||||
|
||||
#include "log_file.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
class APRSLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
@@ -232,7 +233,7 @@ class APRSRxView : public View {
|
||||
RecordView record_view{
|
||||
{0 * 8, 1 * 16, 30 * 8, 1 * 16},
|
||||
u"AFS_????.WAV",
|
||||
u"APRS",
|
||||
aprs_dir,
|
||||
RecordView::FileType::WAV,
|
||||
4096,
|
||||
4};
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include "ui_freqman.hpp"
|
||||
|
||||
@@ -134,7 +135,7 @@ FskxRxMainView::FskxRxMainView(NavigationView& nav)
|
||||
field_frequency.set_value(initial_target_frequency);
|
||||
deviation_frequency.set_value(initial_deviation);
|
||||
|
||||
logger.append(LOG_ROOT_DIR "/FSKRX.TXT");
|
||||
logger.append(logs_dir / u"FSKRX.TXT");
|
||||
|
||||
baseband::set_fsk(initial_deviation);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
class FskRxLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "complex.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
@@ -47,7 +48,7 @@ IQTrimView::IQTrimView(NavigationView& nav)
|
||||
|
||||
field_path.on_select = [this](TextField&) {
|
||||
auto open_view = nav_.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(u"CAPTURES");
|
||||
open_view->push_dir(captures_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
open_file(path);
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "file_reader.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -49,12 +50,12 @@ int32_t GlassView::map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t
|
||||
void GlassView::update_display_beep() {
|
||||
if (beep_enabled) {
|
||||
button_beep_squelch.set_style(&Styles::green);
|
||||
// <bip:-XXXdb>
|
||||
button_beep_squelch.set_text("[bip>" + to_string_dec_int(beep_squelch, 4) + "db]");
|
||||
// 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("[ beep OFF ]");
|
||||
button_beep_squelch.set_text("bip OFF ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,8 +548,8 @@ GlassView::GlassView(
|
||||
|
||||
button_beep_squelch.on_change = [this]() {
|
||||
int new_beep_squelch = beep_squelch + button_beep_squelch.get_encoder_delta();
|
||||
if (new_beep_squelch < -100)
|
||||
new_beep_squelch = -100;
|
||||
if (new_beep_squelch < -99)
|
||||
new_beep_squelch = -99;
|
||||
if (new_beep_squelch > 20)
|
||||
new_beep_squelch = 20;
|
||||
beep_squelch = new_beep_squelch;
|
||||
@@ -571,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.
|
||||
|
||||
@@ -173,7 +173,7 @@ 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: STEPS:", Color::light_grey()}};
|
||||
{{0, 4 * 16}, "RES: VOL:", Color::light_grey()}};
|
||||
@@ -215,12 +215,12 @@ class GlassView : public View {
|
||||
{28 * 8, 1 * 16}};
|
||||
|
||||
OptionsField range_presets{
|
||||
{7 * 8, 2 * 16},
|
||||
10,
|
||||
{2 * 8, 2 * 16},
|
||||
20,
|
||||
{}};
|
||||
|
||||
ButtonWithEncoder button_beep_squelch{
|
||||
{18 * 8, 2 * 16 + 4, 12 * 8, 1 * 8},
|
||||
{240 - 8 * 8, 2 * 16 + 4, 8 * 8, 1 * 8},
|
||||
""};
|
||||
|
||||
TextField field_marker{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fstream>
|
||||
@@ -130,7 +131,7 @@ void PlaylistView::open_file(bool prompt_save) {
|
||||
}
|
||||
|
||||
auto open_view = nav_.push<FileLoadView>(".PPL");
|
||||
open_view->push_dir(u"PLAYLIST");
|
||||
open_view->push_dir(playlist_dir);
|
||||
open_view->on_changed = [this](fs::path new_file_path) {
|
||||
on_file_changed(new_file_path);
|
||||
};
|
||||
@@ -169,7 +170,7 @@ void PlaylistView::save_file(bool show_dialogs) {
|
||||
|
||||
void PlaylistView::add_entry(fs::path&& path) {
|
||||
if (playlist_path_.empty()) {
|
||||
playlist_path_ = next_filename_matching_pattern(u"/PLAYLIST/PLAY_????.PPL");
|
||||
playlist_path_ = next_filename_matching_pattern(playlist_dir / u"PLAY_????.PPL");
|
||||
|
||||
// Hack around focus getting called by ctor before parent is set.
|
||||
if (parent())
|
||||
@@ -387,7 +388,7 @@ PlaylistView::PlaylistView(
|
||||
&waterfall,
|
||||
});
|
||||
|
||||
ensure_directory(u"PLAYLIST");
|
||||
ensure_directory(playlist_dir);
|
||||
waterfall.show_audio_spectrum_view(false);
|
||||
|
||||
field_frequency.set_value(transmitter_model.target_frequency());
|
||||
@@ -410,7 +411,7 @@ PlaylistView::PlaylistView(
|
||||
if (is_active())
|
||||
return;
|
||||
auto open_view = nav_.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(u"CAPTURES");
|
||||
open_view->push_dir(captures_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
add_entry(std::move(path));
|
||||
};
|
||||
|
||||
@@ -337,7 +337,7 @@ ReconView::ReconView(NavigationView& nav)
|
||||
|
||||
// set record View
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"AUTO_AUDIO", u"AUDIO",
|
||||
u"AUTO_AUDIO", audio_dir,
|
||||
RecordView::FileType::WAV, 4096, 4);
|
||||
record_view->set_filename_date_frequency(true);
|
||||
record_view->set_auto_trim(false);
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
@@ -246,7 +247,7 @@ RemoteEntryEditView::RemoteEntryEditView(
|
||||
|
||||
field_path.on_select = [this, &nav](TextField&) {
|
||||
auto open_view = nav.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(u"CAPTURES");
|
||||
open_view->push_dir(captures_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
load_path(std::move(path));
|
||||
refresh_ui();
|
||||
@@ -355,7 +356,7 @@ RemoteView::RemoteView(
|
||||
Dim waterfall_height = waterfall_bottom - waterfall_top;
|
||||
waterfall.set_parent_rect({0, waterfall_top, screen_width, waterfall_height});
|
||||
|
||||
ensure_directory(u"REMOTES");
|
||||
ensure_directory(remotes_dir);
|
||||
|
||||
// Load the previously loaded remote if exists.
|
||||
if (!load_remote(settings_.remote_path))
|
||||
@@ -527,7 +528,7 @@ void RemoteView::new_remote() {
|
||||
|
||||
void RemoteView::open_remote() {
|
||||
auto open_view = nav_.push<FileLoadView>(".REM");
|
||||
open_view->push_dir(u"REMOTES");
|
||||
open_view->push_dir(remotes_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
save_remote();
|
||||
load_remote(std::move(path));
|
||||
@@ -538,7 +539,7 @@ void RemoteView::open_remote() {
|
||||
void RemoteView::init_remote() {
|
||||
model_ = {"<Unnamed Remote>", {}};
|
||||
reset_buttons();
|
||||
set_remote_path(next_filename_matching_pattern(u"/REMOTES/REMOTE_????.REM"));
|
||||
set_remote_path(next_filename_matching_pattern(remotes_dir / u"REMOTE_????.REM"));
|
||||
set_needs_save(false);
|
||||
|
||||
if (remote_path_.empty())
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "optional.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
@@ -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());
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -861,6 +921,7 @@ void SettingsMenuView::on_populate() {
|
||||
//{"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>(); }},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -25,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"
|
||||
@@ -53,7 +55,6 @@ SondeView::SondeView(NavigationView& nav)
|
||||
&field_vga,
|
||||
&rssi,
|
||||
&field_volume,
|
||||
&check_beep,
|
||||
&check_log,
|
||||
&check_crc,
|
||||
&text_signature,
|
||||
@@ -71,13 +72,6 @@ SondeView::SondeView(NavigationView& nav)
|
||||
|
||||
geopos.set_read_only(true);
|
||||
|
||||
check_beep.set_value(beep);
|
||||
check_beep.on_select = [this](Checkbox&, bool v) {
|
||||
beep = v;
|
||||
if (beep)
|
||||
baseband::request_audio_beep(1000, 24000, 60); // 1khz tone for 60ms to acknowledge enablement
|
||||
};
|
||||
|
||||
check_log.set_value(logging);
|
||||
check_log.on_select = [this](Checkbox&, bool v) {
|
||||
logging = v;
|
||||
@@ -112,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
|
||||
@@ -184,7 +181,7 @@ void SondeView::on_packet(const sonde::Packet& packet) {
|
||||
logger->on_packet(packet);
|
||||
}
|
||||
|
||||
if (beep) {
|
||||
if (pmem::beep_on_packets()) {
|
||||
baseband::request_rssi_beep();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,14 +75,12 @@ class SondeView : public View {
|
||||
1750000 /* bandwidth */,
|
||||
2457600 /* sampling rate */
|
||||
};
|
||||
bool beep{false};
|
||||
bool logging{false};
|
||||
bool use_crc{false};
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_sonde",
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"beep"sv, &beep},
|
||||
{"logging"sv, &logging},
|
||||
{"use_crc"sv, &use_crc},
|
||||
}};
|
||||
@@ -124,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,
|
||||
|
||||
@@ -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_};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
|
||||
#include "database.hpp"
|
||||
#include "file.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include <cstring>
|
||||
|
||||
int database::retrieve_mid_record(MidDBRecord* record, std::string search_term) {
|
||||
file_path = "AIS/mids.db";
|
||||
file_path = ais_dir / u"mids.db";
|
||||
index_item_length = 4;
|
||||
record_length = 32;
|
||||
|
||||
@@ -36,7 +37,7 @@ int database::retrieve_mid_record(MidDBRecord* record, std::string search_term)
|
||||
}
|
||||
|
||||
int database::retrieve_airline_record(AirlinesDBRecord* record, std::string search_term) {
|
||||
file_path = "ADSB/airlines.db";
|
||||
file_path = adsb_dir / u"airlines.db";
|
||||
index_item_length = 4;
|
||||
record_length = 64;
|
||||
|
||||
@@ -46,7 +47,7 @@ int database::retrieve_airline_record(AirlinesDBRecord* record, std::string sear
|
||||
}
|
||||
|
||||
int database::retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term) {
|
||||
file_path = "ADSB/icao24.db";
|
||||
file_path = adsb_dir / u"icao24.db";
|
||||
index_item_length = 7;
|
||||
record_length = 146;
|
||||
|
||||
@@ -55,7 +56,7 @@ int database::retrieve_aircraft_record(AircraftDBRecord* record, std::string sea
|
||||
return (result);
|
||||
}
|
||||
|
||||
int database::retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term) {
|
||||
int database::retrieve_record(std::filesystem::path file_path, int index_item_length, int record_length, void* record, std::string search_term) {
|
||||
if (search_term.empty())
|
||||
return DATABASE_RECORD_NOT_FOUND;
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@ class database {
|
||||
int retrieve_aircraft_record(AircraftDBRecord* record, std::string search_term);
|
||||
|
||||
private:
|
||||
std::string file_path = ""; // path inclusing filename
|
||||
int index_item_length = 0; // length of index item
|
||||
int record_length = 0; // length of record
|
||||
std::filesystem::path file_path = ""; // path including filename
|
||||
int index_item_length = 0; // length of index item
|
||||
int record_length = 0; // length of record
|
||||
|
||||
File db_file{};
|
||||
int number_of_records = 0;
|
||||
@@ -74,7 +74,7 @@ class database {
|
||||
|
||||
int result = 0;
|
||||
|
||||
int retrieve_record(std::string file_path, int index_item_length, int record_length, void* record, std::string search_term);
|
||||
int retrieve_record(std::filesystem::path file_path, int index_item_length, int record_length, void* record, std::string search_term);
|
||||
};
|
||||
|
||||
#endif /*__DATABASE_H__*/
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
@@ -266,7 +267,6 @@ bool stack_dump() {
|
||||
|
||||
bool memory_dump(uint32_t* addr_start, uint32_t num_words, bool stack_flag) {
|
||||
Painter painter;
|
||||
std::string debug_dir = "DEBUG";
|
||||
std::filesystem::path filename{};
|
||||
File dump_file{};
|
||||
bool error;
|
||||
@@ -277,7 +277,7 @@ bool memory_dump(uint32_t* addr_start, uint32_t num_words, bool stack_flag) {
|
||||
bool data_found{false};
|
||||
|
||||
ensure_directory(debug_dir);
|
||||
filename = next_filename_matching_pattern(debug_dir + "/" + (stack_flag ? "STACK" : "MEMORY") + "_DUMP_????.TXT");
|
||||
filename = next_filename_matching_pattern(debug_dir + (stack_flag ? u"/STACK" : u"/MEMORY") + u"_DUMP_????.TXT");
|
||||
error = filename.empty();
|
||||
if (!error)
|
||||
error = dump_file.create(filename) != 0;
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace modems;
|
||||
@@ -84,7 +85,7 @@ AFSKRxView::AFSKRxView(NavigationView& nav)
|
||||
|
||||
logger = std::make_unique<AFSKLogger>();
|
||||
if (logger)
|
||||
logger->append(LOG_ROOT_DIR "/AFSK.TXT");
|
||||
logger->append(logs_dir / u"AFSK.TXT");
|
||||
|
||||
// Auto-configure modem for LCR RX (will be removed later)
|
||||
baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false);
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ namespace ui::external_app::afsk_rx {
|
||||
|
||||
class AFSKLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::string& filename) {
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,6 +76,10 @@ set(EXTCPPSRC
|
||||
#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
|
||||
@@ -97,4 +101,5 @@ set(EXTAPPLIST
|
||||
extsensors
|
||||
foxhunt_rx
|
||||
audio_test
|
||||
wardrivemap
|
||||
)
|
||||
|
||||
+7
@@ -41,6 +41,7 @@ MEMORY
|
||||
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
|
||||
@@ -153,4 +154,10 @@ SECTIONS
|
||||
*(*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
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -62,8 +62,7 @@ FoxhuntRxView::FoxhuntRxView(NavigationView& nav)
|
||||
};
|
||||
geomap.set_mode(DISPLAY);
|
||||
geomap.set_manual_panning(false);
|
||||
// geomap.set_enable_additional_zoom(true);
|
||||
// geomap.set_hide_center_marker(true); //todo hide again after testing
|
||||
// geomap.set_hide_center_marker(true); //todo test if needed
|
||||
geomap.set_focusable(true);
|
||||
geomap.clear_markers();
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
|
||||
|
||||
+4
-1
@@ -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};
|
||||
|
||||
@@ -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__*/
|
||||
@@ -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";
|
||||
@@ -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__ */
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.");
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 *****************************************************/
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
@@ -144,16 +145,30 @@ void ADSBRXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
}
|
||||
|
||||
void ADSBRXProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::ADSBConfigure) {
|
||||
bit_count = 0;
|
||||
sample_count = 0;
|
||||
decoding = false;
|
||||
configured = true;
|
||||
switch (message->id) {
|
||||
case Message::ID::ADSBConfigure:
|
||||
bit_count = 0;
|
||||
sample_count = 0;
|
||||
decoding = false;
|
||||
configured = true;
|
||||
break;
|
||||
|
||||
case Message::ID::AudioBeep:
|
||||
on_beep_message(*reinterpret_cast<const AudioBeepMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ADSBRXProcessor::on_beep_message(const AudioBeepMessage& message) {
|
||||
audio::dma::beep_start(message.freq, message.sample_rate, message.duration_ms);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
EventDispatcher event_dispatcher{std::make_unique<ADSBRXProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -53,6 +53,8 @@ class ADSBRXProcessor : public BasebandProcessor {
|
||||
|
||||
uint32_t shifter[ADSB_PREAMBLE_LENGTH + 1];
|
||||
|
||||
void on_beep_message(const AudioBeepMessage& message);
|
||||
|
||||
/* NB: Threads should be the last members in the class definition. */
|
||||
BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive};
|
||||
RSSIThread rssi_thread{};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "proc_ais.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
@@ -64,7 +65,17 @@ void AISProcessor::payload_handler(
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
void AISProcessor::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::AudioBeep)
|
||||
on_beep_message(*reinterpret_cast<const AudioBeepMessage*>(message));
|
||||
}
|
||||
|
||||
void AISProcessor::on_beep_message(const AudioBeepMessage& message) {
|
||||
audio::dma::beep_start(message.freq, message.sample_rate, message.duration_ms);
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
EventDispatcher event_dispatcher{std::make_unique<AISProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -74,13 +74,15 @@ class AISProcessor : public BasebandProcessor {
|
||||
this->payload_handler(packet);
|
||||
}};
|
||||
|
||||
void consume_symbol(const float symbol);
|
||||
void payload_handler(const baseband::Packet& packet);
|
||||
void on_message(const Message* const message);
|
||||
void on_beep_message(const AudioBeepMessage& message);
|
||||
|
||||
/* NB: Threads should be the last members in the class definition. */
|
||||
BasebandThread baseband_thread{
|
||||
baseband_fs, this, baseband::Direction::Receive, /*auto_start*/ false};
|
||||
RSSIThread rssi_thread{};
|
||||
|
||||
void consume_symbol(const float symbol);
|
||||
void payload_handler(const baseband::Packet& packet);
|
||||
};
|
||||
|
||||
#endif /*__PROC_AIS_H__*/
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#include "baseband_processor.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
#define AUDIO_SAMPLE_RATE 24000
|
||||
|
||||
class AudioBeepProcessor : public BasebandProcessor {
|
||||
public:
|
||||
AudioBeepProcessor();
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "proc_ert.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
@@ -109,7 +110,17 @@ void ERTProcessor::idm_handler(
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
void ERTProcessor::on_message(const Message* const msg) {
|
||||
if (msg->id == Message::ID::AudioBeep)
|
||||
on_beep_message(*reinterpret_cast<const AudioBeepMessage*>(msg));
|
||||
}
|
||||
|
||||
void ERTProcessor::on_beep_message(const AudioBeepMessage& message) {
|
||||
audio::dma::beep_start(message.freq, message.sample_rate, message.duration_ms);
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
EventDispatcher event_dispatcher{std::make_unique<ERTProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -102,6 +102,8 @@ class ERTProcessor : public BasebandProcessor {
|
||||
void scm_handler(const baseband::Packet& packet);
|
||||
void scmplus_handler(const baseband::Packet& packet);
|
||||
void idm_handler(const baseband::Packet& packet);
|
||||
void on_message(const Message* const msg);
|
||||
void on_beep_message(const AudioBeepMessage& message);
|
||||
|
||||
float sum_half_period[2];
|
||||
float sum_period[3];
|
||||
|
||||
@@ -361,6 +361,10 @@ void POCSAGProcessor::on_message(const Message* const message) {
|
||||
break;
|
||||
}
|
||||
|
||||
case Message::ID::AudioBeep:
|
||||
on_beep_message(*reinterpret_cast<const AudioBeepMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -414,6 +418,10 @@ void POCSAGProcessor::send_packet() {
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
void POCSAGProcessor::on_beep_message(const AudioBeepMessage& message) {
|
||||
audio::dma::beep_start(message.freq, message.sample_rate, message.duration_ms);
|
||||
}
|
||||
|
||||
/* main **************************************************/
|
||||
|
||||
int main() {
|
||||
|
||||
@@ -212,6 +212,7 @@ class POCSAGProcessor : public BasebandProcessor {
|
||||
void reset();
|
||||
void send_stats() const;
|
||||
void send_packet();
|
||||
void on_beep_message(const AudioBeepMessage& message);
|
||||
|
||||
/* Set once app is ready to receive messages. */
|
||||
bool configured = false;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "proc_tpms.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "dsp_fir_taps.hpp"
|
||||
|
||||
@@ -60,7 +61,17 @@ void TPMSProcessor::execute(const buffer_c8_t& buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
void TPMSProcessor::on_message(const Message* const msg) {
|
||||
if (msg->id == Message::ID::AudioBeep)
|
||||
on_beep_message(*reinterpret_cast<const AudioBeepMessage*>(msg));
|
||||
}
|
||||
|
||||
void TPMSProcessor::on_beep_message(const AudioBeepMessage& message) {
|
||||
audio::dma::beep_start(message.freq, message.sample_rate, message.duration_ms);
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
EventDispatcher event_dispatcher{std::make_unique<TPMSProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user