From d0709a1e0a04bf74f240dc0fdb8697f030b68cea Mon Sep 17 00:00:00 2001 From: Dave Hibberd Date: Tue, 29 Oct 2024 22:45:21 +0000 Subject: [PATCH] New upstream version 0.0.0.73~rc1 --- 6pack.cpp | 377 ++ ALSASound.c | 84 +- BusyDetect.c | 168 +- Config.cpp | 22 +- Config.cpp.bak | 962 ++-- Modulate.c | 28 + QtSoundModem.cpp | 314 +- QtSoundModem.cpp.bak | 6760 +++++++++++++-------------- QtSoundModem.h | 24 + QtSoundModem.pro | 4 +- QtSoundModem.pro.user | 263 ++ QtSoundModem.pro~ | 64 + QtSoundModem.vcxproj | 1 + QtSoundModem.vcxproj.filters | 3 + QtSoundModem.vcxproj.user | 8 +- SMMain.c | 86 +- UZ7HOStuff.h | 32 +- Waveout.c | 56 +- audio.c | 1 + ax25.c | 15 +- ax25_agw.c | 70 +- ax25_demod.c | 11 +- ax25_l2.c | 54 +- ax25_mod.c | 11 + calibrateDialog.ui | 15 +- debug/moc_predefs-LAPTOP-Q6S4RP5Q.h | 24 +- debug/moc_predefs-notpi4-64.h | 24 +- debug/moc_predefs-skigdebian.h | 24 +- debug/moc_predefs.h | 2 +- devicesDialog.ui | 130 +- il2p.c | 4 +- kiss_mode.c | 5 +- main.cpp | 4 + ofdm.c | 1 + sm_main.c.bak | 2862 ++++++------ tcpCode.cpp | 299 +- tcpCode.h | 20 +- 37 files changed, 7217 insertions(+), 5615 deletions(-) create mode 100644 6pack.cpp create mode 100644 QtSoundModem.pro.user create mode 100644 QtSoundModem.pro~ diff --git a/6pack.cpp b/6pack.cpp new file mode 100644 index 0000000..d3ccf68 --- /dev/null +++ b/6pack.cpp @@ -0,0 +1,377 @@ +/* + +Using code from 6pack Linux Kernel driver with the following licence and credits + + * 6pack driver version 0.4.2, 1999/08/22 + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This module implements the AX.25 protocol for kernel-based + * devices like TTYs. It interfaces between a raw TTY, and the + * kernel's AX.25 protocol layers, just like slip.c. + * AX.25 needs to be separated from slip.c while slip.c is no + * longer a static kernel device since it is a module. + * + * Author: Andreas Könsgen + * + * Lots of stuff has been taken from mkiss.c, written by + * Hans Alblas + * + * with the fixes from + * + * Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15. + * Matthias (DG2FEF) Fixed bug in ax25_close(): dev_lock_wait() was + * called twice, causing a deadlock. + */ + + + // 6pack needs fast response to received characters, and I want to be able to operate over TCP links as well as serial. + // So I think the character level stuff may need to run in a separate thread, probably using select. + // + // I also need to support multiple 6pack ports. + + // ?? Do we add this as a backend to KISS driver or a separate Driver. KISS Driver is already quite messy. Not decided yet. + + // ?? If using serial/real TNC we need to be able to interleave control and data bytes, but I think with TCP/QtSM it won't be necessary + // ?? Also a don't see any point in running multiple copies of QtSM on one port, but maybe should treat the QtSM channels as + // multidropped ports for scheduling (?? only if on same radio ??) + + // ?? I think it needs to look like a KISS (L2) driver but will need a transmit scheduler level to do DCD/CSMA/PTT processing, + // ideally with an interlock to other drivers on same port. This needs some thought with QtSM KISS with multiple modems on one channel + + + + + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#define _CRT_SECURE_NO_DEPRECATE + +/**************************************************************************** + * Defines for the 6pack driver. + ****************************************************************************/ + +#define TRUE 1 +#define FALSE 0 + +#define AX25_MAXDEV 16 /* MAX number of AX25 channels; + This can be overridden with + insmod -oax25_maxdev=nnn */ +#define AX_MTU 236 + + /* 6pack protocol bytes/masks. */ +#define SIXP_INIT_CMD 0xE8 +#define SIXP_TNC_FOUND 0xE9 +#define SIXP_CMD_MASK 0xC0 +#define SIXP_PRIO_CMD_MASK 0x80 +#define SIXP_PRIO_DATA_MASK 0x38 +#define SIXP_STD_CMD_MASK 0x40 +#define SIXP_DCD_MASK 0x08 +#define SIXP_RX_DCD_MASK 0x18 +#define SIXP_CHN_MASK 0x07 +#define SIXP_TX_MASK 0x20 +#define SIXP_CON_LED_ON 0x68 +#define SIXP_STA_LED_ON 0x70 +#define SIXP_LED_OFF 0x60 + +/* checksum for a valid 6pack encapsulated packet */ +#define SIXP_CHKSUM 0xFF + +/* priority commands */ +#define SIXP_SEOF 0x40 /* TX underrun */ +#define SIXP_TX_URUN 0x48 /* TX underrun */ +#define SIXP_RX_ORUN 0x50 /* RX overrun */ +#define SIXP_RX_BUF_OVL 0x58 /* RX overrun */ + +struct ax_disp { + int magic; + + char * name; + /* Various fields. */ +// struct tty_struct *tty; /* ptr to TTY structure */ +// struct device *dev; /* easy for intr handling */ + struct ax_disp *sixpack; /* mkiss txport if mkiss channel*/ + + /* These are pointers to the malloc()ed frame buffers. */ + unsigned char *rbuff; /* receiver buffer */ + int rcount; /* received chars counter */ + unsigned char *xbuff; /* transmitter buffer */ + unsigned char *xhead; /* pointer to next byte to XMIT */ + int xleft; /* bytes left in XMIT queue */ + + /* SLIP interface statistics. */ + unsigned long rx_packets; /* inbound frames counter */ + unsigned long tx_packets; /* outbound frames counter */ + unsigned long rx_errors; /* Parity, etc. errors */ + unsigned long tx_errors; /* Planned stuff */ + unsigned long rx_dropped; /* No memory for skb */ + unsigned long tx_dropped; /* When MTU change */ + unsigned long rx_over_errors; /* Frame bigger then SLIP buf. */ + + /* Detailed SLIP statistics. */ + int mtu; /* Our mtu (to spot changes!) */ + int buffsize; /* Max buffers sizes */ + + + unsigned char flags; /* Flag values/ mode etc */ +#define AXF_INUSE 0 /* Channel in use */ +#define AXF_ESCAPE 1 /* ESC received */ +#define AXF_ERROR 2 /* Parity, etc. error */ +#define AXF_KEEPTEST 3 /* Keepalive test flag */ +#define AXF_OUTWAIT 4 /* is outpacket was flag */ + + int mode; + + + /* variables for the state machine */ + unsigned char tnc_ok; + unsigned char status; + unsigned char status1; + unsigned char status2; + + unsigned char duplex; + unsigned char led_state; + unsigned char tx_enable; + + unsigned char raw_buf[4]; /* receive buffer */ + unsigned char cooked_buf[400]; /* receive buffer after 6pack decoding */ + + unsigned int rx_count; /* counter for receive buffer */ + unsigned int rx_count_cooked; /* counter for receive buffer after 6pack decoding */ + + unsigned char tx_delay; + unsigned char persistance; + unsigned char slottime; + +}; + +struct sixpack_channel { + int magic; /* magic word */ + int init; /* channel exists? */ + struct tty_struct *tty; /* link to tty control structure */ +}; + +#define AX25_MAGIC 0x5316 +#define SIXP_DRIVER_MAGIC 0x5304 + +#define SIXP_INIT_RESYNC_TIMEOUT 150 /* in 10 ms */ +#define SIXP_RESYNC_TIMEOUT 500 /* in 10 ms */ + +/* default radio channel access parameters */ +#define SIXP_TXDELAY 25 /* in 10 ms */ +#define SIXP_PERSIST 50 +#define SIXP_SLOTTIME 10 /* in 10 ms */ + +static int sixpack_encaps(unsigned char *tx_buf, unsigned char *tx_buf_raw, int length, unsigned char tx_delay); +static void sixpack_decaps(struct ax_disp *, unsigned char); + +static void decode_prio_command(unsigned char, struct ax_disp *); +static void decode_std_command(unsigned char, struct ax_disp *); +static void decode_data(unsigned char, struct ax_disp *); +static void resync_tnc(unsigned long); +static void xmit_on_air(struct ax_disp *ax); +static void start_tx_timer(struct ax_disp *ax); + +extern "C" void Debugprintf(const char * format, ...); + +void Process6PackByte(unsigned char inbyte); + +struct ax_disp axdisp; + +/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */ +static void ax_bump(struct ax_disp *ax) +{ +} + + +void Process6PackData(unsigned char * Bytes, int Len) +{ + while(Len--) + Process6PackByte(Bytes++[0]); + +} + +void Process6PackByte(unsigned char inbyte) +{ + struct ax_disp *ax = &axdisp; + + if (inbyte == SIXP_INIT_CMD) + { + Debugprintf("6pack: SIXP_INIT_CMD received.\n"); + { + // Reset state machine and allocate a 6pack struct for each modem. + + // reply with INIT_CMD with the channel no of last modem + } + return; + } + + if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) + decode_prio_command(inbyte, ax); + else if ((inbyte & SIXP_STD_CMD_MASK) != 0) + decode_std_command(inbyte, ax); + else { + if ((ax->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) + decode_data(inbyte, ax); + } /* else */ +} + +/* identify and execute a 6pack priority command byte */ + +void decode_prio_command(unsigned char cmd, struct ax_disp *ax) +{ + unsigned char channel; + + channel = cmd & SIXP_CHN_MASK; + if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */ + + /* RX and DCD flags can only be set in the same prio command, + if the DCD flag has been set without the RX flag in the previous + prio command. If DCD has not been set before, something in the + transmission has gone wrong. In this case, RX and DCD are + cleared in order to prevent the decode_data routine from + reading further data that might be corrupt. */ + + if (((ax->status & SIXP_DCD_MASK) == 0) && + ((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) { + if (ax->status != 1) + Debugprintf("6pack: protocol violation\n"); + else + ax->status = 0; + cmd &= !SIXP_RX_DCD_MASK; + } + ax->status = cmd & SIXP_PRIO_DATA_MASK; + } /* if */ + + + /* if the state byte has been received, the TNC is present, + so the resync timer can be reset. */ + + if (ax->tnc_ok == 1) { + // del_timer(&(ax->resync_t)); + // ax->resync_t.data = (unsigned long) ax; + // ax->resync_t.function = resync_tnc; + // ax->resync_t.expires = jiffies + SIXP_INIT_RESYNC_TIMEOUT; + // add_timer(&(ax->resync_t)); + } + + ax->status1 = cmd & SIXP_PRIO_DATA_MASK; +} + +/* try to resync the TNC. Called by the resync timer defined in + decode_prio_command */ + +static void +resync_tnc(unsigned long channel) +{ + static char resync_cmd = SIXP_INIT_CMD; + struct ax_disp *ax = (struct ax_disp *) channel; + + Debugprintf("6pack: resyncing TNC\n"); + + /* clear any data that might have been received */ + + ax->rx_count = 0; + ax->rx_count_cooked = 0; + + /* reset state machine */ + + ax->status = 1; + ax->status1 = 1; + ax->status2 = 0; + ax->tnc_ok = 0; + + /* resync the TNC */ + + ax->led_state = SIXP_LED_OFF; + // ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1); + // ax->tty->driver.write(ax->tty, 0, &resync_cmd, 1); + + + /* Start resync timer again -- the TNC might be still absent */ + + // del_timer(&(ax->resync_t)); + // ax->resync_t.data = (unsigned long) ax; + // ax->resync_t.function = resync_tnc; + // ax->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT; + // add_timer(&(ax->resync_t)); +} + + + +/* identify and execute a standard 6pack command byte */ + +void decode_std_command(unsigned char cmd, struct ax_disp *ax) +{ + unsigned char checksum = 0, channel; + unsigned int i; + + channel = cmd & SIXP_CHN_MASK; + switch (cmd & SIXP_CMD_MASK) { /* normal command */ + case SIXP_SEOF: + if ((ax->rx_count == 0) && (ax->rx_count_cooked == 0)) { + if ((ax->status & SIXP_RX_DCD_MASK) == + SIXP_RX_DCD_MASK) { + ax->led_state = SIXP_CON_LED_ON; + // ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1); + } /* if */ + } + else { + ax->led_state = SIXP_LED_OFF; + // ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1); + /* fill trailing bytes with zeroes */ + if (ax->rx_count == 2) { + decode_data(0, ax); + decode_data(0, ax); + ax->rx_count_cooked -= 2; + } + else if (ax->rx_count == 3) { + decode_data(0, ax); + ax->rx_count_cooked -= 1; + } + for (i = 0; i < ax->rx_count_cooked; i++) + checksum += ax->cooked_buf[i]; + if (checksum != SIXP_CHKSUM) { + Debugprintf("6pack: bad checksum %2.2x\n", checksum); + } + else { + ax->rcount = ax->rx_count_cooked - 1; + ax_bump(ax); + } /* else */ + ax->rx_count_cooked = 0; + } /* else */ + break; + case SIXP_TX_URUN: + Debugprintf("6pack: TX underrun\n"); + break; + case SIXP_RX_ORUN: + Debugprintf("6pack: RX overrun\n"); + break; + case SIXP_RX_BUF_OVL: + Debugprintf("6pack: RX buffer overflow\n"); + } /* switch */ +} /* function */ + +/* decode 4 sixpack-encoded bytes into 3 data bytes */ + +void decode_data(unsigned char inbyte, struct ax_disp *ax) +{ + unsigned char *buf; + + if (ax->rx_count != 3) + ax->raw_buf[ax->rx_count++] = inbyte; + else { + buf = ax->raw_buf; + ax->cooked_buf[ax->rx_count_cooked++] = + buf[0] | ((buf[1] << 2) & 0xc0); + ax->cooked_buf[ax->rx_count_cooked++] = + (buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0); + ax->cooked_buf[ax->rx_count_cooked++] = + (buf[2] & 0x03) | (inbyte << 2); + ax->rx_count = 0; + } +} diff --git a/ALSASound.c b/ALSASound.c index fc950a4..6cb6121 100644 --- a/ALSASound.c +++ b/ALSASound.c @@ -1789,32 +1789,6 @@ int gpioInitialise(void) -int stricmp(const unsigned char * pStr1, const unsigned char *pStr2) -{ - unsigned char c1, c2; - int v; - - if (pStr1 == NULL) - { - if (pStr2) - Debugprintf("stricmp called with NULL 1st param - 2nd %s ", pStr2); - else - Debugprintf("stricmp called with two NULL params"); - - return 1; - } - - - do { - c1 = *pStr1++; - c2 = *pStr2++; - /* The casts are necessary when pStr1 is shorter & char is signed */ - v = tolower(c1) - tolower(c2); - } while ((v == 0) && (c1 != '\0') && (c2 != '\0') ); - - return v; -} - char Leds[8]= {0}; unsigned int PKTLEDTimer = 0; @@ -2107,5 +2081,63 @@ int gethints() return 0; } +// Microsoft routines not available in gcc + +int memicmp(unsigned char *a, unsigned char *b, int n) +{ + if (n) + { + while (n && (toupper(*a) == toupper(*b))) + n--, a++, b++; + + if (n) + return toupper(*a) - toupper(*b); + } + return 0; +} +int stricmp(const unsigned char * pStr1, const unsigned char *pStr2) +{ + unsigned char c1, c2; + int v; + + if (pStr1 == NULL) + { + if (pStr2) + Debugprintf("stricmp called with NULL 1st param - 2nd %s ", pStr2); + else + Debugprintf("stricmp called with two NULL params"); + + return 1; + } + + + do { + c1 = *pStr1++; + c2 = *pStr2++; + /* The casts are necessary when pStr1 is shorter & char is signed */ + v = tolower(c1) - tolower(c2); + } while ((v == 0) && (c1 != '\0') && (c2 != '\0')); + + return v; +} +char * strupr(char* s) +{ + char* p = s; + + if (s == 0) + return 0; + + while (*p = toupper(*p)) p++; + return s; +} + +char * strlwr(char* s) +{ + char* p = s; + while (*p = tolower(*p)) p++; + return s; +} + + diff --git a/BusyDetect.c b/BusyDetect.c index 453bdd2..b0b355a 100644 --- a/BusyDetect.c +++ b/BusyDetect.c @@ -45,156 +45,28 @@ VOID ClearBusy() intLastStop = 0; // This will force the busy detector to ignore old averages and initialze the rolling average filters } -/* -// Function to implement a busy detector based on 1024 point FFT +extern int FFTSize; -BOOL BusyDetect2(float * dblMag, int intStart, int intStop) // this only called while searching for leader ...once leader detected, no longer called. +BOOL BusyDetect3(float * dblMag, int StartFreq, int EndFreq) { - // each bin is about 12000/1024 or 11.72 Hz - // this only called while searching for leader ...once leader detected, no longer called. + + // Based on code from ARDOP, but using diffferent FFT size + // QtSM is using an FFT size based on waterfall settings. + // First sort signals and look at highes signals:baseline ratio.. - - float dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide; - float dblFastAlpha = 0.4f; - float dblSlowAlpha = 0.2f; - float dblAvgStoNNarrow, dblAvgStoNWide; - int intNarrow = 8; // 8 x 11.72 Hz about 94 z - int intWide = ((intStop - intStart) * 2) / 3; //* 0.66); - int blnBusy = FALSE; - float dblAvgStoNSlowNarrow = 0; - float dblAvgStoNFastNarrow = 0; - float dblAvgStoNSlowWide = 0; - float dblAvgStoNFastWide = 0; - - // First narrow band (~94Hz) - - SortSignals(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow); - - if (intLastStart == intStart && intLastStop == intStop) - { - dblAvgStoNSlowNarrow = (1 - dblSlowAlpha) * dblAvgStoNSlowNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; - dblAvgStoNFastNarrow = (1 - dblFastAlpha) * dblAvgStoNFastNarrow + dblFastAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; - } - else - { - dblAvgStoNSlowNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; - dblAvgStoNFastNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; - intLastStart = intStart; - intLastStop = intStop; - } - dblAvgStoNNarrow = max(dblAvgStoNSlowNarrow, dblAvgStoNFastNarrow); // computes fast attack, slow release - - // Wide band (66% ofr current bandwidth) - - SortSignals(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide); - - if (intLastStart == intStart && intLastStop == intStop) - { - dblAvgStoNSlowWide = (1 - dblSlowAlpha) * dblAvgStoNSlowWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide; - dblAvgStoNFastWide = (1 - dblFastAlpha) * dblAvgStoNFastWide + dblFastAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide; - } - else - { - dblAvgStoNSlowWide = dblAVGSignalPerBinWide / dblAVGBaselineWide; - dblAvgStoNFastWide = dblAVGSignalPerBinWide / dblAVGBaselineWide; - intLastStart = intStart; - intLastStop = intStop; - } - - dblAvgStoNNarrow = max(dblAvgStoNSlowNarrow, dblAvgStoNFastNarrow); // computes fast attack, slow release - dblAvgStoNWide = max(dblAvgStoNSlowWide, dblAvgStoNFastWide); // computes fast attack, slow release - - // Preliminary calibration...future a function of bandwidth and BusyDet. - - switch (ARQBandwidth) - { - case B200MAX: - case B200FORCED: - if (dblAvgStoNNarrow > 1.5 * BusyDet|| dblAvgStoNWide > 2.5 * BusyDet) - blnBusy = True; - break; - - case B500MAX: - case B500FORCED: - if (dblAvgStoNNarrow > 1.5 * BusyDet || dblAvgStoNWide > 2.5 * BusyDet) - blnBusy = True; - break; - - case B1000MAX: - case B1000FORCED: - - if (dblAvgStoNNarrow > 1.4 * BusyDet || dblAvgStoNWide > 2 * BusyDet) - blnBusy = True; - break; - - case B2000MAX: - case B2000FORCED: - if (dblAvgStoNNarrow > 1.4 * BusyDet || dblAvgStoNWide > 2 * BusyDet) - blnBusy = True; - } - - if (blnBusy) // This used to skip over one call busy nuisance trips. Busy must be present at least 2 consecutive times to be reported - { - intBusyOnCnt += 1; - intBusyOffCnt = 0; - if (intBusyOnCnt > 1) - blnBusy = True; - else if (!blnBusy) - { - intBusyOffCnt += 1; - intBusyOnCnt = 0; - if (intBusyOffCnt > 3) - blnBusy = False; - } - } - if (blnLastBusy == False && blnBusy) - { - int x = round(dblAvgStoNNarrow); // odd, but PI doesnt print floats properly - int y = round(dblAvgStoNWide); - - blnLastBusy = True; - LastBusyOn = Now; -#ifdef __ARM_ARCH - WriteDebugLog(LOGDEBUG, "[BusyDetect2: BUSY ON StoN Narrow = %d StoN Wide %d", x, y); -#else - WriteDebugLog(LOGDEBUG, "[BusyDetect2: BUSY ON StoN Narrow = %f StoN Wide %f", dblAvgStoNNarrow, dblAvgStoNWide); -#endif - } - else if (blnLastBusy == True && !blnBusy) - { - int x = round(dblAvgStoNNarrow); // odd, but PI doesnt print floats properly - int y = round(dblAvgStoNWide); - - blnLastBusy = False; - LastBusyOff = Now; -#ifdef __ARM_ARCH - WriteDebugLog(LOGDEBUG, "[BusyDetect2: BUSY OFF StoN Narrow = %d StoN Wide %d", x, y); -#else - WriteDebugLog(LOGDEBUG, "[BusyDetect2: BUSY OFF StoN Narrow = %f StoN Wide %f", dblAvgStoNNarrow, dblAvgStoNWide); -#endif - } - - return blnLastBusy; -} + // Start and Stop are in Hz. Convert to bin numbers -*/ - - - - -BOOL BusyDetect3(float * dblMag, int intStart, int intStop) // this only called while searching for leader ...once leader detected, no longer called. -{ - // each bin is about 12000/1024 or 11.72 Hz - // this only called while searching for leader ...once leader detected, no longer called. - // First sort signals and look at highes signals:baseline ratio.. + float BinSize = 12000.0 / FFTSize; + int StartBin = StartFreq / BinSize; + int EndBin = EndFreq / BinSize; float dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide; float dblSlowAlpha = 0.2f; float dblAvgStoNNarrow = 0, dblAvgStoNWide = 0; - int intNarrow = 8; // 8 x 11.72 Hz about 94 z - int intWide = ((intStop - intStart) * 2) / 3; //* 0.66); + int intNarrow = 100 / BinSize; // 8 x 11.72 Hz about 94 z + int intWide = ((EndBin - StartBin) * 2) / 3; //* 0.66); int blnBusy = FALSE; int BusyDet4th = BusyDet * BusyDet * BusyDet * BusyDet; @@ -202,32 +74,32 @@ BOOL BusyDetect3(float * dblMag, int intStart, int intStop) // this only // First sort signals and look at highest signals:baseline ratio.. // First narrow band (~94Hz) - SortSignals2(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow); + SortSignals2(dblMag, StartBin, EndBin, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow); - if (intLastStart == intStart && intLastStop == intStop) + if (intLastStart == StartBin && intLastStop == EndBin) dblAvgStoNNarrow = (1 - dblSlowAlpha) * dblAvgStoNNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; else { // This initializes the Narrow average after a bandwidth change dblAvgStoNNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; - intLastStart = intStart; - intLastStop = intStop; + intLastStart = StartBin; + intLastStop = EndBin; } // Wide band (66% of current bandwidth) - SortSignals2(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide); + SortSignals2(dblMag, StartBin, EndBin, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide); - if (intLastStart == intStart && intLastStop == intStop) + if (intLastStart == StartBin && intLastStop == EndBin) dblAvgStoNWide = (1 - dblSlowAlpha) * dblAvgStoNWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide; else { // This initializes the Wide average after a bandwidth change dblAvgStoNWide = dblAVGSignalPerBinWide / dblAVGBaselineWide; - intLastStart = intStart; - intLastStop = intStop; + intLastStart = StartBin; + intLastStop = EndBin; } // Preliminary calibration...future a function of bandwidth and BusyDet. diff --git a/Config.cpp b/Config.cpp index e2239f9..09513a9 100644 --- a/Config.cpp +++ b/Config.cpp @@ -29,7 +29,7 @@ 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 onlyMixSnoop; +extern "C" bool onlyMixSnoop; //extern "C" int RX_SR; //extern "C" int TX_SR; @@ -59,6 +59,7 @@ extern int CWIDRight; extern int CWIDType; extern bool afterTraffic; extern bool darkTheme; +extern "C" bool useKISSControls; extern "C" int RSID_SABM[4]; extern "C" int RSID_UI[4]; @@ -66,8 +67,10 @@ extern "C" int RSID_SetModem[4]; extern "C" int nonGUIMode; - - +extern char SixPackDevice[256]; +extern int SixPackPort; +extern int SixPackEnable; +extern int MgmtPort; extern QFont Font; @@ -216,6 +219,7 @@ void getSettings() soundChannel[3] = settings->value("Modem/soundChannel4", 0).toInt(); SCO = settings->value("Init/SCO", 0).toInt(); + useKISSControls = settings->value("Init/useKISSControls", 0).toBool(); dcd_threshold = settings->value("Modem/DCDThreshold", 40).toInt(); rxOffset = settings->value("Modem/rxOffset", 0).toInt(); @@ -224,6 +228,11 @@ void getSettings() AGWPort = settings->value("AGWHost/Port", 8000).toInt(); KISSServ = settings->value("KISS/Server", FALSE).toBool(); KISSPort = settings->value("KISS/Port", 8105).toInt(); + MgmtPort = settings->value("MGMT/Port", 0).toInt(); + + SixPackEnable = settings->value("SixPack/Enable", FALSE).toBool(); + SixPackPort = settings->value("SixPack/Port", 0).toInt(); + strcpy(SixPackDevice, settings->value("SixPack/Device", "").toString().toUtf8()); // RX_Samplerate = RX_SR + RX_SR * 0.000001*RX_PPM; // TX_Samplerate = TX_SR + TX_SR * 0.000001*TX_PPM; @@ -402,6 +411,7 @@ void saveSettings() settings->setValue("Init/SndRXDeviceName", CaptureDevice); settings->setValue("Init/SndTXDeviceName", PlaybackDevice); + settings->setValue("Init/useKISSControls", useKISSControls); settings->setValue("Init/SCO", SCO); settings->setValue("Init/DualPTT", DualPTT); settings->setValue("Init/TXRotate", TX_rotate); @@ -460,6 +470,12 @@ void saveSettings() settings->setValue("AGWHost/Port", AGWPort); settings->setValue("KISS/Server", KISSServ); settings->setValue("KISS/Port", KISSPort); + settings->setValue("MGMT/Port", MgmtPort); + + + settings->setValue("SixPack/Enable", SixPackEnable); + settings->setValue("SixPack/Port", SixPackPort); + settings->setValue("SixPack/Device", SixPackDevice); settings->setValue("Modem/PreEmphasisAll1", emph_all[0]); settings->setValue("Modem/PreEmphasisAll2", emph_all[1]); diff --git a/Config.cpp.bak b/Config.cpp.bak index d5d7a67..f660aaa 100644 --- a/Config.cpp.bak +++ b/Config.cpp.bak @@ -1,481 +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); -} +/*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/Modulate.c b/Modulate.c index 86900e3..0eb5f11 100644 --- a/Modulate.c +++ b/Modulate.c @@ -1025,6 +1025,34 @@ void sendCWID(char * strID, BOOL CWOnOff, int Chan) initFilter(200, Filter, Chan); + // if sending 1500 cal tone send mark tone for 10 secs + + if (strcmp(strID, "1500TONE") == 0) + { + float m_amplitude = 30000.0f; + float m_frequency = 1500.0f; + float m_phase = 0.0; + float m_time = 0.0; + float m_deltaTime = 1.0f / 12000; + + float x; + // generate sin wave in mono + for (int sample = 0; sample < 120000; ++sample) + { + x = m_amplitude * sin(2 * M_PI * m_frequency * m_time + m_phase); + ARDOPSampleSink(x); + m_time += m_deltaTime; + } + + + ARDOPTXPtr[Chan] = 0; + ARDOPTXLen[Chan] = Number; + Number = 0; + + return; + + } + //Generate leader for VOX 6 dots long for (k = 6; k >0; k--) diff --git a/QtSoundModem.cpp b/QtSoundModem.cpp index 37c6c46..b4ed7d7 100644 --- a/QtSoundModem.cpp +++ b/QtSoundModem.cpp @@ -71,6 +71,7 @@ QTextEdit * monWindowCopy; extern workerThread *t; extern QtSoundModem * w; extern QCoreApplication * a; +extern serialThread *serial; QList Ports = QSerialPortInfo::availablePorts(); @@ -95,7 +96,7 @@ extern "C" char CaptureNames[16][256]; extern "C" char PlaybackNames[16][256]; extern "C" int SoundMode; -extern "C" int onlyMixSnoop; +extern "C" bool onlyMixSnoop; extern "C" int multiCore; @@ -118,6 +119,8 @@ extern "C" int SendSize; // 100 mS for now extern "C" int txLatency; +extern "C" int BusyDet; + extern "C" { int InitSound(BOOL Report); @@ -169,6 +172,8 @@ int Configuring = 0; bool lockWaterfall = false; bool inWaterfall = false; +int MgmtPort = 0; + extern "C" int NeedWaterfallHeaders; extern "C" float BinSize; @@ -192,6 +197,7 @@ QRgb rxText = qRgb(0, 0, 192); bool darkTheme = true; bool minimizeonStart = true; +extern "C" bool useKISSControls; // Indexed colour list from ARDOPC @@ -241,6 +247,21 @@ int MintoTray = 1; int RSID_WF = 0; // Set to use RSID FFT for Waterfall. +char SixPackDevice[256] = ""; +int SixPackPort = 0; +int SixPackEnable = 0; + +// Stats + +uint64_t PTTonTime[4] = { 0 }; +uint64_t PTTActivemS[4] = { 0 }; // For Stats +uint64_t BusyonTime[4] = { 0 }; +uint64_t BusyActivemS[4] = { 0 }; + +int AvPTT[4] = { 0 }; +int AvBusy[4] = { 0 }; + + extern "C" void WriteDebugLog(char * Mess) { qDebug() << Mess; @@ -249,6 +270,24 @@ extern "C" void WriteDebugLog(char * Mess) void QtSoundModem::doupdateDCD(int Chan, int State) { DCDLabel[Chan]->setVisible(State); + + // This also tries to get a percentage on time over a minute + + uint64_t Time = QDateTime::currentMSecsSinceEpoch(); + + if (State) + { + BusyonTime[Chan] = Time; + return; + } + + if (BusyonTime[Chan]) + { + BusyActivemS[Chan] += Time - BusyonTime[Chan]; + BusyonTime[Chan] = 0; + } + + } extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat); @@ -529,6 +568,8 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) getSettings(); + serial = new serialThread; + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); family = mysettings.value("FontFamily", "Courier New").toString(); @@ -790,6 +831,10 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); timer->start(100); + QTimer *statstimer = new QTimer(this); + connect(statstimer, SIGNAL(timeout()), this, SLOT(StatsTimer())); + statstimer->start(60000); // One Minute + wftimer = new QTimer(this); connect(wftimer, SIGNAL(timeout()), this, SLOT(doRestartWF())); // wftimer->start(1000 * 300); @@ -812,6 +857,9 @@ QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) QTimer::singleShot(200, this, &QtSoundModem::updateFont); + connect(serial, &serialThread::request, this, &QtSoundModem::showRequest); + + } void QtSoundModem::updateFont() @@ -875,7 +923,113 @@ void extSetOffset(int chan) return; } + +extern TMgmtMode ** MgmtConnections; +extern int MgmtConCount; +extern QList _MgmtSockets; +extern "C" void doAGW2MinTimer(); + +#define FEND 0xc0 +#define QTSMKISSCMD 7 + +int AGW2MinTimer = 0; + +void QtSoundModem::StatsTimer() +{ + // Calculate % Busy over last minute + + for (int n = 0; n < 4; n++) + { + if (soundChannel[n] == 0) // Channel not used + continue; + + AvPTT[n] = PTTActivemS[n] / 600; // ms but want % + + PTTActivemS[n] = 0; + + AvBusy[n] = BusyActivemS[n] / 600; + BusyActivemS[n] = 0; + + // send to any connected Mgmt streams + + char Msg[64]; + uint64_t ret; + + if (!useKISSControls) + { + + for (QTcpSocket* socket : _MgmtSockets) + { + // Find Session + + TMgmtMode * MGMT = NULL; + + for (int i = 0; i < MgmtConCount; i++) + { + if (MgmtConnections[i]->Socket == socket) + { + MGMT = MgmtConnections[i]; + break; + } + } + + if (MGMT == NULL) + continue; + + if (MGMT->BPQPort[n]) + { + sprintf(Msg, "STATS %d %d %d\r", MGMT->BPQPort[n], AvPTT[n], AvBusy[n]); + ret = socket->write(Msg); + } + } + } + else // useKISSControls set + { + UCHAR * Control = (UCHAR *)malloc(32); + + int len = sprintf((char *)Control, "%c%cSTATS %d %d%c", FEND, (n) << 4 | QTSMKISSCMD, AvPTT[n], AvBusy[n], FEND); + KISSSendtoServer(NULL, Control, len); + } + } + + AGW2MinTimer++; + + if (AGW2MinTimer > 1) + { + AGW2MinTimer = 0; + doAGW2MinTimer(); + } +} + +// PTT Stats + +extern "C" void UpdatePTTStats(int Chan, int State) +{ + uint64_t Time = QDateTime::currentMSecsSinceEpoch(); + + if (State) + { + PTTonTime[Chan] = Time; + + // Cancel Busy timer (stats include ptt on time in port active + + if (BusyonTime[Chan]) + { + BusyActivemS[Chan] += (Time - BusyonTime[Chan]); + BusyonTime[Chan] = 0; + } + } + else + { + if (PTTonTime[Chan]) + { + PTTActivemS[Chan] += (Time - PTTonTime[Chan]); + PTTonTime[Chan] = 0; + } + } +} + void QtSoundModem::MyTimerSlot() { // 100 mS Timer Event @@ -1083,6 +1237,8 @@ void QtSoundModem::clickedSlotI(int i) if (strcmp(Name, "DCDSlider") == 0) { dcd_threshold = i; + BusyDet = i / 10; // for ardop busy detect code + saveSettings(); return; } @@ -1264,6 +1420,16 @@ void QtSoundModem::clickedSlot() return; } + if (strcmp(Name, "Cal1500") == 0) + { + char call[] = "1500TONE"; + sendCWID(call, 0, 0); + calib_mode[0] = 4; + return; + } + + + if (strcmp(Name, "actFont") == 0) { bool ok; @@ -2009,6 +2175,7 @@ bool myResize::eventFilter(QObject *obj, QEvent *event) void QtSoundModem::doDevices() { char valChar[10]; + QStringList items; Dev = new(Ui_devicesDialog); @@ -2025,6 +2192,22 @@ void QtSoundModem::doDevices() UI.installEventFilter(resize); + // Set serial names + + for (const QSerialPortInfo &info : Ports) + { + items.append(info.portName()); + } + + items.sort(); + + Dev->SixPackSerial->addItem("None"); + + for (const QString &info : items) + { + Dev->SixPackSerial->addItem(info); + } + newSoundMode = SoundMode; oldSoundMode = SoundMode; oldSnoopMix = newSnoopMix = onlyMixSnoop; @@ -2100,6 +2283,7 @@ void QtSoundModem::doDevices() QStandardItem * item = model->item(0, 0); item->setEnabled(false); + Dev->useKISSControls->setChecked(useKISSControls); Dev->singleChannelOutput->setChecked(SCO); Dev->colourWaterfall->setChecked(raduga); @@ -2111,6 +2295,26 @@ void QtSoundModem::doDevices() Dev->AGWPort->setText(valChar); Dev->AGWEnabled->setChecked(AGWServ); + Dev->MgmtPort->setText(QString::number(MgmtPort)); + + // If we are using a user specifed device add it + + i = Dev->SixPackSerial->findText(SixPackDevice, Qt::MatchFixedString); + + if (i == -1) + { + // Add our device to list + + Dev->SixPackSerial->insertItem(0, SixPackDevice); + i = Dev->SixPackSerial->findText(SixPackDevice, Qt::MatchContains); + } + + Dev->SixPackSerial->setCurrentIndex(i); + + sprintf(valChar, "%d", SixPackPort); + Dev->SixPackTCP->setText(valChar); + Dev->SixPackEnable->setChecked(SixPackEnable); + Dev->PTTOn->setText(PTTOnString); Dev->PTTOff->setText(PTTOffString); @@ -2137,8 +2341,6 @@ void QtSoundModem::doDevices() 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))); @@ -2148,13 +2350,6 @@ void QtSoundModem::doDevices() else Dev->RTSDTR->setChecked(true); - for (const QSerialPortInfo &info : Ports) - { - items.append(info.portName()); - } - - items.sort(); - Dev->PTTPort->addItem("None"); Dev->PTTPort->addItem("CM108"); @@ -2342,6 +2537,8 @@ void QtSoundModem::deviceaccept() if (UsingLeft && UsingRight) UsingBothChannels = 1; + + useKISSControls = Dev->useKISSControls->isChecked(); SCO = Dev->singleChannelOutput->isChecked(); raduga = Dev->colourWaterfall->isChecked(); AGWServ = Dev->AGWEnabled->isChecked(); @@ -2353,9 +2550,26 @@ void QtSoundModem::deviceaccept() Q = Dev->AGWPort->text(); AGWPort = Q.toInt(); + Q = Dev->MgmtPort->text(); + MgmtPort = Q.toInt(); + + Q = Dev->SixPackSerial->currentText(); + + char temp[256]; + + strcpy(temp, Q.toString().toUtf8()); + + if (strlen(temp)) + strcpy(SixPackDevice, temp); + + Q = Dev->SixPackTCP->text(); + SixPackPort = Q.toInt(); + + SixPackEnable = Dev->SixPackEnable->isChecked(); + + Q = Dev->PTTPort->currentText(); - char temp[256]; strcpy(temp, Q.toString().toUtf8()); @@ -2588,6 +2802,7 @@ void QtSoundModem::doCalibrate() 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.Cal1500, SIGNAL(released()), this, SLOT(clickedSlot())); /* @@ -3502,7 +3717,7 @@ extern "C" int openTraceLog() return 1; } -extern "C" qint64 writeTraceLog(char * Data, char Dirn) +extern "C" qint64 writeTraceLog(char * Data) { return tracefile.write(Data); } @@ -3534,7 +3749,7 @@ extern "C" void debugTimeStamp(char * Text, char Dirn) char Msg[2048]; sprintf(Msg, "%s %s\n", String.toUtf8().data(), Text); - qint64 ret = writeTraceLog(Msg, Dirn); + writeTraceLog(Msg); } @@ -3599,3 +3814,76 @@ void QtSoundModem::StartWatchdog() } + // KISS Serial Port code - mainly for 6Pack but should work with KISS as well + + // Serial Read needs to block and signal the main thread whenever a character is received. TX can probably be uncontrolled + + void serialThread::startSlave(const QString &portName, int waitTimeout, const QString &response) + { + QMutexLocker locker(&mutex); + this->portName = portName; + this->waitTimeout = waitTimeout; + this->response = response; + if (!isRunning()) + start(); + } + + void serialThread::run() + { + QSerialPort serial; + bool currentPortNameChanged = false; + + mutex.lock(); + QString currentPortName; + if (currentPortName != portName) { + currentPortName = portName; + currentPortNameChanged = true; + } + + int currentWaitTimeout = waitTimeout; + QString currentRespone = response; + mutex.unlock(); + + if (currentPortName.isEmpty()) + { + Debugprintf("Port not set"); + return; + } + + serial.setPortName(currentPortName); + + if (!serial.open(QIODevice::ReadWrite)) + { + Debugprintf("Can't open %s, error code %d", portName, serial.error()); + return; + } + + while (1) + { + + if (serial.waitForReadyRead(currentWaitTimeout)) + { + // read request + QByteArray requestData = serial.readAll(); + while (serial.waitForReadyRead(10)) + requestData += serial.readAll(); + + // Pass data to 6pack handler + + emit this->request(requestData); + + } + else { + Debugprintf("Serial read request timeout"); + } + } + } + + void Process6PackData(unsigned char * Bytes, int Len); + + void QtSoundModem::showRequest(QByteArray Data) + { + Process6PackData((unsigned char *)Data.data(), Data.length()); + } + + \ No newline at end of file diff --git a/QtSoundModem.cpp.bak b/QtSoundModem.cpp.bak index 1dd6aa1..fda07f6 100644 --- a/QtSoundModem.cpp.bak +++ b/QtSoundModem.cpp.bak @@ -1,3380 +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; -} - - - +/* +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 b02f9c2..a759f4b 100644 --- a/QtSoundModem.h +++ b/QtSoundModem.h @@ -43,6 +43,7 @@ private slots: void updateFont(); void MinimizetoTray(); void TrayActivated(QSystemTrayIcon::ActivationReason reason); + void StatsTimer(); void MyTimerSlot(); void returnPressed(); void clickedSlotI(int i); @@ -77,6 +78,7 @@ private slots: void StartWatchdog(); void StopWatchdog(); void PTTWatchdogExpired(); + void showRequest(QByteArray Data); void clickedSlot(); void startCWIDTimerSlot(); void setWaterfallImage(); @@ -124,3 +126,25 @@ protected: #define WaterfallTotalPixels WaterfallDisplayPixels + WaterfallHeaderPixels #define WaterfallImageHeight (WaterfallTotalPixels + WaterfallTotalPixels) + +class serialThread : public QThread +{ + Q_OBJECT + +public: + void run() Q_DECL_OVERRIDE; + void startSlave(const QString &portName, int waitTimeout, const QString &response); + +signals: + void request(const QByteArray &s); + void error(const QString &s); + void timeout(const QString &s); + +private: + QString portName; + QString response; + int waitTimeout; + QMutex mutex; + bool quit; +}; + diff --git a/QtSoundModem.pro b/QtSoundModem.pro index a02654c..796bac4 100644 --- a/QtSoundModem.pro +++ b/QtSoundModem.pro @@ -44,8 +44,8 @@ SOURCES += ./audio.c \ ./ofdm.c \ ./pktARDOP.c \ ./BusyDetect.c \ - ./DW9600.c - + ./DW9600.c \ + ./6pack.cpp FORMS += ./calibrateDialog.ui \ diff --git a/QtSoundModem.pro.user b/QtSoundModem.pro.user new file mode 100644 index 0000000..69ff052 --- /dev/null +++ b/QtSoundModem.pro.user @@ -0,0 +1,263 @@ + + + + + + EnvironmentId + {6e41d268-43e9-43ac-b8fa-a3c083d547a3} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 0 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop (x86-darwin-generic-mach_o-64bit) + Desktop (x86-darwin-generic-mach_o-64bit) + {a36d9ffa-38ce-4dfc-9820-5a456a9dc53d} + 0 + 0 + 0 + + 0 + /Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Debug + /Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Debug + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + Qt4ProjectManager.Qt4BuildConfiguration + 2 + + + /Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Release + /Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Release + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + + + 0 + /Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Profile + /Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Profile + + + true + QtProjectManager.QMakeBuildStep + false + + + + true + Qt4ProjectManager.MakeStep + + 2 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + true + Qt4ProjectManager.MakeStep + clean + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + Qt4ProjectManager.Qt4BuildConfiguration + 0 + 0 + 0 + + 3 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + true + + 2 + + false + Qt4ProjectManager.Qt4RunConfiguration:/Volumes/Source/QT/QtSoundModem/QtSoundModem.pro + /Volumes/Source/QT/QtSoundModem/QtSoundModem.pro + true + true + true + /Volumes/Source/QT/build-QtSoundModem-Desktop_x86_darwin_generic_mach_o_64bit-Debug/QtSoundModem.app/Contents/MacOS + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/QtSoundModem.pro~ b/QtSoundModem.pro~ new file mode 100644 index 0000000..a02654c --- /dev/null +++ b/QtSoundModem.pro~ @@ -0,0 +1,64 @@ + +QT += core gui +QT += network +QT += serialport + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = QtSoundModem +TEMPLATE = app + + +HEADERS += ./UZ7HOStuff.h \ + ./QtSoundModem.h \ + ./tcpCode.h + +SOURCES += ./audio.c \ + ./pulse.c \ + ./ax25.c \ + ./ax25_demod.c \ + ./ax25_l2.c \ + ./ax25_mod.c \ + ./Config.cpp \ + ./kiss_mode.c \ + ./main.cpp \ + ./QtSoundModem.cpp \ + ./ShowFilter.cpp \ + ./SMMain.c \ + ./sm_main.c \ + ./UZ7HOUtils.c \ + ./ALSASound.c \ + ./ax25_agw.c \ + ./berlekamp.c \ + ./galois.c \ + ./rs.c \ + ./rsid.c \ + ./il2p.c \ + ./tcpCode.cpp \ + ./ax25_fec.c \ + ./RSUnit.c \ + ./ARDOPC.c \ + ./ardopSampleArrays.c \ + ./SoundInput.c \ + ./Modulate.c \ + ./ofdm.c \ + ./pktARDOP.c \ + ./BusyDetect.c \ + ./DW9600.c + + + +FORMS += ./calibrateDialog.ui \ + ./devicesDialog.ui \ + ./filterWindow.ui \ + ./ModemDialog.ui \ + ./QtSoundModem.ui + +RESOURCES += QtSoundModem.qrc +RC_ICONS = QtSoundModem.ico + +QMAKE_CFLAGS += -g +#QMAKE_LFLAGS += -lasound -lpulse-simple -lpulse -lfftw3f +QMAKE_LIBS += -lasound -lfftw3f -ldl + + diff --git a/QtSoundModem.vcxproj b/QtSoundModem.vcxproj index 765fd37..1a66795 100644 --- a/QtSoundModem.vcxproj +++ b/QtSoundModem.vcxproj @@ -382,6 +382,7 @@ + diff --git a/QtSoundModem.vcxproj.filters b/QtSoundModem.vcxproj.filters index 2d05c65..94b092a 100644 --- a/QtSoundModem.vcxproj.filters +++ b/QtSoundModem.vcxproj.filters @@ -140,6 +140,9 @@ Generated Files + + Source Files + diff --git a/QtSoundModem.vcxproj.user b/QtSoundModem.vcxproj.user index 3d36629..e2d168a 100644 --- a/QtSoundModem.vcxproj.user +++ b/QtSoundModem.vcxproj.user @@ -20,15 +20,15 @@ WindowsLocalDebugger - 2024-06-21T13:50:20.1736205Z + 2024-10-02T12:37:32.3312444Z - 2024-06-21T13:50:20.3245934Z + 2024-10-02T12:37:32.7461423Z - 2024-06-21T13:50:20.4555992Z + 2024-10-02T12:37:33.4644113Z - 2024-06-21T13:50:20.6366033Z + 2024-10-02T12:37:33.9800504Z \ No newline at end of file diff --git a/SMMain.c b/SMMain.c index bf93779..dc8a0cf 100644 --- a/SMMain.c +++ b/SMMain.c @@ -29,6 +29,42 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include #include + +void platformInit(); +void RsCreate(); +void detector_init(); +void KISS_init(); +void ax25_init(); +void init_raduga(); +void il2p_init(int il2p_debug); +void SoundFlush(); +void BufferFull(short * Samples, int nSamples); +void PollReceivedSamples(); +void chk_dcd1(int snd_ch, int buf_size); +void make_core_BPF(UCHAR snd_ch, short freq, short width); +void modulator(UCHAR snd_ch, int buf_size); +void sendAckModeAcks(int snd_ch); +void PktARDOPEncode(UCHAR * Data, int Len, int Chan); +void RUHEncode(unsigned char * Data, int Len, int chan); +void sendRSID(int Chan, int dropTX); +void SetupGPIOPTT(); +int stricmp(const unsigned char * pStr1, const unsigned char *pStr2); +int gpioInitialise(void); +int OpenCOMPort(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits); +void HAMLIBSetPTT(int PTTState); +void FLRigSetPTT(int PTTState); +void StartWatchdog(); +void StopWatchdog(); +void gpioWrite(unsigned gpio, unsigned level); +BOOL WriteCOMBlock(int fd, char * Block, int BytesToWrite); +void COMSetDTR(int fd); +void COMSetRTS(int fd); +void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag); +size_t write(int fd, void * buf, size_t count); +int close(int fd); +void SendMgmtPTT(int snd_ch, int PTTState); + + BOOL KISSServ; int KISSPort; @@ -367,13 +403,13 @@ extern int intLastStop; void SMSortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin); -BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop) // this only called while searching for leader ...once leader detected, no longer called. +BOOL SMBusyDetect3(int Chan, float * dblMag, int intStart, int intStop) // this only called while searching for leader ...once leader detected, no longer called. { // First sort signals and look at highes signals:baseline ratio.. float dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide; float dblSlowAlpha = 0.2f; - float dblAvgStoNNarrow = 0, dblAvgStoNWide = 0; + static float dblAvgStoNNarrow[4] = { 0 }, dblAvgStoNWide[4] = {0}; int intNarrow = 8; // 8 x 11.72 Hz about 94 z int intWide = ((intStop - intStart) * 2) / 3; //* 0.66); int blnBusy = FALSE; @@ -384,15 +420,16 @@ BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop) // this onl SMSortSignals2(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow); + // Shouldn't dblAvgStoNNarrow, dblAvgStoNWide be static ?????? + + if (intLastStart == intStart && intLastStop == intStop) - dblAvgStoNNarrow = (1 - dblSlowAlpha) * dblAvgStoNNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; + dblAvgStoNNarrow[Chan] = (1 - dblSlowAlpha) * dblAvgStoNNarrow[Chan] + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; else { // This initializes the Narrow average after a bandwidth change - dblAvgStoNNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; - intLastStart = intStart; - intLastStop = intStop; + dblAvgStoNNarrow[Chan] = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; } // Wide band (66% of current bandwidth) @@ -400,12 +437,12 @@ BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop) // this onl SMSortSignals2(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide); if (intLastStart == intStart && intLastStop == intStop) - dblAvgStoNWide = (1 - dblSlowAlpha) * dblAvgStoNWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide; + dblAvgStoNWide[Chan] = (1 - dblSlowAlpha) * dblAvgStoNWide[Chan] + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide; else { // This initializes the Wide average after a bandwidth change - dblAvgStoNWide = dblAVGSignalPerBinWide / dblAVGBaselineWide; + dblAvgStoNWide[Chan] = dblAVGSignalPerBinWide / dblAVGBaselineWide; intLastStart = intStart; intLastStop = intStop; } @@ -413,7 +450,7 @@ BOOL SMBusyDetect3(float * dblMag, int intStart, int intStop) // this onl // Preliminary calibration...future a function of bandwidth and BusyDet. - blnBusy = (dblAvgStoNNarrow > (3 + 0.008 * BusyDet4th)) || (dblAvgStoNWide > (5 + 0.02 * BusyDet4th)); + blnBusy = (dblAvgStoNNarrow[Chan] > (3 + 0.008 * BusyDet4th)) || (dblAvgStoNWide[Chan] > (5 + 0.02 * BusyDet4th)); // if (BusyDet == 0) // blnBusy = FALSE; // 0 Disables check ?? Is this the best place to do this? @@ -452,7 +489,7 @@ void SMSortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumB *dblAVGBaselinePerBin = dblSum2 / (intStopBin - intStartBin - intNumBins - 1); } - +extern void updateDCD(int Chan, BOOL State); void SMUpdateBusyDetector(int LR, float * Real, float *Imag) { @@ -465,10 +502,8 @@ void SMUpdateBusyDetector(int LR, float * Real, float *Imag) static BOOL blnLastBusyStatus[4]; float dblMagAvg = 0; - int intTuneLineLow, intTuneLineHi, intDelta; int i, chan; - return; if (Now - LastBusyCheck < 100) // ?? return; @@ -487,6 +522,9 @@ void SMUpdateBusyDetector(int LR, float * Real, float *Imag) Low = tx_freq[chan] - txbpf[chan] / 2; High = tx_freq[chan] + txbpf[chan] / 2; +// Low = tx_freq[chan] - 0.5*rx_shift[chan]; +// High = tx_freq[chan] + 0.5*rx_shift[chan]; + // BinSize is width of each fft bin in Hz Start = (Low / BinSize); // First and last bins to process @@ -499,20 +537,18 @@ void SMUpdateBusyDetector(int LR, float * Real, float *Imag) dblMagAvg += dblMag[i]; } - blnBusyStatus = SMBusyDetect3(dblMag, Start, End); + blnBusyStatus = SMBusyDetect3(chan, dblMag, Start, End); if (blnBusyStatus && !blnLastBusyStatus[chan]) { - Debugprintf("Ch %d Busy True", chan); +// Debugprintf("Ch %d Busy True", chan); + updateDCD(chan, TRUE); } else if (blnLastBusyStatus[chan] && !blnBusyStatus) { - Debugprintf("Ch %d Busy False", chan); +// Debugprintf("Ch %d Busy False", chan); + updateDCD(chan, FALSE); } - // stcStatus.Text = "FALSE" - // queTNCStatus.Enqueue(stcStatus) - // 'Debug.WriteLine("BUSY FALSE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) - blnLastBusyStatus[chan] = blnBusyStatus; } } @@ -1222,6 +1258,7 @@ void CM108_set_ptt(int PTTState) float amplitudes[4] = { 32000, 32000, 32000, 32000 }; extern float amplitude; void startpttOnTimer(); +extern void UpdatePTTStats(int Chan, int State); void RadioPTT(int snd_ch, BOOL PTTState) { @@ -1240,6 +1277,15 @@ void RadioPTT(int snd_ch, BOOL PTTState) StopWatchdog(); } + if ((PTTMode & PTTHOST)) + { + // Send PTT ON/OFF to any mgmt connections + + SendMgmtPTT(snd_ch, PTTState); + } + + UpdatePTTStats(snd_ch, PTTState); + #ifdef __ARM_ARCH if (useGPIO) { @@ -1306,6 +1352,8 @@ void RadioPTT(int snd_ch, BOOL PTTState) COMClearRTS(hPTTDevice); } } + + startpttOnTimer(); } diff --git a/UZ7HOStuff.h b/UZ7HOStuff.h index c4146ff..563daf9 100644 --- a/UZ7HOStuff.h +++ b/UZ7HOStuff.h @@ -4,8 +4,8 @@ // My port of UZ7HO's Soundmodem // -#define VersionString "0.0.0.72" -#define VersionBytes {0, 0, 0, 72} +#define VersionString "0.0.0.73" +#define VersionBytes {0, 0, 0, 73} //#define LOGTX //#define LOGRX @@ -181,7 +181,7 @@ // Improve reliability of waterfall update // Report and set fx.25 and il2p flags to/from BPQ -// .72 Fix IL2P for RUH modems +// .72 Fix IL2P for RUH modems // July 2024 // Fix crash when closing in non-gui mode // Fix loop in chk_dcd1 // Change method of timing PTT @@ -192,6 +192,11 @@ // Change phase map of QPSK3600 mode to match Nino TNC +//.73 Fix QPSK3600 RX Filters +// Add Mgmt Interface (Beta 2) +// Use ARDOP Busy detector (Beta 3) +// Various fixes to AGW interface (Beta 4) + // As far as I can see txtail is only there to make sure all bits get through the tx filter, @@ -317,6 +322,15 @@ typedef struct TKISSMode_t } TKISSMode; +typedef struct MgmtMode_t +{ + void * Socket; // Used as a key + char Msg[512]; // Received message + int Len; + int BPQPort[4]; // BPQ port for each modem + +} TMgmtMode; + typedef struct TMChannel_t { @@ -461,7 +475,7 @@ typedef struct AGWUser_t { void *socket; string * data_in; - TStringList AGW_frame_buf; +// TStringList AGW_frame_buf; boolean Monitor; boolean Monitor_raw; boolean reportFreqAndModem; // Can report modem and frequency to host @@ -534,6 +548,7 @@ typedef struct TAX25Port_t #define PTTCM108 8 #define PTTHAMLIB 16 #define PTTFLRIG 32 +#define PTTHOST 128 // May be combined with others // Status flags @@ -677,9 +692,9 @@ extern int SendSize; #define MODEM_Q2400_BPF_TAP 256 //256 #define MODEM_Q2400_LPF_TAP 128 //128 // -#define MODEM_Q3600_BPF 1800 -#define MODEM_Q3600_TXBPF 2000 -#define MODEM_Q3600_LPF 600 +#define MODEM_Q3600_BPF 3600 +#define MODEM_Q3600_TXBPF 3000 +#define MODEM_Q3600_LPF 1350 #define MODEM_Q3600_BPF_TAP 256 #define MODEM_Q3600_LPF_TAP 128 // @@ -1011,6 +1026,9 @@ extern int IPOLL[4]; extern int maxframe[4]; extern int TXFrmMode[4]; +extern int bytes[4]; +extern int bytes2mins[4]; + extern char MyDigiCall[4][512]; extern char exclude_callsigns[4][512]; extern char exclude_APRS_frm[4][512]; diff --git a/Waveout.c b/Waveout.c index 22f05d3..fba668f 100644 --- a/Waveout.c +++ b/Waveout.c @@ -44,6 +44,11 @@ void printtick(char * msg); void PollReceivedSamples(); short * SoundInit(); void StdinPollReceivedSamples(); +extern void WriteDebugLog(char * Mess); +extern void UDPPollReceivedSamples(); +extern void QSleep(int ms); +extern void ProcessNewSamples(short * Samples, int nSamples); +extern void sendSamplestoStdout(short * Samples, int nSamples); #include @@ -57,7 +62,7 @@ void GetSoundDevices(); #define MaxReceiveSize 2048 // Enough for 9600 #define MaxSendSize 4096 -short buffer[2][MaxSendSize * 2]; // Two Transfer/DMA buffers of 0.1 Sec (x2 for Stereo) +short buffer[4][MaxSendSize * 2]; // Two Transfer/DMA buffers of 0.1 Sec (x2 for Stereo) short inbuffer[5][MaxReceiveSize * 2]; // Input Transfer/ buffers of 0.1 Sec (x2 for Stereo) extern short * DMABuffer; @@ -102,10 +107,12 @@ WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 2, 12000, 48000, 4, 16, 0 }; HWAVEOUT hWaveOut = 0; HWAVEIN hWaveIn = 0; -WAVEHDR header[2] = +WAVEHDR header[4] = { {(char *)buffer[0], 0, 0, 0, 0, 0, 0, 0}, - {(char *)buffer[1], 0, 0, 0, 0, 0, 0, 0} + {(char *)buffer[1], 0, 0, 0, 0, 0, 0, 0}, + {(char *)buffer[2], 0, 0, 0, 0, 0, 0, 0}, + {(char *)buffer[3], 0, 0, 0, 0, 0, 0, 0} }; WAVEHDR inheader[5] = @@ -155,12 +162,6 @@ VOID __cdecl Debugprintf(const char * format, ...) void platformInit() { - TIMECAPS tc; - unsigned int wTimerRes; - DWORD t, lastt = 0; - int i = 0; - - _strupr(CaptureDevice); _strupr(PlaybackDevice); @@ -224,20 +225,20 @@ FILE * wavfp1; BOOL DMARunning = FALSE; // Used to start DMA on first write -BOOL SeeIfCardBusy() -{ - if ((header[!Index].dwFlags & WHDR_DONE)) - return 0; - - return 1; -} - extern void sendSamplestoUDP(short * Samples, int nSamples, int Port); extern int UDPClientPort; short * SendtoCard(unsigned short * buf, int n) { + int NextBuffer = Index; + char Msg[80]; + + NextBuffer++; + + if (NextBuffer > 3) + NextBuffer = 0; + if (SoundMode == 3) // UDP { sendSamplestoUDP(buf, n, UDPClientPort); @@ -256,15 +257,17 @@ short * SendtoCard(unsigned short * buf, int n) waveOutPrepareHeader(hWaveOut, &header[Index], sizeof(WAVEHDR)); waveOutWrite(hWaveOut, &header[Index], sizeof(WAVEHDR)); - // wait till previous buffer is complete + // wait till next buffer is free - while (!(header[!Index].dwFlags & WHDR_DONE)) + while (!(header[NextBuffer].dwFlags & WHDR_DONE)) { txSleep(5); // Run buckground while waiting } - waveOutUnprepareHeader(hWaveOut, &header[!Index], sizeof(WAVEHDR)); - Index = !Index; + waveOutUnprepareHeader(hWaveOut, &header[NextBuffer], sizeof(WAVEHDR)); + Index = NextBuffer; + + sprintf(Msg, "TX Buffer %d", NextBuffer); return &buffer[Index][0]; } @@ -365,7 +368,7 @@ int onlyMixSnoop = 0; int InitSound(BOOL Report) { - int i, t, ret; + int i, ret; if (SoundMode == 4) { @@ -400,6 +403,8 @@ int InitSound(BOOL Report) header[0].dwFlags = WHDR_DONE; header[1].dwFlags = WHDR_DONE; + header[2].dwFlags = WHDR_DONE; + header[3].dwFlags = WHDR_DONE; if (strlen(PlaybackDevice) <= 2) PlayBackIndex = atoi(PlaybackDevice); @@ -723,11 +728,15 @@ void SoundFlush() SendtoCard(buffer[Index], Number); // Wait for all sound output to complete - + while (!(header[0].dwFlags & WHDR_DONE)) txSleep(10); while (!(header[1].dwFlags & WHDR_DONE)) txSleep(10); + while (!(header[2].dwFlags & WHDR_DONE)) + txSleep(10); + while (!(header[3].dwFlags & WHDR_DONE)) + txSleep(10); // I think we should turn round the link here. I dont see the point in // waiting for MainPoll @@ -922,6 +931,7 @@ BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite) if ((!fWriteStat) || (BytesToWrite != BytesWritten)) { int Err = GetLastError(); + Debugprintf("Serial Write Error %d", Err); ClearCommError(fd, &ErrorFlags, &ComStat); return FALSE; } diff --git a/audio.c b/audio.c index cd2fa16..7e062ec 100644 --- a/audio.c +++ b/audio.c @@ -40,6 +40,7 @@ #endif void Debugprintf(const char * format, ...); +void Sleep(int mS); extern int Closing; diff --git a/ax25.c b/ax25.c index 4ae9083..32933cd 100644 --- a/ax25.c +++ b/ax25.c @@ -29,10 +29,20 @@ __declspec(dllimport) unsigned short __stdcall ntohs(__in unsigned short hostsho #else +#include + +uint32_t htonl(uint32_t hostlong); +uint16_t htons(uint16_t hostshort); +uint32_t ntohl(uint32_t netlong); +uint16_t ntohs(uint16_t netshort); + #define strtok_s strtok_r #include #endif +void set_DM(int snd_ch, Byte * path); +void rst_values(TAX25Port * AX25Sess); + void decode_frame(Byte * frame, int len, Byte * path, string * data, Byte * pid, Byte * nr, Byte * ns, Byte * f_type, Byte * f_id, Byte * rpt, Byte * pf, Byte * cr); @@ -244,6 +254,9 @@ int maxframe[4] = { 0,0,0,0 }; int TXFrmMode[4] = { 0,0,0,0 }; int max_frame_collector[4] = { 0,0,0,0 }; +int bytes[4] = { 0,0,0,0 }; +int bytes2mins[4] = { 0,0,0,0 }; + char MyDigiCall[4][512] = { "","","","" }; char exclude_callsigns[4][512] = { "","","","" }; char exclude_APRS_frm[4][512] = { "","","","" }; @@ -1729,7 +1742,7 @@ int is_excluded_frm(int snd_ch, int f_id, string * data) -int number_digi(string path) +int number_digi(char * path) { int n = 0; diff --git a/ax25_agw.c b/ax25_agw.c index 12526ff..7f6fdae 100644 --- a/ax25_agw.c +++ b/ax25_agw.c @@ -177,7 +177,7 @@ void AGW_del_socket(void * socket) if (AGW == NULL) return; - Clear(&AGW->AGW_frame_buf); +// Clear(&AGW->AGW_frame_buf); freeString(AGW->data_in); AGW->Monitor = 0; AGW->Monitor_raw = 0; @@ -190,9 +190,7 @@ void AGW_add_socket(void * socket) { AGWUser * User = (struct AGWUser_t *)malloc(sizeof(struct AGWUser_t)); // One Client - AGWUsers = realloc(AGWUsers, (AGWConCount + 1) * sizeof(void *)); - AGWUsers[AGWConCount++] = User; User->data_in = newString(); @@ -259,7 +257,7 @@ string * AGW_R_Frame() string * AGW_X_Frame(char * CallFrom, UCHAR reg_call) { - string * Msg = AGW_frame_header(0, 'x', 0, CallFrom, "", 1); + string * Msg = AGW_frame_header(0, 'X', 0, CallFrom, "", 1); stringAdd(Msg, (UCHAR *)®_call, 1); @@ -276,13 +274,16 @@ string * AGW_G_Frame() for (int i = 0; i < 4; i++) { - Ports[0]++; if (soundChannel[i]) + { + Ports[0]++; sprintf(portMsg, "Port%c with SoundCard Ch %c;", Ports[0], 'A' + i); - else - sprintf(portMsg, "Port%c Disabled;", Ports[0]); + strcat(Ports, portMsg); + } +// else +// sprintf(portMsg, "Port%c Disabled;", Ports[0]); - strcat(Ports, portMsg); +; } @@ -294,7 +295,6 @@ string * AGW_G_Frame() }; - string * AGW_Gs_Frame(int port, Byte * port_info, int Len) { string * Msg; @@ -511,12 +511,50 @@ void on_AGW_R_frame(AGWUser * AGW) int refreshModems = 0; +/* + ++ 00 On air baud rate(0 = 1200 / 1 = 2400 / 2 = 4800 / 3 = 9600…) ++ 01 Traffic level(if 0xFF the port is not in autoupdate mode) ++ 02 TX Delay ++ 03 TX Tail ++ 04 Persist ++ 05 SlotTime ++ 06 MaxFrame ++ 07 How Many connections are active on this port ++ 08 LSB Low Word ++ 09 MSB Low Word ++ 10 LSB High Word ++ 11 MSB High Word HowManyBytes(received in the last 2 minutes) as a 32 bits(4 bytes) integer.Updated every two minutes. +*/ + void on_AGW_Gs_frame(AGWUser * AGW, struct AGWHeader * Frame, Byte * Data) { // QTSM with a data field is used by QtSM to set/read Modem Params Byte info[48] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature int Len = 12; + int Port = Frame->Port; + + // KD6YAM wants the info to be correct. BPQ used 24, 3, 100 as a signature but could use the Version. + // For now I'll fill in the rest + + info[2] = txdelay[Port] / 10; + info[3] = txtail[Port] / 10; + info[4] = persist[Port]; + info[5] = slottime[Port]; + info[6] = maxframe[Port]; + info[7] = 0; + + int i = 0; + while (i < port_num) + { + if (AX25Port[Port][i].status != STAT_NO_LINK) + info[7]++; + + i++; + } + + memcpy(&info[8], &bytes2mins[Port], 4); if (Frame->DataLength == 32) { @@ -1360,7 +1398,6 @@ void AGW_Report_Modem_Change(int port) int i; AGWUser * AGW; - string * pkt; if (soundChannel[port] == 0) // Not in use return; @@ -1537,3 +1574,16 @@ void AGW_frame_analiz(AGWUser * AGW) Debugprintf("AGW %c", Frame->DataKind); } } + +void doAGW2MinTimer() +{ + for (int n = 0; n < 4; n++) + { + if (soundChannel[n] == 0) // Channel not used + continue; + + bytes2mins[n] = bytes[n]; + bytes[n] = 0; + } +} + diff --git a/ax25_demod.c b/ax25_demod.c index 06d66f5..a05f39f 100644 --- a/ax25_demod.c +++ b/ax25_demod.c @@ -27,6 +27,14 @@ extern word MEMRecovery[5]; void make_rx_frame_FX25(int snd_ch, int rcvr_nr, int emph, string * data); string * memory_ARQ(TStringList * buf, string * data); +void CreateStringList(TStringList * List); +void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag); +void KISS_on_data_out(int port, string * frame, int TX); +void updateDCD(int Chan, boolean State); +void Frame_Optimize(TAX25Port * AX25Sess, TStringList * buf); +void RX2TX(int snd_ch); +int fx25_decode_rs(Byte * data, int * eras_pos, int no_eras, int pad, int rs_size); +void il2p_rec_bit(int chan, int subchan, int slice, int dbit); float GuessCentreFreq(int i); void ProcessRXFrames(int snd_ch); @@ -374,6 +382,7 @@ void chk_dcd1(int snd_ch, int buf_size) if (lastDCDState[snd_ch] != dcd_bit_sync[snd_ch]) { + updateDCD(snd_ch, dcd_bit_sync[snd_ch]); updateDCD(snd_ch, dcd_bit_sync[snd_ch]); lastDCDState[snd_ch] = dcd_bit_sync[snd_ch]; } @@ -4258,7 +4267,7 @@ void ProcessRXFrames(int snd_ch) // Work out which decoder and which emph settings worked. - if (snd_ch < 0 || snd_ch >3) + if (snd_ch < 0 || snd_ch > 3) return; if (detect_list[snd_ch].Count > 0) // no point if nothing decoded diff --git a/ax25_l2.c b/ax25_l2.c index a99a613..8efcceb 100644 --- a/ax25_l2.c +++ b/ax25_l2.c @@ -28,6 +28,16 @@ extern int RSID_SetModem[4]; extern int needRSID[4]; extern int needRSID[4]; +BOOL useKISSControls = 0; + +#define FEND 0xc0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD +#define KISS_ACKMODE 0x0C +#define KISS_DATA 0 +#define QTSMKISSCMD 7 + /* @@ -60,6 +70,23 @@ string * make_frame(string * data, Byte * path, Byte pid, Byte nr, Byte ns, Byt void rst_t3(TAX25Port * AX25Sess); TAX25Port * get_user_port(int snd_ch, Byte * path); +void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag); +boolean is_last_digi(Byte *path); +int is_excluded_call(int snd_ch, unsigned char * path); +boolean is_correct_path(Byte * path, Byte pid); +void KISS_on_data_out(int port, string * frame, int TX); +void ax25_info_init(TAX25Port * AX25Sess); +void clr_frm_win(TAX25Port * AX25Sess); +void decode_frame(Byte * frame, int len, Byte * path, string * data, + Byte * pid, Byte * nr, Byte * ns, Byte * f_type, Byte * f_id, + Byte * rpt, Byte * pf, Byte * cr); +void ax25_info_init(TAX25Port * AX25Sess); +void AGW_AX25_disc(TAX25Port * AX25Sess, Byte mode); +void AGW_AX25_conn(TAX25Port * AX25Sess, int snd_ch, Byte mode); +int number_digi(char * path); +void AGW_AX25_data_in(void * socket, int snd_ch, int PID, Byte * path, string * data); + + void inc_frack(TAX25Port * AX25Sess) { @@ -1100,7 +1127,8 @@ void on_FRMR(void * socket, TAX25Port * AX25Sess, Byte * path) { AX25Sess->info.stat_end_ses = time(NULL); - AGW_AX25_disc(socket, AX25Sess->snd_ch, MODE_OTHER, path); + AGW_AX25_disc(AX25Sess, MODE_OTHER); + write_ax25_info(AX25Sess); } @@ -1408,14 +1436,18 @@ void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag) if (len < PKT_ERR) return; + bytes[snd_ch] += frame->Length; // For AGW stats + + if (AGWServ) + AGW_AX25_frame_analiz(snd_ch, TRUE, frame); + decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); if (is_excluded_call(snd_ch, path)) - excluded =TRUE; + excluded = TRUE; // if is_excluded_frm(snd_ch,f_id,data) then excluded:=TRUE; - if (excluded) return; @@ -1448,7 +1480,20 @@ void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag) AGW_Raw_monitor(snd_ch, frame); if (KISSServ) + { + if (useKISSControls) + { + // Send a KISS Control frame with frame stats before data frame + + int len = strlen(code); + UCHAR * Control = (UCHAR *)malloc(64); + + len = sprintf(Control, "%c%cPKTINFO [%s]%c", FEND, (snd_ch) << 4 | QTSMKISSCMD, code, FEND); + + KISSSendtoServer(NULL, Control, len); + } KISS_on_data_out(snd_ch, frame, 0); + } } // Digipeat frame @@ -1618,9 +1663,6 @@ void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag) on_FRMR(socket, AX25Sess, path); break; } - - if (AGWServ) - AGW_AX25_frame_analiz(snd_ch, TRUE, frame); } diff --git a/ax25_mod.c b/ax25_mod.c index c8b9eef..dd84dda 100644 --- a/ax25_mod.c +++ b/ax25_mod.c @@ -25,6 +25,17 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses // I assume this modulates (and sends?} frames +void InitBuffers(); +void EncodeRS(Byte * xData, Byte * xEncoded); +void scrambler(UCHAR * in_buf, int Len); +void fx25_encode_rs(Byte * data, Byte *parity, int pad, int rs_size); +int fx25_decode_rs(Byte * data, int * eras_pos, int no_eras, int pad, int rs_size); +int il2p_get_new_bit_tail(UCHAR snd_ch, UCHAR bit); +int il2p_get_new_bit(int snd_ch, Byte bit); +int ARDOPSendToCard(int Chan, int Len); +void Flush(); +void SampleSink(int LR, short Sample); + int RSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen); //unit ax25_mod; diff --git a/calibrateDialog.ui b/calibrateDialog.ui index 66ef990..e786a23 100644 --- a/calibrateDialog.ui +++ b/calibrateDialog.ui @@ -7,7 +7,7 @@ 0 0 270 - 411 + 453 @@ -276,6 +276,19 @@ + + + + 66 + 416 + 139 + 23 + + + + 10 secs 1500Hz Tone + + diff --git a/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h b/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h index 62e1baa..b90fa38 100644 --- a/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h +++ b/debug/moc_predefs-LAPTOP-Q6S4RP5Q.h @@ -1,12 +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 +#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 index 6d708a1..8247e15 100644 --- a/debug/moc_predefs-notpi4-64.h +++ b/debug/moc_predefs-notpi4-64.h @@ -1,12 +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 +#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 index 6d708a1..8247e15 100644 --- a/debug/moc_predefs-skigdebian.h +++ b/debug/moc_predefs-skigdebian.h @@ -1,12 +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 +#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.h b/debug/moc_predefs.h index b857b2e..8247e15 100644 --- a/debug/moc_predefs.h +++ b/debug/moc_predefs.h @@ -1,7 +1,7 @@ #define _MSC_EXTENSIONS #define _INTEGRAL_MAX_BITS 64 #define _MSC_VER 1916 -#define _MSC_FULL_VER 191627045 +#define _MSC_FULL_VER 191627043 #define _MSC_BUILD 0 #define _WIN32 #define _M_IX86 600 diff --git a/devicesDialog.ui b/devicesDialog.ui index 9fef8ab..40bad5e 100644 --- a/devicesDialog.ui +++ b/devicesDialog.ui @@ -6,8 +6,8 @@ 0 0 - 535 - 698 + 528 + 721 @@ -20,9 +20,9 @@ 5 - 10 - 512 - 671 + 0 + 513 + 711 @@ -40,7 +40,7 @@ 0 0 508 - 668 + 706 @@ -49,7 +49,7 @@ 5 10 541 - 331 + 361 @@ -582,14 +582,27 @@ Only Mix/Snoop Devices (needs custom .asoundrc) + + + + 20 + 328 + 271 + 17 + + + + Enable KISS Control and Reporting + + - + 10 - 360 + 380 481 - 101 + 133 @@ -600,7 +613,7 @@ 22 24 - 121 + 129 18 @@ -633,7 +646,7 @@ 22 48 - 111 + 131 18 @@ -718,7 +731,7 @@ 20 74 - 111 + 135 18 @@ -726,12 +739,97 @@ UDP Sound Server Host + + + + 160 + 100 + 81 + 20 + + + + true + + + + + + 20 + 98 + 111 + 18 + + + + 6Pack Serial Port + + + + + + 320 + 100 + 47 + 20 + + + + + + + 252 + 101 + 63 + 18 + + + + TCP Port + + + + + + 380 + 99 + 70 + 23 + + + + Enabled + + + + + + 410 + 25 + 47 + 20 + + + + + + + 340 + 25 + 91 + 20 + + + + Mgmt Port + + 10 - 470 + 520 481 151 @@ -933,8 +1031,8 @@ - 140 - 626 + 130 + 670 215 33 diff --git a/il2p.c b/il2p.c index c28c85c..1b8e2b8 100644 --- a/il2p.c +++ b/il2p.c @@ -88,7 +88,7 @@ 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 uint64_t writeTraceLog(char * Data); extern void closeTraceLog(); #define MAX_ADEVS 3 @@ -4839,7 +4839,7 @@ void debugHexDump(unsigned char * Data, int Len, char Dirn) Line[Len * 3] = 10; Line[Len * 3 + 1] = 0; } - writeTraceLog(Line, Dirn); + writeTraceLog(Line); Data += 16; Len -= 16; diff --git a/kiss_mode.c b/kiss_mode.c index 56feb2a..6c638f1 100644 --- a/kiss_mode.c +++ b/kiss_mode.c @@ -22,6 +22,8 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #include "UZ7HOStuff.h" +int add_raw_frames(int snd_ch, string * frame, TStringList * buf); + // 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) @@ -35,6 +37,7 @@ int KISSConCount = 0; #define TFESC 0xDD #define KISS_ACKMODE 0x0C #define KISS_DATA 0 +#define QTSMKISSCMD 7 struct TKISSMode_t KISS; @@ -75,7 +78,7 @@ end; void KISS_add_stream(void * Socket) { - // Add a new connection. Called wheKISSn QT accepts an incoming call} + // Add a new connection. Called when QT accepts an incoming call} TKISSMode * KISS; diff --git a/main.cpp b/main.cpp index a3b08d4..63391fd 100644 --- a/main.cpp +++ b/main.cpp @@ -33,15 +33,18 @@ extern void saveSettings(); extern int Closing; workerThread *t; +serialThread *serial; mynet m1; QCoreApplication * a; QtSoundModem * w; + int main(int argc, char *argv[]) { char Title[128]; + QString Response; if (argc > 1 && strcmp(argv[1], "nogui") == 0) nonGUIMode = 1; @@ -78,6 +81,7 @@ 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(mgmtSetPTT(int, int)), &m1, SLOT(domgmtSetPTT(int, int)), Qt::QueuedConnection); QObject::connect(&m1, SIGNAL(startTimer(int)), &m1, SLOT(dostartTimer(int)), Qt::QueuedConnection); diff --git a/ofdm.c b/ofdm.c index 8472304..2d1fc88 100644 --- a/ofdm.c +++ b/ofdm.c @@ -200,6 +200,7 @@ extern int LastDataFrameType; // Last data frame processed (for Memory ARQ, et extern int intNAKctr; extern int intACKctr; extern int intTimeouts; +void ARDOPSampleSink(short Sample); extern UCHAR goodReceivedBlocks[128]; extern UCHAR goodReceivedBlockLen[128]; diff --git a/sm_main.c.bak b/sm_main.c.bak index b5b6499..e017275 100644 --- a/sm_main.c.bak +++ b/sm_main.c.bak @@ -1,1431 +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; -} - +/* +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 d6be891..79bda8f 100644 --- a/tcpCode.cpp +++ b/tcpCode.cpp @@ -18,6 +18,7 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses */ + // UZ7HO Soundmodem Port by John Wiseman G8BPQ #include @@ -27,15 +28,26 @@ along with QtSoundModem. If not, see http://www.gnu.org/licenses #define CONNECT(sndr, sig, rcvr, slt) connect(sndr, SIGNAL(sig), rcvr, SLOT(slt)) +QList _MgmtSockets; QList _KISSSockets; QList _AGWSockets; QTcpServer * _KISSserver; QTcpServer * _AGWserver; +QTcpServer * SixPackServer; +QTcpSocket *SixPackSocket; +QTcpServer * _MgmtServer; + +TMgmtMode ** MgmtConnections = NULL; +int MgmtConCount = 0; extern workerThread *t; extern mynet m1; +extern serialThread *serial; +QString Response; + +extern int MgmtPort; extern "C" int KISSPort; extern "C" void * initPulse(); extern "C" int SoundMode; @@ -44,6 +56,10 @@ extern "C" int UDPClientPort; extern "C" int UDPServerPort; extern "C" int TXPort; +extern char SixPackDevice[256]; +extern int SixPackPort; +extern int SixPackEnable; + char UDPHost[64] = ""; int UDPServ = 0; // UDP Server Active (ie broadcast sound frams as UDP packets) @@ -68,9 +84,10 @@ extern "C" void MainLoop(); void set_speed(int snd_ch, int Modem); void init_speed(int snd_ch); - } +void Process6PackData(unsigned char * Bytes, int Len); + extern "C" int nonGUIMode; QTimer *timer; @@ -121,12 +138,56 @@ void mynet::start() } } + + if (MgmtPort) + { + _MgmtServer = new(QTcpServer); + + if (_MgmtServer->listen(QHostAddress::Any, MgmtPort)) + connect(_MgmtServer, SIGNAL(newConnection()), this, SLOT(onMgmtConnection())); + else + { + if (nonGUIMode) + Debugprintf("Listen failed for Mgmt Port"); + else + { + QMessageBox msgBox; + msgBox.setText("Listen failed for Mgmt Port."); + msgBox.exec(); + } + } + } + + QObject::connect(t, SIGNAL(sendtoKISS(void *, unsigned char *, int)), this, SLOT(sendtoKISS(void *, unsigned char *, int)), Qt::QueuedConnection); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); timer->start(100); + + if (SixPackEnable) + { + if (SixPackDevice[0] && strcmp(SixPackDevice, "None") != 0) // Using serial + { + serial->startSlave(SixPackDevice, 30000, Response); + serial->start(); + +// connect(serial, &serialThread::request, this, &QtSoundModem::showRequest); +// connect(serial, &serialThread::error, this, &QtSoundModem::processError); +// connect(serial, &serialThread::timeout, this, &QtSoundModem::processTimeout); + + } + + else if (SixPackPort) // using TCP + { + SixPackServer = new(QTcpServer); + if (SixPackServer->listen(QHostAddress::Any, SixPackPort)) + connect(SixPackServer, SIGNAL(newConnection()), this, SLOT(on6PackConnection())); + + } + } + } void mynet::MyTimerSlot() @@ -172,6 +233,32 @@ void mynet::onAGWReadyRead() AGW_explode_frame(sender, datas.data(), datas.length()); } +void mynet::on6PackReadyRead() +{ + QTcpSocket* sender = static_cast(QObject::sender()); + QByteArray datas = sender->readAll(); + Process6PackData((unsigned char *)datas.data(), datas.length()); +} + + +void mynet::on6PackSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + if (socketState == QAbstractSocket::UnconnectedState) + { + QTcpSocket* sender = static_cast(QObject::sender()); + + } +} + +void mynet::on6PackConnection() +{ + QTcpSocket *clientSocket = SixPackServer->nextPendingConnection(); + connect(clientSocket, SIGNAL(readyRead()), this, SLOT(on6PackReadyRead())); + connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(on6PackSocketStateChanged(QAbstractSocket::SocketState))); + + Debugprintf("6Pack Connect Sock %x", clientSocket); +} + void mynet::onKISSConnection() { @@ -186,6 +273,183 @@ void mynet::onKISSConnection() Debugprintf("KISS Connect Sock %x", clientSocket); } +void Mgmt_del_socket(void * socket) +{ + int i; + + TMgmtMode * MGMT = NULL; + + if (MgmtConCount == 0) + return; + + for (i = 0; i < MgmtConCount; i++) + { + if (MgmtConnections[i]->Socket == socket) + { + MGMT = MgmtConnections[i]; + break; + } + } + + if (MGMT == NULL) + return; + + // Need to remove entry and move others down + + MgmtConCount--; + + while (i < MgmtConCount) + { + MgmtConnections[i] = MgmtConnections[i + 1]; + i++; + } +} + + + +void mynet::onMgmtConnection() +{ + QTcpSocket *clientSocket = (QTcpSocket *)_MgmtServer->nextPendingConnection(); + connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onMgmtReadyRead())); + connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onMgmtSocketStateChanged(QAbstractSocket::SocketState))); + + _MgmtSockets.append(clientSocket); + + // Create a data structure to hold session info + + TMgmtMode * MGMT; + + MgmtConnections = (TMgmtMode **)realloc(MgmtConnections, (MgmtConCount + 1) * sizeof(void *)); + + MGMT = MgmtConnections[MgmtConCount++] = (TMgmtMode *)malloc(sizeof(*MGMT)); + memset(MGMT, 0, sizeof(*MGMT)); + + MGMT->Socket = clientSocket; + + Debugprintf("Mgmt Connect Sock %x", clientSocket); + clientSocket->write("Connected to QtSM\r"); +} + + +void mynet::onMgmtSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + if (socketState == QAbstractSocket::UnconnectedState) + { + QTcpSocket* sender = static_cast(QObject::sender()); + + Mgmt_del_socket(sender); + +// free(sender->Msg); + _MgmtSockets.removeOne(sender); + Debugprintf("Mgmt Disconnect Sock %x", sender); + } +} + +void mynet::onMgmtReadyRead() +{ + QTcpSocket* sender = static_cast(QObject::sender()); + + MgmtProcessLine(sender); +} + +extern "C" void SendMgmtPTT(int snd_ch, int PTTState) +{ + // Won't work in non=gui mode + + emit m1.mgmtSetPTT(snd_ch, PTTState); +} + + +extern "C" char * strlop(char * buf, char delim); +extern "C" char modes_name[modes_count][21]; +extern "C" int speed[5]; + +#ifndef WIN32 +extern "C" int memicmp(char *a, char *b, int n); +#endif + +void mynet::MgmtProcessLine(QTcpSocket* socket) +{ + // Find Session + + TMgmtMode * MGMT = NULL; + + for (int i = 0; i < MgmtConCount; i++) + { + if (MgmtConnections[i]->Socket == socket) + { + MGMT = MgmtConnections[i]; + break; + } + } + + if (MGMT == NULL) + { + socket->readAll(); + return; + } + + while (socket->bytesAvailable()) + { + + QByteArray datas = socket->peek(512); + + char * Line = datas.data(); + + if (strchr(Line, '\r') == 0) + return; + + char * rest = strlop(Line, '\r'); + + int used = rest - Line; + + // Read the upto the cr Null + + datas = socket->read(used); + + Line = datas.data(); + rest = strlop(Line, '\r'); + + if (memicmp(Line, "QtSMPort ", 8) == 0) + { + if (strlen(Line) > 10) + { + int port = atoi(&Line[8]); + int bpqport = atoi(&Line[10]); + + if ((port > 0 && port < 5) && (bpqport > 0 && bpqport < 64)) + { + MGMT->BPQPort[port - 1] = bpqport; + socket->write("Ok\r"); + } + } + } + + else if (memicmp(Line, "Modem ", 6) == 0) + { + int port = atoi(&Line[6]); + + if (port > 0 && port < 5) + { + // if any more params - if a name follows, set it else return it + + char reply[80]; + + sprintf(reply, "Port %d Chan %d Freq %d Modem %s \r", MGMT->BPQPort[port - 1], port, rx_freq[port - 1], modes_name[speed[port - 1]]); + + socket->write(reply); + } + else + socket->write("Invalid Port\r"); + } + else + { + socket->write("Invalid command\r"); + + } + } +} + void mynet::onKISSSocketStateChanged(QAbstractSocket::SocketState socketState) { if (socketState == QAbstractSocket::UnconnectedState) @@ -195,6 +459,7 @@ void mynet::onKISSSocketStateChanged(QAbstractSocket::SocketState socketState) KISS_del_socket(sender); _KISSSockets.removeOne(sender); + Debugprintf("KISS Disconnect Sock %x", sender); } } @@ -235,7 +500,7 @@ void mynet::sendtoKISS(void * sock, unsigned char * Msg, int Len) QTcpSocket* socket = (QTcpSocket*)sock; socket->write((char *)Msg, Len); } -// free(Msg); + free(Msg); } @@ -514,6 +779,36 @@ void mynet::doHLSetPTT(int c) } +void mynet::domgmtSetPTT(int snd_ch, int PTTState) +{ + char Msg[64]; + uint64_t ret; + + for (QTcpSocket* socket : _MgmtSockets) + { + // Find Session + + TMgmtMode * MGMT = NULL; + + for (int i = 0; i < MgmtConCount; i++) + { + if (MgmtConnections[i]->Socket == socket) + { + MGMT = MgmtConnections[i]; + break; + } + } + + if (MGMT == NULL) + continue; + + if (MGMT->BPQPort[snd_ch]) + { + sprintf(Msg, "PTT %d %s\r", MGMT->BPQPort[snd_ch], PTTState ? "ON" : "OFF"); + ret = socket->write(Msg); + } + } +} diff --git a/tcpCode.h b/tcpCode.h index ea5b5a1..f0bb9a3 100644 --- a/tcpCode.h +++ b/tcpCode.h @@ -4,6 +4,16 @@ #define CONNECT(sndr, sig, rcvr, slt) connect(sndr, SIGNAL(sig), rcvr, SLOT(slt)) +//class myTcpSocket : public QTcpSocket +//{ +//public: +// char Msg[512]; // Received message +// int Len; +// int BPQPort[4]; // BPQ port for each modem + +//}; + + class mynet : public QObject { Q_OBJECT @@ -12,6 +22,7 @@ signals: void HLSetPTT(int c); void FLRigSetPTT(int c); + void mgmtSetPTT(int port, int state); void startTimer(int Time); void stopTimer(); @@ -23,11 +34,17 @@ public: public slots: void onAGWReadyRead(); void onKISSSocketStateChanged(QAbstractSocket::SocketState socketState); + void onMgmtSocketStateChanged(QAbstractSocket::SocketState socketState); + void onMgmtReadyRead(); void onKISSReadyRead(); void onAGWSocketStateChanged(QAbstractSocket::SocketState socketState); void onKISSConnection(); + void onMgmtConnection(); void MyTimerSlot(); void onAGWConnection(); + void on6PackConnection(); + void on6PackReadyRead(); + void on6PackSocketStateChanged(QAbstractSocket::SocketState socketState); void dropPTT(); void displayError(QAbstractSocket::SocketError socketError); @@ -45,6 +62,7 @@ public slots: void dostartTimer(int Time); void dostopTimer(); void doHLSetPTT(int c); + void domgmtSetPTT(int chan, int state); void doFLRigSetPTT(int c); void readPendingDatagrams(); @@ -59,6 +77,7 @@ private: int bytesReceived; int TotalBytes; int PayloadSize; + void MgmtProcessLine(QTcpSocket* socket); }; @@ -86,4 +105,3 @@ public: -