mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-15 19:13:00 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fc08d4ead9 | |||
| 07cae4a298 | |||
| e88e0b5f8f | |||
| 131523d726 | |||
| 24d15c1643 | |||
| 4a83118557 | |||
| ff54f59d38 | |||
| 6b8274d792 | |||
| 1fc84e7eac | |||
| d31857c21e | |||
| 52b2cb241a |
@@ -27,7 +27,8 @@ This repository expands upon the previous work by many people and aims to consta
|
||||
|
||||
## What to buy?
|
||||
|
||||
:heavy_check_mark:  The fabulous [PortaPack H4M Mayhem](https://opensourcesdrlab.com/products/h4m-receiver-and-spectrum-analyzer), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://grabify.link/99SAMT). Join their giveaways on discord (check the badge on top).
|
||||
<!---not direct to h4m but to opensourcesdrlab https://grabify.link/TUOLYI--->
|
||||
:heavy_check_mark:  The fabulous [PortaPack H4M Mayhem](https://grabify.link/0STXCA), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://grabify.link/99SAMT). Join their giveaways on discord (check the badge on top).
|
||||
|
||||
:heavy_check_mark: A recommended one is this [PortaPack H2](https://grabify.link/P9CO0J), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/3d-printed-enclosure).
|
||||
|
||||
|
||||
@@ -244,6 +244,7 @@ set(CPPSRC
|
||||
hw/si5351.cpp
|
||||
hw/spi_pp.cpp
|
||||
hw/touch_adc.cpp
|
||||
ook_file.cpp
|
||||
ui_baseband_stats_view.cpp
|
||||
ui_navigation.cpp
|
||||
ui_record_view.cpp
|
||||
@@ -302,7 +303,6 @@ set(CPPSRC
|
||||
apps/ui_rds.cpp
|
||||
apps/ui_recon_settings.cpp
|
||||
apps/ui_recon.cpp
|
||||
apps/ui_remote.cpp
|
||||
apps/ui_scanner.cpp
|
||||
apps/ui_sd_over_usb.cpp
|
||||
apps/ui_sd_wipe.cpp
|
||||
|
||||
@@ -50,10 +50,10 @@ void BattinfoView::update_result() {
|
||||
text_voltage.set("UNKNOWN");
|
||||
text_current.set("-");
|
||||
text_charge.set("-");
|
||||
text_cycles.set("-");
|
||||
// text_cycles.set("-");
|
||||
text_ttef.set("-");
|
||||
text_method.set("-");
|
||||
text_warn.set("");
|
||||
// text_warn.set("");
|
||||
return;
|
||||
}
|
||||
bool uichg = false;
|
||||
@@ -96,22 +96,22 @@ void BattinfoView::update_result() {
|
||||
labels_opt.hidden(true);
|
||||
text_current.hidden(true);
|
||||
text_charge.hidden(true);
|
||||
text_cycles.hidden(true);
|
||||
// text_cycles.hidden(true);
|
||||
text_ttef.hidden(true);
|
||||
text_warn.set("");
|
||||
// text_warn.set("");
|
||||
}
|
||||
if ((valid_mask & battery::BatteryManagement::BATT_VALID_CYCLES) == battery::BatteryManagement::BATT_VALID_CYCLES) {
|
||||
text_cycles.hidden(false);
|
||||
uint16_t cycles = battery::BatteryManagement::get_cycles();
|
||||
/* if ((valid_mask & battery::BatteryManagement::BATT_VALID_CYCLES) == battery::BatteryManagement::BATT_VALID_CYCLES) {
|
||||
// text_cycles.hidden(false);
|
||||
uint16_t cycles = 0; // battery::BatteryManagement::get_cycles();
|
||||
if (cycles < 2)
|
||||
text_warn.set("SoC improves after 2 cycles");
|
||||
text_warn.set("SoC improves after each cycles");
|
||||
else
|
||||
text_warn.set("");
|
||||
text_cycles.set(to_string_dec_uint(cycles));
|
||||
// text_cycles.set(to_string_dec_uint(cycles));
|
||||
} else {
|
||||
text_cycles.hidden(true);
|
||||
// text_cycles.hidden(true);
|
||||
text_warn.set("");
|
||||
}
|
||||
} */
|
||||
if ((valid_mask & battery::BatteryManagement::BATT_VALID_TTEF) == battery::BatteryManagement::BATT_VALID_TTEF) {
|
||||
text_ttef.hidden(false);
|
||||
float ttef = 0;
|
||||
@@ -160,8 +160,8 @@ BattinfoView::BattinfoView(NavigationView& nav)
|
||||
&text_method,
|
||||
&button_mode,
|
||||
&button_exit,
|
||||
&text_cycles,
|
||||
&text_warn,
|
||||
// &text_cycles,
|
||||
// &text_warn,
|
||||
&text_ttef});
|
||||
|
||||
button_exit.on_select = [this, &nav](Button&) {
|
||||
|
||||
@@ -63,7 +63,7 @@ class BattinfoView : public View {
|
||||
{{2 * 8, 4 * 16}, "Current:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 5 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 6 * 16}, "TTF/E:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 7 * 16}, "Cycles:", Theme::getInstance()->fg_light->foreground},
|
||||
// {{2 * 8, 7 * 16}, "Cycles:", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 10 * 16}, "Change method:", Theme::getInstance()->fg_light->foreground},
|
||||
};
|
||||
|
||||
@@ -85,13 +85,13 @@ class BattinfoView : public View {
|
||||
Text text_ttef{
|
||||
{13 * 8, 6 * 16, 10 * 16, 16},
|
||||
"-"};
|
||||
Text text_cycles{
|
||||
/* Text text_cycles{
|
||||
{13 * 8, 7 * 16, 10 * 16, 16},
|
||||
"-"};
|
||||
|
||||
Text text_warn{
|
||||
{2 * 8, 8 * 16, 30 * 8, 2 * 16},
|
||||
""};
|
||||
{1 * 8, 8 * 16, 30 * 8, 2 * 16},
|
||||
""}; */
|
||||
|
||||
Button button_mode{
|
||||
{2 * 8, 11 * 16 + 5, 5 * 16, 32},
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <algorithm>
|
||||
#include "ui_fileman.hpp"
|
||||
#include "ui_playlist.hpp"
|
||||
#include "ui_remote.hpp"
|
||||
#include "ui_ss_viewer.hpp"
|
||||
#include "ui_bmp_file_viewer.hpp"
|
||||
#include "ui_text_editor.hpp"
|
||||
@@ -704,10 +703,11 @@ bool FileManagerView::handle_file_open() {
|
||||
|
||||
reload_current(false);
|
||||
return true;
|
||||
} else if (path_iequal(rem_ext, ext)) {
|
||||
}
|
||||
/*else if (path_iequal(rem_ext, ext)) {
|
||||
nav_.push<RemoteView>(path);
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ SIGFRXView::~SIGFRXView() {
|
||||
void SIGFRXView::paint(Painter& painter) {
|
||||
uint8_t i, xp;
|
||||
|
||||
// portapack::display.drawBMP({0, 302-160}, fox_bmp);
|
||||
// portapack::display.draw_bmp_from_bmp_hex_arr({0, 302-160}, fox_bmp);
|
||||
portapack::display.fill_rectangle({0, 16, 240, 160 - 16}, Theme::getInstance()->bg_darkest->foreground);
|
||||
for (i = 0; i < 6; i++) {
|
||||
xp = sigfrx_marks[i * 3];
|
||||
|
||||
@@ -119,7 +119,7 @@ bool SplashViewer::on_key(const KeyEvent key) {
|
||||
void SplashViewer::paint(Painter& painter) {
|
||||
painter.fill_rectangle({0, 0, screen_width, screen_height}, Color::black());
|
||||
|
||||
if (!portapack::display.drawBMP2({0, 0}, path_)) {
|
||||
if (!portapack::display.draw_bmp_from_sdcard_file({0, 0}, path_)) {
|
||||
painter.draw_string({10, 160}, *Theme::getInstance()->bg_darkest, "Not a valid splash image.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "audio.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
@@ -31,6 +32,18 @@ using namespace ui;
|
||||
|
||||
namespace ui {
|
||||
|
||||
std::string SubGhzDRecentEntry::to_csv() {
|
||||
std::string csv = ";";
|
||||
csv += SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)sensorType);
|
||||
csv += ";" + to_string_dec_uint(bits) + ";";
|
||||
csv += to_string_hex(data, 64 / 4);
|
||||
return csv;
|
||||
}
|
||||
|
||||
void SubGhzDLogger::log_data(SubGhzDRecentEntry& data) {
|
||||
log_file.write_entry(data.to_csv());
|
||||
}
|
||||
|
||||
void SubGhzDRecentEntryDetailView::update_data() {
|
||||
// process protocol data
|
||||
parseProtocol();
|
||||
@@ -76,16 +89,25 @@ SubGhzDView::SubGhzDView(NavigationView& nav)
|
||||
&field_vga,
|
||||
&field_frequency,
|
||||
&button_clear_list,
|
||||
&check_log,
|
||||
&recent_entries_view});
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_subghzd);
|
||||
logger = std::make_unique<SubGhzDLogger>();
|
||||
|
||||
button_clear_list.on_select = [this](Button&) {
|
||||
recent.clear();
|
||||
recent_entries_view.set_dirty();
|
||||
};
|
||||
field_frequency.set_step(10000);
|
||||
|
||||
check_log.on_select = [this](Checkbox&, bool v) {
|
||||
logging = v;
|
||||
if (logger && logging) {
|
||||
logger->append(logs_dir.string() + "/SUBGHZDLOG_" + to_string_timestamp(rtc_time::now()) + ".CSV");
|
||||
logger->write_header();
|
||||
}
|
||||
};
|
||||
check_log.set_value(logging);
|
||||
const Rect content_rect{0, header_height, screen_width, screen_height - header_height};
|
||||
recent_entries_view.set_parent_rect(content_rect);
|
||||
recent_entries_view.on_select = [this](const SubGhzDRecentEntry& entry) {
|
||||
@@ -107,6 +129,9 @@ void SubGhzDView::on_tick_second() {
|
||||
|
||||
void SubGhzDView::on_data(const SubGhzDDataMessage* data) {
|
||||
SubGhzDRecentEntry key{data->sensorType, data->data, data->bits};
|
||||
if (logger && logging) {
|
||||
logger->log_data(key);
|
||||
}
|
||||
auto matching_recent = find(recent, key.key());
|
||||
if (matching_recent != std::end(recent)) {
|
||||
// Found within. Move to front of list, increment counter.
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
#include "../baseband/fprotos/subghztypes.hpp"
|
||||
@@ -68,6 +69,23 @@ struct SubGhzDRecentEntry {
|
||||
void reset_age() {
|
||||
age = 0;
|
||||
}
|
||||
|
||||
std::string to_csv();
|
||||
};
|
||||
|
||||
class SubGhzDLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
void log_data(SubGhzDRecentEntry& data);
|
||||
void write_header() {
|
||||
log_file.write_entry(";Type; Bits; Data;");
|
||||
}
|
||||
|
||||
private:
|
||||
LogFile log_file{};
|
||||
};
|
||||
using SubGhzDRecentEntries = RecentEntries<SubGhzDRecentEntry>;
|
||||
using SubGhzDRecentEntriesView = RecentEntriesView<SubGhzDRecentEntries>;
|
||||
@@ -93,10 +111,13 @@ class SubGhzDView : public View {
|
||||
1'750'000 /* bandwidth */,
|
||||
4'000'000 /* sampling rate */,
|
||||
ReceiverModel::Mode::AMAudio};
|
||||
bool logging = false;
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_subghzd",
|
||||
app_settings::Mode::RX,
|
||||
{}};
|
||||
{
|
||||
{"log"sv, &logging},
|
||||
}};
|
||||
|
||||
SubGhzDRecentEntries recent{};
|
||||
|
||||
@@ -118,8 +139,16 @@ class SubGhzDView : public View {
|
||||
{0, 16, 7 * 8, 32},
|
||||
"Clear"};
|
||||
|
||||
Checkbox check_log{
|
||||
{10 * 8, 18},
|
||||
3,
|
||||
"Log",
|
||||
true};
|
||||
|
||||
static constexpr auto header_height = 3 * 16;
|
||||
|
||||
std::unique_ptr<SubGhzDLogger> logger{};
|
||||
|
||||
const RecentEntriesColumns columns{{
|
||||
{"Type", 19},
|
||||
{"Bits", 4},
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "audio.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "../baseband/fprotos/fprotogeneral.hpp"
|
||||
|
||||
@@ -35,6 +36,21 @@ namespace pmem = portapack::persistent_memory;
|
||||
|
||||
namespace ui {
|
||||
|
||||
std::string WeatherRecentEntry::to_csv() {
|
||||
std::string csv = ";";
|
||||
csv += WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)sensorType);
|
||||
csv += ";" + to_string_dec_uint(id) + ";";
|
||||
csv += to_string_decimal(temp, 2) + ";";
|
||||
csv += to_string_dec_uint(humidity) + ";";
|
||||
csv += to_string_dec_uint(channel) + ";";
|
||||
csv += to_string_dec_uint(battery_low);
|
||||
return csv;
|
||||
}
|
||||
|
||||
void WeatherLogger::log_data(WeatherRecentEntry& data) {
|
||||
log_file.write_entry(data.to_csv());
|
||||
}
|
||||
|
||||
void WeatherRecentEntryDetailView::update_data() {
|
||||
// set text elements
|
||||
text_type.set(WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)entry_.sensorType));
|
||||
@@ -98,8 +114,11 @@ WeatherView::WeatherView(NavigationView& nav)
|
||||
&field_frequency,
|
||||
&options_temperature,
|
||||
&button_clear_list,
|
||||
&check_log,
|
||||
&recent_entries_view});
|
||||
|
||||
logger = std::make_unique<WeatherLogger>();
|
||||
|
||||
baseband::run_image(portapack::spi_flash::image_tag_weather);
|
||||
|
||||
button_clear_list.on_select = [this](Button&) {
|
||||
@@ -114,6 +133,15 @@ WeatherView::WeatherView(NavigationView& nav)
|
||||
};
|
||||
options_temperature.set_selected_index(weather_units_fahr, false);
|
||||
|
||||
check_log.on_select = [this](Checkbox&, bool v) {
|
||||
logging = v;
|
||||
if (logger && logging) {
|
||||
logger->append(logs_dir.string() + "/WEATHERLOG_" + to_string_timestamp(rtc_time::now()) + ".CSV");
|
||||
logger->write_header();
|
||||
}
|
||||
};
|
||||
check_log.set_value(logging);
|
||||
|
||||
const Rect content_rect{0, header_height, screen_width, screen_height - header_height};
|
||||
recent_entries_view.set_parent_rect(content_rect);
|
||||
recent_entries_view.on_select = [this](const WeatherRecentEntry& entry) {
|
||||
@@ -140,6 +168,9 @@ void WeatherView::on_tick_second() {
|
||||
|
||||
void WeatherView::on_data(const WeatherDataMessage* data) {
|
||||
WeatherRecentEntry key = process_data(data);
|
||||
if (logger && logging) {
|
||||
logger->log_data(key);
|
||||
}
|
||||
// WeatherRecentEntry key{data->sensorType, data->id, data->temp, data->humidity, data->channel, data->battery_low};
|
||||
auto matching_recent = find(recent, key.key());
|
||||
if (matching_recent != std::end(recent)) {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "log_file.hpp"
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
#include "../baseband/fprotos/weathertypes.hpp"
|
||||
@@ -78,7 +79,25 @@ struct WeatherRecentEntry {
|
||||
void reset_age() {
|
||||
age = 0;
|
||||
}
|
||||
|
||||
std::string to_csv();
|
||||
};
|
||||
|
||||
class WeatherLogger {
|
||||
public:
|
||||
Optional<File::Error> append(const std::filesystem::path& filename) {
|
||||
return log_file.append(filename);
|
||||
}
|
||||
|
||||
void log_data(WeatherRecentEntry& data);
|
||||
void write_header() {
|
||||
log_file.write_entry(";Type; id; Temp; Hum; CH; Batt");
|
||||
}
|
||||
|
||||
private:
|
||||
LogFile log_file{};
|
||||
};
|
||||
|
||||
using WeatherRecentEntries = RecentEntries<WeatherRecentEntry>;
|
||||
using WeatherRecentEntriesView = RecentEntriesView<WeatherRecentEntries>;
|
||||
|
||||
@@ -103,11 +122,13 @@ class WeatherView : public View {
|
||||
1'750'000 /* bandwidth */,
|
||||
2'000'000 /* sampling rate */,
|
||||
ReceiverModel::Mode::AMAudio};
|
||||
bool logging = false;
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_weather",
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"units_fahr"sv, &weather_units_fahr},
|
||||
{"log"sv, &logging},
|
||||
}};
|
||||
|
||||
WeatherRecentEntries recent{};
|
||||
@@ -140,8 +161,16 @@ class WeatherView : public View {
|
||||
{0, 16, 7 * 8, 32},
|
||||
"Clear"};
|
||||
|
||||
Checkbox check_log{
|
||||
{10 * 8, 18},
|
||||
3,
|
||||
"Log",
|
||||
true};
|
||||
|
||||
static constexpr auto header_height = 3 * 16;
|
||||
|
||||
std::unique_ptr<WeatherLogger> logger{};
|
||||
|
||||
const RecentEntriesColumns columns{{
|
||||
{"Type", 10},
|
||||
{"Temp", 5},
|
||||
|
||||
+705
-553
File diff suppressed because it is too large
Load Diff
+9
-5
@@ -115,9 +115,9 @@ set(EXTCPPSRC
|
||||
external/ookbrute/main.cpp
|
||||
external/ookbrute/ui_ookbrute.cpp
|
||||
|
||||
#rook
|
||||
external/rook/main.cpp
|
||||
external/rook/ui_rook.cpp
|
||||
#ook_editor
|
||||
external/ook_editor/main.cpp
|
||||
external/ook_editor/ui_ook_editor.cpp
|
||||
|
||||
#cvs_spam
|
||||
external/cvs_spam/main.cpp
|
||||
@@ -126,7 +126,10 @@ set(EXTCPPSRC
|
||||
#flippertx
|
||||
external/flippertx/main.cpp
|
||||
external/flippertx/ui_flippertx.cpp
|
||||
|
||||
|
||||
#remote
|
||||
external/remote/main.cpp
|
||||
external/remote/ui_remote.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -157,7 +160,8 @@ set(EXTAPPLIST
|
||||
random_password
|
||||
#acars_rx
|
||||
ookbrute
|
||||
rook
|
||||
ook_editor
|
||||
shoppingcart_lock
|
||||
flippertx
|
||||
remote
|
||||
)
|
||||
|
||||
+11
-5
@@ -52,7 +52,8 @@ MEMORY
|
||||
ram_external_app_cvs_spam(rwx) : org = 0xADCB0000, len = 32k
|
||||
ram_external_app_ookbrute(rwx) : org = 0xADCC0000, len = 32k
|
||||
ram_external_app_flippertx(rwx) : org = 0xADCD0000, len = 32k
|
||||
ram_external_app_rook(rwx) : org = 0xADCE0000, len = 32k
|
||||
ram_external_app_ook_editor(rwx) : org = 0xADCE0000, len = 32k
|
||||
ram_external_app_remote(rwx) : org = 0xADCF0000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -226,11 +227,11 @@ SECTIONS
|
||||
*(*ui*external_app*ookbrute*);
|
||||
} > ram_external_app_ookbrute
|
||||
|
||||
.external_app_rook : ALIGN(4) SUBALIGN(4)
|
||||
.external_app_ook_editor : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_rook.application_information));
|
||||
*(*ui*external_app*rook*);
|
||||
} > ram_external_app_rook
|
||||
KEEP(*(.external_app.app_ook_editor.application_information));
|
||||
*(*ui*external_app*ook_editor*);
|
||||
} > ram_external_app_ook_editor
|
||||
|
||||
.external_app_flippertx : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
@@ -238,4 +239,9 @@ SECTIONS
|
||||
*(*ui*external_app*flippertx*);
|
||||
} > ram_external_app_flippertx
|
||||
|
||||
.external_app_remote : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_remote.application_information));
|
||||
*(*ui*external_app*remote*);
|
||||
} > ram_external_app_remote
|
||||
}
|
||||
|
||||
Vendored
+8
-8
@@ -20,25 +20,25 @@
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_rook.hpp"
|
||||
#include "ui_ook_editor.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::rook {
|
||||
namespace ui::external_app::ook_editor {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<ROOKAppView>();
|
||||
nav.push<OOKEditorAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::rook
|
||||
} // namespace ui::external_app::ook_editor
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_rook.application_information"), used)) application_information_t _application_information_rook = {
|
||||
__attribute__((section(".external_app.app_ook_editor.application_information"), used)) application_information_t _application_information_ook_editor = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::rook::initialize_app,
|
||||
/*.externalAppEntry = */ ui::external_app::ook_editor::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "ROOK",
|
||||
/*.app_name = */ "OOKEditor",
|
||||
/*.bitmap_data = */ {
|
||||
0x20,
|
||||
0x00,
|
||||
@@ -79,4 +79,4 @@ __attribute__((section(".external_app.app_rook.application_information"), used))
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_ook */ {'P', 'O', 'O', 'K'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
|
||||
*
|
||||
* 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_ook_editor.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui::external_app::ook_editor {
|
||||
|
||||
// give focus to set button
|
||||
void OOKEditorAppView::focus() {
|
||||
button_set.focus();
|
||||
}
|
||||
|
||||
// update internal ook_data with GUI values
|
||||
void OOKEditorAppView::update_ook_data_from_app() {
|
||||
ook_data.frequency = field_frequency.value();
|
||||
ook_data.sample_rate = field_sample_rate.selected_index_value();
|
||||
ook_data.symbol_rate = field_symbol_rate.value();
|
||||
ook_data.repeat = field_repeat.value();
|
||||
ook_data.pause_symbol_duration = field_pause_symbol_duration.value();
|
||||
}
|
||||
|
||||
// `start_tx` method: Configures and begins OOK data transmission with a specific message.
|
||||
void OOKEditorAppView::start_tx() {
|
||||
// check if there is a payload
|
||||
if (ook_data.payload.length() < 1) {
|
||||
text_app_status.set("Error: no payload to tx !!");
|
||||
return;
|
||||
}
|
||||
progressbar.set_max(field_repeat.value()); // Size the progress bar accordingly to the number of repeat
|
||||
is_transmitting = true; // set transmitting flag
|
||||
button_send_stop.set_text(LanguageHelper::currentMessages[LANG_STOP]); // set button back to initial "start" state
|
||||
start_ook_file_tx(ook_data); // start the transmission
|
||||
}
|
||||
|
||||
// `stop_tx` method: Stops the transmission and resets the progress bar.
|
||||
void OOKEditorAppView::stop_tx() {
|
||||
is_transmitting = false; // set transmitting flag
|
||||
stop_ook_file_tx(); // stop transmission
|
||||
progressbar.set_value(0); // Reset progress bar to 0
|
||||
button_send_stop.set_text(LanguageHelper::currentMessages[LANG_START]); // set button back to initial "start" state
|
||||
}
|
||||
|
||||
// `on_file_changed` method: Called when a new file is loaded; parses file data into variables
|
||||
void OOKEditorAppView::on_file_changed(const fs::path& new_file_path) {
|
||||
ook_data.payload.clear(); // Clear previous payload content
|
||||
if (!read_ook_file(new_file_path, ook_data)) {
|
||||
text_app_status.set("Error loading " + new_file_path.filename().string());
|
||||
return;
|
||||
}
|
||||
field_frequency.set_value(ook_data.frequency);
|
||||
field_symbol_rate.set_value(ook_data.symbol_rate);
|
||||
field_repeat.set_value(ook_data.repeat);
|
||||
field_pause_symbol_duration.set_value(ook_data.pause_symbol_duration);
|
||||
field_sample_rate.set_by_value(ook_data.sample_rate);
|
||||
text_payload.set(ook_data.payload);
|
||||
button_send_stop.focus();
|
||||
text_app_status.set("Loaded: " + new_file_path.filename().string());
|
||||
}
|
||||
|
||||
// `on_tx_progress` method: Updates the progress bar based on transmission progress.
|
||||
void OOKEditorAppView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
progressbar.set_value(progress); // Update progress bar value
|
||||
if (done) {
|
||||
stop_tx(); // Stop transmission when progress reaches maximum
|
||||
}
|
||||
}
|
||||
|
||||
// `draw_waveform` method: Draws the waveform on the UI based on the payload data
|
||||
void OOKEditorAppView::draw_waveform() {
|
||||
// Padding reason:
|
||||
// In real-world scenarios, the signal would always start low and return low after turning off the radio.
|
||||
// `waveform_buffer` only controls drawing; the actual send logic is handled by frame_fragments.
|
||||
size_t length = ook_data.payload.length();
|
||||
|
||||
// Ensure waveform length does not exceed buffer size
|
||||
if (length + (PADDING_LEFT + PADDING_RIGHT) >= WAVEFORM_BUFFER_SIZE) {
|
||||
length = WAVEFORM_BUFFER_SIZE - (PADDING_LEFT + PADDING_RIGHT);
|
||||
}
|
||||
|
||||
// Left padding
|
||||
for (size_t i = 0; i < PADDING_LEFT; i++) {
|
||||
waveform_buffer[i] = 0;
|
||||
}
|
||||
|
||||
// Draw the actual waveform
|
||||
for (size_t n = 0; n < length; n++) {
|
||||
waveform_buffer[n + PADDING_LEFT] = (ook_data.payload[n] == '0') ? 0 : 1;
|
||||
}
|
||||
|
||||
// Right padding
|
||||
for (size_t i = length + PADDING_LEFT; i < WAVEFORM_BUFFER_SIZE; i++) {
|
||||
waveform_buffer[i] = 0;
|
||||
}
|
||||
|
||||
waveform.set_length(length + PADDING_LEFT + PADDING_RIGHT);
|
||||
waveform.set_dirty();
|
||||
}
|
||||
|
||||
// build a new path+file, make some tests, call save_ook_to_file
|
||||
void OOKEditorAppView::on_save_file(const std::string value) {
|
||||
// check if there is a payload, else Error
|
||||
if (ook_data.payload.length() < 1) {
|
||||
text_app_status.set("Err: can't save, no payload !");
|
||||
return;
|
||||
}
|
||||
ensure_directory(ook_editor_dir);
|
||||
auto new_path = ook_editor_dir / value + ".OOK";
|
||||
if (save_ook_to_file(new_path)) {
|
||||
text_app_status.set("Saved to " + new_path.string());
|
||||
} else {
|
||||
text_app_status.set("Error saving " + new_path.string());
|
||||
}
|
||||
}
|
||||
|
||||
// update ook_data from GUI and save
|
||||
bool OOKEditorAppView::save_ook_to_file(const std::filesystem::path& path) {
|
||||
update_ook_data_from_app();
|
||||
return save_ook_file(ook_data, path);
|
||||
}
|
||||
|
||||
// Destructor for `OOKEditorAppView`: Disables the transmitter and shuts down the baseband
|
||||
OOKEditorAppView::~OOKEditorAppView() {
|
||||
stop_ook_file_tx();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
// Constructor for `OOKEditorAppView`: Sets up the app view and initializes UI elements
|
||||
OOKEditorAppView::OOKEditorAppView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
// load OOK baseband
|
||||
baseband::run_image(portapack::spi_flash::image_tag_ook);
|
||||
|
||||
// add all the widgets
|
||||
add_children({&field_frequency,
|
||||
&tx_view,
|
||||
&button_send_stop,
|
||||
&label_step,
|
||||
&field_step,
|
||||
&label_sample_rate,
|
||||
&field_sample_rate,
|
||||
&label_symbol_rate,
|
||||
&field_symbol_rate,
|
||||
&label_symbol_rate_unit,
|
||||
&text_payload,
|
||||
&button_set,
|
||||
&progressbar,
|
||||
&label_repeat,
|
||||
&field_repeat,
|
||||
&label_pause_symbol_duration,
|
||||
&field_pause_symbol_duration,
|
||||
&label_pause_symbol_duration_unit,
|
||||
&label_payload,
|
||||
&text_app_status,
|
||||
&label_waveform,
|
||||
&waveform,
|
||||
&button_open,
|
||||
&button_save});
|
||||
|
||||
// Initialize default values for controls
|
||||
field_symbol_rate.set_value(100);
|
||||
field_pause_symbol_duration.set_value(100);
|
||||
field_repeat.set_value(4);
|
||||
|
||||
// Configure open ook file button
|
||||
button_open.on_select = [this](Button&) {
|
||||
auto open_view = nav_.push<FileLoadView>(".OOK");
|
||||
ensure_directory(ook_editor_dir);
|
||||
open_view->push_dir(ook_editor_dir);
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
// Postpone `on_file_changed` call until `FileLoadView` is closed
|
||||
nav_.set_on_pop([this, new_file_path]() {
|
||||
on_file_changed(new_file_path);
|
||||
button_send_stop.focus();
|
||||
draw_waveform();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// Configure save to ook file button
|
||||
button_save.on_select = [this, &nav](const ui::Button&) {
|
||||
outputFileBuffer = "";
|
||||
text_prompt(
|
||||
nav,
|
||||
outputFileBuffer,
|
||||
64,
|
||||
[this](std::string& buffer) {
|
||||
on_save_file(buffer);
|
||||
});
|
||||
};
|
||||
|
||||
// clean out loaded file name if field is changed
|
||||
field_symbol_rate.on_change = [this](int32_t) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
// clean out loaded file name if field is changed
|
||||
field_repeat.on_change = [this](int32_t) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
// clean out loaded file name if field is changed
|
||||
field_pause_symbol_duration.on_change = [this](int32_t) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
// clean out loaded file name if field is changed
|
||||
field_sample_rate.on_change = [this](size_t, int32_t) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
|
||||
// setting up FrequencyField
|
||||
field_frequency.set_value(ook_editor_tx_freq);
|
||||
|
||||
// clean out loaded file name if field is changed, save ook_editor_tx_freq
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
ook_editor_tx_freq = f;
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
|
||||
// allow typing frequency number
|
||||
field_frequency.on_edit = [this]() {
|
||||
auto freq_view = nav_.push<FrequencyKeypadView>(field_frequency.value());
|
||||
freq_view->on_changed = [this](rf::Frequency f) {
|
||||
field_frequency.set_value(f);
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
};
|
||||
};
|
||||
|
||||
// allow different steps on symbol_rate and pause_symbol_duration
|
||||
field_step.on_change = [this](size_t, int32_t value) {
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
field_symbol_rate.set_step(value);
|
||||
field_pause_symbol_duration.set_step(value);
|
||||
};
|
||||
|
||||
// Configure button to manually set payload through text input
|
||||
button_set.on_select = [this, &nav](Button&) {
|
||||
text_prompt(
|
||||
nav,
|
||||
ook_data.payload,
|
||||
100,
|
||||
[this](std::string& s) {
|
||||
text_payload.set(s);
|
||||
draw_waveform();
|
||||
text_app_status.set(""); // Clear loaded file text field
|
||||
});
|
||||
};
|
||||
|
||||
// Configure button to start or stop the transmission
|
||||
button_send_stop.on_select = [this](Button&) {
|
||||
if (!is_transmitting) {
|
||||
update_ook_data_from_app();
|
||||
start_tx(); // Begin transmission
|
||||
} else {
|
||||
stop_tx();
|
||||
}
|
||||
};
|
||||
|
||||
// initial waveform drawing (should be a single line)
|
||||
draw_waveform();
|
||||
}
|
||||
} // namespace ui::external_app::ook_editor
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __UI_OOK_EDITOR_H__
|
||||
#define __UI_OOK_EDITOR_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "ook_file.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::ook_editor {
|
||||
|
||||
#define PADDING_LEFT 1 // waveform padding
|
||||
#define PADDING_RIGHT 1 // waveform padding
|
||||
#define OOK_SAMPLERATE_DEFAULT 2280000U // Set the default Sample Rate
|
||||
#define TRANSMISSION_FREQUENCY_DEFAULT 433920000U // Sets the default transmission frequency (27 MHz).
|
||||
#define WAVEFORM_BUFFER_SIZE 550
|
||||
|
||||
class OOKEditorAppView : public View {
|
||||
public:
|
||||
void focus() override;
|
||||
OOKEditorAppView(NavigationView& nav);
|
||||
|
||||
~OOKEditorAppView();
|
||||
|
||||
std::string title() const override {
|
||||
return "OOKEditor";
|
||||
};
|
||||
|
||||
private:
|
||||
NavigationView& nav_; // Reference to the navigation system.
|
||||
uint32_t progress = 0; // Stores the current transmission progress.
|
||||
int16_t waveform_buffer[WAVEFORM_BUFFER_SIZE]; // Buffer for waveform data.
|
||||
bool is_transmitting = false; // State of transmission.
|
||||
rf::Frequency ook_editor_tx_freq{24000000}; // last used transmit frequency
|
||||
std::string outputFileBuffer{}; // buffer for output file
|
||||
ook_file_data ook_data = {0, 0, 0, 0, 0, ""}; // ook files handle
|
||||
|
||||
// draw current payload waveform
|
||||
void draw_waveform();
|
||||
|
||||
// update ook_data from GUI selection
|
||||
void update_ook_data_from_app();
|
||||
|
||||
// start transmission
|
||||
void start_tx();
|
||||
// stop transmission
|
||||
void stop_tx();
|
||||
|
||||
// Updates the transmission progress on the progress bar.
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
// Updates data when a new file is loaded.
|
||||
void on_file_changed(const std::filesystem::path& new_file_path);
|
||||
|
||||
// Prepare a new std::filesystem::path from string value and call saveFile
|
||||
void on_save_file(const std::string value);
|
||||
|
||||
// Called by on_save_file to save the current OOK parameters to the file
|
||||
bool save_ook_to_file(const std::filesystem::path& path);
|
||||
|
||||
// Registers a message handler for transmission progress updates.
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress, // Transmission progress message ID.
|
||||
[this](const Message* const p) { // Callback to handle the message.
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress, message.done);
|
||||
}};
|
||||
|
||||
// Sets transmission frequency, bandwidth, and default sample rate for OOK.
|
||||
TxRadioState radio_state_{TRANSMISSION_FREQUENCY_DEFAULT, 1750000, OOK_SAMPLERATE_DEFAULT};
|
||||
|
||||
// Settings manager for app configuration.
|
||||
app_settings::SettingsManager settings_{
|
||||
"ook_editor",
|
||||
app_settings::Mode::TX,
|
||||
{{"ook_editor_tx_freq"sv, &ook_editor_tx_freq}}};
|
||||
|
||||
// UI components for frequency and transmitter view.
|
||||
FrequencyField field_frequency{{0 * 8, 0 * 16}};
|
||||
TransmitterView2 tx_view{{20 * 7, 0 * 16}, true};
|
||||
|
||||
// Labels for various fields such as sample rate and repeat count.
|
||||
Labels label_step{{{170, 20}, "Step:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_sample_rate{{{0, 20}, "SampleRate:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_symbol_rate{{{0, 40}, "SymbolRate:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_symbol_rate_unit{{{132, 40}, "/s", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_repeat{{{154, 40}, "Repeat:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_pause_symbol_duration{{{0, 60}, "PauseSymbol:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_pause_symbol_duration_unit{{{132, 60}, "us", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_payload{{{0, 80}, "Payload:", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_waveform{{{0, 188}, "Waveform:", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
// Text field to display the various status message of the app
|
||||
Text text_app_status{{0, 160, 30 * 8, 16}, ""};
|
||||
|
||||
// OptionsField for selectable sample rates.
|
||||
OptionsField field_sample_rate{{96, 20}, 7, {{"250k", 250000U}, {"1M", 1000000U}, {"2M", 2000000U}, {"5M", 5000000U}, {"10M", 10000000U}, {"20M", 20000000U}}};
|
||||
// OptionsField for step symbol rates.
|
||||
OptionsField field_step{{210, 20}, 7, {{"1", 1}, {"10", 10}, {"100", 100}}};
|
||||
// Number fields for symbols, pause_symbol between repeat, and repeat count.
|
||||
NumberField field_symbol_rate{{96, 40}, 4, {0, 9999}, 1, '0', false};
|
||||
NumberField field_pause_symbol_duration{{96, 60}, 4, {0, 9999}, 1, '0', false};
|
||||
NumberField field_repeat{{210, 40}, 3, {1, 999}, 1, '0', false};
|
||||
|
||||
// Text field to display the payload data.
|
||||
Text text_payload{{0 * 8, 100, 30 * 8, 16}, ""};
|
||||
|
||||
// Buttons for setting configurations, opening files, and starting transmission.
|
||||
Button button_set{{0, 125, 60, 28}, LanguageHelper::currentMessages[LANG_SET]};
|
||||
Button button_open{{68, 125, 80, 28}, LanguageHelper::currentMessages[LANG_OPEN_FILE]};
|
||||
Button button_save{{154, 125, 80, 28}, LanguageHelper::currentMessages[LANG_SAVE_FILE]};
|
||||
Button button_send_stop{{80, 273, 80, 32}, LanguageHelper::currentMessages[LANG_SEND]};
|
||||
|
||||
// Progress bar to display transmission progress.
|
||||
ProgressBar progressbar{{2 * 8, 250, 208, 16}};
|
||||
|
||||
// Waveform display using waveform buffer and yellow theme color.
|
||||
Waveform waveform{{0, 208, 240, 32}, waveform_buffer, 0, 0, true, Theme::getInstance()->fg_yellow->foreground};
|
||||
};
|
||||
|
||||
}; // namespace ui::external_app::ook_editor
|
||||
|
||||
#endif /*__UI_OOK_EDITOR_H__*/
|
||||
+2
-2
@@ -26,7 +26,7 @@
|
||||
|
||||
namespace ui::external_app::ookbrute {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<OokBruteView>();
|
||||
nav.push<OOKBruteView>();
|
||||
}
|
||||
} // namespace ui::external_app::ookbrute
|
||||
|
||||
@@ -38,7 +38,7 @@ __attribute__((section(".external_app.app_ookbrute.application_information"), us
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "OokBrute",
|
||||
/*.app_name = */ "OOKBrute",
|
||||
/*.bitmap_data = */ {
|
||||
0x20,
|
||||
0x00,
|
||||
|
||||
+10
-10
@@ -31,11 +31,11 @@ using namespace ui;
|
||||
|
||||
namespace ui::external_app::ookbrute {
|
||||
|
||||
void OokBruteView::focus() {
|
||||
void OOKBruteView::focus() {
|
||||
button_startstop.focus();
|
||||
}
|
||||
|
||||
OokBruteView::OokBruteView(NavigationView& nav)
|
||||
OOKBruteView::OOKBruteView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
add_children({
|
||||
&button_startstop,
|
||||
@@ -69,7 +69,7 @@ OokBruteView::OokBruteView(NavigationView& nav)
|
||||
update_start_stop(0);
|
||||
}
|
||||
|
||||
void OokBruteView::update_start_stop(uint32_t proto) {
|
||||
void OOKBruteView::update_start_stop(uint32_t proto) {
|
||||
uint8_t bits = 12;
|
||||
switch (proto) {
|
||||
default:
|
||||
@@ -91,7 +91,7 @@ void OokBruteView::update_start_stop(uint32_t proto) {
|
||||
field_stop.set_value(max);
|
||||
}
|
||||
|
||||
void OokBruteView::validate_start_stop() {
|
||||
void OOKBruteView::validate_start_stop() {
|
||||
if (field_start.value() > field_stop.value()) {
|
||||
field_start.set_value(field_stop.value());
|
||||
}
|
||||
@@ -100,7 +100,7 @@ void OokBruteView::validate_start_stop() {
|
||||
}
|
||||
}
|
||||
|
||||
void OokBruteView::generate_packet() {
|
||||
void OOKBruteView::generate_packet() {
|
||||
uint32_t protocol = options_atkmode.selected_index_value();
|
||||
uint8_t byte = 0;
|
||||
size_t bitstream_length = 0;
|
||||
@@ -210,13 +210,13 @@ void OokBruteView::generate_packet() {
|
||||
0);
|
||||
}
|
||||
|
||||
void OokBruteView::stop() {
|
||||
void OOKBruteView::stop() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
button_startstop.set_text(LanguageHelper::currentMessages[LANG_START]);
|
||||
}
|
||||
|
||||
void OokBruteView::start() {
|
||||
void OOKBruteView::start() {
|
||||
counter = field_start.value();
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
transmitter_model.enable();
|
||||
@@ -224,7 +224,7 @@ void OokBruteView::start() {
|
||||
generate_packet();
|
||||
}
|
||||
|
||||
void OokBruteView::on_tx_progress(const bool done) {
|
||||
void OOKBruteView::on_tx_progress(const bool done) {
|
||||
if (done) {
|
||||
if (is_running) {
|
||||
counter++;
|
||||
@@ -238,7 +238,7 @@ void OokBruteView::on_tx_progress(const bool done) {
|
||||
}
|
||||
}
|
||||
|
||||
OokBruteView::~OokBruteView() {
|
||||
OOKBruteView::~OOKBruteView() {
|
||||
is_running = false;
|
||||
stop();
|
||||
}
|
||||
@@ -250,4 +250,4 @@ OokBruteView::~OokBruteView() {
|
||||
https://web.archive.org/web/20230331125843/https://phreakerclub.com/447
|
||||
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
+4
-4
@@ -24,15 +24,15 @@ namespace ui::external_app::ookbrute {
|
||||
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
|
||||
class OokBruteView : public View {
|
||||
class OOKBruteView : public View {
|
||||
public:
|
||||
OokBruteView(NavigationView& nav);
|
||||
~OokBruteView();
|
||||
OOKBruteView(NavigationView& nav);
|
||||
~OOKBruteView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override {
|
||||
return "OokBrute";
|
||||
return "OOKBrute";
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2024 gullradriel
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_remote.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::remote {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<RemoteAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::remote
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_remote.application_information"), used)) application_information_t _application_information_remote = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::remote::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "Remote",
|
||||
/*.bitmap_data = */ {
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0x20,
|
||||
0x00,
|
||||
0xE0,
|
||||
0x07,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x30,
|
||||
0x0C,
|
||||
0x30,
|
||||
0x0C,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0x70,
|
||||
0x0D,
|
||||
0xB0,
|
||||
0x0E,
|
||||
0x70,
|
||||
0x0D,
|
||||
0xB0,
|
||||
0x0E,
|
||||
0xF0,
|
||||
0x0F,
|
||||
0xE0,
|
||||
0x07,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::HOME,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_replay */ {'P', 'R', 'E', 'P'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
Vendored
+25
-26
@@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
#include "ui_remote.hpp"
|
||||
|
||||
#include "binder.hpp"
|
||||
#include "convert.hpp"
|
||||
#include "file_reader.hpp"
|
||||
@@ -34,11 +33,11 @@
|
||||
#include "utility.hpp"
|
||||
#include "file_path.hpp"
|
||||
|
||||
namespace ui::external_app::remote {
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui {
|
||||
|
||||
static constexpr uint8_t text_edit_max = 30;
|
||||
|
||||
/* RemoteEntryModel **************************************/
|
||||
@@ -309,9 +308,9 @@ void RemoteEntryEditView::load_path(std::filesystem::path&& path) {
|
||||
entry_.metadata = {transmitter_model.target_frequency(), 500'000};
|
||||
}
|
||||
|
||||
/* RemoteView ********************************************/
|
||||
/* RemoteAppView ********************************************/
|
||||
|
||||
RemoteView::RemoteView(
|
||||
RemoteAppView::RemoteAppView(
|
||||
NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_replay);
|
||||
@@ -365,27 +364,27 @@ RemoteView::RemoteView(
|
||||
refresh_ui();
|
||||
}
|
||||
|
||||
RemoteView::RemoteView(NavigationView& nav, fs::path path)
|
||||
: RemoteView(nav) {
|
||||
RemoteAppView::RemoteAppView(NavigationView& nav, fs::path path)
|
||||
: RemoteAppView(nav) {
|
||||
load_remote(std::move(path));
|
||||
refresh_ui();
|
||||
}
|
||||
|
||||
RemoteView::~RemoteView() {
|
||||
RemoteAppView::~RemoteAppView() {
|
||||
stop();
|
||||
baseband::shutdown();
|
||||
|
||||
save_remote(/*show_error*/ false);
|
||||
}
|
||||
|
||||
void RemoteView::focus() {
|
||||
void RemoteAppView::focus() {
|
||||
if (model_.entries.empty())
|
||||
button_add.focus();
|
||||
else
|
||||
buttons_[0]->focus();
|
||||
}
|
||||
|
||||
void RemoteView::create_buttons() {
|
||||
void RemoteAppView::create_buttons() {
|
||||
// Handler callbacks.
|
||||
auto handle_send = [this](RemoteButton& btn) {
|
||||
if (btn.entry()->path.empty())
|
||||
@@ -420,7 +419,7 @@ void RemoteView::create_buttons() {
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteView::reset_buttons() {
|
||||
void RemoteAppView::reset_buttons() {
|
||||
// Whever the model's entries instance is invalidated,
|
||||
// all the pointers in the buttons will end up dangling.
|
||||
// TODO: This is pretty lame. Could maybe static alloc?
|
||||
@@ -428,7 +427,7 @@ void RemoteView::reset_buttons() {
|
||||
btn->set_entry(nullptr);
|
||||
}
|
||||
|
||||
void RemoteView::refresh_ui() {
|
||||
void RemoteAppView::refresh_ui() {
|
||||
field_title.set_text(model_.name);
|
||||
field_filename.set_text(remote_path_.stem().string());
|
||||
|
||||
@@ -441,7 +440,7 @@ void RemoteView::refresh_ui() {
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteView::add_button() {
|
||||
void RemoteAppView::add_button() {
|
||||
if (model_.entries.size() >= max_buttons)
|
||||
return;
|
||||
|
||||
@@ -454,7 +453,7 @@ void RemoteView::add_button() {
|
||||
set_needs_save();
|
||||
}
|
||||
|
||||
void RemoteView::edit_button(RemoteButton& btn) {
|
||||
void RemoteAppView::edit_button(RemoteButton& btn) {
|
||||
// Don't let replay thread read the model while editing.
|
||||
stop();
|
||||
|
||||
@@ -471,7 +470,7 @@ void RemoteView::edit_button(RemoteButton& btn) {
|
||||
};
|
||||
}
|
||||
|
||||
void RemoteView::send_button(RemoteButton& btn) {
|
||||
void RemoteAppView::send_button(RemoteButton& btn) {
|
||||
// TODO: If this is called while is_sending() == true,
|
||||
// it just stops and doesn't start the new button?
|
||||
|
||||
@@ -510,14 +509,14 @@ void RemoteView::send_button(RemoteButton& btn) {
|
||||
});
|
||||
}
|
||||
|
||||
void RemoteView::stop() {
|
||||
void RemoteAppView::stop() {
|
||||
// This terminates the underlying chThread.
|
||||
replay_thread_.reset();
|
||||
transmitter_model.disable();
|
||||
ready_signal_ = false;
|
||||
}
|
||||
|
||||
void RemoteView::new_remote() {
|
||||
void RemoteAppView::new_remote() {
|
||||
save_remote();
|
||||
init_remote();
|
||||
refresh_ui();
|
||||
@@ -526,7 +525,7 @@ void RemoteView::new_remote() {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void RemoteView::open_remote() {
|
||||
void RemoteAppView::open_remote() {
|
||||
auto open_view = nav_.push<FileLoadView>(".REM");
|
||||
open_view->push_dir(remotes_dir);
|
||||
open_view->on_changed = [this](fs::path path) {
|
||||
@@ -536,7 +535,7 @@ void RemoteView::open_remote() {
|
||||
};
|
||||
}
|
||||
|
||||
void RemoteView::init_remote() {
|
||||
void RemoteAppView::init_remote() {
|
||||
model_ = {"<Unnamed Remote>", {}};
|
||||
reset_buttons();
|
||||
set_remote_path(next_filename_matching_pattern(remotes_dir / u"REMOTE_????.REM"));
|
||||
@@ -546,14 +545,14 @@ void RemoteView::init_remote() {
|
||||
show_error("Couldn't make new remote file.");
|
||||
}
|
||||
|
||||
bool RemoteView::load_remote(fs::path&& path) {
|
||||
bool RemoteAppView::load_remote(fs::path&& path) {
|
||||
set_remote_path(std::move(path));
|
||||
set_needs_save(false);
|
||||
reset_buttons();
|
||||
return model_.load(remote_path_);
|
||||
}
|
||||
|
||||
void RemoteView::save_remote(bool show_errors) {
|
||||
void RemoteAppView::save_remote(bool show_errors) {
|
||||
if (!needs_save_)
|
||||
return;
|
||||
|
||||
@@ -564,7 +563,7 @@ void RemoteView::save_remote(bool show_errors) {
|
||||
set_needs_save(false);
|
||||
}
|
||||
|
||||
void RemoteView::rename_remote(const std::string& new_name) {
|
||||
void RemoteAppView::rename_remote(const std::string& new_name) {
|
||||
auto folder = remote_path_.parent_path();
|
||||
auto ext = remote_path_.extension();
|
||||
auto new_path = folder / new_name + ext;
|
||||
@@ -581,7 +580,7 @@ void RemoteView::rename_remote(const std::string& new_name) {
|
||||
set_remote_path(std::move(new_path));
|
||||
}
|
||||
|
||||
void RemoteView::handle_replay_thread_done(uint32_t return_code) {
|
||||
void RemoteAppView::handle_replay_thread_done(uint32_t return_code) {
|
||||
if (return_code == ReplayThread::END_OF_FILE) {
|
||||
if (check_loop.value() && current_btn_) {
|
||||
send_button(*current_btn_);
|
||||
@@ -598,15 +597,15 @@ void RemoteView::handle_replay_thread_done(uint32_t return_code) {
|
||||
stop();
|
||||
}
|
||||
|
||||
void RemoteView::set_remote_path(fs::path&& path) {
|
||||
void RemoteAppView::set_remote_path(fs::path&& path) {
|
||||
// Unfortunately, have to keep these two in sync because
|
||||
// settings doesn't know about fs::path.
|
||||
remote_path_ = std::move(path);
|
||||
settings_.remote_path = remote_path_.string();
|
||||
}
|
||||
|
||||
void RemoteView::show_error(const std::string& msg) const {
|
||||
void RemoteAppView::show_error(const std::string& msg) const {
|
||||
nav_.display_modal("Error", msg);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::remote
|
||||
Vendored
+8
-8
@@ -41,7 +41,7 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::remote {
|
||||
|
||||
/* Maps icon index to bitmap. */
|
||||
class RemoteIcons {
|
||||
@@ -257,14 +257,14 @@ class RemoteEntryEditView : public View {
|
||||
};
|
||||
|
||||
/* App that allows for buttons to be bound to captures for playback. */
|
||||
class RemoteView : public View {
|
||||
class RemoteAppView : public View {
|
||||
public:
|
||||
RemoteView(NavigationView& nav);
|
||||
RemoteView(NavigationView& nav, std::filesystem::path path);
|
||||
~RemoteView();
|
||||
RemoteAppView(NavigationView& nav);
|
||||
RemoteAppView(NavigationView& nav, std::filesystem::path path);
|
||||
~RemoteAppView();
|
||||
|
||||
RemoteView(const RemoteView&) = delete;
|
||||
RemoteView& operator=(const RemoteView&) = delete;
|
||||
RemoteAppView(const RemoteAppView&) = delete;
|
||||
RemoteAppView& operator=(const RemoteAppView&) = delete;
|
||||
|
||||
std::string title() const override { return "Remote"; };
|
||||
void focus() override;
|
||||
@@ -385,4 +385,4 @@ class RemoteView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::remote
|
||||
-275
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
|
||||
*
|
||||
* 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_rook.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include <cstring>
|
||||
#include "baseband_api.hpp"
|
||||
#include "ui_textentry.hpp"
|
||||
#include "encoders.hpp" // Includes data encoding functions for transmission
|
||||
#include "baseband_api.hpp" // Includes baseband API for handling transmission settings
|
||||
#include "file_reader.hpp"
|
||||
|
||||
#define PADDING_LEFT 1
|
||||
#define PADDING_RIGHT 1
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ui::external_app::rook {
|
||||
|
||||
void ROOKAppView::focus() {
|
||||
button_set.focus();
|
||||
}
|
||||
|
||||
// `start_tx` method: Configures and begins OOK data transmission with a specific message.
|
||||
void ROOKAppView::start_tx(const std::string& message) {
|
||||
size_t bitstream_length = encoders::make_bitstream(const_cast<std::string&>(message)); // Convert the message into a bitstream
|
||||
// Retrieve selected sample rate using selected_index_value()
|
||||
int32_t SAMPLE_RATE_VALUE = field_sample_rate.selected_index_value();
|
||||
int32_t SYMBOL_RATE_VALUE = cant_symbol_rate.value();
|
||||
int32_t REPEAT = cant_repeat.value();
|
||||
int32_t PAUSE_SYMBOL = cant_pause_symbol.value();
|
||||
|
||||
transmitter_model.set_sampling_rate(SAMPLE_RATE_VALUE); // Set the OOK sampling rate
|
||||
transmitter_model.set_baseband_bandwidth(1750000); // Set the baseband bandwidth
|
||||
transmitter_model.enable(); // Enable the transmitter
|
||||
|
||||
// Configure OOK data and transmission characteristics
|
||||
baseband::set_ook_data(
|
||||
bitstream_length, // Length of the bitstream to transmit
|
||||
SAMPLE_RATE_VALUE / SYMBOL_RATE_VALUE, // Calculate symbol period based on repetition rate
|
||||
REPEAT, // Set the number of repetitions per symbol
|
||||
PAUSE_SYMBOL // Set the pause between symbols
|
||||
);
|
||||
}
|
||||
|
||||
// `stop_tx` method: Stops the transmission and resets the progress bar.
|
||||
void ROOKAppView::stop_tx() {
|
||||
transmitter_model.disable(); // Disable the transmitter
|
||||
progressBar_progress.set_value(0); // Reset progress bar to 0
|
||||
}
|
||||
|
||||
// `on_file_changed` method: Called when a new file is loaded; parses file data into variables
|
||||
|
||||
void ROOKAppView::on_file_changed(const fs::path& new_file_path) {
|
||||
payload.clear(); // Clear previous payload content
|
||||
|
||||
File data_file;
|
||||
auto open_result = data_file.open(new_file_path);
|
||||
if (open_result) {
|
||||
nav_.display_modal("Error", "Unable to open file.");
|
||||
return;
|
||||
}
|
||||
|
||||
FileLineReader reader(data_file);
|
||||
for (const auto& line : reader) {
|
||||
// Split the line into segments to extract each value
|
||||
size_t first_space = line.find(' ');
|
||||
size_t second_space = line.find(' ', first_space + 1);
|
||||
size_t third_space = line.find(' ', second_space + 1);
|
||||
size_t fourth_space = line.find(' ', third_space + 1);
|
||||
size_t fifth_space = line.find(' ', fourth_space + 1);
|
||||
|
||||
// Extract each component of the line
|
||||
std::string frequency_str = line.substr(0, first_space);
|
||||
std::string sample_rate_str = line.substr(first_space + 1, second_space - first_space - 1);
|
||||
std::string symbols_rate_str = line.substr(second_space + 1, third_space - second_space - 1);
|
||||
std::string repeat_str = line.substr(third_space + 1, fourth_space - third_space - 1);
|
||||
std::string pause_symbol_str = line.substr(fourth_space + 1, fifth_space - fourth_space - 1);
|
||||
std::string payload_data = line.substr(fifth_space + 1); // Extract binary payload as final value
|
||||
|
||||
// Convert and assign frequency
|
||||
unsigned long long frequency = std::stoull(frequency_str);
|
||||
field_frequency.set_value(frequency);
|
||||
transmitter_model.set_target_frequency(frequency);
|
||||
|
||||
// Convert and assign symbols rate
|
||||
unsigned int symbols_rate = static_cast<unsigned int>(atoi(symbols_rate_str.c_str()));
|
||||
|
||||
cant_symbol_rate.set_value(symbols_rate);
|
||||
|
||||
// Convert and assign repeat count
|
||||
unsigned int repeat = static_cast<unsigned int>(atoi(repeat_str.c_str()));
|
||||
|
||||
cant_repeat.set_value(repeat);
|
||||
|
||||
// Convert and assign pause per symbol
|
||||
unsigned int pause_symbol = static_cast<unsigned int>(atoi(pause_symbol_str.c_str()));
|
||||
|
||||
cant_pause_symbol.set_value(pause_symbol);
|
||||
|
||||
// Select sample rate based on value read from file
|
||||
if (sample_rate_str == "250k") {
|
||||
field_sample_rate.set_by_value(250000U);
|
||||
} else if (sample_rate_str == "1M") {
|
||||
field_sample_rate.set_by_value(1000000U);
|
||||
} else if (sample_rate_str == "2M") {
|
||||
field_sample_rate.set_by_value(2000000U);
|
||||
} else if (sample_rate_str == "5M") {
|
||||
field_sample_rate.set_by_value(5000000U);
|
||||
} else if (sample_rate_str == "10M") {
|
||||
field_sample_rate.set_by_value(10000000U);
|
||||
} else if (sample_rate_str == "20M") {
|
||||
field_sample_rate.set_by_value(20000000U);
|
||||
} else {
|
||||
nav_.display_modal("Error", "Invalid sample rate.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update payload with binary data
|
||||
payload = payload_data;
|
||||
|
||||
// Process only the first line
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure UI elements are initialized before use
|
||||
if (parent()) {
|
||||
text_payload.set(payload);
|
||||
button_start.focus();
|
||||
} else {
|
||||
text_payload.set("parent not available");
|
||||
}
|
||||
}
|
||||
|
||||
// `on_tx_progress` method: Updates the progress bar based on transmission progress.
|
||||
void ROOKAppView::on_tx_progress(const uint32_t progress, const bool done) {
|
||||
progressBar_progress.set_value(progress); // Update progress bar value
|
||||
if (done) {
|
||||
stop_tx(); // Stop transmission when progress reaches maximum
|
||||
}
|
||||
}
|
||||
|
||||
// `draw_waveform` method: Draws the waveform on the UI based on the payload data
|
||||
void ROOKAppView::draw_waveform() {
|
||||
// Padding reason:
|
||||
// In real-world scenarios, the signal would always start low and return low after turning off the radio.
|
||||
// `waveform_buffer` only controls drawing; the actual send logic is handled by frame_fragments.
|
||||
size_t length = payload.length();
|
||||
|
||||
// Ensure waveform length does not exceed buffer size
|
||||
if (length + (PADDING_LEFT + PADDING_RIGHT) >= WAVEFORM_BUFFER_SIZE) {
|
||||
length = WAVEFORM_BUFFER_SIZE - (PADDING_LEFT + PADDING_RIGHT);
|
||||
}
|
||||
|
||||
// Left padding
|
||||
for (size_t i = 0; i < PADDING_LEFT; i++) {
|
||||
waveform_buffer[i] = 0;
|
||||
}
|
||||
|
||||
// Draw the actual waveform
|
||||
for (size_t n = 0; n < length; n++) {
|
||||
waveform_buffer[n + PADDING_LEFT] = (payload[n] == '0') ? 0 : 1;
|
||||
}
|
||||
|
||||
// Right padding
|
||||
for (size_t i = length + PADDING_LEFT; i < WAVEFORM_BUFFER_SIZE; i++) {
|
||||
waveform_buffer[i] = 0;
|
||||
}
|
||||
|
||||
waveform.set_length(length + PADDING_LEFT + PADDING_RIGHT);
|
||||
waveform.set_dirty();
|
||||
}
|
||||
|
||||
// Destructor for `ROOKAppView`: Disables the transmitter and shuts down the baseband
|
||||
ROOKAppView::~ROOKAppView() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
// Constructor for `ROOKAppView`: Sets up the app view and initializes UI elements
|
||||
ROOKAppView::ROOKAppView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_ook);
|
||||
|
||||
add_children({
|
||||
&field_frequency,
|
||||
&tx_view,
|
||||
&button_start,
|
||||
&sample_rate,
|
||||
&step_symbol_rate,
|
||||
&cant_step_symbol_rate,
|
||||
&field_sample_rate,
|
||||
&field_symbol_rate,
|
||||
&field_symbol_us_rate,
|
||||
&cant_symbol_rate,
|
||||
&text_payload,
|
||||
&button_set,
|
||||
&progressBar_progress,
|
||||
&repeat,
|
||||
&cant_repeat,
|
||||
&field_pause_symbol,
|
||||
&cant_pause_symbol,
|
||||
&label_waveform,
|
||||
&waveform,
|
||||
&button_open,
|
||||
});
|
||||
|
||||
// Initialize default values for controls
|
||||
cant_pause_symbol.set_value(100);
|
||||
cant_repeat.set_value(4);
|
||||
|
||||
button_open.on_select = [this](Button&) {
|
||||
auto open_view = nav_.push<FileLoadView>(".ROOK");
|
||||
ensure_directory(rook_dir);
|
||||
open_view->push_dir(rook_dir);
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
// Postpone `on_file_changed` call until `FileLoadView` is closed
|
||||
nav_.set_on_pop([this, new_file_path]() {
|
||||
on_file_changed(new_file_path);
|
||||
button_start.focus();
|
||||
draw_waveform();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// Set up changes for symbol rate and step
|
||||
cant_symbol_rate.on_change = [this](int32_t value) {
|
||||
if (value != cant_symbol_rate.value())
|
||||
cant_symbol_rate.set_value(value);
|
||||
};
|
||||
|
||||
cant_step_symbol_rate.on_change = [this](size_t, int32_t value) {
|
||||
cant_symbol_rate.set_step(value);
|
||||
};
|
||||
|
||||
// Configure button to manually set payload through text input
|
||||
button_set.on_select = [this, &nav](Button&) {
|
||||
text_prompt(
|
||||
nav,
|
||||
payload,
|
||||
100,
|
||||
[this](std::string& s) {
|
||||
text_payload.set(s);
|
||||
draw_waveform();
|
||||
});
|
||||
};
|
||||
draw_waveform();
|
||||
button_start.on_select = [this](Button&) {
|
||||
start_tx(payload); // Begin transmission
|
||||
};
|
||||
}
|
||||
} // namespace ui::external_app::rook
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Samir Sánchez Garnica @sasaga92
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __UI_ROOK_H__
|
||||
#define __UI_ROOK_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include <string>
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::rook {
|
||||
|
||||
#define PROGRESS_MAX 100
|
||||
#define OOK_SAMPLERATE_DEFAULT 2280000U // Set the default Sample Rate
|
||||
#define TRANSMISSION_FREQUENCY_DEFAULT 433920000U // Sets the default transmission frequency (27 MHz).
|
||||
#define WAVEFORM_BUFFER_SIZE 550
|
||||
|
||||
class ROOKAppView : public View {
|
||||
public:
|
||||
void focus() override;
|
||||
ROOKAppView(NavigationView& nav);
|
||||
|
||||
~ROOKAppView();
|
||||
|
||||
std::string title() const override {
|
||||
return "RemoteOOK";
|
||||
};
|
||||
|
||||
private:
|
||||
NavigationView& nav_; // Reference to the navigation system.
|
||||
std::string payload{""}; // Holds the data payload as a string.
|
||||
uint32_t progress = 0; // Stores the current transmission progress.
|
||||
int16_t waveform_buffer[WAVEFORM_BUFFER_SIZE]; // Buffer for waveform data.
|
||||
|
||||
void update();
|
||||
void draw_waveform();
|
||||
|
||||
// Initiates data transmission with a specified message.
|
||||
void start_tx(const std::string& message);
|
||||
|
||||
// Stops data transmission.
|
||||
void stop_tx();
|
||||
|
||||
// Updates the transmission progress on the progress bar.
|
||||
void on_tx_progress(const uint32_t progress, const bool done);
|
||||
|
||||
// Updates data when a new file is loaded.
|
||||
void on_file_changed(const std::filesystem::path& new_file_path);
|
||||
|
||||
// Registers a message handler for transmission progress updates.
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress, // Transmission progress message ID.
|
||||
[this](const Message* const p) { // Callback to handle the message.
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.progress, message.done);
|
||||
}};
|
||||
|
||||
// Sets transmission frequency, bandwidth, and default sample rate for OOK.
|
||||
TxRadioState radio_state_{TRANSMISSION_FREQUENCY_DEFAULT, 1750000, OOK_SAMPLERATE_DEFAULT};
|
||||
|
||||
// Settings manager for app configuration.
|
||||
app_settings::SettingsManager settings_{"tx_rook", app_settings::Mode::TX};
|
||||
|
||||
// UI components for frequency and transmitter view.
|
||||
TxFrequencyField field_frequency{{0 * 8, 0 * 16}, nav_};
|
||||
TransmitterView2 tx_view{{20 * 7, 0 * 16}, true};
|
||||
|
||||
// Labels for various fields such as sample rate and repeat count.
|
||||
Labels sample_rate{{{0, 40}, "S/Rate: ", Theme::getInstance()->fg_light->foreground}};
|
||||
// Labels step_symbol_rate {{{160, 0}, "Step/Sym: ",Theme::getInstance()->fg_light->foreground}};
|
||||
Labels step_symbol_rate{{{0, 18}, "Step/Symbols: ", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels repeat{{{0, 60}, "Repeat: ", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels field_symbol_rate{{{111, 40}, "Symbols: ", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels field_pause_symbol{{{111, 60}, "Pause/Sym: ", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels field_symbol_us_rate{{{210, 40}, "us ", Theme::getInstance()->fg_light->foreground}};
|
||||
Labels label_waveform{{{0, 140}, "Waveform:", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
// OptionsField for selectable sample rates.
|
||||
OptionsField field_sample_rate{{55, 40}, 7, {{"250k", 250000U}, {"1M", 1000000U}, {"2M", 2000000U}, {"5M", 5000000U}, {"10M", 10000000U}, {"20M", 20000000U}}};
|
||||
|
||||
// OptionsField for step symbol rates.
|
||||
OptionsField cant_step_symbol_rate{{105, 18}, 7, {{"1", 1}, {"10", 10}, {"100", 100}}};
|
||||
|
||||
// Number fields for symbols, pause between symbols, and repeat count.
|
||||
NumberField cant_symbol_rate{{176, 40}, 4, {0, 9999}, 1, '0', false};
|
||||
NumberField cant_pause_symbol{{200, 60}, 4, {0, 200}, 1, '0', false};
|
||||
NumberField cant_repeat{{55, 60}, 3, {0, 100}, 1, '0', false};
|
||||
|
||||
// Text field to display the payload data.
|
||||
Text text_payload{{0 * 8, 90, 30 * 8, 16}, "Payload:"};
|
||||
|
||||
// Buttons for setting configurations, opening files, and starting transmission.
|
||||
Button button_set{{0, 110, 60, 28}, "Set"};
|
||||
Button button_open{{100, 110, 80, 28}, "Open file"};
|
||||
Button button_start{{80, 273, 80, 32}, "Start"};
|
||||
|
||||
// Progress bar to display transmission progress.
|
||||
ProgressBar progressBar_progress{{2 * 8, 250, 208, 16}};
|
||||
|
||||
// Waveform display using waveform buffer and yellow theme color.
|
||||
Waveform waveform{{0, 165, 240, 32}, waveform_buffer, 0, 0, true, Theme::getInstance()->fg_yellow->foreground};
|
||||
};
|
||||
|
||||
}; // namespace ui::external_app::rook
|
||||
|
||||
#endif /*__UI_ROOK_H__*/
|
||||
@@ -48,4 +48,4 @@ 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 rook_dir = u"ROOK";
|
||||
const std::filesystem::path ook_editor_dir = u"OOKFILES";
|
||||
|
||||
@@ -50,6 +50,6 @@ extern const std::filesystem::path splash_dir;
|
||||
extern const std::filesystem::path sstv_dir;
|
||||
extern const std::filesystem::path wav_dir;
|
||||
extern const std::filesystem::path whipcalc_dir;
|
||||
extern const std::filesystem::path rook_dir;
|
||||
extern const std::filesystem::path ook_editor_dir;
|
||||
|
||||
#endif /* __FILE_PATH_H__ */
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2024 gullradriel
|
||||
*
|
||||
* 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 "ook_file.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
/*
|
||||
struct of an OOK file:
|
||||
|
||||
Frequency SampleRate SymbolRate Repeat PauseSymbolDuration Payload
|
||||
|
||||
-Frequency is in hertz
|
||||
-SampleRate is one of 250k, 1M, 2M, 5M , 10M ,20M
|
||||
-SymbolRate is the number of symbols /s
|
||||
-Repeat is the number of times we will repeat the payload
|
||||
-PauseSymbolDuration is the duration of the pause between repeat, in usec
|
||||
-Payload is the payload in form of a string of 0 and 1
|
||||
*/
|
||||
|
||||
bool read_ook_file(const std::filesystem::path& path, ook_file_data& ook_data) {
|
||||
File data_file;
|
||||
auto open_result = data_file.open(path);
|
||||
if (open_result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileLineReader reader(data_file);
|
||||
|
||||
for (const auto& line : reader) {
|
||||
// Split the line into segments to extract each value
|
||||
size_t first_space = line.find(' ');
|
||||
size_t second_space = line.find(' ', first_space + 1);
|
||||
size_t third_space = line.find(' ', second_space + 1);
|
||||
size_t fourth_space = line.find(' ', third_space + 1);
|
||||
size_t fifth_space = line.find(' ', fourth_space + 1);
|
||||
|
||||
// Extract each component of the line
|
||||
std::string frequency_str = line.substr(0, first_space);
|
||||
std::string sample_rate_str = line.substr(first_space + 1, second_space - first_space - 1);
|
||||
std::string symbol_rate_str = line.substr(second_space + 1, third_space - second_space - 1);
|
||||
std::string repeat_str = line.substr(third_space + 1, fourth_space - third_space - 1);
|
||||
std::string pause_symbol_duration_str = line.substr(fourth_space + 1, fifth_space - fourth_space - 1);
|
||||
std::string payload_data = line.substr(fifth_space + 1); // Extract binary payload as final value
|
||||
|
||||
// Convert and assign frequency
|
||||
ook_data.frequency = std::stoull(frequency_str);
|
||||
// Convert and assign symbol_rate
|
||||
ook_data.symbol_rate = static_cast<unsigned int>(atoi(symbol_rate_str.c_str()));
|
||||
// Convert and assign repeat count
|
||||
ook_data.repeat = static_cast<unsigned int>(atoi(repeat_str.c_str()));
|
||||
// Convert and assign pause_symbol_duration
|
||||
ook_data.pause_symbol_duration = static_cast<unsigned int>(atoi(pause_symbol_duration_str.c_str()));
|
||||
// Select sample rate based on value read from file
|
||||
if (sample_rate_str == "250k") {
|
||||
ook_data.sample_rate = 250000U;
|
||||
} else if (sample_rate_str == "1M") {
|
||||
ook_data.sample_rate = 1000000U;
|
||||
} else if (sample_rate_str == "2M") {
|
||||
ook_data.sample_rate = 2000000U;
|
||||
} else if (sample_rate_str == "5M") {
|
||||
ook_data.sample_rate = 5000000U;
|
||||
} else if (sample_rate_str == "10M") {
|
||||
ook_data.sample_rate = 10000000U;
|
||||
} else if (sample_rate_str == "20M") {
|
||||
ook_data.sample_rate = 20000000U;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// Update payload with binary data
|
||||
ook_data.payload = std::move(payload_data);
|
||||
// Process only the first line
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool save_ook_file(ook_file_data& ook_data, const std::filesystem::path& path) {
|
||||
// delete file if it exists
|
||||
delete_file(path);
|
||||
|
||||
// Attempt to open, if it can't be opened. Create new.
|
||||
auto src = std::make_unique<File>();
|
||||
auto error = src->open(path, false, true);
|
||||
if (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string sample_rate_str;
|
||||
if (ook_data.sample_rate == 250000U) {
|
||||
sample_rate_str = "250k";
|
||||
} else if (ook_data.sample_rate == 1000000U) {
|
||||
sample_rate_str = "1M";
|
||||
} else if (ook_data.sample_rate == 2000000U) {
|
||||
sample_rate_str = "2M";
|
||||
} else if (ook_data.sample_rate == 5000000U) {
|
||||
sample_rate_str = "5M";
|
||||
} else if (ook_data.sample_rate == 10000000U) {
|
||||
sample_rate_str = "10M";
|
||||
} else if (ook_data.sample_rate == 20000000U) {
|
||||
sample_rate_str = "20M";
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// write informations
|
||||
src->write_line(to_string_dec_uint(ook_data.frequency) + " " +
|
||||
sample_rate_str + " " +
|
||||
to_string_dec_uint(ook_data.symbol_rate) + " " +
|
||||
to_string_dec_uint(ook_data.repeat) + " " +
|
||||
to_string_dec_uint(ook_data.pause_symbol_duration) + " " +
|
||||
ook_data.payload);
|
||||
|
||||
// Close files
|
||||
src.reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void start_ook_file_tx(ook_file_data& ook_data) {
|
||||
size_t bitstream_length = encoders::make_bitstream(const_cast<std::string&>(ook_data.payload)); // Convert the message into a bitstream
|
||||
|
||||
transmitter_model.set_target_frequency(ook_data.frequency); // Set target frequency
|
||||
transmitter_model.set_sampling_rate(ook_data.sample_rate); // Set the OOK sampling rate
|
||||
transmitter_model.set_baseband_bandwidth(1750000); // Set the baseband bandwidth
|
||||
transmitter_model.enable(); // Enable the transmitter
|
||||
|
||||
// Configure OOK data and transmission characteristics
|
||||
baseband::set_ook_data(
|
||||
bitstream_length, // Length of the bitstream to transmit
|
||||
ook_data.sample_rate / ook_data.symbol_rate, // Calculate symbol period based on repetition rate
|
||||
ook_data.repeat, // Set the number of times the whole bitstream is repeated
|
||||
ook_data.pause_symbol_duration // Set the pause_symbol between reps
|
||||
);
|
||||
}
|
||||
|
||||
void stop_ook_file_tx() {
|
||||
transmitter_model.disable(); // Disable the transmitter
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2024 gullradriel
|
||||
*
|
||||
* 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 __OOK_FILE_HPP__
|
||||
#define __OOK_FILE_HPP__
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "metadata_file.hpp"
|
||||
#include "encoders.hpp" // Includes data encoding functions for transmission
|
||||
#include "convert.hpp"
|
||||
#include "file_reader.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include <string_view>
|
||||
#include "transmitter_model.hpp"
|
||||
#include "baseband_api.hpp" // Includes baseband API for handling transmission settings
|
||||
|
||||
struct ook_file_data {
|
||||
rf::Frequency frequency = rf::Frequency(0); // Default frequency
|
||||
uint32_t sample_rate = 0; // Default sample rate
|
||||
uint16_t symbol_rate = 0; // Default bit duration
|
||||
uint16_t pause_symbol_duration = 0; // Default pause between repeat
|
||||
uint16_t repeat = 0; // Default repeat
|
||||
std::string payload = ""; // Default payload
|
||||
};
|
||||
|
||||
bool read_ook_file(const std::filesystem::path& path, ook_file_data& ook_data);
|
||||
bool save_ook_file(ook_file_data& data, const std::filesystem::path& path);
|
||||
void start_ook_file_tx(ook_file_data& ook_data);
|
||||
void stop_ook_file_tx();
|
||||
|
||||
#endif // __FLIPPER_SUBFILE_HPP__
|
||||
@@ -52,7 +52,6 @@
|
||||
#include "ui_pocsag_tx.hpp"
|
||||
#include "ui_rds.hpp"
|
||||
#include "ui_recon.hpp"
|
||||
#include "ui_remote.hpp"
|
||||
#include "ui_scanner.hpp"
|
||||
#include "ui_sd_over_usb.hpp"
|
||||
#include "ui_sd_wipe.hpp"
|
||||
@@ -126,7 +125,6 @@ const NavigationView::AppList NavigationView::appList = {
|
||||
{nullptr, "Transmit", HOME, Color::cyan(), &bitmap_icon_transmit, new ViewFactory<TransmittersMenuView>()},
|
||||
{"capture", "Capture", HOME, Color::red(), &bitmap_icon_capture, new ViewFactory<CaptureAppView>()},
|
||||
{"replay", "Replay", HOME, Color::green(), &bitmap_icon_replay, new ViewFactory<PlaylistView>()},
|
||||
{"remote", "Remote", HOME, ui::Color::green(), &bitmap_icon_remote, new ViewFactory<RemoteView>()},
|
||||
{"scanner", "Scanner", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory<ScannerView>()},
|
||||
{"microphone", "Microphone", HOME, Color::green(), &bitmap_icon_microphone, new ViewFactory<MicTXView>()},
|
||||
{"lookingglass", "Looking Glass", HOME, Color::green(), &bitmap_icon_looking, new ViewFactory<GlassView>()},
|
||||
@@ -851,6 +849,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav)
|
||||
|
||||
void SystemMenuView::on_populate() {
|
||||
add_apps(nav_, *this, HOME);
|
||||
addExternalItems(nav_, app_location_t::HOME, *this);
|
||||
add_item({"HackRF", Theme::getInstance()->fg_cyan->foreground, &bitmap_icon_hackrf, [this]() { hackrf_mode(nav_); }});
|
||||
}
|
||||
|
||||
@@ -902,7 +901,7 @@ SystemView::SystemView(
|
||||
navigation_view.push<SystemMenuView>();
|
||||
|
||||
if (pmem::config_splash()) {
|
||||
navigation_view.push<BMPView>();
|
||||
navigation_view.push<SplashScreenView>();
|
||||
}
|
||||
status_view.set_back_enabled(false);
|
||||
status_view.set_title_image_enabled(true);
|
||||
@@ -971,11 +970,11 @@ void SystemView::set_app_fullscreen(bool fullscreen) {
|
||||
|
||||
/* ***********************************************************************/
|
||||
|
||||
void BMPView::focus() {
|
||||
void SplashScreenView::focus() {
|
||||
button_done.focus();
|
||||
}
|
||||
|
||||
BMPView::BMPView(NavigationView& nav)
|
||||
SplashScreenView::SplashScreenView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
add_children({&button_done});
|
||||
|
||||
@@ -984,12 +983,14 @@ BMPView::BMPView(NavigationView& nav)
|
||||
};
|
||||
}
|
||||
|
||||
void BMPView::paint(Painter&) {
|
||||
if (!portapack::display.drawBMP2({0, 0}, splash_dot_bmp))
|
||||
portapack::display.drawBMP({0, 16}, splash_bmp, (const uint8_t[]){0x29, 0x18, 0x16});
|
||||
void SplashScreenView::paint(Painter&) {
|
||||
if (!portapack::display.draw_bmp_from_sdcard_file({0, 0}, splash_dot_bmp))
|
||||
// ^ try draw bmp file from sdcard at (0,0), and the (0,0) already bypassed the status bar, so actual pos is (0, STATUS_BAR_HEIGHT)
|
||||
portapack::display.draw_bmp_from_bmp_hex_arr({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, (const uint8_t[]){0x29, 0x18, 0x16});
|
||||
// ^ draw BMP HEX arr in firmware, note that the BMP HEX arr only cover the image part (instead of fill the screen with background, this position is located it in the center)
|
||||
}
|
||||
|
||||
bool BMPView::on_touch(const TouchEvent event) {
|
||||
bool SplashScreenView::on_touch(const TouchEvent event) {
|
||||
/* the event thing were resolved by HTotoo, talked here https://discord.com/channels/719669764804444213/956561375155589192/1287756910950486027
|
||||
* the touch screen policy can be better, talked here https://discord.com/channels/719669764804444213/956561375155589192/1198926225897443328
|
||||
* this workaround discussed here: https://discord.com/channels/719669764804444213/1170738202924044338/1295630640158478418
|
||||
@@ -1011,7 +1012,7 @@ bool BMPView::on_touch(const TouchEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void BMPView::handle_pop() {
|
||||
void SplashScreenView::handle_pop() {
|
||||
if (nav_.is_valid()) {
|
||||
nav_.pop();
|
||||
}
|
||||
@@ -1080,7 +1081,7 @@ ModalMessageView::ModalMessageView(
|
||||
}
|
||||
|
||||
void ModalMessageView::paint(Painter& painter) {
|
||||
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, (const uint8_t[]){0, 0, 0});
|
||||
if (!compact) portapack::display.draw_bmp_from_bmp_hex_arr({100, 48}, modal_warning_bmp, (const uint8_t[]){0, 0, 0});
|
||||
|
||||
// Break lines.
|
||||
auto lines = split_string(message_, '\n');
|
||||
|
||||
@@ -332,9 +332,9 @@ class InformationView : public View {
|
||||
{86, 0, 19 * 8, 16}};
|
||||
};
|
||||
|
||||
class BMPView : public View {
|
||||
class SplashScreenView : public View {
|
||||
public:
|
||||
BMPView(NavigationView& nav);
|
||||
SplashScreenView(NavigationView& nav);
|
||||
void paint(Painter&) override;
|
||||
void focus() override;
|
||||
|
||||
|
||||
@@ -345,19 +345,19 @@ void ILI9341::render_box(const ui::Point p, const ui::Size s, const ui::Color* l
|
||||
|
||||
// RLE_4 BMP loader (delta not implemented)
|
||||
/* draw transparent, pass transparent color as arg, usage inline anonymous obj
|
||||
* portapack::display.drawBMP({100, 100}, foo_bmp, (const uint8_t[]){41, 24, 22}); // dec, out of {255, 255, 255}
|
||||
* portapack::display.drawBMP({100, 100}, foo_bmp, (const uint8_t[]){0x29, 0x18, 0x16}); //hex out of {0xFF, 0xFF, 0xFF}
|
||||
* portapack::display.draw_bmp_from_bmp_hex_arr({100, 100}, foo_bmp, (const uint8_t[]){41, 24, 22}); // dec, out of {255, 255, 255}
|
||||
* portapack::display.draw_bmp_from_bmp_hex_arr({100, 100}, foo_bmp, (const uint8_t[]){0x29, 0x18, 0x16}); //hex out of {0xFF, 0xFF, 0xFF}
|
||||
*
|
||||
* draw transparent, pass transparent color as arg, usage pass uint8_t[] obj
|
||||
* const uint8_t c[] = {41, 24, 22}; or const uint8_t c[3] = {0x29, 0x18, 0x16};
|
||||
* portapack::display.drawBMP({100, 100}, foo_bmp, c);
|
||||
* portapack::display.draw_bmp_from_bmp_hex_arr({100, 100}, foo_bmp, c);
|
||||
*
|
||||
* don't draw transparent, pass nullptr as arg, usage
|
||||
* portapack::display.drawBMP({100, 100}, foo_bmp, nullptr);
|
||||
* portapack::display.draw_bmp_from_bmp_hex_arr({100, 100}, foo_bmp, nullptr);
|
||||
*
|
||||
* if your image use RLE compress as transparency methods, pass any valid color as arg, it doesn't matter (like the modal bmp. TODO: write RLE transparency generator)
|
||||
* */
|
||||
void ILI9341::drawBMP(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color) {
|
||||
void ILI9341::draw_bmp_from_bmp_hex_arr(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color) {
|
||||
const bmp_header_t* bmp_header = (const bmp_header_t*)bitmap;
|
||||
uint32_t data_idx;
|
||||
uint8_t by, c, count, transp_idx = 0;
|
||||
@@ -463,7 +463,7 @@ void ILI9341::drawBMP(const ui::Point p, const uint8_t* bitmap, const uint8_t* t
|
||||
* 24bpp RGB
|
||||
* 32bpp ARGB
|
||||
*/
|
||||
bool ILI9341::drawBMP2(const ui::Point p, const std::filesystem::path& file) {
|
||||
bool ILI9341::draw_bmp_from_sdcard_file(const ui::Point p, const std::filesystem::path& file) {
|
||||
File bmpimage;
|
||||
size_t file_pos = 0;
|
||||
uint16_t pointer = 0;
|
||||
@@ -512,7 +512,9 @@ bool ILI9341::drawBMP2(const ui::Point p, const std::filesystem::path& file) {
|
||||
|
||||
file_pos = bmp_header.image_data;
|
||||
|
||||
py = height + 16;
|
||||
py = height + 16 - 1;
|
||||
/* ^ this is for to "start" AKA "image end" draw at the 17th line,
|
||||
* because the render_line logic below is start with p.y() + py until "end" AKA "image start"*/
|
||||
|
||||
while (1) {
|
||||
while (px < width) {
|
||||
|
||||
@@ -62,8 +62,8 @@ class ILI9341 {
|
||||
const ui::Color background);
|
||||
|
||||
void draw_pixel(const ui::Point p, const ui::Color color);
|
||||
void drawBMP(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color);
|
||||
bool drawBMP2(const ui::Point p, const std::filesystem::path& file);
|
||||
void draw_bmp_from_bmp_hex_arr(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color);
|
||||
bool draw_bmp_from_sdcard_file(const ui::Point p, const std::filesystem::path& file);
|
||||
void render_line(const ui::Point p, const uint8_t count, const ui::Color* line_buffer);
|
||||
void render_box(const ui::Point p, const ui::Size s, const ui::Color* line_buffer);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "ui_language.hpp"
|
||||
|
||||
// use the exact position in this array! the enum's value is the identifier. Best to add to the end
|
||||
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:", "Loop", "Reset", "Pause", "Resume", "Flood", "Show QR", "Save", "Lock", "Unlock", "Browse"};
|
||||
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:", "Loop", "Reset", "Pause", "Resume", "Flood", "Show QR", "Save", "Lock", "Unlock", "Browse", "Set", "Open File", "Save File", "Send", "Receive"};
|
||||
|
||||
// multi language support will changes (not in use for now)
|
||||
const char** LanguageHelper::currentMessages = englishMessages;
|
||||
|
||||
@@ -61,7 +61,12 @@ enum LangConsts {
|
||||
LANG_SAVE,
|
||||
LANG_LOCK,
|
||||
LANG_UNLOCK,
|
||||
LANG_BROWSE
|
||||
LANG_BROWSE,
|
||||
LANG_SET,
|
||||
LANG_OPEN_FILE,
|
||||
LANG_SAVE_FILE,
|
||||
LANG_SEND,
|
||||
LANG_RECV
|
||||
};
|
||||
|
||||
class LanguageHelper {
|
||||
@@ -74,4 +79,4 @@ class LanguageHelper {
|
||||
static const char* englishMessages[];
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
27000000 1M 1076 4 100 01110111011101110101010101010101010101110111011101110101
|
||||
Reference in New Issue
Block a user