|
|
|
@@ -39,13 +39,13 @@
|
|
|
|
|
#include "viterbi_detector.h"
|
|
|
|
|
#include "grgsm_vitac.h"
|
|
|
|
|
|
|
|
|
|
//signalVector mChanResp;
|
|
|
|
|
gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a RACH burst
|
|
|
|
|
gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
|
|
|
|
|
gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
|
|
|
|
|
const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
|
|
|
|
|
|
|
|
|
|
void initvita() {
|
|
|
|
|
|
|
|
|
|
void initvita()
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* Prepare SCH sequence bits
|
|
|
|
|
*
|
|
|
|
@@ -53,59 +53,72 @@ void initvita() {
|
|
|
|
|
* Burst and two guard periods
|
|
|
|
|
* (one guard period is an arbitrary overlap)
|
|
|
|
|
*/
|
|
|
|
|
gmsk_mapper(SYNC_BITS, N_SYNC_BITS,
|
|
|
|
|
d_sch_training_seq, gr_complex(0.0, -1.0));
|
|
|
|
|
gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0));
|
|
|
|
|
for (auto &i : d_sch_training_seq)
|
|
|
|
|
i = conj(i);
|
|
|
|
|
|
|
|
|
|
/* ab */
|
|
|
|
|
gmsk_mapper(ACCESS_BITS, N_ACCESS_BITS, d_acc_training_seq, gr_complex(0.0, -1.0));
|
|
|
|
|
for (auto &i : d_acc_training_seq)
|
|
|
|
|
i = conj(i);
|
|
|
|
|
|
|
|
|
|
/* Prepare bits of training sequences */
|
|
|
|
|
for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
|
|
|
|
|
/**
|
|
|
|
|
* If first bit of the sequence is 0
|
|
|
|
|
* => first symbol is 1, else -1
|
|
|
|
|
*/
|
|
|
|
|
gr_complex startpoint = train_seq[i][0] == 0 ?
|
|
|
|
|
gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
|
|
|
|
|
gmsk_mapper(train_seq[i], N_TRAIN_BITS,
|
|
|
|
|
d_norm_training_seq[i], startpoint);
|
|
|
|
|
gr_complex startpoint = train_seq[i][0] == 0 ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
|
|
|
|
|
gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
|
|
|
|
|
for (auto &i : d_norm_training_seq[i])
|
|
|
|
|
i = conj(i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MULTI_VER_TARGET_ATTR NO_UBSAN
|
|
|
|
|
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
|
|
|
|
|
template <unsigned int burst_size>
|
|
|
|
|
NO_UBSAN static void detect_burst_generic(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start,
|
|
|
|
|
char *output_binary, int ss)
|
|
|
|
|
{
|
|
|
|
|
std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
|
|
|
|
|
unsigned int stop_states[2] = { 4, 12 };
|
|
|
|
|
gr_complex filtered_burst[BURST_SIZE];
|
|
|
|
|
gr_complex filtered_burst[burst_size];
|
|
|
|
|
gr_complex rhh[CHAN_IMP_RESP_LENGTH];
|
|
|
|
|
float output[BURST_SIZE];
|
|
|
|
|
int start_state = 3;
|
|
|
|
|
|
|
|
|
|
// if(burst_start < 0 ||burst_start > 10)
|
|
|
|
|
// fprintf(stderr, "bo %d\n", burst_start);
|
|
|
|
|
|
|
|
|
|
// burst_start = burst_start >= 0 ? burst_start : 0;
|
|
|
|
|
float output[burst_size];
|
|
|
|
|
int start_state = ss;
|
|
|
|
|
|
|
|
|
|
autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
|
|
|
|
|
for (int ii = 0; ii < d_chan_imp_length; ii++)
|
|
|
|
|
rhh[ii] = conj(rhh_temp[ii * d_OSR]);
|
|
|
|
|
|
|
|
|
|
mafi(&input[burst_start], BURST_SIZE, chan_imp_resp,
|
|
|
|
|
d_chan_imp_length * d_OSR, filtered_burst);
|
|
|
|
|
mafi(&input[burst_start], burst_size, chan_imp_resp, d_chan_imp_length * d_OSR, filtered_burst);
|
|
|
|
|
|
|
|
|
|
viterbi_detector(filtered_burst, BURST_SIZE, rhh,
|
|
|
|
|
start_state, stop_states, 2, output);
|
|
|
|
|
viterbi_detector(filtered_burst, burst_size, rhh, start_state, stop_states, 2, output);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < BURST_SIZE; i++)
|
|
|
|
|
output_binary[i] = output[i] * -127; // pre flip bits!
|
|
|
|
|
for (unsigned int i = 0; i < burst_size; i++)
|
|
|
|
|
output_binary[i] = (char)(output[i] * -127); // pre flip bits!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
gmsk_mapper(const unsigned char* input,
|
|
|
|
|
int nitems, gr_complex* gmsk_output, gr_complex start_point)
|
|
|
|
|
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
|
|
|
|
|
int ss)
|
|
|
|
|
{
|
|
|
|
|
return detect_burst_generic<BURST_SIZE>(input, chan_imp_resp, burst_start, output_binary, ss);
|
|
|
|
|
}
|
|
|
|
|
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
|
|
|
|
|
int ss)
|
|
|
|
|
{
|
|
|
|
|
return detect_burst_generic<8 + 41 + 36 + 3>(input, chan_imp_resp, burst_start, output_binary, ss);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
|
|
|
|
|
{
|
|
|
|
|
return detect_burst_nb(input, chan_imp_resp, burst_start, output_binary, 3);
|
|
|
|
|
}
|
|
|
|
|
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
|
|
|
|
|
{
|
|
|
|
|
return detect_burst_ab(input, chan_imp_resp, burst_start, output_binary, 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point)
|
|
|
|
|
{
|
|
|
|
|
gr_complex j = gr_complex(0.0, 1.0);
|
|
|
|
|
gmsk_output[0] = start_point;
|
|
|
|
@@ -122,16 +135,13 @@ gmsk_mapper(const unsigned char* input,
|
|
|
|
|
encoded_symbol = current_symbol * previous_symbol;
|
|
|
|
|
|
|
|
|
|
/* And do GMSK mapping */
|
|
|
|
|
gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0)
|
|
|
|
|
* gmsk_output[i - 1];
|
|
|
|
|
gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i - 1];
|
|
|
|
|
|
|
|
|
|
previous_symbol = current_symbol;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gr_complex
|
|
|
|
|
correlate_sequence(const gr_complex* sequence,
|
|
|
|
|
int length, const gr_complex* input)
|
|
|
|
|
gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input)
|
|
|
|
|
{
|
|
|
|
|
gr_complex result(0.0, 0.0);
|
|
|
|
|
|
|
|
|
@@ -142,9 +152,7 @@ correlate_sequence(const gr_complex* sequence,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Computes autocorrelation for positive arguments */
|
|
|
|
|
inline void
|
|
|
|
|
autocorrelation(const gr_complex* input,
|
|
|
|
|
gr_complex* out, int nitems)
|
|
|
|
|
inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems)
|
|
|
|
|
{
|
|
|
|
|
for (int k = nitems - 1; k >= 0; k--) {
|
|
|
|
|
out[k] = gr_complex(0, 0);
|
|
|
|
@@ -153,9 +161,7 @@ autocorrelation(const gr_complex* input,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void
|
|
|
|
|
mafi(const gr_complex* input, int nitems,
|
|
|
|
|
gr_complex* filter, int filter_length, gr_complex* output)
|
|
|
|
|
inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int filter_length, gr_complex *output)
|
|
|
|
|
{
|
|
|
|
|
for (int n = 0; n < nitems; n++) {
|
|
|
|
|
int a = n * d_OSR;
|
|
|
|
@@ -170,66 +176,45 @@ mafi(const gr_complex* input, int nitems,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_center, int search_start_pos,
|
|
|
|
|
int search_stop_pos, gr_complex *tseq, int tseqlen, float *corr_max)
|
|
|
|
|
int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_start_pos, int search_stop_pos,
|
|
|
|
|
gr_complex *tseq, int tseqlen, float *corr_max)
|
|
|
|
|
{
|
|
|
|
|
std::vector<gr_complex> correlation_buffer;
|
|
|
|
|
const int num_search_windows = search_stop_pos - search_start_pos;
|
|
|
|
|
const int power_search_window_len = d_chan_imp_length * d_OSR;
|
|
|
|
|
std::vector<float> window_energy_buffer;
|
|
|
|
|
std::vector<float> power_buffer;
|
|
|
|
|
std::vector<gr_complex> correlation_buffer;
|
|
|
|
|
|
|
|
|
|
for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
|
|
|
|
|
gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[ii]);
|
|
|
|
|
power_buffer.reserve(num_search_windows);
|
|
|
|
|
correlation_buffer.reserve(num_search_windows);
|
|
|
|
|
window_energy_buffer.reserve(num_search_windows);
|
|
|
|
|
|
|
|
|
|
for (int ii = 0; ii < num_search_windows; ii++) {
|
|
|
|
|
gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[search_start_pos + ii]);
|
|
|
|
|
correlation_buffer.push_back(correlation);
|
|
|
|
|
power_buffer.push_back(std::pow(abs(correlation), 2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int strongest_corr_nr = max_element(power_buffer.begin(), power_buffer.end()) - power_buffer.begin();
|
|
|
|
|
|
|
|
|
|
/* Compute window energies */
|
|
|
|
|
auto window_energy_start_offset = strongest_corr_nr - 6 * d_OSR;
|
|
|
|
|
window_energy_start_offset = window_energy_start_offset < 0 ? 0 : window_energy_start_offset; //can end up out of range..
|
|
|
|
|
auto window_energy_end_offset = strongest_corr_nr + 6 * d_OSR + d_chan_imp_length * d_OSR;
|
|
|
|
|
auto iter = power_buffer.begin() + window_energy_start_offset;
|
|
|
|
|
auto iter_end = power_buffer.begin() + window_energy_end_offset;
|
|
|
|
|
while (iter != iter_end) {
|
|
|
|
|
std::vector<float>::iterator iter_ii = iter;
|
|
|
|
|
bool loop_end = false;
|
|
|
|
|
float energy = 0;
|
|
|
|
|
float windowSum = 0;
|
|
|
|
|
|
|
|
|
|
int len = d_chan_imp_length * d_OSR;
|
|
|
|
|
for (int ii = 0; ii < len; ii++, iter_ii++) {
|
|
|
|
|
if (iter_ii == power_buffer.end()) {
|
|
|
|
|
loop_end = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// first window
|
|
|
|
|
for (int i = 0; i < power_search_window_len; i++) {
|
|
|
|
|
windowSum += power_buffer[i];
|
|
|
|
|
}
|
|
|
|
|
window_energy_buffer.push_back(windowSum);
|
|
|
|
|
|
|
|
|
|
energy += (*iter_ii);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (loop_end)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
window_energy_buffer.push_back(energy);
|
|
|
|
|
iter++;
|
|
|
|
|
// slide windows
|
|
|
|
|
for (int i = power_search_window_len; i < num_search_windows; i++) {
|
|
|
|
|
windowSum += power_buffer[i] - power_buffer[i - power_search_window_len];
|
|
|
|
|
window_energy_buffer.push_back(windowSum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Calculate the strongest window number */
|
|
|
|
|
int strongest_window_nr = window_energy_start_offset +
|
|
|
|
|
max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
|
|
|
|
|
int strongest_window_nr = std::max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
|
|
|
|
|
window_energy_buffer.begin();
|
|
|
|
|
|
|
|
|
|
// auto window_search_start = window_energy_buffer.begin() + strongest_corr_nr - 5* d_OSR;
|
|
|
|
|
// auto window_search_end = window_energy_buffer.begin() + strongest_corr_nr + 10* d_OSR;
|
|
|
|
|
// window_search_end = window_search_end >= window_energy_buffer.end() ? window_energy_buffer.end() : window_search_end;
|
|
|
|
|
|
|
|
|
|
// /* Calculate the strongest window number */
|
|
|
|
|
// int strongest_window_nr = max_element(window_search_start, window_search_end /* - d_chan_imp_length * d_OSR*/) - window_energy_buffer.begin();
|
|
|
|
|
|
|
|
|
|
// if (strongest_window_nr < 0)
|
|
|
|
|
// strongest_window_nr = 0;
|
|
|
|
|
|
|
|
|
|
float max_correlation = 0;
|
|
|
|
|
for (int ii = 0; ii < d_chan_imp_length * d_OSR; ii++) {
|
|
|
|
|
for (int ii = 0; ii < power_search_window_len; ii++) {
|
|
|
|
|
gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
|
|
|
|
|
if (abs(correlation) > max_correlation)
|
|
|
|
|
max_correlation = abs(correlation);
|
|
|
|
@@ -242,7 +227,27 @@ int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int se
|
|
|
|
|
* Compute first sample position, which corresponds
|
|
|
|
|
* to the first sample of the impulse response
|
|
|
|
|
*/
|
|
|
|
|
return search_start_pos + strongest_window_nr - search_center * d_OSR;
|
|
|
|
|
return search_start_pos + strongest_window_nr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
8 ext tail bits
|
|
|
|
|
41 sync seq
|
|
|
|
|
36 encrypted bits
|
|
|
|
|
3 tail bits
|
|
|
|
|
68.25 extended tail bits (!)
|
|
|
|
|
|
|
|
|
|
center at 8+5 (actually known tb -> known isi, start at 8?) FIXME
|
|
|
|
|
*/
|
|
|
|
|
int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay)
|
|
|
|
|
{
|
|
|
|
|
const int search_center = 8 + 5;
|
|
|
|
|
const int search_start_pos = (search_center - 5) * d_OSR + 1;
|
|
|
|
|
const int search_stop_pos = (search_center + 5 + d_chan_imp_length + max_delay) * d_OSR;
|
|
|
|
|
const auto tseq = &d_acc_training_seq[TRAIN_BEGINNING];
|
|
|
|
|
const auto tseqlen = N_ACCESS_BITS - (2 * TRAIN_BEGINNING);
|
|
|
|
|
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
|
|
|
|
|
search_center * d_OSR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -260,8 +265,8 @@ int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, f
|
|
|
|
|
const int search_stop_pos = (search_center + 5 + d_chan_imp_length) * d_OSR;
|
|
|
|
|
const auto tseq = &d_norm_training_seq[bcc][TRAIN_BEGINNING];
|
|
|
|
|
const auto tseqlen = N_TRAIN_BITS - (2 * TRAIN_BEGINNING);
|
|
|
|
|
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
|
|
|
|
|
corr_max);
|
|
|
|
|
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
|
|
|
|
|
search_center * d_OSR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
@@ -281,8 +286,9 @@ int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
|
|
|
|
|
|
|
|
|
|
// strongest_window_nr + chan_imp_resp_center + SYNC_POS *d_OSR - 48 * d_OSR - 2 * d_OSR + 2 ;
|
|
|
|
|
float corr_max;
|
|
|
|
|
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
|
|
|
|
|
&corr_max);
|
|
|
|
|
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, &corr_max) -
|
|
|
|
|
search_center * d_OSR;
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
|
|
|
|
@@ -291,9 +297,9 @@ int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_r
|
|
|
|
|
const int search_center = SYNC_POS + TRAIN_BEGINNING;
|
|
|
|
|
const int search_start_pos = 0;
|
|
|
|
|
// FIXME: proper end offset
|
|
|
|
|
const int search_stop_pos = len - (N_SYNC_BITS*8);
|
|
|
|
|
const int search_stop_pos = len - (N_SYNC_BITS * 8);
|
|
|
|
|
auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
|
|
|
|
|
|
|
|
|
|
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
|
|
|
|
|
corr_max);
|
|
|
|
|
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
|
|
|
|
|
search_center * d_OSR;
|
|
|
|
|
}
|