#ifndef LEANSDR_DSP_H #define LEANSDR_DSP_H #include #include "leansdr/framework.h" #include "leansdr/math.h" namespace leansdr { ////////////////////////////////////////////////////////////////////// // DSP blocks ////////////////////////////////////////////////////////////////////// // [cconverter] converts complex streams between numric types, // with optional ofsetting and rational scaling. template struct cconverter : runnable { cconverter(scheduler *sch, pipebuf< complex > &_in, pipebuf< complex > &_out) : runnable(sch, "cconverter"), in(_in), out(_out) { } void run() { unsigned long count = min(in.readable(), out.writable()); complex *pin=in.rd(), *pend=pin+count; complex *pout = out.wr(); for ( ; pinre = Zout + (pin->re-(Tin)Zin)*Gn/Gd; pout->im = Zout + (pin->im-(Tin)Zin)*Gn/Gd; } in.read(count); out.written(count); } private: pipereader< complex > in; pipewriter< complex > out; }; template struct cfft_engine { const int n; cfft_engine(int _n) : n(_n), invsqrtn(1.0/sqrt(n)) { // Compute log2(n) logn = 0; for ( int t=n; t>1; t>>=1 ) ++logn; // Bit reversal bitrev = new int[n]; for ( int i=0; i>b)&1); } // Float constants omega = new complex[n]; omega_rev = new complex[n]; for ( int i=0; i *data, bool reverse=false) { // Bit-reversal permutation for ( int i=0; i tmp=data[i]; data[i]=data[r]; data[r]=tmp; } } complex *om = reverse ? omega_rev : omega; // Danielson-Lanczos for ( int i=0; i &w = om[k*dom]; complex &dqk = data[q+k]; complex x(w.re*dqk.re - w.im*dqk.im, w.re*dqk.im + w.im*dqk.re); data[q+k].re = data[p+k].re - x.re; data[q+k].im = data[p+k].im - x.im; data[p+k].re = data[p+k].re + x.re; data[p+k].im = data[p+k].im + x.im; } } } if ( reverse ) { float invn = 1.0 / n; for ( int i=0; i *omega, *omega_rev; float invsqrtn; }; template struct adder : runnable { adder(scheduler *sch, pipebuf &_in1, pipebuf &_in2, pipebuf &_out) : runnable(sch, "adder"), in1(_in1), in2(_in2), out(_out) { } void run() { int n = out.writable(); if ( in1.readable() < n ) n = in1.readable(); if ( in2.readable() < n ) n = in2.readable(); T *pin1=in1.rd(), *pin2=in2.rd(), *pout=out.wr(), *pend=pout+n; while ( pout < pend ) *pout++ = *pin1++ + *pin2++; in1.read(n); in2.read(n); out.written(n); } private: pipereader in1, in2; pipewriter out; }; template struct scaler : runnable { Tscale scale; scaler(scheduler *sch, Tscale _scale, pipebuf &_in, pipebuf &_out) : runnable(sch, "scaler"), scale(_scale), in(_in), out(_out) { } void run() { unsigned long count = min(in.readable(), out.writable()); Tin *pin=in.rd(), *pend=pin+count; Tout *pout = out.wr(); for ( ; pin in; pipewriter out; }; // [awgb_c] generates complex white gaussian noise. template struct wgn_c : runnable { wgn_c(scheduler *sch, pipebuf< complex > &_out) : runnable(sch, "awgn"), stddev(1.0), out(_out) { } void run() { int n = out.writable(); complex *pout=out.wr(), *pend=pout+n; while ( pout < pend ) { // TAOCP float x, y, r2; do { x = 2*drand48() - 1; y = 2*drand48() - 1; r2 = x*x + y*y; } while ( r2==0 || r2>=1 ); float k = sqrtf(-logf(r2)/r2) * stddev; pout->re = k*x; pout->im = k*y; ++pout; } out.written(n); } float stddev; private: pipewriter< complex > out; }; template struct naive_lowpass : runnable { naive_lowpass(scheduler *sch, pipebuf &_in, pipebuf &_out, int _w) : runnable(sch, "lowpass"), in(_in), out(_out), w(_w) { } void run() { if ( in.readable() < w ) return; unsigned long count = min(in.readable()-w, out.writable()); T *pin=in.rd(), *pend=pin+count; T *pout = out.wr(); float k = 1.0 / w; for ( ; pin in; pipewriter out; int w; }; template struct fir_filter : runnable { fir_filter(scheduler *sch, int _ncoeffs, Tc *_coeffs, pipebuf &_in, pipebuf &_out, unsigned int _decim=1) : runnable(sch, "fir_filter"), ncoeffs(_ncoeffs), coeffs(_coeffs), in(_in), out(_out), decim(_decim), freq_tap(NULL), tap_multiplier(1), freq_tol(0.1) { shifted_coeffs = new T[ncoeffs]; set_freq(0); } void run() { if ( in.readable() < ncoeffs ) return; if ( freq_tap ) { float new_freq = *freq_tap * tap_multiplier; if ( fabs(current_freq-new_freq) > freq_tol ) { if ( sch->verbose ) fprintf(stderr, "Shifting filter %f -> %f\n", current_freq, new_freq); set_freq(new_freq); } } unsigned long count = min((in.readable()-ncoeffs)/decim, out.writable()); T *pin=in.rd()+ncoeffs, *pend=pin+count*decim, *pout=out.wr(); // TBD use coeffs when current_freq=0 (fewer mults if float) for ( ; pin in; pipewriter out; unsigned int decim; T *shifted_coeffs; float current_freq; void set_freq(float f) { for ( int i=0; i struct fir_resampler : runnable { fir_resampler(scheduler *sch, int _ncoeffs, Tc *_coeffs, pipebuf &_in, pipebuf &_out, int _interp=1, int _decim=1) : runnable(sch, "fir_resampler"), ncoeffs(_ncoeffs), coeffs(_coeffs), interp(_interp), decim(_decim), in(_in), out(_out,interp), freq_tap(NULL), tap_multiplier(1), freq_tol(0.1) { if ( decim != 1 ) fail("fir_resampler: decim not implemented"); // TBD shifted_coeffs = new T[ncoeffs]; set_freq(0); } void run() { if ( in.readable() < ncoeffs ) return; if ( freq_tap ) { float new_freq = *freq_tap * tap_multiplier; if ( fabs(current_freq-new_freq) > freq_tol ) { if ( sch->verbose ) fprintf(stderr, "Shifting filter %f -> %f\n", current_freq, new_freq); set_freq(new_freq); } } if ( in.readable()*interp < ncoeffs ) return; unsigned long count = min((in.readable()*interp-ncoeffs)/interp, out.writable()/interp); int latency = (ncoeffs+interp) / interp; T *pin=in.rd()+latency, *pend=pin+count, *pout=out.wr(); // TBD use coeffs when current_freq=0 (fewer mults if float) for ( ; pin in; pipewriter out; public: float *freq_tap; float tap_multiplier; float freq_tol; private: T *shifted_coeffs; float current_freq; void set_freq(float f) { for ( int i=0; i