#ifndef LEANSDR_DVB_H #define LEANSDR_DVB_H #include #include "leansdr/viterbi.h" #include "leansdr/convolutional.h" #include "leansdr/sdr.h" #include "leansdr/rs.h" namespace leansdr { static const int SIZE_RSPACKET = 204; static const int MPEG_SYNC = 0x47; static const int MPEG_SYNC_INV = (MPEG_SYNC^0xff); static const int MPEG_SYNC_CORRUPTED = 0x55; // Generic deconvolution enum code_rate { FEC12, FEC23, FEC46, FEC34, FEC56, FEC78, // DVB-S FEC45, FEC89, FEC910, // DVB-S2 FEC_MAX }; // Customize APSK radii according to code rate inline cstln_lut<256> * make_dvbs2_constellation(cstln_lut<256>::predef c, code_rate r) { float gamma1=1, gamma2=1, gamma3=1; switch ( c ) { case cstln_lut<256>::APSK16: // EN 302 307, section 5.4.3, Table 9 switch ( r ) { case FEC23: case FEC46: gamma1 = 3.15; break; case FEC34: gamma1 = 2.85; break; case FEC45: gamma1 = 2.75; break; case FEC56: gamma1 = 2.70; break; case FEC89: gamma1 = 2.60; break; case FEC910: gamma1 = 2.57; break; default: fail("Code rate not supported with APSK16"); } break; case cstln_lut<256>::APSK32: // EN 302 307, section 5.4.4, Table 10 switch ( r ) { case FEC34: gamma1 = 2.84; gamma2 = 5.27; break; case FEC45: gamma1 = 2.72; gamma2 = 4.87; break; case FEC56: gamma1 = 2.64; gamma2 = 4.64; break; case FEC89: gamma1 = 2.54; gamma2 = 4.33; break; case FEC910: gamma1 = 2.53; gamma2 = 4.30; break; default: fail("Code rate not supported with APSK32"); } break; case cstln_lut<256>::APSK64E: // EN 302 307-2, section 5.4.5, Table 13f gamma1 = 2.4; gamma2 = 4.3; gamma3 = 7; break; default: break; } return new cstln_lut<256>(c, gamma1, gamma2, gamma3); } // EN 300 421, section 4.4.3, table 2 Punctured code, G1=0171, G2=0133 static const int DVBS_G1 = 0171; static const int DVBS_G2 = 0133; // G1 = 0b1111001 // G2 = 0b1011011 // // G1 = [ 1 1 1 1 0 0 1 ] // G2 = [ 1 0 1 1 0 1 1 ] // // C = [ G2 ; // G1 ; // 0 G2 ; // 0 G1 ; // 0 0 G2 ; // 0 0 G1 ] // // C = [ 1 0 1 1 0 1 1 0 0 0 0 0 0 ; // 1 1 1 1 0 0 1 0 0 0 0 0 0 ; // 0 1 0 1 1 0 1 1 0 0 0 0 0 ; // 0 1 1 1 1 0 0 1 0 0 0 0 0 ; // 0 0 1 0 1 1 0 1 1 0 0 0 0 ; // 0 0 1 1 1 1 0 0 1 0 0 0 0 ; // 0 0 0 1 0 1 1 0 1 1 0 0 0 ; // 0 0 0 1 1 1 1 0 0 1 0 0 0 ; // 0 0 0 0 1 0 1 1 0 1 1 0 0 ; // 0 0 0 0 1 1 1 1 0 0 1 0 0 ; // 0 0 0 0 0 1 0 1 1 0 1 1 0 ; // 0 0 0 0 0 1 1 1 1 0 0 1 0 ; // 0 0 0 0 0 0 1 0 1 1 0 1 1 ; // 0 0 0 0 0 0 1 1 1 1 0 0 1 ] // // IQ = [ Q1; I1; ... Q10; I10 ] = C * S // // D * C == [ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 ] // // D = [ 0 1 0 1 1 1 0 1 1 1 0 0 0 0] // D = 0x3ba template struct deconvol_sync : runnable { deconvol_sync(scheduler *sch, pipebuf &_in, pipebuf &_out, uint32_t gX, uint32_t gY, uint32_t pX, uint32_t pY) : runnable(sch, "deconvol_sync"), fastlock(false), in(_in), out(_out,SIZE_RSPACKET), skip(0) { conv = new uint32_t[2]; conv[0] = gX; conv[1] = gY; nG = 2; punct = new uint32_t[2]; punct[0] = pX; punct[1] = pY; punctperiod = 0; punctweight = 0; for ( int i=0; i<2; ++i ) { int nbits = log2(punct[i]) + 1; if ( nbits > punctperiod ) punctperiod = nbits; punctweight += hamming_weight(punct[i]); } if ( sch->verbose ) fprintf(stderr, "puncturing %d/%d\n", punctperiod, punctweight); deconv = new iq_t[punctperiod]; deconv2 = new iq_t[punctperiod]; inverse_convolution(); init_syncs(); locked = &syncs[0]; } typedef uint64_t signal_t; typedef uint64_t iq_t; static int log2(uint64_t x) { int n = -1; for ( ; x; ++n,x>>=1 ) ; return n; } iq_t convolve(signal_t s) { int sbits = log2(s) + 1; iq_t iq = 0; unsigned char state = 0; for ( int b=sbits-1; b>=0; --b ) { // Feed into convolver, MSB first unsigned char bit = (s>>b) & 1; state = (state>>1) | (bit<<6); // Shift register for ( int j=0; j *best ) return; if ( nprefix > sizeof(prefix)*8 ) return; int solved = 1; for ( int b=0; b>b)&1) ) { // Current candidate does not solve this column. if ( (response[b]>>nprefix) == 0 ) // No more bits to trace back. return; solved = 0; } } if ( solved ) { *best = prefix; return; } solve_rec(prefix, nprefix+1, exp, best); solve_rec(prefix|((iq_t)1<debug ) { for ( int b=0; b sizeof(iq_t)*8 ) fail("Bug: traceback exceeds register size"); if ( log2(deconv[b])+1 > traceback ) fail("traceback insufficient for deconvolution"); if ( log2(deconv2[b])+1 > traceback ) fail("traceback insufficient for deconvolution (alt)"); } } static const int NSYNCS = 4; struct sync_t { u8 lut[2][2]; // lut[(re>0)?1:0][(im>0)?1:0] = 0b000000IQ iq_t in; int n_in; signal_t out; int n_out; // Auxiliary shift register for fastlock iq_t in2; int n_in2, n_out2; } syncs[NSYNCS]; void init_syncs() { // EN 300 421, section 4.5, Figure 5 QPSK constellation // Four rotations * two conjugations. // 180° rotation is detected as polarity inversion in mpeg_sync. for ( int sync_id=0; sync_id 1 byte // 2/3 12 symbols -> 2 bytes // 3/4 16 symbols -> 3 bytes // 5/6 24 symbols -> 5 bytes // 7/8 32 symbols -> 7 bytes inline Tbyte readbyte(sync_t *s, softsymbol *&p) { while ( s->n_out < 8 ) { iq_t iq = s->in; while ( s->n_in < traceback ) { u8 iqbits = s->lut[(p->symbol&2)?1:0][p->symbol&1]; ++p; iq = (iq<<2) | iqbits; s->n_in += 2; } s->in = iq; for ( int b=punctperiod-1; b>=0; --b ) { u8 bit = parity(iq&deconv[b]); s->out = (s->out<<1) | bit; } s->n_out += punctperiod; s->n_in -= punctweight; } Tbyte res = (s->out >> (s->n_out-8)) & 255; s->n_out -= 8; return res; } inline unsigned long readerrors(sync_t *s, softsymbol *&p) { unsigned long res = 0; while ( s->n_out2 < 8 ) { iq_t iq = s->in2; while ( s->n_in2 < traceback ) { u8 iqbits = s->lut[(p->symbol&2)?1:0][p->symbol&1]; ++p; iq = (iq<<2) | iqbits; s->n_in2 += 2; } s->in2 = iq; for ( int b=punctperiod-1; b>=0; --b ) { u8 bit = parity(iq&deconv[b]); u8 bit2 = parity(iq&deconv2[b]); if ( bit2 != bit ) ++res; } s->n_out2 += punctperiod; s->n_in2 -= punctweight; } s->n_out2 -= 8; return res; } void run_decoding() { in.read(skip); skip = 0; // 8 byte margin to fill the deconvolver if ( in.readable() < 64 ) return; int maxrd = (in.readable()-64) / (punctweight/2) * punctperiod / 8; int maxwr = out.writable(); int n = (maxrddebug ) fprintf(stderr, "{%d->%d}\n", (int)(locked-syncs), (int)(best-syncs)); locked = best; } // If deconvolution bit error rate > 33%, try next sample alignment if ( errors_best > n*8/3 ) { // fprintf(stderr, ">"); skip = 1; } } softsymbol *pin=in.rd(), *pin0=pin; Tbyte *pout=out.wr(), *pout0=pout; while ( n-- ) *pout++ = readbyte(locked, pin); in.read(pin-pin0); out.written(pout-pout0); } pipereader in; pipewriter out; // DECONVOL int nG; uint32_t *conv; // [nG] Convolution polynomials; MSB is newest uint32_t *punct; // [nG] Puncturing pattern int punctperiod, punctweight; iq_t *deconv; // [punctperiod] Deconvolution polynomials iq_t *deconv2; // [punctperiod] Alternate polynomials (for fastlock) sync_t *locked; int skip; }; typedef deconvol_sync deconvol_sync_simple; inline deconvol_sync_simple *make_deconvol_sync_simple(scheduler *sch, pipebuf &_in, pipebuf &_out, enum code_rate rate) { // EN 300 421, section 4.4.3 Inner coding uint32_t pX, pY; switch ( rate ) { case FEC12: pX = 0x1; // 1 pY = 0x1; // 1 break; case FEC23: case FEC46: pX = 0xa; // 1010 (Handle as FEC4/6, no half-symbols) pY = 0xf; // 1111 break; case FEC34: pX = 0x5; // 101 pY = 0x6; // 110 break; case FEC56: pX = 0x15; // 10101 pY = 0x1a; // 11010 break; case FEC78: pX = 0x45; // 1000101 pY = 0x7a; // 1111010 break; default: //fail("Code rate not implemented"); // For testing DVB-S2 constellations. fprintf(stderr, "Code rate not implemented; proceeding anyway\n"); pX = pY = 1; } return new deconvol_sync_simple(sch, _in, _out, DVBS_G1, DVBS_G2, pX, pY); } // CONVOLUTIONAL ENCODER static const uint16_t polys_fec12[] = { DVBS_G1, DVBS_G2 // X1Y1 }; static const uint16_t polys_fec23[] = { DVBS_G1, DVBS_G2, DVBS_G2<<1 // X1Y1Y2 }; // Same code rate as 2/3, usable with QPSK static const uint16_t polys_fec46[] = { DVBS_G1, DVBS_G2, DVBS_G2<<1, // X1Y1Y2 DVBS_G1<<2, DVBS_G2<<2, DVBS_G2<<3 // X3Y3Y4 }; static const uint16_t polys_fec34[] = { DVBS_G1, DVBS_G2, // X1Y1 DVBS_G2<<1, DVBS_G1<<2 // Y2X3 }; static const uint16_t polys_fec45[] = { // Non standard DVBS_G1, DVBS_G2, // X1Y1 DVBS_G2<<1, DVBS_G1<<2, // Y2X3 DVBS_G1<<3 // X4 }; static const uint16_t polys_fec56[] = { DVBS_G1, DVBS_G2, // X1Y1 DVBS_G2<<1, DVBS_G1<<2, // Y2X3 DVBS_G2<<3, DVBS_G1<<4 // Y4X5 }; static const uint16_t polys_fec78[] = { DVBS_G1, DVBS_G2, // X1Y1 DVBS_G2<<1, DVBS_G2<<2, // Y2Y3 DVBS_G2<<3, DVBS_G1<<4, // Y4X5 DVBS_G2<<5, DVBS_G1<<6 // Y6X7 }; // FEC parameters, for convolutional coding only (not S2). static struct fec_spec { int bits_in; // Entering the convolutional coder int bits_out; // Exiting the convolutional coder const uint16_t *polys; // [bits_out] } fec_specs[FEC_MAX] = { [FEC12] = { 1, 2, polys_fec12 }, [FEC23] = { 2, 3, polys_fec23 }, [FEC46] = { 4, 6, polys_fec46 }, [FEC34] = { 3, 4, polys_fec34 }, [FEC56] = { 5, 6, polys_fec56 }, [FEC78] = { 7, 8, polys_fec78 }, [FEC45] = { 4, 5, polys_fec45 }, // Non-standard }; struct dvb_convol : runnable { typedef u8 uncoded_byte; typedef u8 hardsymbol; dvb_convol(scheduler *sch, pipebuf &_in, pipebuf &_out, code_rate fec, int bits_per_symbol) : runnable(sch, "dvb_convol"), in(_in), out(_out,64) // BPSK 7/8: 7 bytes in, 64 symbols out { fec_spec *fs = &fec_specs[fec]; if ( ! fs->bits_in ) fail("Unexpected FEC"); convol.bits_in = fs->bits_in; convol.bits_out = fs->bits_out; convol.polys = fs->polys; convol.bps = bits_per_symbol; // FEC must output a whole number of IQ symbols if ( convol.bits_out % convol.bps ) fail("Code rate not suitable for this constellation"); } void run() { int count = min(in.readable(), out.writable()*convol.bps/ convol.bits_out*convol.bits_in/8); // Process in multiples of the puncturing period and of 8 bits. int chunk = convol.bits_in; count = (count/chunk) * chunk; convol.encode(in.rd(), out.wr(), count); in.read(count); int nout = count*8/convol.bits_in*convol.bits_out/convol.bps; out.written(nout); } private: pipereader in; pipewriter out; convol_multipoly convol; }; // dvb_convol // NEW ALGEBRAIC DECONVOLUTION // QPSK 1/2 only; // With DVB-S polynomials hardcoded. template struct dvb_deconvol_sync : runnable { typedef u8 decoded_byte; int resync_period; static const int chunk_size = 64; // At least 2*sizeof(Thist)/8 dvb_deconvol_sync(scheduler *sch, pipebuf &_in, pipebuf &_out) : runnable(sch, "deconvol_sync_multipoly"), resync_period(32), in(_in), out(_out,chunk_size), resync_phase(0) { init_syncs(); locked = &syncs[0]; } void run() { while ( in.readable() >= chunk_size*8 && out.writable() >= chunk_size ) { int errors_best = 1 << 30; sync_t *best = NULL; for ( sync_t *s=syncs; sdeconv.run(pin, s->lut, pout, chunk_size); if ( nerrors < errors_best ) { errors_best=nerrors; best=s; } } in.read(chunk_size*8); out.written(chunk_size); if ( best != locked ) { if ( sch->debug ) fprintf(stderr, "%%%d", (int)(best-syncs)); locked = best; } if ( ++resync_phase >= resync_period ) resync_phase = 0; } // Work to do } // run() private: pipereader in; pipewriter out; int resync_phase; static const int NSYNCS = 4; struct sync_t { deconvol_poly2 deconv; u8 lut[4]; // TBD Swap and flip bits in the polynomials instead. } syncs[NSYNCS]; sync_t *locked; void init_syncs() { for ( int s=0; s dvb_deconvol_sync_soft; typedef dvb_deconvol_sync dvb_deconvol_sync_hard; // BIT ALIGNMENT AND MPEG SYNC DETECTION template struct mpeg_sync : runnable { int scan_syncs, want_syncs; unsigned long lock_timeout; bool fastlock; int resync_period; mpeg_sync(scheduler *sch, pipebuf &_in, pipebuf &_out, deconvol_sync *_deconv, pipebuf *_state_out=NULL, pipebuf *_locktime_out=NULL) : runnable(sch, "sync_detect"), scan_syncs(8), want_syncs(4), lock_timeout(4), fastlock(false), resync_period(1), in(_in), out(_out, SIZE_RSPACKET*(scan_syncs+1)), deconv(_deconv), polarity(0), resync_phase(0), bitphase(0), synchronized(false), next_sync_count(0), report_state(true) { state_out = _state_out ? new pipewriter(*_state_out) : NULL; locktime_out = _locktime_out ? new pipewriter(*_locktime_out) : NULL; } void run() { if ( report_state && state_out && state_out->writable()>=1 ) { // Report unlocked state on first invocation. state_out->write(0); report_state = false; } if ( synchronized ) run_decoding(); else { if ( fastlock ) run_searching_fast(); else run_searching(); } } void run_searching() { bool next_sync = false; int chunk = SIZE_RSPACKET * scan_syncs; while ( in.readable() >= chunk+1 && // Need 1 ahead for bit shifting out.writable() >= chunk && // Use as temp buffer ( !state_out || state_out->writable()>=1 ) ) { if ( search_sync() ) return; in.read(chunk); // Switch to next bit alignment ++bitphase; if ( bitphase == 8 ) { bitphase = 0; next_sync = true; } } if ( next_sync ) { // No lock this time ++next_sync_count; if ( next_sync_count >= 3 ) { // After a few cycles without a lock, resync the deconvolver. next_sync_count = 0; if ( deconv ) deconv->next_sync(); } } } void run_searching_fast() { int chunk = SIZE_RSPACKET * scan_syncs; while ( in.readable() >= chunk+1 && // Need 1 ahead for bit shifting out.writable() >= chunk && // Use as temp buffer ( !state_out || state_out->writable()>=1 ) ) { if ( resync_phase == 0 ) { // Try all bit alighments for ( bitphase=0; bitphase<=7; ++bitphase ) { if ( search_sync() ) return; } } in.read(SIZE_RSPACKET); if ( ++resync_phase >= resync_period ) resync_phase = 0; } } bool search_sync() { int chunk = SIZE_RSPACKET * scan_syncs; // Bit-shift [scan_sync] packets according to current [bitphase] Tbyte *pin = in.rd(), *pend = pin+chunk; Tbyte *pout = out.wr(); unsigned short w = *pin++; for ( ; pin<=pend; ++pin,++pout ) { w = (w<<8) | *pin; *pout = w >> bitphase; } // Search for [want_sync] start codes at all 204 offsets for ( int i=0; i nsyncs_n) { polarity=0; nsyncs=nsyncs_p; phase8=phase8_p; } else { polarity=-1; nsyncs=nsyncs_n; phase8=phase8_n; } if ( nsyncs>=want_syncs && phase8>=0 ) { if ( sch->debug ) fprintf(stderr, "Locked\n"); if ( ! i ) { // Avoid fixpoint detection in scheduler i = SIZE_RSPACKET; phase8 = (phase8+1) & 7; } in.read(i); // Skip to first start code synchronized = true; lock_timeleft = lock_timeout; locktime = 0; if ( state_out ) state_out->write(1); return true; } } return false; } void run_decoding() { while ( in.readable() >= SIZE_RSPACKET+1 && // +1 for bit shifting out.writable() >= SIZE_RSPACKET && ( !state_out || state_out->writable()>=1 ) && ( !locktime_out || locktime_out->writable()>=1 ) ) { Tbyte *pin = in.rd(), *pend = pin+SIZE_RSPACKET; Tbyte *pout = out.wr(); unsigned short w = *pin++; for ( ; pin<=pend; ++pin,++pout ) { w = (w<<8) | *pin; *pout = (w >> bitphase) ^ polarity; } in.read(SIZE_RSPACKET); Tbyte syncbyte = *out.wr(); out.written(SIZE_RSPACKET); ++locktime; if ( locktime_out ) locktime_out->write(locktime); // Reset timer if sync byte is correct Tbyte expected = phase8 ? MPEG_SYNC : MPEG_SYNC_INV; if ( syncbyte == expected ) lock_timeleft = lock_timeout; phase8 = (phase8+1) & 7; --lock_timeleft; if ( ! lock_timeleft ) { if ( sch->debug ) fprintf(stderr, "Unlocked\n"); synchronized = false; next_sync_count = 0; if ( state_out ) state_out->write(0); return; } } } private: pipereader in; pipewriter out; deconvol_sync *deconv; unsigned char polarity; // XOR mask, 0 or 0xff int resync_phase; int bitphase; bool synchronized; int next_sync_count; int phase8; // Position in 8-packet cycle, -1 if not synchronized unsigned long lock_timeleft; unsigned long locktime; pipewriter *state_out; pipewriter *locktime_out; bool report_state; }; template struct rspacket { Tbyte data[SIZE_RSPACKET]; }; // INTERLEAVER struct interleaver : runnable { interleaver(scheduler *sch, pipebuf< rspacket > &_in, pipebuf< u8 > &_out) : runnable(sch, "interleaver"), in(_in), out(_out,SIZE_RSPACKET) { } void run() { while ( in.readable() >= 12 && out.writable() >= SIZE_RSPACKET ) { rspacket *pin=in.rd(); u8 *pout = out.wr(); int delay = 0; for ( int i=0; i > in; pipewriter out; }; // interleaver // DEINTERLEAVER template struct deinterleaver : runnable { deinterleaver(scheduler *sch, pipebuf &_in, pipebuf< rspacket > &_out) : runnable(sch, "deinterleaver"), in(_in), out(_out) { } void run() { while ( in.readable() >= 17*11*12+SIZE_RSPACKET && out.writable() >= 1 ) { Tbyte *pin = in.rd()+17*11*12, *pend=pin+SIZE_RSPACKET; Tbyte *pout= out.wr()->data; for ( int delay=17*11; pin in; pipewriter< rspacket > out; }; // deinterleaver static const int SIZE_TSPACKET = 188; struct tspacket { u8 data[SIZE_TSPACKET]; }; // RS ENCODER struct rs_encoder : runnable { rs_encoder(scheduler *sch, pipebuf &_in, pipebuf< rspacket > &_out) : runnable(sch, "RS encoder"), in(_in), out(_out) { } void run() { while ( in.readable()>=1 && out.writable()>=1 ) { u8 *pin = in.rd()->data; u8 *pout = out.wr()->data; // The first 188 bytes are the uncoded message P(X) memcpy(pout, pin, SIZE_TSPACKET); // Append 16 RS parity bytes R(X) = - (P(X)*X^16 mod G(X)) // so that G(X) divides the coded message S(X) = P(X)*X^16 - R(X). rs.encode(pout); in.read(1); out.written(1); } } private: rs_engine rs; pipereader in; pipewriter< rspacket > out; }; // rs_encoder // RS DECODER template struct rs_decoder : runnable { rs_engine rs; rs_decoder(scheduler *sch, pipebuf< rspacket > &_in, pipebuf &_out, pipebuf *_bitcount=NULL, pipebuf *_errcount=NULL) : runnable(sch, "RS decoder"), in(_in), out(_out) { bitcount = _bitcount ? new pipewriter(*_bitcount) : NULL; errcount = _errcount ? new pipewriter(*_errcount) : NULL; } void run() { if ( bitcount && bitcount->writable()<1 ) return; if ( errcount && errcount->writable()<1 ) return; int nbits=0, nerrs=0; while ( in.readable()>=1 && out.writable()>=1 ) { Tbyte *pin = in.rd()->data; u8 *pout = out.wr()->data; nbits += SIZE_RSPACKET * 8; // The message is the first 188 bytes. if ( sizeof(Tbyte) == 1 ) memcpy(pout, pin, SIZE_TSPACKET); else fail("Erasures not implemented"); u8 synd[16]; bool corrupted = rs.syndromes(pin, synd); #if 0 if ( ! corrupted ) { // Test BM fprintf(stderr, "Simulating errors\n"); pin[203] ^= 42; pin[202] ^= 99; corrupted = rs.syndromes(pin, synd); } #endif if ( ! corrupted ) { if ( sch->debug ) fprintf(stderr, "_"); // Packet received without errors. } else { corrupted = rs.correct(synd, pout, pin, &nerrs); if ( sch->debug ) { if ( ! corrupted ) fprintf(stderr, "."); // Errors were corrected. else fprintf(stderr, "!"); // Packet still corrupted. } } in.read(1); // Output corrupted packets (with a special mark) // otherwise the derandomizer will lose synchronization. if ( corrupted ) pout[0] ^= MPEG_SYNC_CORRUPTED; out.written(1); } if ( nbits ) { if ( bitcount ) bitcount->write(nbits); if ( errcount ) errcount->write(nerrs); } } private: pipereader< rspacket > in; pipewriter out; pipewriter *bitcount, *errcount; }; // rs_decoder // RANDOMIZER struct randomizer : runnable { randomizer(scheduler *sch, pipebuf &_in, pipebuf &_out) : runnable(sch, "derandomizer"), in(_in), out(_out) { precompute_pattern(); pos = pattern; pattern_end = pattern + sizeof(pattern)/sizeof(pattern[0]); } void precompute_pattern() { // EN 300 421, section 4.4.1 Transport multiplex adaptation pattern[0] = 0xff; // Invert one in eight sync bytes unsigned short st = 000251; // 0b 000 000 010 101 001 (Fig 2 reversed) for ( int i=1; i<188*8; ++i ) { u8 out = 0; for ( int n=8; n--; ) { int bit = ((st>>13) ^ (st>>14)) & 1; // Taps out = (out<<1) | bit; // MSB first st = (st<<1) | bit; // Feedback } pattern[i] = (i%188) ? out : 0; // Inhibit on sync bytes } } void run() { while ( in.readable()>=1 && out.writable()>=1 ) { u8 *pin = in.rd()->data, *pend = pin+SIZE_TSPACKET; u8 *pout= out.wr()->data; if ( pin[0] != MPEG_SYNC ) fprintf(stderr, "randomizer: bad MPEG sync %02x\n", pin[0]); for ( ; pin in; pipewriter out; }; // randomizer // DERANDOMIZER struct derandomizer : runnable { derandomizer(scheduler *sch, pipebuf &_in, pipebuf &_out) : runnable(sch, "derandomizer"), in(_in), out(_out) { precompute_pattern(); pos = pattern; pattern_end = pattern + sizeof(pattern)/sizeof(pattern[0]); } void precompute_pattern() { // EN 300 421, section 4.4.1 Transport multiplex adaptation pattern[0] = 0xff; // Restore the inverted sync byte unsigned short st = 000251; // 0b 000 000 010 101 001 (Fig 2 reversed) for ( int i=1; i<188*8; ++i ) { u8 out = 0; for ( int n=8; n--; ) { int bit = ((st>>13) ^ (st>>14)) & 1; // Taps out = (out<<1) | bit; // MSB first st = (st<<1) | bit; // Feedback } pattern[i] = (i%188) ? out : 0; // Inhibit on sync bytes } } void run() { while ( in.readable()>=1 && out.writable()>=1 ) { u8 *pin = in.rd()->data, *pend = pin+SIZE_TSPACKET; u8 *pout= out.wr()->data; if ( pin[0] == MPEG_SYNC_INV || pin[0] == (MPEG_SYNC_INV^MPEG_SYNC_CORRUPTED) ) { if ( pos != pattern ) { if ( sch->debug ) fprintf(stderr, "derandomizer: resynchronizing\n"); pos = pattern; } } for ( ; pindata[0]; if ( sync == MPEG_SYNC ) { out.written(1); } else { if ( sync != (MPEG_SYNC^MPEG_SYNC_CORRUPTED) ) if ( sch->debug ) fprintf(stderr, "(%02x)", sync); out.wr()->data[1] |= 0x80; // Set the Transport Error Indicator bit // We could output corrupted packets here, in case the // MPEG decoder can use them somehow. //out.written(1); } } } private: u8 pattern[188*8], *pattern_end, *pos; pipereader in; pipewriter out; }; // derandomizer // VITERBI DECODING // Supports all code rates and constellations // Simplified metric to support large constellations. // This version implements puncturing by expanding the trellis. // TBD Compare performance vs skipping updates in a 1/2 trellis. struct viterbi_sync : runnable { typedef uint8_t TS, TCS, TUS; typedef int32_t TBM; // Only 16 bits per IQ, but several IQ per Viterbi CS typedef int32_t TPM; typedef viterbi_dec_interface dvb_dec_interface; // 1/2: 6 bits of state, 1 bit in, 2 bits out typedef bitpath path_12; typedef trellis trellis_12; typedef viterbi_dec dvb_dec_12; // 2/3: 6 bits of state, 2 bits in, 3 bits out typedef bitpath path_23; typedef trellis trellis_23; typedef viterbi_dec dvb_dec_23; // 4/6: 6 bits of state, 4 bits in, 6 bits out typedef bitpath path_46; typedef trellis trellis_46; typedef viterbi_dec dvb_dec_46; // 3/4: 6 bits of state, 3 bits in, 4 bits out typedef bitpath path_34; typedef trellis trellis_34; typedef viterbi_dec dvb_dec_34; // 4/5: 6 bits of state, 4 bits in, 5 bits out (non-standard) typedef bitpath path_45; typedef trellis trellis_45; typedef viterbi_dec dvb_dec_45; // 5/6: 6 bits of state, 5 bits in, 6 bits out typedef bitpath path_56; typedef trellis trellis_56; typedef viterbi_dec dvb_dec_56; // QPSK 7/8: 6 bits of state, 7 bits in, 8 bits out typedef bitpath path_78; typedef trellis trellis_78; typedef viterbi_dec dvb_dec_78; private: pipereader in; pipewriter out; cstln_lut<256> *cstln; fec_spec *fec; int bits_per_symbol; // Bits per IQ symbol (not per coded symbol) int nsyncs; int nshifts; struct sync { int shift; dvb_dec_interface *dec; TCS *map; // [nsymbols] } *syncs; // [nsyncs] int current_sync; static const int chunk_size = 128; int resync_phase; public: int resync_period; viterbi_sync(scheduler *sch, pipebuf &_in, pipebuf &_out, cstln_lut<256> *_cstln, code_rate cr) : runnable(sch, "viterbi_sync"), in(_in), out(_out, chunk_size), cstln(_cstln), current_sync(0), resync_phase(0), resync_period(32) // 1/32 = 9% synchronization overhead TBD { bits_per_symbol = log2i(cstln->nsymbols); fec = &fec_specs[cr]; { // Sanity check: FEC block size must be a multiple of label size. int symbols_per_block = fec->bits_out / bits_per_symbol; if ( bits_per_symbol*symbols_per_block != fec->bits_out ) fail("Code rate not suitable for this constellation"); } int nconj; switch ( cstln->nsymbols ) { case 2: nconj = 1; break; // Conjugation is not relevant for BPSK default: nconj = 2; break; } int nrotations; switch ( cstln->nsymbols ) { case 2: case 4: // For BPSK and QPSK, 180° rotation is handled as // polarity inversion in mpeg_sync. nrotations = cstln->nrotations/2; break; default: nrotations = cstln->nrotations; break; } nshifts = fec->bits_out / bits_per_symbol; nsyncs = nconj * nrotations * nshifts; // TBD Many HOM constellations are labelled in such a way // that certain rot/conj combinations are equivalent to // polarity inversion. We could reduce nsyncs. syncs = new sync[nsyncs]; for ( int s=0; snrotations); #if 0 fprintf(stderr, "sync %3d: conj%d offs%d rot%d/%d map:", s, conj, syncs[s].shift, rot, cstln->nrotations); for ( int i=0; insymbols; ++i ) fprintf(stderr, " %2d", syncs[s].map[i]); fprintf(stderr, "\n"); #endif } if ( cr == FEC12 ) { trellis_12 *trell = new trellis_12(); trell->init_convolutional(fec->polys); for ( int s=0; sinit_convolutional(fec->polys); for ( int s=0; sinit_convolutional(fec->polys); for ( int s=0; sinit_convolutional(fec->polys); for ( int s=0; sinit_convolutional(fec->polys); for ( int s=0; sinit_convolutional(fec->polys); for ( int s=0; sinit_convolutional(fec->polys); for ( int s=0; snsymbols]; float ca=cosf(angle), sa=sinf(angle); for ( int i=0; insymbols; ++i ) { int8_t I = cstln->symbols[i].re; int8_t Q = cstln->symbols[i].im; if ( conj ) Q = -Q; int8_t RI = I*ca - Q*sa; int8_t RQ = I*sa + Q*ca; cstln_lut<256>::result *pr = cstln->lookup(RI, RQ); map[i] = pr->ss.symbol; } return map; } inline TUS update_sync(int s, softsymbol *pin, TPM *discr) { // Read one FEC ouput block pin += syncs[s].shift; TCS cs = 0; TBM cost = 0; for ( int i=0; isymbol]; cost += pin->cost; } return syncs[s].dec->update(cs, cost, discr); } void run() { // Number of FEC blocks to fill the bitpath depth. // Before that we cannot discriminate between synchronizers int discr_delay = 64 / fec->bits_in; // Process [chunk_size] FEC blocks at a time while ( in.readable() >= nshifts*chunk_size + (nshifts-1) && out.writable()*8 >= fec->bits_in*chunk_size ) { TPM totaldiscr[nsyncs]; for ( int s=0; sbits_in) | result; nout += fec->bits_in; if ( blocknum >= discr_delay ) totaldiscr[current_sync] += discr; if ( ! resync_phase ) { // Every [resync_period] chunks, also run the other decoders. for ( int s=0; s= discr_delay ) totaldiscr[s] += discr; } } while ( nout >= 8 ) { out.write(outstream>>(nout-8)); nout -= 8; } } // chunk_size in.read(chunk_size*nshifts); if ( nout ) fail("overlapping out"); if ( ! resync_phase ) { // Switch to another decoder ? int best = current_sync; for ( int s=0; s totaldiscr[best] ) best = s; if ( best != current_sync ) { if ( sch->debug ) fprintf(stderr, "{%d->%d}", current_sync, best); current_sync = best; } } if ( ++resync_phase >= resync_period ) resync_phase = 0; } } }; // viterbi_sync } // namespace #endif // LEANSDR_DVB_H