// leandvbtx.cc copyright (c) 2016-2017 pabr@pabr.org // http://www.pabr.org/radio/leandvb #include #include #include #include #include #include "leansdr/framework.h" #include "leansdr/generic.h" #include "leansdr/dsp.h" #include "leansdr/sdr.h" #include "leansdr/dvb.h" #include "leansdr/rs.h" #include "leansdr/filtergen.h" using namespace leansdr; template struct interpolator : runnable { unsigned int d; interpolator(scheduler *sch, int _d, pipebuf &_in, pipebuf &_out) : runnable(sch, "interpolator"), d(_d), in(_in), out(_out, d) { } void run() { unsigned long count = min(in.readable(), out.writable()/d); T *pin=in.rd(), *pend=pin+count, *pout=out.wr(); for ( ; pin in; pipewriter out; }; struct config { cstln_lut<256>::predef constellation; code_rate fec; float amp; // Desired RMS constellation amplitude bool agc; int interp; int decim; float rolloff; float rrc_rej; enum { OUTPUT_F32, OUTPUT_S16 } output_format; bool fill; bool verbose, debug; config() : constellation(cstln_lut<256>::QPSK), fec(FEC12), amp(1.0), agc(false), interp(2), decim(1), rolloff(0.35), rrc_rej(10), output_format(OUTPUT_F32), fill(false), verbose(false), debug(false) { } }; void run(config &cfg) { scheduler sch; sch.verbose = cfg.verbose; sch.debug = cfg.debug; int buf_factor = 2; unsigned long BUF_PACKETS = 12*buf_factor; // TBD Reduce copying unsigned long BUF_BYTES = SIZE_RSPACKET*BUF_PACKETS; unsigned long BUF_SYMBOLS = BUF_BYTES*8 * 2; // Worst case BPSK 1/2 unsigned long BUF_BASEBAND = 4096; // TS PACKETS ON STDIN pipebuf p_tspackets(&sch, "TS packets", BUF_PACKETS); file_reader r_stdin(&sch, 0, p_tspackets); // RANDOMIZER pipebuf p_rtspackets(&sch, "rand TS packets", BUF_PACKETS); randomizer r_rand(&sch, p_tspackets, p_rtspackets); // RS-ENCODER pipebuf< rspacket > p_rspackets(&sch, "RS-enc packets", BUF_PACKETS); rs_encoder r_rsenc(&sch, p_rtspackets, p_rspackets); // INTERLEAVER pipebuf p_mpegbytes(&sch, "mpegbytes", BUF_BYTES); interleaver r_inter(&sch, p_rspackets, p_mpegbytes); // CONVOLUTIONAL CODER cstln_lut<256> *cstln = make_dvbs2_constellation(cfg.constellation, cfg.fec); int bits_per_symbol = log2i(cstln->nsymbols); if ( cfg.fec==FEC23 && (cstln->nsymbols==4 || cstln->nsymbols==64) ) { if ( cfg.verbose ) fprintf(stderr, "Handling rate 2/3 as 4/6\n"); cfg.fec = FEC46; } pipebuf p_symbols(&sch, "symbols", BUF_SYMBOLS); dvb_convol r_convol(&sch, p_mpegbytes, p_symbols, cfg.fec, bits_per_symbol); // IQ MAPPER pipebuf p_iqsymbols(&sch, "IQ symbols", BUF_SYMBOLS); cstln_transmitter r_mod(&sch, p_symbols, p_iqsymbols); r_mod.cstln = cstln; // RESAMPLER pipebuf p_interp(&sch, "interpolated", BUF_BASEBAND); float Fm = 1.0 / cfg.interp; int order = cfg.interp * cfg.rrc_rej; float *coeffs; int ncoeffs = filtergen::root_raised_cosine (order, Fm, cfg.rolloff, &coeffs); // This yields about the desired power level even without AGC. filtergen::normalize_power(ncoeffs, coeffs, cfg.amp/cstln_amp); if ( sch.verbose ) fprintf(stderr, "Interpolation: ratio %d/%d, rolloff %f, %d coeffs\n", cfg.interp, cfg.decim, cfg.rolloff, ncoeffs); if ( sch.debug ) filtergen::dump_filter("rrc", ncoeffs, coeffs); fir_resampler r_resampler(&sch, ncoeffs, coeffs, p_iqsymbols, p_interp, cfg.interp, 1); // TBD Combine interp and decim pipebuf p_resampled(&sch, "resampled", BUF_BASEBAND); decimator r_decim(&sch, cfg.decim, p_interp, p_resampled); pipebuf *tail = &p_resampled; // AGC if ( cfg.agc ) { pipebuf *p_agc = new pipebuf(&sch, "AGC", BUF_BASEBAND); simple_agc *r_agc = new simple_agc(&sch, *tail, *p_agc); r_agc->out_rms = cfg.amp / sqrtf((float)cfg.interp/cfg.decim); // Adjust bandwidth for large interpolation ratios. r_agc->bw = 0.001 * cfg.decim / cfg.interp; tail = p_agc; } // IQ ON STDOUT switch ( cfg.output_format ) { case config::OUTPUT_F32: (void)new file_writer(&sch, *tail, 1); break; case config::OUTPUT_S16: { pipebuf *p_stdout = new pipebuf(&sch, "stdout", BUF_BASEBAND); (void)new cconverter(&sch, *tail, *p_stdout); (void)new file_writer(&sch, *p_stdout, 1); break; } default: fail("Output format not implemented"); } if ( cfg.fill ) { if ( cfg.verbose ) fprintf(stderr, "Realtime mode\n"); tspacket blank; memset(blank.data, 0, 188); blank.data[0] = 0x47; r_stdin.set_realtime(blank); } sch.run(); sch.shutdown(); if ( sch.verbose ) sch.dump(); } // Command-line void usage(const char *name, FILE *f, int c, const char *info=NULL) { fprintf(f, "Usage: %s [options] < TS > IQ\n", name); fprintf(f, "Modulate MPEG packets into a DVB-S baseband signal\n"); fprintf(f, "Output float complex samples\n"); fprintf (f, "\nOptions:\n" " --const STRING QPSK (default),\n" " BPSK .. 32APSK (DVB-S2),\n" " 64APSKe (DVB-S2X),\n" " 16QAM .. 256QAM (experimental)\n" " --cr STRING 1/2, 2/3, 3/4, 5/6, 7/8\n" " -f INTERP[/DECIM] Samples per symbols (default: 2)\n" " --roll-off FLOAT RRC roll-off (default: 0.35)\n" " --rrc-rej FLOAT RRC filter rejection (defaut: 10)\n" " --power FLOAT Output power (dB, default: 0)\n" " --agc Better regulation of output power\n" " --f32 Output 32-bit floats, range +-1.0 (default)\n" " --s16 Output 16-bit ints\n" " --fill Insert blank packets\n" " -v Output debugging info at startup and exit\n" " -d Output debugging info during operation\n" " --version Display version and exit\n" ); if ( info ) fprintf(f, "** Error while processing '%s'\n", info); exit(c); } #ifndef VERSION #define VERSION "undefined" #endif int main(int argc, char *argv[]) { config cfg; for ( int i=1; i::BPSK; else if ( ! strcmp(argv[i], "QPSK" ) ) cfg.constellation = cstln_lut<256>::QPSK; else if ( ! strcmp(argv[i], "8PSK" ) ) cfg.constellation = cstln_lut<256>::PSK8; else if ( ! strcmp(argv[i], "16APSK" ) ) cfg.constellation = cstln_lut<256>::APSK16; else if ( ! strcmp(argv[i], "32APSK" ) ) cfg.constellation = cstln_lut<256>::APSK32; else if ( ! strcmp(argv[i], "64APSKe" ) ) cfg.constellation = cstln_lut<256>::APSK64E; else if ( ! strcmp(argv[i], "16QAM" ) ) cfg.constellation = cstln_lut<256>::QAM16; else if ( ! strcmp(argv[i], "64QAM" ) ) cfg.constellation = cstln_lut<256>::QAM64; else if ( ! strcmp(argv[i], "256QAM" ) ) cfg.constellation = cstln_lut<256>::QAM256; else usage(argv[0], stderr, 1, argv[i]); } else if ( ! strcmp(argv[i], "-f") && i+1