1 //
2 // nco_pll_modem_example.c
3 //
4 // This example demonstrates how the nco/pll object (numerically-controlled
5 // oscillator with phase-locked loop) can be used for carrier frequency
6 // recovery in digital modems.  The modem type, SNR, and other parameters are
7 // specified via the command-line interface.
8 //
9 // SEE ALSO: nco_example.c
10 //           nco_pll_example.c
11 //
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <time.h>
16 #include <math.h>
17 #include <getopt.h>
18 
19 #include "liquid.h"
20 
21 #define OUTPUT_FILENAME "nco_pll_modem_example.m"
22 
23 // print usage/help message
usage()24 void usage()
25 {
26     printf("nco_pll_modem_example [options]\n");
27     printf("  u/h   : print usage\n");
28     printf("  s     : signal-to-noise ratio, default: 30dB\n");
29     printf("  b     : pll bandwidth, default: 20e-3\n");
30     printf("  n     : number of symbols, default: 256\n");
31     printf("  P     : phase offset (radians), default: pi/10 ~ 0.3146\n");
32     printf("  F     : frequency offset (radians), default: 0.001\n");
33     printf("  m     : modulation scheme, default: qpsk\n");
34     liquid_print_modulation_schemes();
35 }
36 
main(int argc,char * argv[])37 int main(int argc, char*argv[]) {
38     srand( time(NULL) );
39     // parameters
40     float phase_offset = M_PI/10;
41     float frequency_offset = 0.001f;
42     float SNRdB = 30.0f;
43     float pll_bandwidth = 0.02f;
44     modulation_scheme ms = LIQUID_MODEM_QPSK;
45     unsigned int n=256;     // number of iterations
46 
47     int dopt;
48     while ((dopt = getopt(argc,argv,"uhs:b:n:P:F:m:")) != EOF) {
49         switch (dopt) {
50         case 'u':
51         case 'h':   usage();    return 0;
52         case 's':   SNRdB = atof(optarg);           break;
53         case 'b':   pll_bandwidth = atof(optarg);   break;
54         case 'n':   n = atoi(optarg);               break;
55         case 'P':   phase_offset = atof(optarg);    break;
56         case 'F':   frequency_offset= atof(optarg); break;
57         case 'm':
58             ms = liquid_getopt_str2mod(optarg);
59             if (ms == LIQUID_MODEM_UNKNOWN) {
60                 fprintf(stderr,"error: %s, unknown/unsupported modulation scheme \"%s\"\n", argv[0], optarg);
61                 return 1;
62             }
63             break;
64         default:
65             exit(1);
66         }
67     }
68     unsigned int d=n/32;      // print every "d" lines
69 
70     FILE * fid = fopen(OUTPUT_FILENAME,"w");
71     fprintf(fid, "%% %s : auto-generated file\n", OUTPUT_FILENAME);
72     fprintf(fid, "clear all;\n");
73     fprintf(fid, "phi=zeros(1,%u);\n",n);
74     fprintf(fid, "r=zeros(1,%u);\n",n);
75 
76     // objects
77     nco_crcf nco_tx = nco_crcf_create(LIQUID_VCO);
78     nco_crcf nco_rx = nco_crcf_create(LIQUID_VCO);
79 
80     modem mod   = modem_create(ms);
81     modem demod = modem_create(ms);
82 
83     unsigned int bps = modem_get_bps(mod);
84 
85     // initialize objects
86     nco_crcf_set_phase(nco_tx, phase_offset);
87     nco_crcf_set_frequency(nco_tx, frequency_offset);
88     nco_crcf_pll_set_bandwidth(nco_rx, pll_bandwidth);
89 
90     float noise_power = powf(10.0f, -SNRdB/20.0f);
91 
92     // print parameters
93     printf("PLL example :\n");
94     printf("modem : %u-%s\n", 1<<bps, modulation_types[ms].name);
95     printf("frequency offset: %6.3f, phase offset: %6.3f, SNR: %6.2fdB, pll b/w: %6.3f\n",
96             frequency_offset, phase_offset, SNRdB, pll_bandwidth);
97 
98     // run loop
99     unsigned int i, M=1<<bps, sym_in, sym_out, num_errors=0;
100     float phase_error;
101     float complex x, r, v, noise;
102     for (i=0; i<n; i++) {
103         // generate random symbol
104         sym_in = rand() % M;
105 
106         // modulate
107         modem_modulate(mod, sym_in, &x);
108 
109         // channel
110         //r = nco_crcf_cexpf(nco_tx);
111         nco_crcf_mix_up(nco_tx, x, &r);
112 
113         // add complex white noise
114         crandnf(&noise);
115         r += noise * noise_power;
116 
117         //
118         //v = nco_crcf_cexpf(nco_rx);
119         nco_crcf_mix_down(nco_rx, r, &v);
120 
121         // demodulate
122         modem_demodulate(demod, v, &sym_out);
123         num_errors += count_bit_errors(sym_in, sym_out);
124 
125         // error estimation
126         //phase_error = cargf(r*conjf(v));
127         phase_error = modem_get_demodulator_phase_error(demod);
128 
129         // perfect error estimation
130         //phase_error = nco_tx->theta - nco_rx->theta;
131 
132         // print every line in a format that octave can read
133         fprintf(fid, "phi(%u) = %10.6E;\n", i+1, phase_error);
134         fprintf(fid, "r(%u) = %10.6E + j*%10.6E;\n",
135                 i+1, crealf(v), cimagf(v));
136 
137         if ((i+1)%d == 0 || i==n-1) {
138             printf("  %4u: e_hat : %6.3f, phase error : %6.3f, freq error : %6.3f\n",
139                     i+1,                                // iteration
140                     phase_error,                        // estimated phase error
141                     nco_crcf_get_phase(nco_tx) - nco_crcf_get_phase(nco_rx),// true phase error
142                     nco_crcf_get_frequency(nco_tx) - nco_crcf_get_frequency(nco_rx)// true frequency error
143                   );
144         }
145 
146         // update tx nco object
147         nco_crcf_step(nco_tx);
148 
149         // update pll
150         nco_crcf_pll_step(nco_rx, phase_error);
151 
152         // update rx nco object
153         nco_crcf_step(nco_rx);
154     }
155 
156     fprintf(fid, "figure;\n");
157     fprintf(fid, "plot(1:length(phi),phi,'LineWidth',2,'Color',[0 0.25 0.5]);\n");
158     fprintf(fid, "xlabel('Symbol Index');\n");
159     fprintf(fid, "ylabel('Phase Error [radians]');\n");
160     fprintf(fid, "grid on;\n");
161 
162     fprintf(fid, "t0 = round(0.25*length(r));\n");
163     fprintf(fid, "figure;\n");
164     fprintf(fid, "plot(r(1:t0),'x','Color',[0.6 0.6 0.6],r(t0:end),'x','Color',[0 0.25 0.5]);\n");
165     fprintf(fid, "grid on;\n");
166     fprintf(fid, "axis([-1.5 1.5 -1.5 1.5]);\n");
167     fprintf(fid, "axis('square');\n");
168     fprintf(fid, "xlabel('In-Phase');\n");
169     fprintf(fid, "ylabel('Quadrature');\n");
170     fprintf(fid, "legend(['first 25%%'],['last 75%%'],1);\n");
171     fclose(fid);
172 
173     printf("results written to %s.\n",OUTPUT_FILENAME);
174 
175     nco_crcf_destroy(nco_tx);
176     nco_crcf_destroy(nco_rx);
177 
178     modem_destroy(mod);
179     modem_destroy(demod);
180 
181     printf("bit errors: %u / %u\n", num_errors, bps*n);
182     printf("done.\n");
183     return 0;
184 }
185