#ifndef LEANSDR_VITERBI_H #define LEANSDR_VITERBI_H #include #include #include // This is a generic implementation of Viterbi with explicit // representation of the trellis. There is special support for // convolutional coding, but the code can handle other schemes. // TBD This is very inefficient. For a specific trellis all loops // can be be unrolled. namespace leansdr { // TS is an integer type for a least NSTATES+1 states. // NSTATES is the number of states (e.g. 2^(K-1)). // TUS is an integer type for uncoded symbols (branch identifiers). // NUS is the number of uncoded symbols. // TCS is an integer type for coded symbols (branch labels). // NCS is the number of coded symbols. // TP is a type for representing paths. // TPM, TBM are unsigned integer types for path/branch metrics. // TPM is at least as wide as TBM. template struct trellis { static const int NOSTATE = NSTATES+1; struct state { struct branch { TS pred; // Predecessor state or NOSTATE TUS us; // Uncoded symbol } branches[NCS]; // Incoming branches indexed by coded symbol } states[NSTATES]; trellis() { for ( TS s=0; spred != NOSTATE ) { fprintf(stderr, "Invalid convolutional code\n"); exit(1); } b->pred = s; b->us = us; } } } void dump() { for ( int s=0; spred == NOSTATE ) fprintf(stderr, " - "); else fprintf(stderr, " %02x+%x", b->pred, b->us); } fprintf(stderr, "\n"); } } }; // Interface that hides the templated internals. template struct viterbi_dec_interface { virtual TUS update(TBM costs[], TPM *quality=NULL)=0; virtual TUS update(TCS s, TBM cost, TPM *quality=NULL)=0; virtual TUS update(int nm, TCS cs[], TBM costs[], TPM *quality=NULL)=0; }; template struct viterbi_dec : viterbi_dec_interface { trellis *trell; struct state { TPM cost; // Metric of best path leading to this state TP path; // Best path leading to this state }; typedef state statebank[NSTATES]; state statebanks[2][NSTATES]; statebank *states, *newstates; // Alternate between banks viterbi_dec(trellis *_trellis) : trell(_trellis) { states = &statebanks[0]; newstates = &statebanks[1]; for ( TS s=0; smax_tpm; max_tpm=max_tpm*2+1 ) ; } } // Update with full metric TUS update(TBM costs[NCS], TPM *quality=NULL) { TPM best_tpm = max_tpm, best2_tpm = max_tpm; TS best_state = 0; // Update all states for ( int s=0; s::state::branch *best_b = NULL; // Select best branch for ( int cs=0; cs::state::branch *b = &trell->states[s].branches[cs]; if ( b->pred == trell->NOSTATE ) continue; TPM m = (*states)[b->pred].cost + costs[cs]; if ( m <= best_m ) { // <= guarantees one match best_m = m; best_b = b; } } (*newstates)[s].path = (*states)[best_b->pred].path; (*newstates)[s].path.append(best_b->us); (*newstates)[s].cost = best_m; // Select best and second-best states if ( best_m < best_tpm ) { best_state = s; best2_tpm = best_tpm; best_tpm = best_m; } else if ( best_m < best2_tpm ) best2_tpm = best_m; } // Swap banks { statebank *tmp=states; states=newstates; newstates=tmp; } // Prevent overflow of path metrics for ( TS s=0; s::state::branch *best_b = NULL; for ( int im=0; im::state::branch *b = &trell->states[s].branches[cs[im]]; if ( b->pred == trell->NOSTATE ) continue; TPM m = (*states)[b->pred].cost + costs[im]; if ( m <= best_m ) { // <= guarantees one match best_m = m; best_b = b; } } if ( nm != NCS ) { // Also scan the other branches. // We actually rescan the branches with metrics. // This works because costs are negative. for ( int cs=0; cs::state::branch *b = &trell->states[s].branches[cs]; if ( b->pred == trell->NOSTATE ) continue; TPM m = (*states)[b->pred].cost; if ( m <= best_m ) { best_m = m; best_b = b; } } } (*newstates)[s].path = (*states)[best_b->pred].path; (*newstates)[s].path.append(best_b->us); (*newstates)[s].cost = best_m; // Select best states if ( best_m < best_tpm ) { best_state = s; best2_tpm = best_tpm; best_tpm = best_m; } else if ( best_m < best2_tpm ) best2_tpm = best_m; } // Swap banks { statebank *tmp=states; states=newstates; newstates=tmp; } // Prevent overflow of path metrics for ( TS s=0; s struct bitpath { T val; bitpath() : val(0) { } void append(TUS us) { val = (val<>(DEPTH-1)*NBITS) & ((1<