/* -*- c++ -*- */
/*
 * Copyright 2004 Free Software Foundation, Inc.
 * 
 * This file is part of GNU Radio
 * 
 * GNU Radio 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.
 * 
 * GNU Radio 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 GNU Radio; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street,
 * Boston, MA 02110-1301, USA.
 */

/*
 * gr-baz by Balint Seeber (http://spench.net/contact)
 * Information, documentation & samples: http://wiki.spench.net/wiki/gr-baz
 */

/*
 * config.h is generated by configure.  It contains the results
 * of probing for features, options etc.  It should be the first
 * file included in your .cc file.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <baz_acars_decoder.h>
#include <gnuradio/io_signature.h>
#include <gnuradio/blocks/count_bits.h>

#include <stdio.h>
#include <iostream>
#include <assert.h>

using namespace std;

/*
 * Create a new instance of baz_acars_decoder and return
 * a boost shared_ptr.  This is effectively the public constructor.
 */
baz_acars_decoder_sptr 
baz_make_acars_decoder (gr::msg_queue::sptr msgq)
{
	return baz_acars_decoder_sptr (new baz_acars_decoder (msgq));
}

/*
 * Specify constraints on number of input and output streams.
 * This info is used to construct the input and output signatures
 * (2nd & 3rd args to gr::block's constructor).  The input and
 * output signatures are used by the runtime system to
 * check that a valid number and type of inputs and outputs
 * are connected to this block.  In this case, we accept
 * only 1 input and 1 output.
 */
static const int MIN_IN = 1;	// mininum number of input streams
static const int MAX_IN = 2;	// maximum number of input streams
static const int MIN_OUT = 0;	// minimum number of output streams
static const int MAX_OUT = 0;	// maximum number of output streams

static const int PREKEY_LENGTH = (16 * 8);
static const int PREAMBLE_LENGTH = ((2 + 2) * 8);
static const int HISTORY_OFFSET = (PREKEY_LENGTH + PREAMBLE_LENGTH);	// Pre-key, bit sync, char sync

static const unsigned long PREAMBLE = 0x3FFE5C5C;	// Air interface encoded! (0: same, 1: change)

/*
 * The private constructor
 */
baz_acars_decoder::baz_acars_decoder (gr::msg_queue::sptr msgq)
	: gr::sync_block ("acars_decoder",
		gr::io_signature::make (MIN_IN, MAX_IN, sizeof(float)),
		gr::io_signature::make (MIN_OUT, MAX_OUT, 0))
	, d_state(STATE_SEARCHING)
	, d_preamble_state(0)
	, d_preamble_threshold(3)
	, d_bit_counter(0)
	, d_current_byte(0x00)
	, d_byte_counter(0)
	, d_flags(FLAG_NONE)
	, d_msgq(msgq)
	, d_prev_bit(0)
	, d_frequency(0.0f)
{
	memset(&d_current_packet, 0x00, sizeof(d_current_packet));
//fprintf(stderr, "ACARS: packet struct size: %i bytes\n", sizeof(d_current_packet));
	set_history(HISTORY_OFFSET + 1);
}

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

void baz_acars_decoder::set_preamble_threshold(int threshold)
{
	if (threshold < 0)
		return;
fprintf(stderr, "ACARS: Premable threshold: %i (was %i)\n", threshold, d_preamble_threshold);
	d_preamble_threshold = threshold;
}

void baz_acars_decoder::set_frequency(float frequency)
{
	if (frequency < 0.0f)
		return;
	
	d_frequency = frequency;
}

void baz_acars_decoder::set_station_name(const char* station_name)
{
	d_station_name = station_name;
}

int baz_acars_decoder::work (int noutput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items)
{
	const float *in = (const float *) input_items[0];	// > 0.0 (1): 2400 Hz (same), < 0.0 (0): 1200 Hz (change)
	const float *level = NULL;
	if (input_items.size() > 1)
		level = (const float *) input_items[1];
//fprintf(stderr, "ACARS: Work %i\n", noutput_items);
	for (int n = 0; n < noutput_items; ++n)
	{
		int bit_index = HISTORY_OFFSET + n;
		unsigned char bit = (in[bit_index] > 0.0 ? 0x00 : 0x01);	// Hard decision at the moment
		
		switch (d_state)
		{
			case STATE_SEARCHING:
			{
				// Before this: 128 1's - won't see them all
				d_preamble_state <<= 1;
				d_preamble_state |= bit;
				
				unsigned long wrong_bits = PREAMBLE ^ d_preamble_state;	// Works without mask because exactly 32 bits
				int wrong_bit_count = gr::blocks::count_bits32(wrong_bits);
				if (wrong_bit_count <= d_preamble_threshold)
				{
if (wrong_bit_count > 0) fprintf(stderr, "ACARS: %i wrong (threshold %i)\n", wrong_bit_count, d_preamble_threshold);
					memset(&d_current_packet, 0x00, sizeof(d_current_packet));
					
					float ave = 0.0f;
					int ones = 0;
					int continuous_zeroes = 0;
					for (int i = 0; i < PREKEY_LENGTH; ++i)
					{
						ave += in[i];
						unsigned char current = (in[i] > 0.0 ? 0x00 : 0x01);
						if (current == 0x01)
						{
							++ones;
							continuous_zeroes = 0;
						}
						else
							++continuous_zeroes;
					}
					
					ave /= (float)PREKEY_LENGTH;
					
					float ref_level = 0.0f;
					if (level)
					{
						for (int i = 0; i < PREAMBLE_LENGTH; ++i)
							ref_level += level[PREKEY_LENGTH + i];
					}
					
					ref_level /= (float)PREAMBLE_LENGTH;
if (ones > 0) fprintf(stderr, "ACARS: %i ones of %i (%i continuous zeroes), ave: %f, ref level: %f\n", ones, PREKEY_LENGTH, continuous_zeroes, ave, ref_level);
					d_current_packet.reference_level = ref_level;
					d_current_packet.prekey_average = ave;
					d_current_packet.prekey_ones = ones;
					
					d_state = STATE_ASSEMBLE;
					d_bit_counter = /*1*/0;	// FIXME: Why are we off by one?
					d_current_byte = 0x00;
					d_byte_counter = 0;
					d_flags = FLAG_NONE;
					d_prev_bit = 0;	// Parity bit of SYN
				}
				
				break;
			}
			
			case STATE_ASSEMBLE:
			{
				unsigned char decoded_bit = d_prev_bit;
				if (bit)
					decoded_bit = 1 - d_prev_bit;
				d_prev_bit = decoded_bit;	// This is not normal differential decoding (actually 'encoding' here)
				
				d_current_byte <<= 1;
				d_current_byte |= decoded_bit;
				
				++d_bit_counter;
				if (d_bit_counter == 8)
				{
					int ones = gr::blocks::count_bits8(d_current_byte);
					if ((ones % 2) == 0)
					{
						d_current_packet.byte_error[d_byte_counter] = 0x01;
						++d_current_packet.parity_error_count;
//fprintf(stderr, "{#%03i: 0x%02x %i} ", d_byte_counter, d_current_byte, ones);
					}
//fprintf(stderr, "[0x%02x -> ", d_current_byte);
					d_current_byte = ((d_current_byte * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;	// Reverse bits: http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith64Bits
					//d_current_byte >>= 1;
					d_current_byte &= 0x7F;	// FIXME: What's going on here? Wasn't parity on the end?
//fprintf(stderr, "0x%02x] ", d_current_byte);
					d_current_packet.byte_data[d_byte_counter] = d_current_byte;
					
					if ((d_byte_counter == 0) && (d_current_byte == 0x01))	// SOH
					{
						d_flags |= FLAG_SOH;
//fprintf(stderr, "ACARS: SOH\n");
					}
					
					if ((d_byte_counter == (1 + 1 + 7 + 1 + 2 + 1)) && (d_current_byte == 0x02))	// STX
					{
						d_flags |= FLAG_STX;
//fprintf(stderr, "ACARS: STX\n");
					}
					
					// FIXME: Can add 10 for air-ground (Seq # & flight #)
					if ((d_byte_counter > (1 + 1 + 7 + 1 + 2 + 1)) && d_current_byte == 0x03)	// ETX
					{
						d_flags |= FLAG_ETX;
						d_current_packet.etx_index = d_byte_counter;
//fprintf(stderr, "ACARS: ETX @ %i\n", d_byte_counter);
					}
					
					if (((d_current_packet.etx_index > 0) && (d_byte_counter == (d_current_packet.etx_index + 1 + 2))) && (d_current_byte == 0x7F))	// DEL
					{
						d_flags |= FLAG_DEL;
//fprintf(stderr, "ACARS: DEL\n");
					}
					
					d_current_packet.flags = d_flags;
					
					++d_byte_counter;
					++d_current_packet.byte_count;
					d_bit_counter = 0;
					d_current_byte = 0x00;
					
					if ((d_flags & FLAG_DEL) || (d_byte_counter == MAX_ACARS_PACKET_SIZE))
					{
if ((d_flags & FLAG_ETX) == FLAG_NONE) fprintf(stderr, "ACARS: Missing ETX!\n");
if ((d_flags & FLAG_DEL) == FLAG_NONE) fprintf(stderr, "ACARS: Missing DEL!\n");
						if (d_msgq)
						{
							int data_index = 0;
							int station_name_length = (d_station_name.size() + 1);
							int message_data_length = sizeof(d_current_packet) + station_name_length;
							gr::message::sptr msg = gr::message::make(d_current_packet.flags, d_frequency, d_current_packet.reference_level, message_data_length);
							
							memcpy(msg->msg() + data_index, &d_current_packet, sizeof(d_current_packet));
							data_index += sizeof(d_current_packet);
							
							memcpy(msg->msg() + data_index, d_station_name.c_str(), station_name_length);
							data_index += station_name_length;
							
							d_msgq->insert_tail(msg);
							msg.reset();
						}
						
						d_state = STATE_SEARCHING;
						d_preamble_state = 0;
					}
				}
				
				break;
			}
		}
	}
//fprintf(stderr, "ACARS: Work done\n");
	return noutput_items;
}
