spectrum_analyzer.c 18 KB

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