/* -*- c++ -*- */ /* * Copyright 2002, 2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * * SPDX-License-Identifier: GPL-3.0-or-later * */ #include "atsc_single_viterbi.h" #include <cmath> namespace gr { namespace dtv { /* was_sent is a table of what symbol we get given what bit pair was sent and what state we where in [state][pair] */ const int atsc_single_viterbi::d_was_sent[4][4] = { { 0, 2, 4, 6 }, { 0, 2, 4, 6 }, { 1, 3, 5, 7 }, { 1, 3, 5, 7 }, }; /* transition_table is a table of what state we were in given current state and bit pair sent [state][pair] */ const int atsc_single_viterbi::d_transition_table[4][4] = { { 0, 2, 0, 2 }, { 2, 0, 2, 0 }, { 1, 3, 1, 3 }, { 3, 1, 3, 1 }, }; void atsc_single_viterbi::reset() { for (unsigned int i = 0; i < 2; i++) for (unsigned int j = 0; j < 4; j++) { d_path_metrics[i][j] = 0; d_traceback[i][j] = 0; } d_post_coder_state = 0; d_phase = 0; d_best_state_metric = 100000; } atsc_single_viterbi::atsc_single_viterbi() { reset(); } float atsc_single_viterbi::best_state_metric() const { return d_best_state_metric; } char atsc_single_viterbi::decode(float input) { unsigned int best_state = 0; // float best_state_metric = 100000; d_best_state_metric = 100000; /* Precompute distances from input to each possible symbol */ float distances[8] = { fabsf(input + 7), fabsf(input + 5), fabsf(input + 3), fabsf(input + 1), fabsf(input - 1), fabsf(input - 3), fabsf(input - 5), fabsf(input - 7) }; /* We start by iterating over all possible states */ for (unsigned int state = 0; state < 4; state++) { /* Next we find the most probable path from the previous states to the state we are testing, we only need to look at the 4 paths that can be taken given the 2-bit input */ int min_metric_symb = 0; float min_metric = distances[d_was_sent[state][0]] + d_path_metrics[d_phase][d_transition_table[state][0]]; for (unsigned int symbol_sent = 1; symbol_sent < 4; symbol_sent++) if ((distances[d_was_sent[state][symbol_sent]] + d_path_metrics[d_phase][d_transition_table[state][symbol_sent]]) < min_metric) { min_metric = distances[d_was_sent[state][symbol_sent]] + d_path_metrics[d_phase][d_transition_table[state][symbol_sent]]; min_metric_symb = symbol_sent; } d_path_metrics[d_phase ^ 1][state] = min_metric; d_traceback[d_phase ^ 1][state] = (((unsigned long long)min_metric_symb) << 62) | (d_traceback[d_phase][d_transition_table[state][min_metric_symb]] >> 2); /* If this is the most probable state so far remember it, this only needs to be checked when we are about to output a path so this test can be saved till later if needed, if performed later it could also be optimized with SIMD instructions. Even better this check could be eliminated as we are outputting the tail of our traceback not the head, for any head state path will tend towards the optimal path with a probability approaching 1 in just 8 or so transitions */ if (min_metric <= d_best_state_metric) { d_best_state_metric = min_metric; best_state = state; } } if (d_best_state_metric > 10000) { for (unsigned int state = 0; state < 4; state++) d_path_metrics[d_phase ^ 1][state] -= d_best_state_metric; } d_phase ^= 1; int y2 = (0x2 & d_traceback[d_phase][best_state]) >> 1; int x2 = y2 ^ d_post_coder_state; d_post_coder_state = y2; return (x2 << 1) | (0x1 & d_traceback[d_phase][best_state]); } } /* namespace dtv */ } /* namespace gr */