Compare commits

...

93 Commits

Author SHA1 Message Date
sommermorgentraum 8eaeeb6b7f
Merge 6c37d883a0 into 0ea8453e8a 2024-04-30 15:06:04 +00:00
zxkmm 6c37d883a0 merge new changes 2024-04-30 23:05:53 +08:00
sommermorgentraum 0ea8453e8a
tune output order (#2132) 2024-04-30 08:07:39 -05:00
sommermorgentraum 1ffedace7b
replace_my_ide_s_default_comment (#2131) 2024-04-30 08:08:21 +02:00
Mark Thompson e43f7a7980
Change flashing message to match LED behavior (#2130) 2024-04-29 22:29:46 +02:00
Totoo f6a2d21624
support for battery if any + TPMS app to ext (#2129)
* batt initial
* batt widgets
* settings modify
* batt info screen
* ability to hide icon
* battView
* redo tmps
* hide curr + charge if no data
* fix flashing
2024-04-29 17:38:27 +02:00
sommermorgentraum f572b00391
add real gcc ver chk (#2118) 2024-04-29 17:36:30 +02:00
Totoo d74fd527e2
TPMS to ext app (#2128)
* initial

* fix
2024-04-29 13:30:21 +02:00
zxkmm 5d7cfda97b merge new changes 2024-04-28 15:33:50 +08:00
zxkmm 9fc45b32c9 merge new changes:wq 2024-04-18 18:17:03 +08:00
sommermorgentraum b27a313118
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-04-16 21:52:52 +08:00
zxkmm f2c86862c0 merge new changes 2024-04-11 22:53:52 +08:00
sommermorgentraum 8c0715408d
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-04-08 13:47:37 +08:00
sommermorgentraum 3eb6cf47ae
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-04-07 14:08:33 +08:00
sommermorgentraum c288f703da
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-04-07 10:08:03 +08:00
zxkmm 208a89090a Merge new changes 2024-04-06 11:37:35 +08:00
zxkmm 6b3ea87d86 Merge branch 'next' into sdcard_refactor_firmware_part 2024-04-06 00:48:13 +08:00
zxkmm 17b9231601 merge new changes 2024-04-04 10:00:27 +08:00
zxkmm cc89e2af9e log for automove 2024-04-04 09:45:40 +08:00
zxkmm 77ba7bde98 remove debug things 2024-04-04 09:30:55 +08:00
zxkmm e2940a52f2 comment 2024-04-04 00:26:47 +08:00
zxkmm 348ef17131 merge new changes 2024-04-04 00:10:12 +08:00
zxkmm c00dfeed3f worker tune per Nother request 2024-04-04 00:04:41 +08:00
zxkmm 7cbba2d2e7 merge new changes 2024-04-03 16:01:13 +08:00
zxkmm c36c9c9389 format 2024-04-03 15:49:59 +08:00
zxkmm a00543b4f8 format sstv 2024-04-03 15:46:20 +08:00
zxkmm 75a930c1fa format 2024-04-03 15:36:12 +08:00
zxkmm d3f618fa5b format 2024-04-03 15:27:51 +08:00
zxkmm 6d2b290daa gitignore 2024-04-03 14:48:07 +08:00
zxkmm 9e67e250b2 path change per requests 2024-04-03 14:47:03 +08:00
zxkmm 6b4abbc5f7 textual change for review requrest 2024-04-03 13:51:55 +08:00
zxkmm 1b410675f6 format 2024-04-03 12:42:30 +08:00
zxkmm 24ff586155 clean up 2024-04-03 12:37:36 +08:00
zxkmm 6cdcb16a3a fix bad path decleration 2024-04-03 11:43:28 +08:00
zxkmm 7a3b4966b4 modal layout tune 2024-04-03 08:29:57 +08:00
zxkmm 50af3c605f rename AUTO_REMOVE to AUTO_MOVE typo 2024-04-03 08:13:57 +08:00
zxkmm 6a56d743fc worker add a flag 2024-04-03 08:08:21 +08:00
zxkmm 2a903bf9e4 merge new changes, fix worker 2024-04-03 07:47:17 +08:00
zxkmm d150e39852 merge latest change 2024-04-02 23:28:57 +08:00
zxkmm d3e8260318 fix lambda logic 2024-04-02 22:56:19 +08:00
zxkmm fa596227ef textual 2024-04-02 20:39:51 +08:00
zxkmm d4296ed434 sstv 2024-04-02 19:55:17 +08:00
zxkmm 923324e931 remove debug things 2024-04-02 17:34:43 +08:00
zxkmm 98c1b05e81 textual 2024-04-02 16:52:10 +08:00
zxkmm 4ab6a38507 add auto mover 2024-04-02 16:03:12 +08:00
zxkmm 7d48624a78 move user profile into root 2024-04-02 14:48:44 +08:00
zxkmm 6c36d45005 rename RES into SYS, sorry for the typo above 2024-04-02 13:52:24 +08:00
zxkmm 940c2d7682 rename RES into USR 2024-04-02 13:47:34 +08:00
sommermorgentraum a08ccf4f2e
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-03-31 14:56:51 +08:00
zxkmm 5e10a41331 format 2024-03-30 20:45:19 +08:00
zxkmm a0a2ff9724 format 2024-03-30 20:41:34 +08:00
zxkmm 04966f2e0f format 2024-03-30 20:36:07 +08:00
zxkmm 5b5b5333e9 textual 2024-03-30 20:29:46 +08:00
sommermorgentraum 8d032438bd
Merge branch 'next' into sdcard_refactor_firmware_part 2024-03-30 20:24:45 +08:00
zxkmm 9460177fc9 fix recon and scanner casued by new changes 2024-03-30 20:23:53 +08:00
zxkmm 675925269c clean 2024-03-30 18:41:22 +08:00
zxkmm 119ebfe90e analog audio 2024-03-30 18:07:55 +08:00
zxkmm 457eb71014 fix for merged 2024-03-30 17:09:58 +08:00
sommermorgentraum 6fb09f6d59
Merge branch 'next' into sdcard_refactor_firmware_part 2024-03-30 16:06:00 +08:00
not tre mann e38c370d7d
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-03-15 21:57:26 +08:00
not tre mann b04bd4c0d9
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-03-15 12:01:35 +08:00
zxkmm c5039f2eb0 fix for comment 2024-03-15 00:49:54 +08:00
zxkmm 93825234f3 fix for comment 2024-03-15 00:43:16 +08:00
zxkmm 5181923fc6 fixed bug caused by 3a3c79a8ab 2024-03-14 11:37:31 +08:00
zxkmm 1451d49f8a revert 3a3c79a8ab because it cause a bug 2024-03-14 10:44:20 +08:00
not tre mann 3cd9faa24d
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-03-14 10:06:41 +08:00
not tre mann 6d638698d2
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-03-14 01:27:11 +08:00
zxkmm 3a3c79a8ab decided to let user can pop dir even in file picking view 2024-03-14 01:24:06 +08:00
zxkmm fa8ed6e9dd remove unneeded code in push and pop dir 2024-03-14 00:28:31 +08:00
not tre mann 7b4a79ee38
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-03-14 00:01:02 +08:00
not tre mann 7307703692
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-03-13 23:26:12 +08:00
zxkmm 7a52068de8 fix sdcard detect logic 2024-03-13 16:35:07 +08:00
zxkmm 30965ef083 fix shitty path connect 2024-03-13 16:32:37 +08:00
zxkmm 75eeab2368 remove short link 2024-03-13 15:59:36 +08:00
zxkmm 5f5aeb96f3 sstv remove duplict code 2024-03-13 15:57:34 +08:00
zxkmm bb774d362d sdcard_status_guard 2024-03-13 12:36:56 +08:00
zxkmm 4573a78ce0 format and textual 2024-03-13 12:03:51 +08:00
zxkmm 62b8e86381 format 2024-03-13 11:59:00 +08:00
zxkmm 77145d0514 add warning per Nother's request 2024-03-13 11:53:23 +08:00
zxkmm 8bd2f5bc1f splash redirect 2024-03-13 00:01:30 +08:00
zxkmm 7c61e65c0b textual 2024-03-12 21:19:58 +08:00
zxkmm 38c001bd1d textual 2024-03-12 20:59:59 +08:00
zxkmm 19f5d3db93 fix recon logic 2024-03-12 19:05:29 +08:00
zxkmm dc7f5ae35a implement user file cover res file if same name per request by @gullradriel - FREQMAN 2024-03-12 16:16:35 +08:00
zxkmm a95871162d implement user file cover res file if same name per request by @gullradriel - SSTV 2024-03-12 15:50:40 +08:00
zxkmm 30b4cc4e60 implement user file cover res file if same name per request by @gullradriel - SSTV 2024-03-12 15:49:57 +08:00
not tre mann 94ca23fc01
Merge branch 'portapack-mayhem:next' into sdcard_refactor_firmware_part 2024-03-12 12:01:25 +08:00
zxkmm 3167b584de git ignore 2024-03-11 16:52:54 +08:00
zxkmm e7543d102c rename SYS into RES 2024-03-11 16:36:39 +08:00
not tre mann 53b667abca
Merge branch 'next' into sdcard_refactor_firmware_part 2024-03-11 08:49:38 +08:00
zxkmm 8c9e64e61f fix sstv read file logic logic 2024-03-11 00:43:12 +08:00
zxkmm 1f26de2f4a merge #1965 into this branch 2024-03-10 21:31:37 +08:00
zxkmm 78e6ba9f70 seperate firmware change and sdcard change - firmware part 2024-03-10 21:07:48 +08:00
73 changed files with 1838 additions and 377 deletions

6
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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};

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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};

View File

@ -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++;
}
}
}

View File

@ -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};

View File

@ -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

View File

@ -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__*/

View File

@ -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;

View File

@ -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

View File

@ -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"};

View File

@ -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);

View File

@ -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();
}
};
}

View File

@ -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();

View File

@ -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};

View File

@ -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);
};

View File

@ -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.

View File

@ -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));
};

View File

@ -337,7 +337,7 @@ ReconView::ReconView(NavigationView& nav)
// set record View
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
u"AUTO_AUDIO", 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);
}

View File

@ -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;

View File

@ -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.");
}
};
};

View File

@ -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

View File

@ -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())

View File

@ -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) {

View File

@ -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 {

View File

@ -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);

View File

@ -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"};

View File

@ -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;
}

View File

@ -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:

View File

@ -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;

View File

@ -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},

View File

@ -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;

View File

@ -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,

View File

@ -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
)

View File

@ -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
}

View File

@ -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();

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2024 Mark Thompson
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "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
};
}

View File

@ -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

View File

@ -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__*/

View File

@ -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'.');

View File

@ -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) {

View File

@ -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",
};

View File

@ -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__ */

View File

@ -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) {

View File

@ -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. */

View File

@ -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.

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View 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();
}

View 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.
*
@ -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",
};
};

View File

@ -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(

View File

@ -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

View File

@ -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__ */

111
firmware/common/battery.cpp Normal file
View File

@ -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

View File

@ -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

View File

@ -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__*/

View File

@ -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.
*

View File

@ -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 */

View File

@ -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()));

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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.")

View File

@ -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]))

View File

@ -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, "%)")