1880 lines
46 KiB
C
1880 lines
46 KiB
C
|
#ifdef WIN32
|
||
|
#define _CRT_SECURE_NO_DEPRECATE
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include <mmsystem.h>
|
||
|
#pragma comment(lib, "winmm.lib")
|
||
|
#else
|
||
|
#define SOCKET int
|
||
|
#include <unistd.h>
|
||
|
#define closesocket close
|
||
|
#endif
|
||
|
|
||
|
//#include "Version.h"
|
||
|
|
||
|
#include "ARDOPC.h"
|
||
|
//#include "getopt.h"
|
||
|
|
||
|
void CompressCallsign(char * Callsign, UCHAR * Compressed);
|
||
|
void CompressGridSquare(char * Square, UCHAR * Compressed);
|
||
|
void ASCIIto6Bit(char * Padded, UCHAR * Compressed);
|
||
|
void GetTwoToneLeaderWithSync(int intSymLen);
|
||
|
void SendID(BOOL blnEnableCWID);
|
||
|
void PollReceivedSamples();
|
||
|
void CheckTimers();
|
||
|
BOOL GetNextARQFrame();
|
||
|
BOOL TCPHostInit();
|
||
|
BOOL SerialHostInit();
|
||
|
BOOL KISSInit();
|
||
|
void SerialHostPoll();
|
||
|
void TCPHostPoll();
|
||
|
BOOL MainPoll();
|
||
|
VOID PacketStartTX();
|
||
|
void PlatformSleep(int mS);
|
||
|
BOOL BusyDetect2(float * dblMag, int intStart, int intStop);
|
||
|
BOOL IsPingToMe(char * strCallsign);
|
||
|
void LookforPacket(float * dblMag, float dblMagAvg, int count, float * real, float * imag);
|
||
|
void PktARDOPStartTX();
|
||
|
|
||
|
// Config parameters
|
||
|
|
||
|
char GridSquare[9] = "No GS ";
|
||
|
char Callsign[10] = "";
|
||
|
BOOL wantCWID = FALSE;
|
||
|
BOOL NeedID = FALSE; // SENDID Command Flag
|
||
|
BOOL NeedCWID = FALSE;
|
||
|
BOOL NeedConReq = FALSE; // ARQCALL Command Flag
|
||
|
BOOL NeedPing = FALSE; // PING Command Flag
|
||
|
BOOL NeedCQ = FALSE; // PING Command Flag
|
||
|
BOOL NeedTwoToneTest = FALSE;
|
||
|
BOOL UseKISS = TRUE; // Enable Packet (KISS) interface
|
||
|
int PingCount;
|
||
|
int CQCount;
|
||
|
|
||
|
|
||
|
BOOL blnPINGrepeating = False;
|
||
|
BOOL blnFramePending = False; // Cancels last repeat
|
||
|
int intPINGRepeats = 0;
|
||
|
|
||
|
char ConnectToCall[16] = "";
|
||
|
|
||
|
|
||
|
#ifdef TEENSY
|
||
|
int LeaderLength = 500;
|
||
|
#else
|
||
|
int LeaderLength = 300;
|
||
|
#endif
|
||
|
int TrailerLength = 0;
|
||
|
unsigned int ARQTimeout = 120;
|
||
|
int TuningRange = 100;
|
||
|
int TXLevel = 300; // 300 mV p-p Used on Teensy
|
||
|
//int RXLevel = 0; // Configured Level - zero means auto tune
|
||
|
int autoRXLevel = 1500; // calculated level
|
||
|
int ARQConReqRepeats = 5;
|
||
|
BOOL DebugLog = TRUE;
|
||
|
BOOL CommandTrace = TRUE;
|
||
|
int DriveLevel = 100;
|
||
|
char strFECMode[16] = "OFDM.500.55";
|
||
|
int FECRepeats = 0;
|
||
|
BOOL FECId = FALSE;
|
||
|
int Squelch = 5;
|
||
|
|
||
|
enum _ARQBandwidth ARQBandwidth = XB500;
|
||
|
BOOL NegotiateBW = TRUE;
|
||
|
char HostPort[80] = "";
|
||
|
int port = 8515;
|
||
|
int pktport = 0;
|
||
|
BOOL RadioControl = FALSE;
|
||
|
BOOL SlowCPU = FALSE;
|
||
|
BOOL AccumulateStats = TRUE;
|
||
|
BOOL Use600Modes = FALSE;
|
||
|
BOOL EnableOFDM = TRUE;
|
||
|
BOOL UseOFDM = TRUE;
|
||
|
BOOL FSKOnly = FALSE;
|
||
|
BOOL fastStart = TRUE;
|
||
|
BOOL ConsoleLogLevel = LOGDEBUG;
|
||
|
BOOL FileLogLevel = LOGDEBUG;
|
||
|
BOOL EnablePingAck = TRUE;
|
||
|
|
||
|
|
||
|
// Stats
|
||
|
|
||
|
// Public Structure QualityStats
|
||
|
|
||
|
int SessBytesSent;
|
||
|
int SessBytesReceived;
|
||
|
int int4FSKQuality;
|
||
|
int int4FSKQualityCnts;
|
||
|
int int8FSKQuality;
|
||
|
int int8FSKQualityCnts;
|
||
|
int int16FSKQuality;
|
||
|
int int16FSKQualityCnts;
|
||
|
int intFSKSymbolsDecoded;
|
||
|
int intPSKQuality[2];
|
||
|
int intPSKQualityCnts[2];
|
||
|
int intOFDMQuality[8];
|
||
|
int intOFDMQualityCnts[8];
|
||
|
int intPSKSymbolsDecoded;
|
||
|
int intOFDMSymbolsDecoded;
|
||
|
|
||
|
int intQAMQuality;
|
||
|
int intQAMQualityCnts;
|
||
|
int intQAMSymbolsDecoded;
|
||
|
int intGoodQAMSummationDecodes;
|
||
|
|
||
|
|
||
|
int intLeaderDetects;
|
||
|
int intLeaderSyncs;
|
||
|
int intAccumLeaderTracking;
|
||
|
float dblFSKTuningSNAvg;
|
||
|
int intGoodFSKFrameTypes;
|
||
|
int intFailedFSKFrameTypes;
|
||
|
int intAccumFSKTracking;
|
||
|
int intFSKSymbolCnt;
|
||
|
int intGoodFSKFrameDataDecodes;
|
||
|
int intFailedFSKFrameDataDecodes;
|
||
|
int intAvgFSKQuality;
|
||
|
int intFrameSyncs;
|
||
|
int intGoodPSKSummationDecodes;
|
||
|
int intGoodFSKSummationDecodes;
|
||
|
int intGoodQAMSummationDecodes;
|
||
|
int intGoodOFDMSummationDecodes;
|
||
|
float dblLeaderSNAvg;
|
||
|
int intAccumPSKLeaderTracking;
|
||
|
float dblAvgPSKRefErr;
|
||
|
int intPSKTrackAttempts;
|
||
|
int intAccumPSKTracking;
|
||
|
int intQAMTrackAttempts;
|
||
|
int intOFDMTrackAttempts;
|
||
|
int intAccumQAMTracking;
|
||
|
int intAccumOFDMTracking;
|
||
|
int intPSKSymbolCnt;
|
||
|
int intQAMSymbolCnt;
|
||
|
int intOFDMSymbolCnt;
|
||
|
int intGoodPSKFrameDataDecodes;
|
||
|
int intFailedPSKFrameDataDecodes;
|
||
|
int intGoodQAMFrameDataDecodes;
|
||
|
int intFailedQAMFrameDataDecodes;
|
||
|
int intAvgPSKQuality;
|
||
|
int intGoodOFDMFrameDataDecodes;
|
||
|
int intFailedOFDMFrameDataDecodes;
|
||
|
int intAvgOFDMQuality;
|
||
|
float dblAvgDecodeDistance;
|
||
|
int intDecodeDistanceCount;
|
||
|
int intShiftUPs;
|
||
|
int intShiftDNs;
|
||
|
unsigned int dttStartSession;
|
||
|
int intLinkTurnovers;
|
||
|
int intEnvelopeCors;
|
||
|
float dblAvgCorMaxToMaxProduct;
|
||
|
int intConReqSN;
|
||
|
int intConReqQuality;
|
||
|
int intTimeouts;
|
||
|
|
||
|
|
||
|
|
||
|
char stcLastPingstrSender[10];
|
||
|
char stcLastPingstrTarget[10];
|
||
|
int stcLastPingintRcvdSN;
|
||
|
int stcLastPingintQuality;
|
||
|
time_t stcLastPingdttTimeReceived;
|
||
|
|
||
|
BOOL blnInitializing = FALSE;
|
||
|
|
||
|
BOOL blnLastPTT = FALSE;
|
||
|
|
||
|
BOOL PlayComplete = FALSE;
|
||
|
|
||
|
BOOL blnBusyStatus = 0;
|
||
|
BOOL newStatus;
|
||
|
|
||
|
unsigned int tmrSendTimeout;
|
||
|
|
||
|
int intCalcLeader; // the computed leader to use based on the reported Leader Length
|
||
|
int intRmtLeaderMeasure = 0;
|
||
|
|
||
|
int dttCodecStarted;
|
||
|
|
||
|
enum _ReceiveState State;
|
||
|
enum _ARDOPState ProtocolState;
|
||
|
|
||
|
const char ARDOPStates[8][9] = {"OFFLINE", "DISC", "ISS", "IRS", "IDLE", "IRStoISS", "FECSEND", "FECRCV"};
|
||
|
|
||
|
const char ARDOPModes[3][6] = {"Undef", "FEC", "ARQ"};
|
||
|
|
||
|
struct SEM Semaphore = {0, 0, 0, 0};
|
||
|
|
||
|
int DecodeCompleteTime;
|
||
|
|
||
|
BOOL blnAbort = FALSE;
|
||
|
int intRepeatCount;
|
||
|
BOOL blnARQDisconnect = FALSE;
|
||
|
|
||
|
int dttLastPINGSent;
|
||
|
|
||
|
enum _ProtocolMode ProtocolMode = FEC;
|
||
|
|
||
|
extern int intTimeouts;
|
||
|
extern BOOL blnEnbARQRpt;
|
||
|
extern BOOL blnDISCRepeating;
|
||
|
extern char strRemoteCallsign[10];
|
||
|
extern char strLocalCallsign[10];
|
||
|
extern char strFinalIDCallsign[10];
|
||
|
extern int dttTimeoutTrip;
|
||
|
extern unsigned int dttLastFECIDSent;
|
||
|
extern BOOL blnPending;
|
||
|
extern unsigned int tmrIRSPendingTimeout;
|
||
|
extern unsigned int tmrFinalID;
|
||
|
extern unsigned int tmrPollOBQueue;
|
||
|
VOID EncodeAndSend4FSKControl(UCHAR bytFrameType, UCHAR bytSessionID, int LeaderLength);
|
||
|
void SendPING(char * strMycall, char * strTargetCall, int intRpt);
|
||
|
void SendCQ(int intRpt);
|
||
|
|
||
|
int intRepeatCnt;
|
||
|
|
||
|
|
||
|
BOOL blnClosing = FALSE;
|
||
|
BOOL blnCodecStarted = FALSE;
|
||
|
|
||
|
unsigned int dttNextPlay = 0;
|
||
|
|
||
|
|
||
|
const UCHAR bytValidFrameTypesALL[]=
|
||
|
{
|
||
|
DataNAK,
|
||
|
DataNAKLoQ,
|
||
|
ConRejBusy,
|
||
|
ConRejBW,
|
||
|
ConAck,
|
||
|
DISCFRAME,
|
||
|
BREAK,
|
||
|
END,
|
||
|
IDLEFRAME,
|
||
|
ConReq200,
|
||
|
ConReq500,
|
||
|
ConReq2500,
|
||
|
OConReq500,
|
||
|
OConReq2500,
|
||
|
IDFRAME,
|
||
|
PINGACK,
|
||
|
PING,
|
||
|
CQ_de,
|
||
|
D4PSK_200_50_E,
|
||
|
D4PSK_200_50_O,
|
||
|
D4PSK_200_100_E,
|
||
|
D4PSK_200_100_O,
|
||
|
D16QAM_200_100_E,
|
||
|
D16QAM_200_100_O,
|
||
|
D4FSK_500_50_E,
|
||
|
D4FSK_500_50_O,
|
||
|
D4PSK_500_50_E,
|
||
|
D4PSK_500_50_O,
|
||
|
D4PSK_500_100_E,
|
||
|
D4PSK_500_100_O,
|
||
|
D16QAMR_500_100_E,
|
||
|
D16QAMR_500_100_O,
|
||
|
D16QAM_500_100_E,
|
||
|
D16QAM_500_100_O,
|
||
|
DOFDM_200_55_E,
|
||
|
DOFDM_200_55_O,
|
||
|
DOFDM_500_55_E,
|
||
|
DOFDM_500_55_O,
|
||
|
D4FSK_1000_50_E,
|
||
|
D4FSK_1000_50_O,
|
||
|
D4PSKR_2500_50_E,
|
||
|
D4PSKR_2500_50_O,
|
||
|
D4PSK_2500_50_E,
|
||
|
D4PSK_2500_50_O,
|
||
|
D4PSK_2500_100_E,
|
||
|
D4PSK_2500_100_O,
|
||
|
D16QAMR_2500_100_E,
|
||
|
D16QAMR_2500_100_O,
|
||
|
D16QAM_2500_100_E,
|
||
|
D16QAM_2500_100_O,
|
||
|
DOFDM_2500_55_E,
|
||
|
DOFDM_2500_55_O,
|
||
|
|
||
|
PktFrameHeader, // Variable length frame Header
|
||
|
PktFrameData, // Variable length frame Data (Virtual Frsme Type)
|
||
|
OFDMACK,
|
||
|
DataACK,
|
||
|
DataACKHiQ};
|
||
|
|
||
|
const UCHAR bytValidFrameTypesISS[]= // ACKs, NAKs, END, DISC, BREAK
|
||
|
{
|
||
|
DataNAK,
|
||
|
DataNAKLoQ,
|
||
|
ConRejBusy,
|
||
|
ConRejBW,
|
||
|
ConAck,
|
||
|
DISCFRAME,
|
||
|
END,
|
||
|
IDFRAME,
|
||
|
PktFrameHeader, // Variable length frame Header
|
||
|
PktFrameData, // Variable length frame Data (Virtual Frsme Type)
|
||
|
OFDMACK,
|
||
|
DataACK,
|
||
|
DataACKHiQ};
|
||
|
|
||
|
const UCHAR * bytValidFrameTypes;
|
||
|
|
||
|
int bytValidFrameTypesLengthISS = sizeof(bytValidFrameTypesISS);
|
||
|
int bytValidFrameTypesLengthALL = sizeof(bytValidFrameTypesALL);
|
||
|
int bytValidFrameTypesLength;
|
||
|
|
||
|
|
||
|
BOOL blnTimeoutTriggered = FALSE;
|
||
|
|
||
|
// We can't keep the audio samples for retry, but we can keep the
|
||
|
// encoded data
|
||
|
|
||
|
unsigned char bytEncodedBytes[4500] =""; // OFDM is big (maybe not 4500)
|
||
|
int EncLen;
|
||
|
|
||
|
|
||
|
extern UCHAR bytSessionID;
|
||
|
|
||
|
int intLastRcvdFrameQuality;
|
||
|
|
||
|
int intAmp = 26000; // Selected to have some margin in calculations with 16 bit values (< 32767) this must apply to all filters as well.
|
||
|
|
||
|
const char strAllDataModes[18][16] =
|
||
|
{"4PSK.200.50", "4PSK.200.100",
|
||
|
"16QAM.200.100", "4FSK.500.50",
|
||
|
"4PSK.500.50", "4PSK.500.100",
|
||
|
"OFDM.200.55", "OFDM.500.55",
|
||
|
"16QAMR.500.100", "16QAM.500.100",
|
||
|
"4FSK.1000.50",
|
||
|
"4PSKR.2500.50", "4PSK.2500.50",
|
||
|
"4PSK.2500.100",
|
||
|
"16QAMR.2500.100", "16QAM.2500.100", "OFDM.2500.55"};
|
||
|
|
||
|
int strAllDataModesLen = 18;
|
||
|
|
||
|
// Frame Speed By Type (from Rick's spreadsheet) Bytes per minute
|
||
|
|
||
|
const short Rate[64] =
|
||
|
{
|
||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00 - 0F
|
||
|
402,402,826,826,1674,1674,0,0,0,0,402,402,857,857,1674,1674, // 10 - 1F
|
||
|
1674,1674,3349,3359,0,0,0,0,857,857,2143,2143,4286,4286,8372,8372, // 20 - 2F
|
||
|
8372,8372,16744,16744,0,0,0,0,0,0,0,0,0,0,0,0, // 30 - 3F
|
||
|
};
|
||
|
|
||
|
const short FrameSize[64] =
|
||
|
{
|
||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00 - 0F
|
||
|
32,32,64,64,120,120,0,0,0,0,32,32,64,64,128,128, // 10 - 1F
|
||
|
120,120,240,240,360,360,720,720,64,64,160,160,320,320,640,640, // 20 - 2F
|
||
|
600,600,1200,1200,680,680,1360,1360,0,0,0,0,0,0,0,0, // 30 - 3F
|
||
|
};
|
||
|
|
||
|
|
||
|
const char strFrameType[64][18] =
|
||
|
{
|
||
|
// Short Control Frames 1 Car, 500Hz,4FSK, 50 baud
|
||
|
|
||
|
"DataNAK", // 0
|
||
|
"DataNAKLoQ",
|
||
|
"ConRejBusy",
|
||
|
"ConRejBW",
|
||
|
"ConAck", // 4
|
||
|
"DISC",
|
||
|
"BREAK",
|
||
|
"END",
|
||
|
"IDLE", // 8
|
||
|
"ConReq200",
|
||
|
"ConReq500",
|
||
|
"ConReq2500",
|
||
|
"IDFrame", // C
|
||
|
"PingAck",
|
||
|
"Ping", // E
|
||
|
"CQ_de", // F
|
||
|
|
||
|
// 200 Hz Bandwidth
|
||
|
// 1 Car modes
|
||
|
|
||
|
"4PSK.200.50.E", // 0x10
|
||
|
"4PSK.200.50.O",
|
||
|
"4PSK.200.100.E",
|
||
|
"4PSK.200.100.O",
|
||
|
"16QAM.200.100.E", // 0x14
|
||
|
"16QAM.200.100.O",
|
||
|
"", "",// 0x16 to 0x17
|
||
|
"OConReq500", "OConReq2500",
|
||
|
// 500 Hz bandwidth Data
|
||
|
// 1 Car 4FSK Data mode 500 Hz, 50 baud tones spaced @ 100 Hz
|
||
|
|
||
|
"4FSK.500.50.E", // 0x1A
|
||
|
"4FSK.500.50.O",
|
||
|
// 2 Car PSK Data Modes
|
||
|
"4PSK.500.50.E",
|
||
|
"4PSK.500.50.O",
|
||
|
"4PSK.500.100.E", // 0x1E
|
||
|
"4PSK.500.100.O",
|
||
|
// 2 Car 16QAM Data Modes 100 baud
|
||
|
"16QAMR.500.100.E", // 0x20
|
||
|
"16QAMR.500.100.O",
|
||
|
// 2 Car 16QAM Data Modes 100 baud
|
||
|
"16QAM.500.100.E", // 0x22
|
||
|
"16QAM.500.100.O",
|
||
|
"OFDM.500.55.E",
|
||
|
"OFDM.500.55.O",
|
||
|
"OFDM.200.55.E",
|
||
|
"OFDM.200.55.O",
|
||
|
|
||
|
// 1 Khz Bandwidth Data Modes
|
||
|
// 4 Car 4FSK Data mode 1000 Hz, 50 baud tones spaced @ 100 Hz
|
||
|
"4FSK.1000.50.E", // 0x28
|
||
|
"4FSK.1000.50.O",
|
||
|
|
||
|
// 2500 dblOffsetHz bandwidth modes
|
||
|
// 10 Car PSK Data Modes 50 baud
|
||
|
|
||
|
"4PSKR.2500.50.E", // 0x2A
|
||
|
"4PSKR.2500.50.O",
|
||
|
"4PSK.2500.50.E",
|
||
|
"4PSK.2500.50.O",
|
||
|
|
||
|
// 10 Car PSK Data Modes 100 baud
|
||
|
|
||
|
"4PSK.2500.100.E", // 0x2E
|
||
|
"4PSK.2500.100.O",
|
||
|
|
||
|
|
||
|
// 10 Car 16QAM Data modes 100 baud
|
||
|
"16QAMR.2500.100.E", // 0x30
|
||
|
"16QAMR.2500.100.O",
|
||
|
"16QAM.2500.100.E", // 0x32
|
||
|
"16QAM.2500.100.O",
|
||
|
"OFDM.2500.55.E",
|
||
|
"OFDM.2500.55.O",
|
||
|
"",
|
||
|
"",
|
||
|
"", "", // 0x38 to 0x39
|
||
|
"PktFrameHeader", //3A
|
||
|
"PktFrameData",
|
||
|
"", // 0x3C
|
||
|
"OFDMACK",
|
||
|
"DataACK", // note special coding to have large distance from NAKs
|
||
|
"DataACKHiQ" // note special coding to have large distance from NAKs
|
||
|
};
|
||
|
|
||
|
const char shortFrameType[64][12] =
|
||
|
{
|
||
|
// Short Control Frames 1 Car, 500Hz,4FSK, 50 baud
|
||
|
// Used on OLED display
|
||
|
|
||
|
"DataNAK", // 0
|
||
|
"DataNAKLoQ",
|
||
|
"ConRejBusy",
|
||
|
"ConRejBW",
|
||
|
"ConAck", // 4
|
||
|
"DISC",
|
||
|
"BREAK",
|
||
|
"END",
|
||
|
"IDLE", // 8
|
||
|
"ConReq200",
|
||
|
"ConReq500",
|
||
|
"ConReq2500",
|
||
|
"IDFrame", // C
|
||
|
"PingAck",
|
||
|
"Ping", // E
|
||
|
"CQ_de", // F
|
||
|
|
||
|
// 200 Hz Bandwidth
|
||
|
// 1 Car modes
|
||
|
|
||
|
"4P.200.50", // 0x10
|
||
|
"4P.200.50",
|
||
|
"4P.200.100",
|
||
|
"4PS.200.100",
|
||
|
"16Q.200.100", // 0x14
|
||
|
"16Q.200.100",
|
||
|
"", "",// 0x16 to 0x17
|
||
|
"OConReq500", "OConReq2500",
|
||
|
|
||
|
// 500 Hz bandwidth Data
|
||
|
// 1 Car 4FSK Data mode 500 Hz, 50 baud tones spaced @ 100 Hz
|
||
|
|
||
|
"4F.500.50", // 0x1A
|
||
|
"4F.500.50",
|
||
|
// 2 Car PSK Data Modes
|
||
|
"4P.500.50",
|
||
|
"4P.500.50",
|
||
|
"4P.500.100", // 0x1E
|
||
|
"4P.500.100",
|
||
|
// 2 Car 16QAM Data Modes 100 baud
|
||
|
"16QR.500.100", // 0x20
|
||
|
"16QR.500.100",
|
||
|
// 2 Car 16QAM Data Modes 100 baud
|
||
|
"16Q.500.100", // 0x22
|
||
|
"16Q.500.100",
|
||
|
"OFDM.500",
|
||
|
"OFDM.500",
|
||
|
"OFDM.200",
|
||
|
"OFDM.200",
|
||
|
|
||
|
// 1 Khz Bandwidth Data Modes
|
||
|
// 4 Car 4FSK Data mode 1000 Hz, 50 baud tones spaced @ 100 Hz
|
||
|
"4F.1K.50", // 0x28
|
||
|
"4F.1K.50",
|
||
|
|
||
|
// 2500 dblOffsetHz bandwidth modes
|
||
|
// 10 Car PSK Data Modes 50 baud
|
||
|
|
||
|
"4PR.2500.50", // 0x2A
|
||
|
"4PR.2500.50",
|
||
|
"4P.2500.50",
|
||
|
"4P.2500.50",
|
||
|
|
||
|
// 10 Car PSK Data Modes 100 baud
|
||
|
|
||
|
"4P.2500.100", // 0x2E
|
||
|
"4P.2500.100",
|
||
|
|
||
|
|
||
|
// 10 Car 16QAM Data modes 100 baud
|
||
|
"QR.2500.100", // 0x30
|
||
|
"QR.2500.100",
|
||
|
"Q.2500.100", // 0x32
|
||
|
"Q.2500.100",
|
||
|
"OFDM.2500",
|
||
|
"OFDM.2500",
|
||
|
"",
|
||
|
"",
|
||
|
"", "", // 0x38 to 0x39
|
||
|
"PktHeader", //3A
|
||
|
"PktData",
|
||
|
"", // 0x3C
|
||
|
"OFDMACK",
|
||
|
"DataACK", // note special coding to have large distance from NAKs
|
||
|
"DataACKHiQ" // note special coding to have large distance from NAKs
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
void GetSemaphore()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void FreeSemaphore()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
BOOL CheckValidCallsignSyntax(char * strCallsign)
|
||
|
{
|
||
|
// Function for checking valid call sign syntax
|
||
|
|
||
|
char * Dash = strchr(strCallsign, '-');
|
||
|
int callLen = strlen(strCallsign);
|
||
|
char * ptr = strCallsign;
|
||
|
int SSID;
|
||
|
|
||
|
if (Dash)
|
||
|
{
|
||
|
callLen = Dash - strCallsign;
|
||
|
|
||
|
SSID = atoi(Dash + 1);
|
||
|
if (SSID > 15)
|
||
|
return FALSE;
|
||
|
|
||
|
if (strlen(Dash + 1) > 2)
|
||
|
return FALSE;
|
||
|
|
||
|
if (!isalnum(*(Dash + 1)))
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (callLen > 7 || callLen < 3)
|
||
|
return FALSE;
|
||
|
|
||
|
while (callLen--)
|
||
|
{
|
||
|
if (!isalnum(*(ptr++)))
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// Function to check for proper syntax of a 4, 6 or 8 character GS
|
||
|
|
||
|
BOOL CheckGSSyntax(char * GS)
|
||
|
{
|
||
|
int Len = strlen(GS);
|
||
|
|
||
|
if (!(Len == 4 || Len == 6 || Len == 8))
|
||
|
return FALSE;
|
||
|
|
||
|
if (!isalpha(GS[0]) || !isalpha(GS[1]))
|
||
|
return FALSE;
|
||
|
|
||
|
if (!isdigit(GS[2]) || !isdigit(GS[3]))
|
||
|
return FALSE;
|
||
|
|
||
|
if (Len == 4)
|
||
|
return TRUE;
|
||
|
|
||
|
if (!isalpha(GS[4]) || !isalpha(GS[5]))
|
||
|
return FALSE;
|
||
|
|
||
|
if (Len == 6)
|
||
|
return TRUE;
|
||
|
|
||
|
if (!isdigit(GS[6]) || !isdigit(GS[7]))
|
||
|
return FALSE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// Function polled by Main polling loop to see if time to play next wave stream
|
||
|
|
||
|
#ifdef WIN32
|
||
|
|
||
|
extern LARGE_INTEGER Frequency;
|
||
|
extern LARGE_INTEGER StartTicks;
|
||
|
extern LARGE_INTEGER NewTicks;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
extern int NErrors;
|
||
|
|
||
|
void testRS()
|
||
|
{
|
||
|
// feed random data into RS to check robustness
|
||
|
|
||
|
BOOL blnRSOK, FrameOK;
|
||
|
char bytRawData[256];
|
||
|
int DataLen = 128;
|
||
|
int intRSLen = 64;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < DataLen; i++)
|
||
|
{
|
||
|
bytRawData[i] = rand() % 256;
|
||
|
}
|
||
|
|
||
|
FrameOK = RSDecode(bytRawData, DataLen, intRSLen, &blnRSOK);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SendCWID(char * Callsign, BOOL x)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
// Subroutine to generate 1 symbol of leader
|
||
|
|
||
|
// returns pointer to Frame Type Name
|
||
|
|
||
|
const char * Name(UCHAR bytID)
|
||
|
{
|
||
|
return strFrameType[bytID];
|
||
|
}
|
||
|
|
||
|
// returns pointer to Frame Type Name
|
||
|
|
||
|
const char * shortName(UCHAR bytID)
|
||
|
{
|
||
|
return shortFrameType[bytID];
|
||
|
}
|
||
|
// Function to look up frame info from bytFrameType
|
||
|
|
||
|
BOOL FrameInfo(UCHAR bytFrameType, int * blnOdd, int * intNumCar, char * strMod,
|
||
|
int * intBaud, int * intDataLen, int * intRSLen, UCHAR * bytQualThres, char * strType)
|
||
|
{
|
||
|
// Used to "lookup" all parameters by frame Type.
|
||
|
// returns TRUE if all fields updated otherwise FALSE (improper bytFrameType)
|
||
|
|
||
|
// 1 Carrier 4FSK control frames
|
||
|
|
||
|
switch(bytFrameType)
|
||
|
{
|
||
|
case DataNAK:
|
||
|
case DataNAKLoQ:
|
||
|
case DataACK:
|
||
|
case DataACKHiQ:
|
||
|
case ConAck:
|
||
|
case ConRejBW:
|
||
|
case ConRejBusy:
|
||
|
case IDLEFRAME:
|
||
|
case DISCFRAME:
|
||
|
case BREAK:
|
||
|
case END:
|
||
|
|
||
|
*blnOdd = 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 0;
|
||
|
*intRSLen = 0;
|
||
|
strcpy(strMod, "4FSK");
|
||
|
*intBaud = 50;
|
||
|
break;
|
||
|
|
||
|
case IDFRAME:
|
||
|
case PING:
|
||
|
case CQ_de:
|
||
|
|
||
|
*blnOdd = 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 12;
|
||
|
*intRSLen = 4; // changed 0.8.0
|
||
|
strcpy(strMod, "4FSK");
|
||
|
*intBaud = 50;
|
||
|
break;
|
||
|
|
||
|
case PINGACK:
|
||
|
|
||
|
*blnOdd = 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 3;
|
||
|
*intRSLen = 0;
|
||
|
strcpy(strMod, "4FSK");
|
||
|
*intBaud = 50;
|
||
|
break;
|
||
|
|
||
|
|
||
|
case ConReq200:
|
||
|
case ConReq500:
|
||
|
case ConReq2500:
|
||
|
case OConReq500:
|
||
|
case OConReq2500:
|
||
|
|
||
|
*blnOdd = 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 6;
|
||
|
*intRSLen = 2;
|
||
|
strcpy(strMod, "4FSK");
|
||
|
*intBaud = 50;
|
||
|
break;
|
||
|
|
||
|
case OFDMACK:
|
||
|
|
||
|
*blnOdd = 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 6;
|
||
|
*intRSLen = 4;
|
||
|
strcpy(strMod, "4FSK");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
case PktFrameHeader:
|
||
|
|
||
|
// Special Variable Length frame
|
||
|
|
||
|
// This defines the header, 4PSK.500.100. Length is 6 bytes
|
||
|
// Once we have that we receive the rest of the packet in the
|
||
|
// mode defined in the header.
|
||
|
// Header is 4 bits Type 12 Bits Len 2 bytes CRC 2 bytes RS
|
||
|
|
||
|
*blnOdd = 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 2;
|
||
|
*intRSLen = 2;
|
||
|
strcpy(strMod, "4FSK");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
case PktFrameData:
|
||
|
|
||
|
// Special Variable Length frame
|
||
|
|
||
|
// This isn't ever transmitted but is used to define the
|
||
|
// current setting for the data frame. Mode and Length
|
||
|
// are variable
|
||
|
|
||
|
|
||
|
*blnOdd = 1;
|
||
|
*intNumCar = pktCarriers[pktMode];
|
||
|
*intDataLen = pktDataLen;
|
||
|
*intRSLen = pktRSLen;
|
||
|
strcpy(strMod, &pktMod[pktMode][0]);
|
||
|
strlop(strMod, '/');
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
|
||
|
// Others are Even/Odd Pairs
|
||
|
|
||
|
switch(bytFrameType & 0xFE)
|
||
|
{
|
||
|
case D4PSK_200_50_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 32;
|
||
|
*intRSLen = 8;
|
||
|
strcpy(strMod, "4PSK");
|
||
|
*intBaud = 50;
|
||
|
break;
|
||
|
|
||
|
case D4PSK_200_100_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 64;
|
||
|
*intRSLen = 16;
|
||
|
strcpy(strMod, "4PSK");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
|
||
|
case D16QAM_200_100_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 120;
|
||
|
*intRSLen = 40;
|
||
|
strcpy(strMod, "16QAM");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
|
||
|
case D4FSK_500_50_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 1;
|
||
|
*intDataLen = 32;
|
||
|
*intRSLen = 8;
|
||
|
strcpy(strMod, "4FSK");
|
||
|
*intBaud = 50;
|
||
|
*bytQualThres = 30;
|
||
|
break;
|
||
|
|
||
|
case D4PSK_500_50_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 2;
|
||
|
*intDataLen = 32;
|
||
|
*intRSLen = 8;
|
||
|
strcpy(strMod, "4PSK");
|
||
|
*intBaud = 50;
|
||
|
break;
|
||
|
|
||
|
case D4PSK_500_100_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 2;
|
||
|
*intDataLen = 64;
|
||
|
*intRSLen = 16;
|
||
|
strcpy(strMod, "4PSK");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
case D16QAMR_500_100_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 2;
|
||
|
*intDataLen = 120;
|
||
|
*intRSLen = 40;
|
||
|
strcpy(strMod, "16QAMR");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
case D16QAM_500_100_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 2;
|
||
|
*intDataLen = 120;
|
||
|
*intRSLen = 40;
|
||
|
strcpy(strMod, "16QAM");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
case DOFDM_200_55_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 3;
|
||
|
*intDataLen = 40;
|
||
|
*intRSLen = 10;
|
||
|
strcpy(strMod, "OFDM");
|
||
|
*intBaud = 55;
|
||
|
break;
|
||
|
|
||
|
case DOFDM_500_55_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 9;
|
||
|
*intDataLen = 40;
|
||
|
*intRSLen = 10;
|
||
|
strcpy(strMod, "OFDM");
|
||
|
*intBaud = 55;
|
||
|
break;
|
||
|
|
||
|
case D4FSK_1000_50_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 2;
|
||
|
*intDataLen = 32;
|
||
|
*intRSLen = 8;
|
||
|
strcpy(strMod, "4FSK");
|
||
|
*intBaud = 50;
|
||
|
break;
|
||
|
|
||
|
case D4PSKR_2500_50_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 10;
|
||
|
*intDataLen = 32;
|
||
|
*intRSLen = 8;
|
||
|
strcpy(strMod, "4PSKR");
|
||
|
*intBaud = 50;
|
||
|
break;
|
||
|
|
||
|
case D4PSK_2500_50_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 10;
|
||
|
*intDataLen = 32;
|
||
|
*intRSLen = 8;
|
||
|
strcpy(strMod, "4PSK");
|
||
|
*intBaud = 50;
|
||
|
break;
|
||
|
|
||
|
case D4PSK_2500_100_E:
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 10;
|
||
|
*intDataLen = 64;
|
||
|
*intRSLen = 16;
|
||
|
strcpy(strMod, "4PSK");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
case D16QAMR_2500_100_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 10;
|
||
|
*intDataLen = 120;
|
||
|
*intRSLen = 40;
|
||
|
strcpy(strMod, "16QAMR");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
case D16QAM_2500_100_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 10;
|
||
|
*intDataLen = 120;
|
||
|
*intRSLen = 40;
|
||
|
strcpy(strMod, "16QAM");
|
||
|
*intBaud = 100;
|
||
|
break;
|
||
|
|
||
|
case DOFDM_2500_55_E:
|
||
|
|
||
|
*blnOdd = (1 & bytFrameType) != 0;
|
||
|
*intNumCar = 43;
|
||
|
*intDataLen = 40;
|
||
|
*intRSLen = 10;
|
||
|
strcpy(strMod, "OFDM");
|
||
|
*intBaud = 55;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Debugprintf("No data for frame type= %X", bytFrameType);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
strcpy(strType,strFrameType[bytFrameType]);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
int xNPAR = -1; // Number of Parity Bytes - used in RS Code
|
||
|
|
||
|
int MaxErrors = 0;
|
||
|
|
||
|
int xRSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen)
|
||
|
{
|
||
|
// This just returns the Parity Bytes. I don't see the point
|
||
|
// in copying the message about
|
||
|
|
||
|
unsigned char Padded[256]; // The padded Data
|
||
|
|
||
|
int Length = DataLen + RSLen; // Final Length of packet
|
||
|
int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes
|
||
|
|
||
|
// subroutine to do the RS encode. For full length and shortend RS codes up to 8 bit symbols (mm = 8)
|
||
|
|
||
|
if (NPAR != RSLen) // Changed RS Len, so recalc constants;
|
||
|
{
|
||
|
NPAR = RSLen;
|
||
|
MaxErrors = NPAR / 2;
|
||
|
initialize_ecc();
|
||
|
}
|
||
|
|
||
|
// Copy the supplied data to end of data array.
|
||
|
|
||
|
memset(Padded, 0, PadLength);
|
||
|
memcpy(&Padded[PadLength], bytToRS, DataLen);
|
||
|
|
||
|
encode_data(Padded, 255-RSLen, RSBytes);
|
||
|
|
||
|
return RSLen;
|
||
|
}
|
||
|
|
||
|
// Main RS decode function
|
||
|
|
||
|
extern int index_of[];
|
||
|
extern int recd[];
|
||
|
int Corrected[256];
|
||
|
extern int tt; // number of errors that can be corrected
|
||
|
extern int kk; // Info Symbols
|
||
|
|
||
|
BOOL blnErrorsCorrected;
|
||
|
|
||
|
#define NEWRS
|
||
|
|
||
|
// Function to encode ConnectRequest frame
|
||
|
|
||
|
// ' Function to encode an ACK control frame (2 bytes total) ...with 5 bit Quality code
|
||
|
|
||
|
|
||
|
void SendID(BOOL blnEnableCWID)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
void ASCIIto6Bit(char * Padded, UCHAR * Compressed)
|
||
|
{
|
||
|
// Input must be 8 bytes which will convert to 6 bytes of packed 6 bit characters and
|
||
|
// inputs must be the ASCII character set values from 32 to 95....
|
||
|
|
||
|
unsigned long long intSum = 0;
|
||
|
|
||
|
int i;
|
||
|
|
||
|
for (i=0; i<4; i++)
|
||
|
{
|
||
|
intSum = (64 * intSum) + Padded[i] - 32;
|
||
|
}
|
||
|
|
||
|
Compressed[0] = (UCHAR)(intSum >> 16) & 255;
|
||
|
Compressed[1] = (UCHAR)(intSum >> 8) & 255;
|
||
|
Compressed[2] = (UCHAR)intSum & 255;
|
||
|
|
||
|
intSum = 0;
|
||
|
|
||
|
for (i=4; i<8; i++)
|
||
|
{
|
||
|
intSum = (64 * intSum) + Padded[i] - 32;
|
||
|
}
|
||
|
|
||
|
Compressed[3] = (UCHAR)(intSum >> 16) & 255;
|
||
|
Compressed[4] = (UCHAR)(intSum >> 8) & 255;
|
||
|
Compressed[5] = (UCHAR)intSum & 255;
|
||
|
}
|
||
|
|
||
|
void Bit6ToASCII(UCHAR * Padded, UCHAR * UnCompressed)
|
||
|
{
|
||
|
// uncompress 6 to 8
|
||
|
|
||
|
// Input must be 6 bytes which represent packed 6 bit characters that well
|
||
|
// result will be 8 ASCII character set values from 32 to 95...
|
||
|
|
||
|
unsigned long long intSum = 0;
|
||
|
|
||
|
int i;
|
||
|
|
||
|
for (i=0; i<3; i++)
|
||
|
{
|
||
|
intSum = (intSum << 8) + Padded[i];
|
||
|
}
|
||
|
|
||
|
UnCompressed[0] = (UCHAR)((intSum >> 18) & 63) + 32;
|
||
|
UnCompressed[1] = (UCHAR)((intSum >> 12) & 63) + 32;
|
||
|
UnCompressed[2] = (UCHAR)((intSum >> 6) & 63) + 32;
|
||
|
UnCompressed[3] = (UCHAR)(intSum & 63) + 32;
|
||
|
|
||
|
intSum = 0;
|
||
|
|
||
|
for (i=3; i<6; i++)
|
||
|
{
|
||
|
intSum = (intSum << 8) + Padded[i] ;
|
||
|
}
|
||
|
|
||
|
UnCompressed[4] = (UCHAR)((intSum >> 18) & 63) + 32;
|
||
|
UnCompressed[5] = (UCHAR)((intSum >> 12) & 63) + 32;
|
||
|
UnCompressed[6] = (UCHAR)((intSum >> 6) & 63) + 32;
|
||
|
UnCompressed[7] = (UCHAR)(intSum & 63) + 32;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Function to compress callsign (up to 7 characters + optional "-"SSID (-0 to -15 or -A to -Z)
|
||
|
|
||
|
void CompressCallsign(char * inCallsign, UCHAR * Compressed)
|
||
|
{
|
||
|
char Callsign[10] = "";
|
||
|
char Padded[16];
|
||
|
int SSID;
|
||
|
char * Dash;
|
||
|
|
||
|
memcpy(Callsign, inCallsign, 10);
|
||
|
Dash = strchr(Callsign, '-');
|
||
|
|
||
|
if (Dash == 0) // if No SSID
|
||
|
{
|
||
|
strcpy(Padded, Callsign);
|
||
|
strcat(Padded, " ");
|
||
|
Padded[7] = '0'; // "0" indicates no SSID
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*(Dash++) = 0;
|
||
|
SSID = atoi(Dash);
|
||
|
|
||
|
strcpy(Padded, Callsign);
|
||
|
strcat(Padded, " ");
|
||
|
|
||
|
if (SSID >= 10) // ' handles special case of -10 to -15 : ; < = > ? '
|
||
|
Padded[7] = ':' + SSID - 10;
|
||
|
else
|
||
|
Padded[7] = *(Dash);
|
||
|
}
|
||
|
|
||
|
ASCIIto6Bit(Padded, Compressed); //compress to 8 6 bit characters 6 bytes total
|
||
|
}
|
||
|
|
||
|
// Function to compress Gridsquare (up to 8 characters)
|
||
|
|
||
|
void CompressGridSquare(char * Square, UCHAR * Compressed)
|
||
|
{
|
||
|
char Padded[17];
|
||
|
|
||
|
if (strlen(Square) > 8)
|
||
|
return;
|
||
|
|
||
|
strcpy(Padded, Square);
|
||
|
strcat(Padded, " ");
|
||
|
|
||
|
ASCIIto6Bit(Padded, Compressed); //compress to 8 6 bit characters 6 bytes total
|
||
|
}
|
||
|
|
||
|
// Function to decompress 6 byte call sign to 7 characters plus optional -SSID of -0 to -15 or -A to -Z
|
||
|
|
||
|
void DeCompressCallsign(char * bytCallsign, char * returned)
|
||
|
{
|
||
|
char bytTest[10] = "";
|
||
|
char SSID[8] = "";
|
||
|
|
||
|
Bit6ToASCII(bytCallsign, bytTest);
|
||
|
|
||
|
memcpy(returned, bytTest, 7);
|
||
|
returned[7] = 0;
|
||
|
strlop(returned, ' '); // remove trailing space
|
||
|
|
||
|
if (bytTest[7] == '0') // Value of "0" so No SSID
|
||
|
returned[6] = 0;
|
||
|
else if (bytTest[7] >= 58 && bytTest[7] <= 63) //' handles special case for -10 to -15
|
||
|
sprintf(SSID, "-%d", bytTest[7] - 48);
|
||
|
else
|
||
|
sprintf(SSID, "-%c", bytTest[7]);
|
||
|
|
||
|
strcat(returned, SSID);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Function to decompress 6 byte Grid square to 4, 6 or 8 characters
|
||
|
|
||
|
void DeCompressGridSquare(char * bytGS, char * returned)
|
||
|
{
|
||
|
char bytTest[10] = "";
|
||
|
Bit6ToASCII(bytGS, bytTest);
|
||
|
|
||
|
strlop(bytTest, ' ');
|
||
|
strcpy(returned, bytTest);
|
||
|
}
|
||
|
|
||
|
// A function to compute the parity symbol used in the frame type encoding
|
||
|
|
||
|
UCHAR ComputeTypeParity(UCHAR bytFrameType)
|
||
|
{
|
||
|
UCHAR bytMask = 0x30; // only using 6 bits
|
||
|
UCHAR bytParitySum = 3;
|
||
|
UCHAR bytSym = 0;
|
||
|
int k;
|
||
|
|
||
|
for (k = 0; k < 3; k++)
|
||
|
{
|
||
|
bytSym = (bytMask & bytFrameType) >> (2 * (2 - k));
|
||
|
bytParitySum = bytParitySum ^ bytSym;
|
||
|
bytMask = bytMask >> 2;
|
||
|
}
|
||
|
|
||
|
return bytParitySum & 0x3;
|
||
|
}
|
||
|
|
||
|
// Function to look up the byte value from the frame string name
|
||
|
|
||
|
UCHAR FrameCode(char * strFrameName)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < 64; i++)
|
||
|
{
|
||
|
if (strcmp(strFrameType[i], strFrameName) == 0)
|
||
|
{
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
unsigned int GenCRC16(unsigned char * Data, unsigned short length)
|
||
|
{
|
||
|
// 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
|
||
|
|
||
|
int intRegister = 0xffff; //intSeed
|
||
|
int i,j;
|
||
|
int Bit;
|
||
|
int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1
|
||
|
|
||
|
for (j = 0; j < length; j++)
|
||
|
{
|
||
|
int Mask = 0x80; // Top bit first
|
||
|
|
||
|
for (i = 0; i < 8; i++) // for each bit processing MS bit first
|
||
|
{
|
||
|
Bit = Data[j] & Mask;
|
||
|
Mask >>= 1;
|
||
|
|
||
|
if (intRegister & 0x8000) // Then ' the MSB of the register is set
|
||
|
{
|
||
|
// Shift left, place data bit as LSB, then divide
|
||
|
// Register := shiftRegister left shift 1
|
||
|
// Register := shiftRegister xor polynomial
|
||
|
|
||
|
if (Bit)
|
||
|
intRegister = 0xFFFF & (1 + (intRegister << 1));
|
||
|
else
|
||
|
intRegister = 0xFFFF & (intRegister << 1);
|
||
|
|
||
|
intRegister = intRegister ^ intPoly;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// the MSB is not set
|
||
|
// Register is not divisible by polynomial yet.
|
||
|
// Just shift left and bring current data bit onto LSB of shiftRegister
|
||
|
if (Bit)
|
||
|
intRegister = 0xFFFF & (1 + (intRegister << 1));
|
||
|
else
|
||
|
intRegister = 0xFFFF & (intRegister << 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return intRegister;
|
||
|
}
|
||
|
|
||
|
BOOL checkcrc16(unsigned char * Data, unsigned short length)
|
||
|
{
|
||
|
int intRegister = 0xffff; //intSeed
|
||
|
int i,j;
|
||
|
int Bit;
|
||
|
int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1
|
||
|
|
||
|
for (j = 0; j < (length - 2); j++) // ' 2 bytes short of data length
|
||
|
{
|
||
|
int Mask = 0x80; // Top bit first
|
||
|
|
||
|
for (i = 0; i < 8; i++) // for each bit processing MS bit first
|
||
|
{
|
||
|
Bit = Data[j] & Mask;
|
||
|
Mask >>= 1;
|
||
|
|
||
|
if (intRegister & 0x8000) // Then ' the MSB of the register is set
|
||
|
{
|
||
|
// Shift left, place data bit as LSB, then divide
|
||
|
// Register := shiftRegister left shift 1
|
||
|
// Register := shiftRegister xor polynomial
|
||
|
|
||
|
if (Bit)
|
||
|
intRegister = 0xFFFF & (1 + (intRegister << 1));
|
||
|
else
|
||
|
intRegister = 0xFFFF & (intRegister << 1);
|
||
|
|
||
|
intRegister = intRegister ^ intPoly;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// the MSB is not set
|
||
|
// Register is not divisible by polynomial yet.
|
||
|
// Just shift left and bring current data bit onto LSB of shiftRegister
|
||
|
if (Bit)
|
||
|
intRegister = 0xFFFF & (1 + (intRegister << 1));
|
||
|
else
|
||
|
intRegister = 0xFFFF & (intRegister << 1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Data[length - 2] == intRegister >> 8)
|
||
|
if (Data[length - 1] == (intRegister & 0xFF))
|
||
|
return TRUE;
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Subroutine to compute a 16 bit CRC value and append it to the Data... With LS byte XORed by bytFrameType
|
||
|
|
||
|
void GenCRC16FrameType(char * Data, int Length, UCHAR bytFrameType)
|
||
|
{
|
||
|
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) ^ bytFrameType; // LS 8 bits of Register
|
||
|
}
|
||
|
|
||
|
// Function to compute a 16 bit CRC value and check it against the last 2 bytes of Data (the CRC) XORing LS byte with bytFrameType..
|
||
|
|
||
|
unsigned short int compute_crc(unsigned char *buf,int len);
|
||
|
|
||
|
BOOL CheckCRC16FrameType(unsigned char * Data, int Length, UCHAR bytFrameType)
|
||
|
{
|
||
|
// 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) ^ bytFrameType) == Data[Length + 1])
|
||
|
return TRUE;
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
void SaveQueueOnBreak()
|
||
|
{
|
||
|
// Save data we are about to remove from TX buffer
|
||
|
}
|
||
|
|
||
|
|
||
|
extern UCHAR bytEchoData[]; // has to be at least max packet size (?1280)
|
||
|
|
||
|
extern int bytesEchoed;
|
||
|
|
||
|
extern UCHAR DelayedEcho;
|
||
|
|
||
|
|
||
|
// Timer Rotines
|
||
|
|
||
|
void CheckTimers()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
// Main polling Function returns True or FALSE if closing
|
||
|
|
||
|
int dttLastBusy;
|
||
|
int dttLastClear;
|
||
|
int dttStartRTMeasure;
|
||
|
extern int intLastStart;
|
||
|
extern int intLastStop;
|
||
|
float dblAvgBaselineSlow;
|
||
|
float dblAvgBaselineFast;
|
||
|
float dblAvgPk2BaselineRatio;
|
||
|
|
||
|
// Functino to extract bandwidth from ARQBandwidth
|
||
|
|
||
|
|
||
|
// Function to implement a busy detector based on 1024 point FFT
|
||
|
/*
|
||
|
BOOL BusyDetect(float * dblMag, int intStart, int intStop)
|
||
|
{
|
||
|
// this only called while searching for leader ...once leader detected, no longer called.
|
||
|
// First look at simple peak over the frequency band of interest.
|
||
|
// Status: May 28, 2014. Some initial encouraging results. But more work needed.
|
||
|
// 1) Use of Start and Stop ranges good and appear to work well ...may need some tweaking +/_ a few bins.
|
||
|
// 2) Using a Fast attack and slow decay for the dblAvgPk2BaselineRation number e.g.
|
||
|
// dblAvgPk2BaselineRatio = Max(dblPeakPwrAtFreq / dblAvgBaselineX, 0.9 * dblAvgPk2BaselineRatio)
|
||
|
// Seems to work well for the narrow detector. Tested on real CW, PSK, RTTY.
|
||
|
// Still needs work on the wide band detector. (P3, P4 etc) May be issues with AGC speed. (my initial on-air tests using AGC Fast).
|
||
|
// Ideally can find a set of parameters that won't require user "tweaking" (Busy Squelch) but that is an alternative if needed.
|
||
|
// use of technique of re initializing some parameters on a change in detection bandwidth looks good and appears to work well with
|
||
|
// scanning. Could be expanded with properties to "read back" these parameters so they could be saved and re initialize upon each new frequency.
|
||
|
|
||
|
static int intBusyCountPkToBaseline = 0;
|
||
|
static int intBusyCountFastToSlow = 0;
|
||
|
static int intBusyCountSlowToFast = 0;
|
||
|
static BOOL blnLastBusy = FALSE;
|
||
|
|
||
|
float dblAvgBaseline = 0;
|
||
|
float dblPwrAtPeakFreq = 0;
|
||
|
float dblAvgBaselineX;
|
||
|
float dblAlphaBaselineSlow = 0.1f; // This factor affects the filtering of baseline slow. smaller = slower filtering
|
||
|
float dblAlphaBaselineFast = 0.5f; // This factor affects the filtering of baseline fast. smaller = slower filtering
|
||
|
int intPkIndx = 0;
|
||
|
float dblFSRatioNum, dblSFRatioNum;
|
||
|
BOOL blnFS, blnSF, blnPkBaseline;
|
||
|
int i;
|
||
|
|
||
|
// This holds off any processing of data until 100 ms after PTT release to allow receiver recovery.
|
||
|
|
||
|
if (Now - dttStartRTMeasure < 100)
|
||
|
return blnLastBusy;
|
||
|
|
||
|
for (i = intStart; i <= intStop; i++) // cover a range that matches the bandwidth expanded (+/-) by the tuning range
|
||
|
{
|
||
|
if (dblMag[i] > dblPwrAtPeakFreq)
|
||
|
{
|
||
|
dblPwrAtPeakFreq = dblMag[i];
|
||
|
intPkIndx = i;
|
||
|
}
|
||
|
dblAvgBaseline += dblMag[i];
|
||
|
}
|
||
|
|
||
|
if (intPkIndx == 0)
|
||
|
return 0;
|
||
|
|
||
|
// add in the 2 bins above and below the peak (about 59 Hz total bandwidth)
|
||
|
// This needs refinement for FSK mods like RTTY which have near equal peaks making the Peak and baseline on strong signals near equal
|
||
|
// Computer the power within a 59 Hz spectrum around the peak
|
||
|
|
||
|
dblPwrAtPeakFreq += (dblMag[intPkIndx - 2] + dblMag[intPkIndx - 1]) + (dblMag[intPkIndx + 2] + dblMag[intPkIndx + 1]);
|
||
|
dblAvgBaselineX = (dblAvgBaseline - dblPwrAtPeakFreq) / (intStop - intStart - 5); // the avg Pwr per bin ignoring the 59 Hz area centered on the peak
|
||
|
dblPwrAtPeakFreq = dblPwrAtPeakFreq / 5; //the the average Power (per bin) in the region of the peak (peak +/- 2bins...about 59 Hz)
|
||
|
|
||
|
if (intStart == intLastStart && intStop == intLastStop)
|
||
|
{
|
||
|
dblAvgPk2BaselineRatio = dblPwrAtPeakFreq / dblAvgBaselineX;
|
||
|
dblAvgBaselineSlow = (1 - dblAlphaBaselineSlow) * dblAvgBaselineSlow + dblAlphaBaselineSlow * dblAvgBaseline;
|
||
|
dblAvgBaselineFast = (1 - dblAlphaBaselineFast) * dblAvgBaselineFast + dblAlphaBaselineFast * dblAvgBaseline;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// This initializes the values after a bandwidth change
|
||
|
|
||
|
dblAvgPk2BaselineRatio = dblPwrAtPeakFreq / dblAvgBaselineX;
|
||
|
dblAvgBaselineSlow = dblAvgBaseline;
|
||
|
dblAvgBaselineFast = dblAvgBaseline;
|
||
|
intLastStart = intStart;
|
||
|
intLastStop = intStop;
|
||
|
}
|
||
|
|
||
|
if (Now - dttLastBusy < 1000 || ProtocolState != DISC) // Why??
|
||
|
return blnLastBusy;
|
||
|
|
||
|
if (dblAvgPk2BaselineRatio > 1.118f * powf(Squelch, 1.5f)) // These values appear to work OK but may need optimization April 21, 2015
|
||
|
{
|
||
|
blnPkBaseline = TRUE;
|
||
|
dblAvgBaselineSlow = dblAvgBaseline;
|
||
|
dblAvgBaselineFast = dblAvgBaseline;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// 'If intBusyCountPkToBaseline > 0 Then
|
||
|
|
||
|
blnPkBaseline = FALSE;
|
||
|
}
|
||
|
|
||
|
// This detects wide band "pulsy" modes like Pactor 3, MFSK etc
|
||
|
|
||
|
switch(Squelch) // this provides a modest adjustment to the ratio limit based on practical squelch values
|
||
|
{
|
||
|
//These values yield less sensiivity for F:S which minimizes trigger by static crashes but my need further optimization May 2, 2015
|
||
|
|
||
|
case 0:
|
||
|
case 1:
|
||
|
case 2:
|
||
|
dblFSRatioNum = 1.9f;
|
||
|
dblSFRatioNum = 1.2f;
|
||
|
break;
|
||
|
|
||
|
case 3:
|
||
|
dblFSRatioNum = 2.1f;
|
||
|
dblSFRatioNum = 1.4f;
|
||
|
break;
|
||
|
case 4:
|
||
|
dblFSRatioNum = 2.3f;
|
||
|
dblSFRatioNum = 1.6f;
|
||
|
break;
|
||
|
case 5:
|
||
|
dblFSRatioNum = 2.5f;
|
||
|
dblSFRatioNum = 1.8f;
|
||
|
break;
|
||
|
case 6:
|
||
|
dblFSRatioNum = 2.7f;
|
||
|
dblSFRatioNum = 2.0f;
|
||
|
case 7:
|
||
|
dblFSRatioNum = 2.9f;
|
||
|
dblSFRatioNum = 2.2f;
|
||
|
case 8:
|
||
|
case 9:
|
||
|
case 10:
|
||
|
dblFSRatioNum = 3.1f;
|
||
|
dblSFRatioNum = 2.4f;
|
||
|
}
|
||
|
|
||
|
// This covers going from Modulation to no modulation e.g. End of Pactor frame
|
||
|
|
||
|
if ((dblAvgBaselineSlow / dblAvgBaselineFast) > dblSFRatioNum)
|
||
|
|
||
|
//Debug.WriteLine(" Slow to Fast")
|
||
|
blnSF = TRUE;
|
||
|
else
|
||
|
blnSF = FALSE;
|
||
|
|
||
|
// This covers going from no modulation to Modulation e.g. Start of Pactor Frame or Static crash
|
||
|
|
||
|
if ((dblAvgBaselineFast / dblAvgBaselineSlow) > dblFSRatioNum)
|
||
|
//Debug.WriteLine(" Fast to Slow")
|
||
|
blnFS = TRUE;
|
||
|
else
|
||
|
blnFS = FALSE;
|
||
|
|
||
|
if (blnFS || blnSF || blnPkBaseline)
|
||
|
{
|
||
|
//'If blnFS Then Debug.WriteLine("Busy: Fast to Slow")
|
||
|
//'If blnSF Then Debug.WriteLine("Busy: Slow to Fast")
|
||
|
//'If blnPkBaseline Then Debug.WriteLine("Busy: Pk to Baseline")
|
||
|
blnLastBusy = TRUE;
|
||
|
dttLastBusy = Now;
|
||
|
return TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
blnLastBusy = FALSE;
|
||
|
dttLastClear = Now;
|
||
|
return FALSE;
|
||
|
}
|
||
|
return blnLastBusy;
|
||
|
}
|
||
|
*/
|
||
|
// Subroutine to update the Busy detector when not displaying Spectrum or Waterfall (graphics disabled)
|
||
|
|
||
|
|
||
|
extern UCHAR CurrentLevel;
|
||
|
|
||
|
#ifdef PLOTSPECTRUM
|
||
|
float dblMagSpectrum[206];
|
||
|
float dblMaxScale = 0.0f;
|
||
|
extern UCHAR Pixels[4096];
|
||
|
extern UCHAR * pixelPointer;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/* Old Version pre gui
|
||
|
|
||
|
void UpdateBusyDetector(short * bytNewSamples)
|
||
|
{
|
||
|
float dblReF[1024];
|
||
|
float dblImF[1024];
|
||
|
|
||
|
float dblMag[206];
|
||
|
|
||
|
static BOOL blnLastBusyStatus;
|
||
|
|
||
|
float dblMagAvg = 0;
|
||
|
int intTuneLineLow, intTuneLineHi, intDelta;
|
||
|
int i;
|
||
|
|
||
|
if (ProtocolState != DISC) // ' Only process busy when in DISC state
|
||
|
return;
|
||
|
|
||
|
// if (State != SearchingForLeader)
|
||
|
// return; // only when looking for leader
|
||
|
|
||
|
if (Now - LastBusyCheck < 100)
|
||
|
return;
|
||
|
|
||
|
LastBusyCheck = Now;
|
||
|
|
||
|
FourierTransform(1024, bytNewSamples, &dblReF[0], &dblImF[0], FALSE);
|
||
|
|
||
|
for (i = 0; i < 206; i++)
|
||
|
{
|
||
|
// starting at ~300 Hz to ~2700 Hz Which puts the center of the signal in the center of the window (~1500Hz)
|
||
|
|
||
|
dblMag[i] = powf(dblReF[i + 25], 2) + powf(dblImF[i + 25], 2); // first pass
|
||
|
dblMagAvg += dblMag[i];
|
||
|
}
|
||
|
|
||
|
// LookforPacket(dblMag, dblMagAvg, 206, &dblReF[25], &dblImF[25]);
|
||
|
// packet_process_samples(bytNewSamples, 1200);
|
||
|
|
||
|
intDelta = (ExtractARQBandwidth() / 2 + TuningRange) / 11.719f;
|
||
|
|
||
|
intTuneLineLow = max((103 - intDelta), 3);
|
||
|
intTuneLineHi = min((103 + intDelta), 203);
|
||
|
|
||
|
// if (ProtocolState == DISC) // ' Only process busy when in DISC state
|
||
|
{
|
||
|
blnBusyStatus = BusyDetect3(dblMag, intTuneLineLow, intTuneLineHi);
|
||
|
|
||
|
if (blnBusyStatus && !blnLastBusyStatus)
|
||
|
{
|
||
|
QueueCommandToHost("BUSY TRUE");
|
||
|
newStatus = TRUE; // report to PTC
|
||
|
}
|
||
|
// stcStatus.Text = "True"
|
||
|
// queTNCStatus.Enqueue(stcStatus)
|
||
|
// 'Debug.WriteLine("BUSY TRUE @ " & Format(DateTime.UtcNow, "HH:mm:ss"))
|
||
|
|
||
|
else if (blnLastBusyStatus && !blnBusyStatus)
|
||
|
{
|
||
|
QueueCommandToHost("BUSY FALSE");
|
||
|
newStatus = TRUE; // report to PTC
|
||
|
}
|
||
|
// stcStatus.Text = "False"
|
||
|
// queTNCStatus.Enqueue(stcStatus)
|
||
|
// 'Debug.WriteLine("BUSY FALSE @ " & Format(DateTime.UtcNow, "HH:mm:ss"))
|
||
|
|
||
|
blnLastBusyStatus = blnBusyStatus;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*/
|
||
|
|
||
|
// Function to encode data for all PSK frame types
|
||
|
|
||
|
int EncodePSKData(UCHAR bytFrameType, UCHAR * bytDataToSend, int Length, unsigned char * bytEncodedBytes)
|
||
|
{
|
||
|
// Objective is to use this to use this to send all PSK data frames
|
||
|
// '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 inlcude all data (with FEC appended) for the entire frame. Each block will be identical in length.
|
||
|
// Ininitial implementation:
|
||
|
// intNum Car may be 1, 2, 4 or 8
|
||
|
// intBaud may be 100, 167
|
||
|
// intPSKMode may be 4 (4PSK) or 8 (8PSK)
|
||
|
// bytDataToSend must be equal to or less than max data supported by the frame or a exception will be logged and an empty array returned
|
||
|
|
||
|
// First determine if bytDataToSend is compatible with the requested modulation mode.
|
||
|
|
||
|
int intNumCar, intBaud, intDataLen, intRSLen, bytDataToSendLengthPtr, intEncodedDataPtr;
|
||
|
|
||
|
int intCarDataCnt, intStartIndex;
|
||
|
BOOL blnOdd;
|
||
|
char strType[18];
|
||
|
char strMod[16];
|
||
|
BOOL blnFrameTypeOK;
|
||
|
UCHAR bytQualThresh;
|
||
|
int i;
|
||
|
UCHAR * bytToRS = &bytEncodedBytes[2];
|
||
|
|
||
|
blnFrameTypeOK = FrameInfo(bytFrameType, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytQualThresh, strType);
|
||
|
|
||
|
if (intDataLen == 0 || Length == 0 || !blnFrameTypeOK)
|
||
|
{
|
||
|
//Logs.Exception("[EncodeFSKFrameType] Failure to update parameters for frame type H" & Format(bytFrameType, "X") & " DataToSend Len=" & bytDataToSend.Length.ToString)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Generate the 2 bytes for the frame type data:
|
||
|
|
||
|
bytEncodedBytes[0] = bytFrameType;
|
||
|
bytEncodedBytes[1] = bytFrameType ^ bytSessionID;
|
||
|
|
||
|
bytDataToSendLengthPtr = 0;
|
||
|
intEncodedDataPtr = 2;
|
||
|
|
||
|
// Now compute the RS frame for each carrier in sequence and move it to bytEncodedBytes
|
||
|
|
||
|
if (strchr(strMod, 'R'))
|
||
|
{
|
||
|
// Robust Frame. We send data twice, so only encode half the carriers
|
||
|
|
||
|
intNumCar /= 2;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < intNumCar; i++) // across all carriers
|
||
|
{
|
||
|
intCarDataCnt = Length - bytDataToSendLengthPtr;
|
||
|
|
||
|
if (intCarDataCnt > intDataLen) // why not > ??
|
||
|
{
|
||
|
// Won't all fit
|
||
|
|
||
|
bytToRS[0] = intDataLen;
|
||
|
intStartIndex = intEncodedDataPtr;
|
||
|
memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intDataLen);
|
||
|
bytDataToSendLengthPtr += intDataLen;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Last bit
|
||
|
|
||
|
memset(&bytToRS[0], 0, intDataLen);
|
||
|
|
||
|
bytToRS[0] = intCarDataCnt; // Could be 0 if insuffient data for # of carriers
|
||
|
|
||
|
if (intCarDataCnt > 0)
|
||
|
{
|
||
|
memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intCarDataCnt);
|
||
|
bytDataToSendLengthPtr += intCarDataCnt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GenCRC16FrameType(bytToRS, intDataLen + 1, bytFrameType); // calculate the CRC on the byte count + data bytes
|
||
|
|
||
|
RSEncode(bytToRS, bytToRS + intDataLen + 3, intDataLen + 3, intRSLen); // Generate the RS encoding ...now 14 bytes total
|
||
|
|
||
|
// Need: (2 bytes for Frame Type) +( Data + RS + 1 byte byteCount + 2 Byte CRC per carrier)
|
||
|
|
||
|
intEncodedDataPtr += intDataLen + 3 + intRSLen;
|
||
|
|
||
|
bytToRS += intDataLen + 3 + intRSLen;
|
||
|
}
|
||
|
|
||
|
if (strchr(strMod, 'R'))
|
||
|
{
|
||
|
// Robust Frame. We send data twice, so copy data
|
||
|
|
||
|
memcpy(&bytEncodedBytes[intEncodedDataPtr], &bytEncodedBytes[2], intEncodedDataPtr - 2);
|
||
|
intEncodedDataPtr += intEncodedDataPtr - 2;
|
||
|
}
|
||
|
|
||
|
|
||
|
return intEncodedDataPtr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// Function to encode data for all FSK frame types
|
||
|
|
||
|
int EncodeFSKData(UCHAR bytFrameType, UCHAR * bytDataToSend, int Length, unsigned char * bytEncodedBytes)
|
||
|
{
|
||
|
// Objective is to use this to use this to send all 4FSK data frames
|
||
|
// '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 inlcude all data (with FEC appended) for the entire frame. Each block will be identical in length.
|
||
|
// Ininitial implementation:
|
||
|
// intNum Car may be 1, 2, 4 or 8
|
||
|
// intBaud may be 50, 100
|
||
|
// strMod is 4FSK)
|
||
|
// bytDataToSend must be equal to or less than max data supported by the frame or a exception will be logged and an empty array returned
|
||
|
|
||
|
// First determine if bytDataToSend is compatible with the requested modulation mode.
|
||
|
|
||
|
int intNumCar, intBaud, intDataLen, intRSLen, bytDataToSendLengthPtr, intEncodedDataPtr;
|
||
|
|
||
|
int intCarDataCnt, intStartIndex;
|
||
|
BOOL blnOdd;
|
||
|
char strType[18];
|
||
|
char strMod[16];
|
||
|
BOOL blnFrameTypeOK;
|
||
|
UCHAR bytQualThresh;
|
||
|
int i;
|
||
|
UCHAR * bytToRS = &bytEncodedBytes[2];
|
||
|
|
||
|
blnFrameTypeOK = FrameInfo(bytFrameType, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytQualThresh, strType);
|
||
|
|
||
|
if (intDataLen == 0 || Length == 0 || !blnFrameTypeOK)
|
||
|
{
|
||
|
//Logs.Exception("[EncodeFSKFrameType] Failure to update parameters for frame type H" & Format(bytFrameType, "X") & " DataToSend Len=" & bytDataToSend.Length.ToString)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
// Generate the 2 bytes for the frame type data:
|
||
|
|
||
|
bytEncodedBytes[0] = bytFrameType;
|
||
|
bytEncodedBytes[1] = bytFrameType ^ bytSessionID;
|
||
|
|
||
|
// Dim bytToRS(intDataLen + 3 - 1) As Byte ' Data + Count + 2 byte CRC
|
||
|
|
||
|
bytDataToSendLengthPtr = 0;
|
||
|
intEncodedDataPtr = 2;
|
||
|
|
||
|
if (intBaud < 600 || intDataLen < 600)
|
||
|
{
|
||
|
// Now compute the RS frame for each carrier in sequence and move it to bytEncodedBytes
|
||
|
|
||
|
for (i = 0; i < intNumCar; i++) // across all carriers
|
||
|
{
|
||
|
intCarDataCnt = Length - bytDataToSendLengthPtr;
|
||
|
|
||
|
if (intCarDataCnt >= intDataLen) // why not > ??
|
||
|
{
|
||
|
// Won't all fit
|
||
|
|
||
|
bytToRS[0] = intDataLen;
|
||
|
intStartIndex = intEncodedDataPtr;
|
||
|
memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intDataLen);
|
||
|
bytDataToSendLengthPtr += intDataLen;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Last bit
|
||
|
|
||
|
bytToRS[0] = intCarDataCnt; // Could be 0 if insuffient data for # of carriers
|
||
|
|
||
|
if (intCarDataCnt > 0)
|
||
|
{
|
||
|
memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intCarDataCnt);
|
||
|
bytDataToSendLengthPtr += intCarDataCnt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GenCRC16FrameType(bytToRS, intDataLen + 1, bytFrameType); // calculate the CRC on the byte count + data bytes
|
||
|
|
||
|
RSEncode(bytToRS, bytToRS + intDataLen + 3, intDataLen + 3, intRSLen); // Generate the RS encoding ...now 14 bytes total
|
||
|
|
||
|
// Need: (2 bytes for Frame Type) +( Data + RS + 1 byte byteCount + 2 Byte CRC per carrier)
|
||
|
|
||
|
intEncodedDataPtr += intDataLen + 3 + intRSLen;
|
||
|
|
||
|
bytToRS += intDataLen + 3 + intRSLen;
|
||
|
}
|
||
|
return intEncodedDataPtr;
|
||
|
}
|
||
|
|
||
|
// special case for 600 baud 4FSK which has 600 byte data field sent as three sequencial (200 byte + 50 byte RS) groups
|
||
|
|
||
|
for (i = 0; i < 3; i++) // for three blocks of RS data
|
||
|
{
|
||
|
intCarDataCnt = Length - bytDataToSendLengthPtr;
|
||
|
|
||
|
if (intCarDataCnt >= intDataLen / 3) // why not > ??
|
||
|
{
|
||
|
// Won't all fit
|
||
|
|
||
|
bytToRS[0] = intDataLen / 3;
|
||
|
intStartIndex = intEncodedDataPtr;
|
||
|
memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intDataLen / 3);
|
||
|
bytDataToSendLengthPtr += intDataLen / 3;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Last bit
|
||
|
|
||
|
bytToRS[0] = intCarDataCnt; // Could be 0 if insuffient data for # of carriers
|
||
|
|
||
|
if (intCarDataCnt > 0)
|
||
|
{
|
||
|
memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intCarDataCnt);
|
||
|
bytDataToSendLengthPtr += intCarDataCnt;
|
||
|
}
|
||
|
}
|
||
|
GenCRC16FrameType(bytToRS, intDataLen / 3 + 1, bytFrameType); // calculate the CRC on the byte count + data bytes
|
||
|
|
||
|
RSEncode(bytToRS, bytToRS + intDataLen / 3 + 3, intDataLen / 3 + 3, intRSLen / 3); // Generate the RS encoding ...now 14 bytes total
|
||
|
intEncodedDataPtr += intDataLen / 3 + 3 + intRSLen / 3;
|
||
|
bytToRS += intDataLen / 3 + 3 + intRSLen / 3;
|
||
|
}
|
||
|
return intEncodedDataPtr;
|
||
|
}
|
||
|
|
||
|
|
||
|
void DrawRXFrame(int State, const char * Frame)
|
||
|
{
|
||
|
}
|
||
|
void DrawTXFrame(const char * Frame)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
int SendtoGUI(char Type, unsigned char * Msg, int Len)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|