aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSylvain Munaut <tnt@246tNt.com>2018-12-14 20:04:26 +0100
committerSylvain Munaut <tnt@246tNt.com>2018-12-14 20:04:26 +0100
commit8c5277d075815e4e5cf63af004d597afad9d4c1e (patch)
tree424be367054c5e47317f15d41e699a708deb46a6
parent18adc3602af4aa10ec11779b201dbae659dd83d9 (diff)
sw: Import the embedded control software
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
-rw-r--r--sw/Makefile23
-rw-r--r--sw/osmo-rfds.c432
2 files changed, 455 insertions, 0 deletions
diff --git a/sw/Makefile b/sw/Makefile
new file mode 100644
index 0000000..92137c5
--- /dev/null
+++ b/sw/Makefile
@@ -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
diff --git a/sw/osmo-rfds.c b/sw/osmo-rfds.c
new file mode 100644
index 0000000..b62836e
--- /dev/null
+++ b/sw/osmo-rfds.c
@@ -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;
+}