Skip to content

Instantly share code, notes, and snippets.

@tfcollins
Last active August 15, 2018 22:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tfcollins/ca69361d594786ad08aa49f779068db4 to your computer and use it in GitHub Desktop.
Save tfcollins/ca69361d594786ad08aa49f779068db4 to your computer and use it in GitHub Desktop.
/*
* libiio - FMCOMMS5 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 { RXA, RXB, TXA, TXB };
/* 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 *rx1_i = NULL;
static struct iio_channel *rx1_q = NULL;
static struct iio_channel *rx2_i = NULL;
static struct iio_channel *rx2_q = NULL;
static struct iio_channel *rx3_i = NULL;
static struct iio_channel *rx3_q = NULL;
static struct iio_channel *tx0_i = NULL;
static struct iio_channel *tx0_q = NULL;
static struct iio_channel *tx1_i = NULL;
static struct iio_channel *tx1_q = NULL;
static struct iio_channel *tx2_i = NULL;
static struct iio_channel *tx2_q = NULL;
static struct iio_channel *tx3_i = NULL;
static struct iio_channel *tx3_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 (rx1_i) { iio_channel_disable(rx1_i); }
if (rx1_q) { iio_channel_disable(rx1_q); }
if (rx2_i) { iio_channel_disable(rx2_i); }
if (rx2_q) { iio_channel_disable(rx2_q); }
if (rx3_i) { iio_channel_disable(rx3_i); }
if (rx3_q) { iio_channel_disable(rx3_q); }
if (tx0_i) { iio_channel_disable(tx0_i); }
if (tx0_q) { iio_channel_disable(tx0_q); }
if (tx1_i) { iio_channel_disable(tx1_i); }
if (tx1_q) { iio_channel_disable(tx1_q); }
if (tx2_i) { iio_channel_disable(tx2_i); }
if (tx2_q) { iio_channel_disable(tx2_q); }
if (tx3_i) { iio_channel_disable(tx3_i); }
if (tx3_q) { iio_channel_disable(tx3_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 TXA: *dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); return *dev != NULL;
case TXB: *dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-B"); return *dev != NULL;
case RXA: *dev = iio_context_find_device(ctx, "cf-ad9361-A"); return *dev != NULL;
case RXB: *dev = iio_context_find_device(ctx, "cf-ad9361-B"); 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 == TXA)||(d == TXB) );
if (!*chn)
*chn = iio_device_find_channel(dev, get_ch_name("altvoltage", chid), (d == TXA)||(d == TXB));
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 RXA:
case RXB: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), false); return *chn != NULL;
case TXA:
case TXB: *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 RXA:
case RXB: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 0), true); return *chn != NULL;
case TXA:
case TXB: *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 *txA;
struct iio_device *txB;
struct iio_device *rxA;
struct iio_device *rxB;
// 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((ctx = iio_create_network_context("192.168.1.212")) && "No context");
ASSERT(iio_context_get_devices_count(ctx) > 0 && "No devices");
printf("* Acquiring AD9361 streaming devices\n");
ASSERT(get_ad9361_stream_dev(ctx, RXA, &rxA) && "No rx dev found");
ASSERT(get_ad9361_stream_dev(ctx, RXB, &rxB) && "No rx dev found");
ASSERT(get_ad9361_stream_dev(ctx, TXA, &txA) && "No tx dev found");
ASSERT(get_ad9361_stream_dev(ctx, TXB, &txB) && "No tx dev found");
printf("* Configuring AD9361 for streaming\n");
ASSERT(cfg_ad9361_streaming_ch(ctx, &rxcfg, RXA, 0) && "RX port 0 not found");
ASSERT(cfg_ad9361_streaming_ch(ctx, &rxcfg, RXB, 1) && "RX port 1 not found");
ASSERT(cfg_ad9361_streaming_ch(ctx, &txcfg, TXA, 0) && "TX port 0 not found");
ASSERT(cfg_ad9361_streaming_ch(ctx, &txcfg, TXB, 1) && "TX port 1 not found");
printf("* Initializing AD9361 IIO streaming channels\n");
ASSERT(get_ad9361_stream_ch(ctx, RXA, rxA, 0, &rx0_i) && "RX chan i not found");
ASSERT(get_ad9361_stream_ch(ctx, RXA, rxA, 1, &rx0_q) && "RX chan q not found");
ASSERT(get_ad9361_stream_ch(ctx, RXA, rxA, 2, &rx1_i) && "RX chan i not found");
ASSERT(get_ad9361_stream_ch(ctx, RXA, rxA, 3, &rx1_q) && "RX chan q not found");
ASSERT(get_ad9361_stream_ch(ctx, RXB, rxA, 4, &rx2_i) && "RX chan i not found");
ASSERT(get_ad9361_stream_ch(ctx, RXB, rxA, 5, &rx2_q) && "RX chan q not found");
ASSERT(get_ad9361_stream_ch(ctx, RXB, rxA, 6, &rx3_i) && "RX chan i not found");
ASSERT(get_ad9361_stream_ch(ctx, RXB, rxA, 7, &rx3_q) && "RX chan q not found");
ASSERT(get_ad9361_stream_ch(ctx, TXA, txA, 0, &tx0_i) && "TX chan i not found");
ASSERT(get_ad9361_stream_ch(ctx, TXA, txA, 1, &tx0_q) && "TX chan q not found");
ASSERT(get_ad9361_stream_ch(ctx, TXA, txA, 2, &tx1_i) && "TX chan i not found");
ASSERT(get_ad9361_stream_ch(ctx, TXA, txA, 3, &tx1_q) && "TX chan q not found");
ASSERT(get_ad9361_stream_ch(ctx, TXB, txA, 4, &tx2_i) && "TX chan i not found");
ASSERT(get_ad9361_stream_ch(ctx, TXB, txA, 5, &tx2_q) && "TX chan q not found");
ASSERT(get_ad9361_stream_ch(ctx, TXB, txA, 6, &tx3_i) && "TX chan i not found");
ASSERT(get_ad9361_stream_ch(ctx, TXB, txA, 7, &tx3_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(rx1_i);
iio_channel_enable(rx1_q);
iio_channel_enable(rx2_i);
iio_channel_enable(rx2_q);
iio_channel_enable(rx3_i);
iio_channel_enable(rx3_q);
iio_channel_enable(tx0_i);
iio_channel_enable(tx0_q);
iio_channel_enable(tx1_i);
iio_channel_enable(tx1_q);
iio_channel_enable(tx2_i);
iio_channel_enable(tx2_q);
iio_channel_enable(tx3_i);
iio_channel_enable(tx3_q);
printf("* Creating non-cyclic IIO buffers with 1 MiS\n");
rxbuf = iio_device_create_buffer(rxA, 1024*1024, false);
if (!rxbuf) {
perror("Could not create RX buffer");
shutdown();
}
txbuf = iio_device_create_buffer(txA, 1024*1024, false);
if (!txbuf) {
perror("Could not create TX buffer");
shutdown();
}
int chan = 0;
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(); }
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) {
for (chan = 0; chan<8; chan++)
printf("Data: %d (Chan %d)\n",((int16_t*)p_dat)[chan],chan);
}
// 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
for (chan = 0; chan<8; chan++)
((int16_t*)p_dat)[chan] = 0 << 4; // Real (I)
}
// Sample counter increment and status output
nrx += nbytes_rx / iio_device_get_sample_size(rxA);
ntx += nbytes_tx / iio_device_get_sample_size(txA);
printf("\tRX %8.2f MSmp, TX %8.2f MSmp\n", nrx/1e6, ntx/1e6);
}
shutdown();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment