1 /*
2 * Copyright (c) 2007 - 2015 Joseph Gaeddert
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23 //
24 // modem_arb.c
25 //
26
27 // create arbitrary digital modem object
MODEM(_create_arbitrary)28 MODEM() MODEM(_create_arbitrary)(TC * _table,
29 unsigned int _M)
30 {
31 // strip out bits/symbol
32 unsigned int m = liquid_nextpow2(_M);
33 if ( (1<<m) != _M ) {
34 // TODO : eventually support non radix-2 constellation sizes
35 fprintf(stderr,"error: modem_create_arbitrary(), input constellation size must be power of 2\n");
36 exit(1);
37 }
38
39 // create arbitrary modem object, not initialized
40 MODEM() q = MODEM(_create_arb)(m);
41
42 // initialize object from table
43 MODEM(_arb_init)(q, _table, _M);
44
45 // return object
46 return q;
47 }
48
49
50 // create an arbitrary modem object
MODEM(_create_arb)51 MODEM() MODEM(_create_arb)(unsigned int _bits_per_symbol)
52 {
53 MODEM() q = (MODEM()) malloc( sizeof(struct MODEM(_s)) );
54 q->scheme = LIQUID_MODEM_ARB;
55
56 MODEM(_init)(q, _bits_per_symbol);
57
58 q->M = q->M;
59 q->symbol_map = (TC*) calloc( q->M, sizeof(TC) );
60
61 q->modulate_func = &MODEM(_modulate_arb);
62 q->demodulate_func = &MODEM(_demodulate_arb);
63
64 return q;
65 }
66
67 // modulate arbitrary modem type
MODEM(_modulate_arb)68 void MODEM(_modulate_arb)(MODEM() _q,
69 unsigned int _sym_in,
70 TC * _y)
71 {
72 if (_sym_in >= _q->M) {
73 fprintf(stderr,"error: modulate_arb(), input symbol exceeds maximum\n");
74 exit(1);
75 }
76
77 // map sample directly to output
78 *_y = _q->symbol_map[_sym_in];
79 }
80
81 // demodulate arbitrary modem type
MODEM(_demodulate_arb)82 void MODEM(_demodulate_arb)(MODEM() _q,
83 TC _x,
84 unsigned int * _sym_out)
85 {
86 //printf("modem_demodulate_arb() invoked with I=%d, Q=%d\n", x);
87
88 // search for symbol nearest to received sample
89 unsigned int i;
90 unsigned int s=0;
91 T d; // distance
92 T d_min = 0.0f; // minimum distance
93
94 for (i=0; i<_q->M; i++) {
95 // compute distance from received symbol to constellation point
96 d = cabsf(_x - _q->symbol_map[i]);
97
98 // retain symbol with minimum distance
99 if ( i==0 || d < d_min ) {
100 d_min = d;
101 s = i;
102 }
103 }
104
105 // set output symbol
106 *_sym_out = s;
107
108 // re-modulate symbol and store state
109 MODEM(_modulate_arb)(_q, *_sym_out, &_q->x_hat);
110 _q->r = _x;
111 }
112
113 // create a V.29 modem object (4 bits/symbol)
MODEM(_create_V29)114 MODEM() MODEM(_create_V29)()
115 {
116 MODEM() q = MODEM(_create_arb)(4);
117 #if T == float
118 MODEM(_arb_init)(q,(TC*)modem_arb_V29,16);
119 #endif
120 return q;
121 }
122
123 // create an arb16opt (optimal 16-qam) modem object
MODEM(_create_arb16opt)124 MODEM() MODEM(_create_arb16opt)()
125 {
126 MODEM() q = MODEM(_create_arb)(4);
127 #if T == float
128 MODEM(_arb_init)(q,(TC*)modem_arb16opt,16);
129 #endif
130 return q;
131 }
132
133 // create an arb32opt (optimal 32-qam) modem object
MODEM(_create_arb32opt)134 MODEM() MODEM(_create_arb32opt)()
135 {
136 MODEM() q = MODEM(_create_arb)(5);
137 #if T == float
138 MODEM(_arb_init)(q,(TC*)modem_arb32opt,32);
139 #endif
140 return q;
141 }
142
143 // create an arb64opt (optimal 64-qam) modem object
MODEM(_create_arb64opt)144 MODEM() MODEM(_create_arb64opt)()
145 {
146 MODEM() q = MODEM(_create_arb)(6);
147 #if T == float
148 MODEM(_arb_init)(q,(TC*)modem_arb64opt,64);
149 #endif
150 return q;
151 }
152
153 // create an arb128opt (optimal 128-qam) modem object
MODEM(_create_arb128opt)154 MODEM() MODEM(_create_arb128opt)()
155 {
156 MODEM() q = MODEM(_create_arb)(7);
157 #if T == float
158 MODEM(_arb_init)(q,(TC*)modem_arb128opt,128);
159 #endif
160 return q;
161 }
162
163 // create an arb256opt (optimal 256-qam) modem object
MODEM(_create_arb256opt)164 MODEM() MODEM(_create_arb256opt)()
165 {
166 MODEM() q = MODEM(_create_arb)(8);
167 #if T == float
168 MODEM(_arb_init)(q,(TC*)modem_arb256opt,256);
169 #endif
170 return q;
171 }
172
173 // create an arb64vt (64-qam vt logo) modem object
MODEM(_create_arb64vt)174 MODEM() MODEM(_create_arb64vt)()
175 {
176 MODEM() q = MODEM(_create_arb)(6);
177 #if T == float
178 MODEM(_arb_init)(q,(TC*)modem_arb_vt64,64);
179 #endif
180 return q;
181 }
182
183 // initialize an arbitrary modem object
184 // _mod : modem object
185 // _symbol_map : arbitrary modem symbol map
186 // _len : number of symbols in the map
MODEM(_arb_init)187 void MODEM(_arb_init)(MODEM() _q,
188 TC * _symbol_map,
189 unsigned int _len)
190 {
191 #ifdef LIQUID_VALIDATE_INPUT
192 if (_q->scheme != LIQUID_MODEM_ARB) {
193 fprintf(stderr,"error: modem_arb_init(), modem is not of arbitrary type\n");
194 exit(1);
195 } else if (_len != _q->M) {
196 fprintf(stderr,"error: modem_arb_init(), array sizes do not match\n");
197 exit(1);
198 }
199 #endif
200
201 unsigned int i;
202 for (i=0; i<_len; i++)
203 _q->symbol_map[i] = _symbol_map[i];
204
205 // balance I/Q channels
206 if (_q->scheme == LIQUID_MODEM_ARB)
207 MODEM(_arb_balance_iq)(_q);
208
209 // scale modem to have unity energy
210 MODEM(_arb_scale)(_q);
211
212 }
213
214 // initialize an arbitrary modem object on a file
215 // _mod : modem object
216 // _filename : name of the data file
MODEM(_arb_init_file)217 void MODEM(_arb_init_file)(MODEM() _q,
218 char * _filename)
219 {
220 // try to open file
221 FILE * fid = fopen(_filename, "r");
222 if (fid == NULL) {
223 fprintf(stderr,"error: modem_arb_init_file(), could not open file\n");
224 exit(1);
225 }
226
227 unsigned int i, results;
228 T sym_i, sym_q;
229 for (i=0; i<_q->M; i++) {
230 if ( feof(fid) ) {
231 fprintf(stderr,"error: modem_arb_init_file(), premature EOF for '%s'\n", _filename);
232 exit(1);
233 }
234
235 results = fscanf(fid, "%f %f\n", &sym_i, &sym_q);
236 _q->symbol_map[i] = sym_i + _Complex_I*sym_q;
237
238 // ensure proper number of symbols were read
239 if (results < 2) {
240 fprintf(stderr,"error: modem_arb_init_file(), unable to parse line\n");
241 exit(1);
242 }
243 }
244
245 fclose(fid);
246
247 // balance I/Q channels
248 if (_q->scheme == LIQUID_MODEM_ARB)
249 MODEM(_arb_balance_iq)(_q);
250
251 // scale modem to have unity energy
252 MODEM(_arb_scale)(_q);
253 }
254
255 // scale arbitrary modem constellation points
MODEM(_arb_scale)256 void MODEM(_arb_scale)(MODEM() _q)
257 {
258 unsigned int i;
259
260 // calculate energy
261 T mag, e = 0.0f;
262 for (i=0; i<_q->M; i++) {
263 mag = cabsf(_q->symbol_map[i]);
264 e += mag*mag;
265 }
266
267 e = sqrtf( e / _q->M );
268
269 for (i=0; i<_q->M; i++) {
270 _q->symbol_map[i] /= e;
271 }
272 }
273
274 // balance an arbitrary modem's I/Q points
MODEM(_arb_balance_iq)275 void MODEM(_arb_balance_iq)(MODEM() _q)
276 {
277 TC mean=0.0f;
278 unsigned int i;
279
280 // accumulate average signal
281 for (i=0; i<_q->M; i++) {
282 mean += _q->symbol_map[i];
283 }
284 mean /= (T) (_q->M);
285
286 // subtract mean value from reference levels
287 for (i=0; i<_q->M; i++) {
288 _q->symbol_map[i] -= mean;
289 }
290 }
291
292 // demodulate arbitrary modem type (soft)
MODEM(_demodulate_soft_arb)293 void MODEM(_demodulate_soft_arb)(MODEM() _q,
294 TC _r,
295 unsigned int * _s,
296 unsigned char * _soft_bits)
297 {
298 unsigned int bps = _q->m;
299 unsigned int M = _q->M;
300
301 // gamma = 1/(2*sigma^2), approximate for constellation size
302 T gamma = 1.2f*_q->M;
303
304 unsigned int s=0; // hard decision output
305 unsigned int k; // bit index
306 unsigned int i; // symbol index
307 T d; // distance for this symbol
308 TC x_hat; // re-modulated symbol
309
310 T dmin_0[bps];
311 T dmin_1[bps];
312 for (k=0; k<bps; k++) {
313 dmin_0[k] = 4.0f;
314 dmin_1[k] = 4.0f;
315 }
316 T dmin = 0.0f;
317
318 for (i=0; i<M; i++) {
319 // compute distance from received symbol
320 x_hat = _q->symbol_map[i];
321 d = crealf( (_r-x_hat)*conjf(_r-x_hat) );
322
323 // set hard-decision...
324 if (d < dmin || i==0) {
325 s = i;
326 dmin = d;
327 }
328
329 for (k=0; k<bps; k++) {
330 // strip bit
331 if ( (s >> (bps-k-1)) & 0x01 ) {
332 if (d < dmin_1[k]) dmin_1[k] = d;
333 } else {
334 if (d < dmin_0[k]) dmin_0[k] = d;
335 }
336 }
337 }
338
339 // make assignments
340 for (k=0; k<bps; k++) {
341 int soft_bit = ((dmin_0[k] - dmin_1[k])*gamma)*16 + 127;
342 if (soft_bit > 255) soft_bit = 255;
343 if (soft_bit < 0) soft_bit = 0;
344 _soft_bits[k] = (unsigned char)soft_bit;
345 }
346
347 // hard decision
348
349 // set hard output symbol
350 *_s = s;
351
352 // re-modulate symbol and store state
353 MODEM(_modulate_arb)(_q, *_s, &_q->x_hat);
354 _q->r = _r;
355 }
356
357