Advertisement
Guest User

Untitled

a guest
Jul 7th, 2018
900
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.17 KB | None | 0 0
  1. /*
  2. * libiio - Edited AD9361 IIO streaming example
  3. * See the original at: https://github.com/analogdevicesinc/libiio/blob/master/examples/ad9361-iiostream.c
  4. *
  5. */
  6.  
  7. /* Set these defines to prevent fopen() warning and to be able to use
  8. constants from math.h
  9. */
  10. #define _CRT_SECURE_NO_WARNINGS
  11. #define _USE_MATH_DEFINES
  12.  
  13. #include <stdbool.h>
  14. #include <stdint.h>
  15. #include <string.h>
  16. #include <signal.h>
  17. #include <stdio.h>
  18. #include <iio.h>
  19. #include <math.h>
  20.  
  21. /* debugging macros */
  22. //#define DEBUG_SINCOS
  23. //#define USE_DITHERING
  24. #define RX_ENABLED
  25. #define TX_ENABLED
  26.  
  27. /* helper macros */
  28. #define MHZ(x) ((long long)(x*1000000.0 + .5))
  29. #define GHZ(x) ((long long)(x*1000000000.0 + .5))
  30.  
  31. #define TX_BUFFER_SIZE 1024*1024*2
  32. #define RX_BUFFER_SIZE 1024*1024*2
  33. #define RX_BUFFER_COUNT 4
  34. #define TX_BUFFER_COUNT 4
  35.  
  36. #define ASSERT(expr) { \
  37. if (!(expr)) { \
  38. (void) fprintf(stderr, "assertion failed (%s:%d)\n", __FILE__, __LINE__); \
  39. (void) abort(); \
  40. } \
  41. }
  42.  
  43. /* RX is input, TX is output */
  44. enum iodev { RX, TX };
  45.  
  46. /* common RX and TX streaming params */
  47. struct stream_cfg {
  48. long long bw_hz; // Analog banwidth in Hz
  49. long long fs_hz; // Baseband sample rate in Hz
  50. long long lo_hz; // Local oscillator frequency in Hz
  51. const char* rfport; // Port name
  52. };
  53.  
  54. /* static scratch mem for strings */
  55. static char tmpstr[64];
  56.  
  57. /* IIO structs required for streaming */
  58. static struct iio_context *ctx = NULL;
  59. static struct iio_channel *rx0_i = NULL;
  60. static struct iio_channel *rx0_q = NULL;
  61. static struct iio_channel *tx0_i = NULL;
  62. static struct iio_channel *tx0_q = NULL;
  63. static struct iio_buffer *rxbuf = NULL;
  64. static struct iio_buffer *txbuf = NULL;
  65.  
  66. static bool stop = false;
  67.  
  68. // Writing RX samples to a file
  69. FILE *rx_capture = NULL;
  70. const char *filename = "e:\\capture_rx.bin";
  71.  
  72. // Lookup tables for sine and cosine generation
  73. int16_t *sin_table = NULL;
  74. int16_t *cos_table = NULL;
  75.  
  76. /* cleanup and exit */
  77. static void shutdown()
  78. {
  79. printf("* Destroying sine and cosine lookup tables\n");
  80. free(sin_table);
  81. free(cos_table);
  82.  
  83. printf("* Flushing RX capture file to disk\n");
  84. fflush(rx_capture);
  85.  
  86. printf("* Closing RX capture file\n");
  87. fclose(rx_capture);
  88.  
  89. printf("* Destroying buffers\n");
  90. if (rxbuf) { iio_buffer_destroy(rxbuf); }
  91. if (txbuf) { iio_buffer_destroy(txbuf); }
  92.  
  93. printf("* Disabling streaming channels\n");
  94. if (rx0_i) { iio_channel_disable(rx0_i); }
  95. if (rx0_q) { iio_channel_disable(rx0_q); }
  96. if (tx0_i) { iio_channel_disable(tx0_i); }
  97. if (tx0_q) { iio_channel_disable(tx0_q); }
  98.  
  99. printf("* Destroying context\n");
  100. if (ctx) { iio_context_destroy(ctx); }
  101. system("PAUSE");
  102. exit(0);
  103. }
  104.  
  105. static void handle_sig(int sig)
  106. {
  107. printf("Waiting for process to finish...\n");
  108. stop = true;
  109. }
  110.  
  111. /* Generating noise for dithering */
  112. long long noise(long long max_abs_value)
  113. {
  114. return (((long long)rand() % (max_abs_value + 1)) - max_abs_value);
  115. }
  116.  
  117. /* check return value of attr_write function */
  118. static void errchk(int v, const char* what) {
  119. if (v < 0) { fprintf(stderr, "Error %d writing to channel \"%s\"\nvalue may not be supported.\n", v, what); shutdown(); }
  120. }
  121.  
  122. /* write attribute: long long int */
  123. static void wr_ch_lli(struct iio_channel *chn, const char* what, long long val)
  124. {
  125. errchk(iio_channel_attr_write_longlong(chn, what, val), what);
  126. }
  127.  
  128. /* write attribute: string */
  129. static void wr_ch_str(struct iio_channel *chn, const char* what, const char* str)
  130. {
  131. errchk(iio_channel_attr_write(chn, what, str), what);
  132. }
  133.  
  134. /* helper function generating channel names */
  135. static char* get_ch_name(const char* type, int id)
  136. {
  137. snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id);
  138. return tmpstr;
  139. }
  140.  
  141. /* returns ad9361 phy device */
  142. static struct iio_device* get_ad9361_phy(struct iio_context *ctx)
  143. {
  144. struct iio_device *dev = iio_context_find_device(ctx, "ad9361-phy");
  145. ASSERT(dev && "No ad9361-phy found");
  146. return dev;
  147. }
  148.  
  149. /* finds AD9361 streaming IIO devices */
  150. static bool get_ad9361_stream_dev(struct iio_context *ctx, enum iodev d, struct iio_device **dev)
  151. {
  152. switch (d) {
  153. case TX: *dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); return *dev != NULL;
  154. case RX: *dev = iio_context_find_device(ctx, "cf-ad9361-lpc"); return *dev != NULL;
  155. default: ASSERT(0); return false;
  156. }
  157. }
  158.  
  159. /* finds AD9361 streaming IIO channels */
  160. static bool get_ad9361_stream_ch(struct iio_context *ctx, enum iodev d, struct iio_device *dev, int chid, struct iio_channel **chn)
  161. {
  162. *chn = iio_device_find_channel(dev, get_ch_name("voltage", chid), d == TX);
  163. if (!*chn)
  164. *chn = iio_device_find_channel(dev, get_ch_name("altvoltage", chid), d == TX);
  165. return *chn != NULL;
  166. }
  167.  
  168. /* finds AD9361 phy IIO configuration channel with id chid */
  169. static bool get_phy_chan(struct iio_context *ctx, enum iodev d, int chid, struct iio_channel **chn)
  170. {
  171. switch (d) {
  172. case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), false); return *chn != NULL;
  173. case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), true); return *chn != NULL;
  174. default: ASSERT(0); return false;
  175. }
  176. }
  177.  
  178. /* finds AD9361 local oscillator IIO configuration channels */
  179. static bool get_lo_chan(struct iio_context *ctx, enum iodev d, struct iio_channel **chn)
  180. {
  181. switch (d) {
  182. // LO chan is always output, i.e. true
  183. case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 0), true); return *chn != NULL;
  184. case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 1), true); return *chn != NULL;
  185. default: ASSERT(0); return false;
  186. }
  187. }
  188.  
  189. /* applies streaming configuration through IIO */
  190. bool cfg_ad9361_streaming_ch(struct iio_context *ctx, struct stream_cfg *cfg, enum iodev type, int chid)
  191. {
  192. struct iio_channel *chn = NULL;
  193.  
  194. // Configure phy and lo channels
  195. printf("* Acquiring AD9361 phy channel %d\n", chid);
  196. if (!get_phy_chan(ctx, type, chid, &chn)) { return false; }
  197. wr_ch_str(chn, "rf_port_select", cfg->rfport);
  198. wr_ch_lli(chn, "rf_bandwidth", cfg->bw_hz);
  199. wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz);
  200.  
  201. // Configure RX AGC mode and gain
  202. if (type == RX) {
  203. wr_ch_str(chn, "gain_control_mode", "manual");
  204. wr_ch_str(chn, "hardwaregain", "0");
  205. }
  206.  
  207. // Configure LO channel
  208. printf("* Acquiring AD9361 %s lo channel\n", type == TX ? "TX" : "RX");
  209. if (!get_lo_chan(ctx, type, &chn)) { return false; }
  210. wr_ch_lli(chn, "frequency", cfg->lo_hz);
  211. return true;
  212. }
  213.  
  214. /* simple configuration and streaming */
  215. int main (int argc, char **argv)
  216. {
  217. // Streaming devices
  218. struct iio_device *tx;
  219. struct iio_device *rx;
  220.  
  221. // Check for overflow/underflow
  222. int ret;
  223. uint32_t val;
  224.  
  225. // RX and TX sample counters
  226. size_t nrx = 0;
  227. size_t ntx = 0;
  228.  
  229. // AGC and gain configurations
  230. struct iio_channel *chn = NULL;
  231.  
  232. // Stream configurations
  233. struct stream_cfg rxcfg;
  234. struct stream_cfg txcfg;
  235.  
  236. // Listen to ctrl+c and ASSERT
  237. signal(SIGINT, handle_sig);
  238. signal(SIGTERM, handle_sig);
  239.  
  240. // RX stream config
  241. rxcfg.bw_hz = MHZ(2); // 2 MHz rf bandwidth
  242. rxcfg.fs_hz = MHZ(2.5); // 2.5 MS/s rx sample rate
  243. rxcfg.lo_hz = GHZ(2.4); // 2.5 GHz rf frequency
  244. rxcfg.rfport = "A_BALANCED"; // port A (select for rf freq.)
  245.  
  246. // TX stream config
  247. txcfg.bw_hz = MHZ(1.5); // 1.5 MHz rf bandwidth
  248. txcfg.fs_hz = MHZ(2.5); // 2.5 MS/s tx sample rate
  249. txcfg.lo_hz = GHZ(2.4); // 2.5 GHz rf frequency
  250. txcfg.rfport = "A"; // port A (select for rf freq.)
  251.  
  252. // Parameteres for sine wave generation
  253. const long long f0 = MHZ(0.5);
  254. const double f0_over_fs = (double)f0/(double)txcfg.fs_hz;
  255. long long n = 0;
  256.  
  257. // Precalculate sine and cosine tables
  258. printf("* Calculating sine and cosine lookup tables\n");
  259.  
  260. sin_table = (int16_t*)malloc(sizeof(int16_t)*txcfg.fs_hz);
  261. if (!sin_table) {
  262. perror("Failed to allocate memory for sine lookup table");
  263. shutdown();
  264. }
  265.  
  266. cos_table = (int16_t*)malloc(sizeof(int16_t)*txcfg.fs_hz);
  267. if (!cos_table) {
  268. perror("Failed to allocate memory for cosine lookup table");
  269. shutdown();
  270. }
  271.  
  272. double temp_I, temp_Q;
  273.  
  274. #ifdef USE_DITHERING
  275. for (long long i = 0; i < txcfg.fs_hz; i++, n++) {
  276. temp_I = cos(2*M_PI * f0_over_fs * (i + noise(1))) * 32767;
  277. temp_Q = sin(2*M_PI * f0_over_fs * (i + noise(1))) * 32767;
  278. cos_table[i] = (int16_t)temp_I;
  279. sin_table[i] = (int16_t)temp_Q;
  280. }
  281. #else
  282. for (long long i = 0; i < txcfg.fs_hz; i++, n++) {
  283. temp_I = cos(2*M_PI*f0_over_fs*i) * 32767;
  284. temp_Q = sin(2*M_PI*f0_over_fs*i) * 32767;
  285. cos_table[i] = (int16_t)temp_I;
  286. sin_table[i] = (int16_t)temp_Q;
  287. }
  288. #endif
  289.  
  290. #ifdef DEBUG_SINCOS
  291. printf("Debuggiranje...\n");
  292. FILE *file = NULL;
  293. file = fopen("e:\\sincos.bin", "wb");
  294. fwrite(cos_table, sizeof(int16_t), txcfg.fs_hz, file);
  295. fclose(file);
  296. free(sin_table);
  297. free(cos_table);
  298. return 0;
  299. #endif
  300.  
  301. printf("* Open RX stream capture file\n");
  302. rx_capture = fopen(filename, "wb");
  303. if (!rx_capture) {
  304. perror("Could not open RX stream capture file");
  305. shutdown();
  306. }
  307.  
  308. printf("* Acquiring IIO network context\n");
  309. ASSERT((ctx = iio_create_network_context("192.168.3.2")) && "No context");
  310. ASSERT(iio_context_get_devices_count(ctx) > 0 && "No devices");
  311.  
  312. printf("* Acquiring AD9361 streaming devices\n");
  313. ASSERT(get_ad9361_stream_dev(ctx, TX, &tx) && "No tx dev found");
  314. ASSERT(get_ad9361_stream_dev(ctx, RX, &rx) && "No rx dev found");
  315.  
  316. printf("* Configuring AD9361 for streaming\n");
  317. ASSERT(cfg_ad9361_streaming_ch(ctx, &rxcfg, RX, 0) && "RX port 0 not found");
  318. ASSERT(cfg_ad9361_streaming_ch(ctx, &txcfg, TX, 0) && "TX port 0 not found");
  319.  
  320. printf("* Initializing AD9361 IIO streaming channels\n");
  321. ASSERT(get_ad9361_stream_ch(ctx, RX, rx, 0, &rx0_i) && "RX0 chan i not found");
  322. ASSERT(get_ad9361_stream_ch(ctx, RX, rx, 1, &rx0_q) && "RX0 chan q not found");
  323. ASSERT(get_ad9361_stream_ch(ctx, TX, tx, 0, &tx0_i) && "TX0 chan i not found");
  324. ASSERT(get_ad9361_stream_ch(ctx, TX, tx, 1, &tx0_q) && "TX0 chan q not found");
  325.  
  326. /* Set the remote kernel number of buffers */
  327. /* See for more details: https://ez.analog.com/thread/85655 */
  328. if (iio_device_set_kernel_buffers_count(rx, RX_BUFFER_COUNT) < 0) {
  329. fprintf(stderr, "Could not change number of RX kernel buffers\n");
  330. shutdown();
  331. }
  332.  
  333. if (iio_device_set_kernel_buffers_count(tx, TX_BUFFER_COUNT) < 0) {
  334. fprintf(stderr, "Could not change number of TX kernel buffers\n");
  335. shutdown();
  336. }
  337.  
  338. printf("* Enabling IIO streaming channels\n");
  339.  
  340. iio_channel_enable(rx0_i);
  341. iio_channel_enable(rx0_q);
  342.  
  343. iio_channel_enable(tx0_i);
  344. iio_channel_enable(tx0_q);
  345.  
  346. printf("* Creating non-cyclic IIO buffers\n");
  347. rxbuf = iio_device_create_buffer(rx, RX_BUFFER_SIZE, false);
  348. if (!rxbuf) {
  349. perror("Could not create RX buffer");
  350. shutdown();
  351. }
  352.  
  353. txbuf = iio_device_create_buffer(tx, TX_BUFFER_SIZE, false);
  354. if (!txbuf) {
  355. perror("Could not create TX buffer");
  356. shutdown();
  357. }
  358.  
  359. /* Clear overflow/underflow status bits status bits */
  360. iio_device_reg_write(rx, 0x80000088, 0x6);
  361.  
  362. printf("* Starting IO streaming (press CTRL+C to cancel)\n");
  363. while (!stop)
  364. {
  365. ssize_t nbytes_rx = 0, nbytes_tx = 0;
  366. char *p_dat, *p_end, *p_start;
  367. ptrdiff_t p_inc;
  368. ptrdiff_t p_diff;
  369.  
  370. // WRITE: Get pointers to TX buf and write IQ to TX buf port 0
  371. p_inc = iio_buffer_step(txbuf);
  372. p_end = iio_buffer_end(txbuf);
  373. for (p_dat = (uint32_t *)iio_buffer_first(txbuf, tx0_i); p_dat < p_end; p_dat += p_inc) {
  374. // https://wiki.analog.com/resources/eval/user-guides/ad-fmcomms2-ebz/software/basic_iq_datafiles#binary_format
  375. //*((uint32_t *)p_dat) = (cos_table[n] << 16) | (0xFFFF & sin_table[n]);
  376. ((int16_t *)p_dat)[0] = cos_table[n];
  377. ((int16_t *)p_dat)[1] = sin_table[n];
  378.  
  379. n++;
  380. if (n >= txcfg.fs_hz) n = 0;
  381. }
  382.  
  383. // Schedule TX buffer
  384. nbytes_tx = iio_buffer_push(txbuf);
  385. if (nbytes_tx < 0) { printf("Error pushing buf %d\n", (int)nbytes_tx); shutdown(); }
  386.  
  387. // READ: Get pointers to RX buf and read IQ from RX buf port 0
  388. p_inc = iio_buffer_step(rxbuf);
  389. p_end = iio_buffer_end(rxbuf);
  390. p_start = (char *)iio_buffer_first(rxbuf, rx0_i);
  391. p_diff = p_end - p_start;
  392.  
  393. // Refill RX buffer
  394. nbytes_rx = iio_buffer_refill(rxbuf);
  395. if (nbytes_rx < 0) { printf("Error refilling buf %d\n", (int)nbytes_rx); shutdown(); }
  396.  
  397. // WRITE RX -> FILE: Write captured RX samples to a file
  398. size_t num_of_elements = p_diff / p_inc;
  399. if (fwrite(p_start, p_inc, num_of_elements, rx_capture) < num_of_elements) {
  400. printf("Error writing samples to a file\n");
  401. shutdown();
  402. }
  403.  
  404.  
  405. // Check for buffer overflow/underflow
  406. ret = iio_device_reg_read(tx, 0x80000088, &val);
  407. if (ret)
  408. {
  409. fprintf(stderr, "Failed to read status register: %s\n",
  410. strerror(-ret));
  411. continue;
  412. }
  413.  
  414. if (val & 0x1u)
  415. fprintf(stderr, "Underflow detected\n");
  416.  
  417. if (val & 0x4u)
  418. fprintf(stderr, "Overflow detected\n");
  419.  
  420. /* Clear bits */
  421. if (val)
  422. iio_device_reg_write(tx, 0x80000088, val);
  423.  
  424.  
  425. // Sample counter increment and status output
  426. nrx += nbytes_rx >> 2;
  427. ntx += nbytes_tx >> 2;
  428. if (nrx >= 1024*1024*8 || ntx >= 1024*1024*8) // 16M of data
  429. {
  430. fprintf(stderr, "\tRX %8.2f MSmp, TX %8.2f MSmp\n", nrx/1e6, ntx/1e6);
  431. shutdown();
  432. }
  433.  
  434. }
  435.  
  436. shutdown();
  437.  
  438. system("PAUSE");
  439. return 0;
  440. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement