spectrum_analyzer_worker.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. #include "spectrum_analyzer.h"
  2. #include "spectrum_analyzer_worker.h"
  3. #include <furi_hal.h>
  4. #include <furi.h>
  5. #include "helpers/radio_device_loader.h"
  6. #include <lib/drivers/cc1101_regs.h>
  7. struct SpectrumAnalyzerWorker {
  8. FuriThread* thread;
  9. bool should_work;
  10. SpectrumAnalyzerWorkerCallback callback;
  11. void* callback_context;
  12. const SubGhzDevice* radio_device;
  13. uint32_t channel0_frequency;
  14. uint32_t spacing;
  15. uint8_t width;
  16. uint8_t modulation;
  17. float max_rssi;
  18. uint8_t max_rssi_dec;
  19. uint8_t max_rssi_channel;
  20. uint8_t channel_ss[NUM_CHANNELS];
  21. };
  22. /* set the channel bandwidth */
  23. void spectrum_analyzer_worker_set_filter(SpectrumAnalyzerWorker* instance) {
  24. uint8_t filter_config[2][2] = {
  25. {CC1101_MDMCFG4, 0},
  26. {0, 0},
  27. };
  28. // FURI_LOG_D("SpectrumWorker", "spectrum_analyzer_worker_set_filter: width = %u", instance->width);
  29. /* channel spacing should fit within 80% of channel filter bandwidth */
  30. switch(instance->width) {
  31. case NARROW:
  32. filter_config[0][1] = 0xFC; /* 39.2 kHz / .8 = 49 kHz --> 58 kHz */
  33. break;
  34. case ULTRAWIDE:
  35. filter_config[0][1] = 0x0C; /* 784 kHz / .8 = 980 kHz --> 812 kHz */
  36. break;
  37. default:
  38. filter_config[0][1] = 0x6C; /* 196 kHz / .8 = 245 kHz --> 270 kHz */
  39. break;
  40. }
  41. UNUSED(filter_config);
  42. // furi_hal_subghz_load_registers((uint8_t*)filter_config);
  43. }
  44. static int32_t spectrum_analyzer_worker_thread(void* context) {
  45. furi_assert(context);
  46. SpectrumAnalyzerWorker* instance = context;
  47. FURI_LOG_D("SpectrumWorker", "spectrum_analyzer_worker_thread: Start");
  48. // Start CC1101
  49. subghz_devices_reset(instance->radio_device);
  50. subghz_devices_load_preset(instance->radio_device, FuriHalSubGhzPresetOok650Async, NULL);
  51. subghz_devices_set_frequency(instance->radio_device, 433920000);
  52. subghz_devices_flush_rx(instance->radio_device);
  53. subghz_devices_set_rx(instance->radio_device);
  54. // Default modulation
  55. const uint8_t default_modulation[] = {
  56. /* Frequency Synthesizer Control */
  57. CC1101_FSCTRL0,
  58. 0x00,
  59. CC1101_FSCTRL1,
  60. 0x12, // IF = (26*10^6) / (2^10) * 0x12 = 304687.5 Hz
  61. // Modem Configuration
  62. // CC1101_MDMCFG0,
  63. // 0x00, // Channel spacing is 25kHz
  64. // CC1101_MDMCFG1,
  65. // 0x00, // Channel spacing is 25kHz
  66. // CC1101_MDMCFG2,
  67. // 0x30, // Format ASK/OOK, No preamble/sync
  68. // CC1101_MDMCFG3,
  69. // 0x32, // Data rate is 121.399 kBaud
  70. CC1101_MDMCFG4,
  71. 0x6C, // Rx BW filter is 270.83 kHz
  72. /* Frequency Offset Compensation Configuration */
  73. // CC1101_FOCCFG,
  74. // 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off
  75. /* Automatic Gain Control */
  76. // CC1101_AGCCTRL0,
  77. // 0x91, // 10 - Medium hysteresis, medium asymmetric dead zone, medium gain ; 01 - 16 samples agc; 00 - Normal AGC, 01 - 8dB boundary
  78. // CC1101_AGCCTRL1,
  79. // 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
  80. CC1101_AGCCTRL2,
  81. 0xC0, // 03 - The 3 highest DVGA gain settings can not be used; 000 - MAX LNA+LNA2; 000 - MAIN_TARGET 24 dB
  82. /* Frontend configuration */
  83. // CC1101_FREND0,
  84. // 0x11, // Adjusts current TX LO buffer + high is PATABLE[1]
  85. // CC1101_FREND1,
  86. // 0xB6, //
  87. CC1101_TEST2,
  88. 0x88,
  89. CC1101_TEST1,
  90. 0x31,
  91. CC1101_TEST0,
  92. 0x09,
  93. /* End */
  94. 0,
  95. 0,
  96. // ook_async_patable
  97. 0x00,
  98. 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03
  99. 0x00,
  100. 0x00,
  101. 0x00,
  102. 0x00,
  103. 0x00,
  104. 0x00};
  105. // Narrow modulation
  106. const uint8_t narrow_modulation[] = {
  107. /* Frequency Synthesizer Control */
  108. CC1101_FSCTRL0,
  109. 0x00,
  110. CC1101_FSCTRL1,
  111. 0x00, // IF = (26*10^6) / (2^10) * 0x00 = 0 Hz
  112. // Modem Configuration
  113. // CC1101_MDMCFG0,
  114. // 0x00, // Channel spacing is 25kHz
  115. // CC1101_MDMCFG1,
  116. // 0x00, // Channel spacing is 25kHz
  117. // CC1101_MDMCFG2,
  118. // 0x30, // Format ASK/OOK, No preamble/sync
  119. // CC1101_MDMCFG3,
  120. // 0x32, // Data rate is 121.399 kBaud
  121. CC1101_MDMCFG4,
  122. 0xFC, // Rx BW filter is 58.04 kHz
  123. /* Frequency Offset Compensation Configuration */
  124. // CC1101_FOCCFG,
  125. // 0x18, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off
  126. /* Automatic Gain Control */
  127. CC1101_AGCCTRL0,
  128. 0x30, // 00 - NO hysteresis, symmetric dead zone, high gain ; 11 - 32 samples agc; 00 - Normal AGC, 00 - 8dB boundary
  129. CC1101_AGCCTRL1,
  130. 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
  131. CC1101_AGCCTRL2,
  132. 0x84, // 02 - The 2 highest DVGA gain settings can not be used; 000 - MAX LNA+LNA2; 100 - MAIN_TARGET 36 dB
  133. /* Frontend configuration */
  134. // CC1101_FREND0,
  135. // 0x11, // Adjusts current TX LO buffer + high is PATABLE[1]
  136. // CC1101_FREND1,
  137. // 0xB6, //
  138. CC1101_TEST2,
  139. 0x88,
  140. CC1101_TEST1,
  141. 0x31,
  142. CC1101_TEST0,
  143. 0x09,
  144. /* End */
  145. 0,
  146. 0,
  147. // ook_async_patable
  148. 0x00,
  149. 0xC0, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03
  150. 0x00,
  151. 0x00,
  152. 0x00,
  153. 0x00,
  154. 0x00,
  155. 0x00};
  156. const uint8_t* modulations[] = {default_modulation, narrow_modulation};
  157. while(instance->should_work) {
  158. furi_delay_ms(50);
  159. // FURI_LOG_T("SpectrumWorker", "spectrum_analyzer_worker_thread: Worker Loop");
  160. subghz_devices_idle(instance->radio_device);
  161. subghz_devices_load_preset(
  162. instance->radio_device,
  163. FuriHalSubGhzPresetCustom,
  164. (uint8_t*)modulations[instance->modulation]);
  165. //subghz_devices_load_preset(
  166. // instance->radio_device, FuriHalSubGhzPresetCustom, (uint8_t*)default_modulation);
  167. //furi_hal_subghz_load_custom_preset(modulations[instance->modulation]);
  168. // TODO: Check filter!
  169. // spectrum_analyzer_worker_set_filter(instance);
  170. instance->max_rssi_dec = 0;
  171. // Visit each channel non-consecutively
  172. for(uint8_t ch_offset = 0, chunk = 0; ch_offset < CHUNK_SIZE;
  173. ++chunk >= NUM_CHUNKS && ++ch_offset && (chunk = 0)) {
  174. uint8_t ch = chunk * CHUNK_SIZE + ch_offset;
  175. if(subghz_devices_is_frequency_valid(
  176. instance->radio_device,
  177. instance->channel0_frequency + (ch * instance->spacing)))
  178. subghz_devices_set_frequency(
  179. instance->radio_device,
  180. instance->channel0_frequency + (ch * instance->spacing));
  181. subghz_devices_set_rx(instance->radio_device);
  182. furi_delay_ms(3);
  183. // dec dBm
  184. //max_ss = 127 -> -10.5
  185. //max_ss = 0 -> -74.0
  186. //max_ss = 255 -> -74.5
  187. //max_ss = 128 -> -138.0
  188. instance->channel_ss[ch] = (subghz_devices_get_rssi(instance->radio_device) + 138) * 2;
  189. if(instance->channel_ss[ch] > instance->max_rssi_dec) {
  190. instance->max_rssi_dec = instance->channel_ss[ch];
  191. instance->max_rssi = (instance->channel_ss[ch] / 2) - 138;
  192. instance->max_rssi_channel = ch;
  193. }
  194. subghz_devices_idle(instance->radio_device);
  195. }
  196. // FURI_LOG_T("SpectrumWorker", "channel_ss[0]: %u", instance->channel_ss[0]);
  197. // Report results back to main thread
  198. if(instance->callback) {
  199. instance->callback(
  200. (void*)&(instance->channel_ss),
  201. instance->max_rssi,
  202. instance->max_rssi_dec,
  203. instance->max_rssi_channel,
  204. instance->callback_context);
  205. }
  206. }
  207. return 0;
  208. }
  209. SpectrumAnalyzerWorker* spectrum_analyzer_worker_alloc() {
  210. FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_alloc: Start");
  211. SpectrumAnalyzerWorker* instance = malloc(sizeof(SpectrumAnalyzerWorker));
  212. instance->thread = furi_thread_alloc();
  213. furi_thread_set_name(instance->thread, "SpectrumWorker");
  214. furi_thread_set_stack_size(instance->thread, 2048);
  215. furi_thread_set_context(instance->thread, instance);
  216. furi_thread_set_callback(instance->thread, spectrum_analyzer_worker_thread);
  217. subghz_devices_init();
  218. instance->radio_device =
  219. radio_device_loader_set(instance->radio_device, SubGhzRadioDeviceTypeExternalCC1101);
  220. FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_alloc: End");
  221. return instance;
  222. }
  223. void spectrum_analyzer_worker_free(SpectrumAnalyzerWorker* instance) {
  224. FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_free");
  225. furi_assert(instance);
  226. furi_thread_free(instance->thread);
  227. subghz_devices_sleep(instance->radio_device);
  228. radio_device_loader_end(instance->radio_device);
  229. subghz_devices_deinit();
  230. free(instance);
  231. }
  232. void spectrum_analyzer_worker_set_callback(
  233. SpectrumAnalyzerWorker* instance,
  234. SpectrumAnalyzerWorkerCallback callback,
  235. void* context) {
  236. furi_assert(instance);
  237. instance->callback = callback;
  238. instance->callback_context = context;
  239. }
  240. void spectrum_analyzer_worker_set_frequencies(
  241. SpectrumAnalyzerWorker* instance,
  242. uint32_t channel0_frequency,
  243. uint32_t spacing,
  244. uint8_t width) {
  245. furi_assert(instance);
  246. FURI_LOG_D(
  247. "SpectrumWorker",
  248. "spectrum_analyzer_worker_set_frequencies - channel0_frequency= %lu - spacing = %lu - width = %u",
  249. channel0_frequency,
  250. spacing,
  251. width);
  252. instance->channel0_frequency = channel0_frequency;
  253. instance->spacing = spacing;
  254. instance->width = width;
  255. }
  256. void spectrum_analyzer_worker_set_modulation(SpectrumAnalyzerWorker* instance, uint8_t modulation) {
  257. furi_assert(instance);
  258. FURI_LOG_D(
  259. "SpectrumWorker", "spectrum_analyzer_worker_set_modulation - modulation = %u", modulation);
  260. instance->modulation = modulation;
  261. }
  262. void spectrum_analyzer_worker_start(SpectrumAnalyzerWorker* instance) {
  263. FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_start");
  264. furi_assert(instance);
  265. furi_assert(instance->should_work == false);
  266. instance->should_work = true;
  267. furi_thread_start(instance->thread);
  268. }
  269. void spectrum_analyzer_worker_stop(SpectrumAnalyzerWorker* instance) {
  270. FURI_LOG_D("Spectrum", "spectrum_analyzer_worker_stop");
  271. furi_assert(instance);
  272. furi_assert(instance->should_work == true);
  273. instance->should_work = false;
  274. furi_thread_join(instance->thread);
  275. }