/* 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 #include #include "GraphFunctions.h" #include /* 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 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 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); }