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

#include <stdio.h>
#include <string.h>
#include <siglib.h>
#include <nhl.h>					/* Numerix host library */
#include "Tx_FIR.h"					/* Transmitter FIR low-pass filter */

#define RRCF_ENABLE						0					/* Root raised cosine filter on Tx and Rx */

					/* Select one of the following three */
#define DISPLAY_TIME_DOMAIN				0					/* Time domain output */
#define DEBUG_LOG_FILE					0					/* Set to '1' to enable logging to debug.log */

#define NUMBER_OF_BURSTS_IN_FILE		11					/* Number of data bursts in source file */


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

#define SAMPLE_RATE						((SLData_t)48000.0) /* Sample rate */
#define SYMBOL_RATE						((SLData_t)1200.0)	/* Symbol rate */
#define CARRIER_TABLE_FREQ				((SLData_t)100.0)	/* Frequency of sine wave in table */
#define CARRIER_FREQ					((SLData_t)1800.0)	/* Frequency of carrier signal - a multiple of the sine table frequency */

#define SYMBOL_LENGTH					((SLArrayIndex_t)(SAMPLE_RATE / SYMBOL_RATE))

#if RRCF_ENABLE
#define RRCF_PERIOD						(SAMPLE_RATE / SYMBOL_RATE) /* RRCF Period */
#define RRCF_ROLL_OFF					0.75				/* Root raised cosine filter roll off factor */
#define RRCF_LENGTH						((3 * SYMBOL_LENGTH) | 0x01)	/* Root raised cosine filter length */
#else
#define RRCF_PERIOD						SIGLIB_ZERO			/* Dummy value - RRCF Period */
#define RRCF_ROLL_OFF					SIGLIB_ZERO			/* Dummy value - Root raised cosine filter roll off factor */
#define RRCF_LENGTH						1					/* Dummy value - Root raised cosine filter length */
#endif

//#define GAUS_NOISE_VARIANCE				4.0					/* Injected noise parameters */
//#define GAUS_NOISE_OFFSET				SIGLIB_ZERO
#define GAUS_NOISE_VARIANCE				0.01					/* Injected noise parameters */
#define GAUS_NOISE_OFFSET				SIGLIB_ZERO


			/* Derived application definitions */
#define SYMBOL_LENGTH					((SLArrayIndex_t)(SAMPLE_RATE / SYMBOL_RATE))			/* Number of samples per symbol */
#define SYMBOLS_PER_LOOP				((SLArrayIndex_t)(SAMPLE_LENGTH / SYMBOL_LENGTH))		/* Number of symbols per loop for graph */

#define CARRIER_SINE_TABLE_SIZE			((SLArrayIndex_t)(SAMPLE_RATE / CARRIER_TABLE_FREQ))	/* Number of samples in each of cos and sine table */
#define CARRIER_TABLE_INCREMENT			((SLArrayIndex_t)(CARRIER_FREQ / CARRIER_TABLE_FREQ))	/* Carrier frequency */

SLData_t		*pCarrierTable;								/* Overlapped cosine + sine look-up table */

															/* Modem parameters and variables */
SLData_t		TxCarrierPhase;
SLArrayIndex_t	TxPreviousOutputSymbol;

							/* RRCF arrays */
SLData_t		TxIFilterState[RRCF_LENGTH];
SLData_t		TxQFilterState[RRCF_LENGTH];
SLData_t		RRCFCoeffs[RRCF_LENGTH];							/* Raised cosine filter coefficients */
SLArrayIndex_t	TxIFilterIndex;
SLArrayIndex_t	TxQFilterIndex;

SLArrayIndex_t	TxSampleClock;


SLData_t		OutputArray[SAMPLE_LENGTH];

SLData_t		GaussianNoisePhase, GaussianNoiseValue;		/* Variables for injecting noise */

void inject_noise (FILE *);

void main (void)

{
	FILE			*fpi, *fpo;
	SLArrayIndex_t	TxDiBit;
	int				InputChar;
#if DISPLAY_TIME_DOMAIN
	GraphObject		*h2DGraph;						/* Declare graph object */
#endif


	pCarrierTable = SUF_QPSKCarrierArrayAllocate (CARRIER_SINE_TABLE_SIZE); /* Allocate arrays */

													/* Initialize FIR low-pass filter */
	SIF_Fir (TxPreFilterState,						/* Pointer to filter state array */
			 &TxPreFilterIndex,						/* Pointer to filter index register */
			 TX_PRE_FILTER_LENGTH);					/* Filter length */

#if DISPLAY_TIME_DOMAIN
	h2DGraph =										/* Initialize graph */
		Create2DGraph ("Pi/4 DQPSK Modulator",		/* 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 ("Graph creation failure. Please check that the server is running\n");
		exit (1);
	}
#endif
#if DEBUG_LOG_FILE
SUF_ClearDebugfprintf();
#endif

													/* Initialise QPSK functions */
	SIF_PiByFourDQpskModulate (pCarrierTable,						/* Carrier table pointer */
							   CARRIER_TABLE_FREQ / SAMPLE_RATE,	/* Carrier frequency */
							   CARRIER_SINE_TABLE_SIZE,				/* Carrier sine table size */
							   &TxCarrierPhase,						/* Carrier phase pointer */
							   &TxSampleClock,						/* Sample clock pointer */
							   TxIFilterState,						/* RRCF Tx I delay pointer */
							   &TxIFilterIndex,						/* RRCF Tx I Filter Index pointer */
							   TxQFilterState,						/* RRCF Tx Q delay pointer */
							   &TxQFilterIndex,						/* RRCF Tx Q Filter Index pointer */
							   RRCFCoeffs,							/* RRCF Coefficients pointer */
							   RRCF_PERIOD,							/* RRCF Period */
							   RRCF_ROLL_OFF,						/* RRCF Roll off */
							   RRCF_LENGTH,							/* RRCF size */
							   RRCF_ENABLE,							/* RRCF enable / disable switch */
							   &TxPreviousOutputSymbol);			/* Pointer to previous output symbol for differential coding */


	fpi = fopen ("base.txt", "r");
	if (fpi == NULL)
	{
		printf ("Can not open input file\n");
		exit (0);
	}

	fpo = fopen ("base.sig", "w");
	if (fpo == NULL)
	{
		printf ("Can not open output file\n");
		exit (0);
	}

	GaussianNoisePhase = SIGLIB_ZERO;

	inject_noise (fpo);

	while ((InputChar = getc (fpi)) != EOF)			/* Get first bit */
	{
		if (InputChar == '\n')
		{
			inject_noise (fpo);
		}

		if (InputChar == '1')	TxDiBit = 0x2;		/* Convert di-bit pair from input file */
		else					TxDiBit = 0x0;
		
		if ((InputChar = getc (fpi)) == EOF)		/* Get second bit */
		{
			inject_noise (fpo);

			fclose (fpi);
			fclose (fpo);
			exit (0);
		}

		if (InputChar == '\n')
		{
			inject_noise (fpo);
		}

		if (InputChar == '1')	TxDiBit += 0x1;		/* Convert di-bit pair from input file */

		SDA_PiByFourDQpskModulate (TxDiBit,						/* Source data di-bit */
								   OutputArray,					/* Destination array */
								   pCarrierTable,				/* Carrier table pointer */
								   CARRIER_SINE_TABLE_SIZE,		/* Carrier sine table size */
								   &TxCarrierPhase,				/* Carrier phase pointer */
								   &TxSampleClock,				/* Sample clock pointer */
								   CARRIER_TABLE_INCREMENT,		/* Carrier table increment */
								   SYMBOL_LENGTH,				/* Samples per symbol */
								   TxIFilterState,				/* RRCF Tx I delay pointer */
								   &TxIFilterIndex,				/* RRCF Tx I Filter Index pointer */
								   TxQFilterState,				/* RRCF Tx Q delay pointer */
								   &TxQFilterIndex,				/* RRCF Tx Q Filter Index pointer */
								   RRCFCoeffs,					/* RRCF Coefficients pointer */
								   RRCF_LENGTH,					/* RRCF size */
								   RRCF_ENABLE,					/* RRCF enable / disable switch */
								   &TxPreviousOutputSymbol);	/* Pointer to previous output symbol for differential coding */

													/* Set the output signal magnitude */
		SDA_Multiply (OutputArray,					/* Pointer to source array */
					  100.0,						/* Multiplier */
					  OutputArray,					/* Pointer to destination array */
					  SYMBOL_LENGTH);				/* Array length */

		SDA_Fir (OutputArray,						/* Input array to be filtered */
				 OutputArray,						/* Filtered output array */
				 TxPreFilterState,					/* Pointer to filter state array */
				 TxPreFilterCoeffs,					/* Pointer to filter coefficients */
				 &TxPreFilterIndex,					/* Pointer to filter index register */
				 TX_PRE_FILTER_LENGTH,				/* Filter length */
				 SYMBOL_LENGTH);					/* Array length */

		buffer_to_disk (OutputArray, fpo, SYMBOL_LENGTH);

#if DISPLAY_TIME_DOMAIN
		Display2DGraph (h2DGraph,					/* Graph handle */
						"Output data",				/* Title of the dataset */
						OutputArray,				/* Array of Double dataset */
						SYMBOL_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 ("Please hit <Carriage Return> to continue . . ."); getchar ();
#endif
	}

	inject_noise (fpo);								/* Inject noise into the output, if required */

	SUF_MemoryFree (pCarrierTable);					/* Free memory */

	fclose (fpi);									/* Close files */
	fclose (fpo);
}


void inject_noise (FILE *fpo)

{
	int j;

	for (j = 0; j < 15; j++)
	{
													/* Pre fill with noise */
		SDA_SignalGenerate (OutputArray,			/* Pointer to destination array */
							SIGLIB_GAUSSIAN_NOISE,	/* Signal type */
							SIGLIB_ZERO,			/* Peak value of signal */
							SIGLIB_FILL,			/* Buffer fill mode, fill up or add to */
							SIGLIB_ZERO,			/* Signal frequency */
							GAUS_NOISE_OFFSET,		/* Signal offset */
							GAUS_NOISE_VARIANCE,	/* Param, different for each signal type */
							SIGLIB_ZERO,			/* End value */
							&GaussianNoisePhase,	/* Phase offset */
							&GaussianNoiseValue,	/* Current value */
							SAMPLE_LENGTH);			/* Array length */

#if DISPLAY_TIME_DOMAIN
		Display2DGraph (h2DGraph,					/* Graph handle */
						"Output data",				/* Title of the dataset */
						OutputArray,				/* 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 ("Please hit <Carriage Return> to continue . . ."); getchar ();
#endif
		SDA_Fir (OutputArray,						/* Input array to be filtered */
				 OutputArray,						/* Filtered output array */
				 TxPreFilterState,					/* Pointer to filter state array */
				 TxPreFilterCoeffs,					/* Pointer to filter coefficients */
				 &TxPreFilterIndex,					/* Pointer to filter index register */
				 TX_PRE_FILTER_LENGTH,				/* Filter length */
				 SYMBOL_LENGTH);					/* Array length */
		buffer_to_disk (OutputArray, fpo, SAMPLE_LENGTH);
	}

		SDA_SignalGenerate (OutputArray,			/* Pointer to destination array */
							SIGLIB_GAUSSIAN_NOISE,	/* Signal type */
							SIGLIB_ZERO,			/* Peak value of signal */
							SIGLIB_FILL,			/* Buffer fill mode, fill up or add to */
							SIGLIB_ZERO,			/* Signal frequency */
							GAUS_NOISE_OFFSET,		/* Signal offset */
							GAUS_NOISE_VARIANCE,	/* Param, different for each signal type */
							SIGLIB_ZERO,			/* End value */
							&GaussianNoisePhase,	/* Phase offset */
							&GaussianNoiseValue,	/* Current value */
							SAMPLE_LENGTH);			/* Array length */

#if DISPLAY_TIME_DOMAIN
		Display2DGraph (h2DGraph,					/* Graph handle */
						"Output data",				/* Title of the dataset */
						OutputArray,				/* Array of Double dataset */
						100,						/* 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

		SDA_Fir (OutputArray,						/* Input array to be filtered */
				 OutputArray,						/* Filtered output array */
				 TxPreFilterState,					/* Pointer to filter state array */
				 TxPreFilterCoeffs,					/* Pointer to filter coefficients */
				 &TxPreFilterIndex,					/* Pointer to filter index register */
				 TX_PRE_FILTER_LENGTH,				/* Filter length */
				 SYMBOL_LENGTH);					/* Array length */

		buffer_to_disk (OutputArray, fpo, 100L);
}


