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