• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libiio - AD9361 IIO streaming example
3  *
4  * Copyright (C) 2014 IABG mbH
5  * Author: Michael Feilen <feilen_at_iabg.de>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  **/
18 
19 #include <stdbool.h>
20 #include <stdint.h>
21 #include <string.h>
22 #include <signal.h>
23 #include <stdio.h>
24 
25 #ifdef __APPLE__
26 #include <iio/iio.h>
27 #else
28 #include <iio.h>
29 #endif
30 
31 /* helper macros */
32 #define MHZ(x) ((long long)(x*1000000.0 + .5))
33 #define GHZ(x) ((long long)(x*1000000000.0 + .5))
34 
35 #define ASSERT(expr) { \
36 	if (!(expr)) { \
37 		(void) fprintf(stderr, "assertion failed (%s:%d)\n", __FILE__, __LINE__); \
38 		(void) abort(); \
39 	} \
40 }
41 
42 /* RX is input, TX is output */
43 enum iodev { RX, TX };
44 
45 /* common RX and TX streaming params */
46 struct stream_cfg {
47 	long long bw_hz; // Analog banwidth in Hz
48 	long long fs_hz; // Baseband sample rate in Hz
49 	long long lo_hz; // Local oscillator frequency in Hz
50 	const char* rfport; // Port name
51 };
52 
53 /* static scratch mem for strings */
54 static char tmpstr[64];
55 
56 /* IIO structs required for streaming */
57 static struct iio_context *ctx   = NULL;
58 static struct iio_channel *rx0_i = NULL;
59 static struct iio_channel *rx0_q = NULL;
60 static struct iio_channel *tx0_i = NULL;
61 static struct iio_channel *tx0_q = NULL;
62 static struct iio_buffer  *rxbuf = NULL;
63 static struct iio_buffer  *txbuf = NULL;
64 
65 static bool stop;
66 
67 /* cleanup and exit */
shutdown()68 static void shutdown()
69 {
70 	printf("* Destroying buffers\n");
71 	if (rxbuf) { iio_buffer_destroy(rxbuf); }
72 	if (txbuf) { iio_buffer_destroy(txbuf); }
73 
74 	printf("* Disabling streaming channels\n");
75 	if (rx0_i) { iio_channel_disable(rx0_i); }
76 	if (rx0_q) { iio_channel_disable(rx0_q); }
77 	if (tx0_i) { iio_channel_disable(tx0_i); }
78 	if (tx0_q) { iio_channel_disable(tx0_q); }
79 
80 	printf("* Destroying context\n");
81 	if (ctx) { iio_context_destroy(ctx); }
82 	exit(0);
83 }
84 
handle_sig(int sig)85 static void handle_sig(int sig)
86 {
87 	printf("Waiting for process to finish...\n");
88 	stop = true;
89 }
90 
91 /* check return value of attr_write function */
errchk(int v,const char * what)92 static void errchk(int v, const char* what) {
93 	 if (v < 0) { fprintf(stderr, "Error %d writing to channel \"%s\"\nvalue may not be supported.\n", v, what); shutdown(); }
94 }
95 
96 /* write attribute: long long int */
wr_ch_lli(struct iio_channel * chn,const char * what,long long val)97 static void wr_ch_lli(struct iio_channel *chn, const char* what, long long val)
98 {
99 	errchk(iio_channel_attr_write_longlong(chn, what, val), what);
100 }
101 
102 /* write attribute: string */
wr_ch_str(struct iio_channel * chn,const char * what,const char * str)103 static void wr_ch_str(struct iio_channel *chn, const char* what, const char* str)
104 {
105 	errchk(iio_channel_attr_write(chn, what, str), what);
106 }
107 
108 /* helper function generating channel names */
get_ch_name(const char * type,int id)109 static char* get_ch_name(const char* type, int id)
110 {
111 	snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id);
112 	return tmpstr;
113 }
114 
115 /* returns ad9361 phy device */
get_ad9361_phy(struct iio_context * ctx)116 static struct iio_device* get_ad9361_phy(struct iio_context *ctx)
117 {
118 	struct iio_device *dev =  iio_context_find_device(ctx, "ad9361-phy");
119 	ASSERT(dev && "No ad9361-phy found");
120 	return dev;
121 }
122 
123 /* finds AD9361 streaming IIO devices */
get_ad9361_stream_dev(struct iio_context * ctx,enum iodev d,struct iio_device ** dev)124 static bool get_ad9361_stream_dev(struct iio_context *ctx, enum iodev d, struct iio_device **dev)
125 {
126 	switch (d) {
127 	case TX: *dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); return *dev != NULL;
128 	case RX: *dev = iio_context_find_device(ctx, "cf-ad9361-lpc");  return *dev != NULL;
129 	default: ASSERT(0); return false;
130 	}
131 }
132 
133 /* finds AD9361 streaming IIO channels */
get_ad9361_stream_ch(struct iio_context * ctx,enum iodev d,struct iio_device * dev,int chid,struct iio_channel ** chn)134 static bool get_ad9361_stream_ch(struct iio_context *ctx, enum iodev d, struct iio_device *dev, int chid, struct iio_channel **chn)
135 {
136 	*chn = iio_device_find_channel(dev, get_ch_name("voltage", chid), d == TX);
137 	if (!*chn)
138 		*chn = iio_device_find_channel(dev, get_ch_name("altvoltage", chid), d == TX);
139 	return *chn != NULL;
140 }
141 
142 /* finds AD9361 phy IIO configuration channel with id chid */
get_phy_chan(struct iio_context * ctx,enum iodev d,int chid,struct iio_channel ** chn)143 static bool get_phy_chan(struct iio_context *ctx, enum iodev d, int chid, struct iio_channel **chn)
144 {
145 	switch (d) {
146 	case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), false); return *chn != NULL;
147 	case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), true);  return *chn != NULL;
148 	default: ASSERT(0); return false;
149 	}
150 }
151 
152 /* finds AD9361 local oscillator IIO configuration channels */
get_lo_chan(struct iio_context * ctx,enum iodev d,struct iio_channel ** chn)153 static bool get_lo_chan(struct iio_context *ctx, enum iodev d, struct iio_channel **chn)
154 {
155 	switch (d) {
156 	 // LO chan is always output, i.e. true
157 	case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 0), true); return *chn != NULL;
158 	case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 1), true); return *chn != NULL;
159 	default: ASSERT(0); return false;
160 	}
161 }
162 
163 /* applies streaming configuration through IIO */
cfg_ad9361_streaming_ch(struct iio_context * ctx,struct stream_cfg * cfg,enum iodev type,int chid)164 bool cfg_ad9361_streaming_ch(struct iio_context *ctx, struct stream_cfg *cfg, enum iodev type, int chid)
165 {
166 	struct iio_channel *chn = NULL;
167 
168 	// Configure phy and lo channels
169 	printf("* Acquiring AD9361 phy channel %d\n", chid);
170 	if (!get_phy_chan(ctx, type, chid, &chn)) {	return false; }
171 	wr_ch_str(chn, "rf_port_select",     cfg->rfport);
172 	wr_ch_lli(chn, "rf_bandwidth",       cfg->bw_hz);
173 	wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz);
174 
175 	// Configure LO channel
176 	printf("* Acquiring AD9361 %s lo channel\n", type == TX ? "TX" : "RX");
177 	if (!get_lo_chan(ctx, type, &chn)) { return false; }
178 	wr_ch_lli(chn, "frequency", cfg->lo_hz);
179 	return true;
180 }
181 
182 /* simple configuration and streaming */
main(int argc,char ** argv)183 int main (int argc, char **argv)
184 {
185 	// Streaming devices
186 	struct iio_device *tx;
187 	struct iio_device *rx;
188 
189 	// RX and TX sample counters
190 	size_t nrx = 0;
191 	size_t ntx = 0;
192 
193 	// Stream configurations
194 	struct stream_cfg rxcfg;
195 	struct stream_cfg txcfg;
196 
197 	// Listen to ctrl+c and ASSERT
198 	signal(SIGINT, handle_sig);
199 
200 	// RX stream config
201 	rxcfg.bw_hz = MHZ(2);   // 2 MHz rf bandwidth
202 	rxcfg.fs_hz = MHZ(2.5);   // 2.5 MS/s rx sample rate
203 	rxcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
204 	rxcfg.rfport = "A_BALANCED"; // port A (select for rf freq.)
205 
206 	// TX stream config
207 	txcfg.bw_hz = MHZ(1.5); // 1.5 MHz rf bandwidth
208 	txcfg.fs_hz = MHZ(2.5);   // 2.5 MS/s tx sample rate
209 	txcfg.lo_hz = GHZ(2.5); // 2.5 GHz rf frequency
210 	txcfg.rfport = "A"; // port A (select for rf freq.)
211 
212 	printf("* Acquiring IIO context\n");
213 	ASSERT((ctx = iio_create_default_context()) && "No context");
214 	ASSERT(iio_context_get_devices_count(ctx) > 0 && "No devices");
215 
216 	printf("* Acquiring AD9361 streaming devices\n");
217 	ASSERT(get_ad9361_stream_dev(ctx, TX, &tx) && "No tx dev found");
218 	ASSERT(get_ad9361_stream_dev(ctx, RX, &rx) && "No rx dev found");
219 
220 	printf("* Configuring AD9361 for streaming\n");
221 	ASSERT(cfg_ad9361_streaming_ch(ctx, &rxcfg, RX, 0) && "RX port 0 not found");
222 	ASSERT(cfg_ad9361_streaming_ch(ctx, &txcfg, TX, 0) && "TX port 0 not found");
223 
224 	printf("* Initializing AD9361 IIO streaming channels\n");
225 	ASSERT(get_ad9361_stream_ch(ctx, RX, rx, 0, &rx0_i) && "RX chan i not found");
226 	ASSERT(get_ad9361_stream_ch(ctx, RX, rx, 1, &rx0_q) && "RX chan q not found");
227 	ASSERT(get_ad9361_stream_ch(ctx, TX, tx, 0, &tx0_i) && "TX chan i not found");
228 	ASSERT(get_ad9361_stream_ch(ctx, TX, tx, 1, &tx0_q) && "TX chan q not found");
229 
230 	printf("* Enabling IIO streaming channels\n");
231 	iio_channel_enable(rx0_i);
232 	iio_channel_enable(rx0_q);
233 	iio_channel_enable(tx0_i);
234 	iio_channel_enable(tx0_q);
235 
236 	printf("* Creating non-cyclic IIO buffers with 1 MiS\n");
237 	rxbuf = iio_device_create_buffer(rx, 1024*1024, false);
238 	if (!rxbuf) {
239 		perror("Could not create RX buffer");
240 		shutdown();
241 	}
242 	txbuf = iio_device_create_buffer(tx, 1024*1024, false);
243 	if (!txbuf) {
244 		perror("Could not create TX buffer");
245 		shutdown();
246 	}
247 
248 	printf("* Starting IO streaming (press CTRL+C to cancel)\n");
249 	while (!stop)
250 	{
251 		ssize_t nbytes_rx, nbytes_tx;
252 		char *p_dat, *p_end;
253 		ptrdiff_t p_inc;
254 
255 		// Schedule TX buffer
256 		nbytes_tx = iio_buffer_push(txbuf);
257 		if (nbytes_tx < 0) { printf("Error pushing buf %d\n", (int) nbytes_tx); shutdown(); }
258 
259 		// Refill RX buffer
260 		nbytes_rx = iio_buffer_refill(rxbuf);
261 		if (nbytes_rx < 0) { printf("Error refilling buf %d\n",(int) nbytes_rx); shutdown(); }
262 
263 		// READ: Get pointers to RX buf and read IQ from RX buf port 0
264 		p_inc = iio_buffer_step(rxbuf);
265 		p_end = iio_buffer_end(rxbuf);
266 		for (p_dat = (char *)iio_buffer_first(rxbuf, rx0_i); p_dat < p_end; p_dat += p_inc) {
267 			// Example: swap I and Q
268 			const int16_t i = ((int16_t*)p_dat)[0]; // Real (I)
269 			const int16_t q = ((int16_t*)p_dat)[1]; // Imag (Q)
270 			((int16_t*)p_dat)[0] = q;
271 			((int16_t*)p_dat)[1] = i;
272 		}
273 
274 		// WRITE: Get pointers to TX buf and write IQ to TX buf port 0
275 		p_inc = iio_buffer_step(txbuf);
276 		p_end = iio_buffer_end(txbuf);
277 		for (p_dat = (char *)iio_buffer_first(txbuf, tx0_i); p_dat < p_end; p_dat += p_inc) {
278 			// Example: fill with zeros
279 			// 12-bit sample needs to be MSB alligned so shift by 4
280 			// https://wiki.analog.com/resources/eval/user-guides/ad-fmcomms2-ebz/software/basic_iq_datafiles#binary_format
281 			((int16_t*)p_dat)[0] = 0 << 4; // Real (I)
282 			((int16_t*)p_dat)[1] = 0 << 4; // Imag (Q)
283 		}
284 
285 		// Sample counter increment and status output
286 		nrx += nbytes_rx / iio_device_get_sample_size(rx);
287 		ntx += nbytes_tx / iio_device_get_sample_size(tx);
288 		printf("\tRX %8.2f MSmp, TX %8.2f MSmp\n", nrx/1e6, ntx/1e6);
289 	}
290 
291 	shutdown();
292 
293 	return 0;
294 }
295