test_loragw_hal_rx.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. /*
  2. / _____) _ | |
  3. ( (____ _____ ____ _| |_ _____ ____| |__
  4. \____ \| ___ | (_ _) ___ |/ ___) _ \
  5. _____) ) ____| | | || |_| ____( (___| | | |
  6. (______/|_____)_|_|_| \__)_____)\____)_| |_|
  7. (C)2019 Semtech
  8. Description:
  9. Minimum test program for HAL RX capability
  10. License: Revised BSD License, see LICENSE.TXT file include in the project
  11. */
  12. /* -------------------------------------------------------------------------- */
  13. /* --- DEPENDANCIES --------------------------------------------------------- */
  14. /* fix an issue between POSIX and C99 */
  15. #if __STDC_VERSION__ >= 199901L
  16. #define _XOPEN_SOURCE 600
  17. #else
  18. #define _XOPEN_SOURCE 500
  19. #endif
  20. #include <stdint.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <unistd.h>
  25. #include <signal.h>
  26. #include <math.h>
  27. #include <getopt.h>
  28. #include "loragw_hal.h"
  29. #include "loragw_reg.h"
  30. #include "loragw_aux.h"
  31. /* -------------------------------------------------------------------------- */
  32. /* --- PRIVATE MACROS ------------------------------------------------------- */
  33. #define COM_TYPE_DEFAULT LGW_COM_SPI
  34. #define COM_PATH_DEFAULT "/dev/spidev0.0"
  35. #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
  36. #define RAND_RANGE(min, max) (rand() % (max + 1 - min) + min)
  37. /* -------------------------------------------------------------------------- */
  38. /* --- PRIVATE CONSTANTS ---------------------------------------------------- */
  39. #define DEFAULT_FREQ_HZ 868500000U
  40. /* -------------------------------------------------------------------------- */
  41. /* --- PRIVATE VARIABLES ---------------------------------------------------- */
  42. static int exit_sig = 0; /* 1 -> application terminates cleanly (shut down hardware, close open files, etc) */
  43. static int quit_sig = 0; /* 1 -> application terminates without shutting down the hardware */
  44. /* -------------------------------------------------------------------------- */
  45. /* --- PRIVATE FUNCTIONS ---------------------------------------------------- */
  46. static void sig_handler(int sigio) {
  47. if (sigio == SIGQUIT) {
  48. quit_sig = 1;
  49. } else if ((sigio == SIGINT) || (sigio == SIGTERM)) {
  50. exit_sig = 1;
  51. }
  52. }
  53. void usage(void) {
  54. printf("Library version information: %s\n", lgw_version_info());
  55. printf("Available options:\n");
  56. printf(" -h print this help\n");
  57. printf(" -u set COM type as USB (default is SPI)\n");
  58. printf(" -d <path> COM path to be used to connect the concentrator\n");
  59. printf(" => default path: " COM_PATH_DEFAULT "\n");
  60. printf(" -k <uint> Concentrator clock source (Radio A or Radio B) [0..1]\n");
  61. printf(" -r <uint> Radio type (1255, 1257, 1250)\n");
  62. printf(" -a <float> Radio A RX frequency in MHz\n");
  63. printf(" -b <float> Radio B RX frequency in MHz\n");
  64. printf(" -o <float> RSSI Offset to be applied in dB\n");
  65. printf(" -n <uint> Number of packet received with CRC OK for each HAL start/stop loop\n");
  66. printf(" -z <uint> Size of the RX packet array to be passed to lgw_receive()\n");
  67. printf(" -m <uint> Channel frequency plan mode [0:LoRaWAN-like, 1:Same frequency for all channels (-400000Hz on RF0)]\n");
  68. printf(" -j Set radio in single input mode (SX1250 only)\n");
  69. printf( "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" );
  70. printf(" --fdd Enable Full-Duplex mode (CN490 reference design)\n");
  71. }
  72. /* -------------------------------------------------------------------------- */
  73. /* --- MAIN FUNCTION -------------------------------------------------------- */
  74. int main(int argc, char **argv)
  75. {
  76. /* SPI interfaces */
  77. const char com_path_default[] = COM_PATH_DEFAULT;
  78. const char * com_path = com_path_default;
  79. lgw_com_type_t com_type = COM_TYPE_DEFAULT;
  80. struct sigaction sigact; /* SIGQUIT&SIGINT&SIGTERM signal handling */
  81. int i, j, x;
  82. uint32_t fa = DEFAULT_FREQ_HZ;
  83. uint32_t fb = DEFAULT_FREQ_HZ;
  84. double arg_d = 0.0;
  85. unsigned int arg_u;
  86. uint8_t clocksource = 0;
  87. lgw_radio_type_t radio_type = LGW_RADIO_TYPE_NONE;
  88. uint8_t max_rx_pkt = 16;
  89. bool single_input_mode = false;
  90. float rssi_offset = 0.0;
  91. bool full_duplex = false;
  92. struct lgw_conf_board_s boardconf;
  93. struct lgw_conf_rxrf_s rfconf;
  94. struct lgw_conf_rxif_s ifconf;
  95. unsigned long nb_pkt_crc_ok = 0, nb_loop = 0, cnt_loop;
  96. int nb_pkt;
  97. uint8_t channel_mode = 0; /* LoRaWAN-like */
  98. const int32_t channel_if_mode0[9] = {
  99. -400000,
  100. -200000,
  101. 0,
  102. -400000,
  103. -200000,
  104. 0,
  105. 200000,
  106. 400000,
  107. -200000 /* lora service */
  108. };
  109. const int32_t channel_if_mode1[9] = {
  110. -400000,
  111. -400000,
  112. -400000,
  113. -400000,
  114. -400000,
  115. -400000,
  116. -400000,
  117. -400000,
  118. -400000 /* lora service */
  119. };
  120. const uint8_t channel_rfchain_mode0[9] = { 1, 1, 1, 0, 0, 0, 0, 0, 1 };
  121. const uint8_t channel_rfchain_mode1[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  122. /* Parameter parsing */
  123. int option_index = 0;
  124. static struct option long_options[] = {
  125. {"fdd", no_argument, 0, 0},
  126. {0, 0, 0, 0}
  127. };
  128. /* parse command line options */
  129. while ((i = getopt_long(argc, argv, "hja:b:k:r:n:z:m:o:d:u", long_options, &option_index)) != -1) {
  130. switch (i) {
  131. case 'h':
  132. usage();
  133. return -1;
  134. break;
  135. case 'd': /* <char> COM path */
  136. if (optarg != NULL) {
  137. com_path = optarg;
  138. }
  139. break;
  140. case 'u': /* Configure USB connection type */
  141. com_type = LGW_COM_USB;
  142. break;
  143. case 'r': /* <uint> Radio type */
  144. i = sscanf(optarg, "%u", &arg_u);
  145. if ((i != 1) || ((arg_u != 1255) && (arg_u != 1257) && (arg_u != 1250))) {
  146. printf("ERROR: argument parsing of -r argument. Use -h to print help\n");
  147. return EXIT_FAILURE;
  148. } else {
  149. switch (arg_u) {
  150. case 1255:
  151. radio_type = LGW_RADIO_TYPE_SX1255;
  152. break;
  153. case 1257:
  154. radio_type = LGW_RADIO_TYPE_SX1257;
  155. break;
  156. default: /* 1250 */
  157. radio_type = LGW_RADIO_TYPE_SX1250;
  158. break;
  159. }
  160. }
  161. break;
  162. case 'k': /* <uint> Clock Source */
  163. i = sscanf(optarg, "%u", &arg_u);
  164. if ((i != 1) || (arg_u > 1)) {
  165. printf("ERROR: argument parsing of -k argument. Use -h to print help\n");
  166. return EXIT_FAILURE;
  167. } else {
  168. clocksource = (uint8_t)arg_u;
  169. }
  170. break;
  171. case 'j': /* Set radio in single input mode */
  172. single_input_mode = true;
  173. break;
  174. case 'a': /* <float> Radio A RX frequency in MHz */
  175. i = sscanf(optarg, "%lf", &arg_d);
  176. if (i != 1) {
  177. printf("ERROR: argument parsing of -f argument. Use -h to print help\n");
  178. return EXIT_FAILURE;
  179. } else {
  180. fa = (uint32_t)((arg_d*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */
  181. }
  182. break;
  183. case 'b': /* <float> Radio B RX frequency in MHz */
  184. i = sscanf(optarg, "%lf", &arg_d);
  185. if (i != 1) {
  186. printf("ERROR: argument parsing of -f argument. Use -h to print help\n");
  187. return EXIT_FAILURE;
  188. } else {
  189. fb = (uint32_t)((arg_d*1e6) + 0.5); /* .5 Hz offset to get rounding instead of truncating */
  190. }
  191. break;
  192. case 'n': /* <uint> NUmber of packets to be received before exiting */
  193. i = sscanf(optarg, "%u", &arg_u);
  194. if (i != 1) {
  195. printf("ERROR: argument parsing of -n argument. Use -h to print help\n");
  196. return EXIT_FAILURE;
  197. } else {
  198. nb_loop = arg_u;
  199. }
  200. break;
  201. case 'z': /* <uint> Size of the RX packet array to be passed to lgw_receive() */
  202. i = sscanf(optarg, "%u", &arg_u);
  203. if (i != 1) {
  204. printf("ERROR: argument parsing of -z argument. Use -h to print help\n");
  205. return EXIT_FAILURE;
  206. } else {
  207. max_rx_pkt = arg_u;
  208. }
  209. break;
  210. case 'm':
  211. i = sscanf(optarg, "%u", &arg_u);
  212. if ((i != 1) || (arg_u > 1)) {
  213. printf("ERROR: argument parsing of -m argument. Use -h to print help\n");
  214. return EXIT_FAILURE;
  215. } else {
  216. channel_mode = arg_u;
  217. }
  218. break;
  219. case 'o': /* <float> RSSI offset in dB */
  220. i = sscanf(optarg, "%lf", &arg_d);
  221. if (i != 1) {
  222. printf("ERROR: argument parsing of -o argument. Use -h to print help\n");
  223. return EXIT_FAILURE;
  224. } else {
  225. rssi_offset = (float)arg_d;
  226. }
  227. break;
  228. case 0:
  229. if (strcmp(long_options[option_index].name, "fdd") == 0) {
  230. full_duplex = true;
  231. } else {
  232. printf("ERROR: argument parsing options. Use -h to print help\n");
  233. return EXIT_FAILURE;
  234. }
  235. break;
  236. default:
  237. printf("ERROR: argument parsing\n");
  238. usage();
  239. return -1;
  240. }
  241. }
  242. /* configure signal handling */
  243. sigemptyset(&sigact.sa_mask);
  244. sigact.sa_flags = 0;
  245. sigact.sa_handler = sig_handler;
  246. sigaction(SIGQUIT, &sigact, NULL);
  247. sigaction(SIGINT, &sigact, NULL);
  248. sigaction(SIGTERM, &sigact, NULL);
  249. printf("===== sx1302 HAL RX test =====\n");
  250. /* Configure the gateway */
  251. memset( &boardconf, 0, sizeof boardconf);
  252. boardconf.lorawan_public = true;
  253. boardconf.clksrc = clocksource;
  254. boardconf.full_duplex = full_duplex;
  255. boardconf.com_type = com_type;
  256. strncpy(boardconf.com_path, com_path, sizeof boardconf.com_path);
  257. boardconf.com_path[sizeof boardconf.com_path - 1] = '\0'; /* ensure string termination */
  258. if (lgw_board_setconf(&boardconf) != LGW_HAL_SUCCESS) {
  259. printf("ERROR: failed to configure board\n");
  260. return EXIT_FAILURE;
  261. }
  262. /* set configuration for RF chains */
  263. memset( &rfconf, 0, sizeof rfconf);
  264. rfconf.enable = true;
  265. rfconf.freq_hz = fa;
  266. rfconf.type = radio_type;
  267. rfconf.rssi_offset = rssi_offset;
  268. rfconf.tx_enable = false;
  269. rfconf.single_input_mode = single_input_mode;
  270. if (lgw_rxrf_setconf(0, &rfconf) != LGW_HAL_SUCCESS) {
  271. printf("ERROR: failed to configure rxrf 0\n");
  272. return EXIT_FAILURE;
  273. }
  274. memset( &rfconf, 0, sizeof rfconf);
  275. rfconf.enable = true;
  276. rfconf.freq_hz = fb;
  277. rfconf.type = radio_type;
  278. rfconf.rssi_offset = rssi_offset;
  279. rfconf.tx_enable = false;
  280. rfconf.single_input_mode = single_input_mode;
  281. if (lgw_rxrf_setconf(1, &rfconf) != LGW_HAL_SUCCESS) {
  282. printf("ERROR: failed to configure rxrf 1\n");
  283. return EXIT_FAILURE;
  284. }
  285. /* set configuration for LoRa multi-SF channels (bandwidth cannot be set) */
  286. memset(&ifconf, 0, sizeof(ifconf));
  287. for (i = 0; i < 8; i++) {
  288. ifconf.enable = true;
  289. if (channel_mode == 0) {
  290. ifconf.rf_chain = channel_rfchain_mode0[i];
  291. ifconf.freq_hz = channel_if_mode0[i];
  292. } else if (channel_mode == 1) {
  293. ifconf.rf_chain = channel_rfchain_mode1[i];
  294. ifconf.freq_hz = channel_if_mode1[i];
  295. } else {
  296. printf("ERROR: channel mode not supported\n");
  297. return EXIT_FAILURE;
  298. }
  299. ifconf.datarate = DR_LORA_SF7;
  300. if (lgw_rxif_setconf(i, &ifconf) != LGW_HAL_SUCCESS) {
  301. printf("ERROR: failed to configure rxif %d\n", i);
  302. return EXIT_FAILURE;
  303. }
  304. }
  305. /* set configuration for LoRa Service channel */
  306. memset(&ifconf, 0, sizeof(ifconf));
  307. ifconf.rf_chain = channel_rfchain_mode0[i];
  308. ifconf.freq_hz = channel_if_mode0[i];
  309. ifconf.datarate = DR_LORA_SF7;
  310. ifconf.bandwidth = BW_250KHZ;
  311. if (lgw_rxif_setconf(8, &ifconf) != LGW_HAL_SUCCESS) {
  312. printf("ERROR: failed to configure rxif for LoRa service channel\n");
  313. return EXIT_FAILURE;
  314. }
  315. /* set the buffer size to hold received packets */
  316. struct lgw_pkt_rx_s rxpkt[max_rx_pkt];
  317. printf("INFO: rxpkt buffer size is set to %u\n", max_rx_pkt);
  318. printf("INFO: Select channel mode %u\n", channel_mode);
  319. /* Loop until user quits */
  320. cnt_loop = 0;
  321. while( (quit_sig != 1) && (exit_sig != 1) )
  322. {
  323. cnt_loop += 1;
  324. if (com_type == LGW_COM_SPI) {
  325. /* Board reset */
  326. if (system("./reset_lgw.sh start") != 0) {
  327. printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
  328. exit(EXIT_FAILURE);
  329. }
  330. }
  331. /* connect, configure and start the LoRa concentrator */
  332. x = lgw_start();
  333. if (x != 0) {
  334. printf("ERROR: failed to start the gateway\n");
  335. return EXIT_FAILURE;
  336. }
  337. /* Loop until we have enough packets with CRC OK */
  338. printf("Waiting for packets...\n");
  339. nb_pkt_crc_ok = 0;
  340. while (((nb_pkt_crc_ok < nb_loop) || nb_loop == 0) && (quit_sig != 1) && (exit_sig != 1)) {
  341. /* fetch N packets */
  342. nb_pkt = lgw_receive(ARRAY_SIZE(rxpkt), rxpkt);
  343. if (nb_pkt == 0) {
  344. wait_ms(10);
  345. } else {
  346. for (i = 0; i < nb_pkt; i++) {
  347. if (rxpkt[i].status == STAT_CRC_OK) {
  348. nb_pkt_crc_ok += 1;
  349. }
  350. printf("\n----- %s packet -----\n", (rxpkt[i].modulation == MOD_LORA) ? "LoRa" : "FSK");
  351. printf(" count_us: %u\n", rxpkt[i].count_us);
  352. printf(" size: %u\n", rxpkt[i].size);
  353. printf(" chan: %u\n", rxpkt[i].if_chain);
  354. printf(" status: 0x%02X\n", rxpkt[i].status);
  355. printf(" datr: %u\n", rxpkt[i].datarate);
  356. printf(" codr: %u\n", rxpkt[i].coderate);
  357. printf(" rf_chain %u\n", rxpkt[i].rf_chain);
  358. printf(" freq_hz %u\n", rxpkt[i].freq_hz);
  359. printf(" snr_avg: %.1f\n", rxpkt[i].snr);
  360. printf(" rssi_chan:%.1f\n", rxpkt[i].rssic);
  361. printf(" rssi_sig :%.1f\n", rxpkt[i].rssis);
  362. printf(" crc: 0x%04X\n", rxpkt[i].crc);
  363. for (j = 0; j < rxpkt[i].size; j++) {
  364. printf("%02X ", rxpkt[i].payload[j]);
  365. }
  366. printf("\n");
  367. }
  368. printf("Received %d packets (total:%lu)\n", nb_pkt, nb_pkt_crc_ok);
  369. }
  370. }
  371. printf( "\nNb valid packets received: %lu CRC OK (%lu)\n", nb_pkt_crc_ok, cnt_loop );
  372. /* Stop the gateway */
  373. x = lgw_stop();
  374. if (x != 0) {
  375. printf("ERROR: failed to stop the gateway\n");
  376. return EXIT_FAILURE;
  377. }
  378. if (com_type == LGW_COM_SPI) {
  379. /* Board reset */
  380. if (system("./reset_lgw.sh stop") != 0) {
  381. printf("ERROR: failed to reset SX1302, check your reset_lgw.sh script\n");
  382. exit(EXIT_FAILURE);
  383. }
  384. }
  385. }
  386. printf("=========== Test End ===========\n");
  387. return 0;
  388. }
  389. /* --- EOF ------------------------------------------------------------------ */