summaryrefslogtreecommitdiff
path: root/src/examples/ad9361-iiostream.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/examples/ad9361-iiostream.c')
-rw-r--r--src/examples/ad9361-iiostream.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/examples/ad9361-iiostream.c b/src/examples/ad9361-iiostream.c
new file mode 100644
index 0000000..0756b7b
--- /dev/null
+++ b/src/examples/ad9361-iiostream.c
@@ -0,0 +1,294 @@
+/*
+ * libiio - AD9361 IIO streaming example
+ *
+ * Copyright (C) 2014 IABG mbH
+ * Author: Michael Feilen <feilen_at_iabg.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ **/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+
+#ifdef __APPLE__
+#include <iio/iio.h>
+#else
+#include <iio.h>
+#endif
+
+/* helper macros */
+#define MHZ(x) ((long long)(x*1000000.0 + .5))
+#define GHZ(x) ((long long)(x*1000000000.0 + .5))
+
+#define ASSERT(expr) { \
+ if (!(expr)) { \
+ (void) fprintf(stderr, "assertion failed (%s:%d)\n", __FILE__, __LINE__); \
+ (void) abort(); \
+ } \
+}
+
+/* RX is input, TX is output */
+enum iodev { RX, TX };
+
+/* common RX and TX streaming params */
+struct stream_cfg {
+ long long bw_hz; // Analog banwidth in Hz
+ long long fs_hz; // Baseband sample rate in Hz
+ long long lo_hz; // Local oscillator frequency in Hz
+ const char* rfport; // Port name
+};
+
+/* static scratch mem for strings */
+static char tmpstr[64];
+
+/* IIO structs required for streaming */
+static struct iio_context *ctx = NULL;
+static struct iio_channel *rx0_i = NULL;
+static struct iio_channel *rx0_q = NULL;
+static struct iio_channel *tx0_i = NULL;
+static struct iio_channel *tx0_q = NULL;
+static struct iio_buffer *rxbuf = NULL;
+static struct iio_buffer *txbuf = NULL;
+
+static bool stop;
+
+/* cleanup and exit */
+static void shutdown()
+{
+ printf("* Destroying buffers\n");
+ if (rxbuf) { iio_buffer_destroy(rxbuf); }
+ if (txbuf) { iio_buffer_destroy(txbuf); }
+
+ printf("* Disabling streaming channels\n");
+ if (rx0_i) { iio_channel_disable(rx0_i); }
+ if (rx0_q) { iio_channel_disable(rx0_q); }
+ if (tx0_i) { iio_channel_disable(tx0_i); }
+ if (tx0_q) { iio_channel_disable(tx0_q); }
+
+ printf("* Destroying context\n");
+ if (ctx) { iio_context_destroy(ctx); }
+ exit(0);
+}
+
+static void handle_sig(int sig)
+{
+ printf("Waiting for process to finish...\n");
+ stop = true;
+}
+
+/* check return value of attr_write function */
+static void errchk(int v, const char* what) {
+ if (v < 0) { fprintf(stderr, "Error %d writing to channel \"%s\"\nvalue may not be supported.\n", v, what); shutdown(); }
+}
+
+/* write attribute: long long int */
+static void wr_ch_lli(struct iio_channel *chn, const char* what, long long val)
+{
+ errchk(iio_channel_attr_write_longlong(chn, what, val), what);
+}
+
+/* write attribute: string */
+static void wr_ch_str(struct iio_channel *chn, const char* what, const char* str)
+{
+ errchk(iio_channel_attr_write(chn, what, str), what);
+}
+
+/* helper function generating channel names */
+static char* get_ch_name(const char* type, int id)
+{
+ snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id);
+ return tmpstr;
+}
+
+/* returns ad9361 phy device */
+static struct iio_device* get_ad9361_phy(struct iio_context *ctx)
+{
+ struct iio_device *dev = iio_context_find_device(ctx, "ad9361-phy");
+ ASSERT(dev && "No ad9361-phy found");
+ return dev;
+}
+
+/* finds AD9361 streaming IIO devices */
+static bool get_ad9361_stream_dev(struct iio_context *ctx, enum iodev d, struct iio_device **dev)
+{
+ switch (d) {
+ case TX: *dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); return *dev != NULL;
+ case RX: *dev = iio_context_find_device(ctx, "cf-ad9361-lpc"); return *dev != NULL;
+ default: ASSERT(0); return false;
+ }
+}
+
+/* finds AD9361 streaming IIO channels */
+static bool get_ad9361_stream_ch(struct iio_context *ctx, enum iodev d, struct iio_device *dev, int chid, struct iio_channel **chn)
+{
+ *chn = iio_device_find_channel(dev, get_ch_name("voltage", chid), d == TX);
+ if (!*chn)
+ *chn = iio_device_find_channel(dev, get_ch_name("altvoltage", chid), d == TX);
+ return *chn != NULL;
+}
+
+/* finds AD9361 phy IIO configuration channel with id chid */
+static bool get_phy_chan(struct iio_context *ctx, enum iodev d, int chid, struct iio_channel **chn)
+{
+ switch (d) {
+ case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), false); return *chn != NULL;
+ case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), true); return *chn != NULL;
+ default: ASSERT(0); return false;
+ }
+}
+
+/* finds AD9361 local oscillator IIO configuration channels */
+static bool get_lo_chan(struct iio_context *ctx, enum iodev d, struct iio_channel **chn)
+{
+ switch (d) {
+ // LO chan is always output, i.e. true
+ case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 0), true); return *chn != NULL;
+ case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 1), true); return *chn != NULL;
+ default: ASSERT(0); return false;
+ }
+}
+
+/* applies streaming configuration through IIO */
+bool cfg_ad9361_streaming_ch(struct iio_context *ctx, struct stream_cfg *cfg, enum iodev type, int chid)
+{
+ struct iio_channel *chn = NULL;
+
+ // Configure phy and lo channels
+ printf("* Acquiring AD9361 phy channel %d\n", chid);
+ if (!get_phy_chan(ctx, type, chid, &chn)) { return false; }
+ wr_ch_str(chn, "rf_port_select", cfg->rfport);
+ wr_ch_lli(chn, "rf_bandwidth", cfg->bw_hz);
+ wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz);
+
+ // Configure LO channel
+ printf("* Acquiring AD9361 %s lo channel\n", type == TX ? "TX" : "RX");
+ if (!get_lo_chan(ctx, type, &chn)) { return false; }
+ wr_ch_lli(chn, "frequency", cfg->lo_hz);
+ return true;
+}
+
+/* simple configuration and streaming */
+int main (int argc, char **argv)
+{
+ // Streaming devices
+ struct iio_device *tx;
+ struct iio_device *rx;
+
+ // RX and TX sample counters
+ size_t nrx = 0;
+ size_t ntx = 0;
+
+ // Stream configurations
+ struct stream_cfg rxcfg;
+ struct stream_cfg txcfg;
+
+ // Listen to ctrl+c and ASSERT
+ signal(SIGINT, handle_sig);
+
+ // RX stream config
+ rxcfg.bw_hz = MHZ(2); // 2 MHz rf bandwidth
+ rxcfg.fs_hz = MHZ(2.5); // 2.5 MS/s rx sample rate
+ rxcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
+ rxcfg.rfport = "A_BALANCED"; // port A (select for rf freq.)
+
+ // TX stream config
+ txcfg.bw_hz = MHZ(1.5); // 1.5 MHz rf bandwidth
+ txcfg.fs_hz = MHZ(2.5); // 2.5 MS/s tx sample rate
+ txcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
+ txcfg.rfport = "A"; // port A (select for rf freq.)
+
+ printf("* Acquiring IIO context\n");
+ ASSERT((ctx = iio_create_default_context()) && "No context");
+ ASSERT(iio_context_get_devices_count(ctx) > 0 && "No devices");
+
+ printf("* Acquiring AD9361 streaming devices\n");
+ ASSERT(get_ad9361_stream_dev(ctx, TX, &tx) && "No tx dev found");
+ ASSERT(get_ad9361_stream_dev(ctx, RX, &rx) && "No rx dev found");
+
+ printf("* Configuring AD9361 for streaming\n");
+ ASSERT(cfg_ad9361_streaming_ch(ctx, &rxcfg, RX, 0) && "RX port 0 not found");
+ ASSERT(cfg_ad9361_streaming_ch(ctx, &txcfg, TX, 0) && "TX port 0 not found");
+
+ printf("* Initializing AD9361 IIO streaming channels\n");
+ ASSERT(get_ad9361_stream_ch(ctx, RX, rx, 0, &rx0_i) && "RX chan i not found");
+ ASSERT(get_ad9361_stream_ch(ctx, RX, rx, 1, &rx0_q) && "RX chan q not found");
+ ASSERT(get_ad9361_stream_ch(ctx, TX, tx, 0, &tx0_i) && "TX chan i not found");
+ ASSERT(get_ad9361_stream_ch(ctx, TX, tx, 1, &tx0_q) && "TX chan q not found");
+
+ printf("* Enabling IIO streaming channels\n");
+ iio_channel_enable(rx0_i);
+ iio_channel_enable(rx0_q);
+ iio_channel_enable(tx0_i);
+ iio_channel_enable(tx0_q);
+
+ printf("* Creating non-cyclic IIO buffers with 1 MiS\n");
+ rxbuf = iio_device_create_buffer(rx, 1024*1024, false);
+ if (!rxbuf) {
+ perror("Could not create RX buffer");
+ shutdown();
+ }
+ txbuf = iio_device_create_buffer(tx, 1024*1024, false);
+ if (!txbuf) {
+ perror("Could not create TX buffer");
+ shutdown();
+ }
+
+ printf("* Starting IO streaming (press CTRL+C to cancel)\n");
+ while (!stop)
+ {
+ ssize_t nbytes_rx, nbytes_tx;
+ char *p_dat, *p_end;
+ ptrdiff_t p_inc;
+
+ // Schedule TX buffer
+ nbytes_tx = iio_buffer_push(txbuf);
+ if (nbytes_tx < 0) { printf("Error pushing buf %d\n", (int) nbytes_tx); shutdown(); }
+
+ // Refill RX buffer
+ nbytes_rx = iio_buffer_refill(rxbuf);
+ if (nbytes_rx < 0) { printf("Error refilling buf %d\n",(int) nbytes_rx); shutdown(); }
+
+ // READ: Get pointers to RX buf and read IQ from RX buf port 0
+ p_inc = iio_buffer_step(rxbuf);
+ p_end = iio_buffer_end(rxbuf);
+ for (p_dat = (char *)iio_buffer_first(rxbuf, rx0_i); p_dat < p_end; p_dat += p_inc) {
+ // Example: swap I and Q
+ const int16_t i = ((int16_t*)p_dat)[0]; // Real (I)
+ const int16_t q = ((int16_t*)p_dat)[1]; // Imag (Q)
+ ((int16_t*)p_dat)[0] = q;
+ ((int16_t*)p_dat)[1] = i;
+ }
+
+ // WRITE: Get pointers to TX buf and write IQ to TX buf port 0
+ p_inc = iio_buffer_step(txbuf);
+ p_end = iio_buffer_end(txbuf);
+ for (p_dat = (char *)iio_buffer_first(txbuf, tx0_i); p_dat < p_end; p_dat += p_inc) {
+ // Example: fill with zeros
+ // 12-bit sample needs to be MSB alligned so shift by 4
+ // https://wiki.analog.com/resources/eval/user-guides/ad-fmcomms2-ebz/software/basic_iq_datafiles#binary_format
+ ((int16_t*)p_dat)[0] = 0 << 4; // Real (I)
+ ((int16_t*)p_dat)[1] = 0 << 4; // Imag (Q)
+ }
+
+ // Sample counter increment and status output
+ nrx += nbytes_rx / iio_device_get_sample_size(rx);
+ ntx += nbytes_tx / iio_device_get_sample_size(tx);
+ printf("\tRX %8.2f MSmp, TX %8.2f MSmp\n", nrx/1e6, ntx/1e6);
+ }
+
+ shutdown();
+
+ return 0;
+}