diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index 75ffd606d..83700b7fc 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -151,17 +151,21 @@ set(EXTCPPSRC external/app_manager/main.cpp external/app_manager/ui_app_manager.cpp - # whip calculator - external/antenna_length/main.cpp - external/antenna_length/ui_whipcalc.cpp + #hopper + external/hopper/main.cpp + external/hopper/ui_hopper.cpp + + # whip calculator + external/antenna_length/main.cpp + external/antenna_length/ui_whipcalc.cpp - # wav viewer - external/wav_view/main.cpp - external/wav_view/ui_view_wav.cpp + # wav viewer + external/wav_view/main.cpp + external/wav_view/ui_view_wav.cpp - # wipe sdcard - external/sd_wipe/main.cpp - external/sd_wipe/ui_sd_wipe.cpp + # wipe sdcard + external/sd_wipe/main.cpp + external/sd_wipe/ui_sd_wipe.cpp ) set(EXTAPPLIST @@ -201,7 +205,8 @@ set(EXTAPPLIST tuner metronome app_manager - antenna_length - view_wav - sd_wipe -) + hopper + antenna_length + view_wav + sd_wipe +) \ No newline at end of file diff --git a/firmware/application/external/external.ld b/firmware/application/external/external.ld index edb82367c..37f4162e7 100644 --- a/firmware/application/external/external.ld +++ b/firmware/application/external/external.ld @@ -59,9 +59,10 @@ MEMORY ram_external_app_tuner(rwx) : org = 0xADD20000, len = 32k ram_external_app_metronome(rwx) : org = 0xADD30000, 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_view_wav(rwx) : org = 0xADD60000, len = 32k - ram_external_app_sd_wipe(rwx) : org = 0xADD70000, len = 32k + ram_external_app_hopper(rwx) : org = 0xADD50000, len = 32k + ram_external_app_antenna_length(rwx) : org = 0xADD60000, len = 32k + ram_external_app_view_wav(rwx) : org = 0xADD70000, len = 32k + ram_external_app_sd_wipe(rwx) : org = 0xADD80000, len = 32k } SECTIONS @@ -283,6 +284,12 @@ SECTIONS *(*ui*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) { KEEP(*(.external_app.app_antenna_length.application_information)); diff --git a/firmware/application/external/hopper/main.cpp b/firmware/application/external/hopper/main.cpp new file mode 100644 index 000000000..16412fdd6 --- /dev/null +++ b/firmware/application/external/hopper/main.cpp @@ -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(); +} +} // 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 +}; +} diff --git a/firmware/application/external/hopper/ui_hopper.cpp b/firmware/application/external/hopper/ui_hopper.cpp new file mode 100644 index 000000000..c21272244 --- /dev/null +++ b/firmware/application/external/hopper/ui_hopper.cpp @@ -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(".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(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 diff --git a/firmware/application/external/hopper/ui_hopper.hpp b/firmware/application/external/hopper/ui_hopper.hpp new file mode 100644 index 000000000..88cea12fa --- /dev/null +++ b/firmware/application/external/hopper/ui_hopper.hpp @@ -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 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(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 diff --git a/firmware/application/file_path.cpp b/firmware/application/file_path.cpp index 54dd25bdc..cea226ec3 100644 --- a/firmware/application/file_path.cpp +++ b/firmware/application/file_path.cpp @@ -49,4 +49,5 @@ 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 ook_editor_dir = u"OOKFILES"; +const std::filesystem::path hopper_dir = u"HOPPER"; const std::filesystem::path subghz_dir = u"SUBGHZ"; diff --git a/firmware/application/file_path.hpp b/firmware/application/file_path.hpp index 8127765eb..d260268af 100644 --- a/firmware/application/file_path.hpp +++ b/firmware/application/file_path.hpp @@ -51,6 +51,7 @@ 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 ook_editor_dir; +extern const std::filesystem::path hopper_dir; extern const std::filesystem::path subghz_dir; #endif /* __FILE_PATH_H__ */ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 7d66d325d..3a695e745 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -122,7 +122,7 @@ const NavigationView::AppList NavigationView::appList = { {nullptr, "Transmit", HOME, Color::cyan(), &bitmap_icon_transmit, new ViewFactory()}, {"capture", "Capture", HOME, Color::red(), &bitmap_icon_capture, new ViewFactory()}, {"replay", "Replay", HOME, Color::green(), &bitmap_icon_replay, new ViewFactory()}, - {"scanner", "Scanner", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory()}, + {"recon", "Recon", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory()}, {"microphone", "Microphone", HOME, Color::green(), &bitmap_icon_microphone, new ViewFactory()}, {"lookingglass", "Looking Glass", HOME, Color::green(), &bitmap_icon_looking, new ViewFactory()}, {nullptr, "Utilities", HOME, Color::cyan(), &bitmap_icon_utilities, new ViewFactory()}, @@ -139,7 +139,7 @@ const NavigationView::AppList NavigationView::appList = { {"level", "Level", RX, Color::green(), &bitmap_icon_options_radio, new ViewFactory()}, {"pocsag", "POCSAG", RX, Color::green(), &bitmap_icon_pocsag, new ViewFactory()}, {"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory()}, - {"recon", "Recon", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory()}, + {"scanner", "Scanner", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory()}, {"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory()}, {"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory()}, {"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory()}, diff --git a/firmware/common/portapack_shared_memory.hpp b/firmware/common/portapack_shared_memory.hpp index c7fef0bde..a5937f01a 100644 --- a/firmware/common/portapack_shared_memory.hpp +++ b/firmware/common/portapack_shared_memory.hpp @@ -35,6 +35,13 @@ struct JammerChannel { uint32_t duration; }; +struct HopperChannel { + bool enabled; + uint64_t center; + uint32_t width; + uint32_t duration; +}; + struct ToneDef { uint32_t delta; uint32_t duration; @@ -61,7 +68,10 @@ struct SharedMemory { union { ToneData tones_data; - JammerChannel jammer_channels[24]; + struct { + JammerChannel jammer_channels[24]; + HopperChannel hopper_channels[24]; + } dummy_seperate; uint8_t data[512]; } bb_data{{{{0, 0}}, 0, {0}}}; diff --git a/sdcard/HOPPER/BLE_adv.PHOP b/sdcard/HOPPER/BLE_adv.PHOP new file mode 100644 index 000000000..a7df963c3 --- /dev/null +++ b/sdcard/HOPPER/BLE_adv.PHOP @@ -0,0 +1,3 @@ +2402000000 +2426000000 +2480000000 \ No newline at end of file diff --git a/sdcard/HOPPER/fobs.PHOP b/sdcard/HOPPER/fobs.PHOP new file mode 100644 index 000000000..1126f6962 --- /dev/null +++ b/sdcard/HOPPER/fobs.PHOP @@ -0,0 +1,10 @@ +433920000 +315000000 +418000000 +303000000 +310000000 +330000000 +350000000 +370000000 +390000000 +430500000 \ No newline at end of file diff --git a/sdcard/HOPPER/fobs_common.PHOP b/sdcard/HOPPER/fobs_common.PHOP new file mode 100644 index 000000000..d1b7b0f5e --- /dev/null +++ b/sdcard/HOPPER/fobs_common.PHOP @@ -0,0 +1,2 @@ +315000000 +433920000