/*! * \file viterbi_decoder.cc * \brief Implementation of a Viterbi decoder class based on the Iterative Solutions * Coded Modulation Library by Matthew C. Valenti * \author Daniel Fehr 2013. daniel.co(at)bluewin.ch * * ------------------------------------------------------------------------- * * Copyright (C) 2010-2019 (see AUTHORS file for a list of contributors) * * GNSS-SDR is a software defined Global Navigation * Satellite Systems receiver * * This file is part of GNSS-SDR. * * GNSS-SDR 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 of the License, or * (at your option) any later version. * * GNSS-SDR 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 GNSS-SDR. If not, see . * * ------------------------------------------------------------------------- */ #include "viterbi_decoder.h" #include #include // for memset #include // for operator<<, basic_ostream, char_traits, endl // logging #define EVENT 2 // logs important events which don't occur every block #define FLOW 3 // logs the function calls of block processing functions #define BLOCK 4 // once per block #define SAMPLE 5 // about one log entry per sample #define LMORE 6 // many entries per sample / very specific stuff const float MAXLOG = 1e7; /* Define infinity */ Viterbi_Decoder::Viterbi_Decoder(const int g_encoder[], const int KK, const int nn) { d_nn = nn; // Coding rate 1/n d_KK = KK; // Constraint Length // derived code properties d_mm = d_KK - 1; d_states = 1U << d_mm; /* 2^mm */ d_number_symbols = 1U << d_nn; /* 2^nn */ /* create appropriate transition matrices (trellis) */ d_out0.reserve(d_states); d_out1.reserve(d_states); d_state0.reserve(d_states); d_state1.reserve(d_states); nsc_transit(d_out0.data(), d_state0.data(), 0, g_encoder, d_KK, d_nn); nsc_transit(d_out1.data(), d_state1.data(), 1, g_encoder, d_KK, d_nn); // initialise trellis state d_trellis_state_is_initialised = false; Viterbi_Decoder::init_trellis_state(); } void Viterbi_Decoder::reset() { init_trellis_state(); } /* Function decode_block() Description: Uses the Viterbi algorithm to perform hard-decision decoding of a convolutional code. Input parameters: r[] The received signal in LLR-form. For BPSK, must be in form r = 2*a*y/(sigma^2). LL The number of data bits to be decoded (doesn't include the mm zero-tail-bits) Output parameters: output_u_int[] Hard decisions on the data bits (without the mm zero-tail-bits) */ float Viterbi_Decoder::decode_block(const double input_c[], int output_u_int[], const int LL) { int state; int decoding_length_mismatch; VLOG(FLOW) << "decode_block(): LL=" << LL; // init init_trellis_state(); // do add compare select do_acs(input_c, LL + d_mm); // tail, no need to output -> traceback, but don't decode state = do_traceback(d_mm); // traceback and decode decoding_length_mismatch = do_tb_and_decode(d_mm, LL, state, output_u_int, d_indicator_metric); VLOG(FLOW) << "decoding length mismatch: " << decoding_length_mismatch; return d_indicator_metric; } float Viterbi_Decoder::decode_continuous(const double sym[], const int traceback_depth, int bits[], const int nbits_requested, int& nbits_decoded) { int state; int decoding_length_mismatch; VLOG(FLOW) << "decode_continuous(): nbits_requested=" << nbits_requested; // do add compare select do_acs(sym, nbits_requested); // the ML sequence in the newest part of the trellis can not be decoded // since it depends on the future values -> traceback, but don't decode state = do_traceback(traceback_depth); // traceback and decode decoding_length_mismatch = do_tb_and_decode(traceback_depth, nbits_requested, state, bits, d_indicator_metric); nbits_decoded = nbits_requested + decoding_length_mismatch; VLOG(FLOW) << "decoding length mismatch (continuous decoding): " << decoding_length_mismatch; return d_indicator_metric; } void Viterbi_Decoder::init_trellis_state() { int state; // if trellis state has been initialised, free old state memory if (d_trellis_state_is_initialised) { // init trellis state d_pm_t.clear(); d_rec_array.clear(); d_metric_c.clear(); } // reserve new trellis state memory d_pm_t.reserve(d_states); d_trellis_paths = std::deque(); d_rec_array.reserve(d_nn); d_metric_c.reserve(d_number_symbols); d_trellis_state_is_initialised = true; /* initialize trellis */ for (state = 0; state < d_states; state++) { d_pm_t[state] = -MAXLOG; // d_pm_t_next[state] = -MAXLOG; } d_pm_t[0] = 0; /* start in all-zeros state */ d_indicator_metric = 0; } int Viterbi_Decoder::do_acs(const double sym[], int nbits) { int t; int i; int state_at_t; float metric; float max_val; std::vector pm_t_next(d_states); /* t: * - state: state at t * - d_prev_section[state_at_t]: path metric at t for state state_at_t * - d_out0[state_at_t]: sent symbols for a data bit 0 if state is state_at_t at time t * */ for (state_at_t = 0; state_at_t < d_states; state_at_t++) { pm_t_next[state_at_t] = -MAXLOG; } /* go through trellis */ for (t = 0; t < nbits; t++) { /* Temporarily store the received symbols current decoding step */ for (i = 0; i < d_nn; i++) { d_rec_array[i] = static_cast(sym[d_nn * t + i]); } /* precompute all possible branch metrics */ for (i = 0; i < d_number_symbols; i++) { d_metric_c[i] = gamma(d_rec_array.data(), i, d_nn); VLOG(LMORE) << "metric for (tx_sym=" << i << "|ry_sym=(" << d_rec_array[0] << ", " << d_rec_array[1] << ") = " << d_metric_c[i]; } // find the survivor branches leading the trellis states at t+1 Prev next_trellis_states(d_states, t + 1); /* step through all states */ for (state_at_t = 0; state_at_t < d_states; state_at_t++) { int next_state_if_0 = d_state0[state_at_t]; int next_state_if_1 = d_state1[state_at_t]; /* hypothesis: info bit is a zero */ int bm_0 = d_metric_c[d_out0[state_at_t]]; metric = d_pm_t[state_at_t] + bm_0; // path metric + zerobranch metric /* store new metric if more than metric in storage */ if (metric > pm_t_next[next_state_if_0]) { pm_t_next[next_state_if_0] = metric; next_trellis_states.set_current_state_as_ancestor_of_next_state(next_state_if_0, state_at_t); next_trellis_states.set_decoded_bit_for_next_state(next_state_if_0, 0); next_trellis_states.set_survivor_branch_metric_of_next_state(next_state_if_0, bm_0); } /* hypothesis: info bit is a one */ int bm_1 = d_metric_c[d_out1[state_at_t]]; metric = d_pm_t[state_at_t] + bm_1; // path metric + onebranch metric /* store new metric if more than metric in storage */ if (metric > pm_t_next[next_state_if_1]) { pm_t_next[next_state_if_1] = metric; next_trellis_states.set_current_state_as_ancestor_of_next_state(next_state_if_1, state_at_t); next_trellis_states.set_decoded_bit_for_next_state(next_state_if_1, 1); next_trellis_states.set_survivor_branch_metric_of_next_state(next_state_if_1, bm_1); } } d_trellis_paths.push_front(next_trellis_states); /* normalize -> afterwards, the largest metric value is always 0 */ // max_val = 0; max_val = -MAXLOG; for (state_at_t = 0; state_at_t < d_states; state_at_t++) { if (pm_t_next[state_at_t] > max_val) { max_val = pm_t_next[state_at_t]; } } VLOG(LMORE) << "max_val at t=" << t << ": " << max_val; for (state_at_t = 0; state_at_t < d_states; state_at_t++) { d_pm_t[state_at_t] = pm_t_next[state_at_t] - max_val; pm_t_next[state_at_t] = -MAXLOG; } } return t; } int Viterbi_Decoder::do_traceback(size_t traceback_length) { // traceback_length is in bits int state; std::deque::iterator it; VLOG(FLOW) << "do_traceback(): traceback_length=" << traceback_length << std::endl; if (d_trellis_paths.size() < traceback_length) { traceback_length = d_trellis_paths.size(); } state = 0; // maybe start not at state 0, but at state with best metric for (it = d_trellis_paths.begin(); it < d_trellis_paths.begin() + traceback_length; ++it) { state = it->get_anchestor_state_of_current_state(state); } return state; } int Viterbi_Decoder::do_tb_and_decode(int traceback_length, int requested_decoding_length, int state, int output_u_int[], float& indicator_metric) { int n_of_branches_for_indicator_metric = 500; int t_out; std::deque::iterator it; int decoding_length_mismatch; int overstep_length; int n_im = 0; VLOG(FLOW) << "do_tb_and_decode(): requested_decoding_length=" << requested_decoding_length; // decode only decode_length bits -> overstep newer bits which are too much decoding_length_mismatch = d_trellis_paths.size() - (traceback_length + requested_decoding_length); VLOG(BLOCK) << "decoding_length_mismatch=" << decoding_length_mismatch; overstep_length = decoding_length_mismatch >= 0 ? decoding_length_mismatch : 0; VLOG(BLOCK) << "overstep_length=" << overstep_length; for (it = d_trellis_paths.begin() + traceback_length; it < d_trellis_paths.begin() + traceback_length + overstep_length; ++it) { state = it->get_anchestor_state_of_current_state(state); } t_out = d_trellis_paths.end() - (d_trellis_paths.begin() + traceback_length + overstep_length) - 1; // requested_decoding_length-1; indicator_metric = 0; for (it = d_trellis_paths.begin() + traceback_length + overstep_length; it < d_trellis_paths.end(); ++it) { if (it - (d_trellis_paths.begin() + traceback_length + overstep_length) < n_of_branches_for_indicator_metric) { n_im++; indicator_metric += it->get_metric_of_current_state(state); VLOG(SAMPLE) << "@t=" << it->get_t() << " b=" << it->get_bit_of_current_state(state) << " sm=" << indicator_metric << " d=" << it->get_metric_of_current_state(state); } output_u_int[t_out] = it->get_bit_of_current_state(state); state = it->get_anchestor_state_of_current_state(state); t_out--; } if (n_im > 0) { indicator_metric /= n_im; } VLOG(BLOCK) << "indicator metric: " << indicator_metric; // remove old states if (d_trellis_paths.begin() + traceback_length + overstep_length <= d_trellis_paths.end()) { d_trellis_paths.erase(d_trellis_paths.begin() + traceback_length + overstep_length, d_trellis_paths.end()); } return decoding_length_mismatch; } /* function Gamma() Description: Computes the branch metric used for decoding. Output parameters: (returned float) The metric between the hypothetical symbol and the recevieved vector Input parameters: rec_array The received vector, of length nn symbol The hypothetical symbol nn The length of the received vector This function is used by siso() */ float Viterbi_Decoder::gamma(const float rec_array[], int symbol, int nn) { float rm = 0; int i; unsigned int mask = 1U; float txsym; for (i = 0; i < nn; i++) { // if (symbol & mask) rm += rec_array[nn - i - 1]; txsym = symbol & mask ? 1 : -1; rm += txsym * rec_array[nn - i - 1]; mask = mask << 1U; } // rm = rm > 50 ? rm : -1000; return (rm); } /* function that creates the transit and output vectors */ void Viterbi_Decoder::nsc_transit(int output_p[], int trans_p[], int input, const int g[], int KK, int nn) { int nextstate[1]; int state; int states; states = (1U << (KK - 1)); /* The number of states: 2^mm */ /* Determine the output and next state for each possible starting state */ for (state = 0; state < states; state++) { output_p[state] = nsc_enc_bit(nextstate, input, state, g, KK, nn); trans_p[state] = nextstate[0]; } } /* Function nsc_enc_bit() Description: Convolutionally encodes a single bit using a rate 1/n encoder. Takes in one input bit at a time, and produces a n-bit output. Input parameters: input The input data bit (i.e. a 0 or 1). state_in The starting state of the encoder (an int from 0 to 2^m-1). g[] An n-element vector containing the code generators in binary form. KK The constraint length of the convolutional code. nn number of symbols bits per input bits (rate 1/nn) Output parameters: output_p[] An n-element vector containing the encoded bits. state_out_p[] An integer containing the final state of the encoder (i.e. the state after encoding this bit) This function is used by rsc_encode(), nsc_transit(), rsc_transit(), and nsc_transit() */ int Viterbi_Decoder::nsc_enc_bit(int state_out_p[], int input, int state_in, const int g[], int KK, int nn) { /* declare variables */ int state; int i; int out = 0; /* create a word made up of state and new input */ state = (input << (KK - 1)) ^ state_in; /* AND the word with the generators */ for (i = 0; i < nn; i++) { /* update output symbol */ out = (out << 1) + parity_counter(state & g[i], KK); } /* shift the state to make the new state */ state_out_p[0] = state >> 1; return (out); } /* function parity_counter() Description: Determines if a symbol has odd (1) or even (0) parity Output parameters: (returned int): The symbol's parity = 1 for odd and 0 for even Input parameters: symbol: The integer-valued symbol length: The highest bit position in the symbol This function is used by nsc_enc_bit(), rsc_enc_bit(), and rsc_tail() */ int Viterbi_Decoder::parity_counter(int symbol, int length) { int counter; unsigned int temp_parity = 0; for (counter = 0; counter < length; counter++) { temp_parity = temp_parity ^ (symbol & 1U); symbol = symbol >> 1U; } return (temp_parity); } // prev helper class Viterbi_Decoder::Prev::Prev(int states, int t) { this->t = t; num_states = states; state = new int[states]; bit = new int[states]; metric = new float[states]; refcount = new int; *refcount = 1; memset(state, 0, sizeof(int) * num_states); memset(bit, 0, sizeof(int) * num_states); memset(metric, 0, sizeof(float) * num_states); } // copy constructor Viterbi_Decoder::Prev::Prev(const Prev& prev) { refcount = prev.refcount; (*refcount)++; t = prev.t; state = prev.state; num_states = prev.num_states; bit = prev.bit; metric = prev.metric; VLOG(LMORE) << "Prev(" << "?" << ", " << t << ")" << " copy, new refcount = " << *refcount; } // assignment constructor Viterbi_Decoder::Prev& Viterbi_Decoder::Prev::operator=(const Prev& other) { // check for self-assignment if (&other == this) { return *this; } // handle old resources if (*refcount == 1) { // if they are not used anymore -> unallocate them delete[] state; delete[] bit; delete[] metric; delete refcount; } else { // this object is not anymore using them (*refcount)--; } // increase ref counter for this resource set refcount = other.refcount; (*refcount)++; // take over resources t = other.t; state = other.state; bit = other.bit; metric = other.metric; VLOG(LMORE) << "Prev(" << "?" << ", " << t << ")" << " assignment, new refcount = " << *refcount; return *this; } Viterbi_Decoder::Prev::~Prev() { if (*refcount == 1) { delete[] state; delete[] bit; delete[] metric; delete refcount; // std::cout << "~Prev(" << "?" << ", " << t << ")" << " destructor with delete" << std::endl; } else { (*refcount)--; VLOG(LMORE) << "~Prev(" << "?" << ", " << t << ")" << " destructor after copy, new refcount = " << *refcount; } } int Viterbi_Decoder::Prev::get_anchestor_state_of_current_state(int current_state) { // std::cout << "get prev state: for state " << current_state << " at time " << t << ", the prev state at time " << t-1 << " is " << state[current_state] << std::endl; if (num_states > current_state) { return state[current_state]; } // std::cout<<"alarm "<<"num_states="< current_state) { return bit[current_state]; } return 0; } float Viterbi_Decoder::Prev::get_metric_of_current_state(int current_state) { if (num_states > current_state) { return metric[current_state]; } return 0; } int Viterbi_Decoder::Prev::get_t() { return t; } void Viterbi_Decoder::Prev::set_current_state_as_ancestor_of_next_state(int next_state, int current_state) { if (num_states > next_state) { state[next_state] = current_state; } } void Viterbi_Decoder::Prev::set_decoded_bit_for_next_state(int next_state, int bit) { if (num_states > next_state) { this->bit[next_state] = bit; } } void Viterbi_Decoder::Prev::set_survivor_branch_metric_of_next_state(int next_state, float metric) { if (num_states > next_state) { this->metric[next_state] = metric; } }