mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-16 03:22:59 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 64f8158134 | |||
| 59f72cbff1 | |||
| 31c53dc455 | |||
| ff12d06757 | |||
| 5b540fa659 | |||
| 9cccfa63e5 | |||
| 6c838256df | |||
| c8f236a708 | |||
| 4641dcb449 | |||
| 458a6634bf |
@@ -11,7 +11,7 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
|
||||
|
||||
[<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_front.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_inside.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview#portapack-internals)
|
||||
|
||||
*[PortaPack H2+HackRF+battery](https://grabify.link/BKKTWY) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure)*
|
||||
*[PortaPack H2+HackRF+battery](https://grabify.link/P9CO0J) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/3d-printed-enclosure)*
|
||||
|
||||
# What is this?
|
||||
|
||||
@@ -27,11 +27,11 @@ 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://grabify.link/VPMPSL), 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:  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).
|
||||
|
||||
:heavy_check_mark: A recommended one is this [PortaPack H2](https://grabify.link/BKKTWY), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure).
|
||||
: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).
|
||||
|
||||
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://grabify.link/CB3JC5), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
|
||||
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://grabify.link/LZPBH9), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
|
||||
|
||||
:warning: Be cautious , *ask* the seller about compatibility with the latest releases. Look out for the description of the item, if they provide the firmware files for an older version or they have custom setup instructions, this means it might be **NOT compatible**, for example:
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "ui_about_simple.hpp"
|
||||
|
||||
#define ROLL_SPEED_FRAME_PER_LINE 60
|
||||
// cuz frame rate of pp screen is probably 60, scroll per sec
|
||||
|
||||
namespace ui {
|
||||
|
||||
// TODO: Generate this automatically from github
|
||||
@@ -87,10 +90,42 @@ AboutView::AboutView(NavigationView& nav) {
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::on_frame_sync() {
|
||||
if (interacted) return;
|
||||
|
||||
if (frame_sync_count++ % ROLL_SPEED_FRAME_PER_LINE == 0) {
|
||||
const auto current = menu_view.highlighted_index();
|
||||
const auto count = menu_view.item_count();
|
||||
|
||||
if (current < count - 1) {
|
||||
menu_view.set_highlighted(current + 1);
|
||||
} else {
|
||||
menu_view.set_highlighted(0);
|
||||
// ^ to go back to the REAL top instead of make the 2 at the top to make the title disappeares
|
||||
menu_view.set_highlighted(2); // the first line that has human name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::focus() {
|
||||
button_ok.focus();
|
||||
menu_view.set_highlighted(2); // the first line that has human name
|
||||
}
|
||||
|
||||
bool AboutView::on_touch(const TouchEvent) {
|
||||
interacted = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AboutView::on_key(const KeyEvent) {
|
||||
interacted = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AboutView::on_encoder(const EncoderEvent) {
|
||||
interacted = true;
|
||||
menu_view.focus();
|
||||
// put focus on last text line to make it more obvious that list is scrollable
|
||||
menu_view.set_highlighted(10);
|
||||
return false;
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -14,6 +14,13 @@ class AboutView : public View {
|
||||
std::string title() const override { return "About"; };
|
||||
|
||||
private:
|
||||
virtual bool on_key(const KeyEvent event);
|
||||
virtual bool on_encoder(const EncoderEvent event);
|
||||
virtual bool on_touch(const TouchEvent event);
|
||||
|
||||
bool interacted{false};
|
||||
uint16_t frame_sync_count{0};
|
||||
void on_frame_sync();
|
||||
MenuView menu_view{
|
||||
{0, 0, 240, 264},
|
||||
true};
|
||||
@@ -22,6 +29,12 @@ class AboutView : public View {
|
||||
{240 / 3, 270, 240 / 3, 24},
|
||||
"OK",
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_frame_sync{
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
this->on_frame_sync();
|
||||
}};
|
||||
};
|
||||
} // namespace ui
|
||||
|
||||
|
||||
+29
-32
@@ -8,6 +8,7 @@
|
||||
#include "metadata_file.hpp"
|
||||
#include "oversample.hpp"
|
||||
#include "io_convert.hpp"
|
||||
#include "lfsr_random.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -80,7 +81,7 @@ void CVSSpamView::start_tx(const uint32_t id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t sample_rate = 500000;
|
||||
const uint32_t sample_rate = 250000;
|
||||
|
||||
current_file = cvsfiles_dir / file_list[id].filename();
|
||||
|
||||
@@ -149,36 +150,36 @@ void CVSSpamView::start_tx(const uint32_t id) {
|
||||
});
|
||||
}
|
||||
|
||||
void CVSSpamView::start_chaos_tx() {
|
||||
void CVSSpamView::start_random_tx() {
|
||||
if (is_active()) {
|
||||
stop_tx();
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path chaos_file_path = cvsfiles_dir / "chaos.c16";
|
||||
const uint32_t sample_rate = 500000;
|
||||
|
||||
File capture_file;
|
||||
auto open_error = capture_file.open(chaos_file_path);
|
||||
if (open_error) {
|
||||
file_error(chaos_file_path,
|
||||
"Cannot open CHAOS.C16.\n"
|
||||
"Initial file check failed.\n"
|
||||
"Path: " +
|
||||
cvsfiles_dir.string() +
|
||||
"\n"
|
||||
"Error: " +
|
||||
std::to_string(static_cast<uint32_t>(open_error)));
|
||||
if (file_list.empty()) {
|
||||
nav_.display_modal("Error", "No files found!");
|
||||
return;
|
||||
}
|
||||
|
||||
auto metadata_path = get_metadata_path(chaos_file_path);
|
||||
lfsr_v = lfsr_iterate(lfsr_v);
|
||||
size_t random_index = lfsr_v % file_list.size();
|
||||
|
||||
const uint32_t sample_rate = 250000;
|
||||
current_file = cvsfiles_dir / file_list[random_index].filename();
|
||||
|
||||
File capture_file;
|
||||
auto open_error = capture_file.open(current_file);
|
||||
if (open_error) {
|
||||
file_error(current_file, "Cannot open file.\nInitial check failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto metadata_path = get_metadata_path(current_file);
|
||||
auto metadata = read_metadata_file(metadata_path);
|
||||
if (!metadata) {
|
||||
metadata = capture_metadata{transmitter_model.target_frequency(), sample_rate};
|
||||
}
|
||||
|
||||
auto file_size = capture_file.size();
|
||||
capture_file.close();
|
||||
|
||||
replay_thread.reset();
|
||||
@@ -188,18 +189,8 @@ void CVSSpamView::start_chaos_tx() {
|
||||
baseband::set_sample_rate(metadata->sample_rate, get_oversample_rate(metadata->sample_rate));
|
||||
|
||||
auto reader = std::make_unique<FileConvertReader>();
|
||||
if (auto error = reader->open(chaos_file_path)) {
|
||||
file_error(chaos_file_path,
|
||||
"Cannot read CHAOS.C16.\n"
|
||||
"Check file format/perms.\n"
|
||||
"Rate: " +
|
||||
to_string_dec_uint(metadata->sample_rate) +
|
||||
"\n"
|
||||
"Size: " +
|
||||
to_string_dec_uint(file_size) +
|
||||
"\n"
|
||||
"Error: " +
|
||||
std::to_string(static_cast<uint32_t>(error)));
|
||||
if (auto error = reader->open(current_file)) {
|
||||
file_error(current_file, "Cannot read file data.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -241,9 +232,9 @@ bool CVSSpamView::is_active() const {
|
||||
void CVSSpamView::stop_tx() {
|
||||
replay_thread.reset();
|
||||
transmitter_model.disable();
|
||||
audio::output::stop();
|
||||
ready_signal = false;
|
||||
thread_sync_complete = false;
|
||||
chaos_mode = false;
|
||||
progressbar.set_value(0);
|
||||
chThdSleepMilliseconds(50);
|
||||
}
|
||||
@@ -295,7 +286,13 @@ CVSSpamView::CVSSpamView(NavigationView& nav)
|
||||
};
|
||||
|
||||
button_chaos.on_select = [this](Button&) {
|
||||
start_chaos_tx();
|
||||
if (is_active()) {
|
||||
chaos_mode = false;
|
||||
stop_tx();
|
||||
} else {
|
||||
chaos_mode = true;
|
||||
start_random_tx();
|
||||
}
|
||||
};
|
||||
|
||||
button_stop.on_select = [this](Button&) {
|
||||
|
||||
+24
-3
@@ -11,7 +11,6 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "audio.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -29,6 +28,9 @@ class CVSSpamView : public View {
|
||||
static constexpr size_t read_size = 0x4000;
|
||||
static constexpr size_t buffer_count = 3;
|
||||
|
||||
lfsr_word_t lfsr_v = 1;
|
||||
bool chaos_mode{false};
|
||||
|
||||
NavigationView& nav_;
|
||||
std::unique_ptr<ReplayThread> replay_thread{};
|
||||
bool ready_signal{false};
|
||||
@@ -40,7 +42,7 @@ class CVSSpamView : public View {
|
||||
void file_error(const std::filesystem::path& path, const std::string& error_details);
|
||||
void refresh_list();
|
||||
void start_tx(const uint32_t id);
|
||||
void start_chaos_tx();
|
||||
void start_random_tx();
|
||||
void on_tx_progress(const uint32_t progress);
|
||||
|
||||
uint32_t page = 1;
|
||||
@@ -102,12 +104,31 @@ class CVSSpamView : public View {
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
if (message.return_code == ReplayThread::END_OF_FILE) {
|
||||
if (is_active()) {
|
||||
if (chaos_mode) {
|
||||
replay_thread.reset();
|
||||
transmitter_model.disable();
|
||||
ready_signal = false;
|
||||
lfsr_v = lfsr_iterate(lfsr_v);
|
||||
size_t random_index = lfsr_v % file_list.size();
|
||||
menu_view.set_highlighted(random_index);
|
||||
chThdSleepMilliseconds(100);
|
||||
start_tx(random_index);
|
||||
} else {
|
||||
thread_sync_complete = true;
|
||||
stop_tx();
|
||||
}
|
||||
} else if (message.return_code == ReplayThread::READ_ERROR) {
|
||||
file_error(file_list[menu_view.highlighted_index()], "Read error during playback");
|
||||
if (chaos_mode) {
|
||||
replay_thread.reset();
|
||||
transmitter_model.disable();
|
||||
ready_signal = false;
|
||||
lfsr_v = lfsr_iterate(lfsr_v);
|
||||
size_t random_index = lfsr_v % file_list.size();
|
||||
menu_view.set_highlighted(random_index);
|
||||
chThdSleepMilliseconds(100);
|
||||
start_tx(random_index);
|
||||
}
|
||||
}
|
||||
}};
|
||||
};
|
||||
|
||||
+17
-1
@@ -111,9 +111,22 @@ set(EXTCPPSRC
|
||||
external/shoppingcart_lock/main.cpp
|
||||
external/shoppingcart_lock/shoppingcart_lock.cpp
|
||||
|
||||
#ookbrute
|
||||
external/ookbrute/main.cpp
|
||||
external/ookbrute/ui_ookbrute.cpp
|
||||
|
||||
#rook
|
||||
external/rook/main.cpp
|
||||
external/rook/ui_rook.cpp
|
||||
|
||||
#cvs_spam
|
||||
external/cvs_spam/main.cpp
|
||||
external/cvs_spam/cvs_spam.cpp
|
||||
external/cvs_spam/cvs_spam.cpp
|
||||
|
||||
#flippertx
|
||||
external/flippertx/main.cpp
|
||||
external/flippertx/ui_flippertx.cpp
|
||||
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -143,5 +156,8 @@ set(EXTAPPLIST
|
||||
sstvtx
|
||||
random_password
|
||||
#acars_rx
|
||||
ookbrute
|
||||
rook
|
||||
shoppingcart_lock
|
||||
flippertx
|
||||
)
|
||||
|
||||
+21
@@ -50,6 +50,9 @@ MEMORY
|
||||
ram_external_app_acars_rx(rwx) : org = 0xADC90000, len = 32k
|
||||
ram_external_app_shoppingcart_lock(rwx) : org = 0xADCA0000, len = 32k
|
||||
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
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -217,4 +220,22 @@ SECTIONS
|
||||
*(*ui*external_app*cvs_spam*);
|
||||
} > ram_external_app_cvs_spam
|
||||
|
||||
.external_app_ookbrute : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_ookbrute.application_information));
|
||||
*(*ui*external_app*ookbrute*);
|
||||
} > ram_external_app_ookbrute
|
||||
|
||||
.external_app_rook : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_rook.application_information));
|
||||
*(*ui*external_app*rook*);
|
||||
} > ram_external_app_rook
|
||||
|
||||
.external_app_flippertx : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_flippertx.application_information));
|
||||
*(*ui*external_app*flippertx*);
|
||||
} > ram_external_app_flippertx
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_flippertx.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::flippertx {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<FlipperTxView>();
|
||||
}
|
||||
} // namespace ui::external_app::flippertx
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_flippertx.application_information"), used)) application_information_t _application_information_flippertx = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::flippertx::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "FlipperTx",
|
||||
/*.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::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_ookstreaming */ {'P', 'O', 'S', 'K'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui_flippertx.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "buffer_exchange.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::flippertx {
|
||||
|
||||
void FlipperTxView::focus() {
|
||||
button_browse.focus();
|
||||
}
|
||||
|
||||
void FlipperTxView::set_ready() {
|
||||
ready_signal = true;
|
||||
}
|
||||
|
||||
FlipperTxView::FlipperTxView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
add_children({&button_startstop,
|
||||
&field_frequency,
|
||||
&tx_view,
|
||||
&field_filename,
|
||||
&button_browse});
|
||||
|
||||
button_startstop.on_select = [this](Button&) {
|
||||
if (is_running) {
|
||||
stop();
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
};
|
||||
|
||||
button_browse.on_select = [this](Button&) {
|
||||
auto open_view = nav_.push<FileLoadView>(".sub");
|
||||
open_view->push_dir(subghz_dir);
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
if (on_file_changed(new_file_path)) {
|
||||
; // tif (button_startstop.parent()) button_startstop.focus(); parent_ is null error.....
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
bool FlipperTxView::on_file_changed(std::filesystem::path new_file_path) {
|
||||
submeta = read_flippersub_file(new_file_path);
|
||||
filename = "";
|
||||
if (!submeta.is_valid()) {
|
||||
field_filename.set_text("File: err, bad file");
|
||||
return false;
|
||||
}
|
||||
proto = submeta.value().protocol;
|
||||
if (proto != FLIPPER_PROTO_RAW && proto != FLIPPER_PROTO_BINRAW) { // temp disabled
|
||||
field_filename.set_text("File: err, not supp. proto");
|
||||
return false;
|
||||
}
|
||||
preset = submeta.value().preset;
|
||||
if (preset != FLIPPER_PRESET_OOK) {
|
||||
field_filename.set_text("File: err, not supp. preset");
|
||||
return false;
|
||||
}
|
||||
te = submeta.value().te;
|
||||
binraw_bit_count = submeta.value().binraw_bit_count;
|
||||
/*if (binraw_bit_count >= 512 * 800) {
|
||||
field_filename.set_text("File: err, too long"); // should stream, but not in this scope YET
|
||||
return false;
|
||||
}*/
|
||||
field_filename.set_text("File: " + new_file_path.string());
|
||||
filename = new_file_path;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlipperTxView::stop() {
|
||||
if (is_running && replay_thread) replay_thread.reset();
|
||||
is_running = false;
|
||||
ready_signal = false;
|
||||
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
button_startstop.set_text(LanguageHelper::currentMessages[LANG_START]);
|
||||
}
|
||||
|
||||
bool FlipperTxView::start() {
|
||||
if (filename.empty()) return false;
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
transmitter_model.enable();
|
||||
button_startstop.set_text(LanguageHelper::currentMessages[LANG_STOP]);
|
||||
// start thread
|
||||
replay_thread = std::make_unique<FlipperPlayThread>(
|
||||
filename,
|
||||
1024, 4,
|
||||
&ready_signal,
|
||||
submeta.value().protocol,
|
||||
submeta.value().te,
|
||||
[](uint32_t return_code) {
|
||||
ReplayThreadDoneMessage message{return_code};
|
||||
EventDispatcher::send_message(message);
|
||||
});
|
||||
is_running = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlipperTxView::on_tx_progress(const bool done) {
|
||||
if (done) {
|
||||
if (is_running) {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FlipperTxView::~FlipperTxView() {
|
||||
stop();
|
||||
}
|
||||
|
||||
FlipperPlayThread::FlipperPlayThread(
|
||||
std::filesystem::path filename,
|
||||
size_t read_size,
|
||||
size_t buffer_count,
|
||||
bool* ready_signal,
|
||||
FlipperProto proto,
|
||||
uint16_t te,
|
||||
std::function<void(uint32_t return_code)> terminate_callback)
|
||||
: config{read_size, buffer_count},
|
||||
filename{filename},
|
||||
ready_sig{ready_signal},
|
||||
proto{proto},
|
||||
te{te},
|
||||
terminate_callback{std::move(terminate_callback)} {
|
||||
thread = chThdCreateFromHeap(NULL, 6 * 1024, NORMALPRIO + 10, FlipperPlayThread::static_fn, this);
|
||||
}
|
||||
|
||||
FlipperPlayThread::~FlipperPlayThread() {
|
||||
if (thread) {
|
||||
chThdTerminate(thread);
|
||||
if (thread->p_refs) chThdWait(thread);
|
||||
thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
msg_t FlipperPlayThread::static_fn(void* arg) {
|
||||
auto obj = static_cast<FlipperPlayThread*>(arg);
|
||||
const auto return_code = obj->run();
|
||||
if (obj->terminate_callback) {
|
||||
obj->terminate_callback(return_code);
|
||||
}
|
||||
chThdExit(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t FlipperPlayThread::run() {
|
||||
BasebandReplay replay{&config};
|
||||
BufferExchange buffers{&config};
|
||||
StreamBuffer* prefill_buffer{nullptr};
|
||||
int32_t read_result = 0;
|
||||
// assume it is ok, since prev we checked it.
|
||||
// open the sub file
|
||||
File f; // don't worry, destructor will close it.
|
||||
auto error = f.open(filename);
|
||||
if (error) return READ_ERROR;
|
||||
// seek to the first data
|
||||
if (proto == FLIPPER_PROTO_RAW)
|
||||
seek_flipper_raw_first_data(f);
|
||||
else {
|
||||
seek_flipper_binraw_first_data(f);
|
||||
}
|
||||
// Wait for FIFOs to be allocated in baseband
|
||||
// Wait for _view to tell us that the buffers are ready
|
||||
while (!(*ready_sig) && !chThdShouldTerminate()) {
|
||||
chThdSleep(100);
|
||||
};
|
||||
uint8_t readall = 0;
|
||||
int32_t endsignals[3] = {0, 42069, 613379}; // unlikely a valid data
|
||||
// While empty buffers fifo is not empty...
|
||||
while (!buffers.empty() && !chThdShouldTerminate()) {
|
||||
prefill_buffer = buffers.get_prefill();
|
||||
|
||||
if (prefill_buffer == nullptr) {
|
||||
buffers.put_app(prefill_buffer);
|
||||
} else {
|
||||
size_t c = 0;
|
||||
for (c = 0; c < config.read_size / 4;) {
|
||||
read_result = 0;
|
||||
if (0 == readall) {
|
||||
if (proto == FLIPPER_PROTO_RAW) {
|
||||
auto data = read_flipper_raw_next_data(f);
|
||||
if (!data.is_valid()) {
|
||||
((int32_t*)prefill_buffer->data())[c] = endsignals[++readall];
|
||||
} else {
|
||||
read_result = data.value();
|
||||
((int32_t*)prefill_buffer->data())[c] = read_result;
|
||||
}
|
||||
c++;
|
||||
|
||||
} else { // BINRAW
|
||||
auto data = read_flipper_binraw_next_data(f);
|
||||
if (!data.is_valid())
|
||||
readall = 1;
|
||||
else {
|
||||
read_result = data.value();
|
||||
// add 8 data
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
((int32_t*)prefill_buffer->data())[c + bit] = ((get_flipper_binraw_bitvalue(read_result, (7 - bit)) ? 1 : -1) * te);
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
((int32_t*)prefill_buffer->data())[c] = endsignals[readall];
|
||||
if (readall == 1) readall++;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
prefill_buffer->set_size(config.read_size);
|
||||
buffers.put(prefill_buffer);
|
||||
}
|
||||
};
|
||||
baseband::set_fifo_data(nullptr);
|
||||
if (readall) return END_OF_FILE;
|
||||
|
||||
while (!chThdShouldTerminate()) {
|
||||
auto buffer = buffers.get();
|
||||
int32_t read_result = 0;
|
||||
for (size_t d = 0; d < buffer->capacity() / 4; d++) {
|
||||
read_result = -133469;
|
||||
if (!readall) {
|
||||
if (proto == FLIPPER_PROTO_RAW) {
|
||||
auto data = read_flipper_raw_next_data(f);
|
||||
if (!data.is_valid()) {
|
||||
readall = 1;
|
||||
} else {
|
||||
read_result = data.value();
|
||||
}
|
||||
} else {
|
||||
auto data = read_flipper_binraw_next_data(f);
|
||||
if (!data.is_valid())
|
||||
readall = 1;
|
||||
else {
|
||||
read_result = data.value();
|
||||
// add 8 data
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
((int32_t*)prefill_buffer->data())[d + bit] = ((get_flipper_binraw_bitvalue(read_result, (7 - bit)) ? 1 : -1) * te);
|
||||
}
|
||||
d += 7;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (readall >= 1 && readall <= 2) {
|
||||
read_result = endsignals[readall++];
|
||||
}
|
||||
((int32_t*)buffer->data())[d] = read_result;
|
||||
}
|
||||
buffer->set_size(buffer->capacity());
|
||||
buffers.put(buffer);
|
||||
if (readall == 3) return END_OF_FILE;
|
||||
}
|
||||
return TERMINATED;
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::flippertx
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __UI_flippertx_H__
|
||||
#define __UI_flippertx_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "file_path.hpp"
|
||||
#include "metadata_file.hpp"
|
||||
#include "flipper_subfile.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::flippertx {
|
||||
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
class FlipperPlayThread;
|
||||
class FlipperTxView : public View {
|
||||
public:
|
||||
FlipperTxView(NavigationView& nav);
|
||||
~FlipperTxView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override {
|
||||
return "FlipperTx";
|
||||
};
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
TxRadioState radio_state_{
|
||||
433920000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
OOK_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
|
||||
TxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
nav_};
|
||||
TransmitterView2 tx_view{
|
||||
{11 * 8, 0 * 16},
|
||||
/*short_ui*/ true};
|
||||
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_flippertx", app_settings::Mode::TX};
|
||||
|
||||
TextField field_filename{
|
||||
{0, 2 * 16, 300, 1 * 8},
|
||||
"File: -"};
|
||||
|
||||
Button button_startstop{
|
||||
{1, 6 * 16, 96, 24},
|
||||
LanguageHelper::currentMessages[LANG_START]};
|
||||
|
||||
Button button_browse{
|
||||
{1, 3 * 16 + 3, 96, 24},
|
||||
LanguageHelper::currentMessages[LANG_BROWSE]};
|
||||
|
||||
bool is_running{false};
|
||||
|
||||
bool start();
|
||||
void stop();
|
||||
|
||||
void set_ready();
|
||||
void on_tx_progress(const bool done);
|
||||
bool on_file_changed(std::filesystem::path new_file_path);
|
||||
|
||||
std::filesystem::path filename = {};
|
||||
FlipperProto proto = FLIPPER_PROTO_UNSUPPORTED;
|
||||
FlipperPreset preset = FLIPPER_PRESET_UNK;
|
||||
uint16_t te = 0; // for binraw
|
||||
uint32_t binraw_bit_count = 0;
|
||||
bool ready_signal = false;
|
||||
|
||||
std::unique_ptr<FlipperPlayThread> replay_thread{};
|
||||
Optional<flippersub_metadata> submeta{};
|
||||
|
||||
const std::filesystem::path subghz_dir = u"subghz";
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.done);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_fifo_signal{
|
||||
Message::ID::RequestSignal,
|
||||
[this](const Message* const p) {
|
||||
const auto message = static_cast<const RequestSignalMessage*>(p);
|
||||
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
|
||||
this->set_ready();
|
||||
}
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_replay_thread_done{
|
||||
Message::ID::ReplayThreadDone,
|
||||
[this](const Message* const p) {
|
||||
// const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
(void)p;
|
||||
// stop(); //don't stop for now, stop when i get the tx finished msg
|
||||
}};
|
||||
};
|
||||
|
||||
struct BasebandReplay {
|
||||
BasebandReplay(ReplayConfig* const config) {
|
||||
baseband::replay_start(config);
|
||||
}
|
||||
|
||||
~BasebandReplay() {
|
||||
// baseband::replay_stop(); // wont, since we need to send out that is in the buffer
|
||||
}
|
||||
};
|
||||
|
||||
class FlipperPlayThread {
|
||||
public:
|
||||
FlipperPlayThread(
|
||||
std::filesystem::path,
|
||||
size_t read_size,
|
||||
size_t buffer_count,
|
||||
bool* ready_signal,
|
||||
FlipperProto proto,
|
||||
uint16_t te,
|
||||
std::function<void(uint32_t return_code)> terminate_callback);
|
||||
~FlipperPlayThread();
|
||||
|
||||
FlipperPlayThread(const FlipperPlayThread&) = delete;
|
||||
FlipperPlayThread(FlipperPlayThread&&) = delete;
|
||||
FlipperPlayThread& operator=(const FlipperPlayThread&) = delete;
|
||||
FlipperPlayThread& operator=(FlipperPlayThread&&) = delete;
|
||||
|
||||
const ReplayConfig& state() const {
|
||||
return config;
|
||||
};
|
||||
|
||||
enum FlipperPlayThread_return {
|
||||
READ_ERROR = 0,
|
||||
END_OF_FILE,
|
||||
TERMINATED
|
||||
};
|
||||
|
||||
private:
|
||||
ReplayConfig config;
|
||||
std::filesystem::path filename;
|
||||
bool* ready_sig;
|
||||
FlipperProto proto;
|
||||
uint16_t te;
|
||||
std::function<void(uint32_t return_code)> terminate_callback;
|
||||
Thread* thread{nullptr};
|
||||
|
||||
static msg_t static_fn(void* arg);
|
||||
|
||||
uint32_t run();
|
||||
};
|
||||
}; // namespace ui::external_app::flippertx
|
||||
|
||||
#endif /*__UI_flippertx_H__*/
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_ookbrute.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::ookbrute {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<OokBruteView>();
|
||||
}
|
||||
} // namespace ui::external_app::ookbrute
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_ookbrute.application_information"), used)) application_information_t _application_information_ookbrute = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::ookbrute::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "OokBrute",
|
||||
/*.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::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.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,253 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui_ookbrute.hpp"
|
||||
#include "audio.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::ookbrute {
|
||||
|
||||
void OokBruteView::focus() {
|
||||
button_startstop.focus();
|
||||
}
|
||||
|
||||
OokBruteView::OokBruteView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
add_children({
|
||||
&button_startstop,
|
||||
&field_frequency,
|
||||
&tx_view,
|
||||
&options_atkmode,
|
||||
&field_start,
|
||||
&field_stop,
|
||||
});
|
||||
|
||||
button_startstop.on_select = [this](Button&) {
|
||||
if (is_running) {
|
||||
is_running = false;
|
||||
stop();
|
||||
} else {
|
||||
is_running = true;
|
||||
start();
|
||||
}
|
||||
};
|
||||
|
||||
options_atkmode.on_change = [this](size_t, int32_t i) {
|
||||
update_start_stop(i);
|
||||
validate_start_stop();
|
||||
};
|
||||
field_start.on_change = [this](int32_t) {
|
||||
validate_start_stop();
|
||||
};
|
||||
field_stop.on_change = [this](int32_t) {
|
||||
validate_start_stop();
|
||||
};
|
||||
update_start_stop(0);
|
||||
}
|
||||
|
||||
void OokBruteView::update_start_stop(uint32_t proto) {
|
||||
uint8_t bits = 12;
|
||||
switch (proto) {
|
||||
default:
|
||||
case 0:
|
||||
case 2:
|
||||
case 4:
|
||||
bits = 12;
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
case 5:
|
||||
bits = 24;
|
||||
break;
|
||||
}
|
||||
uint32_t max = (1 << bits) - 1;
|
||||
field_start.set_range(0, max);
|
||||
field_stop.set_range(0, max);
|
||||
field_start.set_value(0);
|
||||
field_stop.set_value(max);
|
||||
}
|
||||
|
||||
void OokBruteView::validate_start_stop() {
|
||||
if (field_start.value() > field_stop.value()) {
|
||||
field_start.set_value(field_stop.value());
|
||||
}
|
||||
if (field_stop.value() < field_start.value()) {
|
||||
field_stop.set_value(field_start.value());
|
||||
}
|
||||
}
|
||||
|
||||
void OokBruteView::generate_packet() {
|
||||
uint32_t protocol = options_atkmode.selected_index_value();
|
||||
uint8_t byte = 0;
|
||||
size_t bitstream_length = 0;
|
||||
uint8_t* bitstream = shared_memory.bb_data.data; // max 512 in size
|
||||
uint32_t samples_per_bit = 0; // OOK_SAMPLERATE * bit_duration_in_sec
|
||||
std::string dataFormat = "";
|
||||
std::string zero = "";
|
||||
std::string one = "";
|
||||
uint16_t databits = 0;
|
||||
uint16_t repeat = 1;
|
||||
uint16_t pause_sym = 0;
|
||||
if (protocol == 0) { // came 12
|
||||
samples_per_bit = OOK_SAMPLERATE / ((3 * 1000) / 1);
|
||||
dataFormat = "0000000000000000000000000000000000001CCCCCCCCCCCC0000"; // 36 0 preamble +start bit + data
|
||||
databits = 12;
|
||||
zero = "011";
|
||||
one = "001";
|
||||
repeat = 2;
|
||||
pause_sym = 0;
|
||||
}
|
||||
|
||||
if (protocol == 1) { // came24
|
||||
samples_per_bit = OOK_SAMPLERATE / ((3 * 1000) / 1);
|
||||
dataFormat = "0000000000000000000000000000000000001CCCCCCCCCCCCCCCCCCCCCCCC0000"; // 36 0 preamble +start bit + data
|
||||
databits = 24;
|
||||
zero = "011";
|
||||
one = "001";
|
||||
repeat = 2;
|
||||
pause_sym = 0;
|
||||
}
|
||||
if (protocol == 2) { // nice12
|
||||
samples_per_bit = OOK_SAMPLERATE * (680.0 / 1000000.0);
|
||||
dataFormat = "000000000000000000000000000000000000000001CCCCCCCCCCCC0000"; // 36 0 preamble +start bit + data
|
||||
databits = 12;
|
||||
zero = "011";
|
||||
one = "001";
|
||||
repeat = 2;
|
||||
pause_sym = 0;
|
||||
}
|
||||
if (protocol == 3) { // nice24
|
||||
samples_per_bit = OOK_SAMPLERATE * (680.0 / 1000000.0);
|
||||
dataFormat = "000000000000000000000000000000000000000001CCCCCCCCCCCCCCCCCCCCCCCC0000"; // 36 0 preamble +start bit + data
|
||||
databits = 24;
|
||||
zero = "011";
|
||||
one = "001";
|
||||
repeat = 2;
|
||||
pause_sym = 0;
|
||||
}
|
||||
if (protocol == 4) { // holtek_ht12
|
||||
samples_per_bit = OOK_SAMPLERATE * (390.0 / 1000000.0);
|
||||
dataFormat = "0000000000000000000000000000000000001CCCCCCCCCCCC00000000000"; // 36 0 preamble +start bit + data.
|
||||
databits = 12;
|
||||
zero = "011";
|
||||
one = "001";
|
||||
repeat = 2;
|
||||
pause_sym = 0;
|
||||
}
|
||||
if (protocol == 5) { // princeton24
|
||||
samples_per_bit = OOK_SAMPLERATE * (450.0 / 1000000.0); // long = 3*
|
||||
dataFormat = "000000000000000000000000000000000000CCCCCCCCCCCCCCCCCCCCCCCC10000000"; //
|
||||
databits = 24;
|
||||
zero = "1000";
|
||||
one = "1110";
|
||||
repeat = 6;
|
||||
pause_sym = 0;
|
||||
}
|
||||
|
||||
std::string fragments = ""; // storage
|
||||
|
||||
uint16_t cdb = 0; // current data bit
|
||||
for (auto c : dataFormat) { // generate fragments from template
|
||||
if (c == '0') fragments += '0';
|
||||
if (c == '1') fragments += '1';
|
||||
if (c == 'C') {
|
||||
if (counter & (1 << (databits - cdb - 1))) {
|
||||
fragments += one;
|
||||
} else {
|
||||
fragments += zero;
|
||||
}
|
||||
cdb++;
|
||||
}
|
||||
}
|
||||
|
||||
// create bitstream
|
||||
for (auto c : fragments) {
|
||||
byte <<= 1;
|
||||
if (c != '0') byte |= 1;
|
||||
if ((bitstream_length & 7) == 7)
|
||||
bitstream[bitstream_length >> 3] = byte;
|
||||
bitstream_length++;
|
||||
}
|
||||
|
||||
// Finish last byte if needed
|
||||
size_t padding = 8 - (bitstream_length & 7);
|
||||
if (padding != 8) {
|
||||
byte <<= padding;
|
||||
bitstream[(bitstream_length + padding - 1) >> 3] = byte;
|
||||
padding++;
|
||||
}
|
||||
|
||||
// send bitstream
|
||||
baseband::set_ook_data(
|
||||
bitstream_length,
|
||||
samples_per_bit,
|
||||
repeat,
|
||||
pause_sym,
|
||||
0);
|
||||
}
|
||||
|
||||
void OokBruteView::stop() {
|
||||
transmitter_model.disable();
|
||||
baseband::shutdown();
|
||||
button_startstop.set_text(LanguageHelper::currentMessages[LANG_START]);
|
||||
}
|
||||
|
||||
void OokBruteView::start() {
|
||||
counter = field_start.value();
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
transmitter_model.enable();
|
||||
button_startstop.set_text(LanguageHelper::currentMessages[LANG_STOP]);
|
||||
generate_packet();
|
||||
}
|
||||
|
||||
void OokBruteView::on_tx_progress(const bool done) {
|
||||
if (done) {
|
||||
if (is_running) {
|
||||
counter++;
|
||||
field_start.set_value(counter);
|
||||
if (counter > (uint32_t)field_stop.value()) {
|
||||
stop();
|
||||
} else {
|
||||
generate_packet();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OokBruteView::~OokBruteView() {
|
||||
is_running = false;
|
||||
stop();
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::ookbrute
|
||||
|
||||
/*
|
||||
|
||||
https://web.archive.org/web/20230331125843/https://phreakerclub.com/447
|
||||
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __UI_OOKBRUTE_H__
|
||||
#define __UI_OOKBRUTE_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_transmitter.hpp"
|
||||
#include "ui_freq_field.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "radio_state.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::ookbrute {
|
||||
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
|
||||
class OokBruteView : public View {
|
||||
public:
|
||||
OokBruteView(NavigationView& nav);
|
||||
~OokBruteView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override {
|
||||
return "OokBrute";
|
||||
};
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
TxRadioState radio_state_{
|
||||
433920000 /* frequency */,
|
||||
1750000 /* bandwidth */,
|
||||
OOK_SAMPLERATE /* sampling rate */
|
||||
};
|
||||
|
||||
TxFrequencyField field_frequency{
|
||||
{0 * 8, 0 * 16},
|
||||
nav_};
|
||||
TransmitterView2 tx_view{
|
||||
{11 * 8, 0 * 16},
|
||||
/*short_ui*/ true};
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_ookbrute", app_settings::Mode::TX};
|
||||
|
||||
Button button_startstop{
|
||||
{0, 3 * 16, 96, 24},
|
||||
LanguageHelper::currentMessages[LANG_START]};
|
||||
|
||||
NumberField field_start{
|
||||
{0 * 8, 1 * 16},
|
||||
8,
|
||||
{0, 2500},
|
||||
1,
|
||||
' ',
|
||||
true};
|
||||
|
||||
NumberField field_stop{
|
||||
{11 * 8, 1 * 16},
|
||||
9,
|
||||
{0, 2500},
|
||||
1,
|
||||
' ',
|
||||
true};
|
||||
|
||||
OptionsField options_atkmode{
|
||||
{0 * 8, 2 * 16},
|
||||
12,
|
||||
{{"Came12", 0},
|
||||
{"Came24", 1},
|
||||
{"Nice12", 2},
|
||||
{"Nice24", 3},
|
||||
{"Holtek12", 4},
|
||||
{"Princeton24", 5}}};
|
||||
|
||||
bool is_running{false};
|
||||
|
||||
uint32_t counter = 0; // for packet change
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void on_tx_progress(const bool done);
|
||||
void validate_start_stop();
|
||||
void update_start_stop(uint32_t proto);
|
||||
void generate_packet();
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
|
||||
this->on_tx_progress(message.done);
|
||||
}};
|
||||
};
|
||||
}; // namespace ui::external_app::ookbrute
|
||||
|
||||
#endif /*__UI_OOKBRUTE_H__*/
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.hpp"
|
||||
#include "ui_rook.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::rook {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<ROOKAppView>();
|
||||
}
|
||||
} // namespace ui::external_app::rook
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_rook.application_information"), used)) application_information_t _application_information_rook = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::rook::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "ROOK",
|
||||
/*.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::orange().v,
|
||||
/*.menu_location = */ app_location_t::TX,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_ook */ {'P', 'O', 'O', 'K'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* 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
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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,3 +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";
|
||||
|
||||
@@ -50,5 +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;
|
||||
|
||||
#endif /* __FILE_PATH_H__ */
|
||||
|
||||
@@ -29,15 +29,16 @@
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
const std::string_view filetype_name = "Filetype"sv;
|
||||
const std::string_view frequency_name = "Frequency"sv;
|
||||
const std::string_view latitude_name_old = "Latitute"sv;
|
||||
const std::string_view longitude_name_old = "Longitude"sv;
|
||||
const std::string_view latitude_name = "Lat"sv;
|
||||
const std::string_view longitude_name = "Lon"sv;
|
||||
const std::string_view protocol_name = "Protocol"sv;
|
||||
const std::string_view preset_name = "Preset"sv;
|
||||
const std::string_view te_name = "TE"sv; // only in BinRAW
|
||||
const std::string filetype_name = "Filetype";
|
||||
const std::string frequency_name = "Frequency";
|
||||
const std::string latitude_name_old = "Latitute";
|
||||
const std::string longitude_name_old = "Longitude";
|
||||
const std::string latitude_name = "Lat";
|
||||
const std::string longitude_name = "Lon";
|
||||
const std::string protocol_name = "Protocol";
|
||||
const std::string preset_name = "Preset";
|
||||
const std::string te_name = "TE"; // only in BinRAW
|
||||
const std::string bit_count_name = "Bit"; // for us, only in BinRAW
|
||||
|
||||
/*
|
||||
Filetype: Flipper SubGhz Key File
|
||||
@@ -72,39 +73,54 @@ raw_data- positive: carrier for n time, negative: no carrier for n time. (us)
|
||||
Optional<flippersub_metadata> read_flippersub_file(const fs::path& path) {
|
||||
File f;
|
||||
auto error = f.open(path);
|
||||
|
||||
if (error)
|
||||
return {};
|
||||
|
||||
flippersub_metadata metadata{};
|
||||
|
||||
auto reader = FileLineReader(f);
|
||||
for (const auto& line : reader) {
|
||||
auto cols = split_string(line, ':');
|
||||
|
||||
if (cols.size() != 2)
|
||||
char ch = 0;
|
||||
std::string line = "";
|
||||
auto fr = f.read(&ch, 1);
|
||||
while (!fr.is_error() && fr.value() > 0) {
|
||||
if (line.length() < 130 && ch != '\n') line += ch;
|
||||
if (ch != '\n') {
|
||||
fr = f.read(&ch, 1);
|
||||
continue;
|
||||
}
|
||||
auto it = line.find(':', 0);
|
||||
if (it == std::string::npos) {
|
||||
fr = f.read(&ch, 1);
|
||||
continue; // Bad line.
|
||||
if (cols[1].length() <= 1) continue;
|
||||
std::string fixed = cols[1].data() + 1;
|
||||
}
|
||||
std::string fixed = line.data() + it + 1;
|
||||
fixed = trim(fixed);
|
||||
if (cols[0] == filetype_name) {
|
||||
std::string head = line.substr(0, it);
|
||||
line = "";
|
||||
|
||||
if (fixed.length() <= 1) {
|
||||
fr = f.read(&ch, 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (head == filetype_name) {
|
||||
if (fixed != "Flipper SubGhz Key File" && fixed != "Flipper SubGhz RAW File") return {}; // not supported
|
||||
} else if (cols[0] == frequency_name)
|
||||
} else if (head == frequency_name)
|
||||
parse_int(fixed, metadata.center_frequency);
|
||||
else if (cols[0] == latitude_name)
|
||||
else if (head == latitude_name)
|
||||
parse_float_meta(fixed, metadata.latitude);
|
||||
else if (cols[0] == longitude_name)
|
||||
else if (head == longitude_name)
|
||||
parse_float_meta(fixed, metadata.longitude);
|
||||
else if (cols[0] == latitude_name_old)
|
||||
else if (head == latitude_name_old)
|
||||
parse_float_meta(fixed, metadata.latitude);
|
||||
else if (cols[0] == longitude_name_old)
|
||||
else if (head == longitude_name_old)
|
||||
parse_float_meta(fixed, metadata.longitude);
|
||||
else if (cols[0] == protocol_name) {
|
||||
else if (head == protocol_name) {
|
||||
if (fixed == "RAW") metadata.protocol = FLIPPER_PROTO_RAW;
|
||||
if (fixed == "BinRAW") metadata.protocol = FLIPPER_PROTO_BINRAW;
|
||||
} else if (cols[0] == te_name) {
|
||||
} else if (head == te_name) {
|
||||
metadata.te = atoi(fixed.c_str());
|
||||
} else if (cols[0] == preset_name) {
|
||||
} else if (head == bit_count_name) {
|
||||
metadata.binraw_bit_count = atol(fixed.c_str());
|
||||
} else if (head == preset_name) {
|
||||
if (fixed.find("FSK") != std::string::npos) {
|
||||
metadata.preset = FLIPPER_PRESET_2FSK;
|
||||
} else if (fixed.find("Ook") != std::string::npos) {
|
||||
@@ -112,12 +128,92 @@ Optional<flippersub_metadata> read_flippersub_file(const fs::path& path) {
|
||||
} else if (fixed.find("Custom") != std::string::npos) {
|
||||
metadata.preset = FLIPPER_PRESET_CUSTOM;
|
||||
}
|
||||
|
||||
} else
|
||||
continue;
|
||||
}
|
||||
fr = f.read(&ch, 1);
|
||||
}
|
||||
|
||||
f.close();
|
||||
if (metadata.center_frequency == 0) return {}; // Parse failed.
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
bool seek_flipper_raw_first_data(File& f) {
|
||||
f.seek(0);
|
||||
std::string chs = "";
|
||||
char ch;
|
||||
while (f.read(&ch, 1)) {
|
||||
if (ch == '\r') continue;
|
||||
if (ch == '\n') {
|
||||
chs = "";
|
||||
continue;
|
||||
};
|
||||
chs += ch;
|
||||
if (ch == 0) break;
|
||||
if (chs == "RAW_Data: ") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool seek_flipper_binraw_first_data(File& f, bool seekzero) {
|
||||
if (seekzero) f.seek(0);
|
||||
std::string chs = "";
|
||||
char ch;
|
||||
while (f.read(&ch, 1)) {
|
||||
if (ch == '\r') continue;
|
||||
if (ch == '\n') {
|
||||
chs = "";
|
||||
continue;
|
||||
};
|
||||
if (ch == 0) break;
|
||||
chs += ch;
|
||||
if (chs == "Data_RAW: ") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<int32_t> read_flipper_raw_next_data(File& f) {
|
||||
// RAW_Data: 5832 -12188 130 -162
|
||||
std::string chs = "";
|
||||
char ch = 0;
|
||||
while (f.read(&ch, 1).is_ok()) {
|
||||
if (ch == '\r') continue; // should not present
|
||||
if ((ch == ' ') || ch == '\n') {
|
||||
if (chs == "RAW_Data:") {
|
||||
chs = "";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
};
|
||||
if (ch == 0) break;
|
||||
chs += ch;
|
||||
}
|
||||
if (chs == "") return {};
|
||||
return atol(chs.c_str());
|
||||
}
|
||||
|
||||
Optional<uint8_t> read_flipper_binraw_next_data(File& f) {
|
||||
// Data_RAW: 02 10 84 BUT THERE ARE Bit_RAW lines to skip!
|
||||
std::string chs = "";
|
||||
char ch = 0;
|
||||
while (f.read(&ch, 1)) {
|
||||
if (ch == '\r') continue; // should not present
|
||||
if ((ch == ' ') || ch == '\n') {
|
||||
if (chs == "RAW_Data:") {
|
||||
chs = "";
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
};
|
||||
if (ch == 0) break;
|
||||
chs += ch;
|
||||
}
|
||||
if (chs == "") return {};
|
||||
return static_cast<uint8_t>(std::stoul(chs, nullptr, 16));
|
||||
}
|
||||
|
||||
bool get_flipper_binraw_bitvalue(uint8_t byte, uint8_t nthBit) {
|
||||
return (byte & (1 << nthBit)) != 0;
|
||||
}
|
||||
@@ -44,9 +44,15 @@ struct flippersub_metadata {
|
||||
FlipperProto protocol = FLIPPER_PROTO_UNSUPPORTED;
|
||||
FlipperPreset preset = FLIPPER_PRESET_UNK;
|
||||
uint16_t te = 0;
|
||||
uint32_t binraw_bit_count = 0;
|
||||
};
|
||||
|
||||
Optional<flippersub_metadata> read_flippersub_file(const std::filesystem::path& path);
|
||||
bool seek_flipper_raw_first_data(File& f);
|
||||
bool seek_flipper_binraw_first_data(File& f, bool seekzero = true);
|
||||
Optional<int32_t> read_flipper_raw_next_data(File& f);
|
||||
Optional<uint8_t> read_flipper_binraw_next_data(File& f);
|
||||
bool get_flipper_binraw_bitvalue(uint8_t byte, uint8_t nthBit);
|
||||
|
||||
// Maybe sometime there will be a data part reader / converter
|
||||
|
||||
|
||||
@@ -986,7 +986,7 @@ BMPView::BMPView(NavigationView& nav)
|
||||
|
||||
void BMPView::paint(Painter&) {
|
||||
if (!portapack::display.drawBMP2({0, 0}, splash_dot_bmp))
|
||||
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, (const uint8_t[]){0x29, 0x18, 0x16});
|
||||
portapack::display.drawBMP({0, 16}, splash_bmp, (const uint8_t[]){0x29, 0x18, 0x16});
|
||||
}
|
||||
|
||||
bool BMPView::on_touch(const TouchEvent event) {
|
||||
|
||||
@@ -656,6 +656,14 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PADT adsbtx)
|
||||
|
||||
### OOKStream
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_ook_stream_tx.cpp
|
||||
)
|
||||
DeclareTargets(POSK ookstream)
|
||||
|
||||
|
||||
|
||||
### HackRF "factory" firmware
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2024 HTotoo
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "proc_ook_stream_tx.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
OOKProcessorStreamed::OOKProcessorStreamed() {
|
||||
configured = false;
|
||||
baseband_thread.start();
|
||||
}
|
||||
|
||||
inline void OOKProcessorStreamed::write_sample(const buffer_c8_t& buffer, bool bit_value, size_t i) {
|
||||
int8_t re, im;
|
||||
|
||||
if (bit_value) {
|
||||
phase = (phase + 200); // What ?
|
||||
sphase = phase + (64 << 18);
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
|
||||
} else {
|
||||
re = 0;
|
||||
im = 0;
|
||||
}
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
|
||||
void OOKProcessorStreamed::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured || !stream) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
if (rem_samples <= curr_samples) {
|
||||
// get a new sample from stream
|
||||
int32_t sample = -13346;
|
||||
rem_samples = 0; // reset my pointer
|
||||
curr_samples = 0;
|
||||
size_t readed = 0;
|
||||
if (configured) readed = stream->read(&sample, 4); // read from stream // todo htotoo error handling?!
|
||||
if (readed == 0) {
|
||||
txprogress_message.progress = -10;
|
||||
shared_memory.application_queue.push(txprogress_message); // debug
|
||||
} else {
|
||||
if (sample == endsignals[readerrs + 1]) { // if no more samples, stop
|
||||
readerrs++;
|
||||
if (readerrs == 2) {
|
||||
configured = false;
|
||||
txprogress_message.done = true;
|
||||
txprogress_message.progress = 100;
|
||||
curr_hilow = false;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
}
|
||||
} else {
|
||||
if (sample < 0) {
|
||||
rem_samples = OOK_SAMPLERATE * ((-1 * sample) / 1000000.0);
|
||||
curr_hilow = false;
|
||||
} else {
|
||||
rem_samples = OOK_SAMPLERATE * (sample / 1000000.0);
|
||||
curr_hilow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++curr_samples;
|
||||
write_sample(buffer, curr_hilow, i);
|
||||
}
|
||||
}
|
||||
|
||||
void OOKProcessorStreamed::on_message(const Message* const message) {
|
||||
switch (message->id) {
|
||||
case Message::ID::ReplayConfig:
|
||||
configured = false;
|
||||
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::FIFOData:
|
||||
configured = true;
|
||||
txprogress_message.progress = -4;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OOKProcessorStreamed::replay_config(const ReplayConfigMessage& message) {
|
||||
if (message.config) {
|
||||
txprogress_message.progress = -2;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
stream = std::make_unique<StreamOutput>(message.config);
|
||||
// Tell application that the buffers and FIFO pointers are ready, prefill
|
||||
RequestSignalMessage sig_message{RequestSignalMessage::Signal::FillRequest};
|
||||
shared_memory.application_queue.push(sig_message);
|
||||
} else {
|
||||
txprogress_message.progress = -3;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher{std::make_unique<OOKProcessorStreamed>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
For usage check the FlipperTX app. Don't forget to "close" the tx by sending 42069, 613379 values at the end of the stream.
|
||||
Only close baseband when you get the TX done msg, not on replay thread done, because, there can still data in the buffer.
|
||||
*/
|
||||
|
||||
#ifndef __PROC_OOKSTREAMTX_HPP__
|
||||
#define __PROC_OOKSTREAMTX_HPP__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "stream_output.hpp"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
|
||||
class OOKProcessorStreamed : public BasebandProcessor {
|
||||
public:
|
||||
OOKProcessorStreamed();
|
||||
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
uint32_t rem_samples = 0;
|
||||
uint32_t curr_samples = 0;
|
||||
bool curr_hilow = false;
|
||||
|
||||
uint32_t phase{0}, sphase{0};
|
||||
void write_sample(const buffer_c8_t& buffer, bool bit_value, size_t i);
|
||||
|
||||
std::unique_ptr<StreamOutput> stream{};
|
||||
bool configured{false};
|
||||
void replay_config(const ReplayConfigMessage& message);
|
||||
|
||||
int32_t endsignals[3] = {0, 42069, 613379}; // 0 is skipped, count from 1, don't ask...
|
||||
uint8_t readerrs = 0; // to count in the array
|
||||
|
||||
TXProgressMessage txprogress_message{};
|
||||
/* NB: Threads should be the last members in the class definition. */
|
||||
BasebandThread baseband_thread{OOK_SAMPLERATE, this, baseband::Direction::Transmit};
|
||||
};
|
||||
|
||||
#endif /*__PROC_OOKSTREAMTX_HPP__*/
|
||||
@@ -104,6 +104,7 @@ constexpr image_tag_t image_tag_fskrx{'P', 'F', 'S', 'R'};
|
||||
constexpr image_tag_t image_tag_jammer{'P', 'J', 'A', 'M'};
|
||||
constexpr image_tag_t image_tag_mic_tx{'P', 'M', 'T', 'X'};
|
||||
constexpr image_tag_t image_tag_ook{'P', 'O', 'O', 'K'};
|
||||
constexpr image_tag_t image_tag_ookstream{'P', 'O', 'S', 'K'};
|
||||
constexpr image_tag_t image_tag_rds{'P', 'R', 'D', 'S'};
|
||||
constexpr image_tag_t image_tag_replay{'P', 'R', 'E', 'P'};
|
||||
constexpr image_tag_t image_tag_gps{'P', 'G', 'P', 'S'};
|
||||
|
||||
@@ -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"};
|
||||
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"};
|
||||
|
||||
// multi language support will changes (not in use for now)
|
||||
const char** LanguageHelper::currentMessages = englishMessages;
|
||||
|
||||
@@ -60,7 +60,8 @@ enum LangConsts {
|
||||
LANG_SHOWQR,
|
||||
LANG_SAVE,
|
||||
LANG_LOCK,
|
||||
LANG_UNLOCK
|
||||
LANG_UNLOCK,
|
||||
LANG_BROWSE
|
||||
};
|
||||
|
||||
class LanguageHelper {
|
||||
|
||||
@@ -27,6 +27,9 @@ import sys
|
||||
|
||||
cppheader = """#include "ui_about_simple.hpp"
|
||||
|
||||
#define ROLL_SPEED_FRAME_PER_LINE 60
|
||||
// cuz frame rate of pp screen is probably 60, scroll per sec
|
||||
|
||||
namespace ui {
|
||||
|
||||
// Information: a line starting with a '#' will be yellow coloured
|
||||
@@ -69,10 +72,42 @@ AboutView::AboutView(NavigationView& nav) {
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::on_frame_sync() {
|
||||
if (interacted) return;
|
||||
|
||||
if (frame_sync_count++ % ROLL_SPEED_FRAME_PER_LINE == 0) {
|
||||
const auto current = menu_view.highlighted_index();
|
||||
const auto count = menu_view.item_count();
|
||||
|
||||
if (current < count - 1) {
|
||||
menu_view.set_highlighted(current + 1);
|
||||
} else {
|
||||
menu_view.set_highlighted(0);
|
||||
// ^ to go back to the REAL top instead of make the 2 at the top to make the title disappeares
|
||||
menu_view.set_highlighted(2); // the first line that has human name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::focus() {
|
||||
button_ok.focus();
|
||||
menu_view.set_highlighted(2); // the first line that has human name
|
||||
}
|
||||
|
||||
bool AboutView::on_touch(const TouchEvent) {
|
||||
interacted = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AboutView::on_key(const KeyEvent) {
|
||||
interacted = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AboutView::on_encoder(const EncoderEvent) {
|
||||
interacted = true;
|
||||
menu_view.focus();
|
||||
// put focus on last text line to make it more obvious that list is scrollable
|
||||
menu_view.set_highlighted(10);
|
||||
return false;
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user