EDGE: Add 8-PSK modulator and demodulator

Setup correlator and detection process similar to the GMSK
receiver chain. Require 4 SPS sampling on both Rx and Tx paths
as 1 SPS sampling adds too much distoration for 8-PSK recovery.
Core receiver operations still run at 1 SPS with the exception
of fractional delay filtering, which runs at the higher rate.

Perform linear equalization to handle the Gaussian pulse
induced ISI. The fixed impulse response used for equalizer tap
calculation consists of combined EDGE pulse shape filter and
effects of the downsampling filter. Note that the non-adaptive
equalizer corrects for modulation induced band limiting and
does not account for or compensate for fading channel effects.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
This commit is contained in:
Tom Tsou 2016-03-06 03:08:01 -08:00
parent 5cd70dc4ec
commit d325343ecc
6 changed files with 548 additions and 31 deletions

View File

@ -41,6 +41,17 @@ const BitVector GSM::gTrainingSequence[] = {
BitVector("11101111000100101110111100"),
};
const BitVector GSM::gEdgeTrainingSequence[] = {
BitVector("111111001111111001111001001001111111111111001111111111001111111001111001001001"),
BitVector("111111001111001001111001001001111001001001001111111111001111001001111001001001"),
BitVector("111001111111111111001001001111001001001111001111111001111111111111001001001111"),
BitVector("111001111111111001001001001111001001111001111111111001111111111001001001001111"),
BitVector("111111111001001111001111001001001111111001111111111111111001001111001111001001"),
BitVector("111001111111001001001111001111001001111111111111111001111111001001001111001111"),
BitVector("001111001111111001001001001001111001001111111111001111001111111001001001001001"),
BitVector("001001001111001001001001111111111001111111001111001001001111001001001001111111"),
};
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");

View File

@ -46,6 +46,7 @@ namespace GSM {
/** GSM Training sequences from GSM 05.02 5.2.3. */
extern const BitVector gTrainingSequence[];
extern const BitVector gEdgeTrainingSequence[];
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
extern const BitVector gDummyBurst;

View File

@ -173,10 +173,15 @@ int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
int hist_len = filt_len - 1;
if (!check_vec_len(in_len, out_len, p, q))
return -1;
return -1;
/* Insert history */
memcpy(&in[-2 * hist_len], history, hist_len * 2 * sizeof(float));
if (history_on) {
memcpy(&in[-2 * hist_len],
history, hist_len * 2 * sizeof(float));
} else {
memset(&in[-2 * hist_len], 0,
hist_len * 2 * sizeof(float));
}
/* Generate output from precomputed input/output paths */
for (size_t i = 0; i < out_len; i++) {
@ -190,8 +195,10 @@ int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
}
/* Save history */
memcpy(history, &in[2 * (in_len - hist_len)],
hist_len * 2 * sizeof(float));
if (history_on) {
memcpy(history, &in[2 * (in_len - hist_len)],
hist_len * 2 * sizeof(float));
}
return out_len;
}
@ -221,8 +228,14 @@ size_t Resampler::len()
return filt_len;
}
void Resampler::enableHistory(bool on)
{
history_on = on;
}
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
: in_index(NULL), out_path(NULL), partitions(NULL), history(NULL)
: in_index(NULL), out_path(NULL), partitions(NULL),
history(NULL), history_on(true)
{
this->p = p;
this->q = q;

View File

@ -59,6 +59,11 @@ public:
*/
size_t len();
/*
* Enable/disable history
*/
void enableHistory(bool on);
private:
size_t p;
size_t q;
@ -68,6 +73,7 @@ private:
float **partitions;
float *history;
bool history_on;
bool initFilters(float bw);
void releaseFilters();

View File

@ -29,6 +29,7 @@
#include "sigProcLib.h"
#include "GSMCommon.h"
#include "Logger.h"
#include "Resampler.h"
extern "C" {
#include "convolve.h"
@ -63,6 +64,24 @@ static signalVector *GMSKReverseRotation1 = NULL;
/* Precomputed fractional delay filters */
static signalVector *delayFilters[DELAYFILTS];
static Complex<float> psk8_table[8] = {
Complex<float>(-0.70710678, 0.70710678),
Complex<float>( 0.0, -1.0),
Complex<float>( 0.0, 1.0),
Complex<float>( 0.70710678, -0.70710678),
Complex<float>(-1.0, 0.0),
Complex<float>(-0.70710678, -0.70710678),
Complex<float>( 0.70710678, 0.70710678),
Complex<float>( 1.0, 0.0),
};
/* Downsampling filterbank - 4 SPS to 1 SPS */
#define DOWNSAMPLE_IN_LEN 624
#define DOWNSAMPLE_OUT_LEN 156
static Resampler *dnsampler = NULL;
static signalVector *dnsampler_in = NULL;
/*
* RACH and midamble correlation waveforms. Store the buffer separately
* because we need to allocate it explicitly outside of the signal vector
@ -92,8 +111,8 @@ struct CorrelationSequence {
* for SSE instructions.
*/
struct PulseSequence {
PulseSequence() : c0(NULL), c1(NULL), empty(NULL),
c0_buffer(NULL), c1_buffer(NULL)
PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL),
c0_buffer(NULL), c1_buffer(NULL), c0_inv_buffer(NULL)
{
}
@ -101,6 +120,7 @@ struct PulseSequence {
{
delete c0;
delete c1;
delete c0_inv;
delete empty;
free(c0_buffer);
free(c1_buffer);
@ -108,21 +128,26 @@ struct PulseSequence {
signalVector *c0;
signalVector *c1;
signalVector *c0_inv;
signalVector *empty;
void *c0_buffer;
void *c1_buffer;
void *c0_inv_buffer;
};
CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
CorrelationSequence *gRACHSequence = NULL;
PulseSequence *GSMPulse1 = NULL;
PulseSequence *GSMPulse4 = NULL;
static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
static CorrelationSequence *gRACHSequence = NULL;
static PulseSequence *GSMPulse1 = NULL;
static PulseSequence *GSMPulse4 = NULL;
void sigProcLibDestroy()
{
for (int i = 0; i < 8; i++) {
delete gMidambles[i];
delete gEdgeMidambles[i];
gMidambles[i] = NULL;
gEdgeMidambles[i] = NULL;
}
for (int i = 0; i < DELAYFILTS; i++) {
@ -137,6 +162,8 @@ void sigProcLibDestroy()
delete gRACHSequence;
delete GSMPulse1;
delete GSMPulse4;
delete dnsampler;
delete dnsampler_in;
GMSKRotation1 = NULL;
GMSKRotation4 = NULL;
@ -480,6 +507,31 @@ signalVector *convolve(const signalVector *x,
return y;
}
/*
* Generate static EDGE linear equalizer. This equalizer is not adaptive.
* Filter taps are generated from the inverted 1 SPS impulse response of
* the EDGE pulse shape captured after the downsampling filter.
*/
static bool generateInvertC0Pulse(PulseSequence *pulse)
{
if (!pulse)
return false;
pulse->c0_inv_buffer = convolve_h_alloc(5);
pulse->c0_inv = new signalVector((complex *) pulse->c0_inv_buffer, 0, 5);
pulse->c0_inv->isReal(true);
pulse->c0_inv->setAligned(false);
signalVector::iterator xP = pulse->c0_inv->begin();
*xP++ = 0.15884;
*xP++ = -0.43176;
*xP++ = 1.00000;
*xP++ = -0.42608;
*xP++ = 0.14882;
return true;
}
static bool generateC1Pulse(int sps, PulseSequence *pulse)
{
int len;
@ -527,6 +579,9 @@ static PulseSequence *generateGSMPulse(int sps)
float arg, avg, center;
PulseSequence *pulse;
if ((sps != 1) && (sps != 4))
return NULL;
/* Store a single tap filter used for correlation sequence generation */
pulse = new PulseSequence();
pulse->empty = new signalVector(1);
@ -543,6 +598,7 @@ static PulseSequence *generateGSMPulse(int sps)
case 4:
len = 16;
break;
case 1:
default:
len = 4;
}
@ -590,6 +646,13 @@ static PulseSequence *generateGSMPulse(int sps)
*xP++ /= avg;
}
/*
* Current form of the EDGE equalization filter non-realizable at 4 SPS.
* Load the onto both 1 SPS and 4 SPS objects for convenience. Note that
* the EDGE demodulator downsamples to 1 SPS prior to equalization.
*/
generateInvertC0Pulse(pulse);
return pulse;
}
@ -661,8 +724,22 @@ signalVector* reverseConjugate(signalVector *b)
return tmp;
}
/* soft output slicer */
bool vectorSlicer(signalVector *x)
bool vectorSlicer(SoftVector *x)
{
SoftVector::iterator xP = x->begin();
SoftVector::iterator xPEnd = x->end();
while (xP < xPEnd) {
*xP = 0.5 * (*xP + 1.0f);
if (*xP > 1.0)
*xP = 1.0;
if (*xP < 0.0)
*xP = 0.0;
xP++;
}
return true;
}
bool vectorSlicer(signalVector *x)
{
signalVector::iterator xP = x->begin();
@ -704,6 +781,14 @@ static signalVector *rotateBurst(const BitVector &wBurst,
return shaped;
}
static void rotateBurst2(signalVector &burst, double phase)
{
Complex<float> rot = Complex<float>(cos(phase), sin(phase));
for (size_t i = 0; i < burst.size(); i++)
burst[i] = burst[i] * rot;
}
static signalVector *modulateBurstLaurent(const BitVector &bits,
int guard_len, int sps)
{
@ -791,6 +876,171 @@ static signalVector *modulateBurstLaurent(const BitVector &bits,
return c0_shaped;
}
static signalVector *rotateEdgeBurst(const signalVector &symbols, int sps)
{
signalVector *burst;
signalVector::iterator burst_itr;
burst = new signalVector(symbols.size() * sps);
burst_itr = burst->begin();
for (size_t i = 0; i < symbols.size(); i++) {
float phase = i * 3.0f * M_PI / 8.0f;
Complex<float> rot = Complex<float>(cos(phase), sin(phase));
*burst_itr = symbols[i] * rot;
burst_itr += sps;
}
return burst;
}
static signalVector *derotateEdgeBurst(const signalVector &symbols, int sps)
{
signalVector *burst;
signalVector::iterator burst_itr;
if (symbols.size() % sps)
return NULL;
burst = new signalVector(symbols.size() / sps);
burst_itr = burst->begin();
for (size_t i = 0; i < burst->size(); i++) {
float phase = (float) (i % 16) * 3.0f * M_PI / 8.0f;
Complex<float> rot = Complex<float>(cosf(phase), -sinf(phase));
*burst_itr = symbols[sps * i] * rot;
burst_itr++;
}
return burst;
}
static signalVector *mapEdgeSymbols(const BitVector &bits)
{
if (bits.size() % 3)
return NULL;
signalVector *symbols = new signalVector(bits.size() / 3);
for (size_t i = 0; i < symbols->size(); i++) {
unsigned index = (((unsigned) bits[3 * i + 0] & 0x01) << 0) |
(((unsigned) bits[3 * i + 1] & 0x01) << 1) |
(((unsigned) bits[3 * i + 2] & 0x01) << 2);
(*symbols)[i] = psk8_table[index];
}
return symbols;
}
static signalVector *shapeEdgeBurst(const signalVector &symbols)
{
size_t nsyms, nsamps = 625;
signalVector *burst, *shape;
signalVector::iterator burst_itr;
nsyms = symbols.size();
if (nsyms * 4 > nsamps)
nsyms = 156;
burst = new signalVector(nsamps, GSMPulse4->c0->size());
burst_itr = burst->begin();
for (size_t i = 0; i < nsyms; i++) {
float phase = i * 3.0f * M_PI / 8.0f;
Complex<float> rot = Complex<float>(cos(phase), sin(phase));
*burst_itr = symbols[i] * rot;
burst_itr += 4;
}
/* Single Gaussian pulse approximation shaping */
shape = convolve(burst, GSMPulse4->c0, NULL, START_ONLY);
delete burst;
return shape;
}
/*
* Generate a random 8-PSK EDGE burst. Only 4 SPS is supported with
* the returned burst being 625 samples in length.
*/
signalVector *generateEdgeBurst(int tsc)
{
int tail = 9 / 3;
int data = 174 / 3;
int train = 78 / 3;
if ((tsc < 0) || (tsc > 7))
return NULL;
signalVector *shape, *burst = new signalVector(148);
const BitVector *midamble = &gEdgeTrainingSequence[tsc];
/* Tail */
int n, i = 0;
for (; i < tail; i++)
(*burst)[i] = psk8_table[7];
/* Body */
for (; i < tail + data; i++)
(*burst)[i] = psk8_table[rand() % 8];
/* TSC */
for (n = 0; i < tail + data + train; i++, n++) {
unsigned index = (((unsigned) (*midamble)[3 * n + 0] & 0x01) << 0) |
(((unsigned) (*midamble)[3 * n + 1] & 0x01) << 1) |
(((unsigned) (*midamble)[3 * n + 2] & 0x01) << 2);
(*burst)[i] = psk8_table[index];
}
/* Body */
for (; i < tail + data + train + data; i++)
(*burst)[i] = psk8_table[rand() % 8];
/* Tail */
for (; i < tail + data + train + data + tail; i++)
(*burst)[i] = psk8_table[7];
shape = shapeEdgeBurst(*burst);
delete burst;
return shape;
}
/*
* Modulate 8-PSK burst. When empty pulse shaping (rotation only)
* is enabled, the output vector length will be bit sequence length
* times the SPS value. When pulse shaping is enabled, the output
* vector length is fixed at 625 samples (156.25 sybols at 4 SPS).
* Pulse shaped bit sequences that go beyond one burst are truncated.
* Pulse shaping at anything but 4 SPS is not supported.
*/
signalVector *modulateEdgeBurst(const BitVector &bits,
int sps, bool empty)
{
signalVector *shape, *burst;
if ((sps != 4) && !empty)
return NULL;
burst = mapEdgeSymbols(bits);
if (!burst)
return NULL;
if (empty)
shape = rotateEdgeBurst(*burst, sps);
else
shape = shapeEdgeBurst(*burst);
delete burst;
return shape;
}
static signalVector *modulateBurstBasic(const BitVector &bits,
int guard_len, int sps)
{
@ -1223,6 +1473,41 @@ release:
return status;
}
CorrelationSequence *generateEdgeMidamble(int tsc)
{
complex *data = NULL;
signalVector *midamble = NULL, *_midamble = NULL;
CorrelationSequence *seq;
if ((tsc < 0) || (tsc > 7))
return NULL;
/* Use middle 48 bits of each TSC. Correlation sequence is not pulse shaped */
const BitVector *bits = &gEdgeTrainingSequence[tsc];
midamble = modulateEdgeBurst(bits->segment(15, 48), 1, true);
if (!midamble)
return NULL;
conjugateVector(*midamble);
data = (complex *) convolve_h_alloc(midamble->size());
_midamble = new signalVector(data, 0, midamble->size());
_midamble->setAligned(true);
memcpy(_midamble->begin(), midamble->begin(),
midamble->size() * sizeof(complex));
/* Channel gain is an empirically measured value */
seq = new CorrelationSequence;
seq->buffer = data;
seq->sequence = _midamble;
seq->gain = Complex<float>(-19.6432, 19.5006) / 1.18;
seq->toa = 0;
delete midamble;
return seq;
}
static bool generateRACHSequence(int sps)
{
bool status = true;
@ -1348,12 +1633,28 @@ static int detectBurst(signalVector &burst,
float thresh, int sps, complex *amp, float *toa,
int start, int len)
{
signalVector *corr_in, *dec = NULL;
if (sps == 4) {
dec = downsampleBurst(burst);
corr_in = dec;
sps = 1;
} else {
corr_in = &burst;
}
/* Correlate */
if (!convolve(&burst, sync->sequence, &corr,
CUSTOM, start, len, sps, 0)) {
if (!convolve(corr_in, sync->sequence, &corr,
CUSTOM, start, len, 1, 0)) {
delete dec;
return -1;
}
delete dec;
/* Running at the downsampled rate at this point */
sps = 1;
/* Peak detection - place restrictions at correlation edges */
*amp = fastPeakDetect(corr, toa);
@ -1425,8 +1726,8 @@ int detectGeneralBurst(signalVector &rxBurst,
clipping = true;
}
start = (target - head) * sps - 1;
len = (head + tail) * sps;
start = target - head - 1;
len = head + tail;
corr = new signalVector(len);
rc = detectBurst(rxBurst, *corr, sync,
@ -1442,7 +1743,7 @@ int detectGeneralBurst(signalVector &rxBurst,
}
/* Subtract forward search bits from delay */
toa -= head * sps;
toa -= head;
return 1;
}
@ -1503,6 +1804,37 @@ int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
return rc;
}
int detectEdgeBurst(signalVector &rxBurst, unsigned tsc, float thresh,
int sps, complex &amp, float &toa, unsigned max_toa)
{
int rc, target, head, tail;
CorrelationSequence *sync;
if ((tsc < 0) || (tsc > 7))
return -SIGERR_UNSUPPORTED;
target = 3 + 58 + 16 + 5;
head = 5;
tail = 5 + max_toa;
sync = gEdgeMidambles[tsc];
rc = detectGeneralBurst(rxBurst, thresh, sps, amp, toa,
target, head, tail, sync);
return rc;
}
signalVector *downsampleBurst(signalVector &burst)
{
size_t ilen = DOWNSAMPLE_IN_LEN, olen = DOWNSAMPLE_OUT_LEN;
signalVector *out = new signalVector(olen);
memcpy(dnsampler_in->begin(), burst.begin(), ilen * 2 * sizeof(float));
dnsampler->rotate((float *) dnsampler_in->begin(), ilen,
(float *) out->begin(), olen);
return out;
};
signalVector *decimateVector(signalVector &wVector, size_t factor)
{
signalVector *dec;
@ -1520,27 +1852,78 @@ signalVector *decimateVector(signalVector &wVector, size_t factor)
return dec;
}
/*
* Soft 8-PSK decoding using Manhattan distance metric
*/
static SoftVector *softSliceEdgeBurst(signalVector &burst)
{
size_t nsyms = 148;
if (burst.size() < nsyms)
return NULL;
signalVector::iterator itr;
SoftVector *bits = new SoftVector(nsyms * 3);
/*
* Bits 0 and 1 - First and second bits of the symbol respectively
*/
rotateBurst2(burst, -M_PI / 8.0);
itr = burst.begin();
for (size_t i = 0; i < nsyms; i++) {
(*bits)[3 * i + 0] = -itr->imag();
(*bits)[3 * i + 1] = itr->real();
itr++;
}
/*
* Bit 2 - Collapse symbols into quadrant 0 (positive X and Y).
* Decision area is then simplified to X=Y axis. Rotate again to
* place decision boundary on X-axis.
*/
itr = burst.begin();
for (size_t i = 0; i < burst.size(); i++) {
burst[i] = Complex<float>(fabs(itr->real()), fabs(itr->imag()));
itr++;
}
rotateBurst2(burst, -M_PI / 4.0);
itr = burst.begin();
for (size_t i = 0; i < nsyms; i++) {
(*bits)[3 * i + 2] = -itr->imag();
itr++;
}
signalVector soft(bits->size());
for (size_t i = 0; i < bits->size(); i++)
soft[i] = (*bits)[i];
return bits;
}
/*
* Demodulate GSMK burst. Prior to symbol rotation, operate at
* 4 SPS (if activated) to minimize distortion through the fractional
* delay filters. Symbol rotation and after always operates at 1 SPS.
*/
SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
complex channel, float TOA)
{
signalVector *delay, *dec = NULL;
SoftVector *bits;
signalVector *delay, *dec;
scaleVector(rxBurst, ((complex) 1.0) / channel);
delay = delayVector(&rxBurst, NULL, -TOA);
delay = delayVector(&rxBurst, NULL, -TOA * (float) sps);
/* Shift up by a quarter of a frequency */
GMSKReverseRotate(*delay, sps);
/* Decimate and slice */
if (sps > 1) {
dec = decimateVector(*delay, sps);
delete delay;
delay = NULL;
if (sps == 4) {
dec = downsampleBurst(*delay);
delete delay;
} else {
dec = delay;
dec = delay;
}
/* Shift up by a quarter of a frequency */
GMSKReverseRotate(*dec, 1);
vectorSlicer(dec);
bits = new SoftVector(dec->size());
@ -1556,6 +1939,48 @@ SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
return bits;
}
/*
* Demodulate an 8-PSK burst. Prior to symbol rotation, operate at
* 4 SPS (if activated) to minimize distortion through the fractional
* delay filters. Symbol rotation and after always operates at 1 SPS.
*
* Allow 1 SPS demodulation here, but note that other parts of the
* transceiver restrict EDGE operatoin to 4 SPS - 8-PSK distortion
* through the fractional delay filters at 1 SPS renders signal
* nearly unrecoverable.
*/
SoftVector *demodEdgeBurst(signalVector &burst, int sps,
complex chan, float toa)
{
SoftVector *bits;
signalVector *delay, *dec, *rot, *eq;
if ((sps != 1) && (sps != 4))
return NULL;
scaleVector(burst, ((complex) 1.0) / chan);
delay = delayVector(&burst, NULL, -toa * (float) sps);
if (sps == 4) {
dec = downsampleBurst(*delay);
delete delay;
} else {
dec = delay;
}
eq = convolve(dec, GSMPulse4->c0_inv, NULL, NO_DELAY);
rot = derotateEdgeBurst(*eq, 1);
bits = softSliceEdgeBurst(*dec);
vectorSlicer(bits);
delete dec;
delete eq;
delete rot;
return bits;
}
bool sigProcLibSetup()
{
initTrigTables();
@ -1566,10 +1991,25 @@ bool sigProcLibSetup()
GSMPulse4 = generateGSMPulse(4);
generateRACHSequence(1);
for (int tsc = 0; tsc < 8; tsc++)
for (int tsc = 0; tsc < 8; tsc++) {
generateMidamble(1, tsc);
gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);
}
generateDelayFilters();
dnsampler = new Resampler(1, 4);
if (!dnsampler->init()) {
LOG(ALERT) << "Rx resampler failed to initialize";
goto fail;
}
dnsampler->enableHistory(false);
dnsampler_in = new signalVector(DOWNSAMPLE_IN_LEN, dnsampler->len());
return true;
fail:
sigProcLibDestroy();
return false;
}

View File

@ -104,6 +104,13 @@ signalVector *modulateBurst(const BitVector &wBurst,
int guardPeriodLength,
int sps, bool emptyPulse = false);
/** 8-PSK modulate a burst of bits */
signalVector *modulateEdgeBurst(const BitVector &bits,
int sps, bool emptyPulse = false);
/** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */
signalVector *generateEdgeBurst(int tsc);
/** Sinc function */
float sinc(float x);
@ -201,6 +208,33 @@ int analyzeTrafficBurst(signalVector &rxBurst,
float &TOA,
unsigned maxTOA);
/**
EDGE burst detector
@param burst The received GSM burst of interest
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received TSC burst.
@param TOA The estimate time-of-arrival of received TSC burst.
@param maxTOA The maximum expected time-of-arrival
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int detectEdgeBurst(signalVector &burst,
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
Downsample 4 SPS to 1 SPS using a polyphase filterbank
@param burst Input burst of at least 624 symbols
@return Decimated signal vector of 156 symbols
*/
signalVector *downsampleBurst(signalVector &burst);
/**
Decimate a vector.
@param wVector The vector of interest.
@ -220,4 +254,16 @@ signalVector *decimateVector(signalVector &wVector, size_t factor);
*/
SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
/**
Demodulate 8-PSK EDGE burst with soft symbol ooutput
@param rxBurst The burst to be demodulated.
@param sps The number of samples per GSM symbol.
@param channel The amplitude estimate of the received burst.
@param TOA The time-of-arrival of the received burst.
@return The demodulated bit sequence.
*/
SoftVector *demodEdgeBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
#endif /* SIGPROCLIB_H */