Add spectrum output and JSON syntax for third-party GUIs

master
pabr 2017-12-10 22:50:42 +01:00
rodzic fe0f0a1e9d
commit 20acc20b10
4 zmienionych plików z 209 dodań i 29 usunięć

Wyświetl plik

@ -1,4 +1,5 @@
HEAD
* leandvb: Spectrum output, JSON syntax.
* leandvb, leandvbtx: DVB-S with non-standard constellations (with --viterbi).
* leandvb: Support all DVB-S code rates with --viterbi.
* leandvbtx: Support all DVB-S code rates.

Wyświetl plik

@ -69,6 +69,8 @@ struct config {
int fd_info; // FD for status information in text format, or -1
float Finfo; // Desired refresh rate on fd_info (Hz)
int fd_const; // FD for constellation and symbols, or -1
int fd_spectrum; // FD for spectrum data, or -1
bool json; // Use JSON syntax
config()
: verbose(false),
@ -110,7 +112,9 @@ struct config {
linger(false),
fd_info(-1),
Finfo(5),
fd_const(-1) {
fd_const(-1),
fd_spectrum(-1),
json(false) {
}
};
@ -119,6 +123,20 @@ int decimation(float Fin, float Fout) {
return max(d, 1);
}
void output_initial_info(FILE *f, config &cfg) {
const char *quote = cfg.json ? "\"" : "";
static const char *standard_names[] = {
[config::DVB_S]="DVB-S",
[config::DVB_S2]="DVB-S2",
};
fprintf(f, "STANDARD %s%s%s\n", quote, standard_names[cfg.standard], quote);
fprintf(f, "CONSTELLATION %s%s%s\n",
quote, cstln_names[cfg.constellation], quote);
fec_spec *fs = &fec_specs[cfg.fec];
fprintf(f, "CR %s%d/%d%s\n", quote, fs->bits_in, fs->bits_out, quote);
fprintf(f, "SR %f\n", cfg.Fm);
}
int run(config &cfg) {
int w_timeline = 512, h_timeline = 256;
@ -293,6 +311,20 @@ int run(config &cfg) {
r_cnr->decimation = decimation(cfg.Fs, 1); // 1 Hz
}
// SPECTRUM
pipebuf<f32[1024]> *p_spectrum = NULL;
if ( cfg.fd_spectrum ) {
if ( cfg.verbose )
fprintf(stderr, "Measuring SPD\n");
p_spectrum = new pipebuf<float[1024]>(&sch, "spectrum", BUF_SLOW);
spectrum<f32> *r_spectrum =
new spectrum<f32>(&sch, *p_preprocessed, *p_spectrum);
r_spectrum->decimation = decimation(cfg.Fs, 1); // 1 Hz
r_spectrum->kavg = 0.5;
}
// FILTERING
if ( cfg.verbose ) fprintf(stderr, "Roll-off %g\n", cfg.rolloff);
@ -561,9 +593,8 @@ int run(config &cfg) {
new file_printer<float>(&sch, "VBER %.6f\n", p_vber, cfg.fd_info);
// Output constants immediately
FILE *f = fdopen(cfg.fd_info, "w");
static const char *fec_names[] = { "1/2", "2/3", "3/4", "5/6", "7/8" };
fprintf(f, "CR %s\n", fec_names[cfg.fec]);
fprintf(f, "SR %f\n", cfg.Fm);
if ( ! f ) fatal("fdopen(fd_info)");
output_initial_info(f, cfg);
fflush(f);
}
if ( cfg.fd_const >= 0 ) {
@ -571,14 +602,40 @@ int run(config &cfg) {
if ( c ) {
// Output constellation immediately
FILE *f = fdopen(cfg.fd_const, "w");
fprintf(f, "CONST %d", c->nsymbols);
for ( int i=0; i<c->nsymbols; ++i )
fprintf(f, " %d,%d", c->symbols[i].re, c->symbols[i].im);
fprintf(f, "\n");
if ( ! f ) fatal("fdopen(fd_const)");
if ( cfg.json ) {
fprintf(f, "CONST [");
for ( int i=0; i<c->nsymbols; ++i )
fprintf(f, "%s[%d,%d]", i?",":"",
c->symbols[i].re, c->symbols[i].im);
fprintf(f, "]\n");
} else {
fprintf(f, "CONST %d", c->nsymbols);
for ( int i=0; i<c->nsymbols; ++i )
fprintf(f, " %d,%d", c->symbols[i].re, c->symbols[i].im);
fprintf(f, "\n");
}
fflush(f);
}
new file_carrayprinter<f32>(&sch, "SYMBOLS %d", " %.0f,%.0f", "\n",
p_sampled, cfg.fd_const);
file_carrayprinter<f32> *symbol_printer;
if ( cfg.json )
symbol_printer = new file_carrayprinter<f32>
(&sch, "SYMBOLS [", "[%.0f,%.0f]", ",", "]\n", p_sampled, cfg.fd_const);
else
symbol_printer = new file_carrayprinter<f32>
(&sch, "SYMBOLS %d", " %.0f,%.0f", "", "\n", p_sampled, cfg.fd_const);
symbol_printer->fixed_size = 128;
}
if ( cfg.fd_spectrum >= 0 ) {
file_vectorprinter<f32,1024> *spectrum_printer;
if ( cfg.json )
spectrum_printer = new file_vectorprinter<f32,1024>
(&sch, "SPECTRUM [", "%.3f", ",", "]\n", *p_spectrum, cfg.fd_spectrum);
else
spectrum_printer = new file_vectorprinter<f32,1024>
(&sch, "SPECTRUM %d", " %.3f", "", "\n", *p_spectrum, cfg.fd_spectrum);
(void)spectrum_printer;
}
// TIMELINE SCOPE
@ -830,14 +887,19 @@ int run_highspeed(config &cfg) {
new file_printer<float>(&sch, "VBER %.6f\n", p_vber, cfg.fd_info);
// Output constants immediately
FILE *f = fdopen(cfg.fd_info, "w");
static const char *fec_names[] = { "1/2", "2/3", "3/4", "5/6", "7/8" };
fprintf(f, "CR %s\n", fec_names[cfg.fec]);
fprintf(f, "SR %f\n", cfg.Fm);
if ( ! f ) fatal("fdopen(fd_info)");
output_initial_info(f, cfg);
fflush(f);
}
if ( cfg.fd_const >= 0 ) {
new file_carrayprinter<u8>(&sch, "SYMBOLS %d", " %d,%d", "\n",
p_sampled, cfg.fd_const);
file_carrayprinter<u8> *symbol_printer;
if ( cfg.json )
symbol_printer = new file_carrayprinter<u8>
(&sch, "SYMBOLS [", "[%.0f,%.0f]", ",", "]\n", p_sampled, cfg.fd_const);
else
symbol_printer = new file_carrayprinter<u8>
(&sch, "SYMBOLS %d", " %d,%d", "", "\n", p_sampled, cfg.fd_const);
symbol_printer->fixed_size = 128;
}
// TIMELINE SCOPE
@ -952,6 +1014,8 @@ void usage(const char *name, FILE *f, int c) {
" -d Output debugging info during operation\n"
" --fd-info NUM Output demodulator status to file descriptor\n"
" --fd-const NUM Output constellation and symbols to file descr\n"
" --fd-spectrum NUM Output spectrum to file descr\n"
" --json Use JSON syntax\n"
);
#ifdef GUI
fprintf(f,
@ -1109,6 +1173,10 @@ int main(int argc, const char *argv[]) {
cfg.fd_info = atoi(argv[++i]);
else if ( ! strcmp(argv[i], "--fd-const") && i+1<argc )
cfg.fd_const = atoi(argv[++i]);
else if ( ! strcmp(argv[i], "--fd-spectrum") && i+1<argc )
cfg.fd_spectrum = atoi(argv[++i]);
else if ( ! strcmp(argv[i], "--json") )
cfg.json = true;
else
usage(argv[0], stderr, 1);
}

Wyświetl plik

@ -115,7 +115,7 @@ private:
int phase;
};
// [file_listprinter] writes all data available from a [pipebuf]
// [file_carrayprinter] writes all data available from a [pipebuf]
// to a file descriptor on a single line.
// Special case for complex.
@ -124,29 +124,69 @@ struct file_carrayprinter : runnable {
file_carrayprinter(scheduler *sch,
const char *_head,
const char *_format,
const char *_sep,
const char *_tail,
pipebuf< complex<T> > &_in, int _fdout) :
runnable(sch, _in.name),
scale(1), in(_in),
head(_head), format(_format), tail(_tail),
scale(1), fixed_size(0), in(_in),
head(_head), format(_format), sep(_sep), tail(_tail),
fout(fdopen(_fdout,"w")) {
}
void run() {
int n = in.readable();
if ( n && fout ) {
fprintf(fout, head, n);
complex<T> *pin=in.rd(), *pend=pin+n;
for ( ; pin<pend; ++pin )
fprintf(fout, format, pin->re*scale, pin->im*scale);
fprintf(fout, "%s", tail);
int n, nmin = fixed_size ? fixed_size : 1;
while ( (n=in.readable()) >= nmin ) {
if ( fixed_size ) n = fixed_size;
if ( fout ) {
fprintf(fout, head, n);
complex<T> *pin = in.rd();
for ( int i=0; i<n; ++i ) {
if ( i ) fprintf(fout, "%s", sep);
fprintf(fout, format, pin[i].re*scale, pin[i].im*scale);
}
fprintf(fout, "%s", tail);
}
fflush(fout);
in.read(n);
}
in.read(n);
}
T scale;
int fixed_size; // Number of elements per batch, or 0.
private:
pipereader< complex<T> > in;
const char *head, *format, *sep, *tail;
FILE *fout;
};
template<typename T, int N>
struct file_vectorprinter : runnable {
file_vectorprinter(scheduler *sch,
const char *_head,
const char *_format,
const char *_sep,
const char *_tail,
pipebuf<T[N]> &_in, int _fdout) :
runnable(sch, _in.name), scale(1), in(_in),
head(_head), format(_format), sep(_sep), tail(_tail) {
fout = fdopen(_fdout,"w");
if ( ! fout ) fatal("fdopen");
}
void run() {
while ( in.readable() >= 1 ) {
fprintf(fout, head, N);
T (*pin)[N] = in.rd();
for ( int i=0; i<N; ++i ) {
if ( i ) fprintf(fout, "%s", sep);
fprintf(fout, format, (*pin)[i]*scale);
}
fprintf(fout, "%s", tail);
in.read(1);
}
fflush(fout);
}
T scale;
private:
pipereader< complex<T> > in;
const char *head, *format, *tail;
pipereader<T[N]> in;
const char *head, *format, *sep, *tail;
FILE *fout;
};

Wyświetl plik

@ -555,6 +555,17 @@ namespace leansdr {
}; // cstln_lut
static const char *cstln_names[] = {
[cstln_lut<256>::BPSK] = "BPSK",
[cstln_lut<256>::QPSK] = "QPSK",
[cstln_lut<256>::PSK8] = "8PSK",
[cstln_lut<256>::APSK16] = "16APSK",
[cstln_lut<256>::APSK32] = "32APSK",
[cstln_lut<256>::APSK64E] = "64APSKe",
[cstln_lut<256>::QAM16] = "16QAM",
[cstln_lut<256>::QAM64] = "64QAM",
[cstln_lut<256>::QAM256] = "256QAM"
};
// SAMPLER INTERFACE FOR CSTLN_RECEIVER
@ -1315,7 +1326,67 @@ namespace leansdr {
T *avgpower;
int phase;
}; // cnr_fft
template<typename T>
struct spectrum : runnable {
static const int nfft = 1024;
spectrum(scheduler *sch, pipebuf< complex<T> > &_in,
pipebuf<float[nfft]> &_out)
: runnable(sch, "spectrum"),
decimation(1048576), kavg(0.1),
in(_in), out(_out),
fft(nfft), avgpower(NULL), phase(0) {
}
int decimation;
float kavg;
void run() {
while ( in.readable()>=fft.n && out.writable()>=1 ) {
phase += fft.n;
if ( phase >= decimation ) {
phase -= decimation;
do_spectrum();
}
in.read(fft.n);
}
}
private:
void do_spectrum() {
complex<T> data[fft.n];
memcpy(data, in.rd(), fft.n*sizeof(data[0]));
fft.inplace(data, true);
float power[nfft];
for ( int i=0; i<fft.n; ++i )
power[i] = (float)data[i].re*data[i].re + (float)data[i].im*data[i].im;
if ( ! avgpower ) {
// Initialize with first spectrum
avgpower = new float[fft.n];
memcpy(avgpower, power, fft.n*sizeof(avgpower[0]));
}
// Accumulate and low-pass filter
for ( int i=0; i<fft.n; ++i )
avgpower[i] = avgpower[i]*(1-kavg) + power[i]*kavg;
// Reuse power[]
for ( int i=0; i<fft.n/2; ++i ) {
power[i] = 10 * log10f(avgpower[nfft/2+i]);
power[nfft/2+i] = 10 * log10f(avgpower[i]);
}
memcpy(out.wr(), power, sizeof(power[0])*nfft);
out.written(1);
}
pipereader< complex<T> > in;
pipewriter< float[nfft] > out;
cfft_engine<T> fft;
T *avgpower;
int phase;
}; // spectrum
} // namespace
#endif // LEANSDR_SDR_H