/* SigLib BPSK Example
Simulating :
400 bps
'1' = cos (theta), '0' = - cos (theta)
Carrier frequency = 1200 Hz.
Sample rate = 9600 Hz.
For a typical transmitter the modultor would be
followed by a filter e.g a root raised cosine filter,
which can be implemented using the SigLib
SIF_RootRaisedCosineFilter function.
*/

#include <stdio.h>
#include <siglib.h>
#include "GraphFunctions.h"
#include <dpchar.h>

/* Define constants */
#define	DISPLAY_GRAPHICS				0					/* Set to '1' to display graphics */
#define DISPLAY_FILTER_OUTPUT			0					/* Set to '1' to display the output of the demodulator filter */
#define TX_BIT_MODE_ENABLED				0					/* Set to '1' to process Tx bits, '0' for bytes */
#define DEBUG_LOG_FILE					0					/* Set to '1' to enable logging to debug.log */

#define	SAMPLE_LENGTH					((SLArrayIndex_t)512)
#define	NUMBER_OF_LOOPS					((SLArrayIndex_t)8)

#define	SAMPLE_RATE						9600.0
#define	BAUD_RATE						600.0
#define	CARRIER_FREQ					1200.0

#define SYMBOL_LENGTH					16						/* Number of samples per symbol - SAMPLE_RATE / BAUD_RATE */
#define MAX_RX_STRING_LENGTH			((SLArrayIndex_t)80)	/* Maximum length of an Rx string */
#define CARRIER_TABLE_FREQ				((SLData_t)100.0)		/* Frequency of sine wave in table */
#define CARRIER_SINE_TABLE_SIZE			((SLArrayIndex_t)	(SAMPLE_RATE / CARRIER_TABLE_FREQ))		/* Number of samples in each of cos and sine table */

/* Declare arrays and variables */
char TxString[] = "dHello World - abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
										/* The 'd' at the start is a dummy character that is not
										received while the filters are initialized */

char RxString[MAX_RX_STRING_LENGTH];

char *TxStringPtr, *RxStringPtr;

SLData_t	*pData, *pCarrierTable;

SLData_t	TxCarrierPhase;
SLData_t	SampleCount;


#define	COSTAS_LP_LPF_LENGTH			17L					/* Costas loop LP LPF FIR filter length */
#define	VCO_MODULATION_INDEX			((SLData_t)0.005)	/* Modulation index */

#define LOOP_FILTER_ALPHA				((SLData_t)0.9)		/* Feedback coeff for one-pole loop filter */

#define	VCO_SINE_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		*pVCOLookUpTable;							/* VCO cosine look-up-table pointer */

SLArrayIndex_t	RxSampleClock;								/* Used to keep track of the samples and symbols */
SLData_t		RxSampleSum;								/* Used to keep decide which bit was Tx'd */

#if DISPLAY_FILTER_OUTPUT
SLData_t		*pFilterOutput;
#endif

void main(void)

{
	GraphObject *h2DGraph;							/* Declare graph object */

	SLFixData_t		i, j;
	SLFixData_t		LoopCount;
#if TX_BIT_MODE_ENABLED
	SLArrayIndex_t	TxBitIndex;
#endif
	SLArrayIndex_t	DemodulatedBit;
	SLArrayIndex_t	RxBitIndex = ((SLArrayIndex_t)7);		/* Initialise bit count for correct synchronization */

#if DEBUG_LOG_FILE
	SUF_ClearDebugfprintf ();
	for (i = 0; i < 20; i++)
	{
		SUF_Debugfprintf ("TxString[%ld]", i);
		dpchar (TxString[i]);
	}
#endif

														/* Allocate memory */
	pData = SUF_VectorArrayAllocate (SAMPLE_LENGTH + 10);	/* Not always returning the same number of samples from the modulator */
	pCarrierTable = SUF_VectorArrayAllocate (CARRIER_SINE_TABLE_SIZE);

	pCostasLpLPFCoeffs = SUF_VectorArrayAllocate (COSTAS_LP_LPF_LENGTH);
	pCostasLpLPF1State = SUF_VectorArrayAllocate (COSTAS_LP_LPF_LENGTH);
	pCostasLpLPF2State = SUF_VectorArrayAllocate (COSTAS_LP_LPF_LENGTH);
	pVCOLookUpTable = SUF_CostasLoopVCOArrayAllocate (VCO_SINE_TABLE_SIZE);

	if ((pData == NULL) || (pCarrierTable == NULL) || (pCostasLpLPFCoeffs == NULL) || (pCostasLpLPF1State == NULL) ||
		(pCostasLpLPF2State == NULL) || (pVCOLookUpTable == NULL))
	{
		printf ("Memory allocation failure\n\n");
	}


#if DISPLAY_FILTER_OUTPUT
	pFilterOutput = SUF_VectorArrayAllocate (SAMPLE_LENGTH);	/* Array for displaying output of LPF */
	SDA_Clear (pFilterOutput,						/* Pointer to destination array */
			   SAMPLE_LENGTH);						/* Array length */
#endif


	TxStringPtr = TxString;
	RxStringPtr = RxString;

#if (DISPLAY_FILTER_OUTPUT) || (DISPLAY_GRAPHICS)
	h2DGraph =										/* Initialize graph */
		Create2DGraph ("Binary Phase Shift Keying",	/* 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 (h2DGraph == NULL)							/* Graph creation failed - e.g is server running ? */
	{
		printf ("\nGraph creation failure. Please check that the server is running\n");
		exit (1);
	}
#endif

	SIF_BpskModulate (pCarrierTable,						/* Carrier table pointer */
					  (CARRIER_TABLE_FREQ / SAMPLE_RATE),	/* Carrier frequency */
					  &SampleCount,							/* Transmitter sample count - tracks samples */
					  CARRIER_SINE_TABLE_SIZE);				/* Carrier sine table size */

	SIF_BpskDemodulate (&CostasLpVCOPhase,						/* VCO phase */
						pVCOLookUpTable,						/* VCO look up table */
						VCO_SINE_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 */
						&CostasLpState,							/* Pointer to delayed sample */
						&RxSampleClock,							/* Pointer to Rx sample clock */
						&RxSampleSum);							/* Pointer to Rx sample sum - used to decide which bit was Tx'd */

	TxCarrierPhase = SIGLIB_ZERO;			/* Initialise BPSK transmitter phase */
											/* The phase of the transmitter can be rotated by
												changing this value */

											/* Clear demodulated data input array - This is important
												because we are going to be ORing in the received bits */
	for (i = 0; i < MAX_RX_STRING_LENGTH; i++)
	{
		RxString[i] = 0;
	}

	for (LoopCount = 0; LoopCount < NUMBER_OF_LOOPS; LoopCount++)
	{
		for (i = 0; i < SAMPLE_LENGTH; i += (SIGLIB_BYTE_LENGTH * SYMBOL_LENGTH))
		{
#if TX_BIT_MODE_ENABLED
			for (TxBitIndex = 0; TxBitIndex < SIGLIB_BYTE_LENGTH; TxBitIndex++)
			{
				SDA_BpskModulate ((*TxStringPtr >> TxBitIndex),				/* Modulating bit */
								  pData + i + (TxBitIndex * SYMBOL_LENGTH),	/* Destination array */
								  pCarrierTable,							/* Carrier table pointer */
								  &TxCarrierPhase,							/* Carrier phase pointer */
								  SYMBOL_LENGTH,							/* Samples per symbol */
								  CARRIER_FREQ / CARRIER_TABLE_FREQ,		/* Carrier table increment */
								  CARRIER_SINE_TABLE_SIZE);					/* Carrier sine table size */
			}
			TxStringPtr++;							/* Increment string pointer */

#else
			SDA_BpskModulateByte (*TxStringPtr++,						/* Modulating byte */
								  pData + i,							/* Destination array */
								  pCarrierTable,						/* Carrier table pointer */
								  &TxCarrierPhase,						/* Carrier phase pointer */
								  SYMBOL_LENGTH,						/* Samples per symbol */
								  CARRIER_FREQ / CARRIER_TABLE_FREQ,	/* Carrier table increment */
								  CARRIER_SINE_TABLE_SIZE);				/* Carrier sine table size */
#endif
		}

#if	DISPLAY_GRAPHICS
		Display2DGraph (h2DGraph,					/* Graph handle */
					    "Modulated Signal",			/* Title of the dataset */
					    pData,						/* Array of Double dataset */
					    SAMPLE_LENGTH,				/* Number of data points */
						SV_GRAPH_LINE,				/* Graph type */
					    SV_BLUE,					/* Colour */
						SV_HIDE_MARKERS,			/* Marker enable / disable */
						SV_GRAPH_NEW);				/* New graph */
		printf ("\nModulated Signal\nPlease hit <Carriage Return> to continue . . ."); getchar ();
#endif


		for (i = 0; i < SAMPLE_LENGTH; i += (SIGLIB_BYTE_LENGTH * SYMBOL_LENGTH))
		{
			for (j = 0; j < SIGLIB_BYTE_LENGTH; j++)
			{
#if DISPLAY_FILTER_OUTPUT
				DemodulatedBit =
					SDA_BpskDemodulateDebug (pData + i + (j * SYMBOL_LENGTH),		/* Source array */
											 &CostasLpVCOPhase,						/* VCO phase */
											 VCO_MODULATION_INDEX,					/* VCO modulation index */
											 pVCOLookUpTable,						/* VCO look up table */
											 VCO_SINE_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 */
											 LOOP_FILTER_ALPHA,						/* Loop filter coefficient */
											 &CostasLpState,						/* Pointer to delayed sample */
											 &RxSampleClock,						/* Pointer to receive sample clock */
											 &RxSampleSum,							/* Pointer to Rx sample sum - used to decide which bit was Tx'd */
											 SYMBOL_LENGTH,							/* Samples per symbol */
											 pFilterOutput);						/* Pointer to filter output data */

				Display2DGraph (h2DGraph,					/* Graph handle */
							    "Filter Output",			/* Title of the dataset */
							    pFilterOutput,				/* Array of Double dataset */
							    SAMPLE_LENGTH,				/* Number of data points */
								SV_GRAPH_LINE,				/* Graph type */
							    SV_BLUE,					/* Colour */
								SV_HIDE_MARKERS,			/* Marker enable / disable */
								SV_GRAPH_NEW);				/* New graph */
				printf ("\nFilter Output\nPlease hit <Carriage Return> to continue . . ."); getchar ();
#else
				DemodulatedBit =
					SDA_BpskDemodulate (pData + i + (j * SYMBOL_LENGTH),		/* Source array */
										&CostasLpVCOPhase,						/* VCO phase */
										VCO_MODULATION_INDEX,					/* VCO modulation index */
										pVCOLookUpTable,						/* VCO look up table */
										VCO_SINE_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 */
										LOOP_FILTER_ALPHA,						/* Loop filter coefficient */
										&CostasLpState,							/* Pointer to delayed sample */
										&RxSampleClock,							/* Pointer to receive sample clock */
										&RxSampleSum,							/* Pointer to Rx sample sum - used to decide which bit was Tx'd */
										SYMBOL_LENGTH);							/* Samples per symbol */
#endif

				*RxStringPtr |= (((char)DemodulatedBit) << RxBitIndex);
				RxBitIndex++;
				if (RxBitIndex >= SIGLIB_BYTE_LENGTH)
				{
					RxBitIndex = SIGLIB_AI_ZERO;
					*RxStringPtr++;
				}
			}
		}
	}

#if DEBUG_LOG_FILE
	for (i = 0; i < 20; i++)
	{
		SUF_Debugfprintf ("RxString[%ld]", i);
		dpchar (RxString[i]);
	}
#endif

	*RxStringPtr = 0;								/* Terminate string for printf */
						/* Print received string - Note the first two characters received are not
						from the required string due to receiver filter initialization */
	printf ("BPSK received string : %s\n", RxString+2);


	SUF_MemoryFree (pData);							/* Free memory */
	SUF_MemoryFree (pCarrierTable);
	SUF_MemoryFree (pCostasLpLPFCoeffs);
	SUF_MemoryFree (pCostasLpLPF1State);
	SUF_MemoryFree (pCostasLpLPF2State);
	SUF_MemoryFree (pVCOLookUpTable);

}



