spectrum_analyzer.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. #include <furi.h>
  2. #include <furi_hal.h>
  3. #include <gui/gui.h>
  4. #include <input/input.h>
  5. #include <stdlib.h>
  6. #include "spectrum_analyzer.h"
  7. #include <lib/drivers/cc1101_regs.h>
  8. #include "spectrum_analyzer_worker.h"
  9. typedef struct {
  10. uint32_t center_freq;
  11. uint8_t width;
  12. uint8_t modulation;
  13. uint8_t band;
  14. uint8_t vscroll;
  15. uint32_t channel0_frequency;
  16. uint32_t spacing;
  17. bool mode_change;
  18. bool modulation_change;
  19. float max_rssi;
  20. uint8_t max_rssi_dec;
  21. uint8_t max_rssi_channel;
  22. uint8_t channel_ss[NUM_CHANNELS];
  23. } SpectrumAnalyzerModel;
  24. typedef struct {
  25. SpectrumAnalyzerModel* model;
  26. FuriMutex* model_mutex;
  27. FuriMessageQueue* event_queue;
  28. ViewPort* view_port;
  29. Gui* gui;
  30. SpectrumAnalyzerWorker* worker;
  31. } SpectrumAnalyzer;
  32. void spectrum_analyzer_draw_scale(Canvas* canvas, const SpectrumAnalyzerModel* model) {
  33. // Draw line
  34. canvas_draw_line(
  35. canvas, FREQ_START_X, FREQ_BOTTOM_Y, FREQ_START_X + FREQ_LENGTH_X, FREQ_BOTTOM_Y);
  36. // Draw minor scale
  37. for(int i = FREQ_START_X; i < FREQ_START_X + FREQ_LENGTH_X; i += 5) {
  38. canvas_draw_line(canvas, i, FREQ_BOTTOM_Y, i, FREQ_BOTTOM_Y + 2);
  39. }
  40. // Draw major scale
  41. for(int i = FREQ_START_X; i < FREQ_START_X + FREQ_LENGTH_X; i += 25) {
  42. canvas_draw_line(canvas, i, FREQ_BOTTOM_Y, i, FREQ_BOTTOM_Y + 4);
  43. }
  44. // Draw scale tags
  45. uint32_t tag_left = 0;
  46. uint32_t tag_center = 0;
  47. uint32_t tag_right = 0;
  48. char temp_str[18];
  49. tag_center = model->center_freq;
  50. switch(model->width) {
  51. case NARROW:
  52. tag_left = model->center_freq - 2000;
  53. tag_right = model->center_freq + 2000;
  54. break;
  55. case ULTRANARROW:
  56. tag_left = model->center_freq - 1000;
  57. tag_right = model->center_freq + 1000;
  58. break;
  59. case PRECISE:
  60. tag_left = model->center_freq - 200;
  61. tag_right = model->center_freq + 200;
  62. break;
  63. case ULTRAWIDE:
  64. tag_left = model->center_freq - 40000;
  65. tag_right = model->center_freq + 40000;
  66. break;
  67. default:
  68. tag_left = model->center_freq - 10000;
  69. tag_right = model->center_freq + 10000;
  70. }
  71. canvas_set_font(canvas, FontSecondary);
  72. switch(model->width) {
  73. case PRECISE:
  74. case ULTRANARROW:
  75. snprintf(temp_str, 18, "%.1f", ((double)tag_left) / 1000);
  76. canvas_draw_str_aligned(canvas, FREQ_START_X, 63, AlignCenter, AlignBottom, temp_str);
  77. snprintf(temp_str, 18, "%.1f", ((double)tag_center) / 1000);
  78. canvas_draw_str_aligned(canvas, 128 / 2, 63, AlignCenter, AlignBottom, temp_str);
  79. snprintf(temp_str, 18, "%.1f", ((double)tag_right) / 1000);
  80. canvas_draw_str_aligned(
  81. canvas, FREQ_START_X + FREQ_LENGTH_X - 1, 63, AlignCenter, AlignBottom, temp_str);
  82. break;
  83. default:
  84. snprintf(temp_str, 18, "%lu", tag_left / 1000);
  85. canvas_draw_str_aligned(canvas, FREQ_START_X, 63, AlignCenter, AlignBottom, temp_str);
  86. snprintf(temp_str, 18, "%lu", tag_center / 1000);
  87. canvas_draw_str_aligned(canvas, 128 / 2, 63, AlignCenter, AlignBottom, temp_str);
  88. snprintf(temp_str, 18, "%lu", tag_right / 1000);
  89. canvas_draw_str_aligned(
  90. canvas, FREQ_START_X + FREQ_LENGTH_X - 1, 63, AlignCenter, AlignBottom, temp_str);
  91. }
  92. }
  93. static void spectrum_analyzer_render_callback(Canvas* const canvas, void* ctx) {
  94. SpectrumAnalyzer* spectrum_analyzer = ctx;
  95. //furi_check(furi_mutex_acquire(spectrum_analyzer->model_mutex, FuriWaitForever) == FuriStatusOk);
  96. SpectrumAnalyzerModel* model = spectrum_analyzer->model;
  97. spectrum_analyzer_draw_scale(canvas, model);
  98. for(uint8_t column = 0; column < 128; column++) {
  99. uint8_t ss = model->channel_ss[column + 2];
  100. // Compress height to max of 64 values (255>>2)
  101. uint8_t s = MAX((ss - model->vscroll) >> 2, 0);
  102. uint8_t y = FREQ_BOTTOM_Y - s; // bar height
  103. // Draw each bar
  104. canvas_draw_line(canvas, column, FREQ_BOTTOM_Y, column, y);
  105. }
  106. if(model->mode_change) {
  107. char temp_mode_str[12];
  108. switch(model->width) {
  109. case NARROW:
  110. strncpy(temp_mode_str, "NARROW", 12);
  111. break;
  112. case ULTRANARROW:
  113. strncpy(temp_mode_str, "ULTRANARROW", 12);
  114. break;
  115. case PRECISE:
  116. strncpy(temp_mode_str, "PRECISE", 12);
  117. break;
  118. case ULTRAWIDE:
  119. strncpy(temp_mode_str, "ULTRAWIDE", 12);
  120. break;
  121. default:
  122. strncpy(temp_mode_str, "WIDE", 12);
  123. break;
  124. }
  125. // Current mode label
  126. char tmp_str[21];
  127. snprintf(tmp_str, 21, "Mode: %s", temp_mode_str);
  128. canvas_draw_str_aligned(canvas, 127, 4, AlignRight, AlignTop, tmp_str);
  129. }
  130. if(model->modulation_change) {
  131. char temp_mod_str[12];
  132. switch(model->modulation) {
  133. case NARROW_MODULATION:
  134. strncpy(temp_mod_str, "NARROW", 12);
  135. break;
  136. default:
  137. strncpy(temp_mod_str, "DEFAULT", 12);
  138. break;
  139. }
  140. // Current modulation label
  141. char tmp_str[27];
  142. snprintf(tmp_str, 27, "Modulation: %s", temp_mod_str);
  143. canvas_draw_str_aligned(canvas, 127, 4, AlignRight, AlignTop, tmp_str);
  144. }
  145. // Draw cross and label
  146. if(model->max_rssi > PEAK_THRESHOLD) {
  147. // Compress height to max of 64 values (255>>2)
  148. uint8_t max_y = MAX((model->max_rssi_dec - model->vscroll) >> 2, 0);
  149. max_y = (FREQ_BOTTOM_Y - max_y);
  150. // Cross
  151. int16_t x1, x2, y1, y2;
  152. x1 = model->max_rssi_channel - 2 - 2;
  153. if(x1 < 0) x1 = 0;
  154. y1 = max_y - 2;
  155. if(y1 < 0) y1 = 0;
  156. x2 = model->max_rssi_channel - 2 + 2;
  157. if(x2 > 127) x2 = 127;
  158. y2 = max_y + 2;
  159. if(y2 > 63) y2 = 63; // SHOULD NOT HAPPEN CHECK!
  160. canvas_draw_line(canvas, x1, y1, x2, y2);
  161. x1 = model->max_rssi_channel - 2 + 2;
  162. if(x1 > 127) x1 = 127;
  163. y1 = max_y - 2;
  164. if(y1 < 0) y1 = 0;
  165. x2 = model->max_rssi_channel - 2 - 2;
  166. if(x2 < 0) x2 = 0;
  167. y2 = max_y + 2;
  168. if(y2 > 63) y2 = 63; // SHOULD NOT HAPPEN CHECK!
  169. canvas_draw_line(canvas, (uint8_t)x1, (uint8_t)y1, (uint8_t)x2, (uint8_t)y2);
  170. // Label
  171. char temp_str[36];
  172. snprintf(
  173. temp_str,
  174. 36,
  175. "Peak: %3.2f Mhz %3.1f dbm",
  176. ((double)(model->channel0_frequency + (model->max_rssi_channel * model->spacing)) /
  177. 1000000),
  178. (double)model->max_rssi);
  179. canvas_draw_str_aligned(canvas, 127, 0, AlignRight, AlignTop, temp_str);
  180. }
  181. //furi_mutex_release(spectrum_analyzer->model_mutex);
  182. // FURI_LOG_D("Spectrum", "model->vscroll %u", model->vscroll);
  183. }
  184. static void spectrum_analyzer_input_callback(InputEvent* input_event, void* ctx) {
  185. SpectrumAnalyzer* spectrum_analyzer = ctx;
  186. // Handle short and long presses
  187. if(input_event->type == InputTypeShort || input_event->type == InputTypeLong) {
  188. furi_message_queue_put(spectrum_analyzer->event_queue, input_event, FuriWaitForever);
  189. }
  190. }
  191. static void spectrum_analyzer_worker_callback(
  192. void* channel_ss,
  193. float max_rssi,
  194. uint8_t max_rssi_dec,
  195. uint8_t max_rssi_channel,
  196. void* context) {
  197. SpectrumAnalyzer* spectrum_analyzer = context;
  198. furi_check(
  199. furi_mutex_acquire(spectrum_analyzer->model_mutex, FuriWaitForever) == FuriStatusOk);
  200. SpectrumAnalyzerModel* model = (SpectrumAnalyzerModel*)spectrum_analyzer->model;
  201. memcpy(model->channel_ss, (uint8_t*)channel_ss, sizeof(uint8_t) * NUM_CHANNELS);
  202. model->max_rssi = max_rssi;
  203. model->max_rssi_dec = max_rssi_dec;
  204. model->max_rssi_channel = max_rssi_channel;
  205. furi_mutex_release(spectrum_analyzer->model_mutex);
  206. view_port_update(spectrum_analyzer->view_port);
  207. }
  208. void spectrum_analyzer_calculate_frequencies(SpectrumAnalyzerModel* model) {
  209. // REDO ALL THIS. CALCULATE ONLY WITH SPACING!
  210. uint8_t new_band;
  211. uint32_t min_hz;
  212. uint32_t max_hz;
  213. uint32_t margin;
  214. uint32_t step;
  215. uint32_t upper_limit;
  216. uint32_t lower_limit;
  217. uint32_t next_up;
  218. uint32_t next_down;
  219. uint8_t next_band_up;
  220. uint8_t next_band_down;
  221. switch(model->width) {
  222. case NARROW:
  223. margin = NARROW_MARGIN;
  224. step = NARROW_STEP;
  225. model->spacing = NARROW_SPACING;
  226. break;
  227. case ULTRANARROW:
  228. margin = ULTRANARROW_MARGIN;
  229. step = ULTRANARROW_STEP;
  230. model->spacing = ULTRANARROW_SPACING;
  231. break;
  232. case PRECISE:
  233. margin = PRECISE_MARGIN;
  234. step = PRECISE_STEP;
  235. model->spacing = PRECISE_SPACING;
  236. break;
  237. case ULTRAWIDE:
  238. margin = ULTRAWIDE_MARGIN;
  239. step = ULTRAWIDE_STEP;
  240. model->spacing = ULTRAWIDE_SPACING;
  241. /* nearest 20 MHz step */
  242. model->center_freq = ((model->center_freq + 10000) / 20000) * 20000;
  243. break;
  244. default:
  245. margin = WIDE_MARGIN;
  246. step = WIDE_STEP;
  247. model->spacing = WIDE_SPACING;
  248. /* nearest 5 MHz step */
  249. model->center_freq = ((model->center_freq + 2000) / 5000) * 5000;
  250. break;
  251. }
  252. /* handle cases near edges of bands */
  253. if(model->center_freq > EDGE_900) {
  254. new_band = BAND_900;
  255. upper_limit = UPPER(MAX_900, margin, step);
  256. lower_limit = LOWER(MIN_900, margin, step);
  257. next_up = LOWER(MIN_300, margin, step);
  258. next_down = UPPER(MAX_400, margin, step);
  259. next_band_up = BAND_300;
  260. next_band_down = BAND_400;
  261. } else if(model->center_freq > EDGE_400) {
  262. new_band = BAND_400;
  263. upper_limit = UPPER(MAX_400, margin, step);
  264. lower_limit = LOWER(MIN_400, margin, step);
  265. next_up = LOWER(MIN_900, margin, step);
  266. next_down = UPPER(MAX_300, margin, step);
  267. next_band_up = BAND_900;
  268. next_band_down = BAND_300;
  269. } else {
  270. new_band = BAND_300;
  271. upper_limit = UPPER(MAX_300, margin, step);
  272. lower_limit = LOWER(MIN_300, margin, step);
  273. next_up = LOWER(MIN_400, margin, step);
  274. next_down = UPPER(MAX_900, margin, step);
  275. next_band_up = BAND_400;
  276. next_band_down = BAND_900;
  277. }
  278. if(model->center_freq > upper_limit) {
  279. model->center_freq = upper_limit;
  280. if(new_band == model->band) {
  281. new_band = next_band_up;
  282. model->center_freq = next_up;
  283. }
  284. } else if(model->center_freq < lower_limit) {
  285. model->center_freq = lower_limit;
  286. if(new_band == model->band) {
  287. new_band = next_band_down;
  288. model->center_freq = next_down;
  289. }
  290. }
  291. model->band = new_band;
  292. /* doing everything in Hz from here on */
  293. switch(model->band) {
  294. case BAND_400:
  295. min_hz = MIN_400 * 1000;
  296. max_hz = MAX_400 * 1000;
  297. break;
  298. case BAND_300:
  299. min_hz = MIN_300 * 1000;
  300. max_hz = MAX_300 * 1000;
  301. break;
  302. default:
  303. min_hz = MIN_900 * 1000;
  304. max_hz = MAX_900 * 1000;
  305. break;
  306. }
  307. model->channel0_frequency =
  308. model->center_freq * 1000 - (model->spacing * ((NUM_CHANNELS / 2) + 1));
  309. // /* calibrate upper channels */
  310. // hz = model->center_freq * 1000000;
  311. // max_chan = NUM_CHANNELS / 2;
  312. // while (hz <= max_hz && max_chan < NUM_CHANNELS) {
  313. // instance->chan_table[max_chan].frequency = hz;
  314. // FURI_LOG_T("Spectrum", "calibrate_freq ch[%u]: %lu", max_chan, hz);
  315. // hz += model->spacing;
  316. // max_chan++;
  317. // }
  318. // /* calibrate lower channels */
  319. // hz = instance->freq * 1000000 - model->spacing;
  320. // min_chan = NUM_CHANNELS / 2;
  321. // while (hz >= min_hz && min_chan > 0) {
  322. // min_chan--;
  323. // instance->chan_table[min_chan].frequency = hz;
  324. // FURI_LOG_T("Spectrum", "calibrate_freq ch[%u]: %lu", min_chan, hz);
  325. // hz -= model->spacing;
  326. // }
  327. model->max_rssi = -200.0;
  328. model->max_rssi_dec = 0;
  329. FURI_LOG_D("Spectrum", "setup_frequencies - max_hz: %lu - min_hz: %lu", max_hz, min_hz);
  330. FURI_LOG_D("Spectrum", "center_freq: %lu", model->center_freq);
  331. FURI_LOG_D(
  332. "Spectrum",
  333. "ch[0]: %lu - ch[%u]: %lu",
  334. model->channel0_frequency,
  335. NUM_CHANNELS - 1,
  336. model->channel0_frequency + ((NUM_CHANNELS - 1) * model->spacing));
  337. }
  338. SpectrumAnalyzer* spectrum_analyzer_alloc() {
  339. SpectrumAnalyzer* instance = malloc(sizeof(SpectrumAnalyzer));
  340. instance->model = malloc(sizeof(SpectrumAnalyzerModel));
  341. SpectrumAnalyzerModel* model = instance->model;
  342. for(uint8_t ch = 0; ch < NUM_CHANNELS - 1; ch++) {
  343. model->channel_ss[ch] = 0;
  344. }
  345. model->max_rssi_dec = 0;
  346. model->max_rssi_channel = 0;
  347. model->max_rssi = PEAK_THRESHOLD - 1; // Should initializar to < PEAK_THRESHOLD
  348. model->center_freq = DEFAULT_FREQ;
  349. model->width = WIDE;
  350. model->modulation = DEFAULT_MODULATION;
  351. model->band = BAND_400;
  352. model->vscroll = DEFAULT_VSCROLL;
  353. instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  354. instance->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  355. instance->worker = spectrum_analyzer_worker_alloc();
  356. spectrum_analyzer_worker_set_callback(
  357. instance->worker, spectrum_analyzer_worker_callback, instance);
  358. // Set system callbacks
  359. instance->view_port = view_port_alloc();
  360. view_port_draw_callback_set(instance->view_port, spectrum_analyzer_render_callback, instance);
  361. view_port_input_callback_set(instance->view_port, spectrum_analyzer_input_callback, instance);
  362. // Open GUI and register view_port
  363. instance->gui = furi_record_open(RECORD_GUI);
  364. gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen);
  365. return instance;
  366. }
  367. void spectrum_analyzer_free(SpectrumAnalyzer* instance) {
  368. // view_port_enabled_set(view_port, false);
  369. gui_remove_view_port(instance->gui, instance->view_port);
  370. furi_record_close(RECORD_GUI);
  371. view_port_free(instance->view_port);
  372. spectrum_analyzer_worker_free(instance->worker);
  373. furi_message_queue_free(instance->event_queue);
  374. furi_mutex_free(instance->model_mutex);
  375. free(instance->model);
  376. free(instance);
  377. }
  378. int32_t spectrum_analyzer_app(void* p) {
  379. UNUSED(p);
  380. SpectrumAnalyzer* spectrum_analyzer = spectrum_analyzer_alloc();
  381. InputEvent input;
  382. furi_hal_power_suppress_charge_enter();
  383. FURI_LOG_D("Spectrum", "Main Loop - Starting worker");
  384. furi_delay_ms(50);
  385. spectrum_analyzer_worker_start(spectrum_analyzer->worker);
  386. spectrum_analyzer_calculate_frequencies(spectrum_analyzer->model);
  387. spectrum_analyzer_worker_set_frequencies(
  388. spectrum_analyzer->worker,
  389. spectrum_analyzer->model->channel0_frequency,
  390. spectrum_analyzer->model->spacing,
  391. spectrum_analyzer->model->width);
  392. FURI_LOG_D("Spectrum", "Main Loop - Wait on queue");
  393. furi_delay_ms(50);
  394. while(furi_message_queue_get(spectrum_analyzer->event_queue, &input, FuriWaitForever) ==
  395. FuriStatusOk) {
  396. furi_check(
  397. furi_mutex_acquire(spectrum_analyzer->model_mutex, FuriWaitForever) == FuriStatusOk);
  398. FURI_LOG_D("Spectrum", "Main Loop - Input: %u", input.key);
  399. SpectrumAnalyzerModel* model = spectrum_analyzer->model;
  400. uint8_t vstep = VERTICAL_SHORT_STEP;
  401. uint32_t hstep;
  402. bool exit_loop = false;
  403. switch(model->width) {
  404. case NARROW:
  405. hstep = NARROW_STEP;
  406. break;
  407. case ULTRANARROW:
  408. hstep = ULTRANARROW_STEP;
  409. break;
  410. case ULTRAWIDE:
  411. hstep = ULTRAWIDE_STEP;
  412. break;
  413. case PRECISE:
  414. hstep = PRECISE_STEP;
  415. break;
  416. default:
  417. hstep = WIDE_STEP;
  418. break;
  419. }
  420. switch(input.type) {
  421. case InputTypeShort:
  422. switch(input.key) {
  423. case InputKeyUp:
  424. model->vscroll = MAX(model->vscroll - vstep, MIN_VSCROLL);
  425. FURI_LOG_D("Spectrum", "Vscroll: %u", model->vscroll);
  426. break;
  427. case InputKeyDown:
  428. model->vscroll = MIN(model->vscroll + vstep, MAX_VSCROLL);
  429. FURI_LOG_D("Spectrum", "Vscroll: %u", model->vscroll);
  430. break;
  431. case InputKeyRight:
  432. model->center_freq += hstep;
  433. FURI_LOG_D("Spectrum", "center_freq: %lu", model->center_freq);
  434. spectrum_analyzer_calculate_frequencies(model);
  435. spectrum_analyzer_worker_set_frequencies(
  436. spectrum_analyzer->worker,
  437. model->channel0_frequency,
  438. model->spacing,
  439. model->width);
  440. break;
  441. case InputKeyLeft:
  442. model->center_freq -= hstep;
  443. spectrum_analyzer_calculate_frequencies(model);
  444. spectrum_analyzer_worker_set_frequencies(
  445. spectrum_analyzer->worker,
  446. model->channel0_frequency,
  447. model->spacing,
  448. model->width);
  449. FURI_LOG_D("Spectrum", "center_freq: %lu", model->center_freq);
  450. break;
  451. case InputKeyOk: {
  452. switch(model->width) {
  453. case WIDE:
  454. model->width = NARROW;
  455. break;
  456. case NARROW:
  457. model->width = ULTRANARROW;
  458. break;
  459. case ULTRANARROW:
  460. model->width = PRECISE;
  461. break;
  462. case PRECISE:
  463. model->width = ULTRAWIDE;
  464. break;
  465. case ULTRAWIDE:
  466. model->width = WIDE;
  467. break;
  468. default:
  469. model->width = WIDE;
  470. break;
  471. }
  472. }
  473. model->mode_change = true;
  474. view_port_update(spectrum_analyzer->view_port);
  475. furi_delay_ms(1000);
  476. model->mode_change = false;
  477. spectrum_analyzer_calculate_frequencies(model);
  478. spectrum_analyzer_worker_set_frequencies(
  479. spectrum_analyzer->worker,
  480. model->channel0_frequency,
  481. model->spacing,
  482. model->width);
  483. FURI_LOG_D("Spectrum", "Width: %u", model->width);
  484. break;
  485. case InputKeyBack:
  486. exit_loop = true;
  487. break;
  488. default:
  489. break;
  490. }
  491. break;
  492. case InputTypeLong:
  493. switch(input.key) {
  494. case InputKeyOk:
  495. FURI_LOG_D("Spectrum", "InputTypeLong");
  496. switch(model->modulation) {
  497. case NARROW_MODULATION:
  498. model->modulation = DEFAULT_MODULATION;
  499. break;
  500. case DEFAULT_MODULATION:
  501. default:
  502. model->modulation = NARROW_MODULATION;
  503. break;
  504. }
  505. model->modulation_change = true;
  506. view_port_update(spectrum_analyzer->view_port);
  507. furi_delay_ms(1000);
  508. model->modulation_change = false;
  509. spectrum_analyzer_worker_set_modulation(
  510. spectrum_analyzer->worker, spectrum_analyzer->model->modulation);
  511. break;
  512. default:
  513. break;
  514. }
  515. break;
  516. default:
  517. break;
  518. }
  519. furi_mutex_release(spectrum_analyzer->model_mutex);
  520. view_port_update(spectrum_analyzer->view_port);
  521. if(exit_loop == true) break;
  522. }
  523. spectrum_analyzer_worker_stop(spectrum_analyzer->worker);
  524. furi_hal_power_suppress_charge_exit();
  525. spectrum_analyzer_free(spectrum_analyzer);
  526. return 0;
  527. }