diff --git a/ALSASound.c b/ALSASound.c index 5764e94..fc950a4 100644 --- a/ALSASound.c +++ b/ALSASound.c @@ -42,10 +42,19 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #define VOID void +char * strlop(char * buf, char delim); + +int gethints(); + +struct timespec pttclk; + extern int Closing; int SoundMode = 0; int stdinMode = 0; +int onlyMixSnoop = 0; + +int txLatency; //#define SHARECAPTURE // if defined capture device is opened and closed for each transission @@ -291,11 +300,11 @@ void platformInit() void txSleep(int mS) { - // called while waiting for next TX buffer or to delay response. - // Run background processes - // called while waiting for next TX buffer. Run background processes + if (mS < 0) + return; + while (mS > 50) { PollReceivedSamples(); // discard any received samples @@ -626,7 +635,8 @@ int OpenSoundPlayback(char * PlaybackDevice, int m_sampleRate, int channels, int { int err = 0; - char buf1[100]; + char buf1[256]; + char buf2[256]; char * ptr; if (playhandle) @@ -638,18 +648,20 @@ int OpenSoundPlayback(char * PlaybackDevice, int m_sampleRate, int channels, int strcpy(SavedPlaybackDevice, PlaybackDevice); // Saved so we can reopen in error recovery SavedPlaybackRate = m_sampleRate; - if (strstr(PlaybackDevice, "plug") == 0 && strchr(PlaybackDevice, ':')) - sprintf(buf1, "plug%s", PlaybackDevice); + strcpy(buf2, PlaybackDevice); + + ptr = strchr(buf2, ' '); + if (ptr) *ptr = 0; // Get Device part of name + + + if (strstr(buf2, "plug") == 0 && strchr(buf2, ':')) + sprintf(buf1, "plug%s", buf2); else - strcpy(buf1, PlaybackDevice); + strcpy(buf1, buf2); if (Report) Debugprintf("Real Device %s", buf1); - - ptr = strchr(buf1, ' '); - if (ptr) *ptr = 0; // Get Device part of name - snd_pcm_hw_params_t *hw_params; if ((err = snd_pcm_open(&playhandle, buf1, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) @@ -734,7 +746,9 @@ int OpenSoundCapture(char * CaptureDevice, int m_sampleRate, int Report) { int err = 0; - char buf1[100]; + char buf1[256]; + char buf2[256]; + char * ptr; snd_pcm_hw_params_t *hw_params; @@ -755,16 +769,19 @@ int OpenSoundCapture(char * CaptureDevice, int m_sampleRate, int Report) strcpy(SavedCaptureDevice, CaptureDevice); // Saved so we can reopen in error recovery SavedCaptureRate = m_sampleRate; - if (strstr(CaptureDevice, "plug") == 0 && strchr(CaptureDevice, ':')) - sprintf(buf1, "plug%s", CaptureDevice); + strcpy(buf2, CaptureDevice); + + ptr = strchr(buf2, ' '); + if (ptr) *ptr = 0; // Get Device part of name + + if (strstr(buf2, "plug") == 0 && strchr(buf2, ':')) + sprintf(buf1, "plug%s", buf2); else - strcpy(buf1, CaptureDevice); + strcpy(buf1, buf2); if (Report) Debugprintf("Real Device %s", buf1); - ptr = strchr(buf1, ' '); - if (ptr) *ptr = 0; // Get Device part of name if ((err = snd_pcm_open (&rechandle, buf1, SND_PCM_STREAM_CAPTURE, 0)) < 0) { Debugprintf("cannot open capture audio device %s (%s)", buf1, snd_strerror(err)); @@ -882,17 +899,14 @@ int OpenSoundCapture(char * CaptureDevice, int m_sampleRate, int Report) if (Report) Debugprintf("Capture using %d channels", m_recchannels); - int i; short buf[256]; - for (i = 0; i < 10; ++i) + if ((err = snd_pcm_readi(rechandle, buf, 12)) != 12) { - if ((err = snd_pcm_readi (rechandle, buf, 128)) != 128) - { - Debugprintf("read from audio interface failed (%s)", snd_strerror (err)); - } + Debugprintf("read from audio interface failed (%s)", snd_strerror(err)); } + // Debugprintf("Read got %d", err); return TRUE; @@ -957,11 +971,11 @@ int SoundCardWrite(short * input, int nSamples) // Stop Capture - if (rechandle) - { - snd_pcm_close(rechandle); - rechandle = NULL; - } +// if (rechandle) +// { +// snd_pcm_close(rechandle); +// rechandle = NULL; +// } avail = snd_pcm_avail_update(playhandle); // Debugprintf("avail before play returned %d", (int)avail); @@ -1189,30 +1203,37 @@ short * SoundInit(); void GetSoundDevices() { - if (SoundMode == 0) + if (onlyMixSnoop) { - GetInputDeviceCollection(); - GetOutputDeviceCollection(); + gethints(); } - else if (SoundMode == 1) + else { - PlaybackCount = 3; + if (SoundMode == 0) + { + GetInputDeviceCollection(); + GetOutputDeviceCollection(); + } + else if (SoundMode == 1) + { + PlaybackCount = 3; - strcpy(&PlaybackNames[0][0], "/dev/dsp0"); - strcpy(&PlaybackNames[1][0], "/dev/dsp1"); - strcpy(&PlaybackNames[2][0], "/dev/dsp2"); + strcpy(&PlaybackNames[0][0], "/dev/dsp0"); + strcpy(&PlaybackNames[1][0], "/dev/dsp1"); + strcpy(&PlaybackNames[2][0], "/dev/dsp2"); - CaptureCount = 3; + CaptureCount = 3; - strcpy(&CaptureNames[0][0], "/dev/dsp0"); - strcpy(&CaptureNames[1][0], "/dev/dsp1"); - strcpy(&CaptureNames[2][0], "/dev/dsp2"); - } - else if (SoundMode == 2) - { - // Pulse + strcpy(&CaptureNames[0][0], "/dev/dsp0"); + strcpy(&CaptureNames[1][0], "/dev/dsp1"); + strcpy(&CaptureNames[2][0], "/dev/dsp2"); + } + else if (SoundMode == 2) + { + // Pulse - listpulse(); + listpulse(); + } } } @@ -1300,7 +1321,18 @@ void PollReceivedSamples() // if still not enough, too bad! if (bytes != ReceiveSize * 2) + { + // This seems to happen occasionally even when we shouldn't be in stdin mode. Exit + Debugprintf("Short Read %d", bytes); + closeTraceLog(); + + Closing = TRUE; + + sleep(1); + exit(1); + } + // convert to stereo @@ -1338,7 +1370,7 @@ void PollReceivedSamples() { lastlevelGUI = Now; - if ((Now - lastlevelreport) > 10000) // 10 Secs + if ((Now - lastlevelreport) > 60000) // 60 Secs { char HostCmd[64]; lastlevelreport = Now; @@ -1403,6 +1435,11 @@ short * SoundInit() // Called at end of transmission +int useTimedPTT = 1; + +extern int SampleNo; +int pttOnTime(); + void SoundFlush() { // Append Trailer then send remaining samples @@ -1418,43 +1455,71 @@ void SoundFlush() // Wait for tx to complete - Debugprintf("Flush Soundmode = %d", SoundMode); +// Debugprintf("Flush Soundmode = %d", SoundMode); if (SoundMode == 0) // ALSA { - usleep(100000); - - while (1 && playhandle) + if (useTimedPTT) { - snd_pcm_sframes_t avail = snd_pcm_avail_update(playhandle); + // Calulate PTT Time from Number of samples and samplerate - // Debugprintf("Waiting for complete. Avail %d Max %d", avail, MaxAvail); + // samples sent is is in SampleNo, Time PTT was raised in timeval pttclk + // txLatency is extra ptt time to compenstate for time soundcard takes to start outputting samples - snd_pcm_status_alloca(&status); // alloca allocates once per function, does not need a free + struct timespec pttnow; -// Debugprintf("Waiting for complete. Avail %d Max %d last %d", avail, MaxAvail, lastavail); + clock_gettime(CLOCK_MONOTONIC, &pttnow); - if ((err = snd_pcm_status(playhandle, status)) != 0) + time_t pttontimemS = (pttclk.tv_sec * 1000) + (pttclk.tv_nsec / 1000000); + time_t nowtimemS = (pttnow.tv_sec * 1000) + (pttnow.tv_nsec / 1000000); + + // We have already added latency to tail, so don't add again + + int txlenMs = (1000 * SampleNo / TX_Samplerate); // 12000 samples per sec. + + Debugprintf("Tx Time %d Time till end = %d", txlenMs, (nowtimemS - pttontimemS)); + + txSleep(txlenMs - (nowtimemS - pttontimemS)); + + } + else + { + usleep(100000); + + while (1 && playhandle) { - Debugprintf("snd_pcm_status() failed: %s", snd_strerror(err)); - break; + snd_pcm_sframes_t avail = snd_pcm_avail_update(playhandle); + + // Debugprintf("Waiting for complete. Avail %d Max %d", avail, MaxAvail); + + snd_pcm_status_alloca(&status); // alloca allocates once per function, does not need a free + + // Debugprintf("Waiting for complete. Avail %d Max %d last %d", avail, MaxAvail, lastavail); + + if ((err = snd_pcm_status(playhandle, status)) != 0) + { + Debugprintf("snd_pcm_status() failed: %s", snd_strerror(err)); + break; + } + + res = snd_pcm_status_get_state(status); + + // Debugprintf("PCM Status = %d", res); + + if (res != SND_PCM_STATE_RUNNING || lastavail == avail) // If sound system is not running then it needs data + // if (res != SND_PCM_STATE_RUNNING) // If sound system is not running then it needs data + // if (MaxAvail - avail < 100) + { + // Send complete - Restart Capture + + OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, 0); + break; + } + lastavail = avail; + usleep(50000); } - res = snd_pcm_status_get_state(status); - // Debugprintf("PCM Status = %d", res); - - if (res != SND_PCM_STATE_RUNNING || lastavail == avail) // If sound system is not running then it needs data -// if (res != SND_PCM_STATE_RUNNING) // If sound system is not running then it needs data - // if (MaxAvail - avail < 100) - { - // Send complete - Restart Capture - - OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, 0); - break; - } - lastavail = avail; - usleep(50000); } // I think we should turn round the link here. I dont see the point in // waiting for MainPoll @@ -1819,7 +1884,7 @@ VOID COMClearRTS(HANDLE fd) HANDLE OpenCOMPort(char * Port, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits) { - char buf[100]; + char buf[256]; // Linux Version. @@ -1928,3 +1993,119 @@ VOID CloseCOMPort(HANDLE fd) close(fd); } +// "hints" processing for looking for SNOOP/MIX devices + +int gethints() +{ + const char *iface = "pcm"; + void **hints; + char **n; + int err; + char hwdev[256]; + snd_pcm_t *pcm = NULL; + char NameString[256]; + + CloseSoundCard(); + + Debugprintf("Available Mix/Snoop Devices\n"); + + PlaybackCount = 0; + CaptureCount = 0; + + err = snd_device_name_hint(-1, iface, &hints); + + if (err < 0) + Debugprintf("snd_device_name_hint error: %s", snd_strerror(err)); + + n = (char **)hints; + + while (*n != NULL) + { + if (memcmp(*n, "NAMEmix", 7) == 0) //NAMEmix00|DESCQtSM Mix for hw0:0 + { + char Hint[256]; + char * ptr; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + + strcpy(Hint, *n); + + ptr = strchr(Hint, '|'); + + if (ptr) + { + *ptr++ = 0; + } + + strcpy(hwdev, &Hint[4]); + + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening output device %s ", err, hwdev); + goto nextdevice; + } + + // Add device to list + + if (ptr) + sprintf(NameString, "%s %s", hwdev, &ptr[4]); + else + strcpy(NameString, hwdev); + + Debugprintf(NameString); + + strcpy(PlaybackNames[PlaybackCount++], NameString); + snd_pcm_close(pcm); + pcm = NULL; + } + else if (memcmp(*n, "NAMEsnoop", 9) == 0) + { + char Hint[256]; + char * ptr; + snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE; + + strcpy(Hint, *n); + + ptr = strchr(Hint, '|'); + + if (ptr) + { + *ptr++ = 0; + } + + strcpy(hwdev, &Hint[4]); + + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening input device %s ", err, hwdev); + goto nextdevice; + } + + // Add device to list + + if (ptr) + sprintf(NameString, "%s %s", hwdev, &ptr[4]); + else + strcpy(NameString, hwdev); + + Debugprintf(NameString); + + strcpy(CaptureNames[CaptureCount++], NameString); + snd_pcm_close(pcm); + pcm = NULL; + } + + nextdevice: + + n++; + + } + snd_device_name_free_hint(hints); + return 0; +} + + + diff --git a/ARDOPC.c b/ARDOPC.c index 3f93242..2652808 100644 --- a/ARDOPC.c +++ b/ARDOPC.c @@ -1024,195 +1024,6 @@ extern int kk; // Info Symbols BOOL blnErrorsCorrected; #define NEWRS - -BOOL xRSDecode(UCHAR * bytRcv, int Length, int CheckLen, BOOL * blnRSOK) -{ -#ifdef NEWRS - - // Using a modified version of Henry Minsky's code - - //Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009 - - // Rick's Implementation processes the byte array in reverse. and also - // has the check bytes in the opposite order. I've modified the encoder - // to allow for this, but so far haven't found a way to mske the decoder - // work, so I have to reverse the data and checksum to decode G8BPQ Nov 2015 - - // returns TRUE if was ok or correction succeeded, FALSE if correction impossible - - UCHAR intTemp[256]; // WOrk Area to pass to Decoder - int i; - UCHAR * ptr2 = intTemp; - UCHAR * ptr1 = &bytRcv[Length - CheckLen -1]; // Last Byte of Data - - int DataLen = Length - CheckLen; - int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes - - *blnRSOK = FALSE; - - if (Length > 255 || Length < (1 + CheckLen)) //Too long or too short - return FALSE; - - if (NPAR != CheckLen) // Changed RS Len, so recalc constants; - { - NPAR = CheckLen; - MaxErrors = NPAR /2; - - initialize_ecc(); - } - - - // We reverse the data while zero padding it to speed things up - - // We Need (Data Reversed) (Zero Padding) (Checkbytes Reversed) - - // Reverse Data - - for (i = 0; i < DataLen; i++) - { - *(ptr2++) = *(ptr1--); - } - - // Clear padding - - memset(ptr2, 0, PadLength); - - ptr2+= PadLength; - - // Error Bits - - ptr1 = &bytRcv[Length - 1]; // End of check bytes - - for (i = 0; i < CheckLen; i++) - { - *(ptr2++) = *(ptr1--); - } - - decode_data(intTemp, 255); - - // check if syndrome is all zeros - - if (check_syndrome() == 0) - { - // RS ok, so no need to correct - - *blnRSOK = TRUE; - return TRUE; // No Need to Correct - } - - if (correct_errors_erasures (intTemp, 255, 0, 0) == 0) // Dont support erasures at the momnet - - // Uncorrectable - - return FALSE; - - // Data has been corrected, so need to reverse again - - ptr1 = &intTemp[DataLen - 1]; - ptr2 = bytRcv; // Last Byte of Data - - for (i = 0; i < DataLen; i++) - { - *(ptr2++) = *(ptr1--); - } - - // ?? Do we need to return the check bytes ?? - - // Yes, so we can redo RS Check on supposedly connected frame - - ptr1 = &intTemp[254]; // End of Check Bytes - - for (i = 0; i < CheckLen; i++) - { - *(ptr2++) = *(ptr1--); - } - - return TRUE; -} - -#else - - // Old (Rick's) code - - // Sets blnRSOK if OK without correction - - // Returns TRUE if OK oe Corrected - // False if Can't correct - - - UCHAR intTemp[256]; // Work Area to pass to Decoder - int i; - int intStartIndex; - UCHAR * ptr2 = intTemp; - UCHAR * ptr1 = bytRcv; - BOOL RSWasOK; - - int DataLen = Length - CheckLen; - int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes - - *blnRSOK = FALSE; - - if (Length > 255 || Length < (1 + CheckLen)) //Too long or too short - return FALSE; - - - if (NPAR != CheckLen) // Changed RS Len, so recalc constants; - { - NPAR = CheckLen; - tt = sqrt(NPAR); - kk = 255-CheckLen; - generate_gf(); - gen_poly(); - } - - intStartIndex = 255 - Length; // set the start point for shortened RS codes - - // We always work on a 255 byte buffer, prepending zeros if neccessary - - // Clear padding - - memset(ptr2, 0, PadLength); - ptr2 += PadLength; - - memcpy(ptr2, ptr1, Length); - - // convert to indexed form - - for(i = 0; i < 256; i++) - { -// intIsave = i; -// intIndexSave = index_of[intTemp[i]]; - recd[i] = index_of[intTemp[i]]; - } - -// printtick("entering decode_rs"); - - blnErrorsCorrected = FALSE; - - RSWasOK = decode_rs(); - -// printtick("decode_rs Done"); - - *blnRSOK = RSWasOK; - - if (RSWasOK) - return TRUE; - - if(blnErrorsCorrected) - { - for (i = 0; i < DataLen; i++) - { - bytRcv[i] = recd[i + intStartIndex]; - } - return TRUE; - } - - return FALSE; -} -#endif - - - // Function to encode ConnectRequest frame diff --git a/Config.cpp b/Config.cpp index f660aaa..e2239f9 100644 --- a/Config.cpp +++ b/Config.cpp @@ -1,4 +1,4 @@ -/*extern "C" +/* Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO This file is part of QtSoundModem @@ -29,8 +29,12 @@ 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 onlyMixSnoop; + +//extern "C" int RX_SR; +//extern "C" int TX_SR; +extern "C" int txLatency; + extern "C" int multiCore; extern "C" char * Wisdom; extern int WaterfallMin; @@ -60,6 +64,10 @@ extern "C" int RSID_SABM[4]; extern "C" int RSID_UI[4]; extern "C" int RSID_SetModem[4]; +extern "C" int nonGUIMode; + + + extern QFont Font; @@ -139,8 +147,12 @@ void getSettings() 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(); +// RX_SR = settings->value("Init/RXSampleRate", 12000).toInt(); +// TX_SR = settings->value("Init/TXSampleRate", 12000).toInt(); + txLatency = settings->value("Init/txLatency", 50).toInt(); + + + onlyMixSnoop = settings->value("Init/onlyMixSnoop", 0).toInt(); strcpy(CaptureDevice, settings->value("Init/SndRXDeviceName", "hw:1,0").toString().toUtf8()); strcpy(PlaybackDevice, settings->value("Init/SndTXDeviceName", "hw:1,0").toString().toUtf8()); @@ -166,6 +178,10 @@ void getSettings() HamLibPort = settings->value("Init/HamLibPort", 4532).toInt(); strcpy(HamLibHost, settings->value("Init/HamLibHost", "127.0.0.1").toString().toUtf8()); + FLRigPort = settings->value("Init/FLRigPort", 12345).toInt(); + strcpy(FLRigHost, settings->value("Init/FLRigHost", "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(); @@ -209,8 +225,8 @@ void getSettings() 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; +// 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(); @@ -365,7 +381,9 @@ void saveSettings() settings->setValue("PointSize", Font.pointSize()); settings->setValue("Weight", Font.weight()); - settings->setValue("PSKWindow", constellationDialog->geometry()); + if (nonGUIMode == 0) + settings->setValue("PSKWindow", constellationDialog->geometry()); + settings->setValue("Init/SoundMode", SoundMode); settings->setValue("Init/UDPClientPort", UDPClientPort); settings->setValue("Init/UDPServerPort", UDPServerPort); @@ -375,9 +393,12 @@ void saveSettings() settings->setValue("Init/UDPHost", UDPHost); - settings->setValue("Init/TXSampleRate", TX_SR); - settings->setValue("Init/RXSampleRate", RX_SR); +// settings->setValue("Init/TXSampleRate", TX_SR); +// settings->setValue("Init/RXSampleRate", RX_SR); + settings->setValue("Init/txLatency", txLatency); + settings->setValue("Init/onlyMixSnoop", onlyMixSnoop); + settings->setValue("Init/SndRXDeviceName", CaptureDevice); settings->setValue("Init/SndTXDeviceName", PlaybackDevice); @@ -400,6 +421,9 @@ void saveSettings() settings->setValue("Init/CM108Addr", CM108Addr); settings->setValue("Init/HamLibPort", HamLibPort); settings->setValue("Init/HamLibHost", HamLibHost); + settings->setValue("Init/FLRigPort", FLRigPort); + settings->setValue("Init/FLRigHost", FLRigHost); + settings->setValue("Init/MinimizetoTray", MintoTray); settings->setValue("Init/multiCore", multiCore); settings->setValue("Init/Wisdom", Wisdom); diff --git a/Config.cpp.bak b/Config.cpp.bak new file mode 100644 index 0000000..d5d7a67 --- /dev/null +++ b/Config.cpp.bak @@ -0,0 +1,481 @@ +/*extern "C" +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 + +#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 int WaterfallMin; +extern int WaterfallMax; + +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 QDialog * constellationDialog; +extern QRect PSKRect; + +extern char CWIDCall[128]; +extern "C" char CWIDMark[32]; +extern int CWIDInterval; +extern int CWIDLeft; +extern int CWIDRight; +extern int CWIDType; +extern bool afterTraffic; +extern bool darkTheme; + +extern "C" int RSID_SABM[4]; +extern "C" int RSID_UI[4]; +extern "C" int RSID_SetModem[4]; + +extern QFont Font; + + +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]; + QVariant Q; + QByteArray x; + sprintf(fullKey, "%s/%s", Prefix, key); + Q = settings->value(fullKey, Default); + x = Q.toString().toUtf8(); + + return Q; +} + +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(); + 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()); + strcpy(exclude_callsigns[Chan], getAX25Param("ExcludeCallsigns", "").toString().toUtf8()); + + fx25_mode[Chan] = getAX25Param("FX25", FX25_MODE_RX).toInt(); + il2p_mode[Chan] = getAX25Param("IL2P", IL2P_MODE_NONE).toInt(); + il2p_crc[Chan] = getAX25Param("IL2PCRC", 0).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(); + + 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(); + 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(); + Wisdom = strdup(settings->value("Init/Wisdom", "").toString().toUtf8()); + WaterfallMin = settings->value("Init/WaterfallMin", 0).toInt(); + WaterfallMax = settings->value("Init/WaterfallMax", 3300).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(); + + 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(); + CWIDType = settings->value("Modem/CWIDType", 1).toInt(); // on/off + afterTraffic = settings->value("Modem/afterTraffic", false).toBool(); + + 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; + + } + + darkTheme = settings->value("Init/darkTheme", false).toBool(); + + 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("IL2PCRC", il2p_crc[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("FontFamily", Font.family()); + settings->setValue("PointSize", Font.pointSize()); + settings->setValue("Weight", Font.weight()); + + settings->setValue("PSKWindow", constellationDialog->geometry()); + 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); + + settings->setValue("Init/WaterfallMin", WaterfallMin); + settings->setValue("Init/WaterfallMax", WaterfallMax); + + // 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/CWIDMark", CWIDMark); + settings->setValue("Modem/CWIDInterval", CWIDInterval); + settings->setValue("Modem/CWIDLeft", CWIDLeft); + settings->setValue("Modem/CWIDRight", CWIDRight); + settings->setValue("Modem/CWIDType", CWIDType); + settings->setValue("Modem/afterTraffic", afterTraffic); + + settings->setValue("Init/darkTheme", darkTheme); + + saveAX25Params(0); + saveAX25Params(1); + saveAX25Params(2); + saveAX25Params(3); + + settings->sync(); + + delete(settings); +} diff --git a/ModemDialog.ui b/ModemDialog.ui index 9b5b856..54b5220 100644 --- a/ModemDialog.ui +++ b/ModemDialog.ui @@ -6,8 +6,8 @@ 0 0 - 614 - 599 + 597 + 637 @@ -21,8 +21,8 @@ 10 24 - 591 - 471 + 583 + 533 @@ -37,8 +37,8 @@ -1 -1 - 583 - 441 + 605 + 499 @@ -49,17 +49,471 @@ 0 0 - 577 - 439 + 576 + 494 + + + + 0 + 11 + 281 + 477 + + + + Modem params + + + + + 100 + 181 + 61 + 23 + + + + + + + 100 + 21 + 41 + 23 + + + + + + + 100 + 50 + 41 + 23 + + + + + + + 100 + 151 + 61 + 23 + + + + + + + 100 + 221 + 69 + 22 + + + + + NONE + + + + + SINGLE + + + + + + + 10 + 20 + 71 + 23 + + + + TX Delay + + + + + + 10 + 49 + 71 + 23 + + + + TX Tail + + + + + + 10 + 150 + 71 + 23 + + + + Add RX + + + + + + 10 + 180 + 71 + 23 + + + + Add RX Shift + + + + + + 10 + 220 + 91 + 23 + + + + Bits Recovery + + + + + + 176 + 21 + 71 + 23 + + + + msec + + + + + + 176 + 50 + 71 + 23 + + + + msec + + + + + + 176 + 151 + 71 + 23 + + + + pairs + + + + + + 175 + 181 + 71 + 23 + + + + Hz + + + + + + 100 + 251 + 65 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + + + 10 + 250 + 86 + 23 + + + + FX25 Mode + + + + + + 10 + 79 + 71 + 23 + + + + Frack + + + + + + 176 + 80 + 71 + 23 + + + + secs + + + + + + 100 + 80 + 41 + 23 + + + + + + + 10 + 396 + 171 + 20 + + + + Send RSID before UI + + + + + + 10 + 422 + 251 + 20 + + + + Send RSID before SABM/UA + + + + + + 10 + 448 + 171 + 20 + + + + Set Modem from RSID + + + + + + 164 + 396 + 101 + 23 + + + + Send RSID + + + + + + 98 + 358 + 181 + 23 + + + + + + + 10 + 358 + 71 + 23 + + + + DigiCalls + + + + + + 10 + 280 + 86 + 23 + + + + IL2P Mode + + + + + + 100 + 280 + 81 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + il2p Only + + + + + + + 130 + 111 + 71 + 23 + + + + maxFrame + + + + + + 200 + 112 + 41 + 23 + + + + + + + 70 + 111 + 41 + 23 + + + + + + + 10 + 110 + 71 + 23 + + + + Retries + + + + + + 24 + 308 + 93 + 17 + + + + Send CRC + + + + + + 124 + 308 + 115 + 17 + + + + Check CRC + + + 290 - 10 - 283 - 349 + 12 + 297 + 283 @@ -310,447 +764,6 @@ - - - - 0 - 10 - 281 - 431 - - - - Modem params - - - - - 100 - 181 - 61 - 23 - - - - - - - 100 - 21 - 41 - 23 - - - - - - - 100 - 50 - 41 - 23 - - - - - - - 100 - 151 - 61 - 23 - - - - - - - 100 - 221 - 69 - 22 - - - - - NONE - - - - - SINGLE - - - - - - - 10 - 20 - 71 - 23 - - - - TX Delay - - - - - - 10 - 49 - 71 - 23 - - - - TX Tail - - - - - - 10 - 150 - 71 - 23 - - - - Add RX - - - - - - 10 - 180 - 71 - 23 - - - - Add RX Shift - - - - - - 10 - 220 - 91 - 23 - - - - Bits Recovery - - - - - - 176 - 21 - 71 - 23 - - - - msec - - - - - - 176 - 50 - 71 - 23 - - - - msec - - - - - - 176 - 151 - 71 - 23 - - - - pairs - - - - - - 175 - 181 - 71 - 23 - - - - Hz - - - - - - 100 - 251 - 81 - 22 - - - - - None - - - - - RX Only - - - - - RX+TX - - - - - - - 10 - 250 - 86 - 23 - - - - FX25 Mode - - - - - - 10 - 79 - 71 - 23 - - - - Frack - - - - - - 176 - 80 - 71 - 23 - - - - secs - - - - - - 100 - 80 - 41 - 23 - - - - - - - 10 - 348 - 171 - 20 - - - - Send RSID before UI - - - - - - 10 - 374 - 251 - 20 - - - - Send RSID before SABM/UA - - - - - - 10 - 400 - 171 - 20 - - - - Set Modem from RSID - - - - - - 164 - 348 - 101 - 23 - - - - Send RSID - - - - - - 100 - 310 - 181 - 23 - - - - - - - 10 - 310 - 71 - 23 - - - - DigiCalls - - - - - - 10 - 279 - 86 - 23 - - - - IL2P Mode - - - - - - 100 - 280 - 81 - 22 - - - - - None - - - - - RX Only - - - - - RX+TX - - - - - il2p Only - - - - - - - 130 - 111 - 71 - 23 - - - - maxFrame - - - - - - 200 - 112 - 41 - 23 - - - - - - - 70 - 111 - 41 - 23 - - - - - - - 10 - 110 - 71 - 23 - - - - Retries - - - - - - 200 - 280 - 53 - 21 - - - - CRC - - - @@ -776,14 +789,14 @@ 0 0 570 - 439 + 502 290 - 10 + 11 281 287 @@ -1083,7 +1096,7 @@ 0 10 281 - 431 + 477 @@ -1344,7 +1357,7 @@ 164 - 348 + 396 101 23 @@ -1357,7 +1370,7 @@ 10 - 348 + 396 171 20 @@ -1370,7 +1383,7 @@ 10 - 400 + 448 171 20 @@ -1383,7 +1396,7 @@ 10 - 374 + 422 251 20 @@ -1396,7 +1409,7 @@ 10 - 310 + 358 71 23 @@ -1409,7 +1422,7 @@ 100 - 310 + 358 181 23 @@ -1458,17 +1471,30 @@ - + - 200 - 280 - 53 - 21 + 124 + 308 + 129 + 17 - CRC + Check CRC + + + + + + 24 + 308 + 97 + 17 + + + + Send CRC @@ -1543,7 +1569,7 @@ 0 0 570 - 581 + 502 @@ -1850,7 +1876,7 @@ 0 10 281 - 441 + 477 @@ -2111,7 +2137,7 @@ 10 - 374 + 422 251 20 @@ -2124,7 +2150,7 @@ 10 - 400 + 448 171 20 @@ -2137,7 +2163,7 @@ 10 - 348 + 396 171 20 @@ -2150,7 +2176,7 @@ 164 - 348 + 396 101 23 @@ -2163,7 +2189,7 @@ 10 - 310 + 358 71 23 @@ -2176,7 +2202,7 @@ 100 - 310 + 358 181 23 @@ -2228,17 +2254,30 @@ - + - 200 - 280 - 53 - 21 + 124 + 308 + 107 + 17 - CRC + Check CRC + + + + + + 24 + 308 + 95 + 17 + + + + Send CRC @@ -2309,8 +2348,8 @@ 0 0 - 570 - 581 + 575 + 502 @@ -2617,7 +2656,7 @@ 0 10 281 - 431 + 477 @@ -2878,7 +2917,7 @@ 10 - 374 + 422 251 20 @@ -2891,7 +2930,7 @@ 10 - 400 + 448 171 20 @@ -2904,7 +2943,7 @@ 10 - 348 + 396 171 20 @@ -2917,7 +2956,7 @@ 164 - 348 + 396 101 23 @@ -2930,7 +2969,7 @@ 10 - 310 + 358 71 23 @@ -2943,7 +2982,7 @@ 100 - 310 + 358 181 23 @@ -2995,17 +3034,30 @@ - + - 200 - 280 - 53 - 21 + 124 + 308 + 129 + 17 - CRC + Check CRC + + + + + + 24 + 308 + 99 + 17 + + + + Send CRC @@ -3060,7 +3112,7 @@ 127 - 550 + 596 276 33 @@ -3111,7 +3163,7 @@ 12 - 516 + 562 71 20 @@ -3124,7 +3176,7 @@ 78 - 516 + 562 71 20 @@ -3137,7 +3189,7 @@ 340 - 516 + 562 61 20 @@ -3153,7 +3205,7 @@ 396 - 516 + 562 101 20 @@ -3174,7 +3226,7 @@ 158 - 516 + 562 61 20 @@ -3187,7 +3239,7 @@ 208 - 516 + 562 31 20 @@ -3197,7 +3249,7 @@ 248 - 516 + 562 61 20 @@ -3210,7 +3262,7 @@ 318 - 516 + 562 31 20 @@ -3226,7 +3278,7 @@ 500 - 516 + 562 91 20 diff --git a/Modulate.c b/Modulate.c index 1f996db..86900e3 100644 --- a/Modulate.c +++ b/Modulate.c @@ -5,7 +5,7 @@ #define ARDOPBufferSize 12000 * 100 -extern char CWIDMark[32]; +char CWIDMark[32] = ""; extern short ARDOPTXBuffer[4][ARDOPBufferSize]; // Enough to hold whole frame of samples diff --git a/QtSoundModem.cpp b/QtSoundModem.cpp index fe1a717..37c6c46 100644 --- a/QtSoundModem.cpp +++ b/QtSoundModem.cpp @@ -47,8 +47,10 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include #include #include +#include #include "UZ7HOStuff.h" +#include QImage *Constellation[4]; QImage *Waterfall = 0; @@ -93,6 +95,8 @@ extern "C" char CaptureNames[16][256]; extern "C" char PlaybackNames[16][256]; extern "C" int SoundMode; +extern "C" int onlyMixSnoop; + extern "C" int multiCore; extern "C" int refreshModems; @@ -112,6 +116,8 @@ extern "C" int using48000; // Set if using 48K sample rate (ie RUH Modem activ extern "C" int ReceiveSize; extern "C" int SendSize; // 100 mS for now +extern "C" int txLatency; + extern "C" { int InitSound(BOOL Report); @@ -132,6 +138,7 @@ extern "C" void sendRSID(int Chan, int dropTX); void RSIDinitfft(); void il2p_init(int il2p_debug); + void closeTraceLog(); } void make_graph_buf(float * buf, short tap, QPainter * bitmap); @@ -147,7 +154,7 @@ int FreqD = 1500; int DCD = 50; char CWIDCall[128] = ""; -extern "C" char CWIDMark[32] = ""; +extern "C" char CWIDMark[32]; int CWIDInterval = 0; int CWIDLeft = 0; int CWIDRight = 0; @@ -220,8 +227,13 @@ extern "C" int TXPort; extern char UDPHost[64]; QTimer *cwidtimer; +QTimer *PTTWatchdog; + QWidget * mythis; +QElapsedTimer pttOnTimer; + + QSystemTrayIcon * trayIcon = nullptr; @@ -318,9 +330,6 @@ void QtSoundModem::resizeEvent(QResizeEvent* event) QRect r = geometry(); - QRect r1 = ui.monWindow->geometry(); - QRect r2 = ui.centralWidget->geometry(); - int modemBoxHeight = 34; int Width = r.width(); @@ -500,6 +509,7 @@ void DoPSKWindows() } QTimer *wftimer; +extern "C" struct timespec pttclk; QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) { @@ -507,6 +517,12 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) int csize; QFont::Weight weight; +#ifndef WIN32 + clock_getres(CLOCK_MONOTONIC, &pttclk); + printf("CLOCK_MONOTONIC %d, %d\n", pttclk.tv_sec, pttclk.tv_nsec); +#endif + + ui.setupUi(this); mythis = this; @@ -759,6 +775,11 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) QObject::connect(t, SIGNAL(updateDCD(int, int)), this, SLOT(doupdateDCD(int, int)), Qt::QueuedConnection); QObject::connect(t, SIGNAL(startCWIDTimer()), this, SLOT(startCWIDTimerSlot()), Qt::QueuedConnection); + QObject::connect(t, SIGNAL(setWaterfallImage()), this, SLOT(setWaterfallImage()), Qt::QueuedConnection); + QObject::connect(t, SIGNAL(setLevelImage()), this, SLOT(setLevelImage()), Qt::QueuedConnection); + QObject::connect(t, SIGNAL(setConstellationImage(int, int)), this, SLOT(setConstellationImage(int, int)), Qt::QueuedConnection); + QObject::connect(t, SIGNAL(startWatchdog()), this, SLOT(StartWatchdog()), Qt::QueuedConnection); + QObject::connect(t, SIGNAL(stopWatchdog()), this, SLOT(StopWatchdog()), Qt::QueuedConnection); connect(ui.RXOffsetA, SIGNAL(returnPressed()), this, SLOT(returnPressed())); connect(ui.RXOffsetB, SIGNAL(returnPressed()), this, SLOT(returnPressed())); @@ -771,11 +792,13 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) wftimer = new QTimer(this); connect(wftimer, SIGNAL(timeout()), this, SLOT(doRestartWF())); - wftimer->start(1000 * 300); +// wftimer->start(1000 * 300); cwidtimer = new QTimer(this); connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer())); + PTTWatchdog = new QTimer(this); + connect(PTTWatchdog, SIGNAL(timeout()), this, SLOT(PTTWatchdogExpired())); if (CWIDInterval && afterTraffic == false) cwidtimer->start(CWIDInterval * 60000); @@ -1023,7 +1046,7 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "centerB") == 0) { - if (i > 299) + if (i > 300) { QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); ui.centerB->setValue(Freq_Change(1, i)); @@ -1452,10 +1475,14 @@ void QtSoundModem::doModems() Dlg->IL2PModeC->setCurrentIndex(il2p_mode[2]); Dlg->IL2PModeD->setCurrentIndex(il2p_mode[3]); - Dlg->CRC_A->setChecked(il2p_crc[0]); - Dlg->CRC_B->setChecked(il2p_crc[1]); - Dlg->CRC_C->setChecked(il2p_crc[2]); - Dlg->CRC_D->setChecked(il2p_crc[3]); + Dlg->CRCTX_A->setChecked((il2p_crc[0] & 1)); + Dlg->CRCRX_A->setChecked((il2p_crc[0] & 2)); + Dlg->CRCTX_B->setChecked((il2p_crc[1] & 1)); + Dlg->CRCRX_B->setChecked((il2p_crc[1] & 2)); + Dlg->CRCTX_C->setChecked((il2p_crc[2] & 1)); + Dlg->CRCRX_C->setChecked((il2p_crc[2] & 2)); + Dlg->CRCTX_D->setChecked((il2p_crc[3] & 1)); + Dlg->CRCRX_D->setChecked((il2p_crc[3] & 2)); Dlg->CWIDCall->setText(CWIDCall); Dlg->CWIDInterval->setText(QString::number(CWIDInterval)); @@ -1681,10 +1708,21 @@ void QtSoundModem::modemSave() il2p_mode[2] = Dlg->IL2PModeC->currentIndex(); il2p_mode[3] = Dlg->IL2PModeD->currentIndex(); - il2p_crc[0] = Dlg->CRC_A->isChecked(); - il2p_crc[1] = Dlg->CRC_B->isChecked(); - il2p_crc[2] = Dlg->CRC_C->isChecked(); - il2p_crc[3] = Dlg->CRC_D->isChecked(); + il2p_crc[0] = Dlg->CRCTX_A->isChecked(); + if (Dlg->CRCRX_A->isChecked()) + il2p_crc[0] |= 2; + + il2p_crc[1] = Dlg->CRCTX_B->isChecked(); + if (Dlg->CRCRX_B->isChecked()) + il2p_crc[1] |= 2; + + il2p_crc[2] = Dlg->CRCTX_C->isChecked(); + if (Dlg->CRCRX_C->isChecked()) + il2p_crc[2] |= 2; + + il2p_crc[3] = Dlg->CRCTX_D->isChecked(); + if (Dlg->CRCRX_D->isChecked()) + il2p_crc[3] |= 2; recovery[0] = Dlg->recoverBitA->currentIndex(); recovery[1] = Dlg->recoverBitB->currentIndex(); @@ -1822,6 +1860,8 @@ char NewPTTPort[80]; int newSoundMode = 0; int oldSoundMode = 0; +int oldSnoopMix = 0; +int newSnoopMix = 0; void QtSoundModem::SoundModeChanged(bool State) { @@ -1829,6 +1869,8 @@ void QtSoundModem::SoundModeChanged(bool State) // Mustn't change SoundMode until dialog is accepted + newSnoopMix = Dev->onlyMixSnoop->isChecked(); + if (Dev->UDP->isChecked()) newSoundMode = 3; else if (Dev->PULSE->isChecked()) @@ -1914,6 +1956,17 @@ void QtSoundModem::PTTPortChanged(int Selected) Dev->PTTOn->setText(HamLibHost); Dev->PTTOn->setVisible(true); } + else if (strcmp(NewPTTPort, "FLRIG") == 0) + { + Dev->CM108Label->setVisible(true); + Dev->CM108Label->setText("FLRig Port"); + Dev->VIDPID->setText(QString::number(FLRigPort)); + Dev->VIDPID->setVisible(true); + Dev->PTTOnLab->setText("FLRig Host"); + Dev->PTTOnLab->setVisible(true); + Dev->PTTOn->setText(FLRigHost); + Dev->PTTOn->setVisible(true); + } else { Dev->RTSDTR->setVisible(true); @@ -1974,26 +2027,35 @@ void QtSoundModem::doDevices() newSoundMode = SoundMode; oldSoundMode = SoundMode; + oldSnoopMix = newSnoopMix = onlyMixSnoop; #ifdef WIN32 Dev->ALSA->setText("WaveOut"); Dev->OSS->setVisible(0); Dev->PULSE->setVisible(0); -#endif - + Dev->onlyMixSnoop->setVisible(0); + Dev->ALSA->setChecked(1); +#else if (SoundMode == 0) + { + Dev->onlyMixSnoop->setVisible(1); 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); +#endif + + Dev->onlyMixSnoop->setChecked(onlyMixSnoop); 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))); + connect(Dev->onlyMixSnoop, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); for (i = 0; i < PlaybackCount; i++) Dev->outputDevice->addItem(&PlaybackNames[i][0]); @@ -2025,6 +2087,8 @@ void QtSoundModem::doDevices() } Dev->inputDevice->setCurrentIndex(i); + Dev->txLatency->setText(QString::number(txLatency)); + Dev->Modem_1_Chan->setCurrentIndex(soundChannel[0]); Dev->Modem_2_Chan->setCurrentIndex(soundChannel[1]); Dev->Modem_3_Chan->setCurrentIndex(soundChannel[2]); @@ -2101,6 +2165,7 @@ void QtSoundModem::doDevices() //#endif Dev->PTTPort->addItem("HAMLIB"); + Dev->PTTPort->addItem("FLRIG"); for (const QString &info : items) { @@ -2186,7 +2251,7 @@ void QtSoundModem::deviceaccept() } } - if (oldSoundMode != newSoundMode) + if (oldSoundMode != newSoundMode || oldSnoopMix != newSnoopMix) { QMessageBox msgBox; @@ -2201,6 +2266,7 @@ void QtSoundModem::deviceaccept() if (i == QMessageBox::Ok) { SoundMode = newSoundMode; + onlyMixSnoop = newSnoopMix; saveSettings(); Closing = 1; @@ -2229,6 +2295,12 @@ void QtSoundModem::deviceaccept() cardChanged = 1; } + if (onlyMixSnoop != Dev->onlyMixSnoop->isChecked()) + { + onlyMixSnoop = Dev->onlyMixSnoop->isChecked(); + cardChanged = 1; + } + CaptureIndex = Dev->inputDevice->currentIndex(); Q = Dev->outputDevice->currentText(); @@ -2241,6 +2313,9 @@ void QtSoundModem::deviceaccept() PlayBackIndex = Dev->outputDevice->currentIndex(); + Q = Dev->txLatency->text(); + txLatency = Q.toInt(); + soundChannel[0] = Dev->Modem_1_Chan->currentIndex(); soundChannel[1] = Dev->Modem_2_Chan->currentIndex(); soundChannel[2] = Dev->Modem_3_Chan->currentIndex(); @@ -2343,6 +2418,12 @@ void QtSoundModem::deviceaccept() Q = Dev->PTTOn->text(); strcpy(HamLibHost, Q.toString().toUtf8()); } + else if (strcmp(PTTPort, "FLRIG") == 0) + { + FLRigPort = Q.toInt(); + Q = Dev->PTTOn->text(); + strcpy(FLRigHost, Q.toString().toUtf8()); + } Q = Dev->WaterfallMax->currentText(); newMax = Q.toInt(); @@ -2424,16 +2505,21 @@ void QtSoundModem::handleButton(int Port, int Type) + void QtSoundModem::doRestartWF() { - if (inWaterfall) + return; + + if (((tx_status[0] | tx_status[1] | tx_status[2] | tx_status[3]) != TX_SILENCE) || inWaterfall) { // in waterfall update thread wftimer->start(5000); return; } - + + wftimer->start(1000 * 300); + lockWaterfall = true; if (Firstwaterfall | Secondwaterfall) @@ -2580,7 +2666,7 @@ void RefreshLevel(unsigned int Level, unsigned int LevelR) RXLevel->setPixel(x, y, white); } } - RXLevelCopy->setPixmap(QPixmap::fromImage(*RXLevel)); +// RXLevelCopy->setPixmap(QPixmap::fromImage(*RXLevel)); for (x = 0; x < 150; x++) { @@ -2599,7 +2685,10 @@ void RefreshLevel(unsigned int Level, unsigned int LevelR) RXLevel2->setPixel(x, y, white); } } - RXLevel2Copy->setPixmap(QPixmap::fromImage(*RXLevel2)); + + emit t->setLevelImage(); + +/// RXLevel2Copy->setPixmap(QPixmap::fromImage(*RXLevel2)); } extern "C" unsigned char CurrentLevel; @@ -2888,7 +2977,9 @@ extern "C" void displayWaterfall() else WaterfallCopy->setAlignment(Qt::AlignTop | Qt::AlignLeft); - WaterfallCopy->setPixmap(QPixmap::fromImage(*Waterfall)); +// WaterfallCopy->setPixmap(QPixmap::fromImage(*Waterfall)); + + emit t->setWaterfallImage(); } extern "C" float aFFTAmpl[1024]; @@ -2904,6 +2995,9 @@ void doWaterfallThread(void * param) if (Configuring) return; + if (inWaterfall) + return; + inWaterfall = true; // don't allow restart waterfall if (snd_ch == 1 && UsingLeft == 0) // Only using right @@ -3003,7 +3097,9 @@ void doWaterfallThread(void * param) SMUpdateBusyDetector(snd_ch, RealOut, ImagOut); - // we always do fft so we can get centre freq and do busy detect. But only upodate waterfall if on display + + + // we always do fft so we can get centre freq and do busy detect. But only update waterfall if on display if (bm == 0) { @@ -3042,6 +3138,8 @@ void doWaterfallThread(void * param) // Scroll + + int TopLine = NextWaterfallLine[snd_ch]; int TopScanLine = WaterfallHeaderPixels; @@ -3053,10 +3151,24 @@ void doWaterfallThread(void * param) memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Line, 4096); if (NextWaterfallLine[snd_ch] > 79) NextWaterfallLine[snd_ch] = 0; + + // Sanity check + + if ((79 + TopScanLine) >= bm->height()) + { + printf("Invalid WFMaxLine %d \n", bm->height()); + exit(1); + } + for (int j = 79; j > 0; j--) { p = bm->scanLine(j + TopScanLine); + if (p == nullptr) + { + printf("Invalid WF Pointer \n"); + exit(1); + } memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen); TopLine++; if (TopLine > 79) @@ -3066,6 +3178,26 @@ void doWaterfallThread(void * param) inWaterfall = false; } +void QtSoundModem::setWaterfallImage() +{ + ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall)); +} + +void QtSoundModem::setLevelImage() +{ + RXLevelCopy->setPixmap(QPixmap::fromImage(*RXLevel)); + RXLevel2Copy->setPixmap(QPixmap::fromImage(*RXLevel2)); +} + +void QtSoundModem::setConstellationImage(int chan, int Qual) +{ + char QualText[64]; + sprintf(QualText, "Chan %c Qual = %d", chan + 'A', Qual); + QualLabel[chan]->setText(QualText); + constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan])); +} + + void QtSoundModem::changeEvent(QEvent* e) { @@ -3109,6 +3241,8 @@ void QtSoundModem::closeEvent(QCloseEvent *event) QtSoundModem::~QtSoundModem() { qDebug() << "Saving Settings"; + + closeTraceLog(); QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); mysettings.setValue("geometry", saveGeometry()); @@ -3134,7 +3268,7 @@ void QtSoundModem::show_grid() int snd_ch, i, num_rows, row_idx; QTableWidgetItem *item; - const char * msg; + const char * msg = ""; int speed_tx, speed_rx; @@ -3279,7 +3413,7 @@ void QtSoundModem::onTEselectionChanged() extern "C" int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count) { - // Subroutine to update bmpConstellation plot for PSK modes... + // Subroutine to update Constellation plot for PSK modes... // Skip plotting and calculations of intPSKPhase(0) as this is a reference phase (9/30/2014) float dblPhaseError; @@ -3347,13 +3481,121 @@ extern "C" int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags if (nonGUIMode == 0) { - char QualText[64]; - sprintf(QualText, "Chan %c Qual = %d", chan + 'A', intQuality); - QualLabel[chan]->setText(QualText); - constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan])); + emit t->setConstellationImage(chan, intQuality); +// char QualText[64]; +// sprintf(QualText, "Chan %c Qual = %d", chan + 'A', intQuality); +// QualLabel[chan]->setText(QualText); +// constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan])); } return intQuality; } +QFile tracefile("Tracelog.txt"); + + +extern "C" int openTraceLog() +{ + if (!tracefile.open(QIODevice::Append | QIODevice::Text)) + return 0; + + return 1; +} + +extern "C" qint64 writeTraceLog(char * Data, char Dirn) +{ + return tracefile.write(Data); +} + +extern "C" void closeTraceLog() +{ + tracefile.close(); +} + +extern "C" void debugTimeStamp(char * Text, char Dirn) +{ +#ifndef LOGTX + + if (Dirn == 'T') + return; + +#endif + +#ifndef LOGRX + + if (Dirn == 'R') + return; + +#endif + + + QTime Time(QTime::currentTime()); + QString String = Time.toString("hh:mm:ss.zzz"); + char Msg[2048]; + + sprintf(Msg, "%s %s\n", String.toUtf8().data(), Text); + qint64 ret = writeTraceLog(Msg, Dirn); +} + + + +// Timer functions need to run in GUI Thread + +extern "C" int SampleNo; + + +extern "C" int pttOnTime() +{ + return pttOnTimer.elapsed(); +} + +extern "C" void startpttOnTimer() +{ + pttOnTimer.start(); +} + + +extern "C" void StartWatchdog() +{ + // Get Monotonic clock for PTT drop time calculation + +#ifndef WIN32 + clock_gettime(CLOCK_MONOTONIC, &pttclk); +#endif + debugTimeStamp((char *)"PTT On", 'T'); + emit t->startWatchdog(); + pttOnTimer.start(); +} + +extern "C" void StopWatchdog() +{ + int txlenMs = (1000 * SampleNo / TX_Samplerate); + + Debugprintf("Samples Sent %d, Calc Time %d, PTT Time %d", SampleNo, txlenMs, pttOnTime()); + debugTimeStamp((char *)"PTT Off", 'T'); + closeTraceLog(); + openTraceLog(); + debugTimeStamp((char *)"Log Reopened", 'T'); + + emit t->stopWatchdog(); +} + + + +void QtSoundModem::StartWatchdog() +{ + PTTWatchdog->start(60 * 1000); +} + + void QtSoundModem::StopWatchdog() +{ + PTTWatchdog->stop(); +} + + + void QtSoundModem::PTTWatchdogExpired() + { + PTTWatchdog->stop(); + } + diff --git a/QtSoundModem.cpp.bak b/QtSoundModem.cpp.bak new file mode 100644 index 0000000..1dd6aa1 --- /dev/null +++ b/QtSoundModem.cpp.bak @@ -0,0 +1,3380 @@ +/* +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 +#include "UZ7HOStuff.h" + + +QImage *Constellation[4]; +QImage *Waterfall = 0; +QLabel *DCDLabel[4]; +QLineEdit *chanOffsetLabel[4]; +QImage *DCDLed[4]; + +QImage *RXLevel; +QImage *RXLevel2; + +QLabel *WaterfallCopy; + +QLabel * RXLevelCopy; +QLabel * RXLevel2Copy; + +QTextEdit * monWindowCopy; + +extern workerThread *t; +extern QtSoundModem * w; +extern QCoreApplication * a; + +QList Ports = QSerialPortInfo::availablePorts(); + +void saveSettings(); +void getSettings(); +void DrawModemFreqRange(); + +extern "C" void CloseSound(); +extern "C" void GetSoundDevices(); +extern "C" char modes_name[modes_count][21]; +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; +int NeedPSKRefresh; + +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 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); + 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 FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); + void dofft(short * in, float * outr, float * outi); + void init_raduga(); + void DrawFreqTicks(); + 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] = ""; +extern "C" char CWIDMark[32] = ""; +int CWIDInterval = 0; +int CWIDLeft = 0; +int CWIDRight = 0; +int CWIDType = 1; // on/off +bool afterTraffic = 0; +bool cwidtimerisActive = false; + +int WaterfallMin = 00; +int WaterfallMax = 6000; + +int Configuring = 0; +bool lockWaterfall = false; +bool inWaterfall = false; + +extern "C" int NeedWaterfallHeaders; +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 + +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); + +QRgb txText = qRgb(192, 0, 0); +QRgb rxText = qRgb(0, 0, 192); + +bool darkTheme = true; +bool minimizeonStart = true; + +// 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, 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; +QWidget * mythis; + + +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(); + + QRect r1 = ui.monWindow->geometry(); + QRect r2 = ui.centralWidget->geometry(); + + int modemBoxHeight = 34; + + int Width = r.width(); + int Height = r.height() - 25; + + int monitorTop; + int monitorHeight; + int sessionTop; + int sessionHeight = 0; + int waterfallsTop; + int waterfallsHeight = WaterfallImageHeight; + + 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; + + ui.Waterfall->setVisible(0); + + monitorTop = modemBoxHeight + 1; + + // Now have one waterfall label containing headers and waterfalls + + if (Firstwaterfall || Secondwaterfall) + ui.Waterfall->setVisible(1); + + if (AGWServ) + { + sessionTable->setVisible(true); + sessionHeight = 150; + } + else + { + sessionTable->setVisible(false); + } + + // if only displaying one Waterfall, change height of waterfall area + + if (UsingBothChannels == 0 || (Firstwaterfall == 0) || (Secondwaterfall == 0)) + { + waterfallsHeight /= 2; + } + + if ((Firstwaterfall == 0) && (Secondwaterfall == 0)) + waterfallsHeight = 0; + + monitorHeight = Height - sessionHeight - waterfallsHeight - modemBoxHeight; + waterfallsTop = Height - waterfallsHeight; + sessionTop = Height - (sessionHeight + waterfallsHeight); + + ui.monWindow->setGeometry(QRect(0, monitorTop, Width, monitorHeight)); + + if (AGWServ) + sessionTable->setGeometry(QRect(0, sessionTop, Width, sessionHeight)); + + if (waterfallsHeight) + ui.Waterfall->setGeometry(QRect(0, waterfallsTop, Width, waterfallsHeight + 2)); +} + +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) + Firstwaterfall = state; + + else if (Act == actWaterfall2) + Secondwaterfall = state; + + initWaterfall(Firstwaterfall | Secondwaterfall); + + saveSettings(); +} + +void QtSoundModem::initWaterfall(int state) +{ + if (state == 1) + { + // if (ui.Waterfall) + // { + // delete ui.Waterfall; + // ui.Waterfall = new QLabel(ui.centralWidget); + // } + WaterfallCopy = ui.Waterfall; + + Waterfall = new QImage(1024, WaterfallImageHeight + 2, QImage::Format_RGB32); + + NeedWaterfallHeaders = 1; + + } + else + { + delete(Waterfall); + Waterfall = 0; + } + + QSize Size(800, 602); // Not actually used, but Event constructor needs it + + QResizeEvent *event = new QResizeEvent(Size, Size); + QApplication::sendEvent(this, event); +} + +QRect PSKRect = { 100,100,100,100 }; + +QDialog * constellationDialog; +QLabel * constellationLabel[4]; +QLabel * QualLabel[4]; + +// Local copies + +QLabel *RXOffsetLabel; +QSlider *RXOffset; + +QFont Font; + +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 (soundChannel[i] && 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); +} + +QTimer *wftimer; + +QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) +{ + QString family; + int csize; + QFont::Weight weight; + + ui.setupUi(this); + + mythis = this; + + getSettings(); + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + + family = mysettings.value("FontFamily", "Courier New").toString(); + csize = mysettings.value("PointSize", 0).toInt(); + weight = (QFont::Weight)mysettings.value("Weight", 50).toInt(); + + Font = QFont(family); + Font.setPointSize(csize); + Font.setWeight(weight); + + QApplication::setFont(Font); + + 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]; + 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))); + } + + 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; + + if (FFTSize > 8191) + FFTSize = 8190; + + if (FFTSize & 1) // odd + FFTSize--; + + BinSize = 12000.0 / FFTSize; + + restoreGeometry(mysettings.value("geometry").toByteArray()); + restoreState(mysettings.value("windowState").toByteArray()); + + sessionTable = new QTableWidget(ui.centralWidget); + + 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"; + + mysetstyle(); + + 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())); + + actFont = new QAction("Setup Font", this); + actFont->setObjectName("actFont"); + setupMenu->addAction(actFont); + + connect(actFont, 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())); + + RXLevel = new QImage(150, 10, QImage::Format_RGB32); + RXLevel->fill(white); + ui.RXLevel->setPixmap(QPixmap::fromImage(*RXLevel)); + RXLevelCopy = ui.RXLevel; + + RXLevel2 = new QImage(150, 10, QImage::Format_RGB32); + RXLevel2->fill(white); + ui.RXLevel2->setPixmap(QPixmap::fromImage(*RXLevel2)); + RXLevel2Copy = ui.RXLevel2; + + 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; + + WaterfallCopy = ui.Waterfall; + + initWaterfall(Firstwaterfall | Secondwaterfall); + + monWindowCopy = ui.monWindow; + + ui.monWindow->document()->setMaximumBlockCount(10000); + +// connect(ui.monWindow, SIGNAL(selectionChanged()), this, SLOT(onTEselectionChanged())); + + 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))); + + 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]); + 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); + + QObject::connect(t, SIGNAL(startCWIDTimer()), this, SLOT(startCWIDTimerSlot()), 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); + + 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())); + + + if (CWIDInterval && afterTraffic == false) + cwidtimer->start(CWIDInterval * 60000); + + if (RSID_SetModem[0]) + { + RSID_WF = 1; + RSIDinitfft(); + } +// il2p_init(1); + + QTimer::singleShot(200, this, &QtSoundModem::updateFont); + +} + +void QtSoundModem::updateFont() +{ + QApplication::setFont(Font); +} + +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); + +extern "C" void checkforCWID() +{ + emit(t->startCWIDTimer()); +}; + +extern "C" void QtSoundModem::startCWIDTimerSlot() +{ + if (CWIDInterval && afterTraffic == 1 && cwidtimerisActive == false) + { + cwidtimerisActive = true; + QTimer::singleShot(CWIDInterval * 60000, this, &QtSoundModem::CWIDTimer); + } +} + +void QtSoundModem::CWIDTimer() +{ + cwidtimerisActive = false; + sendCWID(CWIDCall, CWIDType, 0); + calib_mode[0] = 4; +} + +void extSetOffset(int chan) +{ + char valChar[32]; + sprintf(valChar, "%d", chanOffset[chan]); + chanOffsetLabel[chan]->setText(valChar); + + NeedWaterfallHeaders = true; + + 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]); + } + if (NeedPSKRefresh) + { + NeedPSKRefresh = 0; + DoPSKWindows(); + } + + if (NeedWaterfallHeaders) + { + NeedWaterfallHeaders = 0; + + if (Waterfall) + { + Waterfall->fill(black); + DrawModemFreqRange(); + DrawFreqTicks(); + } + } + + 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 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) +{ + char Name[32]; + + strcpy(Name, sender()->objectName().toUtf8()); + + 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; + } + + 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; + } + + 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; + } + + 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; + } + + if (strcmp(Name, "centerA") == 0) + { + if (i > 299) + { + 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 > 299) + { + 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 > 299) + { + 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 > 299) + { + 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); + + NeedWaterfallHeaders = true; + + 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; + } + + if (strcmp(Name, "actFont") == 0) + { + bool ok; + Font = QFontDialog::getFont(&ok, QFont(Font, this)); + + if (ok) + { + // the user clicked OK and font is set to the font the user selected + QApplication::setFont(Font); + sessionTable->horizontalHeader()->setFont(Font); + + saveSettings(); + } + else + { + // the user canceled the dialog; font is set to the initial + // value, in this case Helvetica [Cronyx], 10 + +// QApplication::setFont(Font); + + } + 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", 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]); + 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->CRC_A->setChecked(il2p_crc[0]); + Dlg->CRC_B->setChecked(il2p_crc[1]); + Dlg->CRC_C->setChecked(il2p_crc[2]); + Dlg->CRC_D->setChecked(il2p_crc[3]); + + Dlg->CWIDCall->setText(CWIDCall); + Dlg->CWIDInterval->setText(QString::number(CWIDInterval)); + Dlg->CWIDMark->setText(CWIDMark); + + if (CWIDType) + Dlg->radioButton_2->setChecked(1); + else + Dlg->CWIDType->setChecked(1); + + Dlg->afterTraffic->setChecked(afterTraffic); + + 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(); + + AGW_Report_Modem_Change(0); + AGW_Report_Modem_Change(1); + AGW_Report_Modem_Change(2); + AGW_Report_Modem_Change(3); + + 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->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(); + + 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(); + + il2p_crc[0] = Dlg->CRC_A->isChecked(); + il2p_crc[1] = Dlg->CRC_B->isChecked(); + il2p_crc[2] = Dlg->CRC_C->isChecked(); + il2p_crc[3] = Dlg->CRC_D->isChecked(); + + 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()); + strcpy(CWIDMark, Dlg->CWIDMark->text().toUtf8().toUpper()); + CWIDInterval = Dlg->CWIDInterval->text().toInt(); + CWIDType = Dlg->radioButton_2->isChecked(); + + afterTraffic = Dlg->afterTraffic->isChecked(); + + if (CWIDInterval && afterTraffic == false) + 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]); + } + +/* + Q = Dlg->LPFWidthA->text(); + lpf[0] = Q.toInt(); + + Q = Dlg->LPFWidthB->text(); + lpf[1] = Q.toInt(); + + Q = Dlg->LPFWidthC->text(); + lpf[2] = Q.toInt(); + + Q = Dlg->LPFWidthD->text(); + lpf[3] = Q.toInt(); +*/ + +} + +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); + } + + // If we are using a user specifed device add it + + i = Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString); + + if (i == -1) + { + // Add our device to list + + Dev->PTTPort->insertItem(0, PTTPort); + i = Dev->PTTPort->findText(PTTPort, Qt::MatchContains); + } + + Dev->PTTPort->setCurrentIndex(i); + + PTTPortChanged(0); // Force reevaluation + + Dev->txRotation->setChecked(TX_rotate); + Dev->DualPTT->setChecked(DualPTT); + + Dev->multiCore->setChecked(multiCore); + Dev->darkTheme->setChecked(darkTheme); + + Dev->WaterfallMin->setCurrentIndex(Dev->WaterfallMin->findText(QString::number(WaterfallMin), Qt::MatchFixedString)); + Dev->WaterfallMax->setCurrentIndex(Dev->WaterfallMax->findText(QString::number(WaterfallMax), Qt::MatchFixedString)); + + QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept())); + QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject())); + + UI.exec(); + +} + +void QtSoundModem::mysetstyle() +{ + if (darkTheme) + { + qApp->setStyleSheet( + "QWidget {color: white; background-color: black}" + "QTabBar::tab {color: rgb(127, 127, 127); background-color: black}" + "QTabBar::tab::selected {color: white}" + "QPushButton {border-style: outset; border-width: 2px; border-color: rgb(127, 127, 127)}" + "QPushButton::default {border-style: outset; border-width: 2px; border-color: white}"); + + sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(40, 40, 40) }"); + + txText = qRgb(255, 127, 127); + rxText = qRgb(173, 216, 230); + } + else + { + qApp->setStyleSheet(""); + + sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(224, 224, 224) }"); + + txText = qRgb(192, 0, 0); + rxText = qRgb(0, 0, 192); + } +} + +void QtSoundModem::deviceaccept() +{ + QVariant Q = Dev->inputDevice->currentText(); + int cardChanged = 0; + char portString[32]; + int newMax; + int newMin; + + 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(); + + char temp[256]; + + strcpy(temp, Q.toString().toUtf8()); + + if (strlen(temp)) + strcpy(PTTPort, temp); + + DualPTT = Dev->DualPTT->isChecked(); + TX_rotate = Dev->txRotation->isChecked(); + multiCore = Dev->multiCore->isChecked(); + darkTheme = Dev->darkTheme->isChecked(); + mysetstyle(); + + 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()); + } + + Q = Dev->WaterfallMax->currentText(); + newMax = Q.toInt(); + + Q = Dev->WaterfallMin->currentText(); + newMin = Q.toInt(); + + if (newMax != WaterfallMax || newMin != WaterfallMin) + { + QMessageBox msgBox; + + msgBox.setText("QtSoundModem must restart to change Waterfall range. Program will close if you hit Ok\n" + "It may take up to 30 seconds for the program to start for the first time after changing settings"); + + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + + int i = msgBox.exec(); + + if (i == QMessageBox::Ok) + { + Configuring = 1; // Stop Waterfall + + WaterfallMax = newMax; + WaterfallMin = newMin; + saveSettings(); + Closing = 1; + return; + } + } + + + ClosePTTPort(); + OpenPTTPort(); + + NeedWaterfallHeaders = true; + + 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%s/%d%s", VersionString, AGWPort, AGWServ ? "*": "", KISSPort, KISSServ ? "*" : ""); + 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 (inWaterfall) + { + // in waterfall update thread + + wftimer->start(5000); + return; + } + + lockWaterfall = true; + + if (Firstwaterfall | Secondwaterfall) + { + initWaterfall(0); + initWaterfall(1); + } + + delete(RXLevel); + delete(ui.RXLevel); + ui.RXLevel = new QLabel(ui.centralWidget); + RXLevelCopy = ui.RXLevel; + ui.RXLevel->setGeometry(QRect(780, 14, 150, 11)); + ui.RXLevel->setFrameShape(QFrame::Box); + ui.RXLevel->setFrameShadow(QFrame::Sunken); + + delete(RXLevel2); + delete(ui.RXLevel2); + + ui.RXLevel2 = new QLabel(ui.centralWidget); + RXLevel2Copy = ui.RXLevel2; + + ui.RXLevel2->setGeometry(QRect(780, 23, 150, 11)); + ui.RXLevel2->setFrameShape(QFrame::Box); + ui.RXLevel2->setFrameShadow(QFrame::Sunken); + + RXLevel = new QImage(150, 10, QImage::Format_RGB32); + RXLevel->fill(cyan); + + RXLevel2 = new QImage(150, 10, QImage::Format_RGB32); + RXLevel2->fill(white); + + ui.RXLevel->setVisible(1); + ui.RXLevel2->setVisible(1); + + lockWaterfall = false; +} + + +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->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->setPixel(i, val, Yellow); + if (val < 62) + Waterfall->setPixel(i, val + 1, Gold); + Data++; + } + + ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall)); + +} + +void RefreshLevel(unsigned int Level, unsigned int LevelR) +{ + // Redraw the RX Level Bar Graph + + unsigned int x, y; + + for (x = 0; x < 150; x++) + { + for (y = 0; y < 10; y++) + { + if (x < Level) + { + if (Level < 50) + RXLevel->setPixel(x, y, yellow); + else if (Level > 135) + RXLevel->setPixel(x, y, red); + else + RXLevel->setPixel(x, y, green); + } + else + RXLevel->setPixel(x, y, white); + } + } + RXLevelCopy->setPixmap(QPixmap::fromImage(*RXLevel)); + + for (x = 0; x < 150; x++) + { + for (y = 0; y < 10; y++) + { + if (x < LevelR) + { + if (LevelR < 50) + RXLevel2->setPixel(x, y, yellow); + else if (LevelR > 135) + RXLevel2->setPixel(x, y, red); + else + RXLevel2->setPixel(x, y, green); + } + else + RXLevel2->setPixel(x, y, white); + } + } + RXLevel2Copy->setPixmap(QPixmap::fromImage(*RXLevel2)); +} + +extern "C" unsigned char CurrentLevel; +extern "C" unsigned char CurrentLevelR; + +void QtSoundModem::RefreshWaterfall(int snd_ch, unsigned char * Data) +{ + int j; + unsigned char * Line; + int len = Waterfall->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->scanLine(j); + memcpy(Line, &WaterfallLines[snd_ch][TopLine++][0], len); + if (TopLine > 63) + TopLine = 0; + } + + ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall)); +} + +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(txText); + else + monWindowCopy->setTextColor(rxText); + + 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 DrawFreqTicks() +{ + if (nonGUIMode) + return; + + // Draw Frequency Markers on waterfall header(s); + + int x, i; + char Textxx[20]; + QImage * bm = Waterfall; + + QPainter qPainter(bm); + qPainter.setBrush(Qt::black); + qPainter.setPen(Qt::white); + + int Chan; + +#ifdef WIN32 + int Top = 3; +#else + int Top = 4; +#endif + int Base = 0; + + for (Chan = 0; Chan < 2; Chan++) + { + if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1))) + sprintf(Textxx, "Right"); + else + sprintf(Textxx, "Left"); + + qPainter.drawText(2, Top, 100, 20, 0, Textxx); + + // We drew markers every 100 Hz or 100 / binsize pixels + + int Markers = ((WaterfallMax - WaterfallMin) / 100) + 5; // Number of Markers to draw + int Freq = WaterfallMin; + float PixelsPerMarker = 100.0 / BinSize; + + for (i = 0; i < Markers; i++) + { + x = round(PixelsPerMarker * i); + if (x < 1025) + { + if ((Freq % 500) == 0) + qPainter.drawLine(x, Base + 22, x, Base + 15); + else + qPainter.drawLine(x, Base + 22, x, Base + 18); + + if ((Freq % 500) == 0) + { + sprintf(Textxx, "%d", Freq); + + if (x < 924) + qPainter.drawText(x - 12, Top, 100, 20, 0, Textxx); + } + } + Freq += 100; + } + + if (UsingBothChannels == 0) + break; + + Top += WaterfallTotalPixels; + Base = WaterfallTotalPixels; + } + +} + +// These draws the frequency Markers on the Waterfall + +void DrawModemFreqRange() +{ + if (nonGUIMode) + return; + + // Draws the modem freq bars on waterfall header(s) + + + int x1, x2, k, pos1, pos2, pos3; + QImage * bm = Waterfall; + + QPainter qPainter(bm); + qPainter.setBrush(Qt::NoBrush); + qPainter.setPen(Qt::white); + + int Chan; + int LRtoDisplay = LEFT; + int top = 0; + + for (Chan = 0; Chan < 2; Chan++) + { + if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1))) + LRtoDisplay = RIGHT; + + // bm->fill(black); + + // qPainter.fillRect(top, 23, 1024, 10, Qt::black); + + // We drew markers every 100 Hz or 100 / binsize pixels + + float PixelsPerHz = 1.0 / BinSize; + k = 26 + top; + + // 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 (soundChannel[i] != LRtoDisplay) + continue; + + pos1 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) - 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5; + pos2 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) + 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5; + pos3 = roundf(((rxOffset + chanOffset[i] + rx_freq[i])) - WaterfallMin * PixelsPerHz); + 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] - WaterfallMin) * PixelsPerHz); + 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); + } + + k += 3; + } + if (UsingBothChannels == 0) + break; + + LRtoDisplay = RIGHT; + top = WaterfallTotalPixels; + } +} + + +void doWaterfallThread(void * param); + +extern "C" void doWaterfall(int snd_ch) +{ + if (nonGUIMode) + return; + + if (Closing) + return; + + if (lockWaterfall) + return; + +// if (multiCore) // Run modems in separate threads +// _beginthread(doWaterfallThread, 0, xx); +// else + doWaterfallThread((void *)(size_t)snd_ch); + +} + +extern "C" void displayWaterfall() +{ + // if we are using both channels but only want right need to extract correct half of Image + + if (Waterfall == nullptr) + return; + + if (UsingBothChannels && (Firstwaterfall == 0)) + WaterfallCopy->setAlignment(Qt::AlignBottom | Qt::AlignLeft); + else + WaterfallCopy->setAlignment(Qt::AlignTop | Qt::AlignLeft); + + WaterfallCopy->setPixmap(QPixmap::fromImage(*Waterfall)); +} + +extern "C" float aFFTAmpl[1024]; +extern "C" void SMUpdateBusyDetector(int LR, float * Real, float *Imag); + +void doWaterfallThread(void * param) +{ + int snd_ch = (int)(size_t)param; + + if (lockWaterfall) + return; + + if (Configuring) + return; + + inWaterfall = true; // don't allow restart waterfall + + if (snd_ch == 1 && UsingLeft == 0) // Only using right + snd_ch = 0; // Samples are in first buffer + + QImage * bm = Waterfall; + + int i; + single mag; + UCHAR * p; + UCHAR Line[4096] = ""; // 4 bytes per pixel + + int lineLen, Start, End; + word hFFTSize; + Byte n; + float RealOut[8192] = { 0 }; + float ImagOut[8192]; + + + RefreshLevel(CurrentLevel, CurrentLevelR); // Signal Level + + 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 / BinSize); // First and last bins to process + End = (WaterfallMax / BinSize); + + 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); + } + } + + SMUpdateBusyDetector(snd_ch, RealOut, ImagOut); + + + + // we always do fft so we can get centre freq and do busy detect. But only update waterfall if on display + + if (bm == 0) + { + inWaterfall = false; + return; + } + if ((Firstwaterfall == 0 && snd_ch == 0) || (Secondwaterfall == 0 && snd_ch == 1)) + { + inWaterfall = false; + return; + } + + p = Line; + lineLen = 4096; + + 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]; + int TopScanLine = WaterfallHeaderPixels; + + if (snd_ch) + TopScanLine += WaterfallTotalPixels; + + // 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; + + // Sanity check + + int WFMaxLine = bm->height(); + + + if ((79 + TopScanLine) >= bm->height()) + { + printf("Invalid WFMaxLine %d \n", bm->height()); + exit(1); + } + + + for (int j = 79; j > 0; j--) + { + p = bm->scanLine(j + TopScanLine); + if (p == nullptr) + { + printf("Invalid WF Pointer \n"); + exit(1); + } + memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen); + TopLine++; + if (TopLine > 79) + TopLine = 0; + } + + inWaterfall = false; +} + + +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(); +} + +#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; + + if (Count == 0) + return 0; + + if (nonGUIMode == 0) + { + Constellation[chan]->fill(black); + + for (i = 0; i < 120; i++) + { + Constellation[chan]->setPixel(xCenter, i, cyan); + Constellation[chan]->setPixel(i, xCenter, cyan); + } + } + 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; + + if (nonGUIMode == 0) + { + 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 + + if (nonGUIMode == 0) + { + char QualText[64]; + sprintf(QualText, "Chan %c Qual = %d", chan + 'A', intQuality); + QualLabel[chan]->setText(QualText); + constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan])); + } + return intQuality; +} + + + diff --git a/QtSoundModem.h b/QtSoundModem.h index 626ddd9..b02f9c2 100644 --- a/QtSoundModem.h +++ b/QtSoundModem.h @@ -74,8 +74,14 @@ private slots: void preEmphAllDChanged(int state); void menuChecked(); void onTEselectionChanged(); + void StartWatchdog(); + void StopWatchdog(); + void PTTWatchdogExpired(); void clickedSlot(); void startCWIDTimerSlot(); + void setWaterfallImage(); + void setLevelImage(); + void setConstellationImage(int chan, int Qual); protected: diff --git a/QtSoundModem.vcxproj b/QtSoundModem.vcxproj index 7f39cd1..765fd37 100644 --- a/QtSoundModem.vcxproj +++ b/QtSoundModem.vcxproj @@ -383,6 +383,7 @@ + @@ -406,7 +407,6 @@ - diff --git a/QtSoundModem.vcxproj.filters b/QtSoundModem.vcxproj.filters index c50daf6..2d05c65 100644 --- a/QtSoundModem.vcxproj.filters +++ b/QtSoundModem.vcxproj.filters @@ -98,9 +98,6 @@ Source Files - - Source Files - Source Files @@ -140,14 +137,14 @@ Source Files + + Generated Files + Header Files - - Header Files - Header Files @@ -187,4 +184,9 @@ Resource Files + + + Header Files + + \ No newline at end of file diff --git a/QtSoundModem.vcxproj.user b/QtSoundModem.vcxproj.user index 059beae..3d36629 100644 --- a/QtSoundModem.vcxproj.user +++ b/QtSoundModem.vcxproj.user @@ -20,15 +20,15 @@ WindowsLocalDebugger - 2023-11-24T17:50:14.6138870Z + 2024-06-21T13:50:20.1736205Z - 2023-11-24T17:50:14.7454647Z + 2024-06-21T13:50:20.3245934Z - 2023-11-24T17:50:14.8872599Z + 2024-06-21T13:50:20.4555992Z - 2023-11-24T17:50:15.0594798Z + 2024-06-21T13:50:20.6366033Z \ No newline at end of file diff --git a/QtSoundModem_resource.aps b/QtSoundModem_resource.aps new file mode 100644 index 0000000..74c8a00 Binary files /dev/null and b/QtSoundModem_resource.aps differ diff --git a/SMMain.c b/SMMain.c index 24f6159..bf93779 100644 --- a/SMMain.c +++ b/SMMain.c @@ -170,7 +170,6 @@ void SampleSink(int LR, short Sample) { DMABuffer[2 * Number] = Sample; DMABuffer[1 + 2 * Number] = Sample; - } else { @@ -537,15 +536,21 @@ void doCalib(int Chan, int Act) if (Chan == 1 && calib_mode[0]) return; - calib_mode[Chan] = Act; - if (Act == 0) { + calib_mode[Chan] = 0; tx_status[Chan] = TX_SILENCE; // Stop TX Flush(); RadioPTT(Chan, 0); Debugprintf("Stop Calib"); } + else + { + if (calib_mode[Chan] == 0) + SampleNo = 0; + + calib_mode[Chan] = Act; + } } int Freq_Change(int Chan, int Freq) @@ -681,7 +686,7 @@ void DoTX(int Chan) if (tx_status[Chan] == TX_NO_DATA) { Flush(); - Debugprintf("TX Complete"); + Debugprintf("TX Complete %d", SampleNo); RadioPTT(Chan, 0); Continuation[Chan] = 0; @@ -762,7 +767,7 @@ void DoTX(int Chan) return; } - Debugprintf("TX Complete"); + Debugprintf("TX Complete %d", SampleNo); RadioPTT(Chan, 0); Continuation[Chan] = 0; @@ -954,7 +959,10 @@ BOOL useGPIO = FALSE; BOOL gotGPIO = FALSE; int HamLibPort = 4532; -char HamLibHost[32] = "192.168.1.14"; +char HamLibHost[32] = "127.0.0.1"; + +int FLRigPort = 12345; +char FLRigHost[32] = "127.0.0.1"; char CM108Addr[80] = ""; @@ -1128,6 +1136,12 @@ void OpenPTTPort() HAMLIBSetPTT(0); // to open port return; } + else if (stricmp(PTTPort, "FLRIG") == 0) + { + PTTMode |= PTTFLRIG; + FLRigSetPTT(0); // to open port + return; + } else // Not GPIO { @@ -1207,6 +1221,7 @@ void CM108_set_ptt(int PTTState) float amplitudes[4] = { 32000, 32000, 32000, 32000 }; extern float amplitude; +void startpttOnTimer(); void RadioPTT(int snd_ch, BOOL PTTState) { @@ -1216,11 +1231,13 @@ void RadioPTT(int snd_ch, BOOL PTTState) { txmax = txmin = 0; amplitude = amplitudes[snd_ch]; + StartWatchdog(); } else { Debugprintf("Output peaks = %d, %d, amp %f", txmin, txmax, amplitude); amplitudes[snd_ch] = amplitude; + StopWatchdog(); } #ifdef __ARM_ARCH @@ -1230,7 +1247,7 @@ void RadioPTT(int snd_ch, BOOL PTTState) gpioWrite(pttGPIOPinR, (pttGPIOInvert ? (1 - PTTState) : (PTTState))); else gpioWrite(pttGPIOPin, (pttGPIOInvert ? (1 - PTTState) : (PTTState))); - + startpttOnTimer(); return; } @@ -1239,17 +1256,29 @@ void RadioPTT(int snd_ch, BOOL PTTState) if ((PTTMode & PTTCM108)) { CM108_set_ptt(PTTState); + startpttOnTimer(); return; } if ((PTTMode & PTTHAMLIB)) { HAMLIBSetPTT(PTTState); + startpttOnTimer(); return; } - if (hPTTDevice == 0) - return; + if ((PTTMode & PTTFLRIG)) + { + FLRigSetPTT(PTTState); + startpttOnTimer(); + return; + } + + if (hPTTDevice == 0) + { + startpttOnTimer(); + return; + } if ((PTTMode & PTTCAT)) { if (PTTState) @@ -1277,6 +1306,7 @@ void RadioPTT(int snd_ch, BOOL PTTState) COMClearRTS(hPTTDevice); } } + startpttOnTimer(); } diff --git a/UZ7HOStuff.h b/UZ7HOStuff.h index b27e76f..c4146ff 100644 --- a/UZ7HOStuff.h +++ b/UZ7HOStuff.h @@ -4,9 +4,12 @@ // My port of UZ7HO's Soundmodem // -#define VersionString "0.0.0.72 Beta 1" +#define VersionString "0.0.0.72" #define VersionBytes {0, 0, 0, 72} +//#define LOGTX +//#define LOGRX + // Added FX25. 4x100 FEC and V27 not Working and disabled // 0.8 V27 now OK. @@ -179,6 +182,25 @@ // Report and set fx.25 and il2p flags to/from BPQ // .72 Fix IL2P for RUH modems +// Fix crash when closing in non-gui mode +// Fix loop in chk_dcd1 +// Change method of timing PTT +// Add FLRIG PTT Support + +// Beta 13 revert to new ptt timing +// Beta 14 Add display of mix/snoop devices + +// Change phase map of QPSK3600 mode to match Nino TNC + + + +// As far as I can see txtail is only there to make sure all bits get through the tx filter, +// so it shouldn't really matter what is sent. Code worked in characters, so resolution of txtail +// is 24 mS at 300 baud. I don't see why I can't work in bits, or even samples. Any reason why I shouldn't send +// a single tone during tail? . +// +// I'm currently sending reversals with timing resolution of bits (~3 mS at 300 baud) + #include @@ -511,6 +533,7 @@ typedef struct TAX25Port_t #define PTTCAT 4 #define PTTCM108 8 #define PTTHAMLIB 16 +#define PTTFLRIG 32 // Status flags @@ -654,9 +677,9 @@ extern int SendSize; #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 1800 +#define MODEM_Q3600_TXBPF 2000 +#define MODEM_Q3600_LPF 600 #define MODEM_Q3600_BPF_TAP 256 #define MODEM_Q3600_LPF_TAP 128 // @@ -947,6 +970,8 @@ extern int PID; extern char CM108Addr[80]; extern int HamLibPort; extern char HamLibHost[]; +extern int FLRigPort; +extern char FLRigHost[]; extern int SCO; extern int DualPTT; @@ -1060,6 +1085,7 @@ void AGW_Raw_monitor(int snd_ch, string * data); // Delphi emulation functions string * Strings(TStringList * Q, int Index); +void replaceString(TStringList * Q, int Index, string * item); void Clear(TStringList * Q); int Count(TStringList * List); @@ -1140,6 +1166,10 @@ struct il2p_context_s { extern int NeedWaterfallHeaders; +#define stringAdd(s1, s2, c) mystringAdd(s1, s2, c, __FILE__, __LINE__) + +string * mystringAdd(string * Msg, UCHAR * Chars, int Count, char * FILE, int LINE); + #ifdef __cplusplus } #endif \ No newline at end of file diff --git a/UZ7HOUtils.c b/UZ7HOUtils.c index a2a8fe7..70d27e2 100644 --- a/UZ7HOUtils.c +++ b/UZ7HOUtils.c @@ -96,12 +96,24 @@ void freeString(string * Msg) string * Strings(TStringList * Q, int Index) { + // Gets string at index in stringlist + if (Index >= Q->Count) return NULL; return Q->Items[Index]; } +void replaceString(TStringList * Q, int Index, string * item) +{ + // Gets string at index in stringlist + + if (Index >= Q->Count) + return; + + Q->Items[Index] = item; +} + int Add(TStringList * Q, string * Entry) { Q->Items = realloc(Q->Items,(Q->Count + 1) * sizeof(void *)); @@ -166,16 +178,27 @@ void setlength(string * Msg, int Count) Msg->Length = Count; } -string * stringAdd(string * Msg, UCHAR * Chars, int Count) +string * mystringAdd(string * Msg, UCHAR * Chars, int Count, char * FILE, int LINE) { // Add Chars to string + if (Count < 0 || Count > 65536) + { + printf("stringAdd Strange Count %d called from %s %d\r\n", Count, FILE, LINE); + } + if (Msg->Length + Count > Msg->AllocatedLength) { Msg->AllocatedLength += Count + 256; Msg->Data = realloc(Msg->Data, Msg->AllocatedLength); } + if (Msg->Data == 0) + { + printf("realloc failed\r\n"); + exit(1); + } + memcpy(&Msg->Data[Msg->Length], Chars, Count); Msg->Length += Count; diff --git a/Waveout.c b/Waveout.c index 6b5c987..22f05d3 100644 --- a/Waveout.c +++ b/Waveout.c @@ -95,6 +95,8 @@ int PlaybackCount = 0; char CaptureNames[16][256]= {""}; char PlaybackNames[16][256]= {""}; +int txLatency; + WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 2, 12000, 48000, 4, 16, 0 }; HWAVEOUT hWaveOut = 0; @@ -190,6 +192,9 @@ void txSleep(int mS) { // called while waiting for next TX buffer. Run background processes + if (mS < 0) + return; + while (mS > 50) { if (SoundMode == 3) @@ -356,6 +361,7 @@ void GetSoundDevices() HANDLE hStdin; +int onlyMixSnoop = 0; int InitSound(BOOL Report) { @@ -563,7 +569,7 @@ void PollReceivedSamples() lastlevelGUI = Now; - if ((Now - lastlevelreport) > 10000) // 10 Secs + if ((Now - lastlevelreport) > 60000) // 60 Secs { char HostCmd[64]; lastlevelreport = Now; @@ -703,6 +709,8 @@ extern int Number; // Number of samples waiting to be sent // Subroutine to add trailer before filtering +extern int SampleNo; + void SoundFlush() { // Append Trailer then wait for TX to complete diff --git a/ax25_agw.c b/ax25_agw.c index ec73b2b..12526ff 100644 --- a/ax25_agw.c +++ b/ax25_agw.c @@ -189,7 +189,7 @@ void AGW_del_socket(void * socket) void AGW_add_socket(void * socket) { AGWUser * User = (struct AGWUser_t *)malloc(sizeof(struct AGWUser_t)); // One Client - memset(User, 0, sizeof(struct AGWUser_t)); + AGWUsers = realloc(AGWUsers, (AGWConCount + 1) * sizeof(void *)); diff --git a/ax25_demod.c b/ax25_demod.c index f1b0b36..06d66f5 100644 --- a/ax25_demod.c +++ b/ax25_demod.c @@ -335,7 +335,7 @@ void chk_dcd1(int snd_ch, int buf_size) // ? does this work as Andy passes aborted frames to decoder Byte port; - word i; + int i; single tick; word active; boolean ind_dcd; @@ -423,9 +423,9 @@ void chk_dcd1(int snd_ch, int buf_size) if (TX_rotate) { - for (int i = 0; i < 4; i++) + for (int n = 0; n < 4; n++) { - if (snd_status[i] == SND_TX) + if (snd_status[n] == SND_TX) dcd[snd_ch] = TRUE; } } @@ -435,7 +435,7 @@ void chk_dcd1(int snd_ch, int buf_size) if (!dcd[snd_ch] && resptime_tick[snd_ch] >= resptime[snd_ch]) { - i = 0; + int n = 0; port = new_tx_port[snd_ch]; do @@ -463,9 +463,9 @@ void chk_dcd1(int snd_ch, int buf_size) if (all_frame_buf[snd_ch].Count > 0) new_tx_port[snd_ch] = port; - i++; + n++; - } while (all_frame_buf[snd_ch].Count == 0 && i < port_num); + } while (all_frame_buf[snd_ch].Count == 0 && n < port_num); // Add KISS frames @@ -475,7 +475,7 @@ void chk_dcd1(int snd_ch, int buf_size) if (all_frame_buf[snd_ch].Count > 0) { - for (n = 0; n < all_frame_buf[snd_ch].Count; n++) + for (int n = 0; n < all_frame_buf[snd_ch].Count; n++) { KISS_on_data_out(snd_ch, Strings(&all_frame_buf[snd_ch], n), 1); // Mon TX } @@ -485,14 +485,14 @@ void chk_dcd1(int snd_ch, int buf_size) if (KISS.buffer[snd_ch].Count > 0) { - for (n = 0; n < KISS.buffer[snd_ch].Count; n++) + for (int k = 0; k < KISS.buffer[snd_ch].Count; k++) { if (AGWServ) AGW_Raw_monitor(snd_ch, Strings(&KISS.buffer[snd_ch], n)); // Need to add copy as clear will free original - Add(&all_frame_buf[snd_ch], duplicateString(Strings(&KISS.buffer[snd_ch], n))); + Add(&all_frame_buf[snd_ch], duplicateString(Strings(&KISS.buffer[snd_ch], k))); } Clear(&KISS.buffer[snd_ch]); } diff --git a/ax25_mod.c b/ax25_mod.c index f8fd4bc..c8b9eef 100644 --- a/ax25_mod.c +++ b/ax25_mod.c @@ -866,7 +866,6 @@ int get_new_bit_tail(UCHAR snd_ch, UCHAR bit) } else { - Debugprintf("End TXTAIL %d", SampleNo); tx_status[snd_ch] = TX_WAIT_BPF; } @@ -881,7 +880,6 @@ int get_new_bit_tail(UCHAR snd_ch, UCHAR bit) } else { - Debugprintf("End TXTAIL %d", SampleNo); tx_status[snd_ch] = TX_WAIT_BPF; } break; @@ -1199,7 +1197,7 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) tx_status[snd_ch] = TX_FRAME; if (tx_status[snd_ch] == TX_TAIL) - bit = get_new_bit_tail(snd_ch, bit); + bit = il2p_get_new_bit_tail(snd_ch, bit); if (tx_status[snd_ch] == TX_FRAME) bit = il2p_get_new_bit(snd_ch, bit); @@ -1230,6 +1228,7 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) *bitptr = tx_nrzi(snd_ch, bit); } + } // BPSK Mode @@ -1240,13 +1239,10 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) 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 + // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME if (tx_status[snd_ch] == TX_DELAY) if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) @@ -1255,7 +1251,12 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) bit = get_new_bit_delay(snd_ch, bit); if (tx_status[snd_ch] == TX_TAIL) - bit = get_new_bit_tail(snd_ch, bit); + { + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + bit = il2p_get_new_bit_tail(snd_ch, bit); + else + bit = get_new_bit_tail(snd_ch, bit); + } if (tx_status[snd_ch] == TX_FRAME) { @@ -1266,6 +1267,7 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) else bit = get_new_bit(snd_ch, bit); } + // ?? *bitptr = tx_nrzi(snd_ch, bit); if (bit == 0) @@ -1297,7 +1299,7 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) 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); + bit = il2p_get_new_bit_tail(snd_ch, bit); if (tx_status[snd_ch] == TX_FRAME) bit = il2p_get_new_bit(snd_ch, bit); @@ -1372,7 +1374,6 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) if (tx_status[snd_ch] == TX_SILENCE) { tx_delay_cnt[snd_ch] = 0; - Debugprintf("Start TXD"); tx_status[snd_ch] = TX_DELAY; } @@ -1382,7 +1383,7 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) 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); + bit = il2p_get_new_bit_tail(snd_ch, bit); if (tx_status[snd_ch] == TX_FRAME) bit = il2p_get_new_bit(snd_ch, bit); @@ -1490,7 +1491,7 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) 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); + bit = il2p_get_new_bit_tail(snd_ch, bit); if (tx_status[snd_ch] == TX_FRAME) bit = il2p_get_new_bit(snd_ch, bit); @@ -1590,7 +1591,6 @@ float make_samples(unsigned char snd_ch, unsigned char * bitptr) if (tx_status[snd_ch] == TX_SILENCE) { tx_delay_cnt[snd_ch] = 0; - Debugprintf("Start TXD"); tx_status[snd_ch] = TX_DELAY; } diff --git a/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h b/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h new file mode 100644 index 0000000..62e1baa --- /dev/null +++ b/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h @@ -0,0 +1,12 @@ +#define _MSC_EXTENSIONS +#define _INTEGRAL_MAX_BITS 64 +#define _MSC_VER 1916 +#define _MSC_FULL_VER 191627051 +#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-notpi4-64.h b/debug/moc_predefs-notpi4-64.h new file mode 100644 index 0000000..6d708a1 --- /dev/null +++ b/debug/moc_predefs-notpi4-64.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/debug/moc_predefs-skigdebian.h b/debug/moc_predefs-skigdebian.h new file mode 100644 index 0000000..6d708a1 --- /dev/null +++ b/debug/moc_predefs-skigdebian.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/devicesDialog.ui b/devicesDialog.ui index b090d6b..9fef8ab 100644 --- a/devicesDialog.ui +++ b/devicesDialog.ui @@ -60,7 +60,7 @@ 108 25 - 261 + 371 22
@@ -73,7 +73,7 @@ 108 55 - 261 + 371 22
@@ -99,7 +99,7 @@ 13 55 - 71 + 91 19
@@ -159,63 +159,37 @@ Minimize window on startup
- + - 108 - 124 + 220 + 140 63 20 - 12000 - - - - - - 304 - 124 - 63 - 20 - - - - 12000 + 0 13 - 125 - 91 + 140 + 201 18 - TX SampleRate - - - - - - 190 - 125 - 91 - 18 - - - - RX SampleRate + Soundcard TX latency mS - 70 - 164 + 72 + 170 86 20 @@ -245,7 +219,7 @@ 12 - 166 + 172 61 16 @@ -258,7 +232,7 @@ 180 - 164 + 170 86 20 @@ -283,7 +257,7 @@ 165 - 166 + 172 21 16 @@ -352,7 +326,7 @@ 168 90 - 91 + 131 20 @@ -406,7 +380,7 @@ 290 - 164 + 170 86 20 @@ -431,7 +405,7 @@ 272 - 166 + 172 21 16 @@ -444,7 +418,7 @@ 385 - 166 + 172 21 16 @@ -457,7 +431,7 @@ 400 - 164 + 170 86 20 @@ -595,6 +569,19 @@ Dark Theme + + + + 30 + 110 + 421 + 21 + + + + Only Mix/Snoop Devices (needs custom .asoundrc) + + diff --git a/dw9600.c b/dw9600.c index 8e6db00..da9b3c7 100644 --- a/dw9600.c +++ b/dw9600.c @@ -23,6 +23,10 @@ typedef struct TStringList_T #include #include "dw9600.h" +#define stringAdd(s1, s2, c) mystringAdd(s1, s2, c, __FILE__, __LINE__) + +string * mystringAdd(string * Msg, unsigned char * Chars, int Count, char * FILE, int LINE); + extern int fx25_mode[4]; extern int il2p_mode[4]; extern int il2p_crc[4]; diff --git a/il2p.c b/il2p.c index 895feaa..c28c85c 100644 --- a/il2p.c +++ b/il2p.c @@ -46,6 +46,10 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include // for uint64_t +void debugHexDump(unsigned char * Data, int Len, char Dirn); +extern void debugTimeStamp(char * Text, char Dirn); +extern int useTimedPTT; + // Oct 2023 Nino has added an optional crc // Hamming(7,4) Encoding Table @@ -83,6 +87,10 @@ uint16_t Hamming74DecodeTable[128] = { \ void Debugprintf(const char * format, ...); int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count); +extern int openTraceLog(); +extern uint64_t writeTraceLog(char * Data, char Dirn); +extern void closeTraceLog(); + #define MAX_ADEVS 3 #define MAX_RADIO_CHANS ((MAX_ADEVS) * 2) @@ -1042,12 +1050,19 @@ void multi_modem_process_rec_packet(int snd_ch, int subchan, int slice, packet_t string * data = newString(); char Mode[32] = "IL2P"; int Quality = 0; + int CRCOK = 1; + char debugmsg[256]; sprintf(Mode, "IL2P %d", centreFreq); + + unsigned char * axcall = &pp->frame_data[7]; + char call[10]; + + call[ConvFromAX25(axcall, call)] = 0; // check crc if enabled - if (il2p_crc[snd_ch]) + if (il2p_crc[snd_ch] & 1) { unsigned short CRCMSG; unsigned short CRCCALC; @@ -1065,16 +1080,32 @@ void multi_modem_process_rec_packet(int snd_ch, int subchan, int slice, packet_t crc[2] = Hamming74DecodeTable[(pp->crc[2] & 0x7f)]; crc[3] = Hamming74DecodeTable[(pp->crc[3] & 0x7f)]; + debugTimeStamp("CRC after Hamming decode is", 'R'); + debugHexDump(crc, 4, 'R'); + CRCMSG = crc[0] << 12 | crc[1] << 8 | crc[2] << 4 | crc[3]; CRCCALC = get_fcs(pp->frame_data, pp->frame_len); if (CRCCALC != CRCMSG) { - Debugprintf("CRC Error Decoder %d Received %x Sent %x", subchan, CRCCALC, CRCMSG); - freeString(data); - ax25_delete(pp); - return; + CRCOK = 0; + if ((il2p_crc[snd_ch] & 2) == 0) // Ignore CRC Error + { + Debugprintf("CRC Error from %s Decoder %d Calculated %x Received %x FEC corrections %d But ignore CRC Set", call, subchan, CRCCALC, CRCMSG, retries); + sprintf(debugmsg, "CRC Error from %s Decoder %d Calculated %x Received %x FEC corrections %d But ignore CRC Set", call, subchan, CRCCALC, CRCMSG, retries); + debugTimeStamp(debugmsg, 'R'); + + } + + + else + { + Debugprintf("CRC Error from %s Decoder %d Calculated %x Received %x FEC corrections %d", call, subchan, CRCCALC, CRCMSG, retries); + freeString(data); + ax25_delete(pp); + return; + } } } @@ -1095,16 +1126,59 @@ void multi_modem_process_rec_packet(int snd_ch, int subchan, int slice, packet_t pDET->errors = 0; } - if (detect_list[snd_ch].Count > 0 && - my_indexof(&detect_list[snd_ch], data) >= 0) + if (detect_list[snd_ch].Count > 0) { - // Already have a copy of this frame + int index = my_indexof(&detect_list[snd_ch], data); - freeString(data); - Debugprintf("Discarding copy rcvr %d emph %d", subchan, 0); - return; + if (index >= 0) + { + // Already have a copy of this frame + + // See if new one has fewer corrections + + string * xx = Strings(&detect_list_c[snd_ch], index); // Should be corresponding frame info + string * olddata = Strings(&detect_list[snd_ch], index); + + if (xx) + { + int oldRetries = xx->Data[255]; + int oldCRCOK = xx->Data[254]; + + if ((oldCRCOK == 0 && CRCOK == 1) || (oldRetries > retries)) + { + replaceString(&detect_list[snd_ch], index, data); + freeString(olddata); + + // Just update the metadata + + Debugprintf("Replacing il2p frame from %s rcvr %d emph %d FEC corrections %d CRCOK %d", call, subchan, slice, retries, CRCOK); + + 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); + } + + xx->Length= sprintf(xx->Data, "%s", Mode); + xx->Data[254] = CRCOK; + xx->Data[255] = retries; + + return; + } + } + + freeString(data); + Debugprintf("Discarding copy rcvr %d emph %d FEC corrections %d", subchan, slice, retries); + return; + } } + Debugprintf("Good il2p frame from %s rcvr %d emph %d FEC corrections %d", call, subchan, slice, retries); + sprintf(debugmsg, "Good il2p frame from %s rcvr %d emph %d FEC corrections %d", call, subchan, slice, retries); + debugTimeStamp(debugmsg, 'R'); + string * xx = newString(); memset(xx->Data, 0, 16); @@ -1121,7 +1195,11 @@ 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)); + xx->Data[254] = CRCOK; + xx->Data[255] = retries; + closeTraceLog(); + openTraceLog(); return; @@ -2234,6 +2312,8 @@ void il2p_init(int il2p_debug) } } + openTraceLog(); + } // end il2p_init @@ -2838,6 +2918,9 @@ int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout) int e; int out_len = 0; + debugTimeStamp("TX Raw Packet is", 'T'); + debugHexDump(pp->frame_data, pp->frame_len, 'T'); + e = il2p_type_1_header(pp, max_fec, hdr); if (e >= 0) { il2p_scramble_block(hdr, iout, IL2P_HEADER_SIZE); @@ -2846,6 +2929,10 @@ int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout) if (e == 0) { // Success. No info part. + + debugTimeStamp("TX Type 1 IL2P Packet no info is", 'T'); + debugHexDump(iout, out_len, 'R'); + return (out_len); } @@ -2859,6 +2946,9 @@ int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout) if (k > 0) { out_len += k; // Success. Info part was <= 1023 bytes. + debugTimeStamp("TX Type 1 IL2P Packet is", 'T'); + debugHexDump(iout, out_len, 'T'); + return (out_len); } @@ -2885,6 +2975,10 @@ int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout) if (k > 0) { out_len += k; // Success. Entire AX.25 frame <= 1023 bytes. + + debugTimeStamp("TX Type 2 IL2P Packet is", 'T'); + debugHexDump(iout, out_len, 'T'); + return (out_len); } // Something went wrong with the payload encoding. @@ -4017,6 +4111,9 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit) // Determine Centre Freq centreFreq[chan] = GuessCentreFreq(chan); + + debugTimeStamp("SYNC Detected", 'R'); + } else if (__builtin_popcount((~(F->acc) & 0x00ffffff) ^ IL2P_SYNC_WORD) <= 1) { // FIXME - this pops up occasionally with random noise. Find better way to convey information. @@ -4062,7 +4159,7 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit) F->eplen = il2p_payload_compute(&plprop, len, max_fec); - if (il2p_get_debug() >= 1) + if (il2p_get_debug() >= 2) { Debugprintf("Header type %d, max fec = %d", hdr_type, max_fec); Debugprintf("Need to collect %d encoded bytes for %d byte payload.", F->eplen, len); @@ -4157,11 +4254,21 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit) { // have all crc bytes. enter DECODE + debugTimeStamp("CRC Complete Header is", 'R'); + debugHexDump(F->shdr, 15, 'R'); + if (F->pc) + { + debugTimeStamp("Payload is", 'R'); + debugHexDump(F->spayload, F->pc, 'R'); + } + debugTimeStamp("CRC is", 'R'); + debugHexDump(F->crc, 4, 'R'); + F->state = IL2P_DECODE; } } - break; + break; case IL2P_DECODE: @@ -4182,10 +4289,12 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit) { // Most likely too many FEC errors. Debugprintf("FAILED to construct frame in %s.\n", __func__); + debugTimeStamp("Packet Decode failed", 'R'); } } - if (pp != NULL) { + if (pp != NULL) + { alevel_t alevel = demod_get_audio_level(chan, subchan); retry_t retries = F->corrected; int is_fx25 = 1; // FIXME: distinguish fx.25 and IL2P. @@ -4195,6 +4304,9 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit) // if using crc pass received crc to packet object + debugTimeStamp("Decoded Packet is", 'R'); + debugHexDump(pp->frame_data, pp->frame_len, 'R'); + if (il2p_crc[chan]) { //copy crc bytes to packet object @@ -4205,14 +4317,15 @@ void il2p_rec_bit(int chan, int subchan, int slice, int dbit) pp->crc[3] = F->crc[3]; } + debugTimeStamp("CRC raw bytes", 'R'); + debugHexDump(pp->crc, 4, 'R'); + multi_modem_process_rec_packet(chan, subchan, slice, pp, alevel, retries, is_fx25, slice, centreFreq[chan]); } } // end block for local variables. - if (il2p_get_debug() >= 1) { - + if (il2p_get_debug() >= 2) Debugprintf("-----"); - } F->state = IL2P_SEARCHING; break; @@ -4426,7 +4539,7 @@ string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity) // if we are using crc add it now. elen should point to end of data // crc should be at pp->frame_data[pp->frame_len] - if (il2p_crc[chan]) + if (il2p_crc[chan] & 1) { // The four encoded CRC bytes are arranged : // | CRC3 | CRC2 | CRC1 | CRC0 | @@ -4440,10 +4553,9 @@ string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity) encoded[elen++] = Hamming74EncodeTable[crc1 &0xf]; } - number_of_bits_sent[chan] = 0; - if (il2p_get_debug() >= 1) { + if (il2p_get_debug() >= 2) { Debugprintf("IL2P frame, max_fec = %d, %d encoded bytes total", max_fec, elen); // fx_hex_dump(encoded, elen); } @@ -4475,13 +4587,15 @@ string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity) stringAdd(packet, encoded, elen); + // Add bytes for tail and TX padding, but don't send if another packet is available (?? how ??) + + number_of_bits_sent[chan] = 0; + tx_fx25_size[chan] = packet->Length * 8; return packet; } - - // TX Code. Builds whole packet then sends a bit at a time #define TX_SILENCE 0 @@ -4522,6 +4636,11 @@ string * fill_il2p_data(int snd_ch, string * data) result = il2p_send_frame(snd_ch, pp, 1, 0); + ax25_delete(pp); + + debugTimeStamp("TX Complete packet including Preamble and CRC and Tail", 'T'); + debugHexDump(result->Data, result->Length, 'T'); + return result; } @@ -4657,6 +4776,10 @@ int il2p_get_new_bit(int snd_ch, Byte bit) break; case FRAME_NO_FRAME: + + // I dont really like this state machine. We have run out of frames to send so + // should go straight to tail. This way we add an extra bit. Or does this really matter ?? + tx_tail_cnt[snd_ch] = 0; tx_frame_status[snd_ch] = FRAME_EMPTY; tx_status[snd_ch] = TX_TAIL; @@ -4666,5 +4789,251 @@ int il2p_get_new_bit(int snd_ch, Byte bit) return bit; } +extern int txLatency; +extern int useTImedPTT; + +int il2p_get_new_bit_tail(UCHAR snd_ch, UCHAR bit) +{ + // This sends reversals. It is an experiment + + int tailbits = (txtail[snd_ch] * tx_baudrate[snd_ch]) / 1000; + +#ifndef WIN32 + if (useTimedPTT) + tailbits += (txLatency * tx_baudrate[snd_ch]) / 1000; // add padding to tx buffer to make sure we don't send silence +#endif + if (tx_tail_cnt[snd_ch]++ > tailbits) + tx_status[snd_ch] = TX_WAIT_BPF; + + return (tx_tail_cnt[snd_ch] & 1); // altenating 1/0 + +} +void debugHexDump(unsigned char * Data, int Len, char Dirn) +{ + char Line[256]; + +#ifndef LOGTX + + if (Dirn == 'T') + return; + +#endif + +#ifndef LOGRX + + if (Dirn == 'R') + return; + +#endif + + while (Len > 0) + { + sprintf(Line, "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + Data[0], Data[1], Data[2], Data[3], Data[4], Data[5], Data[6], Data[7], + Data[8], Data[9], Data[10], Data[11], Data[12], Data[13], Data[14], Data[15]); + + if (Len < 16) + { + Line[Len * 3] = 10; + Line[Len * 3 + 1] = 0; + } + writeTraceLog(Line, Dirn); + + Data += 16; + Len -= 16; + } +} + + +// Hamming experiments + +// from https://github.com/nasserkessas/hamming-codes/blob/master/hamming.c + +#define block unsigned short // 16 bits +#define bit uint8_t // 8 bits (only last is used) + +int multipleXor(int *indicies, int len) +{ + int val = indicies[0]; + for (int i = 1; i < len; i++) + { + val = val ^ indicies[i]; + } + return val; +} + +bit getBit(unsigned short b, int i) +{ + return (b << i) & (int)pow(2, (sizeof(unsigned short) * 8 - 1)); +} + + +unsigned short toggleBit(unsigned short b, int i) +{ + return b ^ (1 << i); +} + + +bit getCharBit(char b, int i) +{ + return (b << i) & (int)pow(2, (sizeof(char) * 8 - 1)); +} + +block modifyBit(block n, int p, bit b) +{ + return ((n & ~(1 << (sizeof(block) * 8 - 1 - p))) | (b << (sizeof(block) * 8 - 1 - p))); +} + +void encode(char *input, int len, FILE *ptr) { + + // Amount of bits in a block // + int bits = sizeof(block) * 8; + + // Amount of bits per block used to carry the message // + int messageBits = bits - log2(bits) - 1; + + // Amount of blocks needed to encode message // + int blocks = ceil((float)len / messageBits); + + // Array of encoded blocks // + block encoded[16]; + + // Loop through each block // + for (int i = 0; i < blocks + 1; i++) { + + printf("On Block %d:\n", i); + + // Final encoded block variable // + block thisBlock = 0; + + // Amount of "skipped" bits (used for parity) // + int skipped = 0; + + // Count of how many bits are "on" // + int onCount = 0; + + // Array of "on" bits // + int onList[64]; + + // Loop through each message bit in this block to populate final block // + for (int j = 0; j < bits; j++) { + + // Skip bit if reserved for parity bit // + if ((j & (j - 1)) == 0) { // Check if j is a power of two or 0 + skipped++; + continue; + } + + bit thisBit; + + if (i != blocks) { + + // Current overall bit number // + int currentBit = i * messageBits + (j - skipped); + + // Current character // + int currentChar = currentBit / (sizeof(char) * 8); // int division + + // Value of current bit // + thisBit = currentBit < len * sizeof(char) * 8 ? getCharBit(input[currentChar], currentBit - currentChar * 8) : 0; + } + + else { + thisBit = getBit(len / 8, j - skipped + (sizeof(block) * 8 - messageBits)); + } + + // If bit is "on", add to onList and onCount // + if (thisBit) { + onList[onCount] = j; + onCount++; + } + + // Populate final message block // + thisBlock = modifyBit(thisBlock, j, thisBit); + } + + // Calculate values of parity bits // + block parityBits = multipleXor(onList, onCount); + + // Loop through skipped bits (parity bits) // + for (int k = 1; k < skipped; k++) { // skip bit 0 + + // If bit is "on", add to onCount + if (getBit(parityBits, sizeof(block) * 8 - skipped + k)) { + onCount++; + } + + // Add parity bit to final block // + thisBlock = modifyBit(thisBlock, (int)pow(2, skipped - k - 1), getBit(parityBits, sizeof(block) * 8 - skipped + k)); + } + + // Add overall parity bit (total parity of onCount) // + thisBlock = modifyBit(thisBlock, 0, onCount & 1); + + // Output final block // +// printBlock(thisBlock); +// putchar('\n'); + + // Add block to encoded blocks // + encoded[i] = thisBlock; + } + + // Write encoded message to file // + fwrite(encoded, sizeof(block), blocks + 1, ptr); +} + + +void decode(block input[], int len, FILE *ptr) +{ + + // Amount of bits in a block // + int bits = sizeof(block) * 8; + + for (int b = 0; b < (len / sizeof(block)); b++) { + + printf("On Block %d:\n", b); + + // Print initial block // +// printBlock(input[b]); + + // Count of how many bits are "on" // + int onCount = 0; + + // Array of "on" bits // + int onList[64]; + + // Populate onCount and onList // + for (int i = 1; i < bits; i++) { + getBit(input[b], i); + if (getBit(input[b], i)) { + onList[onCount] = i; + onCount++; + } + } + + // Check for single errors // + int errorLoc = multipleXor(onList, onCount); + + if (errorLoc) { + + // Check for multiple errors // + if (!(onCount & 1 ^ getBit(input[b], 0))) { // last bit of onCount (total parity) XOR first bit of block (parity bit) + printf("\nMore than one error detected. Aborting.\n"); + exit(1); + } + + // Flip error bit // + else { + printf("\nDetected error at position %d, flipping bit.\n", errorLoc); + input[b] = toggleBit(input[b], (bits - 1) - errorLoc); + + // Re-print block for comparison // +// printBlock(input[b]); + } + } + + putchar('\n'); + } +} diff --git a/kiss_mode.c b/kiss_mode.c index 72688a7..56feb2a 100644 --- a/kiss_mode.c +++ b/kiss_mode.c @@ -22,20 +22,6 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include "UZ7HOStuff.h" -/* -uses sysutils,classes; - - procedure KISS_init; - procedure KISS_free; - procedure KISS_add_stream(socket: integer); - procedure KISS_del_stream(socket: integer); - procedure KISS_on_data_in(socket: integer; data: string); - procedure KISS_on_data_out(port: byte; frame: string); - procedure KISS_send_ack(port: byte; data: string); - procedure KISS_send_ack1(port: byte); -*/ -// I don't like this. maybe fine for Dephi but overcomlicated for C - // I think I need a struct for each connection, but a simple array of entries should be fine // My normal ** and count system // Each needs an input buffer of max size kiss frame and length (or maybe string is a good idea) @@ -89,13 +75,14 @@ end; void KISS_add_stream(void * Socket) { - // Add a new connection. Called when QT accepts an incoming call} + // Add a new connection. Called wheKISSn QT accepts an incoming call} TKISSMode * KISS; KissConnections = realloc(KissConnections, (KISSConCount + 1) * sizeof(void *)); - KISS = KissConnections[KISSConCount++] = malloc(sizeof(KISS)); + KISS = KissConnections[KISSConCount++] = malloc(sizeof(*KISS)); + memset(KISS, 0, sizeof(*KISS)); KISS->Socket = Socket; KISS->data_in = newString(); diff --git a/main.cpp b/main.cpp index 43ea7cd..a3b08d4 100644 --- a/main.cpp +++ b/main.cpp @@ -77,6 +77,9 @@ int main(int argc, char *argv[]) } QObject::connect(&m1, SIGNAL(HLSetPTT(int)), &m1, SLOT(doHLSetPTT(int)), Qt::QueuedConnection); + QObject::connect(&m1, SIGNAL(FLRigSetPTT(int)), &m1, SLOT(doFLRigSetPTT(int)), Qt::QueuedConnection); + + QObject::connect(&m1, SIGNAL(startTimer(int)), &m1, SLOT(dostartTimer(int)), Qt::QueuedConnection); QObject::connect(&m1, SIGNAL(stopTimer()), &m1, SLOT(dostopTimer()), Qt::QueuedConnection); diff --git a/sm_main.c b/sm_main.c index d2c6cfd..fba2f2f 100644 --- a/sm_main.c +++ b/sm_main.c @@ -74,8 +74,8 @@ int Channels = 2; int BitsPerSample = 16; float TX_Samplerate = 12000; float RX_Samplerate = 12000; -int RX_SR = 11025; -int TX_SR = 11025; +//int RX_SR = 11025; +//int TX_SR = 11025; int RX_PPM = 0; int TX_PPM = 0; int tx_bufsize = 512; @@ -448,11 +448,12 @@ void init_Q4800(int snd_ch) void init_Q3600(int snd_ch) { - qpsk_set[snd_ch].mode = QPSK_SM; + qpsk_set[snd_ch].mode = QPSK_V26; // QPSK_SM; modem_mode[snd_ch] = MODE_QPSK; rx_shift[snd_ch] = 1800; rx_baudrate[snd_ch] = 1800; tx_bitrate[snd_ch] = 3600; + pskStates[snd_ch] = 4; if (modem_def[snd_ch]) get_filter_values(snd_ch); } @@ -1041,7 +1042,10 @@ void BufferFull(short * Samples, int nSamples) // These are Stereo Samples // 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]; @@ -1087,7 +1091,7 @@ void BufferFull(short * Samples, int nSamples) // These are Stereo Samples data2 += 2; } if (((Toggle & 1) == 0)) - doWaterfall(1); + doWaterfall(1); } if (Firstwaterfall || Secondwaterfall) diff --git a/sm_main.c.bak b/sm_main.c.bak new file mode 100644 index 0000000..b5b6499 --- /dev/null +++ b/sm_main.c.bak @@ -0,0 +1,1431 @@ +/* +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 dw9600ProcessSample(int snd_ch, short Sample); +void init_RUH48(int snd_ch); +void init_RUH96(int snd_ch); + +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", + "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 { + 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; +int multiCore = FALSE; + +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]; + +unsigned int pskStates[4] = {0, 0, 0, 0}; + +int speed[5] = {0,0,0,0}; +int panels[6] = {1,1,1,1,1}; + +short fft_buf[2][8192]; +UCHAR fft_disp[2][1024]; +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; + + +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_2400V26B: + + + 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; + + + 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; +*/ + } +} + + +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); +} + +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; + tx_bitrate[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; + tx_bitrate[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; + tx_bitrate[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; + tx_bitrate[snd_ch] = 400; + + 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; + tx_bitrate[snd_ch] = 4800; + pskStates[snd_ch] = 8; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_V26B2400(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; + 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); +} + +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; + tx_bitrate[snd_ch] = 2400; + pskStates[snd_ch] = 4; + + 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; + tx_bitrate[snd_ch] = 4800; + pskStates[snd_ch] = 4; + + 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; + tx_bitrate[snd_ch] = 3600; + 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; + tx_bitrate[snd_ch] = 2400; + pskStates[snd_ch] = 4; + + 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; + tx_bitrate[snd_ch] = 2400; + pskStates[snd_ch] = 2; + + 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; + tx_bitrate[snd_ch] = 1200; + pskStates[snd_ch] = 2; + + 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; + tx_bitrate[snd_ch] = 600; + pskStates[snd_ch] = 2; + + 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; + 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); +} + +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; + tx_bitrate[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); + +} + +int needPSKRefresh = 0; + +void init_speed(int snd_ch) +{ + int low, high; + + pskStates[snd_ch] = 0; // Not PSK + + switch (speed[snd_ch]) + { + case SPEED_8P4800: + init_8P4800(snd_ch); + break; + + case SPEED_2400V26B: + init_V26B2400(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_Q1200: +// init_Q1200(snd_ch); +// break; + + case SPEED_P600: + init_P600(snd_ch); + break; + + case SPEED_P300: + 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); + 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; + + 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) + { + 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; + */ + NeedWaterfallHeaders = TRUE; + + CheckPSKWindows(); +} + + +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 + + 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); + 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 + +int Toggle = 0; + +void BufferFull(short * Samples, int nSamples) // These are Stereo Samples +{ + word i, i1, j; + 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; + + 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++) + { + 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++; + } + } + 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 + + // We could skip extracting other channel if only one in use - is it worth it?? + + if (UsingBothChannels) + { + for (i = 0; i < nSamples; 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 < nSamples; i++) + { + src_buf[1][i] = data1[i1]; + i1 += 2; + } + } + else + { + // Extract just left + + for (i = 0; i < nSamples; 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 + + // testing + + + + int FirstWaterfallChan = 0; + + doWaterfall(FirstWaterfallChan); + if (Firstwaterfall || Secondwaterfall) + displayWaterfall(); + + + + +/* + + 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 + + Toggle++; + + 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; + } + + if ((Toggle & 1) || (UsingBothChannels == 0)) + doWaterfall(FirstWaterfallChan); + + if (data2) + { + for (i = 0; i < Needed; i++) + { + *ptr2++ = *data2; + data2 += 2; + } + if (((Toggle & 1) == 0)) + doWaterfall(1); + } + + if (Firstwaterfall || Secondwaterfall) + displayWaterfall(); + + 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"; + 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 + 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; +} + diff --git a/tcpCode.cpp b/tcpCode.cpp index 97d5ab0..d6be891 100644 --- a/tcpCode.cpp +++ b/tcpCode.cpp @@ -244,6 +244,10 @@ QTcpSocket * HAMLIBsock; int HAMLIBConnected = 0; int HAMLIBConnecting = 0; +QTcpSocket * FLRIGsock; +int FLRIGConnected = 0; +int FLRIGConnecting = 0; + void mynet::HAMLIBdisplayError(QAbstractSocket::SocketError socketError) { switch (socketError) @@ -332,6 +336,142 @@ extern "C" void HAMLIBSetPTT(int PTTState) emit m1.HLSetPTT(PTTState); } +extern "C" void FLRigSetPTT(int PTTState) +{ + // Won't work in non=gui mode + + emit m1.FLRigSetPTT(PTTState); +} + + +QTcpSocket * FLRigsock; +int FLRigConnected = 0; +int FLRigConnecting = 0; + +void mynet::FLRigdisplayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + + case QAbstractSocket::HostNotFoundError: + QMessageBox::information(nullptr, tr("QtSM"), + "FLRig host was not found. Please check the " + "host name and portsettings->"); + + break; + + case QAbstractSocket::ConnectionRefusedError: + + qDebug() << "FLRig Connection Refused"; + break; + + default: + + qDebug() << "FLRig Connection Failed"; + break; + + } + + FLRigConnecting = 0; + FLRigConnected = 0; +} + +void mynet::FLRigreadyRead() +{ + unsigned char Buffer[4096]; + QTcpSocket* Socket = static_cast(QObject::sender()); + + // read the data from the socket. Don't do anyhing with it at the moment + + Socket->read((char *)Buffer, 4095); +} + +void mynet::onFLRigSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + if (socketState == QAbstractSocket::UnconnectedState) + { + // Close any connections + + FLRigConnected = 0; + FLRigConnecting = 0; + + // delete (FLRigsock); + // FLRigsock = 0; + + qDebug() << "FLRig Connection Closed"; + + } + else if (socketState == QAbstractSocket::ConnectedState) + { + FLRigConnected = 1; + FLRigConnecting = 0; + qDebug() << "FLRig Connected"; + } +} + + +void mynet::ConnecttoFLRig() +{ + delete(FLRigsock); + + FLRigConnected = 0; + FLRigConnecting = 1; + + FLRigsock = new QTcpSocket(); + + connect(FLRigsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(FLRigdisplayError(QAbstractSocket::SocketError))); + connect(FLRigsock, SIGNAL(readyRead()), this, SLOT(FLRigreadyRead())); + connect(FLRigsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onFLRigSocketStateChanged(QAbstractSocket::SocketState))); + + FLRigsock->connectToHost(FLRigHost, FLRigPort); + + return; +} + +static char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n" +"User-Agent: XMLRPC++ 0.8\r\n" +"Host: 127.0.0.1:7362\r\n" +"Content-Type: text/xml\r\n" +"Content-length: %d\r\n" +"\r\n%s"; + +static char Req[] = "\r\n" +"%s\r\n" +"%s" +"\r\n"; + + +void mynet::doFLRigSetPTT(int c) +{ + int Len; + char ReqBuf[512]; + char SendBuff[512]; + char ValueString[256] = ""; + + sprintf(ValueString, "%d", c); + + Len = sprintf(ReqBuf, Req, "rig.set_ptt", ValueString); + Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf); + + if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState) + ConnecttoFLRig(); + + if (FLRigsock == nullptr || FLRigsock->state() != QAbstractSocket::ConnectedState) + return; + + FLRigsock->write(SendBuff); + + FLRigsock->waitForBytesWritten(3000); + + QByteArray datas = FLRigsock->readAll(); + + qDebug(datas.data()); +} + + + extern "C" void startTimer(int Time) { // Won't work in non=gui mode diff --git a/tcpCode.h b/tcpCode.h index 9255a49..ea5b5a1 100644 --- a/tcpCode.h +++ b/tcpCode.h @@ -11,6 +11,7 @@ class mynet : public QObject signals: void HLSetPTT(int c); + void FLRigSetPTT(int c); void startTimer(int Time); void stopTimer(); @@ -33,6 +34,10 @@ public slots: void sendtoKISS(void * sock, unsigned char * Msg, int Len); + void FLRigdisplayError(QAbstractSocket::SocketError socketError); + void FLRigreadyRead(); + void onFLRigSocketStateChanged(QAbstractSocket::SocketState socketState); + void ConnecttoFLRig(); void HAMLIBdisplayError(QAbstractSocket::SocketError socketError); void HAMLIBreadyRead(); void onHAMLIBSocketStateChanged(QAbstractSocket::SocketState socketState); @@ -40,6 +45,7 @@ public slots: void dostartTimer(int Time); void dostopTimer(); void doHLSetPTT(int c); + void doFLRigSetPTT(int c); void readPendingDatagrams(); void socketError(); @@ -65,6 +71,11 @@ signals: void sendtoKISS(void *, unsigned char *, int); void openSockets(); void startCWIDTimer(); + void setWaterfallImage(); + void setLevelImage(); + void setConstellationImage(int, int); + void startWatchdog(); + void stopWatchdog(); private: