Compare commits

...

8 Commits

Author SHA1 Message Date
RocketGod fd158e873a Jammer improvements (#2674)
* Add new jammer modes
Overview:
This PR enhances the PortaPack Jammer app by introducing eight new signal types, ported from my Flipper Zero RF Jammer app (https://github.com/RocketGod-git/flipper-zero-rf-jammer). These modes expand the app's capability to disrupt a wide range of RF communication protocols, from analog radios to modern digital systems. The implementation preserves the original app structure, resolves namespace conflicts, and ensures compatibility with the Mayhem firmware.

New Modes

The following modes have been added to the options_type in ui_jammer.hpp, with corresponding signal generation in proc_jammer.cpp:

Noise: Generates broadband white noise to interfere with analog and digital signals (e.g., Wi-Fi, Bluetooth, key fobs). Highly effective for overwhelming receivers across a frequency range.

Sine: Produces a continuous, unmodulated sine wave to jam narrowband receivers, ideal for analog FM/AM radios or telemetry systems.

Square: Emits a harmonic-rich square wave, disrupting digital protocols (e.g., OOK, ASK) and systems sensitive to sharp transitions, such as remote keyless entry.

Sawtooth (Experimental): Generates a sawtooth wave with a unique harmonic profile, useful for testing interference against PWM-based or niche analog systems.

Triangle (Experimental): Creates a triangle wave with minimal harmonics, suitable for exploratory jamming of narrowband systems or receiver linearity testing.

Chirp: Outputs a rapid frequency-sweeping chirp signal, effective against frequency-hopping and spread-spectrum systems (e.g., some Wi-Fi, Bluetooth, or military radios).

Gauss: Generates Gaussian noise to mimic natural interference, targeting digital systems like GPS or data links by degrading signal-to-noise ratios.

Brute (Experimental): Transmits a constant maximum-amplitude signal to saturate simple receiver front-ends, useful for brute-force jamming of basic analog devices.

* Fixed and made brutal.

This PR introduces user-focused improvements to the Jammer App in the HackRF PortaPack Mayhem Firmware, enhancing usability and flexibility. The changes address specific user requirements for a more intuitive default configuration, continuous waveform support, and dynamic setting adjustments during transmission.
2025-05-31 22:16:57 +02:00
Totoo f90bd44617 Detector RX ext app (#2673) 2025-05-28 19:19:47 +08:00
Totoo 2602c417be ui new coord system examples and macros (#2672) 2025-05-27 17:37:41 +08:00
Totoo aa1264cf91 getres cmd (#2671) 2025-05-27 15:18:33 +08:00
Brumi-2021 c79abccffd wfm_filters_GUI_name_std (#2668) 2025-05-25 20:04:08 +02:00
Brumi-2021 d3c9dc1f10 Storing_selected_NOAA_filter_in_settings_file (#2665)
* Storing_selected_NOAA_filter_in_settings_file

* format_issues
2025-05-24 18:18:24 +02:00
Totoo 3c8335c942 Audio to right (#2664)
* r.align
2025-05-23 12:27:57 +02:00
Totoo ac31f4781c screen max width fixes (#2663)
* max width fixes

* format
2025-05-23 08:42:19 +02:00
69 changed files with 818 additions and 214 deletions
+3
View File
@@ -188,6 +188,7 @@ void copy_to_radio_model(const AppSettings& settings) {
settings.am_config_index,
settings.nbfm_config_index,
settings.wfm_config_index,
settings.wfmam_config_index,
settings.squelch);
}
@@ -218,6 +219,7 @@ void copy_from_radio_model(AppSettings& settings) {
settings.am_config_index = receiver_model.am_configuration();
settings.nbfm_config_index = receiver_model.nbfm_configuration();
settings.wfm_config_index = receiver_model.wfm_configuration();
settings.wfmam_config_index = receiver_model.wfmam_configuration();
}
settings.step = receiver_model.frequency_step();
@@ -271,6 +273,7 @@ SettingsManager::SettingsManager(
bindings_.emplace_back("am_config_index"sv, &settings_.am_config_index);
bindings_.emplace_back("nbfm_config_index"sv, &settings_.nbfm_config_index);
bindings_.emplace_back("wfm_config_index"sv, &settings_.wfm_config_index);
bindings_.emplace_back("wfmam_config_index"sv, &settings_.wfmam_config_index);
bindings_.emplace_back("squelch"sv, &settings_.squelch);
}
+1
View File
@@ -139,6 +139,7 @@ struct AppSettings {
uint8_t am_config_index = 0;
uint8_t nbfm_config_index = 0;
uint8_t wfm_config_index = 0;
uint8_t wfmam_config_index = 0;
uint8_t squelch = 80;
uint8_t volume;
// NOTE: update COMMON_APP_SETTINGS_COUNT when adding to this
+1 -1
View File
@@ -210,7 +210,7 @@ class AISAppView : public View {
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
@@ -185,7 +185,7 @@ class SPECOptionsView : public View {
{19 * 8, 0 * 16, 11 * 8, 1 * 16}, // 18 (x col.) x char_size, 12 (length) x 8 blanking space to delete previous chars.
"Rx_IQ_CAL "};
NumberField field_rx_iq_phase_cal{
{28 * 8, 0 * 16},
{screen_width - 2 * 8, 0 * 16},
2,
{0, 63}, // 5 or 6 bits IQ CAL phase adjustment (range updated later)
1,
@@ -244,7 +244,7 @@ class AnalogAudioView : public View {
{"previous_zoom"sv, &previous_zoom}, // we are saving and restoring AMFM ZOOM factor from Settings.
}};
const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16};
const Rect options_view_rect{0 * 8, 1 * 16, screen_width, 1 * 16};
const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16};
size_t spec_bw_index = 0;
@@ -283,7 +283,7 @@ class AnalogAudioView : public View {
}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
Text text_ctcss{
{16 * 8, 1 * 16, 14 * 8, 1 * 16},
@@ -292,7 +292,7 @@ class AnalogAudioView : public View {
std::unique_ptr<Widget> options_widget{};
RecordView record_view{
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
{0 * 8, 2 * 16, screen_width, 1 * 16},
u"AUD",
u"AUDIO",
RecordView::FileType::WAV,
+1 -1
View File
@@ -216,7 +216,7 @@ class BLETxView : public View {
true};
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
{screen_width - 2 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
+1 -1
View File
@@ -109,7 +109,7 @@ class CaptureAppView : public View {
/*small*/ true};
RecordView record_view{
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
{0 * 8, 2 * 16, screen_width, 1 * 16},
u"BBD_????.*",
captures_dir,
RecordView::FileType::RawS16,
+1 -1
View File
@@ -166,7 +166,7 @@ class ERTAppView : public View {
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
MessageHandlerRegistration message_handler_packet{
Message::ID::ERTPacket,
+1 -1
View File
@@ -259,7 +259,7 @@ class POCSAGAppView : public View {
' ',
true /*wrap*/};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
Image image_status{
{0 * 8 + 4, 1 * 16 + 2, 16, 16},
+4 -4
View File
@@ -107,7 +107,7 @@ class SoundBoardView : public View {
"<="};
Text page_info{
{0, 29 * 8, 30 * 8, 16}};
{0, 29 * 8, screen_width, 16}};
MenuView menu_view{
{0, 0, screen_width, 175},
@@ -131,9 +131,9 @@ class SoundBoardView : public View {
{}};
AudioVolumeField field_volume{
{28 * 8, 180}};
{screen_width - 2 * 8, 180}};
Text text_volume_disabled{
{28 * 8, 180, 3 * 8, 16},
{screen_width - 2 * 8, 180, 3 * 8, 16},
"--"};
Checkbox check_loop{
@@ -147,7 +147,7 @@ class SoundBoardView : public View {
"Random"};
ProgressBar progressbar{
{0 * 8, 31 * 8 + 2, 30 * 8, 4}};
{0 * 8, 31 * 8 + 2, screen_width, 4}};
TransmitterView tx_view{
16 * 16,
+11 -11
View File
@@ -230,7 +230,7 @@ class ADSBRxAircraftDetailsView : public View {
"-"};
Text text_model{
{0 * 8, 6 * 16, 30 * 8, 16},
{0 * 8, 6 * 16, screen_width, 16},
"-"};
Text text_type{
@@ -238,19 +238,19 @@ class ADSBRxAircraftDetailsView : public View {
"-"};
Text text_number_of_engines{
{18 * 8, 8 * 16, 30 * 8, 16},
{18 * 8, 8 * 16, screen_width, 16},
"-"};
Text text_engine_type{
{0 * 8, 10 * 16, 30 * 8, 16},
{0 * 8, 10 * 16, screen_width, 16},
"-"};
Text text_owner{
{0 * 8, 12 * 16, 30 * 8, 16},
{0 * 8, 12 * 16, screen_width, 16},
"-"};
Text text_operator{
{0 * 8, 14 * 16, 30 * 8, 16},
{0 * 8, 14 * 16, screen_width, 16},
"-"};
Button button_close{
@@ -312,7 +312,7 @@ class ADSBRxDetailsView : public View {
"-"};
Text text_airline{
{0 * 8, 4 * 16, 30 * 8, 16},
{0 * 8, 4 * 16, screen_width, 16},
"-"};
Text text_country{
@@ -320,18 +320,18 @@ class ADSBRxDetailsView : public View {
"-"};
Text text_infos{
{0 * 8, 6 * 16, 30 * 8, 16},
{0 * 8, 6 * 16, screen_width, 16},
"-"};
Text text_info2{
{0 * 8, 7 * 16, 30 * 8, 16},
{0 * 8, 7 * 16, screen_width, 16},
"-"};
Text text_frame_pos_even{
{0 * 8, 14 * 16, 30 * 8, 16},
{0 * 8, 14 * 16, screen_width, 16},
"-"};
Text text_frame_pos_odd{
{0 * 8, 16 * 16, 30 * 8, 16},
{0 * 8, 16 * 16, screen_width, 16},
"-"};
Button button_aircraft_details{
@@ -442,7 +442,7 @@ class ADSBRxView : public View {
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
MessageHandlerRegistration message_handler_frame{
Message::ID::ADSBFrame,
+2 -2
View File
@@ -220,7 +220,7 @@ class APRSRxView : public View {
{21 * 8, 5, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
OptionsField options_region{
{0 * 8, 0 * 8},
@@ -241,7 +241,7 @@ class APRSRxView : public View {
// DEBUG
RecordView record_view{
{0 * 8, 1 * 16, 30 * 8, 1 * 16},
{0 * 8, 1 * 16, screen_width, 1 * 16},
u"AFS_????.WAV",
aprs_dir,
RecordView::FileType::WAV,
+1 -1
View File
@@ -91,7 +91,7 @@ class APRSTXView : public View {
' '};
Text text_payload{
{0 * 8, 5 * 16, 30 * 8, 16},
{0 * 8, 5 * 16, screen_width, 16},
"-"};
Button button_set{
{0 * 8, 6 * 16, 80, 32},
+1 -1
View File
@@ -90,7 +90,7 @@ class BattinfoView : public View {
"-"};
Text text_warn{
{1 * 8, 8 * 16, 30 * 8, 2 * 16},
{1 * 8, 8 * 16, screen_width, 2 * 16},
""}; */
Button button_mode{
+1 -1
View File
@@ -218,7 +218,7 @@ class BHTView : public View {
' '};
ProgressBar progressbar{
{0 * 8, 29 * 8, 30 * 8, 16},
{0 * 8, 29 * 8, screen_width, 16},
};
TransmitterView tx_view{
+2 -2
View File
@@ -172,7 +172,7 @@ private:
};
Text text_path {
{ 0 * 8, 2 * 16, 30 * 8, 16 },
{ 0 * 8, 2 * 16, screen_width, 16 },
"",
};
@@ -182,7 +182,7 @@ private:
};
Text text_name {
{ 0 * 8, 7 * 16, 30 * 8, 16 },
{ 0 * 8, 7 * 16, screen_width, 16 },
"",
};
+1 -1
View File
@@ -100,7 +100,7 @@ class FrequencySaveView : public FreqManBaseView {
{{0 * 8, 6 * 16}, "Description:", Theme::getInstance()->bg_darkest->foreground}};
TextField field_description{
{0 * 8, 7 * 16, 30 * 8, 1 * 16},
{0 * 8, 7 * 16, screen_width, 1 * 16},
""};
Button button_save{
+1 -1
View File
@@ -112,7 +112,7 @@ class IQTrimView : public View {
};
TextField field_path{
{0 * 8, 1 * 16, 30 * 8, 1 * 16},
{0 * 8, 1 * 16, screen_width, 1 * 16},
"Open File..."};
Point pos_lines{0 * 8, 4 * 16};
+2 -2
View File
@@ -202,7 +202,7 @@ void MicTXView::rxaudio(bool enable) {
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
// receiver_model.set_nbfm_configuration(n); is called above, depending user's selection (8k5, 11k, 16k).
break;
case MIC_MOD_WFM: // WFM, BW 200Khz aprox, or the two new addional BW filters (180k, 40k)
case MIC_MOD_WFM: // WFM, BW 200Khz aprox, or the two new addional BW filters (180k, 80k)
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
// receiver_model.set_wfm_configuration(n); is called above, depending user's selection (200k, 180k, 0k).
@@ -300,7 +300,7 @@ void MicTXView::update_receiver_rxbw(void) {
receiver_model.set_nbfm_configuration(rxbw_index); // we are in NFM/FM case, we need to select proper NFM/FM RX channel filter, NFM BW 8K5(0), NFM BW 11K(1), FM BW 16K (2)
break;
case MIC_MOD_WFM:
receiver_model.set_wfm_configuration(rxbw_index); // we are in WFM case, we need to select proper WFB RX BW filter, WFM BW 200K(0), WFM BW 180K(1), WFM BW 40K(2)
receiver_model.set_wfm_configuration(rxbw_index); // we are in WFM case, we need to select proper WFB RX BW filter, WFM BW 200K(0), WFM BW 180K(1), WFM BW 80K(2)
break;
case MIC_MOD_AM:
receiver_model.set_am_configuration(rxbw_index); // we are in AM TX mode, we need to select proper AM full path config AM-9K filter. 0+0 =>AM-9K(0), 0+1=1 =>AM-6K(1),
+1 -1
View File
@@ -350,7 +350,7 @@ class MicTXView : public View {
};
Button tx_button{
{10 * 8, 30 * 8, 10 * 8, 5 * 8},
{10 * 8, screen_width, 10 * 8, 5 * 8},
"PTT TX",
true};
+3 -3
View File
@@ -107,7 +107,7 @@ class PlaylistView : public View {
void handle_replay_thread_done(uint32_t return_code);
Text text_filename{
{0 * 8, 0 * 16, 30 * 8, 16}};
{0 * 8, 0 * 16, screen_width, 16}};
FrequencyField field_frequency{
{0 * 8, 1 * 16}};
@@ -139,13 +139,13 @@ class PlaylistView : public View {
true};
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
{screen_width - 2 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
Text text_track{
{0 * 8, 3 * 16, 30 * 8, 16}};
{0 * 8, 3 * 16, screen_width, 16}};
NewButton button_prev{
{2 * 8, 4 * 16, 4 * 8, 2 * 16},
+2 -2
View File
@@ -122,10 +122,10 @@ class POCSAGTXView : public View {
}};
Text text_message{
{0 * 8, 16 * 8, 30 * 8, 16},
{0 * 8, 16 * 8, screen_width, 16},
""};
Text text_message_l2{
{0 * 8, 18 * 8, 30 * 8, 16},
{0 * 8, 18 * 8, screen_width, 16},
""};
Button button_message{
+4 -4
View File
@@ -335,7 +335,7 @@ ReconView::ReconView(NavigationView& nav)
tx_view.hidden(true);
// set record View
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
record_view = std::make_unique<RecordView>(Rect{0, 0, screen_width, 1 * 16},
u"AUTO_AUDIO", audio_dir,
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
@@ -1164,18 +1164,18 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
}
if (new_mod == SPEC_MODULATION) {
if (persistent_memory::recon_repeat_recorded()) {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
record_view = std::make_unique<RecordView>(Rect{0, 0, screen_width, 1 * 16},
u"RECON_REPEAT.C16", captures_dir,
RecordView::FileType::RawS16, 16384, 3);
record_view->set_filename_as_is(true);
} else {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
record_view = std::make_unique<RecordView>(Rect{0, 0, screen_width, 1 * 16},
u"AUTO_RAW", captures_dir,
RecordView::FileType::RawS16, 16384, 3);
record_view->set_filename_date_frequency(true);
}
} else {
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
record_view = std::make_unique<RecordView>(Rect{0, 0, screen_width, 1 * 16},
u"AUTO_AUDIO", audio_dir,
RecordView::FileType::WAV, 4096, 4);
record_view->set_filename_date_frequency(true);
+1 -1
View File
@@ -120,7 +120,7 @@ class SondeView : public View {
{21 * 8, 0, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
Checkbox check_log{
{22 * 8, 8 * 16},
+2 -2
View File
@@ -95,10 +95,10 @@ class TestView : public View {
};
Text text_debug_a{
{0 * 8, 4 * 16, 30 * 8, 16},
{0 * 8, 4 * 16, screen_width, 16},
"..."};
Text text_debug_b{
{0 * 8, 5 * 16, 30 * 8, 16},
{0 * 8, 5 * 16, screen_width, 16},
"..."};
Button button_cal{
@@ -149,7 +149,7 @@ class WeatherView : public View {
{21 * 8, 0, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
+2 -2
View File
@@ -93,8 +93,8 @@ void NBFMConfig::apply(const uint8_t squelch_level) const {
void WFMConfig::apply() const {
const WFMConfigureMessage message{
decim_0, // Dynamic array 24 taps : taps_200k_decim_0 , taps_180k_wfm_decim_0, taps_40k_wfm_decim_0
decim_1, // Dynamic array 16 taps : taps_200k_decim_1 or taps_180k_wfm_decim_1, taps_40k_wfm_decim_1
decim_0, // Dynamic array 24 taps : taps_200k_decim_0 , taps_180k_wfm_decim_0, taps_80k_wfm_decim_0
decim_1, // Dynamic array 16 taps : taps_200k_decim_1 or taps_180k_wfm_decim_1, taps_80k_wfm_decim_1
taps_64_lp_156_198, // Fixed channel audio filter 15khz
75000,
audio_48k_hpf_30hz_config,
+1 -1
View File
@@ -56,7 +56,7 @@ struct NBFMConfig {
};
struct WFMConfig {
const fir_taps_real<24> decim_0; // To handle all 3 WFM filters , 200k, 180k and 40K-
const fir_taps_real<24> decim_0; // To handle all 3 WFM filters , 200k, 180k and 80K-
const fir_taps_real<16> decim_1;
void apply() const;
+1 -1
View File
@@ -90,7 +90,7 @@ class ACARSAppView : public View {
{0, 3 * 16, screen_width, 256}};
AudioVolumeField field_volume{
{28 * 8, 1 * 16}};
{screen_width - 2 * 8, 1 * 16}};
std::unique_ptr<ACARSLogger> logger{};
+1 -1
View File
@@ -84,7 +84,7 @@ class AFSKRxView : public View {
{21 * 8, 5, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
+1 -1
View File
@@ -58,7 +58,7 @@ class AnalogTvView : public View {
app_settings::SettingsManager settings_{
"rx_tv", app_settings::Mode::RX};
const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16};
const Rect options_view_rect{0 * 8, 1 * 16, screen_width, 1 * 16};
const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16};
RSSI rssi{
+1 -1
View File
@@ -44,7 +44,7 @@ class DebugDumpView : public View {
NavigationView& nav_;
Text dump_output{
{0 * 8, 19 * 8, 30 * 8, 16},
{0 * 8, 19 * 8, screen_width, 16},
""};
Button button_exit{
+84
View File
@@ -0,0 +1,84 @@
/*
* 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_detector_rx.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::detector_rx {
void initialize_app(ui::NavigationView& nav) {
nav.push<DetectorRxView>();
}
} // namespace ui::external_app::detector_rx
extern "C" {
__attribute__((section(".external_app.app_detector_rx.application_information"), used)) application_information_t _application_information_detector_rx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::detector_rx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Detector",
/*.bitmap_data = */
{
0x00,
0x00,
0x00,
0x00,
0x04,
0x20,
0x12,
0x48,
0x8A,
0x51,
0xCA,
0x53,
0xCA,
0x53,
0x8A,
0x51,
0x12,
0x48,
0x84,
0x21,
0xC0,
0x03,
0x40,
0x02,
0x60,
0x06,
0x20,
0x04,
0x30,
0x0C,
0xF0,
0x0F},
/*.icon_color = */ ui::Color::orange().v,
/*.menu_location = */ app_location_t::RX,
/*.desired_menu_position = */ -1,
// this has to be the biggest baseband used by the app. SPEC
/*.m4_app_tag = portapack::spi_flash::image_tag_nfm */ {'P', 'W', 'F', 'M'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -0,0 +1,177 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
*
* 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_detector_rx.hpp"
#include "ui_fileman.hpp"
#include "ui_freqman.hpp"
#include "baseband_api.hpp"
#include "file.hpp"
#include "oversample.hpp"
#include "ui_font_fixed_8x16.hpp"
using namespace portapack;
using namespace tonekey;
using portapack::memory::map::backup_ram;
namespace ui::external_app::detector_rx {
// Function to map the value from one range to another
int32_t DetectorRxView::map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh) {
return toLow + (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow);
}
void DetectorRxView::focus() {
field_lna.focus();
}
DetectorRxView::~DetectorRxView() {
// reset performance counters request to default
shared_memory.request_m4_performance_counter = 0;
receiver_model.disable();
audio::output::stop();
baseband::shutdown();
}
void DetectorRxView::on_timer() {
freq_index++;
if (freq_mode == 0 && freq_index >= tetra_uplink_monitoring_frequencies_hz.size()) freq_index = 0; // TETRA UP
if (freq_mode == 1 && freq_index >= lora_monitoring_frequencies_hz.size()) freq_index = 0; // Lora
if (freq_mode == 2 && freq_index >= remotes_monitoring_frequencies_hz.size()) freq_index = 0; // Remotes
if (freq_mode == 2)
freq_ = remotes_monitoring_frequencies_hz[freq_index];
else if (freq_mode == 1)
freq_ = lora_monitoring_frequencies_hz[freq_index];
else
freq_ = tetra_uplink_monitoring_frequencies_hz[freq_index];
receiver_model.set_target_frequency(freq_);
}
DetectorRxView::DetectorRxView(NavigationView& nav)
: nav_{nav} {
add_children({
&labels,
&field_lna,
&field_vga,
&field_rf_amp,
&field_volume,
&text_frequency,
&freq_stats_rssi,
&freq_stats_db,
&text_beep_squelch,
&field_beep_squelch,
&rssi,
&rssi_graph,
&field_mode,
});
// activate vertical bar mode
rssi.set_vertical_rssi(true);
freq_ = receiver_model.target_frequency();
field_beep_squelch.set_value(beep_squelch);
field_beep_squelch.on_change = [this](int32_t v) {
beep_squelch = v;
};
rssi_graph.set_nb_columns(256);
change_mode();
rssi.set_peak(true, 3000);
// FILL STEP OPTIONS
freq_stats_rssi.set_style(Theme::getInstance()->bg_darkest);
freq_stats_db.set_style(Theme::getInstance()->bg_darkest);
field_mode.on_change = [this](size_t, int32_t value) {
freq_mode = value;
freq_index = 0;
switch (value) {
case 1: // Lora
text_frequency.set(" 433, 868, 915 Mhz");
break;
case 2: // Remotes
text_frequency.set(" 433, 315 Mhz");
break;
default:
case 0: // TETRA UP
text_frequency.set(" 380-390 Mhz");
break;
}
};
}
void DetectorRxView::on_statistics_update(const ChannelStatistics& statistics) {
static int16_t last_max_db = 0;
static uint8_t last_min_rssi = 0;
static uint8_t last_avg_rssi = 0;
static uint8_t last_max_rssi = 0;
rssi_graph.add_values(rssi.get_min(), rssi.get_avg(), rssi.get_max(), statistics.max_db);
// refresh db
if (last_max_db != statistics.max_db) {
last_max_db = statistics.max_db;
freq_stats_db.set("Power: " + to_string_dec_int(statistics.max_db) + " db");
rssi.set_db(statistics.max_db);
}
// refresh rssi
if (last_min_rssi != rssi_graph.get_graph_min() || last_avg_rssi != rssi_graph.get_graph_avg() || last_max_rssi != rssi_graph.get_graph_max()) {
last_min_rssi = rssi_graph.get_graph_min();
last_avg_rssi = rssi_graph.get_graph_avg();
last_max_rssi = rssi_graph.get_graph_max();
freq_stats_rssi.set("RSSI: " + to_string_dec_uint(last_min_rssi) + "/" + to_string_dec_uint(last_avg_rssi) + "/" + to_string_dec_uint(last_max_rssi));
}
if (statistics.max_db > beep_squelch) {
baseband::request_audio_beep(map(statistics.max_db, -100, 20, 400, 2600), 24000, 150);
}
} /* on_statistic_updates */
size_t DetectorRxView::change_mode() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
audio_sampling_rate = audio::Rate::Hz_24000;
baseband::run_image(portapack::spi_flash::image_tag_capture);
receiver_model.set_modulation(ReceiverModel::Mode::Capture);
baseband::set_sample_rate(DETECTOR_BW, get_oversample_rate(DETECTOR_BW));
// The radio needs to know the effective sampling rate.
auto actual_sampling_rate = get_actual_sample_rate(DETECTOR_BW);
receiver_model.set_sampling_rate(actual_sampling_rate);
receiver_model.set_baseband_bandwidth(filter_bandwidth_for_sampling_rate(actual_sampling_rate));
audio::set_rate(audio_sampling_rate);
audio::output::start();
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
receiver_model.enable();
return 0;
}
} // namespace ui::external_app::detector_rx
@@ -0,0 +1,201 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef _UI_DETECTOR_RX
#define _UI_DETECTOR_RX
#include "analog_audio_app.hpp"
#include "app_settings.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "file.hpp"
#include "freqman_db.hpp"
#include "portapack_persistent_memory.hpp"
#include "radio_state.hpp"
#include "receiver_model.hpp"
#include "string_format.hpp"
#include "ui.hpp"
#include "ui_mictx.hpp"
#include "ui_receiver.hpp"
#include "ui_spectrum.hpp"
namespace ui::external_app::detector_rx {
#define DETECTOR_BW 750000
class DetectorRxView : public View {
public:
DetectorRxView(NavigationView& nav);
~DetectorRxView();
void focus() override;
std::string title() const override { return "Detector RX"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{};
int32_t map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh);
size_t change_mode();
void on_statistics_update(const ChannelStatistics& statistics);
void set_display_freq(int64_t freq);
void on_timer();
uint8_t freq_index = 0;
rf::Frequency freq_ = {433920000};
int32_t beep_squelch = 0;
audio::Rate audio_sampling_rate = audio::Rate::Hz_48000;
uint8_t freq_mode = 0;
app_settings::SettingsManager settings_{
"rx_detector",
app_settings::Mode::RX,
{
{"beep_squelch"sv, &beep_squelch},
}};
Labels labels{
{{UI_POS_X(0), UI_POS_Y(0)}, "LNA: VGA: AMP: VOL: ", Theme::getInstance()->fg_light->foreground}};
LNAGainField field_lna{
{UI_POS_X(4), UI_POS_Y(0)}};
VGAGainField field_vga{
{UI_POS_X(11), UI_POS_Y(0)}};
RFAmpField field_rf_amp{
{UI_POS_X(18), UI_POS_Y(0)}};
AudioVolumeField field_volume{
{UI_POS_X(24), UI_POS_Y(0)}};
OptionsField field_mode{
{UI_POS_X(0), UI_POS_Y(1)},
9,
{
{"TETRA UP", 0},
{"Lora", 1},
{"Remotes", 2},
}};
Text text_frequency{
{UI_POS_X_RIGHT(20), UI_POS_Y(1), UI_POS_WIDTH(20), UI_POS_DEFAULT_HEIGHT},
""};
Text text_beep_squelch{
{UI_POS_X_RIGHT(9), UI_POS_Y(2), UI_POS_WIDTH(4), UI_POS_DEFAULT_HEIGHT},
"Bip>"};
NumberField field_beep_squelch{
{UI_POS_X_RIGHT(5), UI_POS_Y(2)},
4,
{-100, 20},
1,
' ',
};
// RSSI: XX/XX/XXX
Text freq_stats_rssi{
{UI_POS_X(0), UI_POS_Y(2), UI_POS_WIDTH(15), UI_POS_DEFAULT_HEIGHT},
};
// Power: -XXX db
Text freq_stats_db{
{UI_POS_X(0), UI_POS_Y(3), UI_POS_WIDTH(15), UI_POS_DEFAULT_HEIGHT},
};
RSSIGraph rssi_graph{
{UI_POS_X(0), UI_POS_Y(5), UI_POS_WIDTH_REMAINING(5), UI_POS_HEIGHT_REMAINING(6)},
};
RSSI rssi{
{UI_POS_X_RIGHT(5), UI_POS_Y(5), UI_POS_WIDTH(5), UI_POS_HEIGHT_REMAINING(6)},
};
MessageHandlerRegistration message_handler_stats{
Message::ID::ChannelStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->on_timer();
}};
const std::vector<uint32_t> remotes_monitoring_frequencies_hz = {
// Around 315 MHz (common for older remotes, key fobs in some regions)
// Window centered on 315 MHz, covers 314.625 - 315.375 MHz
315000000,
// Around 433.92 MHz (very common for remotes, sensors, key fobs globally)
// Window centered on 433.92 MHz, covers 433.545 - 434.295 MHz
433920000,
};
const std::vector<uint32_t> lora_monitoring_frequencies_hz = {
// EU433 Band (Europe, typically 433.05 MHz to 434.79 MHz)
// Scanning the approximate range 433.0 MHz to 434.8 MHz with 750kHz steps
433375000, // Covers 433.000 - 433.750 MHz
434125000, // Covers 433.750 - 434.500 MHz (includes 433.92 MHz)
434875000, // Covers 434.500 - 435.250 MHz (covers up to 434.79 MHz)
// EU868 Band (Europe, typically 863 MHz to 870 MHz, specific channels around 868 MHz)
// Targeting common LoRaWAN channel groups (approx 867.0 - 868.6 MHz) with 750kHz steps
867375000, // Covers 867.000 - 867.750 MHz
868125000, // Covers 867.750 - 868.500 MHz
868875000, // Covers 868.500 - 869.250 MHz (covers up to 868.6 MHz)
// US915 Band (North America, typically 902 MHz to 928 MHz, specific channels around 915 MHz)
// Providing a few sample windows around the 915 MHz area with 750kHz steps.
// This band is wide; a full scan would require many more frequencies.
914250000, // Covers 913.875 - 914.625 MHz
915000000, // Covers 914.625 - 915.375 MHz (Centered on 915 MHz)
915750000, // Covers 915.375 - 916.125 MHz
};
const std::vector<uint32_t> tetra_uplink_monitoring_frequencies_hz = {
// Band starts at 380,000,000 Hz, ends at 390,000,000 Hz.
// First center: 380,000,000 + 375,000 = 380,375,000 Hz
// Last center: 380,375,000 + 13 * 750,000 = 390,125,000 Hz (14 frequencies total for this band)
380375000, // Covers 380.000 - 380.750 MHz
381125000, // Covers 380.750 - 381.500 MHz
381875000, // Covers 381.500 - 382.250 MHz
382625000, // Covers 382.250 - 383.000 MHz
383375000, // Covers 383.000 - 383.750 MHz
384125000, // Covers 383.750 - 384.500 MHz
384875000, // Covers 384.500 - 385.250 MHz
385625000, // Covers 385.250 - 386.000 MHz
386375000, // Covers 386.000 - 386.750 MHz
387125000, // Covers 386.750 - 387.500 MHz
387875000, // Covers 387.500 - 388.250 MHz
388625000, // Covers 388.250 - 389.000 MHz
389375000, // Covers 389.000 - 389.750 MHz
390125000, // Covers 389.750 - 390.500 MHz
};
};
} // namespace ui::external_app::detector_rx
#endif
+5
View File
@@ -212,6 +212,10 @@ set(EXTCPPSRC
#gfxEQ
external/gfxeq/main.cpp
external/gfxeq/ui_gfxeq.cpp
#detector_rx
external/detector_rx/main.cpp
external/detector_rx/ui_detector_rx.cpp
)
set(EXTAPPLIST
@@ -266,4 +270,5 @@ set(EXTAPPLIST
scanner
level
gfxeq
detector_rx
)
+10
View File
@@ -74,6 +74,7 @@ MEMORY
ram_external_app_level (rwx) : org = 0xADE10000, len = 32k
ram_external_app_gfxeq (rwx) : org = 0xADE20000, len = 32k
ram_external_app_noaaapt_rx (rwx) : org = 0xADE30000, len = 32k
ram_external_app_detector_rx (rwx) : org = 0xADE40000, len = 32k
}
SECTIONS
@@ -383,4 +384,13 @@ SECTIONS
KEEP(*(.external_app.app_gfxeq.application_information));
*(*ui*external_app*gfxeq*);
} > ram_external_app_gfxeq
.external_app_detector_rx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_detector_rx.application_information));
*(*ui*external_app*detector_rx*);
} > ram_external_app_detector_rx
}
+1 -1
View File
@@ -60,7 +60,7 @@ class ExtSensorsView : public View {
{{0 * 8, 5 * 16}, "ORI:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16}, "ENV:", Theme::getInstance()->fg_light->foreground}};
Text text_info{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "Connect a compatible module..."};
Text text_info{{0 * 8, 0 * 8, screen_width, 16 * 1}, "Connect a compatible module..."};
Text text_gps{{5 * 8, 3 * 16, 24 * 8, 16}, "-"};
Text text_orientation{{5 * 8, 5 * 16, 24 * 8, 16}, "-"};
Text text_envl1{{5 * 8, 7 * 16, 24 * 8, 16}, "-"};
+2 -2
View File
@@ -89,7 +89,7 @@ class FmRadioView : public View {
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
@@ -103,7 +103,7 @@ class FmRadioView : public View {
{21 * 8, 10, 6 * 8, 4}};
Waveform waveform{
{0, 20, 30 * 8, 2 * 16},
{0, 20, screen_width, 2 * 16},
audio_spectrum,
128,
0,
+1 -1
View File
@@ -62,7 +62,7 @@ class FoxhuntRxView : public View {
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
+1 -1
View File
@@ -32,7 +32,7 @@ gfxEQView::gfxEQView(NavigationView& nav)
baseband::run_image(portapack::spi_flash::image_tag_wfm_audio);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.set_wfm_configuration(1); // 200k => 0 , 180k => 1 , 40k => 2. Set to 1 or 2 for better reception
receiver_model.set_wfm_configuration(1); // 200k => 0 , 180k => 1 , 80k => 2. Set to 1 or 2 for better reception
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
+1 -1
View File
@@ -99,7 +99,7 @@ class gfxEQView : public View {
LNAGainField field_lna{{15 * 8, 0 * 16}};
VGAGainField field_vga{{18 * 8, 0 * 16}};
Button button_mood{{21 * 8, 0, 6 * 8, 16}, "MOOD"};
AudioVolumeField field_volume{{28 * 8, 0 * 16}};
AudioVolumeField field_volume{{screen_width - 2 * 8, 0 * 16}};
rf::Frequency frequency_value{93100000};
+1 -1
View File
@@ -112,7 +112,7 @@ class GpsSimAppView : public View {
LanguageHelper::currentMessages[LANG_LOOP],
true};
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
{screen_width - 2 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
+129 -48
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2025 RocketGod - Added modes from my Flipper Zero RF Jammer App - https://betaskynet.com
*
* This file is part of PortaPack.
*
@@ -36,7 +37,6 @@ void RangeView::focus() {
}
void RangeView::update_start(rf::Frequency f) {
// Change everything except max
frequency_range.min = f;
button_start.set_text(to_string_short_freq(f));
@@ -48,7 +48,6 @@ void RangeView::update_start(rf::Frequency f) {
}
void RangeView::update_stop(rf::Frequency f) {
// Change everything except min
frequency_range.max = f;
button_stop.set_text(to_string_short_freq(f));
@@ -60,7 +59,6 @@ void RangeView::update_stop(rf::Frequency f) {
}
void RangeView::update_center(rf::Frequency f) {
// Change min/max/center, keep width
center = f;
button_center.set_text(to_string_short_freq(center));
@@ -75,7 +73,6 @@ void RangeView::update_center(rf::Frequency f) {
}
void RangeView::update_width(uint32_t w) {
// Change min/max/width, keep center
width = w;
button_width.set_text(to_string_short_freq(width));
@@ -91,7 +88,6 @@ void RangeView::update_width(uint32_t w) {
}
void RangeView::paint(Painter&) {
// Draw lines and arrows
Rect r;
Point p;
Coord c;
@@ -162,7 +158,7 @@ RangeView::RangeView(NavigationView& nav) {
auto load_view = nav.push<FrequencyLoadView>();
load_view->on_frequency_loaded = [this](rf::Frequency value) {
update_center(value);
update_width(100000); // 100kHz default jamming bandwidth when loading unique frequency
update_width(100000);
};
load_view->on_range_loaded = [this](rf::Frequency start, rf::Frequency stop) {
update_start(start);
@@ -193,10 +189,37 @@ void JammerView::set_jammer_channel(uint32_t i, uint32_t width, uint64_t center,
jammer_channels[i].enabled = true;
jammer_channels[i].width = (width * 0xFFFFFFULL) / 1536000;
jammer_channels[i].center = center;
jammer_channels[i].duration = 30720 * duration;
jammer_channels[i].duration = duration ? 30720 * duration : 0xFFFFFFFF;
}
void JammerView::start_tx() {
if (update_config()) {
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);
transmitter_model.enable();
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
mscounter = 0;
}
}
void JammerView::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;
seconds = 0;
mscounter = 0;
}
bool JammerView::update_config() {
uint32_t c, i = 0;
size_t num_channels;
rf::Frequency start_freq, range_bw, range_bw_sub, ch_width;
@@ -204,24 +227,19 @@ void JammerView::start_tx() {
size_t hop_value = options_hop.selected_index_value();
// Disable all channels by default
for (c = 0; c < JAMMER_MAX_CH; c++)
jammer_channels[c].enabled = false;
// Generate jamming channels with JAMMER_MAX_CH maximum width
// Convert ranges min/max to center/bw
for (size_t r = 0; r < 3; r++) {
if (range_views[r]->frequency_range.enabled) {
range_bw = abs(range_views[r]->frequency_range.max - range_views[r]->frequency_range.min);
// Get lower bound
if (range_views[r]->frequency_range.min < range_views[r]->frequency_range.max)
start_freq = range_views[r]->frequency_range.min;
else
start_freq = range_views[r]->frequency_range.max;
if (range_bw >= JAMMER_CH_WIDTH) {
// Split range in multiple channels
num_channels = 0;
range_bw_sub = range_bw;
@@ -241,7 +259,6 @@ void JammerView::start_tx() {
i++;
}
} else {
// Range fits in a single channel
if (i >= JAMMER_MAX_CH) {
out_of_ranges = true;
} else {
@@ -254,43 +271,24 @@ void JammerView::start_tx() {
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()
return true;
} else {
if (out_of_ranges)
nav_.display_modal("Error", "Jamming bandwidth too large.\nMust be less than 24MHz.");
else
nav_.display_modal("Error", "No range enabled.");
return false;
}
}
void JammerView::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 JammerView::on_timer() {
if (++mscounter == 60) {
if (++mscounter >= 60) {
mscounter = 0;
if (jamming) {
int32_t timepause = field_timepause.value();
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 .
if (timepause == 0 || ++seconds >= timepause) {
transmitter_model.set_baseband_bandwidth(28'000'000);
transmitter_model.enable();
button_transmit.set_text("STOP");
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
@@ -306,8 +304,7 @@ void JammerView::on_timer() {
seconds = 0;
}
} else {
if (++seconds >= field_timetx.value()) // Start cooling period:
{
if (timepause && ++seconds >= field_timetx.value()) {
transmitter_model.disable();
button_transmit.set_text("PAUSED");
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
@@ -327,11 +324,9 @@ void JammerView::on_timer() {
}
}
JammerView::JammerView(
NavigationView& nav)
JammerView::JammerView(NavigationView& nav)
: nav_{nav} {
Rect view_rect = {0, 3 * 8, screen_width, 80};
// baseband::run_image(portapack::spi_flash::image_tag_jammer);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&tab_view,
@@ -355,15 +350,101 @@ JammerView::JammerView(
view_range_b.set_parent_rect(view_rect);
view_range_c.set_parent_rect(view_rect);
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);
view_range_a.check_enabled.set_value(true);
view_range_a.frequency_range.enabled = true;
view_range_a.update_center(315'000'000);
view_range_a.update_width(1'000'000);
options_type.set_selected_index(3);
options_speed.set_selected_index(3);
options_hop.set_selected_index(0);
field_timetx.set_value(30);
field_timepause.set_value(1);
field_timepause.set_value(0);
field_jitter.set_value(0);
field_gain.set_value(transmitter_model.tx_gain());
field_amp.set_value(transmitter_model.rf_amp());
button_transmit.set_style(&style_val);
options_type.on_change = [this](size_t, OptionsField::value_t) {
if (jamming) update_config();
if (jamming && !cooling) baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
};
options_speed.on_change = [this](size_t, OptionsField::value_t) {
if (jamming) update_config();
if (jamming && !cooling) baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
};
options_hop.on_change = [this](size_t, OptionsField::value_t) {
if (jamming) update_config();
};
field_timetx.on_change = [this](int32_t) {
if (jamming) update_config();
};
field_timepause.on_change = [this](int32_t) {
if (jamming) update_config();
};
field_jitter.on_change = [this](int32_t) {
if (jamming) update_config();
};
field_gain.on_change = [this](int32_t v) {
if (jamming) transmitter_model.set_tx_gain(v);
};
field_amp.on_change = [this](int32_t v) {
if (jamming) transmitter_model.set_rf_amp(v);
};
for (auto range_view : range_views) {
range_view->check_enabled.on_select = [this](Checkbox&, bool) {
if (jamming) update_config();
};
range_view->button_start.on_select = [this, range_view](Button&) {
auto new_view = nav_.push<FrequencyKeypadView>(range_view->frequency_range.min);
new_view->on_changed = [this, range_view](rf::Frequency f) {
range_view->update_start(f);
if (jamming) update_config();
};
};
range_view->button_stop.on_select = [this, range_view](Button&) {
auto new_view = nav_.push<FrequencyKeypadView>(range_view->frequency_range.max);
new_view->on_changed = [this, range_view](rf::Frequency f) {
range_view->update_stop(f);
if (jamming) update_config();
};
};
range_view->button_center.on_select = [this, range_view](Button&) {
auto new_view = nav_.push<FrequencyKeypadView>(range_view->center);
new_view->on_changed = [this, range_view](rf::Frequency f) {
range_view->update_center(f);
if (jamming) update_config();
};
};
range_view->button_width.on_select = [this, range_view](Button&) {
auto new_view = nav_.push<FrequencyKeypadView>(range_view->width);
new_view->on_changed = [this, range_view](rf::Frequency f) {
range_view->update_width(f);
if (jamming) update_config();
};
};
range_view->button_load_range.on_select = [this, range_view](Button&) {
auto load_view = nav_.push<FrequencyLoadView>();
load_view->on_frequency_loaded = [this, range_view](rf::Frequency value) {
range_view->update_center(value);
range_view->update_width(100000);
if (jamming) update_config();
};
load_view->on_range_loaded = [this, range_view](rf::Frequency start, rf::Frequency stop) {
range_view->update_start(start);
range_view->update_stop(stop);
if (jamming) update_config();
};
};
}
button_transmit.on_select = [this](Button&) {
if (jamming || cooling)
+29 -25
View File
@@ -41,27 +41,12 @@ class RangeView : public View {
RangeView(NavigationView& nav);
void focus() override;
void paint(Painter&) override;
void paint(Painter& painter) override;
jammer_range_t frequency_range{false, 0, 0};
private:
void update_start(rf::Frequency f);
void update_stop(rf::Frequency f);
void update_center(rf::Frequency f);
void update_width(uint32_t w);
uint32_t width{};
rf::Frequency center{};
const Style& style_info = *Theme::getInstance()->fg_medium;
Labels labels{
{{2 * 8, 8 * 8 + 4}, LanguageHelper::currentMessages[LANG_START], Theme::getInstance()->fg_light->foreground},
{{23 * 8, 8 * 8 + 4}, LanguageHelper::currentMessages[LANG_STOP], Theme::getInstance()->fg_light->foreground},
{{12 * 8, 5 * 8 - 4}, "Center", Theme::getInstance()->fg_light->foreground},
{{12 * 8 + 4, 13 * 8}, "Width", Theme::getInstance()->fg_light->foreground}};
Checkbox check_enabled{
{1 * 8, 4},
12,
@@ -74,15 +59,32 @@ class RangeView : public View {
Button button_start{
{0 * 8, 11 * 8, 11 * 8, 28},
""};
Button button_stop{
{19 * 8, 11 * 8, 11 * 8, 28},
""};
Button button_center{
{76, 4 * 15 - 4, 11 * 8, 28},
""};
Button button_width{
{76, 8 * 15, 11 * 8, 28},
""};
void update_start(rf::Frequency f);
void update_stop(rf::Frequency f);
void update_center(rf::Frequency f);
void update_width(uint32_t w);
private:
const Style& style_info = *Theme::getInstance()->fg_medium;
Labels labels{
{{2 * 8, 8 * 8 + 4}, LanguageHelper::currentMessages[LANG_START], Theme::getInstance()->fg_light->foreground},
{{23 * 8, 8 * 8 + 4}, LanguageHelper::currentMessages[LANG_STOP], Theme::getInstance()->fg_light->foreground},
{{12 * 8, 5 * 8 - 4}, "Center", Theme::getInstance()->fg_light->foreground},
{{12 * 8 + 4, 13 * 8}, "Width", Theme::getInstance()->fg_light->foreground}};
};
class JammerView : public View {
@@ -110,15 +112,16 @@ class JammerView : public View {
void start_tx();
void on_timer();
void stop_tx();
bool update_config();
void set_jammer_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration);
void on_retune(const rf::Frequency freq, const uint32_t range);
JammerChannel* jammer_channels = (JammerChannel*)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
bool cooling{false};
uint16_t seconds{0};
int16_t mscounter{0};
lfsr_word_t lfsr_v{1};
const Style& style_val = *Theme::getInstance()->fg_green;
const Style& style_cancel = *Theme::getInstance()->fg_red;
@@ -140,8 +143,8 @@ class JammerView : public View {
{{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.
{{1 * 8, 31 * 8}, "Sleep:", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 33 * 8}, "Jitter:", Theme::getInstance()->fg_light->foreground},
{{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},
@@ -182,7 +185,8 @@ class JammerView : public View {
OptionsField options_hop{
{7 * 8, 27 * 8},
5,
{{"10ms ", 1},
{{"Off ", 0},
{"10ms ", 1},
{"50ms ", 5},
{"100ms", 10},
{"1s ", 100},
@@ -201,7 +205,7 @@ class JammerView : public View {
NumberField field_timepause{
{8 * 8, 31 * 8},
2,
{1, 60},
{0, 60},
1,
' ',
};
@@ -209,7 +213,7 @@ class JammerView : public View {
NumberField field_jitter{
{8 * 8, 33 * 8},
2,
{1, 60},
{0, 60},
1,
' ',
};
+1 -1
View File
@@ -171,7 +171,7 @@ class LGEView : public View {
'0'};
Console console{
{0, 18 * 8, 30 * 8, 7 * 16}};
{0, 18 * 8, screen_width, 7 * 16}};
TransmitterView tx_view{
16 * 16,
+1 -1
View File
@@ -131,7 +131,7 @@ void NoaaAptRxView::on_status(NoaaAptRxStatusDataMessage msg) {
// this stores and displays the image. keep it as simple as you can. a bit more complexity will kill the sync
void NoaaAptRxView::on_image(NoaaAptRxImageDataMessage msg) {
if ((line_num) >= screen_height - NOAA_IMG_START_ROW * 16) line_num = 0; // for draw reset
if ((line_num) >= UI_POS_HEIGHT_REMAINING(NOAA_IMG_START_ROW)) line_num = 0; // for draw reset
for (uint16_t i = 0; i < msg.cnt; i += 1) {
Color pxl = {msg.image[i], msg.image[i], msg.image[i]};
+10 -15
View File
@@ -81,22 +81,22 @@ class NoaaAptRxView : public View {
{}};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{UI_POS_X(13), UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{UI_POS_X(15), UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{UI_POS_X(18), UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH(6), 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
RecordView record_view{
{0 * 8, 2 * 16, 30 * 8, 1 * 16},
{UI_POS_X(0), UI_POS_Y(2), UI_POS_MAXWIDTH, UI_POS_DEFAULT_HEIGHT},
u"AUD",
u"AUDIO",
RecordView::FileType::WAV,
@@ -104,22 +104,17 @@ class NoaaAptRxView : public View {
4};
Checkbox check_wav{
{0 * 8, 2 * 16},
{UI_POS_X(0), UI_POS_Y(2)},
12,
"Save WAV too",
true};
/*Labels labels{
{{1 * 8, 1 * 16}, "LPM:", Theme::getInstance()->fg_light->foreground},
{{13 * 8, 1 * 16}, "IOC:", Theme::getInstance()->fg_light->foreground},
};*/
Text txt_status{
{0 * 8, 1 * 16, 20 * 8, 16},
{UI_POS_X(0), UI_POS_Y(1), UI_POS_WIDTH(20), UI_POS_DEFAULT_HEIGHT},
};
Button button_ss{
{190, 1 * 16, 5 * 8, 16},
{UI_POS_X_RIGHT(6), UI_POS_Y(1), UI_POS_WIDTH(5), UI_POS_DEFAULT_HEIGHT},
LanguageHelper::currentMessages[LANG_START]};
MessageHandlerRegistration message_handler_stats{
+2 -2
View File
@@ -109,7 +109,7 @@ class OOKEditorAppView : public View {
Labels label_waveform{{{0, 188}, "Waveform:", Theme::getInstance()->fg_light->foreground}};
// Text field to display the various status message of the app
Text text_app_status{{0, 160, 30 * 8, 16}, ""};
Text text_app_status{{0, 160, screen_width, 16}, ""};
// OptionsField for selectable sample rates.
OptionsField field_sample_rate{{96, 20}, 7, {{"250k", 250000U}, {"1M", 1000000U}, {"2M", 2000000U}, {"5M", 5000000U}, {"10M", 10000000U}, {"20M", 20000000U}}};
@@ -121,7 +121,7 @@ class OOKEditorAppView : public View {
NumberField field_repeat{{210, 40}, 3, {1, 999}, 1, '0', false};
// Text field to display the payload data.
Text text_payload{{0 * 8, 100, 30 * 8, 16}, ""};
Text text_payload{{0 * 8, 100, screen_width, 16}, ""};
// Buttons for setting configurations, opening files, and starting transmission.
Button button_set{{0, 125, 60, 28}, LanguageHelper::currentMessages[LANG_SET]};
+16 -16
View File
@@ -60,29 +60,29 @@ class ProtoView : public View {
"rx_protoview", app_settings::Mode::RX};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
{UI_POS_X(13), UI_POS_Y(0)}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
{UI_POS_X(15), UI_POS_Y(0)}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
{UI_POS_X(18), UI_POS_Y(0)}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
{UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH(6), 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{UI_POS_X_RIGHT(2), UI_POS_Y(0)}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
{UI_POS_X(0), UI_POS_Y(0)},
nav_};
// need to seperate because label shift need to hide independently
Labels label_zoom{
{{0 * 8, 1 * 16}, "Zoom: ", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), UI_POS_Y(1)}, "Zoom: ", Theme::getInstance()->fg_light->foreground}};
Labels label_shift{
{{0 * 8, 2 * 16}, "Shift: ", Theme::getInstance()->fg_light->foreground}};
{{UI_POS_X(0), UI_POS_Y(2)}, "Shift: ", Theme::getInstance()->fg_light->foreground}};
OptionsField options_zoom{
{7 * 8, 1 * 16},
{UI_POS_X(7), UI_POS_Y(1)},
4,
{{"1", 1},
{"2", 2},
@@ -96,22 +96,22 @@ class ProtoView : public View {
{"1000", 1000}}};
NumberField number_shift{
{7 * 8, 2 * 16},
{UI_POS_X(7), UI_POS_Y(2)},
5,
{-MAXSIGNALBUFFER, MAXSIGNALBUFFER},
1,
' '};
Button button_reset{
{screen_width - 12 * 8, 1 * 16, 96, 24},
{UI_POS_X_RIGHT(12), UI_POS_Y(1), UI_POS_WIDTH(12), UI_POS_HEIGHT(1.5)},
LanguageHelper::currentMessages[LANG_RESET]};
Button button_pause{
{screen_width - 12 * 8, 1 * 16 + 24, 96, 24},
{UI_POS_X_RIGHT(12), UI_POS_Y(2.5), UI_POS_WIDTH(12), UI_POS_HEIGHT(1.5)},
LanguageHelper::currentMessages[LANG_PAUSE]};
Waveform waveform{
{0, 8 * 8, screen_width, 50},
{UI_POS_X(0), UI_POS_Y(4), UI_POS_MAXWIDTH, (UI_POS_HEIGHT_REMAINING(5) / 4) - 4},
waveform_buffer,
0,
0,
@@ -119,7 +119,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform2{
{0, 8 * 8 + 55, screen_width, 50},
{UI_POS_X(0), UI_POS_Y(4) + (UI_POS_HEIGHT_REMAINING(5) / 4), UI_POS_MAXWIDTH, (UI_POS_HEIGHT_REMAINING(5) / 4) - 4},
&waveform_buffer[MAXDRAWCNTPERWF],
0,
0,
@@ -127,7 +127,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform3{
{0, 8 * 8 + 110, screen_width, 50},
{UI_POS_X(0), UI_POS_Y(4) + 2 * (UI_POS_HEIGHT_REMAINING(5) / 4), UI_POS_MAXWIDTH, (UI_POS_HEIGHT_REMAINING(5) / 4) - 4},
&waveform_buffer[MAXDRAWCNTPERWF * 2],
0,
0,
@@ -135,7 +135,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform4{
{0, 8 * 8 + 165, screen_width, 50},
{UI_POS_X(0), UI_POS_Y(4) + 3 * (UI_POS_HEIGHT_REMAINING(5) / 4), UI_POS_MAXWIDTH, (UI_POS_HEIGHT_REMAINING(5) / 4) - 4},
&waveform_buffer[MAXDRAWCNTPERWF * 3],
0,
0,
+1 -1
View File
@@ -328,7 +328,7 @@ class RemoteAppView : public View {
bool ready_signal_{}; // Used to signal ReplayThread ready.
TextField field_title{
{0 * 8, 0 * 16 + 2, 30 * 8, 1 * 16},
{0 * 8, 0 * 16 + 2, screen_width, 1 * 16},
{}};
TransmitterView2 tx_view{
+1 -1
View File
@@ -217,7 +217,7 @@ class ScannerView : public View {
NumberField field_lock_wait{
// Signal-Lost wait timer - time to wait before moving on after losing signal lock
{28 * 8, 1 * 16},
{screen_width - 2 * 8, 1 * 16},
2,
{0, 99},
1,
@@ -121,7 +121,7 @@ class SpectrumPainterView : public View {
true};
ImageButton button_play{
{28 * 8, footer_location + 1 * 16, 2 * 8, 1 * 16},
{screen_width - 2 * 8, footer_location + 1 * 16, 2 * 8, 1 * 16},
&bitmap_play,
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
@@ -58,7 +58,7 @@ class SpectrumInputImageView : public View {
uint32_t data_start{0};
Button button_load_image{
{0 * 8, 11 * 16 - 4, 30 * 8, 28},
{0 * 8, 11 * 16 - 4, screen_width, 28},
"Load Image ..."};
bool drawBMP_scaled(const ui::Rect r, const std::string file);
@@ -53,43 +53,43 @@ class SpectrumInputTextView : public View {
void on_set_text(NavigationView& nav);
Text text_message_0{
{0 * 8, 0 * 16, 30 * 8, 16},
{0 * 8, 0 * 16, screen_width, 16},
""};
Text text_message_1{
{0 * 8, 1 * 16, 30 * 8, 16},
{0 * 8, 1 * 16, screen_width, 16},
""};
Text text_message_2{
{0 * 8, 2 * 16, 30 * 8, 16},
{0 * 8, 2 * 16, screen_width, 16},
""};
Text text_message_3{
{0 * 8, 3 * 16, 30 * 8, 16},
{0 * 8, 3 * 16, screen_width, 16},
""};
Text text_message_4{
{0 * 8, 4 * 16, 30 * 8, 16},
{0 * 8, 4 * 16, screen_width, 16},
""};
Text text_message_5{
{0 * 8, 5 * 16, 30 * 8, 16},
{0 * 8, 5 * 16, screen_width, 16},
""};
Text text_message_6{
{0 * 8, 6 * 16, 30 * 8, 16},
{0 * 8, 6 * 16, screen_width, 16},
""};
Text text_message_7{
{0 * 8, 7 * 16, 30 * 8, 16},
{0 * 8, 7 * 16, screen_width, 16},
""};
Text text_message_8{
{0 * 8, 8 * 16, 30 * 8, 16},
{0 * 8, 8 * 16, screen_width, 16},
""};
Text text_message_9{
{0 * 8, 9 * 16, 30 * 8, 16},
{0 * 8, 9 * 16, screen_width, 16},
""};
std::array<Text*, 10> text_message{{
@@ -106,7 +106,7 @@ class SpectrumInputTextView : public View {
}};
Button button_message{
{0 * 8, 11 * 16 - 4, 30 * 8, 28},
{0 * 8, 11 * 16 - 4, screen_width, 28},
"Set message"};
};
+1 -1
View File
@@ -138,7 +138,7 @@ class TPMSAppView : public View {
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
@@ -49,7 +49,7 @@ class WardriveMapView : public View {
NavigationView& nav_;
Text text_info{{0 * 8, 0 * 8, 20 * 8, 16 * 1}, "0 / 30"};
Text text_notfound{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "No GeoTagged captures found"};
Text text_notfound{{0 * 8, 0 * 8, screen_width, 16 * 1}, "No GeoTagged captures found"};
GeoPos geopos{
{0, 20},
GeoPos::alt_unit::METERS,
+3 -3
View File
@@ -118,7 +118,7 @@ class ViewWavView : public View {
Theme::getInstance()->fg_green->foreground,
Theme::getInstance()->fg_green->background};
AudioVolumeField field_volume{
{28 * 8, 18 * 16}};
{screen_width - 2 * 8, 18 * 16}};
Waveform waveform{
{0, 5 * 16, screen_width, 64},
@@ -129,7 +129,7 @@ class ViewWavView : public View {
Theme::getInstance()->bg_darkest->foreground};
ProgressBar progressbar{
{0 * 8, 11 * 16, 30 * 8, 4}};
{0 * 8, 11 * 16, screen_width, 4}};
NumberField field_pos_seconds{
{9 * 8, 12 * 16},
@@ -177,7 +177,7 @@ class ViewWavView : public View {
true};
Text text_delta{
{7 * 8, 16 * 16, 30 * 8, 16},
{7 * 8, 16 * 16, screen_width, 16},
"-"};
MessageHandlerRegistration message_handler_replay_thread_error{
+1 -1
View File
@@ -92,7 +92,7 @@ class WeFaxRxView : public View {
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
{screen_width - 2 * 8, 0 * 16}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
+1 -1
View File
@@ -70,7 +70,7 @@ options_t freqman_bandwidths[6] = {
},
{
// WFM
{"40k", 2},
{"80k", 2},
{"180k", 1},
{"200k", 0},
},
+3 -1
View File
@@ -66,7 +66,7 @@ static constexpr std::array<baseband::NBFMConfig, 3> nbfm_configs{{
static constexpr std::array<baseband::WFMConfig, 3> wfm_configs{{
{taps_200k_wfm_decim_0, taps_200k_wfm_decim_1},
{taps_180k_wfm_decim_0, taps_180k_wfm_decim_1},
{taps_40k_wfm_decim_0, taps_40k_wfm_decim_1},
{taps_80k_wfm_decim_0, taps_80k_wfm_decim_1},
}};
static constexpr std::array<baseband::WFMAMConfig, 2> wfmam_configs{{
@@ -274,12 +274,14 @@ void ReceiverModel::set_configuration_without_update(
size_t new_am_config_index,
size_t new_nbfm_config_index,
size_t new_wfm_config_index,
size_t new_wfmam_config_index,
uint8_t new_squelch_level) {
settings_.mode = new_mode;
settings_.frequency_step = new_frequency_step;
settings_.am_config_index = new_am_config_index;
settings_.nbfm_config_index = new_nbfm_config_index;
settings_.wfm_config_index = new_wfm_config_index;
settings_.wfmam_config_index = new_wfmam_config_index;
settings_.squelch_level = new_squelch_level;
}
+1
View File
@@ -129,6 +129,7 @@ class ReceiverModel {
size_t new_am_config_index,
size_t new_nbfm_config_index,
size_t new_wfm_config_index,
size_t new_wfmam_config_index,
uint8_t new_squelch_level);
void configure_from_app_settings(const app_settings::AppSettings& settings);
+1 -1
View File
@@ -42,7 +42,7 @@ GeoPos::GeoPos(
const alt_unit altitude_unit,
const spd_unit speed_unit)
: altitude_unit_(altitude_unit), speed_unit_(speed_unit) {
set_parent_rect({pos, {30 * 8, 3 * 16}});
set_parent_rect({pos, {screen_width, 3 * 16}});
add_children({&labels_position,
&label_spd_position,
+2 -2
View File
@@ -61,7 +61,7 @@ class AudioSpectrumView : public View {
' '};
Waveform waveform{
{0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16},
{0, 1 * 16 + cursor_band_height, screen_width, 2 * 16},
audio_spectrum,
128,
0,
@@ -150,7 +150,7 @@ class WaterfallView : public View {
private:
void update_widgets_rect();
const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20};
const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, screen_width, 2 * 16 + 20};
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
static constexpr Dim scale_height = 20;
+1 -1
View File
@@ -137,7 +137,7 @@ TabView::TabView(std::initializer_list<TabDef> tab_definitions) {
size_t tab_width = screen_width / n_tabs;
set_parent_rect({0, 0, 30 * 8, 3 * 8});
set_parent_rect({0, 0, screen_width, 3 * 8});
for (auto& tab_definition : tab_definitions) {
tabs[i].set(i, tab_width, tab_definition.text, tab_definition.color);
+1 -1
View File
@@ -130,7 +130,7 @@ TransmitterView::TransmitterView(
const uint32_t channel_bandwidth,
const bool lock)
: lock_{lock} {
set_parent_rect({0, y, 30 * 8, 6 * 8});
set_parent_rect({0, y, screen_width, 6 * 8});
add_children({
&field_frequency,
+2 -2
View File
@@ -62,7 +62,7 @@ class TimeScopeView : public View {
};*/
Waveform waveform{
{0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16},
{0, 1 * 16 + cursor_band_height, screen_width, 2 * 16},
audio_spectrum,
128,
0,
@@ -117,7 +117,7 @@ class TVWidget : public View {
private:
void update_widgets_rect();
const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20};
const Rect audio_spectrum_view_rect{0 * 8, 0 * 16, screen_width, 2 * 16 + 20};
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
static constexpr Dim scale_height = 20;
@@ -1375,6 +1375,13 @@ static void cmd_setfreq(BaseSequentialStream* chp, int argc, char* argv[]) {
chprintf(chp, "ok\r\n");
}
static void cmd_getres(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
std::string res = to_string_dec_uint(screen_width) + "x" + to_string_dec_uint(screen_height) + "\r\nok\r\n";
chprintf(chp, res.c_str());
}
static const ShellCommand commands[] = {
{"reboot", cmd_reboot},
{"dfu", cmd_dfu},
@@ -1410,6 +1417,7 @@ static const ShellCommand commands[] = {
{"sendpocsag", cmd_sendpocsag},
{"asyncmsg", cmd_asyncmsg},
{"setfreq", cmd_setfreq},
{"getres", cmd_getres},
{NULL, NULL}};
static const ShellConfig shell_cfg1 = {
+13 -13
View File
@@ -1240,11 +1240,11 @@ constexpr fir_taps_real<24> taps_180k_wfm_decim_0 = {
}},
};
// IFIR prototype filter: fs=768000, pass=90000, stop=110000, decim=2, fout=384000
// IFIR prototype filter: fs=768000, pass=90000, stop=170000, decim=2, fout=384000
constexpr fir_taps_real<16> taps_180k_wfm_decim_1 = {
.low_frequency_normalized = -90000.0f / 768000.0f,
.high_frequency_normalized = 90000.0f / 768000.0f,
.transition_normalized = 20000.0f / 768000.0f,
.transition_normalized = 80000.0f / 768000.0f,
.taps = {{
55,
19,
@@ -1265,13 +1265,13 @@ constexpr fir_taps_real<16> taps_180k_wfm_decim_1 = {
}},
};
// WFM 40kHZ filter for NOAA APT reception in 137Mhz band with sharp transition //////////////////////////////////////////////
// WFM 80kHZ filter with sharp transition //////////////////////////////////////////////
// IFIR image-reject filter: fs=3072000, pass=20000, stop=97000, decim=4, fout=768000
constexpr fir_taps_real<24> taps_40k_wfm_decim_0 = {
.low_frequency_normalized = -20000.0f / 3072000.0f,
.high_frequency_normalized = 20000.0f / 3072000.0f,
.transition_normalized = 67000.0f / 3072000.0f,
// IFIR image-reject filter: fs=3072000, pass=97000, stop=300000, decim=4, fout=768000
constexpr fir_taps_real<24> taps_80k_wfm_decim_0 = {
.low_frequency_normalized = -97000.0f / 3072000.0f,
.high_frequency_normalized = 97000.0f / 3072000.0f,
.transition_normalized = 203000.0f / 3072000.0f,
.taps = {{
46,
112,
@@ -1300,11 +1300,11 @@ constexpr fir_taps_real<24> taps_40k_wfm_decim_0 = {
}},
};
// IFIR prototype filter: fs=768000, pass=20000, stop=55000, decim=2, fout=384000
constexpr fir_taps_real<16> taps_40k_wfm_decim_1 = {
.low_frequency_normalized = -20000.0f / 768000.0f,
.high_frequency_normalized = 20000.0f / 768000.0f,
.transition_normalized = 35000.0f / 768000.0f,
// IFIR prototype filter: fs=768000, pass=37500, stop=112500, decim=2, fout=384000
constexpr fir_taps_real<16> taps_80k_wfm_decim_1 = {
.low_frequency_normalized = -37500.0f / 768000.0f,
.high_frequency_normalized = 37500.0f / 768000.0f,
.transition_normalized = 75000.0f / 768000.0f,
.taps = {{
83,
299,
+32
View File
@@ -26,6 +26,38 @@
namespace ui {
// Positioning helpers PER CHARACTERS (8*16)
// EACH parameters must be used az CHAR position, not PX coordinates. So If you wanna use the 8,16 coordinates (that is the second character in X and second character in Y you must use UI_POS_X(1) UI_POS_Y(1) (since we count from 0)
// default font height
#define UI_POS_DEFAULT_HEIGHT 16
// small height
#define UI_POS_DEFAULT_HEIGHT_SMALL 8
// default font width
#define UI_POS_DEFAULT_WIDTH 8
// px position of the linenum-th character (Y)
#define UI_POS_Y(linenum) ((int)((linenum)*UI_POS_DEFAULT_HEIGHT))
// px position of the linenum-th character from the bottom of the screen (Y) (please calculate the +1 line top-bar to it too if that is visible!)
#define UI_POS_Y_BOTTOM(linenum) ((int)(screen_height - (linenum)*UI_POS_DEFAULT_HEIGHT))
// px position of the linenum-th character from the left of the screen (X)
#define UI_POS_X(charnum) ((int)((charnum)*UI_POS_DEFAULT_WIDTH))
// px position of the linenum-th character from the right of the screen (X)
#define UI_POS_X_RIGHT(charnum) ((int)(screen_width - ((charnum)*UI_POS_DEFAULT_WIDTH)))
// px position of the left character from the center of the screen (X) (for N character wide string)
#define UI_POS_X_CENTER(charnum) ((int)((screen_width / 2) - ((charnum)*UI_POS_DEFAULT_WIDTH / 2)))
// px width of N characters
#define UI_POS_WIDTH(charnum) ((int)((charnum)*UI_POS_DEFAULT_WIDTH))
// px width of the screen
#define UI_POS_MAXWIDTH (screen_width)
// px height of N line
#define UI_POS_HEIGHT(linecount) ((int)((linecount)*UI_POS_DEFAULT_HEIGHT))
// px height of the screen's percent
#define UI_POS_HEIGHT_PERCENT(percent) ((int)(screen_height * (percent) / 100))
// remaining px from the linenum-th line to the bottom of the screen. (please calculate the +1 line top-bar to it too if that is visible!)
#define UI_POS_HEIGHT_REMAINING(linenum) ((int)(screen_height - ((linenum)*UI_POS_DEFAULT_HEIGHT)))
// remaining px from the charnum-th character to the right of the screen
#define UI_POS_WIDTH_REMAINING(charnum) ((int)(screen_width - ((charnum)*UI_POS_DEFAULT_WIDTH)))
// Escape sequences for colored text; second character is index into term_colors[]
#define STR_COLOR_BLACK "\x1B\x00"
#define STR_COLOR_DARK_BLUE "\x1B\x01"