1 //
2 // eqlms_cccf_decisiondirected_example.c
3 //
4 // Tests least mean-squares (LMS) equalizer (EQ) on a signal with a known
5 // linear modulation scheme, but unknown data. The equalizer is updated
6 // using decision-directed demodulator output samples.
7 //
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <math.h>
13 #include <complex.h>
14 #include <getopt.h>
15 #include <time.h>
16 #include "liquid.h"
17
18 #define OUTPUT_FILENAME "eqlms_cccf_decisiondirected_example.m"
19
20 // print usage/help message
usage()21 void usage()
22 {
23 printf("Usage: eqlms_cccf_decisiondirected_example [OPTION]\n");
24 printf(" h : print help\n");
25 printf(" n : number of symbols, default: 500\n");
26 printf(" s : SNR [dB], default: 30\n");
27 printf(" c : number of channel filter taps (minimum: 1), default: 5\n");
28 printf(" k : samples/symbol, default: 2\n");
29 printf(" m : filter semi-length (symbols), default: 4\n");
30 printf(" b : filter excess bandwidth factor, default: 0.3\n");
31 printf(" p : equalizer semi-length (symbols), default: 3\n");
32 printf(" u : equalizer learning rate, default; 0.05\n");
33 printf(" M : modulation scheme (qpsk default)\n");
34 liquid_print_modulation_schemes();
35 }
36
main(int argc,char * argv[])37 int main(int argc, char*argv[])
38 {
39 srand(time(NULL));
40
41 // options
42 unsigned int num_symbols=500; // number of symbols to observe
43 float SNRdB = 30.0f; // signal-to-noise ratio [dB]
44 unsigned int hc_len=5; // channel filter length
45 unsigned int k=2; // matched filter samples/symbol
46 unsigned int m=3; // matched filter delay (symbols)
47 float beta=0.3f; // matched filter excess bandwidth factor
48 unsigned int p=3; // equalizer length (symbols, hp_len = 2*k*p+1)
49 float mu = 0.08f; // learning rate
50
51 // modulation type/depth
52 modulation_scheme ms = LIQUID_MODEM_QPSK;
53
54 int dopt;
55 while ((dopt = getopt(argc,argv,"hn:s:c:k:m:b:p:u:M:")) != EOF) {
56 switch (dopt) {
57 case 'h': usage(); return 0;
58 case 'n': num_symbols = atoi(optarg); break;
59 case 's': SNRdB = atof(optarg); break;
60 case 'c': hc_len = atoi(optarg); break;
61 case 'k': k = atoi(optarg); break;
62 case 'm': m = atoi(optarg); break;
63 case 'b': beta = atof(optarg); break;
64 case 'p': p = atoi(optarg); break;
65 case 'u': mu = atof(optarg); break;
66 case 'M':
67 ms = liquid_getopt_str2mod(optarg);
68 if (ms == LIQUID_MODEM_UNKNOWN) {
69 fprintf(stderr,"error: %s, unknown/unsupported modulation scheme '%s'\n", argv[0], optarg);
70 return 1;
71 }
72 break;
73 default:
74 exit(1);
75 }
76 }
77
78 // validate input
79 if (num_symbols == 0) {
80 fprintf(stderr,"error: %s, number of symbols must be greater than zero\n", argv[0]);
81 exit(1);
82 } else if (hc_len == 0) {
83 fprintf(stderr,"error: %s, channel must have at least 1 tap\n", argv[0]);
84 exit(1);
85 } else if (k < 2) {
86 fprintf(stderr,"error: %s, samples/symbol must be at least 2\n", argv[0]);
87 exit(1);
88 } else if (m == 0) {
89 fprintf(stderr,"error: %s, filter semi-length must be at least 1 symbol\n", argv[0]);
90 exit(1);
91 } else if (beta < 0.0f || beta > 1.0f) {
92 fprintf(stderr,"error: %s, filter excess bandwidth must be in [0,1]\n", argv[0]);
93 exit(1);
94 } else if (p == 0) {
95 fprintf(stderr,"error: %s, equalizer semi-length must be at least 1 symbol\n", argv[0]);
96 exit(1);
97 } else if (mu < 0.0f || mu > 1.0f) {
98 fprintf(stderr,"error: %s, equalizer learning rate must be in [0,1]\n", argv[0]);
99 exit(1);
100 }
101
102 // derived values
103 unsigned int hm_len = 2*k*m+1; // matched filter length
104 unsigned int hp_len = 2*k*p+1; // equalizer filter length
105 unsigned int num_samples = k*num_symbols;
106
107 // bookkeeping variables
108 float complex sym_tx[num_symbols]; // transmitted data sequence
109 float complex x[num_samples]; // interpolated time series
110 float complex y[num_samples]; // channel output
111 float complex z[num_samples]; // equalized output
112
113 float hm[hm_len]; // matched filter response
114 float complex hc[hc_len]; // channel filter coefficients
115 float complex hp[hp_len]; // equalizer filter coefficients
116
117 unsigned int i;
118
119 // generate matched filter response
120 liquid_firdes_prototype(LIQUID_FIRFILT_RRC, k, m, beta, 0.0f, hm);
121 firinterp_crcf interp = firinterp_crcf_create(k, hm, hm_len);
122
123 // create the modem objects
124 modem mod = modem_create(ms);
125 modem demod = modem_create(ms);
126 unsigned int M = 1 << modem_get_bps(mod);
127
128 // generate channel impulse response, filter
129 hc[0] = 1.0f;
130 for (i=1; i<hc_len; i++)
131 hc[i] = 0.09f*(randnf() + randnf()*_Complex_I);
132 firfilt_cccf fchannel = firfilt_cccf_create(hc, hc_len);
133
134 // generate random symbols
135 for (i=0; i<num_symbols; i++)
136 modem_modulate(mod, rand()%M, &sym_tx[i]);
137
138 // interpolate
139 for (i=0; i<num_symbols; i++)
140 firinterp_crcf_execute(interp, sym_tx[i], &x[i*k]);
141
142 // push through channel
143 float nstd = powf(10.0f, -SNRdB/20.0f);
144 for (i=0; i<num_samples; i++) {
145 firfilt_cccf_push(fchannel, x[i]);
146 firfilt_cccf_execute(fchannel, &y[i]);
147
148 // add noise
149 y[i] += nstd*(randnf() + randnf()*_Complex_I)*M_SQRT1_2;
150 }
151
152 // push through equalizer
153 // create equalizer, intialized with square-root Nyquist filter
154 eqlms_cccf eq = eqlms_cccf_create_rnyquist(LIQUID_FIRFILT_RRC, k, p, beta, 0.0f);
155 eqlms_cccf_set_bw(eq, mu);
156
157 // get initialized weights
158 eqlms_cccf_get_weights(eq, hp);
159
160 // filtered error vector magnitude (emperical RMS error)
161 float evm_hat = 0.03f;
162
163 float complex d_hat = 0.0f;
164 for (i=0; i<num_samples; i++) {
165 // print filtered evm (emperical rms error)
166 if ( ((i+1)%50)==0 )
167 printf("%4u : rms error = %12.8f dB\n", i+1, 10*log10(evm_hat));
168
169 eqlms_cccf_push(eq, y[i]);
170 eqlms_cccf_execute(eq, &d_hat);
171
172 // store output
173 z[i] = d_hat;
174
175 // decimate by k
176 if ( (i%k) != 0 ) continue;
177
178 // estimate transmitted signal
179 unsigned int sym_out; // output symbol
180 float complex d_prime; // estimated input sample
181 modem_demodulate(demod, d_hat, &sym_out);
182 modem_get_demodulator_sample(demod, &d_prime);
183
184 // update equalizer
185 eqlms_cccf_step(eq, d_prime, d_hat);
186
187 // update filtered evm estimate
188 float evm = crealf( (d_prime-d_hat)*conjf(d_prime-d_hat) );
189 evm_hat = 0.98f*evm_hat + 0.02f*evm;
190 }
191
192 // get equalizer weights
193 eqlms_cccf_get_weights(eq, hp);
194
195 // destroy objects
196 eqlms_cccf_destroy(eq);
197 firinterp_crcf_destroy(interp);
198 firfilt_cccf_destroy(fchannel);
199 modem_destroy(mod);
200 modem_destroy(demod);
201
202 //
203 // export output
204 //
205 FILE * fid = fopen(OUTPUT_FILENAME,"w");
206 fprintf(fid,"%% %s : auto-generated file\n\n", OUTPUT_FILENAME);
207 fprintf(fid,"clear all\n");
208 fprintf(fid,"close all\n");
209
210 fprintf(fid,"k = %u;\n", k);
211 fprintf(fid,"m = %u;\n", m);
212 fprintf(fid,"num_symbols = %u;\n", num_symbols);
213 fprintf(fid,"num_samples = num_symbols*k;\n");
214
215 // save transmit matched-filter response
216 fprintf(fid,"hm_len = 2*k*m+1;\n");
217 fprintf(fid,"hm = zeros(1,hm_len);\n");
218 for (i=0; i<hm_len; i++)
219 fprintf(fid,"hm(%4u) = %12.4e;\n", i+1, hm[i]);
220
221 // save channel impulse response
222 fprintf(fid,"hc_len = %u;\n", hc_len);
223 fprintf(fid,"hc = zeros(1,hc_len);\n");
224 for (i=0; i<hc_len; i++)
225 fprintf(fid,"hc(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(hc[i]), cimagf(hc[i]));
226
227 // save equalizer response
228 fprintf(fid,"hp_len = %u;\n", hp_len);
229 fprintf(fid,"hp = zeros(1,hp_len);\n");
230 for (i=0; i<hp_len; i++)
231 fprintf(fid,"hp(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(hp[i]), cimagf(hp[i]));
232
233 // save sample sets
234 fprintf(fid,"x = zeros(1,num_samples);\n");
235 fprintf(fid,"y = zeros(1,num_samples);\n");
236 fprintf(fid,"z = zeros(1,num_samples);\n");
237 for (i=0; i<num_samples; i++) {
238 fprintf(fid,"x(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(x[i]), cimagf(x[i]));
239 fprintf(fid,"y(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(y[i]), cimagf(y[i]));
240 fprintf(fid,"z(%4u) = %12.4e + j*%12.4e;\n", i+1, crealf(z[i]), cimagf(z[i]));
241 }
242
243 // plot time response
244 fprintf(fid,"t = 0:(num_samples-1);\n");
245 fprintf(fid,"tsym = 1:k:num_samples;\n");
246 fprintf(fid,"figure;\n");
247 fprintf(fid,"plot(t,real(z),...\n");
248 fprintf(fid," t(tsym),real(z(tsym)),'x');\n");
249
250 // plot constellation
251 fprintf(fid,"tsym0 = tsym(1:(length(tsym)/2));\n");
252 fprintf(fid,"tsym1 = tsym((length(tsym)/2):end);\n");
253 fprintf(fid,"figure;\n");
254 fprintf(fid,"plot(real(z(tsym0)),imag(z(tsym0)),'x','Color',[1 1 1]*0.7,...\n");
255 fprintf(fid," real(z(tsym1)),imag(z(tsym1)),'x','Color',[1 1 1]*0.0);\n");
256 fprintf(fid,"xlabel('In-Phase');\n");
257 fprintf(fid,"ylabel('Quadrature');\n");
258 fprintf(fid,"axis([-1 1 -1 1]*1.5);\n");
259 fprintf(fid,"axis square;\n");
260 fprintf(fid,"grid on;\n");
261
262 // compute composite response
263 fprintf(fid,"g = real(conv(conv(hm,hc),hp));\n");
264
265 // plot responses
266 fprintf(fid,"nfft = 1024;\n");
267 fprintf(fid,"f = [0:(nfft-1)]/nfft - 0.5;\n");
268 fprintf(fid,"Hm = 20*log10(abs(fftshift(fft(hm/k,nfft))));\n");
269 fprintf(fid,"Hc = 20*log10(abs(fftshift(fft(hc, nfft))));\n");
270 fprintf(fid,"Hp = 20*log10(abs(fftshift(fft(hp, nfft))));\n");
271 fprintf(fid,"G = 20*log10(abs(fftshift(fft(g/k, nfft))));\n");
272
273 fprintf(fid,"figure;\n");
274 fprintf(fid,"plot(f,Hm, f,Hc, f,Hp, f,G,'-k','LineWidth',2, [-0.5/k 0.5/k],[-6.026 -6.026],'or');\n");
275 fprintf(fid,"xlabel('Normalized Frequency');\n");
276 fprintf(fid,"ylabel('Power Spectral Density');\n");
277 fprintf(fid,"legend('transmit','channel','equalizer','composite','half-power points',1);\n");
278 fprintf(fid,"axis([-0.5 0.5 -12 8]);\n");
279 fprintf(fid,"grid on;\n");
280
281 fclose(fid);
282 printf("results written to '%s'\n", OUTPUT_FILENAME);
283
284 return 0;
285 }
286