diff --git a/applications/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/subghz/helpers/subghz_frequency_analyzer_worker.c index 10c5a9ea81a..260676a24e7 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 @@ -28,6 +28,7 @@ struct SubGhzFrequencyAnalyzerWorker { SubGhzSetting* setting; float filVal; + float trigger_level; SubGhzFrequencyAnalyzerWorkerPairCallback pair_callback; void* context; @@ -154,7 +155,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { (double)rssi_min); // Second stage: fine scan - if(frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) { + if(frequency_rssi.rssi_coarse > instance->trigger_level) { furi_hal_subghz_idle(); subghz_frequency_analyzer_worker_load_registers(subghz_preset_ook_58khz); //for example -0.3 ... 433.92 ... +0.3 step 20KHz @@ -189,7 +190,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { } // Deliver results fine - if(frequency_rssi.rssi_fine > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) { + if(frequency_rssi.rssi_fine > instance->trigger_level) { FURI_LOG_D( TAG, "=:%u:%f", frequency_rssi.frequency_fine, (double)frequency_rssi.rssi_fine); @@ -205,7 +206,7 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { instance->context, frequency_rssi.frequency_fine, frequency_rssi.rssi_fine); } } else if( // Deliver results coarse - (frequency_rssi.rssi_coarse > SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD) && + (frequency_rssi.rssi_coarse > instance->trigger_level) && (instance->sample_hold_counter < 10)) { FURI_LOG_D( TAG, @@ -255,6 +256,7 @@ SubGhzFrequencyAnalyzerWorker* subghz_frequency_analyzer_worker_alloc(void* cont SubGhz* subghz = context; instance->setting = subghz->setting; + instance->trigger_level = SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD; return instance; } @@ -297,3 +299,13 @@ bool subghz_frequency_analyzer_worker_is_running(SubGhzFrequencyAnalyzerWorker* furi_assert(instance); return instance->worker_running; } + +void subghz_frequency_analyzer_worker_set_trigger_level( + SubGhzFrequencyAnalyzerWorker* instance, + float value) { + instance->trigger_level = value; +} + +float subghz_frequency_analyzer_worker_get_trigger_level(SubGhzFrequencyAnalyzerWorker* instance) { + return instance->trigger_level; +} \ No newline at end of file diff --git a/applications/subghz/helpers/subghz_frequency_analyzer_worker.h b/applications/subghz/helpers/subghz_frequency_analyzer_worker.h index 50687c76da2..f1f5d21b80c 100644 --- a/applications/subghz/helpers/subghz_frequency_analyzer_worker.h +++ b/applications/subghz/helpers/subghz_frequency_analyzer_worker.h @@ -56,3 +56,19 @@ void subghz_frequency_analyzer_worker_stop(SubGhzFrequencyAnalyzerWorker* instan * @return bool - true if running */ bool subghz_frequency_analyzer_worker_is_running(SubGhzFrequencyAnalyzerWorker* instance); + +/** Set RSSI trigger level + * + * @param instance SubGhzFrequencyAnalyzerWorker instance + * @param value RSSI level + */ +void subghz_frequency_analyzer_worker_set_trigger_level( + SubGhzFrequencyAnalyzerWorker* instance, + float value); + +/** Get RSSI trigger level + * + * @param instance SubGhzFrequencyAnalyzerWorker instance + * @return RSSI trigger level + */ +float subghz_frequency_analyzer_worker_get_trigger_level(SubGhzFrequencyAnalyzerWorker* instance); diff --git a/applications/subghz/scenes/subghz_scene_frequency_analyzer.c b/applications/subghz/scenes/subghz_scene_frequency_analyzer.c index b42acd4d2ca..f067f985990 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 d3f773159eb..058d3b5ce6e 100644 --- a/applications/subghz/views/subghz_frequency_analyzer.c +++ b/applications/subghz/views/subghz_frequency_analyzer.c @@ -6,10 +6,30 @@ #include #include #include +#include #include "../helpers/subghz_frequency_analyzer_worker.h" #include +#define TAG "frequency_analyzer" + +#define RSSI_MIN -97 +#define RSSI_MAX -60 +#define RSSI_SCALE 2 +#define TRIGGER_STEP 1 + +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 +40,18 @@ struct SubGhzFrequencyAnalyzer { SubGhzFrequencyAnalyzerCallback callback; void* context; bool locked; + float rssi_last; + uint32_t frequency_last; + uint32_t frequency_last_vis; + 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 +64,59 @@ 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; - for(size_t i = 1; i < (uint8_t)rssi; i++) { - if(i > 20) break; - if(i % 4) { + if(rssi > RSSI_MAX) rssi = RSSI_MAX; + rssi = (rssi - RSSI_MIN) / RSSI_SCALE; + uint8_t column_number = 0; + for(size_t i = 0; i <= (uint8_t)rssi; i++) { + if((i + 1) % 4) { column_number++; canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number); } } } + + // Last RSSI + if(rssi_last) { + if(rssi_last > RSSI_MAX) rssi_last = RSSI_MAX; + int max_x = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE) * 2; + //if(!(max_x % 8)) max_x -= 2; + int max_h = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE) + 4; + max_h -= (max_h / 4) + 3; + canvas_draw_line(canvas, x + max_x + 1, y - max_h, x + max_x + 1, y + 3); + } + + // Trigger cursor + trigger = (trigger - RSSI_MIN) / RSSI_SCALE; + uint8_t tr_x = x + 2 * trigger; + 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, y + 3, x + (RSSI_MAX - RSSI_MIN) * 2 / RSSI_SCALE, 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, 57, 58); - //Frequency + // Frequency canvas_set_font(canvas, FontBigNumbers); snprintf( buffer, @@ -71,29 +124,107 @@ 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; + + 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 + float trigger_level = subghz_frequency_analyzer_worker_get_trigger_level(instance->worker); + switch(event->key) { + case InputKeyLeft: + trigger_level -= TRIGGER_STEP; + if(trigger_level < RSSI_MIN) trigger_level = RSSI_MIN; + break; + default: + case InputKeyRight: + trigger_level += TRIGGER_STEP; + if(trigger_level > RSSI_MAX) trigger_level = RSSI_MAX; + break; + } + subghz_frequency_analyzer_worker_set_trigger_level(instance->worker, trigger_level); + FURI_LOG_I(TAG, "trigger = %.1f", (double)trigger_level); + 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 = + subghz_frequency_analyzer_worker_get_trigger_level(instance->worker); + 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; + } + + 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->locked) { + // Triggered! + instance->rssi_last = rssi; + notification_message(instance->notifications, &sequence_hw_blink_stop); + notification_message(instance->notifications, &sequence_success); + FURI_LOG_D(TAG, "triggered"); } - } else if((rssi != 0.f) && (!instance->locked)) { - if(instance->callback) { - instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context); + // Update values + if(rssi >= instance->rssi_last) { + instance->rssi_last = rssi; + instance->frequency_last = frequency; } } @@ -101,7 +232,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_vis; + model->trigger = subghz_frequency_analyzer_worker_get_trigger_level(instance->worker); return true; }); } @@ -110,6 +244,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 +258,18 @@ 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; + subghz_frequency_analyzer_worker_set_trigger_level(instance->worker, RSSI_MIN); + with_view_model( instance->view, (SubGhzFrequencyAnalyzerModel * model) { model->rssi = 0; + model->rssi_last = 0; model->frequency = 0; + model->frequency_last = 0; + model->trigger = RSSI_MIN; return true; }); } @@ -132,21 +278,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 +304,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; }