// Help function to keep code base small
//  _kf     :   modulation factor
//  _type   :   demodulation type {LIQUID_FREQDEM_DELAYCONJ, LIQUID_FREQDEM_PLL}
void freqmodem_test(float               _kf,
                    liquid_freqdem_type _type)
{
    // options
    unsigned int num_samples = 1024;
    //float tol = 1e-2f;

    unsigned int i;

    // create mod/demod objects
    freqmod mod = freqmod_create(_kf);          // modulator
    freqdem dem = freqdem_create(_kf,_type);    // demodulator

    // allocate arrays
    float         m[num_samples];   // message signal
    float complex r[num_samples];   // received signal (complex baseband)
    float         y[num_samples];   // demodulator output

    // generate message signal (single-frequency sine)
    for (i=0; i<num_samples; i++)
        m[i] = 0.7f*cosf(2*M_PI*0.013f*i + 0.0f);

    // modulate/demodulate signal
    for (i=0; i<num_samples; i++) {
        // modulate
        freqmod_modulate(mod, m[i], &r[i]);

        // demodulate
        freqdem_demodulate(dem, r[i], &y[i]);
    }

    // delete modem objects
    freqmod_destroy(mod);
    freqdem_destroy(dem);

#if 0
    // compute power spectral densities and compare
    float complex mcf[num_samples];
    float complex ycf[num_samples];
    float complex M[num_samples];
    float complex Y[num_samples];
    for (i=0; i<num_samples; i++) {
        mcf[i] = m[i] * hamming(i,num_samples);
        ycf[i] = y[i] * hamming(i,num_samples);
    }
    fft_run(num_samples, mcf, M, LIQUID_FFT_FORWARD, 0);
    fft_run(num_samples, ycf, Y, LIQUID_FFT_FORWARD, 0);

    // run test: compare spectral magnitude
    for (i=0; i<num_samples; i++)
        CONTEND_DELTA( cabsf(Y[i]), cabsf(M[i]), tol );
#endif
}
// generate short sequence symbols
//  _p      :   subcarrier allocation array
//  _M      :   total number of subcarriers
//  _S0     :   output symbol (freq)
//  _s0     :   output symbol (time)
//  _M_S0   :   total number of enabled subcarriers in S0
void ofdmframe_init_S0(unsigned char * _p,
                       unsigned int    _M,
                       float complex * _S0,
                       float complex * _s0,
                       unsigned int *  _M_S0)
{
    unsigned int i;

    // compute m-sequence length
    unsigned int m = liquid_nextpow2(_M);
    if (m < 4)      m = 4;
    else if (m > 8) m = 8;

    // generate m-sequence generator object
    msequence ms = msequence_create_default(m);

    unsigned int s;
    unsigned int M_S0 = 0;

    // short sequence
    for (i=0; i<_M; i++) {
        // generate symbol
        //s = msequence_generate_symbol(ms,1);
        s = msequence_generate_symbol(ms,3) & 0x01;

        if (_p[i] == OFDMFRAME_SCTYPE_NULL) {
            // NULL subcarrier
            _S0[i] = 0.0f;
        } else {
            if ( (i%2) == 0 ) {
                // even subcarrer
                _S0[i] = s ? 1.0f : -1.0f;
                M_S0++;
            } else {
                // odd subcarrer (ignore)
                _S0[i] = 0.0f;
            }
        }
    }

    // destroy objects
    msequence_destroy(ms);

    // ensure at least one subcarrier was enabled
    if (M_S0 == 0) {
        fprintf(stderr,"error: ofdmframe_init_S0(), no subcarriers enabled; check allocation\n");
        exit(1);
    }

    // set return value(s)
    *_M_S0 = M_S0;

    // run inverse fft to get time-domain sequence
    fft_run(_M, _S0, _s0, FFT_REVERSE, 0);

    // normalize time-domain sequence level
    float g = 1.0f / sqrtf(M_S0);
    for (i=0; i<_M; i++)
        _s0[i] *= g;
}
// generate long sequence symbols
//  _p      :   subcarrier allocation array
//  _M      :   total number of subcarriers
//  _S1     :   output symbol (freq)
//  _s1     :   output symbol (time)
//  _M_S1   :   total number of enabled subcarriers in S1
void ofdmframe_init_S1(unsigned char * _p,
                       unsigned int    _M,
                       float complex * _S1,
                       float complex * _s1,
                       unsigned int *  _M_S1)
{
    unsigned int i;

    // compute m-sequence length
    unsigned int m = liquid_nextpow2(_M);
    if (m < 4)      m = 4;
    else if (m > 8) m = 8;

    // increase m such that the resulting S1 sequence will
    // differ significantly from S0 with the same subcarrier
    // allocation array
    m++;

    // generate m-sequence generator object
    msequence ms = msequence_create_default(m);

    unsigned int s;
    unsigned int M_S1 = 0;

    // long sequence
    for (i=0; i<_M; i++) {
        // generate symbol
        //s = msequence_generate_symbol(ms,1);
        s = msequence_generate_symbol(ms,3) & 0x01;

        if (_p[i] == OFDMFRAME_SCTYPE_NULL) {
            // NULL subcarrier
            _S1[i] = 0.0f;
        } else {
            _S1[i] = s ? 1.0f : -1.0f;
            M_S1++;
        }
    }

    // destroy objects
    msequence_destroy(ms);

    // ensure at least one subcarrier was enabled
    if (M_S1 == 0) {
        fprintf(stderr,"error: ofdmframe_init_S1(), no subcarriers enabled; check allocation\n");
        exit(1);
    }

    // set return value(s)
    *_M_S1 = M_S1;

    // run inverse fft to get time-domain sequence
    fft_run(_M, _S1, _s1, FFT_REVERSE, 0);

    // normalize time-domain sequence level
    float g = 1.0f / sqrtf(M_S1);
    for (i=0; i<_M; i++)
        _s1[i] *= g;
}
Beispiel #4
0
// Design flipped Nyquist/root-Nyquist filter
//  _type   : filter type (e.g. LIQUID_NYQUIST_FEXP)
//  _root   : square-root Nyquist filter?
//  _k      : samples/symbol
//  _m      : symbol delay
//  _beta   : rolloff factor (0 < beta <= 1)
//  _dt     : fractional sample delay
//  _h      : output coefficient buffer (length: 2*k*m+1)
void liquid_firdes_fnyquist(liquid_nyquist_type _type,
                            int                 _root,
                            unsigned int        _k,
                            unsigned int        _m,
                            float               _beta,
                            float               _dt,
                            float *             _h)
{
    // validate input
    if ( _k < 1 ) {
        fprintf(stderr,"error: liquid_firdes_fnyquist(): k must be greater than 0\n");
        exit(1);
    } else if ( _m < 1 ) {
        fprintf(stderr,"error: liquid_firdes_fnyquist(): m must be greater than 0\n");
        exit(1);
    } else if ( (_beta < 0.0f) || (_beta > 1.0f) ) {
        fprintf(stderr,"error: liquid_firdes_fnyquist(): beta must be in [0,1]\n");
        exit(1);
    } else;

    unsigned int i;

    // derived values
    unsigned int h_len = 2*_k*_m+1;   // filter length

    float H_prime[h_len];   // frequency response of Nyquist filter (real)
    float complex H[h_len]; // frequency response of Nyquist filter
    float complex h[h_len]; // impulse response of Nyquist filter

    // compute Nyquist filter frequency response
    switch (_type) {
    case LIQUID_NYQUIST_FEXP:
        liquid_firdes_fexp_freqresponse(_k, _m, _beta, H_prime);
        break;
    case LIQUID_NYQUIST_FSECH:
        liquid_firdes_fsech_freqresponse(_k, _m, _beta, H_prime);
        break;
    case LIQUID_NYQUIST_FARCSECH:
        liquid_firdes_farcsech_freqresponse(_k, _m, _beta, H_prime);
        break;
    default:
        fprintf(stderr,"error: liquid_firdes_fnyquist(), unknown/unsupported filter type\n");
        exit(1);
    }

    // copy result to fft input buffer, computing square root
    // if required
    for (i=0; i<h_len; i++)
        H[i] = _root ? sqrtf(H_prime[i]) : H_prime[i];

    // compute ifft
    fft_run(h_len, H, h, FFT_REVERSE, 0);
    
    // copy shifted, scaled response
    for (i=0; i<h_len; i++)
        _h[i] = crealf( h[(i+_k*_m+1)%h_len] ) * (float)_k / (float)(h_len);
}
Beispiel #5
0
/*
 * Filter with fast convolution (overlap-add algorithm).
 */
gint fftfilt_run(struct fftfilt *s, complex in, complex **out)
{
	gint i;

	/* collect filterlen/2 input samples */
	s->fft->in[s->inptr++] = in;

	if (s->inptr < s->filterlen / 2)
		return 0;

	/* FFT */
	fft_run(s->fft);

	/* multiply with the filter shape */
	for (i = 0; i < s->filterlen; i++)
		s->ift->in[i] = cmul(s->fft->out[i], s->filter[i]);

	/* IFFT */
	fft_run(s->ift);

	/* overlap and add */
	for (i = 0; i < s->filterlen / 2; i++) {
		c_re(s->ift->out[i]) += c_re(s->ovlbuf[i]);
		c_im(s->ift->out[i]) += c_im(s->ovlbuf[i]);
	}
	*out = s->ift->out;

	/* save the second half for overlapping */
	for (i = 0; i < s->filterlen / 2; i++) {
		c_re(s->ovlbuf[i]) = c_re(s->ift->out[i + s->filterlen / 2]);
		c_im(s->ovlbuf[i]) = c_im(s->ift->out[i + s->filterlen / 2]);
	}

	/* clear inbuf */
	fft_clear_inbuf(s->fft);
	s->inptr = 0;

	/* signal the caller there is filterlen/2 samples ready */
	return s->filterlen / 2;
}
Beispiel #6
0
void fftfilt_set_freqs(struct fftfilt *s, gdouble f1, gdouble f2)
{
	gint len = s->filterlen / 2 + 1;
	gdouble t, h, x;
	gint i;

	fft_clear_inbuf(s->tmpfft);

	for (i = 0; i < len; i++) {
		t = i - (len - 1.0) / 2.0;
		h = i / (len - 1.0);

		x = (2 * f2 * sinc(2 * f2 * t) -
		     2 * f1 * sinc(2 * f1 * t)) * hamming(h);

		c_re(s->tmpfft->in[i]) = x;
		c_im(s->tmpfft->in[i]) = 0.0;
#ifdef DEBUG
                fprintf(stderr, "% e\t", x);
#endif
	}

	fft_run(s->tmpfft);

	/*
	 * Scale down by 'filterlen' because inverse transform is 
	 * unscaled in FFTW.
	 */
	for (i = 0; i < s->filterlen; i++) {
		c_re(s->filter[i]) = c_re(s->tmpfft->out[i]) / s->filterlen;
		c_im(s->filter[i]) = c_im(s->tmpfft->out[i]) / s->filterlen;
	}

#ifdef DEBUG
	for (i = 0; i < s->filterlen; i++)
		fprintf(stderr, "% e\n", 10 * log10(cpwr(s->filter[i])));
#endif
}
// 
// AUTOTEST : test multi-stage arbitrary resampler
//
void autotest_msresamp_crcf()
{
    // options
    unsigned int m = 13;        // filter semi-length (filter delay)
    float r=0.127115323f;       // resampling rate (output/input)
    float As=60.0f;             // resampling filter stop-band attenuation [dB]
    unsigned int n=1200;        // number of input samples
    float fx=0.0254230646f;     // complex input sinusoid frequency (0.2*r)
    //float bw=0.45f;             // resampling filter bandwidth
    //unsigned int npfb=64;       // number of filters in bank (timing resolution)

    unsigned int i;

    // number of input samples (zero-padded)
    unsigned int nx = n + m;

    // output buffer with extra padding for good measure
    unsigned int y_len = (unsigned int) ceilf(1.1 * nx * r) + 4;

    // arrays
    float complex x[nx];
    float complex y[y_len];

    // create resampler
    msresamp_crcf q = msresamp_crcf_create(r,As);

    // generate input signal
    float wsum = 0.0f;
    for (i=0; i<nx; i++) {
        // compute window
        float w = i < n ? kaiser(i, n, 10.0f, 0.0f) : 0.0f;

        // apply window to complex sinusoid
        x[i] = cexpf(_Complex_I*2*M_PI*fx*i) * w;

        // accumulate window
        wsum += w;
    }

    // resample
    unsigned int ny=0;
    unsigned int nw;
    for (i=0; i<nx; i++) {
        // execute resampler, storing in output buffer
        msresamp_crcf_execute(q, &x[i], 1, &y[ny], &nw);

        // increment output size
        ny += nw;
    }

    // clean up allocated objects
    msresamp_crcf_destroy(q);

    // 
    // analyze resulting signal
    //

    // check that the actual resampling rate is close to the target
    float r_actual = (float)ny / (float)nx;
    float fy = fx / r;      // expected output frequency

    // run FFT and ensure that carrier has moved and that image
    // frequencies and distortion have been adequately suppressed
    unsigned int nfft = 1 << liquid_nextpow2(ny);
    float complex yfft[nfft];   // fft input
    float complex Yfft[nfft];   // fft output
    for (i=0; i<nfft; i++)
        yfft[i] = i < ny ? y[i] : 0.0f;
    fft_run(nfft, yfft, Yfft, LIQUID_FFT_FORWARD, 0);
    fft_shift(Yfft, nfft);  // run FFT shift

    // find peak frequency
    float Ypeak = 0.0f;
    float fpeak = 0.0f;
    float max_sidelobe = -1e9f;     // maximum side-lobe [dB]
    float main_lobe_width = 0.07f;  // TODO: figure this out from Kaiser's equations
    for (i=0; i<nfft; i++) {
        // normalized output frequency
        float f = (float)i/(float)nfft - 0.5f;

        // scale FFT output appropriately
        Yfft[i] /= (r * wsum);
        float Ymag = 20*log10f( cabsf(Yfft[i]) );

        // find frequency location of maximum magnitude
        if (Ymag > Ypeak || i==0) {
            Ypeak = Ymag;
            fpeak = f;
        }

        // find peak side-lobe value, ignoring frequencies
        // within a certain range of signal frequency
        if ( fabsf(f-fy) > main_lobe_width )
            max_sidelobe = Ymag > max_sidelobe ? Ymag : max_sidelobe;
    }

    if (liquid_autotest_verbose) {
        // print results
        printf("  desired resampling rate   :   %12.8f\n", r);
        printf("  measured resampling rate  :   %12.8f    (%u/%u)\n", r_actual, ny, nx);
        printf("  peak spectrum             :   %12.8f dB (expected 0.0 dB)\n", Ypeak);
        printf("  peak frequency            :   %12.8f    (expected %-12.8f)\n", fpeak, fy);
        printf("  max sidelobe              :   %12.8f dB (expected at least %.2f dB)\n", max_sidelobe, -As);
    }
    CONTEND_DELTA(     r_actual, r,    0.01f ); // check actual output sample rate
    CONTEND_DELTA(     Ypeak,    0.0f, 0.25f ); // peak should be about 0 dB
    CONTEND_DELTA(     fpeak,    fy,   0.01f ); // peak frequency should be nearly 0.2
    CONTEND_LESS_THAN( max_sidelobe, -As );     // maximum side-lobe should be sufficiently low

#if 0
    // export results for debugging
    char filename[] = "msresamp_crcf_autotest.m";
    FILE*fid = fopen(filename,"w");
    fprintf(fid,"%% %s: auto-generated file\n",filename);
    fprintf(fid,"clear all;\n");
    fprintf(fid,"close all;\n");
    fprintf(fid,"r    = %12.8f;\n", r);
    fprintf(fid,"nx   = %u;\n", nx);
    fprintf(fid,"ny   = %u;\n", ny);
    fprintf(fid,"nfft = %u;\n", nfft);

    fprintf(fid,"Y = zeros(1,nfft);\n");
    for (i=0; i<nfft; i++)
        fprintf(fid,"Y(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(Yfft[i]), cimagf(Yfft[i]));

    fprintf(fid,"\n\n");
    fprintf(fid,"%% plot frequency-domain result\n");
    fprintf(fid,"f=[0:(nfft-1)]/nfft-0.5;\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"plot(f,20*log10(abs(Y)),'Color',[0.25 0.5 0.0],'LineWidth',2);\n");
    fprintf(fid,"grid on;\n");
    fprintf(fid,"xlabel('normalized frequency');\n");
    fprintf(fid,"ylabel('PSD [dB]');\n");
    fprintf(fid,"axis([-0.5 0.5 -120 20]);\n");

    fclose(fid);
    printf("results written to %s\n",filename);
#endif
}
Beispiel #8
0
int main(int argc, char*argv[]) {
    // options
    unsigned int k=4;       // filter samples/symbol
    unsigned int m=3;       // filter delay [symbols]
    float beta = 0.3f;      // filter excess bandwidth factor

    // read properties from command line
    int dopt;
    while ((dopt = getopt(argc,argv,"uhk:m:b:")) != EOF) {
        switch (dopt) {
        case 'u':
        case 'h': usage();              return 0;
        case 'k': k     = atoi(optarg); break;
        case 'm': m     = atoi(optarg); break;
        case 'b': beta  = atof(optarg); break;
        default:
            exit(1);
        }
    }


    // validate input
    if (k < 2) {
        fprintf(stderr,"error: %s, k must be at least 2\n", argv[0]);
        exit(1);
    } else if (m < 1) {
        fprintf(stderr,"error: %s, m must be at least 1\n", argv[0]);
        exit(1);
    } else if (beta <= 0.0f || beta >= 1.0f) {
        fprintf(stderr,"error: %s, beta must be in (0,1)\n", argv[0]);
        exit(1);
    }


    unsigned int i;

    // derived values
    unsigned int h_len = 2*k*m+1;   // filter length

    // arrays
    float ht[h_len];         // transmit filter coefficients
    float hr[h_len];         // recieve filter coefficients

    //
    // start of filter design procedure
    //

    float H_prime[h_len];           // frequency response of Nyquist filter
    float complex h_tx[h_len];      // impulse response of square-root Nyquist filter
    float complex H_tx[h_len];      // frequency response of square-root Nyquist filter

    // compute frequency response of Nyquist filter
    for (i=0; i<h_len; i++) {
        float f = (float)i / (float)h_len;
        if (f > 0.5f) f = f - 1.0f;

        H_prime[i] = firdes_freqresponse_fexp(f,k,beta);
    }

    // compute square-root response, copy to fft input
    for (i=0; i<h_len; i++)
        H_tx[i] = sqrtf(H_prime[i]);

    // compute ifft and copy response
    fft_run(h_len, H_tx, h_tx, LIQUID_FFT_BACKWARD, 0);
    for (i=0; i<h_len; i++)
        ht[i] = crealf( h_tx[(i+k*m+1)%h_len] ) / (float)(h_len);

    // copy receive...
    for (i=0; i<h_len; i++)
        hr[i] = ht[i];

#if 0
    // print results
    for (i=0; i<h_len; i++)
        printf("H_prime(%3u) = %12.8f;\n", i+1, H_prime[i]);
#endif

    //
    // end of filter design procedure
    //

    // print results to screen
    printf("fexp receive filter:\n");
    for (i=0; i<h_len; i++)
        printf("  hr(%3u) = %12.8f;\n", i+1, hr[i]);

    // compute isi
    float rxy0 = liquid_filter_crosscorr(ht,h_len, hr,h_len, 0);
    float isi_rms = 0.0f;
    for (i=1; i<2*m; i++) {
        float e = liquid_filter_crosscorr(ht,h_len, hr,h_len, i*k) / rxy0;
        isi_rms += e*e;
    }
    isi_rms = sqrtf(isi_rms / (float)(2*m-1));
    printf("\n");
    printf("ISI (RMS) = %12.8f dB\n", 20*log10f(isi_rms));
    

    // 
    // export output file
    //
    FILE*fid = fopen(OUTPUT_FILENAME,"w");
    fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME);
    fprintf(fid,"clear all;\n");
    fprintf(fid,"close all;\n");
    fprintf(fid,"\n\n");
    fprintf(fid,"k = %u;\n", k);
    fprintf(fid,"m = %u;\n", m);
    fprintf(fid,"beta = %f;\n", beta);
    fprintf(fid,"h_len = 2*k*m+1;\n");
    fprintf(fid,"nfft = 1024;\n");
    fprintf(fid,"ht = zeros(1,h_len);\n");
    fprintf(fid,"hp = zeros(1,h_len);\n");
    fprintf(fid,"hr = zeros(1,h_len);\n");

    // print results
    for (i=0; i<h_len; i++)   fprintf(fid,"ht(%3u) = %12.4e;\n", i+1, ht[i]);
    for (i=0; i<h_len; i++)   fprintf(fid,"hr(%3u) = %12.4e;\n", i+1, hr[i]);
    
    fprintf(fid,"hc = k*conv(ht,hr);\n");

    // plot results
    fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n");
    fprintf(fid,"Ht = 20*log10(abs(fftshift(fft(ht,  nfft))));\n");
    fprintf(fid,"Hr = 20*log10(abs(fftshift(fft(hr,  nfft))));\n");
    fprintf(fid,"Hc = 20*log10(abs(fftshift(fft(hc/k,nfft))));\n");
    fprintf(fid,"\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"plot(f,Ht,'LineWidth',1,'Color',[0.00 0.25 0.50],...\n");
    fprintf(fid,"     f,Hr,'LineWidth',1,'Color',[0.00 0.50 0.25],...\n");
    fprintf(fid,"     f,Hc,'LineWidth',2,'Color',[0.50 0.00 0.00],...\n");
    fprintf(fid,"     [-0.5/k 0.5/k], [1 1]*20*log10(0.5),'or');\n");
    fprintf(fid,"legend('transmit','receive','composite','alias points',1);\n");
    fprintf(fid,"xlabel('Normalized Frequency');\n");
    fprintf(fid,"ylabel('PSD');\n");
    fprintf(fid,"grid on;\n");
    fprintf(fid,"axis([-0.5 0.5 -100 20]);\n");

    fprintf(fid,"\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"tr = [  -k*m:k*m]/k;\n");
    fprintf(fid,"tc = [-2*k*m:2*k*m]/k;\n");
    fprintf(fid,"ic = [0:k:(4*k*m)]+1;\n");
    fprintf(fid,"subplot(2,1,1);\n");
    fprintf(fid,"  plot(tr,ht,'-x', tr,hr,'-x');\n");
    fprintf(fid,"  legend('transmit','receive',1);\n");
    fprintf(fid,"  xlabel('Time');\n");
    fprintf(fid,"  ylabel('fexp Tx/Rx Filters');\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  axis([-2*m 2*m floor(5*min([hr ht]))/5 ceil(5*max([hr ht]))/5]);\n");
    fprintf(fid,"subplot(2,1,2);\n");
    fprintf(fid,"  plot(tc,hc,'-x', tc(ic),hc(ic),'or');\n");
    fprintf(fid,"  xlabel('Time');\n");
    fprintf(fid,"  ylabel('fexp Composite Response');\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  axis([-2*m 2*m -0.2 1.2]);\n");
    fprintf(fid,"  axis([-2*m 2*m floor(5*min(hc))/5 ceil(5*max(hc))/5]);\n");

    fclose(fid);
    printf("results written to %s.\n", OUTPUT_FILENAME);

    return 0;
}
int main(int argc, char*argv[])
{
    // options
    float r           = 1.1f;   // resampling rate (output/input)
    unsigned int m    = 13;     // resampling filter semi-length (filter delay)
    float As          = 60.0f;  // resampling filter stop-band attenuation [dB]
    float bw          = 0.45f;  // resampling filter bandwidth
    unsigned int npfb = 64;     // number of filters in bank (timing resolution)
    unsigned int n    = 400;    // number of input samples
    float fc          = 0.044f; // complex sinusoid frequency

    int dopt;
    while ((dopt = getopt(argc,argv,"hr:m:b:s:p:n:f:")) != EOF) {
        switch (dopt) {
        case 'h':   usage();            return 0;
        case 'r':   r    = atof(optarg); break;
        case 'm':   m    = atoi(optarg); break;
        case 'b':   bw   = atof(optarg); break;
        case 's':   As   = atof(optarg); break;
        case 'p':   npfb = atoi(optarg); break;
        case 'n':   n    = atoi(optarg); break;
        case 'f':   fc   = atof(optarg); break;
        default:
            exit(1);
        }
    }

    // validate input
    if (r <= 0.0f) {
        fprintf(stderr,"error: %s, resampling rate must be greater than zero\n", argv[0]);
        exit(1);
    } else if (m == 0) {
        fprintf(stderr,"error: %s, filter semi-length must be greater than zero\n", argv[0]);
        exit(1);
    } else if (bw == 0.0f || bw >= 0.5f) {
        fprintf(stderr,"error: %s, filter bandwidth must be in (0,0.5)\n", argv[0]);
        exit(1);
    } else if (As < 0.0f) {
        fprintf(stderr,"error: %s, filter stop-band attenuation must be greater than zero\n", argv[0]);
        exit(1);
    } else if (npfb == 0) {
        fprintf(stderr,"error: %s, filter bank size must be greater than zero\n", argv[0]);
        exit(1);
    } else if (n == 0) {
        fprintf(stderr,"error: %s, number of input samples must be greater than zero\n", argv[0]);
        exit(1);
    }

    unsigned int i;

    // number of input samples (zero-padded)
    unsigned int nx = n + m;

    // output buffer with extra padding for good measure
    unsigned int y_len = (unsigned int) ceilf(1.1 * nx * r) + 4;

    // arrays
    float complex x[nx];
    float complex y[y_len];

    // create resampler
    resamp_crcf q = resamp_crcf_create(r,m,bw,As,npfb);

    // generate input signal
    float wsum = 0.0f;
    for (i=0; i<nx; i++) {
        // compute window
        float w = i < n ? kaiser(i, n, 10.0f, 0.0f) : 0.0f;

        // apply window to complex sinusoid
        x[i] = cexpf(_Complex_I*2*M_PI*fc*i) * w;

        // accumulate window
        wsum += w;
    }

    // resample
    unsigned int ny=0;
#if 0
    // execute one sample at a time
    unsigned int nw;
    for (i=0; i<nx; i++) {
        // execute resampler, storing in output buffer
        resamp_crcf_execute(q, x[i], &y[ny], &nw);

        // increment output size
        ny += nw;
    }
#else
    // execute on block of samples
    resamp_crcf_execute_block(q, x, nx, y, &ny);
#endif

    // clean up allocated objects
    resamp_crcf_destroy(q);

    // 
    // analyze resulting signal
    //

    // check that the actual resampling rate is close to the target
    float r_actual = (float)ny / (float)nx;
    float fy = fc / r;      // expected output frequency

    // run FFT and ensure that carrier has moved and that image
    // frequencies and distortion have been adequately suppressed
    unsigned int nfft = 1 << liquid_nextpow2(ny);
    float complex yfft[nfft];   // fft input
    float complex Yfft[nfft];   // fft output
    for (i=0; i<nfft; i++)
        yfft[i] = i < ny ? y[i] : 0.0f;
    fft_run(nfft, yfft, Yfft, LIQUID_FFT_FORWARD, 0);
    fft_shift(Yfft, nfft);  // run FFT shift

    // find peak frequency
    float Ypeak = 0.0f;
    float fpeak = 0.0f;
    float max_sidelobe = -1e9f;     // maximum side-lobe [dB]
    float main_lobe_width = 0.07f;  // TODO: figure this out from Kaiser's equations
    for (i=0; i<nfft; i++) {
        // normalized output frequency
        float f = (float)i/(float)nfft - 0.5f;

        // scale FFT output appropriately
        float Ymag = 20*log10f( cabsf(Yfft[i] / (r * wsum)) );

        // find frequency location of maximum magnitude
        if (Ymag > Ypeak || i==0) {
            Ypeak = Ymag;
            fpeak = f;
        }

        // find peak side-lobe value, ignoring frequencies
        // within a certain range of signal frequency
        if ( fabsf(f-fy) > main_lobe_width )
            max_sidelobe = Ymag > max_sidelobe ? Ymag : max_sidelobe;
    }

    // print results and check frequency location
    printf("  desired resampling rate   :   %12.8f\n", r);
    printf("  measured resampling rate  :   %12.8f    (%u/%u)\n", r_actual, ny, nx);
    printf("  peak spectrum             :   %12.8f dB (expected 0.0 dB)\n", Ypeak);
    printf("  peak frequency            :   %12.8f    (expected %-12.8f)\n", fpeak, fy);
    printf("  max sidelobe              :   %12.8f dB (expected at least %.2f dB)\n", max_sidelobe, -As);


    // 
    // export results
    //
    FILE * fid = fopen(OUTPUT_FILENAME,"w");
    fprintf(fid,"%% %s: auto-generated file\n",OUTPUT_FILENAME);
    fprintf(fid,"clear all;\n");
    fprintf(fid,"close all;\n");
    fprintf(fid,"m=%u;\n", m);
    fprintf(fid,"npfb=%u;\n",  npfb);
    fprintf(fid,"r=%12.8f;\n", r);

    fprintf(fid,"nx = %u;\n", nx);
    fprintf(fid,"x = zeros(1,nx);\n");
    for (i=0; i<nx; i++)
        fprintf(fid,"x(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(x[i]), cimagf(x[i]));

    fprintf(fid,"ny = %u;\n", ny);
    fprintf(fid,"y = zeros(1,ny);\n");
    for (i=0; i<ny; i++)
        fprintf(fid,"y(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i]));

    fprintf(fid,"\n\n");
    fprintf(fid,"%% plot frequency-domain result\n");
    fprintf(fid,"nfft=2^nextpow2(max(nx,ny));\n");
    fprintf(fid,"%% estimate PSD, normalize by array length\n");
    fprintf(fid,"X=20*log10(abs(fftshift(fft(x,nfft)/length(x))));\n");
    fprintf(fid,"Y=20*log10(abs(fftshift(fft(y,nfft)/length(y))));\n");
    fprintf(fid,"G=max(X);\n");
    fprintf(fid,"X=X-G;\n");
    fprintf(fid,"Y=Y-G;\n");
    fprintf(fid,"f=[0:(nfft-1)]/nfft-0.5;\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"if r>1, fx = f/r; fy = f;   %% interpolated\n");
    fprintf(fid,"else,   fx = f;   fy = f*r; %% decimated\n");
    fprintf(fid,"end;\n");
    fprintf(fid,"plot(fx,X,'Color',[0.5 0.5 0.5],fy,Y,'LineWidth',2);\n");
    fprintf(fid,"grid on;\n");
    fprintf(fid,"xlabel('normalized frequency');\n");
    fprintf(fid,"ylabel('PSD [dB]');\n");
    fprintf(fid,"legend('original','resampled','location','northeast');");
    fprintf(fid,"axis([-0.5 0.5 -120 20]);\n");

    fprintf(fid,"\n\n");
    fprintf(fid,"%% plot time-domain result\n");
    fprintf(fid,"tx=[0:(length(x)-1)];\n");
    fprintf(fid,"ty=[0:(length(y)-1)]/r-m;\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"subplot(2,1,1);\n");
    fprintf(fid,"  plot(tx,real(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n");
    fprintf(fid,"       ty,real(y),'-s','Color',[0.5 0 0],    'MarkerSize',1);\n");
    fprintf(fid,"  legend('original','resampled','location','northeast');");
    fprintf(fid,"  xlabel('time');\n");
    fprintf(fid,"  ylabel('real');\n");
    fprintf(fid,"subplot(2,1,2);\n");
    fprintf(fid,"  plot(tx,imag(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n");
    fprintf(fid,"       ty,imag(y),'-s','Color',[0 0.5 0],    'MarkerSize',1);\n");
    fprintf(fid,"  legend('original','resampled','location','northeast');");
    fprintf(fid,"  xlabel('time');\n");
    fprintf(fid,"  ylabel('imag');\n");

    fclose(fid);
    printf("results written to %s\n",OUTPUT_FILENAME);

    printf("done.\n");
    return 0;
}
Beispiel #10
0
int main(int argc, char*argv[])
{
    // set random number generator seed
    srand(time(NULL));

    // options
    unsigned int M = 64;                // number of subcarriers
    unsigned int cp_len = 16;           // cyclic prefix length
    modulation_scheme ms = LIQUID_MODEM_BPSK;
    float SNRdB = 6.5f;                 // signal-to-noise ratio [dB]
    unsigned int hc_len = 1;            // channel impulse response length
    unsigned int num_symbols = 40;      // number of OFDM symbols

    // get options
    int dopt;
    while((dopt = getopt(argc,argv,"hs:M:C:m:n:c:")) != EOF){
        switch (dopt) {
        case 'h': usage(); return 0;
        case 's': SNRdB  = atof(optarg); break;
        case 'M': M      = atoi(optarg); break;
        case 'C': cp_len = atoi(optarg); break;
        case 'm':
            ms = liquid_getopt_str2mod(optarg);
            if (ms == LIQUID_MODEM_UNKNOWN) {
                fprintf(stderr,"error: %s, unknown/unsupported mod. scheme: %s\n", argv[0], optarg);
                exit(-1);
            }
            break;
        case 'n': num_symbols = atoi(optarg); break;
        case 'c': hc_len      = atoi(optarg); break;
        default:
            exit(-1);
        }
    }

    unsigned int i;

    // validate options
    if (M < 4) {
        fprintf(stderr,"error: %s, must have at least 4 subcarriers\n", argv[0]);
        exit(1);
    } else if (hc_len == 0) {
        fprintf(stderr,"error: %s, must have at least 1 channel tap\n", argv[0]);
        exit(1);
    }

    // derived values
    unsigned int symbol_len = M + cp_len;
    float nstd = powf(10.0f, -SNRdB/20.0f);
    float fft_gain = 1.0f / sqrtf(M);   // 'gain' due to taking FFT
    
    // buffers
    unsigned int sym_in[M];             // input data symbols
    unsigned int sym_out[M];            // output data symbols
    float complex x[M];                 // time-domain buffer
    float complex X[M];                 // freq-domain buffer
    float complex buffer[symbol_len];   // 

    // create modulator/demodulator objects
    modem mod   = modem_create(ms);
    modem demod = modem_create(ms);
    unsigned int bps = modem_get_bps(mod);  // modem bits/symbol

    // create channel filter (random taps)
    float complex hc[hc_len];
    hc[0] = 1.0f;
    for (i=1; i<hc_len; i++)
        hc[i] = 0.1f * (randnf() + _Complex_I*randnf());
    firfilt_cccf fchannel = firfilt_cccf_create(hc, hc_len);

    //
    unsigned int n;
    unsigned int num_bit_errors = 0;
    for (n=0; n<num_symbols; n++) {
        // generate random data symbols and modulate onto subcarriers
        for (i=0; i<M; i++) {
            sym_in[i] = rand() % (1<<bps);

            modem_modulate(mod, sym_in[i], &X[i]);
        }

        // run inverse transform
        fft_run(M, X, x, LIQUID_FFT_BACKWARD, 0);

        // scale by FFT gain so E{|x|^2} = 1
        for (i=0; i<M; i++)
            x[i] *= fft_gain;

        // apply channel impairments
        for (i=0; i<M + cp_len; i++) {
            // push samples through channel filter, starting with cyclic prefix
            firfilt_cccf_push(fchannel, x[(M-cp_len+i)%M]);

            // compute output
            firfilt_cccf_execute(fchannel, &buffer[i]);

            // add noise
            buffer[i] += nstd*( randnf() + _Complex_I*randnf() ) * M_SQRT1_2;
        }

        // run forward transform
        fft_run(M, &buffer[cp_len], X, LIQUID_FFT_FORWARD, 0);

        // TODO : apply equalizer to 'X' here

        // demodulate and compute bit errors
        for (i=0; i<M; i++) {
            // scale by fft size
            X[i] *= fft_gain;

            modem_demodulate(demod, X[i], &sym_out[i]);

            num_bit_errors += liquid_count_ones(sym_in[i] ^ sym_out[i]);
        }
    }

    // destroy objects
    modem_destroy(mod);
    modem_destroy(demod);
    firfilt_cccf_destroy(fchannel);

    // print results
    unsigned int total_bits = M*bps*num_symbols;
    float ber = (float)num_bit_errors / (float)total_bits;
    printf("  bit errors : %6u / %6u (%12.4e)\n", num_bit_errors, total_bits, ber);

    printf("done.\n");
    return 0;
}
int main(int argc, char*argv[]) {
    // options
    unsigned int k=4;       // samples/symbol
    unsigned int m=3;       // filter delay [symbols]
    float BT = 0.3f;        // bandwidth-time product

    // read properties from command line
    int dopt;
    while ((dopt = getopt(argc,argv,"uhk:m:b:")) != EOF) {
        switch (dopt) {
        case 'u':
        case 'h':   usage();            return 0;
        case 'k':   k  = atoi(optarg);  break;
        case 'm':   m  = atoi(optarg);  break;
        case 'b':   BT = atof(optarg);  break;
        default:
            exit(1);
        }
    }


    // validate input
    if (k < 2) {
        fprintf(stderr,"error: %s, k must be at least 2\n", argv[0]);
        exit(1);
    } else if (m < 1) {
        fprintf(stderr,"error: %s, m must be at least 1\n", argv[0]);
        exit(1);
    } else if (BT <= 0.0f || BT >= 1.0f) {
        fprintf(stderr,"error: %s, BT must be in (0,1)\n", argv[0]);
        exit(1);
    }


    unsigned int i;

    // derived values
    unsigned int h_len = 2*k*m+1;   // filter length

    // arrays
    float ht[h_len];         // transmit filter coefficients
    float hr[h_len];         // recieve filter coefficients

    // design transmit filter
    liquid_firdes_gmsktx(k,m,BT,0.0f,ht);

    // print results to screen
    printf("gmsk transmit filter:\n");
    for (i=0; i<h_len; i++)
        printf("  ht(%3u) = %12.8f;\n", i+1, ht[i]);

    //
    // start of filter design procedure
    //

    float beta = BT;        // prototype filter cut-off
    float delta = 1e-2f;    // filter design correction factor

    // temporary arrays
    float h_primef[h_len];          // temporary buffer for real coefficients
    float g_primef[h_len];          // 

    float complex h_tx[h_len];      // impulse response of transmit filter
    float complex h_prime[h_len];   // impulse response of 'prototype' filter
    float complex g_prime[h_len];   // impulse response of 'gain' filter
    float complex h_hat[h_len];     // impulse response of receive filter
    
    float complex H_tx[h_len];      // frequency response of transmit filter
    float complex H_prime[h_len];   // frequency response of 'prototype' filter
    float complex G_prime[h_len];   // frequency response of 'gain' filter
    float complex H_hat[h_len];     // frequency response of receive filter

    // create 'prototype' matched filter
    // for now use raised-cosine
    liquid_firdes_nyquist(LIQUID_NYQUIST_RCOS,k,m,beta,0.0f,h_primef);

    // create 'gain' filter to improve stop-band rejection
    float fc = (0.7f + 0.1*beta) / (float)k;
    float As = 60.0f;
    liquid_firdes_kaiser(h_len, fc, As, 0.0f, g_primef);

    // copy to fft input buffer, shifting appropriately
    for (i=0; i<h_len; i++) {
        h_prime[i] = h_primef[ (i+k*m)%h_len ];
        g_prime[i] = g_primef[ (i+k*m)%h_len ];
        h_tx[i]    = ht[       (i+k*m)%h_len ];
    }

    // run ffts
    fft_run(h_len, h_prime, H_prime, FFT_FORWARD, 0);
    fft_run(h_len, g_prime, G_prime, FFT_FORWARD, 0);
    fft_run(h_len, h_tx,    H_tx,    FFT_FORWARD, 0);

#if 0
    // print results
    for (i=0; i<h_len; i++)
        printf("Ht(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(H_tx[i]), cimagf(H_tx[i]));
    for (i=0; i<h_len; i++)
        printf("H_prime(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(H_prime[i]), cimagf(H_prime[i]));
    for (i=0; i<h_len; i++)
        printf("G_prime(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(G_prime[i]), cimagf(G_prime[i]));
#endif

    // find minimum of reponses
    float H_tx_min = 0.0f;
    float H_prime_min = 0.0f;
    float G_prime_min = 0.0f;
    for (i=0; i<h_len; i++) {
        if (i==0 || crealf(H_tx[i])    < H_tx_min)    H_tx_min    = crealf(H_tx[i]);
        if (i==0 || crealf(H_prime[i]) < H_prime_min) H_prime_min = crealf(H_prime[i]);
        if (i==0 || crealf(G_prime[i]) < G_prime_min) G_prime_min = crealf(G_prime[i]);
    }

    // compute 'prototype' response, removing minima, and add correction factor
    for (i=0; i<h_len; i++) {
        // compute response necessary to yeild prototype response (not exact, but close)
        H_hat[i] = crealf(H_prime[i] - H_prime_min + delta) / crealf(H_tx[i] - H_tx_min + delta);

        // include additional term to add stop-band suppression
        H_hat[i] *= crealf(G_prime[i] - G_prime_min) / crealf(G_prime[0]);
    }

    // compute ifft and copy response
    fft_run(h_len, H_hat, h_hat, FFT_REVERSE, 0);
    for (i=0; i<h_len; i++)
        hr[i] = crealf( h_hat[(i+k*m+1)%h_len] ) / (float)(k*h_len);

    //
    // end of filter design procedure
    //

    // print results to screen
    printf("gmsk receive filter:\n");
    for (i=0; i<h_len; i++)
        printf("  hr(%3u) = %12.8f;\n", i+1, hr[i]);

    // compute isi
    float rxy0 = liquid_filter_crosscorr(ht,h_len, hr,h_len, 0);
    float isi_rms = 0.0f;
    for (i=1; i<2*m; i++) {
        float e = liquid_filter_crosscorr(ht,h_len, hr,h_len, i*k) / rxy0;
        isi_rms += e*e;
    }
    isi_rms = sqrtf(isi_rms / (float)(2*m-1));
    printf("\n");
    printf("ISI (RMS) = %12.8f dB\n", 20*log10f(isi_rms));
    

    // 
    // export output file
    //
    FILE*fid = fopen(OUTPUT_FILENAME,"w");
    fprintf(fid,"%% %s : auto-generated file\n", OUTPUT_FILENAME);
    fprintf(fid,"clear all;\n");
    fprintf(fid,"close all;\n");
    fprintf(fid,"\n\n");
    fprintf(fid,"k = %u;\n", k);
    fprintf(fid,"m = %u;\n", m);
    fprintf(fid,"beta = %f;\n", BT);
    fprintf(fid,"h_len = 2*k*m+1;\n");
    fprintf(fid,"nfft = 1024;\n");
    fprintf(fid,"ht = zeros(1,h_len);\n");
    fprintf(fid,"hp = zeros(1,h_len);\n");
    fprintf(fid,"hr = zeros(1,h_len);\n");

    // print results
    for (i=0; i<h_len; i++)   fprintf(fid,"ht(%3u) = %12.4e;\n", i+1, ht[i] / k);
    for (i=0; i<h_len; i++)   fprintf(fid,"hr(%3u) = %12.4e;\n", i+1, hr[i] * k);
    for (i=0; i<h_len; i++)   fprintf(fid,"hp(%3u) = %12.4e;\n", i+1, h_primef[i]);
    
    fprintf(fid,"hc = k*conv(ht,hr);\n");

    // plot results
    fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n");
    fprintf(fid,"Ht = 20*log10(abs(fftshift(fft(ht,  nfft))));\n");
    fprintf(fid,"Hp = 20*log10(abs(fftshift(fft(hp/k,nfft))));\n");
    fprintf(fid,"Hr = 20*log10(abs(fftshift(fft(hr,  nfft))));\n");
    fprintf(fid,"Hc = 20*log10(abs(fftshift(fft(hc/k,nfft))));\n");
    fprintf(fid,"\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"plot(f,Ht,'LineWidth',1,'Color',[0.00 0.25 0.50],...\n");
    fprintf(fid,"     f,Hp,'LineWidth',1,'Color',[0.80 0.80 0.80],...\n");
    fprintf(fid,"     f,Hr,'LineWidth',1,'Color',[0.00 0.50 0.25],...\n");
    fprintf(fid,"     f,Hc,'LineWidth',2,'Color',[0.50 0.00 0.00],...\n");
    fprintf(fid,"     [-0.5/k 0.5/k], [1 1]*20*log10(0.5),'or');\n");
    fprintf(fid,"legend('transmit','prototype','receive','composite','alias points',1);\n");
    fprintf(fid,"xlabel('Normalized Frequency');\n");
    fprintf(fid,"ylabel('PSD');\n");
    fprintf(fid,"grid on;\n");
    fprintf(fid,"axis([-0.5 0.5 -100 20]);\n");

    fprintf(fid,"\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"tr = [  -k*m:k*m]/k;\n");
    fprintf(fid,"tc = [-2*k*m:2*k*m]/k;\n");
    fprintf(fid,"ic = [0:k:(4*k*m)]+1;\n");
    fprintf(fid,"subplot(2,1,1);\n");
    fprintf(fid,"  plot(tr,ht,'-x', tr,hr,'-x');\n");
    fprintf(fid,"  legend('transmit','receive',1);\n");
    fprintf(fid,"  xlabel('Time');\n");
    fprintf(fid,"  ylabel('GMSK Tx/Rx Filters');\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  axis([-2*m 2*m floor(5*min([hr ht]))/5 ceil(5*max([hr ht]))/5]);\n");
    fprintf(fid,"subplot(2,1,2);\n");
    fprintf(fid,"  plot(tc,hc,'-x', tc(ic),hc(ic),'or');\n");
    fprintf(fid,"  xlabel('Time');\n");
    fprintf(fid,"  ylabel('GMSK Composite Response');\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  axis([-2*m 2*m -0.2 1.2]);\n");
    fprintf(fid,"  axis([-2*m 2*m floor(5*min(hc))/5 ceil(5*max(hc))/5]);\n");

    fclose(fid);
    printf("results written to %s.\n", OUTPUT_FILENAME);

    return 0;
}
void
InputProcessor::fftAnalyze() {
  fft_reorder();
  fft_run();
}
Beispiel #13
0
// Design GMSK receive filter
//  _k      : samples/symbol
//  _m      : symbol delay
//  _beta   : rolloff factor (0 < beta <= 1)
//  _dt     : fractional sample delay
//  _h      : output coefficient buffer (length: 2*k*m+1)
void liquid_firdes_gmskrx(unsigned int _k,
                          unsigned int _m,
                          float        _beta,
                          float        _dt,
                          float *      _h)
{
    // validate input
    if ( _k < 1 ) {
        fprintf(stderr,"error: liquid_firdes_gmskrx(): k must be greater than 0\n");
        exit(1);
    } else if ( _m < 1 ) {
        fprintf(stderr,"error: liquid_firdes_gmskrx(): m must be greater than 0\n");
        exit(1);
    } else if ( (_beta < 0.0f) || (_beta > 1.0f) ) {
        fprintf(stderr,"error: liquid_firdes_gmskrx(): beta must be in [0,1]\n");
        exit(1);
    } else;

    unsigned int k = _k;
    unsigned int m = _m;
    float BT = _beta;

    // internal options
    float beta = BT;                // prototype filter cut-off
    float delta = 1e-3f;            // filter design correction factor
    liquid_firfilt_type prototype = LIQUID_FIRFILT_KAISER;    // Nyquist prototype

    unsigned int i;

    // derived values
    unsigned int h_len = 2*k*m+1;   // filter length

    // arrays
    float ht[h_len];         // transmit filter coefficients
    float hr[h_len];         // recieve filter coefficients

    // design transmit filter
    liquid_firdes_gmsktx(k,m,BT,0.0f,ht);

    //
    // start of filter design procedure
    //

    // 'internal' arrays
    float h_primef[h_len];          // temporary buffer for real 'prototype' coefficients
    float g_primef[h_len];          // temporary buffer for real 'gain' coefficient

    float complex h_tx[h_len];      // impulse response of transmit filter
    float complex h_prime[h_len];   // impulse response of 'prototype' filter
    float complex g_prime[h_len];   // impulse response of 'gain' filter
    float complex h_hat[h_len];     // impulse response of receive filter
    
    float complex H_tx[h_len];      // frequency response of transmit filter
    float complex H_prime[h_len];   // frequency response of 'prototype' filter
    float complex G_prime[h_len];   // frequency response of 'gain' filter
    float complex H_hat[h_len];     // frequency response of receive filter

    // create 'prototype' matched filter
    liquid_firdes_nyquist(prototype,k,m,beta,0.0f,h_primef);

    // create 'gain' filter to improve stop-band rejection
    float fc = (0.7f + 0.1*beta) / (float)k;
    float As = 60.0f;
    liquid_firdes_kaiser(h_len, fc, As, 0.0f, g_primef);

    // copy to fft input buffer, shifting appropriately
    for (i=0; i<h_len; i++) {
        h_prime[i] = h_primef[ (i+k*m)%h_len ];
        g_prime[i] = g_primef[ (i+k*m)%h_len ];
        h_tx[i]    = ht[       (i+k*m)%h_len ];
    }

    // run ffts
    fft_run(h_len, h_prime, H_prime, LIQUID_FFT_FORWARD, 0);
    fft_run(h_len, g_prime, G_prime, LIQUID_FFT_FORWARD, 0);
    fft_run(h_len, h_tx,    H_tx,    LIQUID_FFT_FORWARD, 0);

    // find minimum of reponses
    float H_tx_min = 0.0f;
    float H_prime_min = 0.0f;
    float G_prime_min = 0.0f;
    for (i=0; i<h_len; i++) {
        if (i==0 || crealf(H_tx[i])    < H_tx_min)    H_tx_min    = crealf(H_tx[i]);
        if (i==0 || crealf(H_prime[i]) < H_prime_min) H_prime_min = crealf(H_prime[i]);
        if (i==0 || crealf(G_prime[i]) < G_prime_min) G_prime_min = crealf(G_prime[i]);
    }

    // compute 'prototype' response, removing minima, and add correction factor
    for (i=0; i<h_len; i++) {
        // compute response necessary to yeild prototype response (not exact, but close)
        H_hat[i] = crealf(H_prime[i] - H_prime_min + delta) / crealf(H_tx[i] - H_tx_min + delta);

        // include additional term to add stop-band suppression
        H_hat[i] *= crealf(G_prime[i] - G_prime_min) / crealf(G_prime[0]);
    }

    // compute ifft and copy response
    fft_run(h_len, H_hat, h_hat, LIQUID_FFT_BACKWARD, 0);
    for (i=0; i<h_len; i++)
        hr[i] = crealf( h_hat[(i+k*m+1)%h_len] ) / (float)(k*h_len);

    // copy result, scaling by (samples/symbol)^2
    for (i=0; i<h_len; i++)
        _h[i] = hr[i]*_k*_k;
}
int main()
{
  int fd=inotify_init();
  int in=inotify_add_watch(fd,"/dev/shm",IN_CLOSE_WRITE);
  char buf[BUF_LEN];
  pgm_init();
  fft_init();
  if (!glfwInit())
    exit(EXIT_FAILURE);
  GLFWwindow* window = glfwCreateWindow(width[0]+width[1], (int)fmax(2*height[0],2*height[1]),
					"gig-e-camera", NULL, NULL);
  glfwMakeContextCurrent(window);
  const int n_tex=4;
  GLuint texture[n_tex];
  glGenTextures( n_tex, texture );
  int i;
  for(i=0;i<n_tex;i++){
    glBindTexture( GL_TEXTURE_2D, texture[i] );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                     GL_NEAREST );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
    glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,width[i/2],height[i/2],0,
		 GL_LUMINANCE,GL_UNSIGNED_SHORT,0);
  }  
  glEnable(GL_TEXTURE_2D);
 
  while (1){
    int len, i = 0;
    len = read (fd, buf, BUF_LEN);
    
    if(len<=0)
      printf("error\n");
    
    while (i < len) {
      struct inotify_event *event;
      event = (struct inotify_event *) &buf[i];
      printf ("wd=%d mask=%u cookie=%u len=%u ",
	      event->wd, event->mask,
	      event->cookie, event->len);
      if (event->len)
	printf ("name=%s\n", event->name);
      else
	printf("\n");

      i += EVENT_SIZE + event->len;
    }
    
          {
	int i;
	fft_fill(); fft_run();
	for(i=0;i<2;i++){
	  glBindTexture( GL_TEXTURE_2D, texture[2*i] );
	  glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width[i],height[i],GL_LUMINANCE,GL_UNSIGNED_SHORT,kspace[i]);
	  glBindTexture( GL_TEXTURE_2D, texture[2*i+1] );
	  glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width[i],height[i],GL_LUMINANCE,GL_UNSIGNED_SHORT,image[i]);
	}
	{
	  int win_width,win_height;
	  glfwGetFramebufferSize(window, &win_width, &win_height);
	  glViewport(0, 0, win_width, win_height);
	}
	
	glClear(GL_COLOR_BUFFER_BIT);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(0, width[0]+width[1], 0,fmax(2*height[0],2*height[1]), 1.f, -1.f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glColor3f(1.f,1.f,1.f);
	draw_quad(texture[0],0,0,width[0],height[0]);
	draw_quad(texture[1],0,height[0],width[0],height[0]);
	draw_quad(texture[2],width[0],0,width[1],height[1]);
	draw_quad(texture[3],width[0],height[1],width[1],height[1]);
	
	glfwSwapBuffers(window);
      }

  }
  glfwTerminate();
}
int main(int argc, char*argv[])
{
    // options
    unsigned int num_channels=16;   // number of channels
    unsigned int m = 5;             // filter semi-length (symbols)
    unsigned int num_symbols=25;    // number of symbols
    float As = 80.0f;               // filter stop-band attenuation
    
    int dopt;
    while ((dopt = getopt(argc,argv,"hM:m:s:n:")) != EOF) {
        switch (dopt) {
        case 'h':   usage();                     return 0;
        case 'M':   num_channels = atoi(optarg); break;
        case 'm':   m            = atoi(optarg); break;
        case 's':   As           = atof(optarg); break;
        case 'n':   num_symbols  = atof(optarg); break;
        default:
            exit(1);
        }
    }

    unsigned int i;

    // validate input
    if (num_channels < 2 || num_channels % 2) {
        fprintf(stderr,"error: %s, number of channels must be greater than 2 and even\n", argv[0]);
        exit(1);
    } else if (m == 0) {
        fprintf(stderr,"error: %s, filter semi-length must be greater than zero\n", argv[0]);
        exit(1);
    } else if (num_symbols == 0) {
        fprintf(stderr,"error: %s, number of symbols must be greater than zero", argv[0]);
        exit(1);
    }

    // derived values
    unsigned int num_samples = num_channels * num_symbols;

    // allocate arrays
    float complex x[num_samples];
    float complex y[num_samples];

    // generate input signal
    unsigned int w_len = (unsigned int)(0.4*num_samples);
    for (i=0; i<num_samples; i++) {
        //x[i] = (i==0) ? 1.0f : 0.0f;
        //x[i] = cexpf( (-0.05f + 0.07f*_Complex_I)*i );  // decaying complex exponential
        x[i] = cexpf( _Complex_I * (1.3f*i - 0.007f*i*i) );
        x[i] *= i < w_len ? hamming(i,w_len) : 0.0f;
        //x[i] = (i==0) ? 1.0f : 0.0f;
    }

    // create filterbank objects from prototype
    firpfbch2_crcf qa = firpfbch2_crcf_create_kaiser(LIQUID_ANALYZER,    num_channels, m, As);
    firpfbch2_crcf qs = firpfbch2_crcf_create_kaiser(LIQUID_SYNTHESIZER, num_channels, m, As);
    firpfbch2_crcf_print(qa);
    firpfbch2_crcf_print(qs);

    // run channelizer
    float complex Y[num_channels];
    for (i=0; i<num_samples; i+=num_channels/2) {
        // run analysis filterbank
        firpfbch2_crcf_execute(qa, &x[i], Y);

        // run synthesis filterbank
        firpfbch2_crcf_execute(qs, Y, &y[i]);
    }

    // destroy fiterbank objects
    firpfbch2_crcf_destroy(qa); // analysis fitlerbank
    firpfbch2_crcf_destroy(qs); // synthesis filterbank

    // print output
    for (i=0; i<num_samples; i++)
        printf("%4u : %12.8f + %12.8fj\n", i, crealf(y[i]), cimagf(y[i]));

    // compute RMSE
    float rmse = 0.0f;
    unsigned int delay = 2*num_channels*m - num_channels/2 + 1;
    for (i=0; i<num_samples; i++) {
        float complex err = y[i] - (i < delay ? 0.0f : x[i-delay]);
        rmse += crealf( err*conjf(err) );
    }
    rmse = sqrtf( rmse/(float)num_samples );
    printf("rmse : %12.4e\n", rmse);

    //
    // EXPORT DATA TO FILES
    //
    FILE * fid = NULL;
    fid = fopen(OUTPUT_FILENAME_TIME,"w");
    fprintf(fid,"# %s: auto-generated file\n", OUTPUT_FILENAME_TIME);
    fprintf(fid,"#\n");
    fprintf(fid,"# %8s %12s %12s %12s %12s %12s %12s\n",
            "time", "real(x)", "imag(x)", "real(y)", "imag(y)", "real(e)", "imag(e)");

    // save input and output arrays
    for (i=0; i<num_samples; i++) {
        float complex e = (i < delay) ? 0.0f : y[i] - x[i-delay];
        fprintf(fid,"  %8.1f %12.4e %12.4e %12.4e %12.4e %12.4e %12.4e\n",
                (float)i,
                crealf(x[i]), cimagf(x[i]),
                crealf(y[i]), cimagf(y[i]),
                crealf(e),    cimagf(e));
    }
    fclose(fid);
    printf("results written to '%s'\n", OUTPUT_FILENAME_TIME);

    // 
    // export frequency data
    //
    unsigned int nfft = 2048;
    float complex y_time[nfft];
    float complex y_freq[nfft];
    for (i=0; i<nfft; i++)
        y_time[i] = i < num_samples ? y[i] : 0.0f;
    fft_run(nfft, y_time, y_freq, LIQUID_FFT_FORWARD, 0);

    // filter spectrum
    unsigned int h_len = 2*num_channels*m+1;
    float h[h_len];
    float fc = 0.5f/(float)num_channels;
    liquid_firdes_kaiser(h_len, fc, As, 0.0f, h);
    float complex h_time[nfft];
    float complex h_freq[nfft];
    for (i=0; i<nfft; i++)
        h_time[i] = i < h_len ? 2*h[i]*fc : 0.0f;
    fft_run(nfft, h_time, h_freq, LIQUID_FFT_FORWARD, 0);

    // error spectrum
    float complex e_time[nfft];
    float complex e_freq[nfft];
    for (i=0; i<nfft; i++)
        e_time[i] = i < delay || i > num_samples ? 0.0f : y[i] - x[i-delay];
    fft_run(nfft, e_time, e_freq, LIQUID_FFT_FORWARD, 0);

    fid = fopen(OUTPUT_FILENAME_FREQ,"w");
    fprintf(fid,"# %s: auto-generated file\n", OUTPUT_FILENAME_FREQ);
    fprintf(fid,"#\n");
    fprintf(fid,"# nfft = %u\n", nfft);
    fprintf(fid,"# %12s %12s %12s %12s\n", "freq", "PSD [dB]", "filter [dB]", "error [dB]");

    // save input and output arrays
    for (i=0; i<nfft; i++) {
        float f = (float)i/(float)nfft - 0.5f;
        unsigned int k = (i + nfft/2)%nfft;
        fprintf(fid,"  %12.8f %12.8f %12.8f %12.8f\n",
                f,
                20*log10f(cabsf(y_freq[k])),
                20*log10f(cabsf(h_freq[k])),
                20*log10f(cabsf(e_freq[k])));
    }
    fclose(fid);
    printf("results written to '%s'\n", OUTPUT_FILENAME_FREQ);

    printf("done.\n");
    return 0;
}