diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer.c b/applications/external/spectrum_analyzer/spectrum_analyzer.c index 289035cd5..61aa55c11 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer.c +++ b/applications/external/spectrum_analyzer/spectrum_analyzer.c @@ -12,6 +12,7 @@ typedef struct { uint32_t center_freq; uint8_t width; + uint8_t mod; uint8_t band; uint8_t vscroll; @@ -19,6 +20,7 @@ typedef struct { uint32_t spacing; bool mode_change; + bool mod_change; float max_rssi; uint8_t max_rssi_dec; @@ -147,6 +149,24 @@ static void spectrum_analyzer_render_callback(Canvas* const canvas, void* ctx) { snprintf(tmp_str, 21, "Mode: %s", temp_mode_str); canvas_draw_str_aligned(canvas, 127, 4, AlignRight, AlignTop, tmp_str); } + + if(model->mod_change) { + char temp_mod_str[12]; + switch(model->mod) { + case NARROW_MOD: + strncpy(temp_mod_str, "NARROW", 12); + break; + default: + strncpy(temp_mod_str, "DEFAULT", 12); + break; + } + + // Current modulation label + char tmp_str[27]; + snprintf(tmp_str, 27, "Modulation: %s", temp_mod_str); + canvas_draw_str_aligned(canvas, 127, 4, AlignRight, AlignTop, tmp_str); + } + // Draw cross and label if(model->max_rssi > PEAK_THRESHOLD) { // Compress height to max of 64 values (255>>2) @@ -194,8 +214,8 @@ static void spectrum_analyzer_render_callback(Canvas* const canvas, void* ctx) { static void spectrum_analyzer_input_callback(InputEvent* input_event, void* ctx) { SpectrumAnalyzer* spectrum_analyzer = ctx; - // Only handle short presses - if(input_event->type == InputTypeShort) { + // Handle short and long presses + if(input_event->type == InputTypeShort || input_event->type == InputTypeLong) { furi_message_queue_put(spectrum_analyzer->event_queue, input_event, FuriWaitForever); } } @@ -376,6 +396,7 @@ SpectrumAnalyzer* spectrum_analyzer_alloc() { model->center_freq = DEFAULT_FREQ; model->width = WIDE; + model->mod = DEFAULT_MOD; model->band = BAND_400; model->vscroll = DEFAULT_VSCROLL; @@ -469,66 +490,97 @@ int32_t spectrum_analyzer_app(void* p) { hstep = WIDE_STEP; break; } + + switch(input.type) { + case InputTypeShort: + 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 = ULTRANARROW; + break; + case ULTRANARROW: + model->width = PRECISE; + break; + case PRECISE: + model->width = ULTRAWIDE; + break; + case ULTRAWIDE: + model->width = WIDE; + break; + default: + model->width = WIDE; + break; + } + } + model->mode_change = true; + view_port_update(spectrum_analyzer->view_port); - 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; + furi_delay_ms(1000); + + model->mode_change = false; + 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 NARROW: - model->width = ULTRANARROW; - break; - case ULTRANARROW: - model->width = PRECISE; - break; - case PRECISE: - model->width = ULTRAWIDE; - break; - case ULTRAWIDE: - model->width = WIDE; + case InputKeyBack: + exit_loop = true; break; default: - model->width = WIDE; break; } - } - - model->mode_change = true; - view_port_update(spectrum_analyzer->view_port); - - furi_delay_ms(1000); - - model->mode_change = false; - 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; + case InputTypeLong: + switch(input.key) { + case InputKeyOk: + FURI_LOG_D("Spectrum", "InputTypeLong"); + switch(model->mod) { + case NARROW_MOD: + model->mod = DEFAULT_MOD; + break; + case DEFAULT_MOD: + default: + model->mod = NARROW_MOD; + break; + } + + model->mod_change = true; + view_port_update(spectrum_analyzer->view_port); + + furi_delay_ms(1000); + + model->mod_change = false; + spectrum_analyzer_worker_set_modulation( + spectrum_analyzer->worker, + spectrum_analyzer->model->mod); + break; + } break; default: break; diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer.h b/applications/external/spectrum_analyzer/spectrum_analyzer.h index 73dd484e1..c3851423d 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer.h +++ b/applications/external/spectrum_analyzer/spectrum_analyzer.h @@ -77,4 +77,8 @@ #define MID_900 848000000 #define UPPER(a, b, c) ((((a) - (b) + ((c) / 2)) / (c)) * (c)) -#define LOWER(a, b, c) ((((a) + (b)) / (c)) * (c)) \ No newline at end of file +#define LOWER(a, b, c) ((((a) + (b)) / (c)) * (c)) + +/* Modulation references */ +#define DEFAULT_MOD 0 +#define NARROW_MOD 1 \ No newline at end of file diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer_worker.c b/applications/external/spectrum_analyzer/spectrum_analyzer_worker.c index 5b35a47a2..7efc081b4 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer_worker.c +++ b/applications/external/spectrum_analyzer/spectrum_analyzer_worker.c @@ -20,6 +20,7 @@ struct SpectrumAnalyzerWorker { uint32_t channel0_frequency; uint32_t spacing; uint8_t width; + uint8_t modulation; float max_rssi; uint8_t max_rssi_dec; uint8_t max_rssi_channel; @@ -66,18 +67,57 @@ static int32_t spectrum_analyzer_worker_thread(void* context) { subghz_devices_flush_rx(instance->radio_device); subghz_devices_set_rx(instance->radio_device); - const uint8_t radio_config[] = { + // Default modulation + const uint8_t default_modulation[] = { + /* Frequency Synthesizer Control */ CC1101_FSCTRL0, 0x00, CC1101_FSCTRL1, - 0x12, - - CC1101_AGCCTRL2, - 0xC0, + 0x12, // IF = (26*10^6) / (2^10) * 0x12 = 304687.5 Hz + // Modem Configuration + // CC1101_MDMCFG0, + // 0x00, // Channel spacing is 25kHz + // CC1101_MDMCFG1, + // 0x00, // Channel spacing is 25kHz + // CC1101_MDMCFG2, + // 0x30, // Format ASK/OOK, No preamble/sync + // CC1101_MDMCFG3, + // 0x32, // Data rate is 121.399 kBaud CC1101_MDMCFG4, - 0x6C, + 0x6C, // Rx BW filter is 270.83 kHz + + /* Main Radio Control State Machine */ + // CC1101_MCSM0, + // 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + // CC1101_FOCCFG, + // 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + // CC1101_AGCTRL0,0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + // CC1101_AGCTRL1,0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + // CC1101_AGCCTRL2, 0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. + // CC1101_AGCCTRL0, + // 0x91, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary + // CC1101_AGCCTRL1, + // 0x0, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0xC0, // 03 - The 3 highest DVGA gain settings can not be used; 000 - MAX LNA+LNA2; 000 - MAIN_TARGET 24 dB + + /* Wake on radio and timeouts control */ + // CC1101_WORCTRL, + // 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + // CC1101_FREND0, + // 0x11, // Adjusts current TX LO buffer + high is PATABLE[1] + // CC1101_FREND1, + // 0xB6, // + CC1101_TEST2, 0x88, CC1101_TEST1, @@ -97,16 +137,94 @@ static int32_t spectrum_analyzer_worker_thread(void* context) { 0x00, 0x00, 0x00, - 0x00, + 0x00 }; + // Narrow modulation + const uint8_t narrow_modulation[] = { + + /* Frequency Synthesizer Control */ + CC1101_FSCTRL0, + 0x00, + CC1101_FSCTRL1, + 0x00, // IF = (26*10^6) / (2^10) * 0x00 = 0 Hz + + // Modem Configuration + // CC1101_MDMCFG0, + // 0x00, // Channel spacing is 25kHz + // CC1101_MDMCFG1, + // 0x00, // Channel spacing is 25kHz + // CC1101_MDMCFG2, + // 0x30, // Format ASK/OOK, No preamble/sync + // CC1101_MDMCFG3, + // 0x32, // Data rate is 121.399 kBaud + CC1101_MDMCFG4, + 0xFC, // Rx BW filter is 58.04 kHz + + /* Main Radio Control State Machine */ + // CC1101_MCSM0, + // 0x18, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us) + + /* Frequency Offset Compensation Configuration */ + // CC1101_FOCCFG, + // 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off + + /* Automatic Gain Control */ + // CC1101_AGCTRL0,0x40, // 01 - Low hysteresis, small asymmetric dead zone, medium gain; 00 - 8 samples agc; 00 - Normal AGC, 00 - 4dB boundary + // CC1101_AGCTRL1,0x00, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + // CC1101_AGCCTRL2, 0x03, // 00 - DVGA all; 000 - MAX LNA+LNA2; 011 - MAIN_TARGET 24 dB + //MAGN_TARGET for RX filter BW =< 100 kHz is 0x3. For higher RX filter BW's MAGN_TARGET is 0x7. + CC1101_AGCCTRL0, + 0x30, // 00 - NO hysteresis, ymmetric dead zone, high gain ; 11 - 32 samples agc; 00 - Normal AGC, 00 - 8dB boundary + CC1101_AGCCTRL1, + 0x0, // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 0000 - RSSI to MAIN_TARGET + CC1101_AGCCTRL2, + 0x84, // 02 - The 2 highest DVGA gain settings can not be used; 000 - MAX LNA+LNA2; 100 - MAIN_TARGET 36 dB + + /* Wake on radio and timeouts control */ + // CC1101_WORCTRL, + // 0xFB, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours + + /* Frontend configuration */ + // CC1101_FREND0, + // 0x11, // Adjusts current TX LO buffer + high is PATABLE[1] + // CC1101_FREND1, + // 0xB6, // + + CC1101_TEST2, + 0x88, + CC1101_TEST1, + 0x31, + CC1101_TEST0, + 0x09, + + /* End */ + 0, + 0, + + // ook_async_patable + 0x00, + 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 + }; + + const uint8_t* modulations[] = {default_modulation, narrow_modulation}; + while(instance->should_work) { furi_delay_ms(50); // FURI_LOG_T("SpectrumWorker", "spectrum_analyzer_worker_thread: Worker Loop"); subghz_devices_idle(instance->radio_device); subghz_devices_load_preset( - instance->radio_device, FuriHalSubGhzPresetCustom, (uint8_t*)radio_config); + instance->radio_device, FuriHalSubGhzPresetCustom, (uint8_t*)modulations[instance->modulation]); + //subghz_devices_load_preset( + // instance->radio_device, FuriHalSubGhzPresetCustom, (uint8_t*)default_modulation); + //furi_hal_subghz_load_custom_preset(modulations[instance->modulation]); // TODO: Check filter! // spectrum_analyzer_worker_set_filter(instance); @@ -222,6 +340,19 @@ void spectrum_analyzer_worker_set_frequencies( instance->width = width; } +void spectrum_analyzer_worker_set_modulation( + SpectrumAnalyzerWorker* instance, + uint8_t modulation) { + furi_assert(instance); + + FURI_LOG_D( + "SpectrumWorker", + "spectrum_analyzer_worker_set_modulation - modulation = %u", + modulation); + + instance->modulation = modulation; +} + void spectrum_analyzer_worker_start(SpectrumAnalyzerWorker* instance) { FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_start"); diff --git a/applications/external/spectrum_analyzer/spectrum_analyzer_worker.h b/applications/external/spectrum_analyzer/spectrum_analyzer_worker.h index ca051dacc..65704285d 100644 --- a/applications/external/spectrum_analyzer/spectrum_analyzer_worker.h +++ b/applications/external/spectrum_analyzer/spectrum_analyzer_worker.h @@ -28,6 +28,10 @@ void spectrum_analyzer_worker_set_frequencies( uint32_t spacing, uint8_t width); +void spectrum_analyzer_worker_set_modulation( + SpectrumAnalyzerWorker* instance, + uint8_t modulation); + void spectrum_analyzer_worker_start(SpectrumAnalyzerWorker* instance); void spectrum_analyzer_worker_stop(SpectrumAnalyzerWorker* instance);