1 //
2 // ofdmframesync_example.c
3 //
4 // Example demonstrating the base OFDM frame synchronizer with different
5 // parameters and options.
6 //
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <math.h>
11 #include <string.h>
12 #include <getopt.h>
13 #include <time.h>
14
15 #include "liquid.h"
16
17 #define OUTPUT_FILENAME "ofdmframesync_example.m"
18
usage()19 void usage()
20 {
21 printf("Usage: ofdmframesync_example [OPTION]\n");
22 printf(" h : print help\n");
23 printf(" M : number of subcarriers (must be even), default: 1200\n");
24 printf(" C : cyclic prefix length, default: 60\n");
25 printf(" T : taper length, default: 50\n");
26 printf(" s : signal-to-noise ratio [dB], default: 30\n");
27 }
28
29 // forward declaration of callback function; this will be invoked for every
30 // OFDM symbol received by the parent ofdmframesync object. The object will
31 // reset when something other than a zero is returned.
32 // _X : array of received subcarrier samples [size: _M x 1]
33 // _p : subcarrier allocation array [size: _M x 1]
34 // _M : number of subcarriers
35 // _userdata : user-defined data pointer
36 static int callback(float complex * _X,
37 unsigned char * _p,
38 unsigned int _M,
39 void * _userdata);
40
41 // custom data type to pass to callback function
42 struct rx_symbols {
43 float complex syms_bpsk[2000]; // even subcarrier symbols
44 float complex syms_qpsk[2000]; // odd subcarrier symbols
45 unsigned int num_bpsk; // counter
46 unsigned int num_qpsk; // counter
47 };
48
49 // main function
main(int argc,char * argv[])50 int main(int argc, char*argv[])
51 {
52 // set the random seed differently for each run
53 srand(time(NULL));
54
55 // options
56 unsigned int M = 1200; // number of subcarriers
57 unsigned int cp_len = 60; // cyclic prefix length
58 unsigned int taper_len = 50; // taper length
59 unsigned int num_symbols = 20; // number of data symbols
60 float noise_floor = -120.0f; // noise floor [dB]
61 float SNRdB = 30.0f; // signal-to-noise ratio [dB]
62
63 // get options
64 int dopt;
65 while((dopt = getopt(argc,argv,"hdM:C:T:s:")) != EOF){
66 switch (dopt) {
67 case 'h': usage(); return 0;
68 case 'M': M = atoi(optarg); break;
69 case 'C': cp_len = atoi(optarg); break;
70 case 'T': taper_len = atoi(optarg); break;
71 case 's': SNRdB = atof(optarg); break;
72 default:
73 exit(1);
74 }
75 }
76
77 unsigned int i;
78
79 // derived values
80 unsigned int frame_len = M + cp_len;
81 unsigned int num_samples = (3+num_symbols)*frame_len;
82 float nstd = powf(10.0f, noise_floor/20.0f);
83 float gamma = powf(10.0f, (SNRdB + noise_floor)/20.0f);
84
85 unsigned char p[M];
86 float complex X[M]; // channelized symbols
87 float complex y[num_samples]; // output time series
88
89 // initialize subcarrier allocation
90 ofdmframe_init_default_sctype(M, p);
91
92 // create subcarrier notch in upper half of band
93 unsigned int n0 = (unsigned int) (0.13 * M); // lower edge of notch
94 unsigned int n1 = (unsigned int) (0.21 * M); // upper edge of notch
95 for (i=n0; i<n1; i++)
96 p[i] = OFDMFRAME_SCTYPE_NULL;
97
98 // create struct for holding data symbols
99 struct rx_symbols data;
100 data.num_bpsk = 0;
101 data.num_qpsk = 0;
102
103 // create frame generator
104 ofdmframegen fg = ofdmframegen_create(M, cp_len, taper_len, p);
105 //ofdmframegen_print(fg);
106
107 // create frame synchronizer
108 ofdmframesync fs = ofdmframesync_create(M, cp_len, taper_len, p, callback, (void*)&data);
109 //ofdmframesync_print(fs);
110
111 unsigned int n=0;
112
113 // write first S0 symbol
114 ofdmframegen_write_S0a(fg, &y[n]);
115 n += frame_len;
116
117 // write second S0 symbol
118 ofdmframegen_write_S0b(fg, &y[n]);
119 n += frame_len;
120
121 // write S1 symbol
122 ofdmframegen_write_S1( fg, &y[n]);
123 n += frame_len;
124
125 // modulate data subcarriers
126 for (i=0; i<num_symbols; i++) {
127
128 // load different subcarriers with different data
129 unsigned int j;
130 for (j=0; j<M; j++) {
131 // ignore 'null' and 'pilot' subcarriers
132 if (p[j] != OFDMFRAME_SCTYPE_DATA)
133 continue;
134
135 // use BPSK for even frequencies, QPSK for odd
136 if ( (j % 2) == 0 ) {
137 // BPSK
138 X[j] = rand() % 2 ? -1.0f : 1.0f;
139 } else {
140 // QPSK
141 X[j] = (rand() % 2 ? -0.707f : 0.707f) +
142 (rand() % 2 ? -0.707f : 0.707f) * _Complex_I;
143 }
144 }
145
146 // generate OFDM symbol in the time domain
147 ofdmframegen_writesymbol(fg, X, &y[n]);
148 n += frame_len;
149 }
150
151 // add channel effects
152 for (i=0; i<num_samples; i++) {
153
154 // channel gain
155 y[i] *= gamma;
156
157 // add noise
158 y[i] += nstd*(randnf() + _Complex_I*randnf())*M_SQRT1_2;
159 }
160
161 // execute synchronizer on entire frame
162 ofdmframesync_execute(fs,y,num_samples);
163
164 // destroy objects
165 ofdmframegen_destroy(fg);
166 ofdmframesync_destroy(fs);
167
168 // estimate power spectral density of received signal
169 unsigned int nfft = 1024; // FFT size
170 float psd[nfft]; // PSD estimate output array
171 spgramcf_estimate_psd(nfft, y, num_samples, psd);
172
173 //
174 // export output file
175 //
176 FILE * fid = fopen(OUTPUT_FILENAME,"w");
177 fprintf(fid,"%% %s: auto-generated file\n\n", OUTPUT_FILENAME);
178 fprintf(fid,"clear all;\n");
179 fprintf(fid,"close all;\n");
180 fprintf(fid,"M = %u;\n", M);
181 fprintf(fid,"noise_floor = %f;\n", noise_floor);
182 fprintf(fid,"SNRdB = %f;\n", SNRdB);
183 fprintf(fid,"num_samples = %u;\n", num_samples);
184 fprintf(fid,"nfft = %u;\n", nfft);
185
186 // save received time-domain signal
187 fprintf(fid,"y = zeros(1,num_samples);\n");
188 for (i=0; i<num_samples; i++)
189 fprintf(fid,"y(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i]));
190
191 // save power spectral density estimate
192 fprintf(fid,"psd = zeros(1,nfft);\n");
193 for (i=0; i<nfft; i++)
194 fprintf(fid,"psd(%3u) = %12.4e;\n", i+1, psd[i]);
195 fprintf(fid,"psd = 10*log10( fftshift(psd) );\n");
196
197 // save received symbols
198 fprintf(fid,"num_bpsk = %u;\n", data.num_bpsk);
199 fprintf(fid,"num_qpsk = %u;\n", data.num_qpsk);
200 fprintf(fid,"syms_bpsk = zeros(1,num_bpsk);\n");
201 fprintf(fid,"syms_qpsk = zeros(1,num_qpsk);\n");
202 for (i=0; i<data.num_bpsk; i++)
203 fprintf(fid,"syms_bpsk(%3u) = %12.4e + 1i*%12.4e;\n", i+1, crealf(data.syms_bpsk[i]), cimagf(data.syms_bpsk[i]));
204 for (i=0; i<data.num_qpsk; i++)
205 fprintf(fid,"syms_qpsk(%3u) = %12.4e + 1i*%12.4e;\n", i+1, crealf(data.syms_qpsk[i]), cimagf(data.syms_qpsk[i]));
206
207 // plot power spectral density
208 fprintf(fid,"\n\n");
209 fprintf(fid,"f=[0:(nfft-1)]/nfft - 0.5;\n");
210 fprintf(fid,"figure;\n");
211 fprintf(fid,"plot(f,psd,'LineWidth',1.5,'Color',[0 0.3 0.5]);\n");
212 fprintf(fid,"psd_min = noise_floor - 10;\n");
213 fprintf(fid,"psd_max = noise_floor + 10 + max(SNRdB, 0);\n");
214 fprintf(fid,"axis([-0.5 0.5 psd_min psd_max]);\n");
215 fprintf(fid,"grid on;\n");
216 fprintf(fid,"xlabel('Normalized Frequency');\n");
217 fprintf(fid,"ylabel('Power Spectral Density [dB]');\n");
218
219 // plot received constellation
220 fprintf(fid,"\n\n");
221 fprintf(fid,"figure;\n");
222 fprintf(fid,"hold on;\n");
223 fprintf(fid,"plot(real(syms_bpsk),imag(syms_bpsk),'x','MarkerSize',4,'Color',[0 0.3 0.5]);\n");
224 fprintf(fid,"plot(real(syms_qpsk),imag(syms_qpsk),'x','MarkerSize',4,'Color',[0 0.5 0.3]);\n");
225 fprintf(fid,"hold off;\n");
226 fprintf(fid,"axis([-1 1 -1 1]*1.5);\n");
227 fprintf(fid,"axis square\n");
228 fprintf(fid,"grid on;\n");
229 fprintf(fid,"xlabel('I');\n");
230 fprintf(fid,"ylabel('Q');\n");
231 fprintf(fid,"legend('even subcarriers (BPSK)','odd subcarriers (QPSK)','location','northeast');\n");
232
233 fclose(fid);
234 printf("results written to %s\n", OUTPUT_FILENAME);
235
236 printf("done.\n");
237 return 0;
238 }
239
240 // callback function
241 // _X : array of received subcarrier samples [size: _M x 1]
242 // _p : subcarrier allocation array [size: _M x 1]
243 // _M : number of subcarriers
244 // _userdata : user-defined data pointer
callback(float complex * _X,unsigned char * _p,unsigned int _M,void * _userdata)245 static int callback(float complex * _X,
246 unsigned char * _p,
247 unsigned int _M,
248 void * _userdata)
249 {
250 // print status to the screen
251 printf("**** callback invoked\n");
252
253 // save received data subcarriers
254 struct rx_symbols * data = (struct rx_symbols*) _userdata;
255 unsigned int i;
256 for (i=0; i<_M; i++) {
257 // ignore 'null' and 'pilot' subcarriers
258 if (_p[i] != OFDMFRAME_SCTYPE_DATA)
259 continue;
260
261 // extract BPSK (even frequencies) and QPSK (odd frequencies) symbols
262 if ( (i % 2) == 0 && data->num_bpsk < 2000) {
263 // save at most 2000 BPSK symbols
264 data->syms_bpsk[data->num_bpsk] = _X[i];
265 data->num_bpsk++;
266 } else if ( (i % 2) == 1 && data->num_qpsk < 2000) {
267 // save at most 2000 QPSK symbols
268 data->syms_qpsk[data->num_qpsk] = _X[i];
269 data->num_qpsk++;
270 }
271 }
272
273 // return
274 return 0;
275 }
276
277