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