sw: Import the embedded control software
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>master
parent
18adc3602a
commit
8c5277d075
|
@ -0,0 +1,23 @@
|
|||
CROSS ?= arm-linux-gnueabihf-
|
||||
|
||||
CC=$(CROSS)gcc
|
||||
LD=$(CROSS)gcc
|
||||
|
||||
CFLAGS=-Wall --sysroot=$(SYSROOT)
|
||||
LDLIBS=-liio --sysroot=$(SYSROOT)
|
||||
|
||||
|
||||
all: sysroot_test osmo-rfds
|
||||
|
||||
osmo-rfds: osmo-rfds.o
|
||||
|
||||
sysroot_test:
|
||||
@if [ "x$(SYSROOT)" = "x" ]; then \
|
||||
echo >&2 "[!] Need SYSROOT to be pointing to the buildroot SDK"; \
|
||||
false; \
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -f osmo-rfds *.o
|
||||
|
||||
.PHONY: sysroot_test clean
|
|
@ -0,0 +1,432 @@
|
|||
/*
|
||||
* osmo-rfds-ctrl.c
|
||||
*
|
||||
* Control software for the Pluto RF delay simulator
|
||||
*
|
||||
* Copyright (C) 2018 sysmocom GmbH
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <iio.h>
|
||||
|
||||
/*
|
||||
Some info about Pluto use of IIO :
|
||||
|
||||
Device: ad9361-phy => Chip control
|
||||
------------------
|
||||
|
||||
in_voltage0_[…]: targets RX1
|
||||
in_voltage1_[…]: targets RX2 (AD9361 in 2RX2TX mode only)
|
||||
|
||||
out_voltage0_[…]: targets TX1
|
||||
out_voltage1_[…]: targets TX2 (AD9361 in 2RX2TX mode only)
|
||||
|
||||
out_altvoltage0_[…]: targets RX LO
|
||||
out_altvoltage1_[…]: targets TX LO
|
||||
|
||||
|
||||
Device: cf-ad9361-lpc => RX Path
|
||||
---------------------
|
||||
|
||||
voltage 0 -> RX1 I
|
||||
voltage 1 -> RX1 Q
|
||||
|
||||
|
||||
Device: cf-ad9361-dds-core-lpc => TX Path + two tone synthesizer
|
||||
------------------------------
|
||||
|
||||
voltage 0 -> TX1 I
|
||||
voltage 1 -> TX1 Q
|
||||
altvoltage[] -> DDS stuff
|
||||
*/
|
||||
|
||||
|
||||
struct app_options
|
||||
{
|
||||
long long tx_freq; /* Hz */
|
||||
long long rx_freq; /* Hz */
|
||||
float tx_gain;
|
||||
float rx_gain;
|
||||
|
||||
long long samp_rate; /* Hz */
|
||||
|
||||
int buf_cnt;
|
||||
int buf_size;
|
||||
|
||||
int echo_delay;
|
||||
float echo_scale;
|
||||
};
|
||||
|
||||
struct app_state
|
||||
{
|
||||
/* Options */
|
||||
struct app_options opts;
|
||||
|
||||
/* Pluto device */
|
||||
struct {
|
||||
struct iio_context *ctx;
|
||||
struct iio_device *phy;
|
||||
|
||||
struct iio_device *rx;
|
||||
struct iio_channel *rx_i;
|
||||
struct iio_channel *rx_q;
|
||||
struct iio_buffer *rx_buf;
|
||||
|
||||
struct iio_device *tx;
|
||||
struct iio_channel *tx_i;
|
||||
struct iio_channel *tx_q;
|
||||
struct iio_buffer *tx_buf;
|
||||
} pluto;
|
||||
};
|
||||
|
||||
|
||||
static void app_pluto_close(struct app_state *app);
|
||||
|
||||
static int
|
||||
app_pluto_open(struct app_state *app)
|
||||
{
|
||||
/* NULL init */
|
||||
memset(&app->pluto, 0x00, sizeof(app->pluto));
|
||||
|
||||
/* Context */
|
||||
app->pluto.ctx = iio_create_context_from_uri("local:");
|
||||
if (!app->pluto.ctx) {
|
||||
fprintf(stderr, "[!] Failed to create IIO context\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Devices */
|
||||
app->pluto.phy = iio_context_find_device(app->pluto.ctx, "ad9361-phy");
|
||||
if (!app->pluto.phy) {
|
||||
fprintf(stderr, "[!] Failed to find AD9361 PHY device\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
app->pluto.rx = iio_context_find_device(app->pluto.ctx, "cf-ad9361-lpc");
|
||||
if (!app->pluto.rx) {
|
||||
fprintf(stderr, "[!] Failed to find AD9361 RX device\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
app->pluto.tx = iio_context_find_device(app->pluto.ctx, "cf-ad9361-dds-core-lpc");
|
||||
if (!app->pluto.tx) {
|
||||
fprintf(stderr, "[!] Failed to find AD9361 TX device\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Configure RX */
|
||||
/* Frequency / Gain / Sampling */
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(app->pluto.phy, "altvoltage0", true),
|
||||
"frequency", /* RX LO frequency */
|
||||
app->opts.rx_freq
|
||||
);
|
||||
|
||||
iio_channel_attr_write_double(
|
||||
iio_device_find_channel(app->pluto.phy, "voltage0", false),
|
||||
"hardwaregain", /* RX gain */
|
||||
(double)app->opts.rx_gain
|
||||
);
|
||||
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(app->pluto.phy, "voltage0", false),
|
||||
"sampling_frequency", /* RX baseband rate */
|
||||
app->opts.samp_rate
|
||||
);
|
||||
|
||||
/* Channel config */
|
||||
app->pluto.rx_i = iio_device_find_channel(app->pluto.rx, "voltage0", false);
|
||||
app->pluto.rx_q = iio_device_find_channel(app->pluto.rx, "voltage1", false);
|
||||
|
||||
iio_channel_enable(app->pluto.rx_i);
|
||||
iio_channel_enable(app->pluto.rx_q);
|
||||
|
||||
/* Configure TX */
|
||||
/* Frequency / Gain / Sampling */
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(app->pluto.phy, "altvoltage1", true),
|
||||
"frequency", /* TX LO frequency */
|
||||
app->opts.tx_freq
|
||||
);
|
||||
|
||||
iio_channel_attr_write_double(
|
||||
iio_device_find_channel(app->pluto.phy, "voltage0", true),
|
||||
"hardwaregain", /* TX gain */
|
||||
(double)app->opts.tx_gain
|
||||
);
|
||||
|
||||
iio_channel_attr_write_longlong(
|
||||
iio_device_find_channel(app->pluto.phy, "voltage0", true),
|
||||
"sampling_frequency", /* TX baseband rate */
|
||||
app->opts.samp_rate
|
||||
);
|
||||
|
||||
/* Channel config */
|
||||
app->pluto.tx_i = iio_device_find_channel(app->pluto.tx, "voltage0", true);
|
||||
app->pluto.tx_q = iio_device_find_channel(app->pluto.tx, "voltage1", true);
|
||||
|
||||
iio_channel_enable(app->pluto.tx_i);
|
||||
iio_channel_enable(app->pluto.tx_q);
|
||||
|
||||
/* Configure the ECHO path */
|
||||
uint16_t scale = (uint16_t)(0x4000 * app->opts.echo_scale);
|
||||
uint16_t delay = app->opts.echo_delay;
|
||||
uint32_t config = (((uint32_t)delay) << 16) | (uint32_t)scale;
|
||||
|
||||
iio_device_reg_write(app->pluto.tx, 0x8000043c, config); /* I combiner config */
|
||||
iio_device_reg_write(app->pluto.tx, 0x8000047c, config); /* Q combiner config */
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
app_pluto_close(app);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
app_pluto_close(struct app_state *app)
|
||||
{
|
||||
if (app->pluto.rx_i)
|
||||
iio_channel_disable(app->pluto.rx_i);
|
||||
|
||||
if (app->pluto.rx_q)
|
||||
iio_channel_disable(app->pluto.rx_q);
|
||||
|
||||
if (app->pluto.tx_i)
|
||||
iio_channel_disable(app->pluto.tx_i);
|
||||
|
||||
if (app->pluto.tx_q)
|
||||
iio_channel_disable(app->pluto.tx_q);
|
||||
|
||||
if (app->pluto.ctx)
|
||||
iio_context_destroy(app->pluto.ctx);
|
||||
|
||||
memset(&app->pluto, 0x00, sizeof(app->pluto));
|
||||
}
|
||||
|
||||
static int
|
||||
app_pluto_start(struct app_state *app)
|
||||
{
|
||||
/* Create buffers and start the streaming */
|
||||
iio_device_set_kernel_buffers_count(app->pluto.rx, app->opts.buf_cnt);
|
||||
app->pluto.rx_buf = iio_device_create_buffer(app->pluto.rx, app->opts.buf_size, false);
|
||||
if (!app->pluto.rx_buf) {
|
||||
fprintf(stderr, "[!] Could not create RX buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
iio_device_set_kernel_buffers_count(app->pluto.tx, app->opts.buf_cnt);
|
||||
app->pluto.tx_buf = iio_device_create_buffer(app->pluto.tx, app->opts.buf_size, false);
|
||||
if (!app->pluto.tx_buf) {
|
||||
fprintf(stderr, "[!] Could not create TX buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Echo start */
|
||||
iio_device_reg_write(app->pluto.tx, 0x80000418, 0xa); /* I mux to RF loopback */
|
||||
iio_device_reg_write(app->pluto.tx, 0x80000458, 0xa); /* Q mux to RF loopback */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
app_pluto_stop(struct app_state *app)
|
||||
{
|
||||
if (app->pluto.rx_buf)
|
||||
iio_buffer_destroy(app->pluto.rx_buf);
|
||||
|
||||
if (app->pluto.tx_buf)
|
||||
iio_buffer_destroy(app->pluto.tx_buf);
|
||||
|
||||
/* Force zero */
|
||||
iio_device_reg_write(app->pluto.tx, 0x80000418, 0); /* I mux to zero */
|
||||
iio_device_reg_write(app->pluto.tx, 0x80000458, 0); /* Q mux to zero */
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* Options */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void
|
||||
opts_defaults(struct app_options *opts)
|
||||
{
|
||||
memset(opts, 0x00, sizeof(struct app_options));
|
||||
|
||||
opts->tx_freq = 1000000000LL;
|
||||
opts->rx_freq = 1000000000LL;
|
||||
opts->tx_gain = -40.0f;
|
||||
opts->rx_gain = 40.0f ;
|
||||
|
||||
opts->samp_rate = 4000000LL;
|
||||
|
||||
opts->buf_cnt = 4;
|
||||
opts->buf_size = 4096;
|
||||
|
||||
opts->echo_scale = 0.25f;
|
||||
opts->echo_delay = 50;
|
||||
}
|
||||
|
||||
static void
|
||||
opts_help(const char *argv0)
|
||||
{
|
||||
fprintf(stderr, "%s [options]\n", argv0);
|
||||
|
||||
fprintf(stderr, " -t, --tx-freq \n");
|
||||
fprintf(stderr, " -r, --rx-freq \n");
|
||||
fprintf(stderr, " -T, --tx-gain \n");
|
||||
fprintf(stderr, " -R, --rx-gain \n");
|
||||
fprintf(stderr, " -s, --samplerate \n");
|
||||
fprintf(stderr, " -c, --buffer-count \n");
|
||||
fprintf(stderr, " -b, --buffer-size \n");
|
||||
fprintf(stderr, " -a, --amplitude \n");
|
||||
fprintf(stderr, " -d, --delay \n");
|
||||
fprintf(stderr, " -h, --help \n");
|
||||
}
|
||||
|
||||
static int
|
||||
opts_parse(struct app_options *opts, int argc, char *argv[])
|
||||
{
|
||||
const struct option long_options[] =
|
||||
{
|
||||
{ "tx-freq", required_argument, 0, 't' },
|
||||
{ "rx-freq", required_argument, 0, 'r' },
|
||||
{ "tx-gain", required_argument, 0, 'T' },
|
||||
{ "rx-gain", required_argument, 0, 'R' },
|
||||
{ "samplerate", required_argument, 0, 's' },
|
||||
{ "buffer-count", required_argument, 0, 'c' },
|
||||
{ "buffer-size", required_argument, 0, 'b' },
|
||||
{ "amplitude", required_argument, 0, 'a' },
|
||||
{ "delay", required_argument, 0, 'd' },
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
const char *short_options = "t:r:T:R:s:c:b:a:d:h";
|
||||
|
||||
while (1) {
|
||||
int optidx;
|
||||
int c = getopt_long (argc, argv, short_options, long_options, &optidx);
|
||||
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 't':
|
||||
opts->tx_freq = strtoll(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
opts->rx_freq = strtoll(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
opts->tx_gain = strtof(optarg, NULL);
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
opts->rx_gain = strtof(optarg, NULL);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
opts->samp_rate = strtoll(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
opts->buf_cnt = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
opts->buf_size = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
opts->echo_scale = strtof(optarg, NULL);
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
opts->echo_delay = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
opts_help(argv[0]);
|
||||
return 1;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown option\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
opts_print(struct app_options *opts, FILE *fd)
|
||||
{
|
||||
fprintf(fd, "[+] Options :\n");
|
||||
|
||||
fprintf(fd, " . TX frequency : %.3lf MHz\n", (float)opts->tx_freq / 1e6);
|
||||
fprintf(fd, " . TX gain : %.1f dB\n", opts->tx_gain);
|
||||
fprintf(fd, " . RX frequency : %.3lf MHz\n", (float)opts->rx_freq / 1e6);
|
||||
fprintf(fd, " . RX gain : %.1f dB\n", opts->rx_gain);
|
||||
fprintf(fd, "\n");
|
||||
|
||||
fprintf(fd, " . Sample Rate : %.3lf Msps\n", (float)opts->samp_rate / 1e6);
|
||||
fprintf(fd, "\n");
|
||||
|
||||
fprintf(fd, " . Buffer count : %d\n", opts->buf_cnt);
|
||||
fprintf(fd, " . Buffer size : %d bytes\n", opts->buf_size);
|
||||
fprintf(fd, "\n");
|
||||
|
||||
fprintf(fd, " . Echo amplitude : %.1f\n", opts->echo_scale);
|
||||
fprintf(fd, " . Echo delay : %d samples\n", opts->echo_delay);
|
||||
fprintf(fd, "\n");
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* Main */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct app_state _app, *app=&_app;
|
||||
int rv;
|
||||
|
||||
/* Options */
|
||||
memset(app, 0x00, sizeof(struct app_state));
|
||||
opts_defaults(&app->opts);
|
||||
rv = opts_parse(&app->opts, argc, argv);
|
||||
if (rv)
|
||||
return rv < 0 ? rv : 0;
|
||||
opts_print(&app->opts, stderr);
|
||||
|
||||
/* Open and configure pluto */
|
||||
rv = app_pluto_open(app);
|
||||
if (rv)
|
||||
goto err;
|
||||
|
||||
/* Start streaming */
|
||||
app_pluto_start(app);
|
||||
|
||||
/* Dummy loop */
|
||||
while (1)
|
||||
{
|
||||
memset(iio_buffer_start(app->pluto.tx_buf), 0x00, app->opts.buf_size);
|
||||
iio_buffer_push(app->pluto.tx_buf);
|
||||
iio_buffer_refill(app->pluto.rx_buf);
|
||||
}
|
||||
|
||||
err:
|
||||
/* Stop streaming */
|
||||
app_pluto_stop(app);
|
||||
|
||||
/* Shutdown */
|
||||
app_pluto_close(app);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue