unleashed-firmware/lib/subghz/subghz_setting.c
MX db54c463e6
Fix hopper stuck at 433.42 due to wide range tx'es
When we using 433.92 remote flipper in hopping mode will stuck at 433.42 and may loose signal because of that, need to avoid using close freqs in hopping, only freqs with bigger difference like 310 -> 315
2023-03-21 17:52:21 +03:00

479 lines
16 KiB
C

#include "subghz_setting.h"
#include "types.h"
//#include "subghz_i.h"
#include <furi.h>
#include <m-list.h>
#include <furi_hal_subghz_configs.h>
#define TAG "SubGhzSetting"
#define SUBGHZ_SETTING_FILE_TYPE "Flipper SubGhz Setting File"
#define SUBGHZ_SETTING_FILE_VERSION 1
#define FREQUENCY_FLAG_DEFAULT (1 << 31)
#define FREQUENCY_MASK (0xFFFFFFFF ^ FREQUENCY_FLAG_DEFAULT)
/* Default */
static const uint32_t subghz_frequency_list[] = {
/* 300 - 348 */
300000000,
302757000,
303875000,
304250000,
307000000,
307500000,
307800000,
309000000,
310000000,
312000000,
312100000,
312200000,
313000000,
313850000,
314000000,
314350000,
314980000,
315000000,
318000000,
330000000,
345000000,
348000000,
350000000,
/* 387 - 464 */
387000000,
390000000,
418000000,
433075000, /* LPD433 first */
433220000,
433420000,
433657070,
433889000,
433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */
434075000,
434176948,
434190000,
434390000,
434420000,
434620000,
434775000, /* LPD433 last channels */
438900000,
440175000,
464000000,
467750000,
/* 779 - 928 */
779000000,
868350000,
868400000,
868800000,
868950000,
906400000,
915000000,
925000000,
928000000,
0,
};
static const uint32_t subghz_hopper_frequency_list[] = {
310000000,
315000000,
433920000,
868350000,
0,
};
typedef struct {
FuriString* custom_preset_name;
uint8_t* custom_preset_data;
size_t custom_preset_data_size;
} SubGhzSettingCustomPresetItem;
ARRAY_DEF(SubGhzSettingCustomPresetItemArray, SubGhzSettingCustomPresetItem, M_POD_OPLIST)
#define M_OPL_SubGhzSettingCustomPresetItemArray_t() \
ARRAY_OPLIST(SubGhzSettingCustomPresetItemArray, M_POD_OPLIST)
LIST_DEF(FrequencyList, uint32_t)
#define M_OPL_FrequencyList_t() LIST_OPLIST(FrequencyList)
typedef struct {
SubGhzSettingCustomPresetItemArray_t data;
} SubGhzSettingCustomPresetStruct;
struct SubGhzSetting {
FrequencyList_t frequencies;
FrequencyList_t hopper_frequencies;
SubGhzSettingCustomPresetStruct* preset;
};
SubGhzSetting* subghz_setting_alloc(void) {
SubGhzSetting* instance = malloc(sizeof(SubGhzSetting));
FrequencyList_init(instance->frequencies);
FrequencyList_init(instance->hopper_frequencies);
instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct));
SubGhzSettingCustomPresetItemArray_init(instance->preset->data);
return instance;
}
static void subghz_setting_preset_reset(SubGhzSetting* instance) {
for
M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) {
furi_string_free(item->custom_preset_name);
free(item->custom_preset_data);
}
SubGhzSettingCustomPresetItemArray_reset(instance->preset->data);
}
void subghz_setting_free(SubGhzSetting* instance) {
furi_assert(instance);
FrequencyList_clear(instance->frequencies);
FrequencyList_clear(instance->hopper_frequencies);
for
M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) {
furi_string_free(item->custom_preset_name);
free(item->custom_preset_data);
}
SubGhzSettingCustomPresetItemArray_clear(instance->preset->data);
free(instance->preset);
free(instance);
}
static void subghz_setting_load_default_preset(
SubGhzSetting* instance,
const char* preset_name,
const uint8_t* preset_data,
const uint8_t preset_pa_table[8]) {
furi_assert(instance);
furi_assert(preset_data);
uint32_t preset_data_count = 0;
SubGhzSettingCustomPresetItem* item =
SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data);
item->custom_preset_name = furi_string_alloc();
furi_string_set(item->custom_preset_name, preset_name);
while(preset_data[preset_data_count]) {
preset_data_count += 2;
}
preset_data_count += 2;
item->custom_preset_data_size = sizeof(uint8_t) * preset_data_count + sizeof(uint8_t) * 8;
item->custom_preset_data = malloc(item->custom_preset_data_size);
//load preset register
memcpy(&item->custom_preset_data[0], &preset_data[0], preset_data_count);
//load pa table
memcpy(&item->custom_preset_data[preset_data_count], &preset_pa_table[0], 8);
}
static void subghz_setting_load_default_region(
SubGhzSetting* instance,
const uint32_t frequencies[],
const uint32_t hopper_frequencies[]) {
furi_assert(instance);
FrequencyList_reset(instance->frequencies);
FrequencyList_reset(instance->hopper_frequencies);
subghz_setting_preset_reset(instance);
while(*frequencies) {
FrequencyList_push_back(instance->frequencies, *frequencies);
frequencies++;
}
while(*hopper_frequencies) {
FrequencyList_push_back(instance->hopper_frequencies, *hopper_frequencies);
hopper_frequencies++;
}
subghz_setting_load_default_preset(
instance,
"AM270",
(uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs,
furi_hal_subghz_preset_ook_async_patable);
subghz_setting_load_default_preset(
instance,
"AM650",
(uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs,
furi_hal_subghz_preset_ook_async_patable);
subghz_setting_load_default_preset(
instance,
"FM238",
(uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs,
furi_hal_subghz_preset_2fsk_async_patable);
subghz_setting_load_default_preset(
instance,
"FM476",
(uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs,
furi_hal_subghz_preset_2fsk_async_patable);
}
// Region check removed
void subghz_setting_load_default(SubGhzSetting* instance) {
subghz_setting_load_default_region(
instance, subghz_frequency_list, subghz_hopper_frequency_list);
}
void subghz_setting_load(SubGhzSetting* instance, const char* file_path) {
furi_assert(instance);
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
FuriString* temp_str;
temp_str = furi_string_alloc();
uint32_t temp_data32;
bool temp_bool;
subghz_setting_load_default(instance);
if(file_path) {
do {
if(!flipper_format_file_open_existing(fff_data_file, file_path)) {
FURI_LOG_I(TAG, "File is not used %s", file_path);
break;
}
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
FURI_LOG_E(TAG, "Missing or incorrect header");
break;
}
if((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_SETTING_FILE_TYPE)) &&
temp_data32 == SUBGHZ_SETTING_FILE_VERSION) {
} else {
FURI_LOG_E(TAG, "Type or version mismatch");
break;
}
// Standard frequencies (optional)
temp_bool = true;
flipper_format_read_bool(fff_data_file, "Add_standard_frequencies", &temp_bool, 1);
if(!temp_bool) {
FURI_LOG_I(TAG, "Removing standard frequencies");
FrequencyList_reset(instance->frequencies);
FrequencyList_reset(instance->hopper_frequencies);
} else {
FURI_LOG_I(TAG, "Keeping standard frequencies");
}
// Load frequencies
if(!flipper_format_rewind(fff_data_file)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
while(flipper_format_read_uint32(
fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) {
if(furi_hal_subghz_is_frequency_valid(temp_data32)) {
FURI_LOG_I(TAG, "Frequency loaded %lu", temp_data32);
FrequencyList_push_back(instance->frequencies, temp_data32);
} else {
FURI_LOG_E(TAG, "Frequency not supported %lu", temp_data32);
}
}
// Load hopper frequencies
if(!flipper_format_rewind(fff_data_file)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
while(flipper_format_read_uint32(
fff_data_file, "Hopper_frequency", (uint32_t*)&temp_data32, 1)) {
if(furi_hal_subghz_is_frequency_valid(temp_data32)) {
FURI_LOG_I(TAG, "Hopper frequency loaded %lu", temp_data32);
FrequencyList_push_back(instance->hopper_frequencies, temp_data32);
} else {
FURI_LOG_E(TAG, "Hopper frequency not supported %lu", temp_data32);
}
}
// Default frequency (optional)
if(!flipper_format_rewind(fff_data_file)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
if(flipper_format_read_uint32(fff_data_file, "Default_frequency", &temp_data32, 1)) {
subghz_setting_set_default_frequency(instance, temp_data32);
}
// custom preset (optional)
if(!flipper_format_rewind(fff_data_file)) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
while(flipper_format_read_string(fff_data_file, "Custom_preset_name", temp_str)) {
FURI_LOG_I(TAG, "Custom preset loaded %s", furi_string_get_cstr(temp_str));
subghz_setting_load_custom_preset(
instance, furi_string_get_cstr(temp_str), fff_data_file);
}
} while(false);
}
furi_string_free(temp_str);
flipper_format_free(fff_data_file);
furi_record_close(RECORD_STORAGE);
if(!FrequencyList_size(instance->frequencies) ||
!FrequencyList_size(instance->hopper_frequencies)) {
FURI_LOG_E(TAG, "Error loading user settings, loading default settings");
subghz_setting_load_default(instance);
}
}
void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup) {
for
M_EACH(frequency, instance->frequencies, FrequencyList_t) {
*frequency &= FREQUENCY_MASK;
if(*frequency == frequency_to_setup) {
*frequency |= FREQUENCY_FLAG_DEFAULT;
}
}
}
size_t subghz_setting_get_frequency_count(SubGhzSetting* instance) {
furi_assert(instance);
return FrequencyList_size(instance->frequencies);
}
size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance) {
furi_assert(instance);
return FrequencyList_size(instance->hopper_frequencies);
}
size_t subghz_setting_get_preset_count(SubGhzSetting* instance) {
furi_assert(instance);
return SubGhzSettingCustomPresetItemArray_size(instance->preset->data);
}
const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) {
furi_assert(instance);
if(idx >= SubGhzSettingCustomPresetItemArray_size(instance->preset->data)) {
idx = 0;
}
SubGhzSettingCustomPresetItem* item =
SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx);
return furi_string_get_cstr(item->custom_preset_name);
}
int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name) {
furi_assert(instance);
size_t idx = 0;
for
M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) {
if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) {
return idx;
}
idx++;
}
furi_crash("SubGhz: No name preset.");
return -1;
}
bool subghz_setting_load_custom_preset(
SubGhzSetting* instance,
const char* preset_name,
FlipperFormat* fff_data_file) {
furi_assert(instance);
furi_assert(preset_name);
uint32_t temp_data32;
SubGhzSettingCustomPresetItem* item =
SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data);
item->custom_preset_name = furi_string_alloc();
furi_string_set(item->custom_preset_name, preset_name);
do {
if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32))
break;
if(!temp_data32 || (temp_data32 % 2)) {
FURI_LOG_E(TAG, "Integrity error Custom_preset_data");
break;
}
item->custom_preset_data_size = sizeof(uint8_t) * temp_data32;
item->custom_preset_data = malloc(item->custom_preset_data_size);
if(!flipper_format_read_hex(
fff_data_file,
"Custom_preset_data",
item->custom_preset_data,
item->custom_preset_data_size)) {
FURI_LOG_E(TAG, "Missing Custom_preset_data");
break;
}
return true;
} while(true);
return false;
}
bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name) {
furi_assert(instance);
furi_assert(preset_name);
SubGhzSettingCustomPresetItemArray_it_t it;
SubGhzSettingCustomPresetItemArray_it_last(it, instance->preset->data);
while(!SubGhzSettingCustomPresetItemArray_end_p(it)) {
SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_ref(it);
if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) {
furi_string_free(item->custom_preset_name);
free(item->custom_preset_data);
SubGhzSettingCustomPresetItemArray_remove(instance->preset->data, it);
return true;
}
SubGhzSettingCustomPresetItemArray_previous(it);
}
return false;
}
uint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx) {
furi_assert(instance);
SubGhzSettingCustomPresetItem* item =
SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx);
return item->custom_preset_data;
}
size_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx) {
furi_assert(instance);
SubGhzSettingCustomPresetItem* item =
SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx);
return item->custom_preset_data_size;
}
uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name) {
furi_assert(instance);
SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_get(
instance->preset->data, subghz_setting_get_inx_preset_by_name(instance, preset_name));
return item->custom_preset_data;
}
uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) {
furi_assert(instance);
if(idx < FrequencyList_size(instance->frequencies)) {
return (*FrequencyList_get(instance->frequencies, idx)) & FREQUENCY_MASK;
} else {
return 0;
}
}
uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx) {
furi_assert(instance);
if(idx < FrequencyList_size(instance->hopper_frequencies)) {
return *FrequencyList_get(instance->hopper_frequencies, idx);
} else {
return 0;
}
}
uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance) {
furi_assert(instance);
for(size_t i = 0; i < FrequencyList_size(instance->frequencies); i++) {
uint32_t frequency = *FrequencyList_get(instance->frequencies, i);
if(frequency & FREQUENCY_FLAG_DEFAULT) {
return i;
}
}
return 0;
}
uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance) {
furi_assert(instance);
return subghz_setting_get_frequency(
instance, subghz_setting_get_frequency_default_index(instance));
}