/* SigLib BPSK With 8KHz Sample Rate Example
Simulating :
400 bps
'1' = cos (theta), '0' = - cos (theta)
Carrier frequency = 1200 Hz.
Sample rate = 8000 Hz.

The number of  samples per symbol is 13.333
this means that we need to account for the non integer
value by using 13 samples per symbol for 2 out of 3 symbols
and 14 samples per symbol on every 3rd symbol.

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>			/* Plot bit sequences */


/* Define constants */

#define	DISPLAY_GRAPHICS				0					/* Set to '1' to display graphics */
#define	DISPLAY_DEBUG_INFO				1					/* Set to '1' to display receiver debug data */
#define	DISPLAY_BIT_PATTERN				1					/* Set to '1' to display Tx and Rx bit patterns */

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

#define	SAMPLE_RATE						8000.0
#define	BAUD_RATE						600.0
#define	CARRIER_FREQ					1200.0

#define SYMBOL_LENGTH					13					/* Number of samples per symbol - integer part */
#define SYMBOLS_PER_DATA_SET			24					/* Number of symbols per data set - 3 bytes * # symbols per byte */
#define MAX_RX_STRING_LENGTH			((SLArrayIndex_t)80)	/* Maximum length of an Rx string */
#define CARRIER_TABLE_FREQ				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[] = "dddHello World - abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
										/* The 'd' at the start are a dummy characters that are not
										received while the filters are being 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			15L			/* Costas loop LP LPF FIR filter length */
#define	VCO_MODULATION_INDEX			0.0001		/* Modulation index */

#define LOOP_FILTER_ALPHA				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_DEBUG_INFO)
SLData_t	DebugArray[SAMPLE_LENGTH];
SLArrayIndex_t	DebugArrayOffset;
#endif

void main (void);

void   main(void)

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

	SLArrayIndex_t	i, j;
	SLFixData_t		LoopCount;
	SLArrayIndex_t	TxBitIndex;
	SLArrayIndex_t	DemodulatedBit;
	SLArrayIndex_t	RxBitIndex = 7L;							/* Initialise bit count for correct synchronization */
	SLArrayIndex_t	DataArrayOffset = SIGLIB_AI_ZERO;			/* Offset into array */
	SLFixData_t		SamplesPerSymbolCounter = SIGLIB_AI_ZERO;	/* Counts integer portion of samples per symbol */
	SLFixData_t		SamplesPerSymbolOffset = SIGLIB_AI_ZERO;	/* Samples per symbol offset - accounts for the  non integer value */

													/* Allocate memory */
	pData = SUF_VectorArrayAllocate (SAMPLE_LENGTH);
	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");
	}

	TxStringPtr = TxString;
	RxStringPtr = RxString;

	SDA_Clear (pData,								/* Pointer to destination array */
			   SAMPLE_LENGTH);						/* Array length */

#if	DISPLAY_GRAPHICS
	h2DGraph =										/* Initialize graph */
		Create2DGraph ("BPSK With 8KHz Sample Rate",	/* 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
#if	DISPLAY_DEBUG_INFO
	DebugArrayOffset = 0;
	SDA_Clear (DebugArray,							/* Pointer to destination array */
			   SAMPLE_LENGTH);						/* Array length */
#endif
#if	DISPLAY_BIT_PATTERN
	SUF_ClearDebugfprintf ();
	for (i = 0; i < 20; i++)						/* Display Tx bit pattern in debug.log */
	{
		SUF_Debugfprintf ("TxString[%d] ", (int)i);
		dpchar(TxString[i]);
	}
#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++)
	{
		DataArrayOffset = SIGLIB_AI_ZERO;			/* Reset offset into array */

		for (i = 0; i < SYMBOLS_PER_DATA_SET; i += SIGLIB_BYTE_LENGTH)
		{
			for (TxBitIndex = 0; TxBitIndex < SIGLIB_BYTE_LENGTH; TxBitIndex++)
			{
				if (SamplesPerSymbolCounter == 2)	/* Account for non integer number of samples per symbol */
				{
					SamplesPerSymbolOffset = 1L;
					SamplesPerSymbolCounter = SIGLIB_AI_ZERO;
				}
				else
				{
					SamplesPerSymbolOffset = SIGLIB_AI_ZERO;
					SamplesPerSymbolCounter++;
				}
				SDA_BpskModulate ((*TxStringPtr >> TxBitIndex),			/* Modulating bit */
								  pData + DataArrayOffset,				/* Destination array */
								  pCarrierTable,						/* Carrier table pointer */
								  &TxCarrierPhase,						/* Carrier phase pointer */
								  SYMBOL_LENGTH+SamplesPerSymbolOffset,	/* Samples per symbol */
								  CARRIER_FREQ / CARRIER_TABLE_FREQ,	/* Carrier table increment */
								  CARRIER_SINE_TABLE_SIZE);				/* Carrier sine table size */
				DataArrayOffset += SYMBOL_LENGTH+SamplesPerSymbolOffset;
			}
			TxStringPtr++;							/* Increment string pointer */
		}

#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

		SamplesPerSymbolCounter = SIGLIB_AI_ZERO;	/* Reset samples per symbol count for receiver */
		SamplesPerSymbolOffset = SIGLIB_AI_ZERO;	/* Reset samples per symbol offset for receiver */
		DataArrayOffset = SIGLIB_AI_ZERO;			/* Reset offset into array */

		for (i = 0; i < SYMBOLS_PER_DATA_SET; i += SIGLIB_BYTE_LENGTH)
		{
			for (j = 0; j < SIGLIB_BYTE_LENGTH; j++)
			{
				if (SamplesPerSymbolCounter == 2)		/* Account for non integer number of samples per symbol */
				{
					SamplesPerSymbolOffset = 1L;
					SamplesPerSymbolCounter = SIGLIB_AI_ZERO;
				}
				else
				{
					SamplesPerSymbolOffset = SIGLIB_AI_ZERO;
					SamplesPerSymbolCounter++;
				}
#if	DISPLAY_DEBUG_INFO
				DemodulatedBit =
					SDA_BpskDemodulateDebug (pData + DataArrayOffset,				/* 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+SamplesPerSymbolOffset,	/* Samples per symbol */
											 &DebugArray[DebugArrayOffset]);		/* Pointer to filter output data */
				DataArrayOffset += SYMBOL_LENGTH+SamplesPerSymbolOffset;
				DebugArrayOffset += (SYMBOL_LENGTH+SamplesPerSymbolOffset);
#else
				DemodulatedBit =
					SDA_BpskDemodulate (pData + DataArrayOffset,				/* 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+SamplesPerSymbolOffset);	/* Samples per symbol */
				DataArrayOffset += SYMBOL_LENGTH+SamplesPerSymbolOffset;
#endif
				*RxStringPtr |= (((char)DemodulatedBit) << RxBitIndex);
				RxBitIndex++;
				if (RxBitIndex >= SIGLIB_BYTE_LENGTH)
				{
					RxBitIndex = SIGLIB_AI_ZERO;
					*RxStringPtr++;
				}
			}
		}

#if	DISPLAY_DEBUG_INFO
		DebugArrayOffset = 0;
#if	DISPLAY_GRAPHICS
		Display2DGraph (h2DGraph,					/* Graph handle */
					    "Demodulated Data",			/* Title of the dataset */
					    DebugArray,					/* 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 ("\nDemodulated Signal\nPlease hit <Carriage Return> to continue . . ."); getchar ();
#endif
#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+4);

#if	DISPLAY_BIT_PATTERN
	for (i = 0; i < 20; i++)						/* Display Rx bit pattern in debug.log */
	{
		SUF_Debugfprintf ("RxString[%d] ", (int)i);
		dpchar(RxString[i]);
	}
#endif


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

}


