From ce8c491291d48942f30cc04bae3c86a43d8b6069 Mon Sep 17 00:00:00 2001 From: Eng1n33r Date: Mon, 6 Jun 2022 17:20:46 +0300 Subject: [PATCH] add spectrum analyzer --- applications/applications.c | 9 + applications/applications.mk | 6 + .../spectrum_analyzer/spectrum_analyzer.c | 459 ++++++++++++++++++ .../spectrum_analyzer/spectrum_analyzer.h | 66 +++ .../spectrum_analyzer_worker.c | 195 ++++++++ .../spectrum_analyzer_worker.h | 33 ++ assets/compiled/assets_icons.c | 9 + assets/compiled/assets_icons.h | 1 + .../MainMenu/SpectrumAnalyzer_14/frame_0.png | Bin 0 -> 173 bytes .../MainMenu/SpectrumAnalyzer_14/frame_1.png | Bin 0 -> 168 bytes .../MainMenu/SpectrumAnalyzer_14/frame_2.png | Bin 0 -> 157 bytes .../MainMenu/SpectrumAnalyzer_14/frame_3.png | Bin 0 -> 114 bytes .../MainMenu/SpectrumAnalyzer_14/frame_4.png | Bin 0 -> 121 bytes .../MainMenu/SpectrumAnalyzer_14/frame_5.png | Bin 0 -> 150 bytes .../MainMenu/SpectrumAnalyzer_14/frame_rate | 1 + assets/resources/Manifest | 4 +- assets/resources/subghz/assets/setting_user | 2 - 17 files changed, 781 insertions(+), 4 deletions(-) create mode 100644 applications/spectrum_analyzer/spectrum_analyzer.c create mode 100644 applications/spectrum_analyzer/spectrum_analyzer.h create mode 100644 applications/spectrum_analyzer/spectrum_analyzer_worker.c create mode 100644 applications/spectrum_analyzer/spectrum_analyzer_worker.h create mode 100644 assets/icons/MainMenu/SpectrumAnalyzer_14/frame_0.png create mode 100644 assets/icons/MainMenu/SpectrumAnalyzer_14/frame_1.png create mode 100644 assets/icons/MainMenu/SpectrumAnalyzer_14/frame_2.png create mode 100644 assets/icons/MainMenu/SpectrumAnalyzer_14/frame_3.png create mode 100644 assets/icons/MainMenu/SpectrumAnalyzer_14/frame_4.png create mode 100644 assets/icons/MainMenu/SpectrumAnalyzer_14/frame_5.png create mode 100644 assets/icons/MainMenu/SpectrumAnalyzer_14/frame_rate diff --git a/applications/applications.c b/applications/applications.c index 0b1278647..247c601c5 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -51,6 +51,7 @@ extern int32_t music_player_app(void* p); extern int32_t wav_player_app(void* p); extern int32_t clock_app(void *p); extern int32_t unirfremix_app(void *p); +extern int32_t spectrum_analyzer_app(void* p); // Games extern int32_t snake_game_app(void* p); @@ -236,6 +237,14 @@ const FlipperApplication FLIPPER_APPS[] = { .flags = FlipperApplicationFlagDefault}, #endif +#ifdef APP_SPECTRUM_ANALYZER + {.app = spectrum_analyzer_app, + .name = "Spectrum Analyzer", + .stack_size = 1024, + .icon = &A_SpectrumAnalyzer_14, + .flags = FlipperApplicationFlagDefault}, +#endif + #ifdef APP_LF_RFID {.app = lfrfid_app, .name = "125 kHz RFID", diff --git a/applications/applications.mk b/applications/applications.mk index 8cefbd3e4..60a339bdf 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -47,6 +47,7 @@ APP_UPDATER = 1 # Custom apps APP_UNIRFREMIX = 1 APP_CLOCK = 1 +APP_SPECTRUM_ANALYZER = 1 # Plugins APP_MUSIC_PLAYER = 1 @@ -271,6 +272,11 @@ SRV_GUI = 1 SRV_CLI = 0 endif +APP_SPECTRUM_ANALYZER ?= 0 +ifeq ($(APP_SPECTRUM_ANALYZER), 1) +CFLAGS += -DAPP_SPECTRUM_ANALYZER +SRV_GUI = 1 +endif APP_IBUTTON ?= 0 ifeq ($(APP_IBUTTON), 1) diff --git a/applications/spectrum_analyzer/spectrum_analyzer.c b/applications/spectrum_analyzer/spectrum_analyzer.c new file mode 100644 index 000000000..508e3ea62 --- /dev/null +++ b/applications/spectrum_analyzer/spectrum_analyzer.c @@ -0,0 +1,459 @@ +#include +#include + +#include +#include +#include +#include "spectrum_analyzer.h" + +#include + +#include +#include "spectrum_analyzer_worker.h" + +typedef struct { + uint16_t center_freq; + uint8_t width; + uint8_t band; + uint8_t vscroll; + + uint32_t channel0_frequency; + uint32_t spacing; + + float max_rssi; + uint8_t max_rssi_dec; + uint8_t max_rssi_channel; + uint8_t channel_ss[NUM_CHANNELS]; +} SpectrumAnalyzerModel; + +typedef struct { + SpectrumAnalyzerModel* model; + osMutexId_t* model_mutex; + + osMessageQueueId_t event_queue; + + ViewPort* view_port; + Gui* gui; + + SpectrumAnalyzerWorker* worker; +} SpectrumAnalyzer; + +void spectrum_analyzer_draw_scale(Canvas* canvas, const SpectrumAnalyzerModel* model) { + // Draw line + canvas_draw_line( + canvas, FREQ_START_X, FREQ_BOTTOM_Y, FREQ_START_X + FREQ_LENGTH_X, FREQ_BOTTOM_Y); + // Draw minor scale + for(int i = FREQ_START_X; i < FREQ_START_X + FREQ_LENGTH_X; i += 5) { + canvas_draw_line(canvas, i, FREQ_BOTTOM_Y, i, FREQ_BOTTOM_Y + 2); + } + // Draw major scale + for(int i = FREQ_START_X; i < FREQ_START_X + FREQ_LENGTH_X; i += 25) { + canvas_draw_line(canvas, i, FREQ_BOTTOM_Y, i, FREQ_BOTTOM_Y + 4); + } + + // Draw scale tags + uint16_t tag_left; + uint16_t tag_center; + uint16_t tag_right; + char temp_str[18]; + + tag_center = model->center_freq; + + switch(model->width) { + case NARROW: + tag_left = model->center_freq - 2; + tag_right = model->center_freq + 2; + break; + case ULTRAWIDE: + tag_left = model->center_freq - 40; + tag_right = model->center_freq + 40; + break; + default: + tag_left = model->center_freq - 10; + tag_right = model->center_freq + 10; + } + + canvas_set_font(canvas, FontSecondary); + snprintf(temp_str, 18, "%u", tag_left); + canvas_draw_str_aligned(canvas, FREQ_START_X, 63, AlignCenter, AlignBottom, temp_str); + snprintf(temp_str, 18, "%u", tag_center); + canvas_draw_str_aligned(canvas, 128 / 2, 63, AlignCenter, AlignBottom, temp_str); + snprintf(temp_str, 18, "%u", tag_right); + canvas_draw_str_aligned( + canvas, FREQ_START_X + FREQ_LENGTH_X - 1, 63, AlignCenter, AlignBottom, temp_str); +} + +static void spectrum_analyzer_render_callback(Canvas* const canvas, void* ctx) { + SpectrumAnalyzer* spectrum_analyzer = ctx; + furi_check(osMutexAcquire(spectrum_analyzer->model_mutex, osWaitForever) == osOK); + + SpectrumAnalyzerModel* model = spectrum_analyzer->model; + + spectrum_analyzer_draw_scale(canvas, model); + + for(uint8_t column = 0; column < 128; column++) { + uint8_t ss = model->channel_ss[column + 2]; + // Compress height to max of 64 values (255>>2) + uint8_t s = MAX((ss - model->vscroll) >> 2, 0); + uint8_t y = FREQ_BOTTOM_Y - s; // bar height + + // Draw each bar + canvas_draw_line(canvas, column, FREQ_BOTTOM_Y, column, y); + } + + // Draw cross and label + if(model->max_rssi > PEAK_THRESHOLD) { + // Compress height to max of 64 values (255>>2) + uint8_t max_y = MAX((model->max_rssi_dec - model->vscroll) >> 2, 0); + max_y = (FREQ_BOTTOM_Y - max_y); + + // Cross + int16_t x1, x2, y1, y2; + x1 = model->max_rssi_channel - 2 - 2; + if(x1 < 0) x1 = 0; + y1 = max_y - 2; + if(y1 < 0) y1 = 0; + x2 = model->max_rssi_channel - 2 + 2; + if(x2 > 127) x2 = 127; + y2 = max_y + 2; + if(y2 > 63) y2 = 63; // SHOULD NOT HAPPEN CHECK! + canvas_draw_line(canvas, x1, y1, x2, y2); + + x1 = model->max_rssi_channel - 2 + 2; + if(x1 > 127) x1 = 127; + y1 = max_y - 2; + if(y1 < 0) y1 = 0; + x2 = model->max_rssi_channel - 2 - 2; + if(x2 < 0) x2 = 0; + y2 = max_y + 2; + if(y2 > 63) y2 = 63; // SHOULD NOT HAPPEN CHECK! + canvas_draw_line(canvas, (uint8_t)x1, (uint8_t)y1, (uint8_t)x2, (uint8_t)y2); + + // Label + char temp_str[36]; + snprintf( + temp_str, + 36, + "Peak: %3.2f Mhz %3.1f dbm", + ((double)(model->channel0_frequency + (model->max_rssi_channel * model->spacing)) / + 1000000), + (double) model->max_rssi); + canvas_draw_str_aligned(canvas, 127, 0, AlignRight, AlignTop, temp_str); + } + + osMutexRelease(spectrum_analyzer->model_mutex); + + // FURI_LOG_D("Spectrum", "model->vscroll %u", model->vscroll); +} + +static void spectrum_analyzer_input_callback(InputEvent* input_event, void* ctx) { + SpectrumAnalyzer* spectrum_analyzer = ctx; + // Only handle short presses + if(input_event->type == InputTypeShort) { + osMessageQueuePut(spectrum_analyzer->event_queue, input_event, 0, osWaitForever); + } +} + +static void spectrum_analyzer_worker_callback( + void* channel_ss, + float max_rssi, + uint8_t max_rssi_dec, + uint8_t max_rssi_channel, + void* context) { + SpectrumAnalyzer* spectrum_analyzer = context; + furi_check(osMutexAcquire(spectrum_analyzer->model_mutex, osWaitForever) == osOK); + + SpectrumAnalyzerModel* model = (SpectrumAnalyzerModel*)spectrum_analyzer->model; + memcpy(model->channel_ss, (uint8_t*)channel_ss, sizeof(uint8_t) * NUM_CHANNELS); + model->max_rssi = max_rssi; + model->max_rssi_dec = max_rssi_dec; + model->max_rssi_channel = max_rssi_channel; + + osMutexRelease(spectrum_analyzer->model_mutex); + view_port_update(spectrum_analyzer->view_port); +} + +void spectrum_analyzer_calculate_frequencies(SpectrumAnalyzerModel* model) { + // REDO ALL THIS. CALCULATE ONLY WITH SPACING! + + uint8_t new_band; + uint32_t min_hz; + uint32_t max_hz; + uint8_t margin; + uint8_t step; + uint16_t upper_limit; + uint16_t lower_limit; + uint16_t next_up; + uint16_t next_down; + uint8_t next_band_up; + uint8_t next_band_down; + + switch(model->width) { + case NARROW: + margin = NARROW_MARGIN; + step = NARROW_STEP; + model->spacing = NARROW_SPACING; + break; + case ULTRAWIDE: + margin = ULTRAWIDE_MARGIN; + step = ULTRAWIDE_STEP; + model->spacing = ULTRAWIDE_SPACING; + /* nearest 20 MHz step */ + model->center_freq = ((model->center_freq + 10) / 20) * 20; + break; + default: + margin = WIDE_MARGIN; + step = WIDE_STEP; + model->spacing = WIDE_SPACING; + /* nearest 5 MHz step */ + model->center_freq = ((model->center_freq + 2) / 5) * 5; + break; + } + + /* handle cases near edges of bands */ + if(model->center_freq > EDGE_900) { + new_band = BAND_900; + upper_limit = UPPER(MAX_900, margin, step); + lower_limit = LOWER(MIN_900, margin, step); + next_up = LOWER(MIN_300, margin, step); + next_down = UPPER(MAX_400, margin, step); + next_band_up = BAND_300; + next_band_down = BAND_400; + } else if(model->center_freq > EDGE_400) { + new_band = BAND_400; + upper_limit = UPPER(MAX_400, margin, step); + lower_limit = LOWER(MIN_400, margin, step); + next_up = LOWER(MIN_900, margin, step); + next_down = UPPER(MAX_300, margin, step); + next_band_up = BAND_900; + next_band_down = BAND_300; + } else { + new_band = BAND_300; + upper_limit = UPPER(MAX_300, margin, step); + lower_limit = LOWER(MIN_300, margin, step); + next_up = LOWER(MIN_400, margin, step); + next_down = UPPER(MAX_900, margin, step); + next_band_up = BAND_400; + next_band_down = BAND_900; + } + + if(model->center_freq > upper_limit) { + model->center_freq = upper_limit; + if(new_band == model->band) { + new_band = next_band_up; + model->center_freq = next_up; + } + } else if(model->center_freq < lower_limit) { + model->center_freq = lower_limit; + if(new_band == model->band) { + new_band = next_band_down; + model->center_freq = next_down; + } + } + + model->band = new_band; + /* doing everything in Hz from here on */ + switch(model->band) { + case BAND_400: + min_hz = MIN_400 * 1000000; + max_hz = MAX_400 * 1000000; + break; + case BAND_300: + min_hz = MIN_300 * 1000000; + max_hz = MAX_300 * 1000000; + break; + default: + min_hz = MIN_900 * 1000000; + max_hz = MAX_900 * 1000000; + break; + } + + model->channel0_frequency = + model->center_freq * 1000000 - (model->spacing * ((NUM_CHANNELS / 2) + 1)); + + // /* calibrate upper channels */ + // hz = model->center_freq * 1000000; + // max_chan = NUM_CHANNELS / 2; + // while (hz <= max_hz && max_chan < NUM_CHANNELS) { + // instance->chan_table[max_chan].frequency = hz; + // FURI_LOG_T("Spectrum", "calibrate_freq ch[%u]: %lu", max_chan, hz); + // hz += model->spacing; + // max_chan++; + // } + + // /* calibrate lower channels */ + // hz = instance->freq * 1000000 - model->spacing; + // min_chan = NUM_CHANNELS / 2; + // while (hz >= min_hz && min_chan > 0) { + // min_chan--; + // instance->chan_table[min_chan].frequency = hz; + // FURI_LOG_T("Spectrum", "calibrate_freq ch[%u]: %lu", min_chan, hz); + // hz -= model->spacing; + // } + + model->max_rssi = -200.0; + model->max_rssi_dec = 0; + + FURI_LOG_D("Spectrum", "setup_frequencies - max_hz: %u - min_hz: %u", max_hz, min_hz); + FURI_LOG_D("Spectrum", "center_freq: %u", model->center_freq); + FURI_LOG_D( + "Spectrum", + "ch[0]: %lu - ch[%u]: %lu", + model->channel0_frequency, + NUM_CHANNELS - 1, + model->channel0_frequency + ((NUM_CHANNELS - 1) * model->spacing)); +} + +SpectrumAnalyzer* spectrum_analyzer_alloc() { + SpectrumAnalyzer* instance = malloc(sizeof(SpectrumAnalyzer)); + instance->model = malloc(sizeof(SpectrumAnalyzerModel)); + + SpectrumAnalyzerModel* model = instance->model; + + for(uint8_t ch = 0; ch < NUM_CHANNELS - 1; ch++) { + model->channel_ss[ch] = 0; + } + model->max_rssi_dec = 0; + model->max_rssi_channel = 0; + model->max_rssi = PEAK_THRESHOLD - 1; // Should initializar to < PEAK_THRESHOLD + + model->center_freq = DEFAULT_FREQ; + model->width = WIDE; + model->band = BAND_400; + + model->vscroll = DEFAULT_VSCROLL; + + instance->model_mutex = osMutexNew(NULL); + instance->event_queue = osMessageQueueNew(8, sizeof(InputEvent), NULL); + + instance->worker = spectrum_analyzer_worker_alloc(); + + spectrum_analyzer_worker_set_callback( + instance->worker, spectrum_analyzer_worker_callback, instance); + + // Set system callbacks + instance->view_port = view_port_alloc(); + view_port_draw_callback_set(instance->view_port, spectrum_analyzer_render_callback, instance); + view_port_input_callback_set(instance->view_port, spectrum_analyzer_input_callback, instance); + + // Open GUI and register view_port + instance->gui = furi_record_open("gui"); + gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); + + return instance; +} + +void spectrum_analyzer_free(SpectrumAnalyzer* instance) { + // view_port_enabled_set(view_port, false); + gui_remove_view_port(instance->gui, instance->view_port); + furi_record_close("gui"); + view_port_free(instance->view_port); + + spectrum_analyzer_worker_free(instance->worker); + + osMessageQueueDelete(instance->event_queue); + + osMutexDelete(instance->model_mutex); + + free(instance->model); + free(instance); + + furi_hal_subghz_idle(); + furi_hal_subghz_sleep(); +} + +int32_t spectrum_analyzer_app(void* p) { + UNUSED(p); + + SpectrumAnalyzer* spectrum_analyzer = spectrum_analyzer_alloc(); + InputEvent input; + + FURI_LOG_D("Spectrum", "Main Loop - Starting worker"); + furi_hal_delay_ms(50); + + spectrum_analyzer_worker_start(spectrum_analyzer->worker); + + FURI_LOG_D("Spectrum", "Main Loop - Wait on queue"); + furi_hal_delay_ms(50); + + while(osMessageQueueGet(spectrum_analyzer->event_queue, &input, NULL, osWaitForever) == osOK) { + furi_check(osMutexAcquire(spectrum_analyzer->model_mutex, osWaitForever) == osOK); + + FURI_LOG_D("Spectrum", "Main Loop - Input: %u", input.key); + + SpectrumAnalyzerModel* model = spectrum_analyzer->model; + + uint8_t vstep = VERTICAL_SHORT_STEP; + uint8_t hstep; + + bool exit_loop = false; + + switch(model->width) { + case NARROW: + hstep = NARROW_STEP; + break; + case ULTRAWIDE: + hstep = ULTRAWIDE_STEP; + break; + default: + hstep = WIDE_STEP; + break; + } + + switch(input.key) { + case InputKeyUp: + model->vscroll = MAX(model->vscroll - vstep, MIN_VSCROLL); + FURI_LOG_D("Spectrum", "Vscroll: %u", model->vscroll); + break; + case InputKeyDown: + model->vscroll = MIN(model->vscroll + vstep, MAX_VSCROLL); + FURI_LOG_D("Spectrum", "Vscroll: %u", model->vscroll); + break; + case InputKeyRight: + model->center_freq += hstep; + FURI_LOG_D("Spectrum", "center_freq: %lu", model->center_freq); + spectrum_analyzer_calculate_frequencies(model); + spectrum_analyzer_worker_set_frequencies( + spectrum_analyzer->worker, model->channel0_frequency, model->spacing, model->width); + break; + case InputKeyLeft: + model->center_freq -= hstep; + spectrum_analyzer_calculate_frequencies(model); + spectrum_analyzer_worker_set_frequencies( + spectrum_analyzer->worker, model->channel0_frequency, model->spacing, model->width); + FURI_LOG_D("Spectrum", "center_freq: %lu", model->center_freq); + break; + case InputKeyOk: { + switch(model->width) { + case WIDE: + model->width = NARROW; + break; + case NARROW: + model->width = ULTRAWIDE; + break; + case ULTRAWIDE: + default: + model->width = WIDE; + } + } + spectrum_analyzer_calculate_frequencies(model); + spectrum_analyzer_worker_set_frequencies( + spectrum_analyzer->worker, model->channel0_frequency, model->spacing, model->width); + FURI_LOG_D("Spectrum", "Width: %u", model->width); + break; + case InputKeyBack: + exit_loop = true; + break; + } + + osMutexRelease(spectrum_analyzer->model_mutex); + view_port_update(spectrum_analyzer->view_port); + if(exit_loop == true) break; + } + + spectrum_analyzer_worker_stop(spectrum_analyzer->worker); + + spectrum_analyzer_free(spectrum_analyzer); + + return 0; +} \ No newline at end of file diff --git a/applications/spectrum_analyzer/spectrum_analyzer.h b/applications/spectrum_analyzer/spectrum_analyzer.h new file mode 100644 index 000000000..74e135fcf --- /dev/null +++ b/applications/spectrum_analyzer/spectrum_analyzer.h @@ -0,0 +1,66 @@ +#define NUM_CHANNELS 132 + +// Screen coordinates +#define FREQ_BOTTOM_Y 50 +#define FREQ_START_X 14 +// How many channels displayed on the scale (On screen still 218) +#define FREQ_LENGTH_X 102 +// dBm threshold to show peak value +#define PEAK_THRESHOLD -85 + +/* + * ultrawide mode: 80 MHz on screen, 784 kHz per channel + * wide mode (default): 20 MHz on screen, 196 kHz per channel + * narrow mode: 4 MHz on screen, 39 kHz per channel + */ +#define WIDE 0 +#define NARROW 1 +#define ULTRAWIDE 2 + +/* channel spacing in Hz */ +#define WIDE_SPACING 196078 +#define NARROW_SPACING 39215 +#define ULTRAWIDE_SPACING 784313 + +/* vertical scrolling */ +#define VERTICAL_SHORT_STEP 16 +#define MAX_VSCROLL 120 +#define MIN_VSCROLL 0 +#define DEFAULT_VSCROLL 48 + +/* frequencies in MHz */ +#define DEFAULT_FREQ 440 +#define WIDE_STEP 5 +#define NARROW_STEP 1 +#define ULTRAWIDE_STEP 20 +#define WIDE_MARGIN 13 +#define NARROW_MARGIN 3 +#define ULTRAWIDE_MARGIN 42 + +/* frequency bands supported by device */ +#define BAND_300 0 +#define BAND_400 1 +#define BAND_900 2 + +/* band limits in MHz */ +#define MIN_300 281 +#define CEN_300 315 +#define MAX_300 361 +#define MIN_400 378 +#define CEN_400 435 +#define MAX_400 481 +#define MIN_900 749 +#define CEN_900 855 +#define MAX_900 962 + +/* band transition points in MHz */ +#define EDGE_400 369 +#define EDGE_900 615 + +/* VCO transition points in Hz */ +#define MID_300 318000000 +#define MID_400 424000000 +#define MID_900 848000000 + +#define UPPER(a, b, c) ((((a) - (b) + ((c) / 2)) / (c)) * (c)) +#define LOWER(a, b, c) ((((a) + (b)) / (c)) * (c)) diff --git a/applications/spectrum_analyzer/spectrum_analyzer_worker.c b/applications/spectrum_analyzer/spectrum_analyzer_worker.c new file mode 100644 index 000000000..9bf3c1085 --- /dev/null +++ b/applications/spectrum_analyzer/spectrum_analyzer_worker.c @@ -0,0 +1,195 @@ +#include "spectrum_analyzer.h" +#include "spectrum_analyzer_worker.h" + +#include +#include + +#include + +struct SpectrumAnalyzerWorker { + FuriThread* thread; + bool should_work; + + SpectrumAnalyzerWorkerCallback callback; + void* callback_context; + + uint32_t channel0_frequency; + uint32_t spacing; + uint8_t width; + float max_rssi; + uint8_t max_rssi_dec; + uint8_t max_rssi_channel; + + uint8_t channel_ss[NUM_CHANNELS]; +}; + +/* set the channel bandwidth */ +void spectrum_analyzer_worker_set_filter(SpectrumAnalyzerWorker* instance) { + uint8_t filter_config[2][2] = { + {CC1101_MDMCFG4, 0}, + {0, 0}, + }; + + // FURI_LOG_D("SpectrumWorker", "spectrum_analyzer_worker_set_filter: width = %u", instance->width); + + /* channel spacing should fit within 80% of channel filter bandwidth */ + switch(instance->width) { + case NARROW: + filter_config[0][1] = 0xFC; /* 39.2 kHz / .8 = 49 kHz --> 58 kHz */ + break; + case ULTRAWIDE: + filter_config[0][1] = 0x0C; /* 784 kHz / .8 = 980 kHz --> 812 kHz */ + break; + default: + filter_config[0][1] = 0x6C; /* 196 kHz / .8 = 245 kHz --> 270 kHz */ + break; + } + furi_hal_subghz_load_registers(filter_config); +} + +static int32_t spectrum_analyzer_worker_thread(void* context) { + furi_assert(context); + SpectrumAnalyzerWorker* instance = context; + + FURI_LOG_D("SpectrumWorker", "spectrum_analyzer_worker_thread: Start"); + + // Start CC1101 + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); + furi_hal_subghz_set_frequency(433920000); + furi_hal_subghz_flush_rx(); + furi_hal_subghz_rx(); + + static const uint8_t radio_config[][2] = { + {CC1101_FSCTRL1, 0x12}, + {CC1101_FSCTRL0, 0x00}, + + {CC1101_AGCCTRL2, 0xC0}, + + {CC1101_MDMCFG4, 0x6C}, + {CC1101_TEST2, 0x88}, + {CC1101_TEST1, 0x31}, + {CC1101_TEST0, 0x09}, + /* End */ + {0, 0}, + }; + + while(instance->should_work) { + furi_hal_delay_ms(50); + + // FURI_LOG_T("SpectrumWorker", "spectrum_analyzer_worker_thread: Worker Loop"); + furi_hal_subghz_idle(); + furi_hal_subghz_load_registers(radio_config); + + // TODO: Check filter! + // spectrum_analyzer_worker_set_filter(instance); + + instance->max_rssi_dec = 0; + + for(uint8_t ch = 0; ch < NUM_CHANNELS - 1; ch++) { + furi_hal_subghz_set_frequency(instance->channel0_frequency + (ch * instance->spacing)); + + furi_hal_subghz_rx(); + furi_hal_delay_ms(3); + + // dec dBm + //max_ss = 127 -> -10.5 + //max_ss = 0 -> -74.0 + //max_ss = 255 -> -74.5 + //max_ss = 128 -> -138.0 + instance->channel_ss[ch] = (furi_hal_subghz_get_rssi() + 138) * 2; + + if(instance->channel_ss[ch] > instance->max_rssi_dec) { + instance->max_rssi_dec = instance->channel_ss[ch]; + instance->max_rssi = (instance->channel_ss[ch] / 2) - 138; + instance->max_rssi_channel = ch; + } + + furi_hal_subghz_idle(); + } + + // FURI_LOG_T("SpectrumWorker", "channel_ss[0]: %u", instance->channel_ss[0]); + + // Report results back to main thread + if(instance->callback) { + instance->callback( + (void*)&(instance->channel_ss), + instance->max_rssi, + instance->max_rssi_dec, + instance->max_rssi_channel, + instance->callback_context); + } + } + + return 0; +} + +SpectrumAnalyzerWorker* spectrum_analyzer_worker_alloc() { + FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_alloc: Start"); + + SpectrumAnalyzerWorker* instance = malloc(sizeof(SpectrumAnalyzerWorker)); + + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "SpectrumWorker"); + furi_thread_set_stack_size(instance->thread, 2048); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_callback(instance->thread, spectrum_analyzer_worker_thread); + + FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_alloc: End"); + + return instance; +} + +void spectrum_analyzer_worker_free(SpectrumAnalyzerWorker* instance) { + FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_free"); + furi_assert(instance); + furi_thread_free(instance->thread); + free(instance); +} + +void spectrum_analyzer_worker_set_callback( + SpectrumAnalyzerWorker* instance, + SpectrumAnalyzerWorkerCallback callback, + void* context) { + furi_assert(instance); + instance->callback = callback; + instance->callback_context = context; +} + +void spectrum_analyzer_worker_set_frequencies( + SpectrumAnalyzerWorker* instance, + uint32_t channel0_frequency, + uint32_t spacing, + uint8_t width) { + furi_assert(instance); + + FURI_LOG_D( + "SpectrumWorker", + "spectrum_analyzer_worker_set_frequencies - channel0_frequency= %u - spacing = %u - width = %u", + channel0_frequency, + spacing, + width); + + instance->channel0_frequency = channel0_frequency; + instance->spacing = spacing; + instance->width = width; +} + +void spectrum_analyzer_worker_start(SpectrumAnalyzerWorker* instance) { + FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_start"); + + furi_assert(instance); + furi_assert(instance->should_work == false); + + instance->should_work = true; + furi_thread_start(instance->thread); +} + +void spectrum_analyzer_worker_stop(SpectrumAnalyzerWorker* instance) { + FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_stop"); + furi_assert(instance); + furi_assert(instance->should_work == true); + + instance->should_work = false; + furi_thread_join(instance->thread); +} \ No newline at end of file diff --git a/applications/spectrum_analyzer/spectrum_analyzer_worker.h b/applications/spectrum_analyzer/spectrum_analyzer_worker.h new file mode 100644 index 000000000..ca051dacc --- /dev/null +++ b/applications/spectrum_analyzer/spectrum_analyzer_worker.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +typedef void (*SpectrumAnalyzerWorkerCallback)( + void* chan_table, + float max_rssi, + uint8_t max_rssi_dec, + uint8_t max_rssi_channel, + void* context); + +typedef struct SpectrumAnalyzerWorker SpectrumAnalyzerWorker; + +SpectrumAnalyzerWorker* spectrum_analyzer_worker_alloc(); + +void spectrum_analyzer_worker_free(SpectrumAnalyzerWorker* instance); + +void spectrum_analyzer_worker_set_callback( + SpectrumAnalyzerWorker* instance, + SpectrumAnalyzerWorkerCallback callback, + void* context); + +void spectrum_analyzer_worker_set_filter(SpectrumAnalyzerWorker* instance); + +void spectrum_analyzer_worker_set_frequencies( + SpectrumAnalyzerWorker* instance, + uint32_t channel0_frequency, + uint32_t spacing, + uint8_t width); + +void spectrum_analyzer_worker_start(SpectrumAnalyzerWorker* instance); + +void spectrum_analyzer_worker_stop(SpectrumAnalyzerWorker* instance); diff --git a/assets/compiled/assets_icons.c b/assets/compiled/assets_icons.c index 17c84779e..2e2324561 100644 --- a/assets/compiled/assets_icons.c +++ b/assets/compiled/assets_icons.c @@ -450,6 +450,14 @@ const uint8_t _A_Snake_14_13[] = {0x01,0x00,0x14,0x00,0x00,0x0f,0xfe,0x0e,0x01,0 const uint8_t _A_Snake_14_14[] = {0x01,0x00,0x13,0x00,0x00,0x0f,0xfe,0x0e,0x01,0x18,0x04,0x18,0x02,0xb0,0x38,0x37,0x0c,0x02,0x40,0x51,0x09,0xa4,0x00,}; const uint8_t* const _A_Snake_14[] = {_A_Snake_14_0,_A_Snake_14_1,_A_Snake_14_2,_A_Snake_14_3,_A_Snake_14_4,_A_Snake_14_5,_A_Snake_14_6,_A_Snake_14_7,_A_Snake_14_8,_A_Snake_14_9,_A_Snake_14_10,_A_Snake_14_11,_A_Snake_14_12,_A_Snake_14_13,_A_Snake_14_14}; +const uint8_t _A_SpectrumAnalyzer_14_0[] = {0x01,0x00,0x1a,0x00,0x82,0x42,0x21,0x10,0x48,0x94,0x4a,0x29,0x0a,0xea,0xca,0x80,0x22,0x05,0x1e,0x94,0x4b,0x91,0x04,0xe2,0x42,0x38,0x10,0x00,0x0a,0x80,}; +const uint8_t _A_SpectrumAnalyzer_14_1[] = {0x01,0x00,0x18,0x00,0x82,0x42,0x21,0x10,0x48,0x94,0x4a,0x29,0x0a,0xea,0x00,0x48,0x0a,0x3d,0x28,0x97,0x22,0x09,0xc4,0x84,0x70,0x20,0x00,0x15,}; +const uint8_t _A_SpectrumAnalyzer_14_2[] = {0x01,0x00,0x16,0x00,0x82,0x42,0x21,0x10,0x48,0x84,0x08,0x02,0x3d,0x00,0x09,0x01,0x4f,0x91,0x04,0xe2,0x42,0x38,0x10,0x00,0x0a,0x80,}; +const uint8_t _A_SpectrumAnalyzer_14_3[] = {0x01,0x00,0x08,0x00,0x00,0x3f,0x00,0x02,0x40,0x55,0x00,0xc8,}; +const uint8_t _A_SpectrumAnalyzer_14_4[] = {0x01,0x00,0x0a,0x00,0x00,0x3f,0x06,0x40,0x01,0x10,0x28,0xf0,0x00,0x38,}; +const uint8_t _A_SpectrumAnalyzer_14_5[] = {0x01,0x00,0x12,0x00,0x00,0x1c,0x0a,0x21,0x04,0x84,0x71,0x65,0x00,0x11,0x02,0x8f,0x0a,0x21,0xc0,0x80,0x00,0x64,}; +const uint8_t* const _A_SpectrumAnalyzer_14[] = {_A_SpectrumAnalyzer_14_0,_A_SpectrumAnalyzer_14_1,_A_SpectrumAnalyzer_14_2,_A_SpectrumAnalyzer_14_3,_A_SpectrumAnalyzer_14_4,_A_SpectrumAnalyzer_14_5}; + const uint8_t _A_Sub1ghz_14_0[] = {0x01,0x00,0x1a,0x00,0x82,0x42,0x20,0x51,0x08,0x4c,0x92,0x0b,0x28,0xea,0xca,0x80,0x22,0x05,0x1e,0x4c,0x93,0x85,0x10,0xe2,0x42,0x38,0x10,0x00,0x0a,0x80,}; const uint8_t _A_Sub1ghz_14_1[] = {0x01,0x00,0x18,0x00,0x82,0x42,0x20,0x51,0x08,0x4c,0x92,0x0b,0x28,0xe2,0x80,0x48,0x0a,0x3c,0x99,0x27,0x0a,0x21,0xc4,0x84,0x70,0x20,0x00,0x15,}; const uint8_t _A_Sub1ghz_14_2[] = {0x01,0x00,0x16,0x00,0x82,0x42,0x20,0x51,0x08,0x0c,0x80,0x02,0x3c,0x10,0x09,0x01,0x4f,0x85,0x10,0xe2,0x42,0x38,0x10,0x00,0x0a,0x80,}; @@ -835,6 +843,7 @@ const Icon A_Plugins_14 = {.width=14,.height=14,.frame_count=9,.frame_rate=3,.fr const Icon A_Power_14 = {.width=14,.height=14,.frame_count=1,.frame_rate=3,.frames=_A_Power_14}; const Icon A_Settings_14 = {.width=14,.height=14,.frame_count=10,.frame_rate=3,.frames=_A_Settings_14}; const Icon A_Snake_14 = {.width=14,.height=14,.frame_count=15,.frame_rate=3,.frames=_A_Snake_14}; +const Icon A_SpectrumAnalyzer_14 = {.width=14,.height=14,.frame_count=6,.frame_rate=3,.frames=_A_SpectrumAnalyzer_14}; const Icon A_Sub1ghz_14 = {.width=14,.height=14,.frame_count=6,.frame_rate=3,.frames=_A_Sub1ghz_14}; const Icon A_Tamagotchi_14 = {.width=14,.height=14,.frame_count=6,.frame_rate=3,.frames=_A_Tamagotchi_14}; const Icon A_Tetris_14 = {.width=14,.height=14,.frame_count=10,.frame_rate=3,.frames=_A_Tetris_14}; diff --git a/assets/compiled/assets_icons.h b/assets/compiled/assets_icons.h index 665bc7bc4..e89385a36 100644 --- a/assets/compiled/assets_icons.h +++ b/assets/compiled/assets_icons.h @@ -107,6 +107,7 @@ extern const Icon A_Plugins_14; extern const Icon A_Power_14; extern const Icon A_Settings_14; extern const Icon A_Snake_14; +extern const Icon A_SpectrumAnalyzer_14; extern const Icon A_Sub1ghz_14; extern const Icon A_Tamagotchi_14; extern const Icon A_Tetris_14; diff --git a/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_0.png b/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_0.png new file mode 100644 index 0000000000000000000000000000000000000000..4fd68002e264f17319e9ffcebbc942760c3ee111 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R}>7Fi*AsQ3+ zPI44#P~c(S-Shu{(VmDE4y?8_gqiOKIZaS8Ha@T}byY-gP4Fbf=m1vP6Aw6kR^Hs1 zYn8>ukZHVN@5i-$XHKfOtC|-7I&^cz#qKP>YYW)h>SI_Jl>ZZJnA*lrbMgNJ&hM+8 VWhcJU5dd1r;OXk;vd$@?2>`p`J}&?O literal 0 HcmV?d00001 diff --git a/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_1.png b/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f09e6eee588832a55864feceaa7b1a1ddce01976 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R}NuDl_AsQ3+ zPCCeUK!L+Kdh`GPPv04FI16q*TxE2K`O=h0EDS0iZM=`pOPoK+;S)#Eg$Ay^30IGw z-1c_m>ScE~2)!u@Z9SK0u5P~0uQzUo<<$lrpJyeEC(i9>x{%3rK>RQNy*HV*vQOgY RJq6mv;OXk;vd$@?2>{pWKnVZ< literal 0 HcmV?d00001 diff --git a/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_2.png b/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_2.png new file mode 100644 index 0000000000000000000000000000000000000000..1192ed0afda50a9e32d639c36f52d5db55d6e028 GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R};hrvzAsQ3+ zP735}FyL_h-SK^Y(cg4z0-cpX+&>Y1#rdnS-|GrLUtx zSQ;08y)L{xWJc}}>D4c%?ajT;<5lpP!T2b9#iIWQWlt^EmR)eXY%S0V22WQ%mvv4F FO#mHtIwt@C literal 0 HcmV?d00001 diff --git a/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_3.png b/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_3.png new file mode 100644 index 0000000000000000000000000000000000000000..8db45a90100ca4c66ed2e237c5c0da49a0a1c55f GIT binary patch literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R}I-V|$AsQ2t z|NQ^|zn;yALD+H@+e|kgU<2Y;w@);AHw7eIzQ1y@)JFz}_4e$_$2QNL4b;cr>FVdQ I&MBb@06Y;Q7ytkO literal 0 HcmV?d00001 diff --git a/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_4.png b/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_4.png new file mode 100644 index 0000000000000000000000000000000000000000..fbd1b47aaf405826bd7a1c5096fec614087d2611 GIT binary patch literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R}#-1*YAsQ2t z|NQ^|zn;yALD+H@n};$GgmJpLxv|w0rQAN@@ipM+^N>AKC$|-bC^IuN99YYts=6}& Q2~azOr>mdKI;Vst0P0;L#{d8T literal 0 HcmV?d00001 diff --git a/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_5.png b/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_5.png new file mode 100644 index 0000000000000000000000000000000000000000..92d4c523e57753935a12d275ddec006dd506b6e6 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xa#^NA%Cx&(BWL^R}0iG_7AsQ2( zPH^NqV8C%WY0Lk5OY5n=S7!)Ze2gvCoBgL$>|q?FyP%u%NoF*SUWFt}e4 x8uUNqcHNUVN7skn*9-D9*Ie$KW68LO@s7K^@}Z@FHUiCN@O1TaS?83{1OSN-GN}Ln literal 0 HcmV?d00001 diff --git a/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_rate b/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_rate new file mode 100644 index 000000000..e440e5c84 --- /dev/null +++ b/assets/icons/MainMenu/SpectrumAnalyzer_14/frame_rate @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/assets/resources/Manifest b/assets/resources/Manifest index d9205ab86..6d2cb4302 100644 --- a/assets/resources/Manifest +++ b/assets/resources/Manifest @@ -1,5 +1,5 @@ V:0 -T:1654282266 +T:1654516779 D:badusb D:dolphin D:infrared @@ -235,7 +235,7 @@ F:dda1ef895b8a25fde57c874feaaef997:650:subghz/assets/came_atomo F:610a0ffa2479a874f2060eb2348104c5:2712:subghz/assets/keeloq_mfcodes F:9214f9c10463b746a27e82ce0b96e040:465:subghz/assets/keeloq_mfcodes_user F:653bd8d349055a41e1152e557d4a52d3:202:subghz/assets/nice_flor_s -F:293960caf3bf55655ff7297805e9db63:1355:subghz/assets/setting_user +F:48162bfe6129719db13c71f98852db4a:1313:subghz/assets/setting_user F:9a50dd284146dfbd8050a2ade62174d9:266:subghz/assets/universal_rf_map D:u2f/assets F:7e11e688e39034bbb9d88410044795e1:365:u2f/assets/cert.der diff --git a/assets/resources/subghz/assets/setting_user b/assets/resources/subghz/assets/setting_user index 4ded4fad2..1c7289821 100644 --- a/assets/resources/subghz/assets/setting_user +++ b/assets/resources/subghz/assets/setting_user @@ -14,8 +14,6 @@ frequency: 303875000 frequency: 304250000 frequency: 310000000 frequency: 312000000 -frequency: 312107900 -frequency: 312109900 frequency: 312100000 frequency: 313850000 frequency: 314000000