diff --git a/ALSASound.c b/ALSASound.c index d6739a4..3507e9f 100644 --- a/ALSASound.c +++ b/ALSASound.c @@ -93,6 +93,22 @@ extern BOOL UseKISS; // Enable Packet (KISS) interface extern short * DMABuffer; +#define MaxReceiveSize 2048 // Enough for 9600 +#define MaxSendSize 4096 + +short buffer[2][MaxSendSize * 2]; // Two Transfer/DMA buffers of 0.1 Sec (x2 for Stereo) +short inbuffer[MaxReceiveSize * 2]; // Input Transfer/ buffers of 0.1 Sec (x2 for Stereo) + + +extern short * DMABuffer; +extern int Number; + +int ReceiveSize = 512; +int SendSize = 1024; +int using48000 = 0; + +int SampleRate = 12000; + BOOL UseLeft = TRUE; BOOL UseRight = TRUE; @@ -123,8 +139,6 @@ void Sleep(int mS) // Windows and ALSA work with signed samples +- 32767 // STM32 and Teensy DAC uses unsigned 0 - 4095 -short buffer[2][1200 * 2]; // Two Transfer/DMA buffers of 0.1 Sec -short inbuffer[1200 * 2]; // Two Transfer/DMA buffers of 0.1 Sec BOOL Loopback = FALSE; //BOOL Loopback = TRUE; @@ -967,7 +981,7 @@ int SoundCardWrite(short * input, int nSamples) // Debugprintf("Tosend %d Avail %d", nSamples, (int)avail); - while (avail < nSamples || (MaxAvail - avail) > 12000) // Limit to 1 sec of audio + while (avail < nSamples || (MaxAvail - avail) > SampleRate) // Limit to 1 sec of audio { txSleep(10); avail = snd_pcm_avail_update(playhandle); @@ -1203,15 +1217,29 @@ void GetSoundDevices() int InitSound(BOOL Quiet) { + if (using48000) + { + ReceiveSize = 2048; + SendSize = 4096; // 100 mS for now + SampleRate = 48000; + } + else + { + ReceiveSize = 512; + SendSize = 1024; + SampleRate = 12000; + } + GetSoundDevices(); switch (SoundMode) { case 0: // ALSA - if (!OpenSoundCard(CaptureDevice, PlaybackDevice, 12000, 12000, Quiet)) + if (!OpenSoundCard(CaptureDevice, PlaybackDevice, SampleRate, SampleRate, Quiet)) return FALSE; + break; case 1: // OSS diff --git a/Config - Copy.cpp b/Config - Copy.cpp deleted file mode 100644 index 91b2c7d..0000000 --- a/Config - Copy.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/* -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 - -#include "UZ7HOStuff.h" - -extern "C" void get_exclude_list(char * line, TStringList * list); -extern "C" void get_exclude_frm(char * line, TStringList * list); - -extern "C" int SoundMode; -extern "C" int RX_SR; -extern "C" int TX_SR; -extern "C" int multiCore; -extern "C" char * Wisdom; - -extern "C" word MEMRecovery[5]; - -extern int MintoTray; -extern "C" int UDPClientPort; -extern "C" int UDPServerPort; -extern "C" int TXPort; - -extern char UDPHost[64]; - -extern char CWIDCall[128]; -extern int CWIDInterval; -extern int CWIDLeft; -extern int CWIDRight; -extern int CWIDType; - -extern "C" int RSID_SABM[4]; -extern "C" int RSID_UI[4]; -extern "C" int RSID_SetModem[4]; - - -QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - -// This makes geting settings for more channels easier - -char Prefix[16] = "AX25_A"; - -void GetPortSettings(int Chan); - -QVariant getAX25Param(const char * key, QVariant Default) -{ - char fullKey[64]; - - sprintf(fullKey, "%s/%s", Prefix, key); - return settings->value(fullKey, Default); -} - -void getAX25Params(int chan) -{ - Prefix[5] = chan + 'A'; - GetPortSettings(chan); -} - - -void GetPortSettings(int Chan) -{ - tx_hitoneraisedb[Chan] = getAX25Param("HiToneRaise", 0).toInt(); - - maxframe[Chan] = getAX25Param("Maxframe", 3).toInt(); - fracks[Chan] = getAX25Param("Retries", 15).toInt(); - frack_time[Chan] = getAX25Param("FrackTime", 5).toInt(); - - idletime[Chan] = getAX25Param("IdleTime", 180).toInt(); - slottime[Chan] = getAX25Param("SlotTime", 100).toInt(); - persist[Chan] = getAX25Param("Persist", 128).toInt(); - resptime[Chan] = getAX25Param("RespTime", 1500).toInt(); - TXFrmMode[Chan] = getAX25Param("TXFrmMode", 1).toInt(); - max_frame_collector[Chan] = getAX25Param("FrameCollector", 6).toInt(); - //exclude_callsigns[Chan]= getAX25Param("ExcludeCallsigns/"); - //exclude_APRS_frm[Chan]= getAX25Param("ExcludeAPRSFrmType/"); - KISS_opt[Chan] = getAX25Param("KISSOptimization", false).toInt();; - dyn_frack[Chan] = getAX25Param("DynamicFrack", false).toInt();; - recovery[Chan] = getAX25Param("BitRecovery", 0).toInt(); - NonAX25[Chan] = getAX25Param("NonAX25Frm", false).toInt();; - MEMRecovery[Chan]= getAX25Param("MEMRecovery", 200).toInt(); - IPOLL[Chan] = getAX25Param("IPOLL", 80).toInt(); - - strcpy(MyDigiCall[Chan], getAX25Param("MyDigiCall", "").toString().toUtf8()); - fx25_mode[Chan] = getAX25Param("FX25", FX25_MODE_RX).toInt(); - il2p_mode[Chan] = getAX25Param("IL2P", IL2P_MODE_NONE).toInt(); - RSID_UI[Chan] = getAX25Param("RSID_UI", 0).toInt(); - RSID_SABM[Chan] = getAX25Param("RSID_SABM", 0).toInt(); - RSID_SetModem[Chan] = getAX25Param("RSID_SetModem", 0).toInt(); -} - -void getSettings() -{ - int snd_ch; - - QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - settings->sync(); - - SoundMode = settings->value("Init/SoundMode", 0).toInt(); - UDPClientPort = settings->value("Init/UDPClientPort", 8888).toInt(); - UDPServerPort = settings->value("Init/UDPServerPort", 8884).toInt(); - TXPort = settings->value("Init/TXPort", UDPServerPort).toInt(); - - strcpy(UDPHost, settings->value("Init/UDPHost", "192.168.1.255").toString().toUtf8()); - UDPServ = settings->value("Init/UDPServer", FALSE).toBool(); - - RX_SR = settings->value("Init/RXSampleRate", 12000).toInt(); - TX_SR = settings->value("Init/TXSampleRate", 12000).toInt(); - - strcpy(CaptureDevice, settings->value("Init/SndRXDeviceName", "hw:1,0").toString().toUtf8()); - strcpy(PlaybackDevice, settings->value("Init/SndTXDeviceName", "hw:1,0").toString().toUtf8()); - - raduga = settings->value("Init/DispMode", DISP_RGB).toInt(); - - strcpy(PTTPort, settings->value("Init/PTT", "").toString().toUtf8()); - PTTMode = settings->value("Init/PTTMode", 19200).toInt(); - PTTBAUD = settings->value("Init/PTTBAUD", 19200).toInt(); - - strcpy(PTTOnString, settings->value("Init/PTTOnString", "").toString().toUtf8()); - strcpy(PTTOffString, settings->value("Init/PTTOffString", "").toString().toUtf8()); - - pttGPIOPin = settings->value("Init/pttGPIOPin", 17).toInt(); - pttGPIOPinR = settings->value("Init/pttGPIOPinR", 17).toInt(); - -#ifdef WIN32 - strcpy(CM108Addr, settings->value("Init/CM108Addr", "0xD8C:0x08").toString().toUtf8()); -#else - strcpy(CM108Addr, settings->value("Init/CM108Addr", "/dev/hidraw0").toString().toUtf8()); -#endif - - HamLibPort = settings->value("Init/HamLibPort", 4532).toInt(); - strcpy(HamLibHost, settings->value("Init/HamLibHost", "127.0.0.1").toString().toUtf8()); - - DualPTT = settings->value("Init/DualPTT", 1).toInt(); - TX_rotate = settings->value("Init/TXRotate", 0).toInt(); - - multiCore = settings->value("Init/multiCore", 0).toInt(); - Wisdom = strdup(settings->value("Init/Wisdom", "").toString().toUtf8()); - - - - rx_freq[0] = settings->value("Modem/RXFreq1", 1700).toInt(); - rx_freq[1] = settings->value("Modem/RXFreq2", 1700).toInt(); - rx_freq[2] = settings->value("Modem/RXFreq3", 1700).toInt(); - rx_freq[3] = settings->value("Modem/RXFreq4", 1700).toInt(); - - rcvr_offset[0] = settings->value("Modem/RcvrShift1", 30).toInt(); - rcvr_offset[1] = settings->value("Modem/RcvrShift2", 30).toInt(); - rcvr_offset[2] = settings->value("Modem/RcvrShift3", 30).toInt(); - rcvr_offset[3] = settings->value("Modem/RcvrShift4", 30).toInt(); - speed[0] = settings->value("Modem/ModemType1", SPEED_1200).toInt(); - speed[1] = settings->value("Modem/ModemType2", SPEED_1200).toInt(); - speed[2] = settings->value("Modem/ModemType3", SPEED_1200).toInt(); - speed[3] = settings->value("Modem/ModemType4", SPEED_1200).toInt(); - - RCVR[0] = settings->value("Modem/NRRcvrPairs1", 0).toInt();; - RCVR[1] = settings->value("Modem/NRRcvrPairs2", 0).toInt();; - RCVR[2] = settings->value("Modem/NRRcvrPairs3", 0).toInt();; - RCVR[3] = settings->value("Modem/NRRcvrPairs4", 0).toInt();; - - soundChannel[0] = settings->value("Modem/soundChannel1", 1).toInt(); - soundChannel[1] = settings->value("Modem/soundChannel2", 0).toInt(); - soundChannel[2] = settings->value("Modem/soundChannel3", 0).toInt(); - soundChannel[3] = settings->value("Modem/soundChannel4", 0).toInt(); - - SCO = settings->value("Init/SCO", 0).toInt(); - - dcd_threshold = settings->value("Modem/DCDThreshold", 40).toInt(); - rxOffset = settings->value("Modem/rxOffset", 0).toInt(); - - AGWServ = settings->value("AGWHost/Server", TRUE).toBool(); - AGWPort = settings->value("AGWHost/Port", 8000).toInt(); - KISSServ = settings->value("KISS/Server", FALSE).toBool(); - KISSPort = settings->value("KISS/Port", 8105).toInt(); - - RX_Samplerate = RX_SR + RX_SR * 0.000001*RX_PPM; - TX_Samplerate = TX_SR + TX_SR * 0.000001*TX_PPM; - - emph_all[0] = settings->value("Modem/PreEmphasisAll1", FALSE).toBool(); - emph_all[1] = settings->value("Modem/PreEmphasisAll2", FALSE).toBool(); - emph_all[2] = settings->value("Modem/PreEmphasisAll3", FALSE).toBool(); - emph_all[3] = settings->value("Modem/PreEmphasisAll4", FALSE).toBool(); - - emph_db[0] = settings->value("Modem/PreEmphasisDB1", 0).toInt(); - emph_db[1] = settings->value("Modem/PreEmphasisDB2", 0).toInt(); - emph_db[2] = settings->value("Modem/PreEmphasisDB3", 0).toInt(); - emph_db[3] = settings->value("Modem/PreEmphasisDB4", 0).toInt(); - - Firstwaterfall = settings->value("Window/Waterfall1", TRUE).toInt(); - Secondwaterfall = settings->value("Window/Waterfall2", TRUE).toInt(); - - txdelay[0] = settings->value("Modem/TxDelay1", 250).toInt(); - txdelay[1] = settings->value("Modem/TxDelay2", 250).toInt(); - txdelay[2] = settings->value("Modem/TxDelay3", 250).toInt(); - txdelay[3] = settings->value("Modem/TxDelay4", 250).toInt(); - - strcpy(CWIDCall, settings->value("Modem/CWIDCall", "").toString().toUtf8().toUpper()); - CWIDInterval = settings->value("Modem/CWIDInterval", 0).toInt(); - CWIDLeft = settings->value("Modem/CWIDLeft", 0).toInt(); - CWIDRight = settings->value("Modem/CWIDRight", 0).toInt(); - CWIDType = settings->value("Modem/CWIDType", 1).toInt(); // on/off - - - getAX25Params(0); - getAX25Params(1); - getAX25Params(2); - getAX25Params(3); - - // Validate and process settings - - UsingLeft = 0; - UsingRight = 0; - UsingBothChannels = 0; - - for (int i = 0; i < 4; i++) - { - if (soundChannel[i] == LEFT) - { - UsingLeft = 1; - modemtoSoundLR[i] = 0; - } - else if (soundChannel[i] == RIGHT) - { - UsingRight = 1; - modemtoSoundLR[i] = 1; - } - } - - if (UsingLeft && UsingRight) - UsingBothChannels = 1; - - for (snd_ch = 0; snd_ch < 4; snd_ch++) - { - tx_hitoneraise[snd_ch] = powf(10.0f, -abs(tx_hitoneraisedb[snd_ch]) / 20.0f); - - if (IPOLL[snd_ch] < 0) - IPOLL[snd_ch] = 0; - else if (IPOLL[snd_ch] > 65535) - IPOLL[snd_ch] = 65535; - - if (MEMRecovery[snd_ch] < 1) - MEMRecovery[snd_ch] = 1; - - // if (MEMRecovery[snd_ch]> 65535) - // MEMRecovery[snd_ch]= 65535; - - /* - if resptime[snd_ch] < 0 then resptime[snd_ch]= 0; - if resptime[snd_ch] > 65535 then resptime[snd_ch]= 65535; - if persist[snd_ch] > 255 then persist[snd_ch]= 255; - if persist[snd_ch] < 32 then persist[snd_ch]= 32; - if fracks[snd_ch] < 1 then fracks[snd_ch]= 1; - if frack_time[snd_ch] < 1 then frack_time[snd_ch]= 1; - if idletime[snd_ch] < frack_time[snd_ch] then idletime[snd_ch]= 180; - */ - - if (emph_db[snd_ch] < 0 || emph_db[snd_ch] > nr_emph) - emph_db[snd_ch] = 0; - - if (max_frame_collector[snd_ch] > 6) max_frame_collector[snd_ch] = 6; - if (maxframe[snd_ch] == 0 || maxframe[snd_ch] > 7) maxframe[snd_ch] = 3; - if (qpsk_set[snd_ch].mode > 1) qpsk_set[snd_ch].mode = 0; - - } - - delete(settings); -} - -void SavePortSettings(int Chan); - -void saveAX25Param(const char * key, QVariant Value) -{ - char fullKey[64]; - - sprintf(fullKey, "%s/%s", Prefix, key); - - settings->setValue(fullKey, Value); -} - -void saveAX25Params(int chan) -{ - Prefix[5] = chan + 'A'; - SavePortSettings(chan); -} - -void SavePortSettings(int Chan) -{ - saveAX25Param("Retries", fracks[Chan]); - saveAX25Param("HiToneRaise", tx_hitoneraisedb[Chan]); - saveAX25Param("Maxframe",maxframe[Chan]); - saveAX25Param("Retries", fracks[Chan]); - saveAX25Param("FrackTime", frack_time[Chan]); - saveAX25Param("IdleTime", idletime[Chan]); - saveAX25Param("SlotTime", slottime[Chan]); - saveAX25Param("Persist", persist[Chan]); - saveAX25Param("RespTime", resptime[Chan]); - saveAX25Param("TXFrmMode", TXFrmMode[Chan]); - saveAX25Param("FrameCollector", max_frame_collector[Chan]); - saveAX25Param("ExcludeCallsigns", exclude_callsigns[Chan]); - saveAX25Param("ExcludeAPRSFrmType", exclude_APRS_frm[Chan]); - saveAX25Param("KISSOptimization", KISS_opt[Chan]); - saveAX25Param("DynamicFrack", dyn_frack[Chan]); - saveAX25Param("BitRecovery", recovery[Chan]); - saveAX25Param("NonAX25Frm", NonAX25[Chan]); - saveAX25Param("MEMRecovery", MEMRecovery[Chan]); - saveAX25Param("IPOLL", IPOLL[Chan]); - saveAX25Param("MyDigiCall", MyDigiCall[Chan]); - saveAX25Param("FX25", fx25_mode[Chan]); - saveAX25Param("IL2P", il2p_mode[Chan]); - saveAX25Param("RSID_UI", RSID_UI[Chan]); - saveAX25Param("RSID_SABM", RSID_SABM[Chan]); - saveAX25Param("RSID_SetModem", RSID_SetModem[Chan]); -} - -void saveSettings() -{ - QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - - settings->setValue("Init/SoundMode", SoundMode); - settings->setValue("Init/UDPClientPort", UDPClientPort); - settings->setValue("Init/UDPServerPort", UDPServerPort); - settings->setValue("Init/TXPort", TXPort); - - settings->setValue("Init/UDPServer", UDPServ); - settings->setValue("Init/UDPHost", UDPHost); - - - settings->setValue("Init/TXSampleRate", TX_SR); - settings->setValue("Init/RXSampleRate", RX_SR); - - settings->setValue("Init/SndRXDeviceName", CaptureDevice); - settings->setValue("Init/SndTXDeviceName", PlaybackDevice); - - settings->setValue("Init/SCO", SCO); - settings->setValue("Init/DualPTT", DualPTT); - settings->setValue("Init/TXRotate", TX_rotate); - - settings->setValue("Init/DispMode", raduga); - - settings->setValue("Init/PTT", PTTPort); - settings->setValue("Init/PTTBAUD", PTTBAUD); - settings->setValue("Init/PTTMode", PTTMode); - - settings->setValue("Init/PTTOffString", PTTOffString); - settings->setValue("Init/PTTOnString", PTTOnString); - - settings->setValue("Init/pttGPIOPin", pttGPIOPin); - settings->setValue("Init/pttGPIOPinR", pttGPIOPinR); - - settings->setValue("Init/CM108Addr", CM108Addr); - settings->setValue("Init/HamLibPort", HamLibPort); - settings->setValue("Init/HamLibHost", HamLibHost); - settings->setValue("Init/MinimizetoTray", MintoTray); - - settings->setValue("Init/multiCore", multiCore); - - settings->setValue("Init/Wisdom", Wisdom); - - // Don't save freq on close as it could be offset by multiple decoders - - settings->setValue("Modem/NRRcvrPairs1", RCVR[0]); - settings->setValue("Modem/NRRcvrPairs2", RCVR[1]); - settings->setValue("Modem/NRRcvrPairs3", RCVR[2]); - settings->setValue("Modem/NRRcvrPairs4", RCVR[3]); - - settings->setValue("Modem/RcvrShift1", rcvr_offset[0]); - settings->setValue("Modem/RcvrShift2", rcvr_offset[1]); - settings->setValue("Modem/RcvrShift3", rcvr_offset[2]); - settings->setValue("Modem/RcvrShift4", rcvr_offset[3]); - - settings->setValue("Modem/ModemType1", speed[0]); - settings->setValue("Modem/ModemType2", speed[1]); - settings->setValue("Modem/ModemType3", speed[2]); - settings->setValue("Modem/ModemType4", speed[3]); - - settings->setValue("Modem/soundChannel1", soundChannel[0]); - settings->setValue("Modem/soundChannel2", soundChannel[1]); - settings->setValue("Modem/soundChannel3", soundChannel[2]); - settings->setValue("Modem/soundChannel4", soundChannel[3]); - - settings->setValue("Modem/DCDThreshold", dcd_threshold); - settings->setValue("Modem/rxOffset", rxOffset); - - settings->setValue("AGWHost/Server", AGWServ); - settings->setValue("AGWHost/Port", AGWPort); - settings->setValue("KISS/Server", KISSServ); - settings->setValue("KISS/Port", KISSPort); - - settings->setValue("Modem/PreEmphasisAll1", emph_all[0]); - settings->setValue("Modem/PreEmphasisAll2", emph_all[1]); - settings->setValue("Modem/PreEmphasisAll3", emph_all[2]); - settings->setValue("Modem/PreEmphasisAll4", emph_all[3]); - - settings->setValue("Modem/PreEmphasisDB1", emph_db[0]); - settings->setValue("Modem/PreEmphasisDB2", emph_db[1]); - settings->setValue("Modem/PreEmphasisDB3", emph_db[2]); - settings->setValue("Modem/PreEmphasisDB4", emph_db[3]); - - settings->setValue("Window/Waterfall1", Firstwaterfall); - settings->setValue("Window/Waterfall2", Secondwaterfall); - - settings->setValue("Modem/TxDelay1", txdelay[0]); - settings->setValue("Modem/TxDelay2", txdelay[1]); - settings->setValue("Modem/TxDelay3", txdelay[2]); - settings->setValue("Modem/TxDelay4", txdelay[3]); - - settings->setValue("Modem/TxTail1", txtail[0]); - settings->setValue("Modem/TxTail2", txtail[1]); - settings->setValue("Modem/TxTail3", txtail[2]); - settings->setValue("Modem/TxTail4", txtail[3]); - - settings->setValue("Modem/CWIDCall", CWIDCall); - settings->setValue("Modem/CWIDInterval", CWIDInterval); - settings->setValue("Modem/CWIDLeft", CWIDLeft); - settings->setValue("Modem/CWIDRight", CWIDRight); - settings->setValue("Modem/CWIDType", CWIDType); - - saveAX25Params(0); - saveAX25Params(1); - saveAX25Params(2); - saveAX25Params(3); - - settings->sync(); - - delete(settings); -} diff --git a/Config.cpp b/Config.cpp index 4392c2f..bb5ebd8 100644 --- a/Config.cpp +++ b/Config.cpp @@ -21,6 +21,7 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses // UZ7HO Soundmodem Port by John Wiseman G8BPQ #include +#include #include "UZ7HOStuff.h" @@ -43,8 +44,11 @@ extern "C" int UDPServerPort; extern "C" int TXPort; extern char UDPHost[64]; +extern QDialog * constellationDialog; +extern QRect PSKRect; extern char CWIDCall[128]; +extern "C" char CWIDMark[32]; extern int CWIDInterval; extern int CWIDLeft; extern int CWIDRight; @@ -120,6 +124,8 @@ void getSettings() QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); settings->sync(); + PSKRect = settings->value("PSKWindow").toRect(); + SoundMode = settings->value("Init/SoundMode", 0).toInt(); UDPClientPort = settings->value("Init/UDPClientPort", 8888).toInt(); UDPServerPort = settings->value("Init/UDPServerPort", 8884).toInt(); @@ -219,7 +225,13 @@ void getSettings() txdelay[2] = settings->value("Modem/TxDelay3", 250).toInt(); txdelay[3] = settings->value("Modem/TxDelay4", 250).toInt(); + txtail[0] = settings->value("Modem/TxTail1", 50).toInt(); + txtail[1] = settings->value("Modem/TxTail2", 50).toInt(); + txtail[2] = settings->value("Modem/TxTail3", 50).toInt(); + txtail[3] = settings->value("Modem/TxTail4", 50).toInt(); + strcpy(CWIDCall, settings->value("Modem/CWIDCall", "").toString().toUtf8().toUpper()); + strcpy(CWIDMark, settings->value("Modem/CWIDMark", "").toString().toUtf8().toUpper()); CWIDInterval = settings->value("Modem/CWIDInterval", 0).toInt(); CWIDLeft = settings->value("Modem/CWIDLeft", 0).toInt(); CWIDRight = settings->value("Modem/CWIDRight", 0).toInt(); @@ -341,6 +353,7 @@ void saveSettings() { QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + settings->setValue("PSKWindow", constellationDialog->geometry()); settings->setValue("Init/SoundMode", SoundMode); settings->setValue("Init/UDPClientPort", UDPClientPort); settings->setValue("Init/UDPServerPort", UDPServerPort); @@ -436,6 +449,7 @@ void saveSettings() settings->setValue("Modem/TxTail4", txtail[3]); settings->setValue("Modem/CWIDCall", CWIDCall); + settings->setValue("Modem/CWIDMark", CWIDMark); settings->setValue("Modem/CWIDInterval", CWIDInterval); settings->setValue("Modem/CWIDLeft", CWIDLeft); settings->setValue("Modem/CWIDRight", CWIDRight); diff --git a/Config.cpp.bak b/Config.cpp.bak deleted file mode 100644 index 0ca299e..0000000 --- a/Config.cpp.bak +++ /dev/null @@ -1,439 +0,0 @@ -/* -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 - -#include "UZ7HOStuff.h" - -extern "C" void get_exclude_list(char * line, TStringList * list); -extern "C" void get_exclude_frm(char * line, TStringList * list); - -extern "C" int SoundMode; -extern "C" int RX_SR; -extern "C" int TX_SR; -extern "C" int multiCore; - -extern "C" word MEMRecovery[5]; - -extern int MintoTray; -extern "C" int UDPClientPort; -extern "C" int UDPServerPort; -extern "C" int TXPort; - -extern char UDPHost[64]; - -extern char CWIDCall[128]; -extern int CWIDInterval; -extern int CWIDLeft; -extern int CWIDRight; -extern int CWIDType; - -extern "C" int RSID_SABM[4]; -extern "C" int RSID_UI[4]; -extern "C" int RSID_SetModem[4]; - - -QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - -// This makes geting settings for more channels easier - -char Prefix[16] = "AX25_A"; - -void GetPortSettings(int Chan); - -QVariant getAX25Param(const char * key, QVariant Default) -{ - char fullKey[64]; - - sprintf(fullKey, "%s/%s", Prefix, key); - return settings->value(fullKey, Default); -} - -void getAX25Params(int chan) -{ - Prefix[5] = chan + 'A'; - GetPortSettings(chan); -} - - -void GetPortSettings(int Chan) -{ - tx_hitoneraisedb[Chan] = getAX25Param("HiToneRaise", 0).toInt(); - - maxframe[Chan] = getAX25Param("Maxframe", 3).toInt(); - fracks[Chan] = getAX25Param("Retries", 15).toInt(); - frack_time[Chan] = getAX25Param("FrackTime", 5).toInt(); - - idletime[Chan] = getAX25Param("IdleTime", 180).toInt(); - slottime[Chan] = getAX25Param("SlotTime", 100).toInt(); - persist[Chan] = getAX25Param("Persist", 128).toInt(); - resptime[Chan] = getAX25Param("RespTime", 1500).toInt(); - TXFrmMode[Chan] = getAX25Param("TXFrmMode", 1).toInt(); - max_frame_collector[Chan] = getAX25Param("FrameCollector", 6).toInt(); - //exclude_callsigns[Chan]= getAX25Param("ExcludeCallsigns/"); - //exclude_APRS_frm[Chan]= getAX25Param("ExcludeAPRSFrmType/"); - KISS_opt[Chan] = getAX25Param("KISSOptimization", false).toInt();; - dyn_frack[Chan] = getAX25Param("DynamicFrack", false).toInt();; - recovery[Chan] = getAX25Param("BitRecovery", 0).toInt(); - NonAX25[Chan] = getAX25Param("NonAX25Frm", false).toInt();; - MEMRecovery[Chan]= getAX25Param("MEMRecovery", 200).toInt(); - IPOLL[Chan] = getAX25Param("IPOLL", 80).toInt(); - - strcpy(MyDigiCall[Chan], getAX25Param("MyDigiCall", "").toString().toUtf8()); - fx25_mode[Chan] = getAX25Param("FX25", FX25_MODE_RX).toInt(); - il2p_mode[Chan] = getAX25Param("IL2P", IL2P_MODE_NONE).toInt(); - RSID_UI[Chan] = getAX25Param("RSID_UI", 0).toInt(); - RSID_SABM[Chan] = getAX25Param("RSID_SABM", 0).toInt(); - RSID_SetModem[Chan] = getAX25Param("RSID_SetModem", 0).toInt(); -} - -void getSettings() -{ - int snd_ch; - - QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - settings->sync(); - - SoundMode = settings->value("Init/SoundMode", 0).toInt(); - UDPClientPort = settings->value("Init/UDPClientPort", 8888).toInt(); - UDPServerPort = settings->value("Init/UDPServerPort", 8884).toInt(); - TXPort = settings->value("Init/TXPort", UDPServerPort).toInt(); - - strcpy(UDPHost, settings->value("Init/UDPHost", "192.168.1.255").toString().toUtf8()); - UDPServ = settings->value("Init/UDPServer", FALSE).toBool(); - - RX_SR = settings->value("Init/RXSampleRate", 12000).toInt(); - TX_SR = settings->value("Init/TXSampleRate", 12000).toInt(); - - strcpy(CaptureDevice, settings->value("Init/SndRXDeviceName", "hw:1,0").toString().toUtf8()); - strcpy(PlaybackDevice, settings->value("Init/SndTXDeviceName", "hw:1,0").toString().toUtf8()); - - raduga = settings->value("Init/DispMode", DISP_RGB).toInt(); - - strcpy(PTTPort, settings->value("Init/PTT", "").toString().toUtf8()); - PTTMode = settings->value("Init/PTTMode", 19200).toInt(); - PTTBAUD = settings->value("Init/PTTBAUD", 19200).toInt(); - - strcpy(PTTOnString, settings->value("Init/PTTOnString", "").toString().toUtf8()); - strcpy(PTTOffString, settings->value("Init/PTTOffString", "").toString().toUtf8()); - - pttGPIOPin = settings->value("Init/pttGPIOPin", 17).toInt(); - pttGPIOPinR = settings->value("Init/pttGPIOPinR", 17).toInt(); - -#ifdef WIN32 - strcpy(CM108Addr, settings->value("Init/CM108Addr", "0xD8C:0x08").toString().toUtf8()); -#else - strcpy(CM108Addr, settings->value("Init/CM108Addr", "/dev/hidraw0").toString().toUtf8()); -#endif - - HamLibPort = settings->value("Init/HamLibPort", 4532).toInt(); - strcpy(HamLibHost, settings->value("Init/HamLibHost", "127.0.0.1").toString().toUtf8()); - - DualPTT = settings->value("Init/DualPTT", 1).toInt(); - TX_rotate = settings->value("Init/TXRotate", 0).toInt(); - - multiCore = settings->value("Init/multiCore", 0).toInt(); - MintoTray = settings->value("Init/MinimizetoTray", 1).toInt(); - - rx_freq[0] = settings->value("Modem/RXFreq1", 1700).toInt(); - rx_freq[1] = settings->value("Modem/RXFreq2", 1700).toInt(); - rx_freq[2] = settings->value("Modem/RXFreq3", 1700).toInt(); - rx_freq[3] = settings->value("Modem/RXFreq4", 1700).toInt(); - - rcvr_offset[0] = settings->value("Modem/RcvrShift1", 30).toInt(); - rcvr_offset[1] = settings->value("Modem/RcvrShift2", 30).toInt(); - rcvr_offset[2] = settings->value("Modem/RcvrShift3", 30).toInt(); - rcvr_offset[3] = settings->value("Modem/RcvrShift4", 30).toInt(); - speed[0] = settings->value("Modem/ModemType1", SPEED_1200).toInt(); - speed[1] = settings->value("Modem/ModemType2", SPEED_1200).toInt(); - speed[2] = settings->value("Modem/ModemType3", SPEED_1200).toInt(); - speed[3] = settings->value("Modem/ModemType4", SPEED_1200).toInt(); - - RCVR[0] = settings->value("Modem/NRRcvrPairs1", 0).toInt();; - RCVR[1] = settings->value("Modem/NRRcvrPairs2", 0).toInt();; - RCVR[2] = settings->value("Modem/NRRcvrPairs3", 0).toInt();; - RCVR[3] = settings->value("Modem/NRRcvrPairs4", 0).toInt();; - - soundChannel[0] = settings->value("Modem/soundChannel1", 1).toInt(); - soundChannel[1] = settings->value("Modem/soundChannel2", 0).toInt(); - soundChannel[2] = settings->value("Modem/soundChannel3", 0).toInt(); - soundChannel[3] = settings->value("Modem/soundChannel4", 0).toInt(); - - SCO = settings->value("Init/SCO", 0).toInt(); - - dcd_threshold = settings->value("Modem/DCDThreshold", 40).toInt(); - rxOffset = settings->value("Modem/rxOffset", 0).toInt(); - - AGWServ = settings->value("AGWHost/Server", TRUE).toBool(); - AGWPort = settings->value("AGWHost/Port", 8000).toInt(); - KISSServ = settings->value("KISS/Server", FALSE).toBool(); - KISSPort = settings->value("KISS/Port", 8105).toInt(); - - RX_Samplerate = RX_SR + RX_SR * 0.000001*RX_PPM; - TX_Samplerate = TX_SR + TX_SR * 0.000001*TX_PPM; - - emph_all[0] = settings->value("Modem/PreEmphasisAll1", FALSE).toBool(); - emph_all[1] = settings->value("Modem/PreEmphasisAll2", FALSE).toBool(); - emph_all[2] = settings->value("Modem/PreEmphasisAll3", FALSE).toBool(); - emph_all[3] = settings->value("Modem/PreEmphasisAll4", FALSE).toBool(); - - emph_db[0] = settings->value("Modem/PreEmphasisDB1", 0).toInt(); - emph_db[1] = settings->value("Modem/PreEmphasisDB2", 0).toInt(); - emph_db[2] = settings->value("Modem/PreEmphasisDB3", 0).toInt(); - emph_db[3] = settings->value("Modem/PreEmphasisDB4", 0).toInt(); - - Firstwaterfall = settings->value("Window/Waterfall1", TRUE).toInt(); - Secondwaterfall = settings->value("Window/Waterfall2", TRUE).toInt(); - - txdelay[0] = settings->value("Modem/TxDelay1", 250).toInt(); - txdelay[1] = settings->value("Modem/TxDelay2", 250).toInt(); - txdelay[2] = settings->value("Modem/TxDelay3", 250).toInt(); - txdelay[3] = settings->value("Modem/TxDelay4", 250).toInt(); - - strcpy(CWIDCall, settings->value("Modem/CWIDCall", "").toString().toUtf8().toUpper()); - CWIDInterval = settings->value("Modem/CWIDInterval", 0).toInt(); - CWIDLeft = settings->value("Modem/CWIDLeft", 0).toInt(); - CWIDRight = settings->value("Modem/CWIDRight", 0).toInt(); - CWIDType = settings->value("Modem/CWIDType", 1).toInt(); // on/off - - - getAX25Params(0); - getAX25Params(1); - getAX25Params(2); - getAX25Params(3); - - // Validate and process settings - - UsingLeft = 0; - UsingRight = 0; - UsingBothChannels = 0; - - for (int i = 0; i < 4; i++) - { - if (soundChannel[i] == LEFT) - { - UsingLeft = 1; - modemtoSoundLR[i] = 0; - } - else if (soundChannel[i] == RIGHT) - { - UsingRight = 1; - modemtoSoundLR[i] = 1; - } - } - - if (UsingLeft && UsingRight) - UsingBothChannels = 1; - - for (snd_ch = 0; snd_ch < 4; snd_ch++) - { - tx_hitoneraise[snd_ch] = powf(10.0f, -abs(tx_hitoneraisedb[snd_ch]) / 20.0f); - - if (IPOLL[snd_ch] < 0) - IPOLL[snd_ch] = 0; - else if (IPOLL[snd_ch] > 65535) - IPOLL[snd_ch] = 65535; - - if (MEMRecovery[snd_ch] < 1) - MEMRecovery[snd_ch] = 1; - - // if (MEMRecovery[snd_ch]> 65535) - // MEMRecovery[snd_ch]= 65535; - - /* - if resptime[snd_ch] < 0 then resptime[snd_ch]= 0; - if resptime[snd_ch] > 65535 then resptime[snd_ch]= 65535; - if persist[snd_ch] > 255 then persist[snd_ch]= 255; - if persist[snd_ch] < 32 then persist[snd_ch]= 32; - if fracks[snd_ch] < 1 then fracks[snd_ch]= 1; - if frack_time[snd_ch] < 1 then frack_time[snd_ch]= 1; - if idletime[snd_ch] < frack_time[snd_ch] then idletime[snd_ch]= 180; - */ - - if (emph_db[snd_ch] < 0 || emph_db[snd_ch] > nr_emph) - emph_db[snd_ch] = 0; - - if (max_frame_collector[snd_ch] > 6) max_frame_collector[snd_ch] = 6; - if (maxframe[snd_ch] == 0 || maxframe[snd_ch] > 7) maxframe[snd_ch] = 3; - if (qpsk_set[snd_ch].mode > 1) qpsk_set[snd_ch].mode = 0; - - } - - delete(settings); -} - -void SavePortSettings(int Chan); - -void saveAX25Param(const char * key, QVariant Value) -{ - char fullKey[64]; - - sprintf(fullKey, "%s/%s", Prefix, key); - - settings->setValue(fullKey, Value); -} - -void saveAX25Params(int chan) -{ - Prefix[5] = chan + 'A'; - SavePortSettings(chan); -} - -void SavePortSettings(int Chan) -{ - saveAX25Param("Retries", fracks[Chan]); - saveAX25Param("HiToneRaise", tx_hitoneraisedb[Chan]); - saveAX25Param("Maxframe",maxframe[Chan]); - saveAX25Param("Retries", fracks[Chan]); - saveAX25Param("FrackTime", frack_time[Chan]); - saveAX25Param("IdleTime", idletime[Chan]); - saveAX25Param("SlotTime", slottime[Chan]); - saveAX25Param("Persist", persist[Chan]); - saveAX25Param("RespTime", resptime[Chan]); - saveAX25Param("TXFrmMode", TXFrmMode[Chan]); - saveAX25Param("FrameCollector", max_frame_collector[Chan]); - saveAX25Param("ExcludeCallsigns", exclude_callsigns[Chan]); - saveAX25Param("ExcludeAPRSFrmType", exclude_APRS_frm[Chan]); - saveAX25Param("KISSOptimization", KISS_opt[Chan]); - saveAX25Param("DynamicFrack", dyn_frack[Chan]); - saveAX25Param("BitRecovery", recovery[Chan]); - saveAX25Param("NonAX25Frm", NonAX25[Chan]); - saveAX25Param("MEMRecovery", MEMRecovery[Chan]); - saveAX25Param("IPOLL", IPOLL[Chan]); - saveAX25Param("MyDigiCall", MyDigiCall[Chan]); - saveAX25Param("FX25", fx25_mode[Chan]); - saveAX25Param("IL2P", il2p_mode[Chan]); - saveAX25Param("RSID_UI", RSID_UI[Chan]); - saveAX25Param("RSID_SABM", RSID_SABM[Chan]); - saveAX25Param("RSID_SetModem", RSID_SetModem[Chan]); -} - -void saveSettings() -{ - QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - - settings->setValue("Init/SoundMode", SoundMode); - settings->setValue("Init/UDPClientPort", UDPClientPort); - settings->setValue("Init/UDPServerPort", UDPServerPort); - settings->setValue("Init/TXPort", TXPort); - - settings->setValue("Init/UDPServer", UDPServ); - settings->setValue("Init/UDPHost", UDPHost); - - - settings->setValue("Init/TXSampleRate", TX_SR); - settings->setValue("Init/RXSampleRate", RX_SR); - - settings->setValue("Init/SndRXDeviceName", CaptureDevice); - settings->setValue("Init/SndTXDeviceName", PlaybackDevice); - - settings->setValue("Init/SCO", SCO); - settings->setValue("Init/DualPTT", DualPTT); - settings->setValue("Init/TXRotate", TX_rotate); - - settings->setValue("Init/DispMode", raduga); - - settings->setValue("Init/PTT", PTTPort); - settings->setValue("Init/PTTBAUD", PTTBAUD); - settings->setValue("Init/PTTMode", PTTMode); - - settings->setValue("Init/PTTOffString", PTTOffString); - settings->setValue("Init/PTTOnString", PTTOnString); - - settings->setValue("Init/pttGPIOPin", pttGPIOPin); - settings->setValue("Init/pttGPIOPinR", pttGPIOPinR); - - settings->setValue("Init/CM108Addr", CM108Addr); - settings->setValue("Init/HamLibPort", HamLibPort); - settings->setValue("Init/HamLibHost", HamLibHost); - settings->setValue("Init/MinimizetoTray", MintoTray); - - settings->setValue("Init/multiCore", multiCore); - - // Don't save freq on close as it could be offset by multiple decoders - - settings->setValue("Modem/NRRcvrPairs1", RCVR[0]); - settings->setValue("Modem/NRRcvrPairs2", RCVR[1]); - settings->setValue("Modem/NRRcvrPairs3", RCVR[2]); - settings->setValue("Modem/NRRcvrPairs4", RCVR[3]); - - settings->setValue("Modem/RcvrShift1", rcvr_offset[0]); - settings->setValue("Modem/RcvrShift2", rcvr_offset[1]); - settings->setValue("Modem/RcvrShift3", rcvr_offset[2]); - settings->setValue("Modem/RcvrShift4", rcvr_offset[3]); - - settings->setValue("Modem/ModemType1", speed[0]); - settings->setValue("Modem/ModemType2", speed[1]); - settings->setValue("Modem/ModemType3", speed[2]); - settings->setValue("Modem/ModemType4", speed[3]); - - settings->setValue("Modem/soundChannel1", soundChannel[0]); - settings->setValue("Modem/soundChannel2", soundChannel[1]); - settings->setValue("Modem/soundChannel3", soundChannel[2]); - settings->setValue("Modem/soundChannel4", soundChannel[3]); - - settings->setValue("Modem/DCDThreshold", dcd_threshold); - settings->setValue("Modem/rxOffset", rxOffset); - - settings->setValue("AGWHost/Server", AGWServ); - settings->setValue("AGWHost/Port", AGWPort); - settings->setValue("KISS/Server", KISSServ); - settings->setValue("KISS/Port", KISSPort); - - settings->setValue("Modem/PreEmphasisAll1", emph_all[0]); - settings->setValue("Modem/PreEmphasisAll2", emph_all[1]); - settings->setValue("Modem/PreEmphasisAll3", emph_all[2]); - settings->setValue("Modem/PreEmphasisAll4", emph_all[3]); - - settings->setValue("Modem/PreEmphasisDB1", emph_db[0]); - settings->setValue("Modem/PreEmphasisDB2", emph_db[1]); - settings->setValue("Modem/PreEmphasisDB3", emph_db[2]); - settings->setValue("Modem/PreEmphasisDB4", emph_db[3]); - - settings->setValue("Window/Waterfall1", Firstwaterfall); - settings->setValue("Window/Waterfall2", Secondwaterfall); - - settings->setValue("Modem/TxDelay1", txdelay[0]); - settings->setValue("Modem/TxDelay2", txdelay[1]); - settings->setValue("Modem/TxDelay3", txdelay[2]); - settings->setValue("Modem/TxDelay4", txdelay[3]); - - settings->setValue("Modem/TxTail1", txtail[0]); - settings->setValue("Modem/TxTail2", txtail[1]); - settings->setValue("Modem/TxTail3", txtail[2]); - settings->setValue("Modem/TxTail4", txtail[3]); - - settings->setValue("Modem/CWIDCall", CWIDCall); - settings->setValue("Modem/CWIDInterval", CWIDInterval); - settings->setValue("Modem/CWIDLeft", CWIDLeft); - settings->setValue("Modem/CWIDRight", CWIDRight); - settings->setValue("Modem/CWIDType", CWIDType); - - saveAX25Params(0); - saveAX25Params(1); - saveAX25Params(2); - saveAX25Params(3); - - settings->sync(); - - delete(settings); -} diff --git a/HEAD b/HEAD deleted file mode 100644 index cb089cd..0000000 --- a/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/master diff --git a/LinuxBits.c b/LinuxBits.c new file mode 100644 index 0000000..75cb84b --- /dev/null +++ b/LinuxBits.c @@ -0,0 +1,311 @@ +/* +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 + +*/ + +//#define TXSILENCE + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ +// +// Audio interface Routine + +// Passes audio samples to/from the sound interface + +// As this is platform specific it also has the main() routine, which does +// platform specific initialisation before calling ardopmain() + +// This is ALSASound.c for Linux +// Windows Version is Waveout.c + + + + +void gpioSetMode(unsigned gpio, unsigned mode); +void gpioWrite(unsigned gpio, unsigned level); +int _memicmp(unsigned char *a, unsigned char *b, int n); +int stricmp(const unsigned char * pStr1, const unsigned char *pStr2); +int gpioInitialise(void); + +void Sleep(int mS) +{ + usleep(mS * 1000); + return; +} + + + + +// GPIO access stuff for PTT on PI + +#ifdef __ARM_ARCH + +/* + tiny_gpio.c + 2016-04-30 + Public Domain +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPSET0 7 +#define GPSET1 8 + +#define GPCLR0 10 +#define GPCLR1 11 + +#define GPLEV0 13 +#define GPLEV1 14 + +#define GPPUD 37 +#define GPPUDCLK0 38 +#define GPPUDCLK1 39 + +unsigned piModel; +unsigned piRev; + +static volatile uint32_t *gpioReg = MAP_FAILED; + +#define PI_BANK (gpio>>5) +#define PI_BIT (1<<(gpio&0x1F)) + +/* gpio modes. */ + +// PTT via GPIO code + +#ifdef __ARM_ARCH + +#define PI_INPUT 0 +#define PI_OUTPUT 1 +#define PI_ALT0 4 +#define PI_ALT1 5 +#define PI_ALT2 6 +#define PI_ALT3 7 +#define PI_ALT4 3 +#define PI_ALT5 2 + +// Set GPIO pin as output and set low + +void SetupGPIOPTT() +{ + +} +#endif + + + +void gpioSetMode(unsigned gpio, unsigned mode) +{ + int reg, shift; + + reg = gpio/10; + shift = (gpio%10) * 3; + + gpioReg[reg] = (gpioReg[reg] & ~(7<> shift) & 7; +} + +/* Values for pull-ups/downs off, pull-down and pull-up. */ + +#define PI_PUD_OFF 0 +#define PI_PUD_DOWN 1 +#define PI_PUD_UP 2 + +void gpioSetPullUpDown(unsigned gpio, unsigned pud) +{ + *(gpioReg + GPPUD) = pud; + + usleep(20); + + *(gpioReg + GPPUDCLK0 + PI_BANK) = PI_BIT; + + usleep(20); + + *(gpioReg + GPPUD) = 0; + + *(gpioReg + GPPUDCLK0 + PI_BANK) = 0; +} + +int gpioRead(unsigned gpio) +{ + if ((*(gpioReg + GPLEV0 + PI_BANK) & PI_BIT) != 0) return 1; + else return 0; +} +void gpioWrite(unsigned gpio, unsigned level) +{ + if (level == 0) + *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; + else + *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; +} + +void gpioTrigger(unsigned gpio, unsigned pulseLen, unsigned level) +{ + if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; + else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; + + usleep(pulseLen); + + if (level != 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; + else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; +} + +/* Bit (1< - Modem type + Modem params @@ -337,7 +337,7 @@ 100 21 - 61 + 41 23 @@ -346,8 +346,8 @@ 100 - 51 - 61 + 50 + 41 23 @@ -399,7 +399,7 @@ 10 - 50 + 49 71 23 @@ -464,7 +464,7 @@ 176 - 51 + 50 71 23 @@ -541,7 +541,7 @@ 10 - 80 + 79 71 23 @@ -554,7 +554,7 @@ 176 - 81 + 80 71 23 @@ -567,31 +567,8 @@ 100 - 81 - 61 - 23 - - - - - - - 10 - 109 - 71 - 23 - - - - Retries - - - - - - 100 - 110 - 61 + 80 + 41 23 @@ -714,6 +691,52 @@ + + + + 130 + 111 + 71 + 23 + + + + maxFrame + + + + + + 200 + 112 + 41 + 23 + + + + + + + 70 + 111 + 41 + 23 + + + + + + + 10 + 110 + 71 + 23 + + + + Retries + + @@ -1051,7 +1074,7 @@ - Modem type + Modem params @@ -1304,29 +1327,6 @@ - - - - 10 - 109 - 71 - 23 - - - - Retries - - - - - - 100 - 110 - 61 - 23 - - - @@ -1446,6 +1446,52 @@ + + + + 130 + 121 + 71 + 23 + + + + maxFrame + + + + + + 70 + 121 + 41 + 23 + + + + + + + 10 + 121 + 71 + 23 + + + + Retries + + + + + + 200 + 121 + 41 + 23 + + + @@ -1782,7 +1828,7 @@ - Modem type + Modem params @@ -2035,29 +2081,6 @@ - - - - 10 - 109 - 71 - 23 - - - - Retries - - - - - - 100 - 110 - 61 - 23 - - - @@ -2133,6 +2156,52 @@ + + + + 130 + 111 + 71 + 23 + + + + maxFrame + + + + + + 70 + 111 + 41 + 23 + + + + + + + 10 + 111 + 71 + 23 + + + + Retries + + + + + + 200 + 111 + 41 + 23 + + + @@ -2513,7 +2582,7 @@ - Modem type + Modem params @@ -2766,29 +2835,6 @@ - - - - 10 - 109 - 71 - 23 - - - - Retries - - - - - - 100 - 110 - 61 - 23 - - - @@ -2864,6 +2910,52 @@ + + + + 130 + 111 + 71 + 23 + + + + maxFrame + + + + + + 70 + 111 + 41 + 23 + + + + + + + 10 + 111 + 71 + 23 + + + + Retries + + + + + + 200 + 111 + 41 + 23 + + + @@ -2963,8 +3055,8 @@ - 80 - 520 + 44 + 516 71 20 @@ -2976,8 +3068,8 @@ - 150 - 520 + 114 + 516 51 20 @@ -2989,8 +3081,8 @@ - 340 - 520 + 410 + 516 61 20 @@ -3002,8 +3094,8 @@ - 390 - 520 + 460 + 516 101 20 @@ -3015,8 +3107,8 @@ - 220 - 520 + 190 + 516 61 20 @@ -3028,13 +3120,42 @@ - 270 - 520 + 240 + 516 31 20 + + + + 294 + 516 + 61 + 20 + + + + Mark Freq + + + + + + 348 + 516 + 41 + 20 + + + + Leave blank for default + + + + + diff --git a/Modulate.c b/Modulate.c index 5eb8522..1f996db 100644 --- a/Modulate.c +++ b/Modulate.c @@ -5,7 +5,9 @@ #define ARDOPBufferSize 12000 * 100 -extern short ARDOPTXBuffer[4][12000 * 100]; // Enough to hold whole frame of samples +extern char CWIDMark[32]; + +extern short ARDOPTXBuffer[4][ARDOPBufferSize]; // Enough to hold whole frame of samples extern int ARDOPTXLen[4]; // Length of frame extern int ARDOPTXPtr[4]; // Tx Pointer @@ -966,7 +968,7 @@ void sendCWID(char * strID, BOOL CWOnOff, int Chan) 0x557, 0x155, 0x755, 0x1DD5, 0x7775, 0x1DDDD, 0x1D57, 0x1D57}; - float dblHiPhaseInc = 2 * M_PI * 1609.375f / 12000; // 1609.375 Hz High tone + float dblHiPhaseInc = 2 * M_PI * 1509.375f / 12000; // 1609.375 Hz High tone float dblLoPhaseInc = 2 * M_PI * 1390.625f / 12000; // 1390.625 low tone float dblHiPhase = 0; float dblLoPhase = 0; @@ -978,6 +980,23 @@ void sendCWID(char * strID, BOOL CWOnOff, int Chan) char * index; int intMask; int idoffset; + int Filter = 1500; + + if (CWIDMark[0]) + { + // Want nonstandard tones + + float Mark = atof(CWIDMark); + float Space = Mark - 200; + + dblHiPhaseInc = 2 * M_PI * Mark / 12000; // 1609.375 Hz High tone + dblLoPhaseInc = 2 * M_PI * Space / 12000; // 1390.625 low tone + + if (CWOnOff) + Filter = Mark; + else + Filter = (Mark + Space) / 2; + } strlop(strID, '-'); // Remove any SSID @@ -1000,8 +1019,12 @@ void sendCWID(char * strID, BOOL CWOnOff, int Chan) dblLoPhase -= 2 * M_PI; } - initFilter(500,1500, Chan); - + if (CWOnOff) + initFilter(500, Filter, Chan); + else + initFilter(200, Filter, Chan); + + //Generate leader for VOX 6 dots long for (k = 6; k >0; k--) diff --git a/QtSoundModem - Copy.cpp b/QtSoundModem - Copy.cpp deleted file mode 100644 index 8422ee9..0000000 --- a/QtSoundModem - Copy.cpp +++ /dev/null @@ -1,2827 +0,0 @@ -/* -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 - -// UZ7HO Soundmodem Port - -// Not Working 4psk100 FEC - -// Thoughts on Waterfall Display. - -// Original used a 2048 sample FFT giving 5.859375 Hz bins. We plotted 1024 points, giving a 0 to 6000 specrum - -// If we want say 300 to 3300 we need about half the bin size so twice the fft size. But should we also fit required range to window size? - -// Unless we resize the most displayed bit of the screen in around 900 pixels. So each bin should be 3300 / 900 = 3.66667 Hz or a FFT size of around 3273 - -#include "QtSoundModem.h" -#include -//#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "UZ7HOStuff.h" - - -QImage *Constellation; -QImage *Waterfall[4] = { 0,0,0,0 }; -QImage *Header[4]; -QLabel *DCDLabel[4]; -QLineEdit *chanOffsetLabel[4]; -QImage *DCDLed[4]; - -QImage *RXLevel; - -QLabel *WaterfallCopy[2]; -QLabel *HeaderCopy[2]; - -QTextEdit * monWindowCopy; - -extern workerThread *t; -extern QtSoundModem * w; - -QList Ports = QSerialPortInfo::availablePorts(); - -void saveSettings(); -void getSettings(); -extern "C" void CloseSound(); -extern "C" void GetSoundDevices(); -extern "C" char modes_name[modes_count][20]; -extern "C" int speed[5]; -extern "C" int KISSPort; -extern "C" short rx_freq[5]; - -extern "C" int CaptureCount; -extern "C" int PlaybackCount; - -extern "C" int CaptureIndex; // Card number -extern "C" int PlayBackIndex; - -extern "C" char CaptureNames[16][256]; -extern "C" char PlaybackNames[16][256]; - -extern "C" int SoundMode; -extern "C" int multiCore; - -extern "C" int refreshModems; - -extern "C" int pnt_change[5]; -extern "C" int needRSID[4]; - -extern "C" int needSetOffset[4]; - -extern "C" float MagOut[4096]; -extern "C" float MaxMagOut; -extern "C" int MaxMagIndex; - -extern "C" -{ - int InitSound(BOOL Report); - void soundMain(); - void MainLoop(); - void modulator(UCHAR snd_ch, int buf_size); - void SampleSink(int LR, short Sample); - void doCalib(int Port, int Act); - int Freq_Change(int Chan, int Freq); - void set_speed(int snd_ch, int Modem); - void init_speed(int snd_ch); - void wf_pointer(int snd_ch); - void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); - void dofft(short * in, float * outr, float * outi); - void init_raduga(); - void wf_Scale(int Chan); - void AGW_Report_Modem_Change(int port); - char * strlop(char * buf, char delim); - void sendRSID(int Chan, int dropTX); - void RSIDinitfft(); - void il2p_init(int il2p_debug); -} - -void make_graph_buf(float * buf, short tap, QPainter * bitmap); - -int ModemA = 2; -int ModemB = 2; -int ModemC = 2; -int ModemD = 2; -int FreqA = 1500; -int FreqB = 1500; -int FreqC = 1500; -int FreqD = 1500; -int DCD = 50; - -char CWIDCall[128] = ""; -int CWIDInterval = 0; -int CWIDLeft = 0; -int CWIDRight = 0; -int CWIDType = 1; // on/off - -int WaterfallMin = 0; -int WaterfallMax = 3300; - -extern "C" { int RSID_SABM[4]; } -extern "C" { int RSID_UI[4]; } -extern "C" { int RSID_SetModem[4]; } - -int Closing = FALSE; // Set to stop background thread - -QRgb white = qRgb(255, 255, 255); -QRgb black = qRgb(0, 0, 0); - -QRgb green = qRgb(0, 255, 0); -QRgb red = qRgb(255, 0, 0); -QRgb yellow = qRgb(255, 255, 0); -QRgb cyan = qRgb(0, 255, 255); - -// Indexed colour list from ARDOPC - -#define WHITE 0 -#define Tomato 1 -#define Gold 2 -#define Lime 3 -#define Yellow 4 -#define Orange 5 -#define Khaki 6 -#define Cyan 7 -#define DeepSkyBlue 8 -#define RoyalBlue 9 -#define Navy 10 -#define Black 11 -#define Goldenrod 12 -#define Fuchsia 13 - -QRgb vbColours[16] = { qRgb(255, 255, 255), qRgb(255, 99, 71), qRgb(255, 215, 0), qRgb(0, 255, 0), - qRgb(255, 255, 0), qRgb(255, 165, 0), qRgb(240, 240, 140), qRgb(0, 255, 255), - qRgb(0, 191, 255), qRgb(65, 105, 225), qRgb(0, 0, 128), qRgb(0, 0, 0), - qRgb(218, 165, 32), qRgb(255, 0, 255) }; - -unsigned char WaterfallLines[2][80][4096] = { 0 }; -int NextWaterfallLine[2] = { 0 }; - -unsigned int LastLevel = 255; -unsigned int LastBusy = 255; - -extern "C" int UDPClientPort; -extern "C" int UDPServerPort; -extern "C" int TXPort; -extern char UDPHost[64]; - -QTimer *cwidtimer; - -QSystemTrayIcon * trayIcon = nullptr; - -int MintoTray = 1; - -int RSID_WF = 0; // Set to use RSID FFT for Waterfall. - -extern "C" void WriteDebugLog(char * Mess) -{ - qDebug() << Mess; -} - -void QtSoundModem::doupdateDCD(int Chan, int State) -{ - DCDLabel[Chan]->setVisible(State); -} - -extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat); -extern "C" char * ShortDateTime(); - -extern "C" void mon_rsid(int snd_ch, char * RSID) -{ - int Len; - char * Msg = (char *)malloc(1024); // Cant pass local variable via signal/slot - - sprintf(Msg, "%d:%s [%s%c]", snd_ch + 1, RSID, ShortDateTime(), 'R'); - - Len = strlen(Msg); - - if (Msg[Len - 1] != '\r') - { - Msg[Len++] = '\r'; - Msg[Len] = 0; - } - - emit t->sendtoTrace(Msg, 0); -} - -extern "C" void put_frame(int snd_ch, string * frame, char * code, int tx, int excluded) -{ - UNUSED(excluded); - - int Len; - char * Msg = (char *)malloc(1024); // Cant pass local variable via signal/slot - - if (strcmp(code, "NON-AX25") == 0) - sprintf(Msg, "%d: Length, ShortDateTime(), 'R'); - else - sprintf(Msg, "%d:%s", snd_ch + 1, frame_monitor(frame, code, tx)); - - Len = strlen(Msg); - - if (Msg[Len - 1] != '\r') - { - Msg[Len++] = '\r'; - Msg[Len] = 0; - } - - emit t->sendtoTrace(Msg, tx); -} - -extern "C" void updateDCD(int Chan, bool State) -{ - emit t->updateDCD(Chan, State); -} - -bool QtSoundModem::eventFilter(QObject* obj, QEvent *evt) -{ - UNUSED(obj); - - if (evt->type() == QEvent::Resize) - { - return QWidget::event(evt); - } - - if (evt->type() == QEvent::WindowStateChange) - { - if (windowState().testFlag(Qt::WindowMinimized) == true) - w_state = WIN_MINIMIZED; - else - w_state = WIN_MAXIMIZED; - } -// if (evt->type() == QGuiApplication::applicationStateChanged) - this is a sigma; -// { -// qDebug() << "App State changed =" << evt->type() << endl; -// } - - return QWidget::event(evt); -} - -void QtSoundModem::resizeEvent(QResizeEvent* event) -{ - QMainWindow::resizeEvent(event); - - QRect r = geometry(); - - int A, B, C, W; - int modemBoxHeight = 30; - - ui.modeB->setVisible(soundChannel[1]); - ui.centerB->setVisible(soundChannel[1]); - ui.labelB->setVisible(soundChannel[1]); - DCDLabel[1]->setVisible(soundChannel[1]); - ui.RXOffsetB->setVisible(soundChannel[1]); - - ui.modeC->setVisible(soundChannel[2]); - ui.centerC->setVisible(soundChannel[2]); - ui.labelC->setVisible(soundChannel[2]); - DCDLabel[2]->setVisible(soundChannel[2]); - ui.RXOffsetC->setVisible(soundChannel[2]); - - ui.modeD->setVisible(soundChannel[3]); - ui.centerD->setVisible(soundChannel[3]); - ui.labelD->setVisible(soundChannel[3]); - DCDLabel[3]->setVisible(soundChannel[3]); - ui.RXOffsetD->setVisible(soundChannel[3]); - - if (soundChannel[2] || soundChannel[3]) - modemBoxHeight = 60; - - - A = r.height() - 25; // No waterfalls - - if (UsingBothChannels && Secondwaterfall) - { - // Two waterfalls - - ui.WaterfallA->setVisible(1); - ui.HeaderA->setVisible(1); - ui.WaterfallB->setVisible(1); - ui.HeaderB->setVisible(1); - - A = r.height() - 258; // Top of Waterfall A - B = A + 115; // Top of Waterfall B - } - else - { - // One waterfall - - // Could be Left or Right - - if (Firstwaterfall) - { - if (soundChannel[0] == RIGHT) - { - ui.WaterfallA->setVisible(0); - ui.HeaderA->setVisible(0); - ui.WaterfallB->setVisible(1); - ui.HeaderB->setVisible(1); - } - else - { - ui.WaterfallA->setVisible(1); - ui.HeaderA->setVisible(1); - ui.WaterfallB->setVisible(0); - ui.HeaderB->setVisible(0); - } - - A = r.height() - 145; // Top of Waterfall A - } - else - A = r.height() - 25; // Top of Waterfall A - } - - C = A - 150; // Bottom of Monitor, Top of connection list - W = r.width(); - - // Calc Positions of Waterfalls - - ui.monWindow->setGeometry(QRect(0, modemBoxHeight, W, C - (modemBoxHeight + 26))); - sessionTable->setGeometry(QRect(0, C, W, 175)); - - if (UsingBothChannels) - { - ui.HeaderA->setGeometry(QRect(0, A, W, 35)); - ui.WaterfallA->setGeometry(QRect(0, A + 35, W, 80)); - ui.HeaderB->setGeometry(QRect(0, B, W, 35)); - ui.WaterfallB->setGeometry(QRect(0, B + 35, W, 80)); - } - else - { - if (soundChannel[0] == RIGHT) - { - ui.HeaderB->setGeometry(QRect(0, A, W, 35)); - ui.WaterfallB->setGeometry(QRect(0, A + 35, W, 80)); - } - else - { - ui.HeaderA->setGeometry(QRect(0, A, W, 35)); - ui.WaterfallA->setGeometry(QRect(0, A + 35, W, 80)); - } - } -} - -QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State) -{ - QAction * Act = new QAction(Label, parent); - Menu->addAction(Act); - - Act->setCheckable(true); - if (State) - Act->setChecked(true); - - parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked())); - - return Act; -} - -void QtSoundModem::menuChecked() -{ - QAction * Act = static_cast(QObject::sender()); - - int state = Act->isChecked(); - - if (Act == actWaterfall1) - { - int oldstate = Firstwaterfall; - Firstwaterfall = state; - - if (state != oldstate) - initWaterfall(0, state); - - } - else if (Act == actWaterfall2) - { - int oldstate = Secondwaterfall; - Secondwaterfall = state; - - if (state != oldstate) - initWaterfall(1, state); - - } - saveSettings(); -} - -void QtSoundModem::initWaterfall(int chan, int state) -{ - if (state == 1) - { - if (chan == 0) - { - ui.WaterfallA = new QLabel(ui.centralWidget); - WaterfallCopy[0] = ui.WaterfallA; - } - else - { - ui.WaterfallB = new QLabel(ui.centralWidget); - WaterfallCopy[1] = ui.WaterfallB; - } - Waterfall[chan] = new QImage(1024, 80, QImage::Format_RGB32); - Waterfall[chan]->fill(black); - - } - else - { - delete(Waterfall[chan]); - Waterfall[chan] = 0; - } - - QSize Size(800, 602); // Not actually used, but Event constructor needs it - QResizeEvent *event = new QResizeEvent(Size, Size); - QApplication::sendEvent(this, event); -} - -// Local copies - -QLabel *RXOffsetLabel; -QSlider *RXOffset; - -QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) -{ - ui.setupUi(this); - - QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); - - if (MintoTray) - { - char popUp[256]; - sprintf(popUp, "QtSoundModem %d %d", AGWPort, KISSPort); - trayIcon = new QSystemTrayIcon(QIcon(":/QtSoundModem/soundmodem.ico"), this); - trayIcon->setToolTip(popUp); - trayIcon->show(); - - connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(TrayActivated(QSystemTrayIcon::ActivationReason))); - } - - - restoreGeometry(mysettings.value("geometry").toByteArray()); - restoreState(mysettings.value("windowState").toByteArray()); - - sessionTable = new QTableWidget(this); - - sessionTable->verticalHeader()->setVisible(FALSE); - sessionTable->verticalHeader()->setDefaultSectionSize(20); - sessionTable->horizontalHeader()->setDefaultSectionSize(68); - sessionTable->setRowCount(1); - sessionTable->setColumnCount(12); - m_TableHeader << "MyCall" << "DestCall" << "Status" << "Sent pkts" << "Sent Bytes" << "Rcvd pkts" << "Rcvd bytes" << "Rcvd FC" << "FEC corr" << "CPS TX" << "CPS RX" << "Direction"; - - sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(224, 224, 224) }"); - - sessionTable->setHorizontalHeaderLabels(m_TableHeader); - sessionTable->setColumnWidth(0, 80); - sessionTable->setColumnWidth(1, 80); - sessionTable->setColumnWidth(4, 76); - sessionTable->setColumnWidth(5, 76); - sessionTable->setColumnWidth(6, 80); - sessionTable->setColumnWidth(11, 72); - - for (int i = 0; i < modes_count; i++) - { - ui.modeA->addItem(modes_name[i]); - ui.modeB->addItem(modes_name[i]); - ui.modeC->addItem(modes_name[i]); - ui.modeD->addItem(modes_name[i]); - } - - // Set up Menus - - setupMenu = ui.menuBar->addMenu(tr("Settings")); - - actDevices = new QAction("Setup Devices", this); - setupMenu->addAction(actDevices); - - connect(actDevices, SIGNAL(triggered()), this, SLOT(clickedSlot())); - actDevices->setObjectName("actDevices"); - actModems = new QAction("Setup Modems", this); - actModems->setObjectName("actModems"); - setupMenu->addAction(actModems); - - connect(actModems, SIGNAL(triggered()), this, SLOT(clickedSlot())); - - actMintoTray = setupMenu->addAction("Minimize to Tray", this, SLOT(MinimizetoTray())); - actMintoTray->setCheckable(1); - actMintoTray->setChecked(MintoTray); - - viewMenu = ui.menuBar->addMenu(tr("&View")); - - actWaterfall1 = setupMenuLine(viewMenu, (char *)"First waterfall", this, Firstwaterfall); - actWaterfall2 = setupMenuLine(viewMenu, (char *)"Second Waterfall", this, Secondwaterfall); - - actCalib = ui.menuBar->addAction("&Calibration"); - connect(actCalib, SIGNAL(triggered()), this, SLOT(doCalibrate())); - - actRestartWF = ui.menuBar->addAction("Restart Waterfall"); - connect(actRestartWF, SIGNAL(triggered()), this, SLOT(doRestartWF())); - - actAbout = ui.menuBar->addAction("&About"); - connect(actAbout, SIGNAL(triggered()), this, SLOT(doAbout())); - - // Constellation = new QImage(91, 91, QImage::Format_RGB32); - - Header[0] = new QImage(1024, 35, QImage::Format_RGB32); - Header[1] = new QImage(1024, 35, QImage::Format_RGB32); - RXLevel = new QImage(150, 10, QImage::Format_RGB32); - - DCDLabel[0] = new QLabel(this); - DCDLabel[0]->setObjectName(QString::fromUtf8("DCDLedA")); - DCDLabel[0]->setGeometry(QRect(280, 31, 12, 12)); - DCDLabel[0]->setVisible(TRUE); - - DCDLabel[1] = new QLabel(this); - DCDLabel[1]->setObjectName(QString::fromUtf8("DCDLedB")); - DCDLabel[1]->setGeometry(QRect(575, 31, 12, 12)); - DCDLabel[1]->setVisible(TRUE); - - DCDLabel[2] = new QLabel(this); - DCDLabel[2]->setObjectName(QString::fromUtf8("DCDLedC")); - DCDLabel[2]->setGeometry(QRect(280, 61, 12, 12)); - DCDLabel[2]->setVisible(FALSE); - - DCDLabel[3] = new QLabel(this); - DCDLabel[3]->setObjectName(QString::fromUtf8("DCDLedD")); - DCDLabel[3]->setGeometry(QRect(575, 61, 12, 12)); - DCDLabel[3]->setVisible(FALSE); - - DCDLed[0] = new QImage(12, 12, QImage::Format_RGB32); - DCDLed[1] = new QImage(12, 12, QImage::Format_RGB32); - DCDLed[2] = new QImage(12, 12, QImage::Format_RGB32); - DCDLed[3] = new QImage(12, 12, QImage::Format_RGB32); - - DCDLed[0]->fill(red); - DCDLed[1]->fill(red); - DCDLed[2]->fill(red); - DCDLed[3]->fill(red); - - DCDLabel[0]->setPixmap(QPixmap::fromImage(*DCDLed[0])); - DCDLabel[1]->setPixmap(QPixmap::fromImage(*DCDLed[1])); - DCDLabel[2]->setPixmap(QPixmap::fromImage(*DCDLed[2])); - DCDLabel[3]->setPixmap(QPixmap::fromImage(*DCDLed[3])); - - chanOffsetLabel[0] = ui.RXOffsetA; - chanOffsetLabel[1] = ui.RXOffsetB; - chanOffsetLabel[2] = ui.RXOffsetC; - chanOffsetLabel[3] = ui.RXOffsetD; - - - // Waterfall[0]->setColorCount(16); - // Waterfall[1]->setColorCount(16); - - - // for (i = 0; i < 16; i++) - // { - // Waterfall[0]->setColor(i, vbColours[i]); - // Waterfall[1]->setColor(i, vbColours[i]); - // } - - WaterfallCopy[0] = ui.WaterfallA; - WaterfallCopy[1] = ui.WaterfallB; - - initWaterfall(0, 1); - initWaterfall(1, 1); - - Header[0]->fill(black); - Header[1]->fill(black); - - HeaderCopy[0] = ui.HeaderA; - HeaderCopy[1] = ui.HeaderB; - monWindowCopy = ui.monWindow; - - ui.monWindow->document()->setMaximumBlockCount(10000); - -// connect(ui.monWindow, SIGNAL(selectionChanged()), this, SLOT(onTEselectionChanged())); - - ui.HeaderA->setPixmap(QPixmap::fromImage(*Header[0])); - ui.HeaderB->setPixmap(QPixmap::fromImage(*Header[1])); - - wf_pointer(soundChannel[0]); - wf_pointer(soundChannel[1]); - wf_Scale(0); - wf_Scale(1); - - // RefreshLevel(0); - // RXLevel->setPixmap(QPixmap::fromImage(*RXLevel)); - - connect(ui.modeA, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); - connect(ui.modeB, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); - connect(ui.modeC, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); - connect(ui.modeD, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); - - ui.modeA->setCurrentIndex(speed[0]); - ui.modeB->setCurrentIndex(speed[1]); - ui.modeC->setCurrentIndex(speed[2]); - ui.modeD->setCurrentIndex(speed[3]); - - ModemA = ui.modeA->currentIndex(); - - ui.centerA->setValue(rx_freq[0]); - ui.centerB->setValue(rx_freq[1]); - ui.centerC->setValue(rx_freq[2]); - ui.centerD->setValue(rx_freq[3]); - - connect(ui.centerA, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); - connect(ui.centerB, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); - connect(ui.centerC, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); - connect(ui.centerD, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); - - ui.DCDSlider->setValue(dcd_threshold); - - - char valChar[32]; - sprintf(valChar, "RX Offset %d", rxOffset); - ui.RXOffsetLabel->setText(valChar); - ui.RXOffset->setValue(rxOffset); - - RXOffsetLabel = ui.RXOffsetLabel; - RXOffset = ui.RXOffset; - - connect(ui.DCDSlider, SIGNAL(sliderMoved(int)), this, SLOT(clickedSlotI(int))); - connect(ui.RXOffset, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); - - - QObject::connect(t, SIGNAL(sendtoTrace(char *, int)), this, SLOT(sendtoTrace(char *, int)), Qt::QueuedConnection); - QObject::connect(t, SIGNAL(updateDCD(int, int)), this, SLOT(doupdateDCD(int, int)), Qt::QueuedConnection); - - connect(ui.RXOffsetA, SIGNAL(returnPressed()), this, SLOT(returnPressed())); - connect(ui.RXOffsetB, SIGNAL(returnPressed()), this, SLOT(returnPressed())); - connect(ui.RXOffsetC, SIGNAL(returnPressed()), this, SLOT(returnPressed())); - connect(ui.RXOffsetD, SIGNAL(returnPressed()), this, SLOT(returnPressed())); - - QTimer *timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); - timer->start(100); - - - cwidtimer = new QTimer(this); - connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer())); - - if (CWIDInterval) - cwidtimer->start(CWIDInterval * 60000); - - if (RSID_SetModem[0]) - { - RSID_WF = 1; - RSIDinitfft(); - } - il2p_init(1); -} - -void QtSoundModem::MinimizetoTray() -{ - MintoTray = actMintoTray->isChecked(); - saveSettings(); - QMessageBox::about(this, tr("QtSoundModem"), - tr("Program must be restarted to change Minimize mode")); -} - - -void QtSoundModem::TrayActivated(QSystemTrayIcon::ActivationReason reason) -{ - if (reason == 3) - { - showNormal(); - w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); - } -} - -extern "C" void sendCWID(char * strID, BOOL blnPlay, int Chan); - -void QtSoundModem::CWIDTimer() -{ - sendCWID(CWIDCall, CWIDType, 0); - calib_mode[0] = 4; -} - -void extSetOffset(int chan) -{ - char valChar[32]; - sprintf(valChar, "%d", chanOffset[chan]); - chanOffsetLabel[chan]->setText(valChar); - - wf_pointer(soundChannel[chan]); - - pnt_change[0] = 1; - pnt_change[1] = 1; - pnt_change[2] = 1; - pnt_change[3] = 1; - - return; -} - -void QtSoundModem::MyTimerSlot() -{ - // 100 mS Timer Event - - for (int i = 0; i < 4; i++) - { - - if (needSetOffset[i]) - { - needSetOffset[i] = 0; - extSetOffset(i); // Update GUI - } - } - - if (refreshModems) - { - refreshModems = 0; - - ui.modeA->setCurrentIndex(speed[0]); - ui.modeB->setCurrentIndex(speed[1]); - ui.modeC->setCurrentIndex(speed[2]); - ui.modeD->setCurrentIndex(speed[3]); - ui.centerA->setValue(rx_freq[0]); - ui.centerB->setValue(rx_freq[1]); - ui.centerC->setValue(rx_freq[2]); - ui.centerD->setValue(rx_freq[3]); - } - - show_grid(); -} - -void QtSoundModem::returnPressed() -{ - char Name[32]; - int Chan; - QString val; - - strcpy(Name, sender()->objectName().toUtf8()); - - Chan = Name[8] - 'A'; - - val = chanOffsetLabel[Chan]->text(); - - chanOffset[Chan] = val.toInt(); - needSetOffset[Chan] = 1; // Update GUI - - -} - - -void QtSoundModem::clickedSlotI(int i) -{ - char Name[32]; - - strcpy(Name, sender()->objectName().toUtf8()); - - if (strcmp(Name, "modeA") == 0) - { - ModemA = ui.modeA->currentIndex(); - set_speed(0, ModemA); - saveSettings(); - AGW_Report_Modem_Change(0); - return; - } - - if (strcmp(Name, "modeB") == 0) - { - ModemB = ui.modeB->currentIndex(); - set_speed(1, ModemB); - saveSettings(); - AGW_Report_Modem_Change(1); - return; - } - - if (strcmp(Name, "modeC") == 0) - { - ModemC = ui.modeC->currentIndex(); - set_speed(2, ModemC); - saveSettings(); - AGW_Report_Modem_Change(2); - return; - } - - if (strcmp(Name, "modeD") == 0) - { - ModemD = ui.modeD->currentIndex(); - set_speed(3, ModemD); - saveSettings(); - AGW_Report_Modem_Change(3); - return; - } - - if (strcmp(Name, "centerA") == 0) - { - if (i > 300) - { - QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - ui.centerA->setValue(Freq_Change(0, i)); - settings->setValue("Modem/RXFreq1", ui.centerA->value()); - AGW_Report_Modem_Change(0); - - } - return; - } - - if (strcmp(Name, "centerB") == 0) - { - if (i > 300) - { - QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - ui.centerB->setValue(Freq_Change(1, i)); - settings->setValue("Modem/RXFreq2", ui.centerB->value()); - AGW_Report_Modem_Change(1); - } - return; - } - - if (strcmp(Name, "centerC") == 0) - { - if (i > 300) - { - QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - ui.centerC->setValue(Freq_Change(2, i)); - settings->setValue("Modem/RXFreq3", ui.centerC->value()); - AGW_Report_Modem_Change(2); - } - return; - } - - if (strcmp(Name, "centerD") == 0) - { - if (i > 300) - { - QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); - ui.centerD->setValue(Freq_Change(3, i)); - settings->setValue("Modem/RXFreq4", ui.centerD->value()); - AGW_Report_Modem_Change(3); - } - return; - } - - if (strcmp(Name, "DCDSlider") == 0) - { - dcd_threshold = i; - saveSettings(); - return; - } - - if (strcmp(Name, "RXOffset") == 0) - { - char valChar[32]; - rxOffset = i; - sprintf(valChar, "RX Offset %d",rxOffset); - ui.RXOffsetLabel->setText(valChar); - - wf_pointer(soundChannel[0]); - wf_pointer(soundChannel[1]); - - pnt_change[0] = 1; - pnt_change[1] = 1; - pnt_change[2] = 1; - pnt_change[3] = 1; - - saveSettings(); - return; - } - - - QMessageBox msgBox; - msgBox.setWindowTitle("MessageBox Title"); - msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); - msgBox.exec(); -} - - -void QtSoundModem::clickedSlot() -{ - char Name[32]; - - strcpy(Name, sender()->objectName().toUtf8()); - - if (strcmp(Name, "actDevices") == 0) - { - doDevices(); - return; - } - - if (strcmp(Name, "actModems") == 0) - { - doModems(); - return; - } - - if (strcmp(Name, "showBPF_A") == 0) - { - doFilter(0, 0); - return; - } - - if (strcmp(Name, "showTXBPF_A") == 0) - { - doFilter(0, 1); - return; - } - - if (strcmp(Name, "showLPF_A") == 0) - { - doFilter(0, 2); - return; - } - - - if (strcmp(Name, "showBPF_B") == 0) - { - doFilter(1, 0); - return; - } - - if (strcmp(Name, "showTXBPF_B") == 0) - { - doFilter(1, 1); - return; - } - - if (strcmp(Name, "showLPF_B") == 0) - { - doFilter(1, 2); - return; - } - - if (strcmp(Name, "Low_A") == 0) - { - handleButton(0, 1); - return; - } - - if (strcmp(Name, "High_A") == 0) - { - handleButton(0, 2); - return; - } - - if (strcmp(Name, "Both_A") == 0) - { - handleButton(0, 3); - return; - } - - if (strcmp(Name, "Stop_A") == 0) - { - handleButton(0, 0); - return; - } - - - if (strcmp(Name, "Low_B") == 0) - { - handleButton(1, 1); - return; - } - - if (strcmp(Name, "High_B") == 0) - { - handleButton(1, 2); - return; - } - - if (strcmp(Name, "Both_B") == 0) - { - handleButton(1, 3); - return; - } - - if (strcmp(Name, "Stop_B") == 0) - { - handleButton(1, 0); - return; - } - - if (strcmp(Name, "Low_C") == 0) - { - handleButton(2, 1); - return; - } - - if (strcmp(Name, "High_C") == 0) - { - handleButton(2, 2); - return; - } - - if (strcmp(Name, "Both_C") == 0) - { - handleButton(2, 3); - return; - } - - if (strcmp(Name, "Stop_C") == 0) - { - handleButton(2, 0); - return; - } - - if (strcmp(Name, "Low_D") == 0) - { - handleButton(3, 1); - return; - } - - if (strcmp(Name, "High_D") == 0) - { - handleButton(3, 2); - return; - } - - if (strcmp(Name, "Both_D") == 0) - { - handleButton(3, 3); - return; - } - - if (strcmp(Name, "Stop_D") == 0) - { - handleButton(3, 0); - return; - } - - QMessageBox msgBox; - msgBox.setWindowTitle("MessageBox Title"); - msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); - msgBox.exec(); -} - -Ui_ModemDialog * Dlg; - -QDialog * modemUI; -QDialog * deviceUI; - -void QtSoundModem::doModems() -{ - Dlg = new(Ui_ModemDialog); - - QDialog UI; - char valChar[10]; - - Dlg->setupUi(&UI); - - modemUI = &UI; - deviceUI = 0; - - myResize *resize = new myResize(); - - UI.installEventFilter(resize); - - sprintf(valChar, "%d", bpf[0]); - Dlg->BPFWidthA->setText(valChar); - sprintf(valChar, "%d", bpf[1]); - Dlg->BPFWidthB->setText(valChar); - sprintf(valChar, "%d", bpf[2]); - Dlg->BPFWidthC->setText(valChar); - sprintf(valChar, "%d", bpf[3]); - Dlg->BPFWidthD->setText(valChar); - - sprintf(valChar, "%d", txbpf[0]); - Dlg->TXBPFWidthA->setText(valChar); - sprintf(valChar, "%d", txbpf[1]); - Dlg->TXBPFWidthB->setText(valChar); - sprintf(valChar, "%d", txbpf[2]); - Dlg->TXBPFWidthC->setText(valChar); - sprintf(valChar, "%d", txbpf[3]); - Dlg->TXBPFWidthD->setText(valChar); - - sprintf(valChar, "%d", lpf[0]); - Dlg->LPFWidthA->setText(valChar); - sprintf(valChar, "%d", lpf[1]); - Dlg->LPFWidthB->setText(valChar); - sprintf(valChar, "%d", lpf[2]); - Dlg->LPFWidthC->setText(valChar); - sprintf(valChar, "%d", lpf[4]); - Dlg->LPFWidthD->setText(valChar); - - sprintf(valChar, "%d", BPF_tap[0]); - Dlg->BPFTapsA->setText(valChar); - sprintf(valChar, "%d", BPF_tap[1]); - Dlg->BPFTapsB->setText(valChar); - sprintf(valChar, "%d", BPF_tap[2]); - Dlg->BPFTapsC->setText(valChar); - sprintf(valChar, "%d", BPF_tap[3]); - Dlg->BPFTapsD->setText(valChar); - - sprintf(valChar, "%d", LPF_tap[0]); - Dlg->LPFTapsA->setText(valChar); - sprintf(valChar, "%d", LPF_tap[1]); - Dlg->LPFTapsB->setText(valChar); - sprintf(valChar, "%d", LPF_tap[2]); - Dlg->LPFTapsC->setText(valChar); - sprintf(valChar, "%d", LPF_tap[3]); - Dlg->LPFTapsD->setText(valChar); - - Dlg->preEmphAllA->setChecked(emph_all[0]); - - if (emph_all[0]) - Dlg->preEmphA->setDisabled(TRUE); - else - Dlg->preEmphA->setCurrentIndex(emph_db[0]); - - Dlg->preEmphAllB->setChecked(emph_all[1]); - - if (emph_all[1]) - Dlg->preEmphB->setDisabled(TRUE); - else - Dlg->preEmphB->setCurrentIndex(emph_db[1]); - - Dlg->preEmphAllC->setChecked(emph_all[2]); - - if (emph_all[2]) - Dlg->preEmphC->setDisabled(TRUE); - else - Dlg->preEmphC->setCurrentIndex(emph_db[2]); - - Dlg->preEmphAllD->setChecked(emph_all[3]); - - if (emph_all[3]) - Dlg->preEmphD->setDisabled(TRUE); - else - Dlg->preEmphD->setCurrentIndex(emph_db[3]); - - - Dlg->nonAX25A->setChecked(NonAX25[0]); - Dlg->nonAX25B->setChecked(NonAX25[1]); - Dlg->nonAX25C->setChecked(NonAX25[2]); - Dlg->nonAX25D->setChecked(NonAX25[3]); - - Dlg->KISSOptA->setChecked(KISS_opt[0]); - Dlg->KISSOptB->setChecked(KISS_opt[1]); - Dlg->KISSOptC->setChecked(KISS_opt[2]); - Dlg->KISSOptD->setChecked(KISS_opt[3]); - - sprintf(valChar, "%d", txdelay[0]); - Dlg->TXDelayA->setText(valChar); - sprintf(valChar, "%d", txdelay[1]); - Dlg->TXDelayB->setText(valChar); - sprintf(valChar, "%d", txdelay[2]); - Dlg->TXDelayC->setText(valChar); - sprintf(valChar, "%d", txdelay[3]); - Dlg->TXDelayD->setText(valChar); - - sprintf(valChar, "%d", txtail[0]); - Dlg->TXTailA->setText(valChar); - sprintf(valChar, "%d", txtail[1]); - Dlg->TXTailB->setText(valChar); - sprintf(valChar, "%d", txtail[2]); - Dlg->TXTailC->setText(valChar); - sprintf(valChar, "%d", txtail[3]); - Dlg->TXTailD->setText(valChar); - - Dlg->FrackA->setText(QString::number(frack_time[0])); - Dlg->FrackB->setText(QString::number(frack_time[1])); - Dlg->FrackC->setText(QString::number(frack_time[2])); - Dlg->FrackD->setText(QString::number(frack_time[3])); - - Dlg->RetriesA->setText(QString::number(fracks[0])); - Dlg->RetriesB->setText(QString::number(fracks[1])); - Dlg->RetriesC->setText(QString::number(fracks[2])); - Dlg->RetriesD->setText(QString::number(fracks[3])); - - sprintf(valChar, "%d", RCVR[0]); - Dlg->AddRXA->setText(valChar); - sprintf(valChar, "%d", RCVR[1]); - Dlg->AddRXB->setText(valChar); - sprintf(valChar, "%d", RCVR[2]); - Dlg->AddRXC->setText(valChar); - sprintf(valChar, "%d", RCVR[3]); - Dlg->AddRXD->setText(valChar); - - sprintf(valChar, "%d", rcvr_offset[0]); - Dlg->RXShiftA->setText(valChar); - - sprintf(valChar, "%d", rcvr_offset[1]); - Dlg->RXShiftB->setText(valChar); - - sprintf(valChar, "%d", rcvr_offset[2]); - Dlg->RXShiftC->setText(valChar); - sprintf(valChar, "%d", rcvr_offset[3]); - Dlg->RXShiftD->setText(valChar); - - // speed[1] - // speed[2]; - - Dlg->recoverBitA->setCurrentIndex(recovery[0]); - Dlg->recoverBitB->setCurrentIndex(recovery[1]); - Dlg->recoverBitC->setCurrentIndex(recovery[2]); - Dlg->recoverBitD->setCurrentIndex(recovery[3]); - - Dlg->fx25ModeA->setCurrentIndex(fx25_mode[0]); - Dlg->fx25ModeB->setCurrentIndex(fx25_mode[1]); - Dlg->fx25ModeC->setCurrentIndex(fx25_mode[2]); - Dlg->fx25ModeD->setCurrentIndex(fx25_mode[3]); - - Dlg->IL2PModeA->setCurrentIndex(il2p_mode[0]); - Dlg->IL2PModeB->setCurrentIndex(il2p_mode[1]); - Dlg->IL2PModeC->setCurrentIndex(il2p_mode[2]); - Dlg->IL2PModeD->setCurrentIndex(il2p_mode[3]); - - Dlg->CWIDCall->setText(CWIDCall); - Dlg->CWIDInterval->setText(QString::number(CWIDInterval)); - - if (CWIDType) - Dlg->radioButton_2->setChecked(1); - else - Dlg->CWIDType->setChecked(1); - - Dlg->RSIDSABM_A->setChecked(RSID_SABM[0]); - Dlg->RSIDSABM_B->setChecked(RSID_SABM[1]); - Dlg->RSIDSABM_C->setChecked(RSID_SABM[2]); - Dlg->RSIDSABM_D->setChecked(RSID_SABM[3]); - - Dlg->RSIDUI_A->setChecked(RSID_UI[0]); - Dlg->RSIDUI_B->setChecked(RSID_UI[1]); - Dlg->RSIDUI_C->setChecked(RSID_UI[2]); - Dlg->RSIDUI_D->setChecked(RSID_UI[3]); - - Dlg->DigiCallsA->setText(MyDigiCall[0]); - Dlg->DigiCallsB->setText(MyDigiCall[1]); - Dlg->DigiCallsC->setText(MyDigiCall[2]); - Dlg->DigiCallsD->setText(MyDigiCall[3]); - - Dlg->RSID_1_SETMODEM->setChecked(RSID_SetModem[0]); - Dlg->RSID_2_SETMODEM->setChecked(RSID_SetModem[1]); - Dlg->RSID_3_SETMODEM->setChecked(RSID_SetModem[2]); - Dlg->RSID_4_SETMODEM->setChecked(RSID_SetModem[3]); - - connect(Dlg->showBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Dlg->showTXBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Dlg->showLPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); - - connect(Dlg->showBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Dlg->showTXBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Dlg->showLPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); - - connect(Dlg->showBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Dlg->showTXBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Dlg->showLPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); - - connect(Dlg->showBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Dlg->showTXBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Dlg->showLPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); - - connect(Dlg->okButton, SIGNAL(clicked()), this, SLOT(modemaccept())); - connect(Dlg->modemSave, SIGNAL(clicked()), this, SLOT(modemSave())); - connect(Dlg->cancelButton, SIGNAL(clicked()), this, SLOT(modemreject())); - - connect(Dlg->SendRSID_1, SIGNAL(clicked()), this, SLOT(doRSIDA())); - connect(Dlg->SendRSID_2, SIGNAL(clicked()), this, SLOT(doRSIDB())); - connect(Dlg->SendRSID_3, SIGNAL(clicked()), this, SLOT(doRSIDC())); - connect(Dlg->SendRSID_4, SIGNAL(clicked()), this, SLOT(doRSIDD())); - - connect(Dlg->preEmphAllA, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllAChanged(int))); - connect(Dlg->preEmphAllB, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllBChanged(int))); - connect(Dlg->preEmphAllC, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllCChanged(int))); - connect(Dlg->preEmphAllD, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllDChanged(int))); - - UI.exec(); -} - -void QtSoundModem::preEmphAllAChanged(int state) -{ - Dlg->preEmphA->setDisabled(state); -} - -void QtSoundModem::preEmphAllBChanged(int state) -{ - Dlg->preEmphB->setDisabled(state); -} - -void QtSoundModem::preEmphAllCChanged(int state) -{ - Dlg->preEmphC->setDisabled(state); -} - -void QtSoundModem::preEmphAllDChanged(int state) -{ - Dlg->preEmphD->setDisabled(state); -} - -extern "C" void get_exclude_list(char * line, TStringList * list); - -void QtSoundModem::modemaccept() -{ - modemSave(); - delete(Dlg); - saveSettings(); - - modemUI->accept(); - -} - -void QtSoundModem::modemSave() -{ - QVariant Q; - - emph_all[0] = Dlg->preEmphAllA->isChecked(); - emph_db[0] = Dlg->preEmphA->currentIndex(); - - emph_all[1] = Dlg->preEmphAllB->isChecked(); - emph_db[1] = Dlg->preEmphB->currentIndex(); - - emph_all[2] = Dlg->preEmphAllC->isChecked(); - emph_db[2] = Dlg->preEmphC->currentIndex(); - - emph_all[3] = Dlg->preEmphAllD->isChecked(); - emph_db[3] = Dlg->preEmphD->currentIndex(); - - NonAX25[0] = Dlg->nonAX25A->isChecked(); - NonAX25[1] = Dlg->nonAX25B->isChecked(); - NonAX25[2] = Dlg->nonAX25C->isChecked(); - NonAX25[3] = Dlg->nonAX25D->isChecked(); - - KISS_opt[0] = Dlg->KISSOptA->isChecked(); - KISS_opt[1] = Dlg->KISSOptB->isChecked(); - KISS_opt[2] = Dlg->KISSOptC->isChecked(); - KISS_opt[3] = Dlg->KISSOptD->isChecked(); - - if (emph_db[0] < 0 || emph_db[0] > nr_emph) - emph_db[0] = 0; - - if (emph_db[1] < 0 || emph_db[1] > nr_emph) - emph_db[1] = 0; - - if (emph_db[2] < 0 || emph_db[2] > nr_emph) - emph_db[2] = 0; - - if (emph_db[3] < 0 || emph_db[3] > nr_emph) - emph_db[3] = 0; - - Q = Dlg->TXDelayA->text(); - txdelay[0] = Q.toInt(); - - Q = Dlg->TXDelayB->text(); - txdelay[1] = Q.toInt(); - - Q = Dlg->TXDelayC->text(); - txdelay[2] = Q.toInt(); - - Q = Dlg->TXDelayD->text(); - txdelay[3] = Q.toInt(); - - Q = Dlg->TXTailA->text(); - txtail[0] = Q.toInt(); - - Q = Dlg->TXTailB->text(); - txtail[1] = Q.toInt(); - - Q = Dlg->TXTailC->text(); - txtail[2] = Q.toInt(); - - txtail[3] = Dlg->TXTailD->text().toInt(); - - frack_time[0] = Dlg->FrackA->text().toInt(); - frack_time[1] = Dlg->FrackB->text().toInt(); - frack_time[2] = Dlg->FrackC->text().toInt(); - frack_time[3] = Dlg->FrackD->text().toInt(); - - fracks[0] = Dlg->RetriesA->text().toInt(); - fracks[1] = Dlg->RetriesB->text().toInt(); - fracks[2] = Dlg->RetriesC->text().toInt(); - fracks[3] = Dlg->RetriesD->text().toInt(); - - Q = Dlg->AddRXA->text(); - RCVR[0] = Q.toInt(); - - Q = Dlg->AddRXB->text(); - RCVR[1] = Q.toInt(); - - Q = Dlg->AddRXC->text(); - RCVR[2] = Q.toInt(); - - Q = Dlg->AddRXD->text(); - RCVR[3] = Q.toInt(); - - Q = Dlg->RXShiftA->text(); - rcvr_offset[0] = Q.toInt(); - - Q = Dlg->RXShiftB->text(); - rcvr_offset[1] = Q.toInt(); - - Q = Dlg->RXShiftC->text(); - rcvr_offset[2] = Q.toInt(); - - Q = Dlg->RXShiftD->text(); - rcvr_offset[3] = Q.toInt(); - - fx25_mode[0] = Dlg->fx25ModeA->currentIndex(); - fx25_mode[1] = Dlg->fx25ModeB->currentIndex(); - fx25_mode[2] = Dlg->fx25ModeC->currentIndex(); - fx25_mode[3] = Dlg->fx25ModeD->currentIndex(); - - il2p_mode[0] = Dlg->IL2PModeA->currentIndex(); - il2p_mode[1] = Dlg->IL2PModeB->currentIndex(); - il2p_mode[2] = Dlg->IL2PModeC->currentIndex(); - il2p_mode[3] = Dlg->IL2PModeD->currentIndex(); - - recovery[0] = Dlg->recoverBitA->currentIndex(); - recovery[1] = Dlg->recoverBitB->currentIndex(); - recovery[2] = Dlg->recoverBitC->currentIndex(); - recovery[3] = Dlg->recoverBitD->currentIndex(); - - - strcpy(CWIDCall, Dlg->CWIDCall->text().toUtf8().toUpper()); - CWIDInterval = Dlg->CWIDInterval->text().toInt(); - CWIDType = Dlg->radioButton_2->isChecked(); - - if (CWIDInterval) - cwidtimer->start(CWIDInterval * 60000); - else - cwidtimer->stop(); - - - RSID_SABM[0] = Dlg->RSIDSABM_A->isChecked(); - RSID_SABM[1] = Dlg->RSIDSABM_B->isChecked(); - RSID_SABM[2] = Dlg->RSIDSABM_C->isChecked(); - RSID_SABM[3] = Dlg->RSIDSABM_D->isChecked(); - - RSID_UI[0] = Dlg->RSIDUI_A->isChecked(); - RSID_UI[1] = Dlg->RSIDUI_B->isChecked(); - RSID_UI[2] = Dlg->RSIDUI_C->isChecked(); - RSID_UI[3] = Dlg->RSIDUI_D->isChecked(); - - RSID_SetModem[0] = Dlg->RSID_1_SETMODEM->isChecked(); - RSID_SetModem[1] = Dlg->RSID_2_SETMODEM->isChecked(); - RSID_SetModem[2] = Dlg->RSID_3_SETMODEM->isChecked(); - RSID_SetModem[3] = Dlg->RSID_4_SETMODEM->isChecked(); - - Q = Dlg->DigiCallsA->text(); - strcpy(MyDigiCall[0], Q.toString().toUtf8().toUpper()); - - Q = Dlg->DigiCallsB->text(); - strcpy(MyDigiCall[1], Q.toString().toUtf8().toUpper()); - - Q = Dlg->DigiCallsC->text(); - strcpy(MyDigiCall[2], Q.toString().toUtf8().toUpper()); - - Q = Dlg->DigiCallsD->text(); - strcpy(MyDigiCall[3], Q.toString().toUtf8().toUpper()); - - int i; - - for (i = 0; i < 4; i++) - { - initTStringList(&list_digi_callsigns[i]); - - get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]); - } - -} - -void QtSoundModem::modemreject() -{ - delete(Dlg); - modemUI->reject(); -} - -void QtSoundModem::doRSIDA() -{ - needRSID[0] = 1; -} - -void QtSoundModem::doRSIDB() -{ - needRSID[1] = 1; -} - -void QtSoundModem::doRSIDC() -{ - needRSID[2] = 1; -} - -void QtSoundModem::doRSIDD() -{ - needRSID[3] = 1; -} - - - - -void QtSoundModem::doFilter(int Chan, int Filter) -{ - Ui_Dialog Dev; - QImage * bitmap; - - QDialog UI; - - Dev.setupUi(&UI); - - bitmap = new QImage(642, 312, QImage::Format_RGB32); - - bitmap->fill(qRgb(255, 255, 255)); - - QPainter qPainter(bitmap); - qPainter.setBrush(Qt::NoBrush); - qPainter.setPen(Qt::black); - - if (Filter == 0) - make_graph_buf(DET[0][0].BPF_core[Chan], BPF_tap[Chan], &qPainter); - else if (Filter == 1) - make_graph_buf(tx_BPF_core[Chan], tx_BPF_tap[Chan], &qPainter); - else - make_graph_buf(LPF_core[Chan], LPF_tap[Chan], &qPainter); - - qPainter.end(); - Dev.label->setPixmap(QPixmap::fromImage(*bitmap)); - - UI.exec(); - -} - -Ui_devicesDialog * Dev; - -char NewPTTPort[80]; - -int newSoundMode = 0; -int oldSoundMode = 0; - -void QtSoundModem::SoundModeChanged(bool State) -{ - UNUSED(State); - - // Mustn't change SoundMode until dialog is accepted - - if (Dev->UDP->isChecked()) - newSoundMode = 3; - else if (Dev->PULSE->isChecked()) - newSoundMode = 2; - else - newSoundMode = Dev->OSS->isChecked(); - -} - -void QtSoundModem::DualPTTChanged(bool State) -{ - UNUSED(State); - - // Forse Evaluation of Cat Port setting - - PTTPortChanged(0); -} - -void QtSoundModem::CATChanged(bool State) -{ - UNUSED(State); - PTTPortChanged(0); -} - -void QtSoundModem::PTTPortChanged(int Selected) -{ - UNUSED(Selected); - - QVariant Q = Dev->PTTPort->currentText(); - strcpy(NewPTTPort, Q.toString().toUtf8()); - - Dev->RTSDTR->setVisible(false); - Dev->CAT->setVisible(false); - - Dev->PTTOnLab->setVisible(false); - Dev->PTTOn->setVisible(false); - Dev->PTTOff->setVisible(false); - Dev->PTTOffLab->setVisible(false); - Dev->CATLabel->setVisible(false); - Dev->CATSpeed->setVisible(false); - - Dev->GPIOLab->setVisible(false); - Dev->GPIOLeft->setVisible(false); - Dev->GPIORight->setVisible(false); - Dev->GPIOLab2->setVisible(false); - - Dev->CM108Label->setVisible(false); - Dev->VIDPID->setVisible(false); - - if (strcmp(NewPTTPort, "None") == 0) - { - } - else if (strcmp(NewPTTPort, "GPIO") == 0) - { - Dev->GPIOLab->setVisible(true); - Dev->GPIOLeft->setVisible(true); - if (Dev->DualPTT->isChecked()) - { - Dev->GPIORight->setVisible(true); - Dev->GPIOLab2->setVisible(true); - } - } - - else if (strcmp(NewPTTPort, "CM108") == 0) - { - Dev->CM108Label->setVisible(true); -//#ifdef __ARM_ARCHX - Dev->CM108Label->setText("CM108 Device"); -//#else -// Dev->CM108Label->setText("CM108 VID/PID"); -//#endif - Dev->VIDPID->setText(CM108Addr); - Dev->VIDPID->setVisible(true); - } - else if (strcmp(NewPTTPort, "HAMLIB") == 0) - { - Dev->CM108Label->setVisible(true); - Dev->CM108Label->setText("rigctrld Port"); - Dev->VIDPID->setText(QString::number(HamLibPort)); - Dev->VIDPID->setVisible(true); - Dev->PTTOnLab->setText("rigctrld Host"); - Dev->PTTOnLab->setVisible(true); - Dev->PTTOn->setText(HamLibHost); - Dev->PTTOn->setVisible(true); - } - else - { - Dev->RTSDTR->setVisible(true); - Dev->CAT->setVisible(true); - - if (Dev->CAT->isChecked()) - { - Dev->PTTOnLab->setVisible(true); - Dev->PTTOnLab->setText("PTT On String"); - Dev->PTTOn->setText(PTTOnString); - Dev->PTTOn->setVisible(true); - Dev->PTTOff->setVisible(true); - Dev->PTTOff->setText(PTTOffString); - Dev->PTTOffLab->setVisible(true); - Dev->CATLabel->setVisible(true); - Dev->CATSpeed->setVisible(true); - } - } -} - -bool myResize::eventFilter(QObject *obj, QEvent *event) -{ - if (event->type() == QEvent::Resize) - { - QResizeEvent *resizeEvent = static_cast(event); - QSize size = resizeEvent->size(); - int h = size.height(); - int w = size.width(); - - if (obj == deviceUI) - Dev->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10)); - else - Dlg->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10)); - - return true; - } - return QObject::eventFilter(obj, event); -} - -void QtSoundModem::doDevices() -{ - char valChar[10]; - - Dev = new(Ui_devicesDialog); - - QDialog UI; - - int i; - - Dev->setupUi(&UI); - - deviceUI = &UI; - modemUI = 0; - - myResize *resize = new myResize(); - - UI.installEventFilter(resize); - - newSoundMode = SoundMode; - oldSoundMode = SoundMode; - -#ifdef WIN32 - Dev->ALSA->setText("WaveOut"); - Dev->OSS->setVisible(0); - Dev->PULSE->setVisible(0); -#endif - - if (SoundMode == 0) - Dev->ALSA->setChecked(1); - else if (SoundMode == 1) - Dev->OSS->setChecked(1); - else if (SoundMode == 2) - Dev->PULSE->setChecked(1); - else if (SoundMode == 2) - Dev->UDP->setChecked(1); - - connect(Dev->ALSA, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); - connect(Dev->OSS, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); - connect(Dev->PULSE, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); - connect(Dev->UDP, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); - - for (i = 0; i < PlaybackCount; i++) - Dev->outputDevice->addItem(&PlaybackNames[i][0]); - - i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains); - - - if (i == -1) - { - // Add device to list - - Dev->outputDevice->addItem(PlaybackDevice); - i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains); - } - - Dev->outputDevice->setCurrentIndex(i); - - for (i = 0; i < CaptureCount; i++) - Dev->inputDevice->addItem(&CaptureNames[i][0]); - - i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains); - - if (i == -1) - { - // Add device to list - - Dev->inputDevice->addItem(CaptureDevice); - i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains); - } - Dev->inputDevice->setCurrentIndex(i); - - Dev->Modem_1_Chan->setCurrentIndex(soundChannel[0]); - Dev->Modem_2_Chan->setCurrentIndex(soundChannel[1]); - Dev->Modem_3_Chan->setCurrentIndex(soundChannel[2]); - Dev->Modem_4_Chan->setCurrentIndex(soundChannel[3]); - - // Disable "None" option in first modem - - QStandardItemModel *model = dynamic_cast(Dev->Modem_1_Chan->model()); - QStandardItem * item = model->item(0, 0); - item->setEnabled(false); - - Dev->singleChannelOutput->setChecked(SCO); - Dev->colourWaterfall->setChecked(raduga); - - sprintf(valChar, "%d", KISSPort); - Dev->KISSPort->setText(valChar); - Dev->KISSEnabled->setChecked(KISSServ); - - sprintf(valChar, "%d", AGWPort); - Dev->AGWPort->setText(valChar); - Dev->AGWEnabled->setChecked(AGWServ); - - Dev->PTTOn->setText(PTTOnString); - Dev->PTTOff->setText(PTTOffString); - - sprintf(valChar, "%d", PTTBAUD); - Dev->CATSpeed->setText(valChar); - - sprintf(valChar, "%d", UDPClientPort); - Dev->UDPPort->setText(valChar); - Dev->UDPTXHost->setText(UDPHost); - - if (UDPServerPort != TXPort) - sprintf(valChar, "%d/%d", UDPServerPort, TXPort); - else - sprintf(valChar, "%d", UDPServerPort); - - Dev->UDPTXPort->setText(valChar); - - Dev->UDPEnabled->setChecked(UDPServ); - - sprintf(valChar, "%d", pttGPIOPin); - Dev->GPIOLeft->setText(valChar); - sprintf(valChar, "%d", pttGPIOPinR); - Dev->GPIORight->setText(valChar); - - Dev->VIDPID->setText(CM108Addr); - - QStringList items; - - connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool))); - connect(Dev->DualPTT, SIGNAL(toggled(bool)), this, SLOT(DualPTTChanged(bool))); - connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int))); - - - - if (PTTMode == PTTCAT) - Dev->CAT->setChecked(true); - else - Dev->RTSDTR->setChecked(true); - - for (const QSerialPortInfo &info : Ports) - { - items.append(info.portName()); - } - - items.sort(); - - Dev->PTTPort->addItem("None"); - Dev->PTTPort->addItem("CM108"); - - //#ifdef __ARM_ARCH - - Dev->PTTPort->addItem("GPIO"); - - //#endif - - Dev->PTTPort->addItem("HAMLIB"); - - for (const QString &info : items) - { - Dev->PTTPort->addItem(info); - } - - Dev->PTTPort->setCurrentIndex(Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString)); - - PTTPortChanged(0); // Force reevaluation - - Dev->txRotation->setChecked(TX_rotate); - Dev->DualPTT->setChecked(DualPTT); - - Dev->multiCore->setChecked(multiCore); - - QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept())); - QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject())); - - UI.exec(); - -} - -void QtSoundModem::deviceaccept() -{ - QVariant Q = Dev->inputDevice->currentText(); - int cardChanged = 0; - char portString[32]; - - if (Dev->UDP->isChecked()) - { - // cant have server and slave - - if (Dev->UDPEnabled->isChecked()) - { - QMessageBox::about(this, tr("QtSoundModem"), - tr("Can't have UDP sound source and UDP server at same time")); - return; - } - } - - if (oldSoundMode != newSoundMode) - { - QMessageBox msgBox; - - msgBox.setText("QtSoundModem must restart to change Sound Mode.\n" - "Program will close if you hit Ok\n" - "You will need to reselect audio devices after restarting"); - - msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - - int i = msgBox.exec(); - - if (i == QMessageBox::Ok) - { - SoundMode = newSoundMode; - - saveSettings(); - - Closing = 1; - return; - } - - if (oldSoundMode == 0) - Dev->ALSA->setChecked(1); - else if (oldSoundMode == 1) - Dev->OSS->setChecked(1); - else if (oldSoundMode == 2) - Dev->PULSE->setChecked(1); - else if (oldSoundMode == 3) - Dev->UDP->setChecked(1); - - QMessageBox::about(this, tr("Info"), - tr("

Changes not saved

")); - - return; - - } - - if (strcmp(CaptureDevice, Q.toString().toUtf8()) != 0) - { - strcpy(CaptureDevice, Q.toString().toUtf8()); - cardChanged = 1; - } - - CaptureIndex = Dev->inputDevice->currentIndex(); - - Q = Dev->outputDevice->currentText(); - - if (strcmp(PlaybackDevice, Q.toString().toUtf8()) != 0) - { - strcpy(PlaybackDevice, Q.toString().toUtf8()); - cardChanged = 1; - } - - PlayBackIndex = Dev->outputDevice->currentIndex(); - - soundChannel[0] = Dev->Modem_1_Chan->currentIndex(); - soundChannel[1] = Dev->Modem_2_Chan->currentIndex(); - soundChannel[2] = Dev->Modem_3_Chan->currentIndex(); - soundChannel[3] = Dev->Modem_4_Chan->currentIndex(); - - UsingLeft = 0; - UsingRight = 0; - UsingBothChannels = 0; - - for (int i = 0; i < 4; i++) - { - if (soundChannel[i] == LEFT) - { - UsingLeft = 1; - modemtoSoundLR[i] = 0; - } - else if (soundChannel[i] == RIGHT) - { - UsingRight = 1; - modemtoSoundLR[i] = 1; - } - } - - if (UsingLeft && UsingRight) - UsingBothChannels = 1; - - - SCO = Dev->singleChannelOutput->isChecked(); - raduga = Dev->colourWaterfall->isChecked(); - AGWServ = Dev->AGWEnabled->isChecked(); - KISSServ = Dev->KISSEnabled->isChecked(); - - Q = Dev->KISSPort->text(); - KISSPort = Q.toInt(); - - Q = Dev->AGWPort->text(); - AGWPort = Q.toInt(); - - Q = Dev->PTTPort->currentText(); - strcpy(PTTPort, Q.toString().toUtf8()); - - DualPTT = Dev->DualPTT->isChecked(); - TX_rotate = Dev->txRotation->isChecked(); - multiCore = Dev->multiCore->isChecked(); - - if (Dev->CAT->isChecked()) - PTTMode = PTTCAT; - else - PTTMode = PTTRTS; - - Q = Dev->PTTOn->text(); - strcpy(PTTOnString, Q.toString().toUtf8()); - Q = Dev->PTTOff->text(); - strcpy(PTTOffString, Q.toString().toUtf8()); - - Q = Dev->CATSpeed->text(); - PTTBAUD = Q.toInt(); - - Q = Dev->UDPPort->text(); - UDPClientPort = Q.toInt(); - - - Q = Dev->UDPTXPort->text(); - strcpy(portString, Q.toString().toUtf8()); - UDPServerPort = atoi(portString); - - if (strchr(portString, '/')) - { - char * ptr = strlop(portString, '/'); - TXPort = atoi(ptr); - } - else - TXPort = UDPServerPort; - - Q = Dev->UDPTXHost->text(); - strcpy(UDPHost, Q.toString().toUtf8()); - - UDPServ = Dev->UDPEnabled->isChecked(); - - Q = Dev->GPIOLeft->text(); - pttGPIOPin = Q.toInt(); - - Q = Dev->GPIORight->text(); - pttGPIOPinR = Q.toInt(); - - Q = Dev->VIDPID->text(); - - if (strcmp(PTTPort, "CM108") == 0) - strcpy(CM108Addr, Q.toString().toUtf8()); - else if (strcmp(PTTPort, "HAMLIB") == 0) - { - HamLibPort = Q.toInt(); - Q = Dev->PTTOn->text(); - strcpy(HamLibHost, Q.toString().toUtf8()); - } - - ClosePTTPort(); - OpenPTTPort(); - - wf_pointer(soundChannel[0]); - wf_pointer(soundChannel[1]); - - delete(Dev); - saveSettings(); - deviceUI->accept(); - - if (cardChanged) - { - InitSound(1); - } - - // Reset title and tooltip in case ports changed - - char Title[128]; - sprintf(Title, "QtSoundModem Version %s Ports %d/%d", VersionString, AGWPort, KISSPort); - w->setWindowTitle(Title); - - sprintf(Title, "QtSoundModem %d %d", AGWPort, KISSPort); - if (trayIcon) - trayIcon->setToolTip(Title); - - QSize newSize(this->size()); - QSize oldSize(this->size()); - - QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize); - - QCoreApplication::postEvent(this, myResizeEvent); -} - -void QtSoundModem::devicereject() -{ - delete(Dev); - deviceUI->reject(); -} - -void QtSoundModem::handleButton(int Port, int Type) -{ - // interlock calib with CWID - - if (calib_mode[0] == 4) // CWID - return; - - doCalib(Port, Type); -} - -void QtSoundModem::doRestartWF() -{ - if (Firstwaterfall) - { - initWaterfall(0, 0); - initWaterfall(0, 1); - } - - if (Secondwaterfall) - { - initWaterfall(1, 0); - initWaterfall(1, 1); - } -} - - -void QtSoundModem::doAbout() -{ - QMessageBox::about(this, tr("About"), - tr("G8BPQ's port of UZ7HO's Soundmodem\n\nCopyright (C) 2019-2020 Andrei Kopanchuk UZ7HO")); -} - -void QtSoundModem::doCalibrate() -{ - Ui_calDialog Calibrate; - { - QDialog UI; - Calibrate.setupUi(&UI); - - connect(Calibrate.Low_A, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Both_A, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Stop_A, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Low_B, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.High_B, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Both_B, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Stop_B, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Low_C, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.High_C, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Both_C, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Stop_C, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Low_D, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.High_D, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Both_D, SIGNAL(released()), this, SLOT(clickedSlot())); - connect(Calibrate.Stop_D, SIGNAL(released()), this, SLOT(clickedSlot())); - - /* - - connect(Calibrate.Low_A, &QPushButton::released, this, [=] { handleButton(0, 1); }); - connect(Calibrate.High_A, &QPushButton::released, this, [=] { handleButton(0, 2); }); - connect(Calibrate.Both_A, &QPushButton::released, this, [=] { handleButton(0, 3); }); - connect(Calibrate.Stop_A, &QPushButton::released, this, [=] { handleButton(0, 0); }); - connect(Calibrate.Low_B, &QPushButton::released, this, [=] { handleButton(1, 1); }); - connect(Calibrate.High_B, &QPushButton::released, this, [=] { handleButton(1, 2); }); - connect(Calibrate.Both_B, &QPushButton::released, this, [=] { handleButton(1, 3); }); - connect(Calibrate.Stop_B, &QPushButton::released, this, [=] { handleButton(1, 0); }); - -// connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(handleButton(1, 2))); -*/ - UI.exec(); - } -} - -void QtSoundModem::RefreshSpectrum(unsigned char * Data) -{ - int i; - - // Last 4 bytes are level busy and Tuning lines - - Waterfall[0]->fill(Black); - - if (Data[206] != LastLevel) - { - LastLevel = Data[206]; -// RefreshLevel(LastLevel); - } - - if (Data[207] != LastBusy) - { - LastBusy = Data[207]; -// Busy->setVisible(LastBusy); - } - - for (i = 0; i < 205; i++) - { - int val = Data[0]; - - if (val > 63) - val = 63; - - Waterfall[0]->setPixel(i, val, Yellow); - if (val < 62) - Waterfall[0]->setPixel(i, val + 1, Gold); - Data++; - } - - ui.WaterfallA->setPixmap(QPixmap::fromImage(*Waterfall[0])); - -} - -void QtSoundModem::RefreshWaterfall(int snd_ch, unsigned char * Data) -{ - int j; - unsigned char * Line; - int len = Waterfall[0]->bytesPerLine(); - int TopLine = NextWaterfallLine[snd_ch]; - - // Write line to cyclic buffer then draw starting with the line just written - - // Length is 208 bytes, including Level and Busy flags - - memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Data, 206); - if (NextWaterfallLine[snd_ch] > 63) - NextWaterfallLine[snd_ch] = 0; - - for (j = 63; j > 0; j--) - { - Line = Waterfall[0]->scanLine(j); - memcpy(Line, &WaterfallLines[snd_ch][TopLine++][0], len); - if (TopLine > 63) - TopLine = 0; - } - - ui.WaterfallA->setPixmap(QPixmap::fromImage(*Waterfall[0])); -} - - -void QtSoundModem::sendtoTrace(char * Msg, int tx) -{ - const QTextCursor old_cursor = monWindowCopy->textCursor(); - const int old_scrollbar_value = monWindowCopy->verticalScrollBar()->value(); - const bool is_scrolled_down = old_scrollbar_value == monWindowCopy->verticalScrollBar()->maximum(); - - // Move the cursor to the end of the document. - monWindowCopy->moveCursor(QTextCursor::End); - - // Insert the text at the position of the cursor (which is the end of the document). - - if (tx) - monWindowCopy->setTextColor(qRgb(192, 0, 0)); - else - monWindowCopy->setTextColor(qRgb(0, 0, 192)); - - monWindowCopy->textCursor().insertText(Msg); - - if (old_cursor.hasSelection() || !is_scrolled_down) - { - // The user has selected text or scrolled away from the bottom: maintain position. - monWindowCopy->setTextCursor(old_cursor); - monWindowCopy->verticalScrollBar()->setValue(old_scrollbar_value); - } - else - { - // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. - monWindowCopy->moveCursor(QTextCursor::End); - monWindowCopy->verticalScrollBar()->setValue(monWindowCopy->verticalScrollBar()->maximum()); - } - - free(Msg); -} - - -// I think this does the waterfall - -typedef struct TRGBQ_t -{ - Byte b, g, r, re; - -} TRGBWQ; - -typedef struct tagRECT -{ - int left; - int top; - int right; - int bottom; -} RECT; - -unsigned int RGBWF[256] ; - - -extern "C" void init_raduga() -{ - Byte offset[6] = {0, 51, 102, 153, 204}; - Byte i, n; - - for (n = 0; n < 52; n++) - { - i = n * 5; - - RGBWF[n + offset[0]] = qRgb(0, 0, i); - RGBWF[n + offset[1]] = qRgb(0, i, 255); - RGBWF[n + offset[2]] = qRgb(0, 255, 255 - i); - RGBWF[n + offset[3]] = qRgb(1, 255, 0); - RGBWF[n + offset[4]] = qRgb(255, 255 - 1, 0); - } -} - -extern "C" int nonGUIMode; - - -// This draws the Frequency Scale on Waterfall - -extern "C" void wf_Scale(int Chan) -{ - if (nonGUIMode) - return; - - float k; - int maxfreq, x, i; - char Textxx[20]; - QImage * bm = Header[Chan]; - - QPainter qPainter(bm); - qPainter.setBrush(Qt::black); - qPainter.setPen(Qt::white); - - maxfreq = roundf(RX_Samplerate*0.005); - k = 100 * FFTSize / RX_Samplerate; - - if (Chan == 0) - sprintf(Textxx, "Left"); - else - sprintf(Textxx, "Right"); - - qPainter.drawText(2, 1, - 100, 20, 0, Textxx); - - for (i = 0; i < maxfreq; i++) - { - x = round(k*i); - if (x < 1025) - { - if ((i % 5) == 0) - qPainter.drawLine(x, 20, x, 13); - else - qPainter.drawLine(x, 20, x, 16); - - if ((i % 5) == 0) - { - sprintf(Textxx, "%d", i * 100); - - qPainter.drawText(x - 12, 1, - 100, 20, 0, Textxx); - } - } - } - HeaderCopy[Chan]->setPixmap(QPixmap::fromImage(*bm)); - -} - -// This draws the frequency Markers on the Waterfall - - -void do_pointer(int waterfall) -{ - if (nonGUIMode) - return; - - float x; - - int x1, x2, k, pos1, pos2, pos3; - QImage * bm = Header[waterfall]; - - QPainter qPainter(bm); - qPainter.setBrush(Qt::NoBrush); - qPainter.setPen(Qt::white); - - // bm->fill(black); - - qPainter.fillRect(0, 26, 1024, 9, Qt::black); - - k = 29; - x = FFTSize / RX_Samplerate; - - // draw all enabled ports on the ports on this soundcard - - // First Modem is always on the first waterfall - // If second is enabled it is on the first unless different - // channel from first - - for (int i = 0; i < 4; i++) - { - if (UsingBothChannels == 0) - { - // Only One Waterfall. If first chan is - - if ((waterfall == 0 && soundChannel[i] == RIGHT) || (waterfall == 1 && soundChannel[i] == LEFT)) - return; - } - - if (soundChannel[i] == 0) - continue; - - - if (UsingBothChannels == 1) - if ((waterfall == 0 && soundChannel[i] == RIGHT) || (waterfall == 1 && soundChannel[i] == LEFT)) - continue; - - pos1 = roundf(((rxOffset + chanOffset[i] + rx_freq[i]) - 0.5*rx_shift[i])*x) - 5; - pos2 = roundf(((rxOffset + chanOffset[i] + rx_freq[i]) + 0.5*rx_shift[i])*x) - 5; - pos3 = roundf((rxOffset + chanOffset[i] + rx_freq[i]) * x); - x1 = pos1 + 5; - x2 = pos2 + 5; - - qPainter.setPen(Qt::white); - qPainter.drawLine(x1, k, x2, k); - qPainter.drawLine(x1, k - 3, x1, k + 3); - qPainter.drawLine(x2, k - 3, x2, k + 3); - qPainter.drawLine(pos3, k - 3, pos3, k + 3); - - if (rxOffset || chanOffset[i]) - { - // Draw TX posn if rxOffset used - - pos3 = roundf(rx_freq[i] * x); - qPainter.setPen(Qt::magenta); - qPainter.drawLine(pos3, k - 3, pos3, k + 3); - qPainter.drawLine(pos3, k - 3, pos3, k + 3); - qPainter.drawLine(pos3 - 2, k - 3, pos3 + 2, k - 3); - - } - } - HeaderCopy[waterfall]->setPixmap(QPixmap::fromImage(*bm)); -} - -void wf_pointer(int snd_ch) -{ - UNUSED(snd_ch); - - do_pointer(0); - do_pointer(1); -// do_pointer(2); -// do_pointer(3); -} - - -void doWaterfallThread(void * param); - -/* -#ifdef WIN32 - -#define pthread_t uintptr_t - -extern "C" uintptr_t _beginthread(void(__cdecl *start_address)(void *), unsigned stack_size, void *arglist); - -#else - -#include - -extern "C" pthread_t _beginthread(void(*start_address)(void *), unsigned stack_size, void * arglist) -{ - pthread_t thread; - - if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*)arglist) != 0) - perror("New Thread"); - else - pthread_detach(thread); - - return thread; -} - -#endif -*/ -extern "C" void doWaterfall(int snd_ch) -{ - if (nonGUIMode) - return; - - if (Closing) - return; - -// if (multiCore) // Run modems in separate threads -// _beginthread(doWaterfallThread, 0, xx); -// else - doWaterfallThread((void *)(size_t)snd_ch); - -} - - -extern "C" float aFFTAmpl[1024]; - -void doWaterfallThread(void * param) -{ - int snd_ch = (int)(size_t)param; - - QImage * bm = Waterfall[snd_ch]; - - word i, wid; - single mag; - UCHAR * p; - UCHAR Line[4096] = ""; // 4 bytes per pixel - - int lineLen, Start, End; - word hFFTSize; - Byte n; - float RealOut[4096] = { 0 }; - float ImagOut[4096]; - - QRegion exposed; - - hFFTSize = FFTSize / 2; - - // I think an FFT should produce n/2 bins, each of Samp/n Hz - // Looks like my code only works with n a power of 2 - - // So can use 1024 or 4096. 1024 gives 512 bins of 11.71875 and a 512 pixel - // display (is this enough?) - - - - Start = (WaterfallMin * FFTSize) / 12000; // First and last bins to process - End = (WaterfallMax * FFTSize) / 12000; - - - if (0) //RSID_WF - { - // Use the Magnitudes in float aFFTAmpl[RSID_FFT_SIZE]; - - for (i = 0; i < hFFTSize; i++) - { - mag = aFFTAmpl[i]; - - mag *= 0.00000042f; - - if (mag < 0.00001f) - mag = 0.00001f; - - if (mag > 1.0f) - mag = 1.0f; - - mag = 22 * log2f(mag) + 255; - - if (mag < 0) - mag = 0; - - fft_disp[snd_ch][i] = round(mag); - } - } - else - { - dofft(&fft_buf[snd_ch][0], RealOut, ImagOut); - - // FourierTransform(1024, &fft_buf[snd_ch][0], RealOut, ImagOut, 0); - - for (i = Start; i < End; i++) - { - //mag: = ComplexMag(fft_d[i])*0.00000042; - - // mag = sqrtf(powf(RealOut[i], 2) + powf(ImagOut[i], 2)) * 0.00000042f; - - mag = powf(RealOut[i], 2); - mag += powf(ImagOut[i], 2); - mag = sqrtf(mag); - mag *= 0.00000042f; - - - if (mag > MaxMagOut) - { - MaxMagOut = mag; - MaxMagIndex = i; - } - - if (mag < 0.00001f) - mag = 0.00001f; - - if (mag > 1.0f) - mag = 1.0f; - - mag = 22 * log2f(mag) + 255; - - if (mag < 0) - mag = 0; - - MagOut[i] = mag; // for Freq Guess - fft_disp[snd_ch][i] = round(mag); - } - } - - if (bm == 0) - return; - - - wid = bm->width(); - if (wid > hFFTSize) - wid = hFFTSize; - - wid = wid - 1; - - p = Line; - lineLen = bm->bytesPerLine(); - - if (wid > lineLen / 4) - wid = lineLen / 4; - - if (raduga == DISP_MONO) - { - for (i = Start; i < End; i++) - { - n = fft_disp[snd_ch][i]; - *(p++) = n; // all colours the same - *(p++) = n; - *(p++) = n; - p++; - } - } - else - { - for (i = Start; i < End; i++) - { - n = fft_disp[snd_ch][i]; - memcpy(p, &RGBWF[n], 4); - p += 4; - } - } - - // Scroll - - int TopLine = NextWaterfallLine[snd_ch]; - - // Write line to cyclic buffer then draw starting with the line just written - - memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Line, 4096); - if (NextWaterfallLine[snd_ch] > 79) - NextWaterfallLine[snd_ch] = 0; - - for (int j = 79; j > 0; j--) - { - p = bm->scanLine(j); - memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen); - TopLine++; - if (TopLine > 79) - TopLine = 0; - } - - WaterfallCopy[snd_ch]->setPixmap(QPixmap::fromImage(*bm)); - // WaterfallCopy[snd_ch - 1]->setPixmap(*pm); - // WaterfallCopy[1]->setPixmap(QPixmap::fromImage(*bm)); - -} - - - -void QtSoundModem::changeEvent(QEvent* e) -{ - if (e->type() == QEvent::WindowStateChange) - { - QWindowStateChangeEvent* ev = static_cast(e); - - qDebug() << windowState(); - - if (!(ev->oldState() & Qt::WindowMinimized) && windowState() & Qt::WindowMinimized) - { - if (trayIcon) - setVisible(false); - } -// if (!(ev->oldState() != Qt::WindowNoState) && windowState() == Qt::WindowNoState) -// { -// QMessageBox::information(this, "", "Window has been restored"); -// } - - } - QWidget::changeEvent(e); -} - -#include - -void QtSoundModem::closeEvent(QCloseEvent *event) -{ - UNUSED(event); - - QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); - mysettings.setValue("geometry", QWidget::saveGeometry()); - mysettings.setValue("windowState", saveState()); - - Closing = TRUE; - qDebug() << "Closing"; - - QThread::msleep(100); -} - - -QtSoundModem::~QtSoundModem() -{ - qDebug() << "Saving Settings"; - - QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); - mysettings.setValue("geometry", saveGeometry()); - mysettings.setValue("windowState", saveState()); - - saveSettings(); - Closing = TRUE; - qDebug() << "Closing"; - - QThread::msleep(100); -} - -extern "C" void QSleep(int ms) -{ - QThread::msleep(ms); -} - -int upd_time = 30; - -void QtSoundModem::show_grid() -{ - // This refeshes the session list - - int snd_ch, i, num_rows, row_idx; - QTableWidgetItem *item; - const char * msg; - - int speed_tx, speed_rx; - - if (grid_time < 10) - { - grid_time++; - return; - } - - grid_time = 0; - - //label7.Caption = inttostr(stat_r_mem); mem_arq - - num_rows = 0; - row_idx = 0; - - for (snd_ch = 0; snd_ch < 4; snd_ch++) - { - for (i = 0; i < port_num; i++) - { - if (AX25Port[snd_ch][i].status != STAT_NO_LINK) - num_rows++; - } - } - - if (num_rows == 0) - { - sessionTable->clearContents(); - sessionTable->setRowCount(0); - sessionTable->setRowCount(1); - } - else - sessionTable->setRowCount(num_rows); - - - for (snd_ch = 0; snd_ch < 4; snd_ch++) - { - for (i = 0; i < port_num; i++) - { - if (AX25Port[snd_ch][i].status != STAT_NO_LINK) - { - switch (AX25Port[snd_ch][i].status) - { - case STAT_NO_LINK: - - msg = "No link"; - break; - - case STAT_LINK: - - msg = "Link"; - break; - - case STAT_CHK_LINK: - - msg = "Chk link"; - break; - - case STAT_WAIT_ANS: - - msg = "Wait ack"; - break; - - case STAT_TRY_LINK: - - msg = "Try link"; - break; - - case STAT_TRY_UNLINK: - - msg = "Try unlink"; - } - - - item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].mycall); - sessionTable->setItem(row_idx, 0, item); - - item = new QTableWidgetItem(AX25Port[snd_ch][i].kind); - sessionTable->setItem(row_idx, 11, item); - - item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].corrcall); - sessionTable->setItem(row_idx, 1, item); - - item = new QTableWidgetItem(msg); - sessionTable->setItem(row_idx, 2, item); - - item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_pkt)); - sessionTable->setItem(row_idx, 3, item); - - item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_byte)); - sessionTable->setItem(row_idx, 4, item); - - item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_pkt)); - sessionTable->setItem(row_idx, 5, item); - - item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_byte)); - sessionTable->setItem(row_idx, 6, item); - - item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_fc)); - sessionTable->setItem(row_idx, 7, item); - - item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_fec_count)); - sessionTable->setItem(row_idx, 8, item); - - if (grid_timer != upd_time) - grid_timer++; - else - { - grid_timer = 0; - speed_tx = round(abs(AX25Port[snd_ch][i].info.stat_s_byte - AX25Port[snd_ch][i].info.stat_l_s_byte) / upd_time); - speed_rx = round(abs(AX25Port[snd_ch][i].info.stat_r_byte - AX25Port[snd_ch][i].info.stat_l_r_byte) / upd_time); - - item = new QTableWidgetItem(QString::number(speed_tx)); - sessionTable->setItem(row_idx, 9, item); - - item = new QTableWidgetItem(QString::number(speed_rx)); - sessionTable->setItem(row_idx, 10, item); - - AX25Port[snd_ch][i].info.stat_l_r_byte = AX25Port[snd_ch][i].info.stat_r_byte; - AX25Port[snd_ch][i].info.stat_l_s_byte = AX25Port[snd_ch][i].info.stat_s_byte; - } - - row_idx++; - } - } - } -} - -// "Copy on Select" Code - -void QtSoundModem::onTEselectionChanged() -{ - QTextEdit * x = static_cast(QObject::sender()); - x->copy(); -} - diff --git a/QtSoundModem-DESKTOP-MHE5LO8.vcxproj b/QtSoundModem-DESKTOP-MHE5LO8.vcxproj new file mode 100644 index 0000000..adcd4b6 --- /dev/null +++ b/QtSoundModem-DESKTOP-MHE5LO8.vcxproj @@ -0,0 +1,292 @@ +п»ї + + + + Release + Win32 + + + Debug + Win32 + + + + {4EDE958E-D0AC-37B4-81F7-78313A262DCD} + QtSoundModem + QtVS_v304 + 10.0.17763.0 + 10.0.19041.0 + $(MSBuildProjectDirectory)\QtMsBuild + + + + v141 + release\ + false + NotSet + Application + release\ + QtSoundModem + + + v141 + debug\ + false + NotSet + Application + debug\ + QtSoundModem + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\ + QtSoundModem + true + true + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\\ + QtSoundModem + true + false + + + 5.14 + core;network;gui;widgets;serialport + + + 5.14.2 + core;network;gui;widgets;serialport + + + + + + + rsid;.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;release;/include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + MaxSpeed + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + false + $(OutDir) + MultiThreadedDLL + true + true + Level3 + true + + + libfftw3f-3.lib;shell32.lib;setupapi.lib;WS2_32.Lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + true + $(OutDir)QtSoundModem.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtSoundModem + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + + + + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;debug;/include;rsid;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + EditAndContinue + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + Disabled + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + true + $(OutDir) + + + libfftw3f-3.lib;shell32.lib;setupapi.lib;WS2_32.Lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\QtSoundModem.exe + true + Windows + true + false + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtSoundModem + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + + + + + + + + + CompileAsC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + Generate moc_predefs.h + debug\moc_predefs.h;%(Outputs) + + + Document + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h + Generate moc_predefs.h + release\moc_predefs.h;%(Outputs) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QtSoundModem.cpp b/QtSoundModem.cpp index 94ec95d..fdf137f 100644 --- a/QtSoundModem.cpp +++ b/QtSoundModem.cpp @@ -50,7 +50,7 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include "UZ7HOStuff.h" -QImage *Constellation; +QImage *Constellation[4]; QImage *Waterfall[4] = { 0,0,0,0 }; QImage *Header[4]; QLabel *DCDLabel[4]; @@ -73,7 +73,7 @@ void saveSettings(); void getSettings(); extern "C" void CloseSound(); extern "C" void GetSoundDevices(); -extern "C" char modes_name[modes_count][20]; +extern "C" char modes_name[modes_count][21]; extern "C" int speed[5]; extern "C" int KISSPort; extern "C" short rx_freq[5]; @@ -91,6 +91,7 @@ extern "C" int SoundMode; extern "C" int multiCore; extern "C" int refreshModems; +int NeedPSKRefresh; extern "C" int pnt_change[5]; extern "C" int needRSID[4]; @@ -101,6 +102,11 @@ extern "C" float MagOut[4096]; extern "C" float MaxMagOut; extern "C" int MaxMagIndex; + +extern "C" int using48000; // Set if using 48K sample rate (ie RUH Modem active) +extern "C" int ReceiveSize; +extern "C" int SendSize; // 100 mS for now + extern "C" { int InitSound(BOOL Report); @@ -137,6 +143,7 @@ int FreqD = 1500; int DCD = 50; char CWIDCall[128] = ""; +extern "C" char CWIDMark[32] = ""; int CWIDInterval = 0; int CWIDLeft = 0; int CWIDRight = 0; @@ -147,11 +154,12 @@ int WaterfallMax = 6000; int Configuring = 0; -float BinSize; +extern "C" float BinSize; extern "C" { int RSID_SABM[4]; } extern "C" { int RSID_UI[4]; } extern "C" { int RSID_SetModem[4]; } +extern "C" unsigned int pskStates[4]; int Closing = FALSE; // Set to stop background thread @@ -461,17 +469,80 @@ void QtSoundModem::initWaterfall(int chan, int state) QApplication::sendEvent(this, event); } +QRect PSKRect = { 100,100,100,100 }; + +QDialog * constellationDialog; +QLabel * constellationLabel[4]; +QLabel * QualLabel[4]; + // Local copies QLabel *RXOffsetLabel; QSlider *RXOffset; +extern "C" void CheckPSKWindows() +{ + NeedPSKRefresh = 1; +} +void DoPSKWindows() +{ + // Display Constellation for PSK Window; + + int NextX = 0; + int i; + + for (i = 0; i < 4; i++) + { + if (pskStates[i]) + { + constellationLabel[i]->setGeometry(QRect(NextX, 19, 121, 121)); + QualLabel[i]->setGeometry(QRect(1 + NextX, 1, 120, 15)); + constellationLabel[i]->setVisible(1); + QualLabel[i]->setVisible(1); + + NextX += 122; + } + else + { + constellationLabel[i]->setVisible(0); + QualLabel[i]->setVisible(0); + } + } + constellationDialog->resize(NextX, 140); +} + + + QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) { ui.setupUi(this); - + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + constellationDialog = new QDialog(nullptr, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + constellationDialog->resize(488, 140); + constellationDialog->setGeometry(PSKRect); + + QFont f("Arial", 8, QFont::Normal); + + for (int i = 0; i < 4; i++) + { + char Text[16]; + sprintf(Text, "Chan %c", i + 'A'); + + constellationLabel[i] = new QLabel(constellationDialog); + constellationDialog->setWindowTitle("PSK Constellations"); + + QualLabel[i] = new QLabel(constellationDialog); + QualLabel[i]->setText(Text); + QualLabel[i]->setFont(f); + + Constellation[i] = new QImage(121, 121, QImage::Format_RGB32); + Constellation[i]->fill(black); + constellationLabel[i]->setPixmap(QPixmap::fromImage(*Constellation[i])); + } + constellationDialog->show(); + if (MintoTray) { char popUp[256]; @@ -479,10 +550,24 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) trayIcon = new QSystemTrayIcon(QIcon(":/QtSoundModem/soundmodem.ico"), this); trayIcon->setToolTip(popUp); trayIcon->show(); - + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(TrayActivated(QSystemTrayIcon::ActivationReason))); } + using48000 = 0; // Set if using 48K sample rate (ie RUH Modem active) + ReceiveSize = 512; + SendSize = 1024; // 100 mS for now + + for (int i = 0; i < 4; i++) + { + if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96)) + { + using48000 = 1; // Set if using 48K sample rate (ie RUH Modem active) + ReceiveSize = 2048; + SendSize = 4096; // 100 mS for now + } + } + float FFTCalc = 12000.0f / ((WaterfallMax - WaterfallMin) / 900.0f); FFTSize = FFTCalc + 0.4999; @@ -648,6 +733,11 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) connect(ui.modeC, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); connect(ui.modeD, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + ModemA = speed[0]; + ModemB = speed[1]; + ModemC = speed[2]; + ModemD = speed[3]; + ui.modeA->setCurrentIndex(speed[0]); ui.modeB->setCurrentIndex(speed[1]); ui.modeC->setCurrentIndex(speed[2]); @@ -692,7 +782,11 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); timer->start(100); + QTimer *wftimer = new QTimer(this); + connect(wftimer, SIGNAL(timeout()), this, SLOT(doRestartWF())); + wftimer->start(1000 * 300); + cwidtimer = new QTimer(this); connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer())); @@ -776,6 +870,11 @@ void QtSoundModem::MyTimerSlot() ui.centerC->setValue(rx_freq[2]); ui.centerD->setValue(rx_freq[3]); } + if (NeedPSKRefresh) + { + NeedPSKRefresh = 0; + DoPSKWindows(); + } show_grid(); } @@ -798,6 +897,44 @@ void QtSoundModem::returnPressed() } +void CheckforChanges(int Mode, int OldMode) +{ + int old48000 = using48000; + + if (OldMode != Mode && Mode == 15) + { + QMessageBox msgBox; + + msgBox.setText("Warning!!\nARDOP Packet is NOT the same as ARDOP\n" + "It is an experimental mode for sending ax.25 frames using ARDOP packet formats\n"); + + msgBox.setStandardButtons(QMessageBox::Ok); + + msgBox.exec(); + } + + // See if need to switch beween 12000 and 48000 + + using48000 = 0; // Set if using 48K sample rate (ie RUH Modem active) + ReceiveSize = 512; + SendSize = 1024; // 100 mS for now + + for (int i = 0; i < 4; i++) + { + if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96)) + { + using48000 = 1; // Set if using 48K sample rate (ie RUH Modem active) + ReceiveSize = 2048; + SendSize = 4096; // 100 mS for now + } + } + + if (using48000 != old48000) + { + InitSound(1); + } +} + void QtSoundModem::clickedSlotI(int i) { @@ -807,8 +944,10 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "modeA") == 0) { + int OldModem = ModemA; ModemA = ui.modeA->currentIndex(); set_speed(0, ModemA); + CheckforChanges(ModemA, OldModem); saveSettings(); AGW_Report_Modem_Change(0); return; @@ -816,8 +955,10 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "modeB") == 0) { + int OldModem = ModemB; ModemB = ui.modeB->currentIndex(); set_speed(1, ModemB); + CheckforChanges(ModemB, OldModem); saveSettings(); AGW_Report_Modem_Change(1); return; @@ -825,8 +966,10 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "modeC") == 0) { + int OldModem = ModemC; ModemC = ui.modeC->currentIndex(); set_speed(2, ModemC); + CheckforChanges(ModemC, OldModem); saveSettings(); AGW_Report_Modem_Change(2); return; @@ -834,8 +977,10 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "modeD") == 0) { + int OldModem = ModemD; ModemD = ui.modeD->currentIndex(); set_speed(3, ModemD); + CheckforChanges(ModemD, OldModem); saveSettings(); AGW_Report_Modem_Change(3); return; @@ -843,7 +988,7 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "centerA") == 0) { - if (i > 300) + if (i > 299) { QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); ui.centerA->setValue(Freq_Change(0, i)); @@ -856,7 +1001,7 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "centerB") == 0) { - if (i > 300) + if (i > 299) { QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); ui.centerB->setValue(Freq_Change(1, i)); @@ -868,7 +1013,7 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "centerC") == 0) { - if (i > 300) + if (i > 299) { QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); ui.centerC->setValue(Freq_Change(2, i)); @@ -880,7 +1025,7 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "centerD") == 0) { - if (i > 300) + if (i > 299) { QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); ui.centerD->setValue(Freq_Change(3, i)); @@ -1187,6 +1332,15 @@ void QtSoundModem::doModems() Dlg->KISSOptC->setChecked(KISS_opt[2]); Dlg->KISSOptD->setChecked(KISS_opt[3]); + sprintf(valChar, "%d", maxframe[0]); + Dlg->MaxFrameA->setText(valChar); + sprintf(valChar, "%d", maxframe[1]); + Dlg->MaxFrameB->setText(valChar); + sprintf(valChar, "%d", maxframe[2]); + Dlg->MaxFrameC->setText(valChar); + sprintf(valChar, "%d", maxframe[3]); + Dlg->MaxFrameD->setText(valChar); + sprintf(valChar, "%d", txdelay[0]); Dlg->TXDelayA->setText(valChar); sprintf(valChar, "%d", txdelay[1]); @@ -1196,6 +1350,7 @@ void QtSoundModem::doModems() sprintf(valChar, "%d", txdelay[3]); Dlg->TXDelayD->setText(valChar); + sprintf(valChar, "%d", txtail[0]); Dlg->TXTailA->setText(valChar); sprintf(valChar, "%d", txtail[1]); @@ -1255,6 +1410,7 @@ void QtSoundModem::doModems() Dlg->CWIDCall->setText(CWIDCall); Dlg->CWIDInterval->setText(QString::number(CWIDInterval)); + Dlg->CWIDMark->setText(CWIDMark); if (CWIDType) Dlg->radioButton_2->setChecked(1); @@ -1396,6 +1552,23 @@ void QtSoundModem::modemSave() Q = Dlg->TXDelayD->text(); txdelay[3] = Q.toInt(); + Q = Dlg->MaxFrameA->text(); + maxframe[0] = Q.toInt(); + + Q = Dlg->MaxFrameB->text(); + maxframe[1] = Q.toInt(); + + Q = Dlg->MaxFrameC->text(); + maxframe[2] = Q.toInt(); + + Q = Dlg->MaxFrameD->text(); + maxframe[3] = Q.toInt(); + + if (maxframe[0] == 0 || maxframe[0] > 7) maxframe[0] = 3; + if (maxframe[1] == 0 || maxframe[1] > 7) maxframe[1] = 3; + if (maxframe[2] == 0 || maxframe[2] > 7) maxframe[2] = 3; + if (maxframe[3] == 0 || maxframe[3] > 7) maxframe[3] = 3; + Q = Dlg->TXTailA->text(); txtail[0] = Q.toInt(); @@ -1458,6 +1631,7 @@ void QtSoundModem::modemSave() strcpy(CWIDCall, Dlg->CWIDCall->text().toUtf8().toUpper()); + strcpy(CWIDMark, Dlg->CWIDMark->text().toUtf8().toUpper()); CWIDInterval = Dlg->CWIDInterval->text().toInt(); CWIDType = Dlg->radioButton_2->isChecked(); @@ -2091,10 +2265,10 @@ void QtSoundModem::deviceaccept() InitSound(1); } - // Reset title and tooltip in case ports changed + // Reset title and tooltip in case ports changed char Title[128]; - sprintf(Title, "QtSoundModem Version %s Ports %d/%d", VersionString, AGWPort, KISSPort); + sprintf(Title, "QtSoundModem Version %s Ports %d%s/%d%s", VersionString, AGWPort, AGWServ ? "*": "", KISSPort, KISSServ ? "*" : ""); w->setWindowTitle(Title); sprintf(Title, "QtSoundModem %d %d", AGWPort, KISSPort); @@ -2517,6 +2691,7 @@ extern "C" void doWaterfall(int snd_ch) extern "C" float aFFTAmpl[1024]; +extern "C" void SMUpdateBusyDetector(int LR, float * Real, float *Imag); void doWaterfallThread(void * param) { @@ -2617,6 +2792,8 @@ void doWaterfallThread(void * param) } } + SMUpdateBusyDetector(snd_ch, RealOut, ImagOut); + if (bm == 0) return; @@ -2875,3 +3052,82 @@ void QtSoundModem::onTEselectionChanged() x->copy(); } +#define ConstellationHeight 121 +#define ConstellationWidth 121 +#define PLOTRADIUS 60 + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +extern "C" int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count) +{ + // Subroutine to update bmpConstellation plot for PSK modes... + // Skip plotting and calculations of intPSKPhase(0) as this is a reference phase (9/30/2014) + + float dblPhaseError; + float dblPhaseErrorSum = 0; + float intP = 0; + float dblRad = 0; + float dblAvgRad = 0; + float dbPhaseStep; + float MagMax = 0; + float dblPlotRotation = 0; + + int i, intQuality; + + int intX, intY; + int yCenter = (ConstellationHeight - 2) / 2; + int xCenter = (ConstellationWidth - 2) / 2; + + Constellation[chan]->fill(black); + + for (i = 0; i < 120; i++) + { + Constellation[chan]->setPixel(xCenter, i, cyan); + Constellation[chan]->setPixel(i, xCenter, cyan); + } + + if (Count == 0) + return 0; + + dbPhaseStep = 2 * M_PI / intPSKPhase; + + for (i = 1; i < Count; i++) // Don't plot the first phase (reference) + { + MagMax = MAX(MagMax, Mags[i]); // find the max magnitude to auto scale + dblAvgRad += Mags[i]; + } + + dblAvgRad = dblAvgRad / Count; // the average radius + + for (i = 0; i < Count; i++) + { + dblRad = PLOTRADIUS * Mags[i] / MagMax; // scale the radius dblRad based on intMag + intP = round((Phases[i]) / dbPhaseStep); + + // compute the Phase error + + dblPhaseError = fabsf(Phases[i] - intP * dbPhaseStep); // always positive and < .5 * dblPhaseStep + dblPhaseErrorSum += dblPhaseError; + + intX = xCenter + dblRad * cosf(dblPlotRotation + Phases[i]); + intY = yCenter + dblRad * sinf(dblPlotRotation + Phases[i]); + + if (intX > 0 && intY > 0) + if (intX != xCenter && intY != yCenter) + Constellation[chan]->setPixel(intX, intY, yellow); + } + + dblAvgRad = dblAvgRad / Count; // the average radius + + intQuality = MAX(0, ((100 - 200 * (dblPhaseErrorSum / (Count)) / dbPhaseStep))); // ignore radius error for (PSK) but include for QAM + + char QualText[64]; + sprintf(QualText, "Chan %c Qual = %d", chan + 'A', intQuality); + QualLabel[chan]->setText(QualText); + constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan])); +// constellationDialog[chan]->setWindowTitle(QualText); + return intQuality; +} + + + diff --git a/QtSoundModem.pro b/QtSoundModem.pro index db1a6af..a02654c 100644 --- a/QtSoundModem.pro +++ b/QtSoundModem.pro @@ -43,7 +43,8 @@ SOURCES += ./audio.c \ ./Modulate.c \ ./ofdm.c \ ./pktARDOP.c \ - ./BusyDetect.c + ./BusyDetect.c \ + ./DW9600.c diff --git a/QtSoundModem.ui b/QtSoundModem.ui index 65fd2ea..ef72635 100644 --- a/QtSoundModem.ui +++ b/QtSoundModem.ui @@ -124,9 +124,9 @@ - 0 + 80 488 - 953 + 881 80 diff --git a/QtSoundModem.ui.bak b/QtSoundModem.ui.bak deleted file mode 100644 index 90b4371..0000000 --- a/QtSoundModem.ui.bak +++ /dev/null @@ -1,516 +0,0 @@ - - - QtSoundModemClass - - - - 0 - 0 - 962 - 721 - - - - - 1024 - 16777215 - - - - QtSoundModem - - - - :/QtSoundModem/soundmodem.ico:/QtSoundModem/soundmodem.ico - - - - - - 174 - 6 - 56 - 22 - - - - 3000 - - - 1500 - - - - - - 316 - 6 - 145 - 22 - - - - - - - 0 - 586 - 959 - 80 - - - - - 0 - 0 - - - - - 600 - 80 - - - - - 5000 - 100 - - - - - 8 - - - - QFrame::Box - - - QFrame::Plain - - - - - - - - - 6 - 7 - 16 - 18 - - - - A: - - - - - - 468 - 6 - 56 - 22 - - - - 3000 - - - 1500 - - - - - - 0 - 488 - 953 - 80 - - - - - 600 - 80 - - - - - 5000 - 100 - - - - - 8 - - - - QFrame::Box - - - QFrame::Plain - - - - - - - - - 22 - 6 - 145 - 22 - - - - - - - 690 - 0 - 73 - 16 - - - - DCD Level - - - Qt::AlignCenter - - - - - - 300 - 9 - 16 - 14 - - - - B: - - - - - - 690 - 18 - 73 - 14 - - - - Qt::Horizontal - - - QSlider::NoTicks - - - 10 - - - - - - 782 - 6 - 93 - 22 - - - - Hold Pointers - - - - - - 25 - 460 - 1076 - 30 - - - - - 600 - 10 - - - - - 5000 - 100 - - - - - 8 - - - - QFrame::Box - - - QFrame::Plain - - - fall - - - - - - 10 - 560 - 1076 - 30 - - - - - 600 - 10 - - - - - 5000 - 100 - - - - - 8 - - - - QFrame::Box - - - QFrame::Plain - - - fall - - - - - - -6 - 60 - 971 - 201 - - - - true - - - - - - 5 - 32 - 16 - 18 - - - - C: - - - - - - 22 - 31 - 145 - 22 - - - - - - - 468 - 31 - 56 - 22 - - - - 3000 - - - 1500 - - - - - - 316 - 31 - 145 - 22 - - - - - - - 298 - 33 - 16 - 14 - - - - D: - - - - - - 174 - 31 - 56 - 22 - - - - 3000 - - - 1500 - - - - - - 600 - 18 - 63 - 14 - - - - -200 - - - 200 - - - 0 - - - Qt::Horizontal - - - QSlider::NoTicks - - - 10 - - - - - - 600 - 2 - 87 - 16 - - - - RX Offset 0 - - - - - - 238 - 6 - 37 - 22 - - - - 0 - - - Qt::AlignCenter - - - - - - 532 - 6 - 37 - 22 - - - - 0 - - - Qt::AlignCenter - - - - - - 238 - 31 - 37 - 22 - - - - 0 - - - Qt::AlignCenter - - - - - - 532 - 31 - 37 - 20 - - - - 0 - - - Qt::AlignCenter - - - - - - - 0 - 0 - 962 - 21 - - - - - - - - - - diff --git a/QtSoundModem.vcxproj b/QtSoundModem.vcxproj index 93bbb10..7f39cd1 100644 --- a/QtSoundModem.vcxproj +++ b/QtSoundModem.vcxproj @@ -1,6 +1,10 @@ п»ї + + Debug + x64 + Release Win32 @@ -9,12 +13,16 @@ Debug Win32 + + Release + x64 + {4EDE958E-D0AC-37B4-81F7-78313A262DCD} QtSoundModem QtVS_v304 - 10.0.19041.0 + 10.0.17763.0 10.0.19041.0 $(MSBuildProjectDirectory)\QtMsBuild @@ -28,6 +36,15 @@ release\ QtSoundModem + + v141 + release\ + false + NotSet + Application + release\ + QtSoundModem + v141 debug\ @@ -37,6 +54,15 @@ debug\ QtSoundModem + + v141 + debug\ + false + NotSet + Application + debug\ + QtSoundModem + @@ -45,9 +71,15 @@ + + + + + + @@ -59,6 +91,13 @@ true true + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\ + QtSoundModem + true + true + $(SolutionDir)$(Platform)\$(Configuration)\ $(SolutionDir)Intermed\$(Platform)\$(Configuration)\\ @@ -66,14 +105,29 @@ true false + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\\ + QtSoundModem + true + false + 5.14.2 core;network;gui;widgets;serialport + + msvc 2017 5.1464 + core;network;gui;widgets;serialport + 5.14.2 core;network;gui;widgets;serialport + + 5.14.2 + core;network;gui;widgets;serialport + @@ -140,6 +194,69 @@ ui_%(Filename).h + + + rsid;.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;release;/include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + MaxSpeed + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + false + $(OutDir) + MultiThreadedDLL + true + true + Level3 + true + + + libfftw3f-3.lib;shell32.lib;setupapi.lib;WS2_32.Lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + true + $(OutDir)QtSoundModem.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtSoundModem + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;debug;/include;rsid;%(AdditionalIncludeDirectories) @@ -202,13 +319,77 @@ ui_%(Filename).h + + + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;debug;/include;rsid;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + EditAndContinue + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + Disabled + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + true + $(OutDir) + + + libfftw3f-64-3.lib;shell32.lib;setupapi.lib;WS2_32.Lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\QtSoundModem.exe + true + Windows + true + false + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtSoundModem + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + + CompileAsC + CompileAsC @@ -247,18 +428,15 @@ Document true + true $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h Generate moc_predefs.h + Generate moc_predefs.h debug\moc_predefs.h;%(Outputs) - - - Document - $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) - cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h - Generate moc_predefs.h - release\moc_predefs.h;%(Outputs) - true + debug\moc_predefs.h;%(Outputs) diff --git a/QtSoundModem.vcxproj-DESKTOP-MHE5LO8.user b/QtSoundModem.vcxproj-DESKTOP-MHE5LO8.user new file mode 100644 index 0000000..a71d175 --- /dev/null +++ b/QtSoundModem.vcxproj-DESKTOP-MHE5LO8.user @@ -0,0 +1,34 @@ +п»ї + + + C:\DevProgs\BPQ32\SMTest + WindowsLocalDebugger + + + + + c:\devprogs\bpq32\SMSAT2 + WindowsLocalDebugger + < d:\samples.wav + + + .\debug + WindowsLocalDebugger + + + C:\DevProgs\BPQ32\SMSat + WindowsLocalDebugger + + + 2023-08-21T20:12:53.1523329Z + + + 2022-03-11T19:38:31.5906689Z + + + 2023-08-18T07:29:42.4175478Z + + + 2022-03-11T19:38:33.3845083Z + + \ No newline at end of file diff --git a/QtSoundModem.vcxproj.filters b/QtSoundModem.vcxproj.filters index 3e97db7..c50daf6 100644 --- a/QtSoundModem.vcxproj.filters +++ b/QtSoundModem.vcxproj.filters @@ -137,6 +137,9 @@ Source Files + + Source Files + @@ -153,9 +156,6 @@ Generated Files - - Generated Files - diff --git a/QtSoundModem.vcxproj.user b/QtSoundModem.vcxproj.user index 516cab4..b2b9590 100644 --- a/QtSoundModem.vcxproj.user +++ b/QtSoundModem.vcxproj.user @@ -1,7 +1,7 @@ п»ї - C:\DevProgs\BPQ32\SM2 + C:\DevProgs\BPQ32\SMTest WindowsLocalDebugger @@ -20,15 +20,15 @@ WindowsLocalDebugger - 2023-06-27T07:43:13.0567353Z + 2023-08-31T18:31:29.1703485Z - 2022-03-11T19:38:31.5906689Z + 2023-08-31T18:31:29.3763536Z - 2023-06-27T07:43:13.1602990Z + 2023-08-31T18:31:30.2753833Z - 2022-03-11T19:38:33.3845083Z + 2023-08-31T18:31:32.1264353Z \ No newline at end of file diff --git a/SMMain.c.bak b/SMMain-DESKTOP-MHE5LO8.c similarity index 73% rename from SMMain.c.bak rename to SMMain-DESKTOP-MHE5LO8.c index 652911e..8fc249e 100644 --- a/SMMain.c.bak +++ b/SMMain-DESKTOP-MHE5LO8.c @@ -27,6 +27,7 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include "hidapi.h" #include #include +#include BOOL KISSServ; int KISSPort; @@ -62,13 +63,24 @@ extern int pnt_change[5]; // Freq Changed Flag fftwf_complex *in, *out; fftwf_plan p; -#define N 2048 +int FFTSize = 4096; + +char * Wisdom; 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); + fftwf_import_wisdom_from_string(Wisdom); + fftwf_set_timelimit(30); + +#ifndef WIN32 + printf("It may take up to 30 seconds for the program to start for the first time\n"); +#endif + + in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * 10000); + out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * 10000); + p = fftwf_plan_dft_1d(FFTSize, in, out, FFTW_FORWARD, FFTW_PATIENT); + + Wisdom = fftwf_export_wisdom_to_string(); } void dofft(short * inp, float * outr, float * outi) @@ -77,7 +89,7 @@ void dofft(short * inp, float * outr, float * outi) fftwf_complex * fft = in; - for (i = 0; i < N; i++) + for (i = 0; i < FFTSize; i++) { fft[0][0] = inp[0] * 1.0f; fft[0][1] = 0; @@ -89,7 +101,7 @@ void dofft(short * inp, float * outr, float * outi) fft = out; - for (i = 0; i < N; i++) + for (i = 0; i < FFTSize; i++) { outr[0] = fft[0][0]; outi[0] = fft[0][1]; @@ -165,6 +177,18 @@ void SampleSink(int LR, short Sample) DMABuffer[1 + 2 * Number] = 0; } } + if (using48000) + { + // Need to upsample to 48K. Try just duplicating sample + + uint32_t * ptr = &DMABuffer[2 * Number]; + + *(&ptr[1]) = *(ptr); + *(&ptr[2]) = *(ptr); + *(&ptr[3]) = *(ptr); + + Number += 3; + } Number++; #endif if (Number >= SendSize) @@ -321,194 +345,171 @@ extern UCHAR * pixelPointer; #endif extern int blnBusyStatus; -BusyDet = 0; +BusyDet = 5; #define PLOTWATERFALL int WaterfallActive = 1; int SpectrumActive; -/* +float BinSize; -void UpdateBusyDetector(short * bytNewSamples) +extern int intLastStart; +extern int intLastStop; + +void SMSortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin); + + +BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop) // this only called while searching for leader ...once leader detected, no longer called. { - 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 + // First sort signals and look at highes signals:baseline ratio.. - static BOOL blnLastBusyStatus; + float dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide; + float dblSlowAlpha = 0.2f; + float dblAvgStoNNarrow = 0, dblAvgStoNWide = 0; + int intNarrow = 8; // 8 x 11.72 Hz about 94 z + int intWide = ((intStop - intStart) * 2) / 3; //* 0.66); + int blnBusy = FALSE; + int BusyDet4th = BusyDet * BusyDet * BusyDet * BusyDet; + + // First sort signals and look at highest signals:baseline ratio.. + // First narrow band (~94Hz) + + SMSortSignals2(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow); + + if (intLastStart == intStart && intLastStop == intStop) + dblAvgStoNNarrow = (1 - dblSlowAlpha) * dblAvgStoNNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; + else + { + // This initializes the Narrow average after a bandwidth change + + dblAvgStoNNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; + intLastStart = intStart; + intLastStop = intStop; + } + + // Wide band (66% of current bandwidth) + + SMSortSignals2(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide); + + if (intLastStart == intStart && intLastStop == intStop) + dblAvgStoNWide = (1 - dblSlowAlpha) * dblAvgStoNWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide; + else + { + // This initializes the Wide average after a bandwidth change + + dblAvgStoNWide = dblAVGSignalPerBinWide / dblAVGBaselineWide; + intLastStart = intStart; + intLastStop = intStop; + } + + // Preliminary calibration...future a function of bandwidth and BusyDet. + + + blnBusy = (dblAvgStoNNarrow > (3 + 0.008 * BusyDet4th)) || (dblAvgStoNWide > (5 + 0.02 * BusyDet4th)); + +// if (BusyDet == 0) +// blnBusy = FALSE; // 0 Disables check ?? Is this the best place to do this? + +// WriteDebugLog(LOGDEBUG, "Busy %d Wide %f Narrow %f", blnBusy, dblAvgStoNWide, dblAvgStoNNarrow); + + return blnBusy; +} + +extern int compare(const void *p1, const void *p2); + +void SMSortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin) +{ + // puts the top intNumber of bins between intStartBin and intStopBin into dblAVGSignalPerBin, the rest into dblAvgBaselinePerBin + // for decent accuracy intNumBins should be < 75% of intStopBin-intStartBin) + + // This version uses a native sort function which is much faster and reduces CPU loading significantly on wide bandwidths. + + float dblSort[8192]; + float dblSum1 = 0, dblSum2 = 0; + int numtoSort = (intStopBin - intStartBin) + 1, i; + + memcpy(dblSort, &dblMag[intStartBin], numtoSort * sizeof(float)); + + qsort((void *)dblSort, numtoSort, sizeof(float), compare); + + for (i = numtoSort - 1; i >= 0; i--) + { + if (i >= (numtoSort - intNumBins)) + dblSum1 += dblSort[i]; + else + dblSum2 += dblSort[i]; + } + + *dblAVGSignalPerBin = dblSum1 / intNumBins; + *dblAVGBaselinePerBin = dblSum2 / (intStopBin - intStartBin - intNumBins - 1); +} + + + +void SMUpdateBusyDetector(int LR, float * Real, float *Imag) +{ + // Energy based detector for each channel. + // Fed from FFT generated for waterfall display + // FFT size is 4096 + + float dblMag[4096]; + + static BOOL blnLastBusyStatus[4]; float dblMagAvg = 0; int intTuneLineLow, intTuneLineHi, intDelta; - int i; + int i, chan; - // if (State != SearchingForLeader) - // return; // only when looking for leader + return; - if (Now - LastBusyCheck < 100) + if (Now - LastBusyCheck < 100) // ?? return; LastBusyCheck = Now; - FourierTransform(1024, bytNewSamples, &dblReF[0], &dblImF[0], FALSE); + // We need to run busy test on the frequncies used by each modem. - for (i = 0; i < 206; i++) + for (chan = 0; chan < 4; chan++) { - // starting at ~300 Hz to ~2700 Hz Which puts the center of the signal in the center of the window (~1500Hz) + int Low, High, Start, End; - 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 - } + if (soundChannel[chan] != (LR + 1)) // on this side of soundcard + continue; - // LookforPacket(dblMag, dblMagAvg, 206, &dblReF[25], &dblImF[25]); - // packet_process_samples(bytNewSamples, 1200); + Low = tx_freq[chan] - txbpf[chan] / 2; + High = tx_freq[chan] + txbpf[chan] / 2; - intDelta = roundf(500 / 2) + 50 / 11.719f; + // BinSize is width of each fft bin in Hz - intTuneLineLow = max((103 - intDelta), 3); - intTuneLineHi = min((103 + intDelta), 203); + Start = (Low / BinSize); // First and last bins to process + End = (High / BinSize); -// if (ProtocolState == DISC) // ' Only process busy when in DISC state - { - // blnBusyStatus = BusyDetect3(dblMag, intTuneLineLow, intTuneLineHi); - if (blnBusyStatus && !blnLastBusyStatus) + for (i = Start; i < End; i++) { -// QueueCommandToHost("BUSY TRUE"); -// newStatus = TRUE; // report to PTC - - if (!WaterfallActive && !SpectrumActive) - { - UCHAR Msg[2]; - -// Msg[0] = blnBusyStatus; -// SendtoGUI('B', Msg, 1); - } + dblMag[i] = powf(Real[i], 2) + powf(Imag[i], 2); // first pass + dblMagAvg += dblMag[i]; } - // stcStatus.Text = "TRUE" - // queTNCStatus.Enqueue(stcStatus) - // 'Debug.WriteLine("BUSY TRUE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) - else if (blnLastBusyStatus && !blnBusyStatus) + blnBusyStatus = SMBusyDetect3(dblMag, Start, End); + + if (blnBusyStatus && !blnLastBusyStatus[chan]) { -// QueueCommandToHost("BUSY FALSE"); -// newStatus = TRUE; // report to PTC - - if (!WaterfallActive && !SpectrumActive) - { - UCHAR Msg[2]; - - Msg[0] = blnBusyStatus; -// SendtoGUI('B', Msg, 1); - } + Debugprintf("Ch %d Busy True", chan); + } + else if (blnLastBusyStatus[chan] && !blnBusyStatus) + { + Debugprintf("Ch %d Busy False", chan); } // 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 + blnLastBusyStatus[chan] = blnBusyStatus; } } -*/ extern short rawSamples[2400]; // Get Frame Type need 2400 and we may add 1200 int rawSamplesLength = 0; @@ -600,6 +601,7 @@ 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; @@ -628,6 +630,7 @@ int ARDOPSendToCard(int Chan, int Len) } } } + DMABuffer = SendtoCard(DMABuffer, Len); ARDOPTXPtr[Chan] += Len; @@ -669,6 +672,8 @@ void DoTX(int Chan) Flush(); Debugprintf("TX Complete"); RadioPTT(0, 0); + Continuation[Chan] = 0; + tx_status[Chan] = TX_SILENCE; // We should now send any ackmode acks as the channel is now free for dest to reply @@ -680,7 +685,7 @@ void DoTX(int Chan) { // Continue the send - if (modem_mode[Chan] == MODE_ARDOP) + if (modem_mode[Chan] == MODE_ARDOP || modem_mode[Chan] == MODE_RUH) { // if (SeeIfCardBusy()) // return 0; @@ -699,6 +704,8 @@ void DoTX(int Chan) SoundIsPlaying = TRUE; Number = 0; + Continuation[Chan] = 1; + Debugprintf("TX Continuing"); string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message @@ -728,12 +735,17 @@ void DoTX(int Chan) put_frame(Chan, tx_data, "", TRUE, FALSE); - PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + if (modem_mode[Chan] == MODE_ARDOP) + PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + else + RUHEncode(tx_data->Data, tx_data->Length - 2, Chan); freeString(tx_data); // Samples are now in DMABuffer = Send first block + DMABuffer = SoundInit(); + ARDOPSendToCard(Chan, SendSize); tx_status[Chan] = TX_FRAME; return; @@ -741,6 +753,8 @@ void DoTX(int Chan) Debugprintf("TX Complete"); RadioPTT(0, 0); + Continuation[Chan] = 0; + tx_status[Chan] = TX_SILENCE; // We should now send any ackmode acks as the channel is now free for dest to reply @@ -845,6 +859,50 @@ void DoTX(int Chan) ARDOPSendToCard(Chan, SendSize); tx_status[Chan] = TX_FRAME; + } + else if (modem_mode[Chan] == MODE_RUH) + { + // Same as for ARDOP. Generate a whole frame of samples + // then send them out a bit at a time to avoid stopping here + + // We allow two RUH modems + + 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); + + RUHEncode(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); @@ -852,16 +910,6 @@ void DoTX(int Chan) 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) @@ -1146,6 +1194,8 @@ void CM108_set_ptt(int PTTState) void RadioPTT(int snd_ch, BOOL PTTState) { + snd_status[snd_ch] = PTTState; // SND_IDLE = 0 SND_TX = 1 + #ifdef __ARM_ARCH if (useGPIO) { diff --git a/SMMain.c b/SMMain.c index b0edb42..3c87359 100644 --- a/SMMain.c +++ b/SMMain.c @@ -27,6 +27,7 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include "hidapi.h" #include #include +#include BOOL KISSServ; int KISSPort; @@ -40,6 +41,9 @@ int SoundIsPlaying = 0; int UDPSoundIsPlaying = 0; int Capturing = 0; +int txmin = 0; +int txmax = 0; + extern unsigned short buffer[2][1200]; extern int SoundMode; extern int needRSID[4]; @@ -176,6 +180,18 @@ void SampleSink(int LR, short Sample) DMABuffer[1 + 2 * Number] = 0; } } + if (using48000) + { + // Need to upsample to 48K. Try just duplicating sample + + uint32_t * ptr = &DMABuffer[2 * Number]; + + *(&ptr[1]) = *(ptr); + *(&ptr[2]) = *(ptr); + *(&ptr[3]) = *(ptr); + + Number += 3; + } Number++; #endif if (Number >= SendSize) @@ -332,194 +348,171 @@ extern UCHAR * pixelPointer; #endif extern int blnBusyStatus; -BusyDet = 0; +BusyDet = 5; #define PLOTWATERFALL int WaterfallActive = 1; int SpectrumActive; -/* +float BinSize; -void UpdateBusyDetector(short * bytNewSamples) +extern int intLastStart; +extern int intLastStop; + +void SMSortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin); + + +BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop) // this only called while searching for leader ...once leader detected, no longer called. { - 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 + // First sort signals and look at highes signals:baseline ratio.. - static BOOL blnLastBusyStatus; + float dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide; + float dblSlowAlpha = 0.2f; + float dblAvgStoNNarrow = 0, dblAvgStoNWide = 0; + int intNarrow = 8; // 8 x 11.72 Hz about 94 z + int intWide = ((intStop - intStart) * 2) / 3; //* 0.66); + int blnBusy = FALSE; + int BusyDet4th = BusyDet * BusyDet * BusyDet * BusyDet; + + // First sort signals and look at highest signals:baseline ratio.. + // First narrow band (~94Hz) + + SMSortSignals2(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow); + + if (intLastStart == intStart && intLastStop == intStop) + dblAvgStoNNarrow = (1 - dblSlowAlpha) * dblAvgStoNNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; + else + { + // This initializes the Narrow average after a bandwidth change + + dblAvgStoNNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; + intLastStart = intStart; + intLastStop = intStop; + } + + // Wide band (66% of current bandwidth) + + SMSortSignals2(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide); + + if (intLastStart == intStart && intLastStop == intStop) + dblAvgStoNWide = (1 - dblSlowAlpha) * dblAvgStoNWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide; + else + { + // This initializes the Wide average after a bandwidth change + + dblAvgStoNWide = dblAVGSignalPerBinWide / dblAVGBaselineWide; + intLastStart = intStart; + intLastStop = intStop; + } + + // Preliminary calibration...future a function of bandwidth and BusyDet. + + + blnBusy = (dblAvgStoNNarrow > (3 + 0.008 * BusyDet4th)) || (dblAvgStoNWide > (5 + 0.02 * BusyDet4th)); + +// if (BusyDet == 0) +// blnBusy = FALSE; // 0 Disables check ?? Is this the best place to do this? + +// WriteDebugLog(LOGDEBUG, "Busy %d Wide %f Narrow %f", blnBusy, dblAvgStoNWide, dblAvgStoNNarrow); + + return blnBusy; +} + +extern int compare(const void *p1, const void *p2); + +void SMSortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin) +{ + // puts the top intNumber of bins between intStartBin and intStopBin into dblAVGSignalPerBin, the rest into dblAvgBaselinePerBin + // for decent accuracy intNumBins should be < 75% of intStopBin-intStartBin) + + // This version uses a native sort function which is much faster and reduces CPU loading significantly on wide bandwidths. + + float dblSort[8192]; + float dblSum1 = 0, dblSum2 = 0; + int numtoSort = (intStopBin - intStartBin) + 1, i; + + memcpy(dblSort, &dblMag[intStartBin], numtoSort * sizeof(float)); + + qsort((void *)dblSort, numtoSort, sizeof(float), compare); + + for (i = numtoSort - 1; i >= 0; i--) + { + if (i >= (numtoSort - intNumBins)) + dblSum1 += dblSort[i]; + else + dblSum2 += dblSort[i]; + } + + *dblAVGSignalPerBin = dblSum1 / intNumBins; + *dblAVGBaselinePerBin = dblSum2 / (intStopBin - intStartBin - intNumBins - 1); +} + + + +void SMUpdateBusyDetector(int LR, float * Real, float *Imag) +{ + // Energy based detector for each channel. + // Fed from FFT generated for waterfall display + // FFT size is 4096 + + float dblMag[4096]; + + static BOOL blnLastBusyStatus[4]; float dblMagAvg = 0; int intTuneLineLow, intTuneLineHi, intDelta; - int i; + int i, chan; - // if (State != SearchingForLeader) - // return; // only when looking for leader + return; - if (Now - LastBusyCheck < 100) + if (Now - LastBusyCheck < 100) // ?? return; LastBusyCheck = Now; - FourierTransform(1024, bytNewSamples, &dblReF[0], &dblImF[0], FALSE); + // We need to run busy test on the frequncies used by each modem. - for (i = 0; i < 206; i++) + for (chan = 0; chan < 4; chan++) { - // starting at ~300 Hz to ~2700 Hz Which puts the center of the signal in the center of the window (~1500Hz) + int Low, High, Start, End; - 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 - } + if (soundChannel[chan] != (LR + 1)) // on this side of soundcard + continue; - // LookforPacket(dblMag, dblMagAvg, 206, &dblReF[25], &dblImF[25]); - // packet_process_samples(bytNewSamples, 1200); + Low = tx_freq[chan] - txbpf[chan] / 2; + High = tx_freq[chan] + txbpf[chan] / 2; - intDelta = roundf(500 / 2) + 50 / 11.719f; + // BinSize is width of each fft bin in Hz - intTuneLineLow = max((103 - intDelta), 3); - intTuneLineHi = min((103 + intDelta), 203); + Start = (Low / BinSize); // First and last bins to process + End = (High / BinSize); -// if (ProtocolState == DISC) // ' Only process busy when in DISC state - { - // blnBusyStatus = BusyDetect3(dblMag, intTuneLineLow, intTuneLineHi); - if (blnBusyStatus && !blnLastBusyStatus) + for (i = Start; i < End; i++) { -// QueueCommandToHost("BUSY TRUE"); -// newStatus = TRUE; // report to PTC - - if (!WaterfallActive && !SpectrumActive) - { - UCHAR Msg[2]; - -// Msg[0] = blnBusyStatus; -// SendtoGUI('B', Msg, 1); - } + dblMag[i] = powf(Real[i], 2) + powf(Imag[i], 2); // first pass + dblMagAvg += dblMag[i]; } - // stcStatus.Text = "TRUE" - // queTNCStatus.Enqueue(stcStatus) - // 'Debug.WriteLine("BUSY TRUE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) - else if (blnLastBusyStatus && !blnBusyStatus) + blnBusyStatus = SMBusyDetect3(dblMag, Start, End); + + if (blnBusyStatus && !blnLastBusyStatus[chan]) { -// QueueCommandToHost("BUSY FALSE"); -// newStatus = TRUE; // report to PTC - - if (!WaterfallActive && !SpectrumActive) - { - UCHAR Msg[2]; - - Msg[0] = blnBusyStatus; -// SendtoGUI('B', Msg, 1); - } + Debugprintf("Ch %d Busy True", chan); + } + else if (blnLastBusyStatus[chan] && !blnBusyStatus) + { + Debugprintf("Ch %d Busy False", chan); } // 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 + blnLastBusyStatus[chan] = blnBusyStatus; } } -*/ extern short rawSamples[2400]; // Get Frame Type need 2400 and we may add 1200 int rawSamplesLength = 0; @@ -554,9 +547,12 @@ int Freq_Change(int Chan, int Freq) { int low, high; - low = round(rx_shift[1] / 2 + RCVR[Chan] * rcvr_offset[Chan] + 1); + low = round(rx_shift[Chan] / 2 + (RCVR[Chan] * rcvr_offset[Chan])); high = round(RX_Samplerate / 2 - (rx_shift[Chan] / 2 + RCVR[Chan] * rcvr_offset[Chan])); + if (Freq < 300) + return rx_freq[Chan]; // Dont allow change + if (Freq < low) return rx_freq[Chan]; // Dont allow change @@ -611,6 +607,7 @@ 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; @@ -639,6 +636,7 @@ int ARDOPSendToCard(int Chan, int Len) } } } + DMABuffer = SendtoCard(DMABuffer, Len); ARDOPTXPtr[Chan] += Len; @@ -679,7 +677,9 @@ void DoTX(int Chan) { Flush(); Debugprintf("TX Complete"); - RadioPTT(0, 0); + RadioPTT(Chan, 0); + Continuation[Chan] = 0; + tx_status[Chan] = TX_SILENCE; // We should now send any ackmode acks as the channel is now free for dest to reply @@ -691,7 +691,7 @@ void DoTX(int Chan) { // Continue the send - if (modem_mode[Chan] == MODE_ARDOP) + if (modem_mode[Chan] == MODE_ARDOP || modem_mode[Chan] == MODE_RUH) { // if (SeeIfCardBusy()) // return 0; @@ -710,6 +710,8 @@ void DoTX(int Chan) SoundIsPlaying = TRUE; Number = 0; + Continuation[Chan] = 1; + Debugprintf("TX Continuing"); string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message @@ -739,19 +741,26 @@ void DoTX(int Chan) put_frame(Chan, tx_data, "", TRUE, FALSE); - PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + if (modem_mode[Chan] == MODE_ARDOP) + PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + else + RUHEncode(tx_data->Data, tx_data->Length - 2, Chan); freeString(tx_data); // Samples are now in DMABuffer = Send first block + DMABuffer = SoundInit(); + ARDOPSendToCard(Chan, SendSize); tx_status[Chan] = TX_FRAME; return; } Debugprintf("TX Complete"); - RadioPTT(0, 0); + RadioPTT(Chan, 0); + Continuation[Chan] = 0; + tx_status[Chan] = TX_SILENCE; // We should now send any ackmode acks as the channel is now free for dest to reply @@ -856,6 +865,50 @@ void DoTX(int Chan) ARDOPSendToCard(Chan, SendSize); tx_status[Chan] = TX_FRAME; + } + else if (modem_mode[Chan] == MODE_RUH) + { + // Same as for ARDOP. Generate a whole frame of samples + // then send them out a bit at a time to avoid stopping here + + // We allow two RUH modems + + 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); + + RUHEncode(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); @@ -863,16 +916,6 @@ void DoTX(int Chan) 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) @@ -1153,10 +1196,24 @@ void CM108_set_ptt(int PTTState) } - +float amplitudes[4] = { 32767, 32767, 32767, 32767 }; +extern float amplitude; void RadioPTT(int snd_ch, BOOL PTTState) { + snd_status[snd_ch] = PTTState; // SND_IDLE = 0 SND_TX = 1 + + if (PTTState) + { + txmax = txmin = 0; + amplitude = amplitudes[snd_ch]; + } + else + { + Debugprintf("Output peaks = %d, %d, amp %f", txmin, txmax, amplitude); + amplitudes[snd_ch] = amplitude; + } + #ifdef __ARM_ARCH if (useGPIO) { diff --git a/ShowFilter.cpp.bak b/ShowFilter.cpp.bak deleted file mode 100644 index cc18ea0..0000000 --- a/ShowFilter.cpp.bak +++ /dev/null @@ -1,234 +0,0 @@ -/* -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 - -// This displays a graph of the filter characteristics - -#define c3 -1.5000000000000E+00f // cos(2*pi / 3) - 1; -#define c32 8.6602540378444E-01f // sin(2*pi / 3); - -#define u5 1.2566370614359E+00f // 2*pi / 5; -#define c51 -1.2500000000000E+00f // (cos(u5) + cos(2*u5))/2 - 1; -#define c52 5.5901699437495E-01f // (cos(u5) - cos(2*u5))/2; -#define c53 -9.5105651629515E-0f //- sin(u5); -#define c54 -1.5388417685876E+00f //-(sin(u5) + sin(2*u5)); -#define c55 3.6327126400268E-01f // (sin(u5) - sin(2*u5)); -#define c8 = 7.0710678118655E-01f // 1 / sqrt(2); - - -float pnt_graph_buf[4096]; -float graph_buf[4096]; -float prev_graph_buf[4096]; -float src_graph_buf[4096]; -float graph_f; -float RealOut[4096]; -short RealIn[4096]; -float ImagOut[4096]; - -#define Image1Width 642 -#define Image1Height 312 - -void filter_grid(QPainter * Painter) -{ - int col = 20; - int row = 8; - int top_margin = 10; - int bottom_margin = 20; - int left_margin = 30; - int right_margin = 10; - - int x, y; - float kx, ky; - - QPen pen; // creates a default pen - - pen.setStyle(Qt::DotLine); - Painter->setPen(pen); - - - ky = 35; - - kx = (Image1Width - left_margin - right_margin - 2) / col; - - for (y = 0; y < row; y++) - { - Painter->drawLine( - left_margin + 1, - top_margin + round(ky*y) + 1, - Image1Width - right_margin - 1, - top_margin + round(ky*y) + 1); - } - - for (x = 0; x < col; x++) - { - Painter->drawLine( - left_margin + round(kx*x) + 1, - top_margin + 1, - left_margin + round(kx*x) + 1, - Image1Height - bottom_margin - 1); - } - - pen.setStyle(Qt::SolidLine); - Painter->setPen(pen); - - for (y = 0; y < row / 2; y++) - { - char Textxx[20]; - - sprintf(Textxx, "%d", y * -20); - - Painter->drawLine( - left_margin + 1, - top_margin + round(ky*y * 2) + 1, - Image1Width - right_margin - 1, - top_margin + round(ky*y * 2) + 1); - - Painter->drawText( - 1, - top_margin + round(ky*y * 2) + 1, - 100, 20, 0, Textxx); - - } - - - for (x = 0; x <= col / 5; x++) - { - char Textxx[20]; - - sprintf(Textxx, "%d", x * 1000); - - Painter->drawLine( - left_margin + round(kx*x * 5) + 1, - top_margin + 1, - left_margin + round(kx*x * 5) + 1, - Image1Height - bottom_margin - 1); - - Painter->drawText( - top_margin + round(kx*x * 5) + 8, - Image1Height - 15, - 100, 20, 0, Textxx); - } -} - -extern "C" void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); - - -void make_graph(float * buf, int buflen, QPainter * Painter) -{ - int top_margin = 10; - int bottom_margin = 20; - int left_margin = 30; - - int i, y1, y2; - float pixel; - - if (buflen == 0) - return; - - for (i = 0; i <= buflen - 2; i++) - { - y1 = 1 - round(buf[i]); - - if (y1 > Image1Height - top_margin - bottom_margin - 2) - y1 = Image1Height - top_margin - bottom_margin - 2; - - y2 = 1 - round(buf[i + 1]); - - if (y2 > Image1Height - top_margin - bottom_margin - 2) - y2 = Image1Height - top_margin - bottom_margin - 2; - - // 150 pixels for 1000 Hz - - // i is the bin number, but bin is not 10 Hz but 12000 /1024 - // so freq = i * 12000 / 1024; - // and pixel is freq * 300 /1000 - - pixel = i * 12000.0f / 1024.0f; - pixel = pixel * 150.0f /1000.0f; - - Painter->drawLine( - left_margin + pixel, - top_margin + y1, - left_margin + pixel + 1, - top_margin + y2); - } -} - -void make_graph_buf(float * buf, short tap, QPainter * Painter) -{ - int FFTSize; - float max; - int i, k; - - FFTSize = 1024; // 12000 / 10; // 10hz on sample; - - for (i = 0; i < tap; i++) - prev_graph_buf[i]= 0; - - for (i = 0; i < FFTSize; i++) - src_graph_buf[i] = 0; - - src_graph_buf[0]= 1; - - FIR_filter(src_graph_buf, FFTSize, tap, buf, graph_buf, prev_graph_buf); - - - for (k = 0; k < FFTSize; k++) - RealIn[k] = graph_buf[k] * 32768; - - FourierTransform(FFTSize, RealIn, RealOut, ImagOut, 0); - - for (k = 0; k < (FFTSize / 2) - 1; k++) - pnt_graph_buf[k] = powf(RealOut[k], 2) + powf(ImagOut[k], 2); - - max = 0; - - for (i = 0; i < (FFTSize / 2) - 1; i++) - { - if (pnt_graph_buf[i] > max) - max = pnt_graph_buf[i]; - } - - if (max > 0) - { - for (i = 0; i < (FFTSize / 2) - 1; i++) - pnt_graph_buf[i] = pnt_graph_buf[i] / max; - } - - for (i = 0; i < (FFTSize / 2) - 1; i++) - { - if (pnt_graph_buf[i] > 0) - pnt_graph_buf[i] = 70 * log10(pnt_graph_buf[i]); - - else - - pnt_graph_buf[i] = 0; - } - - filter_grid(Painter); - - Painter->setPen(Qt::blue); - - make_graph(pnt_graph_buf, 400, Painter); -} diff --git a/SoundInput.c b/SoundInput.c index 5d7e7bc..32366f5 100644 --- a/SoundInput.c +++ b/SoundInput.c @@ -322,7 +322,7 @@ VOID Track1Car4FSK(short * intSamples, int * intPtr, int intSampPerSymbol, float VOID Decode1CarPSK(int Carrier, BOOL OFDM); int EnvelopeCorrelator(); int EnvelopeCorrelatorNew(); -BOOL DecodeFrame(int intFrameType, UCHAR * bytData); +BOOL DecodeFrame(int chan, int intFrameType, UCHAR * bytData); void Update4FSKConstellation(int * intToneMags, int * intQuality); void Update16FSKConstellation(int * intToneMags, int * intQuality); @@ -864,7 +864,7 @@ float dblFreqBin[MAXCAR]; BOOL CheckFrameTypeParity(int intTonePtr, int * intToneMags); -void ARDOPProcessNewSamples(short * Samples, int nSamples) +void ARDOPProcessNewSamples(int chan, short * Samples, int nSamples) { BOOL blnFrameDecodedOK = FALSE; @@ -1264,7 +1264,7 @@ else if (intPhaseError > 2) // This mechanism is to skip actual decoding and reply/change state...no need to decode - blnFrameDecodedOK = DecodeFrame(intFrameType, bytData); + blnFrameDecodedOK = DecodeFrame(chan, intFrameType, bytData); ProcessFrame: @@ -3047,7 +3047,7 @@ void DemodulateFrame(int intFrameType) int intSNdB = 0, intQuality = 0; -BOOL DecodeFrame(int xxx, UCHAR * bytData) +BOOL DecodeFrame(int chan, int xxx, UCHAR * bytData) { BOOL blnDecodeOK = FALSE; char strCallerCallsign[10] = ""; @@ -3176,7 +3176,7 @@ BOOL DecodeFrame(int xxx, UCHAR * bytData) // Data in bytData len in frameLen - ProcessPktFrame(0, bytData, frameLen); + ProcessPktFrame(chan, bytData, frameLen); // else // L2Routine(bytData, frameLen, intLastRcvdFrameQuality, totalRSErrors, intNumCar, pktRXMode); diff --git a/UZ7HOStuff-DESKTOP-MHE5LO8.h b/UZ7HOStuff-DESKTOP-MHE5LO8.h new file mode 100644 index 0000000..20721cc --- /dev/null +++ b/UZ7HOStuff-DESKTOP-MHE5LO8.h @@ -0,0 +1,1110 @@ +#pragma once + +// +// My port of UZ7HO's Soundmodem +// + +#define VersionString "0.0.0.67alpha-6" +#define VersionBytes {0, 0, 0, 67} + +// Added FX25. 4x100 FEC and V27 not Working and disabled + +// 0.8 V27 now OK. + +// 0.9 Digipeating added + +// 0.10 Fix second channel tones and calibrate + +// 0.11 Fix allocation of sessions to correct modem +// Fix DCD +// Fix Monitoring of Multiline packets +// Fix possible saving of wrong center freq +// Limit TX sample Q in Linux +// + +// 0.12 Add AGWPE monitoring of received frames +// Fix DCD Threshold +// Fix KISS transparency issue + +// 0.13 Fix sending last few bits in FX.25 Mode + +// 0.14 Add "Copy on Select" to Trace Window + +// 0.15 Limit Trace window to 10000 lines + +// 0.16 Fix overwriting monitor window after scrollback + +// 0.17 Add GPIO and CAT PTT + +// 0.18 Add CM108/119 PTT + +// 0.19 Fix scheduling KISS frames + +// 0.20 Debug code added to RR processing + +// 0.21 Fix AGW monitor of multiple line packets +// Close ax.25 sessions if AGW Host session closes + +// 0.22 Add FEC Count to Session Stats + +// 0.23 Retry DISC until UA received or retry count exceeded + +// 0.24 More fixes to DISC handling + +// 0.26 Add OSS PulseAudio and HAMLIB support + +// 0.27 Dynamically load PulseAudio modules + +// 0.28 Add ARDOPPacket Mode + +// 0.29 Fix saving settings and geometry on close +// 0.30 Retructure code to build with Qt 5.3 +// Fix crash in nogui mode if pulse requested but not available +// Try to fix memory leaks + +// 0.31 Add option to run modems in seprate threads + +// 0.32 Fix timing problem with AGW connect at startup +// Add Memory ARQ +// Add Single bit "Correction" +// Fix error in 31 when using multiple decoders + +// 0.33 Fix Single bit correction +// More memory leak fixes + +// 0.34 Add API to set Modem and Center Frequency +// Fix crash in delete_incoming_mycalls + +// 0.35 Return Version in AGW Extended g response + +// 0.36 Fix timing problem on startup + +// 0.37 Add scrollbars to Device and Modem dialogs + +// 0.38 Change default CM108 name to /dev/hidraw0 on Linux + +// 0.39 Dont try to display Message Boxes in nogui mode. +// Close Device and Modem dialogs on Accept or Reject +// Fix using HAMLIB in nogui mode + +// 0.40 Fix bug in frame optimize when using 6 char calls + +// 0.41 Fix "glitch" on waterfall markers when changing modem freqs + +// 0.42 Add "Minimize to Tray" option + +// 0.43 Add Andy's on_SABM fix. +// Fix Crash if KISS Data sent to AGW port + +// 0.44 Add UDP bridge. + +// 0.45 Add two more modems. +// 0.46 Fix two more modems. + +// 0.47 Fix suprious DM when host connection lost +// Add CWID + +// 0.48 Send FRMR for unrecognised frame types + +// 0.49 Add Andy's FEC Tag correlation coode + +// 0.50 Fix Waterfall display when only using right channel +// Allow 1200 baud fsk at other center freqs +// Add Port numbers to Window title and Try Icon tooltip +// Fix calculation of filters for multiple decoders +// Add RX Offset setting (for satellite operation + +// 0.51 Fix Multithreading with more that 2 modems + +// 0.52 Add Stdin as source on Linux + +// 0.53 Use Byte instead of byte as byte is defined in newer versions of gcc + +// 0.54 Fix for ALSA problem on new pi OS + +// 0.55 Fix for compiler error with newer compiler + +// 0.56 Fix errors in Config.cpp June 22 + +// 0.57 Add Restart Waterfall action August 22 + +// 0.58 Add RSID Sept 2022 + +// 0.59 Add config of Digi Calls Dec 2022 + +// 0.60 Allow ARDOP Packet on modems 2 to 4 March 2023 + +// 0.61 Add il2p support April 2023 + +// 0.62 April 2023 +// Add option to specify sound devices that aren't in list +// Add Save button to Modem dialog to save current tab without closing dialog +// Don't add plug: to Linux device addresses unless addr contains : (allows use of eg ARDOP) + +// 0.64 Fix sending ax.25 (broken in .61) + +// 0.65 Allow Set Modem command to use modem index as well as modem name + +// 0.66 Allow configuration of waterfall span June 23 +// Add Exclude + +// .67 Add extra modes RUH 4800 RUH 9600 QPSK 600 QPSK 2400 July 23 +// 8PSK 900 added but not working +// Fix loading txtail +// Fix digipeating +// Add MaxFrame to Modem Dialog + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define UNUSED(x) (void)(x) + +#ifdef M_PI +#undef M_PI +#endif + +#define M_PI 3.1415926f + +#define pi M_PI + +#ifndef WIN32 +#define _strdup strdup +#endif + + //#define NULL ((void *)0) + + //Delphi Types remember case insensitive + +#define single float +#define boolean int +#define Byte unsigned char // 0 to 255 +#define Word unsigned short // 0 to 65,535 +#define SmallInt short // -32,768 to 32,767 +#define LongWord unsigned int // 0 to 4,294,967,295 + // Int6 : Cardinal; // 0 to 4,294,967,295 +#define LongInt int // -2,147,483,648 to 2,147,483,647 +#define Integer int // -2,147,483,648 to 2,147,483,647 +//#define Int64 long long // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 + +//#define Byte unsigned char // 0 to 255 +#define word unsigned short // 0 to 65,535 +#define smallint short // -32,768 to 32,767 +#define longword unsigned int // 0 to 4,294,967,295 + // Int6 : Cardinal; // 0 to 4,294,967,295 +#define longint int // -2,147,483,648 to 2,147,483,647 +#define integer int // -2,147,483,648 to 2,147,483,647 + +typedef unsigned long ULONG; + +#define UCHAR unsigned char +#define UINT unsigned int +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +// Soundcard Channels + +#define NONE 0 +#define LEFT 1 +#define RIGHT 2 + +#define nr_emph 2 + +#define decodedNormal 4 //'-' +#define decodedFEC 3 //'F' +#define decodedMEM 2 //'#' +#define decodedSingle 1 //'$' + + +// Think about implications of changing this !! +extern int FFTSize; + +// Seems to use Delphi TStringList for a lot of queues. This seems to be a list of pointers and a count +// Each pointer is to a Data/Length pair +//Maybe something like + +typedef struct string_T +{ + unsigned char * Data; + int Length; + int AllocatedLength; // A reasonable sized block is allocated at the start to speed up adding chars + +}string; + +typedef struct TStringList_T +{ + int Count; + string ** Items; + +} TStringList; + +// QPSK struct + +typedef struct TQPSK_t +{ + UCHAR tx[4]; + int count[4]; + UCHAR rx[4]; + UCHAR mode; +} TPQSK; + + +typedef struct TKISSMode_t +{ + string * data_in; + void * Socket; // Used as a key + + // Not sure what rest are used for. Seems to be one per channel + + TStringList buffer[4]; // Outgoing Frames + +} TKISSMode; + +typedef struct TMChannel_t +{ + + single prev_LPF1I_buf[4096]; + single prev_LPF1Q_buf[4096]; + single prev_dLPFI_buf[4096]; + single prev_dLPFQ_buf[4096]; + single prev_AFCI_buf[4096]; + single prev_AFCQ_buf[4096]; + single AngleCorr; + single MUX_osc; + single AFC_IZ1; + single AFC_IZ2; + single AFC_QZ1; + single AFC_QZ2; + single AFC_bit_buf1I[1024]; + single AFC_bit_buf1Q[1024]; + single AFC_bit_buf2[1024]; + single AFC_IIZ1; + single AFC_QQZ1; + +} TMChannel; + +typedef struct TFX25_t +{ + string data; + Byte status; + Byte bit_cnt; + Byte byte_rx; + unsigned long long tag; + Byte size; + Byte rs_size; + Byte size_cnt; +} TFX25; + + + +typedef struct TDetector_t +{ + struct TFX25_t fx25[4]; + TStringList mem_ARQ_F_buf[5]; + TStringList mem_ARQ_buf[5]; + float pll_loop[5]; + float last_sample[5]; + UCHAR ones[5]; + UCHAR zeros[5]; + float bit_buf[5][1024]; + float bit_buf1[5][1024]; + UCHAR sample_cnt[5]; + UCHAR last_bit[5]; + float PSK_IZ1[5]; + float PSK_QZ1[5]; + float PkAmpI[5]; + float PkAmpQ[5]; + float PkAmp[5]; + float PkAmpMax[5]; + int newpkpos[5]; + float AverageAmp[5]; + float AngleCorr[5]; + float MinAmp[5]; + float MaxAmp[5]; + float MUX3_osc[5]; + float MUX3_1_osc[5]; + float MUX3_2_osc[5]; + float Preemphasis6[5]; + float Preemphasis12[5]; + float PSK_AGC[5]; + float AGC[5]; + float AGC1[5]; + float AGC2[5]; + float AGC3[5]; + float AGC_max[5]; + float AGC_min[5]; + float AFC_IZ1[5]; + float AFC_IZ2[5]; + float AFC_QZ1[5]; + float AFC_QZ2[5]; + + UCHAR last_rx_bit[5]; + UCHAR bit_stream[5]; + UCHAR byte_rx[5]; + UCHAR bit_stuff_cnt[5]; + UCHAR bit_cnt[5]; + float bit_osc[5]; + UCHAR frame_status[5]; + string rx_data[5]; + string FEC_rx_data[5]; + // + UCHAR FEC_pol[5]; + unsigned short FEC_err[5]; + unsigned long long FEC_header1[5][2]; + unsigned short FEC_blk_int[5]; + unsigned short FEC_len_int[5]; + unsigned short FEC_len[5]; + + unsigned short FEC_len_cnt[5]; + + UCHAR rx_intv_tbl[5][4]; + UCHAR rx_intv_sym[5]; + UCHAR rx_viterbi[5]; + UCHAR viterbi_cnt[5]; + // SurvivorStates [1..4,0..511] of TSurvivor; + // + TMChannel MChannel[5][4]; + + float AFC_dF_avg[5]; + float AFC_dF[5]; + float AFC_bit_osc[5]; + float AFC_bit_buf[5][1024]; + unsigned short AFC_cnt[5]; + + string raw_bits1[5]; + string raw_bits[5]; + UCHAR last_nrzi_bit[5]; + + float BPF_core[5][2048]; + float LPF_core[5][2048]; + + float src_INTR_buf[5][8192]; + float src_INTRI_buf[5][8192]; + float src_INTRQ_buf[5][8192]; + float src_LPF1I_buf[5][8192]; + float src_LPF1Q_buf[5][8192]; + + float src_BPF_buf[5][2048]; + float src_Loop_buf[5][8192]; + float prev_BPF_buf[5][4096]; + + float prev_LPF1I_buf[5][4096]; + float prev_LPF1Q_buf[5][4096]; + float prev_INTR_buf[5][16384]; + float prev_INTRI_buf[5][16384]; + float prev_INTRQ_buf[5][16384]; + + Byte emph_decoded; + Byte rx_decoded; + Byte errors; + +} TDetector; + + + +typedef struct AGWUser_t +{ + void *socket; + string * data_in; + TStringList AGW_frame_buf; + boolean Monitor; + boolean Monitor_raw; + boolean reportFreqAndModem; // Can report modem and frequency to host + +} AGWUser; + +typedef struct TAX25Info_t +{ + longint stat_s_pkt; + longint stat_s_byte; + longint stat_r_pkt; + longint stat_r_byte; + longint stat_r_fc; + longint stat_fec_count; + time_t stat_begin_ses; + time_t stat_end_ses; + longint stat_l_r_byte; + longint stat_l_s_byte; + +} TAX25Info; + +typedef struct TAX25Port_t +{ + Byte hi_vs; + Byte vs; + Byte vr; + Byte PID; + TStringList in_data_buf; + TStringList frm_collector; + string frm_win[8]; + string out_data_buf; + word t1; + word t2; + word t3; + Byte i_lo; + Byte i_hi; + word n1; + word n2; + word IPOLL_cnt; + TStringList frame_buf; //буфер кадров на передачу + TStringList I_frame_buf; + Byte status; + word clk_frack; + char corrcall[10]; + char mycall[10]; + UCHAR digi[56]; + UCHAR Path[80]; // Path in ax25 format - added to save building it each time + UCHAR ReversePath[80]; + int snd_ch; // Simplifies parameter passing + int port; + int pathLen; + void * socket; + char kind[16]; + TAX25Info info; +} TAX25Port; + + +#define LOGEMERGENCY 0 +#define LOGALERT 1 +#define LOGCRIT 2 +#define LOGERROR 3 +#define LOGWARNING 4 +#define LOGNOTICE 5 +#define LOGINFO 6 +#define LOGDEBUG 7 + +#define PTTRTS 1 +#define PTTDTR 2 +#define PTTCAT 4 +#define PTTCM108 8 +#define PTTHAMLIB 16 + +// Status flags + +#define STAT_NO_LINK 0 +#define STAT_LINK 1 +#define STAT_CHK_LINK 2 +#define STAT_WAIT_ANS 3 +#define STAT_TRY_LINK 4 +#define STAT_TRY_UNLINK 5 + + + // Сmd,Resp,Poll,Final,Digipeater flags +#define SET_P 1 +#define SET_F 0 +#define SET_C 1 +#define SET_R 0 +#define SET_NO_RPT 0 +#define SET_RPT 1 + // Frame ID flags +#define I_FRM 0 +#define S_FRM 1 +#define U_FRM 2 +#define I_I 0 +#define S_RR 1 +#define S_RNR 5 +#define S_REJ 9 +#define S_SREJ 0x0D +#define U_SABM 47 +#define U_DISC 67 +#define U_DM 15 +#define U_UA 99 +#define U_FRMR 135 +#define U_UI 3 + // PID flags +#define PID_X25 0x01 // 00000001-CCIT X25 PLP +#define PID_SEGMENT 0x08 // 00001000-Segmentation fragment +#define PID_TEXNET 0xC3 // 11000011-TEXNET Datagram Protocol +#define PID_LQ 0xC4 // 11001000-Link Quality Protocol +#define PID_APPLETALK 0xCA // 11001010-Appletalk +#define PID_APPLEARP 0xCB // 11001011-Appletalk ARP +#define PID_IP 0xCC // 11001100-ARPA Internet Protocol +#define PID_ARP 0xCD // 11001101-ARPA Address Resolution Protocol +#define PID_NET_ROM 0xCF // 11001111-NET/ROM + + +// Sound interface buffer sizes + +extern int ReceiveSize; +extern int SendSize; + +#define NumberofinBuffers 4 + +#define Now getTicks() + +// #defines from all modules (?? is this a good idaa ?? + +#define WIN_MAXIMIZED 0 +#define WIN_MINIMIZED 1 +#define MODEM_CAPTION 'SoundModem by UZ7HO' +#define MODEM_VERSION '1.06' +#define SND_IDLE 0 +#define SND_TX 1 +#define BUF_EMPTY 0 +#define BUF_FULL 1 +#define DISP_MONO FALSE +#define DISP_RGB TRUE +#define MOD_IDLE 0 +#define MOD_RX 1 +#define MOD_TX 2 +#define MOD_WAIT 3 +#define TIMER_FREE 0 +#define TIMER_BUSY 1 +#define TIMER_OFF 2 +#define TIMER_EVENT_ON 3 +#define TIMER_EVENT_OFF 4 +#define DEBUG_TIMER 1 +#define DEBUG_WATERFALL 2 +#define DEBUG_DECODE 4 +#define DEBUG_SOUND 8 +#define IS_LAST TRUE +#define IS_NOT_LAST FALSE +#define modes_count 20 +#define SPEED_300 0 +#define SPEED_1200 1 +#define SPEED_600 2 +#define SPEED_2400 3 +#define SPEED_P1200 4 +#define SPEED_P600 5 +#define SPEED_P300 6 +#define SPEED_P2400 7 +#define SPEED_Q4800 8 +#define SPEED_Q3600 9 +#define SPEED_Q2400 10 +#define SPEED_MP400 11 +#define SPEED_DW2400 12 +#define SPEED_8P4800 13 +#define SPEED_2400V26B 14 +#define SPEED_ARDOP 15 +#define SPEED_Q300 16 +#define SPEED_8PSK300 17 +#define SPEED_RUH48 18 +#define SPEED_RUH96 19 + +#define MODE_FSK 0 +#define MODE_BPSK 1 +#define MODE_QPSK 2 +#define MODE_MPSK 3 +#define MODE_8PSK 4 +#define MODE_PI4QPSK 5 +#define MODE_ARDOP 6 +#define MODE_RUH 7 + +#define QPSK_SM 0 +#define QPSK_V26 1 + +#define MODEM_8P4800_BPF 3200 +#define MODEM_8P4800_TXBPF 3400 +#define MODEM_8P4800_LPF 1000 +#define MODEM_8P4800_BPF_TAP 64 +#define MODEM_8P4800_LPF_TAP 8 + // +#define MODEM_MP400_BPF 775 +#define MODEM_MP400_TXBPF 850 +#define MODEM_MP400_LPF 70 +#define MODEM_MP400_BPF_TAP 256 +#define MODEM_MP400_LPF_TAP 128 + // +#define MODEM_DW2400_BPF 2400 +#define MODEM_DW2400_TXBPF 2500 +#define MODEM_DW2400_LPF 900 +#define MODEM_DW2400_BPF_TAP 256 //256 +#define MODEM_DW2400_LPF_TAP 32 //128 + // +#define MODEM_Q2400_BPF 2400 +#define MODEM_Q2400_TXBPF 2500 +#define MODEM_Q2400_LPF 900 +#define MODEM_Q2400_BPF_TAP 256 //256 +#define MODEM_Q2400_LPF_TAP 128 //128 + // +#define MODEM_Q3600_BPF 3600 +#define MODEM_Q3600_TXBPF 3750 +#define MODEM_Q3600_LPF 1350 +#define MODEM_Q3600_BPF_TAP 256 +#define MODEM_Q3600_LPF_TAP 128 + // +#define MODEM_Q4800_BPF 4800 +#define MODEM_Q4800_TXBPF 5000 +#define MODEM_Q4800_LPF 1800 +#define MODEM_Q4800_BPF_TAP 256 +#define MODEM_Q4800_LPF_TAP 128 + // +#define MODEM_P2400_BPF 4800 +#define MODEM_P2400_TXBPF 5000 +#define MODEM_P2400_LPF 1800 +#define MODEM_P2400_BPF_TAP 256 +#define MODEM_P2400_LPF_TAP 128 + // +#define MODEM_P1200_BPF 2400 +#define MODEM_P1200_TXBPF 2500 +#define MODEM_P1200_LPF 900 +#define MODEM_P1200_BPF_TAP 256 +#define MODEM_P1200_LPF_TAP 128 + // +#define MODEM_P600_BPF 1200 +#define MODEM_P600_TXBPF 1250 +#define MODEM_P600_LPF 400 +#define MODEM_P600_BPF_TAP 256 +#define MODEM_P600_LPF_TAP 128 + // +#define MODEM_P300_BPF 600 +#define MODEM_P300_TXBPF 625 +#define MODEM_P300_LPF 200 +#define MODEM_P300_BPF_TAP 256 +#define MODEM_P300_LPF_TAP 128 + // +#define MODEM_300_BPF 500 +#define MODEM_300_TXBPF 500 +#define MODEM_300_LPF 155 +#define MODEM_300_BPF_TAP 256 +#define MODEM_300_LPF_TAP 128 + // +#define MODEM_600_BPF 800 +#define MODEM_600_TXBPF 900 +#define MODEM_600_LPF 325 +#define MODEM_600_BPF_TAP 256 +#define MODEM_600_LPF_TAP 128 + // +#define MODEM_1200_BPF 1400 +#define MODEM_1200_TXBPF 1600 +#define MODEM_1200_LPF 650 +#define MODEM_1200_BPF_TAP 256 +#define MODEM_1200_LPF_TAP 128 + // +#define MODEM_2400_BPF 3200 +#define MODEM_2400_TXBPF 3200 +#define MODEM_2400_LPF 1400 +#define MODEM_2400_BPF_TAP 256 +#define MODEM_2400_LPF_TAP 128 + +#define TX_SILENCE 0 +#define TX_DELAY 1 +#define TX_TAIL 2 +#define TX_NO_DATA 3 +#define TX_FRAME 4 +#define TX_WAIT_BPF 5 + + +#define FRAME_WAIT 0 +#define FRAME_LOAD 1 +#define RX_BIT0 0 +#define RX_BIT1 128 +#define DCD_WAIT_SLOT 0 +#define DCD_WAIT_PERSIST 1 + +#define FX25_MODE_NONE 0 +#define FX25_MODE_RX 1 +#define FX25_MODE_TXRX 2 +#define FX25_TAG 0 +#define FX25_LOAD 1 + +#define IL2P_MODE_NONE 0 +#define IL2P_MODE_RX 1 // RX il2p + HDLC +#define IL2P_MODE_TXRX 2 +#define IL2P_MODE_ONLY 3 // RX only il2p, TX il2p + + +#define MODE_OUR 0 +#define MODE_OTHER 1 +#define MODE_RETRY 2 + +#define FRAME_FLAG 126 // 7e + +#define port_num 32 // ?? Max AGW sessions +#define PKT_ERR 17 // Minimum packet size, bytes +#define I_MAX 7 // Maximum number of packets + + + // externs for all modules + +#define ARDOPBufferSize 12000 * 100 + +extern short ARDOPTXBuffer[4][ARDOPBufferSize]; // Enough to hold whole frame of samples + +extern int ARDOPTXLen[4]; // Length of frame +extern int ARDOPTXPtr[4]; // Tx Pointer + +extern BOOL KISSServ; +extern int KISSPort; + +extern BOOL AGWServ; +extern int AGWPort; + +extern TStringList KISS_acked[]; +extern TStringList KISS_iacked[]; + +extern TStringList all_frame_buf[5]; + +extern unsigned short pkt_raw_min_len; +extern int stat_r_mem; + +extern UCHAR diddles; + +extern int stdtones; +extern int fullduplex; + +extern struct TQPSK_t qpsk_set[4]; + +extern int NonAX25[5]; + +extern short txtail[5]; +extern short txdelay[5]; + +extern short modem_def[5]; + +extern int emph_db[5]; +extern UCHAR emph_all[5]; + +extern UCHAR modem_mode[5]; + +extern UCHAR RCVR[5]; +extern int soundChannel[5]; +extern int modemtoSoundLR[4]; + +extern short rx_freq[5]; +extern short active_rx_freq[5]; +extern short rx_shift[5]; +extern short rx_baudrate[5]; +extern short rcvr_offset[5]; + +extern int tx_hitoneraisedb[5]; +extern float tx_hitoneraise[5]; + + +extern UCHAR tx_status[5]; +extern float tx_freq[5]; +extern float tx_shift[5]; +extern unsigned short tx_baudrate[5]; +extern unsigned short tx_bitrate[5]; + +extern unsigned short bpf[5]; +extern unsigned short lpf[5]; + +extern unsigned short txbpf[5]; + +extern unsigned short tx_BPF_tap[5]; +extern unsigned short tx_BPF_timer[5]; + +extern unsigned short BPF_tap[5]; +extern unsigned short LPF_tap[5]; + +extern float tx_BPF_core[5][32768]; +extern float LPF_core[5][2048]; + +extern UCHAR xData[256]; +extern UCHAR xEncoded[256]; +extern UCHAR xDecoded[256]; + +extern float PI125; +extern float PI375; +extern float PI625; +extern float PI875; +extern float PI5; +extern float PI25; +extern float PI75; + +extern int max_frame_collector[4]; +extern boolean KISS_opt[4]; + +#define MaxErrors 4 + +extern BOOL MinOnStart; + +//RS TReedSolomon; +// Form1 TForm1; +// WaveFormat TWaveFormatEx; + +extern int UDPServ; +extern long long udpServerSeqno; + +extern int Channels; +extern int BitsPerSample; +extern float TX_Samplerate; +extern float RX_Samplerate; +extern int RX_SR; +extern int TX_SR; +extern int RX_PPM; +extern int TX_PPM; +extern int tx_bufsize; +extern int rx_bufsize; +extern int tx_bufcount; +extern int rx_bufcount; +extern int fft_size; +extern int mouse_down[2]; +//UCHAR * RX_pBuf array[257]; +// RX_header array[1..256] of TWaveHdr; +// TX_pBuf array[1..4,1..256] of pointer; +//TX_header array[1..4,1..256] of TWaveHdr; +extern UCHAR calib_mode[5]; +extern UCHAR snd_status[5]; +extern UCHAR buf_status[5]; +extern UCHAR tx_buf_num1[5]; +extern UCHAR tx_buf_num[5]; +extern int speed[5]; +extern int panels[6]; + +extern int FFTSize; +#define fft_size FFTSize + +extern float fft_window_arr[2048]; +// fft_s,fft_d array[0..2047] of TComplex; +extern short fft_buf[2][8192]; +extern UCHAR fft_disp[2][1024]; +// bm array[1..4] of TBitMap; +// bm1,bm2,bm3 TBitMap; + +// WaveInHandle hWaveIn; +// WaveOutHandle array[1..4] of hWaveOut; +extern int RXBufferLength; + +// data1 PData16; + +extern int grid_time; +extern int fft_mult; +extern int fft_spd; +extern int grid_timer; +extern int stop_wf; +extern int raduga; +extern char snd_rx_device_name[32]; +extern char snd_tx_device_name[32]; +extern int snd_rx_device; +extern int snd_tx_device; +extern UCHAR mod_icon_status; +extern UCHAR last_mod_icon_status; +extern UCHAR icon_timer; +// TelIni TIniFile; +extern char cur_dir[]; +// TimerId1 cardinal; +// TimerId2 cardinal; +extern UCHAR TimerStat1; +extern UCHAR TimerStat2; +extern int stat_log; + +extern char PTTPort[80]; // Port for Hardware PTT - may be same as control port. +extern int PTTMode; +extern int PTTBAUD ; + +extern char PTTOnString[128]; +extern char PTTOffString[128]; + +extern UCHAR PTTOnCmd[64]; +extern UCHAR PTTOnCmdLen; + +extern UCHAR PTTOffCmd[64]; +extern UCHAR PTTOffCmdLen; + +extern int PTT_device; +extern int RX_device; +extern int TX_device; +extern int TX_rotate; +extern int UsingLeft; +extern int UsingRight; +extern int UsingBothChannels; +extern int pttGPIOPin; +extern int pttGPIOPinR; +extern BOOL pttGPIOInvert; +extern BOOL useGPIO; +extern BOOL gotGPIO; +extern int VID; +extern int PID; +extern char CM108Addr[80]; +extern int HamLibPort; +extern char HamLibHost[]; + +extern int SCO; +extern int DualPTT; +extern UCHAR DebugMode; +extern UCHAR TimerEvent; +extern int nr_monitor_lines; +extern int UTC_Tim; +extern int MainPriority; +// MainThreadHandle THandle; +extern UCHAR w_state; + +extern BOOL Firstwaterfall; +extern BOOL Secondwaterfall; + +extern int dcd_threshold; +extern int rxOffset; +extern int chanOffset[4]; +extern int Continuation[4]; // Sending 2nd or more packet of burst + +extern boolean busy; +extern boolean dcd[5]; + +extern struct TKISSMode_t KISS; + +extern boolean dyn_frack[4] ; +extern Byte recovery[4]; +extern Byte users[4]; + +extern int resptime[4]; +extern int slottime[4]; +extern int persist[4]; +extern int fracks[4]; +extern int frack_time[4]; +extern int idletime[4]; +extern int redtime[4]; +extern int IPOLL[4]; +extern int maxframe[4]; +extern int TXFrmMode[4]; + +extern char MyDigiCall[4][512]; +extern char exclude_callsigns[4][512]; +extern char exclude_APRS_frm[4][512]; + +extern TStringList list_exclude_callsigns[4]; +extern TStringList list_exclude_APRS_frm[4]; +extern TStringList list_digi_callsigns[4]; + + +extern int SoundIsPlaying; +extern int Capturing; + +extern struct TDetector_t DET[nr_emph + 1][16]; + +extern char CaptureDevice[80]; +extern char PlaybackDevice[80]; + +extern TAX25Port AX25Port[4][port_num]; + +extern int fx25_mode[4]; +extern int il2p_mode[4]; + +extern int tx_fx25_size[4]; +extern int tx_fx25_size_cnt[4]; +extern int tx_fx25_mode[4]; + +extern int SatelliteMode; + +extern int using48000; // Set if using 48K sample rate (ie RUH Modem active) + + +// Function prototypes + +void KISS_send_ack(UCHAR port, string * data); +void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame); +void FIR_filter(float * src, unsigned short buf_size, unsigned short tap, float * core, float * dest, float * prev); +void make_core_TXBPF(UCHAR snd_ch, float freq, float width); +void OpenPTTPort(); +void ClosePTTPort(); + +void RadioPTT(int snd_ch, BOOL PTTState); +void put_frame(int snd_ch, string * frame, char * code, int tx_stat, int excluded); +void CloseCOMPort(int fd); +void COMClearRTS(int fd); +void COMClearDTR(int fd); +unsigned int getTicks(); +char * ShortDateTime(); +void write_ax25_info(TAX25Port * AX25Sess); +void reverse_addr(Byte * path, Byte * revpath, int Len); +string * get_mycall(string * path); +TAX25Port * get_user_port_by_calls(int snd_ch, char * CallFrom, char * CallTo); +TAX25Port * get_free_port(int snd_ch); +void * in_list_incoming_mycall(Byte * path); +boolean add_incoming_mycalls(void * socket, char * src_call); +int get_addr(char * Calls, UCHAR * AXCalls); +void reverse_addr(Byte * path, Byte * revpath, int Len); +void set_link(TAX25Port * AX25Sess, UCHAR * axpath); +void rst_timer(TAX25Port * AX25Sess); +void set_unlink(TAX25Port * AX25Sess, Byte * path); +unsigned short get_fcs(UCHAR * Data, unsigned short len); +void KISSSendtoServer(void * sock, Byte * Msg, int Len); +int ConvFromAX25(unsigned char * incall, char * outcall); +BOOL ConvToAX25(char * callsign, unsigned char * ax25call); +void Debugprintf(const char * format, ...); + +double pila(double x); + +void AGW_Raw_monitor(int snd_ch, string * data); + +// Delphi emulation functions + +string * Strings(TStringList * Q, int Index); +void Clear(TStringList * Q); +int Count(TStringList * List); + +string * newString(); +string * copy(string * Source, int StartChar, int Count); +TStringList * newTStringList(); + +void freeString(string * Msg); + +void initString(string * S); +void initTStringList(TStringList* T); + +// Two delete() This is confusing!! +// Not really - one acts on String, other TStringList + +void Delete(TStringList * Q, int Index); +void mydelete(string * Source, int StartChar, int Count); + +void move(UCHAR * SourcePointer, UCHAR * DestinationPointer, int CopyCount); +void fmove(float * SourcePointer, float * DestinationPointer, int CopyCount); + +void setlength(string * Msg, int Count); // Set string length + +string * stringAdd(string * Msg, UCHAR * Chars, int Count); // Extend string + +void Assign(TStringList * to, TStringList * from); // Duplicate from to to + +string * duplicateString(string * in); + +// This looks for a string in a stringlist. Returns inhex if found, otherwise -1 + +int my_indexof(TStringList * l, string * s); + +boolean compareStrings(string * a, string * b); + +int Add(TStringList * Q, string * Entry); + + +#define IL2P_SYNC_WORD_SIZE 3 +#define IL2P_HEADER_SIZE 13 // Does not include 2 parity. +#define IL2P_HEADER_PARITY 2 + +#define IL2P_MAX_PAYLOAD_SIZE 1023 +#define IL2P_MAX_PAYLOAD_BLOCKS 5 +#define IL2P_MAX_PARITY_SYMBOLS 16 // For payload only. +#define IL2P_MAX_ENCODED_PAYLOAD_SIZE (IL2P_MAX_PAYLOAD_SIZE + IL2P_MAX_PAYLOAD_BLOCKS * IL2P_MAX_PARITY_SYMBOLS) + +struct il2p_context_s { + + enum { IL2P_SEARCHING = 0, IL2P_HEADER, IL2P_PAYLOAD, IL2P_DECODE } state; + + unsigned int acc; // Accumulate most recent 24 bits for sync word matching. + // Lower 8 bits are also used for accumulating bytes for + // the header and payload. + + int bc; // Bit counter so we know when a complete byte has been accumulated. + + int polarity; // 1 if opposite of expected polarity. + + unsigned char shdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; + // Scrambled header as received over the radio. Includes parity. + int hc; // Number if bytes placed in above. + + unsigned char uhdr[IL2P_HEADER_SIZE]; // Header after FEC and unscrambling. + + int eplen; // Encoded payload length. This is not the nuumber from + // from the header but rather the number of encoded bytes to gather. + + unsigned char spayload[IL2P_MAX_ENCODED_PAYLOAD_SIZE]; + // Scrambled and encoded payload as received over the radio. + int pc; // Number of bytes placed in above. + + int corrected; // Number of symbols corrected by RS FEC. +}; + + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/UZ7HOStuff.h b/UZ7HOStuff.h index d98da89..586edce 100644 --- a/UZ7HOStuff.h +++ b/UZ7HOStuff.h @@ -1,9 +1,11 @@ +#pragma once + // // My port of UZ7HO's Soundmodem // -#define VersionString "0.0.0.66" -#define VersionBytes {0, 0, 0, 66} +#define VersionString "0.0.0.67-2" +#define VersionBytes {0, 0, 0, 67} // Added FX25. 4x100 FEC and V27 not Working and disabled @@ -146,7 +148,17 @@ // 0.66 Allow configuration of waterfall span June 23 // Add Exclude +// .67 Add extra modes 8PSK 900 RUH 4800 RUH 9600 QPSK 600 QPSK 2400 August 23 +// Fix loading txtail +// Fix digipeating +// Add MaxFrame to Modem Dialog +// Fix 64 bit compatibility in ackmode +// Add option to change CWID tones +// Fix minimum centre freq validation +// .68 Monitor XID and TEST +// Flag active interface in title bar +// Improve header validation in il2p @@ -402,7 +414,6 @@ typedef struct TDetector_t Byte rx_decoded; Byte errors; - } TDetector; @@ -516,6 +527,10 @@ typedef struct TAX25Port_t #define U_UA 99 #define U_FRMR 135 #define U_UI 3 + +#define U_XID 0xAF +#define U_TEST 0xE3 + // PID flags #define PID_X25 0x01 // 00000001-CCIT X25 PLP #define PID_SEGMENT 0x08 // 00001000-Segmentation fragment @@ -528,10 +543,11 @@ typedef struct TAX25Port_t #define PID_NET_ROM 0xCF // 11001111-NET/ROM -// Sound interface buffer size +// Sound interface buffer sizes + +extern int ReceiveSize; +extern int SendSize; -#define SendSize 1024 // 100 mS for now -#define ReceiveSize 512 // try 100 mS for now #define NumberofinBuffers 4 #define Now getTicks() @@ -543,8 +559,7 @@ typedef struct TAX25Port_t #define MODEM_CAPTION 'SoundModem by UZ7HO' #define MODEM_VERSION '1.06' #define SND_IDLE 0 -#define SND_RX 1 -#define SND_TX 2 +#define SND_TX 1 #define BUF_EMPTY 0 #define BUF_FULL 1 #define DISP_MONO FALSE @@ -564,7 +579,7 @@ typedef struct TAX25Port_t #define DEBUG_SOUND 8 #define IS_LAST TRUE #define IS_NOT_LAST FALSE -#define modes_count 16 +#define modes_count 20 #define SPEED_300 0 #define SPEED_1200 1 #define SPEED_600 2 @@ -579,8 +594,12 @@ typedef struct TAX25Port_t #define SPEED_MP400 11 #define SPEED_DW2400 12 #define SPEED_8P4800 13 -#define SPEED_AE2400 14 +#define SPEED_2400V26B 14 #define SPEED_ARDOP 15 +#define SPEED_Q300 16 +#define SPEED_8PSK300 17 +#define SPEED_RUH48 18 +#define SPEED_RUH96 19 #define MODE_FSK 0 #define MODE_BPSK 1 @@ -589,11 +608,11 @@ typedef struct TAX25Port_t #define MODE_8PSK 4 #define MODE_PI4QPSK 5 #define MODE_ARDOP 6 +#define MODE_RUH 7 #define QPSK_SM 0 #define QPSK_V26 1 - #define MODEM_8P4800_BPF 3200 #define MODEM_8P4800_TXBPF 3400 #define MODEM_8P4800_LPF 1000 @@ -720,7 +739,7 @@ typedef struct TAX25Port_t #define ARDOPBufferSize 12000 * 100 -extern short ARDOPTXBuffer[4][12000 * 100]; // Enough to hold whole frame of samples +extern short ARDOPTXBuffer[4][ARDOPBufferSize]; // Enough to hold whole frame of samples extern int ARDOPTXLen[4]; // Length of frame extern int ARDOPTXPtr[4]; // Tx Pointer @@ -776,6 +795,7 @@ extern UCHAR tx_status[5]; extern float tx_freq[5]; extern float tx_shift[5]; extern unsigned short tx_baudrate[5]; +extern unsigned short tx_bitrate[5]; extern unsigned short bpf[5]; extern unsigned short lpf[5]; @@ -927,6 +947,7 @@ extern BOOL Secondwaterfall; extern int dcd_threshold; extern int rxOffset; extern int chanOffset[4]; +extern int Continuation[4]; // Sending 2nd or more packet of burst extern boolean busy; extern boolean dcd[5]; @@ -976,6 +997,10 @@ extern int tx_fx25_mode[4]; extern int SatelliteMode; +extern int using48000; // Set if using 48K sample rate (ie RUH Modem active) + +extern int txmin, txmax; + // Function prototypes void KISS_send_ack(UCHAR port, string * data); @@ -1014,7 +1039,7 @@ double pila(double x); void AGW_Raw_monitor(int snd_ch, string * data); -// Dephi emulation functions +// Delphi emulation functions string * Strings(TStringList * Q, int Index); void Clear(TStringList * Q); @@ -1053,6 +1078,46 @@ int my_indexof(TStringList * l, string * s); boolean compareStrings(string * a, string * b); int Add(TStringList * Q, string * Entry); + + +#define IL2P_SYNC_WORD_SIZE 3 +#define IL2P_HEADER_SIZE 13 // Does not include 2 parity. +#define IL2P_HEADER_PARITY 2 + +#define IL2P_MAX_PAYLOAD_SIZE 1023 +#define IL2P_MAX_PAYLOAD_BLOCKS 5 +#define IL2P_MAX_PARITY_SYMBOLS 16 // For payload only. +#define IL2P_MAX_ENCODED_PAYLOAD_SIZE (IL2P_MAX_PAYLOAD_SIZE + IL2P_MAX_PAYLOAD_BLOCKS * IL2P_MAX_PARITY_SYMBOLS) + +struct il2p_context_s { + + enum { IL2P_SEARCHING = 0, IL2P_HEADER, IL2P_PAYLOAD, IL2P_DECODE } state; + + unsigned int acc; // Accumulate most recent 24 bits for sync word matching. + // Lower 8 bits are also used for accumulating bytes for + // the header and payload. + + int bc; // Bit counter so we know when a complete byte has been accumulated. + + int polarity; // 1 if opposite of expected polarity. + + unsigned char shdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; + // Scrambled header as received over the radio. Includes parity. + int hc; // Number if bytes placed in above. + + unsigned char uhdr[IL2P_HEADER_SIZE]; // Header after FEC and unscrambling. + + int eplen; // Encoded payload length. This is not the nuumber from + // from the header but rather the number of encoded bytes to gather. + + unsigned char spayload[IL2P_MAX_ENCODED_PAYLOAD_SIZE]; + // Scrambled and encoded payload as received over the radio. + int pc; // Number of bytes placed in above. + + int corrected; // Number of symbols corrected by RS FEC. +}; + + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/Waveout.c b/Waveout.c index 31a6c72..77c8b10 100644 --- a/Waveout.c +++ b/Waveout.c @@ -54,12 +54,19 @@ void GetSoundDevices(); // Currently use 1200 samples for TX but 480 for RX to reduce latency -short buffer[2][SendSize * 2]; // Two Transfer/DMA buffers of 0.1 Sec (x2 for Stereo) -short inbuffer[5][ReceiveSize * 2]; // Input Transfer/ buffers of 0.1 Sec (x2 for Stereo) +#define MaxReceiveSize 2048 // Enough for 9600 +#define MaxSendSize 4096 + +short buffer[2][MaxSendSize * 2]; // Two Transfer/DMA buffers of 0.1 Sec (x2 for Stereo) +short inbuffer[5][MaxReceiveSize * 2]; // Input Transfer/ buffers of 0.1 Sec (x2 for Stereo) extern short * DMABuffer; extern int Number; +int ReceiveSize = 512; +int SendSize = 1024; +int using48000 = 0; + int SoundMode = 0; int stdinMode = 0; @@ -248,7 +255,7 @@ short * SendtoCard(unsigned short * buf, int n) while (!(header[!Index].dwFlags & WHDR_DONE)) { - txSleep(10); // Run buckground while waiting + txSleep(5); // Run buckground while waiting } waveOutUnprepareHeader(hWaveOut, &header[!Index], sizeof(WAVEHDR)); @@ -404,6 +411,21 @@ int InitSound(BOOL Report) } } + if (using48000) + { + wfx.nSamplesPerSec = 48000; + wfx.nAvgBytesPerSec = 48000 * 4; + ReceiveSize = 2048; + SendSize = 4096; // 100 mS for now + } + else + { + wfx.nSamplesPerSec = 12000; + wfx.nAvgBytesPerSec = 12000 * 4; + ReceiveSize = 512; + SendSize = 1024; + } + ret = waveOutOpen(&hWaveOut, PlayBackIndex, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER if (ret) @@ -507,7 +529,7 @@ void PollReceivedSamples() return; } - if (inheader[inIndex].dwFlags & WHDR_DONE) + while (inheader[inIndex].dwFlags & WHDR_DONE) { short * ptr = &inbuffer[inIndex][0]; int i; @@ -656,6 +678,7 @@ VOID WriteSamples(short * buffer, int len) short * SoundInit() { Index = 0; + inIndex = 0; return &buffer[0][0]; diff --git a/agwlib.h b/agwlib.h new file mode 100644 index 0000000..688f918 --- /dev/null +++ b/agwlib.h @@ -0,0 +1,45 @@ + +#ifndef AGWLIB_H +#define AGWLIB_H 1 + + +// Call at beginning to start it up. + +int agwlib_init (char *host, char *port, int (*init_func)(void)); + + + +// Send commands to TNC. + + +int agwlib_X_register_callsign (int chan, char *call_from); + +int agwlib_x_unregister_callsign (int chan, char *call_from); + +int agwlib_G_ask_port_information (void); + +int agwlib_C_connect (int chan, char *call_from, char *call_to); + +int agwlib_d_disconnect (int chan, char *call_from, char *call_to); + +int agwlib_D_send_connected_data (int chan, int pid, char *call_from, char *call_to, int data_len, char *data); + +int agwlib_Y_outstanding_frames_for_station (int chan, char *call_from, char *call_to); + + + +// The application must define these. + +void agw_cb_C_connection_received (int chan, char *call_from, char *call_to, int data_len, char *data); +void on_C_connection_received (int chan, char *call_from, char *call_to, int incoming, char *data); + +void agw_cb_d_disconnected (int chan, char *call_from, char *call_to, int data_len, char *data); + +void agw_cb_D_connected_data (int chan, char *call_from, char *call_to, int data_len, char *data); + +void agw_cb_G_port_information (int num_chan, char *chan_descriptions[]); + +void agw_cb_Y_outstanding_frames_for_station (int chan, char *call_from, char *call_to, int frame_count); + + +#endif \ No newline at end of file diff --git a/ais.h b/ais.h new file mode 100644 index 0000000..6b96288 --- /dev/null +++ b/ais.h @@ -0,0 +1,8 @@ + + +void ais_to_nmea (unsigned char *ais, int ais_len, char *nema, int nema_size); + +int ais_parse (char *sentence, int quiet, char *descr, int descr_size, char *mssi, int mssi_size, double *odlat, double *odlon, + float *ofknots, float *ofcourse, float *ofalt_m, char *symtab, char *symbol, char *comment, int comment_size); + +int ais_check_length (int type, int length); diff --git a/aprs_tt.h b/aprs_tt.h new file mode 100644 index 0000000..4d33f48 --- /dev/null +++ b/aprs_tt.h @@ -0,0 +1,191 @@ + +/* aprs_tt.h */ + +#ifndef APRS_TT_H +#define APRS_TT_H 1 + + + +/* + * For holding location format specifications from config file. + * Same thing is also useful for macro definitions. + * We have exactly the same situation of looking for a pattern + * match and extracting fixed size groups of digits. + */ + +struct ttloc_s { + enum { TTLOC_POINT, TTLOC_VECTOR, TTLOC_GRID, TTLOC_UTM, TTLOC_MGRS, TTLOC_USNG, TTLOC_MACRO, TTLOC_MHEAD, TTLOC_SATSQ, TTLOC_AMBIG } type; + + char pattern[20]; /* e.g. B998, B5bbbdddd, B2xxyy, Byyyxxx, BAxxxx */ + /* For macros, it should be all fixed digits, */ + /* and the letters x, y, z. e.g. 911, xxyyyz */ + + union { + + struct { + double lat; /* Specific locations. */ + double lon; + } point; + + struct { + double lat; /* For bearing/direction. */ + double lon; + double scale; /* conversion to meters */ + } vector; + + struct { + double lat0; /* yyy all zeros. */ + double lon0; /* xxx */ + double lat9; /* yyy all nines. */ + double lon9; /* xxx */ + } grid; + + struct { + double scale; + double x_offset; + double y_offset; + long lzone; /* UTM zone, should be 1-60 */ + char latband; /* Latitude band if specified, otherwise space or - */ + char hemi; /* UTM Hemisphere, should be 'N' or 'S'. */ + } utm; + + struct { + char zone[8]; /* Zone and square for USNG/MGRS */ + } mgrs; + + struct { + char prefix[24]; /* should be 10, 6, or 4 digits to be */ + /* prepended to the received sequence. */ + } mhead; + + struct { + char *definition; + } macro; + + }; +}; + + +/* Error codes for sending responses to user. */ + +#define TT_ERROR_OK 0 /* Success. */ +#define TT_ERROR_D_MSG 1 /* D was first char of field. Not implemented yet. */ +#define TT_ERROR_INTERNAL 2 /* Internal error. Shouldn't be here. */ +#define TT_ERROR_MACRO_NOMATCH 3 /* No definition for digit sequence. */ +#define TT_ERROR_BAD_CHECKSUM 4 /* Bad checksum on call. */ +#define TT_ERROR_INVALID_CALL 5 /* Invalid callsign. */ +#define TT_ERROR_INVALID_OBJNAME 6 /* Invalid object name. */ +#define TT_ERROR_INVALID_SYMBOL 7 /* Invalid symbol specification. */ +#define TT_ERROR_INVALID_LOC 8 /* Invalid location. */ +#define TT_ERROR_NO_CALL 9 /* No call or object name included. */ +#define TT_ERROR_INVALID_MHEAD 10 /* Invalid Maidenhead Locator. */ +#define TT_ERROR_INVALID_SATSQ 11 /* Satellite square must be 4 digits. */ +#define TT_ERROR_SUFFIX_NO_CALL 12 /* No known callsign for suffix. */ + +#define TT_ERROR_MAXP1 13 /* Number of items above. i.e. Last number plus 1. */ + + +#if CONFIG_C /* Is this being included from config.c? */ + +/* Must keep in sync with above !!! */ + +static const char *tt_msg_id[TT_ERROR_MAXP1] = { + "OK", + "D_MSG", + "INTERNAL", + "MACRO_NOMATCH", + "BAD_CHECKSUM", + "INVALID_CALL", + "INVALID_OBJNAME", + "INVALID_SYMBOL", + "INVALID_LOC", + "NO_CALL", + "INVALID_MHEAD", + "INVALID_SATSQ", + "SUFFIX_NO_CALL" +}; + +#endif + +/* + * Configuration options for APRStt. + */ + +#define TT_MAX_XMITS 10 + +#define TT_MTEXT_LEN 64 + + +struct tt_config_s { + + int gateway_enabled; /* Send DTMF sequences to APRStt gateway. */ + + int obj_recv_chan; /* Channel to listen for tones. */ + + int obj_xmit_chan; /* Channel to transmit object report. */ + /* -1 for none. This could happen if we */ + /* are only sending to application */ + /* and/or IGate. */ + + int obj_send_to_app; /* send to attached application(s). */ + + int obj_send_to_ig; /* send to IGate. */ + + char obj_xmit_via[AX25_MAX_REPEATERS * (AX25_MAX_ADDR_LEN+1)]; + /* e.g. empty or "WIDE2-1,WIDE1-1" */ + + int retain_time; /* Seconds to keep information about a user. */ + + int num_xmits; /* Number of times to transmit object report. */ + + int xmit_delay[TT_MAX_XMITS]; /* Delay between them. */ + /* e.g. 3 seconds before first transmission then */ + /* delays of 16, 32, seconds etc. in between repeats. */ + + struct ttloc_s *ttloc_ptr; /* Pointer to variable length array of above. */ + int ttloc_size; /* Number of elements allocated. */ + int ttloc_len; /* Number of elements actually used. */ + + double corral_lat; /* The "corral" for unknown locations. */ + double corral_lon; + double corral_offset; + int corral_ambiguity; + + char status[10][TT_MTEXT_LEN]; /* Up to 9 status messages. e.g. "/enroute" */ + /* Position 0 means none and can't be changed. */ + + struct { + char method[AX25_MAX_ADDR_LEN]; /* SPEECH or MORSE[-n] */ + char mtext[TT_MTEXT_LEN]; /* Message text. */ + } response[TT_ERROR_MAXP1]; + + char ttcmd[80]; /* Command to generate custom audible response. */ +}; + + + + +void aprs_tt_init (struct tt_config_s *p_config, int debug); + +void aprs_tt_button (int chan, char button); + + + + + +#define APRSTT_LOC_DESC_LEN 32 /* Need at least 26 */ + +#define APRSTT_DEFAULT_SYMTAB '\\' +#define APRSTT_DEFAULT_SYMBOL 'A' + + +void aprs_tt_dao_to_desc (char *dao, char *str); + +void aprs_tt_sequence (int chan, char *msg); + +int dw_run_cmd (char *cmd, int oneline, char *result, size_t resultsiz); + + +#endif + +/* end aprs_tt.h */ \ No newline at end of file diff --git a/audio_stats.h b/audio_stats.h new file mode 100644 index 0000000..4cf8ad0 --- /dev/null +++ b/audio_stats.h @@ -0,0 +1,7 @@ + + +/* audio_stats.h */ + + +extern void audio_stats (int adev, int nchan, int nsamp, int interval); + diff --git a/ax25.c b/ax25.c index 0dfd22c..e55e286 100644 --- a/ax25.c +++ b/ax25.c @@ -1526,6 +1526,40 @@ void get_exclude_list(char * line, TStringList * list) } } +void get_digi_list(char * line, TStringList * list) +{ + // Convert comma separated list of calls to ax25 format in list + + string axcall; + + char copy[512]; + + char * ptr, *Context; + + if (line[0] == 0) + return; + + strcpy(copy, line); // copy as strtok messes with it + strcat(copy, ","); + + axcall.Length = 7; + axcall.AllocatedLength = 8; + axcall.Data = malloc(8); + + memset(axcall.Data, 0, 8); + + ptr = strtok_s(copy, " ,", &Context); + + while (ptr) + { + if (ConvToAX25(ptr, axcall.Data) == 0) + return; + + Add(list, duplicateString(&axcall)); + ptr = strtok_s(NULL, " ,", &Context); + } +} + void get_exclude_frm(char * line, TStringList * list) @@ -1959,7 +1993,7 @@ void ax25_init() initTStringList(&list_digi_callsigns[i]); initTStringList(&KISS_acked[i]); - get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]); + get_digi_list(MyDigiCall[i], &list_digi_callsigns[i]); get_exclude_list(exclude_callsigns[i], &list_exclude_callsigns[i]); get_exclude_frm(exclude_APRS_frm[i], &list_exclude_APRS_frm[i]); diff --git a/ax25_agw.c b/ax25_agw.c index 00574ec..5432195 100644 --- a/ax25_agw.c +++ b/ax25_agw.c @@ -22,7 +22,7 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include "UZ7HOStuff.h" -extern char modes_name[modes_count][20]; +extern char modes_name[modes_count][21]; extern int RSID_SABM[4]; extern int RSID_UI[4]; extern int RSID_SetModem[4]; diff --git a/ax25_demod.c b/ax25_demod.c index eec2f9f..3e1b5bb 100644 --- a/ax25_demod.c +++ b/ax25_demod.c @@ -29,6 +29,10 @@ void make_rx_frame_FX25(int snd_ch, int rcvr_nr, int emph, string * data); string * memory_ARQ(TStringList * buf, string * data); float GuessCentreFreq(int i); +void ProcessRXFrames(int snd_ch); + +extern struct il2p_context_s *il2p_context[4][16][3]; + /* @@ -115,6 +119,8 @@ longword DCD_header[5] = { 0 }; int dcd_on_hdr[5] = { 0 }; extern int centreFreq[4]; + +float lastangle[4]; // pevious value for differential modes unsigned short n_INTR[5] = { 1,1,1,1,1 }; @@ -154,6 +160,12 @@ int modemtoSoundLR[4] = { 0 }; struct TDetector_t DET[nr_emph + 1][16]; +// Chan, Decoder, Emph + +float Phases[4][16][nr_emph + 1][4096]; +float Mags[4][16][nr_emph + 1][4096]; +int nPhases[4][16][nr_emph + 1]; + TStringList detect_list_l[5]; TStringList detect_list[5]; TStringList detect_list_c[5]; @@ -341,6 +353,10 @@ void chk_dcd1(int snd_ch, int buf_size) { dcd_bit_sync[snd_ch] = blnBusyStatus; } + else if (modem_mode[snd_ch] == MODE_RUH) + { + dcd_bit_sync[snd_ch] = blnBusyStatus; + } else { if (dcd_bit_cnt[snd_ch] > 0) @@ -1341,7 +1357,7 @@ int stats[2] = { 0 }; void decode_stream_MPSK(int snd_ch, int rcvr_nr, float * src, int buf_size, int last) { -#ifndef WIN32 +#ifndef XXXX // Until ASM is converted @@ -2787,6 +2803,9 @@ void decode_stream_BPSK(int last, int snd_ch, int rcvr_nr, int emph, float * src angle = atan2f(sumIQ2, sumIQ1); PSK_IZ1 = PkAmpI; PSK_QZ1 = PkAmpQ; + + float Mag = sqrtf(powf(PSK_IZ1, 2) + powf(PSK_QZ1, 2)); + // Phase corrector if (fabsf(angle) < PI5) @@ -2808,11 +2827,24 @@ void decode_stream_BPSK(int last, int snd_ch, int rcvr_nr, int emph, float * src bit = RX_BIT0; // + // is this the best place to store phase for constellation? + // only for ilp2 for now + if (il2p_mode[snd_ch]) + { + struct il2p_context_s * il2p = il2p_context[snd_ch][rcvr_nr][emph]; + + if (il2p && il2p->state > IL2P_SEARCHING) + { + Phases[snd_ch][rcvr_nr][emph][nPhases[snd_ch][rcvr_nr][emph]] = angle; + Mags[snd_ch][rcvr_nr][emph][nPhases[snd_ch][rcvr_nr][emph]++] = Mag; + if (nPhases[snd_ch][rcvr_nr][emph] > 4090) + nPhases[snd_ch][rcvr_nr][emph]--; + } il2p_rec_bit(snd_ch, rcvr_nr, emph, bit); if (il2p_mode[snd_ch] == IL2P_MODE_ONLY) // Dont try HDLC decode continue; - + } if (bit) stats[1]++; else @@ -2949,7 +2981,6 @@ void decode_stream_QPSK(int last, int snd_ch, int rcvr_nr, int emph, float * src struct TDetector_t * pDET = &DET[emph][rcvr_nr]; - bit_stuff_cnt = pDET->bit_stuff_cnt[snd_ch]; last_rx_bit = pDET->last_rx_bit[snd_ch]; sample_cnt = pDET->sample_cnt[snd_ch]; @@ -3073,6 +3104,8 @@ void decode_stream_QPSK(int last, int snd_ch, int rcvr_nr, int emph, float * src PSK_IZ1 = PkAmpI; PSK_QZ1 = PkAmpQ; + float Mag = sqrtf(powf(PSK_IZ1, 2) + powf(PSK_QZ1, 2)); + if (angle > pi || angle < -pi) angle = angle; @@ -3120,6 +3153,8 @@ void decode_stream_QPSK(int last, int snd_ch, int rcvr_nr, int emph, float * src } else { + // "Normal" QPSK + // Phase corrector // I think this sends 0 90 180 270 @@ -3158,6 +3193,26 @@ void decode_stream_QPSK(int last, int snd_ch, int rcvr_nr, int emph, float * src { dibit = dibit << 1; + // is this the best place to store phase for constellation? + // only for ilp2 for now + + if (il2p_mode[snd_ch]) + { + struct il2p_context_s * il2p = il2p_context[snd_ch][rcvr_nr][emph]; + + if (il2p && il2p->state > IL2P_SEARCHING) + { + Phases[snd_ch][rcvr_nr][emph][nPhases[snd_ch][rcvr_nr][emph]] = angle; + Mags[snd_ch][rcvr_nr][emph][nPhases[snd_ch][rcvr_nr][emph]++] = Mag; + if (nPhases[snd_ch][rcvr_nr][emph] > 4090) + nPhases[snd_ch][rcvr_nr][emph]--; + } + + il2p_rec_bit(snd_ch, rcvr_nr, emph, (dibit & RX_BIT1)); + if (il2p_mode[snd_ch] == IL2P_MODE_ONLY) // Dont try HDLC decode + continue; + } + // NRZI if (last_rx_bit == (dibit & RX_BIT1)) @@ -3328,7 +3383,13 @@ void decode_stream_8PSK(int last, int snd_ch, int rcvr_nr, int emph, float * src dcd_bit_cnt[snd_ch] = 0; } - baudrate = 1600 / 6; + // Not sure how this works + + if (tx_baudrate[snd_ch] == 300) + baudrate = 300; + else + baudrate = 1600 / 6; + div_bit_afc = 1.0 / round(BIT_AFC*(RX_Samplerate / 11025)); x = baudrate / RX_Samplerate; max_cnt = round(RX_Samplerate / baudrate) + 1; @@ -3365,7 +3426,7 @@ void decode_stream_8PSK(int last, int snd_ch, int rcvr_nr, int emph, float * src bit_osc = bit_osc + x; if (bit_osc >= 1) - { + { if (sample_cnt <= max_cnt) for (k = sample_cnt; k <= max_cnt; k++) bit_buf[k] = 0.95*bit_buf[k]; @@ -3403,6 +3464,8 @@ void decode_stream_8PSK(int last, int snd_ch, int rcvr_nr, int emph, float * src PSK_IZ1 = PkAmpI; PSK_QZ1 = PkAmpQ; + float Mag = sqrtf(powf(PSK_IZ1, 2) + powf(PSK_QZ1, 2)); + // Phase corrector if (fabsf(angle) < PI125) @@ -3431,8 +3494,7 @@ void decode_stream_8PSK(int last, int snd_ch, int rcvr_nr, int emph, float * src AngleCorr = AngleCorr * 0.95 - KCorr * 0.05; angle = angle + AngleCorr; - // - + if (fabsf(angle) < PI125) tribit = 1; if (angle >= PI125 && angle < PI375) @@ -3455,6 +3517,29 @@ void decode_stream_8PSK(int last, int snd_ch, int rcvr_nr, int emph, float * src for (j = 0; j < 3; j++) { tribit = tribit << 1; + + // look for il2p before nrzi + + // is this the best place to store phase for constellation? + // only for ilp2 for now + + if (il2p_mode[snd_ch]) + { + struct il2p_context_s * il2p = il2p_context[snd_ch][rcvr_nr][emph]; + + if (il2p && il2p->state > IL2P_SEARCHING) + { + Phases[snd_ch][rcvr_nr][emph][nPhases[snd_ch][rcvr_nr][emph]] = angle; + Mags[snd_ch][rcvr_nr][emph][nPhases[snd_ch][rcvr_nr][emph]++] = Mag; + if (nPhases[snd_ch][rcvr_nr][emph] > 4090) + nPhases[snd_ch][rcvr_nr][emph]--; + } + + il2p_rec_bit(snd_ch, rcvr_nr, emph, tribit & RX_BIT1); + if (il2p_mode[snd_ch] == IL2P_MODE_ONLY) // Dont try HDLC decode + continue; + } + //NRZI if (last_rx_bit == (tribit & RX_BIT1)) @@ -3733,6 +3818,14 @@ void make_core_INTR(UCHAR snd_ch) n_INTR[snd_ch] = 1; break; + + case SPEED_Q300: + case SPEED_8PSK300: + + width = roundf(RX_Samplerate / 2); + n_INTR[snd_ch] = 1; + break; + case SPEED_600: width = roundf(RX_Samplerate / 4); @@ -3755,6 +3848,11 @@ void make_core_INTR(UCHAR snd_ch) n_INTR[snd_ch] = 4; break; +// case SPEED_Q1200: +// width = roundf(RX_Samplerate / 8); +// n_INTR[snd_ch] = 4; +// break; + case SPEED_Q2400: width = 300; n_INTR[snd_ch] = 4; @@ -3766,7 +3864,7 @@ void make_core_INTR(UCHAR snd_ch) n_INTR[snd_ch] = 4; break; - case SPEED_AE2400: + case SPEED_2400V26B: width = 300; n_INTR[snd_ch] = 4; @@ -3784,6 +3882,7 @@ void make_core_INTR(UCHAR snd_ch) break; case SPEED_8P4800: + width = 100; n_INTR[snd_ch] = 6; break; @@ -4121,7 +4220,7 @@ void Demodulator(int snd_ch, int rcvr_nr, float * src_buf, int last, int xcenter QPSK_Demodulator(snd_ch, rcvr_nr, emph_db[snd_ch], last); } - // QPSK demodulator + // 8PSK demodulator if (modem_mode[snd_ch]==MODE_8PSK) { @@ -4147,120 +4246,125 @@ void Demodulator(int snd_ch, int rcvr_nr, float * src_buf, int last, int xcenter // I think this handles multiple decoders and passes packet on to next level // Packet manager - if (last) + ProcessRXFrames(snd_ch); +} + +void ProcessRXFrames(int snd_ch) +{ + boolean fecflag = 0; + char indicators[5] = "-$#F+"; // None, Single, MEM, FEC, Normal + + // Work out which decoder and which emph settings worked. + + if (snd_ch < 0 || snd_ch >3) + return; + + if (detect_list[snd_ch].Count > 0) // no point if nothing decoded { - boolean fecflag = 0; + char decoded[32] = ""; char indicators[5] = "-$#F+"; // None, Single, MEM, FEC, Normal + char s_emph[4] = ""; + int emph[4] = { 0 }; + char report[32] = ""; + int il2perrors = 255; - // Work out which decoder and which emph settings worked. + // The is one DET for each Decoder for each Emph setting - if (detect_list[snd_ch].Count > 0) // no point if nothing decoded + struct TDetector_t * pDET; + int i = 0, j, found; + int maxemph = nr_emph; + + for (i = 0; i <= nr_emph; i++) { - char decoded[32] = ""; - char indicators[5] = "-$#F+"; // None, Single, MEM, FEC, Normal - char s_emph[4] = ""; - int emph[4] = { 0 }; - char report[32] = ""; - int il2perrors = 255; + for (j = 0; j <= RCVR[snd_ch] * 2; j++) + { + pDET = &DET[i][j]; - // The is one DET for each Decoder for each Emph setting + if (pDET->rx_decoded > decoded[j]) // Better than other one (| is higher than F) + decoded[j] = pDET->rx_decoded; - struct TDetector_t * pDET; - int i = 0, j; - int maxemph = nr_emph; + if (pDET->emph_decoded > emph[i]) + emph[i] = pDET->emph_decoded; + if (il2perrors > pDET->errors) + il2perrors = pDET->errors; + + pDET->rx_decoded = 0; + pDET->emph_decoded = 0; // Ready for next time + pDET->errors = 255; + } + if (emph_all[snd_ch] == 0) + break; + } + + decoded[j] = 0; + + for (j--; j >= 0; j--) + decoded[j] = indicators[decoded[j]]; + + if (emph_all[snd_ch]) + { for (i = 0; i <= nr_emph; i++) { - for (j = 0; j <= RCVR[snd_ch] * 2; j++) - { - pDET = &DET[i][j]; - - if (pDET->rx_decoded > decoded[j]) // Better than other one (| is higher than F) - decoded[j] = pDET->rx_decoded; - - if (pDET->emph_decoded > emph[i]) - emph[i] = pDET->emph_decoded; - - if (il2perrors > pDET->errors) - il2perrors = pDET->errors; - - pDET->rx_decoded = 0; - pDET->emph_decoded = 0; // Ready for next time - pDET->errors = 255; - } - if (emph_all[snd_ch] == 0) - break; + s_emph[i] = indicators[emph[i]]; } + sprintf(report, "%s][%s", s_emph, decoded); + } - decoded[j] = 0; + else + strcpy(report, decoded); - for (j--; j >= 0; j--) - decoded[j] = indicators[decoded[j]]; + if (detect_list_c[snd_ch].Items[0]->Length) + { + if (il2perrors < 255 && il2perrors > 0) + sprintf(detect_list_c[snd_ch].Items[0]->Data, "%s-%d", detect_list_c[snd_ch].Items[0]->Data, il2perrors); - if (emph_all[snd_ch]) + strcat(report, "]["); + strcat(report, detect_list_c[snd_ch].Items[0]->Data); + } + + if (detect_list[snd_ch].Count > 0) + { + for (i = 0; i < detect_list[snd_ch].Count; i++) { - for (i = 0; i <= nr_emph; i++) + found = 0; + + // if (detect_list_l[snd_ch].Count > 0) + // if (my_indexof(&detect_list_l[snd_ch], detect_list[snd_ch].Items[i]) > -1) + // found = 1; + + if (found == 0) { - s_emph[i] = indicators[emph[i]]; - } - sprintf(report, "%s][%s", s_emph, decoded); - } - - else - strcpy(report, decoded); - - if (detect_list_c[snd_ch].Items[0]->Length) - { - if (il2perrors < 255 && il2perrors > 0) - sprintf(detect_list_c[snd_ch].Items[0]->Data, "%s-%d", detect_list_c[snd_ch].Items[0]->Data, il2perrors); - - strcat(report, "]["); - strcat(report, detect_list_c[snd_ch].Items[0]->Data); - } - - if (detect_list[snd_ch].Count > 0) - { - for (i = 0; i < detect_list[snd_ch].Count; i++) - { - found = 0; - - // if (detect_list_l[snd_ch].Count > 0) - // if (my_indexof(&detect_list_l[snd_ch], detect_list[snd_ch].Items[i]) > -1) - // found = 1; - - if (found == 0) + if (modem_mode[snd_ch] == MODE_MPSK) { - if (modem_mode[snd_ch] == MODE_MPSK) - { - // analiz_frame(snd_ch, detect_list[snd_ch].Items[i]->Data, [snd_ch].Items[i]->Data + ' dF: ' + FloatToStrF(DET[0, 0].AFC_dF[snd_ch], ffFixed, 0, 1)); - } - else - { - analiz_frame(snd_ch, detect_list[snd_ch].Items[i], report, fecflag); - } + // analiz_frame(snd_ch, detect_list[snd_ch].Items[i]->Data, [snd_ch].Items[i]->Data + ' dF: ' + FloatToStrF(DET[0, 0].AFC_dF[snd_ch], ffFixed, 0, 1)); + } + else + { + analiz_frame(snd_ch, detect_list[snd_ch].Items[i], report, fecflag); } } - - // Cancel FX25 decode - - if (fx25_mode[snd_ch] != FX25_MODE_NONE) - { - int e; - - for (i = 0; i < 16; i++) - for (e = 0; e <= nr_emph; e++) - DET[e][i].fx25[snd_ch].status = FX25_TAG; - } } - // Assign(&detect_list_l[snd_ch], &detect_list[snd_ch]); // Duplicate detect_list to detect_list_l + // Cancel FX25 decode - Clear(&detect_list[snd_ch]); - Clear(&detect_list_c[snd_ch]); + if (fx25_mode[snd_ch] != FX25_MODE_NONE) + { + int e; + + for (i = 0; i < 16; i++) + for (e = 0; e <= nr_emph; e++) + DET[e][i].fx25[snd_ch].status = FX25_TAG; + } } - chk_dcd1(snd_ch, rx_bufsize); + + // Assign(&detect_list_l[snd_ch], &detect_list[snd_ch]); // Duplicate detect_list to detect_list_l + + Clear(&detect_list[snd_ch]); + Clear(&detect_list_c[snd_ch]); } + chk_dcd1(snd_ch, rx_bufsize); } string * memory_ARQ(TStringList * buf, string * data) diff --git a/ax25_link.h b/ax25_link.h new file mode 100644 index 0000000..40fa401 --- /dev/null +++ b/ax25_link.h @@ -0,0 +1,88 @@ + +/* ax25_link.h */ + + +#ifndef AX25_LINK_H +#define AX25_LINK_H 1 + +#include "ax25_pad.h" // for AX25_MAX_INFO_LEN + +#include "dlq.h" // for dlq_item_t + +#include "config.h" // for struct misc_config_s + + + +// Limits and defaults for parameters. + + +#define AX25_N1_PACLEN_MIN 1 // Max bytes in Information part of frame. +#define AX25_N1_PACLEN_DEFAULT 256 // some v2.0 implementations have 128 +#define AX25_N1_PACLEN_MAX AX25_MAX_INFO_LEN // from ax25_pad.h + + +#define AX25_N2_RETRY_MIN 1 // Number of times to retry before giving up. +#define AX25_N2_RETRY_DEFAULT 10 +#define AX25_N2_RETRY_MAX 15 + + +#define AX25_T1V_FRACK_MIN 1 // Number of seconds to wait before retrying. +#define AX25_T1V_FRACK_DEFAULT 3 // KPC-3+ has 4. TM-D710A has 3. +#define AX25_T1V_FRACK_MAX 15 + + +#define AX25_K_MAXFRAME_BASIC_MIN 1 // Window size - number of I frames to send before waiting for ack. +#define AX25_K_MAXFRAME_BASIC_DEFAULT 4 +#define AX25_K_MAXFRAME_BASIC_MAX 7 + +#define AX25_K_MAXFRAME_EXTENDED_MIN 1 +#define AX25_K_MAXFRAME_EXTENDED_DEFAULT 32 +#define AX25_K_MAXFRAME_EXTENDED_MAX 63 // In theory 127 but I'm restricting as explained in SREJ handling. + + + +// Call once at startup time. + +void ax25_link_init (struct misc_config_s *pconfig); + + + +// IMPORTANT: + +// These functions must be called on a single thread, one at a time. +// The Data Link Queue (DLQ) is used to serialize events from multiple sources. + +// Maybe the dispatch switch should be moved to ax25_link.c so they can all +// be made static and they can't be called from the wrong place accidentally. + +void dl_connect_request (dlq_item_t *E); + +void dl_disconnect_request (dlq_item_t *E); + +void dl_data_request (dlq_item_t *E); + +void dl_register_callsign (dlq_item_t *E); + +void dl_unregister_callsign (dlq_item_t *E); + +void dl_outstanding_frames_request (dlq_item_t *E); + +void dl_client_cleanup (dlq_item_t *E); + + +void lm_data_indication (dlq_item_t *E); + +void lm_seize_confirm (dlq_item_t *E); + +void lm_channel_busy (dlq_item_t *E); + + +void dl_timer_expiry (void); + + +double ax25_link_get_next_timer_expiry (void); + + +#endif + +/* end ax25_link.h */ \ No newline at end of file diff --git a/ax25_mod-DESKTOP-MHE5LO8.c b/ax25_mod-DESKTOP-MHE5LO8.c new file mode 100644 index 0000000..4c803c2 --- /dev/null +++ b/ax25_mod-DESKTOP-MHE5LO8.c @@ -0,0 +1,1810 @@ +/* +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" + +// I assume this modulates (and sends?} frames + +int RSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen); + +//unit ax25_mod; + +//interface + +//uses sysutils,classes,math + +extern int SampleNo; + +extern BOOL KISSServ; + +extern TStringList KISS_acked[]; +extern TStringList KISS_iacked[]; + +extern UCHAR modem_mode[]; + +#define sbc 175 + +extern single ch_offset[4]; +int Continuation[4] = { 0, 0, 0, 0 }; // Sending 2nd or more packet of burst + +#define COS45 0.70710676908493f + +#define TX_SILENCE 0 +#define TX_DELAY 1 +#define TX_TAIL 2 +#define TX_NO_DATA 3 +#define TX_FRAME 4 +#define TX_WAIT_BPF 5 + + +#define TX_BIT0 0 +#define TX_BIT1 1 +#define FRAME_EMPTY 0 +#define FRAME_FULL 1 +#define FRAME_NO_FRAME 2 +#define FRAME_NEW_FRAME 3 +#define BYTE_EMPTY 0 +#define BYTE_FULL 1 + + +UCHAR gray_8PSK[8] = {7,0,6,5,2,1,3,4}; // ?? was 1::8 + +UCHAR gray_PI4QPSK[4] = {3,1,5,7}; + + +float audio_buf[5][32768]; // [1..4,0..32767] +float tx_src_BPF_buf[5][32768]; +float tx_BPF_buf[5][32768]; +float tx_prev_BPF_buf[5][32768]; +float tx_BPF_core[5][32768]; + +long tx_delay_cnt[5] = {0}; // : array[1..4] of longword=(0,0,0,0}; +long tx_tail_cnt[5] = {0}; + +int tx_hitoneraisedb[5] = {0}; // : array[1..4] of integer=(0,0,0,0}; +float tx_hitoneraise[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_freq[5] = { 1000, 1000, 1000, 1000, 1000}; // : array[1..4] of single=(1000,1000,1000,1000}; +float tx_shift[5] = { 200, 200, 200, 200, 200}; // : array[1..4] of single=(200,200,200,200}; +float tx_bit_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_osc[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_bit_osc[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +unsigned short txbpf[5] = { 400, 400, 400, 400, 400}; // : array[1..4] of word=(400,400,400,400}; +unsigned short tx_BPF_tap[5] = { 256, 256, 256, 256, 256}; // : array[1..4] of word=(256,256,256,256}; +unsigned short tx_baudrate[5] = { 300, 300, 300, 300, 300 }; // : array[1..4] of word=(300,300,300,300}; +unsigned short tx_bitrate[5] = { 300, 300, 300, 300, 300 }; // : array[1..4] of word=(300,300,300,300}; +unsigned short tx_BPF_timer[5] = {0}; // : array[1..4] of word=(0,0,0,0}; +UCHAR tx_pol[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_last_pol[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_last_diddle[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_flag_cnt[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_frame_status[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_byte_status[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_status[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_bit_stuff_cnt[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_bit_cnt[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_last_bit[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_bit_stream[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; + +UCHAR tx_8PSK[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_QPSK[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; + +float tx_I_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_Q_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_QPSK_avg_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_avg_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_df_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_df_Q [5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_old_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_old_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_avg_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_avg_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_df_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_df_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_old_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_old_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; + +float tx_osc1[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_osc2[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_osc3[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_osc4[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +short tx_inv1[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_inv2[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_inv3[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_inv4[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_old_inv1[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_old_inv2[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_old_inv3[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_old_inv4[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +float tx_bit1_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_bit2_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_bit3_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_bit4_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +UINT tx_viterbi[5] = {0}; // : array[1..4] of word=(0,0,0,0}; +UCHAR tx_intv_tbl[5][4]; // : array[1..4,0..3] of byte; + +short tx_inv[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +BOOL tx_change_phase[5] = {0}; // : array[1..4] of boolean=(FALSE,FALSE,FALSE,FALSE}; +BOOL tx_bs_bit[5] = {0}; // : array[1..4] of boolean=(FALSE,FALSE,FALSE,FALSE}; + +string * tx_data[5] = {0}; // : array[1..4] of string=('','','',''}; +int tx_data_len[5] = {0}; + +int tx_fx25_size[4] = { 0, 0, 0, 0 }; +int tx_fx25_size_cnt[4] = { 0, 0, 0, 0 }; +int tx_fx25_mode[4] = { 0, 0, 0, 0 }; + + +// uses sm_main,ax25,ax25_agw,ax25_demod,rsunit; + +UCHAR tx_nrzi(UCHAR snd_ch, UCHAR bit) +{ +// Debugprintf("Before NRZI %d", bit); + + if (bit == TX_BIT0) + { + // Zero so switch bit + + tx_last_bit[snd_ch] ^= 1; + } + return tx_last_bit[snd_ch]; +} + +BOOL tx_bit_stuffing(UCHAR snd_ch, UCHAR bit) +{ + // result = FALSE; + // if bit=TX_BIT1 then inc(tx_bit_stuff_cnt[snd_ch]}; + // if bit=TX_BIT0 then tx_bit_stuff_cnt[snd_ch] = 0; + // if tx_bit_stuff_cnt[snd_ch]=5 then begin tx_bit_stuff_cnt[snd_ch] = 0; result = TRUE; end; +//end; + + if (bit == TX_BIT1) + tx_bit_stuff_cnt[snd_ch]++; + + if (bit == TX_BIT0) + tx_bit_stuff_cnt[snd_ch] = 0; + + if (tx_bit_stuff_cnt[snd_ch] == 5) + { + tx_bit_stuff_cnt[snd_ch] = 0; + return TRUE; + } + + return FALSE; +} + + + + +void interleave(char *s, int len) +{ +// var + // data: string; + // i,k,len: word; + // nr_blocks: word; +//begin{ +// data = ''; + // len = length(s}; + // if len>0 then nr_blocks = ((len-1} div 16}+1 else nr_blocks = 1; + // for i = 1 to 16 do + // for k = 0 to nr_blocks-1 do + // if (i+k*16}<=len then data = data+s[i+k*16]; + // result = data; +//end; + + char data[1024]; + + UINT i,k; + UINT nr_blocks; + int n = 0; + + if (len > 0) + nr_blocks = ((len - 1) / 16) + 1; + else + nr_blocks = 1; + + for (i = 0; i < 16; i++) + { + for (k = 0; k < nr_blocks; k++) + { + if ((i + k * 16) <= len) + data[n++] = s[i + k * 16]; + } + } + + memcpy(s, data, len); +} + +//procedure get_new_frame(snd_ch: byte; var frame_stream: TStringList}; +//var +// header,line,temp: string; +// len,i,size: word; + // crc: word; +//begin + +void get_new_frame(UCHAR snd_ch, TStringList * frame_stream) +{ + UCHAR header[256]; + UCHAR line[1024]; + + int LineLen; + + string ** Items; + + string * myTemp; + + UCHAR temp[1024]; + + UINT len, i, size; + UINT crc; + + tx_bs_bit[snd_ch] = FALSE; + tx_bit_cnt[snd_ch] = 0; + tx_flag_cnt[snd_ch] = 0; + tx_bit_stuff_cnt[snd_ch] = 0; + tx_bit_stream[snd_ch] = FRAME_FLAG; + tx_frame_status[snd_ch] = FRAME_NEW_FRAME; + tx_byte_status[snd_ch] = BYTE_EMPTY; + + if (frame_stream->Count == 0) + { + tx_frame_status[snd_ch] = FRAME_NO_FRAME; + return; + } + + // We now pass control byte and ack bytes on front and pointer to socket on end if ackmode + + myTemp = Strings(frame_stream, 0); // get message + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[snd_ch], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + tx_data[snd_ch] = duplicateString(myTemp); // so can free original below + + Delete(frame_stream, 0); // This will invalidate temp + + AGW_AX25_frame_analiz(snd_ch, FALSE, tx_data[snd_ch]); + + put_frame(snd_ch, tx_data[snd_ch], "", TRUE, FALSE); + + if (tx_data[snd_ch]->Length == 0 || modem_mode[snd_ch] != MODE_MPSK) + return; + + // Reformat MPSK Data + + //Take data 8 bytes at a time and add 8 bytes of RS data + + LineLen = 0; + + while (tx_data[snd_ch]->Length > 0) + { + size = tx_data[snd_ch]->Length; + + if (size > 8) + size = 8; + + memcpy(temp, tx_data[snd_ch]->Data, size); + + // Delete the chars from tx_data + + mydelete(tx_data[snd_ch], 0, 8); + + memset(xData, 0, sizeof(xData)); + memset(xEncoded, 0, sizeof(xEncoded)); + + memcpy(xData, temp, size); + + InitBuffers(); + EncodeRS(xData, xEncoded); // This puts the 8 RS bytes in xEncoded + + memcpy(&line[LineLen], xData, size); + memcpy(&line[LineLen + size], xEncoded, MaxErrors * 2); + + LineLen += size + (MaxErrors * 2); + } + + + + + len = LineLen; + + interleave(line, LineLen); + scrambler(line, LineLen); + + header[0] = 0x7e; + header[1] = 0x7e; + header[2] = len >> 8; + header[3] = len; + + crc = get_fcs(header, 4); + + header[4] = crc >> 8; + header[5] = crc; + + memset(xData, 0, sizeof(xData)); + memset(xEncoded, 0, sizeof(xEncoded)); + memmove(xData, header, 6); + + + // RSEncode(xData, xEncoded, 6 + (MaxErrors * 2), MaxErrors * 2); + + InitBuffers(); + EncodeRS(xData, xEncoded); + + fx25_encode_rs(xData, xEncoded, 0, 8); + + + // We should now have RS Encoded Header in xEncoded; + + // I think we send encoded header then line + + tx_data[snd_ch]->Length = 0; + + stringAdd(tx_data[snd_ch], xData, 6); + stringAdd(tx_data[snd_ch], xEncoded, MaxErrors * 2); + stringAdd(tx_data[snd_ch], line, LineLen); + + // For testing, descramble and de-interleve + + scrambler(line, LineLen); // should look like interleaved + { + Byte unscrambled[1024]; + int count, len; + int origlen; + + len = LineLen; + count = (len + 15) / 16; + + int j1, j2, j3, i, j; + + j3 = 0; + + for (j1 = 0; j1 < 16; j1++) + { + // Each char in block + + for (j2 = 0; j2 < count; j2++) + { + // Blocks + + unscrambled[j2 * 16 + j1] = line[j3]; + j3++; + } + } + + // Now remove RS (will check later) + + i = 0; + j = 0; + + while (j < len) + { + Byte line1[256]; + int nErr, eras_pos = 0; + Byte rs_block[256]; + + memcpy(line1, &unscrambled[j], 16); + + memset(xEncoded, 0, sizeof(xEncoded)); + memset(xDecoded, 0, sizeof(xDecoded)); + + memcpy(xEncoded, &unscrambled[j], 16); + +// nErr = DecodeRS(xEncoded, xDecoded); + + memset(rs_block, 0, 255); + memcpy(rs_block, &unscrambled[j], 8); + memcpy(&rs_block[255 - 8], &unscrambled[j+8], 8); + + nErr = fx25_decode_rs(rs_block, &eras_pos, 0, 0, 8); + + +// line1 = ''; +// for j1 = MaxErrors * 2 to size - 1 do line1 = line1 + chr(xDecoded[j1]); + + + memcpy(&unscrambled[i], &unscrambled[j], 8); + i += 8; + j += 16; + } + + j3 = j3; + + } + +} + + + +int get_new_bit(Byte snd_ch, Byte bit) +{ + unsigned short len; + string * s; + + if (tx_frame_status[snd_ch] == FRAME_FULL) + { + if (tx_byte_status[snd_ch] == BYTE_EMPTY) + { + len = tx_data[snd_ch]->Length; + + if (len > 0) + { + s = tx_data[snd_ch]; + tx_bit_stream[snd_ch] = (s->Data[0]); + tx_frame_status[snd_ch] = FRAME_FULL; + tx_byte_status[snd_ch] = BYTE_FULL; + tx_bit_cnt[snd_ch] = 0; + mydelete(tx_data[snd_ch], 0, 1); + } + + else tx_frame_status[snd_ch] = FRAME_EMPTY; + } + + if (tx_byte_status[snd_ch] == BYTE_FULL) + bit = tx_bit_stream[snd_ch] & TX_BIT1; + + if (modem_mode[snd_ch] == MODE_MPSK) + { + tx_bit_cnt[snd_ch]++; + tx_bit_stream[snd_ch] = tx_bit_stream[snd_ch] >> 1; + if (tx_bit_cnt[snd_ch] >= 8) + tx_byte_status[snd_ch] = BYTE_EMPTY; + + } + else + { + if (tx_bs_bit[snd_ch]) + bit = TX_BIT0; + + tx_bs_bit[snd_ch] = tx_bit_stuffing(snd_ch, bit); + + if (!tx_bs_bit[snd_ch]) + { + tx_bit_cnt[snd_ch]++; + tx_bit_stream[snd_ch] >>= 1; + if (tx_bit_cnt[snd_ch] >= 8 && !tx_bs_bit[snd_ch]) + tx_byte_status[snd_ch] = BYTE_EMPTY; + } + } + } + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + + if ((tx_frame_status[snd_ch] == FRAME_NEW_FRAME) || (tx_frame_status[snd_ch] == FRAME_NO_FRAME)) + { + bit = tx_bit_stream[snd_ch] & TX_BIT1; + tx_flag_cnt[snd_ch]++; + tx_bit_stream[snd_ch] >>= 1; + + if (tx_flag_cnt[snd_ch] == 8) + { + switch (tx_frame_status[snd_ch]) + { + case FRAME_NEW_FRAME: + + tx_frame_status[snd_ch] = FRAME_FULL; + break; + + case FRAME_NO_FRAME: + + tx_tail_cnt[snd_ch] = 0; + tx_frame_status[snd_ch] = FRAME_EMPTY; + tx_status[snd_ch] = TX_TAIL; + + break; + } + } + } + return bit; +} + +////// FX.25 ////// + + +void bit_to_fx25(Byte * tx_byte, Byte * bit_cnt, Byte bit, string * data, int * data_cnt) +{ + *tx_byte = (*tx_byte >> 1) | (bit << 7); + (*bit_cnt)++; + + if (*bit_cnt == 8) + { + stringAdd(data, tx_byte, 1); + *bit_cnt = 0; + } + (*data_cnt)++; +} + +string * fill_fx25_data(int snd_ch, string * data) +{ +#define nr_tags 5 + + string * result; + + Byte rs_roots[nr_tags + 1] = { 16, 32, 64, 32, 16, 16 }; + word rs_payload[nr_tags + 1] = { 1912, 1784, 1528, 1024, 512, 256 }; // 239, 233, 191, 128, 64, 32 + + unsigned long long rs_tag[nr_tags + 1] = + { + 0xB74DB7DF8A532F3E, // 255 / 16 (239) + 0x6E260B1AC5835FAE, // 255 / 32 (223) + 0x3ADB0C13DEAE2836, // 255 / 64 (191) + 0xFF94DC634F1CFF4E, // 160 / 32 (128) + 0xC7DC0508F3D9B09E, // 80 / 16 (64) + 0x8F056EB4369660EE // 48 / 16 (32) + }; + +// 0x26FF60A600CC8FDE) 144; = 16; +// 0x1EB7B9CDBC09C00E) 96; 32; +// 0xDBF869BD2DBB1776) 64;= 32; +// 0xAB69DB6A543188D6) 192; = 64; +// 0x4A4ABEC4A724B796) 128; = 64; + + string * ax25_data = newString(); + + int i, ax25_size; + Byte a, bit, bit_cnt, bit_cnt1, bs, tx_byte; + Byte rs_id; + Byte rs_block[256], parity[256]; + + ax25_size = 0; + bs = 0; + tx_byte = 0; + bit_cnt = 0; + + // Load start flag + a = FRAME_FLAG; + + for (i = 0; i < 8; i++) + { + bit = a & 1; + a = a >> 1; + bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size); + } + + // Load body + for (i = 0; i < data->Length; i++) + { + bit_cnt1 = 0; + a = data->Data[i]; + do + { + if (bs == 5) + { + bit = TX_BIT0; + bs = 0; + } + else + { + bit = a & 1; + a = a >> 1; + bit_cnt1++; + + if (bit == TX_BIT1) + bs++; + else + bs = 0; + } + + bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size); + + } while (bit_cnt1 != 8 || bs == 5); + } + + // Load close flag + + a = FRAME_FLAG; + + for (i = 0; i < 8; i++) + { + bit = a & 1; + a = a >> 1; + bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size); + } + + a = FRAME_FLAG; + + // if too short or too long + + if (ax25_size < 168 || ax25_size > 1912) // < 21 or > 239 + { + // Send as normal ax25 packet + + if (bit_cnt > 0) + { + do + { + tx_byte = tx_byte >> 1; + bit_cnt++; + if (bit_cnt == 8) + stringAdd(ax25_data, &tx_byte, 1); + } while (bit_cnt < 8); + } + tx_fx25_size[snd_ch] = ax25_size; + return ax25_data; + } + + // Send as FX25 Message + + // find RS block size + + rs_id = 0; + + for (i = 0; i <= nr_tags; i++) + if (ax25_size <= rs_payload[i]) + rs_id = i; + + // Padding to block size + + while (ax25_size != rs_payload[rs_id]) + { + bit = a & 1; + a = (a >> 1) | (bit << 7); + bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size); + } + + memset(rs_block, 0, 255); + move(&ax25_data->Data[0], &rs_block[0], ax25_data->Length); + + fx25_encode_rs(rs_block, parity, 0, rs_roots[rs_id]); + + result = newString(); + + stringAdd(result, (Byte *)&rs_tag[rs_id], 8); + stringAdd(result, ax25_data->Data, ax25_data->Length); + stringAdd(result, parity, rs_roots[rs_id]); + + tx_fx25_size[snd_ch] = result->Length << 3; + + freeString(ax25_data); + return result; +} + +void fx25_get_new_frame(int snd_ch, TStringList * frame_stream) +{ + string * myTemp; + + tx_bs_bit[snd_ch] = 0; + tx_bit_cnt[snd_ch] = 0; + tx_flag_cnt[snd_ch] = 0; + tx_bit_stuff_cnt[snd_ch] = 0; + tx_fx25_size_cnt[snd_ch] = 0; + tx_fx25_size[snd_ch] = 1; + tx_frame_status[snd_ch] = FRAME_NEW_FRAME; + tx_byte_status[snd_ch] = BYTE_EMPTY; + if (frame_stream->Count == 0) + tx_frame_status[snd_ch] = FRAME_NO_FRAME; + else + { + // We now pass control byte and ack bytes on front and pointer to socket on end if ackmode + + myTemp = Strings(frame_stream, 0); // get message + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[snd_ch], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + AGW_AX25_frame_analiz(snd_ch, FALSE, myTemp); + put_frame(snd_ch, myTemp, "", TRUE, FALSE); + + tx_data[snd_ch] = fill_fx25_data(snd_ch, myTemp); + + Delete(frame_stream, 0); // This will invalidate temp + } +} + +int fx25_get_new_bit(int snd_ch, Byte bit) +{ + string *s; + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + { + fx25_get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + if (tx_frame_status[snd_ch] == FRAME_NEW_FRAME) + tx_frame_status[snd_ch] = FRAME_FULL; + } + + if (tx_frame_status[snd_ch] == FRAME_FULL) + { + if (tx_byte_status[snd_ch] == BYTE_EMPTY) + { + if (tx_data[snd_ch]->Length) + { + s = tx_data[snd_ch]; + + tx_bit_stream[snd_ch] = s->Data[0]; + tx_frame_status[snd_ch] = FRAME_FULL; + tx_byte_status[snd_ch] = BYTE_FULL; + tx_bit_cnt[snd_ch] = 0; + mydelete(tx_data[snd_ch], 0, 1); + } + else + tx_frame_status[snd_ch] = FRAME_EMPTY; + } + if (tx_byte_status[snd_ch] == BYTE_FULL) + { + bit = tx_bit_stream[snd_ch] & TX_BIT1; + tx_bit_stream[snd_ch] = tx_bit_stream[snd_ch] >> 1; + tx_bit_cnt[snd_ch]++; + tx_fx25_size_cnt[snd_ch]++; + if (tx_bit_cnt[snd_ch] >= 8) + tx_byte_status[snd_ch] = BYTE_EMPTY; + if (tx_fx25_size_cnt[snd_ch] == tx_fx25_size[snd_ch]) + tx_frame_status[snd_ch] = FRAME_EMPTY; + } + } + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + { + fx25_get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + + switch (tx_frame_status[snd_ch]) + { + case FRAME_NEW_FRAME: + tx_frame_status[snd_ch] = FRAME_FULL; + break; + + case FRAME_NO_FRAME: + tx_tail_cnt[snd_ch] = 0; + tx_frame_status[snd_ch] = FRAME_EMPTY; + tx_status[snd_ch] = TX_TAIL; + break; + } + } + return bit; +} + + +////////////////// + + +int get_new_bit_tail(UCHAR snd_ch, UCHAR bit) +{ + long _txtail = 0; + UCHAR _diddles; + + if (modem_mode[snd_ch] == MODE_FSK) + _diddles = diddles; + else + _diddles = 0; + + if (modem_mode[snd_ch] == MODE_FSK) + _txtail = txtail[snd_ch]; + + else if (modem_mode[snd_ch] == MODE_BPSK) + _txtail = txtail[snd_ch]; + + else if (modem_mode[snd_ch] == MODE_8PSK) + _txtail = txtail[snd_ch] * 3; + + else if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK) + _txtail = txtail[snd_ch] << 1; + + else if (modem_mode[snd_ch] == MODE_MPSK) + _txtail = txtail[snd_ch] << 2; + + _txtail = (_txtail * tx_baudrate[snd_ch]) / 1000; + + if (qpsk_set[snd_ch].mode == QPSK_V26 || modem_mode[snd_ch] == MODE_8PSK) + _diddles = 2; + + switch (_diddles) + { + case 0: + + if (tx_tail_cnt[snd_ch] < _txtail) + { + bit = TX_BIT0; + tx_tail_cnt[snd_ch]++; + } + else + { + tx_status[snd_ch] = TX_WAIT_BPF; + } + + break; + + case 1: + + if (tx_tail_cnt[snd_ch] < _txtail) + { + if (tx_last_diddle[snd_ch] == TX_BIT0) + bit = TX_BIT1; + else + bit = TX_BIT0; + + tx_tail_cnt[snd_ch]++; + tx_last_diddle[snd_ch] = bit; + } + else + { + Debugprintf("End TXTAIL %d", SampleNo); + tx_status[snd_ch] = TX_WAIT_BPF; + } + + break; + + case 2: + + if (tx_tail_cnt[snd_ch] < _txtail) + { + bit = FRAME_FLAG >> (tx_tail_cnt[snd_ch] % 8) & 1; + tx_tail_cnt[snd_ch]++; + } + else + { + Debugprintf("End TXTAIL %d", SampleNo); + tx_status[snd_ch] = TX_WAIT_BPF; + } + break; + } + return bit; +} + +int get_new_bit_delay(UCHAR snd_ch, UCHAR bit) +{ + ULONG _txdelay = 0; + UCHAR _diddles; + + _diddles = 0; + + switch (modem_mode[snd_ch]) + { + case MODE_FSK: + + _diddles = diddles; + break; + + case MODE_PI4QPSK: + case MODE_8PSK: + + _diddles = 2; + break; + + case MODE_QPSK: + + if (qpsk_set[snd_ch].mode == QPSK_V26) + _diddles = 2; + break; + } + + if (modem_mode[snd_ch] == MODE_FSK) + _txdelay = txdelay[snd_ch]; + + else if (modem_mode[snd_ch] == MODE_BPSK) + _txdelay = txdelay[snd_ch]; + + else if (modem_mode[snd_ch] == MODE_8PSK) + _txdelay = txdelay[snd_ch] * 3; + + else if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK) + _txdelay = txdelay[snd_ch] << 1; + + else if (modem_mode[snd_ch] == MODE_MPSK) + { + if (txdelay[snd_ch] < 400) + _txdelay = 400 << 2; //AFC delay + else + _txdelay = txdelay[snd_ch] << 2; + } + + _txdelay = (_txdelay * tx_baudrate[snd_ch]) / 1000; + + switch (_diddles) + { + case 0: + + if (tx_delay_cnt[snd_ch] < _txdelay) + { + bit = TX_BIT0; + tx_delay_cnt[snd_ch]++; + } + else + { + tx_status[snd_ch] = TX_FRAME; + } + + break; + + case 1: + + if (tx_delay_cnt[snd_ch] < _txdelay) + { + if (tx_last_diddle[snd_ch] == TX_BIT0) + bit = TX_BIT1; + else + bit = TX_BIT0; + + tx_delay_cnt[snd_ch]++; + tx_last_diddle[snd_ch] = bit; + } + else + { + tx_status[snd_ch] = TX_FRAME; + Debugprintf("End TXD %d", SampleNo); + } + break; + + case 2: + + // Send Flags + + if (tx_delay_cnt[snd_ch] < _txdelay) + { + bit = FRAME_FLAG >> ((8 - (_txdelay % 8) + tx_delay_cnt[snd_ch]) % 8) & 1; + tx_delay_cnt[snd_ch]++; + } + else + { + tx_status[snd_ch] = TX_FRAME; + Debugprintf("End TXD %d", SampleNo); + } + break; + } + return bit; +} + +// is this waiting for the filter to fill? +// No, flushing BPF + +void get_wait_bpf(UCHAR snd_ch) +{ + tx_BPF_timer[snd_ch]++; + + if (tx_BPF_timer[snd_ch] == tx_BPF_tap[snd_ch] ) + { + tx_status[snd_ch] = TX_NO_DATA; + tx_BPF_timer[snd_ch] = 0; + } +} + + +//procedure modulator(snd_ch: byte; var buf: array of single; buf_size: word}; +//{ +/* +function filter(x,k: single}: single; +begin + result = k*cos(x}; + if result>1 then result = 1; + if result<-1 then result = -1; +end; +} +*/ + +single filter(single x) +{ + if (x <= PI25) + return 1.0f; + + if (x >= PI75) + return -1.0f; + + return cosf(2.0f * x -PI5); +} + + +// make_samples return one sample of the waveform + +// But seems to be called only once per bit ?? + +// No, but needs to preserve bit between calls + +float make_samples(unsigned char snd_ch, unsigned char * bitptr) +{ + float pi2, x1, x; + Byte i,qbit,tribit,dibit; + float z1,z2,z3,z4; + unsigned short b, msb, lsb; + unsigned char bit = *bitptr; + + float amp = 0; + + pi2 = 2 * pi / TX_Samplerate; + x1 = pi * tx_baudrate[snd_ch] / TX_Samplerate; + + if (modem_mode[snd_ch] == MODE_FSK) + { + if (bit == TX_BIT0) + x = pi2*(tx_freq[snd_ch] + 0.5f * tx_shift[snd_ch]); + else + x = pi2*(tx_freq[snd_ch] - 0.5f * tx_shift[snd_ch]); + + amp = 1.0f; + + if (tx_baudrate[snd_ch] > 600) + { + if (tx_hitoneraisedb[snd_ch] < 0 && bit == TX_BIT0) + amp = tx_hitoneraise[snd_ch]; + + if (tx_hitoneraisedb[snd_ch] > 0 && bit == TX_BIT1) + amp = tx_hitoneraise[snd_ch]; + } + + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + + if (tx_osc[snd_ch] > 2*pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2*pi; + } + + else if (modem_mode[snd_ch] == MODE_BPSK) + { + if (tx_change_phase[snd_ch]) + tx_bit_mod[snd_ch] = tx_inv[snd_ch] * cos(tx_bit_osc[snd_ch]); + + x = pi2 * (tx_freq[snd_ch]); + + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + + if (tx_osc[snd_ch] > 2 * pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi; + } + + else if (modem_mode[snd_ch] == MODE_QPSK) + { + if (tx_QPSK_old_I[snd_ch] != tx_QPSK_I[snd_ch]) + + tx_I_mod[snd_ch] = tx_QPSK_avg_I[snd_ch] + tx_QPSK_df_I[snd_ch] * filter(tx_bit_osc[snd_ch]); + else + tx_I_mod[snd_ch] = tx_QPSK_I[snd_ch]; + + if (tx_QPSK_old_Q[snd_ch] != tx_QPSK_Q[snd_ch]) + tx_Q_mod[snd_ch] = tx_QPSK_avg_Q[snd_ch] + tx_QPSK_df_Q[snd_ch] * filter(tx_bit_osc[snd_ch]); + else + tx_Q_mod[snd_ch] = tx_QPSK_Q[snd_ch]; + + x = pi2 * (tx_freq[snd_ch]); + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + if (tx_osc[snd_ch] > 2 * pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi; + } + + else if (modem_mode[snd_ch] == MODE_8PSK || modem_mode[snd_ch] == MODE_PI4QPSK) + { + if (tx_8PSK_old_I[snd_ch] != tx_8PSK_I[snd_ch]) + tx_I_mod[snd_ch] = tx_8PSK_avg_I[snd_ch] + tx_8PSK_df_I[snd_ch] * filter(tx_bit_osc[snd_ch]); + else + tx_I_mod[snd_ch] = tx_8PSK_I[snd_ch]; + + if (tx_8PSK_old_Q[snd_ch] != tx_8PSK_Q[snd_ch]) + tx_Q_mod[snd_ch] = tx_8PSK_avg_Q[snd_ch] + tx_8PSK_df_Q[snd_ch] * filter(tx_bit_osc[snd_ch]); + else + tx_Q_mod[snd_ch] = tx_8PSK_Q[snd_ch]; + + x = pi2 * (tx_freq[snd_ch]); + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + + if (tx_osc[snd_ch] > 2 * pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi; + + } + + else if (modem_mode[snd_ch] == MODE_MPSK) + { + z1 = pi2 * (tx_freq[snd_ch] + ch_offset[0]); + z2 = pi2 * (tx_freq[snd_ch] + ch_offset[1]); + z3 = pi2 * (tx_freq[snd_ch] + ch_offset[2]); + z4 = pi2 * (tx_freq[snd_ch] + ch_offset[3]); + + tx_osc1[snd_ch] = tx_osc1[snd_ch] + z1; + tx_osc2[snd_ch] = tx_osc2[snd_ch] + z2; + tx_osc3[snd_ch] = tx_osc3[snd_ch] + z3; + tx_osc4[snd_ch] = tx_osc4[snd_ch] + z4; + + if (tx_osc1[snd_ch] > 2 * pi) + tx_osc1[snd_ch] = tx_osc1[snd_ch] - 2 * pi; + + if (tx_osc2[snd_ch] > 2 * pi) + tx_osc2[snd_ch] = tx_osc2[snd_ch] - 2 * pi; + + if (tx_osc3[snd_ch] > 2 * pi) + tx_osc3[snd_ch] = tx_osc3[snd_ch] - 2 * pi; + + if (tx_osc4[snd_ch] > 2 * pi) + tx_osc4[snd_ch] = tx_osc4[snd_ch] - 2 * pi; + + if (tx_old_inv1[snd_ch] != tx_inv1[snd_ch]) + tx_bit1_mod[snd_ch] = tx_inv1[snd_ch] * cos(tx_bit_osc[snd_ch]); + else + tx_bit1_mod[snd_ch] = -tx_inv1[snd_ch]; + + if (tx_old_inv2[snd_ch] != tx_inv2[snd_ch]) + tx_bit2_mod[snd_ch] = tx_inv2[snd_ch] * cos(tx_bit_osc[snd_ch]); + else + tx_bit2_mod[snd_ch] = -tx_inv2[snd_ch]; + + if (tx_old_inv3[snd_ch] != tx_inv3[snd_ch]) + tx_bit3_mod[snd_ch] = tx_inv3[snd_ch] * cos(tx_bit_osc[snd_ch]); + else + tx_bit3_mod[snd_ch] = -tx_inv3[snd_ch]; + + if (tx_old_inv4[snd_ch] != tx_inv4[snd_ch]) + tx_bit4_mod[snd_ch] = tx_inv4[snd_ch] * cos(tx_bit_osc[snd_ch]); + else + tx_bit4_mod[snd_ch] = -tx_inv4[snd_ch]; + } + + tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] + x1; + + if (tx_bit_osc[snd_ch] > pi) + { + // This seems to get the next bit, + // but why?? - end of samples for last bit + + tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] - pi; + + // FSK Mode + if (modem_mode[snd_ch] == MODE_FSK) + { + bit = 0; + + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + tx_status[snd_ch] = TX_DELAY; + } + + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + { + // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME + + if (tx_status[snd_ch] == TX_DELAY) + tx_status[snd_ch] = TX_FRAME; + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = il2p_get_new_bit(snd_ch, bit); + + + // No nrzi for il2p + + *bitptr = bit; + } + else + { + // ax25/fx25 + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + { + if (tx_fx25_mode[snd_ch]) + bit = fx25_get_new_bit(snd_ch, bit); + else + bit = get_new_bit(snd_ch, bit); + } + + *bitptr = tx_nrzi(snd_ch, bit); + + } + } + + // BPSK Mode + if (modem_mode[snd_ch] == MODE_BPSK) + { + bit = 0; + + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + Debugprintf("Start TXD"); + tx_status[snd_ch] = TX_DELAY; + } + + + + // il2p generates TXDELAY as part of the frame, so go straight too TX_FRAME + + if (tx_status[snd_ch] == TX_DELAY) + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + tx_status[snd_ch] = TX_FRAME; + else + bit = get_new_bit_delay(snd_ch, bit); + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + { + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + bit = il2p_get_new_bit(snd_ch, bit); + else if (tx_fx25_mode[snd_ch]) + bit = fx25_get_new_bit(snd_ch, bit); + else + bit = get_new_bit(snd_ch, bit); + } + // ?? *bitptr = tx_nrzi(snd_ch, bit); + + if (bit == 0) + { + tx_inv[snd_ch] = -tx_inv[snd_ch]; + tx_change_phase[snd_ch] = TRUE; + } + else + tx_change_phase[snd_ch] = FALSE; + } + + // QPSK Mode + + else if (modem_mode[snd_ch] == MODE_QPSK) + { + dibit = 0; + for (i = 0; i < 2; i++) + { + bit = 0; + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + tx_status[snd_ch] = TX_DELAY; + } + + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + { + if (tx_status[snd_ch] == TX_DELAY) + tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = il2p_get_new_bit(snd_ch, bit); + + // No nrzi for il2p + + dibit = (dibit << 1) | bit; + } + else + { + // ax25/fx25 + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + dibit = (dibit << 1) | tx_nrzi(snd_ch, bit); + + } + } + + + dibit = qpsk_set[snd_ch].tx[dibit & 3]; + tx_QPSK[snd_ch] = (tx_QPSK[snd_ch] + dibit) & 3; + tx_QPSK_old_I[snd_ch] = tx_QPSK_I[snd_ch]; + tx_QPSK_old_Q[snd_ch] = tx_QPSK_Q[snd_ch]; + + switch (tx_QPSK[snd_ch]) + { + case 0: + + tx_QPSK_I[snd_ch] = COS45; + tx_QPSK_Q[snd_ch] = COS45; + break; + + case 1: + + tx_QPSK_I[snd_ch] = -COS45; + tx_QPSK_Q[snd_ch] = COS45; + break; + + case 2: + + tx_QPSK_I[snd_ch] = -COS45; + tx_QPSK_Q[snd_ch] = -COS45; + break; + + case 3: + + tx_QPSK_I[snd_ch] = COS45; + tx_QPSK_Q[snd_ch] = -COS45; + break; + } + + tx_QPSK_avg_I[snd_ch] = 0.5f*(tx_QPSK_old_I[snd_ch] + tx_QPSK_I[snd_ch]); + tx_QPSK_df_I[snd_ch] = 0.5f*(tx_QPSK_old_I[snd_ch] - tx_QPSK_I[snd_ch]); + tx_QPSK_avg_Q[snd_ch] = 0.5f*(tx_QPSK_old_Q[snd_ch] + tx_QPSK_Q[snd_ch]); + tx_QPSK_df_Q[snd_ch] = 0.5f*(tx_QPSK_old_Q[snd_ch] - tx_QPSK_Q[snd_ch]); + } + + // PI/4 QPSK Mode + + if (modem_mode[snd_ch] == MODE_PI4QPSK) + { + dibit = 0; + + for (i = 0; i < 2; i++) + { + bit = 0; + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + Debugprintf("Start TXD"); + tx_status[snd_ch] = TX_DELAY; + } + + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + { + if (tx_status[snd_ch] == TX_DELAY) + tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = il2p_get_new_bit(snd_ch, bit); + + // No nrzi for il2p + + dibit = (dibit << 1) | bit; + } + else + { + // ax25/fx25 + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + + *bitptr = tx_nrzi(snd_ch, bit); + + dibit = (dibit << 1) | *bitptr; + } + } + + // This returns 3,1,5 or 7 so we use the odd enties in the 8PSK table + + dibit = gray_PI4QPSK[dibit & 3]; + + tx_8PSK[snd_ch] = (tx_8PSK[snd_ch] + dibit) & 7; + tx_8PSK_old_I[snd_ch] = tx_8PSK_I[snd_ch]; + tx_8PSK_old_Q[snd_ch] = tx_8PSK_Q[snd_ch]; + + switch (tx_8PSK[snd_ch]) + { + case 0: + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = 1; + break; + + case 1: + tx_8PSK_I[snd_ch] = COS45; + tx_8PSK_Q[snd_ch] = COS45; + break; + + case 2: + tx_8PSK_I[snd_ch] = 1; + tx_8PSK_Q[snd_ch] = 0; + break; + + case 3: + tx_8PSK_I[snd_ch] = COS45; + tx_8PSK_Q[snd_ch] = -COS45; + break; + + case 4: + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = -1; + break; + + case 5: + tx_8PSK_I[snd_ch] = -COS45; + tx_8PSK_Q[snd_ch] = -COS45; + break; + + case 6: + tx_8PSK_I[snd_ch] = -1; + tx_8PSK_Q[snd_ch] = 0; + break; + + case 7: + tx_8PSK_I[snd_ch] = -COS45; + tx_8PSK_Q[snd_ch] = COS45; + break; + + } + + tx_8PSK_avg_I[snd_ch] = 0.5*(tx_8PSK_old_I[snd_ch] + tx_8PSK_I[snd_ch]); + tx_8PSK_df_I[snd_ch] = 0.5*(tx_8PSK_old_I[snd_ch] - tx_8PSK_I[snd_ch]); + tx_8PSK_avg_Q[snd_ch] = 0.5*(tx_8PSK_old_Q[snd_ch] + tx_8PSK_Q[snd_ch]); + tx_8PSK_df_Q[snd_ch] = 0.5*(tx_8PSK_old_Q[snd_ch] - tx_8PSK_Q[snd_ch]); + + } + + // 8PSK Mode + + if (modem_mode[snd_ch] == MODE_8PSK) + { + tribit = 0; + for (i = 0; i < 3; i++) + { + bit = 0; + + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + tx_status[snd_ch] = TX_DELAY; + } + + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + { + if (tx_status[snd_ch] == TX_DELAY) + tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = il2p_get_new_bit(snd_ch, bit); + + // No nrzi for il2p + + tribit = (tribit << 1) | bit; + } + else + { + // ax25/fx25 + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + + tribit = (tribit << 1) | tx_nrzi(snd_ch, bit); + } + } + tribit = gray_8PSK[tribit & 7]; + + tx_8PSK[snd_ch] = (tx_8PSK[snd_ch] + tribit) & 7; + tx_8PSK_old_I[snd_ch] = tx_8PSK_I[snd_ch]; + tx_8PSK_old_Q[snd_ch] = tx_8PSK_Q[snd_ch]; + + switch (tx_8PSK[snd_ch]) + { + case 0: + + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = 1; + break; + + case 1: + + tx_8PSK_I[snd_ch] = COS45; + tx_8PSK_Q[snd_ch] = COS45; + break; + + case 2: + + tx_8PSK_I[snd_ch] = 1; + tx_8PSK_Q[snd_ch] = 0; + break; + + case 3: + + tx_8PSK_I[snd_ch] = COS45; + tx_8PSK_Q[snd_ch] = -COS45; + break; + + case 4: + + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = -1; + break; + + case 5: + + tx_8PSK_I[snd_ch] = -COS45; + tx_8PSK_Q[snd_ch] = -COS45; + break; + + case 6: + + tx_8PSK_I[snd_ch] = -1; + tx_8PSK_Q[snd_ch] = 0; + break; + + case 7: + + tx_8PSK_I[snd_ch] = -COS45; + tx_8PSK_Q[snd_ch] = COS45; + break; + + } + + tx_8PSK_avg_I[snd_ch] = 0.5f*(tx_8PSK_old_I[snd_ch] + tx_8PSK_I[snd_ch]); + tx_8PSK_df_I[snd_ch] = 0.5f*(tx_8PSK_old_I[snd_ch] - tx_8PSK_I[snd_ch]); + tx_8PSK_avg_Q[snd_ch] = 0.5f*(tx_8PSK_old_Q[snd_ch] + tx_8PSK_Q[snd_ch]); + tx_8PSK_df_Q[snd_ch] = 0.5f*(tx_8PSK_old_Q[snd_ch] - tx_8PSK_Q[snd_ch]); + } + + if (modem_mode[snd_ch] == MODE_MPSK) + { + qbit = 0; + + // get the bits for each of 4 carriers + + for (i = 1; i <= 4; i++) + { + bit = 0; + + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + Debugprintf("Start TXD"); + tx_status[snd_ch] = TX_DELAY; + } + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + + qbit = (qbit << 1) | bit; + } + + tx_old_inv1[snd_ch] = tx_inv1[snd_ch]; + tx_old_inv2[snd_ch] = tx_inv2[snd_ch]; + tx_old_inv3[snd_ch] = tx_inv3[snd_ch]; + tx_old_inv4[snd_ch] = tx_inv4[snd_ch]; + + if ((qbit & 8) == 0) + tx_inv1[snd_ch] = -tx_inv1[snd_ch]; + if ((qbit & 4) == 0) + tx_inv2[snd_ch] = -tx_inv2[snd_ch]; + if ((qbit & 2) == 0) + tx_inv3[snd_ch] = -tx_inv3[snd_ch]; + if ((qbit & 1) == 0) + tx_inv4[snd_ch] = -tx_inv4[snd_ch]; + + } + } + + if (tx_status[snd_ch] == TX_WAIT_BPF) + get_wait_bpf(snd_ch); + + if (modem_mode[snd_ch] == MODE_FSK) + return amp * sinf(tx_osc[snd_ch]); + + if (modem_mode[snd_ch] == MODE_BPSK) + return sinf(tx_osc[snd_ch]) * tx_bit_mod[snd_ch]; + + if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_8PSK || modem_mode[snd_ch] == MODE_PI4QPSK) + return sin(tx_osc[snd_ch]) * tx_I_mod[snd_ch] + cos(tx_osc[snd_ch]) * tx_Q_mod[snd_ch]; + + if (modem_mode[snd_ch] == MODE_MPSK) + return 0.35*(sinf(tx_osc1[snd_ch])*tx_bit1_mod[snd_ch] + + sinf(tx_osc2[snd_ch])*tx_bit2_mod[snd_ch] + + sinf(tx_osc3[snd_ch])*tx_bit3_mod[snd_ch] + + sinf(tx_osc4[snd_ch])*tx_bit4_mod[snd_ch]); + + return 0.0f; +} + +float make_samples_calib(UCHAR snd_ch, UCHAR tones) +{ + float amp, pi2, x, x1; + + x1 = pi * tx_baudrate[snd_ch] / TX_Samplerate; + pi2 = 2 * pi / TX_Samplerate; + + switch (tones) + { + case 1: + + tx_last_bit[snd_ch] = 1; + break; + + case 2: + + tx_last_bit[snd_ch] = 0; + break; + + case 3: + + tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] + x1; + + if (tx_bit_osc[snd_ch] > pi) + { + tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] - pi; + tx_last_bit[snd_ch] = tx_last_bit[snd_ch] ^ 1; + } + break; + } + + amp = 1; + + if (tx_baudrate[snd_ch] > 600) + { + if (tx_hitoneraisedb[snd_ch] < 0 && tx_last_bit[snd_ch] == 0) + amp = tx_hitoneraise[snd_ch]; + + if (tx_hitoneraisedb[snd_ch] > 0 && tx_last_bit[snd_ch] == 1) + amp = tx_hitoneraise[snd_ch]; + } + + if (tx_last_bit[snd_ch] == 0) + x = pi2*(tx_freq[snd_ch] + 0.5f * tx_shift[snd_ch]); + else + x = pi2*(tx_freq[snd_ch] - 0.5f * tx_shift[snd_ch]); + + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + + if (tx_osc[snd_ch] > 2*pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi; + + return amp * sinf(tx_osc[snd_ch]); +} + +int amplitude = 22000; + +void modulator(UCHAR snd_ch, int buf_size) +{ + // We feed samples to samplesink instead of buffering them + + // I think this is the top of the TX hierarchy + + int i; + short Sample; + + if (calib_mode[snd_ch] > 0) + { + if (calib_mode[snd_ch] == 4) // CWID + { + if (tx_status[snd_ch] == TX_SILENCE) + { + SoundIsPlaying = TRUE; + Debugprintf("Start CWID Chan %d", snd_ch); + RadioPTT(snd_ch, 1); + + tx_status[snd_ch] = 6; + } + + if (ARDOPSendToCard(snd_ch, SendSize) == 1) + { + // End of TX + + tx_status[snd_ch] = TX_SILENCE; // Stop TX + Flush(); + RadioPTT(snd_ch, 0); + Debugprintf("End CWID"); + calib_mode[snd_ch] = 0; + } + return; + } + + + if (tx_status[snd_ch] == TX_SILENCE) + { + SoundIsPlaying = TRUE; + Debugprintf("Start Calib Chan %d", snd_ch); + RadioPTT(snd_ch, 1); + + tx_bit_osc[snd_ch] = 0; + tx_last_bit[snd_ch] = 0; + + // fill filter + + for (i = 0; i < tx_BPF_tap[snd_ch]; i++) + tx_prev_BPF_buf[snd_ch][buf_size + i] = make_samples_calib(snd_ch,calib_mode[snd_ch]); + } + tx_status[snd_ch] = TX_WAIT_BPF; + + for (i = 0; i < buf_size; i++) + tx_src_BPF_buf[snd_ch][i] = make_samples_calib(snd_ch, calib_mode[snd_ch]); + + FIR_filter(tx_src_BPF_buf[snd_ch],buf_size,tx_BPF_tap[snd_ch],tx_BPF_core[snd_ch],tx_BPF_buf[snd_ch],tx_prev_BPF_buf[snd_ch]); + + for (i = 0; i < buf_size; i++) + { + Sample = tx_BPF_buf[snd_ch][i] * amplitude; + SampleSink(modemtoSoundLR[snd_ch], Sample); + } + } + else + { + if (tx_status[snd_ch] == TX_SILENCE) + { + if (fx25_mode[snd_ch] == FX25_MODE_TXRX) + tx_fx25_mode[snd_ch] = 1; + else + tx_fx25_mode[snd_ch] = 0; + + tx_bit_osc[snd_ch] = 0; + tx_8PSK[snd_ch] = 0; + tx_QPSK[snd_ch] = 0; + tx_last_bit[snd_ch] = 0; + tx_inv1[snd_ch] = 1; + tx_inv2[snd_ch] = 1; + tx_inv3[snd_ch] = 1; + tx_inv4[snd_ch] = 1; + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = 1; + tx_8PSK_old_I[snd_ch] = 0; + tx_8PSK_old_Q[snd_ch] = 1; + tx_QPSK_I[snd_ch] = COS45; + tx_QPSK_Q[snd_ch] = COS45; + tx_QPSK_old_I[snd_ch] = COS45; + tx_QPSK_old_Q[snd_ch] = COS45; + + for (i = 0; i < tx_BPF_tap[snd_ch]; i++) + tx_prev_BPF_buf[snd_ch][buf_size+i] = make_samples(snd_ch, &tx_pol[snd_ch]); + } + + for (i = 0; i < buf_size; i++) + tx_src_BPF_buf[snd_ch][i] = make_samples(snd_ch, &tx_pol[snd_ch]); + + FIR_filter(tx_src_BPF_buf[snd_ch], buf_size, tx_BPF_tap[snd_ch], tx_BPF_core[snd_ch], tx_BPF_buf[snd_ch], tx_prev_BPF_buf[snd_ch]); + + for (i = 0; i < buf_size; i++) + { + Sample = tx_BPF_buf[snd_ch][i] * 20000.0f; + SampleSink(modemtoSoundLR[snd_ch], Sample); + } + } +} + + diff --git a/ax25_mod.c b/ax25_mod.c index 64d21e1..f8fd4bc 100644 --- a/ax25_mod.c +++ b/ax25_mod.c @@ -45,6 +45,7 @@ extern UCHAR modem_mode[]; #define sbc 175 extern single ch_offset[4]; +int Continuation[4] = { 0, 0, 0, 0 }; // Sending 2nd or more packet of burst #define COS45 0.70710676908493f @@ -89,7 +90,8 @@ float tx_osc[5] = {0}; // : array[1..4] of single=(0,0,0,0}; float tx_bit_osc[5] = {0}; // : array[1..4] of single=(0,0,0,0}; unsigned short txbpf[5] = { 400, 400, 400, 400, 400}; // : array[1..4] of word=(400,400,400,400}; unsigned short tx_BPF_tap[5] = { 256, 256, 256, 256, 256}; // : array[1..4] of word=(256,256,256,256}; -unsigned short tx_baudrate[5] = { 300, 300, 300, 300, 300}; // : array[1..4] of word=(300,300,300,300}; +unsigned short tx_baudrate[5] = { 300, 300, 300, 300, 300 }; // : array[1..4] of word=(300,300,300,300}; +unsigned short tx_bitrate[5] = { 300, 300, 300, 300, 300 }; // : array[1..4] of word=(300,300,300,300}; unsigned short tx_BPF_timer[5] = {0}; // : array[1..4] of word=(0,0,0,0}; UCHAR tx_pol[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; UCHAR tx_last_pol[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; @@ -1191,9 +1193,11 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) { + // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME + if (tx_status[snd_ch] == TX_DELAY) - tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME - + tx_status[snd_ch] = TX_FRAME; + if (tx_status[snd_ch] == TX_TAIL) bit = get_new_bit_tail(snd_ch, bit); @@ -1240,6 +1244,8 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) tx_status[snd_ch] = TX_DELAY; } + + // il2p generates TXDELAY as part of the frame, so go straight too TX_FRAME if (tx_status[snd_ch] == TX_DELAY) @@ -1273,7 +1279,7 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) // QPSK Mode - if (modem_mode[snd_ch] == MODE_QPSK) + else if (modem_mode[snd_ch] == MODE_QPSK) { dibit = 0; for (i = 0; i < 2; i++) @@ -1284,13 +1290,35 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) tx_delay_cnt[snd_ch] = 0; tx_status[snd_ch] = TX_DELAY; } - if (tx_status[snd_ch] == TX_DELAY) - bit = get_new_bit_delay(snd_ch, bit); - if (tx_status[snd_ch] == TX_TAIL) - bit = get_new_bit_tail(snd_ch, bit); - if (tx_status[snd_ch] == TX_FRAME) - bit = get_new_bit(snd_ch, bit); - dibit = (dibit << 1) | tx_nrzi(snd_ch, bit); + + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + { + if (tx_status[snd_ch] == TX_DELAY) + tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = il2p_get_new_bit(snd_ch, bit); + + // No nrzi for il2p + + dibit = (dibit << 1) | bit; + } + else + { + // ax25/fx25 + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + dibit = (dibit << 1) | tx_nrzi(snd_ch, bit); + + } } @@ -1348,19 +1376,38 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) tx_status[snd_ch] = TX_DELAY; } - if (tx_status[snd_ch] == TX_DELAY) - bit = get_new_bit_delay(snd_ch, bit); + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + { + if (tx_status[snd_ch] == TX_DELAY) + tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME - if (tx_status[snd_ch] == TX_TAIL) - bit = get_new_bit_tail(snd_ch, bit); + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); - if (tx_status[snd_ch] == TX_FRAME) - bit = get_new_bit(snd_ch, bit); + if (tx_status[snd_ch] == TX_FRAME) + bit = il2p_get_new_bit(snd_ch, bit); - *bitptr = tx_nrzi(snd_ch, bit); + // No nrzi for il2p - dibit = (dibit << 1) | *bitptr; + dibit = (dibit << 1) | bit; + } + else + { + // ax25/fx25 + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + + *bitptr = tx_nrzi(snd_ch, bit); + + dibit = (dibit << 1) | *bitptr; + } } // This returns 3,1,5 or 7 so we use the odd enties in the 8PSK table @@ -1436,16 +1483,36 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) tx_delay_cnt[snd_ch] = 0; tx_status[snd_ch] = TX_DELAY; } - if (tx_status[snd_ch] == TX_DELAY) - bit = get_new_bit_delay(snd_ch, bit); - if (tx_status[snd_ch] == TX_TAIL) - bit = get_new_bit_tail(snd_ch, bit); - if (tx_status[snd_ch] == TX_FRAME) - bit = get_new_bit(snd_ch, bit); - tribit = (tribit << 1) | tx_nrzi(snd_ch, bit); + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + { + if (tx_status[snd_ch] == TX_DELAY) + tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = il2p_get_new_bit(snd_ch, bit); + + // No nrzi for il2p + + tribit = (tribit << 1) | bit; + } + else + { + // ax25/fx25 + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + + tribit = (tribit << 1) | tx_nrzi(snd_ch, bit); + } } - tribit = gray_8PSK[tribit & 7]; tx_8PSK[snd_ch] = (tx_8PSK[snd_ch] + tribit) & 7; @@ -1632,7 +1699,7 @@ float make_samples_calib(UCHAR snd_ch, UCHAR tones) return amp * sinf(tx_osc[snd_ch]); } -int amplitude = 22000; +float amplitude = 32000; void modulator(UCHAR snd_ch, int buf_size) { @@ -1641,7 +1708,7 @@ void modulator(UCHAR snd_ch, int buf_size) // I think this is the top of the TX hierarchy int i; - short Sample; + int Sample; if (calib_mode[snd_ch] > 0) { @@ -1694,6 +1761,12 @@ void modulator(UCHAR snd_ch, int buf_size) for (i = 0; i < buf_size; i++) { Sample = tx_BPF_buf[snd_ch][i] * amplitude; + + if (Sample < txmin) + txmin = Sample; + else if (Sample > txmax) + txmax = Sample; + SampleSink(modemtoSoundLR[snd_ch], Sample); } } @@ -1734,7 +1807,22 @@ void modulator(UCHAR snd_ch, int buf_size) for (i = 0; i < buf_size; i++) { - Sample = tx_BPF_buf[snd_ch][i] * 20000.0f; + Sample = tx_BPF_buf[snd_ch][i] * amplitude; + + if (Sample < txmin) + txmin = Sample; + else if (Sample > txmax) + { + txmax = Sample; + + if (txmax > 32767) + { + amplitude = amplitude * 32767 / txmax; + txmax = 32767; + Sample = 32767; + } + } + SampleSink(modemtoSoundLR[snd_ch], Sample); } } diff --git a/ax25_pad2.h b/ax25_pad2.h new file mode 100644 index 0000000..c6dc17a --- /dev/null +++ b/ax25_pad2.h @@ -0,0 +1,55 @@ +/*------------------------------------------------------------------- + * + * Name: ax25_pad2.h + * + * Purpose: Header file for using ax25_pad2.c + * ax25_pad dealt only with UI frames. + * This adds a facility for the other types: U, s, I. + * + *------------------------------------------------------------------*/ + +#ifndef AX25_PAD2_H +#define AX25_PAD2_H 1 + +#include "ax25_pad.h" + + + + +#if AX25MEMDEBUG // to investigate a memory leak problem + + + +packet_t ax25_u_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len, char *src_file, int src_line); + +packet_t ax25_s_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, unsigned char *pinfo, int info_len, char *src_file, int src_line); + +packet_t ax25_i_frame_debug (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len, char *src_file, int src_line); + + +#define ax25_u_frame(a,n,c,f,p,q,i,l) ax25_u_frame_debug(a,n,c,f,p,q,i,l,__FILE__,__LINE__) + +#define ax25_s_frame(a,n,c,f,m,r,p,i,l) ax25_s_frame_debug(a,n,c,f,m,r,p,i,l,__FILE__,__LINE__) + +#define ax25_i_frame(a,n,c,m,r,s,p,q,i,l) ax25_i_frame_debug(a,n,c,m,r,s,p,q,i,l,__FILE__,__LINE__) + + +#else + +packet_t ax25_u_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len); + +packet_t ax25_s_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, unsigned char *pinfo, int info_len); + +packet_t ax25_i_frame (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len); + + +#endif + + + + +#endif /* AX25_PAD2_H */ + +/* end ax25_pad2.h */ + + diff --git a/beacon.h b/beacon.h new file mode 100644 index 0000000..f7d2a56 --- /dev/null +++ b/beacon.h @@ -0,0 +1,6 @@ + +/* beacon.h */ + +void beacon_init (struct audio_s *pmodem, struct misc_config_s *pconfig, struct igate_config_s *pigate); + +void beacon_tracker_set_debug (int level); diff --git a/cdigipeater.h b/cdigipeater.h new file mode 100644 index 0000000..69a4b8c --- /dev/null +++ b/cdigipeater.h @@ -0,0 +1,62 @@ + + +#ifndef CDIGIPEATER_H +#define CDIGIPEATER_H 1 + +#include "regex.h" + +#include "direwolf.h" /* for MAX_CHANS */ +#include "ax25_pad.h" /* for packet_t */ +#include "audio.h" /* for radio channel properties */ + + +/* + * Information required for Connected mode digipeating. + * + * The configuration file reader fills in this information + * and it is passed to cdigipeater_init at application start up time. + */ + + +struct cdigi_config_s { + +/* + * Rules for each of the [from_chan][to_chan] combinations. + */ + int enabled[MAX_CHANS][MAX_CHANS]; // Is it enabled for from/to pair? + + int has_alias[MAX_CHANS][MAX_CHANS]; // If there was no alias in the config file, + // the structure below will not be set up + // properly and an attempt to use it could + // result in a crash. (fixed v1.5) + // Not needed for [APRS] DIGIPEAT because + // the alias is mandatory there. + regex_t alias[MAX_CHANS][MAX_CHANS]; + + char *cfilter_str[MAX_CHANS][MAX_CHANS]; + // NULL or optional Packet Filter strings such as "t/m". +}; + +/* + * Call once at application start up time. + */ + +extern void cdigipeater_init (struct audio_s *p_audio_config, struct cdigi_config_s *p_cdigi_config); + +/* + * Call this for each packet received. + * Suitable packets will be queued for transmission. + */ + +extern void cdigipeater (int from_chan, packet_t pp); + + +/* Make statistics available. */ + +int cdigipeater_get_count (int from_chan, int to_chan); + + +#endif + +/* end cdigipeater.h */ + diff --git a/cm108.h b/cm108.h new file mode 100644 index 0000000..2def77a --- /dev/null +++ b/cm108.h @@ -0,0 +1,5 @@ +/* Dire Wolf cm108.h */ + +extern void cm108_find_ptt (char *output_audio_device, char *ptt_device, int ptt_device_size); + +extern int cm108_set_gpio_pin (char *name, int num, int state); \ No newline at end of file diff --git a/config.h b/config.h new file mode 100644 index 0000000..360ac49 --- /dev/null +++ b/config.h @@ -0,0 +1,262 @@ + +/*---------------------------------------------------------------------------- + * + * Name: config.h + * + * Purpose: + * + * Description: + * + *-----------------------------------------------------------------------------*/ + + +#ifndef CONFIG_H +#define CONFIG_H 1 + +#include "audio.h" /* for struct audio_s */ +#include "digipeater.h" /* for struct digi_config_s */ +#include "cdigipeater.h" /* for struct cdigi_config_s */ +#include "aprs_tt.h" /* for struct tt_config_s */ +#include "igate.h" /* for struct igate_config_s */ + +/* + * All the leftovers. + * This wasn't thought out. It just happened. + */ + +enum beacon_type_e { BEACON_IGNORE, BEACON_POSITION, BEACON_OBJECT, BEACON_TRACKER, BEACON_CUSTOM, BEACON_IGATE }; + +enum sendto_type_e { SENDTO_XMIT, SENDTO_IGATE, SENDTO_RECV }; + + +#define MAX_BEACONS 30 +#define MAX_KISS_TCP_PORTS (MAX_CHANS+1) + +struct misc_config_s { + + int agwpe_port; /* TCP Port number for the "AGW TCPIP Socket Interface" */ + + // Previously we allowed only a single TCP port for KISS. + // An increasing number of people want to run multiple radios. + // Unfortunately, most applications don't know how to deal with multi-radio TNCs. + // They ignore the channel on receive and always transmit to channel 0. + // Running multiple instances of direwolf is a work-around but this leads to + // more complex configuration and we lose the cross-channel digipeating capability. + // In release 1.7 we add a new feature to assign a single radio channel to a TCP port. + // e.g. + // KISSPORT 8001 # default, all channels. Radio channel = KISS channel. + // + // KISSPORT 7000 0 # Only radio channel 0 for receive. + // # Transmit to radio channel 0, ignoring KISS channel. + // + // KISSPORT 7001 1 # Only radio channel 1 for receive. KISS channel set to 0. + // # Transmit to radio channel 1, ignoring KISS channel. + + int kiss_port[MAX_KISS_TCP_PORTS]; /* TCP Port number for the "TCP KISS" protocol. */ + int kiss_chan[MAX_KISS_TCP_PORTS]; /* Radio Channel number for this port or -1 for all. */ + + int kiss_copy; /* Data from network KISS client is copied to all others. */ + int enable_kiss_pt; /* Enable pseudo terminal for KISS. */ + /* Want this to be off by default because it hangs */ + /* after a while if nothing is reading from other end. */ + + char kiss_serial_port[20]; + /* Serial port name for our end of the */ + /* virtual null modem for native Windows apps. */ + /* Version 1.5 add same capability for Linux. */ + + int kiss_serial_speed; /* Speed, in bps, for the KISS serial port. */ + /* If 0, just leave what was already there. */ + + int kiss_serial_poll; /* When using Bluetooth KISS, the /dev/rfcomm0 device */ + /* will appear and disappear as the remote application */ + /* opens and closes the virtual COM port. */ + /* When this is non-zero, we will check periodically to */ + /* see if the device has appeared and we will open it. */ + + char gpsnmea_port[20]; /* Serial port name for reading NMEA sentences from GPS. */ + /* e.g. COM22, /dev/ttyACM0 */ + + int gpsnmea_speed; /* Speed for above, baud, default 4800. */ + + char gpsd_host[20]; /* Host for gpsd server. */ + /* e.g. localhost, 192.168.1.2 */ + + int gpsd_port; /* Port number for gpsd server. */ + /* Default is 2947. */ + + + char waypoint_serial_port[20]; /* Serial port name for sending NMEA waypoint sentences */ + /* to a GPS map display or other mapping application. */ + /* e.g. COM22, /dev/ttyACM0 */ + /* Currently no option for setting non-standard speed. */ + /* This was done in 2014 and no one has complained yet. */ + + char waypoint_udp_hostname[80]; /* Destination host when using UDP. */ + + int waypoint_udp_portnum; /* UDP port. */ + + int waypoint_formats; /* Which sentence formats should be generated? */ + +#define WPL_FORMAT_NMEA_GENERIC 0x01 /* N $GPWPL */ +#define WPL_FORMAT_GARMIN 0x02 /* G $PGRMW */ +#define WPL_FORMAT_MAGELLAN 0x04 /* M $PMGNWPL */ +#define WPL_FORMAT_KENWOOD 0x08 /* K $PKWDWPL */ +#define WPL_FORMAT_AIS 0x10 /* A !AIVDM */ + + + int log_daily_names; /* True to generate new log file each day. */ + + char log_path[80]; /* Either directory or full file name depending on above. */ + + int dns_sd_enabled; /* DNS Service Discovery announcement enabled. */ + char dns_sd_name[64]; /* Name announced on dns-sd; defaults to "Dire Wolf on " */ + + int sb_configured; /* TRUE if SmartBeaconing is configured. */ + int sb_fast_speed; /* MPH */ + int sb_fast_rate; /* seconds */ + int sb_slow_speed; /* MPH */ + int sb_slow_rate; /* seconds */ + int sb_turn_time; /* seconds */ + int sb_turn_angle; /* degrees */ + int sb_turn_slope; /* degrees * MPH */ + +// AX.25 connected mode. + + int frack; /* Number of seconds to wait for ack to transmission. */ + + int retry; /* Number of times to retry before giving up. */ + + int paclen; /* Max number of bytes in information part of frame. */ + + int maxframe_basic; /* Max frames to send before ACK. mod 8 "Window" size. */ + + int maxframe_extended; /* Max frames to send before ACK. mod 128 "Window" size. */ + + int maxv22; /* Maximum number of unanswered SABME frames sent before */ + /* switching to SABM. This is to handle the case of an old */ + /* TNC which simply ignores SABME rather than replying with FRMR. */ + + char **v20_addrs; /* Stations known to understand only AX.25 v2.0 so we don't */ + /* waste time trying v2.2 first. */ + + int v20_count; /* Number of station addresses in array above. */ + + char **noxid_addrs; /* Stations known not to understand XID command so don't */ + /* waste time sending it and eventually giving up. */ + /* AX.25 for Linux is the one known case, so far, where */ + /* SABME is implemented but XID is not. */ + + int noxid_count; /* Number of station addresses in array above. */ + + +// Beacons. + + int num_beacons; /* Number of beacons defined. */ + + struct beacon_s { + + enum beacon_type_e btype; /* Position or object. */ + + int lineno; /* Line number from config file for later error messages. */ + + enum sendto_type_e sendto_type; + + /* SENDTO_XMIT - Usually beacons go to a radio transmitter. */ + /* chan, below is the channel number. */ + /* SENDTO_IGATE - Send to IGate, probably to announce my position */ + /* rather than relying on someone else to hear */ + /* me on the radio and report me. */ + /* SENDTO_RECV - Pretend this was heard on the specified */ + /* radio channel. Mostly for testing. It is a */ + /* convenient way to send packets to attached apps. */ + + int sendto_chan; /* Transmit or simulated receive channel for above. Should be 0 for IGate. */ + + int delay; /* Seconds to delay before first transmission. */ + + int slot; /* Seconds after hour for slotted time beacons. */ + /* If specified, it overrides any 'delay' value. */ + + int every; /* Time between transmissions, seconds. */ + /* Remains fixed for PBEACON and OBEACON. */ + /* Dynamically adjusted for TBEACON. */ + + time_t next; /* Unix time to transmit next one. */ + + char *source; /* NULL or explicit AX.25 source address to use */ + /* instead of the mycall value for the channel. */ + + char *dest; /* NULL or explicit AX.25 destination to use */ + /* instead of the software version such as APDW11. */ + + int compress; /* Use more compact form? */ + + char objname[10]; /* Object name. Any printable characters. */ + + char *via; /* Path, e.g. "WIDE1-1,WIDE2-1" or NULL. */ + + char *custom_info; /* Info part for handcrafted custom beacon. */ + /* Ignore the rest below if this is set. */ + + char *custom_infocmd; /* Command to generate info part. */ + /* Again, other options below are then ignored. */ + + int messaging; /* Set messaging attribute for position report. */ + /* i.e. Data Type Indicator of '=' rather than '!' */ + + double lat; /* Latitude and longitude. */ + double lon; + int ambiguity; /* Number of lower digits to trim from location. 0 (default), 1, 2, 3, 4. */ + float alt_m; /* Altitude in meters. */ + + char symtab; /* Symbol table: / or \ or overlay character. */ + char symbol; /* Symbol code. */ + + float power; /* For PHG. */ + float height; /* HAAT in feet */ + float gain; /* Original protocol spec was unclear. */ + /* Addendum 1.1 clarifies it is dBi not dBd. */ + + char dir[3]; /* 1 or 2 of N,E,W,S, or empty for omni. */ + + float freq; /* MHz. */ + float tone; /* Hz. */ + float offset; /* MHz. */ + + char *comment; /* Comment or NULL. */ + char *commentcmd; /* Command to append more to Comment or NULL. */ + + + } beacon[MAX_BEACONS]; + +}; + + +#define MIN_IP_PORT_NUMBER 1024 +#define MAX_IP_PORT_NUMBER 49151 + + +#define DEFAULT_AGWPE_PORT 8000 /* Like everyone else. */ +#define DEFAULT_KISS_PORT 8001 /* Above plus 1. */ + + +#define DEFAULT_NULLMODEM "COM3" /* should be equiv. to /dev/ttyS2 on Cygwin */ + + + + +extern void config_init (char *fname, struct audio_s *p_modem, + struct digi_config_s *digi_config, + struct cdigi_config_s *cdigi_config, + struct tt_config_s *p_tt_config, + struct igate_config_s *p_igate_config, + struct misc_config_s *misc_config); + + + +#endif /* CONFIG_H */ + +/* end config.h */ + + diff --git a/debug/QtSoundModem.ini b/debug/QtSoundModem.ini new file mode 100644 index 0000000..9747798 --- /dev/null +++ b/debug/QtSoundModem.ini @@ -0,0 +1,197 @@ +[General] +geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\a\0\0\0\0\0\0\x3\xca\0\0\x2\xf0\0\0\0\b\0\0\0\x1f\0\0\x3\xc9\0\0\x2\xef\0\0\0\0\0\0\0\0\x5\0\0\0\0\b\0\0\0\x1f\0\0\x3\xc9\0\0\x2\xef) +windowState=@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\0\0\0\x3\xc2\0\0\x2\xbc\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\0) +PSKWindow=@Rect(46 499 366 140) + +[AX25_A] +Retries=15 +HiToneRaise=0 +Maxframe=3 +FrackTime=5 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=1500 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=0 +DynamicFrack=0 +BitRecovery=0 +NonAX25Frm=0 +MEMRecovery=200 +IPOLL=80 +MyDigiCall= +FX25=1 +IL2P=2 +RSID_UI=0 +RSID_SABM=0 +RSID_SetModem=0 + +[Init] +SoundMode=0 +UDPClientPort=8888 +UDPServerPort=8884 +TXPort=8884 +UDPServer=0 +UDPHost=192.168.1.255 +TXSampleRate=12000 +RXSampleRate=12000 +SndRXDeviceName="CABLE-B OUTPUT (VB-AUDIO CABLE " +SndTXDeviceName=CABLE-B INPUT (VB-AUDIO CABLE B +SCO=0 +DualPTT=1 +TXRotate=0 +DispMode=1 +PTT= +PTTBAUD=19200 +PTTMode=1 +PTTOffString= +PTTOnString= +pttGPIOPin=17 +pttGPIOPinR=17 +CM108Addr=0xD8C:0x08 +HamLibPort=4532 +HamLibHost=127.0.0.1 +MinimizetoTray=1 +multiCore=1 +Wisdom=(fftw-3.3.5 fftwf_wisdom #x08ac4c16 #x457005cc #xea102cf7 #xd7ff9038\n (fftwf_dft_vrank_geq1_register 0 #x10048 #x10048 #x0 #x2fce15e1 #x178d4f4d #x1e956a41 #xf3fd6b80)\n (fftwf_dft_buffered_register 0 #x11048 #x11048 #x0 #x4f8e87b4 #xec4f2fa0 #x79fe76a1 #xa16e32a5)\n (fftwf_codelet_t1fv_6_sse2 0 #x10048 #x10048 #x0 #xd9db29d8 #x3302fcf3 #x19ce6e5d #x869fc341)\n (fftwf_codelet_t1fv_12_sse2 0 #x10048 #x10048 #x0 #x0b0d3933 #x08267d12 #x45613873 #xde496efe)\n (fftwf_codelet_n2fv_12_sse2 0 #x10048 #x10048 #x0 #xb0767e46 #x8d41dd22 #x439264a0 #x18435a99)\n (fftwf_dft_bluestein_register 0 #x11048 #x11048 #x0 #x5e17c068 #x1682c5d6 #x89dd79be #x9b951c0f)\n (fftwf_codelet_t1fuv_8_sse2 0 #x11048 #x11048 #x0 #x34057a74 #x664db78f #xa9524ebc #x606afd88)\n (fftwf_dft_r2hc_register 0 #x11048 #x11048 #x0 #x576d5db6 #xa6a15f8a #x875d87d5 #x7561a866)\n (fftwf_dft_vrank_geq1_register 0 #x11048 #x11048 #x0 #x1e354940 #xac45f390 #x8260fb76 #x1a44862e)\n (fftwf_rdft_rank0_register 0 #x11048 #x11048 #x0 #xff75c762 #x3a0ee093 #x5b78d592 #x6b6be60e)\n (fftwf_dft_nop_register 0 #x11048 #x11048 #x0 #x4a593e24 #xb5f06ddf #xf11fe7f2 #xc010b545)\n)\n +WaterfallMin=0 +WaterfallMax=3300 + +[AX25_B] +Retries=15 +HiToneRaise=0 +Maxframe=3 +FrackTime=5 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=1500 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=0 +DynamicFrack=0 +BitRecovery=0 +NonAX25Frm=0 +MEMRecovery=200 +IPOLL=80 +MyDigiCall= +FX25=1 +IL2P=2 +RSID_UI=0 +RSID_SABM=0 +RSID_SetModem=0 + +[Modem] +NRRcvrPairs1=2 +NRRcvrPairs2=2 +NRRcvrPairs3=0 +NRRcvrPairs4=2 +RcvrShift1=50 +RcvrShift2=30 +RcvrShift3=30 +RcvrShift4=30 +ModemType1=4 +ModemType2=14 +ModemType3=0 +ModemType4=14 +soundChannel1=1 +soundChannel2=1 +soundChannel3=0 +soundChannel4=0 +DCDThreshold=40 +rxOffset=-100 +PreEmphasisAll1=0 +PreEmphasisAll2=0 +PreEmphasisAll3=0 +PreEmphasisAll4=0 +PreEmphasisDB1=0 +PreEmphasisDB2=0 +PreEmphasisDB3=0 +PreEmphasisDB4=0 +TxDelay1=250 +TxDelay2=250 +TxDelay3=250 +TxDelay4=250 +TxTail1=50 +TxTail2=50 +TxTail3=50 +TxTail4=50 +CWIDCall= +CWIDInterval=0 +CWIDLeft=0 +CWIDRight=0 +CWIDType=1 +RXFreq1=1700 +RXFreq2=1700 +RXFreq3=500 +RXFreq4=1700 +CWIDMark= + +[AGWHost] +Server=1 +Port=8000 + +[KISS] +Server=0 +Port=8105 + +[AX25_C] +Retries=15 +HiToneRaise=0 +Maxframe=3 +FrackTime=5 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=1500 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=0 +DynamicFrack=0 +BitRecovery=0 +NonAX25Frm=0 +MEMRecovery=200 +IPOLL=80 +MyDigiCall= +FX25=1 +IL2P=0 +RSID_UI=0 +RSID_SABM=0 +RSID_SetModem=0 + +[Window] +Waterfall1=1 +Waterfall2=1 + +[AX25_D] +Retries=15 +HiToneRaise=0 +Maxframe=3 +FrackTime=5 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=1500 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=0 +DynamicFrack=0 +BitRecovery=0 +NonAX25Frm=0 +MEMRecovery=200 +IPOLL=80 +MyDigiCall= +FX25=1 +IL2P=0 +RSID_UI=0 +RSID_SABM=0 +RSID_SetModem=0 diff --git a/debug/moc_predefs-DESKTOP-MHE5LO8.h b/debug/moc_predefs-DESKTOP-MHE5LO8.h new file mode 100644 index 0000000..2c1557b --- /dev/null +++ b/debug/moc_predefs-DESKTOP-MHE5LO8.h @@ -0,0 +1,12 @@ +#define _MSC_EXTENSIONS +#define _INTEGRAL_MAX_BITS 64 +#define _MSC_VER 1916 +#define _MSC_FULL_VER 191627050 +#define _MSC_BUILD 0 +#define _WIN32 +#define _M_IX86 600 +#define _M_IX86_FP 2 +#define _CPPRTTI +#define _DEBUG +#define _MT +#define _DLL diff --git a/debug/moc_predefs.h b/debug/moc_predefs.h new file mode 100644 index 0000000..8247e15 --- /dev/null +++ b/debug/moc_predefs.h @@ -0,0 +1,12 @@ +#define _MSC_EXTENSIONS +#define _INTEGRAL_MAX_BITS 64 +#define _MSC_VER 1916 +#define _MSC_FULL_VER 191627043 +#define _MSC_BUILD 0 +#define _WIN32 +#define _M_IX86 600 +#define _M_IX86_FP 2 +#define _CPPRTTI +#define _DEBUG +#define _MT +#define _DLL diff --git a/decode_aprs.h b/decode_aprs.h new file mode 100644 index 0000000..f25d1e9 --- /dev/null +++ b/decode_aprs.h @@ -0,0 +1,150 @@ + +/* decode_aprs.h */ + + +#ifndef DECODE_APRS_H + +#define DECODE_APRS_H 1 + + + +#ifndef G_UNKNOWN +#include "latlong.h" +#endif + +#ifndef AX25_MAX_ADDR_LEN +#include "ax25_pad.h" +#endif + +#ifndef APRSTT_LOC_DESC_LEN +#include "aprs_tt.h" +#endif + +typedef struct decode_aprs_s { + + int g_quiet; /* Suppress error messages when decoding. */ + + char g_src[AX25_MAX_ADDR_LEN]; + + char g_dest[AX25_MAX_ADDR_LEN]; + + char g_data_type_desc[100]; /* APRS data type description. Telemetry descriptions get pretty long. */ + + char g_symbol_table; /* The Symbol Table Identifier character selects one */ + /* of the two Symbol Tables, or it may be used as */ + /* single-character (alpha or numeric) overlay, as follows: */ + + /* / Primary Symbol Table (mostly stations) */ + + /* \ Alternate Symbol Table (mostly Objects) */ + + /* 0-9 Numeric overlay. Symbol from Alternate Symbol */ + /* Table (uncompressed lat/long data format) */ + + /* a-j Numeric overlay. Symbol from Alternate */ + /* Symbol Table (compressed lat/long data */ + /* format only). i.e. a-j maps to 0-9 */ + + /* A-Z Alpha overlay. Symbol from Alternate Symbol Table */ + + + char g_symbol_code; /* Where the Symbol Table Identifier is 0-9 or A-Z (or a-j */ + /* with compressed position data only), the symbol comes from */ + /* the Alternate Symbol Table, and is overlaid with the */ + /* identifier (as a single digit or a capital letter). */ + + char g_aprstt_loc[APRSTT_LOC_DESC_LEN]; /* APRStt location from !DAO! */ + + double g_lat, g_lon; /* Location, degrees. Negative for South or West. */ + /* Set to G_UNKNOWN if missing or error. */ + + char g_maidenhead[12]; /* 4 or 6 (or 8?) character maidenhead locator. */ + + char g_name[12]; /* Object or item name. Max. 9 characters. */ + + char g_addressee[12]; /* Addressee for a "message." Max. 9 characters. */ + /* Also for Directed Station Query which is a */ + /* special case of message. */ + + enum message_subtype_e { message_subtype_invalid = 0, + message_subtype_message, + message_subtype_ack, + message_subtype_rej, + message_subtype_telem_parm, + message_subtype_telem_unit, + message_subtype_telem_eqns, + message_subtype_telem_bits, + message_subtype_directed_query + } g_message_subtype; /* Various cases of the overloaded "message." */ + + char g_message_number[12]; /* Message number. Should be 1 - 5 alphanumeric characters if used. */ + /* Addendum 1.1 has new format {mm} or {mm}aa with only two */ + /* characters for message number and an ack riding piggyback. */ + + float g_speed_mph; /* Speed in MPH. */ + /* The APRS transmission uses knots so watch out for */ + /* conversions when sending and receiving APRS packets. */ + + float g_course; /* 0 = North, 90 = East, etc. */ + + int g_power; /* Transmitter power in watts. */ + + int g_height; /* Antenna height above average terrain, feet. */ + + int g_gain; /* Antenna gain in dB. */ + + char g_directivity[12]; /* Direction of max signal strength */ + + float g_range; /* Precomputed radio range in miles. */ + + float g_altitude_ft; /* Feet above median sea level. */ + /* I used feet here because the APRS specification */ + /* has units of feet for alititude. Meters would be */ + /* more natural to the other 96% of the world. */ + + char g_mfr[80]; /* Manufacturer or application. */ + + char g_mic_e_status[32]; /* MIC-E message. */ + + double g_freq; /* Frequency, MHz */ + + float g_tone; /* CTCSS tone, Hz, one fractional digit */ + + int g_dcs; /* Digital coded squelch, print as 3 octal digits. */ + + int g_offset; /* Transmit offset, kHz */ + + + char g_query_type[12]; /* General Query: APRS, IGATE, WX, ... */ + /* Addressee is NOT set. */ + + /* Directed Station Query: exactly 5 characters. */ + /* APRSD, APRST, PING?, ... */ + /* Addressee is set. */ + + double g_footprint_lat; /* A general query may contain a foot print. */ + double g_footprint_lon; /* Set all to G_UNKNOWN if not used. */ + float g_footprint_radius; /* Radius in miles. */ + + char g_query_callsign[12]; /* Directed query may contain callsign. */ + /* e.g. tell me all objects from that callsign. */ + + + char g_weather[500]; /* Weather. Can get quite long. Rethink max size. */ + + char g_telemetry[256]; /* Telemetry data. Rethink max size. */ + + char g_comment[256]; /* Comment. */ + +} decode_aprs_t; + + + + + +extern void decode_aprs (decode_aprs_t *A, packet_t pp, int quiet, char *third_party_src); + +extern void decode_aprs_print (decode_aprs_t *A); + + +#endif diff --git a/dedupe.h b/dedupe.h new file mode 100644 index 0000000..9c0613c --- /dev/null +++ b/dedupe.h @@ -0,0 +1,10 @@ + + +void dedupe_init (int ttl); + +void dedupe_remember (packet_t pp, int chan); + +int dedupe_check (packet_t pp, int chan); + + +/* end dedupe.h */ diff --git a/demod.h b/demod.h new file mode 100644 index 0000000..3233b9b --- /dev/null +++ b/demod.h @@ -0,0 +1,17 @@ + + +/* demod.h */ + +#include "audio.h" /* for struct audio_s */ +#include "ax25_pad.h" /* for alevel_t */ + + +int demod_init (struct audio_s *pa); + +int demod_get_sample (int a); + +void demod_process_sample (int chan, int subchan, int sam); + +void demod_print_agc (int chan, int subchan); + +alevel_t demod_get_audio_level (int chan, int subchan); \ No newline at end of file diff --git a/demod_afsk.h b/demod_afsk.h new file mode 100644 index 0000000..e44a44e --- /dev/null +++ b/demod_afsk.h @@ -0,0 +1,8 @@ + +/* demod_afsk.h */ + + +void demod_afsk_init (int samples_per_sec, int baud, int mark_freq, + int space_freq, char profile, struct demodulator_state_s *D); + +void demod_afsk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D); diff --git a/demod_psk.h b/demod_psk.h new file mode 100644 index 0000000..134b199 --- /dev/null +++ b/demod_psk.h @@ -0,0 +1,7 @@ + +/* demod_psk.h */ + + +void demod_psk_init (enum modem_t modem_type, enum v26_e v26_alt, int samples_per_sec, int bps, char profile, struct demodulator_state_s *D); + +void demod_psk_process_sample (int chan, int subchan, int sam, struct demodulator_state_s *D); diff --git a/digipeater.h b/digipeater.h new file mode 100644 index 0000000..5c84976 --- /dev/null +++ b/digipeater.h @@ -0,0 +1,78 @@ + +#ifndef DIGIPEATER_H +#define DIGIPEATER_H 1 + +#include "regex.h" + +#include "direwolf.h" /* for MAX_CHANS */ +#include "ax25_pad.h" /* for packet_t */ +#include "audio.h" /* for radio channel properties */ + + +/* + * Information required for digipeating. + * + * The configuration file reader fills in this information + * and it is passed to digipeater_init at application start up time. + */ + + +struct digi_config_s { + + + int dedupe_time; /* Don't digipeat duplicate packets */ + /* within this number of seconds. */ + +#define DEFAULT_DEDUPE 30 + +/* + * Rules for each of the [from_chan][to_chan] combinations. + */ + + regex_t alias[MAX_CHANS][MAX_CHANS]; + + regex_t wide[MAX_CHANS][MAX_CHANS]; + + int enabled[MAX_CHANS][MAX_CHANS]; + + enum preempt_e { PREEMPT_OFF, PREEMPT_DROP, PREEMPT_MARK, PREEMPT_TRACE } preempt[MAX_CHANS][MAX_CHANS]; + + // ATGP is an ugly hack for the specific need of ATGP which needs more that 8 digipeaters. + // DO NOT put this in the User Guide. On a need to know basis. + + char atgp[MAX_CHANS][MAX_CHANS][AX25_MAX_ADDR_LEN]; + + char *filter_str[MAX_CHANS+1][MAX_CHANS+1]; + // NULL or optional Packet Filter strings such as "t/m". + // Notice the size of arrays is one larger than normal. + // That extra position is for the IGate. + + int regen[MAX_CHANS][MAX_CHANS]; // Regenerate packet. + // Sort of like digipeating but passed along unchanged. +}; + +/* + * Call once at application start up time. + */ + +extern void digipeater_init (struct audio_s *p_audio_config, struct digi_config_s *p_digi_config); + +/* + * Call this for each packet received. + * Suitable packets will be queued for transmission. + */ + +extern void digipeater (int from_chan, packet_t pp); + +void digi_regen (int from_chan, packet_t pp); + + +/* Make statistics available. */ + +int digipeater_get_count (int from_chan, int to_chan); + + +#endif + +/* end digipeater.h */ + diff --git a/dlq.h b/dlq.h new file mode 100644 index 0000000..8771636 --- /dev/null +++ b/dlq.h @@ -0,0 +1,148 @@ + +/*------------------------------------------------------------------ + * + * Module: dlq.h + * + *---------------------------------------------------------------*/ + +#ifndef DLQ_H +#define DLQ_H 1 + +#include "ax25_pad.h" +#include "audio.h" + + +/* A transmit or receive data block for connected mode. */ + +typedef struct cdata_s { + int magic; /* For integrity checking. */ + +#define TXDATA_MAGIC 0x09110911 + + struct cdata_s *next; /* Pointer to next when part of a list. */ + + int pid; /* Protocol id. */ + + int size; /* Number of bytes allocated. */ + + int len; /* Number of bytes actually used. */ + + char data[]; /* Variable length data. */ + +} cdata_t; + + + +/* Types of things that can be in queue. */ + +typedef enum dlq_type_e {DLQ_REC_FRAME, DLQ_CONNECT_REQUEST, DLQ_DISCONNECT_REQUEST, DLQ_XMIT_DATA_REQUEST, DLQ_REGISTER_CALLSIGN, DLQ_UNREGISTER_CALLSIGN, DLQ_OUTSTANDING_FRAMES_REQUEST, DLQ_CHANNEL_BUSY, DLQ_SEIZE_CONFIRM, DLQ_CLIENT_CLEANUP} dlq_type_t; + + +/* A queue item. */ + +// TODO: call this event rather than item. +// TODO: should add fences. + +typedef struct dlq_item_s { + + struct dlq_item_s *nextp; /* Next item in queue. */ + + dlq_type_t type; /* Type of item. */ + /* See enum definition above. */ + + int chan; /* Radio channel of origin. */ + +// I'm not worried about amount of memory used but this might be a +// little clearer if a union was used for the different event types. + +// Used for received frame. + + int subchan; /* Winning "subchannel" when using multiple */ + /* decoders on one channel. */ + /* Special case, -1 means DTMF decoder. */ + /* Maybe we should have a different type in this case? */ + + int slice; /* Winning slicer. */ + + packet_t pp; /* Pointer to frame structure. */ + + alevel_t alevel; /* Audio level. */ + + int is_fx25; /* Was it from FX.25? */ + + retry_t retries; /* Effort expended to get a valid CRC. */ + /* Bits changed for regular AX.25. */ + /* Number of bytes fixed for FX.25. */ + + char spectrum[MAX_SUBCHANS*MAX_SLICERS+1]; /* "Spectrum" display for multi-decoders. */ + +// Used by requests from a client application, connect, etc. + + char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; + + int num_addr; /* Range 2 .. 10. */ + + int client; + + +// Used only by client request to transmit connected data. + + cdata_t *txdata; + +// Used for channel activity change. +// It is useful to know when the channel is busy either for carrier detect +// or when we are transmitting. + + int activity; /* OCTYPE_PTT for my transmission start/end. */ + /* OCTYPE_DCD if we hear someone else. */ + + int status; /* 1 for active or 0 for quiet. */ + +} dlq_item_t; + + + +void dlq_init (void); + + + +void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, int is_fx25, retry_t retries, char *spectrum); + +void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid); + +void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client); + +void dlq_outstanding_frames_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client); + +void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len); + +void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client); + +void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client); + +void dlq_channel_busy (int chan, int activity, int status); + +void dlq_seize_confirm (int chan); + +void dlq_client_cleanup (int client); + + + +int dlq_wait_while_empty (double timeout_val); + +struct dlq_item_s *dlq_remove (void); + +void dlq_delete (struct dlq_item_s *pitem); + + + +cdata_t *cdata_new (int pid, char *data, int len); + +void cdata_delete (cdata_t *txdata); + +void cdata_check_leak (void); + + +#endif + +/* end dlq.h */ diff --git a/dns_sd_common.h b/dns_sd_common.h new file mode 100644 index 0000000..f104bf8 --- /dev/null +++ b/dns_sd_common.h @@ -0,0 +1,7 @@ + +#if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD) + +char *dns_sd_default_service_name(void); + +#endif + diff --git a/dns_sd_dw.h b/dns_sd_dw.h new file mode 100644 index 0000000..79f4b86 --- /dev/null +++ b/dns_sd_dw.h @@ -0,0 +1,10 @@ + +#if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD) + +#include "config.h" + +#define DNS_SD_SERVICE "_kiss-tnc._tcp" + +void dns_sd_announce (struct misc_config_s *mc); + +#endif // USE_AVAHI_CLIENT diff --git a/dtime_now.h b/dtime_now.h new file mode 100644 index 0000000..411534b --- /dev/null +++ b/dtime_now.h @@ -0,0 +1,18 @@ + + +extern double dtime_realtime (void); + +extern double dtime_monotonic (void); + + +void timestamp_now (char *result, int result_size, int show_ms); + +void timestamp_user_format (char *result, int result_size, char *user_format); + +void timestamp_filename (char *result, int result_size); + + +// FIXME: remove temp workaround. +// Needs many scattered updates. + +#define dtime_now dtime_realtime diff --git a/dtmf.h b/dtmf.h new file mode 100644 index 0000000..c1b52b9 --- /dev/null +++ b/dtmf.h @@ -0,0 +1,14 @@ +/* dtmf.h */ + + +#include "audio.h" + +void dtmf_init (struct audio_s *p_audio_config, int amp); + +char dtmf_sample (int c, float input); + +int dtmf_send (int chan, char *str, int speed, int txdelay, int txtail); + + +/* end dtmf.h */ + diff --git a/dw9600.c b/dw9600.c new file mode 100644 index 0000000..8307342 --- /dev/null +++ b/dw9600.c @@ -0,0 +1,4184 @@ +п»ї// 4800/9600 RHU Mode code for QtSoundModem + +// Based on code from Dire Wolf Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ + + +#define MODE_RUH 7 + +typedef struct string_T +{ + unsigned char * Data; + int Length; + int AllocatedLength; // A reasonable sized block is allocated at the start to speed up adding chars + +}string; + +typedef struct TStringList_T +{ + int Count; + string ** Items; + +} TStringList; + +#include +#include "dw9600.h" + +extern int fx25_mode[4]; +extern int il2p_mode[4]; +extern short rx_baudrate[5]; + +#define FX25_MODE_NONE 0 +#define FX25_MODE_RX 1 +#define FX25_MODE_TXRX 2 +#define FX25_TAG 0 +#define FX25_LOAD 1 + +#define IL2P_MODE_NONE 0 +#define IL2P_MODE_RX 1 // RX il2p + HDLC +#define IL2P_MODE_TXRX 2 +#define IL2P_MODE_ONLY 3 // RX only il2p, TX il2p + + +extern unsigned short * DMABuffer; +extern int SampleNo; + + +void ProcessRXFrames(int snd_ch); + +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see . +// + + +#include +#include +#include +#include +#include // uint64_t +#define RRBB_C 1 + +/******************************************************************************** + * + * File: hdlc_rec.c + * + * Purpose: Extract HDLC frames from a stream of bits. + * + *******************************************************************************/ + + +//#define TEST 1 /* Define for unit testing. */ + +//#define DEBUG3 1 /* monitor the data detect signal. */ + + + +/* + * Minimum & maximum sizes of an AX.25 frame including the 2 octet FCS. + */ + +#define MIN_FRAME_LEN ((AX25_MIN_PACKET_LEN) + 2) + +#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2) + + /* + * This is the current state of the HDLC decoder. + * + * It is possible to run multiple decoders concurrently by + * having a separate set of state variables for each. + * + * Should have a reset function instead of initializations here. + */ + +struct hdlc_state_s { + + + int prev_raw; /* Keep track of previous bit so */ + /* we can look for transitions. */ + /* Should be only 0 or 1. */ + + int lfsr; /* Descrambler shift register for 9600 baud. */ + + int prev_descram; /* Previous descrambled for 9600 baud. */ + + unsigned char pat_det; /* 8 bit pattern detector shift register. */ + /* See below for more details. */ + + unsigned int flag4_det; /* Last 32 raw bits to look for 4 */ + /* flag patterns in a row. */ + + unsigned char oacc; /* Accumulator for building up an octet. */ + + int olen; /* Number of bits in oacc. */ + /* When this reaches 8, oacc is copied */ + /* to the frame buffer and olen is zeroed. */ + /* The value of -1 is a special case meaning */ + /* bits should not be accumulated. */ + + unsigned char frame_buf[MAX_FRAME_LEN]; + /* One frame is kept here. */ + + int frame_len; /* Number of octets in frame_buf. */ + /* Should be in range of 0 .. MAX_FRAME_LEN. */ + + rrbb_t rrbb; /* Handle for bit array for raw received bits. */ + + uint64_t eas_acc; /* Accumulate most recent 64 bits received for EAS. */ + + int eas_gathering; /* Decoding in progress. */ + + int eas_plus_found; /* "+" seen, indicating end of geographical area list. */ + + int eas_fields_after_plus; /* Number of "-" characters after the "+". */ +}; + +static struct hdlc_state_s hdlc_state[MAX_CHANS]; + +static int num_subchan[MAX_CHANS]; //TODO1.2 use ptr rather than copy. + +static int composite_dcd[MAX_CHANS][MAX_SUBCHANS + 1]; + + +/*********************************************************************************** + * + * Name: hdlc_rec_init + * + * Purpose: Call once at the beginning to initialize. + * + * Inputs: None. + * + ***********************************************************************************/ + +int was_init[4] = { 0, 0, 0, 0 }; + +struct audio_s *g_audio_p; +extern struct audio_s pa[4]; + + +void hdlc_rec_init(struct audio_s *pa) +{ + int ch; + struct hdlc_state_s *H; + + //text_color_set(DW_COLOR_DEBUG); + //dw_printf ("hdlc_rec_init (%p) \n", pa); + + assert(pa != NULL); + g_audio_p = &pa; + + memset(composite_dcd, 0, sizeof(composite_dcd)); + + for (ch = 0; ch < MAX_CHANS; ch++) + { + pa->achan[ch].num_subchan = 1; + num_subchan[ch] = pa->achan[ch].num_subchan; + + assert(num_subchan[ch] >= 1 && num_subchan[ch] <= MAX_SUBCHANS); + + H = &hdlc_state[ch]; + + H->olen = -1; + + H->rrbb = rrbb_new(ch, 0, 0, pa->achan[ch].modem_type == MODEM_SCRAMBLE, H->lfsr, H->prev_descram); + } + hdlc_rec2_init(pa); + +} + +/* Own copy of random number generator so we can get */ +/* same predictable results on different operating systems. */ +/* TODO: Consolidate multiple copies somewhere. */ + +#define MY_RAND_MAX 0x7fffffff +static int seed = 1; + +static int my_rand(void) { + // Perform the calculation as unsigned to avoid signed overflow error. + seed = (int)(((unsigned)seed * 1103515245) + 12345) & MY_RAND_MAX; + return (seed); +} + + +/*********************************************************************************** + * + * Name: eas_rec_bit + * + * Purpose: Extract EAS trasmissions from a stream of bits. + * + * Inputs: chan - Channel number. + * + * subchan - This allows multiple demodulators per channel. + * + * slice - Allows multiple slicers per demodulator (subchannel). + * + * raw - One bit from the demodulator. + * should be 0 or 1. + * + * future_use - Not implemented yet. PSK already provides it. + * + * + * Description: This is called once for each received bit. + * For each valid transmission, process_rec_frame() + * is called for further processing. + * + ***********************************************************************************/ + +#define PREAMBLE 0xababababababababULL +#define PREAMBLE_ZCZC 0x435a435aababababULL +#define PREAMBLE_NNNN 0x4e4e4e4eababababULL +#define EAS_MAX_LEN 268 // Not including preamble. Up to 31 geographic areas. + + +static void eas_rec_bit(int chan, int subchan, int slice, int raw, int future_use) +{ + struct hdlc_state_s *H; + + /* + * Different state information for each channel / subchannel / slice. + */ + H = &hdlc_state[chan]; + + //dw_printf ("slice %d = %d\n", slice, raw); + +// Accumulate most recent 64 bits. + + H->eas_acc >>= 1; + if (raw) { + H->eas_acc |= 0x8000000000000000ULL; + } + + int done = 0; + + if (H->eas_acc == PREAMBLE_ZCZC) { + //dw_printf ("ZCZC\n"); + H->olen = 0; + H->eas_gathering = 1; + H->eas_plus_found = 0; + H->eas_fields_after_plus = 0; + strcpy((char*)(H->frame_buf), "ZCZC"); + H->frame_len = 4; + } + else if (H->eas_acc == PREAMBLE_NNNN) { + //dw_printf ("NNNN\n"); + H->olen = 0; + H->eas_gathering = 1; + strcpy((char*)(H->frame_buf), "NNNN"); + H->frame_len = 4; + done = 1; + } + else if (H->eas_gathering) { + H->olen++; + if (H->olen == 8) { + H->olen = 0; + char ch = H->eas_acc >> 56; + H->frame_buf[H->frame_len++] = ch; + H->frame_buf[H->frame_len] = '\0'; + //dw_printf ("frame_buf = %s\n", H->frame_buf); + + // What characters are acceptable? + // Only ASCII is allowed. i.e. the MSB must be 0. + // The examples show only digits but the geographical area can + // contain anything in range of '!' to DEL or CR or LF. + // There are no restrictions listed for the originator and + // examples contain a slash. + // It's not clear if a space can occur in other places. + + if (!((ch >= ' ' && ch <= 0x7f) || ch == '\r' || ch == '\n')) { + //#define DEBUG_E 1 +#ifdef DEBUG_E + dw_printf("reject %d invalid character = %s\n", slice, H->frame_buf); +#endif + H->eas_gathering = 0; + return; + } + if (H->frame_len > EAS_MAX_LEN) { // FIXME: look for other places with max length +#ifdef DEBUG_E + dw_printf("reject %d too long = %s\n", slice, H->frame_buf); +#endif + H->eas_gathering = 0; + return; + } + if (ch == '+') { + H->eas_plus_found = 1; + H->eas_fields_after_plus = 0; + } + if (H->eas_plus_found && ch == '-') { + H->eas_fields_after_plus++; + if (H->eas_fields_after_plus == 3) { + done = 1; // normal case + } + } + } + } + + if (done) { +#ifdef DEBUG_E + dw_printf("frame_buf %d = %s\n", slice, H->frame_buf); +#endif + alevel_t alevel = demod_get_audio_level(chan, subchan); + multi_modem_process_rec_frame(chan, subchan, slice, H->frame_buf, H->frame_len, alevel, 0, 0); + H->eas_gathering = 0; + } + +} // end eas_rec_bit + + +/* + +EAS has no error detection. +Maybe that doesn't matter because we would normally be dealing with a reasonable +VHF FM or TV signal. +Let's see what happens when we intentionally introduce errors. +When some match and others don't, the multislice voting should give preference +to those matching others. + + $ src/atest -P+ -B EAS -e 3e-3 ../../ref-doc/EAS/same.wav + Demodulator profile set to "+" + 96000 samples per second. 16 bits per sample. 1 audio channels. + 2079360 audio bytes in file. Duration = 10.8 seconds. + Fix Bits level = 0 + Channel 0: 521 baud, AFSK 2083 & 1563 Hz, D+, 96000 sample rate / 3. + +case 1: Slice 6 is different than others (EQS vs. EAS) so we want one of the others that match. + Slice 3 has an unexpected character (in 0120u7) so it is a mismatch. + At this point we are not doing validity checking other than all printable characters. + + We are left with 0 & 4 which don't match (012057 vs. 012077). + So I guess we don't have any two that match so it is a toss up. + + reject 7 invalid character = ZCZC-EAS-RWT-0120в–’ + reject 5 invalid character = ZCZC-ECW-RWT-012057-012081-012101-012103-012115+003 + frame_buf 6 = ZCZC-EQS-RWT-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV- + frame_buf 4 = ZCZC-EAS-RWT-012077-012081-012101-012103-012115+0030-2780415-WTSP/TV- + frame_buf 3 = ZCZC-EAS-RWT-0120u7-012281-012101-012103-092115+0038-2780415-VTSP/TV- + frame_buf 0 = ZCZC-EAS-RWT-012057-412081-012101-012103-012115+0030-2780415-WTSP/TV- + + DECODED[1] 0:01.313 EAS audio level = 194(106/108) |__||_|__ + [0.0] EAS>APDW16:{DEZCZC-EAS-RWT-012057-412081-012101-012103-012115+0030-2780415-WTSP/TV- + +Case 2: We have two that match so pick either one. + + reject 5 invalid character = ZCZC-EAS-RWв–’ + reject 7 invalid character = ZCZC-EAS-RWT-0 + reject 3 invalid character = ZCZC-EAS-RWT-012057-012080-012101-012103-01211 + reject 0 invalid character = ZCZC-EAS-RWT-012057-012081-012101-012103-012115+0030-2780415-Wв–’ + frame_buf 6 = ZCZC-EAS-RWT-012057-012081-012!01-012103-012115+0030-2780415-WTSP/TV- + frame_buf 1 = ZCZC-EAS-RWT-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV- + + DECODED[2] 0:03.617 EAS audio level = 194(106/108) _|____|__ + [0.1] EAS>APDW16:{DEZCZC-EAS-RWT-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV- + +Case 3: Slice 6 is a mismatch (EAs vs. EAS). + Slice 7 has RST rather than RWT. + 2 & 4 don't match either (012141 vs. 012101). + We have another case where no two match so there is no clear winner. + + + reject 5 invalid character = ZCZC-EAS-RWT-012057-012081-012101-012103-012115+в–’ + frame_buf 7 = ZCZC-EAS-RST-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV- + frame_buf 6 = ZCZC-EAs-RWT-012057-012081-012101-012103-012115+0030-2780415-WTSP/TV- + frame_buf 4 = ZCZC-EAS-RWT-112057-012081-012101-012103-012115+0030-2780415-WTSP/TV- + frame_buf 2 = ZCZC-EAS-RWT-012057-012081-012141-012103-012115+0030-2780415-WTSP/TV- + + DECODED[3] 0:05.920 EAS audio level = 194(106/108) __|_|_||_ + [0.2] EAS>APDW16:{DEZCZC-EAS-RWT-012057-012081-012141-012103-012115+0030-2780415-WTSP/TV- + +Conclusions: + + (1) The existing algorithm gives a higher preference to those frames matching others. + We didn't see any cases here where that would be to our advantage. + + (2) A partial solution would be more validity checking. (i.e. non-digit where + digit is expected.) But wait... We might want to keep it for consideration: + + (3) If I got REALLY ambitious, some day, we could compare all of them one column + at a time and take the most popular (and valid for that column) character and + use all of the most popular characters. Better yet, at the bit level. + +Of course this is probably all overkill because we would normally expect to have pretty +decent signals. The designers didn't even bother to add any sort of checksum for error checking. + +The random errors injected are also not realistic. Actual noise would probably wipe out the +same bit(s) for all of the slices. + +The protocol specification suggests comparing all 3 transmissions and taking the best 2 out of 3. +I think that would best be left to an external application and we just concentrate on being +a good modem here and providing a result when it is received. + +*/ + + +/*********************************************************************************** + * + * Name: hdlc_rec_bit + * + * Purpose: Extract HDLC frames from a stream of bits. + * + * Inputs: chan - Channel number. + * + * subchan - This allows multiple demodulators per channel. + * + * slice - Allows multiple slicers per demodulator (subchannel). + * + * raw - One bit from the demodulator. + * should be 0 or 1. + * + * is_scrambled - Is the data scrambled? + * + * descram_state - Current descrambler state. (not used - remove) + * Not so fast - plans to add new parameter. PSK already provides it. + * + * + * Description: This is called once for each received bit. + * For each valid frame, process_rec_frame() + * is called for further processing. + * + ***********************************************************************************/ + +void hdlc_rec_bit(int chan, int subchan, int slice, int raw, int is_scrambled, int not_used_remove) +{ + + int dbit; /* Data bit after undoing NRZI. */ + /* Should be only 0 or 1. */ + struct hdlc_state_s *H; + + /* + * Different state information for each channel / subchannel / slice. + */ + H = &hdlc_state[chan]; + + + /* + * Using NRZI encoding, + * A '0' bit is represented by an inversion since previous bit. + * A '1' bit is represented by no change. + */ + + if (is_scrambled) { + int descram; + + descram = descramble(raw, &(H->lfsr)); + + dbit = (descram == H->prev_descram); + H->prev_descram = descram; + H->prev_raw = raw; + } + else { + + dbit = (raw == H->prev_raw); + + H->prev_raw = raw; + } + + // After BER insertion, NRZI, and any descrambling, feed into FX.25 decoder as well. + +// fx25_rec_bit (chan, subchan, slice, dbit); + + il2p_rec_bit (chan, subchan, slice, raw); // Note: skip NRZI. + + /* + * Octets are sent LSB first. + * Shift the most recent 8 bits thru the pattern detector. + */ + H->pat_det >>= 1; + if (dbit) { + H->pat_det |= 0x80; + } + + H->flag4_det >>= 1; + if (dbit) { + H->flag4_det |= 0x80000000; + } + + rrbb_append_bit(H->rrbb, raw); + + if (H->pat_det == 0x7e) { + + rrbb_chop8(H->rrbb); + + /* + * The special pattern 01111110 indicates beginning and ending of a frame. + * If we have an adequate number of whole octets, it is a candidate for + * further processing. + * + * It might look odd that olen is being tested for 7 instead of 0. + * This is because oacc would already have 7 bits from the special + * "flag" pattern before it is detected here. + */ + + +#if OLD_WAY + +#if TEST + text_color_set(DW_COLOR_DEBUG); + dw_printf("\nfound flag, olen = %d, frame_len = %d\n", olen, frame_len); +#endif + if (H->olen == 7 && H->frame_len >= MIN_FRAME_LEN) { + + unsigned short actual_fcs, expected_fcs; + +#if TEST + int j; + dw_printf("TRADITIONAL: frame len = %d\n", H->frame_len); + for (j = 0; j < H->frame_len; j++) { + dw_printf(" %02x", H->frame_buf[j]); + } + dw_printf("\n"); + +#endif + /* Check FCS, low byte first, and process... */ + + /* Alternatively, it is possible to include the two FCS bytes */ + /* in the CRC calculation and look for a magic constant. */ + /* That would be easier in the case where the CRC is being */ + /* accumulated along the way as the octets are received. */ + /* I think making a second pass over it and comparing is */ + /* easier to understand. */ + + actual_fcs = H->frame_buf[H->frame_len - 2] | (H->frame_buf[H->frame_len - 1] << 8); + + expected_fcs = fcs_calc(H->frame_buf, H->frame_len - 2); + + if (actual_fcs == expected_fcs) { + alevel_t alevel = demod_get_audio_level(chan, subchan); + + multi_modem_process_rec_frame(chan, subchan, slice, H->frame_buf, H->frame_len - 2, alevel, RETRY_NONE, 0); /* len-2 to remove FCS. */ + } + else { + +#if TEST + dw_printf("*** actual fcs = %04x, expected fcs = %04x ***\n", actual_fcs, expected_fcs); +#endif + + } + + } + +#else + + /* + * New way - Decode the raw bits in later step. + */ + +#if TEST + text_color_set(DW_COLOR_DEBUG); + dw_printf("\nfound flag, channel %d.%d, %d bits in frame\n", chan, subchan, rrbb_get_len(H->rrbb) - 1); +#endif + if (rrbb_get_len(H->rrbb) >= MIN_FRAME_LEN * 8) { + + alevel_t alevel = demod_get_audio_level(chan, subchan); + + rrbb_set_audio_level(H->rrbb, alevel); + hdlc_rec2_block(H->rrbb); + /* Now owned by someone else who will free it. */ + + H->rrbb = rrbb_new(chan, 0, 0, is_scrambled, H->lfsr, H->prev_descram); /* Allocate a new one. */ + } + else { + rrbb_clear(H->rrbb, is_scrambled, H->lfsr, H->prev_descram); + } + + H->olen = 0; /* Allow accumulation of octets. */ + H->frame_len = 0; + + + rrbb_append_bit(H->rrbb, H->prev_raw); /* Last bit of flag. Needed to get first data bit. */ + /* Now that we are saving other initial state information, */ + /* it would be sensible to do the same for this instead */ + /* of lumping it in with the frame data bits. */ +#endif + + } + + //#define EXPERIMENT12B 1 + +#if EXPERIMENT12B + + else if (H->pat_det == 0xff) { + + /* + * Valid data will never have seven 1 bits in a row. + * + * 11111110 + * + * This indicates loss of signal. + * But we will let it slip thru because it might diminish + * our single bit fixup effort. Instead give up on frame + * only when we see eight 1 bits in a row. + * + * 11111111 + * + * What is the impact? No difference. + * + * Before: atest -P E -F 1 ../02_Track_2.wav = 1003 + * After: atest -P E -F 1 ../02_Track_2.wav = 1003 + */ + +#else + else if (H->pat_det == 0xfe) { + + /* + * Valid data will never have 7 one bits in a row. + * + * 11111110 + * + * This indicates loss of signal. + */ + +#endif + + H->olen = -1; /* Stop accumulating octets. */ + H->frame_len = 0; /* Discard anything in progress. */ + + rrbb_clear(H->rrbb, is_scrambled, H->lfsr, H->prev_descram); + + } + else if ((H->pat_det & 0xfc) == 0x7c) { + + /* + * If we have five '1' bits in a row, followed by a '0' bit, + * + * 0111110xx + * + * the current '0' bit should be discarded because it was added for + * "bit stuffing." + */ + ; + + } + else { + + /* + * In all other cases, accumulate bits into octets, and complete octets + * into the frame buffer. + */ + if (H->olen >= 0) { + + H->oacc >>= 1; + if (dbit) { + H->oacc |= 0x80; + } + H->olen++; + + if (H->olen == 8) { + H->olen = 0; + + if (H->frame_len < MAX_FRAME_LEN) { + H->frame_buf[H->frame_len] = H->oacc; + H->frame_len++; + } + } + } + } +} + +// TODO: Data Carrier Detect (DCD) is now based on DPLL lock +// rather than data patterns found here. +// It would make sense to move the next 2 functions to demod.c +// because this is done at the modem level, rather than HDLC decoder. + +/*------------------------------------------------------------------- + * + * Name: dcd_change + * + * Purpose: Combine DCD states of all subchannels/ into an overall + * state for the channel. + * + * Inputs: chan + * + * subchan 0 to MAX_SUBCHANS-1 for HDLC. + * SPECIAL CASE --> MAX_SUBCHANS for DTMF decoder. + * + * slice slicer number, 0 .. MAX_SLICERS - 1. + * + * state 1 for active, 0 for not. + * + * Returns: None. Use hdlc_rec_data_detect_any to retrieve result. + * + * Description: DCD for the channel is active if ANY of the subchannels/slices + * are active. Update the DCD indicator. + * + * version 1.3: Add DTMF detection into the final result. + * This is now called from dtmf.c too. + * + *--------------------------------------------------------------------*/ + +void dcd_change(int chan, int subchan, int slice, int state) +{ + +} + + +/*------------------------------------------------------------------- + * + * Name: hdlc_rec_data_detect_any + * + * Purpose: Determine if the radio channel is currently busy + * with packet data. + * This version doesn't care about voice or other sounds. + * This is used by the transmit logic to transmit only + * when the channel is clear. + * + * Inputs: chan - Audio channel. + * + * Returns: True if channel is busy (data detected) or + * false if OK to transmit. + * + * + * Description: We have two different versions here. + * + * hdlc_rec_data_detect_any sees if ANY of the decoders + * for this channel are receiving a signal. This is + * used to determine whether the channel is clear and + * we can transmit. This would apply to the 300 baud + * HF SSB case where we have multiple decoders running + * at the same time. The channel is busy if ANY of them + * thinks the channel is busy. + * + * Version 1.3: New option for input signal to inhibit transmit. + * + *--------------------------------------------------------------------*/ + +int hdlc_rec_data_detect_any(int chan) +{ + + int sc; + assert(chan >= 0 && chan < MAX_CHANS); + + for (sc = 0; sc < num_subchan[chan]; sc++) { + if (composite_dcd[chan][sc] != 0) + return (1); + } + + return (0); + +} /* end hdlc_rec_data_detect_any */ + +/* end hdlc_rec.c */ + + + +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see . +// + + + +/******************************************************************************** + * + * File: hdlc_rec2.c + * + * Purpose: Extract HDLC frame from a block of bits after someone + * else has done the work of pulling it out from between + * the special "flag" sequences. + * + * + * New in version 1.1: + * + * Several enhancements provided by Fabrice FAURE: + * + * - Additional types of attempts to fix a bad CRC. + * - Optimized code to reduce execution time. + * - Improved detection of duplicate packets from different fixup attempts. + * - Set limit on number of packets in fix up later queue. + * + * One of the new recovery attempt cases recovers three additional + * packets that were lost before. The one thing I disagree with is + * use of the word "swap" because that sounds like two things + * are being exchanged for each other. I would prefer "flip" + * or "invert" to describe changing a bit to the opposite state. + * I took "swap" out of the user-visible messages but left the + * rest of the source code as provided. + * + * Test results: We intentionally use the worst demodulator so there + * is more opportunity to try to fix the frames. + * + * atest -P A -F n 02_Track_2.wav + * + * n description frames sec + * -- ----------- ------ --- + * 0 no attempt 963 40 error-free frames + * 1 invert 1 979 41 16 more + * 2 invert 2 982 42 3 more + * 3 invert 3 982 42 no change + * 4 remove 1 982 43 no change + * 5 remove 2 982 43 no change + * 6 remove 3 982 43 no change + * 7 insert 1 982 45 no change + * 8 insert 2 982 47 no change + * 9 invert two sep 993 178 11 more, some visually obvious errors. + * 10 invert many? 993 190 no change + * 11 remove many 995 190 2 more, need to investigate in detail. + * 12 remove two sep 995 201 no change + * + * Observations: The "insert" and "remove" techniques had no benefit. I would not expect them to. + * We have a phase locked loop that attempts to track any slight variations in the + * timing so we sample near the middle of the bit interval. Bits can get corrupted + * by noise but not disappear or just appear. That would be a gap in the timing. + * These should probably be removed in a future version. + * + * + * Version 1.2: Now works for 9600 baud. + * This was more complicated due to the data scrambling. + * It was necessary to retain more initial state information after + * the start flag octet. + * + * Version 1.3: Took out all of the "insert" and "remove" cases because they + * offer no benenfit. + * + * Took out the delayed processing and just do it realtime. + * Changed SWAP to INVERT because it is more descriptive. + * + *******************************************************************************/ + + +//#define DEBUG 1 +//#define DEBUGx 1 +//#define DEBUG_LATER 1 + +/* Audio configuration. */ + +static struct audio_s *save_audio_config_p; + + +/* + * Minimum & maximum sizes of an AX.25 frame including the 2 octet FCS. + */ + +#define MIN_FRAME_LEN ((AX25_MIN_PACKET_LEN) + 2) + +#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2) + + + /* + * This is the current state of the HDLC decoder. + * + * It is possible to run multiple decoders concurrently by + * having a separate set of state variables for each. + * + * Should have a reset function instead of initializations here. + */ + + // TODO: Clean up. This is a remnant of splitting hdlc_rec.c into 2 parts. + // This is not the same as hdlc_state_s in hdlc_rec.c + // "2" was added to reduce confusion. Can be trimmed down. + +struct hdlc_state2_s { + + int prev_raw; /* Keep track of previous bit so */ + /* we can look for transitions. */ + /* Should be only 0 or 1. */ + + int is_scrambled; /* Set for 9600 baud. */ + int lfsr; /* Descrambler shift register for 9600 baud. */ + int prev_descram; /* Previous unscrambled for 9600 baud. */ + + + unsigned char pat_det; /* 8 bit pattern detector shift register. */ + /* See below for more details. */ + + unsigned char oacc; /* Accumulator for building up an octet. */ + + int olen; /* Number of bits in oacc. */ + /* When this reaches 8, oacc is copied */ + /* to the frame buffer and olen is zeroed. */ + + unsigned char frame_buf[MAX_FRAME_LEN]; + /* One frame is kept here. */ + + int frame_len; /* Number of octets in frame_buf. */ + /* Should be in range of 0 .. MAX_FRAME_LEN. */ + +}; + + + + +typedef enum retry_mode_e { + RETRY_MODE_CONTIGUOUS = 0, + RETRY_MODE_SEPARATED = 1, +} retry_mode_t; + +typedef enum retry_type_e { + RETRY_TYPE_NONE = 0, + RETRY_TYPE_SWAP = 1 +} retry_type_t; + +typedef struct retry_conf_s { + retry_t retry; + retry_mode_t mode; + retry_type_t type; + union { + struct { + int bit_idx_a; /* */ + int bit_idx_b; /* */ + int bit_idx_c; /* */ + } sep; /* RETRY_MODE_SEPARATED */ + + struct { + int bit_idx; + int nr_bits; + } contig; /* RETRY_MODE_CONTIGUOUS */ + + } u_bits; + int insert_value; + +} retry_conf_t; + + + + +#if defined(DIREWOLF_C) || defined(ATEST_C) || defined(UDPTEST_C) + +static const char * retry_text[] = { + "NONE", + "SINGLE", + "DOUBLE", + "TRIPLE", + "TWO_SEP", + "PASSALL" }; +#endif + + + +static int try_decode(rrbb_t block, int chan, int subchan, int slice, alevel_t alevel, retry_conf_t retry_conf, int passall); + +static int try_to_fix_quick_now(rrbb_t block, int chan, int subchan, int slice, alevel_t alevel); + +static int sanity_check(unsigned char *buf, int blen, retry_t bits_flipped, enum sanity_e sanity_test); + + +/*********************************************************************************** + * + * Name: hdlc_rec2_init + * + * Purpose: Initialization. + * + * Inputs: p_audio_config - Pointer to configuration settings. + * This is what we care about for each channel. + * + * enum retry_e fix_bits; + * Level of effort to recover from + * a bad FCS on the frame. + * 0 = no effort + * 1 = try inverting a single bit + * 2... = more techniques... + * + * enum sanity_e sanity_test; + * Sanity test to apply when finding a good + * CRC after changing one or more bits. + * Must look like APRS, AX.25, or anything. + * + * int passall; + * Allow thru even with bad CRC after exhausting + * all fixup attempts. + * + * Description: Save pointer to configuration for later use. + * + ***********************************************************************************/ + +void hdlc_rec2_init(struct audio_s *p_audio_config) +{ + save_audio_config_p = p_audio_config; +} + + + +/*********************************************************************************** + * + * Name: hdlc_rec2_block + * + * Purpose: Extract HDLC frame from a stream of bits. + * + * Inputs: block - Handle for bit array. + * + * Description: The other (original) hdlc decoder took one bit at a time + * right out of the demodulator. + * + * This is different in that it processes a block of bits + * previously extracted from between two "flag" patterns. + * + * This allows us to try decoding the same received data more + * than once. + * + * Version 1.2: Now works properly for G3RUH type scrambling. + * + ***********************************************************************************/ + + +void hdlc_rec2_block(rrbb_t block) +{ + int chan = rrbb_get_chan(block); + int subchan = rrbb_get_subchan(block); + int slice = rrbb_get_slice(block); + alevel_t alevel = rrbb_get_audio_level(block); + retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits; + int passall = save_audio_config_p->achan[chan].passall; + int ok; + +#if DEBUGx + text_color_set(DW_COLOR_DEBUG); + dw_printf("\n--- try to decode ---\n"); +#endif + + /* Create an empty retry configuration */ + retry_conf_t retry_cfg; + + memset(&retry_cfg, 0, sizeof(retry_cfg)); + + /* + * For our first attempt we don't try to alter any bits. + * Still let it thru if passall AND no retries are desired. + */ + + retry_cfg.type = RETRY_TYPE_NONE; + retry_cfg.mode = RETRY_MODE_CONTIGUOUS; + retry_cfg.retry = RETRY_NONE; + retry_cfg.u_bits.contig.nr_bits = 0; + retry_cfg.u_bits.contig.bit_idx = 0; + + ok = try_decode(block, chan, subchan, slice, alevel, retry_cfg, passall & (fix_bits == RETRY_NONE)); + if (ok) { +#if DEBUG + text_color_set(DW_COLOR_INFO); + dw_printf("Got it the first time.\n"); +#endif + rrbb_delete(block); + return; + } + + /* + * Not successful with frame in original form. + * See if we can "fix" it. + */ + if (try_to_fix_quick_now(block, chan, subchan, slice, alevel)) { + rrbb_delete(block); + return; + } + + + if (passall) { + /* Exhausted all desired fix up attempts. */ + /* Let thru even with bad CRC. Of course, it still */ + /* needs to be a minimum number of whole octets. */ + ok = try_decode(block, chan, subchan, slice, alevel, retry_cfg, 1); + rrbb_delete(block); + } + else { + rrbb_delete(block); + } + +} /* end hdlc_rec2_block */ + + +/*********************************************************************************** + * + * Name: try_to_fix_quick_now + * + * Purpose: Attempt some quick fixups that don't take very long. + * + * Inputs: block - Stream of bits that might be a frame. + * chan - Radio channel from which it was received. + * subchan - Which demodulator when more than one per channel. + * alevel - Audio level for later reporting. + * + * Global In: configuration fix_bits - Maximum level of fix up to attempt. + * + * RETRY_NONE (0) - Don't try any. + * RETRY_INVERT_SINGLE (1) - Try inverting single bits. + * etc. + * + * configuration passall - Let it thru with bad CRC after exhausting + * all fixup attempts. + * + * + * Returns: 1 for success. "try_decode" has passed the result along to the + * processing step. + * 0 for failure. Caller might continue with more aggressive attempts. + * + * Original: Some of the attempted fix up techniques are quick. + * We will attempt them immediately after receiving the frame. + * Others, that take time order N**2, will be done in a later section. + * + * Version 1.2: Now works properly for G3RUH type scrambling. + * + * Version 1.3: Removed the extra cases that didn't help. + * The separated bit case is now handled immediately instead of + * being thrown in a queue for later processing. + * + ***********************************************************************************/ + +static int try_to_fix_quick_now(rrbb_t block, int chan, int subchan, int slice, alevel_t alevel) +{ + int ok; + int len, i; + retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits; + //int passall = save_audio_config_p->achan[chan].passall; + + + len = rrbb_get_len(block); + /* Prepare the retry configuration */ + retry_conf_t retry_cfg; + + memset(&retry_cfg, 0, sizeof(retry_cfg)); + + /* Will modify only contiguous bits*/ + retry_cfg.mode = RETRY_MODE_CONTIGUOUS; + /* + * Try inverting one bit. + */ + if (fix_bits < RETRY_INVERT_SINGLE) { + + /* Stop before single bit fix up. */ + + return 0; /* failure. */ + } + /* Try to swap one bit */ + retry_cfg.type = RETRY_TYPE_SWAP; + retry_cfg.retry = RETRY_INVERT_SINGLE; + retry_cfg.u_bits.contig.nr_bits = 1; + + for (i = 0; i < len; i++) { + /* Set the index of the bit to swap */ + retry_cfg.u_bits.contig.bit_idx = i; + ok = try_decode(block, chan, subchan, slice, alevel, retry_cfg, 0); + if (ok) { +#if DEBUG + text_color_set(DW_COLOR_ERROR); + dw_printf("*** Success by flipping SINGLE bit %d of %d ***\n", i, len); +#endif + return 1; + } + } + + /* + * Try inverting two adjacent bits. + */ + if (fix_bits < RETRY_INVERT_DOUBLE) { + return 0; + } + /* Try to swap two contiguous bits */ + retry_cfg.retry = RETRY_INVERT_DOUBLE; + retry_cfg.u_bits.contig.nr_bits = 2; + + + for (i = 0; i < len - 1; i++) { + retry_cfg.u_bits.contig.bit_idx = i; + ok = try_decode(block, chan, subchan, slice, alevel, retry_cfg, 0); + if (ok) { +#if DEBUG + text_color_set(DW_COLOR_ERROR); + dw_printf("*** Success by flipping DOUBLE bit %d of %d ***\n", i, len); +#endif + return 1; + } + } + + /* + * Try inverting adjacent three bits. + */ + if (fix_bits < RETRY_INVERT_TRIPLE) { + return 0; + } + /* Try to swap three contiguous bits */ + retry_cfg.retry = RETRY_INVERT_TRIPLE; + retry_cfg.u_bits.contig.nr_bits = 3; + + for (i = 0; i < len - 2; i++) { + retry_cfg.u_bits.contig.bit_idx = i; + ok = try_decode(block, chan, subchan, slice, alevel, retry_cfg, 0); + if (ok) { +#if DEBUG + text_color_set(DW_COLOR_ERROR); + dw_printf("*** Success by flipping TRIPLE bit %d of %d ***\n", i, len); +#endif + return 1; + } + } + + + /* + * Two non-adjacent ("separated") single bits. + * It chews up a lot of CPU time. Usual test takes 4 times longer to run. + * + * Processing time is order N squared so time goes up rapidly with larger frames. + */ + if (fix_bits < RETRY_INVERT_TWO_SEP) { + return 0; + } + + retry_cfg.mode = RETRY_MODE_SEPARATED; + retry_cfg.type = RETRY_TYPE_SWAP; + retry_cfg.retry = RETRY_INVERT_TWO_SEP; + retry_cfg.u_bits.sep.bit_idx_c = -1; + +#ifdef DEBUG_LATER + tstart = dtime_now(); + dw_printf("*** Try flipping TWO SEPARATED BITS %d bits\n", len); +#endif + len = rrbb_get_len(block); + for (i = 0; i < len - 2; i++) { + retry_cfg.u_bits.sep.bit_idx_a = i; + int j; + + ok = 0; + for (j = i + 2; j < len; j++) { + retry_cfg.u_bits.sep.bit_idx_b = j; + ok = try_decode(block, chan, subchan, slice, alevel, retry_cfg, 0); + if (ok) { + break; + } + + } + if (ok) { +#if DEBUG + text_color_set(DW_COLOR_ERROR); + dw_printf("*** Success by flipping TWO SEPARATED bits %d and %d of %d \n", i, j, len); +#endif + return (1); + } + } + + return 0; +} + + + +// TODO: Remove this. but first figure out what to do in atest.c + + + +int hdlc_rec2_try_to_fix_later(rrbb_t block, int chan, int subchan, int slice, alevel_t alevel) +{ + int ok; + //int len; + //retry_t fix_bits = save_audio_config_p->achan[chan].fix_bits; + int passall = save_audio_config_p->achan[chan].passall; +#if DEBUG_LATER + double tstart, tend; +#endif + retry_conf_t retry_cfg; + + memset(&retry_cfg, 0, sizeof(retry_cfg)); + + //len = rrbb_get_len(block); + + +/* + * All fix up attempts have failed. + * Should we pass it along anyhow with a bad CRC? + * Note that we still need a minimum number of whole octets. + */ + if (passall) { + + retry_cfg.type = RETRY_TYPE_NONE; + retry_cfg.mode = RETRY_MODE_CONTIGUOUS; + retry_cfg.retry = RETRY_NONE; + retry_cfg.u_bits.contig.nr_bits = 0; + retry_cfg.u_bits.contig.bit_idx = 0; + ok = try_decode(block, chan, subchan, slice, alevel, retry_cfg, passall); + return (ok); + } + + return (0); + +} /* end hdlc_rec2_try_to_fix_later */ + + + +/* + * Check if the specified index of bit has been modified with the current type of configuration + * Provide a specific implementation for contiguous mode to optimize number of tests done in the loop + */ + +inline static char is_contig_bit_modified(int bit_idx, retry_conf_t retry_conf) { + int cont_bit_idx = retry_conf.u_bits.contig.bit_idx; + int cont_nr_bits = retry_conf.u_bits.contig.nr_bits; + + if (bit_idx >= cont_bit_idx && (bit_idx < cont_bit_idx + cont_nr_bits)) + return 1; + else + return 0; +} + +/* + * Check if the specified index of bit has been modified with the current type of configuration in separated bit index mode + * Provide a specific implementation for separated mode to optimize number of tests done in the loop + */ + +inline static char is_sep_bit_modified(int bit_idx, retry_conf_t retry_conf) { + if (bit_idx == retry_conf.u_bits.sep.bit_idx_a || + bit_idx == retry_conf.u_bits.sep.bit_idx_b || + bit_idx == retry_conf.u_bits.sep.bit_idx_c) + return 1; + else + return 0; +} + + + +/*********************************************************************************** + * + * Name: try_decode + * + * Purpose: + * + * Inputs: block - Bit string that was collected between "flag" patterns. + * + * chan, subchan - where it came from. + * + * alevel - audio level for later reporting. + * + * retry_conf - Controls changes that will be attempted to get a good CRC. + * + * retry: + * Level of effort to recover from a bad FCS on the frame. + * RETRY_NONE = 0 + * RETRY_INVERT_SINGLE = 1 + * RETRY_INVERT_DOUBLE = 2 + * RETRY_INVERT_TRIPLE = 3 + * RETRY_INVERT_TWO_SEP = 4 + * + * mode: RETRY_MODE_CONTIGUOUS - change adjacent bits. + * contig.bit_idx - first bit position + * contig.nr_bits - number of bits + * + * RETRY_MODE_SEPARATED - change bits not next to each other. + * sep.bit_idx_a - bit positions + * sep.bit_idx_b - bit positions + * sep.bit_idx_c - bit positions + * + * type: RETRY_TYPE_NONE - Make no changes. + * RETRY_TYPE_SWAP - Try inverting. + * + * passall - All it thru even with bad CRC. + * Valid only when no changes make. i.e. + * retry == RETRY_NONE, type == RETRY_TYPE_NONE + * + * Returns: 1 = successfully extracted something. + * 0 = failure. + * + ***********************************************************************************/ + +static int try_decode(rrbb_t block, int chan, int subchan, int slice, alevel_t alevel, retry_conf_t retry_conf, int passall) +{ + struct hdlc_state2_s H2; + int blen; /* Block length in bits. */ + int i; + int raw; /* From demodulator. Should be 0 or 1. */ +#if DEBUGx + int crc_failed = 1; +#endif + int retry_conf_mode = retry_conf.mode; + int retry_conf_type = retry_conf.type; + int retry_conf_retry = retry_conf.retry; + + + H2.is_scrambled = rrbb_get_is_scrambled(block); + H2.prev_descram = rrbb_get_prev_descram(block); + H2.lfsr = rrbb_get_descram_state(block); + H2.prev_raw = rrbb_get_bit(block, 0); /* Actually last bit of the */ + /* opening flag so we can derive the */ + /* first data bit. */ + + /* Does this make sense? */ + /* This is the last bit of the "flag" pattern. */ + /* If it was corrupted we wouldn't have detected */ + /* the start of frame. */ + + if ((retry_conf.mode == RETRY_MODE_CONTIGUOUS && is_contig_bit_modified(0, retry_conf)) || + (retry_conf.mode == RETRY_MODE_SEPARATED && is_sep_bit_modified(0, retry_conf))) { + H2.prev_raw = !H2.prev_raw; + } + + H2.pat_det = 0; + H2.oacc = 0; + H2.olen = 0; + H2.frame_len = 0; + + blen = rrbb_get_len(block); + +#if DEBUGx + text_color_set(DW_COLOR_DEBUG); + if (retry_conf.type == RETRY_TYPE_NONE) + dw_printf("try_decode: blen=%d\n", blen); +#endif + for (i = 1; i < blen; i++) { + /* Get the value for the current bit */ + raw = rrbb_get_bit(block, i); + /* If swap two sep mode , swap the bit if needed */ + if (retry_conf_retry == RETRY_INVERT_TWO_SEP) { + if (is_sep_bit_modified(i, retry_conf)) + raw = !raw; + } + /* Else handle all the others contiguous modes */ + else if (retry_conf_mode == RETRY_MODE_CONTIGUOUS) { + + if (retry_conf_type == RETRY_TYPE_SWAP) { + /* If this is the bit to swap */ + if (is_contig_bit_modified(i, retry_conf)) + raw = !raw; + } + + } + else { + } + /* + * Octets are sent LSB first. + * Shift the most recent 8 bits thru the pattern detector. + */ + H2.pat_det >>= 1; + + /* + * Using NRZI encoding, + * A '0' bit is represented by an inversion since previous bit. + * A '1' bit is represented by no change. + * Note: this code can be factorized with the raw != H2.prev_raw code at the cost of processing time + */ + + int dbit; + + if (H2.is_scrambled) { + int descram; + + descram = descramble(raw, &(H2.lfsr)); + + dbit = (descram == H2.prev_descram); + H2.prev_descram = descram; + H2.prev_raw = raw; + } + else { + + dbit = (raw == H2.prev_raw); + H2.prev_raw = raw; + } + + if (dbit) { + + H2.pat_det |= 0x80; + /* Valid data will never have 7 one bits in a row: exit. */ + if (H2.pat_det == 0xfe) { +#if DEBUGx + text_color_set(DW_COLOR_DEBUG); + dw_printf("try_decode: found abort, i=%d\n", i); +#endif + return 0; + } + H2.oacc >>= 1; + H2.oacc |= 0x80; + } + else { + + /* The special pattern 01111110 indicates beginning and ending of a frame: exit. */ + if (H2.pat_det == 0x7e) { +#if DEBUGx + text_color_set(DW_COLOR_DEBUG); + dw_printf("try_decode: found flag, i=%d\n", i); +#endif + return 0; + /* + * If we have five '1' bits in a row, followed by a '0' bit, + * + * 011111xx + * + * the current '0' bit should be discarded because it was added for + * "bit stuffing." + */ + + } + else if ((H2.pat_det >> 2) == 0x1f) { + continue; + } + H2.oacc >>= 1; + } + + /* + * Now accumulate bits into octets, and complete octets + * into the frame buffer. + */ + + H2.olen++; + + if (H2.olen & 8) { + H2.olen = 0; + + if (H2.frame_len < MAX_FRAME_LEN) { + H2.frame_buf[H2.frame_len] = H2.oacc; + H2.frame_len++; + + } + } + } /* end of loop on all bits in block */ +/* + * Do we have a minimum number of complete bytes? + */ + +#if DEBUGx + text_color_set(DW_COLOR_DEBUG); + dw_printf("try_decode: olen=%d, frame_len=%d\n", H2.olen, H2.frame_len); +#endif + + if (H2.olen == 0 && H2.frame_len >= MIN_FRAME_LEN) { + + unsigned short actual_fcs, expected_fcs; + +#if DEBUGx + if (retry_conf.type == RETRY_TYPE_NONE) { + int j; + text_color_set(DW_COLOR_DEBUG); + dw_printf("NEW WAY: frame len = %d\n", H2.frame_len); + for (j = 0; j < H2.frame_len; j++) { + dw_printf(" %02x", H2.frame_buf[j]); + } + dw_printf("\n"); + + } +#endif + /* Check FCS, low byte first, and process... */ + + /* Alternatively, it is possible to include the two FCS bytes */ + /* in the CRC calculation and look for a magic constant. */ + /* That would be easier in the case where the CRC is being */ + /* accumulated along the way as the octets are received. */ + /* I think making a second pass over it and comparing is */ + /* easier to understand. */ + + actual_fcs = H2.frame_buf[H2.frame_len - 2] | (H2.frame_buf[H2.frame_len - 1] << 8); + + expected_fcs = get_fcs(H2.frame_buf, H2.frame_len - 2); + + + if (actual_fcs == expected_fcs && + sanity_check(H2.frame_buf, H2.frame_len - 2, retry_conf.retry, save_audio_config_p->achan[chan].sanity_test)) { + + // TODO: Shouldn't be necessary to pass chan, subchan, alevel into + // try_decode because we can obtain them from block. + // Let's make sure that assumption is good... + + assert(rrbb_get_chan(block) == chan); + assert(rrbb_get_subchan(block) == subchan); + multi_modem_process_rec_frame(chan, subchan, slice, H2.frame_buf, H2.frame_len - 2, alevel, retry_conf.retry, 0); /* len-2 to remove FCS. */ + return 1; /* success */ + + } + else if (passall) { + if (retry_conf_retry == RETRY_NONE && retry_conf_type == RETRY_TYPE_NONE) { + + //text_color_set(DW_COLOR_ERROR); + //dw_printf ("ATTEMPTING PASSALL PROCESSING\n"); + + multi_modem_process_rec_frame(chan, subchan, slice, H2.frame_buf, H2.frame_len - 2, alevel, RETRY_MAX, 0); /* len-2 to remove FCS. */ + return 1; /* success */ + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf("try_decode: internal error passall = %d, retry_conf_retry = %d, retry_conf_type = %d\n", + passall, retry_conf_retry, retry_conf_type); + } + } + else { + + goto failure; + } + } + else { +#if DEBUGx + crc_failed = 0; +#endif + goto failure; + } +failure: +#if DEBUGx + if (retry_conf.type == RETRY_TYPE_NONE) { + int j; + text_color_set(DW_COLOR_ERROR); + if (crc_failed) + dw_printf("CRC failed\n"); + if (H2.olen != 0) + dw_printf("Bad olen: %d \n", H2.olen); + else if (H2.frame_len < MIN_FRAME_LEN) { + dw_printf("Frame too small\n"); + goto end; + } + + dw_printf("FAILURE with frame: frame len = %d\n", H2.frame_len); + dw_printf("\n"); + for (j = 0; j < H2.frame_len; j++) { + dw_printf(" %02x", H2.frame_buf[j]); + } + dw_printf("\nDEC\n"); + for (j = 0; j < H2.frame_len; j++) { + dw_printf("%c", H2.frame_buf[j] >> 1); + } + dw_printf("\nORIG\n"); + for (j = 0; j < H2.frame_len; j++) { + dw_printf("%c", H2.frame_buf[j]); + } + dw_printf("\n"); + } +end: +#endif + return 0; /* failure. */ + +} /* end try_decode */ + + + +/*********************************************************************************** + * + * Name: sanity_check + * + * Purpose: Try to weed out bogus packets from initially failed FCS matches. + * + * Inputs: buf + * + * blen + * + * bits_flipped + * + * sanity How much sanity checking to perform: + * SANITY_APRS - Looks like APRS. See User Guide, + * section that discusses bad apples. + * SANITY_AX25 - Has valid AX.25 address part. + * No checking of the rest. Useful for + * connected mode packet. + * SANITY_NONE - No checking. Would be suitable + * only if using frames that don't conform + * to AX.25 standard. + * + * Returns: 1 if it passes the sanity test. + * + * Description: This is NOT a validity check. + * We don't know if modifying the frame fixed the problem or made it worse. + * We can only test if it looks reasonable. + * + ***********************************************************************************/ + + +static int sanity_check(unsigned char *buf, int blen, retry_t bits_flipped, enum sanity_e sanity_test) +{ + int alen; /* Length of address part. */ + int j; + + /* + * No sanity check if we didn't try fixing the data. + * Should we have different levels of checking depending on + * how much we try changing the raw data? + */ + if (bits_flipped == RETRY_NONE) { + return 1; + } + + + /* + * If using frames that do not conform to AX.25, it might be + * desirable to skip the sanity check entirely. + */ + if (sanity_test == SANITY_NONE) { + return (1); + } + + /* + * Address part must be a multiple of 7. + */ + + alen = 0; + for (j = 0; j < blen && alen == 0; j++) { + if (buf[j] & 0x01) { + alen = j + 1; + } + } + + if (alen % 7 != 0) { +#if DEBUGx + text_color_set(DW_COLOR_ERROR); + dw_printf("sanity_check: FAILED. Address part length %d not multiple of 7.\n", alen); +#endif + return 0; + } + + /* + * Need at least 2 addresses and maximum of 8 digipeaters. + */ + + if (alen / 7 < 2 || alen / 7 > 10) { +#if DEBUGx + text_color_set(DW_COLOR_ERROR); + dw_printf("sanity_check: FAILED. Too few or many addresses.\n"); +#endif + return 0; + } + + /* + * Addresses can contain only upper case letters, digits, and space. + */ + + for (j = 0; j < alen; j += 7) { + + char addr[7]; + + addr[0] = buf[j + 0] >> 1; + addr[1] = buf[j + 1] >> 1; + addr[2] = buf[j + 2] >> 1; + addr[3] = buf[j + 3] >> 1; + addr[4] = buf[j + 4] >> 1; + addr[5] = buf[j + 5] >> 1; + addr[6] = '\0'; + + + if ((!isupper(addr[0]) && !isdigit(addr[0])) || + (!isupper(addr[1]) && !isdigit(addr[1]) && addr[1] != ' ') || + (!isupper(addr[2]) && !isdigit(addr[2]) && addr[2] != ' ') || + (!isupper(addr[3]) && !isdigit(addr[3]) && addr[3] != ' ') || + (!isupper(addr[4]) && !isdigit(addr[4]) && addr[4] != ' ') || + (!isupper(addr[5]) && !isdigit(addr[5]) && addr[5] != ' ')) { +#if DEBUGx + text_color_set(DW_COLOR_ERROR); + dw_printf("sanity_check: FAILED. Invalid characters in addresses \"%s\"\n", addr); +#endif + return 0; + } + } + + + /* + * That's good enough for the AX.25 sanity check. + * Continue below for additional APRS checking. + */ + if (sanity_test == SANITY_AX25) { + return (1); + } + + /* + * The next two bytes should be 0x03 and 0xf0 for APRS. + */ + + if (buf[alen] != 0x03 || buf[alen + 1] != 0xf0) { + return (0); + } + + /* + * Finally, look for bogus characters in the information part. + * In theory, the bytes could have any values. + * In practice, we find only printable ASCII characters and: + * + * 0x0a line feed + * 0x0d carriage return + * 0x1c MIC-E + * 0x1d MIC-E + * 0x1e MIC-E + * 0x1f MIC-E + * 0x7f MIC-E + * 0x80 "{UIV32N}<0x0d><0x9f><0x80>" + * 0x9f "{UIV32N}<0x0d><0x9f><0x80>" + * 0xb0 degree symbol, ISO LATIN1 + * (Note: UTF-8 uses two byte sequence 0xc2 0xb0.) + * 0xbe invalid MIC-E encoding. + * 0xf8 degree symbol, Microsoft code page 437 + * + * So, if we have something other than these (in English speaking countries!), + * chances are that we have bogus data from twiddling the wrong bits. + * + * Notice that we shouldn't get here for good packets. This extra level + * of checking happens only if we twiddled a couple of bits, possibly + * creating bad data. We want to be very fussy. + */ + + for (j = alen + 2; j < blen; j++) { + int ch = buf[j]; + + if (!((ch >= 0x1c && ch <= 0x7f) + || ch == 0x0a + || ch == 0x0d + || ch == 0x80 + || ch == 0x9f + || ch == 0xc2 + || ch == 0xb0 + || ch == 0xf8)) { +#if DEBUGx + text_color_set(DW_COLOR_ERROR); + dw_printf("sanity_check: FAILED. Probably bogus info char 0x%02x\n", ch); +#endif + return 0; + } + } + + return 1; +} + + +/* end hdlc_rec2.c */ + + + +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2015, 2019, 2021 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see . +// + + +//#define DEBUG4 1 /* capture 9600 output to log files */ + + +/*------------------------------------------------------------------ + * + * Module: demod_9600.c + * + * Purpose: Demodulator for baseband signal. + * This is used for AX.25 (with scrambling) and IL2P without. + * + * Input: Audio samples from either a file or the "sound card." + * + * Outputs: Calls hdlc_rec_bit() for each bit demodulated. + * + *---------------------------------------------------------------*/ + + + + // Fine tuning for different demodulator types. + // Don't remove this section. It is here for a reason. + + +void gen_lowpass(float fc, float *lp_filter, int filter_size, bp_window_t wtype); +void hdlc_rec_bit(int chan, int subchan, int slice, int raw, int is_scrambled, int not_used_remove); + +static float slice_point[MAX_SUBCHANS]; + + +/* Add sample to buffer and shift the rest down. */ + + +static inline void push_sample(float val, float *buff, int size) +{ + memmove(buff + 1, buff, (size - 1) * sizeof(float)); + buff[0] = val; +} + + +/* FIR filter kernel. */ + + +static inline float convolve(const float *__restrict__ data, const float *__restrict__ filter, int filter_size) +{ + float sum = 0.0f; + int j; + + //#pragma GCC ivdep // ignored until gcc 4.9 + for (j = 0; j < filter_size; j++) { + sum += filter[j] * data[j]; + } + return (sum); +} + +/* Automatic gain control. */ +/* Result should settle down to 1 unit peak to peak. i.e. -0.5 to +0.5 */ + + +static inline float agc(float in, float fast_attack, float slow_decay, float *ppeak, float *pvalley) +{ + if (in >= *ppeak) { + *ppeak = in * fast_attack + *ppeak * (1.0f - fast_attack); + } + else { + *ppeak = in * slow_decay + *ppeak * (1.0f - slow_decay); + } + + if (in <= *pvalley) { + *pvalley = in * fast_attack + *pvalley * (1.0f - fast_attack); + } + else { + *pvalley = in * slow_decay + *pvalley * (1.0f - slow_decay); + } + + if (*ppeak > *pvalley) { + return ((in - 0.5f * (*ppeak + *pvalley)) / (*ppeak - *pvalley)); + } + return (0.0); +} + + +/*------------------------------------------------------------------ + * + * Name: demod_9600_init + * + * Purpose: Initialize the 9600 (or higher) baud demodulator. + * + * Inputs: modem_type - Determines whether scrambling is used. + * + * samples_per_sec - Number of samples per second for audio. + * + * upsample - Factor to upsample the incoming stream. + * After a lot of experimentation, I discovered that + * it works better if the data is upsampled. + * This reduces the jitter for PLL synchronization. + * + * baud - Data rate in bits per second. + * + * D - Address of demodulator state. + * + * Returns: None + * + *----------------------------------------------------------------*/ + +void demod_9600_init(enum modem_t modem_type, int original_sample_rate, int upsample, int baud, struct demodulator_state_s *D) +{ + float fc; + int j; + if (upsample < 1) upsample = 1; + if (upsample > 4) upsample = 4; + + + memset(D, 0, sizeof(struct demodulator_state_s)); + D->modem_type = modem_type; + D->num_slicers = 1; + + // Multiple profiles in future? + + // switch (profile) { + + // case 'J': // upsample x2 with filtering. + // case 'K': // upsample x3 with filtering. + // case 'L': // upsample x4 with filtering. + + + D->lp_filter_len_bits = 1.0; // -U4 = 61 4.59 samples/symbol + + // Works best with odd number in some tests. Even is better in others. + //D->lp_filter_size = ((int) (0.5f * ( D->lp_filter_len_bits * (float)original_sample_rate / (float)baud ))) * 2 + 1; + + // Just round to nearest integer. + D->lp_filter_size = (int)((D->lp_filter_len_bits * (float)original_sample_rate / baud) + 0.5f); + + D->lp_window = BP_WINDOW_COSINE; + + D->lpf_baud = 1.00; + + D->agc_fast_attack = 0.080; + D->agc_slow_decay = 0.00012; + + D->pll_locked_inertia = 0.89; + D->pll_searching_inertia = 0.67; + + // break; + // } + +#if 0 + text_color_set(DW_COLOR_DEBUG); + dw_printf("---------- %s (%d, %d) -----------\n", __func__, samples_per_sec, baud); + dw_printf("filter_len_bits = %.2f\n", D->lp_filter_len_bits); + dw_printf("lp_filter_size = %d\n", D->lp_filter_size); + dw_printf("lp_window = %d\n", D->lp_window); + dw_printf("lpf_baud = %.2f\n", D->lpf_baud); + dw_printf("samples per bit = %.1f\n", (double)samples_per_sec / baud); +#endif + + + // PLL needs to use the upsampled rate. + + D->pll_step_per_sample = + (int)round(TICKS_PER_PLL_CYCLE * (double)baud / (double)(original_sample_rate * upsample)); + + +#ifdef TUNE_LP_WINDOW + D->lp_window = TUNE_LP_WINDOW; +#endif + +#if TUNE_LP_FILTER_SIZE + D->lp_filter_size = TUNE_LP_FILTER_SIZE; +#endif + +#ifdef TUNE_LPF_BAUD + D->lpf_baud = TUNE_LPF_BAUD; +#endif + +#ifdef TUNE_AGC_FAST + D->agc_fast_attack = TUNE_AGC_FAST; +#endif + +#ifdef TUNE_AGC_SLOW + D->agc_slow_decay = TUNE_AGC_SLOW; +#endif + +#if defined(TUNE_PLL_LOCKED) + D->pll_locked_inertia = TUNE_PLL_LOCKED; +#endif + +#if defined(TUNE_PLL_SEARCHING) + D->pll_searching_inertia = TUNE_PLL_SEARCHING; +#endif + + // Initial filter (before scattering) is based on upsampled rate. + + fc = (float)baud * D->lpf_baud / (float)(original_sample_rate * upsample); + + //dw_printf ("demod_9600_init: call gen_lowpass(fc=%.2f, , size=%d, )\n", fc, D->lp_filter_size); + + gen_lowpass(fc, D->u.bb.lp_filter, D->lp_filter_taps * upsample, D->lp_window); + + // New in 1.7 - + // Use a polyphase filter to reduce the CPU load. + // Originally I used zero stuffing to upsample. + // Here is the general idea. + // + // Suppose the input samples are 1 2 3 4 5 6 7 8 9 ... + // Filter coefficients are a b c d e f g h i ... + // + // With original sampling rate, the filtering would involve multiplying and adding: + // + // 1a 2b 3c 4d 5e 6f ... + // + // When upsampling by 3, each of these would need to be evaluated + // for each audio sample: + // + // 1a 0b 0c 2d 0e 0f 3g 0h 0i ... + // 0a 1b 0c 0d 2e 0f 0g 3h 0i ... + // 0a 0b 1c 0d 0e 2f 0g 0h 3i ... + // + // 2/3 of the multiplies are always by a stuffed zero. + // We can do this more efficiently by removing them. + // + // 1a 2d 3g ... + // 1b 2e 3h ... + // 1c 2f 3i ... + // + // We scatter the original filter across multiple shorter filters. + // Each input sample cycles around them to produce the upsampled rate. + // + // a d g ... + // b e h ... + // c f i ... + // + // There are countless sources of information DSP but this one is unique + // in that it is a college course that mentions APRS. + // https://www2.eecs.berkeley.edu/Courses/EE123 + // + // Was the effort worthwhile? Times on an RPi 3. + // + // command: atest -B9600 ~/walkabout9600[abc]-compressed*.wav + // + // These are 3 recordings of a portable system being carried out of + // range and back in again. It is a real world test for weak signals. + // + // options num decoded seconds x realtime + // 1.6 1.7 1.6 1.7 1.6 1.7 + // --- --- --- --- --- --- + // -P- 171 172 23.928 17.967 14.9 19.9 + // -P+ 180 180 54.688 48.772 6.5 7.3 + // -P- -F1 177 178 32.686 26.517 10.9 13.5 + // + // So, it turns out that -P+ doesn't have a dramatic improvement, only + // around 4%, for drastically increased CPU requirements. + // Maybe we should turn that off by default, especially for ARM. + // + + int k = 0; + for (int i = 0; i < D->lp_filter_size; i++) { + D->u.bb.lp_polyphase_1[i] = D->u.bb.lp_filter[k++]; + if (upsample >= 2) { + D->u.bb.lp_polyphase_2[i] = D->u.bb.lp_filter[k++]; + if (upsample >= 3) { + D->u.bb.lp_polyphase_3[i] = D->u.bb.lp_filter[k++]; + if (upsample >= 4) { + D->u.bb.lp_polyphase_4[i] = D->u.bb.lp_filter[k++]; + } + } + } + } + + + /* Version 1.2: Experiment with different slicing levels. */ + // Really didn't help that much because we should have a symmetrical signal. + + for (j = 0; j < MAX_SUBCHANS; j++) { + slice_point[j] = 0.02f * (j - 0.5f * (MAX_SUBCHANS - 1)); + //dw_printf ("slice_point[%d] = %+5.2f\n", j, slice_point[j]); + } + +} /* end fsk_demod_init */ + + + +/*------------------------------------------------------------------- + * + * Name: demod_9600_process_sample + * + * Purpose: (1) Filter & slice the signal. + * (2) Descramble it. + * (2) Recover clock and data. + * + * Inputs: chan - Audio channel. 0 for left, 1 for right. + * + * sam - One sample of audio. + * Should be in range of -32768 .. 32767. + * + * Returns: None + * + * Descripion: "9600 baud" packet is FSK for an FM voice transceiver. + * By the time it gets here, it's really a baseband signal. + * At one extreme, we could have a 4800 Hz square wave. + * A the other extreme, we could go a considerable number + * of bit times without any transitions. + * + * The trick is to extract the digital data which has + * been distorted by going thru voice transceivers not + * intended to pass this sort of "audio" signal. + * + * For G3RUH mode, data is "scrambled" to reduce the amount of DC bias. + * The data stream must be unscrambled at the receiving end. + * + * We also have a digital phase locked loop (PLL) + * to recover the clock and pick out data bits at + * the proper rate. + * + * For each recovered data bit, we call: + * + * hdlc_rec (channel, demodulated_bit); + * + * to decode HDLC frames from the stream of bits. + * + * Future: This could be generalized by passing in the name + * of the function to be called for each bit recovered + * from the demodulator. For now, it's simply hard-coded. + * + * After experimentation, I found that this works better if + * the original signal is upsampled by 2x or even 4x. + * + * References: 9600 Baud Packet Radio Modem Design + * http://www.amsat.org/amsat/articles/g3ruh/109.html + * + * The KD2BD 9600 Baud Modem + * http://www.amsat.org/amsat/articles/kd2bd/9k6modem/ + * + * 9600 Baud Packet Handbook + * ftp://ftp.tapr.org/general/9600baud/96man2x0.txt + * + * + *--------------------------------------------------------------------*/ + +inline static void nudge_pll(int chan, int subchan, int slice, float demod_out, struct demodulator_state_s *D); + +static void process_filtered_sample(int chan, float fsam, struct demodulator_state_s *D); + + + +void demod_9600_process_sample(int chan, int sam, int upsample, struct demodulator_state_s *D) +{ + float fsam; + +#if DEBUG4 + static FILE *demod_log_fp = NULL; + static int log_file_seq = 0; /* Part of log file name */ +#endif + + int subchan = 0; + + assert(chan >= 0 && chan < MAX_CHANS); + assert(subchan >= 0 && subchan < MAX_SUBCHANS); + + /* Scale to nice number for convenience. */ + /* Consistent with the AFSK demodulator, we'd like to use */ + /* only half of the dynamic range to have some headroom. */ + /* i.e. input range +-16k becomes +-1 here and is */ + /* displayed in the heard line as audio level 100. */ + + fsam = (float)sam / 16384.0f; + + // Low pass filter + push_sample(fsam, D->u.bb.audio_in, D->lp_filter_size); + + fsam = convolve(D->u.bb.audio_in, D->u.bb.lp_polyphase_1, D->lp_filter_size); + process_filtered_sample(chan, fsam, D); + if (upsample >= 2) { + fsam = convolve(D->u.bb.audio_in, D->u.bb.lp_polyphase_2, D->lp_filter_size); + process_filtered_sample(chan, fsam, D); + if (upsample >= 3) { + fsam = convolve(D->u.bb.audio_in, D->u.bb.lp_polyphase_3, D->lp_filter_size); + process_filtered_sample(chan, fsam, D); + if (upsample >= 4) { + fsam = convolve(D->u.bb.audio_in, D->u.bb.lp_polyphase_4, D->lp_filter_size); + process_filtered_sample(chan, fsam, D); + } + } + } +} + + +static void process_filtered_sample(int chan, float fsam, struct demodulator_state_s *D) +{ + + int subchan = 0; + + /* + * Version 1.2: Capture the post-filtering amplitude for display. + * This is similar to the AGC without the normalization step. + * We want decay to be substantially slower to get a longer + * range idea of the received audio. + * For AFSK, we keep mark and space amplitudes. + * Here we keep + and - peaks because there could be a DC bias. + */ + + // TODO: probably no need for this. Just use D->m_peak, D->m_valley + + if (fsam >= D->alevel_mark_peak) { + D->alevel_mark_peak = fsam * D->quick_attack + D->alevel_mark_peak * (1.0f - D->quick_attack); + } + else { + D->alevel_mark_peak = fsam * D->sluggish_decay + D->alevel_mark_peak * (1.0f - D->sluggish_decay); + } + + if (fsam <= D->alevel_space_peak) { + D->alevel_space_peak = fsam * D->quick_attack + D->alevel_space_peak * (1.0f - D->quick_attack); + } + else { + D->alevel_space_peak = fsam * D->sluggish_decay + D->alevel_space_peak * (1.0f - D->sluggish_decay); + } + + /* + * The input level can vary greatly. + * More importantly, there could be a DC bias which we need to remove. + * + * Normalize the signal with automatic gain control (AGC). + * This works by looking at the minimum and maximum signal peaks + * and scaling the results to be roughly in the -1.0 to +1.0 range. + */ + float demod_out; + int demod_data; /* Still scrambled. */ + + demod_out = agc(fsam, D->agc_fast_attack, D->agc_slow_decay, &(D->m_peak), &(D->m_valley)); + + // TODO: There is potential for multiple decoders with one filter. + + //dw_printf ("peak=%.2f valley=%.2f fsam=%.2f norm=%.2f\n", D->m_peak, D->m_valley, fsam, norm); + + if (D->num_slicers <= 1) { + + /* Normal case of one demodulator to one HDLC decoder. */ + /* Demodulator output is difference between response from two filters. */ + /* AGC should generally keep this around -1 to +1 range. */ + + demod_data = demod_out > 0; + nudge_pll(chan, subchan, 0, demod_out, D); + } + else { + int slice; + + /* Multiple slicers each feeding its own HDLC decoder. */ + + for (slice = 0; slice < D->num_slicers; slice++) { + demod_data = demod_out - slice_point[slice] > 0; + nudge_pll(chan, subchan, slice, demod_out - slice_point[slice], D); + } + } + + // demod_data is used only for debug out. + // suppress compiler warning about it not being used. + (void)demod_data; + +#if DEBUG4 + + if (chan == 0) { + + if (1) { + //if (D->slicer[slice].data_detect) { + char fname[30]; + int slice = 0; + + if (demod_log_fp == NULL) { + log_file_seq++; + snprintf(fname, sizeof(fname), "demod/%04d.csv", log_file_seq); + //if (log_file_seq == 1) mkdir ("demod", 0777); + if (log_file_seq == 1) mkdir("demod"); + + demod_log_fp = fopen(fname, "w"); + text_color_set(DW_COLOR_DEBUG); + dw_printf("Starting demodulator log file %s\n", fname); + fprintf(demod_log_fp, "Audio, Filtered, Max, Min, Normalized, Sliced, Clock\n"); + } + + fprintf(demod_log_fp, "%.3f, %.3f, %.3f, %.3f, %.3f, %d, %.2f\n", + fsam + 6, + fsam + 4, + D->m_peak + 4, + D->m_valley + 4, + demod_out + 2, + demod_data + 2, + (D->slicer[slice].data_clock_pll & 0x80000000) ? .5 : .0); + + fflush(demod_log_fp); + } + else { + if (demod_log_fp != NULL) { + fclose(demod_log_fp); + demod_log_fp = NULL; + } + } + } +#endif + +} /* end demod_9600_process_sample */ + + +/*------------------------------------------------------------------- + * + * Name: nudge_pll + * + * Purpose: Update the PLL state for each audio sample. + * + * (2) Descramble it. + * (2) Recover clock and data. + * + * Inputs: chan - Audio channel. 0 for left, 1 for right. + * + * subchan - Which demodulator. We could have several running in parallel. + * + * slice - Determines which Slicing level & HDLC decoder to use. + * + * demod_out_f - Demodulator output, possibly shifted by slicing level + * It will be compared with 0.0 to bit binary value out. + * + * D - Demodulator state for this channel / subchannel. + * + * Returns: None + * + * Description: A PLL is used to sample near the centers of the data bits. + * + * D->data_clock_pll is a SIGNED 32 bit variable. + * When it overflows from a large positive value to a negative value, we + * sample a data bit from the demodulated signal. + * + * Ideally, the the demodulated signal transitions should be near + * zero we we sample mid way between the transitions. + * + * Nudge the PLL by removing some small fraction from the value of + * data_clock_pll, pushing it closer to zero. + * + * This adjustment will never change the sign so it won't cause + * any erratic data bit sampling. + * + * If we adjust it too quickly, the clock will have too much jitter. + * If we adjust it too slowly, it will take too long to lock on to a new signal. + * + * I don't think the optimal value will depend on the audio sample rate + * because this happens for each transition from the demodulator. + * + * Version 1.4: Previously, we would always pull the PLL phase toward 0 after + * after a zero crossing was detetected. This adds extra jitter, + * especially when the ratio of audio sample rate to baud is low. + * Now, we interpolate between the two samples to get an estimate + * on when the zero crossing happened. The PLL is pulled toward + * this point. + * + * Results??? TBD + * + * Version 1.6: New experiment where filter size to extract clock is not the same + * as filter to extract the data bit value. + * + *--------------------------------------------------------------------*/ + +inline static void nudge_pll(int chan, int subchan, int slice, float demod_out_f, struct demodulator_state_s *D) +{ + D->slicer[slice].prev_d_c_pll = D->slicer[slice].data_clock_pll; + + // Perform the add as unsigned to avoid signed overflow error. + D->slicer[slice].data_clock_pll = (signed)((unsigned)(D->slicer[slice].data_clock_pll) + (unsigned)(D->pll_step_per_sample)); + + if (D->slicer[slice].prev_d_c_pll > 1000000000 && D->slicer[slice].data_clock_pll < -1000000000) { + + /* Overflow. Was large positive, wrapped around, now large negative. */ + + hdlc_rec_bit(chan, subchan, slice, demod_out_f > 0, D->modem_type == MODEM_SCRAMBLE, D->slicer[slice].lfsr); + pll_dcd_each_symbol2(D, chan, subchan, slice); + } + + /* + * Zero crossing? + */ + if ((D->slicer[slice].prev_demod_out_f < 0 && demod_out_f > 0) || + (D->slicer[slice].prev_demod_out_f > 0 && demod_out_f < 0)) { + + // Note: Test for this demodulator, not overall for channel. + + pll_dcd_signal_transition2(D, slice, D->slicer[slice].data_clock_pll); + + float target = D->pll_step_per_sample * demod_out_f / (demod_out_f - D->slicer[slice].prev_demod_out_f); + + if (D->slicer[slice].data_detect) { + D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_locked_inertia + target * (1.0f - D->pll_locked_inertia)); + } + else { + D->slicer[slice].data_clock_pll = (int)(D->slicer[slice].data_clock_pll * D->pll_searching_inertia + target * (1.0f - D->pll_searching_inertia)); + } + } + + +#if DEBUG5 + + //if (chan == 0) { + if (D->slicer[slice].data_detect) { + + char fname[30]; + + + if (demod_log_fp == NULL) { + seq++; + snprintf(fname, sizeof(fname), "demod96/%04d.csv", seq); + if (seq == 1) mkdir("demod96" +#ifndef __WIN32__ + , 0777 +#endif + ); + + demod_log_fp = fopen(fname, "w"); + text_color_set(DW_COLOR_DEBUG); + dw_printf("Starting 9600 decoder log file %s\n", fname); + fprintf(demod_log_fp, "Audio, Peak, Valley, Demod, SData, Descram, Clock\n"); + } + fprintf(demod_log_fp, "%.3f, %.3f, %.3f, %.3f, %.2f, %.2f, %.2f\n", + 0.5f * fsam + 3.5, + 0.5f * D->m_peak + 3.5, + 0.5f * D->m_valley + 3.5, + 0.5f * demod_out + 2.0, + demod_data ? 1.35 : 1.0, + descram ? .9 : .55, + (D->data_clock_pll & 0x80000000) ? .1 : .45); + } + else { + if (demod_log_fp != NULL) { + fclose(demod_log_fp); + demod_log_fp = NULL; + } + } + //} + +#endif + + +/* + * Remember demodulator output (pre-descrambling) so we can compare next time + * for the DPLL sync. + */ + D->slicer[slice].prev_demod_out_f = demod_out_f; + +} /* end nudge_pll */ + + +/* end demod_9600.c */ + + + + +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see . +// + + +/******************************************************************************** + * + * File: rrbb.c + * + * Purpose: Raw Received Bit Buffer. + * An array of bits used to hold data out of + * the demodulator before feeding it into the HLDC decoding. + * + * Version 1.2: Save initial state of 9600 baud descrambler so we can + * attempt bit fix up on G3RUH/K9NG scrambled data. + * + * Version 1.3: Store as bytes rather than packing 8 bits per byte. + * + *******************************************************************************/ + + + + + +#define MAGIC1 0x12344321 +#define MAGIC2 0x56788765 + + +static int new_count = 0; +static int delete_count = 0; + + +/*********************************************************************************** + * + * Name: rrbb_new + * + * Purpose: Allocate space for an array of samples. + * + * Inputs: chan - Radio channel from whence it came. + * + * subchan - Which demodulator of the channel. + * + * slice - multiple thresholds per demodulator. + * + * is_scrambled - Is data scrambled? (true, false) + * + * descram_state - State of data descrambler. + * + * prev_descram - Previous descrambled bit. + * + * Returns: Handle to be used by other functions. + * + * Description: + * + ***********************************************************************************/ + +rrbb_t rrbb_new(int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram) +{ + rrbb_t result; + + result = malloc(sizeof(struct rrbb_s)); + if (result == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf("FATAL ERROR: Out of memory.\n"); + exit(0); + } + result->magic1 = MAGIC1; + result->chan = chan; + result->subchan = subchan; + result->slice = slice; + result->magic2 = MAGIC2; + + new_count++; + + if (new_count > delete_count + 100) { + text_color_set(DW_COLOR_ERROR); + dw_printf("MEMORY LEAK, rrbb_new, new_count=%d, delete_count=%d\n", new_count, delete_count); + } + + rrbb_clear(result, is_scrambled, descram_state, prev_descram); + + return (result); +} + +/*********************************************************************************** + * + * Name: rrbb_clear + * + * Purpose: Clear by setting length to zero, etc. + * + * Inputs: b -Handle for sample array. + * + * is_scrambled - Is data scrambled? (true, false) + * + * descram_state - State of data descrambler. + * + * prev_descram - Previous descrambled bit. + * + ***********************************************************************************/ + +void rrbb_clear(rrbb_t b, int is_scrambled, int descram_state, int prev_descram) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + assert(is_scrambled == 0 || is_scrambled == 1); + assert(prev_descram == 0 || prev_descram == 1); + + b->nextp = NULL; + + b->alevel.rec = 9999; // TODO: was there some reason for this instead of 0 or -1? + b->alevel.mark = 9999; + b->alevel.space = 9999; + + b->len = 0; + + b->is_scrambled = is_scrambled; + b->descram_state = descram_state; + b->prev_descram = prev_descram; +} + + +/*********************************************************************************** + * + * Name: rrbb_append_bit + * + * Purpose: Append another bit to the end. + * + * Inputs: Handle for sample array. + * Value for the sample. + * + ***********************************************************************************/ + + /* Definition in header file so it can be inlined. */ + + + /*********************************************************************************** + * + * Name: rrbb_chop8 + * + * Purpose: Remove 8 from the length. + * + * Inputs: Handle for bit array. + * + * Description: Back up after appending the flag sequence. + * + ***********************************************************************************/ + +void rrbb_chop8(rrbb_t b) +{ + + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + if (b->len >= 8) { + b->len -= 8; + } +} + +/*********************************************************************************** + * + * Name: rrbb_get_len + * + * Purpose: Get number of bits in the array. + * + * Inputs: Handle for bit array. + * + ***********************************************************************************/ + +int rrbb_get_len(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + return (b->len); +} + + + +/*********************************************************************************** + * + * Name: rrbb_get_bit + * + * Purpose: Get value of bit in specified position. + * + * Inputs: Handle for sample array. + * Index into array. + * + ***********************************************************************************/ + + /* Definition in header file so it can be inlined. */ + + + + + /*********************************************************************************** + * + * Name: rrbb_flip_bit + * + * Purpose: Complement the value of bit in specified position. + * + * Inputs: Handle for bit array. + * Index into array. + * + ***********************************************************************************/ + + //void rrbb_flip_bit (rrbb_t b, unsigned int ind) + //{ + // unsigned int di, mi; + // + // assert (b != NULL); + // assert (b->magic1 == MAGIC1); + // assert (b->magic2 == MAGIC2); + // + // assert (ind < b->len); + // + // di = ind / SOI; + // mi = ind % SOI; + // + // b->data[di] ^= masks[mi]; + //} + + /*********************************************************************************** + * + * Name: rrbb_delete + * + * Purpose: Free the storage associated with the bit array. + * + * Inputs: Handle for bit array. + * + ***********************************************************************************/ + +void rrbb_delete(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + b->magic1 = 0; + b->magic2 = 0; + + free(b); + + delete_count++; +} + + +/*********************************************************************************** + * + * Name: rrbb_set_netxp + * + * Purpose: Set the nextp field, used to maintain a queue. + * + * Inputs: b Handle for bit array. + * np New value for nextp. + * + ***********************************************************************************/ + +void rrbb_set_nextp(rrbb_t b, rrbb_t np) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + b->nextp = np; +} + + +/*********************************************************************************** + * + * Name: rrbb_get_netxp + * + * Purpose: Get value of nextp field. + * + * Inputs: b Handle for bit array. + * + ***********************************************************************************/ + +rrbb_t rrbb_get_nextp(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + return (b->nextp); +} + +/*********************************************************************************** + * + * Name: rrbb_get_chan + * + * Purpose: Get channel from which bit buffer was received. + * + * Inputs: b Handle for bit array. + * + ***********************************************************************************/ + +int rrbb_get_chan(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + assert(b->chan >= 0 && b->chan < MAX_CHANS); + + return (b->chan); +} + + +/*********************************************************************************** + * + * Name: rrbb_get_subchan + * + * Purpose: Get subchannel from which bit buffer was received. + * + * Inputs: b Handle for bit array. + * + ***********************************************************************************/ + +int rrbb_get_subchan(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + assert(b->subchan >= 0 && b->subchan < MAX_SUBCHANS); + + return (b->subchan); +} + + +/*********************************************************************************** + * + * Name: rrbb_get_slice + * + * Purpose: Get slice number from which bit buffer was received. + * + * Inputs: b Handle for bit array. + * + ***********************************************************************************/ + +int rrbb_get_slice(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + assert(b->slice >= 0 && b->slice < MAX_SLICERS); + + return (b->slice); +} + + +/*********************************************************************************** + * + * Name: rrbb_set_audio_level + * + * Purpose: Set audio level at time the frame was received. + * + * Inputs: b Handle for bit array. + * alevel Audio level. + * + ***********************************************************************************/ + +void rrbb_set_audio_level(rrbb_t b, alevel_t alevel) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + b->alevel = alevel; +} + + +/*********************************************************************************** + * + * Name: rrbb_get_audio_level + * + * Purpose: Get audio level at time the frame was received. + * + * Inputs: b Handle for bit array. + * + ***********************************************************************************/ + +alevel_t rrbb_get_audio_level(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + return (b->alevel); +} + + + +/*********************************************************************************** + * + * Name: rrbb_get_is_scrambled + * + * Purpose: Find out if using scrambled data. + * + * Inputs: b Handle for bit array. + * + * Returns: True (for 9600 baud) or false (for slower AFSK). + * + ***********************************************************************************/ + +int rrbb_get_is_scrambled(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + return (b->is_scrambled); +} + + + +/*********************************************************************************** + * + * Name: rrbb_get_descram_state + * + * Purpose: Get data descrambler state before first data bit of frame. + * + * Inputs: b Handle for bit array. + * + ***********************************************************************************/ + +int rrbb_get_descram_state(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + return (b->descram_state); +} + + +/*********************************************************************************** + * + * Name: rrbb_get_prev_descram + * + * Purpose: Get previous descrambled bit before first data bit of frame. + * + * Inputs: b Handle for bit array. + * + ***********************************************************************************/ + +int rrbb_get_prev_descram(rrbb_t b) +{ + assert(b != NULL); + assert(b->magic1 == MAGIC1); + assert(b->magic2 == MAGIC2); + + return (b->prev_descram); +} + + +/* end rrbb.c */ + + + +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2014, 2015, 2016, 2019 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see . +// + + +/*------------------------------------------------------------------ + * + * Module: gen_tone.c + * + * Purpose: Convert bits to AFSK for writing to .WAV sound file + * or a sound device. + * + * + *---------------------------------------------------------------*/ + +// Properties of the digitized sound stream & modem. + +static struct audio_s *save_audio_config_p = NULL; + +/* + * 8 bit samples are unsigned bytes in range of 0 .. 255. + * + * 16 bit samples are signed short in range of -32768 .. +32767. + */ + + + /* Constants after initialization. */ + +#define TICKS_PER_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 ) + +static int ticks_per_sample[MAX_CHANS]; /* Same for both channels of same soundcard */ + /* because they have same sample rate */ + /* but less confusing to have for each channel. */ + +static int ticks_per_bit[MAX_CHANS]; +static int f1_change_per_sample[MAX_CHANS]; +static int f2_change_per_sample[MAX_CHANS]; + + +static short sine_table[256]; + + +/* Accumulators. */ + +static unsigned int tone_phase[MAX_CHANS]; // Phase accumulator for tone generation. + // Upper bits are used as index into sine table. + +#define PHASE_SHIFT_180 ( 128u << 24 ) +#define PHASE_SHIFT_90 ( 64u << 24 ) +#define PHASE_SHIFT_45 ( 32u << 24 ) + + +static int bit_len_acc[MAX_CHANS]; // To accumulate fractional samples per bit. + +static int lfsr[MAX_CHANS]; // Shift register for scrambler. + +static int bit_count[MAX_CHANS]; // Counter incremented for each bit transmitted + // on the channel. This is only used for QPSK. + // The LSB determines if we save the bit until + // next time, or send this one with the previously saved. + // The LSB+1 position determines if we add an + // extra 180 degrees to the phase to compensate + // for having 1.5 carrier cycles per symbol time. + + // For 8PSK, it has a different meaning. It is the + // number of bits in 'save_bit' so we can accumulate + // three for each symbol. +static int save_bit[MAX_CHANS]; + + +static int prev_dat[MAX_CHANS]; // Previous data bit. Used for G3RUH style. + + + + +/*------------------------------------------------------------------ + * + * Name: gen_tone_init + * + * Purpose: Initialize for AFSK tone generation which might + * be used for RTTY or amateur packet radio. + * + * Inputs: audio_config_p - Pointer to modem parameter structure, modem_s. + * + * The fields we care about are: + * + * samples_per_sec + * baud + * mark_freq + * space_freq + * samples_per_sec + * + * amp - Signal amplitude on scale of 0 .. 100. + * + * 100% uses the full 16 bit sample range of +-32k. + * + * gen_packets - True if being called from "gen_packets" utility + * rather than the "direwolf" application. + * + * Returns: 0 for success. + * -1 for failure. + * + * Description: Calculate various constants for use by the direct digital synthesis + * audio tone generation. + * + *----------------------------------------------------------------*/ + +static int amp16bit; /* for 9600 baud */ + + +int gen_tone_init(struct audio_s *pa, int amp, int gen_packets) +{ + int j; + int chan = 0; + +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + dw_printf("gen_tone_init ( audio_config_p=%p, amp=%d, gen_packets=%d )\n", + audio_config_p, amp, gen_packets); +#endif + + /* + * Save away modem parameters for later use. + */ + + // This should be in config somewhere + + pa->adev[0].num_channels = DEFAULT_NUM_CHANNELS; + pa->adev[0].samples_per_sec = 48000; + pa->adev[0].bits_per_sample = 16; + + pa->achan[0].baud = rx_baudrate[0]; + + pa->adev[1].num_channels = DEFAULT_NUM_CHANNELS; + pa->adev[1].samples_per_sec = 48000; + pa->adev[1].bits_per_sample = 16; + + pa->achan[1].baud = rx_baudrate[1]; + + pa->chan_medium[0] = MEDIUM_RADIO; + pa->chan_medium[1] = MEDIUM_RADIO; + + pa->achan[0].modem_type = MODEM_SCRAMBLE; + pa->achan[1].modem_type = MODEM_SCRAMBLE; + + save_audio_config_p = pa; + + amp16bit = (int)((32767 * amp) / 100); + + for (chan = 0; chan < MAX_CHANS; chan++) { + + if (pa->chan_medium[chan] == MEDIUM_RADIO) { + + int a = ACHAN2ADEV(chan); + +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + dw_printf("gen_tone_init: chan=%d, modem_type=%d, bps=%d, samples_per_sec=%d\n", + chan, + save_pa->achan[chan].modem_type, + pa->achan[chan].baud, + pa->adev[a].samples_per_sec); +#endif + + tone_phase[chan] = 0; + bit_len_acc[chan] = 0; + lfsr[chan] = 0; + + ticks_per_sample[chan] = (int)((TICKS_PER_CYCLE / (double)pa->adev[a].samples_per_sec) + 0.5); + + // The terminology is all wrong here. Didn't matter with 1200 and 9600. + // The config speed should be bits per second rather than baud. + // ticks_per_bit should be ticks_per_symbol. + + switch (pa->achan[chan].modem_type) { + + case MODEM_QPSK: + + pa->achan[chan].mark_freq = 1800; + pa->achan[chan].space_freq = pa->achan[chan].mark_freq; // Not Used. + + // symbol time is 1 / (half of bps) + ticks_per_bit[chan] = (int)((TICKS_PER_CYCLE / ((double)pa->achan[chan].baud * 0.5)) + 0.5); + f1_change_per_sample[chan] = (int)(((double)pa->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)pa->adev[a].samples_per_sec) + 0.5); + f2_change_per_sample[chan] = f1_change_per_sample[chan]; // Not used. + + tone_phase[chan] = PHASE_SHIFT_45; // Just to mimic first attempt. + break; + + case MODEM_8PSK: + + pa->achan[chan].mark_freq = 1800; + pa->achan[chan].space_freq = pa->achan[chan].mark_freq; // Not Used. + + // symbol time is 1 / (third of bps) + ticks_per_bit[chan] = (int)((TICKS_PER_CYCLE / ((double)pa->achan[chan].baud / 3.)) + 0.5); + f1_change_per_sample[chan] = (int)(((double)pa->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)pa->adev[a].samples_per_sec) + 0.5); + f2_change_per_sample[chan] = f1_change_per_sample[chan]; // Not used. + break; + + case MODEM_BASEBAND: + case MODEM_SCRAMBLE: + case MODEM_AIS: + + // Tone is half baud. + ticks_per_bit[chan] = (int)((TICKS_PER_CYCLE / (double)pa->achan[chan].baud) + 0.5); + f1_change_per_sample[chan] = (int)(((double)pa->achan[chan].baud * 0.5 * TICKS_PER_CYCLE / (double)pa->adev[a].samples_per_sec) + 0.5); + break; + + default: // AFSK + + ticks_per_bit[chan] = (int)((TICKS_PER_CYCLE / (double)pa->achan[chan].baud) + 0.5); + f1_change_per_sample[chan] = (int)(((double)pa->achan[chan].mark_freq * TICKS_PER_CYCLE / (double)pa->adev[a].samples_per_sec) + 0.5); + f2_change_per_sample[chan] = (int)(((double)pa->achan[chan].space_freq * TICKS_PER_CYCLE / (double)pa->adev[a].samples_per_sec) + 0.5); + break; + } + } + } + + for (j = 0; j < 256; j++) { + double a; + int s; + + a = ((double)(j) / 256.0) * (2 * M_PI); + s = (int)(sin(a) * 32767 * amp / 100.0); + + /* 16 bit sound sample must fit in range of -32768 .. +32767. */ + + if (s < -32768) { + text_color_set(DW_COLOR_ERROR); + dw_printf("gen_tone_init: Excessive amplitude is being clipped.\n"); + s = -32768; + } + else if (s > 32767) { + text_color_set(DW_COLOR_ERROR); + dw_printf("gen_tone_init: Excessive amplitude is being clipped.\n"); + s = 32767; + } + sine_table[j] = s; + } + + return (0); + +} /* end gen_tone_init */ + + +/*------------------------------------------------------------------- + * + * Name: tone_gen_put_bit + * + * Purpose: Generate tone of proper duration for one data bit. + * + * Inputs: chan - Audio channel, 0 = first. + * + * dat - 0 for f1, 1 for f2. + * + * -1 inserts half bit to test data + * recovery PLL. + * + * Assumption: fp is open to a file for write. + * + * Version 1.4: Attempt to implement 2400 and 4800 bps PSK modes. + * + * Version 1.6: For G3RUH, rather than generating square wave and low + * pass filtering, generate the waveform directly. + * This avoids overshoot, ringing, and adding more jitter. + * Alternating bits come out has sine wave of baud/2 Hz. + * + * Version 1.6: MFJ-2400 compatibility for V.26. + * + *--------------------------------------------------------------------*/ + +static const int gray2phase_v26[4] = { 0, 1, 3, 2 }; +static const int gray2phase_v27[8] = { 1, 0, 2, 3, 6, 7, 5, 4 }; + +// We are only using this for RUH modes + +void tone_gen_put_bit(int chan, int dat) +{ + int modem_type = MODEM_SCRAMBLE; + int a = 0; + + // scramble + + int x; + + x = (dat ^ (lfsr[chan] >> 16) ^ (lfsr[chan] >> 11)) & 1; + lfsr[chan] = (lfsr[chan] << 1) | (x & 1); + dat = x; + + do { /* until enough audio samples for this symbol. */ + + int sam; + + switch (modem_type) + { + + case MODEM_AFSK: + + // v1.7 reversed. + // Previously a data '1' selected the second (usually higher) tone. + // It never really mattered before because we were using NRZI. + // With the addition of IL2P, we need to be more careful. + // A data '1' should be the mark tone. + + tone_phase[chan] += dat ? f1_change_per_sample[chan] : f2_change_per_sample[chan]; + sam = sine_table[(tone_phase[chan] >> 24) & 0xff]; + gen_tone_put_sample(chan, a, sam); + break; + + case MODEM_QPSK: + case MODEM_8PSK: + + tone_phase[chan] += f1_change_per_sample[chan]; + sam = sine_table[(tone_phase[chan] >> 24) & 0xff]; + gen_tone_put_sample(chan, a, sam); + break; + + case MODEM_BASEBAND: + case MODEM_SCRAMBLE: + case MODEM_AIS: + + if (dat != prev_dat[chan]) + { + tone_phase[chan] += f1_change_per_sample[chan]; + } + else + { + if (tone_phase[chan] & 0x80000000) + tone_phase[chan] = 0xc0000000; // 270 degrees. + else + tone_phase[chan] = 0x40000000; // 90 degrees. + } + + sam = sine_table[(tone_phase[chan] >> 24) & 0xff]; + gen_tone_put_sample(chan, a, sam); + break; + + default: + text_color_set(DW_COLOR_ERROR); + dw_printf("INTERNAL ERROR: %s %d achan[%d].modem_type = %d\n", + __FILE__, __LINE__, chan, save_audio_config_p->achan[chan].modem_type); + exit(0); + } + + /* Enough for the bit time? */ + + bit_len_acc[chan] += ticks_per_sample[chan]; + + } while (bit_len_acc[chan] < ticks_per_bit[chan]); + + bit_len_acc[chan] -= ticks_per_bit[chan]; + + prev_dat[chan] = dat; // Only needed for G3RUH baseband/scrambled. + +} /* end tone_gen_put_bit */ + +#define ARDOPBufferSize 12000 * 100 // May need to be bigger for 48K + +extern short ARDOPTXBuffer[4][ARDOPBufferSize]; // Enough to hold whole frame of samples + +void gen_tone_put_sample(int chan, int a, int sam) +{ + // This replaces the DW code + + ARDOPTXBuffer[chan][SampleNo++] = sam; +} + + + + +/* end gen_tone.c */ + + + +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2013, 2014, 2019, 2021 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see . +// + +static void send_byte_msb_first(int chan, int x, int polarity); + +static void send_control_nrzi(int, int); +static void send_data_nrzi(int, int); +static void send_bit_nrzi(int, int); + + + +static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "hdlc_send_frame" or "hdlc_send_flags" + + + +/*------------------------------------------------------------- + * + * Name: layer2_send_frame + * + * Purpose: Convert frames to a stream of bits. + * Originally this was for AX.25 only, hence the file name. + * Over time, FX.25 and IL2P were shoehorned in. + * + * Inputs: chan - Audio channel number, 0 = first. + * + * pp - Packet object. + * + * bad_fcs - Append an invalid FCS for testing purposes. + * Applies only to regular AX.25. + * + * Outputs: Bits are shipped out by calling tone_gen_put_bit(). + * + * Returns: Number of bits sent including "flags" and the + * stuffing bits. + * The required time can be calculated by dividing this + * number by the transmit rate of bits/sec. + * + * Description: For AX.25, send: + * start flag + * bit stuffed data + * calculated FCS + * end flag + * NRZI encoding for all but the "flags." + * + * + * Assumptions: It is assumed that the tone_gen module has been + * properly initialized so that bits sent with + * tone_gen_put_bit() are processed correctly. + * + *--------------------------------------------------------------*/ + +static int ax25_only_hdlc_send_frame(int chan, unsigned char *fbuf, int flen, int bad_fcs); + + +int layer2_send_frame(int chan, packet_t pp, int bad_fcs, struct audio_s *audio_config_p) +{ + if (audio_config_p->achan[chan].layer2_xmit == LAYER2_IL2P) { + + int n = il2p_send_frame(chan, pp, audio_config_p->achan[chan].il2p_max_fec, + audio_config_p->achan[chan].il2p_invert_polarity); + if (n > 0) { + return (n); + } + text_color_set(DW_COLOR_ERROR); + dw_printf("Unable to send IL2p frame. Falling back to regular AX.25.\n"); + // Not sure if we should fall back to AX.25 or not here. + } + else if (audio_config_p->achan[chan].layer2_xmit == LAYER2_FX25) + { + unsigned char fbuf[AX25_MAX_PACKET_LEN + 2]; + int flen = ax25_pack(pp, fbuf); + int n = fx25_send_frame(chan, fbuf, flen, audio_config_p->achan[chan].fx25_strength); + if (n > 0) { + return (n); + } + text_color_set(DW_COLOR_ERROR); + dw_printf("Unable to send FX.25. Falling back to regular AX.25.\n"); + // Definitely need to fall back to AX.25 here because + // the FX.25 frame length is so limited. + } + + unsigned char fbuf[AX25_MAX_PACKET_LEN + 2]; + int flen = ax25_pack(pp, fbuf); + return (ax25_only_hdlc_send_frame(chan, fbuf, flen, bad_fcs)); +} + + + +static int ax25_only_hdlc_send_frame(int chan, unsigned char *fbuf, int flen, int bad_fcs) +{ + int j, fcs; + + + number_of_bits_sent[chan] = 0; + +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + dw_printf("hdlc_send_frame ( chan = %d, fbuf = %p, flen = %d, bad_fcs = %d)\n", chan, fbuf, flen, bad_fcs); + fflush(stdout); +#endif + + send_control_nrzi(chan, 0x7e); /* Start frame */ + + for (j = 0; j < flen; j++) { + send_data_nrzi(chan, fbuf[j]); + } + + fcs = get_fcs(fbuf, flen); + + if (bad_fcs) { + /* For testing only - Simulate a frame getting corrupted along the way. */ + send_data_nrzi(chan, (~fcs) & 0xff); + send_data_nrzi(chan, ((~fcs) >> 8) & 0xff); + } + else { + send_data_nrzi(chan, fcs & 0xff); + send_data_nrzi(chan, (fcs >> 8) & 0xff); + } + + send_control_nrzi(chan, 0x7e); /* End frame */ + + return (number_of_bits_sent[chan]); +} + + + +// The next one is only for IL2P. No NRZI. +// MSB first, opposite of AX.25. + +static void send_byte_msb_first(int chan, int x, int polarity) +{ + int i; + + for (i = 0; i < 8; i++) { + int dbit = (x & 0x80) != 0; + tone_gen_put_bit(chan, (dbit ^ polarity) & 1); + x <<= 1; + number_of_bits_sent[chan]++; + } +} + + +// The following are only for HDLC. +// All bits are sent NRZI. +// Data (non flags) use bit stuffing. + + +static int stuff[MAX_CHANS]; // Count number of "1" bits to keep track of when we + // need to break up a long run by "bit stuffing." + // Needs to be array because we could be transmitting + // on multiple channels at the same time. + +static void send_control_nrzi(int chan, int x) +{ + int i; + + for (i = 0; i < 8; i++) { + send_bit_nrzi(chan, x & 1); + x >>= 1; + } + + stuff[chan] = 0; +} + +static void send_data_nrzi(int chan, int x) +{ + int i; + + for (i = 0; i < 8; i++) { + send_bit_nrzi(chan, x & 1); + if (x & 1) { + stuff[chan]++; + if (stuff[chan] == 5) { + send_bit_nrzi(chan, 0); + stuff[chan] = 0; + } + } + else { + stuff[chan] = 0; + } + x >>= 1; + } +} + +/* + * NRZI encoding. + * data 1 bit -> no change. + * data 0 bit -> invert signal. + */ + +static void send_bit_nrzi(int chan, int b) +{ + static int output[MAX_CHANS]; + + if (b == 0) { + output[chan] = !output[chan]; + } + + tone_gen_put_bit(chan, output[chan]); + + number_of_bits_sent[chan]++; +} + +/* end hdlc_send.c */ + + + +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2015, 2019 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see . +// + + +/*------------------------------------------------------------------ + * + * Name: dsp.c + * + * Purpose: Generate the filters used by the demodulators. + * + *----------------------------------------------------------------*/ + + +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) + + + // Don't remove this. It serves as a reminder that an experiment is underway. + +#if defined(TUNE_MS_FILTER_SIZE) || defined(TUNE_MS2_FILTER_SIZE) || defined(TUNE_AGC_FAST) || defined(TUNE_LPF_BAUD) || defined(TUNE_PLL_LOCKED) || defined(TUNE_PROFILE) +#define DEBUG1 1 // Don't remove this. +#endif + + +/*------------------------------------------------------------------ + * + * Name: window + * + * Purpose: Filter window shape functions. + * + * Inputs: type - BP_WINDOW_HAMMING, etc. + * size - Number of filter taps. + * j - Index in range of 0 to size-1. + * + * Returns: Multiplier for the window shape. + * + *----------------------------------------------------------------*/ + + static float window(bp_window_t type, int size, int j) +{ + float center; + float w; + + center = 0.5 * (size - 1); + + switch (type) { + + case BP_WINDOW_COSINE: + w = cos((j - center) / size * M_PI); + //w = sin(j * M_PI / (size - 1)); + break; + + case BP_WINDOW_HAMMING: + w = 0.53836 - 0.46164 * cos((j * 2 * M_PI) / (size - 1)); + break; + + case BP_WINDOW_BLACKMAN: + w = 0.42659 - 0.49656 * cos((j * 2 * M_PI) / (size - 1)) + + 0.076849 * cos((j * 4 * M_PI) / (size - 1)); + break; + + case BP_WINDOW_FLATTOP: + w = 1.0 - 1.93 * cos((j * 2 * M_PI) / (size - 1)) + + 1.29 * cos((j * 4 * M_PI) / (size - 1)) + - 0.388 * cos((j * 6 * M_PI) / (size - 1)) + + 0.028 * cos((j * 8 * M_PI) / (size - 1)); + break; + + case BP_WINDOW_TRUNCATED: + default: + w = 1.0; + break; + } + return (w); +} + + +/*------------------------------------------------------------------ + * + * Name: gen_lowpass + * + * Purpose: Generate low pass filter kernel. + * + * Inputs: fc - Cutoff frequency as fraction of sampling frequency. + * filter_size - Number of filter taps. + * wtype - Window type, BP_WINDOW_HAMMING, etc. + * lp_delay_fract - Fudge factor for the delay value. + * + * Outputs: lp_filter + * + * Returns: Signal delay thru the filter in number of audio samples. + * + *----------------------------------------------------------------*/ + + +void gen_lowpass(float fc, float *lp_filter, int filter_size, bp_window_t wtype) +{ + int j; + float G; + + +#if DEBUG1 + text_color_set(DW_COLOR_DEBUG); + + dw_printf("Lowpass, size=%d, fc=%.2f\n", filter_size, fc); + dw_printf(" j shape sinc final\n"); +#endif + + assert(filter_size >= 3 && filter_size <= MAX_FILTER_SIZE); + + for (j = 0; j < filter_size; j++) { + float center; + float sinc; + float shape; + + center = 0.5 * (filter_size - 1); + + if (j - center == 0) { + sinc = 2 * fc; + } + else { + sinc = sin(2 * M_PI * fc * (j - center)) / (M_PI*(j - center)); + } + + shape = window(wtype, filter_size, j); + lp_filter[j] = sinc * shape; + +#if DEBUG1 + dw_printf("%6d %6.2f %6.3f %6.3f\n", j, shape, sinc, lp_filter[j]); +#endif + } + + /* + * Normalize lowpass for unity gain at DC. + */ + G = 0; + for (j = 0; j < filter_size; j++) { + G += lp_filter[j]; + } + for (j = 0; j < filter_size; j++) { + lp_filter[j] = lp_filter[j] / G; + } + + return; + +} /* end gen_lowpass */ + + +#undef DEBUG1 + + + +/*------------------------------------------------------------------ + * + * Name: gen_bandpass + * + * Purpose: Generate band pass filter kernel for the prefilter. + * This is NOT for the mark/space filters. + * + * Inputs: f1 - Lower cutoff frequency as fraction of sampling frequency. + * f2 - Upper cutoff frequency... + * filter_size - Number of filter taps. + * wtype - Window type, BP_WINDOW_HAMMING, etc. + * + * Outputs: bp_filter + * + * Reference: http://www.labbookpages.co.uk/audio/firWindowing.html + * + * Does it need to be an odd length? + * + *----------------------------------------------------------------*/ + + +void gen_bandpass(float f1, float f2, float *bp_filter, int filter_size, bp_window_t wtype) +{ + int j; + float w; + float G; + float center = 0.5 * (filter_size - 1); + +#if DEBUG1 + text_color_set(DW_COLOR_DEBUG); + + dw_printf("Bandpass, size=%d\n", filter_size); + dw_printf(" j shape sinc final\n"); +#endif + + assert(filter_size >= 3 && filter_size <= MAX_FILTER_SIZE); + + for (j = 0; j < filter_size; j++) { + float sinc; + float shape; + + if (j - center == 0) { + sinc = 2 * (f2 - f1); + } + else { + sinc = sin(2 * M_PI * f2 * (j - center)) / (M_PI*(j - center)) + - sin(2 * M_PI * f1 * (j - center)) / (M_PI*(j - center)); + } + + shape = window(wtype, filter_size, j); + bp_filter[j] = sinc * shape; + +#if DEBUG1 + dw_printf("%6d %6.2f %6.3f %6.3f\n", j, shape, sinc, bp_filter[j]); +#endif + } + + + /* + * Normalize bandpass for unity gain in middle of passband. + * Can't use same technique as for lowpass. + * Instead compute gain in middle of passband. + * See http://dsp.stackexchange.com/questions/4693/fir-filter-gain + */ + w = 2 * M_PI * (f1 + f2) / 2; + G = 0; + for (j = 0; j < filter_size; j++) { + G += 2 * bp_filter[j] * cos((j - center)*w); // is this correct? + } + +#if DEBUG1 + dw_printf("Before normalizing, G=%.3f\n", G); +#endif + for (j = 0; j < filter_size; j++) { + bp_filter[j] = bp_filter[j] / G; + } + +} /* end gen_bandpass */ + + + +/*------------------------------------------------------------------ + * + * Name: gen_ms + * + * Purpose: Generate mark and space filters. + * + * Inputs: fc - Tone frequency, i.e. mark or space. + * sps - Samples per second. + * filter_size - Number of filter taps. + * wtype - Window type, BP_WINDOW_HAMMING, etc. + * + * Outputs: bp_filter + * + * Reference: http://www.labbookpages.co.uk/audio/firWindowing.html + * + * Does it need to be an odd length? + * + *----------------------------------------------------------------*/ + + +void gen_ms(int fc, int sps, float *sin_table, float *cos_table, int filter_size, int wtype) +{ + int j; + float Gs = 0, Gc = 0;; + + for (j = 0; j < filter_size; j++) { + + float center = 0.5f * (filter_size - 1); + float am = ((float)(j - center) / (float)sps) * ((float)fc) * (2.0f * (float)M_PI); + + float shape = window(wtype, filter_size, j); + + sin_table[j] = sinf(am) * shape; + cos_table[j] = cosf(am) * shape; + + Gs += sin_table[j] * sinf(am); + Gc += cos_table[j] * cosf(am); + +#if DEBUG1 + dw_printf("%6d %6.2f %6.2f %6.2f\n", j, shape, sin_table[j], cos_table[j]); +#endif + } + + + /* Normalize for unity gain */ + +#if DEBUG1 + dw_printf("Before normalizing, Gs = %.2f, Gc = %.2f\n", Gs, Gc); +#endif + for (j = 0; j < filter_size; j++) { + sin_table[j] = sin_table[j] / Gs; + cos_table[j] = cos_table[j] / Gc; + } + +} /* end gen_ms */ + + + + + +/*------------------------------------------------------------------ + * + * Name: rrc + * + * Purpose: Root Raised Cosine function. + * Why do they call it that? + * It's mostly the sinc function with cos windowing to taper off edges faster. + * + * Inputs: t - Time in units of symbol duration. + * i.e. The centers of two adjacent symbols would differ by 1. + * + * a - Roll off factor, between 0 and 1. + * + * Returns: Basically the sinc (sin(x)/x) function with edges decreasing faster. + * Should be 1 for t = 0 and 0 at all other integer values of t. + * + *----------------------------------------------------------------*/ + + +float rrc(float t, float a) +{ + float sinc, window, result; + + if (t > -0.001 && t < 0.001) { + sinc = 1; + } + else { + sinc = sinf(M_PI * t) / (M_PI * t); + } + + if (fabsf(a * t) > 0.499 && fabsf(a * t) < 0.501) { + window = M_PI / 4; + } + else { + window = cos(M_PI * a * t) / (1 - powf(2 * a * t, 2)); + // This made nicer looking waveforms for generating signal. + //window = cos(M_PI * a * t); + // Do we want to let it go negative? + // I think this would happen when a > 0.5 / (filter width in symbol times) + if (window < 0) { + //printf ("'a' is too large for range of 't'.\n"); + //window = 0; + } + } + + result = sinc * window; + +#if DEBUGRRC + // t should vary from - to + half of filter size in symbols. + // Result should be 1 at t=0 and 0 at all other integer values of t. + + printf("%.3f, %.3f, %.3f, %.3f\n", t, sinc, window, result); +#endif + return (result); +} + +// The Root Raised Cosine (RRC) low pass filter is suppposed to minimize Intersymbol Interference (ISI). + +void gen_rrc_lowpass(float *pfilter, int filter_taps, float rolloff, float samples_per_symbol) +{ + int k; + float t; + + for (k = 0; k < filter_taps; k++) { + t = (k - ((filter_taps - 1.0) / 2.0)) / samples_per_symbol; + pfilter[k] = rrc(t, rolloff); + } + + // Scale it for unity gain. + + t = 0; + for (k = 0; k < filter_taps; k++) { + t += pfilter[k]; + } + for (k = 0; k < filter_taps; k++) { + pfilter[k] = pfilter[k] / t; + } +} + +/* end dsp.c */ + + +int ax25_pack(packet_t this_p, unsigned char * result) +{ + int len = 0; + // assert(this_p->magic1 == MAGIC); + // assert(this_p->magic2 == MAGIC); + + // assert(this_p->frame_len >= 0 && this_p->frame_len <= AX25_MAX_PACKET_LEN); + + // memcpy(result, this_p->frame_data, this_p->frame_len); + + return (len); +} + + +int audio_flush(int a) +{ + return 0; +} + +int fx25_send_frame(int chan, unsigned char *fbuf, int flen, int fx_mode) +{ + return 0; +} + + +struct demodulator_state_s D[4]; +struct audio_s pa[4]; + +extern int was_init[4]; + +extern short rx_baudrate[5]; +extern unsigned char modem_mode[5]; + +void dw9600ProcessSample(int snd_ch, short Sample) +{ + demod_9600_process_sample(snd_ch, Sample, 1, &D[snd_ch]); +} + +void init_RUH48(int snd_ch) +{ + modem_mode[snd_ch] = MODE_RUH; + rx_baudrate[snd_ch] = 4800; + + if (was_init[snd_ch] == 0) + { + hdlc_rec_init(&pa[snd_ch]); + gen_tone_init(&pa[snd_ch], 100, 0); + was_init[snd_ch] = 1; + } + + demod_9600_init(2, + 48000, + 1, //upsample + 4800, + &D[snd_ch]); + +} + +void init_RUH96(int snd_ch) +{ + modem_mode[snd_ch] = MODE_RUH; + rx_baudrate[snd_ch] = 9600; + + if (was_init[snd_ch] == 0) + { + hdlc_rec_init(&pa[snd_ch]); + gen_tone_init(&pa[snd_ch], 100, 0); + was_init[snd_ch]; + } + + demod_9600_init(2, + 48000, + 1, //upsample + 9600, + &D[snd_ch]); + +} + + +void text_color_set(dw_color_t c) +{ + return; +} + + +extern TStringList detect_list[5]; +extern TStringList detect_list_c[5]; + + +int multi_modem_process_rec_frame(int chan, int subchan, int slice, unsigned char *fbuf, int flen, int alevel, int retries, int is_fx25) +{ + // Convert to QtSM internal format + +// struct TDetector_t * pDET = &DET[emph][subchan]; + string * data = newString(); + char Mode[16] = "RUH"; + int i, found; + + stringAdd(data, fbuf, flen + 2); // QTSM assumes a CRC + + if (detect_list[chan].Count > 0 && + my_indexof(&detect_list[chan], data) >= 0) + { + // Already have a copy of this frame + + freeString(data); + Debugprintf("Discarding copy rcvr %d emph %d", subchan, 0); + return; + } + + string * xx = newString(); + memset(xx->Data, 0, 16); + + Add(&detect_list_c[chan], xx); + Add(&detect_list[chan], data); + + // if (retries) + // sprintf(Mode, "IP2P-%d", retries); + + stringAdd(xx, Mode, strlen(Mode)); + return 0; + +} + +extern unsigned short * DMABuffer; + + +extern int Number; +extern int SampleNo; + +extern short txtail[5]; +extern short txdelay[5]; +extern short tx_baudrate[5]; + +extern int ARDOPTXLen[4]; // Length of frame +extern int ARDOPTXPtr[4]; // Tx Pointer + + +void RUHEncode(unsigned char * Data, int Len, int chan) +{ + // Generate audio samples from frame data + + int bitcount; + int txdcount; + int j; + unsigned short CRC; + + number_of_bits_sent[chan] = 0; + + SampleNo = 0; + + // First do TX delay + + // Set up txd worth of flags + + txdcount = (txdelay[chan] * tx_baudrate[chan]) / 8000; // 8 for bits, 1000 for mS + + if (txdcount > 1024) + txdcount = 1024; + + while (txdcount--) + send_control_nrzi(chan, 0x7e); // Flag + + for (j = 0; j < Len; j++) + { + send_data_nrzi(chan, Data[j]); + } + + CRC = get_fcs(Data, Len); + + send_data_nrzi(chan, (CRC & 0xff)); + send_data_nrzi(chan, (CRC >> 8)); + + // do we need tail here?? + + send_control_nrzi(chan, 0x7e); // Flag + + ARDOPTXLen[chan] = SampleNo; + ARDOPTXPtr[chan] = 0; + + // sampleNo should now contain number of (stereo) samples + +} + + + + + + + diff --git a/dw9600.h b/dw9600.h new file mode 100644 index 0000000..e80d2ac --- /dev/null +++ b/dw9600.h @@ -0,0 +1,2042 @@ +#pragma once + +// Includes code from Dire Wolf Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ +// +// This program 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 2 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see . +// + + + +// Dephi emulation functions + +string * Strings(TStringList * Q, int Index); +void Clear(TStringList * Q); +int Count(TStringList * List); + +string * newString(); +string * copy(string * Source, int StartChar, int Count); +TStringList * newTStringList(); + +void freeString(string * Msg); + +void initString(string * S); +void initTStringList(TStringList* T); + +// Two delete() This is confusing!! +// Not really - one acts on String, other TStringList + +void Delete(TStringList * Q, int Index); +void mydelete(string * Source, int StartChar, int Count); + +void move(unsigned char * SourcePointer, unsigned char * DestinationPointer, int CopyCount); +void fmove(float * SourcePointer, float * DestinationPointer, int CopyCount); + +void setlength(string * Msg, int Count); // Set string length + +string * stringAdd(string * Msg, unsigned char * Chars, int Count); // Extend string + +void Assign(TStringList * to, TStringList * from); // Duplicate from to to + +string * duplicateString(string * in); + +// This looks for a string in a stringlist. Returns inhex if found, otherwise -1 + +int my_indexof(TStringList * l, string * s); + +int Add(TStringList * Q, string * Entry); + + +#define MAX_FILTER_SIZE 480 /* 401 is needed for profile A, 300 baud & 44100. Revisit someday. */ +// Size comes out to 417 for 1200 bps with 48000 sample rate +// v1.7 - Was 404. Bump up to 480. + + +#define MAX_TOTAL_CHANS 16 +#define MAX_ADEVS 3 +#define AX25_MAX_ADDR_LEN 12 + +#include // for uint64_t + + + +#define AX25_MAX_REPEATERS 8 +#define AX25_MIN_ADDRS 2 /* Destination & Source. */ +#define AX25_MAX_ADDRS 10 /* Destination, Source, 8 digipeaters. */ + +#define AX25_DESTINATION 0 /* Address positions in frame. */ +#define AX25_SOURCE 1 +#define AX25_REPEATER_1 2 +#define AX25_REPEATER_2 3 +#define AX25_REPEATER_3 4 +#define AX25_REPEATER_4 5 +#define AX25_REPEATER_5 6 +#define AX25_REPEATER_6 7 +#define AX25_REPEATER_7 8 +#define AX25_REPEATER_8 9 + +#define AX25_MAX_ADDR_LEN 12 /* In theory, you would expect the maximum length */ + /* to be 6 letters, dash, 2 digits, and nul for a */ + /* total of 10. However, object labels can be 10 */ + /* characters so throw in a couple extra bytes */ + /* to be safe. */ + +#define AX25_MIN_INFO_LEN 0 /* Previously 1 when considering only APRS. */ + +#define AX25_MAX_INFO_LEN 2048 /* Maximum size for APRS. */ + /* AX.25 starts out with 256 as the default max */ + /* length but the end stations can negotiate */ + /* something different. */ + /* version 0.8: Change from 256 to 2028 to */ + /* handle the larger paclen for Linux AX25. */ + + /* These don't include the 2 bytes for the */ + /* HDLC frame FCS. */ + +/* + * Previously, for APRS only. + * #define AX25_MIN_PACKET_LEN ( 2 * 7 + 2 + AX25_MIN_INFO_LEN) + * #define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + AX25_MAX_INFO_LEN) + */ + + /* The more general case. */ + /* An AX.25 frame can have a control byte and no protocol. */ + +#define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 ) + +#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + 3 + AX25_MAX_INFO_LEN) + + +/* + * packet_t is a pointer to a packet object. + * + * The actual implementation is not visible outside ax25_pad.c. + */ + +#define AX25_UI_FRAME 3 /* Control field value. */ + +#define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */ +#define AX25_PID_SEGMENTATION_FRAGMENT 0x08 +#define AX25_PID_ESCAPE_CHARACTER 0xff + + +struct packet_s { + + int magic1; /* for error checking. */ + + int seq; /* unique sequence number for debugging. */ + + double release_time; /* Time stamp in format returned by dtime_now(). */ + /* When to release from the SATgate mode delay queue. */ + +#define MAGIC 0x41583235 + + struct packet_s *nextp; /* Pointer to next in queue. */ + + int num_addr; /* Number of addresses in frame. */ + /* Range of AX25_MIN_ADDRS .. AX25_MAX_ADDRS for AX.25. */ + /* It will be 0 if it doesn't look like AX.25. */ + /* -1 is used temporarily at allocation to mean */ + /* not determined yet. */ + + + + /* + * The 7th octet of each address contains: + * + * Bits: H R R SSID 0 + * + * H for digipeaters set to 0 initially. + * Changed to 1 when position has been used. + * + * for source & destination it is called + * command/response. Normally both 1 for APRS. + * They should be opposites for connected mode. + * + * R R Reserved. Normally set to 1 1. + * + * SSID Substation ID. Range of 0 - 15. + * + * 0 Usually 0 but 1 for last address. + */ + + +#define SSID_H_MASK 0x80 +#define SSID_H_SHIFT 7 + +#define SSID_RR_MASK 0x60 +#define SSID_RR_SHIFT 5 + +#define SSID_SSID_MASK 0x1e +#define SSID_SSID_SHIFT 1 + +#define SSID_LAST_MASK 0x01 + + + int frame_len; /* Frame length without CRC. */ + + int modulo; /* I & S frames have sequence numbers of either 3 bits (modulo 8) */ + /* or 7 bits (modulo 128). This is conveyed by either 1 or 2 */ + /* control bytes. Unfortunately, we can't determine this by looking */ + /* at an isolated frame. We need to know about the context. If we */ + /* are part of the conversation, we would know. But if we are */ + /* just listening to others, this would be more difficult to determine. */ + + /* For U frames: set to 0 - not applicable */ + /* For I & S frames: 8 or 128 if known. 0 if unknown. */ + + unsigned char frame_data[AX25_MAX_PACKET_LEN + 1]; + /* Raw frame contents, without the CRC. */ + + + int magic2; /* Will get stomped on if above overflows. */ +}; + + + + + +typedef struct packet_s *packet_t; + +typedef enum cmdres_e { cr_00 = 2, cr_cmd = 1, cr_res = 0, cr_11 = 3 } cmdres_t; + + +extern packet_t ax25_new(void); + + +/* + * APRS always has one control octet of 0x03 but the more + * general AX.25 case is one or two control bytes depending on + * whether "modulo 128 operation" is in effect. + */ + + //#define DEBUGX 1 + +static inline int ax25_get_control_offset(packet_t this_p) +{ + return (this_p->num_addr * 7); +} + +static inline int ax25_get_num_control(packet_t this_p) +{ + int c; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0) { /* I xxxx xxx0 */ +#if DEBUGX + dw_printf("ax25_get_num_control, %02x is I frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1); +#endif + return ((this_p->modulo == 128) ? 2 : 1); + } + + if ((c & 0x03) == 1) { /* S xxxx xx01 */ +#if DEBUGX + dw_printf("ax25_get_num_control, %02x is S frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1); +#endif + return ((this_p->modulo == 128) ? 2 : 1); + } + +#if DEBUGX + dw_printf("ax25_get_num_control, %02x is U frame, always returns 1.\n", c); +#endif + + return (1); /* U xxxx xx11 */ +} + + + +/* + * APRS always has one protocol octet of 0xF0 meaning no level 3 + * protocol but the more general case is 0, 1 or 2 protocol ID octets. + */ + +static inline int ax25_get_pid_offset(packet_t this_p) +{ + return (ax25_get_control_offset(this_p) + ax25_get_num_control(this_p)); +} + +static int ax25_get_num_pid(packet_t this_p) +{ + int c; + int pid; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0 || /* I xxxx xxx0 */ + c == 0x03 || c == 0x13) { /* UI 000x 0011 */ + + pid = this_p->frame_data[ax25_get_pid_offset(this_p)]; +#if DEBUGX + dw_printf("ax25_get_num_pid, %02x is I or UI frame, pid = %02x, returns %d\n", c, pid, (pid == AX25_PID_ESCAPE_CHARACTER) ? 2 : 1); +#endif + if (pid == AX25_PID_ESCAPE_CHARACTER) { + return (2); /* pid 1111 1111 means another follows. */ + } + return (1); + } +#if DEBUGX + dw_printf("ax25_get_num_pid, %02x is neither I nor UI frame, returns 0\n", c); +#endif + return (0); +} + + +/* + * AX.25 has info field for 5 frame types depending on the control field. + * + * xxxx xxx0 I + * 000x 0011 UI (which includes APRS) + * 101x 1111 XID + * 111x 0011 TEST + * 100x 0111 FRMR + * + * APRS always has an Information field with at least one octet for the Data Type Indicator. + */ + +static inline int ax25_get_info_offset(packet_t this_p) +{ + int offset = ax25_get_control_offset(this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p); +#if DEBUGX + dw_printf("ax25_get_info_offset, returns %d\n", offset); +#endif + return (offset); +} + +static inline int ax25_get_num_info(packet_t this_p) +{ + int len; + + /* assuming AX.25 frame. */ + + len = this_p->frame_len - this_p->num_addr * 7 - ax25_get_num_control(this_p) - ax25_get_num_pid(this_p); + if (len < 0) { + len = 0; /* print error? */ + } + + return (len); +} + + + + +typedef enum ax25_modulo_e { modulo_unknown = 0, modulo_8 = 8, modulo_128 = 128 } ax25_modulo_t; + +typedef enum ax25_frame_type_e { + + frame_type_I = 0, // Information + + frame_type_S_RR, // Receive Ready - System Ready To Receive + frame_type_S_RNR, // Receive Not Ready - TNC Buffer Full + frame_type_S_REJ, // Reject Frame - Out of Sequence or Duplicate + frame_type_S_SREJ, // Selective Reject - Request single frame repeat + + frame_type_U_SABME, // Set Async Balanced Mode, Extended + frame_type_U_SABM, // Set Async Balanced Mode + frame_type_U_DISC, // Disconnect + frame_type_U_DM, // Disconnect Mode + frame_type_U_UA, // Unnumbered Acknowledge + frame_type_U_FRMR, // Frame Reject + frame_type_U_UI, // Unnumbered Information + frame_type_U_XID, // Exchange Identification + frame_type_U_TEST, // Test + frame_type_U, // other Unnumbered, not used by AX.25. + + frame_not_AX25 // Could not get control byte from frame. + // This must be last because value plus 1 is + // for the size of an array. + +} ax25_frame_type_t; + + +/* + * Originally this was a single number. + * Let's try something new in version 1.2. + * Also collect AGC values from the mark and space filters. + */ + +typedef struct alevel_s { + + int rec; + int mark; + int space; + //float ms_ratio; // TODO: take out after temporary investigation. +} alevel_t; + + +#ifndef AXTEST +// TODO: remove this? +#define AX25MEMDEBUG 1 +#endif + + +#if AX25MEMDEBUG // to investigate a memory leak problem + + +extern void ax25memdebug_set(void); +extern int ax25memdebug_get(void); +extern int ax25memdebug_seq(packet_t this_p); + + +extern packet_t ax25_from_text_debug(char *monitor, int strict, char *src_file, int src_line); +#define ax25_from_text(m,s) ax25_from_text_debug(m,s,__FILE__,__LINE__) + +extern packet_t ax25_from_frame_debug(unsigned char *data, int len, alevel_t alevel, char *src_file, int src_line); +#define ax25_from_frame(d,l,a) ax25_from_frame_debug(d,l,a,__FILE__,__LINE__); + +extern packet_t ax25_dup_debug(packet_t copy_from, char *src_file, int src_line); +#define ax25_dup(p) ax25_dup_debug(p,__FILE__,__LINE__); + +extern void ax25_delete_debug(packet_t pp, char *src_file, int src_line); +#define ax25_delete(p) ax25_delete_debug(p,__FILE__,__LINE__); + +#else + +extern packet_t ax25_from_text(char *monitor, int strict); + +extern packet_t ax25_from_frame(unsigned char *data, int len, alevel_t alevel); + +extern packet_t ax25_dup(packet_t copy_from); + +extern void ax25_delete(packet_t pp); + +#endif + + + + +extern int ax25_parse_addr(int position, char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard); +extern int ax25_check_addresses(packet_t pp); + +extern packet_t ax25_unwrap_third_party(packet_t from_pp); + +extern void ax25_set_addr(packet_t pp, int, char *); +extern void ax25_insert_addr(packet_t this_p, int n, char *ad); +extern void ax25_remove_addr(packet_t this_p, int n); + +extern int ax25_get_num_addr(packet_t pp); +extern int ax25_get_num_repeaters(packet_t this_p); + +extern void ax25_get_addr_with_ssid(packet_t pp, int n, char *station); +extern void ax25_get_addr_no_ssid(packet_t pp, int n, char *station); + +extern int ax25_get_ssid(packet_t pp, int n); +extern void ax25_set_ssid(packet_t this_p, int n, int ssid); + +extern int ax25_get_h(packet_t pp, int n); + +extern void ax25_set_h(packet_t pp, int n); + +extern int ax25_get_heard(packet_t this_p); + +extern int ax25_get_first_not_repeated(packet_t pp); + +extern int ax25_get_rr(packet_t this_p, int n); + +extern int ax25_get_info(packet_t pp, unsigned char **paddr); +extern void ax25_set_info(packet_t pp, unsigned char *info_ptr, int info_len); +extern int ax25_cut_at_crlf(packet_t this_p); + +extern void ax25_set_nextp(packet_t this_p, packet_t next_p); + +extern int ax25_get_dti(packet_t this_p); + +extern packet_t ax25_get_nextp(packet_t this_p); + +extern void ax25_set_release_time(packet_t this_p, double release_time); +extern double ax25_get_release_time(packet_t this_p); + +extern void ax25_set_modulo(packet_t this_p, int modulo); +extern int ax25_get_modulo(packet_t this_p); + +extern void ax25_format_addrs(packet_t pp, char *); +extern void ax25_format_via_path(packet_t this_p, char *result, size_t result_size); + +extern int ax25_pack(packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]); + +extern ax25_frame_type_t ax25_frame_type(packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns); + +extern void ax25_hex_dump(packet_t this_p); + +extern int ax25_is_aprs(packet_t pp); +extern int ax25_is_null_frame(packet_t this_p); + +extern int ax25_get_control(packet_t this_p); +extern int ax25_get_c2(packet_t this_p); + +extern int ax25_get_pid(packet_t this_p); + +extern int ax25_get_frame_len(packet_t this_p); +extern unsigned char *ax25_get_frame_data_ptr(packet_t this_p); + +extern unsigned short ax25_dedupe_crc(packet_t pp); + +extern unsigned short ax25_m_m_crc(packet_t pp); + +extern void ax25_safe_print(char *, int, int ascii_only); + +#define AX25_ALEVEL_TO_TEXT_SIZE 40 // overkill but safe. +extern int ax25_alevel_to_text(alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE]); + + + +int demod_init(struct audio_s *pa); +int demod_get_sample(int a); +void demod_process_sample(int chan, int subchan, int sam); +void demod_print_agc(int chan, int subchan); +alevel_t demod_get_audio_level(int chan, int subchan); +void hdlc_rec_bit(int chan, int subchan, int slice, int raw, int is_scrambled, int descram_state); + +/* Provided elsewhere to process a complete frame. */ + +//void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level); + + +/* Is HLDC decoder is currently gathering bits into a frame? */ +/* Similar to, but not exactly the same as, data carrier detect. */ +/* We use this to influence the PLL inertia. */ + +int hdlc_rec_gathering(int chan, int subchan, int slice); + +/* Transmit needs to know when someone else is transmitting. */ + +void dcd_change(int chan, int subchan, int slice, int state); + +int hdlc_rec_data_detect_any(int chan); + + + +/* direwolf.h - Common stuff used many places. */ + +// TODO: include this file first before anything else in each .c file. + + +#ifdef NDEBUG +#undef NDEBUG // Because it would disable assert(). +#endif + +#define __restrict__ + +#define dw_printf Debugprintf + +#define M_PI 3.1415926f + +#define no_init_all deprecated + +#ifndef DIREWOLF_H +#define DIREWOLF_H 1 + +/* + * Support Windows XP and later. + * + * We need this before "#include ". + * + * Don't know what other impact it might have on others. + */ + +#ifdef WIN32 +#define __WIN32__ 1 +#endif + +#if __WIN32__ + +#ifdef _WIN32_WINNT +#error Include "direwolf.h" before any windows system files. +#endif +#ifdef WINVER +#error Include "direwolf.h" before any windows system files. +#endif + +#define _WIN32_WINNT 0x0501 /* Minimum OS version is XP. */ +#define WINVER 0x0501 /* Minimum OS version is XP. */ + +#include +#include + +#endif + + /* + * Maximum number of audio devices. + * Three is probably adequate for standard version. + * Larger reasonable numbers should also be fine. + * + * For example, if you wanted to use 4 audio devices at once, change this to 4. + */ + +#define MAX_ADEVS 3 + + + /* + * Maximum number of radio channels. + * Note that there could be gaps. + * Suppose audio device 0 was in mono mode and audio device 1 was stereo. + * The channels available would be: + * + * ADevice 0: channel 0 + * ADevice 1: left = 2, right = 3 + * + * TODO1.2: Look for any places that have + * for (ch=0; ch>1) +#define ADEVFIRSTCHAN(n) ((n) * 2) + + /* + * Maximum number of modems per channel. + * I called them "subchannels" (in the code) because + * it is short and unambiguous. + * Nothing magic about the number. Could be larger + * but CPU demands might be overwhelming. + */ + +#define MAX_SUBCHANS 9 + + /* + * Each one of these can have multiple slicers, at + * different levels, to compensate for different + * amplitudes of the AFSK tones. + * Initially used same number as subchannels but + * we could probably trim this down a little + * without impacting performance. + */ + +#define MAX_SLICERS 9 + + +#if __WIN32__ +#define SLEEP_SEC(n) Sleep((n)*1000) +#define SLEEP_MS(n) Sleep(n) +#else +#define SLEEP_SEC(n) sleep(n) +#define SLEEP_MS(n) usleep((n)*1000) +#endif + +#if __WIN32__ + +#define PTW32_STATIC_LIB + //#include "pthreads/pthread.h" + + // This enables definitions of localtime_r and gmtime_r in system time.h. + //#define _POSIX_THREAD_SAFE_FUNCTIONS 1 +#define _POSIX_C_SOURCE 1 + +#else +#include +#endif + + +#ifdef __APPLE__ + + // https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2072 + + // The original suggestion was to add this to only ptt.c. + // I thought it would make sense to put it here, so it will apply to all files, + // consistently, rather than only one file ptt.c. + + // The placement of this is critical. Putting it earlier was a problem. + // https://github.com/wb2osz/direwolf/issues/113 + + // It needs to be after the include pthread.h because + // pthread.h pulls in , which redefines __DARWIN_C_LEVEL back to ansi, + // which breaks things. + // Maybe it should just go in ptt.c as originally suggested. + + // #define __DARWIN_C_LEVEL __DARWIN_C_FULL + + // There is a more involved patch here: + // https://groups.yahoo.com/neo/groups/direwolf_packet/conversations/messages/2458 + +#ifndef _DARWIN_C_SOURCE +#define _DARWIN_C_SOURCE +#endif + +// Defining _DARWIN_C_SOURCE ensures that the definition for the cfmakeraw function (or similar) +// are pulled in through the include file . + +#ifdef __DARWIN_C_LEVEL +#undef __DARWIN_C_LEVEL +#endif + +#define __DARWIN_C_LEVEL __DARWIN_C_FULL + +#endif + + + + +#define DW_METERS_TO_FEET(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 3.2808399) +#define DW_FEET_TO_METERS(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.3048) +#define DW_KM_TO_MILES(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.621371192) +#define DW_MILES_TO_KM(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 1.609344) + +#define DW_KNOTS_TO_MPH(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 1.15077945) +#define DW_KNOTS_TO_METERS_PER_SEC(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.51444444444) +#define DW_MPH_TO_KNOTS(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.868976) +#define DW_MPH_TO_METERS_PER_SEC(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.44704) + +#define DW_MBAR_TO_INHG(x) ((x) == G_UNKNOWN ? G_UNKNOWN : (x) * 0.0295333727) + + + + +#if __WIN32__ + +typedef CRITICAL_SECTION dw_mutex_t; + +#define dw_mutex_init(x) \ + InitializeCriticalSection (x) + +/* This one waits for lock. */ + +#define dw_mutex_lock(x) \ + EnterCriticalSection (x) + +/* Returns non-zero if lock was obtained. */ + +#define dw_mutex_try_lock(x) \ + TryEnterCriticalSection (x) + +#define dw_mutex_unlock(x) \ + LeaveCriticalSection (x) + + +#else + +typedef pthread_mutex_t dw_mutex_t; + +#define dw_mutex_init(x) pthread_mutex_init (x, NULL) + +/* this one will wait. */ + +#define dw_mutex_lock(x) \ + { \ + int err; \ + err = pthread_mutex_lock (x); \ + if (err != 0) { \ + text_color_set(DW_COLOR_ERROR); \ + dw_printf ("INTERNAL ERROR %s %d pthread_mutex_lock returned %d", __FILE__, __LINE__, err); \ + exit (1); \ + } \ + } + +/* This one returns true if lock successful, false if not. */ +/* pthread_mutex_trylock returns 0 for success. */ + +#define dw_mutex_try_lock(x) \ + ({ \ + int err; \ + err = pthread_mutex_trylock (x); \ + if (err != 0 && err != EBUSY) { \ + text_color_set(DW_COLOR_ERROR); \ + dw_printf ("INTERNAL ERROR %s %d pthread_mutex_trylock returned %d", __FILE__, __LINE__, err); \ + exit (1); \ + } ; \ + ! err; \ + }) + +#define dw_mutex_unlock(x) \ + { \ + int err; \ + err = pthread_mutex_unlock (x); \ + if (err != 0) { \ + text_color_set(DW_COLOR_ERROR); \ + dw_printf ("INTERNAL ERROR %s %d pthread_mutex_unlock returned %d", __FILE__, __LINE__, err); \ + exit (1); \ + } \ + } + +#endif + + + +// Formerly used write/read on Linux, for some forgotten reason, +// but always using send/recv makes more sense. +// Need option to prevent a SIGPIPE signal on Linux. (added for 1.5 beta 2) + +#if __WIN32__ || __APPLE__ +#define SOCK_SEND(s,data,size) send(s,data,size,0) +#else +#define SOCK_SEND(s,data,size) send(s,data,size, MSG_NOSIGNAL) +#endif +#define SOCK_RECV(s,data,size) recv(s,data,size,0) + + +/* Platform differences for string functions. */ + + + +#if __WIN32__ +char *strsep(char **stringp, const char *delim); +char *strtok_r(char *str, const char *delim, char **saveptr); +#endif + +// Don't recall why I added this for everyone rather than only for Windows. +char *strcasestr(const char *S, const char *FIND); + +#endif /* ifndef DIREWOLF_H */ + + + +/*------------------------------------------------------------------ + * + * Module: audio.h + * + * Purpose: Interface to audio device commonly called a "sound card" + * for historical reasons. + * + *---------------------------------------------------------------*/ + + + + /* + * PTT control. + */ + +enum ptt_method_e { + PTT_METHOD_NONE, /* VOX or no transmit. */ + PTT_METHOD_SERIAL, /* Serial port RTS or DTR. */ + PTT_METHOD_GPIO, /* General purpose I/O, Linux only. */ + PTT_METHOD_LPT, /* Parallel printer port, Linux only. */ + PTT_METHOD_HAMLIB, /* HAMLib, Linux only. */ + PTT_METHOD_CM108 +}; /* GPIO pin of CM108/CM119/etc. Linux only. */ + +typedef enum ptt_method_e ptt_method_t; + +enum ptt_line_e { PTT_LINE_NONE = 0, PTT_LINE_RTS = 1, PTT_LINE_DTR = 2 }; // Important: 0 for neither. +typedef enum ptt_line_e ptt_line_t; + +enum audio_in_type_e { + AUDIO_IN_TYPE_SOUNDCARD, + AUDIO_IN_TYPE_SDR_UDP, + AUDIO_IN_TYPE_STDIN +}; + +/* For option to try fixing frames with bad CRC. */ + +typedef enum retry_e { + RETRY_NONE = 0, + RETRY_INVERT_SINGLE = 1, + RETRY_INVERT_DOUBLE = 2, + RETRY_INVERT_TRIPLE = 3, + RETRY_INVERT_TWO_SEP = 4, + RETRY_MAX = 5 +} retry_t; + +// Type of communication medium associated with the channel. + +enum medium_e { + MEDIUM_NONE = 0, // Channel is not valid for use. + MEDIUM_RADIO, // Internal modem for radio. + MEDIUM_IGATE, // Access IGate as ordinary channel. + MEDIUM_NETTNC +}; // Remote network TNC. (possible future) + + +typedef enum sanity_e { SANITY_APRS, SANITY_AX25, SANITY_NONE } sanity_t; + + +struct audio_s { + + /* Previously we could handle only a single audio device. */ + /* In version 1.2, we generalize this to handle multiple devices. */ + /* This means we can now have more than 2 radio channels. */ + + struct adev_param_s { + + /* Properties of the sound device. */ + + int defined; /* Was device defined? */ + /* First one defaults to yes. */ + + char adevice_in[80]; /* Name of the audio input device (or file?). */ + /* TODO: Can be "-" to read from stdin. */ + + char adevice_out[80]; /* Name of the audio output device (or file?). */ + + int num_channels; /* Should be 1 for mono or 2 for stereo. */ + int samples_per_sec; /* Audio sampling rate. Typically 11025, 22050, or 44100. */ + int bits_per_sample; /* 8 (unsigned char) or 16 (signed short). */ + + } adev[MAX_ADEVS]; + + + /* Common to all channels. */ + + char tts_script[80]; /* Script for text to speech. */ + + int statistics_interval; /* Number of seconds between the audio */ + /* statistics reports. This is set by */ + /* the "-a" option. 0 to disable feature. */ + + int xmit_error_rate; /* For testing purposes, we can generate frames with an invalid CRC */ + /* to simulate corruption while going over the air. */ + /* This is the probability, in per cent, of randomly corrupting it. */ + /* Normally this is 0. 25 would mean corrupt it 25% of the time. */ + + int recv_error_rate; /* Similar but the % probability of dropping a received frame. */ + + float recv_ber; /* Receive Bit Error Rate (BER). */ + /* Probability of inverting a bit coming out of the modem. */ + + //int fx25_xmit_enable; /* Enable transmission of FX.25. */ + /* See fx25_init.c for explanation of values. */ + /* Initially this applies to all channels. */ + /* This should probably be per channel. One step at a time. */ + /* v1.7 - replaced by layer2_xmit==LAYER2_FX25 */ + + int fx25_auto_enable; /* Turn on FX.25 for current connected mode session */ + /* under poor conditions. */ + /* Set to 0 to disable feature. */ + /* I put it here, rather than with the rest of the link layer */ + /* parameters because it is really a part of the HDLC layer */ + /* and is part of the KISS TNC functionality rather than our data link layer. */ + /* Future: not used yet. */ + + + char timestamp_format[40]; /* -T option */ + /* Precede received & transmitted frames with timestamp. */ + /* Command line option uses "strftime" format string. */ + + + + /* originally a "channel" was always connected to an internal modem. */ + /* In version 1.6, this is generalized so that a channel (as seen by client application) */ + /* can be connected to something else. Initially, this will allow application */ + /* access to the IGate. Later we might have network TNCs or other internal functions. */ + + // Properties for all channels. + + enum medium_e chan_medium[MAX_TOTAL_CHANS]; + // MEDIUM_NONE for invalid. + // MEDIUM_RADIO for internal modem. (only possibility earlier) + // MEDIUM_IGATE allows application access to IGate. + // MEDIUM_NETTNC for external TNC via TCP. + + int igate_vchannel; /* Virtual channel mapped to APRS-IS. */ + /* -1 for none. */ + /* Redundant but it makes things quicker and simpler */ + /* than always searching thru above. */ + + /* Properties for each radio channel, common to receive and transmit. */ + /* Can be different for each radio channel. */ + + struct achan_param_s { + + // What else should be moved out of structure and enlarged when NETTNC is implemented. ??? + char mycall[AX25_MAX_ADDR_LEN]; /* Call associated with this radio channel. */ + /* Could all be the same or different. */ + + + enum modem_t { MODEM_AFSK, MODEM_BASEBAND, MODEM_SCRAMBLE, MODEM_QPSK, MODEM_8PSK, MODEM_OFF, MODEM_16_QAM, MODEM_64_QAM, MODEM_AIS, MODEM_EAS } modem_type; + + /* Usual AFSK. */ + /* Baseband signal. Not used yet. */ + /* Scrambled http://www.amsat.org/amsat/articles/g3ruh/109/fig03.gif */ + /* Might try MFJ-2400 / CCITT v.26 / Bell 201 someday. */ + /* No modem. Might want this for DTMF only channel. */ + + enum layer2_t { LAYER2_AX25 = 0, LAYER2_FX25, LAYER2_IL2P } layer2_xmit; + + // IL2P - New for version 1.7. + // New layer 2 with FEC. Much less overhead than FX.25 but no longer backward compatible. + // Only applies to transmit. + // Listening for FEC sync word should add negligible overhead so + // we leave reception enabled all the time as we do with FX.25. + // TODO: FX.25 should probably be put here rather than global for all channels. + + int fx25_strength; // Strength of FX.25 FEC. + // 16, 23, 64 for specific number of parity symbols. + // 1 for automatic selection based on frame size. + + int il2p_max_fec; // 1 for max FEC length, 0 for automatic based on size. + + int il2p_invert_polarity; // 1 means invert on transmit. Receive handles either automatically. + + enum v26_e { V26_UNSPECIFIED = 0, V26_A, V26_B } v26_alternative; + + // Original implementation used alternative A for 2400 bbps PSK. + // Years later, we discover that MFJ-2400 used alternative B. + // It's likely the others did too. it also works a little better. + // Default to MFJ compatible and print warning if user did not + // pick one explicitly. + +#define V26_DEFAULT V26_B + + enum dtmf_decode_t { DTMF_DECODE_OFF, DTMF_DECODE_ON } dtmf_decode; + + /* Originally the DTMF ("Touch Tone") decoder was always */ + /* enabled because it took a negligible amount of CPU. */ + /* There were complaints about the false positives when */ + /* hearing other modulation schemes on HF SSB so now it */ + /* is enabled only when needed. */ + + /* "On" will send special "t" packet to attached applications */ + /* and process as APRStt. Someday we might want to separate */ + /* these but for now, we have a single off/on. */ + + int decimate; /* Reduce AFSK sample rate by this factor to */ + /* decrease computational requirements. */ + + int upsample; /* Upsample by this factor for G3RUH. */ + + int mark_freq; /* Two tones for AFSK modulation, in Hz. */ + int space_freq; /* Standard tones are 1200 and 2200 for 1200 baud. */ + + int baud; /* Data bits per second. */ + /* Standard rates are 1200 for VHF and 300 for HF. */ + /* This should really be called bits per second. */ + + /* Next 3 come from config file or command line. */ + + char profiles[16]; /* zero or more of ABC etc, optional + */ + + int num_freq; /* Number of different frequency pairs for decoders. */ + + int offset; /* Spacing between filter frequencies. */ + + int num_slicers; /* Number of different threshold points to decide */ + /* between mark or space. */ + + /* This is derived from above by demod_init. */ + + int num_subchan; /* Total number of modems for each channel. */ + + + /* These are for dealing with imperfect frames. */ + + enum retry_e fix_bits; /* Level of effort to recover from */ + /* a bad FCS on the frame. */ + /* 0 = no effort */ + /* 1 = try fixing a single bit */ + /* 2... = more techniques... */ + + enum sanity_e sanity_test; /* Sanity test to apply when finding a good */ + /* CRC after making a change. */ + /* Must look like APRS, AX.25, or anything. */ + + int passall; /* Allow thru even with bad CRC. */ + + + + /* Additional properties for transmit. */ + + /* Originally we had control outputs only for PTT. */ + /* In version 1.2, we generalize this to allow others such as DCD. */ + /* In version 1.4 we add CON for connected to another station. */ + /* Index following structure by one of these: */ + + +#define OCTYPE_PTT 0 +#define OCTYPE_DCD 1 +#define OCTYPE_CON 2 + +#define NUM_OCTYPES 3 /* number of values above. i.e. last value +1. */ + + struct { + + ptt_method_t ptt_method; /* none, serial port, GPIO, LPT, HAMLIB, CM108. */ + + char ptt_device[128]; /* Serial device name for PTT. e.g. COM1 or /dev/ttyS0 */ + /* Also used for HAMLIB. Could be host:port when model is 1. */ + /* For years, 20 characters was plenty then we start getting extreme names like this: */ + /* /dev/serial/by-id/usb-FTDI_Navigator__CAT___2nd_PTT__00000000-if00-port0 */ + /* /dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0 */ + /* Issue 104, changed to 100 bytes in version 1.5. */ + + /* This same field is also used for CM108/CM119 GPIO PTT which will */ + /* have a name like /dev/hidraw1 for Linux or */ + /* \\?\hid#vid_0d8c&pid_0008&mi_03#8&39d3555&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} */ + /* for Windows. Largest observed was 95 but add some extra to be safe. */ + + ptt_line_t ptt_line; /* Control line when using serial port. PTT_LINE_RTS, PTT_LINE_DTR. */ + ptt_line_t ptt_line2; /* Optional second one: PTT_LINE_NONE when not used. */ + + int out_gpio_num; /* GPIO number. Originally this was only for PTT. */ + /* It is now more general. */ + /* octrl array is indexed by PTT, DCD, or CONnected indicator. */ + /* For CM108/CM119, this should be in range of 1-8. */ + +#define MAX_GPIO_NAME_LEN 20 // 12 would cover any case I've seen so this should be safe + + char out_gpio_name[MAX_GPIO_NAME_LEN]; + /* originally, gpio number NN was assumed to simply */ + /* have the name gpioNN but this turned out not to be */ + /* the case for CubieBoard where it was longer. */ + /* This is filled in by ptt_init so we don't have to */ + /* recalculate it each time we access it. */ + + /* This could probably be collapsed into ptt_device instead of being separate. */ + + int ptt_lpt_bit; /* Bit number for parallel printer port. */ + /* Bit 0 = pin 2, ..., bit 7 = pin 9. */ + + int ptt_invert; /* Invert the output. */ + int ptt_invert2; /* Invert the secondary output. */ + +#ifdef USE_HAMLIB + + int ptt_model; /* HAMLIB model. -1 for AUTO. 2 for rigctld. Others are radio model. */ + int ptt_rate; /* Serial port speed when using hamlib CAT control for PTT. */ + /* If zero, hamlib will come up with a default for pariticular rig. */ +#endif + + } octrl[NUM_OCTYPES]; + + + /* Each channel can also have associated input lines. */ + /* So far, we just have one for transmit inhibit. */ + +#define ICTYPE_TXINH 0 + +#define NUM_ICTYPES 1 /* number of values above. i.e. last value +1. */ + + struct { + ptt_method_t method; /* none, serial port, GPIO, LPT. */ + + int in_gpio_num; /* GPIO number */ + + char in_gpio_name[MAX_GPIO_NAME_LEN]; + /* originally, gpio number NN was assumed to simply */ + /* have the name gpioNN but this turned out not to be */ + /* the case for CubieBoard where it was longer. */ + /* This is filled in by ptt_init so we don't have to */ + /* recalculate it each time we access it. */ + + int invert; /* 1 = active low */ + } ictrl[NUM_ICTYPES]; + + /* Transmit timing. */ + + int dwait; /* First wait extra time for receiver squelch. */ + /* Default 0 units of 10 mS each . */ + + int slottime; /* Slot time in 10 mS units for persistence algorithm. */ + /* Typical value is 10 meaning 100 milliseconds. */ + + int persist; /* Sets probability for transmitting after each */ + /* slot time delay. Transmit if a random number */ + /* in range of 0 - 255 <= persist value. */ + /* Otherwise wait another slot time and try again. */ + /* Default value is 63 for 25% probability. */ + + int txdelay; /* After turning on the transmitter, */ + /* send "flags" for txdelay * 10 mS. */ + /* Default value is 30 meaning 300 milliseconds. */ + + int txtail; /* Amount of time to keep transmitting after we */ + /* are done sending the data. This is to avoid */ + /* dropping PTT too soon and chopping off the end */ + /* of the frame. Again 10 mS units. */ + /* At this point, I'm thinking of 10 (= 100 mS) as the default */ + /* because we're not quite sure when the soundcard audio stops. */ + + int fulldup; /* Full Duplex. */ + + } achan[MAX_CHANS]; + +#ifdef USE_HAMLIB + int rigs; /* Total number of configured rigs */ + RIG *rig[MAX_RIGS]; /* HAMLib rig instances */ +#endif + +}; + + +#if __WIN32__ +#define DEFAULT_ADEVICE "" /* Windows: Empty string = default audio device. */ +#elif __APPLE__ +#define DEFAULT_ADEVICE "" /* Mac OSX: Empty string = default audio device. */ +#elif USE_ALSA +#define DEFAULT_ADEVICE "default" /* Use default device for ALSA. */ +#elif USE_SNDIO +#define DEFAULT_ADEVICE "default" /* Use default device for sndio. */ +#else +#define DEFAULT_ADEVICE "/dev/dsp" /* First audio device for OSS. (FreeBSD) */ +#endif + + + +/* + * UDP audio receiving port. Couldn't find any standard or usage precedent. + * Got the number from this example: http://gqrx.dk/doc/streaming-audio-over-udp + * Any better suggestions? + */ + +#define DEFAULT_UDP_AUDIO_PORT 7355 + + + // Maximum size of the UDP buffer (for allowing IP routing, udp packets are often limited to 1472 bytes) + +#define SDR_UDP_BUF_MAXLEN 2000 + + + +#define DEFAULT_NUM_CHANNELS 1 +#define DEFAULT_SAMPLES_PER_SEC 44100 /* Very early observations. Might no longer be valid. */ + /* 22050 works a lot better than 11025. */ + /* 44100 works a little better than 22050. */ + /* If you have a reasonable machine, use the highest rate. */ +#define MIN_SAMPLES_PER_SEC 8000 +//#define MAX_SAMPLES_PER_SEC 48000 /* Originally 44100. Later increased because */ + /* Software Defined Radio often uses 48000. */ + +#define MAX_SAMPLES_PER_SEC 192000 /* The cheap USB-audio adapters (e.g. CM108) can handle 44100 and 48000. */ + /* The "soundcard" in my desktop PC can do 96kHz or even 192kHz. */ + /* We will probably need to increase the sample rate to go much above 9600 baud. */ + +#define DEFAULT_BITS_PER_SAMPLE 16 + +#define DEFAULT_FIX_BITS RETRY_INVERT_SINGLE + +/* + * Standard for AFSK on VHF FM. + * Reversing mark and space makes no difference because + * NRZI encoding only cares about change or lack of change + * between the two tones. + * + * HF SSB uses 300 baud and 200 Hz shift. + * 1600 & 1800 Hz is a popular tone pair, sometimes + * called the KAM tones. + */ + +#define DEFAULT_MARK_FREQ 1200 +#define DEFAULT_SPACE_FREQ 2200 +#define DEFAULT_BAUD 1200 + + /* Used for sanity checking in config file and command line options. */ + /* 9600 baud is known to work. */ + /* TODO: Is 19200 possible with a soundcard at 44100 samples/sec or do we need a higher sample rate? */ + +#define MIN_BAUD 100 +//#define MAX_BAUD 10000 +#define MAX_BAUD 40000 // Anyone want to try 38.4 k baud? + +/* + * Typical transmit timings for VHF. + */ + +#define DEFAULT_DWAIT 0 +#define DEFAULT_SLOTTIME 10 +#define DEFAULT_PERSIST 63 +#define DEFAULT_TXDELAY 30 +#define DEFAULT_TXTAIL 10 +#define DEFAULT_FULLDUP 0 + + /* + * Note that we have two versions of these in audio.c and audio_win.c. + * Use one or the other depending on the platform. + */ + +int audio_open(struct audio_s *pa); + +int audio_get(int a); /* a = audio device, 0 for first */ + +int audio_put(int a, int c); + +int audio_flush(int a); + +void audio_wait(int a); + +int audio_close(void); + + + +/* end audio.h */ + + + +void multi_modem_init(struct audio_s *pmodem); + +void multi_modem_process_sample(int c, int audio_sample); + +int multi_modem_get_dc_average(int chan); + +// Deprecated. Replace with ...packet + + +void multi_modem_process_rec_packet(int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25); + + +void fsk_gen_filter(int samples_per_sec, + int baud, + int mark_freq, int space_freq, + char profile, + struct demodulator_state_s *D); + + + +/* fsk_demod_state.h */ + + + +/* + * Demodulator state. + * The name of the file is from we only had FSK. Now we have other techniques. + * Different copy is required for each channel & subchannel being processed concurrently. + */ + + // TODO1.2: change prefix from BP_ to DSP_ + +typedef enum bp_window_e { + BP_WINDOW_TRUNCATED, + BP_WINDOW_COSINE, + BP_WINDOW_HAMMING, + BP_WINDOW_BLACKMAN, + BP_WINDOW_FLATTOP +} bp_window_t; + +// Experimental low pass filter to detect DC bias or low frequency changes. +// IIR behaves like an analog R-C filter. +// Intuitively, it seems like FIR would be better because it is based on a finite history. +// However, it would require MANY taps and a LOT of computation for a low frequency. +// We can use a little trick here to keep a running average. +// This would be equivalent to convolving with an array of all 1 values. +// That would eliminate the need to multiply. +// We can also eliminate the need to add them all up each time by keeping a running total. +// Add a sample to the total when putting it in our array of recent samples. +// Subtract it from the total when it gets pushed off the end. +// We can also eliminate the need to shift them all down by using a circular buffer. + +#define CIC_LEN_MAX 4000 + +typedef struct cic_s { + int len; // Number of elements used. + // Might want to dynamically allocate. + short in[CIC_LEN_MAX]; // Samples coming in. + int sum; // Running sum. + int inext; // Next position to fill. +} cic_t; + + +#define MAX_FILTER_SIZE 480 /* 401 is needed for profile A, 300 baud & 44100. Revisit someday. */ +// Size comes out to 417 for 1200 bps with 48000 sample rate +// v1.7 - Was 404. Bump up to 480. + +struct demodulator_state_s +{ + /* + * These are set once during initialization. + */ + enum modem_t modem_type; // MODEM_AFSK, MODEM_8PSK, etc. + +// enum v26_e v26_alt; // Which alternative when V.26. + + char profile; // 'A', 'B', etc. Upper case. + // Only needed to see if we are using 'F' to take fast path. + +#define TICKS_PER_PLL_CYCLE ( 256.0 * 256.0 * 256.0 * 256.0 ) + + int pll_step_per_sample; // PLL is advanced by this much each audio sample. + // Data is sampled when it overflows. + + +/* + * Window type for the various filters. + */ + + bp_window_t lp_window; + + /* + * Alternate Low pass filters. + * First is arbitrary number for quick IIR. + * Second is frequency as ratio to baud rate for FIR. + */ + int lpf_use_fir; /* 0 for IIR, 1 for FIR. */ + + float lpf_iir; /* Only if using IIR. */ + + float lpf_baud; /* Cutoff frequency as fraction of baud. */ + /* Intuitively we'd expect this to be somewhere */ + /* in the range of 0.5 to 1. */ + /* In practice, it turned out a little larger */ + /* for profiles B, C, D. */ + + float lp_filter_width_sym; /* Length in number of symbol times. */ + +#define lp_filter_len_bits lp_filter_width_sym // FIXME: temp hack + + int lp_filter_taps; /* Size of Low Pass filter, in audio samples. */ + +#define lp_filter_size lp_filter_taps // FIXME: temp hack + + +/* + * Automatic gain control. Fast attack and slow decay factors. + */ + float agc_fast_attack; + float agc_slow_decay; + + /* + * Use a longer term view for reporting signal levels. + */ + float quick_attack; + float sluggish_decay; + + /* + * Hysteresis before final demodulator 0 / 1 decision. + */ + float hysteresis; + int num_slicers; /* >1 for multiple slicers. */ + +/* + * Phase Locked Loop (PLL) inertia. + * Larger number means less influence by signal transitions. + * It is more resistant to change when locked on to a signal. + */ + float pll_locked_inertia; + float pll_searching_inertia; + + + /* + * Optional band pass pre-filter before mark/space detector. + */ + int use_prefilter; /* True to enable it. */ + + float prefilter_baud; /* Cutoff frequencies, as fraction of */ + /* baud rate, beyond tones used. */ + /* Example, if we used 1600/1800 tones at */ + /* 300 baud, and this was 0.5, the cutoff */ + /* frequencies would be: */ + /* lower = min(1600,1800) - 0.5 * 300 = 1450 */ + /* upper = max(1600,1800) + 0.5 * 300 = 1950 */ + + float pre_filter_len_sym; // Length in number of symbol times. +#define pre_filter_len_bits pre_filter_len_sym // temp until all references changed. + + bp_window_t pre_window; // Window type for filter shaping. + + int pre_filter_taps; // Calculated number of filter taps. +#define pre_filter_size pre_filter_taps // temp until all references changed. + + float pre_filter[MAX_FILTER_SIZE]; + + float raw_cb[MAX_FILTER_SIZE]; // audio in, need better name. + +/* + * The rest are continuously updated. + */ + + unsigned int lo_phase; /* Local oscillator for PSK. */ + + +/* + * Use half of the AGC code to get a measure of input audio amplitude. + * These use "quick" attack and "sluggish" decay while the + * AGC uses "fast" attack and "slow" decay. + */ + + float alevel_rec_peak; + float alevel_rec_valley; + float alevel_mark_peak; + float alevel_space_peak; + + /* + * Outputs from the mark and space amplitude detection, + * used as inputs to the FIR lowpass filters. + * Kernel for the lowpass filters. + */ + + float lp_filter[MAX_FILTER_SIZE]; + + float m_peak, s_peak; + float m_valley, s_valley; + float m_amp_prev, s_amp_prev; + + + /* + * For the PLL and data bit timing. + * starting in version 1.2 we can have multiple slicers for one demodulator. + * Each slicer has its own PLL and HDLC decoder. + */ + + /* + * Version 1.3: Clean up subchan vs. slicer. + * + * Originally some number of CHANNELS (originally 2, later 6) + * which can have multiple parallel demodulators called SUB-CHANNELS. + * This was originally for staggered frequencies for HF SSB. + * It can also be used for multiple demodulators with the same + * frequency but other differing parameters. + * Each subchannel has its own demodulator and HDLC decoder. + * + * In version 1.2 we added multiple SLICERS. + * The data structure, here, has multiple slicers per + * demodulator (subchannel). Due to fuzzy thinking or + * expediency, the multiple slicers got mapped into subchannels. + * This means we can't use both multiple decoders and + * multiple slicers at the same time. + * + * Clean this up in 1.3 and keep the concepts separate. + * This means adding a third variable many places + * we are passing around the origin. + * + */ + struct { + + signed int data_clock_pll; // PLL for data clock recovery. + // It is incremented by pll_step_per_sample + // for each audio sample. + // Must be 32 bits!!! + // So far, this is the case for every compiler used. + + signed int prev_d_c_pll; // Previous value of above, before + // incrementing, to detect overflows. + + int pll_symbol_count; // Number symbols during time nudge_total is accumulated. + int64_t pll_nudge_total; // Sum of DPLL nudge amounts. + // Both of these are cleared at start of frame. + // At end of frame, we can see if incoming + // baud rate is a little off. + + int prev_demod_data; // Previous data bit detected. + // Used to look for transitions. + float prev_demod_out_f; + + /* This is used only for "9600" baud data. */ + + int lfsr; // Descrambler shift register. + + // This is for detecting phase lock to incoming signal. + + int good_flag; // Set if transition is near where expected, + // i.e. at a good time. + int bad_flag; // Set if transition is not where expected, + // i.e. at a bad time. + unsigned char good_hist; // History of good transitions for past octet. + unsigned char bad_hist; // History of bad transitions for past octet. + unsigned int score; // History of whether good triumphs over bad + // for past 32 symbols. + int data_detect; // True when locked on to signal. + + } slicer[MAX_SLICERS]; // Actual number in use is num_slicers. + // Should be in range 1 .. MAX_SLICERS, +/* + * Version 1.6: + * + * This has become quite disorganized and messy with different combinations of + * fields used for different demodulator types. Start to reorganize it into a common + * part (with things like the DPLL for clock recovery), and separate sections + * for each of the demodulator types. + * Still a lot to do here. + */ + + union { + + ////////////////////////////////////////////////////////////////////////////////// + // // + // AFSK only - new method in 1.7 // + // // + ////////////////////////////////////////////////////////////////////////////////// + + + struct afsk_only_s { + + unsigned int m_osc_phase; // Phase for Mark local oscillator. + unsigned int m_osc_delta; // How much to change for each audio sample. + + unsigned int s_osc_phase; // Phase for Space local oscillator. + unsigned int s_osc_delta; // How much to change for each audio sample. + + unsigned int c_osc_phase; // Phase for Center frequency local oscillator. + unsigned int c_osc_delta; // How much to change for each audio sample. + + // Need two mixers for profile "A". + + float m_I_raw[MAX_FILTER_SIZE]; + float m_Q_raw[MAX_FILTER_SIZE]; + + float s_I_raw[MAX_FILTER_SIZE]; + float s_Q_raw[MAX_FILTER_SIZE]; + + // Only need one mixer for profile "B". Reuse the same storage? + + //#define c_I_raw m_I_raw + //#define c_Q_raw m_Q_raw + float c_I_raw[MAX_FILTER_SIZE]; + float c_Q_raw[MAX_FILTER_SIZE]; + + int use_rrc; // Use RRC rather than generic low pass. + + float rrc_width_sym; /* Width of RRC filter in number of symbols. */ + + float rrc_rolloff; /* Rolloff factor for RRC. Between 0 and 1. */ + + float prev_phase; // To see phase shift between samples for FM demod. + + float normalize_rpsam; // Normalize to -1 to +1 for expected tones. + + } afsk; + + ////////////////////////////////////////////////////////////////////////////////// + // // + // Baseband only, AKA G3RUH // + // // + ////////////////////////////////////////////////////////////////////////////////// + + // TODO: Continue experiments with root raised cosine filter. + // Either switch to that or take out all the related stuff. + + struct bb_only_s { + + float rrc_width_sym; /* Width of RRC filter in number of symbols. */ + + float rrc_rolloff; /* Rolloff factor for RRC. Between 0 and 1. */ + + int rrc_filter_taps; // Number of elements used in the next two. + + // FIXME: TODO: reevaluate max size needed. + + float audio_in[MAX_FILTER_SIZE]; // Audio samples in. + + + float lp_filter[MAX_FILTER_SIZE]; // Low pass filter. + + // New in 1.7 - Polyphase filter to reduce CPU requirements. + + float lp_polyphase_1[MAX_FILTER_SIZE]; + float lp_polyphase_2[MAX_FILTER_SIZE]; + float lp_polyphase_3[MAX_FILTER_SIZE]; + float lp_polyphase_4[MAX_FILTER_SIZE]; + + float lp_1_iir_param; // very low pass filters to get DC offset. + float lp_1_out; + + float lp_2_iir_param; + float lp_2_out; + + float agc_1_fast_attack; // Signal envelope detection. + float agc_1_slow_decay; + float agc_1_peak; + float agc_1_valley; + + float agc_2_fast_attack; + float agc_2_slow_decay; + float agc_2_peak; + float agc_2_valley; + + float agc_3_fast_attack; + float agc_3_slow_decay; + float agc_3_peak; + float agc_3_valley; + + // CIC low pass filters to detect DC bias or low frequency changes. + // IIR behaves like an analog R-C filter. + // Intuitively, it seems like FIR would be better because it is based on a finite history. + // However, it would require MANY taps and a LOT of computation for a low frequency. + // We can use a little trick here to keep a running average. + // This would be equivalent to convolving with an array of all 1 values. + // That would eliminate the need to multiply. + // We can also eliminate the need to add them all up each time by keeping a running total. + // Add a sample to the total when putting it in our array of recent samples. + // Subtract it from the total when it gets pushed off the end. + // We can also eliminate the need to shift them all down by using a circular buffer. + // This only works with integers because float would have cumulated round off errors. + + cic_t cic_center1; + cic_t cic_above; + cic_t cic_below; + + } bb; + + ////////////////////////////////////////////////////////////////////////////////// + // // + // PSK only. // + // // + ////////////////////////////////////////////////////////////////////////////////// + + + struct psk_only_s { + + enum v26_e v26_alt; // Which alternative when V.26. + + float sin_table256[256]; // Precomputed sin table for speed. + + + // Optional band pass pre-filter before phase detector. + + // TODO? put back into common section? + // TODO? Why was I thinking that? + + int use_prefilter; // True to enable it. + + float prefilter_baud; // Cutoff frequencies, as fraction of baud rate, beyond tones used. + // In the case of PSK, we use only a single tone of 1800 Hz. + // If we were using 2400 bps (= 1200 baud), this would be + // the fraction of 1200 for the cutoff below and above 1800. + + + float pre_filter_width_sym; /* Length in number of symbol times. */ + + int pre_filter_taps; /* Size of pre filter, in audio samples. */ + + bp_window_t pre_window; + + float audio_in[MAX_FILTER_SIZE]; + float pre_filter[MAX_FILTER_SIZE]; + + // Use local oscillator or correlate with previous sample. + + int psk_use_lo; /* Use local oscillator rather than self correlation. */ + + unsigned int lo_step; /* How much to advance the local oscillator */ + /* phase for each audio sample. */ + + unsigned int lo_phase; /* Local oscillator phase accumulator for PSK. */ + + // After mixing with LO before low pass filter. + + float I_raw[MAX_FILTER_SIZE]; // signal * LO cos. + float Q_raw[MAX_FILTER_SIZE]; // signal * LO sin. + + // Number of delay line taps into previous symbol. + // They are one symbol period and + or - 45 degrees of the carrier frequency. + + int boffs; /* symbol length based on sample rate and baud. */ + int coffs; /* to get cos component of previous symbol. */ + int soffs; /* to get sin component of previous symbol. */ + + float delay_line_width_sym; + int delay_line_taps; // In audio samples. + + float delay_line[MAX_FILTER_SIZE]; + + // Low pass filter Second is frequency as ratio to baud rate for FIR. + + // TODO? put back into common section? + // TODO? What are the tradeoffs? + float lpf_baud; /* Cutoff frequency as fraction of baud. */ + /* Intuitively we'd expect this to be somewhere */ + /* in the range of 0.5 to 1. */ + + float lp_filter_width_sym; /* Length in number of symbol times. */ + + int lp_filter_taps; /* Size of Low Pass filter, in audio samples (i.e. filter taps). */ + + bp_window_t lp_window; + + float lp_filter[MAX_FILTER_SIZE]; + + } psk; + + } u; // end of union for different demodulator types. + +}; + + +/*------------------------------------------------------------------- + * + * Name: pll_dcd_signal_transition2 + * dcd_each_symbol2 + * + * Purpose: New DCD strategy for 1.6. + * + * Inputs: D Pointer to demodulator state. + * + * chan Radio channel: 0 to MAX_CHANS - 1 + * + * subchan Which of multiple demodulators: 0 to MAX_SUBCHANS - 1 + * + * slice Slicer number: 0 to MAX_SLICERS - 1. + * + * dpll_phase Signed 32 bit counter for DPLL phase. + * Wraparound is where data is sampled. + * Ideally transitions would occur close to 0. + * + * Output: D->slicer[slice].data_detect - true when PLL is locked to incoming signal. + * + * Description: From the beginning, DCD was based on finding several flag octets + * in a row and dropping when eight bits with no transitions. + * It was less than ideal but we limped along with it all these years. + * This fell apart when FX.25 came along and a couple of the + * correlation tags have eight "1" bits in a row. + * + * Our new strategy is to keep a running score of how well demodulator + * output transitions match to where expected. + * + *--------------------------------------------------------------------*/ + + + + // These are good for 1200 bps AFSK. + // Might want to override for other modems. + +#ifndef DCD_THRESH_ON +#define DCD_THRESH_ON 30 // Hysteresis: Can miss 2 out of 32 for detecting lock. + // 31 is best for TNC Test CD. 30 almost as good. + // 30 better for 1200 regression test. +#endif + +#ifndef DCD_THRESH_OFF +#define DCD_THRESH_OFF 6 // Might want a little more fine tuning. +#endif + +#ifndef DCD_GOOD_WIDTH +#define DCD_GOOD_WIDTH 512 // No more than 1024!!! +#endif + + +inline static void pll_dcd_signal_transition2(struct demodulator_state_s *D, int slice, int dpll_phase) +{ + if (dpll_phase > -DCD_GOOD_WIDTH * 1024 * 1024 && dpll_phase < DCD_GOOD_WIDTH * 1024 * 1024) { + D->slicer[slice].good_flag = 1; + } + else { + D->slicer[slice].bad_flag = 1; + } +} + + +inline static void pll_dcd_each_symbol2(struct demodulator_state_s *D, int chan, int subchan, int slice) +{ + D->slicer[slice].good_hist <<= 1; + D->slicer[slice].good_hist |= D->slicer[slice].good_flag; + D->slicer[slice].good_flag = 0; + + D->slicer[slice].bad_hist <<= 1; + D->slicer[slice].bad_hist |= D->slicer[slice].bad_flag; + D->slicer[slice].bad_flag = 0; + + D->slicer[slice].score <<= 1; + // 2 is to detect 'flag' patterns with 2 transitions per octet. + D->slicer[slice].score |= (signed)__builtin_popcount(D->slicer[slice].good_hist) + - (signed)__builtin_popcount(D->slicer[slice].bad_hist) >= 2; + + int s = __builtin_popcount(D->slicer[slice].score); + if (s >= DCD_THRESH_ON) { + if (D->slicer[slice].data_detect == 0) { + D->slicer[slice].data_detect = 1; + dcd_change(chan, subchan, slice, D->slicer[slice].data_detect); + } + } + else if (s <= DCD_THRESH_OFF) { + if (D->slicer[slice].data_detect != 0) { + D->slicer[slice].data_detect = 0; + dcd_change(chan, subchan, slice, D->slicer[slice].data_detect); + } + } +} + + + +/* Provided elsewhere to process a complete frame. */ + +//void process_rec_frame (int chan, unsigned char *fbuf, int flen, int level); + + +/* Is HLDC decoder is currently gathering bits into a frame? */ +/* Similar to, but not exactly the same as, data carrier detect. */ +/* We use this to influence the PLL inertia. */ + +int hdlc_rec_gathering(int chan, int subchan, int slice); + +/* Transmit needs to know when someone else is transmitting. */ + +void dcd_change(int chan, int subchan, int slice, int state); + +int hdlc_rec_data_detect_any(int chan); + + +#define FASTER13 1 // Don't pack 8 samples per byte. + + +//typedef short slice_t; + + +/* + * Maximum size (in bytes) of an AX.25 frame including the 2 octet FCS. + */ + +#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2) + + /* + * Maximum number of bits in AX.25 frame excluding the flags. + * Adequate for extreme case of bit stuffing after every 5 bits + * which could never happen. + */ + +#define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5) + +typedef struct rrbb_s { + int magic1; + struct rrbb_s* nextp; /* Next pointer to maintain a queue. */ + + int chan; /* Radio channel from which it was received. */ + int subchan; /* Which modem when more than one per channel. */ + int slice; /* Which slicer. */ + + alevel_t alevel; /* Received audio level at time of frame capture. */ + unsigned int len; /* Current number of samples in array. */ + + int is_scrambled; /* Is data scrambled G3RUH / K9NG style? */ + int descram_state; /* Descrambler state before first data bit of frame. */ + int prev_descram; /* Previous descrambled bit. */ + + unsigned char fdata[MAX_NUM_BITS]; + + int magic2; +} *rrbb_t; + + + +rrbb_t rrbb_new(int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram); + +void rrbb_clear(rrbb_t b, int is_scrambled, int descram_state, int prev_descram); + + +static inline /*__attribute__((always_inline))*/ void rrbb_append_bit(rrbb_t b, const unsigned char val) +{ + if (b->len >= MAX_NUM_BITS) { + return; /* Silently discard if full. */ + } + b->fdata[b->len] = val; + b->len++; +} + +static inline /*__attribute__((always_inline))*/ unsigned char rrbb_get_bit(const rrbb_t b, const int ind) +{ + return (b->fdata[ind]); +} + + +void rrbb_chop8(rrbb_t b); + +int rrbb_get_len(rrbb_t b); + +//void rrbb_flip_bit (rrbb_t b, unsigned int ind); + +void rrbb_delete(rrbb_t b); + +void rrbb_set_nextp(rrbb_t b, rrbb_t np); +rrbb_t rrbb_get_nextp(rrbb_t b); + +int rrbb_get_chan(rrbb_t b); +int rrbb_get_subchan(rrbb_t b); +int rrbb_get_slice(rrbb_t b); + +void rrbb_set_audio_level(rrbb_t b, alevel_t alevel); +alevel_t rrbb_get_audio_level(rrbb_t b); + +int rrbb_get_is_scrambled(rrbb_t b); +int rrbb_get_descram_state(rrbb_t b); +int rrbb_get_prev_descram(rrbb_t b); + + + + +void hdlc_rec2_init(struct audio_s *audio_config_p); + +void hdlc_rec2_block(rrbb_t block); + +int hdlc_rec2_try_to_fix_later(rrbb_t block, int chan, int subchan, int slice, alevel_t alevel); + +/* Provided by the top level application to process a complete frame. */ + +void app_process_rec_packet(int chan, int subchan, int slice, packet_t pp, alevel_t level, int is_fx25, retry_t retries, char *spectrum); + + + + + +int gen_tone_init(struct audio_s *pp, int amp, int gen_packets); + + +//int gen_tone_open (int nchan, int sample_rate, int bit_rate, int f1, int f2, int amp, char *fname); + +//int gen_tone_open_fd (int nchan, int sample_rate, int bit_rate, int f1, int f2, int amp, int fd) ; + +//int gen_tone_close (void); + +void tone_gen_put_bit(int chan, int dat); + +void gen_tone_put_sample(int chan, int a, int sam); + +enum dw_color_e { + DW_COLOR_INFO, /* black */ + DW_COLOR_ERROR, /* red */ + DW_COLOR_REC, /* green */ + DW_COLOR_DECODED, /* blue */ + DW_COLOR_XMIT, /* magenta */ + DW_COLOR_DEBUG /* dark_green */ +}; + +typedef enum dw_color_e dw_color_t; + + +void text_color_init(int enable_color); +void text_color_set(dw_color_t c); +void text_color_term(void); + + +/* Degree symbol. */ + +#if __WIN32__ + +//#define CH_DEGREE "\xc2\xb0" /* UTF-8. */ + +#define CH_DEGREE " " + + +#else + +/* Maybe we could change this based on LANG environment variable. */ + +//#define CH_DEGREE "\xc2\xb0" /* UTF-8. */ + +#define CH_DEGREE " " + +#endif + +/* demod_9600.h */ + + +void demod_9600_init(enum modem_t modem_type, int original_sample_rate, int upsample, int baud, struct demodulator_state_s *D); + +void demod_9600_process_sample(int chan, int sam, int upsample, struct demodulator_state_s *D); + + +/* Undo data scrambling for 9600 baud. */ + +static inline int descramble(int in, int *state) +{ + int out; + + out = (in ^ (*state >> 16) ^ (*state >> 11)) & 1; + *state = (*state << 1) | (in & 1); + return (out); +} diff --git a/dwgps.h b/dwgps.h new file mode 100644 index 0000000..78f821f --- /dev/null +++ b/dwgps.h @@ -0,0 +1,61 @@ + +/* dwgps.h */ + +#ifndef DWGPS_H +#define DWGPS_H 1 + + +#include +#include "config.h" /* for struct misc_config_s */ + + +/* + * Values for fix, equivalent to values from libgps. + * -2 = not initialized. + * -1 = error communicating with GPS receiver. + * 0 = nothing heard yet. + * 1 = had signal but lost it. + * 2 = 2D. + * 3 = 3D. + * + * Undefined float & double values are set to G_UNKNOWN. + * + */ + +enum dwfix_e { DWFIX_NOT_INIT= -2, DWFIX_ERROR= -1, DWFIX_NOT_SEEN=0, DWFIX_NO_FIX=1, DWFIX_2D=2, DWFIX_3D=3 }; + +typedef enum dwfix_e dwfix_t; + +typedef struct dwgps_info_s { + time_t timestamp; /* When last updated. System time. */ + dwfix_t fix; /* Quality of position fix. */ + double dlat; /* Latitude. Valid if fix >= 2. */ + double dlon; /* Longitude. Valid if fix >= 2. */ + float speed_knots; /* libgps uses meters/sec but we use GPS usual knots. */ + float track; /* What is difference between track and course? */ + float altitude; /* meters above mean sea level. Valid if fix == 3. */ +} dwgps_info_t; + + + + + +void dwgps_init (struct misc_config_s *pconfig, int debug); + +void dwgps_clear (dwgps_info_t *gpsinfo); + +dwfix_t dwgps_read (dwgps_info_t *gpsinfo); + +void dwgps_print (char *msg, dwgps_info_t *gpsinfo); + +void dwgps_term (void); + +void dwgps_set_data (dwgps_info_t *gpsinfo); + + +#endif /* DWGPS_H 1 */ + +/* end dwgps.h */ + + + diff --git a/dwgpsd.h b/dwgpsd.h new file mode 100644 index 0000000..4c0e0fd --- /dev/null +++ b/dwgpsd.h @@ -0,0 +1,22 @@ + +/* dwgpsd.h - For communicating with daemon */ + + + +#ifndef DWGPSD_H +#define DWGPSD_H 1 + +#include "config.h" + + +int dwgpsd_init (struct misc_config_s *pconfig, int debug); + +void dwgpsd_term (void); + +#endif + + +/* end dwgpsd.h */ + + + diff --git a/dwgpsnmea.h b/dwgpsnmea.h new file mode 100644 index 0000000..ffe5a12 --- /dev/null +++ b/dwgpsnmea.h @@ -0,0 +1,32 @@ + +/* dwgpsnmea.h - For reading NMEA sentences over serial port */ + + + +#ifndef DWGPSNMEA_H +#define DWGPSNMEA_H 1 + +#include "dwgps.h" /* for dwfix_t */ +#include "config.h" +#include "serial_port.h" /* for MYFDTYPE */ + + +int dwgpsnmea_init (struct misc_config_s *pconfig, int debug); + +MYFDTYPE dwgpsnmea_get_fd(char *wp_port_name, int speed); + +void dwgpsnmea_term (void); + + +dwfix_t dwgpsnmea_gprmc (char *sentence, int quiet, double *odlat, double *odlon, float *oknots, float *ocourse); + +dwfix_t dwgpsnmea_gpgga (char *sentence, int quiet, double *odlat, double *odlon, float *oalt, int *onsat); + + +#endif + + +/* end dwgpsnmea.h */ + + + diff --git a/dwsock.h b/dwsock.h new file mode 100644 index 0000000..986f6a2 --- /dev/null +++ b/dwsock.h @@ -0,0 +1,21 @@ + +/* dwsock.h - Socket helper functions. */ + +#ifndef DWSOCK_H +#define DWSOCK_H 1 + +#define DWSOCK_IPADDR_LEN 48 // Size of string to hold IPv4 or IPv6 address. + // I think 40 would be adequate but we'll make + // it a little larger just to be safe. + // Use INET6_ADDRSTRLEN (from netinet/in.h) instead? + +int dwsock_init (void); + +int dwsock_connect (char *hostname, char *port, char *description, int allow_ipv6, int debug, char ipaddr_str[DWSOCK_IPADDR_LEN]); + /* ipaddr_str needs to be at least SOCK_IPADDR_LEN bytes */ + +char *dwsock_ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t StringBufSize); + +void dwsock_close (int fd); + +#endif \ No newline at end of file diff --git a/encode_aprs.h b/encode_aprs.h new file mode 100644 index 0000000..dc7b8bd --- /dev/null +++ b/encode_aprs.h @@ -0,0 +1,17 @@ + +int encode_position (int messaging, int compressed, double lat, double lon, int ambiguity, int alt_ft, + char symtab, char symbol, + int power, int height, int gain, char *dir, + int course, int speed_knots, + float freq, float tone, float offset, + char *comment, + char *presult, size_t result_size); + +int encode_object (char *name, int compressed, time_t thyme, double lat, double lon, int ambiguity, + char symtab, char symbol, + int power, int height, int gain, char *dir, + int course, int speed_knots, + float freq, float tone, float offset, char *comment, + char *presult, size_t result_size); + +int encode_message (char *addressee, char *text, char *id, char *presult, size_t result_size); diff --git a/fcs_calc.h b/fcs_calc.h new file mode 100644 index 0000000..2e2b0ef --- /dev/null +++ b/fcs_calc.h @@ -0,0 +1,11 @@ + +/* fcs_calc.h */ + + +unsigned short fcs_calc (unsigned char *data, int len); + +unsigned short crc16 (unsigned char *data, int len, unsigned short seed); + +/* end fcs_calc.h */ + + diff --git a/fsk_filters.h b/fsk_filters.h new file mode 100644 index 0000000..81c4e9a --- /dev/null +++ b/fsk_filters.h @@ -0,0 +1,7 @@ +/* 1200 bits/sec with Audio sample rate = 11025 */ +/* Mark freq = 1200, Space freq = 2200 */ + +static const signed short m_sin_table[9] = { 0 , 7347 , 11257 , 9899 , 3909 , -3909 , -9899 , -11257 , -7347 }; +static const signed short m_cos_table[9] = { 11431 , 8756 , 1984 , -5715 , -10741 , -10741 , -5715 , 1984 , 8756 }; +static const signed short s_sin_table[9] = { 0 , 10950 , 6281 , -7347 , -10496 , 1327 , 11257 , 5130 , -8314 }; +static const signed short s_cos_table[9] = { 11431 , 3278 , -9550 , -8756 , 4527 , 11353 , 1984 , -10215 , -7844 }; diff --git a/fsk_gen_filter.h b/fsk_gen_filter.h new file mode 100644 index 0000000..e7e8fa6 --- /dev/null +++ b/fsk_gen_filter.h @@ -0,0 +1,15 @@ + + +#ifndef FSK_GEN_FILTER_H +#define FSK_GEN_FILTER_H 1 + +#include "audio.h" +#include "fsk_demod_state.h" + +void fsk_gen_filter (int samples_per_sec, + int baud, + int mark_freq, int space_freq, + char profile, + struct demodulator_state_s *D); + +#endif \ No newline at end of file diff --git a/gen_tone.h b/gen_tone.h new file mode 100644 index 0000000..bbe23b5 --- /dev/null +++ b/gen_tone.h @@ -0,0 +1,17 @@ +/* + * gen_tone.h + */ + + +int gen_tone_init (struct audio_s *pp, int amp, int gen_packets); + + +//int gen_tone_open (int nchan, int sample_rate, int bit_rate, int f1, int f2, int amp, char *fname); + +//int gen_tone_open_fd (int nchan, int sample_rate, int bit_rate, int f1, int f2, int amp, int fd) ; + +//int gen_tone_close (void); + +void tone_gen_put_bit (int chan, int dat); + +void gen_tone_put_sample (int chan, int a, int sam); \ No newline at end of file diff --git a/grm_sym.h b/grm_sym.h new file mode 100644 index 0000000..7a15041 --- /dev/null +++ b/grm_sym.h @@ -0,0 +1,501 @@ + +/* + * grm_sym.h + * + * Symbol codes for use in $PGRMWPL sentence. + * + * Copied from + * Garmin Device Interface Specification + * May 19, 2006 + * Drawing Number: 001-00063-00 Rev. C + */ + + +typedef unsigned short symbol_type_t; + +enum symbol_type_e +{ +/*--------------------------------------------------------------- +Marine symbols +---------------------------------------------------------------*/ +sym_anchor = 0, /* white anchor symbol */ +sym_bell = 1, /* white bell symbol */ +sym_diamond_grn = 2, /* green diamond symbol */ +sym_diamond_red = 3, /* red diamond symbol */ +sym_dive1 = 4, /* diver down flag 1 */ +sym_dive2 = 5, /* diver down flag 2 */ +sym_dollar = 6, /* white dollar symbol */ +sym_fish = 7, /* white fish symbol */ +sym_fuel = 8, /* white fuel symbol */ +sym_horn = 9, /* white horn symbol */ +sym_house = 10, /* white house symbol */ +sym_knife = 11, /* white knife & fork symbol */ +sym_light = 12, /* white light symbol */ +sym_mug = 13, /* white mug symbol */ +sym_skull = 14, /* white skull and crossbones symbol*/ +sym_square_grn = 15, /* green square symbol */ +sym_square_red = 16, /* red square symbol */ +sym_wbuoy = 17, /* white buoy waypoint symbol */ +sym_wpt_dot = 18, /* waypoint dot */ +sym_wreck = 19, /* white wreck symbol */ +sym_null = 20, /* null symbol (transparent) */ +sym_mob = 21, /* man overboard symbol */ +sym_buoy_ambr = 22, /* amber map buoy symbol */ +sym_buoy_blck = 23, /* black map buoy symbol */ +sym_buoy_blue = 24, /* blue map buoy symbol */ +sym_buoy_grn = 25, /* green map buoy symbol */ +sym_buoy_grn_red = 26, /* green/red map buoy symbol */ +sym_buoy_grn_wht = 27, /* green/white map buoy symbol */ +sym_buoy_orng = 28, /* orange map buoy symbol */ +sym_buoy_red = 29, /* red map buoy symbol */ +sym_buoy_red_grn = 30, /* red/green map buoy symbol */ +sym_buoy_red_wht = 31, /* red/white map buoy symbol */ +sym_buoy_violet = 32, /* violet map buoy symbol */ +sym_buoy_wht = 33, /* white map buoy symbol */ +sym_buoy_wht_grn = 34, /* white/green map buoy symbol */ +sym_buoy_wht_red = 35, /* white/red map buoy symbol */ +sym_dot = 36, /* white dot symbol */ +sym_rbcn = 37, /* radio beacon symbol */ +sym_boat_ramp = 150, /* boat ramp symbol */ +sym_camp = 151, /* campground symbol */ +sym_restrooms = 152, /* restrooms symbol */ +sym_showers = 153, /* shower symbol */ +sym_drinking_wtr = 154, /* drinking water symbol */ +sym_phone = 155, /* telephone symbol */ +sym_1st_aid = 156, /* first aid symbol */ +sym_info = 157, /* information symbol */ +sym_parking = 158, /* parking symbol */ +sym_park = 159, /* park symbol */ +sym_picnic = 160, /* picnic symbol */ +sym_scenic = 161, /* scenic area symbol */ +sym_skiing = 162, /* skiing symbol */ +sym_swimming = 163, /* swimming symbol */ +sym_dam = 164, /* dam symbol */ +sym_controlled = 165, /* controlled area symbol */ +sym_danger = 166, /* danger symbol */ +sym_restricted = 167, /* restricted area symbol */ +sym_null_2 = 168, /* null symbol */ +sym_ball = 169, /* ball symbol */ +sym_car = 170, /* car symbol */ +sym_deer = 171, /* deer symbol */ +sym_shpng_cart = 172, /* shopping cart symbol */ +sym_lodging = 173, /* lodging symbol */ +sym_mine = 174, /* mine symbol */ +sym_trail_head = 175, /* trail head symbol */ +sym_truck_stop = 176, /* truck stop symbol */ +sym_user_exit = 177, /* user exit symbol */ +sym_flag = 178, /* flag symbol */ +sym_circle_x = 179, /* circle with x in the center */ +sym_open_24hr = 180, /* open 24 hours symbol */ +sym_fhs_facility = 181, /* U Fishing Hot Spots(tm) Facility */ +sym_bot_cond = 182, /* Bottom Conditions */ +sym_tide_pred_stn = 183, /* Tide/Current Prediction Station */ +sym_anchor_prohib = 184, /* U anchor prohibited symbol */ +sym_beacon = 185, /* U beacon symbol */ +sym_coast_guard = 186, /* U coast guard symbol */ +sym_reef = 187, /* U reef symbol */ +sym_weedbed = 188, /* U weedbed symbol */ +sym_dropoff = 189, /* U dropoff symbol */ +sym_dock = 190, /* U dock symbol */ +sym_marina = 191, /* U marina symbol */ +sym_bait_tackle = 192, /* U bait and tackle symbol */ +sym_stump = 193, /* U stump symbol */ +/*--------------------------------------------------------------- +User customizable symbols +The values from sym_begin_custom to sym_end_custom inclusive are +reserved for the identification of user customizable symbols. +---------------------------------------------------------------*/ +sym_begin_custom = 7680, /* first user customizable symbol */ +sym_end_custom = 8191, /* last user customizable symbol */ +/*--------------------------------------------------------------- +Land symbols +---------------------------------------------------------------*/ +sym_is_hwy = 8192, /* interstate hwy symbol */ +sym_us_hwy = 8193, /* us hwy symbol */ +sym_st_hwy = 8194, /* state hwy symbol */ +sym_mi_mrkr = 8195, /* mile marker symbol */ +sym_trcbck = 8196, /* TracBack (feet) symbol */ +sym_golf = 8197, /* golf symbol */ +sym_sml_cty = 8198, /* small city symbol */ +sym_med_cty = 8199, /* medium city symbol */ +sym_lrg_cty = 8200, /* large city symbol */ +sym_freeway = 8201, /* intl freeway hwy symbol */ +sym_ntl_hwy = 8202, /* intl national hwy symbol */ +sym_cap_cty = 8203, /* capitol city symbol (star) */ +sym_amuse_pk = 8204, /* amusement park symbol */ +sym_bowling = 8205, /* bowling symbol */ +sym_car_rental = 8206, /* car rental symbol */ +sym_car_repair = 8207, /* car repair symbol */ +sym_fastfood = 8208, /* fast food symbol */ +sym_fitness = 8209, /* fitness symbol */ +sym_movie = 8210, /* movie symbol */ +sym_museum = 8211, /* museum symbol */ +sym_pharmacy = 8212, /* pharmacy symbol */ +sym_pizza = 8213, /* pizza symbol */ +sym_post_ofc = 8214, /* post office symbol */ +sym_rv_park = 8215, /* RV park symbol */ +sym_school = 8216, /* school symbol */ +sym_stadium = 8217, /* stadium symbol */ +sym_store = 8218, /* dept. store symbol */ +sym_zoo = 8219, /* zoo symbol */ +sym_gas_plus = 8220, /* convenience store symbol */ +sym_faces = 8221, /* live theater symbol */ +sym_ramp_int = 8222, /* ramp intersection symbol */ +sym_st_int = 8223, /* street intersection symbol */ +sym_weigh_sttn = 8226, /* inspection/weigh station symbol */ +sym_toll_booth = 8227, /* toll booth symbol */ +sym_elev_pt = 8228, /* elevation point symbol */ +sym_ex_no_srvc = 8229, /* exit without services symbol */ +sym_geo_place_mm = 8230, /* Geographic place name, man-made */ +sym_geo_place_wtr = 8231, /* Geographic place name, water */ +sym_geo_place_lnd = 8232, /* Geographic place name, land */ +sym_bridge = 8233, /* bridge symbol */ +sym_building = 8234, /* building symbol */ +sym_cemetery = 8235, /* cemetery symbol */ +sym_church = 8236, /* church symbol */ +sym_civil = 8237, /* civil location symbol */ +sym_crossing = 8238, /* crossing symbol */ +sym_hist_town = 8239, /* historical town symbol */ +sym_levee = 8240, /* levee symbol */ +sym_military = 8241, /* military location symbol */ +sym_oil_field = 8242, /* oil field symbol */ +sym_tunnel = 8243, /* tunnel symbol */ +sym_beach = 8244, /* beach symbol */ +sym_forest = 8245, /* forest symbol */ +sym_summit = 8246, /* summit symbol */ +sym_lrg_ramp_int = 8247, /* large ramp intersection symbol */ +sym_lrg_ex_no_srvc = 8248, /* large exit without services smbl */ +sym_badge = 8249, /* police/official badge symbol */ +sym_cards = 8250, /* gambling/casino symbol */ +sym_snowski = 8251, /* snow skiing symbol */ +sym_iceskate = 8252, /* ice skating symbol */ +sym_wrecker = 8253, /* tow truck (wrecker) symbol */ +sym_border = 8254, /* border crossing (port of entry) */ +sym_geocache = 8255, /* geocache location */ +sym_geocache_fnd = 8256, /* found geocache */ +sym_cntct_smiley = 8257, /* Rino contact symbol, "smiley" */ +sym_cntct_ball_cap = 8258, /* Rino contact symbol, "ball cap" */ +sym_cntct_big_ears = 8259, /* Rino contact symbol, "big ear" */ +sym_cntct_spike = 8260, /* Rino contact symbol, "spike" */ +sym_cntct_goatee = 8261, /* Rino contact symbol, "goatee" */ +sym_cntct_afro = 8262, /* Rino contact symbol, "afro" */ +sym_cntct_dreads = 8263, /* Rino contact symbol, "dreads" */ +sym_cntct_female1 = 8264, /* Rino contact symbol, "female 1" */ +sym_cntct_female2 = 8265, /* Rino contact symbol, "female 2" */ +sym_cntct_female3 = 8266, /* Rino contact symbol, "female 3" */ +sym_cntct_ranger = 8267, /* Rino contact symbol, "ranger" */ +sym_cntct_kung_fu = 8268, /* Rino contact symbol, "kung fu" */ +sym_cntct_sumo = 8269, /* Rino contact symbol, "sumo" */ +sym_cntct_pirate = 8270, /* Rino contact symbol, "pirate" */ +sym_cntct_biker = 8271, /* Rino contact symbol, "biker" */ +sym_cntct_alien = 8272, /* Rino contact symbol, "alien" */ +sym_cntct_bug = 8273, /* Rino contact symbol, "bug" */ +sym_cntct_cat = 8274, /* Rino contact symbol, "cat" */ +sym_cntct_dog = 8275, /* Rino contact symbol, "dog" */ +sym_cntct_pig = 8276, /* Rino contact symbol, "pig" */ +sym_hydrant = 8282, /* water hydrant symbol */ +sym_flag_blue = 8284, /* blue flag symbol */ +sym_flag_green = 8285, /* green flag symbol */ +sym_flag_red = 8286, /* red flag symbol */ +sym_pin_blue = 8287, /* blue pin symbol */ +sym_pin_green = 8288, /* green pin symbol */ +sym_pin_red = 8289, /* red pin symbol */ +sym_block_blue = 8290, /* blue block symbol */ +sym_block_green = 8291, /* green block symbol */ +sym_block_red = 8292, /* red block symbol */ +sym_bike_trail = 8293, /* bike trail symbol */ +sym_circle_red = 8294, /* red circle symbol */ +sym_circle_green = 8295, /* green circle symbol */ +sym_circle_blue = 8296, /* blue circle symbol */ +sym_diamond_blue = 8299, /* blue diamond symbol */ +sym_oval_red = 8300, /* red oval symbol */ +sym_oval_green = 8301, /* green oval symbol */ +sym_oval_blue = 8302, /* blue oval symbol */ +sym_rect_red = 8303, /* red rectangle symbol */ +sym_rect_green = 8304, /* green rectangle symbol */ +sym_rect_blue = 8305, /* blue rectangle symbol */ +sym_square_blue = 8308, /* blue square symbol */ +sym_letter_a_red = 8309, /* red letter 'A' symbol */ +sym_letter_b_red = 8310, /* red letter 'B' symbol */ +sym_letter_c_red = 8311, /* red letter 'C' symbol */ +sym_letter_d_red = 8312, /* red letter 'D' symbol */ +sym_letter_a_green = 8313, /* green letter 'A' symbol */ +sym_letter_c_green = 8314, /* green letter 'C' symbol */ +sym_letter_b_green = 8315, /* green letter 'B' symbol */ +sym_letter_d_green = 8316, /* green letter 'D' symbol */ +sym_letter_a_blue = 8317, /* blue letter 'A' symbol */ +sym_letter_b_blue = 8318, /* blue letter 'B' symbol */ +sym_letter_c_blue = 8319, /* blue letter 'C' symbol */ +sym_letter_d_blue = 8320, /* blue letter 'D' symbol */ +sym_number_0_red = 8321, /* red number '0' symbol */ +sym_number_1_red = 8322, /* red number '1' symbol */ +sym_number_2_red = 8323, /* red number '2' symbol */ +sym_number_3_red = 8324, /* red number '3' symbol */ +sym_number_4_red = 8325, /* red number '4' symbol */ +sym_number_5_red = 8326, /* red number '5' symbol */ +sym_number_6_red = 8327, /* red number '6' symbol */ +sym_number_7_red = 8328, /* red number '7' symbol */ +sym_number_8_red = 8329, /* red number '8' symbol */ +sym_number_9_red = 8330, /* red number '9' symbol */ +sym_number_0_green = 8331, /* green number '0' symbol */ +sym_number_1_green = 8332, /* green number '1' symbol */ +sym_number_2_green = 8333, /* green number '2' symbol */ +sym_number_3_green = 8334, /* green number '3' symbol */ +sym_number_4_green = 8335, /* green number '4' symbol */ +sym_number_5_green = 8336, /* green number '5' symbol */ +sym_number_6_green = 8337, /* green number '6' symbol */ +sym_number_7_green = 8338, /* green number '7' symbol */ +sym_number_8_green = 8339, /* green number '8' symbol */ +sym_number_9_green = 8340, /* green number '9' symbol */ +sym_number_0_blue = 8341, /* blue number '0' symbol */ +sym_number_1_blue = 8342, /* blue number '1' symbol */ +sym_number_2_blue = 8343, /* blue number '2' symbol */ +sym_number_3_blue = 8344, /* blue number '3' symbol */ +sym_number_4_blue = 8345, /* blue number '4' symbol */ +sym_number_5_blue = 8346, /* blue number '5' symbol */ +sym_number_6_blue = 8347, /* blue number '6' symbol */ +sym_number_7_blue = 8348, /* blue number '7' symbol */ +sym_number_8_blue = 8349, /* blue number '8' symbol */ +sym_number_9_blue = 8350, /* blue number '9' symbol */ +sym_triangle_blue = 8351, /* blue triangle symbol */ +sym_triangle_green = 8352, /* green triangle symbol */ +sym_triangle_red = 8353, /* red triangle symbol */ +sym_food_asian = 8359, /* asian food symbol */ +sym_food_deli = 8360, /* deli symbol */ +sym_food_italian = 8361, /* italian food symbol */ +sym_food_seafood = 8362, /* seafood symbol */ +sym_food_steak = 8363, /* steak symbol */ +/*--------------------------------------------------------------- +Aviation symbols +---------------------------------------------------------------*/ +sym_airport = 16384, /* airport symbol */ +sym_int = 16385, /* intersection symbol */ +sym_ndb = 16386, /* non-directional beacon symbol */ +sym_vor = 16387, /* VHF omni-range symbol */ +sym_heliport = 16388, /* heliport symbol */ +sym_private = 16389, /* private field symbol */ +sym_soft_fld = 16390, /* soft field symbol */ +sym_tall_tower = 16391, /* tall tower symbol */ +sym_short_tower = 16392, /* short tower symbol */ +sym_glider = 16393, /* glider symbol */ +sym_ultralight = 16394, /* ultralight symbol */ +sym_parachute = 16395, /* parachute symbol */ +sym_vortac = 16396, /* VOR/TACAN symbol */ +sym_vordme = 16397, /* VOR-DME symbol */ +sym_faf = 16398, /* first approach fix */ +sym_lom = 16399, /* localizer outer marker */ +sym_map = 16400, /* missed approach point */ +sym_tacan = 16401, /* TACAN symbol */ +sym_seaplane = 16402, /* Seaplane Base */ +}; + + + +/* + * Mapping from APRS symbols to Garmin. + */ + +// TODO: NEEDS MORE WORK!!! + + +#define SYMTAB_SIZE 95 + +#define sym_default sym_diamond_grn + + +static const symbol_type_t grm_primary_symtab[SYMTAB_SIZE] = { + + sym_default, // 00 --no-symbol-- + sym_cntct_ranger, // ! 01 Police, Sheriff + sym_default, // " 02 reserved (was rain) + sym_rbcn, // # 03 DIGI (white center) + sym_phone, // $ 04 PHONE + sym_rbcn, // % 05 DX CLUSTER + sym_rbcn, // & 06 HF GATEway + sym_glider, // ' 07 Small AIRCRAFT + sym_rbcn, // ( 08 Mobile Satellite Station + sym_default, // ) 09 Wheelchair (handicapped) + sym_car, // * 10 SnowMobile + sym_1st_aid, // + 11 Red Cross + sym_cntct_ball_cap, // , 12 Boy Scouts + sym_house, // - 13 House QTH (VHF) + sym_default, // . 14 X + sym_default, // / 15 Red Dot + sym_default, // 0 16 # circle (obsolete) + sym_default, // 1 17 TBD + sym_default, // 2 18 TBD + sym_default, // 3 19 TBD + sym_default, // 4 20 TBD + sym_default, // 5 21 TBD + sym_default, // 6 22 TBD + sym_default, // 7 23 TBD + sym_default, // 8 24 TBD + sym_default, // 9 25 TBD + sym_default, // : 26 FIRE + sym_camp, // ; 27 Campground (Portable ops) + sym_cntct_biker, // < 28 Motorcycle + sym_default, // = 29 RAILROAD ENGINE + sym_car, // > 30 CAR + sym_default, // ? 31 SERVER for Files + sym_default, // @ 32 HC FUTURE predict (dot) + sym_1st_aid, // A 33 Aid Station + sym_rbcn, // B 34 BBS or PBBS + sym_boat_ramp, // C 35 Canoe + sym_default, // D 36 + sym_default, // E 37 EYEBALL (Eye catcher!) + sym_default, // F 38 Farm Vehicle (tractor) + sym_default, // G 39 Grid Square (6 digit) + sym_lodging, // H 40 HOTEL (blue bed symbol) + sym_rbcn, // I 41 TcpIp on air network stn + sym_default, // J 42 + sym_school, // K 43 School + sym_default, // L 44 PC user + sym_default, // M 45 MacAPRS + sym_default, // N 46 NTS Station + sym_parachute, // O 47 BALLOON + sym_cntct_ranger, // P 48 Police + sym_default, // Q 49 TBD + sym_rv_park, // R 50 REC. VEHICLE + sym_glider, // S 51 SHUTTLE + sym_default, // T 52 SSTV + sym_car, // U 53 BUS + sym_cntct_biker, // V 54 ATV + sym_default, // W 55 National WX Service Site + sym_default, // X 56 HELO + sym_default, // Y 57 YACHT (sail) + sym_default, // Z 58 WinAPRS + sym_cntct_smiley, // [ 59 Human/Person (HT) + sym_triangle_green, // \ 60 TRIANGLE(DF station) + sym_default, // ] 61 MAIL/PostOffice(was PBBS) + sym_glider, // ^ 62 LARGE AIRCRAFT + sym_default, // _ 63 WEATHER Station (blue) + sym_rbcn, // ` 64 Dish Antenna + sym_1st_aid, // a 65 AMBULANCE + sym_cntct_biker, // b 66 BIKE + sym_default, // c 67 Incident Command Post + sym_hydrant, // d 68 Fire dept + sym_deer, // e 69 HORSE (equestrian) + sym_hydrant, // f 70 FIRE TRUCK + sym_glider, // g 71 Glider + sym_1st_aid, // h 72 HOSPITAL + sym_default, // i 73 IOTA (islands on the air) + sym_car, // j 74 JEEP + sym_car, // k 75 TRUCK + sym_default, // l 76 Laptop + sym_rbcn, // m 77 Mic-E Repeater + sym_default, // n 78 Node (black bulls-eye) + sym_default, // o 79 EOC + sym_cntct_dog, // p 80 ROVER (puppy, or dog) + sym_default, // q 81 GRID SQ shown above 128 m + sym_rbcn, // r 82 Repeater + sym_default, // s 83 SHIP (pwr boat) + sym_truck_stop, // t 84 TRUCK STOP + sym_truck_stop, // u 85 TRUCK (18 wheeler) + sym_car, // v 86 VAN + sym_drinking_wtr, // w 87 WATER station + sym_default, // x 88 xAPRS (Unix) + sym_tall_tower, // y 89 YAGI @ QTH + sym_default, // z 90 TBD + sym_default, // { 91 + sym_default, // | 92 TNC Stream Switch + sym_default, // } 93 + sym_default }; // ~ 94 TNC Stream Switch + +static const symbol_type_t grm_alternate_symtab[SYMTAB_SIZE] = { + + sym_default, // 00 --no-symbol-- + sym_default, // ! 01 EMERGENCY (!) + sym_default, // " 02 reserved + sym_default, // # 03 OVERLAY DIGI (green star) + sym_default, // $ 04 Bank or ATM (green box) + sym_default, // % 05 Power Plant with overlay + sym_rbcn, // & 06 I=Igte IGate R=RX T=1hopTX 2=2hopTX + sym_default, // ' 07 Crash (& now Incident sites) + sym_default, // ( 08 CLOUDY (other clouds w ovrly) + sym_hydrant, // ) 09 Firenet MEO, MODIS Earth Obs. + sym_default, // * 10 SNOW (& future ovrly codes) + sym_default, // + 11 Church + sym_cntct_female1, // , 12 Girl Scouts + sym_house, // - 13 House (H=HF) (O = Op Present) + sym_default, // . 14 Ambiguous (Big Question mark) + sym_default, // / 15 Waypoint Destination + sym_default, // 0 16 CIRCLE (E/I/W=IRLP/Echolink/WIRES) + sym_default, // 1 17 + sym_default, // 2 18 + sym_default, // 3 19 + sym_default, // 4 20 + sym_default, // 5 21 + sym_default, // 6 22 + sym_default, // 7 23 + sym_default, // 8 24 802.11 or other network node + sym_default, // 9 25 Gas Station (blue pump) + sym_default, // : 26 Hail (& future ovrly codes) + sym_park, // ; 27 Park/Picnic area + sym_default, // < 28 ADVISORY (one WX flag) + sym_rbcn, // = 29 APRStt Touchtone (DTMF users) + sym_car, // > 30 OVERLAID CAR + sym_default, // ? 31 INFO Kiosk (Blue box with ?) + sym_default, // @ 32 HURRICANE/Trop-Storm + sym_default, // A 33 overlayBOX DTMF & RFID & XO + sym_default, // B 34 Blwng Snow (& future codes) + sym_coast_guard, // C 35 Coast Guard + sym_default, // D 36 Drizzle (proposed APRStt) + sym_default, // E 37 Smoke (& other vis codes) + sym_default, // F 38 Freezng rain (&future codes) + sym_default, // G 39 Snow Shwr (& future ovrlys) + sym_default, // H 40 Haze (& Overlay Hazards) + sym_default, // I 41 Rain Shower + sym_default, // J 42 Lightning (& future ovrlys) + sym_rbcn, // K 43 Kenwood HT (W) + sym_light, // L 44 Lighthouse + sym_default, // M 45 MARS (A=Army,N=Navy,F=AF) + sym_default, // N 46 Navigation Buoy + sym_default, // O 47 Rocket + sym_default, // P 48 Parking + sym_default, // Q 49 QUAKE + sym_default, // R 50 Restaurant + sym_rbcn, // S 51 Satellite/Pacsat + sym_default, // T 52 Thunderstorm + sym_default, // U 53 SUNNY + sym_default, // V 54 VORTAC Nav Aid + sym_default, // W 55 # NWS site (NWS options) + sym_pharmacy, // X 56 Pharmacy Rx (Apothicary) + sym_rbcn, // Y 57 Radios and devices + sym_default, // Z 58 + sym_default, // [ 59 W.Cloud (& humans w Ovrly) + sym_default, // \ 60 New overlayable GPS symbol + sym_default, // ] 61 + sym_glider, // ^ 62 # Aircraft (shows heading) + sym_default, // _ 63 # WX site (green digi) + sym_default, // ` 64 Rain (all types w ovrly) + sym_default, // a 65 ARRL, ARES, WinLINK + sym_default, // b 66 Blwng Dst/Snd (& others) + sym_default, // c 67 CD triangle RACES/SATERN/etc + sym_default, // d 68 DX spot by callsign + sym_default, // e 69 Sleet (& future ovrly codes) + sym_default, // f 70 Funnel Cloud + sym_default, // g 71 Gale Flags + sym_default, // h 72 Store. or HAMFST Hh=HAM store + sym_default, // i 73 BOX or points of Interest + sym_default, // j 74 WorkZone (Steam Shovel) + sym_car, // k 75 Special Vehicle SUV,ATV,4x4 + sym_default, // l 76 Areas (box,circles,etc) + sym_default, // m 77 Value Sign (3 digit display) + sym_default, // n 78 OVERLAY TRIANGLE + sym_default, // o 79 small circle + sym_default, // p 80 Prtly Cldy (& future ovrlys) + sym_default, // q 81 + sym_restrooms, // r 82 Restrooms + sym_default, // s 83 OVERLAY SHIP/boat (top view) + sym_default, // t 84 Tornado + sym_car, // u 85 OVERLAID TRUCK + sym_car, // v 86 OVERLAID Van + sym_default, // w 87 Flooding + sym_wreck, // x 88 Wreck or Obstruction ->X<- + sym_default, // y 89 Skywarn + sym_default, // z 90 OVERLAID Shelter + sym_default, // { 91 Fog (& future ovrly codes) + sym_default, // | 92 TNC Stream Switch + sym_default, // } 93 + sym_default }; // ~ 94 TNC Stream Switch + diff --git a/hdlc_send.h b/hdlc_send.h new file mode 100644 index 0000000..4f8a105 --- /dev/null +++ b/hdlc_send.h @@ -0,0 +1,17 @@ + +/* hdlc_send.h */ + +// In version 1.7 an extra layer of abstraction was added here. +// Rather than calling hdlc_send_frame, we now use another function +// which sends AX.25, FX.25, or IL2P depending on + +#include "ax25_pad.h" +#include "audio.h" + +int layer2_send_frame (int chan, packet_t pp, int bad_fcs, struct audio_s *audio_config_p); + +int layer2_preamble_postamble (int chan, int flags, int finish, struct audio_s *audio_config_p); + +/* end hdlc_send.h */ + + diff --git a/igate.h b/igate.h new file mode 100644 index 0000000..8203ac7 --- /dev/null +++ b/igate.h @@ -0,0 +1,128 @@ + +/*---------------------------------------------------------------------------- + * + * Name: igate.h + * + * Purpose: Interface to the Internet Gateway functions. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef IGATE_H +#define IGATE_H 1 + + +#include "ax25_pad.h" +#include "digipeater.h" +#include "audio.h" + + +#define DEFAULT_IGATE_PORT 14580 + + + +struct igate_config_s { + +/* + * For logging into the IGate server. + */ + char t2_server_name[40]; /* Tier 2 IGate server name. */ + + int t2_server_port; /* Typically 14580. */ + + char t2_login[AX25_MAX_ADDR_LEN];/* e.g. WA9XYZ-15 */ + /* Note that the ssid could be any two alphanumeric */ + /* characters not just 1 thru 15. */ + /* Could be same or different than the radio call(s). */ + /* Not sure what the consequences would be. */ + + char t2_passcode[8]; /* Max. 5 digits. Could be "-1". */ + + char *t2_filter; /* Optional filter for IS -> RF direction. */ + /* This is the "server side" filter. */ + /* A better name would be subscription or something */ + /* like that because we can only ask for more. */ + +/* + * For transmitting. + */ + int tx_chan; /* Radio channel for transmitting. */ + /* 0=first, etc. -1 for none. */ + /* Presently IGate can transmit on only a single channel. */ + /* A future version might generalize this. */ + /* Each transmit channel would have its own client side filtering. */ + + char tx_via[80]; /* VIA path for transmitting third party packets. */ + /* Usual text representation. */ + /* Must start with "," if not empty so it can */ + /* simply be inserted after the destination address. */ + + int max_digi_hops; /* Maximum number of digipeater hops possible for via path. */ + /* Derived from the SSID when last character of address is a digit. */ + /* e.g. "WIDE1-1,WIDE5-2" would be 3. */ + /* This is useful to know so we can determine how many */ + /* stations we might be able to reach. */ + + int tx_limit_1; /* Max. packets to transmit in 1 minute. */ + + int tx_limit_5; /* Max. packets to transmit in 5 minutes. */ + + int igmsp; /* Number of message sender position reports to allow. */ + /* Common practice is to default to 1. */ + /* We allow additional flexibility of 0 to disable feature */ + /* or a small number to allow more. */ + +/* + * Receiver to IS data options. + */ + int rx2ig_dedupe_time; /* seconds. 0 to disable. */ + +/* + * Special SATgate mode to delay packets heard directly. + */ + int satgate_delay; /* seconds. 0 to disable. */ +}; + + +#define IGATE_TX_LIMIT_1_DEFAULT 6 +#define IGATE_TX_LIMIT_1_MAX 20 + +#define IGATE_TX_LIMIT_5_DEFAULT 20 +#define IGATE_TX_LIMIT_5_MAX 80 + +#define IGATE_RX2IG_DEDUPE_TIME 0 /* Issue 85. 0 means disable dupe checking in RF>IS direction. */ + /* See comments in rx_to_ig_remember & rx_to_ig_allow. */ + /* Currently there is no configuration setting to change this. */ + +#define DEFAULT_SATGATE_DELAY 10 +#define MIN_SATGATE_DELAY 5 +#define MAX_SATGATE_DELAY 30 + + +/* Call this once at startup */ + +void igate_init (struct audio_s *p_audio_config, struct igate_config_s *p_igate_config, struct digi_config_s *p_digi_config, int debug_level); + +/* Call this with each packet received from the radio. */ + +void igate_send_rec_packet (int chan, packet_t recv_pp); + +/* This when digipeater transmits. Set bydigi to 1 . */ + +void ig_to_tx_remember (packet_t pp, int chan, int bydigi); + + + +/* Get statistics for IGATE status beacon. */ + +int igate_get_msg_cnt (void); + +int igate_get_pkt_cnt (void); + +int igate_get_upl_cnt (void); + +int igate_get_dnl_cnt (void); + + + +#endif diff --git a/il2p.c b/il2p.c index bea8521..6b3ae52 100644 --- a/il2p.c +++ b/il2p.c @@ -48,6 +48,7 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include "UZ7HOStuff.h" void Debugprintf(const char * format, ...); +int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count); #define MAX_ADEVS 3 @@ -69,6 +70,10 @@ void Debugprintf(const char * format, ...); #define max(x, y) ((x) > (y) ? (x) : (y)) #define min(x, y) ((x) < (y) ? (x) : (y)) +extern int nPhases[4][16][nr_emph + 1]; +extern float Phases[4][16][nr_emph + 1][4096]; +extern float Mags[4][16][nr_emph + 1][4096]; + /* For option to try fixing frames with bad CRC. */ typedef enum retry_e { @@ -106,6 +111,7 @@ int MaxMagIndex = 0; #include // for uint64_t +extern unsigned int pskStates[4]; /* Reed-Solomon codec control block */ struct rs { @@ -702,7 +708,7 @@ packet_t ax25_new(void) //if (new_count > delete_count + 100) { if (new_count > delete_count + 256) { - Debugprintf("Report to WB2OSZ - Memory leak for packet objects. new=%d, delete=%d\n", new_count, delete_count); + Debugprintf("Memory leak for packet objects. new=%d, delete=%d\n", new_count, delete_count); #if AX25MEMDEBUG #endif } @@ -1004,7 +1010,8 @@ void multi_modem_process_rec_packet(int snd_ch, int subchan, int slice, packet_t struct TDetector_t * pDET = &DET[emph][subchan]; string * data = newString(); - char Mode[16] = "IL2P"; + char Mode[32] = "IL2P"; + int Quality = 0; sprintf(Mode, "IL2P %d", centreFreq); @@ -1038,6 +1045,12 @@ void multi_modem_process_rec_packet(int snd_ch, int subchan, int slice, packet_t string * xx = newString(); memset(xx->Data, 0, 16); + if (pskStates[snd_ch]) + { + Quality = SMUpdatePhaseConstellation(snd_ch, &Phases[snd_ch][subchan][slice][0], &Mags[snd_ch][subchan][slice][0], pskStates[snd_ch], nPhases[snd_ch][subchan][slice]); + sprintf(Mode, "%s][Q%d", Mode, Quality); + } + Add(&detect_list_c[snd_ch], xx); Add(&detect_list[snd_ch], data); @@ -1045,6 +1058,8 @@ void multi_modem_process_rec_packet(int snd_ch, int subchan, int slice, packet_t // sprintf(Mode, "IP2P-%d", retries); stringAdd(xx, Mode, strlen(Mode)); + + return; } @@ -2256,6 +2271,7 @@ int il2p_decode_rs(unsigned char *rec_block, int data_size, int num_parity, unsi int derrlocs[FX25_MAX_CHECK]; // Half would probably be OK. int derrors = DECODE_RS(il2p_find_rs(num_parity), rs_block, derrlocs, 0); + memcpy(out, rs_block + sizeof(rs_block) - n, data_size); if (il2p_get_debug() >= 3) { @@ -2753,7 +2769,6 @@ void fx_hex_dump(unsigned char *p, int len) int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout) { - // Can a type 1 header be used? unsigned char hdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; @@ -2772,6 +2787,7 @@ int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout) } // Payload is AX.25 info part. + unsigned char *pinfo; int info_len; info_len = ax25_get_info(pp, &pinfo); @@ -3583,6 +3599,12 @@ int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_descram int e = il2p_decode_rs(rec_hdr, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, corrected); + if (e > 1) // only have 2 rs bytes so can only detect 1 error + { + Debugprintf("Header correction seems ok but errors > 1"); + return -1; + } + il2p_descramble_block(corrected, corrected_descrambled_hdr, IL2P_HEADER_SIZE); return (e); @@ -3857,38 +3879,7 @@ int il2p_decode_payload(unsigned char *received, int payload_size, int max_fec, // end il2p_payload.c - - -struct il2p_context_s { - - enum { IL2P_SEARCHING = 0, IL2P_HEADER, IL2P_PAYLOAD, IL2P_DECODE } state; - - unsigned int acc; // Accumulate most recent 24 bits for sync word matching. - // Lower 8 bits are also used for accumulating bytes for - // the header and payload. - - int bc; // Bit counter so we know when a complete byte has been accumulated. - - int polarity; // 1 if opposite of expected polarity. - - unsigned char shdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; - // Scrambled header as received over the radio. Includes parity. - int hc; // Number if bytes placed in above. - - unsigned char uhdr[IL2P_HEADER_SIZE]; // Header after FEC and unscrambling. - - int eplen; // Encoded payload length. This is not the nuumber from - // from the header but rather the number of encoded bytes to gather. - - unsigned char spayload[IL2P_MAX_ENCODED_PAYLOAD_SIZE]; - // Scrambled and encoded payload as received over the radio. - int pc; // Number of bytes placed in above. - - int corrected; // Number of symbols corrected by RS FEC. -}; - -static struct il2p_context_s *il2p_context[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS]; - +struct il2p_context_s *il2p_context[4][16][3]; /*********************************************************************************** @@ -3948,7 +3939,8 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit) F->state = IL2P_HEADER; F->bc = 0; F->hc = 0; - + nPhases[chan][subchan][slice] = 0; + // Determine Centre Freq centreFreq[chan] = GuessCentreFreq(chan); @@ -3962,6 +3954,7 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit) F->bc = 0; F->hc = 0; centreFreq[chan] = GuessCentreFreq(chan); + nPhases[chan][subchan][slice] = 0; } break; @@ -4005,6 +3998,12 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit) plprop.large_block_count, plprop.large_block_size, plprop.parity_symbols_per_block); } + if (len > 340) + { + Debugprintf("Packet too big for QtSM"); + // F->state = IL2P_SEARCHING; + // return; + } if (F->eplen >= 1) { // Need to gather payload. F->pc = 0; F->state = IL2P_PAYLOAD; @@ -4277,12 +4276,12 @@ string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity) int preamblecount; unsigned char preamble[1024]; - encoded[0] = (IL2P_SYNC_WORD >> 16) & 0xff; encoded[1] = (IL2P_SYNC_WORD >> 8) & 0xff; encoded[2] = (IL2P_SYNC_WORD) & 0xff; int elen = il2p_encode_frame(pp, max_fec, encoded + IL2P_SYNC_WORD_SIZE); + if (elen <= 0) { Debugprintf("IL2P: Unable to encode frame into IL2P.\n"); return (packet); @@ -4301,14 +4300,27 @@ string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity) // Try using preaamble for txdelay - preamblecount = (txdelay[chan] * tx_baudrate[chan]) / 8000; // 8 for bits, 1000 for mS + // Nino now uses 00 as preamble for QPSK - if (preamblecount > 1024) - preamblecount = 1024; + // We don't need txdelay between frames in one transmission - memset(preamble, IL2P_PREAMBLE, preamblecount); - stringAdd(packet, preamble, preamblecount); + if (Continuation[chan] == 0) + { + preamblecount = (txdelay[chan] * tx_bitrate[chan]) / 8000; // 8 for bits, 1000 for mS + + if (preamblecount > 1024) + preamblecount = 1024; + + if (pskStates[chan]) // PSK Modes + memset(preamble, 01, preamblecount); + else + memset(preamble, IL2P_PREAMBLE, preamblecount); + + stringAdd(packet, preamble, preamblecount); + Continuation[chan] = 1; + } + stringAdd(packet, encoded, elen); tx_fx25_size[chan] = packet->Length * 8; @@ -4351,7 +4363,6 @@ string * fill_il2p_data(int snd_ch, string * data) string * result; packet_t pp = ax25_new(); - // Call il2p_send_frame to build the bit stream pp->frame_len = data->Length - 2; // Included CRC diff --git a/kiss.h b/kiss.h new file mode 100644 index 0000000..1dc40da --- /dev/null +++ b/kiss.h @@ -0,0 +1,24 @@ + +/* + * Name: kiss.h + * + * This is for the pseudo terminal KISS interface. + */ + + +#include "ax25_pad.h" /* for packet_t */ + +#include "config.h" + +#include "kiss_frame.h" // for struct kissport_status_s + + +void kisspt_init (struct misc_config_s *misc_config); + +void kisspt_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, + struct kissport_status_s *notused1, int notused2); + +void kisspt_set_debug (int n); + + +/* end kiss.h */ diff --git a/kiss_frame.h b/kiss_frame.h new file mode 100644 index 0000000..941f5c0 --- /dev/null +++ b/kiss_frame.h @@ -0,0 +1,126 @@ + +/* kiss_frame.h */ + +#ifndef KISS_FRAME_H +#define KISS_FRAME_H + + +#include "audio.h" /* for struct audio_s */ + + +/* + * The first byte of a KISS frame has: + * channel in upper nybble. + * command in lower nybble. + */ + +#define KISS_CMD_DATA_FRAME 0 +#define KISS_CMD_TXDELAY 1 +#define KISS_CMD_PERSISTENCE 2 +#define KISS_CMD_SLOTTIME 3 +#define KISS_CMD_TXTAIL 4 +#define KISS_CMD_FULLDUPLEX 5 +#define KISS_CMD_SET_HARDWARE 6 +#define XKISS_CMD_DATA 12 // Not supported. http://he.fi/pub/oh7lzb/bpq/multi-kiss.pdf +#define XKISS_CMD_POLL 14 // Not supported. +#define KISS_CMD_END_KISS 15 + + + +/* + * Special characters used by SLIP protocol. + */ + +#define FEND 0xC0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + + + +enum kiss_state_e { + KS_SEARCHING = 0, /* Looking for FEND to start KISS frame. */ + /* Must be 0 so we can simply zero whole structure to initialize. */ + KS_COLLECTING}; /* In process of collecting KISS frame. */ + + +#define MAX_KISS_LEN 2048 /* Spec calls for at least 1024. */ + /* Might want to make it longer to accommodate */ + /* maximum packet length. */ + +#define MAX_NOISE_LEN 100 + +typedef struct kiss_frame_s { + + enum kiss_state_e state; + + unsigned char kiss_msg[MAX_KISS_LEN]; + /* Leading FEND is optional. */ + /* Contains escapes and ending FEND. */ + int kiss_len; + + unsigned char noise[MAX_NOISE_LEN]; + int noise_len; + +} kiss_frame_t; + + +// This is used only for TCPKISS but it put in kissnet.h, +// there would be a circular dependency between the two header files. +// Each KISS TCP port has its own status block. + +struct kissport_status_s { + + struct kissport_status_s *pnext; // To next in list. + + volatile int arg2; // temp for passing second arg into + // kissnet_listen_thread + + int tcp_port; // default 8001 + + int chan; // Radio channel for this tcp port. + // -1 for all. + + // The default is a limit of 3 client applications at the same time. + // You can increase the limit by changing the line below. + // A larger number consumes more resources so don't go crazy by making it larger than needed. + // TODO: Should this be moved to direwolf.h so max number of audio devices + // client apps are in the same place? + +#define MAX_NET_CLIENTS 3 + + int client_sock[MAX_NET_CLIENTS]; + /* File descriptor for socket for */ + /* communication with client application. */ + /* Set to -1 if not connected. */ + /* (Don't use SOCKET type because it is unsigned.) */ + + kiss_frame_t kf[MAX_NET_CLIENTS]; + /* Accumulated KISS frame and state of decoder. */ +}; + + + +#ifndef KISSUTIL +void kiss_frame_init (struct audio_s *pa); +#endif + +int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out); + +int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out); + +void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, struct kissport_status_s *kps, int client, + void (*sendfun)(int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *onlykps, int onlyclient)); + +typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t; + +void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug, struct kissport_status_s *kps, int client, + void (*sendfun)(int chan, int kiss_cmd, unsigned char *fbuf, int flen, struct kissport_status_s *onlykps, int onlyclient)); + +void kiss_debug_print (fromto_t fromto, char *special, unsigned char *pmsg, int msg_len); + + +#endif // KISS_FRAME_H + + +/* end kiss_frame.h */ diff --git a/kiss_mode.c b/kiss_mode.c index eb21953..5adba5d 100644 --- a/kiss_mode.c +++ b/kiss_mode.c @@ -473,7 +473,7 @@ void sendAckModeAcks(int snd_ch) // Socket to reply to is on end - Msg += (temp->Length - 4); + Msg += (temp->Length - sizeof(void *)); memcpy(&socket, Msg, sizeof(void *)); diff --git a/kissnet.h b/kissnet.h new file mode 100644 index 0000000..469e4e6 --- /dev/null +++ b/kissnet.h @@ -0,0 +1,29 @@ + +/* + * Name: kissnet.h + */ + +#ifndef KISSNET_H +#define KISSNET_H + +#include "ax25_pad.h" /* for packet_t */ + +#include "config.h" + +#include "kiss_frame.h" + + + +void kissnet_init (struct misc_config_s *misc_config); + +void kissnet_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, + struct kissport_status_s *onlykps, int onlyclient); + +void kiss_net_set_debug (int n); + +void kissnet_copy (unsigned char *kiss_msg, int kiss_len, int chan, int cmd, struct kissport_status_s *from_kps, int from_client); + + +#endif // KISSNET_H + +/* end kissnet.h */ diff --git a/kissserial.h b/kissserial.h new file mode 100644 index 0000000..44fb3c3 --- /dev/null +++ b/kissserial.h @@ -0,0 +1,23 @@ + +/* + * Name: kissserial.h + */ + + +#include "ax25_pad.h" /* for packet_t */ + +#include "config.h" + +#include "kiss_frame.h" + + +void kissserial_init (struct misc_config_s *misc_config); + +void kissserial_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, + struct kissport_status_s *notused1, int notused2); + + +void kissserial_set_debug (int n); + + +/* end kissserial.h */ diff --git a/latlong.h b/latlong.h new file mode 100644 index 0000000..ae98fe6 --- /dev/null +++ b/latlong.h @@ -0,0 +1,24 @@ + +/* latlong.h */ + + +/* Use this value for unknown latitude/longitude or other values. */ + +#define G_UNKNOWN (-999999) + + +void latitude_to_str (double dlat, int ambiguity, char *slat); +void longitude_to_str (double dlong, int ambiguity, char *slong); + +void latitude_to_comp_str (double dlat, char *clat); +void longitude_to_comp_str (double dlon, char *clon); + +void latitude_to_nmea (double dlat, char *slat, char *hemi); +void longitude_to_nmea (double dlong, char *slong, char *hemi); + +double latitude_from_nmea (char *pstr, char *phemi); +double longitude_from_nmea (char *pstr, char *phemi); + +double ll_distance_km (double lat1, double lon1, double lat2, double lon2); + +int ll_from_grid_square (char *maidenhead, double *dlat, double *dlon); \ No newline at end of file diff --git a/libfftw3f-64-3.lib b/libfftw3f-64-3.lib new file mode 100644 index 0000000..e605463 Binary files /dev/null and b/libfftw3f-64-3.lib differ diff --git a/log.h b/log.h new file mode 100644 index 0000000..3afb6b1 --- /dev/null +++ b/log.h @@ -0,0 +1,19 @@ + +/* log.h */ + + +#include "hdlc_rec2.h" // for retry_t + +#include "decode_aprs.h" // for decode_aprs_t + +#include "ax25_pad.h" + + + +void log_init (int daily_names, char *path); + +void log_write (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries); + +void log_rr_bits (decode_aprs_t *A, packet_t pp); + +void log_term (void); \ No newline at end of file diff --git a/main.cpp b/main.cpp index 34973b9..402e849 100644 --- a/main.cpp +++ b/main.cpp @@ -67,7 +67,7 @@ int main(int argc, char *argv[]) w = new QtSoundModem(); char Title[128]; - sprintf(Title, "QtSoundModem Version %s Ports %d/%d", VersionString, AGWPort, KISSPort); + sprintf(Title, "QtSoundModem Version %s Ports %d%s/%d%s", VersionString, AGWPort, AGWServ ? "*" : "", KISSPort, KISSServ ? "*" : ""); w->setWindowTitle(Title); w->show(); diff --git a/mgn_icon.h b/mgn_icon.h new file mode 100644 index 0000000..c870bc0 --- /dev/null +++ b/mgn_icon.h @@ -0,0 +1,276 @@ + + +/* + * MGN_icon.h + * + * Waypoint icon codes for use in the $PMGNWPL sentence. + * + * Derived from Data Transmission Protocol For Magellan Products - version 2.11, March 2003 + * + * http://www.gpsinformation.org/mag-proto-2-11.pdf + * + * + * That's 13 years ago. There should be something newer available but I can't find it. + * + * The is based on the newer models at the time. Earlier models had shorter incompatible icon lists. + */ + + + +#define MGN_crossed_square "a" +#define MGN_box "b" +#define MGN_house "c" +#define MGN_aerial "d" +#define MGN_airport "e" +#define MGN_amusement_park "f" +#define MGN_ATM "g" +#define MGN_auto_repair "h" +#define MGN_boating "I" +#define MGN_camping "j" +#define MGN_exit_ramp "k" +#define MGN_first_aid "l" +#define MGN_nav_aid "m" +#define MGN_buoy "n" +#define MGN_fuel "o" +#define MGN_garden "p" +#define MGN_golf "q" +#define MGN_hotel "r" +#define MGN_hunting_fishing "s" +#define MGN_large_city "t" +#define MGN_lighthouse "u" +#define MGN_major_city "v" +#define MGN_marina "w" +#define MGN_medium_city "x" +#define MGN_museum "y" +#define MGN_obstruction "z" +#define MGN_park "aa" +#define MGN_resort "ab" +#define MGN_restaurant "ac" +#define MGN_rock "ad" +#define MGN_scuba "ae" +#define MGN_RV_service "af" +#define MGN_shooting "ag" +#define MGN_sight_seeing "ah" +#define MGN_small_city "ai" +#define MGN_sounding "aj" +#define MGN_sports_arena "ak" +#define MGN_tourist_info "al" +#define MGN_truck_service "am" +#define MGN_winery "an" +#define MGN_wreck "ao" +#define MGN_zoo "ap" + + +/* + * Mapping from APRS symbols to Magellan. + * + * This is a bit of a challenge because there + * are no icons for moving objects. + * We can use airport for flying things but + * what about wheeled transportation devices? + */ + +// TODO: NEEDS MORE WORK!!! + + +#define MGN_default MGN_crossed_square + +#define SYMTAB_SIZE 95 + +static const char mgn_primary_symtab[SYMTAB_SIZE][3] = { + + MGN_default, // 00 --no-symbol-- + MGN_default, // ! 01 Police, Sheriff + MGN_default, // " 02 reserved (was rain) + MGN_aerial, // # 03 DIGI (white center) + MGN_default, // $ 04 PHONE + MGN_aerial, // % 05 DX CLUSTER + MGN_aerial, // & 06 HF GATEway + MGN_airport, // ' 07 Small AIRCRAFT + MGN_aerial, // ( 08 Mobile Satellite Station + MGN_default, // ) 09 Wheelchair (handicapped) + MGN_default, // * 10 SnowMobile + MGN_default, // + 11 Red Cross + MGN_default, // , 12 Boy Scouts + MGN_house, // - 13 House QTH (VHF) + MGN_default, // . 14 X + MGN_default, // / 15 Red Dot + MGN_default, // 0 16 # circle (obsolete) + MGN_default, // 1 17 TBD + MGN_default, // 2 18 TBD + MGN_default, // 3 19 TBD + MGN_default, // 4 20 TBD + MGN_default, // 5 21 TBD + MGN_default, // 6 22 TBD + MGN_default, // 7 23 TBD + MGN_default, // 8 24 TBD + MGN_default, // 9 25 TBD + MGN_default, // : 26 FIRE + MGN_camping, // ; 27 Campground (Portable ops) + MGN_default, // < 28 Motorcycle + MGN_default, // = 29 RAILROAD ENGINE + MGN_default, // > 30 CAR + MGN_default, // ? 31 SERVER for Files + MGN_default, // @ 32 HC FUTURE predict (dot) + MGN_first_aid, // A 33 Aid Station + MGN_aerial, // B 34 BBS or PBBS + MGN_boating, // C 35 Canoe + MGN_default, // D 36 + MGN_default, // E 37 EYEBALL (Eye catcher!) + MGN_default, // F 38 Farm Vehicle (tractor) + MGN_default, // G 39 Grid Square (6 digit) + MGN_hotel, // H 40 HOTEL (blue bed symbol) + MGN_aerial, // I 41 TcpIp on air network stn + MGN_default, // J 42 + MGN_default, // K 43 School + MGN_default, // L 44 PC user + MGN_default, // M 45 MacAPRS + MGN_aerial, // N 46 NTS Station + MGN_airport, // O 47 BALLOON + MGN_default, // P 48 Police + MGN_default, // Q 49 TBD + MGN_RV_service, // R 50 REC. VEHICLE + MGN_airport, // S 51 SHUTTLE + MGN_default, // T 52 SSTV + MGN_default, // U 53 BUS + MGN_default, // V 54 ATV + MGN_default, // W 55 National WX Service Site + MGN_default, // X 56 HELO + MGN_boating, // Y 57 YACHT (sail) + MGN_default, // Z 58 WinAPRS + MGN_default, // [ 59 Human/Person (HT) + MGN_default, // \ 60 TRIANGLE(DF station) + MGN_default, // ] 61 MAIL/PostOffice(was PBBS) + MGN_airport, // ^ 62 LARGE AIRCRAFT + MGN_default, // _ 63 WEATHER Station (blue) + MGN_aerial, // ` 64 Dish Antenna + MGN_default, // a 65 AMBULANCE + MGN_default, // b 66 BIKE + MGN_default, // c 67 Incident Command Post + MGN_default, // d 68 Fire dept + MGN_zoo, // e 69 HORSE (equestrian) + MGN_default, // f 70 FIRE TRUCK + MGN_airport, // g 71 Glider + MGN_default, // h 72 HOSPITAL + MGN_default, // i 73 IOTA (islands on the air) + MGN_default, // j 74 JEEP + MGN_default, // k 75 TRUCK + MGN_default, // l 76 Laptop + MGN_aerial, // m 77 Mic-E Repeater + MGN_default, // n 78 Node (black bulls-eye) + MGN_default, // o 79 EOC + MGN_zoo, // p 80 ROVER (puppy, or dog) + MGN_default, // q 81 GRID SQ shown above 128 m + MGN_aerial, // r 82 Repeater + MGN_default, // s 83 SHIP (pwr boat) + MGN_default, // t 84 TRUCK STOP + MGN_default, // u 85 TRUCK (18 wheeler) + MGN_default, // v 86 VAN + MGN_default, // w 87 WATER station + MGN_aerial, // x 88 xAPRS (Unix) + MGN_aerial, // y 89 YAGI @ QTH + MGN_default, // z 90 TBD + MGN_default, // { 91 + MGN_default, // | 92 TNC Stream Switch + MGN_default, // } 93 + MGN_default }; // ~ 94 TNC Stream Switch + + +static const char mgn_alternate_symtab[SYMTAB_SIZE][3] = { + + MGN_default, // 00 --no-symbol-- + MGN_default, // ! 01 EMERGENCY (!) + MGN_default, // " 02 reserved + MGN_aerial, // # 03 OVERLAY DIGI (green star) + MGN_ATM, // $ 04 Bank or ATM (green box) + MGN_default, // % 05 Power Plant with overlay + MGN_aerial, // & 06 I=Igte IGate R=RX T=1hopTX 2=2hopTX + MGN_default, // ' 07 Crash (& now Incident sites) + MGN_default, // ( 08 CLOUDY (other clouds w ovrly) + MGN_aerial, // ) 09 Firenet MEO, MODIS Earth Obs. + MGN_default, // * 10 SNOW (& future ovrly codes) + MGN_default, // + 11 Church + MGN_default, // , 12 Girl Scouts + MGN_house, // - 13 House (H=HF) (O = Op Present) + MGN_default, // . 14 Ambiguous (Big Question mark) + MGN_default, // / 15 Waypoint Destination + MGN_default, // 0 16 CIRCLE (E/I/W=IRLP/Echolink/WIRES) + MGN_default, // 1 17 + MGN_default, // 2 18 + MGN_default, // 3 19 + MGN_default, // 4 20 + MGN_default, // 5 21 + MGN_default, // 6 22 + MGN_default, // 7 23 + MGN_aerial, // 8 24 802.11 or other network node + MGN_fuel, // 9 25 Gas Station (blue pump) + MGN_default, // : 26 Hail (& future ovrly codes) + MGN_park, // ; 27 Park/Picnic area + MGN_default, // < 28 ADVISORY (one WX flag) + MGN_default, // = 29 APRStt Touchtone (DTMF users) + MGN_default, // > 30 OVERLAID CAR + MGN_tourist_info, // ? 31 INFO Kiosk (Blue box with ?) + MGN_default, // @ 32 HURRICANE/Trop-Storm + MGN_box, // A 33 overlayBOX DTMF & RFID & XO + MGN_default, // B 34 Blwng Snow (& future codes) + MGN_boating, // C 35 Coast Guard + MGN_default, // D 36 Drizzle (proposed APRStt) + MGN_default, // E 37 Smoke (& other vis codes) + MGN_default, // F 38 Freezng rain (&future codes) + MGN_default, // G 39 Snow Shwr (& future ovrlys) + MGN_default, // H 40 Haze (& Overlay Hazards) + MGN_default, // I 41 Rain Shower + MGN_default, // J 42 Lightning (& future ovrlys) + MGN_default, // K 43 Kenwood HT (W) + MGN_lighthouse, // L 44 Lighthouse + MGN_default, // M 45 MARS (A=Army,N=Navy,F=AF) + MGN_buoy, // N 46 Navigation Buoy + MGN_airport, // O 47 Rocket + MGN_default, // P 48 Parking + MGN_default, // Q 49 QUAKE + MGN_restaurant, // R 50 Restaurant + MGN_aerial, // S 51 Satellite/Pacsat + MGN_default, // T 52 Thunderstorm + MGN_default, // U 53 SUNNY + MGN_nav_aid, // V 54 VORTAC Nav Aid + MGN_default, // W 55 # NWS site (NWS options) + MGN_default, // X 56 Pharmacy Rx (Apothicary) + MGN_aerial, // Y 57 Radios and devices + MGN_default, // Z 58 + MGN_default, // [ 59 W.Cloud (& humans w Ovrly) + MGN_default, // \ 60 New overlayable GPS symbol + MGN_default, // ] 61 + MGN_airport, // ^ 62 # Aircraft (shows heading) + MGN_default, // _ 63 # WX site (green digi) + MGN_default, // ` 64 Rain (all types w ovrly) + MGN_aerial, // a 65 ARRL, ARES, WinLINK + MGN_default, // b 66 Blwng Dst/Snd (& others) + MGN_default, // c 67 CD triangle RACES/SATERN/etc + MGN_default, // d 68 DX spot by callsign + MGN_default, // e 69 Sleet (& future ovrly codes) + MGN_default, // f 70 Funnel Cloud + MGN_default, // g 71 Gale Flags + MGN_default, // h 72 Store. or HAMFST Hh=HAM store + MGN_box, // i 73 BOX or points of Interest + MGN_default, // j 74 WorkZone (Steam Shovel) + MGN_default, // k 75 Special Vehicle SUV,ATV,4x4 + MGN_default, // l 76 Areas (box,circles,etc) + MGN_default, // m 77 Value Sign (3 digit display) + MGN_default, // n 78 OVERLAY TRIANGLE + MGN_default, // o 79 small circle + MGN_default, // p 80 Prtly Cldy (& future ovrlys) + MGN_default, // q 81 + MGN_default, // r 82 Restrooms + MGN_default, // s 83 OVERLAY SHIP/boat (top view) + MGN_default, // t 84 Tornado + MGN_default, // u 85 OVERLAID TRUCK + MGN_default, // v 86 OVERLAID Van + MGN_default, // w 87 Flooding + MGN_wreck, // x 88 Wreck or Obstruction ->X<- + MGN_default, // y 89 Skywarn + MGN_default, // z 90 OVERLAID Shelter + MGN_default, // { 91 Fog (& future ovrly codes) + MGN_default, // | 92 TNC Stream Switch + MGN_default, // } 93 + MGN_default }; // ~ 94 TNC Stream Switch + diff --git a/mheard.h b/mheard.h new file mode 100644 index 0000000..f8466ba --- /dev/null +++ b/mheard.h @@ -0,0 +1,20 @@ + + +/* mheard.h */ + +#include "decode_aprs.h" // for decode_aprs_t + + +void mheard_init (int debug); + +void mheard_save_rf (int chan, decode_aprs_t *A, packet_t pp, alevel_t alevel, retry_t retries); + +void mheard_save_is (char *ptext); + +int mheard_count (int max_hops, int time_limit); + +int mheard_was_recently_nearby (char *role, char *callsign, int time_limit, int max_hops, double dlat, double dlon, double km); + +void mheard_set_msp (char *callsign, int num); + +int mheard_get_msp (char *callsign); \ No newline at end of file diff --git a/morse.h b/morse.h new file mode 100644 index 0000000..e34dd7b --- /dev/null +++ b/morse.h @@ -0,0 +1,8 @@ +/* morse.h */ + +int morse_init (struct audio_s *audio_config_p, int amp) ; + +int morse_send (int chan, char *str, int wpm, int txdelay, int txtail); + +#define MORSE_DEFAULT_WPM 10 + diff --git a/multi_modem.h b/multi_modem.h new file mode 100644 index 0000000..de3061e --- /dev/null +++ b/multi_modem.h @@ -0,0 +1,24 @@ +/* multi_modem.h */ + +#ifndef MULTI_MODEM_H +#define MULTI_MODEM 1 + +/* Needed for typedef retry_t. */ +#include "hdlc_rec2.h" + +/* Needed for struct audio_s */ +#include "audio.h" + + +void multi_modem_init (struct audio_s *pmodem); + +void multi_modem_process_sample (int c, int audio_sample); + +int multi_modem_get_dc_average (int chan); + +// Deprecated. Replace with ...packet +void multi_modem_process_rec_frame (int chan, int subchan, int slice, unsigned char *fbuf, int flen, alevel_t alevel, retry_t retries, int is_fx25); + +void multi_modem_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25); + +#endif diff --git a/pfilter.h b/pfilter.h new file mode 100644 index 0000000..d54e056 --- /dev/null +++ b/pfilter.h @@ -0,0 +1,13 @@ + +/* pfilter.h */ + + +#include "igate.h" // for igate_config_s + + + +void pfilter_init (struct igate_config_s *p_igate_config, int debug_level); + +int pfilter (int from_chan, int to_chan, char *filter, packet_t pp, int is_aprs); + +int is_telem_metadata (char *infop); \ No newline at end of file diff --git a/pktARDOP.c b/pktARDOP.c index 2f38059..a2e7473 100644 --- a/pktARDOP.c +++ b/pktARDOP.c @@ -48,7 +48,7 @@ extern int PacketMonLength; #define ARDOPBufferSize 12000 * 100 -short ARDOPTXBuffer[4][12000 * 100]; // Enough to hold whole frame of samples +short ARDOPTXBuffer[4][ARDOPBufferSize]; // Enough to hold whole frame of samples int ARDOPTXLen[4] = { 0,0,0,0 }; // Length of frame int ARDOPTXPtr[4] = { 0,0,0,0 }; // Tx Pointer diff --git a/ptt.h b/ptt.h new file mode 100644 index 0000000..6e75253 --- /dev/null +++ b/ptt.h @@ -0,0 +1,26 @@ + + +#ifndef PTT_H +#define PTT_H 1 + + +#include "audio.h" /* for struct audio_s and definitions for octype values */ + + +void ptt_set_debug(int debug); + +void ptt_init (struct audio_s *p_modem); + +void ptt_set (int octype, int chan, int ptt); + +void ptt_term (void); + +int get_input (int it, int chan); + +#endif + + +/* end ptt.h */ + + + diff --git a/recv.h b/recv.h new file mode 100644 index 0000000..3201991 --- /dev/null +++ b/recv.h @@ -0,0 +1,6 @@ + +/* recv.h */ + +void recv_init (struct audio_s *pa); + +void recv_process (void); \ No newline at end of file diff --git a/redecode.h b/redecode.h new file mode 100644 index 0000000..ffd9a95 --- /dev/null +++ b/redecode.h @@ -0,0 +1,15 @@ + + +#ifndef REDECODE_H +#define REDECODE_H 1 + +#include "rrbb.h" + + +extern void redecode_init (struct audio_s *p_audio_config); + + +#endif + +/* end redecode.h */ + diff --git a/release/moc_predefs-DESKTOP-MHE5LO8.h b/release/moc_predefs-DESKTOP-MHE5LO8.h new file mode 100644 index 0000000..4c9c0c7 --- /dev/null +++ b/release/moc_predefs-DESKTOP-MHE5LO8.h @@ -0,0 +1,11 @@ +#define _MSC_EXTENSIONS +#define _INTEGRAL_MAX_BITS 64 +#define _MSC_VER 1916 +#define _MSC_FULL_VER 191627043 +#define _MSC_BUILD 0 +#define _WIN32 +#define _M_IX86 600 +#define _M_IX86_FP 2 +#define _CPPRTTI +#define _MT +#define _DLL diff --git a/release/moc_predefs.h b/release/moc_predefs.h new file mode 100644 index 0000000..4c9c0c7 --- /dev/null +++ b/release/moc_predefs.h @@ -0,0 +1,11 @@ +#define _MSC_EXTENSIONS +#define _INTEGRAL_MAX_BITS 64 +#define _MSC_VER 1916 +#define _MSC_FULL_VER 191627043 +#define _MSC_BUILD 0 +#define _WIN32 +#define _M_IX86 600 +#define _M_IX86_FP 2 +#define _CPPRTTI +#define _MT +#define _DLL diff --git a/rpack.h b/rpack.h new file mode 100644 index 0000000..d972a54 --- /dev/null +++ b/rpack.h @@ -0,0 +1,94 @@ + +/*------------------------------------------------------------------ + * + * File: rpack.h + * + * Purpose: Definition of Garmin Rino message format. + * + * References: http://www.radio-active.net.au/web3/APRS/Resources/RINO + * + * http://www.radio-active.net.au/web3/APRS/Resources/RINO/OnAir + * + *---------------------------------------------------------------*/ + + +#ifndef RPACK_H +#define RPACK_H 1 + + +#define RPACK_FRAME_LEN 168 + + +#ifdef RPACK_C /* Expose private details */ + + + +// Transmission order is LSB first. + +struct __attribute__((__packed__)) rpack_s { + + int lat; // Latitude. + // Signed integer. Scaled by 2**30/90. + + int lon; // Longitude. Same encoding. + + char unknown1; // Unproven theory: altitude. + char unknown2; + + unsigned name0:6; // 10 character name. + unsigned name1:6; // Bit packing is implementation dependent. + unsigned name2:6; // Should rewrite to be more portable. + unsigned name3:6; + unsigned name4:6; + unsigned name5:6; + unsigned name6:6; + unsigned name7:6; + unsigned name8:6; + unsigned name9:6; + + unsigned symbol:5; + + unsigned unknown3:7; + + +// unsigned crc:16; // Safe bet this is CRC for error checking. + + unsigned char crc1; + unsigned char crc2; + + char dummy[3]; // Total size should be 24 bytes if no gaps. + +}; + +#else /* Show only public interface. */ + + +struct rpack_s { + char stuff[24]; +}; + + +#endif + + + +void rpack_set_bit (struct rpack_s *rp, int position, int value); + +int rpack_is_valid (struct rpack_s *rp); + +int rpack_get_bit (struct rpack_s *rp, int position); + +double rpack_get_lat (struct rpack_s *rp); + +double rpack_get_lon (struct rpack_s *rp); + +int rpack_get_symbol (struct rpack_s *rp); + +void rpack_get_name (struct rpack_s *rp, char *str); + + + +#endif + +/* end rpack.h */ + diff --git a/rrbb.h b/rrbb.h new file mode 100644 index 0000000..4b28372 --- /dev/null +++ b/rrbb.h @@ -0,0 +1,92 @@ + +#ifndef RRBB_H + +#define RRBB_H + + +#define FASTER13 1 // Don't pack 8 samples per byte. + + +//typedef short slice_t; + + +/* + * Maximum size (in bytes) of an AX.25 frame including the 2 octet FCS. + */ + +#define MAX_FRAME_LEN ((AX25_MAX_PACKET_LEN) + 2) + +/* + * Maximum number of bits in AX.25 frame excluding the flags. + * Adequate for extreme case of bit stuffing after every 5 bits + * which could never happen. + */ + +#define MAX_NUM_BITS (MAX_FRAME_LEN * 8 * 6 / 5) + +typedef struct rrbb_s { + int magic1; + struct rrbb_s* nextp; /* Next pointer to maintain a queue. */ + + int chan; /* Radio channel from which it was received. */ + int subchan; /* Which modem when more than one per channel. */ + int slice; /* Which slicer. */ + + alevel_t alevel; /* Received audio level at time of frame capture. */ + unsigned int len; /* Current number of samples in array. */ + + int is_scrambled; /* Is data scrambled G3RUH / K9NG style? */ + int descram_state; /* Descrambler state before first data bit of frame. */ + int prev_descram; /* Previous descrambled bit. */ + + unsigned char fdata[MAX_NUM_BITS]; + + int magic2; +} *rrbb_t; + + + +rrbb_t rrbb_new (int chan, int subchan, int slice, int is_scrambled, int descram_state, int prev_descram); + +void rrbb_clear (rrbb_t b, int is_scrambled, int descram_state, int prev_descram); + + +static inline /*__attribute__((always_inline))*/ void rrbb_append_bit (rrbb_t b, const unsigned char val) +{ + if (b->len >= MAX_NUM_BITS) { + return; /* Silently discard if full. */ + } + b->fdata[b->len] = val; + b->len++; +} + +static inline /*__attribute__((always_inline))*/ unsigned char rrbb_get_bit (const rrbb_t b, const int ind) +{ + return (b->fdata[ind]); +} + + +void rrbb_chop8 (rrbb_t b); + +int rrbb_get_len (rrbb_t b); + +//void rrbb_flip_bit (rrbb_t b, unsigned int ind); + +void rrbb_delete (rrbb_t b); + +void rrbb_set_nextp (rrbb_t b, rrbb_t np); +rrbb_t rrbb_get_nextp (rrbb_t b); + +int rrbb_get_chan (rrbb_t b); +int rrbb_get_subchan (rrbb_t b); +int rrbb_get_slice (rrbb_t b); + +void rrbb_set_audio_level (rrbb_t b, alevel_t alevel); +alevel_t rrbb_get_audio_level (rrbb_t b); + +int rrbb_get_is_scrambled (rrbb_t b); +int rrbb_get_descram_state (rrbb_t b); +int rrbb_get_prev_descram (rrbb_t b); + + +#endif diff --git a/serial_port.h b/serial_port.h new file mode 100644 index 0000000..8a65a0b --- /dev/null +++ b/serial_port.h @@ -0,0 +1,32 @@ +/* serial_port.h */ + + +#ifndef SERIAL_PORT_H +#define SERIAL_PORT_H 1 + + +#if __WIN32__ + +#include + +typedef HANDLE MYFDTYPE; +#define MYFDERROR INVALID_HANDLE_VALUE + +#else + +typedef int MYFDTYPE; +#define MYFDERROR (-1) + +#endif + + +extern MYFDTYPE serial_port_open (char *devicename, int baud); + +extern int serial_port_write (MYFDTYPE fd, char *str, int len); + +extern int serial_port_get1 (MYFDTYPE fd); + +extern void serial_port_close (MYFDTYPE fd); + + +#endif \ No newline at end of file diff --git a/server.h b/server.h new file mode 100644 index 0000000..4cc2ea0 --- /dev/null +++ b/server.h @@ -0,0 +1,32 @@ + +/* + * Name: server.h + */ + + +#include "ax25_pad.h" /* for packet_t */ + +#include "config.h" + + +void server_set_debug (int n); + +void server_init (struct audio_s *audio_config_p, struct misc_config_s *misc_config); + +void server_send_rec_packet (int chan, packet_t pp, unsigned char *fbuf, int flen); + +void server_send_monitored (int chan, packet_t pp, int own_xmit); + +int server_callsign_registered_by_client (char *callsign); + + +void server_link_established (int chan, int client, char *remote_call, char *own_call, int incoming); + +void server_link_terminated (int chan, int client, char *remote_call, char *own_call, int timeout); + +void server_rec_conn_data (int chan, int client, char *remote_call, char *own_call, int pid, char *data_ptr, int data_len); + +void server_outstanding_frames_reply (int chan, int client, char *own_call, char *remote_call, int count); + + +/* end server.h */ diff --git a/sm_main - Copy.c b/sm_main - Copy.c deleted file mode 100644 index 9114a25..0000000 --- a/sm_main - Copy.c +++ /dev/null @@ -1,2850 +0,0 @@ -/* -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. -newsamp - -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" - -void make_core_BPF(UCHAR snd_ch, short freq, short width); -void make_core_TXBPF(UCHAR snd_ch, float freq, float width); -void make_core_INTR(UCHAR snd_ch); -void make_core_LPF(UCHAR snd_ch, short width); -void wf_pointer(int snd_ch); - -char modes_name[modes_count][20] = -{ - "AFSK AX.25 300bd","AFSK AX.25 1200bd","AFSK AX.25 600bd","AFSK AX.25 2400bd", - "BPSK AX.25 1200bd","BPSK AX.25 600bd","BPSK AX.25 300bd","BPSK AX.25 2400bd", - "QPSK AX.25 4800bd","QPSK AX.25 3600bd","QPSK AX.25 2400bd","BPSK FEC 4x100bd", - "DW QPSK V26A 2400bd","DW 8PSK V27 4800bd","DW QPSK V26B 2400bd", "ARDOP Packet" -}; - -typedef struct wavehdr_tag { - unsigned short * lpData; /* pointer to locked data buffer */ - int dwBufferLength; /* length of data buffer */ - int dwBytesRecorded; /* used for input only */ - int * dwUser; /* for client's use */ - int dwFlags; /* assorted flags (see defines) */ - int dwLoops; /* loop control counter */ - struct wavehdr_tag *lpNext; /* reserved for driver */ - int * reserved; /* reserved for driver */ -} WAVEHDR, *PWAVEHDR, * NPWAVEHDR, * LPWAVEHDR; - -extern int pnt_change[5]; -int debugmode = 0; -extern float src_buf[5][2048]; -extern Byte RCVR[5]; - -int SatelliteMode = 0; - -int UDPServerPort = 8884; -int UDPClientPort = 8888; -int TXPort = 8884; - -BOOL Firstwaterfall = 1; -BOOL Secondwaterfall = 1; - -BOOL multiCore = FALSE; - -/* -type - TComboBox = class(StdCtrls.TComboBox) - private - procedure CMMouseWheel(var msg TCMMouseWheel); message CM_MOUSEWHEEL; - end; - TData16 = array [0..4095] of smallint; - PData16 = ^TData16; - TWaveHeader = record - RIFF dword; - ChunkLen integer; - WAVE dword; - fmt dword; - FormatLen integer; - Format word; - Channels word; - Frequency integer; - BytesPS integer; - BlockAlign word; - BitsPS word; - data dword; - DataLen integer - end; - TForm1 = class(TForm) - Panel5 TPanel; - ServerSocket1 TServerSocket; - MainMenu1 TMainMenu; - Settings1 TMenuItem; - OutputVolume1 TMenuItem; - InputVolume1 TMenuItem; - CoolTrayIcon1 TCoolTrayIcon; - ImageList1 TImageList; - ABout1 TMenuItem; - Panel1 TPanel; - Panel2 TPanel; - View1 TMenuItem; - Firstwaterfall1 TMenuItem; - Secondwaterfall1 TMenuItem; - Panel3 TPanel; - StringGrid1 TStringGrid; - Devices1 TMenuItem; - Statustable1 TMenuItem; - Monitor1 TMenuItem; - Panel4 TPanel; - PaintBox2 TPaintBox; - Filters1 TMenuItem; - Clearmonitor1 TMenuItem; - RxRichEdit1 TRxRichEdit; - MemoPopupMenu1 TPopupMenu; - Copytext1 TMenuItem; - Label1 TLabel; - Label5 TLabel; - ApplicationEvents1 TApplicationEvents; - PaintBox1 TPaintBox; - PaintBox3 TPaintBox; - ServerSocket2 TServerSocket; - Font1 TMenuItem; - FontDialog1 TFontDialog; - N1 TMenuItem; - Calibration1 TMenuItem; - Panel9 TPanel; - Panel6 TPanel; - Label4 TLabel; - Shape2 TShape; - ComboBox2 TComboBox; - SpinEdit2 TSpinEdit; - Panel7 TPanel; - Label3 TLabel; - Shape1 TShape; - ComboBox1 TComboBox; - SpinEdit1 TSpinEdit; - Panel8 TPanel; - Label2 TLabel; - TrackBar1 TTrackBar; - CheckBox1 TCheckBox; - OpenDialog1 TOpenDialog; - procedure FormCreate(Sender TObject); - procedure TrackBar1Change(Sender TObject); - procedure PaintBox1MouseMove(Sender TObject; Shift TShiftState; X, - Y Integer); - procedure PaintBox1MouseDown(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); - procedure ServerSocket1ClientRead(Sender TObject; - Socket TCustomWinSocket); - procedure ServerSocket1ClientError(Sender TObject; - Socket TCustomWinSocket; ErrorEvent TErrorEvent; - var ErrorCode Integer); - procedure OutputVolume1Click(Sender TObject); - procedure InputVolume1Click(Sender TObject); - procedure ServerSocket1ClientConnect(Sender TObject; - Socket TCustomWinSocket); - procedure ServerSocket1ClientDisconnect(Sender TObject; - Socket TCustomWinSocket); - procedure CoolTrayIcon1Click(Sender TObject); - procedure CoolTrayIcon1Cycle(Sender TObject; NextIndex Integer); - procedure ABout1Click(Sender TObject); - procedure PaintBox3MouseDown(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); - procedure PaintBox3MouseMove(Sender TObject; Shift TShiftState; X, - Y Integer); - procedure Firstwaterfall1Click(Sender TObject); - procedure Secondwaterfall1Click(Sender TObject); - procedure Devices1Click(Sender TObject); - procedure Statustable1Click(Sender TObject); - procedure Monitor1Click(Sender TObject); - procedure FormPaint(Sender TObject); - procedure Filters1Click(Sender TObject); - procedure SpinEdit1Change(Sender TObject); - procedure SpinEdit2Change(Sender TObject); - procedure Clearmonitor1Click(Sender TObject); - procedure Copytext1Click(Sender TObject); - procedure PaintBox3MouseUp(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); - procedure PaintBox1MouseUp(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); - procedure ApplicationEvents1Minimize(Sender TObject); - procedure ApplicationEvents1Restore(Sender TObject); - procedure ServerSocket2ClientConnect(Sender TObject; - Socket TCustomWinSocket); - procedure ServerSocket2ClientDisconnect(Sender TObject; - Socket TCustomWinSocket); - procedure ServerSocket2ClientError(Sender TObject; - Socket TCustomWinSocket; ErrorEvent TErrorEvent; - var ErrorCode Integer); - procedure ServerSocket2ClientRead(Sender TObject; - Socket TCustomWinSocket); - procedure Font1Click(Sender TObject); - procedure Calibration1Click(Sender TObject); - procedure ComboBox1Change(Sender TObject); - procedure ComboBox1KeyDown(Sender TObject; var Key Word; - Shift TShiftState); - procedure ComboBox1KeyPress(Sender TObject; var Key Char); - procedure ComboBox2Change(Sender TObject); - procedure FormDestroy(Sender TObject); - private - { Private declarations } - procedure BufferFull(var Msg TMessage); Message MM_WIM_DATA; - procedure BufferFull1(var Msg TMessage); Message MM_WOM_DONE; - procedure make_wave_buf(snd_ch byte; buf PChar); - procedure disp2(snd_ch byte); - procedure create_timer1; - procedure free_timer1; - procedure show_panels; - procedure show_combobox; - procedure Timer_Event2; - procedure waterfall_init; - procedure waterfall_free; - public - { Public declarations } - function get_idx_by_name(name string) word; - function frame_monitor(s,code string; tx_stat boolean) string; - procedure ChangePriority; - procedure put_frame(snd_ch byte; frame,code string; tx_stat,excluded boolean); - procedure show_grid; - procedure RX2TX(snd_ch byte); - procedure TX2RX(snd_ch byte); - procedure WriteIni; - procedure ReadIni; - procedure init_8P4800(snd_ch byte); - procedure init_DW2400(snd_ch byte); - procedure init_AE2400(snd_ch byte); - procedure init_MP400(snd_ch byte); - procedure init_Q4800(snd_ch byte); - procedure init_Q3600(snd_ch byte); - procedure init_Q2400(snd_ch byte); - procedure init_P2400(snd_ch byte); - procedure init_P1200(snd_ch byte); - procedure init_P600(snd_ch byte); - procedure init_P300(snd_ch byte); - procedure init_300(snd_ch byte); - procedure init_600(snd_ch byte); - procedure init_1200(snd_ch byte); - procedure init_2400(snd_ch byte); - procedure init_speed(snd_ch byte); - procedure get_filter_values(idx byte; var dbpf,dtxbpf,dbpftap,dlpf,dlpftap word); - procedure show_mode_panels; - procedure show_modes; - procedure show_freq_a; - procedure show_freq_b; - procedure ChkSndDevName; - procedure StartRx; - procedure StartTx(snd_ch byte); - procedure StopRx; - procedure StopTx(snd_ch byte); - procedure StartAGW; - procedure StartKISS; - procedure wf_scale; - procedure wf_pointer(snd_ch byte); - end; - -var -/*/ - -BOOL MinOnStart = 0; -//RS TReedSolomon; -// Form1 TForm1; -// WaveFormat TWaveFormatEx; -int Channels = 2; -int BitsPerSample = 16; -float TX_Samplerate = 12000; -float RX_Samplerate = 12000; -int RX_SR = 11025; -int TX_SR = 11025; -int RX_PPM = 0; -int TX_PPM = 0; -int tx_bufsize = 512; -int rx_bufsize = 512; -int tx_bufcount = 16; -int rx_bufcount = 16; -int mouse_down[2] = {0, 0}; -//UCHAR * RX_pBuf array[257]; -// RX_header array[1..256] of TWaveHdr; -// TX_pBuf array[1..4,1..256] of pointer; -//TX_header array[1..4,1..256] of TWaveHdr; -UCHAR calib_mode[5] = {0,0,0,0}; -UCHAR snd_status [5] = {0,0,0,0}; -UCHAR buf_status [5] = {0,0,0,0}; -UCHAR tx_buf_num1 [5] = {0,0,0,0}; -UCHAR tx_buf_num [5] = {0,0,0,0}; - -extern short active_rx_freq[5]; - - - -int speed[5] = {0,0,0,0}; -int panels[6] = {1,1,1,1,1}; - -short fft_buf[5][4096]; -UCHAR fft_disp[5][4096]; -int fftCount = 0; // FTF samples collected - -// bm array[1..4] of TBitMap; -// bm1,bm2,bm3 TBitMap; - -// WaveInHandle hWaveIn; -// WaveOutHandle array[1..4] of hWaveOut; -int RXBufferLength; - -int grid_time = 0; -int fft_mult = 0; -int fft_spd = 3; -int grid_timer = 0; -int stop_wf = 0; -int raduga = DISP_RGB; -char snd_rx_device_name[32] = ""; -char snd_tx_device_name[32] = ""; -int snd_rx_device = 0; -int snd_tx_device = 0; -UCHAR mod_icon_status = MOD_IDLE; -UCHAR last_mod_icon_status = MOD_IDLE; -UCHAR icon_timer = 0; -// TelIni TIniFile; -char cur_dir[] = ""; -// TimerId1 cardinal; -// TimerId2 cardinal; -UCHAR TimerStat1 = TIMER_FREE; -UCHAR TimerStat2 = TIMER_FREE; -int stat_log = FALSE; - -int PTT_device = FALSE; -int RX_device = FALSE; -int TX_device = FALSE; -int TX_rotate = FALSE; -int UsingBothChannels = FALSE; -int UsingLeft = FALSE; -int UsingRight = FALSE; - -int SCO = FALSE; -int DualPTT = TRUE; -UCHAR DebugMode = 0; -UCHAR TimerEvent = TIMER_EVENT_OFF; -int nr_monitor_lines = 50; -int UTC_Time = FALSE; -int MainPriority = 0; -// MainThreadHandle THandle; -UCHAR w_state = WIN_MAXIMIZED; - - /* -implementation - -{$R *.DFM} - -uses ax25_mod, ax25_demod, ax25, ax25_l2, ax25_ptt, ax25_agw, ax25_about, rgb_rad, - AX25_set, ax25_filter, AX25_modem_set, kiss_mode, ax25_calibration; - -procedure TComboBox.CMMouseWheel(var msg TCMMouseWheel); -begin - if SendMessage(GetFocus, CB_GETDROPPEDSTATE, 0, 0) = 0 then msg.Result = 1; -end; - -procedure TForm1.ChangePriority; -begin - case MainPriority of - 0 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_NORMAL); - 1 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_ABOVE_NORMAL); - 2 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_HIGHEST); - 3 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_TIME_CRITICAL); - end; -end; - -procedure TForm1.show_modes; -var - s string; -begin - s = MODEM_CAPTION+" - Ver "+MODEM_VERSION+" - ["+modes_name[Speed[1]]; - if dualchan then s = s+" - "+modes_name[Speed[2]]; - form1.Caption = s+"]"; -end; - -procedure TForm1.show_freq_a; -begin - SpinEdit1.Value = round(rx_freq[1]); - SpinEdit1.Refresh; -end; - -procedure TForm1.show_freq_b; -begin - SpinEdit2.Value = round(rx_freq[2]); - SpinEdit2.Refresh; -end; -*/ -void get_filter_values(UCHAR snd_ch) -{ - //, unsigned short dbpf, -//unsigned short dtxbpf, -//unsigned short dbpftap, -//unsigned short dlpf, -//unsigned short dlpftap) -// speed[snd_ch], bpf[snd_ch], txbpf[snd_ch], bpf_tap[snd_ch], lpf[snd_ch], lpf_tap[snd_ch]); - - switch (speed[snd_ch]) - { - case SPEED_8P4800: - - lpf[snd_ch] = MODEM_8P4800_LPF; - bpf[snd_ch] = MODEM_8P4800_BPF; - txbpf[snd_ch] = MODEM_8P4800_TXBPF; - BPF_tap[snd_ch] = MODEM_8P4800_BPF_TAP; - LPF_tap[snd_ch] = MODEM_8P4800_LPF_TAP; - break; - - case SPEED_MP400: - - lpf[snd_ch] = MODEM_MP400_LPF; - bpf[snd_ch] = MODEM_MP400_BPF; - txbpf[snd_ch] = MODEM_MP400_TXBPF; - BPF_tap[snd_ch] = MODEM_MP400_BPF_TAP; - LPF_tap[snd_ch] = MODEM_MP400_LPF_TAP; - - break; - - - case SPEED_Q4800: - - lpf[snd_ch] = MODEM_Q4800_LPF; - bpf[snd_ch] = MODEM_Q4800_BPF; - txbpf[snd_ch] = MODEM_Q4800_TXBPF; - BPF_tap[snd_ch] = MODEM_Q4800_BPF_TAP; - LPF_tap[snd_ch] = MODEM_Q4800_LPF_TAP; - - break; - - case SPEED_Q3600: - - lpf[snd_ch] = MODEM_Q3600_LPF; - bpf[snd_ch] = MODEM_Q3600_BPF; - txbpf[snd_ch] = MODEM_Q3600_TXBPF; - BPF_tap[snd_ch] = MODEM_Q3600_BPF_TAP; - LPF_tap[snd_ch] = MODEM_Q3600_LPF_TAP; - break; - - case SPEED_Q2400: - - lpf[snd_ch] = MODEM_Q2400_LPF; - bpf[snd_ch] = MODEM_Q2400_BPF; - txbpf[snd_ch] = MODEM_Q2400_TXBPF; - BPF_tap[snd_ch] = MODEM_Q2400_BPF_TAP; - LPF_tap[snd_ch] = MODEM_Q2400_LPF_TAP; - - break; - - case SPEED_DW2400: - case SPEED_AE2400: - - - lpf[snd_ch] = MODEM_DW2400_LPF; - bpf[snd_ch] = MODEM_DW2400_BPF; - txbpf[snd_ch] = MODEM_DW2400_TXBPF; - BPF_tap[snd_ch] = MODEM_DW2400_BPF_TAP; - LPF_tap[snd_ch] = MODEM_DW2400_LPF_TAP; - break; - - case SPEED_P2400: - - lpf[snd_ch] = MODEM_P2400_LPF; - bpf[snd_ch] = MODEM_P2400_BPF; - txbpf[snd_ch] = MODEM_P2400_TXBPF; - BPF_tap[snd_ch] = MODEM_P2400_BPF_TAP; - LPF_tap[snd_ch] = MODEM_P2400_LPF_TAP; - break; - - case SPEED_P1200: - - lpf[snd_ch] = MODEM_P1200_LPF; - bpf[snd_ch] = MODEM_P1200_BPF; - txbpf[snd_ch] = MODEM_P1200_TXBPF; - BPF_tap[snd_ch] = MODEM_P1200_BPF_TAP; - LPF_tap[snd_ch] = MODEM_P1200_LPF_TAP; - break; - - case SPEED_P600: - - lpf[snd_ch] = MODEM_P600_LPF; - bpf[snd_ch] = MODEM_P600_BPF; - txbpf[snd_ch] = MODEM_P600_TXBPF; - BPF_tap[snd_ch] = MODEM_P600_BPF_TAP; - LPF_tap[snd_ch] = MODEM_P600_LPF_TAP; - break; - - case SPEED_P300: - - lpf[snd_ch] = MODEM_P300_LPF; - bpf[snd_ch] = MODEM_P300_BPF; - txbpf[snd_ch] = MODEM_P300_TXBPF; - BPF_tap[snd_ch] = MODEM_P300_BPF_TAP; - LPF_tap[snd_ch] = MODEM_P300_LPF_TAP; - break; - - case SPEED_300: - - lpf[snd_ch] = MODEM_300_LPF; - bpf[snd_ch] = MODEM_300_BPF; - txbpf[snd_ch] = MODEM_300_TXBPF; - BPF_tap[snd_ch] = MODEM_300_BPF_TAP; - LPF_tap[snd_ch] = MODEM_300_LPF_TAP; - - break; - - case SPEED_600: - - lpf[snd_ch] = MODEM_600_LPF; - bpf[snd_ch] = MODEM_600_BPF; - txbpf[snd_ch] = MODEM_600_TXBPF; - BPF_tap[snd_ch] = MODEM_600_BPF_TAP; - LPF_tap[snd_ch] = MODEM_600_LPF_TAP; - - break; - - case SPEED_1200: - - lpf[snd_ch] = MODEM_1200_LPF; - bpf[snd_ch] = MODEM_1200_BPF; - txbpf[snd_ch] = MODEM_1200_TXBPF; - BPF_tap[snd_ch] = MODEM_1200_BPF_TAP; - LPF_tap[snd_ch] = MODEM_1200_LPF_TAP; - break; - - case SPEED_2400: - - lpf[snd_ch] = MODEM_2400_LPF; - bpf[snd_ch] = MODEM_2400_BPF; - txbpf[snd_ch] = MODEM_2400_TXBPF; - BPF_tap[snd_ch] = MODEM_2400_BPF_TAP; - LPF_tap[snd_ch] = MODEM_2400_LPF_TAP; - break; - } - -} - - -void init_2400(int snd_ch) -{ - modem_mode[snd_ch] = MODE_FSK; - rx_shift[snd_ch] = 1805; - rx_baudrate[snd_ch] = 2400; - - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_1200(int snd_ch) -{ - modem_mode[snd_ch] = MODE_FSK; - rx_shift[snd_ch] = 1000; - - if (stdtones) - rx_freq[snd_ch] = 1700; - - rx_baudrate[snd_ch] = 1200; - - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_600(int snd_ch) -{ - modem_mode[snd_ch] = MODE_FSK; - rx_shift[snd_ch] = 450; - - rx_baudrate[snd_ch] = 600; - - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_300(int snd_ch) -{ - modem_mode[snd_ch] = MODE_FSK; - rx_shift[snd_ch] = 200; - rx_baudrate[snd_ch] = 300; - - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_MP400(int snd_ch) -{ - modem_mode[snd_ch] = MODE_MPSK; - rx_shift[snd_ch] = 175 /*sbc*/ * 3; - rx_baudrate[snd_ch] = 100; - - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - - -void init_8P4800(int snd_ch) -{ - modem_mode[snd_ch] = MODE_8PSK; - if (stdtones) - rx_freq[snd_ch] = 1800; - - rx_shift[snd_ch] = 1600; - rx_baudrate[snd_ch] = 1600; - - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_AE2400(int snd_ch) -{ - qpsk_set[snd_ch].mode = QPSK_V26; - modem_mode[snd_ch] = MODE_PI4QPSK; - - if (stdtones) - rx_freq[snd_ch] = 1800; - - rx_shift[snd_ch] = 1200; - rx_baudrate[snd_ch] = 1200; - - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_DW2400(int snd_ch) -{ - qpsk_set[snd_ch].mode = QPSK_V26; - modem_mode[snd_ch] = MODE_QPSK; - - if (stdtones) - rx_freq[snd_ch] = 1800; - - rx_shift[snd_ch] = 1200; - rx_baudrate[snd_ch] = 1200; - - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_Q4800(int snd_ch) -{ - qpsk_set[snd_ch].mode = QPSK_SM; - modem_mode[snd_ch] = MODE_QPSK; - rx_shift[snd_ch] = 2400; - rx_baudrate[snd_ch] = 2400; - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_Q3600(int snd_ch) -{ - qpsk_set[snd_ch].mode = QPSK_SM; - modem_mode[snd_ch] = MODE_QPSK; - rx_shift[snd_ch] = 1800; - rx_baudrate[snd_ch] = 1800; - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_Q2400(int snd_ch) -{ - qpsk_set[snd_ch].mode = QPSK_SM; - modem_mode[snd_ch] = MODE_QPSK; - rx_shift[snd_ch] = 1200; - rx_baudrate[snd_ch] = 1200; - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_P2400(int snd_ch) -{ - modem_mode[snd_ch] = MODE_BPSK; - rx_shift[snd_ch] = 2400; - rx_baudrate[snd_ch] = 2400; - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_P1200(int snd_ch) -{ - modem_mode[snd_ch] = MODE_BPSK; - rx_shift[snd_ch] = 1200; - rx_baudrate[snd_ch] = 1200; - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_P600(int snd_ch) -{ - modem_mode[snd_ch] = MODE_BPSK; - rx_shift[snd_ch] = 600; - rx_baudrate[snd_ch] = 600; - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_P300(int snd_ch) -{ - modem_mode[snd_ch] = MODE_BPSK; - rx_shift[snd_ch] = 300; - rx_baudrate[snd_ch] = 300; - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - -void init_ARDOP(int snd_ch) -{ - modem_mode[snd_ch] = MODE_ARDOP; - rx_shift[snd_ch] = 500; - rx_freq[snd_ch] = 1500; - rx_baudrate[snd_ch] = 500; - - if (modem_def[snd_ch]) - get_filter_values(snd_ch); -} - - -void init_speed(int snd_ch); - -void set_speed(int snd_ch, int Modem) -{ - speed[snd_ch] = Modem; - - init_speed(snd_ch); - -} - -void init_speed(int snd_ch) -{ - int low, high; - - /* - - if (BPF[snd_ch]>round(rx_samplerate/2) then BPF[snd_ch] = round(rx_samplerate/2); - if TXBPF[snd_ch]>round(rx_samplerate/2) then TXBPF[snd_ch] = round(rx_samplerate/2); - if LPF[snd_ch]>round(rx_samplerate/2) then LPF[snd_ch] = round(rx_samplerate/2); - if BPF[snd_ch]<1 then BPF[snd_ch] = 1; - if TXBPF[snd_ch]<1 then TXBPF[snd_ch] = 1; - if LPF[snd_ch]<1 then LPF[snd_ch] = 1; - if TXDelay[snd_ch]<1 then TXDelay[snd_ch] = 1; - if TXTail[snd_ch]<1 then TXTail[snd_ch] = 1; - if BPF_tap[snd_ch]>1024 then BPF_tap[snd_ch] = 1024; - if LPF_tap[snd_ch]>512 then LPF_tap[snd_ch] = 512; - if BPF_tap[snd_ch]<8 then BPF_tap[snd_ch] = 8; - if LPF_tap[snd_ch]<8 then LPF_tap[snd_ch] = 8; - if not (RCVR[snd_ch] in [0..8]) then RCVR[snd_ch] = 0; - if not (rcvr_offset[snd_ch] in [0..100]) then rcvr_offset[snd_ch] = 30; - if not (speed[snd_ch] in [0..modes_count]) then speed[snd_ch] = SPEED_300; -*/ - switch (speed[snd_ch]) - { - case SPEED_8P4800: - init_8P4800(snd_ch); - break; - - case SPEED_AE2400: - init_AE2400(snd_ch); - break; - - case SPEED_DW2400: - init_DW2400(snd_ch); - break; - - case SPEED_MP400: - init_MP400(snd_ch); - break; - case SPEED_Q4800: - init_Q4800(snd_ch); - break; - - case SPEED_Q3600: - init_Q3600(snd_ch); - break; - - case SPEED_Q2400: - init_Q2400(snd_ch); - break; - - case SPEED_P2400: - init_P2400(snd_ch); - break; - - case SPEED_P1200: - init_P1200(snd_ch); - break; - - case SPEED_P600: - init_P600(snd_ch); - break; - - case SPEED_P300: - init_P300(snd_ch); - break; - - case SPEED_300: - - init_300(snd_ch); - break; - - case SPEED_600: - - init_600(snd_ch); - break; - - case SPEED_1200: - - init_1200(snd_ch); - break; - - case SPEED_2400: - - init_2400(snd_ch); - break; - - case SPEED_ARDOP: - - init_ARDOP(snd_ch); - break; - } - - //QPSK_SM: begin move(#0#1#2#3, tx[0], 4); move(#0#32#64#96, rx[0], 4); end; - //QPSK_V26: begin move(#2#3#1#0, tx[0], 4); move(#96#64#0#32, rx[0], 4); end; - - - if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK) - { - switch (qpsk_set[snd_ch].mode) - { - case QPSK_SM: - - memcpy(&qpsk_set[snd_ch].tx[0], "\0\1\2\3", 4); - memcpy(&qpsk_set[snd_ch].rx[0], "\x0\x20\x40\x60", 4); - break; - - case QPSK_V26: - - memcpy(&qpsk_set[snd_ch].tx[0], "\2\3\1\0", 4); - memcpy(&qpsk_set[snd_ch].rx[0], "\x60\x40\0\x20", 4); - break; - } - } - - tx_shift[snd_ch] = rx_shift[snd_ch]; - tx_baudrate[snd_ch] = rx_baudrate[snd_ch]; - low = roundf(rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch] + 1); - high = roundf(RX_Samplerate / 2 - (rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch])); - - if (rx_freq[snd_ch] - low < 0) rx_freq[snd_ch] = low; - if (high - rx_freq[snd_ch] < 0) rx_freq[snd_ch] = high; - - tx_freq[snd_ch] = rx_freq[snd_ch]; - - make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); - make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]); - make_core_INTR(snd_ch); - make_core_LPF(snd_ch, lpf[snd_ch]); - - /* - for i = 0 to 16 do - for j = 0 to nr_emph do with DET[j,i] do - begin - minamp[snd_ch] = 0; - maxamp[snd_ch] = 0; - ones[snd_ch] = 0; - zeros[snd_ch] = 0; - sample_cnt[snd_ch] = 0; - bit_cnt[snd_ch] = 0; - bit_osc[snd_ch] = 0; - frame_status[snd_ch] = FRAME_WAIT; - end; - form1.show_modes; - form1.show_freq_a; - form1.show_freq_b; - */ - wf_pointer(soundChannel[snd_ch]); - - -} - -/* -procedure TForm1.show_combobox; -var - i word; -begin - for i = 0 to length(modes_name)-1 do - begin - ComboBox1.Items.Add(modes_name[i]); - ComboBox2.Items.Add(modes_name[i]); - end; - ComboBox1.ItemIndex = ComboBox1.Items.IndexOf(modes_name[Speed[1]]); - ComboBox2.ItemIndex = ComboBox2.Items.IndexOf(modes_name[Speed[2]]); -end; - -function TForm1.get_idx_by_name(name string) word; -var - i word; - found boolean; -begin - i = 0; - found = FALSE; - result = 0; - repeat - if name = modes_name[i] then - begin - found = TRUE; - result = i; - end - else inc(i); - until found or (i = length(modes_name)); -end; - -procedure TForm1.ReadIni; -var - snd_ch byte; -begin - TelIni = TIniFile.Create(cur_dir+"soundmodem.ini"); - with TelIni do - begin - UTC_Time = ReadBool("Init","UTCTime",FALSE); - MainPriority = ReadInteger("Init","Priority",2); - nr_monitor_lines = ReadInteger("Init","NRMonitorLines",500); - ptt = ReadString("Init","PTT","NONE"); - stop_wf = ReadBool("Init","StopWF",FALSE); - raduga = ReadBool("Init","DispMode",DISP_MONO); - stat_log = ReadBool("Init","StatLog",FALSE); - SND_RX_DEVICE = ReadInteger("Init","SndRXDevice",0); - SND_TX_DEVICE = ReadInteger("Init","SndTXDevice",0); - snd_rx_device_name = ReadString("Init","SndRXDeviceName",""); - snd_tx_device_name = ReadString("Init","SndTXDeviceName",""); - RX_SR = ReadInteger("Init","RXSampleRate",11025); - TX_SR = ReadInteger("Init","TXSampleRate",11025); - RX_PPM = ReadInteger("Init","RX_corr_PPM",0); - TX_PPM = ReadInteger("Init","TX_corr_PPM",0); - tx_bufcount = ReadInteger("Init","TXBufNumber",32); - rx_bufcount = ReadInteger("Init","RXBufNumber",32); - DebugMode = ReadInteger("Init","DisableUnit",0); - TX_rotate = ReadBool("Init","TXRotate",FALSE); - DualChan = ReadBool("Init","DualChan",FALSE); - DualPTT = ReadBool("Init","DualPTT",TRUE); - SCO = ReadBool("Init","SCO",FALSE); - stdtones = ReadBool("Init","UseStandardTones",TRUE); - // Channel A settings - maxframe[1] = ReadInteger("AX25_A","Maxframe",3); - fracks[1] = ReadInteger("AX25_A","Retries",15); - frack_time[1] = ReadInteger("AX25_A","FrackTime",5); - idletime[1] = ReadInteger("AX25_A","IdleTime",180); - slottime[1] = ReadInteger("AX25_A","SlotTime",100); - persist[1] = ReadInteger("AX25_A","Persist",128); - resptime[1] = ReadInteger("AX25_A","RespTime",1500); - TXFrmMode[1] = ReadInteger("AX25_A","TXFrmMode",1); - max_frame_collector[1] = ReadInteger("AX25_A","FrameCollector",6); - exclude_callsigns[1] = ReadString("AX25_A","ExcludeCallsigns",""); - exclude_APRS_frm[1] = ReadString("AX25_A","ExcludeAPRSFrmType",""); - KISS_opt[1] = ReadBool("AX25_A","KISSOptimization",FALSE); - dyn_frack[1] = ReadBool("AX25_A","DynamicFrack",FALSE); - recovery[1] = ReadInteger("AX25_A","BitRecovery",0); - NonAX25[1] = ReadBool("AX25_A","NonAX25Frm",FALSE); - MEMRecovery[1] = ReadInteger("AX25_A","MEMRecovery",200); - IPOLL[1] = ReadInteger("AX25_A","IPOLL",80); - MyDigiCall[1] = ReadString("AX25_A","MyDigiCall",""); - tx_hitoneraisedb[1] = ReadInteger("AX25_A","HiToneRaise",0); - // Channel B settings - maxframe[2] = ReadInteger("AX25_B","Maxframe",3); - fracks[2] = ReadInteger("AX25_B","Retries",15); - frack_time[2] = ReadInteger("AX25_B","FrackTime",5); - idletime[2] = ReadInteger("AX25_B","IdleTime",180); - slottime[2] = ReadInteger("AX25_B","SlotTime",100); - persist[2] = ReadInteger("AX25_B","Persist",128); - resptime[2] = ReadInteger("AX25_B","RespTime",1500); - TXFrmMode[2] = ReadInteger("AX25_B","TXFrmMode",1); - max_frame_collector[2] = ReadInteger("AX25_B","FrameCollector",6); - exclude_callsigns[2] = ReadString("AX25_B","ExcludeCallsigns",""); - exclude_APRS_frm[2] = ReadString("AX25_B","ExcludeAPRSFrmType",""); - KISS_opt[2] = ReadBool("AX25_B","KISSOptimization",FALSE); - dyn_frack[2] = ReadBool("AX25_B","DynamicFrack",FALSE); - recovery[2] = ReadInteger("AX25_B","BitRecovery",0); - NonAX25[2] = ReadBool("AX25_B","NonAX25Frm",FALSE); - MEMRecovery[2] = ReadInteger("AX25_B","MEMRecovery",200); - IPOLL[2] = ReadInteger("AX25_B","IPOLL",80); - MyDigiCall[2] = ReadString("AX25_B","MyDigiCall",""); - tx_hitoneraisedb[2] = ReadInteger("AX25_B","HiToneRaise",0); - // Modem settings - pkt_raw_min_len = ReadInteger("Modem","RawPktMinLen",17); - swap_ptt = ReadBool("Modem","SwapPTTPins",FALSE); - inv_ptt = ReadBool("Modem","InvPTTPins",FALSE); - Emph_all[1] = ReadBool("Modem","PreEmphasisAll1",TRUE); - Emph_all[2] = ReadBool("Modem","PreEmphasisAll2",TRUE); - emph_db[1] = ReadInteger("Modem","PreEmphasisDB1",0); - emph_db[2] = ReadInteger("Modem","PreEmphasisDB2",0); - txbpf[1] = ReadInteger("Modem","TXBPF1",500); - txbpf[2] = ReadInteger("Modem","TXBPF2",500); - bpf[1] = ReadInteger("Modem","BPF1",500); - bpf[2] = ReadInteger("Modem","BPF2",500); - lpf[1] = ReadInteger("Modem","LPF1",150); - lpf[2] = ReadInteger("Modem","LPF2",150); - BPF_tap[1] = ReadInteger("Modem","BPFTap1",256); - BPF_tap[2] = ReadInteger("Modem","BPFTap2",256); - LPF_tap[1] = ReadInteger("Modem","LPFTap1",128); - LPF_tap[2] = ReadInteger("Modem","LPFTap2",128); - DCD_threshold = ReadInteger("Modem","DCDThreshold",32); - rx_freq[1] = ReadFloat("Modem","RXFreq1",1700); - rx_freq[2] = ReadFloat("Modem","RXFreq2",1700); - CheckBox1.Checked = ReadBool("Modem","HoldPnt",FALSE); - BIT_AFC = ReadInteger("Modem","AFC",32); - txdelay[1] = ReadInteger("Modem","TxDelay1",250); - txdelay[2] = ReadInteger("Modem","TxDelay2",250); - txtail[1] = ReadInteger("Modem","TxTail1",50); - txtail[2] = ReadInteger("Modem","TxTail2",50); - diddles = ReadInteger("Modem","Diddles",0); - RCVR[1] = ReadInteger("Modem","NRRcvrPairs1",0); - RCVR[2] = ReadInteger("Modem","NRRcvrPairs2",0); - rcvr_offset[1] = ReadInteger("Modem","RcvrShift1",30); - rcvr_offset[2] = ReadInteger("Modem","RcvrShift2",30); - speed[1] = ReadInteger("Modem","ModemType1",SPEED_1200); - speed[2] = ReadInteger("Modem","ModemType2",SPEED_1200); - modem_def[1] = ReadBool("Modem","Default1",TRUE); - modem_def[2] = ReadBool("Modem","Default2",TRUE); - AGWServ = ReadBool("AGWHost","Server",TRUE); - AGWPort = ReadInteger("AGWHost","Port",8000); - KISSServ = ReadBool("KISS","Server",FALSE); - KISSPort = ReadInteger("KISS","Port",8100); - Form1.Top = ReadInteger("Window","Top",0); - Form1.Left = ReadInteger("Window","Left",0); - Form1.Height = ReadInteger("Window","Height",656); - Form1.Width = ReadInteger("Window","Width",764); - MinOnStart = ReadBool("Window","MinimizedOnStartup",FALSE); - Firstwaterfall1.checked = ReadBool("Window","Waterfall1",TRUE); - Secondwaterfall1.checked = ReadBool("Window","Waterfall2",FALSE); - Statustable1.checked = ReadBool("Window","StatTable",TRUE); - Monitor1.checked = ReadBool("Window","Monitor",TRUE); - RXRichEdit1.Font.Size = ReadInteger("Font","Size",RXRichEdit1.Font.Size); - RXRichEdit1.Font.Name = ReadString("Font","Name",RXRichEdit1.Font.Name); - end; - TelIni.Free; - newAGWPort = AGWPort; - newAGWServ = AGWServ; - newKISSPort = KISSPort; - newKISSServ = KISSServ; - - RX_SampleRate = RX_SR+RX_SR*0.000001*RX_PPM; - TX_SampleRate = TX_SR+TX_SR*0.000001*TX_PPM; - - panels[4] = Monitor1.Checked; - panels[3] = Statustable1.Checked; - panels[2] = Firstwaterfall1.Checked; - panels[1] = Secondwaterfall1.Checked; - - if tx_bufcount>255 then tx_bufcount = 255; - if tx_bufcount<2 then tx_bufcount = 2; - if rx_bufcount>255 then rx_bufcount = 255; - if rx_bufcount<2 then rx_bufcount = 2; - - if not (diddles in [0..2]) then diddles = 0; - - if nr_monitor_lines>65535 then nr_monitor_lines = 65535; - if nr_monitor_lines<10 then nr_monitor_lines = 10; - - if not (MainPriority in [0..3]) then MainPriority = 2; - - for snd_ch = 1 to 2 do - begin - - tx_hitoneraise[snd_ch] = power(10,-abs(tx_hitoneraisedb[snd_ch])/20); - - if IPOLL[snd_ch]<0 then IPOLL[snd_ch] = 0; - if IPOLL[snd_ch]>65535 then IPOLL[snd_ch] = 65535; - - if MEMRecovery[snd_ch]<1 then MEMRecovery[snd_ch] = 1; - if MEMRecovery[snd_ch]>65535 then MEMRecovery[snd_ch] = 65535; - - get_exclude_list(AnsiUpperCase(MyDigiCall[snd_ch]),list_digi_callsigns[snd_ch]); - get_exclude_list(AnsiUpperCase(exclude_callsigns[snd_ch]),list_exclude_callsigns[snd_ch]); - get_exclude_frm(exclude_APRS_frm[snd_ch],list_exclude_APRS_frm[snd_ch]); - - if resptime[snd_ch]<0 then resptime[snd_ch] = 0; - if resptime[snd_ch]>65535 then resptime[snd_ch] = 65535; - if persist[snd_ch]>255 then persist[snd_ch] = 255; - if persist[snd_ch]<32 then persist[snd_ch] = 32; - if fracks[snd_ch]<1 then fracks[snd_ch] = 1; - if frack_time[snd_ch]<1 then frack_time[snd_ch] = 1; - if idletime[snd_ch]0 then - for k = 0 to numdevs-1 do - begin - waveOutGetDevCaps(k,@DevOutCaps,sizeof(DevOutCaps)); - TXDevList.Add(DevOutCaps.szpname); - end; - numdevs = WaveInGetNumDevs; - if numdevs>0 then - for k = 0 to numdevs-1 do - begin - waveInGetDevCaps(k,@DevInCaps,sizeof(DevInCaps)); - RXDevList.Add(DevInCaps.szpname); - end; - // TX Dev - if (snd_tx_device<0) or (snd_tx_device> = TXDevList.Count) then snd_tx_device = 0; - if TXDevList.Count>0 then - if TXDevList.Strings[snd_tx_device]<>snd_tx_device_name then - begin - i = TXDevList.IndexOf(snd_tx_device_name); - if i> = 0 then snd_tx_device = i else snd_tx_device_name = TXDevList.Strings[snd_tx_device]; - end; - // RX Dev - if (snd_rx_device<0) or (snd_rx_device> = RXDevList.Count) then snd_rx_device = 0; - if RXDevList.Count>0 then - if RXDevList.Strings[snd_rx_device]<>snd_rx_device_name then - begin - i = RXDevList.IndexOf(snd_rx_device_name); - if i> = 0 then snd_rx_device = i else snd_rx_device_name = RXDevList.Strings[snd_rx_device]; - end; - RXDevList.Free; - TXDevList.Free; -end; - -procedure TForm1.startrx; -var - OpenResult MMRESULT; - Loop integer; - ErrorText string; -Begin - RX_device = TRUE; - RXBufferLength = rx_bufsize * Channels * (BitsPerSample div 8); - with WaveFormat do - begin - wFormatTag = WAVE_FORMAT_PCM; - nChannels = Channels; - nSamplesPerSec = RX_SR; - nAvgBytesPerSec = RX_SR * Channels * (BitsPerSample div 8); - nBlockAlign = Channels * (BitsPerSample div 8); - wBitsPerSample = BitsPerSample; - cbSize = 0; - end; - OpenResult = waveInOpen (@WaveInHandle,SND_RX_DEVICE,@WaveFormat,integer(Self.Handle),0,CALLBACK_WINDOW); - if OpenResult = MMSYSERR_NOERROR then - begin - for Loop = 1 to rx_bufcount do - begin - GetMem(RX_pbuf[Loop], RXBufferLength); - RX_header[Loop].lpData = RX_pbuf[Loop]; - RX_header[Loop].dwBufferLength = RXBufferLength; - RX_header[Loop].dwUser = Loop; - RX_header[Loop].dwFlags = 0; - RX_header[Loop].dwLoops = 0; - OpenResult = WaveInPrepareHeader(WaveInhandle, @RX_header[Loop], sizeof(TWaveHdr)); - if OpenResult = MMSYSERR_NOERROR then WaveInAddBuffer(WaveInHandle, @RX_header[Loop], sizeof(TWaveHdr)) - else - begin - case OpenResult of - MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; - MMSYSERR_NODRIVER ErrorText = "no device driver present"; - MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; - else ErrorText = "unknown error"; - end; - MessageDlg(format("Error adding buffer %d device (%s)",[Loop, ErrorText]), mtError, [mbOk], 0); - end; - end; - WaveInStart(WaveInHandle); - end - else - begin - case OpenResult of - MMSYSERR_ERROR ErrorText = "unspecified error"; - MMSYSERR_BADDEVICEID ErrorText = "device ID out of range"; - MMSYSERR_NOTENABLED ErrorText = "driver failed enable"; - MMSYSERR_ALLOCATED ErrorText = "device already allocated"; - MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; - MMSYSERR_NODRIVER ErrorText = "no device driver present"; - MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; - MMSYSERR_NOTSUPPORTED ErrorText = "function isn""t supported"; - MMSYSERR_BADERRNUM ErrorText = "error value out of range"; - MMSYSERR_INVALFLAG ErrorText = "invalid flag passed"; - MMSYSERR_INVALPARAM ErrorText = "invalid parameter passed"; - MMSYSERR_HANDLEBUSY ErrorText = "handle being used simultaneously on another thread (eg callback)"; - MMSYSERR_INVALIDALIAS ErrorText = "specified alias not found"; - MMSYSERR_BADDB ErrorText = "bad registry database"; - MMSYSERR_KEYNOTFOUND ErrorText = "registry key not found"; - MMSYSERR_READERROR ErrorText = "registry read error"; - MMSYSERR_WRITEERROR ErrorText = "registry write error"; - MMSYSERR_DELETEERROR ErrorText = "registry delete error"; - MMSYSERR_VALNOTFOUND ErrorText = "registry value not found"; - MMSYSERR_NODRIVERCB ErrorText = "driver does not call DriverCallback"; - else ErrorText = "unknown error"; - end; - MessageDlg(format("Error opening wave input device (%s)",[ErrorText]), mtError, [mbOk], 0); - RX_device = FALSE; - end; -end; - -procedure TForm1.stoprx; -var - Loop integer; -begin - if not RX_device then exit; - WaveInStop(WaveInHandle); - WaveInReset(WaveInHandle); - for Loop = 1 to rx_bufcount do - WaveInUnPrepareHeader(WaveInHandle, @RX_header[Loop], sizeof(TWaveHdr)); - WaveInClose(WaveInHandle); - for Loop = 1 to rx_bufcount do - begin - if RX_pbuf[Loop]<>nil then - begin - FreeMem(RX_pbuf[Loop]); - RX_pbuf[Loop] = nil; - end; - end; - RX_device = FALSE; -end; - -procedure TForm1.make_wave_buf(snd_ch byte; buf PChar); -const - amplitude = 22000; -var - i word; -begin - modulator(snd_ch,audio_buf[snd_ch],tx_bufsize); - if tx_status[snd_ch] = TX_NO_DATA then buf_status[snd_ch] = BUF_EMPTY; - for i = 0 to tx_bufsize-1 do - begin - case snd_ch of - 1 - begin - // left channel - PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]); - Inc(PSmallInt(Buf)); - // right channel - if SCO then PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]) else PSmallInt(buf)^ = 0; - Inc(PSmallInt(Buf)); - end; - 2 - begin - // left channel - if SCO then PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]) else PSmallInt(buf)^ = 0; - Inc(PSmallInt(Buf)); - // right channel - PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]); - Inc(PSmallInt(Buf)); - end; - end; - end; -end; - -procedure TForm1.starttx(snd_ch byte); -var - OpenResult MMRESULT; - Loop integer; - ErrorText string; - BufferLength longint; -Begin - if snd_status[snd_ch]<>SND_IDLE then exit; - BufferLength = tx_bufsize * Channels * (BitsPerSample div 8); - with WaveFormat do - begin - wFormatTag = WAVE_FORMAT_PCM; - nChannels = Channels; - nSamplesPerSec = TX_SR; - nAvgBytesPerSec = TX_SR * Channels * (BitsPerSample div 8); - nBlockAlign = Channels * (BitsPerSample div 8); - wBitsPerSample = BitsPerSample; - cbSize = 0; - end; - OpenResult = WaveOutOpen (@WaveOutHandle[snd_ch],SND_TX_DEVICE,@WaveFormat,integer(Self.Handle),0,CALLBACK_WINDOW); - if OpenResult = MMSYSERR_NOERROR then - begin - snd_status[snd_ch] = SND_TX; - buf_status[snd_ch] = BUF_FULL; - tx_status[snd_ch] = TX_SILENCE; - tx_buf_num[snd_ch] = 0; - tx_buf_num1[snd_ch] = 0; - for Loop = 1 to tx_bufcount do - begin - GetMem(TX_pbuf[snd_ch][Loop], BufferLength); - TX_header[snd_ch][Loop].lpData = TX_pbuf[snd_ch][Loop]; - TX_header[snd_ch][Loop].dwBufferLength = BufferLength; - TX_header[snd_ch][Loop].dwUser = 0; - TX_header[snd_ch][Loop].dwFlags = 0; - TX_header[snd_ch][Loop].dwLoops = 0; - OpenResult = WaveOutPrepareHeader(WaveOuthandle[snd_ch], @TX_header[snd_ch][Loop], sizeof(TWaveHdr)); - if OpenResult = MMSYSERR_NOERROR then - begin - // Заполнить буфер на передачу - if buf_status[snd_ch] = BUF_FULL then - begin - make_wave_buf(snd_ch,TX_pbuf[snd_ch][Loop]); - WaveOutWrite(WaveOutHandle[snd_ch],@TX_header[snd_ch][Loop],sizeof(TWaveHdr)); - inc(tx_buf_num1[snd_ch]); - end; - end - else - begin - case OpenResult of - MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; - MMSYSERR_NODRIVER ErrorText = "no device driver present"; - MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; - else ErrorText = "unknown error"; - end; - MessageDlg(format("Error adding buffer %d device (%s)",[Loop, ErrorText]), mtError, [mbOk], 0); - end; - end; - end - else - begin - case OpenResult of - MMSYSERR_ERROR ErrorText = "unspecified error"; - MMSYSERR_BADDEVICEID ErrorText = "device ID out of range"; - MMSYSERR_NOTENABLED ErrorText = "driver failed enable"; - MMSYSERR_ALLOCATED ErrorText = "device already allocated"; - MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; - MMSYSERR_NODRIVER ErrorText = "no device driver present"; - MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; - MMSYSERR_NOTSUPPORTED ErrorText = "function isn""t supported"; - MMSYSERR_BADERRNUM ErrorText = "error value out of range"; - MMSYSERR_INVALFLAG ErrorText = "invalid flag passed"; - MMSYSERR_INVALPARAM ErrorText = "invalid parameter passed"; - MMSYSERR_HANDLEBUSY ErrorText = "handle being used simultaneously on another thread (eg callback)"; - MMSYSERR_INVALIDALIAS ErrorText = "specified alias not found"; - MMSYSERR_BADDB ErrorText = "bad registry database"; - MMSYSERR_KEYNOTFOUND ErrorText = "registry key not found"; - MMSYSERR_READERROR ErrorText = "registry read error"; - MMSYSERR_WRITEERROR ErrorText = "registry write error"; - MMSYSERR_DELETEERROR ErrorText = "registry delete error"; - MMSYSERR_VALNOTFOUND ErrorText = "registry value not found"; - MMSYSERR_NODRIVERCB ErrorText = "driver does not call DriverCallback"; - else ErrorText = "unknown error"; - end; - MessageDlg(format("Error opening wave output device (%s)",[ErrorText]), mtError, [mbOk], 0); - end; -end; - -procedure TForm1.stoptx(snd_ch byte); -var - Loop integer; -begin - if snd_status[snd_ch]<>SND_TX then exit; - WaveOutReset(WaveOutHandle[snd_ch]); - for Loop = 1 to tx_bufcount do - WaveOutUnPrepareHeader(WaveOutHandle[snd_ch], @TX_header[snd_ch][Loop], sizeof(TWaveHdr)); - WaveOutClose(WaveOutHandle[snd_ch]); - for Loop = 1 to tx_bufcount do - begin - if TX_pbuf[snd_ch][Loop]<>nil then - begin - FreeMem(TX_pbuf[snd_ch][Loop]); - TX_pbuf[snd_ch][Loop] = nil; - end; - end; - WaveOutHandle[snd_ch] = 0; - snd_status[snd_ch] = SND_IDLE; -end; - -procedure show_grid_title; -const - title array [0..10] of string = ("MyCall","DestCall","Status","Sent pkts","Sent bytes","Rcvd pkts","Rcvd bytes","Rcvd FC","CPS TX","CPS RX","Direction"); -var - i byte; -begin - for i = 0 to 10 do Form1.StringGrid1.Cells[i,0] = title[i]; -end; -*/ -/* - -procedure disp1(src1,src2 array of single); -var - i,n word; - k,k1,amp1,amp2,amp3,amp4 single; - bm TBitMap; -begin - bm = TBitMap.Create; - bm.pixelformat = pf32bit; - //bm.pixelformat = pf24bit; - bm.Width = Form1.PaintBox2.Width; - bm.Height = Form1.PaintBox2.Height; - amp1 = 0; - amp3 = 0; - //k = 0.20; - k = 50000; - k1 = 0; - //k = 1000; - //k = 0.00001; - bm.Canvas.MoveTo(0,50); - bm.Canvas.LineTo(512,50); - n = 0; - for i = 0 to RX_Bufsize-1 do - begin - begin - amp2 = src1[i]; - amp4 = src2[i]; - bm.Canvas.Pen.Color = clRed; - bm.Canvas.MoveTo(n,50-round(amp1*k1)); - bm.Canvas.LineTo(n+1,50-round(amp2*k1)); - bm.Canvas.Pen.Color = clBlue; - bm.Canvas.MoveTo(n,50-round(amp3*k)); - bm.Canvas.LineTo(n+1,50-round(amp4*k)); - bm.Canvas.Pen.Color = clBlack; - inc(n); - amp1 = amp2; - amp3 = amp4; - end; - end; - Form1.PaintBox2.Canvas.Draw(0,0,bm); - bm.Free; -end; -*/ - -/* - -procedure TForm1.wf_pointer(snd_ch byte); -var - x single; - x1,x2,y,k,pos1,pos2,pos3 word; -begin - k = 24; - x = FFTSize/RX_SampleRate; - pos1 = round((rx_freq[snd_ch]-0.5*rx_shift[snd_ch])*x)-5; - pos2 = round((rx_freq[snd_ch]+0.5*rx_shift[snd_ch])*x)-5; - pos3 = round(rx_freq[snd_ch]*x); - x1 = pos1+5; - x2 = pos2+5; - y = k+5; - with bm3.Canvas do - begin - Draw(0,20,bm[snd_ch]); - Pen.Color = clWhite; - Brush.Color = clRed; - Polygon([Point(x1+3,y),Point(x1,y-7),Point(x1-3,y),Point(x2+3,y),Point(x2,y-7),Point(x2-3,y)]); - Brush.Color = clBlue; - Polygon([Point(x1+3,y),Point(x1,y+7),Point(x1-3,y),Point(x2+3,y),Point(x2,y+7),Point(x2-3,y)]); - Polyline([Point(pos3,k+1),Point(pos3,k+9)]); - Pen.Color = clBlack; - end; - case snd_ch of - 1 PaintBox1.Canvas.Draw(0,0,bm3); - 2 PaintBox3.Canvas.Draw(0,0,bm3); - end; -end; - -procedure TForm1.wf_Scale; -var - k single; - max_freq,x,i word; -begin - max_freq = round(RX_SampleRate*0.005); - k = 100*FFTSize/RX_SampleRate; - with bm1.Canvas do - begin - Brush.Color = clBlack; - FillRect(ClipRect); - Pen.Color = clWhite; - Font.Color = clWhite; - Font.Size = 8; - for i = 0 to max_freq do - begin - x = round(k*i); - if x<1025 then - begin - if (i mod 5) = 0 then - PolyLine([Point(x,20),Point(x,13)]) - else - PolyLine([Point(x,20),Point(x,16)]); - if (i mod 10) = 0 then TextOut(x-12,1,inttostr(i*100)); - end; - end; - Pen.Color = clBlack; - end; - bm3.Canvas.Draw(0,0,bm1); -end; -*/ - -void chk_snd_buf(float * buf, int len) -{ - word i; - boolean good; - single prev_amp; - - if (len < 2) - return; - - good = FALSE; - i = 1; - prev_amp = buf[0]; - do - { - if (buf[i++] != prev_amp) - good = TRUE; - - } while (good == FALSE && i < len); - - // Make noise - if (!good) - for (i = 0; i < len; i++) - buf[i] = rand(); -} - -#ifdef WIN32 - -typedef void *HANDLE; -typedef unsigned long DWORD; - -#define WINAPI __stdcall -__declspec(dllimport) -DWORD -WINAPI -WaitForSingleObject( - __in HANDLE hHandle, - __in DWORD dwMilliseconds -); - - - - -#define pthread_t uintptr_t - -uintptr_t _beginthread(void(__cdecl *start_address)(void *), unsigned stack_size, void *arglist); -#else - -#include - -pthread_t _beginthread(void(*start_address)(void *), unsigned stack_size, void * arglist) -{ - pthread_t thread; - - if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*)arglist) != 0) - perror("New Thread"); - - return thread; -} - -#endif - -void runModemthread(void * param); - -void runModems() -{ - int snd_ch, res; - pthread_t thread[4] = { 0,0,0,0 }; - - for (snd_ch = 0; snd_ch < 4; snd_ch++) - { - if (soundChannel[snd_ch] == 0) // Unused channed - continue; - - if (modem_mode[snd_ch] == MODE_ARDOP) - continue; // Processed above - - // do we need to do this again ?? -// make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); - - if (multiCore) // Run modems in separate threads - thread[snd_ch] = _beginthread(runModemthread, 0, (void *)(size_t)snd_ch); - else - runModemthread((void *)(size_t)snd_ch); - } - - if (multiCore) - { -#ifdef WIN32 - if (thread[0]) WaitForSingleObject(&thread[0], 2000); - if (thread[1]) WaitForSingleObject(&thread[1], 2000); - if (thread[2]) WaitForSingleObject(&thread[2], 2000); - if (thread[3]) WaitForSingleObject(&thread[3], 2000); -#else - if (thread[0]) pthread_join(thread[0], &res); - if (thread[1]) pthread_join(thread[1], &res); - if (thread[2]) pthread_join(thread[2], &res); - if (thread[3]) pthread_join(thread[3], &res); -#endif - } -} - -Byte rcvr_idx; - -void runModemthread(void * param) -{ - int snd_ch = (int)(size_t)param; - - // I want to run lowest to highest to simplify my display - - int offset = -(RCVR[snd_ch] * rcvr_offset[snd_ch]); // lowest - int lastrx = RCVR[snd_ch] * 2; - - if (soundChannel[snd_ch] == 0) // Unused channed - return; - - for (rcvr_idx = 0; rcvr_idx <= lastrx; rcvr_idx++) - { - active_rx_freq[snd_ch] = rxOffset + chanOffset[snd_ch] + rx_freq[snd_ch] + offset; - offset += rcvr_offset[snd_ch]; - - Demodulator(snd_ch, rcvr_idx, src_buf[modemtoSoundLR[snd_ch]], rcvr_idx == lastrx, offset == 0); - } -} - -// I think this processes a buffer of samples - -void BufferFull(short * Samples, int nSamples) // These are Stereo Samples -{ - word i, i1; - Byte snd_ch, rcvr_idx; - int buf_offset; - int Needed; - short * data1; - short * data2 = 0; - - // if UDP server active send as UDP Datagram - - if (UDPServ) // Extract just left - { - short Buff[1024]; - - i1 = 0; - - for (i = 0; i < rx_bufsize; i++) - { - Buff[i] = Samples[i1]; - i1 += 2; - } - - sendSamplestoUDP(Buff, 512, TXPort); - } - - // Do RSID processing (can we also use this for waterfall?? - - RSIDProcessSamples(Samples, nSamples); - - // Do FFT on every 4th buffer (2048 samples) - - // if in Satellite Mode look for a Tuning signal - -// if (SatelliteMode) -// { -// doTuning(Samples, nSamples); -// } - - for (snd_ch = 0; snd_ch < 4; snd_ch++) - { - if (soundChannel[snd_ch] == 0) // Unused channed - continue; - - if (pnt_change[snd_ch]) - { - make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); - make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]); - pnt_change[snd_ch] = FALSE; - } - - } - - // I don't think we should process RX if either is sending - - if (snd_status[0] != SND_TX && snd_status[1] != SND_TX && snd_status[2] != SND_TX && snd_status[3] != SND_TX) - { - for (snd_ch = 0; snd_ch < 4; snd_ch++) - { - if (soundChannel[snd_ch] == 0) // Unused channed - continue; - - if (modem_mode[snd_ch] == MODE_ARDOP) - { - short ardopbuff[1200]; - i1 = 0; - - for (i = 0; i < rx_bufsize; i++) - { - ardopbuff[i] = Samples[i1]; - i1++; - i1++; - } - - ARDOPProcessNewSamples(ardopbuff, nSamples); - } - } - - // extract mono samples from data. - - data1 = Samples; - - i1 = 0; - - // src_buf[0] is left data,. src_buf[1] right - - // We could skip extracting other channel if only one in use - is it worth it?? - - if (UsingBothChannels) - { - for (i = 0; i < rx_bufsize; i++) - { - src_buf[0][i] = data1[i1]; - i1++; - src_buf[1][i] = data1[i1]; - i1++; - } - } - else if (UsingRight) - { - // Extract just right - - i1 = 1; - - for (i = 0; i < rx_bufsize; i++) - { - src_buf[1][i] = data1[i1]; - i1 += 2; - } - } - else - { - // Extract just left - - for (i = 0; i < rx_bufsize; i++) - { - src_buf[0][i] = data1[i1]; - i1 += 2; - } - } - - // Run modems before waterfall so fft buffer has values from before sync was detected - - runModems(); - - // Do whichever waterfall is needed - - // We need to run the waterfall FFT for the frequency guessing to work - - int FirstWaterfallChan = 0; - short * ptr1 = &fft_buf[0][fftCount]; - short * ptr2 = &fft_buf[1][fftCount]; - - int remainingSamples = rx_bufsize; - - if (UsingLeft == 0) - { - FirstWaterfallChan = 1; - data1++; // to Right Samples - } - - if (UsingBothChannels) // Second is always Right - data2 = &Samples[1]; // to Right Samples - - - // FFT size isn't necessarily a multiple of rx_bufsize, so this is a bit more complicated - // Save FFTSize samples then process. Put the unused bits back in the fft buffer - - // Collect samples for both channels if needed - - Needed = FFTSize - fftCount; - - if (Needed <= rx_bufsize) - { - // add Needed samples to fft_buf and process. Copy rest to start of fft_buf - - for (i = 0; i < Needed; i++) - { - *ptr1++ = *data1; - data1 += 2; - } - - doWaterfall(FirstWaterfallChan); - - if (data2) - { - for (i = 0; i < Needed; i++) - { - *ptr2++ = *data2; - data2 += 2; - } - doWaterfall(1); - } - - remainingSamples = rx_bufsize - Needed; - fftCount = 0; - - ptr1 = &fft_buf[0][0]; - ptr2 = &fft_buf[1][0]; - } - - for (i = 0; i < remainingSamples; i++) - { - *ptr1++ = *data1; - data1 += 2; - } - - if (data2) - { - for (i = 0; i < remainingSamples; i++) - { - *ptr2++ = *data2; - data2 += 2; - } - } - fftCount += remainingSamples; - } - - if (TimerEvent == TIMER_EVENT_ON) - { - timer_event(); -// timer_event2(); - } -} - - /* - -procedure TForm1.BufferFull1(var Msg TMessage); -var - i,snd_ch byte; -begin - for snd_ch = 1 to 2 do - if pnt_change[snd_ch] then - begin - make_core_BPF(snd_ch,rx_freq[snd_ch],bpf[snd_ch]); - make_core_TXBPF(snd_ch,tx_freq[snd_ch],txbpf[snd_ch]); - pnt_change[snd_ch] = FALSE; - end; - snd_ch = 0; - for i = 1 to 2 do if msg.WParam = WaveOutHandle[i] then snd_ch = i; - if (snd_ch = 0) then exit; - if (snd_status[snd_ch]<>SND_TX) then exit; - inc(tx_buf_num[snd_ch]); if tx_buf_num[snd_ch]>tx_bufcount then tx_buf_num[snd_ch] = 1; - if (buf_status[snd_ch] = BUF_EMPTY) and (tx_buf_num[snd_ch] = tx_buf_num1[snd_ch]) then TX2RX(snd_ch); - if buf_status[snd_ch] = BUF_FULL then - beginf - make_wave_buf(snd_ch,TX_pbuf[snd_ch][tx_buf_num[snd_ch]]); - WaveOutWrite(WaveOutHandle[snd_ch],@TX_header[snd_ch][tx_buf_num[snd_ch]],sizeof(TWaveHdr)); - inc(tx_buf_num1[snd_ch]); if tx_buf_num1[snd_ch]>tx_bufcount then tx_buf_num1[snd_ch] = 1; - end; -end; - -procedure TForm1.TX2RX(snd_ch byte); -begin - if snd_status[snd_ch] = SND_TX then stoptx(snd_ch); - if snd_status[snd_ch] = SND_IDLE then begin pttoff(snd_ch); end; -end; -*/ - - -// Monitor Code - from moncode.asm - - -#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND -#define RESP 2 // CURRENT MSG IS RESPONSE -#define VER1 1 // CURRENT MSG IS VERSION 1 - - -#define UI 3 -#define SABM 0x2F -#define DISC 0x43 -#define DM 0x0F -#define UA 0x63 -#define FRMR 0x87 -#define RR 1 -#define RNR 5 -#define REJ 9 - -#define SREJ 0x0D -#define SABME 0x6F -#define XID 0xAF -#define TEST 0xE3 - - -#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE - -#define NETROM_PID 0xCF -#define IP_PID 0xCC -#define ARP_PID 0xCD - -#define NODES_SIG 0xFF - -char FrameData[1024] = ""; - -char * frame_monitor(string * frame, char * code, int tx_stat) -{ - char mon_frm[512]; - char AGW_path[256]; - string * AGW_data; - - const Byte * frm = "???"; - Byte * datap; - Byte _data[512] = ""; - Byte * p_data = _data; - int _datalen; - - char agw_port; - char CallFrom[10], CallTo[10], Digi[80]; - - char TR = 'R'; - char codestr[64] = ""; - - integer i; - char time_now[32]; - int len; - - AGWUser * AGW; - - Byte pid, nr, ns, f_type, f_id; - Byte rpt, cr, pf; - Byte path[80]; - char c; - const char * p; - - string * data = newString(); - - if (code[0] && strlen(code) < 60) - sprintf(codestr, "[%s]", code); - - if (tx_stat) - TR = 'T'; - - decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); - - datap = data->Data; - - len = data->Length; - - // if (pid == 0xCF) - // data = parse_NETROM(data, f_id); - // IP parsing - // else if (pid == 0xCC) - // data = parse_IP(data); - // ARP parsing - // else if (pid == 0xCD) - // data = parse_ARP(data); - // - - if (len > 0) - { - for (i = 0; i < len; i++) - { - if (datap[i] > 31 || datap[i] == 13 || datap[i] == 9) - *(p_data++) = datap[i]; - } - } - - _datalen = p_data - _data; - - if (_datalen) - { - Byte * ptr = _data; - i = 0; - - // remove successive cr or cr on end while (i < _datalen) - - while (i < _datalen) - { - if ((_data[i] == 13) && (_data[i + 1] == 13)) - i++; - else - *(ptr++) = _data[i++]; - } - - if (*(ptr - 1) == 13) - ptr--; - - *ptr = 0; - - _datalen = ptr - _data; - } - - get_monitor_path(path, CallTo, CallFrom, Digi); - - if (cr) - { - c = 'C'; - if (pf) - p = " P"; - else p = ""; - } - else - { - c = 'R'; - if (pf) - p = " F"; - else - p = ""; - } - - switch (f_id) - { - case I_I: - - frm = "I"; - break; - - case S_RR: - - frm = "RR"; - break; - - case S_RNR: - - frm = "RNR"; - break; - - case S_REJ: - - frm = "REJ"; - break; - - case S_SREJ: - - frm = "SREJ"; - break; - - case U_SABM: - - frm = "SABM"; - break; - - case SABME: - - frm = "SABME"; - break; - - case U_DISC: - - frm = "DISC"; - break; - - case U_DM: - - frm = "DM"; - break; - - case U_UA: - - frm = "UA"; - break; - - case U_FRMR: - - frm = "FRMR"; - break; - - case U_UI: - - frm = "UI"; - } - - if (Digi[0]) - sprintf(AGW_path, "Fm %s To %s Via %s <%s %c%s",CallFrom, CallTo, Digi, frm, c, p); - else - sprintf(AGW_path, "Fm %s To %s <%s %c %s", CallFrom, CallTo, frm, c, p); - - - switch (f_type) - { - case I_FRM: - - //mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' S' + inttostr(ns) + ' pid=' + dec2hex(pid) + ' Len=' + inttostr(len) + ' >' + time_now + #13 + _data + #13#13; - sprintf(mon_frm, "%s R%d S%d pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, nr, ns, pid, len, ShortDateTime(), TR, codestr, _data); - - break; - - case U_FRM: - - if (f_id == U_UI) - { - sprintf(mon_frm, "%s pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, pid, len, ShortDateTime(), TR, codestr, _data); // "= AGW_path + ctrl + '>' + time_now + #13; - } - else if (f_id == U_FRMR) - { - sprintf(mon_frm, "%s>%02x %02x %02x[%s]\r", AGW_path, datap[0], datap[1], datap[2], ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13; - } - else - sprintf(mon_frm, "%s>[%s%c]%s\r", AGW_path, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13; - - break; - - case S_FRM: - - // mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' >' + time_now + #13; - sprintf(mon_frm, "%s R%d>[%s%c]%s\r", AGW_path, nr, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13; - - break; - - } - sprintf(FrameData, "%s", mon_frm); - return FrameData; -} - - -/* -procedure TForm1.RX2TX(snd_ch byte); -begin - if snd_status[snd_ch] = SND_IDLE then begin ptton(snd_ch); starttx(snd_ch); end; -end; - -function TForm1.frame_monitor(s,code string; tx_stat boolean) string; -var - len word; - s_tx_stat string; - time_now,s1,c,p string; - callfrom,callto,digi,path,data,frm string; - frm_body string; - pid,nr,ns,f_type,f_id byte; - rpt,cr,pf boolean; - i word; -begin - decode_frame(s,path,data,pid,nr,ns,f_type,f_id,rpt,pf,cr); - len = length(data); - // NETROM parsing - if pid = $CF then data = parse_NETROM(data,f_id); - // IP parsing - if pid = $CC then data = parse_IP(data); - // ARP parsing - if pid = $CD then data = parse_ARP(data); - // - get_monitor_path(path,CallTo,CallFrom,Digi); - if cr then - begin - c = "C"; - if pf then p = " P" else p = ""; - end - else - begin - c = "R"; - if pf then p = " F" else p = ""; - end; - frm = "UNKN"; - case f_id of - I_I frm = "I"; - S_RR frm = "RR"; - S_RNR frm = "RNR"; - S_REJ frm = "REJ"; - U_SABM frm = "SABM"; - U_DISC frm = "DISC"; - U_DM frm = "DM"; - U_UA frm = "UA"; - U_FRMR frm = "FRMR"; - U_UI frm = "UI"; - end; - case tx_stat of - TRUE s_tx_stat = "T"; - FALSE s_tx_stat = "R"; - end; - s1 = ""; - - if code<>"" then code = " ["+code+"]"; - if UTC_Time then time_now = " [UTC"+get_UTC_time+s_tx_stat+"]" - else time_now = " ["+FormatDateTime("hhmmss",now)+s_tx_stat+"]"; - - if digi = "" then frm_body = "Fm "+CallFrom+" To "+CallTo+" <"+frm+" "+c+p - else frm_body = "Fm "+CallFrom+" To "+CallTo+" Via "+Digi+" <"+frm+" "+c+p; - case f_type of - I_FRM frm_body = frm_body+" R"+inttostr(nr)+" S"+inttostr(ns)+" Pid = "+dec2hex(pid)+" Len = "+inttostr(len)+">"+time_now+code+#13+data; - U_FRM if f_id = U_UI then frm_body = frm_body+" Pid = "+dec2hex(pid)+" Len = "+inttostr(len)+">"+time_now+code+#13+data - else if f_id = U_FRMR then begin data = copy(data+#0#0#0,1,3); frm_body = frm_body+">"+time_now+code+#13+inttohex((byte(data[1]) shl 16) or (byte(data[2]) shl 8) or byte(data[3]),6) end - else frm_body = frm_body+">"+time_now+code; - S_FRM frm_body = frm_body+" R"+inttostr(nr)+">"+time_now+code; - end; - for i = 1 to length(frm_body) do - begin - if frm_body[i]>#31 then s1 = s1+frm_body[i]; - if frm_body[i] = #13 then s1 = s1+#13#10; - if frm_body[i] = #10 then s1 = s1+""; - if frm_body[i] = #9 then s1 = s1+#9; - end; - result = s1; -end; - -procedure TForm1.waterfall_init; -begin - bm[1] = TBitMap.Create; - bm[2] = TBitMap.Create; - bm1 = TBitMap.Create; - bm2 = TBitMap.Create; - bm3 = TBitMap.Create; - bm[1].pixelformat = pf32bit; - bm[2].pixelformat = pf32bit; - bm1.pixelformat = pf32bit; - bm2.pixelformat = pf32bit; - bm3.pixelformat = pf32bit; - bm[1].Height = PaintBox1.Height-20; - bm[1].Width = PaintBox1.width; - bm[2].Height = PaintBox1.Height-20; - bm[2].Width = PaintBox1.width; - bm1.Height = 20; - bm1.Width = PaintBox1.width; - bm3.Height = PaintBox1.Height; - bm3.Width = PaintBox1.width; -end; - -procedure TForm1.waterfall_free; -begin - bm[1].Free; - bm[2].Free; - bm1.Free; - bm2.Free; - bm3.Free; -end; - -procedure TForm1.StartAGW; -begin - try - ServerSocket1.Port = AGWPort; - ServerSocket1.Active = AGWServ; - except - ServerSocket1.Active = FALSE; - MessageDlg("AGW host port is busy!", mtWarning,[mbOk],0); - end; -end; - -procedure TForm1.StartKISS; -begin - try - ServerSocket2.Port = KISSPort; - ServerSocket2.Active = KISSServ; - except - ServerSocket2.Active = FALSE; - MessageDlg("KISS port is busy!", mtWarning,[mbOk],0); - end; -end; - -procedure fft_window_init; -var - mag single; - i word; -begin - mag = 2*pi/(FFTSize-1); - for i = 0 to FFTSize-1 do fft_window_arr[i] = 0.5-0.5*cos(i*mag); //hann -end; - -procedure TForm1.FormCreate(Sender TObject); -begin - if hPrevInst <> 0 then begin - MessageDlg("Программа уже запущена!", mtError, [mbOk], 0); - Application.Terminate; - end; - RS = TReedSolomon.Create(Self); - MainThreadHandle = GetCurrentThread; - form1.Caption = MODEM_CAPTION+" - Ver "+MODEM_VERSION; - cur_dir = ExtractFilePath(Application.ExeName); - fft_window_init; - detector_init; - kiss_init; - agw_init; - ax25_init; - init_raduga; - waterfall_init; - ReadIni; - show_combobox; - show_grid_title; - show_mode_panels; - show_panels; - wf_pointer(1); - wf_pointer(2); - wf_Scale; - ChangePriority; - Visible = TRUE; - StartAGW; - StartKISS; - PTTOpen; - startrx; - TimerEvent = TIMER_EVENT_OFF; - if (debugmode and DEBUG_TIMER) = 0 then create_timer1; - if MinOnStart then WindowState = wsMinimized; -end; - -procedure TForm1.TrackBar1Change(Sender TObject); -begin - dcd_threshold = TrackBar1.position; -end; -*/ - -void Timer_Event2() -{ - if (TimerStat2 == TIMER_BUSY || TimerStat2 == TIMER_OFF) - return; - - TimerStat2 = TIMER_BUSY; - -// show_grid(); - - /* - - if (mod_icon_status = MOD_WAIT) then inc(icon_timer); - if icon_timer = 10 then mod_icon_status = MOD_IDLE; - if (mod_icon_status<>MOD_WAIT) and (mod_icon_status<>last_mod_icon_status) then - begin - icon_timer = 0; - case mod_icon_status of - MOD_IDLE form1.CoolTrayIcon1.IconIndex = 0; - MOD_RX begin form1.CoolTrayIcon1.IconIndex = 1; mod_icon_status = MOD_WAIT; end; - MOD_TX form1.CoolTrayIcon1.IconIndex = 2; - end; - last_mod_icon_status = mod_icon_status; - end; - //*/ - - TimerStat2 = TIMER_FREE; -} - -/* - -procedure TimeProc1(uTimerId, uMesssage UINT; dwUser, dw1, dw2 DWORD); stdcall; -begin - TimerEvent = TIMER_EVENT_ON; -end; - -procedure TForm1.create_timer1; -var - TimeEpk cardinal; -begin - TimeEpk = 100; - TimerId1 = TimeSetEvent(TimeEpk,0,@TimeProc1,0,TIME_PERIODIC); -end; - -procedure TForm1.free_timer1; -begin - TimerStat1 = TIMER_OFF; - timeKillEvent(TimerId1); -end; - -*/ - -/* - - -procedure TForm1.PaintBox1MouseMove(Sender TObject; Shift TShiftState; X, - Y Integer); -var - low,high word; -begin - if CheckBox1.Checked then exit; - if not mouse_down[1] then Exit; - rx_freq[1] = round(x*RX_SampleRate/FFTSize); - low = round(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1]+1); - high = round(rx_samplerate/2-(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1])); - if (rx_freq[1]-low)<0 then rx_freq[1] = low; - if (high-rx_freq[1])<0 then rx_freq[1] = high; - tx_freq[1] = rx_freq[1]; - show_freq_a; -end; - -procedure TForm1.PaintBox3MouseMove(Sender TObject; Shift TShiftState; X, - Y Integer); -var - low,high word; -begin - if CheckBox1.Checked then exit; - if not mouse_down[2] then Exit; - rx_freq[2] = round(x*RX_SampleRate/FFTSize); - low = round(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2]+1); - high = round(rx_samplerate/2-(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2])); - if (rx_freq[2]-low)<0 then rx_freq[2] = low; - if (high-rx_freq[2])<0 then rx_freq[2] = high; - tx_freq[2] = rx_freq[2]; - show_freq_b; -end; - -procedure TForm1.PaintBox1MouseUp(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); -begin - mouse_down[1] = FALSE; -end; - -procedure TForm1.PaintBox3MouseUp(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); -begin - mouse_down[2] = FALSE; -end; - -procedure TForm1.PaintBox1MouseDown(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); -var - low,high word; -begin - if CheckBox1.Checked then exit; - if not (ssLeft in shift) then Exit; - mouse_down[1] = TRUE; - rx_freq[1] = round(x*RX_SampleRate/FFTSize); - low = round(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1]+1); - high = round(rx_samplerate/2-(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1])); - if (rx_freq[1]-low)<0 then rx_freq[1] = low; - if (high-rx_freq[1])<0 then rx_freq[1] = high; - tx_freq[1] = rx_freq[1]; - show_freq_a; -end; - -procedure TForm1.PaintBox3MouseDown(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); -var - low,high word; -begin - if CheckBox1.Checked then exit; - if not (ssLeft in shift) then Exit; - mouse_down[2] = TRUE; - rx_freq[2] = round(x*RX_SampleRate/FFTSize); - low = round(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2]+1); - high = round(rx_samplerate/2-(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2])); - if (rx_freq[2]-low)<0 then rx_freq[2] = low; - if (high-rx_freq[2])<0 then rx_freq[2] = high; - tx_freq[2] = rx_freq[2]; - show_freq_b; -end; - -procedure TForm1.ServerSocket1ClientRead(Sender TObject; - Socket TCustomWinSocket); -var - s string; -begin - s = Socket.ReceiveText; - AGW_explode_frame(Socket.sockethandle,s); -end; - -procedure TForm1.ServerSocket1ClientDisconnect(Sender TObject; - Socket TCustomWinSocket); -begin - del_incoming_mycalls_by_sock(socket.SocketHandle); - AGW_del_socket(socket.SocketHandle); -end; - -procedure TForm1.ServerSocket1ClientError(Sender TObject; - Socket TCustomWinSocket; ErrorEvent TErrorEvent; - var ErrorCode Integer); -begin - del_incoming_mycalls_by_sock(socket.SocketHandle); - AGW_del_socket(socket.SocketHandle); - ErrorCode = 0; -end; - -procedure TForm1.ServerSocket1ClientConnect(Sender TObject; - Socket TCustomWinSocket); -begin - agw_add_socket(Socket.sockethandle); -end; - -procedure TForm1.OutputVolume1Click(Sender TObject); -var - s string; -begin - s = "SndVol32.exe -D"+inttostr(SND_TX_DEVICE); - WinExec(pchar(s),SW_SHOWNORMAL); -end; - -procedure TForm1.InputVolume1Click(Sender TObject); -var - s string; -begin - s = "SndVol32.exe -R -D"+inttostr(SND_RX_DEVICE); - WinExec(pchar(s),SW_SHOWNORMAL); -end; - -procedure TForm1.CoolTrayIcon1Click(Sender TObject); -begin - CoolTrayIcon1.ShowMainForm; -end; - -procedure TForm1.CoolTrayIcon1Cycle(Sender TObject; NextIndex Integer); -begin - CoolTrayIcon1.IconIndex = 2; - CoolTrayIcon1.CycleIcons = FALSE; -end; - -procedure TForm1.ABout1Click(Sender TObject); -begin - Form2.ShowModal; -end; - -procedure TForm1.put_frame(snd_ch byte; frame,code string; tx_stat,excluded boolean); -var - s string; -begin - if RxRichedit1.Focused then Windows.SetFocus(0); - if code = "NON-AX25" then - s = inttostr(snd_ch)+" ["+FormatDateTime("hhmmss",now)+"R]" - else - s = inttostr(snd_ch)+""+frame_monitor(frame,code,tx_stat); - //RxRichedit1.Lines.BeginUpdate; - RxRichedit1.SelStart = length(RxRichedit1.text); - RxRichEdit1.SelLength = length(s); - case tx_stat of - TRUE RxRichEdit1.SelAttributes.Color = clMaroon; - FALSE RxRichEdit1.SelAttributes.Color = clBlack; - end; - if excluded then RxRichEdit1.SelAttributes.Color = clGreen; - RxRichedit1.SelText = s+#10; - if RxRichedit1.Lines.Count>nr_monitor_lines then - repeat - RxRichedit1.Lines.Delete(0); - until RxRichedit1.Lines.Count = nr_monitor_lines; - RxRichedit1.HideSelection = FALSE; - RxRichedit1.SelStart = length(RxRichedit1.text); - RxRichedit1.SelLength = 0; - RxRichedit1.HideSelection = TRUE; - //RxRichedit1.Lines.EndUpdate; -end; - -procedure TForm1.show_mode_panels; -begin - panel8.Align = alNone; - panel6.Align = alNone; - if dualchan then panel6.Visible = TRUE else panel6.Visible = FALSE; - panel8.Align = alLeft; - panel6.Align = alLeft; -end; - -procedure TForm1.show_panels; -var - i byte; -begin - panel1.Align = alNone; - panel2.Align = alNone; - panel3.Align = alNone; - panel4.Align = alNone; - panel5.Align = alNone; - for i = 1 to 5 do - case i of - 1 panel1.Visible = panels[i]; - 2 panel5.Visible = panels[i]; - 3 panel3.Visible = panels[i]; - 4 panel4.Visible = panels[i]; - 5 panel2.Visible = panels[i]; - end; - panel1.Align = alBottom; - panel5.Align = alBottom; - panel3.Align = alBottom; - panel2.Align = alTop; - panel4.Align = alClient; -end; - -procedure TForm1.Secondwaterfall1Click(Sender TObject); -begin - case Secondwaterfall1.Checked of - TRUE Secondwaterfall1.Checked = FALSE; - FALSE Secondwaterfall1.Checked = TRUE; - end; - panels[1] = Secondwaterfall1.Checked; - show_panels; -end; - - - -procedure TForm1.Firstwaterfall1Click(Sender TObject); -begin - case Firstwaterfall1.Checked of - TRUE Firstwaterfall1.Checked = FALSE; - FALSE Firstwaterfall1.Checked = TRUE; - end; - panels[2] = Firstwaterfall1.Checked; - show_panels; -end; - -procedure TForm1.Statustable1Click(Sender TObject); -begin - case Statustable1.Checked of - TRUE Statustable1.Checked = FALSE; - FALSE Statustable1.Checked = TRUE; - end; - panels[3] = Statustable1.Checked; - show_panels; -end; - -procedure TForm1.Monitor1Click(Sender TObject); -begin - case Monitor1.Checked of - TRUE Monitor1.Checked = FALSE; - FALSE Monitor1.Checked = TRUE; - end; - panels[4] = Monitor1.Checked; - show_panels; -end; - -procedure TForm1.Devices1Click(Sender TObject); -begin - if (ptt = "EXT") or (ptt = "CAT") then Form3.Button3.Enabled = TRUE else Form3.Button3.Enabled = FALSE; - Form3.GetDeviceInfo; - form3.ShowModal; -end; - -procedure TForm1.FormPaint(Sender TObject); -begin - RxRichedit1.HideSelection = FALSE; - RxRichedit1.SelStart = length(RxRichedit1.text); - RxRichedit1.SelLength = 0; - RxRichedit1.HideSelection = TRUE; -end; - -procedure TForm1.Filters1Click(Sender TObject); -begin - Form5.Show_modem_settings; -end; - -procedure TForm1.Clearmonitor1Click(Sender TObject); -begin - RxRichEdit1.Clear; - frame_count = 0; - single_frame_count = 0; -end; - -procedure TForm1.Copytext1Click(Sender TObject); -begin - RxRichEdit1.CopyToClipboard; -end; - -procedure TForm1.ApplicationEvents1Minimize(Sender TObject); -begin - if stop_wf then w_state = WIN_MINIMIZED; -end; - -procedure TForm1.ApplicationEvents1Restore(Sender TObject); -begin - w_state = WIN_MAXIMIZED; -end; - -procedure TForm1.ServerSocket2ClientConnect(Sender TObject; - Socket TCustomWinSocket); -begin - KISS_add_stream(socket.sockethandle); -end; - -procedure TForm1.ServerSocket2ClientDisconnect(Sender TObject; - Socket TCustomWinSocket); -begin - KISS_del_stream(socket.sockethandle); -end; - -procedure TForm1.ServerSocket2ClientError(Sender TObject; - Socket TCustomWinSocket; ErrorEvent TErrorEvent; - var ErrorCode Integer); -begin - KISS_del_stream(socket.sockethandle); - ErrorCode = 0; -end; - -procedure TForm1.ServerSocket2ClientRead(Sender TObject; - Socket TCustomWinSocket); -var - data string; -begin - data = socket.ReceiveText; - KISS_on_data_in(socket.sockethandle,data); -end; - -procedure TForm1.Font1Click(Sender TObject); -begin - FontDialog1.Font = RXRichEdit1.Font; - if FontDialog1.Execute then - begin - RXRichEdit1.SelStart = 0; - RXRichEdit1.SelLength = Length(RXRichEdit1.Text); - RXRichEdit1.SelAttributes.Size = FontDialog1.Font.Size; - RXRichEdit1.SelAttributes.Name = FontDialog1.Font.Name; - RXRichEdit1.Font.Size = FontDialog1.Font.Size; - RXRichEdit1.Font.Name = FontDialog1.Font.Name; - WriteIni; - end; -end; - -procedure TForm1.Calibration1Click(Sender TObject); -begin - Form6.ShowModal; -end; - -procedure TForm1.ComboBox1Change(Sender TObject); -begin - Speed[1] = get_idx_by_name(ComboBox1.Text); - init_speed(1); - windows.setfocus(0); -end; - -procedure TForm1.ComboBox1KeyDown(Sender TObject; var Key Word; - Shift TShiftState); -begin - key = 0; - windows.SetFocus(0); -end; - -procedure TForm1.ComboBox1KeyPress(Sender TObject; var Key Char); -begin - key = #0; - windows.SetFocus(0); -end; - -procedure TForm1.ComboBox2Change(Sender TObject); -begin - Speed[2] = get_idx_by_name(ComboBox2.Text); - init_speed(2); - windows.setfocus(0); -end; - -procedure TForm1.FormDestroy(Sender TObject); -var - snd_ch byte; -begin - stoprx; - for snd_ch = 1 to 2 do if snd_status[snd_ch] = SND_TX then stoptx(snd_ch); - if (debugmode and DEBUG_TIMER) = 0 then free_timer1; - TimerStat2 = TIMER_OFF; - PTTClose; - ax25_free; - agw_free; - kiss_free; - detector_free; - RS.Free; - waterfall_free; - WriteIni; -end; - -end. -*/ \ No newline at end of file diff --git a/sm_main.c b/sm_main.c index 0e08aad..a75ea5f 100644 --- a/sm_main.c +++ b/sm_main.c @@ -28,13 +28,17 @@ void make_core_TXBPF(UCHAR snd_ch, float freq, float width); void make_core_INTR(UCHAR snd_ch); void make_core_LPF(UCHAR snd_ch, short width); void wf_pointer(int snd_ch); +void dw9600ProcessSample(int snd_ch, short Sample); +void init_RUH48(int snd_ch); +void init_RUH96(int snd_ch); -char modes_name[modes_count][20] = +char modes_name[modes_count][21] = { "AFSK AX.25 300bd","AFSK AX.25 1200bd","AFSK AX.25 600bd","AFSK AX.25 2400bd", "BPSK AX.25 1200bd","BPSK AX.25 600bd","BPSK AX.25 300bd","BPSK AX.25 2400bd", "QPSK AX.25 4800bd","QPSK AX.25 3600bd","QPSK AX.25 2400bd","BPSK FEC 4x100bd", - "DW QPSK V26A 2400bd","DW 8PSK V27 4800bd","DW QPSK V26B 2400bd", "ARDOP Packet" + "QPSK V26A 2400bps","8PSK V27 4800bps","QPSK V26B 2400bps", "Not Available", + "QPSK V26A 600bps", "8PSK 900bps", "RUH 4800(DW)", "RUH 9600(DW)" }; typedef struct wavehdr_tag { @@ -64,198 +68,7 @@ BOOL Secondwaterfall = 1; int multiCore = FALSE; -/* -type - TComboBox = class(StdCtrls.TComboBox) - private - procedure CMMouseWheel(var msg TCMMouseWheel); message CM_MOUSEWHEEL; - end; - TData16 = array [0..4095] of smallint; - PData16 = ^TData16; - TWaveHeader = record - RIFF dword; - ChunkLen integer; - WAVE dword; - fmt dword; - FormatLen integer; - Format word; - Channels word; - Frequency integer; - BytesPS integer; - BlockAlign word; - BitsPS word; - data dword; - DataLen integer - end; - TForm1 = class(TForm) - Panel5 TPanel; - ServerSocket1 TServerSocket; - MainMenu1 TMainMenu; - Settings1 TMenuItem; - OutputVolume1 TMenuItem; - InputVolume1 TMenuItem; - CoolTrayIcon1 TCoolTrayIcon; - ImageList1 TImageList; - ABout1 TMenuItem; - Panel1 TPanel; - Panel2 TPanel; - View1 TMenuItem; - Firstwaterfall1 TMenuItem; - Secondwaterfall1 TMenuItem; - Panel3 TPanel; - StringGrid1 TStringGrid; - Devices1 TMenuItem; - Statustable1 TMenuItem; - Monitor1 TMenuItem; - Panel4 TPanel; - PaintBox2 TPaintBox; - Filters1 TMenuItem; - Clearmonitor1 TMenuItem; - RxRichEdit1 TRxRichEdit; - MemoPopupMenu1 TPopupMenu; - Copytext1 TMenuItem; - Label1 TLabel; - Label5 TLabel; - ApplicationEvents1 TApplicationEvents; - PaintBox1 TPaintBox; - PaintBox3 TPaintBox; - ServerSocket2 TServerSocket; - Font1 TMenuItem; - FontDialog1 TFontDialog; - N1 TMenuItem; - Calibration1 TMenuItem; - Panel9 TPanel; - Panel6 TPanel; - Label4 TLabel; - Shape2 TShape; - ComboBox2 TComboBox; - SpinEdit2 TSpinEdit; - Panel7 TPanel; - Label3 TLabel; - Shape1 TShape; - ComboBox1 TComboBox; - SpinEdit1 TSpinEdit; - Panel8 TPanel; - Label2 TLabel; - TrackBar1 TTrackBar; - CheckBox1 TCheckBox; - OpenDialog1 TOpenDialog; - procedure FormCreate(Sender TObject); - procedure TrackBar1Change(Sender TObject); - procedure PaintBox1MouseMove(Sender TObject; Shift TShiftState; X, - Y Integer); - procedure PaintBox1MouseDown(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); - procedure ServerSocket1ClientRead(Sender TObject; - Socket TCustomWinSocket); - procedure ServerSocket1ClientError(Sender TObject; - Socket TCustomWinSocket; ErrorEvent TErrorEvent; - var ErrorCode Integer); - procedure OutputVolume1Click(Sender TObject); - procedure InputVolume1Click(Sender TObject); - procedure ServerSocket1ClientConnect(Sender TObject; - Socket TCustomWinSocket); - procedure ServerSocket1ClientDisconnect(Sender TObject; - Socket TCustomWinSocket); - procedure CoolTrayIcon1Click(Sender TObject); - procedure CoolTrayIcon1Cycle(Sender TObject; NextIndex Integer); - procedure ABout1Click(Sender TObject); - procedure PaintBox3MouseDown(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); - procedure PaintBox3MouseMove(Sender TObject; Shift TShiftState; X, - Y Integer); - procedure Firstwaterfall1Click(Sender TObject); - procedure Secondwaterfall1Click(Sender TObject); - procedure Devices1Click(Sender TObject); - procedure Statustable1Click(Sender TObject); - procedure Monitor1Click(Sender TObject); - procedure FormPaint(Sender TObject); - procedure Filters1Click(Sender TObject); - procedure SpinEdit1Change(Sender TObject); - procedure SpinEdit2Change(Sender TObject); - procedure Clearmonitor1Click(Sender TObject); - procedure Copytext1Click(Sender TObject); - procedure PaintBox3MouseUp(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); - procedure PaintBox1MouseUp(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); - procedure ApplicationEvents1Minimize(Sender TObject); - procedure ApplicationEvents1Restore(Sender TObject); - procedure ServerSocket2ClientConnect(Sender TObject; - Socket TCustomWinSocket); - procedure ServerSocket2ClientDisconnect(Sender TObject; - Socket TCustomWinSocket); - procedure ServerSocket2ClientError(Sender TObject; - Socket TCustomWinSocket; ErrorEvent TErrorEvent; - var ErrorCode Integer); - procedure ServerSocket2ClientRead(Sender TObject; - Socket TCustomWinSocket); - procedure Font1Click(Sender TObject); - procedure Calibration1Click(Sender TObject); - procedure ComboBox1Change(Sender TObject); - procedure ComboBox1KeyDown(Sender TObject; var Key Word; - Shift TShiftState); - procedure ComboBox1KeyPress(Sender TObject; var Key Char); - procedure ComboBox2Change(Sender TObject); - procedure FormDestroy(Sender TObject); - private - { Private declarations } - procedure BufferFull(var Msg TMessage); Message MM_WIM_DATA; - procedure BufferFull1(var Msg TMessage); Message MM_WOM_DONE; - procedure make_wave_buf(snd_ch byte; buf PChar); - procedure disp2(snd_ch byte); - procedure create_timer1; - procedure free_timer1; - procedure show_panels; - procedure show_combobox; - procedure Timer_Event2; - procedure waterfall_init; - procedure waterfall_free; - public - { Public declarations } - function get_idx_by_name(name string) word; - function frame_monitor(s,code string; tx_stat boolean) string; - procedure ChangePriority; - procedure put_frame(snd_ch byte; frame,code string; tx_stat,excluded boolean); - procedure show_grid; - procedure RX2TX(snd_ch byte); - procedure TX2RX(snd_ch byte); - procedure WriteIni; - procedure ReadIni; - procedure init_8P4800(snd_ch byte); - procedure init_DW2400(snd_ch byte); - procedure init_AE2400(snd_ch byte); - procedure init_MP400(snd_ch byte); - procedure init_Q4800(snd_ch byte); - procedure init_Q3600(snd_ch byte); - procedure init_Q2400(snd_ch byte); - procedure init_P2400(snd_ch byte); - procedure init_P1200(snd_ch byte); - procedure init_P600(snd_ch byte); - procedure init_P300(snd_ch byte); - procedure init_300(snd_ch byte); - procedure init_600(snd_ch byte); - procedure init_1200(snd_ch byte); - procedure init_2400(snd_ch byte); - procedure init_speed(snd_ch byte); - procedure get_filter_values(idx byte; var dbpf,dtxbpf,dbpftap,dlpf,dlpftap word); - procedure show_mode_panels; - procedure show_modes; - procedure show_freq_a; - procedure show_freq_b; - procedure ChkSndDevName; - procedure StartRx; - procedure StartTx(snd_ch byte); - procedure StopRx; - procedure StopTx(snd_ch byte); - procedure StartAGW; - procedure StartKISS; - procedure wf_scale; - procedure wf_pointer(snd_ch byte); - end; -var -/*/ BOOL MinOnStart = 0; //RS TReedSolomon; @@ -286,7 +99,7 @@ UCHAR tx_buf_num [5] = {0,0,0,0}; extern short active_rx_freq[5]; - +unsigned int pskStates[4] = {0, 0, 0, 0}; int speed[5] = {0,0,0,0}; int panels[6] = {1,1,1,1,1}; @@ -341,50 +154,7 @@ int MainPriority = 0; // MainThreadHandle THandle; UCHAR w_state = WIN_MAXIMIZED; - /* -implementation - -{$R *.DFM} - -uses ax25_mod, ax25_demod, ax25, ax25_l2, ax25_ptt, ax25_agw, ax25_about, rgb_rad, - AX25_set, ax25_filter, AX25_modem_set, kiss_mode, ax25_calibration; - -procedure TComboBox.CMMouseWheel(var msg TCMMouseWheel); -begin - if SendMessage(GetFocus, CB_GETDROPPEDSTATE, 0, 0) = 0 then msg.Result = 1; -end; - -procedure TForm1.ChangePriority; -begin - case MainPriority of - 0 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_NORMAL); - 1 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_ABOVE_NORMAL); - 2 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_HIGHEST); - 3 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_TIME_CRITICAL); - end; -end; - -procedure TForm1.show_modes; -var - s string; -begin - s = MODEM_CAPTION+" - Ver "+MODEM_VERSION+" - ["+modes_name[Speed[1]]; - if dualchan then s = s+" - "+modes_name[Speed[2]]; - form1.Caption = s+"]"; -end; - -procedure TForm1.show_freq_a; -begin - SpinEdit1.Value = round(rx_freq[1]); - SpinEdit1.Refresh; -end; - -procedure TForm1.show_freq_b; -begin - SpinEdit2.Value = round(rx_freq[2]); - SpinEdit2.Refresh; -end; -*/ + void get_filter_values(UCHAR snd_ch) { //, unsigned short dbpf, @@ -446,7 +216,7 @@ void get_filter_values(UCHAR snd_ch) break; case SPEED_DW2400: - case SPEED_AE2400: + case SPEED_2400V26B: lpf[snd_ch] = MODEM_DW2400_LPF; @@ -529,8 +299,31 @@ void get_filter_values(UCHAR snd_ch) BPF_tap[snd_ch] = MODEM_2400_BPF_TAP; LPF_tap[snd_ch] = MODEM_2400_LPF_TAP; break; - } + + case SPEED_Q300: + case SPEED_8PSK300: + + lpf[snd_ch] = MODEM_P300_LPF; + bpf[snd_ch] = MODEM_P300_BPF; + txbpf[snd_ch] = MODEM_P300_TXBPF; + BPF_tap[snd_ch] = MODEM_P300_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P300_LPF_TAP; + + break; + +/* + + case SPEED_Q1200: + + lpf[snd_ch] = MODEM_P1200_LPF; + bpf[snd_ch] = MODEM_P1200_BPF; + txbpf[snd_ch] = MODEM_P1200_TXBPF; + BPF_tap[snd_ch] = MODEM_P1200_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P1200_LPF_TAP; + break; +*/ + } } @@ -539,6 +332,7 @@ void init_2400(int snd_ch) modem_mode[snd_ch] = MODE_FSK; rx_shift[snd_ch] = 1805; rx_baudrate[snd_ch] = 2400; + tx_bitrate[snd_ch] = 2400; if (modem_def[snd_ch]) get_filter_values(snd_ch); @@ -553,6 +347,7 @@ void init_1200(int snd_ch) rx_freq[snd_ch] = 1700; rx_baudrate[snd_ch] = 1200; + tx_bitrate[snd_ch] = 1200; if (modem_def[snd_ch]) get_filter_values(snd_ch); @@ -564,6 +359,7 @@ void init_600(int snd_ch) rx_shift[snd_ch] = 450; rx_baudrate[snd_ch] = 600; + tx_bitrate[snd_ch] = 600; if (modem_def[snd_ch]) get_filter_values(snd_ch); @@ -574,6 +370,7 @@ void init_300(int snd_ch) modem_mode[snd_ch] = MODE_FSK; rx_shift[snd_ch] = 200; rx_baudrate[snd_ch] = 300; + tx_bitrate[snd_ch] = 300; if (modem_def[snd_ch]) get_filter_values(snd_ch); @@ -584,6 +381,7 @@ void init_MP400(int snd_ch) modem_mode[snd_ch] = MODE_MPSK; rx_shift[snd_ch] = 175 /*sbc*/ * 3; rx_baudrate[snd_ch] = 100; + tx_bitrate[snd_ch] = 400; if (modem_def[snd_ch]) get_filter_values(snd_ch); @@ -598,12 +396,14 @@ void init_8P4800(int snd_ch) rx_shift[snd_ch] = 1600; rx_baudrate[snd_ch] = 1600; + tx_bitrate[snd_ch] = 4800; + pskStates[snd_ch] = 8; if (modem_def[snd_ch]) get_filter_values(snd_ch); } -void init_AE2400(int snd_ch) +void init_V26B2400(int snd_ch) { qpsk_set[snd_ch].mode = QPSK_V26; modem_mode[snd_ch] = MODE_PI4QPSK; @@ -613,6 +413,8 @@ void init_AE2400(int snd_ch) rx_shift[snd_ch] = 1200; rx_baudrate[snd_ch] = 1200; + tx_bitrate[snd_ch] = 2400; + pskStates[snd_ch] = 8; // Pretend 8 so quality calc works if (modem_def[snd_ch]) get_filter_values(snd_ch); @@ -628,6 +430,8 @@ void init_DW2400(int snd_ch) rx_shift[snd_ch] = 1200; rx_baudrate[snd_ch] = 1200; + tx_bitrate[snd_ch] = 2400; + pskStates[snd_ch] = 4; if (modem_def[snd_ch]) get_filter_values(snd_ch); @@ -639,6 +443,9 @@ void init_Q4800(int snd_ch) modem_mode[snd_ch] = MODE_QPSK; rx_shift[snd_ch] = 2400; rx_baudrate[snd_ch] = 2400; + tx_bitrate[snd_ch] = 4800; + pskStates[snd_ch] = 4; + if (modem_def[snd_ch]) get_filter_values(snd_ch); } @@ -649,6 +456,7 @@ void init_Q3600(int snd_ch) modem_mode[snd_ch] = MODE_QPSK; rx_shift[snd_ch] = 1800; rx_baudrate[snd_ch] = 1800; + tx_bitrate[snd_ch] = 3600; if (modem_def[snd_ch]) get_filter_values(snd_ch); } @@ -659,6 +467,9 @@ void init_Q2400(int snd_ch) modem_mode[snd_ch] = MODE_QPSK; rx_shift[snd_ch] = 1200; rx_baudrate[snd_ch] = 1200; + tx_bitrate[snd_ch] = 2400; + pskStates[snd_ch] = 4; + if (modem_def[snd_ch]) get_filter_values(snd_ch); } @@ -668,7 +479,10 @@ void init_P2400(int snd_ch) modem_mode[snd_ch] = MODE_BPSK; rx_shift[snd_ch] = 2400; rx_baudrate[snd_ch] = 2400; - if (modem_def[snd_ch]) + tx_bitrate[snd_ch] = 2400; + pskStates[snd_ch] = 2; + + if (modem_def[snd_ch]) get_filter_values(snd_ch); } @@ -677,6 +491,9 @@ void init_P1200(int snd_ch) modem_mode[snd_ch] = MODE_BPSK; rx_shift[snd_ch] = 1200; rx_baudrate[snd_ch] = 1200; + tx_bitrate[snd_ch] = 1200; + pskStates[snd_ch] = 2; + if (modem_def[snd_ch]) get_filter_values(snd_ch); } @@ -686,6 +503,9 @@ void init_P600(int snd_ch) modem_mode[snd_ch] = MODE_BPSK; rx_shift[snd_ch] = 600; rx_baudrate[snd_ch] = 600; + tx_bitrate[snd_ch] = 600; + pskStates[snd_ch] = 2; + if (modem_def[snd_ch]) get_filter_values(snd_ch); } @@ -695,6 +515,36 @@ void init_P300(int snd_ch) modem_mode[snd_ch] = MODE_BPSK; rx_shift[snd_ch] = 300; rx_baudrate[snd_ch] = 300; + pskStates[snd_ch] = 2; + tx_bitrate[snd_ch] = 300; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_Q300(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_V26; + modem_mode[snd_ch] = MODE_QPSK; + + rx_shift[snd_ch] = 300; + rx_baudrate[snd_ch] = 300; + tx_bitrate[snd_ch] = 600; + pskStates[snd_ch] = 4; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_8PSK300(int snd_ch) +{ + modem_mode[snd_ch] = MODE_8PSK; + + rx_shift[snd_ch] = 300; + rx_baudrate[snd_ch] = 300; + tx_bitrate[snd_ch] = 900; + pskStates[snd_ch] = 8; + if (modem_def[snd_ch]) get_filter_values(snd_ch); } @@ -705,6 +555,7 @@ void init_ARDOP(int snd_ch) rx_shift[snd_ch] = 500; rx_freq[snd_ch] = 1500; rx_baudrate[snd_ch] = 500; + tx_bitrate[snd_ch] = 500; if (modem_def[snd_ch]) get_filter_values(snd_ch); @@ -721,36 +572,23 @@ void set_speed(int snd_ch, int Modem) } +int needPSKRefresh = 0; + void init_speed(int snd_ch) { int low, high; - /* + pskStates[snd_ch] = 0; // Not PSK - if (BPF[snd_ch]>round(rx_samplerate/2) then BPF[snd_ch] = round(rx_samplerate/2); - if TXBPF[snd_ch]>round(rx_samplerate/2) then TXBPF[snd_ch] = round(rx_samplerate/2); - if LPF[snd_ch]>round(rx_samplerate/2) then LPF[snd_ch] = round(rx_samplerate/2); - if BPF[snd_ch]<1 then BPF[snd_ch] = 1; - if TXBPF[snd_ch]<1 then TXBPF[snd_ch] = 1; - if LPF[snd_ch]<1 then LPF[snd_ch] = 1; - if TXDelay[snd_ch]<1 then TXDelay[snd_ch] = 1; - if TXTail[snd_ch]<1 then TXTail[snd_ch] = 1; - if BPF_tap[snd_ch]>1024 then BPF_tap[snd_ch] = 1024; - if LPF_tap[snd_ch]>512 then LPF_tap[snd_ch] = 512; - if BPF_tap[snd_ch]<8 then BPF_tap[snd_ch] = 8; - if LPF_tap[snd_ch]<8 then LPF_tap[snd_ch] = 8; - if not (RCVR[snd_ch] in [0..8]) then RCVR[snd_ch] = 0; - if not (rcvr_offset[snd_ch] in [0..100]) then rcvr_offset[snd_ch] = 30; - if not (speed[snd_ch] in [0..modes_count]) then speed[snd_ch] = SPEED_300; -*/ switch (speed[snd_ch]) { case SPEED_8P4800: init_8P4800(snd_ch); break; - case SPEED_AE2400: - init_AE2400(snd_ch); + case SPEED_2400V26B: + init_V26B2400(snd_ch); + break; case SPEED_DW2400: @@ -780,6 +618,10 @@ void init_speed(int snd_ch) init_P1200(snd_ch); break; +// case SPEED_Q1200: +// init_Q1200(snd_ch); +// break; + case SPEED_P600: init_P600(snd_ch); break; @@ -788,6 +630,14 @@ void init_speed(int snd_ch) init_P300(snd_ch); break; + case SPEED_Q300: + init_Q300(snd_ch); + break; + + case SPEED_8PSK300: + init_8PSK300(snd_ch); + break; + case SPEED_300: init_300(snd_ch); @@ -812,12 +662,22 @@ void init_speed(int snd_ch) init_ARDOP(snd_ch); break; + + case SPEED_RUH48: + + init_RUH48(snd_ch); + break; + + case SPEED_RUH96: + + init_RUH96(snd_ch); + break; + } //QPSK_SM: begin move(#0#1#2#3, tx[0], 4); move(#0#32#64#96, rx[0], 4); end; //QPSK_V26: begin move(#2#3#1#0, tx[0], 4); move(#96#64#0#32, rx[0], 4); end; - if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK) { switch (qpsk_set[snd_ch].mode) @@ -870,752 +730,9 @@ void init_speed(int snd_ch) */ wf_pointer(soundChannel[snd_ch]); - + CheckPSKWindows(); } -/* -procedure TForm1.show_combobox; -var - i word; -begin - for i = 0 to length(modes_name)-1 do - begin - ComboBox1.Items.Add(modes_name[i]); - ComboBox2.Items.Add(modes_name[i]); - end; - ComboBox1.ItemIndex = ComboBox1.Items.IndexOf(modes_name[Speed[1]]); - ComboBox2.ItemIndex = ComboBox2.Items.IndexOf(modes_name[Speed[2]]); -end; - -function TForm1.get_idx_by_name(name string) word; -var - i word; - found boolean; -begin - i = 0; - found = FALSE; - result = 0; - repeat - if name = modes_name[i] then - begin - found = TRUE; - result = i; - end - else inc(i); - until found or (i = length(modes_name)); -end; - -procedure TForm1.ReadIni; -var - snd_ch byte; -begin - TelIni = TIniFile.Create(cur_dir+"soundmodem.ini"); - with TelIni do - begin - UTC_Time = ReadBool("Init","UTCTime",FALSE); - MainPriority = ReadInteger("Init","Priority",2); - nr_monitor_lines = ReadInteger("Init","NRMonitorLines",500); - ptt = ReadString("Init","PTT","NONE"); - stop_wf = ReadBool("Init","StopWF",FALSE); - raduga = ReadBool("Init","DispMode",DISP_MONO); - stat_log = ReadBool("Init","StatLog",FALSE); - SND_RX_DEVICE = ReadInteger("Init","SndRXDevice",0); - SND_TX_DEVICE = ReadInteger("Init","SndTXDevice",0); - snd_rx_device_name = ReadString("Init","SndRXDeviceName",""); - snd_tx_device_name = ReadString("Init","SndTXDeviceName",""); - RX_SR = ReadInteger("Init","RXSampleRate",11025); - TX_SR = ReadInteger("Init","TXSampleRate",11025); - RX_PPM = ReadInteger("Init","RX_corr_PPM",0); - TX_PPM = ReadInteger("Init","TX_corr_PPM",0); - tx_bufcount = ReadInteger("Init","TXBufNumber",32); - rx_bufcount = ReadInteger("Init","RXBufNumber",32); - DebugMode = ReadInteger("Init","DisableUnit",0); - TX_rotate = ReadBool("Init","TXRotate",FALSE); - DualChan = ReadBool("Init","DualChan",FALSE); - DualPTT = ReadBool("Init","DualPTT",TRUE); - SCO = ReadBool("Init","SCO",FALSE); - stdtones = ReadBool("Init","UseStandardTones",TRUE); - // Channel A settings - maxframe[1] = ReadInteger("AX25_A","Maxframe",3); - fracks[1] = ReadInteger("AX25_A","Retries",15); - frack_time[1] = ReadInteger("AX25_A","FrackTime",5); - idletime[1] = ReadInteger("AX25_A","IdleTime",180); - slottime[1] = ReadInteger("AX25_A","SlotTime",100); - persist[1] = ReadInteger("AX25_A","Persist",128); - resptime[1] = ReadInteger("AX25_A","RespTime",1500); - TXFrmMode[1] = ReadInteger("AX25_A","TXFrmMode",1); - max_frame_collector[1] = ReadInteger("AX25_A","FrameCollector",6); - exclude_callsigns[1] = ReadString("AX25_A","ExcludeCallsigns",""); - exclude_APRS_frm[1] = ReadString("AX25_A","ExcludeAPRSFrmType",""); - KISS_opt[1] = ReadBool("AX25_A","KISSOptimization",FALSE); - dyn_frack[1] = ReadBool("AX25_A","DynamicFrack",FALSE); - recovery[1] = ReadInteger("AX25_A","BitRecovery",0); - NonAX25[1] = ReadBool("AX25_A","NonAX25Frm",FALSE); - MEMRecovery[1] = ReadInteger("AX25_A","MEMRecovery",200); - IPOLL[1] = ReadInteger("AX25_A","IPOLL",80); - MyDigiCall[1] = ReadString("AX25_A","MyDigiCall",""); - tx_hitoneraisedb[1] = ReadInteger("AX25_A","HiToneRaise",0); - // Channel B settings - maxframe[2] = ReadInteger("AX25_B","Maxframe",3); - fracks[2] = ReadInteger("AX25_B","Retries",15); - frack_time[2] = ReadInteger("AX25_B","FrackTime",5); - idletime[2] = ReadInteger("AX25_B","IdleTime",180); - slottime[2] = ReadInteger("AX25_B","SlotTime",100); - persist[2] = ReadInteger("AX25_B","Persist",128); - resptime[2] = ReadInteger("AX25_B","RespTime",1500); - TXFrmMode[2] = ReadInteger("AX25_B","TXFrmMode",1); - max_frame_collector[2] = ReadInteger("AX25_B","FrameCollector",6); - exclude_callsigns[2] = ReadString("AX25_B","ExcludeCallsigns",""); - exclude_APRS_frm[2] = ReadString("AX25_B","ExcludeAPRSFrmType",""); - KISS_opt[2] = ReadBool("AX25_B","KISSOptimization",FALSE); - dyn_frack[2] = ReadBool("AX25_B","DynamicFrack",FALSE); - recovery[2] = ReadInteger("AX25_B","BitRecovery",0); - NonAX25[2] = ReadBool("AX25_B","NonAX25Frm",FALSE); - MEMRecovery[2] = ReadInteger("AX25_B","MEMRecovery",200); - IPOLL[2] = ReadInteger("AX25_B","IPOLL",80); - MyDigiCall[2] = ReadString("AX25_B","MyDigiCall",""); - tx_hitoneraisedb[2] = ReadInteger("AX25_B","HiToneRaise",0); - // Modem settings - pkt_raw_min_len = ReadInteger("Modem","RawPktMinLen",17); - swap_ptt = ReadBool("Modem","SwapPTTPins",FALSE); - inv_ptt = ReadBool("Modem","InvPTTPins",FALSE); - Emph_all[1] = ReadBool("Modem","PreEmphasisAll1",TRUE); - Emph_all[2] = ReadBool("Modem","PreEmphasisAll2",TRUE); - emph_db[1] = ReadInteger("Modem","PreEmphasisDB1",0); - emph_db[2] = ReadInteger("Modem","PreEmphasisDB2",0); - txbpf[1] = ReadInteger("Modem","TXBPF1",500); - txbpf[2] = ReadInteger("Modem","TXBPF2",500); - bpf[1] = ReadInteger("Modem","BPF1",500); - bpf[2] = ReadInteger("Modem","BPF2",500); - lpf[1] = ReadInteger("Modem","LPF1",150); - lpf[2] = ReadInteger("Modem","LPF2",150); - BPF_tap[1] = ReadInteger("Modem","BPFTap1",256); - BPF_tap[2] = ReadInteger("Modem","BPFTap2",256); - LPF_tap[1] = ReadInteger("Modem","LPFTap1",128); - LPF_tap[2] = ReadInteger("Modem","LPFTap2",128); - DCD_threshold = ReadInteger("Modem","DCDThreshold",32); - rx_freq[1] = ReadFloat("Modem","RXFreq1",1700); - rx_freq[2] = ReadFloat("Modem","RXFreq2",1700); - CheckBox1.Checked = ReadBool("Modem","HoldPnt",FALSE); - BIT_AFC = ReadInteger("Modem","AFC",32); - txdelay[1] = ReadInteger("Modem","TxDelay1",250); - txdelay[2] = ReadInteger("Modem","TxDelay2",250); - txtail[1] = ReadInteger("Modem","TxTail1",50); - txtail[2] = ReadInteger("Modem","TxTail2",50); - diddles = ReadInteger("Modem","Diddles",0); - RCVR[1] = ReadInteger("Modem","NRRcvrPairs1",0); - RCVR[2] = ReadInteger("Modem","NRRcvrPairs2",0); - rcvr_offset[1] = ReadInteger("Modem","RcvrShift1",30); - rcvr_offset[2] = ReadInteger("Modem","RcvrShift2",30); - speed[1] = ReadInteger("Modem","ModemType1",SPEED_1200); - speed[2] = ReadInteger("Modem","ModemType2",SPEED_1200); - modem_def[1] = ReadBool("Modem","Default1",TRUE); - modem_def[2] = ReadBool("Modem","Default2",TRUE); - AGWServ = ReadBool("AGWHost","Server",TRUE); - AGWPort = ReadInteger("AGWHost","Port",8000); - KISSServ = ReadBool("KISS","Server",FALSE); - KISSPort = ReadInteger("KISS","Port",8100); - Form1.Top = ReadInteger("Window","Top",0); - Form1.Left = ReadInteger("Window","Left",0); - Form1.Height = ReadInteger("Window","Height",656); - Form1.Width = ReadInteger("Window","Width",764); - MinOnStart = ReadBool("Window","MinimizedOnStartup",FALSE); - Firstwaterfall1.checked = ReadBool("Window","Waterfall1",TRUE); - Secondwaterfall1.checked = ReadBool("Window","Waterfall2",FALSE); - Statustable1.checked = ReadBool("Window","StatTable",TRUE); - Monitor1.checked = ReadBool("Window","Monitor",TRUE); - RXRichEdit1.Font.Size = ReadInteger("Font","Size",RXRichEdit1.Font.Size); - RXRichEdit1.Font.Name = ReadString("Font","Name",RXRichEdit1.Font.Name); - end; - TelIni.Free; - newAGWPort = AGWPort; - newAGWServ = AGWServ; - newKISSPort = KISSPort; - newKISSServ = KISSServ; - - RX_SampleRate = RX_SR+RX_SR*0.000001*RX_PPM; - TX_SampleRate = TX_SR+TX_SR*0.000001*TX_PPM; - - panels[4] = Monitor1.Checked; - panels[3] = Statustable1.Checked; - panels[2] = Firstwaterfall1.Checked; - panels[1] = Secondwaterfall1.Checked; - - if tx_bufcount>255 then tx_bufcount = 255; - if tx_bufcount<2 then tx_bufcount = 2; - if rx_bufcount>255 then rx_bufcount = 255; - if rx_bufcount<2 then rx_bufcount = 2; - - if not (diddles in [0..2]) then diddles = 0; - - if nr_monitor_lines>65535 then nr_monitor_lines = 65535; - if nr_monitor_lines<10 then nr_monitor_lines = 10; - - if not (MainPriority in [0..3]) then MainPriority = 2; - - for snd_ch = 1 to 2 do - begin - - tx_hitoneraise[snd_ch] = power(10,-abs(tx_hitoneraisedb[snd_ch])/20); - - if IPOLL[snd_ch]<0 then IPOLL[snd_ch] = 0; - if IPOLL[snd_ch]>65535 then IPOLL[snd_ch] = 65535; - - if MEMRecovery[snd_ch]<1 then MEMRecovery[snd_ch] = 1; - if MEMRecovery[snd_ch]>65535 then MEMRecovery[snd_ch] = 65535; - - get_exclude_list(AnsiUpperCase(MyDigiCall[snd_ch]),list_digi_callsigns[snd_ch]); - get_exclude_list(AnsiUpperCase(exclude_callsigns[snd_ch]),list_exclude_callsigns[snd_ch]); - get_exclude_frm(exclude_APRS_frm[snd_ch],list_exclude_APRS_frm[snd_ch]); - - if resptime[snd_ch]<0 then resptime[snd_ch] = 0; - if resptime[snd_ch]>65535 then resptime[snd_ch] = 65535; - if persist[snd_ch]>255 then persist[snd_ch] = 255; - if persist[snd_ch]<32 then persist[snd_ch] = 32; - if fracks[snd_ch]<1 then fracks[snd_ch] = 1; - if frack_time[snd_ch]<1 then frack_time[snd_ch] = 1; - if idletime[snd_ch]0 then - for k = 0 to numdevs-1 do - begin - waveOutGetDevCaps(k,@DevOutCaps,sizeof(DevOutCaps)); - TXDevList.Add(DevOutCaps.szpname); - end; - numdevs = WaveInGetNumDevs; - if numdevs>0 then - for k = 0 to numdevs-1 do - begin - waveInGetDevCaps(k,@DevInCaps,sizeof(DevInCaps)); - RXDevList.Add(DevInCaps.szpname); - end; - // TX Dev - if (snd_tx_device<0) or (snd_tx_device> = TXDevList.Count) then snd_tx_device = 0; - if TXDevList.Count>0 then - if TXDevList.Strings[snd_tx_device]<>snd_tx_device_name then - begin - i = TXDevList.IndexOf(snd_tx_device_name); - if i> = 0 then snd_tx_device = i else snd_tx_device_name = TXDevList.Strings[snd_tx_device]; - end; - // RX Dev - if (snd_rx_device<0) or (snd_rx_device> = RXDevList.Count) then snd_rx_device = 0; - if RXDevList.Count>0 then - if RXDevList.Strings[snd_rx_device]<>snd_rx_device_name then - begin - i = RXDevList.IndexOf(snd_rx_device_name); - if i> = 0 then snd_rx_device = i else snd_rx_device_name = RXDevList.Strings[snd_rx_device]; - end; - RXDevList.Free; - TXDevList.Free; -end; - -procedure TForm1.startrx; -var - OpenResult MMRESULT; - Loop integer; - ErrorText string; -Begin - RX_device = TRUE; - RXBufferLength = rx_bufsize * Channels * (BitsPerSample div 8); - with WaveFormat do - begin - wFormatTag = WAVE_FORMAT_PCM; - nChannels = Channels; - nSamplesPerSec = RX_SR; - nAvgBytesPerSec = RX_SR * Channels * (BitsPerSample div 8); - nBlockAlign = Channels * (BitsPerSample div 8); - wBitsPerSample = BitsPerSample; - cbSize = 0; - end; - OpenResult = waveInOpen (@WaveInHandle,SND_RX_DEVICE,@WaveFormat,integer(Self.Handle),0,CALLBACK_WINDOW); - if OpenResult = MMSYSERR_NOERROR then - begin - for Loop = 1 to rx_bufcount do - begin - GetMem(RX_pbuf[Loop], RXBufferLength); - RX_header[Loop].lpData = RX_pbuf[Loop]; - RX_header[Loop].dwBufferLength = RXBufferLength; - RX_header[Loop].dwUser = Loop; - RX_header[Loop].dwFlags = 0; - RX_header[Loop].dwLoops = 0; - OpenResult = WaveInPrepareHeader(WaveInhandle, @RX_header[Loop], sizeof(TWaveHdr)); - if OpenResult = MMSYSERR_NOERROR then WaveInAddBuffer(WaveInHandle, @RX_header[Loop], sizeof(TWaveHdr)) - else - begin - case OpenResult of - MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; - MMSYSERR_NODRIVER ErrorText = "no device driver present"; - MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; - else ErrorText = "unknown error"; - end; - MessageDlg(format("Error adding buffer %d device (%s)",[Loop, ErrorText]), mtError, [mbOk], 0); - end; - end; - WaveInStart(WaveInHandle); - end - else - begin - case OpenResult of - MMSYSERR_ERROR ErrorText = "unspecified error"; - MMSYSERR_BADDEVICEID ErrorText = "device ID out of range"; - MMSYSERR_NOTENABLED ErrorText = "driver failed enable"; - MMSYSERR_ALLOCATED ErrorText = "device already allocated"; - MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; - MMSYSERR_NODRIVER ErrorText = "no device driver present"; - MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; - MMSYSERR_NOTSUPPORTED ErrorText = "function isn""t supported"; - MMSYSERR_BADERRNUM ErrorText = "error value out of range"; - MMSYSERR_INVALFLAG ErrorText = "invalid flag passed"; - MMSYSERR_INVALPARAM ErrorText = "invalid parameter passed"; - MMSYSERR_HANDLEBUSY ErrorText = "handle being used simultaneously on another thread (eg callback)"; - MMSYSERR_INVALIDALIAS ErrorText = "specified alias not found"; - MMSYSERR_BADDB ErrorText = "bad registry database"; - MMSYSERR_KEYNOTFOUND ErrorText = "registry key not found"; - MMSYSERR_READERROR ErrorText = "registry read error"; - MMSYSERR_WRITEERROR ErrorText = "registry write error"; - MMSYSERR_DELETEERROR ErrorText = "registry delete error"; - MMSYSERR_VALNOTFOUND ErrorText = "registry value not found"; - MMSYSERR_NODRIVERCB ErrorText = "driver does not call DriverCallback"; - else ErrorText = "unknown error"; - end; - MessageDlg(format("Error opening wave input device (%s)",[ErrorText]), mtError, [mbOk], 0); - RX_device = FALSE; - end; -end; - -procedure TForm1.stoprx; -var - Loop integer; -begin - if not RX_device then exit; - WaveInStop(WaveInHandle); - WaveInReset(WaveInHandle); - for Loop = 1 to rx_bufcount do - WaveInUnPrepareHeader(WaveInHandle, @RX_header[Loop], sizeof(TWaveHdr)); - WaveInClose(WaveInHandle); - for Loop = 1 to rx_bufcount do - begin - if RX_pbuf[Loop]<>nil then - begin - FreeMem(RX_pbuf[Loop]); - RX_pbuf[Loop] = nil; - end; - end; - RX_device = FALSE; -end; - -procedure TForm1.make_wave_buf(snd_ch byte; buf PChar); -const - amplitude = 22000; -var - i word; -begin - modulator(snd_ch,audio_buf[snd_ch],tx_bufsize); - if tx_status[snd_ch] = TX_NO_DATA then buf_status[snd_ch] = BUF_EMPTY; - for i = 0 to tx_bufsize-1 do - begin - case snd_ch of - 1 - begin - // left channel - PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]); - Inc(PSmallInt(Buf)); - // right channel - if SCO then PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]) else PSmallInt(buf)^ = 0; - Inc(PSmallInt(Buf)); - end; - 2 - begin - // left channel - if SCO then PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]) else PSmallInt(buf)^ = 0; - Inc(PSmallInt(Buf)); - // right channel - PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]); - Inc(PSmallInt(Buf)); - end; - end; - end; -end; - -procedure TForm1.starttx(snd_ch byte); -var - OpenResult MMRESULT; - Loop integer; - ErrorText string; - BufferLength longint; -Begin - if snd_status[snd_ch]<>SND_IDLE then exit; - BufferLength = tx_bufsize * Channels * (BitsPerSample div 8); - with WaveFormat do - begin - wFormatTag = WAVE_FORMAT_PCM; - nChannels = Channels; - nSamplesPerSec = TX_SR; - nAvgBytesPerSec = TX_SR * Channels * (BitsPerSample div 8); - nBlockAlign = Channels * (BitsPerSample div 8); - wBitsPerSample = BitsPerSample; - cbSize = 0; - end; - OpenResult = WaveOutOpen (@WaveOutHandle[snd_ch],SND_TX_DEVICE,@WaveFormat,integer(Self.Handle),0,CALLBACK_WINDOW); - if OpenResult = MMSYSERR_NOERROR then - begin - snd_status[snd_ch] = SND_TX; - buf_status[snd_ch] = BUF_FULL; - tx_status[snd_ch] = TX_SILENCE; - tx_buf_num[snd_ch] = 0; - tx_buf_num1[snd_ch] = 0; - for Loop = 1 to tx_bufcount do - begin - GetMem(TX_pbuf[snd_ch][Loop], BufferLength); - TX_header[snd_ch][Loop].lpData = TX_pbuf[snd_ch][Loop]; - TX_header[snd_ch][Loop].dwBufferLength = BufferLength; - TX_header[snd_ch][Loop].dwUser = 0; - TX_header[snd_ch][Loop].dwFlags = 0; - TX_header[snd_ch][Loop].dwLoops = 0; - OpenResult = WaveOutPrepareHeader(WaveOuthandle[snd_ch], @TX_header[snd_ch][Loop], sizeof(TWaveHdr)); - if OpenResult = MMSYSERR_NOERROR then - begin - // Заполнить буфер на передачу - if buf_status[snd_ch] = BUF_FULL then - begin - make_wave_buf(snd_ch,TX_pbuf[snd_ch][Loop]); - WaveOutWrite(WaveOutHandle[snd_ch],@TX_header[snd_ch][Loop],sizeof(TWaveHdr)); - inc(tx_buf_num1[snd_ch]); - end; - end - else - begin - case OpenResult of - MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; - MMSYSERR_NODRIVER ErrorText = "no device driver present"; - MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; - else ErrorText = "unknown error"; - end; - MessageDlg(format("Error adding buffer %d device (%s)",[Loop, ErrorText]), mtError, [mbOk], 0); - end; - end; - end - else - begin - case OpenResult of - MMSYSERR_ERROR ErrorText = "unspecified error"; - MMSYSERR_BADDEVICEID ErrorText = "device ID out of range"; - MMSYSERR_NOTENABLED ErrorText = "driver failed enable"; - MMSYSERR_ALLOCATED ErrorText = "device already allocated"; - MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; - MMSYSERR_NODRIVER ErrorText = "no device driver present"; - MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; - MMSYSERR_NOTSUPPORTED ErrorText = "function isn""t supported"; - MMSYSERR_BADERRNUM ErrorText = "error value out of range"; - MMSYSERR_INVALFLAG ErrorText = "invalid flag passed"; - MMSYSERR_INVALPARAM ErrorText = "invalid parameter passed"; - MMSYSERR_HANDLEBUSY ErrorText = "handle being used simultaneously on another thread (eg callback)"; - MMSYSERR_INVALIDALIAS ErrorText = "specified alias not found"; - MMSYSERR_BADDB ErrorText = "bad registry database"; - MMSYSERR_KEYNOTFOUND ErrorText = "registry key not found"; - MMSYSERR_READERROR ErrorText = "registry read error"; - MMSYSERR_WRITEERROR ErrorText = "registry write error"; - MMSYSERR_DELETEERROR ErrorText = "registry delete error"; - MMSYSERR_VALNOTFOUND ErrorText = "registry value not found"; - MMSYSERR_NODRIVERCB ErrorText = "driver does not call DriverCallback"; - else ErrorText = "unknown error"; - end; - MessageDlg(format("Error opening wave output device (%s)",[ErrorText]), mtError, [mbOk], 0); - end; -end; - -procedure TForm1.stoptx(snd_ch byte); -var - Loop integer; -begin - if snd_status[snd_ch]<>SND_TX then exit; - WaveOutReset(WaveOutHandle[snd_ch]); - for Loop = 1 to tx_bufcount do - WaveOutUnPrepareHeader(WaveOutHandle[snd_ch], @TX_header[snd_ch][Loop], sizeof(TWaveHdr)); - WaveOutClose(WaveOutHandle[snd_ch]); - for Loop = 1 to tx_bufcount do - begin - if TX_pbuf[snd_ch][Loop]<>nil then - begin - FreeMem(TX_pbuf[snd_ch][Loop]); - TX_pbuf[snd_ch][Loop] = nil; - end; - end; - WaveOutHandle[snd_ch] = 0; - snd_status[snd_ch] = SND_IDLE; -end; - -procedure show_grid_title; -const - title array [0..10] of string = ("MyCall","DestCall","Status","Sent pkts","Sent bytes","Rcvd pkts","Rcvd bytes","Rcvd FC","CPS TX","CPS RX","Direction"); -var - i byte; -begin - for i = 0 to 10 do Form1.StringGrid1.Cells[i,0] = title[i]; -end; -*/ -/* - -procedure disp1(src1,src2 array of single); -var - i,n word; - k,k1,amp1,amp2,amp3,amp4 single; - bm TBitMap; -begin - bm = TBitMap.Create; - bm.pixelformat = pf32bit; - //bm.pixelformat = pf24bit; - bm.Width = Form1.PaintBox2.Width; - bm.Height = Form1.PaintBox2.Height; - amp1 = 0; - amp3 = 0; - //k = 0.20; - k = 50000; - k1 = 0; - //k = 1000; - //k = 0.00001; - bm.Canvas.MoveTo(0,50); - bm.Canvas.LineTo(512,50); - n = 0; - for i = 0 to RX_Bufsize-1 do - begin - begin - amp2 = src1[i]; - amp4 = src2[i]; - bm.Canvas.Pen.Color = clRed; - bm.Canvas.MoveTo(n,50-round(amp1*k1)); - bm.Canvas.LineTo(n+1,50-round(amp2*k1)); - bm.Canvas.Pen.Color = clBlue; - bm.Canvas.MoveTo(n,50-round(amp3*k)); - bm.Canvas.LineTo(n+1,50-round(amp4*k)); - bm.Canvas.Pen.Color = clBlack; - inc(n); - amp1 = amp2; - amp3 = amp4; - end; - end; - Form1.PaintBox2.Canvas.Draw(0,0,bm); - bm.Free; -end; -*/ - -/* - -procedure TForm1.wf_pointer(snd_ch byte); -var - x single; - x1,x2,y,k,pos1,pos2,pos3 word; -begin - k = 24; - x = FFTSize/RX_SampleRate; - pos1 = round((rx_freq[snd_ch]-0.5*rx_shift[snd_ch])*x)-5; - pos2 = round((rx_freq[snd_ch]+0.5*rx_shift[snd_ch])*x)-5; - pos3 = round(rx_freq[snd_ch]*x); - x1 = pos1+5; - x2 = pos2+5; - y = k+5; - with bm3.Canvas do - begin - Draw(0,20,bm[snd_ch]); - Pen.Color = clWhite; - Brush.Color = clRed; - Polygon([Point(x1+3,y),Point(x1,y-7),Point(x1-3,y),Point(x2+3,y),Point(x2,y-7),Point(x2-3,y)]); - Brush.Color = clBlue; - Polygon([Point(x1+3,y),Point(x1,y+7),Point(x1-3,y),Point(x2+3,y),Point(x2,y+7),Point(x2-3,y)]); - Polyline([Point(pos3,k+1),Point(pos3,k+9)]); - Pen.Color = clBlack; - end; - case snd_ch of - 1 PaintBox1.Canvas.Draw(0,0,bm3); - 2 PaintBox3.Canvas.Draw(0,0,bm3); - end; -end; - -procedure TForm1.wf_Scale; -var - k single; - max_freq,x,i word; -begin - max_freq = round(RX_SampleRate*0.005); - k = 100*FFTSize/RX_SampleRate; - with bm1.Canvas do - begin - Brush.Color = clBlack; - FillRect(ClipRect); - Pen.Color = clWhite; - Font.Color = clWhite; - Font.Size = 8; - for i = 0 to max_freq do - begin - x = round(k*i); - if x<1025 then - begin - if (i mod 5) = 0 then - PolyLine([Point(x,20),Point(x,13)]) - else - PolyLine([Point(x,20),Point(x,16)]); - if (i mod 10) = 0 then TextOut(x-12,1,inttostr(i*100)); - end; - end; - Pen.Color = clBlack; - end; - bm3.Canvas.Draw(0,0,bm1); -end; -*/ void chk_snd_buf(float * buf, int len) { @@ -1693,8 +810,11 @@ void runModems() if (modem_mode[snd_ch] == MODE_ARDOP) continue; // Processed above - // do we need to do this again ?? -// make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); + if (modem_mode[snd_ch] == MODE_RUH) + continue; // Processed above + + // do we need to do this again ?? + // make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); if (multiCore) // Run modems in separate threads thread[snd_ch] = _beginthread(runModemthread, 0, (void *)(size_t)snd_ch); @@ -1745,7 +865,7 @@ void runModemthread(void * param) void BufferFull(short * Samples, int nSamples) // These are Stereo Samples { - word i, i1; + word i, i1, j; Byte snd_ch, rcvr_idx; int buf_offset; int Needed; @@ -1810,21 +930,72 @@ void BufferFull(short * Samples, int nSamples) // These are Stereo Samples short ardopbuff[1200]; i1 = 0; - for (i = 0; i < rx_bufsize; i++) + if (using48000) { - ardopbuff[i] = Samples[i1]; + i1 = 0; + j = 0; + + // Need to downsample 48K to 12K + // Try just skipping 3 samples + + nSamples /= 4; + + for (i = 0; i < nSamples; i++) + { + ardopbuff[i] = Samples[i1]; + i1 += 8; + } + } + else + { + for (i = 0; i < nSamples; i++) + { + ardopbuff[i] = Samples[i1]; + i1++; + i1++; + } + } + ARDOPProcessNewSamples(snd_ch, ardopbuff, nSamples); + } + + else if (modem_mode[snd_ch] == MODE_RUH) + { + i1 = 0; + if (modemtoSoundLR[snd_ch] == 1) // Using Right Chan + i1++; + + for (i = 0; i < nSamples; i++) + { + dw9600ProcessSample(snd_ch, Samples[i1]); i1++; i1++; } - - ARDOPProcessNewSamples(ardopbuff, nSamples); } + ProcessRXFrames(snd_ch); } // extract mono samples from data. data1 = Samples; + if (using48000) + { + i1 = 0; + j = 0; + + // Need to downsample 48K to 12K + // Try just skipping 3 samples + + nSamples /= 4; + + for (i = 0; i < nSamples; i++) + { + Samples[j++] = Samples[i1]; + Samples[j++] = Samples[i1 + 1]; + i1 += 8; + } + } + i1 = 0; // src_buf[0] is left data,. src_buf[1] right @@ -1833,7 +1004,7 @@ void BufferFull(short * Samples, int nSamples) // These are Stereo Samples if (UsingBothChannels) { - for (i = 0; i < rx_bufsize; i++) + for (i = 0; i < nSamples; i++) { src_buf[0][i] = data1[i1]; i1++; @@ -1847,7 +1018,7 @@ void BufferFull(short * Samples, int nSamples) // These are Stereo Samples i1 = 1; - for (i = 0; i < rx_bufsize; i++) + for (i = 0; i < nSamples; i++) { src_buf[1][i] = data1[i1]; i1 += 2; @@ -1857,7 +1028,7 @@ void BufferFull(short * Samples, int nSamples) // These are Stereo Samples { // Extract just left - for (i = 0; i < rx_bufsize; i++) + for (i = 0; i < nSamples; i++) { src_buf[0][i] = data1[i1]; i1 += 2; @@ -2185,8 +1356,18 @@ char * frame_monitor(string * frame, char * code, int tx_stat) case U_UI: frm = "UI"; + break; + + case U_XID: + + frm = "XID"; + break; + + case U_TEST: + + frm = "TEST"; } - + if (Digi[0]) sprintf(AGW_path, "Fm %s To %s Via %s <%s %c%s",CallFrom, CallTo, Digi, frm, c, p); else @@ -2229,624 +1410,3 @@ char * frame_monitor(string * frame, char * code, int tx_stat) return FrameData; } - -/* -procedure TForm1.RX2TX(snd_ch byte); -begin - if snd_status[snd_ch] = SND_IDLE then begin ptton(snd_ch); starttx(snd_ch); end; -end; - -function TForm1.frame_monitor(s,code string; tx_stat boolean) string; -var - len word; - s_tx_stat string; - time_now,s1,c,p string; - callfrom,callto,digi,path,data,frm string; - frm_body string; - pid,nr,ns,f_type,f_id byte; - rpt,cr,pf boolean; - i word; -begin - decode_frame(s,path,data,pid,nr,ns,f_type,f_id,rpt,pf,cr); - len = length(data); - // NETROM parsing - if pid = $CF then data = parse_NETROM(data,f_id); - // IP parsing - if pid = $CC then data = parse_IP(data); - // ARP parsing - if pid = $CD then data = parse_ARP(data); - // - get_monitor_path(path,CallTo,CallFrom,Digi); - if cr then - begin - c = "C"; - if pf then p = " P" else p = ""; - end - else - begin - c = "R"; - if pf then p = " F" else p = ""; - end; - frm = "UNKN"; - case f_id of - I_I frm = "I"; - S_RR frm = "RR"; - S_RNR frm = "RNR"; - S_REJ frm = "REJ"; - U_SABM frm = "SABM"; - U_DISC frm = "DISC"; - U_DM frm = "DM"; - U_UA frm = "UA"; - U_FRMR frm = "FRMR"; - U_UI frm = "UI"; - end; - case tx_stat of - TRUE s_tx_stat = "T"; - FALSE s_tx_stat = "R"; - end; - s1 = ""; - - if code<>"" then code = " ["+code+"]"; - if UTC_Time then time_now = " [UTC"+get_UTC_time+s_tx_stat+"]" - else time_now = " ["+FormatDateTime("hhmmss",now)+s_tx_stat+"]"; - - if digi = "" then frm_body = "Fm "+CallFrom+" To "+CallTo+" <"+frm+" "+c+p - else frm_body = "Fm "+CallFrom+" To "+CallTo+" Via "+Digi+" <"+frm+" "+c+p; - case f_type of - I_FRM frm_body = frm_body+" R"+inttostr(nr)+" S"+inttostr(ns)+" Pid = "+dec2hex(pid)+" Len = "+inttostr(len)+">"+time_now+code+#13+data; - U_FRM if f_id = U_UI then frm_body = frm_body+" Pid = "+dec2hex(pid)+" Len = "+inttostr(len)+">"+time_now+code+#13+data - else if f_id = U_FRMR then begin data = copy(data+#0#0#0,1,3); frm_body = frm_body+">"+time_now+code+#13+inttohex((byte(data[1]) shl 16) or (byte(data[2]) shl 8) or byte(data[3]),6) end - else frm_body = frm_body+">"+time_now+code; - S_FRM frm_body = frm_body+" R"+inttostr(nr)+">"+time_now+code; - end; - for i = 1 to length(frm_body) do - begin - if frm_body[i]>#31 then s1 = s1+frm_body[i]; - if frm_body[i] = #13 then s1 = s1+#13#10; - if frm_body[i] = #10 then s1 = s1+""; - if frm_body[i] = #9 then s1 = s1+#9; - end; - result = s1; -end; - -procedure TForm1.waterfall_init; -begin - bm[1] = TBitMap.Create; - bm[2] = TBitMap.Create; - bm1 = TBitMap.Create; - bm2 = TBitMap.Create; - bm3 = TBitMap.Create; - bm[1].pixelformat = pf32bit; - bm[2].pixelformat = pf32bit; - bm1.pixelformat = pf32bit; - bm2.pixelformat = pf32bit; - bm3.pixelformat = pf32bit; - bm[1].Height = PaintBox1.Height-20; - bm[1].Width = PaintBox1.width; - bm[2].Height = PaintBox1.Height-20; - bm[2].Width = PaintBox1.width; - bm1.Height = 20; - bm1.Width = PaintBox1.width; - bm3.Height = PaintBox1.Height; - bm3.Width = PaintBox1.width; -end; - -procedure TForm1.waterfall_free; -begin - bm[1].Free; - bm[2].Free; - bm1.Free; - bm2.Free; - bm3.Free; -end; - -procedure TForm1.StartAGW; -begin - try - ServerSocket1.Port = AGWPort; - ServerSocket1.Active = AGWServ; - except - ServerSocket1.Active = FALSE; - MessageDlg("AGW host port is busy!", mtWarning,[mbOk],0); - end; -end; - -procedure TForm1.StartKISS; -begin - try - ServerSocket2.Port = KISSPort; - ServerSocket2.Active = KISSServ; - except - ServerSocket2.Active = FALSE; - MessageDlg("KISS port is busy!", mtWarning,[mbOk],0); - end; -end; - -procedure fft_window_init; -var - mag single; - i word; -begin - mag = 2*pi/(FFTSize-1); - for i = 0 to FFTSize-1 do fft_window_arr[i] = 0.5-0.5*cos(i*mag); //hann -end; - -procedure TForm1.FormCreate(Sender TObject); -begin - if hPrevInst <> 0 then begin - MessageDlg("Программа уже запущена!", mtError, [mbOk], 0); - Application.Terminate; - end; - RS = TReedSolomon.Create(Self); - MainThreadHandle = GetCurrentThread; - form1.Caption = MODEM_CAPTION+" - Ver "+MODEM_VERSION; - cur_dir = ExtractFilePath(Application.ExeName); - fft_window_init; - detector_init; - kiss_init; - agw_init; - ax25_init; - init_raduga; - waterfall_init; - ReadIni; - show_combobox; - show_grid_title; - show_mode_panels; - show_panels; - wf_pointer(1); - wf_pointer(2); - wf_Scale; - ChangePriority; - Visible = TRUE; - StartAGW; - StartKISS; - PTTOpen; - startrx; - TimerEvent = TIMER_EVENT_OFF; - if (debugmode and DEBUG_TIMER) = 0 then create_timer1; - if MinOnStart then WindowState = wsMinimized; -end; - -procedure TForm1.TrackBar1Change(Sender TObject); -begin - dcd_threshold = TrackBar1.position; -end; -*/ - -void Timer_Event2() -{ - if (TimerStat2 == TIMER_BUSY || TimerStat2 == TIMER_OFF) - return; - - TimerStat2 = TIMER_BUSY; - -// show_grid(); - - /* - - if (mod_icon_status = MOD_WAIT) then inc(icon_timer); - if icon_timer = 10 then mod_icon_status = MOD_IDLE; - if (mod_icon_status<>MOD_WAIT) and (mod_icon_status<>last_mod_icon_status) then - begin - icon_timer = 0; - case mod_icon_status of - MOD_IDLE form1.CoolTrayIcon1.IconIndex = 0; - MOD_RX begin form1.CoolTrayIcon1.IconIndex = 1; mod_icon_status = MOD_WAIT; end; - MOD_TX form1.CoolTrayIcon1.IconIndex = 2; - end; - last_mod_icon_status = mod_icon_status; - end; - //*/ - - TimerStat2 = TIMER_FREE; -} - -/* - -procedure TimeProc1(uTimerId, uMesssage UINT; dwUser, dw1, dw2 DWORD); stdcall; -begin - TimerEvent = TIMER_EVENT_ON; -end; - -procedure TForm1.create_timer1; -var - TimeEpk cardinal; -begin - TimeEpk = 100; - TimerId1 = TimeSetEvent(TimeEpk,0,@TimeProc1,0,TIME_PERIODIC); -end; - -procedure TForm1.free_timer1; -begin - TimerStat1 = TIMER_OFF; - timeKillEvent(TimerId1); -end; - -*/ - -/* - - -procedure TForm1.PaintBox1MouseMove(Sender TObject; Shift TShiftState; X, - Y Integer); -var - low,high word; -begin - if CheckBox1.Checked then exit; - if not mouse_down[1] then Exit; - rx_freq[1] = round(x*RX_SampleRate/FFTSize); - low = round(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1]+1); - high = round(rx_samplerate/2-(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1])); - if (rx_freq[1]-low)<0 then rx_freq[1] = low; - if (high-rx_freq[1])<0 then rx_freq[1] = high; - tx_freq[1] = rx_freq[1]; - show_freq_a; -end; - -procedure TForm1.PaintBox3MouseMove(Sender TObject; Shift TShiftState; X, - Y Integer); -var - low,high word; -begin - if CheckBox1.Checked then exit; - if not mouse_down[2] then Exit; - rx_freq[2] = round(x*RX_SampleRate/FFTSize); - low = round(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2]+1); - high = round(rx_samplerate/2-(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2])); - if (rx_freq[2]-low)<0 then rx_freq[2] = low; - if (high-rx_freq[2])<0 then rx_freq[2] = high; - tx_freq[2] = rx_freq[2]; - show_freq_b; -end; - -procedure TForm1.PaintBox1MouseUp(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); -begin - mouse_down[1] = FALSE; -end; - -procedure TForm1.PaintBox3MouseUp(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); -begin - mouse_down[2] = FALSE; -end; - -procedure TForm1.PaintBox1MouseDown(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); -var - low,high word; -begin - if CheckBox1.Checked then exit; - if not (ssLeft in shift) then Exit; - mouse_down[1] = TRUE; - rx_freq[1] = round(x*RX_SampleRate/FFTSize); - low = round(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1]+1); - high = round(rx_samplerate/2-(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1])); - if (rx_freq[1]-low)<0 then rx_freq[1] = low; - if (high-rx_freq[1])<0 then rx_freq[1] = high; - tx_freq[1] = rx_freq[1]; - show_freq_a; -end; - -procedure TForm1.PaintBox3MouseDown(Sender TObject; Button TMouseButton; - Shift TShiftState; X, Y Integer); -var - low,high word; -begin - if CheckBox1.Checked then exit; - if not (ssLeft in shift) then Exit; - mouse_down[2] = TRUE; - rx_freq[2] = round(x*RX_SampleRate/FFTSize); - low = round(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2]+1); - high = round(rx_samplerate/2-(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2])); - if (rx_freq[2]-low)<0 then rx_freq[2] = low; - if (high-rx_freq[2])<0 then rx_freq[2] = high; - tx_freq[2] = rx_freq[2]; - show_freq_b; -end; - -procedure TForm1.ServerSocket1ClientRead(Sender TObject; - Socket TCustomWinSocket); -var - s string; -begin - s = Socket.ReceiveText; - AGW_explode_frame(Socket.sockethandle,s); -end; - -procedure TForm1.ServerSocket1ClientDisconnect(Sender TObject; - Socket TCustomWinSocket); -begin - del_incoming_mycalls_by_sock(socket.SocketHandle); - AGW_del_socket(socket.SocketHandle); -end; - -procedure TForm1.ServerSocket1ClientError(Sender TObject; - Socket TCustomWinSocket; ErrorEvent TErrorEvent; - var ErrorCode Integer); -begin - del_incoming_mycalls_by_sock(socket.SocketHandle); - AGW_del_socket(socket.SocketHandle); - ErrorCode = 0; -end; - -procedure TForm1.ServerSocket1ClientConnect(Sender TObject; - Socket TCustomWinSocket); -begin - agw_add_socket(Socket.sockethandle); -end; - -procedure TForm1.OutputVolume1Click(Sender TObject); -var - s string; -begin - s = "SndVol32.exe -D"+inttostr(SND_TX_DEVICE); - WinExec(pchar(s),SW_SHOWNORMAL); -end; - -procedure TForm1.InputVolume1Click(Sender TObject); -var - s string; -begin - s = "SndVol32.exe -R -D"+inttostr(SND_RX_DEVICE); - WinExec(pchar(s),SW_SHOWNORMAL); -end; - -procedure TForm1.CoolTrayIcon1Click(Sender TObject); -begin - CoolTrayIcon1.ShowMainForm; -end; - -procedure TForm1.CoolTrayIcon1Cycle(Sender TObject; NextIndex Integer); -begin - CoolTrayIcon1.IconIndex = 2; - CoolTrayIcon1.CycleIcons = FALSE; -end; - -procedure TForm1.ABout1Click(Sender TObject); -begin - Form2.ShowModal; -end; - -procedure TForm1.put_frame(snd_ch byte; frame,code string; tx_stat,excluded boolean); -var - s string; -begin - if RxRichedit1.Focused then Windows.SetFocus(0); - if code = "NON-AX25" then - s = inttostr(snd_ch)+" ["+FormatDateTime("hhmmss",now)+"R]" - else - s = inttostr(snd_ch)+""+frame_monitor(frame,code,tx_stat); - //RxRichedit1.Lines.BeginUpdate; - RxRichedit1.SelStart = length(RxRichedit1.text); - RxRichEdit1.SelLength = length(s); - case tx_stat of - TRUE RxRichEdit1.SelAttributes.Color = clMaroon; - FALSE RxRichEdit1.SelAttributes.Color = clBlack; - end; - if excluded then RxRichEdit1.SelAttributes.Color = clGreen; - RxRichedit1.SelText = s+#10; - if RxRichedit1.Lines.Count>nr_monitor_lines then - repeat - RxRichedit1.Lines.Delete(0); - until RxRichedit1.Lines.Count = nr_monitor_lines; - RxRichedit1.HideSelection = FALSE; - RxRichedit1.SelStart = length(RxRichedit1.text); - RxRichedit1.SelLength = 0; - RxRichedit1.HideSelection = TRUE; - //RxRichedit1.Lines.EndUpdate; -end; - -procedure TForm1.show_mode_panels; -begin - panel8.Align = alNone; - panel6.Align = alNone; - if dualchan then panel6.Visible = TRUE else panel6.Visible = FALSE; - panel8.Align = alLeft; - panel6.Align = alLeft; -end; - -procedure TForm1.show_panels; -var - i byte; -begin - panel1.Align = alNone; - panel2.Align = alNone; - panel3.Align = alNone; - panel4.Align = alNone; - panel5.Align = alNone; - for i = 1 to 5 do - case i of - 1 panel1.Visible = panels[i]; - 2 panel5.Visible = panels[i]; - 3 panel3.Visible = panels[i]; - 4 panel4.Visible = panels[i]; - 5 panel2.Visible = panels[i]; - end; - panel1.Align = alBottom; - panel5.Align = alBottom; - panel3.Align = alBottom; - panel2.Align = alTop; - panel4.Align = alClient; -end; - -procedure TForm1.Secondwaterfall1Click(Sender TObject); -begin - case Secondwaterfall1.Checked of - TRUE Secondwaterfall1.Checked = FALSE; - FALSE Secondwaterfall1.Checked = TRUE; - end; - panels[1] = Secondwaterfall1.Checked; - show_panels; -end; - - - -procedure TForm1.Firstwaterfall1Click(Sender TObject); -begin - case Firstwaterfall1.Checked of - TRUE Firstwaterfall1.Checked = FALSE; - FALSE Firstwaterfall1.Checked = TRUE; - end; - panels[2] = Firstwaterfall1.Checked; - show_panels; -end; - -procedure TForm1.Statustable1Click(Sender TObject); -begin - case Statustable1.Checked of - TRUE Statustable1.Checked = FALSE; - FALSE Statustable1.Checked = TRUE; - end; - panels[3] = Statustable1.Checked; - show_panels; -end; - -procedure TForm1.Monitor1Click(Sender TObject); -begin - case Monitor1.Checked of - TRUE Monitor1.Checked = FALSE; - FALSE Monitor1.Checked = TRUE; - end; - panels[4] = Monitor1.Checked; - show_panels; -end; - -procedure TForm1.Devices1Click(Sender TObject); -begin - if (ptt = "EXT") or (ptt = "CAT") then Form3.Button3.Enabled = TRUE else Form3.Button3.Enabled = FALSE; - Form3.GetDeviceInfo; - form3.ShowModal; -end; - -procedure TForm1.FormPaint(Sender TObject); -begin - RxRichedit1.HideSelection = FALSE; - RxRichedit1.SelStart = length(RxRichedit1.text); - RxRichedit1.SelLength = 0; - RxRichedit1.HideSelection = TRUE; -end; - -procedure TForm1.Filters1Click(Sender TObject); -begin - Form5.Show_modem_settings; -end; - -procedure TForm1.Clearmonitor1Click(Sender TObject); -begin - RxRichEdit1.Clear; - frame_count = 0; - single_frame_count = 0; -end; - -procedure TForm1.Copytext1Click(Sender TObject); -begin - RxRichEdit1.CopyToClipboard; -end; - -procedure TForm1.ApplicationEvents1Minimize(Sender TObject); -begin - if stop_wf then w_state = WIN_MINIMIZED; -end; - -procedure TForm1.ApplicationEvents1Restore(Sender TObject); -begin - w_state = WIN_MAXIMIZED; -end; - -procedure TForm1.ServerSocket2ClientConnect(Sender TObject; - Socket TCustomWinSocket); -begin - KISS_add_stream(socket.sockethandle); -end; - -procedure TForm1.ServerSocket2ClientDisconnect(Sender TObject; - Socket TCustomWinSocket); -begin - KISS_del_stream(socket.sockethandle); -end; - -procedure TForm1.ServerSocket2ClientError(Sender TObject; - Socket TCustomWinSocket; ErrorEvent TErrorEvent; - var ErrorCode Integer); -begin - KISS_del_stream(socket.sockethandle); - ErrorCode = 0; -end; - -procedure TForm1.ServerSocket2ClientRead(Sender TObject; - Socket TCustomWinSocket); -var - data string; -begin - data = socket.ReceiveText; - KISS_on_data_in(socket.sockethandle,data); -end; - -procedure TForm1.Font1Click(Sender TObject); -begin - FontDialog1.Font = RXRichEdit1.Font; - if FontDialog1.Execute then - begin - RXRichEdit1.SelStart = 0; - RXRichEdit1.SelLength = Length(RXRichEdit1.Text); - RXRichEdit1.SelAttributes.Size = FontDialog1.Font.Size; - RXRichEdit1.SelAttributes.Name = FontDialog1.Font.Name; - RXRichEdit1.Font.Size = FontDialog1.Font.Size; - RXRichEdit1.Font.Name = FontDialog1.Font.Name; - WriteIni; - end; -end; - -procedure TForm1.Calibration1Click(Sender TObject); -begin - Form6.ShowModal; -end; - -procedure TForm1.ComboBox1Change(Sender TObject); -begin - Speed[1] = get_idx_by_name(ComboBox1.Text); - init_speed(1); - windows.setfocus(0); -end; - -procedure TForm1.ComboBox1KeyDown(Sender TObject; var Key Word; - Shift TShiftState); -begin - key = 0; - windows.SetFocus(0); -end; - -procedure TForm1.ComboBox1KeyPress(Sender TObject; var Key Char); -begin - key = #0; - windows.SetFocus(0); -end; - -procedure TForm1.ComboBox2Change(Sender TObject); -begin - Speed[2] = get_idx_by_name(ComboBox2.Text); - init_speed(2); - windows.setfocus(0); -end; - -procedure TForm1.FormDestroy(Sender TObject); -var - snd_ch byte; -begin - stoprx; - for snd_ch = 1 to 2 do if snd_status[snd_ch] = SND_TX then stoptx(snd_ch); - if (debugmode and DEBUG_TIMER) = 0 then free_timer1; - TimerStat2 = TIMER_OFF; - PTTClose; - ax25_free; - agw_free; - kiss_free; - detector_free; - RS.Free; - waterfall_free; - WriteIni; -end; - -end. -*/ \ No newline at end of file diff --git a/symbols.h b/symbols.h new file mode 100644 index 0000000..5ed91ad --- /dev/null +++ b/symbols.h @@ -0,0 +1,19 @@ + +/* symbols.h */ + +void symbols_init (void); + +void symbols_list (void); + +void symbols_from_dest_or_src (char dti, char *src, char *dest, char *symtab, char *symbol); + +int symbols_into_dest (char symtab, char symbol, char *dest); + +void symbols_get_description (char symtab, char symbol, char *description, size_t desc_size); + +int symbols_code_from_description (char overlay, char *description, char *symtab, char *symbol); + +void symbols_to_tones (char symtab, char symbol, char *tones, size_t tonessize); + + +/* end symbols.h */ diff --git a/telemetry.h b/telemetry.h new file mode 100644 index 0000000..4ef9b62 --- /dev/null +++ b/telemetry.h @@ -0,0 +1,15 @@ + + +/* telemetry.h */ + +void telemetry_data_original (char *station, char *info, int quiet, char *output, size_t outputsize, char *comment, size_t commentsize); + +void telemetry_data_base91 (char *station, char *cdata, char *output, size_t outputsize); + +void telemetry_name_message (char *station, char *msg); + +void telemetry_unit_label_message (char *station, char *msg); + +void telemetry_coefficents_message (char *station, char *msg, int quiet); + +void telemetry_bit_sense_message (char *station, char *msg, int quiet); diff --git a/tq.h b/tq.h new file mode 100644 index 0000000..37599d5 --- /dev/null +++ b/tq.h @@ -0,0 +1,41 @@ + +/*------------------------------------------------------------------ + * + * Module: tq.h + * + * Purpose: Transmit queue - hold packets for transmission until the channel is clear. + * + *---------------------------------------------------------------*/ + +#ifndef TQ_H +#define TQ_H 1 + +#include "ax25_pad.h" +#include "audio.h" + +#define TQ_NUM_PRIO 2 /* Number of priorities. */ + +#define TQ_PRIO_0_HI 0 +#define TQ_PRIO_1_LO 1 + + + +void tq_init (struct audio_s *audio_config_p); + +void tq_append (int chan, int prio, packet_t pp); + +void lm_data_request (int chan, int prio, packet_t pp); + +void lm_seize_request (int chan); + +void tq_wait_while_empty (int chan); + +packet_t tq_remove (int chan, int prio); + +packet_t tq_peek (int chan, int prio); + +int tq_count (int chan, int prio, char *source, char *dest, int bytes); + +#endif + +/* end tq.h */ diff --git a/tt_text.h b/tt_text.h new file mode 100644 index 0000000..7cab3b8 --- /dev/null +++ b/tt_text.h @@ -0,0 +1,38 @@ + +/* tt_text.h */ + + +/* Encode normal human readable to DTMF representation. */ + +int tt_text_to_multipress (const char *text, int quiet, char *buttons); + +int tt_text_to_two_key (const char *text, int quiet, char *buttons); + +int tt_text_to_call10 (const char *text, int quiet, char *buttons); + +int tt_text_to_mhead (const char *text, int quiet, char *buttons, size_t buttonsiz); + +int tt_text_to_satsq (const char *text, int quiet, char *buttons, size_t buttonsiz); + +int tt_text_to_ascii2d (const char *text, int quiet, char *buttons); + + +/* Decode DTMF to normal human readable form. */ + +int tt_multipress_to_text (const char *buttons, int quiet, char *text); + +int tt_two_key_to_text (const char *buttons, int quiet, char *text); + +int tt_call10_to_text (const char *buttons, int quiet, char *text); + +int tt_call5_suffix_to_text (const char *buttons, int quiet, char *text); + +int tt_mhead_to_text (const char *buttons, int quiet, char *text, size_t textsiz); + +int tt_satsq_to_text (const char *buttons, int quiet, char *text); + +int tt_ascii2d_to_text (const char *buttons, int quiet, char *text); + + + +/* end tt_text.h */ \ No newline at end of file diff --git a/tt_user.h b/tt_user.h new file mode 100644 index 0000000..4ff2ec8 --- /dev/null +++ b/tt_user.h @@ -0,0 +1,15 @@ + +/* tt_user.h */ + + +#include "audio.h" + +void tt_user_init (struct audio_s *p_audio_config, struct tt_config_s *p); + +int tt_user_heard (char *callsign, int ssid, char overlay, char symbol, char *loc_text, double latitude, + double longitude, int ambiguity, char *freq, char *ctcss, char *comment, char mic_e, char *dao); + +int tt_3char_suffix_search (char *suffix, char *callsign); + +void tt_user_background (void); +void tt_user_dump (void); \ No newline at end of file diff --git a/tune.h b/tune.h new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/tune.h @@ -0,0 +1 @@ + diff --git a/version.h b/version.h new file mode 100644 index 0000000..a09490c --- /dev/null +++ b/version.h @@ -0,0 +1,21 @@ + +/* Dire Wolf version 1.6 */ + +// Put in destination field to identify the equipment used. + +#define APP_TOCALL "APDW" // Assigned by WB4APR in tocalls.txt + +// This now comes from compile command line options. + +//#define MAJOR_VERSION 1 +//#define MINOR_VERSION 6 +//#define EXTRA_VERSION "Beta Test" + + +// For user-defined data format. +// APRS protocol spec Chapter 18 and http://www.aprs.org/aprs11/expfmts.txt + +#define USER_DEF_USER_ID 'D' // user id D for direwolf + +#define USER_DEF_TYPE_AIS 'A' // data type A for AIS NMEA sentence +#define USER_DEF_TYPE_EAS 'E' // data type E for EAS broadcasts diff --git a/waypoint.h b/waypoint.h new file mode 100644 index 0000000..3ba6f1c --- /dev/null +++ b/waypoint.h @@ -0,0 +1,24 @@ + +/* + * Name: waypoint.h + */ + + +#include "ax25_pad.h" /* for packet_t */ + +#include "config.h" /* for struct misc_config_s */ + + +void waypoint_init (struct misc_config_s *misc_config); + +void waypoint_set_debug (int n); + +void waypoint_send_sentence (char *wname_in, double dlat, double dlong, char symtab, char symbol, + float alt, float course, float speed, char *comment_in); + +void waypoint_send_ais (char *sentence); + +void waypoint_term (); + + +/* end waypoint.h */ diff --git a/xid.h b/xid.h new file mode 100644 index 0000000..a221b73 --- /dev/null +++ b/xid.h @@ -0,0 +1,32 @@ + + +/* xid.h */ + + +#include "ax25_pad.h" // for enum ax25_modulo_e + + +struct xid_param_s { + + int full_duplex; + + // Order is important because negotiation keeps the lower value of + // REJ (srej_none), SREJ (default without negotiation), Multi-SREJ (if both agree). + + enum srej_e { srej_none=0, srej_single=1, srej_multi=2, srej_not_specified=3 } srej; + + enum ax25_modulo_e modulo; + + int i_field_length_rx; /* In bytes. XID has it in bits. */ + + int window_size_rx; + + int ack_timer; /* "T1" in mSec. */ + + int retries; /* "N1" */ +}; + + +int xid_parse (unsigned char *info, int info_len, struct xid_param_s *result, char *desc, int desc_size); + +int xid_encode (struct xid_param_s *param, unsigned char *info, cmdres_t cr); \ No newline at end of file diff --git a/xmit.h b/xmit.h new file mode 100644 index 0000000..248037d --- /dev/null +++ b/xmit.h @@ -0,0 +1,27 @@ + + +#ifndef XMIT_H +#define XMIT_H 1 + +#include "audio.h" /* for struct audio_s */ + + +extern void xmit_init (struct audio_s *p_modem, int debug_xmit_packet); + +extern void xmit_set_txdelay (int channel, int value); + +extern void xmit_set_persist (int channel, int value); + +extern void xmit_set_slottime (int channel, int value); + +extern void xmit_set_txtail (int channel, int value); + +extern void xmit_set_fulldup (int channel, int value); + + +extern int xmit_speak_it (char *script, int c, char *msg); + +#endif + +/* end xmit.h */ +