mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2025-02-22 12:28:29 +00:00
Add new app "hopper" app. (#2482)
* make both jammer and hopper exist * add example hopper payload * example files * swap scanner and recon app location
This commit is contained in:
parent
9b352f45a2
commit
200f10397b
31
firmware/application/external/external.cmake
vendored
31
firmware/application/external/external.cmake
vendored
@ -151,17 +151,21 @@ set(EXTCPPSRC
|
|||||||
external/app_manager/main.cpp
|
external/app_manager/main.cpp
|
||||||
external/app_manager/ui_app_manager.cpp
|
external/app_manager/ui_app_manager.cpp
|
||||||
|
|
||||||
# whip calculator
|
#hopper
|
||||||
external/antenna_length/main.cpp
|
external/hopper/main.cpp
|
||||||
external/antenna_length/ui_whipcalc.cpp
|
external/hopper/ui_hopper.cpp
|
||||||
|
|
||||||
|
# whip calculator
|
||||||
|
external/antenna_length/main.cpp
|
||||||
|
external/antenna_length/ui_whipcalc.cpp
|
||||||
|
|
||||||
# wav viewer
|
# wav viewer
|
||||||
external/wav_view/main.cpp
|
external/wav_view/main.cpp
|
||||||
external/wav_view/ui_view_wav.cpp
|
external/wav_view/ui_view_wav.cpp
|
||||||
|
|
||||||
# wipe sdcard
|
# wipe sdcard
|
||||||
external/sd_wipe/main.cpp
|
external/sd_wipe/main.cpp
|
||||||
external/sd_wipe/ui_sd_wipe.cpp
|
external/sd_wipe/ui_sd_wipe.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(EXTAPPLIST
|
set(EXTAPPLIST
|
||||||
@ -201,7 +205,8 @@ set(EXTAPPLIST
|
|||||||
tuner
|
tuner
|
||||||
metronome
|
metronome
|
||||||
app_manager
|
app_manager
|
||||||
antenna_length
|
hopper
|
||||||
view_wav
|
antenna_length
|
||||||
sd_wipe
|
view_wav
|
||||||
)
|
sd_wipe
|
||||||
|
)
|
13
firmware/application/external/external.ld
vendored
13
firmware/application/external/external.ld
vendored
@ -59,9 +59,10 @@ MEMORY
|
|||||||
ram_external_app_tuner(rwx) : org = 0xADD20000, len = 32k
|
ram_external_app_tuner(rwx) : org = 0xADD20000, len = 32k
|
||||||
ram_external_app_metronome(rwx) : org = 0xADD30000, len = 32k
|
ram_external_app_metronome(rwx) : org = 0xADD30000, len = 32k
|
||||||
ram_external_app_app_manager(rwx) : org = 0xADD40000, len = 32k
|
ram_external_app_app_manager(rwx) : org = 0xADD40000, len = 32k
|
||||||
ram_external_app_antenna_length(rwx) : org = 0xADD50000, len = 32k
|
ram_external_app_hopper(rwx) : org = 0xADD50000, len = 32k
|
||||||
ram_external_app_view_wav(rwx) : org = 0xADD60000, len = 32k
|
ram_external_app_antenna_length(rwx) : org = 0xADD60000, len = 32k
|
||||||
ram_external_app_sd_wipe(rwx) : org = 0xADD70000, len = 32k
|
ram_external_app_view_wav(rwx) : org = 0xADD70000, len = 32k
|
||||||
|
ram_external_app_sd_wipe(rwx) : org = 0xADD80000, len = 32k
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@ -283,6 +284,12 @@ SECTIONS
|
|||||||
*(*ui*external_app*app_manager*);
|
*(*ui*external_app*app_manager*);
|
||||||
} > ram_external_app_app_manager
|
} > ram_external_app_app_manager
|
||||||
|
|
||||||
|
.external_app_hopper : ALIGN(4) SUBALIGN(4)
|
||||||
|
{
|
||||||
|
KEEP(*(.external_app.app_hopper.application_information));
|
||||||
|
*(*ui*external_app*hopper*);
|
||||||
|
} > ram_external_app_hopper
|
||||||
|
|
||||||
.external_app_antenna_length : ALIGN(4) SUBALIGN(4)
|
.external_app_antenna_length : ALIGN(4) SUBALIGN(4)
|
||||||
{
|
{
|
||||||
KEEP(*(.external_app.app_antenna_length.application_information));
|
KEEP(*(.external_app.app_antenna_length.application_information));
|
||||||
|
83
firmware/application/external/hopper/main.cpp
vendored
Normal file
83
firmware/application/external/hopper/main.cpp
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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_hopper.hpp"
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "external_app.hpp"
|
||||||
|
|
||||||
|
namespace ui::external_app::hopper {
|
||||||
|
void initialize_app(ui::NavigationView& nav) {
|
||||||
|
nav.push<HopperView>();
|
||||||
|
}
|
||||||
|
} // namespace ui::external_app::hopper
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
__attribute__((section(".external_app.app_hopper.application_information"), used)) application_information_t _application_information_hopper = {
|
||||||
|
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||||
|
/*.externalAppEntry = */ ui::external_app::hopper::initialize_app,
|
||||||
|
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||||
|
/*.app_version = */ VERSION_MD5,
|
||||||
|
|
||||||
|
/*.app_name = */ "Hopper",
|
||||||
|
/*.bitmap_data = */ {
|
||||||
|
0xE0,
|
||||||
|
0x07,
|
||||||
|
0xF8,
|
||||||
|
0x1F,
|
||||||
|
0x1C,
|
||||||
|
0x38,
|
||||||
|
0x0E,
|
||||||
|
0x78,
|
||||||
|
0x06,
|
||||||
|
0x7C,
|
||||||
|
0x03,
|
||||||
|
0xCE,
|
||||||
|
0x03,
|
||||||
|
0xC7,
|
||||||
|
0x83,
|
||||||
|
0xC3,
|
||||||
|
0xC3,
|
||||||
|
0xC1,
|
||||||
|
0xE3,
|
||||||
|
0xC0,
|
||||||
|
0x73,
|
||||||
|
0xC0,
|
||||||
|
0x3E,
|
||||||
|
0x60,
|
||||||
|
0x1E,
|
||||||
|
0x70,
|
||||||
|
0x1C,
|
||||||
|
0x38,
|
||||||
|
0xF8,
|
||||||
|
0x1F,
|
||||||
|
0xE0,
|
||||||
|
0x07,
|
||||||
|
},
|
||||||
|
/*.icon_color = */ ui::Color::green().v,
|
||||||
|
/*.menu_location = */ app_location_t::TX,
|
||||||
|
/*.desired_menu_position = */ -1,
|
||||||
|
|
||||||
|
/*.m4_app_tag = portapack::spi_flash::image_tag_jammer */ {'P', 'J', 'A', 'M'},
|
||||||
|
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||||
|
};
|
||||||
|
}
|
346
firmware/application/external/hopper/ui_hopper.cpp
vendored
Normal file
346
firmware/application/external/hopper/ui_hopper.cpp
vendored
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||||
|
* Copyright (C) 2016 Furrtek
|
||||||
|
* Copyright (C) 2020 euquiq
|
||||||
|
* copyleft mr.r0b0t from the F society
|
||||||
|
*
|
||||||
|
* 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_hopper.hpp"
|
||||||
|
#include "ui_receiver.hpp"
|
||||||
|
#include "ui_freqman.hpp"
|
||||||
|
#include "ui_fileman.hpp"
|
||||||
|
#include "file_path.hpp"
|
||||||
|
#include "file_reader.hpp"
|
||||||
|
#include "convert.hpp"
|
||||||
|
|
||||||
|
#include "baseband_api.hpp"
|
||||||
|
#include "string_format.hpp"
|
||||||
|
|
||||||
|
using namespace portapack;
|
||||||
|
|
||||||
|
namespace ui::external_app::hopper {
|
||||||
|
|
||||||
|
void HopperView::focus() {
|
||||||
|
button_load_list.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
HopperView::~HopperView() {
|
||||||
|
transmitter_model.disable();
|
||||||
|
baseband::shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HopperView::update_freq_list_menu_view() {
|
||||||
|
menu_freq_list.clear();
|
||||||
|
uint8_t list_count = freq_list.size();
|
||||||
|
|
||||||
|
if (list_count == 0) {
|
||||||
|
menu_freq_list.add_item({"Add freq or load list",
|
||||||
|
Theme::getInstance()->bg_darkest->foreground,
|
||||||
|
nullptr,
|
||||||
|
nullptr});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < list_count; i++) {
|
||||||
|
menu_freq_list.add_item({to_string_rounded_freq(freq_list[i], INT8_MAX),
|
||||||
|
Theme::getInstance()->bg_darkest->foreground,
|
||||||
|
nullptr,
|
||||||
|
nullptr});
|
||||||
|
}
|
||||||
|
|
||||||
|
set_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HopperView::on_retune(const rf::Frequency freq, const uint32_t range) {
|
||||||
|
if (freq) {
|
||||||
|
transmitter_model.set_target_frequency(freq);
|
||||||
|
text_range_number.set(to_string_dec_uint(range, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HopperView::set_hopper_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration) {
|
||||||
|
hopper_channels[i].enabled = true;
|
||||||
|
hopper_channels[i].width = (width * 0xFFFFFFULL) / 1536000;
|
||||||
|
hopper_channels[i].center = center;
|
||||||
|
hopper_channels[i].duration = 30720 * duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HopperView::start_tx() {
|
||||||
|
uint32_t c, i = 0;
|
||||||
|
bool out_of_ranges = false;
|
||||||
|
|
||||||
|
size_t hop_value = options_hop.selected_index_value();
|
||||||
|
|
||||||
|
// Disable all channels by default
|
||||||
|
for (c = 0; c < JAMMER_MAX_CH; c++)
|
||||||
|
hopper_channels[c].enabled = false;
|
||||||
|
|
||||||
|
uint8_t channel_count = freq_list.size();
|
||||||
|
|
||||||
|
if (channel_count <= JAMMER_MAX_CH) {
|
||||||
|
for (c = 0; c < channel_count; c++) {
|
||||||
|
if (i >= JAMMER_MAX_CH) {
|
||||||
|
out_of_ranges = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
set_hopper_channel(i, JAMMER_CH_WIDTH, freq_list[c], hop_value);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out_of_ranges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!out_of_ranges && i) {
|
||||||
|
text_range_total.set("/" + to_string_dec_uint(i, 2));
|
||||||
|
|
||||||
|
jamming = true;
|
||||||
|
button_transmit.set_style(&style_cancel);
|
||||||
|
button_transmit.set_text("STOP");
|
||||||
|
|
||||||
|
transmitter_model.set_rf_amp(field_amp.value());
|
||||||
|
transmitter_model.set_tx_gain(field_gain.value());
|
||||||
|
transmitter_model.set_baseband_bandwidth(28'000'000); // Although tx is narrowband , let's use Max TX LPF .
|
||||||
|
transmitter_model.enable();
|
||||||
|
|
||||||
|
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
|
||||||
|
mscounter = 0; // euquiq: Reset internal ms counter for do_timer()
|
||||||
|
} else {
|
||||||
|
if (out_of_ranges)
|
||||||
|
nav_.display_modal("Error", "Jam freq too much.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HopperView::stop_tx() {
|
||||||
|
button_transmit.set_style(&style_val);
|
||||||
|
button_transmit.set_text("START");
|
||||||
|
transmitter_model.disable();
|
||||||
|
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
|
||||||
|
jamming = false;
|
||||||
|
cooling = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// called each 1/60th of second
|
||||||
|
void HopperView::on_timer() {
|
||||||
|
if (++mscounter == 60) {
|
||||||
|
mscounter = 0;
|
||||||
|
if (jamming) {
|
||||||
|
if (cooling) {
|
||||||
|
if (++seconds >= field_timepause.value()) { // Re-start TX
|
||||||
|
transmitter_model.set_baseband_bandwidth(28'000'000); // Although tx is narrowband , let's use Max TX LPF .
|
||||||
|
transmitter_model.enable();
|
||||||
|
button_transmit.set_text("STOP");
|
||||||
|
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
|
||||||
|
|
||||||
|
int32_t jitter_amount = field_jitter.value();
|
||||||
|
if (jitter_amount) {
|
||||||
|
lfsr_v = lfsr_iterate(lfsr_v);
|
||||||
|
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
|
||||||
|
mscounter += jitter_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
cooling = false;
|
||||||
|
seconds = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (++seconds >= field_timetx.value()) // Start cooling period:
|
||||||
|
{
|
||||||
|
transmitter_model.disable();
|
||||||
|
button_transmit.set_text("PAUSED");
|
||||||
|
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
|
||||||
|
|
||||||
|
int32_t jitter_amount = field_jitter.value();
|
||||||
|
if (jitter_amount) {
|
||||||
|
lfsr_v = lfsr_iterate(lfsr_v);
|
||||||
|
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
|
||||||
|
mscounter += jitter_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
cooling = true;
|
||||||
|
seconds = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HopperView::load_list() {
|
||||||
|
freq_list.clear();
|
||||||
|
|
||||||
|
auto open_view = nav_.push<FileLoadView>(".PHOP");
|
||||||
|
open_view->push_dir(hopper_dir);
|
||||||
|
open_view->on_changed = [this](std::filesystem::path path) {
|
||||||
|
File f;
|
||||||
|
auto error = f.open(path);
|
||||||
|
if (error) {
|
||||||
|
nav_.display_modal("Err", "Can't open.");
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
freq_list.clear();
|
||||||
|
|
||||||
|
auto reader = FileLineReader(f);
|
||||||
|
for (const auto& line : reader) {
|
||||||
|
if (line.length() == 0 || line[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rf::Frequency freq;
|
||||||
|
if (parse_int(line, freq)) {
|
||||||
|
if (freq_list.size() >= JAMMER_MAX_CH) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
freq_list.push_back(freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
};
|
||||||
|
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HopperView::save_list() {
|
||||||
|
if (freq_list.empty()) {
|
||||||
|
nav_.display_modal("Err", "Nothing to save");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_directory(hopper_dir);
|
||||||
|
|
||||||
|
filename_buffer = "";
|
||||||
|
text_prompt(
|
||||||
|
nav_,
|
||||||
|
filename_buffer,
|
||||||
|
64,
|
||||||
|
[this](std::string& value) {
|
||||||
|
auto path = hopper_dir / (value + ".PHOP");
|
||||||
|
|
||||||
|
File f;
|
||||||
|
auto error = f.create(path);
|
||||||
|
if (error) {
|
||||||
|
nav_.display_modal("Err", "Create fail.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& freq : freq_list) {
|
||||||
|
auto freq_str = to_string_dec_uint(freq) + "\n";
|
||||||
|
f.write(freq_str.c_str(), freq_str.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
text_range_number.set("Saved: " + path.filename().string());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
HopperView::HopperView(
|
||||||
|
NavigationView& nav)
|
||||||
|
: nav_{nav} {
|
||||||
|
// baseband::run_image(portapack::spi_flash::image_tag_jammer);
|
||||||
|
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||||
|
|
||||||
|
add_children({&menu_freq_list,
|
||||||
|
&button_load_list,
|
||||||
|
&button_add_freq,
|
||||||
|
&button_delete_freq,
|
||||||
|
&button_save_list,
|
||||||
|
&button_clear,
|
||||||
|
&labels,
|
||||||
|
&options_type,
|
||||||
|
&text_range_number,
|
||||||
|
&text_range_total,
|
||||||
|
&options_speed,
|
||||||
|
&options_hop,
|
||||||
|
&field_timetx,
|
||||||
|
&field_timepause,
|
||||||
|
&field_jitter,
|
||||||
|
&field_gain,
|
||||||
|
&field_amp,
|
||||||
|
&button_transmit});
|
||||||
|
|
||||||
|
options_type.set_selected_index(3); // Rand CW
|
||||||
|
options_speed.set_selected_index(3); // 10kHz
|
||||||
|
options_hop.set_selected_index(1); // 50ms
|
||||||
|
button_transmit.set_style(&style_val);
|
||||||
|
|
||||||
|
field_timetx.set_value(30);
|
||||||
|
field_timepause.set_value(1);
|
||||||
|
field_gain.set_value(transmitter_model.tx_gain());
|
||||||
|
field_amp.set_value(transmitter_model.rf_amp());
|
||||||
|
|
||||||
|
button_load_list.on_select = [this]() {
|
||||||
|
load_list();
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
};
|
||||||
|
|
||||||
|
button_add_freq.on_select = [this]() {
|
||||||
|
if (freq_list.size() < JAMMER_MAX_CH) {
|
||||||
|
auto kb_view = nav_.push<FrequencyKeypadView>(0);
|
||||||
|
kb_view->on_changed = [this](rf::Frequency freq) {
|
||||||
|
freq_list.push_back(freq);
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
};
|
||||||
|
|
||||||
|
} else {
|
||||||
|
nav_.display_modal("Err", "No more.");
|
||||||
|
}
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
};
|
||||||
|
|
||||||
|
button_delete_freq.on_select = [this]() {
|
||||||
|
auto i = menu_freq_list.highlighted_index();
|
||||||
|
if (i < freq_list.size()) {
|
||||||
|
freq_list.erase(freq_list.begin() + i);
|
||||||
|
}
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
};
|
||||||
|
|
||||||
|
button_save_list.on_select = [this]() {
|
||||||
|
save_list();
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
};
|
||||||
|
|
||||||
|
button_clear.on_select = [this]() {
|
||||||
|
// clang-format off
|
||||||
|
nav_.display_modal("Del:", "Clean all?\n", YESNO, [this](bool choice) {
|
||||||
|
if (choice){
|
||||||
|
freq_list.clear();
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
} }, TRUE);
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
};
|
||||||
|
|
||||||
|
button_transmit.on_select = [this](Button&) {
|
||||||
|
if (jamming || cooling)
|
||||||
|
stop_tx();
|
||||||
|
else
|
||||||
|
start_tx();
|
||||||
|
};
|
||||||
|
|
||||||
|
menu_freq_list.on_left = [this]() {
|
||||||
|
button_load_list.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
menu_freq_list.on_right = [this]() {
|
||||||
|
button_load_list.focus();
|
||||||
|
};
|
||||||
|
|
||||||
|
update_freq_list_menu_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ui::external_app::hopper
|
233
firmware/application/external/hopper/ui_hopper.hpp
vendored
Normal file
233
firmware/application/external/hopper/ui_hopper.hpp
vendored
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||||
|
* Copyright (C) 2016 Furrtek
|
||||||
|
* Copyright (C) 2020 euquiq
|
||||||
|
* copyleft mr.r0b0t from the F society
|
||||||
|
*
|
||||||
|
* 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_language.hpp"
|
||||||
|
#include "ui_widget.hpp"
|
||||||
|
#include "ui_navigation.hpp"
|
||||||
|
#include "ui_tabview.hpp"
|
||||||
|
#include "transmitter_model.hpp"
|
||||||
|
#include "message.hpp"
|
||||||
|
#include "jammer.hpp"
|
||||||
|
#include "lfsr_random.hpp"
|
||||||
|
#include "radio_state.hpp"
|
||||||
|
|
||||||
|
using namespace jammer;
|
||||||
|
|
||||||
|
namespace ui::external_app::hopper {
|
||||||
|
|
||||||
|
class HopperView : public View {
|
||||||
|
public:
|
||||||
|
HopperView(NavigationView& nav);
|
||||||
|
~HopperView();
|
||||||
|
|
||||||
|
HopperView(const HopperView&) = delete;
|
||||||
|
HopperView(HopperView&&) = delete;
|
||||||
|
HopperView& operator=(const HopperView&) = delete;
|
||||||
|
HopperView& operator=(HopperView&&) = delete;
|
||||||
|
|
||||||
|
void focus() override;
|
||||||
|
void update_freq_list_menu_view();
|
||||||
|
|
||||||
|
std::string title() const override { return "Hopper"; };
|
||||||
|
|
||||||
|
std::vector<rf::Frequency> freq_list{315000000, 433920000};
|
||||||
|
|
||||||
|
private:
|
||||||
|
NavigationView& nav_;
|
||||||
|
TxRadioState radio_state_{
|
||||||
|
0 /* frequency */,
|
||||||
|
3500000 /* bandwidth */,
|
||||||
|
3072000 /* sampling rate */
|
||||||
|
};
|
||||||
|
|
||||||
|
void start_tx();
|
||||||
|
void on_timer();
|
||||||
|
void stop_tx();
|
||||||
|
void set_hopper_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration);
|
||||||
|
void on_retune(const rf::Frequency freq, const uint32_t range);
|
||||||
|
void load_list();
|
||||||
|
void save_list();
|
||||||
|
void on_file_save_write(const std::string& value);
|
||||||
|
|
||||||
|
HopperChannel* hopper_channels = (HopperChannel*)shared_memory.bb_data.data;
|
||||||
|
bool jamming{false};
|
||||||
|
bool cooling{false}; // euquiq: Indicates jammer in cooldown
|
||||||
|
uint16_t seconds = 0; // euquiq: seconds counter for toggling tx / cooldown
|
||||||
|
int16_t mscounter = 0; // euquiq: Internal ms counter for do_timer()
|
||||||
|
lfsr_word_t lfsr_v = 1; // euquiq: Used to generate "random" Jitter
|
||||||
|
|
||||||
|
std::string filename_buffer = "";
|
||||||
|
|
||||||
|
const Style& style_val = *Theme::getInstance()->fg_green;
|
||||||
|
const Style& style_cancel = *Theme::getInstance()->fg_red;
|
||||||
|
|
||||||
|
MenuView menu_freq_list{
|
||||||
|
{0, 0, screen_width, 8 * 16},
|
||||||
|
false};
|
||||||
|
|
||||||
|
NewButton button_load_list{
|
||||||
|
{0 * 8, 9 * 16 + 4, 4 * 8, 32},
|
||||||
|
{},
|
||||||
|
&bitmap_icon_load,
|
||||||
|
Color::dark_blue(),
|
||||||
|
/*vcenter*/ true};
|
||||||
|
|
||||||
|
NewButton button_save_list{
|
||||||
|
{4 * 8, 9 * 16 + 4, 4 * 8, 32},
|
||||||
|
{},
|
||||||
|
&bitmap_icon_save,
|
||||||
|
Color::dark_blue(),
|
||||||
|
/*vcenter*/ true};
|
||||||
|
|
||||||
|
NewButton button_add_freq{
|
||||||
|
{8 * 8 + 4, 9 * 16 + 4, 4 * 8, 32},
|
||||||
|
{},
|
||||||
|
&bitmap_icon_add,
|
||||||
|
Color::dark_green(),
|
||||||
|
/*vcenter*/ true};
|
||||||
|
|
||||||
|
NewButton button_delete_freq{
|
||||||
|
{12 * 8 + 4, 9 * 16 + 4, 4 * 8, 32},
|
||||||
|
{},
|
||||||
|
&bitmap_icon_trash,
|
||||||
|
Color::dark_red(),
|
||||||
|
/*vcenter*/ true};
|
||||||
|
|
||||||
|
NewButton button_clear{
|
||||||
|
{screen_width - 4 * 8, 9 * 16 + 4, 4 * 8, 32},
|
||||||
|
{},
|
||||||
|
&bitmap_icon_tools_wipesd,
|
||||||
|
Color::red(),
|
||||||
|
/*vcenter*/ true};
|
||||||
|
|
||||||
|
Labels labels{
|
||||||
|
{{2 * 8, 23 * 8}, "Type:", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{1 * 8, 25 * 8}, "Speed:", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{3 * 8, 27 * 8}, "Hop:", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{4 * 8, 29 * 8}, "TX:", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{1 * 8, 31 * 8}, "Sle3p:", Theme::getInstance()->fg_light->foreground}, // euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty.
|
||||||
|
{{0 * 8, 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground}, // Maybe the repository curator can keep the "mystype" for some versions.
|
||||||
|
{{11 * 8, 29 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{11 * 8, 31 * 8}, "Secs.", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{11 * 8, 33 * 8}, "/60", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{2 * 8, 35 * 8}, "Gain:", Theme::getInstance()->fg_light->foreground},
|
||||||
|
{{11 * 8, 35 * 8}, "A:", Theme::getInstance()->fg_light->foreground}};
|
||||||
|
|
||||||
|
OptionsField options_type{
|
||||||
|
{7 * 8, 23 * 8},
|
||||||
|
8,
|
||||||
|
{
|
||||||
|
{"Rand FSK", 0},
|
||||||
|
{"FM tone", 1},
|
||||||
|
{"CW sweep", 2},
|
||||||
|
{"Rand CW", 3},
|
||||||
|
}};
|
||||||
|
|
||||||
|
Text text_range_number{
|
||||||
|
{16 * 8, 23 * 8, 2 * 8, 16},
|
||||||
|
"--"};
|
||||||
|
Text text_range_total{
|
||||||
|
{18 * 8, 23 * 8, 3 * 8, 16},
|
||||||
|
"/--"};
|
||||||
|
|
||||||
|
OptionsField options_speed{
|
||||||
|
{7 * 8, 25 * 8},
|
||||||
|
6,
|
||||||
|
{{"10Hz ", 10},
|
||||||
|
{"100Hz ", 100},
|
||||||
|
{"1kHz ", 1000},
|
||||||
|
{"10kHz ", 10000},
|
||||||
|
{"100kHz", 100000}}};
|
||||||
|
|
||||||
|
OptionsField options_hop{
|
||||||
|
{7 * 8, 27 * 8},
|
||||||
|
5,
|
||||||
|
{{"10ms ", 1},
|
||||||
|
{"50ms ", 5},
|
||||||
|
{"100ms", 10},
|
||||||
|
{"1s ", 100},
|
||||||
|
{"2s ", 200},
|
||||||
|
{"5s ", 500},
|
||||||
|
{"10s ", 1000}}};
|
||||||
|
|
||||||
|
NumberField field_timetx{
|
||||||
|
{7 * 8, 29 * 8},
|
||||||
|
3,
|
||||||
|
{1, 180},
|
||||||
|
1,
|
||||||
|
' ',
|
||||||
|
};
|
||||||
|
|
||||||
|
NumberField field_timepause{
|
||||||
|
{8 * 8, 31 * 8},
|
||||||
|
2,
|
||||||
|
{1, 60},
|
||||||
|
1,
|
||||||
|
' ',
|
||||||
|
};
|
||||||
|
|
||||||
|
NumberField field_jitter{
|
||||||
|
{8 * 8, 33 * 8},
|
||||||
|
2,
|
||||||
|
{1, 60},
|
||||||
|
1,
|
||||||
|
' ',
|
||||||
|
};
|
||||||
|
|
||||||
|
NumberField field_gain{
|
||||||
|
{8 * 8, 35 * 8},
|
||||||
|
2,
|
||||||
|
{0, 47},
|
||||||
|
1,
|
||||||
|
' ',
|
||||||
|
};
|
||||||
|
|
||||||
|
NumberField field_amp{
|
||||||
|
{13 * 8, 35 * 8},
|
||||||
|
1,
|
||||||
|
{0, 1},
|
||||||
|
1,
|
||||||
|
' ',
|
||||||
|
};
|
||||||
|
|
||||||
|
Button button_transmit{
|
||||||
|
{148, 216, 80, 80},
|
||||||
|
LanguageHelper::currentMessages[LANG_START]};
|
||||||
|
|
||||||
|
MessageHandlerRegistration message_handler_retune{
|
||||||
|
Message::ID::Retune,
|
||||||
|
[this](Message* const p) {
|
||||||
|
const auto message = static_cast<const RetuneMessage*>(p);
|
||||||
|
this->on_retune(message->freq, message->range);
|
||||||
|
}};
|
||||||
|
|
||||||
|
MessageHandlerRegistration message_handler_frame_sync{
|
||||||
|
Message::ID::DisplayFrameSync,
|
||||||
|
[this](const Message* const) {
|
||||||
|
this->on_timer();
|
||||||
|
}};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui::external_app::hopper
|
@ -49,4 +49,5 @@ const std::filesystem::path sstv_dir = u"SSTV";
|
|||||||
const std::filesystem::path wav_dir = u"WAV";
|
const std::filesystem::path wav_dir = u"WAV";
|
||||||
const std::filesystem::path whipcalc_dir = u"WHIPCALC";
|
const std::filesystem::path whipcalc_dir = u"WHIPCALC";
|
||||||
const std::filesystem::path ook_editor_dir = u"OOKFILES";
|
const std::filesystem::path ook_editor_dir = u"OOKFILES";
|
||||||
|
const std::filesystem::path hopper_dir = u"HOPPER";
|
||||||
const std::filesystem::path subghz_dir = u"SUBGHZ";
|
const std::filesystem::path subghz_dir = u"SUBGHZ";
|
||||||
|
@ -51,6 +51,7 @@ extern const std::filesystem::path sstv_dir;
|
|||||||
extern const std::filesystem::path wav_dir;
|
extern const std::filesystem::path wav_dir;
|
||||||
extern const std::filesystem::path whipcalc_dir;
|
extern const std::filesystem::path whipcalc_dir;
|
||||||
extern const std::filesystem::path ook_editor_dir;
|
extern const std::filesystem::path ook_editor_dir;
|
||||||
|
extern const std::filesystem::path hopper_dir;
|
||||||
extern const std::filesystem::path subghz_dir;
|
extern const std::filesystem::path subghz_dir;
|
||||||
|
|
||||||
#endif /* __FILE_PATH_H__ */
|
#endif /* __FILE_PATH_H__ */
|
||||||
|
@ -122,7 +122,7 @@ const NavigationView::AppList NavigationView::appList = {
|
|||||||
{nullptr, "Transmit", HOME, Color::cyan(), &bitmap_icon_transmit, new ViewFactory<TransmittersMenuView>()},
|
{nullptr, "Transmit", HOME, Color::cyan(), &bitmap_icon_transmit, new ViewFactory<TransmittersMenuView>()},
|
||||||
{"capture", "Capture", HOME, Color::red(), &bitmap_icon_capture, new ViewFactory<CaptureAppView>()},
|
{"capture", "Capture", HOME, Color::red(), &bitmap_icon_capture, new ViewFactory<CaptureAppView>()},
|
||||||
{"replay", "Replay", HOME, Color::green(), &bitmap_icon_replay, new ViewFactory<PlaylistView>()},
|
{"replay", "Replay", HOME, Color::green(), &bitmap_icon_replay, new ViewFactory<PlaylistView>()},
|
||||||
{"scanner", "Scanner", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory<ScannerView>()},
|
{"recon", "Recon", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory<ReconView>()},
|
||||||
{"microphone", "Microphone", HOME, Color::green(), &bitmap_icon_microphone, new ViewFactory<MicTXView>()},
|
{"microphone", "Microphone", HOME, Color::green(), &bitmap_icon_microphone, new ViewFactory<MicTXView>()},
|
||||||
{"lookingglass", "Looking Glass", HOME, Color::green(), &bitmap_icon_looking, new ViewFactory<GlassView>()},
|
{"lookingglass", "Looking Glass", HOME, Color::green(), &bitmap_icon_looking, new ViewFactory<GlassView>()},
|
||||||
{nullptr, "Utilities", HOME, Color::cyan(), &bitmap_icon_utilities, new ViewFactory<UtilitiesMenuView>()},
|
{nullptr, "Utilities", HOME, Color::cyan(), &bitmap_icon_utilities, new ViewFactory<UtilitiesMenuView>()},
|
||||||
@ -139,7 +139,7 @@ const NavigationView::AppList NavigationView::appList = {
|
|||||||
{"level", "Level", RX, Color::green(), &bitmap_icon_options_radio, new ViewFactory<LevelView>()},
|
{"level", "Level", RX, Color::green(), &bitmap_icon_options_radio, new ViewFactory<LevelView>()},
|
||||||
{"pocsag", "POCSAG", RX, Color::green(), &bitmap_icon_pocsag, new ViewFactory<POCSAGAppView>()},
|
{"pocsag", "POCSAG", RX, Color::green(), &bitmap_icon_pocsag, new ViewFactory<POCSAGAppView>()},
|
||||||
{"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory<SondeView>()},
|
{"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory<SondeView>()},
|
||||||
{"recon", "Recon", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory<ReconView>()},
|
{"scanner", "Scanner", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory<ScannerView>()},
|
||||||
{"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory<SearchView>()},
|
{"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory<SearchView>()},
|
||||||
{"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<SubGhzDView>()},
|
{"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<SubGhzDView>()},
|
||||||
{"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory<WeatherView>()},
|
{"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory<WeatherView>()},
|
||||||
|
@ -35,6 +35,13 @@ struct JammerChannel {
|
|||||||
uint32_t duration;
|
uint32_t duration;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct HopperChannel {
|
||||||
|
bool enabled;
|
||||||
|
uint64_t center;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t duration;
|
||||||
|
};
|
||||||
|
|
||||||
struct ToneDef {
|
struct ToneDef {
|
||||||
uint32_t delta;
|
uint32_t delta;
|
||||||
uint32_t duration;
|
uint32_t duration;
|
||||||
@ -61,7 +68,10 @@ struct SharedMemory {
|
|||||||
|
|
||||||
union {
|
union {
|
||||||
ToneData tones_data;
|
ToneData tones_data;
|
||||||
JammerChannel jammer_channels[24];
|
struct {
|
||||||
|
JammerChannel jammer_channels[24];
|
||||||
|
HopperChannel hopper_channels[24];
|
||||||
|
} dummy_seperate;
|
||||||
uint8_t data[512];
|
uint8_t data[512];
|
||||||
} bb_data{{{{0, 0}}, 0, {0}}};
|
} bb_data{{{{0, 0}}, 0, {0}}};
|
||||||
|
|
||||||
|
3
sdcard/HOPPER/BLE_adv.PHOP
Normal file
3
sdcard/HOPPER/BLE_adv.PHOP
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
2402000000
|
||||||
|
2426000000
|
||||||
|
2480000000
|
10
sdcard/HOPPER/fobs.PHOP
Normal file
10
sdcard/HOPPER/fobs.PHOP
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
433920000
|
||||||
|
315000000
|
||||||
|
418000000
|
||||||
|
303000000
|
||||||
|
310000000
|
||||||
|
330000000
|
||||||
|
350000000
|
||||||
|
370000000
|
||||||
|
390000000
|
||||||
|
430500000
|
2
sdcard/HOPPER/fobs_common.PHOP
Normal file
2
sdcard/HOPPER/fobs_common.PHOP
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
315000000
|
||||||
|
433920000
|
Loading…
x
Reference in New Issue
Block a user