PfbSynthesizerComponent::PfbSynthesizerComponent(std::string name)
  : PhyComponent(name,                      // component name
                "pfbsynthesizer",           // component type
                "A polyphase filterbank synthesizer",  // description
                "Paul Sutton",              // author
                "0.1")                      // version
{
  taps.push_back(0.0f);
  channelizer = firpfbch_crcf_create(LIQUID_SYNTHESIZER, 1, 1, &taps[0]);

  registerParameter(
    "debug", "Running in debug mode?",
    "false", false, debug_x);

  registerParameter(
    "numchannels", "Number of channels",
    "8", false, nChans_x, Interval<int>(1,65536));
}
int main() {
    // options
    unsigned int num_channels = 16;     // number of channels
    unsigned int m            =  5;     // filter delay
    float        As           = 60;     // stop-band attenuation
    unsigned int num_frames   = 25;     // number of frames

    //
    unsigned int i;
    unsigned int k;

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

    // data arrays
    float complex x[num_channels][num_frames];  // channelized input
    float complex y[num_samples];               // time-domain output [size: num_samples  x 1]

    // create narrow-band pulse
    unsigned int pulse_len = 17;        // pulse length [samples]
    float        bw        = 0.30f;     // pulse width (bandwidth)
    float        pulse[pulse_len];      // buffer
    liquid_firdes_kaiser(pulse_len, bw, 50.0f, 0.0f, pulse);

    // generate input signal(s)
    int enabled[num_channels];  // signal enabled?
    for (i=0; i<num_channels; i++) {
        // pseudo-random channel enabled flag
        enabled[i] = ((i*37)%101)%2;

        if (enabled[i]) {
            // move pulse to input buffer
            for (k=0; k<num_frames; k++)
                x[i][k] = k < pulse_len ? bw*pulse[k] : 0.0f;
        } else {
            // channel disabled; set as nearly zero (-100 dB impulse)
            for (k=0; k<num_frames; k++)
                x[i][k] = (k == 0) ? 1e-5f : 0.0f;
        }
    }

    // create prototype filter
    unsigned int h_len = 2*num_channels*m + 1;
    float h[h_len];
    liquid_firdes_kaiser(h_len, 0.5f/(float)num_channels, As, 0.0f, h);

#if 0
    // create filterbank channelizer object using internal method for filter
    firpfbch_crcf q = firpfbch_crcf_create_kaiser(LIQUID_SYNTHESIZER, num_channels, m, As);
#else
    // create filterbank channelizer object using external filter coefficients
    firpfbch_crcf q = firpfbch_crcf_create(LIQUID_SYNTHESIZER, num_channels, 2*m, h);
#endif

    // channelize input data
    float complex v[num_channels];
    for (i=0; i<num_frames; i++) {
        // assemble input vector
        for (k=0; k<num_channels; k++)
            v[k] = x[k][i];

        // execute synthesis filter bank
        firpfbch_crcf_synthesizer_execute(q, v, &y[i*num_channels]);
    }

    // destroy channelizer object
    firpfbch_crcf_destroy(q);
    
    // 
    // export results to file
    //
    FILE * fid = fopen(OUTPUT_FILENAME,"w");
    fprintf(fid,"%% %s: auto-generated file\n\n", OUTPUT_FILENAME);
    fprintf(fid,"clear all;\n");
    fprintf(fid,"close all;\n");
    fprintf(fid,"num_channels = %u;\n", num_channels);
    fprintf(fid,"m            = %u;\n", m);
    fprintf(fid,"num_frames   = %u;\n", num_frames);
    fprintf(fid,"h_len        = 2*num_channels*m+1;\n");
    fprintf(fid,"num_samples  = num_frames*num_channels;\n");

    fprintf(fid,"h = zeros(1,h_len);\n");
    fprintf(fid,"x = zeros(num_channels, num_frames);\n");
    fprintf(fid,"y = zeros(1,num_samples);\n");

    // save prototype filter
    for (i=0; i<h_len; i++)
        fprintf(fid,"  h(%6u) = %12.4e;\n", i+1, h[i]);

    // save channelized input signals
    for (i=0; i<num_frames; i++) {
        for (k=0; k<num_channels; k++) {
            float complex v = x[k][i];
            fprintf(fid,"  x(%3u,%6u) = %12.4e + 1i*%12.4e;\n", k+1, i+1, crealf(v), cimagf(v));
        }
    }

    // save output time signal
    for (i=0; i<num_samples; i++)
        fprintf(fid,"  y(%6u) = %12.4e + 1i*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i]));

    // compute the PSD of each input and plot results on a square grid
    fprintf(fid,"nfft = 1024;\n"); // TODO: use nextpow2
    fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n");
    fprintf(fid,"n = ceil(sqrt(num_channels));\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"for i=1:num_channels,\n");
    fprintf(fid,"  X = 20*log10(abs(fftshift(fft(x(i,:),nfft))));\n");
    fprintf(fid,"  subplot(n,n,i);\n");
    fprintf(fid,"  plot(f, X, 'Color', [0.25 0 0.25], 'LineWidth', 1.5);\n");
    fprintf(fid,"  axis([-0.5 0.5 -120 20]);\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  title(num2str(i-1));\n");
    fprintf(fid,"end;\n");

    // plot results
    fprintf(fid,"\n");
    fprintf(fid,"H = 20*log10(abs(fftshift(fft(h/num_channels,nfft))));\n");
    fprintf(fid,"Y = 20*log10(abs(fftshift(fft(y/num_channels,nfft))));\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"subplot(2,1,1);\n");
    fprintf(fid,"  plot(f, H, 'Color', [0 0.5 0.25], 'LineWidth', 2);\n");
    fprintf(fid,"  axis([-0.5 0.5 -100 10]);\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  xlabel('Normalized Frequency [f/F_s]');\n");
    fprintf(fid,"  ylabel('Prototype Filter PSD');\n");
    fprintf(fid,"subplot(2,1,2);\n");
    fprintf(fid,"  plot(f, Y, 'Color', [0 0.25 0.5], 'LineWidth', 2);\n");
    fprintf(fid,"  axis([-0.5 0.5 -100 0]);\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  xlabel('Normalized Frequency [f/F_s]');\n");
    fprintf(fid,"  ylabel('Output PSD');\n");

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

    printf("done.\n");
    return 0;
}
Ejemplo n.º 3
0
int main() {
    // options
    unsigned int num_channels=4;    // number of channels
    unsigned int p=3;               // filter length (symbols)
    unsigned int num_symbols=6;     // number of symbols

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

    unsigned int i;
    unsigned int j;

    // generate synthesis filter
    // NOTE : these coefficients can be random; the purpose of this
    //        exercise is to demonstrate mathematical equivalence
    unsigned int h_len = p*num_channels;
    float h[h_len];
    for (i=0; i<h_len; i++) h[i] = randnf();

    // generate analysis filter
    unsigned int g_len = p*num_channels;
    float g[g_len];
    for (i=0; i<g_len; i++)
        g[i] = h[g_len-i-1];

    // create synthesis/analysis filter objects
    firfilt_crcf fs = firfilt_crcf_create(h, h_len);
    firfilt_crcf fa = firfilt_crcf_create(g, g_len);

    // create synthesis/analysis filterbank channelizer objects
    firpfbch_crcf qs = firpfbch_crcf_create(LIQUID_SYNTHESIZER, num_channels, p, h);
    firpfbch_crcf qa = firpfbch_crcf_create(LIQUID_ANALYZER,    num_channels, p, g);

    float complex x[num_samples];                   // random input (noise)
    float complex Y0[num_symbols][num_channels];    // channelized output (filterbank)
    float complex Y1[num_symbols][num_channels];    // channelized output
    float complex z0[num_samples];                  // time-domain output (filterbank)
    float complex z1[num_samples];                  // time-domain output

    // generate input sequence (complex noise)
    for (i=0; i<num_samples; i++)
        x[i] = randnf() * cexpf(_Complex_I*randf()*2*M_PI);

    // 
    // ANALYZERS
    //
    
    // 
    // run analysis filter bank
    //
    for (i=0; i<num_symbols; i++)
        firpfbch_crcf_analyzer_execute(qa, &x[i*num_channels], &Y0[i][0]);


    // 
    // run traditional down-converter (inefficient)
    //
    float dphi; // carrier frequency
    unsigned int n=0;
    for (i=0; i<num_channels; i++) {

        // reset filter
        firfilt_crcf_clear(fa);

        // set center frequency
        dphi = 2.0f * M_PI * (float)i / (float)num_channels;

        // reset symbol counter
        n=0;

        for (j=0; j<num_samples; j++) {
            // push down-converted sample into filter
            firfilt_crcf_push(fa, x[j]*cexpf(-_Complex_I*j*dphi));

            // compute output at the appropriate sample time
            assert(n<num_symbols);
            if ( ((j+1)%num_channels)==0 ) {
                firfilt_crcf_execute(fa, &Y1[n][i]);
                n++;
            }
        }
        assert(n==num_symbols);

    }


    // 
    // SYNTHESIZERS
    //

    // 
    // run synthesis filter bank
    //
    for (i=0; i<num_symbols; i++)
        firpfbch_crcf_synthesizer_execute(qs, &Y0[i][0], &z0[i*num_channels]);

    // 
    // run traditional up-converter (inefficient)
    //

    // clear output array
    for (i=0; i<num_samples; i++)
        z1[i] = 0.0f;

    float complex y_hat;
    for (i=0; i<num_channels; i++) {
        // reset filter
        firfilt_crcf_clear(fs);

        // set center frequency
        dphi = 2.0f * M_PI * (float)i / (float)num_channels;

        // reset input symbol counter
        n=0;

        for (j=0; j<num_samples; j++) {

            // interpolate sequence
            if ( (j%num_channels)==0 ) {
                assert(n<num_symbols);
                firfilt_crcf_push(fs, Y1[n][i]);
                n++;
            } else {
                firfilt_crcf_push(fs, 0);
            }
            firfilt_crcf_execute(fs, &y_hat);

            // accumulate up-converted sample
            z1[j] += y_hat * cexpf(_Complex_I*j*dphi);
        }
        assert(n==num_symbols);
    }

    // destroy objects
    firfilt_crcf_destroy(fs);
    firfilt_crcf_destroy(fa);
    firpfbch_crcf_destroy(qs);
    firpfbch_crcf_destroy(qa);


    //
    // RESULTS
    //

    // 
    // analyzers
    //

    // print filterbank channelizer
    printf("\n");
    printf("filterbank channelizer:\n");
    for (i=0; i<num_symbols; i++) {
        printf("%3u: ", i);
        for (j=0; j<num_channels; j++) {
            printf("  %8.5f+j%8.5f, ", crealf(Y0[i][j]), cimagf(Y0[i][j]));
        }
        printf("\n");
    }

    // print traditional channelizer
    printf("\n");
    printf("traditional channelizer:\n");
    for (i=0; i<num_symbols; i++) {
        printf("%3u: ", i);
        for (j=0; j<num_channels; j++) {
            printf("  %8.5f+j%8.5f, ", crealf(Y1[i][j]), cimagf(Y1[i][j]));
        }
        printf("\n");
    }


    float mse_analyzer[num_channels];
    float complex d;
    for (i=0; i<num_channels; i++) {
        mse_analyzer[i] = 0.0f;
        for (j=0; j<num_symbols; j++) {
            d = Y0[j][i] - Y1[j][i];
            mse_analyzer[i] += crealf(d*conjf(d));
        }

        mse_analyzer[i] /= num_symbols;
    }
    printf("\n");
    printf("rmse: ");
    for (i=0; i<num_channels; i++)
        printf("%12.4e          ", sqrt(mse_analyzer[i]));
    printf("\n");


    // 
    // synthesizers
    //

    printf("\n");
    printf("output: filterbank:             traditional:\n");
    for (i=0; i<num_samples; i++) {
        printf("%3u: %10.5f+%10.5fj  %10.5f+%10.5fj\n",
            i,
            crealf(z0[i]), cimagf(z0[i]),
            crealf(z1[i]), cimagf(z1[i]));
    }

    float mse_synthesizer = 0.0f;
    for (i=0; i<num_samples; i++) {
        d = z0[i] - z1[i];
        mse_synthesizer += crealf(d*conjf(d));
    }
    mse_synthesizer /= num_samples;
    printf("\n");
    printf("rmse: %12.4e\n", sqrtf(mse_synthesizer));

    //
    // EXPORT DATA TO FILE
    //

    FILE * fid = fopen(OUTPUT_FILENAME,"w");
    fprintf(fid,"%% %s: auto-generated file\n\n", OUTPUT_FILENAME);
    fprintf(fid,"clear all;\n");
    fprintf(fid,"close all;\n");
    fprintf(fid,"num_channels=%u;\n", num_channels);
    fprintf(fid,"num_symbols=%u;\n", num_symbols);
    fprintf(fid,"num_samples = num_channels*num_symbols;\n");

    fprintf(fid,"x  = zeros(1,num_samples);\n");
    fprintf(fid,"y0 = zeros(num_symbols,num_channels);\n");
    fprintf(fid,"y1 = zeros(num_symbols,num_channels);\n");
    fprintf(fid,"z0 = zeros(1,num_samples);\n");
    fprintf(fid,"z1 = zeros(1,num_samples);\n");

    // input
    for (i=0; i<num_samples; i++)
        fprintf(fid,"x(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(x[i]), cimag(x[i]));

    // analysis
    for (i=0; i<num_symbols; i++) {
        for (j=0; j<num_channels; j++) {
            fprintf(fid,"y0(%4u,%4u) = %12.4e + j*%12.4e;\n", i+1, j+1, crealf(Y0[i][j]), cimag(Y0[i][j]));
            fprintf(fid,"y1(%4u,%4u) = %12.4e + j*%12.4e;\n", i+1, j+1, crealf(Y1[i][j]), cimag(Y1[i][j]));
        }
    }

    // synthesis
    for (i=0; i<num_samples; i++) {
        fprintf(fid,"z0(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(z0[i]), cimag(z0[i]));
        fprintf(fid,"z1(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(z1[i]), cimag(z1[i]));
    }
    fprintf(fid,"z0 = z0 / num_channels;\n");
    fprintf(fid,"z1 = z1 / num_channels;\n");

    // plot results
    fprintf(fid,"\n\n");
    fprintf(fid,"ts = 0:(num_symbols-1);\n");
    fprintf(fid,"for i=1:num_channels,\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"title(['channel ' num2str(i)]);\n");
    fprintf(fid,"subplot(2,1,1);\n");
    fprintf(fid,"    plot(ts,real(y0(:,i)),'-x', ts,real(y1(:,i)),'s');\n");
    //fprintf(fid,"    axis([0 (num_symbols-1) -2 2]);\n");
    fprintf(fid,"subplot(2,1,2);\n");
    fprintf(fid,"    plot(ts,imag(y0(:,i)),'-x', ts,imag(y1(:,i)),'s');\n");
    //fprintf(fid,"    axis([0 (num_symbols-1) -2 2]);\n");
    fprintf(fid,"end;\n");

    fprintf(fid,"t  = 0:(num_samples-1);\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"title('composite');\n");
    fprintf(fid,"subplot(2,1,1);\n");
    fprintf(fid,"    plot(t,real(z0),'-x', t,real(z1),'s');\n");
    fprintf(fid,"subplot(2,1,2);\n");
    fprintf(fid,"    plot(t,imag(z0),'-x', t,imag(z1),'s');\n");


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

    printf("done.\n");
    return 0;
}
Ejemplo n.º 4
0
int main() {
    // options
    unsigned int num_channels=8;    // number of channels
    unsigned int m=4;               // filter delay
    float As=60;                    // stop-band attenuation
    unsigned int num_frames=25;     // number of frames

    //
    unsigned int i;
    unsigned int k;

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

    // data arrays
    float complex x[num_samples];  // time-domain input  [size: num_samples  x 1         ]
    float complex y[num_samples];  // channelized output [size: num_channels x num_frames]

    // initialize input with zeros
    for (i=0; i<num_samples; i++)
        x[i] = 0.0f;

    // generate input signal(s)
    unsigned int num_signals = 4;
    float fc[4] = {0.0f,   0.25f,  0.375f, -0.375f}; // center frequencies
    float bw[4] = {0.035f, 0.035f, 0.035f,  0.035f}; // bandwidths
    unsigned int pulse_len = 137;
    float pulse[pulse_len];
    for (i=0; i<num_signals; i++) {
        // create pulse
        liquid_firdes_kaiser(pulse_len, bw[i], 50.0f, 0.0f, pulse);

        // add pulse to input signal with carrier offset
        for (k=0; k<pulse_len; k++)
            x[k] += pulse[k] * cexpf(_Complex_I*2*M_PI*fc[i]*k) * bw[i];
    }

    // create prototype filter
    unsigned int h_len = 2*num_channels*m + 1;
    float h[h_len];
    liquid_firdes_kaiser(h_len, 0.5f/(float)num_channels, As, 0.0f, h);

#if 0
    // create filterbank channelizer object using internal method for filter
    firpfbch_crcf q = firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, num_channels, m, As);
#else
    // create filterbank channelizer object using external filter coefficients
    firpfbch_crcf q = firpfbch_crcf_create(LIQUID_ANALYZER, num_channels, 2*m, h);
#endif

    // channelize input data
    for (i=0; i<num_frames; i++) {
        // execute analysis filter bank
        firpfbch_crcf_analyzer_execute(q, &x[i*num_channels], &y[i*num_channels]);
    }

    // destroy channelizer object
    firpfbch_crcf_destroy(q);
    
    // 
    // export results to file
    //
    FILE * fid = fopen(OUTPUT_FILENAME,"w");
    fprintf(fid,"%% %s: auto-generated file\n\n", OUTPUT_FILENAME);
    fprintf(fid,"clear all;\n");
    fprintf(fid,"close all;\n");
    fprintf(fid,"num_channels = %u;\n", num_channels);
    fprintf(fid,"m            = %u;\n", m);
    fprintf(fid,"num_frames   = %u;\n", num_frames);
    fprintf(fid,"h_len        = 2*num_channels*m+1;\n");
    fprintf(fid,"num_samples  = num_frames*num_channels;\n");

    fprintf(fid,"h = zeros(1,h_len);\n");
    fprintf(fid,"x = zeros(1,num_samples);\n");
    fprintf(fid,"y = zeros(num_channels, num_frames);\n");

    // save prototype filter
    for (i=0; i<h_len; i++)
        fprintf(fid,"  h(%6u) = %12.4e;\n", i+1, h[i]);

    // save input signal
    for (i=0; i<num_samples; i++)
        fprintf(fid,"  x(%6u) = %12.4e + 1i*%12.4e;\n", i+1, crealf(x[i]), cimagf(x[i]));

    // save channelized output signals
    for (i=0; i<num_frames; i++) {
        for (k=0; k<num_channels; k++) {
            float complex v = y[i*num_channels + k];
            fprintf(fid,"  y(%3u,%6u) = %12.4e + 1i*%12.4e;\n", k+1, i+1, crealf(v), cimagf(v));
        }
    }

    // plot results
    fprintf(fid,"\n");
    fprintf(fid,"nfft = 1024;\n"); // TODO: use nextpow2
    fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n");
    fprintf(fid,"H = 20*log10(abs(fftshift(fft(h/num_channels,nfft))));\n");
    fprintf(fid,"X = 20*log10(abs(fftshift(fft(x,nfft))));\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"subplot(2,1,1);\n");
    fprintf(fid,"  plot(f, H, 'Color', [0 0.5 0.25], 'LineWidth', 2);\n");
    fprintf(fid,"  axis([-0.5 0.5 -100 10]);\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  xlabel('Normalized Frequency [f/F_s]');\n");
    fprintf(fid,"  ylabel('Prototype Filter PSD');\n");
    fprintf(fid,"subplot(2,1,2);\n");
    fprintf(fid,"  plot(f, X, 'Color', [0 0.25 0.5], 'LineWidth', 2);\n");
    fprintf(fid,"  axis([-0.5 0.5 -100 0]);\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  xlabel('Normalized Frequency [f/F_s]');\n");
    fprintf(fid,"  ylabel('Input PSD');\n");

    // compute the PSD of each output and plot results on a square grid
    fprintf(fid,"n = ceil(sqrt(num_channels));\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"for i=1:num_channels,\n");
    fprintf(fid,"  Y = 20*log10(abs(fftshift(fft(y(i,:),nfft))));\n");
    fprintf(fid,"  subplot(n,n,i);\n");
    fprintf(fid,"  plot(f, Y, 'Color', [0.25 0 0.25], 'LineWidth', 1.5);\n");
    fprintf(fid,"  axis([-0.5 0.5 -120 20]);\n");
    fprintf(fid,"  grid on;\n");
    fprintf(fid,"  title(num2str(i-1));\n");
    fprintf(fid,"end;\n");

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

    printf("done.\n");
    return 0;
}
//
// AUTOTEST: validate analysis correctness
//
void autotest_firpfbch_crcf_analysis()
{
    float tol = 1e-4f;              // error tolerance
    unsigned int num_channels=4;    // number of channels
    unsigned int p=5;               // filter length (symbols)
    unsigned int num_symbols=12;    // number of symbols

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

    unsigned int i;
    unsigned int j;

    // generate filter
    // NOTE : these coefficients can be random; the purpose of this
    //        exercise is to demonstrate mathematical equivalence
    unsigned int h_len = p*num_channels;
    float h[h_len];
    for (i=0; i<h_len; i++) h[i] = randnf();

    // create filterbank object
    firpfbch_crcf q = firpfbch_crcf_create(LIQUID_ANALYZER, num_channels, p, h);

    // generate filter object
    firfilt_crcf f = firfilt_crcf_create(h, h_len);

    // allocate memory for arrays
    float complex y[num_samples];                   // time-domain input
    float complex Y0[num_symbols][num_channels];    // channelized output
    float complex Y1[num_symbols][num_channels];    // channelized output

    // generate input sequence (complex noise)
    for (i=0; i<num_samples; i++)
        y[i] = randnf() * cexpf(_Complex_I*randf()*2*M_PI);

    // 
    // run analysis filter bank
    //

    for (i=0; i<num_symbols; i++)
        firpfbch_crcf_analyzer_execute(q, &y[i*num_channels], &Y0[i][0]);


    // 
    // run traditional down-converter (inefficient)
    //

    float dphi; // carrier frequency
    unsigned int n=0;
    for (i=0; i<num_channels; i++) {

        // reset filter
        firfilt_crcf_clear(f);

        // set center frequency
        dphi = 2.0f * M_PI * (float)i / (float)num_channels;

        // reset symbol counter
        n=0;

        for (j=0; j<num_samples; j++) {
            // push down-converted sample into filter
            firfilt_crcf_push(f, y[j]*cexpf(-_Complex_I*j*dphi));

            // compute output at the appropriate sample time
            assert(n<num_symbols);
            if ( ((j+1)%num_channels)==0 ) {
                firfilt_crcf_execute(f, &Y1[n][i]);
                n++;
            }
        }
        assert(n==num_symbols);

    }

    // destroy objects
    firfilt_crcf_destroy(f);
    firpfbch_crcf_destroy(q);

    if (liquid_autotest_verbose) {
        // print filterbank channelizer
        printf("\n");
        printf("filterbank channelizer:\n");
        for (i=0; i<num_symbols; i++) {
            printf("%3u: ", i);
            for (j=0; j<num_channels; j++) {
                printf("  %8.5f+j%8.5f, ", crealf(Y0[i][j]), cimagf(Y0[i][j]));
            }
            printf("\n");
        }

        // print traditional channelizer
        printf("\n");
        printf("traditional channelizer:\n");
        for (i=0; i<num_symbols; i++) {
            printf("%3u: ", i);
            for (j=0; j<num_channels; j++) {
                printf("  %8.5f+j%8.5f, ", crealf(Y1[i][j]), cimagf(Y1[i][j]));
            }
            printf("\n");
        }
    }

    // compare results
    for (i=0; i<num_symbols; i++) {
        for (j=0; j<num_channels; j++) {
            CONTEND_DELTA( crealf(Y0[i][j]), crealf(Y1[i][j]), tol );
            CONTEND_DELTA( cimagf(Y0[i][j]), cimagf(Y1[i][j]), tol );
        }
    }

}