From 1650cc5ad2eba4fb8857ffc920170ae0f9f1a18d Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 17 Nov 2017 22:51:18 +0100 Subject: Restructure: Move sdr from common code to 'libsdr' --- .gitignore | 1 + configure.ac | 1 + src/Makefile.am | 40 +- src/amps/Makefile.am | 9 +- src/anetz/Makefile.am | 9 +- src/bnetz/Makefile.am | 9 +- src/cnetz/Makefile.am | 9 +- src/common/Makefile.am | 22 +- src/common/display_iq.c | 280 -------------- src/common/display_spectrum.c | 292 -------------- src/common/main_mobile.c | 4 +- src/common/sdr.c | 851 ----------------------------------------- src/common/sdr.h | 8 - src/common/sdr_config.c | 262 ------------- src/common/sdr_config.h | 32 -- src/common/sender.h | 2 +- src/common/soapy.c | 466 ----------------------- src/common/soapy.h | 8 - src/common/uhd.c | 583 ---------------------------- src/common/uhd.h | 8 - src/jtacs/Makefile.am | 9 +- src/libimage/Makefile.am | 2 +- src/libsdr/Makefile.am | 26 ++ src/libsdr/display_iq.c | 280 ++++++++++++++ src/libsdr/display_spectrum.c | 292 ++++++++++++++ src/libsdr/sdr.c | 865 ++++++++++++++++++++++++++++++++++++++++++ src/libsdr/sdr.h | 8 + src/libsdr/sdr_config.c | 262 +++++++++++++ src/libsdr/sdr_config.h | 32 ++ src/libsdr/soapy.c | 468 +++++++++++++++++++++++ src/libsdr/soapy.h | 8 + src/libsdr/uhd.c | 585 ++++++++++++++++++++++++++++ src/libsdr/uhd.h | 8 + src/nmt/Makefile.am | 7 +- src/r2000/Makefile.am | 7 +- src/tacs/Makefile.am | 9 +- src/test/Makefile.am | 36 +- src/tv/Makefile.am | 15 +- src/tv/main.c | 4 +- 39 files changed, 2971 insertions(+), 2848 deletions(-) delete mode 100644 src/common/display_iq.c delete mode 100644 src/common/display_spectrum.c delete mode 100644 src/common/sdr.c delete mode 100644 src/common/sdr.h delete mode 100644 src/common/sdr_config.c delete mode 100644 src/common/sdr_config.h delete mode 100644 src/common/soapy.c delete mode 100644 src/common/soapy.h delete mode 100644 src/common/uhd.c delete mode 100644 src/common/uhd.h create mode 100644 src/libsdr/Makefile.am create mode 100644 src/libsdr/display_iq.c create mode 100644 src/libsdr/display_spectrum.c create mode 100644 src/libsdr/sdr.c create mode 100644 src/libsdr/sdr.h create mode 100644 src/libsdr/sdr_config.c create mode 100644 src/libsdr/sdr_config.h create mode 100644 src/libsdr/soapy.c create mode 100644 src/libsdr/soapy.h create mode 100644 src/libsdr/uhd.c create mode 100644 src/libsdr/uhd.h diff --git a/.gitignore b/.gitignore index 5a7ad4e..88eaa4f 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ src/libwave/libwave.a src/libfft/libfft.a src/libmncc/libmncc.a src/libsound/libsound.a +src/libsdr/libsdr.a src/common/libcommon.a src/common/libmobile.a src/anetz/libgermanton.a diff --git a/configure.ac b/configure.ac index 8a4e745..515a874 100644 --- a/configure.ac +++ b/configure.ac @@ -93,6 +93,7 @@ AC_OUTPUT( src/libfft/Makefile src/libmncc/Makefile src/libsound/Makefile + src/libsdr/Makefile src/common/Makefile src/anetz/Makefile src/bnetz/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 9b0653e..17f4565 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,41 @@ AUTOMAKE_OPTIONS = foreign -SUBDIRS = libsound libimage libcompandor libgoertzel libjitter libsquelch libhagelbarger libdtmf libtimer libsamplerate libscrambler libemphasis libfsk libfm libfilter libwave libfft libmncc common anetz bnetz cnetz nmt amps tacs jtacs r2000 tv test + +SUBDIRS = \ + libimage \ + libcompandor \ + libgoertzel \ + libjitter \ + libsquelch \ + libhagelbarger \ + libdtmf \ + libtimer \ + libsamplerate \ + libscrambler \ + libemphasis \ + libfsk \ + libfm \ + libfilter \ + libwave \ + libfft \ + libmncc \ + libsound + +if HAVE_SDR +SUBDIRS += \ + libsdr +endif + +SUBDIRS += \ + common \ + anetz \ + bnetz \ + cnetz \ + nmt \ + amps \ + tacs \ + jtacs \ + r2000 \ + tv \ + test + diff --git a/src/amps/Makefile.am b/src/amps/Makefile.am index c2fe952..1f384d5 100644 --- a/src/amps/Makefile.am +++ b/src/amps/Makefile.am @@ -34,10 +34,8 @@ amps_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -45,3 +43,10 @@ amps_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +amps_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + diff --git a/src/anetz/Makefile.am b/src/anetz/Makefile.am index 5c9c8d6..9ff905e 100644 --- a/src/anetz/Makefile.am +++ b/src/anetz/Makefile.am @@ -26,10 +26,8 @@ anetz_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -37,3 +35,10 @@ anetz_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +anetz_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + diff --git a/src/bnetz/Makefile.am b/src/bnetz/Makefile.am index 88f7a44..922e8fb 100644 --- a/src/bnetz/Makefile.am +++ b/src/bnetz/Makefile.am @@ -22,10 +22,8 @@ bnetz_LDADD = \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ $(top_builddir)/src/libfsk/libfsk.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -33,3 +31,10 @@ bnetz_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +bnetz_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + diff --git a/src/cnetz/Makefile.am b/src/cnetz/Makefile.am index e01c685..95b83d4 100644 --- a/src/cnetz/Makefile.am +++ b/src/cnetz/Makefile.am @@ -25,10 +25,8 @@ cnetz_LDADD = \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libscrambler/libscrambler.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -36,3 +34,10 @@ cnetz_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +cnetz_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 6d1e07b..9abad04 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) $(GRAPHICSMAGICK_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(UHD_CFLAGS) $(SOAPY_CFLAGS) +AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) noinst_LIBRARIES = libcommon.a libmobile.a @@ -16,25 +16,5 @@ libmobile_a_SOURCES = \ if HAVE_SDR AM_CPPFLAGS += -DHAVE_SDR - -libcommon_a_SOURCES += \ - sdr_config.c \ - sdr.c \ - display_iq.c \ - display_spectrum.c -endif - -if HAVE_UHD -AM_CPPFLAGS += -DHAVE_UHD - -libcommon_a_SOURCES += \ - uhd.c -endif - -if HAVE_SOAPY -AM_CPPFLAGS += -DHAVE_SOAPY - -libcommon_a_SOURCES += \ - soapy.c endif diff --git a/src/common/display_iq.c b/src/common/display_iq.c deleted file mode 100644 index c63a455..0000000 --- a/src/common/display_iq.c +++ /dev/null @@ -1,280 +0,0 @@ -/* display IQ data form functions - * - * (C) 2016 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include "sample.h" -#include "sender.h" - -/* must be odd value! */ -#define SIZE 23 - -static char screen[SIZE][MAX_DISPLAY_WIDTH]; -static uint8_t screen_color[SIZE][MAX_DISPLAY_WIDTH]; -static uint8_t screen_history[SIZE * 2][MAX_DISPLAY_WIDTH]; -static int iq_on = 0; -static double db = 80; - -static dispiq_t disp; - -void display_iq_init(int samplerate) -{ - memset(&disp, 0, sizeof(disp)); - memset(&screen_history, 0, sizeof(screen_history)); - disp.interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5; - /* should not happen due to low interval */ - if (disp.interval_max < MAX_DISPLAY_IQ - 1) - disp.interval_max = MAX_DISPLAY_IQ - 1; -} - -void display_iq_on(int on) -{ - int j; - int w, h; - - get_win_size(&w, &h); - - if (iq_on) { - memset(&screen, ' ', sizeof(screen)); - memset(&screen_history, 0, sizeof(screen_history)); - printf("\0337\033[H"); - for (j = 0; j < SIZE; j++) { - screen[j][w] = '\0'; - puts(screen[j]); - } - printf("\0338"); fflush(stdout); - } - - if (on < 0) { - if (++iq_on == 3) - iq_on = 0; - } else - iq_on = on; -} - -void display_iq_limit_scroll(int on) -{ - int w, h; - - if (!iq_on) - return; - - get_win_size(&w, &h); - - printf("\0337"); - printf("\033[%d;%dr", (on) ? SIZE + 1 : 1, h); - printf("\0338"); -} - -/* - * plot IQ data: - * - * theoretical example: SIZE = 3 allows 6 steps plotted as dots - * - * Line 0: : - * Line 1: : - * Line 2: : - * - * The level of -1.0 .. 1.0 is scaled to -3 and 3. - * - * The lowest of the upper 3 dots ranges from 0.0 .. <1.5. - * The upper most dot ranges from 2.5 .. <3.5. - * The highest of the lower 3 dots ranges from <0.0 .. >-1.5; - * The lower most dot ranges from -2.5 .. >-3.5. - * - * The center column ranges from -0.5 .. <0.5. - * The columns about the center from -1.5 .. <1.5. - */ -void display_iq(float *samples, int length) -{ - int pos, max; - float *buffer; - int i, j, k; - int color = 9; /* default color */ - int x_center, y_center; - double I, Q, L, l, s; - int x, y; - int v, r; - int width, h; - - if (!iq_on) - return; - - get_win_size(&width, &h); - - /* at what line we draw our zero-line and what character we use */ - x_center = width >> 1; - y_center = (SIZE - 1) >> 1; - - pos = disp.interval_pos; - max = disp.interval_max; - buffer = disp.buffer; - - for (i = 0; i < length; i++) { - if (pos >= MAX_DISPLAY_IQ) { - if (++pos == max) - pos = 0; - continue; - } - buffer[pos * 2] = samples[i * 2]; - buffer[pos * 2 + 1] = samples[i * 2 + 1]; - pos++; - if (pos == MAX_DISPLAY_IQ) { - memset(&screen, ' ', sizeof(screen)); - memset(&screen_color, 7, sizeof(screen_color)); - /* render screen history to screen */ - for (y = 0; y < SIZE * 2; y++) { - for (x = 0; x < width; x++) { - v = screen_history[y][x]; - v -= 8; - if (v < 0) - v = 0; - screen_history[y][x] = v; - r = random() & 0x3f; - if (r >= v) - continue; - if (screen[y/2][x] == ':') - continue; - if (screen[y/2][x] == '.') { - if ((y & 1) == 0) - screen[y/2][x] = ':'; - continue; - } - if (screen[y/2][x] == '\'') { - if ((y & 1)) - screen[y/2][x] = ':'; - continue; - } - if ((y & 1) == 0) - screen[y/2][x] = '\''; - else - screen[y/2][x] = '.'; - screen_color[y/2][x] = 4; - } - } - /* plot current IQ date */ - for (j = 0; j < MAX_DISPLAY_IQ; j++) { - I = buffer[j * 2]; - Q = buffer[j * 2 + 1]; - L = I*I + Q*Q; - if (iq_on > 1) { - /* logarithmic scale */ - l = sqrt(L); - s = log10(l) * 20 + db; - if (s < 0) - s = 0; - I = (I / l) * (s / db); - Q = (Q / l) * (s / db); - } - x = x_center + (int)(I * (double)SIZE + (double)width + 0.5) - width; - if (x < 0) - continue; - if (x > width - 1) - continue; - if (Q >= 0) - y = SIZE - 1 - (int)(Q * (double)SIZE - 0.5); - else - y = SIZE - (int)(Q * (double)SIZE + 0.5); - if (y < 0) - continue; - if (y > SIZE * 2 - 1) - continue; - if (screen[y/2][x] == ':' && screen_color[y/2][x] >= 10) - goto cont; - if (screen[y/2][x] == '.' && screen_color[y/2][x] >= 10) { - if ((y & 1) == 0) - screen[y/2][x] = ':'; - goto cont; - } - if (screen[y/2][x] == '\'' && screen_color[y/2][x] >= 10) { - if ((y & 1)) - screen[y/2][x] = ':'; - goto cont; - } - if ((y & 1) == 0) - screen[y/2][x] = '\''; - else - screen[y/2][x] = '.'; -cont: - screen_history[y][x] = 255; - /* overdrive: - * red = close to -1..1 or above - * yellow = close to -0.5..0.5 or above - * Note: L is square of vector length, - * so we compare with square values. - */ - if (L > 0.9 * 0.9) - screen_color[y/2][x] = 11; - else if (L > 0.45 * 0.45 && screen_color[y/2][x] != 11) - screen_color[y/2][x] = 13; - else if (screen_color[y/2][x] < 10) - screen_color[y/2][x] = 12; - } - if (iq_on == 1) - sprintf(screen[0], "(IQ linear"); - else - sprintf(screen[0], "(IQ log %.0f dB", db); - *strchr(screen[0], '\0') = ')'; - printf("\0337\033[H"); - for (j = 0; j < SIZE; j++) { - for (k = 0; k < width; k++) { - if ((j == y_center || k == x_center) && screen[j][k] == ' ') { - /* cross */ - if (color != 4) { - color = 4; - printf("\033[0;34m"); - } - if (j == y_center) { - if (k == x_center) - putchar('o'); - else if (k == x_center - SIZE) - putchar('+'); - else if (k == x_center + SIZE) - putchar('+'); - else - putchar('-'); - } else { - if (j == 0 || j == SIZE - 1) - putchar('+'); - else - putchar('|'); - } - } else { - if (screen_color[j][k] != color) { - color = screen_color[j][k]; - printf("\033[%d;3%dm", color / 10, color % 10); - } - putchar(screen[j][k]); - } - } - printf("\n"); - } - /* reset color and position */ - printf("\033[0;39m\0338"); fflush(stdout); - } - } - - disp.interval_pos = pos; -} - - diff --git a/src/common/display_spectrum.c b/src/common/display_spectrum.c deleted file mode 100644 index 80af735..0000000 --- a/src/common/display_spectrum.c +++ /dev/null @@ -1,292 +0,0 @@ -/* display spectrum of IQ data - * - * (C) 2016 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include "sample.h" -#include "sender.h" -#include "../libfft/fft.h" - -#define HEIGHT 20 - -static double buffer_max[MAX_DISPLAY_SPECTRUM]; -static char screen[HEIGHT][MAX_DISPLAY_WIDTH]; -static uint8_t screen_color[HEIGHT][MAX_DISPLAY_WIDTH]; -static int spectrum_on = 0; -static double db = 120; -static double center_frequency, frequency_range; - -static dispspectrum_t disp; - -void display_spectrum_init(int samplerate, double _center_frequency) -{ - memset(&disp, 0, sizeof(disp)); - disp.interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5; - /* should not happen due to low interval */ - if (disp.interval_max < MAX_DISPLAY_SPECTRUM - 1) - disp.interval_max = MAX_DISPLAY_SPECTRUM - 1; - memset(buffer_max, 0, sizeof(buffer_max)); - - center_frequency = _center_frequency; - frequency_range = (double)samplerate; -} - -void display_spectrum_on(int on) -{ - int j; - int w, h; - - get_win_size(&w, &h); - - if (spectrum_on) { - memset(&screen, ' ', sizeof(screen)); - printf("\0337\033[H"); - for (j = 0; j < HEIGHT; j++) { - screen[j][w] = '\0'; - puts(screen[j]); - } - printf("\0338"); fflush(stdout); - } - - if (on < 0) { - if (++spectrum_on == 2) - spectrum_on = 0; - } else - spectrum_on = on; -} - -void display_spectrum_limit_scroll(int on) -{ - int w, h; - - if (!spectrum_on) - return; - - get_win_size(&w, &h); - - printf("\0337"); - printf("\033[%d;%dr", (on) ? HEIGHT + 1 : 1, h); - printf("\0338"); -} - -/* - * plot spectrum data: - * - */ -void display_spectrum(float *samples, int length) -{ - sender_t *sender; - char print_channel[32], print_frequency[32]; - int width, h; - int pos, max; - double *buffer_I, *buffer_Q; - int color = 9; /* default color */ - int i, j, k, o; - double I, Q, v; - int s, e, l, n; - - if (!spectrum_on) - return; - - get_win_size(&width, &h); - if (width > MAX_DISPLAY_WIDTH) - width = MAX_DISPLAY_WIDTH; - - /* calculate size of FFT */ - int m, fft_size = 0, fft_taps = 0; - for (m = 0; m < 16; m++) { - if ((1 << m) > MAX_DISPLAY_SPECTRUM) - break; - if ((1 << m) <= width) { - fft_taps = m; - fft_size = 1 << m; - } - } - if (m == 16) { - fprintf(stderr, "Size of spectrum is not a power of 2, please fix!\n"); - abort(); - } - - int heigh[fft_size], low[fft_size]; - - pos = disp.interval_pos; - max = disp.interval_max; - buffer_I = disp.buffer_I; - buffer_Q = disp.buffer_Q; - - for (i = 0; i < length; i++) { - if (pos >= fft_size) { - if (++pos == max) - pos = 0; - continue; - } - buffer_I[pos] = samples[i * 2]; - buffer_Q[pos] = samples[i * 2 + 1]; - pos++; - if (pos == fft_size) { - fft_process(1, fft_taps, buffer_I, buffer_Q); - k = 0; - for (j = 0; j < fft_size; j++) { - /* scale result vertically */ - I = buffer_I[(j + fft_size / 2) % fft_size]; - Q = buffer_Q[(j + fft_size / 2) % fft_size]; - v = sqrt(I*I + Q*Q); - v = log10(v) * 20 + db; - if (v < 0) - v = 0; - v /= db; - buffer_max[j] -= DISPLAY_INTERVAL / 10.0; - if (v > buffer_max[j]) - buffer_max[j] = v; - - /* heigh is the maximum value */ - heigh[j] = (double)(HEIGHT * 2 - 1) * (1.0 - buffer_max[j]); - if (heigh[j] < 0) - heigh[j] = 0; - if (heigh[j] >= (HEIGHT * 2)) - heigh[j] = (HEIGHT * 2) - 1; - /* low is the current value */ - low[j] = (double)(HEIGHT * 2 - 1) * (1.0 - v); - if (low[j] < 0) - low[j] = 0; - if (low[j] >= (HEIGHT * 2)) - low[j] = (HEIGHT * 2) - 1; - } - /* plot scaled buffer */ - memset(&screen, ' ', sizeof(screen)); - memset(&screen_color, 7, sizeof(screen_color)); /* all white */ - sprintf(screen[0], "(spectrum log %.0f dB", db); - *strchr(screen[0], '\0') = ')'; - o = (width - fft_size) / 2; /* offset from left border */ - for (j = 0; j < fft_size; j++) { - s = l = n = low[j]; - /* get last and next value */ - if (j > 0) - l = (low[j - 1] + s) / 2; - if (j < fft_size - 1) - n = (low[j + 1] + s) / 2; - if (s > l && s > n) { - /* current value is a minimum */ - e = s; - s = (l < n) ? (l + 1) : (n + 1); - } else if (s < l && s < n) { - /* current value is a maximum */ - e = (l > n) ? l : n; - } else if (l < n) { - /* last value is higher, next value is lower */ - s = l + 1; - e = n; - } else if (l > n) { - /* last value is lower, next value is higher */ - s = n + 1; - e = l; - } else { - /* current, last and next values are equal */ - e = s; - } - if (s == e) { - if ((s & 1) == 0) - screen[s >> 1][j + o] = '\''; - else - screen[s >> 1][j + o] = '.'; - screen_color[s >> 1][j + o] = 13; - } else { - if ((s & 1) == 0) - screen[s >> 1][j + o] = '|'; - else - screen[s >> 1][j + o] = '.'; - screen_color[s >> 1][j + o] = 13; - if ((e & 1) == 0) - screen[e >> 1][j + o] = '\''; - else - screen[e >> 1][j + o] = '|'; - screen_color[e >> 1][j + o] = 13; - for (k = (s >> 1) + 1; k < (e >> 1); k++) { - screen[k][j + o] = '|'; - screen_color[k][j + o] = 13; - } - } - e = s; - s = heigh[j]; - if ((s >> 1) < (e >> 1)) { - if ((s & 1) == 0) - screen[s >> 1][j + o] = '|'; - else - screen[s >> 1][j + o] = '.'; - screen_color[s >> 1][j + o] = 4; - for (k = (s >> 1) + 1; k < (e >> 1); k++) { - screen[k][j + o] = '|'; - screen_color[k][j + o] = 4; - } - } - } - for (sender = sender_head; sender; sender = sender->next) { - j = (int)((sender->empfangsfrequenz - center_frequency) / frequency_range * (double) fft_size + width / 2 + 0.5); - if (j < 0 || j >= width) /* check out-of-range, should not happen */ - continue; - for (k = 0; k < HEIGHT; k++) { - /* skip yellow graph */ - if (screen_color[k][j] == 13) - continue; - screen[k][j] = ':'; - screen_color[k][j] = 12; - } - sprintf(print_channel, "Ch%d", sender->kanal); - for (o = 0; o < (int)strlen(print_channel); o++) { - s = j - strlen(print_channel) + o; - if (s >= 0 && s < width) { - screen[HEIGHT - 1][s] = print_channel[o]; - screen_color[HEIGHT - 1][s] = 7; - } - } - if (fmod(sender->empfangsfrequenz, 1000.0)) - sprintf(print_frequency, "%.4f", sender->empfangsfrequenz / 1e6); - else - sprintf(print_frequency, "%.3f", sender->empfangsfrequenz / 1e6); - for (o = 0; o < (int)strlen(print_frequency); o++) { - s = j + o + 1; - if (s >= 0 && s < width) { - screen[HEIGHT - 1][s] = print_frequency[o]; - screen_color[HEIGHT - 1][s] = 7; - } - } - } - printf("\0337\033[H"); - for (j = 0; j < HEIGHT; j++) { - for (k = 0; k < width; k++) { - if (screen_color[j][k] != color) { - color = screen_color[j][k]; - printf("\033[%d;3%dm", color / 10, color % 10); - } - putchar(screen[j][k]); - } - printf("\n"); - } - /* reset color and position */ - printf("\033[0;39m\0338"); fflush(stdout); - } - } - - disp.interval_pos = pos; -} - - diff --git a/src/common/main_mobile.c b/src/common/main_mobile.c index 7516bba..b4f9343 100644 --- a/src/common/main_mobile.c +++ b/src/common/main_mobile.c @@ -39,8 +39,8 @@ #include "../libmncc/mncc_sock.h" #include "../libmncc/mncc_cross.h" #ifdef HAVE_SDR -#include "sdr.h" -#include "sdr_config.h" +#include "../libsdr/sdr.h" +#include "../libsdr/sdr_config.h" #endif static int got_init = 0; diff --git a/src/common/sdr.c b/src/common/sdr.c deleted file mode 100644 index d7a445b..0000000 --- a/src/common/sdr.c +++ /dev/null @@ -1,851 +0,0 @@ -/* SDR processing - * - * (C) 2017 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -enum paging_signal; - -#include -#include -#include -#include -#include -#include -#include -#define __USE_GNU -#include -#include -#include "sample.h" -#include "../libfm/fm.h" -#include "../libtimer/timer.h" -#include "sender.h" -#include "sdr_config.h" -#include "sdr.h" -#ifdef HAVE_UHD -#include "uhd.h" -#endif -#ifdef HAVE_SOAPY -#include "soapy.h" -#endif -#include "debug.h" - -/* enable to debug buffer handling */ -//#define DEBUG_BUFFER - -/* enable to test without oversampling filter */ -//#define DISABLE_FILTER - -/* usable bandwidth of IQ rate, because no filter is perfect */ -#define USABLE_BANDWIDTH 0.75 - -typedef struct sdr_thread { - int use; - volatile int running, exit; /* flags to control exit of threads */ - int buffer_size; - volatile float *buffer; - float *buffer2; - volatile int in, out; /* in and out pointers (atomic, so no locking required) */ - int max_fill; /* measure maximum buffer fill */ - double max_fill_timer; /* timer to display/reset maximum fill */ - iir_filter_t lp[2]; /* filter for upsample/downsample IQ data */ -} sdr_thread_t; - -typedef struct sdr_chan { - double tx_frequency; /* frequency used */ - double rx_frequency; /* frequency used */ - fm_mod_t mod; /* modulator instance */ - fm_demod_t demod; /* demodulator instance */ - dispmeasparam_t *dmp_rf_level; - dispmeasparam_t *dmp_freq_offset; - dispmeasparam_t *dmp_deviation; -} sdr_chan_t; - -typedef struct sdr { - int threads; /* use threads */ - int oversample; /* oversample IQ rate */ - sdr_thread_t thread_read, - thread_write; - sdr_chan_t *chan; /* settings for all channels */ - int paging_channel; /* if set, points to paging channel */ - sdr_chan_t paging_chan; /* settings for extra paging channel */ - int channels; /* number of frequencies */ - double amplitude; /* amplitude of each carrier */ - int samplerate; /* sample rate of audio data */ - int latspl; /* latency in audio samples */ - wave_rec_t wave_rx_rec; - wave_rec_t wave_tx_rec; - wave_play_t wave_rx_play; - wave_play_t wave_tx_play; - float *modbuff; /* buffer for FM transmodulation */ - sample_t *modbuff_I; - sample_t *modbuff_Q; - sample_t *wavespl0; /* sample buffer for wave generation */ - sample_t *wavespl1; -} sdr_t; - -void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_frequency, double *rx_frequency, int channels, double paging_frequency, int samplerate, int latspl, double max_deviation, double max_modulation) -{ - sdr_t *sdr; - int threads = 1, oversample = 1; /* always use threads */ - double bandwidth; - double tx_center_frequency = 0.0, rx_center_frequency = 0.0; - int rc; - int c; - - PDEBUG(DSDR, DEBUG_DEBUG, "Open SDR device\n"); - - if (sdr_config->samplerate != samplerate) { - if (samplerate > sdr_config->samplerate) { - PDEBUG(DSDR, DEBUG_ERROR, "SDR sample rate must be greater than audio sample rate!\n"); - PDEBUG(DSDR, DEBUG_ERROR, "You selected an SDR rate of %d and an audio rate of %d.\n", sdr_config->samplerate, samplerate); - return NULL; - } - if ((sdr_config->samplerate % samplerate)) { - PDEBUG(DSDR, DEBUG_ERROR, "SDR sample rate must be a multiple of audio sample rate!\n"); - PDEBUG(DSDR, DEBUG_ERROR, "You selected an SDR rate of %d and an audio rate of %d.\n", sdr_config->samplerate, samplerate); - return NULL; - } - oversample = sdr_config->samplerate / samplerate; - threads = 1; - } - - bandwidth = 2.0 * (max_deviation + max_modulation); - PDEBUG(DSDR, DEBUG_INFO, "Require bandwidth of each channel is 2 * (%.1f deviation + %.1f modulation) = %.1f KHz\n", max_deviation / 1e3, max_modulation / 1e3, bandwidth / 1e3); - - if (channels < 1) { - PDEBUG(DSDR, DEBUG_ERROR, "No channel given, please fix!\n"); - abort(); - } - - sdr = calloc(sizeof(*sdr), 1); - if (!sdr) { - PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); - goto error; - } - sdr->channels = channels; - sdr->amplitude = 1.0 / (double)channels; - sdr->samplerate = samplerate; - sdr->latspl = latspl; - sdr->threads = threads; /* always requried, because write may block */ - sdr->oversample = oversample; - - if (threads) { - memset(&sdr->thread_read, 0, sizeof(sdr->thread_read)); - sdr->thread_read.buffer_size = sdr->latspl * 2 * sdr->oversample + 2; - sdr->thread_read.buffer = calloc(sdr->thread_read.buffer_size, sizeof(*sdr->thread_read.buffer)); - if (!sdr->thread_read.buffer) { - PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); - goto error; - } - sdr->thread_read.buffer2 = calloc(sdr->thread_read.buffer_size, sizeof(*sdr->thread_read.buffer2)); - if (!sdr->thread_read.buffer2) { - PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); - goto error; - } - sdr->thread_read.in = sdr->thread_read.out = 0; - if (oversample > 1) { - iir_lowpass_init(&sdr->thread_read.lp[0], samplerate / 2.0, sdr_config->samplerate, 2); - iir_lowpass_init(&sdr->thread_read.lp[1], samplerate / 2.0, sdr_config->samplerate, 2); - } - memset(&sdr->thread_write, 0, sizeof(sdr->thread_write)); - sdr->thread_write.buffer_size = sdr->latspl * 2 + 2; - sdr->thread_write.buffer = calloc(sdr->thread_write.buffer_size, sizeof(*sdr->thread_write.buffer)); - if (!sdr->thread_write.buffer) { - PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); - goto error; - } - sdr->thread_write.buffer2 = calloc(sdr->thread_write.buffer_size * sdr->oversample, sizeof(*sdr->thread_write.buffer2)); - if (!sdr->thread_write.buffer2) { - PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); - goto error; - } - sdr->thread_write.in = sdr->thread_write.out = 0; - if (oversample > 1) { - iir_lowpass_init(&sdr->thread_write.lp[0], samplerate / 2.0, sdr_config->samplerate, 2); - iir_lowpass_init(&sdr->thread_write.lp[1], samplerate / 2.0, sdr_config->samplerate, 2); - } - } - - /* alloc fm modulation buffers */ - sdr->modbuff = calloc(sdr->latspl * 2, sizeof(*sdr->modbuff)); - if (!sdr->modbuff) { - PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); - goto error; - } - sdr->modbuff_I = calloc(sdr->latspl, sizeof(*sdr->modbuff_I)); - if (!sdr->modbuff_I) { - PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); - goto error; - } - sdr->modbuff_Q = calloc(sdr->latspl, sizeof(*sdr->modbuff_Q)); - if (!sdr->modbuff_Q) { - PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); - goto error; - } - sdr->wavespl0 = calloc(sdr->latspl, sizeof(*sdr->wavespl0)); - if (!sdr->wavespl0) { - PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); - goto error; - } - sdr->wavespl1 = calloc(sdr->latspl, sizeof(*sdr->wavespl1)); - if (!sdr->wavespl1) { - PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); - goto error; - } - - /* special case where we use a paging frequency */ - if (paging_frequency) { - /* add extra paging channel */ - sdr->paging_channel = channels; - } - - /* create list of channel states */ - sdr->chan = calloc(sizeof(*sdr->chan), channels + (sdr->paging_channel != 0)); - if (!sdr->chan) { - PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); - goto error; - } - - if (tx_frequency) { - /* calculate required bandwidth (IQ rate) */ - for (c = 0; c < channels; c++) { - PDEBUG(DSDR, DEBUG_INFO, "Frequency #%d: TX = %.6f MHz\n", c, tx_frequency[c] / 1e6); - sdr->chan[c].tx_frequency = tx_frequency[c]; - } - if (sdr->paging_channel) { - PDEBUG(DSDR, DEBUG_INFO, "Paging Frequency: TX = %.6f MHz\n", paging_frequency / 1e6); - sdr->chan[sdr->paging_channel].tx_frequency = paging_frequency; - } - - double tx_low_frequency = sdr->chan[0].tx_frequency, tx_high_frequency = sdr->chan[0].tx_frequency; - for (c = 1; c < channels; c++) { - if (sdr->chan[c].tx_frequency < tx_low_frequency) - tx_low_frequency = sdr->chan[c].tx_frequency; - if (sdr->chan[c].tx_frequency > tx_high_frequency) - tx_high_frequency = sdr->chan[c].tx_frequency; - } - if (sdr->paging_channel) { - if (sdr->chan[sdr->paging_channel].tx_frequency < tx_low_frequency) - tx_low_frequency = sdr->chan[sdr->paging_channel].tx_frequency; - if (sdr->chan[sdr->paging_channel].tx_frequency > tx_high_frequency) - tx_high_frequency = sdr->chan[sdr->paging_channel].tx_frequency; - } - /* range of TX */ - double range = tx_high_frequency - tx_low_frequency + bandwidth; - PDEBUG(DSDR, DEBUG_INFO, "Total bandwidth for all TX Frequencies: %.0f Hz\n", range); - if (range > samplerate * USABLE_BANDWIDTH) { - PDEBUG(DSDR, DEBUG_NOTICE, "*******************************************************************************\n"); - PDEBUG(DSDR, DEBUG_NOTICE, "The required bandwidth of %.0f Hz exceeds %.0f%% of the sample rate.\n", range, USABLE_BANDWIDTH * 100.0); - PDEBUG(DSDR, DEBUG_NOTICE, "Please increase samplerate!\n"); - PDEBUG(DSDR, DEBUG_NOTICE, "*******************************************************************************\n"); - goto error; - } - tx_center_frequency = (tx_high_frequency + tx_low_frequency) / 2.0; - PDEBUG(DSDR, DEBUG_INFO, "Using center frequency: TX %.6f MHz\n", tx_center_frequency / 1e6); - /* set offsets to center frequency */ - for (c = 0; c < channels; c++) { - double tx_offset; - tx_offset = sdr->chan[c].tx_frequency - tx_center_frequency; - PDEBUG(DSDR, DEBUG_DEBUG, "Frequency #%d: TX offset: %.6f MHz\n", c, tx_offset / 1e6); - rc = fm_mod_init(&sdr->chan[c].mod, samplerate, tx_offset, sdr->amplitude); - if (rc < 0) - goto error; - } - if (sdr->paging_channel) { - double tx_offset; - tx_offset = sdr->chan[sdr->paging_channel].tx_frequency - tx_center_frequency; - PDEBUG(DSDR, DEBUG_DEBUG, "Paging Frequency: TX offset: %.6f MHz\n", tx_offset / 1e6); - rc = fm_mod_init(&sdr->chan[sdr->paging_channel].mod, samplerate, tx_offset, sdr->amplitude); - if (rc < 0) - goto error; - } - /* show gain */ - PDEBUG(DSDR, DEBUG_INFO, "Using gain: TX %.1f dB\n", sdr_config->tx_gain); - /* open wave */ - if (sdr_config->write_iq_tx_wave) { - rc = wave_create_record(&sdr->wave_tx_rec, sdr_config->write_iq_tx_wave, samplerate, 2, 1.0); - if (rc < 0) { - PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE recoding instance!\n"); - goto error; - } - } - if (sdr_config->read_iq_tx_wave) { - rc = wave_create_playback(&sdr->wave_tx_play, sdr_config->read_iq_tx_wave, samplerate, 2, 1.0); - if (rc < 0) { - PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE playback instance!\n"); - goto error; - } - } - } - - if (rx_frequency) { - for (c = 0; c < channels; c++) { - PDEBUG(DSDR, DEBUG_INFO, "Frequency #%d: RX = %.6f MHz\n", c, rx_frequency[c] / 1e6); - sdr->chan[c].rx_frequency = rx_frequency[c]; - } - - /* calculate required bandwidth (IQ rate) */ - double rx_low_frequency = sdr->chan[0].rx_frequency, rx_high_frequency = sdr->chan[0].rx_frequency; - for (c = 1; c < channels; c++) { - if (sdr->chan[c].rx_frequency < rx_low_frequency) - rx_low_frequency = sdr->chan[c].rx_frequency; - if (sdr->chan[c].rx_frequency > rx_high_frequency) - rx_high_frequency = sdr->chan[c].rx_frequency; - } - /* range of RX */ - double range = rx_high_frequency - rx_low_frequency + bandwidth; - PDEBUG(DSDR, DEBUG_INFO, "Total bandwidth for all RX Frequencies: %.0f Hz\n", range); - if (range > samplerate * USABLE_BANDWIDTH) { - PDEBUG(DSDR, DEBUG_NOTICE, "*******************************************************************************\n"); - PDEBUG(DSDR, DEBUG_NOTICE, "The required bandwidth of %.0f Hz exceeds %.0f%% of the sample rate.\n", range, USABLE_BANDWIDTH * 100.0); - PDEBUG(DSDR, DEBUG_NOTICE, "Please increase samplerate!\n"); - PDEBUG(DSDR, DEBUG_NOTICE, "*******************************************************************************\n"); - goto error; - } - rx_center_frequency = (rx_high_frequency + rx_low_frequency) / 2.0; - PDEBUG(DSDR, DEBUG_INFO, "Using center frequency: RX %.6f MHz\n", rx_center_frequency / 1e6); - /* set offsets to center frequency */ - for (c = 0; c < channels; c++) { - double rx_offset; - rx_offset = sdr->chan[c].rx_frequency - rx_center_frequency; - PDEBUG(DSDR, DEBUG_DEBUG, "Frequency #%d: RX offset: %.6f MHz\n", c, rx_offset / 1e6); - rc = fm_demod_init(&sdr->chan[c].demod, samplerate, rx_offset, bandwidth); - if (rc < 0) - goto error; - } - /* show gain */ - PDEBUG(DSDR, DEBUG_INFO, "Using gain: RX %.1f dB\n", sdr_config->rx_gain); - /* open wave */ - if (sdr_config->write_iq_rx_wave) { - rc = wave_create_record(&sdr->wave_rx_rec, sdr_config->write_iq_rx_wave, samplerate, 2, 1.0); - if (rc < 0) { - PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE recoding instance!\n"); - goto error; - } - } - if (sdr_config->read_iq_rx_wave) { - rc = wave_create_playback(&sdr->wave_rx_play, sdr_config->read_iq_rx_wave, samplerate, 2, 1.0); - if (rc < 0) { - PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE playback instance!\n"); - goto error; - } - } - /* init measurements display */ - for (c = 0; c < channels; c++) { - sender_t *sender = get_sender_by_empfangsfrequenz(sdr->chan[c].rx_frequency); - if (!sender) - continue; - sdr->chan[c].dmp_rf_level = display_measurements_add(sender, "RF Level", "%.1f dB", DISPLAY_MEAS_AVG, DISPLAY_MEAS_LEFT, -96.0, 0.0, -INFINITY); - sdr->chan[c].dmp_freq_offset = display_measurements_add(sender, "Freq. Offset", "%+.2f KHz", DISPLAY_MEAS_AVG, DISPLAY_MEAS_CENTER, -max_deviation / 1000.0 * 2.0, max_deviation / 1000.0 * 2.0, 0.0); - sdr->chan[c].dmp_deviation = display_measurements_add(sender, "Deviation", "%.2f KHz", DISPLAY_MEAS_PEAK2PEAK, DISPLAY_MEAS_LEFT, 0.0, max_deviation / 1000.0 * 1.5, max_deviation / 1000.0); - } - } - - if (sdr_config->swap_links) { - double temp; - PDEBUG(DSDR, DEBUG_NOTICE, "Sapping RX and TX frequencies!\n"); - temp = rx_center_frequency; - rx_center_frequency = tx_center_frequency; - tx_center_frequency = temp; - } - - display_iq_init(samplerate); - display_spectrum_init(samplerate, rx_center_frequency); - -#ifdef HAVE_UHD - if (sdr_config->uhd) { - rc = uhd_open(sdr_config->channel, sdr_config->device_args, sdr_config->stream_args, sdr_config->tune_args, sdr_config->tx_antenna, sdr_config->rx_antenna, tx_center_frequency, rx_center_frequency, sdr_config->samplerate, sdr_config->tx_gain, sdr_config->rx_gain, sdr_config->bandwidth, sdr_config->uhd_tx_timestamps); - if (rc) - goto error; - } -#endif - -#ifdef HAVE_SOAPY - if (sdr_config->soapy) { - rc = soapy_open(sdr_config->channel, sdr_config->device_args, sdr_config->stream_args, sdr_config->tune_args, sdr_config->tx_antenna, sdr_config->rx_antenna, tx_center_frequency, rx_center_frequency, sdr_config->samplerate, sdr_config->tx_gain, sdr_config->rx_gain, sdr_config->bandwidth); - if (rc) - goto error; - } -#endif - - return sdr; - -error: - sdr_close(sdr); - return NULL; -} - -static void *sdr_write_child(void *arg) -{ - sdr_t *sdr = (sdr_t *)arg; - int num; - int fill, out; - int s, ss, o; - - while (sdr->thread_write.running) { - /* write to SDR */ - fill = (sdr->thread_write.in - sdr->thread_write.out + sdr->thread_write.buffer_size) % sdr->thread_write.buffer_size; - if (fill > sdr->thread_write.max_fill) - sdr->thread_write.max_fill = fill; - if (sdr->thread_write.max_fill_timer == 0.0) - sdr->thread_write.max_fill_timer = get_time(); - if (get_time() - sdr->thread_write.max_fill_timer > 1.0) { - double delay; - delay = (double)sdr->thread_write.max_fill / 2.0 / (double)sdr->samplerate; - sdr->thread_write.max_fill = 0; - sdr->thread_write.max_fill_timer += 1.0; - PDEBUG(DSDR, DEBUG_DEBUG, "write delay = %.3f ms\n", delay * 1000.0); - } - num = fill / 2; - if (num) { -#ifdef DEBUG_BUFFER - printf("Thread found %d samples in write buffer and forwards them to SDR.\n", num); -#endif - out = sdr->thread_write.out; - for (s = 0, ss = 0; s < num; s++) { - for (o = 0; o < sdr->oversample; o++) { - sdr->thread_write.buffer2[ss++] = sdr->thread_write.buffer[out]; - sdr->thread_write.buffer2[ss++] = sdr->thread_write.buffer[out + 1]; - } - out = (out + 2) % sdr->thread_write.buffer_size; - } - sdr->thread_write.out = out; -#ifndef DISABLE_FILTER - /* filter spectrum */ - if (sdr->oversample > 1) { - iir_process_baseband(&sdr->thread_write.lp[0], sdr->thread_write.buffer2, num * sdr->oversample); - iir_process_baseband(&sdr->thread_write.lp[1], sdr->thread_write.buffer2 + 1, num * sdr->oversample); - } -#endif -#ifdef HAVE_UHD - if (sdr_config->uhd) - uhd_send(sdr->thread_write.buffer2, num * sdr->oversample); -#endif -#ifdef HAVE_SOAPY - if (sdr_config->soapy) - soapy_send(sdr->thread_write.buffer2, num * sdr->oversample); -#endif - } - - /* delay some time */ - usleep(1000); - } - - PDEBUG(DSDR, DEBUG_DEBUG, "Thread received exit!\n"); - sdr->thread_write.exit = 1; - return NULL; -} - -static void *sdr_read_child(void *arg) -{ - sdr_t *sdr = (sdr_t *)arg; - int num, count = 0; - int space, in; - int s, ss; - - while (sdr->thread_read.running) { - /* read from SDR */ - space = (sdr->thread_read.out - sdr->thread_read.in - 2 + sdr->thread_read.buffer_size) % sdr->thread_read.buffer_size; - num = space / 2; - if (num) { -#ifdef HAVE_UHD - if (sdr_config->uhd) - count = uhd_receive(sdr->thread_read.buffer2, num); -#endif -#ifdef HAVE_SOAPY - if (sdr_config->soapy) - count = soapy_receive(sdr->thread_read.buffer2, num); -#endif - if (count > 0) { -#ifdef DEBUG_BUFFER - printf("Thread read %d samples from SDR and writes them to read buffer.\n", count); -#endif -#ifndef DISABLE_FILTER - /* filter spectrum */ - if (sdr->oversample > 1) { - iir_process_baseband(&sdr->thread_read.lp[0], sdr->thread_read.buffer2, count); - iir_process_baseband(&sdr->thread_read.lp[1], sdr->thread_read.buffer2 + 1, count); - } -#endif - in = sdr->thread_read.in; - for (s = 0, ss = 0; s < count; s++) { - sdr->thread_read.buffer[in++] = sdr->thread_read.buffer2[ss++]; - sdr->thread_read.buffer[in++] = sdr->thread_read.buffer2[ss++]; - in %= sdr->thread_read.buffer_size; - } - sdr->thread_read.in = in; - } - } - - /* delay some time */ - usleep(1000); - } - - PDEBUG(DSDR, DEBUG_DEBUG, "Thread received exit!\n"); - sdr->thread_read.exit = 1; - return NULL; -} - -/* start streaming */ -int sdr_start(void *inst) -{ - sdr_t *sdr = (sdr_t *)inst; - int rc = -EINVAL; - -#ifdef HAVE_UHD - if (sdr_config->uhd) - rc = uhd_start(); -#endif -#ifdef HAVE_SOAPY - if (sdr_config->soapy) - rc = soapy_start(); -#endif - if (rc < 0) - return rc; - - if (sdr->threads) { - int rc; - pthread_t tid; - char tname[64]; - - PDEBUG(DSDR, DEBUG_DEBUG, "Create threads!\n"); - sdr->thread_write.running = 1; - sdr->thread_write.exit = 0; - rc = pthread_create(&tid, NULL, sdr_write_child, inst); - if (rc < 0) { - sdr->thread_write.running = 0; - PDEBUG(DSDR, DEBUG_ERROR, "Failed to create thread!\n"); - return rc; - } - pthread_getname_np(tid, tname, sizeof(tname)); - strncat(tname, "-sdr_tx", sizeof(tname)); - tname[sizeof(tname) - 1] = '\0'; - pthread_setname_np(tid, tname); - sdr->thread_read.running = 1; - sdr->thread_read.exit = 0; - rc = pthread_create(&tid, NULL, sdr_read_child, inst); - if (rc < 0) { - sdr->thread_read.running = 0; - PDEBUG(DSDR, DEBUG_ERROR, "Failed to create thread!\n"); - return rc; - } - pthread_getname_np(tid, tname, sizeof(tname)); - strncat(tname, "-sdr_rx", sizeof(tname)); - tname[sizeof(tname) - 1] = '\0'; - pthread_setname_np(tid, tname); - } - - return 0; -} - -void sdr_close(void *inst) -{ - sdr_t *sdr = (sdr_t *)inst; - - PDEBUG(DSDR, DEBUG_DEBUG, "Close SDR device\n"); - - if (sdr->threads) { - if (sdr->thread_write.running) { - PDEBUG(DSDR, DEBUG_DEBUG, "Thread sending exit!\n"); - sdr->thread_write.running = 0; - while (sdr->thread_write.exit == 0) - usleep(1000); - } - if (sdr->thread_read.running) { - PDEBUG(DSDR, DEBUG_DEBUG, "Thread sending exit!\n"); - sdr->thread_read.running = 0; - while (sdr->thread_read.exit == 0) - usleep(1000); - } - } - - if (sdr->thread_read.buffer) - free((void *)sdr->thread_read.buffer); - if (sdr->thread_read.buffer2) - free((void *)sdr->thread_read.buffer2); - if (sdr->thread_write.buffer) - free((void *)sdr->thread_write.buffer); - if (sdr->thread_write.buffer2) - free((void *)sdr->thread_write.buffer2); - -#ifdef HAVE_UHD - if (sdr_config->uhd) - uhd_close(); -#endif - -#ifdef HAVE_SOAPY - if (sdr_config->soapy) - soapy_close(); -#endif - - if (sdr) { - free(sdr->modbuff); - free(sdr->modbuff_I); - free(sdr->modbuff_Q); - free(sdr->wavespl0); - free(sdr->wavespl1); - wave_destroy_record(&sdr->wave_rx_rec); - wave_destroy_record(&sdr->wave_tx_rec); - wave_destroy_playback(&sdr->wave_rx_play); - wave_destroy_playback(&sdr->wave_tx_play); - if (sdr->chan) { - int c; - - for (c = 0; c < sdr->channels; c++) { - fm_mod_exit(&sdr->chan[c].mod); - fm_demod_exit(&sdr->chan[c].demod); - } - if (sdr->paging_channel) - fm_mod_exit(&sdr->chan[sdr->paging_channel].mod); - free(sdr->chan); - } - free(sdr); - sdr = NULL; - } -} - -int sdr_write(void *inst, sample_t **samples, uint8_t **power, int num, enum paging_signal __attribute__((unused)) *paging_signal, int *on, int channels) -{ - sdr_t *sdr = (sdr_t *)inst; - float *buff = NULL; - int c, s, ss; - int sent = 0; - - if (num > sdr->latspl) { - fprintf(stderr, "exceeding maximum size given by sdr_latspl, please fix!\n"); - abort(); - } - if (channels != sdr->channels && channels != 0) { - PDEBUG(DSDR, DEBUG_ERROR, "Invalid number of channels, please fix!\n"); - abort(); - } - - /* process all channels */ - if (channels) { - buff = sdr->modbuff; - memset(buff, 0, sizeof(*buff) * num * 2); - for (c = 0; c < channels; c++) { - /* switch to paging channel, if requested */ - if (on[c] && sdr->paging_channel) - fm_modulate_complex(&sdr->chan[sdr->paging_channel].mod, samples[c], power[c], num, buff); - else - fm_modulate_complex(&sdr->chan[c].mod, samples[c], power[c], num, buff); - } - } else { - buff = (float *)samples; - } - - if (sdr->wave_tx_rec.fp) { - sample_t *spl_list[2] = { sdr->wavespl0, sdr->wavespl1 }; - for (s = 0, ss = 0; s < num; s++) { - spl_list[0][s] = buff[ss++]; - spl_list[1][s] = buff[ss++]; - } - wave_write(&sdr->wave_tx_rec, spl_list, num); - } - if (sdr->wave_tx_play.fp) { - sample_t *spl_list[2] = { sdr->wavespl0, sdr->wavespl1 }; - wave_read(&sdr->wave_tx_play, spl_list, num); - for (s = 0, ss = 0; s < num; s++) { - buff[ss++] = spl_list[0][s]; - buff[ss++] = spl_list[1][s]; - } - } - - if (sdr->threads) { - /* store data towards SDR in ring buffer */ - int space, in; - - space = (sdr->thread_write.out - sdr->thread_write.in - 2 + sdr->thread_write.buffer_size) % sdr->thread_write.buffer_size; - if (space < num * 2) { - PDEBUG(DSDR, DEBUG_ERROR, "Write SDR buffer overflow!\n"); - num = space / 2; - } -#ifdef DEBUG_BUFFER - printf("Writing %d samples to write buffer.\n", num); -#endif - in = sdr->thread_write.in; - for (s = 0, ss = 0; s < num; s++) { - sdr->thread_write.buffer[in++] = buff[ss++]; - sdr->thread_write.buffer[in++] = buff[ss++]; - in %= sdr->thread_write.buffer_size; - } - sdr->thread_write.in = in; - sent = num; - } else { -#ifdef HAVE_UHD - if (sdr_config->uhd) - sent = uhd_send(buff, num); -#endif -#ifdef HAVE_SOAPY - if (sdr_config->soapy) - sent = soapy_send(buff, num); -#endif - if (sent < 0) - return sent; - } - - return sent; -} - -int sdr_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db) -{ - sdr_t *sdr = (sdr_t *)inst; - float *buff = NULL; - int count = 0; - int c, s, ss; - - if (num > sdr->latspl) { - fprintf(stderr, "exceeding maximum size given by sdr_latspl, please fix!\n"); - abort(); - } - - if (channels) { - buff = sdr->modbuff; - } else { - buff = (float *)samples; - } - - if (sdr->threads) { - /* load data from SDR out of ring buffer */ - int fill, out; - - fill = (sdr->thread_read.in - sdr->thread_read.out + sdr->thread_read.buffer_size) % sdr->thread_read.buffer_size; - if (fill > sdr->thread_read.max_fill) - sdr->thread_read.max_fill = fill; - if (sdr->thread_read.max_fill_timer == 0.0) - sdr->thread_read.max_fill_timer = get_time(); - if (get_time() - sdr->thread_read.max_fill_timer > 1.0) { - double delay; - delay = (double)sdr->thread_read.max_fill / 2.0 / (double)sdr_config->samplerate; - sdr->thread_read.max_fill = 0; - sdr->thread_read.max_fill_timer += 1.0; - PDEBUG(DSDR, DEBUG_DEBUG, "read delay = %.3f ms\n", delay * 1000.0); - } - if (fill / 2 / sdr->oversample < num) - num = fill / 2 / sdr->oversample; -#ifdef DEBUG_BUFFER - printf("Reading %d samples from read buffer.\n", num); -#endif - out = sdr->thread_read.out; - for (s = 0, ss = 0; s < num; s++) { - buff[ss++] = sdr->thread_read.buffer[out]; - buff[ss++] = sdr->thread_read.buffer[out + 1]; - out = (out + 2 * sdr->oversample) % sdr->thread_read.buffer_size; - } - sdr->thread_read.out = out; - count = num; - } else { -#ifdef HAVE_UHD - if (sdr_config->uhd) - count = uhd_receive(buff, num); -#endif -#ifdef HAVE_SOAPY - if (sdr_config->soapy) - count = soapy_receive(buff, num); -#endif - if (count <= 0) - return count; - } - - if (sdr->wave_rx_rec.fp) { - sample_t *spl_list[2] = { sdr->wavespl0, sdr->wavespl1 }; - for (s = 0, ss = 0; s < count; s++) { - spl_list[0][s] = buff[ss++]; - spl_list[1][s] = buff[ss++]; - } - wave_write(&sdr->wave_rx_rec, spl_list, count); - } - if (sdr->wave_rx_play.fp) { - sample_t *spl_list[2] = { sdr->wavespl0, sdr->wavespl1 }; - wave_read(&sdr->wave_rx_play, spl_list, count); - for (s = 0, ss = 0; s < count; s++) { - buff[ss++] = spl_list[0][s]; - buff[ss++] = spl_list[1][s]; - } - } - display_iq(buff, count); - display_spectrum(buff, count); - - if (channels) { - for (c = 0; c < channels; c++) { - fm_demodulate_complex(&sdr->chan[c].demod, samples[c], count, buff, sdr->modbuff_I, sdr->modbuff_Q); - sender_t *sender = get_sender_by_empfangsfrequenz(sdr->chan[c].rx_frequency); - if (!sender || !count) - continue; - double min, max, avg; - avg = 0.0; - for (s = 0; s < count; s++) { - /* average the square length of vector */ - avg += sdr->modbuff_I[s] * sdr->modbuff_I[s] + sdr->modbuff_Q[s] * sdr->modbuff_Q[s]; - } - avg = sqrt(avg /(double)count); /* RMS */ - avg = log10(avg) * 20; - display_measurements_update(sdr->chan[c].dmp_rf_level, avg, 0.0); - rf_level_db[c] = avg; - min = 0.0; - max = 0.0; - avg = 0.0; - for (s = 0; s < count; s++) { - avg += samples[c][s]; - if (s == 0 || samples[c][s] > max) - max = samples[c][s]; - if (s == 0 || samples[c][s] < min) - min = samples[c][s]; - } - avg /= (double)count; - display_measurements_update(sdr->chan[c].dmp_freq_offset, avg / 1000.0, 0.0); - /* use half min and max, because we want the deviation above/below (+-) center frequency. */ - display_measurements_update(sdr->chan[c].dmp_deviation, min / 2.0 / 1000.0, max / 2.0 / 1000.0); - } - } - - return count; -} - -/* how much do we need to send (in audio sample duration) to get the target delay (latspl) */ -int sdr_get_tosend(void *inst, int latspl) -{ - sdr_t *sdr = (sdr_t *)inst; - int count = 0; - -#ifdef HAVE_UHD - if (sdr_config->uhd) - count = uhd_get_tosend(latspl * sdr->oversample); -#endif -#ifdef HAVE_SOAPY - if (sdr_config->soapy) - count = soapy_get_tosend(latspl * sdr->oversample); -#endif - if (count < 0) - return count; - count /= sdr->oversample; - - if (sdr->threads) { - /* substract what we have in write buffer, because this is not jent sent to the SDR */ - int fill; - - fill = (sdr->thread_write.in - sdr->thread_write.out + sdr->thread_write.buffer_size) % sdr->thread_write.buffer_size; - count -= fill / 2; - if (count < 0) - count = 0; - } - - return count; -} - - diff --git a/src/common/sdr.h b/src/common/sdr.h deleted file mode 100644 index 360f424..0000000 --- a/src/common/sdr.h +++ /dev/null @@ -1,8 +0,0 @@ - -int sdr_start(void *inst); -void *sdr_open(const char *audiodev, double *tx_frequency, double *rx_frequency, int channels, double paging_frequency, int samplerate, int latspl, double bandwidth, double sample_deviation); -void sdr_close(void *inst); -int sdr_write(void *inst, sample_t **samples, uint8_t **power, int num, enum paging_signal *paging_signal, int *on, int channels); -int sdr_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db); -int sdr_get_tosend(void *inst, int latspl); - diff --git a/src/common/sdr_config.c b/src/common/sdr_config.c deleted file mode 100644 index 4a49bae..0000000 --- a/src/common/sdr_config.c +++ /dev/null @@ -1,262 +0,0 @@ -/* Config for SDR - * - * (C) 2017 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -enum paging_signal; - -#include -#include -#include -#include -#include -#include "sample.h" -#include "sdr.h" -#include "sdr_config.h" - -static int got_init = 0; -extern int use_sdr; -sdr_config_t *sdr_config = NULL; - -void sdr_config_init(void) -{ - sdr_config = calloc(1, sizeof(*sdr_config)); - memset(sdr_config, 0, sizeof(*sdr_config)); - sdr_config->device_args = ""; - sdr_config->stream_args = ""; - sdr_config->tune_args = ""; - - got_init = 1; -} - -void sdr_config_print_help(void) -{ - printf("\nSDR options:\n"); - /* - - */ -#ifdef HAVE_UHD - printf(" --sdr-uhd\n"); - printf(" Force UHD driver\n"); -#endif -#ifdef HAVE_SOAPY - printf(" --sdr-soapy\n"); - printf(" Force SoapySDR driver\n"); -#endif - printf(" --sdr-channel \n"); - printf(" Give channel number for multi channel SDR device (default = %d)\n", sdr_config->channel); - printf(" --sdr-device-args \n"); - printf(" --sdr-stream-args \n"); - printf(" --sdr-tune-args \n"); - printf(" Optional SDR device arguments, seperated by comma\n"); - printf(" e.g. --sdr-device-args =[,=[,...]]\n"); - printf(" --sdr-samplerate \n"); - printf(" Sample rate to use with SDR. By default it equals the regular sample\n"); - printf(" rate.\n"); - printf(" --sdr-bandwidth \n"); - printf(" Give IF filter bandwidth to use. If not, sample rate is used.\n"); - printf(" --sdr-rx-antenna \n"); - printf(" SDR device's RX antenna name, use 'list' to get a list\n"); - printf(" --sdr-tx-antenna \n"); - printf(" SDR device's TX antenna name, use 'list' to get a list\n"); - printf(" --sdr-rx-gain \n"); - printf(" SDR device's RX gain in dB (default = %.1f)\n", sdr_config->rx_gain); - printf(" --sdr-tx-gain \n"); - printf(" SDR device's TX gain in dB (default = %.1f)\n", sdr_config->tx_gain); - printf(" --write-iq-rx-wave \n"); - printf(" Write received IQ data to given wave file.\n"); - printf(" --write-iq-tx-wave \n"); - printf(" Write transmitted IQ data to given wave file.\n"); - printf(" --read-iq-rx-wave \n"); - printf(" Replace received IQ data by given wave file.\n"); - printf(" --read-iq-tx-wave \n"); - printf(" Replace transmitted IQ data by given wave file.\n"); - printf(" --sdr-swap-links\n"); - printf(" Swap RX and TX frequencies for loopback tests over the air.\n"); -#ifdef HAVE_UHD - printf(" --sdr-uhd-tx-timestamps\n"); - printf(" Use TX timestamps on UHD device. (May not work with some devices!)\n"); -#endif -} - -void sdr_config_print_hotkeys(void) -{ - printf("Press 'q' key to toggle display of RX I/Q vector.\n"); - printf("Press 's' key to toggle display of RX spectrum.\n"); -} - -#define OPT_SDR_UHD 1500 -#define OPT_SDR_SOAPY 1501 -#define OPT_SDR_CHANNEL 1502 -#define OPT_SDR_DEVICE_ARGS 1503 -#define OPT_SDR_STREAM_ARGS 1504 -#define OPT_SDR_TUNE_ARGS 1505 -#define OPT_SDR_RX_ANTENNA 1506 -#define OPT_SDR_TX_ANTENNA 1507 -#define OPT_SDR_RX_GAIN 1508 -#define OPT_SDR_TX_GAIN 1509 -#define OPT_SDR_SAMPLERATE 1510 -#define OPT_SDR_BANDWIDTH 1511 -#define OPT_WRITE_IQ_RX_WAVE 1512 -#define OPT_WRITE_IQ_TX_WAVE 1513 -#define OPT_READ_IQ_RX_WAVE 1514 -#define OPT_READ_IQ_TX_WAVE 1515 -#define OPT_SDR_SWAP_LINKS 1516 -#define OPT_SDR_UHD_TX_TS 1517 - -struct option sdr_config_long_options[] = { - {"sdr-uhd", 0, 0, OPT_SDR_UHD}, - {"sdr-soapy", 0, 0, OPT_SDR_SOAPY}, - {"sdr-channel", 1, 0, OPT_SDR_CHANNEL}, - {"sdr-device-args", 1, 0, OPT_SDR_DEVICE_ARGS}, - {"sdr-stream-args", 1, 0, OPT_SDR_STREAM_ARGS}, - {"sdr-tune-args", 1, 0, OPT_SDR_TUNE_ARGS}, - {"sdr-samplerate", 1, 0, OPT_SDR_SAMPLERATE}, - {"sdr-bandwidth", 1, 0, OPT_SDR_BANDWIDTH}, - {"sdr-rx-antenna", 1, 0, OPT_SDR_RX_ANTENNA}, - {"sdr-tx-antenna", 1, 0, OPT_SDR_TX_ANTENNA}, - {"sdr-rx-gain", 1, 0, OPT_SDR_RX_GAIN}, - {"sdr-tx-gain", 1, 0, OPT_SDR_TX_GAIN}, - {"write-iq-rx-wave", 1, 0, OPT_WRITE_IQ_RX_WAVE}, - {"write-iq-tx-wave", 1, 0, OPT_WRITE_IQ_TX_WAVE}, - {"read-iq-rx-wave", 1, 0, OPT_READ_IQ_RX_WAVE}, - {"read-iq-tx-wave", 1, 0, OPT_READ_IQ_TX_WAVE}, - {"sdr-swap-links", 0, 0, OPT_SDR_SWAP_LINKS}, - {"sdr-uhd-tx-timestamps", 0, 0, OPT_SDR_UHD_TX_TS}, - {0, 0, 0, 0} -}; - -const char *sdr_config_optstring = ""; - -int sdr_config_opt_switch(int c, int *skip_args) -{ - switch (c) { - case OPT_SDR_UHD: -#ifdef HAVE_UHD - sdr_config->uhd = 1; - use_sdr = 1; -#else - fprintf(stderr, "UHD SDR support not compiled in!\n"); - exit(0); -#endif - *skip_args += 1; - break; - case OPT_SDR_SOAPY: -#ifdef HAVE_SOAPY - sdr_config->soapy = 1; - use_sdr = 1; -#else - fprintf(stderr, "SoapySDR support not compiled in!\n"); - exit(0); -#endif - *skip_args += 1; - break; - case OPT_SDR_CHANNEL: - sdr_config->channel = atoi(optarg); - *skip_args += 2; - break; - case OPT_SDR_DEVICE_ARGS: - sdr_config->device_args = strdup(optarg); - *skip_args += 2; - break; - case OPT_SDR_STREAM_ARGS: - sdr_config->stream_args = strdup(optarg); - *skip_args += 2; - break; - case OPT_SDR_TUNE_ARGS: - sdr_config->tune_args = strdup(optarg); - *skip_args += 2; - break; - case OPT_SDR_SAMPLERATE: - sdr_config->samplerate = atoi(optarg); - *skip_args += 2; - break; - case OPT_SDR_BANDWIDTH: - sdr_config->bandwidth = atof(optarg); - *skip_args += 2; - break; - case OPT_SDR_RX_ANTENNA: - sdr_config->rx_antenna = strdup(optarg); - *skip_args += 2; - break; - case OPT_SDR_TX_ANTENNA: - sdr_config->tx_antenna = strdup(optarg); - *skip_args += 2; - break; - case OPT_SDR_RX_GAIN: - sdr_config->rx_gain = atof(optarg); - *skip_args += 2; - break; - case OPT_SDR_TX_GAIN: - sdr_config->tx_gain = atof(optarg); - *skip_args += 2; - break; - case OPT_WRITE_IQ_RX_WAVE: - sdr_config->write_iq_rx_wave = strdup(optarg); - *skip_args += 2; - break; - case OPT_WRITE_IQ_TX_WAVE: - sdr_config->write_iq_tx_wave = strdup(optarg); - *skip_args += 2; - break; - case OPT_READ_IQ_RX_WAVE: - sdr_config->read_iq_rx_wave = strdup(optarg); - *skip_args += 2; - break; - case OPT_READ_IQ_TX_WAVE: - sdr_config->read_iq_tx_wave = strdup(optarg); - *skip_args += 2; - break; - case OPT_SDR_SWAP_LINKS: - sdr_config->swap_links = 1; - *skip_args += 1; - break; - case OPT_SDR_UHD_TX_TS: - sdr_config->uhd_tx_timestamps = 1; - *skip_args += 1; - break; - default: - return -1; - } - - return 0; -} - -int sdr_configure(int samplerate) -{ - if (!got_init) { - fprintf(stderr, "sdr_config_init was not called, please fix!\n"); - abort(); - } - - /* no sdr selected -> return 0 */ - if (!sdr_config->uhd && !sdr_config->soapy) - return 0; - - if ((sdr_config->uhd == 1 && sdr_config->soapy == 1)) { - fprintf(stderr, "You must choose which one you want: --sdr-uhd or --sdr-soapy\n"); - exit(0); - } - - if (sdr_config->samplerate == 0) - sdr_config->samplerate = samplerate; - if (sdr_config->bandwidth == 0.0) - sdr_config->bandwidth = (double)sdr_config->samplerate; - - /* sdr selected -> return 1 */ - return 1; -} - - diff --git a/src/common/sdr_config.h b/src/common/sdr_config.h deleted file mode 100644 index b64424d..0000000 --- a/src/common/sdr_config.h +++ /dev/null @@ -1,32 +0,0 @@ - -typedef struct sdr_config { - int uhd, /* select UHD API */ - soapy; /* select Soapy SDR API */ - int channel; /* channel number */ - const char *device_args, /* arguments */ - *stream_args, - *tune_args; - int samplerate; /* ADC/DAC sample rate */ - double bandwidth; /* IF bandwidth */ - double tx_gain, /* gain */ - rx_gain; - const char *tx_antenna, /* list/override antennas */ - *rx_antenna; - const char *write_iq_tx_wave; /* wave recording and playback */ - const char *write_iq_rx_wave; - const char *read_iq_tx_wave; - const char *read_iq_rx_wave; - int swap_links; /* swap DL and UL frequency */ - int uhd_tx_timestamps; /* use UHD time stamps */ -} sdr_config_t; - -extern sdr_config_t *sdr_config; - -void sdr_config_init(void); -void sdr_config_print_help(void); -void sdr_config_print_hotkeys(void); -extern struct option sdr_config_long_options[]; -extern const char *sdr_config_optstring; -int sdr_config_opt_switch(int c, int *skip_args); -int sdr_configure(int samplerate); - diff --git a/src/common/sender.h b/src/common/sender.h index 22a23a1..12a17c9 100644 --- a/src/common/sender.h +++ b/src/common/sender.h @@ -1,6 +1,6 @@ #include "../libsound/sound.h" #ifdef HAVE_SDR -#include "sdr.h" +#include "../libsdr/sdr.h" #endif #include "../libwave/wave.h" #include "../libsamplerate/samplerate.h" diff --git a/src/common/soapy.c b/src/common/soapy.c deleted file mode 100644 index e5d10de..0000000 --- a/src/common/soapy.c +++ /dev/null @@ -1,466 +0,0 @@ -/* SoapySDR device access - * - * (C) 2017 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "soapy.h" -#include "debug.h" - -static SoapySDRDevice *sdr = NULL; -SoapySDRStream *rxStream = NULL; -SoapySDRStream *txStream = NULL; -static int tx_samps_per_buff, rx_samps_per_buff; -static double samplerate; -static uint64_t rx_count = 0; -static uint64_t tx_count = 0; - -static int parse_args(SoapySDRKwargs *args, const char *_args_string) -{ - char *args_string = strdup(_args_string), *key, *val; - - memset(args, 0, sizeof(*args)); - while (args_string && *args_string) { - key = args_string; - val = strchr(key, '='); - if (!val) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Error parsing SDR args: No '=' after key\n"); - soapy_close(); - return -EIO; - } - *val++ = '\0'; - args_string = strchr(val, ','); - if (args_string) - *args_string++ = '\0'; - PDEBUG(DSOAPY, DEBUG_DEBUG, "SDR device args: key='%s' value='%s'\n", key, val); - SoapySDRKwargs_set(args, key, val); - } - - return 0; -} - -int soapy_open(size_t channel, const char *_device_args, const char *_stream_args, const char *_tune_args, const char *tx_antenna, const char *rx_antenna, double tx_frequency, double rx_frequency, double rate, double tx_gain, double rx_gain, double bandwidth) -{ - double got_frequency, got_rate, got_gain, got_bandwidth; - const char *got_antenna; - size_t num_channels; - SoapySDRKwargs device_args; - SoapySDRKwargs stream_args; - SoapySDRKwargs tune_args; - int rc; - - samplerate = rate; - - /* parsing ARGS */ - PDEBUG(DSOAPY, DEBUG_INFO, "Using device args \"%s\"\n", _device_args); - rc = parse_args(&device_args, _device_args); - if (rc < 0) - return rc; - PDEBUG(DSOAPY, DEBUG_INFO, "Using stream args \"%s\"\n", _stream_args); - rc = parse_args(&stream_args, _stream_args); - if (rc < 0) - return rc; - PDEBUG(DSOAPY, DEBUG_INFO, "Using tune args \"%s\"\n", _tune_args); - rc = parse_args(&tune_args, _tune_args); - if (rc < 0) - return rc; - - /* create SoapySDR device */ - sdr = SoapySDRDevice_make(&device_args); - if (!sdr) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to create SoapySDR\n"); - soapy_close(); - return -EIO; - } - - if (tx_frequency) { - /* get number of channels and check if requested channel is in range */ - num_channels = SoapySDRDevice_getNumChannels(sdr, SOAPY_SDR_TX); - PDEBUG(DSOAPY, DEBUG_DEBUG, "We have %d TX channel, selecting channel #%d\n", (int)num_channels, (int)channel); - if (channel >= num_channels) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Requested channel #%d (capable of TX) does not exist. Please select channel %d..%d!\n", (int)channel, 0, (int)num_channels - 1); - soapy_close(); - return -EIO; - } - - /* antenna */ - if (tx_antenna && tx_antenna[0]) { - if (!strcasecmp(tx_antenna, "list")) { - char **antennas; - size_t antennas_length; - int i; - antennas = SoapySDRDevice_listAntennas(sdr, SOAPY_SDR_TX, channel, &antennas_length); - if (!antennas) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to request list of TX antennas!\n"); - soapy_close(); - return -EIO; - } - for (i = 0; i < (int)antennas_length; i++) - PDEBUG(DSOAPY, DEBUG_NOTICE, "TX Antenna: '%s'\n", antennas[i]); - got_antenna = SoapySDRDevice_getAntenna(sdr, SOAPY_SDR_TX, channel); - PDEBUG(DSOAPY, DEBUG_NOTICE, "Default TX Antenna: '%s'\n", got_antenna); - soapy_close(); - return 1; - } - - if (SoapySDRDevice_setAntenna(sdr, SOAPY_SDR_TX, channel, tx_antenna) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX antenna to '%s'\n", tx_antenna); - soapy_close(); - return -EIO; - } - got_antenna = SoapySDRDevice_getAntenna(sdr, SOAPY_SDR_TX, channel); - if (!!strcasecmp(tx_antenna, got_antenna)) { - PDEBUG(DSOAPY, DEBUG_NOTICE, "Given TX antenna '%s' was accepted, but driver claims to use '%s'\n", tx_antenna, got_antenna); - soapy_close(); - return -EINVAL; - } - } - - /* set rate */ - if (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_TX, channel, rate) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX rate to %.0f Hz\n", rate); - soapy_close(); - return -EIO; - } - - /* see what rate actually is */ - got_rate = SoapySDRDevice_getSampleRate(sdr, SOAPY_SDR_TX, channel); - if (fabs(got_rate - rate) > 1.0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Given TX rate %.3f Hz is not supported, try %.3f Hz\n", rate, got_rate); - soapy_close(); - return -EINVAL; - } - - if (tx_gain) { - /* set gain */ - if (SoapySDRDevice_setGain(sdr, SOAPY_SDR_TX, channel, tx_gain) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX gain to %.0f\n", tx_gain); - soapy_close(); - return -EIO; - } - - /* see what gain actually is */ - got_gain = SoapySDRDevice_getGain(sdr, SOAPY_SDR_TX, channel); - if (fabs(got_gain - tx_gain) > 0.001) { - PDEBUG(DSOAPY, DEBUG_NOTICE, "Given TX gain %.3f is not supported, we use %.3f\n", tx_gain, got_gain); - tx_gain = got_gain; - } - } - - /* set frequency */ - if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_TX, channel, tx_frequency, &tune_args) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX frequency to %.0f Hz\n", tx_frequency); - soapy_close(); - return -EIO; - } - - /* see what frequency actually is */ - got_frequency = SoapySDRDevice_getFrequency(sdr, SOAPY_SDR_TX, channel); - if (fabs(got_frequency - tx_frequency) > 100.0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Given TX frequency %.0f Hz is not supported, try %.0f Hz\n", tx_frequency, got_frequency); - soapy_close(); - return -EINVAL; - } - - /* set bandwidth */ - if (SoapySDRDevice_setBandwidth(sdr, SOAPY_SDR_TX, channel, bandwidth) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX bandwidth to %.0f Hz\n", bandwidth); - soapy_close(); - return -EIO; - } - - /* see what bandwidth actually is */ - got_bandwidth = SoapySDRDevice_getBandwidth(sdr, SOAPY_SDR_TX, channel); - if (fabs(got_bandwidth - bandwidth) > 100.0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Given TX bandwidth %.0f Hz is not supported, try %.0f Hz\n", bandwidth, got_bandwidth); - soapy_close(); - return -EINVAL; - } - - /* set up streamer */ - if (SoapySDRDevice_setupStream(sdr, &txStream, SOAPY_SDR_TX, SOAPY_SDR_CF32, &channel, 1, &stream_args) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX streamer args\n"); - soapy_close(); - return -EIO; - } - - /* get buffer sizes */ - tx_samps_per_buff = SoapySDRDevice_getStreamMTU(sdr, txStream); - if (tx_samps_per_buff == 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to get TX streamer sample buffer\n"); - soapy_close(); - return -EIO; - } - } - - if (rx_frequency) { - /* get number of channels and check if requested channel is in range */ - num_channels = SoapySDRDevice_getNumChannels(sdr, SOAPY_SDR_RX); - PDEBUG(DSOAPY, DEBUG_DEBUG, "We have %d RX channel, selecting channel #%d\n", (int)num_channels, (int)channel); - if (channel >= num_channels) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Requested channel #%d (capable of RX) does not exist. Please select channel %d..%d!\n", (int)channel, 0, (int)num_channels - 1); - soapy_close(); - return -EIO; - } - - /* antenna */ - if (rx_antenna && rx_antenna[0]) { - if (!strcasecmp(rx_antenna, "list")) { - char **antennas; - size_t antennas_length; - int i; - antennas = SoapySDRDevice_listAntennas(sdr, SOAPY_SDR_RX, channel, &antennas_length); - if (!antennas) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to request list of RX antennas!\n"); - soapy_close(); - return -EIO; - } - for (i = 0; i < (int)antennas_length; i++) - PDEBUG(DSOAPY, DEBUG_NOTICE, "RX Antenna: '%s'\n", antennas[i]); - got_antenna = SoapySDRDevice_getAntenna(sdr, SOAPY_SDR_RX, channel); - PDEBUG(DSOAPY, DEBUG_NOTICE, "Default RX Antenna: '%s'\n", got_antenna); - soapy_close(); - return 1; - } - - if (SoapySDRDevice_setAntenna(sdr, SOAPY_SDR_RX, channel, rx_antenna) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX antenna to '%s'\n", rx_antenna); - soapy_close(); - return -EIO; - } - got_antenna = SoapySDRDevice_getAntenna(sdr, SOAPY_SDR_RX, channel); - if (!!strcasecmp(rx_antenna, got_antenna)) { - PDEBUG(DSOAPY, DEBUG_NOTICE, "Given RX antenna '%s' was accepted, but driver claims to use '%s'\n", rx_antenna, got_antenna); - soapy_close(); - return -EINVAL; - } - } - - /* set rate */ - if (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_RX, channel, rate) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX rate to %.0f Hz\n", rate); - soapy_close(); - return -EIO; - } - - /* see what rate actually is */ - got_rate = SoapySDRDevice_getSampleRate(sdr, SOAPY_SDR_RX, channel); - if (fabs(got_rate - rate) > 1.0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Given RX rate %.3f Hz is not supported, try %.3f Hz\n", rate, got_rate); - soapy_close(); - return -EINVAL; - } - - if (rx_gain) { - /* set gain */ - if (SoapySDRDevice_setGain(sdr, SOAPY_SDR_RX, channel, rx_gain) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX gain to %.0f\n", rx_gain); - soapy_close(); - return -EIO; - } - - /* see what gain actually is */ - got_gain = SoapySDRDevice_getGain(sdr, SOAPY_SDR_RX, channel); - if (fabs(got_gain - rx_gain) > 0.001) { - PDEBUG(DSOAPY, DEBUG_NOTICE, "Given RX gain %.3f is not supported, we use %.3f\n", rx_gain, got_gain); - rx_gain = got_gain; - } - } - - /* set frequency */ - if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_RX, channel, rx_frequency, &tune_args) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX frequency to %.0f Hz\n", rx_frequency); - soapy_close(); - return -EIO; - } - - /* see what frequency actually is */ - got_frequency = SoapySDRDevice_getFrequency(sdr, SOAPY_SDR_RX, channel); - if (fabs(got_frequency - rx_frequency) > 100.0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Given RX frequency %.0f Hz is not supported, try %.0f Hz\n", rx_frequency, got_frequency); - soapy_close(); - return -EINVAL; - } - - /* set bandwidth */ - if (SoapySDRDevice_setBandwidth(sdr, SOAPY_SDR_RX, channel, bandwidth) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX bandwidth to %.0f Hz\n", bandwidth); - soapy_close(); - return -EIO; - } - - /* see what bandwidth actually is */ - got_bandwidth = SoapySDRDevice_getBandwidth(sdr, SOAPY_SDR_RX, channel); - if (fabs(got_bandwidth - bandwidth) > 100.0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Given RX bandwidth %.0f Hz is not supported, try %.0f Hz\n", bandwidth, got_bandwidth); - soapy_close(); - return -EINVAL; - } - - /* set up streamer */ - if (SoapySDRDevice_setupStream(sdr, &rxStream, SOAPY_SDR_RX, SOAPY_SDR_CF32, &channel, 1, &stream_args) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX streamer args\n"); - soapy_close(); - return -EIO; - } - - /* get buffer sizes */ - rx_samps_per_buff = SoapySDRDevice_getStreamMTU(sdr, rxStream); - if (rx_samps_per_buff == 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to get RX streamer sample buffer\n"); - soapy_close(); - return -EIO; - } - } - - return 0; -} - -/* start streaming */ -int soapy_start(void) -{ - /* enable rx stream */ - if (SoapySDRDevice_activateStream(sdr, rxStream, 0, 0, 0) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to issue RX stream command\n"); - return -EIO; - } - - /* enable tx stream */ - if (SoapySDRDevice_activateStream(sdr, txStream, 0, 0, 0) != 0) { - PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to issue TX stream command\n"); - return -EIO; - } - return 0; -} - -void soapy_close(void) -{ - PDEBUG(DSOAPY, DEBUG_DEBUG, "Clean up SoapySDR\n"); - if (txStream) { - SoapySDRDevice_deactivateStream(sdr, txStream, 0, 0); - SoapySDRDevice_closeStream(sdr, txStream); - txStream = NULL; - } - if (rxStream) { - SoapySDRDevice_deactivateStream(sdr, rxStream, 0, 0); - SoapySDRDevice_closeStream(sdr, rxStream); - rxStream = NULL; - } - if (sdr) { - SoapySDRDevice_unmake(sdr); - sdr = NULL; - } -} - -int soapy_send(float *buff, int num) -{ - const void *buffs_ptr[1]; - int chunk; - int sent = 0, count; - int flags = 0; - - while (num) { - chunk = num; - if (chunk > tx_samps_per_buff) - chunk = tx_samps_per_buff; - /* create tx metadata */ - buffs_ptr[0] = buff; - count = SoapySDRDevice_writeStream(sdr, txStream, buffs_ptr, chunk, &flags, 0, 1000000); - if (count <= 0) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to write to TX streamer (error=%d)\n", count); - break; - } - - sent += count; - buff += count * 2; - num -= count; - } - /* increment tx counter */ - tx_count += sent; - - return sent; -} - -/* read what we got, return 0, if buffer is empty, otherwise return the number of samples */ -int soapy_receive(float *buff, int max) -{ - void *buffs_ptr[1]; - int got = 0, count; - long long timeNs; - int flags = 0; - - while (1) { - if (max < rx_samps_per_buff) { - /* no more space this time */ - PDEBUG(DSOAPY, DEBUG_ERROR, "SDR RX overflow!\n"); - break; - } - /* read RX stream */ - buffs_ptr[0] = buff; - count = SoapySDRDevice_readStream(sdr, rxStream, buffs_ptr, rx_samps_per_buff, &flags, &timeNs, 0); - if (count > 0) { - /* commit received data to buffer */ - got += count; - buff += count * 2; - max -= count; - } else { - /* got nothing this time */ - break; - } - } - /* update current rx time */ - rx_count += got; - - return got; -} - -/* estimate number of samples that can be sent */ -int soapy_get_tosend(int latspl) -{ - int tosend; - - /* we need the rx time stamp to determine how much data is already sent in advance */ - if (rx_count == 0) - return 0; - - /* if we have not yet sent any data, we set initial tx time stamp */ - if (tx_count == 0) - tx_count = rx_count; - - /* we check how advance our transmitted time stamp is */ - tosend = latspl - (tx_count - rx_count); - /* in case of underrun: */ - if (tosend > latspl) { -// It is normal that we have underruns, prior inital filling of buffer. -// FIXME: better solution to detect underrun -// PDEBUG(DSOAPY, DEBUG_ERROR, "SDR TX underrun!\n"); - tosend = 0; - tx_count = rx_count; - } - if (tosend < 0) - tosend = 0; - - return tosend; -} - diff --git a/src/common/soapy.h b/src/common/soapy.h deleted file mode 100644 index 702890b..0000000 --- a/src/common/soapy.h +++ /dev/null @@ -1,8 +0,0 @@ - -int soapy_open(size_t channel, const char *_device_args, const char *_stream_args, const char *_tune_args, const char *tx_antenna, const char *rx_antenna, double tx_frequency, double rx_frequency, double rate, double tx_gain, double rx_gain, double bandwidth); -int soapy_start(void); -void soapy_close(void); -int soapy_send(float *buff, int num); -int soapy_receive(float *buff, int max); -int soapy_get_tosend(int latspl); - diff --git a/src/common/uhd.c b/src/common/uhd.c deleted file mode 100644 index e79b1f7..0000000 --- a/src/common/uhd.c +++ /dev/null @@ -1,583 +0,0 @@ -/* UHD device access - * - * (C) 2017 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include "uhd.h" -#include "debug.h" - -/* use to TX time stamp */ -//#define TX_TIMESTAMP - -static uhd_usrp_handle usrp = NULL; -static uhd_tx_streamer_handle tx_streamer = NULL; -static uhd_rx_streamer_handle rx_streamer = NULL; -static uhd_tx_metadata_handle tx_metadata = NULL; -static uhd_rx_metadata_handle rx_metadata = NULL; -static uhd_tune_request_t tune_request; -static uhd_tune_result_t tune_result; -static uhd_stream_args_t stream_args; -static uhd_stream_cmd_t stream_cmd; -static size_t tx_samps_per_buff, rx_samps_per_buff; -static double samplerate; -static time_t rx_time_secs = 0; -static double rx_time_fract_sec = 0.0; -static time_t tx_time_secs = 0; -static double tx_time_fract_sec = 0.0; -static int tx_timestamps; - -int uhd_open(size_t channel, const char *_device_args, const char *_stream_args, const char *_tune_args, const char *tx_antenna, const char *rx_antenna, double tx_frequency, double rx_frequency, double rate, double tx_gain, double rx_gain, double bandwidth, int _tx_timestamps) -{ - uhd_error error; - double got_frequency, got_rate, got_gain, got_bandwidth; - char got_antenna[64]; - - samplerate = rate; - tx_timestamps = _tx_timestamps; - - PDEBUG(DUHD, DEBUG_INFO, "Using device args \"%s\"\n", _device_args); - PDEBUG(DUHD, DEBUG_INFO, "Using stream args \"%s\"\n", _stream_args); - PDEBUG(DUHD, DEBUG_INFO, "Using tune args \"%s\"\n", _tune_args); - - /* create USRP */ - PDEBUG(DUHD, DEBUG_INFO, "Creating USRP with args \"%s\"...\n", _device_args); - error = uhd_usrp_make(&usrp, _device_args); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to create USRP\n"); - uhd_close(); - return -EIO; - } - - if (tx_frequency) { - /* antenna */ - if (tx_antenna && tx_antenna[0]) { - if (!strcasecmp(tx_antenna, "list")) { - uhd_string_vector_handle antennas; - size_t antennas_length; - int i; - error = uhd_string_vector_make(&antennas); - if (error) { - tx_vector_error: - PDEBUG(DUHD, DEBUG_ERROR, "Failed to hande UHD vector, please fix!\n"); - uhd_close(); - return -EIO; - } - error = uhd_usrp_get_tx_antennas(usrp, channel, &antennas); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to request list of TX antennas!\n"); - uhd_close(); - return -EIO; - } - error = uhd_string_vector_size(antennas, &antennas_length); - if (error) - goto tx_vector_error; - for (i = 0; i < (int)antennas_length; i++) { - error = uhd_string_vector_at(antennas, i, got_antenna, sizeof(got_antenna)); - if (error) - goto tx_vector_error; - PDEBUG(DUHD, DEBUG_NOTICE, "TX Antenna: '%s'\n", got_antenna); - } - uhd_string_vector_free(&antennas); - error = uhd_usrp_get_tx_antenna(usrp, channel, got_antenna, sizeof(got_antenna)); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX antenna\n"); - uhd_close(); - return -EINVAL; - } - PDEBUG(DUHD, DEBUG_NOTICE, "Default TX Antenna: '%s'\n", got_antenna); - uhd_close(); - return 1; - } - error = uhd_usrp_set_tx_antenna(usrp, tx_antenna, channel); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX antenna to '%s'\n", tx_antenna); - uhd_close(); - return -EIO; - } - error = uhd_usrp_get_tx_antenna(usrp, channel, got_antenna, sizeof(got_antenna)); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX antenna\n"); - uhd_close(); - return -EINVAL; - } - if (!!strcasecmp(tx_antenna, got_antenna)) { - PDEBUG(DUHD, DEBUG_NOTICE, "Given TX antenna '%s' was accepted, but driver claims to use '%s'\n", tx_antenna, got_antenna); - uhd_close(); - return -EINVAL; - } - } - - /* create streamers */ - error = uhd_tx_streamer_make(&tx_streamer); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to create TX streamer\n"); - uhd_close(); - return -EIO; - } - - /* set rate */ - error = uhd_usrp_set_tx_rate(usrp, rate, channel); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX rate to %.0f Hz\n", rate); - uhd_close(); - return -EIO; - } - - /* see what rate actually is */ - error = uhd_usrp_get_tx_rate(usrp, channel, &got_rate); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX rate\n"); - uhd_close(); - return -EIO; - } - if (fabs(got_rate - rate) > 0.001) { - PDEBUG(DUHD, DEBUG_ERROR, "Given TX rate %.0f Hz is not supported, try %.0f Hz\n", rate, got_rate); - uhd_close(); - return -EINVAL; - } - - /* set gain */ - error = uhd_usrp_set_tx_gain(usrp, tx_gain, channel, ""); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX gain to %.0f\n", tx_gain); - uhd_close(); - return -EIO; - } - - /* see what gain actually is */ - error = uhd_usrp_get_tx_gain(usrp, channel, "", &got_gain); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX gain\n"); - uhd_close(); - return -EIO; - } - if (fabs(got_gain - tx_gain) > 0.001) { - PDEBUG(DUHD, DEBUG_NOTICE, "Given TX gain %.0f is not supported, we use %.0f\n", tx_gain, got_gain); - tx_gain = got_gain; - } - - /* set frequency */ - memset(&tune_request, 0, sizeof(tune_request)); - tune_request.target_freq = tx_frequency; - tune_request.rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO; - tune_request.dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO; - tune_request.args = strdup(_tune_args); - error = uhd_usrp_set_tx_freq(usrp, &tune_request, channel, &tune_result); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX frequeny to %.0f Hz\n", tx_frequency); - uhd_close(); - return -EIO; - } - - /* see what frequency actually is */ - error = uhd_usrp_get_tx_freq(usrp, channel, &got_frequency); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX frequency\n"); - uhd_close(); - return -EIO; - } - if (fabs(got_frequency - tx_frequency) > 100.0) { - PDEBUG(DUHD, DEBUG_ERROR, "Given TX frequency %.0f Hz is not supported, try %.0f Hz\n", tx_frequency, got_frequency); - uhd_close(); - return -EINVAL; - } - - /* set bandwidth */ - if (uhd_usrp_set_tx_bandwidth(usrp, bandwidth, channel) != 0) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX bandwidth to %.0f Hz\n", bandwidth); - uhd_close(); - return -EIO; - } - - /* see what bandwidth actually is */ - error = uhd_usrp_get_tx_bandwidth(usrp, channel, &got_bandwidth); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX bandwidth\n"); - uhd_close(); - return -EIO; - } - if (fabs(got_bandwidth - bandwidth) > 0.001) { - PDEBUG(DUHD, DEBUG_ERROR, "Given TX bandwidth %.0f Hz is not supported, try %.0f Hz\n", bandwidth, got_bandwidth); - uhd_close(); - return -EINVAL; - } - - /* set up streamer */ - memset(&stream_args, 0, sizeof(stream_args)); - stream_args.cpu_format = "fc32"; - stream_args.otw_format = "sc16"; - stream_args.args = strdup(_stream_args); - stream_args.channel_list = &channel; - stream_args.n_channels = 1; - error = uhd_usrp_get_tx_stream(usrp, &stream_args, tx_streamer); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX streamer args\n"); - uhd_close(); - return -EIO; - } - - /* get buffer sizes */ - error = uhd_tx_streamer_max_num_samps(tx_streamer, &tx_samps_per_buff); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX streamer sample buffer\n"); - uhd_close(); - return -EIO; - } - } - - if (rx_frequency) { - /* antenna */ - if (rx_antenna && rx_antenna[0]) { - if (!strcasecmp(rx_antenna, "list")) { - uhd_string_vector_handle antennas; - size_t antennas_length; - int i; - error = uhd_string_vector_make(&antennas); - if (error) { - rx_vector_error: - PDEBUG(DUHD, DEBUG_ERROR, "Failed to hande UHD vector, please fix!\n"); - uhd_close(); - return -EIO; - } - error = uhd_usrp_get_rx_antennas(usrp, channel, &antennas); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to request list of RX antennas!\n"); - uhd_close(); - return -EIO; - } - error = uhd_string_vector_size(antennas, &antennas_length); - if (error) - goto rx_vector_error; - for (i = 0; i < (int)antennas_length; i++) { - error = uhd_string_vector_at(antennas, i, got_antenna, sizeof(got_antenna)); - if (error) - goto rx_vector_error; - PDEBUG(DUHD, DEBUG_NOTICE, "RX Antenna: '%s'\n", got_antenna); - } - uhd_string_vector_free(&antennas); - error = uhd_usrp_get_rx_antenna(usrp, channel, got_antenna, sizeof(got_antenna)); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX antenna\n"); - uhd_close(); - return -EINVAL; - } - PDEBUG(DUHD, DEBUG_NOTICE, "Default RX Antenna: '%s'\n", got_antenna); - uhd_close(); - return 1; - } - error = uhd_usrp_set_rx_antenna(usrp, rx_antenna, channel); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX antenna to '%s'\n", rx_antenna); - uhd_close(); - return -EIO; - } - error = uhd_usrp_get_rx_antenna(usrp, channel, got_antenna, sizeof(got_antenna)); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX antenna\n"); - uhd_close(); - return -EINVAL; - } - if (!!strcasecmp(rx_antenna, got_antenna)) { - PDEBUG(DUHD, DEBUG_NOTICE, "Given RX antenna '%s' was accepted, but driver claims to use '%s'\n", rx_antenna, got_antenna); - uhd_close(); - return -EINVAL; - } - } - /* create streamers */ - error = uhd_rx_streamer_make(&rx_streamer); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to create RX streamer\n"); - uhd_close(); - return -EIO; - } - - /* create metadata */ - error = uhd_rx_metadata_make(&rx_metadata); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to create RX metadata\n"); - uhd_close(); - return -EIO; - } - - /* set rate */ - error = uhd_usrp_set_rx_rate(usrp, rate, channel); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX rate to %.0f Hz\n", rate); - uhd_close(); - return -EIO; - } - - /* see what rate actually is */ - error = uhd_usrp_get_rx_rate(usrp, channel, &got_rate); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX rate\n"); - uhd_close(); - return -EIO; - } - if (fabs(got_rate - rate) > 0.001) { - PDEBUG(DUHD, DEBUG_ERROR, "Given RX rate %.0f Hz is not supported, try %.0f Hz\n", rate, got_rate); - uhd_close(); - return -EINVAL; - } - - /* set gain */ - error = uhd_usrp_set_rx_gain(usrp, rx_gain, channel, ""); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX gain to %.0f\n", rx_gain); - uhd_close(); - return -EIO; - } - - /* see what gain actually is */ - error = uhd_usrp_get_rx_gain(usrp, channel, "", &got_gain); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX gain\n"); - uhd_close(); - return -EIO; - } - if (fabs(got_gain - rx_gain) > 0.001) { - PDEBUG(DUHD, DEBUG_NOTICE, "Given RX gain %.3f is not supported, we use %.3f\n", rx_gain, got_gain); - rx_gain = got_gain; - } - - /* set frequency */ - memset(&tune_request, 0, sizeof(tune_request)); - tune_request.target_freq = rx_frequency; - tune_request.rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO; - tune_request.dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO; - tune_request.args = strdup(_tune_args); - error = uhd_usrp_set_rx_freq(usrp, &tune_request, channel, &tune_result); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX frequeny to %.0f Hz\n", rx_frequency); - uhd_close(); - return -EIO; - } - - /* see what frequency actually is */ - error = uhd_usrp_get_rx_freq(usrp, channel, &got_frequency); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX frequency\n"); - uhd_close(); - return -EIO; - } - if (fabs(got_frequency - rx_frequency) > 100.0) { - PDEBUG(DUHD, DEBUG_ERROR, "Given RX frequency %.0f Hz is not supported, try %.0f Hz\n", rx_frequency, got_frequency); - uhd_close(); - return -EINVAL; - } - - /* set bandwidth */ - if (uhd_usrp_set_rx_bandwidth(usrp, bandwidth, channel) != 0) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX bandwidth to %.0f Hz\n", bandwidth); - uhd_close(); - return -EIO; - } - - /* see what bandwidth actually is */ - error = uhd_usrp_get_rx_bandwidth(usrp, channel, &got_bandwidth); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX bandwidth\n"); - uhd_close(); - return -EIO; - } - if (fabs(got_bandwidth - bandwidth) > 0.001) { - PDEBUG(DUHD, DEBUG_ERROR, "Given RX bandwidth %.0f Hz is not supported, try %.0f Hz\n", bandwidth, got_bandwidth); - uhd_close(); - return -EINVAL; - } - - /* set up streamer */ - memset(&stream_args, 0, sizeof(stream_args)); - stream_args.cpu_format = "fc32"; - stream_args.otw_format = "sc16"; - stream_args.args = strdup(_stream_args); - stream_args.channel_list = &channel; - stream_args.n_channels = 1; - error = uhd_usrp_get_rx_stream(usrp, &stream_args, rx_streamer); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX streamer args\n"); - uhd_close(); - return -EIO; - } - - /* get buffer sizes */ - error = uhd_rx_streamer_max_num_samps(rx_streamer, &rx_samps_per_buff); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX streamer sample buffer\n"); - uhd_close(); - return -EIO; - } - } - - return 0; -} - -/* start streaming */ -int uhd_start(void) -{ - uhd_error error; - - /* enable rx stream */ - memset(&stream_cmd, 0, sizeof(stream_cmd)); - stream_cmd.stream_mode = UHD_STREAM_MODE_START_CONTINUOUS; - stream_cmd.stream_now = true; - error = uhd_rx_streamer_issue_stream_cmd(rx_streamer, &stream_cmd); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to issue RX stream command\n"); - return -EIO; - } - return 0; -} - -void uhd_close(void) -{ - PDEBUG(DUHD, DEBUG_DEBUG, "Clean up UHD\n"); - if (tx_metadata) - uhd_tx_metadata_free(&tx_metadata); - if (rx_metadata) - uhd_rx_metadata_free(&rx_metadata); - if (tx_streamer) - uhd_tx_streamer_free(&tx_streamer); - if (rx_streamer) - uhd_rx_streamer_free(&rx_streamer); - if (usrp) - uhd_usrp_free(&usrp); -} - -int uhd_send(float *buff, int num) -{ - const void *buffs_ptr[1]; - int chunk; - size_t sent = 0, count; - uhd_error error; - - while (num) { - chunk = num; - if (chunk > (int)tx_samps_per_buff) - chunk = (int)tx_samps_per_buff; - /* create tx metadata */ - if (tx_timestamps) - error = uhd_tx_metadata_make(&tx_metadata, true, tx_time_secs, tx_time_fract_sec, false, false); - else - error = uhd_tx_metadata_make(&tx_metadata, false, 0, 0.0, false, false); - if (error) - PDEBUG(DUHD, DEBUG_ERROR, "Failed to create TX metadata\n"); - buffs_ptr[0] = buff; - count = 0; - error = uhd_tx_streamer_send(tx_streamer, buffs_ptr, chunk, &tx_metadata, 1.0, &count); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to write to TX streamer\n"); - break; - } - if (count == 0) - break; - - /* increment time stamp */ - tx_time_fract_sec += (double)count / samplerate; - while (tx_time_fract_sec >= 1.0) { - tx_time_secs++; - tx_time_fract_sec -= 1.0; - } -//printf("adv=%.3f\n", ((double)tx_time_secs + tx_time_fract_sec) - ((double)rx_time_secs + rx_time_fract_sec)); - - sent += count; - buff += count * 2; - num -= count; - } - - return sent; -} - -/* read what we got, return 0, if buffer is empty, otherwise return the number of samples */ -int uhd_receive(float *buff, int max) -{ - void *buffs_ptr[1]; - size_t got = 0, count; - uhd_error error; - - while (1) { - if (max < (int)rx_samps_per_buff) { - /* no more space this time */ - PDEBUG(DUHD, DEBUG_ERROR, "SDR RX overflow!\n"); - break; - } - /* read RX stream */ - buffs_ptr[0] = buff; - count = 0; - error = uhd_rx_streamer_recv(rx_streamer, buffs_ptr, rx_samps_per_buff, &rx_metadata, 0.0, false, &count); - if (error) { - PDEBUG(DUHD, DEBUG_ERROR, "Failed to read from UHD device.\n"); - break; - } - if (count) { - /* get time stamp of received RX packet */ - uhd_rx_metadata_time_spec(rx_metadata, &rx_time_secs, &rx_time_fract_sec); - /* commit received data to buffer */ - got += count; - buff += count * 2; - max -= count; - } else { - /* got nothing this time */ - break; - } - } - - return got; -} - -/* estimate number of samples that can be sent */ -int uhd_get_tosend(int latspl) -{ - double advance; - int tosend; - - /* we need the rx time stamp to determine how much data is already sent in advance */ - if (rx_time_secs == 0 && rx_time_fract_sec == 0.0) - return 0; - - /* if we have not yet sent any data, we set initial tx time stamp */ - if (tx_time_secs == 0 && tx_time_fract_sec == 0.0) { - tx_time_secs = rx_time_secs; - tx_time_fract_sec = rx_time_fract_sec; - if (tx_timestamps) { - tx_time_fract_sec += (double)latspl / samplerate; - if (tx_time_fract_sec >= 1.0) { - tx_time_fract_sec -= 1.0; - tx_time_secs++; - } - } - } - - /* we check how advance our transmitted time stamp is */ - advance = ((double)tx_time_secs + tx_time_fract_sec) - ((double)rx_time_secs + rx_time_fract_sec); - /* in case of underrun: */ - if (advance < 0) - advance = 0; - tosend = latspl - (int)(advance * samplerate); - if (tosend < 0) - tosend = 0; - - return tosend; -} - diff --git a/src/common/uhd.h b/src/common/uhd.h deleted file mode 100644 index 60eb94f..0000000 --- a/src/common/uhd.h +++ /dev/null @@ -1,8 +0,0 @@ - -int uhd_open(size_t channel, const char *_device_args, const char *_stream_args, const char *_tune_args, const char *tx_antenna, const char *rx_antenna, double tx_frequency, double rx_frequency, double rate, double tx_gain, double rx_gain, double bandwidth, int _tx_timestamps); -int uhd_start(void); -void uhd_close(void); -int uhd_send(float *buff, int num); -int uhd_receive(float *buff, int max); -int uhd_get_tosend(int latspl); - diff --git a/src/jtacs/Makefile.am b/src/jtacs/Makefile.am index 61e7a44..c6a231e 100644 --- a/src/jtacs/Makefile.am +++ b/src/jtacs/Makefile.am @@ -20,10 +20,8 @@ jtacs_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -31,3 +29,10 @@ jtacs_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +jtacs_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + diff --git a/src/libimage/Makefile.am b/src/libimage/Makefile.am index 7805bbe..ed9041c 100644 --- a/src/libimage/Makefile.am +++ b/src/libimage/Makefile.am @@ -1,4 +1,4 @@ -AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) +AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) $(GRAPHICSMAGICK_CFLAGS) $(IMAGEMAGICK_CFLAGS) noinst_LIBRARIES = libimage.a diff --git a/src/libsdr/Makefile.am b/src/libsdr/Makefile.am new file mode 100644 index 0000000..5a00457 --- /dev/null +++ b/src/libsdr/Makefile.am @@ -0,0 +1,26 @@ +AM_CPPFLAGS = -Wall -Wextra -g $(all_includes) + +noinst_LIBRARIES = libsdr.a + +libsdr_a_SOURCES = \ +dd sdr_config.c \ + sdr.c \ + display_iq.c \ + display_spectrum.c + +AM_CPPFLAGS += -DHAVE_SDR + +if HAVE_UHD +AM_CPPFLAGS += -DHAVE_UHD + +libsdr_a_SOURCES += \ + uhd.c +endif + +if HAVE_SOAPY +AM_CPPFLAGS += -DHAVE_SOAPY + +libsdr_a_SOURCES += \ + soapy.c +endif + diff --git a/src/libsdr/display_iq.c b/src/libsdr/display_iq.c new file mode 100644 index 0000000..350d076 --- /dev/null +++ b/src/libsdr/display_iq.c @@ -0,0 +1,280 @@ +/* display IQ data form functions + * + * (C) 2016 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include "../common/sample.h" +#include "../common/sender.h" + +/* must be odd value! */ +#define SIZE 23 + +static char screen[SIZE][MAX_DISPLAY_WIDTH]; +static uint8_t screen_color[SIZE][MAX_DISPLAY_WIDTH]; +static uint8_t screen_history[SIZE * 2][MAX_DISPLAY_WIDTH]; +static int iq_on = 0; +static double db = 80; + +static dispiq_t disp; + +void display_iq_init(int samplerate) +{ + memset(&disp, 0, sizeof(disp)); + memset(&screen_history, 0, sizeof(screen_history)); + disp.interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5; + /* should not happen due to low interval */ + if (disp.interval_max < MAX_DISPLAY_IQ - 1) + disp.interval_max = MAX_DISPLAY_IQ - 1; +} + +void display_iq_on(int on) +{ + int j; + int w, h; + + get_win_size(&w, &h); + + if (iq_on) { + memset(&screen, ' ', sizeof(screen)); + memset(&screen_history, 0, sizeof(screen_history)); + printf("\0337\033[H"); + for (j = 0; j < SIZE; j++) { + screen[j][w] = '\0'; + puts(screen[j]); + } + printf("\0338"); fflush(stdout); + } + + if (on < 0) { + if (++iq_on == 3) + iq_on = 0; + } else + iq_on = on; +} + +void display_iq_limit_scroll(int on) +{ + int w, h; + + if (!iq_on) + return; + + get_win_size(&w, &h); + + printf("\0337"); + printf("\033[%d;%dr", (on) ? SIZE + 1 : 1, h); + printf("\0338"); +} + +/* + * plot IQ data: + * + * theoretical example: SIZE = 3 allows 6 steps plotted as dots + * + * Line 0: : + * Line 1: : + * Line 2: : + * + * The level of -1.0 .. 1.0 is scaled to -3 and 3. + * + * The lowest of the upper 3 dots ranges from 0.0 .. <1.5. + * The upper most dot ranges from 2.5 .. <3.5. + * The highest of the lower 3 dots ranges from <0.0 .. >-1.5; + * The lower most dot ranges from -2.5 .. >-3.5. + * + * The center column ranges from -0.5 .. <0.5. + * The columns about the center from -1.5 .. <1.5. + */ +void display_iq(float *samples, int length) +{ + int pos, max; + float *buffer; + int i, j, k; + int color = 9; /* default color */ + int x_center, y_center; + double I, Q, L, l, s; + int x, y; + int v, r; + int width, h; + + if (!iq_on) + return; + + get_win_size(&width, &h); + + /* at what line we draw our zero-line and what character we use */ + x_center = width >> 1; + y_center = (SIZE - 1) >> 1; + + pos = disp.interval_pos; + max = disp.interval_max; + buffer = disp.buffer; + + for (i = 0; i < length; i++) { + if (pos >= MAX_DISPLAY_IQ) { + if (++pos == max) + pos = 0; + continue; + } + buffer[pos * 2] = samples[i * 2]; + buffer[pos * 2 + 1] = samples[i * 2 + 1]; + pos++; + if (pos == MAX_DISPLAY_IQ) { + memset(&screen, ' ', sizeof(screen)); + memset(&screen_color, 7, sizeof(screen_color)); + /* render screen history to screen */ + for (y = 0; y < SIZE * 2; y++) { + for (x = 0; x < width; x++) { + v = screen_history[y][x]; + v -= 8; + if (v < 0) + v = 0; + screen_history[y][x] = v; + r = random() & 0x3f; + if (r >= v) + continue; + if (screen[y/2][x] == ':') + continue; + if (screen[y/2][x] == '.') { + if ((y & 1) == 0) + screen[y/2][x] = ':'; + continue; + } + if (screen[y/2][x] == '\'') { + if ((y & 1)) + screen[y/2][x] = ':'; + continue; + } + if ((y & 1) == 0) + screen[y/2][x] = '\''; + else + screen[y/2][x] = '.'; + screen_color[y/2][x] = 4; + } + } + /* plot current IQ date */ + for (j = 0; j < MAX_DISPLAY_IQ; j++) { + I = buffer[j * 2]; + Q = buffer[j * 2 + 1]; + L = I*I + Q*Q; + if (iq_on > 1) { + /* logarithmic scale */ + l = sqrt(L); + s = log10(l) * 20 + db; + if (s < 0) + s = 0; + I = (I / l) * (s / db); + Q = (Q / l) * (s / db); + } + x = x_center + (int)(I * (double)SIZE + (double)width + 0.5) - width; + if (x < 0) + continue; + if (x > width - 1) + continue; + if (Q >= 0) + y = SIZE - 1 - (int)(Q * (double)SIZE - 0.5); + else + y = SIZE - (int)(Q * (double)SIZE + 0.5); + if (y < 0) + continue; + if (y > SIZE * 2 - 1) + continue; + if (screen[y/2][x] == ':' && screen_color[y/2][x] >= 10) + goto cont; + if (screen[y/2][x] == '.' && screen_color[y/2][x] >= 10) { + if ((y & 1) == 0) + screen[y/2][x] = ':'; + goto cont; + } + if (screen[y/2][x] == '\'' && screen_color[y/2][x] >= 10) { + if ((y & 1)) + screen[y/2][x] = ':'; + goto cont; + } + if ((y & 1) == 0) + screen[y/2][x] = '\''; + else + screen[y/2][x] = '.'; +cont: + screen_history[y][x] = 255; + /* overdrive: + * red = close to -1..1 or above + * yellow = close to -0.5..0.5 or above + * Note: L is square of vector length, + * so we compare with square values. + */ + if (L > 0.9 * 0.9) + screen_color[y/2][x] = 11; + else if (L > 0.45 * 0.45 && screen_color[y/2][x] != 11) + screen_color[y/2][x] = 13; + else if (screen_color[y/2][x] < 10) + screen_color[y/2][x] = 12; + } + if (iq_on == 1) + sprintf(screen[0], "(IQ linear"); + else + sprintf(screen[0], "(IQ log %.0f dB", db); + *strchr(screen[0], '\0') = ')'; + printf("\0337\033[H"); + for (j = 0; j < SIZE; j++) { + for (k = 0; k < width; k++) { + if ((j == y_center || k == x_center) && screen[j][k] == ' ') { + /* cross */ + if (color != 4) { + color = 4; + printf("\033[0;34m"); + } + if (j == y_center) { + if (k == x_center) + putchar('o'); + else if (k == x_center - SIZE) + putchar('+'); + else if (k == x_center + SIZE) + putchar('+'); + else + putchar('-'); + } else { + if (j == 0 || j == SIZE - 1) + putchar('+'); + else + putchar('|'); + } + } else { + if (screen_color[j][k] != color) { + color = screen_color[j][k]; + printf("\033[%d;3%dm", color / 10, color % 10); + } + putchar(screen[j][k]); + } + } + printf("\n"); + } + /* reset color and position */ + printf("\033[0;39m\0338"); fflush(stdout); + } + } + + disp.interval_pos = pos; +} + + diff --git a/src/libsdr/display_spectrum.c b/src/libsdr/display_spectrum.c new file mode 100644 index 0000000..6c60237 --- /dev/null +++ b/src/libsdr/display_spectrum.c @@ -0,0 +1,292 @@ +/* display spectrum of IQ data + * + * (C) 2016 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include "../common/sample.h" +#include "../common/sender.h" +#include "../libfft/fft.h" + +#define HEIGHT 20 + +static double buffer_max[MAX_DISPLAY_SPECTRUM]; +static char screen[HEIGHT][MAX_DISPLAY_WIDTH]; +static uint8_t screen_color[HEIGHT][MAX_DISPLAY_WIDTH]; +static int spectrum_on = 0; +static double db = 120; +static double center_frequency, frequency_range; + +static dispspectrum_t disp; + +void display_spectrum_init(int samplerate, double _center_frequency) +{ + memset(&disp, 0, sizeof(disp)); + disp.interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5; + /* should not happen due to low interval */ + if (disp.interval_max < MAX_DISPLAY_SPECTRUM - 1) + disp.interval_max = MAX_DISPLAY_SPECTRUM - 1; + memset(buffer_max, 0, sizeof(buffer_max)); + + center_frequency = _center_frequency; + frequency_range = (double)samplerate; +} + +void display_spectrum_on(int on) +{ + int j; + int w, h; + + get_win_size(&w, &h); + + if (spectrum_on) { + memset(&screen, ' ', sizeof(screen)); + printf("\0337\033[H"); + for (j = 0; j < HEIGHT; j++) { + screen[j][w] = '\0'; + puts(screen[j]); + } + printf("\0338"); fflush(stdout); + } + + if (on < 0) { + if (++spectrum_on == 2) + spectrum_on = 0; + } else + spectrum_on = on; +} + +void display_spectrum_limit_scroll(int on) +{ + int w, h; + + if (!spectrum_on) + return; + + get_win_size(&w, &h); + + printf("\0337"); + printf("\033[%d;%dr", (on) ? HEIGHT + 1 : 1, h); + printf("\0338"); +} + +/* + * plot spectrum data: + * + */ +void display_spectrum(float *samples, int length) +{ + sender_t *sender; + char print_channel[32], print_frequency[32]; + int width, h; + int pos, max; + double *buffer_I, *buffer_Q; + int color = 9; /* default color */ + int i, j, k, o; + double I, Q, v; + int s, e, l, n; + + if (!spectrum_on) + return; + + get_win_size(&width, &h); + if (width > MAX_DISPLAY_WIDTH) + width = MAX_DISPLAY_WIDTH; + + /* calculate size of FFT */ + int m, fft_size = 0, fft_taps = 0; + for (m = 0; m < 16; m++) { + if ((1 << m) > MAX_DISPLAY_SPECTRUM) + break; + if ((1 << m) <= width) { + fft_taps = m; + fft_size = 1 << m; + } + } + if (m == 16) { + fprintf(stderr, "Size of spectrum is not a power of 2, please fix!\n"); + abort(); + } + + int heigh[fft_size], low[fft_size]; + + pos = disp.interval_pos; + max = disp.interval_max; + buffer_I = disp.buffer_I; + buffer_Q = disp.buffer_Q; + + for (i = 0; i < length; i++) { + if (pos >= fft_size) { + if (++pos == max) + pos = 0; + continue; + } + buffer_I[pos] = samples[i * 2]; + buffer_Q[pos] = samples[i * 2 + 1]; + pos++; + if (pos == fft_size) { + fft_process(1, fft_taps, buffer_I, buffer_Q); + k = 0; + for (j = 0; j < fft_size; j++) { + /* scale result vertically */ + I = buffer_I[(j + fft_size / 2) % fft_size]; + Q = buffer_Q[(j + fft_size / 2) % fft_size]; + v = sqrt(I*I + Q*Q); + v = log10(v) * 20 + db; + if (v < 0) + v = 0; + v /= db; + buffer_max[j] -= DISPLAY_INTERVAL / 10.0; + if (v > buffer_max[j]) + buffer_max[j] = v; + + /* heigh is the maximum value */ + heigh[j] = (double)(HEIGHT * 2 - 1) * (1.0 - buffer_max[j]); + if (heigh[j] < 0) + heigh[j] = 0; + if (heigh[j] >= (HEIGHT * 2)) + heigh[j] = (HEIGHT * 2) - 1; + /* low is the current value */ + low[j] = (double)(HEIGHT * 2 - 1) * (1.0 - v); + if (low[j] < 0) + low[j] = 0; + if (low[j] >= (HEIGHT * 2)) + low[j] = (HEIGHT * 2) - 1; + } + /* plot scaled buffer */ + memset(&screen, ' ', sizeof(screen)); + memset(&screen_color, 7, sizeof(screen_color)); /* all white */ + sprintf(screen[0], "(spectrum log %.0f dB", db); + *strchr(screen[0], '\0') = ')'; + o = (width - fft_size) / 2; /* offset from left border */ + for (j = 0; j < fft_size; j++) { + s = l = n = low[j]; + /* get last and next value */ + if (j > 0) + l = (low[j - 1] + s) / 2; + if (j < fft_size - 1) + n = (low[j + 1] + s) / 2; + if (s > l && s > n) { + /* current value is a minimum */ + e = s; + s = (l < n) ? (l + 1) : (n + 1); + } else if (s < l && s < n) { + /* current value is a maximum */ + e = (l > n) ? l : n; + } else if (l < n) { + /* last value is higher, next value is lower */ + s = l + 1; + e = n; + } else if (l > n) { + /* last value is lower, next value is higher */ + s = n + 1; + e = l; + } else { + /* current, last and next values are equal */ + e = s; + } + if (s == e) { + if ((s & 1) == 0) + screen[s >> 1][j + o] = '\''; + else + screen[s >> 1][j + o] = '.'; + screen_color[s >> 1][j + o] = 13; + } else { + if ((s & 1) == 0) + screen[s >> 1][j + o] = '|'; + else + screen[s >> 1][j + o] = '.'; + screen_color[s >> 1][j + o] = 13; + if ((e & 1) == 0) + screen[e >> 1][j + o] = '\''; + else + screen[e >> 1][j + o] = '|'; + screen_color[e >> 1][j + o] = 13; + for (k = (s >> 1) + 1; k < (e >> 1); k++) { + screen[k][j + o] = '|'; + screen_color[k][j + o] = 13; + } + } + e = s; + s = heigh[j]; + if ((s >> 1) < (e >> 1)) { + if ((s & 1) == 0) + screen[s >> 1][j + o] = '|'; + else + screen[s >> 1][j + o] = '.'; + screen_color[s >> 1][j + o] = 4; + for (k = (s >> 1) + 1; k < (e >> 1); k++) { + screen[k][j + o] = '|'; + screen_color[k][j + o] = 4; + } + } + } + for (sender = sender_head; sender; sender = sender->next) { + j = (int)((sender->empfangsfrequenz - center_frequency) / frequency_range * (double) fft_size + width / 2 + 0.5); + if (j < 0 || j >= width) /* check out-of-range, should not happen */ + continue; + for (k = 0; k < HEIGHT; k++) { + /* skip yellow graph */ + if (screen_color[k][j] == 13) + continue; + screen[k][j] = ':'; + screen_color[k][j] = 12; + } + sprintf(print_channel, "Ch%d", sender->kanal); + for (o = 0; o < (int)strlen(print_channel); o++) { + s = j - strlen(print_channel) + o; + if (s >= 0 && s < width) { + screen[HEIGHT - 1][s] = print_channel[o]; + screen_color[HEIGHT - 1][s] = 7; + } + } + if (fmod(sender->empfangsfrequenz, 1000.0)) + sprintf(print_frequency, "%.4f", sender->empfangsfrequenz / 1e6); + else + sprintf(print_frequency, "%.3f", sender->empfangsfrequenz / 1e6); + for (o = 0; o < (int)strlen(print_frequency); o++) { + s = j + o + 1; + if (s >= 0 && s < width) { + screen[HEIGHT - 1][s] = print_frequency[o]; + screen_color[HEIGHT - 1][s] = 7; + } + } + } + printf("\0337\033[H"); + for (j = 0; j < HEIGHT; j++) { + for (k = 0; k < width; k++) { + if (screen_color[j][k] != color) { + color = screen_color[j][k]; + printf("\033[%d;3%dm", color / 10, color % 10); + } + putchar(screen[j][k]); + } + printf("\n"); + } + /* reset color and position */ + printf("\033[0;39m\0338"); fflush(stdout); + } + } + + disp.interval_pos = pos; +} + + diff --git a/src/libsdr/sdr.c b/src/libsdr/sdr.c new file mode 100644 index 0000000..69e07c1 --- /dev/null +++ b/src/libsdr/sdr.c @@ -0,0 +1,865 @@ +/* SDR processing + * + * (C) 2017 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +enum paging_signal; + +#include +#include +#include +#include +#include +#include +#include +#define __USE_GNU +#include +#include +#include "../common/sample.h" +#include "../libfm/fm.h" +#include "../libtimer/timer.h" +#include "../common/sender.h" +#include "sdr_config.h" +#include "sdr.h" +#ifdef HAVE_UHD +#include "uhd.h" +#endif +#ifdef HAVE_SOAPY +#include "soapy.h" +#endif +#include "../common/debug.h" + +/* enable to debug buffer handling */ +//#define DEBUG_BUFFER + +/* enable to test without oversampling filter */ +//#define DISABLE_FILTER + +/* usable bandwidth of IQ rate, because no filter is perfect */ +#define USABLE_BANDWIDTH 0.75 + +int sdr_rx_overflow = 0; + +typedef struct sdr_thread { + int use; + volatile int running, exit; /* flags to control exit of threads */ + int buffer_size; + volatile float *buffer; + float *buffer2; + volatile int in, out; /* in and out pointers (atomic, so no locking required) */ + int max_fill; /* measure maximum buffer fill */ + double max_fill_timer; /* timer to display/reset maximum fill */ + iir_filter_t lp[2]; /* filter for upsample/downsample IQ data */ +} sdr_thread_t; + +typedef struct sdr_chan { + double tx_frequency; /* frequency used */ + double rx_frequency; /* frequency used */ + fm_mod_t mod; /* modulator instance */ + fm_demod_t demod; /* demodulator instance */ + dispmeasparam_t *dmp_rf_level; + dispmeasparam_t *dmp_freq_offset; + dispmeasparam_t *dmp_deviation; +} sdr_chan_t; + +typedef struct sdr { + int threads; /* use threads */ + int oversample; /* oversample IQ rate */ + sdr_thread_t thread_read, + thread_write; + sdr_chan_t *chan; /* settings for all channels */ + int paging_channel; /* if set, points to paging channel */ + sdr_chan_t paging_chan; /* settings for extra paging channel */ + int channels; /* number of frequencies */ + double amplitude; /* amplitude of each carrier */ + int samplerate; /* sample rate of audio data */ + int latspl; /* latency in audio samples */ + wave_rec_t wave_rx_rec; + wave_rec_t wave_tx_rec; + wave_play_t wave_rx_play; + wave_play_t wave_tx_play; + float *modbuff; /* buffer for FM transmodulation */ + sample_t *modbuff_I; + sample_t *modbuff_Q; + sample_t *wavespl0; /* sample buffer for wave generation */ + sample_t *wavespl1; +} sdr_t; + +void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_frequency, double *rx_frequency, int channels, double paging_frequency, int samplerate, int latspl, double max_deviation, double max_modulation) +{ + sdr_t *sdr; + int threads = 1, oversample = 1; /* always use threads */ + double bandwidth; + double tx_center_frequency = 0.0, rx_center_frequency = 0.0; + int rc; + int c; + + PDEBUG(DSDR, DEBUG_DEBUG, "Open SDR device\n"); + + if (sdr_config->samplerate != samplerate) { + if (samplerate > sdr_config->samplerate) { + PDEBUG(DSDR, DEBUG_ERROR, "SDR sample rate must be greater than audio sample rate!\n"); + PDEBUG(DSDR, DEBUG_ERROR, "You selected an SDR rate of %d and an audio rate of %d.\n", sdr_config->samplerate, samplerate); + return NULL; + } + if ((sdr_config->samplerate % samplerate)) { + PDEBUG(DSDR, DEBUG_ERROR, "SDR sample rate must be a multiple of audio sample rate!\n"); + PDEBUG(DSDR, DEBUG_ERROR, "You selected an SDR rate of %d and an audio rate of %d.\n", sdr_config->samplerate, samplerate); + return NULL; + } + oversample = sdr_config->samplerate / samplerate; + threads = 1; + } + + bandwidth = 2.0 * (max_deviation + max_modulation); + PDEBUG(DSDR, DEBUG_INFO, "Require bandwidth of each channel is 2 * (%.1f deviation + %.1f modulation) = %.1f KHz\n", max_deviation / 1e3, max_modulation / 1e3, bandwidth / 1e3); + + if (channels < 1) { + PDEBUG(DSDR, DEBUG_ERROR, "No channel given, please fix!\n"); + abort(); + } + + sdr = calloc(sizeof(*sdr), 1); + if (!sdr) { + PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); + goto error; + } + sdr->channels = channels; + sdr->amplitude = 1.0 / (double)channels; + sdr->samplerate = samplerate; + sdr->latspl = latspl; + sdr->threads = threads; /* always requried, because write may block */ + sdr->oversample = oversample; + + if (threads) { + memset(&sdr->thread_read, 0, sizeof(sdr->thread_read)); + sdr->thread_read.buffer_size = sdr->latspl * 2 * sdr->oversample + 2; + sdr->thread_read.buffer = calloc(sdr->thread_read.buffer_size, sizeof(*sdr->thread_read.buffer)); + if (!sdr->thread_read.buffer) { + PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); + goto error; + } + sdr->thread_read.buffer2 = calloc(sdr->thread_read.buffer_size, sizeof(*sdr->thread_read.buffer2)); + if (!sdr->thread_read.buffer2) { + PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); + goto error; + } + sdr->thread_read.in = sdr->thread_read.out = 0; + if (oversample > 1) { + iir_lowpass_init(&sdr->thread_read.lp[0], samplerate / 2.0, sdr_config->samplerate, 2); + iir_lowpass_init(&sdr->thread_read.lp[1], samplerate / 2.0, sdr_config->samplerate, 2); + } + memset(&sdr->thread_write, 0, sizeof(sdr->thread_write)); + sdr->thread_write.buffer_size = sdr->latspl * 2 + 2; + sdr->thread_write.buffer = calloc(sdr->thread_write.buffer_size, sizeof(*sdr->thread_write.buffer)); + if (!sdr->thread_write.buffer) { + PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); + goto error; + } + sdr->thread_write.buffer2 = calloc(sdr->thread_write.buffer_size * sdr->oversample, sizeof(*sdr->thread_write.buffer2)); + if (!sdr->thread_write.buffer2) { + PDEBUG(DSDR, DEBUG_ERROR, "No mem!\n"); + goto error; + } + sdr->thread_write.in = sdr->thread_write.out = 0; + if (oversample > 1) { + iir_lowpass_init(&sdr->thread_write.lp[0], samplerate / 2.0, sdr_config->samplerate, 2); + iir_lowpass_init(&sdr->thread_write.lp[1], samplerate / 2.0, sdr_config->samplerate, 2); + } + } + + /* alloc fm modulation buffers */ + sdr->modbuff = calloc(sdr->latspl * 2, sizeof(*sdr->modbuff)); + if (!sdr->modbuff) { + PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); + goto error; + } + sdr->modbuff_I = calloc(sdr->latspl, sizeof(*sdr->modbuff_I)); + if (!sdr->modbuff_I) { + PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); + goto error; + } + sdr->modbuff_Q = calloc(sdr->latspl, sizeof(*sdr->modbuff_Q)); + if (!sdr->modbuff_Q) { + PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); + goto error; + } + sdr->wavespl0 = calloc(sdr->latspl, sizeof(*sdr->wavespl0)); + if (!sdr->wavespl0) { + PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); + goto error; + } + sdr->wavespl1 = calloc(sdr->latspl, sizeof(*sdr->wavespl1)); + if (!sdr->wavespl1) { + PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); + goto error; + } + + /* special case where we use a paging frequency */ + if (paging_frequency) { + /* add extra paging channel */ + sdr->paging_channel = channels; + } + + /* create list of channel states */ + sdr->chan = calloc(sizeof(*sdr->chan), channels + (sdr->paging_channel != 0)); + if (!sdr->chan) { + PDEBUG(DSDR, DEBUG_ERROR, "NO MEM!\n"); + goto error; + } + + if (tx_frequency) { + /* calculate required bandwidth (IQ rate) */ + for (c = 0; c < channels; c++) { + PDEBUG(DSDR, DEBUG_INFO, "Frequency #%d: TX = %.6f MHz\n", c, tx_frequency[c] / 1e6); + sdr->chan[c].tx_frequency = tx_frequency[c]; + } + if (sdr->paging_channel) { + PDEBUG(DSDR, DEBUG_INFO, "Paging Frequency: TX = %.6f MHz\n", paging_frequency / 1e6); + sdr->chan[sdr->paging_channel].tx_frequency = paging_frequency; + } + + double tx_low_frequency = sdr->chan[0].tx_frequency, tx_high_frequency = sdr->chan[0].tx_frequency; + for (c = 1; c < channels; c++) { + if (sdr->chan[c].tx_frequency < tx_low_frequency) + tx_low_frequency = sdr->chan[c].tx_frequency; + if (sdr->chan[c].tx_frequency > tx_high_frequency) + tx_high_frequency = sdr->chan[c].tx_frequency; + } + if (sdr->paging_channel) { + if (sdr->chan[sdr->paging_channel].tx_frequency < tx_low_frequency) + tx_low_frequency = sdr->chan[sdr->paging_channel].tx_frequency; + if (sdr->chan[sdr->paging_channel].tx_frequency > tx_high_frequency) + tx_high_frequency = sdr->chan[sdr->paging_channel].tx_frequency; + } + /* range of TX */ + double range = tx_high_frequency - tx_low_frequency + bandwidth; + PDEBUG(DSDR, DEBUG_INFO, "Total bandwidth for all TX Frequencies: %.0f Hz\n", range); + if (range > samplerate * USABLE_BANDWIDTH) { + PDEBUG(DSDR, DEBUG_NOTICE, "*******************************************************************************\n"); + PDEBUG(DSDR, DEBUG_NOTICE, "The required bandwidth of %.0f Hz exceeds %.0f%% of the sample rate.\n", range, USABLE_BANDWIDTH * 100.0); + PDEBUG(DSDR, DEBUG_NOTICE, "Please increase samplerate!\n"); + PDEBUG(DSDR, DEBUG_NOTICE, "*******************************************************************************\n"); + goto error; + } + tx_center_frequency = (tx_high_frequency + tx_low_frequency) / 2.0; + PDEBUG(DSDR, DEBUG_INFO, "Using center frequency: TX %.6f MHz\n", tx_center_frequency / 1e6); + /* set offsets to center frequency */ + for (c = 0; c < channels; c++) { + double tx_offset; + tx_offset = sdr->chan[c].tx_frequency - tx_center_frequency; + PDEBUG(DSDR, DEBUG_DEBUG, "Frequency #%d: TX offset: %.6f MHz\n", c, tx_offset / 1e6); + rc = fm_mod_init(&sdr->chan[c].mod, samplerate, tx_offset, sdr->amplitude); + if (rc < 0) + goto error; + } + if (sdr->paging_channel) { + double tx_offset; + tx_offset = sdr->chan[sdr->paging_channel].tx_frequency - tx_center_frequency; + PDEBUG(DSDR, DEBUG_DEBUG, "Paging Frequency: TX offset: %.6f MHz\n", tx_offset / 1e6); + rc = fm_mod_init(&sdr->chan[sdr->paging_channel].mod, samplerate, tx_offset, sdr->amplitude); + if (rc < 0) + goto error; + } + /* show gain */ + PDEBUG(DSDR, DEBUG_INFO, "Using gain: TX %.1f dB\n", sdr_config->tx_gain); + /* open wave */ + if (sdr_config->write_iq_tx_wave) { + rc = wave_create_record(&sdr->wave_tx_rec, sdr_config->write_iq_tx_wave, samplerate, 2, 1.0); + if (rc < 0) { + PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE recoding instance!\n"); + goto error; + } + } + if (sdr_config->read_iq_tx_wave) { + rc = wave_create_playback(&sdr->wave_tx_play, sdr_config->read_iq_tx_wave, samplerate, 2, 1.0); + if (rc < 0) { + PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE playback instance!\n"); + goto error; + } + } + } + + if (rx_frequency) { + for (c = 0; c < channels; c++) { + PDEBUG(DSDR, DEBUG_INFO, "Frequency #%d: RX = %.6f MHz\n", c, rx_frequency[c] / 1e6); + sdr->chan[c].rx_frequency = rx_frequency[c]; + } + + /* calculate required bandwidth (IQ rate) */ + double rx_low_frequency = sdr->chan[0].rx_frequency, rx_high_frequency = sdr->chan[0].rx_frequency; + for (c = 1; c < channels; c++) { + if (sdr->chan[c].rx_frequency < rx_low_frequency) + rx_low_frequency = sdr->chan[c].rx_frequency; + if (sdr->chan[c].rx_frequency > rx_high_frequency) + rx_high_frequency = sdr->chan[c].rx_frequency; + } + /* range of RX */ + double range = rx_high_frequency - rx_low_frequency + bandwidth; + PDEBUG(DSDR, DEBUG_INFO, "Total bandwidth for all RX Frequencies: %.0f Hz\n", range); + if (range > samplerate * USABLE_BANDWIDTH) { + PDEBUG(DSDR, DEBUG_NOTICE, "*******************************************************************************\n"); + PDEBUG(DSDR, DEBUG_NOTICE, "The required bandwidth of %.0f Hz exceeds %.0f%% of the sample rate.\n", range, USABLE_BANDWIDTH * 100.0); + PDEBUG(DSDR, DEBUG_NOTICE, "Please increase samplerate!\n"); + PDEBUG(DSDR, DEBUG_NOTICE, "*******************************************************************************\n"); + goto error; + } + rx_center_frequency = (rx_high_frequency + rx_low_frequency) / 2.0; + PDEBUG(DSDR, DEBUG_INFO, "Using center frequency: RX %.6f MHz\n", rx_center_frequency / 1e6); + /* set offsets to center frequency */ + for (c = 0; c < channels; c++) { + double rx_offset; + rx_offset = sdr->chan[c].rx_frequency - rx_center_frequency; + PDEBUG(DSDR, DEBUG_DEBUG, "Frequency #%d: RX offset: %.6f MHz\n", c, rx_offset / 1e6); + rc = fm_demod_init(&sdr->chan[c].demod, samplerate, rx_offset, bandwidth); + if (rc < 0) + goto error; + } + /* show gain */ + PDEBUG(DSDR, DEBUG_INFO, "Using gain: RX %.1f dB\n", sdr_config->rx_gain); + /* open wave */ + if (sdr_config->write_iq_rx_wave) { + rc = wave_create_record(&sdr->wave_rx_rec, sdr_config->write_iq_rx_wave, samplerate, 2, 1.0); + if (rc < 0) { + PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE recoding instance!\n"); + goto error; + } + } + if (sdr_config->read_iq_rx_wave) { + rc = wave_create_playback(&sdr->wave_rx_play, sdr_config->read_iq_rx_wave, samplerate, 2, 1.0); + if (rc < 0) { + PDEBUG(DSDR, DEBUG_ERROR, "Failed to create WAVE playback instance!\n"); + goto error; + } + } + /* init measurements display */ + for (c = 0; c < channels; c++) { + sender_t *sender = get_sender_by_empfangsfrequenz(sdr->chan[c].rx_frequency); + if (!sender) + continue; + sdr->chan[c].dmp_rf_level = display_measurements_add(sender, "RF Level", "%.1f dB", DISPLAY_MEAS_AVG, DISPLAY_MEAS_LEFT, -96.0, 0.0, -INFINITY); + sdr->chan[c].dmp_freq_offset = display_measurements_add(sender, "Freq. Offset", "%+.2f KHz", DISPLAY_MEAS_AVG, DISPLAY_MEAS_CENTER, -max_deviation / 1000.0 * 2.0, max_deviation / 1000.0 * 2.0, 0.0); + sdr->chan[c].dmp_deviation = display_measurements_add(sender, "Deviation", "%.2f KHz", DISPLAY_MEAS_PEAK2PEAK, DISPLAY_MEAS_LEFT, 0.0, max_deviation / 1000.0 * 1.5, max_deviation / 1000.0); + } + } + + if (sdr_config->swap_links) { + double temp; + PDEBUG(DSDR, DEBUG_NOTICE, "Sapping RX and TX frequencies!\n"); + temp = rx_center_frequency; + rx_center_frequency = tx_center_frequency; + tx_center_frequency = temp; + } + + display_iq_init(samplerate); + display_spectrum_init(samplerate, rx_center_frequency); + +#ifdef HAVE_UHD + if (sdr_config->uhd) { + rc = uhd_open(sdr_config->channel, sdr_config->device_args, sdr_config->stream_args, sdr_config->tune_args, sdr_config->tx_antenna, sdr_config->rx_antenna, tx_center_frequency, rx_center_frequency, sdr_config->samplerate, sdr_config->tx_gain, sdr_config->rx_gain, sdr_config->bandwidth, sdr_config->uhd_tx_timestamps); + if (rc) + goto error; + } +#endif + +#ifdef HAVE_SOAPY + if (sdr_config->soapy) { + rc = soapy_open(sdr_config->channel, sdr_config->device_args, sdr_config->stream_args, sdr_config->tune_args, sdr_config->tx_antenna, sdr_config->rx_antenna, tx_center_frequency, rx_center_frequency, sdr_config->samplerate, sdr_config->tx_gain, sdr_config->rx_gain, sdr_config->bandwidth); + if (rc) + goto error; + } +#endif + + return sdr; + +error: + sdr_close(sdr); + return NULL; +} + +static void *sdr_write_child(void *arg) +{ + sdr_t *sdr = (sdr_t *)arg; + int num; + int fill, out; + int s, ss, o; + + while (sdr->thread_write.running) { + /* write to SDR */ + fill = (sdr->thread_write.in - sdr->thread_write.out + sdr->thread_write.buffer_size) % sdr->thread_write.buffer_size; + num = fill / 2; + if (num) { +#ifdef DEBUG_BUFFER + printf("Thread found %d samples in write buffer and forwards them to SDR.\n", num); +#endif + out = sdr->thread_write.out; + for (s = 0, ss = 0; s < num; s++) { + for (o = 0; o < sdr->oversample; o++) { + sdr->thread_write.buffer2[ss++] = sdr->thread_write.buffer[out]; + sdr->thread_write.buffer2[ss++] = sdr->thread_write.buffer[out + 1]; + } + out = (out + 2) % sdr->thread_write.buffer_size; + } + sdr->thread_write.out = out; +#ifndef DISABLE_FILTER + /* filter spectrum */ + if (sdr->oversample > 1) { + iir_process_baseband(&sdr->thread_write.lp[0], sdr->thread_write.buffer2, num * sdr->oversample); + iir_process_baseband(&sdr->thread_write.lp[1], sdr->thread_write.buffer2 + 1, num * sdr->oversample); + } +#endif +#ifdef HAVE_UHD + if (sdr_config->uhd) + uhd_send(sdr->thread_write.buffer2, num * sdr->oversample); +#endif +#ifdef HAVE_SOAPY + if (sdr_config->soapy) + soapy_send(sdr->thread_write.buffer2, num * sdr->oversample); +#endif + } + + /* delay some time */ + usleep(1000); + } + + PDEBUG(DSDR, DEBUG_DEBUG, "Thread received exit!\n"); + sdr->thread_write.exit = 1; + return NULL; +} + +static void *sdr_read_child(void *arg) +{ + sdr_t *sdr = (sdr_t *)arg; + int num, count = 0; + int space, in; + int s, ss; + + while (sdr->thread_read.running) { + /* read from SDR */ + space = (sdr->thread_read.out - sdr->thread_read.in - 2 + sdr->thread_read.buffer_size) % sdr->thread_read.buffer_size; + num = space / 2; + if (num) { +#ifdef HAVE_UHD + if (sdr_config->uhd) + count = uhd_receive(sdr->thread_read.buffer2, num); +#endif +#ifdef HAVE_SOAPY + if (sdr_config->soapy) + count = soapy_receive(sdr->thread_read.buffer2, num); +#endif + if (count > 0) { +#ifdef DEBUG_BUFFER + printf("Thread read %d samples from SDR and writes them to read buffer.\n", count); +#endif +#ifndef DISABLE_FILTER + /* filter spectrum */ + if (sdr->oversample > 1) { + iir_process_baseband(&sdr->thread_read.lp[0], sdr->thread_read.buffer2, count); + iir_process_baseband(&sdr->thread_read.lp[1], sdr->thread_read.buffer2 + 1, count); + } +#endif + in = sdr->thread_read.in; + for (s = 0, ss = 0; s < count; s++) { + sdr->thread_read.buffer[in++] = sdr->thread_read.buffer2[ss++]; + sdr->thread_read.buffer[in++] = sdr->thread_read.buffer2[ss++]; + in %= sdr->thread_read.buffer_size; + } + sdr->thread_read.in = in; + } + } + + /* delay some time */ + usleep(1000); + } + + PDEBUG(DSDR, DEBUG_DEBUG, "Thread received exit!\n"); + sdr->thread_read.exit = 1; + return NULL; +} + +/* start streaming */ +int sdr_start(void *inst) +{ + sdr_t *sdr = (sdr_t *)inst; + int rc = -EINVAL; + +#ifdef HAVE_UHD + if (sdr_config->uhd) + rc = uhd_start(); +#endif +#ifdef HAVE_SOAPY + if (sdr_config->soapy) + rc = soapy_start(); +#endif + if (rc < 0) + return rc; + + if (sdr->threads) { + int rc; + pthread_t tid; + char tname[64]; + + PDEBUG(DSDR, DEBUG_DEBUG, "Create threads!\n"); + sdr->thread_write.running = 1; + sdr->thread_write.exit = 0; + rc = pthread_create(&tid, NULL, sdr_write_child, inst); + if (rc < 0) { + sdr->thread_write.running = 0; + PDEBUG(DSDR, DEBUG_ERROR, "Failed to create thread!\n"); + return rc; + } + pthread_getname_np(tid, tname, sizeof(tname)); + strncat(tname, "-sdr_tx", sizeof(tname)); + tname[sizeof(tname) - 1] = '\0'; + pthread_setname_np(tid, tname); + sdr->thread_read.running = 1; + sdr->thread_read.exit = 0; + rc = pthread_create(&tid, NULL, sdr_read_child, inst); + if (rc < 0) { + sdr->thread_read.running = 0; + PDEBUG(DSDR, DEBUG_ERROR, "Failed to create thread!\n"); + return rc; + } + pthread_getname_np(tid, tname, sizeof(tname)); + strncat(tname, "-sdr_rx", sizeof(tname)); + tname[sizeof(tname) - 1] = '\0'; + pthread_setname_np(tid, tname); + } + + return 0; +} + +void sdr_close(void *inst) +{ + sdr_t *sdr = (sdr_t *)inst; + + PDEBUG(DSDR, DEBUG_DEBUG, "Close SDR device\n"); + + if (sdr->threads) { + if (sdr->thread_write.running) { + PDEBUG(DSDR, DEBUG_DEBUG, "Thread sending exit!\n"); + sdr->thread_write.running = 0; + while (sdr->thread_write.exit == 0) + usleep(1000); + } + if (sdr->thread_read.running) { + PDEBUG(DSDR, DEBUG_DEBUG, "Thread sending exit!\n"); + sdr->thread_read.running = 0; + while (sdr->thread_read.exit == 0) + usleep(1000); + } + } + + if (sdr->thread_read.buffer) + free((void *)sdr->thread_read.buffer); + if (sdr->thread_read.buffer2) + free((void *)sdr->thread_read.buffer2); + if (sdr->thread_write.buffer) + free((void *)sdr->thread_write.buffer); + if (sdr->thread_write.buffer2) + free((void *)sdr->thread_write.buffer2); + +#ifdef HAVE_UHD + if (sdr_config->uhd) + uhd_close(); +#endif + +#ifdef HAVE_SOAPY + if (sdr_config->soapy) + soapy_close(); +#endif + + if (sdr) { + free(sdr->modbuff); + free(sdr->modbuff_I); + free(sdr->modbuff_Q); + free(sdr->wavespl0); + free(sdr->wavespl1); + wave_destroy_record(&sdr->wave_rx_rec); + wave_destroy_record(&sdr->wave_tx_rec); + wave_destroy_playback(&sdr->wave_rx_play); + wave_destroy_playback(&sdr->wave_tx_play); + if (sdr->chan) { + int c; + + for (c = 0; c < sdr->channels; c++) { + fm_mod_exit(&sdr->chan[c].mod); + fm_demod_exit(&sdr->chan[c].demod); + } + if (sdr->paging_channel) + fm_mod_exit(&sdr->chan[sdr->paging_channel].mod); + free(sdr->chan); + } + free(sdr); + sdr = NULL; + } +} + +int sdr_write(void *inst, sample_t **samples, uint8_t **power, int num, enum paging_signal __attribute__((unused)) *paging_signal, int *on, int channels) +{ + sdr_t *sdr = (sdr_t *)inst; + float *buff = NULL; + int c, s, ss; + int sent = 0; + + if (num > sdr->latspl) { + fprintf(stderr, "exceeding maximum size given by sdr_latspl, please fix!\n"); + abort(); + } + if (channels != sdr->channels && channels != 0) { + PDEBUG(DSDR, DEBUG_ERROR, "Invalid number of channels, please fix!\n"); + abort(); + } + + /* process all channels */ + if (channels) { + buff = sdr->modbuff; + memset(buff, 0, sizeof(*buff) * num * 2); + for (c = 0; c < channels; c++) { + /* switch to paging channel, if requested */ + if (on[c] && sdr->paging_channel) + fm_modulate_complex(&sdr->chan[sdr->paging_channel].mod, samples[c], power[c], num, buff); + else + fm_modulate_complex(&sdr->chan[c].mod, samples[c], power[c], num, buff); + } + } else { + buff = (float *)samples; + } + + if (sdr->wave_tx_rec.fp) { + sample_t *spl_list[2] = { sdr->wavespl0, sdr->wavespl1 }; + for (s = 0, ss = 0; s < num; s++) { + spl_list[0][s] = buff[ss++]; + spl_list[1][s] = buff[ss++]; + } + wave_write(&sdr->wave_tx_rec, spl_list, num); + } + if (sdr->wave_tx_play.fp) { + sample_t *spl_list[2] = { sdr->wavespl0, sdr->wavespl1 }; + wave_read(&sdr->wave_tx_play, spl_list, num); + for (s = 0, ss = 0; s < num; s++) { + buff[ss++] = spl_list[0][s]; + buff[ss++] = spl_list[1][s]; + } + } + + if (sdr->threads) { + /* store data towards SDR in ring buffer */ + int fill, space, in; + + fill = (sdr->thread_write.in - sdr->thread_write.out + sdr->thread_write.buffer_size) % sdr->thread_write.buffer_size; + space = (sdr->thread_write.out - sdr->thread_write.in - 2 + sdr->thread_write.buffer_size) % sdr->thread_write.buffer_size; + + /* debug fill level */ + if (fill > sdr->thread_write.max_fill) + sdr->thread_write.max_fill = fill; + if (sdr->thread_write.max_fill_timer == 0.0) + sdr->thread_write.max_fill_timer = get_time(); + if (get_time() - sdr->thread_write.max_fill_timer > 1.0) { + double delay; + delay = (double)sdr->thread_write.max_fill / 2.0 / (double)sdr->samplerate; + sdr->thread_write.max_fill = 0; + sdr->thread_write.max_fill_timer += 1.0; + PDEBUG(DSDR, DEBUG_DEBUG, "write delay = %.3f ms\n", delay * 1000.0); + } + + if (space < num * 2) { + PDEBUG(DSDR, DEBUG_ERROR, "Write SDR buffer overflow!\n"); + num = space / 2; + } +#ifdef DEBUG_BUFFER + printf("Writing %d samples to write buffer.\n", num); +#endif + in = sdr->thread_write.in; + for (s = 0, ss = 0; s < num; s++) { + sdr->thread_write.buffer[in++] = buff[ss++]; + sdr->thread_write.buffer[in++] = buff[ss++]; + in %= sdr->thread_write.buffer_size; + } + sdr->thread_write.in = in; + sent = num; + } else { +#ifdef HAVE_UHD + if (sdr_config->uhd) + sent = uhd_send(buff, num); +#endif +#ifdef HAVE_SOAPY + if (sdr_config->soapy) + sent = soapy_send(buff, num); +#endif + if (sent < 0) + return sent; + } + + return sent; +} + +int sdr_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db) +{ + sdr_t *sdr = (sdr_t *)inst; + float *buff = NULL; + int count = 0; + int c, s, ss; + + if (num > sdr->latspl) { + fprintf(stderr, "exceeding maximum size given by sdr_latspl, please fix!\n"); + abort(); + } + + if (channels) { + buff = sdr->modbuff; + } else { + buff = (float *)samples; + } + + if (sdr->threads) { + /* load data from SDR out of ring buffer */ + int fill, out; + + fill = (sdr->thread_read.in - sdr->thread_read.out + sdr->thread_read.buffer_size) % sdr->thread_read.buffer_size; + + /* debug fill level */ + if (fill > sdr->thread_read.max_fill) + sdr->thread_read.max_fill = fill; + if (sdr->thread_read.max_fill_timer == 0.0) + sdr->thread_read.max_fill_timer = get_time(); + if (get_time() - sdr->thread_read.max_fill_timer > 1.0) { + double delay; + delay = (double)sdr->thread_read.max_fill / 2.0 / (double)sdr_config->samplerate; + sdr->thread_read.max_fill = 0; + sdr->thread_read.max_fill_timer += 1.0; + PDEBUG(DSDR, DEBUG_DEBUG, "read delay = %.3f ms\n", delay * 1000.0); + } + + if (fill / 2 / sdr->oversample < num) + num = fill / 2 / sdr->oversample; +#ifdef DEBUG_BUFFER + printf("Reading %d samples from read buffer.\n", num); +#endif + out = sdr->thread_read.out; + for (s = 0, ss = 0; s < num; s++) { + buff[ss++] = sdr->thread_read.buffer[out]; + buff[ss++] = sdr->thread_read.buffer[out + 1]; + out = (out + 2 * sdr->oversample) % sdr->thread_read.buffer_size; + } + sdr->thread_read.out = out; + count = num; + } else { +#ifdef HAVE_UHD + if (sdr_config->uhd) + count = uhd_receive(buff, num); +#endif +#ifdef HAVE_SOAPY + if (sdr_config->soapy) + count = soapy_receive(buff, num); +#endif + if (count <= 0) + return count; + } + + if (sdr_rx_overflow) { + PDEBUG(DSDR, DEBUG_ERROR, "SDR RX overflow!\n"); + sdr_rx_overflow = 0; + } + + if (sdr->wave_rx_rec.fp) { + sample_t *spl_list[2] = { sdr->wavespl0, sdr->wavespl1 }; + for (s = 0, ss = 0; s < count; s++) { + spl_list[0][s] = buff[ss++]; + spl_list[1][s] = buff[ss++]; + } + wave_write(&sdr->wave_rx_rec, spl_list, count); + } + if (sdr->wave_rx_play.fp) { + sample_t *spl_list[2] = { sdr->wavespl0, sdr->wavespl1 }; + wave_read(&sdr->wave_rx_play, spl_list, count); + for (s = 0, ss = 0; s < count; s++) { + buff[ss++] = spl_list[0][s]; + buff[ss++] = spl_list[1][s]; + } + } + display_iq(buff, count); + display_spectrum(buff, count); + + if (channels) { + for (c = 0; c < channels; c++) { + fm_demodulate_complex(&sdr->chan[c].demod, samples[c], count, buff, sdr->modbuff_I, sdr->modbuff_Q); + sender_t *sender = get_sender_by_empfangsfrequenz(sdr->chan[c].rx_frequency); + if (!sender || !count) + continue; + double min, max, avg; + avg = 0.0; + for (s = 0; s < count; s++) { + /* average the square length of vector */ + avg += sdr->modbuff_I[s] * sdr->modbuff_I[s] + sdr->modbuff_Q[s] * sdr->modbuff_Q[s]; + } + avg = sqrt(avg /(double)count); /* RMS */ + avg = log10(avg) * 20; + display_measurements_update(sdr->chan[c].dmp_rf_level, avg, 0.0); + rf_level_db[c] = avg; + min = 0.0; + max = 0.0; + avg = 0.0; + for (s = 0; s < count; s++) { + avg += samples[c][s]; + if (s == 0 || samples[c][s] > max) + max = samples[c][s]; + if (s == 0 || samples[c][s] < min) + min = samples[c][s]; + } + avg /= (double)count; + display_measurements_update(sdr->chan[c].dmp_freq_offset, avg / 1000.0, 0.0); + /* use half min and max, because we want the deviation above/below (+-) center frequency. */ + display_measurements_update(sdr->chan[c].dmp_deviation, min / 2.0 / 1000.0, max / 2.0 / 1000.0); + } + } + + return count; +} + +/* how much do we need to send (in audio sample duration) to get the target delay (latspl) */ +int sdr_get_tosend(void *inst, int latspl) +{ + sdr_t *sdr = (sdr_t *)inst; + int count = 0; + +#ifdef HAVE_UHD + if (sdr_config->uhd) + count = uhd_get_tosend(latspl * sdr->oversample); +#endif +#ifdef HAVE_SOAPY + if (sdr_config->soapy) + count = soapy_get_tosend(latspl * sdr->oversample); +#endif + if (count < 0) + return count; + count /= sdr->oversample; + + if (sdr->threads) { + /* substract what we have in write buffer, because this is not jent sent to the SDR */ + int fill; + + fill = (sdr->thread_write.in - sdr->thread_write.out + sdr->thread_write.buffer_size) % sdr->thread_write.buffer_size; + count -= fill / 2; + if (count < 0) + count = 0; + } + + return count; +} + + diff --git a/src/libsdr/sdr.h b/src/libsdr/sdr.h new file mode 100644 index 0000000..360f424 --- /dev/null +++ b/src/libsdr/sdr.h @@ -0,0 +1,8 @@ + +int sdr_start(void *inst); +void *sdr_open(const char *audiodev, double *tx_frequency, double *rx_frequency, int channels, double paging_frequency, int samplerate, int latspl, double bandwidth, double sample_deviation); +void sdr_close(void *inst); +int sdr_write(void *inst, sample_t **samples, uint8_t **power, int num, enum paging_signal *paging_signal, int *on, int channels); +int sdr_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db); +int sdr_get_tosend(void *inst, int latspl); + diff --git a/src/libsdr/sdr_config.c b/src/libsdr/sdr_config.c new file mode 100644 index 0000000..0ab24bf --- /dev/null +++ b/src/libsdr/sdr_config.c @@ -0,0 +1,262 @@ +/* Config for SDR + * + * (C) 2017 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +enum paging_signal; + +#include +#include +#include +#include +#include +#include "../common/sample.h" +#include "sdr.h" +#include "sdr_config.h" + +static int got_init = 0; +extern int use_sdr; +sdr_config_t *sdr_config = NULL; + +void sdr_config_init(void) +{ + sdr_config = calloc(1, sizeof(*sdr_config)); + memset(sdr_config, 0, sizeof(*sdr_config)); + sdr_config->device_args = ""; + sdr_config->stream_args = ""; + sdr_config->tune_args = ""; + + got_init = 1; +} + +void sdr_config_print_help(void) +{ + printf("\nSDR options:\n"); + /* - - */ +#ifdef HAVE_UHD + printf(" --sdr-uhd\n"); + printf(" Force UHD driver\n"); +#endif +#ifdef HAVE_SOAPY + printf(" --sdr-soapy\n"); + printf(" Force SoapySDR driver\n"); +#endif + printf(" --sdr-channel \n"); + printf(" Give channel number for multi channel SDR device (default = %d)\n", sdr_config->channel); + printf(" --sdr-device-args \n"); + printf(" --sdr-stream-args \n"); + printf(" --sdr-tune-args \n"); + printf(" Optional SDR device arguments, seperated by comma\n"); + printf(" e.g. --sdr-device-args =[,=[,...]]\n"); + printf(" --sdr-samplerate \n"); + printf(" Sample rate to use with SDR. By default it equals the regular sample\n"); + printf(" rate.\n"); + printf(" --sdr-bandwidth \n"); + printf(" Give IF filter bandwidth to use. If not, sample rate is used.\n"); + printf(" --sdr-rx-antenna \n"); + printf(" SDR device's RX antenna name, use 'list' to get a list\n"); + printf(" --sdr-tx-antenna \n"); + printf(" SDR device's TX antenna name, use 'list' to get a list\n"); + printf(" --sdr-rx-gain \n"); + printf(" SDR device's RX gain in dB (default = %.1f)\n", sdr_config->rx_gain); + printf(" --sdr-tx-gain \n"); + printf(" SDR device's TX gain in dB (default = %.1f)\n", sdr_config->tx_gain); + printf(" --write-iq-rx-wave \n"); + printf(" Write received IQ data to given wave file.\n"); + printf(" --write-iq-tx-wave \n"); + printf(" Write transmitted IQ data to given wave file.\n"); + printf(" --read-iq-rx-wave \n"); + printf(" Replace received IQ data by given wave file.\n"); + printf(" --read-iq-tx-wave \n"); + printf(" Replace transmitted IQ data by given wave file.\n"); + printf(" --sdr-swap-links\n"); + printf(" Swap RX and TX frequencies for loopback tests over the air.\n"); +#ifdef HAVE_UHD + printf(" --sdr-uhd-tx-timestamps\n"); + printf(" Use TX timestamps on UHD device. (May not work with some devices!)\n"); +#endif +} + +void sdr_config_print_hotkeys(void) +{ + printf("Press 'q' key to toggle display of RX I/Q vector.\n"); + printf("Press 's' key to toggle display of RX spectrum.\n"); +} + +#define OPT_SDR_UHD 1500 +#define OPT_SDR_SOAPY 1501 +#define OPT_SDR_CHANNEL 1502 +#define OPT_SDR_DEVICE_ARGS 1503 +#define OPT_SDR_STREAM_ARGS 1504 +#define OPT_SDR_TUNE_ARGS 1505 +#define OPT_SDR_RX_ANTENNA 1506 +#define OPT_SDR_TX_ANTENNA 1507 +#define OPT_SDR_RX_GAIN 1508 +#define OPT_SDR_TX_GAIN 1509 +#define OPT_SDR_SAMPLERATE 1510 +#define OPT_SDR_BANDWIDTH 1511 +#define OPT_WRITE_IQ_RX_WAVE 1512 +#define OPT_WRITE_IQ_TX_WAVE 1513 +#define OPT_READ_IQ_RX_WAVE 1514 +#define OPT_READ_IQ_TX_WAVE 1515 +#define OPT_SDR_SWAP_LINKS 1516 +#define OPT_SDR_UHD_TX_TS 1517 + +struct option sdr_config_long_options[] = { + {"sdr-uhd", 0, 0, OPT_SDR_UHD}, + {"sdr-soapy", 0, 0, OPT_SDR_SOAPY}, + {"sdr-channel", 1, 0, OPT_SDR_CHANNEL}, + {"sdr-device-args", 1, 0, OPT_SDR_DEVICE_ARGS}, + {"sdr-stream-args", 1, 0, OPT_SDR_STREAM_ARGS}, + {"sdr-tune-args", 1, 0, OPT_SDR_TUNE_ARGS}, + {"sdr-samplerate", 1, 0, OPT_SDR_SAMPLERATE}, + {"sdr-bandwidth", 1, 0, OPT_SDR_BANDWIDTH}, + {"sdr-rx-antenna", 1, 0, OPT_SDR_RX_ANTENNA}, + {"sdr-tx-antenna", 1, 0, OPT_SDR_TX_ANTENNA}, + {"sdr-rx-gain", 1, 0, OPT_SDR_RX_GAIN}, + {"sdr-tx-gain", 1, 0, OPT_SDR_TX_GAIN}, + {"write-iq-rx-wave", 1, 0, OPT_WRITE_IQ_RX_WAVE}, + {"write-iq-tx-wave", 1, 0, OPT_WRITE_IQ_TX_WAVE}, + {"read-iq-rx-wave", 1, 0, OPT_READ_IQ_RX_WAVE}, + {"read-iq-tx-wave", 1, 0, OPT_READ_IQ_TX_WAVE}, + {"sdr-swap-links", 0, 0, OPT_SDR_SWAP_LINKS}, + {"sdr-uhd-tx-timestamps", 0, 0, OPT_SDR_UHD_TX_TS}, + {0, 0, 0, 0} +}; + +const char *sdr_config_optstring = ""; + +int sdr_config_opt_switch(int c, int *skip_args) +{ + switch (c) { + case OPT_SDR_UHD: +#ifdef HAVE_UHD + sdr_config->uhd = 1; + use_sdr = 1; +#else + fprintf(stderr, "UHD SDR support not compiled in!\n"); + exit(0); +#endif + *skip_args += 1; + break; + case OPT_SDR_SOAPY: +#ifdef HAVE_SOAPY + sdr_config->soapy = 1; + use_sdr = 1; +#else + fprintf(stderr, "SoapySDR support not compiled in!\n"); + exit(0); +#endif + *skip_args += 1; + break; + case OPT_SDR_CHANNEL: + sdr_config->channel = atoi(optarg); + *skip_args += 2; + break; + case OPT_SDR_DEVICE_ARGS: + sdr_config->device_args = strdup(optarg); + *skip_args += 2; + break; + case OPT_SDR_STREAM_ARGS: + sdr_config->stream_args = strdup(optarg); + *skip_args += 2; + break; + case OPT_SDR_TUNE_ARGS: + sdr_config->tune_args = strdup(optarg); + *skip_args += 2; + break; + case OPT_SDR_SAMPLERATE: + sdr_config->samplerate = atoi(optarg); + *skip_args += 2; + break; + case OPT_SDR_BANDWIDTH: + sdr_config->bandwidth = atof(optarg); + *skip_args += 2; + break; + case OPT_SDR_RX_ANTENNA: + sdr_config->rx_antenna = strdup(optarg); + *skip_args += 2; + break; + case OPT_SDR_TX_ANTENNA: + sdr_config->tx_antenna = strdup(optarg); + *skip_args += 2; + break; + case OPT_SDR_RX_GAIN: + sdr_config->rx_gain = atof(optarg); + *skip_args += 2; + break; + case OPT_SDR_TX_GAIN: + sdr_config->tx_gain = atof(optarg); + *skip_args += 2; + break; + case OPT_WRITE_IQ_RX_WAVE: + sdr_config->write_iq_rx_wave = strdup(optarg); + *skip_args += 2; + break; + case OPT_WRITE_IQ_TX_WAVE: + sdr_config->write_iq_tx_wave = strdup(optarg); + *skip_args += 2; + break; + case OPT_READ_IQ_RX_WAVE: + sdr_config->read_iq_rx_wave = strdup(optarg); + *skip_args += 2; + break; + case OPT_READ_IQ_TX_WAVE: + sdr_config->read_iq_tx_wave = strdup(optarg); + *skip_args += 2; + break; + case OPT_SDR_SWAP_LINKS: + sdr_config->swap_links = 1; + *skip_args += 1; + break; + case OPT_SDR_UHD_TX_TS: + sdr_config->uhd_tx_timestamps = 1; + *skip_args += 1; + break; + default: + return -1; + } + + return 0; +} + +int sdr_configure(int samplerate) +{ + if (!got_init) { + fprintf(stderr, "sdr_config_init was not called, please fix!\n"); + abort(); + } + + /* no sdr selected -> return 0 */ + if (!sdr_config->uhd && !sdr_config->soapy) + return 0; + + if ((sdr_config->uhd == 1 && sdr_config->soapy == 1)) { + fprintf(stderr, "You must choose which one you want: --sdr-uhd or --sdr-soapy\n"); + exit(0); + } + + if (sdr_config->samplerate == 0) + sdr_config->samplerate = samplerate; + if (sdr_config->bandwidth == 0.0) + sdr_config->bandwidth = (double)sdr_config->samplerate; + + /* sdr selected -> return 1 */ + return 1; +} + + diff --git a/src/libsdr/sdr_config.h b/src/libsdr/sdr_config.h new file mode 100644 index 0000000..b64424d --- /dev/null +++ b/src/libsdr/sdr_config.h @@ -0,0 +1,32 @@ + +typedef struct sdr_config { + int uhd, /* select UHD API */ + soapy; /* select Soapy SDR API */ + int channel; /* channel number */ + const char *device_args, /* arguments */ + *stream_args, + *tune_args; + int samplerate; /* ADC/DAC sample rate */ + double bandwidth; /* IF bandwidth */ + double tx_gain, /* gain */ + rx_gain; + const char *tx_antenna, /* list/override antennas */ + *rx_antenna; + const char *write_iq_tx_wave; /* wave recording and playback */ + const char *write_iq_rx_wave; + const char *read_iq_tx_wave; + const char *read_iq_rx_wave; + int swap_links; /* swap DL and UL frequency */ + int uhd_tx_timestamps; /* use UHD time stamps */ +} sdr_config_t; + +extern sdr_config_t *sdr_config; + +void sdr_config_init(void); +void sdr_config_print_help(void); +void sdr_config_print_hotkeys(void); +extern struct option sdr_config_long_options[]; +extern const char *sdr_config_optstring; +int sdr_config_opt_switch(int c, int *skip_args); +int sdr_configure(int samplerate); + diff --git a/src/libsdr/soapy.c b/src/libsdr/soapy.c new file mode 100644 index 0000000..5def101 --- /dev/null +++ b/src/libsdr/soapy.c @@ -0,0 +1,468 @@ +/* SoapySDR device access + * + * (C) 2017 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "soapy.h" +#include "../common/debug.h" + +extern int sdr_rx_overflow; + +static SoapySDRDevice *sdr = NULL; +SoapySDRStream *rxStream = NULL; +SoapySDRStream *txStream = NULL; +static int tx_samps_per_buff, rx_samps_per_buff; +static double samplerate; +static uint64_t rx_count = 0; +static uint64_t tx_count = 0; + +static int parse_args(SoapySDRKwargs *args, const char *_args_string) +{ + char *args_string = strdup(_args_string), *key, *val; + + memset(args, 0, sizeof(*args)); + while (args_string && *args_string) { + key = args_string; + val = strchr(key, '='); + if (!val) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Error parsing SDR args: No '=' after key\n"); + soapy_close(); + return -EIO; + } + *val++ = '\0'; + args_string = strchr(val, ','); + if (args_string) + *args_string++ = '\0'; + PDEBUG(DSOAPY, DEBUG_DEBUG, "SDR device args: key='%s' value='%s'\n", key, val); + SoapySDRKwargs_set(args, key, val); + } + + return 0; +} + +int soapy_open(size_t channel, const char *_device_args, const char *_stream_args, const char *_tune_args, const char *tx_antenna, const char *rx_antenna, double tx_frequency, double rx_frequency, double rate, double tx_gain, double rx_gain, double bandwidth) +{ + double got_frequency, got_rate, got_gain, got_bandwidth; + const char *got_antenna; + size_t num_channels; + SoapySDRKwargs device_args; + SoapySDRKwargs stream_args; + SoapySDRKwargs tune_args; + int rc; + + samplerate = rate; + + /* parsing ARGS */ + PDEBUG(DSOAPY, DEBUG_INFO, "Using device args \"%s\"\n", _device_args); + rc = parse_args(&device_args, _device_args); + if (rc < 0) + return rc; + PDEBUG(DSOAPY, DEBUG_INFO, "Using stream args \"%s\"\n", _stream_args); + rc = parse_args(&stream_args, _stream_args); + if (rc < 0) + return rc; + PDEBUG(DSOAPY, DEBUG_INFO, "Using tune args \"%s\"\n", _tune_args); + rc = parse_args(&tune_args, _tune_args); + if (rc < 0) + return rc; + + /* create SoapySDR device */ + sdr = SoapySDRDevice_make(&device_args); + if (!sdr) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to create SoapySDR\n"); + soapy_close(); + return -EIO; + } + + if (tx_frequency) { + /* get number of channels and check if requested channel is in range */ + num_channels = SoapySDRDevice_getNumChannels(sdr, SOAPY_SDR_TX); + PDEBUG(DSOAPY, DEBUG_DEBUG, "We have %d TX channel, selecting channel #%d\n", (int)num_channels, (int)channel); + if (channel >= num_channels) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Requested channel #%d (capable of TX) does not exist. Please select channel %d..%d!\n", (int)channel, 0, (int)num_channels - 1); + soapy_close(); + return -EIO; + } + + /* antenna */ + if (tx_antenna && tx_antenna[0]) { + if (!strcasecmp(tx_antenna, "list")) { + char **antennas; + size_t antennas_length; + int i; + antennas = SoapySDRDevice_listAntennas(sdr, SOAPY_SDR_TX, channel, &antennas_length); + if (!antennas) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to request list of TX antennas!\n"); + soapy_close(); + return -EIO; + } + for (i = 0; i < (int)antennas_length; i++) + PDEBUG(DSOAPY, DEBUG_NOTICE, "TX Antenna: '%s'\n", antennas[i]); + got_antenna = SoapySDRDevice_getAntenna(sdr, SOAPY_SDR_TX, channel); + PDEBUG(DSOAPY, DEBUG_NOTICE, "Default TX Antenna: '%s'\n", got_antenna); + soapy_close(); + return 1; + } + + if (SoapySDRDevice_setAntenna(sdr, SOAPY_SDR_TX, channel, tx_antenna) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX antenna to '%s'\n", tx_antenna); + soapy_close(); + return -EIO; + } + got_antenna = SoapySDRDevice_getAntenna(sdr, SOAPY_SDR_TX, channel); + if (!!strcasecmp(tx_antenna, got_antenna)) { + PDEBUG(DSOAPY, DEBUG_NOTICE, "Given TX antenna '%s' was accepted, but driver claims to use '%s'\n", tx_antenna, got_antenna); + soapy_close(); + return -EINVAL; + } + } + + /* set rate */ + if (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_TX, channel, rate) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX rate to %.0f Hz\n", rate); + soapy_close(); + return -EIO; + } + + /* see what rate actually is */ + got_rate = SoapySDRDevice_getSampleRate(sdr, SOAPY_SDR_TX, channel); + if (fabs(got_rate - rate) > 1.0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Given TX rate %.3f Hz is not supported, try %.3f Hz\n", rate, got_rate); + soapy_close(); + return -EINVAL; + } + + if (tx_gain) { + /* set gain */ + if (SoapySDRDevice_setGain(sdr, SOAPY_SDR_TX, channel, tx_gain) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX gain to %.0f\n", tx_gain); + soapy_close(); + return -EIO; + } + + /* see what gain actually is */ + got_gain = SoapySDRDevice_getGain(sdr, SOAPY_SDR_TX, channel); + if (fabs(got_gain - tx_gain) > 0.001) { + PDEBUG(DSOAPY, DEBUG_NOTICE, "Given TX gain %.3f is not supported, we use %.3f\n", tx_gain, got_gain); + tx_gain = got_gain; + } + } + + /* set frequency */ + if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_TX, channel, tx_frequency, &tune_args) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX frequency to %.0f Hz\n", tx_frequency); + soapy_close(); + return -EIO; + } + + /* see what frequency actually is */ + got_frequency = SoapySDRDevice_getFrequency(sdr, SOAPY_SDR_TX, channel); + if (fabs(got_frequency - tx_frequency) > 100.0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Given TX frequency %.0f Hz is not supported, try %.0f Hz\n", tx_frequency, got_frequency); + soapy_close(); + return -EINVAL; + } + + /* set bandwidth */ + if (SoapySDRDevice_setBandwidth(sdr, SOAPY_SDR_TX, channel, bandwidth) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX bandwidth to %.0f Hz\n", bandwidth); + soapy_close(); + return -EIO; + } + + /* see what bandwidth actually is */ + got_bandwidth = SoapySDRDevice_getBandwidth(sdr, SOAPY_SDR_TX, channel); + if (fabs(got_bandwidth - bandwidth) > 100.0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Given TX bandwidth %.0f Hz is not supported, try %.0f Hz\n", bandwidth, got_bandwidth); + soapy_close(); + return -EINVAL; + } + + /* set up streamer */ + if (SoapySDRDevice_setupStream(sdr, &txStream, SOAPY_SDR_TX, SOAPY_SDR_CF32, &channel, 1, &stream_args) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set TX streamer args\n"); + soapy_close(); + return -EIO; + } + + /* get buffer sizes */ + tx_samps_per_buff = SoapySDRDevice_getStreamMTU(sdr, txStream); + if (tx_samps_per_buff == 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to get TX streamer sample buffer\n"); + soapy_close(); + return -EIO; + } + } + + if (rx_frequency) { + /* get number of channels and check if requested channel is in range */ + num_channels = SoapySDRDevice_getNumChannels(sdr, SOAPY_SDR_RX); + PDEBUG(DSOAPY, DEBUG_DEBUG, "We have %d RX channel, selecting channel #%d\n", (int)num_channels, (int)channel); + if (channel >= num_channels) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Requested channel #%d (capable of RX) does not exist. Please select channel %d..%d!\n", (int)channel, 0, (int)num_channels - 1); + soapy_close(); + return -EIO; + } + + /* antenna */ + if (rx_antenna && rx_antenna[0]) { + if (!strcasecmp(rx_antenna, "list")) { + char **antennas; + size_t antennas_length; + int i; + antennas = SoapySDRDevice_listAntennas(sdr, SOAPY_SDR_RX, channel, &antennas_length); + if (!antennas) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to request list of RX antennas!\n"); + soapy_close(); + return -EIO; + } + for (i = 0; i < (int)antennas_length; i++) + PDEBUG(DSOAPY, DEBUG_NOTICE, "RX Antenna: '%s'\n", antennas[i]); + got_antenna = SoapySDRDevice_getAntenna(sdr, SOAPY_SDR_RX, channel); + PDEBUG(DSOAPY, DEBUG_NOTICE, "Default RX Antenna: '%s'\n", got_antenna); + soapy_close(); + return 1; + } + + if (SoapySDRDevice_setAntenna(sdr, SOAPY_SDR_RX, channel, rx_antenna) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX antenna to '%s'\n", rx_antenna); + soapy_close(); + return -EIO; + } + got_antenna = SoapySDRDevice_getAntenna(sdr, SOAPY_SDR_RX, channel); + if (!!strcasecmp(rx_antenna, got_antenna)) { + PDEBUG(DSOAPY, DEBUG_NOTICE, "Given RX antenna '%s' was accepted, but driver claims to use '%s'\n", rx_antenna, got_antenna); + soapy_close(); + return -EINVAL; + } + } + + /* set rate */ + if (SoapySDRDevice_setSampleRate(sdr, SOAPY_SDR_RX, channel, rate) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX rate to %.0f Hz\n", rate); + soapy_close(); + return -EIO; + } + + /* see what rate actually is */ + got_rate = SoapySDRDevice_getSampleRate(sdr, SOAPY_SDR_RX, channel); + if (fabs(got_rate - rate) > 1.0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Given RX rate %.3f Hz is not supported, try %.3f Hz\n", rate, got_rate); + soapy_close(); + return -EINVAL; + } + + if (rx_gain) { + /* set gain */ + if (SoapySDRDevice_setGain(sdr, SOAPY_SDR_RX, channel, rx_gain) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX gain to %.0f\n", rx_gain); + soapy_close(); + return -EIO; + } + + /* see what gain actually is */ + got_gain = SoapySDRDevice_getGain(sdr, SOAPY_SDR_RX, channel); + if (fabs(got_gain - rx_gain) > 0.001) { + PDEBUG(DSOAPY, DEBUG_NOTICE, "Given RX gain %.3f is not supported, we use %.3f\n", rx_gain, got_gain); + rx_gain = got_gain; + } + } + + /* set frequency */ + if (SoapySDRDevice_setFrequency(sdr, SOAPY_SDR_RX, channel, rx_frequency, &tune_args) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX frequency to %.0f Hz\n", rx_frequency); + soapy_close(); + return -EIO; + } + + /* see what frequency actually is */ + got_frequency = SoapySDRDevice_getFrequency(sdr, SOAPY_SDR_RX, channel); + if (fabs(got_frequency - rx_frequency) > 100.0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Given RX frequency %.0f Hz is not supported, try %.0f Hz\n", rx_frequency, got_frequency); + soapy_close(); + return -EINVAL; + } + + /* set bandwidth */ + if (SoapySDRDevice_setBandwidth(sdr, SOAPY_SDR_RX, channel, bandwidth) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX bandwidth to %.0f Hz\n", bandwidth); + soapy_close(); + return -EIO; + } + + /* see what bandwidth actually is */ + got_bandwidth = SoapySDRDevice_getBandwidth(sdr, SOAPY_SDR_RX, channel); + if (fabs(got_bandwidth - bandwidth) > 100.0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Given RX bandwidth %.0f Hz is not supported, try %.0f Hz\n", bandwidth, got_bandwidth); + soapy_close(); + return -EINVAL; + } + + /* set up streamer */ + if (SoapySDRDevice_setupStream(sdr, &rxStream, SOAPY_SDR_RX, SOAPY_SDR_CF32, &channel, 1, &stream_args) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to set RX streamer args\n"); + soapy_close(); + return -EIO; + } + + /* get buffer sizes */ + rx_samps_per_buff = SoapySDRDevice_getStreamMTU(sdr, rxStream); + if (rx_samps_per_buff == 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to get RX streamer sample buffer\n"); + soapy_close(); + return -EIO; + } + } + + return 0; +} + +/* start streaming */ +int soapy_start(void) +{ + /* enable rx stream */ + if (SoapySDRDevice_activateStream(sdr, rxStream, 0, 0, 0) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to issue RX stream command\n"); + return -EIO; + } + + /* enable tx stream */ + if (SoapySDRDevice_activateStream(sdr, txStream, 0, 0, 0) != 0) { + PDEBUG(DSOAPY, DEBUG_ERROR, "Failed to issue TX stream command\n"); + return -EIO; + } + return 0; +} + +void soapy_close(void) +{ + PDEBUG(DSOAPY, DEBUG_DEBUG, "Clean up SoapySDR\n"); + if (txStream) { + SoapySDRDevice_deactivateStream(sdr, txStream, 0, 0); + SoapySDRDevice_closeStream(sdr, txStream); + txStream = NULL; + } + if (rxStream) { + SoapySDRDevice_deactivateStream(sdr, rxStream, 0, 0); + SoapySDRDevice_closeStream(sdr, rxStream); + rxStream = NULL; + } + if (sdr) { + SoapySDRDevice_unmake(sdr); + sdr = NULL; + } +} + +int soapy_send(float *buff, int num) +{ + const void *buffs_ptr[1]; + int chunk; + int sent = 0, count; + int flags = 0; + + while (num) { + chunk = num; + if (chunk > tx_samps_per_buff) + chunk = tx_samps_per_buff; + /* create tx metadata */ + buffs_ptr[0] = buff; + count = SoapySDRDevice_writeStream(sdr, txStream, buffs_ptr, chunk, &flags, 0, 1000000); + if (count <= 0) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to write to TX streamer (error=%d)\n", count); + break; + } + + sent += count; + buff += count * 2; + num -= count; + } + /* increment tx counter */ + tx_count += sent; + + return sent; +} + +/* read what we got, return 0, if buffer is empty, otherwise return the number of samples */ +int soapy_receive(float *buff, int max) +{ + void *buffs_ptr[1]; + int got = 0, count; + long long timeNs; + int flags = 0; + + while (1) { + if (max < rx_samps_per_buff) { + /* no more space this time */ + sdr_rx_overflow = 1; + break; + } + /* read RX stream */ + buffs_ptr[0] = buff; + count = SoapySDRDevice_readStream(sdr, rxStream, buffs_ptr, rx_samps_per_buff, &flags, &timeNs, 0); + if (count > 0) { + /* commit received data to buffer */ + got += count; + buff += count * 2; + max -= count; + } else { + /* got nothing this time */ + break; + } + } + /* update current rx time */ + rx_count += got; + + return got; +} + +/* estimate number of samples that can be sent */ +int soapy_get_tosend(int latspl) +{ + int tosend; + + /* we need the rx time stamp to determine how much data is already sent in advance */ + if (rx_count == 0) + return 0; + + /* if we have not yet sent any data, we set initial tx time stamp */ + if (tx_count == 0) + tx_count = rx_count; + + /* we check how advance our transmitted time stamp is */ + tosend = latspl - (tx_count - rx_count); + /* in case of underrun: */ + if (tosend > latspl) { +// It is normal that we have underruns, prior inital filling of buffer. +// FIXME: better solution to detect underrun +// PDEBUG(DSOAPY, DEBUG_ERROR, "SDR TX underrun!\n"); + tosend = 0; + tx_count = rx_count; + } + if (tosend < 0) + tosend = 0; + + return tosend; +} + diff --git a/src/libsdr/soapy.h b/src/libsdr/soapy.h new file mode 100644 index 0000000..702890b --- /dev/null +++ b/src/libsdr/soapy.h @@ -0,0 +1,8 @@ + +int soapy_open(size_t channel, const char *_device_args, const char *_stream_args, const char *_tune_args, const char *tx_antenna, const char *rx_antenna, double tx_frequency, double rx_frequency, double rate, double tx_gain, double rx_gain, double bandwidth); +int soapy_start(void); +void soapy_close(void); +int soapy_send(float *buff, int num); +int soapy_receive(float *buff, int max); +int soapy_get_tosend(int latspl); + diff --git a/src/libsdr/uhd.c b/src/libsdr/uhd.c new file mode 100644 index 0000000..185fdf6 --- /dev/null +++ b/src/libsdr/uhd.c @@ -0,0 +1,585 @@ +/* UHD device access + * + * (C) 2017 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "uhd.h" +#include "../common/debug.h" + +/* use to TX time stamp */ +//#define TX_TIMESTAMP + +extern int sdr_rx_overflow; + +static uhd_usrp_handle usrp = NULL; +static uhd_tx_streamer_handle tx_streamer = NULL; +static uhd_rx_streamer_handle rx_streamer = NULL; +static uhd_tx_metadata_handle tx_metadata = NULL; +static uhd_rx_metadata_handle rx_metadata = NULL; +static uhd_tune_request_t tune_request; +static uhd_tune_result_t tune_result; +static uhd_stream_args_t stream_args; +static uhd_stream_cmd_t stream_cmd; +static size_t tx_samps_per_buff, rx_samps_per_buff; +static double samplerate; +static time_t rx_time_secs = 0; +static double rx_time_fract_sec = 0.0; +static time_t tx_time_secs = 0; +static double tx_time_fract_sec = 0.0; +static int tx_timestamps; + +int uhd_open(size_t channel, const char *_device_args, const char *_stream_args, const char *_tune_args, const char *tx_antenna, const char *rx_antenna, double tx_frequency, double rx_frequency, double rate, double tx_gain, double rx_gain, double bandwidth, int _tx_timestamps) +{ + uhd_error error; + double got_frequency, got_rate, got_gain, got_bandwidth; + char got_antenna[64]; + + samplerate = rate; + tx_timestamps = _tx_timestamps; + + PDEBUG(DUHD, DEBUG_INFO, "Using device args \"%s\"\n", _device_args); + PDEBUG(DUHD, DEBUG_INFO, "Using stream args \"%s\"\n", _stream_args); + PDEBUG(DUHD, DEBUG_INFO, "Using tune args \"%s\"\n", _tune_args); + + /* create USRP */ + PDEBUG(DUHD, DEBUG_INFO, "Creating USRP with args \"%s\"...\n", _device_args); + error = uhd_usrp_make(&usrp, _device_args); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to create USRP\n"); + uhd_close(); + return -EIO; + } + + if (tx_frequency) { + /* antenna */ + if (tx_antenna && tx_antenna[0]) { + if (!strcasecmp(tx_antenna, "list")) { + uhd_string_vector_handle antennas; + size_t antennas_length; + int i; + error = uhd_string_vector_make(&antennas); + if (error) { + tx_vector_error: + PDEBUG(DUHD, DEBUG_ERROR, "Failed to hande UHD vector, please fix!\n"); + uhd_close(); + return -EIO; + } + error = uhd_usrp_get_tx_antennas(usrp, channel, &antennas); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to request list of TX antennas!\n"); + uhd_close(); + return -EIO; + } + error = uhd_string_vector_size(antennas, &antennas_length); + if (error) + goto tx_vector_error; + for (i = 0; i < (int)antennas_length; i++) { + error = uhd_string_vector_at(antennas, i, got_antenna, sizeof(got_antenna)); + if (error) + goto tx_vector_error; + PDEBUG(DUHD, DEBUG_NOTICE, "TX Antenna: '%s'\n", got_antenna); + } + uhd_string_vector_free(&antennas); + error = uhd_usrp_get_tx_antenna(usrp, channel, got_antenna, sizeof(got_antenna)); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX antenna\n"); + uhd_close(); + return -EINVAL; + } + PDEBUG(DUHD, DEBUG_NOTICE, "Default TX Antenna: '%s'\n", got_antenna); + uhd_close(); + return 1; + } + error = uhd_usrp_set_tx_antenna(usrp, tx_antenna, channel); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX antenna to '%s'\n", tx_antenna); + uhd_close(); + return -EIO; + } + error = uhd_usrp_get_tx_antenna(usrp, channel, got_antenna, sizeof(got_antenna)); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX antenna\n"); + uhd_close(); + return -EINVAL; + } + if (!!strcasecmp(tx_antenna, got_antenna)) { + PDEBUG(DUHD, DEBUG_NOTICE, "Given TX antenna '%s' was accepted, but driver claims to use '%s'\n", tx_antenna, got_antenna); + uhd_close(); + return -EINVAL; + } + } + + /* create streamers */ + error = uhd_tx_streamer_make(&tx_streamer); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to create TX streamer\n"); + uhd_close(); + return -EIO; + } + + /* set rate */ + error = uhd_usrp_set_tx_rate(usrp, rate, channel); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX rate to %.0f Hz\n", rate); + uhd_close(); + return -EIO; + } + + /* see what rate actually is */ + error = uhd_usrp_get_tx_rate(usrp, channel, &got_rate); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX rate\n"); + uhd_close(); + return -EIO; + } + if (fabs(got_rate - rate) > 0.001) { + PDEBUG(DUHD, DEBUG_ERROR, "Given TX rate %.0f Hz is not supported, try %.0f Hz\n", rate, got_rate); + uhd_close(); + return -EINVAL; + } + + /* set gain */ + error = uhd_usrp_set_tx_gain(usrp, tx_gain, channel, ""); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX gain to %.0f\n", tx_gain); + uhd_close(); + return -EIO; + } + + /* see what gain actually is */ + error = uhd_usrp_get_tx_gain(usrp, channel, "", &got_gain); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX gain\n"); + uhd_close(); + return -EIO; + } + if (fabs(got_gain - tx_gain) > 0.001) { + PDEBUG(DUHD, DEBUG_NOTICE, "Given TX gain %.0f is not supported, we use %.0f\n", tx_gain, got_gain); + tx_gain = got_gain; + } + + /* set frequency */ + memset(&tune_request, 0, sizeof(tune_request)); + tune_request.target_freq = tx_frequency; + tune_request.rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO; + tune_request.dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO; + tune_request.args = strdup(_tune_args); + error = uhd_usrp_set_tx_freq(usrp, &tune_request, channel, &tune_result); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX frequeny to %.0f Hz\n", tx_frequency); + uhd_close(); + return -EIO; + } + + /* see what frequency actually is */ + error = uhd_usrp_get_tx_freq(usrp, channel, &got_frequency); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX frequency\n"); + uhd_close(); + return -EIO; + } + if (fabs(got_frequency - tx_frequency) > 100.0) { + PDEBUG(DUHD, DEBUG_ERROR, "Given TX frequency %.0f Hz is not supported, try %.0f Hz\n", tx_frequency, got_frequency); + uhd_close(); + return -EINVAL; + } + + /* set bandwidth */ + if (uhd_usrp_set_tx_bandwidth(usrp, bandwidth, channel) != 0) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX bandwidth to %.0f Hz\n", bandwidth); + uhd_close(); + return -EIO; + } + + /* see what bandwidth actually is */ + error = uhd_usrp_get_tx_bandwidth(usrp, channel, &got_bandwidth); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX bandwidth\n"); + uhd_close(); + return -EIO; + } + if (fabs(got_bandwidth - bandwidth) > 0.001) { + PDEBUG(DUHD, DEBUG_ERROR, "Given TX bandwidth %.0f Hz is not supported, try %.0f Hz\n", bandwidth, got_bandwidth); + uhd_close(); + return -EINVAL; + } + + /* set up streamer */ + memset(&stream_args, 0, sizeof(stream_args)); + stream_args.cpu_format = "fc32"; + stream_args.otw_format = "sc16"; + stream_args.args = strdup(_stream_args); + stream_args.channel_list = &channel; + stream_args.n_channels = 1; + error = uhd_usrp_get_tx_stream(usrp, &stream_args, tx_streamer); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set TX streamer args\n"); + uhd_close(); + return -EIO; + } + + /* get buffer sizes */ + error = uhd_tx_streamer_max_num_samps(tx_streamer, &tx_samps_per_buff); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get TX streamer sample buffer\n"); + uhd_close(); + return -EIO; + } + } + + if (rx_frequency) { + /* antenna */ + if (rx_antenna && rx_antenna[0]) { + if (!strcasecmp(rx_antenna, "list")) { + uhd_string_vector_handle antennas; + size_t antennas_length; + int i; + error = uhd_string_vector_make(&antennas); + if (error) { + rx_vector_error: + PDEBUG(DUHD, DEBUG_ERROR, "Failed to hande UHD vector, please fix!\n"); + uhd_close(); + return -EIO; + } + error = uhd_usrp_get_rx_antennas(usrp, channel, &antennas); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to request list of RX antennas!\n"); + uhd_close(); + return -EIO; + } + error = uhd_string_vector_size(antennas, &antennas_length); + if (error) + goto rx_vector_error; + for (i = 0; i < (int)antennas_length; i++) { + error = uhd_string_vector_at(antennas, i, got_antenna, sizeof(got_antenna)); + if (error) + goto rx_vector_error; + PDEBUG(DUHD, DEBUG_NOTICE, "RX Antenna: '%s'\n", got_antenna); + } + uhd_string_vector_free(&antennas); + error = uhd_usrp_get_rx_antenna(usrp, channel, got_antenna, sizeof(got_antenna)); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX antenna\n"); + uhd_close(); + return -EINVAL; + } + PDEBUG(DUHD, DEBUG_NOTICE, "Default RX Antenna: '%s'\n", got_antenna); + uhd_close(); + return 1; + } + error = uhd_usrp_set_rx_antenna(usrp, rx_antenna, channel); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX antenna to '%s'\n", rx_antenna); + uhd_close(); + return -EIO; + } + error = uhd_usrp_get_rx_antenna(usrp, channel, got_antenna, sizeof(got_antenna)); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX antenna\n"); + uhd_close(); + return -EINVAL; + } + if (!!strcasecmp(rx_antenna, got_antenna)) { + PDEBUG(DUHD, DEBUG_NOTICE, "Given RX antenna '%s' was accepted, but driver claims to use '%s'\n", rx_antenna, got_antenna); + uhd_close(); + return -EINVAL; + } + } + /* create streamers */ + error = uhd_rx_streamer_make(&rx_streamer); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to create RX streamer\n"); + uhd_close(); + return -EIO; + } + + /* create metadata */ + error = uhd_rx_metadata_make(&rx_metadata); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to create RX metadata\n"); + uhd_close(); + return -EIO; + } + + /* set rate */ + error = uhd_usrp_set_rx_rate(usrp, rate, channel); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX rate to %.0f Hz\n", rate); + uhd_close(); + return -EIO; + } + + /* see what rate actually is */ + error = uhd_usrp_get_rx_rate(usrp, channel, &got_rate); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX rate\n"); + uhd_close(); + return -EIO; + } + if (fabs(got_rate - rate) > 0.001) { + PDEBUG(DUHD, DEBUG_ERROR, "Given RX rate %.0f Hz is not supported, try %.0f Hz\n", rate, got_rate); + uhd_close(); + return -EINVAL; + } + + /* set gain */ + error = uhd_usrp_set_rx_gain(usrp, rx_gain, channel, ""); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX gain to %.0f\n", rx_gain); + uhd_close(); + return -EIO; + } + + /* see what gain actually is */ + error = uhd_usrp_get_rx_gain(usrp, channel, "", &got_gain); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX gain\n"); + uhd_close(); + return -EIO; + } + if (fabs(got_gain - rx_gain) > 0.001) { + PDEBUG(DUHD, DEBUG_NOTICE, "Given RX gain %.3f is not supported, we use %.3f\n", rx_gain, got_gain); + rx_gain = got_gain; + } + + /* set frequency */ + memset(&tune_request, 0, sizeof(tune_request)); + tune_request.target_freq = rx_frequency; + tune_request.rf_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO; + tune_request.dsp_freq_policy = UHD_TUNE_REQUEST_POLICY_AUTO; + tune_request.args = strdup(_tune_args); + error = uhd_usrp_set_rx_freq(usrp, &tune_request, channel, &tune_result); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX frequeny to %.0f Hz\n", rx_frequency); + uhd_close(); + return -EIO; + } + + /* see what frequency actually is */ + error = uhd_usrp_get_rx_freq(usrp, channel, &got_frequency); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX frequency\n"); + uhd_close(); + return -EIO; + } + if (fabs(got_frequency - rx_frequency) > 100.0) { + PDEBUG(DUHD, DEBUG_ERROR, "Given RX frequency %.0f Hz is not supported, try %.0f Hz\n", rx_frequency, got_frequency); + uhd_close(); + return -EINVAL; + } + + /* set bandwidth */ + if (uhd_usrp_set_rx_bandwidth(usrp, bandwidth, channel) != 0) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX bandwidth to %.0f Hz\n", bandwidth); + uhd_close(); + return -EIO; + } + + /* see what bandwidth actually is */ + error = uhd_usrp_get_rx_bandwidth(usrp, channel, &got_bandwidth); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX bandwidth\n"); + uhd_close(); + return -EIO; + } + if (fabs(got_bandwidth - bandwidth) > 0.001) { + PDEBUG(DUHD, DEBUG_ERROR, "Given RX bandwidth %.0f Hz is not supported, try %.0f Hz\n", bandwidth, got_bandwidth); + uhd_close(); + return -EINVAL; + } + + /* set up streamer */ + memset(&stream_args, 0, sizeof(stream_args)); + stream_args.cpu_format = "fc32"; + stream_args.otw_format = "sc16"; + stream_args.args = strdup(_stream_args); + stream_args.channel_list = &channel; + stream_args.n_channels = 1; + error = uhd_usrp_get_rx_stream(usrp, &stream_args, rx_streamer); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to set RX streamer args\n"); + uhd_close(); + return -EIO; + } + + /* get buffer sizes */ + error = uhd_rx_streamer_max_num_samps(rx_streamer, &rx_samps_per_buff); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to get RX streamer sample buffer\n"); + uhd_close(); + return -EIO; + } + } + + return 0; +} + +/* start streaming */ +int uhd_start(void) +{ + uhd_error error; + + /* enable rx stream */ + memset(&stream_cmd, 0, sizeof(stream_cmd)); + stream_cmd.stream_mode = UHD_STREAM_MODE_START_CONTINUOUS; + stream_cmd.stream_now = true; + error = uhd_rx_streamer_issue_stream_cmd(rx_streamer, &stream_cmd); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to issue RX stream command\n"); + return -EIO; + } + return 0; +} + +void uhd_close(void) +{ + PDEBUG(DUHD, DEBUG_DEBUG, "Clean up UHD\n"); + if (tx_metadata) + uhd_tx_metadata_free(&tx_metadata); + if (rx_metadata) + uhd_rx_metadata_free(&rx_metadata); + if (tx_streamer) + uhd_tx_streamer_free(&tx_streamer); + if (rx_streamer) + uhd_rx_streamer_free(&rx_streamer); + if (usrp) + uhd_usrp_free(&usrp); +} + +int uhd_send(float *buff, int num) +{ + const void *buffs_ptr[1]; + int chunk; + size_t sent = 0, count; + uhd_error error; + + while (num) { + chunk = num; + if (chunk > (int)tx_samps_per_buff) + chunk = (int)tx_samps_per_buff; + /* create tx metadata */ + if (tx_timestamps) + error = uhd_tx_metadata_make(&tx_metadata, true, tx_time_secs, tx_time_fract_sec, false, false); + else + error = uhd_tx_metadata_make(&tx_metadata, false, 0, 0.0, false, false); + if (error) + PDEBUG(DUHD, DEBUG_ERROR, "Failed to create TX metadata\n"); + buffs_ptr[0] = buff; + count = 0; + error = uhd_tx_streamer_send(tx_streamer, buffs_ptr, chunk, &tx_metadata, 1.0, &count); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to write to TX streamer\n"); + break; + } + if (count == 0) + break; + + /* increment time stamp */ + tx_time_fract_sec += (double)count / samplerate; + while (tx_time_fract_sec >= 1.0) { + tx_time_secs++; + tx_time_fract_sec -= 1.0; + } +//printf("adv=%.3f\n", ((double)tx_time_secs + tx_time_fract_sec) - ((double)rx_time_secs + rx_time_fract_sec)); + + sent += count; + buff += count * 2; + num -= count; + } + + return sent; +} + +/* read what we got, return 0, if buffer is empty, otherwise return the number of samples */ +int uhd_receive(float *buff, int max) +{ + void *buffs_ptr[1]; + size_t got = 0, count; + uhd_error error; + + while (1) { + if (max < (int)rx_samps_per_buff) { + /* no more space this time */ + sdr_rx_overflow = 1; + break; + } + /* read RX stream */ + buffs_ptr[0] = buff; + count = 0; + error = uhd_rx_streamer_recv(rx_streamer, buffs_ptr, rx_samps_per_buff, &rx_metadata, 0.0, false, &count); + if (error) { + PDEBUG(DUHD, DEBUG_ERROR, "Failed to read from UHD device.\n"); + break; + } + if (count) { + /* get time stamp of received RX packet */ + uhd_rx_metadata_time_spec(rx_metadata, &rx_time_secs, &rx_time_fract_sec); + /* commit received data to buffer */ + got += count; + buff += count * 2; + max -= count; + } else { + /* got nothing this time */ + break; + } + } + + return got; +} + +/* estimate number of samples that can be sent */ +int uhd_get_tosend(int latspl) +{ + double advance; + int tosend; + + /* we need the rx time stamp to determine how much data is already sent in advance */ + if (rx_time_secs == 0 && rx_time_fract_sec == 0.0) + return 0; + + /* if we have not yet sent any data, we set initial tx time stamp */ + if (tx_time_secs == 0 && tx_time_fract_sec == 0.0) { + tx_time_secs = rx_time_secs; + tx_time_fract_sec = rx_time_fract_sec; + if (tx_timestamps) { + tx_time_fract_sec += (double)latspl / samplerate; + if (tx_time_fract_sec >= 1.0) { + tx_time_fract_sec -= 1.0; + tx_time_secs++; + } + } + } + + /* we check how advance our transmitted time stamp is */ + advance = ((double)tx_time_secs + tx_time_fract_sec) - ((double)rx_time_secs + rx_time_fract_sec); + /* in case of underrun: */ + if (advance < 0) + advance = 0; + tosend = latspl - (int)(advance * samplerate); + if (tosend < 0) + tosend = 0; + + return tosend; +} + diff --git a/src/libsdr/uhd.h b/src/libsdr/uhd.h new file mode 100644 index 0000000..60eb94f --- /dev/null +++ b/src/libsdr/uhd.h @@ -0,0 +1,8 @@ + +int uhd_open(size_t channel, const char *_device_args, const char *_stream_args, const char *_tune_args, const char *tx_antenna, const char *rx_antenna, double tx_frequency, double rx_frequency, double rate, double tx_gain, double rx_gain, double bandwidth, int _tx_timestamps); +int uhd_start(void); +void uhd_close(void); +int uhd_send(float *buff, int num); +int uhd_receive(float *buff, int max); +int uhd_get_tosend(int latspl); + diff --git a/src/nmt/Makefile.am b/src/nmt/Makefile.am index d6ad99c..2c425ad 100644 --- a/src/nmt/Makefile.am +++ b/src/nmt/Makefile.am @@ -36,7 +36,6 @@ nmt_LDADD = \ $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -44,3 +43,9 @@ nmt_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +nmt_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a +endif + diff --git a/src/r2000/Makefile.am b/src/r2000/Makefile.am index 4066ff7..0b95757 100644 --- a/src/r2000/Makefile.am +++ b/src/r2000/Makefile.am @@ -24,7 +24,6 @@ radiocom2000_LDADD = \ $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -32,3 +31,9 @@ radiocom2000_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +radiocom2000_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a +endif + diff --git a/src/tacs/Makefile.am b/src/tacs/Makefile.am index 62d681f..1d6d43a 100644 --- a/src/tacs/Makefile.am +++ b/src/tacs/Makefile.am @@ -21,10 +21,8 @@ tacs_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -32,3 +30,10 @@ tacs_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +tacs_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + diff --git a/src/test/Makefile.am b/src/test/Makefile.am index dfdfd5f..6e1b2bf 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -19,10 +19,8 @@ test_filter_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -30,6 +28,13 @@ test_filter_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +test_filter_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + test_compandor_SOURCES = dummy.x test_compandor.c test_compandor_LDADD = \ @@ -52,10 +57,8 @@ test_emphasis_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -63,6 +66,13 @@ test_emphasis_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +test_emphasis_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + test_dms_SOURCES = test_dms.c dummy.c test_dms_LDADD = \ @@ -74,10 +84,8 @@ test_dms_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -85,6 +93,13 @@ test_dms_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +test_dms_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + test_sms_SOURCES = dummy.c test_sms.c test_sms_LDADD = \ @@ -96,10 +111,8 @@ test_sms_LDADD = \ $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libsamplerate/libsamplerate.a \ $(top_builddir)/src/libemphasis/libemphasis.a \ - $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(top_builddir)/src/libmncc/libmncc.a \ $(top_builddir)/src/libsound/libsound.a \ $(ALSA_LIBS) \ @@ -107,6 +120,13 @@ test_sms_LDADD = \ $(SOAPY_LIBS) \ -lm +if HAVE_SDR +test_sms_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a \ + $(top_builddir)/src/libfm/libfm.a +endif + test_performance_SOURCES = dummy.c test_performance.c test_performance_LDADD = \ diff --git a/src/tv/Makefile.am b/src/tv/Makefile.am index da94296..d1677e4 100644 --- a/src/tv/Makefile.am +++ b/src/tv/Makefile.am @@ -16,11 +16,9 @@ osmotv_LDADD = \ $(COMMON_LA) \ $(top_builddir)/src/libimage/libimage.a \ $(top_builddir)/src/common/libcommon.a \ - $(top_builddir)/src/libtimer/libtimer.a \ $(top_builddir)/src/libfm/libfm.a \ $(top_builddir)/src/libfilter/libfilter.a \ $(top_builddir)/src/libwave/libwave.a \ - $(top_builddir)/src/libfft/libfft.a \ $(ALSA_LIBS) \ $(UHD_LIBS) \ $(SOAPY_LIBS) \ @@ -28,15 +26,16 @@ osmotv_LDADD = \ -lm if HAVE_SDR -AM_CPPFLAGS += -DHAVE_SDR +osmotv_LDADD += \ + $(top_builddir)/src/libsdr/libsdr.a \ + $(top_builddir)/src/libfft/libfft.a endif -if HAVE_UHD -AM_CPPFLAGS += -DHAVE_UHD -endif +osmotv_LDADD += \ + $(top_builddir)/src/libtimer/libtimer.a -if HAVE_SOAPY -AM_CPPFLAGS += -DHAVE_SOAPY +if HAVE_SDR +AM_CPPFLAGS += -DHAVE_SDR endif if ENABLE_MAGICK diff --git a/src/tv/main.c b/src/tv/main.c index 8663952..ed4558f 100644 --- a/src/tv/main.c +++ b/src/tv/main.c @@ -34,8 +34,8 @@ enum paging_signal; #include "../libimage/img.h" #include "../common/debug.h" #ifdef HAVE_SDR -#include "../common/sdr_config.h" -#include "../common/sdr.h" +#include "../libsdr/sdr_config.h" +#include "../libsdr/sdr.h" #endif #include "bas.h" #include "tv_modulate.h" -- cgit v1.2.3