From 3cdcbeea3e2891f4a10c79b272da144f37ff24f3 Mon Sep 17 00:00:00 2001 From: Dave Hibberd Date: Tue, 12 Sep 2023 21:38:15 +0100 Subject: [PATCH] New upstream version 0.67 --- ALSASound.c | 36 +- Config - Copy.cpp | 444 --- Config.cpp | 14 + Config.cpp.bak | 439 --- HEAD | 1 - LinuxBits.c | 311 ++ ModemDialog.ui | 355 +- Modulate.c | 31 +- QtSoundModem - Copy.cpp | 2827 -------------- QtSoundModem-DESKTOP-MHE5LO8.vcxproj | 292 ++ QtSoundModem.cpp | 278 +- QtSoundModem.pro | 3 +- QtSoundModem.ui | 4 +- QtSoundModem.ui.bak | 516 --- QtSoundModem.vcxproj | 196 +- QtSoundModem.vcxproj-DESKTOP-MHE5LO8.user | 34 + QtSoundModem.vcxproj.filters | 6 +- QtSoundModem.vcxproj.user | 10 +- SMMain.c.bak => SMMain-DESKTOP-MHE5LO8.c | 386 +- SMMain.c | 389 +- ShowFilter.cpp.bak | 234 -- SoundInput.c | 10 +- UZ7HOStuff-DESKTOP-MHE5LO8.h | 1110 ++++++ UZ7HOStuff.h | 91 +- Waveout.c | 31 +- agwlib.h | 45 + ais.h | 8 + aprs_tt.h | 191 + audio_stats.h | 7 + ax25.c | 36 +- ax25_agw.c | 2 +- ax25_demod.c | 300 +- ax25_link.h | 88 + ax25_mod-DESKTOP-MHE5LO8.c | 1810 +++++++++ ax25_mod.c | 148 +- ax25_pad2.h | 55 + beacon.h | 6 + cdigipeater.h | 62 + cm108.h | 5 + config.h | 262 ++ debug/QtSoundModem.ini | 197 + debug/moc_predefs-DESKTOP-MHE5LO8.h | 12 + debug/moc_predefs.h | 12 + decode_aprs.h | 150 + dedupe.h | 10 + demod.h | 17 + demod_afsk.h | 8 + demod_psk.h | 7 + digipeater.h | 78 + dlq.h | 148 + dns_sd_common.h | 7 + dns_sd_dw.h | 10 + dtime_now.h | 18 + dtmf.h | 14 + dw9600.c | 4184 +++++++++++++++++++++ dw9600.h | 2042 ++++++++++ dwgps.h | 61 + dwgpsd.h | 22 + dwgpsnmea.h | 32 + dwsock.h | 21 + encode_aprs.h | 17 + fcs_calc.h | 11 + fsk_filters.h | 7 + fsk_gen_filter.h | 15 + gen_tone.h | 17 + grm_sym.h | 501 +++ hdlc_send.h | 17 + igate.h | 128 + il2p.c | 97 +- kiss.h | 24 + kiss_frame.h | 126 + kiss_mode.c | 2 +- kissnet.h | 29 + kissserial.h | 23 + latlong.h | 24 + libfftw3f-64-3.lib | Bin 0 -> 247600 bytes log.h | 19 + main.cpp | 2 +- mgn_icon.h | 276 ++ mheard.h | 20 + morse.h | 8 + multi_modem.h | 24 + pfilter.h | 13 + pktARDOP.c | 2 +- ptt.h | 26 + recv.h | 6 + redecode.h | 15 + release/moc_predefs-DESKTOP-MHE5LO8.h | 11 + release/moc_predefs.h | 11 + rpack.h | 94 + rrbb.h | 92 + serial_port.h | 32 + server.h | 32 + sm_main - Copy.c | 2850 -------------- sm_main.c | 1836 +-------- symbols.h | 19 + telemetry.h | 15 + tq.h | 41 + tt_text.h | 38 + tt_user.h | 15 + tune.h | 1 + version.h | 21 + waypoint.h | 24 + xid.h | 32 + xmit.h | 27 + 105 files changed, 15126 insertions(+), 9637 deletions(-) delete mode 100644 Config - Copy.cpp delete mode 100644 Config.cpp.bak delete mode 100644 HEAD create mode 100644 LinuxBits.c delete mode 100644 QtSoundModem - Copy.cpp create mode 100644 QtSoundModem-DESKTOP-MHE5LO8.vcxproj delete mode 100644 QtSoundModem.ui.bak create mode 100644 QtSoundModem.vcxproj-DESKTOP-MHE5LO8.user rename SMMain.c.bak => SMMain-DESKTOP-MHE5LO8.c (73%) delete mode 100644 ShowFilter.cpp.bak create mode 100644 UZ7HOStuff-DESKTOP-MHE5LO8.h create mode 100644 agwlib.h create mode 100644 ais.h create mode 100644 aprs_tt.h create mode 100644 audio_stats.h create mode 100644 ax25_link.h create mode 100644 ax25_mod-DESKTOP-MHE5LO8.c create mode 100644 ax25_pad2.h create mode 100644 beacon.h create mode 100644 cdigipeater.h create mode 100644 cm108.h create mode 100644 config.h create mode 100644 debug/QtSoundModem.ini create mode 100644 debug/moc_predefs-DESKTOP-MHE5LO8.h create mode 100644 debug/moc_predefs.h create mode 100644 decode_aprs.h create mode 100644 dedupe.h create mode 100644 demod.h create mode 100644 demod_afsk.h create mode 100644 demod_psk.h create mode 100644 digipeater.h create mode 100644 dlq.h create mode 100644 dns_sd_common.h create mode 100644 dns_sd_dw.h create mode 100644 dtime_now.h create mode 100644 dtmf.h create mode 100644 dw9600.c create mode 100644 dw9600.h create mode 100644 dwgps.h create mode 100644 dwgpsd.h create mode 100644 dwgpsnmea.h create mode 100644 dwsock.h create mode 100644 encode_aprs.h create mode 100644 fcs_calc.h create mode 100644 fsk_filters.h create mode 100644 fsk_gen_filter.h create mode 100644 gen_tone.h create mode 100644 grm_sym.h create mode 100644 hdlc_send.h create mode 100644 igate.h create mode 100644 kiss.h create mode 100644 kiss_frame.h create mode 100644 kissnet.h create mode 100644 kissserial.h create mode 100644 latlong.h create mode 100644 libfftw3f-64-3.lib create mode 100644 log.h create mode 100644 mgn_icon.h create mode 100644 mheard.h create mode 100644 morse.h create mode 100644 multi_modem.h create mode 100644 pfilter.h create mode 100644 ptt.h create mode 100644 recv.h create mode 100644 redecode.h create mode 100644 release/moc_predefs-DESKTOP-MHE5LO8.h create mode 100644 release/moc_predefs.h create mode 100644 rpack.h create mode 100644 rrbb.h create mode 100644 serial_port.h create mode 100644 server.h delete mode 100644 sm_main - Copy.c create mode 100644 symbols.h create mode 100644 telemetry.h create mode 100644 tq.h create mode 100644 tt_text.h create mode 100644 tt_user.h create mode 100644 tune.h create mode 100644 version.h create mode 100644 waypoint.h create mode 100644 xid.h create mode 100644 xmit.h 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 0000000000000000000000000000000000000000..e605463135599f1275e44c396d134c591d209ebf GIT binary patch literal 247600 zcmeEveUuzU^>%eNBE}daM#P9QBF2ajvzreRj1gmqB!&=Uz!)Qw{g}xP*`3|Y&L#m7 z5fKp)5iuenA|fIpA|fIpA|fIpA|fIpA|fIpLd1xO`Mpn7-R|kCzPBcSzsEi2Ikj`2 zd+X!Yt?KHUo~~Cc=xO#1EFAaxss3Nr-1)P*=gpfvzuTA3q>ppEyXMWd|F0jX)U%&Z z$wO;Z^6*(I`O9r8`Rh!=g-C%%_a>Z*6!@Daj371G?(kSmB^Gxg1^&K_a2-9#}bx47yQNF=5J01SJStS;*|1Yo;?XkEQDe&|~4*!{=5{qp} zfoCQVPDTp+-)h3GNN-hIEh1cv6iBx@>^4zpi&K#T&)SV}JW?PzfN%*?Ku;rFfD}mg zad_5K&A_ewp5gtJbyaYA|lhF?r*CGX8I+t)cQsBkw38?#vfxXud?nDZ_WQD^% z`zvj6AyVLFQwV1x1*YstI1?%Ga(uQh{{vI;*~0t}yaJys%>Tf?_-t_oQs9*n3Fd!b zKYX@09VzfC_{-unq`hA0q`(Z=Tbzg#c=b5K2}ligI7}Z@ z+TsDEzzoO@UUMYj7No%IjwalP6gXfN;U=WO>klGah7_2&FJT)}pbO<&9FNpsyF+(P zX$!yQHTy9pN| z1rC}_I14H82K0x;sYrnZD97SNqy{@3-ndn1izkr+3mb$-kOFV&C)|$|ICw1qbvhV$ z^Gd?4NP$I52x$8v;4KRXS0e=$quv&mA_WeaMz{beuw);?Hl)B?(GCl=;jO@-n2#)u zM{4kt!_t=07VzIvVA%lS0i?j&))7$Ww*kvhZ;M-y0*5Uoz_*71D-I%Di4-_|CINLm z99TJ>fI6=P-adtJHd0{KB*N)Pfp?4}oP-outqADz)do8pju=te;!&i)ku?I^a3t_f zw8H{zcqgz1?Xb8NDex}%-{N|tz*@A!0&Q3ey!!yc#Ylm5`x4GW3cP1;0%G$$z)|A~ zCnE(asJ8{`TrqgkVSPhsi-(Z{J^h6HkOIAH33nm|`cQ8R_`VP5N4+gj=YF7yxUskj zDNuv|EiOR{Y?wwkA1N>}nQ#_TpgxgsDpKI+-3Z4cH9*`LY-|!9LkbKI5FSJd3>`(d z2Px24O}HH?FuaU#6H;L0V8S&>fladsmm>w5`x7oi3XD!6oP!i-O(L9*6d2osa3WHJ zXB;+fRoY@ZQeaDi0N-x`w)PV4MG73VhHwW`;MnB^`1)Ajy{M`NP+j)2=^lePFPF06Djb46@;6S0w*paT#FR=;9LTH^+Dhy_{-ol z)X(A)q`=3f61E`)PM<(H38}%;4j&&;+TtOkz!~cZ@WC0tCk`cChZHyyQ&3NfN00&^ zLH}9YgA_P*72#H-z(*Go;Ioec=RW4}xqhWBVDmZPyp;s_`aIzC3kg>u1TwYfloCE=z~uI+m0gKg%tSo z3Ic3D4V;a7T3m$`_{;%>i;x26Od)JT3Ve0~;bf!+I~~s548H&uO(&d-6!`K)!bwOC z&<6$=4-)Q23VdZX0lxbRa0%LFaXC`ptNRhoMhaXyo^S$EgC`xnHlVb{gGhnP))U~n z%Yd&RNw^&;aQSk=O-O-nEFxTs6u4p@0lvKg_$K^gaWPWhO4Q5ZJfy(4CKJv=3S2dj za4J&Z+q)A`=WiQ4<8U>6XR#eA@E!Qa;$ft~HHcG-`;Y?PT|>ANDR3?N)#7HP!1vG& zi|ddA*P$I2Xv1~D_h%3;LJC|zm2eJH;0KckXCMV`7)Lk>sliT%A8u9J0(Jf&a3kt% z@eoqrNASPJy-0zZP;ZMnkODtOy)97Z9|JcpCZNtY13x*4a3xaUmYIZ0kODuQMmQfS zaO*yVZAgKi?MXNdDRA2!gcFe(pdAK3M>{N@L<-#AAUuK;_(ebAex$%1YYBHD1%A1b za4S;a&LxEFkpjO$J1nk33fzTuSfCAe0l(gla2`_N?!5_TA_aako^T3M;2upl4ynNo zhu@AUZGk%f7PuGnwzv-|@H_b50>1wpa3B0{0pH&T{2p;*aV=8d{&|EekOF_0LAVGh z@W52USxA9D?minoYP2De&k02^S&-9!5D9h|PzA zzwAXg11azb{9|!4QsA$Oa4b>-_{ZQ;_{Rdic@+2?>SY1n{0(>v{;{|dDe!mr#{$0j zJMcLCV{si);0gH0;!32z_5%o)AO-%hAK`qYz>|{+XCVdtIe~C0Qeek!gyWGKJmv7O zrqULVA_blrAUuE+`1d-(T}XkaR}pSS3jF6#!VO4)XBH5yLJItMCINN+FR*hO0d?L9 z{NFx=ZAiDNRP9MP4JnZ9K{yeq!A=LgRizf&kpk%;;UT2JZtDs6AO)Vans7T(VE1K& zn~(y}KA3PVQecm{ge#B&&zV8E7%4DrU&6Uaf#>c;I0Gp#ejMQ>qy|qrJg=!zi~EoQ z6HpI}tB?ZEpGMe*6qtziTO5bf;Bki+^s3b2Zlu7TXoJOaq`(Uo6Rt!GOqx!(2r2L) z)ZgM9q`+Q#5za&kym)T{+VWyxZ`8vAeXuw15=9t6YOvj5GXA%C5GnA|b%eW+0{fs| z7FQz$UIxEfpbjqsri>?`j41|BIlTO6m0CQ26qtGx;U1*GD^?J0K?>{(zgpak6nNzV z!sSST{bmy2i~WFCO(vX-)Zl4{X@e@YxEm?(YLsJfBT``cJi>)Yf!FL!I2|dl|L%m7 zkQzMh@Y;TrTA=Q)1!gQEps!{CubWCZ4JmK{{BMC6IKTk)Gk87zw|E9AFmr@(KT@D; zIpKPwKzBFc5~RSa{RrnG1!hkq!1uEab~wy|A1vVeIl$Z_3Gnq?;J{^stC0fpW;o2B zq*8-<=R3?toERL`OSl~=Fdx1#c*8=%rAUDVlL@CFH9(sUu;=0MFj8P4>SJ*eQs7PV z2x#M*fP?oTpid44-V7THjnts!uxO1+EpA2%VE@D63Z%f|X@qS^fkVa-PCyF0wMMuf zDR3zAGdN_2!xH$>;z6Xqp?5eeh5sy2_ocwHxrB?50&m-!a3)e<`FO&qNDX#69ENsS zY)1;LXb>Jl3LK93uy`CPuyQ>CeXtUE`y#@XNP$(;2xlV&un*#J98v?6VX*pW!UITw zBUTZvM+zJ{gKz;-;GL5QCnGg@(qT=%N-gd{3cPC(;R>X{+ImZi$j1V0dJk~abi#Q^fePwl0bf=O;7^0~Ey5#6fgbqF;x42>?@GcgNP)hC z3D+V8`WFz)|3Gyn;bNpfZ7Sg`q`-y=gwv1$1LFwCBQ-!A8PppNgXgH!p#Ffv(Q8#| zfw~+GY()7MmmviPQJ%rjM8bBY1}8W)22^TsA5vg=HQ{EYz{o-Z+A;!c>LwuXO+a%x z0kP5qMkf)_wo#xpfq?Q_22VPSq3>RpxE?9+zS)GUkOIe}T^5%k1>Qe{a0yc2gaZg@^9jHQ(61J- z{Qz(x>@BWD3Vd)H;asG^Ns|btAT`+O@S&DUEzrgf0VktBEzqWufe*v~7Rd9%z$sG+ zXCVbXGLCQ>QUl~`aOzgVcBH^Z2MG5e1x{N{K+K#5d~6}%I;6nq3kdMt>A=U)UW<#7 z0%uGnpblpMpO`>68L7e34rex0YVjyi;FIeJw<874!Z@|K8Y%Fp*#wmNDPY@l!ZxJ9 zr*|hDhtyz)!`Xu>wYVQC@R=hCHzNhknMXj~&jCIQKU$oP6gYP`LKCS0`oiFIsISEx zNP+VXC7`b70iT~vI2S2!{zSqlNP#aX!d9dPk2_p|dC%f*q`((f5pG2aT)2#IGg9D7 zO9+VVF98=FM7SI&@Z~9l(~treYXZu=*x*Trub^!f4^adP>0J#RBG_m zs~s+#p;C+Ukpf?vL^uVh!7~n*VO}!0VwwZ?Nm7g3kOG&Z9TteO%YkpqC0vFSxB@;j z_}2Xn-<+sY3zYFq;7a(@0&Tj|;Bkj<4L~2b3gubAhgSjLMtoUZixjvTcdx62J5}GT5HR!+UsViN@c|n%a@mx)cXrsPG|L@N33{T zW$~L=zZq8bp^=Koq*mz}tZ!%xRU56!a7{5HUw3f0uhKU>G%{G-nkyG&W$k9QXV6o% z0#9#rwAyUdhZ~hzPkpf3pJ|8UqD}8ut-n6nBGv3uQKN65+PBebT?H9-S4M`&D5Bu! zGdw(6tu%Z3>sgcg1_~licd**i7#qn7pihiOr8NLI^pEo-$^j5lN26;P~q}|PEcYATwM|Cv$%we?Wh}v@*?YWXRQxUNq-Ao%H z=_*&m3MG9e`|e64|L(F|pfCBQyE3cXRx6b1GrQ!+?#gVTwWeG*_zV3OsY_YA zyCT;-jN5_UZbTey6N}|wlm&B(H4&oT za(p1f;(%?6Gy+T+{k0Zz)hjS!d#oiJcf`ak*|u5BNc#*g78!|lYsUsWF|oQzVGq-I zX8khP4A2=Kz7ASPBOKQxSCWONu>45AJ(BsHLN_rB~vCx+`(oAxw8EO6Ns=;n=t@=Eb{&Xeo~xf5Ernhz&Pk!A8F7g5>6B zE0)XQi;0!OUSVtEJtx*SHQ|!(av5w>Vl6T2f%5*1=>cNIvMkTW9WilBHps1Iqf(&=kc+Tdp^8|xQw~vwbXdu{a>SLa zN5|`ttyCp{WPCgqX(j6_5?{$?I#GXgSjoCP;(@v=aVoB4vjS`-WOFRhHNGnmrl*2*O9($+V$g}r?+rRm$sguI!G2v>Z&iY>KKy4qr;$5ce# zqk=xN3bU72vqrz{7Axdi%6(gdOr$G5sM59G%J|&5%81__As?>lQjeN7bJ;P%ssXFz zk}^a>t;6FkzyN5lZdhBzfm>}Q)}TnR%kQs<42a!WhyPqHPB{UEqa=%GCy-5U&hU%mrEnd z*o(CYM;4U%kp&{0zN|=yjBsQ@R~lKp`JI73J2tx73}1@lnX7$fDy}!2LKm|REi!Vc zUEFS2X4;|DZf=R=Y>AS~g@V}JjEh){?UnC`sA7>~9VM4KODxqfF-x(rvpSW^t*KpG zzmyqGQ&Ni zWLwe0-5op(UTSMb$)!r&+F{(V-*Y%0b%3p$`#PF6iac$IQVwP<5I8#f> zw*1=2m^vnADV9phCfQb48<}FGYa>z+3y!aitTo%QZ6;D@zH6On2QTw$BV!tyaUCx< zHo7)4w)}k5X5_XC^N};{&{lrpHKwtd*YRRoqjRsZ<<~}SMsBMx5jxX&TZOd|OoOl_S9#1h3}8e1D7UwM#5*G8}u1E|9YmBXjg_!QPgs7rKhIjdDVaI$>>O4de3YKKhHWMBT%kouwskWbS88O=vsv?#UXhYZWv2sc8krA>7uy<}5v(mcR0@n- zU;g?@rg9`11^)V3V8l{|>nE9ZD3zNPtfeL7GTAGcGmMwWU&)LmF=Cn7<%$Wyj1q0d zKkUGSX zFaz2gS7Pfp8<%HNy zA&RV_*v?JmCljoQD+NUO^neu!y5aKx(B;302N6CGU`0Z{;qw5{<(~%t5q@iLMSQ+k z!EhJ$P3DVu`3gWZ)7eEvERl<8YbejfL`x+`uISd1+vO+_Voljy4l;~Hm#{9hhAz;> z64`hr!$^skHTizb3BE*2vtDg8q9xytFpTx1rKJ+1{Rm6GA9F&isnCzsFz!`uUfN`~ zBxC((X{n9W!(3gP>0n#2`Hm4sx4x35EI+dPga~Ay5VU}ZE%RkfR^w~xT z&$U?#wksk%!FEKX1#CA&dZF}`o2{kroSWssC*4RW_5vH>k@yauc(eKQ&%7CG>C&a; zM@kEXR4;4CbsrH?JFZu(o!m2)=6Z*SSb0)4<Yk@gdJK$#OlZmFk{9)Pnvn38)n8z9%+W< z2Ah)$HN@i1?A{Mj_fFL@>oeit0H3sd2$nx6-P=c{i1^UP`+-O z9_B`k6-P=EniTCPp_}CKgG#U?$jKwM<3`GjM_iiDg?W5Fnk zwGPJsl=C-`@$H9G9TX{!q~PVA>S~jp)196?-;%!;NIcX z>MBrtIi$es-Q>pNev#AQw&9A5TrT^hNM_ohRPL!#XDUCdmhoa^BTuV2TW$le$jEJF zzn;iUJG7PC1azkIGiwIk&`oWec26kSE2aGLEgg1$r-s+Y2;Ybcw5=0DQtx!Pg7*P)UVlt7GjO* zscZlK(B&GPoAB+8?BR@(OJ%A{%BR@#mi+ZtVI8(V&gXftwK zg-ODhc4#XbTcay&JP{$>lD8S5t=5t+wN1&k!Yl#P*h<^fN}46wlw7JX za$p)AIhGgO8Xq~Z6-G|Z$n}**jx~)%6Q*H)s|6m6uCyI5wKcxdHnyUNi4ohHEvLif zpv$yFTiI2Mv5c&=EiE=Qc14G!u+nZbVmq_tG}F$El5M4SMz3+=O54=7Lpy~FTz2IX zYbssdI;^x&BQbzFj8I{vZF7uAkFe5)Wpt(OcqxG5D{W&d^<`|OZSBM&JM^V+DeZ72 zHMCS>{5mRLMl(W;9_b?5VWkc42$#@|5K9zR+BU~X^x%1BVRmmbe3_1C9b2G8TmJG1 z%h-%(X{kuMtcoQHGam9JCT1xW8lUlyH$VU6gjl99|5(F#fAUK|7&<~MQHWV!-8|1{dAud2kie?q(bjhXoFPiU9EF_X?|gDz7I;TyAV z=36i;6TDaYW=nc|e#`vgk6#%V7`_x|$wfSwMxw#kvKIzx8o4qMFP2IRNwLQKYSH8x zTP+eVl^R_wT3hyFUtr|=@|SxujeD4#JFTU>T6DD7(8!h7Sn_jcff3u8O_0HAkxb+5 zWamy}=?Jk*e(rRJ@e;-EjI7;EzH(>A?yZa^zjiAyVk@%+8spiF5^dRQgef#~jc~LS zL9rdg!f%Mf%3^&&vlL%f$FqeNDA|^ui;QV(E^@qBYIH7wtuPn08L__Egu}Qgqhwn? zculFX=ykl**6gNME|t3rFdZIQ$2nRoGBy_>V_`08Gh$t(xyYH?`iQptTm;M5Tx4mf zois~{C5qkIp*MxO2z80hMc{?GsLhCV%_bZsZZk@><>w+48k>tOEqOLN7hydstWeqv zUsuPog%&8$R)|}e5)-qOO3gxAwB_R#g~sC6(o&JpxP>L(pE)7cl)wMzwuZJYtmQ0R zwD@&yfr(ji%_P$frLxx+YZ|$>5HFS*yS5lx{@PMt#I|M^kYJ3GX}qm;1>sC1vysi3 zpN@)@SYv*~7}MB@Azt!ubi^22cEq#^v9|oG(;3FS%g*%1u!C=9*T!aglQBC++l1Ig zevCT9NEd~t7jEoG{`+9l@tC_kd3$!;>(XsVD zWL{}jH`GU4?QU*WIFh%QjoVCMIksW&n2~lB-N&L%e|5Cg96rWIAL)4_PZ#GJ_70B0 zqFQgXi*)miIrNYrOZH>6TD4j2XU$7oXx%s5s8vQ=J&pdJX4b2wU|$|uk%ze=OH^oE zZ}iuj)xH*IT;@dURg*i5cN4P9JacI-*9_ZtYe0j;Lr@z`{&3vM4 z*Vu@l-qg+8mdMb4ba-%cHmXeDy3e6jYoJ-#45!+ny11ewjWDNpE*u-?aBQ_X+^RN4 zv+;vU=ORyc0Aa-SMM++=@?B0N&Y1axXICHU8_LFhzxz<+tAjlwqZo*z^+sRjZ(rtV zkGASVJ-D#-4d<60Oauj)r@gh>hpQktq)ML{jS70$*Rm2pv(nd{T~H`bqiv;<1%AO; z&L6$d=UuO|+?u8QjfBg46!U+JseM}>wPIS5TZ-*zYm{5%bAH()!_8J@OMSF|cqsQp zQ6_3vTEmqZE})!M&ULhI^bAFFuzWNZT%ziY4N@(L^>e^fYA5Y_yU7JnQMU%g;pS+i z-e_fu3Qr{IRz`<<1_vu6&H7L^8~Q~_ zJng~Zk-R?ke~%q*Zm0&)Q1fJ-Hd@zc6&DA!g2dBoRpex8 zr|nPnt_F2q=;_-iAhl9N)Qu#2%z-|5)2ZIsEI}*?DdB7%WkT z{3)+K#88Z_H&2og$wci+tvNhYoGCrK0=o-26lO~ym(qO6=y@}y%zO1|;DUxlQ?;i* zkC~##(;ap5Ue*uUM_*@CGrP9eN6r5pOC@32t>;>vtf+nTFm5AGBQv`q!&sK?4#=(`FIfdQg4QCJdD_)xqdJHn$|u;31qq{RE=$AN zs)uw*=4oU8DBTup^o5~*`98uJ6s4X8mMJBVda^*fbi1(;%dd70MeU)Ujro=dMQvrY zr^DbLyXCJ_LMwbRh@xA>fy;Se@0Re~$SO@Ywh2 zZ7bMMWsNK@Q3N#nRk)MoZm}ApVJTycQCzn2JssHO60fvsi|b1VD$Fs6_8+GB_( zJ^PJ4BXzSSm$fMW>}z={_CuMkFL89Ib@c94M6(nA+L{%aJ#}(qLGE#!`Md5Zc|Lg?lY@ZxZ}(H~$b-dZ!6Ii^OSi@|o3i=nqOuB12g~+8e_m|c*pRHf zH&arX%v{-K3bUmrlyb*P%3p!!wL>ullC?Ku2f<~xt3%ItoM?^ZMPDR?BE2hlFiy7P zyIqRtY0_)PYm?JKZLnR`pRWWrntLI9^mWXBZN8Z@w2?k?I+dC2%ePZA5>_++-Yv_p z0tu^`e+QRq2H(Wx@>!+QAz|~JUDAYv)tpn(goM?s4`b)JJ&#fQNgA=p-$j+T!`VR2 zS~Z8a4D}pc>X1q<53HK?4cPd|qZ~bKKYLok{bPgKj^2>_P~;o!@U;Ji9PT^%$M8i{ zzCFYP1EO-WYZad$!=-CDUsQih9_=tMv3|y_9`oG_uPYsraiQ zBkUVFhKkBQg}Ds*1=)|BPO&JXR4!|{h5fohY3_5Fm-%X>pkzN5bw=ua8xeG*l6~?t z%p$tzMY{}kf|6eLZKk~0pK_o8jN`CQgAj|0tYe_c?_KcIM9_NgUTzwAk)!b43=9;lm8yvLL+?dCuhC$@!7 z!e}yn&-$XQP4{c~3KeVI{%ZD}s%uk;WbC6)Sy48EPB}O3S*l_uaeSCdr5x7nEEuw6 z?>{P7u=Rr>=m!Kg~9);36IRhUOL z3r&}m`236;N)IvGdhODf$e%VWR3VpnB~}r#GiTx{RI{6iS+o?=vVxz_2HMZWHm-}& z>`N((kuufEc63t~qKq|2-rj7z`WuAp!i$_}4Ig;hrJi}4^zDbqg?6ori*$W!*@-b7 zCbzDvN5ToFYBMU+L~87ZR)s=^K$;>RI5qHnZ3@EB}Gv;f>j9XtX2` zt;~1PgLsfB+b=K{ZJDR-zD3EFrOtaaz%n7D9|RH^_8WB? ztwu8!dAcJ#TN;(Y;ofEAX#z>ZMC4}243!1~C4x|Z6U37HbZ zdkC^m|u*1(0c)N@c>?AXiGgfwH@@MS&?lM7sZM%yfxL-s?%%;s3m%Xrqju`zq(f;36y%qESu`Hhd47SYr*`EE4TAF&N{n{u@~Pg8$rp-4?k_lzZNjR^b7XF zTEoM=_3UQ{^ue>oJ&OHNSLUbuGpj|vJfxwX>5?>Jfji!?0B2+f-_CvunqLpIl6 z;G1S)5agJPxtudHETJv;tT4gkn)d$^>(PEa>0)FM^o3rI z9>p8@CD1Qayx;9)c05-skJX5pv!%OV`2=>EkSUKS-XJKU@I#th-;;Uy7a3OF&)t6E zR+v89pNqNX(k#btEea7F$Li9P2Y%wqrT5YTt|7T*c?OZ>QQKTfMKa7#sCq%Ms zlKZAGY3Cj-&D9=LDxf?wd-_b@7z?p)h-8bA`^H!?eUpjVz9Et=$@ZCO#q^EIOzayX z*^>CaF;5)XCtr%Ay)7)&ET_CO3R2m^1P)t@CE|9nArskoZHznE5-lN@XlU6`DzUKM zvw>-=QCji&RJjzl66EXFUc;1HRiHZAmdk7#OKodwu)P@2ooZn6h}FXJopND&w_TG+ zANxwNnz%?P)g*sXsn`!~p{(zfl^ZZ#&$-awJ|UM>^08+aD>3wIZpeGoqxts5ycmue zn;k!Da;jX4Flxfg`Fx$R+nlh&Gm=|yl`)#^6^00}rn#)tYlaD~Dw#;qE#=H#NlZ4u zRU{L!Ioqp5;0pfA;XOX))h1v{<@kKWG>Fpti>TbBAn{6R%NbQaSMPhhOgj)3^Y$s; zprT$s)&J|7JAYR9ym_LyI8nF#B~df~ny3qb zy&p}~set}lqDFx2K<%+a-3cuFd!nubYX3;oJ-~`56LkYH_n(Qn1n7P|Q5OSKo&t}2J}9asJnn=|3uPeBwAepOw(Fj0PF*uryy_c zuGOPJ?b%x053JtRMp#OSQTj zSpQ<=53Jc+t2==e(Aj?U3bjtF$@|7(WfZ0v7JC)fK?B*J^bRFkyyPCj#SMtAV2ao zTHOL1{W|ypSapC_HvtE|UaQN1eP^OCfN@>u17JH)>xQ3z6|=Ou0hl`*{Q^vxgSrB{ z&(-Q!;4z^0K&|cqmd(@ZI-q;LRu=)257O!^VB#B4Z(#QYs3))!*!o7To&*{T;Zvah zP3SjZ?ZLh%sd=<1JhR`Z(z#X(Qm+{Rp7 z2~exic3=lEas=WMs2zzifOYRg8NkXls6VjeU0Pia9JCf?00+DqF$e6sPOI~Pz25_W z0^^TDoB^tWyn!cy#(J$D2Ksx@Z@}7K#1^o!5A_F@^doOzK~<}(fSEP)5io6oR_6ng z2jG8TVjaE#b~_rr0iFVy8{r#ZU{I?Efun}d-@xjIR<{Goh7lvc!6RB-1I*rpI0yD` zBBp^Uqli6VQVYHT_83Fo0?zP}VAZMUb71jDQ8(bRbJ2I70|%@;5A_BXejafE?0-J`2-xcj z$QRHTz;D1~K>rtE1FXCdeE=-@62?2w_!Qaz9JLKG2CVorcwph#=zrjV&tTjEQ_ewK zfC-;POaVKA&8W-ti?liynD}M*1$YV=ycqrkR(}Qc0~TBYe**h`73~JbUy3;ecoG=+ z8e#=le;Mir9Qk#`3b6cg)B{-b4YV7WcLnMT9Pmx_FR_ifd6nVA1!`Z@|3kPzEsL`)D&T^?Jl3FzE+~ zC1BhQXd|!_*!n}{4Gi80{{icNgm?tj+=N&FR{R)w1B-7)-oQaWK_3A#Z$WGT(|(G+ z0`|ETeg*dY8Tt&^<2J+!uoKw&bNC2o+>V$4`hNkx0&DL;UjZwBiP!*^+=Qg0zeD|j75Ab3z@p#7 z|G>Qa;TvGaAK)8c>I3jQu*V;f7w|aH{~)e)!16z#KEUjUa4i7#|1;tqnEEjM3GDS3 z_!Aia2x19Pf5rR+>;Og{MSXz&zo92@4KER^K;ZI=R6YwW+z;?71*zX_k zCouU*)C-vKPs9VT+YXE^;3=T_FN}X+;3>?jz`B3KkHD&@;YZ-m|KPd+EO-Wf1ZMsh zc>~jSB5z=y|3hB^d#Y5O2JDff>O^2CuvMpOJ2050>LFnLZmGHlSpBS2-3~0$#~q0~j|xRVM*Y1I_29SZ^SY38}gYnD+cs zZ3Fh0n5yG|$AR7#r0Q6z}_!LIlvx! zqYZ$1NvcMG?IxX^st18}FHO~5z~X&Ubu}>KWyk{;?=3T5tuhURTlz#za~|u1H11J z9{`U7{jWv-z>*ng7clj8=mTJn1KI9(n zR@5I@b13SF&x1?QAHW^>ylg4*2j(t=KY_j9hW-S`FHhB}z)qlb80ri(R;21NVB~Q4 z5LmwwZ2%U%9X_r~{ z2lru&01Nt&0yC=^Bf!*Js?Gu?Y(U=w;|5Z7Jg@_3)DfG5s5{cNMEIsbQZ=4F#A)m z1*UI9`~tgw8gT^d00z&7KY$}Y10MtP&O!db)X&1Vz;5Tlm%u~7y3Zk=fJ4tizQFX) zqs_p?^U-ENeF5zN9tZj_z?=cB`Xbs6EV~f-153UHe**_y1iu1Pz6>7#`eN7uPXf)a zU>pKRU4rs}L%)i#aT&@)I^$A|bzsuhP$uvU@Bp}JSD+1FPt|R}lFQ*UVD2~2SHQX8 z?*A6*`AygW>PnOaJPr&%e&kiCBe3Y(h%aF7)u4AgO0}PQm3pn3pn4pmFlGW9mKTpgxXsKeDt^>(#N zy+gfAtyS+<>(qPHYITGS(o5^{PJAuketv8dO87p@!9n z+N7FlRJGKY+N`#yt?C$cta`6HPQ6bZuimdtP#;jIsE?>q)kkstK3#oWov1#jPEsFI zC#w&ukEt`%C)AnhljDYZ>~TAi&vqs~#ERp+YDsq@t5)%ofR>H_seb)ou_x=4Ll zU97&ME>T}qm#VL+%hcD^igBc&(v+|=jwL#3w4M3rMgr7O5LS?t?pL8QTM3ds(aP% z)P3sr>VEYH^?>@LdQkmIJ*57u9#(%*kEp+@N7didW9skfarK1SuKuB(RR2^v)W6hI z>fh>V^&j<&`mfrl{tum>Bsxix-I8Y|yC=_1_DG(Sj7y%Ij8C4IOh}%eOiW&o?3uhU znUuUJ*(-T*vUl>5WODM-WS`_^$&}>f$<*W($-c=ell_ucCDW2uC)1PHB>N|?O=cvo zOAbh0pUh0UlI~<7AJ=!OOm%H z>yq~*M-crus_ zC5>b_8A&!J&15ubC1c6vWJ|I&IVL$ad2e!D^1kHwIlYBNgH~Cz0Uh?_m{NxMC1<4na3zIJ;7bRa#E>6CZT#|e>xitA&a#`~AKTd8=ev;gh z{4}{W`B`#X^7G{OufsOB-4r3I@P=BXX)Mbv-KYOIeMIat{$(Srzhy=>xudWdQbgAJxRYv@1CR4>zS)64Z?dWAk*uhehX ztMohcYJG%0QomEL(eKi0^}F>t{T_XkuITl;NB8PJ-LI>4qNG zBYKl=>QOzWTY9tJqPOZ}^s)NA`Z)bQeY}3ZK0$v#pQt~mPtqUKC+iREQ}jplsrsY( zH2pDsy8gI6Lw`b_sXwXD(x1}X^r!XN`ZM|*{aJmk{+vEfe_o%jzkplnFX{{Rm-I#Y z%lcyd6@7{Rs=iczO<$(Jt}oZ$&{ybh>MQlP^i}%X`fB|heU1LEzE*!vU#GvXuh&1& zH|QVg8}*O$P5Ng2V|}asiM~buRR2uhrhl$)*T2wr=wIqP^{@0@`q%ny{TuyTeJ^g; zey8u#zt{KcKj;VaAN7O!Vf`2Vi2jp)NdH;?RX?i#rXSOP*N^Kb^mhFZ{iOb<-l6}c zpVI%~u~#H$5<&m(EXTr3a-8(l?}UOc$naN)JxooGwb=k}ggUNtdK= zO%F|%rpwZ|rOVU9(iQ39>B{u&>8kV{>FV@|^vLv`>6-Li>Du(&>ALhi=}~DVU7z-( zy=h*>+y#&j?pN*n2LI+AWmo9SrUO2^X8>6UbBdQ5t3`rh=o^nK~^ z>HE_Y(hsC3rXNgCNFLMQGty6_XQrP_&q_a)Zc9I% zo}GRsJtzHadT#o;^t|-*>G|mw(hJfrrWdAPN-s*koL-!MCA}p5YI*?j` zH_|K8Z>CqK-%77aznxy4ekZ*q{cd_~`n~kJ^!w=#(i_qrrZ=WPN^eT9Pk)^LB)uj5 zX?kn=v-Gy~=Je<3FVZ{GU#54aze?{)Z%==n-kts?y(j%`dT;u>^uF}>>HX;+(g)H% zrVplnN*_x9oIafXC4D6QYx-#VxAgDnuvTh~>-6O7u{y`(ciF?t^`oJbs>P-xCv<;0KWS={tTm=^kOAoN(|()BG&5 zQtPp2uN6uBVyPcL^z#h*2C97!Q0bD&k=;-cGZWo|12v_pnfS%?{S_BnZ4by^t4L0(h=oW!iUYi*;t!Mv z(f#vz3%tW35>$?r9OqamejF>bz>z{_4Jn3;BZW?Oq~K3W#tdWa6g%AJ8fj%ER-1?e z@Pi6@u$w}yLafSGA=W!qAyReA$Ez6aCfJK(n_3`_SW`aHw6(hJsZbrV>bBP|d8DJ#(W8>hYimqkS*2a56`hP6uT^#k zatEzlXx}b1FLIW6yLs7R#qm1Hrw_Mtq9cgo8OeuJw=;^ILfux9&k8Q;M0>6lomRj` z#uV7dgaTF`Pax&tQj5(aAmv#Aq&&gaq{RTj9GtKqv1F{7u~tT=j9SbA7Cve59u=AL%T^cQEhJ{%CNT?BlJE^_{Vf`=;Y`Uqc?9r+d9r2CpyMOXGYYX;(D5BI zh|Yp0hxjaLH6l)Me1|&3WogV;7# zqsu6CXhM7zG#SKuF**yvJUR1eoGuBcK~0jxW{4 zRS@x!>YEH={pa`&8N@okWDxBEqY(3;n8840MUAlCsw3nLGe8}cB zABH*22V+LZ^A@&6bK`i?{D{hogGnOC08Ov{^8aCF+ z(w4cilY7YA^RpW|&QNy0w5#syWZz0V>((w(&qx?a`|CE#NDCs-BTU#f$4J}cpurVM zcC#EV!fbErAqGkLo3$crDyAoyVef*-)GuHG@+dC8jxxq`S>bQ@+PXwAo5+3Q;?VJ8Q-A>@$X9+T0_zy_WCby-xif*~PKh$98d?LcH~nT{LSRodnDmE%DBa zP65$g|ATkQEC~|y<8pPtrWDwcz|B}Sn`6XKT=lR;!Mvvj-x(Mt*J%( zBzGFf9mcVtID_rNk)2TOXeVI!po`E+r^EIt6r^0b*auV}j@~{_$-`bnrMn z;(aK@Lf6u9^JwV8J|4PIh{c|zl>BGB>i45AT; z4B`=J6k=WA_zpFQM4+*cMIiWS1Uf!mek=lEAB{lp9hx7FK$u4((DCu=$088+(Fnw) zAR0i9kCYFEsQ(=w&miW1*hl>jKGuFq%cal42#t+E;yYvz8G$x~*h0%F#6}?T@fyTN zpv@o_gGM3Ng~Z2e5a~i=AL~NHM~0B2*E~`hAs=V}j%A`;4U0S1+sK{Y{K;q^I5##C$HsfX}K`def7u zjFpGS)c3V2JtHH7^}e3o!Kz<@R%JuAQEk@Ed!;tuMN!qDf;o>oMU}A)gU5_``4*2) z_X+%{ymxR6$Ddd04L(EjyK|IQt2V3svPR!ntneS`)H$#LcP&nZ&u+L zw%MLWtJ%{S9kH*%;#_2O8^a@VM$PU4!Mnz^+0$QbMvQwJ8_}9g-LlH)@Ze^@S53aH zfo5ehYHqE&Wb0kgIkwsyZdDtjeoqdd@^aG*8);g7sBg#*nd)HA$S8VzwBG3Rn}cAf z5A|SF_YDVwl#Z?U>Qy+(UXoXEnxe?zJ8+i6IeV-;KYcRJie={Z@Mc(Y;ZX7*_X1OC z4OeQI4mxWzdWJe^jka*s-i9D23;;V&>i+xTYLE^$M=_LI{$y7qDx*VqpdnzsrN6$pJ{mISXmww=c=Wl?pwLFk&i1~%S@pfJq1vkSHOKnR zWg~EqIrJ4v`HjP)1J$Mz0>A71$V%^%v5U4;h}P{ z({c24JbbQG3U@}23HW;9FnGTzqb`mE;ihKGmtg9x_Vk;9QTLgnhjGno^&l!egNUax?t4fbsC2hmXf zoTV#P`+EIzda*9>10H(SFu(peVaG!YtJU5xJIH*;JH1j(#u}sb4UKA0?P|}cS>(FQ zpm{53X;K^7*fUZ$C*%4BZxlq8pa8394jE_6jd%+uukpEeaGUfukNX4sWqdrw7Z+ z$6635iOnEAxP)~F){gzb4876ZHUw+9Are+*hMn$|aRPVXXp#qa-+4Z>1pV29e2yTm z5BJxH!T~^KJ1%(mGOs~%c*{`F(dBkF>l?60^IJ3AKQ`#Emm8tOjR4oh{xRI#2J=y) zx)n=WvzqpDZH!@&-LnN(O;3!)a;2x=Z!XFF>B0!Hflx9Y;;fCZY(Ct=61kLtxpnk} zBlW(GaI-JNwTP*`J-j{JNwBk1BY5?-(+I9anJ|o_Tpq+xE)U`;mxp(YMM5kDMmPh$ z+)Oi@7)~lIya~(;GlMu786GDUX6(4a=+*}HDOa-1m2O*YnFXG$FY)0mbI~@x?G;PU zHKKKBYl$@}U2pSx*twnUUef~0%DIZItK@a!+t%jgmKAi^8rbzL+jkkm=bf{1u57#P z1Vdx7+$z=tLN;KHoC{VX=K^(0y){s2&`njTY^(t)Y`uoJfEgMNh9JKskgf69jA$jo zac+mJHHaDvR$Knun{#HP!>e}A)Enl_{XU02?e4Js>8|>8LO{j8yg07jK#2EY)4^Vn z{Y#qD5O*vcJ<=0Y*;dPJC(h(=*qm9m-ze->k`nTYWnz~TgE4 zLU(05)M<9w5H(u6&_UR&ls2es4*h0>l-p0XY;o}{xa&lWxmYx-qrOW5&h%Of-;fTE32MRpL2f>B3|iY)9r3SZMVhWh zo!^uzaqF$ zx-X8)-`3|A_EClYa0dWBG&=|U@ZVDJ?;or(H_R`O~JMm&`+a<_tZtI9G_f^8$6u%S?JW!;3 zvzqxznG*%F;`huk%ID}SNAH`@UC>}v==)8tW&T&1fmz{;%n0LCc5kE<>$wzjh*y4* z5Ewin#hZW-m|2nu2*E@UmjW`cuwlg35E;o-Y?U#UTV`0Xbwox=6{ym9$y;d{v6Vzd zG8J2COyyP@R%|7aky05d-;XPYQclIpjB6vNhK}*3&JlA%OMGcMwtZtOU7nEJTzjSy z%55u1CAT#qt@o8WXBF{8J1fB-`Q~G}Njb&TAm{jIcy!F1uX=7Ct2Ez<)>P<3-oi-P z;Yb8ieZ#QBkO;wq9e&1ypF$180w@~?17nw@!SPeTo4cxZhdH7+|# zgib*973u6#MmUEt$1n2AnzI=hXI!oi9<49gniuwjWtdm>lx1_vGR71?JS>^wl{_Wc zEJnm+S8gnRb6CoZsT#iVEvX$?7BNnmA8KLW&z(COWa#AO20MAV@0`3;FDEWl#E6*= zbK;VxoVer%E9RB?dgGS`T=VfFB&P9r85JjGn}U_8{k-m*k#9Aue^&EcJFG82oJTABZfhIhB6?USn_?|yI{q2uQ6Cpe-x z{RT_o=?6FwI$CA1AVbC(m%^8u@wn*@vT}aRFvT<%PEp*pqug((x$Nlo z{~X0llP)`UinJ7aNy!+=5%d#vrVe04Bgy0Jg~wKbz2;<$RF2q5avzCZB=?ZmL9zqr z?I4WTz(}nT`&8~y_r^b_7u2`zXc_lbKPE4FonOe{r~AUJ;272chT>-h z$B0J4tYEC9S;2B_j5gmqSP~y}#z=@dL-7&h7^x&-R)7&0(MYVG zG%J|=B##TT0!)Ni!7)g?NbDkEw4wr{2ju<{W(8y>%?gH+ye7;FFyUtfOGrNB zX9buDvjP}?RjrhQ^6x*Z3h$XSUwnn_O4aJWiVx*G9Cdn=0 zhk_{wj8u;31wO7#ZhVjuBb6gMPWBrcai$nP;tJ>81(%!RNp~E@`xpF%<7O z$B0I}7mShM22euyI7TW-Y?9m}A$(Ad6h5FtF9_iS6Cr#YBeh3#oa{F~d{B%KK6s@V ze|hY73|lo&g5Mk?8S#FDjo>#hg5Mk?mBTv$Hmnoy{Cs#@3Q>5w2~l`!2~l`!2~l`! z2~l{@XwOUIf|UrSU6$eeM}%a;U5>2@dpWiY=RG3WQV?8xTPz`&@NIz!8w>V~H@+<} z5!wQVZ;K_Eis4C!?5s9_#>|N@P;w#+gq#S&AtR)mO4}j1fmB2oshL4(cO)aiIL_o! z0a6o6J0@91;kHR8563|!4>wSZyu5AYq~XSulXBiP_c_5Xms92(R^HWex|~nSJ6l$l ztpnSg<^3(20b4J&7fVx-wP)+fbslC_&M{oKmF6y664PPkV64(kBb2S2agCKTHMVl5 z(nc<1!}-{%3)wS%HtL+Gjhyk75eu0MRp&fza$$UK#7y8CG3R&Q>*XC@(--AEUXpU< zl{bH#GUt!-2C&oRyuo&3`FjPML1`n{O1Tc?yN~TWw(CmQZCeuCZEUBpT~_Ka96nVV zBt{lSwvmMaW@Mp1A!AHS1Hz~=*@b?DT4@kK7Irv9OlC~gO5THI81N8hFEPBKO`;&w}%5xZUDC~i@cF=9#Fvc?&)>l#OKw+0y_mc(sNNC{irn&22g%fNLY$FMM#()iDF$~IDP~6(J^94 zd?*+rAr=h9_p4*XlK7rAM#A)HD86qUBbLPXt}zm(N8GVWy=xi1Bv3-$tuX?ir2SUqWaG)C;SWeKTr!t@9mX?ir2RE{((#8DyagxK?fL=VXQ zBTSE|vNSy!N@|TTJ;Fp7MDiFChmbfuq7-2OiQ@-*e?ovdhK&JGeArk*G7-WCHd5Gt z;%7!nNagSo4@}riPVtF=9#Fn6ftPD#b8-ND(8I!-kZx;a5Y35u3wL zE5?MMR)~?R#)hu35keOOUWf$8u;mzvj|9g^MnWXONQwkQ@vd`>SP~lv#)gjsFhV3a zMkuzJiyW22qm^wl#h3z3@ z!be=;M4jN0MGRXG0=SFsA;(BYLJz@6n0pGRhT?wlImCOf;P zsOR2;Q<9js-;l6XJn|rym0pJzGVX=Bbbw7xx^rpV$Ku;%s4(;M5auiz(l=!36q+{Y zzuvDc7piK?D!;O?ZI*NG7XxK)Co0s|duwCc*nb~eLGB;GQY1=m@N1Ly#b$*y zej^`P=WjfaPEjZ&(QeOg$%Ms|TS#WlY@z>)Z_&vLD!c)Yw9J_l?%&{S783fWvb2Ty zK`l;XPh!dbEuO&=$_i7kd--DF+!fNb$FT^5+n%-J^!!s+NanqyiW=rlRv`s*2uF5T zYEM%kxj9EA``7lk_Z}giqO(&x_3%U#&Z2#$N+E#!*9zGaRlE@L&Pbsmv(r(skLFwy z`agSRm^~52$qHwoI6-#KN&AJCzI?QB{)xvF-lJNO2eW7Kv=h>D=bz9aA|$gf=L}6< zNQ6?1-cnV~TQmYa@gx&hqF20kdP&ANjtQdLJFz6wqO(fqqdTP}|KQFi$v>KRMCKpN znI!oKdm2gcQ#L_mCy?Y^e)b4S_)|xS%+DLiKAYFvWTnz^BEiReqQ)^2=I!v9xwrT# z=0uTVeihSqCCNY_GB1Pmm5e0&?${M(j+MxBrqe=#&-R>k= z+oLz~3edY&aRP_0fHOUUx2xnwZlG7QSLg;dZKl2M%+0(YqGIj%4@-8F0!?foX?sK{ z-g?(e9(0ICP`iL;sz@67DI9qQ?iHWeNB2_Ayk_){O`N&mE0|Zb2a^0=AV1#%uVoK( zaJEJu!KoU7gwE4orTHTfc^UZ`8o555oAB)QF?29&UPrYy-v-2+KQ~RWgf*`v$$OwZ{MSD>{UxXK3LKZnEl-1^$61ke&+lj#arYtMx@?h%RJ`W45Q1%k@V&wrrL z`P;}cGk0arU$L91(Ipvc_U7<0q?@1EK<7L--CSxLO5@y=c-$c5WGTT;G3!+66U9%xTmC}bw|jy*l0aEM01f;m^Az=jSN z&r&GZ$fqcTO2PRF1v=QiiJX~0M)^qxh0_vb3g;w*q*;Uct0VZlxE2e^>`VbWGY6+4 zWSLu?@_7hFnm-Alq`{qmP*mdME~{ht+yiG@Jn?`e?u5Hwf-0YKK-%8<1|*{c?7X>* z92tZ$NzG{nZQJ}D0}{1QFvtYqq-r(^aE@;F;(n**&MTn*?Rju!lec{rN|Qu>MuDf9 zNsFfw1PVBppd`soBq$TvSp;RqoI>D9gJ!s@lQ3KUQRPWG+t0wAHsIx%P3Y}&23XoT za@5Z*vn4X9LUyWvmtQ#ZW)m__67cm3X9$#t?DT*V;VRMnUsGEzRQ&50>kXh z_nrj_b5-wc_oP{rWs3PdDK$L*Y+lUl?24k2%)E>_P_u7B4`k*A%wB?< z3wc%J5?0+1JY(v^ni!(Y6s8#KM_ZxdB-56*$95_IHdQX6&9SZV>6kg&vbK4tefcIr zvAVg?D=C+TRrCnnB&=3$!dEDCxK%GXJk&CugU}|CS1BV4LYhP+wIGy@8bl>EPvGr_ zM3|h)m+Swpx3d6~tH|1L6JUYh?l8E8kkKUU0>KF`!KLX;_e`4fbWgf_BETYxySux) zJM020I4d|T3#{O#={T-{SbF1ny-x>Z^p4@fsop(-7*%v<&-TGb$>TDoy%>~+BLDgZNh|(iES+n zzs3!H)j4kB(4P(+oj!5i#F&%6NH0B4BC%j%@YmRMX6iui+ItOsnb_9-;q>7zhJK%v zSYoTO*>tL$TDxudua=|8hfisa>+&#X_mjQ*K#*&kb63p1kW$ zLx11!+am5O|6%+;iNt)BKlu+af8|dRQeu){0=dt8Lw25d=FYAEuiTF({D&Ns7?wUM zk*Iv^X~W;Ch#X`M{Y;1d8TTJ0sz1|N`;Pzq+Wucdzx@9_k=ShLEC1W5!~yQ#$>ii+ zcHeE!DamcN-D~STC+|LGw>^_}pWFR?B`C-b;lLSP_rLf~k<-@MQ5QL*Dv|R<c8tEr+xUFK5}ju{$l8NA34Dy;=A~GRrf(eyQ(cWvEFinIrpIh3_6^* z9LTxOVPpB6^*LAnYf@s;aL#oMf8YPyTf<)r{qBD*h=sq4b=Pno!nUe)4i~#aM<)_K zanmRI%C=TT?R-OP#8fWZoA1ly%gI7dv~VWW`Ow+LGkr?W;|k5-`Z1NOe@8|na-tt% zoU?i19#2Bmo>u<-&l=FodqqPWP{Bigug z0w25UIvXz-rB!ztL@4@<0a?0GjoR>@n2^lTl<79(Pl)P@em$~v$Me=*$60q_!W??f zO;o79Isob-EY%(K3QAp+r*?*=F2+*bVYTM9g7{sWrFIZ#a(XX4b?b0lh22tcQG{szoWrp?VvMjSJYRl-?BkKfX=yDvBnpR%{ zK@2U=GQ-Bu3M|vWpTM>&a?JLq`~DwuB4FEE=?VMOabn?64xS@0e3x~%T3Kp z^{y{*>5yZ}-9X~fG}3dyihM(XOXXTd^F|VtPMY*;K~#+qxJ>fuxxmUB3tXDKYAXZk zSb<7I%96T?K&1skOYIW6q8qvTIF6^f{VeQIr1maiw*odZ9GSA)0NWy}yQE}&lGLz71GO+gdWim}?y|n@P2Qrxkm??QPnM@0bo-FjuH*vAOxk#?w z+Y@oFzO&LL%nrsm{uY>DIldqL0Fkdp?9_Du{gV)X@NT>a&Q|}Hm>*pKR){}%!B0^A zTVsB3{o4Tk&|O7A^>2&$Hu{B5*jSf;+aZ4C_O__}$k$C}-}aEsiZ`u(I{^OB{ZSFQ zw%I=!^H~E#KG4Ya2j1Ti@CUELiqN8Mypidk_U;7fta!cmf%=^RpD%q8Yc!>P7f27U zepkRByr3+i*3|dALAsH8&@tYf<-1pP2ksc}A#s@{dc^)CM;p+)r^Gdw&7+R-UJ}<} zE|0iVB(A}H8gcjLx!kf`UCR)6ABk(QLPgwtC9c7W3GLz9PvBCy+AKn9_m`-2F>dPJ z0|YLUyn3#%I`cc_4ivaFdDT`1)Pn>n4Jk|N!2*>Q3@sIPZ4cq8v2=`+fXxg?>c~R6 z4+U(Cs79T{6ksOs0iIPX?>@oJfwRW88=N z?9x#kZ9sn>@nh&17cf7#{(i*g+a~(>6K040iNOra53aum_;GZMOPFt?A9T&ih#$OT zJOJseczyJN?4JqvadeDlVLodBb@c<@pAGooJH~S$ofWV5K2U!+;Pa(VZF>-VM?iXb z^+y7J_>S=pA>BrOr(k`5uI(tw=i9~Vh(pw)S*peA98r(qsTL;zfQ64`sm$U*Z($*) z{psl+u~fQn&}K3^v3(rNq~^6`9?vrAV!@Jm0>iZEHz3=d$TI1&+fwIAEHkV>PiC2P z*{RkE&UT!_F{x=y^Yc`e*?cbp#n5Ri)4-p=wx@HIi|t6HdKc$Ak2Jg z=<)MHmf0HJ{Sp0o#N7!<=S3{jpbmjtxtL|rf~(FIz`TTI8ssNnUdl3Q@w3!<8Ox-_ zPt64Rc{#_VA*8Pfz_wSg%&>ElD_N$2KY?wp;+Qm5Ol^BL%cKsqjG=2dCQVPhPGH+> zIVN?dDf2pxNz+r$1i5lO%cRD&^ydv6Gw|HxMvfVHZgLaHG?<&9r|UN}O!t!Ep_e^J zyr`ZnpuHovh-8~CAX0s+z;3m9@&V44-X@ZbR0HN|k*YK5#^t4zK!t zfj#(E^GGh4R`CZ!a(LAb3T&fic)-^WiRAFAALiM@riF;_N3Nql^&=uVyy`~q(w0Y|BuSL3}?Yl3CGudjn5DEwICT`WcDL zidNejX#O!z=7(y%wLy&iL?Q>*{47uA%b2O==OnU?W?=2-d9r(rY-;B0=xikVbyFkO zPen2-T5oR<-9Ho9tY|$M*!$-anH6oB(ZAry2B044twMMRIu6zZ2NuBm5(gY@`~jwmufv?7(F@dVC_0S<&i> z0!HiK^W+#hWq%OKtZ2Qxfv5i{u)}-$PZF6Gt@bp~{3%c7hibjGL5zJSk%MdgoG0^T z%vAFi64^#Gu=bZcIfhQzS0b4et+zLb?ym)Q_>BHWBD10`Gx}Se97Ct<&k{Mf=D+ad z7&>KtmB_(0|BWZd&?)=7M7Gfks@Oj$xhj&}z~-kWma6|NlEbV1pTG{ko}Md`!>dlrGyI0)?xA;|=6znru%4brBpay)vy)K* zJN)cqUWv?#R_7L|W%Kdm7*>|^i)2=`zJdc!e~)MLrA$u-_Fh0Dv!X3My&zAHVa2hK zNM=Rr?F~FVT40CI>4haSD_ZSopm`CV9K-B;QHgA$8RXSs3^|@>wu_5oRZJrWU%u6u1?JyNi{$XCml4?EXQIoB015>Jj{Cc3gnW<~2OIPml;Jex0NdNQ#0suGzMZRzRNcybIg(bYvV zD_U=F;OQ2D9X_YmkjSiPwWopRHF5;?f$t$1<_`MkA64z76{LN2y- zPn|q@GIv#`de((T$!%q_!Mqlcx0A^Rvpz)LUM3sN{kp(AOm`5;)Y#f}3sO8;=F+Pk zP2+1vnQY-@VC$VkGF?1b+InZ1OP#E@HBh{ZNTy3SwPL{CRpe3^TXJ_3xipQ{T+sR0 zUFK3ln|gQ;kxZ|IR4WD^-c#h#g{viZFOf^HhSYM=yLqRuTy8M7r0y+H4Q{R$-bUu% zkh_mSrNv6Ebz;lVuR}*C5_9J!66q7iocu+4JxASFqSAm;XAtu2ege1oF#%Ec7pSyc zsBH{9dw@V~zA7WF2MW~YYchJ@&p|wu2C->09V}4kq{`B-hX_;yzk+ugC3z}M98(J) zDp09o)fNU?Q#_TXt6nRhPUWf8t)|p&o=VeIPX)CpEl{b6E&ZC|sdVzB*9!dF!&5Cr zIza8^sdVC`){0iR(>Q7m-^kE<7ICv8xA{texQB^cnzojDr;A*gxLR%}n78F5E;X}h zT=j|E=Eqv(-@M3e-oMDs1&M1g-XgvIB9}VYGOlJwTsk1A;|kfiC~>KKO}QnBOVd}) zMgA>|Txw`b{|-o8i?J8ftC&L*v?yC@JzAjBlB}kJdHOLtl?JhT96)vASb^I7 zYzk3-Bv1|f3M_maPo;@tYT@GrDs`-7G@ZaxX}apQ0t=tWQ>j}`sVDJNnyz{(s7)se zRBB>Nzn;QVEoM)^ucz` zIm4VfAig9(9ZTp9i&w~Oo4uRh^w*UFo8OGBjb>!=t3)y@T5oZn`f7n4{@(a&L~?l5 z*9vTY6S}3ZuM^4PRbMZ#`EBTys&5d<;Z@%#u*08PzDXp9SADa<<}YWp^z|(wIlSsy z1vY=^z*6;XB00S3+XXg%L93pGss_v}H#Bj3@JpYQ42V zjQw092iN=yp3E<)O*OwDk%Mb~ktg#DYE#WGNn{(%;0EBA8FEideg9WvwvBF(S+5H0 z@GI)qL^3PdG|pZZ*x~#BzZA*gRsTw0^HVbQ$N{qJ*CIK*>fZ=#eo|(s`VEmBUiF&- zJAC)#Es-2v_1glQpQ2d$`W=xRUiG^Io1dgus(w!-hgbc+z~-kZma0Dx$>CN1mSe}# z_y15NhgbbOfgOI9@{vdmuli$w9e(8cL?nk-{d<8Oe&qUtNDi<1k32huzW<*@a(LCB z3heL^{+UQNQVn`Xp9}2py`wKAGAmj=W`cF*mpnO!zW-MunH8;HX97=uEwICT`WuPN zidK6XX#SQb^NUQqwLy&iSt1A5{1=|gmoZb#f0f8Knt`?d#*<^{`~O`ev!eC(2GRWw zfgL`h|0$7K(UuwgFP_XVs`b_eG4^kX99;8%crw4FHr4!Ji5y(>|9CRLpf=S!S0dYJ z1`(DR6@4Rexiy}*`phGeSL}o>+?G0+#sysP{+52iDnH8Es|N$dV2#;ufwxrSf{Nk zky+8|=mwr%k0HBv-o-E{TVEuzqV=8zsy7hW;pb!t4hY!iVUeoi({BD12^_6Awq%9CT5leLLtR=@=`9TJ%p zt@bp~+{u&ql-64t#8{U^w$Tj8<9TunbFv8{nH8_Ub_~^fl0;@jtK%DJ-jXNBkkea9WE;&O zy0@m}_IP^V+lXXVv}sOnE3m__v$hk-;Z<+Xvtw9i?I4oFtDY>d`AM0&o`7}Mjv_g{ z>YaFY45QZ0BH2hasNlQs>=;I^T_rLrS{-FT^KLwu4{y^dy1PUUu6Ylh9K%Sxr$n~V z3`Xj`7;-!v(J3OC6>S=0dkgIFBlSKaIlSt9d3FpV^?o8byz2c0HebrrH58202Z-eG zst@GZF^tp)iDVzPOfpR8U&gL?u!xNEUO6Yw&u4U-gp`#OtxpNbV^oe6m{vy48!u_d^n(xW> z4iq!V{#+`b&lCwb@Rjvg2mzxTP{dsWo;8Z(~}; zv3hE({yD8_0#oY(tloN7Tl1_=p)#!QbWLkyCA+gkn%a(tmyPlY20USWUBl@+EW+zg zWoc0A6@tC1(-|UXR&B(fJ=ZywNlmL}cJNuhrn78cgy}1dL97b-zK$d2IpWlT9)k!1 z{w&m}}))PAv+Vx zaDDE8YE-PJ5A^2d^b<4dd4k%toZ3>Am(G;R#loD*xYaxpS-4zd_LP%Ta|4c?$>t3$ zjNV`~&`2NX>B$r`X&J7L>fwRAh0YUZHn6)3`JQB{oXV$D#pde<+Uq{6fjeS4sUr;8 zdv*gmn@?wpneMWky~hg`y30lP9G+b+rt+o!LMdbSDbSnG4{!Js`9i;p)mt7!+Yt@i zV%s#!3M$mF4V=}FY~-fWnWCj)p_=*H{lf@3KRwx-nbBrQ@2t+!8?(E`(T{4NmkPO= z&5x1e1^>1=-q8)b^0Z=drVH$fo{xm172 z4a}u%zPtH6LTiT;Ys^wP+m~`pgYE*eoYrPerw~W0Ih`lfn6oq8u1`V2R!tMeLBCQt zIZ8{WduY2rO%zt}>pJ2o95LD5MwgzJ)Kgh%v8_9(bx$MIqJhpXA$oijI-L+pZdFz` z@hY0jogs5ge2ds;0=9{VK~*}dj@@48L__&*quQav|B{8V${}h3p;Y z;_DpD&Zqhe_=3Ityy9G$?-r@qd~eh0(Y)fkI=WjbR2C~)>TLB~9Wl?ZGJ71bP%I_0 z`7&)^shM5C%NMZBWT`Ke%O(4Z*}e>&OsT6i=w@A5{o2k1lPs(X0&Q+^Mqy0s7 z)MCli%fiy2ys^bsSleAo5zD$AR*yEu^cPnDPwXiB}JMja>)xGnwD3#Dn9qQ(4M zSV8-K;FS$jJ0^OHbybx)EmfKpHv6xxW6jLC#YJD5J^rMPRrE~q8ivU}2-U}rux?w= zdGgvCwc9UKLE~P{Mcv)&B3%1!BZ{T#tMqJYDlN2XrD!C%p^lZy_U8LCuJRS=!x+n0 zy0OM|U30KlRVzi~*G)BQ4_9ur3Q@n|=0+C1Hc78csO^3nxur(yOU-V+_Mq6jwT_j| zH_t*H|J&+Vl@&{Aj(f<`CoNpmHbr@Hdz5XpS`ubGe#LMH&rJ3d3w<{0CDe(%b0oUW ziV4i+??Rs-hBedOHGbYTB3v^%lS*6EU*S1}-??#5jaeF8uQZ=is0!X&$D2_!=${Gk z>}T=!)mTHebPK|)0Tb*LxxdOhtl%EUmQ!vf-@HXpAM1fCak^_rx}_L+%&r|{(C&f< z3AZ%E@QDfN+&vT}8$Jm|HTmI2uGvlsv?_e0#>^D+nVc)e%u-G3SETaMDABhv3I^R@ zVFuw>hmX~m)4JQhBUb$wE7W`6Uw^!TYWZXr+4qSW)wNvhG6pf9tY?A;$=W#T5`4Oe zv+h$>W?yQ0bt~u4sFp0HdJH-aXb${z9iI(geJp`B|1%Mq@7`FGoi>6_e9l-?8}-4(;}Qww~FOOZ6JeiBRM`SL3BSC+|8Xth(pxX;aD-taMNg;PBGYb^m2_pJ=LG}2M?N#9h4WZ z)bUuDeslt>yjLT%q(#LBtDx5ikJ&1rR|w+ub%r>rX%BV+jlYZ%drYhfH2x||^mFCr zHG)~quNmSriZu1)Z=%ei$vzvP@(qsIMhvT03F^(85vIi{C$z8ft$Ln+yl8L&0IfLQ zro3b~(+W|KS;C6LuOi=JnCvzWy(dAJ>0O5EnuR2M)Jj40yw|`ixPv5gYlWHzG`>%H zCi|p1!Rq`2p2-whwNem6zh$Uw0n*11(E6c3rA0`q6?G1OCs7S-j5_@v$=qanziXSi z;?(@?wo8}~`Q7y&3!fl4j*+hSGe!`jn={O)L-J`o&!3r~!;fAcn2CNyc*$=1m_uEwgb{B;m$9F7 zMDY=sCHV_V9(>N?k6@9cRg;BzsXvMHB_anuvHBII+pIUh==(Lxv{-SV&cinXRSYk6 zE}=cj-%`4taJ0E6c{ULy*xUE$|ID*pU$b78K|F<^neil3lpW&u>W z|5nd;+p^OJPel;#?{z$X(u>)&tM)K@e)$i=OOB%hzt*FO_)khSxM>3s|3!%w_icdj z_urJ*9@eV=p~Q}`#Q#!arvVYH!v05z*+M$oXRsC#`uhGH(p-t!)zSRy0&o-a4!;q% zZ~9;*NEQpT`cj86B|)8^!c4Bk>7A#ZTg>*ly{%2_M4k3gHC`b-kTW zp-W#|g|W-`yXI?Pxt#>*0e1}o(`3_ALEn4+8Z|T9ol@{;T!;friQlUc2i&Qe)GW83 zzCpf)5L9{GZ<@IN~r@P^h;MCRx)n)M- z%{_v(%5}8QW620R^i$HM2rn6UZoD)l2A&%)Lx~o1W1&yu z&wDLPiQFN5eO7|G>T)d8#G8P+JWsXoD4?#uQv-W-MM~w4$eVg~C6*c5t1I)=z+PR2 zr&@Rw#L}vbRCmj$KNf1S!vO7-UrnOg-1s2OPJG{db&<>kvw9>4mTqCmCbxPBoslt4 z?;0%C@MaG|vA^+QP0Y6OHQ=u`0^h{rV3)w!ly8$+!tB&{PuAg?Hns-bbp>u{8?VQ3 zO>zptpgRV8lHF7zb`njwy68rnoS_#i&9t((VlxQ)B1;lnrG?>4l zeLNkF#97($v}E4yq_8l%@TcWE8^~_Is$J9yV^52tcGakbS&rFPFzCgC{?qsxPrigl zpC`bs6B>z@b_LXlb=1oKQ9Ew}>ZVm{v2y!n*_Xat#^UBiVI248z`swaJ=~!+%hk^h zC^MTcWeiTdcL_@Ud}*^9wX%i8J%Td6nH%lc-8@3?F7);1GP9G-Ki@2}u=Rd!PtJi8V8w|*b#qzK#gUTU<%aLWj}Kh=C)M7t@sitzj;$>Ze1%jyb=P7-g; zu&wW;M%|ljqI{QIvUGJ70rQJ(BV?QVu+h1V?V?1hEE4+XexKv^5$4ct1b3H^yRRdi zF}P7^yfDM)aP%Fb^iX$z3*(~i98HdVh7h-aqaBbtHt>@p4;%XY232&YMmm0_gPsi5 zVmmjI)BZYgv-hJ2dF{KDyEJk~yxvV)Q;@HBZDhL)8{uUVYKx=#vRfnFUmoB`>ZVt| z=$y;$jZ8R5t1T^57TsfrK6Lvdo|MeK}#J~75NnI-6!a^WO_EJep74g;V=wP{m}V`?&>G-w|XV}2684{T4BB9d(r6{aqx7d zS@#mnBQrIsyTjby+Ty$5(+Y*@&3Bp5nA+38^>^XqvV9i!SPGTJ?=A1GF)QauMFXp2 z8Fdh+MX6#L)7B6)U(H6T10{?7<-*A2cMKlZKrE-GR_;1#exyb!r`MSMsag4Cu28VJ z2Va;Q_$Pq5I->kAMeRutwS6^WrEg^ywdhXUe3i;y_o`MXtnB==j6#i7Ilbqvq~|Vi zQ!_!e?T<2jW51XwHDC46tbT?>^@D5Ky&t?VKtF)K8iDvN(obM;RsucUEpbFw8@e<7 z^r*R7E9!BVS@O`c6jGQhqicXAmj(>(?gN!#W|S&z!BATnjc>CAs=)~rkiWA9YV+p{ z(M_RqqSVZc0p>V`+PollDL+O`+vv<1%{|2|^%pEE1Bl_XD9_yiowK-s8jP@KOH`Yi z9Dv?)>d2KFF~r7&Hg8a0@!T49RyLi^WrX9_Y9^Q^o=2FuOun~l@y0?>h0m{2{R7F? zw0ks|ZGO)Ix@YMEM=bU9lxGcB-y|verWtx6;ZdO4k6_+?5#bHLVyStZs0V+s$ZcLP z>hWDtwgq$fs|k++)%w5+?Ha=Kn{u14J!rhV zmSs|?TBlG8e1GFQmg%mSpjfp(QJuYBE>L7jXMvngDk zH(*upP?R2vmme0WRIECpg}UfF;E&X(b#It9U;_I-$}=fc?^UoKd8|sUtY+E8f!eY{ z4Er5jk5is&GZ|Dv;LRskCWTt+e3CNTLhF2rWro%HG-bAj*7*#}G|(yRpI*bo?T;yw z0`-{*qV^}0$WDgUQ43bl&+<%@3`MKx=R__wvZdbVd9F#OBE3HqxncGGjOUtUEYkaP zk!zqAjH$oixfE)exi3&EyOgtxV28#2G87AL~dBUukzgBnfsc^ zHP8z(_jR62q58}P)L%AI{jI0|E;$P-c=GWpfl9I33__j8Ukg-rlA*RT>NNfaki%+z zLm;!|*;MnJfZV)h)SY>YAshA1K}^3bP@C6^&KJJJP)+;i!rlpgSH-)KZJ=AITmF3O zdys9E(So=Ay)5sK$PcUk1IRbZZlM3SBl4Tqk8=FOM*i>}s1&NMX{eL_JAq2EmgCk( z0@WzWg$of+wm6fT@ zA+(47Q_3@0DWa$8pRr5>yMk`>=agx(LPR>hV3}cceo2`oD?_C7E0$@X6Lev}rc4S{ zk8xsetWindowTitle(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 */ +