File: ofdmframesync_example.c

package info (click to toggle)
liquid-dsp 1.7.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,216 kB
  • sloc: ansic: 115,859; sh: 3,513; makefile: 1,350; python: 274; asm: 11
file content (276 lines) | stat: -rw-r--r-- 9,450 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
//
// ofdmframesync_example.c
//
// Example demonstrating the base OFDM frame synchronizer with different
// parameters and options.
//

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <getopt.h>
#include <time.h>

#include "liquid.h"

#define OUTPUT_FILENAME "ofdmframesync_example.m"

void usage()
{
    printf("Usage: ofdmframesync_example [OPTION]\n");
    printf("  h     : print help\n");
    printf("  M     : number of subcarriers (must be even),  default: 1200\n");
    printf("  C     : cyclic prefix length,                  default: 60\n");
    printf("  T     : taper length,                          default: 50\n");
    printf("  s     : signal-to-noise ratio [dB],            default: 30\n");
}

// forward declaration of callback function; this will be invoked for every
// OFDM symbol received by the parent ofdmframesync object. The object will
// reset when something other than a zero is returned.
//  _X          : array of received subcarrier samples [size: _M x 1]
//  _p          : subcarrier allocation array [size: _M x 1]
//  _M          : number of subcarriers
//  _userdata   : user-defined data pointer
static int callback(float complex * _X,
                    unsigned char * _p,
                    unsigned int    _M,
                    void *          _userdata);

// custom data type to pass to callback function
struct rx_symbols {
    float complex syms_bpsk[2000];  // even subcarrier symbols
    float complex syms_qpsk[2000];  // odd subcarrier symbols
    unsigned int  num_bpsk;         // counter
    unsigned int  num_qpsk;         // counter
};

// main function
int main(int argc, char*argv[])
{
    // set the random seed differently for each run
    srand(time(NULL));

    // options
    unsigned int M           = 1200;    // number of subcarriers
    unsigned int cp_len      = 60;      // cyclic prefix length
    unsigned int taper_len   = 50;      // taper length
    unsigned int num_symbols = 20;      // number of data symbols
    float noise_floor        = -120.0f; // noise floor [dB]
    float SNRdB              = 30.0f;   // signal-to-noise ratio [dB]

    // get options
    int dopt;
    while((dopt = getopt(argc,argv,"hdM:C:T:s:")) != EOF){
        switch (dopt) {
        case 'h': usage();                      return 0;
        case 'M': M         = atoi(optarg);     break;
        case 'C': cp_len    = atoi(optarg);     break;
        case 'T': taper_len = atoi(optarg);     break;
        case 's': SNRdB     = atof(optarg);     break;
        default:
            exit(1);
        }
    }

    unsigned int i;

    // derived values
    unsigned int frame_len   = M + cp_len;
    unsigned int num_samples = (3+num_symbols)*frame_len;
    float        nstd        = powf(10.0f, noise_floor/20.0f);
    float        gamma       = powf(10.0f, (SNRdB + noise_floor)/20.0f);

    unsigned char p[M];
    float complex X[M];             // channelized symbols
    float complex y[num_samples];   // output time series

    // initialize subcarrier allocation
    ofdmframe_init_default_sctype(M, p);

    // create subcarrier notch in upper half of band
    unsigned int n0 = (unsigned int) (0.13 * M);    // lower edge of notch
    unsigned int n1 = (unsigned int) (0.21 * M);    // upper edge of notch
    for (i=n0; i<n1; i++)
        p[i] = OFDMFRAME_SCTYPE_NULL;

    // create struct for holding data symbols
    struct rx_symbols data;
    data.num_bpsk = 0;
    data.num_qpsk = 0;

    // create frame generator
    ofdmframegen fg = ofdmframegen_create(M, cp_len, taper_len, p);
    ofdmframegen_print(fg);

    // create frame synchronizer
    ofdmframesync fs = ofdmframesync_create(M, cp_len, taper_len, p, callback, (void*)&data);
    ofdmframesync_print(fs);

    unsigned int n=0;

    // write first S0 symbol
    ofdmframegen_write_S0a(fg, &y[n]);
    n += frame_len;

    // write second S0 symbol
    ofdmframegen_write_S0b(fg, &y[n]);
    n += frame_len;

    // write S1 symbol
    ofdmframegen_write_S1( fg, &y[n]);
    n += frame_len;

    // modulate data subcarriers
    for (i=0; i<num_symbols; i++) {

        // load different subcarriers with different data
        unsigned int j;
        for (j=0; j<M; j++) {
            // ignore 'null' and 'pilot' subcarriers
            if (p[j] != OFDMFRAME_SCTYPE_DATA)
                continue;

            // use BPSK for even frequencies, QPSK for odd
            if ( (j % 2) == 0 ) {
                // BPSK
                X[j] = rand() % 2 ? -1.0f : 1.0f;
            } else {
                // QPSK
                X[j] = (rand() % 2 ? -0.707f : 0.707f) +
                       (rand() % 2 ? -0.707f : 0.707f) * _Complex_I;
            }
        }

        // generate OFDM symbol in the time domain
        ofdmframegen_writesymbol(fg, X, &y[n]);
        n += frame_len;
    }

    // add channel effects
    for (i=0; i<num_samples; i++) {

        // channel gain
        y[i] *= gamma;

        // add noise
        y[i] += nstd*(randnf() + _Complex_I*randnf())*M_SQRT1_2;
    }

    // execute synchronizer on entire frame
    ofdmframesync_execute(fs,y,num_samples);

    // destroy objects
    ofdmframegen_destroy(fg);
    ofdmframesync_destroy(fs);

    // estimate power spectral density of received signal
    unsigned int nfft = 1024;   // FFT size
    float        psd[nfft];     // PSD estimate output array
    spgramcf_estimate_psd(nfft, y, num_samples, psd);

    // 
    // export output 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,"M           = %u;\n", M);
    fprintf(fid,"noise_floor = %f;\n", noise_floor);
    fprintf(fid,"SNRdB       = %f;\n", SNRdB);
    fprintf(fid,"num_samples = %u;\n", num_samples);
    fprintf(fid,"nfft        = %u;\n", nfft);

    // save received time-domain signal
    fprintf(fid,"y = zeros(1,num_samples);\n");
    for (i=0; i<num_samples; i++)
        fprintf(fid,"y(%3u) = %12.4e + j*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i]));

    // save power spectral density estimate
    fprintf(fid,"psd = zeros(1,nfft);\n");
    for (i=0; i<nfft; i++)
        fprintf(fid,"psd(%3u) = %12.4e;\n", i+1, psd[i]);
    fprintf(fid,"psd = 10*log10( fftshift(psd) );\n");

    // save received symbols
    fprintf(fid,"num_bpsk = %u;\n", data.num_bpsk);
    fprintf(fid,"num_qpsk = %u;\n", data.num_qpsk);
    fprintf(fid,"syms_bpsk = zeros(1,num_bpsk);\n");
    fprintf(fid,"syms_qpsk = zeros(1,num_qpsk);\n");
    for (i=0; i<data.num_bpsk; i++)
        fprintf(fid,"syms_bpsk(%3u) = %12.4e + 1i*%12.4e;\n", i+1, crealf(data.syms_bpsk[i]), cimagf(data.syms_bpsk[i]));
    for (i=0; i<data.num_qpsk; i++)
        fprintf(fid,"syms_qpsk(%3u) = %12.4e + 1i*%12.4e;\n", i+1, crealf(data.syms_qpsk[i]), cimagf(data.syms_qpsk[i]));

    // plot power spectral density
    fprintf(fid,"\n\n");
    fprintf(fid,"f=[0:(nfft-1)]/nfft - 0.5;\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"plot(f,psd,'LineWidth',1.5,'Color',[0 0.3 0.5]);\n");
    fprintf(fid,"psd_min = noise_floor - 10;\n");
    fprintf(fid,"psd_max = noise_floor + 10 + max(SNRdB, 0);\n");
    fprintf(fid,"axis([-0.5 0.5 psd_min psd_max]);\n");
    fprintf(fid,"grid on;\n");
    fprintf(fid,"xlabel('Normalized Frequency');\n");
    fprintf(fid,"ylabel('Power Spectral Density [dB]');\n");

    // plot received constellation
    fprintf(fid,"\n\n");
    fprintf(fid,"figure;\n");
    fprintf(fid,"hold on;\n");
    fprintf(fid,"plot(real(syms_bpsk),imag(syms_bpsk),'x','MarkerSize',4,'Color',[0 0.3 0.5]);\n");
    fprintf(fid,"plot(real(syms_qpsk),imag(syms_qpsk),'x','MarkerSize',4,'Color',[0 0.5 0.3]);\n");
    fprintf(fid,"hold off;\n");
    fprintf(fid,"axis([-1 1 -1 1]*1.5);\n");
    fprintf(fid,"axis square\n");
    fprintf(fid,"grid on;\n");
    fprintf(fid,"xlabel('I');\n");
    fprintf(fid,"ylabel('Q');\n");
    fprintf(fid,"legend('even subcarriers (BPSK)','odd subcarriers (QPSK)','location','northeast');\n");

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

    printf("done.\n");
    return 0;
}

// callback function
//  _X          : array of received subcarrier samples [size: _M x 1]
//  _p          : subcarrier allocation array [size: _M x 1]
//  _M          : number of subcarriers
//  _userdata   : user-defined data pointer
static int callback(float complex * _X,
                    unsigned char * _p,
                    unsigned int    _M,
                    void *          _userdata)
{
    // print status to the screen
    printf("**** callback invoked\n");

    // save received data subcarriers
    struct rx_symbols * data = (struct rx_symbols*) _userdata;
    unsigned int i;
    for (i=0; i<_M; i++) {
        // ignore 'null' and 'pilot' subcarriers
        if (_p[i] != OFDMFRAME_SCTYPE_DATA)
            continue;

        // extract BPSK (even frequencies) and QPSK (odd frequencies) symbols
        if ( (i % 2) == 0 && data->num_bpsk < 2000) {
            // save at most 2000 BPSK symbols
            data->syms_bpsk[data->num_bpsk] = _X[i];
            data->num_bpsk++;
        } else if ( (i % 2) == 1 && data->num_qpsk < 2000) {
            // save at most 2000 QPSK symbols
            data->syms_qpsk[data->num_qpsk] = _X[i];
            data->num_qpsk++;
        }
    }

    // return
    return 0;
}