diff --git a/applications/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/subghz/helpers/subghz_frequency_analyzer_worker.c index 10c5a9ea8..0b91a1f4f 100644 --- a/applications/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -5,7 +5,7 @@ #define TAG "SubghzFrequencyAnalyzerWorker" -#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -95.0f +#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -97.0f static const uint8_t subghz_preset_ook_58khz[][2] = { {CC1101_MDMCFG4, 0b11110111}, // Rx BW filter is 58.035714kHz diff --git a/applications/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/subghz/scenes/subghz_scene_frequency_analyzer.c index b42acd4d2..f067f9859 100644 --- a/applications/subghz/scenes/subghz_scene_frequency_analyzer.c +++ b/applications/subghz/scenes/subghz_scene_frequency_analyzer.c @@ -17,16 +17,8 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) { } bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) { - SubGhz* subghz = context; - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubGhzCustomEventSceneAnalyzerLock) { - notification_message(subghz->notifications, &sequence_set_green_255); - return true; - } else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) { - notification_message(subghz->notifications, &sequence_reset_rgb); - return true; - } - } + UNUSED(context); + UNUSED(event); return false; } diff --git a/applications/subghz/views/subghz_frequency_analyzer.c b/applications/subghz/views/subghz_frequency_analyzer.c index d3f773159..ebc912d70 100644 --- a/applications/subghz/views/subghz_frequency_analyzer.c +++ b/applications/subghz/views/subghz_frequency_analyzer.c @@ -6,10 +6,31 @@ #include #include #include +#include #include "../helpers/subghz_frequency_analyzer_worker.h" #include +#define TAG "frequency_analyzer" + +#define RSSI_MIN -101 +#define RSSI_MAX -60 +#define RSSI_SCALE 2 +#define TRIGGER_STEP 1 +#define TRIGGER_MIN RSSI_MIN + RSSI_SCALE * 2 + +static const NotificationSequence sequence_hw_blink = { + &message_blink_start_10, + &message_blink_set_color_cyan, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence sequence_hw_blink_stop = { + &message_blink_stop, + NULL, +}; + typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, } SubGhzFrequencyAnalyzerStatus; @@ -20,11 +41,19 @@ struct SubGhzFrequencyAnalyzer { SubGhzFrequencyAnalyzerCallback callback; void* context; bool locked; + float rssi_last; + uint32_t frequency_last; + float trigger; + bool triggered; + NotificationApp* notifications; }; typedef struct { uint32_t frequency; + uint32_t frequency_last; float rssi; + float rssi_last; + float trigger; } SubGhzFrequencyAnalyzerModel; void subghz_frequency_analyzer_set_callback( @@ -37,33 +66,64 @@ void subghz_frequency_analyzer_set_callback( subghz_frequency_analyzer->context = context; } -void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) { - uint8_t x = 48; - uint8_t y = 56; - uint8_t column_number = 0; +void subghz_frequency_analyzer_draw_rssi( + Canvas* canvas, + float rssi, + float rssi_last, + float trigger, + uint8_t x, + uint8_t y) { + // Current RSSI if(rssi) { - rssi = (rssi + 90) / 3; + rssi = (rssi - RSSI_MIN) / RSSI_SCALE; + if(rssi > 20) rssi = 20; + uint8_t column_number = 0; for(size_t i = 1; i < (uint8_t)rssi; i++) { - if(i > 20) break; if(i % 4) { column_number++; canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number); } } } + + // Last RSSI + if(rssi_last) { + int max_x = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE - 1) * 2; + //if(!(max_x % 8)) max_x -= 2; + int max_h = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE - 1) + 4; + max_h -= (max_h / 4) + 3; + if(max_x > 38) max_h = 38; + if(max_h > 19) max_h = 19; + if(max_x >= 0 && max_h > 0) { + canvas_draw_line(canvas, x + max_x + 1, y - max_h, x + max_x + 1, y + 3); + } + } + + // Trigger cursor + if(trigger >= RSSI_MIN + RSSI_SCALE * 2) { + trigger = (trigger - RSSI_MIN) / RSSI_SCALE; + uint8_t tr_x = x + 2 * trigger - 2; + canvas_draw_dot(canvas, tr_x, y + 4); + canvas_draw_line(canvas, tr_x - 1, y + 5, tr_x + 1, y + 5); + } + + canvas_draw_line(canvas, x + 2, y + 3, x + 39, y + 3); } void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) { char buffer[64]; + // Title canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); - canvas_draw_str(canvas, 28, 60, "RSSI"); - subghz_frequency_analyzer_draw_rssi(canvas, model->rssi); + // RSSI + canvas_draw_str(canvas, 33, 62, "RSSI"); + subghz_frequency_analyzer_draw_rssi( + canvas, model->rssi, model->rssi_last, model->trigger, 55, 58); - //Frequency + // Frequency canvas_set_font(canvas, FontBigNumbers); snprintf( buffer, @@ -71,29 +131,113 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel "%03ld.%03ld", model->frequency / 1000000 % 1000, model->frequency / 1000 % 1000); - canvas_draw_str(canvas, 8, 35, buffer); - canvas_draw_icon(canvas, 96, 24, &I_MHz_25x11); + canvas_draw_str(canvas, 8, 30, buffer); + canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11); + + // Last detected frequency + canvas_set_font(canvas, FontSecondary); + if(model->frequency_last) { + snprintf( + buffer, + sizeof(buffer), + "Last: %03ld.%03ld MHz", + model->frequency_last / 1000000 % 1000, + model->frequency_last / 1000 % 1000); + } else { + strcpy(buffer, "Last: ---.--- MHz"); + } + canvas_draw_str(canvas, 9, 42, buffer); + + // Buttons hint + elements_button_left(canvas, "T-"); + elements_button_right(canvas, "T+"); } bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { furi_assert(context); + SubGhzFrequencyAnalyzer* instance = context; - if(event->key == InputKeyBack) { - return false; + bool need_redraw = false; + + if(event->key == InputKeyBack) return false; + + if(((event->type == InputTypePress) || (event->type == InputTypeRepeat)) && + ((event->key == InputKeyLeft) || (event->key == InputKeyRight))) { + // Trigger setup + switch(event->key) { + case InputKeyLeft: + instance->trigger -= TRIGGER_STEP; + if(instance->trigger < RSSI_MIN + RSSI_SCALE * 2) instance->trigger = TRIGGER_MIN; + break; + default: + case InputKeyRight: + if(instance->trigger < RSSI_MIN + RSSI_SCALE * 2) + instance->trigger = TRIGGER_MIN; + else + instance->trigger += TRIGGER_STEP; + if(instance->trigger > RSSI_MAX) instance->trigger = RSSI_MAX; + break; + } + if(instance->trigger > RSSI_MIN) + FURI_LOG_I(TAG, "trigger = %.1f", (double)instance->trigger); + else + FURI_LOG_I(TAG, "trigger disabled"); + need_redraw = true; + } + + if(need_redraw) { + SubGhzFrequencyAnalyzer* instance = context; + with_view_model( + instance->view, (SubGhzFrequencyAnalyzerModel * model) { + model->rssi_last = instance->rssi_last; + model->frequency_last = instance->frequency_last; + model->trigger = instance->trigger; + return true; + }); } return true; } +uint32_t round_int(uint32_t value, uint8_t n) { + // Round value + uint8_t on = n; + while(n--) { + uint8_t i = value % 10; + value /= 10; + if(i >= 5) value++; + } + while(on--) value *= 10; + return value; +} + void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) { + furi_assert(context); SubGhzFrequencyAnalyzer* instance = context; + if((rssi == 0.f) && (instance->locked)) { - if(instance->callback) { - instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context); - } - } else if((rssi != 0.f) && (!instance->locked)) { - if(instance->callback) { - instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context); + notification_message(instance->notifications, &sequence_hw_blink); + instance->triggered = false; + } + + if((rssi != 0.f) && (frequency != 0)) { + // Threre is some signal + FURI_LOG_I(TAG, "rssi = %.2f, frequency = %d Hz", (double)rssi, frequency); + frequency = round_int(frequency, 3); // Round 299999990Hz to 300000000Hz + if((instance->trigger <= RSSI_MIN + RSSI_SCALE * 2) || (rssi >= instance->trigger)) { + if(!instance->triggered) { + // Triggered! + instance->triggered = true; + instance->rssi_last = rssi; + notification_message(instance->notifications, &sequence_hw_blink_stop); + notification_message(instance->notifications, &sequence_success); + FURI_LOG_D(TAG, "triggered"); + } + // Update values + if(rssi > instance->rssi_last) instance->rssi_last = rssi; + instance->frequency_last = frequency; + } else { + instance->triggered = false; } } @@ -101,7 +245,10 @@ void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, with_view_model( instance->view, (SubGhzFrequencyAnalyzerModel * model) { model->rssi = rssi; + model->rssi_last = instance->rssi_last; model->frequency = frequency; + model->frequency_last = instance->frequency_last; + model->trigger = instance->trigger; return true; }); } @@ -110,6 +257,10 @@ void subghz_frequency_analyzer_enter(void* context) { furi_assert(context); SubGhzFrequencyAnalyzer* instance = context; + // Notifications + instance->notifications = furi_record_open(RECORD_NOTIFICATION); + notification_message(instance->notifications, &sequence_hw_blink); + //Start worker instance->worker = subghz_frequency_analyzer_worker_alloc(instance->context); @@ -120,10 +271,18 @@ void subghz_frequency_analyzer_enter(void* context) { subghz_frequency_analyzer_worker_start(instance->worker); + instance->rssi_last = 0; + instance->frequency_last = 0; + instance->trigger = TRIGGER_MIN; + instance->triggered = false; + with_view_model( instance->view, (SubGhzFrequencyAnalyzerModel * model) { model->rssi = 0; + model->rssi_last = 0; model->frequency = 0; + model->frequency_last = 0; + model->trigger = instance->trigger; return true; }); } @@ -132,21 +291,21 @@ void subghz_frequency_analyzer_exit(void* context) { furi_assert(context); SubGhzFrequencyAnalyzer* instance = context; - //Stop worker + // Stop blinking + notification_message(instance->notifications, &sequence_hw_blink_stop); + + // Stop worker if(subghz_frequency_analyzer_worker_is_running(instance->worker)) { subghz_frequency_analyzer_worker_stop(instance->worker); } subghz_frequency_analyzer_worker_free(instance->worker); - with_view_model( - instance->view, (SubGhzFrequencyAnalyzerModel * model) { - model->rssi = 0; - return true; - }); + furi_record_close(RECORD_NOTIFICATION); } SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer)); + furi_assert(instance); // View allocation and configuration instance->view = view_alloc(); @@ -158,12 +317,6 @@ SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { view_set_enter_callback(instance->view, subghz_frequency_analyzer_enter); view_set_exit_callback(instance->view, subghz_frequency_analyzer_exit); - with_view_model( - instance->view, (SubGhzFrequencyAnalyzerModel * model) { - model->rssi = 0; - return true; - }); - return instance; }