From ff6fd58b96633c35834c156870744dd95e3c8b2a Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Sat, 30 Jul 2022 20:57:10 +0400 Subject: [PATCH 1/3] New frequency analyzer --- .../subghz/views/subghz_frequency_analyzer.c | 178 +++++++++++++++--- 1 file changed, 154 insertions(+), 24 deletions(-) diff --git a/applications/subghz/views/subghz_frequency_analyzer.c b/applications/subghz/views/subghz_frequency_analyzer.c index d3f773159eb..fdbc28bf4f2 100644 --- a/applications/subghz/views/subghz_frequency_analyzer.c +++ b/applications/subghz/views/subghz_frequency_analyzer.c @@ -10,6 +10,23 @@ #include +#define TAG "frequency_analyzer" + +#define TRIGGER_MIN -85.5 +#define TRIGGER_MAX -28.5 + +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 +37,20 @@ struct SubGhzFrequencyAnalyzer { SubGhzFrequencyAnalyzerCallback callback; void* context; bool locked; + float rssi_last; + uint32_t frequency_last; + uint32_t frequency_last_vis; + 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 +63,57 @@ 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; - if(rssi) { +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; + 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 && rssi_last) { + int max_x = (int)((rssi_last + 87.0) / 3) * 2; + //if (!(max_x % 8)) max_x -= 2; + int max_h = (int)((rssi_last + 87.0) / 3) + 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 > TRIGGER_MIN) { + trigger = (trigger + 90) / 3; + 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,37 +121,110 @@ 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); } bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { furi_assert(context); + SubGhzFrequencyAnalyzer* instance = context; - if(event->key == InputKeyBack) { + 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 -= 1.5; + if (instance->trigger < TRIGGER_MIN) instance->trigger = TRIGGER_MIN; + break; + default: + case InputKeyRight: + instance->trigger += 1.5; + if (instance->trigger > TRIGGER_MAX) instance->trigger = TRIGGER_MAX; + break; + } + if (instance->trigger > TRIGGER_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_vis; + model->trigger = instance->trigger; + return true; + }); } return true; } 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); + notification_message(instance->notifications, &sequence_hw_blink); + instance->frequency_last_vis = instance->frequency_last; } + instance->triggered = false; } else if((rssi != 0.f) && (!instance->locked)) { if(instance->callback) { instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context); } } + if ((rssi != 0.f) && (frequency != 0)) { + // Threre is some signal + FURI_LOG_I(TAG, "rssi = %.2f, frequency = %d", (double)rssi, frequency); + if (!instance->triggered && ((instance->trigger <= TRIGGER_MIN) || (rssi >= instance->trigger))) { + // 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"); + } + if (instance->triggered) { + // Update values + if (rssi > instance->rssi_last) instance->rssi_last = rssi; + instance->frequency_last = frequency; + } + } + instance->locked = (rssi != 0.f); 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_vis; + model->trigger = instance->trigger; return true; }); } @@ -110,6 +233,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 +247,19 @@ void subghz_frequency_analyzer_enter(void* context) { subghz_frequency_analyzer_worker_start(instance->worker); + instance->rssi_last = 0; + instance->frequency_last = 0; + instance->frequency_last_vis = 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 +268,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 +294,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; } From e35f6401b0d1b4d81bc697bdaa2d122c2d7dba43 Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Sun, 31 Jul 2022 12:04:10 +0400 Subject: [PATCH 2/3] clang-formatted code --- .../subghz/views/subghz_frequency_analyzer.c | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/applications/subghz/views/subghz_frequency_analyzer.c b/applications/subghz/views/subghz_frequency_analyzer.c index fdbc28bf4f2..aa5bb651f43 100644 --- a/applications/subghz/views/subghz_frequency_analyzer.c +++ b/applications/subghz/views/subghz_frequency_analyzer.c @@ -63,11 +63,17 @@ void subghz_frequency_analyzer_set_callback( subghz_frequency_analyzer->context = context; } -void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi, float rssi_last, float trigger, uint8_t x, uint8_t y) { +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) { + if(rssi) { rssi = (rssi + 90) / 3; - if (rssi > 20) rssi = 20; + if(rssi > 20) rssi = 20; uint8_t column_number = 0; for(size_t i = 1; i < (uint8_t)rssi; i++) { if(i % 4) { @@ -78,20 +84,20 @@ void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi, float rssi_ } // Last RSSI - if (!rssi && rssi_last) { + if(!rssi && rssi_last) { int max_x = (int)((rssi_last + 87.0) / 3) * 2; - //if (!(max_x % 8)) max_x -= 2; + //if(!(max_x % 8)) max_x -= 2; int max_h = (int)((rssi_last + 87.0) / 3) + 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) { + 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 > TRIGGER_MIN) { + if(trigger > TRIGGER_MIN) { trigger = (trigger + 90) / 3; uint8_t tr_x = x + 2 * trigger - 2; canvas_draw_dot(canvas, tr_x, y + 4); @@ -111,7 +117,8 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel // RSSI canvas_draw_str(canvas, 33, 62, "RSSI"); - subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, model->rssi_last, model->trigger, 55, 58); + subghz_frequency_analyzer_draw_rssi( + canvas, model->rssi, model->rssi_last, model->trigger, 55, 58); // Frequency canvas_set_font(canvas, FontBigNumbers); @@ -126,7 +133,7 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel // Last detected frequency canvas_set_font(canvas, FontSecondary); - if (model->frequency_last) { + if(model->frequency_last) { snprintf( buffer, sizeof(buffer), @@ -145,31 +152,30 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { bool need_redraw = false; - if (event->key == InputKeyBack) - return false; + if(event->key == InputKeyBack) return false; - if (((event->type == InputTypePress) || (event->type == InputTypeRepeat)) - && ((event->key == InputKeyLeft) || (event->key == InputKeyRight))) { + if(((event->type == InputTypePress) || (event->type == InputTypeRepeat)) && + ((event->key == InputKeyLeft) || (event->key == InputKeyRight))) { // Trigger setup - switch (event->key) { + switch(event->key) { case InputKeyLeft: instance->trigger -= 1.5; - if (instance->trigger < TRIGGER_MIN) instance->trigger = TRIGGER_MIN; + if(instance->trigger < TRIGGER_MIN) instance->trigger = TRIGGER_MIN; break; default: case InputKeyRight: instance->trigger += 1.5; - if (instance->trigger > TRIGGER_MAX) instance->trigger = TRIGGER_MAX; + if(instance->trigger > TRIGGER_MAX) instance->trigger = TRIGGER_MAX; break; } - if (instance->trigger > TRIGGER_MIN) + if(instance->trigger > TRIGGER_MIN) FURI_LOG_I(TAG, "trigger = %.1f", (double)instance->trigger); else FURI_LOG_I(TAG, "trigger disabled"); need_redraw = true; } - if (need_redraw) { + if(need_redraw) { SubGhzFrequencyAnalyzer* instance = context; with_view_model( instance->view, (SubGhzFrequencyAnalyzerModel * model) { @@ -199,10 +205,11 @@ void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, } } - if ((rssi != 0.f) && (frequency != 0)) { + if((rssi != 0.f) && (frequency != 0)) { // Threre is some signal FURI_LOG_I(TAG, "rssi = %.2f, frequency = %d", (double)rssi, frequency); - if (!instance->triggered && ((instance->trigger <= TRIGGER_MIN) || (rssi >= instance->trigger))) { + if(!instance->triggered && + ((instance->trigger <= TRIGGER_MIN) || (rssi >= instance->trigger))) { // Triggered! instance->triggered = true; instance->rssi_last = rssi; @@ -210,9 +217,9 @@ void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, notification_message(instance->notifications, &sequence_success); FURI_LOG_D(TAG, "triggered"); } - if (instance->triggered) { + if(instance->triggered) { // Update values - if (rssi > instance->rssi_last) instance->rssi_last = rssi; + if(rssi > instance->rssi_last) instance->rssi_last = rssi; instance->frequency_last = frequency; } } From 0352d97d497b117b761c135d2e71ece07540c6ce Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Sun, 31 Jul 2022 15:09:17 +0400 Subject: [PATCH 3/3] Round frequency value to KHz (299999990Hz to 300000000Hz) --- .../subghz/views/subghz_frequency_analyzer.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/applications/subghz/views/subghz_frequency_analyzer.c b/applications/subghz/views/subghz_frequency_analyzer.c index aa5bb651f43..c25ae444489 100644 --- a/applications/subghz/views/subghz_frequency_analyzer.c +++ b/applications/subghz/views/subghz_frequency_analyzer.c @@ -189,9 +189,22 @@ bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { 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); @@ -207,7 +220,8 @@ void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, if((rssi != 0.f) && (frequency != 0)) { // Threre is some signal - FURI_LOG_I(TAG, "rssi = %.2f, frequency = %d", (double)rssi, frequency); + FURI_LOG_I(TAG, "rssi = %.2f, frequency = %d Hz", (double)rssi, frequency); + frequency = round_int(frequency, 3); // Round 299999990Hz to 300000000Hz if(!instance->triggered && ((instance->trigger <= TRIGGER_MIN) || (rssi >= instance->trigger))) { // Triggered!