/* SigLib example for V.26b using pi/4 Differential Quadrature Phase Shift keying (DQPSK) demodulation
	Data rate - 2400 bps
	Baud rate - 1200 Baud
	Sample rate - 48000 Hz
	Carrier freq. - 1800 Hz

This program uses :
	SDS_CostasQamDemodulate - To test the demodulator and completely decode the rx data stream
	SDA_CostasQamDemodulate - To just test the demodulator

Description :
	This program scans the input file to ascertain the peak signal amplitude. It then applies
	an automatic gain control function to normalize the gain into the demodulator.
	This data is fed through an IIR Elliptic filter to reduce the out-of-band noise.
	The demodulator outputs the detected symbols which are then decoded using pi by 4 DQPSK.
	The symbols are then scanned for the synchronization sequence and once this is detected the
	symbols are saved to the output file "demodout.txt".
	Once the data burst has ended the demodulator is reset and is ready for the next data burst.
*/

#include <stdio.h>
#include <string.h>
#include <siglib.h>
#include "GraphFunctions.h"
#include <nhl.h>						/* Header for .wav file I/O functions */
#include "../plot_fd.h"					/* Frequency domain plots */
#include <dpchar.h>
#include "Rx_FIR.h"						/* Demodulator pre-filter to reduce the out-of-band noise. */

#define	PER_SAMPLE						0			/* Set to '1' to process a sample or '0' to process and array */
#define	ENABLE_DECIMATE					0			/* Set to '1' to decimate the input or '0' to process at original rate */

										/* Select which graphs to display */
#define	DISPLAY_TIME_DOMAIN				1			/* Set to '1' to display the time domain input */
#define	DISPLAY_FREQ_DOMAIN				1			/* Set to '1' to display the input spectrum */
#define	DISPLAY_EYE_DIAGRAM				1			/* Set to '1' to display the eye diagram output */
#define	DISPLAY_CONSTELLATION			1			/* Set to '1' to display the constellation diagram */

										/* If we are displaying the eye diagram then we can display the trigger.
										   The trigger shows the output of the early-late-gate timing detector
										   i.e. where the symbols are being decoded */
#define	DISPLAY_TRIGGER					0			/* Set to '1' to display the trigger */

										/* Select either or both of these for logging to 'debug.log' */
#define	DEBUG_LOG_FILE					0			/* Set to '1' to enable logging */
#define	DEBUG_DIBITS_TO_LOG_FILE		0			/* Set to '1' to enable logging of the output dibits */

#if (PER_SAMPLE)									/* If we are processing on a per-sample basis then we can not display the eye diagram */
#undef DISPLAY_EYE_DIAGRAM
#define	DISPLAY_EYE_DIAGRAM				0
#endif

#if (!DISPLAY_EYE_DIAGRAM)							/* If we are not displaying the eye diagram then can not display the trigger information */
#undef DISPLAY_TRIGGER
#define	DISPLAY_TRIGGER					0
#endif

			/* Basic application definitions */
#define	SAMPLE_LENGTH					((SLArrayIndex_t)512)	/* Number of samples in array */

#if ENABLE_DECIMATE
#define	SAMPLE_RATE						((SLData_t)12000.0)		/* Processing sample rate */
#else
#define	SAMPLE_RATE						((SLData_t)48000.0)		/* Processing sample rate */
#endif

#define	SYMBOL_RATE						((SLData_t)1200.0)		/* Symbol rate */
#define	CARRIER_FREQ					((SLData_t)1800.0)		/* Frequency of carrier signal - a multpImagDatale of the sine table frequency */

#define	SYMBOL_LENGTH					((SLArrayIndex_t)(SAMPLE_RATE / SYMBOL_RATE))	/* Number of samples per symbol */
#define	CARRIER_CYCLE_LENGTH			((SLArrayIndex_t)(SAMPLE_RATE / CARRIER_FREQ))	/* Carrier Period */

#define	EYE_DIAGRAM_SIZE				(((SLArrayIndex_t)2) * SYMBOL_LENGTH)			/* Size of eye diagram graph - Two complete symbol periods */

#define	MAX_CONST_POINTS_PER_BLOCK		((SAMPLE_LENGTH / SYMBOL_LENGTH) + SIGLIB_AI_ONE)	/* Maximum number of output constellation points from SDA_CostasQamDemodulate */
#define	MAX_CONST_POINTS_PER_BURST		((SLArrayIndex_t)400)	/* Maximum number of constellation points displayed per data burst */

											/* Signal presence detector definitions */
#define	NOISE_FLOOR_THRESHOLD			((SLData_t)40.0)		/* Noise floor level - used to detect presence of a burst of data */

											/* Automatic gain control definitions */
#define	AGC_OUTPUT_LEVEL				((SLData_t)255.0)		/* Output level for AGC */
#if ENABLE_DECIMATE
#define	AGC_ATTACK_SENSITIVITY			((SLData_t)1.0005)		/* Sensitivity of attack gain adjustment */
#define	AGC_DECAY_SENSITIVITY			((SLData_t)0.9995)		/* Sensitivity of decay gain adjustment */
#else
//#define	AGC_ATTACK_SENSITIVITY			((SLData_t)1.0005)		/* Sensitivity of attack gain adjustment */
//#define	AGC_DECAY_SENSITIVITY			((SLData_t)0.9995)		/* Sensitivity of decay gain adjustment */
#define	AGC_ATTACK_SENSITIVITY			((SLData_t)1.0001)		/* Sensitivity of attack gain adjustment */
#define	AGC_DECAY_SENSITIVITY			((SLData_t)0.9999)		/* Sensitivity of decay gain adjustment */
#endif
#define	AGC_SUB_ARRAY_LEN				((SLArrayIndex_t)32)	/* Sub array length */


enum ProcessingState_t											/* Demodulator processing state */
{
	DEMODULATOR_RESET,
	DEMODULATING_DATA
};

enum ProcessingState_t	DemodulatorState;						/* Demodulator processing state */


									/* Costas loop data */
						/* Note : Costas loop LPF lengths are chosen so that there
						are at least two full cycles and an odd number - for an integer group delay */
#define	COSTAS_LP_LPF_LENGTH			(((SLArrayIndex_t)(2.0*CARRIER_CYCLE_LENGTH)) | 0x1)	/* Costas loop LP LPF FIR filter length */
									/* Note the next few parameters vary depending on whether or not we decimate */
#if ENABLE_DECIMATE
//#define	COSTAS_LP_VCO_TRACK_MODE_MODULATION_GAIN	((SLData_t)0.00001)	/* Tracking mode modulation gain */
//#define	COSTAS_LP_VCO_ACQ_MODE_MODULATION_GAIN	((SLData_t)0.0001)		/* Modulation gain */
#define	COSTAS_LP_VCO_TRACK_MODE_MODULATION_GAIN	((SLData_t)0.00001)	/* Tracking mode modulation gain */
#define	COSTAS_LP_VCO_ACQ_MODE_MODULATION_GAIN	((SLData_t)0.0001)		/* Modulation gain */
#else
//#define	COSTAS_LP_VCO_TRACK_MODE_MODULATION_GAIN	((SLData_t)0.000005)	/* Tracking mode modulation gain */
//#define	COSTAS_LP_VCO_ACQ_MODE_MODULATION_GAIN	((SLData_t)0.00001)		/* Modulation gain */
#define	COSTAS_LP_VCO_TRACK_MODE_MODULATION_GAIN	((SLData_t)0.000005)	/* Tracking mode modulation gain */
#define	COSTAS_LP_VCO_ACQ_MODE_MODULATION_GAIN	((SLData_t)0.000001)		/* Modulation gain */
#endif
#define	COSTAS_LP_LOOP_FILTER_ALPHA		((SLData_t)0.5)			/* Feedback coeff for one-pole loop filter */
#define	COSTAS_LP_VCO_TABLE_SIZE		((SLArrayIndex_t)1024)	/* Look up table for fast sine calculation */


SLData_t		*pCostasLpLPFCoeffs, *pCostasLpLPF1State, *pCostasLpLPF2State;	/* Costas loop loop filter coefficient pointer */

SLArrayIndex_t	CostasLpLPF1Index;								/* Costas loop inphase LPF filter index */
SLArrayIndex_t	CostasLpLPF2Index;								/* Costas loop quadrature phase LPF filter index */
SLData_t		CostasLpVCOPhase;								/* Costas loop VCO phase */
SLData_t		CostasLpState;									/* Costas loop feedback state for next iteration */

SLData_t		CostasLpLoopFilterState;						/* Costas loop loop filter feedback coeff */
SLData_t		*pCostasLpVCOLookUpTable;						/* VCO table pointer */


									/* Early-late gate symbol synchronizer data */
#define	ELG_EARLY_GATE_DELAY_LENGTH		(SYMBOL_LENGTH >> 1)
#define	ELG_LOOP_FILTER_LENGTH			((3 * SYMBOL_LENGTH) + 1)
#define	ELG_LOOP_FILTER_FC				(SYMBOL_RATE / SIGLIB_TWO)
#if ENABLE_DECIMATE
#define	ELG_PULSE_DETECTOR_THRESHOLD	((SLData_t)20.0)			/* Threshold below which the timing is not detected */
#else
#define	ELG_PULSE_DETECTOR_THRESHOLD	((SLData_t)200.0)			/* Threshold below which the timing is not detected */
#endif
#define	ELG_SYNCH_DELAY_ARRAY_LENGTH	(8 * SYMBOL_LENGTH)		/* Length of array for accounting for delay through timing detector
																This array length must be long enough to account for the TED delay */

SLData_t		ELGMatchedFilterSum;
SLData_t		*pELGMatchedFilterState;
SLArrayIndex_t	ELGMatchedFilterIndex;

SLData_t		*pELGEarlyGateDelay;
SLArrayIndex_t	ELGEarlyGateDelayIndex, ELGEarlyGateDelayDepth;

SLData_t		*pELGLoopFilterCoeffs;
SLData_t		*pELGLoopFilterState;
SLArrayIndex_t	ELGLoopFilterIndex;
SLFixData_t		ELGPulseDetectorThresholdFlag;					/* Flag to indicate signal level higher than noise level */
SLData_t		ELGZeroCrossingPreviousSample;					/* Previous sample used for zero crossing detector */
SLArrayIndex_t	ELGTriggerLatency;								/* Estimate of the latency through the ELG TED */
SLArrayIndex_t	ELGTriggerCount;								/* Variables for trigger reverberator */
SLFixData_t		ELGTriggerDetectedFlag;
SLFixData_t		ELGTriggerUpdatedFlag;

SLData_t		*pELGRealOutputSynchDelay, *pELGImagOutputSynchDelay;	/* ELG output real and imaginary synchronization delay */
SLArrayIndex_t	ELGOutputSynchDelayIndex;

																/* Variable and arrays for Costas QAM demodulator output */
SLData_t		pRealOutput[MAX_CONST_POINTS_PER_BLOCK], pImagOutput[MAX_CONST_POINTS_PER_BLOCK];
SLData_t		ConstellationI, ConstellationQ;


									/* Data for Pi / 4 differential QPSK */
#define	CONSTELLATION_POINTS					8				/* Number of constellation points */
#define	IDEAL_CONSTELLATION_POINT_MAGNITUDE		(SLData_t)90.0
#define	CONSTELLATION_THRESHOLD ((IDEAL_CONSTELLATION_POINT_MAGNITUDE * IDEAL_CONSTELLATION_POINT_MAGNITUDE) / SIGLIB_FOUR)

SLComplexRect_s	NormalizedConstellationPoints[CONSTELLATION_POINTS] = 
{
	{(SLData_t)1.0,  (SLData_t)0.0},
	{(SLData_t)0.70710678,  (SLData_t)0.70710678},
	{(SLData_t)0.0,  (SLData_t)1.0},
	{(SLData_t)-0.70710678,  (SLData_t)0.70710678},
	{(SLData_t)-1.0,  (SLData_t)0.0},
	{(SLData_t)-0.70710678,  (SLData_t)-0.70710678},
	{(SLData_t)0.0,  (SLData_t)-1.0},
	{(SLData_t)0.70710678,  (SLData_t)-0.70710678},
};		/* Constellation diagram array */

SLComplexRect_s	IdealConstellationPoints[CONSTELLATION_POINTS] = 
{
	{(SLData_t)(1.0 * IDEAL_CONSTELLATION_POINT_MAGNITUDE),  (SLData_t)(0.0 * IDEAL_CONSTELLATION_POINT_MAGNITUDE)},
	{(SLData_t)(0.70710678 * IDEAL_CONSTELLATION_POINT_MAGNITUDE),  (SLData_t)(0.70710678 * IDEAL_CONSTELLATION_POINT_MAGNITUDE)},
	{(SLData_t)(0.0 * IDEAL_CONSTELLATION_POINT_MAGNITUDE),  (SLData_t)(1.0 * IDEAL_CONSTELLATION_POINT_MAGNITUDE)},
	{(SLData_t)(-0.70710678 * IDEAL_CONSTELLATION_POINT_MAGNITUDE),  (SLData_t)(0.70710678 * IDEAL_CONSTELLATION_POINT_MAGNITUDE)},
	{(SLData_t)(-1.0 * IDEAL_CONSTELLATION_POINT_MAGNITUDE),  (SLData_t)(0.0 * IDEAL_CONSTELLATION_POINT_MAGNITUDE)},
	{(SLData_t)(-0.70710678 * IDEAL_CONSTELLATION_POINT_MAGNITUDE),  (SLData_t)(-0.70710678 * IDEAL_CONSTELLATION_POINT_MAGNITUDE)},
	{(SLData_t)(0.0 * IDEAL_CONSTELLATION_POINT_MAGNITUDE),  (SLData_t)(-1.0 * IDEAL_CONSTELLATION_POINT_MAGNITUDE)},
	{(SLData_t)(0.70710678 * IDEAL_CONSTELLATION_POINT_MAGNITUDE),  (SLData_t)(-0.70710678 * IDEAL_CONSTELLATION_POINT_MAGNITUDE)},
};		/* Constellation diagram array */



SLData_t	ConstellationPointErrors[CONSTELLATION_POINTS];

/* Differential phase    Constellation points
   to dibit mapping		    numbering
	+45		00			        2
	+135	01			     3  |  1
	+225	11			    4---+---0
	+315	10			     5  |  7
						        6
	Note - table supports positive and negative phase changes.
		- alternate entries are not valid state changes but are included to locate nearest ideal point
	Useage :
	RxDiBit = ConstellationPointDeltaDibits [ReceivedConstellationPoint - PrevReceivedConstellationPoint + 7];
*/
SLFixData_t	ConstellationPointDeltaDibits[2*CONSTELLATION_POINTS] = 
{
	0, 0, 1, 1, 3, 3, 2, 2, 0, 0, 1, 1, 3, 3, 2, 2
};


SLData_t		*pData;											/* Processing array */

									/* Table of tan^-1 with all positive angles - 0 to 2.pi radians */
SLData_t	IdealConstellationPointPhaseTable[] = 
{
	0.00000000, 0.78539816, 1.57079633, 2.35619449, 3.14159265, 3.92699082, 4.71238898, 5.49778714
};


									/* Data for synchronization detector */
#define	SYNCH_SEQUENCE_WORD_LEN	((SLArrayIndex_t)2)			/* Process dibits */
#define	SYNCH_SEQUENCE_LEN	((SLArrayIndex_t)14)			/* Number of bits in synch sequence length */
SLFixData_t SynchSequence = ((SLFixData_t)0x0147e);			/* Synchronization sequence - 01,0100,0111,1110 */
SLFixData_t	SynchSequenceBitMask;							/* Bit synch. mask */
SLFixData_t	SynchSequenceDetectorState;						/* State variable for bit synch. detector */
SLArrayIndex_t	SynchDetectedFlag;							/* Flag set to >= 0 when synch detected */

									/* Debug arrays */
#if DISPLAY_CONSTELLATION
SLComplexRect_s	ReceivedConstellationPoints[MAX_CONST_POINTS_PER_BURST];		/* Constellation diagram array */
#endif
#if DISPLAY_EYE_DIAGRAM
SLData_t	DebugRealFilterOutput[SAMPLE_LENGTH];
SLData_t	DebugImagFilterOutput[SAMPLE_LENGTH];
SLData_t	DebugELGTriggerOutput[SAMPLE_LENGTH];
#if PER_SAMPLE
SLArrayIndex_t	TriggerIndexCount;
#endif
#endif

WAV_FILE_INFO WavInfo;


SLError_t ClearDemodOutput (void);
SLError_t DemodOutput (SLInt8_t Input);

SLData_t	*pInput;							/* Array to handle input before decimation */
#if ENABLE_DECIMATE
#define	DECIMATION_RATIO	4
#else
#define	DECIMATION_RATIO	1
#endif


void main( int argc, char **argv )

{
#if DISPLAY_TIME_DOMAIN
	GraphObject		*hTimeDomainGraph;					/* Declare time domain graph object */
#endif
#if DISPLAY_EYE_DIAGRAM
	GraphObject		*hEyeDiagramGraph;					/* Declare eye diagram graph object */
#endif
#if DISPLAY_CONSTELLATION
	GraphObject		*hConstellationDiagramGraph;		/* Declare constellation diagram graph object */
#endif
	SLError_t		ErrorCode;
	SLArrayIndex_t	i, j;
	SLFixData_t		RxDiBit;
	SLArrayIndex_t	ReceivedWordCount = 0;				/* Rx pipeline count */
	SLArrayIndex_t	FirstNonZeroSampleIndex;			/* First non zero sample - used in silence threshold detection */
	SLArrayIndex_t	ProcessSampleLength;				/* Length of data set to process */
	SLArrayIndex_t	RxSymbolCount;						/* Number of symbols returned from demodulation function */

	SLArrayIndex_t	BurstSymbolCount = (SLArrayIndex_t)0;	/* Number of symbols received in a burst */
	SLData_t		CostasLoopVCOModulationIndex = COSTAS_LP_VCO_ACQ_MODE_MODULATION_GAIN;

	FILE			*pInputWavFile;
	char			WavFileName[20];

	SLData_t		AGCGain = SIGLIB_ONE;
	SLData_t		AGCMax = SIGLIB_ZERO;

	SLArrayIndex_t	ReceivedConstellationPoint;
	SLArrayIndex_t	PrevReceivedConstellationPoint = (SLArrayIndex_t)0;
#if PER_SAMPLE
	SLData_t		RealMagn;
	SLData_t		ImagMagn;
#endif

#if DEBUG_LOG_FILE
	SLArrayIndex_t	SampleCount = -(DECIMATION_RATIO*SAMPLE_LENGTH);	/* Count number of samples read from wav file */
#endif

#if DISPLAY_CONSTELLATION
	SLArrayIndex_t	ConstellationBurstCount = 0L;	/* Initialise the number of constellation points in a block */

	hConstellationDiagramGraph =					/* Initialize graph */
		CreateXYGraph ("Constellation Diagram",		/* Graph title */
					   "X-Axis",					/* X-Axis label */
					   "Y-Axis",					/* Y-Axis label */
					   SV_SIGNED,					/* Sign mode */
					   SV_GRAPH_LINE,				/* Graph type */
					   120.0,						/* Dimension - this is square */
					   "localhost");				/* Graph server */
	if (hConstellationDiagramGraph == NULL)			/* Graph creation failed - e.g is server running ? */
	{
		printf ("\nGraph creation failure. Please check that the server is running\n");
		exit (1);
	}
#endif

#if DISPLAY_TRIGGER
#if PER_SAMPLE
	TriggerIndexCount = 0;
#endif
#endif

	ClearDemodOutput ();										/* Clear the demodulate data output file */
																/* Initialize numerical bit synch. detector */
	SIF_DetectNumericalBitSequence (&SynchSequenceBitMask,			/* Synchronization sequence bit mask */
									&SynchSequenceDetectorState,	/* Detector state variable */
									SYNCH_SEQUENCE_LEN);			/* Synchronization sequence length */
	SynchDetectedFlag = SIGLIB_SEQUENCE_NOT_DETECTED;			/* Synch has not been detected */

#if DEBUG_LOG_FILE
SUF_ClearDebugfprintf();
#endif
#if DEBUG_DIBITS_TO_LOG_FILE
SUF_ClearDebugfprintf();
#endif

	if (argc != 2)
	{
		printf("Useage : qpskwav filename [delay]\nIt is not necessary to include the .wav extension\n\n");
		return;
	}


	strcpy (WavFileName, argv[1]);
	strcat (WavFileName, ".wav");

	printf ("Input .wav filename = %s\n", WavFileName);

	if ((pInputWavFile = fopen(WavFileName, "rb")) == NULL)	/* Note this file is binary */
	{
		printf ("Error opening input .WAV file\n");
		exit (1);
	}

	WavInfo = wav_read_header (pInputWavFile);
	if (WavInfo.NumberOfChannels == 0)	/* Check how many channels */
	{
		printf ("Error reading .WAV file header\n");
		exit (1);
	}
	wav_display_info (WavInfo);
	printf ("\nPlease hit <CR> to continue . . .\n");
	getchar ();


							/* Memory for processing modulated input data */
	pData = SUF_VectorArrayAllocate (SAMPLE_LENGTH);
	pInput = SUF_VectorArrayAllocate (DECIMATION_RATIO * SAMPLE_LENGTH);

							/* Memory for Costas loop synchronizer */
	pCostasLpLPFCoeffs = SUF_VectorArrayAllocate (COSTAS_LP_LPF_LENGTH);
	pCostasLpLPF1State = SUF_VectorArrayAllocate (COSTAS_LP_LPF_LENGTH);
	pCostasLpLPF2State = SUF_VectorArrayAllocate (COSTAS_LP_LPF_LENGTH);
	pCostasLpVCOLookUpTable = SUF_CostasLoopVCOArrayAllocate (COSTAS_LP_VCO_TABLE_SIZE);

	pELGMatchedFilterState = SUF_VectorArrayAllocate (SYMBOL_LENGTH);
	pELGEarlyGateDelay = SUF_VectorArrayAllocate (ELG_EARLY_GATE_DELAY_LENGTH);
	pELGLoopFilterCoeffs = SUF_VectorArrayAllocate (ELG_LOOP_FILTER_LENGTH);
	pELGLoopFilterState = SUF_VectorArrayAllocate (ELG_LOOP_FILTER_LENGTH);
	pELGRealOutputSynchDelay = SUF_VectorArrayAllocate (ELG_SYNCH_DELAY_ARRAY_LENGTH);
	pELGImagOutputSynchDelay = SUF_VectorArrayAllocate (ELG_SYNCH_DELAY_ARRAY_LENGTH);


	if ((pData == NULL) || (pCostasLpLPFCoeffs == NULL) || (pCostasLpLPF1State == NULL) ||
		(pCostasLpLPF2State == NULL) || (pCostasLpVCOLookUpTable == NULL) || (pInput == NULL) ||
		(pELGMatchedFilterState == NULL) || (pELGEarlyGateDelay == NULL) ||
		(pELGLoopFilterCoeffs == NULL) || (pELGLoopFilterState == NULL) ||
		(pELGRealOutputSynchDelay == NULL) || (pELGImagOutputSynchDelay == NULL))
	{
		printf ("Memory allocation failure\n");
		exit (0);
	}

				/* Always initialise to largest size first */
#if (DISPLAY_TIME_DOMAIN)
	hTimeDomainGraph =								/* Initialize graph */
		Create2DGraph ("Pi/4 DQPSK Demodulator Time Domain",	/* Graph title */
					   "Time",						/* X-Axis label */
					   "Magnitude",					/* Y-Axis label */
					   310.0,						/* Scaling mode */
					   SV_SIGNED,					/* Sign mode */
					   SV_GRAPH_LINE,				/* Graph type */
					   "localhost");				/* Graph server */

	if (hTimeDomainGraph == NULL)					/* Graph creation failed - e.g is server running ? */
	{
		printf ("Graph creation failure. Please check that the server is running\n");
		exit (1);
	}
#endif
#if (DISPLAY_EYE_DIAGRAM)
	hEyeDiagramGraph =								/* Initialize graph */
		Create2DGraph ("Pi/4 DQPSK Demodulator Eye Diagram",	/* Graph title */
					   "Time",						/* X-Axis label */
					   "Magnitude",					/* Y-Axis label */
					   SV_AUTO_SCALE,				/* Scaling mode */
					   SV_SIGNED,					/* Sign mode */
					   SV_GRAPH_LINE,				/* Graph type */
					   "localhost");				/* Graph server */

	if (hEyeDiagramGraph == NULL)					/* Graph creation failed - e.g is server running ? */
	{
		printf ("Graph creation failure. Please check that the server is running\n");
		exit (1);
	}
#endif


#if DEBUG_LOG_FILE
SUF_Debugfprintf ("Calling SIF_CostasQamDemodulate\n");
#endif
								/* Initialise the Costas loop QAM demodulator */
	ErrorCode =
		SIF_CostasQamDemodulate (&CostasLpVCOPhase,						/* VCO phase */
								 pCostasLpVCOLookUpTable,				/* VCO look up table */
								 COSTAS_LP_VCO_TABLE_SIZE,				/* VCO look up table size */
								 SYMBOL_RATE / SAMPLE_RATE,				/* Low-pass filter cut-off frequency */
								 pCostasLpLPF1State,					/* Pointer to loop filter 1 state */
								 &CostasLpLPF1Index,					/* Pointer to loop filter 1 index */
								 pCostasLpLPF2State,					/* Pointer to loop filter 2 state */
								 &CostasLpLPF2Index,					/* Pointer to loop filter 2 index */
								 pCostasLpLPFCoeffs,					/* Pointer to loop filter coefficients */
								 COSTAS_LP_LPF_LENGTH,					/* Loop filter length */
								 &CostasLpLoopFilterState,				/* Pointer to loop filter state */
								 &CostasLpState,						/* Pointer to delayed sample */
								 pELGMatchedFilterState,				/* Pointer to matched filter state array */
								 &ELGMatchedFilterIndex,				/* Pointer to matched filter index */
								 &ELGMatchedFilterSum,					/* Pointer to matched filter sum */
								 pELGEarlyGateDelay,					/* Pointer to early gate state array */
								 &ELGEarlyGateDelayIndex,				/* Pointer to early gate delay index */
								 ELG_EARLY_GATE_DELAY_LENGTH,			/* Early gate delay length */
								 pELGLoopFilterState,					/* Pointer to loop filter state array */
								 pELGLoopFilterCoeffs,					/* Pointer to loop filter coefficients */
								 &ELGLoopFilterIndex,					/* Pointer to loop filter index */
								 ELG_LOOP_FILTER_LENGTH,				/* Loop filter length */
								 ELG_LOOP_FILTER_FC/SAMPLE_RATE,		/* Loop filter cut-off / centre frequency */
								 &ELGPulseDetectorThresholdFlag,		/* Pointer to pulse detector threshold flag */
								 &ELGZeroCrossingPreviousSample,		/* Pointer to zero crossing previous sample */
								 &ELGTriggerCount,						/* Pointer to trigger counter */
								 &ELGTriggerDetectedFlag,				/* Pointer to trigger detected flag */
								 &ELGTriggerUpdatedFlag,				/* Pointer to trigger updated flag */
								 &ELGTriggerLatency,					/* Pointer to ELG trigger latency */
								 SYMBOL_LENGTH,							/* Samples per symbol */
								 pELGRealOutputSynchDelay,				/* Pointer to ELG real output synchronization delay state array */
								 pELGImagOutputSynchDelay,				/* Pointer to ELG imaginary output synchronization delay state array */
								 &ELGOutputSynchDelayIndex);			/* Pointer to ELG synchronization delay index */

													/* Initialize pre-filter */
	SIF_Fir (RxPreFilterState,						/* Pointer to filter state array */
			 &RxPreFilterIndex,						/* Pointer to filter index register */
			 RX_PRE_FILTER_LENGTH);					/* Filter length */

#if DEBUG_LOG_FILE
SUF_Debugfprintf ("Returned from SIF_CostasQamDemodulate\n");
#endif

#if DEBUG_LOG_FILE
SUF_Debugfprintf ("Returned from SIF_RootRaisedCosineFilter\n");
#endif

	if (ErrorCode != SIGLIB_NO_ERROR)
	{
		printf ("Error in SIF_CostasQamDemodulate\n");
		exit (0);
	}

#if DISPLAY_CONSTELLATION
	SDA_Clear ((SLData_t *)ReceivedConstellationPoints,
			   2*MAX_CONST_POINTS_PER_BURST);
#endif

	DemodulatorState = DEMODULATOR_RESET;			/* Set initial demodulator state */

													/* Main data processing loop */
	while ((ProcessSampleLength = wav_read_data (pInput, pInputWavFile, WavInfo, DECIMATION_RATIO*SAMPLE_LENGTH)) != 0L)	/* Successively read arrays of 128 samples*/
	{
#if DEBUG_LOG_FILE
	SampleCount+=ProcessSampleLength;
#endif
#if DEBUG_LOG_FILE
	SUF_DebugPrintArray (pInput, ProcessSampleLength);
#endif

#if ENABLE_DECIMATE
		ProcessSampleLength /= DECIMATION_RATIO;	/* Set the new sample length */

													/* Pre filter the data to select the pass-band only */
		for (i = 0; i < ProcessSampleLength; i++)	/* Decimate the input data stream */
		{
													/* Pre-filter the data */
			pData [i] =
				SDS_Fir (*(pInput + (i * DECIMATION_RATIO)),	/* Input data sample to be filtered */
						 RxPreFilterState,						/* Pointer to filter state array */
						 RxPreFilterCoeffs,						/* Pointer to filter coefficients */
						 &RxPreFilterIndex,						/* Pointer to filter index register */
						 RX_PRE_FILTER_LENGTH);					/* Filter length */
			for (j = 1; j < DECIMATION_RATIO; j++)	/* Add new samples into filter state array */
			{
				SDS_FirAddSample (*(pInput + (i * DECIMATION_RATIO) + j),	/* Input sample to add to delay line */
								  RxPreFilterState,							/* Pointer to filter state array */
								  &RxPreFilterIndex,						/* Pointer to filter index register */
								  RX_PRE_FILTER_LENGTH);					/* Filter length */
			}
		}
#else
													/* Pre-filter the data */
		SDA_Fir (pInput,							/* Input array to be filtered */
				 pData,								/* Filtered output array */
				 RxPreFilterState,					/* Pointer to filter state array */
				 RxPreFilterCoeffs,					/* Pointer to filter coefficients */
				 &RxPreFilterIndex,					/* Pointer to filter index register */
				 RX_PRE_FILTER_LENGTH,				/* Filter length */
				 ProcessSampleLength);				/* Array length */
#endif

		SDA_AgcPeak (pData,							/* Pointer to source array */
					 pData,							/* Pointer to destination array */
					 AGC_OUTPUT_LEVEL,				/* Peak to control to */
					 NOISE_FLOOR_THRESHOLD,			/* Minimum threshold */
					 AGC_ATTACK_SENSITIVITY,		/* Attack sensitivity */
					 AGC_DECAY_SENSITIVITY,			/* Decay sensitivity */
					 &AGCGain,						/* Previous gain pointer */
					 &AGCMax,						/* Previous max pointer */
					 AGC_SUB_ARRAY_LEN,				/* Sub array length */
					 ProcessSampleLength);			/* Array length */

													/* Test signal magnitude over noise threshold */
		FirstNonZeroSampleIndex =
			SDA_TestAbsOverThreshold (pData,					/* Pointer to source array */
									  NOISE_FLOOR_THRESHOLD,	/* Threshold */
									  ProcessSampleLength);		/* Array length */

														/* If we have finished demodulating a block of data then reset system */
		if (FirstNonZeroSampleIndex == SIGLIB_SIGNAL_NOT_PRESENT)	/* If signal is not over threshold */
		{
#if DEBUG_LOG_FILE
	SUF_Debugfprintf ("NoData : SampleCount = %ld\n", SampleCount);
#endif
			if (DemodulatorState == DEMODULATING_DATA)	/* If we have finished demodulating a block of data then reset system */
			{
#if DEBUG_LOG_FILE
	SUF_Debugfprintf ("Performing reset\n");
#endif
				DemodulatorState = DEMODULATOR_RESET;

				BurstSymbolCount = (SLArrayIndex_t)0;		/* Reset count of symbols received in a burst */
				CostasLoopVCOModulationIndex = COSTAS_LP_VCO_ACQ_MODE_MODULATION_GAIN;

				ELGTriggerDetectedFlag = (SLFixData_t)0;	/* Stop the output trigger until restarted */

															/* Reset the Costas loop */
				SRF_CostasLoop (&CostasLpVCOPhase,			/* VCO phase */
								pCostasLpLPF1State,			/* Pointer to loop filter 1 state */
								&CostasLpLPF1Index,			/* Pointer to loop filter 1 index */
								pCostasLpLPF2State,			/* Pointer to loop filter 2 state */
								&CostasLpLPF2Index,			/* Pointer to loop filter 2 index */
								COSTAS_LP_LPF_LENGTH,		/* Loop filter length */
								&CostasLpLoopFilterState,	/* Pointer to loop filter state */
								&CostasLpState);			/* Pointer to delayed sample */

#if DISPLAY_CONSTELLATION
				if (ConstellationBurstCount > 20)	/* Check that we have enough points to make it worthwhile displaying constellation */
				{
					DisplayXYPlot (hConstellationDiagramGraph,		/* Graph handle */
								   "Constellation Diagram",			/* Title of the dataset */
								   (Complex_s *)ReceivedConstellationPoints,	/* Array of complex dataset */
								   (int)ConstellationBurstCount,	/* Number of data points */
								   SV_GRAPH_LINE,					/* Graph type */
//								   SV_GRAPH_POINT,					/* Graph type */
								   SV_BLUE,							/* Colour */
								   SV_HIDE_MARKERS,					/* Marker enable / disable */
								   SV_GRAPH_NEW);					/* New graph */

					ConstellationBurstCount = 0L;
					printf ("Hit any key to exit..."); getchar ();
				}
#endif

				SynchDetectedFlag = SIGLIB_SEQUENCE_NOT_DETECTED;		/* Reset synch detected flag */
				DemodOutput (4);							/* Output a <CR> separator to demod output file */
#if DEBUG_DIBITS_TO_LOG_FILE
				SUF_Debugfprintf ("\n");
#endif
			}
		}

		else												/* Signal is over threshold */
		{
#if DEBUG_LOG_FILE
	SUF_Debugfprintf ("GotData : SampleCount = %ld\n", SampleCount);
#endif

			DemodulatorState = DEMODULATING_DATA;			/* Indicate state is demodulating data */

#if DISPLAY_TIME_DOMAIN
			if (FirstNonZeroSampleIndex != SIGLIB_SIGNAL_NOT_PRESENT)
			{
				Display2DGraph (hTimeDomainGraph,			/* Graph handle */
							    "Input data",				/* Title of the dataset */
							    pData,						/* Array of Double dataset */
							    ProcessSampleLength,		/* Number of data points */
								SV_GRAPH_LINE,				/* Graph type */
							    SV_BLUE,					/* Colour */
								SV_HIDE_MARKERS,			/* Marker enable / disable */
								SV_GRAPH_NEW);				/* New graph */
				printf ("Please hit <Carriage Return> to continue . . ."); getchar ();
			}
#endif
#if DISPLAY_FREQ_DOMAIN
			if (FirstNonZeroSampleIndex != SIGLIB_SIGNAL_NOT_PRESENT)
			{
				if (plot_frequency_domain (pData, SIGLIB_FLAT_TOP, "FFT Of Modulated Data", ProcessSampleLength, SAMPLE_LENGTH) != SIGLIB_NO_ERROR)
				{
					fclose (pInputWavFile);
					printf ("Memory allocation failure in plot_frequency_domain\n");
					exit (0);
				}
			}
#endif

#if DEBUG_LOG_FILE
	SUF_Debugfprintf ("Calling SDA_CostasQamDemodulate\n");
	SUF_Debugfprintf ("pData = %ld\n", (long)pData);
	SUF_Debugfprintf ("ProcessSampleLength = %ld\n", (long)ProcessSampleLength);
#endif


								/* Implement the Costas loop QAM demodulator */
#if PER_SAMPLE
			for (i = 0; i < ProcessSampleLength; i++)
			{
#if DISPLAY_EYE_DIAGRAM
				RxSymbolCount =
					SDS_CostasQamDemodulateDebug (*(pData+i),							/* Source data sample */
												  &RealMagn,							/* Pointer to real destination symbol point */
												  &ImagMagn,							/* Pointer to imaginary destination symbol point */
												  &CostasLpVCOPhase,					/* VCO phase */
												  CostasLoopVCOModulationIndex,			/* VCO modulation index */
												  pCostasLpVCOLookUpTable,				/* VCO look up table */
												  COSTAS_LP_VCO_TABLE_SIZE,				/* VCO look up table size */
												  CARRIER_FREQ / SAMPLE_RATE,			/* Carrier frequency */
												  pCostasLpLPF1State,					/* Pointer to loop filter 1 state */
												  &CostasLpLPF1Index,					/* Pointer to loop filter 1 index */
												  pCostasLpLPF2State,					/* Pointer to loop filter 2 state */
												  &CostasLpLPF2Index,					/* Pointer to loop filter 2 index */
												  pCostasLpLPFCoeffs,					/* Pointer to loop filter coefficients */
												  COSTAS_LP_LPF_LENGTH,					/* Loop filter length */
												  &CostasLpLoopFilterState,				/* Pointer to loop filter state */
												  COSTAS_LP_LOOP_FILTER_ALPHA,			/* Loop filter coefficient */
												  SIGLIB_COSTAS_LOOP_HARD_LIMITED_LOOP,	/* Loop feedback mode */
												  &CostasLpState,						/* Pointer to delayed sample */
												  pELGMatchedFilterState,				/* Pointer to matched filter state array */
												  &ELGMatchedFilterIndex,				/* Pointer to matched filter index */
												  &ELGMatchedFilterSum,					/* Pointer to matched filter sum */
												  pELGEarlyGateDelay,					/* Pointer to early gate state array */
												  &ELGEarlyGateDelayIndex,				/* Pointer to early gate delay index */
												  ELG_EARLY_GATE_DELAY_LENGTH,			/* Early gate delay length */
												  pELGLoopFilterState,					/* Pointer to loop filter state array */
												  pELGLoopFilterCoeffs,					/* Pointer to loop filter coefficients */
												  &ELGLoopFilterIndex,					/* Pointer to loop filter index */
												  ELG_LOOP_FILTER_LENGTH,				/* Loop filter length */
												  ELG_PULSE_DETECTOR_THRESHOLD,			/* Loop filter cut-off / centre frequency */
												  &ELGPulseDetectorThresholdFlag,		/* Pointer to pulse detector threshold flag */
												  &ELGZeroCrossingPreviousSample,		/* Pointer to zero crossing previous sample */
												  &ELGTriggerCount,						/* Pointer to trigger counter */
												  &ELGTriggerDetectedFlag,				/* Pointer to trigger detected flag */
												  &ELGTriggerUpdatedFlag,				/* Pointer to trigger updated flag */
												  SYMBOL_LENGTH,						/* Samples per symbol */
												  pELGRealOutputSynchDelay,				/* Pointer to ELG real output synchronization delay state array */
												  pELGImagOutputSynchDelay,				/* Pointer to ELG imaginary output synchronization delay state array */
												  &ELGOutputSynchDelayIndex,			/* Pointer to ELG synchronization delay index */
												  ELGTriggerLatency,					/* ELG output synchronization delay length */
												  DebugRealFilterOutput,				/* Pointer to debug real filter output */
												  DebugImagFilterOutput,				/* Pointer to debug imaginary filter output */
												  DebugELGTriggerOutput,				/* Pointer to debug ELG trigger output */
												  &TriggerIndexCount);					/* Pointer to debug ELG trigger count */
#else
				RxSymbolCount =
					SDS_CostasQamDemodulate (*(pData+i),							/* Source data sample */
											 &RealMagn,								/* Pointer to real destination symbol point */
											 &ImagMagn,								/* Pointer to imaginary destination symbol point */
											 &CostasLpVCOPhase,						/* VCO phase */
											 CostasLoopVCOModulationIndex,			/* VCO modulation index */
											 pCostasLpVCOLookUpTable,				/* VCO look up table */
											 COSTAS_LP_VCO_TABLE_SIZE,				/* VCO look up table size */
											 CARRIER_FREQ / SAMPLE_RATE,			/* Carrier frequency */
											 pCostasLpLPF1State,					/* Pointer to loop filter 1 state */
											 &CostasLpLPF1Index,					/* Pointer to loop filter 1 index */
											 pCostasLpLPF2State,					/* Pointer to loop filter 2 state */
											 &CostasLpLPF2Index,					/* Pointer to loop filter 2 index */
											 pCostasLpLPFCoeffs,					/* Pointer to loop filter coefficients */
											 COSTAS_LP_LPF_LENGTH,					/* Loop filter length */
											 &CostasLpLoopFilterState,				/* Pointer to loop filter state */
											 COSTAS_LP_LOOP_FILTER_ALPHA,			/* Loop filter coefficient */
											 SIGLIB_COSTAS_LOOP_HARD_LIMITED_LOOP,	/* Loop feedback mode */
											 &CostasLpState,						/* Pointer to delayed sample */
											 pELGMatchedFilterState,				/* Pointer to matched filter state array */
											 &ELGMatchedFilterIndex,				/* Pointer to matched filter index */
											 &ELGMatchedFilterSum,					/* Pointer to matched filter sum */
											 pELGEarlyGateDelay,					/* Pointer to early gate state array */
											 &ELGEarlyGateDelayIndex,				/* Pointer to early gate delay index */
											 ELG_EARLY_GATE_DELAY_LENGTH,			/* Early gate delay length */
											 pELGLoopFilterState,					/* Pointer to loop filter state array */
											 pELGLoopFilterCoeffs,					/* Pointer to loop filter coefficients */
											 &ELGLoopFilterIndex,					/* Pointer to loop filter index */
											 ELG_LOOP_FILTER_LENGTH,				/* Loop filter length */
											 ELG_PULSE_DETECTOR_THRESHOLD,			/* Loop filter cut-off / centre frequency */
											 &ELGPulseDetectorThresholdFlag,		/* Pointer to pulse detector threshold flag */
											 &ELGZeroCrossingPreviousSample,		/* Pointer to zero crossing previous sample */
											 &ELGTriggerCount,						/* Pointer to trigger counter */
											 &ELGTriggerDetectedFlag,				/* Pointer to trigger detected flag */
											 &ELGTriggerUpdatedFlag,				/* Pointer to trigger updated flag */
											 SYMBOL_LENGTH,							/* Samples per symbol */
											 pELGRealOutputSynchDelay,				/* Pointer to ELG real output synchronization delay state array */
											 pELGImagOutputSynchDelay,				/* Pointer to ELG imaginary output synchronization delay state array */
											 &ELGOutputSynchDelayIndex,				/* Pointer to ELG synchronization delay index */
											 ELGTriggerLatency);					/* ELG output synchronization delay length */
#endif

									/* If we have received a symbol and the magnitude is above the threshold */
				if ((RxSymbolCount == 1) && (((RealMagn*RealMagn)+(ImagMagn*ImagMagn)) > CONSTELLATION_THRESHOLD))
				{
#if DEBUG_LOG_FILE
					SUF_Debugfprintf ("pRealOutput = %lf, pImagOutput = %lf, Magnitude = %lf\n", RealMagn, ImagMagn, SDS_Sqrt((RealMagn*RealMagn)+(ImagMagn*ImagMagn)));
#endif
#if DISPLAY_CONSTELLATION
					if (ConstellationBurstCount < MAX_CONST_POINTS_PER_BURST)	/* Ensure we don't overflow array */
					{
						ReceivedConstellationPoints[ConstellationBurstCount].real = RealMagn;
						ReceivedConstellationPoints[ConstellationBurstCount].imag = ImagMagn;
						ConstellationBurstCount++;
					}
#endif

					for (j = 0; j < CONSTELLATION_POINTS; j++)		/* Calculate the magnitude square errors from the ideal points */
					{
						SLData_t	RealError = (IdealConstellationPoints[j].real - RealMagn);
						SLData_t	ImagError = (IdealConstellationPoints[j].imag - ImagMagn);
						ConstellationPointErrors[j] = (RealError*RealError) + (ImagError * ImagError);
					}

														/* Select minimum error point */
					ReceivedConstellationPoint =
						SDA_MinPos (ConstellationPointErrors,	/* Pointer to source array */
									CONSTELLATION_POINTS);		/* Array length */

														/* Use octant shift (rounded to nearest to calculate the di-bits */
					RxDiBit = ConstellationPointDeltaDibits [((int)(ReceivedConstellationPoint - PrevReceivedConstellationPoint)) + 7];
					PrevReceivedConstellationPoint = ReceivedConstellationPoint;	/* Save constellation point index for next iteration */

#if DEBUG_DIBITS_TO_LOG_FILE
//					SUF_Debugfprintf ("ReceivedConstellationPoint = %d\n", (int)ReceivedConstellationPoint);
					if (RxDiBit == 0x0) SUF_Debugfprintf ("00");
					else if (RxDiBit == 0x1) SUF_Debugfprintf ("01");
					else if (RxDiBit == 0x2) SUF_Debugfprintf ("10");
					else if (RxDiBit == 0x3) SUF_Debugfprintf ("11");
//					SUF_Debugfprintf ("\n");
#endif

					BurstSymbolCount++;
					if (BurstSymbolCount == 20)			/* Only acquire timing on 0x03 symbols */
					{
						CostasLoopVCOModulationIndex = COSTAS_LP_VCO_TRACK_MODE_MODULATION_GAIN;
					}

					if (SynchDetectedFlag == SIGLIB_SEQUENCE_NOT_DETECTED)		/* If we haven't got synch then look for it */
					{
														/* Search for synchronization sequence */
						SynchDetectedFlag =
							SDS_DetectNumericalBitSequence ((SLFixData_t)(RxDiBit & 0x3),	/* Input word */
															SynchSequence,					/* Synchronization sequence */
															SynchSequenceBitMask,			/* Synchronization sequence bit mask */
															&SynchSequenceDetectorState,	/* Detector state variable */
															SYNCH_SEQUENCE_WORD_LEN);		/* Input word length */

						if (SynchDetectedFlag != SIGLIB_SEQUENCE_NOT_DETECTED)	/* If we have just detected synch then save synch sequence to output file */
						{
														/* Output the synch sequence to the output file */
							DemodOutput ((SLInt8_t)0x1); DemodOutput ((SLInt8_t)0x1);
							DemodOutput ((SLInt8_t)0x0); DemodOutput ((SLInt8_t)0x1);
							DemodOutput ((SLInt8_t)0x3); DemodOutput ((SLInt8_t)0x3);
							DemodOutput ((SLInt8_t)0x2);
						}
					}
					else
					{
						DemodOutput ((SLInt8_t)(RxDiBit & 0x3));		/* Output the dibit to demod output file */
					}
				}
			}
#else
#if DISPLAY_EYE_DIAGRAM
			RxSymbolCount =
				SDA_CostasQamDemodulateDebug (pData,								/* Source data pointer */
											  pRealOutput,							/* Real destination data pointer */
											  pImagOutput,							/* Imaginary destination data pointer */
											  &CostasLpVCOPhase,					/* VCO phase */
											  CostasLoopVCOModulationIndex,			/* VCO modulation index */
											  pCostasLpVCOLookUpTable,				/* VCO look up table */
											  COSTAS_LP_VCO_TABLE_SIZE,				/* VCO look up table size */
											  CARRIER_FREQ / SAMPLE_RATE,			/* Carrier frequency */
											  pCostasLpLPF1State,					/* Pointer to loop filter 1 state */
											  &CostasLpLPF1Index,					/* Pointer to loop filter 1 index */
											  pCostasLpLPF2State,					/* Pointer to loop filter 2 state */
											  &CostasLpLPF2Index,					/* Pointer to loop filter 2 index */
											  pCostasLpLPFCoeffs,					/* Pointer to loop filter coefficients */
											  COSTAS_LP_LPF_LENGTH,					/* Loop filter length */
											  &CostasLpLoopFilterState,				/* Pointer to loop filter state */
											  COSTAS_LP_LOOP_FILTER_ALPHA,			/* Loop filter coefficient */
											  SIGLIB_COSTAS_LOOP_HARD_LIMITED_LOOP,	/* Loop feedback mode */
											  &CostasLpState,						/* Pointer to delayed sample */
											  pELGMatchedFilterState,				/* Pointer to matched filter state array */
											  &ELGMatchedFilterIndex,				/* Pointer to matched filter index */
											  &ELGMatchedFilterSum,					/* Pointer to matched filter sum */
											  pELGEarlyGateDelay,					/* Pointer to early gate state array */
											  &ELGEarlyGateDelayIndex,				/* Pointer to early gate delay index */
											  ELG_EARLY_GATE_DELAY_LENGTH,			/* Early gate delay length */
											  pELGLoopFilterState,					/* Pointer to loop filter state array */
											  pELGLoopFilterCoeffs,					/* Pointer to loop filter coefficients */
											  &ELGLoopFilterIndex,					/* Pointer to loop filter index */
											  ELG_LOOP_FILTER_LENGTH,				/* Loop filter length */
											  ELG_PULSE_DETECTOR_THRESHOLD,			/* Noise threshold */
											  &ELGPulseDetectorThresholdFlag,		/* Pointer to pulse detector threshold flag */
											  &ELGZeroCrossingPreviousSample,		/* Pointer to zero crossing previous sample */
											  &ELGTriggerCount,						/* Pointer to trigger counter */
											  &ELGTriggerDetectedFlag,				/* Pointer to trigger detected flag */
											  &ELGTriggerUpdatedFlag,				/* Pointer to trigger updated flag */
											  SYMBOL_LENGTH,						/* Samples per symbol */
											  pELGRealOutputSynchDelay,				/* Pointer to ELG real output synchronization delay state array */
											  pELGImagOutputSynchDelay,				/* Pointer to ELG imaginary output synchronization delay state array */
											  &ELGOutputSynchDelayIndex,			/* Pointer to ELG synchronization delay index */
											  ELGTriggerLatency,					/* ELG output synchronization delay length */
											  ProcessSampleLength,					/* Source array length */
											  DebugRealFilterOutput,				/* Pointer to debug real filter output */
											  DebugImagFilterOutput,				/* Pointer to debug imaginary filter output */
											  DebugELGTriggerOutput);				/* Pointer to debug ELG trigger output */
#else
			RxSymbolCount =
				SDA_CostasQamDemodulate (pData,									/* Source data pointer */
										 pRealOutput,							/* Real destination data pointer */
										 pImagOutput,							/* Imaginary destination data pointer */
										 &CostasLpVCOPhase,						/* VCO phase */
										 CostasLoopVCOModulationIndex,			/* VCO modulation index */
										 pCostasLpVCOLookUpTable,				/* VCO look up table */
										 COSTAS_LP_VCO_TABLE_SIZE,				/* VCO look up table size */
										 CARRIER_FREQ / SAMPLE_RATE,			/* Carrier frequency */
										 pCostasLpLPF1State,					/* Pointer to loop filter 1 state */
										 &CostasLpLPF1Index,					/* Pointer to loop filter 1 index */
										 pCostasLpLPF2State,					/* Pointer to loop filter 2 state */
										 &CostasLpLPF2Index,					/* Pointer to loop filter 2 index */
										 pCostasLpLPFCoeffs,					/* Pointer to loop filter coefficients */
										 COSTAS_LP_LPF_LENGTH,					/* Loop filter length */
										 &CostasLpLoopFilterState,				/* Pointer to loop filter state */
										 COSTAS_LP_LOOP_FILTER_ALPHA,			/* Loop filter coefficient */
										 SIGLIB_COSTAS_LOOP_HARD_LIMITED_LOOP,	/* Loop feedback mode */
										 &CostasLpState,						/* Pointer to delayed sample */
										 pELGMatchedFilterState,				/* Pointer to matched filter state array */
										 &ELGMatchedFilterIndex,				/* Pointer to matched filter index */
										 &ELGMatchedFilterSum,					/* Pointer to matched filter sum */
										 pELGEarlyGateDelay,					/* Pointer to early gate state array */
										 &ELGEarlyGateDelayIndex,				/* Pointer to early gate delay index */
										 ELG_EARLY_GATE_DELAY_LENGTH,			/* Early gate delay length */
										 pELGLoopFilterState,					/* Pointer to loop filter state array */
										 pELGLoopFilterCoeffs,					/* Pointer to loop filter coefficients */
										 &ELGLoopFilterIndex,					/* Pointer to loop filter index */
										 ELG_LOOP_FILTER_LENGTH,				/* Loop filter length */
										 ELG_PULSE_DETECTOR_THRESHOLD,			/* Noise threshold */
										 &ELGPulseDetectorThresholdFlag,		/* Pointer to pulse detector threshold flag */
										 &ELGZeroCrossingPreviousSample,		/* Pointer to zero crossing previous sample */
										 &ELGTriggerCount,						/* Pointer to trigger counter */
										 &ELGTriggerDetectedFlag,				/* Pointer to trigger detected flag */
										 &ELGTriggerUpdatedFlag,				/* Pointer to trigger updated flag */
										 SYMBOL_LENGTH,							/* Samples per symbol */
										 pELGRealOutputSynchDelay,				/* Pointer to ELG real output synchronization delay state array */
										 pELGImagOutputSynchDelay,				/* Pointer to ELG imaginary output synchronization delay state array */
										 &ELGOutputSynchDelayIndex,				/* Pointer to ELG synchronization delay index */
										 ELGTriggerLatency,						/* ELG output synchronization delay length */
										 ProcessSampleLength);					/* Source array length */
#endif

#if DEBUG_LOG_FILE
			for (i = 0; i < RxSymbolCount; i++)			/* Plot all consteallation diagram points */
			{
				SUF_Debugfprintf ("pRealOutput = %lf, pImagOutput = %lf, Magnitude = %lf\n", pRealOutput[i], pImagOutput[i], SDS_Sqrt((pRealOutput[i]*pRealOutput[i])+(pImagOutput[i]*pImagOutput[i])));
			}
#endif

#if DEBUG_LOG_FILE
			SUF_Debugfprintf ("Returned from CostasQamDemodulate\n");
#endif

															/* Decode the receive bits */
			for (i = 0; i < RxSymbolCount; i++)				/* For all consteallation diagram points */
			{
#if DISPLAY_CONSTELLATION
#if DEBUG_LOG_FILE
				SUF_Debugfprintf ("Display const - RxSymbolCount = %d\n", (int)RxSymbolCount);
				SUF_Debugfprintf ("Display const - i = %d\n", (int)i);
#endif
				if (ConstellationBurstCount < MAX_CONST_POINTS_PER_BURST)	/* Ensure we don't overflow array */
				{
					ReceivedConstellationPoints[ConstellationBurstCount].real = pRealOutput[i];
					ReceivedConstellationPoints[ConstellationBurstCount].imag = pImagOutput[i];
					ConstellationBurstCount++;
				}
#endif
#if DEBUG_LOG_FILE
				SUF_Debugfprintf ("Decode - i = %d\n", (int)i);
#endif

																	/* Check the magnitude - only output if above threshold */
				if (((pRealOutput[i] * pRealOutput[i]) + (pImagOutput[i] * pImagOutput[i])) > CONSTELLATION_THRESHOLD)
				{
					for (j = 0; j < CONSTELLATION_POINTS; j++)		/* Calculate the errors from the ideal points */
					{
						SLData_t	RealError = (IdealConstellationPoints[j].real - pRealOutput[i]);
						SLData_t	ImagError = (IdealConstellationPoints[j].imag - pImagOutput[i]);
						ConstellationPointErrors[j] = SDS_Sqrt ((RealError*RealError) + (ImagError * ImagError));
					}
																	/* Select minimum error point */
					ReceivedConstellationPoint =
						SDA_MinPos (ConstellationPointErrors,		/* Pointer to source array */
									CONSTELLATION_POINTS);			/* Array length */
					RxDiBit = ConstellationPointDeltaDibits [(int)((ReceivedConstellationPoint - PrevReceivedConstellationPoint) + 0.1) + 7];
					PrevReceivedConstellationPoint = ReceivedConstellationPoint;	/* Save constellation point index for next iteration */

					DemodOutput ((SLInt8_t)(RxDiBit & 0x3));		/* Output the dibit to demod output file */
				}
			}
#endif			/* End of SDA_SDA_CostasQamDemodulate processing loop */

#if DISPLAY_EYE_DIAGRAM
		if (FirstNonZeroSampleIndex != SIGLIB_SIGNAL_NOT_PRESENT)
		{
			WipeGraph (hEyeDiagramGraph);

			for (i = 0; i < (10 * EYE_DIAGRAM_SIZE); i += EYE_DIAGRAM_SIZE)
			{
				Display2DGraph (hEyeDiagramGraph,			/* Graph handle */
							    "Real data",				/* Title of the dataset */
							    DebugRealFilterOutput+i,	/* Array of Double dataset */
							    EYE_DIAGRAM_SIZE,			/* Number of data points */
								SV_GRAPH_LINE,				/* Graph type */
							    SV_BLUE,					/* Colour */
								SV_HIDE_MARKERS,			/* Marker enable / disable */
								SV_GRAPH_ADD);				/* New graph */
				Display2DGraph (hEyeDiagramGraph,			/* Graph handle */
							    "Imaginary data",			/* Title of the dataset */
							    DebugImagFilterOutput+i,	/* Array of Double dataset */
							    EYE_DIAGRAM_SIZE,			/* Number of data points */
								SV_GRAPH_LINE,				/* Graph type */
							    SV_RED,						/* Colour */
								SV_HIDE_MARKERS,			/* Marker enable / disable */
								SV_GRAPH_ADD);				/* New graph */
			}
#if DISPLAY_TRIGGER
													/* Scale the trigger to plot */
			SDA_Multiply (DebugELGTriggerOutput,	/* Input data pointer */
						  60.0,						/* Scaling factor */
						  DebugELGTriggerOutput,	/* Output data pointer */
						  EYE_DIAGRAM_SIZE);		/* Array length */
			Display2DGraph (hEyeDiagramGraph,			/* Graph handle */
							"DebugELGTriggerOutput",	/* Title of the dataset */
							DebugELGTriggerOutput,		/* Array of Double dataset */
							EYE_DIAGRAM_SIZE,			/* Number of data points */
							SV_GRAPH_LINE,				/* Graph type */
							SV_CYAN,					/* Colour */
							SV_HIDE_MARKERS,			/* Marker enable / disable */
							SV_GRAPH_ADD);				/* New graph */
#endif
			printf ("Please hit <Carriage Return> to continue . . ."); getchar ();
		}
#endif
#if DISPLAY_EYE_DIAGRAM
#if PER_SAMPLE
	TriggerIndexCount = 0;
#endif
#endif
		}					/* End of processing loop */

	}						/* End of while we have data from the .wav file */

#if DEBUG_LOG_FILE
	SUF_Debugfprintf ("Got to the end\n");
#endif

	fclose (pInputWavFile);

	SUF_MemoryFree (pData);							/* Free memory */
	SUF_MemoryFree (pCostasLpLPFCoeffs);
	SUF_MemoryFree (pCostasLpLPF1State);
	SUF_MemoryFree (pCostasLpLPF2State);
	SUF_MemoryFree (pCostasLpVCOLookUpTable);
	SUF_MemoryFree (pELGMatchedFilterState);
	SUF_MemoryFree (pELGEarlyGateDelay);
	SUF_MemoryFree (pELGLoopFilterCoeffs);
	SUF_MemoryFree (pELGLoopFilterState);
	SUF_MemoryFree (pELGRealOutputSynchDelay);
	SUF_MemoryFree (pELGImagOutputSynchDelay);

}


/**/
/********************************************************
* Function : ClearDemodOutput
*
* Parameters :
*	None
*
* Return value :
*	Error code
*
* Description : Clear the demodulated data output file.
*
********************************************************/

SLError_t ClearDemodOutput (void)
{
	FILE *fp_LogFile;
	fp_LogFile = fopen( "DemodOutput.txt", "w");
	if (fp_LogFile == NULL)
	{
		return (SIGLIB_FILE_ERROR);
	}
	fclose (fp_LogFile);

	return (SIGLIB_NO_ERROR);
}


/**/
/********************************************************
* Function : DemodOutput
*
* Parameters :
*	Input bits - if not 0, 1, 2 or 3 then enters a CR
*
* Return value :
*	Error code
*
* Description : Prints string to demodulated data output file
*
********************************************************/

SLError_t DemodOutput (SLInt8_t Input)

{
	FILE *fp_LogFile;
	fp_LogFile = fopen ("DemodOutput.txt", "a");
	if (fp_LogFile == NULL)
	{
		return (SIGLIB_FILE_ERROR);
	}

	switch (Input)
	{
		case 0:
			fprintf (fp_LogFile, "00");
			break;
		case 1:
			fprintf (fp_LogFile, "01");
			break;
		case 2:
			fprintf (fp_LogFile, "10");
			break;
		case 3:
			fprintf (fp_LogFile, "11");
			break;
		case 4:
			fprintf (fp_LogFile, "\n");
			break;
		default:
			fprintf (fp_LogFile, "%d", (int) Input);
			break;
	}
	fclose (fp_LogFile);

	return (SIGLIB_NO_ERROR);
}

