/* -*- c++ -*- */
/*
 * Copyright 2023 jmfriedt.
 *
 * This 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.
 *
 * This software 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 this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <gnuradio/io_signature.h>
#include "m17_decoder_impl.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>

#include "m17.h"

#define CODE_MEAN      -0.75	// mean(str_sync_symbols)
#define CODE_STD        8.21583836f	//std(str_sync_symbols)*sqrt(length(str_sync_symbols)-1)

namespace gr
{
  namespace m17
  {

    m17_decoder::sptr
      m17_decoder::make (bool debug_data, bool debug_ctrl, float threshold,
			 bool callsign, bool signed_str, int encr_type,
			 std::string key, std::string seed)
    {
      return gnuradio::get_initial_sptr
	(new
	 m17_decoder_impl (debug_data, debug_ctrl, threshold, callsign,
			   signed_str, encr_type, key, seed));
    }


    /*
     * The private constructor
     */
    m17_decoder_impl::m17_decoder_impl (bool debug_data, bool debug_ctrl,
					float threshold, bool callsign,
					bool signed_str, int encr_type,
					std::string key, std::string seed):
             gr::block ("m17_decoder", 
             gr::io_signature::make (1, 1, sizeof (float)),
	     gr::io_signature::make (1, 1, sizeof (char))),
      _debug_data (debug_data), _debug_ctrl (debug_ctrl),
      _threshold (threshold), _callsign (callsign), _signed_str (signed_str)
    {
      set_debug_data (debug_data);
      set_debug_ctrl (debug_ctrl);
      set_threshold (threshold);
      set_callsign (callsign);
      set_signed (signed_str);
      set_key (key);
      set_encr_type (encr_type);
      _expected_next_fn = 0;
    }

    /*
     * Our virtual destructor.
     */
    m17_decoder_impl::~m17_decoder_impl ()
    {
    }

    void m17_decoder_impl::set_threshold (float threshold)
    {
      _threshold = threshold;
      printf ("Threshold: %f\n", _threshold);
    }

    void m17_decoder_impl::set_debug_data (bool debug)
    {
      _debug_data = debug;
      if (_debug_data == true)
	printf ("Data debug: true\n");
      else
	printf ("Data debug: false\n");
    }

    void m17_decoder_impl::set_debug_ctrl (bool debug)
    {
      _debug_ctrl = debug;
      if (_debug_ctrl == true)
	printf ("Debug control: true\n");
      else
	printf ("Debug control: false\n");
    }

    void m17_decoder_impl::set_encr_type (int encr_type)
    {
      switch (encr_type)
      {case 0:_encr_type=ENCR_NONE;break;
       case 1:_encr_type=ENCR_SCRAM;break;
       case 2:_encr_type=ENCR_AES;break;
       case 3:_encr_type=ENCR_RES;break;
       default:_encr_type=ENCR_NONE;
      }
      printf ("new encr type: %x -> ", _encr_type);
    }

    void m17_decoder_impl::set_callsign (bool callsign)
    {
      _callsign = callsign;
      if (_callsign == true)
	printf ("Display callsign\n");
      else
	printf ("Do not display callsign\n");
    }

    void m17_decoder_impl::set_signed (bool signed_str)
    {
      _signed_str = signed_str;
      if (_callsign == true)
	printf ("Signed\n");
      else
	printf ("Unsigned\n");
    }

    void m17_decoder_impl::set_key (std::string arg)	// *UTF-8* encoded byte array
    {
      int length;
      printf ("new key: ");
      length = arg.size ();
      int i = 0, j = 0;
      while ((j < 32) && (i < length))
	{
	  if ((unsigned int) arg.data ()[i] < 0xc2)	// https://www.utf8-chartable.de/
	    {
	      _key[j] = arg.data ()[i];
	      i++;
	      j++;
	    }
	  else
	    {
	      _key[j] = (arg.data ()[i] - 0xc2) * 0x40 + arg.data ()[i + 1];
	      i += 2;
	      j++;
	    }
	}
      length = j;		// index from 0 to length-1
      printf ("%d bytes: ", length);
      for (i = 0; i < length; i++)
	printf ("%02X ", _key[i]);
      printf ("\n");
      fflush (stdout);
    }

    void m17_decoder_impl::set_seed (std::string arg)     // *UTF-8* encoded byte array
    { 
      int length;
      printf ("new seed: ");
      length = arg.size ();
      int i = 0, j = 0;
      while ((j < 3) && (i < length))
        { 
          if ((unsigned int) arg.data ()[i] < 0xc2)     // https://www.utf8-chartable.de/
            { 
              _seed[j] = arg.data ()[i];
              i++;
              j++;
            }
          else
            { 
              _seed[j] = (arg.data ()[i] - 0xc2) * 0x40 + arg.data ()[i + 1];
              i += 2;
              j++;
            }
        }
      length = j;               // index from 0 to length-1
      printf ("%d bytes: ", length);
      for (i = 0; i < length; i++)
        printf ("%02X ", _seed[i]);
      printf ("\n");
      fflush (stdout);
      if(length<=2)
        {
         _scrambler_seed = _scrambler_seed >> 16;
         fprintf(stderr, "Scrambler key: 0x%02X (8-bit)\n", _scrambler_seed);
        }
      else if(length<=4)
        {
         _scrambler_seed = _scrambler_seed >> 8;
         fprintf(stderr, "Scrambler key: 0x%04X (16-bit)\n", _scrambler_seed);
        }
      else
         fprintf(stderr, "Scrambler key: 0x%06X (24-bit)\n", _scrambler_seed);

      _encr_type=ENCR_SCRAM; //Scrambler key was passed
    }

    void
      m17_decoder_impl::forecast (int noutput_items,
				  gr_vector_int & ninput_items_required)
    {
      ninput_items_required[0] = 0;	//  noutput_items;
    }

//this is generating a correct seed value based on the fn value,
//ideally, we would only want to run this under poor signal, frame skips, etc 
//Note: Running this every frame will lag if high fn values (observed with test file)
    uint32_t m17_decoder_impl::scrambler_seed_calculation (int8_t subtype,
							   uint32_t key,
							   int fn)
    {
      int i;
      uint32_t lfsr, bit;

      lfsr = key;
      bit = 0;
      for (i = 0; i < 128 * fn; i++)
	{
	  //get feedback bit with specified taps, depending on the subtype
	  if (subtype == 0)
	    bit = (lfsr >> 7) ^ (lfsr >> 5) ^ (lfsr >> 4) ^ (lfsr >> 3);
	  else if (subtype == 1)
	    bit = (lfsr >> 15) ^ (lfsr >> 14) ^ (lfsr >> 12) ^ (lfsr >> 3);
	  else if (subtype == 2)
	    bit = (lfsr >> 23) ^ (lfsr >> 22) ^ (lfsr >> 21) ^ (lfsr >> 16);
	  else
	    bit = 0;		//should never get here, but just in case

	  bit &= 1;		//truncate bit to 1 bit
	  lfsr = (lfsr << 1) | bit;	//shift LFSR left once and OR bit onto LFSR's LSB
	  lfsr &= 0xFFFFFF;	//truncate lfsr to 24-bit

	}

      //truncate seed so subtype will continue to set properly on subsequent passes
      if (_scrambler_subtype == 0)
	_scrambler_seed &= 0xFF;
      else if (_scrambler_subtype == 1)
	_scrambler_seed &= 0xFFFF;
      else if (_scrambler_subtype == 2)
	_scrambler_seed &= 0xFFFFFF;

      //debug
      //fprintf (stderr, "\nScrambler Key: 0x%06X; Seed: 0x%06X; Subtype: %02d; FN: %05d; ", key, lfsr, subtype, fn);

      return lfsr;
    }

//scrambler pn sequence generation
    void m17_decoder_impl::scrambler_sequence_generator ()
    {
      int i = 0;
      uint32_t lfsr, bit;
      lfsr = _scrambler_seed;

      //only set if not initially set (first run), it is possible (and observed) that the scrambler_subtype can 
      //change on subsequent passes if the current SEED for the LFSR falls below one of these thresholds
      if (_scrambler_subtype == -1)
	{
	  if (lfsr > 0 && lfsr <= 0xFF)
	    _scrambler_subtype = 0;	// 8-bit key
	  else if (lfsr > 0xFF && lfsr <= 0xFFFF)
	    _scrambler_subtype = 1;	//16-bit key
	  else if (lfsr > 0xFFFF && lfsr <= 0xFFFFFF)
	    _scrambler_subtype = 2;	//24-bit key
	  else
	    _scrambler_subtype = 0;	// 8-bit key (default)
	}

      //TODO: Set Frame Type based on scrambler_subtype value
      if (_debug_ctrl == true)
	{
	  fprintf (stderr,
		   "\nScrambler Key: 0x%06X; Seed: 0x%06X; Subtype: %02d;",
		   _scrambler_seed, lfsr, _scrambler_subtype);
	  fprintf (stderr, "\n pN: ");
	}

      //run pN sequence with taps specified
      for (i = 0; i < 128; i++)
	{
	  //get feedback bit with specified taps, depending on the scrambler_subtype
	  if (_scrambler_subtype == 0)
	    bit = (lfsr >> 7) ^ (lfsr >> 5) ^ (lfsr >> 4) ^ (lfsr >> 3);
	  else if (_scrambler_subtype == 1)
	    bit = (lfsr >> 15) ^ (lfsr >> 14) ^ (lfsr >> 12) ^ (lfsr >> 3);
	  else if (_scrambler_subtype == 2)
	    bit = (lfsr >> 23) ^ (lfsr >> 22) ^ (lfsr >> 21) ^ (lfsr >> 16);
	  else
	    bit = 0;		//should never get here, but just in case

	  bit &= 1;		//truncate bit to 1 bit (required since I didn't do it above)
	  lfsr = (lfsr << 1) | bit;	//shift LFSR left once and OR bit onto LFSR's LSB
	  lfsr &= 0xFFFFFF;	//truncate lfsr to 24-bit (really doesn't matter)
	  _scrambler_pn[i] = bit;

	}

      //pack bit array into byte array for easy data XOR
      pack_bit_array_into_byte_array (_scrambler_pn, _scr_bytes, 16);

      //save scrambler seed for next round
      _scrambler_seed = lfsr;

      //truncate seed so subtype will continue to set properly on subsequent passes
      if (_scrambler_subtype == 0)
	_scrambler_seed &= 0xFF;
      else if (_scrambler_subtype == 1)
	_scrambler_seed &= 0xFFFF;
      else if (_scrambler_subtype == 2)
	_scrambler_seed &= 0xFFFFFF;

      if (_debug_ctrl == true)
	{
	  //debug packed bytes
	  for (i = 0; i < 16; i++)
	    fprintf (stderr, " %02X", _scr_bytes[i]);
	  fprintf (stderr, "\n");
	}

    }

//convert a user string (as hex octets) into a uint8_t array for key
    void m17_decoder_impl::parse_raw_key_string (uint8_t * dest,
						 const char *inp)
    {
      uint16_t len = strlen (inp);

      if (len == 0)
	return;			//return silently and pretend nothing happened

      memset (dest, 0, len / 2);	//one character represents half of a byte

      if (!(len % 2))		//length even?
	{
	  for (uint8_t i = 0; i < len; i += 2)
	    {
	      if (inp[i] >= 'a')
		dest[i / 2] |= (inp[i] - 'a' + 10) * 0x10;
	      else if (inp[i] >= 'A')
		dest[i / 2] |= (inp[i] - 'A' + 10) * 0x10;
	      else if (inp[i] >= '0')
		dest[i / 2] |= (inp[i] - '0') * 0x10;

	      if (inp[i + 1] >= 'a')
		dest[i / 2] |= inp[i + 1] - 'a' + 10;
	      else if (inp[i + 1] >= 'A')
		dest[i / 2] |= inp[i + 1] - 'A' + 10;
	      else if (inp[i + 1] >= '0')
		dest[i / 2] |= inp[i + 1] - '0';
	    }
	}
      else
	{
	  if (inp[0] >= 'a')
	    dest[0] |= inp[0] - 'a' + 10;
	  else if (inp[0] >= 'A')
	    dest[0] |= inp[0] - 'A' + 10;
	  else if (inp[0] >= '0')
	    dest[0] |= inp[0] - '0';

	  for (uint8_t i = 1; i < len - 1; i += 2)
	    {
	      if (inp[i] >= 'a')
		dest[i / 2 + 1] |= (inp[i] - 'a' + 10) * 0x10;
	      else if (inp[i] >= 'A')
		dest[i / 2 + 1] |= (inp[i] - 'A' + 10) * 0x10;
	      else if (inp[i] >= '0')
		dest[i / 2 + 1] |= (inp[i] - '0') * 0x10;

	      if (inp[i + 1] >= 'a')
		dest[i / 2 + 1] |= inp[i + 1] - 'a' + 10;
	      else if (inp[i + 1] >= 'A')
		dest[i / 2 + 1] |= inp[i + 1] - 'A' + 10;
	      else if (inp[i + 1] >= '0')
		dest[i / 2 + 1] |= inp[i + 1] - '0';
	    }
	}
    }

    int
      m17_decoder_impl::general_work (int noutput_items,
				      gr_vector_int & ninput_items,
				      gr_vector_const_void_star & input_items,
				      gr_vector_void_star & output_items)
    {
      const float *in = (const float *) input_items[0];
      char *out = (char *) output_items[0];
      int countout = 0;

      float sample;		//last raw sample from the stdin
      float dist;		//Euclidean distance for finding syncwords in the symbol stream

      for (int counterin = 0; counterin < ninput_items[0]; counterin++)
	{
	  //wait for another symbol
	  sample = in[counterin];

	  if (!syncd)
	    {
	      //push new symbol
	      for (uint8_t i = 0; i < 7; i++)
		{
		  last[i] = last[i + 1];
		}

	      last[7] = sample;

	      //calculate euclidean norm
	      dist = eucl_norm (last, str_sync_symbols, 8);

	      if (dist < _threshold)	//frame syncword detected
		{
		  //fprintf(stderr, "str_sync_symbols dist: %3.5f\n", dist);
		  syncd = 1;
		  pushed = 0;
		  fl = 0;
		}
	      else
		{
		  //calculate euclidean norm again, this time against LSF syncword
		  dist = eucl_norm (last, lsf_sync_symbols, 8);

		  if (dist < _threshold)	//LSF syncword
		    {
		      //fprintf(stderr, "lsf_sync dist: %3.5f\n", dist);
		      syncd = 1;
		      pushed = 0;
		      fl = 1;
		    }
		}
	    }
	  else
	    {
	      pld[pushed++] = sample;

	      if (pushed == SYM_PER_PLD)
		{
		  //common operations for all frame types
		  //slice symbols to soft dibits
		  slice_symbols (soft_bit, pld);

		  //derandomize
		  randomize_soft_bits (soft_bit);

		  //deinterleave
		  reorder_soft_bits (d_soft_bit, soft_bit);

		  //if it is a frame
		  if (!fl)
		    {
		      //extract data
		      for (uint16_t i = 0; i < 272; i++)
			{
			  enc_data[i] = d_soft_bit[96 + i];
			}

		      //decode
		      uint32_t e =
			viterbi_decode_punctured (frame_data, enc_data,
						  puncture_pattern_2, 272,
						  12);

		      uint16_t fn = (frame_data[1] << 8) | frame_data[2];

		      if (_debug_data == true)
			{	//dump data - first byte is empty
			  printf ("RX FN: %04X PLD: ", fn);
			}

		      for (uint8_t i = 3; i < 19; i++)
			{
			  if (_debug_data == true)
			    {
			      printf ("%02X", frame_data[i]);
			    }
			  out[countout] = frame_data[i];
			  countout++;
			}
		      if (_debug_data == true)
			{
			  printf (" e=%1.1f\n", (float) e / 0xFFFF);
			}
		      //send codec2 stream to stdout
		      //fwrite(&frame_data[3], 16, 1, stdout);

		      //if the stream is signed
		      if (_signed_str == true && fn < 0x7FFC)
			{
			  //if thats the first frame (fn=0)
			  if (fn == 0)
			    memcpy (_digest, &frame_data[3],
				    sizeof (_digest));

			  for (uint8_t i = 0; i < sizeof (_digest); i++)
			    _digest[i] ^= frame_data[3 + i];
			  uint8_t tmp = _digest[0];
			  for (uint8_t i = 0; i < sizeof (_digest) - 1; i++)
			    _digest[i] = _digest[i + 1];
			  _digest[sizeof (_digest) - 1] = tmp;
			}

		      //NOTE: Don't attempt decryption when a signed stream is >= 0x7FFC
		      //The Signature is not encrypted

		      //AES
		      if (_encr_type == ENCR_AES)
			{
			  memcpy (_iv, lsf + 14, 14);
			  _iv[14] = frame_data[1] & 0x7F;
			  _iv[15] = frame_data[2] & 0xFF;

			  if (_signed_str == true && (fn % 0x8000) < 0x7FFC)	//signed stream
			    aes_ctr_bytewise_payload_crypt (_iv, _key, frame_data + 3, AES128);	//hardcoded for now
			  else if (_signed_str == false)	//non-signed stream
			    aes_ctr_bytewise_payload_crypt (_iv, _key, frame_data + 3, AES128);	//hardcoded for now
			}

		      //Scrambler
		      if (_encr_type == ENCR_SCRAM)
			{
			  if (fn != 0 && (fn % 0x8000) != _expected_next_fn)	//frame skip, etc
			    _scrambler_seed =
			      scrambler_seed_calculation (_scrambler_subtype,
							  _scrambler_key,
							  fn & 0x7FFF);
			  else if (fn == 0)
			    _scrambler_seed = _scrambler_key;	//reset back to key value

			  if (_signed_str == true && (fn % 0x8000) < 0x7FFC)	//signed stream
			    scrambler_sequence_generator ();
			  else if (_signed_str == false)	//non-signed stream
			    scrambler_sequence_generator ();

			  for (uint8_t i = 0; i < 16; i++)
			    {
			      frame_data[i + 3] ^= _scr_bytes[i];
			    }
			}

		      //dump data - first byte is empty
		      printf ("FN: %04X PLD: ", fn);
		      for (uint8_t i = 3; i < 19; i++)
			{
			  printf ("%02X", frame_data[i]);
			}
		      if (_debug_ctrl == true)
			printf (" e=%1.1f\n", (float) e / 0xFFFF);

		      printf ("\n");

		      //send codec2 stream to stdout
		      //fwrite(&frame_data[3], 16, 1, stdout);

		      //extract LICH
		      for (uint16_t i = 0; i < 96; i++)
			{
			  lich_chunk[i] = d_soft_bit[i];
			}

		      //Golay decoder
		      decode_LICH (lich_b, lich_chunk);
		      lich_cnt = lich_b[5] >> 5;

		      //If we're at the start of a superframe, or we missed a frame, reset the LICH state
		      if ((lich_cnt == 0)
			  || ((fn % 0x8000) != _expected_next_fn))
			lich_chunks_rcvd = 0;

		      lich_chunks_rcvd |= (1 << lich_cnt);
		      memcpy (&lsf[lich_cnt * 5], lich_b, 5);

		      //debug - dump LICH
		      if (lich_chunks_rcvd == 0x3F)	//all 6 chunks received?
			{
			  if (_debug_ctrl == true)
			    {
			      if (_callsign == true)
				{
				  decode_callsign_bytes (d_dst, &lsf[0]);
				  decode_callsign_bytes (d_src, &lsf[6]);
				  printf ("DST: %-9s ", d_dst);	//DST
				  printf ("SRC: %-9s ", d_src);	//SRC
				}
			      else
				{
				  printf ("DST: ");	//DST
				  for (uint8_t i = 0; i < 6; i++)
				    printf ("%02X", lsf[i]);
				  printf (" ");
				  printf ("SRC: ");	//SRC
				  for (uint8_t i = 0; i < 6; i++)
				    printf ("%02X", lsf[6 + i]);
				  printf (" ");
				}
			    }

			  //TYPE
			  uint16_t type = (uint16_t) lsf[12] * 0x100 + lsf[13];	//big-endian
			  if (_debug_ctrl == true)
			    {
			      printf ("TYPE: %04X (", type);
			      if (type && 1)
				printf ("STREAM: ");
			      else
				printf ("PACKET: ");	//shouldn't happen
			      if (((type >> 1) & 3) == 1)
				printf ("DATA, ");
			      else if (((type >> 1) & 3) == 2)
				printf ("VOICE, ");
			      else if (((type >> 1) & 3) == 3)
				printf ("VOICE+DATA, ");
			      printf ("ENCR: ");
			      if (((type >> 3) & 3) == 0)
				printf ("PLAIN, ");
			      else if (((type >> 3) & 3) == 1)
				{
				  printf ("SCRAM ");
				  if (((type >> 5) & 3) == 1)
				    printf ("8-bit, ");
				  else if (((type >> 5) & 3) == 2)
				    printf ("16-bit, ");
				  else if (((type >> 5) & 3) == 3)
				    printf ("24-bit, ");
				}
			      else if (((type >> 3) & 3) == 2)
				printf ("AES, ");
			      else
				printf ("UNK, ");
			      printf ("CAN: %d", (type >> 7) & 0xF);
			      if ((type >> 11) & 1)
				printf (", SIGNED");
			      printf (") ");
			    }

			  //META
			  if (_debug_ctrl == true)
			    {
			      printf ("META: ");
			      for (uint8_t i = 0; i < 14; i++)
				printf ("%02X", lsf[14 + i]);

			      if (CRC_M17 (lsf, 30))	//CRC
				printf (" LSF_CRC_ERR");
			      else
				printf (" LSF_CRC_OK ");
			      printf ("\n");
			    }
			}

		      //if the contents of the payload is now digital signature, not data/voice
		      if (fn >= 0x7FFC && _signed_str == true)
			{
			  memcpy (&_sig[((fn & 0x7FFF) - 0x7FFC) * 16],
				  &frame_data[3], 16);

			  if (fn == (0x7FFF | 0x8000))
			    {
			      //dump data
			      /*printf("DEC-Digest: ");
			         for(uint8_t i=0; i<sizeof(digest); i++)
			         printf("%02X", digest[i]);
			         printf("\n");

			         printf("Key: ");
			         for(uint8_t i=0; i<sizeof(pub_key); i++)
			         printf("%02X", pub_key[i]);
			         printf("\n");

			         printf("Signature: ");
			         for(uint8_t i=0; i<sizeof(sig); i++)
			         printf("%02X", sig[i]);
			         printf("\n"); */

			      if (uECC_verify
				  (_key, _digest, sizeof (_digest), _sig,
				   _curve))
				{
				  printf ("Signature OK\n");
				}
			      else
				{
				  printf ("Signature invalid\n");
				}
			    }
			}

		      _expected_next_fn = (fn + 1) % 0x8000;
		    }
		  else		//lsf
		    {
		      if (_debug_ctrl == true)
			{
			  printf ("{LSF}\n");
			}
		      //decode
		      uint32_t e =
			viterbi_decode_punctured (lsf, d_soft_bit,
						  puncture_pattern_1,
						  2 * SYM_PER_PLD, 61);

		      //shift the buffer 1 position left - get rid of the encoded flushing bits
		      for (uint8_t i = 0; i < 30; i++)
			lsf[i] = lsf[i + 1];

		      //dump data
		      if (_debug_ctrl == true)
			{
			  if (_callsign == true)
			    {
			      decode_callsign_bytes (d_dst, &lsf[0]);
			      decode_callsign_bytes (d_src, &lsf[6]);
			      printf ("DST: %-9s ", d_dst);	//DST
			      printf ("SRC: %-9s ", d_src);	//SRC
			    }
			  else
			    {
			      printf ("DST: ");	//DST
			      for (uint8_t i = 0; i < 6; i++)
				printf ("%02X", lsf[i]);
			      printf (" ");

			      //SRC
			      printf ("SRC: ");
			      for (uint8_t i = 0; i < 6; i++)
				printf ("%02X", lsf[6 + i]);
			      printf (" ");
			    }

			  //TYPE
			  printf ("TYPE: ");
			  for (uint8_t i = 0; i < 2; i++)
			    printf ("%02X", lsf[12 + i]);
			  printf (" ");

			  //META
			  printf ("META: ");
			  for (uint8_t i = 0; i < 14; i++)
			    printf ("%02X", lsf[14 + i]);
			  printf (" ");

			  //CRC
			  //printf("CRC: ");
			  //for(uint8_t i=0; i<2; i++)
			  //printf("%02X", lsf[28+i]);
			  if (CRC_M17 (lsf, 30))
			    printf ("LSF_CRC_ERR");
			  else
			    printf ("LSF_CRC_OK ");
			  //Viterbi decoder errors
			  printf (" e=%1.1f\n", (float) e / 0xFFFF);
			}
		    }

		  //job done
		  syncd = 0;
		  pushed = 0;

		  for (uint8_t i = 0; i < 8; i++)
		    last[i] = 0.0;
		}
	    }
	}
      // Tell runtime system how many input items we consumed on
      // each input stream.
      consume_each (ninput_items[0]);

      // Tell runtime system how many output items we produced.
      return countout;
    }

  }				/* namespace m17 */
}				/* namespace gr */
