1 //
2 // symsync_crcf_full_example.c
3 //
4 // This example extends that of `symsync_crcf_example.c` by including options
5 // for simulating a timing rate offset in addition to just a timing phase
6 // error. The resulting output file shows not just the constellation but the
7 // time domain sequence as well as the timing phase estimate over time.
8 //
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
14 #include <getopt.h>
15 #include <time.h>
16 #include <assert.h>
17
18 #include "liquid.h"
19
20 #define OUTPUT_FILENAME "symsync_crcf_full_example.m"
21
22 // print usage/help message
usage()23 void usage()
24 {
25 printf("symsync_crcf_full_example [options]\n");
26 printf(" h : print usage\n");
27 printf(" T : filter type: [rrcos], rkaiser, arkaiser, hM3, gmsk\n");
28 printf(" k : filter samples/symbol, default: 2\n");
29 printf(" K : output samples/symbol, default: 2\n");
30 printf(" m : filter delay (symbols), default: 5\n");
31 printf(" b : filter excess bandwidth, default: 0.5\n");
32 printf(" B : filter polyphase banks, default: 32\n");
33 printf(" s : signal-to-noise ratio, default: 30dB\n");
34 printf(" w : timing pll bandwidth, default: 0.02\n");
35 printf(" n : number of symbols, default: 400\n");
36 printf(" t : timing phase offset [%% symbol], t in [-0.5,0.5], default: -0.2\n");
37 printf(" r : timing freq. offset [%% symbol], default: 1.000\n");
38 }
39
40
main(int argc,char * argv[])41 int main(int argc, char*argv[]) {
42 srand(time(NULL));
43
44 // options
45 unsigned int k = 2; // samples/symbol (input)
46 unsigned int k_out = 2; // samples/symbol (output)
47 unsigned int m = 5; // filter delay (symbols)
48 float beta = 0.5f; // filter excess bandwidth factor
49 unsigned int num_filters = 32; // number of filters in the bank
50 unsigned int num_symbols = 400; // number of data symbols
51 float SNRdB = 30.0f; // signal-to-noise ratio
52
53 // transmit/receive filter types
54 liquid_firfilt_type ftype_tx = LIQUID_FIRFILT_RRC;
55 liquid_firfilt_type ftype_rx = LIQUID_FIRFILT_RRC;
56
57 float bt = 0.02f; // loop filter bandwidth
58 float tau = -0.2f; // fractional symbol offset
59 float r = 1.00f; // resampled rate
60
61 // use random data or 101010 phasing pattern
62 int random_data=1;
63
64 int dopt;
65 while ((dopt = getopt(argc,argv,"hT:k:K:m:b:B:s:w:n:t:r:")) != EOF) {
66 switch (dopt) {
67 case 'h': usage(); return 0;
68 case 'T':
69 if (strcmp(optarg,"gmsk")==0) {
70 ftype_tx = LIQUID_FIRFILT_GMSKTX;
71 ftype_rx = LIQUID_FIRFILT_GMSKRX;
72 } else {
73 ftype_tx = liquid_getopt_str2firfilt(optarg);
74 ftype_rx = liquid_getopt_str2firfilt(optarg);
75 }
76 if (ftype_tx == LIQUID_FIRFILT_UNKNOWN) {
77 fprintf(stderr,"error: %s, unknown filter type '%s'\n", argv[0], optarg);
78 exit(1);
79 }
80 break;
81 case 'k': k = atoi(optarg); break;
82 case 'K': k_out = atoi(optarg); break;
83 case 'm': m = atoi(optarg); break;
84 case 'b': beta = atof(optarg); break;
85 case 'B': num_filters = atoi(optarg); break;
86 case 's': SNRdB = atof(optarg); break;
87 case 'w': bt = atof(optarg); break;
88 case 'n': num_symbols = atoi(optarg); break;
89 case 't': tau = atof(optarg); break;
90 case 'r': r = atof(optarg); break;
91 default:
92 exit(1);
93 }
94 }
95
96 // validate input
97 if (k < 2) {
98 fprintf(stderr,"error: k (samples/symbol) must be at least 2\n");
99 exit(1);
100 } else if (m < 1) {
101 fprintf(stderr,"error: m (filter delay) must be greater than 0\n");
102 exit(1);
103 } else if (beta <= 0.0f || beta > 1.0f) {
104 fprintf(stderr,"error: beta (excess bandwidth factor) must be in (0,1]\n");
105 exit(1);
106 } else if (num_filters == 0) {
107 fprintf(stderr,"error: number of polyphase filters must be greater than 0\n");
108 exit(1);
109 } else if (bt <= 0.0f) {
110 fprintf(stderr,"error: timing PLL bandwidth must be greater than 0\n");
111 exit(1);
112 } else if (num_symbols == 0) {
113 fprintf(stderr,"error: number of symbols must be greater than 0\n");
114 exit(1);
115 } else if (tau < -1.0f || tau > 1.0f) {
116 fprintf(stderr,"error: timing phase offset must be in [-1,1]\n");
117 exit(1);
118 } else if (r < 0.5f || r > 2.0f) {
119 fprintf(stderr,"error: timing frequency offset must be in [0.5,2]\n");
120 exit(1);
121 }
122
123 // compute delay
124 while (tau < 0) tau += 1.0f; // ensure positive tau
125 float g = k*tau; // number of samples offset
126 int ds=floorf(g); // additional symbol delay
127 float dt = (g - (float)ds); // fractional sample offset
128 if (dt > 0.5f) { // force dt to be in [0.5,0.5]
129 dt -= 1.0f;
130 ds++;
131 }
132
133 unsigned int i, n=0;
134
135 unsigned int num_samples = k*num_symbols;
136 unsigned int num_samples_resamp = (unsigned int) ceilf(num_samples*r*1.1f) + 4;
137 float complex s[num_symbols]; // data symbols
138 float complex x[num_samples]; // interpolated samples
139 float complex y[num_samples_resamp]; // resampled data (resamp_crcf)
140 float complex z[k_out*num_symbols + 64];// synchronized samples
141 float complex sym_out[num_symbols + 64];// synchronized symbols
142
143 for (i=0; i<num_symbols; i++) {
144 if (random_data) {
145 // random signal (QPSK)
146 s[i] = cexpf(_Complex_I*0.5f*M_PI*((rand() % 4) + 0.5f));
147 } else {
148 s[i] = (i%2) ? 1.0f : -1.0f; // 101010 phasing pattern
149 }
150 }
151
152 //
153 // create and run interpolator
154 //
155
156 // design interpolating filter
157 unsigned int h_len = 2*k*m+1;
158 float h[h_len];
159 liquid_firdes_prototype(ftype_tx,k,m,beta,dt,h);
160 firinterp_crcf q = firinterp_crcf_create(k,h,h_len);
161 for (i=0; i<num_symbols; i++) {
162 firinterp_crcf_execute(q, s[i], &x[n]);
163 n+=k;
164 }
165 assert(n == num_samples);
166 firinterp_crcf_destroy(q);
167
168 //
169 // run resampler
170 //
171 unsigned int resamp_len = 10*k; // resampling filter semi-length (filter delay)
172 float resamp_bw = 0.45f; // resampling filter bandwidth
173 float resamp_As = 60.0f; // resampling filter stop-band attenuation
174 unsigned int resamp_npfb = 64; // number of filters in bank
175 resamp_crcf f = resamp_crcf_create(r, resamp_len, resamp_bw, resamp_As, resamp_npfb);
176 unsigned int num_samples_resampled = 0;
177 unsigned int num_written;
178 for (i=0; i<num_samples; i++) {
179 #if 0
180 // bypass arbitrary resampler
181 y[i] = x[i];
182 num_samples_resampled = num_samples;
183 #else
184 // TODO : compensate for resampler filter delay
185 resamp_crcf_execute(f, x[i], &y[num_samples_resampled], &num_written);
186 num_samples_resampled += num_written;
187 #endif
188 }
189 resamp_crcf_destroy(f);
190
191 //
192 // add noise
193 //
194 float nstd = powf(10.0f, -SNRdB/20.0f);
195 for (i=0; i<num_samples_resampled; i++)
196 y[i] += nstd*(randnf() + _Complex_I*randnf());
197
198
199 //
200 // create and run symbol synchronizer
201 //
202
203 symsync_crcf d = symsync_crcf_create_rnyquist(ftype_rx, k, m, beta, num_filters);
204 symsync_crcf_set_lf_bw(d,bt);
205 symsync_crcf_set_output_rate(d,k_out);
206
207 unsigned int num_samples_sync=0;
208 unsigned int nn;
209 unsigned int num_symbols_sync = 0;
210 float tau_hat[num_samples];
211 for (i=ds; i<num_samples_resampled; i++) {
212 tau_hat[num_samples_sync] = symsync_crcf_get_tau(d);
213 symsync_crcf_execute(d, &y[i], 1, &z[num_samples_sync], &nn);
214
215 // decimate
216 unsigned int j;
217 for (j=0; j<nn; j++) {
218 if ( (num_samples_sync%k_out)==0 )
219 sym_out[num_symbols_sync++] = z[num_samples_sync];
220 num_samples_sync++;
221 }
222 }
223 symsync_crcf_destroy(d);
224
225 // print last several symbols to screen
226 printf("output symbols:\n");
227 printf(" ...\n");
228 for (i=num_symbols_sync-10; i<num_symbols_sync; i++)
229 printf(" sym_out(%2u) = %8.4f + j*%8.4f;\n", i+1, crealf(sym_out[i]), cimagf(sym_out[i]));
230
231 //
232 // export output file
233 //
234
235 FILE* fid = fopen(OUTPUT_FILENAME,"w");
236 fprintf(fid,"%% %s, auto-generated file\n\n", OUTPUT_FILENAME);
237 fprintf(fid,"close all;\nclear all;\n\n");
238
239 fprintf(fid,"k=%u;\n",k);
240 fprintf(fid,"m=%u;\n",m);
241 fprintf(fid,"beta=%12.8f;\n",beta);
242 fprintf(fid,"k_out=%u;\n",k_out);
243 fprintf(fid,"num_filters=%u;\n",num_filters);
244 fprintf(fid,"num_symbols=%u;\n",num_symbols);
245
246 for (i=0; i<h_len; i++)
247 fprintf(fid,"h(%3u) = %12.5f;\n", i+1, h[i]);
248
249 for (i=0; i<num_symbols; i++)
250 fprintf(fid,"s(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(s[i]), cimagf(s[i]));
251
252 for (i=0; i<num_samples; i++)
253 fprintf(fid,"x(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(x[i]), cimagf(x[i]));
254
255 for (i=0; i<num_samples_resampled; i++)
256 fprintf(fid,"y(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(y[i]), cimagf(y[i]));
257
258 for (i=0; i<num_samples_sync; i++)
259 fprintf(fid,"z(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(z[i]), cimagf(z[i]));
260
261 for (i=0; i<num_symbols_sync; i++)
262 fprintf(fid,"sym_out(%3u) = %12.8f + j*%12.8f;\n", i+1, crealf(sym_out[i]), cimagf(sym_out[i]));
263
264 for (i=0; i<num_samples_sync; i++)
265 fprintf(fid,"tau_hat(%3u) = %12.8f;\n", i+1, tau_hat[i]);
266
267
268 fprintf(fid,"\n\n");
269 fprintf(fid,"%% scale QPSK in-phase by sqrt(2)\n");
270 fprintf(fid,"z = z*sqrt(2);\n");
271 fprintf(fid,"\n\n");
272 fprintf(fid,"tz = [0:length(z)-1]/k_out;\n");
273 fprintf(fid,"iz = 1:k_out:length(z);\n");
274 fprintf(fid,"figure;\n");
275 fprintf(fid,"plot(tz, real(z), '-',...\n");
276 fprintf(fid," tz(iz), real(z(iz)),'or');\n");
277 fprintf(fid,"xlabel('Time');\n");
278 fprintf(fid,"ylabel('Output Signal (real)');\n");
279 fprintf(fid,"grid on;\n");
280 fprintf(fid,"legend('output time series','optimum timing','location','northeast');\n");
281
282 fprintf(fid,"iz0 = iz( 1:round(length(iz)*0.5) );\n");
283 fprintf(fid,"iz1 = iz( round(length(iz)*0.5):length(iz) );\n");
284 fprintf(fid,"figure;\n");
285 fprintf(fid,"hold on;\n");
286 fprintf(fid,"plot(real(z(iz0)),imag(z(iz0)),'x','MarkerSize',4,'Color',[0.6 0.6 0.6]);\n");
287 fprintf(fid,"plot(real(z(iz1)),imag(z(iz1)),'o','MarkerSize',4,'Color',[0 0.25 0.5]);\n");
288 fprintf(fid,"hold off;\n");
289 fprintf(fid,"axis square;\n");
290 fprintf(fid,"grid on;\n");
291 fprintf(fid,"axis([-1 1 -1 1]*2.0);\n");
292 fprintf(fid,"xlabel('In-phase');\n");
293 fprintf(fid,"ylabel('Quadrature');\n");
294 fprintf(fid,"legend(['first 50%%'],['last 50%%'],'location','northeast');\n");
295
296 fprintf(fid,"figure;\n");
297 fprintf(fid,"tt = 0:(length(tau_hat)-1);\n");
298 fprintf(fid,"b = floor(num_filters*tau_hat + 0.5);\n");
299 fprintf(fid,"stairs(tt,tau_hat*num_filters);\n");
300 fprintf(fid,"hold on;\n");
301 fprintf(fid,"plot(tt,b,'-k','Color',[0 0 0]);\n");
302 fprintf(fid,"hold off;\n");
303 fprintf(fid,"xlabel('time');\n");
304 fprintf(fid,"ylabel('filterbank index');\n");
305 fprintf(fid,"grid on;\n");
306 fprintf(fid,"axis([0 length(tau_hat) -1 num_filters]);\n");
307
308 fclose(fid);
309 printf("results written to %s.\n", OUTPUT_FILENAME);
310
311 // clean it up
312 printf("done.\n");
313 return 0;
314 }
315