Make the CVS Spam app (#2352)

This commit is contained in:
RocketGod 2024-11-08 13:26:04 -08:00 committed by GitHub
parent 09dff447de
commit b8a64598ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 474 additions and 1 deletions

View File

@ -0,0 +1,313 @@
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
// If you can read this, you're a nerd. :P
// Come join us at https://discord.gg/thepiratesreborn
#include "cvs_spam.hpp"
#include "io_file.hpp"
#include "metadata_file.hpp"
#include "oversample.hpp"
#include "io_convert.hpp"
using namespace portapack;
namespace ui::external_app::cvs_spam {
void CVSSpamView::file_error(const std::filesystem::path& path, const std::string& error_details) {
nav_.display_modal("Error",
"File error:\n" + path.string() + "\n" + error_details);
}
void CVSSpamView::refresh_list() {
file_list.clear();
current_page = page;
uint32_t count = 0;
for (const auto& entry : std::filesystem::directory_iterator(cvsfiles_dir, u"*")) {
if (std::filesystem::is_regular_file(entry.status())) {
auto filename = entry.path().filename().string();
auto extension = entry.path().extension().string();
std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
if (extension == ".c16" || extension == ".cu8") {
if (count >= (page - 1) * 50 && count < page * 50) {
file_list.push_back(entry.path());
if (file_list.size() == 50) {
page++;
break;
}
}
count++;
}
}
}
if (file_list.empty()) {
if (page == 1) {
menu_view.hidden(true);
text_empty.hidden(false);
} else {
page = 1;
refresh_list();
return;
}
} else {
menu_view.hidden(false);
text_empty.hidden(true);
menu_view.clear();
for (const auto& file : file_list) {
menu_view.add_item({file.filename().string(),
ui::Theme::getInstance()->fg_green->foreground,
nullptr,
[this](KeyEvent key) {
if (key == KeyEvent::Select) {
start_tx(menu_view.highlighted_index());
}
}});
}
page_info.set("Page " + std::to_string(current_page));
menu_view.set_highlighted(0);
}
if (file_list.size() < 50) {
page = 1;
}
}
void CVSSpamView::start_tx(const uint32_t id) {
if (is_active()) {
stop_tx();
return;
}
const uint32_t sample_rate = 500000;
current_file = cvsfiles_dir / file_list[id].filename();
File capture_file;
auto open_error = capture_file.open(current_file);
if (open_error) {
file_error(current_file,
"Cannot open file.\n"
"Initial file check failed.\n"
"Path: " +
cvsfiles_dir.string() +
"\n"
"Error: " +
std::to_string(static_cast<uint32_t>(open_error)));
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();
transmitter_model.disable();
ready_signal = false;
baseband::set_sample_rate(metadata->sample_rate, get_oversample_rate(metadata->sample_rate));
auto reader = std::make_unique<FileConvertReader>();
if (auto error = reader->open(current_file)) {
file_error(current_file,
"Cannot read C16 data.\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)));
return;
}
progressbar.set_value(0);
transmitter_model.set_sampling_rate(get_actual_sample_rate(metadata->sample_rate));
transmitter_model.set_baseband_bandwidth(metadata->sample_rate <= 500000 ? 1750000 : 2500000);
transmitter_model.set_target_frequency(metadata->center_frequency);
transmitter_model.enable();
chThdSleepMilliseconds(100);
replay_thread = std::make_unique<ReplayThread>(
std::move(reader),
read_size,
buffer_count,
&ready_signal,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});
}
void CVSSpamView::start_chaos_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)));
return;
}
auto metadata_path = get_metadata_path(chaos_file_path);
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();
transmitter_model.disable();
ready_signal = false;
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)));
return;
}
progressbar.set_value(0);
transmitter_model.set_sampling_rate(get_actual_sample_rate(metadata->sample_rate));
transmitter_model.set_baseband_bandwidth(metadata->sample_rate <= 500000 ? 1750000 : 2500000);
transmitter_model.set_target_frequency(metadata->center_frequency);
transmitter_model.enable();
chThdSleepMilliseconds(100);
replay_thread = std::make_unique<ReplayThread>(
std::move(reader),
read_size,
buffer_count,
&ready_signal,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});
}
void CVSSpamView::on_tx_progress(const uint32_t progress) {
if (is_active()) {
progressbar.set_value(progress % 100);
progressbar.set_style(Theme::getInstance()->fg_red);
}
}
void CVSSpamView::focus() {
menu_view.focus();
}
bool CVSSpamView::is_active() const {
return (bool)replay_thread;
}
void CVSSpamView::stop_tx() {
replay_thread.reset();
transmitter_model.disable();
audio::output::stop();
ready_signal = false;
thread_sync_complete = false;
progressbar.set_value(0);
chThdSleepMilliseconds(50);
}
CVSSpamView::CVSSpamView(NavigationView& nav)
: nav_{nav},
current_file{} {
baseband::run_image(portapack::spi_flash::image_tag_replay);
add_children({&menu_view,
&text_empty,
&button_prev_page,
&button_next_page,
&page_info,
&button_send,
&button_chaos,
&button_stop,
&progressbar});
button_send.set_style(Theme::getInstance()->fg_magenta);
button_chaos.set_style(Theme::getInstance()->fg_yellow);
button_stop.set_style(Theme::getInstance()->fg_red);
transmitter_model.set_sampling_rate(1536000);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.set_rf_amp(true);
transmitter_model.set_tx_gain(47);
transmitter_model.set_channel_bandwidth(1750000);
refresh_list();
button_prev_page.on_select = [this](Button&) {
if (is_active()) return;
if (current_page == 1) return;
if (current_page == 2) page = 1;
page = current_page - 1;
refresh_list();
};
button_next_page.on_select = [this](Button&) {
if (is_active()) return;
refresh_list();
};
button_send.on_select = [this](Button&) {
if (!file_list.empty()) {
start_tx(menu_view.highlighted_index());
}
};
button_chaos.on_select = [this](Button&) {
start_chaos_tx();
};
button_stop.on_select = [this](Button&) {
if (is_active()) {
stop_tx();
}
};
}
CVSSpamView::~CVSSpamView() {
stop_tx();
baseband::shutdown();
}
} // namespace ui::external_app::cvs_spam

View File

@ -0,0 +1,115 @@
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
// If you can read this, you're a nerd. :P
// Come join us at https://discord.gg/thepiratesreborn
#pragma once
#include "ui_widget.hpp"
#include "ui_transmitter.hpp"
#include "replay_thread.hpp"
#include "baseband_api.hpp"
#include "ui_language.hpp"
#include "file_path.hpp"
#include "audio.hpp"
using namespace portapack;
namespace ui::external_app::cvs_spam {
class CVSSpamView : public View {
public:
explicit CVSSpamView(NavigationView& nav);
~CVSSpamView();
void focus() override;
std::string title() const override { return "CVS Spam"; };
private:
static constexpr size_t read_size = 0x4000;
static constexpr size_t buffer_count = 3;
NavigationView& nav_;
std::unique_ptr<ReplayThread> replay_thread{};
bool ready_signal{false};
bool thread_sync_complete{false};
std::filesystem::path current_file;
bool is_active() const;
void stop_tx();
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 on_tx_progress(const uint32_t progress);
uint32_t page = 1;
uint32_t current_page = 1;
std::vector<std::filesystem::path> file_list{};
MenuView menu_view{
{0, 0, 240, 180},
true};
Text text_empty{
{7 * 8, 12 * 8, 16 * 8, 16},
"Empty directory!"};
Button button_prev_page{
{0, 180, 50, 32},
"<"};
Button button_next_page{
{190, 180, 50, 32},
">"};
Text page_info{
{95, 188, 50, 16}};
Button button_send{
{0, 212, 75, 36},
"CALL"};
Button button_chaos{
{82, 212, 75, 36},
"CHAOS"};
Button button_stop{
{165, 212, 75, 36},
LanguageHelper::currentMessages[LANG_STOP]};
ProgressBar progressbar{
{0, 256, 240, 44}};
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) {
ready_signal = true;
}
}};
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.progress);
}};
MessageHandlerRegistration message_handler_replay_thread_done{
Message::ID::ReplayThreadDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
if (message.return_code == ReplayThread::END_OF_FILE) {
if (is_active()) {
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");
}
}};
};
} // namespace ui::external_app::cvs_spam

View File

@ -0,0 +1,33 @@
// CVS Spam app by RocketGod (@rocketgod-git) https://betaskynet.com
// Original .cu8 files by @jimilinuxguy https://github.com/jimilinuxguy/customer-assistance-buttons-sdr
// If you can read this, you're a nerd. :P
// Come join us at https://discord.gg/thepiratesreborn
#include "ui.hpp"
#include "cvs_spam.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::cvs_spam {
void initialize_app(NavigationView& nav) {
nav.push<CVSSpamView>();
}
} // namespace ui::external_app::cvs_spam
extern "C" {
__attribute__((section(".external_app.app_cvs_spam.application_information"), used)) application_information_t _application_information_cvs_spam = {
(uint8_t*)0x00000000,
ui::external_app::cvs_spam::initialize_app,
CURRENT_HEADER_VERSION,
VERSION_MD5,
"CVS Spam",
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81},
ui::Color::red().v,
app_location_t::TX,
{'P', 'R', 'E', 'P'},
0x00000000};
}

View File

@ -110,6 +110,10 @@ set(EXTCPPSRC
#shoppingcart_lock
external/shoppingcart_lock/main.cpp
external/shoppingcart_lock/shoppingcart_lock.cpp
#cvs_spam
external/cvs_spam/main.cpp
external/cvs_spam/cvs_spam.cpp
)
set(EXTAPPLIST
@ -131,6 +135,7 @@ set(EXTAPPLIST
foxhunt_rx
audio_test
wardrivemap
cvs_spam
tpmsrx
protoview
adsbtx

View File

@ -49,6 +49,7 @@ MEMORY
ram_external_app_random_password(rwx) : org = 0xADC80000, len = 32k
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
}
SECTIONS
@ -210,6 +211,10 @@ SECTIONS
*(*ui*external_app*shoppingcart_lock*);
} > ram_external_app_shoppingcart_lock
.external_app_cvs_spam : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_cvs_spam.application_information));
*(*ui*external_app*cvs_spam*);
} > ram_external_app_cvs_spam
}

View File

@ -30,6 +30,7 @@ const std::filesystem::path audio_dir = u"AUDIO";
const std::filesystem::path blerx_dir = u"BLERX";
const std::filesystem::path bletx_dir = u"BLETX";
const std::filesystem::path captures_dir = u"CAPTURES";
const std::filesystem::path cvsfiles_dir = u"CVSFILES";
const std::filesystem::path debug_dir = u"DEBUG";
const std::filesystem::path firmware_dir = u"FIRMWARE";
const std::filesystem::path freqman_dir = u"FREQMAN";

View File

@ -32,6 +32,7 @@ extern const std::filesystem::path audio_dir;
extern const std::filesystem::path blerx_dir;
extern const std::filesystem::path bletx_dir;
extern const std::filesystem::path captures_dir;
extern const std::filesystem::path cvsfiles_dir;
extern const std::filesystem::path debug_dir;
extern const std::filesystem::path firmware_dir;
extern const std::filesystem::path freqman_dir;