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_apsk.c
25 //
26 
27 // create an apsk (amplitude/phase-shift keying) modem object
MODEM(_create_apsk)28 MODEM() MODEM(_create_apsk)(unsigned int _bits_per_symbol)
29 {
30     // pointer to APSK definition container
31     struct liquid_apsk_s * apskdef = NULL;
32 
33     switch (_bits_per_symbol) {
34     case 2: apskdef = &liquid_apsk4;    break;
35     case 3: apskdef = &liquid_apsk8;    break;
36     case 4: apskdef = &liquid_apsk16;   break;
37     case 5: apskdef = &liquid_apsk32;   break;
38     case 6: apskdef = &liquid_apsk64;   break;
39     case 7: apskdef = &liquid_apsk128;  break;
40     case 8: apskdef = &liquid_apsk256;  break;
41     default:
42         fprintf(stderr,"error: modem_create_apsk(), unsupported modulation level (%u)\n",
43                 _bits_per_symbol);
44         exit(1);
45     }
46 
47     MODEM() q = (MODEM()) malloc( sizeof(struct MODEM(_s)) );
48     q->scheme = apskdef->scheme;
49     MODEM(_init)(q, _bits_per_symbol);
50 
51     // set APSK internals
52     unsigned int i;
53     q->data.apsk.num_levels = apskdef->num_levels;
54     for (i=0; i<q->data.apsk.num_levels; i++) {
55         q->data.apsk.p[i]   = apskdef->p[i];
56         q->data.apsk.r[i]   = apskdef->r[i];
57         q->data.apsk.phi[i] = apskdef->phi[i];
58     }
59 
60     // radius slicer
61     for (i=0; i<q->data.apsk.num_levels-1; i++)
62         q->data.apsk.r_slicer[i] = apskdef->r_slicer[i];
63 
64     // copy symbol map
65     q->data.apsk.map = (unsigned char *) malloc(q->M*sizeof(unsigned char));
66     memmove(q->data.apsk.map, apskdef->map, q->M*sizeof(unsigned char));
67 
68     // set modulation/demodulation function pointers
69     q->modulate_func = &MODEM(_modulate_apsk);
70     q->demodulate_func = &MODEM(_demodulate_apsk);
71 
72     // initialize soft-demodulation look-up table
73     switch (q->m) {
74     case 2: MODEM(_demodsoft_gentab)(q, 3); break;
75     case 3: MODEM(_demodsoft_gentab)(q, 3); break;
76     case 4: MODEM(_demodsoft_gentab)(q, 4); break;
77     case 5: MODEM(_demodsoft_gentab)(q, 4); break;
78     case 6: MODEM(_demodsoft_gentab)(q, 4); break;
79     case 7: MODEM(_demodsoft_gentab)(q, 5); break;
80     case 8: MODEM(_demodsoft_gentab)(q, 5); break;
81     default:;
82     }
83 
84     // initialize symbol map
85     q->symbol_map = (TC*)malloc(q->M*sizeof(TC));
86     MODEM(_init_map)(q);
87     q->modulate_using_map = 1;
88 
89     // reset modem and return
90     MODEM(_reset)(q);
91     return q;
92 }
93 
94 // modulate APSK
MODEM(_modulate_apsk)95 void MODEM(_modulate_apsk)(MODEM()      _q,
96                            unsigned int _sym_in,
97                            TC *         _y)
98 {
99     if (_sym_in >= _q->M) {
100         fprintf(stderr,"error: modem_modulate_apsk(), input symbol exceeds maximum\n");
101         return;
102     }
103 
104     // map input symbol to constellation symbol
105     unsigned int i;
106     unsigned int s = _q->data.apsk.map[_sym_in];
107 
108     // determine in which level the symbol is located
109     unsigned int p=0;   // level
110     unsigned int t=0;   // accumulated number of points per level
111     for (i=0; i<_q->data.apsk.num_levels; i++) {
112         if (s < t + _q->data.apsk.p[i]) {
113             p = i;
114             break;
115         }
116         t += _q->data.apsk.p[i];
117     }
118     unsigned int s0 = s - t;
119     unsigned int s1 = _q->data.apsk.p[p];
120 
121 #if 0
122     printf("  s : %3u -> %3u in level %3u (t = %3u) [symbol %3u / %3u]\n", _sym_in, s, p, t, s0,s1);
123 #endif
124 
125     // map symbol to constellation point (radius, angle)
126     T r = _q->data.apsk.r[p];
127     T phi = _q->data.apsk.phi[p] + (T)(s0)*2.0f*M_PI / (T)(s1);
128 
129     // compute output symbol
130     *_y = r * liquid_cexpjf(phi);
131 }
132 
133 // demodulate APSK
MODEM(_demodulate_apsk)134 void MODEM(_demodulate_apsk)(MODEM()        _q,
135                              TC             _x,
136                              unsigned int * _sym_out)
137 {
138     // compute amplitude
139     T r = cabsf(_x);
140 
141     // determine which ring to demodulate with
142     unsigned int i, p=0;
143     for (i=0; i<_q->data.apsk.num_levels-1; i++) {
144         if (r < _q->data.apsk.r_slicer[i]) {
145             p = i;
146             break;
147         } else {
148             p = _q->data.apsk.num_levels-1;
149         }
150     }
151 
152     // find closest point in ring
153     T theta = cargf(_x);
154     if (theta < 0.0f) theta += 2.0f*M_PI;
155     T dphi = 2.0f*M_PI / (T) _q->data.apsk.p[p];
156     unsigned int s_hat=0;
157     T i_hat = (theta - _q->data.apsk.phi[p]) / dphi;
158     s_hat = roundf(i_hat);      // compute symbol (closest angle)
159     s_hat %= _q->data.apsk.p[p];   // ensure symbol is in range
160     //printf("          i_hat : %12.8f (%3u)\n", i_hat, s_hat);
161 
162     // accumulate symbol points
163     for (i=0; i<p; i++)
164         s_hat += _q->data.apsk.p[i];
165     //assert(s_hat < _q->M);
166 
167     // reverse symbol mapping
168     unsigned int s_prime=0;
169     for (i=0; i<_q->M; i++) {
170         if ( _q->data.apsk.map[i] == s_hat) {
171             s_prime = i;
172             break;
173         }
174     }
175 
176 #if 0
177     printf("              x : %12.8f + j*%12.8f\n", crealf(_x), cimagf(_x));
178     printf("              p : %3u\n", p);
179     printf("          theta : %12.8f\n", theta);
180     printf("           dmin : %12.8f\n", dmin);
181     printf("              s : %3u > %3u\n", s_hat, s_prime);
182 #endif
183 
184     *_sym_out = s_prime;
185 
186     // re-modulate symbol and store state
187     MODEM(_modulate)(_q, s_prime, &_q->x_hat);
188     _q->r = _x;
189 }
190 
191