diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index a205d6b6..ea493011 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -44,7 +44,9 @@ COMMON_SOURCES = \ Channelizer.cpp \ Synthesis.cpp \ proto_trxd.c \ - sch.c + sch.c \ + grgsm_vitac/grgsm_vitac.cpp \ + grgsm_vitac/viterbi_detector.cc libtransceiver_common_la_SOURCES = \ $(COMMON_SOURCES) \ @@ -66,7 +68,9 @@ noinst_HEADERS = \ ChannelizerBase.h \ Channelizer.h \ Synthesis.h \ - proto_trxd.h + proto_trxd.h \ + grgsm_vitac/viterbi_detector.h \ + grgsm_vitac/constants.h COMMON_LDADD = \ libtransceiver_common.la \ diff --git a/Transceiver52M/Transceiver2.cpp b/Transceiver52M/Transceiver2.cpp index 595508e0..41189dff 100644 --- a/Transceiver52M/Transceiver2.cpp +++ b/Transceiver52M/Transceiver2.cpp @@ -21,6 +21,7 @@ along with this program. If not, see . */ +#include "BitVector.h" #include "osmocom/core/bits.h" #include #include @@ -87,7 +88,7 @@ void TransceiverState::init(size_t slot, signalVector *burst, bool fill) fillerTable[i][slot] = filler; } } - +extern void initvita(); Transceiver2::Transceiver2(int wBasePort, const char *TRXAddress, size_t wSPS, size_t wChans, @@ -113,6 +114,8 @@ Transceiver2::Transceiver2(int wBasePort, for (int i = 0; i < 8; i++) mRxSlotMask[i] = 0; + + initvita(); } Transceiver2::~Transceiver2() @@ -480,6 +483,7 @@ bool Transceiver2::correctFCCH(TransceiverState *state, signalVector *burst) return true; } +extern int process_vita_burst(std::complex* input, int tsc, unsigned char* output_binary); /* * Pull bursts from the FIFO and handle according to the slot * and burst correlation type. Equalzation is currently disabled. @@ -494,7 +498,7 @@ SoftVector *Transceiver2::pullRadioVector(GSM::Time &wTime, int &RSSI, signalVector *burst; SoftVector *bits = NULL; TransceiverState *state = &mStates[chan]; - + bool printme = 0; GSM::Time sch_time, burst_time, diff_time; /* Blocking FIFO read */ @@ -634,13 +638,40 @@ SoftVector *Transceiver2::pullRadioVector(GSM::Time &wTime, int &RSSI, } } + if (rc < 0) goto release; + if(type == TSC){ + unsigned char outbin[148]; - /* Ignore noise threshold on MS mode for now */ - //if ((type == SCH) || (avg - state->mNoiseLev > 0.0)) - bits = demodAnyBurst(*burst, type, rx_sps, &ebp); + auto start = reinterpret_cast(burst->begin()); + for(int i=0; i < 625*2; i++) + start[i] *= 1./32767.; + + int ret = process_vita_burst(reinterpret_cast*>(burst->begin()), mTSC, outbin); + bits = new SoftVector(); + bits->resize(148); + for(int i=0; i < 148; i++) + (*bits)[i] = outbin[i] < 1 ? -1 : 1; + + // printme = ret >= 0 ? true : false; + + // if(printme) { + // std::cerr << std::endl << "vita:" << std::endl; + // for(auto i : outbin) + // std::cerr << (int) i; + // std::cerr << std::endl << "org:" << std::endl; + // } + } else { + /* Ignore noise threshold on MS mode for now */ + //if ((type == SCH) || (avg - state->mNoiseLev > 0.0)) + bits = demodAnyBurst(*burst, type, rx_sps, &ebp); + + // if(printme) + // for(int i=0; i < 148; i++) + // std::cerr << (int) (bits->operator[](i) > 0 ? 1 : 0); + } /* MS: Decode SCH and adjust GSM clock */ if ((type != TSC) && diff --git a/Transceiver52M/device/ipc/IPCDevice.h b/Transceiver52M/device/ipc/IPCDevice.h index 35279a2a..af6feb82 100644 --- a/Transceiver52M/device/ipc/IPCDevice.h +++ b/Transceiver52M/device/ipc/IPCDevice.h @@ -224,6 +224,8 @@ class IPCDevice : public RadioDevice { /** return whether user drives synchronization of Tx/Rx of USRP */ virtual GSM::Time minLatency() override; + bool setRxOffset(double wOffset, size_t chan = 0) override {return true;} + /** Return internal status values */ virtual inline double getTxFreq(size_t chan = 0) override { diff --git a/Transceiver52M/grgsm_vitac/constants.h b/Transceiver52M/grgsm_vitac/constants.h new file mode 100644 index 00000000..f678cd48 --- /dev/null +++ b/Transceiver52M/grgsm_vitac/constants.h @@ -0,0 +1,121 @@ +#pragma once + +#include + +#define gr_complex std::complex + + +#define GSM_SYMBOL_RATE (1625000.0/6.0) //symbols per second +#define GSM_SYMBOL_PERIOD (1.0/GSM_SYMBOL_RATE) //seconds per symbol + +//Burst timing +#define TAIL_BITS 3 +#define GUARD_BITS 8 +#define GUARD_FRACTIONAL 0.25 //fractional part of guard period +#define GUARD_PERIOD GUARD_BITS + GUARD_FRACTIONAL +#define DATA_BITS 57 //size of 1 data block in normal burst +#define STEALING_BIT 1 +#define N_TRAIN_BITS 26 +#define N_SYNC_BITS 64 +#define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS ) +#define FCCH_BITS USEFUL_BITS +#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS) +#define ACCESS_BURST_SIZE 88 +#define PROCESSED_CHUNK BURST_SIZE+2*GUARD_PERIOD + +#define SCH_DATA_LEN 39 +#define TS_BITS (TAIL_BITS+USEFUL_BITS+TAIL_BITS+GUARD_BITS) //a full TS (156 bits) +#define TS_PER_FRAME 8 +#define FRAME_BITS (TS_PER_FRAME * TS_BITS + 2) // 156.25 * 8 +#define FCCH_POS TAIL_BITS +#define SYNC_POS 39 +#define TRAIN_POS ( TAIL_BITS + (DATA_BITS+STEALING_BIT) + 5) //first 5 bits of a training sequence + //aren't used for channel impulse response estimation +#define TRAIN_BEGINNING 5 +#define SAFETY_MARGIN 6 // + +#define FCCH_HITS_NEEDED (USEFUL_BITS - 4) +#define FCCH_MAX_MISSES 1 +#define FCCH_MAX_FREQ_OFFSET 100 + +#define CHAN_IMP_RESP_LENGTH 5 + +#define MAX_SCH_ERRORS 10 //maximum number of subsequent sch errors after which gsm receiver goes to find_next_fcch state + +typedef enum { empty, fcch_burst, sch_burst, normal_burst, rach_burst, dummy, dummy_or_normal, normal_or_noise } burst_type; +typedef enum { unknown, multiframe_26, multiframe_51 } multiframe_type; + +static const unsigned char SYNC_BITS[] = { + 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, + 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, + 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1 +}; + +const unsigned FCCH_FRAMES[] = { 0, 10, 20, 30, 40 }; +const unsigned SCH_FRAMES[] = { 1, 11, 21, 31, 41 }; + +const unsigned BCCH_FRAMES[] = { 2, 3, 4, 5 }; //!!the receiver shouldn't care about logical + //!!channels so this will be removed from this header +const unsigned TEST_CCH_FRAMES[] = { 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49 }; +const unsigned TRAFFIC_CHANNEL_F[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; +const unsigned TEST51[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }; + + +#define TSC0 0 +#define TSC1 1 +#define TSC2 2 +#define TSC3 3 +#define TSC4 4 +#define TSC5 5 +#define TSC6 6 +#define TSC7 7 +#define TS_DUMMY 8 + +#define TRAIN_SEQ_NUM 9 + +#define TIMESLOT0 0 +#define TIMESLOT1 1 +#define TIMESLOT2 2 +#define TIMESLOT3 3 +#define TIMESLOT4 4 +#define TIMESLOT5 5 +#define TIMESLOT6 6 +#define TIMESLOT7 7 + + +static const unsigned char train_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS] = { + {0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1}, + {0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1}, + {0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0}, + {0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0}, + {0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1}, + {0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0}, + {1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1}, + {1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0}, + {0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1} // DUMMY +}; + + +//Dummy burst 0xFB 76 0A 4E 09 10 1F 1C 5C 5C 57 4A 33 39 E9 F1 2F A8 +static const unsigned char dummy_burst[] = { + 0, 0, 0, + 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, + 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, + 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, + 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 0, 0, + + 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, + 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 1, 0, 1, + + 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, + 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, + 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, + 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, + 1, 1, 1, 0, 1, 0, 1, 0, + 0, 0, 0 +}; diff --git a/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp b/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp new file mode 100644 index 00000000..a31c8bd2 --- /dev/null +++ b/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp @@ -0,0 +1,434 @@ +/* -*- c++ -*- */ +/* + * @file + * @author (C) 2009-2017 by Piotr Krysik + * @section LICENSE + * + * Gr-gsm is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * Gr-gsm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gr-gsm; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "grgsm_vitac/constants.h" +#define _CRT_SECURE_NO_WARNINGS + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + + +#include +#include +#include +#include +#include +#include + +#include "viterbi_detector.h" + + + +//signalVector mChanResp; +gr_complex d_sch_training_seq[N_SYNC_BITS]; /// channel_imp_resp(CHAN_IMP_RESP_LENGTH* d_OSR); + +void initv(); +void process(); +int +get_sch_chan_imp_resp(const gr_complex* input, + gr_complex* chan_imp_resp); +void +detect_burst(const gr_complex* input, + gr_complex* chan_imp_resp, int burst_start, + unsigned char* output_binary); +void +gmsk_mapper(const unsigned char* input, + int nitems, gr_complex* gmsk_output, gr_complex start_point) + ; +gr_complex +correlate_sequence(const gr_complex* sequence, + int length, const gr_complex* input) + ; +inline void +autocorrelation(const gr_complex* input, + gr_complex* out, int nitems) + ; +inline void +mafi(const gr_complex* input, int nitems, + gr_complex* filter, int filter_length, gr_complex* output) + ; +int +get_norm_chan_imp_resp(const gr_complex* input, + gr_complex* chan_imp_resp, float* corr_max, int bcc) + ; + + +struct fdata { + unsigned int fn; + int tn; + int bcc; + std::string fpath; + std::vector data; +}; + + +std::vector files_to_process; + +void initvita() { + + /** + * Prepare SCH sequence bits + * + * (TS_BITS + 2 * GUARD_PERIOD) + * 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)); + + /* 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); + } + +} + +int +get_sch_chan_imp_resp(const gr_complex* input, + gr_complex* chan_imp_resp) +{ + std::vector correlation_buffer; + std::vector window_energy_buffer; + std::vector power_buffer; + + int chan_imp_resp_center = 0; + int strongest_window_nr; + int burst_start; + float energy = 0; + + int len = (SYNC_POS + SYNC_SEARCH_RANGE) * d_OSR; + for (int ii = SYNC_POS * d_OSR; ii < len; ii++) { + gr_complex correlation = correlate_sequence(&d_sch_training_seq[5], + N_SYNC_BITS - 10, &input[ii]); + correlation_buffer.push_back(correlation); + power_buffer.push_back(std::pow(abs(correlation), 2)); + } + + /* Compute window energies */ + std::vector::iterator iter = power_buffer.begin(); + while (iter != power_buffer.end()) { + std::vector::iterator iter_ii = iter; + bool loop_end = false; + energy = 0; + + for (int ii = 0; ii < (d_chan_imp_length)*d_OSR; ii++, iter_ii++) { + if (iter_ii == power_buffer.end()) { + loop_end = true; + break; + } + + energy += (*iter_ii); + } + + if (loop_end) + break; + + window_energy_buffer.push_back(energy); + iter++; + } + + strongest_window_nr = max_element(window_energy_buffer.begin(), + window_energy_buffer.end()) - window_energy_buffer.begin(); + +#if 0 + d_channel_imp_resp.clear(); +#endif + + float max_correlation = 0; + for (int ii = 0; ii < (d_chan_imp_length)*d_OSR; ii++) { + gr_complex correlation = correlation_buffer[strongest_window_nr + ii]; + if (abs(correlation) > max_correlation) { + chan_imp_resp_center = ii; + max_correlation = abs(correlation); + } + +#if 0 + d_channel_imp_resp.push_back(correlation); +#endif + + chan_imp_resp[ii] = correlation; + } + + burst_start = strongest_window_nr + chan_imp_resp_center + - 48 * d_OSR - 2 * d_OSR + 2 + SYNC_POS * d_OSR; + return burst_start; +} + + +#if defined(__has_attribute) + #if __has_attribute(target_clones) + #if defined(__x86_64) + #define MULTI_VER_TARGET_ATTR __attribute__((target_clones("avx","sse4.2","sse3","sse2","sse","default"))) + #endif + #else + #define MULTI_VER_TARGET_ATTR + #endif +#endif + +MULTI_VER_TARGET_ATTR +void +detect_burst(const gr_complex* input, + gr_complex* chan_imp_resp, int burst_start, + unsigned char* output_binary) +{ + std::vector rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR); + unsigned int stop_states[2] = { 4, 12 }; + gr_complex filtered_burst[BURST_SIZE]; + gr_complex rhh[CHAN_IMP_RESP_LENGTH]; + float output[BURST_SIZE]; + int start_state = 3; + + 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); + + 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] > 0; +} + + + +int d_c0_burst_start; + +int process_vita_burst(gr_complex* input, int tsc, unsigned char* output_binary) { + unsigned int normal_burst_start, dummy_burst_start; + float dummy_corr_max, normal_corr_max; + + dummy_burst_start = get_norm_chan_imp_resp(input, + &channel_imp_resp[0], &dummy_corr_max, TS_DUMMY); + normal_burst_start = get_norm_chan_imp_resp(input, + &channel_imp_resp[0], &normal_corr_max, tsc); + + if (normal_corr_max > dummy_corr_max) { + d_c0_burst_start = normal_burst_start; + + /* Perform MLSE detection */ + detect_burst(input, &channel_imp_resp[0], + normal_burst_start, output_binary); + + return 0; + + } + else { + d_c0_burst_start = dummy_burst_start; + memcpy(output_binary, dummy_burst, 148); + //std::cerr << std::endl << "#NOPE#" << dd.fpath << std::endl << std::endl; + return -1; + } + +} + +int process_vita_sc_burst(gr_complex* input, int tsc, unsigned char* output_binary, int* offset) { + + int ncc, bcc; + int t1, t2, t3; + int rc; + + /* Get channel impulse response */ + d_c0_burst_start = get_sch_chan_imp_resp(input, + &channel_imp_resp[0]); + + /* Perform MLSE detection */ + detect_burst(input, &channel_imp_resp[0], + d_c0_burst_start, output_binary); + + /** + * Decoding was successful, now + * compute offset from burst_start, + * burst should start after a guard period. + */ + *offset = d_c0_burst_start - floor((GUARD_PERIOD) * d_OSR); + +} + +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; + + int previous_symbol = 2 * input[0] - 1; + int current_symbol; + int encoded_symbol; + + for (int i = 1; i < nitems; i++) { + /* Change bits representation to NRZ */ + current_symbol = 2 * input[i] - 1; + + /* Differentially encode */ + encoded_symbol = current_symbol * previous_symbol; + + /* And do GMSK mapping */ + 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 result(0.0, 0.0); + + for (int ii = 0; ii < length; ii++) + result += sequence[ii] * conj(input[ii * d_OSR]); + + return result / gr_complex(length, 0); +} + +/* Computes autocorrelation for positive arguments */ +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); + for (int i = k; i < nitems; i++) + out[k] += input[i] * conj(input[i - k]); + } +} + +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; + output[n] = 0; + + for (int ii = 0; ii < filter_length; ii++) { + if ((a + ii) >= nitems * d_OSR) + break; + + output[n] += input[a + ii] * filter[ii]; + } + } +} + +/* Especially computations of strongest_window_nr */ +int +get_norm_chan_imp_resp(const gr_complex* input, + gr_complex* chan_imp_resp, float* corr_max, int bcc) +{ + std::vector correlation_buffer; + std::vector window_energy_buffer; + std::vector power_buffer; + + int search_center = (int)(TRAIN_POS + 0) * d_OSR; + int search_start_pos = search_center + 1 - 5 * d_OSR; + int search_stop_pos = search_center + + d_chan_imp_length * d_OSR + 5 * d_OSR; + + for (int ii = search_start_pos; ii < search_stop_pos; ii++) { + gr_complex correlation = correlate_sequence( + &d_norm_training_seq[bcc][TRAIN_BEGINNING], + N_TRAIN_BITS - 10, &input[ii]); + correlation_buffer.push_back(correlation); + power_buffer.push_back(std::pow(abs(correlation), 2)); + } + +#if 0 + plot(power_buffer); +#endif + + /* Compute window energies */ + std::vector::iterator iter = power_buffer.begin(); + while (iter != power_buffer.end()) { + std::vector::iterator iter_ii = iter; + bool loop_end = false; + float energy = 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; + } + + energy += (*iter_ii); + } + + if (loop_end) + break; + + window_energy_buffer.push_back(energy); + iter++; + } + + /* Calculate the strongest window number */ + int strongest_window_nr = max_element(window_energy_buffer.begin(), + window_energy_buffer.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++) { + gr_complex correlation = correlation_buffer[strongest_window_nr + ii]; + if (abs(correlation) > max_correlation) + max_correlation = abs(correlation); + +#if 0 + d_channel_imp_resp.push_back(correlation); +#endif + + chan_imp_resp[ii] = correlation; + } + + *corr_max = max_correlation; + + /** + * Compute first sample position, which corresponds + * to the first sample of the impulse response + */ + return search_start_pos + strongest_window_nr - TRAIN_POS * d_OSR; +} + + + diff --git a/Transceiver52M/grgsm_vitac/viterbi_detector.cc b/Transceiver52M/grgsm_vitac/viterbi_detector.cc new file mode 100644 index 00000000..beee3863 --- /dev/null +++ b/Transceiver52M/grgsm_vitac/viterbi_detector.cc @@ -0,0 +1,392 @@ +/* -*- c++ -*- */ +/* + * @file + * @author (C) 2009 by Piotr Krysik + * @section LICENSE + * + * Gr-gsm is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * Gr-gsm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gr-gsm; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * viterbi_detector: + * This part does the detection of received sequnece. + * Employed algorithm is viterbi Maximum Likehood Sequence Estimation. + * At this moment it gives hard decisions on the output, but + * it was designed with soft decisions in mind. + * + * SYNTAX: void viterbi_detector( + * const gr_complex * input, + * unsigned int samples_num, + * gr_complex * rhh, + * unsigned int start_state, + * const unsigned int * stop_states, + * unsigned int stops_num, + * float * output) + * + * INPUT: input: Complex received signal afted matched filtering. + * samples_num: Number of samples in the input table. + * rhh: The autocorrelation of the estimated channel + * impulse response. + * start_state: Number of the start point. In GSM each burst + * starts with sequence of three bits (0,0,0) which + * indicates start point of the algorithm. + * stop_states: Table with numbers of possible stop states. + * stops_num: Number of possible stop states + * + * + * OUTPUT: output: Differentially decoded hard output of the algorithm: + * -1 for logical "0" and 1 for logical "1" + * + * SUB_FUNC: none + * + * TEST(S): Tested with real world normal burst. + */ + +#include "constants.h" +#include + +#define PATHS_NUM (1 << (CHAN_IMP_RESP_LENGTH-1)) + +void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output) +{ + float increment[8]; + float path_metrics1[16]; + float path_metrics2[16]; + float paths_difference; + float * new_path_metrics; + float * old_path_metrics; + float * tmp; + float trans_table[BURST_SIZE][16]; + float pm_candidate1, pm_candidate2; + bool real_imag; + float input_symbol_real, input_symbol_imag; + unsigned int i, sample_nr; + +/* +* Setup first path metrics, so only state pointed by start_state is possible. +* Start_state metric is equal to zero, the rest is written with some very low value, +* which makes them practically impossible to occur. +*/ + for(i=0; i max_stop_state_metric){ + max_stop_state_metric = stop_state_metric; + best_stop_state = stop_states[i]; + } + } + +/* +* This table was generated with hope that it gives a litle speedup during +* traceback stage. +* Received bit is related to the number of state in the trellis. +* I've numbered states so their parity (number of ones) is related +* to a received bit. +*/ + static const unsigned int parity_table[PATHS_NUM] = { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, }; + +/* +* Table of previous states in the trellis diagram. +* For GMSK modulation every state has two previous states. +* Example: +* previous_state_nr1 = prev_table[current_state_nr][0] +* previous_state_nr2 = prev_table[current_state_nr][1] +*/ + static const unsigned int prev_table[PATHS_NUM][2] = { {0,8}, {0,8}, {1,9}, {1,9}, {2,10}, {2,10}, {3,11}, {3,11}, {4,12}, {4,12}, {5,13}, {5,13}, {6,14}, {6,14}, {7,15}, {7,15}, }; + +/* +* Traceback and differential decoding of received sequence. +* Decisions stored in trans_table are used to restore best path in the trellis. +*/ + sample_nr=samples_num; + unsigned int state_nr=best_stop_state; + unsigned int decision; + bool out_bit=0; + + while(sample_nr>0){ + sample_nr--; + decision = (trans_table[sample_nr][state_nr]>0); + + if(decision != out_bit) + output[sample_nr]=-trans_table[sample_nr][state_nr]; + else + output[sample_nr]=trans_table[sample_nr][state_nr]; + + out_bit = out_bit ^ real_imag ^ parity_table[state_nr]; + state_nr = prev_table[state_nr][decision]; + real_imag = !real_imag; + } +} diff --git a/Transceiver52M/grgsm_vitac/viterbi_detector.h b/Transceiver52M/grgsm_vitac/viterbi_detector.h new file mode 100644 index 00000000..2595343f --- /dev/null +++ b/Transceiver52M/grgsm_vitac/viterbi_detector.h @@ -0,0 +1,64 @@ +/* -*- c++ -*- */ +/* + * @file + * @author (C) 2009 Piotr Krysik + * @section LICENSE + * + * Gr-gsm is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * Gr-gsm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gr-gsm; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +/* + * viterbi_detector: + * This part does the detection of received sequnece. + * Employed algorithm is viterbi Maximum Likehood Sequence Estimation. + * At this moment it gives hard decisions on the output, but + * it was designed with soft decisions in mind. + * + * SYNTAX: void viterbi_detector( + * const gr_complex * input, + * unsigned int samples_num, + * gr_complex * rhh, + * unsigned int start_state, + * const unsigned int * stop_states, + * unsigned int stops_num, + * float * output) + * + * INPUT: input: Complex received signal afted matched filtering. + * samples_num: Number of samples in the input table. + * rhh: The autocorrelation of the estimated channel + * impulse response. + * start_state: Number of the start point. In GSM each burst + * starts with sequence of three bits (0,0,0) which + * indicates start point of the algorithm. + * stop_states: Table with numbers of possible stop states. + * stops_num: Number of possible stop states + * + * + * OUTPUT: output: Differentially decoded hard output of the algorithm: + * -1 for logical "0" and 1 for logical "1" + * + * SUB_FUNC: none + * + * TEST(S): Tested with real world normal burst. + */ + +#ifndef INCLUDED_VITERBI_DETECTOR_H +#define INCLUDED_VITERBI_DETECTOR_H +#include "constants.h" + +void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output); + +#endif /* INCLUDED_VITERBI_DETECTOR_H */