Files
Momentum-Firmware/applications/main/subghz/scenes/subghz_scene_decode_raw.c
Willy-JL 0e8703a7a1 Fix many SubGhz history bugs
No "history full" if "delete old signals"
Delete more old signals if history full
Fix visual offset bug with "delete old signals"
Fix led and sound for full history / not
"Delete old signals" in decode raw too
2024-01-27 19:12:59 +00:00

309 lines
12 KiB
C

#include "../subghz_i.h"
#define TAG "SubGhzDecodeRaw"
#define SAMPLES_TO_READ_PER_TICK 400
static void subghz_scene_receiver_update_statusbar(void* context) {
SubGhz* subghz = context;
FuriString* history_stat_str = furi_string_alloc();
if(!subghz_history_get_text_space_left(
subghz->history,
history_stat_str,
subghz->gps->satellites,
subghz->last_settings->delete_old_signals)) {
FuriString* frequency_str = furi_string_alloc();
FuriString* modulation_str = furi_string_alloc();
subghz_txrx_get_frequency_and_modulation(
subghz->txrx, frequency_str, modulation_str, false);
subghz_view_receiver_add_data_statusbar(
subghz->subghz_receiver,
furi_string_get_cstr(frequency_str),
furi_string_get_cstr(modulation_str),
furi_string_get_cstr(history_stat_str),
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0,
subghz->repeater);
furi_string_free(frequency_str);
furi_string_free(modulation_str);
} else {
subghz_view_receiver_add_data_statusbar(
subghz->subghz_receiver,
furi_string_get_cstr(history_stat_str),
"",
"",
subghz_txrx_hopper_get_state(subghz->txrx) != SubGhzHopperStateOFF,
READ_BIT(subghz->filter, SubGhzProtocolFlag_BinRAW) > 0,
subghz->repeater);
}
furi_string_free(history_stat_str);
}
void subghz_scene_decode_raw_callback(SubGhzCustomEvent event, void* context) {
furi_assert(context);
SubGhz* subghz = context;
view_dispatcher_send_custom_event(subghz->view_dispatcher, event);
}
static void subghz_scene_add_to_history_callback(
SubGhzReceiver* receiver,
SubGhzProtocolDecoderBase* decoder_base,
void* context) {
furi_assert(context);
SubGhz* subghz = context;
FuriString* item_name = furi_string_alloc();
FuriString* item_time = furi_string_alloc();
uint16_t idx = subghz_history_get_item(subghz->history);
SubGhzRadioPreset preset = subghz_txrx_get_preset(subghz->txrx);
preset.latitude = subghz->gps->latitude;
preset.longitude = subghz->gps->longitude;
if(subghz->last_settings->delete_old_signals && subghz_history_full(subghz->history)) {
subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver);
while(idx > 0 && subghz_history_full(subghz->history)) {
subghz_history_delete_item(subghz->history, 0);
subghz_view_receiver_delete_item(subghz->subghz_receiver, 0);
idx--;
}
subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver);
subghz_scene_receiver_update_statusbar(subghz);
subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
}
if(subghz_history_add_to_history(subghz->history, decoder_base, &preset)) {
furi_string_reset(item_name);
furi_string_reset(item_time);
subghz->state_notifications = SubGhzNotificationStateRxDone;
if(subghz->remove_duplicates) {
// Look in history for signal hash
uint32_t hash_data = subghz_protocol_decoder_base_get_hash_data_long(decoder_base);
subghz_view_receiver_disable_draw_callback(subghz->subghz_receiver);
for(uint16_t i = idx; i > 0; i--) {
i--; // Iterating in reverse with off by one
if(subghz_history_get_hash_data(subghz->history, i) == hash_data) {
// Remove previous instance and update menu index
subghz_history_delete_item(subghz->history, i);
subghz_view_receiver_delete_item(subghz->subghz_receiver, i);
idx--;
}
i++;
}
// Restore ui state
subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
subghz_view_receiver_enable_draw_callback(subghz->subghz_receiver);
}
subghz_history_get_text_item_menu(subghz->history, item_name, idx);
subghz_history_get_time_item_menu(subghz->history, item_time, idx);
subghz_view_receiver_add_item_to_menu(
subghz->subghz_receiver,
furi_string_get_cstr(item_name),
furi_string_get_cstr(item_time),
subghz_history_get_type_protocol(subghz->history, idx),
subghz_history_get_repeats(subghz->history, idx));
subghz_scene_receiver_update_statusbar(subghz);
}
subghz_receiver_reset(receiver);
furi_string_free(item_name);
furi_string_free(item_time);
}
bool subghz_scene_decode_raw_start(SubGhz* subghz) {
FuriString* file_name = furi_string_alloc();
bool success = false;
do {
if(!flipper_format_rewind(subghz_txrx_get_fff_data(subghz->txrx))) {
FURI_LOG_E(TAG, "Rewind error");
break;
}
if(!flipper_format_read_string(
subghz_txrx_get_fff_data(subghz->txrx), "File_name", file_name)) {
FURI_LOG_E(TAG, "Missing File_name");
break;
}
success = true;
} while(false);
if(success) {
//FURI_LOG_I(TAG, "Listening at \033[0;33m%s\033[0m.", furi_string_get_cstr(file_name));
subghz->decode_raw_file_worker_encoder = subghz_file_encoder_worker_alloc();
if(subghz_file_encoder_worker_start(
subghz->decode_raw_file_worker_encoder,
furi_string_get_cstr(file_name),
subghz_txrx_radio_device_get_name(subghz->txrx))) {
//the worker needs a file in order to open and read part of the file
furi_delay_ms(100);
} else {
success = false;
}
if(!success) {
subghz_file_encoder_worker_free(subghz->decode_raw_file_worker_encoder);
}
}
furi_string_free(file_name);
return success;
}
bool subghz_scene_decode_raw_next(SubGhz* subghz) {
LevelDuration level_duration;
SubGhzReceiver* receiver = subghz_txrx_get_receiver(subghz->txrx);
for(uint32_t read = SAMPLES_TO_READ_PER_TICK; read > 0; --read) {
level_duration =
subghz_file_encoder_worker_get_level_duration(subghz->decode_raw_file_worker_encoder);
if(!level_duration_is_reset(level_duration)) {
bool level = level_duration_get_level(level_duration);
uint32_t duration = level_duration_get_duration(level_duration);
subghz_receiver_decode(receiver, level, duration);
} else {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateLoaded);
subghz->state_notifications = SubGhzNotificationStateIDLE;
subghz_view_receiver_add_data_progress(subghz->subghz_receiver, "Done!");
return false; // No more samples available
}
}
// Update progress info
FuriString* progress_str = furi_string_alloc();
subghz_file_encoder_worker_get_text_progress(
subghz->decode_raw_file_worker_encoder, progress_str);
subghz_view_receiver_add_data_progress(
subghz->subghz_receiver, furi_string_get_cstr(progress_str));
furi_string_free(progress_str);
return true; // More samples available
}
void subghz_scene_decode_raw_on_enter(void* context) {
SubGhz* subghz = context;
FuriString* item_name = furi_string_alloc();
FuriString* item_time = furi_string_alloc();
subghz_view_receiver_set_mode(subghz->subghz_receiver, SubGhzViewReceiverModeFile);
subghz_view_receiver_set_callback(
subghz->subghz_receiver, subghz_scene_decode_raw_callback, subghz);
subghz_txrx_set_rx_callback(subghz->txrx, subghz_scene_add_to_history_callback, subghz);
subghz_txrx_receiver_set_filter(subghz->txrx, SubGhzProtocolFlag_Decodable);
if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneDecodeRAW) ==
SubGhzDecodeRawStateStart) {
//Decode RAW to history
subghz_history_reset(subghz->history);
if(subghz_scene_decode_raw_start(subghz)) {
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateLoading);
subghz->state_notifications = SubGhzNotificationStateRx;
}
} else {
//Load history to receiver
subghz_view_receiver_exit(subghz->subghz_receiver);
for(uint16_t i = 0; i < subghz_history_get_item(subghz->history); i++) {
furi_string_reset(item_name);
furi_string_reset(item_time);
subghz_history_get_text_item_menu(subghz->history, item_name, i);
subghz_history_get_time_item_menu(subghz->history, item_time, i);
subghz_view_receiver_add_item_to_menu(
subghz->subghz_receiver,
furi_string_get_cstr(item_name),
furi_string_get_cstr(item_time),
subghz_history_get_type_protocol(subghz->history, i),
subghz_history_get_repeats(subghz->history, i));
}
subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->idx_menu_chosen);
}
furi_string_free(item_name);
furi_string_free(item_time);
subghz_scene_receiver_update_statusbar(subghz);
view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver);
}
bool subghz_scene_decode_raw_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SubGhzCustomEventViewReceiverBack:
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneDecodeRAW, SubGhzDecodeRawStateStart);
subghz->idx_menu_chosen = 0;
subghz_txrx_set_rx_callback(subghz->txrx, NULL, subghz);
if(subghz_file_encoder_worker_is_running(subghz->decode_raw_file_worker_encoder)) {
subghz_file_encoder_worker_stop(subghz->decode_raw_file_worker_encoder);
}
subghz_file_encoder_worker_free(subghz->decode_raw_file_worker_encoder);
subghz->state_notifications = SubGhzNotificationStateIDLE;
scene_manager_set_scene_state(
subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerNoSet);
scene_manager_search_and_switch_to_previous_scene(
subghz->scene_manager, SubGhzSceneMoreRAW);
consumed = true;
break;
case SubGhzCustomEventViewReceiverOK:
subghz->idx_menu_chosen = subghz_view_receiver_get_idx_menu(subghz->subghz_receiver);
subghz->state_notifications = SubGhzNotificationStateIDLE;
scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverInfo);
consumed = true;
break;
case SubGhzCustomEventViewReceiverConfig:
FURI_LOG_W(TAG, "No config options");
consumed = true;
break;
case SubGhzCustomEventViewReceiverOffDisplay:
notification_message(subghz->notifications, &sequence_display_backlight_off);
consumed = true;
break;
default:
break;
}
} else if(event.type == SceneManagerEventTypeTick) {
switch(subghz->state_notifications) {
case SubGhzNotificationStateRx:
notification_message(subghz->notifications, &sequence_blink_cyan_10);
break;
case SubGhzNotificationStateRxDone:
notification_message(subghz->notifications, &subghz_sequence_rx);
subghz->state_notifications = SubGhzNotificationStateRx;
break;
default:
break;
}
switch(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneDecodeRAW)) {
case SubGhzDecodeRawStateLoading:
subghz_scene_decode_raw_next(subghz);
break;
default:
break;
}
}
return consumed;
}
void subghz_scene_decode_raw_on_exit(void* context) {
UNUSED(context);
}