// // OFDM Module for ARDOP // /* Thoughts on OFDM We have lots (?43) carriers, so requiring all to be good is unlikely to succeed If we ack each carrier separately we need some form of block number, so we can infill bits of data that were missed. Always sending first block on first carrier seems a bad idea (but look at redundancy ideas below) We could send the same data (with same block number) on multiple carriers for resilience We need a ack frame with one bit per carrier (? 6 bytes) which is about 1 sec with 50 baud (do we need fec or just crc??). Could send ACK at 100 baud, shortening it a bit, but is overall throughput increace worth it? Using one byte block number but limit to 0 - 127. Window is 10160 in 16QAM or 12700 for 32QAM unless we use a different block length for each mode. Takes 10% of 2FSK throughput. Receiver must be able to hold window of frames (may be a problem with Teensy) Must handle missed ack. ? base next ack on combination of repeats ? Should we wait to pass to host till next seq frame received or all received ok? Should we have multiple frame types for each mod mode, or add a frame type to carrier? Ideally would like 2, 4, 8 PSK, 16, 32QAM. Too many if we stick with ARDOP2 frame structure, so need frame type Frame type has to be sent in fixed mode (not same as data) we need 2 or 3 bits. With 2FSK that is about 60 mS. Need to validate. If we use same type for all carriers (any good reason not to?) we can decode type byte on all frames and compare. Very unlikely to choose wrong one, even if some are different. Could even try more than one ( may be a problem with Teensy). Could use combination of redundancy and mode to give very wide speed (and hopefully resilience) ratio, but gear shifting may be a nightmare. Is reducing carriers and therefore increasing power per carrier better than massive redundacy with lots of carriers? Would dividing single carrier into multiple RS blocks be beneficial? Adds 3 byte overhead per block (Len and CRC) if done with slow carriers would limit window size, so may need different block size per mode, which makes reassembly tricky For a block of 4-5 secs we get 16OFDM 80 bytes/carrier, 3440 bytes per frame, approx 4600 BPS Net 8OFDM 60 bytes/carrier, 2580 bytes per frame, approx 3440 BPS Net 4OFDM 40 bytes/carrier, 1720 bytes per frame, approx 2300 BPS Net 2OFDM 20 bytes/carrier, 860 bytes per frame, approx 1150 BPS Net For Comparison 16QAM.2500.100 120 bytes/carrier, 1200 bytes per frame, approx 2225 BPS Net */ #ifdef WIN32 #define _CRT_SECURE_NO_DEPRECATE #include #else #define SOCKET int #include #define closesocket close #endif #include #include "ARDOPC.h" #pragma warning(disable : 4244) // Code does lots of float to int int OFDMMode; // OFDM can use various modulation modes and redundancy levels int LastSentOFDMMode; // For retries int LastSentOFDMType; // For retries int SavedOFDMMode = -1; // used if we switch to a more robust mode cos we don't have much to send int SavedFrameType; extern UCHAR bytCurrentFrameType; int RXOFDMMode = 0; const char OFDMModes[8][6] = {"PSK2", "PSK4", "PSK8", "QAM16", "PSK16", "QAM32", "PSK4S", "Undef"}; int OFDMFrameLen[8] = {19, 40, 57, 80, 80}; // Bytes per carrier for each submode int OFDMCarriersReceived[8] = {0}; int OFDMCarriersDecoded[8] = {0}; int OFDMCarriersNaked[8] = {0}; int OFDMCarriersAcked[8] = {0}; // Functions to encode data for all OFDM frame types // For the moment will will send all carriers with the same mode (I don't think there is an advantage being different), // So we can use a different block length in each mode. We need to keep record of which blocks within bytDataToSend have // been acked. Note that the first block is always unacked - acked data at the front of buffer is removed. // Although we have an 8 bit sequence, I don't see the need for more than 128 outstanding blocks (carriers). If we miss // just the first block of a 43 frame transmission, next time we send block 1 and 44 - 86. If 1 is still the only one // unacked I will repeat it several times in the next transmission, which will be the repeats plus 87 - 127. // Unfortunately this means bytDataToSend must be at least 128 * max block size (80) = 10240, which is a lot on a Teensy. // Maybe can come upwith better design! UCHAR UnackedOFDMBlocks[128] = {0}; // This is bit list of outstanding blocks. UCHAR UnackedOFDMBlockLen[128] = {0}; // Length of each block. do we need both (ie can we send a block of zero length ??) int NextOFDMBlock = 0; int UnAckedBlockPtr = 0; int CarriersSent; int BytesSent = 0; // Sent but not acked int CarriersACKed; int CarriersNAKed; int lastOFDMRXMode; int LastDemodType = 0; int OFDMLevel; // % "compression" // This is used if we send a partial block. Will normally only happen // at end of transmission, but could due to flow control // // Also used if we want to change mode int DontSendNewData = 0; // Dont send new till all acked int LimitNewData = 0; // Repeat unacked several times till all acked int NeedShiftDown = 0; // Shift down once all sent int Duplicate = 0; // Send data twice int firstNewCarrier = 0; UCHAR SentOFDMBlocks[MAXCAR]; UCHAR SentOFDMBlockLen[MAXCAR]; // Must match actual carrier number UCHAR OFDMBlocks[MAXCAR]; // Build the carrier set in here UCHAR OFDMBlockLen[MAXCAR]; UCHAR goodReceivedBlocks[128]; UCHAR goodReceivedBlockLen[128]; #define MAX_RAW_LENGTH_FSK 43 // 1 + 32 + 8 + 2 #define MAX_RAW_LENGTH 163 // Len Byte + Data + RS + CRC I think! int BytesSenttoHost = 0; extern UCHAR bytData[128 * 80]; extern int bytQDataInProcessLen; extern UCHAR bytSessionID; extern UCHAR bytFrameData[10][MAX_RAW_LENGTH + 10]; // Received chars extern char CarrierOk[MAXCAR]; // RS OK Flags per carrier extern double dblPhaseInc; // in milliradians extern short intNforGoertzel[MAXCAR]; extern short intPSKPhase_1[MAXCAR], intPSKPhase_0[MAXCAR]; extern short intCP[MAXCAR]; // Cyclic prefix offset extern float dblFreqBin[MAXCAR]; extern short intFilteredMixedSamples[]; // Get Frame Type need 2400 and we may add 1200 extern int intFilteredMixedSamplesLength; extern int MaxFilteredMixedSamplesLength; extern short intCarMagThreshold[MAXCAR]; extern short ** intMags; extern short ** intPhases; extern float floatCarFreq; //(was int) // Are these the same ?? extern int intNumCar; extern int intSampPerSym; extern int intDataLen; extern int intRSLen; extern int SymbolsLeft; extern int intPhasesLen; extern int intPhasesLen; extern int intPSKMode; extern int intSymbolsPerByte; extern int PSKInitDone; extern int intLastRcvdFrameQuality; extern int frameLen; extern int intFrameType; extern const char Good[MAXCAR]; extern const char Bad[MAXCAR]; extern int pskStart; extern int charIndex; // Index into received chars extern int RepeatedFrame; // set if this data frame is a repeat extern int intShiftUpDn; extern int dttTimeoutTrip; extern int LastDataFrameType; // Last data frame processed (for Memory ARQ, etc) extern int intNAKctr; extern int intACKctr; extern int intTimeouts; extern UCHAR goodReceivedBlocks[128]; extern UCHAR goodReceivedBlockLen[128]; void GoertzelRealImag(short intRealIn[], int intPtr, int N, float m, float * dblReal, float * dblImag); int ComputeAng1_Ang2(int intAng1, int intAng2); int Demod1CarOFDMChar(int Start, int Carrier, int intNumOfSymbols); VOID Decode1CarPSK(int Carrier, BOOL OFDM); int CorrectRawDataWithRS(UCHAR * bytRawData, UCHAR * bytCorrectedData, int intDataLen, int intRSLen, int bytFrameType, int Carrier); UCHAR GetSym8PSK(int intDataPtr, int k, int intCar, UCHAR * bytEncodedBytes, int intDataBytesPerCar); int Track1CarPSK(int floatCarFreq, int PSKMode, BOOL QAM, BOOL OFDM, float dblUnfilteredPhase, BOOL blnInit); void SendLeaderAndSYNC(UCHAR * bytEncodedBytes, int intLeaderLen); void ARDOPFlush(); BOOL CheckCRC16(unsigned char * Data, int Length); void CorrectPhaseForTuningOffset(short * intPhase, int intPhaseLength, int intPSKMode); BOOL DemodOFDM(); void GenCRC16Normal(char * Data, int Length) { unsigned int CRC = GenCRC16(Data, Length); // Put the two CRC bytes after the stop index Data[Length++] = (CRC >> 8); // MS 8 bits of Register Data[Length] = (CRC & 0xFF); // LS 8 bits of Register } void ClearOFDMVariables() { OFDMMode = PSK4; memset(UnackedOFDMBlocks, 0, sizeof(UnackedOFDMBlocks)); memset(UnackedOFDMBlockLen, 0, sizeof(UnackedOFDMBlockLen)); NextOFDMBlock = 0; BytesSent = 0; SavedOFDMMode = -1; DontSendNewData = LimitNewData = Duplicate = 0; memset(SentOFDMBlocks, 0, sizeof(SentOFDMBlocks)); memset(SentOFDMBlockLen, 0, sizeof(SentOFDMBlockLen)); CarriersACKed = CarriersNAKed = NeedShiftDown = 0; lastOFDMRXMode = LastDemodType = 0; } int GetNextOFDMBlockNumber(int * Len) { BOOL Looping = 0; resend: while (UnAckedBlockPtr >= 0) { if (UnackedOFDMBlocks[UnAckedBlockPtr]) { *Len = UnackedOFDMBlockLen[UnAckedBlockPtr--]; return UnAckedBlockPtr + 1; // We send unacked blocks backwards } UnAckedBlockPtr--; } if (LimitNewData) { Debugprintf("LimitNewData Set - repeating unacked blocks"); UnAckedBlockPtr = 127; // Send unacked again LimitNewData--; goto resend; } if (DontSendNewData && Looping == 0) { Debugprintf("DontSendNewData Set - repeating unacked blocks"); UnAckedBlockPtr = 127; // Send unacked again Looping = 1; // Protect against loop goto resend; } // No unacked blocks, send new NextOFDMBlock++; *Len = -1; return NextOFDMBlock - 1; } UCHAR * GetNextOFDMBlock(int Block, int intDataLen) { return 0; &bytDataToSend[Block * intDataLen]; } void GetOFDMFrameInfo(int OFDMMode, int * intDataLen, int * intRSLen, int * Mode, int * Symbols) { switch (OFDMMode) { case PSK2: *intDataLen = 19; *intRSLen = 6; // Must be even *Symbols = 8; *Mode = 2; break; case PSK4: *intDataLen = 40; *intRSLen = 10; *Symbols = 4; *Mode = 4; break; case PSK8: *intDataLen = 57; // Must be multiple of 3 *intRSLen = 18; // Must be multiple of 3 and even (so multiple of 6) *Symbols = 8; // Actually 8 symbols for 3 bytes *Mode = 8; break; case PSK16: *intDataLen = 80; *intRSLen = 20; *Symbols = 2; *Mode = 16; break; case QAM16: *intDataLen = 80; *intRSLen = 20; *Symbols = 2; *Mode = 8; break; case QAM32: *intDataLen = 100; *intRSLen = 25; *Symbols = 8; // Actually 8 symbols for 3 bytes *Mode = 16; break; case PSK4S: *intDataLen = 12; *intRSLen = 4; *Symbols = 4; *Mode = 4; break; default: *intDataLen = *intRSLen = 0; } } int EncodeOFDMData(UCHAR bytFrameType, UCHAR * bytDataToSend, int Length, unsigned char * bytEncodedBytes) { // Output is a byte array which includes: // 1) A 2 byte Header which include the Frame ID. This will be sent using 4FSK at 50 baud. It will include the Frame ID and ID Xored by the Session bytID. // 2) n sections one for each carrier that will include all data (with FEC appended) for the entire frame. Each block will be identical in length. // Each carrier starts with an 8 bit block number, which may not be sequential (each carrier is ack'ed separately) // and may be repeated (for redundancy) // OFDM Has several modes, selected by OFDMMode not Frame Type (all are OFDM.500 or OFDM.2500 // For the moment will will send all carriers wirh the same mode (I don't think there is an advantage being different), // So we can use a different block length in each mode. We need to keep record of which blocks within bytDataToSend have // been acked. Note that the first block is always unacked - acked data at the front of buffer is removed. int intNumCar, intBaud, intDataLen, intRSLen, bytDataToSendLengthPtr, intEncodedDataPtr; int intCarDataCnt; BOOL blnOdd; char strType[18]; char strMod[16]; BOOL blnFrameTypeOK; UCHAR bytQualThresh; int i, j, Dummy; UCHAR * bytToRS = &bytEncodedBytes[2]; int RepeatIndex = 0; // used to duplicate data if too short to fill frame blnFrameTypeOK = FrameInfo(bytFrameType, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytQualThresh, strType); if (intDataLen == 0 || Length == 0 || !blnFrameTypeOK) return 0; GetOFDMFrameInfo(OFDMMode, &intDataLen, &intRSLen, &Dummy, &Dummy); // Generate the 2 bytes for the frame type data: CarriersSent = intNumCar; bytEncodedBytes[0] = bytFrameType; bytEncodedBytes[1] = bytFrameType ^ bytSessionID; bytDataToSendLengthPtr = 0; firstNewCarrier = -1; intEncodedDataPtr = 2; UnAckedBlockPtr = 127; // We send unacked blocks backwards // Length is data still queued. BytesSent is unacked data Length -= BytesSent; // New data to send if (Length == 0) DontSendNewData = 1; Debugprintf("OFDM Bytes to Send %d DontSendNewData %d", Length, DontSendNewData); // Often the first carrier is the only one missed, and if we repeat it first it will always // fail. So it would be good if logical block number 0 isn't always sent on carrier 0 // The carrier number must match the block number so we can ack it. for (i = 0; i < intNumCar; i++) // across all carriers { int blkLen; int blkNum; intCarDataCnt = Length - bytDataToSendLengthPtr; // If we have no new data to send we would repeat the last sent blocks from // SentOFDMBlocks which is wrong if there is no new data. So in that case just // send outstanding data. if (DontSendNewData && BytesSent) // Just send outstanding data repeatedly if necessary { OFDMBlocks[i] = blkNum = GetNextOFDMBlockNumber(&blkLen); OFDMBlockLen[i] = UnackedOFDMBlockLen[blkNum] = blkLen; UnackedOFDMBlocks[blkNum] = 1; } else if (Duplicate & (i >= ((intNumCar - firstNewCarrier) /2))) goto repeatblocks; else if (intCarDataCnt > intDataLen) // why not > ?? { // Won't all fit tryagain: OFDMBlocks[i] = blkNum = GetNextOFDMBlockNumber(&blkLen); if (blkLen == -1) { // New Block. Make sure it will fit in window int limit; if (intNumCar == 9) limit = 24; // Don't want too many outstanding or shift up will be slow else limit = 125; if (firstNewCarrier == -1) firstNewCarrier = i; if ((NextOFDMBlock + (intNumCar - i)) > limit) { // no room NextOFDMBlock-- ; // we aren't going to send it UnAckedBlockPtr = 127; // send unacked again goto tryagain; } blkLen = intDataLen; bytDataToSendLengthPtr += intDataLen; // Don't reduce bytes to send if repeating BytesSent += intDataLen; } OFDMBlockLen[i] = UnackedOFDMBlockLen[blkNum] = blkLen; UnackedOFDMBlocks[blkNum] = 1; } else { // Last bit memset(&bytToRS[0], 0, intDataLen); bytToRS[0] = intCarDataCnt; // Could be 0 if insuffient data for # of carriers if (intCarDataCnt > 0) { OFDMBlocks[i] = blkNum = GetNextOFDMBlockNumber(&blkLen); if (blkLen == -1) { if (firstNewCarrier == -1) firstNewCarrier = i; blkLen = intCarDataCnt; // Could be 0 if insuffient data for # of carriers bytDataToSendLengthPtr += intCarDataCnt; // Don't reduce bytes to send if repeating BytesSent += intCarDataCnt; if (intCarDataCnt < intDataLen) DontSendNewData = TRUE; // sending a part block so mustnt send more till all acked } UnackedOFDMBlockLen[blkNum] = OFDMBlockLen[i] = blkLen; UnackedOFDMBlocks[blkNum] = 1; } else { // No more data to send - duplicate sent carriers. Gives extra redundancy repeatblocks: blkNum = OFDMBlocks[RepeatIndex]; blkLen = OFDMBlockLen[RepeatIndex++]; OFDMBlocks[i] = blkNum; OFDMBlockLen[i] = blkLen; UnackedOFDMBlockLen[blkNum] = blkLen; UnackedOFDMBlocks[blkNum] = 1; } } } // We now have pointers to the logical blocks in OFDMBlocks/Len. We don't // have to modulate in that order, but must update SentOFDMBlocks with the real // Carrier number j = rand() % intNumCar; for (i = 0; i < intNumCar; i++) { if (j >= intNumCar) j = 0; SentOFDMBlockLen[i] = bytToRS[0] = OFDMBlockLen[j]; SentOFDMBlocks[i] = bytToRS[1] = OFDMBlocks[j++]; Debugprintf("Sending OFDM Carrier %d Block %d Len %d", i,bytToRS[1], bytToRS[0]); memcpy(&bytToRS[2], GetNextOFDMBlock(bytToRS[1], intDataLen), bytToRS[0]); GenCRC16Normal(bytToRS, intDataLen + 2); // calculate the CRC on the byte count + data bytes // Data + RS + 1 byte byteCount + 1 byte blockno + 2 Byte CRC RSEncode(bytToRS, bytToRS+intDataLen+4, intDataLen + 4, intRSLen); // Generate the RS encoding intEncodedDataPtr += intDataLen + 4 + intRSLen; bytToRS += intDataLen + 4 + intRSLen; } return intEncodedDataPtr; } // OFDM RX Routines extern int NErrors; BOOL Decode4FSKOFDMACK() { BOOL FrameOK; BOOL blnRSOK; // 6 Byte payload, 2 CRC 4 RS if (CheckCRC16(&bytFrameData[0][0], 6)) { Debugprintf("OFDMACK Decode OK"); return TRUE; } // Try RS Correction FrameOK = RSDecode(&bytFrameData[0][0], 12, 4, &blnRSOK); if (FrameOK && blnRSOK == FALSE) { // RS Claims to have corrected it, but check Debugprintf("OFDMACK %d Errors Corrected by RS", NErrors); if (CheckCRC16(&bytFrameData[0][0], 6)) { Debugprintf("OFDMACK Corrected by RS OK"); return TRUE; } } Debugprintf("OFDMACK Decode Failed after RS"); return FALSE; } void RemoveProcessedOFDMData() { // ISS has changed toggle, so last ack was processed. // if the last frame wasn't completely decoded then we need to remove any data sent to host and corresponding // entries in goodReceivedBlocks and goodReceivedBlockLen // This allows us to accumulate carriers from repeated frames. This could be good for FEC, but I think it is // of limited value for ARQ. Is it worth it ??? int i, n, Len = 0; for (i = 0; i < 128; i++) { n = goodReceivedBlockLen[i]; if (n) Len += n; else break; // exit loop on first missed block. } // i is number of blocks to remove if (i == 0) return; Debugprintf("Removing %d received OFDM blocks Length %d", i, Len); memmove(goodReceivedBlocks, &goodReceivedBlocks[i], 128 - i); memmove(goodReceivedBlockLen, &goodReceivedBlockLen[i], 128 - i); memset(&goodReceivedBlocks[128 - i], 0, i); memset(&goodReceivedBlockLen[128 - i], 0, i); memmove(bytData, &bytData[Len], sizeof(bytData) - Len); } VOID InitDemodOFDM() { // Called at start of frame int i; float dblPhase, dblReal, dblImag; short modePhase[MAXCAR][3]; int OFDMType[MAXCAR] = {0}; int ModeCount[8] = {0}; int MaxModeCount = 0; char Msg[64]; intSampPerSym = 240; floatCarFreq = 1500.0f + ((intNumCar /2) * 10000.0f) / 180.0f; // Top freq (spacing is 10000/180) for (i= 0; i < intNumCar; i++) { // OFDM uses 55.5555 Hz carrier interval intCP[i] = 24; //CP length intNforGoertzel[i] = 216; dblFreqBin[i] = floatCarFreq / 55.5555f; // Get initial Reference Phase GoertzelRealImag(intFilteredMixedSamples, intCP[i], intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); dblPhase = atan2f(dblImag, dblReal); // Set initial mag from Reference Phase and Mode Bits (which should be full power) intCarMagThreshold[i] = sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); intPSKPhase_1[i] = 1000 * dblPhase; // Get the 3 OFDM mode bits GoertzelRealImag(intFilteredMixedSamples + 240, intCP[i], intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); dblPhase = atan2f(dblImag, dblReal); intPSKPhase_0[i] = 1000 * atan2f(dblImag, dblReal); modePhase[i][0] = -(ComputeAng1_Ang2(intPSKPhase_0[i], intPSKPhase_1[i])); intPSKPhase_1[i] = intPSKPhase_0[i]; intCarMagThreshold[i] += sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); GoertzelRealImag(intFilteredMixedSamples + 480, intCP[i], intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); dblPhase = atan2f(dblImag, dblReal); intPSKPhase_0[i] = 1000 * atan2f(dblImag, dblReal); modePhase[i][1] = -(ComputeAng1_Ang2(intPSKPhase_0[i], intPSKPhase_1[i])); intPSKPhase_1[i] = intPSKPhase_0[i]; intCarMagThreshold[i] += sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); GoertzelRealImag(intFilteredMixedSamples + 720, intCP[i], intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); dblPhase = atan2f(dblImag, dblReal); intPSKPhase_0[i] = 1000 * atan2f(dblImag, dblReal); modePhase[i][2] = -(ComputeAng1_Ang2(intPSKPhase_0[i], intPSKPhase_1[i])); intPSKPhase_1[i] = intPSKPhase_0[i]; intCarMagThreshold[i] += sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); intCarMagThreshold[i] *= 0.75f; // We have accumulated 4 values so divide by 4 intCarMagThreshold[i] /= 4.0f; if (modePhase[i][0] >= 1572 || modePhase[i][0] <= -1572) OFDMType[i] |= 1; if (modePhase[i][1] >= 1572 || modePhase[i][1] <= -1572) OFDMType[i] |= 2; if (modePhase[i][2] >= 1572 || modePhase[i][2] <= -1572) OFDMType[i] |= 4; floatCarFreq -= 55.555664f; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. } // Get RX Mode. May be corrupt on some carriers, so go with majority // But incorrectly seeing a change will cause corruption, so perhaps // need more than simple majority // Or maybe don't actually clear old data until we decode at least // one frame. That way an incorrect frame type won't cause a problem // (as frame won't decode). But what if type is correct and frame still // won't decode ?? // So if almost all types aren't the same, and type is new, discard. for (i = 0; i < intNumCar; i++) ModeCount[OFDMType[i]]++; for (i = 0; i < 8; i++) { if (ModeCount[i] > MaxModeCount) { MaxModeCount = ModeCount[i]; RXOFDMMode = i; } } if (MaxModeCount != intNumCar) Debugprintf("Not all OFDM Types the same (%d)", intNumCar - MaxModeCount); if (RXOFDMMode != lastOFDMRXMode) { // has changed. Only accept if all ok // ?? is this a bit extreme ??. Try 1 error if (MaxModeCount < (intNumCar - 1)) { // Not sure. Safer to assume wrong // if it really has changed decode will fail // and frame repeat RXOFDMMode = lastOFDMRXMode; Debugprintf("New OFDM Mode but more than 1 carrier different (%d) - assume corrupt and don't change", intNumCar - MaxModeCount); } } GetOFDMFrameInfo(RXOFDMMode, &intDataLen, &intRSLen, &intPSKMode, &intSymbolsPerByte); // if OFDM mode (or frame type) has changed clear any received but unprocessed data // If we aren't going to decode because it is a repeat we don't need to // check, as type can't have changed, and new type might be corrupt if (!RepeatedFrame || (memcmp(CarrierOk, Bad, intNumCar) == 0)) { // We are going to decode it, so check if (RXOFDMMode != lastOFDMRXMode || (intFrameType & 0xFE) != (LastDemodType & 0xFE)) { memset(goodReceivedBlocks, 0, sizeof(goodReceivedBlocks)); memset(goodReceivedBlockLen, 0, sizeof(goodReceivedBlockLen)); BytesSenttoHost = 0; lastOFDMRXMode = RXOFDMMode; if ((intFrameType & 0xFE) != (LastDemodType & 0xFE)) Debugprintf("New OFDM Mode - clear any received data"); else Debugprintf("New Frame Type - clear any received data"); } } Track1CarPSK(floatCarFreq, intPSKMode, FALSE, TRUE, dblPhase, TRUE); SymbolsLeft = intDataLen + intRSLen + 4; // Data has length Blockno and CRC dblPhaseInc = 2 * M_PI * 1000 / intPSKMode; intPhasesLen = 0; PSKInitDone = TRUE; Debugprintf("OFDM Mode %s", OFDMModes[RXOFDMMode]); sprintf(Msg, "%s/%s", Name(intFrameType), OFDMModes[RXOFDMMode]); DrawRXFrame(0, Msg); } VOID Decode1CarOFDM(int Carrier) { unsigned int intData; int k; float dblAlpha = 0.1f; // this determins how quickly the rolling average dblTrackingThreshold responds. // dblAlpha value of .1 seems to work well...needs to be tested on fading channel (e.g. Multipath) int Threshold = intCarMagThreshold[Carrier]; int Len = intPhasesLen; UCHAR * Decoded = bytFrameData[0]; // Always use first buffer pskStart = 0; charIndex = 0; // We calculated initial mag from reference symbol // use filtered tracking of refernce phase amplitude // (should be full amplitude value) // On WGN this appears to improve decoding threshold about 1 dB 9/3/2016 while (Len >= 0) { // Phase Samples are in intPhases intData = 0; for (k = 0; k < 2; k++) { intData <<= 4; if (intPhases[Carrier][pskStart] < 393 && intPhases[Carrier][pskStart] > -393) { } // Zero so no need to do anything else if (intPhases[Carrier][pskStart] >= 393 && intPhases[Carrier][pskStart] < 1179) intData += 1; else if (intPhases[Carrier][pskStart] >= 1179 && intPhases[Carrier][pskStart] < 1965) intData += 2; else if (intPhases[Carrier][pskStart] >= 1965 && intPhases[Carrier][pskStart] < 2751) intData += 3; else if (intPhases[Carrier][pskStart] >= 2751 || intPhases[Carrier][pskStart] < -2751) intData += 4; else if (intPhases[Carrier][pskStart] >= -2751 && intPhases[Carrier][pskStart] < -1965) intData += 5; else if (intPhases[Carrier][pskStart] >= -1965 && intPhases[Carrier][pskStart] <= -1179) intData += 6; else intData += 7; if (intMags[Carrier][pskStart] < Threshold) { intData += 8; // add 8 to "inner circle" symbols. Threshold = (Threshold * 900 + intMags[Carrier][pskStart] * 150) / 1000; } else { Threshold = ( Threshold * 900 + intMags[Carrier][pskStart] * 75) / 1000; } intCarMagThreshold[Carrier] = Threshold; pskStart++; } Decoded[charIndex++] = intData; Len -=2; } } BOOL DemodOFDM() { int Used = 0; int Start = 0; int i, n, MemARQOk = 0; int skip = rand() % intNumCar; // We can't wait for the full frame as we don't have enough RAM, so // we do one DMA Buffer at a time, until we run out or end of frame // Only continue if we have enough samples while (State == AcquireFrame) { if (PSKInitDone == 0) // First time through { if (intFilteredMixedSamplesLength < (240 * 4)) // Reference and 3 Mode bits return FALSE; InitDemodOFDM(); intFilteredMixedSamplesLength -= 4 * intSampPerSym; if (intFilteredMixedSamplesLength < 0) Debugprintf("Corrupt intFilteredMixedSamplesLength"); Start += 4 * intSampPerSym; // We normally don't decode Repeated frames. But if all carriers failed to // decode we should if (RepeatedFrame) { if (memcmp(CarrierOk, Bad, intNumCar) == 0) RepeatedFrame = FALSE; } } if (intFilteredMixedSamplesLength < intSymbolsPerByte * intSampPerSym + 10) { // Move any unprocessessed data down buffer // (while checking process - will use cyclic buffer eventually if (intFilteredMixedSamplesLength > 0) memmove(intFilteredMixedSamples, &intFilteredMixedSamples[Start], intFilteredMixedSamplesLength * 2); return FALSE; } // call the decode char routine for each carrier // start at the highest carrier freq which is actually the lowest transmitted carrier due to Reverse sideband mixing floatCarFreq = 1500.0f + ((intNumCar / 2) * 10000.0f) / 180.0f; // spacing is 10000/180 = 55.5555555 for (i = 0; i < intNumCar; i++) { Used = Demod1CarOFDMChar(Start, i, intSymbolsPerByte); // demods 2 phase values - enough for one char intPhasesLen -= intSymbolsPerByte; floatCarFreq -= 55.555664f; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. } intPhasesLen += intSymbolsPerByte; if (RXOFDMMode == PSK8) SymbolsLeft -=3; else SymbolsLeft--; // number still to decode Start += Used; intFilteredMixedSamplesLength -= Used; if (intFilteredMixedSamplesLength < 0) Debugprintf("Corrupt intFilteredMixedSamplesLength"); if (SymbolsLeft <= 0) { // Frame complete - decode it DecodeCompleteTime = Now; // prepare for next so we can exit when we have finished decode DiscardOldSamples(); ClearAllMixedSamples(); State = SearchingForLeader; // Rick uses the last carrier for Quality // Get quality from middle carrier (?? is this best ?? intLastRcvdFrameQuality = UpdatePhaseConstellation(&intPhases[intNumCar/2][0], &intMags[intNumCar/2][0], intPSKMode, FALSE, TRUE); // Decode the phases. Mode was determined from header frameLen = 0; if (RepeatedFrame) { Debugprintf("Repeated frame - discard"); frameLen = BytesSenttoHost; return TRUE; } for (i = 0; i < intNumCar; i++) { UCHAR decodeBuff[256]; // 82 doesnt seem to be enough ??Max length of OFDM block int decodeLen, ofdmBlock;; CarrierOk[i] = 0; // Always reprocess carriers if (RXOFDMMode == QAM16) Decode1CarOFDM(i); else Decode1CarPSK(i, TRUE); // with OFDM each carrier has a sequence number, as we can do selective repeats if a carrier is missed. // so decode into a separate buffer, and copy good data into the correct place in the received data buffer. decodeLen = CorrectRawDataWithRS(&bytFrameData[0][0], decodeBuff , intDataLen + 1, intRSLen, intFrameType, i); // if decode fails try with a tuning offset correction if (CarrierOk[i] == 0) { CorrectPhaseForTuningOffset(&intPhases[i][0], intPhasesLen, intPSKMode); if (RXOFDMMode == QAM16) Decode1CarOFDM(i); else Decode1CarPSK(i, TRUE); decodeLen = CorrectRawDataWithRS(&bytFrameData[0][0], decodeBuff , intDataLen + 1, intRSLen, intFrameType, i); } OFDMCarriersReceived[RXOFDMMode]++; if (CarrierOk[i]) { ofdmBlock = decodeBuff[0]; // CRC check isn't perfect. At least we can check that Block and Length // are reasonable if (ofdmBlock < 128 && decodeLen <= intDataLen) { // copy data to correct place in bytData OFDMCarriersDecoded[RXOFDMMode]++; if (goodReceivedBlocks[ofdmBlock] == 0) { memcpy(&bytData[intDataLen * ofdmBlock], &decodeBuff[1], decodeLen); goodReceivedBlocks[ofdmBlock] = 1; goodReceivedBlockLen[ofdmBlock] = decodeLen; } } } } // Pass any contiguous blocks starting from 0 to host (may need to reconsider!) for (i = 0; i < 128; i++) { n = goodReceivedBlockLen[i]; if (n) frameLen += n; else break; // exit loop on first missed block. } // If this is a repeated frame, we should only send any data that is beyond what we sent at last try BytesSenttoHost = frameLen; } // if all carriers have been decoded we must have passed all data to the host, so clear partial receive info if (memcmp(CarrierOk, Good, intNumCar) == 0) { memset(goodReceivedBlocks, 0, sizeof(goodReceivedBlocks)); memset(goodReceivedBlockLen, 0, sizeof(goodReceivedBlockLen)); BytesSenttoHost = 0; } } return TRUE; } int Demod1CarOFDMChar(int Start, int Carrier, int intNumOfSymbols) { // Converts intSample to an array of differential phase and magnitude values for the Specific Carrier Freq // intPtr should be pointing to the approximate start of the first reference/training symbol (1 of 3) // intPhase() is an array of phase values (in milliradians range of 0 to 6283) for each symbol // intMag() is an array of Magnitude values (not used in PSK decoding but for constellation plotting or QAM decoding) // Objective is to use Minimum Phase Error Tracking to maintain optimum pointer position // It demodulates one byte's worth of samples (2 or 4) float dblReal, dblImag, dblPhase; int intMiliRadPerSample = floatCarFreq * M_PI / 6; int i; int origStart = Start; // int Corrections; // With OFDM we save received data in Receive buffer, so don't keep // the raw frames. So we must always decode if (RepeatedFrame) // We just repeat previous ack/nak, so don't bother to decode { intPhasesLen += intNumOfSymbols; return intSampPerSym * intNumOfSymbols; } for (i = 0; i < intNumOfSymbols; i++) { GoertzelRealImag(intFilteredMixedSamples, Start + intCP[Carrier], intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); intMags[Carrier][intPhasesLen] = sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); dblPhase = atan2f(dblImag, dblReal); intPSKPhase_0[Carrier] = 1000 * dblPhase; intPhases[Carrier][intPhasesLen] = -(ComputeAng1_Ang2(intPSKPhase_0[Carrier], intPSKPhase_1[Carrier])); // Should we track each carrier ?? /* if (Carrier == 0) { Corrections = Track1CarPSK(floatCarFreq, intPSKMode, FALSE, TRUE, dblPhase, FALSE); if (Corrections != 0) { Start += Corrections; GoertzelRealImag(intFilteredMixedSamples, Start + intCP[Carrier], intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); intPSKPhase_0[Carrier] = 1000 * atan2f(dblImag, dblReal); } } */ intPSKPhase_1[Carrier] = intPSKPhase_0[Carrier]; intPhasesLen++; Start += intSampPerSym; } if (AccumulateStats) intOFDMSymbolCnt += intNumOfSymbols; return (Start - origStart); // Symbols we've consumed } VOID EncodeAndSendOFDMACK(UCHAR bytSessionID, int LeaderLength, int Chan) { // OFDMACK has one bit per carrier. As this needs 43 bits meassage is 6 bytes. The spare 5 bits are // used to send quality unsigned long long val = intLastRcvdFrameQuality >> 2; int i; // Not sure if best to use CRC or FEC. Could use both, but message gets a bit long. // Lets go with crc for now // Now try CRC and 4 RS. OTT but see if it reduces number // of failed OFDMACKs bytEncodedBytes[0] = OFDMACK; bytEncodedBytes[1] = OFDMACK ^ bytSessionID; for (i = MAXCAR - 1; i >= 0; i--) { val <<= 1; if (CarrierOk[i]) val |= 1; } for (i = 2; i < 8; i++) { bytEncodedBytes[i] = val & 0xff; val >>= 8; } GenCRC16Normal(&bytEncodedBytes[2], 6); // calculate the CRC RSEncode(&bytEncodedBytes[2], &bytEncodedBytes[10], 8, 4); // Generate the RS encoding ...now 14 bytes total Mod4FSKDataAndPlay(&bytEncodedBytes[0], 14, LeaderLength, Chan); } UCHAR bytLastSym[43]; float dblOFDMCarRatio = 0.5f; void SendOFDM2PSK(int symbol, int intNumCar) { int intCarIndex = (MAXCAR - intNumCar) / 2; int intSample; int OFDMFrame[240] = {0}; // accumulated samples for each carrier short OFDMSamples[240]; // 216 data, 24 CP int i, n, p, q; // start at 24, copy CP later for (i = 0; i < intNumCar; i++) // across all active carriers { p = 24; memset(OFDMSamples, 0, sizeof(OFDMSamples)); for (n = 0; n < 216; n++) { if (symbol) OFDMSamples[p++] -= intOFDMTemplate[intCarIndex][0][n]; else OFDMSamples[p++] += intOFDMTemplate[intCarIndex][0][n]; } // we now have the 216 samples. Copy last 24 to front as CP memcpy(OFDMSamples, &OFDMSamples[216], 24 * 2); // and add into the multicarrier value for (q = 0; q < 240; q++) OFDMFrame[q] += OFDMSamples[q]; // now do the next carrier bytLastSym[intCarIndex] = symbol; intCarIndex++; } // Done all carriers - send sample for (q = 0; q < 240; q++) { intSample = OFDMFrame[q] / intNumCar; ARDOPSampleSink((intSample * OFDMLevel)/100); } } void ModOFDMDataAndPlay(unsigned char * bytEncodedBytes, int Len, int intLeaderLen, int Chan) { int intNumCar, intBaud, intDataLen, intRSLen, intDataPtr, intSampPerSym, intDataBytesPerCar; BOOL blnOdd; int Type = bytEncodedBytes[0]; int intSample; char strType[18] = ""; char strMod[16] = ""; UCHAR bytSym, bytSymToSend, bytMinQualThresh; float dblCarScalingFactor; int intMask = 0; int intLeaderLenMS; int i, j, k, s, n; int intCarStartIndex; int intPeakAmp; int intCarIndex; BOOL QAM = 0; int OFDMFrame[240] = { 0 }; // accumulated samples for each carrier short OFDMSamples[240]; // 216 data, 24 CP int p, q; // start at 24, copy CP later char fType[64]; if (!FrameInfo(Type, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytMinQualThresh, strType)) return; intDataBytesPerCar = (Len - 2) / intNumCar; // We queue the samples here, so dont copy below intCarIndex = intCarStartIndex = (MAXCAR - intNumCar) / 2; switch (OFDMMode) { case PSK2: s = 8; // 8 symbols per byte break; case PSK4: case PSK4S: s = 4; // 4 symbols per byte break; case PSK8: s = 8; // 8 symbols for 3 bytes break; case PSK16: s = 2; // 2 symbols per byte break; case QAM16: s = 2; // 2 symbols per byte QAM = 1; break; default: Debugprintf("Undefined OFDM Mode %d", OFDMMode); return; } intSampPerSym = 216; // 55 baud if (Type == PktFrameData) { intDataBytesPerCar = pktDataLen + pktRSLen + 3; intDataPtr = 11; // Over Header goto PktLoopBack; } Debugprintf("Sending Frame Type %s Mode %s", strType, OFDMModes[OFDMMode]); sprintf(fType, "%s/%s", strType, OFDMModes[OFDMMode]); DrawTXFrame(fType); if (intNumCar == 3) { initFilter(500, 1500, Chan); OFDMLevel = 80; } else if (intNumCar == 9) { initFilter(500, 1500, Chan); OFDMLevel = 100; } else { initFilter(2500, 1500, Chan); OFDMLevel = 125; } if (intLeaderLen == 0) intLeaderLenMS = LeaderLength; else intLeaderLenMS = intLeaderLen; // Create the leader SendLeaderAndSYNC(bytEncodedBytes, intLeaderLen); intPeakAmp = 0; intDataPtr = 2; // initialize pointer to start of data. PktLoopBack: // Reenter here to send rest of variable length packet frame // Now create a reference symbol for each carrier // We have to do each carrier for each sample, as we write // the sample immediately SendOFDM2PSK(0, intNumCar); // Reference symbol is always zero, so same in any mode // Now send OFDM Type as 3 x 2PSK symbols. Same value sent on all carriers. The Type is send as 2PSK // bytLastSym ends up correct bytSym = (OFDMMode) & 1; bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 1); // Values 0=1 SendOFDM2PSK(bytSymToSend, intNumCar); bytSym = (OFDMMode >> 1) & 1; bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 1); // Values 0-1 SendOFDM2PSK(bytSymToSend, intNumCar); bytSym = (OFDMMode >> 2) & 1; bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 1); // Values 0-1 SendOFDM2PSK(bytSymToSend, intNumCar); // Correct bytLastSYM to match PSK level of actual mode for (i = intCarStartIndex; i < intCarStartIndex + intNumCar; i++) { if (OFDMMode == PSK4 || OFDMMode == PSK4S) bytLastSym[i] <<= 1; else if (OFDMMode == PSK8 || OFDMMode == QAM16) bytLastSym[i] <<= 2; if (OFDMMode == PSK16) bytLastSym[i] <<= 3; } // Unlike ARDOP_WIN we send samples as they are created, // so we loop through carriers, then data bytes for (j = 0; j < intDataBytesPerCar; j++) // for each referance and data symbol { // Loop through each symbol of byte (4 for PSK 2 for QAM for (k = 0; k < s; k++) { // with OFDM we must create separate samples for each // carrier, so we can add the cyclic prefix intCarIndex = intCarStartIndex; // initialize the carrrier index for (i = 0; i < intNumCar; i++) // across all active carriers { if (OFDMMode == PSK2) { bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> ((7 - k))) & 1; bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 1); // Values 0-1 } else if (OFDMMode == PSK4 || OFDMMode == PSK4S) { bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> (2 * (3 - k))) & 3; bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 3); // Values 0-3 } else if (OFDMMode == PSK8) { // More complex ...must go through data in 3 byte chunks creating 8 Three bit symbols for each 3 bytes of data. bytSym = GetSym8PSK(intDataPtr, k, i, bytEncodedBytes, intDataBytesPerCar); bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 7); // mod 8 } else if (OFDMMode == PSK16) { bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> (4 * (1 - k))) & 15; bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 15); // Values 0-3 } else { // 16QAM bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> (4 * (1 - k))) & 15; bytSymToSend = ((bytLastSym[intCarIndex] & 7) + (bytSym & 7) & 7); // Compute the differential phase to send bytSymToSend = bytSymToSend | (bytSym & 8); // add in the amplitude bit directly from symbol } p = 24; memset(OFDMSamples, 0, sizeof(OFDMSamples)); for (n = 0; n < intSampPerSym; n++) { if (OFDMMode == PSK2) { if (bytSymToSend) // This uses the symmetry of the symbols to reduce the table size by a factor of 2 OFDMSamples[p++] -= intOFDMTemplate[intCarIndex][0][n]; else OFDMSamples[p++] += intOFDMTemplate[intCarIndex][0][n]; } else if (OFDMMode == PSK4 || OFDMMode == PSK4S) { if (bytSymToSend < 2) // This uses the symmetry of the symbols to reduce the table size by a factor of 2 OFDMSamples[p++] += intOFDMTemplate[intCarIndex][4 * bytSymToSend][n]; // double the symbol value during template lookup for 4PSK. (skips over odd PSK 8 symbols) else OFDMSamples[p++] -= intOFDMTemplate[intCarIndex][4 * (bytSymToSend - 2)][n]; // subtract 2 from the symbol value before doubling and subtract value of table } else if (OFDMMode == PSK16) { if (bytSymToSend < 8) // This uses the symmetry of the symbols to reduce the table size by a factor of 2 OFDMSamples[p++] += intOFDMTemplate[intCarIndex][bytSymToSend][n]; // double the symbol value during template lookup for 4PSK. (skips over odd PSK 8 symbols) else OFDMSamples[p++] -= intOFDMTemplate[intCarIndex][(bytSymToSend - 8)][n]; // subtract 2 from the symbol value before doubling and subtract value of table } else { // This works for both 8PSK and 16QAM as 8PSK does'nt have the ampiltude bit // 4bits/symbol (use table symbol values 0, 1, 2, 3, -0, -1, -2, -3) and modulate amplitude with MSB if (bytSymToSend < 4)// This uses the symmetry of the symbols to reduce the table size by a factor of 2 OFDMSamples[p++] = intOFDMTemplate[intCarIndex][2 * bytSymToSend][n]; // double the symbol value during template lookup for 4PSK. (skips over odd PSK 8 symbols) else if (bytSymToSend < 8) OFDMSamples[p++] = -intOFDMTemplate[intCarIndex][2 * (bytSymToSend - 4)][n]; // subtract 4 from the symbol value before doubling and subtract value of table else if (bytSymToSend < 12) OFDMSamples[p++] = dblOFDMCarRatio * intOFDMTemplate[intCarIndex][2 * (bytSymToSend - 8)][n]; // subtract 4 from the symbol value before doubling and subtract value of table else OFDMSamples[p++] = -dblOFDMCarRatio * intOFDMTemplate[intCarIndex][2 * (bytSymToSend - 12)][n]; // subtract 4 from the symbol value before doubling and subtract value of table } } // we now have the 216 samples. Copy last 24 to front as CP memcpy(OFDMSamples, &OFDMSamples[216], 24 * 2); // and add into the multicarrier value for (q = 0; q < 240; q++) OFDMFrame[q] += OFDMSamples[q]; // now do the next carrier bytLastSym[intCarIndex] = bytSymToSend; intCarIndex++; } // Done all carriers - send sample for (q = 0; q < 240; q++) { intSample = OFDMFrame[q] / intNumCar; ARDOPSampleSink((intSample * OFDMLevel) / 100); OFDMFrame[q] = 0; } } if (OFDMMode == PSK8) { intDataPtr += 3; j += 2; // We've used 3 bytes } else intDataPtr++; } if (Type == PktFrameHeader) { // just sent packet header. Send rest in current mode Type = 0; // Prevent reentry strcpy(strMod, &pktMod[pktMode][0]); intDataBytesPerCar = pktDataLen + pktRSLen + 3; intDataPtr = 11; // Over Header intNumCar = pktCarriers[pktMode]; switch (intNumCar) { case 1: intCarStartIndex = 4; // dblCarScalingFactor = 1.0f; // Starting at 1500 Hz (scaling factors determined emperically to minimize crest factor) TODO: needs verification dblCarScalingFactor = 1.2f; // Starting at 1500 Hz Selected to give < 13% clipped values yielding a PAPR = 1.6 Constellation Quality >98 case 2: intCarStartIndex = 3; // dblCarScalingFactor = 0.53f; if (strcmp(strMod, "16QAM") == 0) dblCarScalingFactor = 0.67f; // Carriers at 1400 and 1600 Selected to give < 2.5% clipped values yielding a PAPR = 2.17, Constellation Quality >92 else dblCarScalingFactor = 0.65f; // Carriers at 1400 and 1600 Selected to give < 4% clipped values yielding a PAPR = 2.0, Constellation Quality >95 break; case 4: intCarStartIndex = 2; // dblCarScalingFactor = 0.29f; // Starting at 1200 Hz dblCarScalingFactor = 0.4f; // Starting at 1200 Hz Selected to give < 3% clipped values yielding a PAPR = 2.26, Constellation Quality >95 break; case 8: intCarStartIndex = 0; // dblCarScalingFactor = 0.17f; // Starting at 800 Hz if (strcmp(strMod, "16QAM") == 0) dblCarScalingFactor = 0.27f; // Starting at 800 Hz Selected to give < 1% clipped values yielding a PAPR = 2.64, Constellation Quality >94 else dblCarScalingFactor = 0.25f; // Starting at 800 Hz Selected to give < 2% clipped values yielding a PAPR = 2.5, Constellation Quality >95 } goto PktLoopBack; // Reenter to send rest of variable length packet frame } ARDOPFlush(); } // Function to compute a 16 bit CRC value and check it against the last 2 bytes of Data (the CRC) unsigned short int compute_crc(unsigned char *buf,int len); BOOL CheckCRC16(unsigned char * Data, int Length) { // returns TRUE if CRC matches, else FALSE // For CRC-16-CCITT = x^16 + x^12 +x^5 + 1 intPoly = 1021 Init FFFF // intSeed is the seed value for the shift register and must be in the range 0-0xFFFF unsigned int CRC = GenCRC16(Data, Length); unsigned short CRC2 = compute_crc(Data, Length); CRC2 ^= 0xffff; // Compare the register with the last two bytes of Data (the CRC) if ((CRC >> 8) == Data[Length]) if ((CRC & 0xFF) == Data[Length + 1]) return TRUE; return FALSE; } // Subroutine to get intDataLen bytes from outbound queue (bytDataToSend)