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