mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-27 23:10:22 +00:00
Merge spectrum analyzer
This commit is contained in:
parent
16db5b1564
commit
40b0efc971
6 changed files with 769 additions and 0 deletions
|
@ -49,6 +49,7 @@ extern int32_t text_box_test_app(void* p);
|
||||||
extern int32_t music_player_app(void* p);
|
extern int32_t music_player_app(void* p);
|
||||||
extern int32_t snake_game_app(void* p);
|
extern int32_t snake_game_app(void* p);
|
||||||
extern int32_t tetris_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
|
// On system start hooks declaration
|
||||||
extern void bt_on_system_start();
|
extern void bt_on_system_start();
|
||||||
|
@ -357,6 +358,14 @@ const FlipperApplication FLIPPER_PLUGINS[] = {
|
||||||
.stack_size = 1024,
|
.stack_size = 1024,
|
||||||
.icon = NULL},
|
.icon = NULL},
|
||||||
#endif
|
#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);
|
const size_t FLIPPER_PLUGINS_COUNT = COUNT_OF(FLIPPER_PLUGINS);
|
||||||
|
|
|
@ -48,6 +48,7 @@ APP_UPDATER = 1
|
||||||
APP_MUSIC_PLAYER = 1
|
APP_MUSIC_PLAYER = 1
|
||||||
APP_SNAKE_GAME = 1
|
APP_SNAKE_GAME = 1
|
||||||
APP_TETRIS_GAME = 1
|
APP_TETRIS_GAME = 1
|
||||||
|
APP_SPECTRUM_ANALYZER = 1
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
APP_ACCESSOR = 1
|
APP_ACCESSOR = 1
|
||||||
|
@ -241,6 +242,12 @@ CFLAGS += -DAPP_TETRIS_GAME
|
||||||
SRV_GUI = 1
|
SRV_GUI = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
APP_SPECTRUM_ANALYZER ?= 0
|
||||||
|
ifeq ($(APP_SPECTRUM_ANALYZER), 1)
|
||||||
|
CFLAGS += -DAPP_SPECTRUM_ANALYZER
|
||||||
|
SRV_GUI = 1
|
||||||
|
endif
|
||||||
|
|
||||||
APP_IBUTTON ?= 0
|
APP_IBUTTON ?= 0
|
||||||
ifeq ($(APP_IBUTTON), 1)
|
ifeq ($(APP_IBUTTON), 1)
|
||||||
CFLAGS += -DAPP_IBUTTON
|
CFLAGS += -DAPP_IBUTTON
|
||||||
|
|
459
applications/spectrum_analyzer/spectrum_analyzer.c
Normal file
459
applications/spectrum_analyzer/spectrum_analyzer.c
Normal file
|
@ -0,0 +1,459 @@
|
||||||
|
#include <furi.h>
|
||||||
|
#include <furi_hal.h>
|
||||||
|
|
||||||
|
#include <gui/gui.h>
|
||||||
|
#include <input/input.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "spectrum_analyzer.h"
|
||||||
|
|
||||||
|
#include <gui/gui.h>
|
||||||
|
|
||||||
|
#include <lib/drivers/cc1101_regs.h>
|
||||||
|
#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;
|
||||||
|
}
|
66
applications/spectrum_analyzer/spectrum_analyzer.h
Normal file
66
applications/spectrum_analyzer/spectrum_analyzer.h
Normal file
|
@ -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))
|
195
applications/spectrum_analyzer/spectrum_analyzer_worker.c
Normal file
195
applications/spectrum_analyzer/spectrum_analyzer_worker.c
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
#include "spectrum_analyzer.h"
|
||||||
|
#include "spectrum_analyzer_worker.h"
|
||||||
|
|
||||||
|
#include <furi_hal.h>
|
||||||
|
#include <furi.h>
|
||||||
|
|
||||||
|
#include <lib/drivers/cc1101_regs.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
33
applications/spectrum_analyzer/spectrum_analyzer_worker.h
Normal file
33
applications/spectrum_analyzer/spectrum_analyzer_worker.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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);
|
Loading…
Reference in a new issue