diff --git a/applications/applications.c b/applications/applications.c index 7067685f7..9e19ea5d8 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -49,6 +49,7 @@ extern int32_t text_box_test_app(void* p); extern int32_t music_player_app(void* p); extern int32_t snake_game_app(void* p); extern int32_t tetris_game_app(void *p); +extern int32_t spectrum_analyzer_app(void* p); // On system start hooks declaration extern void bt_on_system_start(); @@ -357,6 +358,14 @@ const FlipperApplication FLIPPER_PLUGINS[] = { .stack_size = 1024, .icon = NULL}, #endif + +#ifdef APP_SPECTRUM_ANALYZER + {.app = spectrum_analyzer_app, + .name = "Spectrum Analyzer", + .stack_size = 1024, + .icon = &A_Plugins_14, + .flags = FlipperApplicationFlagDefault}, +#endif }; const size_t FLIPPER_PLUGINS_COUNT = COUNT_OF(FLIPPER_PLUGINS); diff --git a/applications/applications.mk b/applications/applications.mk index 2508395d7..4ae0a87ad 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -48,6 +48,7 @@ APP_UPDATER = 1 APP_MUSIC_PLAYER = 1 APP_SNAKE_GAME = 1 APP_TETRIS_GAME = 1 +APP_SPECTRUM_ANALYZER = 1 # Debug APP_ACCESSOR = 1 @@ -241,6 +242,12 @@ CFLAGS += -DAPP_TETRIS_GAME SRV_GUI = 1 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) CFLAGS += -DAPP_IBUTTON diff --git a/applications/spectrum_analyzer/spectrum_analyzer.c b/applications/spectrum_analyzer/spectrum_analyzer.c new file mode 100644 index 000000000..5d9ec2531 --- /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", + ((float)(model->channel0_frequency + (model->max_rssi_channel * model->spacing)) / + 1000000), + 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);