Compare commits

...

1 Commits

Author SHA1 Message Date
Andrea Santaniello
730bb318fb 47lecoste's advanced modulation hopper merge 2026-03-13 14:50:24 +01:00
12 changed files with 359 additions and 215 deletions

View File

@@ -35,6 +35,9 @@ SubGhzTxRx* subghz_txrx_alloc(void) {
instance->txrx_state = SubGhzTxRxStateSleep; instance->txrx_state = SubGhzTxRxStateSleep;
subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF); subghz_txrx_hopper_set_state(instance, SubGhzHopperStateOFF);
subghz_txrx_preset_hopper_set_state(instance, SubGhzPresetHopperStateOFF);
instance->preset_hopper_idx = 0;
instance->preset_hopper_timeout = 0;
subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable); subghz_txrx_speaker_set_state(instance, SubGhzSpeakerStateDisable);
subghz_txrx_set_debug_pin_state(instance, false); subghz_txrx_set_debug_pin_state(instance, false);
@@ -495,63 +498,153 @@ void subghz_txrx_hopper_pause(SubGhzTxRx* instance) {
} }
} }
bool subghz_txrx_mod_hopper_get_running(SubGhzTxRx* instance) { void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold) {
furi_assert(instance); furi_assert(instance);
return instance->mod_hopper_running;
}
void subghz_txrx_mod_hopper_set_running( switch(instance->preset_hopper_state) {
SubGhzTxRx* instance, case SubGhzPresetHopperStateOFF:
bool running, case SubGhzPresetHopperStatePause:
uint8_t dwell_ticks,
float rssi_threshold) {
furi_assert(instance);
instance->mod_hopper_running = running;
instance->mod_hopper_dwell = dwell_ticks;
instance->mod_hopper_rssi_threshold = rssi_threshold;
if(running) instance->mod_hopper_timer = dwell_ticks;
}
void subghz_txrx_mod_hopper_update(SubGhzTxRx* instance, float current_rssi) {
furi_assert(instance);
if(!instance->mod_hopper_running) return;
// If RSSI gating is enabled and signal is present, pause hopping
if(!isnan(instance->mod_hopper_rssi_threshold) &&
current_rssi > instance->mod_hopper_rssi_threshold) {
instance->mod_hopper_timer = instance->mod_hopper_dwell;
return; return;
case SubGhzPresetHopperStateRSSITimeOut:
if(instance->preset_hopper_timeout != 0) {
instance->preset_hopper_timeout--;
return;
}
break;
default:
break;
} }
if(instance->mod_hopper_timer > 0) { if(instance->preset_hopper_state != SubGhzPresetHopperStateRSSITimeOut) {
instance->mod_hopper_timer--; float rssi = subghz_devices_get_rssi(instance->radio_device);
return;
if(rssi > stay_threshold) {
instance->preset_hopper_timeout = 20;
instance->preset_hopper_state = SubGhzPresetHopperStateRSSITimeOut;
return;
}
} else {
instance->preset_hopper_state = SubGhzPresetHopperStateRunning;
} }
instance->mod_hopper_timer = instance->mod_hopper_dwell;
size_t count = subghz_setting_get_preset_count(instance->setting); size_t hopper_preset_count = subghz_setting_get_hopper_preset_count(instance->setting);
if(count == 0) return;
// Advance index, skip CUSTOM presets if(hopper_preset_count > 0) {
uint8_t tries = 0; if(instance->preset_hopper_idx < hopper_preset_count - 1) {
do { instance->preset_hopper_idx++;
instance->mod_hopper_idx = (instance->mod_hopper_idx + 1) % count; } else {
tries++; instance->preset_hopper_idx = 0;
} while(tries < count && }
strcmp(
subghz_setting_get_preset_name(instance->setting, instance->mod_hopper_idx),
"CUSTOM") == 0);
const char* preset_name = size_t actual_preset_idx = subghz_setting_get_hopper_preset_index(
subghz_setting_get_preset_name(instance->setting, instance->mod_hopper_idx); instance->setting, instance->preset_hopper_idx);
uint8_t* preset_data =
subghz_setting_get_preset_data(instance->setting, instance->mod_hopper_idx);
size_t preset_data_size =
subghz_setting_get_preset_data_size(instance->setting, instance->mod_hopper_idx);
subghz_txrx_set_preset( if(instance->txrx_state == SubGhzTxRxStateRx) {
instance, preset_name, instance->preset->frequency, preset_data, preset_data_size); subghz_txrx_rx_end(instance);
subghz_txrx_rx_start(instance); }
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
const char* old_preset_name = furi_string_get_cstr(instance->preset->name);
const char* preset_name =
subghz_setting_get_preset_name(instance->setting, actual_preset_idx);
subghz_txrx_set_preset_internal(
instance, instance->preset->frequency, actual_preset_idx, 0);
bool old_is_am = (strstr(old_preset_name, "AM") != NULL);
bool new_is_am = (strstr(preset_name, "AM") != NULL);
bool modulation_changed = (old_is_am != new_is_am);
if(modulation_changed) {
subghz_devices_reset(instance->radio_device);
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
} else {
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
}
subghz_txrx_rx(instance, instance->preset->frequency);
}
} else {
size_t preset_count = subghz_setting_get_preset_count(instance->setting);
if(instance->preset_hopper_idx < preset_count - 1) {
instance->preset_hopper_idx++;
} else {
instance->preset_hopper_idx = 0;
}
if(instance->txrx_state == SubGhzTxRxStateRx) {
subghz_txrx_rx_end(instance);
}
if(instance->txrx_state == SubGhzTxRxStateIDLE) {
const char* old_preset_name = furi_string_get_cstr(instance->preset->name);
const char* preset_name =
subghz_setting_get_preset_name(instance->setting, instance->preset_hopper_idx);
subghz_txrx_set_preset_internal(
instance, instance->preset->frequency, instance->preset_hopper_idx, 0);
bool old_is_am = (strstr(old_preset_name, "AM") != NULL);
bool new_is_am = (strstr(preset_name, "AM") != NULL);
bool modulation_changed = (old_is_am != new_is_am);
if(modulation_changed) {
subghz_devices_reset(instance->radio_device);
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
} else {
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
}
subghz_txrx_rx(instance, instance->preset->frequency);
}
}
}
SubGhzPresetHopperState subghz_txrx_preset_hopper_get_state(SubGhzTxRx* instance) {
furi_assert(instance);
return instance->preset_hopper_state;
}
void subghz_txrx_preset_hopper_set_state(SubGhzTxRx* instance, SubGhzPresetHopperState state) {
furi_assert(instance);
instance->preset_hopper_state = state;
if(state == SubGhzPresetHopperStateRunning) {
subghz_devices_reset(instance->radio_device);
subghz_devices_load_preset(
instance->radio_device,
FuriHalSubGhzPresetCustom,
instance->preset->data);
}
}
void subghz_txrx_preset_hopper_unpause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->preset_hopper_state == SubGhzPresetHopperStatePause) {
instance->preset_hopper_state = SubGhzPresetHopperStateRunning;
}
}
void subghz_txrx_preset_hopper_pause(SubGhzTxRx* instance) {
furi_assert(instance);
if(instance->preset_hopper_state == SubGhzPresetHopperStateRunning) {
instance->preset_hopper_state = SubGhzPresetHopperStatePause;
}
}
void subghz_txrx_preset_hopper_reset_index(SubGhzTxRx* instance, size_t index) {
furi_assert(instance);
instance->preset_hopper_idx = index;
} }
void subghz_txrx_speaker_on(SubGhzTxRx* instance) { void subghz_txrx_speaker_on(SubGhzTxRx* instance) {

View File

@@ -164,37 +164,17 @@ void subghz_txrx_hopper_unpause(SubGhzTxRx* instance);
*/ */
void subghz_txrx_hopper_pause(SubGhzTxRx* instance); void subghz_txrx_hopper_pause(SubGhzTxRx* instance);
/** void subghz_txrx_preset_hopper_update(SubGhzTxRx* instance, float stay_threshold);
* Update modulation (preset) CC1101 in automatic mode (mod hopper)
* Cycles through available presets at the configured dwell time.
* Pauses hopping when current_rssi exceeds the configured threshold.
*
* @param instance Pointer to a SubGhzTxRx
* @param current_rssi Current RSSI reading from the radio
*/
void subghz_txrx_mod_hopper_update(SubGhzTxRx* instance, float current_rssi);
/** SubGhzPresetHopperState subghz_txrx_preset_hopper_get_state(SubGhzTxRx* instance);
* Set mod hopper running state with configuration
*
* @param instance Pointer to a SubGhzTxRx
* @param running true to enable, false to disable
* @param dwell_ticks Ticks to dwell on each modulation (100ms per tick)
* @param rssi_threshold RSSI threshold to pause hopping (NAN = no gating)
*/
void subghz_txrx_mod_hopper_set_running(
SubGhzTxRx* instance,
bool running,
uint8_t dwell_ticks,
float rssi_threshold);
/** void subghz_txrx_preset_hopper_set_state(SubGhzTxRx* instance, SubGhzPresetHopperState state);
* Get mod hopper running state
* void subghz_txrx_preset_hopper_unpause(SubGhzTxRx* instance);
* @param instance Pointer to a SubGhzTxRx
* @return true if mod hopping is active void subghz_txrx_preset_hopper_pause(SubGhzTxRx* instance);
*/
bool subghz_txrx_mod_hopper_get_running(SubGhzTxRx* instance); void subghz_txrx_preset_hopper_reset_index(SubGhzTxRx* instance, size_t index);
/** /**
* Speaker on * Speaker on

View File

@@ -19,11 +19,9 @@ struct SubGhzTxRx {
bool is_database_loaded; bool is_database_loaded;
SubGhzHopperState hopper_state; SubGhzHopperState hopper_state;
uint8_t mod_hopper_idx; // index into setting presets (wraps around) uint8_t preset_hopper_timeout;
uint8_t mod_hopper_timer; // countdown ticks before advancing modulation size_t preset_hopper_idx;
uint8_t mod_hopper_dwell; // stored dwell ticks (configurable) SubGhzPresetHopperState preset_hopper_state;
float mod_hopper_rssi_threshold; // RSSI threshold; NAN = no RSSI gating
bool mod_hopper_running; // is mod hopping active
SubGhzTxRxState txrx_state; SubGhzTxRxState txrx_state;
SubGhzSpeakerState speaker_state; SubGhzSpeakerState speaker_state;

View File

@@ -29,6 +29,14 @@ typedef enum {
SubGhzHopperStateRSSITimeOut, SubGhzHopperStateRSSITimeOut,
} SubGhzHopperState; } SubGhzHopperState;
/** SubGhzPresetHopperState state */
typedef enum {
SubGhzPresetHopperStateOFF,
SubGhzPresetHopperStateRunning,
SubGhzPresetHopperStatePause,
SubGhzPresetHopperStateRSSITimeOut,
} SubGhzPresetHopperState;
/** SubGhzSpeakerState state */ /** SubGhzSpeakerState state */
typedef enum { typedef enum {
SubGhzSpeakerStateDisable, SubGhzSpeakerStateDisable,

View File

@@ -40,3 +40,8 @@ Version: 1
#Custom_preset_name: AM_2 #Custom_preset_name: AM_2
#Custom_preset_module: CC1101 #Custom_preset_module: CC1101
#Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 #Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00
# Presets used for preset hopping mode (cycles through these modulations)
#Hopping_Preset: AM650
#Hopping_Preset: FM238
#Hopping_Preset: FM476

View File

@@ -1,5 +1,4 @@
#include "../subghz_i.h" #include "../subghz_i.h"
#include <math.h>
#include <dolphin/dolphin.h> #include <dolphin/dolphin.h>
#include <lib/subghz/protocols/bin_raw.h> #include <lib/subghz/protocols/bin_raw.h>
@@ -214,11 +213,12 @@ void subghz_scene_receiver_on_enter(void* context) {
} else { } else {
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
} }
subghz_txrx_mod_hopper_set_running(
subghz->txrx, if(subghz->last_settings->enable_preset_hopping) {
!isnan(subghz->last_settings->mod_hopping_threshold), subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateRunning);
(uint8_t)subghz->last_settings->mod_hopping_dwell, } else {
subghz->last_settings->mod_hopping_threshold); subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateOFF);
}
subghz_txrx_rx_start(subghz->txrx); subghz_txrx_rx_start(subghz->txrx);
subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen); subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen);
@@ -247,6 +247,7 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
subghz->state_notifications = SubGhzNotificationStateIDLE; subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz_txrx_stop(subghz->txrx); subghz_txrx_stop(subghz->txrx);
subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF); subghz_txrx_hopper_set_state(subghz->txrx, SubGhzHopperStateOFF);
subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateOFF);
subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz); subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz);
if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) { if(subghz_rx_key_state_get(subghz) == SubGhzRxKeyStateAddKey) {
@@ -307,9 +308,8 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
subghz_txrx_hopper_update(subghz->txrx, subghz->last_settings->hopping_threshold); subghz_txrx_hopper_update(subghz->txrx, subghz->last_settings->hopping_threshold);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }
if(subghz_txrx_mod_hopper_get_running(subghz->txrx)) { if(subghz_txrx_preset_hopper_get_state(subghz->txrx) != SubGhzPresetHopperStateOFF) {
float rssi = subghz_txrx_radio_device_get_rssi(subghz->txrx); subghz_txrx_preset_hopper_update(subghz->txrx, subghz->last_settings->preset_hopping_threshold);
subghz_txrx_mod_hopper_update(subghz->txrx, rssi);
subghz_scene_receiver_update_statusbar(subghz); subghz_scene_receiver_update_statusbar(subghz);
} }

View File

@@ -6,9 +6,9 @@
enum SubGhzSettingIndex { enum SubGhzSettingIndex {
SubGhzSettingIndexFrequency, SubGhzSettingIndexFrequency,
SubGhzSettingIndexHopping,
SubGhzSettingIndexModulation, SubGhzSettingIndexModulation,
SubGhzSettingIndexModHopping, SubGhzSettingIndexHopping,
SubGhzSettingIndexPresetHopping,
SubGhzSettingIndexBinRAW, SubGhzSettingIndexBinRAW,
SubGhzSettingIndexIgnoreReversRB2, SubGhzSettingIndexIgnoreReversRB2,
SubGhzSettingIndexIgnoreAlarms, SubGhzSettingIndexIgnoreAlarms,
@@ -82,18 +82,22 @@ const float hopping_mode_value[HOPPING_MODE_COUNT] = {
-40.0f, -40.0f,
}; };
#define MOD_HOP_DBM_COUNT 8 #define PRESET_HOPPING_MODE_COUNT 12
const char* const mod_hop_dbm_text[MOD_HOP_DBM_COUNT] = { const char* const preset_hopping_mode_text[PRESET_HOPPING_MODE_COUNT] = {
"OFF", "OFF",
"-90", "-90dBm",
"-85", "-85dBm",
"-80", "-80dBm",
"-75", "-75dBm",
"-70", "-70dBm",
"-65", "-65dBm",
"-60", "-60dBm",
"-55dBm",
"-50dBm",
"-45dBm",
"-40dBm",
}; };
const float mod_hop_dbm_value[MOD_HOP_DBM_COUNT] = { const float preset_hopping_mode_value[PRESET_HOPPING_MODE_COUNT] = {
NAN, NAN,
-90.0f, -90.0f,
-85.0f, -85.0f,
@@ -102,22 +106,10 @@ const float mod_hop_dbm_value[MOD_HOP_DBM_COUNT] = {
-70.0f, -70.0f,
-65.0f, -65.0f,
-60.0f, -60.0f,
}; -55.0f,
-50.0f,
#define MOD_HOP_TIME_COUNT 5 -45.0f,
const char* const mod_hop_time_text[MOD_HOP_TIME_COUNT] = { -40.0f,
"0.5s",
"1s",
"2s",
"5s",
"10s",
};
const uint32_t mod_hop_time_ticks[MOD_HOP_TIME_COUNT] = {
5,
10,
20,
50,
100,
}; };
#define COMBO_BOX_COUNT 2 #define COMBO_BOX_COUNT 2
@@ -192,6 +184,23 @@ uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void*
return index; return index;
} }
uint8_t subghz_scene_receiver_config_preset_hopper_value_index(void* context) {
furi_assert(context);
SubGhz* subghz = context;
if(subghz_txrx_preset_hopper_get_state(subghz->txrx) == SubGhzPresetHopperStateOFF) {
return 0;
} else {
variable_item_set_current_value_text(
variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexModulation),
" -----");
return value_index_float(
subghz->last_settings->preset_hopping_threshold,
preset_hopping_mode_value,
PRESET_HOPPING_MODE_COUNT);
}
}
uint8_t subghz_scene_receiver_config_hopper_value_index(void* context) { uint8_t subghz_scene_receiver_config_hopper_value_index(void* context) {
furi_assert(context); furi_assert(context);
SubGhz* subghz = context; SubGhz* subghz = context;
@@ -252,19 +261,21 @@ static void subghz_scene_receiver_config_set_preset(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx); SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
const char* preset_name = subghz_setting_get_preset_name(setting, index); if(subghz_txrx_preset_hopper_get_state(subghz->txrx) == SubGhzPresetHopperStateOFF) {
variable_item_set_current_value_text(item, preset_name); const char* preset_name = subghz_setting_get_preset_name(setting, index);
//subghz->last_settings->preset = index; variable_item_set_current_value_text(item, preset_name);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx); SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
uint8_t* preset_data = subghz_setting_get_preset_data(setting, index); uint8_t* preset_data = subghz_setting_get_preset_data(setting, index);
size_t preset_data_size = subghz_setting_get_preset_data_size(setting, index); size_t preset_data_size = subghz_setting_get_preset_data_size(setting, index);
//Edit TX power, if necessary. subghz_txrx_set_tx_power(preset_data, preset_data_size, subghz->tx_power);
subghz_txrx_set_tx_power(preset_data, preset_data_size, subghz->tx_power);
subghz_txrx_set_preset( subghz_txrx_set_preset(
subghz->txrx, preset_name, preset.frequency, preset_data, preset_data_size); subghz->txrx, preset_name, preset.frequency, preset_data, preset_data_size);
subghz->last_settings->preset_index = index; subghz->last_settings->preset_index = index;
} else {
variable_item_set_current_value_index(item, subghz->last_settings->preset_index);
}
} }
static void subghz_scene_receiver_config_set_hopping(VariableItem* item) { static void subghz_scene_receiver_config_set_hopping(VariableItem* item) {
@@ -313,33 +324,57 @@ static void subghz_scene_receiver_config_set_hopping(VariableItem* item) {
subghz->last_settings->hopping_threshold = hopping_mode_value[index]; subghz->last_settings->hopping_threshold = hopping_mode_value[index];
subghz_txrx_hopper_set_state( subghz_txrx_hopper_set_state(
subghz->txrx, index != 0 ? SubGhzHopperStateRunning : SubGhzHopperStateOFF); subghz->txrx, index != 0 ? SubGhzHopperStateRunning : SubGhzHopperStateOFF);
VariableItem* preset_hopping_item =
variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexPresetHopping);
variable_item_set_locked(
preset_hopping_item,
index != 0,
"Turn off\nHopping\nfirst!");
} }
static void subghz_scene_receiver_config_set_mod_hop_dbm(VariableItem* item) { static void subghz_scene_receiver_config_set_preset_hopping(VariableItem* item) {
SubGhz* subghz = variable_item_get_context(item); SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, mod_hop_dbm_text[index]); SubGhzSetting* setting = subghz_txrx_get_setting(subghz->txrx);
subghz->last_settings->mod_hopping_threshold = mod_hop_dbm_value[index]; VariableItem* preset_item =
bool enabled = index != 0; variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexModulation);
subghz_txrx_mod_hopper_set_running(
subghz->txrx,
enabled,
(uint8_t)subghz->last_settings->mod_hopping_dwell,
mod_hop_dbm_value[index]);
}
static void subghz_scene_receiver_config_set_mod_hop_time(VariableItem* item) { variable_item_set_current_value_text(item, preset_hopping_mode_text[index]);
SubGhz* subghz = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item); if(index == 0) {
variable_item_set_current_value_text(item, mod_hop_time_text[index]); SubGhzRadioPreset current_preset = subghz_txrx_get_preset(subghz->txrx);
subghz->last_settings->mod_hopping_dwell = mod_hop_time_ticks[index]; const char* current_preset_name = furi_string_get_cstr(current_preset.name);
if(subghz_txrx_mod_hopper_get_running(subghz->txrx)) { int current_preset_index = subghz_setting_get_inx_preset_by_name(setting, current_preset_name);
subghz_txrx_mod_hopper_set_running( if(current_preset_index >= 0) {
subghz->txrx, subghz->last_settings->preset_index = current_preset_index;
true, }
(uint8_t)mod_hop_time_ticks[index], variable_item_set_current_value_text(preset_item, current_preset_name);
subghz->last_settings->mod_hopping_threshold); variable_item_set_current_value_index(preset_item, subghz->last_settings->preset_index);
subghz->last_settings->enable_preset_hopping = false;
subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateOFF);
} else {
bool was_running = (subghz_txrx_preset_hopper_get_state(subghz->txrx) == SubGhzPresetHopperStateRunning);
if(was_running) {
subghz_txrx_preset_hopper_pause(subghz->txrx);
}
subghz->last_settings->preset_hopping_threshold = preset_hopping_mode_value[index];
variable_item_set_current_value_text(preset_item, " -----");
variable_item_set_current_value_index(preset_item, subghz->last_settings->preset_index);
subghz->last_settings->enable_preset_hopping = true;
subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateRunning);
} }
VariableItem* hopping_item =
variable_item_list_get(subghz->variable_item_list, SubGhzSettingIndexHopping);
variable_item_set_locked(
hopping_item,
index != 0,
"Turn off\nPreset\nHopping\nfirst!");
} }
static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { static void subghz_scene_receiver_config_set_speaker(VariableItem* item) {
@@ -442,9 +477,9 @@ static void subghz_scene_receiver_config_var_list_enter_callback(void* context,
subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]); subghz_txrx_hopper_set_state(subghz->txrx, hopping_value[default_index]);
subghz->last_settings->enable_hopping = hopping_value[default_index]; subghz->last_settings->enable_hopping = hopping_value[default_index];
subghz->last_settings->mod_hopping_threshold = NAN; subghz->last_settings->enable_preset_hopping = false;
subghz->last_settings->mod_hopping_dwell = 20; subghz->last_settings->preset_hopping_threshold = SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD;
subghz_txrx_mod_hopper_set_running(subghz->txrx, false, 20, NAN); subghz_txrx_preset_hopper_set_state(subghz->txrx, SubGhzPresetHopperStateOFF);
variable_item_list_set_selected_item(subghz->variable_item_list, default_index); variable_item_list_set_selected_item(subghz->variable_item_list, default_index);
variable_item_list_reset(subghz->variable_item_list); variable_item_list_reset(subghz->variable_item_list);
@@ -509,47 +544,26 @@ void subghz_scene_receiver_config_on_enter(void* context) {
variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, hopping_mode_text[value_index]); variable_item_set_current_value_text(item, hopping_mode_text[value_index]);
// Mod Hop dBm variable_item_set_locked(
{ item,
uint8_t mod_dbm_idx = 0; // OFF subghz_txrx_preset_hopper_get_state(subghz->txrx) != SubGhzPresetHopperStateOFF,
float thresh = subghz->last_settings->mod_hopping_threshold; "Turn off\nPreset\nHopping\nfirst!");
if(!isnan(thresh)) {
for(uint8_t k = 1; k < MOD_HOP_DBM_COUNT; k++) {
if(mod_hop_dbm_value[k] == thresh) {
mod_dbm_idx = k;
break;
}
}
if(mod_dbm_idx == 0) mod_dbm_idx = 1; // fallback to -90
}
item = variable_item_list_add(
subghz->variable_item_list,
"Mod Hop dBm",
MOD_HOP_DBM_COUNT,
subghz_scene_receiver_config_set_mod_hop_dbm,
subghz);
variable_item_set_current_value_index(item, mod_dbm_idx);
variable_item_set_current_value_text(item, mod_hop_dbm_text[mod_dbm_idx]);
}
// Mod Hop Time value_index = subghz_scene_receiver_config_preset_hopper_value_index(subghz);
{ item = variable_item_list_add(
uint8_t mod_time_idx = 2; // default 2s subghz->variable_item_list,
for(uint8_t k = 0; k < MOD_HOP_TIME_COUNT; k++) { "Preset Hopping",
if(mod_hop_time_ticks[k] == subghz->last_settings->mod_hopping_dwell) { PRESET_HOPPING_MODE_COUNT,
mod_time_idx = k; subghz_scene_receiver_config_set_preset_hopping,
break; subghz);
}
} variable_item_set_current_value_index(item, value_index);
item = variable_item_list_add( variable_item_set_current_value_text(item, preset_hopping_mode_text[value_index]);
subghz->variable_item_list,
"Mod Hop Time", variable_item_set_locked(
MOD_HOP_TIME_COUNT, item,
subghz_scene_receiver_config_set_mod_hop_time, subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
subghz); "Turn off\nHopping\nfirst!");
variable_item_set_current_value_index(item, mod_time_idx);
variable_item_set_current_value_text(item, mod_hop_time_text[mod_time_idx]);
}
} }
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) !=

View File

@@ -1,6 +1,5 @@
#include "subghz_last_settings.h" #include "subghz_last_settings.h"
#include "subghz_i.h" #include "subghz_i.h"
#include <math.h>
#define TAG "SubGhzLastSettings" #define TAG "SubGhzLastSettings"
@@ -14,8 +13,8 @@
#define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger" #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER "FATrigger"
#define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES "ProtocolNames" #define SUBGHZ_LAST_SETTING_FIELD_PROTOCOL_FILE_NAMES "ProtocolNames"
#define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping" #define SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE "Hopping"
#define SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_THRESHOLD "ModHopThreshold" #define SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING "PresetHopping"
#define SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_DWELL "ModHopDwell" #define SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING_THRESHOLD "PresetHoppingThreshold"
#define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter" #define SUBGHZ_LAST_SETTING_FIELD_IGNORE_FILTER "IgnoreFilter"
#define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter" #define SUBGHZ_LAST_SETTING_FIELD_FILTER "Filter"
#define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI" #define SUBGHZ_LAST_SETTING_FIELD_RSSI_THRESHOLD "RSSI"
@@ -48,8 +47,8 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
instance->filter = SubGhzProtocolFlag_Decodable; instance->filter = SubGhzProtocolFlag_Decodable;
instance->rssi = SUBGHZ_RAW_THRESHOLD_MIN; instance->rssi = SUBGHZ_RAW_THRESHOLD_MIN;
instance->hopping_threshold = -90.0f; instance->hopping_threshold = -90.0f;
instance->mod_hopping_threshold = NAN; // disabled by default instance->enable_preset_hopping = false;
instance->mod_hopping_dwell = 20; // 2 seconds (20 × 100ms ticks) instance->preset_hopping_threshold = SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD;
instance->leds_and_amp = true; instance->leds_and_amp = true;
Storage* storage = furi_record_open(RECORD_STORAGE); Storage* storage = furi_record_open(RECORD_STORAGE);
@@ -103,19 +102,24 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count
1)) { 1)) {
flipper_format_rewind(fff_data_file); flipper_format_rewind(fff_data_file);
} }
if(!flipper_format_read_float( if(!flipper_format_read_bool(
fff_data_file, fff_data_file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_THRESHOLD, SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING,
&instance->mod_hopping_threshold, &instance->enable_preset_hopping,
1)) { 1)) {
instance->enable_preset_hopping = false;
flipper_format_rewind(fff_data_file); flipper_format_rewind(fff_data_file);
} }
if(!flipper_format_read_uint32( float temp_preset_threshold = 0;
if(!flipper_format_read_float(
fff_data_file, fff_data_file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_DWELL, SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING_THRESHOLD,
&instance->mod_hopping_dwell, &temp_preset_threshold,
1)) { 1)) {
instance->preset_hopping_threshold = SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD;
flipper_format_rewind(fff_data_file); flipper_format_rewind(fff_data_file);
} else {
instance->preset_hopping_threshold = temp_preset_threshold;
} }
if(!flipper_format_read_uint32( if(!flipper_format_read_uint32(
fff_data_file, fff_data_file,
@@ -232,17 +236,17 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) {
file, SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE, &instance->enable_hopping, 1)) { file, SUBGHZ_LAST_SETTING_FIELD_HOPPING_ENABLE, &instance->enable_hopping, 1)) {
break; break;
} }
if(!flipper_format_write_float( if(!flipper_format_write_bool(
file, file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_THRESHOLD, SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING,
&instance->mod_hopping_threshold, &instance->enable_preset_hopping,
1)) { 1)) {
break; break;
} }
if(!flipper_format_write_uint32( if(!flipper_format_write_float(
file, file,
SUBGHZ_LAST_SETTING_FIELD_MOD_HOP_DWELL, SUBGHZ_LAST_SETTING_FIELD_PRESET_HOPPING_THRESHOLD,
&instance->mod_hopping_dwell, &instance->preset_hopping_threshold,
1)) { 1)) {
break; break;
} }

View File

@@ -12,21 +12,22 @@
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1 #define SUBGHZ_LAST_SETTING_DEFAULT_PRESET 1
#define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000 #define SUBGHZ_LAST_SETTING_DEFAULT_FREQUENCY 433920000
#define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2 #define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2
#define SUBGHZ_LAST_SETTING_DEFAULT_PRESET_HOPPING_THRESHOLD (-80.0f)
typedef struct { typedef struct {
uint32_t frequency; uint32_t frequency;
uint32_t preset_index; // AKA Modulation uint32_t preset_index;
uint32_t frequency_analyzer_feedback_level; uint32_t frequency_analyzer_feedback_level;
float frequency_analyzer_trigger; float frequency_analyzer_trigger;
bool protocol_file_names; bool protocol_file_names;
bool enable_hopping; bool enable_hopping;
float mod_hopping_threshold; // RSSI threshold for mod hopping, NAN = disabled
uint32_t mod_hopping_dwell; // Dwell time in ticks (100ms units)
uint32_t ignore_filter; uint32_t ignore_filter;
uint32_t filter; uint32_t filter;
float rssi; float rssi;
bool delete_old_signals; bool delete_old_signals;
float hopping_threshold; float hopping_threshold;
bool enable_preset_hopping;
float preset_hopping_threshold;
bool leds_and_amp; bool leds_and_amp;
uint8_t tx_power; uint8_t tx_power;
} SubGhzLastSettings; } SubGhzLastSettings;

View File

@@ -117,6 +117,7 @@ typedef struct {
struct SubGhzSetting { struct SubGhzSetting {
FrequencyList_t frequencies; FrequencyList_t frequencies;
FrequencyList_t hopper_frequencies; FrequencyList_t hopper_frequencies;
PresetIndexList_t hopper_preset_indices;
SubGhzSettingCustomPresetStruct* preset; SubGhzSettingCustomPresetStruct* preset;
}; };
@@ -124,6 +125,7 @@ SubGhzSetting* subghz_setting_alloc(void) {
SubGhzSetting* instance = malloc(sizeof(SubGhzSetting)); SubGhzSetting* instance = malloc(sizeof(SubGhzSetting));
FrequencyList_init(instance->frequencies); FrequencyList_init(instance->frequencies);
FrequencyList_init(instance->hopper_frequencies); FrequencyList_init(instance->hopper_frequencies);
PresetIndexList_init(instance->hopper_preset_indices);
instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct)); instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct));
SubGhzSettingCustomPresetItemArray_init(instance->preset->data); SubGhzSettingCustomPresetItemArray_init(instance->preset->data);
return instance; return instance;
@@ -142,6 +144,7 @@ void subghz_setting_free(SubGhzSetting* instance) {
furi_check(instance); furi_check(instance);
FrequencyList_clear(instance->frequencies); FrequencyList_clear(instance->frequencies);
FrequencyList_clear(instance->hopper_frequencies); FrequencyList_clear(instance->hopper_frequencies);
PresetIndexList_clear(instance->hopper_preset_indices);
for for
M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) {
furi_string_free(item->custom_preset_name); furi_string_free(item->custom_preset_name);
@@ -183,6 +186,7 @@ static void subghz_setting_load_default_region(
FrequencyList_reset(instance->frequencies); FrequencyList_reset(instance->frequencies);
FrequencyList_reset(instance->hopper_frequencies); FrequencyList_reset(instance->hopper_frequencies);
PresetIndexList_reset(instance->hopper_preset_indices);
subghz_setting_preset_reset(instance); subghz_setting_preset_reset(instance);
while(*frequencies) { while(*frequencies) {
@@ -308,6 +312,22 @@ void subghz_setting_load(SubGhzSetting* instance, const char* file_path) {
instance, furi_string_get_cstr(temp_str), fff_data_file); instance, furi_string_get_cstr(temp_str), fff_data_file);
} }
if(!flipper_format_rewind(fff_data_file)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
furi_string_reset(temp_str);
while(flipper_format_read_string(fff_data_file, "Hopping_Preset", temp_str)) {
const char* preset_name = furi_string_get_cstr(temp_str);
int preset_index = subghz_setting_get_inx_preset_by_name(instance, preset_name);
if(preset_index >= 0) {
PresetIndexList_push_back(instance->hopper_preset_indices, (size_t)preset_index);
FURI_LOG_I(TAG, "Hopper preset loaded: %s (index %d)", preset_name, preset_index);
} else {
FURI_LOG_E(TAG, "Hopper preset not found: %s", preset_name);
}
}
} while(false); } while(false);
} }
@@ -347,6 +367,19 @@ size_t subghz_setting_get_preset_count(SubGhzSetting* instance) {
return SubGhzSettingCustomPresetItemArray_size(instance->preset->data); return SubGhzSettingCustomPresetItemArray_size(instance->preset->data);
} }
size_t subghz_setting_get_hopper_preset_count(SubGhzSetting* instance) {
furi_check(instance);
return PresetIndexList_size(instance->hopper_preset_indices);
}
size_t subghz_setting_get_hopper_preset_index(SubGhzSetting* instance, size_t idx) {
furi_check(instance);
if(idx < PresetIndexList_size(instance->hopper_preset_indices)) {
return *PresetIndexList_get(instance->hopper_preset_indices, idx);
}
return 0;
}
const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) { const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) {
furi_check(instance); furi_check(instance);
if(idx >= SubGhzSettingCustomPresetItemArray_size(instance->preset->data)) { if(idx >= SubGhzSettingCustomPresetItemArray_size(instance->preset->data)) {
@@ -367,7 +400,6 @@ int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* p
} }
idx++; idx++;
} }
furi_crash("SubGhz: No name preset.");
return -1; return -1;
} }

View File

@@ -3,6 +3,7 @@
#include <math.h> #include <math.h>
#include <furi.h> #include <furi.h>
#include <m-list.h>
#include <furi_hal.h> #include <furi_hal.h>
#include <lib/flipper_format/flipper_format.h> #include <lib/flipper_format/flipper_format.h>
@@ -12,6 +13,9 @@ extern "C" {
#define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 #define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4
LIST_DEF(PresetIndexList, size_t)
#define M_OPL_PresetIndexList_t() LIST_OPLIST(PresetIndexList)
typedef struct SubGhzSetting SubGhzSetting; typedef struct SubGhzSetting SubGhzSetting;
SubGhzSetting* subghz_setting_alloc(void); SubGhzSetting* subghz_setting_alloc(void);
@@ -55,6 +59,9 @@ void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t freq
uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance); uint8_t subghz_setting_customs_presets_to_log(SubGhzSetting* instance);
size_t subghz_setting_get_hopper_preset_count(SubGhzSetting* instance);
size_t subghz_setting_get_hopper_preset_index(SubGhzSetting* instance, size_t idx);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params entry,status,name,type,params
Version,+,87.9,, Version,+,87.10,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/applications.h,, Header,+,applications/services/applications.h,,
Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt.h,,
@@ -3692,6 +3692,8 @@ Function,+,subghz_setting_get_frequency_count,size_t,SubGhzSetting*
Function,+,subghz_setting_get_frequency_default_index,uint32_t,SubGhzSetting* Function,+,subghz_setting_get_frequency_default_index,uint32_t,SubGhzSetting*
Function,+,subghz_setting_get_hopper_frequency,uint32_t,"SubGhzSetting*, size_t" Function,+,subghz_setting_get_hopper_frequency,uint32_t,"SubGhzSetting*, size_t"
Function,+,subghz_setting_get_hopper_frequency_count,size_t,SubGhzSetting* Function,+,subghz_setting_get_hopper_frequency_count,size_t,SubGhzSetting*
Function,+,subghz_setting_get_hopper_preset_count,size_t,SubGhzSetting*
Function,+,subghz_setting_get_hopper_preset_index,size_t,"SubGhzSetting*, size_t"
Function,+,subghz_setting_get_inx_preset_by_name,int,"SubGhzSetting*, const char*" Function,+,subghz_setting_get_inx_preset_by_name,int,"SubGhzSetting*, const char*"
Function,+,subghz_setting_get_preset_count,size_t,SubGhzSetting* Function,+,subghz_setting_get_preset_count,size_t,SubGhzSetting*
Function,+,subghz_setting_get_preset_data,uint8_t*,"SubGhzSetting*, size_t" Function,+,subghz_setting_get_preset_data,uint8_t*,"SubGhzSetting*, size_t"
1 entry status name type params
2 Version + 87.9 87.10
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/applications.h
5 Header + applications/services/bt/bt_service/bt.h
3692 Function + subghz_setting_get_frequency_default_index uint32_t SubGhzSetting*
3693 Function + subghz_setting_get_hopper_frequency uint32_t SubGhzSetting*, size_t
3694 Function + subghz_setting_get_hopper_frequency_count size_t SubGhzSetting*
3695 Function + subghz_setting_get_hopper_preset_count size_t SubGhzSetting*
3696 Function + subghz_setting_get_hopper_preset_index size_t SubGhzSetting*, size_t
3697 Function + subghz_setting_get_inx_preset_by_name int SubGhzSetting*, const char*
3698 Function + subghz_setting_get_preset_count size_t SubGhzSetting*
3699 Function + subghz_setting_get_preset_data uint8_t* SubGhzSetting*, size_t