1 //
2 // resamp_crcf_example.c
3 //
4 // Demonstration of arbitrary resampler object whereby an input signal
5 // is resampled at an arbitrary rate.
6 //
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <complex.h>
11 #include <math.h>
12 #include <getopt.h>
13 
14 #include "liquid.h"
15 
16 #define OUTPUT_FILENAME "resamp_crcf_example.m"
17 
18 // print usage/help message
usage()19 void usage()
20 {
21     printf("Usage: %s [OPTION]\n", __FILE__);
22     printf("  -h            : print help\n");
23     printf("  -r <rate>     : resampling rate (output/input),    default: 1.1\n");
24     printf("  -m <delay>    : filter semi-length (delay),        default: 13\n");
25     printf("  -b <bandwidth>: filter bandwidth, 0 < b < 0.5,     default: 0.4\n");
26     printf("  -s <atten>    : filter stop-band attenuation [dB], default: 60\n");
27     printf("  -p <npfb>     : filter bank size,                  default: 64\n");
28     printf("  -n <num>      : number of input samples,           default: 400\n");
29     printf("  -f <frequency>: input signal frequency,            default: 0.044\n");
30 }
31 
main(int argc,char * argv[])32 int main(int argc, char*argv[])
33 {
34     // options
35     float        r    = 1.1f;   // resampling rate (output/input)
36     unsigned int m    = 13;     // resampling filter semi-length (filter delay)
37     float        As   = 60.0f;  // resampling filter stop-band attenuation [dB]
38     float        bw   = 0.45f;  // resampling filter bandwidth
39     unsigned int npfb = 64;     // number of filters in bank (timing resolution)
40     unsigned int n    = 400;    // number of input samples
41     float        fc   = 0.044f; // complex sinusoid frequency
42 
43     int dopt;
44     while ((dopt = getopt(argc,argv,"hr:m:b:s:p:n:f:")) != EOF) {
45         switch (dopt) {
46         case 'h':   usage();            return 0;
47         case 'r':   r    = atof(optarg); break;
48         case 'm':   m    = atoi(optarg); break;
49         case 'b':   bw   = atof(optarg); break;
50         case 's':   As   = atof(optarg); break;
51         case 'p':   npfb = atoi(optarg); break;
52         case 'n':   n    = atoi(optarg); break;
53         case 'f':   fc   = atof(optarg); break;
54         default:
55             exit(1);
56         }
57     }
58 
59     // validate input
60     if (r <= 0.0f) {
61         fprintf(stderr,"error: %s, resampling rate must be greater than zero\n", argv[0]);
62         exit(1);
63     } else if (m == 0) {
64         fprintf(stderr,"error: %s, filter semi-length must be greater than zero\n", argv[0]);
65         exit(1);
66     } else if (bw == 0.0f || bw >= 0.5f) {
67         fprintf(stderr,"error: %s, filter bandwidth must be in (0,0.5)\n", argv[0]);
68         exit(1);
69     } else if (As < 0.0f) {
70         fprintf(stderr,"error: %s, filter stop-band attenuation must be greater than zero\n", argv[0]);
71         exit(1);
72     } else if (npfb == 0) {
73         fprintf(stderr,"error: %s, filter bank size must be greater than zero\n", argv[0]);
74         exit(1);
75     } else if (n == 0) {
76         fprintf(stderr,"error: %s, number of input samples must be greater than zero\n", argv[0]);
77         exit(1);
78     }
79 
80     unsigned int i;
81 
82     // number of input samples (zero-padded)
83     unsigned int nx = n + m;
84 
85     // output buffer with extra padding for good measure
86     unsigned int y_len = (unsigned int) ceilf(1.1 * nx * r) + 4;
87 
88     // arrays
89     float complex x[nx];
90     float complex y[y_len];
91 
92     // create resampler
93     resamp_crcf q = resamp_crcf_create(r,m,bw,As,npfb);
94 
95     // generate input signal
96     float wsum = 0.0f;
97     for (i=0; i<nx; i++) {
98         // compute window
99         float w = i < n ? kaiser(i, n, 10.0f, 0.0f) : 0.0f;
100 
101         // apply window to complex sinusoid
102         x[i] = cexpf(_Complex_I*2*M_PI*fc*i) * w;
103 
104         // accumulate window
105         wsum += w;
106     }
107 
108     // resample
109     unsigned int ny=0;
110     // execute on block of samples
111     resamp_crcf_execute_block(q, x, nx, y, &ny);
112 
113     // clean up allocated objects
114     resamp_crcf_destroy(q);
115 
116     //
117     // analyze resulting signal
118     //
119 
120     // check that the actual resampling rate is close to the target
121     float r_actual = (float)ny / (float)nx;
122     float fy = fc / r;      // expected output frequency
123 
124     // run FFT and ensure that carrier has moved and that image
125     // frequencies and distortion have been adequately suppressed
126     unsigned int nfft = 1 << liquid_nextpow2(ny);
127     float complex yfft[nfft];   // fft input
128     float complex Yfft[nfft];   // fft output
129     for (i=0; i<nfft; i++)
130         yfft[i] = i < ny ? y[i] : 0.0f;
131     fft_run(nfft, yfft, Yfft, LIQUID_FFT_FORWARD, 0);
132     fft_shift(Yfft, nfft);  // run FFT shift
133 
134     // find peak frequency
135     float Ypeak = 0.0f;
136     float fpeak = 0.0f;
137     float max_sidelobe = -1e9f;     // maximum side-lobe [dB]
138     float main_lobe_width = 0.07f;  // TODO: figure this out from Kaiser's equations
139     for (i=0; i<nfft; i++) {
140         // normalized output frequency
141         float f = (float)i/(float)nfft - 0.5f;
142 
143         // scale FFT output appropriately
144         float Ymag = 20*log10f( cabsf(Yfft[i] / (r * wsum)) );
145 
146         // find frequency location of maximum magnitude
147         if (Ymag > Ypeak || i==0) {
148             Ypeak = Ymag;
149             fpeak = f;
150         }
151 
152         // find peak side-lobe value, ignoring frequencies
153         // within a certain range of signal frequency
154         if ( fabsf(f-fy) > main_lobe_width )
155             max_sidelobe = Ymag > max_sidelobe ? Ymag : max_sidelobe;
156     }
157 
158     // print results and check frequency location
159     printf("  desired resampling rate   :   %12.8f\n", r);
160     printf("  measured resampling rate  :   %12.8f    (%u/%u)\n", r_actual, ny, nx);
161     printf("  peak spectrum             :   %12.8f dB (expected 0.0 dB)\n", Ypeak);
162     printf("  peak frequency            :   %12.8f    (expected %-12.8f)\n", fpeak, fy);
163     printf("  max sidelobe              :   %12.8f dB (expected at least %.2f dB)\n", max_sidelobe, -As);
164 
165 
166     // export results
167     FILE * fid = fopen(OUTPUT_FILENAME,"w");
168     fprintf(fid,"%% %s: auto-generated file\n",OUTPUT_FILENAME);
169     fprintf(fid,"clear all;\n");
170     fprintf(fid,"close all;\n");
171     fprintf(fid,"m=%u;\n", m);
172     fprintf(fid,"npfb=%u;\n",  npfb);
173     fprintf(fid,"r=%12.8f;\n", r);
174     fprintf(fid,"n=%u;\n", n);
175 
176     fprintf(fid,"nx = %u;\n", nx);
177     fprintf(fid,"x = zeros(1,nx);\n");
178     for (i=0; i<nx; i++)
179         fprintf(fid,"x(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(x[i]), cimagf(x[i]));
180 
181     fprintf(fid,"ny = %u;\n", ny);
182     fprintf(fid,"y = zeros(1,ny);\n");
183     for (i=0; i<ny; i++)
184         fprintf(fid,"y(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i]));
185 
186     fprintf(fid,"\n\n");
187     fprintf(fid,"%% plot time-domain result\n");
188     fprintf(fid,"tx=[0:(length(x)-1)];\n");
189     fprintf(fid,"ty=[0:(length(y)-1)]/r-m;\n");
190     fprintf(fid,"figure('Color','white','position',[500 500 500 900]);\n");
191     fprintf(fid,"subplot(4,1,1);\n");
192     fprintf(fid,"  plot(tx,real(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n");
193     fprintf(fid,"       ty,real(y),'-s','Color',[0.5 0 0],    'MarkerSize',1);\n");
194     fprintf(fid,"  legend('original','resampled','location','northeast');");
195     fprintf(fid,"  xlabel('time');\n");
196     fprintf(fid,"  ylabel('real');\n");
197     fprintf(fid,"  axis([0 n -1.2 1.2]);\n");
198     fprintf(fid,"  grid on;\n");
199     fprintf(fid,"subplot(4,1,2);\n");
200     fprintf(fid,"  plot(tx,imag(x),'-s','Color',[0.5 0.5 0.5],'MarkerSize',1,...\n");
201     fprintf(fid,"       ty,imag(y),'-s','Color',[0 0.5 0],    'MarkerSize',1);\n");
202     fprintf(fid,"  legend('original','resampled','location','northeast');");
203     fprintf(fid,"  xlabel('time');\n");
204     fprintf(fid,"  ylabel('imag');\n");
205     fprintf(fid,"  axis([0 n -1.2 1.2]);\n");
206     fprintf(fid,"  grid on;\n");
207     fprintf(fid,"%% plot frequency-domain result\n");
208     fprintf(fid,"nfft=2^nextpow2(max(nx,ny));\n");
209     fprintf(fid,"%% estimate PSD, normalize by array length\n");
210     fprintf(fid,"X=20*log10(abs(fftshift(fft(x,nfft)/length(x))));\n");
211     fprintf(fid,"Y=20*log10(abs(fftshift(fft(y,nfft)/length(y))));\n");
212     fprintf(fid,"G=max(X);\n");
213     fprintf(fid,"X=X-G;\n");
214     fprintf(fid,"Y=Y-G;\n");
215     fprintf(fid,"f=[0:(nfft-1)]/nfft-0.5;\n");
216     fprintf(fid,"if r>1, fx = f/r; fy = f;   %% interpolated\n");
217     fprintf(fid,"else,   fx = f;   fy = f*r; %% decimated\n");
218     fprintf(fid,"end;\n");
219     fprintf(fid,"subplot(4,1,3:4);\n");
220     fprintf(fid,"  plot(fx,X,'Color',[0.5 0.5 0.5],fy,Y,'LineWidth',2);\n");
221     fprintf(fid,"  grid on;\n");
222     fprintf(fid,"  xlabel('normalized frequency');\n");
223     fprintf(fid,"  ylabel('PSD [dB]');\n");
224     fprintf(fid,"  legend('original','resampled','location','northeast');");
225     fprintf(fid,"  axis([-0.5 0.5 -120 20]);\n");
226 
227     fclose(fid);
228     printf("results written to %s\n",OUTPUT_FILENAME);
229 
230     printf("done.\n");
231     return 0;
232 }
233