qtsoundmodem/SMMain-HPLaptop.c

1382 lines
28 KiB
C
Raw Permalink Normal View History

2023-09-04 19:06:44 +01:00
/*
Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO
This file is part of QtSoundModem
QtSoundModem is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QtSoundModem is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QtSoundModem. If not, see http://www.gnu.org/licenses
*/
// UZ7HO Soundmodem Port by John Wiseman G8BPQ
#include "UZ7HOStuff.h"
#include "fftw3.h"
#include <time.h>
#include "ecc.h" // RS Constants
#include "hidapi.h"
#include <fcntl.h>
#include <errno.h>
BOOL KISSServ;
int KISSPort;
BOOL AGWServ;
int AGWPort;
int Number = 0; // Number waiting to be sent
int SoundIsPlaying = 0;
int UDPSoundIsPlaying = 0;
int Capturing = 0;
extern unsigned short buffer[2][1200];
extern int SoundMode;
extern int needRSID[4];
extern short * DMABuffer;
unsigned short * SendtoCard(unsigned short * buf, int n);
short * SoundInit();
void DoTX(int Chan);
void UDPPollReceivedSamples();
extern int SampleNo;
extern int pnt_change[5]; // Freq Changed Flag
// fftw library interface
fftwf_complex *in, *out;
fftwf_plan p;
#define N 2048
void initfft()
{
in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * N);
out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * N);
p = fftwf_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
}
void dofft(short * inp, float * outr, float * outi)
{
int i;
fftwf_complex * fft = in;
for (i = 0; i < N; i++)
{
fft[0][0] = inp[0] * 1.0f;
fft[0][1] = 0;
fft++;
inp++;
}
fftwf_execute(p);
fft = out;
for (i = 0; i < N; i++)
{
outr[0] = fft[0][0];
outi[0] = fft[0][1];
fft++;
outi++;
outr++;
}
}
void freefft()
{
fftwf_destroy_plan(p);
fftwf_free(in);
fftwf_free(out);
}
int nonGUIMode = 0;
void soundMain()
{
// non platform specific initialisation
platformInit();
// initialise fft library
RsCreate(); // RS code for MPSK
detector_init();
KISS_init();
ax25_init();
init_raduga(); // Set up waterfall colour table
initfft();
if (nonGUIMode)
{
Firstwaterfall = 0;
Secondwaterfall = 0;
}
OpenPTTPort();
}
void SampleSink(int LR, short Sample)
{
// This version is passed samples one at a time, as we don't have
// enough RAM in embedded systems to hold a full audio frame
// LR - 1 == Right Chan
#ifdef TEENSY
int work = Sample;
DMABuffer[Number++] = (work + 32768) >> 4; // 12 bit left justify
#else
if (SCO) // Single Channel Output - same to both L and R
{
DMABuffer[2 * Number] = Sample;
DMABuffer[1 + 2 * Number] = Sample;
}
else
{
if (LR) // Right
{
DMABuffer[1 + 2 * Number] = Sample;
DMABuffer[2 * Number] = 0;
}
else
{
DMABuffer[2 * Number] = Sample;
DMABuffer[1 + 2 * Number] = 0;
}
}
Number++;
#endif
if (Number >= SendSize)
{
// send this buffer to sound interface
DMABuffer = SendtoCard(DMABuffer, SendSize);
Number = 0;
}
// Last120[Last120Put++] = Sample;
// if (Last120Put == (intN + 1))
// Last120Put = 0;
SampleNo++;
}
void Flush()
{
SoundFlush(Number);
}
int ipow(int base, int exp)
{
int result = 1;
while (exp)
{
if (exp & 1)
result *= base;
exp >>= 1;
base *= base;
}
return result;
}
int NumberOfBitsNeeded(int PowerOfTwo)
{
int i;
for (i = 0; i <= 16; i++)
{
if ((PowerOfTwo & ipow(2, i)) != 0)
return i;
}
return 0;
}
int ReverseBits(int Index, int NumBits)
{
int i, Rev = 0;
for (i = 0; i < NumBits; i++)
{
Rev = (Rev * 2) | (Index & 1);
Index = Index / 2;
}
return Rev;
}
void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform)
{
float AngleNumerator;
unsigned char NumBits;
int i, j, K, n, BlockSize, BlockEnd;
float DeltaAngle, DeltaAr;
float Alpha, Beta;
float TR, TI, AR, AI;
if (InverseTransform)
AngleNumerator = -2.0f * M_PI;
else
AngleNumerator = 2.0f * M_PI;
NumBits = NumberOfBitsNeeded(NumSamples);
for (i = 0; i < NumSamples; i++)
{
j = ReverseBits(i, NumBits);
RealOut[j] = RealIn[i];
ImagOut[j] = 0.0f; // Not using i in ImageIn[i];
}
BlockEnd = 1;
BlockSize = 2;
while (BlockSize <= NumSamples)
{
DeltaAngle = AngleNumerator / BlockSize;
Alpha = sinf(0.5f * DeltaAngle);
Alpha = 2.0f * Alpha * Alpha;
Beta = sinf(DeltaAngle);
i = 0;
while (i < NumSamples)
{
AR = 1.0f;
AI = 0.0f;
j = i;
for (n = 0; n < BlockEnd; n++)
{
K = j + BlockEnd;
TR = AR * RealOut[K] - AI * ImagOut[K];
TI = AI * RealOut[K] + AR * ImagOut[K];
RealOut[K] = RealOut[j] - TR;
ImagOut[K] = ImagOut[j] - TI;
RealOut[j] = RealOut[j] + TR;
ImagOut[j] = ImagOut[j] + TI;
DeltaAr = Alpha * AR + Beta * AI;
AI = AI - (Alpha * AI - Beta * AR);
AR = AR - DeltaAr;
j = j + 1;
}
i = i + BlockSize;
}
BlockEnd = BlockSize;
BlockSize = BlockSize * 2;
}
if (InverseTransform)
{
// Normalize the resulting time samples...
for (i = 0; i < NumSamples; i++)
{
RealOut[i] = RealOut[i] / NumSamples;
ImagOut[i] = ImagOut[i] / NumSamples;
}
}
}
int LastBusyCheck = 0;
extern UCHAR CurrentLevel;
#ifdef PLOTSPECTRUM
float dblMagSpectrum[206];
float dblMaxScale = 0.0f;
extern UCHAR Pixels[4096];
extern UCHAR * pixelPointer;
#endif
extern int blnBusyStatus;
BusyDet = 0;
#define PLOTWATERFALL
int WaterfallActive = 1;
int SpectrumActive;
/*
void UpdateBusyDetector(short * bytNewSamples)
{
float dblReF[1024];
float dblImF[1024];
float dblMag[206];
#ifdef PLOTSPECTRUM
float dblMagMax = 0.0000000001f;
float dblMagMin = 10000000000.0f;
#endif
UCHAR Waterfall[256]; // Colour index values to send to GUI
int clrTLC = Lime; // Default Bandwidth lines on waterfall
static BOOL blnLastBusyStatus;
float dblMagAvg = 0;
int intTuneLineLow, intTuneLineHi, intDelta;
int i;
// 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];
#ifdef PLOTSPECTRUM
dblMagSpectrum[i] = 0.2f * dblMag[i] + 0.8f * dblMagSpectrum[i];
dblMagMax = max(dblMagMax, dblMagSpectrum[i]);
dblMagMin = min(dblMagMin, dblMagSpectrum[i]);
#endif
}
// LookforPacket(dblMag, dblMagAvg, 206, &dblReF[25], &dblImF[25]);
// packet_process_samples(bytNewSamples, 1200);
intDelta = roundf(500 / 2) + 50 / 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
if (!WaterfallActive && !SpectrumActive)
{
UCHAR Msg[2];
// Msg[0] = blnBusyStatus;
// SendtoGUI('B', Msg, 1);
}
}
// 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
if (!WaterfallActive && !SpectrumActive)
{
UCHAR Msg[2];
Msg[0] = blnBusyStatus;
// SendtoGUI('B', Msg, 1);
}
}
// stcStatus.Text = "FALSE"
// queTNCStatus.Enqueue(stcStatus)
// 'Debug.WriteLine("BUSY FALSE @ " & Format(DateTime.UtcNow, "HH:mm:ss"))
blnLastBusyStatus = blnBusyStatus;
}
if (BusyDet == 0)
clrTLC = Goldenrod;
else if (blnBusyStatus)
clrTLC = Fuchsia;
// At the moment we only get here what seaching for leader,
// but if we want to plot spectrum we should call
// it always
if (WaterfallActive)
{
#ifdef PLOTWATERFALL
dblMagAvg = log10f(dblMagAvg / 5000.0f);
for (i = 0; i < 206; i++)
{
// The following provides some AGC over the waterfall to compensate for avg input level.
float y1 = (0.25f + 2.5f / dblMagAvg) * log10f(0.01 + dblMag[i]);
int objColor;
// Set the pixel color based on the intensity (log) of the spectral line
if (y1 > 6.5)
objColor = Orange; // Strongest spectral line
else if (y1 > 6)
objColor = Khaki;
else if (y1 > 5.5)
objColor = Cyan;
else if (y1 > 5)
objColor = DeepSkyBlue;
else if (y1 > 4.5)
objColor = RoyalBlue;
else if (y1 > 4)
objColor = Navy;
else
objColor = Black;
if (i == 102)
Waterfall[i] = Tomato; // 1500 Hz line (center)
else if (i == intTuneLineLow || i == intTuneLineLow - 1 || i == intTuneLineHi || i == intTuneLineHi + 1)
Waterfall[i] = clrTLC;
else
Waterfall[i] = objColor; // ' Else plot the pixel as received
}
// Send Signal level and Busy indicator to save extra packets
Waterfall[206] = CurrentLevel;
Waterfall[207] = blnBusyStatus;
doWaterfall(Waterfall);
#endif
}
else if (SpectrumActive)
{
#ifdef PLOTSPECTRUM
// This performs an auto scaling mechansim with fast attack and slow release
if (dblMagMin / dblMagMax < 0.0001) // more than 10000:1 difference Max:Min
dblMaxScale = max(dblMagMax, dblMaxScale * 0.9f);
else
dblMaxScale = max(10000 * dblMagMin, dblMagMax);
// clearDisplay();
for (i = 0; i < 206; i++)
{
// The following provides some AGC over the spectrum to compensate for avg input level.
float y1 = -0.25f * (SpectrumHeight - 1) * log10f((max(dblMagSpectrum[i], dblMaxScale / 10000)) / dblMaxScale); // ' range should be 0 to bmpSpectrumHeight -1
int objColor = Yellow;
Waterfall[i] = round(y1);
}
// Send Signal level and Busy indicator to save extra packets
Waterfall[206] = CurrentLevel;
Waterfall[207] = blnBusyStatus;
Waterfall[208] = intTuneLineLow;
Waterfall[209] = intTuneLineHi;
// SendtoGUI('X', Waterfall, 210);
#endif
}
}
*/
extern short rawSamples[2400]; // Get Frame Type need 2400 and we may add 1200
int rawSamplesLength = 0;
extern int maxrawSamplesLength;
void ProcessNewSamples(short * Samples, int nSamples)
{
if (SoundIsPlaying == FALSE && UDPSoundIsPlaying == FALSE)
BufferFull(Samples, nSamples);
};
void doCalib(int Chan, int Act)
{
if (Chan == 0 && calib_mode[1])
return;
if (Chan == 1 && calib_mode[0])
return;
calib_mode[Chan] = Act;
if (Act == 0)
{
tx_status[Chan] = TX_SILENCE; // Stop TX
Flush();
RadioPTT(Chan, 0);
Debugprintf("Stop Calib");
}
}
int Freq_Change(int Chan, int Freq)
{
int low, high;
low = round(rx_shift[1] / 2 + RCVR[Chan] * rcvr_offset[Chan] + 1);
high = round(RX_Samplerate / 2 - (rx_shift[Chan] / 2 + RCVR[Chan] * rcvr_offset[Chan]));
if (Freq < low)
return rx_freq[Chan]; // Dont allow change
if (Freq > high)
return rx_freq[Chan]; // Dont allow change
rx_freq[Chan] = Freq;
tx_freq[Chan] = Freq;
pnt_change[Chan] = TRUE;
wf_pointer(soundChannel[Chan]);
return Freq;
}
void MainLoop()
{
// Called by background thread every 10 ms (maybe)
// Actually we may have two cards
// Original only allowed one channel per card.
// I think we should be able to run more, ie two or more
// modems on same soundcard channel
// So All the soundcard stuff will need to be generalised
if (UDPServ)
UDPPollReceivedSamples();
if (SoundMode == 3)
UDPPollReceivedSamples();
else
PollReceivedSamples();
if (modem_mode[0] == MODE_ARDOP)
{
chk_dcd1(0, 512);
}
DoTX(0);
DoTX(1);
DoTX(2);
DoTX(3);
}
int ARDOPSendToCard(int Chan, int Len)
{
// Send Next Block of samples to the soundcard
short * in = &ARDOPTXBuffer[Chan][ARDOPTXPtr[Chan]]; // Enough to hold whole frame of samples
short * out = DMABuffer;
int LR = modemtoSoundLR[Chan];
int i;
for (i = 0; i < Len; i++)
{
if (SCO) // Single Channel Output - same to both L and R
{
*out++ = *in;
*out++ = *in++;
}
else
{
if (LR) // Right
{
*out++ = 0;
*out++ = *in++;
}
else
{
*out++ = *in++;
*out++ = 0;
}
}
}
DMABuffer = SendtoCard(DMABuffer, Len);
ARDOPTXPtr[Chan] += Len;
// See if end of buffer
if (ARDOPTXPtr[Chan] > ARDOPTXLen[Chan])
return 1;
return 0;
}
void DoTX(int Chan)
{
// This kicks off a send sequence or calibrate
// printtick("dotx");
if (calib_mode[Chan])
{
// Maybe new calib or continuation
if (pnt_change[Chan])
{
make_core_BPF(Chan, rx_freq[Chan], bpf[Chan]);
make_core_TXBPF(Chan, tx_freq[Chan], txbpf[Chan]);
pnt_change[Chan] = FALSE;
}
// Note this may block in SendtoCard
modulator(Chan, tx_bufsize);
return;
}
// I think we have to detect NO_DATA here and drop PTT and return to SILENCE
if (tx_status[Chan] == TX_NO_DATA)
{
Flush();
Debugprintf("TX Complete");
RadioPTT(0, 0);
tx_status[Chan] = TX_SILENCE;
// We should now send any ackmode acks as the channel is now free for dest to reply
sendAckModeAcks(Chan);
}
if (tx_status[Chan] != TX_SILENCE)
{
// Continue the send
if (modem_mode[Chan] == MODE_ARDOP)
{
// if (SeeIfCardBusy())
// return 0;
if (ARDOPSendToCard(Chan, SendSize) == 1)
{
// End of TX
Number = 0;
Flush();
// See if more to send. If so, don't drop PTT
if (all_frame_buf[Chan].Count)
{
SoundIsPlaying = TRUE;
Number = 0;
Debugprintf("TX Continuing");
string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message
string * tx_data;
if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE
{
// Save copy then copy data up 3 bytes
Add(&KISS_acked[Chan], duplicateString(myTemp));
mydelete(myTemp, 0, 3);
myTemp->Length -= sizeof(void *);
}
else
{
// Just remove control
mydelete(myTemp, 0, 1);
}
tx_data = duplicateString(myTemp); // so can free original below
Delete(&all_frame_buf[Chan], 0); // This will invalidate temp
AGW_AX25_frame_analiz(Chan, FALSE, tx_data);
put_frame(Chan, tx_data, "", TRUE, FALSE);
PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan);
freeString(tx_data);
// Samples are now in DMABuffer = Send first block
ARDOPSendToCard(Chan, SendSize);
tx_status[Chan] = TX_FRAME;
return;
}
Debugprintf("TX Complete");
RadioPTT(0, 0);
tx_status[Chan] = TX_SILENCE;
// We should now send any ackmode acks as the channel is now free for dest to reply
}
return;
}
modulator(Chan, tx_bufsize);
return;
}
if (SoundIsPlaying || UDPSoundIsPlaying)
return;
// Not doing anything so see if we have anything new to send
// See if frequency has changed
if (pnt_change[Chan])
{
make_core_BPF(Chan, rx_freq[Chan], bpf[Chan]);
make_core_TXBPF(Chan, tx_freq[Chan], txbpf[Chan]);
pnt_change[Chan] = FALSE;
}
// See if we need an RSID
if (needRSID[Chan])
{
needRSID[Chan] = 0;
// Note this may block in SampleSink
Debugprintf("Sending RSID");
sendRSID(Chan, all_frame_buf[Chan].Count == 0);
return;
}
if (all_frame_buf[Chan].Count == 0)
return;
// Start a new send. modulator should handle TXD etc
Debugprintf("TX Start");
SampleNo = 0;
SoundIsPlaying = TRUE;
RadioPTT(Chan, 1);
Number = 0;
if (modem_mode[Chan] == MODE_ARDOP)
{
// I think ARDOP will have to generate a whole frame of samples
// then send them out a bit at a time to avoid stopping here for
// possibly 10's of seconds
// Can do this here as unlike normal ardop we don't need to run on Teensy
// to 12000 sample rate we need either 24K or 48K per second, depending on
// where we do the stereo mux.
// Slowest rate is 50 baud, so a 255 byte packet would take about a minute
// allowing for RS overhead. Not really realistic put perhaps should be possible.
// RAM isn't an issue so maybe allocate 2 MB.
// ?? Should we allow two ARDOP modems - could make sense if we can run sound
// card channels independently
string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message
string * tx_data;
if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE
{
// Save copy then copy data up 3 bytes
Add(&KISS_acked[Chan], duplicateString(myTemp));
mydelete(myTemp, 0, 3);
myTemp->Length -= sizeof(void *);
}
else
{
// Just remove control
mydelete(myTemp, 0, 1);
}
tx_data = duplicateString(myTemp); // so can free original below
Delete(&all_frame_buf[Chan], 0); // This will invalidate temp
AGW_AX25_frame_analiz(Chan, FALSE, tx_data);
put_frame(Chan, tx_data, "", TRUE, FALSE);
PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan);
freeString(tx_data);
// Samples are now in DMABuffer = Send first block
ARDOPSendToCard(Chan, SendSize);
tx_status[Chan] = TX_FRAME;
}
else
modulator(Chan, tx_bufsize);
return;
}
void stoptx(int snd_ch)
{
Flush();
Debugprintf("TX Complete");
RadioPTT(snd_ch, 0);
tx_status[snd_ch] = TX_SILENCE;
snd_status[snd_ch] = SND_IDLE;
}
void RX2TX(int snd_ch)
{
if (snd_status[snd_ch] == SND_IDLE)
{
DoTX(snd_ch);
}
}
// PTT Stuff
int hPTTDevice = 0;
char PTTPort[80] = ""; // Port for Hardware PTT - may be same as control port.
int PTTBAUD = 19200;
int PTTMode = PTTRTS; // PTT Control Flags.
char PTTOnString[128] = "";
char PTTOffString[128] = "";
UCHAR PTTOnCmd[64];
UCHAR PTTOnCmdLen = 0;
UCHAR PTTOffCmd[64];
UCHAR PTTOffCmdLen = 0;
int pttGPIOPin = 17; // Default
int pttGPIOPinR = 17;
BOOL pttGPIOInvert = FALSE;
BOOL useGPIO = FALSE;
BOOL gotGPIO = FALSE;
int HamLibPort = 4532;
char HamLibHost[32] = "192.168.1.14";
char CM108Addr[80] = "";
int VID = 0;
int PID = 0;
// CM108 Code
char * CM108Device = NULL;
void DecodeCM108(char * ptr)
{
// Called if Device Name or PTT = Param is CM108
#ifdef WIN32
// Next Param is VID and PID - 0xd8c:0x8 or Full device name
// On Windows device name is very long and difficult to find, so
// easier to use VID/PID, but allow device in case more than one needed
char * next;
long VID = 0, PID = 0;
char product[256] = "Unknown";
struct hid_device_info *devs, *cur_dev;
const char *path_to_open = NULL;
hid_device *handle = NULL;
if (strlen(ptr) > 16)
CM108Device = _strdup(ptr);
else
{
VID = strtol(ptr, &next, 0);
if (next)
PID = strtol(++next, &next, 0);
// Look for Device
devs = hid_enumerate((unsigned short)VID, (unsigned short)PID);
cur_dev = devs;
while (cur_dev)
{
if (cur_dev->product_string)
wcstombs(product, cur_dev->product_string, 255);
Debugprintf("HID Device %s VID %X PID %X", product, cur_dev->vendor_id, cur_dev->product_id);
if (cur_dev->vendor_id == VID && cur_dev->product_id == PID)
{
path_to_open = cur_dev->path;
break;
}
cur_dev = cur_dev->next;
}
if (path_to_open)
{
handle = hid_open_path(path_to_open);
if (handle)
{
hid_close(handle);
CM108Device = _strdup(path_to_open);
}
else
{
Debugprintf("Unable to open CM108 device %x %x", VID, PID);
}
}
else
Debugprintf("Couldn't find CM108 device %x %x", VID, PID);
hid_free_enumeration(devs);
}
#else
// Linux - Next Param HID Device, eg /dev/hidraw0
CM108Device = _strdup(ptr);
#endif
}
char * strlop(char * buf, char delim)
{
// Terminate buf at delim, and return rest of string
char * ptr = strchr(buf, delim);
if (ptr == NULL) return NULL;
*(ptr)++ = 0;
return ptr;
}
void OpenPTTPort()
{
PTTMode &= ~PTTCM108;
PTTMode &= ~PTTHAMLIB;
if (PTTPort[0] && strcmp(PTTPort, "None") != 0)
{
if (PTTMode == PTTCAT)
{
// convert config strings from Hex
char * ptr1 = PTTOffString;
UCHAR * ptr2 = PTTOffCmd;
char c;
int val;
while (c = *(ptr1++))
{
val = c - 0x30;
if (val > 15) val -= 7;
val <<= 4;
c = *(ptr1++) - 0x30;
if (c > 15) c -= 7;
val |= c;
*(ptr2++) = val;
}
PTTOffCmdLen = ptr2 - PTTOffCmd;
ptr1 = PTTOnString;
ptr2 = PTTOnCmd;
while (c = *(ptr1++))
{
val = c - 0x30;
if (val > 15) val -= 7;
val <<= 4;
c = *(ptr1++) - 0x30;
if (c > 15) c -= 7;
val |= c;
*(ptr2++) = val;
}
PTTOnCmdLen = ptr2 - PTTOnCmd;
}
if (stricmp(PTTPort, "GPIO") == 0)
{
// Initialise GPIO for PTT if available
#ifdef __ARM_ARCH
if (gpioInitialise() == 0)
{
printf("GPIO interface for PTT available\n");
gotGPIO = TRUE;
SetupGPIOPTT();
}
else
printf("Couldn't initialise GPIO interface for PTT\n");
#else
printf("GPIO interface for PTT not available on this platform\n");
#endif
}
else if (stricmp(PTTPort, "CM108") == 0)
{
DecodeCM108(CM108Addr);
PTTMode |= PTTCM108;
}
else if (stricmp(PTTPort, "HAMLIB") == 0)
{
PTTMode |= PTTHAMLIB;
HAMLIBSetPTT(0); // to open port
return;
}
else // Not GPIO
{
hPTTDevice = OpenCOMPort(PTTPort, PTTBAUD, FALSE, FALSE, FALSE, 0);
}
}
}
void ClosePTTPort()
{
CloseCOMPort(hPTTDevice);
hPTTDevice = 0;
}
void CM108_set_ptt(int PTTState)
{
char io[5];
hid_device *handle;
int n;
io[0] = 0;
io[1] = 0;
io[2] = 1 << (3 - 1);
io[3] = PTTState << (3 - 1);
io[4] = 0;
if (CM108Device == NULL)
return;
#ifdef WIN32
handle = hid_open_path(CM108Device);
if (!handle) {
printf("unable to open device\n");
return;
}
n = hid_write(handle, io, 5);
if (n < 0)
{
printf("Unable to write()\n");
printf("Error: %ls\n", hid_error(handle));
}
hid_close(handle);
#else
int fd;
fd = open(CM108Device, O_WRONLY);
if (fd == -1)
{
printf("Could not open %s for write, errno=%d\n", CM108Device, errno);
return;
}
io[0] = 0;
io[1] = 0;
io[2] = 1 << (3 - 1);
io[3] = PTTState << (3 - 1);
io[4] = 0;
n = write(fd, io, 5);
if (n != 5)
{
printf("Write to %s failed, n=%d, errno=%d\n", CM108Device, n, errno);
}
close(fd);
#endif
return;
}
void RadioPTT(int snd_ch, BOOL PTTState)
{
#ifdef __ARM_ARCH
if (useGPIO)
{
if (DualPTT && modemtoSoundLR[snd_ch] == 1)
gpioWrite(pttGPIOPinR, (pttGPIOInvert ? (1 - PTTState) : (PTTState)));
else
gpioWrite(pttGPIOPin, (pttGPIOInvert ? (1 - PTTState) : (PTTState)));
return;
}
#endif
if ((PTTMode & PTTCM108))
{
CM108_set_ptt(PTTState);
return;
}
if ((PTTMode & PTTHAMLIB))
{
HAMLIBSetPTT(PTTState);
return;
}
if (hPTTDevice == 0)
return;
if ((PTTMode & PTTCAT))
{
if (PTTState)
WriteCOMBlock(hPTTDevice, PTTOnCmd, PTTOnCmdLen);
else
WriteCOMBlock(hPTTDevice, PTTOffCmd, PTTOffCmdLen);
return;
}
if (DualPTT && modemtoSoundLR[snd_ch] == 1) // use DTR
{
if (PTTState)
COMSetDTR(hPTTDevice);
else
COMClearDTR(hPTTDevice);
}
else
{
if ((PTTMode & PTTRTS))
{
if (PTTState)
COMSetRTS(hPTTDevice);
else
COMClearRTS(hPTTDevice);
}
}
}
char ShortDT[] = "HH:MM:SS";
char * ShortDateTime()
{
struct tm * tm;
time_t NOW = time(NULL);
tm = gmtime(&NOW);
sprintf(ShortDT, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
return ShortDT;
}
// Reed Solomon Stuff
int NPAR = -1; // Number of Parity Bytes - used in RS Code
int xMaxErrors = 0;
int RSEncode(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;
xMaxErrors = 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[];
extern int Corrected[256];
extern int tt; // number of errors that can be corrected
extern int kk; // Info Symbols
extern BOOL blnErrorsCorrected;
BOOL RSDecode(UCHAR * bytRcv, int Length, int CheckLen, BOOL * blnRSOK)
{
// Using a modified version of Henry Minsky's code
//Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009
// Rick's Implementation processes the byte array in reverse. and also
// has the check bytes in the opposite order. I've modified the encoder
// to allow for this, but so far haven't found a way to mske the decoder
// work, so I have to reverse the data and checksum to decode G8BPQ Nov 2015
// returns TRUE if was ok or correction succeeded, FALSE if correction impossible
UCHAR intTemp[256]; // WOrk Area to pass to Decoder
int i;
UCHAR * ptr2 = intTemp;
UCHAR * ptr1 = &bytRcv[Length - CheckLen - 1]; // Last Byte of Data
int DataLen = Length - CheckLen;
int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes
*blnRSOK = FALSE;
if (Length > 255 || Length < (1 + CheckLen)) //Too long or too short
return FALSE;
if (NPAR != CheckLen) // Changed RS Len, so recalc constants;
{
NPAR = CheckLen;
xMaxErrors = NPAR / 2;
initialize_ecc();
}
// We reverse the data while zero padding it to speed things up
// We Need (Data Reversed) (Zero Padding) (Checkbytes Reversed)
// Reverse Data
for (i = 0; i < DataLen; i++)
{
*(ptr2++) = *(ptr1--);
}
// Clear padding
memset(ptr2, 0, PadLength);
ptr2 += PadLength;
// Error Bits
ptr1 = &bytRcv[Length - 1]; // End of check bytes
for (i = 0; i < CheckLen; i++)
{
*(ptr2++) = *(ptr1--);
}
decode_data(intTemp, 255);
// check if syndrome is all zeros
if (check_syndrome() == 0)
{
// RS ok, so no need to correct
*blnRSOK = TRUE;
return TRUE; // No Need to Correct
}
if (correct_errors_erasures(intTemp, 255, 0, 0) == 0) // Dont support erasures at the momnet
// Uncorrectable
return FALSE;
// Data has been corrected, so need to reverse again
ptr1 = &intTemp[DataLen - 1];
ptr2 = bytRcv; // Last Byte of Data
for (i = 0; i < DataLen; i++)
{
*(ptr2++) = *(ptr1--);
}
// ?? Do we need to return the check bytes ??
// Yes, so we can redo RS Check on supposedly connected frame
ptr1 = &intTemp[254]; // End of Check Bytes
for (i = 0; i < CheckLen; i++)
{
*(ptr2++) = *(ptr1--);
}
return TRUE;
}
extern TStringList detect_list[5];
extern TStringList detect_list_c[5];
void ProcessPktFrame(int snd_ch, UCHAR * Data, int frameLen)
{
string * pkt = newString();
stringAdd(pkt, Data, frameLen + 2); // 2 for crc (not actually there)
analiz_frame(snd_ch, pkt, "ARDOP", 1);
}