mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-10-23 08:22:00 +00:00
ms: add sigproclib demod
This is basically a fixed version of ttsous ancient branch that can be used instead of the VA. Required config option part of a future patchset. Change-Id: I6558992bd69f18526be5ebe7d424ca00ceb67772
This commit is contained in:
@@ -55,12 +55,15 @@ const BitVector GSM::gEdgeTrainingSequence[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
||||||
|
const BitVector GSM::gDummyBurstTSC("01110001011100010111000101");
|
||||||
|
|
||||||
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
|
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
|
||||||
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
|
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
|
||||||
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
|
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
|
||||||
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
|
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
|
||||||
|
|
||||||
|
const BitVector GSM::gSCHSynchSequence("1011100101100010000001000000111100101101010001010111011000011011");
|
||||||
|
|
||||||
// |-head-||---------midamble----------------------||--------------data----------------||t|
|
// |-head-||---------midamble----------------------||--------------data----------------||t|
|
||||||
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
||||||
|
|
||||||
|
@@ -52,11 +52,16 @@ extern const BitVector gEdgeTrainingSequence[];
|
|||||||
|
|
||||||
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
|
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
|
||||||
extern const BitVector gDummyBurst;
|
extern const BitVector gDummyBurst;
|
||||||
|
extern const BitVector gDummyBurstTSC;
|
||||||
|
|
||||||
/** Random access burst synch. sequence */
|
/** Random access burst synch. sequence */
|
||||||
extern const BitVector gRACHSynchSequenceTS0;
|
extern const BitVector gRACHSynchSequenceTS0;
|
||||||
extern const BitVector gRACHSynchSequenceTS1;
|
extern const BitVector gRACHSynchSequenceTS1;
|
||||||
extern const BitVector gRACHSynchSequenceTS2;
|
extern const BitVector gRACHSynchSequenceTS2;
|
||||||
|
|
||||||
|
/** Synchronization burst sync sequence */
|
||||||
|
extern const BitVector gSCHSynchSequence;
|
||||||
|
|
||||||
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
|
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
|
||||||
extern const BitVector gRACHBurst;
|
extern const BitVector gRACHBurst;
|
||||||
|
|
||||||
|
@@ -32,7 +32,7 @@ extern "C" {
|
|||||||
#define M_PI 3.14159265358979323846264338327f
|
#define M_PI 3.14159265358979323846264338327f
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MAX_OUTPUT_LEN 4096
|
#define MAX_OUTPUT_LEN 4096*4
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@@ -19,6 +19,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "sigProcLib.h"
|
||||||
|
#include "signalVector.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
@@ -155,12 +157,12 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
|
|||||||
auto current_gsm_time = timekeeper.gsmtime();
|
auto current_gsm_time = timekeeper.gsmtime();
|
||||||
const auto buf_len = is_first_sch_acq ? SCH_LEN_SPS : ONE_TS_BURST_LEN;
|
const auto buf_len = is_first_sch_acq ? SCH_LEN_SPS : ONE_TS_BURST_LEN;
|
||||||
const auto which_in_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer;
|
const auto which_in_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer;
|
||||||
|
memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
|
||||||
|
#if 1
|
||||||
const auto which_out_buffer = is_first_sch_acq ? sch_acq_buffer : &sch_acq_buffer[40 * 2];
|
const auto which_out_buffer = is_first_sch_acq ? sch_acq_buffer : &sch_acq_buffer[40 * 2];
|
||||||
const auto ss = reinterpret_cast<std::complex<float> *>(which_out_buffer);
|
const auto ss = reinterpret_cast<std::complex<float> *>(which_out_buffer);
|
||||||
std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
|
std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
|
||||||
|
|
||||||
int start;
|
int start;
|
||||||
memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
|
|
||||||
convert_and_scale(which_out_buffer, which_in_buffer, buf_len * 2, 1.f / float(rxFullScale));
|
convert_and_scale(which_out_buffer, which_in_buffer, buf_len * 2, 1.f / float(rxFullScale));
|
||||||
if (is_first_sch_acq) {
|
if (is_first_sch_acq) {
|
||||||
float max_corr = 0;
|
float max_corr = 0;
|
||||||
@@ -173,9 +175,22 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
|
|||||||
detect_burst_nb(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
|
detect_burst_nb(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
|
||||||
|
|
||||||
auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq);
|
auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq);
|
||||||
|
#if 0
|
||||||
|
auto burst = new signalVector(buf_len, 50);
|
||||||
|
const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL;
|
||||||
|
struct estim_burst_params ebp;
|
||||||
|
|
||||||
|
// scale like uhd, +-2k -> +-32k
|
||||||
|
convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR);
|
||||||
|
|
||||||
|
auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp);
|
||||||
|
|
||||||
|
int howmuchdelay = ebp.toa * 4;
|
||||||
|
std::cerr << "ooffs: " << howmuchdelay << " " << std::endl;
|
||||||
|
std::cerr << "voffs: " << start << " " << sch_decode_success << std::endl;
|
||||||
|
#endif
|
||||||
if (sch_decode_success) {
|
if (sch_decode_success) {
|
||||||
const auto ts_offset_symb = 0;
|
const auto ts_offset_symb = 4;
|
||||||
if (is_first_sch_acq) {
|
if (is_first_sch_acq) {
|
||||||
// update ts to first sample in sch buffer, to allow delay calc for current ts
|
// update ts to first sample in sch buffer, to allow delay calc for current ts
|
||||||
first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1;
|
first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1;
|
||||||
@@ -190,6 +205,97 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
|
|||||||
DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << start << " " << current_gsm_time.FN()
|
DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << start << " " << current_gsm_time.FN()
|
||||||
<< ":" << current_gsm_time.TN() << std::endl;
|
<< ":" << current_gsm_time.TN() << std::endl;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
const auto ts_offset_symb = 4;
|
||||||
|
auto burst = new signalVector(buf_len, 50);
|
||||||
|
const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL;
|
||||||
|
struct estim_burst_params ebp;
|
||||||
|
|
||||||
|
// scale like uhd, +-2k -> +-32k
|
||||||
|
convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR);
|
||||||
|
|
||||||
|
auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp);
|
||||||
|
|
||||||
|
int howmuchdelay = ebp.toa * 4;
|
||||||
|
|
||||||
|
if (!rv) {
|
||||||
|
delete burst;
|
||||||
|
DBGLG() << "SCH : \x1B[31m detect fail \033[0m NOOOOOOOOOOOOOOOOOO toa:" << ebp.toa << " "
|
||||||
|
<< current_gsm_time.FN() << ":" << current_gsm_time.TN() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SoftVector *bits;
|
||||||
|
if (is_first_sch_acq) {
|
||||||
|
// can't be legit with a buf size spanning _at least_ one SCH but delay that implies partial sch burst
|
||||||
|
if (howmuchdelay < 0 || (buf_len - howmuchdelay) < ONE_TS_BURST_LEN) {
|
||||||
|
delete burst;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct estim_burst_params ebp2;
|
||||||
|
// auto sch_chunk = new signalVector(ONE_TS_BURST_LEN, 50);
|
||||||
|
// auto sch_chunk_start = sch_chunk->begin();
|
||||||
|
// memcpy(sch_chunk_start, sch_buf_f.data() + howmuchdelay, sizeof(std::complex<float>) * ONE_TS_BURST_LEN);
|
||||||
|
|
||||||
|
auto delay = delayVector(burst, NULL, -howmuchdelay);
|
||||||
|
|
||||||
|
scaleVector(*delay, (complex)1.0 / ebp.amp);
|
||||||
|
|
||||||
|
auto rv2 = detectSCHBurst(*delay, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp2);
|
||||||
|
DBGLG() << "FIRST SCH : " << (rv2 ? "yes " : " ") << "Timing offset " << ebp2.toa << " symbols"
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
bits = demodAnyBurst(*delay, SCH, 4, &ebp2);
|
||||||
|
delete delay;
|
||||||
|
} else {
|
||||||
|
bits = demodAnyBurst(*burst, SCH, 4, &ebp);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete burst;
|
||||||
|
|
||||||
|
// clamp to +-1.5 because +-127 softbits scaled by 64 after -0.5 can be at most +-1.5
|
||||||
|
clamp_array(bits->begin(), 148, 1.5f);
|
||||||
|
|
||||||
|
float_to_sbit(&bits->begin()[0], (signed char *)&sch_demod_bits[0], 62, 148);
|
||||||
|
// float_to_sbit(&bits->begin()[106], &data[39], 62, 39);
|
||||||
|
|
||||||
|
if (decode_sch((char *)sch_demod_bits, is_first_sch_acq)) {
|
||||||
|
auto current_gsm_time_updated = timekeeper.gsmtime();
|
||||||
|
if (is_first_sch_acq) {
|
||||||
|
// update ts to first sample in sch buffer, to allow delay calc for current ts
|
||||||
|
first_sch_ts_start = first_sch_buf_rcv_ts + howmuchdelay - (ts_offset_symb * 4);
|
||||||
|
} else {
|
||||||
|
// continuous sch tracking, only update if off too much
|
||||||
|
auto diff = [](float x, float y) { return x > y ? x - y : y - x; };
|
||||||
|
|
||||||
|
auto d = diff(ebp.toa, ts_offset_symb);
|
||||||
|
if (abs(d) > 0.3) {
|
||||||
|
if (ebp.toa < ts_offset_symb)
|
||||||
|
ebp.toa = d;
|
||||||
|
else
|
||||||
|
ebp.toa = -d;
|
||||||
|
temp_ts_corr_offset += ebp.toa * 4;
|
||||||
|
|
||||||
|
DBGLG() << "offs: " << ebp.toa << " " << temp_ts_corr_offset << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto a = gsm_sch_check_fn(current_gsm_time_updated.FN() - 1);
|
||||||
|
auto b = gsm_sch_check_fn(current_gsm_time_updated.FN());
|
||||||
|
auto c = gsm_sch_check_fn(current_gsm_time_updated.FN() + 1);
|
||||||
|
DBGLG() << "L SCH : Timing offset " << rv << " " << ebp.toa << " " << a << b << c << "fn "
|
||||||
|
<< current_gsm_time_updated.FN() << ":" << current_gsm_time_updated.TN() << std::endl;
|
||||||
|
|
||||||
|
delete bits;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << ebp.toa << " " << current_gsm_time.FN()
|
||||||
|
<< ":" << current_gsm_time.TN() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete bits;
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -199,6 +199,7 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 1
|
||||||
convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
|
convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
|
||||||
|
|
||||||
pow = energyDetect(sv, 20 * 4 /*sps*/);
|
pow = energyDetect(sv, 20 * 4 /*sps*/);
|
||||||
@@ -232,6 +233,42 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
|||||||
// detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
|
// detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
// lower layer sch detection offset, easy to verify by just printing the detected value using both the va+sigproc code.
|
||||||
|
convert_and_scale(ss + 16, e.burst, ONE_TS_BURST_LEN * 2, 15);
|
||||||
|
|
||||||
|
pow = energyDetect(sv, 20 * 4 /*sps*/);
|
||||||
|
if (pow < -1) {
|
||||||
|
LOG(ALERT) << "Received empty burst";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
avg = sqrt(pow);
|
||||||
|
|
||||||
|
/* Detect normal or RACH bursts */
|
||||||
|
CorrType type = CorrType::TSC;
|
||||||
|
struct estim_burst_params ebp;
|
||||||
|
auto rc = detectAnyBurst(sv, mTSC, 3, 4, type, 48, &ebp);
|
||||||
|
if (rc > 0) {
|
||||||
|
type = (CorrType)rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
std::cerr << "UR : \x1B[31m rx fail \033[0m @ toa:" << ebp.toa << " " << e.gsmts.FN() << ":"
|
||||||
|
<< e.gsmts.TN() << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SoftVector *bits = demodAnyBurst(sv, type, 4, &ebp);
|
||||||
|
|
||||||
|
SoftVector::const_iterator burstItr = bits->begin();
|
||||||
|
// invert and fix to +-127 sbits
|
||||||
|
for (int ii = 0; ii < 148; ii++) {
|
||||||
|
demodded_softbits[ii] = *burstItr++ > 0.0f ? -127 : 127;
|
||||||
|
}
|
||||||
|
delete bits;
|
||||||
|
|
||||||
|
#endif
|
||||||
RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
|
RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
|
||||||
// FIXME: properly handle offset, sch/nb alignment diff? handled by lower anyway...
|
// FIXME: properly handle offset, sch/nb alignment diff? handled by lower anyway...
|
||||||
timingOffset = (int)round(0);
|
timingOffset = (int)round(0);
|
||||||
|
@@ -129,6 +129,8 @@ struct PulseSequence {
|
|||||||
static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,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 *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
|
||||||
static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL};
|
static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL};
|
||||||
|
static CorrelationSequence *gSCHSequence = NULL;
|
||||||
|
static CorrelationSequence *gDummySequence = NULL;
|
||||||
static PulseSequence *GSMPulse1 = NULL;
|
static PulseSequence *GSMPulse1 = NULL;
|
||||||
static PulseSequence *GSMPulse4 = NULL;
|
static PulseSequence *GSMPulse4 = NULL;
|
||||||
|
|
||||||
@@ -151,6 +153,12 @@ void sigProcLibDestroy()
|
|||||||
gRACHSequences[i] = NULL;
|
gRACHSequences[i] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete gSCHSequence;
|
||||||
|
gSCHSequence = NULL;
|
||||||
|
|
||||||
|
delete gDummySequence;
|
||||||
|
gDummySequence = NULL;
|
||||||
|
|
||||||
delete GMSKRotation1;
|
delete GMSKRotation1;
|
||||||
delete GMSKReverseRotation1;
|
delete GMSKReverseRotation1;
|
||||||
delete GMSKRotation4;
|
delete GMSKRotation4;
|
||||||
@@ -315,6 +323,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
|
|||||||
append = true;
|
append = true;
|
||||||
break;
|
break;
|
||||||
case CUSTOM:
|
case CUSTOM:
|
||||||
|
// FIXME: x->getstart?
|
||||||
if (start < h->size() - 1) {
|
if (start < h->size() - 1) {
|
||||||
head = h->size() - start;
|
head = h->size() - start;
|
||||||
append = true;
|
append = true;
|
||||||
@@ -1289,6 +1298,77 @@ release:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool generateDummyMidamble(int sps)
|
||||||
|
{
|
||||||
|
bool status = true;
|
||||||
|
float toa;
|
||||||
|
complex *data = NULL;
|
||||||
|
signalVector *autocorr = NULL, *midamble = NULL;
|
||||||
|
signalVector *midMidamble = NULL, *_midMidamble = NULL;
|
||||||
|
|
||||||
|
delete gDummySequence;
|
||||||
|
|
||||||
|
/* Use middle 16 bits of each TSC. Correlation sequence is not pulse shaped */
|
||||||
|
midMidamble = modulateBurst(gDummyBurstTSC.segment(5,16), 0, sps, true);
|
||||||
|
if (!midMidamble)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Simulated receive sequence is pulse shaped */
|
||||||
|
midamble = modulateBurst(gDummyBurstTSC, 0, sps, false);
|
||||||
|
if (!midamble) {
|
||||||
|
status = false;
|
||||||
|
goto release;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst,
|
||||||
|
// the ideal TSC has an + 180 degree phase shift,
|
||||||
|
// due to the pi/2 frequency shift, that
|
||||||
|
// needs to be accounted for.
|
||||||
|
// 26-midamble is 61 symbols into burst, has +90 degree phase shift.
|
||||||
|
scaleVector(*midMidamble, complex(-1.0, 0.0));
|
||||||
|
scaleVector(*midamble, complex(0.0, 1.0));
|
||||||
|
|
||||||
|
conjugateVector(*midMidamble);
|
||||||
|
|
||||||
|
/* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
|
||||||
|
data = (complex *) convolve_h_alloc(midMidamble->size());
|
||||||
|
_midMidamble = new signalVector(data, 0, midMidamble->size(), convolve_h_alloc, free);
|
||||||
|
_midMidamble->setAligned(true);
|
||||||
|
midMidamble->copyTo(*_midMidamble);
|
||||||
|
|
||||||
|
autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY);
|
||||||
|
if (!autocorr) {
|
||||||
|
status = false;
|
||||||
|
goto release;
|
||||||
|
}
|
||||||
|
|
||||||
|
gDummySequence = new CorrelationSequence;
|
||||||
|
gDummySequence->sequence = _midMidamble;
|
||||||
|
gDummySequence->gain = peakDetect(*autocorr, &toa, NULL);
|
||||||
|
|
||||||
|
/* For 1 sps only
|
||||||
|
* (Half of correlation length - 1) + midpoint of pulse shape + remainder
|
||||||
|
* 13.5 = (16 / 2 - 1) + 1.5 + (26 - 10) / 2
|
||||||
|
*/
|
||||||
|
if (sps == 1)
|
||||||
|
gDummySequence->toa = toa - 13.5;
|
||||||
|
else
|
||||||
|
gDummySequence->toa = 0;
|
||||||
|
|
||||||
|
release:
|
||||||
|
delete autocorr;
|
||||||
|
delete midamble;
|
||||||
|
delete midMidamble;
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
delete _midMidamble;
|
||||||
|
free(data);
|
||||||
|
gDummySequence = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
static CorrelationSequence *generateEdgeMidamble(int tsc)
|
static CorrelationSequence *generateEdgeMidamble(int tsc)
|
||||||
{
|
{
|
||||||
complex *data = NULL;
|
complex *data = NULL;
|
||||||
@@ -1384,6 +1464,69 @@ release:
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool generateSCHSequence(int sps)
|
||||||
|
{
|
||||||
|
bool status = true;
|
||||||
|
float toa;
|
||||||
|
complex *data = NULL;
|
||||||
|
signalVector *autocorr = NULL;
|
||||||
|
signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
|
||||||
|
|
||||||
|
delete gSCHSequence;
|
||||||
|
|
||||||
|
seq0 = modulateBurst(gSCHSynchSequence, 0, sps, false);
|
||||||
|
if (!seq0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
seq1 = modulateBurst(gSCHSynchSequence, 0, sps, true);
|
||||||
|
if (!seq1) {
|
||||||
|
status = false;
|
||||||
|
goto release;
|
||||||
|
}
|
||||||
|
|
||||||
|
conjugateVector(*seq1);
|
||||||
|
|
||||||
|
/* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
|
||||||
|
data = (complex *) convolve_h_alloc(seq1->size());
|
||||||
|
_seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free);
|
||||||
|
_seq1->setAligned(true);
|
||||||
|
seq1->copyTo(*_seq1);
|
||||||
|
|
||||||
|
autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
|
||||||
|
if (!autocorr) {
|
||||||
|
status = false;
|
||||||
|
goto release;
|
||||||
|
}
|
||||||
|
|
||||||
|
gSCHSequence = new CorrelationSequence;
|
||||||
|
gSCHSequence->sequence = _seq1;
|
||||||
|
gSCHSequence->buffer = data;
|
||||||
|
gSCHSequence->gain = peakDetect(*autocorr, &toa, NULL);
|
||||||
|
|
||||||
|
/* For 1 sps only
|
||||||
|
* (Half of correlation length - 1) + midpoint of pulse shaping filer
|
||||||
|
* 20.5 = (64 / 2 - 1) + 1.5
|
||||||
|
*/
|
||||||
|
if (sps == 1)
|
||||||
|
gSCHSequence->toa = toa - 32.5;
|
||||||
|
else
|
||||||
|
gSCHSequence->toa = 0.0;
|
||||||
|
|
||||||
|
release:
|
||||||
|
delete autocorr;
|
||||||
|
delete seq0;
|
||||||
|
delete seq1;
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
delete _seq1;
|
||||||
|
free(data);
|
||||||
|
gSCHSequence = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Peak-to-average computation +/- range from peak in symbols
|
* Peak-to-average computation +/- range from peak in symbols
|
||||||
*/
|
*/
|
||||||
@@ -1441,14 +1584,15 @@ float energyDetect(const signalVector &rxBurst, unsigned windowLength)
|
|||||||
return energy/windowLength;
|
return energy/windowLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
static signalVector *downsampleBurst(const signalVector &burst)
|
static signalVector *downsampleBurst(const signalVector &burst, int in_len = DOWNSAMPLE_IN_LEN,
|
||||||
|
int out_len = DOWNSAMPLE_OUT_LEN)
|
||||||
{
|
{
|
||||||
signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len());
|
signalVector in(in_len, dnsampler->len());
|
||||||
signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN);
|
// gSCHSequence->sequence->size(), ensure next conv has no realloc
|
||||||
burst.copyToSegment(in, 0, DOWNSAMPLE_IN_LEN);
|
signalVector *out = new signalVector(out_len, 64);
|
||||||
|
burst.copyToSegment(in, 0, in_len);
|
||||||
|
|
||||||
if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN,
|
if (dnsampler->rotate((float *)in.begin(), in_len, (float *)out->begin(), out_len) < 0) {
|
||||||
(float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) {
|
|
||||||
delete out;
|
delete out;
|
||||||
out = NULL;
|
out = NULL;
|
||||||
}
|
}
|
||||||
@@ -1469,6 +1613,12 @@ static float computeCI(const signalVector *burst, const CorrelationSequence *syn
|
|||||||
/* Integer position where the sequence starts */
|
/* Integer position where the sequence starts */
|
||||||
const int ps = start + 1 - N + (int)roundf(toa);
|
const int ps = start + 1 - N + (int)roundf(toa);
|
||||||
|
|
||||||
|
if(ps < 0) // might be -22 for toa 40 with N=64, if off by a lot during sch ms sync
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ps + N > (int)burst->size())
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Estimate Signal power */
|
/* Estimate Signal power */
|
||||||
S = 0.0f;
|
S = 0.0f;
|
||||||
for (int i=0, j=ps; i<(int)N; i++,j++)
|
for (int i=0, j=ps; i<(int)N; i++,j++)
|
||||||
@@ -1652,6 +1802,80 @@ static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int detectSCHBurst(signalVector &burst,
|
||||||
|
float thresh,
|
||||||
|
int sps,
|
||||||
|
sch_detect_type state, struct estim_burst_params *ebp)
|
||||||
|
{
|
||||||
|
int rc, start, target, head, tail, len;
|
||||||
|
complex _amp;
|
||||||
|
CorrelationSequence *sync;
|
||||||
|
|
||||||
|
if ((sps != 1) && (sps != 4))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
target = 3 + 39 + 64;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case sch_detect_type::SCH_DETECT_NARROW:
|
||||||
|
head = 4;
|
||||||
|
tail = 4;
|
||||||
|
break;
|
||||||
|
case sch_detect_type::SCH_DETECT_BUFFER:
|
||||||
|
target = 1;
|
||||||
|
head = 0;
|
||||||
|
tail = (12 * 8 * 625) / 4; // 12 frames, downsampled /4 to 1 sps
|
||||||
|
break;
|
||||||
|
case sch_detect_type::SCH_DETECT_FULL:
|
||||||
|
default:
|
||||||
|
head = target - 1;
|
||||||
|
tail = 39 + 3 + 9;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = (target - head) * 1 - 1;
|
||||||
|
len = (head + tail) * 1;
|
||||||
|
sync = gSCHSequence;
|
||||||
|
signalVector corr(len);
|
||||||
|
|
||||||
|
signalVector *dec = downsampleBurst(burst, len * 4, len);
|
||||||
|
rc = detectBurst(*dec, corr, sync, thresh, 1, start, len, ebp);
|
||||||
|
delete dec;
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
return -1;
|
||||||
|
} else if (!rc) {
|
||||||
|
ebp->amp = 0.0f;
|
||||||
|
ebp->toa = 0.0f;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == sch_detect_type::SCH_DETECT_BUFFER)
|
||||||
|
ebp->toa = ebp->toa - (3 + 39 + 64);
|
||||||
|
else {
|
||||||
|
/* Subtract forward search bits from delay */
|
||||||
|
ebp->toa = ebp->toa - head;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int detectDummyBurst(const signalVector &burst, float threshold,
|
||||||
|
int sps, unsigned max_toa, struct estim_burst_params *ebp)
|
||||||
|
{
|
||||||
|
int rc, target, head, tail;
|
||||||
|
CorrelationSequence *sync;
|
||||||
|
|
||||||
|
target = 3 + 58 + 16 + 5;
|
||||||
|
head = 10;
|
||||||
|
tail = 6 + max_toa;
|
||||||
|
sync = gDummySequence;
|
||||||
|
|
||||||
|
ebp->tsc = 0;
|
||||||
|
rc = detectGeneralBurst(burst, threshold, sps, target, head, tail, sync, ebp);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Normal burst detection
|
* Normal burst detection
|
||||||
*
|
*
|
||||||
@@ -1670,7 +1894,7 @@ static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float th
|
|||||||
return -SIGERR_UNSUPPORTED;
|
return -SIGERR_UNSUPPORTED;
|
||||||
|
|
||||||
target = 3 + 58 + 16 + 5;
|
target = 3 + 58 + 16 + 5;
|
||||||
head = 6;
|
head = 10;
|
||||||
tail = 6 + max_toa;
|
tail = 6 + max_toa;
|
||||||
sync = gMidambles[tsc];
|
sync = gMidambles[tsc];
|
||||||
|
|
||||||
@@ -1719,6 +1943,9 @@ int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold,
|
|||||||
case RACH:
|
case RACH:
|
||||||
rc = detectRACHBurst(burst, threshold, sps, max_toa, type == EXT_RACH, ebp);
|
rc = detectRACHBurst(burst, threshold, sps, max_toa, type == EXT_RACH, ebp);
|
||||||
break;
|
break;
|
||||||
|
case IDLE:
|
||||||
|
rc = detectDummyBurst(burst, threshold, sps, max_toa, ebp);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(ERR) << "Invalid correlation type";
|
LOG(ERR) << "Invalid correlation type";
|
||||||
}
|
}
|
||||||
@@ -1921,6 +2148,9 @@ bool sigProcLibSetup()
|
|||||||
generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1);
|
generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1);
|
||||||
generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1);
|
generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1);
|
||||||
|
|
||||||
|
generateSCHSequence(1);
|
||||||
|
generateDummyMidamble(1);
|
||||||
|
|
||||||
for (int tsc = 0; tsc < 8; tsc++) {
|
for (int tsc = 0; tsc < 8; tsc++) {
|
||||||
generateMidamble(1, tsc);
|
generateMidamble(1, tsc);
|
||||||
gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);
|
gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);
|
||||||
|
@@ -31,6 +31,7 @@ enum CorrType{
|
|||||||
TSC, ///< timeslot should contain a normal burst
|
TSC, ///< timeslot should contain a normal burst
|
||||||
EXT_RACH, ///< timeslot should contain an extended access burst
|
EXT_RACH, ///< timeslot should contain an extended access burst
|
||||||
RACH, ///< timeslot should contain an access burst
|
RACH, ///< timeslot should contain an access burst
|
||||||
|
SCH,
|
||||||
EDGE, ///< timeslot should contain an EDGE burst
|
EDGE, ///< timeslot should contain an EDGE burst
|
||||||
IDLE ///< timeslot is an idle (or dummy) burst
|
IDLE ///< timeslot is an idle (or dummy) burst
|
||||||
};
|
};
|
||||||
@@ -93,6 +94,8 @@ signalVector *generateDummyBurst(int sps, int tn);
|
|||||||
void scaleVector(signalVector &x,
|
void scaleVector(signalVector &x,
|
||||||
complex scale);
|
complex scale);
|
||||||
|
|
||||||
|
signalVector *delayVector(const signalVector *in, signalVector *out, float delay);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Rough energy estimator.
|
Rough energy estimator.
|
||||||
@param rxBurst A GSM burst.
|
@param rxBurst A GSM burst.
|
||||||
@@ -133,6 +136,17 @@ int detectAnyBurst(const signalVector &burst,
|
|||||||
unsigned max_toa,
|
unsigned max_toa,
|
||||||
struct estim_burst_params *ebp);
|
struct estim_burst_params *ebp);
|
||||||
|
|
||||||
|
enum class sch_detect_type {
|
||||||
|
SCH_DETECT_FULL,
|
||||||
|
SCH_DETECT_NARROW,
|
||||||
|
SCH_DETECT_BUFFER,
|
||||||
|
};
|
||||||
|
|
||||||
|
int detectSCHBurst(signalVector &rxBurst,
|
||||||
|
float detectThreshold,
|
||||||
|
int sps,
|
||||||
|
sch_detect_type state, struct estim_burst_params *ebp);
|
||||||
|
|
||||||
/** Demodulate burst basde on type and output soft bits */
|
/** Demodulate burst basde on type and output soft bits */
|
||||||
SoftVector *demodAnyBurst(const signalVector &burst, CorrType type,
|
SoftVector *demodAnyBurst(const signalVector &burst, CorrType type,
|
||||||
int sps, struct estim_burst_params *ebp);
|
int sps, struct estim_burst_params *ebp);
|
||||||
|
Reference in New Issue
Block a user