blob: 04f7e30cd72a75eeaf3a9c19f2c3338808240b91 [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
kurtis.heimerla198d452011-11-26 03:19:28 +00002* Copyright 2008, 2011 Free Software Foundation, Inc.
dburgessb3a0ca42011-10-12 07:44:40 +00003*
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02004* SPDX-License-Identifier: AGPL-3.0+
5*
dburgessb3a0ca42011-10-12 07:44:40 +00006* This software is distributed under the terms of the GNU Affero Public License.
7* See the COPYING file in the main directory for details.
8*
9* This use of this software may be subject to additional restrictions.
10* See the LEGAL file in the main directory for details.
11
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU Affero General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU Affero General Public License for more details.
21
22 You should have received a copy of the GNU Affero General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24
25*/
26
Thomas Tsou7e4e5362013-10-30 21:18:55 -040027#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
dburgessb3a0ca42011-10-12 07:44:40 +000031#include "sigProcLib.h"
32#include "GSMCommon.h"
Alexander Chemeris954b1182015-06-04 15:39:41 -040033#include "Logger.h"
Tom Tsoud3253432016-03-06 03:08:01 -080034#include "Resampler.h"
dburgessb3a0ca42011-10-12 07:44:40 +000035
Thomas Tsou3eaae802013-08-20 19:31:14 -040036extern "C" {
37#include "convolve.h"
Thomas Tsou7e4e5362013-10-30 21:18:55 -040038#include "scale.h"
Thomas Tsou0a3dc4c2013-11-09 02:29:55 -050039#include "mult.h"
Thomas Tsou3eaae802013-08-20 19:31:14 -040040}
41
Thomas Tsou7e4e5362013-10-30 21:18:55 -040042using namespace GSM;
43
Thomas Tsouf79c4d02013-11-09 15:51:56 -060044#define TABLESIZE 1024
45#define DELAYFILTS 64
dburgessb3a0ca42011-10-12 07:44:40 +000046
Tom Tsou577cd022015-05-18 13:57:54 -070047/* Clipping detection threshold */
48#define CLIP_THRESH 30000.0f
49
dburgessb3a0ca42011-10-12 07:44:40 +000050/** Lookup tables for trigonometric approximation */
Tom Tsoubb0c68a2017-06-16 17:08:40 -070051static float sincTable[TABLESIZE+1]; // add 1 element for wrap around
dburgessb3a0ca42011-10-12 07:44:40 +000052
53/** Constants */
54static const float M_PI_F = (float)M_PI;
dburgessb3a0ca42011-10-12 07:44:40 +000055
Thomas Tsouc1f7c422013-10-11 13:49:55 -040056/* Precomputed rotation vectors */
Tom Tsou2079a3c2016-03-06 00:58:56 -080057static signalVector *GMSKRotation4 = NULL;
58static signalVector *GMSKReverseRotation4 = NULL;
Thomas Tsouc1f7c422013-10-11 13:49:55 -040059static signalVector *GMSKRotation1 = NULL;
60static signalVector *GMSKReverseRotation1 = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +000061
Thomas Tsouf79c4d02013-11-09 15:51:56 -060062/* Precomputed fractional delay filters */
63static signalVector *delayFilters[DELAYFILTS];
64
Tom Tsou70134a02017-06-12 14:23:53 -070065static const Complex<float> psk8_table[8] = {
Tom Tsoud3253432016-03-06 03:08:01 -080066 Complex<float>(-0.70710678, 0.70710678),
67 Complex<float>( 0.0, -1.0),
68 Complex<float>( 0.0, 1.0),
69 Complex<float>( 0.70710678, -0.70710678),
70 Complex<float>(-1.0, 0.0),
71 Complex<float>(-0.70710678, -0.70710678),
72 Complex<float>( 0.70710678, 0.70710678),
73 Complex<float>( 1.0, 0.0),
74};
75
76/* Downsampling filterbank - 4 SPS to 1 SPS */
77#define DOWNSAMPLE_IN_LEN 624
78#define DOWNSAMPLE_OUT_LEN 156
79
80static Resampler *dnsampler = NULL;
Tom Tsoud3253432016-03-06 03:08:01 -080081
Thomas Tsoue5dcfc42013-08-20 16:27:12 -040082/*
Thomas Tsou3eaae802013-08-20 19:31:14 -040083 * RACH and midamble correlation waveforms. Store the buffer separately
84 * because we need to allocate it explicitly outside of the signal vector
85 * constructor. This is because C++ (prior to C++11) is unable to natively
86 * perform 16-byte memory alignment required by many SSE instructions.
Thomas Tsoue5dcfc42013-08-20 16:27:12 -040087 */
88struct CorrelationSequence {
Harald Welte5c6ca172019-07-21 11:49:44 +020089 CorrelationSequence() : sequence(NULL), buffer(NULL), toa(0.0)
Thomas Tsoue5dcfc42013-08-20 16:27:12 -040090 {
91 }
92
93 ~CorrelationSequence()
94 {
95 delete sequence;
96 }
97
dburgessb3a0ca42011-10-12 07:44:40 +000098 signalVector *sequence;
Thomas Tsou3eaae802013-08-20 19:31:14 -040099 void *buffer;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400100 float toa;
dburgessb3a0ca42011-10-12 07:44:40 +0000101 complex gain;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400102};
dburgessb3a0ca42011-10-12 07:44:40 +0000103
Thomas Tsou83e06892013-08-20 16:10:01 -0400104/*
Thomas Tsou3eaae802013-08-20 19:31:14 -0400105 * Gaussian and empty modulation pulses. Like the correlation sequences,
106 * store the runtime (Gaussian) buffer separately because of needed alignment
107 * for SSE instructions.
Thomas Tsou83e06892013-08-20 16:10:01 -0400108 */
109struct PulseSequence {
Pau Espin Pedrolf7331762018-12-03 17:46:04 +0100110 PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL)
Thomas Tsou83e06892013-08-20 16:10:01 -0400111 {
112 }
113
114 ~PulseSequence()
115 {
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800116 delete c0;
117 delete c1;
Tom Tsoud3253432016-03-06 03:08:01 -0800118 delete c0_inv;
Thomas Tsou83e06892013-08-20 16:10:01 -0400119 delete empty;
120 }
121
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800122 signalVector *c0;
123 signalVector *c1;
Tom Tsoud3253432016-03-06 03:08:01 -0800124 signalVector *c0_inv;
Thomas Tsou83e06892013-08-20 16:10:01 -0400125 signalVector *empty;
126};
127
Tom Tsoud3253432016-03-06 03:08:01 -0800128static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
129static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
Vadim Yanitskiya79bc702018-10-17 11:01:58 +0200130static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL};
Tom Tsoud3253432016-03-06 03:08:01 -0800131static PulseSequence *GSMPulse1 = NULL;
132static PulseSequence *GSMPulse4 = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000133
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400134void sigProcLibDestroy()
135{
dburgessb3a0ca42011-10-12 07:44:40 +0000136 for (int i = 0; i < 8; i++) {
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400137 delete gMidambles[i];
Tom Tsoud3253432016-03-06 03:08:01 -0800138 delete gEdgeMidambles[i];
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400139 gMidambles[i] = NULL;
Tom Tsoud3253432016-03-06 03:08:01 -0800140 gEdgeMidambles[i] = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000141 }
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400142
Thomas Tsouf79c4d02013-11-09 15:51:56 -0600143 for (int i = 0; i < DELAYFILTS; i++) {
144 delete delayFilters[i];
145 delayFilters[i] = NULL;
146 }
147
Vadim Yanitskiya79bc702018-10-17 11:01:58 +0200148 for (int i = 0; i < 3; i++) {
149 delete gRACHSequences[i];
150 gRACHSequences[i] = NULL;
151 }
152
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400153 delete GMSKRotation1;
154 delete GMSKReverseRotation1;
Tom Tsou2079a3c2016-03-06 00:58:56 -0800155 delete GMSKRotation4;
156 delete GMSKReverseRotation4;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400157 delete GSMPulse1;
Tom Tsou2079a3c2016-03-06 00:58:56 -0800158 delete GSMPulse4;
Tom Tsoud3253432016-03-06 03:08:01 -0800159 delete dnsampler;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400160
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400161 GMSKRotation1 = NULL;
Tom Tsou2079a3c2016-03-06 00:58:56 -0800162 GMSKRotation4 = NULL;
163 GMSKReverseRotation4 = NULL;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400164 GMSKReverseRotation1 = NULL;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400165 GSMPulse1 = NULL;
Tom Tsou2079a3c2016-03-06 00:58:56 -0800166 GSMPulse4 = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000167}
168
Tom Tsou70134a02017-06-12 14:23:53 -0700169static float vectorNorm2(const signalVector &x)
dburgessb3a0ca42011-10-12 07:44:40 +0000170{
171 signalVector::const_iterator xPtr = x.begin();
172 float Energy = 0.0;
173 for (;xPtr != x.end();xPtr++) {
174 Energy += xPtr->norm2();
175 }
176 return Energy;
177}
178
Tom Tsou2079a3c2016-03-06 00:58:56 -0800179/*
180 * Initialize 4 sps and 1 sps rotation tables
181 */
182static void initGMSKRotationTables()
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400183{
Tom Tsou2079a3c2016-03-06 00:58:56 -0800184 size_t len1 = 157, len4 = 625;
185
186 GMSKRotation4 = new signalVector(len4);
187 GMSKReverseRotation4 = new signalVector(len4);
188 signalVector::iterator rotPtr = GMSKRotation4->begin();
189 signalVector::iterator revPtr = GMSKReverseRotation4->begin();
Tom Tsoubb0c68a2017-06-16 17:08:40 -0700190 auto phase = 0.0;
Tom Tsou2079a3c2016-03-06 00:58:56 -0800191 while (rotPtr != GMSKRotation4->end()) {
Tom Tsoubb0c68a2017-06-16 17:08:40 -0700192 *rotPtr++ = complex(cos(phase), sin(phase));
193 *revPtr++ = complex(cos(-phase), sin(-phase));
194 phase += M_PI / 2.0 / 4.0;
dburgessb3a0ca42011-10-12 07:44:40 +0000195 }
dburgessb3a0ca42011-10-12 07:44:40 +0000196
Tom Tsou2079a3c2016-03-06 00:58:56 -0800197 GMSKRotation1 = new signalVector(len1);
198 GMSKReverseRotation1 = new signalVector(len1);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400199 rotPtr = GMSKRotation1->begin();
200 revPtr = GMSKReverseRotation1->begin();
201 phase = 0.0;
202 while (rotPtr != GMSKRotation1->end()) {
Tom Tsoubb0c68a2017-06-16 17:08:40 -0700203 *rotPtr++ = complex(cos(phase), sin(phase));
204 *revPtr++ = complex(cos(-phase), sin(-phase));
205 phase += M_PI / 2.0;
Thomas Tsoue57004d2013-08-20 18:55:33 -0400206 }
dburgessb3a0ca42011-10-12 07:44:40 +0000207}
208
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400209static void GMSKRotate(signalVector &x, int sps)
210{
Thomas Tsou0a3dc4c2013-11-09 02:29:55 -0500211#if HAVE_NEON
212 size_t len;
213 signalVector *a, *b, *out;
214
215 a = &x;
216 out = &x;
217 len = out->size();
218
219 if (len == 157)
220 len--;
221
222 if (sps == 1)
223 b = GMSKRotation1;
224 else
Tom Tsou2079a3c2016-03-06 00:58:56 -0800225 b = GMSKRotation4;
Thomas Tsou0a3dc4c2013-11-09 02:29:55 -0500226
227 mul_complex((float *) out->begin(),
228 (float *) a->begin(),
229 (float *) b->begin(), len);
230#else
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400231 signalVector::iterator rotPtr, xPtr = x.begin();
232
233 if (sps == 1)
234 rotPtr = GMSKRotation1->begin();
235 else
Tom Tsou2079a3c2016-03-06 00:58:56 -0800236 rotPtr = GMSKRotation4->begin();
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400237
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500238 if (x.isReal()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000239 while (xPtr < x.end()) {
240 *xPtr = *rotPtr++ * (xPtr->real());
241 xPtr++;
242 }
243 }
244 else {
245 while (xPtr < x.end()) {
246 *xPtr = *rotPtr++ * (*xPtr);
247 xPtr++;
248 }
249 }
Thomas Tsou0a3dc4c2013-11-09 02:29:55 -0500250#endif
dburgessb3a0ca42011-10-12 07:44:40 +0000251}
252
Tom Tsou2079a3c2016-03-06 00:58:56 -0800253static bool GMSKReverseRotate(signalVector &x, int sps)
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400254{
255 signalVector::iterator rotPtr, xPtr= x.begin();
256
257 if (sps == 1)
258 rotPtr = GMSKReverseRotation1->begin();
Tom Tsou2079a3c2016-03-06 00:58:56 -0800259 else if (sps == 4)
260 rotPtr = GMSKReverseRotation4->begin();
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400261 else
Tom Tsou2079a3c2016-03-06 00:58:56 -0800262 return false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400263
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500264 if (x.isReal()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000265 while (xPtr < x.end()) {
266 *xPtr = *rotPtr++ * (xPtr->real());
267 xPtr++;
268 }
269 }
270 else {
271 while (xPtr < x.end()) {
272 *xPtr = *rotPtr++ * (*xPtr);
273 xPtr++;
274 }
275 }
Tom Tsou2079a3c2016-03-06 00:58:56 -0800276
277 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000278}
279
Tom Tsou70134a02017-06-12 14:23:53 -0700280/** Convolution type indicator */
281enum ConvType {
282 START_ONLY,
283 NO_DELAY,
284 CUSTOM,
285 UNDEFINED,
286};
287
288static signalVector *convolve(const signalVector *x, const signalVector *h,
289 signalVector *y, ConvType spanType,
Sylvain Munauta3934a12018-12-20 19:10:26 +0100290 size_t start = 0, size_t len = 0)
dburgessb3a0ca42011-10-12 07:44:40 +0000291{
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500292 int rc;
293 size_t head = 0, tail = 0;
Thomas Tsou3eaae802013-08-20 19:31:14 -0400294 bool alloc = false, append = false;
295 const signalVector *_x = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000296
Thomas Tsou3eaae802013-08-20 19:31:14 -0400297 if (!x || !h)
dburgessb3a0ca42011-10-12 07:44:40 +0000298 return NULL;
299
Thomas Tsou3eaae802013-08-20 19:31:14 -0400300 switch (spanType) {
301 case START_ONLY:
302 start = 0;
Thomas Tsou6f4906e2013-11-09 02:40:18 -0500303 head = h->size() - 1;
Thomas Tsou3eaae802013-08-20 19:31:14 -0400304 len = x->size();
Thomas Tsou6f4906e2013-11-09 02:40:18 -0500305
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500306 if (x->getStart() < head)
Thomas Tsou6f4906e2013-11-09 02:40:18 -0500307 append = true;
dburgessb3a0ca42011-10-12 07:44:40 +0000308 break;
Thomas Tsou3eaae802013-08-20 19:31:14 -0400309 case NO_DELAY:
310 start = h->size() / 2;
311 head = start;
312 tail = start;
313 len = x->size();
314 append = true;
315 break;
316 case CUSTOM:
317 if (start < h->size() - 1) {
318 head = h->size() - start;
319 append = true;
320 }
321 if (start + len > x->size()) {
322 tail = start + len - x->size();
323 append = true;
dburgessb3a0ca42011-10-12 07:44:40 +0000324 }
325 break;
326 default:
327 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000328 }
dburgessb3a0ca42011-10-12 07:44:40 +0000329
Thomas Tsou3eaae802013-08-20 19:31:14 -0400330 /*
331 * Error if the output vector is too small. Create the output vector
332 * if the pointer is NULL.
333 */
334 if (y && (len > y->size()))
335 return NULL;
336 if (!y) {
Pau Espin Pedrolf7331762018-12-03 17:46:04 +0100337 y = new signalVector(len, convolve_h_alloc, free);
Thomas Tsou3eaae802013-08-20 19:31:14 -0400338 alloc = true;
339 }
340
341 /* Prepend or post-pend the input vector if the parameters require it */
342 if (append)
343 _x = new signalVector(*x, head, tail);
344 else
345 _x = x;
346
347 /*
Martin Hauke066fd042019-10-13 19:08:00 +0200348 * Four convolve types:
Thomas Tsou3eaae802013-08-20 19:31:14 -0400349 * 1. Complex-Real (aligned)
350 * 2. Complex-Complex (aligned)
351 * 3. Complex-Real (!aligned)
352 * 4. Complex-Complex (!aligned)
353 */
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500354 if (h->isReal() && h->isAligned()) {
Thomas Tsou3eaae802013-08-20 19:31:14 -0400355 rc = convolve_real((float *) _x->begin(), _x->size(),
356 (float *) h->begin(), h->size(),
357 (float *) y->begin(), y->size(),
Sylvain Munauta3934a12018-12-20 19:10:26 +0100358 start, len);
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500359 } else if (!h->isReal() && h->isAligned()) {
Thomas Tsou3eaae802013-08-20 19:31:14 -0400360 rc = convolve_complex((float *) _x->begin(), _x->size(),
361 (float *) h->begin(), h->size(),
362 (float *) y->begin(), y->size(),
Sylvain Munauta3934a12018-12-20 19:10:26 +0100363 start, len);
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500364 } else if (h->isReal() && !h->isAligned()) {
Thomas Tsou3eaae802013-08-20 19:31:14 -0400365 rc = base_convolve_real((float *) _x->begin(), _x->size(),
366 (float *) h->begin(), h->size(),
367 (float *) y->begin(), y->size(),
Sylvain Munauta3934a12018-12-20 19:10:26 +0100368 start, len);
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500369 } else if (!h->isReal() && !h->isAligned()) {
Thomas Tsou3eaae802013-08-20 19:31:14 -0400370 rc = base_convolve_complex((float *) _x->begin(), _x->size(),
371 (float *) h->begin(), h->size(),
372 (float *) y->begin(), y->size(),
Sylvain Munauta3934a12018-12-20 19:10:26 +0100373 start, len);
Thomas Tsou3eaae802013-08-20 19:31:14 -0400374 } else {
375 rc = -1;
376 }
377
378 if (append)
379 delete _x;
380
381 if (rc < 0) {
382 if (alloc)
383 delete y;
384 return NULL;
385 }
386
387 return y;
388}
dburgessb3a0ca42011-10-12 07:44:40 +0000389
Tom Tsoud3253432016-03-06 03:08:01 -0800390/*
391 * Generate static EDGE linear equalizer. This equalizer is not adaptive.
392 * Filter taps are generated from the inverted 1 SPS impulse response of
393 * the EDGE pulse shape captured after the downsampling filter.
394 */
395static bool generateInvertC0Pulse(PulseSequence *pulse)
396{
397 if (!pulse)
398 return false;
399
Pau Espin Pedrolf7331762018-12-03 17:46:04 +0100400 pulse->c0_inv = new signalVector((complex *) convolve_h_alloc(5), 0, 5, convolve_h_alloc, free);
Tom Tsoud3253432016-03-06 03:08:01 -0800401 pulse->c0_inv->isReal(true);
402 pulse->c0_inv->setAligned(false);
403
404 signalVector::iterator xP = pulse->c0_inv->begin();
405 *xP++ = 0.15884;
406 *xP++ = -0.43176;
407 *xP++ = 1.00000;
408 *xP++ = -0.42608;
409 *xP++ = 0.14882;
410
411 return true;
412}
413
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400414static bool generateC1Pulse(int sps, PulseSequence *pulse)
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800415{
416 int len;
417
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400418 if (!pulse)
419 return false;
420
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800421 switch (sps) {
422 case 4:
423 len = 8;
424 break;
425 default:
426 return false;
427 }
428
Pau Espin Pedrolf7331762018-12-03 17:46:04 +0100429 pulse->c1 = new signalVector((complex *) convolve_h_alloc(len), 0, len, convolve_h_alloc, free);
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500430 pulse->c1->isReal(true);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800431
432 /* Enable alignment for SSE usage */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400433 pulse->c1->setAligned(true);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800434
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400435 signalVector::iterator xP = pulse->c1->begin();
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800436
437 switch (sps) {
438 case 4:
439 /* BT = 0.30 */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400440 *xP++ = 0.0;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800441 *xP++ = 8.16373112e-03;
442 *xP++ = 2.84385729e-02;
443 *xP++ = 5.64158904e-02;
444 *xP++ = 7.05463553e-02;
445 *xP++ = 5.64158904e-02;
446 *xP++ = 2.84385729e-02;
447 *xP++ = 8.16373112e-03;
448 }
449
450 return true;
451}
452
Tom Tsou2079a3c2016-03-06 00:58:56 -0800453static PulseSequence *generateGSMPulse(int sps)
dburgessb3a0ca42011-10-12 07:44:40 +0000454{
Thomas Tsou83e06892013-08-20 16:10:01 -0400455 int len;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800456 float arg, avg, center;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400457 PulseSequence *pulse;
Thomas Tsou83e06892013-08-20 16:10:01 -0400458
Tom Tsoud3253432016-03-06 03:08:01 -0800459 if ((sps != 1) && (sps != 4))
460 return NULL;
461
Thomas Tsou83e06892013-08-20 16:10:01 -0400462 /* Store a single tap filter used for correlation sequence generation */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400463 pulse = new PulseSequence();
464 pulse->empty = new signalVector(1);
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500465 pulse->empty->isReal(true);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400466 *(pulse->empty->begin()) = 1.0f;
Thomas Tsou83e06892013-08-20 16:10:01 -0400467
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400468 /*
469 * For 4 samples-per-symbol use a precomputed single pulse Laurent
470 * approximation. This should yields below 2 degrees of phase error at
471 * the modulator output. Use the existing pulse approximation for all
472 * other oversampling factors.
473 */
474 switch (sps) {
475 case 4:
476 len = 16;
477 break;
Tom Tsoud3253432016-03-06 03:08:01 -0800478 case 1:
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400479 default:
Tom Tsou2079a3c2016-03-06 00:58:56 -0800480 len = 4;
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400481 }
Thomas Tsou3eaae802013-08-20 19:31:14 -0400482
Pau Espin Pedrolf7331762018-12-03 17:46:04 +0100483 pulse->c0 = new signalVector((complex *) convolve_h_alloc(len), 0, len, convolve_h_alloc, free);
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500484 pulse->c0->isReal(true);
Thomas Tsou3eaae802013-08-20 19:31:14 -0400485
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800486 /* Enable alingnment for SSE usage */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400487 pulse->c0->setAligned(true);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800488
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400489 signalVector::iterator xP = pulse->c0->begin();
Thomas Tsou83e06892013-08-20 16:10:01 -0400490
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400491 if (sps == 4) {
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800492 *xP++ = 0.0;
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400493 *xP++ = 4.46348606e-03;
494 *xP++ = 2.84385729e-02;
495 *xP++ = 1.03184855e-01;
496 *xP++ = 2.56065552e-01;
497 *xP++ = 4.76375085e-01;
498 *xP++ = 7.05961177e-01;
499 *xP++ = 8.71291644e-01;
500 *xP++ = 9.29453645e-01;
501 *xP++ = 8.71291644e-01;
502 *xP++ = 7.05961177e-01;
503 *xP++ = 4.76375085e-01;
504 *xP++ = 2.56065552e-01;
505 *xP++ = 1.03184855e-01;
506 *xP++ = 2.84385729e-02;
507 *xP++ = 4.46348606e-03;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400508 generateC1Pulse(sps, pulse);
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400509 } else {
510 center = (float) (len - 1.0) / 2.0;
Thomas Tsou83e06892013-08-20 16:10:01 -0400511
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400512 /* GSM pulse approximation */
513 for (int i = 0; i < len; i++) {
514 arg = ((float) i - center) / (float) sps;
515 *xP++ = 0.96 * exp(-1.1380 * arg * arg -
516 0.527 * arg * arg * arg * arg);
517 }
dburgessb3a0ca42011-10-12 07:44:40 +0000518
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400519 avg = sqrtf(vectorNorm2(*pulse->c0) / sps);
520 xP = pulse->c0->begin();
521 for (int i = 0; i < len; i++)
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800522 *xP++ /= avg;
523 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400524
Tom Tsoud3253432016-03-06 03:08:01 -0800525 /*
526 * Current form of the EDGE equalization filter non-realizable at 4 SPS.
527 * Load the onto both 1 SPS and 4 SPS objects for convenience. Note that
528 * the EDGE demodulator downsamples to 1 SPS prior to equalization.
529 */
530 generateInvertC0Pulse(pulse);
531
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400532 return pulse;
dburgessb3a0ca42011-10-12 07:44:40 +0000533}
534
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200535/* Convert -1..+1 soft bits to 0..1 soft bits */
536void vectorSlicer(float *dest, const float *src, size_t len)
Tom Tsoud3253432016-03-06 03:08:01 -0800537{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200538 size_t i;
539 for (i = 0; i < len; i++) {
540 dest[i] = 0.5 * (src[i] + 1.0f);
541 if (dest[i] > 1.0)
542 dest[i] = 1.0;
543 else if (dest[i] < 0.0)
544 dest[i] = 0.0;
545 }
Tom Tsoud3253432016-03-06 03:08:01 -0800546}
547
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800548static signalVector *rotateBurst(const BitVector &wBurst,
549 int guardPeriodLength, int sps)
550{
551 int burst_len;
Tom Tsou7278a872017-06-14 14:50:39 -0700552 signalVector *pulse, rotated;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800553 signalVector::iterator itr;
554
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400555 pulse = GSMPulse1->empty;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800556 burst_len = sps * (wBurst.size() + guardPeriodLength);
557 rotated = signalVector(burst_len);
558 itr = rotated.begin();
559
560 for (unsigned i = 0; i < wBurst.size(); i++) {
561 *itr = 2.0 * (wBurst[i] & 0x01) - 1.0;
562 itr += sps;
563 }
564
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400565 GMSKRotate(rotated, sps);
Thomas Tsou20eb6d62013-11-09 14:30:41 -0500566 rotated.isReal(false);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800567
568 /* Dummy filter operation */
Tom Tsou7278a872017-06-14 14:50:39 -0700569 return convolve(&rotated, pulse, NULL, START_ONLY);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800570}
571
Tom Tsoud3253432016-03-06 03:08:01 -0800572static void rotateBurst2(signalVector &burst, double phase)
573{
574 Complex<float> rot = Complex<float>(cos(phase), sin(phase));
575
576 for (size_t i = 0; i < burst.size(); i++)
577 burst[i] = burst[i] * rot;
578}
579
Tom Tsou4dfd64a2016-03-06 20:31:51 -0800580/*
581 * Ignore the guard length argument in the GMSK modulator interface
582 * because it results in 624/628 sized bursts instead of the preferred
583 * burst length of 625. Only 4 SPS is supported.
584 */
585static signalVector *modulateBurstLaurent(const BitVector &bits)
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800586{
Tom Tsou4dfd64a2016-03-06 20:31:51 -0800587 int burst_len, sps = 4;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800588 float phase;
Tom Tsou7278a872017-06-14 14:50:39 -0700589 signalVector *c0_pulse, *c1_pulse, *c0_shaped, *c1_shaped;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800590 signalVector::iterator c0_itr, c1_itr;
591
Tom Tsou2079a3c2016-03-06 00:58:56 -0800592 c0_pulse = GSMPulse4->c0;
593 c1_pulse = GSMPulse4->c1;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800594
Tom Tsou4dfd64a2016-03-06 20:31:51 -0800595 if (bits.size() > 156)
596 return NULL;
597
598 burst_len = 625;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800599
Tom Tsou7278a872017-06-14 14:50:39 -0700600 signalVector c0_burst(burst_len, c0_pulse->size());
601 c0_burst.isReal(true);
602 c0_itr = c0_burst.begin();
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800603
Tom Tsou7278a872017-06-14 14:50:39 -0700604 signalVector c1_burst(burst_len, c1_pulse->size());
605 c1_itr = c1_burst.begin();
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800606
Tom Tsouaa15d622016-08-11 14:36:23 -0700607 /* Padded differential tail bits */
608 *c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800609 c0_itr += sps;
610
611 /* Main burst bits */
612 for (unsigned i = 0; i < bits.size(); i++) {
613 *c0_itr = 2.0 * (bits[i] & 0x01) - 1.0;
614 c0_itr += sps;
615 }
616
Tom Tsouaa15d622016-08-11 14:36:23 -0700617 /* Padded differential tail bits */
618 *c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800619
620 /* Generate C0 phase coefficients */
Tom Tsou7278a872017-06-14 14:50:39 -0700621 GMSKRotate(c0_burst, sps);
622 c0_burst.isReal(false);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800623
Tom Tsou7278a872017-06-14 14:50:39 -0700624 c0_itr = c0_burst.begin();
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800625 c0_itr += sps * 2;
626 c1_itr += sps * 2;
627
628 /* Start magic */
629 phase = 2.0 * ((0x01 & 0x01) ^ (0x01 & 0x01)) - 1.0;
630 *c1_itr = *c0_itr * Complex<float>(0, phase);
631 c0_itr += sps;
632 c1_itr += sps;
633
634 /* Generate C1 phase coefficients */
635 for (unsigned i = 2; i < bits.size(); i++) {
636 phase = 2.0 * ((bits[i - 1] & 0x01) ^ (bits[i - 2] & 0x01)) - 1.0;
637 *c1_itr = *c0_itr * Complex<float>(0, phase);
638
639 c0_itr += sps;
640 c1_itr += sps;
641 }
642
643 /* End magic */
644 int i = bits.size();
645 phase = 2.0 * ((bits[i-1] & 0x01) ^ (bits[i-2] & 0x01)) - 1.0;
646 *c1_itr = *c0_itr * Complex<float>(0, phase);
647
648 /* Primary (C0) and secondary (C1) pulse shaping */
Tom Tsou7278a872017-06-14 14:50:39 -0700649 c0_shaped = convolve(&c0_burst, c0_pulse, NULL, START_ONLY);
650 c1_shaped = convolve(&c1_burst, c1_pulse, NULL, START_ONLY);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800651
652 /* Sum shaped outputs into C0 */
653 c0_itr = c0_shaped->begin();
654 c1_itr = c1_shaped->begin();
655 for (unsigned i = 0; i < c0_shaped->size(); i++ )
656 *c0_itr++ += *c1_itr++;
657
658 delete c1_shaped;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800659 return c0_shaped;
660}
661
Tom Tsoud3253432016-03-06 03:08:01 -0800662static signalVector *rotateEdgeBurst(const signalVector &symbols, int sps)
663{
664 signalVector *burst;
665 signalVector::iterator burst_itr;
666
667 burst = new signalVector(symbols.size() * sps);
668 burst_itr = burst->begin();
669
670 for (size_t i = 0; i < symbols.size(); i++) {
671 float phase = i * 3.0f * M_PI / 8.0f;
672 Complex<float> rot = Complex<float>(cos(phase), sin(phase));
673
674 *burst_itr = symbols[i] * rot;
675 burst_itr += sps;
676 }
677
678 return burst;
679}
680
681static signalVector *derotateEdgeBurst(const signalVector &symbols, int sps)
682{
683 signalVector *burst;
684 signalVector::iterator burst_itr;
685
686 if (symbols.size() % sps)
687 return NULL;
688
689 burst = new signalVector(symbols.size() / sps);
690 burst_itr = burst->begin();
691
692 for (size_t i = 0; i < burst->size(); i++) {
693 float phase = (float) (i % 16) * 3.0f * M_PI / 8.0f;
694 Complex<float> rot = Complex<float>(cosf(phase), -sinf(phase));
695
696 *burst_itr = symbols[sps * i] * rot;
697 burst_itr++;
698 }
699
700 return burst;
701}
702
703static signalVector *mapEdgeSymbols(const BitVector &bits)
704{
705 if (bits.size() % 3)
706 return NULL;
707
708 signalVector *symbols = new signalVector(bits.size() / 3);
709
710 for (size_t i = 0; i < symbols->size(); i++) {
711 unsigned index = (((unsigned) bits[3 * i + 0] & 0x01) << 0) |
712 (((unsigned) bits[3 * i + 1] & 0x01) << 1) |
713 (((unsigned) bits[3 * i + 2] & 0x01) << 2);
714
715 (*symbols)[i] = psk8_table[index];
716 }
717
718 return symbols;
719}
720
Tom Tsoud2b07032016-04-26 19:28:59 -0700721/*
722 * EDGE 8-PSK rotate and pulse shape
723 *
724 * Delay the EDGE downlink bursts by one symbol in order to match GMSK pulse
725 * shaping group delay. The difference in group delay arises from the dual
Martin Hauke066fd042019-10-13 19:08:00 +0200726 * pulse filter combination of the GMSK Laurent representation whereas 8-PSK
Tom Tsoud2b07032016-04-26 19:28:59 -0700727 * uses a single pulse linear filter.
728 */
Tom Tsoud3253432016-03-06 03:08:01 -0800729static signalVector *shapeEdgeBurst(const signalVector &symbols)
730{
Tom Tsoud2b07032016-04-26 19:28:59 -0700731 size_t nsyms, nsamps = 625, sps = 4;
Tom Tsoud3253432016-03-06 03:08:01 -0800732 signalVector::iterator burst_itr;
733
734 nsyms = symbols.size();
735
Tom Tsoud2b07032016-04-26 19:28:59 -0700736 if (nsyms * sps > nsamps)
Tom Tsoud3253432016-03-06 03:08:01 -0800737 nsyms = 156;
738
Tom Tsou7278a872017-06-14 14:50:39 -0700739 signalVector burst(nsamps, GSMPulse4->c0->size());
Tom Tsoud3253432016-03-06 03:08:01 -0800740
Tom Tsoud2b07032016-04-26 19:28:59 -0700741 /* Delay burst by 1 symbol */
Tom Tsou7278a872017-06-14 14:50:39 -0700742 burst_itr = burst.begin() + sps;
Tom Tsou06676ea2016-07-19 12:50:21 -0700743 for (size_t i = 0; i < nsyms; i++) {
Tom Tsoud3253432016-03-06 03:08:01 -0800744 float phase = i * 3.0f * M_PI / 8.0f;
745 Complex<float> rot = Complex<float>(cos(phase), sin(phase));
746
747 *burst_itr = symbols[i] * rot;
Tom Tsoud2b07032016-04-26 19:28:59 -0700748 burst_itr += sps;
Tom Tsoud3253432016-03-06 03:08:01 -0800749 }
750
751 /* Single Gaussian pulse approximation shaping */
Tom Tsou7278a872017-06-14 14:50:39 -0700752 return convolve(&burst, GSMPulse4->c0, NULL, START_ONLY);
Tom Tsoud3253432016-03-06 03:08:01 -0800753}
754
755/*
Tom Tsou8ee2f382016-03-06 20:57:34 -0800756 * Generate a random GSM normal burst.
757 */
758signalVector *genRandNormalBurst(int tsc, int sps, int tn)
759{
760 if ((tsc < 0) || (tsc > 7) || (tn < 0) || (tn > 7))
761 return NULL;
762 if ((sps != 1) && (sps != 4))
763 return NULL;
764
765 int i = 0;
Tom Tsou7278a872017-06-14 14:50:39 -0700766 BitVector bits(148);
Tom Tsou8ee2f382016-03-06 20:57:34 -0800767
768 /* Tail bits */
Alexander Chemeris5e65b532017-03-24 23:24:22 -0700769 for (; i < 3; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700770 bits[i] = 0;
Tom Tsou8ee2f382016-03-06 20:57:34 -0800771
772 /* Random bits */
Alexander Chemeris5e65b532017-03-24 23:24:22 -0700773 for (; i < 60; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700774 bits[i] = rand() % 2;
Tom Tsou8ee2f382016-03-06 20:57:34 -0800775
Alexander Chemeris5e65b532017-03-24 23:24:22 -0700776 /* Stealing bit */
Tom Tsou7278a872017-06-14 14:50:39 -0700777 bits[i++] = 0;
Alexander Chemeris5e65b532017-03-24 23:24:22 -0700778
Tom Tsou8ee2f382016-03-06 20:57:34 -0800779 /* Training sequence */
780 for (int n = 0; i < 87; i++, n++)
Tom Tsou7278a872017-06-14 14:50:39 -0700781 bits[i] = gTrainingSequence[tsc][n];
Tom Tsou8ee2f382016-03-06 20:57:34 -0800782
Alexander Chemeris5e65b532017-03-24 23:24:22 -0700783 /* Stealing bit */
Tom Tsou7278a872017-06-14 14:50:39 -0700784 bits[i++] = 0;
Alexander Chemeris5e65b532017-03-24 23:24:22 -0700785
Tom Tsou8ee2f382016-03-06 20:57:34 -0800786 /* Random bits */
Alexander Chemeris5e65b532017-03-24 23:24:22 -0700787 for (; i < 145; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700788 bits[i] = rand() % 2;
Tom Tsou8ee2f382016-03-06 20:57:34 -0800789
790 /* Tail bits */
791 for (; i < 148; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700792 bits[i] = 0;
Tom Tsou8ee2f382016-03-06 20:57:34 -0800793
794 int guard = 8 + !(tn % 4);
Tom Tsou7278a872017-06-14 14:50:39 -0700795 return modulateBurst(bits, guard, sps);
Tom Tsou8ee2f382016-03-06 20:57:34 -0800796}
797
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300798/*
799 * Generate a random GSM access burst.
800 */
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300801signalVector *genRandAccessBurst(int delay, int sps, int tn)
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300802{
803 if ((tn < 0) || (tn > 7))
804 return NULL;
805 if ((sps != 1) && (sps != 4))
806 return NULL;
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300807 if (delay > 68)
808 return NULL;
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300809
810 int i = 0;
Tom Tsou7278a872017-06-14 14:50:39 -0700811 BitVector bits(88 + delay);
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300812
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300813 /* delay */
814 for (; i < delay; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700815 bits[i] = 0;
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300816
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300817 /* head and synch bits */
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300818 for (int n = 0; i < 49+delay; i++, n++)
Tom Tsou7278a872017-06-14 14:50:39 -0700819 bits[i] = gRACHBurst[n];
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300820
821 /* Random bits */
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300822 for (; i < 85+delay; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700823 bits[i] = rand() % 2;
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300824
825 /* Tail bits */
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300826 for (; i < 88+delay; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700827 bits[i] = 0;
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300828
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300829 int guard = 68-delay + !(tn % 4);
Tom Tsou7278a872017-06-14 14:50:39 -0700830 return modulateBurst(bits, guard, sps);
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300831}
832
Tom Tsou8ee2f382016-03-06 20:57:34 -0800833signalVector *generateEmptyBurst(int sps, int tn)
834{
835 if ((tn < 0) || (tn > 7))
836 return NULL;
837
838 if (sps == 4)
839 return new signalVector(625);
840 else if (sps == 1)
841 return new signalVector(148 + 8 + !(tn % 4));
842 else
843 return NULL;
844}
845
846signalVector *generateDummyBurst(int sps, int tn)
847{
848 if (((sps != 1) && (sps != 4)) || (tn < 0) || (tn > 7))
849 return NULL;
850
851 return modulateBurst(gDummyBurst, 8 + !(tn % 4), sps);
852}
853
854/*
Tom Tsoud3253432016-03-06 03:08:01 -0800855 * Generate a random 8-PSK EDGE burst. Only 4 SPS is supported with
856 * the returned burst being 625 samples in length.
857 */
858signalVector *generateEdgeBurst(int tsc)
859{
860 int tail = 9 / 3;
861 int data = 174 / 3;
862 int train = 78 / 3;
863
864 if ((tsc < 0) || (tsc > 7))
865 return NULL;
866
Tom Tsou7278a872017-06-14 14:50:39 -0700867 signalVector burst(148);
Tom Tsoud3253432016-03-06 03:08:01 -0800868 const BitVector *midamble = &gEdgeTrainingSequence[tsc];
869
870 /* Tail */
871 int n, i = 0;
872 for (; i < tail; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700873 burst[i] = psk8_table[7];
Tom Tsoud3253432016-03-06 03:08:01 -0800874
875 /* Body */
876 for (; i < tail + data; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700877 burst[i] = psk8_table[rand() % 8];
Tom Tsoud3253432016-03-06 03:08:01 -0800878
879 /* TSC */
880 for (n = 0; i < tail + data + train; i++, n++) {
881 unsigned index = (((unsigned) (*midamble)[3 * n + 0] & 0x01) << 0) |
882 (((unsigned) (*midamble)[3 * n + 1] & 0x01) << 1) |
883 (((unsigned) (*midamble)[3 * n + 2] & 0x01) << 2);
884
Tom Tsou7278a872017-06-14 14:50:39 -0700885 burst[i] = psk8_table[index];
Tom Tsoud3253432016-03-06 03:08:01 -0800886 }
887
888 /* Body */
889 for (; i < tail + data + train + data; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700890 burst[i] = psk8_table[rand() % 8];
Tom Tsoud3253432016-03-06 03:08:01 -0800891
892 /* Tail */
893 for (; i < tail + data + train + data + tail; i++)
Tom Tsou7278a872017-06-14 14:50:39 -0700894 burst[i] = psk8_table[7];
Tom Tsoud3253432016-03-06 03:08:01 -0800895
Tom Tsou7278a872017-06-14 14:50:39 -0700896 return shapeEdgeBurst(burst);
Tom Tsoud3253432016-03-06 03:08:01 -0800897}
898
899/*
900 * Modulate 8-PSK burst. When empty pulse shaping (rotation only)
901 * is enabled, the output vector length will be bit sequence length
902 * times the SPS value. When pulse shaping is enabled, the output
Alexander Chemeris9270a5a2017-03-17 13:03:41 -0700903 * vector length is fixed at 625 samples (156.25 symbols at 4 SPS).
Tom Tsoud3253432016-03-06 03:08:01 -0800904 * Pulse shaped bit sequences that go beyond one burst are truncated.
905 * Pulse shaping at anything but 4 SPS is not supported.
906 */
907signalVector *modulateEdgeBurst(const BitVector &bits,
908 int sps, bool empty)
909{
910 signalVector *shape, *burst;
911
912 if ((sps != 4) && !empty)
913 return NULL;
914
915 burst = mapEdgeSymbols(bits);
916 if (!burst)
917 return NULL;
918
919 if (empty)
920 shape = rotateEdgeBurst(*burst, sps);
921 else
922 shape = shapeEdgeBurst(*burst);
923
924 delete burst;
925 return shape;
926}
927
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800928static signalVector *modulateBurstBasic(const BitVector &bits,
929 int guard_len, int sps)
930{
931 int burst_len;
Tom Tsou7278a872017-06-14 14:50:39 -0700932 signalVector *pulse;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800933 signalVector::iterator burst_itr;
934
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400935 if (sps == 1)
936 pulse = GSMPulse1->c0;
937 else
Tom Tsou2079a3c2016-03-06 00:58:56 -0800938 pulse = GSMPulse4->c0;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400939
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800940 burst_len = sps * (bits.size() + guard_len);
941
Tom Tsou7278a872017-06-14 14:50:39 -0700942 signalVector burst(burst_len, pulse->size());
943 burst.isReal(true);
944 burst_itr = burst.begin();
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800945
946 /* Raw bits are not differentially encoded */
947 for (unsigned i = 0; i < bits.size(); i++) {
948 *burst_itr = 2.0 * (bits[i] & 0x01) - 1.0;
949 burst_itr += sps;
950 }
951
Tom Tsou7278a872017-06-14 14:50:39 -0700952 GMSKRotate(burst, sps);
953 burst.isReal(false);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800954
955 /* Single Gaussian pulse approximation shaping */
Tom Tsou7278a872017-06-14 14:50:39 -0700956 return convolve(&burst, pulse, NULL, START_ONLY);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800957}
958
Thomas Tsou3eaae802013-08-20 19:31:14 -0400959/* Assume input bits are not differentially encoded */
Thomas Tsou83e06892013-08-20 16:10:01 -0400960signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength,
961 int sps, bool emptyPulse)
dburgessb3a0ca42011-10-12 07:44:40 +0000962{
Thomas Tsou83e06892013-08-20 16:10:01 -0400963 if (emptyPulse)
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800964 return rotateBurst(wBurst, guardPeriodLength, sps);
965 else if (sps == 4)
Tom Tsou4dfd64a2016-03-06 20:31:51 -0800966 return modulateBurstLaurent(wBurst);
Thomas Tsou83e06892013-08-20 16:10:01 -0400967 else
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800968 return modulateBurstBasic(wBurst, guardPeriodLength, sps);
dburgessb3a0ca42011-10-12 07:44:40 +0000969}
970
Tom Tsou2079a3c2016-03-06 00:58:56 -0800971static void generateSincTable()
Thomas Tsou0e0e1f42013-11-09 22:08:51 -0500972{
Thomas Tsou0e0e1f42013-11-09 22:08:51 -0500973 for (int i = 0; i < TABLESIZE; i++) {
Tom Tsoua3dce852017-06-16 17:14:31 -0700974 auto x = (double) i / TABLESIZE * 8 * M_PI;
975 auto y = sin(x) / x;
Tom Tsou35474132017-06-19 16:00:34 -0700976 sincTable[i] = std::isnan(y) ? 1.0 : y;
Thomas Tsou0e0e1f42013-11-09 22:08:51 -0500977 }
978}
979
Tom Tsou70134a02017-06-12 14:23:53 -0700980static float sinc(float x)
dburgessb3a0ca42011-10-12 07:44:40 +0000981{
Thomas Tsou0e0e1f42013-11-09 22:08:51 -0500982 if (fabs(x) >= 8 * M_PI)
983 return 0.0;
984
985 int index = (int) floorf(fabs(x) / (8 * M_PI) * TABLESIZE);
986
987 return sincTable[index];
dburgessb3a0ca42011-10-12 07:44:40 +0000988}
989
Thomas Tsouf79c4d02013-11-09 15:51:56 -0600990/*
991 * Create fractional delay filterbank with Blackman-harris windowed
992 * sinc function generator. The number of filters generated is specified
993 * by the DELAYFILTS value.
994 */
Tom Tsou70134a02017-06-12 14:23:53 -0700995static void generateDelayFilters()
Thomas Tsouf79c4d02013-11-09 15:51:56 -0600996{
997 int h_len = 20;
998 complex *data;
999 signalVector *h;
1000 signalVector::iterator itr;
1001
1002 float k, sum;
1003 float a0 = 0.35875;
1004 float a1 = 0.48829;
1005 float a2 = 0.14128;
1006 float a3 = 0.01168;
1007
1008 for (int i = 0; i < DELAYFILTS; i++) {
1009 data = (complex *) convolve_h_alloc(h_len);
Pau Espin Pedrolf7331762018-12-03 17:46:04 +01001010 h = new signalVector(data, 0, h_len, convolve_h_alloc, free);
Thomas Tsouf79c4d02013-11-09 15:51:56 -06001011 h->setAligned(true);
1012 h->isReal(true);
1013
1014 sum = 0.0;
1015 itr = h->end();
1016 for (int n = 0; n < h_len; n++) {
1017 k = (float) n;
1018 *--itr = (complex) sinc(M_PI_F *
1019 (k - (float) h_len / 2.0 - (float) i / DELAYFILTS));
1020 *itr *= a0 -
1021 a1 * cos(2 * M_PI * n / (h_len - 1)) +
1022 a2 * cos(4 * M_PI * n / (h_len - 1)) -
1023 a3 * cos(6 * M_PI * n / (h_len - 1));
1024
1025 sum += itr->real();
1026 }
1027
1028 itr = h->begin();
1029 for (int n = 0; n < h_len; n++)
1030 *itr++ /= sum;
1031
1032 delayFilters[i] = h;
1033 }
1034}
1035
Alexander Chemerise0c12182017-03-18 13:27:48 -07001036signalVector *delayVector(const signalVector *in, signalVector *out, float delay)
dburgessb3a0ca42011-10-12 07:44:40 +00001037{
Thomas Tsouf79c4d02013-11-09 15:51:56 -06001038 int whole, index;
Thomas Tsou2c282f52013-10-08 21:34:35 -04001039 float frac;
Thomas Tsou94edaae2013-11-09 22:19:19 -05001040 signalVector *h, *shift, *fshift = NULL;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001041
Thomas Tsou2c282f52013-10-08 21:34:35 -04001042 whole = floor(delay);
1043 frac = delay - whole;
1044
1045 /* Sinc interpolated fractional shift (if allowable) */
1046 if (fabs(frac) > 1e-2) {
Thomas Tsouf79c4d02013-11-09 15:51:56 -06001047 index = floorf(frac * (float) DELAYFILTS);
1048 h = delayFilters[index];
Thomas Tsou2c282f52013-10-08 21:34:35 -04001049
Thomas Tsou94edaae2013-11-09 22:19:19 -05001050 fshift = convolve(in, h, NULL, NO_DELAY);
1051 if (!fshift)
1052 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +00001053 }
1054
Thomas Tsou94edaae2013-11-09 22:19:19 -05001055 if (!fshift)
1056 shift = new signalVector(*in);
1057 else
1058 shift = fshift;
1059
Thomas Tsou2c282f52013-10-08 21:34:35 -04001060 /* Integer sample shift */
1061 if (whole < 0) {
1062 whole = -whole;
Thomas Tsou94edaae2013-11-09 22:19:19 -05001063 signalVector::iterator wBurstItr = shift->begin();
1064 signalVector::iterator shiftedItr = shift->begin() + whole;
Thomas Tsou2c282f52013-10-08 21:34:35 -04001065
Thomas Tsou94edaae2013-11-09 22:19:19 -05001066 while (shiftedItr < shift->end())
dburgessb3a0ca42011-10-12 07:44:40 +00001067 *wBurstItr++ = *shiftedItr++;
Thomas Tsou2c282f52013-10-08 21:34:35 -04001068
Thomas Tsou94edaae2013-11-09 22:19:19 -05001069 while (wBurstItr < shift->end())
1070 *wBurstItr++ = 0.0;
1071 } else if (whole >= 0) {
1072 signalVector::iterator wBurstItr = shift->end() - 1;
1073 signalVector::iterator shiftedItr = shift->end() - 1 - whole;
1074
1075 while (shiftedItr >= shift->begin())
dburgessb3a0ca42011-10-12 07:44:40 +00001076 *wBurstItr-- = *shiftedItr--;
Thomas Tsou94edaae2013-11-09 22:19:19 -05001077
1078 while (wBurstItr >= shift->begin())
dburgessb3a0ca42011-10-12 07:44:40 +00001079 *wBurstItr-- = 0.0;
1080 }
Thomas Tsou2c282f52013-10-08 21:34:35 -04001081
Thomas Tsou94edaae2013-11-09 22:19:19 -05001082 if (!out)
1083 return shift;
1084
1085 out->clone(*shift);
1086 delete shift;
1087 return out;
dburgessb3a0ca42011-10-12 07:44:40 +00001088}
Thomas Tsou2c282f52013-10-08 21:34:35 -04001089
Tom Tsou70134a02017-06-12 14:23:53 -07001090static complex interpolatePoint(const signalVector &inSig, float ix)
dburgessb3a0ca42011-10-12 07:44:40 +00001091{
dburgessb3a0ca42011-10-12 07:44:40 +00001092 int start = (int) (floor(ix) - 10);
1093 if (start < 0) start = 0;
1094 int end = (int) (floor(ix) + 11);
1095 if ((unsigned) end > inSig.size()-1) end = inSig.size()-1;
Pau Espin Pedrol21ce05c2018-08-30 20:47:20 +02001096
dburgessb3a0ca42011-10-12 07:44:40 +00001097 complex pVal = 0.0;
Thomas Tsou20eb6d62013-11-09 14:30:41 -05001098 if (!inSig.isReal()) {
Pau Espin Pedrol21ce05c2018-08-30 20:47:20 +02001099 for (int i = start; i < end; i++)
dburgessb3a0ca42011-10-12 07:44:40 +00001100 pVal += inSig[i] * sinc(M_PI_F*(i-ix));
1101 }
1102 else {
Pau Espin Pedrol21ce05c2018-08-30 20:47:20 +02001103 for (int i = start; i < end; i++)
dburgessb3a0ca42011-10-12 07:44:40 +00001104 pVal += inSig[i].real() * sinc(M_PI_F*(i-ix));
1105 }
Pau Espin Pedrol21ce05c2018-08-30 20:47:20 +02001106
dburgessb3a0ca42011-10-12 07:44:40 +00001107 return pVal;
1108}
1109
Thomas Tsou8181b012013-08-20 21:17:19 -04001110static complex fastPeakDetect(const signalVector &rxBurst, float *index)
1111{
1112 float val, max = 0.0f;
1113 complex amp;
1114 int _index = -1;
1115
Thomas Tsou3f32ab52013-11-15 16:32:54 -05001116 for (size_t i = 0; i < rxBurst.size(); i++) {
Thomas Tsou8181b012013-08-20 21:17:19 -04001117 val = rxBurst[i].norm2();
1118 if (val > max) {
1119 max = val;
1120 _index = i;
1121 amp = rxBurst[i];
1122 }
1123 }
1124
1125 if (index)
1126 *index = (float) _index;
1127
1128 return amp;
1129}
1130
Tom Tsou70134a02017-06-12 14:23:53 -07001131static complex peakDetect(const signalVector &rxBurst,
1132 float *peakIndex, float *avgPwr)
dburgessb3a0ca42011-10-12 07:44:40 +00001133{
dburgessb3a0ca42011-10-12 07:44:40 +00001134 complex maxVal = 0.0;
1135 float maxIndex = -1;
1136 float sumPower = 0.0;
1137
1138 for (unsigned int i = 0; i < rxBurst.size(); i++) {
1139 float samplePower = rxBurst[i].norm2();
1140 if (samplePower > maxVal.real()) {
1141 maxVal = samplePower;
1142 maxIndex = i;
1143 }
1144 sumPower += samplePower;
1145 }
1146
1147 // interpolate around the peak
1148 // to save computation, we'll use early-late balancing
1149 float earlyIndex = maxIndex-1;
1150 float lateIndex = maxIndex+1;
Pau Espin Pedrol21ce05c2018-08-30 20:47:20 +02001151
dburgessb3a0ca42011-10-12 07:44:40 +00001152 float incr = 0.5;
1153 while (incr > 1.0/1024.0) {
1154 complex earlyP = interpolatePoint(rxBurst,earlyIndex);
1155 complex lateP = interpolatePoint(rxBurst,lateIndex);
Pau Espin Pedrol21ce05c2018-08-30 20:47:20 +02001156 if (earlyP < lateP)
dburgessb3a0ca42011-10-12 07:44:40 +00001157 earlyIndex += incr;
1158 else if (earlyP > lateP)
1159 earlyIndex -= incr;
1160 else break;
1161 incr /= 2.0;
1162 lateIndex = earlyIndex + 2.0;
1163 }
1164
1165 maxIndex = earlyIndex + 1.0;
1166 maxVal = interpolatePoint(rxBurst,maxIndex);
1167
1168 if (peakIndex!=NULL)
1169 *peakIndex = maxIndex;
1170
1171 if (avgPwr!=NULL)
1172 *avgPwr = (sumPower-maxVal.norm2()) / (rxBurst.size()-1);
1173
1174 return maxVal;
1175
1176}
1177
1178void scaleVector(signalVector &x,
1179 complex scale)
1180{
Thomas Tsou7e4e5362013-10-30 21:18:55 -04001181#ifdef HAVE_NEON
1182 int len = x.size();
1183
1184 scale_complex((float *) x.begin(),
1185 (float *) x.begin(),
1186 (float *) &scale, len);
1187#else
dburgessb3a0ca42011-10-12 07:44:40 +00001188 signalVector::iterator xP = x.begin();
1189 signalVector::iterator xPEnd = x.end();
Thomas Tsou20eb6d62013-11-09 14:30:41 -05001190 if (!x.isReal()) {
dburgessb3a0ca42011-10-12 07:44:40 +00001191 while (xP < xPEnd) {
1192 *xP = *xP * scale;
1193 xP++;
1194 }
1195 }
1196 else {
1197 while (xP < xPEnd) {
1198 *xP = xP->real() * scale;
1199 xP++;
1200 }
1201 }
Thomas Tsou7e4e5362013-10-30 21:18:55 -04001202#endif
dburgessb3a0ca42011-10-12 07:44:40 +00001203}
1204
1205/** in-place conjugation */
Tom Tsou70134a02017-06-12 14:23:53 -07001206static void conjugateVector(signalVector &x)
dburgessb3a0ca42011-10-12 07:44:40 +00001207{
Thomas Tsou20eb6d62013-11-09 14:30:41 -05001208 if (x.isReal()) return;
dburgessb3a0ca42011-10-12 07:44:40 +00001209 signalVector::iterator xP = x.begin();
1210 signalVector::iterator xPEnd = x.end();
1211 while (xP < xPEnd) {
1212 *xP = xP->conj();
1213 xP++;
1214 }
1215}
1216
Tom Tsou2079a3c2016-03-06 00:58:56 -08001217static bool generateMidamble(int sps, int tsc)
dburgessb3a0ca42011-10-12 07:44:40 +00001218{
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001219 bool status = true;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001220 float toa;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001221 complex *data = NULL;
1222 signalVector *autocorr = NULL, *midamble = NULL;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001223 signalVector *midMidamble = NULL, *_midMidamble = NULL;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001224
Thomas Tsou3eaae802013-08-20 19:31:14 -04001225 if ((tsc < 0) || (tsc > 7))
dburgessb3a0ca42011-10-12 07:44:40 +00001226 return false;
1227
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001228 delete gMidambles[tsc];
Thomas Tsou3eaae802013-08-20 19:31:14 -04001229
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001230 /* Use middle 16 bits of each TSC. Correlation sequence is not pulse shaped */
1231 midMidamble = modulateBurst(gTrainingSequence[tsc].segment(5,16), 0, sps, true);
1232 if (!midMidamble)
1233 return false;
1234
Thomas Tsou3eaae802013-08-20 19:31:14 -04001235 /* Simulated receive sequence is pulse shaped */
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001236 midamble = modulateBurst(gTrainingSequence[tsc], 0, sps, false);
1237 if (!midamble) {
1238 status = false;
1239 goto release;
dburgessb3a0ca42011-10-12 07:44:40 +00001240 }
Thomas Tsou3eaae802013-08-20 19:31:14 -04001241
dburgessb3a0ca42011-10-12 07:44:40 +00001242 // NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst,
1243 // the ideal TSC has an + 180 degree phase shift,
Pau Espin Pedrol21ce05c2018-08-30 20:47:20 +02001244 // due to the pi/2 frequency shift, that
dburgessb3a0ca42011-10-12 07:44:40 +00001245 // needs to be accounted for.
1246 // 26-midamble is 61 symbols into burst, has +90 degree phase shift.
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001247 scaleVector(*midMidamble, complex(-1.0, 0.0));
1248 scaleVector(*midamble, complex(0.0, 1.0));
dburgessb3a0ca42011-10-12 07:44:40 +00001249
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001250 conjugateVector(*midMidamble);
dburgessb3a0ca42011-10-12 07:44:40 +00001251
Thomas Tsou3eaae802013-08-20 19:31:14 -04001252 /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
1253 data = (complex *) convolve_h_alloc(midMidamble->size());
Pau Espin Pedrolf7331762018-12-03 17:46:04 +01001254 _midMidamble = new signalVector(data, 0, midMidamble->size(), convolve_h_alloc, free);
Thomas Tsou3eaae802013-08-20 19:31:14 -04001255 _midMidamble->setAligned(true);
Pau Espin Pedrol5e68cde2018-08-30 20:45:14 +02001256 midMidamble->copyTo(*_midMidamble);
Thomas Tsou3eaae802013-08-20 19:31:14 -04001257
1258 autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY);
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001259 if (!autocorr) {
1260 status = false;
1261 goto release;
1262 }
dburgessb3a0ca42011-10-12 07:44:40 +00001263
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001264 gMidambles[tsc] = new CorrelationSequence;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001265 gMidambles[tsc]->sequence = _midMidamble;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001266 gMidambles[tsc]->gain = peakDetect(*autocorr, &toa, NULL);
1267
1268 /* For 1 sps only
1269 * (Half of correlation length - 1) + midpoint of pulse shape + remainder
1270 * 13.5 = (16 / 2 - 1) + 1.5 + (26 - 10) / 2
1271 */
1272 if (sps == 1)
1273 gMidambles[tsc]->toa = toa - 13.5;
1274 else
1275 gMidambles[tsc]->toa = 0;
dburgessb3a0ca42011-10-12 07:44:40 +00001276
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001277release:
dburgessb3a0ca42011-10-12 07:44:40 +00001278 delete autocorr;
1279 delete midamble;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001280 delete midMidamble;
dburgessb3a0ca42011-10-12 07:44:40 +00001281
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001282 if (!status) {
Thomas Tsou3eaae802013-08-20 19:31:14 -04001283 delete _midMidamble;
1284 free(data);
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001285 gMidambles[tsc] = NULL;
1286 }
1287
1288 return status;
dburgessb3a0ca42011-10-12 07:44:40 +00001289}
1290
Tom Tsou70134a02017-06-12 14:23:53 -07001291static CorrelationSequence *generateEdgeMidamble(int tsc)
Tom Tsoud3253432016-03-06 03:08:01 -08001292{
1293 complex *data = NULL;
1294 signalVector *midamble = NULL, *_midamble = NULL;
1295 CorrelationSequence *seq;
1296
1297 if ((tsc < 0) || (tsc > 7))
1298 return NULL;
1299
1300 /* Use middle 48 bits of each TSC. Correlation sequence is not pulse shaped */
1301 const BitVector *bits = &gEdgeTrainingSequence[tsc];
1302 midamble = modulateEdgeBurst(bits->segment(15, 48), 1, true);
1303 if (!midamble)
1304 return NULL;
1305
1306 conjugateVector(*midamble);
1307
1308 data = (complex *) convolve_h_alloc(midamble->size());
Pau Espin Pedrolf7331762018-12-03 17:46:04 +01001309 _midamble = new signalVector(data, 0, midamble->size(), convolve_h_alloc, free);
Tom Tsoud3253432016-03-06 03:08:01 -08001310 _midamble->setAligned(true);
Pau Espin Pedrol5e68cde2018-08-30 20:45:14 +02001311 midamble->copyTo(*_midamble);
Tom Tsoud3253432016-03-06 03:08:01 -08001312
1313 /* Channel gain is an empirically measured value */
1314 seq = new CorrelationSequence;
Tom Tsoud3253432016-03-06 03:08:01 -08001315 seq->sequence = _midamble;
1316 seq->gain = Complex<float>(-19.6432, 19.5006) / 1.18;
1317 seq->toa = 0;
1318
1319 delete midamble;
1320
1321 return seq;
1322}
1323
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001324static bool generateRACHSequence(CorrelationSequence **seq, const BitVector &bv, int sps)
dburgessb3a0ca42011-10-12 07:44:40 +00001325{
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001326 bool status = true;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001327 float toa;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001328 complex *data = NULL;
1329 signalVector *autocorr = NULL;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001330 signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001331
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001332 if (*seq != NULL)
1333 delete *seq;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001334
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001335 seq0 = modulateBurst(bv, 0, sps, false);
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001336 if (!seq0)
1337 return false;
1338
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001339 seq1 = modulateBurst(bv.segment(0, 40), 0, sps, true);
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001340 if (!seq1) {
1341 status = false;
1342 goto release;
dburgessb3a0ca42011-10-12 07:44:40 +00001343 }
1344
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001345 conjugateVector(*seq1);
dburgessb3a0ca42011-10-12 07:44:40 +00001346
Thomas Tsou3eaae802013-08-20 19:31:14 -04001347 /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
1348 data = (complex *) convolve_h_alloc(seq1->size());
Pau Espin Pedrolf7331762018-12-03 17:46:04 +01001349 _seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free);
Thomas Tsou3eaae802013-08-20 19:31:14 -04001350 _seq1->setAligned(true);
Pau Espin Pedrol5e68cde2018-08-30 20:45:14 +02001351 seq1->copyTo(*_seq1);
Thomas Tsou3eaae802013-08-20 19:31:14 -04001352
1353 autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
1354 if (!autocorr) {
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001355 status = false;
1356 goto release;
1357 }
dburgessb3a0ca42011-10-12 07:44:40 +00001358
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001359 *seq = new CorrelationSequence;
1360 (*seq)->sequence = _seq1;
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001361 (*seq)->gain = peakDetect(*autocorr, &toa, NULL);
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001362
1363 /* For 1 sps only
1364 * (Half of correlation length - 1) + midpoint of pulse shaping filer
1365 * 20.5 = (40 / 2 - 1) + 1.5
1366 */
1367 if (sps == 1)
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001368 (*seq)->toa = toa - 20.5;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001369 else
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001370 (*seq)->toa = 0.0;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001371
1372release:
dburgessb3a0ca42011-10-12 07:44:40 +00001373 delete autocorr;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001374 delete seq0;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001375 delete seq1;
dburgessb3a0ca42011-10-12 07:44:40 +00001376
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001377 if (!status) {
Thomas Tsou3eaae802013-08-20 19:31:14 -04001378 delete _seq1;
1379 free(data);
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001380 *seq = NULL;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001381 }
dburgessb3a0ca42011-10-12 07:44:40 +00001382
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001383 return status;
dburgessb3a0ca42011-10-12 07:44:40 +00001384}
Thomas Tsou3eaae802013-08-20 19:31:14 -04001385
Tom Tsoua84e1622016-06-29 14:50:25 -07001386/*
1387 * Peak-to-average computation +/- range from peak in symbols
1388 */
1389#define COMPUTE_PEAK_MIN 2
1390#define COMPUTE_PEAK_MAX 5
1391
1392/*
1393 * Minimum number of values needed to compute peak-to-average
1394 */
1395#define COMPUTE_PEAK_CNT 5
1396
Thomas Tsou865bca42013-08-21 20:58:00 -04001397static float computePeakRatio(signalVector *corr,
1398 int sps, float toa, complex amp)
dburgessb3a0ca42011-10-12 07:44:40 +00001399{
Thomas Tsou865bca42013-08-21 20:58:00 -04001400 int num = 0;
1401 complex *peak;
1402 float rms, avg = 0.0;
dburgessb3a0ca42011-10-12 07:44:40 +00001403
Thomas Tsou865bca42013-08-21 20:58:00 -04001404 /* Check for bogus results */
1405 if ((toa < 0.0) || (toa > corr->size()))
1406 return 0.0;
dburgessb3a0ca42011-10-12 07:44:40 +00001407
Alexander Chemeris1e9b4d52015-06-04 19:05:28 -04001408 peak = corr->begin() + (int) rint(toa);
1409
Tom Tsoua84e1622016-06-29 14:50:25 -07001410 for (int i = COMPUTE_PEAK_MIN * sps; i <= COMPUTE_PEAK_MAX * sps; i++) {
Thomas Tsou865bca42013-08-21 20:58:00 -04001411 if (peak - i >= corr->begin()) {
Thomas Tsou3eaae802013-08-20 19:31:14 -04001412 avg += (peak - i)->norm2();
1413 num++;
1414 }
Thomas Tsou865bca42013-08-21 20:58:00 -04001415 if (peak + i < corr->end()) {
Thomas Tsou3eaae802013-08-20 19:31:14 -04001416 avg += (peak + i)->norm2();
1417 num++;
1418 }
dburgessb3a0ca42011-10-12 07:44:40 +00001419 }
1420
Tom Tsoua84e1622016-06-29 14:50:25 -07001421 if (num < COMPUTE_PEAK_CNT)
Thomas Tsou865bca42013-08-21 20:58:00 -04001422 return 0.0;
dburgessb3a0ca42011-10-12 07:44:40 +00001423
Thomas Tsou3eaae802013-08-20 19:31:14 -04001424 rms = sqrtf(avg / (float) num) + 0.00001;
dburgessb3a0ca42011-10-12 07:44:40 +00001425
Thomas Tsou865bca42013-08-21 20:58:00 -04001426 return (amp.abs()) / rms;
dburgessb3a0ca42011-10-12 07:44:40 +00001427}
1428
Alexander Chemeris1470fcd2017-03-17 22:35:02 -07001429float energyDetect(const signalVector &rxBurst, unsigned windowLength)
dburgessb3a0ca42011-10-12 07:44:40 +00001430{
1431
1432 signalVector::const_iterator windowItr = rxBurst.begin(); //+rxBurst.size()/2 - 5*windowLength/2;
1433 float energy = 0.0;
Tom Tsou2af14402017-03-23 14:54:00 -07001434 if (windowLength == 0) return 0.0;
dburgessb3a0ca42011-10-12 07:44:40 +00001435 if (windowLength > rxBurst.size()) windowLength = rxBurst.size();
1436 for (unsigned i = 0; i < windowLength; i++) {
1437 energy += windowItr->norm2();
1438 windowItr+=4;
1439 }
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +03001440 return energy/windowLength;
dburgessb3a0ca42011-10-12 07:44:40 +00001441}
dburgessb3a0ca42011-10-12 07:44:40 +00001442
Tom Tsou70134a02017-06-12 14:23:53 -07001443static signalVector *downsampleBurst(const signalVector &burst)
1444{
Tom Tsou7278a872017-06-14 14:50:39 -07001445 signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len());
1446 signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN);
Pau Espin Pedrol5e68cde2018-08-30 20:45:14 +02001447 burst.copyToSegment(in, 0, DOWNSAMPLE_IN_LEN);
Tom Tsou70134a02017-06-12 14:23:53 -07001448
Tom Tsou7278a872017-06-14 14:50:39 -07001449 if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN,
Tom Tsou70134a02017-06-12 14:23:53 -07001450 (float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) {
1451 delete out;
1452 out = NULL;
1453 }
1454
Tom Tsou70134a02017-06-12 14:23:53 -07001455 return out;
1456};
1457
Thomas Tsou865bca42013-08-21 20:58:00 -04001458/*
Sylvain Munautb49a42e2019-05-14 18:23:29 +02001459 * Computes C/I (Carrier-to-Interference ratio) in dB (deciBels).
1460 * It is computed from the training sequence of each received burst,
1461 * by comparing the "ideal" training sequence with the actual one.
1462 */
1463static float computeCI(const signalVector *burst, CorrelationSequence *sync,
1464 float toa, int start, complex xcorr)
1465{
1466 float S, C;
1467 int ps;
1468
1469 /* Integer position where the sequence starts */
1470 ps = start + 1 - sync->sequence->size() + (int)roundf(toa);
1471
1472 /* Estimate Signal power */
1473 S = 0.0f;
1474 for (int i=0, j=ps; i<(int)sync->sequence->size(); i++,j++)
1475 S += (*burst)[j].norm2();
1476 S /= sync->sequence->size();
1477
1478 /* Esimate Carrier power */
1479 C = xcorr.norm2() / ((sync->sequence->size() - 1) * sync->gain.abs());
1480
1481 /* Interference = Signal - Carrier, so C/I = C / (S - C) */
1482 return 3.0103f * log2f(C / (S - C));
1483}
1484
1485/*
Thomas Tsou865bca42013-08-21 20:58:00 -04001486 * Detect a burst based on correlation and peak-to-average ratio
1487 *
1488 * For one sampler-per-symbol, perform fast peak detection (no interpolation)
1489 * for initial gating. We do this because energy detection should be disabled.
1490 * For higher oversampling values, we assume the energy detector is in place
1491 * and we run full interpolating peak detection.
1492 */
Alexander Chemeris1470fcd2017-03-17 22:35:02 -07001493static int detectBurst(const signalVector &burst,
Thomas Tsou865bca42013-08-21 20:58:00 -04001494 signalVector &corr, CorrelationSequence *sync,
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001495 float thresh, int sps, int start, int len,
1496 struct estim_burst_params *ebp)
dburgessb3a0ca42011-10-12 07:44:40 +00001497{
Alexander Chemeris1470fcd2017-03-17 22:35:02 -07001498 const signalVector *corr_in;
1499 signalVector *dec = NULL;
Sylvain Munautb49a42e2019-05-14 18:23:29 +02001500 complex xcorr;
1501 int rc = 1;
Tom Tsoud3253432016-03-06 03:08:01 -08001502
1503 if (sps == 4) {
1504 dec = downsampleBurst(burst);
1505 corr_in = dec;
1506 sps = 1;
1507 } else {
1508 corr_in = &burst;
1509 }
1510
Thomas Tsou865bca42013-08-21 20:58:00 -04001511 /* Correlate */
Tom Tsoud3253432016-03-06 03:08:01 -08001512 if (!convolve(corr_in, sync->sequence, &corr,
Sylvain Munauta3934a12018-12-20 19:10:26 +01001513 CUSTOM, start, len)) {
Sylvain Munautb49a42e2019-05-14 18:23:29 +02001514 rc = -1;
1515 goto del_ret;
dburgessb3a0ca42011-10-12 07:44:40 +00001516 }
1517
Tom Tsoud3253432016-03-06 03:08:01 -08001518 /* Running at the downsampled rate at this point */
1519 sps = 1;
1520
Thomas Tsou865bca42013-08-21 20:58:00 -04001521 /* Peak detection - place restrictions at correlation edges */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001522 ebp->amp = fastPeakDetect(corr, &ebp->toa);
Thomas Tsou3eaae802013-08-20 19:31:14 -04001523
Sylvain Munautb49a42e2019-05-14 18:23:29 +02001524 if ((ebp->toa < 3 * sps) || (ebp->toa > len - 3 * sps)) {
1525 rc = 0;
1526 goto del_ret;
1527 }
Thomas Tsou3eaae802013-08-20 19:31:14 -04001528
Sylvain Munautb49a42e2019-05-14 18:23:29 +02001529 /* Peak-to-average ratio */
1530 if (computePeakRatio(&corr, sps, ebp->toa, ebp->amp) < thresh) {
1531 rc = 0;
1532 goto del_ret;
1533 }
Thomas Tsou865bca42013-08-21 20:58:00 -04001534
Sylvain Munautb49a42e2019-05-14 18:23:29 +02001535 /* Refine TOA and correlation value */
1536 xcorr = peakDetect(corr, &ebp->toa, NULL);
1537
1538 /* Compute C/I */
1539 ebp->ci = computeCI(corr_in, sync, ebp->toa, start, xcorr);
Thomas Tsou865bca42013-08-21 20:58:00 -04001540
1541 /* Normalize our channel gain */
Sylvain Munautb49a42e2019-05-14 18:23:29 +02001542 ebp->amp = xcorr / sync->gain;
Thomas Tsou865bca42013-08-21 20:58:00 -04001543
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001544 /* Compensate for residuate time lag */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001545 ebp->toa = ebp->toa - sync->toa;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001546
Sylvain Munautb49a42e2019-05-14 18:23:29 +02001547del_ret:
1548 delete dec;
1549 return rc;
Thomas Tsou865bca42013-08-21 20:58:00 -04001550}
1551
Alexander Chemeris1470fcd2017-03-17 22:35:02 -07001552static float maxAmplitude(const signalVector &burst)
Tom Tsou577cd022015-05-18 13:57:54 -07001553{
Alexander Chemeris954b1182015-06-04 15:39:41 -04001554 float max = 0.0;
1555 for (size_t i = 0; i < burst.size(); i++) {
1556 if (fabs(burst[i].real()) > max)
1557 max = fabs(burst[i].real());
1558 if (fabs(burst[i].imag()) > max)
1559 max = fabs(burst[i].imag());
1560 }
Tom Tsou577cd022015-05-18 13:57:54 -07001561
Alexander Chemeris954b1182015-06-04 15:39:41 -04001562 return max;
Tom Tsou577cd022015-05-18 13:57:54 -07001563}
1564
Alexander Chemeris130a8002015-06-09 20:52:11 -04001565/*
1566 * RACH/Normal burst detection with clipping detection
Thomas Tsou865bca42013-08-21 20:58:00 -04001567 *
1568 * Correlation window parameters:
Alexander Chemeris130a8002015-06-09 20:52:11 -04001569 * target: Tail bits + burst length
1570 * head: Search symbols before target
1571 * tail: Search symbols after target
Thomas Tsou865bca42013-08-21 20:58:00 -04001572 */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001573static int detectGeneralBurst(const signalVector &rxBurst, float thresh, int sps,
Alexander Chemeris1470fcd2017-03-17 22:35:02 -07001574 int target, int head, int tail,
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001575 CorrelationSequence *sync,
1576 struct estim_burst_params *ebp)
Thomas Tsou865bca42013-08-21 20:58:00 -04001577{
Alexander Chemeris130a8002015-06-09 20:52:11 -04001578 int rc, start, len;
Alexander Chemeris954b1182015-06-04 15:39:41 -04001579 bool clipping = false;
Thomas Tsou865bca42013-08-21 20:58:00 -04001580
1581 if ((sps != 1) && (sps != 4))
Tom Tsou577cd022015-05-18 13:57:54 -07001582 return -SIGERR_UNSUPPORTED;
1583
Alexander Chemeris954b1182015-06-04 15:39:41 -04001584 // Detect potential clipping
1585 // We still may be able to demod the burst, so we'll give it a try
1586 // and only report clipping if we can't demod.
1587 float maxAmpl = maxAmplitude(rxBurst);
1588 if (maxAmpl > CLIP_THRESH) {
1589 LOG(DEBUG) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
1590 clipping = true;
1591 }
Thomas Tsou865bca42013-08-21 20:58:00 -04001592
Tom Tsoud3253432016-03-06 03:08:01 -08001593 start = target - head - 1;
1594 len = head + tail;
Tom Tsou7278a872017-06-14 14:50:39 -07001595 signalVector corr(len);
Thomas Tsou865bca42013-08-21 20:58:00 -04001596
Tom Tsou7278a872017-06-14 14:50:39 -07001597 rc = detectBurst(rxBurst, corr, sync,
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001598 thresh, sps, start, len, ebp);
Thomas Tsou865bca42013-08-21 20:58:00 -04001599 if (rc < 0) {
Tom Tsou577cd022015-05-18 13:57:54 -07001600 return -SIGERR_INTERNAL;
Thomas Tsou865bca42013-08-21 20:58:00 -04001601 } else if (!rc) {
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001602 ebp->amp = 0.0f;
1603 ebp->toa = 0.0f;
Sylvain Munautb49a42e2019-05-14 18:23:29 +02001604 ebp->ci = 0.0f;
Alexander Chemeris954b1182015-06-04 15:39:41 -04001605 return clipping?-SIGERR_CLIP:SIGERR_NONE;
dburgessb3a0ca42011-10-12 07:44:40 +00001606 }
1607
Thomas Tsou865bca42013-08-21 20:58:00 -04001608 /* Subtract forward search bits from delay */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001609 ebp->toa -= head;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001610
Thomas Tsou865bca42013-08-21 20:58:00 -04001611 return 1;
1612}
Thomas Tsou3eaae802013-08-20 19:31:14 -04001613
Alexander Chemeris130a8002015-06-09 20:52:11 -04001614
Pau Espin Pedrol21ce05c2018-08-30 20:47:20 +02001615/*
Alexander Chemeris130a8002015-06-09 20:52:11 -04001616 * RACH burst detection
1617 *
1618 * Correlation window parameters:
1619 * target: Tail bits + RACH length (reduced from 41 to a multiple of 4)
Tom Tsoue90c24c2016-06-21 16:14:39 -07001620 * head: Search 8 symbols before target
1621 * tail: Search 8 symbols + maximum expected delay
Alexander Chemeris130a8002015-06-09 20:52:11 -04001622 */
Tom Tsou70134a02017-06-12 14:23:53 -07001623static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001624 unsigned max_toa, bool ext, struct estim_burst_params *ebp)
Alexander Chemeris130a8002015-06-09 20:52:11 -04001625{
1626 int rc, target, head, tail;
Vadim Yanitskiy444ff342018-10-22 02:25:23 +02001627 int i, num_seq;
Alexander Chemeris130a8002015-06-09 20:52:11 -04001628
1629 target = 8 + 40;
Tom Tsoue90c24c2016-06-21 16:14:39 -07001630 head = 8;
Alexander Chemeris14d13b62017-03-17 15:12:17 -07001631 tail = 8 + max_toa;
Vadim Yanitskiy444ff342018-10-22 02:25:23 +02001632 num_seq = ext ? 3 : 1;
Alexander Chemeris130a8002015-06-09 20:52:11 -04001633
Vadim Yanitskiy444ff342018-10-22 02:25:23 +02001634 for (i = 0; i < num_seq; i++) {
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001635 rc = detectGeneralBurst(burst, threshold, sps, target, head, tail,
1636 gRACHSequences[i], ebp);
Pau Espin Pedrolc3d68c12019-07-04 16:27:47 +02001637 if (rc > 0) {
1638 ebp->tsc = i;
Vadim Yanitskiy444ff342018-10-22 02:25:23 +02001639 break;
Pau Espin Pedrolc3d68c12019-07-04 16:27:47 +02001640 }
Vadim Yanitskiy444ff342018-10-22 02:25:23 +02001641 }
Alexander Chemeris130a8002015-06-09 20:52:11 -04001642
1643 return rc;
1644}
1645
Pau Espin Pedrol21ce05c2018-08-30 20:47:20 +02001646/*
Thomas Tsou865bca42013-08-21 20:58:00 -04001647 * Normal burst detection
1648 *
1649 * Correlation window parameters:
1650 * target: Tail + data + mid-midamble + 1/2 remaining midamblebits
Tom Tsoue90c24c2016-06-21 16:14:39 -07001651 * head: Search 6 symbols before target
1652 * tail: Search 6 symbols + maximum expected delay
Thomas Tsou865bca42013-08-21 20:58:00 -04001653 */
Tom Tsou70134a02017-06-12 14:23:53 -07001654static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float threshold,
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001655 int sps, unsigned max_toa, struct estim_burst_params *ebp)
Thomas Tsou865bca42013-08-21 20:58:00 -04001656{
Alexander Chemeris130a8002015-06-09 20:52:11 -04001657 int rc, target, head, tail;
Thomas Tsou865bca42013-08-21 20:58:00 -04001658 CorrelationSequence *sync;
1659
Tom Tsouae91f132017-03-28 14:40:38 -07001660 if (tsc > 7)
Tom Tsou577cd022015-05-18 13:57:54 -07001661 return -SIGERR_UNSUPPORTED;
1662
Thomas Tsou865bca42013-08-21 20:58:00 -04001663 target = 3 + 58 + 16 + 5;
Tom Tsoue90c24c2016-06-21 16:14:39 -07001664 head = 6;
1665 tail = 6 + max_toa;
Thomas Tsou865bca42013-08-21 20:58:00 -04001666 sync = gMidambles[tsc];
Thomas Tsou865bca42013-08-21 20:58:00 -04001667
Pau Espin Pedrolc3d68c12019-07-04 16:27:47 +02001668 ebp->tsc = tsc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001669 rc = detectGeneralBurst(burst, threshold, sps, target, head, tail, sync, ebp);
Alexander Chemeris130a8002015-06-09 20:52:11 -04001670 return rc;
dburgessb3a0ca42011-10-12 07:44:40 +00001671}
1672
Tom Tsou70134a02017-06-12 14:23:53 -07001673static int detectEdgeBurst(const signalVector &burst, unsigned tsc, float threshold,
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001674 int sps, unsigned max_toa, struct estim_burst_params *ebp)
Tom Tsoud3253432016-03-06 03:08:01 -08001675{
1676 int rc, target, head, tail;
1677 CorrelationSequence *sync;
1678
Tom Tsouae91f132017-03-28 14:40:38 -07001679 if (tsc > 7)
Tom Tsoud3253432016-03-06 03:08:01 -08001680 return -SIGERR_UNSUPPORTED;
1681
1682 target = 3 + 58 + 16 + 5;
Tom Tsoue90c24c2016-06-21 16:14:39 -07001683 head = 6;
1684 tail = 6 + max_toa;
Tom Tsoud3253432016-03-06 03:08:01 -08001685 sync = gEdgeMidambles[tsc];
1686
Pau Espin Pedrolc3d68c12019-07-04 16:27:47 +02001687 ebp->tsc = tsc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001688 rc = detectGeneralBurst(burst, threshold, sps,
1689 target, head, tail, sync, ebp);
Tom Tsoud3253432016-03-06 03:08:01 -08001690 return rc;
1691}
1692
Alexander Chemeris1470fcd2017-03-17 22:35:02 -07001693int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold,
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001694 int sps, CorrType type, unsigned max_toa,
1695 struct estim_burst_params *ebp)
Alexander Chemeris4e6c9382017-03-17 15:24:18 -07001696{
1697 int rc = 0;
1698
1699 switch (type) {
1700 case EDGE:
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001701 rc = detectEdgeBurst(burst, tsc, threshold, sps, max_toa, ebp);
Alexander Chemeris4e6c9382017-03-17 15:24:18 -07001702 if (rc > 0)
1703 break;
1704 else
1705 type = TSC;
1706 case TSC:
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001707 rc = analyzeTrafficBurst(burst, tsc, threshold, sps, max_toa, ebp);
Alexander Chemeris4e6c9382017-03-17 15:24:18 -07001708 break;
Vadim Yanitskiy444ff342018-10-22 02:25:23 +02001709 case EXT_RACH:
Alexander Chemeris4e6c9382017-03-17 15:24:18 -07001710 case RACH:
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +02001711 rc = detectRACHBurst(burst, threshold, sps, max_toa, type == EXT_RACH, ebp);
Alexander Chemeris4e6c9382017-03-17 15:24:18 -07001712 break;
1713 default:
1714 LOG(ERR) << "Invalid correlation type";
1715 }
1716
1717 if (rc > 0)
1718 return type;
1719
1720 return rc;
1721}
1722
Tom Tsoud3253432016-03-06 03:08:01 -08001723/*
1724 * Soft 8-PSK decoding using Manhattan distance metric
1725 */
1726static SoftVector *softSliceEdgeBurst(signalVector &burst)
1727{
1728 size_t nsyms = 148;
1729
1730 if (burst.size() < nsyms)
1731 return NULL;
1732
1733 signalVector::iterator itr;
1734 SoftVector *bits = new SoftVector(nsyms * 3);
1735
1736 /*
1737 * Bits 0 and 1 - First and second bits of the symbol respectively
1738 */
1739 rotateBurst2(burst, -M_PI / 8.0);
1740 itr = burst.begin();
1741 for (size_t i = 0; i < nsyms; i++) {
1742 (*bits)[3 * i + 0] = -itr->imag();
1743 (*bits)[3 * i + 1] = itr->real();
1744 itr++;
1745 }
1746
1747 /*
1748 * Bit 2 - Collapse symbols into quadrant 0 (positive X and Y).
1749 * Decision area is then simplified to X=Y axis. Rotate again to
1750 * place decision boundary on X-axis.
1751 */
1752 itr = burst.begin();
1753 for (size_t i = 0; i < burst.size(); i++) {
1754 burst[i] = Complex<float>(fabs(itr->real()), fabs(itr->imag()));
1755 itr++;
1756 }
1757
1758 rotateBurst2(burst, -M_PI / 4.0);
1759 itr = burst.begin();
1760 for (size_t i = 0; i < nsyms; i++) {
1761 (*bits)[3 * i + 2] = -itr->imag();
1762 itr++;
1763 }
1764
1765 signalVector soft(bits->size());
1766 for (size_t i = 0; i < bits->size(); i++)
1767 soft[i] = (*bits)[i];
1768
1769 return bits;
1770}
1771
1772/*
Alexander Chemeris132fb242017-03-17 17:22:33 -07001773 * Convert signalVector to SoftVector by taking real part of the signal.
1774 */
1775static SoftVector *signalToSoftVector(signalVector *dec)
1776{
1777 SoftVector *bits = new SoftVector(dec->size());
1778
1779 SoftVector::iterator bit_itr = bits->begin();
1780 signalVector::iterator burst_itr = dec->begin();
1781
1782 for (; burst_itr < dec->end(); burst_itr++)
1783 *bit_itr++ = burst_itr->real();
1784
1785 return bits;
1786}
1787
1788/*
Tom Tsou7fec3032016-03-06 22:33:20 -08001789 * Shared portion of GMSK and EDGE demodulators consisting of timing
1790 * recovery and single tap channel correction. For 4 SPS (if activated),
1791 * the output is downsampled prior to the 1 SPS modulation specific
1792 * stages.
1793 */
Alexander Chemerise0c12182017-03-18 13:27:48 -07001794static signalVector *demodCommon(const signalVector &burst, int sps,
Tom Tsou7fec3032016-03-06 22:33:20 -08001795 complex chan, float toa)
1796{
1797 signalVector *delay, *dec;
1798
1799 if ((sps != 1) && (sps != 4))
1800 return NULL;
1801
Tom Tsou7fec3032016-03-06 22:33:20 -08001802 delay = delayVector(&burst, NULL, -toa * (float) sps);
Alexander Chemerise0c12182017-03-18 13:27:48 -07001803 scaleVector(*delay, (complex) 1.0 / chan);
Tom Tsou7fec3032016-03-06 22:33:20 -08001804
1805 if (sps == 1)
1806 return delay;
1807
1808 dec = downsampleBurst(*delay);
1809
1810 delete delay;
1811 return dec;
1812}
1813
1814/*
Tom Tsoud3253432016-03-06 03:08:01 -08001815 * Demodulate GSMK burst. Prior to symbol rotation, operate at
1816 * 4 SPS (if activated) to minimize distortion through the fractional
1817 * delay filters. Symbol rotation and after always operates at 1 SPS.
1818 */
Tom Tsou70134a02017-06-12 14:23:53 -07001819static SoftVector *demodGmskBurst(const signalVector &rxBurst,
1820 int sps, complex channel, float TOA)
dburgessb3a0ca42011-10-12 07:44:40 +00001821{
Thomas Tsou94edaae2013-11-09 22:19:19 -05001822 SoftVector *bits;
Tom Tsou7fec3032016-03-06 22:33:20 -08001823 signalVector *dec;
dburgessb3a0ca42011-10-12 07:44:40 +00001824
Tom Tsou7fec3032016-03-06 22:33:20 -08001825 dec = demodCommon(rxBurst, sps, channel, TOA);
1826 if (!dec)
1827 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +00001828
Tom Tsoud3253432016-03-06 03:08:01 -08001829 /* Shift up by a quarter of a frequency */
1830 GMSKReverseRotate(*dec, 1);
Alexander Chemeris132fb242017-03-17 17:22:33 -07001831 /* Take real part of the signal */
1832 bits = signalToSoftVector(dec);
Thomas Tsou94edaae2013-11-09 22:19:19 -05001833 delete dec;
dburgessb3a0ca42011-10-12 07:44:40 +00001834
Thomas Tsou94edaae2013-11-09 22:19:19 -05001835 return bits;
dburgessb3a0ca42011-10-12 07:44:40 +00001836}
Thomas Tsou94edaae2013-11-09 22:19:19 -05001837
Tom Tsoud3253432016-03-06 03:08:01 -08001838/*
1839 * Demodulate an 8-PSK burst. Prior to symbol rotation, operate at
1840 * 4 SPS (if activated) to minimize distortion through the fractional
1841 * delay filters. Symbol rotation and after always operates at 1 SPS.
1842 *
1843 * Allow 1 SPS demodulation here, but note that other parts of the
1844 * transceiver restrict EDGE operatoin to 4 SPS - 8-PSK distortion
1845 * through the fractional delay filters at 1 SPS renders signal
1846 * nearly unrecoverable.
1847 */
Tom Tsou70134a02017-06-12 14:23:53 -07001848static SoftVector *demodEdgeBurst(const signalVector &burst,
1849 int sps, complex chan, float toa)
Tom Tsoud3253432016-03-06 03:08:01 -08001850{
1851 SoftVector *bits;
Tom Tsou7fec3032016-03-06 22:33:20 -08001852 signalVector *dec, *rot, *eq;
Tom Tsoud3253432016-03-06 03:08:01 -08001853
Tom Tsou7fec3032016-03-06 22:33:20 -08001854 dec = demodCommon(burst, sps, chan, toa);
1855 if (!dec)
Tom Tsoud3253432016-03-06 03:08:01 -08001856 return NULL;
1857
Tom Tsou7fec3032016-03-06 22:33:20 -08001858 /* Equalize and derotate */
Tom Tsoud3253432016-03-06 03:08:01 -08001859 eq = convolve(dec, GSMPulse4->c0_inv, NULL, NO_DELAY);
1860 rot = derotateEdgeBurst(*eq, 1);
1861
Tom Tsou7fec3032016-03-06 22:33:20 -08001862 /* Soft slice and normalize */
Tom Tsou04795622016-04-26 21:17:36 -07001863 bits = softSliceEdgeBurst(*rot);
Tom Tsoud3253432016-03-06 03:08:01 -08001864
1865 delete dec;
1866 delete eq;
1867 delete rot;
1868
1869 return bits;
1870}
1871
Alexander Chemerise0c12182017-03-18 13:27:48 -07001872SoftVector *demodAnyBurst(const signalVector &burst, int sps, complex amp,
Alexander Chemeris6e1dffd2017-03-17 16:13:51 -07001873 float toa, CorrType type)
1874{
1875 if (type == EDGE)
1876 return demodEdgeBurst(burst, sps, amp, toa);
1877 else
1878 return demodGmskBurst(burst, sps, amp, toa);
1879}
1880
Tom Tsou2079a3c2016-03-06 00:58:56 -08001881bool sigProcLibSetup()
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001882{
Thomas Tsou0e0e1f42013-11-09 22:08:51 -05001883 generateSincTable();
Tom Tsou2079a3c2016-03-06 00:58:56 -08001884 initGMSKRotationTables();
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001885
Tom Tsou2079a3c2016-03-06 00:58:56 -08001886 GSMPulse1 = generateGSMPulse(1);
1887 GSMPulse4 = generateGSMPulse(4);
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001888
Vadim Yanitskiya79bc702018-10-17 11:01:58 +02001889 generateRACHSequence(&gRACHSequences[0], gRACHSynchSequenceTS0, 1);
1890 generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1);
1891 generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1);
1892
Tom Tsoud3253432016-03-06 03:08:01 -08001893 for (int tsc = 0; tsc < 8; tsc++) {
Tom Tsou2079a3c2016-03-06 00:58:56 -08001894 generateMidamble(1, tsc);
Tom Tsoud3253432016-03-06 03:08:01 -08001895 gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);
1896 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001897
Thomas Tsouf79c4d02013-11-09 15:51:56 -06001898 generateDelayFilters();
1899
Tom Tsoud3253432016-03-06 03:08:01 -08001900 dnsampler = new Resampler(1, 4);
1901 if (!dnsampler->init()) {
1902 LOG(ALERT) << "Rx resampler failed to initialize";
1903 goto fail;
1904 }
1905
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001906 return true;
Tom Tsoud3253432016-03-06 03:08:01 -08001907
1908fail:
1909 sigProcLibDestroy();
1910 return false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001911}