diff --git a/applications/subghz/views/subghz_frequency_analyzer.c b/applications/subghz/views/subghz_frequency_analyzer.c index d3f773159eb..c25ae444489 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,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; + 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 +128,124 @@ 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; + + 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))) { + // 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; } +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); + 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 Hz", (double)rssi, frequency); + frequency = round_int(frequency, 3); // Round 299999990Hz to 300000000Hz + 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 +254,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 +268,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 +289,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 +315,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; }