From 95182b266cfd6fc1d81ec1ff4019bfcca930e1cb Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Thu, 3 Nov 2022 07:42:54 +0300 Subject: [PATCH 1/3] BadUSB scrolllock typo fix (#1968) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/bad_usb/bad_usb_script.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 8ff38ef66..b90218f8d 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -82,7 +82,7 @@ static const DuckyKey ducky_keys[] = { {"PAGEUP", HID_KEYBOARD_PAGE_UP}, {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, - {"SCROLLOCK", HID_KEYBOARD_SCROLL_LOCK}, + {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, {"SPACE", HID_KEYBOARD_SPACEBAR}, {"TAB", HID_KEYBOARD_TAB}, {"MENU", HID_KEYBOARD_APPLICATION}, From eee90c6c406fef114be9b2250936c5e36f0a46cd Mon Sep 17 00:00:00 2001 From: head47 <63517545+head47@users.noreply.github.com> Date: Thu, 3 Nov 2022 08:21:44 +0300 Subject: [PATCH 2/3] Run Bad USB immediately after connection (#1955) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/bad_usb/bad_usb_script.c | 31 ++++++++++++++++++- applications/main/bad_usb/bad_usb_script.h | 1 + .../main/bad_usb/views/bad_usb_view.c | 10 +++++- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index b90218f8d..33b3f5030 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -524,12 +524,16 @@ static int32_t bad_usb_worker(void* context) { } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect, FuriFlagWaitAny, FuriWaitForever); + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, + FuriFlagWaitAny, + FuriWaitForever); furi_check((flags & FuriFlagError) == 0); if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { worker_state = BadUsbStateIdle; // Ready to run + } else if(flags & WorkerEvtToggle) { + worker_state = BadUsbStateWillRun; // Will run when USB is connected } bad_usb->st.state = worker_state; @@ -556,6 +560,31 @@ static int32_t bad_usb_worker(void* context) { } bad_usb->st.state = worker_state; + } else if(worker_state == BadUsbStateWillRun) { // State: start on connection + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, + FuriFlagWaitAny, + FuriWaitForever); + furi_check((flags & FuriFlagError) == 0); + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { // Start executing script + DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + delay_val = 0; + bad_usb->buf_len = 0; + bad_usb->st.line_cur = 0; + bad_usb->defdelay = 0; + bad_usb->repeat_cnt = 0; + bad_usb->file_end = false; + storage_file_seek(script_file, 0, true); + // extra time for PC to recognize Flipper as keyboard + furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); + worker_state = BadUsbStateRunning; + } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution + worker_state = BadUsbStateNotConnected; + } + bad_usb->st.state = worker_state; + } else if(worker_state == BadUsbStateRunning) { // State: running uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h index f24372fab..188142db8 100644 --- a/applications/main/bad_usb/bad_usb_script.h +++ b/applications/main/bad_usb/bad_usb_script.h @@ -12,6 +12,7 @@ typedef enum { BadUsbStateInit, BadUsbStateNotConnected, BadUsbStateIdle, + BadUsbStateWillRun, BadUsbStateRunning, BadUsbStateDelay, BadUsbStateDone, diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index e5c5d92a3..b3eb9bb56 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -29,10 +29,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 20, &I_UsbTree_48x22); - if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) { + if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || + (model->state.state == BadUsbStateNotConnected)) { elements_button_center(canvas, "Run"); } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { elements_button_center(canvas, "Stop"); + } else if(model->state.state == BadUsbStateWillRun) { + elements_button_center(canvas, "Cancel"); } if(model->state.state == BadUsbStateNotConnected) { @@ -40,6 +43,11 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Connect"); canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "to USB"); + } else if(model->state.state == BadUsbStateWillRun) { + canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Will run"); + canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "on connect"); } else if(model->state.state == BadUsbStateFileError) { canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); From 60d125e72a832d300a487b3668187f7a0fe6e996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20D=C3=ADaz?= Date: Thu, 3 Nov 2022 08:57:56 +0100 Subject: [PATCH 3/3] subghz: add analyzer frequency logs (#1914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * subghz: add analyzer frequency logs * SubGhz: switch to change on short press * SubGhz: use full RSSI bar for history view Co-authored-by: あく --- ...subghz_frequency_analyzer_log_item_array.c | 27 ++ ...subghz_frequency_analyzer_log_item_array.h | 73 +++++ .../subghz/views/subghz_frequency_analyzer.c | 280 ++++++++++++++++-- 3 files changed, 355 insertions(+), 25 deletions(-) create mode 100644 applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c create mode 100644 applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c new file mode 100644 index 000000000..9b33e92d1 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.c @@ -0,0 +1,27 @@ +#include "subghz_frequency_analyzer_log_item_array.h" + +const char* + subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by) { + if(order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) { + return "Seq. A"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) { + return "Count D"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) { + return "Count A"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) { + return "RSSI D"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) { + return "RSSI A"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) { + return "Freq. D"; + } + if(order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { + return "Freq. A"; + } + return "Seq. D"; +} diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h new file mode 100644 index 000000000..eaf53b663 --- /dev/null +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_log_item_array.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include + +typedef enum { + SubGhzFrequencyAnalyzerLogOrderBySeqDesc, + SubGhzFrequencyAnalyzerLogOrderBySeqAsc, + SubGhzFrequencyAnalyzerLogOrderByCountDesc, + SubGhzFrequencyAnalyzerLogOrderByCountAsc, + SubGhzFrequencyAnalyzerLogOrderByRSSIDesc, + SubGhzFrequencyAnalyzerLogOrderByRSSIAsc, + SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc, + SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc, +} SubGhzFrequencyAnalyzerLogOrderBy; + +const char* + subghz_frequency_analyzer_log_get_order_name(SubGhzFrequencyAnalyzerLogOrderBy order_by); + +TUPLE_DEF2( + SubGhzFrequencyAnalyzerLogItem, + (seq, uint8_t), + (frequency, uint32_t), + (count, uint8_t), + (rssi_max, uint8_t)) +/* Register globaly the oplist */ +#define M_OPL_SubGhzFrequencyAnalyzerLogItem_t() \ + TUPLE_OPLIST(SubGhzFrequencyAnalyzerLogItem, M_POD_OPLIST, M_DEFAULT_OPLIST, M_DEFAULT_OPLIST) + +/* Define the array, register the oplist and define further algorithms on it */ +ARRAY_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItem_t) +#define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_t() \ + ARRAY_OPLIST(SubGhzFrequencyAnalyzerLogItemArray, M_OPL_SubGhzFrequencyAnalyzerLogItem_t()) +ALGO_DEF(SubGhzFrequencyAnalyzerLogItemArray, SubGhzFrequencyAnalyzerLogItemArray_t) + +FUNC_OBJ_INS_DEF( + SubGhzFrequencyAnalyzerLogItemArray_compare_by /* name of the instance */, + SubGhzFrequencyAnalyzerLogItemArray_cmp_obj /* name of the interface */, + (a, + b) /* name of the input parameters of the function like object. The type are inherited from the interface. */ + , + { + /* code of the function object */ + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { + return a->frequency < b->frequency ? -1 : a->frequency > b->frequency; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByFrequencyDesc) { + return a->frequency > b->frequency ? -1 : a->frequency < b->frequency; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIAsc) { + return a->rssi_max < b->rssi_max ? -1 : a->rssi_max > b->rssi_max; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByRSSIDesc) { + return a->rssi_max > b->rssi_max ? -1 : a->rssi_max < b->rssi_max; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountAsc) { + return a->count < b->count ? -1 : a->count > b->count; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderByCountDesc) { + return a->count > b->count ? -1 : a->count < b->count; + } + if(self->order_by == SubGhzFrequencyAnalyzerLogOrderBySeqAsc) { + return a->seq < b->seq ? -1 : a->seq > b->seq; + } + + return a->seq > b->seq ? -1 : a->seq < b->seq; + }, + /* Additional fields stored in the function object */ + (order_by, SubGhzFrequencyAnalyzerLogOrderBy)) +#define M_OPL_SubGhzFrequencyAnalyzerLogItemArray_compare_by_t() \ + FUNC_OBJ_INS_OPLIST(SubGhzFrequencyAnalyzerLogItemArray_compare_by, M_DEFAULT_OPLIST) diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index c169f3611..e980bd970 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -5,30 +5,53 @@ #include #include #include +#include #include #include "../helpers/subghz_frequency_analyzer_worker.h" +#include "../helpers/subghz_frequency_analyzer_log_item_array.h" #include +#define LOG_FREQUENCY_MAX_ITEMS 60 // uint8_t (limited by 'seq' of SubGhzFrequencyAnalyzerLogItem) +#define RSSI_OFFSET 74 +#define RSSI_MAX 53 // 127 - RSSI_OFFSET + +#define SNPRINTF_FREQUENCY(buff, freq) \ + snprintf(buff, sizeof(buff), "%03ld.%03ld", freq / 1000000 % 1000, freq / 1000 % 1000); + typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, } SubGhzFrequencyAnalyzerStatus; +typedef enum { + SubGhzFrequencyAnalyzerFragmentBottomTypeMain, + SubGhzFrequencyAnalyzerFragmentBottomTypeLog, +} SubGhzFrequencyAnalyzerFragmentBottomType; + struct SubGhzFrequencyAnalyzer { View* view; SubGhzFrequencyAnalyzerWorker* worker; SubGhzFrequencyAnalyzerCallback callback; void* context; bool locked; + uint32_t last_frequency; }; typedef struct { uint32_t frequency; - float rssi; + uint8_t rssi; uint32_t history_frequency[3]; bool signal; + SubGhzFrequencyAnalyzerLogItemArray_t log_frequency; + SubGhzFrequencyAnalyzerFragmentBottomType fragment_bottom_type; + SubGhzFrequencyAnalyzerLogOrderBy log_frequency_order_by; + uint8_t log_frequency_scroll_offset; } SubGhzFrequencyAnalyzerModel; +static inline uint8_t rssi_sanitize(float rssi) { + return (rssi * -1.0f) - RSSI_OFFSET; +} + void subghz_frequency_analyzer_set_callback( SubGhzFrequencyAnalyzer* subghz_frequency_analyzer, SubGhzFrequencyAnalyzerCallback callback, @@ -39,13 +62,11 @@ void subghz_frequency_analyzer_set_callback( subghz_frequency_analyzer->context = context; } -void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) { - uint8_t x = 20; - uint8_t y = 64; +void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { uint8_t column_number = 0; if(rssi) { - rssi = (rssi + 90) / 3; - for(size_t i = 1; i < (uint8_t)rssi; i++) { + rssi = rssi / 3; + for(uint8_t i = 1; i < rssi; i++) { if(i > 20) break; if(i % 4) { column_number++; @@ -55,6 +76,54 @@ void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) { } } +static void subghz_frequency_analyzer_log_frequency_draw( + Canvas* canvas, + SubGhzFrequencyAnalyzerModel* model) { + char buffer[64]; + const uint8_t offset_x = 0; + const uint8_t offset_y = 43; + canvas_set_font(canvas, FontKeyboard); + + const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + if(items_count == 0) { + canvas_draw_rframe(canvas, offset_x + 27u, offset_y - 3u, 73u, 16u, 5u); + canvas_draw_str_aligned( + canvas, offset_x + 64u, offset_y + 8u, AlignCenter, AlignBottom, "No records"); + return; + } else if(items_count > 3) { + elements_scrollbar_pos( + canvas, + offset_x + 127, + offset_y - 8, + 29, + model->log_frequency_scroll_offset, + items_count - 2); + } + + SubGhzFrequencyAnalyzerLogItem_t* log_frequency_item; + for(uint8_t i = 0; i < 3; ++i) { + const uint8_t item_pos = model->log_frequency_scroll_offset + i; + if(item_pos >= items_count) { + break; + } + log_frequency_item = + SubGhzFrequencyAnalyzerLogItemArray_get(model->log_frequency, item_pos); + // Frequency + SNPRINTF_FREQUENCY(buffer, (*log_frequency_item)->frequency) + canvas_draw_str(canvas, offset_x, offset_y + i * 10, buffer); + + // Count + snprintf(buffer, sizeof(buffer), "%3d", (*log_frequency_item)->count); + canvas_draw_str(canvas, offset_x + 48, offset_y + i * 10, buffer); + + // Max RSSI + subghz_frequency_analyzer_draw_rssi( + canvas, (*log_frequency_item)->rssi_max, offset_x + 69, (offset_y + i * 10)); + } + + canvas_set_font(canvas, FontSecondary); +} + static void subghz_frequency_analyzer_history_frequency_draw( Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) { @@ -65,12 +134,7 @@ static void subghz_frequency_analyzer_history_frequency_draw( canvas_set_font(canvas, FontKeyboard); for(uint8_t i = 0; i < 3; i++) { if(model->history_frequency[i]) { - snprintf( - buffer, - sizeof(buffer), - "%03ld.%03ld", - model->history_frequency[i] / 1000000 % 1000, - model->history_frequency[i] / 1000 % 1000); + SNPRINTF_FREQUENCY(buffer, model->history_frequency[i]) canvas_draw_str(canvas, x, y + i * 10, buffer); } else { canvas_draw_str(canvas, x, y + i * 10, "---.---"); @@ -81,18 +145,34 @@ static void subghz_frequency_analyzer_history_frequency_draw( } void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) { + furi_assert(canvas); + furi_assert(model); char buffer[64]; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); - canvas_draw_str(canvas, 0, 64, "RSSI"); - subghz_frequency_analyzer_draw_rssi(canvas, model->rssi); + if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + const char* log_order_by_name = + subghz_frequency_analyzer_log_get_order_name(model->log_frequency_order_by); + if(items_count < LOG_FREQUENCY_MAX_ITEMS) { + snprintf(buffer, sizeof(buffer), "Frequency Analyzer [%s]", log_order_by_name); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignBottom, buffer); + } else { + snprintf(buffer, sizeof(buffer), "The log is full! [%s]", log_order_by_name); + canvas_draw_str(canvas, 2, 8, buffer); + } + subghz_frequency_analyzer_log_frequency_draw(canvas, model); + } else { + canvas_draw_str(canvas, 20, 8, "Frequency Analyzer"); + canvas_draw_str(canvas, 0, 64, "RSSI"); + subghz_frequency_analyzer_draw_rssi(canvas, model->rssi, 20u, 64u); - subghz_frequency_analyzer_history_frequency_draw(canvas, model); + subghz_frequency_analyzer_history_frequency_draw(canvas, model); + } - //Frequency + // Frequency canvas_set_font(canvas, FontBigNumbers); snprintf( buffer, @@ -103,23 +183,151 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel if(model->signal) { canvas_draw_box(canvas, 4, 12, 121, 22); canvas_set_color(canvas, ColorWhite); - } else { } - canvas_draw_str(canvas, 8, 30, buffer); canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11); } +static void subghz_frequency_analyzer_log_frequency_sort(SubGhzFrequencyAnalyzerModel* model) { + furi_assert(model); + M_LET((cmp, model->log_frequency_order_by), SubGhzFrequencyAnalyzerLogItemArray_compare_by_t) + SubGhzFrequencyAnalyzerLogItemArray_sort_fo( + model->log_frequency, SubGhzFrequencyAnalyzerLogItemArray_compare_by_as_interface(cmp)); +} + bool subghz_frequency_analyzer_input(InputEvent* event, void* context) { furi_assert(context); + SubGhzFrequencyAnalyzer* instance = context; if(event->key == InputKeyBack) { return false; } + if((event->type == InputTypeShort) && + ((event->key == InputKeyLeft) || (event->key == InputKeyRight))) { + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + if(event->key == InputKeyLeft) { + if(model->fragment_bottom_type == 0) { + model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeLog; + } else { + --model->fragment_bottom_type; + } + } else if(event->key == InputKeyRight) { + if(model->fragment_bottom_type == + SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + model->fragment_bottom_type = 0; + } else { + ++model->fragment_bottom_type; + } + } + }, + true); + } else if((event->type == InputTypeShort) && (event->key == InputKeyOk)) { + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + ++model->log_frequency_order_by; + if(model->log_frequency_order_by > + SubGhzFrequencyAnalyzerLogOrderByFrequencyAsc) { + model->log_frequency_order_by = 0; + } + subghz_frequency_analyzer_log_frequency_sort(model); + } + }, + true); + } else if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) { + with_view_model( + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + if(model->fragment_bottom_type == SubGhzFrequencyAnalyzerFragmentBottomTypeLog) { + if(event->key == InputKeyUp) { + if(model->log_frequency_scroll_offset > 0) { + --model->log_frequency_scroll_offset; + } + } else if(event->key == InputKeyDown) { + const size_t items_count = + SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + if((model->log_frequency_scroll_offset + 3u) < items_count) { + ++model->log_frequency_scroll_offset; + } + } + } + }, + true); + } + return true; } +static void subghz_frequency_analyzer_log_frequency_search_it( + SubGhzFrequencyAnalyzerLogItemArray_it_t* itref, + SubGhzFrequencyAnalyzerLogItemArray_t* log_frequency, + uint32_t frequency) { + furi_assert(log_frequency); + + SubGhzFrequencyAnalyzerLogItemArray_it(*itref, *log_frequency); + SubGhzFrequencyAnalyzerLogItem_t* item; + while(!SubGhzFrequencyAnalyzerLogItemArray_end_p(*itref)) { + item = SubGhzFrequencyAnalyzerLogItemArray_ref(*itref); + if((*item)->frequency == frequency) { + break; + } + SubGhzFrequencyAnalyzerLogItemArray_next(*itref); + } +} + +static bool subghz_frequency_analyzer_log_frequency_insert(SubGhzFrequencyAnalyzerModel* model) { + furi_assert(model); + const size_t items_count = SubGhzFrequencyAnalyzerLogItemArray_size(model->log_frequency); + if(items_count < LOG_FREQUENCY_MAX_ITEMS) { + SubGhzFrequencyAnalyzerLogItem_t* item = + SubGhzFrequencyAnalyzerLogItemArray_push_new(model->log_frequency); + if(item == NULL) { + return false; + } + (*item)->frequency = model->frequency; + (*item)->count = 1u; + (*item)->rssi_max = model->rssi; + (*item)->seq = items_count; + return true; + } + return false; +} + +static void subghz_frequency_analyzer_log_frequency_update( + SubGhzFrequencyAnalyzerModel* model, + bool need_insert) { + furi_assert(model); + if(!model->frequency) { + return; + } + + SubGhzFrequencyAnalyzerLogItemArray_it_t it; + subghz_frequency_analyzer_log_frequency_search_it( + &it, &model->log_frequency, model->frequency); + if(!SubGhzFrequencyAnalyzerLogItemArray_end_p(it)) { + SubGhzFrequencyAnalyzerLogItem_t* item = SubGhzFrequencyAnalyzerLogItemArray_ref(it); + if((*item)->rssi_max < model->rssi) { + (*item)->rssi_max = model->rssi; + } + + if(need_insert && (*item)->count < UINT8_MAX) { + ++(*item)->count; + subghz_frequency_analyzer_log_frequency_sort(model); + } + } else if(need_insert) { + if(subghz_frequency_analyzer_log_frequency_insert(model)) { + subghz_frequency_analyzer_log_frequency_sort(model); + } + } +} + void subghz_frequency_analyzer_pair_callback( void* context, uint32_t frequency, @@ -130,6 +338,7 @@ void subghz_frequency_analyzer_pair_callback( if(instance->callback) { instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context); } + instance->last_frequency = 0; //update history with_view_model( instance->view, @@ -151,9 +360,14 @@ void subghz_frequency_analyzer_pair_callback( instance->view, SubGhzFrequencyAnalyzerModel * model, { - model->rssi = rssi; + model->rssi = rssi_sanitize(rssi); model->frequency = frequency; model->signal = signal; + if(frequency) { + subghz_frequency_analyzer_log_frequency_update( + model, frequency != instance->last_frequency); + instance->last_frequency = frequency; + } }, true); } @@ -176,11 +390,14 @@ void subghz_frequency_analyzer_enter(void* context) { instance->view, SubGhzFrequencyAnalyzerModel * model, { - model->rssi = 0; + model->rssi = 0u; model->frequency = 0; - model->history_frequency[2] = 0; - model->history_frequency[1] = 0; - model->history_frequency[0] = 0; + model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; + model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; + model->log_frequency_scroll_offset = 0u; + model->history_frequency[0] = model->history_frequency[1] = + model->history_frequency[2] = 0u; + SubGhzFrequencyAnalyzerLogItemArray_init(model->log_frequency); }, true); } @@ -196,13 +413,26 @@ void subghz_frequency_analyzer_exit(void* context) { subghz_frequency_analyzer_worker_free(instance->worker); with_view_model( - instance->view, SubGhzFrequencyAnalyzerModel * model, { model->rssi = 0; }, true); + instance->view, + SubGhzFrequencyAnalyzerModel * model, + { + model->rssi = 0u; + model->frequency = 0; + model->fragment_bottom_type = SubGhzFrequencyAnalyzerFragmentBottomTypeMain; + model->log_frequency_order_by = SubGhzFrequencyAnalyzerLogOrderBySeqDesc; + model->log_frequency_scroll_offset = 0u; + model->history_frequency[0] = model->history_frequency[1] = + model->history_frequency[2] = 0u; + SubGhzFrequencyAnalyzerLogItemArray_clear(model->log_frequency); + }, + true); } SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() { SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer)); // View allocation and configuration + instance->last_frequency = 0; instance->view = view_alloc(); view_allocate_model( instance->view, ViewModelTypeLocking, sizeof(SubGhzFrequencyAnalyzerModel));