Compare commits
93 Commits
06f3283d85
...
8eaeeb6b7f
Author | SHA1 | Date |
---|---|---|
sommermorgentraum | 8eaeeb6b7f | |
zxkmm | 6c37d883a0 | |
sommermorgentraum | 0ea8453e8a | |
sommermorgentraum | 1ffedace7b | |
Mark Thompson | e43f7a7980 | |
Totoo | f6a2d21624 | |
sommermorgentraum | f572b00391 | |
Totoo | d74fd527e2 | |
zxkmm | 5d7cfda97b | |
zxkmm | 9fc45b32c9 | |
sommermorgentraum | b27a313118 | |
zxkmm | f2c86862c0 | |
sommermorgentraum | 8c0715408d | |
sommermorgentraum | 3eb6cf47ae | |
sommermorgentraum | c288f703da | |
zxkmm | 208a89090a | |
zxkmm | 6b3ea87d86 | |
zxkmm | 17b9231601 | |
zxkmm | cc89e2af9e | |
zxkmm | 77ba7bde98 | |
zxkmm | e2940a52f2 | |
zxkmm | 348ef17131 | |
zxkmm | c00dfeed3f | |
zxkmm | 7cbba2d2e7 | |
zxkmm | c36c9c9389 | |
zxkmm | a00543b4f8 | |
zxkmm | 75a930c1fa | |
zxkmm | d3f618fa5b | |
zxkmm | 6d2b290daa | |
zxkmm | 9e67e250b2 | |
zxkmm | 6b4abbc5f7 | |
zxkmm | 1b410675f6 | |
zxkmm | 24ff586155 | |
zxkmm | 6cdcb16a3a | |
zxkmm | 7a3b4966b4 | |
zxkmm | 50af3c605f | |
zxkmm | 6a56d743fc | |
zxkmm | 2a903bf9e4 | |
zxkmm | d150e39852 | |
zxkmm | d3e8260318 | |
zxkmm | fa596227ef | |
zxkmm | d4296ed434 | |
zxkmm | 923324e931 | |
zxkmm | 98c1b05e81 | |
zxkmm | 4ab6a38507 | |
zxkmm | 7d48624a78 | |
zxkmm | 6c36d45005 | |
zxkmm | 940c2d7682 | |
sommermorgentraum | a08ccf4f2e | |
zxkmm | 5e10a41331 | |
zxkmm | a0a2ff9724 | |
zxkmm | 04966f2e0f | |
zxkmm | 5b5b5333e9 | |
sommermorgentraum | 8d032438bd | |
zxkmm | 9460177fc9 | |
zxkmm | 675925269c | |
zxkmm | 119ebfe90e | |
zxkmm | 457eb71014 | |
sommermorgentraum | 6fb09f6d59 | |
not tre mann | e38c370d7d | |
not tre mann | b04bd4c0d9 | |
zxkmm | c5039f2eb0 | |
zxkmm | 93825234f3 | |
zxkmm | 5181923fc6 | |
zxkmm | 1451d49f8a | |
not tre mann | 3cd9faa24d | |
not tre mann | 6d638698d2 | |
zxkmm | 3a3c79a8ab | |
zxkmm | fa8ed6e9dd | |
not tre mann | 7b4a79ee38 | |
not tre mann | 7307703692 | |
zxkmm | 7a52068de8 | |
zxkmm | 30965ef083 | |
zxkmm | 75eeab2368 | |
zxkmm | 5f5aeb96f3 | |
zxkmm | bb774d362d | |
zxkmm | 4573a78ce0 | |
zxkmm | 62b8e86381 | |
zxkmm | 77145d0514 | |
zxkmm | 8bd2f5bc1f | |
zxkmm | 7c61e65c0b | |
zxkmm | 38c001bd1d | |
zxkmm | 19f5d3db93 | |
zxkmm | dc7f5ae35a | |
zxkmm | a95871162d | |
zxkmm | 30b4cc4e60 | |
not tre mann | 94ca23fc01 | |
zxkmm | 3167b584de | |
zxkmm | e7543d102c | |
not tre mann | 53b667abca | |
zxkmm | 8c9e64e61f | |
zxkmm | 1f26de2f4a | |
zxkmm | 78e6ba9f70 |
|
@ -9,10 +9,16 @@
|
|||
/firmware/application/portapack_cpld_data.cpp
|
||||
/firmware/application/hackrf_cpld_data.cpp
|
||||
/firmware/application/hackrf_cpld_data.hpp
|
||||
/sdcard/SYS/ADSB/world_map.bin
|
||||
/sdcard/SYS/FREQMAN/BHT*
|
||||
/sdcard/SYS/FREQMAN/R.TXT
|
||||
/sdcard/SYS/FREQMAN/XXX.TXT
|
||||
/sdcard/ADSB/world_map.bin
|
||||
/sdcard/FREQMAN/BHT*
|
||||
/sdcard/FREQMAN/R.TXT
|
||||
/sdcard/FREQMAN/XXX.TXT
|
||||
|
||||
# Toolchain binaries
|
||||
/armbin
|
||||
|
||||
# Compiled Object files
|
||||
|
|
|
@ -177,6 +177,7 @@ set(CPPSRC
|
|||
${COMMON}/utility.cpp
|
||||
${COMMON}/wm8731.cpp
|
||||
${COMMON}/ads1110.cpp
|
||||
${COMMON}/battery.cpp
|
||||
${COMMON}/performance_counter.cpp
|
||||
${COMMON}/bmpfile.cpp
|
||||
app_settings.cpp
|
||||
|
@ -277,13 +278,13 @@ set(CPPSRC
|
|||
apps/pocsag_app.cpp
|
||||
# apps/replay_app.cpp
|
||||
apps/soundboard_app.cpp
|
||||
apps/tpms_app.cpp
|
||||
apps/tpms_app.cpp
|
||||
# apps/tpms_app.cpp
|
||||
apps/ui_about_simple.cpp
|
||||
apps/ui_adsb_rx.cpp
|
||||
apps/ui_adsb_tx.cpp
|
||||
apps/ui_aprs_rx.cpp
|
||||
apps/ui_aprs_tx.cpp
|
||||
apps/ui_battinfo.cpp
|
||||
apps/ui_bht_tx.cpp
|
||||
apps/ui_bmp_file_viewer.cpp
|
||||
apps/ui_btle_rx.cpp
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "tone_key.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
|
@ -226,7 +227,7 @@ class AnalogAudioView : public View {
|
|||
RecordView record_view{
|
||||
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
|
||||
u"AUD",
|
||||
u"AUDIO",
|
||||
audio_dir_user,
|
||||
RecordView::FileType::WAV,
|
||||
4096,
|
||||
4};
|
||||
|
|
|
@ -470,7 +470,7 @@ BLERxView::BLERxView(NavigationView& nav)
|
|||
logging = v;
|
||||
|
||||
if (logger && logging)
|
||||
logger->append(blerx_dir.string() + "/Logs/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
logger->append(blerx_dir_user.string() + "/Logs/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
|
||||
};
|
||||
check_log.set_value(logging);
|
||||
|
||||
|
|
|
@ -134,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{blerx_dir / u"Lists/????.csv"};
|
||||
std::filesystem::path packet_save_path{blerx_dir_user / u"Lists/????.csv"};
|
||||
|
||||
static constexpr uint8_t total_data_lines{5};
|
||||
|
||||
|
@ -251,9 +251,9 @@ class BLERxView : public View {
|
|||
uint64_t total_count = 0;
|
||||
std::vector<std::string> searchList{};
|
||||
|
||||
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"};
|
||||
std::filesystem::path find_packet_path{blerx_dir_user / u"Find/????.TXT"};
|
||||
std::filesystem::path log_packets_path{blerx_dir_user / u"Logs/????.TXT"};
|
||||
std::filesystem::path packet_save_path{blerx_dir_user / u"Lists/????.csv"};
|
||||
|
||||
static constexpr auto header_height = 4 * 16;
|
||||
static constexpr auto switch_button_height = 3 * 16;
|
||||
|
|
|
@ -139,7 +139,7 @@ class BLETxView : public View {
|
|||
uint32_t prev_value{0};
|
||||
|
||||
std::filesystem::path file_path{};
|
||||
std::filesystem::path packet_save_path{bletx_dir / u"BLETX_????.TXT"};
|
||||
std::filesystem::path packet_save_path{bletx_dir_user / u"BLETX_????.TXT"};
|
||||
uint8_t channel_number = 37;
|
||||
bool auto_channel = false;
|
||||
|
||||
|
@ -166,7 +166,7 @@ class BLETxView : public View {
|
|||
|
||||
std::unique_ptr<FileWrapper> dataFileWrapper{};
|
||||
File dataFile{};
|
||||
std::filesystem::path dataTempFilePath{bletx_dir / u"dataFileTemp.TXT"};
|
||||
std::filesystem::path dataTempFilePath{bletx_dir_resources / u"dataFileTemp.TXT"};
|
||||
std::vector<uint16_t> markedBytes{};
|
||||
CursorPos cursor_pos{};
|
||||
uint8_t marked_counter = 0;
|
||||
|
|
|
@ -102,7 +102,7 @@ class CaptureAppView : public View {
|
|||
RecordView record_view{
|
||||
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
|
||||
u"BBD_????.*",
|
||||
captures_dir,
|
||||
captures_dir_user,
|
||||
RecordView::FileType::RawS16,
|
||||
16384,
|
||||
3};
|
||||
|
|
|
@ -95,9 +95,11 @@ void SoundBoardView::start_tx(const uint32_t id) {
|
|||
|
||||
stop();
|
||||
|
||||
if (!reader->open(u"/WAV/" + file_list[id].native())) {
|
||||
file_error();
|
||||
return;
|
||||
if (!reader->open(wav_dir_resources + u"/" + file_list[id].native())) {
|
||||
if (!reader->open(wav_dir_user + u"/" + file_list[id].native())) {
|
||||
file_error();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
playing_id = id;
|
||||
|
@ -164,28 +166,34 @@ void SoundBoardView::refresh_list() {
|
|||
c_page = page;
|
||||
|
||||
// List directories and files, put directories up top
|
||||
std::filesystem::path file_list_index[2];
|
||||
file_list_index[0] = wav_dir_user;
|
||||
file_list_index[1] = wav_dir_resources;
|
||||
uint32_t count = 0;
|
||||
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();
|
||||
// tempnewnote: need test
|
||||
for (const auto& now_path : file_list_index) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(now_path, u"*")) {
|
||||
if (std::filesystem::is_regular_file(entry.status())) {
|
||||
if (entry.path().string().length()) {
|
||||
auto entry_extension = entry.path().extension().string();
|
||||
|
||||
for (auto& c : entry_extension)
|
||||
c = toupper(c);
|
||||
for (auto& c : entry_extension)
|
||||
c = toupper(c);
|
||||
|
||||
if (entry_extension == ".WAV") {
|
||||
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();
|
||||
if (count >= (page - 1) * 100 && count < page * 100) {
|
||||
file_list.push_back(entry.path());
|
||||
if (file_list.size() == 100) {
|
||||
page++;
|
||||
break;
|
||||
if (entry_extension == ".WAV") {
|
||||
if (reader->open(now_path / entry.path().native())) {
|
||||
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"/SYS/WAV/" + entry.path().native();
|
||||
if (count >= (page - 1) * 100 && count < page * 100) {
|
||||
file_list.push_back(entry.path());
|
||||
if (file_list.size() == 100) {
|
||||
page++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,7 +233,7 @@ class APRSRxView : public View {
|
|||
RecordView record_view{
|
||||
{0 * 8, 1 * 16, 30 * 8, 1 * 16},
|
||||
u"AFS_????.WAV",
|
||||
aprs_dir,
|
||||
aprs_dir_user,
|
||||
RecordView::FileType::WAV,
|
||||
4096,
|
||||
4};
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui_battinfo.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
void BattinfoView::focus() {
|
||||
button_exit.focus();
|
||||
}
|
||||
|
||||
// called each 1/60th of second, so 6 = 100ms
|
||||
void BattinfoView::on_timer() {
|
||||
if (++timer_counter == timer_period) {
|
||||
timer_counter = 0;
|
||||
update_result();
|
||||
}
|
||||
}
|
||||
|
||||
void BattinfoView::update_result() {
|
||||
if (!battery::BatteryManagement::isDetected()) {
|
||||
// todo show no batt management
|
||||
text_percent.set("UNKNOWN");
|
||||
text_voltage.set("UNKNOWN");
|
||||
text_current.set("-");
|
||||
text_charge.set("-");
|
||||
return;
|
||||
}
|
||||
bool uichg = false;
|
||||
battery::BatteryManagement::getBatteryInfo(percent, voltage, current, isCharging);
|
||||
// update text fields
|
||||
if (percent <= 100)
|
||||
text_percent.set(to_string_dec_uint(percent) + " %");
|
||||
else
|
||||
text_percent.set("UNKNOWN");
|
||||
if (voltage > 1) {
|
||||
text_voltage.set(to_string_decimal(voltage / 1000.0, 3) + " V");
|
||||
} else {
|
||||
text_voltage.set("UNKNOWN");
|
||||
}
|
||||
if (current != 0) {
|
||||
if (labels_opt.hidden()) uichg = true;
|
||||
labels_opt.hidden(false);
|
||||
text_current.hidden(false);
|
||||
text_charge.hidden(false);
|
||||
text_current.set(to_string_dec_int(current) + " mA");
|
||||
text_charge.set(isCharging ? "charge" : "discharge");
|
||||
labels_opt.hidden(false);
|
||||
} else {
|
||||
if (!labels_opt.hidden()) uichg = true;
|
||||
labels_opt.hidden(true);
|
||||
text_current.hidden(true);
|
||||
text_charge.hidden(true);
|
||||
}
|
||||
if (uichg) set_dirty();
|
||||
// to update status bar too, send message in behalf of batt manager
|
||||
BatteryStateMessage msg{percent, isCharging, voltage};
|
||||
EventDispatcher::send_message(msg);
|
||||
}
|
||||
|
||||
BattinfoView::BattinfoView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
add_children({&labels,
|
||||
&labels_opt,
|
||||
&text_percent,
|
||||
&text_voltage,
|
||||
&text_current,
|
||||
&text_charge,
|
||||
&button_exit});
|
||||
|
||||
button_exit.on_select = [this, &nav](Button&) {
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
update_result();
|
||||
}
|
||||
|
||||
} // namespace ui
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_BATTINFO_H__
|
||||
#define __UI_BATTINFO_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
namespace ui {
|
||||
class BattinfoView : public View {
|
||||
public:
|
||||
BattinfoView(NavigationView& nav);
|
||||
void focus() override;
|
||||
std::string title() const override { return "Battery"; };
|
||||
|
||||
private:
|
||||
void update_result();
|
||||
void on_timer();
|
||||
NavigationView& nav_;
|
||||
uint16_t timer_period = 60;
|
||||
uint16_t timer_counter = 0;
|
||||
uint8_t percent = 0;
|
||||
uint16_t voltage = 0;
|
||||
int32_t current = 0;
|
||||
bool isCharging = false;
|
||||
|
||||
Labels labels{
|
||||
{{2 * 8, 1 * 16}, "Percent:", Color::light_grey()},
|
||||
{{2 * 8, 2 * 16}, "Voltage:", Color::light_grey()}};
|
||||
|
||||
Labels labels_opt{
|
||||
{{2 * 8, 3 * 16}, "Current:", Color::light_grey()},
|
||||
{{2 * 8, 4 * 16}, "Charge:", Color::light_grey()}};
|
||||
|
||||
Text text_percent{
|
||||
{13 * 8, 1 * 16, 10 * 16, 16},
|
||||
"-"};
|
||||
Text text_voltage{
|
||||
{13 * 8, 2 * 16, 10 * 16, 16},
|
||||
"-"};
|
||||
Text text_current{
|
||||
{13 * 8, 3 * 16, 10 * 16, 16},
|
||||
"-"};
|
||||
Text text_charge{
|
||||
{13 * 8, 4 * 16, 10 * 16, 16},
|
||||
"-"};
|
||||
|
||||
Button button_exit{
|
||||
{72, 17 * 16, 96, 32},
|
||||
"Back"};
|
||||
|
||||
MessageHandlerRegistration message_handler_frame_sync{
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
this->on_timer();
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif /*__UI_BATTINFO__*/
|
|
@ -42,7 +42,7 @@ DfuMenu::DfuMenu(NavigationView& nav)
|
|||
&text_info_line_9,
|
||||
&text_info_line_10});
|
||||
|
||||
if (portapack::battery_ads1110.isDetected()) {
|
||||
if (battery::BatteryManagement::isDetected()) {
|
||||
add_child(&voltage_label);
|
||||
add_child(&text_info_line_11);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ void DfuMenu::paint(Painter& painter) {
|
|||
size_t m0_fragmented_free_space = 0;
|
||||
const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space);
|
||||
|
||||
auto lines = (portapack::battery_ads1110.isDetected() ? 11 : 10) + 2;
|
||||
auto lines = (battery::BatteryManagement::isDetected() ? 11 : 10) + 2;
|
||||
|
||||
text_info_line_1.set(to_string_dec_uint(chCoreStatus(), 6));
|
||||
text_info_line_2.set(to_string_dec_uint(m0_fragmented_free_space, 6));
|
||||
|
@ -65,8 +65,8 @@ void DfuMenu::paint(Painter& painter) {
|
|||
text_info_line_8.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6));
|
||||
text_info_line_9.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
|
||||
text_info_line_10.set(to_string_dec_uint(chTimeNow() / 1000, 6));
|
||||
if (portapack::battery_ads1110.isDetected()) {
|
||||
text_info_line_11.set(to_string_decimal_padding(portapack::battery_ads1110.readVoltage(), 3, 6));
|
||||
if (battery::BatteryManagement::isDetected()) {
|
||||
text_info_line_11.set(to_string_decimal_padding((float)battery::BatteryManagement::getVoltage() / 1000.0, 3, 6));
|
||||
}
|
||||
|
||||
constexpr auto margin = 5;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 Furrtek
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -49,6 +50,7 @@ static const fs::path cxx_ext{u".C*"};
|
|||
static const fs::path png_ext{u".PNG"};
|
||||
static const fs::path bmp_ext{u".BMP"};
|
||||
static const fs::path rem_ext{u".REM"};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
namespace {
|
||||
|
@ -227,7 +229,7 @@ void FileManBaseView::load_directory_contents(const fs::path& dir_path) {
|
|||
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));
|
||||
text_current.set(current_path.empty() ? "/" : truncate(dir_path, 24));
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(dir_path, u"*")) {
|
||||
// Hide files starting with '.' (hidden / tmp).
|
||||
|
@ -237,6 +239,7 @@ 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().string(), (uint32_t)entry.size(), false});
|
||||
|
||||
} else if (fs::is_directory(entry.status())) {
|
||||
insert_sorted(entry_list, {entry.path().string(), 0, true});
|
||||
}
|
||||
|
@ -291,6 +294,8 @@ FileManBaseView::FileManBaseView(
|
|||
extension_filter{filter} {
|
||||
add_children({&labels,
|
||||
&text_current,
|
||||
&option_profile_switch,
|
||||
&label_profile,
|
||||
&button_exit});
|
||||
|
||||
button_exit.on_select = [this](Button&) {
|
||||
|
@ -313,6 +318,24 @@ FileManBaseView::FileManBaseView(
|
|||
pop_dir();
|
||||
};
|
||||
}
|
||||
|
||||
option_profile_switch.on_change = [this](size_t, uint8_t profiles) {
|
||||
switch (static_cast<DirProfiles>(profiles)) {
|
||||
case DirProfiles::User:
|
||||
jumping_between_profiles(current_path, DirProfiles::User);
|
||||
option_profile_switch.set_style(nullptr);
|
||||
|
||||
break;
|
||||
|
||||
case DirProfiles::System:
|
||||
jumping_between_profiles(current_path, DirProfiles::System);
|
||||
option_profile_switch.set_style(&Styles::red);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
reload_current();
|
||||
};
|
||||
}
|
||||
|
||||
void FileManBaseView::focus() {
|
||||
|
@ -324,8 +347,29 @@ void FileManBaseView::focus() {
|
|||
}
|
||||
|
||||
void FileManBaseView::push_dir(const fs::path& path) {
|
||||
// if you want it freely jump between profiles when picking files in your app, don't use this
|
||||
// , you should use push_fake_dir, which handle and call back the dir automatically
|
||||
if (path == parent_dir_path) {
|
||||
pop_dir();
|
||||
} else if (path == system_dir || path == apps_dir || path == firmware_dir) {
|
||||
nav_.push<ModalMessageView>(
|
||||
"Warning",
|
||||
"It is not recommended to\n"
|
||||
"modify system files,\n"
|
||||
"so you can easily\n"
|
||||
"update sdcard content.\n"
|
||||
"You can add all the custom\n"
|
||||
"files in root folders.\n"
|
||||
"Continue anyway?",
|
||||
YESNO,
|
||||
[this, path](bool choice) {
|
||||
if (choice) {
|
||||
current_path /= path;
|
||||
saved_index_stack.push_back(menu_view.highlighted_index());
|
||||
menu_view.set_highlighted(0);
|
||||
reload_current();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
current_path /= path;
|
||||
saved_index_stack.push_back(menu_view.highlighted_index());
|
||||
|
@ -334,14 +378,33 @@ void FileManBaseView::push_dir(const fs::path& path) {
|
|||
}
|
||||
}
|
||||
|
||||
void FileManBaseView::pop_dir() {
|
||||
if (saved_index_stack.empty())
|
||||
return;
|
||||
void FileManBaseView::push_fake_dir(const fs::path& path) {
|
||||
// the one this accepted is just a flag (e.g. CAPTURE, instead of /SYS/CAPTURE nor /CAPTURE), not real dir
|
||||
// after passing the flag here, this func will handle it automatically and make callback automatically
|
||||
fs::path first_level = path.extract_first_level();
|
||||
const fs::path default_mother_dir = user_dir;
|
||||
|
||||
current_path = current_path.parent_path();
|
||||
reload_current(true);
|
||||
menu_view.set_highlighted(saved_index_stack.back());
|
||||
saved_index_stack.pop_back();
|
||||
if (first_level != user_dir && first_level != system_dir) {
|
||||
current_path = default_mother_dir / path;
|
||||
saved_index_stack.push_back(
|
||||
menu_view.highlighted_index()); // TODO: do we really want to allow user to redirect to other dir tho?
|
||||
menu_view.set_highlighted(0);
|
||||
reload_current();
|
||||
}
|
||||
}
|
||||
|
||||
void FileManBaseView::pop_dir() {
|
||||
if (saved_index_stack.empty() && current_path == u"/") {
|
||||
return;
|
||||
} else if (saved_index_stack.empty()) {
|
||||
current_path = u"";
|
||||
reload_current();
|
||||
} else {
|
||||
current_path = current_path.parent_path();
|
||||
reload_current();
|
||||
menu_view.set_highlighted(saved_index_stack.back());
|
||||
saved_index_stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_extension(std::string t) {
|
||||
|
@ -388,8 +451,10 @@ void FileManBaseView::refresh_list() {
|
|||
}
|
||||
|
||||
menu_view.add_item(
|
||||
{entry_name.substr(0, max_filename_length) + std::string(21 - entry_name.length(), ' ') + size_str,
|
||||
ui::Color::yellow(),
|
||||
|
||||
{entry_name + std::string(21 - entry_name.length(), ' ') + size_str,
|
||||
(entry.path == system_dir || entry.path == apps_dir || entry.path == firmware_dir) ? Color::red() : Color::yellow(),
|
||||
|
||||
&bitmap_icon_dir,
|
||||
[this](KeyEvent key) {
|
||||
if (on_select_entry)
|
||||
|
@ -420,6 +485,31 @@ void FileManBaseView::reload_current(bool reset_pagination) {
|
|||
refresh_list();
|
||||
}
|
||||
|
||||
fs::path FileManBaseView::jumping_between_profiles(fs::path& path, DirProfiles profile) {
|
||||
fs::path first_level = path.extract_first_level();
|
||||
const fs::path null_path = u"";
|
||||
|
||||
if (profile == DirProfiles::User) {
|
||||
if (first_level == system_dir) { // /SYS/abcdef
|
||||
path = path.remove_first_level();
|
||||
} else if (first_level == null_path) { // /abcdef
|
||||
// this is for: after user redirected to other dir and then switch to user profile
|
||||
path = user_dir;
|
||||
}
|
||||
|
||||
} else if (profile == DirProfiles::System) {
|
||||
if (first_level == null_path && path != system_dir) { // /abcdef
|
||||
// the second argument is for preventing circuling enter system dir
|
||||
path = system_dir / path;
|
||||
} else if (first_level == system_dir) { // /SYS/abcdef
|
||||
// this is for: after user redirected to other dir and then switch to system profile
|
||||
path = system_dir;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
const FileManBaseView::file_assoc_t& FileManBaseView::get_assoc(
|
||||
const fs::path& ext) const {
|
||||
size_t index = 0;
|
||||
|
@ -547,7 +637,8 @@ void FileManagerView::refresh_widgets(const bool v) {
|
|||
button_paste.hidden(v);
|
||||
button_new_dir.hidden(v);
|
||||
button_new_file.hidden(v);
|
||||
|
||||
option_profile_switch.hidden(!v);
|
||||
label_profile.hidden(!v);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
|
@ -752,7 +843,8 @@ FileManagerView::FileManagerView(
|
|||
} else {
|
||||
text_date.set_style(&Styles::grey);
|
||||
if (selected_is_valid())
|
||||
text_date.set((is_directory(get_selected_full_path()) ? "Created " : "Modified ") + to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
|
||||
text_date.set((is_directory(get_selected_full_path()) ? "Created " : "Modified ") +
|
||||
to_string_FAT_timestamp(file_created_date(get_selected_full_path())));
|
||||
else
|
||||
text_date.set("");
|
||||
}
|
||||
|
@ -862,5 +954,4 @@ FileManagerView::FileManagerView(
|
|||
reload_current();
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
|
|
|
@ -48,6 +48,11 @@ enum class ClipboardMode : uint8_t {
|
|||
Copy
|
||||
};
|
||||
|
||||
enum class DirProfiles : uint8_t {
|
||||
User = 1,
|
||||
System = 2
|
||||
};
|
||||
|
||||
class FileManBaseView : public View {
|
||||
public:
|
||||
FileManBaseView(
|
||||
|
@ -59,6 +64,7 @@ class FileManBaseView : public View {
|
|||
void focus() override;
|
||||
std::string title() const override { return "Fileman"; };
|
||||
void push_dir(const std::filesystem::path& path);
|
||||
void push_fake_dir(const std::filesystem::path& path);
|
||||
|
||||
protected:
|
||||
uint32_t prev_highlight = 0;
|
||||
|
@ -95,6 +101,9 @@ class FileManBaseView : public View {
|
|||
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);
|
||||
|
||||
std::filesystem::path jumping_between_profiles(std::filesystem::path& path, DirProfiles profile);
|
||||
|
||||
const file_assoc_t& get_assoc(const std::filesystem::path& ext) const;
|
||||
|
||||
NavigationView& nav_;
|
||||
|
@ -106,6 +115,7 @@ class FileManBaseView : public View {
|
|||
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""};
|
||||
|
@ -116,10 +126,10 @@ class FileManBaseView : public View {
|
|||
bool show_hidden_files{false};
|
||||
|
||||
Labels labels{
|
||||
{{0, 0}, "Path:", Color::light_grey()}};
|
||||
{{0, 0}, "\u007F", Color::white()}};
|
||||
|
||||
Text text_current{
|
||||
{6 * 8, 0 * 8, 24 * 8, 16},
|
||||
{1 * 8, 0 * 8, 20 * 8, 16},
|
||||
"",
|
||||
};
|
||||
|
||||
|
@ -127,6 +137,16 @@ class FileManBaseView : public View {
|
|||
{0, 2 * 8, 240, 26 * 8},
|
||||
true};
|
||||
|
||||
OptionsField option_profile_switch{
|
||||
{11 * 8, 32 * 8},
|
||||
16,
|
||||
{{"User Directory ", 1},
|
||||
{"System Directory", 2}}};
|
||||
|
||||
Labels label_profile{
|
||||
{{0 * 8, 32 * 8}, "Profile: ", Color::light_grey()},
|
||||
{{9 * 8, 32 * 8}, "\u007F/", Color::white()}};
|
||||
|
||||
Button button_exit{
|
||||
{22 * 8, 34 * 8, 8 * 8, 32},
|
||||
"Exit"};
|
||||
|
|
|
@ -163,8 +163,8 @@ bool FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
|
|||
ui::Color::black());
|
||||
|
||||
painter.draw_string({12, 24}, this->nav_.style(), "This will take 15 seconds.");
|
||||
painter.draw_string({12, 64}, this->nav_.style(), "Please wait while LEDs RX");
|
||||
painter.draw_string({12, 84}, this->nav_.style(), "and TX are flashing.");
|
||||
painter.draw_string({12, 64}, this->nav_.style(), "Please wait while LED RX");
|
||||
painter.draw_string({12, 84}, this->nav_.style(), "is on and TX is flashing.");
|
||||
painter.draw_string({12, 124}, this->nav_.style(), "Device will then restart.");
|
||||
|
||||
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (path.length() + 1) * 2);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2023 Kyle Reed
|
||||
* copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -96,7 +97,11 @@ void FreqManBaseView::focus() {
|
|||
if (error_ == ERROR_ACCESS) {
|
||||
nav_.display_modal("Error", "File access error", ABORT);
|
||||
} else if (error_ == ERROR_NOFILES) {
|
||||
nav_.display_modal("Error", "No database files\nin /FREQMAN", ABORT);
|
||||
nav_.display_modal("Error",
|
||||
"No database files\nin either" +
|
||||
freqman_dir_user.string() +
|
||||
"\nor" + freqman_dir_resources.string(),
|
||||
ABORT);
|
||||
} else {
|
||||
options_category.focus();
|
||||
}
|
||||
|
@ -107,7 +112,15 @@ void FreqManBaseView::change_category(size_t new_index) {
|
|||
return;
|
||||
|
||||
current_category_index = new_index;
|
||||
if (!db_.open(get_freqman_path(current_category()))) {
|
||||
|
||||
current_is_system_item = false;
|
||||
if (!db_.open(get_freqman_path(current_category(), dir_profile::ProfileUser))) {
|
||||
current_is_system_item = true;
|
||||
}
|
||||
|
||||
if (current_is_system_item && // <<< this means that entered previus condition, it's a work around to save 1 byte of ram
|
||||
(!db_.open(
|
||||
get_freqman_path(current_category(), dir_profile::ProfileSystem)))) {
|
||||
error_ = ERROR_ACCESS;
|
||||
}
|
||||
|
||||
|
@ -117,15 +130,20 @@ void FreqManBaseView::change_category(size_t new_index) {
|
|||
void FreqManBaseView::refresh_categories() {
|
||||
OptionsField::options_t new_categories;
|
||||
|
||||
scan_root_files(
|
||||
freqman_dir, u"*.TXT", [&new_categories](const fs::path& path) {
|
||||
// Skip temp/hidden files.
|
||||
if (path.empty() || path.native()[0] == u'.')
|
||||
return;
|
||||
auto load_files = [&new_categories](const fs::path& dir) {
|
||||
scan_root_files(
|
||||
dir, u"*.TXT", [&new_categories](const fs::path& path) {
|
||||
// Skip temp/hidden files.
|
||||
if (path.empty() || path.native()[0] == u'.')
|
||||
return;
|
||||
|
||||
// The UI layer will truncate long file names when displaying.
|
||||
new_categories.emplace_back(path.stem().string(), new_categories.size());
|
||||
});
|
||||
// The UI layer will truncate long file names when displaying.
|
||||
new_categories.emplace_back(path.stem().string(), new_categories.size());
|
||||
});
|
||||
};
|
||||
|
||||
load_files(freqman_dir_resources);
|
||||
load_files(freqman_dir_user);
|
||||
|
||||
// Alphabetically sort the categories.
|
||||
std::sort(new_categories.begin(), new_categories.end(), [](auto& left, auto& right) {
|
||||
|
@ -163,9 +181,17 @@ FrequencySaveView::FrequencySaveView(
|
|||
|
||||
bind(field_description, entry_.description, nav);
|
||||
|
||||
button_save.on_select = [this, &nav](Button&) {
|
||||
db_.insert_entry(db_.entry_count(), entry_);
|
||||
nav_.pop();
|
||||
button_save.on_select = [this, &nav](Button&) { // TODO: don't list system category here
|
||||
if (current_is_system_item) {
|
||||
nav.display_modal("Forbid",
|
||||
"Can't save to\n"
|
||||
"system category.\n"
|
||||
"Please save to \n"
|
||||
"a user category.");
|
||||
} else {
|
||||
db_.insert_entry(db_.entry_count(), entry_);
|
||||
nav_.pop();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -238,6 +264,7 @@ void FrequencyManagerView::on_edit_desc() {
|
|||
}
|
||||
|
||||
void FrequencyManagerView::on_add_category() {
|
||||
ensure_directory(freqman_dir_user);
|
||||
temp_buffer_.clear();
|
||||
text_prompt(nav_, temp_buffer_, 20, [this](std::string& new_name) {
|
||||
if (!new_name.empty()) {
|
||||
|
@ -252,8 +279,8 @@ void FrequencyManagerView::on_del_category() {
|
|||
"Delete", "Delete " + current_category() + "\nAre you sure?", YESNO,
|
||||
[this](bool choice) {
|
||||
if (choice) {
|
||||
db_.close(); // Ensure file is closed.
|
||||
auto path = get_freqman_path(current_category());
|
||||
db_.close(); // Ensure file is closed.
|
||||
auto path = get_freqman_path(current_category(), dir_profile::ProfileUser); // only allow del user's
|
||||
delete_file(path);
|
||||
refresh_categories();
|
||||
}
|
||||
|
@ -285,6 +312,19 @@ void FrequencyManagerView::on_del_entry() {
|
|||
}
|
||||
});
|
||||
}
|
||||
bool FrequencyManagerView::forbid_delete_system_item_helper(NavigationView& nav) {
|
||||
// this is just a modal, however, it's been forbidden in those handler.
|
||||
|
||||
if (current_is_system_item) {
|
||||
nav.display_modal("Forbid",
|
||||
"Can't do that to \n"
|
||||
"system item.\n"
|
||||
"If you have to,\n"
|
||||
"do it with File Manager");
|
||||
}
|
||||
|
||||
return current_is_system_item;
|
||||
}
|
||||
|
||||
FrequencyManagerView::FrequencyManagerView(
|
||||
NavigationView& nav)
|
||||
|
@ -309,31 +349,44 @@ FrequencyManagerView::FrequencyManagerView(
|
|||
};
|
||||
|
||||
button_add_category.on_select = [this]() {
|
||||
// this one will only directly add to /FREQMAN
|
||||
on_add_category();
|
||||
};
|
||||
|
||||
button_del_category.on_select = [this]() {
|
||||
on_del_category();
|
||||
button_del_category.on_select = [this, &nav]() {
|
||||
if (!forbid_delete_system_item_helper(nav)) {
|
||||
on_del_category();
|
||||
}
|
||||
};
|
||||
|
||||
button_edit_entry.on_select = [this](Button&) {
|
||||
on_edit_entry();
|
||||
button_edit_entry.on_select = [this, &nav](Button&) {
|
||||
if (!forbid_delete_system_item_helper(nav)) {
|
||||
on_edit_entry();
|
||||
}
|
||||
};
|
||||
|
||||
button_edit_freq.on_select = [this](Button&) {
|
||||
on_edit_freq();
|
||||
button_edit_freq.on_select = [this, &nav](Button&) {
|
||||
if (!forbid_delete_system_item_helper(nav)) {
|
||||
on_edit_freq();
|
||||
}
|
||||
};
|
||||
|
||||
button_edit_desc.on_select = [this](Button&) {
|
||||
on_edit_desc();
|
||||
button_edit_desc.on_select = [this, &nav](Button&) {
|
||||
if (!forbid_delete_system_item_helper(nav)) {
|
||||
on_edit_desc();
|
||||
}
|
||||
};
|
||||
|
||||
button_add_entry.on_select = [this]() {
|
||||
on_add_entry();
|
||||
button_add_entry.on_select = [this, &nav]() {
|
||||
if (!forbid_delete_system_item_helper(nav)) {
|
||||
on_add_entry();
|
||||
}
|
||||
};
|
||||
|
||||
button_del_entry.on_select = [this]() {
|
||||
on_del_entry();
|
||||
button_del_entry.on_select = [this, &nav]() {
|
||||
if (!forbid_delete_system_item_helper(nav)) {
|
||||
on_del_entry();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2023 Kyle Reed
|
||||
* copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -123,6 +124,7 @@ class FrequencyManagerView : public FreqManBaseView {
|
|||
std::string title() const override { return "Freqman"; }
|
||||
|
||||
private:
|
||||
bool forbid_delete_system_item_helper(NavigationView& nav);
|
||||
std::string temp_buffer_{};
|
||||
|
||||
void on_edit_entry();
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#include "radio_state.hpp"
|
||||
#include "pocsag_app.hpp"
|
||||
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class FskRxLogger {
|
||||
|
@ -160,7 +162,7 @@ class FskxRxMainView : public View {
|
|||
RecordView record_view{
|
||||
{0 * 8, 4 * 16, 30 * 8, 1 * 16},
|
||||
u"FSKRX_????.C16",
|
||||
u"FSKRX",
|
||||
fskrx_dir_user,
|
||||
RecordView::FileType::RawS16,
|
||||
16384,
|
||||
3};
|
||||
|
|
|
@ -48,7 +48,7 @@ IQTrimView::IQTrimView(NavigationView& nav)
|
|||
|
||||
field_path.on_select = [this](TextField&) {
|
||||
auto open_view = nav_.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(captures_dir);
|
||||
open_view->push_fake_dir(captures_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
open_file(path);
|
||||
};
|
||||
|
|
|
@ -573,6 +573,8 @@ 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(looking_glass_dir / u"PRESETS.TXT");
|
||||
// TODO: this app originally ain't support user load list.
|
||||
// should support, but ain't have funtionality loss in sdcard refactor PR.
|
||||
presets_db.clear();
|
||||
|
||||
// Add the "Manual" entry.
|
||||
|
|
|
@ -131,7 +131,7 @@ void PlaylistView::open_file(bool prompt_save) {
|
|||
}
|
||||
|
||||
auto open_view = nav_.push<FileLoadView>(".PPL");
|
||||
open_view->push_dir(playlist_dir);
|
||||
open_view->push_fake_dir(playlist_dir);
|
||||
open_view->on_changed = [this](fs::path new_file_path) {
|
||||
on_file_changed(new_file_path);
|
||||
};
|
||||
|
@ -170,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(playlist_dir / u"PLAY_????.PPL");
|
||||
playlist_path_ = next_filename_matching_pattern(playlist_dir_user / u"PLAY_????.PPL");
|
||||
|
||||
// Hack around focus getting called by ctor before parent is set.
|
||||
if (parent())
|
||||
|
@ -388,7 +388,7 @@ PlaylistView::PlaylistView(
|
|||
&waterfall,
|
||||
});
|
||||
|
||||
ensure_directory(playlist_dir);
|
||||
ensure_fake_directories(playlist_dir);
|
||||
waterfall.show_audio_spectrum_view(false);
|
||||
|
||||
field_frequency.set_value(transmitter_model.target_frequency());
|
||||
|
@ -411,7 +411,7 @@ PlaylistView::PlaylistView(
|
|||
if (is_active())
|
||||
return;
|
||||
auto open_view = nav_.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(captures_dir);
|
||||
open_view->push_fake_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", audio_dir,
|
||||
u"AUTO_AUDIO", audio_dir_user,
|
||||
RecordView::FileType::WAV, 4096, 4);
|
||||
record_view->set_filename_date_frequency(true);
|
||||
record_view->set_auto_trim(false);
|
||||
|
@ -680,7 +680,7 @@ ReconView::ReconView(NavigationView& nav)
|
|||
open_view->on_changed = [this](std::vector<std::string> result) {
|
||||
input_file = result[0];
|
||||
output_file = result[1];
|
||||
freq_file_path = get_freqman_path(output_file).string();
|
||||
freq_file_path = get_freqman_path(output_file, dir_profile::ProfileUser).string();
|
||||
|
||||
load_persisted_settings();
|
||||
ui_settings.save();
|
||||
|
@ -731,7 +731,7 @@ ReconView::ReconView(NavigationView& nav)
|
|||
file_name.set("=>");
|
||||
|
||||
// Loading input and output file from settings
|
||||
freq_file_path = get_freqman_path(output_file).string();
|
||||
freq_file_path = get_freqman_path(output_file, dir_profile::ProfileUser).string();
|
||||
|
||||
field_recon_match_mode.set_selected_index(recon_match_mode);
|
||||
field_squelch.set_value(squelch);
|
||||
|
@ -790,7 +790,10 @@ void ReconView::frequency_file_load() {
|
|||
.load_ranges = load_ranges,
|
||||
.load_hamradios = load_hamradios,
|
||||
.load_repeaters = load_repeaters};
|
||||
if (!load_freqman_file(file_input, frequency_list, options) || frequency_list.empty()) {
|
||||
if (
|
||||
(!load_freqman_file(file_input, frequency_list, options, dir_profile::ProfileSystem) &&
|
||||
!load_freqman_file(file_input, frequency_list, options, dir_profile::ProfileUser)) ||
|
||||
frequency_list.empty()) {
|
||||
file_name.set_style(&Styles::red);
|
||||
desc_cycle.set("...empty file...");
|
||||
frequency_list.clear();
|
||||
|
@ -1170,18 +1173,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", captures_dir,
|
||||
u"RECON_REPEAT.C16", captures_dir_user,
|
||||
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", captures_dir,
|
||||
u"AUTO_RAW", captures_dir_user,
|
||||
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", audio_dir,
|
||||
u"AUTO_AUDIO", audio_dir_user,
|
||||
RecordView::FileType::WAV, 4096, 4);
|
||||
record_view->set_filename_date_frequency(true);
|
||||
}
|
||||
|
|
|
@ -182,6 +182,7 @@ 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";
|
||||
// tempnote: path const var removed
|
||||
const size_t repeat_read_size{16384};
|
||||
const size_t repeat_buffer_count{3};
|
||||
int8_t repeat_cur_rep = 0;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -57,26 +58,30 @@ ReconSetupViewMain::ReconSetupViewMain(NavigationView& nav, Rect parent_rect, st
|
|||
|
||||
button_input_file.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".TXT");
|
||||
open_view->push_dir(freqman_dir);
|
||||
open_view->push_fake_dir(freqman_dir); // the argu that push fake dir accepted is just a flag, so can safely hard coded
|
||||
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
|
||||
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
|
||||
if ((new_file_path.native().find((u"/" / freqman_dir_resources).native()) == 0) || new_file_path.native().find((freqman_dir_user).native()) == 0) {
|
||||
_input_file = new_file_path.stem().string();
|
||||
text_input_file.set(_input_file);
|
||||
} else {
|
||||
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
|
||||
nav.display_modal("LOAD ERROR", "A valid file from\n" +
|
||||
freqman_dir_user.string() +
|
||||
"\nor\n" +
|
||||
freqman_dir_resources.string() +
|
||||
"\ndirectories is\nrequired.");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
button_choose_output_file.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".TXT");
|
||||
open_view->push_dir(freqman_dir);
|
||||
open_view->push_fake_dir(freqman_dir); // the argu that push fake dir accepted is just a flag, so can safely hard coded
|
||||
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
|
||||
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
|
||||
if (new_file_path.native().find((freqman_dir_user).native()) == 0) {
|
||||
_output_file = new_file_path.stem().string();
|
||||
button_choose_output_name.set_text(_output_file);
|
||||
} else {
|
||||
nav.display_modal("SAVE ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
|
||||
nav.display_modal("SAVE ERROR", "A valid file from\n" + freqman_dir_user.string() + "\ndirectories is\nrequired.");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
// screen size helper
|
||||
#define SCREEN_W 240
|
||||
//#define SCREEN_H 320
|
||||
// #define SCREEN_H 320
|
||||
|
||||
// recon settings nb params
|
||||
#define RECON_SETTINGS_NB_PARAMS 7
|
||||
|
|
|
@ -247,7 +247,7 @@ RemoteEntryEditView::RemoteEntryEditView(
|
|||
|
||||
field_path.on_select = [this, &nav](TextField&) {
|
||||
auto open_view = nav.push<FileLoadView>(".C*");
|
||||
open_view->push_dir(captures_dir);
|
||||
open_view->push_fake_dir(captures_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
load_path(std::move(path));
|
||||
refresh_ui();
|
||||
|
@ -356,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(remotes_dir);
|
||||
ensure_fake_directories(remotes_dir);
|
||||
|
||||
// Load the previously loaded remote if exists.
|
||||
if (!load_remote(settings_.remote_path))
|
||||
|
@ -528,7 +528,7 @@ void RemoteView::new_remote() {
|
|||
|
||||
void RemoteView::open_remote() {
|
||||
auto open_view = nav_.push<FileLoadView>(".REM");
|
||||
open_view->push_dir(remotes_dir);
|
||||
open_view->push_fake_dir(remotes_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
save_remote();
|
||||
load_remote(std::move(path));
|
||||
|
@ -539,7 +539,7 @@ void RemoteView::open_remote() {
|
|||
void RemoteView::init_remote() {
|
||||
model_ = {"<Unnamed Remote>", {}};
|
||||
reset_buttons();
|
||||
set_remote_path(next_filename_matching_pattern(remotes_dir / u"REMOTE_????.REM"));
|
||||
set_remote_path(next_filename_matching_pattern(remotes_dir_user / u"REMOTE_????.REM"));
|
||||
set_needs_save(false);
|
||||
|
||||
if (remote_path_.empty())
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2018 Furrtek
|
||||
* Copyright (C) 2023 Mark Thompson
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -336,13 +337,13 @@ ScannerView::ScannerView(
|
|||
// Button to load a Freqman file.
|
||||
button_load.on_select = [this, &nav](Button&) {
|
||||
auto open_view = nav.push<FileLoadView>(".TXT");
|
||||
open_view->push_dir(freqman_dir);
|
||||
open_view->push_fake_dir(freqman_dir); // the argu that push fake dir accepted is just a flag, so can safely hard coded
|
||||
open_view->on_changed = [this, &nav](std::filesystem::path new_file_path) {
|
||||
if (new_file_path.native().find((u"/" / freqman_dir).native()) == 0) {
|
||||
if ((new_file_path.native().find((u"/" / freqman_dir_resources).native()) == 0) || new_file_path.native().find((freqman_dir_user).native()) == 0) {
|
||||
scan_pause();
|
||||
frequency_file_load(new_file_path);
|
||||
} else {
|
||||
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
|
||||
nav.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directories is\nrequired.");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -491,7 +492,7 @@ ScannerView::ScannerView(
|
|||
// Button to add current frequency (found during Search) to the Scan Frequency List
|
||||
button_add.on_select = [this](Button&) {
|
||||
FreqmanDB db;
|
||||
if (db.open(get_freqman_path(freqman_file), /*create*/ true)) {
|
||||
if (db.open(get_freqman_path(freqman_file, dir_profile::ProfileUser), /*create*/ true)) {
|
||||
freqman_entry entry{
|
||||
.frequency_a = current_frequency,
|
||||
.type = freqman_type::Single,
|
||||
|
@ -537,7 +538,7 @@ ScannerView::ScannerView(
|
|||
receiver_model.set_squelch_level(0);
|
||||
|
||||
// LOAD FREQUENCIES
|
||||
frequency_file_load(get_freqman_path(freqman_file));
|
||||
frequency_file_load(get_freqman_path(freqman_file, dir_profile::ProfileUser));
|
||||
}
|
||||
|
||||
void ScannerView::frequency_file_load(const fs::path& path) {
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
#define SCANNER_SLEEP_MS 50 // ms that Scanner Thread sleeps per loop
|
||||
#define STATISTICS_UPDATES_PER_SEC 10
|
||||
#define MAX_FREQ_LOCK 10 //# of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spurious
|
||||
#define MAX_FREQ_LOCK 10 // # of 50ms cycles scanner locks into freq when signal detected, to verify signal is not spurious
|
||||
|
||||
namespace ui {
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (C) 2023 Kyle Reed
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -329,6 +329,10 @@ SetUIView::SetUIView(NavigationView& nav) {
|
|||
if (audio::speaker_disable_supported()) {
|
||||
add_child(&toggle_speaker);
|
||||
}
|
||||
if (battery::BatteryManagement::isDetected()) {
|
||||
add_child(&toggle_battery_icon);
|
||||
add_child(&toggle_battery_text);
|
||||
}
|
||||
|
||||
checkbox_disable_touchscreen.set_value(pmem::disable_touchscreen());
|
||||
checkbox_showsplash.set_value(pmem::config_splash());
|
||||
|
@ -355,6 +359,8 @@ SetUIView::SetUIView(NavigationView& nav) {
|
|||
toggle_speaker.set_value(!pmem::ui_hide_speaker());
|
||||
toggle_mute.set_value(!pmem::ui_hide_mute());
|
||||
toggle_fake_brightness.set_value(!pmem::ui_hide_fake_brightness());
|
||||
toggle_battery_icon.set_value(!pmem::ui_hide_battery_icon());
|
||||
toggle_battery_text.set_value(!pmem::ui_hide_numeric_battery());
|
||||
toggle_sd_card.set_value(!pmem::ui_hide_sd_card());
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
|
@ -382,6 +388,8 @@ SetUIView::SetUIView(NavigationView& nav) {
|
|||
pmem::set_ui_hide_speaker(!toggle_speaker.value());
|
||||
pmem::set_ui_hide_mute(!toggle_mute.value());
|
||||
pmem::set_ui_hide_fake_brightness(!toggle_fake_brightness.value());
|
||||
pmem::set_ui_hide_battery_icon(!toggle_battery_icon.value());
|
||||
pmem::set_ui_hide_numeric_battery(!toggle_battery_text.value());
|
||||
pmem::set_ui_hide_sd_card(!toggle_sd_card.value());
|
||||
send_system_refresh();
|
||||
|
||||
|
@ -807,6 +815,7 @@ SetMenuColorView::SetMenuColorView(NavigationView& nav) {
|
|||
&field_green_level,
|
||||
&field_blue_level,
|
||||
&button_save,
|
||||
&button_reset,
|
||||
&button_cancel});
|
||||
|
||||
button_sample.set_focusable(false);
|
||||
|
@ -824,6 +833,13 @@ SetMenuColorView::SetMenuColorView(NavigationView& nav) {
|
|||
field_green_level.on_change = color_changed_fn;
|
||||
field_blue_level.on_change = color_changed_fn;
|
||||
|
||||
button_reset.on_select = [&nav, this](Button&) {
|
||||
field_red_level.set_value(127);
|
||||
field_green_level.set_value(127);
|
||||
field_blue_level.set_value(127);
|
||||
set_dirty();
|
||||
};
|
||||
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
Color c = Color(field_red_level.value(), field_green_level.value(), field_blue_level.value());
|
||||
pmem::set_menu_color(c);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (C) 2023 Kyle Reed
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -323,37 +323,45 @@ class SetUIView : public View {
|
|||
};
|
||||
|
||||
ImageToggle toggle_camera{
|
||||
{6 * 8, 14 * 16 + 2, 16, 16},
|
||||
{2 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_camera};
|
||||
|
||||
ImageToggle toggle_sleep{
|
||||
{8 * 8, 14 * 16 + 2, 16, 16},
|
||||
{4 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_sleep};
|
||||
|
||||
ImageToggle toggle_stealth{
|
||||
{10 * 8, 14 * 16 + 2, 16, 16},
|
||||
{6 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_stealth};
|
||||
|
||||
ImageToggle toggle_converter{
|
||||
{12 * 8, 14 * 16 + 2, 16, 16},
|
||||
{8 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_upconvert};
|
||||
|
||||
ImageToggle toggle_bias_tee{
|
||||
{14 * 8, 14 * 16 + 2, 16, 16},
|
||||
{10 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_biast_off};
|
||||
|
||||
ImageToggle toggle_clock{
|
||||
{16 * 8, 14 * 16 + 2, 8, 16},
|
||||
{12 * 8, 14 * 16 + 2, 8, 16},
|
||||
&bitmap_icon_clk_ext};
|
||||
|
||||
ImageToggle toggle_mute{
|
||||
{17 * 8, 14 * 16 + 2, 16, 16},
|
||||
{13 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_speaker_and_headphones_mute};
|
||||
|
||||
ImageToggle toggle_speaker{
|
||||
{19 * 8, 14 * 16 + 2, 16, 16},
|
||||
{15 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_speaker_mute};
|
||||
|
||||
ImageToggle toggle_battery_icon{
|
||||
{17 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_batt_icon};
|
||||
|
||||
ImageToggle toggle_battery_text{
|
||||
{19 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_batt_text};
|
||||
|
||||
ImageToggle toggle_fake_brightness{
|
||||
{21 * 8, 14 * 16 + 2, 16, 16},
|
||||
&bitmap_icon_brightness};
|
||||
|
@ -789,6 +797,11 @@ class SetMenuColorView : public View {
|
|||
' ',
|
||||
};
|
||||
|
||||
Button button_reset{
|
||||
{2 * 8, 13 * 16, 12 * 8, 32},
|
||||
"Reset",
|
||||
};
|
||||
|
||||
Button button_save{
|
||||
{2 * 8, 16 * 16, 12 * 8, 32},
|
||||
"Save"};
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace fs = std::filesystem;
|
|||
|
||||
namespace ui {
|
||||
|
||||
const std::filesystem::path splash_dot_bmp{u"/splash.bmp"};
|
||||
const std::filesystem::path current_using_splash_image{u"/splash.bmp"};
|
||||
|
||||
ScreenshotViewer::ScreenshotViewer(
|
||||
NavigationView& nav,
|
||||
|
@ -108,8 +108,8 @@ SplashViewer::SplashViewer(
|
|||
|
||||
bool SplashViewer::on_key(const KeyEvent key) {
|
||||
if (valid_image && key == KeyEvent::Right) {
|
||||
delete_file(splash_dot_bmp);
|
||||
copy_file(path_, splash_dot_bmp);
|
||||
delete_file(current_using_splash_image);
|
||||
copy_file(path_, current_using_splash_image);
|
||||
}
|
||||
|
||||
nav_.pop();
|
||||
|
@ -125,7 +125,7 @@ void SplashViewer::paint(Painter& painter) {
|
|||
}
|
||||
|
||||
// Show option to set splash screen if it's not already the splash screen
|
||||
if (!path_iequal(path_, splash_dot_bmp)) {
|
||||
if (!path_iequal(path_, current_using_splash_image)) {
|
||||
painter.draw_string({0, 0}, Styles::white, "*RIGHT BUTTON UPDATES SPLASH*");
|
||||
valid_image = true;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
namespace ui {
|
||||
|
||||
extern const std::filesystem::path splash_dot_bmp;
|
||||
extern const std::filesystem::path current_using_splash_image;
|
||||
|
||||
class ScreenshotViewer : public View {
|
||||
public:
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "portapack.hpp"
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
@ -35,7 +36,13 @@ namespace ui {
|
|||
|
||||
void SSTVTXView::focus() {
|
||||
if (file_error)
|
||||
nav_.display_modal("No files", "No valid bitmaps\nin /sstv directory.", ABORT);
|
||||
nav_.display_modal("No files",
|
||||
"No valid bitmaps\nin either" +
|
||||
sstv_dir.string() +
|
||||
"\nor\n" +
|
||||
sstv_dir_resources.string() +
|
||||
"directory.",
|
||||
ABORT);
|
||||
else
|
||||
options_bitmaps.focus();
|
||||
}
|
||||
|
@ -172,7 +179,10 @@ void SSTVTXView::start_tx() {
|
|||
}
|
||||
|
||||
void SSTVTXView::on_bitmap_changed(const size_t index) {
|
||||
bmp_file.open("/sstv/" + bitmaps[index].string());
|
||||
auto open_system_dir = bmp_file.open(sstv_dir_user + u"/" + bitmaps[index].string());
|
||||
if (!open_system_dir->ok()) {
|
||||
bmp_file.open(sstv_dir_resources + u"/" + bitmaps[index].string());
|
||||
}
|
||||
bmp_file.read(&bmp_header, sizeof(bmp_header));
|
||||
set_dirty();
|
||||
}
|
||||
|
@ -200,31 +210,47 @@ SSTVTXView::SSTVTXView(
|
|||
NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
std::vector<std::filesystem::path> file_list;
|
||||
std::filesystem::path file_list_index[2];
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t bitmap_options;
|
||||
options_t mode_options;
|
||||
uint32_t c;
|
||||
|
||||
// Search for valid bitmaps
|
||||
file_list = scan_root_files(u"/sstv", u"*.bmp");
|
||||
if (!file_list.size()) {
|
||||
file_error = true;
|
||||
return;
|
||||
}
|
||||
for (const auto& file_name : file_list) {
|
||||
if (!bmp_file.open("/sstv/" + file_name.string()).is_valid()) {
|
||||
bmp_file.read(&bmp_header, sizeof(bmp_header));
|
||||
if ((bmp_header.signature == 0x4D42) && // "BM"
|
||||
(bmp_header.width == 320) && // Must be exactly 320x256 pixels for now
|
||||
(bmp_header.height == 256) &&
|
||||
(bmp_header.planes == 1) &&
|
||||
(bmp_header.bpp == 24) && // 24 bpp only
|
||||
(bmp_header.compression == 0)) { // No compression
|
||||
bitmaps.push_back(file_name);
|
||||
file_list_index[0] = sstv_dir_user;
|
||||
file_list_index[1] = u"/" + sstv_dir_resources;
|
||||
|
||||
bool found_files = false;
|
||||
|
||||
for (const auto& now_path : file_list_index) {
|
||||
file_list = scan_root_files(now_path, u"*.bmp");
|
||||
|
||||
if (!file_list.size()) {
|
||||
continue; // Skip to the next path if no files found
|
||||
}
|
||||
|
||||
found_files = true; // Mark that we found some files
|
||||
|
||||
for (const auto& file_name : file_list) {
|
||||
if (!bmp_file.open(now_path / file_name).is_valid()) {
|
||||
bmp_file.read(&bmp_header, sizeof(bmp_header));
|
||||
if ((bmp_header.signature == 0x4D42) && // "BM"
|
||||
(bmp_header.width == 320) && // Must be exactly 320x256 pixels for now
|
||||
(bmp_header.height == 256) &&
|
||||
(bmp_header.planes == 1) &&
|
||||
(bmp_header.bpp == 24) && // 24 bpp only
|
||||
(bmp_header.compression == 0)) { // No compression
|
||||
bitmaps.push_back(file_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_files) {
|
||||
file_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bitmaps.size()) {
|
||||
file_error = true;
|
||||
return;
|
||||
|
|
|
@ -80,8 +80,14 @@ class SSTVTXView : public View {
|
|||
void prepare_scanline();
|
||||
|
||||
Labels labels{
|
||||
{{1 * 8, 1 * 8}, "File:", Color::light_grey()},
|
||||
{{1 * 8, 3 * 8}, "Mode:", Color::light_grey()}};
|
||||
{{1 * 8, 1 * 8}, "Profile:", Color::light_grey()},
|
||||
{{1 * 8, 3 * 8}, "File:", Color::light_grey()},
|
||||
{{1 * 8, 5 * 8}, "Mode:", Color::light_grey()}};
|
||||
|
||||
OptionsField options_profile{
|
||||
{6 * 8, 1 * 8},
|
||||
16,
|
||||
{}};
|
||||
|
||||
OptionsField options_bitmaps{
|
||||
{6 * 8, 1 * 8},
|
||||
|
|
|
@ -144,7 +144,6 @@ WhipCalcView::WhipCalcView(NavigationView& nav)
|
|||
void WhipCalcView::load_antenna_db() {
|
||||
File antennas_file;
|
||||
auto error = antennas_file.open(whipcalc_dir / u"ANTENNAS.TXT");
|
||||
|
||||
if (error)
|
||||
return;
|
||||
|
||||
|
|
|
@ -2447,6 +2447,82 @@ static constexpr Bitmap bitmap_icon_camera{
|
|||
{16, 16},
|
||||
bitmap_icon_camera_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_batt_icon_data[] = {
|
||||
0xC0,
|
||||
0x03,
|
||||
0xC0,
|
||||
0x03,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x10,
|
||||
0x08,
|
||||
0x10,
|
||||
0x08,
|
||||
0x10,
|
||||
0x08,
|
||||
0x10,
|
||||
0x08,
|
||||
0x10,
|
||||
0x08,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_batt_icon{
|
||||
{16, 16},
|
||||
bitmap_icon_batt_icon_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_batt_text_data[] = {
|
||||
0x00,
|
||||
0x00,
|
||||
0x30,
|
||||
0x06,
|
||||
0x48,
|
||||
0x09,
|
||||
0x48,
|
||||
0x09,
|
||||
0x70,
|
||||
0x0E,
|
||||
0x40,
|
||||
0x08,
|
||||
0x30,
|
||||
0x06,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x48,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x10,
|
||||
0x00,
|
||||
0x48,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_batt_text{
|
||||
{16, 16},
|
||||
bitmap_icon_batt_text_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_tools_wipesd_data[] = {
|
||||
0xF0,
|
||||
0x3F,
|
||||
|
|
|
@ -80,6 +80,10 @@ set(EXTCPPSRC
|
|||
#wardrivemap
|
||||
external/wardrivemap/main.cpp
|
||||
external/wardrivemap/ui_wardrivemap.cpp
|
||||
|
||||
#tpmsrx
|
||||
external/tpmsrx/main.cpp
|
||||
external/tpmsrx/tpms_app.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
|
@ -102,4 +106,5 @@ set(EXTAPPLIST
|
|||
foxhunt_rx
|
||||
audio_test
|
||||
wardrivemap
|
||||
tpmsrx
|
||||
)
|
||||
|
|
|
@ -42,6 +42,7 @@ MEMORY
|
|||
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
|
||||
ram_external_app_tpmsrx(rwx) : org = 0xADC30000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
|
@ -160,4 +161,10 @@ SECTIONS
|
|||
*(*ui*external_app*wardrivemap*);
|
||||
} > ram_external_app_wardrivemap
|
||||
|
||||
.external_app_tpmsrx : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_tpmsrx.application_information));
|
||||
*(*ui*external_app*tpmsrx*);
|
||||
} > ram_external_app_tpmsrx
|
||||
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ SpectrumInputImageView::SpectrumInputImageView(NavigationView& nav) {
|
|||
|
||||
ensure_directory(spectrum_dir);
|
||||
open_view->push_dir(spectrum_dir);
|
||||
// tempnewnote: const var removed
|
||||
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
this->file = new_file_path.string();
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "tpms_app.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::tpmsrx {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<TPMSAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::tpmsrx
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_tpmsrx.application_information"), used)) application_information_t _application_information_tpmsrx = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000, // will be filled at compile time
|
||||
/*.externalAppEntry = */ ui::external_app::tpmsrx::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "TPMS RX",
|
||||
/*.bitmap_data = */ {
|
||||
0xC0,
|
||||
0x03,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x18,
|
||||
0x18,
|
||||
0xEC,
|
||||
0x37,
|
||||
0x36,
|
||||
0x6D,
|
||||
0x3A,
|
||||
0x59,
|
||||
0x4B,
|
||||
0xD5,
|
||||
0x8B,
|
||||
0xD3,
|
||||
0xCB,
|
||||
0xD1,
|
||||
0xAB,
|
||||
0xD2,
|
||||
0x9A,
|
||||
0x5C,
|
||||
0xB6,
|
||||
0x6C,
|
||||
0xEC,
|
||||
0x37,
|
||||
0x18,
|
||||
0x18,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xC0,
|
||||
0x03,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::RX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_tpms */ {'P', 'T', 'P', 'M'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
|
@ -34,15 +34,15 @@ using namespace portapack;
|
|||
|
||||
namespace pmem = portapack::persistent_memory;
|
||||
|
||||
namespace tpms {
|
||||
namespace ui::external_app::tpmsrx {
|
||||
|
||||
namespace format {
|
||||
|
||||
std::string type(Reading::Type type) {
|
||||
std::string type(tpms::Reading::Type type) {
|
||||
return to_string_dec_uint(toUType(type), 2);
|
||||
}
|
||||
|
||||
std::string id(TransponderID id) {
|
||||
std::string id(tpms::TransponderID id) {
|
||||
return to_string_hex(id.value(), 8);
|
||||
}
|
||||
|
||||
|
@ -54,17 +54,17 @@ std::string temperature(Temperature temperature) {
|
|||
return to_string_dec_int(units_fahr ? temperature.fahrenheit() : temperature.celsius(), 3);
|
||||
}
|
||||
|
||||
std::string flags(Flags flags) {
|
||||
std::string flags(tpms::Flags flags) {
|
||||
return to_string_hex(flags, 2);
|
||||
}
|
||||
|
||||
static std::string signal_type(SignalType signal_type) {
|
||||
static std::string signal_type(tpms::SignalType signal_type) {
|
||||
switch (signal_type) {
|
||||
case SignalType::FSK_19k2_Schrader:
|
||||
case tpms::SignalType::FSK_19k2_Schrader:
|
||||
return "FSK 38400 19200 Schrader";
|
||||
case SignalType::OOK_8k192_Schrader:
|
||||
case tpms::SignalType::OOK_8k192_Schrader:
|
||||
return "OOK - 8192 Schrader";
|
||||
case SignalType::OOK_8k4_Schrader:
|
||||
case tpms::SignalType::OOK_8k4_Schrader:
|
||||
return "OOK - 8400 Schrader";
|
||||
default:
|
||||
return "- - - -";
|
||||
|
@ -73,15 +73,13 @@ static std::string signal_type(SignalType signal_type) {
|
|||
|
||||
} /* namespace format */
|
||||
|
||||
} /* namespace tpms */
|
||||
|
||||
void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_frequency) {
|
||||
const auto hex_formatted = packet.symbols_formatted();
|
||||
|
||||
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
|
||||
const auto target_frequency_str = to_string_dec_uint(target_frequency, 10);
|
||||
|
||||
std::string entry = target_frequency_str + " " + tpms::format::signal_type(packet.signal_type()) + " " + hex_formatted.data + "/" + hex_formatted.errors;
|
||||
std::string entry = target_frequency_str + " " + ui::external_app::tpmsrx::format::signal_type(packet.signal_type()) + " " + hex_formatted.data + "/" + hex_formatted.errors;
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
}
|
||||
|
||||
|
@ -101,52 +99,9 @@ void TPMSRecentEntry::update(const tpms::Reading& reading) {
|
|||
}
|
||||
}
|
||||
|
||||
namespace ui {
|
||||
|
||||
template <>
|
||||
void RecentEntriesTable<TPMSRecentEntries>::draw(
|
||||
const Entry& entry,
|
||||
const Rect& target_rect,
|
||||
Painter& painter,
|
||||
const Style& style) {
|
||||
std::string line = tpms::format::type(entry.type) + " " + tpms::format::id(entry.id);
|
||||
|
||||
if (entry.last_pressure.is_valid()) {
|
||||
line += " " + tpms::format::pressure(entry.last_pressure.value());
|
||||
} else {
|
||||
line +=
|
||||
" "
|
||||
" ";
|
||||
}
|
||||
|
||||
if (entry.last_temperature.is_valid()) {
|
||||
line += " " + tpms::format::temperature(entry.last_temperature.value());
|
||||
} else {
|
||||
line +=
|
||||
" "
|
||||
" ";
|
||||
}
|
||||
|
||||
if (entry.received_count > 999) {
|
||||
line += " +++";
|
||||
} else {
|
||||
line += " " + to_string_dec_uint(entry.received_count, 3);
|
||||
}
|
||||
|
||||
if (entry.last_flags.is_valid()) {
|
||||
line += " " + tpms::format::flags(entry.last_flags.value());
|
||||
} else {
|
||||
line +=
|
||||
" "
|
||||
" ";
|
||||
}
|
||||
|
||||
line.resize(target_rect.width() / 8, ' ');
|
||||
painter.draw_string(target_rect.location(), style, line);
|
||||
}
|
||||
|
||||
TPMSAppView::TPMSAppView(NavigationView&) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_tpms);
|
||||
// baseband::run_image(portapack::spi_flash::image_tag_tpms);
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
|
||||
add_children({&rssi,
|
||||
&field_volume,
|
||||
|
@ -167,16 +122,16 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
|||
options_band.set_by_value(receiver_model.target_frequency());
|
||||
|
||||
options_pressure.on_change = [this](size_t, int32_t i) {
|
||||
tpms::format::units_psi = (bool)i;
|
||||
format::units_psi = (bool)i;
|
||||
update_view();
|
||||
};
|
||||
options_pressure.set_selected_index(tpms::format::units_psi, true);
|
||||
options_pressure.set_selected_index(format::units_psi, true);
|
||||
|
||||
options_temperature.on_change = [this](size_t, int32_t i) {
|
||||
tpms::format::units_fahr = (bool)i;
|
||||
format::units_fahr = (bool)i;
|
||||
update_view();
|
||||
};
|
||||
options_temperature.set_selected_index(tpms::format::units_fahr, true);
|
||||
options_temperature.set_selected_index(format::units_fahr, true);
|
||||
|
||||
logger = std::make_unique<TPMSLogger>();
|
||||
if (logger) {
|
||||
|
@ -234,4 +189,50 @@ void TPMSAppView::on_show_list() {
|
|||
recent_entries_view.focus();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::tpmsrx
|
||||
|
||||
namespace ui {
|
||||
|
||||
template <>
|
||||
void RecentEntriesTable<ui::external_app::tpmsrx::TPMSRecentEntries>::draw(
|
||||
const Entry& entry,
|
||||
const Rect& target_rect,
|
||||
Painter& painter,
|
||||
const Style& style) {
|
||||
std::string line = ui::external_app::tpmsrx::format::type(entry.type) + " " + ui::external_app::tpmsrx::format::id(entry.id);
|
||||
|
||||
if (entry.last_pressure.is_valid()) {
|
||||
line += " " + ui::external_app::tpmsrx::format::pressure(entry.last_pressure.value());
|
||||
} else {
|
||||
line +=
|
||||
" "
|
||||
" ";
|
||||
}
|
||||
|
||||
if (entry.last_temperature.is_valid()) {
|
||||
line += " " + ui::external_app::tpmsrx::format::temperature(entry.last_temperature.value());
|
||||
} else {
|
||||
line +=
|
||||
" "
|
||||
" ";
|
||||
}
|
||||
|
||||
if (entry.received_count > 999) {
|
||||
line += " +++";
|
||||
} else {
|
||||
line += " " + to_string_dec_uint(entry.received_count, 3);
|
||||
}
|
||||
|
||||
if (entry.last_flags.is_valid()) {
|
||||
line += " " + ui::external_app::tpmsrx::format::flags(entry.last_flags.value());
|
||||
} else {
|
||||
line +=
|
||||
" "
|
||||
" ";
|
||||
}
|
||||
|
||||
line.resize(target_rect.width() / 8, ' ');
|
||||
painter.draw_string(target_rect.location(), style, line);
|
||||
}
|
||||
|
||||
} // namespace ui
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
#include "tpms_packet.hpp"
|
||||
|
||||
namespace tpms {
|
||||
namespace ui::external_app::tpmsrx {
|
||||
|
||||
namespace format {
|
||||
|
||||
|
@ -47,12 +47,6 @@ static bool units_fahr{false};
|
|||
|
||||
} /* namespace format */
|
||||
|
||||
} /* namespace tpms */
|
||||
|
||||
namespace std {
|
||||
|
||||
} /* namespace std */
|
||||
|
||||
struct TPMSRecentEntry {
|
||||
using Key = std::pair<tpms::Reading::Type, tpms::TransponderID>;
|
||||
|
||||
|
@ -94,8 +88,6 @@ class TPMSLogger {
|
|||
LogFile log_file{};
|
||||
};
|
||||
|
||||
namespace ui {
|
||||
|
||||
using TPMSRecentEntriesView = RecentEntriesView<TPMSRecentEntries>;
|
||||
|
||||
class TPMSAppView : public View {
|
||||
|
@ -115,16 +107,18 @@ class TPMSAppView : public View {
|
|||
|
||||
private:
|
||||
RxRadioState radio_state_{
|
||||
314950000 /* frequency*/,
|
||||
314950000 /* frequency*/
|
||||
,
|
||||
1750000 /* bandwidth */,
|
||||
2457600 /* sampling rate */};
|
||||
2457600 /* sampling rate */
|
||||
};
|
||||
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_tpms",
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"units_psi"sv, &tpms::format::units_psi},
|
||||
{"units_fahr"sv, &tpms::format::units_fahr},
|
||||
{"units_psi"sv, &format::units_psi},
|
||||
{"units_fahr"sv, &format::units_fahr},
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_packet{
|
||||
|
@ -198,6 +192,6 @@ class TPMSAppView : public View {
|
|||
void update_view();
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::tpmsrx
|
||||
|
||||
#endif /*__TPMS_APP_H__*/
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -22,6 +23,7 @@
|
|||
|
||||
#include "file.hpp"
|
||||
#include "complex.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
|
@ -368,6 +370,15 @@ std::filesystem::filesystem_error ensure_directory(
|
|||
return make_new_directory(dir_path);
|
||||
}
|
||||
|
||||
void ensure_fake_directories(
|
||||
const std::filesystem::path& dir_path) {
|
||||
std::filesystem::path combined_user_dir = user_dir / dir_path;
|
||||
std::filesystem::path combimed_system_dir = system_dir / dir_path;
|
||||
|
||||
ensure_directory(combimed_system_dir);
|
||||
ensure_directory(combined_user_dir);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
namespace filesystem {
|
||||
|
||||
|
@ -454,6 +465,49 @@ path path::filename() const {
|
|||
}
|
||||
}
|
||||
|
||||
path path::remove_first_level() const { // /abc/def/ghi/hjk.q to /def/ghi/hjk.q
|
||||
|
||||
std::size_t start_index = _s.find(preferred_separator);
|
||||
if (start_index == _s.npos) {
|
||||
return _s;
|
||||
} else {
|
||||
start_index += 1; // To move past the first slash
|
||||
std::size_t end_index = _s.find(preferred_separator, start_index);
|
||||
if (end_index == _s.npos) {
|
||||
end_index = _s.length(); // If there's no more slashes, end at the end of the string
|
||||
}
|
||||
return _s.substr(end_index);
|
||||
}
|
||||
}
|
||||
|
||||
path path::extract_first_level() const { // /abc/def/ghi/hjk.q to /abc
|
||||
|
||||
const auto first_separator = _s.find_first_of(preferred_separator);
|
||||
if (first_separator == _s.npos) {
|
||||
return _s;
|
||||
} else {
|
||||
const auto second_separator = _s.find_first_of(preferred_separator, first_separator + 1);
|
||||
if (second_separator == _s.npos) {
|
||||
return _s.substr(0, first_separator);
|
||||
} else {
|
||||
return _s.substr(0, second_separator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path path::absolute_trimmed_path() const {
|
||||
basic_string<char16_t> path_str = _s.substr();
|
||||
|
||||
if (path_str.front() == '/') {
|
||||
path_str.erase(0, 1);
|
||||
}
|
||||
if (path_str.back() == '/') {
|
||||
path_str.pop_back();
|
||||
}
|
||||
|
||||
return path{path_str};
|
||||
}
|
||||
|
||||
path path::stem() const {
|
||||
const auto t = filename().native();
|
||||
const auto index = t.find_last_of(u'.');
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -116,6 +117,9 @@ struct path {
|
|||
path parent_path() const;
|
||||
path extension() const;
|
||||
path filename() const;
|
||||
path remove_first_level() const;
|
||||
path extract_first_level() const;
|
||||
path absolute_trimmed_path() const;
|
||||
path stem() const;
|
||||
|
||||
bool empty() const {
|
||||
|
@ -276,6 +280,7 @@ std::filesystem::filesystem_error file_update_date(const std::filesystem::path&
|
|||
std::filesystem::filesystem_error make_new_file(const std::filesystem::path& file_path);
|
||||
std::filesystem::filesystem_error make_new_directory(const std::filesystem::path& dir_path);
|
||||
std::filesystem::filesystem_error ensure_directory(const std::filesystem::path& dir_path);
|
||||
void ensure_fake_directories(const std::filesystem::path& dir_path);
|
||||
|
||||
template <typename TCallback>
|
||||
void scan_root_files(const std::filesystem::path& directory, const std::filesystem::path& extension, const TCallback& fn) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
* copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -22,28 +23,104 @@
|
|||
#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";
|
||||
const std::filesystem::path adsb_dir = u"SYS/ADSB"; // this dir no need to seperate profiles since all are resources
|
||||
|
||||
const std::filesystem::path ais_dir = u"SYS/AIS"; // this dir no need to seperate profiles since all are resources
|
||||
|
||||
const std::filesystem::path apps_dir = u"APPS"; // currently in root
|
||||
|
||||
// APRS
|
||||
const std::filesystem::path aprs_dir = u"APRS"; // fake path
|
||||
const std::filesystem::path aprs_dir_user = u"/APRS";
|
||||
const std::filesystem::path aprs_dir_resources = u"SYS/APRS";
|
||||
|
||||
// AUDIO
|
||||
const std::filesystem::path audio_dir = u"AUDIO"; // fake path
|
||||
const std::filesystem::path audio_dir_user = u"/AUDIO";
|
||||
const std::filesystem::path audio_dir_resources = u"SYS/AUDIO";
|
||||
|
||||
// BLERX
|
||||
const std::filesystem::path blerx_dir = u"BLERX"; // fake path
|
||||
const std::filesystem::path blerx_dir_user = u"/BLERX";
|
||||
const std::filesystem::path blerx_dir_resources = u"SYS/BLERX";
|
||||
|
||||
// BLETX
|
||||
const std::filesystem::path bletx_dir = u"BLETX"; // fake path
|
||||
const std::filesystem::path bletx_dir_user = u"/BLETX";
|
||||
const std::filesystem::path bletx_dir_resources = u"SYS/BLETX";
|
||||
|
||||
// CAPTURES
|
||||
const std::filesystem::path captures_dir = u"CAPTURES"; // fake path
|
||||
const std::filesystem::path captures_dir_user = u"/CAPTURES"; // used by recon and capture apps to save capture C16 into USR
|
||||
const std::filesystem::path captures_dir_resources = u"SYS/CAPTURES";
|
||||
|
||||
const std::filesystem::path debug_dir = u"DEBUG"; // in root
|
||||
|
||||
const std::filesystem::path firmware_dir = u"FIRMWARE"; // in root
|
||||
|
||||
// FREQMAN
|
||||
const std::filesystem::path freqman_dir = u"FREQMAN"; // fake path
|
||||
const std::filesystem::path freqman_dir_user = u"/FREQMAN";
|
||||
const std::filesystem::path freqman_dir_resources = u"SYS/FREQMAN";
|
||||
|
||||
const std::filesystem::path fskrx_dir_user = u"/FSKRX";
|
||||
|
||||
const std::filesystem::path gps_dir = u"/GPS";
|
||||
|
||||
const std::filesystem::path logs_dir = u"/LOGS";
|
||||
|
||||
const std::filesystem::path looking_glass_dir = u"SYS/LOOKINGGLASS";
|
||||
|
||||
// PLAYLIST
|
||||
const std::filesystem::path playlist_dir = u"PLAYLIST"; // fake dir
|
||||
const std::filesystem::path playlist_dir_user = u"/PLAYLIST"; // used by playlist aka replay app, to save nre created PPL files into USR
|
||||
const std::filesystem::path playlist_dir_resources = u"SYS/PLAYLIST"; //
|
||||
|
||||
// REMOTE
|
||||
const std::filesystem::path remotes_dir = u"REMOTES"; // fake dir
|
||||
const std::filesystem::path remotes_dir_user = u"/REMOTES";
|
||||
const std::filesystem::path remotes_dir_resources = u"SYS/REMOTES";
|
||||
|
||||
// recon app
|
||||
const std::filesystem::path repeat_rec_path = u"/CAPTURES"; // this is for recon captures files
|
||||
|
||||
const std::filesystem::path samples_dir = u"SYS/CAPTURES"; // TODO: rename var name
|
||||
|
||||
const std::filesystem::path screenshots_dir = u"/SCREENSHOTS"; // TODO: rename var name to screenshots_dir
|
||||
|
||||
const std::filesystem::path settings_dir = u"/SETTINGS";
|
||||
|
||||
const std::filesystem::path spectrum_dir = u"SPECTRUM"; // fake dir
|
||||
|
||||
const std::filesystem::path splash_dir = u"/SPLASH";
|
||||
|
||||
const std::filesystem::path sstv_dir = u"SSTV"; // fake path
|
||||
const std::filesystem::path sstv_dir_user = u"/SSTV";
|
||||
const std::filesystem::path sstv_dir_resources = u"SYS/SSTV";
|
||||
|
||||
const std::filesystem::path system_dir = u"/SYS"; // in root
|
||||
const std::filesystem::path user_dir = u"/"; // root
|
||||
|
||||
// WAV
|
||||
const std::filesystem::path wav_dir = u"WAV"; // fake path
|
||||
const std::filesystem::path wav_dir_user = u"/WAV";
|
||||
const std::filesystem::path wav_dir_resources = u"SYS/WAV";
|
||||
|
||||
const std::filesystem::path whipcalc_dir = u"SYS/WHIPCALC";
|
||||
|
||||
// vector
|
||||
|
||||
const std::vector<std::string> allow_dirs = {
|
||||
"ADSB",
|
||||
"AIS",
|
||||
"BLETX",
|
||||
"GPS",
|
||||
"LOOKINGGLASS",
|
||||
"PLAYLIST",
|
||||
"REMOTES",
|
||||
"SPLASH",
|
||||
"SSTV",
|
||||
"WAV",
|
||||
"WHIPCALC",
|
||||
"SAMPLES",
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
* copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -25,29 +26,83 @@
|
|||
#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 aprs_dir_user;
|
||||
extern const std::filesystem::path aprs_dir_resources;
|
||||
|
||||
extern const std::filesystem::path audio_dir;
|
||||
extern const std::filesystem::path audio_dir_user;
|
||||
extern const std::filesystem::path audio_dir_resources;
|
||||
|
||||
extern const std::filesystem::path blerx_dir;
|
||||
extern const std::filesystem::path blerx_dir_user;
|
||||
extern const std::filesystem::path blerx_dir_resources;
|
||||
|
||||
extern const std::filesystem::path bletx_dir;
|
||||
extern const std::filesystem::path bletx_dir_user;
|
||||
extern const std::filesystem::path bletx_dir_resources;
|
||||
|
||||
extern const std::filesystem::path captures_dir;
|
||||
extern const std::filesystem::path captures_dir_user;
|
||||
extern const std::filesystem::path captures_dir_resources;
|
||||
|
||||
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 freqman_dir_user;
|
||||
extern const std::filesystem::path freqman_dir_resources;
|
||||
|
||||
extern const std::filesystem::path fskrx_dir_user;
|
||||
|
||||
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 playlist_dir_user;
|
||||
extern const std::filesystem::path playlist_dir_resources;
|
||||
|
||||
extern const std::filesystem::path remotes_dir;
|
||||
extern const std::filesystem::path remotes_dir_user;
|
||||
extern const std::filesystem::path remotes_dir_resources;
|
||||
|
||||
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 sstv_dir_user;
|
||||
extern const std::filesystem::path sstv_dir_resources;
|
||||
|
||||
extern const std::filesystem::path system_dir;
|
||||
|
||||
extern const std::filesystem::path user_dir;
|
||||
|
||||
extern const std::filesystem::path wav_dir;
|
||||
|
||||
extern const std::filesystem::path wav_dir_user;
|
||||
|
||||
extern const std::filesystem::path wav_dir_resources;
|
||||
|
||||
extern const std::filesystem::path whipcalc_dir;
|
||||
|
||||
extern const std::vector<std::string> allow_dirs;
|
||||
|
||||
#endif /* __FILE_PATH_H__ */
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
|
||||
* Copyright (C) 2023 Kyle Reed
|
||||
* copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -39,6 +41,7 @@
|
|||
namespace fs = std::filesystem;
|
||||
|
||||
const std::filesystem::path freqman_extension{u".TXT"};
|
||||
bool current_is_system_item{true};
|
||||
|
||||
// NB: Don't include UI headers to keep this code unit testable.
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
|
@ -218,21 +221,32 @@ std::string freqman_entry_get_step_string_short(freqman_index_t step) {
|
|||
return {};
|
||||
}
|
||||
|
||||
const std::filesystem::path get_freqman_path(const std::string& stem) {
|
||||
return freqman_dir / stem + freqman_extension;
|
||||
const std::filesystem::path get_freqman_path(const std::string& stem, dir_profile profile) {
|
||||
switch (profile) {
|
||||
case dir_profile::ProfileSystem:
|
||||
return freqman_dir_resources / stem + freqman_extension;
|
||||
break;
|
||||
case dir_profile::ProfileUser:
|
||||
return freqman_dir_user / stem + freqman_extension;
|
||||
break;
|
||||
default:
|
||||
// throw ?
|
||||
return freqman_dir_user / stem + freqman_extension;
|
||||
}
|
||||
}
|
||||
|
||||
bool create_freqman_file(const std::string& file_stem) {
|
||||
auto fs_error = make_new_file(get_freqman_path(file_stem));
|
||||
// always create in user dir, so no judgement here
|
||||
auto fs_error = make_new_file(get_freqman_path(file_stem, dir_profile::ProfileUser)); // only allow create on usr dir
|
||||
return fs_error.ok();
|
||||
}
|
||||
|
||||
bool load_freqman_file(const std::string& file_stem, freqman_db& db, freqman_load_options options) {
|
||||
return parse_freqman_file(get_freqman_path(file_stem), db, options);
|
||||
bool load_freqman_file(const std::string& file_stem, freqman_db& db, freqman_load_options options, dir_profile profile) {
|
||||
return parse_freqman_file(get_freqman_path(file_stem, profile), db, options);
|
||||
}
|
||||
|
||||
void delete_freqman_file(const std::string& file_stem) {
|
||||
delete_file(get_freqman_path(file_stem));
|
||||
delete_file(get_freqman_path(file_stem, dir_profile::ProfileUser)); // don't allow to delete sys files
|
||||
}
|
||||
|
||||
std::string pretty_string(const freqman_entry& entry, size_t max_length) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
|
||||
* Copyright (C) 2023 Kyle Reed
|
||||
* copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -37,6 +38,7 @@
|
|||
|
||||
/* Defined in freqman_db.cpp */
|
||||
extern const std::filesystem::path freqman_extension;
|
||||
extern bool current_is_system_item;
|
||||
|
||||
using freqman_index_t = uint8_t;
|
||||
constexpr freqman_index_t freqman_invalid_index = static_cast<freqman_index_t>(-1);
|
||||
|
@ -60,6 +62,11 @@ enum class freqman_type : uint8_t {
|
|||
Unknown,
|
||||
};
|
||||
|
||||
enum class dir_profile : uint8_t {
|
||||
ProfileUser = 1,
|
||||
ProfileSystem = 2
|
||||
};
|
||||
|
||||
/* Tables for next round of changes.
|
||||
* // TODO: attempt to consolidate all of these values
|
||||
* // and settings into a single location.
|
||||
|
@ -177,9 +184,9 @@ using freqman_entry_ptr = std::unique_ptr<freqman_entry>;
|
|||
using freqman_db = std::vector<freqman_entry_ptr>;
|
||||
|
||||
/* Gets the full path for a given file stem (no extension). */
|
||||
const std::filesystem::path get_freqman_path(const std::string& stem);
|
||||
const std::filesystem::path get_freqman_path(const std::string& stem, dir_profile profile);
|
||||
bool create_freqman_file(const std::string& file_stem);
|
||||
bool load_freqman_file(const std::string& file_stem, freqman_db& db, freqman_load_options options);
|
||||
bool load_freqman_file(const std::string& file_stem, freqman_db& db, freqman_load_options options, dir_profile profile);
|
||||
void delete_freqman_file(const std::string& file_stem);
|
||||
|
||||
/* Gets a pretty string representation for an entry. */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* copyleft (ɔ) 2023 zxkmm under GPL license
|
||||
* copyleft (ɔ) 2023 zxkmm with the GPL license
|
||||
* Copyright (C) 2023 u-foka
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* copyleft (ɔ) 2023 zxkmm under GPL license
|
||||
* copyleft (ɔ) 2023 zxkmm with the GPL license
|
||||
* Copyright (C) 2023 u-foka
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
|
|
|
@ -85,7 +85,6 @@ ClockManager clock_manager{
|
|||
|
||||
WM8731 audio_codec_wm8731{i2c0, 0x1a};
|
||||
AK4951 audio_codec_ak4951{i2c0, 0x12};
|
||||
ads1110::ADS1110 battery_ads1110{i2c0, 0x48};
|
||||
|
||||
ReceiverModel receiver_model;
|
||||
TransmitterModel transmitter_model;
|
||||
|
@ -587,7 +586,7 @@ init_status_t init() {
|
|||
chThdSleepMilliseconds(10);
|
||||
|
||||
audio::init(portapack_audio_codec());
|
||||
battery_ads1110.init();
|
||||
battery::BatteryManagement::init();
|
||||
|
||||
if (lcd_fast_setup)
|
||||
draw_splash_screen_icon(4, ui::bitmap_icon_speaker);
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "radio.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include "temperature_logger.hpp"
|
||||
#include "battery.hpp"
|
||||
|
||||
/* TODO: This would be better as a class to add
|
||||
* guardrails on setting properties. */
|
||||
|
@ -63,7 +64,6 @@ extern portapack::USBSerial usb_serial;
|
|||
extern si5351::Si5351 clock_generator;
|
||||
extern ClockManager clock_manager;
|
||||
|
||||
extern ads1110::ADS1110 battery_ads1110;
|
||||
extern ReceiverModel receiver_model;
|
||||
extern TransmitterModel transmitter_model;
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "ui_bmpview.hpp"
|
||||
#include "usb_serial_asyncmsg.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
bool BMPViewer::load_bmp(const std::filesystem::path& file) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -84,6 +84,7 @@
|
|||
#include "ui_weatherstation.hpp"
|
||||
#include "ui_subghzd.hpp"
|
||||
#include "ui_whipcalc.hpp"
|
||||
#include "ui_battinfo.hpp"
|
||||
#include "ui_external_items_menu_loader.hpp"
|
||||
|
||||
// #include "acars_app.hpp"
|
||||
|
@ -100,7 +101,7 @@
|
|||
#include "pocsag_app.hpp"
|
||||
#include "replay_app.hpp"
|
||||
#include "soundboard_app.hpp"
|
||||
#include "tpms_app.hpp"
|
||||
// #include "tpms_app.hpp" //moved to ext
|
||||
|
||||
#include "core_control.hpp"
|
||||
#include "file.hpp"
|
||||
|
@ -171,9 +172,9 @@ const NavigationView::AppList NavigationView::appList = {
|
|||
{"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory<SondeView>()},
|
||||
{"recon", "Recon", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory<ReconView>()},
|
||||
{"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory<SearchView>()},
|
||||
{"tmps", "TPMS Cars", RX, Color::green(), &bitmap_icon_tpms, new ViewFactory<TPMSAppView>()},
|
||||
{"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory<WeatherView>()},
|
||||
//{"tmps", "TPMS Cars", RX, Color::green(), &bitmap_icon_tpms, new ViewFactory<TPMSAppView>()},
|
||||
{"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<SubGhzDView>()},
|
||||
{"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory<WeatherView>()},
|
||||
//{"fskrx", "FSK RX", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<FskxRxMainView>()},
|
||||
//{"dmr", "DMR", RX, Color::dark_grey(), &bitmap_icon_dmr, new ViewFactory<NotImplementedView>()},
|
||||
//{"sigfox", "SIGFOX", RX, Color::dark_grey(), &bitmap_icon_fox, new ViewFactory<NotImplementedView>()},
|
||||
|
@ -334,6 +335,9 @@ SystemStatusView::SystemStatusView(
|
|||
refresh();
|
||||
};
|
||||
|
||||
battery_icon.on_select = [this]() { on_battery_details(); };
|
||||
battery_text.on_select = [this]() { on_battery_details(); };
|
||||
|
||||
button_fake_brightness.on_select = [this](ImageButton&) {
|
||||
set_dirty();
|
||||
pmem::toggle_fake_brightness_level();
|
||||
|
@ -370,6 +374,26 @@ SystemStatusView::SystemStatusView(
|
|||
refresh();
|
||||
}
|
||||
|
||||
// when battery icon / text is clicked
|
||||
void SystemStatusView::on_battery_details() {
|
||||
if (!nav_.is_valid()) return;
|
||||
if (batt_info_up) return;
|
||||
batt_info_up = true;
|
||||
nav_.push<BattinfoView>();
|
||||
nav_.set_on_pop([this]() {
|
||||
batt_info_up = false;
|
||||
});
|
||||
}
|
||||
|
||||
void SystemStatusView::on_battery_data(const BatteryStateMessage* msg) {
|
||||
if (!pmem::ui_hide_numeric_battery()) {
|
||||
battery_text.set_battery(msg->percent, msg->on_charger);
|
||||
}
|
||||
if (!pmem::ui_hide_battery_icon()) {
|
||||
battery_icon.set_battery(msg->percent, msg->on_charger);
|
||||
};
|
||||
}
|
||||
|
||||
void SystemStatusView::refresh() {
|
||||
// NB: Order of insertion is the display order Left->Right.
|
||||
// TODO: Might be better to support hide and only add once.
|
||||
|
@ -386,8 +410,20 @@ void SystemStatusView::refresh() {
|
|||
if (audio::speaker_disable_supported() && !pmem::ui_hide_speaker()) status_icons.add(&toggle_speaker);
|
||||
|
||||
if (!pmem::ui_hide_fake_brightness()) status_icons.add(&button_fake_brightness);
|
||||
if (battery::BatteryManagement::isDetected()) {
|
||||
uint8_t percent = battery::BatteryManagement::getPercent();
|
||||
if (!pmem::ui_hide_battery_icon()) {
|
||||
status_icons.add(&battery_icon);
|
||||
battery_text.set_battery(percent, false); // got an on select, that may pop up the details of the battery.
|
||||
};
|
||||
if (!pmem::ui_hide_numeric_battery()) {
|
||||
status_icons.add(&battery_text);
|
||||
battery_text.set_battery(percent, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pmem::ui_hide_sd_card()) status_icons.add(&sd_card_status_view);
|
||||
|
||||
status_icons.update_layout();
|
||||
|
||||
// Clock status
|
||||
|
@ -429,6 +465,11 @@ void SystemStatusView::set_back_hidden(bool new_value) {
|
|||
void SystemStatusView::set_title_image_enabled(bool new_value) {
|
||||
if (new_value) {
|
||||
add_child(&button_title);
|
||||
if (!already_shown_sdcard_warning_in_this_boot) {
|
||||
// the modal warning can't be called from constructor since the nav isn't valid at that time, so putting here as a work around
|
||||
new_sdcard_structure_checker();
|
||||
already_shown_sdcard_warning_in_this_boot = true;
|
||||
}
|
||||
} else {
|
||||
remove_child(&button_title);
|
||||
}
|
||||
|
@ -563,6 +604,93 @@ void SystemStatusView::rtc_battery_workaround() {
|
|||
}
|
||||
}
|
||||
|
||||
void SystemStatusView::new_sdcard_structure_checker() {
|
||||
const std::filesystem::path root_dir = u"/";
|
||||
const std::filesystem::path resources_dir = u"SYS";
|
||||
std::vector<std::filesystem::path> directories = scan_root_directories(root_dir);
|
||||
|
||||
auto scan_result = std::find(directories.begin(), directories.end(), resources_dir);
|
||||
|
||||
auto sd_status = sd_card::status();
|
||||
|
||||
if (
|
||||
nav_.is_valid() &&
|
||||
(scan_result == directories.end()) &&
|
||||
(sd_status == sd_card::Status::Mounted)) {
|
||||
nav_.display_modal(
|
||||
"Warning",
|
||||
" WARNING!!!\n"
|
||||
"SD Card content changed\n"
|
||||
"in this update,\n"
|
||||
"Press YES to auto move.\n"
|
||||
"After finished,\n"
|
||||
"you should move your own\n"
|
||||
"files back into\n"
|
||||
"original folders,\n"
|
||||
"which are now only for users.\n"
|
||||
"You can also choose NO,\n"
|
||||
"and then manually merge\n"
|
||||
"following the Wiki.\n"
|
||||
"Details:\n"
|
||||
"Mayhem wiki - Updating",
|
||||
YESNO,
|
||||
[this](bool choice) {
|
||||
if (choice) {
|
||||
new_sdcard_structure_worker();
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
void SystemStatusView::new_sdcard_structure_worker() {
|
||||
const std::filesystem::path root_dir = u"/";
|
||||
const std::filesystem::path system_dir = u"SYS";
|
||||
|
||||
/// worker log
|
||||
std::filesystem::path filename{};
|
||||
File automove_dump_file{};
|
||||
ensure_directory(logs_dir);
|
||||
filename = next_filename_matching_pattern(logs_dir + "/AUTOMOVE_LOG_????.TXT");
|
||||
automove_dump_file.create(filename);
|
||||
|
||||
std::vector<std::filesystem::path> root_dirs = scan_root_directories(root_dir);
|
||||
|
||||
std::vector<std::string> scan_result_string_vec;
|
||||
for (const auto& e : root_dirs) {
|
||||
scan_result_string_vec.push_back(e.string());
|
||||
}
|
||||
|
||||
std::string scan_result_string = "";
|
||||
for (const auto& e : scan_result_string_vec) {
|
||||
scan_result_string += e + "\n";
|
||||
}
|
||||
|
||||
automove_dump_file.write_line(scan_result_string + "\n\n");
|
||||
|
||||
/// worker moving folders
|
||||
ensure_directory(system_dir);
|
||||
|
||||
auto directories = scan_root_directories(root_dir);
|
||||
|
||||
for (const auto& e : root_dirs) {
|
||||
for (const auto& d : allow_dirs) {
|
||||
if (e.absolute_trimmed_path().string() == d) {
|
||||
std::filesystem::path new_path = system_dir / d;
|
||||
rename_file(e, new_path);
|
||||
automove_dump_file.write_line("Moved: " + e.string() + " to " + new_path.string() + "\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// worker renaming
|
||||
rename_file(u"/SYS/SAMPLES", u"/SYS/CAPTURES"); // it's because, make it easier to use the sample folder.
|
||||
|
||||
/// worker check adding flag
|
||||
make_new_file(U"/AUTO_MOVED"); // add a flag to check in case if need something as flag to check in the future
|
||||
}
|
||||
|
||||
/* Information View *****************************************************/
|
||||
|
||||
InformationView::InformationView(
|
||||
|
@ -667,8 +795,9 @@ void NavigationView::display_modal(
|
|||
const std::string& title,
|
||||
const std::string& message,
|
||||
modal_t type,
|
||||
std::function<void(bool)> on_choice) {
|
||||
push<ModalMessageView>(title, message, type, on_choice);
|
||||
std::function<void(bool)> on_choice,
|
||||
bool compact) {
|
||||
push<ModalMessageView>(title, message, type, on_choice, compact);
|
||||
}
|
||||
|
||||
void NavigationView::free_view() {
|
||||
|
@ -969,7 +1098,7 @@ BMPView::BMPView(NavigationView& nav) {
|
|||
}
|
||||
|
||||
void BMPView::paint(Painter&) {
|
||||
if (!portapack::display.drawBMP2({0, 0}, splash_dot_bmp))
|
||||
if (!portapack::display.drawBMP2({0, 0}, current_using_splash_image))
|
||||
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, false);
|
||||
}
|
||||
|
||||
|
@ -997,11 +1126,13 @@ ModalMessageView::ModalMessageView(
|
|||
const std::string& title,
|
||||
const std::string& message,
|
||||
modal_t type,
|
||||
std::function<void(bool)> on_choice)
|
||||
std::function<void(bool)> on_choice,
|
||||
bool compact)
|
||||
: title_{title},
|
||||
message_{message},
|
||||
type_{type},
|
||||
on_choice_{on_choice} {
|
||||
on_choice_{on_choice},
|
||||
compact{compact} {
|
||||
if (type == INFO) {
|
||||
add_child(&button_ok);
|
||||
button_ok.on_select = [this, &nav](Button&) {
|
||||
|
@ -1034,21 +1165,23 @@ ModalMessageView::ModalMessageView(
|
|||
}
|
||||
|
||||
void ModalMessageView::paint(Painter& painter) {
|
||||
portapack::display.drawBMP({100, 48}, modal_warning_bmp, false);
|
||||
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, false);
|
||||
|
||||
// Break lines.
|
||||
auto lines = split_string(message_, '\n');
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
painter.draw_string(
|
||||
{1 * 8, (Coord)(120 + (i * 16))},
|
||||
|
||||
{1 * 8, (Coord)(((compact) ? 8 * 3 : 120) + (i * 16))},
|
||||
|
||||
style(),
|
||||
lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ModalMessageView::focus() {
|
||||
if ((type_ == YESNO)) {
|
||||
button_yes.focus();
|
||||
if (type_ == YESNO) {
|
||||
button_no.focus(); // it should default to NO like all the operating system
|
||||
} else {
|
||||
button_ok.focus();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
@ -120,7 +120,8 @@ class NavigationView : public View {
|
|||
const std::string& title,
|
||||
const std::string& message,
|
||||
modal_t type,
|
||||
std::function<void(bool)> on_choice = nullptr);
|
||||
std::function<void(bool)> on_choice = nullptr,
|
||||
bool compact = false);
|
||||
|
||||
void focus() override;
|
||||
|
||||
|
@ -190,8 +191,9 @@ class SystemStatusView : public View {
|
|||
void set_title(const std::string new_value);
|
||||
|
||||
private:
|
||||
bool already_shown_sdcard_warning_in_this_boot{false};
|
||||
static constexpr auto default_title = "";
|
||||
|
||||
bool batt_info_up = false; // to prevent show multiple batt info dialog
|
||||
NavigationView& nav_;
|
||||
|
||||
Rectangle backdrop{
|
||||
|
@ -278,6 +280,9 @@ class SystemStatusView : public View {
|
|||
SDCardStatusView sd_card_status_view{
|
||||
{0, 0 * 16, 2 * 8, 1 * 16}};
|
||||
|
||||
BatteryTextField battery_text{{0, 0, 2 * 8, 1 * 16}, 102};
|
||||
BatteryIcon battery_icon{{0, 0, 10, 1 * 16}, 102};
|
||||
|
||||
void on_converter();
|
||||
void on_bias_tee();
|
||||
void on_camera();
|
||||
|
@ -286,12 +291,25 @@ class SystemStatusView : public View {
|
|||
void on_clk();
|
||||
void rtc_battery_workaround();
|
||||
|
||||
void new_sdcard_structure_checker();
|
||||
void new_sdcard_structure_worker();
|
||||
|
||||
void on_battery_data(const BatteryStateMessage* msg);
|
||||
void on_battery_details();
|
||||
|
||||
MessageHandlerRegistration message_handler_refresh{
|
||||
Message::ID::StatusRefresh,
|
||||
[this](const Message* const p) {
|
||||
(void)p;
|
||||
this->refresh();
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_battery{
|
||||
Message::ID::BatteryStateData,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const BatteryStateMessage*>(p);
|
||||
this->on_battery_data(message);
|
||||
}};
|
||||
};
|
||||
|
||||
class InformationView : public View {
|
||||
|
@ -418,7 +436,8 @@ class ModalMessageView : public View {
|
|||
const std::string& title,
|
||||
const std::string& message,
|
||||
modal_t type,
|
||||
std::function<void(bool)> on_choice);
|
||||
std::function<void(bool)> on_choice,
|
||||
bool compact = false);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
void focus() override;
|
||||
|
@ -430,19 +449,20 @@ class ModalMessageView : public View {
|
|||
const std::string message_;
|
||||
const modal_t type_;
|
||||
const std::function<void(bool)> on_choice_;
|
||||
const bool compact;
|
||||
|
||||
Button button_ok{
|
||||
{10 * 8, 14 * 16, 10 * 8, 48},
|
||||
{0, screen_height - 8 * 8, screen_width, 48},
|
||||
"OK",
|
||||
};
|
||||
|
||||
Button button_yes{
|
||||
{5 * 8, 14 * 16, 8 * 8, 48},
|
||||
{0, screen_height - 8 * 8, screen_width / 2, 48},
|
||||
"YES",
|
||||
};
|
||||
|
||||
Button button_no{
|
||||
{17 * 8, 14 * 16, 8 * 8, 48},
|
||||
{screen_width / 2, screen_height - 8 * 8, screen_width / 2, 48},
|
||||
"NO",
|
||||
};
|
||||
};
|
||||
|
|
|
@ -498,12 +498,6 @@ set(MODE_CPPSRC
|
|||
)
|
||||
DeclareTargets(PTON tones)
|
||||
|
||||
### TPMS
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_tpms.cpp
|
||||
)
|
||||
DeclareTargets(PTPM tpms)
|
||||
|
||||
### Wideband Spectrum
|
||||
|
||||
|
@ -651,6 +645,15 @@ set(MODE_CPPSRC
|
|||
)
|
||||
DeclareTargets(PABP audio_beep)
|
||||
|
||||
|
||||
### TPMS
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_tpms.cpp
|
||||
)
|
||||
DeclareTargets(PTPM tpms)
|
||||
|
||||
|
||||
### HackRF "factory" firmware
|
||||
|
||||
add_custom_command(
|
||||
|
|
|
@ -24,10 +24,11 @@
|
|||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
namespace battery {
|
||||
namespace ads1110 {
|
||||
|
||||
constexpr float BATTERY_MIN_VOLTAGE = 3.0;
|
||||
constexpr float BATTERY_MAX_VOLTAGE = 4.0;
|
||||
constexpr uint16_t BATTERY_MIN_VOLTAGE = 3000;
|
||||
constexpr uint16_t BATTERY_MAX_VOLTAGE = 4000;
|
||||
|
||||
void ADS1110::init() {
|
||||
if (!detected_) {
|
||||
|
@ -50,6 +51,7 @@ bool ADS1110::detect() {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
detected_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -57,7 +59,8 @@ bool ADS1110::write(const uint8_t value) {
|
|||
return bus.transmit(bus_address, &value, 1);
|
||||
}
|
||||
|
||||
float ADS1110::readVoltage() {
|
||||
// returns the batt voltage in mV
|
||||
uint16_t ADS1110::readVoltage() {
|
||||
// Read the conversion result
|
||||
uint8_t data[3];
|
||||
if (!bus.receive(bus_address, data, 3)) {
|
||||
|
@ -67,7 +70,7 @@ float ADS1110::readVoltage() {
|
|||
uint16_t raw = (static_cast<uint16_t>(data[0]) << 8) | data[1];
|
||||
|
||||
// Calculate the voltage based on the output code
|
||||
float voltage = 0.0f;
|
||||
int16_t voltage = 0;
|
||||
float minCode = 0;
|
||||
float pga = 0.0f;
|
||||
|
||||
|
@ -110,21 +113,21 @@ float ADS1110::readVoltage() {
|
|||
}
|
||||
|
||||
// 2.048 is the reference voltage & 2.0 is to make up for the voltage divider
|
||||
voltage = raw / (-1.0 * minCode) * pga * 2.048 * 2.0;
|
||||
|
||||
return voltage;
|
||||
voltage = (int16_t)(raw / (-1.0 * minCode) * pga * 2.048 * 2.0 * 1000.0); // v to mV
|
||||
if (voltage < 0) voltage *= -1; // should not happen in this build, but prevent it
|
||||
return (uint16_t)voltage;
|
||||
}
|
||||
|
||||
void ADS1110::getBatteryInfo(float& batteryPercentage, float& voltage) {
|
||||
void ADS1110::getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage) {
|
||||
voltage = readVoltage();
|
||||
|
||||
// Calculate the remaining battery percentage
|
||||
batteryPercentage = (voltage - BATTERY_MIN_VOLTAGE) /
|
||||
(BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE) * 100.0;
|
||||
batteryPercentage = (float)(voltage - BATTERY_MIN_VOLTAGE) / (float)(BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE) * 100.0;
|
||||
|
||||
// Limit the values to the valid range
|
||||
batteryPercentage = std::clamp(batteryPercentage, 0.0f, 100.0f);
|
||||
batteryPercentage = (batteryPercentage > 100) ? 100 : batteryPercentage;
|
||||
// ToDo: if its > 4, then 100%, if < 3 then 0%
|
||||
}
|
||||
|
||||
} /* namespace ads1110 */
|
||||
} // namespace battery
|
|
@ -27,11 +27,10 @@
|
|||
#include <string>
|
||||
|
||||
#include "i2c_pp.hpp"
|
||||
|
||||
namespace battery {
|
||||
namespace ads1110 {
|
||||
|
||||
using address_t = uint8_t;
|
||||
using reg_t = uint16_t;
|
||||
|
||||
class ADS1110 {
|
||||
public:
|
||||
|
@ -39,10 +38,11 @@ class ADS1110 {
|
|||
: bus(bus), bus_address(bus_address), detected_(false) {}
|
||||
|
||||
void init();
|
||||
bool detect();
|
||||
bool isDetected() const { return detected_; }
|
||||
|
||||
float readVoltage();
|
||||
void getBatteryInfo(float& batteryPercentage, float& voltage);
|
||||
uint16_t readVoltage();
|
||||
void getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage);
|
||||
|
||||
private:
|
||||
I2C& bus;
|
||||
|
@ -50,9 +50,8 @@ class ADS1110 {
|
|||
bool detected_;
|
||||
|
||||
bool write(const uint8_t value);
|
||||
bool detect();
|
||||
};
|
||||
|
||||
} /* namespace ads1110 */
|
||||
|
||||
} // namespace battery
|
||||
#endif /* __ADS1110_H__ */
|
|
@ -0,0 +1,111 @@
|
|||
#include "battery.hpp"
|
||||
#include "event_m0.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "ads1110.hpp"
|
||||
|
||||
// uncomment if you want to emulate batt management system
|
||||
// #define USE_BATT_EMULATOR
|
||||
|
||||
extern I2C portapack::i2c0;
|
||||
|
||||
namespace battery {
|
||||
|
||||
constexpr uint32_t BATTERY_UPDATE_INTERVAL = 30000;
|
||||
BatteryManagement::BatteryModules BatteryManagement::detected_ = BatteryManagement::BATT_NONE;
|
||||
|
||||
ads1110::ADS1110 battery_ads1110{portapack::i2c0, 0x48};
|
||||
|
||||
Thread* BatteryManagement::thread = nullptr;
|
||||
|
||||
void BatteryManagement::init() {
|
||||
// try to detect supported modules
|
||||
detected_ = BATT_NONE;
|
||||
if (battery_ads1110.detect()) {
|
||||
battery_ads1110.init();
|
||||
detected_ = BATT_ADS1110;
|
||||
}
|
||||
|
||||
// add new supported module detect + init here
|
||||
|
||||
#ifdef USE_BATT_EMULATOR
|
||||
if (detected_ == BATT_NONE) {
|
||||
detected_ = BATT_EMULATOR;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (detected_ != BATT_NONE) {
|
||||
// sets timer to query and broadcats this info
|
||||
create_thread();
|
||||
}
|
||||
}
|
||||
|
||||
// sets the values, it the currend module supports it.
|
||||
bool BatteryManagement::getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current, bool& isCharging) {
|
||||
if (detected_ == BATT_NONE) return false;
|
||||
if (detected_ == BATT_ADS1110) {
|
||||
battery_ads1110.getBatteryInfo(batteryPercentage, voltage);
|
||||
return true;
|
||||
}
|
||||
// add new module query here
|
||||
|
||||
#ifdef USE_BATT_EMULATOR
|
||||
if (detected_ == BATT_EMULATOR) {
|
||||
batteryPercentage += 5; // %
|
||||
if (batteryPercentage > 100) batteryPercentage = 0;
|
||||
voltage = rand() % 1000 + 3000; // mV
|
||||
current = rand() % 150; // mA
|
||||
isCharging = rand() % 2;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
(void)isCharging; // keep the compiler calm
|
||||
(void)current;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t BatteryManagement::getPercent() {
|
||||
if (detected_ == BATT_NONE) return 102;
|
||||
uint8_t batteryPercentage = 0;
|
||||
bool isCharging = false;
|
||||
uint16_t voltage = 0;
|
||||
int32_t current = 0;
|
||||
getBatteryInfo(batteryPercentage, voltage, current, isCharging);
|
||||
return batteryPercentage;
|
||||
}
|
||||
|
||||
uint16_t BatteryManagement::getVoltage() {
|
||||
if (detected_ == BATT_NONE) return 0;
|
||||
if (detected_ == BATT_NONE) return 102;
|
||||
uint8_t batteryPercentage = 0;
|
||||
bool isCharging = false;
|
||||
uint16_t voltage = 0;
|
||||
int32_t current = 0;
|
||||
getBatteryInfo(batteryPercentage, voltage, current, isCharging);
|
||||
return voltage;
|
||||
}
|
||||
|
||||
msg_t BatteryManagement::timer_fn(void* arg) {
|
||||
(void)arg;
|
||||
if (!detected_) return 0;
|
||||
uint8_t batteryPercentage = 102;
|
||||
bool isCharging = false;
|
||||
uint16_t voltage = 0;
|
||||
int32_t current = 0;
|
||||
chThdSleepMilliseconds(1000); // wait ui for fully load
|
||||
while (1) {
|
||||
if (BatteryManagement::getBatteryInfo(batteryPercentage, voltage, current, isCharging)) {
|
||||
// send local message
|
||||
BatteryStateMessage msg{batteryPercentage, isCharging, voltage};
|
||||
EventDispatcher::send_message(msg);
|
||||
}
|
||||
chThdSleepMilliseconds(BATTERY_UPDATE_INTERVAL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BatteryManagement::create_thread() {
|
||||
thread = chThdCreateFromHeap(NULL, 512, NORMALPRIO, BatteryManagement::timer_fn, nullptr);
|
||||
}
|
||||
|
||||
} // namespace battery
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2024 HTotoo.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BATTERY_H__
|
||||
#define __BATTERY_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include "ch.h"
|
||||
|
||||
namespace battery {
|
||||
|
||||
class BatteryManagement {
|
||||
public:
|
||||
enum BatteryModules {
|
||||
BATT_NONE = 0,
|
||||
BATT_ADS1110 = 1,
|
||||
BATT_EMULATOR = 254
|
||||
};
|
||||
static void init();
|
||||
static bool isDetected() { return detected_ != BATT_NONE; }
|
||||
static bool getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current, bool& isCharging);
|
||||
static uint16_t getVoltage();
|
||||
static uint8_t getPercent();
|
||||
|
||||
private:
|
||||
static void create_thread();
|
||||
static msg_t timer_fn(void* arg);
|
||||
static Thread* thread;
|
||||
static BatteryModules detected_; // if there is any batt management system
|
||||
};
|
||||
}; // namespace battery
|
||||
#endif
|
|
@ -123,6 +123,7 @@ class Message {
|
|||
EnvironmentData = 65,
|
||||
AudioBeep = 66,
|
||||
PocsagTosend = 67,
|
||||
BatteryStateData = 68,
|
||||
MAX
|
||||
};
|
||||
|
||||
|
@ -1409,4 +1410,20 @@ class PocsagTosendMessage : public Message {
|
|||
uint64_t addr = 0;
|
||||
};
|
||||
|
||||
class BatteryStateMessage : public Message {
|
||||
public:
|
||||
constexpr BatteryStateMessage(
|
||||
uint8_t percent,
|
||||
bool on_charger,
|
||||
uint16_t voltage)
|
||||
: Message{ID::BatteryStateData},
|
||||
percent{percent},
|
||||
on_charger{on_charger},
|
||||
voltage{voltage} {
|
||||
}
|
||||
uint8_t percent = 0;
|
||||
bool on_charger = false;
|
||||
uint16_t voltage = 0; // mV
|
||||
};
|
||||
|
||||
#endif /*__MESSAGE_H__*/
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
* Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
*
|
||||
|
@ -347,42 +347,6 @@ class IO {
|
|||
addr(1); /* Set up for data phase (most likely after a command) */
|
||||
}
|
||||
|
||||
// void high_contrast(ui::Color& pixel, size_t contrast_level_shift) { // TODO
|
||||
// uint16_t r = (pixel.v >> 11) & 0x1F;
|
||||
// uint16_t g = (pixel.v >> 5) & 0x3F;
|
||||
// uint16_t b = pixel.v & 0x1F;
|
||||
//
|
||||
// if ((r << contrast_level_shift) > 0x1F) { // should be slightly smaller, need more obverse...
|
||||
// r = 0x1F;
|
||||
// } else {
|
||||
// r = r << contrast_level_shift;
|
||||
// }
|
||||
//
|
||||
// if ((g << contrast_level_shift) > 0x3F) { // same as above
|
||||
// g = 0x3F;
|
||||
// } else {
|
||||
// g = g << contrast_level_shift;
|
||||
// }
|
||||
//
|
||||
// if ((b << contrast_level_shift) > 0x1F) { // same as above
|
||||
// b = 0x1F;
|
||||
// } else {
|
||||
// b = b << contrast_level_shift;
|
||||
// }
|
||||
//
|
||||
// pixel.v = (r << 11) | (g << 5) | b;
|
||||
// }
|
||||
//
|
||||
// void gray_scale(ui::Color& pixel) { // TODO: the blackwhite not looks right....
|
||||
// uint16_t r = (pixel.v >> 11) & 0x1F;
|
||||
// uint16_t g = (pixel.v >> 5) & 0x3F;
|
||||
// uint16_t b = pixel.v & 0x1F;
|
||||
//
|
||||
// uint16_t average = (r + g + b) / 3;
|
||||
//
|
||||
// pixel.v = (average << 11) | (average << 5) | average;
|
||||
// }
|
||||
|
||||
void lcd_write_data(const uint32_t value) __attribute__((always_inline)) {
|
||||
// NOTE: Assumes and DIR=0 and ADDR=1 from command phase.
|
||||
data_write_high(value); /* Drive high byte */
|
||||
|
|
|
@ -132,8 +132,8 @@ struct ui_config2_t {
|
|||
|
||||
bool hide_mute : 1;
|
||||
bool hide_fake_brightness : 1;
|
||||
bool UNUSED_1 : 1;
|
||||
bool UNUSED_2 : 1;
|
||||
bool hide_numeric_battery : 1;
|
||||
bool hide_battery_icon : 1;
|
||||
bool UNUSED_3 : 1;
|
||||
bool UNUSED_4 : 1;
|
||||
bool UNUSED_5 : 1;
|
||||
|
@ -423,6 +423,7 @@ void defaults() {
|
|||
set_encoder_dial_sensitivity(DIAL_SENSITIVITY_NORMAL);
|
||||
set_config_speaker_disable(true); // Disable AK4951 speaker by default (in case of OpenSourceSDRLab H2)
|
||||
set_menu_color(Color::grey());
|
||||
set_ui_hide_numeric_battery(true); // hide the numeric battery by default - no space to display it
|
||||
|
||||
// Default values for recon app.
|
||||
set_recon_autosave_freqs(false);
|
||||
|
@ -954,6 +955,13 @@ bool ui_hide_fake_brightness() {
|
|||
return data->ui_config2.hide_fake_brightness;
|
||||
}
|
||||
|
||||
bool ui_hide_numeric_battery() {
|
||||
return data->ui_config2.hide_numeric_battery;
|
||||
}
|
||||
bool ui_hide_battery_icon() {
|
||||
return data->ui_config2.hide_battery_icon;
|
||||
}
|
||||
|
||||
void set_ui_hide_speaker(bool v) {
|
||||
data->ui_config2.hide_speaker = v;
|
||||
}
|
||||
|
@ -986,6 +994,12 @@ void set_ui_hide_sd_card(bool v) {
|
|||
void set_ui_hide_fake_brightness(bool v) {
|
||||
data->ui_config2.hide_fake_brightness = v;
|
||||
}
|
||||
void set_ui_hide_numeric_battery(bool v) {
|
||||
data->ui_config2.hide_numeric_battery = v;
|
||||
}
|
||||
void set_ui_hide_battery_icon(bool v) {
|
||||
data->ui_config2.hide_battery_icon = v;
|
||||
}
|
||||
|
||||
/* Converter */
|
||||
bool config_converter() {
|
||||
|
@ -1248,6 +1262,8 @@ bool debug_dump() {
|
|||
pmem_dump_file.write_line("ui_config2 hide_sd_card: " + to_string_dec_uint(data->ui_config2.hide_sd_card));
|
||||
pmem_dump_file.write_line("ui_config2 hide_mute: " + to_string_dec_uint(data->ui_config2.hide_mute));
|
||||
pmem_dump_file.write_line("ui_config2 hide_fake_brightness: " + to_string_dec_uint(data->ui_config2.hide_fake_brightness));
|
||||
pmem_dump_file.write_line("ui_config2 hide_battery_icon: " + to_string_dec_uint(data->ui_config2.hide_battery_icon));
|
||||
pmem_dump_file.write_line("ui_config2 hide_numeric_battery: " + to_string_dec_uint(data->ui_config2.hide_numeric_battery));
|
||||
|
||||
// misc_config bits
|
||||
pmem_dump_file.write_line("misc_config config_audio_mute: " + to_string_dec_int(config_audio_mute()));
|
||||
|
|
|
@ -335,6 +335,8 @@ bool ui_hide_sleep();
|
|||
bool ui_hide_bias_tee();
|
||||
bool ui_hide_clock();
|
||||
bool ui_hide_fake_brightness();
|
||||
bool ui_hide_numeric_battery();
|
||||
bool ui_hide_battery_icon();
|
||||
bool ui_hide_sd_card();
|
||||
void set_ui_hide_speaker(bool v);
|
||||
void set_ui_hide_mute(bool v);
|
||||
|
@ -345,6 +347,8 @@ void set_ui_hide_sleep(bool v);
|
|||
void set_ui_hide_bias_tee(bool v);
|
||||
void set_ui_hide_clock(bool v);
|
||||
void set_ui_hide_fake_brightness(bool v);
|
||||
void set_ui_hide_numeric_battery(bool v);
|
||||
void set_ui_hide_battery_icon(bool v);
|
||||
void set_ui_hide_sd_card(bool v);
|
||||
|
||||
// sd persisting settings
|
||||
|
|
|
@ -2044,6 +2044,135 @@ bool TextField::on_touch(TouchEvent event) {
|
|||
return false;
|
||||
}
|
||||
|
||||
/* BatteryIcon *************************************************************/
|
||||
|
||||
BatteryIcon::BatteryIcon(Rect parent_rect, uint8_t percent)
|
||||
: Widget(parent_rect) {
|
||||
this->set_battery(percent, false);
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
void BatteryIcon::getAccessibilityText(std::string& result) {
|
||||
result = to_string_dec_uint(percent_) + "%";
|
||||
}
|
||||
void BatteryIcon::getWidgetName(std::string& result) {
|
||||
result = "Battery percent";
|
||||
}
|
||||
|
||||
void BatteryIcon::set_battery(uint8_t percentage, bool charge) {
|
||||
if (charge == charge_ && percent_ == percentage) return;
|
||||
percent_ = percentage;
|
||||
charge_ = charge;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
bool BatteryIcon::on_key(KeyEvent key) {
|
||||
if (key == KeyEvent::Select && on_select) {
|
||||
on_select();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BatteryIcon::on_touch(TouchEvent event) {
|
||||
if (event.type == TouchEvent::Type::Start) {
|
||||
focus();
|
||||
return true;
|
||||
}
|
||||
if (event.type == TouchEvent::Type::End && on_select) {
|
||||
on_select();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void BatteryIcon::paint(Painter& painter) {
|
||||
ui::Rect rect = screen_rect(); // 10, 1 * 16
|
||||
painter.fill_rectangle(rect, has_focus() || highlighted() ? Color::light_grey() : Color::dark_grey()); // clear
|
||||
ui::Color battColor = (charge_) ? Color::cyan() : Color::green();
|
||||
// batt body:
|
||||
painter.draw_vline({rect.left() + 1, rect.top() + 2}, rect.height() - 4, battColor);
|
||||
painter.draw_vline({rect.right() - 2, rect.top() + 2}, rect.height() - 4, battColor);
|
||||
painter.draw_hline({rect.left() + 1, rect.top() + 2}, rect.width() - 2, battColor);
|
||||
painter.draw_hline({rect.left() + 1, rect.bottom() - 2}, rect.width() - 2, battColor);
|
||||
// batt cap:
|
||||
painter.draw_hline({rect.left() + 3, rect.top() + 1}, rect.width() - 6, battColor);
|
||||
painter.draw_hline({rect.left() + 3, 0}, rect.width() - 6, battColor);
|
||||
if (percent_ > 100) { // error / unk
|
||||
painter.draw_string({rect.left() + 2, rect.top() + 3}, font::fixed_5x8, Color::white(), Color::dark_grey(), "?");
|
||||
return;
|
||||
}
|
||||
int8_t ppx = (rect.bottom() - 3) - (rect.top() + 2); // 11px max height to draw bars
|
||||
int8_t ptd = (int8_t)((static_cast<float>(percent_) / 100.0f) * (float)ppx + 0.5); // pixels to draw
|
||||
int8_t pp = ppx - ptd; // pixels to start from
|
||||
|
||||
if (percent_ >= 70)
|
||||
battColor = Color::green();
|
||||
else if (percent_ >= 40)
|
||||
battColor = Color::orange();
|
||||
else
|
||||
battColor = Color::red();
|
||||
// fill the bars
|
||||
for (int y = pp; y < ppx; y++) {
|
||||
painter.draw_hline({rect.left() + 2, rect.top() + 3 + y}, rect.width() - 4, battColor);
|
||||
}
|
||||
}
|
||||
|
||||
/* BatteryTextField *************************************************************/
|
||||
|
||||
BatteryTextField::BatteryTextField(Rect parent_rect, uint8_t percent)
|
||||
: Widget(parent_rect) {
|
||||
this->set_battery(percent, false);
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
void BatteryTextField::paint(Painter& painter) {
|
||||
Color bg = has_focus() || highlighted() ? Color::light_grey() : Color::dark_grey();
|
||||
ui::Rect rect = screen_rect(); // 2 * 8, 1 * 16
|
||||
painter.fill_rectangle(rect, bg); // clear
|
||||
std::string txt_batt = percent_ <= 100 ? to_string_dec_uint(percent_) : "UNK";
|
||||
int xdelta = 0;
|
||||
if (txt_batt.length() == 1)
|
||||
xdelta = 5;
|
||||
else if (txt_batt.length() == 2)
|
||||
xdelta = 2;
|
||||
painter.draw_string({rect.left() + xdelta, rect.top()}, font::fixed_5x8, Color::white(), bg, txt_batt);
|
||||
painter.draw_string({rect.left(), rect.top() + 8}, font::fixed_5x8, Color::white(), bg, (charge_) ? "+%" : " %");
|
||||
}
|
||||
|
||||
void BatteryTextField::getAccessibilityText(std::string& result) {
|
||||
result = to_string_dec_uint(percent_) + "%";
|
||||
}
|
||||
void BatteryTextField::getWidgetName(std::string& result) {
|
||||
result = "Battery percent";
|
||||
}
|
||||
|
||||
void BatteryTextField::set_battery(uint8_t percentage, bool charge) {
|
||||
if (charge == charge_ && percent_ == percentage) return;
|
||||
charge_ = charge;
|
||||
percent_ = percentage;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
bool BatteryTextField::on_key(KeyEvent key) {
|
||||
if (key == KeyEvent::Select && on_select) {
|
||||
on_select();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BatteryTextField::on_touch(TouchEvent event) {
|
||||
if (event.type == TouchEvent::Type::Start) {
|
||||
focus();
|
||||
return true;
|
||||
}
|
||||
if (event.type == TouchEvent::Type::End && on_select) {
|
||||
on_select();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* NumberField ***********************************************************/
|
||||
|
||||
NumberField::NumberField(
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "portapack.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "ui/ui_font_fixed_5x8.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
@ -781,6 +783,52 @@ class TextField : public Text {
|
|||
using Text::set;
|
||||
};
|
||||
|
||||
class BatteryTextField : public Widget {
|
||||
public:
|
||||
std::function<void()> on_select{};
|
||||
|
||||
BatteryTextField(Rect parent_rect, uint8_t percent);
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void set_battery(uint8_t percentage, bool charge);
|
||||
void set_text(std::string_view value);
|
||||
|
||||
bool on_key(KeyEvent key) override;
|
||||
bool on_touch(TouchEvent event) override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
uint8_t percent_{102};
|
||||
bool charge_{false};
|
||||
|
||||
static constexpr Style style{
|
||||
.font = font::fixed_5x8,
|
||||
.background = Color::dark_grey(),
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
};
|
||||
|
||||
class BatteryIcon : public Widget {
|
||||
public:
|
||||
std::function<void()> on_select{};
|
||||
|
||||
BatteryIcon(Rect parent_rect, uint8_t percent);
|
||||
void paint(Painter& painter) override;
|
||||
void set_battery(uint8_t percentage, bool charge);
|
||||
|
||||
bool on_key(KeyEvent key) override;
|
||||
bool on_touch(TouchEvent event) override;
|
||||
|
||||
void getAccessibilityText(std::string& result) override;
|
||||
void getWidgetName(std::string& result) override;
|
||||
|
||||
private:
|
||||
uint8_t percent_{102};
|
||||
bool charge_{false};
|
||||
};
|
||||
|
||||
class NumberField : public Widget {
|
||||
public:
|
||||
std::function<void(NumberField&)> on_select{};
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 167 B |
Binary file not shown.
After Width: | Height: | Size: 188 B |
|
@ -1,3 +1,27 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
#
|
||||
# Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
# Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
#
|
||||
# Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
# Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
if_succeed = False
|
||||
|
||||
|
||||
def get_gcc_version(elf_file):
|
||||
global if_succeed
|
||||
|
||||
output = subprocess.check_output(['readelf', '-p', '.comment', elf_file])
|
||||
output = output.decode('utf-8')
|
||||
lines = output.split('\n')
|
||||
|
||||
for line in lines:
|
||||
if 'GCC:' in line:
|
||||
version_info = line.split('GCC:')[1].strip()
|
||||
if_succeed = True
|
||||
return version_info
|
||||
|
||||
if not if_succeed: # didn't use try except here cuz don't need to break compile if this is bad result anyway
|
||||
return None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
current_dir = subprocess.check_output(['pwd'])
|
||||
print(f"current dir: {current_dir}")
|
||||
print("usage python check_gcc_version_from_elf.py xxx.elf")
|
||||
sys.exit(1)
|
||||
|
||||
elf_file = sys.argv[1]
|
||||
version_info = get_gcc_version(elf_file)
|
||||
|
||||
if version_info is not None:
|
||||
print(f"real gcc version readed from elf: {version_info}")
|
||||
else:
|
||||
print("something went wrong when checking gcc version don't worry tho, it's not deadly issue. skip.")
|
|
@ -23,11 +23,11 @@ import sys
|
|||
import struct
|
||||
from PIL import Image
|
||||
|
||||
outfile = open('../../sdcard/ADSB/world_map.bin', 'wb')
|
||||
outfile = open('../../sdcard/SYS/ADSB/world_map.bin', 'wb')
|
||||
|
||||
# Allow for bigger images
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
im = Image.open("../../sdcard/ADSB/world_map.jpg")
|
||||
im = Image.open("../../sdcard/SYS/ADSB/world_map.jpg")
|
||||
pix = im.load()
|
||||
# Write as unsigned short (2 bytes) as little endian
|
||||
outfile.write(struct.pack('<H', im.size[0]))
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
# Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
# Copyright (C) 2024 Mark Thompson
|
||||
# Copyleft (ɔ) 2024 zxkmm with the GPL license
|
||||
#
|
||||
# This file is part of PortaPack.
|
||||
#
|
||||
|
@ -23,9 +24,11 @@
|
|||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
from external_app_info import maximum_application_size
|
||||
from external_app_info import external_apps_address_start
|
||||
from external_app_info import external_apps_address_end
|
||||
import subprocess
|
||||
|
||||
usage_message = """
|
||||
PortaPack SPI flash image generator
|
||||
|
@ -34,65 +37,120 @@ Usage: <command> <application_path> <baseband_path> <output_path>
|
|||
Where paths refer to the .bin files for each component project.
|
||||
"""
|
||||
|
||||
|
||||
def read_image(path):
|
||||
f = open(path, 'rb')
|
||||
data = f.read()
|
||||
f.close()
|
||||
return data
|
||||
f = open(path, 'rb')
|
||||
data = f.read()
|
||||
f.close()
|
||||
return data
|
||||
|
||||
|
||||
def write_image(data, path):
|
||||
f = open(path, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
f = open(path, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
|
||||
def get_gcc_version_from_elf(elf_file):
|
||||
succeed = False
|
||||
|
||||
output = subprocess.check_output(['readelf', '-p', '.comment', elf_file])
|
||||
output = output.decode('utf-8')
|
||||
lines = output.split('\n')
|
||||
|
||||
for line in lines:
|
||||
if 'GCC:' in line:
|
||||
version_info = line.split('GCC:')[1].strip()
|
||||
succeed = True
|
||||
return version_info
|
||||
|
||||
if not succeed: # didn't use try except here cuz don't need to break compile if this is bad result anyway
|
||||
return None
|
||||
|
||||
|
||||
def get_gcc_version_from_elf_files_in_giving_path_or_filename_s_path(path):
|
||||
elf_files = []
|
||||
if os.path.isdir(path):
|
||||
elf_files = [os.path.join(path, f) for f in os.listdir(path) if f.endswith(".elf")]
|
||||
elif os.path.isfile(path):
|
||||
elf_files = [os.path.join(os.path.dirname(path), f) for f in os.listdir(os.path.dirname(path)) if
|
||||
f.endswith(".elf")]
|
||||
else:
|
||||
print(
|
||||
"gave path or filename is not valid") # didn't use except nor exit here cuz don't need to break compile if this is bad result anyway
|
||||
|
||||
gcc_versions = []
|
||||
for elf_file in elf_files:
|
||||
version_info = get_gcc_version_from_elf(elf_file)
|
||||
if version_info is not None:
|
||||
extract_elf_file_name = os.path.basename(elf_file)
|
||||
gcc_versions.append("gcc version of " + extract_elf_file_name + " is " + version_info)
|
||||
return gcc_versions
|
||||
|
||||
|
||||
if len(sys.argv) != 4:
|
||||
print(usage_message)
|
||||
sys.exit(-1)
|
||||
print(usage_message)
|
||||
sys.exit(-1)
|
||||
|
||||
application_image = read_image(sys.argv[1])
|
||||
baseband_image = read_image(sys.argv[2])
|
||||
output_path = sys.argv[3]
|
||||
|
||||
print("\ncheck gcc versions from all elf target\n")
|
||||
application_gcc_versions = get_gcc_version_from_elf_files_in_giving_path_or_filename_s_path(sys.argv[1])
|
||||
baseband_gcc_versions = get_gcc_version_from_elf_files_in_giving_path_or_filename_s_path(sys.argv[2])
|
||||
|
||||
for itap in application_gcc_versions:
|
||||
print(itap)
|
||||
|
||||
for itbb in baseband_gcc_versions:
|
||||
print(itbb)
|
||||
|
||||
print("\n")
|
||||
spi_size = 1048576
|
||||
|
||||
images = (
|
||||
{
|
||||
'name': 'application',
|
||||
'data': application_image,
|
||||
'size': len(application_image),
|
||||
},
|
||||
{
|
||||
'name': 'baseband',
|
||||
'data': baseband_image,
|
||||
'size': len(baseband_image),
|
||||
},
|
||||
{
|
||||
'name': 'application',
|
||||
'data': application_image,
|
||||
'size': len(application_image),
|
||||
},
|
||||
{
|
||||
'name': 'baseband',
|
||||
'data': baseband_image,
|
||||
'size': len(baseband_image),
|
||||
},
|
||||
)
|
||||
|
||||
spi_image = bytearray()
|
||||
spi_image_default_byte = bytearray((255,))
|
||||
|
||||
for image in images:
|
||||
if len(image['data']) > image['size']:
|
||||
raise RuntimeError('data for image "%(name)s" is longer than 0x%(size)x bytes 0x%(sz)x' % {'name':image['name'], 'size':image['size'], 'sz':len(image['data'])})
|
||||
pad_size = image['size'] - len(image['data'])
|
||||
padded_data = image['data'] + (spi_image_default_byte * pad_size)
|
||||
spi_image += padded_data
|
||||
if len(image['data']) > image['size']:
|
||||
raise RuntimeError(
|
||||
'data for image "%(name)s" is longer than 0x%(size)x bytes 0x%(sz)x' % {'name': image['name'],
|
||||
'size': image['size'],
|
||||
'sz': len(image['data'])})
|
||||
pad_size = image['size'] - len(image['data'])
|
||||
padded_data = image['data'] + (spi_image_default_byte * pad_size)
|
||||
spi_image += padded_data
|
||||
|
||||
if len(spi_image) > spi_size - 4:
|
||||
raise RuntimeError('SPI flash image size of %d exceeds device size of %d bytes' % (len(spi_image) + 4, spi_size))
|
||||
raise RuntimeError('SPI flash image size of %d exceeds device size of %d bytes' % (len(spi_image) + 4, spi_size))
|
||||
|
||||
pad_size = spi_size - 4 - len(spi_image)
|
||||
for i in range(pad_size):
|
||||
spi_image += spi_image_default_byte
|
||||
spi_image += spi_image_default_byte
|
||||
|
||||
# quick "add up the words" checksum, and check for possible references to code in external apps
|
||||
checksum = 0
|
||||
for i in range(0, len(spi_image), 4):
|
||||
snippet = spi_image[i:i+4]
|
||||
val = int.from_bytes(snippet, byteorder='little')
|
||||
checksum += val
|
||||
if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size):
|
||||
print ("WARNING: Possible external code address", hex(val), "at offset", hex(i), "in", sys.argv[3])
|
||||
snippet = spi_image[i:i + 4]
|
||||
val = int.from_bytes(snippet, byteorder='little')
|
||||
checksum += val
|
||||
if (val >= external_apps_address_start) and (val < external_apps_address_end) and (
|
||||
(val & 0xFFFF) < maximum_application_size):
|
||||
print("WARNING: Possible external code address", hex(val), "at offset", hex(i), "in", sys.argv[3])
|
||||
|
||||
final_checksum = 0
|
||||
checksum = (final_checksum - checksum) & 0xFFFFFFFF
|
||||
|
@ -102,4 +160,4 @@ spi_image += checksum.to_bytes(4, 'little')
|
|||
write_image(spi_image, output_path)
|
||||
|
||||
percent_remaining = round(1000 * pad_size / spi_size) / 10;
|
||||
print ("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
|
||||
print("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
|
||||
|
|
Loading…
Reference in New Issue