vita demod by piotr krysik, modified

Grabbed from gr-gsm 2de47e28ce1fb9a518337bfc0add36c8e3cff5eb
Had a few rounds of extensive cleanup (not the va itself). Uses gcc
multiversioning for x86 targets.

Change-Id: I5466c522cf4de984a4810ec46df43a10b52ed78f
diff --git a/Transceiver52M/grgsm_vitac/constants.h b/Transceiver52M/grgsm_vitac/constants.h
new file mode 100644
index 0000000..07f2290
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/constants.h
@@ -0,0 +1,142 @@
+#pragma once
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009-2017  by Piotr Krysik <ptrkrysik@gmail.com>
+ * @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 <complex>
+
+#define gr_complex std::complex<float>
+
+
+#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          (TAIL_BITS + 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 0000000..bf5487a
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp
@@ -0,0 +1,299 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009-2017  by Piotr Krysik <ptrkrysik@gmail.com>
+ * @author Contributions by sysmocom - s.f.m.c. GmbH / Eric Wild <ewild@sysmocom.de>
+ * @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 "constants.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <complex>
+
+
+#include <algorithm>
+#include <string.h>
+#include <iostream>
+#include <numeric>
+#include <vector>
+#include <fstream>
+
+#include "viterbi_detector.h"
+#include "grgsm_vitac.h"
+
+//signalVector mChanResp;
+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() {
+
+	/**
+	 * 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));
+	for (auto &i : d_sch_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);
+		for (auto &i : d_norm_training_seq[i])
+			i = conj(i);
+	}
+
+}
+
+MULTI_VER_TARGET_ATTR
+void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
+{
+	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 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;
+
+	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] * -127; // pre flip bits!
+}
+
+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] * input[ii * d_OSR];
+
+	return conj(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];
+		}
+	}
+}
+
+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)
+{
+	std::vector<gr_complex> correlation_buffer;
+	std::vector<float> window_energy_buffer;
+	std::vector<float> power_buffer;
+
+	for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
+		gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[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;
+
+		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 = window_energy_start_offset +
+				  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++) {
+		gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
+		if (abs(correlation) > max_correlation)
+			max_correlation = abs(correlation);
+		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 - search_center * d_OSR;
+}
+
+/*
+
+3 + 57 + 1 + 26 + 1 + 57 + 3 + 8.25
+
+search center = 3 + 57 + 1 + 5 (due to tsc 5+16+5 split)
+this is +-5 samples around (+5 beginning) of truncated t16 tsc
+
+*/
+int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc)
+{
+	const int search_center = TRAIN_POS;
+	const int search_start_pos = (search_center - 5) * d_OSR + 1;
+	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);
+}
+
+/*
+
+3 tail | 39 data | 64 tsc | 39 data | 3 tail | 8.25 guard
+start 3+39 - 10
+end 3+39 + SYNC_SEARCH_RANGE
+
+*/
+int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
+{
+	const int search_center = SYNC_POS + TRAIN_BEGINNING;
+	const int search_start_pos = (search_center - 10) * d_OSR;
+	const int search_stop_pos = (search_center + SYNC_SEARCH_RANGE) * d_OSR;
+	const auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
+	const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
+
+	// 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);
+}
+
+int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
+{
+	const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
+	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);
+	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);
+}
\ No newline at end of file
diff --git a/Transceiver52M/grgsm_vitac/grgsm_vitac.h b/Transceiver52M/grgsm_vitac/grgsm_vitac.h
new file mode 100644
index 0000000..8ba818b
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/grgsm_vitac.h
@@ -0,0 +1,72 @@
+#pragma once
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009-2017  by Piotr Krysik <ptrkrysik@gmail.com>
+ * @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 <vector>
+#include "constants.h"
+
+/* may only be used for for the DEFINITIONS!
+* see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91664
+*/
+#if defined(__has_attribute)
+#if __has_attribute(target_clones) && defined(__x86_64) && true
+#define MULTI_VER_TARGET_ATTR __attribute__((target_clones("avx", "sse4.2", "sse3", "sse2", "sse", "default")))
+#else
+#define MULTI_VER_TARGET_ATTR
+#endif
+#endif
+
+/* ... but apaprently clang disagrees... */
+#if defined(__clang__)
+#define MULTI_VER_TARGET_ATTR_CLANGONLY MULTI_VER_TARGET_ATTR
+#else
+#define MULTI_VER_TARGET_ATTR_CLANGONLY
+#endif
+
+#define SYNC_SEARCH_RANGE 30
+const int d_OSR(4);
+
+void initvita();
+
+int process_vita_burst(gr_complex *input, int tsc, unsigned char *output_binary);
+int process_vita_sc_burst(gr_complex *input, int tsc, unsigned char *output_binary, int *offset);
+
+MULTI_VER_TARGET_ATTR_CLANGONLY
+void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, 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_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp);
+int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc);
+int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max);
+
+enum class btype { NB, SCH };
+struct fdata {
+	btype t;
+	unsigned int fn;
+	int tn;
+	int bcc;
+	std::string fpath;
+	std::vector<gr_complex> data;
+	unsigned int data_start_offset;
+};
\ No newline at end of file
diff --git a/Transceiver52M/grgsm_vitac/viterbi_detector.cc b/Transceiver52M/grgsm_vitac/viterbi_detector.cc
new file mode 100644
index 0000000..4ac4505
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/viterbi_detector.cc
@@ -0,0 +1,392 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009 by Piotr Krysik <ptrkrysik@gmail.com>
+ * @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 <cmath>
+
+#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<PATHS_NUM; i++){
+      path_metrics1[i]=(-10e30);
+   }
+   path_metrics1[start_state]=0;
+
+/*
+* Compute Increment - a table of values which does not change for subsequent input samples.
+* Increment is table of reference levels for computation of branch metrics:
+*    branch metric = (+/-)received_sample (+/-) reference_level
+*/
+   increment[0] = -rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
+   increment[1] = rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
+   increment[2] = -rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
+   increment[3] = rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
+   increment[4] = -rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
+   increment[5] = rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
+   increment[6] = -rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
+   increment[7] = rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
+
+
+/*
+* Computation of path metrics and decisions (Add-Compare-Select).
+* It's composed of two parts: one for odd input samples (imaginary numbers)
+* and one for even samples (real numbers).
+* Each part is composed of independent (parallelisable) statements like  
+* this one:
+*      pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2];
+*      pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5];
+*      paths_difference=pm_candidate2-pm_candidate1;
+*      new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+*      trans_table[sample_nr][1] = paths_difference;
+* This is very good point for optimisations (SIMD or OpenMP) as it's most time 
+* consuming part of this function. 
+*/
+   sample_nr=0;
+   old_path_metrics=path_metrics1;
+   new_path_metrics=path_metrics2;
+   while(sample_nr<samples_num){
+      //Processing imag states
+      real_imag=1;
+      input_symbol_imag = input[sample_nr].imag();
+
+      pm_candidate1 = old_path_metrics[0] +input_symbol_imag -increment[2];
+      pm_candidate2 = old_path_metrics[8] +input_symbol_imag +increment[5];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][0] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2];
+      pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][1] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[1] +input_symbol_imag -increment[3];
+      pm_candidate2 = old_path_metrics[9] +input_symbol_imag +increment[4];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][2] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[1] -input_symbol_imag +increment[3];
+      pm_candidate2 = old_path_metrics[9] -input_symbol_imag -increment[4];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][3] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[2] +input_symbol_imag -increment[0];
+      pm_candidate2 = old_path_metrics[10] +input_symbol_imag +increment[7];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][4] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[2] -input_symbol_imag +increment[0];
+      pm_candidate2 = old_path_metrics[10] -input_symbol_imag -increment[7];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][5] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[3] +input_symbol_imag -increment[1];
+      pm_candidate2 = old_path_metrics[11] +input_symbol_imag +increment[6];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][6] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[3] -input_symbol_imag +increment[1];
+      pm_candidate2 = old_path_metrics[11] -input_symbol_imag -increment[6];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][7] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[4] +input_symbol_imag -increment[6];
+      pm_candidate2 = old_path_metrics[12] +input_symbol_imag +increment[1];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][8] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[4] -input_symbol_imag +increment[6];
+      pm_candidate2 = old_path_metrics[12] -input_symbol_imag -increment[1];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][9] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[5] +input_symbol_imag -increment[7];
+      pm_candidate2 = old_path_metrics[13] +input_symbol_imag +increment[0];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][10] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[5] -input_symbol_imag +increment[7];
+      pm_candidate2 = old_path_metrics[13] -input_symbol_imag -increment[0];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][11] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[6] +input_symbol_imag -increment[4];
+      pm_candidate2 = old_path_metrics[14] +input_symbol_imag +increment[3];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][12] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[6] -input_symbol_imag +increment[4];
+      pm_candidate2 = old_path_metrics[14] -input_symbol_imag -increment[3];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][13] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[7] +input_symbol_imag -increment[5];
+      pm_candidate2 = old_path_metrics[15] +input_symbol_imag +increment[2];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][14] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[7] -input_symbol_imag +increment[5];
+      pm_candidate2 = old_path_metrics[15] -input_symbol_imag -increment[2];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][15] = paths_difference;
+      tmp=old_path_metrics;
+      old_path_metrics=new_path_metrics;
+      new_path_metrics=tmp;
+
+      sample_nr++;
+      if(sample_nr==samples_num)
+         break;
+
+      //Processing real states
+      real_imag=0;
+      input_symbol_real = input[sample_nr].real();
+
+      pm_candidate1 = old_path_metrics[0] -input_symbol_real -increment[7];
+      pm_candidate2 = old_path_metrics[8] -input_symbol_real +increment[0];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][0] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[0] +input_symbol_real +increment[7];
+      pm_candidate2 = old_path_metrics[8] +input_symbol_real -increment[0];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][1] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[1] -input_symbol_real -increment[6];
+      pm_candidate2 = old_path_metrics[9] -input_symbol_real +increment[1];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][2] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[1] +input_symbol_real +increment[6];
+      pm_candidate2 = old_path_metrics[9] +input_symbol_real -increment[1];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][3] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[2] -input_symbol_real -increment[5];
+      pm_candidate2 = old_path_metrics[10] -input_symbol_real +increment[2];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][4] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[2] +input_symbol_real +increment[5];
+      pm_candidate2 = old_path_metrics[10] +input_symbol_real -increment[2];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][5] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[3] -input_symbol_real -increment[4];
+      pm_candidate2 = old_path_metrics[11] -input_symbol_real +increment[3];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][6] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[3] +input_symbol_real +increment[4];
+      pm_candidate2 = old_path_metrics[11] +input_symbol_real -increment[3];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][7] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[4] -input_symbol_real -increment[3];
+      pm_candidate2 = old_path_metrics[12] -input_symbol_real +increment[4];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][8] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[4] +input_symbol_real +increment[3];
+      pm_candidate2 = old_path_metrics[12] +input_symbol_real -increment[4];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][9] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[5] -input_symbol_real -increment[2];
+      pm_candidate2 = old_path_metrics[13] -input_symbol_real +increment[5];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][10] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[5] +input_symbol_real +increment[2];
+      pm_candidate2 = old_path_metrics[13] +input_symbol_real -increment[5];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][11] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[6] -input_symbol_real -increment[1];
+      pm_candidate2 = old_path_metrics[14] -input_symbol_real +increment[6];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][12] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[6] +input_symbol_real +increment[1];
+      pm_candidate2 = old_path_metrics[14] +input_symbol_real -increment[6];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][13] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[7] -input_symbol_real -increment[0];
+      pm_candidate2 = old_path_metrics[15] -input_symbol_real +increment[7];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][14] = paths_difference;
+
+      pm_candidate1 = old_path_metrics[7] +input_symbol_real +increment[0];
+      pm_candidate2 = old_path_metrics[15] +input_symbol_real -increment[7];
+      paths_difference=pm_candidate2-pm_candidate1;
+      new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+      trans_table[sample_nr][15] = paths_difference;
+
+      tmp=old_path_metrics;
+      old_path_metrics=new_path_metrics;
+      new_path_metrics=tmp;
+
+      sample_nr++;
+   }
+
+/*
+* Find the best from the stop states by comparing their path metrics.
+* Not every stop state is always possible, so we are searching in
+* a subset of them.
+*/
+   unsigned int best_stop_state;
+   float stop_state_metric, max_stop_state_metric;
+   best_stop_state = stop_states[0];
+   max_stop_state_metric = old_path_metrics[best_stop_state];
+   for(i=1; i< stops_num; i++){
+      stop_state_metric = old_path_metrics[stop_states[i]];
+      if(stop_state_metric > 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 0000000..92bc2a8
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/viterbi_detector.h
@@ -0,0 +1,64 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009 Piotr Krysik <ptrkrysik@gmail.com>
+ * @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 */