1557 lines
47 KiB
C
1557 lines
47 KiB
C
//
|
|
// 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 <windows.h>
|
|
#else
|
|
#define SOCKET int
|
|
#include <unistd.h>
|
|
#define closesocket close
|
|
#endif
|
|
|
|
#include <math.h>
|
|
|
|
#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;
|
|
void ARDOPSampleSink(short Sample);
|
|
|
|
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)
|
|
|
|
|
|
|