blob: 5a1ab770c25f871e2c6bebae22d5e7e773705f8c [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*
4* This software is distributed under the terms of the GNU Affero Public License.
5* See the COPYING file in the main directory for details.
6*
7* This use of this software may be subject to additional restrictions.
8* See the LEGAL file in the main directory for details.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU Affero General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU Affero General Public License for more details.
19
20 You should have received a copy of the GNU Affero General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23*/
24
Thomas Tsou7e4e5362013-10-30 21:18:55 -040025#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
dburgessb3a0ca42011-10-12 07:44:40 +000029#include "sigProcLib.h"
30#include "GSMCommon.h"
31
Thomas Tsou3eaae802013-08-20 19:31:14 -040032extern "C" {
33#include "convolve.h"
Thomas Tsou7e4e5362013-10-30 21:18:55 -040034#include "scale.h"
Thomas Tsou3eaae802013-08-20 19:31:14 -040035}
36
Thomas Tsou7e4e5362013-10-30 21:18:55 -040037using namespace GSM;
38
dburgessb3a0ca42011-10-12 07:44:40 +000039#define TABLESIZE 1024
40
41/** Lookup tables for trigonometric approximation */
42float cosTable[TABLESIZE+1]; // add 1 element for wrap around
43float sinTable[TABLESIZE+1];
44
45/** Constants */
46static const float M_PI_F = (float)M_PI;
47static const float M_2PI_F = (float)(2.0*M_PI);
48static const float M_1_2PI_F = 1/M_2PI_F;
49
Thomas Tsouc1f7c422013-10-11 13:49:55 -040050/* Precomputed rotation vectors */
51static signalVector *GMSKRotationN = NULL;
52static signalVector *GMSKReverseRotationN = NULL;
53static signalVector *GMSKRotation1 = NULL;
54static signalVector *GMSKReverseRotation1 = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +000055
Thomas Tsoue5dcfc42013-08-20 16:27:12 -040056/*
Thomas Tsou3eaae802013-08-20 19:31:14 -040057 * RACH and midamble correlation waveforms. Store the buffer separately
58 * because we need to allocate it explicitly outside of the signal vector
59 * constructor. This is because C++ (prior to C++11) is unable to natively
60 * perform 16-byte memory alignment required by many SSE instructions.
Thomas Tsoue5dcfc42013-08-20 16:27:12 -040061 */
62struct CorrelationSequence {
Thomas Tsou3eaae802013-08-20 19:31:14 -040063 CorrelationSequence() : sequence(NULL), buffer(NULL)
Thomas Tsoue5dcfc42013-08-20 16:27:12 -040064 {
65 }
66
67 ~CorrelationSequence()
68 {
69 delete sequence;
Thomas Tsou3eaae802013-08-20 19:31:14 -040070 free(buffer);
Thomas Tsoue5dcfc42013-08-20 16:27:12 -040071 }
72
dburgessb3a0ca42011-10-12 07:44:40 +000073 signalVector *sequence;
Thomas Tsou3eaae802013-08-20 19:31:14 -040074 void *buffer;
Thomas Tsouc1f7c422013-10-11 13:49:55 -040075 float toa;
dburgessb3a0ca42011-10-12 07:44:40 +000076 complex gain;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -040077};
dburgessb3a0ca42011-10-12 07:44:40 +000078
Thomas Tsou83e06892013-08-20 16:10:01 -040079/*
Thomas Tsou3eaae802013-08-20 19:31:14 -040080 * Gaussian and empty modulation pulses. Like the correlation sequences,
81 * store the runtime (Gaussian) buffer separately because of needed alignment
82 * for SSE instructions.
Thomas Tsou83e06892013-08-20 16:10:01 -040083 */
84struct PulseSequence {
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080085 PulseSequence() : c0(NULL), c1(NULL), empty(NULL),
86 c0_buffer(NULL), c1_buffer(NULL)
Thomas Tsou83e06892013-08-20 16:10:01 -040087 {
88 }
89
90 ~PulseSequence()
91 {
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080092 delete c0;
93 delete c1;
Thomas Tsou83e06892013-08-20 16:10:01 -040094 delete empty;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080095 free(c0_buffer);
96 free(c1_buffer);
Thomas Tsou83e06892013-08-20 16:10:01 -040097 }
98
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080099 signalVector *c0;
100 signalVector *c1;
Thomas Tsou83e06892013-08-20 16:10:01 -0400101 signalVector *empty;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800102 void *c0_buffer;
103 void *c1_buffer;
Thomas Tsou83e06892013-08-20 16:10:01 -0400104};
105
dburgessb3a0ca42011-10-12 07:44:40 +0000106CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
107CorrelationSequence *gRACHSequence = NULL;
Thomas Tsou83e06892013-08-20 16:10:01 -0400108PulseSequence *GSMPulse = NULL;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400109PulseSequence *GSMPulse1 = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000110
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400111void sigProcLibDestroy()
112{
dburgessb3a0ca42011-10-12 07:44:40 +0000113 for (int i = 0; i < 8; i++) {
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400114 delete gMidambles[i];
115 gMidambles[i] = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000116 }
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400117
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400118 delete GMSKRotationN;
119 delete GMSKReverseRotationN;
120 delete GMSKRotation1;
121 delete GMSKReverseRotation1;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400122 delete gRACHSequence;
123 delete GSMPulse;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400124 delete GSMPulse1;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400125
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400126 GMSKRotationN = NULL;
127 GMSKRotation1 = NULL;
128 GMSKReverseRotationN = NULL;
129 GMSKReverseRotation1 = NULL;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -0400130 gRACHSequence = NULL;
131 GSMPulse = NULL;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400132 GSMPulse1 = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000133}
134
dburgessb3a0ca42011-10-12 07:44:40 +0000135// dB relative to 1.0.
136// if > 1.0, then return 0 dB
137float dB(float x) {
138
139 float arg = 1.0F;
140 float dB = 0.0F;
141
142 if (x >= 1.0F) return 0.0F;
143 if (x <= 0.0F) return -200.0F;
144
145 float prevArg = arg;
146 float prevdB = dB;
147 float stepSize = 16.0F;
148 float dBstepSize = 12.0F;
149 while (stepSize > 1.0F) {
150 do {
151 prevArg = arg;
152 prevdB = dB;
153 arg /= stepSize;
154 dB -= dBstepSize;
155 } while (arg > x);
156 arg = prevArg;
157 dB = prevdB;
158 stepSize *= 0.5F;
159 dBstepSize -= 3.0F;
160 }
161 return ((arg-x)*(dB-3.0F) + (x-arg*0.5F)*dB)/(arg - arg*0.5F);
162
163}
164
165// 10^(-dB/10), inverse of dB func.
166float dBinv(float x) {
167
168 float arg = 1.0F;
169 float dB = 0.0F;
170
171 if (x >= 0.0F) return 1.0F;
172 if (x <= -200.0F) return 0.0F;
173
174 float prevArg = arg;
175 float prevdB = dB;
176 float stepSize = 16.0F;
177 float dBstepSize = 12.0F;
178 while (stepSize > 1.0F) {
179 do {
180 prevArg = arg;
181 prevdB = dB;
182 arg /= stepSize;
183 dB -= dBstepSize;
184 } while (dB > x);
185 arg = prevArg;
186 dB = prevdB;
187 stepSize *= 0.5F;
188 dBstepSize -= 3.0F;
189 }
190
191 return ((dB-x)*(arg*0.5F)+(x-(dB-3.0F))*(arg))/3.0F;
192
193}
194
195float vectorNorm2(const signalVector &x)
196{
197 signalVector::const_iterator xPtr = x.begin();
198 float Energy = 0.0;
199 for (;xPtr != x.end();xPtr++) {
200 Energy += xPtr->norm2();
201 }
202 return Energy;
203}
204
205
206float vectorPower(const signalVector &x)
207{
208 return vectorNorm2(x)/x.size();
209}
210
211/** compute cosine via lookup table */
212float cosLookup(const float x)
213{
214 float arg = x*M_1_2PI_F;
215 while (arg > 1.0F) arg -= 1.0F;
216 while (arg < 0.0F) arg += 1.0F;
217
218 const float argT = arg*((float)TABLESIZE);
219 const int argI = (int)argT;
220 const float delta = argT-argI;
221 const float iDelta = 1.0F-delta;
222 return iDelta*cosTable[argI] + delta*cosTable[argI+1];
223}
224
225/** compute sine via lookup table */
226float sinLookup(const float x)
227{
228 float arg = x*M_1_2PI_F;
229 while (arg > 1.0F) arg -= 1.0F;
230 while (arg < 0.0F) arg += 1.0F;
231
232 const float argT = arg*((float)TABLESIZE);
233 const int argI = (int)argT;
234 const float delta = argT-argI;
235 const float iDelta = 1.0F-delta;
236 return iDelta*sinTable[argI] + delta*sinTable[argI+1];
237}
238
239
240/** compute e^(-jx) via lookup table. */
241complex expjLookup(float x)
242{
243 float arg = x*M_1_2PI_F;
244 while (arg > 1.0F) arg -= 1.0F;
245 while (arg < 0.0F) arg += 1.0F;
246
247 const float argT = arg*((float)TABLESIZE);
248 const int argI = (int)argT;
249 const float delta = argT-argI;
250 const float iDelta = 1.0F-delta;
251 return complex(iDelta*cosTable[argI] + delta*cosTable[argI+1],
252 iDelta*sinTable[argI] + delta*sinTable[argI+1]);
253}
254
255/** Library setup functions */
256void initTrigTables() {
257 for (int i = 0; i < TABLESIZE+1; i++) {
258 cosTable[i] = cos(2.0*M_PI*i/TABLESIZE);
259 sinTable[i] = sin(2.0*M_PI*i/TABLESIZE);
260 }
261}
262
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400263void initGMSKRotationTables(int sps)
264{
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400265 GMSKRotationN = new signalVector(157 * sps);
266 GMSKReverseRotationN = new signalVector(157 * sps);
267 signalVector::iterator rotPtr = GMSKRotationN->begin();
268 signalVector::iterator revPtr = GMSKReverseRotationN->begin();
dburgessb3a0ca42011-10-12 07:44:40 +0000269 float phase = 0.0;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400270 while (rotPtr != GMSKRotationN->end()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000271 *rotPtr++ = expjLookup(phase);
272 *revPtr++ = expjLookup(-phase);
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400273 phase += M_PI_F / 2.0F / (float) sps;
dburgessb3a0ca42011-10-12 07:44:40 +0000274 }
dburgessb3a0ca42011-10-12 07:44:40 +0000275
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400276 GMSKRotation1 = new signalVector(157);
277 GMSKReverseRotation1 = new signalVector(157);
278 rotPtr = GMSKRotation1->begin();
279 revPtr = GMSKReverseRotation1->begin();
280 phase = 0.0;
281 while (rotPtr != GMSKRotation1->end()) {
282 *rotPtr++ = expjLookup(phase);
283 *revPtr++ = expjLookup(-phase);
284 phase += M_PI_F / 2.0F;
Thomas Tsoue57004d2013-08-20 18:55:33 -0400285 }
dburgessb3a0ca42011-10-12 07:44:40 +0000286}
287
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400288static void GMSKRotate(signalVector &x, int sps)
289{
290 signalVector::iterator rotPtr, xPtr = x.begin();
291
292 if (sps == 1)
293 rotPtr = GMSKRotation1->begin();
294 else
295 rotPtr = GMSKRotationN->begin();
296
dburgessb3a0ca42011-10-12 07:44:40 +0000297 if (x.isRealOnly()) {
298 while (xPtr < x.end()) {
299 *xPtr = *rotPtr++ * (xPtr->real());
300 xPtr++;
301 }
302 }
303 else {
304 while (xPtr < x.end()) {
305 *xPtr = *rotPtr++ * (*xPtr);
306 xPtr++;
307 }
308 }
309}
310
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400311static void GMSKReverseRotate(signalVector &x, int sps)
312{
313 signalVector::iterator rotPtr, xPtr= x.begin();
314
315 if (sps == 1)
316 rotPtr = GMSKReverseRotation1->begin();
317 else
318 rotPtr = GMSKReverseRotationN->begin();
319
dburgessb3a0ca42011-10-12 07:44:40 +0000320 if (x.isRealOnly()) {
321 while (xPtr < x.end()) {
322 *xPtr = *rotPtr++ * (xPtr->real());
323 xPtr++;
324 }
325 }
326 else {
327 while (xPtr < x.end()) {
328 *xPtr = *rotPtr++ * (*xPtr);
329 xPtr++;
330 }
331 }
332}
333
Thomas Tsou3eaae802013-08-20 19:31:14 -0400334signalVector *convolve(const signalVector *x,
335 const signalVector *h,
336 signalVector *y,
337 ConvType spanType, int start,
338 unsigned len, unsigned step, int offset)
dburgessb3a0ca42011-10-12 07:44:40 +0000339{
Thomas Tsou3eaae802013-08-20 19:31:14 -0400340 int rc, head = 0, tail = 0;
341 bool alloc = false, append = false;
342 const signalVector *_x = NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000343
Thomas Tsou3eaae802013-08-20 19:31:14 -0400344 if (!x || !h)
dburgessb3a0ca42011-10-12 07:44:40 +0000345 return NULL;
346
Thomas Tsou3eaae802013-08-20 19:31:14 -0400347 switch (spanType) {
348 case START_ONLY:
349 start = 0;
350 head = h->size();
351 len = x->size();
352 append = true;
dburgessb3a0ca42011-10-12 07:44:40 +0000353 break;
Thomas Tsou3eaae802013-08-20 19:31:14 -0400354 case NO_DELAY:
355 start = h->size() / 2;
356 head = start;
357 tail = start;
358 len = x->size();
359 append = true;
360 break;
361 case CUSTOM:
362 if (start < h->size() - 1) {
363 head = h->size() - start;
364 append = true;
365 }
366 if (start + len > x->size()) {
367 tail = start + len - x->size();
368 append = true;
dburgessb3a0ca42011-10-12 07:44:40 +0000369 }
370 break;
371 default:
372 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000373 }
dburgessb3a0ca42011-10-12 07:44:40 +0000374
Thomas Tsou3eaae802013-08-20 19:31:14 -0400375 /*
376 * Error if the output vector is too small. Create the output vector
377 * if the pointer is NULL.
378 */
379 if (y && (len > y->size()))
380 return NULL;
381 if (!y) {
382 y = new signalVector(len);
383 alloc = true;
384 }
385
386 /* Prepend or post-pend the input vector if the parameters require it */
387 if (append)
388 _x = new signalVector(*x, head, tail);
389 else
390 _x = x;
391
392 /*
393 * Four convovle types:
394 * 1. Complex-Real (aligned)
395 * 2. Complex-Complex (aligned)
396 * 3. Complex-Real (!aligned)
397 * 4. Complex-Complex (!aligned)
398 */
399 if (h->isRealOnly() && h->isAligned()) {
400 rc = convolve_real((float *) _x->begin(), _x->size(),
401 (float *) h->begin(), h->size(),
402 (float *) y->begin(), y->size(),
403 start, len, step, offset);
404 } else if (!h->isRealOnly() && h->isAligned()) {
405 rc = convolve_complex((float *) _x->begin(), _x->size(),
406 (float *) h->begin(), h->size(),
407 (float *) y->begin(), y->size(),
408 start, len, step, offset);
409 } else if (h->isRealOnly() && !h->isAligned()) {
410 rc = base_convolve_real((float *) _x->begin(), _x->size(),
411 (float *) h->begin(), h->size(),
412 (float *) y->begin(), y->size(),
413 start, len, step, offset);
414 } else if (!h->isRealOnly() && !h->isAligned()) {
415 rc = base_convolve_complex((float *) _x->begin(), _x->size(),
416 (float *) h->begin(), h->size(),
417 (float *) y->begin(), y->size(),
418 start, len, step, offset);
419 } else {
420 rc = -1;
421 }
422
423 if (append)
424 delete _x;
425
426 if (rc < 0) {
427 if (alloc)
428 delete y;
429 return NULL;
430 }
431
432 return y;
433}
dburgessb3a0ca42011-10-12 07:44:40 +0000434
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400435static bool generateC1Pulse(int sps, PulseSequence *pulse)
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800436{
437 int len;
438
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400439 if (!pulse)
440 return false;
441
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800442 switch (sps) {
443 case 4:
444 len = 8;
445 break;
446 default:
447 return false;
448 }
449
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400450 pulse->c1_buffer = convolve_h_alloc(len);
451 pulse->c1 = new signalVector((complex *)
452 pulse->c1_buffer, 0, len);
453 pulse->c1->isRealOnly(true);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800454
455 /* Enable alignment for SSE usage */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400456 pulse->c1->setAligned(true);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800457
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400458 signalVector::iterator xP = pulse->c1->begin();
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800459
460 switch (sps) {
461 case 4:
462 /* BT = 0.30 */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400463 *xP++ = 0.0;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800464 *xP++ = 8.16373112e-03;
465 *xP++ = 2.84385729e-02;
466 *xP++ = 5.64158904e-02;
467 *xP++ = 7.05463553e-02;
468 *xP++ = 5.64158904e-02;
469 *xP++ = 2.84385729e-02;
470 *xP++ = 8.16373112e-03;
471 }
472
473 return true;
474}
475
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400476static PulseSequence *generateGSMPulse(int sps, int symbolLength)
dburgessb3a0ca42011-10-12 07:44:40 +0000477{
Thomas Tsou83e06892013-08-20 16:10:01 -0400478 int len;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800479 float arg, avg, center;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400480 PulseSequence *pulse;
Thomas Tsou83e06892013-08-20 16:10:01 -0400481
482 /* Store a single tap filter used for correlation sequence generation */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400483 pulse = new PulseSequence();
484 pulse->empty = new signalVector(1);
485 pulse->empty->isRealOnly(true);
486 *(pulse->empty->begin()) = 1.0f;
Thomas Tsou83e06892013-08-20 16:10:01 -0400487
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400488 /*
489 * For 4 samples-per-symbol use a precomputed single pulse Laurent
490 * approximation. This should yields below 2 degrees of phase error at
491 * the modulator output. Use the existing pulse approximation for all
492 * other oversampling factors.
493 */
494 switch (sps) {
495 case 4:
496 len = 16;
497 break;
498 default:
499 len = sps * symbolLength;
500 if (len < 4)
501 len = 4;
502 }
Thomas Tsou3eaae802013-08-20 19:31:14 -0400503
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400504 pulse->c0_buffer = convolve_h_alloc(len);
505 pulse->c0 = new signalVector((complex *) pulse->c0_buffer, 0, len);
506 pulse->c0->isRealOnly(true);
Thomas Tsou3eaae802013-08-20 19:31:14 -0400507
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800508 /* Enable alingnment for SSE usage */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400509 pulse->c0->setAligned(true);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800510
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400511 signalVector::iterator xP = pulse->c0->begin();
Thomas Tsou83e06892013-08-20 16:10:01 -0400512
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400513 if (sps == 4) {
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800514 *xP++ = 0.0;
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400515 *xP++ = 4.46348606e-03;
516 *xP++ = 2.84385729e-02;
517 *xP++ = 1.03184855e-01;
518 *xP++ = 2.56065552e-01;
519 *xP++ = 4.76375085e-01;
520 *xP++ = 7.05961177e-01;
521 *xP++ = 8.71291644e-01;
522 *xP++ = 9.29453645e-01;
523 *xP++ = 8.71291644e-01;
524 *xP++ = 7.05961177e-01;
525 *xP++ = 4.76375085e-01;
526 *xP++ = 2.56065552e-01;
527 *xP++ = 1.03184855e-01;
528 *xP++ = 2.84385729e-02;
529 *xP++ = 4.46348606e-03;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400530 generateC1Pulse(sps, pulse);
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400531 } else {
532 center = (float) (len - 1.0) / 2.0;
Thomas Tsou83e06892013-08-20 16:10:01 -0400533
Thomas Tsou9ccd9f22013-08-21 13:59:52 -0400534 /* GSM pulse approximation */
535 for (int i = 0; i < len; i++) {
536 arg = ((float) i - center) / (float) sps;
537 *xP++ = 0.96 * exp(-1.1380 * arg * arg -
538 0.527 * arg * arg * arg * arg);
539 }
dburgessb3a0ca42011-10-12 07:44:40 +0000540
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400541 avg = sqrtf(vectorNorm2(*pulse->c0) / sps);
542 xP = pulse->c0->begin();
543 for (int i = 0; i < len; i++)
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800544 *xP++ /= avg;
545 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400546
547 return pulse;
dburgessb3a0ca42011-10-12 07:44:40 +0000548}
549
550signalVector* frequencyShift(signalVector *y,
551 signalVector *x,
552 float freq,
553 float startPhase,
554 float *finalPhase)
555{
556
557 if (!x) return NULL;
558
559 if (y==NULL) {
560 y = new signalVector(x->size());
561 y->isRealOnly(x->isRealOnly());
562 if (y==NULL) return NULL;
563 }
564
565 if (y->size() < x->size()) return NULL;
566
567 float phase = startPhase;
568 signalVector::iterator yP = y->begin();
569 signalVector::iterator xPEnd = x->end();
570 signalVector::iterator xP = x->begin();
571
572 if (x->isRealOnly()) {
573 while (xP < xPEnd) {
574 (*yP++) = expjLookup(phase)*( (xP++)->real() );
575 phase += freq;
576 }
577 }
578 else {
579 while (xP < xPEnd) {
580 (*yP++) = (*xP++)*expjLookup(phase);
581 phase += freq;
582 }
583 }
584
585
586 if (finalPhase) *finalPhase = phase;
587
588 return y;
589}
590
591signalVector* reverseConjugate(signalVector *b)
592{
593 signalVector *tmp = new signalVector(b->size());
594 tmp->isRealOnly(b->isRealOnly());
595 signalVector::iterator bP = b->begin();
596 signalVector::iterator bPEnd = b->end();
597 signalVector::iterator tmpP = tmp->end()-1;
598 if (!b->isRealOnly()) {
599 while (bP < bPEnd) {
600 *tmpP-- = bP->conj();
601 bP++;
602 }
603 }
604 else {
605 while (bP < bPEnd) {
606 *tmpP-- = bP->real();
607 bP++;
608 }
609 }
610
611 return tmp;
612}
613
dburgessb3a0ca42011-10-12 07:44:40 +0000614/* soft output slicer */
615bool vectorSlicer(signalVector *x)
616{
617
618 signalVector::iterator xP = x->begin();
619 signalVector::iterator xPEnd = x->end();
620 while (xP < xPEnd) {
621 *xP = (complex) (0.5*(xP->real()+1.0F));
622 if (xP->real() > 1.0) *xP = 1.0;
623 if (xP->real() < 0.0) *xP = 0.0;
624 xP++;
625 }
626 return true;
627}
Thomas Tsou3eaae802013-08-20 19:31:14 -0400628
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800629static signalVector *rotateBurst(const BitVector &wBurst,
630 int guardPeriodLength, int sps)
631{
632 int burst_len;
633 signalVector *pulse, rotated, *shaped;
634 signalVector::iterator itr;
635
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400636 pulse = GSMPulse1->empty;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800637 burst_len = sps * (wBurst.size() + guardPeriodLength);
638 rotated = signalVector(burst_len);
639 itr = rotated.begin();
640
641 for (unsigned i = 0; i < wBurst.size(); i++) {
642 *itr = 2.0 * (wBurst[i] & 0x01) - 1.0;
643 itr += sps;
644 }
645
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400646 GMSKRotate(rotated, sps);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800647 rotated.isRealOnly(false);
648
649 /* Dummy filter operation */
650 shaped = convolve(&rotated, pulse, NULL, START_ONLY);
651 if (!shaped)
652 return NULL;
653
654 return shaped;
655}
656
657static signalVector *modulateBurstLaurent(const BitVector &bits,
658 int guard_len, int sps)
659{
660 int burst_len;
661 float phase;
662 signalVector *c0_pulse, *c1_pulse, c0_burst, c1_burst, *c0_shaped, *c1_shaped;
663 signalVector::iterator c0_itr, c1_itr;
664
665 /*
666 * Apply before and after bits to reduce phase error at burst edges.
667 * Make sure there is enough room in the burst to accomodate all bits.
668 */
669 if (guard_len < 4)
670 guard_len = 4;
671
672 c0_pulse = GSMPulse->c0;
673 c1_pulse = GSMPulse->c1;
674
675 burst_len = sps * (bits.size() + guard_len);
676
677 c0_burst = signalVector(burst_len);
678 c0_burst.isRealOnly(true);
679 c0_itr = c0_burst.begin();
680
681 c1_burst = signalVector(burst_len);
682 c1_burst.isRealOnly(true);
683 c1_itr = c1_burst.begin();
684
685 /* Padded differential start bits */
686 *c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
687 c0_itr += sps;
688
689 /* Main burst bits */
690 for (unsigned i = 0; i < bits.size(); i++) {
691 *c0_itr = 2.0 * (bits[i] & 0x01) - 1.0;
692 c0_itr += sps;
693 }
694
695 /* Padded differential end bits */
696 *c0_itr = 2.0 * (0x01 & 0x01) - 1.0;
697
698 /* Generate C0 phase coefficients */
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400699 GMSKRotate(c0_burst, sps);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800700 c0_burst.isRealOnly(false);
701
702 c0_itr = c0_burst.begin();
703 c0_itr += sps * 2;
704 c1_itr += sps * 2;
705
706 /* Start magic */
707 phase = 2.0 * ((0x01 & 0x01) ^ (0x01 & 0x01)) - 1.0;
708 *c1_itr = *c0_itr * Complex<float>(0, phase);
709 c0_itr += sps;
710 c1_itr += sps;
711
712 /* Generate C1 phase coefficients */
713 for (unsigned i = 2; i < bits.size(); i++) {
714 phase = 2.0 * ((bits[i - 1] & 0x01) ^ (bits[i - 2] & 0x01)) - 1.0;
715 *c1_itr = *c0_itr * Complex<float>(0, phase);
716
717 c0_itr += sps;
718 c1_itr += sps;
719 }
720
721 /* End magic */
722 int i = bits.size();
723 phase = 2.0 * ((bits[i-1] & 0x01) ^ (bits[i-2] & 0x01)) - 1.0;
724 *c1_itr = *c0_itr * Complex<float>(0, phase);
725
726 /* Primary (C0) and secondary (C1) pulse shaping */
727 c0_shaped = convolve(&c0_burst, c0_pulse, NULL, START_ONLY);
728 c1_shaped = convolve(&c1_burst, c1_pulse, NULL, START_ONLY);
729
730 /* Sum shaped outputs into C0 */
731 c0_itr = c0_shaped->begin();
732 c1_itr = c1_shaped->begin();
733 for (unsigned i = 0; i < c0_shaped->size(); i++ )
734 *c0_itr++ += *c1_itr++;
735
736 delete c1_shaped;
737
738 return c0_shaped;
739}
740
741static signalVector *modulateBurstBasic(const BitVector &bits,
742 int guard_len, int sps)
743{
744 int burst_len;
745 signalVector *pulse, burst, *shaped;
746 signalVector::iterator burst_itr;
747
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400748 if (sps == 1)
749 pulse = GSMPulse1->c0;
750 else
751 pulse = GSMPulse->c0;
752
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800753 burst_len = sps * (bits.size() + guard_len);
754
755 burst = signalVector(burst_len);
756 burst.isRealOnly(true);
757 burst_itr = burst.begin();
758
759 /* Raw bits are not differentially encoded */
760 for (unsigned i = 0; i < bits.size(); i++) {
761 *burst_itr = 2.0 * (bits[i] & 0x01) - 1.0;
762 burst_itr += sps;
763 }
764
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400765 GMSKRotate(burst, sps);
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800766 burst.isRealOnly(false);
767
768 /* Single Gaussian pulse approximation shaping */
769 shaped = convolve(&burst, pulse, NULL, START_ONLY);
770
771 return shaped;
772}
773
Thomas Tsou3eaae802013-08-20 19:31:14 -0400774/* Assume input bits are not differentially encoded */
Thomas Tsou83e06892013-08-20 16:10:01 -0400775signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength,
776 int sps, bool emptyPulse)
dburgessb3a0ca42011-10-12 07:44:40 +0000777{
Thomas Tsou83e06892013-08-20 16:10:01 -0400778 if (emptyPulse)
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800779 return rotateBurst(wBurst, guardPeriodLength, sps);
780 else if (sps == 4)
781 return modulateBurstLaurent(wBurst, guardPeriodLength, sps);
Thomas Tsou83e06892013-08-20 16:10:01 -0400782 else
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800783 return modulateBurstBasic(wBurst, guardPeriodLength, sps);
dburgessb3a0ca42011-10-12 07:44:40 +0000784}
785
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800786float sinc(float x)
dburgessb3a0ca42011-10-12 07:44:40 +0000787{
788 if ((x >= 0.01F) || (x <= -0.01F)) return (sinLookup(x)/x);
789 return 1.0F;
790}
791
Thomas Tsou3eaae802013-08-20 19:31:14 -0400792bool delayVector(signalVector &wBurst, float delay)
dburgessb3a0ca42011-10-12 07:44:40 +0000793{
Thomas Tsou2c282f52013-10-08 21:34:35 -0400794 int whole, h_len = 20;
795 float frac;
796 complex *data;
797 signalVector *h, *shift;
798 signalVector::iterator itr;
Thomas Tsou3eaae802013-08-20 19:31:14 -0400799
Thomas Tsou2c282f52013-10-08 21:34:35 -0400800 whole = floor(delay);
801 frac = delay - whole;
802
803 /* Sinc interpolated fractional shift (if allowable) */
804 if (fabs(frac) > 1e-2) {
805 data = (complex *) convolve_h_alloc(h_len);
806 h = new signalVector(data, 0, h_len);
807 h->setAligned(true);
808 h->isRealOnly(true);
809
810 itr = h->end();
811 for (int i = 0; i < h_len; i++)
812 *--itr = (complex) sinc(M_PI_F * (i - h_len / 2 - frac));
813
814 shift = convolve(&wBurst, h, NULL, NO_DELAY);
815
816 delete h;
817 free(data);
818
819 if (!shift)
Thomas Tsou3eaae802013-08-20 19:31:14 -0400820 return false;
Thomas Tsou2c282f52013-10-08 21:34:35 -0400821
822 wBurst.clone(*shift);
823 delete shift;
dburgessb3a0ca42011-10-12 07:44:40 +0000824 }
825
Thomas Tsou2c282f52013-10-08 21:34:35 -0400826 /* Integer sample shift */
827 if (whole < 0) {
828 whole = -whole;
dburgessb3a0ca42011-10-12 07:44:40 +0000829 signalVector::iterator wBurstItr = wBurst.begin();
Thomas Tsou2c282f52013-10-08 21:34:35 -0400830 signalVector::iterator shiftedItr = wBurst.begin() + whole;
831
dburgessb3a0ca42011-10-12 07:44:40 +0000832 while (shiftedItr < wBurst.end())
833 *wBurstItr++ = *shiftedItr++;
834 while (wBurstItr < wBurst.end())
835 *wBurstItr++ = 0.0;
Thomas Tsou2c282f52013-10-08 21:34:35 -0400836 } else {
837 signalVector::iterator wBurstItr = wBurst.end() - 1;
838 signalVector::iterator shiftedItr = wBurst.end() - 1 - whole;
839
dburgessb3a0ca42011-10-12 07:44:40 +0000840 while (shiftedItr >= wBurst.begin())
841 *wBurstItr-- = *shiftedItr--;
842 while (wBurstItr >= wBurst.begin())
843 *wBurstItr-- = 0.0;
844 }
Thomas Tsou2c282f52013-10-08 21:34:35 -0400845
846 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000847}
Thomas Tsou2c282f52013-10-08 21:34:35 -0400848
dburgessb3a0ca42011-10-12 07:44:40 +0000849signalVector *gaussianNoise(int length,
850 float variance,
851 complex mean)
852{
853
854 signalVector *noise = new signalVector(length);
855 signalVector::iterator nPtr = noise->begin();
856 float stddev = sqrtf(variance);
857 while (nPtr < noise->end()) {
858 float u1 = (float) rand()/ (float) RAND_MAX;
859 while (u1==0.0)
860 u1 = (float) rand()/ (float) RAND_MAX;
861 float u2 = (float) rand()/ (float) RAND_MAX;
862 float arg = 2.0*M_PI*u2;
863 *nPtr = mean + stddev*complex(cos(arg),sin(arg))*sqrtf(-2.0*log(u1));
864 nPtr++;
865 }
866
867 return noise;
868}
869
870complex interpolatePoint(const signalVector &inSig,
871 float ix)
872{
873
874 int start = (int) (floor(ix) - 10);
875 if (start < 0) start = 0;
876 int end = (int) (floor(ix) + 11);
877 if ((unsigned) end > inSig.size()-1) end = inSig.size()-1;
878
879 complex pVal = 0.0;
880 if (!inSig.isRealOnly()) {
881 for (int i = start; i < end; i++)
882 pVal += inSig[i] * sinc(M_PI_F*(i-ix));
883 }
884 else {
885 for (int i = start; i < end; i++)
886 pVal += inSig[i].real() * sinc(M_PI_F*(i-ix));
887 }
888
889 return pVal;
890}
891
Thomas Tsou8181b012013-08-20 21:17:19 -0400892static complex fastPeakDetect(const signalVector &rxBurst, float *index)
893{
894 float val, max = 0.0f;
895 complex amp;
896 int _index = -1;
897
898 for (int i = 0; i < rxBurst.size(); i++) {
899 val = rxBurst[i].norm2();
900 if (val > max) {
901 max = val;
902 _index = i;
903 amp = rxBurst[i];
904 }
905 }
906
907 if (index)
908 *index = (float) _index;
909
910 return amp;
911}
912
dburgessb3a0ca42011-10-12 07:44:40 +0000913complex peakDetect(const signalVector &rxBurst,
914 float *peakIndex,
915 float *avgPwr)
916{
917
918
919 complex maxVal = 0.0;
920 float maxIndex = -1;
921 float sumPower = 0.0;
922
923 for (unsigned int i = 0; i < rxBurst.size(); i++) {
924 float samplePower = rxBurst[i].norm2();
925 if (samplePower > maxVal.real()) {
926 maxVal = samplePower;
927 maxIndex = i;
928 }
929 sumPower += samplePower;
930 }
931
932 // interpolate around the peak
933 // to save computation, we'll use early-late balancing
934 float earlyIndex = maxIndex-1;
935 float lateIndex = maxIndex+1;
936
937 float incr = 0.5;
938 while (incr > 1.0/1024.0) {
939 complex earlyP = interpolatePoint(rxBurst,earlyIndex);
940 complex lateP = interpolatePoint(rxBurst,lateIndex);
941 if (earlyP < lateP)
942 earlyIndex += incr;
943 else if (earlyP > lateP)
944 earlyIndex -= incr;
945 else break;
946 incr /= 2.0;
947 lateIndex = earlyIndex + 2.0;
948 }
949
950 maxIndex = earlyIndex + 1.0;
951 maxVal = interpolatePoint(rxBurst,maxIndex);
952
953 if (peakIndex!=NULL)
954 *peakIndex = maxIndex;
955
956 if (avgPwr!=NULL)
957 *avgPwr = (sumPower-maxVal.norm2()) / (rxBurst.size()-1);
958
959 return maxVal;
960
961}
962
963void scaleVector(signalVector &x,
964 complex scale)
965{
Thomas Tsou7e4e5362013-10-30 21:18:55 -0400966#ifdef HAVE_NEON
967 int len = x.size();
968
969 scale_complex((float *) x.begin(),
970 (float *) x.begin(),
971 (float *) &scale, len);
972#else
dburgessb3a0ca42011-10-12 07:44:40 +0000973 signalVector::iterator xP = x.begin();
974 signalVector::iterator xPEnd = x.end();
975 if (!x.isRealOnly()) {
976 while (xP < xPEnd) {
977 *xP = *xP * scale;
978 xP++;
979 }
980 }
981 else {
982 while (xP < xPEnd) {
983 *xP = xP->real() * scale;
984 xP++;
985 }
986 }
Thomas Tsou7e4e5362013-10-30 21:18:55 -0400987#endif
dburgessb3a0ca42011-10-12 07:44:40 +0000988}
989
990/** in-place conjugation */
991void conjugateVector(signalVector &x)
992{
993 if (x.isRealOnly()) return;
994 signalVector::iterator xP = x.begin();
995 signalVector::iterator xPEnd = x.end();
996 while (xP < xPEnd) {
997 *xP = xP->conj();
998 xP++;
999 }
1000}
1001
1002
1003// in-place addition!!
1004bool addVector(signalVector &x,
1005 signalVector &y)
1006{
1007 signalVector::iterator xP = x.begin();
1008 signalVector::iterator yP = y.begin();
1009 signalVector::iterator xPEnd = x.end();
1010 signalVector::iterator yPEnd = y.end();
1011 while ((xP < xPEnd) && (yP < yPEnd)) {
1012 *xP = *xP + *yP;
1013 xP++; yP++;
1014 }
1015 return true;
1016}
1017
1018// in-place multiplication!!
1019bool multVector(signalVector &x,
1020 signalVector &y)
1021{
1022 signalVector::iterator xP = x.begin();
1023 signalVector::iterator yP = y.begin();
1024 signalVector::iterator xPEnd = x.end();
1025 signalVector::iterator yPEnd = y.end();
1026 while ((xP < xPEnd) && (yP < yPEnd)) {
1027 *xP = (*xP) * (*yP);
1028 xP++; yP++;
1029 }
1030 return true;
1031}
1032
1033
1034void offsetVector(signalVector &x,
1035 complex offset)
1036{
1037 signalVector::iterator xP = x.begin();
1038 signalVector::iterator xPEnd = x.end();
1039 if (!x.isRealOnly()) {
1040 while (xP < xPEnd) {
1041 *xP += offset;
1042 xP++;
1043 }
1044 }
1045 else {
1046 while (xP < xPEnd) {
1047 *xP = xP->real() + offset;
1048 xP++;
1049 }
1050 }
1051}
1052
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001053bool generateMidamble(int sps, int tsc)
dburgessb3a0ca42011-10-12 07:44:40 +00001054{
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001055 bool status = true;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001056 float toa;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001057 complex *data = NULL;
1058 signalVector *autocorr = NULL, *midamble = NULL;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001059 signalVector *midMidamble = NULL, *_midMidamble = NULL;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001060
Thomas Tsou3eaae802013-08-20 19:31:14 -04001061 if ((tsc < 0) || (tsc > 7))
dburgessb3a0ca42011-10-12 07:44:40 +00001062 return false;
1063
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001064 delete gMidambles[tsc];
Thomas Tsou3eaae802013-08-20 19:31:14 -04001065
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001066 /* Use middle 16 bits of each TSC. Correlation sequence is not pulse shaped */
1067 midMidamble = modulateBurst(gTrainingSequence[tsc].segment(5,16), 0, sps, true);
1068 if (!midMidamble)
1069 return false;
1070
Thomas Tsou3eaae802013-08-20 19:31:14 -04001071 /* Simulated receive sequence is pulse shaped */
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001072 midamble = modulateBurst(gTrainingSequence[tsc], 0, sps, false);
1073 if (!midamble) {
1074 status = false;
1075 goto release;
dburgessb3a0ca42011-10-12 07:44:40 +00001076 }
Thomas Tsou3eaae802013-08-20 19:31:14 -04001077
dburgessb3a0ca42011-10-12 07:44:40 +00001078 // NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst,
1079 // the ideal TSC has an + 180 degree phase shift,
1080 // due to the pi/2 frequency shift, that
1081 // needs to be accounted for.
1082 // 26-midamble is 61 symbols into burst, has +90 degree phase shift.
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001083 scaleVector(*midMidamble, complex(-1.0, 0.0));
1084 scaleVector(*midamble, complex(0.0, 1.0));
dburgessb3a0ca42011-10-12 07:44:40 +00001085
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001086 conjugateVector(*midMidamble);
dburgessb3a0ca42011-10-12 07:44:40 +00001087
Thomas Tsou3eaae802013-08-20 19:31:14 -04001088 /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
1089 data = (complex *) convolve_h_alloc(midMidamble->size());
1090 _midMidamble = new signalVector(data, 0, midMidamble->size());
1091 _midMidamble->setAligned(true);
1092 memcpy(_midMidamble->begin(), midMidamble->begin(),
1093 midMidamble->size() * sizeof(complex));
1094
1095 autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY);
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001096 if (!autocorr) {
1097 status = false;
1098 goto release;
1099 }
dburgessb3a0ca42011-10-12 07:44:40 +00001100
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001101 gMidambles[tsc] = new CorrelationSequence;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001102 gMidambles[tsc]->buffer = data;
1103 gMidambles[tsc]->sequence = _midMidamble;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001104 gMidambles[tsc]->gain = peakDetect(*autocorr, &toa, NULL);
1105
1106 /* For 1 sps only
1107 * (Half of correlation length - 1) + midpoint of pulse shape + remainder
1108 * 13.5 = (16 / 2 - 1) + 1.5 + (26 - 10) / 2
1109 */
1110 if (sps == 1)
1111 gMidambles[tsc]->toa = toa - 13.5;
1112 else
1113 gMidambles[tsc]->toa = 0;
dburgessb3a0ca42011-10-12 07:44:40 +00001114
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001115release:
dburgessb3a0ca42011-10-12 07:44:40 +00001116 delete autocorr;
1117 delete midamble;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001118 delete midMidamble;
dburgessb3a0ca42011-10-12 07:44:40 +00001119
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001120 if (!status) {
Thomas Tsou3eaae802013-08-20 19:31:14 -04001121 delete _midMidamble;
1122 free(data);
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001123 gMidambles[tsc] = NULL;
1124 }
1125
1126 return status;
dburgessb3a0ca42011-10-12 07:44:40 +00001127}
1128
Thomas Tsou83e06892013-08-20 16:10:01 -04001129bool generateRACHSequence(int sps)
dburgessb3a0ca42011-10-12 07:44:40 +00001130{
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001131 bool status = true;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001132 float toa;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001133 complex *data = NULL;
1134 signalVector *autocorr = NULL;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001135 signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001136
1137 delete gRACHSequence;
1138
1139 seq0 = modulateBurst(gRACHSynchSequence, 0, sps, false);
1140 if (!seq0)
1141 return false;
1142
1143 seq1 = modulateBurst(gRACHSynchSequence.segment(0, 40), 0, sps, true);
1144 if (!seq1) {
1145 status = false;
1146 goto release;
dburgessb3a0ca42011-10-12 07:44:40 +00001147 }
1148
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001149 conjugateVector(*seq1);
dburgessb3a0ca42011-10-12 07:44:40 +00001150
Thomas Tsou3eaae802013-08-20 19:31:14 -04001151 /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
1152 data = (complex *) convolve_h_alloc(seq1->size());
1153 _seq1 = new signalVector(data, 0, seq1->size());
1154 _seq1->setAligned(true);
1155 memcpy(_seq1->begin(), seq1->begin(), seq1->size() * sizeof(complex));
1156
1157 autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
1158 if (!autocorr) {
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001159 status = false;
1160 goto release;
1161 }
dburgessb3a0ca42011-10-12 07:44:40 +00001162
1163 gRACHSequence = new CorrelationSequence;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001164 gRACHSequence->sequence = _seq1;
1165 gRACHSequence->buffer = data;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001166 gRACHSequence->gain = peakDetect(*autocorr, &toa, NULL);
1167
1168 /* For 1 sps only
1169 * (Half of correlation length - 1) + midpoint of pulse shaping filer
1170 * 20.5 = (40 / 2 - 1) + 1.5
1171 */
1172 if (sps == 1)
1173 gRACHSequence->toa = toa - 20.5;
1174 else
1175 gRACHSequence->toa = 0.0;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001176
1177release:
dburgessb3a0ca42011-10-12 07:44:40 +00001178 delete autocorr;
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001179 delete seq0;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001180 delete seq1;
dburgessb3a0ca42011-10-12 07:44:40 +00001181
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001182 if (!status) {
Thomas Tsou3eaae802013-08-20 19:31:14 -04001183 delete _seq1;
1184 free(data);
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001185 gRACHSequence = NULL;
1186 }
dburgessb3a0ca42011-10-12 07:44:40 +00001187
Thomas Tsoue5dcfc42013-08-20 16:27:12 -04001188 return status;
dburgessb3a0ca42011-10-12 07:44:40 +00001189}
Thomas Tsou3eaae802013-08-20 19:31:14 -04001190
Thomas Tsou865bca42013-08-21 20:58:00 -04001191static float computePeakRatio(signalVector *corr,
1192 int sps, float toa, complex amp)
dburgessb3a0ca42011-10-12 07:44:40 +00001193{
Thomas Tsou865bca42013-08-21 20:58:00 -04001194 int num = 0;
1195 complex *peak;
1196 float rms, avg = 0.0;
dburgessb3a0ca42011-10-12 07:44:40 +00001197
Thomas Tsou865bca42013-08-21 20:58:00 -04001198 peak = corr->begin() + (int) rint(toa);
dburgessb3a0ca42011-10-12 07:44:40 +00001199
Thomas Tsou865bca42013-08-21 20:58:00 -04001200 /* Check for bogus results */
1201 if ((toa < 0.0) || (toa > corr->size()))
1202 return 0.0;
dburgessb3a0ca42011-10-12 07:44:40 +00001203
Thomas Tsou3eaae802013-08-20 19:31:14 -04001204 for (int i = 2 * sps; i <= 5 * sps; i++) {
Thomas Tsou865bca42013-08-21 20:58:00 -04001205 if (peak - i >= corr->begin()) {
Thomas Tsou3eaae802013-08-20 19:31:14 -04001206 avg += (peak - i)->norm2();
1207 num++;
1208 }
Thomas Tsou865bca42013-08-21 20:58:00 -04001209 if (peak + i < corr->end()) {
Thomas Tsou3eaae802013-08-20 19:31:14 -04001210 avg += (peak + i)->norm2();
1211 num++;
1212 }
dburgessb3a0ca42011-10-12 07:44:40 +00001213 }
1214
Thomas Tsou3eaae802013-08-20 19:31:14 -04001215 if (num < 2)
Thomas Tsou865bca42013-08-21 20:58:00 -04001216 return 0.0;
dburgessb3a0ca42011-10-12 07:44:40 +00001217
Thomas Tsou3eaae802013-08-20 19:31:14 -04001218 rms = sqrtf(avg / (float) num) + 0.00001;
dburgessb3a0ca42011-10-12 07:44:40 +00001219
Thomas Tsou865bca42013-08-21 20:58:00 -04001220 return (amp.abs()) / rms;
dburgessb3a0ca42011-10-12 07:44:40 +00001221}
1222
1223bool energyDetect(signalVector &rxBurst,
1224 unsigned windowLength,
1225 float detectThreshold,
1226 float *avgPwr)
1227{
1228
1229 signalVector::const_iterator windowItr = rxBurst.begin(); //+rxBurst.size()/2 - 5*windowLength/2;
1230 float energy = 0.0;
1231 if (windowLength < 0) windowLength = 20;
1232 if (windowLength > rxBurst.size()) windowLength = rxBurst.size();
1233 for (unsigned i = 0; i < windowLength; i++) {
1234 energy += windowItr->norm2();
1235 windowItr+=4;
1236 }
1237 if (avgPwr) *avgPwr = energy/windowLength;
dburgessb3a0ca42011-10-12 07:44:40 +00001238 return (energy/windowLength > detectThreshold*detectThreshold);
1239}
dburgessb3a0ca42011-10-12 07:44:40 +00001240
Thomas Tsou865bca42013-08-21 20:58:00 -04001241/*
1242 * Detect a burst based on correlation and peak-to-average ratio
1243 *
1244 * For one sampler-per-symbol, perform fast peak detection (no interpolation)
1245 * for initial gating. We do this because energy detection should be disabled.
1246 * For higher oversampling values, we assume the energy detector is in place
1247 * and we run full interpolating peak detection.
1248 */
1249static int detectBurst(signalVector &burst,
1250 signalVector &corr, CorrelationSequence *sync,
1251 float thresh, int sps, complex *amp, float *toa,
1252 int start, int len)
dburgessb3a0ca42011-10-12 07:44:40 +00001253{
Thomas Tsou865bca42013-08-21 20:58:00 -04001254 /* Correlate */
1255 if (!convolve(&burst, sync->sequence, &corr,
Thomas Tsou3eaae802013-08-20 19:31:14 -04001256 CUSTOM, start, len, sps, 0)) {
1257 return -1;
dburgessb3a0ca42011-10-12 07:44:40 +00001258 }
1259
Thomas Tsou865bca42013-08-21 20:58:00 -04001260 /* Peak detection - place restrictions at correlation edges */
1261 *amp = fastPeakDetect(corr, toa);
Thomas Tsou3eaae802013-08-20 19:31:14 -04001262
Thomas Tsou865bca42013-08-21 20:58:00 -04001263 if ((*toa < 3 * sps) || (*toa > len - 3 * sps))
1264 return 0;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001265
Thomas Tsou865bca42013-08-21 20:58:00 -04001266 /* Peak -to-average ratio */
1267 if (computePeakRatio(&corr, sps, *toa, *amp) < thresh)
1268 return 0;
1269
1270 /* Compute peak-to-average ratio. Reject if we don't have enough values */
1271 *amp = peakDetect(corr, toa, NULL);
1272
1273 /* Normalize our channel gain */
1274 *amp = *amp / sync->gain;
1275
Thomas Tsoua57bc8a2013-09-05 08:16:47 +08001276 /* Compenate for residual rotation with dual Laurent pulse */
1277 if (sps == 4)
1278 *amp = *amp * complex(0.0, 1.0);
1279
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001280 /* Compensate for residuate time lag */
1281 *toa = *toa - sync->toa;
1282
Thomas Tsou865bca42013-08-21 20:58:00 -04001283 return 1;
1284}
1285
1286/*
1287 * RACH burst detection
1288 *
1289 * Correlation window parameters:
1290 * target: Tail bits + RACH length (reduced from 41 to a multiple of 4)
Thomas Tsoudafb3372013-09-18 16:21:26 -04001291 * head: Search 4 symbols before target
1292 * tail: Search 10 symbols after target
Thomas Tsou865bca42013-08-21 20:58:00 -04001293 */
1294int detectRACHBurst(signalVector &rxBurst,
1295 float thresh,
1296 int sps,
1297 complex *amp,
1298 float *toa)
1299{
1300 int rc, start, target, head, tail, len;
1301 float _toa;
1302 complex _amp;
1303 signalVector corr;
1304 CorrelationSequence *sync;
1305
1306 if ((sps != 1) && (sps != 4))
1307 return -1;
1308
1309 target = 8 + 40;
Thomas Tsoudafb3372013-09-18 16:21:26 -04001310 head = 4;
1311 tail = 10;
Thomas Tsou865bca42013-08-21 20:58:00 -04001312
1313 start = (target - head) * sps - 1;
1314 len = (head + tail) * sps;
1315 sync = gRACHSequence;
1316 corr = signalVector(len);
1317
1318 rc = detectBurst(rxBurst, corr, sync,
1319 thresh, sps, &_amp, &_toa, start, len);
1320 if (rc < 0) {
1321 return -1;
1322 } else if (!rc) {
1323 if (amp)
1324 *amp = 0.0f;
1325 if (toa)
1326 *toa = 0.0f;
1327 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +00001328 }
1329
Thomas Tsou865bca42013-08-21 20:58:00 -04001330 /* Subtract forward search bits from delay */
1331 if (toa)
1332 *toa = _toa - head * sps;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001333 if (amp)
Thomas Tsou865bca42013-08-21 20:58:00 -04001334 *amp = _amp;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001335
Thomas Tsou865bca42013-08-21 20:58:00 -04001336 return 1;
1337}
Thomas Tsou3eaae802013-08-20 19:31:14 -04001338
Thomas Tsou865bca42013-08-21 20:58:00 -04001339/*
1340 * Normal burst detection
1341 *
1342 * Correlation window parameters:
1343 * target: Tail + data + mid-midamble + 1/2 remaining midamblebits
Thomas Tsoudafb3372013-09-18 16:21:26 -04001344 * head: Search 4 symbols before target
1345 * tail: Search 4 symbols + maximum expected delay
Thomas Tsou865bca42013-08-21 20:58:00 -04001346 */
1347int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
1348 int sps, complex *amp, float *toa, unsigned max_toa,
1349 bool chan_req, signalVector **chan, float *chan_offset)
1350{
1351 int rc, start, target, head, tail, len;
1352 complex _amp;
1353 float _toa;
1354 signalVector corr;
1355 CorrelationSequence *sync;
1356
1357 if ((tsc < 0) || (tsc > 7) || ((sps != 1) && (sps != 4)))
1358 return -1;
1359
1360 target = 3 + 58 + 16 + 5;
Thomas Tsoudafb3372013-09-18 16:21:26 -04001361 head = 4;
1362 tail = 4 + max_toa;
Thomas Tsou865bca42013-08-21 20:58:00 -04001363
1364 start = (target - head) * sps - 1;
1365 len = (head + tail) * sps;
1366 sync = gMidambles[tsc];
1367 corr = signalVector(len);
1368
1369 rc = detectBurst(rxBurst, corr, sync,
1370 thresh, sps, &_amp, &_toa, start, len);
1371 if (rc < 0) {
1372 return -1;
1373 } else if (!rc) {
1374 if (amp)
1375 *amp = 0.0f;
1376 if (toa)
1377 *toa = 0.0f;
1378 return 0;
1379 }
1380
1381 /* Subtract forward search bits from delay */
1382 _toa -= head * sps;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001383 if (toa)
1384 *toa = _toa;
Thomas Tsou865bca42013-08-21 20:58:00 -04001385 if (amp)
1386 *amp = _amp;
Thomas Tsou3eaae802013-08-20 19:31:14 -04001387
Thomas Tsou865bca42013-08-21 20:58:00 -04001388 /* Equalization not currently supported */
Thomas Tsou3eaae802013-08-20 19:31:14 -04001389 if (chan_req) {
Thomas Tsou865bca42013-08-21 20:58:00 -04001390 *chan = new signalVector(6 * sps);
Thomas Tsou3eaae802013-08-20 19:31:14 -04001391
1392 if (chan_offset)
Thomas Tsou865bca42013-08-21 20:58:00 -04001393 *chan_offset = 0.0;
dburgessb3a0ca42011-10-12 07:44:40 +00001394 }
1395
Thomas Tsou3eaae802013-08-20 19:31:14 -04001396 return 1;
dburgessb3a0ca42011-10-12 07:44:40 +00001397}
1398
1399signalVector *decimateVector(signalVector &wVector,
1400 int decimationFactor)
1401{
1402
1403 if (decimationFactor <= 1) return NULL;
1404
1405 signalVector *decVector = new signalVector(wVector.size()/decimationFactor);
1406 decVector->isRealOnly(wVector.isRealOnly());
1407
1408 signalVector::iterator vecItr = decVector->begin();
1409 for (unsigned int i = 0; i < wVector.size();i+=decimationFactor)
1410 *vecItr++ = wVector[i];
1411
1412 return decVector;
1413}
1414
1415
Thomas Tsou83e06892013-08-20 16:10:01 -04001416SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
1417 complex channel, float TOA)
dburgessb3a0ca42011-10-12 07:44:40 +00001418{
1419 scaleVector(rxBurst,((complex) 1.0)/channel);
1420 delayVector(rxBurst,-TOA);
1421
1422 signalVector *shapedBurst = &rxBurst;
1423
1424 // shift up by a quarter of a frequency
1425 // ignore starting phase, since spec allows for discontinuous phase
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001426 GMSKReverseRotate(*shapedBurst, sps);
dburgessb3a0ca42011-10-12 07:44:40 +00001427
1428 // run through slicer
Thomas Tsoud24cc2c2013-08-20 15:41:45 -04001429 if (sps > 1) {
1430 signalVector *decShapedBurst = decimateVector(*shapedBurst, sps);
dburgessb3a0ca42011-10-12 07:44:40 +00001431 shapedBurst = decShapedBurst;
1432 }
1433
dburgessb3a0ca42011-10-12 07:44:40 +00001434 vectorSlicer(shapedBurst);
1435
1436 SoftVector *burstBits = new SoftVector(shapedBurst->size());
1437
1438 SoftVector::iterator burstItr = burstBits->begin();
1439 signalVector::iterator shapedItr = shapedBurst->begin();
1440 for (; shapedItr < shapedBurst->end(); shapedItr++)
1441 *burstItr++ = shapedItr->real();
1442
Thomas Tsoud24cc2c2013-08-20 15:41:45 -04001443 if (sps > 1)
1444 delete shapedBurst;
dburgessb3a0ca42011-10-12 07:44:40 +00001445
1446 return burstBits;
1447
1448}
dburgessb3a0ca42011-10-12 07:44:40 +00001449
dburgessb3a0ca42011-10-12 07:44:40 +00001450// Assumes symbol-spaced sampling!!!
1451// Based upon paper by Al-Dhahir and Cioffi
1452bool designDFE(signalVector &channelResponse,
1453 float SNRestimate,
1454 int Nf,
1455 signalVector **feedForwardFilter,
1456 signalVector **feedbackFilter)
1457{
1458
1459 signalVector G0(Nf);
1460 signalVector G1(Nf);
1461 signalVector::iterator G0ptr = G0.begin();
1462 signalVector::iterator G1ptr = G1.begin();
1463 signalVector::iterator chanPtr = channelResponse.begin();
1464
1465 int nu = channelResponse.size()-1;
1466
1467 *G0ptr = 1.0/sqrtf(SNRestimate);
1468 for(int j = 0; j <= nu; j++) {
1469 *G1ptr = chanPtr->conj();
1470 G1ptr++; chanPtr++;
1471 }
1472
1473 signalVector *L[Nf];
1474 signalVector::iterator Lptr;
1475 float d;
1476 for(int i = 0; i < Nf; i++) {
1477 d = G0.begin()->norm2() + G1.begin()->norm2();
1478 L[i] = new signalVector(Nf+nu);
1479 Lptr = L[i]->begin()+i;
1480 G0ptr = G0.begin(); G1ptr = G1.begin();
1481 while ((G0ptr < G0.end()) && (Lptr < L[i]->end())) {
1482 *Lptr = (*G0ptr*(G0.begin()->conj()) + *G1ptr*(G1.begin()->conj()) )/d;
1483 Lptr++;
1484 G0ptr++;
1485 G1ptr++;
1486 }
1487 complex k = (*G1.begin())/(*G0.begin());
1488
1489 if (i != Nf-1) {
1490 signalVector G0new = G1;
1491 scaleVector(G0new,k.conj());
1492 addVector(G0new,G0);
1493
1494 signalVector G1new = G0;
1495 scaleVector(G1new,k*(-1.0));
1496 addVector(G1new,G1);
1497 delayVector(G1new,-1.0);
1498
1499 scaleVector(G0new,1.0/sqrtf(1.0+k.norm2()));
1500 scaleVector(G1new,1.0/sqrtf(1.0+k.norm2()));
1501 G0 = G0new;
1502 G1 = G1new;
1503 }
1504 }
1505
1506 *feedbackFilter = new signalVector(nu);
1507 L[Nf-1]->segmentCopyTo(**feedbackFilter,Nf,nu);
1508 scaleVector(**feedbackFilter,(complex) -1.0);
1509 conjugateVector(**feedbackFilter);
1510
1511 signalVector v(Nf);
1512 signalVector::iterator vStart = v.begin();
1513 signalVector::iterator vPtr;
1514 *(vStart+Nf-1) = (complex) 1.0;
1515 for(int k = Nf-2; k >= 0; k--) {
1516 Lptr = L[k]->begin()+k+1;
1517 vPtr = vStart + k+1;
1518 complex v_k = 0.0;
1519 for (int j = k+1; j < Nf; j++) {
1520 v_k -= (*vPtr)*(*Lptr);
1521 vPtr++; Lptr++;
1522 }
1523 *(vStart + k) = v_k;
1524 }
1525
1526 *feedForwardFilter = new signalVector(Nf);
Thomas Tsou3eaae802013-08-20 19:31:14 -04001527 signalVector::iterator w = (*feedForwardFilter)->end();
dburgessb3a0ca42011-10-12 07:44:40 +00001528 for (int i = 0; i < Nf; i++) {
1529 delete L[i];
1530 complex w_i = 0.0;
1531 int endPt = ( nu < (Nf-1-i) ) ? nu : (Nf-1-i);
1532 vPtr = vStart+i;
1533 chanPtr = channelResponse.begin();
1534 for (int k = 0; k < endPt+1; k++) {
1535 w_i += (*vPtr)*(chanPtr->conj());
1536 vPtr++; chanPtr++;
1537 }
Thomas Tsou3eaae802013-08-20 19:31:14 -04001538 *--w = w_i/d;
dburgessb3a0ca42011-10-12 07:44:40 +00001539 }
1540
1541
1542 return true;
1543
1544}
1545
1546// Assumes symbol-rate sampling!!!!
1547SoftVector *equalizeBurst(signalVector &rxBurst,
1548 float TOA,
Thomas Tsoud24cc2c2013-08-20 15:41:45 -04001549 int sps,
dburgessb3a0ca42011-10-12 07:44:40 +00001550 signalVector &w, // feedforward filter
1551 signalVector &b) // feedback filter
1552{
Thomas Tsou3eaae802013-08-20 19:31:14 -04001553 signalVector *postForwardFull;
dburgessb3a0ca42011-10-12 07:44:40 +00001554
Thomas Tsou3eaae802013-08-20 19:31:14 -04001555 if (!delayVector(rxBurst, -TOA))
1556 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +00001557
Thomas Tsou3eaae802013-08-20 19:31:14 -04001558 postForwardFull = convolve(&rxBurst, &w, NULL,
1559 CUSTOM, 0, rxBurst.size() + w.size() - 1);
1560 if (!postForwardFull)
1561 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +00001562
1563 signalVector* postForward = new signalVector(rxBurst.size());
1564 postForwardFull->segmentCopyTo(*postForward,w.size()-1,rxBurst.size());
1565 delete postForwardFull;
1566
1567 signalVector::iterator dPtr = postForward->begin();
1568 signalVector::iterator dBackPtr;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001569 signalVector::iterator rotPtr = GMSKRotationN->begin();
1570 signalVector::iterator revRotPtr = GMSKReverseRotationN->begin();
dburgessb3a0ca42011-10-12 07:44:40 +00001571
1572 signalVector *DFEoutput = new signalVector(postForward->size());
1573 signalVector::iterator DFEItr = DFEoutput->begin();
1574
1575 // NOTE: can insert the midamble and/or use midamble to estimate BER
1576 for (; dPtr < postForward->end(); dPtr++) {
1577 dBackPtr = dPtr-1;
1578 signalVector::iterator bPtr = b.begin();
1579 while ( (bPtr < b.end()) && (dBackPtr >= postForward->begin()) ) {
1580 *dPtr = *dPtr + (*bPtr)*(*dBackPtr);
1581 bPtr++;
1582 dBackPtr--;
1583 }
1584 *dPtr = *dPtr * (*revRotPtr);
1585 *DFEItr = *dPtr;
1586 // make decision on symbol
1587 *dPtr = (dPtr->real() > 0.0) ? 1.0 : -1.0;
1588 //*DFEItr = *dPtr;
1589 *dPtr = *dPtr * (*rotPtr);
1590 DFEItr++;
1591 rotPtr++;
1592 revRotPtr++;
1593 }
1594
1595 vectorSlicer(DFEoutput);
1596
1597 SoftVector *burstBits = new SoftVector(postForward->size());
1598 SoftVector::iterator burstItr = burstBits->begin();
1599 DFEItr = DFEoutput->begin();
1600 for (; DFEItr < DFEoutput->end(); DFEItr++)
1601 *burstItr++ = DFEItr->real();
1602
1603 delete postForward;
1604
1605 delete DFEoutput;
1606
1607 return burstBits;
1608}
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001609
1610bool sigProcLibSetup(int sps)
1611{
1612 if ((sps != 1) && (sps != 4))
1613 return false;
1614
1615 initTrigTables();
1616 initGMSKRotationTables(sps);
1617
1618 GSMPulse1 = generateGSMPulse(1, 2);
1619 if (sps > 1)
1620 GSMPulse = generateGSMPulse(sps, 2);
1621
1622 if (!generateRACHSequence(1)) {
1623 sigProcLibDestroy();
1624 return false;
1625 }
1626
1627 return true;
1628}