From f7fc865d93f8448779d320ce152f0fa44a64fcad Mon Sep 17 00:00:00 2001 From: Dave Hibberd Date: Mon, 4 Sep 2023 19:06:44 +0100 Subject: [PATCH] New upstream version 0.0.0.67 --- ALSASound.c | 1900 ++++ ARDOPC.c | 2069 ++++ ARDOPC.h | 771 ++ BusyDetect.c | 367 + Calibrate.c | 159 + Config - Copy.cpp | 444 + Config.cpp | 452 + Config.cpp.bak | 439 + DialogButtonBottom.ui | 100 + HEAD | 1 + ModemDialog.ui | 3041 +++++ Modulate.c | 1066 ++ QtSoundModem - Copy.cpp | 2827 +++++ QtSoundModem-HPLaptop.vcxproj | 288 + QtSoundModem.aps | Bin 0 -> 3972 bytes QtSoundModem.cpp | 2877 +++++ QtSoundModem.cpp.bak | 2866 +++++ QtSoundModem.h | 106 + QtSoundModem.ico | Bin 0 -> 766 bytes QtSoundModem.ini | 195 + QtSoundModem.pri | 36 + QtSoundModem.pro | 63 + QtSoundModem.qrc | 5 + QtSoundModem.rc | Bin 0 -> 5232 bytes QtSoundModem.ui | 516 + QtSoundModem.ui.bak | 516 + QtSoundModem.vcxproj | 291 + QtSoundModem.vcxproj-HPLaptop.user | 34 + QtSoundModem.vcxproj.filters | 190 + QtSoundModem.vcxproj.user | 34 + QtSoundModemCopy.vcxproj | 175 + QtSoundModem_resource.rc | 37 + RSUnit.c | 768 ++ Resource.aps | Bin 0 -> 1368 bytes SMMain-HPLaptop.c | 1381 +++ SMMain.c | 1394 +++ SMMain.c.bak | 1383 +++ ShowFilter.cpp | 234 + ShowFilter.cpp.bak | 234 + SoundInput.c | 5253 +++++++++ UZ7HOStuff-HPLaptop.h | 1023 ++ UZ7HOStuff.h | 1058 ++ UZ7HOStuff.h.bak | 1049 ++ UZ7HOUtils.c | 321 + Waveout.c | 992 ++ ardopSampleArrays.c | 16077 +++++++++++++++++++++++++++ audio.c | 327 + ax25.c | 3294 ++++++ ax25_agw.c | 1500 +++ ax25_demod.c | 4313 +++++++ ax25_fec.c | 417 + ax25_l2.c | 1626 +++ ax25_mod.c | 1743 +++ berlekamp.c | 329 + calibrateDialog.ui | 285 + config | 9 + description | 1 + devicesDialog.ui | 976 ++ ecc.h | 102 + fftw3.f | 72 + fftw3.h | 415 + filterWindow.ui | 106 + galois.c | 113 + globals.h | 337 + hid.c | 910 ++ hidapi.h | 386 + il2p.c | 4507 ++++++++ il2p.c.bak | 4502 ++++++++ kiss_mode.c | 490 + libfftw3f-3.def | 1017 ++ libfftw3f-3.dll | Bin 0 -> 2391615 bytes libfftw3f-3.exp | Bin 0 -> 149709 bytes libfftw3f-3.lib | Bin 0 -> 252722 bytes main.cpp | 88 + makeit | 11 + ofdm.c | 1555 +++ pktARDOP.c | 198 + pulse.c | 518 + resource1.h | 14 + rs.c | 214 + rsid.c | 733 ++ rsid.cxx | 1096 ++ rsid.h | 78 + rsid_defs.cxx | 53 + sm_main - Copy.c | 2850 +++++ sm_main.c | 2852 +++++ soundmodem.ico | Bin 0 -> 766 bytes tcpCode.cpp | 759 ++ tcpCode.h | 76 + 89 files changed, 91904 insertions(+) create mode 100644 ALSASound.c create mode 100644 ARDOPC.c create mode 100644 ARDOPC.h create mode 100644 BusyDetect.c create mode 100644 Calibrate.c create mode 100644 Config - Copy.cpp create mode 100644 Config.cpp create mode 100644 Config.cpp.bak create mode 100644 DialogButtonBottom.ui create mode 100644 HEAD create mode 100644 ModemDialog.ui create mode 100644 Modulate.c create mode 100644 QtSoundModem - Copy.cpp create mode 100644 QtSoundModem-HPLaptop.vcxproj create mode 100644 QtSoundModem.aps create mode 100644 QtSoundModem.cpp create mode 100644 QtSoundModem.cpp.bak create mode 100644 QtSoundModem.h create mode 100644 QtSoundModem.ico create mode 100644 QtSoundModem.ini create mode 100644 QtSoundModem.pri create mode 100644 QtSoundModem.pro create mode 100644 QtSoundModem.qrc create mode 100644 QtSoundModem.rc create mode 100644 QtSoundModem.ui create mode 100644 QtSoundModem.ui.bak create mode 100644 QtSoundModem.vcxproj create mode 100644 QtSoundModem.vcxproj-HPLaptop.user create mode 100644 QtSoundModem.vcxproj.filters create mode 100644 QtSoundModem.vcxproj.user create mode 100644 QtSoundModemCopy.vcxproj create mode 100644 QtSoundModem_resource.rc create mode 100644 RSUnit.c create mode 100644 Resource.aps create mode 100644 SMMain-HPLaptop.c create mode 100644 SMMain.c create mode 100644 SMMain.c.bak create mode 100644 ShowFilter.cpp create mode 100644 ShowFilter.cpp.bak create mode 100644 SoundInput.c create mode 100644 UZ7HOStuff-HPLaptop.h create mode 100644 UZ7HOStuff.h create mode 100644 UZ7HOStuff.h.bak create mode 100644 UZ7HOUtils.c create mode 100644 Waveout.c create mode 100644 ardopSampleArrays.c create mode 100644 audio.c create mode 100644 ax25.c create mode 100644 ax25_agw.c create mode 100644 ax25_demod.c create mode 100644 ax25_fec.c create mode 100644 ax25_l2.c create mode 100644 ax25_mod.c create mode 100644 berlekamp.c create mode 100644 calibrateDialog.ui create mode 100644 config create mode 100644 description create mode 100644 devicesDialog.ui create mode 100644 ecc.h create mode 100644 fftw3.f create mode 100644 fftw3.h create mode 100644 filterWindow.ui create mode 100644 galois.c create mode 100644 globals.h create mode 100644 hid.c create mode 100644 hidapi.h create mode 100644 il2p.c create mode 100644 il2p.c.bak create mode 100644 kiss_mode.c create mode 100644 libfftw3f-3.def create mode 100644 libfftw3f-3.dll create mode 100644 libfftw3f-3.exp create mode 100644 libfftw3f-3.lib create mode 100644 main.cpp create mode 100644 makeit create mode 100644 ofdm.c create mode 100644 pktARDOP.c create mode 100644 pulse.c create mode 100644 resource1.h create mode 100644 rs.c create mode 100644 rsid.c create mode 100644 rsid.cxx create mode 100644 rsid.h create mode 100644 rsid_defs.cxx create mode 100644 sm_main - Copy.c create mode 100644 sm_main.c create mode 100644 soundmodem.ico create mode 100644 tcpCode.cpp create mode 100644 tcpCode.h diff --git a/ALSASound.c b/ALSASound.c new file mode 100644 index 0000000..d6739a4 --- /dev/null +++ b/ALSASound.c @@ -0,0 +1,1900 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +//#define TXSILENCE + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ +// +// Audio interface Routine + +// Passes audio samples to/from the sound interface + +// As this is platform specific it also has the main() routine, which does +// platform specific initialisation before calling ardopmain() + +// This is ALSASound.c for Linux +// Windows Version is Waveout.c + + +#include +#include +#include +#include + +#include "UZ7HOStuff.h" + +#define VOID void + +extern int Closing; + +int SoundMode = 0; +int stdinMode = 0; + +//#define SHARECAPTURE // if defined capture device is opened and closed for each transission + +#define HANDLE int + +void gpioSetMode(unsigned gpio, unsigned mode); +void gpioWrite(unsigned gpio, unsigned level); +int WriteLog(char * msg, int Log); +int _memicmp(unsigned char *a, unsigned char *b, int n); +int stricmp(const unsigned char * pStr1, const unsigned char *pStr2); +int gpioInitialise(void); +HANDLE OpenCOMPort(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits); +int CloseSoundCard(); +int PackSamplesAndSend(short * input, int nSamples); +void displayLevel(int max); +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite); +VOID processargs(int argc, char * argv[]); +void PollReceivedSamples(); + + +HANDLE OpenCOMPort(char * Port, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits); +VOID COMSetDTR(HANDLE fd); +VOID COMClearDTR(HANDLE fd); +VOID COMSetRTS(HANDLE fd); +VOID COMClearRTS(HANDLE fd); + +int oss_read(short * samples, int nSamples); +int oss_write(short * ptr, int len); +int oss_flush(); +int oss_audio_open(char * adevice_in, char * adevice_out); +void oss_audio_close(); + +int listpulse(); +int pulse_read(short * ptr, int len); +int pulse_write(short * ptr, int len); +int pulse_flush(); +int pulse_audio_open(char * adevice_in, char * adevice_out); +void pulse_audio_close(); + + +int initdisplay(); + +extern BOOL blnDISCRepeating; +extern BOOL UseKISS; // Enable Packet (KISS) interface + +extern short * DMABuffer; + + +BOOL UseLeft = TRUE; +BOOL UseRight = TRUE; +char LogDir[256] = ""; + +void WriteDebugLog(char * Msg); + +VOID Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + WriteDebugLog(Mess); + + return; +} + + +void Sleep(int mS) +{ + usleep(mS * 1000); + return; +} + + +// Windows and ALSA work with signed samples +- 32767 +// STM32 and Teensy DAC uses unsigned 0 - 4095 + +short buffer[2][1200 * 2]; // Two Transfer/DMA buffers of 0.1 Sec +short inbuffer[1200 * 2]; // Two Transfer/DMA buffers of 0.1 Sec + +BOOL Loopback = FALSE; +//BOOL Loopback = TRUE; + +char CaptureDevice[80] = "plughw:0,0"; +char PlaybackDevice[80] = "plughw:0,0"; + +char * CaptureDevices = CaptureDevice; +char * PlaybackDevices = CaptureDevice; + +int CaptureIndex = 0; +int PlayBackIndex = 0; + +int Ticks; + +int LastNow; + +extern int Number; // Number waiting to be sent + +snd_pcm_sframes_t MaxAvail; + +#include + +FILE *logfile[3] = {NULL, NULL, NULL}; +char LogName[3][256] = {"ARDOPDebug", "ARDOPException", "ARDOPSession"}; + +#define DEBUGLOG 0 +#define EXCEPTLOG 1 +#define SESSIONLOG 2 + +FILE *statslogfile = NULL; + +void printtick(char * msg) +{ + Debugprintf("%s %i", msg, Now - LastNow); + LastNow = Now; +} + +struct timespec time_start; + +unsigned int getTicks() +{ + struct timespec tp; + + clock_gettime(CLOCK_MONOTONIC, &tp); + return (tp.tv_sec - time_start.tv_sec) * 1000 + (tp.tv_nsec - time_start.tv_nsec) / 1000000; +} + +void PlatformSleep(int mS) +{ + Sleep(mS); +} + +// PTT via GPIO code + +#ifdef __ARM_ARCH + +#define PI_INPUT 0 +#define PI_OUTPUT 1 +#define PI_ALT0 4 +#define PI_ALT1 5 +#define PI_ALT2 6 +#define PI_ALT3 7 +#define PI_ALT4 3 +#define PI_ALT5 2 + +// Set GPIO pin as output and set low + +void SetupGPIOPTT() +{ + if (pttGPIOPin == -1) + { + Debugprintf("GPIO PTT disabled"); + useGPIO = FALSE; + } + else + { + if (pttGPIOPin < 0) { + pttGPIOInvert = TRUE; + pttGPIOPin = -pttGPIOPin; + } + + gpioSetMode(pttGPIOPin, PI_OUTPUT); + gpioWrite(pttGPIOPin, pttGPIOInvert ? 1 : 0); + Debugprintf("Using GPIO pin %d for Left/Mono PTT", pttGPIOPin); + + if (pttGPIOPinR != -1) + { + gpioSetMode(pttGPIOPinR, PI_OUTPUT); + gpioWrite(pttGPIOPinR, pttGPIOInvert ? 1 : 0); + Debugprintf("Using GPIO pin %d for Right PTT", pttGPIOPin); + } + + useGPIO = TRUE; + } +} +#endif + + +static void sigterm_handler(int n) +{ + UNUSED(n); + + printf("terminating on SIGTERM\n"); + Closing = TRUE; +} + +static void sigint_handler(int n) +{ + UNUSED(n); + + printf("terminating on SIGINT\n"); + Closing = TRUE; +} + +char * PortString = NULL; + + +void platformInit() +{ + struct sigaction act; + +// Sleep(1000); // Give LinBPQ time to complete init if exec'ed by linbpq + + // Get Time Reference + + clock_gettime(CLOCK_MONOTONIC, &time_start); + LastNow = getTicks(); + + // Trap signals + + memset (&act, '\0', sizeof(act)); + + act.sa_handler = &sigint_handler; + if (sigaction(SIGINT, &act, NULL) < 0) + perror ("SIGINT"); + + act.sa_handler = &sigterm_handler; + if (sigaction(SIGTERM, &act, NULL) < 0) + perror ("SIGTERM"); + + act.sa_handler = SIG_IGN; + + if (sigaction(SIGHUP, &act, NULL) < 0) + perror ("SIGHUP"); + + if (sigaction(SIGPIPE, &act, NULL) < 0) + perror ("SIGPIPE"); +} + +void txSleep(int mS) +{ + // called while waiting for next TX buffer or to delay response. + // Run background processes + + // called while waiting for next TX buffer. Run background processes + + while (mS > 50) + { + PollReceivedSamples(); // discard any received samples + + Sleep(50); + mS -= 50; + } + + Sleep(mS); + + PollReceivedSamples(); // discard any received samples +} + +// ALSA Code + +#define true 1 +#define false 0 + +snd_pcm_t * playhandle = NULL; +snd_pcm_t * rechandle = NULL; + +int m_playchannels = 2; +int m_recchannels = 2; + + +char SavedCaptureDevice[256]; // Saved so we can reopen +char SavedPlaybackDevice[256]; + +int Savedplaychannels = 2; + +int SavedCaptureRate; +int SavedPlaybackRate; + +char CaptureNames[16][256] = { "" }; +char PlaybackNames[16][256] = { "" }; + +int PlaybackCount = 0; +int CaptureCount = 0; + +// Routine to check that library is available + +int CheckifLoaded() +{ + // Prevent CTRL/C from closing the TNC + // (This causes problems if the TNC is started by LinBPQ) + + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + + return TRUE; +} + +int GetOutputDeviceCollection() +{ + // Get all the suitable devices and put in a list for GetNext to return + + snd_ctl_t *handle= NULL; + snd_pcm_t *pcm= NULL; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_hw_params_t *pars; + snd_pcm_format_mask_t *fmask; + char NameString[256]; + + Debugprintf("Playback Devices\n"); + + CloseSoundCard(); + + // free old struct if called again + +// while (PlaybackCount) +// { +// PlaybackCount--; +// free(PlaybackNames[PlaybackCount]); +// } + +// if (PlaybackNames) +// free(PlaybackNames); + + PlaybackCount = 0; + + // Get Device List from ALSA + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + snd_pcm_hw_params_alloca(&pars); + snd_pcm_format_mask_alloca(&fmask); + + char hwdev[80]; + unsigned min, max, ratemin, ratemax; + int card, err, dev, nsubd; + snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; + + card = -1; + + if (snd_card_next(&card) < 0) + { + Debugprintf("No Devices"); + return 0; + } + + if (playhandle) + snd_pcm_close(playhandle); + + playhandle = NULL; + + while (card >= 0) + { + sprintf(hwdev, "hw:%d", card); + err = snd_ctl_open(&handle, hwdev, 0); + err = snd_ctl_card_info(handle, info); + + Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), + snd_ctl_card_info_get_name(info)); + + + dev = -1; + + if(snd_ctl_pcm_next_device(handle, &dev) < 0) + { + // Card has no devices + + snd_ctl_close(handle); + goto nextcard; + } + + while (dev >= 0) + { + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + + err = snd_ctl_pcm_info(handle, pcminfo); + + + if (err == -ENOENT) + goto nextdevice; + + nsubd = snd_pcm_info_get_subdevices_count(pcminfo); + + Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", + card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), + nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); + + sprintf(hwdev, "hw:%d,%d", card, dev); + + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening output device", err); + goto nextdevice; + } + + // Get parameters for this device + + err = snd_pcm_hw_params_any(pcm, pars); + + snd_pcm_hw_params_get_channels_min(pars, &min); + snd_pcm_hw_params_get_channels_max(pars, &max); + + snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); + snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); + + if( min == max ) + if( min == 1 ) + Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); + else + Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); + else + Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); + + // Add device to list + + sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, + snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); + + strcpy(PlaybackNames[PlaybackCount++], NameString); + + snd_pcm_close(pcm); + pcm= NULL; + +nextdevice: + if (snd_ctl_pcm_next_device(handle, &dev) < 0) + break; + } + snd_ctl_close(handle); + +nextcard: + + Debugprintf(""); + + if (snd_card_next(&card) < 0) // No more cards + break; + } + + return PlaybackCount; +} + + +int GetInputDeviceCollection() +{ + // Get all the suitable devices and put in a list for GetNext to return + + snd_ctl_t *handle= NULL; + snd_pcm_t *pcm= NULL; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_pcm_hw_params_t *pars; + snd_pcm_format_mask_t *fmask; + char NameString[256]; + + Debugprintf("Capture Devices\n"); + + CaptureCount = 0; + + // Get Device List from ALSA + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + snd_pcm_hw_params_alloca(&pars); + snd_pcm_format_mask_alloca(&fmask); + + char hwdev[80]; + unsigned min, max, ratemin, ratemax; + int card, err, dev, nsubd; + snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE; + + card = -1; + + if(snd_card_next(&card) < 0) + { + Debugprintf("No Devices"); + return 0; + } + + if (rechandle) + snd_pcm_close(rechandle); + + rechandle = NULL; + + while(card >= 0) + { + sprintf(hwdev, "hw:%d", card); + err = snd_ctl_open(&handle, hwdev, 0); + err = snd_ctl_card_info(handle, info); + + Debugprintf("Card %d, ID `%s', name `%s'", card, snd_ctl_card_info_get_id(info), + snd_ctl_card_info_get_name(info)); + + dev = -1; + + if (snd_ctl_pcm_next_device(handle, &dev) < 0) // No Devicdes + { + snd_ctl_close(handle); + goto nextcard; + } + + while(dev >= 0) + { + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + err= snd_ctl_pcm_info(handle, pcminfo); + + if (err == -ENOENT) + goto nextdevice; + + nsubd= snd_pcm_info_get_subdevices_count(pcminfo); + Debugprintf(" Device hw:%d,%d ID `%s', name `%s', %d subdevices (%d available)", + card, dev, snd_pcm_info_get_id(pcminfo), snd_pcm_info_get_name(pcminfo), + nsubd, snd_pcm_info_get_subdevices_avail(pcminfo)); + + sprintf(hwdev, "hw:%d,%d", card, dev); + + err = snd_pcm_open(&pcm, hwdev, stream, SND_PCM_NONBLOCK); + + if (err) + { + Debugprintf("Error %d opening input device", err); + goto nextdevice; + } + + err = snd_pcm_hw_params_any(pcm, pars); + + snd_pcm_hw_params_get_channels_min(pars, &min); + snd_pcm_hw_params_get_channels_max(pars, &max); + snd_pcm_hw_params_get_rate_min(pars, &ratemin, NULL); + snd_pcm_hw_params_get_rate_max(pars, &ratemax, NULL); + + if( min == max ) + if( min == 1 ) + Debugprintf(" 1 channel, sampling rate %u..%u Hz", ratemin, ratemax); + else + Debugprintf(" %d channels, sampling rate %u..%u Hz", min, ratemin, ratemax); + else + Debugprintf(" %u..%u channels, sampling rate %u..%u Hz", min, max, ratemin, ratemax); + + sprintf(NameString, "hw:%d,%d %s(%s)", card, dev, + snd_pcm_info_get_name(pcminfo), snd_ctl_card_info_get_name(info)); + +// Debugprintf("%s", NameString); + + strcpy(CaptureNames[CaptureCount++], NameString); + + snd_pcm_close(pcm); + pcm= NULL; + +nextdevice: + if (snd_ctl_pcm_next_device(handle, &dev) < 0) + break; + } + snd_ctl_close(handle); +nextcard: + + Debugprintf(""); + if (snd_card_next(&card) < 0 ) + break; + } + + strcpy(CaptureNames[CaptureCount++], "stdin"); + + return CaptureCount; +} + +int OpenSoundPlayback(char * PlaybackDevice, int m_sampleRate, int channels, int Report) +{ + int err = 0; + + char buf1[100]; + char * ptr; + + if (playhandle) + { + snd_pcm_close(playhandle); + playhandle = NULL; + } + + strcpy(SavedPlaybackDevice, PlaybackDevice); // Saved so we can reopen in error recovery + SavedPlaybackRate = m_sampleRate; + + if (strstr(PlaybackDevice, "plug") == 0 && strchr(PlaybackDevice, ':')) + sprintf(buf1, "plug%s", PlaybackDevice); + else + strcpy(buf1, PlaybackDevice); + + if (Report) + Debugprintf("Real Device %s", buf1); + + + ptr = strchr(buf1, ' '); + if (ptr) *ptr = 0; // Get Device part of name + + snd_pcm_hw_params_t *hw_params; + + if ((err = snd_pcm_open(&playhandle, buf1, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) + { + Debugprintf("cannot open playback audio device %s (%s)", buf1, snd_strerror(err)); + return false; + } + + if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) + { + Debugprintf("cannot allocate hardware parameter structure (%s)", snd_strerror(err)); + return false; + } + + if ((err = snd_pcm_hw_params_any (playhandle, hw_params)) < 0) { + Debugprintf("cannot initialize hardware parameter structure (%s)", snd_strerror(err)); + return false; + } + + if ((err = snd_pcm_hw_params_set_access (playhandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + Debugprintf("cannot set playback access type (%s)", snd_strerror (err)); + return false; + } + if ((err = snd_pcm_hw_params_set_format (playhandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { + Debugprintf("cannot setplayback sample format (%s)", snd_strerror(err)); + return false; + } + + if ((err = snd_pcm_hw_params_set_rate (playhandle, hw_params, m_sampleRate, 0)) < 0) { + Debugprintf("cannot set playback sample rate (%s)", snd_strerror(err)); + return false; + } + + // Initial call has channels set to 1. Subequent ones set to what worked last time + + channels = 2; + + if ((err = snd_pcm_hw_params_set_channels (playhandle, hw_params, channels)) < 0) + { + Debugprintf("cannot set play channel count to %d (%s)", channels, snd_strerror(err)); + + if (channels == 2) + return false; // Shouldn't happen as should have worked before + + channels = 2; + + if ((err = snd_pcm_hw_params_set_channels (playhandle, hw_params, 2)) < 0) + { + Debugprintf("cannot play set channel count to 2 (%s)", snd_strerror(err)); + return false; + } + } + + if (Report) + Debugprintf("Play using %d channels", channels); + + if ((err = snd_pcm_hw_params (playhandle, hw_params)) < 0) + { + Debugprintf("cannot set parameters (%s)", snd_strerror(err)); + return false; + } + + snd_pcm_hw_params_free(hw_params); + + if ((err = snd_pcm_prepare (playhandle)) < 0) + { + Debugprintf("cannot prepare audio interface for use (%s)", snd_strerror(err)); + return false; + } + + Savedplaychannels = m_playchannels = channels; + + MaxAvail = snd_pcm_avail_update(playhandle); + + if (Report) + Debugprintf("Playback Buffer Size %d", (int)MaxAvail); + + return true; +} + +int OpenSoundCapture(char * CaptureDevice, int m_sampleRate, int Report) +{ + int err = 0; + + char buf1[100]; + char * ptr; + snd_pcm_hw_params_t *hw_params; + + if (strcmp(CaptureDevice, "stdin") == 0) + { + stdinMode = 1; + + Debugprintf("Input from stdin"); + return TRUE; + } + + if (rechandle) + { + snd_pcm_close(rechandle); + rechandle = NULL; + } + + strcpy(SavedCaptureDevice, CaptureDevice); // Saved so we can reopen in error recovery + SavedCaptureRate = m_sampleRate; + + if (strstr(CaptureDevice, "plug") == 0 && strchr(CaptureDevice, ':')) + sprintf(buf1, "plug%s", CaptureDevice); + else + strcpy(buf1, CaptureDevice); + + if (Report) + Debugprintf("Real Device %s", buf1); + + ptr = strchr(buf1, ' '); + if (ptr) *ptr = 0; // Get Device part of name + + if ((err = snd_pcm_open (&rechandle, buf1, SND_PCM_STREAM_CAPTURE, 0)) < 0) { + Debugprintf("cannot open capture audio device %s (%s)", buf1, snd_strerror(err)); + return false; + } + + if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { + Debugprintf("cannot allocate capture hardware parameter structure (%s)", snd_strerror(err)); + return false; + } + + if ((err = snd_pcm_hw_params_any (rechandle, hw_params)) < 0) { + Debugprintf("cannot initialize capture hardware parameter structure (%s)", snd_strerror(err)); + return false; + } + + if ((err = snd_pcm_hw_params_set_access (rechandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + Debugprintf("cannot set capture access type (%s)", snd_strerror (err)); + return false; + } + if ((err = snd_pcm_hw_params_set_format (rechandle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0) { + Debugprintf("cannot set capture sample format (%s)", snd_strerror(err)); + return false; + } + + if ((err = snd_pcm_hw_params_set_rate (rechandle, hw_params, m_sampleRate, 0)) < 0) { + Debugprintf("cannot set capture sample rate (%s)", snd_strerror(err)); + return false; + } + + m_recchannels = 2; + + if ((err = snd_pcm_hw_params_set_channels(rechandle, hw_params, m_recchannels)) < 0) + { + Debugprintf("cannot set rec channel count to 2 (%s)", snd_strerror(err)); + + m_recchannels = 1; + + if ((err = snd_pcm_hw_params_set_channels(rechandle, hw_params, 1)) < 0) + { + Debugprintf("cannot set rec channel count to 1 (%s)", snd_strerror(err)); + return false; + } + if (Report) + Debugprintf("Record channel count set to 1"); + } + else + if (Report) + Debugprintf("Record channel count set to 2"); + + /* + { + unsigned int val = 0; + unsigned int dir = 0, frames = 0; + + + snd_pcm_hw_params_get_channels(rechandle, &val); + printf("channels = %d\n", val); + + snd_pcm_hw_params_get_rate(rechandle, &val, &dir); + printf("rate = %d bps\n", val); + + snd_pcm_hw_params_get_period_time(rechandle, &val, &dir); + printf("period time = %d us\n", val); + + snd_pcm_hw_params_get_period_size(rechandle, &frames, &dir); + printf("period size = %d frames\n", (int)frames); + + snd_pcm_hw_params_get_buffer_time(rechandle, &val, &dir); + printf("buffer time = %d us\n", val); + + snd_pcm_hw_params_get_buffer_size(rechandle, (snd_pcm_uframes_t *)&val); + printf("buffer size = %d frames\n", val); + + snd_pcm_hw_params_get_periods(rechandle, &val, &dir); + printf("periods per buffer = %d frames\n", val); + } + */ + + if ((err = snd_pcm_hw_params (rechandle, hw_params)) < 0) + { + // Try setting some more params Have to reinit params + + snd_pcm_hw_params_any(rechandle, hw_params); + snd_pcm_hw_params_set_access(rechandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); + snd_pcm_hw_params_set_format(rechandle, hw_params, SND_PCM_FORMAT_S16_LE); + snd_pcm_hw_params_set_rate(rechandle, hw_params, m_sampleRate, 0); + snd_pcm_hw_params_set_channels(rechandle, hw_params, m_recchannels); + + err = snd_pcm_hw_params_set_buffer_size(rechandle, hw_params, 65536); + + if (err) + Debugprintf("cannot set buffer size (%s)", snd_strerror(err)); + + err = snd_pcm_hw_params_set_period_size(rechandle, hw_params, (snd_pcm_uframes_t) { 1024 }, (int) { 0 }); + + if (err) + Debugprintf("cannot set period size (%s)", snd_strerror(err)); + + if ((err = snd_pcm_hw_params(rechandle, hw_params)) < 0) + { + Debugprintf("cannot set parameters (%s)", snd_strerror(err)); + return false; + } + } + + snd_pcm_hw_params_free(hw_params); + + if ((err = snd_pcm_prepare (rechandle)) < 0) { + Debugprintf("cannot prepare audio interface for use (%s)", snd_strerror(err)); + return FALSE; + } + + if (Report) + Debugprintf("Capture using %d channels", m_recchannels); + + int i; + short buf[256]; + + for (i = 0; i < 10; ++i) + { + if ((err = snd_pcm_readi (rechandle, buf, 128)) != 128) + { + Debugprintf("read from audio interface failed (%s)", snd_strerror (err)); + } + } + +// Debugprintf("Read got %d", err); + + return TRUE; +} + +int OpenSoundCard(char * CaptureDevice, char * PlaybackDevice, int c_sampleRate, int p_sampleRate, int Report) +{ + int Channels = 1; + + if (Report) + Debugprintf("Opening Playback Device %s Rate %d", PlaybackDevice, p_sampleRate); + +// if (UseLeft == 0 || UseRight == 0) + Channels = 2; // L or R implies stereo + + if (OpenSoundPlayback(PlaybackDevice, p_sampleRate, Channels, Report)) + { +#ifdef SHARECAPTURE + + // Close playback device so it can be shared + + if (playhandle) + { + snd_pcm_close(playhandle); + playhandle = NULL; + } +#endif + if (Report) + Debugprintf("Opening Capture Device %s Rate %d", CaptureDevice, c_sampleRate); + return OpenSoundCapture(CaptureDevice, c_sampleRate, Report); + } + else + return false; +} + + + +int CloseSoundCard() +{ + if (rechandle) + { + snd_pcm_close(rechandle); + rechandle = NULL; + } + + if (playhandle) + { + snd_pcm_close(playhandle); + playhandle = NULL; + } + return 0; +} + + +int SoundCardWrite(short * input, int nSamples) +{ + unsigned int ret; + snd_pcm_sframes_t avail; // , maxavail; + + if (playhandle == NULL) + return 0; + + // Stop Capture + + if (rechandle) + { + snd_pcm_close(rechandle); + rechandle = NULL; + } + + avail = snd_pcm_avail_update(playhandle); +// Debugprintf("avail before play returned %d", (int)avail); + + if (avail < 0) + { + if (avail != -32) + Debugprintf("Playback Avail Recovering from %d ..", (int)avail); + snd_pcm_recover(playhandle, avail, 1); + + avail = snd_pcm_avail_update(playhandle); + + if (avail < 0) + Debugprintf("avail play after recovery returned %d", (int)avail); + } + +// maxavail = avail; + +// Debugprintf("Tosend %d Avail %d", nSamples, (int)avail); + + while (avail < nSamples || (MaxAvail - avail) > 12000) // Limit to 1 sec of audio + { + txSleep(10); + avail = snd_pcm_avail_update(playhandle); +// Debugprintf("After Sleep Tosend %d Avail %d", nSamples, (int)avail); + } + + ret = PackSamplesAndSend(input, nSamples); + + return ret; +} + +int PackSamplesAndSend(short * input, int nSamples) +{ + unsigned short samples[256000]; + int ret; + + ret = snd_pcm_writei(playhandle, input, nSamples); + + if (ret < 0) + { +// Debugprintf("Write Recovering from %d ..", ret); + snd_pcm_recover(playhandle, ret, 1); + ret = snd_pcm_writei(playhandle, samples, nSamples); +// Debugprintf("Write after recovery returned %d", ret); + } + + snd_pcm_avail_update(playhandle); + return ret; + +} +/* +int xSoundCardClearInput() +{ + short samples[65536]; + int n; + int ret; + int avail; + + if (rechandle == NULL) + return 0; + + // Clear queue + + avail = snd_pcm_avail_update(rechandle); + + if (avail < 0) + { + Debugprintf("Discard Recovering from %d ..", avail); + if (rechandle) + { + snd_pcm_close(rechandle); + rechandle = NULL; + } + OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, NULL); + avail = snd_pcm_avail_update(rechandle); + } + + while (avail) + { + if (avail > 65536) + avail = 65536; + + ret = snd_pcm_readi(rechandle, samples, avail); +// Debugprintf("Discarded %d samples from card", ret); + avail = snd_pcm_avail_update(rechandle); + +// Debugprintf("Discarding %d samples from card", avail); + } + return 0; +} +*/ + +int SoundCardRead(short * input, int nSamples) +{ + short samples[65536]; + int n; + int ret; + int avail; + + if (SoundMode == 1) // OSS + { + ret = oss_read(samples, nSamples); + } + else if (SoundMode == 2)// Pulse + { + ret = pulse_read(samples, nSamples); + } + else + { + if (rechandle == NULL) + return 0; + + avail = snd_pcm_avail_update(rechandle); + + if (avail < 0) + { + Debugprintf("avail Recovering from %d ..", avail); + if (rechandle) + { + snd_pcm_close(rechandle); + rechandle = NULL; + } + + OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, 0); + // snd_pcm_recover(rechandle, avail, 0); + avail = snd_pcm_avail_update(rechandle); + Debugprintf("After avail recovery %d ..", avail); + } + + if (avail < nSamples) + return 0; + + // Debugprintf("ALSARead available %d", avail); + + ret = snd_pcm_readi(rechandle, samples, nSamples); + + if (ret < 0) + { + Debugprintf("RX Error %d", ret); + // snd_pcm_recover(rechandle, avail, 0); + if (rechandle) + { + snd_pcm_close(rechandle); + rechandle = NULL; + } + + OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, 0); + // snd_pcm_recover(rechandle, avail, 0); + avail = snd_pcm_avail_update(rechandle); + Debugprintf("After Read recovery Avail %d ..", avail); + + return 0; + } + } + + + if (ret < nSamples) + return 0; + + if (m_recchannels == 1) + { + for (n = 0; n < ret; n++) + { + *(input++) = samples[n]; + *(input++) = samples[n]; // Duplicate + } + } + else + { + for (n = 0; n < ret * 2; n++) // return all + { + *(input++) = samples[n]; + } + } + + return ret; +} + + + + +int PriorSize = 0; + +int Index = 0; // DMA Buffer being used 0 or 1 +int inIndex = 0; // DMA Buffer being used 0 or 1 + +BOOL DMARunning = FALSE; // Used to start DMA on first write + +void ProcessNewSamples(short * Samples, int nSamples); + +short * SendtoCard(short * buf, int n) +{ + if (Loopback) + { + // Loop back to decode for testing + + ProcessNewSamples(buf, 1200); // signed + } + + if (SoundMode == 1) // OSS + oss_write(buf, n); + else if (SoundMode == 2) // Pulse + pulse_write(buf, n); + else + { + if (playhandle) + SoundCardWrite(buf, n); + + // txSleep(10); // Run buckground while waiting + } + + Index = !Index; + return &buffer[Index][0]; +} + +short loopbuff[1200]; // Temp for testing - loop sent samples to decoder + + +// // This generates a nice musical pattern for sound interface testing +// for (t = 0; t < sizeof(buffer); ++t) +// buffer[t] =((((t * (t >> 8 | t >> 9) & 46 & t >> 8)) ^ (t & t >> 13 | t >> 6)) & 0xFF); + +short * SoundInit(); + +void GetSoundDevices() +{ + if (SoundMode == 0) + { + GetInputDeviceCollection(); + GetOutputDeviceCollection(); + } + else if (SoundMode == 1) + { + PlaybackCount = 3; + + strcpy(&PlaybackNames[0][0], "/dev/dsp0"); + strcpy(&PlaybackNames[1][0], "/dev/dsp1"); + strcpy(&PlaybackNames[2][0], "/dev/dsp2"); + + CaptureCount = 3; + + strcpy(&CaptureNames[0][0], "/dev/dsp0"); + strcpy(&CaptureNames[1][0], "/dev/dsp1"); + strcpy(&CaptureNames[2][0], "/dev/dsp2"); + } + else if (SoundMode == 2) + { + // Pulse + + listpulse(); + } +} + +int InitSound(BOOL Quiet) +{ + GetSoundDevices(); + + switch (SoundMode) + { + case 0: // ALSA + + if (!OpenSoundCard(CaptureDevice, PlaybackDevice, 12000, 12000, Quiet)) + return FALSE; + + break; + + case 1: // OSS + + if (!oss_audio_open(CaptureDevice, PlaybackDevice)) + return FALSE; + + break; + + case 2: // PulseAudio + + if (!pulse_audio_open(CaptureDevice, PlaybackDevice)) + return FALSE; + + break; + + } + + printf("InitSound %s %s\n", CaptureDevice, PlaybackDevice); + + DMABuffer = SoundInit(); + return TRUE; +} + +int min = 0, max = 0, lastlevelreport = 0, lastlevelGUI = 0; +UCHAR CurrentLevel = 0; // Peak from current samples + +void PollReceivedSamples() +{ + // Process any captured samples + // Ideally call at least every 100 mS, more than 200 will loose data + + int bytes; +#ifdef TXSILENCE + SendSilence(); // send silence (attempt to fix CM delay issue) +#endif + + if (stdinMode) + { + // will block if no input. May get less, in which case wait a bit then try to read rest + + // rtl_udp outputs mono samples + + short input[1200]; + short * ptr1, *ptr2; + int n = 20; // Max Wait + + bytes = read(STDIN_FILENO, input, ReceiveSize * 2); // 4 = Stereo 2 bytes per sample + + while (bytes < ReceiveSize * 2 && n--) + { + Sleep(50); //mS + bytes += read(STDIN_FILENO, &input[bytes / 2], (ReceiveSize * 2) - bytes); + } + + // if still not enough, too bad! + + if (bytes != ReceiveSize * 2) + Debugprintf("Short Read %d", bytes); + + // convert to stereo + + ptr1 = input; + ptr2 = inbuffer; + n = ReceiveSize; + + while (n--) + { + *ptr2++ = *ptr1; + *ptr2++ = *ptr1++; + } + } + else + bytes = SoundCardRead(inbuffer, ReceiveSize); // returns ReceiveSize or none + + if (bytes > 0) + { + short * ptr = inbuffer; + int i; + + for (i = 0; i < ReceiveSize; i++) + { + if (*(ptr) < min) + min = *ptr; + else if (*(ptr) > max) + max = *ptr; + ptr++; + } + + + CurrentLevel = ((max - min) * 75) /32768; // Scale to 150 max + + if ((Now - lastlevelGUI) > 2000) // 2 Secs + { + lastlevelGUI = Now; + + if ((Now - lastlevelreport) > 10000) // 10 Secs + { + char HostCmd[64]; + lastlevelreport = Now; + + sprintf(HostCmd, "INPUTPEAKS %d %d", min, max); + + Debugprintf("Input peaks = %d, %d", min, max); + } + min = max = 0; // Every 2 secs + } + + ProcessNewSamples(inbuffer, ReceiveSize); + } +} + +void StopCapture() +{ + Capturing = FALSE; + +#ifdef SHARECAPTURE + + // Stopcapture is only called when we are about to transmit, so use it to open plaback device. We don't keep + // it open all the time to facilitate sharing. + + OpenSoundPlayback(SavedPlaybackDevice, SavedPlaybackRate, Savedplaychannels, NULL); +#endif +} + +void StartCapture() +{ + Capturing = TRUE; + +// Debugprintf("Start Capture"); +} + +void CloseSound() +{ + switch (SoundMode) + { + case 0: // ALSA + + CloseSoundCard(); + return; + + case 1: // OSS + + oss_audio_close(); + return; + + case 2: // PulseAudio + + pulse_audio_close(); + return; + } +} + +short * SoundInit() +{ + Index = 0; + return &buffer[0][0]; +} + +// Called at end of transmission + +void SoundFlush() +{ + // Append Trailer then send remaining samples + + snd_pcm_status_t *status = NULL; + int err, res; + int lastavail = 0; + + if (Loopback) + ProcessNewSamples(&buffer[Index][0], Number); + + SendtoCard(&buffer[Index][0], Number); + + // Wait for tx to complete + + Debugprintf("Flush Soundmode = %d", SoundMode); + + if (SoundMode == 0) // ALSA + { + usleep(100000); + + while (1 && playhandle) + { + snd_pcm_sframes_t avail = snd_pcm_avail_update(playhandle); + + // Debugprintf("Waiting for complete. Avail %d Max %d", avail, MaxAvail); + + snd_pcm_status_alloca(&status); // alloca allocates once per function, does not need a free + +// Debugprintf("Waiting for complete. Avail %d Max %d last %d", avail, MaxAvail, lastavail); + + if ((err = snd_pcm_status(playhandle, status)) != 0) + { + Debugprintf("snd_pcm_status() failed: %s", snd_strerror(err)); + break; + } + + res = snd_pcm_status_get_state(status); + + // Debugprintf("PCM Status = %d", res); + + if (res != SND_PCM_STATE_RUNNING || lastavail == avail) // If sound system is not running then it needs data +// if (res != SND_PCM_STATE_RUNNING) // If sound system is not running then it needs data + // if (MaxAvail - avail < 100) + { + // Send complete - Restart Capture + + OpenSoundCapture(SavedCaptureDevice, SavedCaptureRate, 0); + break; + } + lastavail = avail; + usleep(50000); + } + // I think we should turn round the link here. I dont see the point in + // waiting for MainPoll + +#ifdef SHARECAPTURE + if (playhandle) + { + snd_pcm_close(playhandle); + playhandle = NULL; + } +#endif + } + else if (SoundMode == 1) + { + oss_flush(); + } + else if (SoundMode == 2) + { + pulse_flush(); + } + + SoundIsPlaying = FALSE; + + Number = 0; + + memset(buffer, 0, sizeof(buffer)); + DMABuffer = &buffer[0][0]; + +#ifdef TXSILENCE + SendtoCard(&buffer[0][0], 1200); // Start sending silence (attempt to fix CM delay issue) +#endif + + StartCapture(); + return; +} + +#ifdef TXSILENCE + +// send silence (attempt to fix CM delay issue) + + +void SendSilence() +{ + short buffer[2400]; + + snd_pcm_sframes_t Avail = snd_pcm_avail_update(playhandle); + + if ((MaxAvail - Avail) < 1200) + { + // Keep at least 100 ms of audio in buffer + +// printtick("Silence"); + + memset(buffer, 0, sizeof(buffer)); + SendtoCard(buffer, 1200); // Start sending silence (attempt to fix CM delay issue) + } +} + +#endif + +// GPIO access stuff for PTT on PI + +#ifdef __ARM_ARCH + +/* + tiny_gpio.c + 2016-04-30 + Public Domain +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPSET0 7 +#define GPSET1 8 + +#define GPCLR0 10 +#define GPCLR1 11 + +#define GPLEV0 13 +#define GPLEV1 14 + +#define GPPUD 37 +#define GPPUDCLK0 38 +#define GPPUDCLK1 39 + +unsigned piModel; +unsigned piRev; + +static volatile uint32_t *gpioReg = MAP_FAILED; + +#define PI_BANK (gpio>>5) +#define PI_BIT (1<<(gpio&0x1F)) + +/* gpio modes. */ + +void gpioSetMode(unsigned gpio, unsigned mode) +{ + int reg, shift; + + reg = gpio/10; + shift = (gpio%10) * 3; + + gpioReg[reg] = (gpioReg[reg] & ~(7<> shift) & 7; +} + +/* Values for pull-ups/downs off, pull-down and pull-up. */ + +#define PI_PUD_OFF 0 +#define PI_PUD_DOWN 1 +#define PI_PUD_UP 2 + +void gpioSetPullUpDown(unsigned gpio, unsigned pud) +{ + *(gpioReg + GPPUD) = pud; + + usleep(20); + + *(gpioReg + GPPUDCLK0 + PI_BANK) = PI_BIT; + + usleep(20); + + *(gpioReg + GPPUD) = 0; + + *(gpioReg + GPPUDCLK0 + PI_BANK) = 0; +} + +int gpioRead(unsigned gpio) +{ + if ((*(gpioReg + GPLEV0 + PI_BANK) & PI_BIT) != 0) return 1; + else return 0; +} +void gpioWrite(unsigned gpio, unsigned level) +{ + if (level == 0) + *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; + else + *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; +} + +void gpioTrigger(unsigned gpio, unsigned pulseLen, unsigned level) +{ + if (level == 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; + else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; + + usleep(pulseLen); + + if (level != 0) *(gpioReg + GPCLR0 + PI_BANK) = PI_BIT; + else *(gpioReg + GPSET0 + PI_BANK) = PI_BIT; +} + +/* Bit (1<user_speed != -1; s++) + if (s->user_speed == speed) + break; + + if (s->user_speed == -1) + { + fprintf(stderr, "tty_speed: invalid speed %d", speed); + return FALSE; + } + + if (tcgetattr(fd, &term) == -1) + { + perror("tty_speed: tcgetattr"); + return FALSE; + } + + cfmakeraw(&term); + cfsetispeed(&term, s->termios_speed); + cfsetospeed(&term, s->termios_speed); + + if (tcsetattr(fd, TCSANOW, &term) == -1) + { + perror("tty_speed: tcsetattr"); + return FALSE; + } + + ioctl(fd, FIONBIO, ¶m); + + Debugprintf("Port %s fd %d", fulldev, fd); + + if (SetDTR) + COMSetDTR(fd); + else + COMClearDTR(fd); + + if (SetRTS) + COMSetRTS(fd); + else + COMClearRTS(fd); + + return fd; +} + +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite) +{ + // Some systems seem to have a very small max write size + + int ToSend = BytesToWrite; + int Sent = 0, ret; + + while (ToSend) + { + ret = write(fd, &Block[Sent], ToSend); + + if (ret >= ToSend) + return TRUE; + +// perror("WriteCOM"); + + if (ret == -1) + { + if (errno != 11 && errno != 35) // Would Block + return FALSE; + + usleep(10000); + ret = 0; + } + + Sent += ret; + ToSend -= ret; + } + return TRUE; +} + +VOID CloseCOMPort(HANDLE fd) +{ + close(fd); +} + diff --git a/ARDOPC.c b/ARDOPC.c new file mode 100644 index 0000000..3f93242 --- /dev/null +++ b/ARDOPC.c @@ -0,0 +1,2069 @@ +#ifdef WIN32 +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#pragma comment(lib, "winmm.lib") +#else +#define SOCKET int +#include +#define closesocket close +#endif + +//#include "Version.h" + +#include "ARDOPC.h" +//#include "getopt.h" + +void CompressCallsign(char * Callsign, UCHAR * Compressed); +void CompressGridSquare(char * Square, UCHAR * Compressed); +void ASCIIto6Bit(char * Padded, UCHAR * Compressed); +void GetTwoToneLeaderWithSync(int intSymLen); +void SendID(BOOL blnEnableCWID); +void PollReceivedSamples(); +void CheckTimers(); +BOOL GetNextARQFrame(); +BOOL TCPHostInit(); +BOOL SerialHostInit(); +BOOL KISSInit(); +void SerialHostPoll(); +void TCPHostPoll(); +BOOL MainPoll(); +VOID PacketStartTX(); +void PlatformSleep(int mS); +BOOL BusyDetect2(float * dblMag, int intStart, int intStop); +BOOL IsPingToMe(char * strCallsign); +void LookforPacket(float * dblMag, float dblMagAvg, int count, float * real, float * imag); +void PktARDOPStartTX(); + +// Config parameters + +char GridSquare[9] = "No GS "; +char Callsign[10] = ""; +BOOL wantCWID = FALSE; +BOOL NeedID = FALSE; // SENDID Command Flag +BOOL NeedCWID = FALSE; +BOOL NeedConReq = FALSE; // ARQCALL Command Flag +BOOL NeedPing = FALSE; // PING Command Flag +BOOL NeedCQ = FALSE; // PING Command Flag +BOOL NeedTwoToneTest = FALSE; +BOOL UseKISS = TRUE; // Enable Packet (KISS) interface +int PingCount; +int CQCount; + + +BOOL blnPINGrepeating = False; +BOOL blnFramePending = False; // Cancels last repeat +int intPINGRepeats = 0; + +char ConnectToCall[16] = ""; + + +#ifdef TEENSY +int LeaderLength = 500; +#else +int LeaderLength = 300; +#endif +int TrailerLength = 0; +unsigned int ARQTimeout = 120; +int TuningRange = 100; +int TXLevel = 300; // 300 mV p-p Used on Teensy +//int RXLevel = 0; // Configured Level - zero means auto tune +int autoRXLevel = 1500; // calculated level +int ARQConReqRepeats = 5; +BOOL DebugLog = TRUE; +BOOL CommandTrace = TRUE; +int DriveLevel = 100; +char strFECMode[16] = "OFDM.500.55"; +int FECRepeats = 0; +BOOL FECId = FALSE; +int Squelch = 5; + +enum _ARQBandwidth ARQBandwidth = XB500; +BOOL NegotiateBW = TRUE; +char HostPort[80] = ""; +int port = 8515; +int pktport = 0; +BOOL RadioControl = FALSE; +BOOL SlowCPU = FALSE; +BOOL AccumulateStats = TRUE; +BOOL Use600Modes = FALSE; +BOOL EnableOFDM = TRUE; +BOOL UseOFDM = TRUE; +BOOL FSKOnly = FALSE; +BOOL fastStart = TRUE; +BOOL ConsoleLogLevel = LOGDEBUG; +BOOL FileLogLevel = LOGDEBUG; +BOOL EnablePingAck = TRUE; + + +// Stats + +// Public Structure QualityStats + +int SessBytesSent; +int SessBytesReceived; +int int4FSKQuality; +int int4FSKQualityCnts; +int int8FSKQuality; +int int8FSKQualityCnts; +int int16FSKQuality; +int int16FSKQualityCnts; +int intFSKSymbolsDecoded; +int intPSKQuality[2]; +int intPSKQualityCnts[2]; +int intOFDMQuality[8]; +int intOFDMQualityCnts[8]; +int intPSKSymbolsDecoded; +int intOFDMSymbolsDecoded; + +int intQAMQuality; +int intQAMQualityCnts; +int intQAMSymbolsDecoded; +int intGoodQAMSummationDecodes; + + +int intLeaderDetects; +int intLeaderSyncs; +int intAccumLeaderTracking; +float dblFSKTuningSNAvg; +int intGoodFSKFrameTypes; +int intFailedFSKFrameTypes; +int intAccumFSKTracking; +int intFSKSymbolCnt; +int intGoodFSKFrameDataDecodes; +int intFailedFSKFrameDataDecodes; +int intAvgFSKQuality; +int intFrameSyncs; +int intGoodPSKSummationDecodes; +int intGoodFSKSummationDecodes; +int intGoodQAMSummationDecodes; +int intGoodOFDMSummationDecodes; +float dblLeaderSNAvg; +int intAccumPSKLeaderTracking; +float dblAvgPSKRefErr; +int intPSKTrackAttempts; +int intAccumPSKTracking; +int intQAMTrackAttempts; +int intOFDMTrackAttempts; +int intAccumQAMTracking; +int intAccumOFDMTracking; +int intPSKSymbolCnt; +int intQAMSymbolCnt; +int intOFDMSymbolCnt; +int intGoodPSKFrameDataDecodes; +int intFailedPSKFrameDataDecodes; +int intGoodQAMFrameDataDecodes; +int intFailedQAMFrameDataDecodes; +int intAvgPSKQuality; +int intGoodOFDMFrameDataDecodes; +int intFailedOFDMFrameDataDecodes; +int intAvgOFDMQuality; +float dblAvgDecodeDistance; +int intDecodeDistanceCount; +int intShiftUPs; +int intShiftDNs; +unsigned int dttStartSession; +int intLinkTurnovers; +int intEnvelopeCors; +float dblAvgCorMaxToMaxProduct; +int intConReqSN; +int intConReqQuality; +int intTimeouts; + + + +char stcLastPingstrSender[10]; +char stcLastPingstrTarget[10]; +int stcLastPingintRcvdSN; +int stcLastPingintQuality; +time_t stcLastPingdttTimeReceived; + +BOOL blnInitializing = FALSE; + +BOOL blnLastPTT = FALSE; + +BOOL PlayComplete = FALSE; + +BOOL blnBusyStatus = 0; +BOOL newStatus; + +unsigned int tmrSendTimeout; + +int intCalcLeader; // the computed leader to use based on the reported Leader Length +int intRmtLeaderMeasure = 0; + +int dttCodecStarted; + +enum _ReceiveState State; +enum _ARDOPState ProtocolState; + +const char ARDOPStates[8][9] = {"OFFLINE", "DISC", "ISS", "IRS", "IDLE", "IRStoISS", "FECSEND", "FECRCV"}; + +const char ARDOPModes[3][6] = {"Undef", "FEC", "ARQ"}; + +struct SEM Semaphore = {0, 0, 0, 0}; + +int DecodeCompleteTime; + +BOOL blnAbort = FALSE; +int intRepeatCount; +BOOL blnARQDisconnect = FALSE; + +int dttLastPINGSent; + +enum _ProtocolMode ProtocolMode = FEC; + +extern int intTimeouts; +extern BOOL blnEnbARQRpt; +extern BOOL blnDISCRepeating; +extern char strRemoteCallsign[10]; +extern char strLocalCallsign[10]; +extern char strFinalIDCallsign[10]; +extern int dttTimeoutTrip; +extern unsigned int dttLastFECIDSent; +extern BOOL blnPending; +extern unsigned int tmrIRSPendingTimeout; +extern unsigned int tmrFinalID; +extern unsigned int tmrPollOBQueue; +VOID EncodeAndSend4FSKControl(UCHAR bytFrameType, UCHAR bytSessionID, int LeaderLength); +void SendPING(char * strMycall, char * strTargetCall, int intRpt); +void SendCQ(int intRpt); + +int intRepeatCnt; + + +BOOL blnClosing = FALSE; +BOOL blnCodecStarted = FALSE; + +unsigned int dttNextPlay = 0; + + +const UCHAR bytValidFrameTypesALL[]= +{ + DataNAK, + DataNAKLoQ, + ConRejBusy, + ConRejBW, + ConAck, + DISCFRAME, + BREAK, + END, + IDLEFRAME, + ConReq200, + ConReq500, + ConReq2500, + OConReq500, + OConReq2500, + IDFRAME, + PINGACK, + PING, + CQ_de, + D4PSK_200_50_E, + D4PSK_200_50_O, + D4PSK_200_100_E, + D4PSK_200_100_O, + D16QAM_200_100_E, + D16QAM_200_100_O, + D4FSK_500_50_E, + D4FSK_500_50_O, + D4PSK_500_50_E, + D4PSK_500_50_O, + D4PSK_500_100_E, + D4PSK_500_100_O, + D16QAMR_500_100_E, + D16QAMR_500_100_O, + D16QAM_500_100_E, + D16QAM_500_100_O, + DOFDM_200_55_E, + DOFDM_200_55_O, + DOFDM_500_55_E, + DOFDM_500_55_O, + D4FSK_1000_50_E, + D4FSK_1000_50_O, + D4PSKR_2500_50_E, + D4PSKR_2500_50_O, + D4PSK_2500_50_E, + D4PSK_2500_50_O, + D4PSK_2500_100_E, + D4PSK_2500_100_O, + D16QAMR_2500_100_E, + D16QAMR_2500_100_O, + D16QAM_2500_100_E, + D16QAM_2500_100_O, + DOFDM_2500_55_E, + DOFDM_2500_55_O, + + PktFrameHeader, // Variable length frame Header + PktFrameData, // Variable length frame Data (Virtual Frsme Type) + OFDMACK, + DataACK, + DataACKHiQ}; + +const UCHAR bytValidFrameTypesISS[]= // ACKs, NAKs, END, DISC, BREAK +{ + DataNAK, + DataNAKLoQ, + ConRejBusy, + ConRejBW, + ConAck, + DISCFRAME, + END, + IDFRAME, + PktFrameHeader, // Variable length frame Header + PktFrameData, // Variable length frame Data (Virtual Frsme Type) + OFDMACK, + DataACK, + DataACKHiQ}; + +const UCHAR * bytValidFrameTypes; + +int bytValidFrameTypesLengthISS = sizeof(bytValidFrameTypesISS); +int bytValidFrameTypesLengthALL = sizeof(bytValidFrameTypesALL); +int bytValidFrameTypesLength; + + +BOOL blnTimeoutTriggered = FALSE; + +// We can't keep the audio samples for retry, but we can keep the +// encoded data + +unsigned char bytEncodedBytes[4500] =""; // OFDM is big (maybe not 4500) +int EncLen; + + +extern UCHAR bytSessionID; + +int intLastRcvdFrameQuality; + +int intAmp = 26000; // Selected to have some margin in calculations with 16 bit values (< 32767) this must apply to all filters as well. + +const char strAllDataModes[18][16] = + {"4PSK.200.50", "4PSK.200.100", + "16QAM.200.100", "4FSK.500.50", + "4PSK.500.50", "4PSK.500.100", + "OFDM.200.55", "OFDM.500.55", + "16QAMR.500.100", "16QAM.500.100", + "4FSK.1000.50", + "4PSKR.2500.50", "4PSK.2500.50", + "4PSK.2500.100", + "16QAMR.2500.100", "16QAM.2500.100", "OFDM.2500.55"}; + +int strAllDataModesLen = 18; + +// Frame Speed By Type (from Rick's spreadsheet) Bytes per minute + +const short Rate[64] = +{ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00 - 0F + 402,402,826,826,1674,1674,0,0,0,0,402,402,857,857,1674,1674, // 10 - 1F + 1674,1674,3349,3359,0,0,0,0,857,857,2143,2143,4286,4286,8372,8372, // 20 - 2F + 8372,8372,16744,16744,0,0,0,0,0,0,0,0,0,0,0,0, // 30 - 3F +}; + +const short FrameSize[64] = +{ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00 - 0F + 32,32,64,64,120,120,0,0,0,0,32,32,64,64,128,128, // 10 - 1F + 120,120,240,240,360,360,720,720,64,64,160,160,320,320,640,640, // 20 - 2F + 600,600,1200,1200,680,680,1360,1360,0,0,0,0,0,0,0,0, // 30 - 3F +}; + + +const char strFrameType[64][18] = +{ + // Short Control Frames 1 Car, 500Hz,4FSK, 50 baud + + "DataNAK", // 0 + "DataNAKLoQ", + "ConRejBusy", + "ConRejBW", + "ConAck", // 4 + "DISC", + "BREAK", + "END", + "IDLE", // 8 + "ConReq200", + "ConReq500", + "ConReq2500", + "IDFrame", // C + "PingAck", + "Ping", // E + "CQ_de", // F + + // 200 Hz Bandwidth + // 1 Car modes + + "4PSK.200.50.E", // 0x10 + "4PSK.200.50.O", + "4PSK.200.100.E", + "4PSK.200.100.O", + "16QAM.200.100.E", // 0x14 + "16QAM.200.100.O", + "", "",// 0x16 to 0x17 + "OConReq500", "OConReq2500", + // 500 Hz bandwidth Data + // 1 Car 4FSK Data mode 500 Hz, 50 baud tones spaced @ 100 Hz + + "4FSK.500.50.E", // 0x1A + "4FSK.500.50.O", + // 2 Car PSK Data Modes + "4PSK.500.50.E", + "4PSK.500.50.O", + "4PSK.500.100.E", // 0x1E + "4PSK.500.100.O", + // 2 Car 16QAM Data Modes 100 baud + "16QAMR.500.100.E", // 0x20 + "16QAMR.500.100.O", + // 2 Car 16QAM Data Modes 100 baud + "16QAM.500.100.E", // 0x22 + "16QAM.500.100.O", + "OFDM.500.55.E", + "OFDM.500.55.O", + "OFDM.200.55.E", + "OFDM.200.55.O", + + // 1 Khz Bandwidth Data Modes + // 4 Car 4FSK Data mode 1000 Hz, 50 baud tones spaced @ 100 Hz + "4FSK.1000.50.E", // 0x28 + "4FSK.1000.50.O", + + // 2500 dblOffsetHz bandwidth modes + // 10 Car PSK Data Modes 50 baud + + "4PSKR.2500.50.E", // 0x2A + "4PSKR.2500.50.O", + "4PSK.2500.50.E", + "4PSK.2500.50.O", + + // 10 Car PSK Data Modes 100 baud + + "4PSK.2500.100.E", // 0x2E + "4PSK.2500.100.O", + + + // 10 Car 16QAM Data modes 100 baud + "16QAMR.2500.100.E", // 0x30 + "16QAMR.2500.100.O", + "16QAM.2500.100.E", // 0x32 + "16QAM.2500.100.O", + "OFDM.2500.55.E", + "OFDM.2500.55.O", + "", + "", + "", "", // 0x38 to 0x39 + "PktFrameHeader", //3A + "PktFrameData", + "", // 0x3C + "OFDMACK", + "DataACK", // note special coding to have large distance from NAKs + "DataACKHiQ" // note special coding to have large distance from NAKs +}; + +const char shortFrameType[64][12] = +{ + // Short Control Frames 1 Car, 500Hz,4FSK, 50 baud + // Used on OLED display + + "DataNAK", // 0 + "DataNAKLoQ", + "ConRejBusy", + "ConRejBW", + "ConAck", // 4 + "DISC", + "BREAK", + "END", + "IDLE", // 8 + "ConReq200", + "ConReq500", + "ConReq2500", + "IDFrame", // C + "PingAck", + "Ping", // E + "CQ_de", // F + + // 200 Hz Bandwidth + // 1 Car modes + + "4P.200.50", // 0x10 + "4P.200.50", + "4P.200.100", + "4PS.200.100", + "16Q.200.100", // 0x14 + "16Q.200.100", + "", "",// 0x16 to 0x17 + "OConReq500", "OConReq2500", + + // 500 Hz bandwidth Data + // 1 Car 4FSK Data mode 500 Hz, 50 baud tones spaced @ 100 Hz + + "4F.500.50", // 0x1A + "4F.500.50", + // 2 Car PSK Data Modes + "4P.500.50", + "4P.500.50", + "4P.500.100", // 0x1E + "4P.500.100", + // 2 Car 16QAM Data Modes 100 baud + "16QR.500.100", // 0x20 + "16QR.500.100", + // 2 Car 16QAM Data Modes 100 baud + "16Q.500.100", // 0x22 + "16Q.500.100", + "OFDM.500", + "OFDM.500", + "OFDM.200", + "OFDM.200", + + // 1 Khz Bandwidth Data Modes + // 4 Car 4FSK Data mode 1000 Hz, 50 baud tones spaced @ 100 Hz + "4F.1K.50", // 0x28 + "4F.1K.50", + + // 2500 dblOffsetHz bandwidth modes + // 10 Car PSK Data Modes 50 baud + + "4PR.2500.50", // 0x2A + "4PR.2500.50", + "4P.2500.50", + "4P.2500.50", + + // 10 Car PSK Data Modes 100 baud + + "4P.2500.100", // 0x2E + "4P.2500.100", + + + // 10 Car 16QAM Data modes 100 baud + "QR.2500.100", // 0x30 + "QR.2500.100", + "Q.2500.100", // 0x32 + "Q.2500.100", + "OFDM.2500", + "OFDM.2500", + "", + "", + "", "", // 0x38 to 0x39 + "PktHeader", //3A + "PktData", + "", // 0x3C + "OFDMACK", + "DataACK", // note special coding to have large distance from NAKs + "DataACKHiQ" // note special coding to have large distance from NAKs +}; + + + +void GetSemaphore() +{ +} + +void FreeSemaphore() +{ +} + +BOOL CheckValidCallsignSyntax(char * strCallsign) +{ + // Function for checking valid call sign syntax + + char * Dash = strchr(strCallsign, '-'); + int callLen = strlen(strCallsign); + char * ptr = strCallsign; + int SSID; + + if (Dash) + { + callLen = Dash - strCallsign; + + SSID = atoi(Dash + 1); + if (SSID > 15) + return FALSE; + + if (strlen(Dash + 1) > 2) + return FALSE; + + if (!isalnum(*(Dash + 1))) + return FALSE; + } + + if (callLen > 7 || callLen < 3) + return FALSE; + + while (callLen--) + { + if (!isalnum(*(ptr++))) + return FALSE; + } + return TRUE; +} + +// Function to check for proper syntax of a 4, 6 or 8 character GS + +BOOL CheckGSSyntax(char * GS) +{ + int Len = strlen(GS); + + if (!(Len == 4 || Len == 6 || Len == 8)) + return FALSE; + + if (!isalpha(GS[0]) || !isalpha(GS[1])) + return FALSE; + + if (!isdigit(GS[2]) || !isdigit(GS[3])) + return FALSE; + + if (Len == 4) + return TRUE; + + if (!isalpha(GS[4]) || !isalpha(GS[5])) + return FALSE; + + if (Len == 6) + return TRUE; + + if (!isdigit(GS[6]) || !isdigit(GS[7])) + return FALSE; + + return TRUE; +} + +// Function polled by Main polling loop to see if time to play next wave stream + +#ifdef WIN32 + +extern LARGE_INTEGER Frequency; +extern LARGE_INTEGER StartTicks; +extern LARGE_INTEGER NewTicks; + +#endif + +extern int NErrors; + +void testRS() +{ + // feed random data into RS to check robustness + + BOOL blnRSOK, FrameOK; + char bytRawData[256]; + int DataLen = 128; + int intRSLen = 64; + int i; + + for (i = 0; i < DataLen; i++) + { + bytRawData[i] = rand() % 256; + } + + FrameOK = RSDecode(bytRawData, DataLen, intRSLen, &blnRSOK); +} + + +void SendCWID(char * Callsign, BOOL x) +{ +} + +// Subroutine to generate 1 symbol of leader + +// returns pointer to Frame Type Name + +const char * Name(UCHAR bytID) +{ + return strFrameType[bytID]; +} + +// returns pointer to Frame Type Name + +const char * shortName(UCHAR bytID) +{ + return shortFrameType[bytID]; +} +// Function to look up frame info from bytFrameType + +BOOL FrameInfo(UCHAR bytFrameType, int * blnOdd, int * intNumCar, char * strMod, + int * intBaud, int * intDataLen, int * intRSLen, UCHAR * bytQualThres, char * strType) +{ + // Used to "lookup" all parameters by frame Type. + // returns TRUE if all fields updated otherwise FALSE (improper bytFrameType) + + // 1 Carrier 4FSK control frames + + switch(bytFrameType) + { + case DataNAK: + case DataNAKLoQ: + case DataACK: + case DataACKHiQ: + case ConAck: + case ConRejBW: + case ConRejBusy: + case IDLEFRAME: + case DISCFRAME: + case BREAK: + case END: + + *blnOdd = 0; + *intNumCar = 1; + *intDataLen = 0; + *intRSLen = 0; + strcpy(strMod, "4FSK"); + *intBaud = 50; + break; + + case IDFRAME: + case PING: + case CQ_de: + + *blnOdd = 0; + *intNumCar = 1; + *intDataLen = 12; + *intRSLen = 4; // changed 0.8.0 + strcpy(strMod, "4FSK"); + *intBaud = 50; + break; + + case PINGACK: + + *blnOdd = 0; + *intNumCar = 1; + *intDataLen = 3; + *intRSLen = 0; + strcpy(strMod, "4FSK"); + *intBaud = 50; + break; + + + case ConReq200: + case ConReq500: + case ConReq2500: + case OConReq500: + case OConReq2500: + + *blnOdd = 0; + *intNumCar = 1; + *intDataLen = 6; + *intRSLen = 2; + strcpy(strMod, "4FSK"); + *intBaud = 50; + break; + + case OFDMACK: + + *blnOdd = 0; + *intNumCar = 1; + *intDataLen = 6; + *intRSLen = 4; + strcpy(strMod, "4FSK"); + *intBaud = 100; + break; + + case PktFrameHeader: + + // Special Variable Length frame + + // This defines the header, 4PSK.500.100. Length is 6 bytes + // Once we have that we receive the rest of the packet in the + // mode defined in the header. + // Header is 4 bits Type 12 Bits Len 2 bytes CRC 2 bytes RS + + *blnOdd = 0; + *intNumCar = 1; + *intDataLen = 2; + *intRSLen = 2; + strcpy(strMod, "4FSK"); + *intBaud = 100; + break; + + case PktFrameData: + + // Special Variable Length frame + + // This isn't ever transmitted but is used to define the + // current setting for the data frame. Mode and Length + // are variable + + + *blnOdd = 1; + *intNumCar = pktCarriers[pktMode]; + *intDataLen = pktDataLen; + *intRSLen = pktRSLen; + strcpy(strMod, &pktMod[pktMode][0]); + strlop(strMod, '/'); + *intBaud = 100; + break; + + default: + + // Others are Even/Odd Pairs + + switch(bytFrameType & 0xFE) + { + case D4PSK_200_50_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 1; + *intDataLen = 32; + *intRSLen = 8; + strcpy(strMod, "4PSK"); + *intBaud = 50; + break; + + case D4PSK_200_100_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 1; + *intDataLen = 64; + *intRSLen = 16; + strcpy(strMod, "4PSK"); + *intBaud = 100; + break; + + + case D16QAM_200_100_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 1; + *intDataLen = 120; + *intRSLen = 40; + strcpy(strMod, "16QAM"); + *intBaud = 100; + break; + + + case D4FSK_500_50_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 1; + *intDataLen = 32; + *intRSLen = 8; + strcpy(strMod, "4FSK"); + *intBaud = 50; + *bytQualThres = 30; + break; + + case D4PSK_500_50_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 2; + *intDataLen = 32; + *intRSLen = 8; + strcpy(strMod, "4PSK"); + *intBaud = 50; + break; + + case D4PSK_500_100_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 2; + *intDataLen = 64; + *intRSLen = 16; + strcpy(strMod, "4PSK"); + *intBaud = 100; + break; + + case D16QAMR_500_100_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 2; + *intDataLen = 120; + *intRSLen = 40; + strcpy(strMod, "16QAMR"); + *intBaud = 100; + break; + + case D16QAM_500_100_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 2; + *intDataLen = 120; + *intRSLen = 40; + strcpy(strMod, "16QAM"); + *intBaud = 100; + break; + + case DOFDM_200_55_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 3; + *intDataLen = 40; + *intRSLen = 10; + strcpy(strMod, "OFDM"); + *intBaud = 55; + break; + + case DOFDM_500_55_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 9; + *intDataLen = 40; + *intRSLen = 10; + strcpy(strMod, "OFDM"); + *intBaud = 55; + break; + + case D4FSK_1000_50_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 2; + *intDataLen = 32; + *intRSLen = 8; + strcpy(strMod, "4FSK"); + *intBaud = 50; + break; + + case D4PSKR_2500_50_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 10; + *intDataLen = 32; + *intRSLen = 8; + strcpy(strMod, "4PSKR"); + *intBaud = 50; + break; + + case D4PSK_2500_50_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 10; + *intDataLen = 32; + *intRSLen = 8; + strcpy(strMod, "4PSK"); + *intBaud = 50; + break; + + case D4PSK_2500_100_E: + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 10; + *intDataLen = 64; + *intRSLen = 16; + strcpy(strMod, "4PSK"); + *intBaud = 100; + break; + + case D16QAMR_2500_100_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 10; + *intDataLen = 120; + *intRSLen = 40; + strcpy(strMod, "16QAMR"); + *intBaud = 100; + break; + + case D16QAM_2500_100_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 10; + *intDataLen = 120; + *intRSLen = 40; + strcpy(strMod, "16QAM"); + *intBaud = 100; + break; + + case DOFDM_2500_55_E: + + *blnOdd = (1 & bytFrameType) != 0; + *intNumCar = 43; + *intDataLen = 40; + *intRSLen = 10; + strcpy(strMod, "OFDM"); + *intBaud = 55; + break; + + default: + Debugprintf("No data for frame type= %X", bytFrameType); + return FALSE; + } + } + + strcpy(strType,strFrameType[bytFrameType]); + + return TRUE; +} + +int xNPAR = -1; // Number of Parity Bytes - used in RS Code + +int MaxErrors = 0; + +int xRSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen) +{ + // This just returns the Parity Bytes. I don't see the point + // in copying the message about + + unsigned char Padded[256]; // The padded Data + + int Length = DataLen + RSLen; // Final Length of packet + int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes + + // subroutine to do the RS encode. For full length and shortend RS codes up to 8 bit symbols (mm = 8) + + if (NPAR != RSLen) // Changed RS Len, so recalc constants; + { + NPAR = RSLen; + MaxErrors = NPAR / 2; + initialize_ecc(); + } + + // Copy the supplied data to end of data array. + + memset(Padded, 0, PadLength); + memcpy(&Padded[PadLength], bytToRS, DataLen); + + encode_data(Padded, 255-RSLen, RSBytes); + + return RSLen; +} + +// Main RS decode function + +extern int index_of[]; +extern int recd[]; +int Corrected[256]; +extern int tt; // number of errors that can be corrected +extern int kk; // Info Symbols + +BOOL blnErrorsCorrected; + +#define NEWRS + +BOOL xRSDecode(UCHAR * bytRcv, int Length, int CheckLen, BOOL * blnRSOK) +{ +#ifdef NEWRS + + // Using a modified version of Henry Minsky's code + + //Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009 + + // Rick's Implementation processes the byte array in reverse. and also + // has the check bytes in the opposite order. I've modified the encoder + // to allow for this, but so far haven't found a way to mske the decoder + // work, so I have to reverse the data and checksum to decode G8BPQ Nov 2015 + + // returns TRUE if was ok or correction succeeded, FALSE if correction impossible + + UCHAR intTemp[256]; // WOrk Area to pass to Decoder + int i; + UCHAR * ptr2 = intTemp; + UCHAR * ptr1 = &bytRcv[Length - CheckLen -1]; // Last Byte of Data + + int DataLen = Length - CheckLen; + int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes + + *blnRSOK = FALSE; + + if (Length > 255 || Length < (1 + CheckLen)) //Too long or too short + return FALSE; + + if (NPAR != CheckLen) // Changed RS Len, so recalc constants; + { + NPAR = CheckLen; + MaxErrors = NPAR /2; + + initialize_ecc(); + } + + + // We reverse the data while zero padding it to speed things up + + // We Need (Data Reversed) (Zero Padding) (Checkbytes Reversed) + + // Reverse Data + + for (i = 0; i < DataLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + // Clear padding + + memset(ptr2, 0, PadLength); + + ptr2+= PadLength; + + // Error Bits + + ptr1 = &bytRcv[Length - 1]; // End of check bytes + + for (i = 0; i < CheckLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + decode_data(intTemp, 255); + + // check if syndrome is all zeros + + if (check_syndrome() == 0) + { + // RS ok, so no need to correct + + *blnRSOK = TRUE; + return TRUE; // No Need to Correct + } + + if (correct_errors_erasures (intTemp, 255, 0, 0) == 0) // Dont support erasures at the momnet + + // Uncorrectable + + return FALSE; + + // Data has been corrected, so need to reverse again + + ptr1 = &intTemp[DataLen - 1]; + ptr2 = bytRcv; // Last Byte of Data + + for (i = 0; i < DataLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + // ?? Do we need to return the check bytes ?? + + // Yes, so we can redo RS Check on supposedly connected frame + + ptr1 = &intTemp[254]; // End of Check Bytes + + for (i = 0; i < CheckLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + return TRUE; +} + +#else + + // Old (Rick's) code + + // Sets blnRSOK if OK without correction + + // Returns TRUE if OK oe Corrected + // False if Can't correct + + + UCHAR intTemp[256]; // Work Area to pass to Decoder + int i; + int intStartIndex; + UCHAR * ptr2 = intTemp; + UCHAR * ptr1 = bytRcv; + BOOL RSWasOK; + + int DataLen = Length - CheckLen; + int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes + + *blnRSOK = FALSE; + + if (Length > 255 || Length < (1 + CheckLen)) //Too long or too short + return FALSE; + + + if (NPAR != CheckLen) // Changed RS Len, so recalc constants; + { + NPAR = CheckLen; + tt = sqrt(NPAR); + kk = 255-CheckLen; + generate_gf(); + gen_poly(); + } + + intStartIndex = 255 - Length; // set the start point for shortened RS codes + + // We always work on a 255 byte buffer, prepending zeros if neccessary + + // Clear padding + + memset(ptr2, 0, PadLength); + ptr2 += PadLength; + + memcpy(ptr2, ptr1, Length); + + // convert to indexed form + + for(i = 0; i < 256; i++) + { +// intIsave = i; +// intIndexSave = index_of[intTemp[i]]; + recd[i] = index_of[intTemp[i]]; + } + +// printtick("entering decode_rs"); + + blnErrorsCorrected = FALSE; + + RSWasOK = decode_rs(); + +// printtick("decode_rs Done"); + + *blnRSOK = RSWasOK; + + if (RSWasOK) + return TRUE; + + if(blnErrorsCorrected) + { + for (i = 0; i < DataLen; i++) + { + bytRcv[i] = recd[i + intStartIndex]; + } + return TRUE; + } + + return FALSE; +} +#endif + + + + +// Function to encode ConnectRequest frame + +// ' Function to encode an ACK control frame (2 bytes total) ...with 5 bit Quality code + + +void SendID(BOOL blnEnableCWID) +{ + +} + + +void ASCIIto6Bit(char * Padded, UCHAR * Compressed) +{ + // Input must be 8 bytes which will convert to 6 bytes of packed 6 bit characters and + // inputs must be the ASCII character set values from 32 to 95.... + + unsigned long long intSum = 0; + + int i; + + for (i=0; i<4; i++) + { + intSum = (64 * intSum) + Padded[i] - 32; + } + + Compressed[0] = (UCHAR)(intSum >> 16) & 255; + Compressed[1] = (UCHAR)(intSum >> 8) & 255; + Compressed[2] = (UCHAR)intSum & 255; + + intSum = 0; + + for (i=4; i<8; i++) + { + intSum = (64 * intSum) + Padded[i] - 32; + } + + Compressed[3] = (UCHAR)(intSum >> 16) & 255; + Compressed[4] = (UCHAR)(intSum >> 8) & 255; + Compressed[5] = (UCHAR)intSum & 255; +} + +void Bit6ToASCII(UCHAR * Padded, UCHAR * UnCompressed) +{ + // uncompress 6 to 8 + + // Input must be 6 bytes which represent packed 6 bit characters that well + // result will be 8 ASCII character set values from 32 to 95... + + unsigned long long intSum = 0; + + int i; + + for (i=0; i<3; i++) + { + intSum = (intSum << 8) + Padded[i]; + } + + UnCompressed[0] = (UCHAR)((intSum >> 18) & 63) + 32; + UnCompressed[1] = (UCHAR)((intSum >> 12) & 63) + 32; + UnCompressed[2] = (UCHAR)((intSum >> 6) & 63) + 32; + UnCompressed[3] = (UCHAR)(intSum & 63) + 32; + + intSum = 0; + + for (i=3; i<6; i++) + { + intSum = (intSum << 8) + Padded[i] ; + } + + UnCompressed[4] = (UCHAR)((intSum >> 18) & 63) + 32; + UnCompressed[5] = (UCHAR)((intSum >> 12) & 63) + 32; + UnCompressed[6] = (UCHAR)((intSum >> 6) & 63) + 32; + UnCompressed[7] = (UCHAR)(intSum & 63) + 32; +} + + +// Function to compress callsign (up to 7 characters + optional "-"SSID (-0 to -15 or -A to -Z) + +void CompressCallsign(char * inCallsign, UCHAR * Compressed) +{ + char Callsign[10] = ""; + char Padded[16]; + int SSID; + char * Dash; + + memcpy(Callsign, inCallsign, 10); + Dash = strchr(Callsign, '-'); + + if (Dash == 0) // if No SSID + { + strcpy(Padded, Callsign); + strcat(Padded, " "); + Padded[7] = '0'; // "0" indicates no SSID + } + else + { + *(Dash++) = 0; + SSID = atoi(Dash); + + strcpy(Padded, Callsign); + strcat(Padded, " "); + + if (SSID >= 10) // ' handles special case of -10 to -15 : ; < = > ? ' + Padded[7] = ':' + SSID - 10; + else + Padded[7] = *(Dash); + } + + ASCIIto6Bit(Padded, Compressed); //compress to 8 6 bit characters 6 bytes total +} + +// Function to compress Gridsquare (up to 8 characters) + +void CompressGridSquare(char * Square, UCHAR * Compressed) +{ + char Padded[17]; + + if (strlen(Square) > 8) + return; + + strcpy(Padded, Square); + strcat(Padded, " "); + + ASCIIto6Bit(Padded, Compressed); //compress to 8 6 bit characters 6 bytes total +} + +// Function to decompress 6 byte call sign to 7 characters plus optional -SSID of -0 to -15 or -A to -Z + +void DeCompressCallsign(char * bytCallsign, char * returned) +{ + char bytTest[10] = ""; + char SSID[8] = ""; + + Bit6ToASCII(bytCallsign, bytTest); + + memcpy(returned, bytTest, 7); + returned[7] = 0; + strlop(returned, ' '); // remove trailing space + + if (bytTest[7] == '0') // Value of "0" so No SSID + returned[6] = 0; + else if (bytTest[7] >= 58 && bytTest[7] <= 63) //' handles special case for -10 to -15 + sprintf(SSID, "-%d", bytTest[7] - 48); + else + sprintf(SSID, "-%c", bytTest[7]); + + strcat(returned, SSID); +} + + +// Function to decompress 6 byte Grid square to 4, 6 or 8 characters + +void DeCompressGridSquare(char * bytGS, char * returned) +{ + char bytTest[10] = ""; + Bit6ToASCII(bytGS, bytTest); + + strlop(bytTest, ' '); + strcpy(returned, bytTest); +} + +// A function to compute the parity symbol used in the frame type encoding + +UCHAR ComputeTypeParity(UCHAR bytFrameType) +{ + UCHAR bytMask = 0x30; // only using 6 bits + UCHAR bytParitySum = 3; + UCHAR bytSym = 0; + int k; + + for (k = 0; k < 3; k++) + { + bytSym = (bytMask & bytFrameType) >> (2 * (2 - k)); + bytParitySum = bytParitySum ^ bytSym; + bytMask = bytMask >> 2; + } + + return bytParitySum & 0x3; +} + +// Function to look up the byte value from the frame string name + +UCHAR FrameCode(char * strFrameName) +{ + int i; + + for (i = 0; i < 64; i++) + { + if (strcmp(strFrameType[i], strFrameName) == 0) + { + return i; + } + } + return 0; +} + +unsigned int GenCRC16(unsigned char * Data, unsigned short length) +{ + // For CRC-16-CCITT = x^16 + x^12 +x^5 + 1 intPoly = 1021 Init FFFF + // intSeed is the seed value for the shift register and must be in the range 0-0xFFFF + + int intRegister = 0xffff; //intSeed + int i,j; + int Bit; + int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1 + + for (j = 0; j < length; j++) + { + int Mask = 0x80; // Top bit first + + for (i = 0; i < 8; i++) // for each bit processing MS bit first + { + Bit = Data[j] & Mask; + Mask >>= 1; + + if (intRegister & 0x8000) // Then ' the MSB of the register is set + { + // Shift left, place data bit as LSB, then divide + // Register := shiftRegister left shift 1 + // Register := shiftRegister xor polynomial + + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + + intRegister = intRegister ^ intPoly; + } + else + { + // the MSB is not set + // Register is not divisible by polynomial yet. + // Just shift left and bring current data bit onto LSB of shiftRegister + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + } + } + } + + return intRegister; +} + +BOOL checkcrc16(unsigned char * Data, unsigned short length) +{ + int intRegister = 0xffff; //intSeed + int i,j; + int Bit; + int intPoly = 0x8810; // This implements the CRC polynomial x^16 + x^12 +x^5 + 1 + + for (j = 0; j < (length - 2); j++) // ' 2 bytes short of data length + { + int Mask = 0x80; // Top bit first + + for (i = 0; i < 8; i++) // for each bit processing MS bit first + { + Bit = Data[j] & Mask; + Mask >>= 1; + + if (intRegister & 0x8000) // Then ' the MSB of the register is set + { + // Shift left, place data bit as LSB, then divide + // Register := shiftRegister left shift 1 + // Register := shiftRegister xor polynomial + + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + + intRegister = intRegister ^ intPoly; + } + else + { + // the MSB is not set + // Register is not divisible by polynomial yet. + // Just shift left and bring current data bit onto LSB of shiftRegister + if (Bit) + intRegister = 0xFFFF & (1 + (intRegister << 1)); + else + intRegister = 0xFFFF & (intRegister << 1); + } + } + } + + if (Data[length - 2] == intRegister >> 8) + if (Data[length - 1] == (intRegister & 0xFF)) + return TRUE; + + return FALSE; +} + + +// Subroutine to compute a 16 bit CRC value and append it to the Data... With LS byte XORed by bytFrameType + +void GenCRC16FrameType(char * Data, int Length, UCHAR bytFrameType) +{ + unsigned int CRC = GenCRC16(Data, Length); + + // Put the two CRC bytes after the stop index + + Data[Length++] = (CRC >> 8); // MS 8 bits of Register + Data[Length] = (CRC & 0xFF) ^ bytFrameType; // LS 8 bits of Register +} + +// Function to compute a 16 bit CRC value and check it against the last 2 bytes of Data (the CRC) XORing LS byte with bytFrameType.. + +unsigned short int compute_crc(unsigned char *buf,int len); + +BOOL CheckCRC16FrameType(unsigned char * Data, int Length, UCHAR bytFrameType) +{ + // returns TRUE if CRC matches, else FALSE + // For CRC-16-CCITT = x^16 + x^12 +x^5 + 1 intPoly = 1021 Init FFFF + // intSeed is the seed value for the shift register and must be in the range 0-0xFFFF + + unsigned int CRC = GenCRC16(Data, Length); + unsigned short CRC2 = compute_crc(Data, Length); + CRC2 ^= 0xffff; + + // Compare the register with the last two bytes of Data (the CRC) + + if ((CRC >> 8) == Data[Length]) + if (((CRC & 0xFF) ^ bytFrameType) == Data[Length + 1]) + return TRUE; + + return FALSE; +} + + +void SaveQueueOnBreak() +{ + // Save data we are about to remove from TX buffer +} + + +extern UCHAR bytEchoData[]; // has to be at least max packet size (?1280) + +extern int bytesEchoed; + +extern UCHAR DelayedEcho; + + +// Timer Rotines + +void CheckTimers() +{ + +} + +// Main polling Function returns True or FALSE if closing + +int dttLastBusy; +int dttLastClear; +int dttStartRTMeasure; +extern int intLastStart; +extern int intLastStop; +float dblAvgBaselineSlow; +float dblAvgBaselineFast; +float dblAvgPk2BaselineRatio; + +// Functino to extract bandwidth from ARQBandwidth + + +// Function to implement a busy detector based on 1024 point FFT + /* +BOOL BusyDetect(float * dblMag, int intStart, int intStop) +{ + // this only called while searching for leader ...once leader detected, no longer called. + // First look at simple peak over the frequency band of interest. + // Status: May 28, 2014. Some initial encouraging results. But more work needed. + // 1) Use of Start and Stop ranges good and appear to work well ...may need some tweaking +/_ a few bins. + // 2) Using a Fast attack and slow decay for the dblAvgPk2BaselineRation number e.g. + // dblAvgPk2BaselineRatio = Max(dblPeakPwrAtFreq / dblAvgBaselineX, 0.9 * dblAvgPk2BaselineRatio) + // Seems to work well for the narrow detector. Tested on real CW, PSK, RTTY. + // Still needs work on the wide band detector. (P3, P4 etc) May be issues with AGC speed. (my initial on-air tests using AGC Fast). + // Ideally can find a set of parameters that won't require user "tweaking" (Busy Squelch) but that is an alternative if needed. + // use of technique of re initializing some parameters on a change in detection bandwidth looks good and appears to work well with + // scanning. Could be expanded with properties to "read back" these parameters so they could be saved and re initialize upon each new frequency. + + static int intBusyCountPkToBaseline = 0; + static int intBusyCountFastToSlow = 0; + static int intBusyCountSlowToFast = 0; + static BOOL blnLastBusy = FALSE; + + float dblAvgBaseline = 0; + float dblPwrAtPeakFreq = 0; + float dblAvgBaselineX; + float dblAlphaBaselineSlow = 0.1f; // This factor affects the filtering of baseline slow. smaller = slower filtering + float dblAlphaBaselineFast = 0.5f; // This factor affects the filtering of baseline fast. smaller = slower filtering + int intPkIndx = 0; + float dblFSRatioNum, dblSFRatioNum; + BOOL blnFS, blnSF, blnPkBaseline; + int i; + + // This holds off any processing of data until 100 ms after PTT release to allow receiver recovery. + + if (Now - dttStartRTMeasure < 100) + return blnLastBusy; + + for (i = intStart; i <= intStop; i++) // cover a range that matches the bandwidth expanded (+/-) by the tuning range + { + if (dblMag[i] > dblPwrAtPeakFreq) + { + dblPwrAtPeakFreq = dblMag[i]; + intPkIndx = i; + } + dblAvgBaseline += dblMag[i]; + } + + if (intPkIndx == 0) + return 0; + + // add in the 2 bins above and below the peak (about 59 Hz total bandwidth) + // This needs refinement for FSK mods like RTTY which have near equal peaks making the Peak and baseline on strong signals near equal + // Computer the power within a 59 Hz spectrum around the peak + + dblPwrAtPeakFreq += (dblMag[intPkIndx - 2] + dblMag[intPkIndx - 1]) + (dblMag[intPkIndx + 2] + dblMag[intPkIndx + 1]); + dblAvgBaselineX = (dblAvgBaseline - dblPwrAtPeakFreq) / (intStop - intStart - 5); // the avg Pwr per bin ignoring the 59 Hz area centered on the peak + dblPwrAtPeakFreq = dblPwrAtPeakFreq / 5; //the the average Power (per bin) in the region of the peak (peak +/- 2bins...about 59 Hz) + + if (intStart == intLastStart && intStop == intLastStop) + { + dblAvgPk2BaselineRatio = dblPwrAtPeakFreq / dblAvgBaselineX; + dblAvgBaselineSlow = (1 - dblAlphaBaselineSlow) * dblAvgBaselineSlow + dblAlphaBaselineSlow * dblAvgBaseline; + dblAvgBaselineFast = (1 - dblAlphaBaselineFast) * dblAvgBaselineFast + dblAlphaBaselineFast * dblAvgBaseline; + } + else + { + // This initializes the values after a bandwidth change + + dblAvgPk2BaselineRatio = dblPwrAtPeakFreq / dblAvgBaselineX; + dblAvgBaselineSlow = dblAvgBaseline; + dblAvgBaselineFast = dblAvgBaseline; + intLastStart = intStart; + intLastStop = intStop; + } + + if (Now - dttLastBusy < 1000 || ProtocolState != DISC) // Why?? + return blnLastBusy; + + if (dblAvgPk2BaselineRatio > 1.118f * powf(Squelch, 1.5f)) // These values appear to work OK but may need optimization April 21, 2015 + { + blnPkBaseline = TRUE; + dblAvgBaselineSlow = dblAvgBaseline; + dblAvgBaselineFast = dblAvgBaseline; + } + else + { + // 'If intBusyCountPkToBaseline > 0 Then + + blnPkBaseline = FALSE; + } + + // This detects wide band "pulsy" modes like Pactor 3, MFSK etc + + switch(Squelch) // this provides a modest adjustment to the ratio limit based on practical squelch values + { + //These values yield less sensiivity for F:S which minimizes trigger by static crashes but my need further optimization May 2, 2015 + + case 0: + case 1: + case 2: + dblFSRatioNum = 1.9f; + dblSFRatioNum = 1.2f; + break; + + case 3: + dblFSRatioNum = 2.1f; + dblSFRatioNum = 1.4f; + break; + case 4: + dblFSRatioNum = 2.3f; + dblSFRatioNum = 1.6f; + break; + case 5: + dblFSRatioNum = 2.5f; + dblSFRatioNum = 1.8f; + break; + case 6: + dblFSRatioNum = 2.7f; + dblSFRatioNum = 2.0f; + case 7: + dblFSRatioNum = 2.9f; + dblSFRatioNum = 2.2f; + case 8: + case 9: + case 10: + dblFSRatioNum = 3.1f; + dblSFRatioNum = 2.4f; + } + + // This covers going from Modulation to no modulation e.g. End of Pactor frame + + if ((dblAvgBaselineSlow / dblAvgBaselineFast) > dblSFRatioNum) + + //Debug.WriteLine(" Slow to Fast") + blnSF = TRUE; + else + blnSF = FALSE; + + // This covers going from no modulation to Modulation e.g. Start of Pactor Frame or Static crash + + if ((dblAvgBaselineFast / dblAvgBaselineSlow) > dblFSRatioNum) + //Debug.WriteLine(" Fast to Slow") + blnFS = TRUE; + else + blnFS = FALSE; + + if (blnFS || blnSF || blnPkBaseline) + { + //'If blnFS Then Debug.WriteLine("Busy: Fast to Slow") + //'If blnSF Then Debug.WriteLine("Busy: Slow to Fast") + //'If blnPkBaseline Then Debug.WriteLine("Busy: Pk to Baseline") + blnLastBusy = TRUE; + dttLastBusy = Now; + return TRUE; + } + else + { + blnLastBusy = FALSE; + dttLastClear = Now; + return FALSE; + } + return blnLastBusy; +} +*/ +// Subroutine to update the Busy detector when not displaying Spectrum or Waterfall (graphics disabled) + + +extern UCHAR CurrentLevel; + +#ifdef PLOTSPECTRUM +float dblMagSpectrum[206]; +float dblMaxScale = 0.0f; +extern UCHAR Pixels[4096]; +extern UCHAR * pixelPointer; +#endif + + +/* Old Version pre gui + +void UpdateBusyDetector(short * bytNewSamples) +{ + float dblReF[1024]; + float dblImF[1024]; + + float dblMag[206]; + + static BOOL blnLastBusyStatus; + + float dblMagAvg = 0; + int intTuneLineLow, intTuneLineHi, intDelta; + int i; + + if (ProtocolState != DISC) // ' Only process busy when in DISC state + return; + +// if (State != SearchingForLeader) +// return; // only when looking for leader + + if (Now - LastBusyCheck < 100) + return; + + LastBusyCheck = Now; + + FourierTransform(1024, bytNewSamples, &dblReF[0], &dblImF[0], FALSE); + + for (i = 0; i < 206; i++) + { + // starting at ~300 Hz to ~2700 Hz Which puts the center of the signal in the center of the window (~1500Hz) + + dblMag[i] = powf(dblReF[i + 25], 2) + powf(dblImF[i + 25], 2); // first pass + dblMagAvg += dblMag[i]; + } + +// LookforPacket(dblMag, dblMagAvg, 206, &dblReF[25], &dblImF[25]); +// packet_process_samples(bytNewSamples, 1200); + + intDelta = (ExtractARQBandwidth() / 2 + TuningRange) / 11.719f; + + intTuneLineLow = max((103 - intDelta), 3); + intTuneLineHi = min((103 + intDelta), 203); + +// if (ProtocolState == DISC) // ' Only process busy when in DISC state + { + blnBusyStatus = BusyDetect3(dblMag, intTuneLineLow, intTuneLineHi); + + if (blnBusyStatus && !blnLastBusyStatus) + { + QueueCommandToHost("BUSY TRUE"); + newStatus = TRUE; // report to PTC + } + // stcStatus.Text = "True" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY TRUE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + else if (blnLastBusyStatus && !blnBusyStatus) + { + QueueCommandToHost("BUSY FALSE"); + newStatus = TRUE; // report to PTC + } + // stcStatus.Text = "False" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY FALSE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + blnLastBusyStatus = blnBusyStatus; + } +} + +*/ + +// Function to encode data for all PSK frame types + +int EncodePSKData(UCHAR bytFrameType, UCHAR * bytDataToSend, int Length, unsigned char * bytEncodedBytes) +{ + // Objective is to use this to use this to send all PSK data frames + // 'Output is a byte array which includes: + // 1) A 2 byte Header which include the Frame ID. This will be sent using 4FSK at 50 baud. It will include the Frame ID and ID Xored by the Session bytID. + // 2) n sections one for each carrier that will inlcude all data (with FEC appended) for the entire frame. Each block will be identical in length. + // Ininitial implementation: + // intNum Car may be 1, 2, 4 or 8 + // intBaud may be 100, 167 + // intPSKMode may be 4 (4PSK) or 8 (8PSK) + // bytDataToSend must be equal to or less than max data supported by the frame or a exception will be logged and an empty array returned + + // First determine if bytDataToSend is compatible with the requested modulation mode. + + int intNumCar, intBaud, intDataLen, intRSLen, bytDataToSendLengthPtr, intEncodedDataPtr; + + int intCarDataCnt, intStartIndex; + BOOL blnOdd; + char strType[18]; + char strMod[16]; + BOOL blnFrameTypeOK; + UCHAR bytQualThresh; + int i; + UCHAR * bytToRS = &bytEncodedBytes[2]; + + blnFrameTypeOK = FrameInfo(bytFrameType, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytQualThresh, strType); + + if (intDataLen == 0 || Length == 0 || !blnFrameTypeOK) + { + //Logs.Exception("[EncodeFSKFrameType] Failure to update parameters for frame type H" & Format(bytFrameType, "X") & " DataToSend Len=" & bytDataToSend.Length.ToString) + return 0; + } + + // Generate the 2 bytes for the frame type data: + + bytEncodedBytes[0] = bytFrameType; + bytEncodedBytes[1] = bytFrameType ^ bytSessionID; + + bytDataToSendLengthPtr = 0; + intEncodedDataPtr = 2; + + // Now compute the RS frame for each carrier in sequence and move it to bytEncodedBytes + + if (strchr(strMod, 'R')) + { + // Robust Frame. We send data twice, so only encode half the carriers + + intNumCar /= 2; + } + + for (i = 0; i < intNumCar; i++) // across all carriers + { + intCarDataCnt = Length - bytDataToSendLengthPtr; + + if (intCarDataCnt > intDataLen) // why not > ?? + { + // Won't all fit + + bytToRS[0] = intDataLen; + intStartIndex = intEncodedDataPtr; + memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intDataLen); + bytDataToSendLengthPtr += intDataLen; + } + else + { + // Last bit + + memset(&bytToRS[0], 0, intDataLen); + + bytToRS[0] = intCarDataCnt; // Could be 0 if insuffient data for # of carriers + + if (intCarDataCnt > 0) + { + memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intCarDataCnt); + bytDataToSendLengthPtr += intCarDataCnt; + } + } + + GenCRC16FrameType(bytToRS, intDataLen + 1, bytFrameType); // calculate the CRC on the byte count + data bytes + + RSEncode(bytToRS, bytToRS + intDataLen + 3, intDataLen + 3, intRSLen); // Generate the RS encoding ...now 14 bytes total + + // Need: (2 bytes for Frame Type) +( Data + RS + 1 byte byteCount + 2 Byte CRC per carrier) + + intEncodedDataPtr += intDataLen + 3 + intRSLen; + + bytToRS += intDataLen + 3 + intRSLen; + } + + if (strchr(strMod, 'R')) + { + // Robust Frame. We send data twice, so copy data + + memcpy(&bytEncodedBytes[intEncodedDataPtr], &bytEncodedBytes[2], intEncodedDataPtr - 2); + intEncodedDataPtr += intEncodedDataPtr - 2; + } + + + return intEncodedDataPtr; +} + + + +// Function to encode data for all FSK frame types + +int EncodeFSKData(UCHAR bytFrameType, UCHAR * bytDataToSend, int Length, unsigned char * bytEncodedBytes) +{ + // Objective is to use this to use this to send all 4FSK data frames + // 'Output is a byte array which includes: + // 1) A 2 byte Header which include the Frame ID. This will be sent using 4FSK at 50 baud. It will include the Frame ID and ID Xored by the Session bytID. + // 2) n sections one for each carrier that will inlcude all data (with FEC appended) for the entire frame. Each block will be identical in length. + // Ininitial implementation: + // intNum Car may be 1, 2, 4 or 8 + // intBaud may be 50, 100 + // strMod is 4FSK) + // bytDataToSend must be equal to or less than max data supported by the frame or a exception will be logged and an empty array returned + + // First determine if bytDataToSend is compatible with the requested modulation mode. + + int intNumCar, intBaud, intDataLen, intRSLen, bytDataToSendLengthPtr, intEncodedDataPtr; + + int intCarDataCnt, intStartIndex; + BOOL blnOdd; + char strType[18]; + char strMod[16]; + BOOL blnFrameTypeOK; + UCHAR bytQualThresh; + int i; + UCHAR * bytToRS = &bytEncodedBytes[2]; + + blnFrameTypeOK = FrameInfo(bytFrameType, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytQualThresh, strType); + + if (intDataLen == 0 || Length == 0 || !blnFrameTypeOK) + { + //Logs.Exception("[EncodeFSKFrameType] Failure to update parameters for frame type H" & Format(bytFrameType, "X") & " DataToSend Len=" & bytDataToSend.Length.ToString) + return 0; + } + + // Generate the 2 bytes for the frame type data: + + bytEncodedBytes[0] = bytFrameType; + bytEncodedBytes[1] = bytFrameType ^ bytSessionID; + + // Dim bytToRS(intDataLen + 3 - 1) As Byte ' Data + Count + 2 byte CRC + + bytDataToSendLengthPtr = 0; + intEncodedDataPtr = 2; + + if (intBaud < 600 || intDataLen < 600) + { + // Now compute the RS frame for each carrier in sequence and move it to bytEncodedBytes + + for (i = 0; i < intNumCar; i++) // across all carriers + { + intCarDataCnt = Length - bytDataToSendLengthPtr; + + if (intCarDataCnt >= intDataLen) // why not > ?? + { + // Won't all fit + + bytToRS[0] = intDataLen; + intStartIndex = intEncodedDataPtr; + memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intDataLen); + bytDataToSendLengthPtr += intDataLen; + } + else + { + // Last bit + + bytToRS[0] = intCarDataCnt; // Could be 0 if insuffient data for # of carriers + + if (intCarDataCnt > 0) + { + memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intCarDataCnt); + bytDataToSendLengthPtr += intCarDataCnt; + } + } + + GenCRC16FrameType(bytToRS, intDataLen + 1, bytFrameType); // calculate the CRC on the byte count + data bytes + + RSEncode(bytToRS, bytToRS + intDataLen + 3, intDataLen + 3, intRSLen); // Generate the RS encoding ...now 14 bytes total + + // Need: (2 bytes for Frame Type) +( Data + RS + 1 byte byteCount + 2 Byte CRC per carrier) + + intEncodedDataPtr += intDataLen + 3 + intRSLen; + + bytToRS += intDataLen + 3 + intRSLen; + } + return intEncodedDataPtr; + } + + // special case for 600 baud 4FSK which has 600 byte data field sent as three sequencial (200 byte + 50 byte RS) groups + + for (i = 0; i < 3; i++) // for three blocks of RS data + { + intCarDataCnt = Length - bytDataToSendLengthPtr; + + if (intCarDataCnt >= intDataLen / 3) // why not > ?? + { + // Won't all fit + + bytToRS[0] = intDataLen / 3; + intStartIndex = intEncodedDataPtr; + memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intDataLen / 3); + bytDataToSendLengthPtr += intDataLen / 3; + } + else + { + // Last bit + + bytToRS[0] = intCarDataCnt; // Could be 0 if insuffient data for # of carriers + + if (intCarDataCnt > 0) + { + memcpy(&bytToRS[1], &bytDataToSend[bytDataToSendLengthPtr], intCarDataCnt); + bytDataToSendLengthPtr += intCarDataCnt; + } + } + GenCRC16FrameType(bytToRS, intDataLen / 3 + 1, bytFrameType); // calculate the CRC on the byte count + data bytes + + RSEncode(bytToRS, bytToRS + intDataLen / 3 + 3, intDataLen / 3 + 3, intRSLen / 3); // Generate the RS encoding ...now 14 bytes total + intEncodedDataPtr += intDataLen / 3 + 3 + intRSLen / 3; + bytToRS += intDataLen / 3 + 3 + intRSLen / 3; + } + return intEncodedDataPtr; +} + + +void DrawRXFrame(int State, const char * Frame) +{ +} +void DrawTXFrame(const char * Frame) +{ +} + +int SendtoGUI(char Type, unsigned char * Msg, int Len) +{ + return 0; +} \ No newline at end of file diff --git a/ARDOPC.h b/ARDOPC.h new file mode 100644 index 0000000..47ea3d6 --- /dev/null +++ b/ARDOPC.h @@ -0,0 +1,771 @@ + + +#ifndef ARDOPCHEADERDEFINED +#define ARDOPCHEADERDEFINED + +#ifdef CONST +#undef CONST +#endif +#define CONST const // for building sample arrays + +extern const char ProductName[]; +extern const char ProductVersion[]; + +//#define USE_SOUNDMODEM + +#define UseGUI // Enable GUI Front End Support + +#ifndef TEENSY +#ifdef UseGUI + +// Constellation and Waterfall for GUI interface + +//#define PLOTCONSTELLATION +//#define PLOTWATERFALL +//#define PLOTSPECTRUM +#define ConstellationHeight 90 +#define ConstellationWidth 90 +#define WaterfallWidth 205 +#define WaterfallHeight 64 +#define SpectrumWidth 205 +#define SpectrumHeight 64 + +#define PLOTRADIUS 42 +#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 + +#endif +#endif + + + +// Sound interface buffer size + +#define SendSize 1200 // 100 mS for now +#define ReceiveSize 240 // try 100 mS for now +#define NumberofinBuffers 4 + +#define MAXCAR 43 // Max OFDM Carriers + +#define DATABUFFERSIZE 11000 + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + +#ifndef WIN32 +#define max(x, y) ((x) > (y) ? (x) : (y)) +#define min(x, y) ((x) < (y) ? (x) : (y)) +#endif + +#ifdef WIN32 + +typedef void *HANDLE; +#else +#define HANDLE int +#endif + +void txSleep(int mS); + +unsigned int getTicks(); + +//#ifdef WIN32 +//#define round(x) floorf(x + 0.5f); +//#endif + + +#define Now getTicks() + +// DebugLog Severity Levels + +#define LOGEMERGENCY 0 +#define LOGALERT 1 +#define LOGCRIT 2 +#define LOGERROR 3 +#define LOGWARNING 4 +#define LOGNOTICE 5 +#define LOGINFO 6 +#define LOGDEBUG 7 + +#include + +#include +#include +#include +#include +//#include + +#ifdef M_PI +#undef M_PI +#endif + +#define M_PI 3.1415926f + +#ifndef TEENSY +#ifndef WIN32 +#define LINUX +#endif +#endif + +#ifdef __ARM_ARCH +#ifndef TEENSY +#define ARMLINUX +#endif +#endif + +#include "ecc.h" // RS Constants + +typedef int BOOL; +typedef unsigned char UCHAR; + +#define VOID void + +#define FALSE 0 +#define TRUE 1 + +#define False 0 +#define True 1 + +// TEENSY Interface board equates + +#ifdef TEENSY +#ifdef PIBOARD +#define ISSLED LED0 +#else +#define ISSLED LED1 +#endif +#define IRSLED LED1 +#define TRAFFICLED LED2 +#else +#define ISSLED 1 +#define IRSLED 2 +#define TRAFFICLED 3 +#define PKTLED 4 +#endif + +BOOL KeyPTT(BOOL State); + +UCHAR FrameCode(char * strFrameName); +BOOL FrameInfo(UCHAR bytFrameType, int * blnOdd, int * intNumCar, char * strMod, + int * intBaud, int * intDataLen, int * intRSLen, UCHAR * bytQualThres, char * strType); + +void ClearDataToSend(); +int EncodeFSKData(UCHAR bytFrameType, UCHAR * bytDataToSend, int Length, unsigned char * bytEncodedBytes); +int EncodePSKData(UCHAR bytFrameType, UCHAR * bytDataToSend, int Length, unsigned char * bytEncodedBytes); +int EncodeOFDMData(UCHAR bytFrameType, UCHAR * bytDataToSend, int Length, unsigned char * bytEncodedBytes); +int Encode4FSKIDFrame(char * Callsign, char * Square, unsigned char * bytreturn, UCHAR SessionID); +int EncodeDATAACK(int intQuality, UCHAR bytSessionID, UCHAR * bytreturn); +int EncodeDATANAK(int intQuality , UCHAR bytSessionID, UCHAR * bytreturn); +void Mod4FSKDataAndPlay(unsigned char * bytEncodedBytes, int Len, int intLeaderLen, int Chan); +void ModPSKDataAndPlay(unsigned char * bytEncodedBytes, int Len, int intLeaderLen, int Chan); +BOOL IsDataFrame(UCHAR intFrameType); +BOOL CheckValidCallsignSyntax(char * strTargetCallsign); +void StartCodec(char * strFault); +void StopCodec(char * strFault); +BOOL SendARQConnectRequest(char * strMycall, char * strTargetCall); +void AddDataToDataToSend(UCHAR * bytNewData, int Len); +BOOL StartFEC(UCHAR * bytData, int Len, char * strDataMode, int intRepeats, BOOL blnSendID); +void SendID(BOOL blnEnableCWID); +BOOL CheckGSSyntax(char * GS); +//void SetARDOPProtocolState(int value); +unsigned int GenCRC16(unsigned char * Data, unsigned short length); +void SendCommandToHost(char * Cmd); +void TCPSendCommandToHost(char * Cmd); +void SCSSendCommandToHost(char * Cmd); +void SendCommandToHostQuiet(char * Cmd); +void TCPSendCommandToHostQuiet(char * Cmd); +void SCSSendCommandToHostQuiet(char * Cmd); +void UpdateBusyDetector(short * bytNewSamples); +int UpdatePhaseConstellation(short * intPhases, short * intMags, int intPSKPhase, BOOL blnQAM, BOOL OFDM); +void SetARDOPProtocolState(int value); +BOOL BusyDetect3(float * dblMag, int intStart, int intStop); +void SendLogToHost(char * Msg, int len); +VOID Gearshift_2(int intAckNakValue, BOOL blnInit); + +void displayState(const char * State); +void displayCall(int dirn, char * call); + +void SampleSink(int LR, short Sample); +void SoundFlush(); +void StopCapture(); +void StartCapture(); +void DiscardOldSamples(); +void ClearAllMixedSamples(); + +void SetFilter(void * Filter()); + +void AddTrailer(); +void CWID(char * strID, short * intSamples, BOOL blnPlay); +void sendCWID(char * Call, BOOL Play, int Chan); +UCHAR ComputeTypeParity(UCHAR bytFrameType); +void GenCRC16FrameType(char * Data, int Length, UCHAR bytFrameType); +BOOL CheckCRC16FrameType(unsigned char * Data, int Length, UCHAR bytFrameType); +char * strlop(char * buf, char delim); +void QueueCommandToHost(char * Cmd); +void SCSQueueCommandToHost(char * Cmd); +void TCPQueueCommandToHost(char * Cmd); +void SendReplyToHost(char * strText); +void TCPSendReplyToHost(char * strText); +void SCSSendReplyToHost(char * strText); +void LogStats(); +int GetNextFrameData(int * intUpDn, UCHAR * bytFrameTypeToSend, UCHAR * strMod, BOOL blnInitialize); +void SendData(); +int ComputeInterFrameInterval(int intRequestedIntervalMS); +VOID EncodeAndSend4FSKControl(UCHAR bytFrameType, UCHAR bytSessionID, int LeaderLength); +VOID WriteExceptionLog(const char * format, ...); +void SaveQueueOnBreak(); +VOID Statsprintf(const char * format, ...); +VOID CloseDebugLog(); +VOID CloseStatsLog(); +void Abort(); +void SetLED(int LED, int State); +VOID ClearBusy(); +VOID CloseCOMPort(HANDLE fd); +VOID COMClearRTS(HANDLE fd); +VOID COMClearDTR(HANDLE fd); + +//#ifdef WIN32 +void ProcessNewSamples(short * Samples, int nSamples); +VOID Debugprintf(const char * format, ...); +VOID WriteDebugLog(const char * format, ...); +void ardopmain(); +BOOL GetNextFECFrame(); +void GenerateFSKTemplates(); +void printtick(char * msg); +void InitValidFrameTypes(); +//#endif + +extern void Generate50BaudTwoToneLeaderTemplate(); +extern BOOL blnDISCRepeating; + +BOOL DemodDecode4FSKID(UCHAR bytFrameType, char * strCallID, char * strGridSquare); +void DeCompressCallsign(char * bytCallsign, char * returned); +void DeCompressGridSquare(char * bytGS, char * returned); + +int RSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen); +BOOL RSDecode(UCHAR * bytRcv, int Length, int CheckLen, BOOL * blnRSOK); + +void ProcessRcvdFECDataFrame(int intFrameType, UCHAR * bytData, BOOL blnFrameDecodedOK); +void ProcessUnconnectedConReqFrame(int intFrameType, UCHAR * bytData); +void ProcessRcvdARQFrame(UCHAR intFrameType, UCHAR * bytData, int DataLen, BOOL blnFrameDecodedOK); +void InitializeConnection(); + +void AddTagToDataAndSendToHost(UCHAR * Msg, char * Type, int Len); +void TCPAddTagToDataAndSendToHost(UCHAR * Msg, char * Type, int Len); +void SCSAddTagToDataAndSendToHost(UCHAR * Msg, char * Type, int Len); + +void RemoveDataFromQueue(int Len); +void RemodulateLastFrame(); + +void GetSemaphore(); +void FreeSemaphore(); +const char * Name(UCHAR bytID); +const char * shortName(UCHAR bytID); +void InitSound(); +void initFilter(int Width, int centerFreq, int Chan); +void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); +VOID ClosePacketSessions(); +VOID LostHost(); +VOID ProcessPacketHostBytes(UCHAR * RXBuffer, int Len); +int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength); +VOID ProcessDEDModeFrame(UCHAR * rxbuffer, unsigned int Length); +BOOL CheckForPktMon(); +BOOL CheckForPktData(); +void ModOFDMDataAndPlay(unsigned char * bytEncodedBytes, int Len, int intLeaderLen, int Chan); +void GetOFDMFrameInfo(int OFDMMode, int * intDataLen, int * intRSLen, int * Mode, int * Symbols); +void ClearOFDMVariables(); +VOID EncodeAndSendOFDMACK(UCHAR bytSessionID, int LeaderLength, int Chan); +int ProcessOFDMAck(int AckType); +void ProcessOFDMNak(int AckType); + +int SendtoGUI(char Type, unsigned char * Msg, int Len); +void DrawRXFrame(int State, const char * Frame); +void DrawTXFrame(const char * Frame); +void mySetPixel(unsigned char x, unsigned char y, unsigned int Colour); +void clearDisplay(); +void DrawDecode(char * Decode); + + +extern int WaterfallActive; +extern int SpectrumActive; +extern unsigned int PKTLEDTimer; + +extern char stcLastPingstrSender[10]; +extern char stcLastPingstrTarget[10]; +extern int stcLastPingintRcvdSN; +extern int stcLastPingintQuality; +extern time_t stcLastPingdttTimeReceived; + +enum _ReceiveState // used for initial receive testing...later put in correct protocol states +{ + SearchingForLeader, + AcquireSymbolSync, + AcquireFrameSync, + AcquireFrameType, + DecodeFrameType, + AcquireFrame, + DecodeFramestate +}; + +extern enum _ReceiveState State; + +enum _ARQBandwidth +{ + XB200, + XB500, + XB2500, + UNDEFINED +}; + +extern enum _ARQBandwidth ARQBandwidth; +extern const char ARQBandwidths[9][12]; + +enum _ARDOPState +{ + OFFLINE, + DISC, + ISS, + IRS, + IDLE, // ISS in quiet state ...no transmissions) + IRStoISS, // IRS during transition to ISS waiting for ISS's ACK from IRS's BREAK + FECSend, + FECRcv +}; + +extern enum _ARDOPState ProtocolState; + +extern const char ARDOPStates[8][9]; + + + +// Enum of ARQ Substates + +enum _ARQSubStates +{ + None, + ISSConReq, + ISSConAck, + ISSData, + ISSId, + IRSConAck, + IRSData, + IRSBreak, + IRSfromISS, + DISCArqEnd +}; + +extern enum _ARQSubStates ARQState; + +enum _ProtocolMode +{ + Undef, + FEC, + ARQ +}; + +extern enum _ProtocolMode ProtocolMode; + +extern const char ARDOPModes[3][6]; + +extern enum _ARQSubStates ARQState; + +struct SEM +{ + unsigned int Flag; + int Clashes; + int Gets; + int Rels; +}; + +extern struct SEM Semaphore; + +#define DataNAK 0x00 +#define DataNAKLoQ 0x01 +#define ConRejBusy 0x02 +#define ConRejBW 0x03 +#define ConAck 0x04 +#define DISCFRAME 0x05 +#define BREAK 0x06 +#define END 0x07 +#define IDLEFRAME 0x08 +#define ConReq200 0x09 +#define ConReq500 0x0A +#define ConReq2500 0x0B +#define IDFRAME 0x0C +#define PINGACK 0x0D +#define PING 0x0E +#define CQ_de 0x0F + + // 200 Hz Bandwidth + // 1 Car modes + +#define D4PSK_200_50_E 0x10 +#define D4PSK_200_50_O 0x11 +#define D4PSK_200_100_E 0x12 +#define D4PSK_200_100_O 0x13 +#define D16QAM_200_100_E 0x14 +#define D16QAM_200_100_O 0x15 + + // 500 Hz bandwidth Data + // 1 Car 4FSK Data mode 500 Hz, 50 baud tones spaced @ 100 Hz + +#define D4FSK_500_50_E 0x1A +#define D4FSK_500_50_O 0x1B +#define D4PSK_500_50_E 0x1C +#define D4PSK_500_50_O 0x1D +#define D4PSK_500_100_E 0x1E +#define D4PSK_500_100_O 0x1F + // 2 Car 16QAM Data Modes 100 baud +#define D16QAMR_500_100_E 0x20 +#define D16QAMR_500_100_O 0x21 +#define D16QAM_500_100_E 0x22 +#define D16QAM_500_100_O 0x23 + +// OFDM modes + +#define DOFDM_500_55_E 0x24 +#define DOFDM_500_55_O 0x25 + +#define DOFDM_200_55_E 0x26 +#define DOFDM_200_55_O 0x27 + +#define OConReq500 0x18 +#define OConReq2500 0x19 + + + // 1 Khz Bandwidth Data Modes + // 2 Car 4FSK Data mode 1000 Hz, 50 baud tones spaced @ 100 Hz +#define D4FSK_1000_50_E 0x28 +#define D4FSK_1000_50_O 0x29 + + // 2500 bandwidth modes + // 10 Car PSK Data Modes 50 baud + +#define D4PSKR_2500_50_E 0x2A +#define D4PSKR_2500_50_O 0x2B +#define D4PSK_2500_50_E 0x2C +#define D4PSK_2500_50_O 0x2D + + // 10 Car PSK Data Modes 100 baud + +#define D4PSK_2500_100_E 0x2E +#define D4PSK_2500_100_O 0x2F + + // 10 Car 10 Car 16QAMRobust (duplicated carriers) + +#define D16QAMR_2500_100_E 0x30 +#define D16QAMR_2500_100_O 0x31 + + // 10 Car 16QAM Data modes 100 baud + +#define D16QAM_2500_100_E 0x32 +#define D16QAM_2500_100_O 0x33 + + // OFDM modes + +#define DOFDM_2500_55_E 0x34 +#define DOFDM_2500_55_O 0x35 + +#define PktFrameHeader 0x3A // Variable length frame Header +#define PktFrameData 0x3B // Variable length frame Data (Virtual Frsme Type) + +#define OFDMACK 0x3D +#define DataACK 0x3E +#define DataACKHiQ 0x3F + + + +extern CONST short int50BaudTwoToneLeaderTemplate[240]; // holds just 1 symbol (20 ms) of the leader + +//The actual templates over 11 carriers for 16QAM in a 8-8 circular constellation. First 4 symbols only +// (only positive Phase values are in the table, sign reversal is used to get the negative phase values) This reduces the template size to 5280 integers +extern CONST short intQAM50bdCarTemplate[11][4][120]; + +extern CONST short intFSK50bdCarTemplate[12][240]; // Template for 4FSK carriers spaced at 50 Hz, 50 baud +extern CONST short intFSK100bdCarTemplate[4][120]; + +extern CONST short intOFDMTemplate[MAXCAR][8][216]; + +// Config Params +extern char GridSquare[9]; +extern char Callsign[10]; +extern BOOL wantCWID; +extern BOOL CWOnOff; +extern int LeaderLength; +extern int TrailerLength; +extern unsigned int ARQTimeout; +extern int TuningRange; +extern int TXLevel; +extern int RXLevel; +extern int autoRXLevel; +extern BOOL DebugLog; +extern int ARQConReqRepeats; +extern BOOL CommandTrace; +extern char strFECMode[]; +extern char CaptureDevice[]; +extern char PlaybackDevice[]; +extern int port; +extern char HostPort[80]; +extern int pktport; +extern BOOL RadioControl; +extern BOOL SlowCPU; +extern BOOL AccumulateStats; +extern BOOL Use600Modes; +extern BOOL UseOFDM; +extern BOOL EnableOFDM; +extern BOOL FSKOnly; +extern BOOL fastStart; +extern BOOL ConsoleLogLevel; +extern BOOL FileLogLevel; +extern BOOL EnablePingAck; +extern BOOL NegotiateBW; + +extern int dttLastPINGSent; + +extern BOOL blnPINGrepeating; +extern BOOL blnFramePending; +extern int intPINGRepeats; + +extern BOOL gotGPIO; +extern BOOL useGPIO; + +extern int pttGPIOPin; + +extern HANDLE hCATDevice; // port for Rig Control +extern char CATPort[80]; +extern int CATBAUD; +extern int EnableHostCATRX; + +extern HANDLE hPTTDevice; // port for PTT +extern char PTTPort[80]; // Port for Hardware PTT - may be same as control port. +extern int PTTBAUD; + +#define PTTRTS 1 +#define PTTDTR 2 +#define PTTCI_V 4 + +extern UCHAR PTTOnCmd[]; +extern UCHAR PTTOnCmdLen; + +extern UCHAR PTTOffCmd[]; +extern UCHAR PTTOffCmdLen; + +extern int PTTMode; // PTT Control Flags. + + + + +extern char * CaptureDevices; +extern char * PlaybackDevices; + +extern int dttCodecStarted; +extern int dttStartRTMeasure; + +extern int intCalcLeader; // the computed leader to use based on the reported Leader Length + +extern const char strFrameType[64][18]; +extern const char shortFrameType[64][12]; +extern BOOL Capturing; +extern int SoundIsPlaying; +extern int blnLastPTT; +extern BOOL blnAbort; +extern BOOL blnClosing; +extern BOOL blnCodecStarted; +extern BOOL blnInitializing; +extern BOOL blnARQDisconnect; +extern int DriveLevel; +extern int FECRepeats; +extern BOOL FECId; +extern int Squelch; +extern int BusyDet; +extern BOOL blnEnbARQRpt; +extern unsigned int dttNextPlay; + +extern UCHAR bytDataToSend[]; +extern int bytDataToSendLength; + +extern BOOL blnListen; +extern BOOL Monitor; +extern BOOL AutoBreak; +extern BOOL BusyBlock; + +extern int DecodeCompleteTime; + +extern BOOL AccumulateStats; + +extern unsigned char bytEncodedBytes[4500]; +extern int EncLen; + +extern char AuxCalls[10][10]; +extern int AuxCallsLength; + +extern int bytValidFrameTypesLength; +extern int bytValidFrameTypesLengthALL; +extern int bytValidFrameTypesLengthISS; + +extern BOOL blnTimeoutTriggered; +extern int intFrameRepeatInterval; +extern int extraDelay; +extern BOOL PlayComplete; + +extern const UCHAR bytValidFrameTypesALL[]; +extern const UCHAR bytValidFrameTypesISS[]; +extern const UCHAR * bytValidFrameTypes; + +extern const char strAllDataModes[][16]; +extern int strAllDataModesLen; + +extern const short Rate[64]; // Data Rate (in bits/sec) by Frame Type + + +extern BOOL newStatus; + +// RS Variables + +extern int MaxCorrections; + +// Stats counters + +extern int SessBytesSent; +extern int SessBytesReceived; +extern int intLeaderDetects; +extern int intLeaderSyncs; +extern int intAccumLeaderTracking; +extern float dblFSKTuningSNAvg; +extern int intGoodFSKFrameTypes; +extern int intFailedFSKFrameTypes; +extern int intAccumFSKTracking; +extern int intFSKSymbolCnt; +extern int intGoodFSKFrameDataDecodes; +extern int intFailedFSKFrameDataDecodes; +extern int intAvgFSKQuality; +extern int intFrameSyncs; +extern int intGoodPSKSummationDecodes; +extern int intGoodFSKSummationDecodes; +extern int intGoodOFDMSummationDecodes; +extern float dblLeaderSNAvg; +extern int intAccumPSKLeaderTracking; +extern float dblAvgPSKRefErr; +extern int intPSKTrackAttempts; +extern int intAccumPSKTracking; +extern int intQAMTrackAttempts; +extern int intAccumQAMTracking; +extern int intOFDMTrackAttempts; +extern int intAccumOFDMTracking; +extern int intPSKSymbolCnt; +extern int intQAMSymbolCnt; +extern int intOFDMSymbolCnt; +extern int intGoodPSKFrameDataDecodes; +extern int intFailedPSKFrameDataDecodes; +extern int intAvgPSKQuality; +extern int intGoodOFDMFrameDataDecodes; +extern int intFailedOFDMFrameDataDecodes; +extern int intAvgOFDMQuality; +extern float dblAvgDecodeDistance; +extern int intDecodeDistanceCount; +extern int intShiftUPs; +extern int intShiftDNs; +extern unsigned int dttStartSession; +extern int intLinkTurnovers; +extern int intEnvelopeCors; +extern float dblAvgCorMaxToMaxProduct; +extern int intConReqSN; +extern int intConReqQuality; + + + +extern int int4FSKQuality; +extern int int4FSKQualityCnts; +extern int int8FSKQuality; +extern int int8FSKQualityCnts; +extern int int16FSKQuality; +extern int int16FSKQualityCnts; +extern int intFSKSymbolsDecoded; +extern int intPSKQuality[2]; +extern int intPSKQualityCnts[2]; +extern int intPSKSymbolsDecoded; + +extern int intOFDMQuality[8]; +extern int intOFDMQualityCnts[8]; +extern int intOFDMSymbolsDecoded; + +extern int intQAMQuality; +extern int intQAMQualityCnts; +extern int intQAMSymbolsDecoded; +extern int intQAMSymbolCnt; +extern int intOFDMSymbolCnt; +extern int intGoodQAMFrameDataDecodes; +extern int intFailedQAMFrameDataDecodes; +extern int intGoodQAMSummationDecodes; + +extern int dttLastBusyOn; +extern int dttLastBusyOff; +extern int dttLastLeaderDetect; + +extern int LastBusyOn; +extern int LastBusyOff; +extern int dttLastLeaderDetect; + +extern int pktDataLen; +extern int pktRSLen; +extern const char pktMod[16][12]; +extern int pktMode; +extern int pktModeLen; +extern const int pktBW[16]; +extern const int pktCarriers[16]; +extern const int defaultPacLen[16]; +extern const BOOL pktFSK[16]; + +extern int pktMaxFrame; +extern int pktMaxBandwidth; +extern int pktPacLen; +extern int initMode; // 0 - 4PSK 1 - 8PSK 2 = 16QAM + +extern UCHAR UnackedOFDMBlocks[128]; +extern int NextOFDMBlock; + + + +extern BOOL SerialMode; // Set if using SCS Mode, Unset ofr TCP Mode + +// Has to follow enum defs + +BOOL EncodeARQConRequest(char * strMyCallsign, char * strTargetCallsign, enum _ARQBandwidth ARQBandwidth, UCHAR * bytReturn); + + +// OFDM Modes + +#define PSK2 0 +#define PSK4 1 +#define PSK8 2 +#define QAM16 3 +#define PSK16 4 // Experimental - is it better than 16QAM? +#define QAM32 5 +#define PSK4S 6 // Special shorter frame for short messages + +extern int OFDMMode; // OFDM can use various modulation modes and redundancy levels +extern int LastSentOFDMMode; // For retries +extern int LastSentOFDMType; // For retries + +extern int SavedOFDMMode; // used if we switch to a more robust mode cos we don't have much to send +extern int SavedFrameType; + + +extern const char OFDMModes[8][6]; + +#endif \ No newline at end of file diff --git a/BusyDetect.c b/BusyDetect.c new file mode 100644 index 0000000..453bdd2 --- /dev/null +++ b/BusyDetect.c @@ -0,0 +1,367 @@ + + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + +#include +#endif + +#include "ARDOPC.h" + +VOID SortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin); + +int LastBusyOn; +int LastBusyOff; + +BOOL blnLastBusy = FALSE; + +float dblAvgStoNSlowNarrow; +float dblAvgStoNFastNarrow; +float dblAvgStoNSlowWide; +float dblAvgStoNFastWide; +int intLastStart = 0; +int intLastStop = 0; +int intBusyOnCnt = 0; // used to filter Busy ON detections +int intBusyOffCnt = 0; // used to filter Busy OFF detections +int dttLastBusyTrip = 0; +int dttPriorLastBusyTrip = 0; +int dttLastBusyClear = 0; +int dttLastTrip; +extern float dblAvgPk2BaselineRatio, dblAvgBaselineSlow, dblAvgBaselineFast; +int intHoldMs = 5000; + + +VOID ClearBusy() +{ + dttLastBusyTrip = Now; + dttPriorLastBusyTrip = dttLastBusyTrip; + dttLastBusyClear = dttLastBusyTrip + 610; // This insures test in ARDOPprotocol ~ line 887 will work + dttLastTrip = dttLastBusyTrip -intHoldMs; // This clears the busy detect immediatly (required for scanning when re enabled by Listen=True + blnLastBusy = False; + intBusyOnCnt = 0; + intBusyOffCnt = 0; + intLastStart = 0; + 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 + +BOOL BusyDetect2(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 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; +} + + +*/ + + + + +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 dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide; + float dblSlowAlpha = 0.2f; + float dblAvgStoNNarrow = 0, dblAvgStoNWide = 0; + int intNarrow = 8; // 8 x 11.72 Hz about 94 z + int intWide = ((intStop - intStart) * 2) / 3; //* 0.66); + int blnBusy = FALSE; + int BusyDet4th = BusyDet * BusyDet * BusyDet * BusyDet; + + + // First sort signals and look at highest signals:baseline ratio.. + // First narrow band (~94Hz) + + SortSignals2(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow); + + if (intLastStart == intStart && intLastStop == intStop) + dblAvgStoNNarrow = (1 - dblSlowAlpha) * dblAvgStoNNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; + else + { + // This initializes the Narrow average after a bandwidth change + + dblAvgStoNNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; + intLastStart = intStart; + intLastStop = intStop; + } + + // Wide band (66% of current bandwidth) + + SortSignals2(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide); + + if (intLastStart == intStart && intLastStop == intStop) + dblAvgStoNWide = (1 - dblSlowAlpha) * dblAvgStoNWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide; + else + { + // This initializes the Wide average after a bandwidth change + + dblAvgStoNWide = dblAVGSignalPerBinWide / dblAVGBaselineWide; + intLastStart = intStart; + intLastStop = intStop; + } + + // Preliminary calibration...future a function of bandwidth and BusyDet. + + switch (ARQBandwidth) + { + case XB200: + blnBusy = (dblAvgStoNNarrow > (3 + 0.008 * BusyDet4th)) || (dblAvgStoNWide > (5 + 0.02 * BusyDet4th)); + break; + + case XB500: + blnBusy = (dblAvgStoNNarrow > (3 + 0.008 * BusyDet4th) )|| (dblAvgStoNWide > (5 + 0.02 * BusyDet4th)); + break; + + case XB2500: + blnBusy = (dblAvgStoNNarrow > (3 + 0.008 * BusyDet4th)) || (dblAvgStoNWide > (5 + 0.016 * BusyDet4th)); + } + + if (BusyDet == 0) + blnBusy = FALSE; // 0 Disables check ?? Is this the best place to do this? + +// WriteDebugLog(LOGDEBUG, "Busy %d Wide %f Narrow %f", blnBusy, dblAvgStoNWide, dblAvgStoNNarrow); + + if (blnBusy) + { + // This requires multiple adjacent busy conditions to skip over one nuisance Busy trips. + // Busy must be present at least 3 consecutive times ( ~250 ms) to be reported + + intBusyOnCnt += 1; + intBusyOffCnt = 0; + if (intBusyOnCnt > 3) + dttLastTrip = Now; + } + else + { + intBusyOffCnt += 1; + intBusyOnCnt = 0; + } + + if (blnLastBusy == False && intBusyOnCnt >= 3) + { + dttPriorLastBusyTrip = dttLastBusyTrip; // save old dttLastBusyTrip for use in BUSYBLOCKING function + dttLastBusyTrip = Now; + blnLastBusy = True; + } + else + { + if (blnLastBusy && (Now - dttLastTrip) > intHoldMs && intBusyOffCnt >= 3) + { + dttLastBusyClear = Now; + blnLastBusy = False; + } + } + return blnLastBusy; +} + +VOID SortSignals(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin) +{ + // puts the top intNumber of bins between intStartBin and intStopBin into dblAVGSignalPerBin, the rest into dblAvgBaselinePerBin + // for decent accuracy intNumBins should be < 75% of intStopBin-intStartBin) + + float dblAVGSignal[200] = {0};//intNumBins + float dblAVGBaseline[200] = {0};//intStopBin - intStartBin - intNumBins + + float dblSigSum = 0; + float dblTotalSum = 0; + int intSigPtr = 0; + int intBasePtr = 0; + int i, j, k; + + for (i = 0; i < intNumBins; i++) + { + for (j = intStartBin; j <= intStopBin; j++) + { + if (i == 0) + { + dblTotalSum += dblMag[j]; + if (dblMag[j] > dblAVGSignal[i]) + dblAVGSignal[i] = dblMag[j]; + } + else + { + if (dblMag[j] > dblAVGSignal[i] && dblMag[j] < dblAVGSignal[i - 1]) + dblAVGSignal[i] = dblMag[j]; + } + } + } + + for(k = 0; k < intNumBins; k++) + { + dblSigSum += dblAVGSignal[k]; + } + *dblAVGSignalPerBin = dblSigSum / intNumBins; + *dblAVGBaselinePerBin = (dblTotalSum - dblSigSum) / (intStopBin - intStartBin - intNumBins + 1); +} + + BOOL compare(const void *p1, const void *p2) +{ + float x = *(const float *)p1; + float y = *(const float *)p2; + + if (x < y) + return -1; // Return -1 if you want ascending, 1 if you want descending order. + else if (x > y) + return 1; // Return 1 if you want ascending, -1 if you want descending order. + + return 0; +} + +VOID SortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin) +{ + // puts the top intNumber of bins between intStartBin and intStopBin into dblAVGSignalPerBin, the rest into dblAvgBaselinePerBin + // for decent accuracy intNumBins should be < 75% of intStopBin-intStartBin) + + // This version uses a native sort function which is much faster and reduces CPU loading significantly on wide bandwidths. + + float dblSort[202]; + float dblSum1 = 0, dblSum2 = 0; + int numtoSort = (intStopBin - intStartBin) + 1, i; + + memcpy(dblSort, &dblMag[intStartBin], numtoSort * sizeof(float)); + + qsort((void *)dblSort, numtoSort, sizeof(float), compare); + + for (i = numtoSort -1; i >= 0; i--) + { + if (i >= (numtoSort - intNumBins)) + dblSum1 += dblSort[i]; + else + dblSum2 += dblSort[i]; + } + + *dblAVGSignalPerBin = dblSum1 / intNumBins; + *dblAVGBaselinePerBin = dblSum2 / (intStopBin - intStartBin - intNumBins - 1); +} + + \ No newline at end of file diff --git a/Calibrate.c b/Calibrate.c new file mode 100644 index 0000000..f4af53a --- /dev/null +++ b/Calibrate.c @@ -0,0 +1,159 @@ + +#include "UZ7HOStuff.h" + +// if in Satellite Mode look for a Tuning signal + +// As a first try, use ardop leader pattern then single tone + +static short rawSamples[2400]; // Get Frame Type need 2400 and we may add 1200 +static int rawSamplesLength = 0; +static int maxrawSamplesLength; +static float dblOffsetHz = 0;; +static int blnLeaderFound = 0; + +enum _ReceiveState // used for initial receive testing...later put in correct protocol states +{ + SearchingForLeader, + AcquireSymbolSync, + AcquireFrameSync, + AcquireFrameType, + DecodeFrameType, + AcquireFrame, + DecodeFramestate +}; + +static enum _ReceiveState State; + + +void LookForCalPattern(short * Samples, int nSamples); + +void doTuning(short * Samples, int nSamples) +{ + short ardopbuff[2][1200]; + int i, i1 = 0; + + if (UsingBothChannels) + { + for (i = 0; i < rx_bufsize; i++) + { + ardopbuff[0][i] = Samples[i1]; + i1++; + ardopbuff[1][i] = Samples[i1]; + i1++; + } + } + else if (UsingRight) + { + // Extract just right + + i1 = 1; + + for (i = 0; i < rx_bufsize; i++) + { + ardopbuff[1][i] = Samples[i1]; + i1 += 2; + } + } + else + { + // Extract just left + + for (i = 0; i < rx_bufsize; i++) + { + ardopbuff[0][i] = Samples[i1]; + i1 += 2; + } + } + + if (UsingLeft) + { + LookForCalPattern(&ardopbuff[0][0], 0); + } + + if (UsingRight) + { + LookForCalPattern(&ardopbuff[0][0], 1); + } + +} + + + + +void LookForCalPattern(short * Samples, int nSamples) +{ + BOOL blnFrameDecodedOK = FALSE; + + // LookforUZ7HOLeader(Samples, nSamples); + + // printtick("Start afsk"); + // DemodAFSK(Samples, nSamples); + // printtick("End afsk"); + + // return; + + // Append new data to anything in rawSamples + + memcpy(&rawSamples[rawSamplesLength], Samples, nSamples * 2); + rawSamplesLength += nSamples; + + if (rawSamplesLength > maxrawSamplesLength) + maxrawSamplesLength = rawSamplesLength; + + if (rawSamplesLength >= 2400) + Debugprintf("Corrupt rawSamplesLength %d", rawSamplesLength); + + + nSamples = rawSamplesLength; + Samples = rawSamples; + + rawSamplesLength = 0; + + // printtick("Start Busy"); + if (nSamples >= 1024) + UpdateBusyDetector(Samples); + // printtick("Done Busy"); + + // it seems that searchforleader runs on unmixed and unfilered samples + + // Searching for leader + + if (State == SearchingForLeader) + { + // Search for leader as long as 960 samples (8 symbols) available + +// printtick("Start Leader Search"); + + while (State == SearchingForLeader && nSamples >= 1200) + { + int intSN; + + blnLeaderFound = SearchFor2ToneLeader4(Samples, nSamples, &dblOffsetHz, &intSN); + // blnLeaderFound = SearchFor2ToneLeader2(Samples, nSamples, &dblOffsetHz, &intSN); + + if (blnLeaderFound) + { + // Debugprintf("Got Leader"); + + nSamples -= 480; + Samples += 480; // !!!! needs attention !!! + } + else + { + nSamples -= 240; + Samples += 240; // !!!! needs attention !!! + } + } + if (State == SearchingForLeader) + { + // Save unused samples + + memmove(rawSamples, Samples, nSamples * 2); + rawSamplesLength = nSamples; + + // printtick("End Leader Search"); + + return; + } + } +} diff --git a/Config - Copy.cpp b/Config - Copy.cpp new file mode 100644 index 0000000..91b2c7d --- /dev/null +++ b/Config - Copy.cpp @@ -0,0 +1,444 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include + +#include "UZ7HOStuff.h" + +extern "C" void get_exclude_list(char * line, TStringList * list); +extern "C" void get_exclude_frm(char * line, TStringList * list); + +extern "C" int SoundMode; +extern "C" int RX_SR; +extern "C" int TX_SR; +extern "C" int multiCore; +extern "C" char * Wisdom; + +extern "C" word MEMRecovery[5]; + +extern int MintoTray; +extern "C" int UDPClientPort; +extern "C" int UDPServerPort; +extern "C" int TXPort; + +extern char UDPHost[64]; + +extern char CWIDCall[128]; +extern int CWIDInterval; +extern int CWIDLeft; +extern int CWIDRight; +extern int CWIDType; + +extern "C" int RSID_SABM[4]; +extern "C" int RSID_UI[4]; +extern "C" int RSID_SetModem[4]; + + +QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + +// This makes geting settings for more channels easier + +char Prefix[16] = "AX25_A"; + +void GetPortSettings(int Chan); + +QVariant getAX25Param(const char * key, QVariant Default) +{ + char fullKey[64]; + + sprintf(fullKey, "%s/%s", Prefix, key); + return settings->value(fullKey, Default); +} + +void getAX25Params(int chan) +{ + Prefix[5] = chan + 'A'; + GetPortSettings(chan); +} + + +void GetPortSettings(int Chan) +{ + tx_hitoneraisedb[Chan] = getAX25Param("HiToneRaise", 0).toInt(); + + maxframe[Chan] = getAX25Param("Maxframe", 3).toInt(); + fracks[Chan] = getAX25Param("Retries", 15).toInt(); + frack_time[Chan] = getAX25Param("FrackTime", 5).toInt(); + + idletime[Chan] = getAX25Param("IdleTime", 180).toInt(); + slottime[Chan] = getAX25Param("SlotTime", 100).toInt(); + persist[Chan] = getAX25Param("Persist", 128).toInt(); + resptime[Chan] = getAX25Param("RespTime", 1500).toInt(); + TXFrmMode[Chan] = getAX25Param("TXFrmMode", 1).toInt(); + max_frame_collector[Chan] = getAX25Param("FrameCollector", 6).toInt(); + //exclude_callsigns[Chan]= getAX25Param("ExcludeCallsigns/"); + //exclude_APRS_frm[Chan]= getAX25Param("ExcludeAPRSFrmType/"); + KISS_opt[Chan] = getAX25Param("KISSOptimization", false).toInt();; + dyn_frack[Chan] = getAX25Param("DynamicFrack", false).toInt();; + recovery[Chan] = getAX25Param("BitRecovery", 0).toInt(); + NonAX25[Chan] = getAX25Param("NonAX25Frm", false).toInt();; + MEMRecovery[Chan]= getAX25Param("MEMRecovery", 200).toInt(); + IPOLL[Chan] = getAX25Param("IPOLL", 80).toInt(); + + strcpy(MyDigiCall[Chan], getAX25Param("MyDigiCall", "").toString().toUtf8()); + fx25_mode[Chan] = getAX25Param("FX25", FX25_MODE_RX).toInt(); + il2p_mode[Chan] = getAX25Param("IL2P", IL2P_MODE_NONE).toInt(); + RSID_UI[Chan] = getAX25Param("RSID_UI", 0).toInt(); + RSID_SABM[Chan] = getAX25Param("RSID_SABM", 0).toInt(); + RSID_SetModem[Chan] = getAX25Param("RSID_SetModem", 0).toInt(); +} + +void getSettings() +{ + int snd_ch; + + QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + settings->sync(); + + SoundMode = settings->value("Init/SoundMode", 0).toInt(); + UDPClientPort = settings->value("Init/UDPClientPort", 8888).toInt(); + UDPServerPort = settings->value("Init/UDPServerPort", 8884).toInt(); + TXPort = settings->value("Init/TXPort", UDPServerPort).toInt(); + + strcpy(UDPHost, settings->value("Init/UDPHost", "192.168.1.255").toString().toUtf8()); + UDPServ = settings->value("Init/UDPServer", FALSE).toBool(); + + RX_SR = settings->value("Init/RXSampleRate", 12000).toInt(); + TX_SR = settings->value("Init/TXSampleRate", 12000).toInt(); + + strcpy(CaptureDevice, settings->value("Init/SndRXDeviceName", "hw:1,0").toString().toUtf8()); + strcpy(PlaybackDevice, settings->value("Init/SndTXDeviceName", "hw:1,0").toString().toUtf8()); + + raduga = settings->value("Init/DispMode", DISP_RGB).toInt(); + + strcpy(PTTPort, settings->value("Init/PTT", "").toString().toUtf8()); + PTTMode = settings->value("Init/PTTMode", 19200).toInt(); + PTTBAUD = settings->value("Init/PTTBAUD", 19200).toInt(); + + strcpy(PTTOnString, settings->value("Init/PTTOnString", "").toString().toUtf8()); + strcpy(PTTOffString, settings->value("Init/PTTOffString", "").toString().toUtf8()); + + pttGPIOPin = settings->value("Init/pttGPIOPin", 17).toInt(); + pttGPIOPinR = settings->value("Init/pttGPIOPinR", 17).toInt(); + +#ifdef WIN32 + strcpy(CM108Addr, settings->value("Init/CM108Addr", "0xD8C:0x08").toString().toUtf8()); +#else + strcpy(CM108Addr, settings->value("Init/CM108Addr", "/dev/hidraw0").toString().toUtf8()); +#endif + + HamLibPort = settings->value("Init/HamLibPort", 4532).toInt(); + strcpy(HamLibHost, settings->value("Init/HamLibHost", "127.0.0.1").toString().toUtf8()); + + DualPTT = settings->value("Init/DualPTT", 1).toInt(); + TX_rotate = settings->value("Init/TXRotate", 0).toInt(); + + multiCore = settings->value("Init/multiCore", 0).toInt(); + Wisdom = strdup(settings->value("Init/Wisdom", "").toString().toUtf8()); + + + + rx_freq[0] = settings->value("Modem/RXFreq1", 1700).toInt(); + rx_freq[1] = settings->value("Modem/RXFreq2", 1700).toInt(); + rx_freq[2] = settings->value("Modem/RXFreq3", 1700).toInt(); + rx_freq[3] = settings->value("Modem/RXFreq4", 1700).toInt(); + + rcvr_offset[0] = settings->value("Modem/RcvrShift1", 30).toInt(); + rcvr_offset[1] = settings->value("Modem/RcvrShift2", 30).toInt(); + rcvr_offset[2] = settings->value("Modem/RcvrShift3", 30).toInt(); + rcvr_offset[3] = settings->value("Modem/RcvrShift4", 30).toInt(); + speed[0] = settings->value("Modem/ModemType1", SPEED_1200).toInt(); + speed[1] = settings->value("Modem/ModemType2", SPEED_1200).toInt(); + speed[2] = settings->value("Modem/ModemType3", SPEED_1200).toInt(); + speed[3] = settings->value("Modem/ModemType4", SPEED_1200).toInt(); + + RCVR[0] = settings->value("Modem/NRRcvrPairs1", 0).toInt();; + RCVR[1] = settings->value("Modem/NRRcvrPairs2", 0).toInt();; + RCVR[2] = settings->value("Modem/NRRcvrPairs3", 0).toInt();; + RCVR[3] = settings->value("Modem/NRRcvrPairs4", 0).toInt();; + + soundChannel[0] = settings->value("Modem/soundChannel1", 1).toInt(); + soundChannel[1] = settings->value("Modem/soundChannel2", 0).toInt(); + soundChannel[2] = settings->value("Modem/soundChannel3", 0).toInt(); + soundChannel[3] = settings->value("Modem/soundChannel4", 0).toInt(); + + SCO = settings->value("Init/SCO", 0).toInt(); + + dcd_threshold = settings->value("Modem/DCDThreshold", 40).toInt(); + rxOffset = settings->value("Modem/rxOffset", 0).toInt(); + + AGWServ = settings->value("AGWHost/Server", TRUE).toBool(); + AGWPort = settings->value("AGWHost/Port", 8000).toInt(); + KISSServ = settings->value("KISS/Server", FALSE).toBool(); + KISSPort = settings->value("KISS/Port", 8105).toInt(); + + RX_Samplerate = RX_SR + RX_SR * 0.000001*RX_PPM; + TX_Samplerate = TX_SR + TX_SR * 0.000001*TX_PPM; + + emph_all[0] = settings->value("Modem/PreEmphasisAll1", FALSE).toBool(); + emph_all[1] = settings->value("Modem/PreEmphasisAll2", FALSE).toBool(); + emph_all[2] = settings->value("Modem/PreEmphasisAll3", FALSE).toBool(); + emph_all[3] = settings->value("Modem/PreEmphasisAll4", FALSE).toBool(); + + emph_db[0] = settings->value("Modem/PreEmphasisDB1", 0).toInt(); + emph_db[1] = settings->value("Modem/PreEmphasisDB2", 0).toInt(); + emph_db[2] = settings->value("Modem/PreEmphasisDB3", 0).toInt(); + emph_db[3] = settings->value("Modem/PreEmphasisDB4", 0).toInt(); + + Firstwaterfall = settings->value("Window/Waterfall1", TRUE).toInt(); + Secondwaterfall = settings->value("Window/Waterfall2", TRUE).toInt(); + + txdelay[0] = settings->value("Modem/TxDelay1", 250).toInt(); + txdelay[1] = settings->value("Modem/TxDelay2", 250).toInt(); + txdelay[2] = settings->value("Modem/TxDelay3", 250).toInt(); + txdelay[3] = settings->value("Modem/TxDelay4", 250).toInt(); + + strcpy(CWIDCall, settings->value("Modem/CWIDCall", "").toString().toUtf8().toUpper()); + CWIDInterval = settings->value("Modem/CWIDInterval", 0).toInt(); + CWIDLeft = settings->value("Modem/CWIDLeft", 0).toInt(); + CWIDRight = settings->value("Modem/CWIDRight", 0).toInt(); + CWIDType = settings->value("Modem/CWIDType", 1).toInt(); // on/off + + + getAX25Params(0); + getAX25Params(1); + getAX25Params(2); + getAX25Params(3); + + // Validate and process settings + + UsingLeft = 0; + UsingRight = 0; + UsingBothChannels = 0; + + for (int i = 0; i < 4; i++) + { + if (soundChannel[i] == LEFT) + { + UsingLeft = 1; + modemtoSoundLR[i] = 0; + } + else if (soundChannel[i] == RIGHT) + { + UsingRight = 1; + modemtoSoundLR[i] = 1; + } + } + + if (UsingLeft && UsingRight) + UsingBothChannels = 1; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + tx_hitoneraise[snd_ch] = powf(10.0f, -abs(tx_hitoneraisedb[snd_ch]) / 20.0f); + + if (IPOLL[snd_ch] < 0) + IPOLL[snd_ch] = 0; + else if (IPOLL[snd_ch] > 65535) + IPOLL[snd_ch] = 65535; + + if (MEMRecovery[snd_ch] < 1) + MEMRecovery[snd_ch] = 1; + + // if (MEMRecovery[snd_ch]> 65535) + // MEMRecovery[snd_ch]= 65535; + + /* + if resptime[snd_ch] < 0 then resptime[snd_ch]= 0; + if resptime[snd_ch] > 65535 then resptime[snd_ch]= 65535; + if persist[snd_ch] > 255 then persist[snd_ch]= 255; + if persist[snd_ch] < 32 then persist[snd_ch]= 32; + if fracks[snd_ch] < 1 then fracks[snd_ch]= 1; + if frack_time[snd_ch] < 1 then frack_time[snd_ch]= 1; + if idletime[snd_ch] < frack_time[snd_ch] then idletime[snd_ch]= 180; + */ + + if (emph_db[snd_ch] < 0 || emph_db[snd_ch] > nr_emph) + emph_db[snd_ch] = 0; + + if (max_frame_collector[snd_ch] > 6) max_frame_collector[snd_ch] = 6; + if (maxframe[snd_ch] == 0 || maxframe[snd_ch] > 7) maxframe[snd_ch] = 3; + if (qpsk_set[snd_ch].mode > 1) qpsk_set[snd_ch].mode = 0; + + } + + delete(settings); +} + +void SavePortSettings(int Chan); + +void saveAX25Param(const char * key, QVariant Value) +{ + char fullKey[64]; + + sprintf(fullKey, "%s/%s", Prefix, key); + + settings->setValue(fullKey, Value); +} + +void saveAX25Params(int chan) +{ + Prefix[5] = chan + 'A'; + SavePortSettings(chan); +} + +void SavePortSettings(int Chan) +{ + saveAX25Param("Retries", fracks[Chan]); + saveAX25Param("HiToneRaise", tx_hitoneraisedb[Chan]); + saveAX25Param("Maxframe",maxframe[Chan]); + saveAX25Param("Retries", fracks[Chan]); + saveAX25Param("FrackTime", frack_time[Chan]); + saveAX25Param("IdleTime", idletime[Chan]); + saveAX25Param("SlotTime", slottime[Chan]); + saveAX25Param("Persist", persist[Chan]); + saveAX25Param("RespTime", resptime[Chan]); + saveAX25Param("TXFrmMode", TXFrmMode[Chan]); + saveAX25Param("FrameCollector", max_frame_collector[Chan]); + saveAX25Param("ExcludeCallsigns", exclude_callsigns[Chan]); + saveAX25Param("ExcludeAPRSFrmType", exclude_APRS_frm[Chan]); + saveAX25Param("KISSOptimization", KISS_opt[Chan]); + saveAX25Param("DynamicFrack", dyn_frack[Chan]); + saveAX25Param("BitRecovery", recovery[Chan]); + saveAX25Param("NonAX25Frm", NonAX25[Chan]); + saveAX25Param("MEMRecovery", MEMRecovery[Chan]); + saveAX25Param("IPOLL", IPOLL[Chan]); + saveAX25Param("MyDigiCall", MyDigiCall[Chan]); + saveAX25Param("FX25", fx25_mode[Chan]); + saveAX25Param("IL2P", il2p_mode[Chan]); + saveAX25Param("RSID_UI", RSID_UI[Chan]); + saveAX25Param("RSID_SABM", RSID_SABM[Chan]); + saveAX25Param("RSID_SetModem", RSID_SetModem[Chan]); +} + +void saveSettings() +{ + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + + settings->setValue("Init/SoundMode", SoundMode); + settings->setValue("Init/UDPClientPort", UDPClientPort); + settings->setValue("Init/UDPServerPort", UDPServerPort); + settings->setValue("Init/TXPort", TXPort); + + settings->setValue("Init/UDPServer", UDPServ); + settings->setValue("Init/UDPHost", UDPHost); + + + settings->setValue("Init/TXSampleRate", TX_SR); + settings->setValue("Init/RXSampleRate", RX_SR); + + settings->setValue("Init/SndRXDeviceName", CaptureDevice); + settings->setValue("Init/SndTXDeviceName", PlaybackDevice); + + settings->setValue("Init/SCO", SCO); + settings->setValue("Init/DualPTT", DualPTT); + settings->setValue("Init/TXRotate", TX_rotate); + + settings->setValue("Init/DispMode", raduga); + + settings->setValue("Init/PTT", PTTPort); + settings->setValue("Init/PTTBAUD", PTTBAUD); + settings->setValue("Init/PTTMode", PTTMode); + + settings->setValue("Init/PTTOffString", PTTOffString); + settings->setValue("Init/PTTOnString", PTTOnString); + + settings->setValue("Init/pttGPIOPin", pttGPIOPin); + settings->setValue("Init/pttGPIOPinR", pttGPIOPinR); + + settings->setValue("Init/CM108Addr", CM108Addr); + settings->setValue("Init/HamLibPort", HamLibPort); + settings->setValue("Init/HamLibHost", HamLibHost); + settings->setValue("Init/MinimizetoTray", MintoTray); + + settings->setValue("Init/multiCore", multiCore); + + settings->setValue("Init/Wisdom", Wisdom); + + // Don't save freq on close as it could be offset by multiple decoders + + settings->setValue("Modem/NRRcvrPairs1", RCVR[0]); + settings->setValue("Modem/NRRcvrPairs2", RCVR[1]); + settings->setValue("Modem/NRRcvrPairs3", RCVR[2]); + settings->setValue("Modem/NRRcvrPairs4", RCVR[3]); + + settings->setValue("Modem/RcvrShift1", rcvr_offset[0]); + settings->setValue("Modem/RcvrShift2", rcvr_offset[1]); + settings->setValue("Modem/RcvrShift3", rcvr_offset[2]); + settings->setValue("Modem/RcvrShift4", rcvr_offset[3]); + + settings->setValue("Modem/ModemType1", speed[0]); + settings->setValue("Modem/ModemType2", speed[1]); + settings->setValue("Modem/ModemType3", speed[2]); + settings->setValue("Modem/ModemType4", speed[3]); + + settings->setValue("Modem/soundChannel1", soundChannel[0]); + settings->setValue("Modem/soundChannel2", soundChannel[1]); + settings->setValue("Modem/soundChannel3", soundChannel[2]); + settings->setValue("Modem/soundChannel4", soundChannel[3]); + + settings->setValue("Modem/DCDThreshold", dcd_threshold); + settings->setValue("Modem/rxOffset", rxOffset); + + settings->setValue("AGWHost/Server", AGWServ); + settings->setValue("AGWHost/Port", AGWPort); + settings->setValue("KISS/Server", KISSServ); + settings->setValue("KISS/Port", KISSPort); + + settings->setValue("Modem/PreEmphasisAll1", emph_all[0]); + settings->setValue("Modem/PreEmphasisAll2", emph_all[1]); + settings->setValue("Modem/PreEmphasisAll3", emph_all[2]); + settings->setValue("Modem/PreEmphasisAll4", emph_all[3]); + + settings->setValue("Modem/PreEmphasisDB1", emph_db[0]); + settings->setValue("Modem/PreEmphasisDB2", emph_db[1]); + settings->setValue("Modem/PreEmphasisDB3", emph_db[2]); + settings->setValue("Modem/PreEmphasisDB4", emph_db[3]); + + settings->setValue("Window/Waterfall1", Firstwaterfall); + settings->setValue("Window/Waterfall2", Secondwaterfall); + + settings->setValue("Modem/TxDelay1", txdelay[0]); + settings->setValue("Modem/TxDelay2", txdelay[1]); + settings->setValue("Modem/TxDelay3", txdelay[2]); + settings->setValue("Modem/TxDelay4", txdelay[3]); + + settings->setValue("Modem/TxTail1", txtail[0]); + settings->setValue("Modem/TxTail2", txtail[1]); + settings->setValue("Modem/TxTail3", txtail[2]); + settings->setValue("Modem/TxTail4", txtail[3]); + + settings->setValue("Modem/CWIDCall", CWIDCall); + settings->setValue("Modem/CWIDInterval", CWIDInterval); + settings->setValue("Modem/CWIDLeft", CWIDLeft); + settings->setValue("Modem/CWIDRight", CWIDRight); + settings->setValue("Modem/CWIDType", CWIDType); + + saveAX25Params(0); + saveAX25Params(1); + saveAX25Params(2); + saveAX25Params(3); + + settings->sync(); + + delete(settings); +} diff --git a/Config.cpp b/Config.cpp new file mode 100644 index 0000000..4392c2f --- /dev/null +++ b/Config.cpp @@ -0,0 +1,452 @@ +/*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 "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 char CWIDCall[128]; +extern int CWIDInterval; +extern int CWIDLeft; +extern int CWIDRight; +extern int CWIDType; + +extern "C" int RSID_SABM[4]; +extern "C" int RSID_UI[4]; +extern "C" int RSID_SetModem[4]; + + +QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + +// This makes geting settings for more channels easier + +char Prefix[16] = "AX25_A"; + +void GetPortSettings(int Chan); + +QVariant getAX25Param(const char * key, QVariant Default) +{ + char fullKey[64]; + 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(); + RSID_UI[Chan] = getAX25Param("RSID_UI", 0).toInt(); + RSID_SABM[Chan] = getAX25Param("RSID_SABM", 0).toInt(); + RSID_SetModem[Chan] = getAX25Param("RSID_SetModem", 0).toInt(); +} + +void getSettings() +{ + int snd_ch; + + QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + settings->sync(); + + SoundMode = settings->value("Init/SoundMode", 0).toInt(); + UDPClientPort = settings->value("Init/UDPClientPort", 8888).toInt(); + UDPServerPort = settings->value("Init/UDPServerPort", 8884).toInt(); + TXPort = settings->value("Init/TXPort", UDPServerPort).toInt(); + + strcpy(UDPHost, settings->value("Init/UDPHost", "192.168.1.255").toString().toUtf8()); + UDPServ = settings->value("Init/UDPServer", FALSE).toBool(); + + RX_SR = settings->value("Init/RXSampleRate", 12000).toInt(); + TX_SR = settings->value("Init/TXSampleRate", 12000).toInt(); + + strcpy(CaptureDevice, settings->value("Init/SndRXDeviceName", "hw:1,0").toString().toUtf8()); + strcpy(PlaybackDevice, settings->value("Init/SndTXDeviceName", "hw:1,0").toString().toUtf8()); + + raduga = settings->value("Init/DispMode", DISP_RGB).toInt(); + + strcpy(PTTPort, settings->value("Init/PTT", "").toString().toUtf8()); + PTTMode = settings->value("Init/PTTMode", 19200).toInt(); + PTTBAUD = settings->value("Init/PTTBAUD", 19200).toInt(); + + strcpy(PTTOnString, settings->value("Init/PTTOnString", "").toString().toUtf8()); + strcpy(PTTOffString, settings->value("Init/PTTOffString", "").toString().toUtf8()); + + pttGPIOPin = settings->value("Init/pttGPIOPin", 17).toInt(); + pttGPIOPinR = settings->value("Init/pttGPIOPinR", 17).toInt(); + +#ifdef WIN32 + strcpy(CM108Addr, settings->value("Init/CM108Addr", "0xD8C:0x08").toString().toUtf8()); +#else + strcpy(CM108Addr, settings->value("Init/CM108Addr", "/dev/hidraw0").toString().toUtf8()); +#endif + + HamLibPort = settings->value("Init/HamLibPort", 4532).toInt(); + strcpy(HamLibHost, settings->value("Init/HamLibHost", "127.0.0.1").toString().toUtf8()); + + DualPTT = settings->value("Init/DualPTT", 1).toInt(); + TX_rotate = settings->value("Init/TXRotate", 0).toInt(); + multiCore = settings->value("Init/multiCore", 0).toInt(); + MintoTray = settings->value("Init/MinimizetoTray", 1).toInt(); + 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(); + + strcpy(CWIDCall, settings->value("Modem/CWIDCall", "").toString().toUtf8().toUpper()); + CWIDInterval = settings->value("Modem/CWIDInterval", 0).toInt(); + CWIDLeft = settings->value("Modem/CWIDLeft", 0).toInt(); + CWIDRight = settings->value("Modem/CWIDRight", 0).toInt(); + CWIDType = settings->value("Modem/CWIDType", 1).toInt(); // on/off + + + getAX25Params(0); + getAX25Params(1); + getAX25Params(2); + getAX25Params(3); + + // Validate and process settings + + UsingLeft = 0; + UsingRight = 0; + UsingBothChannels = 0; + + for (int i = 0; i < 4; i++) + { + if (soundChannel[i] == LEFT) + { + UsingLeft = 1; + modemtoSoundLR[i] = 0; + } + else if (soundChannel[i] == RIGHT) + { + UsingRight = 1; + modemtoSoundLR[i] = 1; + } + } + + if (UsingLeft && UsingRight) + UsingBothChannels = 1; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + tx_hitoneraise[snd_ch] = powf(10.0f, -abs(tx_hitoneraisedb[snd_ch]) / 20.0f); + + if (IPOLL[snd_ch] < 0) + IPOLL[snd_ch] = 0; + else if (IPOLL[snd_ch] > 65535) + IPOLL[snd_ch] = 65535; + + if (MEMRecovery[snd_ch] < 1) + MEMRecovery[snd_ch] = 1; + + // if (MEMRecovery[snd_ch]> 65535) + // MEMRecovery[snd_ch]= 65535; + + /* + if resptime[snd_ch] < 0 then resptime[snd_ch]= 0; + if resptime[snd_ch] > 65535 then resptime[snd_ch]= 65535; + if persist[snd_ch] > 255 then persist[snd_ch]= 255; + if persist[snd_ch] < 32 then persist[snd_ch]= 32; + if fracks[snd_ch] < 1 then fracks[snd_ch]= 1; + if frack_time[snd_ch] < 1 then frack_time[snd_ch]= 1; + if idletime[snd_ch] < frack_time[snd_ch] then idletime[snd_ch]= 180; + */ + + if (emph_db[snd_ch] < 0 || emph_db[snd_ch] > nr_emph) + emph_db[snd_ch] = 0; + + if (max_frame_collector[snd_ch] > 6) max_frame_collector[snd_ch] = 6; + if (maxframe[snd_ch] == 0 || maxframe[snd_ch] > 7) maxframe[snd_ch] = 3; + if (qpsk_set[snd_ch].mode > 1) qpsk_set[snd_ch].mode = 0; + + } + + delete(settings); +} + +void SavePortSettings(int Chan); + +void saveAX25Param(const char * key, QVariant Value) +{ + char fullKey[64]; + + sprintf(fullKey, "%s/%s", Prefix, key); + + settings->setValue(fullKey, Value); +} + +void saveAX25Params(int chan) +{ + Prefix[5] = chan + 'A'; + SavePortSettings(chan); +} + +void SavePortSettings(int Chan) +{ + saveAX25Param("Retries", fracks[Chan]); + saveAX25Param("HiToneRaise", tx_hitoneraisedb[Chan]); + saveAX25Param("Maxframe",maxframe[Chan]); + saveAX25Param("Retries", fracks[Chan]); + saveAX25Param("FrackTime", frack_time[Chan]); + saveAX25Param("IdleTime", idletime[Chan]); + saveAX25Param("SlotTime", slottime[Chan]); + saveAX25Param("Persist", persist[Chan]); + saveAX25Param("RespTime", resptime[Chan]); + saveAX25Param("TXFrmMode", TXFrmMode[Chan]); + saveAX25Param("FrameCollector", max_frame_collector[Chan]); + saveAX25Param("ExcludeCallsigns", exclude_callsigns[Chan]); + saveAX25Param("ExcludeAPRSFrmType", exclude_APRS_frm[Chan]); + saveAX25Param("KISSOptimization", KISS_opt[Chan]); + saveAX25Param("DynamicFrack", dyn_frack[Chan]); + saveAX25Param("BitRecovery", recovery[Chan]); + saveAX25Param("NonAX25Frm", NonAX25[Chan]); + saveAX25Param("MEMRecovery", MEMRecovery[Chan]); + saveAX25Param("IPOLL", IPOLL[Chan]); + saveAX25Param("MyDigiCall", MyDigiCall[Chan]); + saveAX25Param("FX25", fx25_mode[Chan]); + saveAX25Param("IL2P", il2p_mode[Chan]); + saveAX25Param("RSID_UI", RSID_UI[Chan]); + saveAX25Param("RSID_SABM", RSID_SABM[Chan]); + saveAX25Param("RSID_SetModem", RSID_SetModem[Chan]); +} + +void saveSettings() +{ + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + + settings->setValue("Init/SoundMode", SoundMode); + settings->setValue("Init/UDPClientPort", UDPClientPort); + settings->setValue("Init/UDPServerPort", UDPServerPort); + settings->setValue("Init/TXPort", TXPort); + + settings->setValue("Init/UDPServer", UDPServ); + settings->setValue("Init/UDPHost", UDPHost); + + + settings->setValue("Init/TXSampleRate", TX_SR); + settings->setValue("Init/RXSampleRate", RX_SR); + + settings->setValue("Init/SndRXDeviceName", CaptureDevice); + settings->setValue("Init/SndTXDeviceName", PlaybackDevice); + + settings->setValue("Init/SCO", SCO); + settings->setValue("Init/DualPTT", DualPTT); + settings->setValue("Init/TXRotate", TX_rotate); + + settings->setValue("Init/DispMode", raduga); + + settings->setValue("Init/PTT", PTTPort); + settings->setValue("Init/PTTBAUD", PTTBAUD); + settings->setValue("Init/PTTMode", PTTMode); + + settings->setValue("Init/PTTOffString", PTTOffString); + settings->setValue("Init/PTTOnString", PTTOnString); + + settings->setValue("Init/pttGPIOPin", pttGPIOPin); + settings->setValue("Init/pttGPIOPinR", pttGPIOPinR); + + settings->setValue("Init/CM108Addr", CM108Addr); + settings->setValue("Init/HamLibPort", HamLibPort); + settings->setValue("Init/HamLibHost", HamLibHost); + settings->setValue("Init/MinimizetoTray", MintoTray); + settings->setValue("Init/multiCore", multiCore); + settings->setValue("Init/Wisdom", Wisdom); + + 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/CWIDInterval", CWIDInterval); + settings->setValue("Modem/CWIDLeft", CWIDLeft); + settings->setValue("Modem/CWIDRight", CWIDRight); + settings->setValue("Modem/CWIDType", CWIDType); + + saveAX25Params(0); + saveAX25Params(1); + saveAX25Params(2); + saveAX25Params(3); + + settings->sync(); + + delete(settings); +} diff --git a/Config.cpp.bak b/Config.cpp.bak new file mode 100644 index 0000000..0ca299e --- /dev/null +++ b/Config.cpp.bak @@ -0,0 +1,439 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include + +#include "UZ7HOStuff.h" + +extern "C" void get_exclude_list(char * line, TStringList * list); +extern "C" void get_exclude_frm(char * line, TStringList * list); + +extern "C" int SoundMode; +extern "C" int RX_SR; +extern "C" int TX_SR; +extern "C" int multiCore; + +extern "C" word MEMRecovery[5]; + +extern int MintoTray; +extern "C" int UDPClientPort; +extern "C" int UDPServerPort; +extern "C" int TXPort; + +extern char UDPHost[64]; + +extern char CWIDCall[128]; +extern int CWIDInterval; +extern int CWIDLeft; +extern int CWIDRight; +extern int CWIDType; + +extern "C" int RSID_SABM[4]; +extern "C" int RSID_UI[4]; +extern "C" int RSID_SetModem[4]; + + +QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + +// This makes geting settings for more channels easier + +char Prefix[16] = "AX25_A"; + +void GetPortSettings(int Chan); + +QVariant getAX25Param(const char * key, QVariant Default) +{ + char fullKey[64]; + + sprintf(fullKey, "%s/%s", Prefix, key); + return settings->value(fullKey, Default); +} + +void getAX25Params(int chan) +{ + Prefix[5] = chan + 'A'; + GetPortSettings(chan); +} + + +void GetPortSettings(int Chan) +{ + tx_hitoneraisedb[Chan] = getAX25Param("HiToneRaise", 0).toInt(); + + maxframe[Chan] = getAX25Param("Maxframe", 3).toInt(); + fracks[Chan] = getAX25Param("Retries", 15).toInt(); + frack_time[Chan] = getAX25Param("FrackTime", 5).toInt(); + + idletime[Chan] = getAX25Param("IdleTime", 180).toInt(); + slottime[Chan] = getAX25Param("SlotTime", 100).toInt(); + persist[Chan] = getAX25Param("Persist", 128).toInt(); + resptime[Chan] = getAX25Param("RespTime", 1500).toInt(); + TXFrmMode[Chan] = getAX25Param("TXFrmMode", 1).toInt(); + max_frame_collector[Chan] = getAX25Param("FrameCollector", 6).toInt(); + //exclude_callsigns[Chan]= getAX25Param("ExcludeCallsigns/"); + //exclude_APRS_frm[Chan]= getAX25Param("ExcludeAPRSFrmType/"); + KISS_opt[Chan] = getAX25Param("KISSOptimization", false).toInt();; + dyn_frack[Chan] = getAX25Param("DynamicFrack", false).toInt();; + recovery[Chan] = getAX25Param("BitRecovery", 0).toInt(); + NonAX25[Chan] = getAX25Param("NonAX25Frm", false).toInt();; + MEMRecovery[Chan]= getAX25Param("MEMRecovery", 200).toInt(); + IPOLL[Chan] = getAX25Param("IPOLL", 80).toInt(); + + strcpy(MyDigiCall[Chan], getAX25Param("MyDigiCall", "").toString().toUtf8()); + fx25_mode[Chan] = getAX25Param("FX25", FX25_MODE_RX).toInt(); + il2p_mode[Chan] = getAX25Param("IL2P", IL2P_MODE_NONE).toInt(); + RSID_UI[Chan] = getAX25Param("RSID_UI", 0).toInt(); + RSID_SABM[Chan] = getAX25Param("RSID_SABM", 0).toInt(); + RSID_SetModem[Chan] = getAX25Param("RSID_SetModem", 0).toInt(); +} + +void getSettings() +{ + int snd_ch; + + QSettings* settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + settings->sync(); + + SoundMode = settings->value("Init/SoundMode", 0).toInt(); + UDPClientPort = settings->value("Init/UDPClientPort", 8888).toInt(); + UDPServerPort = settings->value("Init/UDPServerPort", 8884).toInt(); + TXPort = settings->value("Init/TXPort", UDPServerPort).toInt(); + + strcpy(UDPHost, settings->value("Init/UDPHost", "192.168.1.255").toString().toUtf8()); + UDPServ = settings->value("Init/UDPServer", FALSE).toBool(); + + RX_SR = settings->value("Init/RXSampleRate", 12000).toInt(); + TX_SR = settings->value("Init/TXSampleRate", 12000).toInt(); + + strcpy(CaptureDevice, settings->value("Init/SndRXDeviceName", "hw:1,0").toString().toUtf8()); + strcpy(PlaybackDevice, settings->value("Init/SndTXDeviceName", "hw:1,0").toString().toUtf8()); + + raduga = settings->value("Init/DispMode", DISP_RGB).toInt(); + + strcpy(PTTPort, settings->value("Init/PTT", "").toString().toUtf8()); + PTTMode = settings->value("Init/PTTMode", 19200).toInt(); + PTTBAUD = settings->value("Init/PTTBAUD", 19200).toInt(); + + strcpy(PTTOnString, settings->value("Init/PTTOnString", "").toString().toUtf8()); + strcpy(PTTOffString, settings->value("Init/PTTOffString", "").toString().toUtf8()); + + pttGPIOPin = settings->value("Init/pttGPIOPin", 17).toInt(); + pttGPIOPinR = settings->value("Init/pttGPIOPinR", 17).toInt(); + +#ifdef WIN32 + strcpy(CM108Addr, settings->value("Init/CM108Addr", "0xD8C:0x08").toString().toUtf8()); +#else + strcpy(CM108Addr, settings->value("Init/CM108Addr", "/dev/hidraw0").toString().toUtf8()); +#endif + + HamLibPort = settings->value("Init/HamLibPort", 4532).toInt(); + strcpy(HamLibHost, settings->value("Init/HamLibHost", "127.0.0.1").toString().toUtf8()); + + DualPTT = settings->value("Init/DualPTT", 1).toInt(); + TX_rotate = settings->value("Init/TXRotate", 0).toInt(); + + multiCore = settings->value("Init/multiCore", 0).toInt(); + MintoTray = settings->value("Init/MinimizetoTray", 1).toInt(); + + rx_freq[0] = settings->value("Modem/RXFreq1", 1700).toInt(); + rx_freq[1] = settings->value("Modem/RXFreq2", 1700).toInt(); + rx_freq[2] = settings->value("Modem/RXFreq3", 1700).toInt(); + rx_freq[3] = settings->value("Modem/RXFreq4", 1700).toInt(); + + rcvr_offset[0] = settings->value("Modem/RcvrShift1", 30).toInt(); + rcvr_offset[1] = settings->value("Modem/RcvrShift2", 30).toInt(); + rcvr_offset[2] = settings->value("Modem/RcvrShift3", 30).toInt(); + rcvr_offset[3] = settings->value("Modem/RcvrShift4", 30).toInt(); + speed[0] = settings->value("Modem/ModemType1", SPEED_1200).toInt(); + speed[1] = settings->value("Modem/ModemType2", SPEED_1200).toInt(); + speed[2] = settings->value("Modem/ModemType3", SPEED_1200).toInt(); + speed[3] = settings->value("Modem/ModemType4", SPEED_1200).toInt(); + + RCVR[0] = settings->value("Modem/NRRcvrPairs1", 0).toInt();; + RCVR[1] = settings->value("Modem/NRRcvrPairs2", 0).toInt();; + RCVR[2] = settings->value("Modem/NRRcvrPairs3", 0).toInt();; + RCVR[3] = settings->value("Modem/NRRcvrPairs4", 0).toInt();; + + soundChannel[0] = settings->value("Modem/soundChannel1", 1).toInt(); + soundChannel[1] = settings->value("Modem/soundChannel2", 0).toInt(); + soundChannel[2] = settings->value("Modem/soundChannel3", 0).toInt(); + soundChannel[3] = settings->value("Modem/soundChannel4", 0).toInt(); + + SCO = settings->value("Init/SCO", 0).toInt(); + + dcd_threshold = settings->value("Modem/DCDThreshold", 40).toInt(); + rxOffset = settings->value("Modem/rxOffset", 0).toInt(); + + AGWServ = settings->value("AGWHost/Server", TRUE).toBool(); + AGWPort = settings->value("AGWHost/Port", 8000).toInt(); + KISSServ = settings->value("KISS/Server", FALSE).toBool(); + KISSPort = settings->value("KISS/Port", 8105).toInt(); + + RX_Samplerate = RX_SR + RX_SR * 0.000001*RX_PPM; + TX_Samplerate = TX_SR + TX_SR * 0.000001*TX_PPM; + + emph_all[0] = settings->value("Modem/PreEmphasisAll1", FALSE).toBool(); + emph_all[1] = settings->value("Modem/PreEmphasisAll2", FALSE).toBool(); + emph_all[2] = settings->value("Modem/PreEmphasisAll3", FALSE).toBool(); + emph_all[3] = settings->value("Modem/PreEmphasisAll4", FALSE).toBool(); + + emph_db[0] = settings->value("Modem/PreEmphasisDB1", 0).toInt(); + emph_db[1] = settings->value("Modem/PreEmphasisDB2", 0).toInt(); + emph_db[2] = settings->value("Modem/PreEmphasisDB3", 0).toInt(); + emph_db[3] = settings->value("Modem/PreEmphasisDB4", 0).toInt(); + + Firstwaterfall = settings->value("Window/Waterfall1", TRUE).toInt(); + Secondwaterfall = settings->value("Window/Waterfall2", TRUE).toInt(); + + txdelay[0] = settings->value("Modem/TxDelay1", 250).toInt(); + txdelay[1] = settings->value("Modem/TxDelay2", 250).toInt(); + txdelay[2] = settings->value("Modem/TxDelay3", 250).toInt(); + txdelay[3] = settings->value("Modem/TxDelay4", 250).toInt(); + + strcpy(CWIDCall, settings->value("Modem/CWIDCall", "").toString().toUtf8().toUpper()); + CWIDInterval = settings->value("Modem/CWIDInterval", 0).toInt(); + CWIDLeft = settings->value("Modem/CWIDLeft", 0).toInt(); + CWIDRight = settings->value("Modem/CWIDRight", 0).toInt(); + CWIDType = settings->value("Modem/CWIDType", 1).toInt(); // on/off + + + getAX25Params(0); + getAX25Params(1); + getAX25Params(2); + getAX25Params(3); + + // Validate and process settings + + UsingLeft = 0; + UsingRight = 0; + UsingBothChannels = 0; + + for (int i = 0; i < 4; i++) + { + if (soundChannel[i] == LEFT) + { + UsingLeft = 1; + modemtoSoundLR[i] = 0; + } + else if (soundChannel[i] == RIGHT) + { + UsingRight = 1; + modemtoSoundLR[i] = 1; + } + } + + if (UsingLeft && UsingRight) + UsingBothChannels = 1; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + tx_hitoneraise[snd_ch] = powf(10.0f, -abs(tx_hitoneraisedb[snd_ch]) / 20.0f); + + if (IPOLL[snd_ch] < 0) + IPOLL[snd_ch] = 0; + else if (IPOLL[snd_ch] > 65535) + IPOLL[snd_ch] = 65535; + + if (MEMRecovery[snd_ch] < 1) + MEMRecovery[snd_ch] = 1; + + // if (MEMRecovery[snd_ch]> 65535) + // MEMRecovery[snd_ch]= 65535; + + /* + if resptime[snd_ch] < 0 then resptime[snd_ch]= 0; + if resptime[snd_ch] > 65535 then resptime[snd_ch]= 65535; + if persist[snd_ch] > 255 then persist[snd_ch]= 255; + if persist[snd_ch] < 32 then persist[snd_ch]= 32; + if fracks[snd_ch] < 1 then fracks[snd_ch]= 1; + if frack_time[snd_ch] < 1 then frack_time[snd_ch]= 1; + if idletime[snd_ch] < frack_time[snd_ch] then idletime[snd_ch]= 180; + */ + + if (emph_db[snd_ch] < 0 || emph_db[snd_ch] > nr_emph) + emph_db[snd_ch] = 0; + + if (max_frame_collector[snd_ch] > 6) max_frame_collector[snd_ch] = 6; + if (maxframe[snd_ch] == 0 || maxframe[snd_ch] > 7) maxframe[snd_ch] = 3; + if (qpsk_set[snd_ch].mode > 1) qpsk_set[snd_ch].mode = 0; + + } + + delete(settings); +} + +void SavePortSettings(int Chan); + +void saveAX25Param(const char * key, QVariant Value) +{ + char fullKey[64]; + + sprintf(fullKey, "%s/%s", Prefix, key); + + settings->setValue(fullKey, Value); +} + +void saveAX25Params(int chan) +{ + Prefix[5] = chan + 'A'; + SavePortSettings(chan); +} + +void SavePortSettings(int Chan) +{ + saveAX25Param("Retries", fracks[Chan]); + saveAX25Param("HiToneRaise", tx_hitoneraisedb[Chan]); + saveAX25Param("Maxframe",maxframe[Chan]); + saveAX25Param("Retries", fracks[Chan]); + saveAX25Param("FrackTime", frack_time[Chan]); + saveAX25Param("IdleTime", idletime[Chan]); + saveAX25Param("SlotTime", slottime[Chan]); + saveAX25Param("Persist", persist[Chan]); + saveAX25Param("RespTime", resptime[Chan]); + saveAX25Param("TXFrmMode", TXFrmMode[Chan]); + saveAX25Param("FrameCollector", max_frame_collector[Chan]); + saveAX25Param("ExcludeCallsigns", exclude_callsigns[Chan]); + saveAX25Param("ExcludeAPRSFrmType", exclude_APRS_frm[Chan]); + saveAX25Param("KISSOptimization", KISS_opt[Chan]); + saveAX25Param("DynamicFrack", dyn_frack[Chan]); + saveAX25Param("BitRecovery", recovery[Chan]); + saveAX25Param("NonAX25Frm", NonAX25[Chan]); + saveAX25Param("MEMRecovery", MEMRecovery[Chan]); + saveAX25Param("IPOLL", IPOLL[Chan]); + saveAX25Param("MyDigiCall", MyDigiCall[Chan]); + saveAX25Param("FX25", fx25_mode[Chan]); + saveAX25Param("IL2P", il2p_mode[Chan]); + saveAX25Param("RSID_UI", RSID_UI[Chan]); + saveAX25Param("RSID_SABM", RSID_SABM[Chan]); + saveAX25Param("RSID_SetModem", RSID_SetModem[Chan]); +} + +void saveSettings() +{ + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + + settings->setValue("Init/SoundMode", SoundMode); + settings->setValue("Init/UDPClientPort", UDPClientPort); + settings->setValue("Init/UDPServerPort", UDPServerPort); + settings->setValue("Init/TXPort", TXPort); + + settings->setValue("Init/UDPServer", UDPServ); + settings->setValue("Init/UDPHost", UDPHost); + + + settings->setValue("Init/TXSampleRate", TX_SR); + settings->setValue("Init/RXSampleRate", RX_SR); + + settings->setValue("Init/SndRXDeviceName", CaptureDevice); + settings->setValue("Init/SndTXDeviceName", PlaybackDevice); + + settings->setValue("Init/SCO", SCO); + settings->setValue("Init/DualPTT", DualPTT); + settings->setValue("Init/TXRotate", TX_rotate); + + settings->setValue("Init/DispMode", raduga); + + settings->setValue("Init/PTT", PTTPort); + settings->setValue("Init/PTTBAUD", PTTBAUD); + settings->setValue("Init/PTTMode", PTTMode); + + settings->setValue("Init/PTTOffString", PTTOffString); + settings->setValue("Init/PTTOnString", PTTOnString); + + settings->setValue("Init/pttGPIOPin", pttGPIOPin); + settings->setValue("Init/pttGPIOPinR", pttGPIOPinR); + + settings->setValue("Init/CM108Addr", CM108Addr); + settings->setValue("Init/HamLibPort", HamLibPort); + settings->setValue("Init/HamLibHost", HamLibHost); + settings->setValue("Init/MinimizetoTray", MintoTray); + + settings->setValue("Init/multiCore", multiCore); + + // Don't save freq on close as it could be offset by multiple decoders + + settings->setValue("Modem/NRRcvrPairs1", RCVR[0]); + settings->setValue("Modem/NRRcvrPairs2", RCVR[1]); + settings->setValue("Modem/NRRcvrPairs3", RCVR[2]); + settings->setValue("Modem/NRRcvrPairs4", RCVR[3]); + + settings->setValue("Modem/RcvrShift1", rcvr_offset[0]); + settings->setValue("Modem/RcvrShift2", rcvr_offset[1]); + settings->setValue("Modem/RcvrShift3", rcvr_offset[2]); + settings->setValue("Modem/RcvrShift4", rcvr_offset[3]); + + settings->setValue("Modem/ModemType1", speed[0]); + settings->setValue("Modem/ModemType2", speed[1]); + settings->setValue("Modem/ModemType3", speed[2]); + settings->setValue("Modem/ModemType4", speed[3]); + + settings->setValue("Modem/soundChannel1", soundChannel[0]); + settings->setValue("Modem/soundChannel2", soundChannel[1]); + settings->setValue("Modem/soundChannel3", soundChannel[2]); + settings->setValue("Modem/soundChannel4", soundChannel[3]); + + settings->setValue("Modem/DCDThreshold", dcd_threshold); + settings->setValue("Modem/rxOffset", rxOffset); + + settings->setValue("AGWHost/Server", AGWServ); + settings->setValue("AGWHost/Port", AGWPort); + settings->setValue("KISS/Server", KISSServ); + settings->setValue("KISS/Port", KISSPort); + + settings->setValue("Modem/PreEmphasisAll1", emph_all[0]); + settings->setValue("Modem/PreEmphasisAll2", emph_all[1]); + settings->setValue("Modem/PreEmphasisAll3", emph_all[2]); + settings->setValue("Modem/PreEmphasisAll4", emph_all[3]); + + settings->setValue("Modem/PreEmphasisDB1", emph_db[0]); + settings->setValue("Modem/PreEmphasisDB2", emph_db[1]); + settings->setValue("Modem/PreEmphasisDB3", emph_db[2]); + settings->setValue("Modem/PreEmphasisDB4", emph_db[3]); + + settings->setValue("Window/Waterfall1", Firstwaterfall); + settings->setValue("Window/Waterfall2", Secondwaterfall); + + settings->setValue("Modem/TxDelay1", txdelay[0]); + settings->setValue("Modem/TxDelay2", txdelay[1]); + settings->setValue("Modem/TxDelay3", txdelay[2]); + settings->setValue("Modem/TxDelay4", txdelay[3]); + + settings->setValue("Modem/TxTail1", txtail[0]); + settings->setValue("Modem/TxTail2", txtail[1]); + settings->setValue("Modem/TxTail3", txtail[2]); + settings->setValue("Modem/TxTail4", txtail[3]); + + settings->setValue("Modem/CWIDCall", CWIDCall); + settings->setValue("Modem/CWIDInterval", CWIDInterval); + settings->setValue("Modem/CWIDLeft", CWIDLeft); + settings->setValue("Modem/CWIDRight", CWIDRight); + settings->setValue("Modem/CWIDType", CWIDType); + + saveAX25Params(0); + saveAX25Params(1); + saveAX25Params(2); + saveAX25Params(3); + + settings->sync(); + + delete(settings); +} diff --git a/DialogButtonBottom.ui b/DialogButtonBottom.ui new file mode 100644 index 0000000..5159f82 --- /dev/null +++ b/DialogButtonBottom.ui @@ -0,0 +1,100 @@ + + + + + Dialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + 20 + 250 + 351 + 33 + + + + + 0 + + + 6 + + + + + Qt::Horizontal + + + + 131 + 31 + + + + + + + + OK + + + + + + + Cancel + + + + + + + + + + + okButton + clicked() + Dialog + accept() + + + 278 + 253 + + + 96 + 254 + + + + + cancelButton + clicked() + Dialog + reject() + + + 369 + 253 + + + 179 + 282 + + + + + diff --git a/HEAD b/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/ModemDialog.ui b/ModemDialog.ui new file mode 100644 index 0000000..5bc043c --- /dev/null +++ b/ModemDialog.ui @@ -0,0 +1,3041 @@ + + + ModemDialog + + + + 0 + 0 + 614 + 599 + + + + Modem Settings + + + true + + + + + 10 + 24 + 591 + 471 + + + + 0 + + + + Modem 1 + + + + + -1 + -1 + 583 + 441 + + + + false + + + + + 0 + 0 + 577 + 439 + + + + + + 290 + 10 + 283 + 349 + + + + Modem Filters + + + + + 172 + 30 + 61 + 25 + + + + Show + + + + + + 172 + 60 + 61 + 25 + + + + Show + + + + + + 172 + 90 + 60 + 25 + + + + Show + + + + + + 90 + 30 + 61 + 23 + + + + + + + 10 + 30 + 71 + 23 + + + + BPF Width + + + + + + 10 + 60 + 81 + 23 + + + + TXBPF Width + + + + + + 90 + 60 + 61 + 23 + + + + + + + 10 + 90 + 71 + 23 + + + + LPF Width + + + + + + 90 + 90 + 61 + 23 + + + + + + + 90 + 120 + 61 + 23 + + + + + + + 90 + 150 + 61 + 23 + + + + + + + 150 + 204 + 61 + 22 + + + + + None + + + + + 6 dB + + + + + 12 dB + + + + + + + 10 + 180 + 161 + 17 + + + + Default Settings + + + + + + 10 + 232 + 166 + 17 + + + + KISS Optimisations + + + + + + 10 + 256 + 176 + 17 + + + + non-AX25 filter + + + + + + 10 + 120 + 71 + 23 + + + + BPFTaps + + + + + + 10 + 150 + 71 + 23 + + + + LPF Taps + + + + + + 10 + 203 + 141 + 22 + + + + PreEmphasis filter + + + + + + 230 + 207 + 51 + 16 + + + + All + + + + + + + 0 + 10 + 281 + 431 + + + + Modem type + + + + + 100 + 181 + 61 + 23 + + + + + + + 100 + 21 + 61 + 23 + + + + + + + 100 + 51 + 61 + 23 + + + + + + + 100 + 151 + 61 + 23 + + + + + + + 100 + 221 + 69 + 22 + + + + + NONE + + + + + SINGLE + + + + + + + 10 + 20 + 71 + 23 + + + + TX Delay + + + + + + 10 + 50 + 71 + 23 + + + + TX Tail + + + + + + 10 + 150 + 71 + 23 + + + + Add RX + + + + + + 10 + 180 + 71 + 23 + + + + Add RX Shift + + + + + + 10 + 220 + 91 + 23 + + + + Bits Recovery + + + + + + 176 + 21 + 71 + 23 + + + + msec + + + + + + 176 + 51 + 71 + 23 + + + + msec + + + + + + 176 + 151 + 71 + 23 + + + + pairs + + + + + + 175 + 181 + 71 + 23 + + + + Hz + + + + + + 100 + 251 + 81 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + + + 10 + 250 + 86 + 23 + + + + FX25 Mode + + + + + + 10 + 80 + 71 + 23 + + + + Frack + + + + + + 176 + 81 + 71 + 23 + + + + secs + + + + + + 100 + 81 + 61 + 23 + + + + + + + 10 + 109 + 71 + 23 + + + + Retries + + + + + + 100 + 110 + 61 + 23 + + + + + + + 10 + 348 + 171 + 20 + + + + Send RSID before UI + + + + + + 10 + 374 + 251 + 20 + + + + Send RSID before SABM/UA + + + + + + 10 + 400 + 171 + 20 + + + + Set Modem from RSID + + + + + + 164 + 348 + 101 + 23 + + + + Send RSID + + + + + + 100 + 310 + 181 + 23 + + + + + + + 10 + 310 + 71 + 23 + + + + DigiCalls + + + + + + 10 + 279 + 86 + 23 + + + + IL2P Mode + + + + + + 100 + 280 + 81 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + il2p Only + + + + + + + + + + Modem 2 + + + + + 0 + 0 + 581 + 601 + + + + false + + + + + 0 + 0 + 570 + 439 + + + + + + 290 + 10 + 281 + 287 + + + + Modem Filters + + + + + 172 + 30 + 61 + 25 + + + + Show + + + + + + 172 + 60 + 61 + 25 + + + + Show + + + + + + 172 + 90 + 60 + 25 + + + + Show + + + + + + 90 + 30 + 61 + 23 + + + + + + + 10 + 30 + 71 + 23 + + + + BPF Width + + + + + + 10 + 60 + 81 + 23 + + + + TXBPF Width + + + + + + 90 + 60 + 61 + 23 + + + + + + + 10 + 90 + 71 + 23 + + + + LPF Width + + + + + + 90 + 90 + 61 + 23 + + + + + + + 90 + 120 + 61 + 23 + + + + + + + 90 + 150 + 61 + 23 + + + + + + + 150 + 204 + 61 + 22 + + + + + None + + + + + 6 dB + + + + + 12 dB + + + + + + + 10 + 180 + 171 + 17 + + + + Default Settings + + + + + + 10 + 232 + 166 + 17 + + + + KISS Optimisations + + + + + + 10 + 256 + 176 + 17 + + + + non-AX25 filter + + + + + + 10 + 120 + 71 + 23 + + + + BPFTaps + + + + + + 10 + 150 + 71 + 23 + + + + LPF Taps + + + + + + 10 + 203 + 141 + 22 + + + + PreEmphasis filter + + + + + + 230 + 207 + 51 + 16 + + + + All + + + + + + + 175 + 545 + 209 + 33 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + 0 + 10 + 281 + 431 + + + + Modem type + + + + + 100 + 181 + 61 + 23 + + + + + + + 100 + 21 + 61 + 23 + + + + + + + 100 + 51 + 61 + 23 + + + + + + + 100 + 151 + 61 + 23 + + + + + + + 100 + 221 + 69 + 22 + + + + + NONE + + + + + SINGLE + + + + + + + 10 + 20 + 71 + 23 + + + + TX Delay + + + + + + 10 + 50 + 71 + 23 + + + + TX Tail + + + + + + 10 + 150 + 71 + 23 + + + + Add RX + + + + + + 10 + 180 + 71 + 23 + + + + Add RX Shift + + + + + + 10 + 220 + 91 + 23 + + + + Bits Recovery + + + + + + 176 + 21 + 71 + 23 + + + + msec + + + + + + 176 + 51 + 71 + 23 + + + + msec + + + + + + 176 + 151 + 71 + 23 + + + + pairs + + + + + + 175 + 181 + 71 + 23 + + + + Hz + + + + + + 100 + 251 + 81 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + + + 10 + 250 + 86 + 23 + + + + FX25 Mode + + + + + + 10 + 80 + 71 + 23 + + + + Frack + + + + + + 176 + 81 + 71 + 23 + + + + secs + + + + + + 100 + 81 + 61 + 23 + + + + + + + 10 + 109 + 71 + 23 + + + + Retries + + + + + + 100 + 110 + 61 + 23 + + + + + + + 164 + 348 + 101 + 23 + + + + Send RSID + + + + + + 10 + 348 + 171 + 20 + + + + Send RSID before UI + + + + + + 10 + 400 + 171 + 20 + + + + Set Modem from RSID + + + + + + 10 + 374 + 251 + 20 + + + + Send RSID before SABM/UA + + + + + + 10 + 310 + 71 + 23 + + + + DigiCalls + + + + + + 100 + 310 + 181 + 23 + + + + + + + 10 + 280 + 86 + 23 + + + + IL2P Mode + + + + + + 100 + 281 + 81 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + il2p Only + + + + + + + + + + Modem 3 + + + + + 0 + 0 + 583 + 601 + + + + false + + + + + 0 + 0 + 570 + 581 + + + + + + 290 + 10 + 281 + 287 + + + + Modem Filters + + + + + 172 + 30 + 61 + 25 + + + + Show + + + + + + 172 + 60 + 61 + 25 + + + + Show + + + + + + 172 + 90 + 60 + 25 + + + + Show + + + + + + 90 + 30 + 61 + 23 + + + + + + + 10 + 30 + 71 + 23 + + + + BPF Width + + + + + + 10 + 60 + 81 + 23 + + + + TXBPF Width + + + + + + 90 + 60 + 61 + 23 + + + + + + + 10 + 90 + 71 + 23 + + + + LPF Width + + + + + + 90 + 90 + 61 + 23 + + + + + + + 90 + 120 + 61 + 23 + + + + + + + 90 + 150 + 61 + 23 + + + + + + + 150 + 204 + 61 + 22 + + + + + None + + + + + 6 dB + + + + + 12 dB + + + + + + + 10 + 180 + 171 + 17 + + + + Default Settings + + + + + + 10 + 232 + 166 + 17 + + + + KISS Optimisations + + + + + + 10 + 256 + 176 + 17 + + + + non-AX25 filter + + + + + + 10 + 120 + 71 + 23 + + + + BPFTaps + + + + + + 10 + 150 + 71 + 23 + + + + LPF Taps + + + + + + 10 + 203 + 141 + 22 + + + + PreEmphasis filter + + + + + + 230 + 207 + 51 + 16 + + + + All + + + + + + + 175 + 545 + 209 + 33 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + 0 + 10 + 281 + 441 + + + + Modem type + + + + + 100 + 181 + 61 + 23 + + + + + + + 100 + 21 + 61 + 23 + + + + + + + 100 + 51 + 61 + 23 + + + + + + + 100 + 151 + 61 + 23 + + + + + + + 100 + 221 + 69 + 22 + + + + + NONE + + + + + SINGLE + + + + + + + 10 + 20 + 71 + 23 + + + + TX Delay + + + + + + 10 + 50 + 71 + 23 + + + + TX Tail + + + + + + 10 + 150 + 71 + 23 + + + + Add RX + + + + + + 10 + 180 + 71 + 23 + + + + Add RX Shift + + + + + + 10 + 220 + 91 + 23 + + + + Bits Recovery + + + + + + 176 + 21 + 71 + 23 + + + + msec + + + + + + 176 + 51 + 71 + 23 + + + + msec + + + + + + 176 + 151 + 71 + 23 + + + + pairs + + + + + + 175 + 181 + 71 + 23 + + + + Hz + + + + + + 100 + 251 + 81 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + + + 10 + 250 + 86 + 23 + + + + FX25 Mode + + + + + + 10 + 80 + 71 + 23 + + + + Frack + + + + + + 176 + 81 + 71 + 23 + + + + secs + + + + + + 100 + 81 + 61 + 23 + + + + + + + 10 + 109 + 71 + 23 + + + + Retries + + + + + + 100 + 110 + 61 + 23 + + + + + + + 10 + 374 + 251 + 20 + + + + Send RSID before SABM/UA + + + + + + 10 + 400 + 171 + 20 + + + + Set Modem from RSID + + + + + + 10 + 348 + 171 + 20 + + + + Send RSID before UI + + + + + + 164 + 348 + 101 + 23 + + + + Send RSID + + + + + + 10 + 310 + 71 + 23 + + + + DigiCalls + + + + + + 100 + 310 + 181 + 23 + + + + + + + + 100 + 291 + 81 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + il2p Only + + + + + + + 10 + 290 + 86 + 23 + + + + IL2P Mode + + + + + + + + Modem 4 + + + + + 0 + 0 + 583 + 601 + + + + false + + + + + 0 + 0 + 570 + 581 + + + + + + 290 + 10 + 281 + 287 + + + + Modem Filters + + + + + 172 + 30 + 61 + 25 + + + + Show + + + + + + 172 + 60 + 61 + 25 + + + + Show + + + + + + 172 + 90 + 60 + 25 + + + + Show + + + + + + 90 + 30 + 61 + 23 + + + + + + + 10 + 30 + 71 + 23 + + + + BPF Width + + + + + + 10 + 60 + 81 + 23 + + + + TXBPF Width + + + + + + 90 + 60 + 61 + 23 + + + + + + + 10 + 90 + 71 + 23 + + + + LPF Width + + + + + + 90 + 90 + 61 + 23 + + + + + + + 90 + 120 + 61 + 23 + + + + + + + 90 + 150 + 61 + 23 + + + + + + + 150 + 204 + 61 + 22 + + + + + None + + + + + 6 dB + + + + + 12 dB + + + + + + + 10 + 180 + 161 + 17 + + + + Default Settings + + + + + + 10 + 232 + 166 + 17 + + + + KISS Optimisations + + + + + + 10 + 256 + 176 + 17 + + + + non-AX25 filter + + + + + + 10 + 120 + 71 + 23 + + + + BPFTaps + + + + + + 10 + 150 + 71 + 23 + + + + LPF Taps + + + + + + 10 + 203 + 141 + 22 + + + + PreEmphasis filter + + + + + + 230 + 207 + 70 + 16 + + + + All + + + + + + + 175 + 545 + 209 + 33 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + 0 + 10 + 281 + 431 + + + + Modem type + + + + + 100 + 181 + 61 + 23 + + + + + + + 100 + 21 + 61 + 23 + + + + + + + 100 + 51 + 61 + 23 + + + + + + + 100 + 151 + 61 + 23 + + + + + + + 100 + 221 + 69 + 22 + + + + + NONE + + + + + SINGLE + + + + + + + 10 + 20 + 71 + 23 + + + + TX Delay + + + + + + 10 + 50 + 71 + 23 + + + + TX Tail + + + + + + 10 + 150 + 71 + 23 + + + + Add RX + + + + + + 10 + 180 + 71 + 23 + + + + Add RX Shift + + + + + + 10 + 220 + 91 + 23 + + + + Bits Recovery + + + + + + 176 + 21 + 71 + 23 + + + + msec + + + + + + 176 + 51 + 71 + 23 + + + + msec + + + + + + 176 + 151 + 71 + 23 + + + + pairs + + + + + + 175 + 181 + 71 + 23 + + + + Hz + + + + + + 100 + 251 + 81 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + + + 10 + 250 + 86 + 23 + + + + FX25 Mode + + + + + + 10 + 80 + 71 + 23 + + + + Frack + + + + + + 176 + 81 + 71 + 23 + + + + secs + + + + + + 100 + 81 + 61 + 23 + + + + + + + 10 + 109 + 71 + 23 + + + + Retries + + + + + + 100 + 110 + 61 + 23 + + + + + + + 10 + 374 + 251 + 20 + + + + Send RSID before SABM/UA + + + + + + 10 + 400 + 171 + 20 + + + + Set Modem from RSID + + + + + + 10 + 348 + 171 + 20 + + + + Send RSID before UI + + + + + + 164 + 348 + 101 + 23 + + + + Send RSID + + + + + + 10 + 310 + 71 + 23 + + + + DigiCalls + + + + + + 100 + 310 + 181 + 23 + + + + + + + + 100 + 291 + 81 + 22 + + + + + None + + + + + RX Only + + + + + RX+TX + + + + + il2p Only + + + + + + + 10 + 290 + 86 + 23 + + + + IL2P Mode + + + + + + + + + + 127 + 550 + 276 + 33 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Save + + + + + + + Cancel + + + + + + + + + 80 + 520 + 71 + 20 + + + + CWID Call + + + + + + 150 + 520 + 51 + 20 + + + + 20 + + + + + + 340 + 520 + 61 + 20 + + + + FSK + + + + + + 390 + 520 + 101 + 20 + + + + Tone On/Off + + + + + + 220 + 520 + 61 + 20 + + + + Interval + + + + + + 270 + 520 + 31 + 20 + + + + + + + diff --git a/Modulate.c b/Modulate.c new file mode 100644 index 0000000..5eb8522 --- /dev/null +++ b/Modulate.c @@ -0,0 +1,1066 @@ +// Sample Creation routines (encode and filter) for ARDOP Modem + +#include "ARDOPC.h" +#include + +#define ARDOPBufferSize 12000 * 100 + +extern short ARDOPTXBuffer[4][12000 * 100]; // Enough to hold whole frame of samples + +extern int ARDOPTXLen[4]; // Length of frame +extern int ARDOPTXPtr[4]; // Tx Pointer + +extern int intSessionBW; // Negotiated speed + +#pragma warning(disable : 4244) // Code does lots of float to int + +FILE * fp1; + +float dblQAMCarRatio = 1.0f / 1.765f; //Optimum for 8,8 circular constellation with no phase offset: (Dmin inner to inner = Dmin inner to outer) + +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +// Function to generate the Two-tone leader and Frame Sync (used in all frame types) + +extern short Dummy; + +int intSoftClipCnt = 0; +BOOL SendingHeader200 = 0; // Set when sending header in 200 Hz Modes + +void ARDOPFlush(); + +int intBW; // Requested connect speed +int intSessionBW; // Negotiated speed +UCHAR bytLastReceivedDataFrameType; +UCHAR bytLastARQSessionID; +int blnPending = 0; +int intSessionBW = 500; + +void ARDOPSampleSink(short Sample); +extern CONST short int50BaudTwoToneLeaderTemplate[240]; // holds just 1 symbol (20 ms) of the leader + + +extern int TrailerLength; + +void AddTrailer(int Chan) +{ + int intAddedSymbols = 1 + TrailerLength / 10; // add 1 symbol + 1 per each 10 ms of MCB.Trailer + int i, k; + + for (i = 1; i <= intAddedSymbols; i++) + { + for (k = 0; k < 120; k++) + { + ARDOPSampleSink(intQAM50bdCarTemplate[5][0][k % 60]); + } + } +} + +extern int Number; + +void ARDOPFlush(int Chan) +{ + AddTrailer(Chan); // add the trailer. + ARDOPTXPtr[Chan] = 0; + ARDOPTXLen[Chan] = Number; +} + +// Function to soft clip combined waveforms. +int SoftClip(int intInput) +{ + if (intInput > 30000) // soft clip above/below 30000 + { + intInput = 30000; //min(32700, 30000 + 20 * sqrt(intInput - 30000)); + intSoftClipCnt += 1; + } + else if(intInput < -30000) + { + intInput = -30000; //max(-32700, -30000 - 20 * sqrt(-(intInput + 30000))); + intSoftClipCnt += 1; + } + + if (intInput == 0) + intInput = 0; + + + return intInput; +} + + +void GetTwoToneLeaderWithSync(int intSymLen) +{ + // Generate a 50 baud (20 ms symbol time) 2 tone leader + // leader tones used are 1475 and 1525 Hz. + + int intSign = 1; + int i, j; + short intSample; + + if ((intSymLen & 1) == 1) + intSign = -1; + + for (i = 0; i < intSymLen; i++) //for the number of symbols needed (two symbols less than total leader length) + { + for (j = 0; j < 240; j++) // for 240 samples per symbol (50 baud) + { + if (i != (intSymLen - 1)) + intSample = intSign * int50BaudTwoToneLeaderTemplate[j]; + else + intSample = -intSign * int50BaudTwoToneLeaderTemplate[j]; + + ARDOPSampleSink(intSample); + } + intSign = -intSign; + } +} + +void SendLeaderAndSYNC(UCHAR * bytEncodedBytes, int intLeaderLen) +{ + int intLeaderLenMS; + int j, k, n; + UCHAR bytMask; + UCHAR bytSymToSend; + short intSample; + if (intLeaderLen == 0) + intLeaderLenMS = LeaderLength; + else + intLeaderLenMS = intLeaderLen; + + // Create the leader + + GetTwoToneLeaderWithSync(intLeaderLenMS / 20); + + //Create the 8 symbols (16 bit) 50 baud 4FSK frame type with Implied SessionID + + for(j = 0; j < 2; j++) // for the 2 bytes of the frame type + { + bytMask = 0x30; + + for(k = 0; k < 4; k++) // for 4 symbols per byte (3 data + 1 parity) + { + if (k < 3) + bytSymToSend = (bytMask & bytEncodedBytes[j]) >> (2 * (2 - k)); + else + bytSymToSend = ComputeTypeParity(bytEncodedBytes[j]); + + for(n = 0; n < 240; n++) + { +// if ( ARQBandwidth == XB2500) // 2500 Hz +// { +// if (bytSymToSend < 2) +// intSample = (0.62 * intFSK50bdCarTemplate[4 + bytSymToSend][n]) + (0.62 * intFSK50bdCarTemplate[bytSymToSend][n]); // 4 is offset to center tones 1350, 1450, 1550, 1650Hz +// else +// intSample = (0.62 * intFSK50bdCarTemplate[4 + bytSymToSend][n]) + (0.62 * intFSK50bdCarTemplate[8 + bytSymToSend][n]); // 4 is offset to center tones 1350, 1450, 1550, 1650Hz, 8 is offset to tones 1800 and 1900 +// } +// else + intSample = intFSK50bdCarTemplate[bytSymToSend + 4][n]; + + ARDOPSampleSink(intSample); + } + bytMask = bytMask >> 2; + } + } +} + +void Mod4FSKDataAndPlay(unsigned char * bytEncodedBytes, int Len, int intLeaderLen, int Chan) +{ + // Function to Modulate data encoded for 4FSK, create + // the 16 bit samples and send to sound interface + + // Function works for 1, 2 or 4 simultaneous carriers + + int intNumCar, intBaud, intDataLen, intRSLen, intDataPtr, intSampPerSym, intDataBytesPerCar; + BOOL blnOdd; + + int intSample; + + char strType[18] = ""; + char strMod[16] = ""; + + UCHAR bytSymToSend, bytMask, bytMinQualThresh; + + float dblCarScalingFactor; + int intLeaderLenMS; + int k, m, n; + UCHAR Type = bytEncodedBytes[0]; + + if (!FrameInfo(Type, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytMinQualThresh, strType)) + return; + + if (strcmp(strMod, "4FSK") != 0) + return; + + Debugprintf("Sending Frame Type %s", strType); + DrawTXFrame(strType); + + if (bytEncodedBytes[0] == PktFrameHeader) + { + // Leader is 4FSK which needs 500 filter + + if (pktBW[pktMode] < 1000) + initFilter(500, 1500, Chan); + else + initFilter(2500, 1500, Chan); + } + else + { + if (ARQBandwidth == XB200) + initFilter(200, 1500, Chan); + else if (intNumCar == 1) + initFilter(500, 1500, Chan); + else + initFilter(2500, 1500, Chan); + } + + if (intLeaderLen == 0) + intLeaderLenMS = LeaderLength; + else + intLeaderLenMS = intLeaderLen; + + switch (intBaud) + { + case 50: + + intSampPerSym = 240; + break; + + case 100: + + intSampPerSym = 120; + } + + intDataBytesPerCar = (Len - 2) / intNumCar; // We queue the samples here, so dont copy below + + SendLeaderAndSYNC(bytEncodedBytes, intLeaderLen); + + intDataPtr = 2; + +Reenter: + + switch (intNumCar) + { + case 1: // use carriers 0-3 + + dblCarScalingFactor = 1.0; // (scaling factors determined emperically to minimize crest factor) + + for (m = 0; m < intDataBytesPerCar; m++) // For each byte of input data + { + bytMask = 0xC0; // Initialize mask each new data byte + + for (k = 0; k < 4; k++) // for 4 symbol values per byte of data + { + bytSymToSend = (bytMask & bytEncodedBytes[intDataPtr]) >> (2 * (3 - k)); // Values 0-3 + + for (n = 0; n < intSampPerSym; n++) // Sum for all the samples of a symbols + { + if (intBaud == 50) + { + if (intSessionBW == 200 && (bytSymToSend == 0 || bytSymToSend == 3)) + // This scales down the two outer tones in 200 Hz mode to restrict bandwidth slightly + intSample = 0.7f * intFSK50bdCarTemplate[4 + bytSymToSend][n]; //4 is offset to center tones 1350, 1450, 1550, 1650Hz + else + intSample = intFSK50bdCarTemplate[4 + bytSymToSend][n]; //4 is offset to center tones 1350, 1450, 1550, 1650Hz + } + else if (intBaud == 100) // Used for OFDMACK + { + intSample = intFSK100bdCarTemplate[bytSymToSend][n]; + } + ARDOPSampleSink(intSample); + } + + bytMask = bytMask >> 2; + } + intDataPtr += 1; + } + + if (Type == PktFrameHeader) + { + + // just sent packet header. Send rest in current mode + // Assumes we are using 4FSK for Packet Header + + bytEncodedBytes[0] = Type = PktFrameData; // Prevent reentry + + strcpy(strMod, &pktMod[pktMode][0]); + intDataBytesPerCar = pktDataLen + pktRSLen + 3; + intDataPtr = 11; // Over Header + intNumCar = pktCarriers[pktMode]; + + // This assumes Packet Data is sent as PSK/QAM + + switch (intNumCar) + { + case 1: + // intCarStartIndex = 4; + dblCarScalingFactor = 1.0f; // Starting at 1500 Hz (scaling factors determined emperically to minimize crest factor) TODO: needs verification + break; + case 2: + // intCarStartIndex = 3; + dblCarScalingFactor = 0.53f; // Starting at 1400 Hz + break; + case 4: + // intCarStartIndex = 2; + dblCarScalingFactor = 0.29f; // Starting at 1200 Hz + break; + case 8: + // intCarStartIndex = 0; + dblCarScalingFactor = 0.17f; // Starting at 800 Hz + } + + // Reenter to send rest of variable length packet frame + + if (pktFSK[pktMode]) + goto Reenter; + else + ModPSKDataAndPlay(bytEncodedBytes, 0, 0, Chan); + return; + } + + ARDOPFlush(Chan); + + break; + + case 2: // use carriers 0-3 and 8-11 (50 baud only) + + dblCarScalingFactor = 0.6f; // (scaling factors determined emperically to minimize crest factor) + + for (m = 0; m < intDataBytesPerCar; m++) // For each byte of input data + { + bytMask = 0xC0; // Initialize mask each new data byte + + for (k = 0; k < 4; k++) // for 4 symbol values per byte of data + { + for (n = 0; n < intSampPerSym; n++) // for all the samples of a symbol for 2 carriers + { + //' First carrier + + bytSymToSend = (bytMask & bytEncodedBytes[intDataPtr]) >> (2 * (3 - k)); // Values 0-3 + intSample = intFSK50bdCarTemplate[bytSymToSend][n]; + // Second carrier + + bytSymToSend = (bytMask & bytEncodedBytes[intDataPtr + intDataBytesPerCar]) >> (2 * (3 - k)); // Values 0-3 + intSample = SoftClip(dblCarScalingFactor * (intSample + intFSK50bdCarTemplate[8 + bytSymToSend][n])); + + ARDOPSampleSink(intSample); + } + bytMask = bytMask >> 2; + } + intDataPtr += 1; + } + + ARDOPFlush(Chan); + + break; + } +} + +// Function to extract an 8PSK symbol from an encoded data array + + +UCHAR GetSym8PSK(int intDataPtr, int k, int intCar, UCHAR * bytEncodedBytes, int intDataBytesPerCar) +{ + int int3Bytes = bytEncodedBytes[intDataPtr + intCar * intDataBytesPerCar]; +// int intMask = 7; + int intSym; + UCHAR bytSym; + + int3Bytes = int3Bytes << 8; + int3Bytes += bytEncodedBytes[intDataPtr + intCar * intDataBytesPerCar + 1]; + int3Bytes = int3Bytes << 8; + int3Bytes += bytEncodedBytes[intDataPtr + intCar * intDataBytesPerCar + 2]; // now have 3 bytes, 24 bits or 8 8PSK symbols +// intMask = intMask << (3 * (7 - k)); + intSym = int3Bytes >> (3 * (7 - k)); + bytSym = intSym & 7; //(intMask && int3Bytes) >> (3 * (7 - k)); + + return bytSym; +} + + + +// Function to Modulate data encoded for PSK and 16QAM, create +// the 16 bit samples and send to sound interface + + +void ModPSKDataAndPlay(unsigned char * bytEncodedBytes, int Len, int intLeaderLen, int Chan) +{ + int intNumCar, intBaud, intDataLen, intRSLen, intDataPtr, intSampPerSym, intDataBytesPerCar; + BOOL blnOdd; + int Type = bytEncodedBytes[0]; + + int intSample; + char strType[18] = ""; + char strMod[16] = ""; + UCHAR bytSym, bytSymToSend, bytMinQualThresh; + float dblCarScalingFactor; + int intLeaderLenMS; + int i, j, k, l = 4, n; + int intCarStartIndex; + int intPeakAmp; + int intCarIndex; + BOOL QAM = 0; + + UCHAR bytLastSym[43]; // = {0}; // Holds the last symbol sent (per carrier). bytLastSym(4) is 1500 Hz carrier (only used on 1 carrier modes) + + if (!FrameInfo(Type, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytMinQualThresh, strType)) + return; + + intDataBytesPerCar = (Len - 2) / intNumCar; // We queue the samples here, so dont copy below + + // These new scaling factor combined with soft clipping to provide near optimum scaling Jan 6, 2018 + // The Test form was changed to calculate the Peak power to RMS power (PAPR) of the test waveform and count the number of "soft clips" out of ~ 50,000 samples. + // These values arrived at emperically using the Test form (Quick brown fox message) to minimize PAPR at a minor decrease in maximum constellation quality + + if (strstr(strMod, "16QAM")) + { + // QAM Modes + + QAM = 1; + l = 2; // 2 symbols per byte + + switch (intNumCar) + { + case 1: + intCarStartIndex = 5; + dblCarScalingFactor = 2.0f; // Starting at 1500 Hz Selected to give < 13% clipped values yielding a PAPR = 1.6 Constellation Quality >98 + break; + case 2: + intCarStartIndex = 4; + dblCarScalingFactor = 1.0f; + break; + case 10: + intCarStartIndex = 0; + dblCarScalingFactor = 0.4f; + } + } + else // 4PSK + { + switch (intNumCar) + { + case 1: + intCarStartIndex = 5; + dblCarScalingFactor = 2.0f; // Starting at 1500 Hz Selected to give < 13% clipped values yielding a PAPR = 1.6 Constellation Quality >98 + break; + case 2: + intCarStartIndex = 4; + dblCarScalingFactor = 1.2f; + break; + case 10: + intCarStartIndex = 0; + dblCarScalingFactor = 0.35f; + } + } + + if (intBaud == 50) + intSampPerSym = 240; + else + intSampPerSym = 120; + + if (Type == PktFrameData) + { + intDataBytesPerCar = pktDataLen + pktRSLen + 3; + intDataPtr = 11; // Over Header + goto PktLoopBack; + } + + Debugprintf("Sending Frame Type %s", strType); + DrawTXFrame(strType); + + if (intNumCar == 1) + initFilter(200, 1500, Chan); + else if (intNumCar == 2 || intNumCar == 9) + initFilter(500, 1500, Chan); + else + initFilter(2500, 1500, Chan); + + if (intLeaderLen == 0) + intLeaderLenMS = LeaderLength; + else + intLeaderLenMS = intLeaderLen; + + intSoftClipCnt = 0; + + // Create the leader + + SendLeaderAndSYNC(bytEncodedBytes, intLeaderLen); + SendingHeader200 = FALSE; + + intPeakAmp = 0; + + intDataPtr = 2; // initialize pointer to start of data. + +PktLoopBack: // Reenter here to send rest of variable length packet frame + + + // Now create a reference symbol for each carrier + + // We have to do each carrier for each sample, as we write + // the sample immediately + + for (n = 0; n < intSampPerSym; n++) // Sum for all the samples of a symbols + { + intSample = 0; + intCarIndex = intCarStartIndex; // initialize to correct starting carrier + + for (i = 0; i < intNumCar; i++) // across all carriers + { + bytSymToSend = 0; // using non 0 causes error on first data byte 12/8/2014 ...Values 0-3 not important (carries no data). (Possible chance for Crest Factor reduction?) + bytLastSym[intCarIndex] = 0; + + + intSample += intQAM50bdCarTemplate[intCarIndex][0][n % 120]; + + intCarIndex++; + if (intCarIndex == 5) + intCarIndex = 6; // skip over 1500 Hz for multi carrier modes (multi carrier modes all use even hundred Hz tones) + } + intSample = SoftClip(intSample * 0.5 * dblCarScalingFactor); + ARDOPSampleSink(intSample); + } + + // End of reference phase generation + + // Unlike ARDOP_WIN we send samples as they are created, + // so we loop through carriers, then data bytes + + for (j = 0; j < intDataBytesPerCar; j++) // for each referance and data symbol + { + // Loop through each symbol of byte (4 for PSK 2 for QAM + + for (k = 0; k < l; k++) + { + for (n = 0; n < intSampPerSym; n++) + { + intSample = 0; + intCarIndex = intCarStartIndex; // initialize the carrrier index + + for (i = 0; i < intNumCar; i++) // across all active carriers + { + if (QAM == 0) + { + bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> (2 * (3 - k))) & 3; + bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 3); // Values 0-3 + + if (bytSymToSend < 2) // This uses the symmetry of the symbols to reduce the table size by a factor of 2 + intSample += intQAM50bdCarTemplate[intCarIndex][2 * bytSymToSend][n % 120]; // double the symbol value during template lookup for 4PSK. (skips over odd PSK 8 symbols) + else if (bytSymToSend < 4) + intSample -= intQAM50bdCarTemplate[intCarIndex][2 * (bytSymToSend - 2)][n % 120]; // subtract 2 from the symbol value before doubling and subtract value of table + + } + else + { + // For 16QAM the angle is sent differential but the amplitude is sent as is for the symbol...verified 4/20 2018 + + bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> (4 * (1 - k))) & 15; + bytSymToSend = ((bytLastSym[intCarIndex] & 7) + (bytSym & 7) & 7); // Compute the differential phase to send + bytSymToSend = bytSymToSend | (bytSym & 8); // add in the amplitude bit directly from symbol + + // 4bits/symbol (use table symbol values 0, 1, 2, 3, -0, -1, -2, -3) and modulate amplitude with MSB + + if (bytSymToSend < 4)// This uses the symmetry of the symbols to reduce the table size by a factor of 2 + intSample += intQAM50bdCarTemplate[intCarIndex][bytSymToSend][n % 120]; // double the symbol value during template lookup for 4PSK. (skips over odd PSK 8 symbols) + else if (bytSymToSend < 8) + intSample -= intQAM50bdCarTemplate[intCarIndex][(bytSymToSend - 4)][n % 120]; // subtract 4 from the symbol value before doubling and subtract value of table + else if (bytSymToSend < 12) + intSample += dblQAMCarRatio * intQAM50bdCarTemplate[intCarIndex][bytSymToSend - 8][n % 120]; // subtract 4 from the symbol value before doubling and subtract value of table + else + intSample -= dblQAMCarRatio * intQAM50bdCarTemplate[intCarIndex][bytSymToSend - 12][n % 120]; // subtract 4 from the symbol value before doubling and subtract value of table + } + + if (n == intSampPerSym - 1) // Last sample? + bytLastSym[intCarIndex] = bytSymToSend; + + intCarIndex += 1; + if (intCarIndex == 5) + intCarIndex = 6; // skip over 1500 Hz carrier for multicarrier modes + } + + // Done all carriers - send sample + + intSample = SoftClip(intSample * 0.5 * dblCarScalingFactor); + ARDOPSampleSink(intSample); + } + + // Done all samples for this symbol + // now next symbol of byte + + } + intDataPtr++; + } + + if (Type == PktFrameHeader) + { + // just sent packet header. Send rest in current mode + + Type = 0; // Prevent reentry + + strcpy(strMod, &pktMod[pktMode][0]); + intDataBytesPerCar = pktDataLen + pktRSLen + 3; + intDataPtr = 11; // Over Header + intNumCar = pktCarriers[pktMode]; + + switch (intNumCar) + { + case 1: + intCarStartIndex = 4; + // dblCarScalingFactor = 1.0f; // Starting at 1500 Hz (scaling factors determined emperically to minimize crest factor) TODO: needs verification + dblCarScalingFactor = 1.2f; // Starting at 1500 Hz Selected to give < 13% clipped values yielding a PAPR = 1.6 Constellation Quality >98 + case 2: + intCarStartIndex = 3; + // dblCarScalingFactor = 0.53f; + if (strcmp(strMod, "16QAM") == 0) + dblCarScalingFactor = 0.67f; // Carriers at 1400 and 1600 Selected to give < 2.5% clipped values yielding a PAPR = 2.17, Constellation Quality >92 + else + dblCarScalingFactor = 0.65f; // Carriers at 1400 and 1600 Selected to give < 4% clipped values yielding a PAPR = 2.0, Constellation Quality >95 + break; + case 4: + intCarStartIndex = 2; + // dblCarScalingFactor = 0.29f; // Starting at 1200 Hz + dblCarScalingFactor = 0.4f; // Starting at 1200 Hz Selected to give < 3% clipped values yielding a PAPR = 2.26, Constellation Quality >95 + break; + case 8: + intCarStartIndex = 0; + // dblCarScalingFactor = 0.17f; // Starting at 800 Hz + if (strcmp(strMod, "16QAM") == 0) + dblCarScalingFactor = 0.27f; // Starting at 800 Hz Selected to give < 1% clipped values yielding a PAPR = 2.64, Constellation Quality >94 + else + dblCarScalingFactor = 0.25f; // Starting at 800 Hz Selected to give < 2% clipped values yielding a PAPR = 2.5, Constellation Quality >95 + } + goto PktLoopBack; // Reenter to send rest of variable length packet frame + } + + ARDOPFlush(Chan); + + if (intSoftClipCnt > 0) + Debugprintf("Soft Clips %d ", intSoftClipCnt); + +} + + + +// Resends the last frame + +void RemodulateLastFrame(int Chan) +{ + int intNumCar, intBaud, intDataLen, intRSLen; + UCHAR bytMinQualThresh; + BOOL blnOdd; + + char strType[18] = ""; + char strMod[16] = ""; + + if (!FrameInfo(bytEncodedBytes[0], &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytMinQualThresh, strType)) + return; + + if (strcmp(strMod, "4FSK") == 0) + { + Mod4FSKDataAndPlay(bytEncodedBytes, EncLen, intCalcLeader, Chan); // Modulate Data frame + return; + } + + if (strcmp(strMod, "OFDM") == 0) + { + int save = OFDMMode; + OFDMMode = LastSentOFDMMode; + + ModOFDMDataAndPlay(bytEncodedBytes, EncLen, intCalcLeader, Chan); // Modulate Data frame + + OFDMMode = save; + + return; + } + + ModPSKDataAndPlay(bytEncodedBytes, EncLen, intCalcLeader, Chan); // Modulate Data frame +} + +// Filter State Variables + +static float dblR = (float)0.9995f; // insures stability (must be < 1.0) (Value .9995 7/8/2013 gives good results) +static int intN = 120; //Length of filter 12000/100 +static float dblRn; + +static float dblR2; +static float dblCoef[34] = {0.0f}; // the coefficients +float dblZin = 0, dblZin_1 = 0, dblZin_2 = 0, dblZComb= 0; // Used in the comb generator + +// The resonators + +float dblZout_0[34] = {0.0f}; // resonator outputs +float dblZout_1[34] = {0.0f}; // resonator outputs delayed one sample +float dblZout_2[34] = {0.0f}; // resonator outputs delayed two samples + +int fWidth; // Filter BandWidth +int SampleNo; +int outCount = 0; +int first, last; // Filter slots +int centreSlot; + +float largest = 0; +float smallest = 0; + +short Last120[256]; // Now need 240 for 200 Hz filter + +int Last120Get = 0; +int Last120Put = 120; + +extern int Number; // Number waiting to be sent + +UCHAR bytPendingSessionID; +UCHAR bytSessionID = 0x3f; +BOOL blnARQConnected; + +extern unsigned short buffer[2][1200]; + +unsigned short * DMABuffer; + +unsigned short * SendtoCard(unsigned short * buf, int n); +unsigned short * SoundInit(); + +// initFilter is called to set up each packet. It selects filter width + +void initFilter(int Width, int Centre, int Chan) +{ + int i, j; + fWidth = Width; + centreSlot = Centre / 100; + largest = smallest = 0; + SampleNo = 0; + Number = 0; + outCount = 0; + memset(Last120, 0, 256); + + DMABuffer = &ARDOPTXBuffer[Chan][0]; + +// KeyPTT(TRUE); + SoundIsPlaying = TRUE; +// StopCapture(); + + Last120Get = 0; + Last120Put = 120; + + dblRn = powf(dblR, intN); + dblR2 = powf(dblR, 2); + + dblZin_2 = dblZin_1 = 0; + + switch (fWidth) + { + case 200: + + // Used for PSK 200 Hz modulation XMIT filter + // implements 5 50 Hz wide sections centered on 1500 Hz (~200 Hz wide @ - 30dB centered on 1500 Hz) + + SendingHeader200 = TRUE; + intN = 240; + Last120Put = 240; + centreSlot = Centre / 50; + first = centreSlot - 3; + last = centreSlot + 3; // 7 filter sections + break; + + case 500: + + // implements 7 100 Hz wide sections centered on 1500 Hz (~500 Hz wide @ - 30dB centered on 1500 Hz) + + intN = 120; + first = centreSlot - 3; + last = centreSlot + 3; // 7 filter sections + break; + + case 2500: + + // implements 26 100 Hz wide sections centered on 1500 Hz (~2000 Hz wide @ - 30dB centered on 1500 Hz) + + intN = 120; + first = centreSlot - 13; + last = centreSlot + 13; // 27 filter sections + break; + + default: + + Debugprintf("Invalid Filter Width %d", fWidth); + } + + + for (j = first; j <= last; j++) + { + dblZout_0[j] = 0; + dblZout_1[j] = 0; + dblZout_2[j] = 0; + } + + // Initialise the coefficients + +// if (dblCoef[last] == 0.0) + { + for (i = first; i <= last; i++) + { + double x = 2 * M_PI * i / intN; + x = cosf(1); + + dblCoef[i] = 2.0 * dblR * cosf(2 * M_PI * i / intN); // For Frequency = bin i + } + } + } + + +void ARDOPSampleSink(short Sample) +{ + // Filter and send to sound interface + + // This version is passed samples one at a time, as we don't have + // enough RAM in embedded systems to hold a full audio frame + + int intFilLen = intN / 2; + int j; + float intFilteredSample = 0; // Filtered sample + + // We save the previous intN samples + // The samples are held in a cyclic buffer + + if (SampleNo < intN) + dblZin = Sample; + else + dblZin = Sample - dblRn * Last120[Last120Get]; + + if (++Last120Get == (intN + 1)) + Last120Get = 0; + + // Compute the Comb + + dblZComb = dblZin - dblZin_2 * dblR2; + dblZin_2 = dblZin_1; + dblZin_1 = dblZin; + + // Now the resonators + + for (j = first; j <= last; j++) + { + dblZout_0[j] = dblZComb + dblCoef[j] * dblZout_1[j] - dblR2 * dblZout_2[j]; + + if (dblZout_0[j] != dblZout_0[j]) + j = j; + + dblZout_2[j] = dblZout_1[j]; + dblZout_1[j] = dblZout_0[j]; + + switch (fWidth) + { + case 200: + + // scale each by transition coeff and + (Even) or - (Odd) + + if (SampleNo >= intFilLen) + { + if (j == first || j == last) + { + if (SendingHeader200) + intFilteredSample -= dblZout_0[j]; // This provides no attenuation to the Frame Type tones at 1350 and 1650 + else + intFilteredSample -= 0.1 * dblZout_0[j]; // This smaller value required to filter down to 200 Hz bandwidth + } + else if ((j & 1) == 0) + intFilteredSample += (int)dblZout_0[j]; + else + intFilteredSample -= (int)dblZout_0[j]; + } + + break; + + case 500: + + // scale each by transition coeff and + (Even) or - (Odd) + // Resonators 12 and 18 scaled to get best shape and side lobe supression to - 45 dB while keeping BW at 500 Hz @ -26 dB + // practical range of scaling .05 to .25 + // Scaling also accomodates for the filter "gain" of approx 60. + + if (SampleNo >= intFilLen) + { + if (j == first || j == last) + intFilteredSample += 0.389f * dblZout_0[j]; + else if ((j & 1) == 0) + intFilteredSample += (int)dblZout_0[j]; + else + intFilteredSample -= (int)dblZout_0[j]; + } + + break; + + case 2500: + + // scale each by transition coeff and + (Even) or - (Odd) + // Resonators 2 and 28 scaled to get best shape and side lobe supression to - 45 dB while keeping BW at 500 Hz @ -26 dB + // practical range of scaling .05 to .25 + // Scaling also accomodates for the filter "gain" of approx 60. + + if (SampleNo >= intFilLen) + { + if (j == first || j == last) + intFilteredSample += 0.3891f * dblZout_0[j]; + else if ((j & 1) == 0) // Even + intFilteredSample += (int)dblZout_0[j]; + else + intFilteredSample -= (int)dblZout_0[j]; + } + } + } + + if (SampleNo >= intFilLen) + { + intFilteredSample = intFilteredSample * 0.00833333333f; // rescales for gain of filter + largest = max(largest, intFilteredSample); + smallest = min(smallest, intFilteredSample); + + if (intFilteredSample > 32700) // Hard clip above 32700 + intFilteredSample = 32700; + else if (intFilteredSample < -32700) + intFilteredSample = -32700; + +#ifdef TEENSY + int work = (short)(intFilteredSample); + DMABuffer[Number++] = (work + 32768) >> 4; // 12 bit left justify +#else + DMABuffer[Number++] = (short)intFilteredSample; +#endif + if (Number == ARDOPBufferSize) + { + // send this buffer to sound interface (shouldn't happen) + + DMABuffer = SendtoCard(DMABuffer, SendSize); + Number = 0; + } + } + + Last120[Last120Put++] = Sample; + + if (Last120Put == (intN + 1)) + Last120Put = 0; + + SampleNo++; +} + + + +extern int dttTimeoutTrip; + +extern UCHAR bytSessionID; + + +// Subroutine to make a CW ID Wave File + +void sendCWID(char * strID, BOOL CWOnOff, int Chan) +{ + // This generates a phase synchronous FSK MORSE keying of strID + // FSK used to maintain VOX on some sound cards + // Sent at 90% of max ampllitude + + char strAlphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/"; + + //Look up table for strAlphabet...each bit represents one dot time, 3 adjacent dots = 1 dash + // one dot spacing between dots or dashes + + int intCW[] = {0x17, 0x1D5, 0x75D, 0x75, 0x1, 0x15D, + 0x1DD, 0x55, 0x5, 0x1777, 0x1D7, 0x175, + 0x77, 0x1D, 0x777, 0x5DD, 0x1DD7, 0x5D, + 0x15, 0x7, 0x57, 0x157, 0x177, 0x757, + 0x1D77, 0x775, 0x77777, 0x17777, 0x5777, 0x1577, + 0x557, 0x155, 0x755, 0x1DD5, 0x7775, 0x1DDDD, 0x1D57, 0x1D57}; + + + float dblHiPhaseInc = 2 * M_PI * 1609.375f / 12000; // 1609.375 Hz High tone + float dblLoPhaseInc = 2 * M_PI * 1390.625f / 12000; // 1390.625 low tone + float dblHiPhase = 0; + float dblLoPhase = 0; + int intDotSampCnt = 768; // about 12 WPM or so (should be a multiple of 256 + short intDot[768]; + short intSpace[768]; + int i, j, k; + int intAmp = 26000; // Selected to have some margin in calculations with 16 bit values (< 32767) this must apply to all filters as well. + char * index; + int intMask; + int idoffset; + + strlop(strID, '-'); // Remove any SSID + + // Generate the dot samples (high tone) and space samples (low tone) + + for (i = 0; i < intDotSampCnt; i++) + { + if (CWOnOff) + intSpace[i] = 0; + else + intSpace[i] = sin(dblLoPhase) * 0.9 * intAmp; + + intDot[i] = sin(dblHiPhase) * 0.9 * intAmp; + + dblHiPhase += dblHiPhaseInc; + if (dblHiPhase > 2 * M_PI) + dblHiPhase -= 2 * M_PI; + dblLoPhase += dblLoPhaseInc; + if (dblLoPhase > 2 * M_PI) + dblLoPhase -= 2 * M_PI; + } + + initFilter(500,1500, Chan); + + //Generate leader for VOX 6 dots long + + for (k = 6; k >0; k--) + for (i = 0; i < intDotSampCnt; i++) + ARDOPSampleSink(intSpace[i]); + + for (j = 0; j < strlen(strID); j++) + { + index = strchr(strAlphabet, strID[j]); + if (index) + idoffset = index - &strAlphabet[0]; + else + idoffset = 0; + + intMask = 0x40000000; + + if (index == NULL) + { + // process this as a space adding 6 dots worth of space to the wave file + + for (k = 6; k >0; k--) + for (i = 0; i < intDotSampCnt; i++) + ARDOPSampleSink(intSpace[i]); + } + else + { + while (intMask > 0) // search for the first non 0 bit + if (intMask & intCW[idoffset]) + break; // intMask is pointing to the first non 0 entry + else + intMask >>= 1; // Right shift mask + + while (intMask > 0) // search for the first non 0 bit + { + if (intMask & intCW[idoffset]) + for (i = 0; i < intDotSampCnt; i++) + ARDOPSampleSink(intDot[i]); + else + for (i = 0; i < intDotSampCnt; i++) + ARDOPSampleSink(intSpace[i]); + + intMask >>= 1; // Right shift mask + } + } + // add 2 dot spaces for inter letter spacing + for (k = 4; k >0; k--) + for (i = 0; i < intDotSampCnt; i++) + ARDOPSampleSink(intSpace[i]); + } + + //add 3 spaces for the end tail + +// for (k = 6; k >0; k--) +// for (i = 0; i < intDotSampCnt; i++) +// ARDOPSampleSink(intSpace[i]); + + ARDOPTXPtr[Chan] = 0; + ARDOPTXLen[Chan] = Number; + Number = 0; +} + + diff --git a/QtSoundModem - Copy.cpp b/QtSoundModem - Copy.cpp new file mode 100644 index 0000000..8422ee9 --- /dev/null +++ b/QtSoundModem - Copy.cpp @@ -0,0 +1,2827 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +// UZ7HO Soundmodem Port + +// Not Working 4psk100 FEC + +// Thoughts on Waterfall Display. + +// Original used a 2048 sample FFT giving 5.859375 Hz bins. We plotted 1024 points, giving a 0 to 6000 specrum + +// If we want say 300 to 3300 we need about half the bin size so twice the fft size. But should we also fit required range to window size? + +// Unless we resize the most displayed bit of the screen in around 900 pixels. So each bin should be 3300 / 900 = 3.66667 Hz or a FFT size of around 3273 + +#include "QtSoundModem.h" +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "UZ7HOStuff.h" + + +QImage *Constellation; +QImage *Waterfall[4] = { 0,0,0,0 }; +QImage *Header[4]; +QLabel *DCDLabel[4]; +QLineEdit *chanOffsetLabel[4]; +QImage *DCDLed[4]; + +QImage *RXLevel; + +QLabel *WaterfallCopy[2]; +QLabel *HeaderCopy[2]; + +QTextEdit * monWindowCopy; + +extern workerThread *t; +extern QtSoundModem * w; + +QList Ports = QSerialPortInfo::availablePorts(); + +void saveSettings(); +void getSettings(); +extern "C" void CloseSound(); +extern "C" void GetSoundDevices(); +extern "C" char modes_name[modes_count][20]; +extern "C" int speed[5]; +extern "C" int KISSPort; +extern "C" short rx_freq[5]; + +extern "C" int CaptureCount; +extern "C" int PlaybackCount; + +extern "C" int CaptureIndex; // Card number +extern "C" int PlayBackIndex; + +extern "C" char CaptureNames[16][256]; +extern "C" char PlaybackNames[16][256]; + +extern "C" int SoundMode; +extern "C" int multiCore; + +extern "C" int refreshModems; + +extern "C" int pnt_change[5]; +extern "C" int needRSID[4]; + +extern "C" int needSetOffset[4]; + +extern "C" float MagOut[4096]; +extern "C" float MaxMagOut; +extern "C" int MaxMagIndex; + +extern "C" +{ + int InitSound(BOOL Report); + void soundMain(); + void MainLoop(); + void modulator(UCHAR snd_ch, int buf_size); + void SampleSink(int LR, short Sample); + void doCalib(int Port, int Act); + int Freq_Change(int Chan, int Freq); + void set_speed(int snd_ch, int Modem); + void init_speed(int snd_ch); + void wf_pointer(int snd_ch); + void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); + void dofft(short * in, float * outr, float * outi); + void init_raduga(); + void wf_Scale(int Chan); + void AGW_Report_Modem_Change(int port); + char * strlop(char * buf, char delim); + void sendRSID(int Chan, int dropTX); + void RSIDinitfft(); + void il2p_init(int il2p_debug); +} + +void make_graph_buf(float * buf, short tap, QPainter * bitmap); + +int ModemA = 2; +int ModemB = 2; +int ModemC = 2; +int ModemD = 2; +int FreqA = 1500; +int FreqB = 1500; +int FreqC = 1500; +int FreqD = 1500; +int DCD = 50; + +char CWIDCall[128] = ""; +int CWIDInterval = 0; +int CWIDLeft = 0; +int CWIDRight = 0; +int CWIDType = 1; // on/off + +int WaterfallMin = 0; +int WaterfallMax = 3300; + +extern "C" { int RSID_SABM[4]; } +extern "C" { int RSID_UI[4]; } +extern "C" { int RSID_SetModem[4]; } + +int Closing = FALSE; // Set to stop background thread + +QRgb white = qRgb(255, 255, 255); +QRgb black = qRgb(0, 0, 0); + +QRgb green = qRgb(0, 255, 0); +QRgb red = qRgb(255, 0, 0); +QRgb yellow = qRgb(255, 255, 0); +QRgb cyan = qRgb(0, 255, 255); + +// Indexed colour list from ARDOPC + +#define WHITE 0 +#define Tomato 1 +#define Gold 2 +#define Lime 3 +#define Yellow 4 +#define Orange 5 +#define Khaki 6 +#define Cyan 7 +#define DeepSkyBlue 8 +#define RoyalBlue 9 +#define Navy 10 +#define Black 11 +#define Goldenrod 12 +#define Fuchsia 13 + +QRgb vbColours[16] = { qRgb(255, 255, 255), qRgb(255, 99, 71), qRgb(255, 215, 0), qRgb(0, 255, 0), + qRgb(255, 255, 0), qRgb(255, 165, 0), qRgb(240, 240, 140), qRgb(0, 255, 255), + qRgb(0, 191, 255), qRgb(65, 105, 225), qRgb(0, 0, 128), qRgb(0, 0, 0), + qRgb(218, 165, 32), qRgb(255, 0, 255) }; + +unsigned char WaterfallLines[2][80][4096] = { 0 }; +int NextWaterfallLine[2] = { 0 }; + +unsigned int LastLevel = 255; +unsigned int LastBusy = 255; + +extern "C" int UDPClientPort; +extern "C" int UDPServerPort; +extern "C" int TXPort; +extern char UDPHost[64]; + +QTimer *cwidtimer; + +QSystemTrayIcon * trayIcon = nullptr; + +int MintoTray = 1; + +int RSID_WF = 0; // Set to use RSID FFT for Waterfall. + +extern "C" void WriteDebugLog(char * Mess) +{ + qDebug() << Mess; +} + +void QtSoundModem::doupdateDCD(int Chan, int State) +{ + DCDLabel[Chan]->setVisible(State); +} + +extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat); +extern "C" char * ShortDateTime(); + +extern "C" void mon_rsid(int snd_ch, char * RSID) +{ + int Len; + char * Msg = (char *)malloc(1024); // Cant pass local variable via signal/slot + + sprintf(Msg, "%d:%s [%s%c]", snd_ch + 1, RSID, ShortDateTime(), 'R'); + + Len = strlen(Msg); + + if (Msg[Len - 1] != '\r') + { + Msg[Len++] = '\r'; + Msg[Len] = 0; + } + + emit t->sendtoTrace(Msg, 0); +} + +extern "C" void put_frame(int snd_ch, string * frame, char * code, int tx, int excluded) +{ + UNUSED(excluded); + + int Len; + char * Msg = (char *)malloc(1024); // Cant pass local variable via signal/slot + + if (strcmp(code, "NON-AX25") == 0) + sprintf(Msg, "%d: Length, ShortDateTime(), 'R'); + else + sprintf(Msg, "%d:%s", snd_ch + 1, frame_monitor(frame, code, tx)); + + Len = strlen(Msg); + + if (Msg[Len - 1] != '\r') + { + Msg[Len++] = '\r'; + Msg[Len] = 0; + } + + emit t->sendtoTrace(Msg, tx); +} + +extern "C" void updateDCD(int Chan, bool State) +{ + emit t->updateDCD(Chan, State); +} + +bool QtSoundModem::eventFilter(QObject* obj, QEvent *evt) +{ + UNUSED(obj); + + if (evt->type() == QEvent::Resize) + { + return QWidget::event(evt); + } + + if (evt->type() == QEvent::WindowStateChange) + { + if (windowState().testFlag(Qt::WindowMinimized) == true) + w_state = WIN_MINIMIZED; + else + w_state = WIN_MAXIMIZED; + } +// if (evt->type() == QGuiApplication::applicationStateChanged) - this is a sigma; +// { +// qDebug() << "App State changed =" << evt->type() << endl; +// } + + return QWidget::event(evt); +} + +void QtSoundModem::resizeEvent(QResizeEvent* event) +{ + QMainWindow::resizeEvent(event); + + QRect r = geometry(); + + int A, B, C, W; + int modemBoxHeight = 30; + + ui.modeB->setVisible(soundChannel[1]); + ui.centerB->setVisible(soundChannel[1]); + ui.labelB->setVisible(soundChannel[1]); + DCDLabel[1]->setVisible(soundChannel[1]); + ui.RXOffsetB->setVisible(soundChannel[1]); + + ui.modeC->setVisible(soundChannel[2]); + ui.centerC->setVisible(soundChannel[2]); + ui.labelC->setVisible(soundChannel[2]); + DCDLabel[2]->setVisible(soundChannel[2]); + ui.RXOffsetC->setVisible(soundChannel[2]); + + ui.modeD->setVisible(soundChannel[3]); + ui.centerD->setVisible(soundChannel[3]); + ui.labelD->setVisible(soundChannel[3]); + DCDLabel[3]->setVisible(soundChannel[3]); + ui.RXOffsetD->setVisible(soundChannel[3]); + + if (soundChannel[2] || soundChannel[3]) + modemBoxHeight = 60; + + + A = r.height() - 25; // No waterfalls + + if (UsingBothChannels && Secondwaterfall) + { + // Two waterfalls + + ui.WaterfallA->setVisible(1); + ui.HeaderA->setVisible(1); + ui.WaterfallB->setVisible(1); + ui.HeaderB->setVisible(1); + + A = r.height() - 258; // Top of Waterfall A + B = A + 115; // Top of Waterfall B + } + else + { + // One waterfall + + // Could be Left or Right + + if (Firstwaterfall) + { + if (soundChannel[0] == RIGHT) + { + ui.WaterfallA->setVisible(0); + ui.HeaderA->setVisible(0); + ui.WaterfallB->setVisible(1); + ui.HeaderB->setVisible(1); + } + else + { + ui.WaterfallA->setVisible(1); + ui.HeaderA->setVisible(1); + ui.WaterfallB->setVisible(0); + ui.HeaderB->setVisible(0); + } + + A = r.height() - 145; // Top of Waterfall A + } + else + A = r.height() - 25; // Top of Waterfall A + } + + C = A - 150; // Bottom of Monitor, Top of connection list + W = r.width(); + + // Calc Positions of Waterfalls + + ui.monWindow->setGeometry(QRect(0, modemBoxHeight, W, C - (modemBoxHeight + 26))); + sessionTable->setGeometry(QRect(0, C, W, 175)); + + if (UsingBothChannels) + { + ui.HeaderA->setGeometry(QRect(0, A, W, 35)); + ui.WaterfallA->setGeometry(QRect(0, A + 35, W, 80)); + ui.HeaderB->setGeometry(QRect(0, B, W, 35)); + ui.WaterfallB->setGeometry(QRect(0, B + 35, W, 80)); + } + else + { + if (soundChannel[0] == RIGHT) + { + ui.HeaderB->setGeometry(QRect(0, A, W, 35)); + ui.WaterfallB->setGeometry(QRect(0, A + 35, W, 80)); + } + else + { + ui.HeaderA->setGeometry(QRect(0, A, W, 35)); + ui.WaterfallA->setGeometry(QRect(0, A + 35, W, 80)); + } + } +} + +QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State) +{ + QAction * Act = new QAction(Label, parent); + Menu->addAction(Act); + + Act->setCheckable(true); + if (State) + Act->setChecked(true); + + parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked())); + + return Act; +} + +void QtSoundModem::menuChecked() +{ + QAction * Act = static_cast(QObject::sender()); + + int state = Act->isChecked(); + + if (Act == actWaterfall1) + { + int oldstate = Firstwaterfall; + Firstwaterfall = state; + + if (state != oldstate) + initWaterfall(0, state); + + } + else if (Act == actWaterfall2) + { + int oldstate = Secondwaterfall; + Secondwaterfall = state; + + if (state != oldstate) + initWaterfall(1, state); + + } + saveSettings(); +} + +void QtSoundModem::initWaterfall(int chan, int state) +{ + if (state == 1) + { + if (chan == 0) + { + ui.WaterfallA = new QLabel(ui.centralWidget); + WaterfallCopy[0] = ui.WaterfallA; + } + else + { + ui.WaterfallB = new QLabel(ui.centralWidget); + WaterfallCopy[1] = ui.WaterfallB; + } + Waterfall[chan] = new QImage(1024, 80, QImage::Format_RGB32); + Waterfall[chan]->fill(black); + + } + else + { + delete(Waterfall[chan]); + Waterfall[chan] = 0; + } + + QSize Size(800, 602); // Not actually used, but Event constructor needs it + QResizeEvent *event = new QResizeEvent(Size, Size); + QApplication::sendEvent(this, event); +} + +// Local copies + +QLabel *RXOffsetLabel; +QSlider *RXOffset; + +QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) +{ + ui.setupUi(this); + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + + if (MintoTray) + { + char popUp[256]; + sprintf(popUp, "QtSoundModem %d %d", AGWPort, KISSPort); + trayIcon = new QSystemTrayIcon(QIcon(":/QtSoundModem/soundmodem.ico"), this); + trayIcon->setToolTip(popUp); + trayIcon->show(); + + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(TrayActivated(QSystemTrayIcon::ActivationReason))); + } + + + restoreGeometry(mysettings.value("geometry").toByteArray()); + restoreState(mysettings.value("windowState").toByteArray()); + + sessionTable = new QTableWidget(this); + + sessionTable->verticalHeader()->setVisible(FALSE); + sessionTable->verticalHeader()->setDefaultSectionSize(20); + sessionTable->horizontalHeader()->setDefaultSectionSize(68); + sessionTable->setRowCount(1); + sessionTable->setColumnCount(12); + m_TableHeader << "MyCall" << "DestCall" << "Status" << "Sent pkts" << "Sent Bytes" << "Rcvd pkts" << "Rcvd bytes" << "Rcvd FC" << "FEC corr" << "CPS TX" << "CPS RX" << "Direction"; + + sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(224, 224, 224) }"); + + sessionTable->setHorizontalHeaderLabels(m_TableHeader); + sessionTable->setColumnWidth(0, 80); + sessionTable->setColumnWidth(1, 80); + sessionTable->setColumnWidth(4, 76); + sessionTable->setColumnWidth(5, 76); + sessionTable->setColumnWidth(6, 80); + sessionTable->setColumnWidth(11, 72); + + for (int i = 0; i < modes_count; i++) + { + ui.modeA->addItem(modes_name[i]); + ui.modeB->addItem(modes_name[i]); + ui.modeC->addItem(modes_name[i]); + ui.modeD->addItem(modes_name[i]); + } + + // Set up Menus + + setupMenu = ui.menuBar->addMenu(tr("Settings")); + + actDevices = new QAction("Setup Devices", this); + setupMenu->addAction(actDevices); + + connect(actDevices, SIGNAL(triggered()), this, SLOT(clickedSlot())); + actDevices->setObjectName("actDevices"); + actModems = new QAction("Setup Modems", this); + actModems->setObjectName("actModems"); + setupMenu->addAction(actModems); + + connect(actModems, SIGNAL(triggered()), this, SLOT(clickedSlot())); + + actMintoTray = setupMenu->addAction("Minimize to Tray", this, SLOT(MinimizetoTray())); + actMintoTray->setCheckable(1); + actMintoTray->setChecked(MintoTray); + + viewMenu = ui.menuBar->addMenu(tr("&View")); + + actWaterfall1 = setupMenuLine(viewMenu, (char *)"First waterfall", this, Firstwaterfall); + actWaterfall2 = setupMenuLine(viewMenu, (char *)"Second Waterfall", this, Secondwaterfall); + + actCalib = ui.menuBar->addAction("&Calibration"); + connect(actCalib, SIGNAL(triggered()), this, SLOT(doCalibrate())); + + actRestartWF = ui.menuBar->addAction("Restart Waterfall"); + connect(actRestartWF, SIGNAL(triggered()), this, SLOT(doRestartWF())); + + actAbout = ui.menuBar->addAction("&About"); + connect(actAbout, SIGNAL(triggered()), this, SLOT(doAbout())); + + // Constellation = new QImage(91, 91, QImage::Format_RGB32); + + Header[0] = new QImage(1024, 35, QImage::Format_RGB32); + Header[1] = new QImage(1024, 35, QImage::Format_RGB32); + RXLevel = new QImage(150, 10, QImage::Format_RGB32); + + DCDLabel[0] = new QLabel(this); + DCDLabel[0]->setObjectName(QString::fromUtf8("DCDLedA")); + DCDLabel[0]->setGeometry(QRect(280, 31, 12, 12)); + DCDLabel[0]->setVisible(TRUE); + + DCDLabel[1] = new QLabel(this); + DCDLabel[1]->setObjectName(QString::fromUtf8("DCDLedB")); + DCDLabel[1]->setGeometry(QRect(575, 31, 12, 12)); + DCDLabel[1]->setVisible(TRUE); + + DCDLabel[2] = new QLabel(this); + DCDLabel[2]->setObjectName(QString::fromUtf8("DCDLedC")); + DCDLabel[2]->setGeometry(QRect(280, 61, 12, 12)); + DCDLabel[2]->setVisible(FALSE); + + DCDLabel[3] = new QLabel(this); + DCDLabel[3]->setObjectName(QString::fromUtf8("DCDLedD")); + DCDLabel[3]->setGeometry(QRect(575, 61, 12, 12)); + DCDLabel[3]->setVisible(FALSE); + + DCDLed[0] = new QImage(12, 12, QImage::Format_RGB32); + DCDLed[1] = new QImage(12, 12, QImage::Format_RGB32); + DCDLed[2] = new QImage(12, 12, QImage::Format_RGB32); + DCDLed[3] = new QImage(12, 12, QImage::Format_RGB32); + + DCDLed[0]->fill(red); + DCDLed[1]->fill(red); + DCDLed[2]->fill(red); + DCDLed[3]->fill(red); + + DCDLabel[0]->setPixmap(QPixmap::fromImage(*DCDLed[0])); + DCDLabel[1]->setPixmap(QPixmap::fromImage(*DCDLed[1])); + DCDLabel[2]->setPixmap(QPixmap::fromImage(*DCDLed[2])); + DCDLabel[3]->setPixmap(QPixmap::fromImage(*DCDLed[3])); + + chanOffsetLabel[0] = ui.RXOffsetA; + chanOffsetLabel[1] = ui.RXOffsetB; + chanOffsetLabel[2] = ui.RXOffsetC; + chanOffsetLabel[3] = ui.RXOffsetD; + + + // Waterfall[0]->setColorCount(16); + // Waterfall[1]->setColorCount(16); + + + // for (i = 0; i < 16; i++) + // { + // Waterfall[0]->setColor(i, vbColours[i]); + // Waterfall[1]->setColor(i, vbColours[i]); + // } + + WaterfallCopy[0] = ui.WaterfallA; + WaterfallCopy[1] = ui.WaterfallB; + + initWaterfall(0, 1); + initWaterfall(1, 1); + + Header[0]->fill(black); + Header[1]->fill(black); + + HeaderCopy[0] = ui.HeaderA; + HeaderCopy[1] = ui.HeaderB; + monWindowCopy = ui.monWindow; + + ui.monWindow->document()->setMaximumBlockCount(10000); + +// connect(ui.monWindow, SIGNAL(selectionChanged()), this, SLOT(onTEselectionChanged())); + + ui.HeaderA->setPixmap(QPixmap::fromImage(*Header[0])); + ui.HeaderB->setPixmap(QPixmap::fromImage(*Header[1])); + + wf_pointer(soundChannel[0]); + wf_pointer(soundChannel[1]); + wf_Scale(0); + wf_Scale(1); + + // RefreshLevel(0); + // RXLevel->setPixmap(QPixmap::fromImage(*RXLevel)); + + connect(ui.modeA, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.modeB, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.modeC, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.modeD, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + + ui.modeA->setCurrentIndex(speed[0]); + ui.modeB->setCurrentIndex(speed[1]); + ui.modeC->setCurrentIndex(speed[2]); + ui.modeD->setCurrentIndex(speed[3]); + + ModemA = ui.modeA->currentIndex(); + + ui.centerA->setValue(rx_freq[0]); + ui.centerB->setValue(rx_freq[1]); + ui.centerC->setValue(rx_freq[2]); + ui.centerD->setValue(rx_freq[3]); + + connect(ui.centerA, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.centerB, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.centerC, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.centerD, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + + ui.DCDSlider->setValue(dcd_threshold); + + + char valChar[32]; + sprintf(valChar, "RX Offset %d", rxOffset); + ui.RXOffsetLabel->setText(valChar); + ui.RXOffset->setValue(rxOffset); + + RXOffsetLabel = ui.RXOffsetLabel; + RXOffset = ui.RXOffset; + + connect(ui.DCDSlider, SIGNAL(sliderMoved(int)), this, SLOT(clickedSlotI(int))); + connect(ui.RXOffset, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + + + QObject::connect(t, SIGNAL(sendtoTrace(char *, int)), this, SLOT(sendtoTrace(char *, int)), Qt::QueuedConnection); + QObject::connect(t, SIGNAL(updateDCD(int, int)), this, SLOT(doupdateDCD(int, int)), Qt::QueuedConnection); + + connect(ui.RXOffsetA, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(ui.RXOffsetB, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(ui.RXOffsetC, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(ui.RXOffsetD, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); + timer->start(100); + + + cwidtimer = new QTimer(this); + connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer())); + + if (CWIDInterval) + cwidtimer->start(CWIDInterval * 60000); + + if (RSID_SetModem[0]) + { + RSID_WF = 1; + RSIDinitfft(); + } + il2p_init(1); +} + +void QtSoundModem::MinimizetoTray() +{ + MintoTray = actMintoTray->isChecked(); + saveSettings(); + QMessageBox::about(this, tr("QtSoundModem"), + tr("Program must be restarted to change Minimize mode")); +} + + +void QtSoundModem::TrayActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == 3) + { + showNormal(); + w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + } +} + +extern "C" void sendCWID(char * strID, BOOL blnPlay, int Chan); + +void QtSoundModem::CWIDTimer() +{ + sendCWID(CWIDCall, CWIDType, 0); + calib_mode[0] = 4; +} + +void extSetOffset(int chan) +{ + char valChar[32]; + sprintf(valChar, "%d", chanOffset[chan]); + chanOffsetLabel[chan]->setText(valChar); + + wf_pointer(soundChannel[chan]); + + pnt_change[0] = 1; + pnt_change[1] = 1; + pnt_change[2] = 1; + pnt_change[3] = 1; + + return; +} + +void QtSoundModem::MyTimerSlot() +{ + // 100 mS Timer Event + + for (int i = 0; i < 4; i++) + { + + if (needSetOffset[i]) + { + needSetOffset[i] = 0; + extSetOffset(i); // Update GUI + } + } + + if (refreshModems) + { + refreshModems = 0; + + ui.modeA->setCurrentIndex(speed[0]); + ui.modeB->setCurrentIndex(speed[1]); + ui.modeC->setCurrentIndex(speed[2]); + ui.modeD->setCurrentIndex(speed[3]); + ui.centerA->setValue(rx_freq[0]); + ui.centerB->setValue(rx_freq[1]); + ui.centerC->setValue(rx_freq[2]); + ui.centerD->setValue(rx_freq[3]); + } + + show_grid(); +} + +void QtSoundModem::returnPressed() +{ + char Name[32]; + int Chan; + QString val; + + strcpy(Name, sender()->objectName().toUtf8()); + + Chan = Name[8] - 'A'; + + val = chanOffsetLabel[Chan]->text(); + + chanOffset[Chan] = val.toInt(); + needSetOffset[Chan] = 1; // Update GUI + + +} + + +void QtSoundModem::clickedSlotI(int i) +{ + char Name[32]; + + strcpy(Name, sender()->objectName().toUtf8()); + + if (strcmp(Name, "modeA") == 0) + { + ModemA = ui.modeA->currentIndex(); + set_speed(0, ModemA); + saveSettings(); + AGW_Report_Modem_Change(0); + return; + } + + if (strcmp(Name, "modeB") == 0) + { + ModemB = ui.modeB->currentIndex(); + set_speed(1, ModemB); + saveSettings(); + AGW_Report_Modem_Change(1); + return; + } + + if (strcmp(Name, "modeC") == 0) + { + ModemC = ui.modeC->currentIndex(); + set_speed(2, ModemC); + saveSettings(); + AGW_Report_Modem_Change(2); + return; + } + + if (strcmp(Name, "modeD") == 0) + { + ModemD = ui.modeD->currentIndex(); + set_speed(3, ModemD); + saveSettings(); + AGW_Report_Modem_Change(3); + return; + } + + if (strcmp(Name, "centerA") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerA->setValue(Freq_Change(0, i)); + settings->setValue("Modem/RXFreq1", ui.centerA->value()); + AGW_Report_Modem_Change(0); + + } + return; + } + + if (strcmp(Name, "centerB") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerB->setValue(Freq_Change(1, i)); + settings->setValue("Modem/RXFreq2", ui.centerB->value()); + AGW_Report_Modem_Change(1); + } + return; + } + + if (strcmp(Name, "centerC") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerC->setValue(Freq_Change(2, i)); + settings->setValue("Modem/RXFreq3", ui.centerC->value()); + AGW_Report_Modem_Change(2); + } + return; + } + + if (strcmp(Name, "centerD") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerD->setValue(Freq_Change(3, i)); + settings->setValue("Modem/RXFreq4", ui.centerD->value()); + AGW_Report_Modem_Change(3); + } + return; + } + + if (strcmp(Name, "DCDSlider") == 0) + { + dcd_threshold = i; + saveSettings(); + return; + } + + if (strcmp(Name, "RXOffset") == 0) + { + char valChar[32]; + rxOffset = i; + sprintf(valChar, "RX Offset %d",rxOffset); + ui.RXOffsetLabel->setText(valChar); + + wf_pointer(soundChannel[0]); + wf_pointer(soundChannel[1]); + + pnt_change[0] = 1; + pnt_change[1] = 1; + pnt_change[2] = 1; + pnt_change[3] = 1; + + saveSettings(); + return; + } + + + QMessageBox msgBox; + msgBox.setWindowTitle("MessageBox Title"); + msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); + msgBox.exec(); +} + + +void QtSoundModem::clickedSlot() +{ + char Name[32]; + + strcpy(Name, sender()->objectName().toUtf8()); + + if (strcmp(Name, "actDevices") == 0) + { + doDevices(); + return; + } + + if (strcmp(Name, "actModems") == 0) + { + doModems(); + return; + } + + if (strcmp(Name, "showBPF_A") == 0) + { + doFilter(0, 0); + return; + } + + if (strcmp(Name, "showTXBPF_A") == 0) + { + doFilter(0, 1); + return; + } + + if (strcmp(Name, "showLPF_A") == 0) + { + doFilter(0, 2); + return; + } + + + if (strcmp(Name, "showBPF_B") == 0) + { + doFilter(1, 0); + return; + } + + if (strcmp(Name, "showTXBPF_B") == 0) + { + doFilter(1, 1); + return; + } + + if (strcmp(Name, "showLPF_B") == 0) + { + doFilter(1, 2); + return; + } + + if (strcmp(Name, "Low_A") == 0) + { + handleButton(0, 1); + return; + } + + if (strcmp(Name, "High_A") == 0) + { + handleButton(0, 2); + return; + } + + if (strcmp(Name, "Both_A") == 0) + { + handleButton(0, 3); + return; + } + + if (strcmp(Name, "Stop_A") == 0) + { + handleButton(0, 0); + return; + } + + + if (strcmp(Name, "Low_B") == 0) + { + handleButton(1, 1); + return; + } + + if (strcmp(Name, "High_B") == 0) + { + handleButton(1, 2); + return; + } + + if (strcmp(Name, "Both_B") == 0) + { + handleButton(1, 3); + return; + } + + if (strcmp(Name, "Stop_B") == 0) + { + handleButton(1, 0); + return; + } + + if (strcmp(Name, "Low_C") == 0) + { + handleButton(2, 1); + return; + } + + if (strcmp(Name, "High_C") == 0) + { + handleButton(2, 2); + return; + } + + if (strcmp(Name, "Both_C") == 0) + { + handleButton(2, 3); + return; + } + + if (strcmp(Name, "Stop_C") == 0) + { + handleButton(2, 0); + return; + } + + if (strcmp(Name, "Low_D") == 0) + { + handleButton(3, 1); + return; + } + + if (strcmp(Name, "High_D") == 0) + { + handleButton(3, 2); + return; + } + + if (strcmp(Name, "Both_D") == 0) + { + handleButton(3, 3); + return; + } + + if (strcmp(Name, "Stop_D") == 0) + { + handleButton(3, 0); + return; + } + + QMessageBox msgBox; + msgBox.setWindowTitle("MessageBox Title"); + msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); + msgBox.exec(); +} + +Ui_ModemDialog * Dlg; + +QDialog * modemUI; +QDialog * deviceUI; + +void QtSoundModem::doModems() +{ + Dlg = new(Ui_ModemDialog); + + QDialog UI; + char valChar[10]; + + Dlg->setupUi(&UI); + + modemUI = &UI; + deviceUI = 0; + + myResize *resize = new myResize(); + + UI.installEventFilter(resize); + + sprintf(valChar, "%d", bpf[0]); + Dlg->BPFWidthA->setText(valChar); + sprintf(valChar, "%d", bpf[1]); + Dlg->BPFWidthB->setText(valChar); + sprintf(valChar, "%d", bpf[2]); + Dlg->BPFWidthC->setText(valChar); + sprintf(valChar, "%d", bpf[3]); + Dlg->BPFWidthD->setText(valChar); + + sprintf(valChar, "%d", txbpf[0]); + Dlg->TXBPFWidthA->setText(valChar); + sprintf(valChar, "%d", txbpf[1]); + Dlg->TXBPFWidthB->setText(valChar); + sprintf(valChar, "%d", txbpf[2]); + Dlg->TXBPFWidthC->setText(valChar); + sprintf(valChar, "%d", txbpf[3]); + Dlg->TXBPFWidthD->setText(valChar); + + sprintf(valChar, "%d", lpf[0]); + Dlg->LPFWidthA->setText(valChar); + sprintf(valChar, "%d", lpf[1]); + Dlg->LPFWidthB->setText(valChar); + sprintf(valChar, "%d", lpf[2]); + Dlg->LPFWidthC->setText(valChar); + sprintf(valChar, "%d", lpf[4]); + Dlg->LPFWidthD->setText(valChar); + + sprintf(valChar, "%d", BPF_tap[0]); + Dlg->BPFTapsA->setText(valChar); + sprintf(valChar, "%d", BPF_tap[1]); + Dlg->BPFTapsB->setText(valChar); + sprintf(valChar, "%d", BPF_tap[2]); + Dlg->BPFTapsC->setText(valChar); + sprintf(valChar, "%d", BPF_tap[3]); + Dlg->BPFTapsD->setText(valChar); + + sprintf(valChar, "%d", LPF_tap[0]); + Dlg->LPFTapsA->setText(valChar); + sprintf(valChar, "%d", LPF_tap[1]); + Dlg->LPFTapsB->setText(valChar); + sprintf(valChar, "%d", LPF_tap[2]); + Dlg->LPFTapsC->setText(valChar); + sprintf(valChar, "%d", LPF_tap[3]); + Dlg->LPFTapsD->setText(valChar); + + Dlg->preEmphAllA->setChecked(emph_all[0]); + + if (emph_all[0]) + Dlg->preEmphA->setDisabled(TRUE); + else + Dlg->preEmphA->setCurrentIndex(emph_db[0]); + + Dlg->preEmphAllB->setChecked(emph_all[1]); + + if (emph_all[1]) + Dlg->preEmphB->setDisabled(TRUE); + else + Dlg->preEmphB->setCurrentIndex(emph_db[1]); + + Dlg->preEmphAllC->setChecked(emph_all[2]); + + if (emph_all[2]) + Dlg->preEmphC->setDisabled(TRUE); + else + Dlg->preEmphC->setCurrentIndex(emph_db[2]); + + Dlg->preEmphAllD->setChecked(emph_all[3]); + + if (emph_all[3]) + Dlg->preEmphD->setDisabled(TRUE); + else + Dlg->preEmphD->setCurrentIndex(emph_db[3]); + + + Dlg->nonAX25A->setChecked(NonAX25[0]); + Dlg->nonAX25B->setChecked(NonAX25[1]); + Dlg->nonAX25C->setChecked(NonAX25[2]); + Dlg->nonAX25D->setChecked(NonAX25[3]); + + Dlg->KISSOptA->setChecked(KISS_opt[0]); + Dlg->KISSOptB->setChecked(KISS_opt[1]); + Dlg->KISSOptC->setChecked(KISS_opt[2]); + Dlg->KISSOptD->setChecked(KISS_opt[3]); + + sprintf(valChar, "%d", txdelay[0]); + Dlg->TXDelayA->setText(valChar); + sprintf(valChar, "%d", txdelay[1]); + Dlg->TXDelayB->setText(valChar); + sprintf(valChar, "%d", txdelay[2]); + Dlg->TXDelayC->setText(valChar); + sprintf(valChar, "%d", txdelay[3]); + Dlg->TXDelayD->setText(valChar); + + sprintf(valChar, "%d", txtail[0]); + Dlg->TXTailA->setText(valChar); + sprintf(valChar, "%d", txtail[1]); + Dlg->TXTailB->setText(valChar); + sprintf(valChar, "%d", txtail[2]); + Dlg->TXTailC->setText(valChar); + sprintf(valChar, "%d", txtail[3]); + Dlg->TXTailD->setText(valChar); + + Dlg->FrackA->setText(QString::number(frack_time[0])); + Dlg->FrackB->setText(QString::number(frack_time[1])); + Dlg->FrackC->setText(QString::number(frack_time[2])); + Dlg->FrackD->setText(QString::number(frack_time[3])); + + Dlg->RetriesA->setText(QString::number(fracks[0])); + Dlg->RetriesB->setText(QString::number(fracks[1])); + Dlg->RetriesC->setText(QString::number(fracks[2])); + Dlg->RetriesD->setText(QString::number(fracks[3])); + + sprintf(valChar, "%d", RCVR[0]); + Dlg->AddRXA->setText(valChar); + sprintf(valChar, "%d", RCVR[1]); + Dlg->AddRXB->setText(valChar); + sprintf(valChar, "%d", RCVR[2]); + Dlg->AddRXC->setText(valChar); + sprintf(valChar, "%d", RCVR[3]); + Dlg->AddRXD->setText(valChar); + + sprintf(valChar, "%d", rcvr_offset[0]); + Dlg->RXShiftA->setText(valChar); + + sprintf(valChar, "%d", rcvr_offset[1]); + Dlg->RXShiftB->setText(valChar); + + sprintf(valChar, "%d", rcvr_offset[2]); + Dlg->RXShiftC->setText(valChar); + sprintf(valChar, "%d", rcvr_offset[3]); + Dlg->RXShiftD->setText(valChar); + + // speed[1] + // speed[2]; + + Dlg->recoverBitA->setCurrentIndex(recovery[0]); + Dlg->recoverBitB->setCurrentIndex(recovery[1]); + Dlg->recoverBitC->setCurrentIndex(recovery[2]); + Dlg->recoverBitD->setCurrentIndex(recovery[3]); + + Dlg->fx25ModeA->setCurrentIndex(fx25_mode[0]); + Dlg->fx25ModeB->setCurrentIndex(fx25_mode[1]); + Dlg->fx25ModeC->setCurrentIndex(fx25_mode[2]); + Dlg->fx25ModeD->setCurrentIndex(fx25_mode[3]); + + Dlg->IL2PModeA->setCurrentIndex(il2p_mode[0]); + Dlg->IL2PModeB->setCurrentIndex(il2p_mode[1]); + Dlg->IL2PModeC->setCurrentIndex(il2p_mode[2]); + Dlg->IL2PModeD->setCurrentIndex(il2p_mode[3]); + + Dlg->CWIDCall->setText(CWIDCall); + Dlg->CWIDInterval->setText(QString::number(CWIDInterval)); + + if (CWIDType) + Dlg->radioButton_2->setChecked(1); + else + Dlg->CWIDType->setChecked(1); + + Dlg->RSIDSABM_A->setChecked(RSID_SABM[0]); + Dlg->RSIDSABM_B->setChecked(RSID_SABM[1]); + Dlg->RSIDSABM_C->setChecked(RSID_SABM[2]); + Dlg->RSIDSABM_D->setChecked(RSID_SABM[3]); + + Dlg->RSIDUI_A->setChecked(RSID_UI[0]); + Dlg->RSIDUI_B->setChecked(RSID_UI[1]); + Dlg->RSIDUI_C->setChecked(RSID_UI[2]); + Dlg->RSIDUI_D->setChecked(RSID_UI[3]); + + Dlg->DigiCallsA->setText(MyDigiCall[0]); + Dlg->DigiCallsB->setText(MyDigiCall[1]); + Dlg->DigiCallsC->setText(MyDigiCall[2]); + Dlg->DigiCallsD->setText(MyDigiCall[3]); + + Dlg->RSID_1_SETMODEM->setChecked(RSID_SetModem[0]); + Dlg->RSID_2_SETMODEM->setChecked(RSID_SetModem[1]); + Dlg->RSID_3_SETMODEM->setChecked(RSID_SetModem[2]); + Dlg->RSID_4_SETMODEM->setChecked(RSID_SetModem[3]); + + connect(Dlg->showBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->showBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->showBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->showBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->okButton, SIGNAL(clicked()), this, SLOT(modemaccept())); + connect(Dlg->modemSave, SIGNAL(clicked()), this, SLOT(modemSave())); + connect(Dlg->cancelButton, SIGNAL(clicked()), this, SLOT(modemreject())); + + connect(Dlg->SendRSID_1, SIGNAL(clicked()), this, SLOT(doRSIDA())); + connect(Dlg->SendRSID_2, SIGNAL(clicked()), this, SLOT(doRSIDB())); + connect(Dlg->SendRSID_3, SIGNAL(clicked()), this, SLOT(doRSIDC())); + connect(Dlg->SendRSID_4, SIGNAL(clicked()), this, SLOT(doRSIDD())); + + connect(Dlg->preEmphAllA, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllAChanged(int))); + connect(Dlg->preEmphAllB, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllBChanged(int))); + connect(Dlg->preEmphAllC, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllCChanged(int))); + connect(Dlg->preEmphAllD, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllDChanged(int))); + + UI.exec(); +} + +void QtSoundModem::preEmphAllAChanged(int state) +{ + Dlg->preEmphA->setDisabled(state); +} + +void QtSoundModem::preEmphAllBChanged(int state) +{ + Dlg->preEmphB->setDisabled(state); +} + +void QtSoundModem::preEmphAllCChanged(int state) +{ + Dlg->preEmphC->setDisabled(state); +} + +void QtSoundModem::preEmphAllDChanged(int state) +{ + Dlg->preEmphD->setDisabled(state); +} + +extern "C" void get_exclude_list(char * line, TStringList * list); + +void QtSoundModem::modemaccept() +{ + modemSave(); + delete(Dlg); + saveSettings(); + + modemUI->accept(); + +} + +void QtSoundModem::modemSave() +{ + QVariant Q; + + emph_all[0] = Dlg->preEmphAllA->isChecked(); + emph_db[0] = Dlg->preEmphA->currentIndex(); + + emph_all[1] = Dlg->preEmphAllB->isChecked(); + emph_db[1] = Dlg->preEmphB->currentIndex(); + + emph_all[2] = Dlg->preEmphAllC->isChecked(); + emph_db[2] = Dlg->preEmphC->currentIndex(); + + emph_all[3] = Dlg->preEmphAllD->isChecked(); + emph_db[3] = Dlg->preEmphD->currentIndex(); + + NonAX25[0] = Dlg->nonAX25A->isChecked(); + NonAX25[1] = Dlg->nonAX25B->isChecked(); + NonAX25[2] = Dlg->nonAX25C->isChecked(); + NonAX25[3] = Dlg->nonAX25D->isChecked(); + + KISS_opt[0] = Dlg->KISSOptA->isChecked(); + KISS_opt[1] = Dlg->KISSOptB->isChecked(); + KISS_opt[2] = Dlg->KISSOptC->isChecked(); + KISS_opt[3] = Dlg->KISSOptD->isChecked(); + + if (emph_db[0] < 0 || emph_db[0] > nr_emph) + emph_db[0] = 0; + + if (emph_db[1] < 0 || emph_db[1] > nr_emph) + emph_db[1] = 0; + + if (emph_db[2] < 0 || emph_db[2] > nr_emph) + emph_db[2] = 0; + + if (emph_db[3] < 0 || emph_db[3] > nr_emph) + emph_db[3] = 0; + + Q = Dlg->TXDelayA->text(); + txdelay[0] = Q.toInt(); + + Q = Dlg->TXDelayB->text(); + txdelay[1] = Q.toInt(); + + Q = Dlg->TXDelayC->text(); + txdelay[2] = Q.toInt(); + + Q = Dlg->TXDelayD->text(); + txdelay[3] = Q.toInt(); + + Q = Dlg->TXTailA->text(); + txtail[0] = Q.toInt(); + + Q = Dlg->TXTailB->text(); + txtail[1] = Q.toInt(); + + Q = Dlg->TXTailC->text(); + txtail[2] = Q.toInt(); + + txtail[3] = Dlg->TXTailD->text().toInt(); + + frack_time[0] = Dlg->FrackA->text().toInt(); + frack_time[1] = Dlg->FrackB->text().toInt(); + frack_time[2] = Dlg->FrackC->text().toInt(); + frack_time[3] = Dlg->FrackD->text().toInt(); + + fracks[0] = Dlg->RetriesA->text().toInt(); + fracks[1] = Dlg->RetriesB->text().toInt(); + fracks[2] = Dlg->RetriesC->text().toInt(); + fracks[3] = Dlg->RetriesD->text().toInt(); + + Q = Dlg->AddRXA->text(); + RCVR[0] = Q.toInt(); + + Q = Dlg->AddRXB->text(); + RCVR[1] = Q.toInt(); + + Q = Dlg->AddRXC->text(); + RCVR[2] = Q.toInt(); + + Q = Dlg->AddRXD->text(); + RCVR[3] = Q.toInt(); + + Q = Dlg->RXShiftA->text(); + rcvr_offset[0] = Q.toInt(); + + Q = Dlg->RXShiftB->text(); + rcvr_offset[1] = Q.toInt(); + + Q = Dlg->RXShiftC->text(); + rcvr_offset[2] = Q.toInt(); + + Q = Dlg->RXShiftD->text(); + rcvr_offset[3] = Q.toInt(); + + fx25_mode[0] = Dlg->fx25ModeA->currentIndex(); + fx25_mode[1] = Dlg->fx25ModeB->currentIndex(); + fx25_mode[2] = Dlg->fx25ModeC->currentIndex(); + fx25_mode[3] = Dlg->fx25ModeD->currentIndex(); + + il2p_mode[0] = Dlg->IL2PModeA->currentIndex(); + il2p_mode[1] = Dlg->IL2PModeB->currentIndex(); + il2p_mode[2] = Dlg->IL2PModeC->currentIndex(); + il2p_mode[3] = Dlg->IL2PModeD->currentIndex(); + + recovery[0] = Dlg->recoverBitA->currentIndex(); + recovery[1] = Dlg->recoverBitB->currentIndex(); + recovery[2] = Dlg->recoverBitC->currentIndex(); + recovery[3] = Dlg->recoverBitD->currentIndex(); + + + strcpy(CWIDCall, Dlg->CWIDCall->text().toUtf8().toUpper()); + CWIDInterval = Dlg->CWIDInterval->text().toInt(); + CWIDType = Dlg->radioButton_2->isChecked(); + + if (CWIDInterval) + cwidtimer->start(CWIDInterval * 60000); + else + cwidtimer->stop(); + + + RSID_SABM[0] = Dlg->RSIDSABM_A->isChecked(); + RSID_SABM[1] = Dlg->RSIDSABM_B->isChecked(); + RSID_SABM[2] = Dlg->RSIDSABM_C->isChecked(); + RSID_SABM[3] = Dlg->RSIDSABM_D->isChecked(); + + RSID_UI[0] = Dlg->RSIDUI_A->isChecked(); + RSID_UI[1] = Dlg->RSIDUI_B->isChecked(); + RSID_UI[2] = Dlg->RSIDUI_C->isChecked(); + RSID_UI[3] = Dlg->RSIDUI_D->isChecked(); + + RSID_SetModem[0] = Dlg->RSID_1_SETMODEM->isChecked(); + RSID_SetModem[1] = Dlg->RSID_2_SETMODEM->isChecked(); + RSID_SetModem[2] = Dlg->RSID_3_SETMODEM->isChecked(); + RSID_SetModem[3] = Dlg->RSID_4_SETMODEM->isChecked(); + + Q = Dlg->DigiCallsA->text(); + strcpy(MyDigiCall[0], Q.toString().toUtf8().toUpper()); + + Q = Dlg->DigiCallsB->text(); + strcpy(MyDigiCall[1], Q.toString().toUtf8().toUpper()); + + Q = Dlg->DigiCallsC->text(); + strcpy(MyDigiCall[2], Q.toString().toUtf8().toUpper()); + + Q = Dlg->DigiCallsD->text(); + strcpy(MyDigiCall[3], Q.toString().toUtf8().toUpper()); + + int i; + + for (i = 0; i < 4; i++) + { + initTStringList(&list_digi_callsigns[i]); + + get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]); + } + +} + +void QtSoundModem::modemreject() +{ + delete(Dlg); + modemUI->reject(); +} + +void QtSoundModem::doRSIDA() +{ + needRSID[0] = 1; +} + +void QtSoundModem::doRSIDB() +{ + needRSID[1] = 1; +} + +void QtSoundModem::doRSIDC() +{ + needRSID[2] = 1; +} + +void QtSoundModem::doRSIDD() +{ + needRSID[3] = 1; +} + + + + +void QtSoundModem::doFilter(int Chan, int Filter) +{ + Ui_Dialog Dev; + QImage * bitmap; + + QDialog UI; + + Dev.setupUi(&UI); + + bitmap = new QImage(642, 312, QImage::Format_RGB32); + + bitmap->fill(qRgb(255, 255, 255)); + + QPainter qPainter(bitmap); + qPainter.setBrush(Qt::NoBrush); + qPainter.setPen(Qt::black); + + if (Filter == 0) + make_graph_buf(DET[0][0].BPF_core[Chan], BPF_tap[Chan], &qPainter); + else if (Filter == 1) + make_graph_buf(tx_BPF_core[Chan], tx_BPF_tap[Chan], &qPainter); + else + make_graph_buf(LPF_core[Chan], LPF_tap[Chan], &qPainter); + + qPainter.end(); + Dev.label->setPixmap(QPixmap::fromImage(*bitmap)); + + UI.exec(); + +} + +Ui_devicesDialog * Dev; + +char NewPTTPort[80]; + +int newSoundMode = 0; +int oldSoundMode = 0; + +void QtSoundModem::SoundModeChanged(bool State) +{ + UNUSED(State); + + // Mustn't change SoundMode until dialog is accepted + + if (Dev->UDP->isChecked()) + newSoundMode = 3; + else if (Dev->PULSE->isChecked()) + newSoundMode = 2; + else + newSoundMode = Dev->OSS->isChecked(); + +} + +void QtSoundModem::DualPTTChanged(bool State) +{ + UNUSED(State); + + // Forse Evaluation of Cat Port setting + + PTTPortChanged(0); +} + +void QtSoundModem::CATChanged(bool State) +{ + UNUSED(State); + PTTPortChanged(0); +} + +void QtSoundModem::PTTPortChanged(int Selected) +{ + UNUSED(Selected); + + QVariant Q = Dev->PTTPort->currentText(); + strcpy(NewPTTPort, Q.toString().toUtf8()); + + Dev->RTSDTR->setVisible(false); + Dev->CAT->setVisible(false); + + Dev->PTTOnLab->setVisible(false); + Dev->PTTOn->setVisible(false); + Dev->PTTOff->setVisible(false); + Dev->PTTOffLab->setVisible(false); + Dev->CATLabel->setVisible(false); + Dev->CATSpeed->setVisible(false); + + Dev->GPIOLab->setVisible(false); + Dev->GPIOLeft->setVisible(false); + Dev->GPIORight->setVisible(false); + Dev->GPIOLab2->setVisible(false); + + Dev->CM108Label->setVisible(false); + Dev->VIDPID->setVisible(false); + + if (strcmp(NewPTTPort, "None") == 0) + { + } + else if (strcmp(NewPTTPort, "GPIO") == 0) + { + Dev->GPIOLab->setVisible(true); + Dev->GPIOLeft->setVisible(true); + if (Dev->DualPTT->isChecked()) + { + Dev->GPIORight->setVisible(true); + Dev->GPIOLab2->setVisible(true); + } + } + + else if (strcmp(NewPTTPort, "CM108") == 0) + { + Dev->CM108Label->setVisible(true); +//#ifdef __ARM_ARCHX + Dev->CM108Label->setText("CM108 Device"); +//#else +// Dev->CM108Label->setText("CM108 VID/PID"); +//#endif + Dev->VIDPID->setText(CM108Addr); + Dev->VIDPID->setVisible(true); + } + else if (strcmp(NewPTTPort, "HAMLIB") == 0) + { + Dev->CM108Label->setVisible(true); + Dev->CM108Label->setText("rigctrld Port"); + Dev->VIDPID->setText(QString::number(HamLibPort)); + Dev->VIDPID->setVisible(true); + Dev->PTTOnLab->setText("rigctrld Host"); + Dev->PTTOnLab->setVisible(true); + Dev->PTTOn->setText(HamLibHost); + Dev->PTTOn->setVisible(true); + } + else + { + Dev->RTSDTR->setVisible(true); + Dev->CAT->setVisible(true); + + if (Dev->CAT->isChecked()) + { + Dev->PTTOnLab->setVisible(true); + Dev->PTTOnLab->setText("PTT On String"); + Dev->PTTOn->setText(PTTOnString); + Dev->PTTOn->setVisible(true); + Dev->PTTOff->setVisible(true); + Dev->PTTOff->setText(PTTOffString); + Dev->PTTOffLab->setVisible(true); + Dev->CATLabel->setVisible(true); + Dev->CATSpeed->setVisible(true); + } + } +} + +bool myResize::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Resize) + { + QResizeEvent *resizeEvent = static_cast(event); + QSize size = resizeEvent->size(); + int h = size.height(); + int w = size.width(); + + if (obj == deviceUI) + Dev->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10)); + else + Dlg->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10)); + + return true; + } + return QObject::eventFilter(obj, event); +} + +void QtSoundModem::doDevices() +{ + char valChar[10]; + + Dev = new(Ui_devicesDialog); + + QDialog UI; + + int i; + + Dev->setupUi(&UI); + + deviceUI = &UI; + modemUI = 0; + + myResize *resize = new myResize(); + + UI.installEventFilter(resize); + + newSoundMode = SoundMode; + oldSoundMode = SoundMode; + +#ifdef WIN32 + Dev->ALSA->setText("WaveOut"); + Dev->OSS->setVisible(0); + Dev->PULSE->setVisible(0); +#endif + + if (SoundMode == 0) + Dev->ALSA->setChecked(1); + else if (SoundMode == 1) + Dev->OSS->setChecked(1); + else if (SoundMode == 2) + Dev->PULSE->setChecked(1); + else if (SoundMode == 2) + Dev->UDP->setChecked(1); + + connect(Dev->ALSA, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + connect(Dev->OSS, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + connect(Dev->PULSE, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + connect(Dev->UDP, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + + for (i = 0; i < PlaybackCount; i++) + Dev->outputDevice->addItem(&PlaybackNames[i][0]); + + i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains); + + + if (i == -1) + { + // Add device to list + + Dev->outputDevice->addItem(PlaybackDevice); + i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains); + } + + Dev->outputDevice->setCurrentIndex(i); + + for (i = 0; i < CaptureCount; i++) + Dev->inputDevice->addItem(&CaptureNames[i][0]); + + i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains); + + if (i == -1) + { + // Add device to list + + Dev->inputDevice->addItem(CaptureDevice); + i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains); + } + Dev->inputDevice->setCurrentIndex(i); + + Dev->Modem_1_Chan->setCurrentIndex(soundChannel[0]); + Dev->Modem_2_Chan->setCurrentIndex(soundChannel[1]); + Dev->Modem_3_Chan->setCurrentIndex(soundChannel[2]); + Dev->Modem_4_Chan->setCurrentIndex(soundChannel[3]); + + // Disable "None" option in first modem + + QStandardItemModel *model = dynamic_cast(Dev->Modem_1_Chan->model()); + QStandardItem * item = model->item(0, 0); + item->setEnabled(false); + + Dev->singleChannelOutput->setChecked(SCO); + Dev->colourWaterfall->setChecked(raduga); + + sprintf(valChar, "%d", KISSPort); + Dev->KISSPort->setText(valChar); + Dev->KISSEnabled->setChecked(KISSServ); + + sprintf(valChar, "%d", AGWPort); + Dev->AGWPort->setText(valChar); + Dev->AGWEnabled->setChecked(AGWServ); + + Dev->PTTOn->setText(PTTOnString); + Dev->PTTOff->setText(PTTOffString); + + sprintf(valChar, "%d", PTTBAUD); + Dev->CATSpeed->setText(valChar); + + sprintf(valChar, "%d", UDPClientPort); + Dev->UDPPort->setText(valChar); + Dev->UDPTXHost->setText(UDPHost); + + if (UDPServerPort != TXPort) + sprintf(valChar, "%d/%d", UDPServerPort, TXPort); + else + sprintf(valChar, "%d", UDPServerPort); + + Dev->UDPTXPort->setText(valChar); + + Dev->UDPEnabled->setChecked(UDPServ); + + sprintf(valChar, "%d", pttGPIOPin); + Dev->GPIOLeft->setText(valChar); + sprintf(valChar, "%d", pttGPIOPinR); + Dev->GPIORight->setText(valChar); + + Dev->VIDPID->setText(CM108Addr); + + QStringList items; + + connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool))); + connect(Dev->DualPTT, SIGNAL(toggled(bool)), this, SLOT(DualPTTChanged(bool))); + connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int))); + + + + if (PTTMode == PTTCAT) + Dev->CAT->setChecked(true); + else + Dev->RTSDTR->setChecked(true); + + for (const QSerialPortInfo &info : Ports) + { + items.append(info.portName()); + } + + items.sort(); + + Dev->PTTPort->addItem("None"); + Dev->PTTPort->addItem("CM108"); + + //#ifdef __ARM_ARCH + + Dev->PTTPort->addItem("GPIO"); + + //#endif + + Dev->PTTPort->addItem("HAMLIB"); + + for (const QString &info : items) + { + Dev->PTTPort->addItem(info); + } + + Dev->PTTPort->setCurrentIndex(Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString)); + + PTTPortChanged(0); // Force reevaluation + + Dev->txRotation->setChecked(TX_rotate); + Dev->DualPTT->setChecked(DualPTT); + + Dev->multiCore->setChecked(multiCore); + + QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept())); + QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject())); + + UI.exec(); + +} + +void QtSoundModem::deviceaccept() +{ + QVariant Q = Dev->inputDevice->currentText(); + int cardChanged = 0; + char portString[32]; + + if (Dev->UDP->isChecked()) + { + // cant have server and slave + + if (Dev->UDPEnabled->isChecked()) + { + QMessageBox::about(this, tr("QtSoundModem"), + tr("Can't have UDP sound source and UDP server at same time")); + return; + } + } + + if (oldSoundMode != newSoundMode) + { + QMessageBox msgBox; + + msgBox.setText("QtSoundModem must restart to change Sound Mode.\n" + "Program will close if you hit Ok\n" + "You will need to reselect audio devices after restarting"); + + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + + int i = msgBox.exec(); + + if (i == QMessageBox::Ok) + { + SoundMode = newSoundMode; + + saveSettings(); + + Closing = 1; + return; + } + + if (oldSoundMode == 0) + Dev->ALSA->setChecked(1); + else if (oldSoundMode == 1) + Dev->OSS->setChecked(1); + else if (oldSoundMode == 2) + Dev->PULSE->setChecked(1); + else if (oldSoundMode == 3) + Dev->UDP->setChecked(1); + + QMessageBox::about(this, tr("Info"), + tr("

Changes not saved

")); + + return; + + } + + if (strcmp(CaptureDevice, Q.toString().toUtf8()) != 0) + { + strcpy(CaptureDevice, Q.toString().toUtf8()); + cardChanged = 1; + } + + CaptureIndex = Dev->inputDevice->currentIndex(); + + Q = Dev->outputDevice->currentText(); + + if (strcmp(PlaybackDevice, Q.toString().toUtf8()) != 0) + { + strcpy(PlaybackDevice, Q.toString().toUtf8()); + cardChanged = 1; + } + + PlayBackIndex = Dev->outputDevice->currentIndex(); + + soundChannel[0] = Dev->Modem_1_Chan->currentIndex(); + soundChannel[1] = Dev->Modem_2_Chan->currentIndex(); + soundChannel[2] = Dev->Modem_3_Chan->currentIndex(); + soundChannel[3] = Dev->Modem_4_Chan->currentIndex(); + + UsingLeft = 0; + UsingRight = 0; + UsingBothChannels = 0; + + for (int i = 0; i < 4; i++) + { + if (soundChannel[i] == LEFT) + { + UsingLeft = 1; + modemtoSoundLR[i] = 0; + } + else if (soundChannel[i] == RIGHT) + { + UsingRight = 1; + modemtoSoundLR[i] = 1; + } + } + + if (UsingLeft && UsingRight) + UsingBothChannels = 1; + + + SCO = Dev->singleChannelOutput->isChecked(); + raduga = Dev->colourWaterfall->isChecked(); + AGWServ = Dev->AGWEnabled->isChecked(); + KISSServ = Dev->KISSEnabled->isChecked(); + + Q = Dev->KISSPort->text(); + KISSPort = Q.toInt(); + + Q = Dev->AGWPort->text(); + AGWPort = Q.toInt(); + + Q = Dev->PTTPort->currentText(); + strcpy(PTTPort, Q.toString().toUtf8()); + + DualPTT = Dev->DualPTT->isChecked(); + TX_rotate = Dev->txRotation->isChecked(); + multiCore = Dev->multiCore->isChecked(); + + if (Dev->CAT->isChecked()) + PTTMode = PTTCAT; + else + PTTMode = PTTRTS; + + Q = Dev->PTTOn->text(); + strcpy(PTTOnString, Q.toString().toUtf8()); + Q = Dev->PTTOff->text(); + strcpy(PTTOffString, Q.toString().toUtf8()); + + Q = Dev->CATSpeed->text(); + PTTBAUD = Q.toInt(); + + Q = Dev->UDPPort->text(); + UDPClientPort = Q.toInt(); + + + Q = Dev->UDPTXPort->text(); + strcpy(portString, Q.toString().toUtf8()); + UDPServerPort = atoi(portString); + + if (strchr(portString, '/')) + { + char * ptr = strlop(portString, '/'); + TXPort = atoi(ptr); + } + else + TXPort = UDPServerPort; + + Q = Dev->UDPTXHost->text(); + strcpy(UDPHost, Q.toString().toUtf8()); + + UDPServ = Dev->UDPEnabled->isChecked(); + + Q = Dev->GPIOLeft->text(); + pttGPIOPin = Q.toInt(); + + Q = Dev->GPIORight->text(); + pttGPIOPinR = Q.toInt(); + + Q = Dev->VIDPID->text(); + + if (strcmp(PTTPort, "CM108") == 0) + strcpy(CM108Addr, Q.toString().toUtf8()); + else if (strcmp(PTTPort, "HAMLIB") == 0) + { + HamLibPort = Q.toInt(); + Q = Dev->PTTOn->text(); + strcpy(HamLibHost, Q.toString().toUtf8()); + } + + ClosePTTPort(); + OpenPTTPort(); + + wf_pointer(soundChannel[0]); + wf_pointer(soundChannel[1]); + + delete(Dev); + saveSettings(); + deviceUI->accept(); + + if (cardChanged) + { + InitSound(1); + } + + // Reset title and tooltip in case ports changed + + char Title[128]; + sprintf(Title, "QtSoundModem Version %s Ports %d/%d", VersionString, AGWPort, KISSPort); + w->setWindowTitle(Title); + + sprintf(Title, "QtSoundModem %d %d", AGWPort, KISSPort); + if (trayIcon) + trayIcon->setToolTip(Title); + + QSize newSize(this->size()); + QSize oldSize(this->size()); + + QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize); + + QCoreApplication::postEvent(this, myResizeEvent); +} + +void QtSoundModem::devicereject() +{ + delete(Dev); + deviceUI->reject(); +} + +void QtSoundModem::handleButton(int Port, int Type) +{ + // interlock calib with CWID + + if (calib_mode[0] == 4) // CWID + return; + + doCalib(Port, Type); +} + +void QtSoundModem::doRestartWF() +{ + if (Firstwaterfall) + { + initWaterfall(0, 0); + initWaterfall(0, 1); + } + + if (Secondwaterfall) + { + initWaterfall(1, 0); + initWaterfall(1, 1); + } +} + + +void QtSoundModem::doAbout() +{ + QMessageBox::about(this, tr("About"), + tr("G8BPQ's port of UZ7HO's Soundmodem\n\nCopyright (C) 2019-2020 Andrei Kopanchuk UZ7HO")); +} + +void QtSoundModem::doCalibrate() +{ + Ui_calDialog Calibrate; + { + QDialog UI; + Calibrate.setupUi(&UI); + + connect(Calibrate.Low_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Low_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Low_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Low_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_D, SIGNAL(released()), this, SLOT(clickedSlot())); + + /* + + connect(Calibrate.Low_A, &QPushButton::released, this, [=] { handleButton(0, 1); }); + connect(Calibrate.High_A, &QPushButton::released, this, [=] { handleButton(0, 2); }); + connect(Calibrate.Both_A, &QPushButton::released, this, [=] { handleButton(0, 3); }); + connect(Calibrate.Stop_A, &QPushButton::released, this, [=] { handleButton(0, 0); }); + connect(Calibrate.Low_B, &QPushButton::released, this, [=] { handleButton(1, 1); }); + connect(Calibrate.High_B, &QPushButton::released, this, [=] { handleButton(1, 2); }); + connect(Calibrate.Both_B, &QPushButton::released, this, [=] { handleButton(1, 3); }); + connect(Calibrate.Stop_B, &QPushButton::released, this, [=] { handleButton(1, 0); }); + +// connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(handleButton(1, 2))); +*/ + UI.exec(); + } +} + +void QtSoundModem::RefreshSpectrum(unsigned char * Data) +{ + int i; + + // Last 4 bytes are level busy and Tuning lines + + Waterfall[0]->fill(Black); + + if (Data[206] != LastLevel) + { + LastLevel = Data[206]; +// RefreshLevel(LastLevel); + } + + if (Data[207] != LastBusy) + { + LastBusy = Data[207]; +// Busy->setVisible(LastBusy); + } + + for (i = 0; i < 205; i++) + { + int val = Data[0]; + + if (val > 63) + val = 63; + + Waterfall[0]->setPixel(i, val, Yellow); + if (val < 62) + Waterfall[0]->setPixel(i, val + 1, Gold); + Data++; + } + + ui.WaterfallA->setPixmap(QPixmap::fromImage(*Waterfall[0])); + +} + +void QtSoundModem::RefreshWaterfall(int snd_ch, unsigned char * Data) +{ + int j; + unsigned char * Line; + int len = Waterfall[0]->bytesPerLine(); + int TopLine = NextWaterfallLine[snd_ch]; + + // Write line to cyclic buffer then draw starting with the line just written + + // Length is 208 bytes, including Level and Busy flags + + memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Data, 206); + if (NextWaterfallLine[snd_ch] > 63) + NextWaterfallLine[snd_ch] = 0; + + for (j = 63; j > 0; j--) + { + Line = Waterfall[0]->scanLine(j); + memcpy(Line, &WaterfallLines[snd_ch][TopLine++][0], len); + if (TopLine > 63) + TopLine = 0; + } + + ui.WaterfallA->setPixmap(QPixmap::fromImage(*Waterfall[0])); +} + + +void QtSoundModem::sendtoTrace(char * Msg, int tx) +{ + const QTextCursor old_cursor = monWindowCopy->textCursor(); + const int old_scrollbar_value = monWindowCopy->verticalScrollBar()->value(); + const bool is_scrolled_down = old_scrollbar_value == monWindowCopy->verticalScrollBar()->maximum(); + + // Move the cursor to the end of the document. + monWindowCopy->moveCursor(QTextCursor::End); + + // Insert the text at the position of the cursor (which is the end of the document). + + if (tx) + monWindowCopy->setTextColor(qRgb(192, 0, 0)); + else + monWindowCopy->setTextColor(qRgb(0, 0, 192)); + + monWindowCopy->textCursor().insertText(Msg); + + if (old_cursor.hasSelection() || !is_scrolled_down) + { + // The user has selected text or scrolled away from the bottom: maintain position. + monWindowCopy->setTextCursor(old_cursor); + monWindowCopy->verticalScrollBar()->setValue(old_scrollbar_value); + } + else + { + // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. + monWindowCopy->moveCursor(QTextCursor::End); + monWindowCopy->verticalScrollBar()->setValue(monWindowCopy->verticalScrollBar()->maximum()); + } + + free(Msg); +} + + +// I think this does the waterfall + +typedef struct TRGBQ_t +{ + Byte b, g, r, re; + +} TRGBWQ; + +typedef struct tagRECT +{ + int left; + int top; + int right; + int bottom; +} RECT; + +unsigned int RGBWF[256] ; + + +extern "C" void init_raduga() +{ + Byte offset[6] = {0, 51, 102, 153, 204}; + Byte i, n; + + for (n = 0; n < 52; n++) + { + i = n * 5; + + RGBWF[n + offset[0]] = qRgb(0, 0, i); + RGBWF[n + offset[1]] = qRgb(0, i, 255); + RGBWF[n + offset[2]] = qRgb(0, 255, 255 - i); + RGBWF[n + offset[3]] = qRgb(1, 255, 0); + RGBWF[n + offset[4]] = qRgb(255, 255 - 1, 0); + } +} + +extern "C" int nonGUIMode; + + +// This draws the Frequency Scale on Waterfall + +extern "C" void wf_Scale(int Chan) +{ + if (nonGUIMode) + return; + + float k; + int maxfreq, x, i; + char Textxx[20]; + QImage * bm = Header[Chan]; + + QPainter qPainter(bm); + qPainter.setBrush(Qt::black); + qPainter.setPen(Qt::white); + + maxfreq = roundf(RX_Samplerate*0.005); + k = 100 * FFTSize / RX_Samplerate; + + if (Chan == 0) + sprintf(Textxx, "Left"); + else + sprintf(Textxx, "Right"); + + qPainter.drawText(2, 1, + 100, 20, 0, Textxx); + + for (i = 0; i < maxfreq; i++) + { + x = round(k*i); + if (x < 1025) + { + if ((i % 5) == 0) + qPainter.drawLine(x, 20, x, 13); + else + qPainter.drawLine(x, 20, x, 16); + + if ((i % 5) == 0) + { + sprintf(Textxx, "%d", i * 100); + + qPainter.drawText(x - 12, 1, + 100, 20, 0, Textxx); + } + } + } + HeaderCopy[Chan]->setPixmap(QPixmap::fromImage(*bm)); + +} + +// This draws the frequency Markers on the Waterfall + + +void do_pointer(int waterfall) +{ + if (nonGUIMode) + return; + + float x; + + int x1, x2, k, pos1, pos2, pos3; + QImage * bm = Header[waterfall]; + + QPainter qPainter(bm); + qPainter.setBrush(Qt::NoBrush); + qPainter.setPen(Qt::white); + + // bm->fill(black); + + qPainter.fillRect(0, 26, 1024, 9, Qt::black); + + k = 29; + x = FFTSize / RX_Samplerate; + + // draw all enabled ports on the ports on this soundcard + + // First Modem is always on the first waterfall + // If second is enabled it is on the first unless different + // channel from first + + for (int i = 0; i < 4; i++) + { + if (UsingBothChannels == 0) + { + // Only One Waterfall. If first chan is + + if ((waterfall == 0 && soundChannel[i] == RIGHT) || (waterfall == 1 && soundChannel[i] == LEFT)) + return; + } + + if (soundChannel[i] == 0) + continue; + + + if (UsingBothChannels == 1) + if ((waterfall == 0 && soundChannel[i] == RIGHT) || (waterfall == 1 && soundChannel[i] == LEFT)) + continue; + + pos1 = roundf(((rxOffset + chanOffset[i] + rx_freq[i]) - 0.5*rx_shift[i])*x) - 5; + pos2 = roundf(((rxOffset + chanOffset[i] + rx_freq[i]) + 0.5*rx_shift[i])*x) - 5; + pos3 = roundf((rxOffset + chanOffset[i] + rx_freq[i]) * x); + x1 = pos1 + 5; + x2 = pos2 + 5; + + qPainter.setPen(Qt::white); + qPainter.drawLine(x1, k, x2, k); + qPainter.drawLine(x1, k - 3, x1, k + 3); + qPainter.drawLine(x2, k - 3, x2, k + 3); + qPainter.drawLine(pos3, k - 3, pos3, k + 3); + + if (rxOffset || chanOffset[i]) + { + // Draw TX posn if rxOffset used + + pos3 = roundf(rx_freq[i] * x); + qPainter.setPen(Qt::magenta); + qPainter.drawLine(pos3, k - 3, pos3, k + 3); + qPainter.drawLine(pos3, k - 3, pos3, k + 3); + qPainter.drawLine(pos3 - 2, k - 3, pos3 + 2, k - 3); + + } + } + HeaderCopy[waterfall]->setPixmap(QPixmap::fromImage(*bm)); +} + +void wf_pointer(int snd_ch) +{ + UNUSED(snd_ch); + + do_pointer(0); + do_pointer(1); +// do_pointer(2); +// do_pointer(3); +} + + +void doWaterfallThread(void * param); + +/* +#ifdef WIN32 + +#define pthread_t uintptr_t + +extern "C" uintptr_t _beginthread(void(__cdecl *start_address)(void *), unsigned stack_size, void *arglist); + +#else + +#include + +extern "C" pthread_t _beginthread(void(*start_address)(void *), unsigned stack_size, void * arglist) +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*)arglist) != 0) + perror("New Thread"); + else + pthread_detach(thread); + + return thread; +} + +#endif +*/ +extern "C" void doWaterfall(int snd_ch) +{ + if (nonGUIMode) + return; + + if (Closing) + return; + +// if (multiCore) // Run modems in separate threads +// _beginthread(doWaterfallThread, 0, xx); +// else + doWaterfallThread((void *)(size_t)snd_ch); + +} + + +extern "C" float aFFTAmpl[1024]; + +void doWaterfallThread(void * param) +{ + int snd_ch = (int)(size_t)param; + + QImage * bm = Waterfall[snd_ch]; + + word i, wid; + single mag; + UCHAR * p; + UCHAR Line[4096] = ""; // 4 bytes per pixel + + int lineLen, Start, End; + word hFFTSize; + Byte n; + float RealOut[4096] = { 0 }; + float ImagOut[4096]; + + QRegion exposed; + + hFFTSize = FFTSize / 2; + + // I think an FFT should produce n/2 bins, each of Samp/n Hz + // Looks like my code only works with n a power of 2 + + // So can use 1024 or 4096. 1024 gives 512 bins of 11.71875 and a 512 pixel + // display (is this enough?) + + + + Start = (WaterfallMin * FFTSize) / 12000; // First and last bins to process + End = (WaterfallMax * FFTSize) / 12000; + + + if (0) //RSID_WF + { + // Use the Magnitudes in float aFFTAmpl[RSID_FFT_SIZE]; + + for (i = 0; i < hFFTSize; i++) + { + mag = aFFTAmpl[i]; + + mag *= 0.00000042f; + + if (mag < 0.00001f) + mag = 0.00001f; + + if (mag > 1.0f) + mag = 1.0f; + + mag = 22 * log2f(mag) + 255; + + if (mag < 0) + mag = 0; + + fft_disp[snd_ch][i] = round(mag); + } + } + else + { + dofft(&fft_buf[snd_ch][0], RealOut, ImagOut); + + // FourierTransform(1024, &fft_buf[snd_ch][0], RealOut, ImagOut, 0); + + for (i = Start; i < End; i++) + { + //mag: = ComplexMag(fft_d[i])*0.00000042; + + // mag = sqrtf(powf(RealOut[i], 2) + powf(ImagOut[i], 2)) * 0.00000042f; + + mag = powf(RealOut[i], 2); + mag += powf(ImagOut[i], 2); + mag = sqrtf(mag); + mag *= 0.00000042f; + + + if (mag > MaxMagOut) + { + MaxMagOut = mag; + MaxMagIndex = i; + } + + if (mag < 0.00001f) + mag = 0.00001f; + + if (mag > 1.0f) + mag = 1.0f; + + mag = 22 * log2f(mag) + 255; + + if (mag < 0) + mag = 0; + + MagOut[i] = mag; // for Freq Guess + fft_disp[snd_ch][i] = round(mag); + } + } + + if (bm == 0) + return; + + + wid = bm->width(); + if (wid > hFFTSize) + wid = hFFTSize; + + wid = wid - 1; + + p = Line; + lineLen = bm->bytesPerLine(); + + if (wid > lineLen / 4) + wid = lineLen / 4; + + if (raduga == DISP_MONO) + { + for (i = Start; i < End; i++) + { + n = fft_disp[snd_ch][i]; + *(p++) = n; // all colours the same + *(p++) = n; + *(p++) = n; + p++; + } + } + else + { + for (i = Start; i < End; i++) + { + n = fft_disp[snd_ch][i]; + memcpy(p, &RGBWF[n], 4); + p += 4; + } + } + + // Scroll + + int TopLine = NextWaterfallLine[snd_ch]; + + // Write line to cyclic buffer then draw starting with the line just written + + memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Line, 4096); + if (NextWaterfallLine[snd_ch] > 79) + NextWaterfallLine[snd_ch] = 0; + + for (int j = 79; j > 0; j--) + { + p = bm->scanLine(j); + memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen); + TopLine++; + if (TopLine > 79) + TopLine = 0; + } + + WaterfallCopy[snd_ch]->setPixmap(QPixmap::fromImage(*bm)); + // WaterfallCopy[snd_ch - 1]->setPixmap(*pm); + // WaterfallCopy[1]->setPixmap(QPixmap::fromImage(*bm)); + +} + + + +void QtSoundModem::changeEvent(QEvent* e) +{ + if (e->type() == QEvent::WindowStateChange) + { + QWindowStateChangeEvent* ev = static_cast(e); + + qDebug() << windowState(); + + if (!(ev->oldState() & Qt::WindowMinimized) && windowState() & Qt::WindowMinimized) + { + if (trayIcon) + setVisible(false); + } +// if (!(ev->oldState() != Qt::WindowNoState) && windowState() == Qt::WindowNoState) +// { +// QMessageBox::information(this, "", "Window has been restored"); +// } + + } + QWidget::changeEvent(e); +} + +#include + +void QtSoundModem::closeEvent(QCloseEvent *event) +{ + UNUSED(event); + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + mysettings.setValue("geometry", QWidget::saveGeometry()); + mysettings.setValue("windowState", saveState()); + + Closing = TRUE; + qDebug() << "Closing"; + + QThread::msleep(100); +} + + +QtSoundModem::~QtSoundModem() +{ + qDebug() << "Saving Settings"; + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + mysettings.setValue("geometry", saveGeometry()); + mysettings.setValue("windowState", saveState()); + + saveSettings(); + Closing = TRUE; + qDebug() << "Closing"; + + QThread::msleep(100); +} + +extern "C" void QSleep(int ms) +{ + QThread::msleep(ms); +} + +int upd_time = 30; + +void QtSoundModem::show_grid() +{ + // This refeshes the session list + + int snd_ch, i, num_rows, row_idx; + QTableWidgetItem *item; + const char * msg; + + int speed_tx, speed_rx; + + if (grid_time < 10) + { + grid_time++; + return; + } + + grid_time = 0; + + //label7.Caption = inttostr(stat_r_mem); mem_arq + + num_rows = 0; + row_idx = 0; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (i = 0; i < port_num; i++) + { + if (AX25Port[snd_ch][i].status != STAT_NO_LINK) + num_rows++; + } + } + + if (num_rows == 0) + { + sessionTable->clearContents(); + sessionTable->setRowCount(0); + sessionTable->setRowCount(1); + } + else + sessionTable->setRowCount(num_rows); + + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (i = 0; i < port_num; i++) + { + if (AX25Port[snd_ch][i].status != STAT_NO_LINK) + { + switch (AX25Port[snd_ch][i].status) + { + case STAT_NO_LINK: + + msg = "No link"; + break; + + case STAT_LINK: + + msg = "Link"; + break; + + case STAT_CHK_LINK: + + msg = "Chk link"; + break; + + case STAT_WAIT_ANS: + + msg = "Wait ack"; + break; + + case STAT_TRY_LINK: + + msg = "Try link"; + break; + + case STAT_TRY_UNLINK: + + msg = "Try unlink"; + } + + + item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].mycall); + sessionTable->setItem(row_idx, 0, item); + + item = new QTableWidgetItem(AX25Port[snd_ch][i].kind); + sessionTable->setItem(row_idx, 11, item); + + item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].corrcall); + sessionTable->setItem(row_idx, 1, item); + + item = new QTableWidgetItem(msg); + sessionTable->setItem(row_idx, 2, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_pkt)); + sessionTable->setItem(row_idx, 3, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_byte)); + sessionTable->setItem(row_idx, 4, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_pkt)); + sessionTable->setItem(row_idx, 5, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_byte)); + sessionTable->setItem(row_idx, 6, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_fc)); + sessionTable->setItem(row_idx, 7, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_fec_count)); + sessionTable->setItem(row_idx, 8, item); + + if (grid_timer != upd_time) + grid_timer++; + else + { + grid_timer = 0; + speed_tx = round(abs(AX25Port[snd_ch][i].info.stat_s_byte - AX25Port[snd_ch][i].info.stat_l_s_byte) / upd_time); + speed_rx = round(abs(AX25Port[snd_ch][i].info.stat_r_byte - AX25Port[snd_ch][i].info.stat_l_r_byte) / upd_time); + + item = new QTableWidgetItem(QString::number(speed_tx)); + sessionTable->setItem(row_idx, 9, item); + + item = new QTableWidgetItem(QString::number(speed_rx)); + sessionTable->setItem(row_idx, 10, item); + + AX25Port[snd_ch][i].info.stat_l_r_byte = AX25Port[snd_ch][i].info.stat_r_byte; + AX25Port[snd_ch][i].info.stat_l_s_byte = AX25Port[snd_ch][i].info.stat_s_byte; + } + + row_idx++; + } + } + } +} + +// "Copy on Select" Code + +void QtSoundModem::onTEselectionChanged() +{ + QTextEdit * x = static_cast(QObject::sender()); + x->copy(); +} + diff --git a/QtSoundModem-HPLaptop.vcxproj b/QtSoundModem-HPLaptop.vcxproj new file mode 100644 index 0000000..3b99dd0 --- /dev/null +++ b/QtSoundModem-HPLaptop.vcxproj @@ -0,0 +1,288 @@ +п»ї + + + + Release + Win32 + + + Debug + Win32 + + + + {4EDE958E-D0AC-37B4-81F7-78313A262DCD} + QtSoundModem + QtVS_v304 + 10.0.19041.0 + 10.0.19041.0 + $(MSBuildProjectDirectory)\QtMsBuild + + + + v141 + release\ + false + NotSet + Application + release\ + QtSoundModem + + + v141 + debug\ + false + NotSet + Application + debug\ + QtSoundModem + + + + + + + + + + + + + + + + + + C:\Dev\Msdev2005\projects\QT\QtSoundModem\Win32\Debug\ + C:\Dev\Msdev2005\projects\QT\QtSoundModem\Intermed\Win32\Debug\ + QtSoundModem + true + true + + + C:\Dev\Msdev2005\projects\QT\QtSoundModem\Win32\Release\ + C:\Dev\Msdev2005\projects\QT\QtSoundModem\Intermed\Win32\Release\ + QtSoundModem + true + false + + + 5.14.2_msvc2017 + core;network;gui;widgets;serialport + + + 5.14.2_msvc2017 + core;network;gui;widgets;serialport + + + + + + + rsid;.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;release;/include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + MaxSpeed + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + false + $(OutDir) + MultiThreadedDLL + true + true + Level3 + true + + + libfftw3f-3.lib;shell32.lib;setupapi.lib;WS2_32.Lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + true + $(OutDir)\QtSoundModem.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtSoundModem + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + + + + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;debug;/include;rsid;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + EditAndContinue + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + Disabled + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + true + $(OutDir) + + + libfftw3f-3.lib;shell32.lib;setupapi.lib;WS2_32.Lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\QtSoundModem.exe + true + Windows + true + false + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtSoundModem + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + Generate moc_predefs.h + debug\moc_predefs.h;%(Outputs) + + + Document + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h + Generate moc_predefs.h + release\moc_predefs.h;%(Outputs) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QtSoundModem.aps b/QtSoundModem.aps new file mode 100644 index 0000000000000000000000000000000000000000..a6c5b06aa85b73233e80360de99d9950c01f23b7 GIT binary patch literal 3972 zcmdT{&5sjh82=4hK34fC#285Y7?8xpxXW}|S0NhdPG@1H+gaM_0;aKGTXq+BThew} z6o`o-#sddqxOnqVFoqLRPlTx9;L#(|i#Io(IBXby&pT~rro%3n;K8?fr|FGi_XPtJ-d8^7#fyOgEjWsdO&WA&(gb z1#FEn-V@XUdN!kH zj9D(YY1cL}G@LqsVQ6ZvJpW)+{4vD8s9%0ogs@*cZ#&~tqo-PEUz9};mwvNzX118IFsi8gDLafk^hj)~hfR(1eElbWDrquC>pd?urUD&= z{i(?QK*<*Qz^<5|4iWTYr8kptt93!n`k{T8i zzm6eX##nqR{`ziki(j&LS1zrr9BD-1HD9+^t9_(QZr^^U~!CfO&P6Fd$n z{mDtBMtp)SW;$nDS!O=j>`Z#X$-6wd0`iudKZ7wJu#e@OyoEP>=!{jMixgMRp}W+| z%sK^&5m3N!D?5$D{yN;Qh|$=y*6t{DSwMV5Ka0@W1^e4oA3fgh$Pns8!^%O#JB7QkK5gWfb6IA5h8TGt_z?~Fp;zYXm4&5hP2bmTe*3mPL*9YM$`TINSE$#^M)vB* z*g)U*?F+pdFhsFB>7JM5jPni+$O&hdw`D#pNW3IzFaNQsFhF;23vIV46Jx-0>oLyLF#YwO>&>FRFgi;BFZvJuyv*O=5NapXalP z_nEB6dCFH+SMd8%wExK7Q4EH3%ut7Pym?CTWp{gp{Ev0s8WY&bJ#yBz;;+;Gh0rP1 z>USgLzRW4(GaRIhETLJoTRd`+z7UVjvJ1A!6URyCGuicYT4J!^5A)3`E15d`u$)gi z*F2#UN}_nULU~L0DB=~d;XQQ@r=wFMDd*_=TOdCwPja3kygX=(g7oYvwaFVH9M`pE zh#li92GPC!kqF0OR8I>2|>Jli>16~uOM%eRR?nWs9r zJa3>^N6FB;Q0^!~Kgua>w0|T!h{UF?xc>Qd+_<}n7bX{Q=k^`kyL%7S@2+C&_ziq7 z>rdKPeYlG2Kd$1|@2f~`$l~T#H=!?}a}FR!wFv#Ebd zmp<{SpF_6}{QEY+$emEKUxThERTW6pXn3IY-%W__jP6RlwWLnh`__{0qgxT4`1g_2 z;XUdL!Zi5V=1jHm`kukO*gaPg$!kO1`-KUhICKDfywk`41!Z B^o#%i literal 0 HcmV?d00001 diff --git a/QtSoundModem.cpp b/QtSoundModem.cpp new file mode 100644 index 0000000..94ec95d --- /dev/null +++ b/QtSoundModem.cpp @@ -0,0 +1,2877 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +// UZ7HO Soundmodem Port + +// Not Working 4psk100 FEC + +// Thoughts on Waterfall Display. + +// Original used a 2048 sample FFT giving 5.859375 Hz bins. We plotted 1024 points, giving a 0 to 6000 specrum + +// If we want say 300 to 3300 we need about half the bin size so twice the fft size. But should we also fit required range to window size? + +// Unless we resize the most displayed bit of the screen in around 900 pixels. So each bin should be 3300 / 900 = 3.66667 Hz or a FFT size of around 3273 + +#include "QtSoundModem.h" +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "UZ7HOStuff.h" + + +QImage *Constellation; +QImage *Waterfall[4] = { 0,0,0,0 }; +QImage *Header[4]; +QLabel *DCDLabel[4]; +QLineEdit *chanOffsetLabel[4]; +QImage *DCDLed[4]; + +QImage *RXLevel; + +QLabel *WaterfallCopy[2]; +QLabel *HeaderCopy[2]; + +QTextEdit * monWindowCopy; + +extern workerThread *t; +extern QtSoundModem * w; + +QList Ports = QSerialPortInfo::availablePorts(); + +void saveSettings(); +void getSettings(); +extern "C" void CloseSound(); +extern "C" void GetSoundDevices(); +extern "C" char modes_name[modes_count][20]; +extern "C" int speed[5]; +extern "C" int KISSPort; +extern "C" short rx_freq[5]; + +extern "C" int CaptureCount; +extern "C" int PlaybackCount; + +extern "C" int CaptureIndex; // Card number +extern "C" int PlayBackIndex; + +extern "C" char CaptureNames[16][256]; +extern "C" char PlaybackNames[16][256]; + +extern "C" int SoundMode; +extern "C" int multiCore; + +extern "C" int refreshModems; + +extern "C" int pnt_change[5]; +extern "C" int needRSID[4]; + +extern "C" int needSetOffset[4]; + +extern "C" float MagOut[4096]; +extern "C" float MaxMagOut; +extern "C" int MaxMagIndex; + +extern "C" +{ + int InitSound(BOOL Report); + void soundMain(); + void MainLoop(); + void modulator(UCHAR snd_ch, int buf_size); + void SampleSink(int LR, short Sample); + void doCalib(int Port, int Act); + int Freq_Change(int Chan, int Freq); + void set_speed(int snd_ch, int Modem); + void init_speed(int snd_ch); + void wf_pointer(int snd_ch); + void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); + void dofft(short * in, float * outr, float * outi); + void init_raduga(); + void wf_Scale(int Chan); + void AGW_Report_Modem_Change(int port); + char * strlop(char * buf, char delim); + void sendRSID(int Chan, int dropTX); + void RSIDinitfft(); + void il2p_init(int il2p_debug); +} + +void make_graph_buf(float * buf, short tap, QPainter * bitmap); + +int ModemA = 2; +int ModemB = 2; +int ModemC = 2; +int ModemD = 2; +int FreqA = 1500; +int FreqB = 1500; +int FreqC = 1500; +int FreqD = 1500; +int DCD = 50; + +char CWIDCall[128] = ""; +int CWIDInterval = 0; +int CWIDLeft = 0; +int CWIDRight = 0; +int CWIDType = 1; // on/off + +int WaterfallMin = 00; +int WaterfallMax = 6000; + +int Configuring = 0; + +float BinSize; + +extern "C" { int RSID_SABM[4]; } +extern "C" { int RSID_UI[4]; } +extern "C" { int RSID_SetModem[4]; } + +int Closing = FALSE; // Set to stop background thread + +QRgb white = qRgb(255, 255, 255); +QRgb black = qRgb(0, 0, 0); + +QRgb green = qRgb(0, 255, 0); +QRgb red = qRgb(255, 0, 0); +QRgb yellow = qRgb(255, 255, 0); +QRgb cyan = qRgb(0, 255, 255); + +// Indexed colour list from ARDOPC + +#define WHITE 0 +#define Tomato 1 +#define Gold 2 +#define Lime 3 +#define Yellow 4 +#define Orange 5 +#define Khaki 6 +#define Cyan 7 +#define DeepSkyBlue 8 +#define RoyalBlue 9 +#define Navy 10 +#define Black 11 +#define Goldenrod 12 +#define Fuchsia 13 + +QRgb vbColours[16] = { qRgb(255, 255, 255), qRgb(255, 99, 71), qRgb(255, 215, 0), qRgb(0, 255, 0), + qRgb(255, 255, 0), qRgb(255, 165, 0), qRgb(240, 240, 140), qRgb(0, 255, 255), + qRgb(0, 191, 255), qRgb(65, 105, 225), qRgb(0, 0, 128), qRgb(0, 0, 0), + qRgb(218, 165, 32), qRgb(255, 0, 255) }; + +unsigned char WaterfallLines[2][80][4096] = { 0 }; +int NextWaterfallLine[2] = { 0 }; + +unsigned int LastLevel = 255; +unsigned int LastBusy = 255; + +extern "C" int UDPClientPort; +extern "C" int UDPServerPort; +extern "C" int TXPort; +extern char UDPHost[64]; + +QTimer *cwidtimer; + +QSystemTrayIcon * trayIcon = nullptr; + +int MintoTray = 1; + +int RSID_WF = 0; // Set to use RSID FFT for Waterfall. + +extern "C" void WriteDebugLog(char * Mess) +{ + qDebug() << Mess; +} + +void QtSoundModem::doupdateDCD(int Chan, int State) +{ + DCDLabel[Chan]->setVisible(State); +} + +extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat); +extern "C" char * ShortDateTime(); + +extern "C" void mon_rsid(int snd_ch, char * RSID) +{ + int Len; + char * Msg = (char *)malloc(1024); // Cant pass local variable via signal/slot + + sprintf(Msg, "%d:%s [%s%c]", snd_ch + 1, RSID, ShortDateTime(), 'R'); + + Len = strlen(Msg); + + if (Msg[Len - 1] != '\r') + { + Msg[Len++] = '\r'; + Msg[Len] = 0; + } + + emit t->sendtoTrace(Msg, 0); +} + +extern "C" void put_frame(int snd_ch, string * frame, char * code, int tx, int excluded) +{ + UNUSED(excluded); + + int Len; + char * Msg = (char *)malloc(1024); // Cant pass local variable via signal/slot + + if (strcmp(code, "NON-AX25") == 0) + sprintf(Msg, "%d: Length, ShortDateTime(), 'R'); + else + sprintf(Msg, "%d:%s", snd_ch + 1, frame_monitor(frame, code, tx)); + + Len = strlen(Msg); + + if (Msg[Len - 1] != '\r') + { + Msg[Len++] = '\r'; + Msg[Len] = 0; + } + + emit t->sendtoTrace(Msg, tx); +} + +extern "C" void updateDCD(int Chan, bool State) +{ + emit t->updateDCD(Chan, State); +} + +bool QtSoundModem::eventFilter(QObject* obj, QEvent *evt) +{ + UNUSED(obj); + + if (evt->type() == QEvent::Resize) + { + return QWidget::event(evt); + } + + if (evt->type() == QEvent::WindowStateChange) + { + if (windowState().testFlag(Qt::WindowMinimized) == true) + w_state = WIN_MINIMIZED; + else + w_state = WIN_MAXIMIZED; + } +// if (evt->type() == QGuiApplication::applicationStateChanged) - this is a sigma; +// { +// qDebug() << "App State changed =" << evt->type() << endl; +// } + + return QWidget::event(evt); +} + +void QtSoundModem::resizeEvent(QResizeEvent* event) +{ + QMainWindow::resizeEvent(event); + + QRect r = geometry(); + + int A, B, C, W; + int modemBoxHeight = 30; + + ui.modeB->setVisible(soundChannel[1]); + ui.centerB->setVisible(soundChannel[1]); + ui.labelB->setVisible(soundChannel[1]); + DCDLabel[1]->setVisible(soundChannel[1]); + ui.RXOffsetB->setVisible(soundChannel[1]); + + ui.modeC->setVisible(soundChannel[2]); + ui.centerC->setVisible(soundChannel[2]); + ui.labelC->setVisible(soundChannel[2]); + DCDLabel[2]->setVisible(soundChannel[2]); + ui.RXOffsetC->setVisible(soundChannel[2]); + + ui.modeD->setVisible(soundChannel[3]); + ui.centerD->setVisible(soundChannel[3]); + ui.labelD->setVisible(soundChannel[3]); + DCDLabel[3]->setVisible(soundChannel[3]); + ui.RXOffsetD->setVisible(soundChannel[3]); + + if (soundChannel[2] || soundChannel[3]) + modemBoxHeight = 60; + + + A = r.height() - 25; // No waterfalls + + if (UsingBothChannels && Secondwaterfall) + { + // Two waterfalls + + ui.WaterfallA->setVisible(1); + ui.HeaderA->setVisible(1); + ui.WaterfallB->setVisible(1); + ui.HeaderB->setVisible(1); + + A = r.height() - 258; // Top of Waterfall A + B = A + 115; // Top of Waterfall B + } + else + { + // One waterfall + + // Could be Left or Right + + if (Firstwaterfall) + { + if (soundChannel[0] == RIGHT) + { + ui.WaterfallA->setVisible(0); + ui.HeaderA->setVisible(0); + ui.WaterfallB->setVisible(1); + ui.HeaderB->setVisible(1); + } + else + { + ui.WaterfallA->setVisible(1); + ui.HeaderA->setVisible(1); + ui.WaterfallB->setVisible(0); + ui.HeaderB->setVisible(0); + } + + A = r.height() - 145; // Top of Waterfall A + } + else + A = r.height() - 25; // Top of Waterfall A + } + + C = A - 150; // Bottom of Monitor, Top of connection list + W = r.width(); + + // Calc Positions of Waterfalls + + ui.monWindow->setGeometry(QRect(0, modemBoxHeight, W, C - (modemBoxHeight + 26))); + sessionTable->setGeometry(QRect(0, C, W, 175)); + + if (UsingBothChannels) + { + ui.HeaderA->setGeometry(QRect(0, A, W, 35)); + ui.WaterfallA->setGeometry(QRect(0, A + 35, W, 80)); + ui.HeaderB->setGeometry(QRect(0, B, W, 35)); + ui.WaterfallB->setGeometry(QRect(0, B + 35, W, 80)); + } + else + { + if (soundChannel[0] == RIGHT) + { + ui.HeaderB->setGeometry(QRect(0, A, W, 35)); + ui.WaterfallB->setGeometry(QRect(0, A + 35, W, 80)); + } + else + { + ui.HeaderA->setGeometry(QRect(0, A, W, 35)); + ui.WaterfallA->setGeometry(QRect(0, A + 35, W, 80)); + } + } +} + +QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State) +{ + QAction * Act = new QAction(Label, parent); + Menu->addAction(Act); + + Act->setCheckable(true); + if (State) + Act->setChecked(true); + + parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked())); + + return Act; +} + +void QtSoundModem::menuChecked() +{ + QAction * Act = static_cast(QObject::sender()); + + int state = Act->isChecked(); + + if (Act == actWaterfall1) + { + int oldstate = Firstwaterfall; + Firstwaterfall = state; + + if (state != oldstate) + initWaterfall(0, state); + + } + else if (Act == actWaterfall2) + { + int oldstate = Secondwaterfall; + Secondwaterfall = state; + + if (state != oldstate) + initWaterfall(1, state); + + } + saveSettings(); +} + +void QtSoundModem::initWaterfall(int chan, int state) +{ + if (state == 1) + { + if (chan == 0) + { + ui.WaterfallA = new QLabel(ui.centralWidget); + WaterfallCopy[0] = ui.WaterfallA; + } + else + { + ui.WaterfallB = new QLabel(ui.centralWidget); + WaterfallCopy[1] = ui.WaterfallB; + } + Waterfall[chan] = new QImage(1024, 80, QImage::Format_RGB32); + Waterfall[chan]->fill(black); + + } + else + { + delete(Waterfall[chan]); + Waterfall[chan] = 0; + } + + QSize Size(800, 602); // Not actually used, but Event constructor needs it + QResizeEvent *event = new QResizeEvent(Size, Size); + QApplication::sendEvent(this, event); +} + +// Local copies + +QLabel *RXOffsetLabel; +QSlider *RXOffset; + +QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) +{ + ui.setupUi(this); + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + + if (MintoTray) + { + char popUp[256]; + sprintf(popUp, "QtSoundModem %d %d", AGWPort, KISSPort); + trayIcon = new QSystemTrayIcon(QIcon(":/QtSoundModem/soundmodem.ico"), this); + trayIcon->setToolTip(popUp); + trayIcon->show(); + + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(TrayActivated(QSystemTrayIcon::ActivationReason))); + } + + 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(this); + + sessionTable->verticalHeader()->setVisible(FALSE); + sessionTable->verticalHeader()->setDefaultSectionSize(20); + sessionTable->horizontalHeader()->setDefaultSectionSize(68); + sessionTable->setRowCount(1); + sessionTable->setColumnCount(12); + m_TableHeader << "MyCall" << "DestCall" << "Status" << "Sent pkts" << "Sent Bytes" << "Rcvd pkts" << "Rcvd bytes" << "Rcvd FC" << "FEC corr" << "CPS TX" << "CPS RX" << "Direction"; + + sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(224, 224, 224) }"); + + sessionTable->setHorizontalHeaderLabels(m_TableHeader); + sessionTable->setColumnWidth(0, 80); + sessionTable->setColumnWidth(1, 80); + sessionTable->setColumnWidth(4, 76); + sessionTable->setColumnWidth(5, 76); + sessionTable->setColumnWidth(6, 80); + sessionTable->setColumnWidth(11, 72); + + for (int i = 0; i < modes_count; i++) + { + ui.modeA->addItem(modes_name[i]); + ui.modeB->addItem(modes_name[i]); + ui.modeC->addItem(modes_name[i]); + ui.modeD->addItem(modes_name[i]); + } + + // Set up Menus + + setupMenu = ui.menuBar->addMenu(tr("Settings")); + + actDevices = new QAction("Setup Devices", this); + setupMenu->addAction(actDevices); + + connect(actDevices, SIGNAL(triggered()), this, SLOT(clickedSlot())); + actDevices->setObjectName("actDevices"); + actModems = new QAction("Setup Modems", this); + actModems->setObjectName("actModems"); + setupMenu->addAction(actModems); + + connect(actModems, SIGNAL(triggered()), this, SLOT(clickedSlot())); + + actMintoTray = setupMenu->addAction("Minimize to Tray", this, SLOT(MinimizetoTray())); + actMintoTray->setCheckable(1); + actMintoTray->setChecked(MintoTray); + + viewMenu = ui.menuBar->addMenu(tr("&View")); + + actWaterfall1 = setupMenuLine(viewMenu, (char *)"First waterfall", this, Firstwaterfall); + actWaterfall2 = setupMenuLine(viewMenu, (char *)"Second Waterfall", this, Secondwaterfall); + + actCalib = ui.menuBar->addAction("&Calibration"); + connect(actCalib, SIGNAL(triggered()), this, SLOT(doCalibrate())); + + actRestartWF = ui.menuBar->addAction("Restart Waterfall"); + connect(actRestartWF, SIGNAL(triggered()), this, SLOT(doRestartWF())); + + actAbout = ui.menuBar->addAction("&About"); + connect(actAbout, SIGNAL(triggered()), this, SLOT(doAbout())); + + // Constellation = new QImage(91, 91, QImage::Format_RGB32); + + Header[0] = new QImage(1024, 35, QImage::Format_RGB32); + Header[1] = new QImage(1024, 35, QImage::Format_RGB32); + RXLevel = new QImage(150, 10, QImage::Format_RGB32); + + DCDLabel[0] = new QLabel(this); + DCDLabel[0]->setObjectName(QString::fromUtf8("DCDLedA")); + DCDLabel[0]->setGeometry(QRect(280, 31, 12, 12)); + DCDLabel[0]->setVisible(TRUE); + + DCDLabel[1] = new QLabel(this); + DCDLabel[1]->setObjectName(QString::fromUtf8("DCDLedB")); + DCDLabel[1]->setGeometry(QRect(575, 31, 12, 12)); + DCDLabel[1]->setVisible(TRUE); + + DCDLabel[2] = new QLabel(this); + DCDLabel[2]->setObjectName(QString::fromUtf8("DCDLedC")); + DCDLabel[2]->setGeometry(QRect(280, 61, 12, 12)); + DCDLabel[2]->setVisible(FALSE); + + DCDLabel[3] = new QLabel(this); + DCDLabel[3]->setObjectName(QString::fromUtf8("DCDLedD")); + DCDLabel[3]->setGeometry(QRect(575, 61, 12, 12)); + DCDLabel[3]->setVisible(FALSE); + + DCDLed[0] = new QImage(12, 12, QImage::Format_RGB32); + DCDLed[1] = new QImage(12, 12, QImage::Format_RGB32); + DCDLed[2] = new QImage(12, 12, QImage::Format_RGB32); + DCDLed[3] = new QImage(12, 12, QImage::Format_RGB32); + + DCDLed[0]->fill(red); + DCDLed[1]->fill(red); + DCDLed[2]->fill(red); + DCDLed[3]->fill(red); + + DCDLabel[0]->setPixmap(QPixmap::fromImage(*DCDLed[0])); + DCDLabel[1]->setPixmap(QPixmap::fromImage(*DCDLed[1])); + DCDLabel[2]->setPixmap(QPixmap::fromImage(*DCDLed[2])); + DCDLabel[3]->setPixmap(QPixmap::fromImage(*DCDLed[3])); + + chanOffsetLabel[0] = ui.RXOffsetA; + chanOffsetLabel[1] = ui.RXOffsetB; + chanOffsetLabel[2] = ui.RXOffsetC; + chanOffsetLabel[3] = ui.RXOffsetD; + + + // Waterfall[0]->setColorCount(16); + // Waterfall[1]->setColorCount(16); + + + // for (i = 0; i < 16; i++) + // { + // Waterfall[0]->setColor(i, vbColours[i]); + // Waterfall[1]->setColor(i, vbColours[i]); + // } + + WaterfallCopy[0] = ui.WaterfallA; + WaterfallCopy[1] = ui.WaterfallB; + + initWaterfall(0, 1); + initWaterfall(1, 1); + + Header[0]->fill(black); + Header[1]->fill(black); + + HeaderCopy[0] = ui.HeaderA; + HeaderCopy[1] = ui.HeaderB; + monWindowCopy = ui.monWindow; + + ui.monWindow->document()->setMaximumBlockCount(10000); + +// connect(ui.monWindow, SIGNAL(selectionChanged()), this, SLOT(onTEselectionChanged())); + + ui.HeaderA->setPixmap(QPixmap::fromImage(*Header[0])); + ui.HeaderB->setPixmap(QPixmap::fromImage(*Header[1])); + + wf_pointer(soundChannel[0]); + wf_pointer(soundChannel[1]); + wf_Scale(0); + wf_Scale(1); + + // RefreshLevel(0); + // RXLevel->setPixmap(QPixmap::fromImage(*RXLevel)); + + connect(ui.modeA, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.modeB, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.modeC, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.modeD, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + + ui.modeA->setCurrentIndex(speed[0]); + ui.modeB->setCurrentIndex(speed[1]); + ui.modeC->setCurrentIndex(speed[2]); + ui.modeD->setCurrentIndex(speed[3]); + + ModemA = ui.modeA->currentIndex(); + + ui.centerA->setValue(rx_freq[0]); + ui.centerB->setValue(rx_freq[1]); + ui.centerC->setValue(rx_freq[2]); + ui.centerD->setValue(rx_freq[3]); + + connect(ui.centerA, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.centerB, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.centerC, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.centerD, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + + ui.DCDSlider->setValue(dcd_threshold); + + + char valChar[32]; + sprintf(valChar, "RX Offset %d", rxOffset); + ui.RXOffsetLabel->setText(valChar); + ui.RXOffset->setValue(rxOffset); + + RXOffsetLabel = ui.RXOffsetLabel; + RXOffset = ui.RXOffset; + + connect(ui.DCDSlider, SIGNAL(sliderMoved(int)), this, SLOT(clickedSlotI(int))); + connect(ui.RXOffset, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + + + QObject::connect(t, SIGNAL(sendtoTrace(char *, int)), this, SLOT(sendtoTrace(char *, int)), Qt::QueuedConnection); + QObject::connect(t, SIGNAL(updateDCD(int, int)), this, SLOT(doupdateDCD(int, int)), Qt::QueuedConnection); + + connect(ui.RXOffsetA, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(ui.RXOffsetB, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(ui.RXOffsetC, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(ui.RXOffsetD, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); + timer->start(100); + + + cwidtimer = new QTimer(this); + connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer())); + + if (CWIDInterval) + cwidtimer->start(CWIDInterval * 60000); + + if (RSID_SetModem[0]) + { + RSID_WF = 1; + RSIDinitfft(); + } + il2p_init(1); +} + +void QtSoundModem::MinimizetoTray() +{ + MintoTray = actMintoTray->isChecked(); + saveSettings(); + QMessageBox::about(this, tr("QtSoundModem"), + tr("Program must be restarted to change Minimize mode")); +} + + +void QtSoundModem::TrayActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == 3) + { + showNormal(); + w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + } +} + +extern "C" void sendCWID(char * strID, BOOL blnPlay, int Chan); + +void QtSoundModem::CWIDTimer() +{ + sendCWID(CWIDCall, CWIDType, 0); + calib_mode[0] = 4; +} + +void extSetOffset(int chan) +{ + char valChar[32]; + sprintf(valChar, "%d", chanOffset[chan]); + chanOffsetLabel[chan]->setText(valChar); + + wf_pointer(soundChannel[chan]); + + pnt_change[0] = 1; + pnt_change[1] = 1; + pnt_change[2] = 1; + pnt_change[3] = 1; + + return; +} + +void QtSoundModem::MyTimerSlot() +{ + // 100 mS Timer Event + + for (int i = 0; i < 4; i++) + { + + if (needSetOffset[i]) + { + needSetOffset[i] = 0; + extSetOffset(i); // Update GUI + } + } + + if (refreshModems) + { + refreshModems = 0; + + ui.modeA->setCurrentIndex(speed[0]); + ui.modeB->setCurrentIndex(speed[1]); + ui.modeC->setCurrentIndex(speed[2]); + ui.modeD->setCurrentIndex(speed[3]); + ui.centerA->setValue(rx_freq[0]); + ui.centerB->setValue(rx_freq[1]); + ui.centerC->setValue(rx_freq[2]); + ui.centerD->setValue(rx_freq[3]); + } + + show_grid(); +} + +void QtSoundModem::returnPressed() +{ + char Name[32]; + int Chan; + QString val; + + strcpy(Name, sender()->objectName().toUtf8()); + + Chan = Name[8] - 'A'; + + val = chanOffsetLabel[Chan]->text(); + + chanOffset[Chan] = val.toInt(); + needSetOffset[Chan] = 1; // Update GUI + + +} + + +void QtSoundModem::clickedSlotI(int i) +{ + char Name[32]; + + strcpy(Name, sender()->objectName().toUtf8()); + + if (strcmp(Name, "modeA") == 0) + { + ModemA = ui.modeA->currentIndex(); + set_speed(0, ModemA); + saveSettings(); + AGW_Report_Modem_Change(0); + return; + } + + if (strcmp(Name, "modeB") == 0) + { + ModemB = ui.modeB->currentIndex(); + set_speed(1, ModemB); + saveSettings(); + AGW_Report_Modem_Change(1); + return; + } + + if (strcmp(Name, "modeC") == 0) + { + ModemC = ui.modeC->currentIndex(); + set_speed(2, ModemC); + saveSettings(); + AGW_Report_Modem_Change(2); + return; + } + + if (strcmp(Name, "modeD") == 0) + { + ModemD = ui.modeD->currentIndex(); + set_speed(3, ModemD); + saveSettings(); + AGW_Report_Modem_Change(3); + return; + } + + if (strcmp(Name, "centerA") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerA->setValue(Freq_Change(0, i)); + settings->setValue("Modem/RXFreq1", ui.centerA->value()); + AGW_Report_Modem_Change(0); + + } + return; + } + + if (strcmp(Name, "centerB") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerB->setValue(Freq_Change(1, i)); + settings->setValue("Modem/RXFreq2", ui.centerB->value()); + AGW_Report_Modem_Change(1); + } + return; + } + + if (strcmp(Name, "centerC") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerC->setValue(Freq_Change(2, i)); + settings->setValue("Modem/RXFreq3", ui.centerC->value()); + AGW_Report_Modem_Change(2); + } + return; + } + + if (strcmp(Name, "centerD") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerD->setValue(Freq_Change(3, i)); + settings->setValue("Modem/RXFreq4", ui.centerD->value()); + AGW_Report_Modem_Change(3); + } + return; + } + + if (strcmp(Name, "DCDSlider") == 0) + { + dcd_threshold = i; + saveSettings(); + return; + } + + if (strcmp(Name, "RXOffset") == 0) + { + char valChar[32]; + rxOffset = i; + sprintf(valChar, "RX Offset %d",rxOffset); + ui.RXOffsetLabel->setText(valChar); + + wf_pointer(soundChannel[0]); + wf_pointer(soundChannel[1]); + + pnt_change[0] = 1; + pnt_change[1] = 1; + pnt_change[2] = 1; + pnt_change[3] = 1; + + saveSettings(); + return; + } + + + QMessageBox msgBox; + msgBox.setWindowTitle("MessageBox Title"); + msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); + msgBox.exec(); +} + + +void QtSoundModem::clickedSlot() +{ + char Name[32]; + + strcpy(Name, sender()->objectName().toUtf8()); + + if (strcmp(Name, "actDevices") == 0) + { + doDevices(); + return; + } + + if (strcmp(Name, "actModems") == 0) + { + doModems(); + return; + } + + if (strcmp(Name, "showBPF_A") == 0) + { + doFilter(0, 0); + return; + } + + if (strcmp(Name, "showTXBPF_A") == 0) + { + doFilter(0, 1); + return; + } + + if (strcmp(Name, "showLPF_A") == 0) + { + doFilter(0, 2); + return; + } + + + if (strcmp(Name, "showBPF_B") == 0) + { + doFilter(1, 0); + return; + } + + if (strcmp(Name, "showTXBPF_B") == 0) + { + doFilter(1, 1); + return; + } + + if (strcmp(Name, "showLPF_B") == 0) + { + doFilter(1, 2); + return; + } + + if (strcmp(Name, "Low_A") == 0) + { + handleButton(0, 1); + return; + } + + if (strcmp(Name, "High_A") == 0) + { + handleButton(0, 2); + return; + } + + if (strcmp(Name, "Both_A") == 0) + { + handleButton(0, 3); + return; + } + + if (strcmp(Name, "Stop_A") == 0) + { + handleButton(0, 0); + return; + } + + + if (strcmp(Name, "Low_B") == 0) + { + handleButton(1, 1); + return; + } + + if (strcmp(Name, "High_B") == 0) + { + handleButton(1, 2); + return; + } + + if (strcmp(Name, "Both_B") == 0) + { + handleButton(1, 3); + return; + } + + if (strcmp(Name, "Stop_B") == 0) + { + handleButton(1, 0); + return; + } + + if (strcmp(Name, "Low_C") == 0) + { + handleButton(2, 1); + return; + } + + if (strcmp(Name, "High_C") == 0) + { + handleButton(2, 2); + return; + } + + if (strcmp(Name, "Both_C") == 0) + { + handleButton(2, 3); + return; + } + + if (strcmp(Name, "Stop_C") == 0) + { + handleButton(2, 0); + return; + } + + if (strcmp(Name, "Low_D") == 0) + { + handleButton(3, 1); + return; + } + + if (strcmp(Name, "High_D") == 0) + { + handleButton(3, 2); + return; + } + + if (strcmp(Name, "Both_D") == 0) + { + handleButton(3, 3); + return; + } + + if (strcmp(Name, "Stop_D") == 0) + { + handleButton(3, 0); + return; + } + + QMessageBox msgBox; + msgBox.setWindowTitle("MessageBox Title"); + msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); + msgBox.exec(); +} + +Ui_ModemDialog * Dlg; + +QDialog * modemUI; +QDialog * deviceUI; + +void QtSoundModem::doModems() +{ + Dlg = new(Ui_ModemDialog); + + QDialog UI; + char valChar[10]; + + Dlg->setupUi(&UI); + + modemUI = &UI; + deviceUI = 0; + + myResize *resize = new myResize(); + + UI.installEventFilter(resize); + + sprintf(valChar, "%d", bpf[0]); + Dlg->BPFWidthA->setText(valChar); + sprintf(valChar, "%d", bpf[1]); + Dlg->BPFWidthB->setText(valChar); + sprintf(valChar, "%d", bpf[2]); + Dlg->BPFWidthC->setText(valChar); + sprintf(valChar, "%d", bpf[3]); + Dlg->BPFWidthD->setText(valChar); + + sprintf(valChar, "%d", txbpf[0]); + Dlg->TXBPFWidthA->setText(valChar); + sprintf(valChar, "%d", txbpf[1]); + Dlg->TXBPFWidthB->setText(valChar); + sprintf(valChar, "%d", txbpf[2]); + Dlg->TXBPFWidthC->setText(valChar); + sprintf(valChar, "%d", txbpf[3]); + Dlg->TXBPFWidthD->setText(valChar); + + sprintf(valChar, "%d", lpf[0]); + Dlg->LPFWidthA->setText(valChar); + sprintf(valChar, "%d", lpf[1]); + Dlg->LPFWidthB->setText(valChar); + sprintf(valChar, "%d", lpf[2]); + Dlg->LPFWidthC->setText(valChar); + sprintf(valChar, "%d", lpf[4]); + Dlg->LPFWidthD->setText(valChar); + + sprintf(valChar, "%d", BPF_tap[0]); + Dlg->BPFTapsA->setText(valChar); + sprintf(valChar, "%d", BPF_tap[1]); + Dlg->BPFTapsB->setText(valChar); + sprintf(valChar, "%d", BPF_tap[2]); + Dlg->BPFTapsC->setText(valChar); + sprintf(valChar, "%d", BPF_tap[3]); + Dlg->BPFTapsD->setText(valChar); + + sprintf(valChar, "%d", LPF_tap[0]); + Dlg->LPFTapsA->setText(valChar); + sprintf(valChar, "%d", LPF_tap[1]); + Dlg->LPFTapsB->setText(valChar); + sprintf(valChar, "%d", LPF_tap[2]); + Dlg->LPFTapsC->setText(valChar); + sprintf(valChar, "%d", LPF_tap[3]); + Dlg->LPFTapsD->setText(valChar); + + Dlg->preEmphAllA->setChecked(emph_all[0]); + + if (emph_all[0]) + Dlg->preEmphA->setDisabled(TRUE); + else + Dlg->preEmphA->setCurrentIndex(emph_db[0]); + + Dlg->preEmphAllB->setChecked(emph_all[1]); + + if (emph_all[1]) + Dlg->preEmphB->setDisabled(TRUE); + else + Dlg->preEmphB->setCurrentIndex(emph_db[1]); + + Dlg->preEmphAllC->setChecked(emph_all[2]); + + if (emph_all[2]) + Dlg->preEmphC->setDisabled(TRUE); + else + Dlg->preEmphC->setCurrentIndex(emph_db[2]); + + Dlg->preEmphAllD->setChecked(emph_all[3]); + + if (emph_all[3]) + Dlg->preEmphD->setDisabled(TRUE); + else + Dlg->preEmphD->setCurrentIndex(emph_db[3]); + + + Dlg->nonAX25A->setChecked(NonAX25[0]); + Dlg->nonAX25B->setChecked(NonAX25[1]); + Dlg->nonAX25C->setChecked(NonAX25[2]); + Dlg->nonAX25D->setChecked(NonAX25[3]); + + Dlg->KISSOptA->setChecked(KISS_opt[0]); + Dlg->KISSOptB->setChecked(KISS_opt[1]); + Dlg->KISSOptC->setChecked(KISS_opt[2]); + Dlg->KISSOptD->setChecked(KISS_opt[3]); + + sprintf(valChar, "%d", txdelay[0]); + Dlg->TXDelayA->setText(valChar); + sprintf(valChar, "%d", txdelay[1]); + Dlg->TXDelayB->setText(valChar); + sprintf(valChar, "%d", txdelay[2]); + Dlg->TXDelayC->setText(valChar); + sprintf(valChar, "%d", txdelay[3]); + Dlg->TXDelayD->setText(valChar); + + sprintf(valChar, "%d", txtail[0]); + Dlg->TXTailA->setText(valChar); + sprintf(valChar, "%d", txtail[1]); + Dlg->TXTailB->setText(valChar); + sprintf(valChar, "%d", txtail[2]); + Dlg->TXTailC->setText(valChar); + sprintf(valChar, "%d", txtail[3]); + Dlg->TXTailD->setText(valChar); + + Dlg->FrackA->setText(QString::number(frack_time[0])); + Dlg->FrackB->setText(QString::number(frack_time[1])); + Dlg->FrackC->setText(QString::number(frack_time[2])); + Dlg->FrackD->setText(QString::number(frack_time[3])); + + Dlg->RetriesA->setText(QString::number(fracks[0])); + Dlg->RetriesB->setText(QString::number(fracks[1])); + Dlg->RetriesC->setText(QString::number(fracks[2])); + Dlg->RetriesD->setText(QString::number(fracks[3])); + + sprintf(valChar, "%d", RCVR[0]); + Dlg->AddRXA->setText(valChar); + sprintf(valChar, "%d", RCVR[1]); + Dlg->AddRXB->setText(valChar); + sprintf(valChar, "%d", RCVR[2]); + Dlg->AddRXC->setText(valChar); + sprintf(valChar, "%d", RCVR[3]); + Dlg->AddRXD->setText(valChar); + + sprintf(valChar, "%d", rcvr_offset[0]); + Dlg->RXShiftA->setText(valChar); + + sprintf(valChar, "%d", rcvr_offset[1]); + Dlg->RXShiftB->setText(valChar); + + sprintf(valChar, "%d", rcvr_offset[2]); + Dlg->RXShiftC->setText(valChar); + sprintf(valChar, "%d", rcvr_offset[3]); + Dlg->RXShiftD->setText(valChar); + + // speed[1] + // speed[2]; + + Dlg->recoverBitA->setCurrentIndex(recovery[0]); + Dlg->recoverBitB->setCurrentIndex(recovery[1]); + Dlg->recoverBitC->setCurrentIndex(recovery[2]); + Dlg->recoverBitD->setCurrentIndex(recovery[3]); + + Dlg->fx25ModeA->setCurrentIndex(fx25_mode[0]); + Dlg->fx25ModeB->setCurrentIndex(fx25_mode[1]); + Dlg->fx25ModeC->setCurrentIndex(fx25_mode[2]); + Dlg->fx25ModeD->setCurrentIndex(fx25_mode[3]); + + Dlg->IL2PModeA->setCurrentIndex(il2p_mode[0]); + Dlg->IL2PModeB->setCurrentIndex(il2p_mode[1]); + Dlg->IL2PModeC->setCurrentIndex(il2p_mode[2]); + Dlg->IL2PModeD->setCurrentIndex(il2p_mode[3]); + + Dlg->CWIDCall->setText(CWIDCall); + Dlg->CWIDInterval->setText(QString::number(CWIDInterval)); + + if (CWIDType) + Dlg->radioButton_2->setChecked(1); + else + Dlg->CWIDType->setChecked(1); + + Dlg->RSIDSABM_A->setChecked(RSID_SABM[0]); + Dlg->RSIDSABM_B->setChecked(RSID_SABM[1]); + Dlg->RSIDSABM_C->setChecked(RSID_SABM[2]); + Dlg->RSIDSABM_D->setChecked(RSID_SABM[3]); + + Dlg->RSIDUI_A->setChecked(RSID_UI[0]); + Dlg->RSIDUI_B->setChecked(RSID_UI[1]); + Dlg->RSIDUI_C->setChecked(RSID_UI[2]); + Dlg->RSIDUI_D->setChecked(RSID_UI[3]); + + Dlg->DigiCallsA->setText(MyDigiCall[0]); + Dlg->DigiCallsB->setText(MyDigiCall[1]); + Dlg->DigiCallsC->setText(MyDigiCall[2]); + Dlg->DigiCallsD->setText(MyDigiCall[3]); + + Dlg->RSID_1_SETMODEM->setChecked(RSID_SetModem[0]); + Dlg->RSID_2_SETMODEM->setChecked(RSID_SetModem[1]); + Dlg->RSID_3_SETMODEM->setChecked(RSID_SetModem[2]); + Dlg->RSID_4_SETMODEM->setChecked(RSID_SetModem[3]); + + connect(Dlg->showBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->showBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->showBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->showBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->okButton, SIGNAL(clicked()), this, SLOT(modemaccept())); + connect(Dlg->modemSave, SIGNAL(clicked()), this, SLOT(modemSave())); + connect(Dlg->cancelButton, SIGNAL(clicked()), this, SLOT(modemreject())); + + connect(Dlg->SendRSID_1, SIGNAL(clicked()), this, SLOT(doRSIDA())); + connect(Dlg->SendRSID_2, SIGNAL(clicked()), this, SLOT(doRSIDB())); + connect(Dlg->SendRSID_3, SIGNAL(clicked()), this, SLOT(doRSIDC())); + connect(Dlg->SendRSID_4, SIGNAL(clicked()), this, SLOT(doRSIDD())); + + connect(Dlg->preEmphAllA, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllAChanged(int))); + connect(Dlg->preEmphAllB, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllBChanged(int))); + connect(Dlg->preEmphAllC, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllCChanged(int))); + connect(Dlg->preEmphAllD, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllDChanged(int))); + + UI.exec(); +} + +void QtSoundModem::preEmphAllAChanged(int state) +{ + Dlg->preEmphA->setDisabled(state); +} + +void QtSoundModem::preEmphAllBChanged(int state) +{ + Dlg->preEmphB->setDisabled(state); +} + +void QtSoundModem::preEmphAllCChanged(int state) +{ + Dlg->preEmphC->setDisabled(state); +} + +void QtSoundModem::preEmphAllDChanged(int state) +{ + Dlg->preEmphD->setDisabled(state); +} + +extern "C" void get_exclude_list(char * line, TStringList * list); + +void QtSoundModem::modemaccept() +{ + modemSave(); + delete(Dlg); + saveSettings(); + + modemUI->accept(); + +} + +void QtSoundModem::modemSave() +{ + QVariant Q; + + emph_all[0] = Dlg->preEmphAllA->isChecked(); + emph_db[0] = Dlg->preEmphA->currentIndex(); + + emph_all[1] = Dlg->preEmphAllB->isChecked(); + emph_db[1] = Dlg->preEmphB->currentIndex(); + + emph_all[2] = Dlg->preEmphAllC->isChecked(); + emph_db[2] = Dlg->preEmphC->currentIndex(); + + emph_all[3] = Dlg->preEmphAllD->isChecked(); + emph_db[3] = Dlg->preEmphD->currentIndex(); + + NonAX25[0] = Dlg->nonAX25A->isChecked(); + NonAX25[1] = Dlg->nonAX25B->isChecked(); + NonAX25[2] = Dlg->nonAX25C->isChecked(); + NonAX25[3] = Dlg->nonAX25D->isChecked(); + + KISS_opt[0] = Dlg->KISSOptA->isChecked(); + KISS_opt[1] = Dlg->KISSOptB->isChecked(); + KISS_opt[2] = Dlg->KISSOptC->isChecked(); + KISS_opt[3] = Dlg->KISSOptD->isChecked(); + + if (emph_db[0] < 0 || emph_db[0] > nr_emph) + emph_db[0] = 0; + + if (emph_db[1] < 0 || emph_db[1] > nr_emph) + emph_db[1] = 0; + + if (emph_db[2] < 0 || emph_db[2] > nr_emph) + emph_db[2] = 0; + + if (emph_db[3] < 0 || emph_db[3] > nr_emph) + emph_db[3] = 0; + + Q = Dlg->TXDelayA->text(); + txdelay[0] = Q.toInt(); + + Q = Dlg->TXDelayB->text(); + txdelay[1] = Q.toInt(); + + Q = Dlg->TXDelayC->text(); + txdelay[2] = Q.toInt(); + + Q = Dlg->TXDelayD->text(); + txdelay[3] = Q.toInt(); + + Q = Dlg->TXTailA->text(); + txtail[0] = Q.toInt(); + + Q = Dlg->TXTailB->text(); + txtail[1] = Q.toInt(); + + Q = Dlg->TXTailC->text(); + txtail[2] = Q.toInt(); + + txtail[3] = Dlg->TXTailD->text().toInt(); + + frack_time[0] = Dlg->FrackA->text().toInt(); + frack_time[1] = Dlg->FrackB->text().toInt(); + frack_time[2] = Dlg->FrackC->text().toInt(); + frack_time[3] = Dlg->FrackD->text().toInt(); + + fracks[0] = Dlg->RetriesA->text().toInt(); + fracks[1] = Dlg->RetriesB->text().toInt(); + fracks[2] = Dlg->RetriesC->text().toInt(); + fracks[3] = Dlg->RetriesD->text().toInt(); + + Q = Dlg->AddRXA->text(); + RCVR[0] = Q.toInt(); + + Q = Dlg->AddRXB->text(); + RCVR[1] = Q.toInt(); + + Q = Dlg->AddRXC->text(); + RCVR[2] = Q.toInt(); + + Q = Dlg->AddRXD->text(); + RCVR[3] = Q.toInt(); + + Q = Dlg->RXShiftA->text(); + rcvr_offset[0] = Q.toInt(); + + Q = Dlg->RXShiftB->text(); + rcvr_offset[1] = Q.toInt(); + + Q = Dlg->RXShiftC->text(); + rcvr_offset[2] = Q.toInt(); + + Q = Dlg->RXShiftD->text(); + rcvr_offset[3] = Q.toInt(); + + fx25_mode[0] = Dlg->fx25ModeA->currentIndex(); + fx25_mode[1] = Dlg->fx25ModeB->currentIndex(); + fx25_mode[2] = Dlg->fx25ModeC->currentIndex(); + fx25_mode[3] = Dlg->fx25ModeD->currentIndex(); + + il2p_mode[0] = Dlg->IL2PModeA->currentIndex(); + il2p_mode[1] = Dlg->IL2PModeB->currentIndex(); + il2p_mode[2] = Dlg->IL2PModeC->currentIndex(); + il2p_mode[3] = Dlg->IL2PModeD->currentIndex(); + + recovery[0] = Dlg->recoverBitA->currentIndex(); + recovery[1] = Dlg->recoverBitB->currentIndex(); + recovery[2] = Dlg->recoverBitC->currentIndex(); + recovery[3] = Dlg->recoverBitD->currentIndex(); + + + strcpy(CWIDCall, Dlg->CWIDCall->text().toUtf8().toUpper()); + CWIDInterval = Dlg->CWIDInterval->text().toInt(); + CWIDType = Dlg->radioButton_2->isChecked(); + + if (CWIDInterval) + cwidtimer->start(CWIDInterval * 60000); + else + cwidtimer->stop(); + + + RSID_SABM[0] = Dlg->RSIDSABM_A->isChecked(); + RSID_SABM[1] = Dlg->RSIDSABM_B->isChecked(); + RSID_SABM[2] = Dlg->RSIDSABM_C->isChecked(); + RSID_SABM[3] = Dlg->RSIDSABM_D->isChecked(); + + RSID_UI[0] = Dlg->RSIDUI_A->isChecked(); + RSID_UI[1] = Dlg->RSIDUI_B->isChecked(); + RSID_UI[2] = Dlg->RSIDUI_C->isChecked(); + RSID_UI[3] = Dlg->RSIDUI_D->isChecked(); + + RSID_SetModem[0] = Dlg->RSID_1_SETMODEM->isChecked(); + RSID_SetModem[1] = Dlg->RSID_2_SETMODEM->isChecked(); + RSID_SetModem[2] = Dlg->RSID_3_SETMODEM->isChecked(); + RSID_SetModem[3] = Dlg->RSID_4_SETMODEM->isChecked(); + + Q = Dlg->DigiCallsA->text(); + strcpy(MyDigiCall[0], Q.toString().toUtf8().toUpper()); + + Q = Dlg->DigiCallsB->text(); + strcpy(MyDigiCall[1], Q.toString().toUtf8().toUpper()); + + Q = Dlg->DigiCallsC->text(); + strcpy(MyDigiCall[2], Q.toString().toUtf8().toUpper()); + + Q = Dlg->DigiCallsD->text(); + strcpy(MyDigiCall[3], Q.toString().toUtf8().toUpper()); + + int i; + + for (i = 0; i < 4; i++) + { + initTStringList(&list_digi_callsigns[i]); + + get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]); + } + +} + +void QtSoundModem::modemreject() +{ + delete(Dlg); + modemUI->reject(); +} + +void QtSoundModem::doRSIDA() +{ + needRSID[0] = 1; +} + +void QtSoundModem::doRSIDB() +{ + needRSID[1] = 1; +} + +void QtSoundModem::doRSIDC() +{ + needRSID[2] = 1; +} + +void QtSoundModem::doRSIDD() +{ + needRSID[3] = 1; +} + + + + +void QtSoundModem::doFilter(int Chan, int Filter) +{ + Ui_Dialog Dev; + QImage * bitmap; + + QDialog UI; + + Dev.setupUi(&UI); + + bitmap = new QImage(642, 312, QImage::Format_RGB32); + + bitmap->fill(qRgb(255, 255, 255)); + + QPainter qPainter(bitmap); + qPainter.setBrush(Qt::NoBrush); + qPainter.setPen(Qt::black); + + if (Filter == 0) + make_graph_buf(DET[0][0].BPF_core[Chan], BPF_tap[Chan], &qPainter); + else if (Filter == 1) + make_graph_buf(tx_BPF_core[Chan], tx_BPF_tap[Chan], &qPainter); + else + make_graph_buf(LPF_core[Chan], LPF_tap[Chan], &qPainter); + + qPainter.end(); + Dev.label->setPixmap(QPixmap::fromImage(*bitmap)); + + UI.exec(); + +} + +Ui_devicesDialog * Dev; + +char NewPTTPort[80]; + +int newSoundMode = 0; +int oldSoundMode = 0; + +void QtSoundModem::SoundModeChanged(bool State) +{ + UNUSED(State); + + // Mustn't change SoundMode until dialog is accepted + + if (Dev->UDP->isChecked()) + newSoundMode = 3; + else if (Dev->PULSE->isChecked()) + newSoundMode = 2; + else + newSoundMode = Dev->OSS->isChecked(); + +} + +void QtSoundModem::DualPTTChanged(bool State) +{ + UNUSED(State); + + // Forse Evaluation of Cat Port setting + + PTTPortChanged(0); +} + +void QtSoundModem::CATChanged(bool State) +{ + UNUSED(State); + PTTPortChanged(0); +} + +void QtSoundModem::PTTPortChanged(int Selected) +{ + UNUSED(Selected); + + QVariant Q = Dev->PTTPort->currentText(); + strcpy(NewPTTPort, Q.toString().toUtf8()); + + Dev->RTSDTR->setVisible(false); + Dev->CAT->setVisible(false); + + Dev->PTTOnLab->setVisible(false); + Dev->PTTOn->setVisible(false); + Dev->PTTOff->setVisible(false); + Dev->PTTOffLab->setVisible(false); + Dev->CATLabel->setVisible(false); + Dev->CATSpeed->setVisible(false); + + Dev->GPIOLab->setVisible(false); + Dev->GPIOLeft->setVisible(false); + Dev->GPIORight->setVisible(false); + Dev->GPIOLab2->setVisible(false); + + Dev->CM108Label->setVisible(false); + Dev->VIDPID->setVisible(false); + + if (strcmp(NewPTTPort, "None") == 0) + { + } + else if (strcmp(NewPTTPort, "GPIO") == 0) + { + Dev->GPIOLab->setVisible(true); + Dev->GPIOLeft->setVisible(true); + if (Dev->DualPTT->isChecked()) + { + Dev->GPIORight->setVisible(true); + Dev->GPIOLab2->setVisible(true); + } + } + + else if (strcmp(NewPTTPort, "CM108") == 0) + { + Dev->CM108Label->setVisible(true); +//#ifdef __ARM_ARCHX + Dev->CM108Label->setText("CM108 Device"); +//#else +// Dev->CM108Label->setText("CM108 VID/PID"); +//#endif + Dev->VIDPID->setText(CM108Addr); + Dev->VIDPID->setVisible(true); + } + else if (strcmp(NewPTTPort, "HAMLIB") == 0) + { + Dev->CM108Label->setVisible(true); + Dev->CM108Label->setText("rigctrld Port"); + Dev->VIDPID->setText(QString::number(HamLibPort)); + Dev->VIDPID->setVisible(true); + Dev->PTTOnLab->setText("rigctrld Host"); + Dev->PTTOnLab->setVisible(true); + Dev->PTTOn->setText(HamLibHost); + Dev->PTTOn->setVisible(true); + } + else + { + Dev->RTSDTR->setVisible(true); + Dev->CAT->setVisible(true); + + if (Dev->CAT->isChecked()) + { + Dev->PTTOnLab->setVisible(true); + Dev->PTTOnLab->setText("PTT On String"); + Dev->PTTOn->setText(PTTOnString); + Dev->PTTOn->setVisible(true); + Dev->PTTOff->setVisible(true); + Dev->PTTOff->setText(PTTOffString); + Dev->PTTOffLab->setVisible(true); + Dev->CATLabel->setVisible(true); + Dev->CATSpeed->setVisible(true); + } + } +} + +bool myResize::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Resize) + { + QResizeEvent *resizeEvent = static_cast(event); + QSize size = resizeEvent->size(); + int h = size.height(); + int w = size.width(); + + if (obj == deviceUI) + Dev->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10)); + else + Dlg->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10)); + + return true; + } + return QObject::eventFilter(obj, event); +} + +void QtSoundModem::doDevices() +{ + char valChar[10]; + + Dev = new(Ui_devicesDialog); + + QDialog UI; + + int i; + + Dev->setupUi(&UI); + + deviceUI = &UI; + modemUI = 0; + + myResize *resize = new myResize(); + + UI.installEventFilter(resize); + + newSoundMode = SoundMode; + oldSoundMode = SoundMode; + +#ifdef WIN32 + Dev->ALSA->setText("WaveOut"); + Dev->OSS->setVisible(0); + Dev->PULSE->setVisible(0); +#endif + + if (SoundMode == 0) + Dev->ALSA->setChecked(1); + else if (SoundMode == 1) + Dev->OSS->setChecked(1); + else if (SoundMode == 2) + Dev->PULSE->setChecked(1); + else if (SoundMode == 2) + Dev->UDP->setChecked(1); + + connect(Dev->ALSA, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + connect(Dev->OSS, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + connect(Dev->PULSE, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + connect(Dev->UDP, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + + for (i = 0; i < PlaybackCount; i++) + Dev->outputDevice->addItem(&PlaybackNames[i][0]); + + i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains); + + + if (i == -1) + { + // Add device to list + + Dev->outputDevice->addItem(PlaybackDevice); + i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains); + } + + Dev->outputDevice->setCurrentIndex(i); + + for (i = 0; i < CaptureCount; i++) + Dev->inputDevice->addItem(&CaptureNames[i][0]); + + i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains); + + if (i == -1) + { + // Add device to list + + Dev->inputDevice->addItem(CaptureDevice); + i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains); + } + Dev->inputDevice->setCurrentIndex(i); + + Dev->Modem_1_Chan->setCurrentIndex(soundChannel[0]); + Dev->Modem_2_Chan->setCurrentIndex(soundChannel[1]); + Dev->Modem_3_Chan->setCurrentIndex(soundChannel[2]); + Dev->Modem_4_Chan->setCurrentIndex(soundChannel[3]); + + // Disable "None" option in first modem + + QStandardItemModel *model = dynamic_cast(Dev->Modem_1_Chan->model()); + QStandardItem * item = model->item(0, 0); + item->setEnabled(false); + + Dev->singleChannelOutput->setChecked(SCO); + Dev->colourWaterfall->setChecked(raduga); + + sprintf(valChar, "%d", KISSPort); + Dev->KISSPort->setText(valChar); + Dev->KISSEnabled->setChecked(KISSServ); + + sprintf(valChar, "%d", AGWPort); + Dev->AGWPort->setText(valChar); + Dev->AGWEnabled->setChecked(AGWServ); + + Dev->PTTOn->setText(PTTOnString); + Dev->PTTOff->setText(PTTOffString); + + sprintf(valChar, "%d", PTTBAUD); + Dev->CATSpeed->setText(valChar); + + sprintf(valChar, "%d", UDPClientPort); + Dev->UDPPort->setText(valChar); + Dev->UDPTXHost->setText(UDPHost); + + if (UDPServerPort != TXPort) + sprintf(valChar, "%d/%d", UDPServerPort, TXPort); + else + sprintf(valChar, "%d", UDPServerPort); + + Dev->UDPTXPort->setText(valChar); + + Dev->UDPEnabled->setChecked(UDPServ); + + sprintf(valChar, "%d", pttGPIOPin); + Dev->GPIOLeft->setText(valChar); + sprintf(valChar, "%d", pttGPIOPinR); + Dev->GPIORight->setText(valChar); + + Dev->VIDPID->setText(CM108Addr); + + QStringList items; + + connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool))); + connect(Dev->DualPTT, SIGNAL(toggled(bool)), this, SLOT(DualPTTChanged(bool))); + connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int))); + + if (PTTMode == PTTCAT) + Dev->CAT->setChecked(true); + else + Dev->RTSDTR->setChecked(true); + + for (const QSerialPortInfo &info : Ports) + { + items.append(info.portName()); + } + + items.sort(); + + Dev->PTTPort->addItem("None"); + Dev->PTTPort->addItem("CM108"); + + //#ifdef __ARM_ARCH + + Dev->PTTPort->addItem("GPIO"); + + //#endif + + Dev->PTTPort->addItem("HAMLIB"); + + for (const QString &info : items) + { + Dev->PTTPort->addItem(info); + } + + Dev->PTTPort->setCurrentIndex(Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString)); + + PTTPortChanged(0); // Force reevaluation + + Dev->txRotation->setChecked(TX_rotate); + Dev->DualPTT->setChecked(DualPTT); + + Dev->multiCore->setChecked(multiCore); + + 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::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(); + strcpy(PTTPort, Q.toString().toUtf8()); + + DualPTT = Dev->DualPTT->isChecked(); + TX_rotate = Dev->txRotation->isChecked(); + multiCore = Dev->multiCore->isChecked(); + + if (Dev->CAT->isChecked()) + PTTMode = PTTCAT; + else + PTTMode = PTTRTS; + + Q = Dev->PTTOn->text(); + strcpy(PTTOnString, Q.toString().toUtf8()); + Q = Dev->PTTOff->text(); + strcpy(PTTOffString, Q.toString().toUtf8()); + + Q = Dev->CATSpeed->text(); + PTTBAUD = Q.toInt(); + + Q = Dev->UDPPort->text(); + UDPClientPort = Q.toInt(); + + + Q = Dev->UDPTXPort->text(); + strcpy(portString, Q.toString().toUtf8()); + UDPServerPort = atoi(portString); + + if (strchr(portString, '/')) + { + char * ptr = strlop(portString, '/'); + TXPort = atoi(ptr); + } + else + TXPort = UDPServerPort; + + Q = Dev->UDPTXHost->text(); + strcpy(UDPHost, Q.toString().toUtf8()); + + UDPServ = Dev->UDPEnabled->isChecked(); + + Q = Dev->GPIOLeft->text(); + pttGPIOPin = Q.toInt(); + + Q = Dev->GPIORight->text(); + pttGPIOPinR = Q.toInt(); + + Q = Dev->VIDPID->text(); + + if (strcmp(PTTPort, "CM108") == 0) + strcpy(CM108Addr, Q.toString().toUtf8()); + else if (strcmp(PTTPort, "HAMLIB") == 0) + { + HamLibPort = Q.toInt(); + Q = Dev->PTTOn->text(); + strcpy(HamLibHost, Q.toString().toUtf8()); + } + + 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(); + + wf_pointer(soundChannel[0]); + wf_pointer(soundChannel[1]); + + delete(Dev); + saveSettings(); + deviceUI->accept(); + + if (cardChanged) + { + InitSound(1); + } + + // Reset title and tooltip in case ports changed + + char Title[128]; + sprintf(Title, "QtSoundModem Version %s Ports %d/%d", VersionString, AGWPort, KISSPort); + w->setWindowTitle(Title); + + sprintf(Title, "QtSoundModem %d %d", AGWPort, KISSPort); + if (trayIcon) + trayIcon->setToolTip(Title); + + QSize newSize(this->size()); + QSize oldSize(this->size()); + + QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize); + + QCoreApplication::postEvent(this, myResizeEvent); +} + +void QtSoundModem::devicereject() +{ + delete(Dev); + deviceUI->reject(); +} + +void QtSoundModem::handleButton(int Port, int Type) +{ + // interlock calib with CWID + + if (calib_mode[0] == 4) // CWID + return; + + doCalib(Port, Type); +} + +void QtSoundModem::doRestartWF() +{ + if (Firstwaterfall) + { + initWaterfall(0, 0); + initWaterfall(0, 1); + } + + if (Secondwaterfall) + { + initWaterfall(1, 0); + initWaterfall(1, 1); + } +} + + +void QtSoundModem::doAbout() +{ + QMessageBox::about(this, tr("About"), + tr("G8BPQ's port of UZ7HO's Soundmodem\n\nCopyright (C) 2019-2020 Andrei Kopanchuk UZ7HO")); +} + +void QtSoundModem::doCalibrate() +{ + Ui_calDialog Calibrate; + { + QDialog UI; + Calibrate.setupUi(&UI); + + connect(Calibrate.Low_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Low_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Low_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Low_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_D, SIGNAL(released()), this, SLOT(clickedSlot())); + + /* + + connect(Calibrate.Low_A, &QPushButton::released, this, [=] { handleButton(0, 1); }); + connect(Calibrate.High_A, &QPushButton::released, this, [=] { handleButton(0, 2); }); + connect(Calibrate.Both_A, &QPushButton::released, this, [=] { handleButton(0, 3); }); + connect(Calibrate.Stop_A, &QPushButton::released, this, [=] { handleButton(0, 0); }); + connect(Calibrate.Low_B, &QPushButton::released, this, [=] { handleButton(1, 1); }); + connect(Calibrate.High_B, &QPushButton::released, this, [=] { handleButton(1, 2); }); + connect(Calibrate.Both_B, &QPushButton::released, this, [=] { handleButton(1, 3); }); + connect(Calibrate.Stop_B, &QPushButton::released, this, [=] { handleButton(1, 0); }); + +// connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(handleButton(1, 2))); +*/ + UI.exec(); + } +} + +void QtSoundModem::RefreshSpectrum(unsigned char * Data) +{ + int i; + + // Last 4 bytes are level busy and Tuning lines + + Waterfall[0]->fill(Black); + + if (Data[206] != LastLevel) + { + LastLevel = Data[206]; +// RefreshLevel(LastLevel); + } + + if (Data[207] != LastBusy) + { + LastBusy = Data[207]; +// Busy->setVisible(LastBusy); + } + + for (i = 0; i < 205; i++) + { + int val = Data[0]; + + if (val > 63) + val = 63; + + Waterfall[0]->setPixel(i, val, Yellow); + if (val < 62) + Waterfall[0]->setPixel(i, val + 1, Gold); + Data++; + } + + ui.WaterfallA->setPixmap(QPixmap::fromImage(*Waterfall[0])); + +} + +void QtSoundModem::RefreshWaterfall(int snd_ch, unsigned char * Data) +{ + int j; + unsigned char * Line; + int len = Waterfall[0]->bytesPerLine(); + int TopLine = NextWaterfallLine[snd_ch]; + + // Write line to cyclic buffer then draw starting with the line just written + + // Length is 208 bytes, including Level and Busy flags + + memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Data, 206); + if (NextWaterfallLine[snd_ch] > 63) + NextWaterfallLine[snd_ch] = 0; + + for (j = 63; j > 0; j--) + { + Line = Waterfall[0]->scanLine(j); + memcpy(Line, &WaterfallLines[snd_ch][TopLine++][0], len); + if (TopLine > 63) + TopLine = 0; + } + + ui.WaterfallA->setPixmap(QPixmap::fromImage(*Waterfall[0])); +} + + +void QtSoundModem::sendtoTrace(char * Msg, int tx) +{ + const QTextCursor old_cursor = monWindowCopy->textCursor(); + const int old_scrollbar_value = monWindowCopy->verticalScrollBar()->value(); + const bool is_scrolled_down = old_scrollbar_value == monWindowCopy->verticalScrollBar()->maximum(); + + // Move the cursor to the end of the document. + monWindowCopy->moveCursor(QTextCursor::End); + + // Insert the text at the position of the cursor (which is the end of the document). + + if (tx) + monWindowCopy->setTextColor(qRgb(192, 0, 0)); + else + monWindowCopy->setTextColor(qRgb(0, 0, 192)); + + monWindowCopy->textCursor().insertText(Msg); + + if (old_cursor.hasSelection() || !is_scrolled_down) + { + // The user has selected text or scrolled away from the bottom: maintain position. + monWindowCopy->setTextCursor(old_cursor); + monWindowCopy->verticalScrollBar()->setValue(old_scrollbar_value); + } + else + { + // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. + monWindowCopy->moveCursor(QTextCursor::End); + monWindowCopy->verticalScrollBar()->setValue(monWindowCopy->verticalScrollBar()->maximum()); + } + + free(Msg); +} + + +// I think this does the waterfall + +typedef struct TRGBQ_t +{ + Byte b, g, r, re; + +} TRGBWQ; + +typedef struct tagRECT +{ + int left; + int top; + int right; + int bottom; +} RECT; + +unsigned int RGBWF[256] ; + + +extern "C" void init_raduga() +{ + Byte offset[6] = {0, 51, 102, 153, 204}; + Byte i, n; + + for (n = 0; n < 52; n++) + { + i = n * 5; + + RGBWF[n + offset[0]] = qRgb(0, 0, i); + RGBWF[n + offset[1]] = qRgb(0, i, 255); + RGBWF[n + offset[2]] = qRgb(0, 255, 255 - i); + RGBWF[n + offset[3]] = qRgb(1, 255, 0); + RGBWF[n + offset[4]] = qRgb(255, 255 - 1, 0); + } +} + +extern "C" int nonGUIMode; + + +// This draws the Frequency Scale on Waterfall + +extern "C" void wf_Scale(int Chan) +{ + if (nonGUIMode) + return; + + int x, i; + char Textxx[20]; + QImage * bm = Header[Chan]; + + QPainter qPainter(bm); + qPainter.setBrush(Qt::black); + qPainter.setPen(Qt::white); + + if (Chan == 0) + sprintf(Textxx, "Left"); + else + sprintf(Textxx, "Right"); + +#ifdef WIN32 + int Top = 3; +#else + int Top = 4; +#endif + + 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, 22, x, 15); + else + qPainter.drawLine(x, 22, x, 18); + + if ((Freq % 500) == 0) + { + sprintf(Textxx, "%d", Freq); + + if (x < 924) + qPainter.drawText(x - 12, Top, 100, 20, 0, Textxx); + } + } + Freq += 100; + } + HeaderCopy[Chan]->setPixmap(QPixmap::fromImage(*bm)); + +} + +// This draws the frequency Markers on the Waterfall + + +void do_pointer(int waterfall) +{ + if (nonGUIMode) + return; + + + int x1, x2, k, pos1, pos2, pos3; + QImage * bm = Header[waterfall]; + + QPainter qPainter(bm); + qPainter.setBrush(Qt::NoBrush); + qPainter.setPen(Qt::white); + + // bm->fill(black); + + qPainter.fillRect(0, 23, 1024, 10, Qt::black); + + // We drew markers every 100 Hz or 100 / binsize pixels + + float PixelsPerHz = 1.0 / BinSize; + k = 28; + + // draw all enabled ports on the ports on this soundcard + + // First Modem is always on the first waterfall + // If second is enabled it is on the first unless different + // channel from first + + for (int i = 0; i < 4; i++) + { + if (UsingBothChannels == 0) + { + // Only One Waterfall. If first chan is + + if ((waterfall == 0 && soundChannel[i] == RIGHT) || (waterfall == 1 && soundChannel[i] == LEFT)) + return; + } + + if (soundChannel[i] == 0) + continue; + + + if (UsingBothChannels == 1) + if ((waterfall == 0 && soundChannel[i] == RIGHT) || (waterfall == 1 && soundChannel[i] == LEFT)) + continue; + + pos1 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) - 0.5*rx_shift[i]) - 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); + } + } + HeaderCopy[waterfall]->setPixmap(QPixmap::fromImage(*bm)); +} + +void wf_pointer(int snd_ch) +{ + UNUSED(snd_ch); + + do_pointer(0); + do_pointer(1); +// do_pointer(2); +// do_pointer(3); +} + + +void doWaterfallThread(void * param); + +/* +#ifdef WIN32 + +#define pthread_t uintptr_t + +extern "C" uintptr_t _beginthread(void(__cdecl *start_address)(void *), unsigned stack_size, void *arglist); + +#else + +#include + +extern "C" pthread_t _beginthread(void(*start_address)(void *), unsigned stack_size, void * arglist) +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*)arglist) != 0) + perror("New Thread"); + else + pthread_detach(thread); + + return thread; +} + +#endif +*/ +extern "C" void doWaterfall(int snd_ch) +{ + if (nonGUIMode) + return; + + if (Closing) + return; + +// if (multiCore) // Run modems in separate threads +// _beginthread(doWaterfallThread, 0, xx); +// else + doWaterfallThread((void *)(size_t)snd_ch); + +} + + +extern "C" float aFFTAmpl[1024]; + +void doWaterfallThread(void * param) +{ + int snd_ch = (int)(size_t)param; + + QImage * bm = Waterfall[snd_ch]; + + 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]; + + if (Configuring) + return; + + 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); + } + } + + if (bm == 0) + return; + + p = Line; + lineLen = bm->bytesPerLine(); + + if (raduga == DISP_MONO) + { + for (i = Start; i < End; i++) + { + n = fft_disp[snd_ch][i]; + *(p++) = n; // all colours the same + *(p++) = n; + *(p++) = n; + p++; + } + } + else + { + for (i = Start; i < End; i++) + { + n = fft_disp[snd_ch][i]; + memcpy(p, &RGBWF[n], 4); + p += 4; + } + } + + // Scroll + + int TopLine = NextWaterfallLine[snd_ch]; + + // Write line to cyclic buffer then draw starting with the line just written + + memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Line, 4096); + if (NextWaterfallLine[snd_ch] > 79) + NextWaterfallLine[snd_ch] = 0; + + for (int j = 79; j > 0; j--) + { + p = bm->scanLine(j); + memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen); + TopLine++; + if (TopLine > 79) + TopLine = 0; + } + + WaterfallCopy[snd_ch]->setPixmap(QPixmap::fromImage(*bm)); + // WaterfallCopy[snd_ch - 1]->setPixmap(*pm); + // WaterfallCopy[1]->setPixmap(QPixmap::fromImage(*bm)); + +} + + + +void QtSoundModem::changeEvent(QEvent* e) +{ + if (e->type() == QEvent::WindowStateChange) + { + QWindowStateChangeEvent* ev = static_cast(e); + + qDebug() << windowState(); + + if (!(ev->oldState() & Qt::WindowMinimized) && windowState() & Qt::WindowMinimized) + { + if (trayIcon) + setVisible(false); + } +// if (!(ev->oldState() != Qt::WindowNoState) && windowState() == Qt::WindowNoState) +// { +// QMessageBox::information(this, "", "Window has been restored"); +// } + + } + QWidget::changeEvent(e); +} + +#include + +void QtSoundModem::closeEvent(QCloseEvent *event) +{ + UNUSED(event); + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + mysettings.setValue("geometry", QWidget::saveGeometry()); + mysettings.setValue("windowState", saveState()); + + Closing = TRUE; + qDebug() << "Closing"; + + QThread::msleep(100); +} + + +QtSoundModem::~QtSoundModem() +{ + qDebug() << "Saving Settings"; + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + mysettings.setValue("geometry", saveGeometry()); + mysettings.setValue("windowState", saveState()); + + saveSettings(); + Closing = TRUE; + qDebug() << "Closing"; + + QThread::msleep(100); +} + +extern "C" void QSleep(int ms) +{ + QThread::msleep(ms); +} + +int upd_time = 30; + +void QtSoundModem::show_grid() +{ + // This refeshes the session list + + int snd_ch, i, num_rows, row_idx; + QTableWidgetItem *item; + const char * msg; + + int speed_tx, speed_rx; + + if (grid_time < 10) + { + grid_time++; + return; + } + + grid_time = 0; + + //label7.Caption = inttostr(stat_r_mem); mem_arq + + num_rows = 0; + row_idx = 0; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (i = 0; i < port_num; i++) + { + if (AX25Port[snd_ch][i].status != STAT_NO_LINK) + num_rows++; + } + } + + if (num_rows == 0) + { + sessionTable->clearContents(); + sessionTable->setRowCount(0); + sessionTable->setRowCount(1); + } + else + sessionTable->setRowCount(num_rows); + + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (i = 0; i < port_num; i++) + { + if (AX25Port[snd_ch][i].status != STAT_NO_LINK) + { + switch (AX25Port[snd_ch][i].status) + { + case STAT_NO_LINK: + + msg = "No link"; + break; + + case STAT_LINK: + + msg = "Link"; + break; + + case STAT_CHK_LINK: + + msg = "Chk link"; + break; + + case STAT_WAIT_ANS: + + msg = "Wait ack"; + break; + + case STAT_TRY_LINK: + + msg = "Try link"; + break; + + case STAT_TRY_UNLINK: + + msg = "Try unlink"; + } + + + item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].mycall); + sessionTable->setItem(row_idx, 0, item); + + item = new QTableWidgetItem(AX25Port[snd_ch][i].kind); + sessionTable->setItem(row_idx, 11, item); + + item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].corrcall); + sessionTable->setItem(row_idx, 1, item); + + item = new QTableWidgetItem(msg); + sessionTable->setItem(row_idx, 2, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_pkt)); + sessionTable->setItem(row_idx, 3, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_byte)); + sessionTable->setItem(row_idx, 4, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_pkt)); + sessionTable->setItem(row_idx, 5, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_byte)); + sessionTable->setItem(row_idx, 6, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_fc)); + sessionTable->setItem(row_idx, 7, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_fec_count)); + sessionTable->setItem(row_idx, 8, item); + + if (grid_timer != upd_time) + grid_timer++; + else + { + grid_timer = 0; + speed_tx = round(abs(AX25Port[snd_ch][i].info.stat_s_byte - AX25Port[snd_ch][i].info.stat_l_s_byte) / upd_time); + speed_rx = round(abs(AX25Port[snd_ch][i].info.stat_r_byte - AX25Port[snd_ch][i].info.stat_l_r_byte) / upd_time); + + item = new QTableWidgetItem(QString::number(speed_tx)); + sessionTable->setItem(row_idx, 9, item); + + item = new QTableWidgetItem(QString::number(speed_rx)); + sessionTable->setItem(row_idx, 10, item); + + AX25Port[snd_ch][i].info.stat_l_r_byte = AX25Port[snd_ch][i].info.stat_r_byte; + AX25Port[snd_ch][i].info.stat_l_s_byte = AX25Port[snd_ch][i].info.stat_s_byte; + } + + row_idx++; + } + } + } +} + +// "Copy on Select" Code + +void QtSoundModem::onTEselectionChanged() +{ + QTextEdit * x = static_cast(QObject::sender()); + x->copy(); +} + diff --git a/QtSoundModem.cpp.bak b/QtSoundModem.cpp.bak new file mode 100644 index 0000000..3efef28 --- /dev/null +++ b/QtSoundModem.cpp.bak @@ -0,0 +1,2866 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +// UZ7HO Soundmodem Port + +// Not Working 4psk100 FEC + +// Thoughts on Waterfall Display. + +// Original used a 2048 sample FFT giving 5.859375 Hz bins. We plotted 1024 points, giving a 0 to 6000 specrum + +// If we want say 300 to 3300 we need about half the bin size so twice the fft size. But should we also fit required range to window size? + +// Unless we resize the most displayed bit of the screen in around 900 pixels. So each bin should be 3300 / 900 = 3.66667 Hz or a FFT size of around 3273 + +#include "QtSoundModem.h" +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "UZ7HOStuff.h" + + +QImage *Constellation; +QImage *Waterfall[4] = { 0,0,0,0 }; +QImage *Header[4]; +QLabel *DCDLabel[4]; +QLineEdit *chanOffsetLabel[4]; +QImage *DCDLed[4]; + +QImage *RXLevel; + +QLabel *WaterfallCopy[2]; +QLabel *HeaderCopy[2]; + +QTextEdit * monWindowCopy; + +extern workerThread *t; +extern QtSoundModem * w; + +QList Ports = QSerialPortInfo::availablePorts(); + +void saveSettings(); +void getSettings(); +extern "C" void CloseSound(); +extern "C" void GetSoundDevices(); +extern "C" char modes_name[modes_count][20]; +extern "C" int speed[5]; +extern "C" int KISSPort; +extern "C" short rx_freq[5]; + +extern "C" int CaptureCount; +extern "C" int PlaybackCount; + +extern "C" int CaptureIndex; // Card number +extern "C" int PlayBackIndex; + +extern "C" char CaptureNames[16][256]; +extern "C" char PlaybackNames[16][256]; + +extern "C" int SoundMode; +extern "C" int multiCore; + +extern "C" int refreshModems; + +extern "C" int pnt_change[5]; +extern "C" int needRSID[4]; + +extern "C" int needSetOffset[4]; + +extern "C" float MagOut[4096]; +extern "C" float MaxMagOut; +extern "C" int MaxMagIndex; + +extern "C" +{ + int InitSound(BOOL Report); + void soundMain(); + void MainLoop(); + void modulator(UCHAR snd_ch, int buf_size); + void SampleSink(int LR, short Sample); + void doCalib(int Port, int Act); + int Freq_Change(int Chan, int Freq); + void set_speed(int snd_ch, int Modem); + void init_speed(int snd_ch); + void wf_pointer(int snd_ch); + void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); + void dofft(short * in, float * outr, float * outi); + void init_raduga(); + void wf_Scale(int Chan); + void AGW_Report_Modem_Change(int port); + char * strlop(char * buf, char delim); + void sendRSID(int Chan, int dropTX); + void RSIDinitfft(); + void il2p_init(int il2p_debug); +} + +void make_graph_buf(float * buf, short tap, QPainter * bitmap); + +int ModemA = 2; +int ModemB = 2; +int ModemC = 2; +int ModemD = 2; +int FreqA = 1500; +int FreqB = 1500; +int FreqC = 1500; +int FreqD = 1500; +int DCD = 50; + +char CWIDCall[128] = ""; +int CWIDInterval = 0; +int CWIDLeft = 0; +int CWIDRight = 0; +int CWIDType = 1; // on/off + +extern "C" { int RSID_SABM[4]; } +extern "C" { int RSID_UI[4]; } +extern "C" { int RSID_SetModem[4]; } + +int Closing = FALSE; // Set to stop background thread + +QRgb white = qRgb(255, 255, 255); +QRgb black = qRgb(0, 0, 0); + +QRgb green = qRgb(0, 255, 0); +QRgb red = qRgb(255, 0, 0); +QRgb yellow = qRgb(255, 255, 0); +QRgb cyan = qRgb(0, 255, 255); + +// Indexed colour list from ARDOPC + +#define WHITE 0 +#define Tomato 1 +#define Gold 2 +#define Lime 3 +#define Yellow 4 +#define Orange 5 +#define Khaki 6 +#define Cyan 7 +#define DeepSkyBlue 8 +#define RoyalBlue 9 +#define Navy 10 +#define Black 11 +#define Goldenrod 12 +#define Fuchsia 13 + +QRgb vbColours[16] = { qRgb(255, 255, 255), qRgb(255, 99, 71), qRgb(255, 215, 0), qRgb(0, 255, 0), + qRgb(255, 255, 0), qRgb(255, 165, 0), qRgb(240, 240, 140), qRgb(0, 255, 255), + qRgb(0, 191, 255), qRgb(65, 105, 225), qRgb(0, 0, 128), qRgb(0, 0, 0), + qRgb(218, 165, 32), qRgb(255, 0, 255) }; + +unsigned char WaterfallLines[2][80][4096] = { 0 }; +int NextWaterfallLine[2] = { 0 }; + +unsigned int LastLevel = 255; +unsigned int LastBusy = 255; + +extern "C" int UDPClientPort; +extern "C" int UDPServerPort; +extern "C" int TXPort; +extern char UDPHost[64]; + +QTimer *cwidtimer; + +QSystemTrayIcon * trayIcon = nullptr; + +int MintoTray = 1; + +int RSID_WF = 0; // Set to use RSID FFT for Waterfall. + +extern "C" void WriteDebugLog(char * Mess) +{ + qDebug() << Mess; +} + +void QtSoundModem::doupdateDCD(int Chan, int State) +{ + DCDLabel[Chan]->setVisible(State); +} + +extern "C" char * frame_monitor(string * frame, char * code, bool tx_stat); +extern "C" char * ShortDateTime(); + +extern "C" void mon_rsid(int snd_ch, char * RSID) +{ + int Len; + char * Msg = (char *)malloc(1024); // Cant pass local variable via signal/slot + + sprintf(Msg, "%d:%s [%s%c]", snd_ch + 1, RSID, ShortDateTime(), 'R'); + + Len = strlen(Msg); + + if (Msg[Len - 1] != '\r') + { + Msg[Len++] = '\r'; + Msg[Len] = 0; + } + + emit t->sendtoTrace(Msg, 0); +} + +extern "C" void put_frame(int snd_ch, string * frame, char * code, int tx, int excluded) +{ + UNUSED(excluded); + + int Len; + char * Msg = (char *)malloc(1024); // Cant pass local variable via signal/slot + + if (strcmp(code, "NON-AX25") == 0) + sprintf(Msg, "%d: Length, ShortDateTime(), 'R'); + else + sprintf(Msg, "%d:%s", snd_ch + 1, frame_monitor(frame, code, tx)); + + Len = strlen(Msg); + + if (Msg[Len - 1] != '\r') + { + Msg[Len++] = '\r'; + Msg[Len] = 0; + } + + emit t->sendtoTrace(Msg, tx); +} + +extern "C" void updateDCD(int Chan, bool State) +{ + emit t->updateDCD(Chan, State); +} + +bool QtSoundModem::eventFilter(QObject* obj, QEvent *evt) +{ + UNUSED(obj); + + if (evt->type() == QEvent::Resize) + { + return QWidget::event(evt); + } + + if (evt->type() == QEvent::WindowStateChange) + { + if (windowState().testFlag(Qt::WindowMinimized) == true) + w_state = WIN_MINIMIZED; + else + w_state = WIN_MAXIMIZED; + } +// if (evt->type() == QGuiApplication::applicationStateChanged) - this is a sigma; +// { +// qDebug() << "App State changed =" << evt->type() << endl; +// } + + return QWidget::event(evt); +} + +void QtSoundModem::resizeEvent(QResizeEvent* event) +{ + QMainWindow::resizeEvent(event); + + QRect r = geometry(); + + int A, B, C, W; + int modemBoxHeight = 30; + + ui.modeB->setVisible(soundChannel[1]); + ui.centerB->setVisible(soundChannel[1]); + ui.labelB->setVisible(soundChannel[1]); + DCDLabel[1]->setVisible(soundChannel[1]); + ui.RXOffsetB->setVisible(soundChannel[1]); + + ui.modeC->setVisible(soundChannel[2]); + ui.centerC->setVisible(soundChannel[2]); + ui.labelC->setVisible(soundChannel[2]); + DCDLabel[2]->setVisible(soundChannel[2]); + ui.RXOffsetC->setVisible(soundChannel[2]); + + ui.modeD->setVisible(soundChannel[3]); + ui.centerD->setVisible(soundChannel[3]); + ui.labelD->setVisible(soundChannel[3]); + DCDLabel[3]->setVisible(soundChannel[3]); + ui.RXOffsetD->setVisible(soundChannel[3]); + + if (soundChannel[2] || soundChannel[3]) + modemBoxHeight = 60; + + + A = r.height() - 25; // No waterfalls + + if (UsingBothChannels && Secondwaterfall) + { + // Two waterfalls + + ui.WaterfallA->setVisible(1); + ui.HeaderA->setVisible(1); + ui.WaterfallB->setVisible(1); + ui.HeaderB->setVisible(1); + + A = r.height() - 258; // Top of Waterfall A + B = A + 115; // Top of Waterfall B + } + else + { + // One waterfall + + // Could be Left or Right + + if (Firstwaterfall) + { + if (soundChannel[0] == RIGHT) + { + ui.WaterfallA->setVisible(0); + ui.HeaderA->setVisible(0); + ui.WaterfallB->setVisible(1); + ui.HeaderB->setVisible(1); + } + else + { + ui.WaterfallA->setVisible(1); + ui.HeaderA->setVisible(1); + ui.WaterfallB->setVisible(0); + ui.HeaderB->setVisible(0); + } + + A = r.height() - 145; // Top of Waterfall A + } + else + A = r.height() - 25; // Top of Waterfall A + } + + C = A - 150; // Bottom of Monitor, Top of connection list + W = r.width(); + + // Calc Positions of Waterfalls + + ui.monWindow->setGeometry(QRect(0, modemBoxHeight, W, C - (modemBoxHeight + 26))); + sessionTable->setGeometry(QRect(0, C, W, 175)); + + if (UsingBothChannels) + { + ui.HeaderA->setGeometry(QRect(0, A, W, 35)); + ui.WaterfallA->setGeometry(QRect(0, A + 35, W, 80)); + ui.HeaderB->setGeometry(QRect(0, B, W, 35)); + ui.WaterfallB->setGeometry(QRect(0, B + 35, W, 80)); + } + else + { + if (soundChannel[0] == RIGHT) + { + ui.HeaderB->setGeometry(QRect(0, A, W, 35)); + ui.WaterfallB->setGeometry(QRect(0, A + 35, W, 80)); + } + else + { + ui.HeaderA->setGeometry(QRect(0, A, W, 35)); + ui.WaterfallA->setGeometry(QRect(0, A + 35, W, 80)); + } + } +} + +QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State) +{ + QAction * Act = new QAction(Label, parent); + Menu->addAction(Act); + + Act->setCheckable(true); + if (State) + Act->setChecked(true); + + parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked())); + + return Act; +} + +void QtSoundModem::menuChecked() +{ + QAction * Act = static_cast(QObject::sender()); + + int state = Act->isChecked(); + + if (Act == actWaterfall1) + { + int oldstate = Firstwaterfall; + Firstwaterfall = state; + + if (state != oldstate) + initWaterfall(0, state); + + } + else if (Act == actWaterfall2) + { + int oldstate = Secondwaterfall; + Secondwaterfall = state; + + if (state != oldstate) + initWaterfall(1, state); + + } + saveSettings(); +} + +void QtSoundModem::initWaterfall(int chan, int state) +{ + if (state == 1) + { + if (chan == 0) + { + ui.WaterfallA = new QLabel(ui.centralWidget); + WaterfallCopy[0] = ui.WaterfallA; + } + else + { + ui.WaterfallB = new QLabel(ui.centralWidget); + WaterfallCopy[1] = ui.WaterfallB; + } + Waterfall[chan] = new QImage(1024, 80, QImage::Format_RGB32); + Waterfall[chan]->fill(black); + + } + else + { + delete(Waterfall[chan]); + Waterfall[chan] = 0; + } + + QSize Size(800, 602); // Not actually used, but Event constructor needs it + QResizeEvent *event = new QResizeEvent(Size, Size); + QApplication::sendEvent(this, event); +} + +// Local copies + +QLabel *RXOffsetLabel; +QSlider *RXOffset; + +QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) +{ + ui.setupUi(this); + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + + if (MintoTray) + { + char popUp[256]; + sprintf(popUp, "QtSoundModem %d %d", AGWPort, KISSPort); + trayIcon = new QSystemTrayIcon(QIcon(":/QtSoundModem/soundmodem.ico"), this); + trayIcon->setToolTip(popUp); + trayIcon->show(); + + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(TrayActivated(QSystemTrayIcon::ActivationReason))); + } + + + restoreGeometry(mysettings.value("geometry").toByteArray()); + restoreState(mysettings.value("windowState").toByteArray()); + + sessionTable = new QTableWidget(this); + + sessionTable->verticalHeader()->setVisible(FALSE); + sessionTable->verticalHeader()->setDefaultSectionSize(20); + sessionTable->horizontalHeader()->setDefaultSectionSize(68); + sessionTable->setRowCount(1); + sessionTable->setColumnCount(12); + m_TableHeader << "MyCall" << "DestCall" << "Status" << "Sent pkts" << "Sent Bytes" << "Rcvd pkts" << "Rcvd bytes" << "Rcvd FC" << "FEC corr" << "CPS TX" << "CPS RX" << "Direction"; + + sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(224, 224, 224) }"); + + sessionTable->setHorizontalHeaderLabels(m_TableHeader); + sessionTable->setColumnWidth(0, 80); + sessionTable->setColumnWidth(1, 80); + sessionTable->setColumnWidth(4, 76); + sessionTable->setColumnWidth(5, 76); + sessionTable->setColumnWidth(6, 80); + sessionTable->setColumnWidth(11, 72); + + for (int i = 0; i < modes_count; i++) + { + ui.modeA->addItem(modes_name[i]); + ui.modeB->addItem(modes_name[i]); + ui.modeC->addItem(modes_name[i]); + ui.modeD->addItem(modes_name[i]); + } + + // Set up Menus + + setupMenu = ui.menuBar->addMenu(tr("Settings")); + + actDevices = new QAction("Setup Devices", this); + setupMenu->addAction(actDevices); + + connect(actDevices, SIGNAL(triggered()), this, SLOT(clickedSlot())); + actDevices->setObjectName("actDevices"); + actModems = new QAction("Setup Modems", this); + actModems->setObjectName("actModems"); + setupMenu->addAction(actModems); + + connect(actModems, SIGNAL(triggered()), this, SLOT(clickedSlot())); + + actMintoTray = setupMenu->addAction("Minimize to Tray", this, SLOT(MinimizetoTray())); + actMintoTray->setCheckable(1); + actMintoTray->setChecked(MintoTray); + + viewMenu = ui.menuBar->addMenu(tr("&View")); + + actWaterfall1 = setupMenuLine(viewMenu, (char *)"First waterfall", this, Firstwaterfall); + actWaterfall2 = setupMenuLine(viewMenu, (char *)"Second Waterfall", this, Secondwaterfall); + + actCalib = ui.menuBar->addAction("&Calibration"); + connect(actCalib, SIGNAL(triggered()), this, SLOT(doCalibrate())); + + actRestartWF = ui.menuBar->addAction("Restart Waterfall"); + connect(actRestartWF, SIGNAL(triggered()), this, SLOT(doRestartWF())); + + actAbout = ui.menuBar->addAction("&About"); + connect(actAbout, SIGNAL(triggered()), this, SLOT(doAbout())); + + // Constellation = new QImage(91, 91, QImage::Format_RGB32); + + Header[0] = new QImage(1024, 35, QImage::Format_RGB32); + Header[1] = new QImage(1024, 35, QImage::Format_RGB32); + RXLevel = new QImage(150, 10, QImage::Format_RGB32); + + DCDLabel[0] = new QLabel(this); + DCDLabel[0]->setObjectName(QString::fromUtf8("DCDLedA")); + DCDLabel[0]->setGeometry(QRect(280, 31, 12, 12)); + DCDLabel[0]->setVisible(TRUE); + + DCDLabel[1] = new QLabel(this); + DCDLabel[1]->setObjectName(QString::fromUtf8("DCDLedB")); + DCDLabel[1]->setGeometry(QRect(575, 31, 12, 12)); + DCDLabel[1]->setVisible(TRUE); + + DCDLabel[2] = new QLabel(this); + DCDLabel[2]->setObjectName(QString::fromUtf8("DCDLedC")); + DCDLabel[2]->setGeometry(QRect(280, 61, 12, 12)); + DCDLabel[2]->setVisible(FALSE); + + DCDLabel[3] = new QLabel(this); + DCDLabel[3]->setObjectName(QString::fromUtf8("DCDLedD")); + DCDLabel[3]->setGeometry(QRect(575, 61, 12, 12)); + DCDLabel[3]->setVisible(FALSE); + + DCDLed[0] = new QImage(12, 12, QImage::Format_RGB32); + DCDLed[1] = new QImage(12, 12, QImage::Format_RGB32); + DCDLed[2] = new QImage(12, 12, QImage::Format_RGB32); + DCDLed[3] = new QImage(12, 12, QImage::Format_RGB32); + + DCDLed[0]->fill(red); + DCDLed[1]->fill(red); + DCDLed[2]->fill(red); + DCDLed[3]->fill(red); + + DCDLabel[0]->setPixmap(QPixmap::fromImage(*DCDLed[0])); + DCDLabel[1]->setPixmap(QPixmap::fromImage(*DCDLed[1])); + DCDLabel[2]->setPixmap(QPixmap::fromImage(*DCDLed[2])); + DCDLabel[3]->setPixmap(QPixmap::fromImage(*DCDLed[3])); + + chanOffsetLabel[0] = ui.RXOffsetA; + chanOffsetLabel[1] = ui.RXOffsetB; + chanOffsetLabel[2] = ui.RXOffsetC; + chanOffsetLabel[3] = ui.RXOffsetD; + + + // Waterfall[0]->setColorCount(16); + // Waterfall[1]->setColorCount(16); + + + // for (i = 0; i < 16; i++) + // { + // Waterfall[0]->setColor(i, vbColours[i]); + // Waterfall[1]->setColor(i, vbColours[i]); + // } + + WaterfallCopy[0] = ui.WaterfallA; + WaterfallCopy[1] = ui.WaterfallB; + + initWaterfall(0, 1); + initWaterfall(1, 1); + + Header[0]->fill(black); + Header[1]->fill(black); + + HeaderCopy[0] = ui.HeaderA; + HeaderCopy[1] = ui.HeaderB; + monWindowCopy = ui.monWindow; + + ui.monWindow->document()->setMaximumBlockCount(10000); + +// connect(ui.monWindow, SIGNAL(selectionChanged()), this, SLOT(onTEselectionChanged())); + + ui.HeaderA->setPixmap(QPixmap::fromImage(*Header[0])); + ui.HeaderB->setPixmap(QPixmap::fromImage(*Header[1])); + + wf_pointer(soundChannel[0]); + wf_pointer(soundChannel[1]); + wf_Scale(0); + wf_Scale(1); + + // RefreshLevel(0); + // RXLevel->setPixmap(QPixmap::fromImage(*RXLevel)); + + connect(ui.modeA, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.modeB, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.modeC, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.modeD, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); + + ui.modeA->setCurrentIndex(speed[0]); + ui.modeB->setCurrentIndex(speed[1]); + ui.modeC->setCurrentIndex(speed[2]); + ui.modeD->setCurrentIndex(speed[3]); + + ModemA = ui.modeA->currentIndex(); + + ui.centerA->setValue(rx_freq[0]); + ui.centerB->setValue(rx_freq[1]); + ui.centerC->setValue(rx_freq[2]); + ui.centerD->setValue(rx_freq[3]); + + connect(ui.centerA, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.centerB, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.centerC, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + connect(ui.centerD, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + + ui.DCDSlider->setValue(dcd_threshold); + + + char valChar[32]; + sprintf(valChar, "RX Offset %d", rxOffset); + ui.RXOffsetLabel->setText(valChar); + ui.RXOffset->setValue(rxOffset); + + RXOffsetLabel = ui.RXOffsetLabel; + RXOffset = ui.RXOffset; + + connect(ui.DCDSlider, SIGNAL(sliderMoved(int)), this, SLOT(clickedSlotI(int))); + connect(ui.RXOffset, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); + + + QObject::connect(t, SIGNAL(sendtoTrace(char *, int)), this, SLOT(sendtoTrace(char *, int)), Qt::QueuedConnection); + QObject::connect(t, SIGNAL(updateDCD(int, int)), this, SLOT(doupdateDCD(int, int)), Qt::QueuedConnection); + + connect(ui.RXOffsetA, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(ui.RXOffsetB, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(ui.RXOffsetC, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + connect(ui.RXOffsetD, SIGNAL(returnPressed()), this, SLOT(returnPressed())); + + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); + timer->start(100); + + + cwidtimer = new QTimer(this); + connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer())); + + if (CWIDInterval) + cwidtimer->start(CWIDInterval * 60000); + + if (RSID_SetModem[0]) + { + RSID_WF = 1; + RSIDinitfft(); + } + il2p_init(1); +} + +void QtSoundModem::MinimizetoTray() +{ + MintoTray = actMintoTray->isChecked(); + saveSettings(); + QMessageBox::about(this, tr("QtSoundModem"), + tr("Program must be restarted to change Minimize mode")); +} + + +void QtSoundModem::TrayActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == 3) + { + showNormal(); + w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + } +} + +extern "C" void sendCWID(char * strID, BOOL blnPlay, int Chan); + +void QtSoundModem::CWIDTimer() +{ + sendCWID(CWIDCall, CWIDType, 0); + calib_mode[0] = 4; +} + +void extSetOffset(int chan) +{ + char valChar[32]; + sprintf(valChar, "%d", chanOffset[chan]); + chanOffsetLabel[chan]->setText(valChar); + + wf_pointer(soundChannel[chan]); + + pnt_change[0] = 1; + pnt_change[1] = 1; + pnt_change[2] = 1; + pnt_change[3] = 1; + + return; +} + +void QtSoundModem::MyTimerSlot() +{ + // 100 mS Timer Event + + for (int i = 0; i < 4; i++) + { + + if (needSetOffset[i]) + { + needSetOffset[i] = 0; + extSetOffset(i); // Update GUI + } + } + + if (refreshModems) + { + refreshModems = 0; + + ui.modeA->setCurrentIndex(speed[0]); + ui.modeB->setCurrentIndex(speed[1]); + ui.modeC->setCurrentIndex(speed[2]); + ui.modeD->setCurrentIndex(speed[3]); + ui.centerA->setValue(rx_freq[0]); + ui.centerB->setValue(rx_freq[1]); + ui.centerC->setValue(rx_freq[2]); + ui.centerD->setValue(rx_freq[3]); + } + + show_grid(); +} + +void QtSoundModem::returnPressed() +{ + char Name[32]; + int Chan; + QString val; + + strcpy(Name, sender()->objectName().toUtf8()); + + Chan = Name[8] - 'A'; + + val = chanOffsetLabel[Chan]->text(); + + chanOffset[Chan] = val.toInt(); + needSetOffset[Chan] = 1; // Update GUI + + +} + + +void QtSoundModem::clickedSlotI(int i) +{ + char Name[32]; + + strcpy(Name, sender()->objectName().toUtf8()); + + if (strcmp(Name, "modeA") == 0) + { + ModemA = ui.modeA->currentIndex(); + set_speed(0, ModemA); + saveSettings(); + AGW_Report_Modem_Change(0); + return; + } + + if (strcmp(Name, "modeB") == 0) + { + ModemB = ui.modeB->currentIndex(); + set_speed(1, ModemB); + saveSettings(); + AGW_Report_Modem_Change(1); + return; + } + + if (strcmp(Name, "modeC") == 0) + { + ModemC = ui.modeC->currentIndex(); + set_speed(2, ModemC); + saveSettings(); + AGW_Report_Modem_Change(2); + return; + } + + if (strcmp(Name, "modeD") == 0) + { + ModemD = ui.modeD->currentIndex(); + set_speed(3, ModemD); + saveSettings(); + AGW_Report_Modem_Change(3); + return; + } + + if (strcmp(Name, "centerA") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerA->setValue(Freq_Change(0, i)); + settings->setValue("Modem/RXFreq1", ui.centerA->value()); + AGW_Report_Modem_Change(0); + + } + return; + } + + if (strcmp(Name, "centerB") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerB->setValue(Freq_Change(1, i)); + settings->setValue("Modem/RXFreq2", ui.centerB->value()); + AGW_Report_Modem_Change(1); + } + return; + } + + if (strcmp(Name, "centerC") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerC->setValue(Freq_Change(2, i)); + settings->setValue("Modem/RXFreq3", ui.centerC->value()); + AGW_Report_Modem_Change(2); + } + return; + } + + if (strcmp(Name, "centerD") == 0) + { + if (i > 300) + { + QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); + ui.centerD->setValue(Freq_Change(3, i)); + settings->setValue("Modem/RXFreq4", ui.centerD->value()); + AGW_Report_Modem_Change(3); + } + return; + } + + if (strcmp(Name, "DCDSlider") == 0) + { + dcd_threshold = i; + saveSettings(); + return; + } + + if (strcmp(Name, "RXOffset") == 0) + { + char valChar[32]; + rxOffset = i; + sprintf(valChar, "RX Offset %d",rxOffset); + ui.RXOffsetLabel->setText(valChar); + + wf_pointer(soundChannel[0]); + wf_pointer(soundChannel[1]); + + pnt_change[0] = 1; + pnt_change[1] = 1; + pnt_change[2] = 1; + pnt_change[3] = 1; + + saveSettings(); + return; + } + + + QMessageBox msgBox; + msgBox.setWindowTitle("MessageBox Title"); + msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); + msgBox.exec(); +} + + +void QtSoundModem::clickedSlot() +{ + char Name[32]; + + strcpy(Name, sender()->objectName().toUtf8()); + + if (strcmp(Name, "actDevices") == 0) + { + doDevices(); + return; + } + + if (strcmp(Name, "actModems") == 0) + { + doModems(); + return; + } + + if (strcmp(Name, "showBPF_A") == 0) + { + doFilter(0, 0); + return; + } + + if (strcmp(Name, "showTXBPF_A") == 0) + { + doFilter(0, 1); + return; + } + + if (strcmp(Name, "showLPF_A") == 0) + { + doFilter(0, 2); + return; + } + + + if (strcmp(Name, "showBPF_B") == 0) + { + doFilter(1, 0); + return; + } + + if (strcmp(Name, "showTXBPF_B") == 0) + { + doFilter(1, 1); + return; + } + + if (strcmp(Name, "showLPF_B") == 0) + { + doFilter(1, 2); + return; + } + + if (strcmp(Name, "Low_A") == 0) + { + handleButton(0, 1); + return; + } + + if (strcmp(Name, "High_A") == 0) + { + handleButton(0, 2); + return; + } + + if (strcmp(Name, "Both_A") == 0) + { + handleButton(0, 3); + return; + } + + if (strcmp(Name, "Stop_A") == 0) + { + handleButton(0, 0); + return; + } + + + if (strcmp(Name, "Low_B") == 0) + { + handleButton(1, 1); + return; + } + + if (strcmp(Name, "High_B") == 0) + { + handleButton(1, 2); + return; + } + + if (strcmp(Name, "Both_B") == 0) + { + handleButton(1, 3); + return; + } + + if (strcmp(Name, "Stop_B") == 0) + { + handleButton(1, 0); + return; + } + + if (strcmp(Name, "Low_C") == 0) + { + handleButton(2, 1); + return; + } + + if (strcmp(Name, "High_C") == 0) + { + handleButton(2, 2); + return; + } + + if (strcmp(Name, "Both_C") == 0) + { + handleButton(2, 3); + return; + } + + if (strcmp(Name, "Stop_C") == 0) + { + handleButton(2, 0); + return; + } + + if (strcmp(Name, "Low_D") == 0) + { + handleButton(3, 1); + return; + } + + if (strcmp(Name, "High_D") == 0) + { + handleButton(3, 2); + return; + } + + if (strcmp(Name, "Both_D") == 0) + { + handleButton(3, 3); + return; + } + + if (strcmp(Name, "Stop_D") == 0) + { + handleButton(3, 0); + return; + } + + QMessageBox msgBox; + msgBox.setWindowTitle("MessageBox Title"); + msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); + msgBox.exec(); +} + +Ui_ModemDialog * Dlg; + +QDialog * modemUI; +QDialog * deviceUI; + +void QtSoundModem::doModems() +{ + Dlg = new(Ui_ModemDialog); + + QDialog UI; + char valChar[10]; + + Dlg->setupUi(&UI); + + modemUI = &UI; + deviceUI = 0; + + myResize *resize = new myResize(); + + UI.installEventFilter(resize); + + sprintf(valChar, "%d", bpf[0]); + Dlg->BPFWidthA->setText(valChar); + sprintf(valChar, "%d", bpf[1]); + Dlg->BPFWidthB->setText(valChar); + sprintf(valChar, "%d", bpf[2]); + Dlg->BPFWidthC->setText(valChar); + sprintf(valChar, "%d", bpf[3]); + Dlg->BPFWidthD->setText(valChar); + + sprintf(valChar, "%d", txbpf[0]); + Dlg->TXBPFWidthA->setText(valChar); + sprintf(valChar, "%d", txbpf[1]); + Dlg->TXBPFWidthB->setText(valChar); + sprintf(valChar, "%d", txbpf[2]); + Dlg->TXBPFWidthC->setText(valChar); + sprintf(valChar, "%d", txbpf[3]); + Dlg->TXBPFWidthD->setText(valChar); + + sprintf(valChar, "%d", lpf[0]); + Dlg->LPFWidthA->setText(valChar); + sprintf(valChar, "%d", lpf[1]); + Dlg->LPFWidthB->setText(valChar); + sprintf(valChar, "%d", lpf[2]); + Dlg->LPFWidthC->setText(valChar); + sprintf(valChar, "%d", lpf[4]); + Dlg->LPFWidthD->setText(valChar); + + sprintf(valChar, "%d", BPF_tap[0]); + Dlg->BPFTapsA->setText(valChar); + sprintf(valChar, "%d", BPF_tap[1]); + Dlg->BPFTapsB->setText(valChar); + sprintf(valChar, "%d", BPF_tap[2]); + Dlg->BPFTapsC->setText(valChar); + sprintf(valChar, "%d", BPF_tap[3]); + Dlg->BPFTapsD->setText(valChar); + + sprintf(valChar, "%d", LPF_tap[0]); + Dlg->LPFTapsA->setText(valChar); + sprintf(valChar, "%d", LPF_tap[1]); + Dlg->LPFTapsB->setText(valChar); + sprintf(valChar, "%d", LPF_tap[2]); + Dlg->LPFTapsC->setText(valChar); + sprintf(valChar, "%d", LPF_tap[3]); + Dlg->LPFTapsD->setText(valChar); + + Dlg->preEmphAllA->setChecked(emph_all[0]); + + if (emph_all[0]) + Dlg->preEmphA->setDisabled(TRUE); + else + Dlg->preEmphA->setCurrentIndex(emph_db[0]); + + Dlg->preEmphAllB->setChecked(emph_all[1]); + + if (emph_all[1]) + Dlg->preEmphB->setDisabled(TRUE); + else + Dlg->preEmphB->setCurrentIndex(emph_db[1]); + + Dlg->preEmphAllC->setChecked(emph_all[2]); + + if (emph_all[2]) + Dlg->preEmphC->setDisabled(TRUE); + else + Dlg->preEmphC->setCurrentIndex(emph_db[2]); + + Dlg->preEmphAllD->setChecked(emph_all[3]); + + if (emph_all[3]) + Dlg->preEmphD->setDisabled(TRUE); + else + Dlg->preEmphD->setCurrentIndex(emph_db[3]); + + + Dlg->nonAX25A->setChecked(NonAX25[0]); + Dlg->nonAX25B->setChecked(NonAX25[1]); + Dlg->nonAX25C->setChecked(NonAX25[2]); + Dlg->nonAX25D->setChecked(NonAX25[3]); + + Dlg->KISSOptA->setChecked(KISS_opt[0]); + Dlg->KISSOptB->setChecked(KISS_opt[1]); + Dlg->KISSOptC->setChecked(KISS_opt[2]); + Dlg->KISSOptD->setChecked(KISS_opt[3]); + + sprintf(valChar, "%d", txdelay[0]); + Dlg->TXDelayA->setText(valChar); + sprintf(valChar, "%d", txdelay[1]); + Dlg->TXDelayB->setText(valChar); + sprintf(valChar, "%d", txdelay[2]); + Dlg->TXDelayC->setText(valChar); + sprintf(valChar, "%d", txdelay[3]); + Dlg->TXDelayD->setText(valChar); + + sprintf(valChar, "%d", txtail[0]); + Dlg->TXTailA->setText(valChar); + sprintf(valChar, "%d", txtail[1]); + Dlg->TXTailB->setText(valChar); + sprintf(valChar, "%d", txtail[2]); + Dlg->TXTailC->setText(valChar); + sprintf(valChar, "%d", txtail[3]); + Dlg->TXTailD->setText(valChar); + + Dlg->FrackA->setText(QString::number(frack_time[0])); + Dlg->FrackB->setText(QString::number(frack_time[1])); + Dlg->FrackC->setText(QString::number(frack_time[2])); + Dlg->FrackD->setText(QString::number(frack_time[3])); + + Dlg->RetriesA->setText(QString::number(fracks[0])); + Dlg->RetriesB->setText(QString::number(fracks[1])); + Dlg->RetriesC->setText(QString::number(fracks[2])); + Dlg->RetriesD->setText(QString::number(fracks[3])); + + sprintf(valChar, "%d", RCVR[0]); + Dlg->AddRXA->setText(valChar); + sprintf(valChar, "%d", RCVR[1]); + Dlg->AddRXB->setText(valChar); + sprintf(valChar, "%d", RCVR[2]); + Dlg->AddRXC->setText(valChar); + sprintf(valChar, "%d", RCVR[3]); + Dlg->AddRXD->setText(valChar); + + sprintf(valChar, "%d", rcvr_offset[0]); + Dlg->RXShiftA->setText(valChar); + + sprintf(valChar, "%d", rcvr_offset[1]); + Dlg->RXShiftB->setText(valChar); + + sprintf(valChar, "%d", rcvr_offset[2]); + Dlg->RXShiftC->setText(valChar); + sprintf(valChar, "%d", rcvr_offset[3]); + Dlg->RXShiftD->setText(valChar); + + // speed[1] + // speed[2]; + + Dlg->recoverBitA->setCurrentIndex(recovery[0]); + Dlg->recoverBitB->setCurrentIndex(recovery[1]); + Dlg->recoverBitC->setCurrentIndex(recovery[2]); + Dlg->recoverBitD->setCurrentIndex(recovery[3]); + + Dlg->fx25ModeA->setCurrentIndex(fx25_mode[0]); + Dlg->fx25ModeB->setCurrentIndex(fx25_mode[1]); + Dlg->fx25ModeC->setCurrentIndex(fx25_mode[2]); + Dlg->fx25ModeD->setCurrentIndex(fx25_mode[3]); + + Dlg->IL2PModeA->setCurrentIndex(il2p_mode[0]); + Dlg->IL2PModeB->setCurrentIndex(il2p_mode[1]); + Dlg->IL2PModeC->setCurrentIndex(il2p_mode[2]); + Dlg->IL2PModeD->setCurrentIndex(il2p_mode[3]); + + Dlg->CWIDCall->setText(CWIDCall); + Dlg->CWIDInterval->setText(QString::number(CWIDInterval)); + + if (CWIDType) + Dlg->radioButton_2->setChecked(1); + else + Dlg->CWIDType->setChecked(1); + + Dlg->RSIDSABM_A->setChecked(RSID_SABM[0]); + Dlg->RSIDSABM_B->setChecked(RSID_SABM[1]); + Dlg->RSIDSABM_C->setChecked(RSID_SABM[2]); + Dlg->RSIDSABM_D->setChecked(RSID_SABM[3]); + + Dlg->RSIDUI_A->setChecked(RSID_UI[0]); + Dlg->RSIDUI_B->setChecked(RSID_UI[1]); + Dlg->RSIDUI_C->setChecked(RSID_UI[2]); + Dlg->RSIDUI_D->setChecked(RSID_UI[3]); + + Dlg->DigiCallsA->setText(MyDigiCall[0]); + Dlg->DigiCallsB->setText(MyDigiCall[1]); + Dlg->DigiCallsC->setText(MyDigiCall[2]); + Dlg->DigiCallsD->setText(MyDigiCall[3]); + + Dlg->RSID_1_SETMODEM->setChecked(RSID_SetModem[0]); + Dlg->RSID_2_SETMODEM->setChecked(RSID_SetModem[1]); + Dlg->RSID_3_SETMODEM->setChecked(RSID_SetModem[2]); + Dlg->RSID_4_SETMODEM->setChecked(RSID_SetModem[3]); + + connect(Dlg->showBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->showBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->showBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->showBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showTXBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Dlg->showLPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); + + connect(Dlg->okButton, SIGNAL(clicked()), this, SLOT(modemaccept())); + connect(Dlg->modemSave, SIGNAL(clicked()), this, SLOT(modemSave())); + connect(Dlg->cancelButton, SIGNAL(clicked()), this, SLOT(modemreject())); + + connect(Dlg->SendRSID_1, SIGNAL(clicked()), this, SLOT(doRSIDA())); + connect(Dlg->SendRSID_2, SIGNAL(clicked()), this, SLOT(doRSIDB())); + connect(Dlg->SendRSID_3, SIGNAL(clicked()), this, SLOT(doRSIDC())); + connect(Dlg->SendRSID_4, SIGNAL(clicked()), this, SLOT(doRSIDD())); + + connect(Dlg->preEmphAllA, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllAChanged(int))); + connect(Dlg->preEmphAllB, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllBChanged(int))); + connect(Dlg->preEmphAllC, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllCChanged(int))); + connect(Dlg->preEmphAllD, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllDChanged(int))); + + UI.exec(); +} + +void QtSoundModem::preEmphAllAChanged(int state) +{ + Dlg->preEmphA->setDisabled(state); +} + +void QtSoundModem::preEmphAllBChanged(int state) +{ + Dlg->preEmphB->setDisabled(state); +} + +void QtSoundModem::preEmphAllCChanged(int state) +{ + Dlg->preEmphC->setDisabled(state); +} + +void QtSoundModem::preEmphAllDChanged(int state) +{ + Dlg->preEmphD->setDisabled(state); +} + +extern "C" void get_exclude_list(char * line, TStringList * list); + +void QtSoundModem::modemaccept() +{ + modemSave(); + delete(Dlg); + saveSettings(); + + modemUI->accept(); + +} + +void QtSoundModem::modemSave() +{ + QVariant Q; + + emph_all[0] = Dlg->preEmphAllA->isChecked(); + emph_db[0] = Dlg->preEmphA->currentIndex(); + + emph_all[1] = Dlg->preEmphAllB->isChecked(); + emph_db[1] = Dlg->preEmphB->currentIndex(); + + emph_all[2] = Dlg->preEmphAllC->isChecked(); + emph_db[2] = Dlg->preEmphC->currentIndex(); + + emph_all[3] = Dlg->preEmphAllD->isChecked(); + emph_db[3] = Dlg->preEmphD->currentIndex(); + + NonAX25[0] = Dlg->nonAX25A->isChecked(); + NonAX25[1] = Dlg->nonAX25B->isChecked(); + NonAX25[2] = Dlg->nonAX25C->isChecked(); + NonAX25[3] = Dlg->nonAX25D->isChecked(); + + KISS_opt[0] = Dlg->KISSOptA->isChecked(); + KISS_opt[1] = Dlg->KISSOptB->isChecked(); + KISS_opt[2] = Dlg->KISSOptC->isChecked(); + KISS_opt[3] = Dlg->KISSOptD->isChecked(); + + if (emph_db[0] < 0 || emph_db[0] > nr_emph) + emph_db[0] = 0; + + if (emph_db[1] < 0 || emph_db[1] > nr_emph) + emph_db[1] = 0; + + if (emph_db[2] < 0 || emph_db[2] > nr_emph) + emph_db[2] = 0; + + if (emph_db[3] < 0 || emph_db[3] > nr_emph) + emph_db[3] = 0; + + Q = Dlg->TXDelayA->text(); + txdelay[0] = Q.toInt(); + + Q = Dlg->TXDelayB->text(); + txdelay[1] = Q.toInt(); + + Q = Dlg->TXDelayC->text(); + txdelay[2] = Q.toInt(); + + Q = Dlg->TXDelayD->text(); + txdelay[3] = Q.toInt(); + + Q = Dlg->TXTailA->text(); + txtail[0] = Q.toInt(); + + Q = Dlg->TXTailB->text(); + txtail[1] = Q.toInt(); + + Q = Dlg->TXTailC->text(); + txtail[2] = Q.toInt(); + + txtail[3] = Dlg->TXTailD->text().toInt(); + + frack_time[0] = Dlg->FrackA->text().toInt(); + frack_time[1] = Dlg->FrackB->text().toInt(); + frack_time[2] = Dlg->FrackC->text().toInt(); + frack_time[3] = Dlg->FrackD->text().toInt(); + + fracks[0] = Dlg->RetriesA->text().toInt(); + fracks[1] = Dlg->RetriesB->text().toInt(); + fracks[2] = Dlg->RetriesC->text().toInt(); + fracks[3] = Dlg->RetriesD->text().toInt(); + + Q = Dlg->AddRXA->text(); + RCVR[0] = Q.toInt(); + + Q = Dlg->AddRXB->text(); + RCVR[1] = Q.toInt(); + + Q = Dlg->AddRXC->text(); + RCVR[2] = Q.toInt(); + + Q = Dlg->AddRXD->text(); + RCVR[3] = Q.toInt(); + + Q = Dlg->RXShiftA->text(); + rcvr_offset[0] = Q.toInt(); + + Q = Dlg->RXShiftB->text(); + rcvr_offset[1] = Q.toInt(); + + Q = Dlg->RXShiftC->text(); + rcvr_offset[2] = Q.toInt(); + + Q = Dlg->RXShiftD->text(); + rcvr_offset[3] = Q.toInt(); + + fx25_mode[0] = Dlg->fx25ModeA->currentIndex(); + fx25_mode[1] = Dlg->fx25ModeB->currentIndex(); + fx25_mode[2] = Dlg->fx25ModeC->currentIndex(); + fx25_mode[3] = Dlg->fx25ModeD->currentIndex(); + + il2p_mode[0] = Dlg->IL2PModeA->currentIndex(); + il2p_mode[1] = Dlg->IL2PModeB->currentIndex(); + il2p_mode[2] = Dlg->IL2PModeC->currentIndex(); + il2p_mode[3] = Dlg->IL2PModeD->currentIndex(); + + recovery[0] = Dlg->recoverBitA->currentIndex(); + recovery[1] = Dlg->recoverBitB->currentIndex(); + recovery[2] = Dlg->recoverBitC->currentIndex(); + recovery[3] = Dlg->recoverBitD->currentIndex(); + + + strcpy(CWIDCall, Dlg->CWIDCall->text().toUtf8().toUpper()); + CWIDInterval = Dlg->CWIDInterval->text().toInt(); + CWIDType = Dlg->radioButton_2->isChecked(); + + if (CWIDInterval) + cwidtimer->start(CWIDInterval * 60000); + else + cwidtimer->stop(); + + + RSID_SABM[0] = Dlg->RSIDSABM_A->isChecked(); + RSID_SABM[1] = Dlg->RSIDSABM_B->isChecked(); + RSID_SABM[2] = Dlg->RSIDSABM_C->isChecked(); + RSID_SABM[3] = Dlg->RSIDSABM_D->isChecked(); + + RSID_UI[0] = Dlg->RSIDUI_A->isChecked(); + RSID_UI[1] = Dlg->RSIDUI_B->isChecked(); + RSID_UI[2] = Dlg->RSIDUI_C->isChecked(); + RSID_UI[3] = Dlg->RSIDUI_D->isChecked(); + + RSID_SetModem[0] = Dlg->RSID_1_SETMODEM->isChecked(); + RSID_SetModem[1] = Dlg->RSID_2_SETMODEM->isChecked(); + RSID_SetModem[2] = Dlg->RSID_3_SETMODEM->isChecked(); + RSID_SetModem[3] = Dlg->RSID_4_SETMODEM->isChecked(); + + Q = Dlg->DigiCallsA->text(); + strcpy(MyDigiCall[0], Q.toString().toUtf8().toUpper()); + + Q = Dlg->DigiCallsB->text(); + strcpy(MyDigiCall[1], Q.toString().toUtf8().toUpper()); + + Q = Dlg->DigiCallsC->text(); + strcpy(MyDigiCall[2], Q.toString().toUtf8().toUpper()); + + Q = Dlg->DigiCallsD->text(); + strcpy(MyDigiCall[3], Q.toString().toUtf8().toUpper()); + + int i; + + for (i = 0; i < 4; i++) + { + initTStringList(&list_digi_callsigns[i]); + + get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]); + } + +} + +void QtSoundModem::modemreject() +{ + delete(Dlg); + modemUI->reject(); +} + +void QtSoundModem::doRSIDA() +{ + needRSID[0] = 1; +} + +void QtSoundModem::doRSIDB() +{ + needRSID[1] = 1; +} + +void QtSoundModem::doRSIDC() +{ + needRSID[2] = 1; +} + +void QtSoundModem::doRSIDD() +{ + needRSID[3] = 1; +} + + + + +void QtSoundModem::doFilter(int Chan, int Filter) +{ + Ui_Dialog Dev; + QImage * bitmap; + + QDialog UI; + + Dev.setupUi(&UI); + + bitmap = new QImage(642, 312, QImage::Format_RGB32); + + bitmap->fill(qRgb(255, 255, 255)); + + QPainter qPainter(bitmap); + qPainter.setBrush(Qt::NoBrush); + qPainter.setPen(Qt::black); + + if (Filter == 0) + make_graph_buf(DET[0][0].BPF_core[Chan], BPF_tap[Chan], &qPainter); + else if (Filter == 1) + make_graph_buf(tx_BPF_core[Chan], tx_BPF_tap[Chan], &qPainter); + else + make_graph_buf(LPF_core[Chan], LPF_tap[Chan], &qPainter); + + qPainter.end(); + Dev.label->setPixmap(QPixmap::fromImage(*bitmap)); + + UI.exec(); + +} + +Ui_devicesDialog * Dev; + +char NewPTTPort[80]; + +int newSoundMode = 0; +int oldSoundMode = 0; + +void QtSoundModem::SoundModeChanged(bool State) +{ + UNUSED(State); + + // Mustn't change SoundMode until dialog is accepted + + if (Dev->UDP->isChecked()) + newSoundMode = 3; + else if (Dev->PULSE->isChecked()) + newSoundMode = 2; + else + newSoundMode = Dev->OSS->isChecked(); + +} + +void QtSoundModem::DualPTTChanged(bool State) +{ + UNUSED(State); + + // Forse Evaluation of Cat Port setting + + PTTPortChanged(0); +} + +void QtSoundModem::CATChanged(bool State) +{ + UNUSED(State); + PTTPortChanged(0); +} + +void QtSoundModem::PTTPortChanged(int Selected) +{ + UNUSED(Selected); + + QVariant Q = Dev->PTTPort->currentText(); + strcpy(NewPTTPort, Q.toString().toUtf8()); + + Dev->RTSDTR->setVisible(false); + Dev->CAT->setVisible(false); + + Dev->PTTOnLab->setVisible(false); + Dev->PTTOn->setVisible(false); + Dev->PTTOff->setVisible(false); + Dev->PTTOffLab->setVisible(false); + Dev->CATLabel->setVisible(false); + Dev->CATSpeed->setVisible(false); + + Dev->GPIOLab->setVisible(false); + Dev->GPIOLeft->setVisible(false); + Dev->GPIORight->setVisible(false); + Dev->GPIOLab2->setVisible(false); + + Dev->CM108Label->setVisible(false); + Dev->VIDPID->setVisible(false); + + if (strcmp(NewPTTPort, "None") == 0) + { + } + else if (strcmp(NewPTTPort, "GPIO") == 0) + { + Dev->GPIOLab->setVisible(true); + Dev->GPIOLeft->setVisible(true); + if (Dev->DualPTT->isChecked()) + { + Dev->GPIORight->setVisible(true); + Dev->GPIOLab2->setVisible(true); + } + } + + else if (strcmp(NewPTTPort, "CM108") == 0) + { + Dev->CM108Label->setVisible(true); +//#ifdef __ARM_ARCHX + Dev->CM108Label->setText("CM108 Device"); +//#else +// Dev->CM108Label->setText("CM108 VID/PID"); +//#endif + Dev->VIDPID->setText(CM108Addr); + Dev->VIDPID->setVisible(true); + } + else if (strcmp(NewPTTPort, "HAMLIB") == 0) + { + Dev->CM108Label->setVisible(true); + Dev->CM108Label->setText("rigctrld Port"); + Dev->VIDPID->setText(QString::number(HamLibPort)); + Dev->VIDPID->setVisible(true); + Dev->PTTOnLab->setText("rigctrld Host"); + Dev->PTTOnLab->setVisible(true); + Dev->PTTOn->setText(HamLibHost); + Dev->PTTOn->setVisible(true); + } + else + { + Dev->RTSDTR->setVisible(true); + Dev->CAT->setVisible(true); + + if (Dev->CAT->isChecked()) + { + Dev->PTTOnLab->setVisible(true); + Dev->PTTOnLab->setText("PTT On String"); + Dev->PTTOn->setText(PTTOnString); + Dev->PTTOn->setVisible(true); + Dev->PTTOff->setVisible(true); + Dev->PTTOff->setText(PTTOffString); + Dev->PTTOffLab->setVisible(true); + Dev->CATLabel->setVisible(true); + Dev->CATSpeed->setVisible(true); + } + } +} + +bool myResize::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Resize) + { + QResizeEvent *resizeEvent = static_cast(event); + QSize size = resizeEvent->size(); + int h = size.height(); + int w = size.width(); + + if (obj == deviceUI) + Dev->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10)); + else + Dlg->scrollArea->setGeometry(QRect(5, 5, w - 10, h - 10)); + + return true; + } + return QObject::eventFilter(obj, event); +} + +void QtSoundModem::doDevices() +{ + char valChar[10]; + + Dev = new(Ui_devicesDialog); + + QDialog UI; + + int i; + + Dev->setupUi(&UI); + + deviceUI = &UI; + modemUI = 0; + + myResize *resize = new myResize(); + + UI.installEventFilter(resize); + + newSoundMode = SoundMode; + oldSoundMode = SoundMode; + +#ifdef WIN32 + Dev->ALSA->setText("WaveOut"); + Dev->OSS->setVisible(0); + Dev->PULSE->setVisible(0); +#endif + + if (SoundMode == 0) + Dev->ALSA->setChecked(1); + else if (SoundMode == 1) + Dev->OSS->setChecked(1); + else if (SoundMode == 2) + Dev->PULSE->setChecked(1); + else if (SoundMode == 2) + Dev->UDP->setChecked(1); + + connect(Dev->ALSA, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + connect(Dev->OSS, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + connect(Dev->PULSE, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + connect(Dev->UDP, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); + + for (i = 0; i < PlaybackCount; i++) + Dev->outputDevice->addItem(&PlaybackNames[i][0]); + + i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains); + + + if (i == -1) + { + // Add device to list + + Dev->outputDevice->addItem(PlaybackDevice); + i = Dev->outputDevice->findText(PlaybackDevice, Qt::MatchContains); + } + + Dev->outputDevice->setCurrentIndex(i); + + for (i = 0; i < CaptureCount; i++) + Dev->inputDevice->addItem(&CaptureNames[i][0]); + + i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains); + + if (i == -1) + { + // Add device to list + + Dev->inputDevice->addItem(CaptureDevice); + i = Dev->inputDevice->findText(CaptureDevice, Qt::MatchContains); + } + Dev->inputDevice->setCurrentIndex(i); + + Dev->Modem_1_Chan->setCurrentIndex(soundChannel[0]); + Dev->Modem_2_Chan->setCurrentIndex(soundChannel[1]); + Dev->Modem_3_Chan->setCurrentIndex(soundChannel[2]); + Dev->Modem_4_Chan->setCurrentIndex(soundChannel[3]); + + // Disable "None" option in first modem + + QStandardItemModel *model = dynamic_cast(Dev->Modem_1_Chan->model()); + QStandardItem * item = model->item(0, 0); + item->setEnabled(false); + + Dev->singleChannelOutput->setChecked(SCO); + Dev->colourWaterfall->setChecked(raduga); + + sprintf(valChar, "%d", KISSPort); + Dev->KISSPort->setText(valChar); + Dev->KISSEnabled->setChecked(KISSServ); + + sprintf(valChar, "%d", AGWPort); + Dev->AGWPort->setText(valChar); + Dev->AGWEnabled->setChecked(AGWServ); + + Dev->PTTOn->setText(PTTOnString); + Dev->PTTOff->setText(PTTOffString); + + sprintf(valChar, "%d", PTTBAUD); + Dev->CATSpeed->setText(valChar); + + sprintf(valChar, "%d", UDPClientPort); + Dev->UDPPort->setText(valChar); + Dev->UDPTXHost->setText(UDPHost); + + if (UDPServerPort != TXPort) + sprintf(valChar, "%d/%d", UDPServerPort, TXPort); + else + sprintf(valChar, "%d", UDPServerPort); + + Dev->UDPTXPort->setText(valChar); + + Dev->UDPEnabled->setChecked(UDPServ); + + sprintf(valChar, "%d", pttGPIOPin); + Dev->GPIOLeft->setText(valChar); + sprintf(valChar, "%d", pttGPIOPinR); + Dev->GPIORight->setText(valChar); + + Dev->VIDPID->setText(CM108Addr); + + QStringList items; + + connect(Dev->CAT, SIGNAL(toggled(bool)), this, SLOT(CATChanged(bool))); + connect(Dev->DualPTT, SIGNAL(toggled(bool)), this, SLOT(DualPTTChanged(bool))); + connect(Dev->PTTPort, SIGNAL(currentIndexChanged(int)), this, SLOT(PTTPortChanged(int))); + + + + if (PTTMode == PTTCAT) + Dev->CAT->setChecked(true); + else + Dev->RTSDTR->setChecked(true); + + for (const QSerialPortInfo &info : Ports) + { + items.append(info.portName()); + } + + items.sort(); + + Dev->PTTPort->addItem("None"); + Dev->PTTPort->addItem("CM108"); + + //#ifdef __ARM_ARCH + + Dev->PTTPort->addItem("GPIO"); + + //#endif + + Dev->PTTPort->addItem("HAMLIB"); + + for (const QString &info : items) + { + Dev->PTTPort->addItem(info); + } + + Dev->PTTPort->setCurrentIndex(Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString)); + + PTTPortChanged(0); // Force reevaluation + + Dev->txRotation->setChecked(TX_rotate); + Dev->DualPTT->setChecked(DualPTT); + + Dev->multiCore->setChecked(multiCore); + + QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept())); + QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject())); + + UI.exec(); + +} + +void QtSoundModem::deviceaccept() +{ + QVariant Q = Dev->inputDevice->currentText(); + int cardChanged = 0; + char portString[32]; + + if (Dev->UDP->isChecked()) + { + // cant have server and slave + + if (Dev->UDPEnabled->isChecked()) + { + QMessageBox::about(this, tr("QtSoundModem"), + tr("Can't have UDP sound source and UDP server at same time")); + return; + } + } + + if (oldSoundMode != newSoundMode) + { + QMessageBox msgBox; + + msgBox.setText("QtSoundModem must restart to change Sound Mode.\n" + "Program will close if you hit Ok\n" + "You will need to reselect audio devices after restarting"); + + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + + int i = msgBox.exec(); + + if (i == QMessageBox::Ok) + { + SoundMode = newSoundMode; + + saveSettings(); + + Closing = 1; + return; + } + + if (oldSoundMode == 0) + Dev->ALSA->setChecked(1); + else if (oldSoundMode == 1) + Dev->OSS->setChecked(1); + else if (oldSoundMode == 2) + Dev->PULSE->setChecked(1); + else if (oldSoundMode == 3) + Dev->UDP->setChecked(1); + + QMessageBox::about(this, tr("Info"), + tr("

Changes not saved

")); + + return; + + } + + if (strcmp(CaptureDevice, Q.toString().toUtf8()) != 0) + { + strcpy(CaptureDevice, Q.toString().toUtf8()); + cardChanged = 1; + } + + CaptureIndex = Dev->inputDevice->currentIndex(); + + Q = Dev->outputDevice->currentText(); + + if (strcmp(PlaybackDevice, Q.toString().toUtf8()) != 0) + { + strcpy(PlaybackDevice, Q.toString().toUtf8()); + cardChanged = 1; + } + + PlayBackIndex = Dev->outputDevice->currentIndex(); + + soundChannel[0] = Dev->Modem_1_Chan->currentIndex(); + soundChannel[1] = Dev->Modem_2_Chan->currentIndex(); + soundChannel[2] = Dev->Modem_3_Chan->currentIndex(); + soundChannel[3] = Dev->Modem_4_Chan->currentIndex(); + + UsingLeft = 0; + UsingRight = 0; + UsingBothChannels = 0; + + for (int i = 0; i < 4; i++) + { + if (soundChannel[i] == LEFT) + { + UsingLeft = 1; + modemtoSoundLR[i] = 0; + } + else if (soundChannel[i] == RIGHT) + { + UsingRight = 1; + modemtoSoundLR[i] = 1; + } + } + + if (UsingLeft && UsingRight) + UsingBothChannels = 1; + + + SCO = Dev->singleChannelOutput->isChecked(); + raduga = Dev->colourWaterfall->isChecked(); + AGWServ = Dev->AGWEnabled->isChecked(); + KISSServ = Dev->KISSEnabled->isChecked(); + + Q = Dev->KISSPort->text(); + KISSPort = Q.toInt(); + + Q = Dev->AGWPort->text(); + AGWPort = Q.toInt(); + + Q = Dev->PTTPort->currentText(); + strcpy(PTTPort, Q.toString().toUtf8()); + + DualPTT = Dev->DualPTT->isChecked(); + TX_rotate = Dev->txRotation->isChecked(); + multiCore = Dev->multiCore->isChecked(); + + if (Dev->CAT->isChecked()) + PTTMode = PTTCAT; + else + PTTMode = PTTRTS; + + Q = Dev->PTTOn->text(); + strcpy(PTTOnString, Q.toString().toUtf8()); + Q = Dev->PTTOff->text(); + strcpy(PTTOffString, Q.toString().toUtf8()); + + Q = Dev->CATSpeed->text(); + PTTBAUD = Q.toInt(); + + Q = Dev->UDPPort->text(); + UDPClientPort = Q.toInt(); + + + Q = Dev->UDPTXPort->text(); + strcpy(portString, Q.toString().toUtf8()); + UDPServerPort = atoi(portString); + + if (strchr(portString, '/')) + { + char * ptr = strlop(portString, '/'); + TXPort = atoi(ptr); + } + else + TXPort = UDPServerPort; + + Q = Dev->UDPTXHost->text(); + strcpy(UDPHost, Q.toString().toUtf8()); + + UDPServ = Dev->UDPEnabled->isChecked(); + + Q = Dev->GPIOLeft->text(); + pttGPIOPin = Q.toInt(); + + Q = Dev->GPIORight->text(); + pttGPIOPinR = Q.toInt(); + + Q = Dev->VIDPID->text(); + + if (strcmp(PTTPort, "CM108") == 0) + strcpy(CM108Addr, Q.toString().toUtf8()); + else if (strcmp(PTTPort, "HAMLIB") == 0) + { + HamLibPort = Q.toInt(); + Q = Dev->PTTOn->text(); + strcpy(HamLibHost, Q.toString().toUtf8()); + } + + ClosePTTPort(); + OpenPTTPort(); + + wf_pointer(soundChannel[0]); + wf_pointer(soundChannel[1]); + + delete(Dev); + saveSettings(); + deviceUI->accept(); + + if (cardChanged) + { + InitSound(1); + } + + // Reset title and tooltip in case ports changed + + char Title[128]; + sprintf(Title, "QtSoundModem Version %s Ports %d/%d", VersionString, AGWPort, KISSPort); + w->setWindowTitle(Title); + + sprintf(Title, "QtSoundModem %d %d", AGWPort, KISSPort); + if (trayIcon) + trayIcon->setToolTip(Title); + + QSize newSize(this->size()); + QSize oldSize(this->size()); + + QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize); + + QCoreApplication::postEvent(this, myResizeEvent); +} + +void QtSoundModem::devicereject() +{ + delete(Dev); + deviceUI->reject(); +} + +void QtSoundModem::handleButton(int Port, int Type) +{ + // interlock calib with CWID + + if (calib_mode[0] == 4) // CWID + return; + + doCalib(Port, Type); +} + +void QtSoundModem::doRestartWF() +{ + if (Firstwaterfall) + { + initWaterfall(0, 0); + initWaterfall(0, 1); + } + + if (Secondwaterfall) + { + initWaterfall(1, 0); + initWaterfall(1, 1); + } +} + + +void QtSoundModem::doAbout() +{ + QMessageBox::about(this, tr("About"), + tr("G8BPQ's port of UZ7HO's Soundmodem\n\nCopyright (C) 2019-2020 Andrei Kopanchuk UZ7HO")); +} + +void QtSoundModem::doCalibrate() +{ + Ui_calDialog Calibrate; + { + QDialog UI; + Calibrate.setupUi(&UI); + + connect(Calibrate.Low_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_A, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Low_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_B, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Low_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_C, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Low_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.High_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Both_D, SIGNAL(released()), this, SLOT(clickedSlot())); + connect(Calibrate.Stop_D, SIGNAL(released()), this, SLOT(clickedSlot())); + + /* + + connect(Calibrate.Low_A, &QPushButton::released, this, [=] { handleButton(0, 1); }); + connect(Calibrate.High_A, &QPushButton::released, this, [=] { handleButton(0, 2); }); + connect(Calibrate.Both_A, &QPushButton::released, this, [=] { handleButton(0, 3); }); + connect(Calibrate.Stop_A, &QPushButton::released, this, [=] { handleButton(0, 0); }); + connect(Calibrate.Low_B, &QPushButton::released, this, [=] { handleButton(1, 1); }); + connect(Calibrate.High_B, &QPushButton::released, this, [=] { handleButton(1, 2); }); + connect(Calibrate.Both_B, &QPushButton::released, this, [=] { handleButton(1, 3); }); + connect(Calibrate.Stop_B, &QPushButton::released, this, [=] { handleButton(1, 0); }); + +// connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(handleButton(1, 2))); +*/ + UI.exec(); + } +} + +void QtSoundModem::RefreshSpectrum(unsigned char * Data) +{ + int i; + + // Last 4 bytes are level busy and Tuning lines + + Waterfall[0]->fill(Black); + + if (Data[206] != LastLevel) + { + LastLevel = Data[206]; +// RefreshLevel(LastLevel); + } + + if (Data[207] != LastBusy) + { + LastBusy = Data[207]; +// Busy->setVisible(LastBusy); + } + + for (i = 0; i < 205; i++) + { + int val = Data[0]; + + if (val > 63) + val = 63; + + Waterfall[0]->setPixel(i, val, Yellow); + if (val < 62) + Waterfall[0]->setPixel(i, val + 1, Gold); + Data++; + } + + ui.WaterfallA->setPixmap(QPixmap::fromImage(*Waterfall[0])); + +} + +void QtSoundModem::RefreshWaterfall(int snd_ch, unsigned char * Data) +{ + int j; + unsigned char * Line; + int len = Waterfall[0]->bytesPerLine(); + int TopLine = NextWaterfallLine[snd_ch]; + + // Write line to cyclic buffer then draw starting with the line just written + + // Length is 208 bytes, including Level and Busy flags + + memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Data, 206); + if (NextWaterfallLine[snd_ch] > 63) + NextWaterfallLine[snd_ch] = 0; + + for (j = 63; j > 0; j--) + { + Line = Waterfall[0]->scanLine(j); + memcpy(Line, &WaterfallLines[snd_ch][TopLine++][0], len); + if (TopLine > 63) + TopLine = 0; + } + + ui.WaterfallA->setPixmap(QPixmap::fromImage(*Waterfall[0])); +} + + +void QtSoundModem::sendtoTrace(char * Msg, int tx) +{ + const QTextCursor old_cursor = monWindowCopy->textCursor(); + const int old_scrollbar_value = monWindowCopy->verticalScrollBar()->value(); + const bool is_scrolled_down = old_scrollbar_value == monWindowCopy->verticalScrollBar()->maximum(); + + // Move the cursor to the end of the document. + monWindowCopy->moveCursor(QTextCursor::End); + + // Insert the text at the position of the cursor (which is the end of the document). + + if (tx) + monWindowCopy->setTextColor(qRgb(192, 0, 0)); + else + monWindowCopy->setTextColor(qRgb(0, 0, 192)); + + monWindowCopy->textCursor().insertText(Msg); + + if (old_cursor.hasSelection() || !is_scrolled_down) + { + // The user has selected text or scrolled away from the bottom: maintain position. + monWindowCopy->setTextCursor(old_cursor); + monWindowCopy->verticalScrollBar()->setValue(old_scrollbar_value); + } + else + { + // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. + monWindowCopy->moveCursor(QTextCursor::End); + monWindowCopy->verticalScrollBar()->setValue(monWindowCopy->verticalScrollBar()->maximum()); + } + + free(Msg); +} + + +// I think this does the waterfall + +typedef struct TRGBQ_t +{ + Byte b, g, r, re; + +} TRGBWQ; + +typedef struct tagRECT +{ + int left; + int top; + int right; + int bottom; +} RECT; + +unsigned int RGBWF[256] ; + + +extern "C" void init_raduga() +{ + Byte offset[6] = {0, 51, 102, 153, 204}; + Byte i, n; + + for (n = 0; n < 52; n++) + { + i = n * 5; + + RGBWF[n + offset[0]] = qRgb(0, 0, i); + RGBWF[n + offset[1]] = qRgb(0, i, 255); + RGBWF[n + offset[2]] = qRgb(0, 255, 255 - i); + RGBWF[n + offset[3]] = qRgb(1, 255, 0); + RGBWF[n + offset[4]] = qRgb(255, 255 - 1, 0); + } +} + +extern "C" int nonGUIMode; + + +// This draws the Frequency Scale on Waterfall + +extern "C" void wf_Scale(int Chan) +{ + if (nonGUIMode) + return; + + float k; + int maxfreq, x, i; + char Textxx[20]; + QImage * bm = Header[Chan]; + + QPainter qPainter(bm); + qPainter.setBrush(Qt::black); + qPainter.setPen(Qt::white); + + maxfreq = roundf(RX_Samplerate*0.005); + k = 100 * fft_size / RX_Samplerate; + + if (Chan == 0) + sprintf(Textxx, "Left"); + else + sprintf(Textxx, "Right"); + + qPainter.drawText(2, 1, + 100, 20, 0, Textxx); + + for (i = 0; i < maxfreq; i++) + { + x = round(k*i); + if (x < 1025) + { + if ((i % 5) == 0) + qPainter.drawLine(x, 20, x, 13); + else + qPainter.drawLine(x, 20, x, 16); + + if ((i % 10) == 0) + { + sprintf(Textxx, "%d", i * 100); + + qPainter.drawText(x - 12, 1, + 100, 20, 0, Textxx); + } + } + } + HeaderCopy[Chan]->setPixmap(QPixmap::fromImage(*bm)); + +} + +// This draws the frequency Markers on the Waterfall + + +void do_pointer(int waterfall) +{ + if (nonGUIMode) + return; + + float x; + + int x1, x2, k, pos1, pos2, pos3; + QImage * bm = Header[waterfall]; + + QPainter qPainter(bm); + qPainter.setBrush(Qt::NoBrush); + qPainter.setPen(Qt::white); + + // bm->fill(black); + + qPainter.fillRect(0, 26, 1024, 9, Qt::black); + + k = 29; + x = fft_size / RX_Samplerate; + + // draw all enabled ports on the ports on this soundcard + + // First Modem is always on the first waterfall + // If second is enabled it is on the first unless different + // channel from first + + for (int i = 0; i < 4; i++) + { + if (UsingBothChannels == 0) + { + // Only One Waterfall. If first chan is + + if ((waterfall == 0 && soundChannel[i] == RIGHT) || (waterfall == 1 && soundChannel[i] == LEFT)) + return; + } + + if (soundChannel[i] == 0) + continue; + + + if (UsingBothChannels == 1) + if ((waterfall == 0 && soundChannel[i] == RIGHT) || (waterfall == 1 && soundChannel[i] == LEFT)) + continue; + + pos1 = roundf(((rxOffset + chanOffset[i] + rx_freq[i]) - 0.5*rx_shift[i])*x) - 5; + pos2 = roundf(((rxOffset + chanOffset[i] + rx_freq[i]) + 0.5*rx_shift[i])*x) - 5; + pos3 = roundf((rxOffset + chanOffset[i] + rx_freq[i]) * x); + x1 = pos1 + 5; + x2 = pos2 + 5; + + qPainter.setPen(Qt::white); + qPainter.drawLine(x1, k, x2, k); + qPainter.drawLine(x1, k - 3, x1, k + 3); + qPainter.drawLine(x2, k - 3, x2, k + 3); + qPainter.drawLine(pos3, k - 3, pos3, k + 3); + + if (rxOffset || chanOffset[i]) + { + // Draw TX posn if rxOffset used + + pos3 = roundf(rx_freq[i] * x); + qPainter.setPen(Qt::magenta); + qPainter.drawLine(pos3, k - 3, pos3, k + 3); + qPainter.drawLine(pos3, k - 3, pos3, k + 3); + qPainter.drawLine(pos3 - 2, k - 3, pos3 + 2, k - 3); + + } + } + HeaderCopy[waterfall]->setPixmap(QPixmap::fromImage(*bm)); +} + +void wf_pointer(int snd_ch) +{ + UNUSED(snd_ch); + + do_pointer(0); + do_pointer(1); +// do_pointer(2); +// do_pointer(3); +} + + +void doWaterfallThread(void * param); + +/* +#ifdef WIN32 + +#define pthread_t uintptr_t + +extern "C" uintptr_t _beginthread(void(__cdecl *start_address)(void *), unsigned stack_size, void *arglist); + +#else + +#include + +extern "C" pthread_t _beginthread(void(*start_address)(void *), unsigned stack_size, void * arglist) +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*)arglist) != 0) + perror("New Thread"); + else + pthread_detach(thread); + + return thread; +} + +#endif +*/ +extern "C" void doWaterfall(int snd_ch) +{ + if (nonGUIMode) + return; + + if (Closing) + return; + +// if (multiCore) // Run modems in separate threads +// _beginthread(doWaterfallThread, 0, xx); +// else + doWaterfallThread((void *)(size_t)snd_ch); + +} + + +extern "C" float aFFTAmpl[1024]; + +void doWaterfallThread(void * param) +{ + int snd_ch = (int)(size_t)param; + + QImage * bm = Waterfall[snd_ch]; + + word i, wid; + single mag; + UCHAR * p; + UCHAR Line[4096]; + + int lineLen; + word hfft_size; + Byte n; + float RealOut[4096] = { 0 }; + float ImagOut[4096]; + + QRegion exposed; + + hfft_size = fft_size / 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?) + + // This does 2048 + + if (0) //RSID_WF + { + // Use the Magnitudes in float aFFTAmpl[RSID_FFT_SIZE]; + + for (i = 0; i < hfft_size; 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 = 0; i < hfft_size; 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; + fft_disp[snd_ch][i] = round(mag); + } + } + + + + /* + for (i = 0; i < hfft_size; i++) + fft[i] = (powf(RealOut[i], 2) + powf(ImagOut[i], 2)); + + for (i = 0; i < hfft_size; i++) + { + if (fft[i] > max) + { + max = fft[i]; + imax = i; + } + } + + if (max > 0) + { + for (i = 0; i < hfft_size; i++) + fft[i] = fft[i] / max; + } + + + for (i = 0; i < hfft_size; i++) + { + mag = fft[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; + + fft_disp[snd_ch][i] = round(mag); + } + + */ + + // bm[snd_ch].Canvas.CopyRect(d, bm[snd_ch].canvas, s) + + //pm->scroll(0, 1, 0, 0, 1024, 80, &exposed); + + // Each bin is 12000 /2048 = 5.859375 + // I think we plot at 6 Hz per pixel. + + wid = bm->width(); + if (wid > hfft_size) + wid = hfft_size; + + wid = wid - 1; + + p = Line; + lineLen = bm->bytesPerLine(); + + if (wid > lineLen / 4) + wid = lineLen / 4; + + if (raduga == DISP_MONO) + { + for (i = 0; i < wid; i++) + { + n = fft_disp[snd_ch][i]; + *(p++) = n; // all colours the same + *(p++) = n; + *(p++) = n; + p++; + } + } + else + { + for (i = 0; i < wid; i++) + { + n = fft_disp[snd_ch][i]; + + memcpy(p, &RGBWF[n], 4); + p += 4; + } + } + + // Scroll + + int TopLine = NextWaterfallLine[snd_ch]; + + // Write line to cyclic buffer then draw starting with the line just written + + memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Line, 4096); + if (NextWaterfallLine[snd_ch] > 79) + NextWaterfallLine[snd_ch] = 0; + + for (int j = 79; j > 0; j--) + { + p = bm->scanLine(j); + memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen); + TopLine++; + if (TopLine > 79) + TopLine = 0; + } + + WaterfallCopy[snd_ch]->setPixmap(QPixmap::fromImage(*bm)); + // WaterfallCopy[snd_ch - 1]->setPixmap(*pm); + // WaterfallCopy[1]->setPixmap(QPixmap::fromImage(*bm)); + +} + + + +void QtSoundModem::changeEvent(QEvent* e) +{ + if (e->type() == QEvent::WindowStateChange) + { + QWindowStateChangeEvent* ev = static_cast(e); + + qDebug() << windowState(); + + if (!(ev->oldState() & Qt::WindowMinimized) && windowState() & Qt::WindowMinimized) + { + if (trayIcon) + setVisible(false); + } +// if (!(ev->oldState() != Qt::WindowNoState) && windowState() == Qt::WindowNoState) +// { +// QMessageBox::information(this, "", "Window has been restored"); +// } + + } + QWidget::changeEvent(e); +} + +#include + +void QtSoundModem::closeEvent(QCloseEvent *event) +{ + UNUSED(event); + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + mysettings.setValue("geometry", QWidget::saveGeometry()); + mysettings.setValue("windowState", saveState()); + + Closing = TRUE; + qDebug() << "Closing"; + + QThread::msleep(100); +} + + +QtSoundModem::~QtSoundModem() +{ + qDebug() << "Saving Settings"; + + QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); + mysettings.setValue("geometry", saveGeometry()); + mysettings.setValue("windowState", saveState()); + + saveSettings(); + Closing = TRUE; + qDebug() << "Closing"; + + QThread::msleep(100); +} + +extern "C" void QSleep(int ms) +{ + QThread::msleep(ms); +} + +int upd_time = 30; + +void QtSoundModem::show_grid() +{ + // This refeshes the session list + + int snd_ch, i, num_rows, row_idx; + QTableWidgetItem *item; + const char * msg; + + int speed_tx, speed_rx; + + if (grid_time < 10) + { + grid_time++; + return; + } + + grid_time = 0; + + //label7.Caption = inttostr(stat_r_mem); mem_arq + + num_rows = 0; + row_idx = 0; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (i = 0; i < port_num; i++) + { + if (AX25Port[snd_ch][i].status != STAT_NO_LINK) + num_rows++; + } + } + + if (num_rows == 0) + { + sessionTable->clearContents(); + sessionTable->setRowCount(0); + sessionTable->setRowCount(1); + } + else + sessionTable->setRowCount(num_rows); + + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (i = 0; i < port_num; i++) + { + if (AX25Port[snd_ch][i].status != STAT_NO_LINK) + { + switch (AX25Port[snd_ch][i].status) + { + case STAT_NO_LINK: + + msg = "No link"; + break; + + case STAT_LINK: + + msg = "Link"; + break; + + case STAT_CHK_LINK: + + msg = "Chk link"; + break; + + case STAT_WAIT_ANS: + + msg = "Wait ack"; + break; + + case STAT_TRY_LINK: + + msg = "Try link"; + break; + + case STAT_TRY_UNLINK: + + msg = "Try unlink"; + } + + + item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].mycall); + sessionTable->setItem(row_idx, 0, item); + + item = new QTableWidgetItem(AX25Port[snd_ch][i].kind); + sessionTable->setItem(row_idx, 11, item); + + item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].corrcall); + sessionTable->setItem(row_idx, 1, item); + + item = new QTableWidgetItem(msg); + sessionTable->setItem(row_idx, 2, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_pkt)); + sessionTable->setItem(row_idx, 3, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_byte)); + sessionTable->setItem(row_idx, 4, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_pkt)); + sessionTable->setItem(row_idx, 5, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_byte)); + sessionTable->setItem(row_idx, 6, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_fc)); + sessionTable->setItem(row_idx, 7, item); + + item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_fec_count)); + sessionTable->setItem(row_idx, 8, item); + + if (grid_timer != upd_time) + grid_timer++; + else + { + grid_timer = 0; + speed_tx = round(abs(AX25Port[snd_ch][i].info.stat_s_byte - AX25Port[snd_ch][i].info.stat_l_s_byte) / upd_time); + speed_rx = round(abs(AX25Port[snd_ch][i].info.stat_r_byte - AX25Port[snd_ch][i].info.stat_l_r_byte) / upd_time); + + item = new QTableWidgetItem(QString::number(speed_tx)); + sessionTable->setItem(row_idx, 9, item); + + item = new QTableWidgetItem(QString::number(speed_rx)); + sessionTable->setItem(row_idx, 10, item); + + AX25Port[snd_ch][i].info.stat_l_r_byte = AX25Port[snd_ch][i].info.stat_r_byte; + AX25Port[snd_ch][i].info.stat_l_s_byte = AX25Port[snd_ch][i].info.stat_s_byte; + } + + row_idx++; + } + } + } +} + +// "Copy on Select" Code + +void QtSoundModem::onTEselectionChanged() +{ + QTextEdit * x = static_cast(QObject::sender()); + x->copy(); +} + diff --git a/QtSoundModem.h b/QtSoundModem.h new file mode 100644 index 0000000..869ae25 --- /dev/null +++ b/QtSoundModem.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include "ui_QtSoundModem.h" +#include "ui_calibrateDialog.h" +#include "ui_devicesDialog.h" +#include "ui_filterWindow.h" +#include "ui_ModemDialog.h" +#include "QThread" +#include +#include +#include +#include +#include +#include + +#include "tcpCode.h" + + +class QtSoundModem : public QMainWindow +{ + Q_OBJECT + +public: + + QtSoundModem(QWidget *parent = Q_NULLPTR); + void changeEvent(QEvent * e); + void closeEvent(QCloseEvent * event); + ~QtSoundModem(); + + void RefreshWaterfall(int snd_ch, unsigned char * Data); + void initWaterfall(int chan, int state); + void show_grid(); + +private slots: + + void doDevices(); + void MinimizetoTray(); + void TrayActivated(QSystemTrayIcon::ActivationReason reason); + void CWIDTimer(); + void MyTimerSlot(); + void returnPressed(); + void clickedSlotI(int i); + void doModems(); + void doFilter(int Chan, int Filter); + void SoundModeChanged(bool State); + void DualPTTChanged(bool State); + void CATChanged(bool State); + void PTTPortChanged(int); + void deviceaccept(); + void devicereject(); + void modemaccept(); + void modemSave(); + void modemreject(); + void doRSIDA(); + void doRSIDB(); + void doRSIDC(); + void doRSIDD(); + void handleButton(int Port, int Act); + void doCalibrate(); + void doAbout(); + void doRestartWF(); + void doupdateDCD(int, int); + void sendtoTrace(char * Msg, int tx); + void preEmphAllAChanged(int); + void preEmphAllBChanged(int); + void preEmphAllCChanged(int state); + void preEmphAllDChanged(int state); + void menuChecked(); + void onTEselectionChanged(); + void clickedSlot(); + +protected: + + bool eventFilter(QObject * obj, QEvent * evt); + void resizeEvent(QResizeEvent *event) override; + +private: + Ui::QtSoundModemClass ui; + QTableWidget* sessionTable; + QStringList m_TableHeader; + + QMenu *setupMenu; + QMenu *viewMenu; + + QAction *actDevices; + QAction *actModems; + QAction *actMintoTray; + QAction *actCalib; + QAction *actAbout; + QAction *actRestartWF; + QAction *actWaterfall1; + QAction *actWaterfall2; + + + void RefreshSpectrum(unsigned char * Data); +}; + +class myResize : public QObject +{ + Q_OBJECT + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; +}; + diff --git a/QtSoundModem.ico b/QtSoundModem.ico new file mode 100644 index 0000000000000000000000000000000000000000..c8243311c2ccb529cb00645c570b6e6a4c56bf60 GIT binary patch literal 766 zcmZQzU<5)11py$*!tjELfkBLcfk6X^6@b_Qh(Y4$KmaPh!p6hU($>PzbL}H!sNp+WzrO|3IAyhJ|HKL@i)w12 z^2`agwIE;s<~xAG@B$DpFmOQ32ipL)1IR~r&woZqK`4+E0J-;r9E>jrbS@Au!uVim zpaP&nLBJIvjW7|cdWaG~7#R40QN#X#fq@wx2B`sJexQ2!KMV}^{}>qRfk8^NuL1V1 B!^;2w literal 0 HcmV?d00001 diff --git a/QtSoundModem.ini b/QtSoundModem.ini new file mode 100644 index 0000000..1edd582 --- /dev/null +++ b/QtSoundModem.ini @@ -0,0 +1,195 @@ +[General] +geometry=@ByteArray(\x1\xd9\xd0\xcb\0\x3\0\0\0\0\0\xb5\0\0\0\xa1\0\0\x4\xb2\0\0\x3v\0\0\0\xb6\0\0\0\xc0\0\0\x4\xb1\0\0\x3u\0\0\0\0\0\0\0\0\x5\0\0\0\0\xb6\0\0\0\xc0\0\0\x4\xb1\0\0\x3u) +windowState=@ByteArray(\0\0\0\xff\0\0\0\0\xfd\0\0\0\0\0\0\x3\xfc\0\0\x2\xa1\0\0\0\x4\0\0\0\x4\0\0\0\b\0\0\0\b\xfc\0\0\0\0) + +[Init] +TXSampleRate=12000 +RXSampleRate=12000 +SndRXDeviceName="CABLE-A OUTPUT (VB-AUDIO CABLE " +SndTXDeviceName=CABLE INPUT (VB-AUDIO VIRTUAL C +DualChan=2 +SCO=0 +DualPTT=1 +PTT=HAMLIB +TXRotate=1 +DispMode=1 +SoundMode=0 +UDPClientPort=8888 +UDPServerPort=8884 +UDPServer=0 +PTTBAUD=19200 +PTTMode=17 +PTTOffString= +PTTOnString=127.0.0.1 +pttGPIOPin=17 +pttGPIOPinR=17 +CM108Addr=0xD8C:0x08 +HamLibPort=4532 +HamLibHost=127.0.0.1 +MinimizetoTray=0 +multiCore=0 +UDPHost=127.0.0.1 +TXPort=8888 + +[Modem] +RXFreq1=1100 +RXFreq2=2000 +ModemType1=0 +ModemType2=0 +DCDThreshold=36 +NRRcvrPairs1=2 +NRRcvrPairs2=2 +RcvrShift1=30 +RcvrShift2=30 +soundChannel1=1 +soundChannel2=1 +RawPktMinLen=17 +SwapPTTPins=0 +PreEmphasisDB1=0 +PreEmphasisDB2=0 +PreEmphasisAll1=1 +PreEmphasisAll2=0 +Default1=1 +Default2=1 +HoldPnt=0 +AFC=32 +TxDelay1=250 +TxDelay2=250 +TxTail1=50 +TxTail2=50 +Diddles=0 +InvPTTPins=0 +RXFreq3=2000 +NRRcvrPairs3=2 +NRRcvrPairs4=0 +RcvrShift3=30 +RcvrShift4=30 +ModemType3=0 +ModemType4=0 +soundChannel3=0 +soundChannel4=0 +PreEmphasisAll3=1 +PreEmphasisAll4=0 +PreEmphasisDB3=0 +PreEmphasisDB4=0 +TxDelay3=250 +TxDelay4=250 +TxTail3=50 +TxTail4=50 +RXFreq4=2700 +CWIDCall= +CWIDInterval=0 +CWIDLeft=0 +CWIDRight=0 +CWIDType=1 + +[AGWHost] +Server=1 +Port=8009 + +[KISS] +Server=0 +Port=8100 + +[AX25_A] +Maxframe=2 +Retries=8 +FrackTime=5 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=2000 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=1 +DynamicFrack=0 +BitRecovery=0 +NonAX25Frm=1 +MEMRecovery=200 +IPOLL=80 +MyDigiCall= +HiToneRaise=0 +soundChannel=1 +FX25=2 + +[AX25_B] +Maxframe=2 +Retries=5 +FrackTime=5 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=2000 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=1 +DynamicFrack=0 +BitRecovery=0 +NonAX25Frm=1 +MEMRecovery=200 +IPOLL=80 +MyDigiCall= +HiToneRaise=0 +soundChannel=0 +FX25=2 + +[Window] +Top=281 +Left=73 +Height=735 +Width=810 +Waterfall1=1 +Waterfall2=1 +StatTable=1 +Monitor=1 +MinimizedOnStartup=0 + +[Font] +Size=8 +Name=MS Sans Serif + +[AX25_C] +Retries=15 +HiToneRaise=0 +Maxframe=3 +FrackTime=5 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=1500 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=0 +DynamicFrack=0 +BitRecovery=0 +NonAX25Frm=0 +IPOLL=80 +MyDigiCall= +FX25=1 + +[AX25_D] +Retries=15 +HiToneRaise=0 +Maxframe=3 +FrackTime=5 +IdleTime=180 +SlotTime=100 +Persist=128 +RespTime=1500 +TXFrmMode=1 +FrameCollector=6 +ExcludeCallsigns= +ExcludeAPRSFrmType= +KISSOptimization=0 +DynamicFrack=0 +BitRecovery=0 +NonAX25Frm=0 +IPOLL=80 +MyDigiCall= +FX25=1 diff --git a/QtSoundModem.pri b/QtSoundModem.pri new file mode 100644 index 0000000..8a394ae --- /dev/null +++ b/QtSoundModem.pri @@ -0,0 +1,36 @@ +# ---------------------------------------------------- +# This file is generated by the Qt Visual Studio Tools. +# ------------------------------------------------------ + +# This is a reminder that you are using a generated .pro file. +# Remove it when you are finished editing this file. +message("You are running qmake on a generated .pro file. This may not work!") + + +HEADERS += ./UZ7HOStuff.h \ + ./QtSoundModem.h \ + ./tcpCode.h +SOURCES += ./ax25.c \ + ./ax25_agw.c \ + ./ax25_demod.c \ + ./ax25_l2.c \ + ./ax25_mod.c \ + ./berlekamp.c \ + ./Config.cpp \ + ./galois.c \ + ./kiss_mode.c \ + ./main.cpp \ + ./QtSoundModem.cpp \ + ./rs.c \ + ./ShowFilter.cpp \ + ./SMMain.c \ + ./sm_main.c \ + ./UZ7HOUtils.c \ + ./Waveout.c \ + ./tcpCode.cpp +FORMS += ./calibrateDialog.ui \ + ./devicesDialog.ui \ + ./filterWindow.ui \ + ./ModemDialog.ui \ + ./QtSoundModem.ui +RESOURCES += QtSoundModem.qrc diff --git a/QtSoundModem.pro b/QtSoundModem.pro new file mode 100644 index 0000000..db1a6af --- /dev/null +++ b/QtSoundModem.pro @@ -0,0 +1,63 @@ + +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 + + + +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.qrc b/QtSoundModem.qrc new file mode 100644 index 0000000..7500e30 --- /dev/null +++ b/QtSoundModem.qrc @@ -0,0 +1,5 @@ + + + soundmodem.ico + + diff --git a/QtSoundModem.rc b/QtSoundModem.rc new file mode 100644 index 0000000000000000000000000000000000000000..616f0270db0cbec3cee908364b81dbfb28eedd20 GIT binary patch literal 5232 zcmdUzU2hsk6o$`rrT&L0zG$PSHg+vFy^Jw-k+C7+Lv3XVxwavw1}r|>MCxx(`n)p? zyUQ+aO>;rD8fJIqoHOV1of$rTJg@_Ec4PM(f+0u`XjXTiA^~@Kv#Id7KUG z-o~^k<8%5u#-{Y*Tkv+vyW>mJhV}@}zI|z5u*$)z-(zMB#RD97p1zcmv2X9J z=jr;MPx4<|(^}THj-6V~8rHM7R%P^*_baR0FO0PKy|fcX>byrCq{r?(QM=eXJI62U ze(vL?iH)#-j(6mTHdg=4Zw;#}*eQQh89$<}M1MU6ssEbx^w zsgK>;;l)GDf)XovzF*W!=q-NxRvu}v`n6Fri7e0mqyrE2aQIH3-R8P+0}nK&+P2Sts#uCCEIJpXdlj>+dh-DHIZS=9l9+E4Nv+3}Jmu5jk@;~Why z!I2jAuT|cYnSEv?U)kSZ?H)q7DyY+;QnI=xl7fW875_bWJi}K{)0b29+O4!|mZO1w zLH02^>N#HWBpz#Xs%fHOR)=IhU}lyou3TLILf8hLm1jz5Fem21g8@+*V%a^h8l&rE zt?G-V#mdM^E!!)s`O#bS8^2lnNO+3yDbB{eOB{95kq=7ft$ps1)9Bbfkb5_L-7~KJ zqeKi}5zD5%*|1`<%xL#1lF}#HPzOUZ~KqU+o8WuZDdMr*<%%iWTik z-n2W7w$db9iBW?xXY1HhEyZ3fwox(FnPEz=6aKpA-Y{FF{XRVJY0p`!4*kHdqG&x5 zCuP`x^UEALAyWAnooy=qq;Z6mhh)!p*jV;Wm@BUezlZqw!pl_-+#+>hzh`3c99
G`vVW^7OuPGqxYxo@4D0LuC2Xav~xHeG_Ls*Mg@Gm zBDygHi~AbrA-dw({Ah`lB3h4l3uwK+v?x@A{J;4e__^+8yOn!sZDfDCg|pLBckld3 vI4y6t-eYye{=fbUquWpZh7qNw{kU6xNNXc4H)s5qI#KoJ|8vR5U6TF=K2~yO literal 0 HcmV?d00001 diff --git a/QtSoundModem.ui b/QtSoundModem.ui new file mode 100644 index 0000000..65fd2ea --- /dev/null +++ b/QtSoundModem.ui @@ -0,0 +1,516 @@ + + + QtSoundModemClass + + + + 0 + 0 + 962 + 721 + + + + + 1024 + 16777215 + + + + QtSoundModem + + + + :/QtSoundModem/soundmodem.ico:/QtSoundModem/soundmodem.ico + + + + + + 174 + 6 + 56 + 22 + + + + 3000 + + + 1500 + + + + + + 316 + 6 + 145 + 22 + + + + + + + 0 + 586 + 959 + 80 + + + + + 0 + 0 + + + + + 600 + 80 + + + + + 5000 + 100 + + + + + 8 + + + + QFrame::Box + + + QFrame::Plain + + + Waterfall + + + + + + 6 + 7 + 16 + 18 + + + + A: + + + + + + 468 + 6 + 56 + 22 + + + + 3000 + + + 1500 + + + + + + 0 + 488 + 953 + 80 + + + + + 600 + 80 + + + + + 5000 + 100 + + + + + 8 + + + + QFrame::Box + + + QFrame::Plain + + + Waterfall + + + + + + 22 + 6 + 145 + 22 + + + + + + + 690 + 0 + 73 + 16 + + + + DCD Level + + + Qt::AlignCenter + + + + + + 300 + 9 + 16 + 14 + + + + B: + + + + + + 690 + 18 + 73 + 14 + + + + Qt::Horizontal + + + QSlider::NoTicks + + + 10 + + + + + + 782 + 6 + 93 + 22 + + + + Hold Pointers + + + + + + 25 + 460 + 1076 + 30 + + + + + 600 + 10 + + + + + 5000 + 100 + + + + + 8 + + + + QFrame::Box + + + QFrame::Plain + + + Waterfall + + + + + + 10 + 560 + 1076 + 30 + + + + + 600 + 10 + + + + + 5000 + 100 + + + + + 8 + + + + QFrame::Box + + + QFrame::Plain + + + Waterfall + + + + + + -6 + 60 + 971 + 201 + + + + true + + + + + + 5 + 32 + 16 + 18 + + + + C: + + + + + + 22 + 31 + 145 + 22 + + + + + + + 468 + 31 + 56 + 22 + + + + 3000 + + + 1500 + + + + + + 316 + 31 + 145 + 22 + + + + + + + 298 + 33 + 16 + 14 + + + + D: + + + + + + 174 + 31 + 56 + 22 + + + + 3000 + + + 1500 + + + + + + 600 + 18 + 63 + 14 + + + + -200 + + + 200 + + + 0 + + + Qt::Horizontal + + + QSlider::NoTicks + + + 10 + + + + + + 600 + 2 + 87 + 16 + + + + RX Offset 0 + + + + + + 238 + 6 + 37 + 22 + + + + 0 + + + Qt::AlignCenter + + + + + + 532 + 6 + 37 + 22 + + + + 0 + + + Qt::AlignCenter + + + + + + 238 + 31 + 37 + 22 + + + + 0 + + + Qt::AlignCenter + + + + + + 532 + 31 + 37 + 20 + + + + 0 + + + Qt::AlignCenter + + + + + + + 0 + 0 + 962 + 21 + + + + + + + + + + diff --git a/QtSoundModem.ui.bak b/QtSoundModem.ui.bak new file mode 100644 index 0000000..90b4371 --- /dev/null +++ b/QtSoundModem.ui.bak @@ -0,0 +1,516 @@ + + + QtSoundModemClass + + + + 0 + 0 + 962 + 721 + + + + + 1024 + 16777215 + + + + QtSoundModem + + + + :/QtSoundModem/soundmodem.ico:/QtSoundModem/soundmodem.ico + + + + + + 174 + 6 + 56 + 22 + + + + 3000 + + + 1500 + + + + + + 316 + 6 + 145 + 22 + + + + + + + 0 + 586 + 959 + 80 + + + + + 0 + 0 + + + + + 600 + 80 + + + + + 5000 + 100 + + + + + 8 + + + + QFrame::Box + + + QFrame::Plain + + + + + + + + + 6 + 7 + 16 + 18 + + + + A: + + + + + + 468 + 6 + 56 + 22 + + + + 3000 + + + 1500 + + + + + + 0 + 488 + 953 + 80 + + + + + 600 + 80 + + + + + 5000 + 100 + + + + + 8 + + + + QFrame::Box + + + QFrame::Plain + + + + + + + + + 22 + 6 + 145 + 22 + + + + + + + 690 + 0 + 73 + 16 + + + + DCD Level + + + Qt::AlignCenter + + + + + + 300 + 9 + 16 + 14 + + + + B: + + + + + + 690 + 18 + 73 + 14 + + + + Qt::Horizontal + + + QSlider::NoTicks + + + 10 + + + + + + 782 + 6 + 93 + 22 + + + + Hold Pointers + + + + + + 25 + 460 + 1076 + 30 + + + + + 600 + 10 + + + + + 5000 + 100 + + + + + 8 + + + + QFrame::Box + + + QFrame::Plain + + + fall + + + + + + 10 + 560 + 1076 + 30 + + + + + 600 + 10 + + + + + 5000 + 100 + + + + + 8 + + + + QFrame::Box + + + QFrame::Plain + + + fall + + + + + + -6 + 60 + 971 + 201 + + + + true + + + + + + 5 + 32 + 16 + 18 + + + + C: + + + + + + 22 + 31 + 145 + 22 + + + + + + + 468 + 31 + 56 + 22 + + + + 3000 + + + 1500 + + + + + + 316 + 31 + 145 + 22 + + + + + + + 298 + 33 + 16 + 14 + + + + D: + + + + + + 174 + 31 + 56 + 22 + + + + 3000 + + + 1500 + + + + + + 600 + 18 + 63 + 14 + + + + -200 + + + 200 + + + 0 + + + Qt::Horizontal + + + QSlider::NoTicks + + + 10 + + + + + + 600 + 2 + 87 + 16 + + + + RX Offset 0 + + + + + + 238 + 6 + 37 + 22 + + + + 0 + + + Qt::AlignCenter + + + + + + 532 + 6 + 37 + 22 + + + + 0 + + + Qt::AlignCenter + + + + + + 238 + 31 + 37 + 22 + + + + 0 + + + Qt::AlignCenter + + + + + + 532 + 31 + 37 + 20 + + + + 0 + + + Qt::AlignCenter + + + + + + + 0 + 0 + 962 + 21 + + + + + + + + + + diff --git a/QtSoundModem.vcxproj b/QtSoundModem.vcxproj new file mode 100644 index 0000000..93bbb10 --- /dev/null +++ b/QtSoundModem.vcxproj @@ -0,0 +1,291 @@ +п»ї + + + + Release + Win32 + + + Debug + Win32 + + + + {4EDE958E-D0AC-37B4-81F7-78313A262DCD} + QtSoundModem + QtVS_v304 + 10.0.19041.0 + 10.0.19041.0 + $(MSBuildProjectDirectory)\QtMsBuild + + + + v141 + release\ + false + NotSet + Application + release\ + QtSoundModem + + + v141 + debug\ + false + NotSet + Application + debug\ + QtSoundModem + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\ + QtSoundModem + true + true + + + $(SolutionDir)$(Platform)\$(Configuration)\ + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\\ + QtSoundModem + true + false + + + 5.14.2 + core;network;gui;widgets;serialport + + + 5.14.2 + core;network;gui;widgets;serialport + + + + + + + rsid;.\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;release;/include;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + MaxSpeed + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;%(PreprocessorDefinitions) + false + $(OutDir) + MultiThreadedDLL + true + true + Level3 + true + + + libfftw3f-3.lib;shell32.lib;setupapi.lib;WS2_32.Lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + true + $(OutDir)QtSoundModem.exe + true + Windows + true + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;NDEBUG;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtSoundModem + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + + + + .\GeneratedFiles\$(ConfigurationName);.\GeneratedFiles;.;debug;/include;rsid;%(AdditionalIncludeDirectories) + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + EditAndContinue + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + Disabled + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + true + $(OutDir) + + + libfftw3f-3.lib;shell32.lib;setupapi.lib;WS2_32.Lib;%(AdditionalDependencies) + C:\opensslx86\lib;C:\Utils\my_sql\mysql-5.7.25-win32\lib;C:\Utils\postgresqlx86\pgsql\lib;%(AdditionalLibraryDirectories) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\QtSoundModem.exe + true + Windows + true + false + + + Unsigned + None + 0 + + + _WINDOWS;UNICODE;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_SERIALPORT_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + + + msvc + ./$(Configuration)/moc_predefs.h + Moc'ing %(Identity)... + output + $(IntDir) + moc_%(Filename).cpp + + + QtSoundModem + default + Rcc'ing %(Identity)... + $(IntDir) + qrc_%(Filename).cpp + + + Uic'ing %(Identity)... + $(IntDir) + ui_%(Filename).h + + + + + + + + + CompileAsC + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >debug\moc_predefs.h + Generate moc_predefs.h + debug\moc_predefs.h;%(Outputs) + + + Document + $(QTDIR)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx"$(QTDIR)\bin\qmake.exe" -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -W3 -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 -wd4577 -wd4467 -E $(QTDIR)\mkspecs\features\data\dummy.cpp 2>NUL >release\moc_predefs.h + Generate moc_predefs.h + release\moc_predefs.h;%(Outputs) + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QtSoundModem.vcxproj-HPLaptop.user b/QtSoundModem.vcxproj-HPLaptop.user new file mode 100644 index 0000000..8995d93 --- /dev/null +++ b/QtSoundModem.vcxproj-HPLaptop.user @@ -0,0 +1,34 @@ +п»ї + + + C:\Devprogs\bpq32\SMSat2 + WindowsLocalDebugger + + + + + c:\devprogs\bpq32\SMSAT2 + WindowsLocalDebugger + < d:\samples.wav + + + C:\DevProgs\BPQ32\SMSat + WindowsLocalDebugger + + + C:\DevProgs\BPQ32\SMSat + WindowsLocalDebugger + + + 2022-12-30T15:55:55.0433562Z + + + 2022-03-11T19:38:31.5906689Z + + + 2022-12-30T15:55:55.2283725Z + + + 2022-03-11T19:38:33.3845083Z + + \ No newline at end of file diff --git a/QtSoundModem.vcxproj.filters b/QtSoundModem.vcxproj.filters new file mode 100644 index 0000000..3e97db7 --- /dev/null +++ b/QtSoundModem.vcxproj.filters @@ -0,0 +1,190 @@ +п»ї + + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + false + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + false + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + Generated Files + + + Generated Files + + + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + + + Resource Files + + + + + + + + Resource Files + + + \ No newline at end of file diff --git a/QtSoundModem.vcxproj.user b/QtSoundModem.vcxproj.user new file mode 100644 index 0000000..516cab4 --- /dev/null +++ b/QtSoundModem.vcxproj.user @@ -0,0 +1,34 @@ +п»ї + + + C:\DevProgs\BPQ32\SM2 + WindowsLocalDebugger + + + + + c:\devprogs\bpq32\SMSAT2 + WindowsLocalDebugger + < d:\samples.wav + + + .\debug + WindowsLocalDebugger + + + C:\DevProgs\BPQ32\SMSat + WindowsLocalDebugger + + + 2023-06-27T07:43:13.0567353Z + + + 2022-03-11T19:38:31.5906689Z + + + 2023-06-27T07:43:13.1602990Z + + + 2022-03-11T19:38:33.3845083Z + + \ No newline at end of file diff --git a/QtSoundModemCopy.vcxproj b/QtSoundModemCopy.vcxproj new file mode 100644 index 0000000..68671af --- /dev/null +++ b/QtSoundModemCopy.vcxproj @@ -0,0 +1,175 @@ +п»ї + + + + Debug + Win32 + + + Release + Win32 + + + + {B12702AD-ABFB-343A-A199-8E24837244A3} + QtVS_v301 + 10.0.17763.0 + + + + Application + v141 + + + Application + v141 + + + + $(MSBuildProjectDirectory)\QtMsBuild + + + $(SolutionDir)$(Platform)\$(Configuration)\Copy\ + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\Copy\ + true + + + $(SolutionDir)$(Platform)\$(Configuration)\Copy\ + true + $(SolutionDir)Intermed\$(Platform)\$(Configuration)\Copy\ + + + + + + + + + + + + + + + + + + msvc2017 + core;gui;network;widgets + + + msvc2017 + core;gui;network;widgets + + + + + + + true + Disabled + EditAndContinue + MultiThreadedDebugDLL + true + + + Windows + $(OutDir)\$(ProjectName).exe + true + false + libfftw3f-3.lib;setupapi.lib;WS2_32.Lib;$(QtDir)\lib\Qt5SerialPort.lib;%(AdditionalDependencies) + + + + + true + EditAndContinue + MultiThreadedDLL + true + + + Windows + $(OutDir)\$(ProjectName).exe + false + false + libfftw3f-3.lib;setupapi.lib;WS2_32.Lib;$(QtDir)\lib\Qt5SerialPort.lib;%(AdditionalDependencies) + + + + + + + CompileAsC + + + + + + + + + + Default + + + + + + + + + + input + %(Filename).moc + input + %(Filename).moc + + + + + + + + + + + + + + + + + + Designer + + + + Designer + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QtSoundModem_resource.rc b/QtSoundModem_resource.rc new file mode 100644 index 0000000..984c31a --- /dev/null +++ b/QtSoundModem_resource.rc @@ -0,0 +1,37 @@ +#include + +IDI_ICON1 ICON DISCARDABLE "SoundModem.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,0,0,0 + PRODUCTVERSION 0,0,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "\0" + VALUE "FileVersion", "0.0.0.0\0" + VALUE "LegalCopyright", "\0" + VALUE "OriginalFilename", "QtSoundModem.exe\0" + VALUE "ProductName", "QtSoundModem\0" + VALUE "ProductVersion", "0.0.0.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END + END +/* End of Version info */ + diff --git a/RSUnit.c b/RSUnit.c new file mode 100644 index 0000000..b14039e --- /dev/null +++ b/RSUnit.c @@ -0,0 +1,768 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" + +/*{*********************************************************************** +* * +* RSUnit.pas * +* * +* (C) Copyright 1990-1999 Bruce K. Christensen * +* * +* Modifications * +* ============= * +* * +***********************************************************************} + +{ This program is an encoder/decoder for Reed-Solomon codes. Encoding + is in systematic form, decoding via the Berlekamp iterative algorithm. + In the present form , the constants mm, nn, tt, and kk=nn-2tt must be + specified (the double letters are used simply to avoid clashes with + other n,k,t used in other programs into which this was incorporated!) + Also, the irreducible polynomial used to generate GF(2**mm) must also + be entered -- these can be found in Lin and Costello, and also Clark + and Cain. + + The representation of the elements of GF(2**m) is either in index + form, where the number is the power of the primitive element alpha, + which is convenient for multiplication (add the powers + modulo 2**m-1) or in polynomial form, where the bits represent the + coefficients of the polynomial representation of the number, which + is the most convenient form for addition. The two forms are swapped + between via lookup tables. This leads to fairly messy looking + expressions, but unfortunately, there is no easy alternative when + working with Galois arithmetic. + + The code is not written in the most elegant way, but to the best of + my knowledge, (no absolute guarantees!), it works. However, when + including it into a simulation program, you may want to do some + conversion of global variables (used here because I am lazy!) to + local variables where appropriate, and passing parameters (eg array + addresses) to the functions may be a sensible move to reduce the + number of global variables and thus decrease the chance of a bug + being introduced. + + This program does not handle erasures at present, but should not be + hard to adapt to do this, as it is just an adjustment to the + Berlekamp-Massey algorithm. It also does not attempt to decode past + the BCH bound. + -- see Blahut "Theory and practiceof error control codes" + for how to do this. + + Simon Rockliff, University of Adelaide 21/9/89 } + */ + + +#define mm 8 // { RS code over GF(2**mm) - change to suit } +#define nn (1 << mm) - 1 // { nn=2**mm -1 length of codeword } +#define MaxErrors 4 // { number of errors that can be corrected } +#define np 2 * MaxErrors // { number of parity symbols } +#define kk nn - np //{ data symbols, kk = nn-2*MaxErrors } + + /* + short = short ; + + TReedSolomon = Class(TComponent) + Procedure generate_gf ; + Procedure gen_poly ; + + Procedure SetPrimitive(Var PP ; + nIdx : Integer ) ; + + Public + Procedure InitBuffers ; + + Procedure EncodeRS(Var xData ; + Var xEncoded ) ; + Function DecodeRS(Var xData ; + Var xDecoded ) : Integer ; + + Constructor Create(AOwner : TComponent) ; Reintroduce ; + Destructor Destroy ; Reintroduce ; + End ; + */ + // specify irreducible polynomial coeffts } + +Byte PP[17]; + +Byte CodeWord[256]; +short Original_Recd[256]; + +short bb[np]; +short data[256]; // +short recd[nn]; + +short alpha_to[nn + 1]; +short index_of[nn + 1]; +short gg[np + 1]; + +string cDuring; +string cName; + + +//aPPType = Array[2..16] of Pointer; + +void * pPP[17]; + +Byte PP2[] = { 1 , 1 , 1 }; + +// { 1 + x + x^3 } + +Byte PP3[] = { 1 , 1 , 0 , 1 }; + +// { 1 + x + x^4 } +Byte PP4[] = { 1 , 1 , 0 , 0 , 1 }; + +// { 1 + x^2 + x^5 } +Byte PP5[] = { 1 , 0 , 1 , 0 , 0 , 1 }; + +// { 1 + x + x^6 } +Byte PP6[] = { 1 , 1 , 0 , 0 , 0 , 0 , 1 }; + +// { 1 + x^3 + x^7 } +Byte PP7[] = { 1, 0, 0, 1, 0, 0, 0, 1 }; + +// { 1+x^2+x^3+x^4+x^8 } +Byte PP8[] = { 1 , 0 , 1 , 1 , 1 , 0 , 0 , 0 , 1 }; + +// { 1+x^4+x^9 } +Byte PP9[] = { 1, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; + +// { 1+x^3+x^10 } +Byte PP10[] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 }; + +// { 1+x^2+x^11 } +Byte PP11[] = { 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + +// { 1+x+x^4+x^6+x^12 } +Byte PP12[] = { 1, 1, 0, 0, 1, 0, 1, 0, 0, + 0, 0, 0, 1 }; + +// { 1+x+x^3+x^4+x^13 } +Byte PP13[] = { 1, 1, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1 }; + +// { 1+x+x^6+x^10+x^14 } +Byte PP14[] = { 1, 1, 0, 0, 0, 0, 1, 0, 0, + 0, 1, 0, 0, 0, 1 }; + +// { 1+x+x^15 } +Byte PP15[] = { 1, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1 }; +// { 1+x+x^3+x^12+x^16 } +Byte PP16[] = { 1, 1, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 1 }; + + +void InitBuffers(); + +/*********************************************************************** +* * +* TReedSolomon.SetPrimitive * +* * +* Primitive polynomials - see Lin & Costello, Appendix A, * +* and Lee & Messerschmitt, p. 453. * +* * +* Modifications * +* ============= * +* * +***********************************************************************/ + +void SetPrimitive(void* PP, int nIdx) +{ + move(pPP[nIdx], PP, (nIdx + 1)); +} + +/************************************************************************ +* * +* Generate_GF * +* * +* Modifications * +* ============= * +* * +***********************************************************************/ + +void Generate_gf() +{ + /* generate GF(2**mm) from the irreducible polynomial p(X) + in pp[0]..pp[mm] + + lookup tables: + index->polynomial form alpha_to[] contains j=alpha**i ; + polynomial form -> index form index_of[j=alpha**i] = i + alpha = 2 is the primitive element of GF(2**mm) + */ + + int i; + short mask; + + SetPrimitive(PP, mm); + + mask = 1; + alpha_to[mm] = 0; + for (i = 0; i < mm; i++) + { + alpha_to[i] = mask; + index_of[alpha_to[i]] = i; + + if (PP[i] != 0) + alpha_to[mm] = alpha_to[mm] ^ mask; + mask = mask << 1; + } + + + index_of[alpha_to[mm]] = mm; + mask = mask >> 1; + + for (i = mm + 1; i < nn; i++) + { + if (alpha_to[i - 1] >= mask) + alpha_to[i] = alpha_to[mm] ^ ((alpha_to[i - 1] ^ mask) << 1); + else + alpha_to[i] = alpha_to[i - 1] << 1; + + index_of[alpha_to[i]] = i; + } + index_of[0] = -1; +} + + +/*********************************************************************** +* * +* Gen_Poly * +* * +* Modifications * +* ============= * +* * +***********************************************************************/ + +void gen_poly() +{ + /* Obtain the generator polynomial of the tt-error correcting, length + nn=(2**mm -1) Reed Solomon code from the product of + (X+alpha**i), i=1..2*tt +*/ + + short i, j; + + gg[0] = 2; //{ primitive element alpha = 2 for GF(2**mm) } + gg[1] = 1; //{ g(x) = (X+alpha) initially } + + i = nn; + j = kk; + + j = i - j; + + for (i = 2; i <= 8; i++) + { + gg[i] = 1; + + for (j = (i - 1); j > 0; j--) + { + if (gg[j] != 0) + gg[j] = gg[j - 1] ^ alpha_to[(index_of[gg[j]] + i) % nn]; + else + gg[j] = gg[j - 1]; + } + + // { gg[0] can never be zero } + + gg[0] = alpha_to[(index_of[gg[0]] + i) % nn]; + } + + // { Convert gg[] to index form for quicker encoding. } + + for (i = 0; i <= np; i++) + gg[i] = index_of[gg[i]]; + +} + + +/*********************************************************************** +* * +* TxBase.Create * +* * +* Modifications * +* ============= * +* * +***********************************************************************/ + +void RsCreate() +{ + InitBuffers(); + + //{ generate the Galois Field GF(2**mm) } + Generate_gf(); + gen_poly(); +} + +/*********************************************************************** +* * +* TReedSolomon.Destroy * +* * +* Modifications * +* ============= * +* * +************************************************************************/ + +/*********************************************************************** +* * +* TReedSolomon.EncodeRS * +* * +* Modifications * +* ============= * +* * +***********************************************************************/ + +void EncodeRS(Byte * xData, Byte * xEncoded) +{ + + /* take the string of symbols in data[i], i=0..(k-1) and encode + systematically to produce 2*tt parity symbols in bb[0]..bb[2*tt-1] + + data[] is input and bb[] is output in polynomial form. Encoding is + done by using a feedback shift register with appropriate connections + specified by the elements of gg[], which was generated above. + + Codeword is c(X) = data(X)*X**(np)+ b(X) } + */ + + // Type + // bArrType = Array[0..16383] of Byte; + + int nI, i, j; + + short feedback; + + // absolute means variables share the same data + + //axData : bArrType absolute xData ; + + + memset(bb, 0, sizeof(bb)); + + for (nI = 0; nI < nn; nI++) + data[nI] = xData[nI]; + + for (i = (kk - 1); i >= 0; i--) + { + feedback = index_of[data[i] ^ bb[np - 1]]; + + if (feedback != -1) + { + for (j = (np - 1); j > 0; j--) + { + if (gg[j] != -1) + bb[j] = bb[j - 1] ^ alpha_to[(gg[j] + feedback) % nn]; + else + bb[j] = bb[j - 1]; + } + bb[0] = alpha_to[(gg[0] + feedback) % nn]; + } + else + { + for (j = (np - 1); j > 0; j--) + bb[j] = bb[j - 1]; + bb[0] = 0; + } + } + + //{ put the transmitted codeword, made up of data } + //{ plus parity, in CodeWord } + + for (nI = 0; nI < np; nI++) + recd[nI] = bb[nI]; + + for (nI = 0; nI < kk; nI++) + recd[nI + np] = data[nI]; + + for (nI = 0; nI < nn; nI++) + CodeWord[nI] = recd[nI]; + + move(CodeWord, xEncoded, nn); +} + + + +/*********************************************************************** +* * +* DecodeRS * +* * +* Modifications * +* ============= * +* * +***********************************************************************} + +Function TReedSolomon.DecodeRS(Var xData ; + Var xDecoded ) : Integer ; +{ assume we have received bits grouped into mm-bit symbols in recd[i], + i=0..(nn-1), and recd[i] is index form (ie as powers of alpha). + We first compute the 2*tt syndromes by substituting alpha**i into + rec(X) and evaluating, storing the syndromes in s[i], i=1..2tt + (leave s[0] zero). Then we use the Berlekamp iteration to find the + error location polynomial elp[i]. If the degree of the elp is >tt, + we cannot correct all the errors and hence just put out the information + symbols uncorrected. If the degree of elp is <=tt, we substitute + alpha**i , i=1..n into the elp to get the roots, hence the inverse + roots, the error location numbers. If the number of errors located + does not equal the degree of the elp, we have more than tt errors and + cannot correct them. Otherwise, we then solve for the error value at + the error location and correct the error. The procedure is that found + in Lin and Costello. for the cases where the number of errors is known + to be too large to correct, the information symbols as received are + output (the advantage of systematic encoding is that hopefully some + of the information symbols will be okay and that if we are in luck, the + errors are in the parity part of the transmitted codeword). Of course, + these insoluble cases can be returned as error flags to the calling + routine if desired. */ + + +int DecodeRS(Byte * xData, Byte * xDecoded) +{ + UNUSED(xDecoded); + +// string cStr; + int nI; // , nJ, nK; + + int i, j; +// short u, q; + +// short elp[np + 2][np]; +// short d[np + 2]; +// short l[np + 2]; +// short u_lu[np + 2]; + short s[np + 1]; + +// short count; + short syn_error; + +// short root[MaxErrors]; +// short loc[MaxErrors]; +// short z[MaxErrors]; +// short err[nn]; +// short reg[MaxErrors + 1]; + + for (nI = 0; nI < nn; nI++) + recd[nI] = xData[nI]; + + for (i = 0; i < nn; i++) + recd[i] = index_of[recd[i]]; // { put recd[i] into index form } + +// count = 0; + syn_error = 0; + + // { first form the syndromes } + + for (i = 0; i < np; i++) + { + s[i] = 0; + + for (j = 0; j < nn; j++) + { + if (recd[j] != -1) + // { recd[j] in index form } + { + s[i] = s[i] ^ alpha_to[(recd[j] + i * j) % nn]; + } + } + + //{ convert syndrome from polynomial form to index form } + if (s[i] != 0) + { + syn_error = 1; // { set flag if non-zero syndrome => error } + } + s[i] = index_of[s[i]]; + } + + if (syn_error != 0) // { if errors, try and correct } + { + /* + { Compute the error location polynomial via the Berlekamp } + { iterative algorithm, following the terminology of Lin and } + { Costello: d[u] is the 'mu'th discrepancy, where u = 'mu' + 1 } + { and 'mu' (the Greek letter!) is the step number ranging from } + { -1 to 2 * tt(see L&C), l[u] is the degree of the elp at that } + { step, and u_l[u] is the difference between the step number } + {and the degree of the elp. } + + { Initialize table entries } + d[0] : = 0; { index form } + d[1] : = s[1]; { index form } + elp[0][0] : = 0; { index form } + elp[1][0] : = 1; { polynomial form } + + for i : = 1 to(np - 1) do + Begin + elp[0][i] : = -1; { index form } + elp[1][i] : = 0; { polynomial form } + End; + + l[0] : = 0; + l[1] : = 0; + u_lu[0] : = -1; + u_lu[1] : = 0; + u: = 0; + + While((u < np) and (l[u + 1] <= MaxErrors)) do + Begin + Inc(u); + + If(d[u] = -1) then + Begin + l[u + 1] : = l[u]; + + for i : = 0 to l[u] do + Begin + elp[u + 1][i] : = elp[u][i]; + elp[u][i] : = index_of[elp[u][i]]; + End; + End + Else + { search for words with greatest u_lu[q] for which d[q] != 0 } + Begin + q : = u - 1; + + While((d[q] = -1) and (q > 0)) do + Dec(q); + + { have found first non - zero d[q] } + If(q > 0) then + Begin + j : = q; + + While j > 0 do + Begin + Dec(j); + If((d[j] != -1) and (u_lu[q] < u_lu[j])) then + q : = j; + End; + End; + + { have now found q such that d[u] != 0 and u_lu[q] is maximum } + { store degree of new elp polynomial } + If(l[u] > l[q] + u - q) then + l[u + 1] : = l[u] + Else + l[u + 1] : = l[q] + u - q; + + { form new elp(x) } + for i : = 0 to(np - 1) do + elp[u + 1][i] : = 0; + + for i : = 0 to l[q] do + If(elp[q][i] != -1) then + elp[u + 1][i + u - q] : = alpha_to[(d[u] + nn - d[q] + elp[q][i]) mod nn]; + + for i : = 0 to l[u] do + Begin + elp[u + 1][i] : = elp[u + 1][i] ^ elp[u][i]; + { convert old elp value to index } + elp[u][i] : = index_of[elp[u][i]]; + End; + End; + + u_lu[u + 1] : = u - l[u + 1]; + + { form(u + 1)th discrepancy } + If u < np then{ no discrepancy computed on last iteration } + Begin + If(s[u + 1] != -1) then + d[u + 1] : = alpha_to[s[u + 1]] + Else + d[u + 1] : = 0; + + for i : = 1 to l[u + 1] do + If((s[u + 1 - i] != -1) and (elp[u + 1][i] != 0)) then + d[u + 1] : = d[u + 1] ^ alpha_to[(s[u + 1 - i] + index_of[elp[u + 1][i]]) mod nn]; + { put d[u + 1] into index form } + d[u + 1] : = index_of[d[u + 1]]; + End; + End; { end While } + + Inc(u); + If l[u] <= MaxErrors then{ can correct error } + Begin + { put elp into index form } + for i : = 0 to l[u]do + elp[u][i] : = index_of[elp[u][i]]; + + { find roots of the error location polynomial } + for i : = 1 to l[u] do + Begin + reg[i] : = elp[u][i]; + End; + + for i : = 1 to nn do + Begin + q : = 1; + + for j : = 1 to l[u] do + If reg[j] != -1 then + Begin + reg[j] : = (reg[j] + j) mod nn; + q: = q ^ alpha_to[reg[j]]; + End; + + If q = 0 then{ store root and error location number indices } + Begin + root[count] : = i; + loc[count] : = nn - i; + Inc(count); + End; + End; + + If count = l[u] then{ no.roots = degree of elp hence <= tt errors } + Begin + Result : = count; + + { form polynomial z(x) } + for i : = 1 to l[u] do { Z[0] = 1 always - do not need } + Begin + If((s[i] != -1) and (elp[u][i] != -1)) then + z[i] : = alpha_to[s[i]] ^ alpha_to[elp[u][i]] + Else + If((s[i] != -1) and (elp[u][i] = -1)) then + z[i] : = alpha_to[s[i]] + Else + If((s[i] = -1) and (elp[u][i] != -1)) then + z[i] : = alpha_to[elp[u][i]] + Else + z[i] : = 0; + + for j : = 1 to(i - 1) do + if ((s[j] != -1) and (elp[u][i - j] != -1)) then + z[i] : = z[i] ^ alpha_to[(elp[u][i - j] + s[j]) mod nn]; + + { put into index form } + z[i] : = index_of[z[i]]; + End; + + { evaluate errors at locations given by } + { error location numbers loc[i] } + for i : = 0 to(nn - 1) do + Begin + err[i] : = 0; + If recd[i] != -1 then{ convert recd[] to polynomial form } + recd[i] : = alpha_to[recd[i]] + Else + recd[i] : = 0; + End; + + for i : = 0 to(l[u] - 1) do { compute numerator of error term first } + Begin + err[loc[i]] : = 1; { accounts for z[0] } + for j : = 1 to l[u] do + If z[j] != -1 then + err[loc[i]] : = err[loc[i]] ^ alpha_to[(z[j] + j * root[i]) mod nn]; + + If err[loc[i]] != 0 then + Begin + err[loc[i]] : = index_of[err[loc[i]]]; + + q: = 0; { form denominator of error term } + + for j : = 0 to(l[u] - 1) do + If j != i then + q : = q + index_of[1 ^ alpha_to[(loc[j] + root[i]) mod nn]]; + q: = q mod nn; + + err[loc[i]] : = alpha_to[(err[loc[i]] - q + nn) mod nn]; + { recd[i] must be in polynomial form } + recd[loc[i]] : = recd[loc[i]] ^ err[loc[i]]; + End; + End; + End + Else{ no.roots != degree of elp = > > tt errors and cannot solve } + Begin + Result : = -1; { Signal an error. } + + for i : = 0 to(nn - 1) do { could return error flag if desired } + If recd[i] != -1 then{ convert recd[] to polynomial form } + recd[i] : = alpha_to[recd[i]] + Else + recd[i] : = 0; { just output received codeword as is } + End; + End{ if l[u] <= tt then } + Else{ elp has degree has degree > tt hence cannot solve } + for i : = 0 to(nn - 1) do { could return error flag if desired } + If recd[i] != -1 then{ convert recd[] to polynomial form } + recd[i] : = alpha_to[recd[i]] + Else + recd[i] : = 0; { just output received codeword as is } + End{ If syn_error != 0 then } + { no non - zero syndromes = > no errors : output received codeword } + Else + Begin + for i : = 0 to(nn - 1) do + If recd[i] != -1 then{ convert recd[] to polynomial form } + recd[i] : = alpha_to[recd[i]] + Else + recd[i] : = 0; + + Result: = 0; { No errors ocurred. } + End; + + for nI : = 0 to(NN - 1) do + axDecoded[nI] : = Recd[nI]; + End; { TReedSolomon.DecodeRS } + */ + + return syn_error; + } + return 0; +} + + + +/*********************************************************************** +* * +* TReedSolomon.InitBuffers * +* * +* Modifications * +* ============= * +* * +***********************************************************************/ + +void InitBuffers() +{ + memset(data, 0, sizeof(data)); + memset(recd, 0, sizeof(recd)); + memset(CodeWord, 0, sizeof(CodeWord)); + + //{ Initialize the Primitive Polynomial vector. } + pPP[2] = PP2; + pPP[3] = PP3; + pPP[4] = PP4; + pPP[5] = PP5; + pPP[6] = PP6; + pPP[7] = PP7; + pPP[8] = PP8; + pPP[9] = PP9; + pPP[10] = PP10; + pPP[11] = PP11; + pPP[12] = PP12; + pPP[13] = PP13; + pPP[14] = PP14; + pPP[15] = PP15; + pPP[16] = PP16; +} + diff --git a/Resource.aps b/Resource.aps new file mode 100644 index 0000000000000000000000000000000000000000..40f5ee3c038959122111f98a9174f2e17db7bf46 GIT binary patch literal 1368 zcmb7EO>f#j5Ph^gKtC=$^z5V@5~)GBiP9=JV;hAO?0^kPB}-NXRzV_mWFX|n_0VJ6 zpOOBJO7!j8#zwSMT{P?U%$u3#*%<&pp{!Pk@VK!IzEvsbxI%-^)vA=*E9LZn@)JNm z^ZK(Oo_QD1IP@B>qEDm6$kT@N$z9+?i+C7#UDxZ*c@9UNXcXLdyK+*s8Bpgl`qd?hTPC^E&jrU(XCFU_@ zHy44}j3pc&sN1a7w>2)w4(v(I9*W#qQD3`se9OFc{hsOA{hn_61FhXRq1IJYigw-Z zbTrFI%_sFb0nD8T&~3}@+3mGI?azvK=VQmr+(mR&J|mTt0VR|Mjz2JaPRq9ZmesVO z;w4HgL-!q5b6YwNj?_aOp(K69{*xV)tSUcfvku>-LDW90!zx{svW_!&yyqDL#N0hx zAi@|SPYrg0!akT%v%rWN4MWT^!5z_#)W*~Vv~-CT`#jZ$#CM1o(RxEm%;&7&p`ykt zqO+2fF}Wl%!e{QE=%=8`Y*Xp0-~jh{k5e2n-WByCM}#HgD)_+nx#X|1%RF{)NM=*X zTksRwniLi9_&de1eD{bD{FX9a!ZKmjB)iQz{@ALc%bsajdb@A1SERMdFDh!^{!x3p z)(SlEX6w%KdCBg~ppTGU{{>=>zfk^Xj>YW$+Ub*XAxthYf#>A>_dMG(`aqvs`sDoc d`Anj+-n8E{U5?3nH^Yc~`O5G)Ccl>q^BWac_6h(1 literal 0 HcmV?d00001 diff --git a/SMMain-HPLaptop.c b/SMMain-HPLaptop.c new file mode 100644 index 0000000..8daaf17 --- /dev/null +++ b/SMMain-HPLaptop.c @@ -0,0 +1,1381 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" +#include "fftw3.h" +#include +#include "ecc.h" // RS Constants +#include "hidapi.h" +#include +#include + +BOOL KISSServ; +int KISSPort; + +BOOL AGWServ; +int AGWPort; + +int Number = 0; // Number waiting to be sent + +int SoundIsPlaying = 0; +int UDPSoundIsPlaying = 0; +int Capturing = 0; + +extern unsigned short buffer[2][1200]; +extern int SoundMode; +extern int needRSID[4]; + +extern short * DMABuffer; + +unsigned short * SendtoCard(unsigned short * buf, int n); +short * SoundInit(); +void DoTX(int Chan); +void UDPPollReceivedSamples(); + + +extern int SampleNo; + +extern int pnt_change[5]; // Freq Changed Flag + +// fftw library interface + + +fftwf_complex *in, *out; +fftwf_plan p; + +#define N 2048 + +void initfft() +{ + in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * N); + out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * N); + p = fftwf_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE); +} + +void dofft(short * inp, float * outr, float * outi) +{ + int i; + + fftwf_complex * fft = in; + + for (i = 0; i < N; i++) + { + fft[0][0] = inp[0] * 1.0f; + fft[0][1] = 0; + fft++; + inp++; + } + + fftwf_execute(p); + + fft = out; + + for (i = 0; i < N; i++) + { + outr[0] = fft[0][0]; + outi[0] = fft[0][1]; + fft++; + outi++; + outr++; + } +} + +void freefft() +{ + fftwf_destroy_plan(p); + fftwf_free(in); + fftwf_free(out); +} + +int nonGUIMode = 0; + +void soundMain() +{ + // non platform specific initialisation + + platformInit(); + + // initialise fft library + + RsCreate(); // RS code for MPSK + + detector_init(); + KISS_init(); + ax25_init(); + init_raduga(); // Set up waterfall colour table + + initfft(); + + if (nonGUIMode) + { + Firstwaterfall = 0; + Secondwaterfall = 0; + } + + OpenPTTPort(); +} + + +void SampleSink(int LR, short Sample) +{ + // This version is passed samples one at a time, as we don't have + // enough RAM in embedded systems to hold a full audio frame + + // LR - 1 == Right Chan + +#ifdef TEENSY + int work = Sample; + DMABuffer[Number++] = (work + 32768) >> 4; // 12 bit left justify +#else + if (SCO) // Single Channel Output - same to both L and R + { + DMABuffer[2 * Number] = Sample; + DMABuffer[1 + 2 * Number] = Sample; + + } + else + { + if (LR) // Right + { + DMABuffer[1 + 2 * Number] = Sample; + DMABuffer[2 * Number] = 0; + } + else + { + DMABuffer[2 * Number] = Sample; + DMABuffer[1 + 2 * Number] = 0; + } + } + Number++; +#endif + if (Number >= SendSize) + { + // send this buffer to sound interface + + DMABuffer = SendtoCard(DMABuffer, SendSize); + Number = 0; + } + + +// Last120[Last120Put++] = Sample; + +// if (Last120Put == (intN + 1)) +// Last120Put = 0; + + SampleNo++; +} + + +void Flush() +{ + SoundFlush(Number); +} + +int ipow(int base, int exp) +{ + int result = 1; + while (exp) + { + if (exp & 1) + result *= base; + exp >>= 1; + base *= base; + } + + return result; +} + +int NumberOfBitsNeeded(int PowerOfTwo) +{ + int i; + + for (i = 0; i <= 16; i++) + { + if ((PowerOfTwo & ipow(2, i)) != 0) + return i; + + } + return 0; +} + + +int ReverseBits(int Index, int NumBits) +{ + int i, Rev = 0; + + for (i = 0; i < NumBits; i++) + { + Rev = (Rev * 2) | (Index & 1); + Index = Index / 2; + } + + return Rev; +} + + +void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform) +{ + float AngleNumerator; + unsigned char NumBits; + + int i, j, K, n, BlockSize, BlockEnd; + float DeltaAngle, DeltaAr; + float Alpha, Beta; + float TR, TI, AR, AI; + + if (InverseTransform) + AngleNumerator = -2.0f * M_PI; + else + AngleNumerator = 2.0f * M_PI; + + NumBits = NumberOfBitsNeeded(NumSamples); + + for (i = 0; i < NumSamples; i++) + { + j = ReverseBits(i, NumBits); + RealOut[j] = RealIn[i]; + ImagOut[j] = 0.0f; // Not using i in ImageIn[i]; + } + + BlockEnd = 1; + BlockSize = 2; + + while (BlockSize <= NumSamples) + { + DeltaAngle = AngleNumerator / BlockSize; + Alpha = sinf(0.5f * DeltaAngle); + Alpha = 2.0f * Alpha * Alpha; + Beta = sinf(DeltaAngle); + + i = 0; + + while (i < NumSamples) + { + AR = 1.0f; + AI = 0.0f; + + j = i; + + for (n = 0; n < BlockEnd; n++) + { + K = j + BlockEnd; + TR = AR * RealOut[K] - AI * ImagOut[K]; + TI = AI * RealOut[K] + AR * ImagOut[K]; + RealOut[K] = RealOut[j] - TR; + ImagOut[K] = ImagOut[j] - TI; + RealOut[j] = RealOut[j] + TR; + ImagOut[j] = ImagOut[j] + TI; + DeltaAr = Alpha * AR + Beta * AI; + AI = AI - (Alpha * AI - Beta * AR); + AR = AR - DeltaAr; + j = j + 1; + } + i = i + BlockSize; + } + BlockEnd = BlockSize; + BlockSize = BlockSize * 2; + } + + if (InverseTransform) + { + // Normalize the resulting time samples... + + for (i = 0; i < NumSamples; i++) + { + RealOut[i] = RealOut[i] / NumSamples; + ImagOut[i] = ImagOut[i] / NumSamples; + } + } +} + + + +int LastBusyCheck = 0; + +extern UCHAR CurrentLevel; + +#ifdef PLOTSPECTRUM +float dblMagSpectrum[206]; +float dblMaxScale = 0.0f; +extern UCHAR Pixels[4096]; +extern UCHAR * pixelPointer; +#endif + +extern int blnBusyStatus; +BusyDet = 0; + +#define PLOTWATERFALL + +int WaterfallActive = 1; +int SpectrumActive; + +/* + +void UpdateBusyDetector(short * bytNewSamples) +{ + float dblReF[1024]; + float dblImF[1024]; + float dblMag[206]; +#ifdef PLOTSPECTRUM + float dblMagMax = 0.0000000001f; + float dblMagMin = 10000000000.0f; +#endif + UCHAR Waterfall[256]; // Colour index values to send to GUI + int clrTLC = Lime; // Default Bandwidth lines on waterfall + + static BOOL blnLastBusyStatus; + + float dblMagAvg = 0; + int intTuneLineLow, intTuneLineHi, intDelta; + int i; + + // if (State != SearchingForLeader) + // return; // only when looking for leader + + if (Now - LastBusyCheck < 100) + return; + + LastBusyCheck = Now; + + FourierTransform(1024, bytNewSamples, &dblReF[0], &dblImF[0], FALSE); + + for (i = 0; i < 206; i++) + { + // starting at ~300 Hz to ~2700 Hz Which puts the center of the signal in the center of the window (~1500Hz) + + dblMag[i] = powf(dblReF[i + 25], 2) + powf(dblImF[i + 25], 2); // first pass + dblMagAvg += dblMag[i]; +#ifdef PLOTSPECTRUM + dblMagSpectrum[i] = 0.2f * dblMag[i] + 0.8f * dblMagSpectrum[i]; + dblMagMax = max(dblMagMax, dblMagSpectrum[i]); + dblMagMin = min(dblMagMin, dblMagSpectrum[i]); +#endif + } + + // LookforPacket(dblMag, dblMagAvg, 206, &dblReF[25], &dblImF[25]); + // packet_process_samples(bytNewSamples, 1200); + + intDelta = roundf(500 / 2) + 50 / 11.719f; + + intTuneLineLow = max((103 - intDelta), 3); + intTuneLineHi = min((103 + intDelta), 203); + +// if (ProtocolState == DISC) // ' Only process busy when in DISC state + { + // blnBusyStatus = BusyDetect3(dblMag, intTuneLineLow, intTuneLineHi); + + if (blnBusyStatus && !blnLastBusyStatus) + { +// QueueCommandToHost("BUSY TRUE"); +// newStatus = TRUE; // report to PTC + + if (!WaterfallActive && !SpectrumActive) + { + UCHAR Msg[2]; + +// Msg[0] = blnBusyStatus; +// SendtoGUI('B', Msg, 1); + } + } + // stcStatus.Text = "TRUE" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY TRUE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + else if (blnLastBusyStatus && !blnBusyStatus) + { +// QueueCommandToHost("BUSY FALSE"); +// newStatus = TRUE; // report to PTC + + if (!WaterfallActive && !SpectrumActive) + { + UCHAR Msg[2]; + + Msg[0] = blnBusyStatus; +// SendtoGUI('B', Msg, 1); + } + } + // stcStatus.Text = "FALSE" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY FALSE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + blnLastBusyStatus = blnBusyStatus; + } + + if (BusyDet == 0) + clrTLC = Goldenrod; + else if (blnBusyStatus) + clrTLC = Fuchsia; + + // At the moment we only get here what seaching for leader, + // but if we want to plot spectrum we should call + // it always + + + + if (WaterfallActive) + { +#ifdef PLOTWATERFALL + dblMagAvg = log10f(dblMagAvg / 5000.0f); + + for (i = 0; i < 206; i++) + { + // The following provides some AGC over the waterfall to compensate for avg input level. + + float y1 = (0.25f + 2.5f / dblMagAvg) * log10f(0.01 + dblMag[i]); + int objColor; + + // Set the pixel color based on the intensity (log) of the spectral line + if (y1 > 6.5) + objColor = Orange; // Strongest spectral line + else if (y1 > 6) + objColor = Khaki; + else if (y1 > 5.5) + objColor = Cyan; + else if (y1 > 5) + objColor = DeepSkyBlue; + else if (y1 > 4.5) + objColor = RoyalBlue; + else if (y1 > 4) + objColor = Navy; + else + objColor = Black; + + if (i == 102) + Waterfall[i] = Tomato; // 1500 Hz line (center) + else if (i == intTuneLineLow || i == intTuneLineLow - 1 || i == intTuneLineHi || i == intTuneLineHi + 1) + Waterfall[i] = clrTLC; + else + Waterfall[i] = objColor; // ' Else plot the pixel as received + } + + // Send Signal level and Busy indicator to save extra packets + + Waterfall[206] = CurrentLevel; + Waterfall[207] = blnBusyStatus; + + doWaterfall(Waterfall); +#endif + } + else if (SpectrumActive) + { +#ifdef PLOTSPECTRUM + // This performs an auto scaling mechansim with fast attack and slow release + if (dblMagMin / dblMagMax < 0.0001) // more than 10000:1 difference Max:Min + dblMaxScale = max(dblMagMax, dblMaxScale * 0.9f); + else + dblMaxScale = max(10000 * dblMagMin, dblMagMax); + +// clearDisplay(); + + for (i = 0; i < 206; i++) + { + // The following provides some AGC over the spectrum to compensate for avg input level. + + float y1 = -0.25f * (SpectrumHeight - 1) * log10f((max(dblMagSpectrum[i], dblMaxScale / 10000)) / dblMaxScale); // ' range should be 0 to bmpSpectrumHeight -1 + int objColor = Yellow; + + Waterfall[i] = round(y1); + } + + // Send Signal level and Busy indicator to save extra packets + + Waterfall[206] = CurrentLevel; + Waterfall[207] = blnBusyStatus; + Waterfall[208] = intTuneLineLow; + Waterfall[209] = intTuneLineHi; + +// SendtoGUI('X', Waterfall, 210); +#endif + } +} + +*/ + +extern short rawSamples[2400]; // Get Frame Type need 2400 and we may add 1200 +int rawSamplesLength = 0; +extern int maxrawSamplesLength; + +void ProcessNewSamples(short * Samples, int nSamples) +{ + if (SoundIsPlaying == FALSE && UDPSoundIsPlaying == FALSE) + BufferFull(Samples, nSamples); +}; + +void doCalib(int Chan, int Act) +{ + if (Chan == 0 && calib_mode[1]) + return; + + if (Chan == 1 && calib_mode[0]) + return; + + calib_mode[Chan] = Act; + + if (Act == 0) + { + tx_status[Chan] = TX_SILENCE; // Stop TX + Flush(); + RadioPTT(Chan, 0); + Debugprintf("Stop Calib"); + } +} + +int Freq_Change(int Chan, int Freq) +{ + int low, high; + + low = round(rx_shift[1] / 2 + RCVR[Chan] * rcvr_offset[Chan] + 1); + high = round(RX_Samplerate / 2 - (rx_shift[Chan] / 2 + RCVR[Chan] * rcvr_offset[Chan])); + + if (Freq < low) + return rx_freq[Chan]; // Dont allow change + + if (Freq > high) + return rx_freq[Chan]; // Dont allow change + + rx_freq[Chan] = Freq; + tx_freq[Chan] = Freq; + + pnt_change[Chan] = TRUE; + wf_pointer(soundChannel[Chan]); + + return Freq; +} + +void MainLoop() +{ + // Called by background thread every 10 ms (maybe) + + // Actually we may have two cards + + // Original only allowed one channel per card. + // I think we should be able to run more, ie two or more + // modems on same soundcard channel + + // So All the soundcard stuff will need to be generalised + + if (UDPServ) + UDPPollReceivedSamples(); + + if (SoundMode == 3) + UDPPollReceivedSamples(); + else + PollReceivedSamples(); + + + if (modem_mode[0] == MODE_ARDOP) + { + chk_dcd1(0, 512); + } + + DoTX(0); + DoTX(1); + DoTX(2); + DoTX(3); + +} + +int ARDOPSendToCard(int Chan, int Len) +{ + // Send Next Block of samples to the soundcard + + short * in = &ARDOPTXBuffer[Chan][ARDOPTXPtr[Chan]]; // Enough to hold whole frame of samples + short * out = DMABuffer; + + int LR = modemtoSoundLR[Chan]; + + int i; + + for (i = 0; i < Len; i++) + { + if (SCO) // Single Channel Output - same to both L and R + { + *out++ = *in; + *out++ = *in++; + } + else + { + if (LR) // Right + { + *out++ = 0; + *out++ = *in++; + } + else + { + *out++ = *in++; + *out++ = 0; + } + } + } + DMABuffer = SendtoCard(DMABuffer, Len); + + ARDOPTXPtr[Chan] += Len; + + // See if end of buffer + + if (ARDOPTXPtr[Chan] > ARDOPTXLen[Chan]) + return 1; + + return 0; +} +void DoTX(int Chan) +{ + // This kicks off a send sequence or calibrate + +// printtick("dotx"); + + if (calib_mode[Chan]) + { + // Maybe new calib or continuation + + if (pnt_change[Chan]) + { + make_core_BPF(Chan, rx_freq[Chan], bpf[Chan]); + make_core_TXBPF(Chan, tx_freq[Chan], txbpf[Chan]); + pnt_change[Chan] = FALSE; + } + + // Note this may block in SendtoCard + + modulator(Chan, tx_bufsize); + return; + } + + // I think we have to detect NO_DATA here and drop PTT and return to SILENCE + + if (tx_status[Chan] == TX_NO_DATA) + { + Flush(); + Debugprintf("TX Complete"); + RadioPTT(0, 0); + tx_status[Chan] = TX_SILENCE; + + // We should now send any ackmode acks as the channel is now free for dest to reply + + sendAckModeAcks(Chan); + } + + if (tx_status[Chan] != TX_SILENCE) + { + // Continue the send + + if (modem_mode[Chan] == MODE_ARDOP) + { +// if (SeeIfCardBusy()) +// return 0; + + if (ARDOPSendToCard(Chan, SendSize) == 1) + { + // End of TX + + Number = 0; + Flush(); + + // See if more to send. If so, don't drop PTT + + if (all_frame_buf[Chan].Count) + { + SoundIsPlaying = TRUE; + Number = 0; + + Debugprintf("TX Continuing"); + + string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message + string * tx_data; + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[Chan], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + tx_data = duplicateString(myTemp); // so can free original below + + Delete(&all_frame_buf[Chan], 0); // This will invalidate temp + + AGW_AX25_frame_analiz(Chan, FALSE, tx_data); + + put_frame(Chan, tx_data, "", TRUE, FALSE); + + PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + + freeString(tx_data); + + // Samples are now in DMABuffer = Send first block + + ARDOPSendToCard(Chan, SendSize); + tx_status[Chan] = TX_FRAME; + return; + } + + Debugprintf("TX Complete"); + RadioPTT(0, 0); + tx_status[Chan] = TX_SILENCE; + + // We should now send any ackmode acks as the channel is now free for dest to reply + } + + return; + } + + modulator(Chan, tx_bufsize); + return; + } + + if (SoundIsPlaying || UDPSoundIsPlaying) + return; + + // Not doing anything so see if we have anything new to send + + // See if frequency has changed + + if (pnt_change[Chan]) + { + make_core_BPF(Chan, rx_freq[Chan], bpf[Chan]); + make_core_TXBPF(Chan, tx_freq[Chan], txbpf[Chan]); + pnt_change[Chan] = FALSE; + } + + // See if we need an RSID + + if (needRSID[Chan]) + { + needRSID[Chan] = 0; + + // Note this may block in SampleSink + + Debugprintf("Sending RSID"); + sendRSID(Chan, all_frame_buf[Chan].Count == 0); + return; + } + + if (all_frame_buf[Chan].Count == 0) + return; + + // Start a new send. modulator should handle TXD etc + + Debugprintf("TX Start"); + SampleNo = 0; + + SoundIsPlaying = TRUE; + RadioPTT(Chan, 1); + Number = 0; + + if (modem_mode[Chan] == MODE_ARDOP) + { + // I think ARDOP will have to generate a whole frame of samples + // then send them out a bit at a time to avoid stopping here for + // possibly 10's of seconds + + // Can do this here as unlike normal ardop we don't need to run on Teensy + // to 12000 sample rate we need either 24K or 48K per second, depending on + // where we do the stereo mux. + + // Slowest rate is 50 baud, so a 255 byte packet would take about a minute + // allowing for RS overhead. Not really realistic put perhaps should be possible. + // RAM isn't an issue so maybe allocate 2 MB. + + // ?? Should we allow two ARDOP modems - could make sense if we can run sound + // card channels independently + + string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message + string * tx_data; + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[Chan], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + tx_data = duplicateString(myTemp); // so can free original below + + Delete(&all_frame_buf[Chan], 0); // This will invalidate temp + + AGW_AX25_frame_analiz(Chan, FALSE, tx_data); + + put_frame(Chan, tx_data, "", TRUE, FALSE); + + PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + + freeString(tx_data); + + // Samples are now in DMABuffer = Send first block + + ARDOPSendToCard(Chan, SendSize); + tx_status[Chan] = TX_FRAME; + + } + else + modulator(Chan, tx_bufsize); + + return; +} + +void stoptx(int snd_ch) +{ + Flush(); + Debugprintf("TX Complete"); + RadioPTT(snd_ch, 0); + tx_status[snd_ch] = TX_SILENCE; + + snd_status[snd_ch] = SND_IDLE; +} + +void RX2TX(int snd_ch) +{ + if (snd_status[snd_ch] == SND_IDLE) + { + DoTX(snd_ch); + } +} + +// PTT Stuff + +int hPTTDevice = 0; +char PTTPort[80] = ""; // Port for Hardware PTT - may be same as control port. +int PTTBAUD = 19200; +int PTTMode = PTTRTS; // PTT Control Flags. + +char PTTOnString[128] = ""; +char PTTOffString[128] = ""; + +UCHAR PTTOnCmd[64]; +UCHAR PTTOnCmdLen = 0; + +UCHAR PTTOffCmd[64]; +UCHAR PTTOffCmdLen = 0; + +int pttGPIOPin = 17; // Default +int pttGPIOPinR = 17; +BOOL pttGPIOInvert = FALSE; +BOOL useGPIO = FALSE; +BOOL gotGPIO = FALSE; + +int HamLibPort = 4532; +char HamLibHost[32] = "192.168.1.14"; + +char CM108Addr[80] = ""; + +int VID = 0; +int PID = 0; + +// CM108 Code + +char * CM108Device = NULL; + +void DecodeCM108(char * ptr) +{ + // Called if Device Name or PTT = Param is CM108 + +#ifdef WIN32 + + // Next Param is VID and PID - 0xd8c:0x8 or Full device name + // On Windows device name is very long and difficult to find, so + // easier to use VID/PID, but allow device in case more than one needed + + char * next; + long VID = 0, PID = 0; + char product[256] = "Unknown"; + + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + if (strlen(ptr) > 16) + CM108Device = _strdup(ptr); + else + { + VID = strtol(ptr, &next, 0); + if (next) + PID = strtol(++next, &next, 0); + + // Look for Device + + devs = hid_enumerate((unsigned short)VID, (unsigned short)PID); + cur_dev = devs; + + while (cur_dev) + { + if (cur_dev->product_string) + wcstombs(product, cur_dev->product_string, 255); + + Debugprintf("HID Device %s VID %X PID %X", product, cur_dev->vendor_id, cur_dev->product_id); + if (cur_dev->vendor_id == VID && cur_dev->product_id == PID) + { + path_to_open = cur_dev->path; + break; + } + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + handle = hid_open_path(path_to_open); + + if (handle) + { + hid_close(handle); + CM108Device = _strdup(path_to_open); + } + else + { + Debugprintf("Unable to open CM108 device %x %x", VID, PID); + } + } + else + Debugprintf("Couldn't find CM108 device %x %x", VID, PID); + + hid_free_enumeration(devs); + } +#else + + // Linux - Next Param HID Device, eg /dev/hidraw0 + + CM108Device = _strdup(ptr); +#endif +} + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++ = 0; + return ptr; +} + +void OpenPTTPort() +{ + PTTMode &= ~PTTCM108; + PTTMode &= ~PTTHAMLIB; + + if (PTTPort[0] && strcmp(PTTPort, "None") != 0) + { + if (PTTMode == PTTCAT) + { + // convert config strings from Hex + + char * ptr1 = PTTOffString; + UCHAR * ptr2 = PTTOffCmd; + char c; + int val; + + while (c = *(ptr1++)) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + PTTOffCmdLen = ptr2 - PTTOffCmd; + + ptr1 = PTTOnString; + ptr2 = PTTOnCmd; + + while (c = *(ptr1++)) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + PTTOnCmdLen = ptr2 - PTTOnCmd; + } + + if (stricmp(PTTPort, "GPIO") == 0) + { + // Initialise GPIO for PTT if available + +#ifdef __ARM_ARCH + + if (gpioInitialise() == 0) + { + printf("GPIO interface for PTT available\n"); + gotGPIO = TRUE; + + SetupGPIOPTT(); + } + else + printf("Couldn't initialise GPIO interface for PTT\n"); + +#else + printf("GPIO interface for PTT not available on this platform\n"); +#endif + + } + else if (stricmp(PTTPort, "CM108") == 0) + { + DecodeCM108(CM108Addr); + PTTMode |= PTTCM108; + } + + else if (stricmp(PTTPort, "HAMLIB") == 0) + { + PTTMode |= PTTHAMLIB; + HAMLIBSetPTT(0); // to open port + return; + } + + else // Not GPIO + { + hPTTDevice = OpenCOMPort(PTTPort, PTTBAUD, FALSE, FALSE, FALSE, 0); + } + } +} + +void ClosePTTPort() +{ + CloseCOMPort(hPTTDevice); + hPTTDevice = 0; +} +void CM108_set_ptt(int PTTState) +{ + char io[5]; + hid_device *handle; + int n; + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + if (CM108Device == NULL) + return; + +#ifdef WIN32 + handle = hid_open_path(CM108Device); + + if (!handle) { + printf("unable to open device\n"); + return; + } + + n = hid_write(handle, io, 5); + if (n < 0) + { + printf("Unable to write()\n"); + printf("Error: %ls\n", hid_error(handle)); + } + + hid_close(handle); + +#else + + int fd; + + fd = open(CM108Device, O_WRONLY); + + if (fd == -1) + { + printf("Could not open %s for write, errno=%d\n", CM108Device, errno); + return; + } + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + n = write(fd, io, 5); + if (n != 5) + { + printf("Write to %s failed, n=%d, errno=%d\n", CM108Device, n, errno); + } + + close(fd); +#endif + return; + +} + + + +void RadioPTT(int snd_ch, BOOL PTTState) +{ +#ifdef __ARM_ARCH + if (useGPIO) + { + if (DualPTT && modemtoSoundLR[snd_ch] == 1) + gpioWrite(pttGPIOPinR, (pttGPIOInvert ? (1 - PTTState) : (PTTState))); + else + gpioWrite(pttGPIOPin, (pttGPIOInvert ? (1 - PTTState) : (PTTState))); + + return; + } + +#endif + + if ((PTTMode & PTTCM108)) + { + CM108_set_ptt(PTTState); + return; + } + + if ((PTTMode & PTTHAMLIB)) + { + HAMLIBSetPTT(PTTState); + return; + } + if (hPTTDevice == 0) + return; + + if ((PTTMode & PTTCAT)) + { + if (PTTState) + WriteCOMBlock(hPTTDevice, PTTOnCmd, PTTOnCmdLen); + else + WriteCOMBlock(hPTTDevice, PTTOffCmd, PTTOffCmdLen); + + return; + } + + if (DualPTT && modemtoSoundLR[snd_ch] == 1) // use DTR + { + if (PTTState) + COMSetDTR(hPTTDevice); + else + COMClearDTR(hPTTDevice); + } + else + { + if ((PTTMode & PTTRTS)) + { + if (PTTState) + COMSetRTS(hPTTDevice); + else + COMClearRTS(hPTTDevice); + } + } + +} + +char ShortDT[] = "HH:MM:SS"; + +char * ShortDateTime() +{ + struct tm * tm; + time_t NOW = time(NULL); + + tm = gmtime(&NOW); + + sprintf(ShortDT, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); + return ShortDT; +} + + +// Reed Solomon Stuff + + +int NPAR = -1; // Number of Parity Bytes - used in RS Code + +int xMaxErrors = 0; + +int RSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen) +{ + // This just returns the Parity Bytes. I don't see the point + // in copying the message about + + unsigned char Padded[256]; // The padded Data + + int Length = DataLen + RSLen; // Final Length of packet + int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes + + // subroutine to do the RS encode. For full length and shortend RS codes up to 8 bit symbols (mm = 8) + + if (NPAR != RSLen) // Changed RS Len, so recalc constants; + { + NPAR = RSLen; + xMaxErrors = NPAR / 2; + initialize_ecc(); + } + + // Copy the supplied data to end of data array. + + memset(Padded, 0, PadLength); + memcpy(&Padded[PadLength], bytToRS, DataLen); + + encode_data(Padded, 255 - RSLen, RSBytes); + + return RSLen; +} + +// Main RS decode function + +extern int index_of[]; +extern int recd[]; +extern int Corrected[256]; +extern int tt; // number of errors that can be corrected +extern int kk; // Info Symbols + +extern BOOL blnErrorsCorrected; + + +BOOL RSDecode(UCHAR * bytRcv, int Length, int CheckLen, BOOL * blnRSOK) +{ + + + // Using a modified version of Henry Minsky's code + + //Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009 + + // Rick's Implementation processes the byte array in reverse. and also + // has the check bytes in the opposite order. I've modified the encoder + // to allow for this, but so far haven't found a way to mske the decoder + // work, so I have to reverse the data and checksum to decode G8BPQ Nov 2015 + + // returns TRUE if was ok or correction succeeded, FALSE if correction impossible + + UCHAR intTemp[256]; // WOrk Area to pass to Decoder + int i; + UCHAR * ptr2 = intTemp; + UCHAR * ptr1 = &bytRcv[Length - CheckLen - 1]; // Last Byte of Data + + int DataLen = Length - CheckLen; + int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes + + *blnRSOK = FALSE; + + if (Length > 255 || Length < (1 + CheckLen)) //Too long or too short + return FALSE; + + if (NPAR != CheckLen) // Changed RS Len, so recalc constants; + { + NPAR = CheckLen; + xMaxErrors = NPAR / 2; + + initialize_ecc(); + } + + + // We reverse the data while zero padding it to speed things up + + // We Need (Data Reversed) (Zero Padding) (Checkbytes Reversed) + + // Reverse Data + + for (i = 0; i < DataLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + // Clear padding + + memset(ptr2, 0, PadLength); + + ptr2 += PadLength; + + // Error Bits + + ptr1 = &bytRcv[Length - 1]; // End of check bytes + + for (i = 0; i < CheckLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + decode_data(intTemp, 255); + + // check if syndrome is all zeros + + if (check_syndrome() == 0) + { + // RS ok, so no need to correct + + *blnRSOK = TRUE; + return TRUE; // No Need to Correct + } + + if (correct_errors_erasures(intTemp, 255, 0, 0) == 0) // Dont support erasures at the momnet + + // Uncorrectable + + return FALSE; + + // Data has been corrected, so need to reverse again + + ptr1 = &intTemp[DataLen - 1]; + ptr2 = bytRcv; // Last Byte of Data + + for (i = 0; i < DataLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + // ?? Do we need to return the check bytes ?? + + // Yes, so we can redo RS Check on supposedly connected frame + + ptr1 = &intTemp[254]; // End of Check Bytes + + for (i = 0; i < CheckLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + return TRUE; +} + +extern TStringList detect_list[5]; +extern TStringList detect_list_c[5]; + +void ProcessPktFrame(int snd_ch, UCHAR * Data, int frameLen) +{ + string * pkt = newString(); + + stringAdd(pkt, Data, frameLen + 2); // 2 for crc (not actually there) + + analiz_frame(snd_ch, pkt, "ARDOP", 1); + +} diff --git a/SMMain.c b/SMMain.c new file mode 100644 index 0000000..b0edb42 --- /dev/null +++ b/SMMain.c @@ -0,0 +1,1394 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" +#include "fftw3.h" +#include +#include "ecc.h" // RS Constants +#include "hidapi.h" +#include +#include + +BOOL KISSServ; +int KISSPort; + +BOOL AGWServ; +int AGWPort; + +int Number = 0; // Number waiting to be sent + +int SoundIsPlaying = 0; +int UDPSoundIsPlaying = 0; +int Capturing = 0; + +extern unsigned short buffer[2][1200]; +extern int SoundMode; +extern int needRSID[4]; + +extern short * DMABuffer; + +unsigned short * SendtoCard(unsigned short * buf, int n); +short * SoundInit(); +void DoTX(int Chan); +void UDPPollReceivedSamples(); + + +extern int SampleNo; + +extern int pnt_change[5]; // Freq Changed Flag + +// fftw library interface + + +fftwf_complex *in, *out; +fftwf_plan p; + +int FFTSize = 4096; + +char * Wisdom; + +void initfft() +{ + fftwf_import_wisdom_from_string(Wisdom); + fftwf_set_timelimit(30); + +#ifndef WIN32 + printf("It may take up to 30 seconds for the program to start for the first time\n"); +#endif + + in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * 10000); + out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * 10000); + p = fftwf_plan_dft_1d(FFTSize, in, out, FFTW_FORWARD, FFTW_PATIENT); + + Wisdom = fftwf_export_wisdom_to_string(); +} + +void dofft(short * inp, float * outr, float * outi) +{ + int i; + + fftwf_complex * fft = in; + + for (i = 0; i < FFTSize; i++) + { + fft[0][0] = inp[0] * 1.0f; + fft[0][1] = 0; + fft++; + inp++; + } + + fftwf_execute(p); + + fft = out; + + for (i = 0; i < FFTSize; i++) + { + outr[0] = fft[0][0]; + outi[0] = fft[0][1]; + fft++; + outi++; + outr++; + } +} + +void freefft() +{ + fftwf_destroy_plan(p); + fftwf_free(in); + fftwf_free(out); +} + +int nonGUIMode = 0; + +void soundMain() +{ + // non platform specific initialisation + + platformInit(); + + // initialise fft library + + RsCreate(); // RS code for MPSK + + detector_init(); + KISS_init(); + ax25_init(); + init_raduga(); // Set up waterfall colour table + + initfft(); + + if (nonGUIMode) + { + Firstwaterfall = 0; + Secondwaterfall = 0; + } + + OpenPTTPort(); +} + + +void SampleSink(int LR, short Sample) +{ + // This version is passed samples one at a time, as we don't have + // enough RAM in embedded systems to hold a full audio frame + + // LR - 1 == Right Chan + +#ifdef TEENSY + int work = Sample; + DMABuffer[Number++] = (work + 32768) >> 4; // 12 bit left justify +#else + if (SCO) // Single Channel Output - same to both L and R + { + DMABuffer[2 * Number] = Sample; + DMABuffer[1 + 2 * Number] = Sample; + + } + else + { + if (LR) // Right + { + DMABuffer[1 + 2 * Number] = Sample; + DMABuffer[2 * Number] = 0; + } + else + { + DMABuffer[2 * Number] = Sample; + DMABuffer[1 + 2 * Number] = 0; + } + } + Number++; +#endif + if (Number >= SendSize) + { + // send this buffer to sound interface + + DMABuffer = SendtoCard(DMABuffer, SendSize); + Number = 0; + } + + +// Last120[Last120Put++] = Sample; + +// if (Last120Put == (intN + 1)) +// Last120Put = 0; + + SampleNo++; +} + + +void Flush() +{ + SoundFlush(Number); +} + +int ipow(int base, int exp) +{ + int result = 1; + while (exp) + { + if (exp & 1) + result *= base; + exp >>= 1; + base *= base; + } + + return result; +} + +int NumberOfBitsNeeded(int PowerOfTwo) +{ + int i; + + for (i = 0; i <= 16; i++) + { + if ((PowerOfTwo & ipow(2, i)) != 0) + return i; + + } + return 0; +} + + +int ReverseBits(int Index, int NumBits) +{ + int i, Rev = 0; + + for (i = 0; i < NumBits; i++) + { + Rev = (Rev * 2) | (Index & 1); + Index = Index / 2; + } + + return Rev; +} + + +void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform) +{ + float AngleNumerator; + unsigned char NumBits; + + int i, j, K, n, BlockSize, BlockEnd; + float DeltaAngle, DeltaAr; + float Alpha, Beta; + float TR, TI, AR, AI; + + if (InverseTransform) + AngleNumerator = -2.0f * M_PI; + else + AngleNumerator = 2.0f * M_PI; + + NumBits = NumberOfBitsNeeded(NumSamples); + + for (i = 0; i < NumSamples; i++) + { + j = ReverseBits(i, NumBits); + RealOut[j] = RealIn[i]; + ImagOut[j] = 0.0f; // Not using i in ImageIn[i]; + } + + BlockEnd = 1; + BlockSize = 2; + + while (BlockSize <= NumSamples) + { + DeltaAngle = AngleNumerator / BlockSize; + Alpha = sinf(0.5f * DeltaAngle); + Alpha = 2.0f * Alpha * Alpha; + Beta = sinf(DeltaAngle); + + i = 0; + + while (i < NumSamples) + { + AR = 1.0f; + AI = 0.0f; + + j = i; + + for (n = 0; n < BlockEnd; n++) + { + K = j + BlockEnd; + TR = AR * RealOut[K] - AI * ImagOut[K]; + TI = AI * RealOut[K] + AR * ImagOut[K]; + RealOut[K] = RealOut[j] - TR; + ImagOut[K] = ImagOut[j] - TI; + RealOut[j] = RealOut[j] + TR; + ImagOut[j] = ImagOut[j] + TI; + DeltaAr = Alpha * AR + Beta * AI; + AI = AI - (Alpha * AI - Beta * AR); + AR = AR - DeltaAr; + j = j + 1; + } + i = i + BlockSize; + } + BlockEnd = BlockSize; + BlockSize = BlockSize * 2; + } + + if (InverseTransform) + { + // Normalize the resulting time samples... + + for (i = 0; i < NumSamples; i++) + { + RealOut[i] = RealOut[i] / NumSamples; + ImagOut[i] = ImagOut[i] / NumSamples; + } + } +} + + + +int LastBusyCheck = 0; + +extern UCHAR CurrentLevel; + +#ifdef PLOTSPECTRUM +float dblMagSpectrum[206]; +float dblMaxScale = 0.0f; +extern UCHAR Pixels[4096]; +extern UCHAR * pixelPointer; +#endif + +extern int blnBusyStatus; +BusyDet = 0; + +#define PLOTWATERFALL + +int WaterfallActive = 1; +int SpectrumActive; + +/* + +void UpdateBusyDetector(short * bytNewSamples) +{ + float dblReF[1024]; + float dblImF[1024]; + float dblMag[206]; +#ifdef PLOTSPECTRUM + float dblMagMax = 0.0000000001f; + float dblMagMin = 10000000000.0f; +#endif + UCHAR Waterfall[256]; // Colour index values to send to GUI + int clrTLC = Lime; // Default Bandwidth lines on waterfall + + static BOOL blnLastBusyStatus; + + float dblMagAvg = 0; + int intTuneLineLow, intTuneLineHi, intDelta; + int i; + + // if (State != SearchingForLeader) + // return; // only when looking for leader + + if (Now - LastBusyCheck < 100) + return; + + LastBusyCheck = Now; + + FourierTransform(1024, bytNewSamples, &dblReF[0], &dblImF[0], FALSE); + + for (i = 0; i < 206; i++) + { + // starting at ~300 Hz to ~2700 Hz Which puts the center of the signal in the center of the window (~1500Hz) + + dblMag[i] = powf(dblReF[i + 25], 2) + powf(dblImF[i + 25], 2); // first pass + dblMagAvg += dblMag[i]; +#ifdef PLOTSPECTRUM + dblMagSpectrum[i] = 0.2f * dblMag[i] + 0.8f * dblMagSpectrum[i]; + dblMagMax = max(dblMagMax, dblMagSpectrum[i]); + dblMagMin = min(dblMagMin, dblMagSpectrum[i]); +#endif + } + + // LookforPacket(dblMag, dblMagAvg, 206, &dblReF[25], &dblImF[25]); + // packet_process_samples(bytNewSamples, 1200); + + intDelta = roundf(500 / 2) + 50 / 11.719f; + + intTuneLineLow = max((103 - intDelta), 3); + intTuneLineHi = min((103 + intDelta), 203); + +// if (ProtocolState == DISC) // ' Only process busy when in DISC state + { + // blnBusyStatus = BusyDetect3(dblMag, intTuneLineLow, intTuneLineHi); + + if (blnBusyStatus && !blnLastBusyStatus) + { +// QueueCommandToHost("BUSY TRUE"); +// newStatus = TRUE; // report to PTC + + if (!WaterfallActive && !SpectrumActive) + { + UCHAR Msg[2]; + +// Msg[0] = blnBusyStatus; +// SendtoGUI('B', Msg, 1); + } + } + // stcStatus.Text = "TRUE" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY TRUE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + else if (blnLastBusyStatus && !blnBusyStatus) + { +// QueueCommandToHost("BUSY FALSE"); +// newStatus = TRUE; // report to PTC + + if (!WaterfallActive && !SpectrumActive) + { + UCHAR Msg[2]; + + Msg[0] = blnBusyStatus; +// SendtoGUI('B', Msg, 1); + } + } + // stcStatus.Text = "FALSE" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY FALSE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + blnLastBusyStatus = blnBusyStatus; + } + + if (BusyDet == 0) + clrTLC = Goldenrod; + else if (blnBusyStatus) + clrTLC = Fuchsia; + + // At the moment we only get here what seaching for leader, + // but if we want to plot spectrum we should call + // it always + + + + if (WaterfallActive) + { +#ifdef PLOTWATERFALL + dblMagAvg = log10f(dblMagAvg / 5000.0f); + + for (i = 0; i < 206; i++) + { + // The following provides some AGC over the waterfall to compensate for avg input level. + + float y1 = (0.25f + 2.5f / dblMagAvg) * log10f(0.01 + dblMag[i]); + int objColor; + + // Set the pixel color based on the intensity (log) of the spectral line + if (y1 > 6.5) + objColor = Orange; // Strongest spectral line + else if (y1 > 6) + objColor = Khaki; + else if (y1 > 5.5) + objColor = Cyan; + else if (y1 > 5) + objColor = DeepSkyBlue; + else if (y1 > 4.5) + objColor = RoyalBlue; + else if (y1 > 4) + objColor = Navy; + else + objColor = Black; + + if (i == 102) + Waterfall[i] = Tomato; // 1500 Hz line (center) + else if (i == intTuneLineLow || i == intTuneLineLow - 1 || i == intTuneLineHi || i == intTuneLineHi + 1) + Waterfall[i] = clrTLC; + else + Waterfall[i] = objColor; // ' Else plot the pixel as received + } + + // Send Signal level and Busy indicator to save extra packets + + Waterfall[206] = CurrentLevel; + Waterfall[207] = blnBusyStatus; + + doWaterfall(Waterfall); +#endif + } + else if (SpectrumActive) + { +#ifdef PLOTSPECTRUM + // This performs an auto scaling mechansim with fast attack and slow release + if (dblMagMin / dblMagMax < 0.0001) // more than 10000:1 difference Max:Min + dblMaxScale = max(dblMagMax, dblMaxScale * 0.9f); + else + dblMaxScale = max(10000 * dblMagMin, dblMagMax); + +// clearDisplay(); + + for (i = 0; i < 206; i++) + { + // The following provides some AGC over the spectrum to compensate for avg input level. + + float y1 = -0.25f * (SpectrumHeight - 1) * log10f((max(dblMagSpectrum[i], dblMaxScale / 10000)) / dblMaxScale); // ' range should be 0 to bmpSpectrumHeight -1 + int objColor = Yellow; + + Waterfall[i] = round(y1); + } + + // Send Signal level and Busy indicator to save extra packets + + Waterfall[206] = CurrentLevel; + Waterfall[207] = blnBusyStatus; + Waterfall[208] = intTuneLineLow; + Waterfall[209] = intTuneLineHi; + +// SendtoGUI('X', Waterfall, 210); +#endif + } +} + +*/ + +extern short rawSamples[2400]; // Get Frame Type need 2400 and we may add 1200 +int rawSamplesLength = 0; +extern int maxrawSamplesLength; + +void ProcessNewSamples(short * Samples, int nSamples) +{ + if (SoundIsPlaying == FALSE && UDPSoundIsPlaying == FALSE) + BufferFull(Samples, nSamples); +}; + +void doCalib(int Chan, int Act) +{ + if (Chan == 0 && calib_mode[1]) + return; + + if (Chan == 1 && calib_mode[0]) + return; + + calib_mode[Chan] = Act; + + if (Act == 0) + { + tx_status[Chan] = TX_SILENCE; // Stop TX + Flush(); + RadioPTT(Chan, 0); + Debugprintf("Stop Calib"); + } +} + +int Freq_Change(int Chan, int Freq) +{ + int low, high; + + low = round(rx_shift[1] / 2 + RCVR[Chan] * rcvr_offset[Chan] + 1); + high = round(RX_Samplerate / 2 - (rx_shift[Chan] / 2 + RCVR[Chan] * rcvr_offset[Chan])); + + if (Freq < low) + return rx_freq[Chan]; // Dont allow change + + if (Freq > high) + return rx_freq[Chan]; // Dont allow change + + rx_freq[Chan] = Freq; + tx_freq[Chan] = Freq; + + pnt_change[Chan] = TRUE; + wf_pointer(soundChannel[Chan]); + + return Freq; +} + +void MainLoop() +{ + // Called by background thread every 10 ms (maybe) + + // Actually we may have two cards + + // Original only allowed one channel per card. + // I think we should be able to run more, ie two or more + // modems on same soundcard channel + + // So All the soundcard stuff will need to be generalised + + if (UDPServ) + UDPPollReceivedSamples(); + + if (SoundMode == 3) + UDPPollReceivedSamples(); + else + PollReceivedSamples(); + + + for (int i = 0; i < 4; i++) + { + if (modem_mode[i] == MODE_ARDOP) + { + chk_dcd1(i, 512); + } + } + DoTX(0); + DoTX(1); + DoTX(2); + DoTX(3); + +} + +int ARDOPSendToCard(int Chan, int Len) +{ + // Send Next Block of samples to the soundcard + + short * in = &ARDOPTXBuffer[Chan][ARDOPTXPtr[Chan]]; // Enough to hold whole frame of samples + short * out = DMABuffer; + + int LR = modemtoSoundLR[Chan]; + + int i; + + for (i = 0; i < Len; i++) + { + if (SCO) // Single Channel Output - same to both L and R + { + *out++ = *in; + *out++ = *in++; + } + else + { + if (LR) // Right + { + *out++ = 0; + *out++ = *in++; + } + else + { + *out++ = *in++; + *out++ = 0; + } + } + } + DMABuffer = SendtoCard(DMABuffer, Len); + + ARDOPTXPtr[Chan] += Len; + + // See if end of buffer + + if (ARDOPTXPtr[Chan] > ARDOPTXLen[Chan]) + return 1; + + return 0; +} +void DoTX(int Chan) +{ + // This kicks off a send sequence or calibrate + +// printtick("dotx"); + + if (calib_mode[Chan]) + { + // Maybe new calib or continuation + + if (pnt_change[Chan]) + { + make_core_BPF(Chan, rx_freq[Chan], bpf[Chan]); + make_core_TXBPF(Chan, tx_freq[Chan], txbpf[Chan]); + pnt_change[Chan] = FALSE; + } + + // Note this may block in SendtoCard + + modulator(Chan, tx_bufsize); + return; + } + + // I think we have to detect NO_DATA here and drop PTT and return to SILENCE + + if (tx_status[Chan] == TX_NO_DATA) + { + Flush(); + Debugprintf("TX Complete"); + RadioPTT(0, 0); + tx_status[Chan] = TX_SILENCE; + + // We should now send any ackmode acks as the channel is now free for dest to reply + + sendAckModeAcks(Chan); + } + + if (tx_status[Chan] != TX_SILENCE) + { + // Continue the send + + if (modem_mode[Chan] == MODE_ARDOP) + { +// if (SeeIfCardBusy()) +// return 0; + + if (ARDOPSendToCard(Chan, SendSize) == 1) + { + // End of TX + + Number = 0; + Flush(); + + // See if more to send. If so, don't drop PTT + + if (all_frame_buf[Chan].Count) + { + SoundIsPlaying = TRUE; + Number = 0; + + Debugprintf("TX Continuing"); + + string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message + string * tx_data; + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[Chan], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + tx_data = duplicateString(myTemp); // so can free original below + + Delete(&all_frame_buf[Chan], 0); // This will invalidate temp + + AGW_AX25_frame_analiz(Chan, FALSE, tx_data); + + put_frame(Chan, tx_data, "", TRUE, FALSE); + + PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + + freeString(tx_data); + + // Samples are now in DMABuffer = Send first block + + ARDOPSendToCard(Chan, SendSize); + tx_status[Chan] = TX_FRAME; + return; + } + + Debugprintf("TX Complete"); + RadioPTT(0, 0); + tx_status[Chan] = TX_SILENCE; + + // We should now send any ackmode acks as the channel is now free for dest to reply + } + + return; + } + + modulator(Chan, tx_bufsize); + return; + } + + if (SoundIsPlaying || UDPSoundIsPlaying) + return; + + // Not doing anything so see if we have anything new to send + + // See if frequency has changed + + if (pnt_change[Chan]) + { + make_core_BPF(Chan, rx_freq[Chan], bpf[Chan]); + make_core_TXBPF(Chan, tx_freq[Chan], txbpf[Chan]); + pnt_change[Chan] = FALSE; + } + + // See if we need an RSID + + if (needRSID[Chan]) + { + needRSID[Chan] = 0; + + // Note this may block in SampleSink + + Debugprintf("Sending RSID"); + sendRSID(Chan, all_frame_buf[Chan].Count == 0); + return; + } + + if (all_frame_buf[Chan].Count == 0) + return; + + // Start a new send. modulator should handle TXD etc + + Debugprintf("TX Start"); + SampleNo = 0; + + SoundIsPlaying = TRUE; + RadioPTT(Chan, 1); + Number = 0; + + if (modem_mode[Chan] == MODE_ARDOP) + { + // I think ARDOP will have to generate a whole frame of samples + // then send them out a bit at a time to avoid stopping here for + // possibly 10's of seconds + + // Can do this here as unlike normal ardop we don't need to run on Teensy + // to 12000 sample rate we need either 24K or 48K per second, depending on + // where we do the stereo mux. + + // Slowest rate is 50 baud, so a 255 byte packet would take about a minute + // allowing for RS overhead. Not really realistic put perhaps should be possible. + // RAM isn't an issue so maybe allocate 2 MB. + + // ?? Should we allow two ARDOP modems - could make sense if we can run sound + // card channels independently + + string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message + string * tx_data; + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[Chan], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + tx_data = duplicateString(myTemp); // so can free original below + + Delete(&all_frame_buf[Chan], 0); // This will invalidate temp + + AGW_AX25_frame_analiz(Chan, FALSE, tx_data); + + put_frame(Chan, tx_data, "", TRUE, FALSE); + + PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + + freeString(tx_data); + + // Samples are now in DMABuffer = Send first block + + ARDOPSendToCard(Chan, SendSize); + tx_status[Chan] = TX_FRAME; + + } + else + modulator(Chan, tx_bufsize); + + return; +} + +void stoptx(int snd_ch) +{ + Flush(); + Debugprintf("TX Complete"); + RadioPTT(snd_ch, 0); + tx_status[snd_ch] = TX_SILENCE; + + snd_status[snd_ch] = SND_IDLE; +} + +void RX2TX(int snd_ch) +{ + if (snd_status[snd_ch] == SND_IDLE) + { + DoTX(snd_ch); + } +} + +// PTT Stuff + +int hPTTDevice = 0; +char PTTPort[80] = ""; // Port for Hardware PTT - may be same as control port. +int PTTBAUD = 19200; +int PTTMode = PTTRTS; // PTT Control Flags. + +char PTTOnString[128] = ""; +char PTTOffString[128] = ""; + +UCHAR PTTOnCmd[64]; +UCHAR PTTOnCmdLen = 0; + +UCHAR PTTOffCmd[64]; +UCHAR PTTOffCmdLen = 0; + +int pttGPIOPin = 17; // Default +int pttGPIOPinR = 17; +BOOL pttGPIOInvert = FALSE; +BOOL useGPIO = FALSE; +BOOL gotGPIO = FALSE; + +int HamLibPort = 4532; +char HamLibHost[32] = "192.168.1.14"; + +char CM108Addr[80] = ""; + +int VID = 0; +int PID = 0; + +// CM108 Code + +char * CM108Device = NULL; + +void DecodeCM108(char * ptr) +{ + // Called if Device Name or PTT = Param is CM108 + +#ifdef WIN32 + + // Next Param is VID and PID - 0xd8c:0x8 or Full device name + // On Windows device name is very long and difficult to find, so + // easier to use VID/PID, but allow device in case more than one needed + + char * next; + long VID = 0, PID = 0; + char product[256] = "Unknown"; + + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + if (strlen(ptr) > 16) + CM108Device = _strdup(ptr); + else + { + VID = strtol(ptr, &next, 0); + if (next) + PID = strtol(++next, &next, 0); + + // Look for Device + + devs = hid_enumerate((unsigned short)VID, (unsigned short)PID); + cur_dev = devs; + + while (cur_dev) + { + if (cur_dev->product_string) + wcstombs(product, cur_dev->product_string, 255); + + Debugprintf("HID Device %s VID %X PID %X", product, cur_dev->vendor_id, cur_dev->product_id); + if (cur_dev->vendor_id == VID && cur_dev->product_id == PID) + { + path_to_open = cur_dev->path; + break; + } + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + handle = hid_open_path(path_to_open); + + if (handle) + { + hid_close(handle); + CM108Device = _strdup(path_to_open); + } + else + { + Debugprintf("Unable to open CM108 device %x %x", VID, PID); + } + } + else + Debugprintf("Couldn't find CM108 device %x %x", VID, PID); + + hid_free_enumeration(devs); + } +#else + + // Linux - Next Param HID Device, eg /dev/hidraw0 + + CM108Device = _strdup(ptr); +#endif +} + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++ = 0; + return ptr; +} + +void OpenPTTPort() +{ + PTTMode &= ~PTTCM108; + PTTMode &= ~PTTHAMLIB; + + if (PTTPort[0] && strcmp(PTTPort, "None") != 0) + { + if (PTTMode == PTTCAT) + { + // convert config strings from Hex + + char * ptr1 = PTTOffString; + UCHAR * ptr2 = PTTOffCmd; + char c; + int val; + + while (c = *(ptr1++)) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + PTTOffCmdLen = ptr2 - PTTOffCmd; + + ptr1 = PTTOnString; + ptr2 = PTTOnCmd; + + while (c = *(ptr1++)) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + PTTOnCmdLen = ptr2 - PTTOnCmd; + } + + if (stricmp(PTTPort, "GPIO") == 0) + { + // Initialise GPIO for PTT if available + +#ifdef __ARM_ARCH + + if (gpioInitialise() == 0) + { + printf("GPIO interface for PTT available\n"); + gotGPIO = TRUE; + + SetupGPIOPTT(); + } + else + printf("Couldn't initialise GPIO interface for PTT\n"); + +#else + printf("GPIO interface for PTT not available on this platform\n"); +#endif + + } + else if (stricmp(PTTPort, "CM108") == 0) + { + DecodeCM108(CM108Addr); + PTTMode |= PTTCM108; + } + + else if (stricmp(PTTPort, "HAMLIB") == 0) + { + PTTMode |= PTTHAMLIB; + HAMLIBSetPTT(0); // to open port + return; + } + + else // Not GPIO + { + hPTTDevice = OpenCOMPort(PTTPort, PTTBAUD, FALSE, FALSE, FALSE, 0); + } + } +} + +void ClosePTTPort() +{ + CloseCOMPort(hPTTDevice); + hPTTDevice = 0; +} +void CM108_set_ptt(int PTTState) +{ + char io[5]; + hid_device *handle; + int n; + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + if (CM108Device == NULL) + return; + +#ifdef WIN32 + handle = hid_open_path(CM108Device); + + if (!handle) { + printf("unable to open device\n"); + return; + } + + n = hid_write(handle, io, 5); + if (n < 0) + { + printf("Unable to write()\n"); + printf("Error: %ls\n", hid_error(handle)); + } + + hid_close(handle); + +#else + + int fd; + + fd = open(CM108Device, O_WRONLY); + + if (fd == -1) + { + printf("Could not open %s for write, errno=%d\n", CM108Device, errno); + return; + } + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + n = write(fd, io, 5); + if (n != 5) + { + printf("Write to %s failed, n=%d, errno=%d\n", CM108Device, n, errno); + } + + close(fd); +#endif + return; + +} + + + +void RadioPTT(int snd_ch, BOOL PTTState) +{ +#ifdef __ARM_ARCH + if (useGPIO) + { + if (DualPTT && modemtoSoundLR[snd_ch] == 1) + gpioWrite(pttGPIOPinR, (pttGPIOInvert ? (1 - PTTState) : (PTTState))); + else + gpioWrite(pttGPIOPin, (pttGPIOInvert ? (1 - PTTState) : (PTTState))); + + return; + } + +#endif + + if ((PTTMode & PTTCM108)) + { + CM108_set_ptt(PTTState); + return; + } + + if ((PTTMode & PTTHAMLIB)) + { + HAMLIBSetPTT(PTTState); + return; + } + if (hPTTDevice == 0) + return; + + if ((PTTMode & PTTCAT)) + { + if (PTTState) + WriteCOMBlock(hPTTDevice, PTTOnCmd, PTTOnCmdLen); + else + WriteCOMBlock(hPTTDevice, PTTOffCmd, PTTOffCmdLen); + + return; + } + + if (DualPTT && modemtoSoundLR[snd_ch] == 1) // use DTR + { + if (PTTState) + COMSetDTR(hPTTDevice); + else + COMClearDTR(hPTTDevice); + } + else + { + if ((PTTMode & PTTRTS)) + { + if (PTTState) + COMSetRTS(hPTTDevice); + else + COMClearRTS(hPTTDevice); + } + } + +} + +char ShortDT[] = "HH:MM:SS"; + +char * ShortDateTime() +{ + struct tm * tm; + time_t NOW = time(NULL); + + tm = gmtime(&NOW); + + sprintf(ShortDT, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); + return ShortDT; +} + + +// Reed Solomon Stuff + + +int NPAR = -1; // Number of Parity Bytes - used in RS Code + +int xMaxErrors = 0; + +int RSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen) +{ + // This just returns the Parity Bytes. I don't see the point + // in copying the message about + + unsigned char Padded[256]; // The padded Data + + int Length = DataLen + RSLen; // Final Length of packet + int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes + + // subroutine to do the RS encode. For full length and shortend RS codes up to 8 bit symbols (mm = 8) + + if (NPAR != RSLen) // Changed RS Len, so recalc constants; + { + NPAR = RSLen; + xMaxErrors = NPAR / 2; + initialize_ecc(); + } + + // Copy the supplied data to end of data array. + + memset(Padded, 0, PadLength); + memcpy(&Padded[PadLength], bytToRS, DataLen); + + encode_data(Padded, 255 - RSLen, RSBytes); + + return RSLen; +} + +// Main RS decode function + +extern int index_of[]; +extern int recd[]; +extern int Corrected[256]; +extern int tt; // number of errors that can be corrected +extern int kk; // Info Symbols + +extern BOOL blnErrorsCorrected; + + +BOOL RSDecode(UCHAR * bytRcv, int Length, int CheckLen, BOOL * blnRSOK) +{ + + + // Using a modified version of Henry Minsky's code + + //Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009 + + // Rick's Implementation processes the byte array in reverse. and also + // has the check bytes in the opposite order. I've modified the encoder + // to allow for this, but so far haven't found a way to mske the decoder + // work, so I have to reverse the data and checksum to decode G8BPQ Nov 2015 + + // returns TRUE if was ok or correction succeeded, FALSE if correction impossible + + UCHAR intTemp[256]; // WOrk Area to pass to Decoder + int i; + UCHAR * ptr2 = intTemp; + UCHAR * ptr1 = &bytRcv[Length - CheckLen - 1]; // Last Byte of Data + + int DataLen = Length - CheckLen; + int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes + + *blnRSOK = FALSE; + + if (Length > 255 || Length < (1 + CheckLen)) //Too long or too short + return FALSE; + + if (NPAR != CheckLen) // Changed RS Len, so recalc constants; + { + NPAR = CheckLen; + xMaxErrors = NPAR / 2; + + initialize_ecc(); + } + + + // We reverse the data while zero padding it to speed things up + + // We Need (Data Reversed) (Zero Padding) (Checkbytes Reversed) + + // Reverse Data + + for (i = 0; i < DataLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + // Clear padding + + memset(ptr2, 0, PadLength); + + ptr2 += PadLength; + + // Error Bits + + ptr1 = &bytRcv[Length - 1]; // End of check bytes + + for (i = 0; i < CheckLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + decode_data(intTemp, 255); + + // check if syndrome is all zeros + + if (check_syndrome() == 0) + { + // RS ok, so no need to correct + + *blnRSOK = TRUE; + return TRUE; // No Need to Correct + } + + if (correct_errors_erasures(intTemp, 255, 0, 0) == 0) // Dont support erasures at the momnet + + // Uncorrectable + + return FALSE; + + // Data has been corrected, so need to reverse again + + ptr1 = &intTemp[DataLen - 1]; + ptr2 = bytRcv; // Last Byte of Data + + for (i = 0; i < DataLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + // ?? Do we need to return the check bytes ?? + + // Yes, so we can redo RS Check on supposedly connected frame + + ptr1 = &intTemp[254]; // End of Check Bytes + + for (i = 0; i < CheckLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + return TRUE; +} + +extern TStringList detect_list[5]; +extern TStringList detect_list_c[5]; + +void ProcessPktFrame(int snd_ch, UCHAR * Data, int frameLen) +{ + string * pkt = newString(); + + stringAdd(pkt, Data, frameLen + 2); // 2 for crc (not actually there) + + analiz_frame(snd_ch, pkt, "ARDOP", 1); + +} diff --git a/SMMain.c.bak b/SMMain.c.bak new file mode 100644 index 0000000..652911e --- /dev/null +++ b/SMMain.c.bak @@ -0,0 +1,1383 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" +#include "fftw3.h" +#include +#include "ecc.h" // RS Constants +#include "hidapi.h" +#include +#include + +BOOL KISSServ; +int KISSPort; + +BOOL AGWServ; +int AGWPort; + +int Number = 0; // Number waiting to be sent + +int SoundIsPlaying = 0; +int UDPSoundIsPlaying = 0; +int Capturing = 0; + +extern unsigned short buffer[2][1200]; +extern int SoundMode; +extern int needRSID[4]; + +extern short * DMABuffer; + +unsigned short * SendtoCard(unsigned short * buf, int n); +short * SoundInit(); +void DoTX(int Chan); +void UDPPollReceivedSamples(); + + +extern int SampleNo; + +extern int pnt_change[5]; // Freq Changed Flag + +// fftw library interface + + +fftwf_complex *in, *out; +fftwf_plan p; + +#define N 2048 + +void initfft() +{ + in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * N); + out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * N); + p = fftwf_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE); +} + +void dofft(short * inp, float * outr, float * outi) +{ + int i; + + fftwf_complex * fft = in; + + for (i = 0; i < N; i++) + { + fft[0][0] = inp[0] * 1.0f; + fft[0][1] = 0; + fft++; + inp++; + } + + fftwf_execute(p); + + fft = out; + + for (i = 0; i < N; i++) + { + outr[0] = fft[0][0]; + outi[0] = fft[0][1]; + fft++; + outi++; + outr++; + } +} + +void freefft() +{ + fftwf_destroy_plan(p); + fftwf_free(in); + fftwf_free(out); +} + +int nonGUIMode = 0; + +void soundMain() +{ + // non platform specific initialisation + + platformInit(); + + // initialise fft library + + RsCreate(); // RS code for MPSK + + detector_init(); + KISS_init(); + ax25_init(); + init_raduga(); // Set up waterfall colour table + + initfft(); + + if (nonGUIMode) + { + Firstwaterfall = 0; + Secondwaterfall = 0; + } + + OpenPTTPort(); +} + + +void SampleSink(int LR, short Sample) +{ + // This version is passed samples one at a time, as we don't have + // enough RAM in embedded systems to hold a full audio frame + + // LR - 1 == Right Chan + +#ifdef TEENSY + int work = Sample; + DMABuffer[Number++] = (work + 32768) >> 4; // 12 bit left justify +#else + if (SCO) // Single Channel Output - same to both L and R + { + DMABuffer[2 * Number] = Sample; + DMABuffer[1 + 2 * Number] = Sample; + + } + else + { + if (LR) // Right + { + DMABuffer[1 + 2 * Number] = Sample; + DMABuffer[2 * Number] = 0; + } + else + { + DMABuffer[2 * Number] = Sample; + DMABuffer[1 + 2 * Number] = 0; + } + } + Number++; +#endif + if (Number >= SendSize) + { + // send this buffer to sound interface + + DMABuffer = SendtoCard(DMABuffer, SendSize); + Number = 0; + } + + +// Last120[Last120Put++] = Sample; + +// if (Last120Put == (intN + 1)) +// Last120Put = 0; + + SampleNo++; +} + + +void Flush() +{ + SoundFlush(Number); +} + +int ipow(int base, int exp) +{ + int result = 1; + while (exp) + { + if (exp & 1) + result *= base; + exp >>= 1; + base *= base; + } + + return result; +} + +int NumberOfBitsNeeded(int PowerOfTwo) +{ + int i; + + for (i = 0; i <= 16; i++) + { + if ((PowerOfTwo & ipow(2, i)) != 0) + return i; + + } + return 0; +} + + +int ReverseBits(int Index, int NumBits) +{ + int i, Rev = 0; + + for (i = 0; i < NumBits; i++) + { + Rev = (Rev * 2) | (Index & 1); + Index = Index / 2; + } + + return Rev; +} + + +void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform) +{ + float AngleNumerator; + unsigned char NumBits; + + int i, j, K, n, BlockSize, BlockEnd; + float DeltaAngle, DeltaAr; + float Alpha, Beta; + float TR, TI, AR, AI; + + if (InverseTransform) + AngleNumerator = -2.0f * M_PI; + else + AngleNumerator = 2.0f * M_PI; + + NumBits = NumberOfBitsNeeded(NumSamples); + + for (i = 0; i < NumSamples; i++) + { + j = ReverseBits(i, NumBits); + RealOut[j] = RealIn[i]; + ImagOut[j] = 0.0f; // Not using i in ImageIn[i]; + } + + BlockEnd = 1; + BlockSize = 2; + + while (BlockSize <= NumSamples) + { + DeltaAngle = AngleNumerator / BlockSize; + Alpha = sinf(0.5f * DeltaAngle); + Alpha = 2.0f * Alpha * Alpha; + Beta = sinf(DeltaAngle); + + i = 0; + + while (i < NumSamples) + { + AR = 1.0f; + AI = 0.0f; + + j = i; + + for (n = 0; n < BlockEnd; n++) + { + K = j + BlockEnd; + TR = AR * RealOut[K] - AI * ImagOut[K]; + TI = AI * RealOut[K] + AR * ImagOut[K]; + RealOut[K] = RealOut[j] - TR; + ImagOut[K] = ImagOut[j] - TI; + RealOut[j] = RealOut[j] + TR; + ImagOut[j] = ImagOut[j] + TI; + DeltaAr = Alpha * AR + Beta * AI; + AI = AI - (Alpha * AI - Beta * AR); + AR = AR - DeltaAr; + j = j + 1; + } + i = i + BlockSize; + } + BlockEnd = BlockSize; + BlockSize = BlockSize * 2; + } + + if (InverseTransform) + { + // Normalize the resulting time samples... + + for (i = 0; i < NumSamples; i++) + { + RealOut[i] = RealOut[i] / NumSamples; + ImagOut[i] = ImagOut[i] / NumSamples; + } + } +} + + + +int LastBusyCheck = 0; + +extern UCHAR CurrentLevel; + +#ifdef PLOTSPECTRUM +float dblMagSpectrum[206]; +float dblMaxScale = 0.0f; +extern UCHAR Pixels[4096]; +extern UCHAR * pixelPointer; +#endif + +extern int blnBusyStatus; +BusyDet = 0; + +#define PLOTWATERFALL + +int WaterfallActive = 1; +int SpectrumActive; + +/* + +void UpdateBusyDetector(short * bytNewSamples) +{ + float dblReF[1024]; + float dblImF[1024]; + float dblMag[206]; +#ifdef PLOTSPECTRUM + float dblMagMax = 0.0000000001f; + float dblMagMin = 10000000000.0f; +#endif + UCHAR Waterfall[256]; // Colour index values to send to GUI + int clrTLC = Lime; // Default Bandwidth lines on waterfall + + static BOOL blnLastBusyStatus; + + float dblMagAvg = 0; + int intTuneLineLow, intTuneLineHi, intDelta; + int i; + + // if (State != SearchingForLeader) + // return; // only when looking for leader + + if (Now - LastBusyCheck < 100) + return; + + LastBusyCheck = Now; + + FourierTransform(1024, bytNewSamples, &dblReF[0], &dblImF[0], FALSE); + + for (i = 0; i < 206; i++) + { + // starting at ~300 Hz to ~2700 Hz Which puts the center of the signal in the center of the window (~1500Hz) + + dblMag[i] = powf(dblReF[i + 25], 2) + powf(dblImF[i + 25], 2); // first pass + dblMagAvg += dblMag[i]; +#ifdef PLOTSPECTRUM + dblMagSpectrum[i] = 0.2f * dblMag[i] + 0.8f * dblMagSpectrum[i]; + dblMagMax = max(dblMagMax, dblMagSpectrum[i]); + dblMagMin = min(dblMagMin, dblMagSpectrum[i]); +#endif + } + + // LookforPacket(dblMag, dblMagAvg, 206, &dblReF[25], &dblImF[25]); + // packet_process_samples(bytNewSamples, 1200); + + intDelta = roundf(500 / 2) + 50 / 11.719f; + + intTuneLineLow = max((103 - intDelta), 3); + intTuneLineHi = min((103 + intDelta), 203); + +// if (ProtocolState == DISC) // ' Only process busy when in DISC state + { + // blnBusyStatus = BusyDetect3(dblMag, intTuneLineLow, intTuneLineHi); + + if (blnBusyStatus && !blnLastBusyStatus) + { +// QueueCommandToHost("BUSY TRUE"); +// newStatus = TRUE; // report to PTC + + if (!WaterfallActive && !SpectrumActive) + { + UCHAR Msg[2]; + +// Msg[0] = blnBusyStatus; +// SendtoGUI('B', Msg, 1); + } + } + // stcStatus.Text = "TRUE" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY TRUE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + else if (blnLastBusyStatus && !blnBusyStatus) + { +// QueueCommandToHost("BUSY FALSE"); +// newStatus = TRUE; // report to PTC + + if (!WaterfallActive && !SpectrumActive) + { + UCHAR Msg[2]; + + Msg[0] = blnBusyStatus; +// SendtoGUI('B', Msg, 1); + } + } + // stcStatus.Text = "FALSE" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY FALSE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + blnLastBusyStatus = blnBusyStatus; + } + + if (BusyDet == 0) + clrTLC = Goldenrod; + else if (blnBusyStatus) + clrTLC = Fuchsia; + + // At the moment we only get here what seaching for leader, + // but if we want to plot spectrum we should call + // it always + + + + if (WaterfallActive) + { +#ifdef PLOTWATERFALL + dblMagAvg = log10f(dblMagAvg / 5000.0f); + + for (i = 0; i < 206; i++) + { + // The following provides some AGC over the waterfall to compensate for avg input level. + + float y1 = (0.25f + 2.5f / dblMagAvg) * log10f(0.01 + dblMag[i]); + int objColor; + + // Set the pixel color based on the intensity (log) of the spectral line + if (y1 > 6.5) + objColor = Orange; // Strongest spectral line + else if (y1 > 6) + objColor = Khaki; + else if (y1 > 5.5) + objColor = Cyan; + else if (y1 > 5) + objColor = DeepSkyBlue; + else if (y1 > 4.5) + objColor = RoyalBlue; + else if (y1 > 4) + objColor = Navy; + else + objColor = Black; + + if (i == 102) + Waterfall[i] = Tomato; // 1500 Hz line (center) + else if (i == intTuneLineLow || i == intTuneLineLow - 1 || i == intTuneLineHi || i == intTuneLineHi + 1) + Waterfall[i] = clrTLC; + else + Waterfall[i] = objColor; // ' Else plot the pixel as received + } + + // Send Signal level and Busy indicator to save extra packets + + Waterfall[206] = CurrentLevel; + Waterfall[207] = blnBusyStatus; + + doWaterfall(Waterfall); +#endif + } + else if (SpectrumActive) + { +#ifdef PLOTSPECTRUM + // This performs an auto scaling mechansim with fast attack and slow release + if (dblMagMin / dblMagMax < 0.0001) // more than 10000:1 difference Max:Min + dblMaxScale = max(dblMagMax, dblMaxScale * 0.9f); + else + dblMaxScale = max(10000 * dblMagMin, dblMagMax); + +// clearDisplay(); + + for (i = 0; i < 206; i++) + { + // The following provides some AGC over the spectrum to compensate for avg input level. + + float y1 = -0.25f * (SpectrumHeight - 1) * log10f((max(dblMagSpectrum[i], dblMaxScale / 10000)) / dblMaxScale); // ' range should be 0 to bmpSpectrumHeight -1 + int objColor = Yellow; + + Waterfall[i] = round(y1); + } + + // Send Signal level and Busy indicator to save extra packets + + Waterfall[206] = CurrentLevel; + Waterfall[207] = blnBusyStatus; + Waterfall[208] = intTuneLineLow; + Waterfall[209] = intTuneLineHi; + +// SendtoGUI('X', Waterfall, 210); +#endif + } +} + +*/ + +extern short rawSamples[2400]; // Get Frame Type need 2400 and we may add 1200 +int rawSamplesLength = 0; +extern int maxrawSamplesLength; + +void ProcessNewSamples(short * Samples, int nSamples) +{ + if (SoundIsPlaying == FALSE && UDPSoundIsPlaying == FALSE) + BufferFull(Samples, nSamples); +}; + +void doCalib(int Chan, int Act) +{ + if (Chan == 0 && calib_mode[1]) + return; + + if (Chan == 1 && calib_mode[0]) + return; + + calib_mode[Chan] = Act; + + if (Act == 0) + { + tx_status[Chan] = TX_SILENCE; // Stop TX + Flush(); + RadioPTT(Chan, 0); + Debugprintf("Stop Calib"); + } +} + +int Freq_Change(int Chan, int Freq) +{ + int low, high; + + low = round(rx_shift[1] / 2 + RCVR[Chan] * rcvr_offset[Chan] + 1); + high = round(RX_Samplerate / 2 - (rx_shift[Chan] / 2 + RCVR[Chan] * rcvr_offset[Chan])); + + if (Freq < low) + return rx_freq[Chan]; // Dont allow change + + if (Freq > high) + return rx_freq[Chan]; // Dont allow change + + rx_freq[Chan] = Freq; + tx_freq[Chan] = Freq; + + pnt_change[Chan] = TRUE; + wf_pointer(soundChannel[Chan]); + + return Freq; +} + +void MainLoop() +{ + // Called by background thread every 10 ms (maybe) + + // Actually we may have two cards + + // Original only allowed one channel per card. + // I think we should be able to run more, ie two or more + // modems on same soundcard channel + + // So All the soundcard stuff will need to be generalised + + if (UDPServ) + UDPPollReceivedSamples(); + + if (SoundMode == 3) + UDPPollReceivedSamples(); + else + PollReceivedSamples(); + + + for (int i = 0; i < 4; i++) + { + if (modem_mode[i] == MODE_ARDOP) + { + chk_dcd1(i, 512); + } + } + DoTX(0); + DoTX(1); + DoTX(2); + DoTX(3); + +} + +int ARDOPSendToCard(int Chan, int Len) +{ + // Send Next Block of samples to the soundcard + + short * in = &ARDOPTXBuffer[Chan][ARDOPTXPtr[Chan]]; // Enough to hold whole frame of samples + short * out = DMABuffer; + + int LR = modemtoSoundLR[Chan]; + + int i; + + for (i = 0; i < Len; i++) + { + if (SCO) // Single Channel Output - same to both L and R + { + *out++ = *in; + *out++ = *in++; + } + else + { + if (LR) // Right + { + *out++ = 0; + *out++ = *in++; + } + else + { + *out++ = *in++; + *out++ = 0; + } + } + } + DMABuffer = SendtoCard(DMABuffer, Len); + + ARDOPTXPtr[Chan] += Len; + + // See if end of buffer + + if (ARDOPTXPtr[Chan] > ARDOPTXLen[Chan]) + return 1; + + return 0; +} +void DoTX(int Chan) +{ + // This kicks off a send sequence or calibrate + +// printtick("dotx"); + + if (calib_mode[Chan]) + { + // Maybe new calib or continuation + + if (pnt_change[Chan]) + { + make_core_BPF(Chan, rx_freq[Chan], bpf[Chan]); + make_core_TXBPF(Chan, tx_freq[Chan], txbpf[Chan]); + pnt_change[Chan] = FALSE; + } + + // Note this may block in SendtoCard + + modulator(Chan, tx_bufsize); + return; + } + + // I think we have to detect NO_DATA here and drop PTT and return to SILENCE + + if (tx_status[Chan] == TX_NO_DATA) + { + Flush(); + Debugprintf("TX Complete"); + RadioPTT(0, 0); + tx_status[Chan] = TX_SILENCE; + + // We should now send any ackmode acks as the channel is now free for dest to reply + + sendAckModeAcks(Chan); + } + + if (tx_status[Chan] != TX_SILENCE) + { + // Continue the send + + if (modem_mode[Chan] == MODE_ARDOP) + { +// if (SeeIfCardBusy()) +// return 0; + + if (ARDOPSendToCard(Chan, SendSize) == 1) + { + // End of TX + + Number = 0; + Flush(); + + // See if more to send. If so, don't drop PTT + + if (all_frame_buf[Chan].Count) + { + SoundIsPlaying = TRUE; + Number = 0; + + Debugprintf("TX Continuing"); + + string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message + string * tx_data; + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[Chan], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + tx_data = duplicateString(myTemp); // so can free original below + + Delete(&all_frame_buf[Chan], 0); // This will invalidate temp + + AGW_AX25_frame_analiz(Chan, FALSE, tx_data); + + put_frame(Chan, tx_data, "", TRUE, FALSE); + + PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + + freeString(tx_data); + + // Samples are now in DMABuffer = Send first block + + ARDOPSendToCard(Chan, SendSize); + tx_status[Chan] = TX_FRAME; + return; + } + + Debugprintf("TX Complete"); + RadioPTT(0, 0); + tx_status[Chan] = TX_SILENCE; + + // We should now send any ackmode acks as the channel is now free for dest to reply + } + + return; + } + + modulator(Chan, tx_bufsize); + return; + } + + if (SoundIsPlaying || UDPSoundIsPlaying) + return; + + // Not doing anything so see if we have anything new to send + + // See if frequency has changed + + if (pnt_change[Chan]) + { + make_core_BPF(Chan, rx_freq[Chan], bpf[Chan]); + make_core_TXBPF(Chan, tx_freq[Chan], txbpf[Chan]); + pnt_change[Chan] = FALSE; + } + + // See if we need an RSID + + if (needRSID[Chan]) + { + needRSID[Chan] = 0; + + // Note this may block in SampleSink + + Debugprintf("Sending RSID"); + sendRSID(Chan, all_frame_buf[Chan].Count == 0); + return; + } + + if (all_frame_buf[Chan].Count == 0) + return; + + // Start a new send. modulator should handle TXD etc + + Debugprintf("TX Start"); + SampleNo = 0; + + SoundIsPlaying = TRUE; + RadioPTT(Chan, 1); + Number = 0; + + if (modem_mode[Chan] == MODE_ARDOP) + { + // I think ARDOP will have to generate a whole frame of samples + // then send them out a bit at a time to avoid stopping here for + // possibly 10's of seconds + + // Can do this here as unlike normal ardop we don't need to run on Teensy + // to 12000 sample rate we need either 24K or 48K per second, depending on + // where we do the stereo mux. + + // Slowest rate is 50 baud, so a 255 byte packet would take about a minute + // allowing for RS overhead. Not really realistic put perhaps should be possible. + // RAM isn't an issue so maybe allocate 2 MB. + + // ?? Should we allow two ARDOP modems - could make sense if we can run sound + // card channels independently + + string * myTemp = Strings(&all_frame_buf[Chan], 0); // get message + string * tx_data; + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[Chan], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + tx_data = duplicateString(myTemp); // so can free original below + + Delete(&all_frame_buf[Chan], 0); // This will invalidate temp + + AGW_AX25_frame_analiz(Chan, FALSE, tx_data); + + put_frame(Chan, tx_data, "", TRUE, FALSE); + + PktARDOPEncode(tx_data->Data, tx_data->Length - 2, Chan); + + freeString(tx_data); + + // Samples are now in DMABuffer = Send first block + + ARDOPSendToCard(Chan, SendSize); + tx_status[Chan] = TX_FRAME; + + } + else + modulator(Chan, tx_bufsize); + + return; +} + +void stoptx(int snd_ch) +{ + Flush(); + Debugprintf("TX Complete"); + RadioPTT(snd_ch, 0); + tx_status[snd_ch] = TX_SILENCE; + + snd_status[snd_ch] = SND_IDLE; +} + +void RX2TX(int snd_ch) +{ + if (snd_status[snd_ch] == SND_IDLE) + { + DoTX(snd_ch); + } +} + +// PTT Stuff + +int hPTTDevice = 0; +char PTTPort[80] = ""; // Port for Hardware PTT - may be same as control port. +int PTTBAUD = 19200; +int PTTMode = PTTRTS; // PTT Control Flags. + +char PTTOnString[128] = ""; +char PTTOffString[128] = ""; + +UCHAR PTTOnCmd[64]; +UCHAR PTTOnCmdLen = 0; + +UCHAR PTTOffCmd[64]; +UCHAR PTTOffCmdLen = 0; + +int pttGPIOPin = 17; // Default +int pttGPIOPinR = 17; +BOOL pttGPIOInvert = FALSE; +BOOL useGPIO = FALSE; +BOOL gotGPIO = FALSE; + +int HamLibPort = 4532; +char HamLibHost[32] = "192.168.1.14"; + +char CM108Addr[80] = ""; + +int VID = 0; +int PID = 0; + +// CM108 Code + +char * CM108Device = NULL; + +void DecodeCM108(char * ptr) +{ + // Called if Device Name or PTT = Param is CM108 + +#ifdef WIN32 + + // Next Param is VID and PID - 0xd8c:0x8 or Full device name + // On Windows device name is very long and difficult to find, so + // easier to use VID/PID, but allow device in case more than one needed + + char * next; + long VID = 0, PID = 0; + char product[256] = "Unknown"; + + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + if (strlen(ptr) > 16) + CM108Device = _strdup(ptr); + else + { + VID = strtol(ptr, &next, 0); + if (next) + PID = strtol(++next, &next, 0); + + // Look for Device + + devs = hid_enumerate((unsigned short)VID, (unsigned short)PID); + cur_dev = devs; + + while (cur_dev) + { + if (cur_dev->product_string) + wcstombs(product, cur_dev->product_string, 255); + + Debugprintf("HID Device %s VID %X PID %X", product, cur_dev->vendor_id, cur_dev->product_id); + if (cur_dev->vendor_id == VID && cur_dev->product_id == PID) + { + path_to_open = cur_dev->path; + break; + } + cur_dev = cur_dev->next; + } + + if (path_to_open) + { + handle = hid_open_path(path_to_open); + + if (handle) + { + hid_close(handle); + CM108Device = _strdup(path_to_open); + } + else + { + Debugprintf("Unable to open CM108 device %x %x", VID, PID); + } + } + else + Debugprintf("Couldn't find CM108 device %x %x", VID, PID); + + hid_free_enumeration(devs); + } +#else + + // Linux - Next Param HID Device, eg /dev/hidraw0 + + CM108Device = _strdup(ptr); +#endif +} + +char * strlop(char * buf, char delim) +{ + // Terminate buf at delim, and return rest of string + + char * ptr = strchr(buf, delim); + + if (ptr == NULL) return NULL; + + *(ptr)++ = 0; + return ptr; +} + +void OpenPTTPort() +{ + PTTMode &= ~PTTCM108; + PTTMode &= ~PTTHAMLIB; + + if (PTTPort[0] && strcmp(PTTPort, "None") != 0) + { + if (PTTMode == PTTCAT) + { + // convert config strings from Hex + + char * ptr1 = PTTOffString; + UCHAR * ptr2 = PTTOffCmd; + char c; + int val; + + while (c = *(ptr1++)) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + PTTOffCmdLen = ptr2 - PTTOffCmd; + + ptr1 = PTTOnString; + ptr2 = PTTOnCmd; + + while (c = *(ptr1++)) + { + val = c - 0x30; + if (val > 15) val -= 7; + val <<= 4; + c = *(ptr1++) - 0x30; + if (c > 15) c -= 7; + val |= c; + *(ptr2++) = val; + } + + PTTOnCmdLen = ptr2 - PTTOnCmd; + } + + if (stricmp(PTTPort, "GPIO") == 0) + { + // Initialise GPIO for PTT if available + +#ifdef __ARM_ARCH + + if (gpioInitialise() == 0) + { + printf("GPIO interface for PTT available\n"); + gotGPIO = TRUE; + + SetupGPIOPTT(); + } + else + printf("Couldn't initialise GPIO interface for PTT\n"); + +#else + printf("GPIO interface for PTT not available on this platform\n"); +#endif + + } + else if (stricmp(PTTPort, "CM108") == 0) + { + DecodeCM108(CM108Addr); + PTTMode |= PTTCM108; + } + + else if (stricmp(PTTPort, "HAMLIB") == 0) + { + PTTMode |= PTTHAMLIB; + HAMLIBSetPTT(0); // to open port + return; + } + + else // Not GPIO + { + hPTTDevice = OpenCOMPort(PTTPort, PTTBAUD, FALSE, FALSE, FALSE, 0); + } + } +} + +void ClosePTTPort() +{ + CloseCOMPort(hPTTDevice); + hPTTDevice = 0; +} +void CM108_set_ptt(int PTTState) +{ + char io[5]; + hid_device *handle; + int n; + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + if (CM108Device == NULL) + return; + +#ifdef WIN32 + handle = hid_open_path(CM108Device); + + if (!handle) { + printf("unable to open device\n"); + return; + } + + n = hid_write(handle, io, 5); + if (n < 0) + { + printf("Unable to write()\n"); + printf("Error: %ls\n", hid_error(handle)); + } + + hid_close(handle); + +#else + + int fd; + + fd = open(CM108Device, O_WRONLY); + + if (fd == -1) + { + printf("Could not open %s for write, errno=%d\n", CM108Device, errno); + return; + } + + io[0] = 0; + io[1] = 0; + io[2] = 1 << (3 - 1); + io[3] = PTTState << (3 - 1); + io[4] = 0; + + n = write(fd, io, 5); + if (n != 5) + { + printf("Write to %s failed, n=%d, errno=%d\n", CM108Device, n, errno); + } + + close(fd); +#endif + return; + +} + + + +void RadioPTT(int snd_ch, BOOL PTTState) +{ +#ifdef __ARM_ARCH + if (useGPIO) + { + if (DualPTT && modemtoSoundLR[snd_ch] == 1) + gpioWrite(pttGPIOPinR, (pttGPIOInvert ? (1 - PTTState) : (PTTState))); + else + gpioWrite(pttGPIOPin, (pttGPIOInvert ? (1 - PTTState) : (PTTState))); + + return; + } + +#endif + + if ((PTTMode & PTTCM108)) + { + CM108_set_ptt(PTTState); + return; + } + + if ((PTTMode & PTTHAMLIB)) + { + HAMLIBSetPTT(PTTState); + return; + } + if (hPTTDevice == 0) + return; + + if ((PTTMode & PTTCAT)) + { + if (PTTState) + WriteCOMBlock(hPTTDevice, PTTOnCmd, PTTOnCmdLen); + else + WriteCOMBlock(hPTTDevice, PTTOffCmd, PTTOffCmdLen); + + return; + } + + if (DualPTT && modemtoSoundLR[snd_ch] == 1) // use DTR + { + if (PTTState) + COMSetDTR(hPTTDevice); + else + COMClearDTR(hPTTDevice); + } + else + { + if ((PTTMode & PTTRTS)) + { + if (PTTState) + COMSetRTS(hPTTDevice); + else + COMClearRTS(hPTTDevice); + } + } + +} + +char ShortDT[] = "HH:MM:SS"; + +char * ShortDateTime() +{ + struct tm * tm; + time_t NOW = time(NULL); + + tm = gmtime(&NOW); + + sprintf(ShortDT, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); + return ShortDT; +} + + +// Reed Solomon Stuff + + +int NPAR = -1; // Number of Parity Bytes - used in RS Code + +int xMaxErrors = 0; + +int RSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen) +{ + // This just returns the Parity Bytes. I don't see the point + // in copying the message about + + unsigned char Padded[256]; // The padded Data + + int Length = DataLen + RSLen; // Final Length of packet + int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes + + // subroutine to do the RS encode. For full length and shortend RS codes up to 8 bit symbols (mm = 8) + + if (NPAR != RSLen) // Changed RS Len, so recalc constants; + { + NPAR = RSLen; + xMaxErrors = NPAR / 2; + initialize_ecc(); + } + + // Copy the supplied data to end of data array. + + memset(Padded, 0, PadLength); + memcpy(&Padded[PadLength], bytToRS, DataLen); + + encode_data(Padded, 255 - RSLen, RSBytes); + + return RSLen; +} + +// Main RS decode function + +extern int index_of[]; +extern int recd[]; +extern int Corrected[256]; +extern int tt; // number of errors that can be corrected +extern int kk; // Info Symbols + +extern BOOL blnErrorsCorrected; + + +BOOL RSDecode(UCHAR * bytRcv, int Length, int CheckLen, BOOL * blnRSOK) +{ + + + // Using a modified version of Henry Minsky's code + + //Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009 + + // Rick's Implementation processes the byte array in reverse. and also + // has the check bytes in the opposite order. I've modified the encoder + // to allow for this, but so far haven't found a way to mske the decoder + // work, so I have to reverse the data and checksum to decode G8BPQ Nov 2015 + + // returns TRUE if was ok or correction succeeded, FALSE if correction impossible + + UCHAR intTemp[256]; // WOrk Area to pass to Decoder + int i; + UCHAR * ptr2 = intTemp; + UCHAR * ptr1 = &bytRcv[Length - CheckLen - 1]; // Last Byte of Data + + int DataLen = Length - CheckLen; + int PadLength = 255 - Length; // Padding bytes needed for shortened RS codes + + *blnRSOK = FALSE; + + if (Length > 255 || Length < (1 + CheckLen)) //Too long or too short + return FALSE; + + if (NPAR != CheckLen) // Changed RS Len, so recalc constants; + { + NPAR = CheckLen; + xMaxErrors = NPAR / 2; + + initialize_ecc(); + } + + + // We reverse the data while zero padding it to speed things up + + // We Need (Data Reversed) (Zero Padding) (Checkbytes Reversed) + + // Reverse Data + + for (i = 0; i < DataLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + // Clear padding + + memset(ptr2, 0, PadLength); + + ptr2 += PadLength; + + // Error Bits + + ptr1 = &bytRcv[Length - 1]; // End of check bytes + + for (i = 0; i < CheckLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + decode_data(intTemp, 255); + + // check if syndrome is all zeros + + if (check_syndrome() == 0) + { + // RS ok, so no need to correct + + *blnRSOK = TRUE; + return TRUE; // No Need to Correct + } + + if (correct_errors_erasures(intTemp, 255, 0, 0) == 0) // Dont support erasures at the momnet + + // Uncorrectable + + return FALSE; + + // Data has been corrected, so need to reverse again + + ptr1 = &intTemp[DataLen - 1]; + ptr2 = bytRcv; // Last Byte of Data + + for (i = 0; i < DataLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + // ?? Do we need to return the check bytes ?? + + // Yes, so we can redo RS Check on supposedly connected frame + + ptr1 = &intTemp[254]; // End of Check Bytes + + for (i = 0; i < CheckLen; i++) + { + *(ptr2++) = *(ptr1--); + } + + return TRUE; +} + +extern TStringList detect_list[5]; +extern TStringList detect_list_c[5]; + +void ProcessPktFrame(int snd_ch, UCHAR * Data, int frameLen) +{ + string * pkt = newString(); + + stringAdd(pkt, Data, frameLen + 2); // 2 for crc (not actually there) + + analiz_frame(snd_ch, pkt, "ARDOP", 1); + +} diff --git a/ShowFilter.cpp b/ShowFilter.cpp new file mode 100644 index 0000000..a849760 --- /dev/null +++ b/ShowFilter.cpp @@ -0,0 +1,234 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" +#include + +// This displays a graph of the filter characteristics + +#define c3 -1.5000000000000E+00f // cos(2*pi / 3) - 1; +#define c32 8.6602540378444E-01f // sin(2*pi / 3); + +#define u5 1.2566370614359E+00f // 2*pi / 5; +#define c51 -1.2500000000000E+00f // (cos(u5) + cos(2*u5))/2 - 1; +#define c52 5.5901699437495E-01f // (cos(u5) - cos(2*u5))/2; +#define c53 -9.5105651629515E-0f //- sin(u5); +#define c54 -1.5388417685876E+00f //-(sin(u5) + sin(2*u5)); +#define c55 3.6327126400268E-01f // (sin(u5) - sin(2*u5)); +#define c8 = 7.0710678118655E-01f // 1 / sqrt(2); + + +float pnt_graph_buf[4096]; +float graph_buf[4096]; +float prev_graph_buf[4096]; +float src_graph_buf[4096]; +float graph_f; +float RealOut[4096]; +short RealIn[4096]; +float ImagOut[4096]; + +#define Image1Width 642 +#define Image1Height 312 + +void filter_grid(QPainter * Painter) +{ + int col = 20; + int row = 8; + int top_margin = 10; + int bottom_margin = 20; + int left_margin = 30; + int right_margin = 10; + + int x, y; + float kx, ky; + + QPen pen; // creates a default pen + + pen.setStyle(Qt::DotLine); + Painter->setPen(pen); + + + ky = 35; + + kx = (Image1Width - left_margin - right_margin - 2) / col; + + for (y = 0; y < row; y++) + { + Painter->drawLine( + left_margin + 1, + top_margin + round(ky*y) + 1, + Image1Width - right_margin - 1, + top_margin + round(ky*y) + 1); + } + + for (x = 0; x < col; x++) + { + Painter->drawLine( + left_margin + round(kx*x) + 1, + top_margin + 1, + left_margin + round(kx*x) + 1, + Image1Height - bottom_margin - 1); + } + + pen.setStyle(Qt::SolidLine); + Painter->setPen(pen); + + for (y = 0; y < row / 2; y++) + { + char Textxx[20]; + + sprintf(Textxx, "%d", y * -20); + + Painter->drawLine( + left_margin + 1, + top_margin + round(ky*y * 2) + 1, + Image1Width - right_margin - 1, + top_margin + round(ky*y * 2) + 1); + + Painter->drawText( + 1, + top_margin + round(ky*y * 2) + 1, + 100, 20, 0, Textxx); + + } + + + for (x = 0; x <= col / 5; x++) + { + char Textxx[20]; + + sprintf(Textxx, "%d", x * 1000); + + Painter->drawLine( + left_margin + round(kx*x * 5) + 1, + top_margin + 1, + left_margin + round(kx*x * 5) + 1, + Image1Height - bottom_margin - 1); + + Painter->drawText( + top_margin + round(kx*x * 5) + 8, + Image1Height - 15, + 100, 20, 0, Textxx); + } +} + +extern "C" void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); + + +void make_graph(float * buf, int buflen, QPainter * Painter) +{ + int top_margin = 10; + int bottom_margin = 20; + int left_margin = 30; + + int i, y1, y2; + float pixel; + + if (buflen == 0) + return; + + for (i = 0; i <= buflen - 2; i++) + { + y1 = 1 - round(buf[i]); + + if (y1 > Image1Height - top_margin - bottom_margin - 2) + y1 = Image1Height - top_margin - bottom_margin - 2; + + y2 = 1 - round(buf[i + 1]); + + if (y2 > Image1Height - top_margin - bottom_margin - 2) + y2 = Image1Height - top_margin - bottom_margin - 2; + + // 150 pixels for 1000 Hz + + // i is the bin number, but bin is not 10 Hz but 12000 /1024 + // so freq = i * 12000 / 1024; + // and pixel is freq * 300 /1000 + + pixel = i * 12000.0f / 1024.0f; + pixel = pixel * 150.0f /1000.0f; + + Painter->drawLine( + left_margin + pixel, + top_margin + y1, + left_margin + pixel + 1, + top_margin + y2); + } +} + +void make_graph_buf(float * buf, short tap, QPainter * Painter) +{ + int fft_size; + float max; + int i, k; + + fft_size = 1024; // 12000 / 10; // 10hz on sample; + + for (i = 0; i < tap; i++) + prev_graph_buf[i]= 0; + + for (i = 0; i < fft_size; i++) + src_graph_buf[i] = 0; + + src_graph_buf[0]= 1; + + FIR_filter(src_graph_buf, fft_size, tap, buf, graph_buf, prev_graph_buf); + + + for (k = 0; k < fft_size; k++) + RealIn[k] = graph_buf[k] * 32768; + + FourierTransform(fft_size, RealIn, RealOut, ImagOut, 0); + + for (k = 0; k < (fft_size / 2) - 1; k++) + pnt_graph_buf[k] = powf(RealOut[k], 2) + powf(ImagOut[k], 2); + + max = 0; + + for (i = 0; i < (fft_size / 2) - 1; i++) + { + if (pnt_graph_buf[i] > max) + max = pnt_graph_buf[i]; + } + + if (max > 0) + { + for (i = 0; i < (fft_size / 2) - 1; i++) + pnt_graph_buf[i] = pnt_graph_buf[i] / max; + } + + for (i = 0; i < (fft_size / 2) - 1; i++) + { + if (pnt_graph_buf[i] > 0) + pnt_graph_buf[i] = 70 * log10(pnt_graph_buf[i]); + + else + + pnt_graph_buf[i] = 0; + } + + filter_grid(Painter); + + Painter->setPen(Qt::blue); + + make_graph(pnt_graph_buf, 400, Painter); +} diff --git a/ShowFilter.cpp.bak b/ShowFilter.cpp.bak new file mode 100644 index 0000000..cc18ea0 --- /dev/null +++ b/ShowFilter.cpp.bak @@ -0,0 +1,234 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" +#include + +// This displays a graph of the filter characteristics + +#define c3 -1.5000000000000E+00f // cos(2*pi / 3) - 1; +#define c32 8.6602540378444E-01f // sin(2*pi / 3); + +#define u5 1.2566370614359E+00f // 2*pi / 5; +#define c51 -1.2500000000000E+00f // (cos(u5) + cos(2*u5))/2 - 1; +#define c52 5.5901699437495E-01f // (cos(u5) - cos(2*u5))/2; +#define c53 -9.5105651629515E-0f //- sin(u5); +#define c54 -1.5388417685876E+00f //-(sin(u5) + sin(2*u5)); +#define c55 3.6327126400268E-01f // (sin(u5) - sin(2*u5)); +#define c8 = 7.0710678118655E-01f // 1 / sqrt(2); + + +float pnt_graph_buf[4096]; +float graph_buf[4096]; +float prev_graph_buf[4096]; +float src_graph_buf[4096]; +float graph_f; +float RealOut[4096]; +short RealIn[4096]; +float ImagOut[4096]; + +#define Image1Width 642 +#define Image1Height 312 + +void filter_grid(QPainter * Painter) +{ + int col = 20; + int row = 8; + int top_margin = 10; + int bottom_margin = 20; + int left_margin = 30; + int right_margin = 10; + + int x, y; + float kx, ky; + + QPen pen; // creates a default pen + + pen.setStyle(Qt::DotLine); + Painter->setPen(pen); + + + ky = 35; + + kx = (Image1Width - left_margin - right_margin - 2) / col; + + for (y = 0; y < row; y++) + { + Painter->drawLine( + left_margin + 1, + top_margin + round(ky*y) + 1, + Image1Width - right_margin - 1, + top_margin + round(ky*y) + 1); + } + + for (x = 0; x < col; x++) + { + Painter->drawLine( + left_margin + round(kx*x) + 1, + top_margin + 1, + left_margin + round(kx*x) + 1, + Image1Height - bottom_margin - 1); + } + + pen.setStyle(Qt::SolidLine); + Painter->setPen(pen); + + for (y = 0; y < row / 2; y++) + { + char Textxx[20]; + + sprintf(Textxx, "%d", y * -20); + + Painter->drawLine( + left_margin + 1, + top_margin + round(ky*y * 2) + 1, + Image1Width - right_margin - 1, + top_margin + round(ky*y * 2) + 1); + + Painter->drawText( + 1, + top_margin + round(ky*y * 2) + 1, + 100, 20, 0, Textxx); + + } + + + for (x = 0; x <= col / 5; x++) + { + char Textxx[20]; + + sprintf(Textxx, "%d", x * 1000); + + Painter->drawLine( + left_margin + round(kx*x * 5) + 1, + top_margin + 1, + left_margin + round(kx*x * 5) + 1, + Image1Height - bottom_margin - 1); + + Painter->drawText( + top_margin + round(kx*x * 5) + 8, + Image1Height - 15, + 100, 20, 0, Textxx); + } +} + +extern "C" void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); + + +void make_graph(float * buf, int buflen, QPainter * Painter) +{ + int top_margin = 10; + int bottom_margin = 20; + int left_margin = 30; + + int i, y1, y2; + float pixel; + + if (buflen == 0) + return; + + for (i = 0; i <= buflen - 2; i++) + { + y1 = 1 - round(buf[i]); + + if (y1 > Image1Height - top_margin - bottom_margin - 2) + y1 = Image1Height - top_margin - bottom_margin - 2; + + y2 = 1 - round(buf[i + 1]); + + if (y2 > Image1Height - top_margin - bottom_margin - 2) + y2 = Image1Height - top_margin - bottom_margin - 2; + + // 150 pixels for 1000 Hz + + // i is the bin number, but bin is not 10 Hz but 12000 /1024 + // so freq = i * 12000 / 1024; + // and pixel is freq * 300 /1000 + + pixel = i * 12000.0f / 1024.0f; + pixel = pixel * 150.0f /1000.0f; + + Painter->drawLine( + left_margin + pixel, + top_margin + y1, + left_margin + pixel + 1, + top_margin + y2); + } +} + +void make_graph_buf(float * buf, short tap, QPainter * Painter) +{ + int FFTSize; + float max; + int i, k; + + FFTSize = 1024; // 12000 / 10; // 10hz on sample; + + for (i = 0; i < tap; i++) + prev_graph_buf[i]= 0; + + for (i = 0; i < FFTSize; i++) + src_graph_buf[i] = 0; + + src_graph_buf[0]= 1; + + FIR_filter(src_graph_buf, FFTSize, tap, buf, graph_buf, prev_graph_buf); + + + for (k = 0; k < FFTSize; k++) + RealIn[k] = graph_buf[k] * 32768; + + FourierTransform(FFTSize, RealIn, RealOut, ImagOut, 0); + + for (k = 0; k < (FFTSize / 2) - 1; k++) + pnt_graph_buf[k] = powf(RealOut[k], 2) + powf(ImagOut[k], 2); + + max = 0; + + for (i = 0; i < (FFTSize / 2) - 1; i++) + { + if (pnt_graph_buf[i] > max) + max = pnt_graph_buf[i]; + } + + if (max > 0) + { + for (i = 0; i < (FFTSize / 2) - 1; i++) + pnt_graph_buf[i] = pnt_graph_buf[i] / max; + } + + for (i = 0; i < (FFTSize / 2) - 1; i++) + { + if (pnt_graph_buf[i] > 0) + pnt_graph_buf[i] = 70 * log10(pnt_graph_buf[i]); + + else + + pnt_graph_buf[i] = 0; + } + + filter_grid(Painter); + + Painter->setPen(Qt::blue); + + make_graph(pnt_graph_buf, 400, Painter); +} diff --git a/SoundInput.c b/SoundInput.c new file mode 100644 index 0000000..5d7e7bc --- /dev/null +++ b/SoundInput.c @@ -0,0 +1,5253 @@ +// ARDOP Modem Decode Sound Samples + +#include +#include "ARDOPC.h" + +#pragma warning(disable : 4244) // Code does lots of float to int + +#ifndef TEENSY +#define MEMORYARQ +#endif + +#undef PLOTWATERFALL + +#ifdef PLOTWATERFALL +#define WHITE 0xffff +#define Tomato 0xffff +#define Orange 0xffff +#define Khaki 0xffff +#define Cyan 0xffff +#define DeepSkyBlue 0 +#define RoyalBlue 0 +#define Navy 0 +#define Black 0 +#endif + +#ifdef TEENSY +#define PKTLED LED3 // flash when packet received +extern unsigned int PKTLEDTimer; +#endif + +//#define max(x, y) ((x) > (y) ? (x) : (y)) +//#define min(x, y) ((x) < (y) ? (x) : (y)) + +void SendFrametoHost(unsigned char *data, unsigned dlen); + +void CheckandAdjustRXLevel(int maxlevel, int minlevel, BOOL Force); +void mySetPixel(unsigned char x, unsigned char y, unsigned int Colour); +void clearDisplay(); +void updateDisplay(); +VOID L2Routine(UCHAR * Packet, int Length, int FrameQuality, int totalRSErrors, int NumCar, int pktRXMode); +void RemoveProcessedOFDMData(); +BOOL CheckCRC16(unsigned char * Data, int Length); + +void DrawAxes(int Qual, const char * Frametype, char * Mode); + +extern int lastmax, lastmin; // Sample Levels + +char strRcvFrameTag[32]; + +BOOL blnLeaderFound = FALSE; + +int intLeaderRcvdMs = 1000; // Leader length?? + +extern int intLastRcvdFrameQuality; +extern int intReceivedLeaderLen; +extern UCHAR bytLastReceivedDataFrameType; +extern int NErrors; +extern BOOL blnBREAKCmd; +extern UCHAR bytLastACKedDataFrameType; +extern int intARQDefaultDlyMs; +unsigned int tmrFinalID; +extern BOOL PKTCONNECTED; +extern int LastDemodType; + +extern int pktRXMode; +extern int RXOFDMMode; + +extern BOOL blnBusyStatus; +BOOL blnLastBusyStatus; +int BusyCount; + +short intPriorMixedSamples[120]; // a buffer of 120 samples to hold the prior samples used in the filter +int intPriorMixedSamplesLength = 120; // size of Prior sample buffer + +// While searching for leader we must save unprocessed samples +// We may have up to 720 left, so need 1920 + +short rawSamples[2400]; // Get Frame Type need 2400 and we may add 1200 +extern int rawSamplesLength; +int maxrawSamplesLength; + +short intFilteredMixedSamples[3500]; // Get Frame Type need 2400 and we may add 1200 +int intFilteredMixedSamplesLength = 0; +int MaxFilteredMixedSamplesLength = 0; + +int intFrameType= 0; // Type we are decoding +int LastDataFrameType = 0; // Last data frame processed (for Memory ARQ, etc) + +char strDecodeCapture[256]; + +// Frame type parameters + +int intCenterFreq = 1500; +float floatCarFreq; //(was int) // Are these the same ?? +int intNumCar; +int intSampPerSym; +int intBaud; +int intDataLen; +int intRSLen; +int intSampleLen; +int DataRate = 0; // For SCS Reporting +int intDataPtr; +int intDataBytesPerCar; +BOOL blnOdd; +char strType[18] = ""; +char strMod[16] = ""; +UCHAR bytMinQualThresh; +int intPSKMode; +int intSymbolsPerByte = 4; + +// ARDOP V2 has max 10 carriers and 160 (120 + 40RS) per carrier + +#define MAX_RAW_LENGTH 163 // Len Byte + Data + RS + CRC I think! +#define MAX_RAW_LENGTH_FSK 43 // MAX FSK 32 data + 8 RS +// OFDM is MAXCAR * 100 +// 10 carrier 16QAM id 10 * 160 + +#define MAX_DATA_LENGTH MAXCAR * 100 // I think! (OFDM 16QAM) + +// intToneMags should be an array with one row per carrier. +// and 16 * max bytes data (2 bits per symbol, 4 samples per symbol in 4FSK. + +// but as 600 Baud frames are very long (750 bytes), but only one carrier +// may be better to store as scalar and calculate offsets into it for each carrier +// treat 600 as 3 * 200, but scalar still may be better + +// Needs 64K if ints + another 64 for MEM ARQ. (maybe able to store as shorts) +// 48K would do if we use a scalar (600 baud, 750 bytes) +// Max is 4 carrier, 83 bytes or 1 carrier 762 (or treat as 3 * 253) + +// Could just about do this on Teensy 3.6 or Nucleo F7 + +// looks like we have 4 samples for each 2 bits, which means 16 samples per byte. + +// ARDOP 2 only has one and two carrier FSK modes + +// Teensy is rather short of RAM, but as we never receive FSK and PSK +// at the same time we can use same data area (saves about 20K) + +int intToneMagsIndex[2]; + +// Same here + +int intSumCounts[MAXCAR]; // number in above arrays + +int intToneMagsLength; + +unsigned char goodCarriers = 0; // Carriers we have already decoded + +// We always collect all phases for PSK and QAM so we can do phase correction + +// Max PSK frame is 83, 4 samples per byte = 332 +// Max 16QAM frame is 163, 2 samples per byte = 326 + +// OFDM frames are shorter, so carriers 11 - 17 could have smaller sample buffers + +// This is a bit complicated, but allows smaller buffers for the OFDM carriers (Needed for Teensy) + +//short intPhases[MAXCAR][332] = {0}; + +short QAMPhases[10][332]; // 6640 bytes +short OFDMPhases[MAXCAR - 10][232]; // Need 232 = (PSK2 8 * 29); 15312 + +short * Phaseptrs[MAXCAR] = + {&QAMPhases[0][0], &QAMPhases[1][0], &QAMPhases[2][0], &QAMPhases[3][0], &QAMPhases[4][0], + &QAMPhases[5][0], &QAMPhases[6][0], &QAMPhases[7][0], &QAMPhases[8][0], &QAMPhases[9][0], + &OFDMPhases[0][0], &OFDMPhases[1][0], &OFDMPhases[2][0], &OFDMPhases[3][0], &OFDMPhases[4][0], + &OFDMPhases[5][0], &OFDMPhases[6][0], &OFDMPhases[7][0], &OFDMPhases[8][0], &OFDMPhases[9][0], + &OFDMPhases[10][0], &OFDMPhases[11][0], &OFDMPhases[12][0], &OFDMPhases[13][0], &OFDMPhases[14][0], + &OFDMPhases[15][0], &OFDMPhases[16][0], &OFDMPhases[17][0], &OFDMPhases[18][0], &OFDMPhases[19][0], + &OFDMPhases[20][0], &OFDMPhases[21][0], &OFDMPhases[22][0], &OFDMPhases[23][0], &OFDMPhases[24][0], + &OFDMPhases[25][0], &OFDMPhases[26][0], &OFDMPhases[27][0], &OFDMPhases[28][0], &OFDMPhases[29][0], + &OFDMPhases[30][0], &OFDMPhases[31][0], &OFDMPhases[32][0]}; + +short ** intPhases = &Phaseptrs[0]; + +short QAMMags[10][332]; +short OFDMMags[MAXCAR - 10][232]; + +short * Magptrs[MAXCAR] = + {&QAMMags[0][0], &QAMMags[1][0], &QAMMags[2][0], &QAMMags[3][0], &QAMMags[4][0], + &QAMMags[5][0], &QAMMags[6][0], &QAMMags[7][0], &QAMMags[8][0], &QAMMags[9][0], + &OFDMMags[0][0], &OFDMMags[1][0], &OFDMMags[2][0], &OFDMMags[3][0], &OFDMMags[4][0], + &OFDMMags[5][0], &OFDMMags[6][0], &OFDMMags[7][0], &OFDMMags[8][0], &OFDMMags[9][0], + &OFDMMags[10][0], &OFDMMags[11][0], &OFDMMags[12][0], &OFDMMags[13][0], &OFDMMags[14][0], + &OFDMMags[15][0], &OFDMMags[16][0], &OFDMMags[17][0], &OFDMMags[18][0], &OFDMMags[19][0], + &OFDMMags[20][0], &OFDMMags[21][0], &OFDMMags[22][0], &OFDMMags[23][0], &OFDMMags[24][0], + &OFDMMags[25][0], &OFDMMags[26][0], &OFDMMags[27][0], &OFDMMags[28][0], &OFDMMags[29][0], + &OFDMMags[30][0], &OFDMMags[31][0], &OFDMMags[32][0]}; + +short ** intMags = &Magptrs[0]; + + + +//int Tones[2][16 * MAX_RAW_LENGTH_FSK]; + +int intToneMags[4][16 * MAX_RAW_LENGTH_FSK] = {0}; // Need one per carrier + +// We need 5504 bytes for FSK but can overlay on PSK data areas + +//int * Toneptrs[2] = {(int *)&Tones[0][0], (int *)&Tones[1][0]}; + +//int ** intToneMags = &Toneptrs[0]; + + +#ifdef MEMORYARQ + +// Enough RAM for memory ARQ so keep all samples for FSK and a copy of tones or phase/amplitude + +int intToneMagsAvg[2][332]; //???? FSK Tone averages + +short intCarPhaseAvg[MAXCAR][332]; // array to accumulate phases for averaging (Memory ARQ) +short intCarMagAvg[MAXCAR][332]; // array to accumulate mags for averaging (Memory ARQ) + +#endif + + + + +//219 /3 * 8= 73 * 8 = 584 +//163 * 4 = 652 + +// If we do Mem ARQ we will need a fair amount of RAM + +int intPhasesLen; + +// Received Frame + +UCHAR bytData[128 * 80]; // Max OFDM Window +int frameLen; + +int totalRSErrors; + +// for comparing with CarrierOK +const char Good[MAXCAR] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; // All Good +const char Bad[MAXCAR] = {0}; // All bad + +// We need one raw buffer per carrier + +// This can be optimized quite a bit to save space +// We can probably overlay on bytData + +// If we still have 600 baud modes may need a lot more for first + +// Note OFDM doesn't need one per carrier so only need 10 + +UCHAR bytFrameData[10][MAX_RAW_LENGTH + 10]; // Received chars + +char CarrierOk[MAXCAR]; // RS OK Flags per carrier +int RepeatedFrame = 0; // set if this dats frame is a repeat + +int charIndex = 0; // Index into received chars + +int SymbolsLeft; // number still to decode + +int DummyCarrier = 0; // pseudo carrier used for long 600 baud frames +UCHAR * Decode600Buffer; + +BOOL PSKInitDone = FALSE; + +BOOL blnSymbolSyncFound, blnFrameSyncFound; + +extern UCHAR bytLastARQSessionID; +extern UCHAR bytCurrentFrameType; +extern int intShiftUpDn; +extern const char ARQSubStates[10][11]; +extern int intLastARQDataFrameToHost; + +// dont think I need it short intRcvdSamples[12000]; // 1 second. May need to optimise + +float dblOffsetLastGoodDecode = 0; +int dttLastGoodFrameTypeDecode = -20000; + +float dblOffsetHz = 0;; +int dttLastLeaderDetect; + +extern int intRmtLeaderMeasure; + +extern BOOL blnARQConnected; + + +extern BOOL blnPending; +extern UCHAR bytPendingSessionID; +extern UCHAR bytSessionID; + +int dttLastGoodFrameTypeDecod; +int dttStartRmtLeaderMeasure; + +char lastGoodID[11] = ""; + +int GotBitSyncTicks; + +int intARQRTmeasuredMs; + +float dbl2Pi = 2 * M_PI; + +float dblSNdBPwr; +float dblNCOFreq = 3000; // nominal NC) frequency +float dblNCOPhase = 0; +float dblNCOPhaseInc = 2 * M_PI * 3000 / 12000; // was dblNCOFreq +float dblPwrSNPower_dBPrior = 0; +float dblPhaseDiff1_2Avg; // an initial value of -10 causes initialization in AcquireFrameSyncRSBAvg + + +int intMFSReadPtr = 0; // reset the MFSReadPtr offset 30 to accomodate the filter delay + +int RcvdSamplesLen = 0; // Samples in RX buffer + +float dblPhaseDiff1_2Avg; +int intPhaseError = 0; + + +BOOL Acquire2ToneLeaderSymbolFraming(); +BOOL SearchFor2ToneLeader4(short * intNewSamples, int Length, float * dblOffsetHz, int * intSN); +BOOL AcquireFrameSyncRSB(); +BOOL AcquireFrameSyncRSBAvg(); +int Acquire4FSKFrameType(); + +void DemodulateFrame(int intFrameType); +void Demod1Car4FSKChar(int Start, UCHAR * Decoded, int Carrier); +VOID Track1Car4FSK(short * intSamples, int * intPtr, int intSampPerSymbol, float intSearchFreq, int intBaud, UCHAR * bytSymHistory); +VOID Decode1CarPSK(int Carrier, BOOL OFDM); +int EnvelopeCorrelator(); +int EnvelopeCorrelatorNew(); +BOOL DecodeFrame(int intFrameType, UCHAR * bytData); + +void Update4FSKConstellation(int * intToneMags, int * intQuality); +void Update16FSKConstellation(int * intToneMags, int * intQuality); +void Update8FSKConstellation(int * intToneMags, int * intQuality); +void ProcessPingFrame(char * bytData); +int Compute4FSKSN(); + +void DemodPSK(); +BOOL DemodQAM(); +BOOL DemodOFDM(); +BOOL Decode4FSKOFDMACK(); + + +void PrintCarrierFlags() +{ + char Msg[128]; + + if (intNumCar == 1) + Debugprintf("MEMARQ Flags %d", CarrierOk[0]); + else if (intNumCar == 2) + Debugprintf("MEMARQ Flags %d %d", CarrierOk[0], CarrierOk[1]); + + else + { + sprintf(Msg, "MEMARQ Flags %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", + CarrierOk[0], CarrierOk[1], CarrierOk[2], CarrierOk[3], CarrierOk[4], CarrierOk[5], CarrierOk[6], CarrierOk[7], CarrierOk[8], CarrierOk[9], + CarrierOk[10], CarrierOk[11], CarrierOk[12], CarrierOk[13], CarrierOk[14], CarrierOk[15], CarrierOk[16], CarrierOk[17], CarrierOk[18], CarrierOk[19], + CarrierOk[20], CarrierOk[21], CarrierOk[22], CarrierOk[23], CarrierOk[24], CarrierOk[25], CarrierOk[26], CarrierOk[27], CarrierOk[28], CarrierOk[29], + CarrierOk[30], CarrierOk[31], CarrierOk[32], CarrierOk[33], CarrierOk[34], CarrierOk[35], CarrierOk[36], CarrierOk[37], CarrierOk[38], CarrierOk[39], + CarrierOk[40], CarrierOk[41], CarrierOk[42]); + + Msg[12 + 2 * intNumCar] = 0; + Debugprintf(Msg); + } + +} + + +// Function to determine if frame type is short control frame + +BOOL IsShortControlFrame(UCHAR bytType) +{ + switch (intFrameType) + { + case DataNAK: + case DataNAKLoQ: + case ConRejBusy: + case ConRejBW: + case ConAck: + case DISCFRAME: + case BREAK: + case END: + case IDLEFRAME: + case DataACK: + case DataACKHiQ: + + return TRUE; + } + + return FALSE; +} + +BOOL IsConReqFrame(UCHAR bytType) +{ + switch (bytType) + { + case ConReq200: + case ConReq500: + case ConReq2500: + case OConReq500: + case OConReq2500: + + return TRUE; + } + return FALSE; +} + + + +// Function to determine if it is a data frame (Even OR Odd) + +BOOL IsDataFrame(UCHAR intFrameType) +{ + const char * String = Name(intFrameType); + + if (intFrameType == PktFrameHeader) + return TRUE; + + if (String == NULL || String[0] == 0) + return FALSE; + + if (strstr(String, ".E") || strstr(String, ".O")) + return TRUE; + + return FALSE; +} + +// Subroutine to clear all mixed samples + +void ClearAllMixedSamples() +{ + intFilteredMixedSamplesLength = 0; + intMFSReadPtr = 0; + rawSamplesLength = 0; // Clear saved +} + +// Subroutine to Initialize mixed samples + +void InitializeMixedSamples() +{ + // Measure the time from release of PTT to leader detection of reply. + + intARQRTmeasuredMs = min(10000, Now - dttStartRTMeasure); //?????? needs work + intPriorMixedSamplesLength = 120; // zero out prior samples in Prior sample buffer + intFilteredMixedSamplesLength = 0; // zero out the FilteredMixedSamples array + intMFSReadPtr = 0; // reset the MFSReadPtr offset 30 to accomodate the filter delay +} + +// Subroutine to discard all sampled prior to current intRcvdSamplesRPtr + +void DiscardOldSamples() +{ + // This restructures the intRcvdSamples array discarding all samples prior to intRcvdSamplesRPtr + + //not sure why we need this !! +/* + if (RcvdSamplesLen - intRcvdSamplesRPtr <= 0) + RcvdSamplesLen = intRcvdSamplesRPtr = 0; + else + { + // This is rather slow. I'd prefer a cyclic buffer. Lets see.... + + memmove(intRcvdSamples, &intRcvdSamples[intRcvdSamplesRPtr], (RcvdSamplesLen - intRcvdSamplesRPtr)* 2); + RcvdSamplesLen -= intRcvdSamplesRPtr; + intRcvdSamplesRPtr = 0; + } +*/ +} + +// Subroutine to apply 2000 Hz filter to mixed samples + +float xdblZin_1 = 0, xdblZin_2 = 0, xdblZComb= 0; // Used in the comb generator + + // The resonators + +float xdblZout_0[29] = {0.0f}; // resonator outputs +float xdblZout_1[29] = {0.0f}; // resonator outputs delayed one sample +float xdblZout_2[29] = {0.0f}; // resonator outputs delayed two samples +float xdblCoef[29] = {0.0}; // the coefficients +float xdblR = 0.9995f; // insures stability (must be < 1.0) (Value .9995 7/8/2013 gives good results) +int xintN = 120; //Length of filter 12000/100 + + +void FSMixFilter2500Hz(short * intMixedSamples, int intMixedSamplesLength) +{ + // assumes sample rate of 12000 + // implements 27 100 Hz wide sections (~2500 Hz wide @ - 30dB centered on 1500 Hz) + + // FSF (Frequency Selective Filter) variables + + // This works on intMixedSamples, len intMixedSamplesLength; + + // Filtered data is appended to intFilteredMixedSamples + + float dblRn; + float dblR2; + + float dblZin = 0; + + int i, j; + + float intFilteredSample = 0; // Filtered sample + + if (intFilteredMixedSamplesLength < 0) + Debugprintf("Corrupt intFilteredMixedSamplesLength"); + + dblRn = powf(xdblR, xintN); + + dblR2 = powf(xdblR, 2); + + // Initialize the coefficients + + if (xdblCoef[28] == 0) + { + for (i = 2; i <= 28; i++) + { + xdblCoef[i] = 2 * xdblR * cosf(2 * M_PI * i / xintN); // For Frequency = bin i + } + } + + for (i = 0; i < intMixedSamplesLength; i++) + { + intFilteredSample = 0; + + if (i < xintN) + dblZin = intMixedSamples[i] - dblRn * intPriorMixedSamples[i]; + else + dblZin = intMixedSamples[i] - dblRn * intMixedSamples[i - xintN]; + + //Compute the Comb + + xdblZComb = dblZin - xdblZin_2 * dblR2; + xdblZin_2 = xdblZin_1; + xdblZin_1 = dblZin; + + // Now the resonators + for (j = 2; j <= 28; j++) // calculate output for 3 resonators + { + xdblZout_0[j] = xdblZComb + xdblCoef[j] * xdblZout_1[j] - dblR2 * xdblZout_2[j]; + xdblZout_2[j] = xdblZout_1[j]; + xdblZout_1[j] = xdblZout_0[j]; + + //' scale each by transition coeff and + (Even) or - (Odd) + //' Resonators 2 and 13 scaled by .389 get best shape and side lobe supression + //' Scaling also accomodates for the filter "gain" of approx 60. + + if (j == 2 || j == 28) + intFilteredSample += 0.389f * xdblZout_0[j]; + else if ((j & 1) == 0) + intFilteredSample += xdblZout_0[j]; + else + intFilteredSample -= xdblZout_0[j]; + } + + intFilteredSample = intFilteredSample * 0.00833333333f; + intFilteredMixedSamples[intFilteredMixedSamplesLength++] = intFilteredSample; // rescales for gain of filter + } + + // update the prior intPriorMixedSamples array for the next filter call + + memmove(intPriorMixedSamples, &intMixedSamples[intMixedSamplesLength - xintN], intPriorMixedSamplesLength * 2); + + if (intFilteredMixedSamplesLength > MaxFilteredMixedSamplesLength) + MaxFilteredMixedSamplesLength = intFilteredMixedSamplesLength; + + if (intFilteredMixedSamplesLength > 3500) + Debugprintf("Corrupt intFilteredMixedSamplesLength %d", intFilteredMixedSamplesLength); + +} + +// Function to apply 150Hz filter used in Envelope correlator + +void Filter150Hz(short * intFilterOut) +{ + // assumes sample rate of 12000 + // implements 3 100 Hz wide sections (~150 Hz wide @ - 30dB centered on 1500 Hz) + + // FSF (Frequency Selective Filter) variables + + static float dblR = 0.9995f; // insures stability (must be < 1.0) (Value .9995 7/8/2013 gives good results) + static int intN = 120; //Length of filter 12000/100 + static float dblRn; + static float dblR2; + static float dblCoef[17] = {0.0}; // the coefficients + float dblZin = 0, dblZin_1 = 0, dblZin_2 = 0, dblZComb= 0; // Used in the comb generator + // The resonators + + float dblZout_0[17] = {0.0}; // resonator outputs + float dblZout_1[17] = {0.0}; // resonator outputs delayed one sample + float dblZout_2[17] = {0.0}; // resonator outputs delayed two samples + + int i, j; + + float FilterOut = 0; // Filtered sample + float largest = 0; + + dblRn = powf(dblR, intN); + + dblR2 = powf(dblR, 2); + + // Initialize the coefficients + + if (dblCoef[17] == 0) + { + for (i = 14; i <= 16; i++) + { + dblCoef[i] = 2 * dblR * cosf(2 * M_PI * i / intN); // For Frequency = bin i + } + } + + for (i = 0; i < 480; i++) + { + if (i < intN) + dblZin = intFilteredMixedSamples[intMFSReadPtr + i] - dblRn * 0; // no prior mixed samples + else + dblZin = intFilteredMixedSamples[intMFSReadPtr + i] - dblRn * intFilteredMixedSamples[intMFSReadPtr + i - intN]; + + // Compute the Comb + + dblZComb = dblZin - dblZin_2 * dblR2; + dblZin_2 = dblZin_1; + dblZin_1 = dblZin; + + // Now the resonators + + for (j = 14; j <= 16; j++) // calculate output for 3 resonators + { + dblZout_0[j] = dblZComb + dblCoef[j] * dblZout_1[j] - dblR2 * dblZout_2[j]; + dblZout_2[j] = dblZout_1[j]; + dblZout_1[j] = dblZout_0[j]; + + // scale each by transition coeff and + (Even) or - (Odd) + + // Scaling also accomodates for the filter "gain" of approx 120. + // These transition coefficients fairly close to optimum for WGN 0db PSK4, 100 baud (yield highest average quality) 5/24/2014 + + if (j == 14 || j == 16) + FilterOut = 0.2f * dblZout_0[j]; // this transisiton minimizes ringing and peaks + else + FilterOut -= dblZout_0[j]; + } + intFilterOut[i] = (int)ceil(FilterOut * 0.00833333333); // rescales for gain of filter + } + +} + +// Function to apply 75Hz filter used in Envelope correlator + +void Filter75Hz(short * intFilterOut, BOOL blnInitialise, int intSamplesToFilter) +{ + // assumes sample rate of 12000 + // implements 3 100 Hz wide sections (~150 Hz wide @ - 30dB centered on 1500 Hz) + + // FSF (Frequency Selective Filter) variables + + static float dblR = 0.9995f; // insures stability (must be < 1.0) (Value .9995 7/8/2013 gives good results) + static int intN = 240; //Length of filter 12000/50 - delays output 120 samples from input + static float dblRn; + static float dblR2; + static float dblCoef[3] = {0.0}; // the coefficients + float dblZin = 0, dblZin_1 = 0, dblZin_2 = 0, dblZComb= 0; // Used in the comb generator + // The resonators + + float dblZout_0[3] = {0.0}; // resonator outputs + float dblZout_1[3] = {0.0}; // resonator outputs delayed one sample + float dblZout_2[3] = {0.0}; // resonator outputs delayed two samples + + int i, j; + + float FilterOut = 0; // Filtered sample + float largest = 0; + + dblRn = powf(dblR, intN); + + dblR2 = powf(dblR, 2); + + // Initialize the coefficients + + if (dblCoef[2] == 0) + { + for (i = 0; i <= 3; i++) + { + dblCoef[i] = 2 * dblR * cosf(2 * M_PI * (29 + i)/ intN); // For Frequency = bin 29, 30, 31 + } + } + + for (i = 0; i < intSamplesToFilter; i++) + { + if (i < intN) + dblZin = intFilteredMixedSamples[intMFSReadPtr + i] - dblRn * 0; // no prior mixed samples + else + dblZin = intFilteredMixedSamples[intMFSReadPtr + i] - dblRn * intFilteredMixedSamples[intMFSReadPtr + i - intN]; + + // Compute the Comb + + dblZComb = dblZin - dblZin_2 * dblR2; + dblZin_2 = dblZin_1; + dblZin_1 = dblZin; + + // Now the resonators + + for (j = 0; j < 3; j++) // calculate output for 3 resonators + { + dblZout_0[j] = dblZComb + dblCoef[j] * dblZout_1[j] - dblR2 * dblZout_2[j]; + dblZout_2[j] = dblZout_1[j]; + dblZout_1[j] = dblZout_0[j]; + + // scale each by transition coeff and + (Even) or - (Odd) + + // Scaling also accomodates for the filter "gain" of approx 120. + // These transition coefficients fairly close to optimum for WGN 0db PSK4, 100 baud (yield highest average quality) 5/24/2014 + + if (j == 0 || j == 2) + FilterOut -= 0.39811f * dblZout_0[j]; // this transisiton minimizes ringing and peaks + else + FilterOut += dblZout_0[j]; + } + intFilterOut[i] = (int)ceil(FilterOut * 0.0041f); // rescales for gain of filter + } +} + +// Subroutine to Mix new samples with NCO to tune to nominal 1500 Hz center with reversed sideband and filter. + +void MixNCOFilter(short * intNewSamples, int Length, float dblOffsetHz) +{ + // Correct the dimension of intPriorMixedSamples if needed (should only happen after a bandwidth setting change). + + int i; + short intMixedSamples[2400]; // All we need at once ( I hope!) // may need to be int + int intMixedSamplesLength = 0; //size of intMixedSamples + + if (Length == 0) + return; + + // Nominal NCO freq is 3000 Hz to downmix intNewSamples (NCO - Fnew) to center of 1500 Hz (invertes the sideband too) + + dblNCOFreq = 3000 + dblOffsetHz; + dblNCOPhaseInc = dblNCOFreq * dbl2Pi / 12000; + + intMixedSamplesLength = Length; + + for (i = 0; i < Length; i++) + { + intMixedSamples[i] = (int)ceilf(intNewSamples[i] * cosf(dblNCOPhase)); // later may want a lower "cost" implementation of "Cos" + dblNCOPhase += dblNCOPhaseInc; + if (dblNCOPhase > dbl2Pi) + dblNCOPhase -= dbl2Pi; + } + + + + // showed no significant difference if the 2000 Hz filer used for all bandwidths. +// printtick("Start Filter"); + FSMixFilter2500Hz(intMixedSamples, intMixedSamplesLength); // filter through the FS filter (required to reject image from Local oscillator) +// printtick("Done Filter"); + + // save for analysys + +// WriteSamples(&intFilteredMixedSamples[oldlen], Length); +// WriteSamples(intMixedSamples, Length); + +} + +// Function to Correct Raw demodulated data with Reed Solomon FEC + +int CorrectRawDataWithRS(UCHAR * bytRawData, UCHAR * bytCorrectedData, int intDataLen, int intRSLen, int bytFrameType, int Carrier) +{ + BOOL blnRSOK; + BOOL FrameOK; + BOOL OK; + + //Dim bytNoRS(1 + intDataLen + 2 - 1) As Byte ' 1 byte byte Count, Data, 2 byte CRC + //Array.Copy(bytRawData, 0, bytNoRS, 0, bytNoRS.Length) + + if (CarrierOk[Carrier]) // Already decoded this carrier? + { + // Athough we have already checked the data, it may be in the wrong place + // in the buffer if another carrier was decoded wrong. + + memcpy(bytCorrectedData, &bytRawData[1], bytRawData[0] + 1); // Extra char in case OFDM + + if (strFrameType[LastDataFrameType][0] == 'O') + Debugprintf("[CorrectRawDataWithRS] Carrier %d already decoded Block %d, Len %d", Carrier, bytRawData[1], bytRawData[0]); + else + Debugprintf("[CorrectRawDataWithRS] Carrier %d already decoded Len %d", Carrier, bytRawData[0]); + return bytRawData[0]; // don't do it again + } + + if (strFrameType[intFrameType][0] == 'O') + OK = CheckCRC16(bytRawData, intDataLen + 1); + else + OK = CheckCRC16FrameType(bytRawData, intDataLen + 1, bytFrameType); + + // As crc can fail also check returned lenght is reasonable + + if (OK && bytRawData[0] <= intDataLen) // No RS correction needed // return the actual data + { + memcpy(bytCorrectedData, &bytRawData[1], bytRawData[0] + 1); + if (strFrameType[intFrameType][0] == 'O') + Debugprintf("[CorrectRawDataWithRS] Carrier %d OK without RS, Block %d Len = %d", Carrier, bytRawData[1], bytRawData[0]); + else + Debugprintf("[CorrectRawDataWithRS] Carrier %d OK without RS, Len = %d", Carrier, bytRawData[0]); + + CarrierOk[Carrier] = TRUE; + return bytRawData[0]; + } + + // Try correcting with RS Parity + + FrameOK = RSDecode(bytRawData, intDataLen + 3 + intRSLen, intRSLen, &blnRSOK); + + if (blnRSOK) + {} +// Debugprintf("RS Says OK without correction"); + else + if (FrameOK) + {} +// Debugprintf("RS Says OK after %d correction(s)", NErrors); + else + { + Debugprintf("[intFrameType] RS Says Can't Correct"); + goto returnBad; + } + + if (FrameOK) + { + if (strFrameType[intFrameType][0] == 'O') + OK = CheckCRC16(bytRawData, intDataLen + 1); + else + OK = CheckCRC16FrameType(bytRawData, intDataLen + 1, bytFrameType); + + if (OK && bytRawData[0] <= intDataLen) // Now OK - return the actual data + { + int intFailedByteCnt = 0; + + if (strFrameType[intFrameType][0] == 'O') + Debugprintf("[CorrectRawDataWithRS] Carrier %d OK with RS %d corrections, Block %d, Len = %d", Carrier, NErrors, bytRawData[1], bytRawData[0]); + else + Debugprintf("[CorrectRawDataWithRS] Carrier %d OK with RS %d corrections, Len = %d", Carrier, NErrors, bytRawData[0]); + + totalRSErrors += NErrors; + + memcpy(bytCorrectedData, &bytRawData[1], bytRawData[0] + 1); + CarrierOk[Carrier] = TRUE; + return bytRawData[0]; + } + Debugprintf("[CorrectRawDataWithRS] Carrier %d RS says ok but CRC still bad", Carrier); + } + // return uncorrected data without byte count or RS Parity + +returnBad: + + memcpy(bytCorrectedData, &bytRawData[1], intDataLen + 1); + + CarrierOk[Carrier] = FALSE; + return intDataLen; +} + + + +// Subroutine to process new samples as received from the sound card via Main.ProcessCapturedData +// Only called when not transmitting + +double dblPhaseInc; // in milliradians +short intNforGoertzel[MAXCAR]; +short intPSKPhase_1[MAXCAR], intPSKPhase_0[MAXCAR]; +short intCP[MAXCAR]; // Cyclic prefix offset +float dblFreqBin[MAXCAR]; + +BOOL CheckFrameTypeParity(int intTonePtr, int * intToneMags); + +void ARDOPProcessNewSamples(short * Samples, int nSamples) +{ + BOOL blnFrameDecodedOK = FALSE; + +// LookforUZ7HOLeader(Samples, nSamples); + +// printtick("Start afsk"); +// DemodAFSK(Samples, nSamples); +// printtick("End afsk"); + +// return; + + + if (ProtocolState == FECSend) + return; + + // Append new data to anything in rawSamples + + memcpy(&rawSamples[rawSamplesLength], Samples, nSamples * 2); + rawSamplesLength += nSamples; + + if (rawSamplesLength > maxrawSamplesLength) + maxrawSamplesLength = rawSamplesLength; + + if (rawSamplesLength >= 2400) + Debugprintf("Corrupt rawSamplesLength %d", rawSamplesLength); + + + nSamples = rawSamplesLength; + Samples = rawSamples; + + rawSamplesLength = 0; + +// printtick("Start Busy"); + if (nSamples >= 1024) + UpdateBusyDetector(Samples); +// printtick("Done Busy"); + + // it seems that searchforleader runs on unmixed and unfilered samples + + // Searching for leader + + if (State == SearchingForLeader) + { + // Search for leader as long as 960 samples (8 symbols) available + +// printtick("Start Leader Search"); + + if (nSamples >= 1200) + { + if (ProtocolState == FECSend) + return; + } + while (State == SearchingForLeader && nSamples >= 1200) + { + int intSN; + + blnLeaderFound = SearchFor2ToneLeader4(Samples, nSamples, &dblOffsetHz, &intSN); +// blnLeaderFound = SearchFor2ToneLeader2(Samples, nSamples, &dblOffsetHz, &intSN); + + if (blnLeaderFound) + { +// Debugprintf("Got Leader"); + + dttLastLeaderDetect = Now; + + nSamples -= 480; + Samples += 480; // !!!! needs attention !!! + + InitializeMixedSamples(); + State = AcquireSymbolSync; + } + else + { + if (SlowCPU) + { + nSamples -= 480; + Samples += 480; // advance pointer 2 symbols (40 ms) ' reduce CPU loading + } + else + { + nSamples -= 240; + Samples += 240; // !!!! needs attention !!! + } + } + } + if (State == SearchingForLeader) + { + // Save unused samples + + memmove(rawSamples, Samples, nSamples * 2); + rawSamplesLength = nSamples; + +// printtick("End Leader Search"); + + return; + } + } + + + // Got leader + + // At this point samples haven't been processed, and are in Samples, len nSamples + + // I'm going to filter all samples into intFilteredMixedSamples. + +// printtick("Start Mix"); + + MixNCOFilter(Samples, nSamples, dblOffsetHz); // Mix and filter new samples (Mixing consumes all intRcvdSamples) + nSamples = 0; // all used + +// printtick("Done Mix Samples"); + + // Acquire Symbol Sync + + if (State == AcquireSymbolSync) + { + if ((intFilteredMixedSamplesLength - intMFSReadPtr) > 960) + { + blnSymbolSyncFound = Acquire2ToneLeaderSymbolFraming(); // adjust the pointer to the nominal symbol start based on phase + if (blnSymbolSyncFound) + State = AcquireFrameSync; + else + { + // Rick's Latest code (2.0.3) advances pointer instead of clearing samples +// DiscardOldSamples(); +// ClearAllMixedSamples(); + intMFSReadPtr += 240; // advance the MFSReadPointer one symbol and try to search for leader again. + State = SearchingForLeader; + return; + } +// printtick("Got Sym Sync"); + } + } + + // Acquire Frame Sync + + if (State == AcquireFrameSync) + { + blnFrameSyncFound = AcquireFrameSyncRSB(); + + // Remove used samples + + intFilteredMixedSamplesLength -= intMFSReadPtr; + + if (intFilteredMixedSamplesLength < 0) + Debugprintf("Corrupt intFilteredMixedSamplesLength"); + + memmove(intFilteredMixedSamples, + &intFilteredMixedSamples[intMFSReadPtr], intFilteredMixedSamplesLength * 2); + + intMFSReadPtr = 0; + + + if (blnFrameSyncFound) + { + State = AcquireFrameType; + + // Have frame Sync. Remove used samples from buffer + + printtick("Got Frame Sync"); + + } + else if ((Now - dttLastLeaderDetect) > 1000) // no Frame sync within 1000 ms (may want to make this limit a funciton of Mode and leaders) + { + DiscardOldSamples(); + ClearAllMixedSamples(); + State = SearchingForLeader; + printtick("frame sync timeout"); + } +/* +else if (intPhaseError > 2) + { + DiscardOldSamples(); + ClearAllMixedSamples(); + State = SearchingForLeader; + printtick("frame sync timeout"); + } +// else +// printtick("no frame sync"); +*/ + + + } + + // Acquire Frame Type + + if (State == AcquireFrameType) + { +// printtick("getting frame type"); + + intFrameType = Acquire4FSKFrameType(); + if (intFrameType == -2) + { +// sprintf(Msg, "not enough %d %d", intFilteredMixedSamplesLength, intMFSReadPtr); +// printtick(Msg); + return; // insufficient samples + } + + if (intFrameType == -1) // poor decode quality (large decode distance) + { + State = SearchingForLeader; + ClearAllMixedSamples(); + DiscardOldSamples(); + Debugprintf("poor frame type decode"); + + // stcStatus.BackColor = SystemColors.Control + // stcStatus.Text = "" + // stcStatus.ControlName = "lblRcvFrame" + // queTNCStatus.Enqueue(stcStatus) + } + else + { + // Get Frame info and Initialise Demodulate variables + + // We've used intMFSReadPtr samples, so remove from Buffer + +// sprintf(Msg, "Got Frame Type %x", intFrameType); +// printtick(Msg); + + blnLastBusyStatus = 1; + blnBusyStatus = 1; + BusyCount = 10; + + intFilteredMixedSamplesLength -= intMFSReadPtr; + + if (intFilteredMixedSamplesLength < 0) + Debugprintf("Corrupt intFilteredMixedSamplesLength"); + + memmove(intFilteredMixedSamples, + &intFilteredMixedSamples[intMFSReadPtr], intFilteredMixedSamplesLength * 2); + + intMFSReadPtr = 0; + + if (!FrameInfo(intFrameType, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytMinQualThresh, strType)) + { + printtick("bad frame type"); + State = SearchingForLeader; + ClearAllMixedSamples(); + DiscardOldSamples(); + return; + } + + if (IsShortControlFrame(intFrameType)) + { + // Frame has no data so is now complete + + DrawRXFrame(1, Name(intFrameType)); + + // See if IRStoISS shortcut can be invoked + // prepare for next + + DiscardOldSamples(); + ClearAllMixedSamples(); + State = SearchingForLeader; + blnFrameDecodedOK = TRUE; + Debugprintf("[DecodeFrame] Frame: %s ", Name(intFrameType)); + + DecodeCompleteTime = Now; + + goto ProcessFrame; + } + + DrawRXFrame(0, Name(intFrameType)); + + if (intBaud == 25) + intSampPerSym = 480; + else if (intBaud == 50) + intSampPerSym = 240; + else if (intBaud == 55) + intSampPerSym = 216; + else if (intBaud == 100) + intSampPerSym = 120; + else if (intBaud == 167) + intSampPerSym = 72; + else if (intBaud == 600) + intSampPerSym = 20; + + if (IsDataFrame(intFrameType)) + SymbolsLeft = intDataLen + intRSLen + 3; // Data has crc + length byte + else if (intFrameType == OFDMACK) + SymbolsLeft = intDataLen + intRSLen + 2; // CRC but no len + else + SymbolsLeft = intDataLen + intRSLen; // No CRC + + if (intDataLen == 600) + SymbolsLeft += 6; // 600 baud has 3 * RS Blocks + + // Save data rate for PTC reporting + + if (Rate[intFrameType] > 0) + DataRate = Rate[intFrameType]; + + intToneMagsLength = 16 * SymbolsLeft; // 4 tones, 2 bits per set + + memset(intToneMagsIndex, 0, sizeof(intToneMagsIndex)); + + charIndex = 0; + PSKInitDone = 0; + + frameLen = 0; + totalRSErrors = 0; + + DummyCarrier = 0; // pseudo carrier used for long 600 baud frames + Decode600Buffer = &bytFrameData[0][0]; + + State = AcquireFrame; + + // if a data frame, and not the same frame type as last, reinitialise + // correctly received carriers byte and memory ARQ fields + +// if (IsDataFrame(intFrameType) && LastDataFrameType != intFrameType) + + if (intFrameType == PktFrameHeader || intFrameType == PktFrameData) + { + memset(CarrierOk, 0, sizeof(CarrierOk)); + memset(intSumCounts, 0, sizeof(intSumCounts)); +#ifdef MEMORYARQ + memset(intToneMagsAvg, 0, sizeof(intToneMagsAvg)); + memset(intCarPhaseAvg, 0, sizeof(intCarPhaseAvg)); + memset(intCarMagAvg, 0, sizeof(intCarMagAvg)); +#endif + LastDataFrameType = intFrameType; + } + else if (LastDataFrameType != intFrameType) + { + if (strFrameType[LastDataFrameType][0] == 'O') + { + // OFDM Frame, We know the ISS received the last ack, so can remove any data passed to host. + // We need to do that, as new frame block numbers will start from first unacked block. + + if (intFrameType == OFDMACK) + RepeatedFrame = FALSE; + + RepeatedFrame = FALSE; + RemoveProcessedOFDMData(); + } + + Debugprintf("New frame type - MEMARQ flags reset"); + memset(CarrierOk, 0, sizeof(CarrierOk)); + + if (IsDataFrame(intFrameType)) + LastDataFrameType = intFrameType; + + // note that although we only do mem arq if enough RAM we + // still skip decoding carriers that have been received; + +#ifdef MEMORYARQ + memset(intSumCounts, 0, sizeof(intSumCounts)); + memset(intToneMagsAvg, 0, sizeof(intToneMagsAvg)); + memset(intCarPhaseAvg, 0, sizeof(intCarPhaseAvg)); + memset(intCarMagAvg, 0, sizeof(intCarMagAvg)); +#endif + } + else + { + // Repeated frame. OFDM needs to know, as we may have passed data to host. + + if (IsDataFrame(intFrameType)) + RepeatedFrame = TRUE; + + } + + PrintCarrierFlags(); + } + } + // Acquire Frame + + if (State == AcquireFrame) + { + // Call DemodulateFrame for each set of samples + + DemodulateFrame(intFrameType); + + if (State == AcquireFrame) + + // We haven't got it all yet so wait for more samples + return; + + // We have the whole frame, so process it + + +// printtick("got whole frame"); + + LastDemodType = intFrameType; + + if (strcmp (strMod, "4FSK") == 0) + Update4FSKConstellation(&intToneMags[0][0], &intLastRcvdFrameQuality); + else if (strcmp (strMod, "16FSK") == 0) + Update16FSKConstellation(&intToneMags[0][0], &intLastRcvdFrameQuality); + else if (strcmp (strMod, "8FSK") == 0) + Update8FSKConstellation(&intToneMags[0][0], &intLastRcvdFrameQuality); + + // PSK and QAM quality done in Decode routines + + Debugprintf("Qual = %d", intLastRcvdFrameQuality); + + // This mechanism is to skip actual decoding and reply/change state...no need to decode + + + blnFrameDecodedOK = DecodeFrame(intFrameType, bytData); + +ProcessFrame: + + if (!blnFrameDecodedOK) + DrawRXFrame(2, Name(intFrameType)); + + if (intFrameType == PktFrameData) + { +#ifdef TEENSY + SetLED(PKTLED, TRUE); // Flash LED + PKTLEDTimer = Now + 200; // for 200 mS +#endif + return; + } + + if (blnFrameDecodedOK) + { + // Set input level if supported + +#ifdef HASPOTS + CheckandAdjustRXLevel(lastmax, lastmin, TRUE); +#endif + if (AccumulateStats) + if (IsDataFrame(intFrameType)) + if (strstr (strMod, "PSK")) + intGoodPSKFrameDataDecodes++; + else if (strstr (strMod, "QAM")) + intGoodQAMFrameDataDecodes++; + else if (strstr (strMod, "OFDM")) + intGoodOFDMFrameDataDecodes++; + else + intGoodFSKFrameDataDecodes++; + +#ifdef TEENSY + if (IsDataFrame(intFrameType)) + { + SetLED(PKTLED, TRUE); // Flash LED + PKTLEDTimer = Now + 400; // For 400 Ms + } +#endif + } + else + { + // Bad decode + + if (AccumulateStats) + if (IsDataFrame(intFrameType)) + if (strstr(strMod, "PSK")) + intFailedPSKFrameDataDecodes++; + else if (strstr(strMod, "QAM")) + intFailedQAMFrameDataDecodes++; + else if (strstr(strMod, "OFDM")) + intFailedOFDMFrameDataDecodes++; + else + intFailedFSKFrameDataDecodes++; + + + // Debug.WriteLine("[DecodePSKData2] bytPass = " & Format(bytPass, "X")) + + } +skipDecode: + State = SearchingForLeader; + ClearAllMixedSamples(); + DiscardOldSamples(); + return; + + } +} +// Subroutine to compute Goertzel algorithm and return Real and Imag components for a single frequency bin + +void GoertzelRealImag(short intRealIn[], int intPtr, int N, float m, float * dblReal, float * dblImag) +{ + // intRealIn is a buffer at least intPtr + N in length + // N need not be a power of 2 + // m need not be an integer + // Computes the Real and Imaginary Freq values for bin m + // Verified to = FFT results for at least 10 significant digits + // Timings for 1024 Point on Laptop (64 bit Core Duo 2.2 Ghz) + // GoertzelRealImag .015 ms Normal FFT (.5 ms) + // assuming Goertzel is proportional to N and FFT time proportional to Nlog2N + // FFT:Goertzel time ratio ~ 3.3 Log2(N) + + // Sanity check + + //if (intPtr < 0 Or (intRealIn.Length - intPtr) < N Then + // dblReal = 0 : dblImag = 0 : Exit Sub + // End If + + float dblZ_1 = 0.0f, dblZ_2 = 0.0f, dblW = 0.0f; + float dblCoeff = 2 * cosf(2 * M_PI * m / N); + int i; + + for (i = 0; i <= N; i++) + { + if (i == N) + dblW = dblZ_1 * dblCoeff - dblZ_2; + else + dblW = intRealIn[intPtr] + dblZ_1 * dblCoeff - dblZ_2; + + dblZ_2 = dblZ_1; + dblZ_1 = dblW; + intPtr++; + } + *dblReal = 2 * (dblW - cosf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 + *dblImag = 2 * (sinf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 (this sign agrees with Scope DSP phase values) +} + +// Subroutine to compute Goertzel algorithm and return Real and Imag components for a single frequency bin with a Hanning Window function + +float dblHanWin[120]; +float dblHanAng; +int HanWinLen = 0; + +float dblHannWin[480]; +float dblHannAng; + +// Subroutine to compute Goertzel algorithm and return Real and Imag components for a single frequency bin with a Hann Window function for N a multiple of 120 + +void GoertzelRealImagHann120(short intRealIn[], int intPtr, int N, float m, float * dblReal, float * dblImag) +{ + // This version precomputes the raised cosine (Hann or Hanning) window and uses it for any length that is a multiple of 120 samples + // intRealIn is a buffer at least intPtr + N in length + // N must be 960 to use this function + // Hann coefficients are approximated for N>120 but should be close + // m need not be an integer + // Computes the Real and Imaginary Freq values for bin m + // Verified to = FFT results for at least 10 significant digits + // Timings for 1024 Point on Laptop (64 bit Core Duo 2.2 Ghz) + // GoertzelRealImag .015 ms Normal FFT (.5 ms) + // assuming Goertzel is proportional to N and FFT time proportional to Nlog2N + // FFT:Goertzel time ratio ~ 3.3 Log2(N) + + + float dblZ_1 = 0.0f, dblZ_2 = 0.0f, dblW = 0.0f; + float dblCoeff = 2 * cosf(2 * M_PI * m / N); + + int i; + int intM = N / 120; // No if 120 sample blocks + + if (HanWinLen != N) //if there is any change in N this is then recalculate the Hanning Window...this mechanism reduces use of Cos + { + HanWinLen = N; + + dblHanAng = 2 * M_PI / 120; + + for (i = 0; i < 60; i++) + { + dblHanWin[i] = 0.5 - 0.5 * cosf(i * dblHanAng + dblHanAng); + } + } + + for (i = 0; i <= N; i++) + { + if (i == N) + dblW = dblZ_1 * dblCoeff - dblZ_2; + + else if (i < (N / 2)) // ist half of 120 sample block + // looks like we use values 0 ti 59 then 59 down to 0 + dblW = intRealIn[intPtr] * dblHanWin[(i /intM) % 60] + dblZ_1 * dblCoeff - dblZ_2; + else + dblW = intRealIn[intPtr] * dblHanWin[59 - ((i /intM) % 60)] + dblZ_1 * dblCoeff - dblZ_2; + + dblZ_2 = dblZ_1; + dblZ_1 = dblW; + intPtr++; + } + + *dblReal = 2 * (dblW - cosf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 + *dblImag = 2 * (sinf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 (this sign agrees with Scope DSP phase values) + +} + + + + +void GoertzelRealImagHann960(short intRealIn[], int intPtr, int N, float m, float * dblReal, float * dblImag) +{ + // This version precomputes the raised cosine (Hann or Hanning) window and uses it for any length that is a multiple of 120 samples + // intRealIn is a buffer at least intPtr + N in length + // N must be a multiple of 120 to use this function + // Hann coefficients are approximated for N>120 but should be close + // m need not be an integer + // Computes the Real and Imaginary Freq values for bin m + // Verified to = FFT results for at least 10 significant digits + // Timings for 1024 Point on Laptop (64 bit Core Duo 2.2 Ghz) + // GoertzelRealImag .015 ms Normal FFT (.5 ms) + // assuming Goertzel is proportional to N and FFT time proportional to Nlog2N + // FFT:Goertzel time ratio ~ 3.3 Log2(N) + + + float dblZ_1 = 0.0f, dblZ_2 = 0.0f, dblW = 0.0f; + float dblCoeff = 2 * cosf(2 * M_PI * m / N); + + int i; + int intM = N / 120; // No if 120 sample blocks + + if (dblHannWin[479] < 0.5) //if there is any change in N this is then recalculate the Hanning Window...this mechanism reduces use of Cos + { + dblHannAng = 2 * M_PI / 960; + + for (i = 0; i < 480; i++) + { + dblHannWin[i] = 0.5 - 0.5 * cosf(i * dblHannAng + dblHannAng); + } + } + + for (i = 0; i <= N; i++) + { + if (i == N) + dblW = dblZ_1 * dblCoeff - dblZ_2; + + else if (i < (N / 2)) // ist half of 120 sample block + // looks like we use values 0 ti 59 then 59 down to 0 + dblW = intRealIn[intPtr] * dblHannWin[(i /intM) % 60] + dblZ_1 * dblCoeff - dblZ_2; + else + dblW = intRealIn[intPtr] * dblHannWin[479 - ((i /intM) % 60)] + dblZ_1 * dblCoeff - dblZ_2; + + dblZ_2 = dblZ_1; + dblZ_1 = dblW; + intPtr++; + } + + *dblReal = 2 * (dblW - cosf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 + *dblImag = 2 * (sinf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 (this sign agrees with Scope DSP phase values) + +} + + + + + +void GoertzelRealImagHanning(short intRealIn[], int intPtr, int N, float m, float * dblReal, float * dblImag) +{ + // intRealIn is a buffer at least intPtr + N in length + // N need not be a power of 2 + // m need not be an integer + // Computes the Real and Imaginary Freq values for bin m + // Verified to = FFT results for at least 10 significant digits + // Timings for 1024 Point on Laptop (64 bit Core Duo 2.2 Ghz) + // GoertzelRealImag .015 ms Normal FFT (.5 ms) + // assuming Goertzel is proportional to N and FFT time proportional to Nlog2N + // FFT:Goertzel time ratio ~ 3.3 Log2(N) + + // Sanity check + + float dblZ_1 = 0.0f, dblZ_2 = 0.0f, dblW = 0.0f; + float dblCoeff = 2 * cosf(2 * M_PI * m / N); + + int i; + + if (HanWinLen != N) //if there is any change in N this is then recalculate the Hanning Window...this mechanism reduces use of Cos + { + HanWinLen = N; + + dblHanAng = 2 * M_PI / (N - 1); + + for (i = 0; i < N; i++) + { + dblHanWin[i] = 0.5 - 0.5 * cosf(i * dblHanAng); + } + } + + for (i = 0; i <= N; i++) + { + if (i == N) + dblW = dblZ_1 * dblCoeff - dblZ_2; + else + dblW = intRealIn[intPtr] * dblHanWin[i] + dblZ_1 * dblCoeff - dblZ_2; + + dblZ_2 = dblZ_1; + dblZ_1 = dblW; + intPtr++; + } + + *dblReal = 2 * (dblW - cosf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 + *dblImag = 2 * (sinf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 (this sign agrees with Scope DSP phase values) +} + +float dblHamWin[1200]; +float dblHamAng; +int HamWinLen = 0; + +void GoertzelRealImagHamming(short intRealIn[], int intPtr, int N, float m, float * dblReal, float * dblImag) +{ + // intRealIn is a buffer at least intPtr + N in length + // N need not be a power of 2 + // m need not be an integer + // Computes the Real and Imaginary Freq values for bin m + // Verified to = FFT results for at least 10 significant digits + // Timings for 1024 Point on Laptop (64 bit Core Duo 2.2 Ghz) + // GoertzelRealImag .015 ms Normal FFT (.5 ms) + // assuming Goertzel is proportional to N and FFT time proportional to Nlog2N + // FFT:Goertzel time ratio ~ 3.3 Log2(N) + + // Sanity check + + float dblZ_1 = 0.0f, dblZ_2 = 0.0f, dblW = 0.0f; + float dblCoeff = 2 * cosf(2 * M_PI * m / N); + + int i; + + if (HamWinLen != N) //if there is any cHamge in N this is then recalculate the Hanning Window...this mechanism reduces use of Cos + { + HamWinLen = N; + + dblHamAng = 2 * M_PI / (N - 1); + + for (i = 0; i < N; i++) + { + dblHamWin[i] = 0.54f - 0.46f * cosf(i * dblHamAng); + } + } + + for (i = 0; i <= N; i++) + { + if (i == N) + dblW = dblZ_1 * dblCoeff - dblZ_2; + else + dblW = intRealIn[intPtr] * dblHamWin[i] + dblZ_1 * dblCoeff - dblZ_2; + + dblZ_2 = dblZ_1; + dblZ_1 = dblW; + intPtr++; + } + + *dblReal = 2 * (dblW - cosf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 + *dblImag = 2 * (sinf(2 * M_PI * m / N) * dblZ_2) / N; // scale results by N/2 (this sign agrees with Scope DSP phase values) +} + +// Function to interpolate spectrum peak using Quinn algorithm + +float QuinnSpectralPeakLocator(float XkM1Re, float XkM1Im, float XkRe, float XkIm, float XkP1Re, float XkP1Im) +{ + // based on the Quinn algorithm in Streamlining Digital Processing page 139 + // Alpha1 = Re(Xk-1/Xk) + // Alpha2 = Re(Xk+1/Xk) + //Delta1 = Alpha1/(1 - Alpha1) + //'Delta2 = Alpha2/(1 - Alpha2) + // if Delta1 > 0 and Delta2 > 0 then Delta = Delta2 else Delta = Delta1 + // should be within .1 bin for S:N > 2 dB + + float dblDenom = powf(XkRe, 2) + powf(XkIm, 2); + float dblAlpha1; + float dblAlpha2; + float dblDelta1; + float dblDelta2; + + dblAlpha1 = ((XkM1Re * XkRe) + (XkM1Im * XkIm)) / dblDenom; + dblAlpha2 = ((XkP1Re * XkRe) + (XkP1Im * XkIm)) / dblDenom; + dblDelta1 = dblAlpha1 / (1 - dblAlpha1); + dblDelta2 = dblAlpha2 / (1 - dblAlpha2); + + if (dblDelta1 > 0 && dblDelta2 > 0) + return dblDelta2; + else + return dblDelta1; +} + +// Function to interpolate spectrum peak using simple interpolation + +float SpectralPeakLocator(float XkM1Re, float XkM1Im, float XkRe, float XkIm, float XkP1Re, float XkP1Im, float * dblCentMag, char * Win) +{ + // Use this for Windowed samples instead of QuinnSpectralPeakLocator + + float dblLeftMag, dblRightMag; + *dblCentMag = sqrtf(powf(XkRe, 2) + powf(XkIm, 2)); + + dblLeftMag = sqrtf(powf(XkM1Re, 2) + powf(XkM1Im, 2)); + dblRightMag = sqrtf(powf(XkP1Re, 2) + powf(XkP1Im, 2)); + + //Factor 1.22 empirically determine optimum for Hamming window + // For Hanning Window use factor of 1.36 + // For Blackman Window use factor of 1.75 + + if (strcmp(Win, "Blackman")) + return 1.75 * (dblRightMag - dblLeftMag) / (dblLeftMag + *dblCentMag + dblRightMag); // Optimized for Hamming Window + if (strcmp(Win, "Hann")) + return 1.36 * (dblRightMag - dblLeftMag) / (dblLeftMag + *dblCentMag + dblRightMag); // Optimized for Hamming Window + if (strcmp(Win, "Hamming")) + return 1.22 * (dblRightMag - dblLeftMag) / (dblLeftMag + *dblCentMag + dblRightMag); // Optimized for Hamming Window + + return 0; +} + +// Function to detect and tune the 50 baud 2 tone leader (for all bandwidths) Updated version of SearchFor2ToneLeader2 + +float dblPriorFineOffset = 1000.0f; + + +BOOL SearchFor2ToneLeader3(short * intNewSamples, int Length, float * dblOffsetHz, int * intSN) +{ + // This version uses 10Hz bin spacing. Hamming window on Goertzel, and simple spectral peak interpolator + // It requires about 50% more CPU time when running but produces more sensive leader detection and more accurate tuning + // search through the samples looking for the telltail 50 baud 2 tone pattern (nominal tones 1475, 1525 Hz) + // Find the offset in Hz (due to missmatch in transmitter - receiver tuning + // Finds the S:N (power ratio of the tones 1475 and 1525 ratioed to "noise" averaged from bins at 1425, 1450, 1550, and 1575Hz) + + float dblGoertzelReal[56]; + float dblGoertzelImag[56]; + float dblMag[56]; + float dblPower, dblLeftMag, dblRightMag; + float dblMaxPeak = 0.0, dblMaxPeakSN = 0.0, dblBinAdj; + int intInterpCnt = 0; // the count 0 to 3 of the interpolations that were < +/- .5 bin + int intIatMaxPeak = 0; + float dblAlpha = 0.3f; // Works well possibly some room for optimization Changed from .5 to .3 on Rev 0.1.5.3 + float dblInterpretThreshold= 1.0f; // Good results June 6, 2014 (was .4) ' Works well possibly some room for optimization + float dblFilteredMaxPeak = 0; + int intStartBin, intStopBin; + float dblLeftCar, dblRightCar, dblBinInterpLeft, dblBinInterpRight, dblCtrR, dblCtrI, dblLeftP, dblRightP; + float dblLeftR[3], dblLeftI[3], dblRightR[3], dblRightI[3]; + int i; + int Ptr = 0; + float dblAvgNoisePerBin, dblCoarsePwrSN, dblBinAdj1475, dblBinAdj1525, dblCoarseOffset = 1000; + float dblTrialOffset, dblPowerEarly, dblSNdBPwrEarly; + + if ((Length) < 1200) + return FALSE; // ensure there are at least 1200 samples (5 symbols of 240 samples) + +// if ((Now - dttLastGoodFrameTypeDecode > 20000) && TuningRange > 0) + { + // this is the full search over the full tuning range selected. Uses more CPU time and with possibly larger deviation once connected. + + intStartBin = ((200 - TuningRange) / 10); + intStopBin = 55 - intStartBin; + + dblMaxPeak = 0; + + // Generate the Power magnitudes for up to 56 10 Hz bins (a function of MCB.TuningRange) + + for (i = intStartBin; i <= intStopBin; i++) + { + // note hamming window reduces end effect caused by 1200 samples (not an even multiple of 240) but spreads response peaks + + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, i + 122.5f, &dblGoertzelReal[i], &dblGoertzelImag[i]); + dblMag[i] = powf(dblGoertzelReal[i], 2) + powf(dblGoertzelImag[i], 2); // dblMag(i) in units of power (V^2) + } + + // Search the bins to locate the max S:N in the two tone signal/avg noise. + + for (i = intStartBin + 5; i <= intStopBin - 10; i++) // ' +/- MCB.TuningRange from nominal + { + dblPower = sqrtf(dblMag[i] * dblMag[i + 5]); // using the product to minimize sensitivity to one strong carrier vs the two tone + // sqrt converts back to units of power from Power ^2 + // don't use center noise bin as too easily corrupted by adjacent carriers + + dblAvgNoisePerBin = (dblMag[i - 5] + dblMag[i - 3] + dblMag[i + 8] + dblMag[i + 10]) / 4; // Simple average + dblMaxPeak = dblPower / dblAvgNoisePerBin; + if (dblMaxPeak > dblMaxPeakSN) + { + dblMaxPeakSN = dblMaxPeak; + dblCoarsePwrSN = 10 * log10f(dblMaxPeak); + intIatMaxPeak = i + 122; + } + } + // Do the interpolation based on the two carriers at nominal 1475 and 1525Hz + + if (((intIatMaxPeak - 123) >= intStartBin) && ((intIatMaxPeak - 118) <= intStopBin)) // check to ensure no index errors + { + // Interpolate the adjacent bins using QuinnSpectralPeakLocator + + dblBinAdj1475 = SpectralPeakLocator( + dblGoertzelReal[intIatMaxPeak - 123], dblGoertzelImag[intIatMaxPeak - 123], + dblGoertzelReal[intIatMaxPeak - 122], dblGoertzelImag[intIatMaxPeak - 122], + dblGoertzelReal[intIatMaxPeak - 121], dblGoertzelImag[intIatMaxPeak - 121], &dblLeftMag, "Hamming"); + + if (dblBinAdj1475 < dblInterpretThreshold && dblBinAdj1475 > -dblInterpretThreshold) + { + dblBinAdj = dblBinAdj1475; + intInterpCnt += 1; + } + + dblBinAdj1525 = SpectralPeakLocator( + dblGoertzelReal[intIatMaxPeak - 118], dblGoertzelImag[intIatMaxPeak - 118], + dblGoertzelReal[intIatMaxPeak - 117], dblGoertzelImag[intIatMaxPeak - 117], + dblGoertzelReal[intIatMaxPeak - 116], dblGoertzelImag[intIatMaxPeak - 116], &dblRightMag, "Hamming"); + + if (dblBinAdj1525 < dblInterpretThreshold && dblBinAdj1525 > -dblInterpretThreshold) + { + dblBinAdj += dblBinAdj1525; + intInterpCnt += 1; + } + if (intInterpCnt == 0) + { + dblPriorFineOffset = 1000.0f; + return FALSE; + } + else + { + dblBinAdj = dblBinAdj / intInterpCnt; // average the offsets that are within 1 bin + dblCoarseOffset = 10.0f * (intIatMaxPeak + dblBinAdj - 147); // compute the Coarse tuning offset in Hz + } + } + else + { + dblPriorFineOffset = 1000.0f; + return FALSE; + } + } + + // Drop into Narrow Search + + + if (dblCoarseOffset < 999) + dblTrialOffset = dblCoarseOffset; // use the CoarseOffset calculation from above + else + dblTrialOffset = *dblOffsetHz; // use the prior offset value + + if (fabsf(dblTrialOffset) > TuningRange && TuningRange > 0) + { + dblPriorFineOffset = 1000.0f; + return False; + } + + dblLeftCar = 147.5f + dblTrialOffset / 10.0f; // the nominal positions of the two tone carriers based on the last computerd dblOffsetHz + dblRightCar = 152.5f + dblTrialOffset / 10.0f; + + // Calculate 4 bins total for Noise values in S/N computation (calculate average noise) ' Simple average of noise bins + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, 142.5f + dblTrialOffset / 10.0f, &dblCtrR, &dblCtrI); // nominal center -75 Hz + dblAvgNoisePerBin = powf(dblCtrR, 2) + powf(dblCtrI, 2); + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, 145.0f + dblTrialOffset / 10.0f, &dblCtrR, &dblCtrI); // center - 50 Hz + dblAvgNoisePerBin += powf(dblCtrR, 2) + powf(dblCtrI, 2); + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, 155.0 + dblTrialOffset / 10.0f, &dblCtrR, &dblCtrI); // center + 50 Hz + dblAvgNoisePerBin += powf(dblCtrR, 2) + powf(dblCtrI, 2); + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, 157.5 + dblTrialOffset / 10.0f, &dblCtrR, &dblCtrI); // center + 75 Hz + dblAvgNoisePerBin += powf(dblCtrR, 2) + powf(dblCtrI, 2); + dblAvgNoisePerBin = dblAvgNoisePerBin * 0.25f; // simple average, now units of power + + // Calculate one bin above and below the two nominal 2 tone positions for Quinn Spectral Peak locator + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, dblLeftCar - 1, &dblLeftR[0], &dblLeftI[0]); + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, dblLeftCar, &dblLeftR[1], &dblLeftI[1]); + dblLeftP = powf(dblLeftR[1], 2) + powf(dblLeftI[1], 2); + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, dblLeftCar + 1, &dblLeftR[2], &dblLeftI[2]); + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, dblRightCar - 1, &dblRightR[0], &dblRightI[0]); + GoertzelRealImagHamming(intNewSamples, Ptr, 1200, dblRightCar, &dblRightR[1], &dblRightI[1]); + dblRightP = powf(dblRightR[1], 2) + powf(dblRightI[1], 2); + GoertzelRealImag(intNewSamples, Ptr, 1200, dblRightCar + 1, &dblRightR[2], &dblRightI[2]); + + // Calculate the total power in the two tones + // This mechanism designed to reject single carrier but average both carriers if ratios is less than 4:1 + + if (dblLeftP > 4 * dblRightP) + dblPower = dblRightP; + else if (dblRightP > 4 * dblLeftP) + dblPower = dblLeftP; + else + dblPower = sqrtf(dblLeftP * dblRightP); + + dblSNdBPwr = 10 * log10f(dblPower / dblAvgNoisePerBin); + + // Early leader detect code to calculate S:N on the first 2 symbols) + // concept is to allow more accurate framing and sync detection and reduce false leader detects + + GoertzelRealImag(intNewSamples, Ptr, 480, 57.0f + dblTrialOffset / 25.0f, &dblCtrR, &dblCtrI); // nominal center -75 Hz + dblAvgNoisePerBin = powf(dblCtrR, 2) + powf(dblCtrI, 2); + GoertzelRealImag(intNewSamples, Ptr, 480, 58.0f + dblTrialOffset / 25.0f, &dblCtrR, &dblCtrI); // nominal center -75 Hz + dblAvgNoisePerBin += powf(dblCtrR, 2) + powf(dblCtrI, 2); + GoertzelRealImag(intNewSamples, Ptr, 480, 62.0f + dblTrialOffset / 25.0f, &dblCtrR, &dblCtrI); // nominal center -75 Hz + dblAvgNoisePerBin += powf(dblCtrR, 2) + powf(dblCtrI, 2); + GoertzelRealImag(intNewSamples, Ptr, 480, 63.0f + dblTrialOffset / 25.0f, &dblCtrR, &dblCtrI); // nominal center -75 Hz + dblAvgNoisePerBin = max(1000.0f, 0.25 * (dblAvgNoisePerBin + powf(dblCtrR, 2) + powf(dblCtrI, 2))); // average of 4 noise bins + dblLeftCar = 59 + dblTrialOffset / 25; // the nominal positions of the two tone carriers based on the last computerd dblOffsetHz + dblRightCar = 61 + dblTrialOffset / 25; + + GoertzelRealImag(intNewSamples, Ptr, 480, dblLeftCar, &dblCtrR, &dblCtrI); // LEFT carrier + dblLeftP = powf(dblCtrR, 2) + powf(dblCtrI, 2); + GoertzelRealImag(intNewSamples, Ptr, 480, dblRightCar, &dblCtrR, &dblCtrI); // Right carrier + dblRightP = powf(dblCtrR, 2) + powf(dblCtrI, 2); + + // the following rejects a single tone carrier but averages the two tones if ratio is < 4:1 + + if (dblLeftP > 4 * dblRightP) + dblPowerEarly = dblRightP; + else if (dblRightP > 4 * dblLeftP) + dblPowerEarly = dblLeftP; + else + dblPowerEarly = sqrtf(dblLeftP * dblRightP); + + dblSNdBPwrEarly = 10 * log10f(dblPowerEarly / dblAvgNoisePerBin); + + // End of Early leader detect test code + + if (dblSNdBPwr > (4 + Squelch) && dblSNdBPwrEarly > Squelch && (dblAvgNoisePerBin > 100.0f || dblPriorFineOffset != 1000.0f)) // making early threshold = lower (after 3 dB compensation for bandwidth) + { +// Debugprintf("Fine Search S:N= %f dB, Early S:N= %f dblAvgNoisePerBin %f ", dblSNdBPwr, dblSNdBPwrEarly, dblAvgNoisePerBin); + + // Calculate the interpolation based on the left of the two tones + + dblBinInterpLeft = SpectralPeakLocator(dblLeftR[0], dblLeftI[0], dblLeftR[1], dblLeftI[1], dblLeftR[2], dblLeftI[2], &dblLeftMag, "Hamming"); + + // And the right of the two tones + + dblBinInterpRight = SpectralPeakLocator(dblRightR[0], dblRightI[0], dblRightR[1], dblRightI[1], dblRightR[2], dblRightI[2], &dblRightMag, "Hamming"); + + // Weight the interpolated values in proportion to their magnitudes + + dblBinInterpLeft = dblBinInterpLeft * dblLeftMag / (dblLeftMag + dblRightMag); + dblBinInterpRight = dblBinInterpRight * dblRightMag / (dblLeftMag + dblRightMag); + +#ifdef ARMLINUX + { + int x = round(dblBinInterpLeft); // odd, but PI doesnt print floats properly + int y = round(dblBinInterpRight); + +// Debugprintf(" SPL Left= %d SPL Right= %d Offset %f, LeftMag %f RightMag %f", x, y, *dblOffsetHz, dblLeftMag, dblRightMag); + } +#else +// Debugprintf(" SPL Left= %f SPL Right= %f, Offset %f, LeftMag %f RightMag %f", +// dblBinInterpLeft, dblBinInterpRight, *dblOffsetHz, dblLeftMag, dblRightMag); +#endif + if (fabsf(dblBinInterpLeft + dblBinInterpRight) < 1.0) // sanity check for the interpolators + { + if (dblBinInterpLeft + dblBinInterpRight > 0) // consider different bounding below + *dblOffsetHz = dblTrialOffset + min((dblBinInterpLeft + dblBinInterpRight) * 10.0f, 3); // average left and right, adjustment bounded to +/- 3Hz max + else + *dblOffsetHz = dblTrialOffset + max((dblBinInterpLeft + dblBinInterpRight) * 10.0f, -3); + + // Note the addition of requiring a second detect with small offset dramatically reduces false triggering even at Squelch values of 3 + // The following demonstrated good detection down to -10 dB S:N with squelch = 3 and minimal false triggering. + // Added rev 0.8.2.2 11/6/2016 RM + + if (abs(dblPriorFineOffset - *dblOffsetHz) < 2.9f) + { + Debugprintf("Prior-Offset= %f", (dblPriorFineOffset - *dblOffsetHz)); + + // Capture power for debugging ...note: convert to 3 KHz noise bandwidth from 25Hz or 12.Hz for reporting consistancy. + + Debugprintf("Ldr; S:N(3KHz) Early= %f dB, Full %f dB, Offset= %f Hz: ", dblSNdBPwrEarly - 20.8f, dblSNdBPwr - 24.77f, *dblOffsetHz); + dttStartRmtLeaderMeasure = Now; + + + if (AccumulateStats) + { + dblLeaderSNAvg = ((dblLeaderSNAvg * intLeaderDetects) + dblSNdBPwr) / (1 + intLeaderDetects); + intLeaderDetects++; + } + + dblNCOFreq = 3000 + *dblOffsetHz; // Set the NCO frequency and phase inc for mixing + dblNCOPhaseInc = dbl2Pi * dblNCOFreq / 12000; + dttLastLeaderDetect = dttStartRmtLeaderMeasure = Now; + + State = AcquireSymbolSync; + *intSN = dblSNdBPwr - 24.77; // 23.8dB accomodates ratio of 3Kz BW:10 Hz BW (10Log 3000/10 = 24.77) + + // don't advance the pointer here + + dblPriorFineOffset = 1000.0f; + return TRUE; + } + else + dblPriorFineOffset = *dblOffsetHz; + + // always use 1 symbol inc when looking for next minimal offset + } + } + return FALSE; +} + + + + +BOOL SearchFor2ToneLeader4(short * intNewSamples, int Length, float * dblOffsetHz, int * intSN) +{ + // This version uses 12.5 Hz bin spacing. Blackman window on Goertzel, and simple spectral peak interpolator optimized for Blackman + // Blackman selected for maximum rejection (about 60 dB) of the other two-tone bin 50 Hz (4 x 12.5 Hz bins) away. + // search through the samples looking for the telltail 50 baud 2 tone pattern (nominal tones 1475, 1525 Hz) + // Find the offset in Hz (due to missmatch in transmitter - receiver tuning + // Finds the S:N (power ratio of the tones 1475 and 1525 ratioed to "noise" averaged from bins at 1425, 1450, 1550, and 1575Hz) + + float dblGoertzelReal[45]; + float dblGoertzelImag[45]; + float dblMag[45]; + float dblPower, dblPwrSNdB, dblLeftMag, dblRightMag, dblAvgNoisePerBinAtPeak; + float dblRealL, dblRealR, dblImagL, dblImagR; + float dblMaxPeak = 0.0, dblMaxPeakSN = 0.0, dblMagWindow; + int intInterpCnt = 0; // the count 0 to 3 of the interpolations that were < +/- .5 bin + int intIatMaxPeak = 0; + float dblAlpha = 0.3f; // Works well possibly some room for optimization Changed from .5 to .3 on Rev 0.1.5.3 + float dblInterpretThreshold= 1.0f; // Good results June 6, 2014 (was .4) ' Works well possibly some room for optimization + float dblFilteredMaxPeak = 0; + int intStartBin, intStopBin; + int i; + int Ptr = 0; + float dblAvgNoisePerBin, dblBinAdj1475, dblBinAdj1525, dblCoarseOffset = 1000; + float dblOffset = 1000; // initialize to impossible value + + // This should allow tunning from nominal bins at 1425Hz to 1575Hz +/- 200 Hz tuning range + + if ((Length) < 1200) + return FALSE; // ensure there are at least 1200 samples (5 symbols of 240 samples) + +// if ((Now - dttLastGoodFrameTypeDecode > 20000) && TuningRange > 0) +// { +// // this is the full search over the full tuning range selected. Uses more CPU time and with possibly larger deviation once connected. + + intStartBin = ((200 - TuningRange) / 12.5); + intStopBin = 44 - intStartBin; + + dblMaxPeak = 0; + dblMagWindow = 0; + dblMaxPeakSN = -100; + + // Generate the Power magnitudes for up to 56 10 Hz bins (a function of MCB.TuningRange) + + for (i = intStartBin; i <= intStopBin; i++) + { + // note Blackman window reduced end effect but looses sensitivity so sticking with Hann window + // Test of 4/22/2018 indicated accurate Hann window (960) gives about 1-2 dB more sensitivity than Blackman window + + GoertzelRealImagHann960(intNewSamples, Ptr, 960, i + 98, &dblGoertzelReal[i], &dblGoertzelImag[i]); + dblMag[i] = powf(dblGoertzelReal[i], 2) + powf(dblGoertzelImag[i], 2); // dblMag(i) in units of power (V^2) + dblMagWindow += dblMag[i]; + } + + // Search the bins to locate the max S:N in the two tone signal/avg noise. + + for (i = intStartBin + 4; i <= intStopBin - 8; i++) // ' +/- MCB.TuningRange from nominal + { + dblPower = sqrtf(dblMag[i] * dblMag[i + 4]); // using the product to minimize sensitivity to one strong carrier vs the two tone + // sqrt converts back to units of power from Power ^2 + // don't use center 7 noise bins as too easily corrupted by adjacent two-tone carriers + + dblAvgNoisePerBin = (dblMagWindow - (dblMag[i - 1] + dblMag[i] + dblMag[i + 1] + dblMag[i + 2] + dblMag[i + 3] + dblMag[i + 4] + dblMag[i + 5])) / (intStopBin - (intStartBin + 7)); + dblMaxPeak = dblPower / dblAvgNoisePerBin; + + if (dblMaxPeak > dblMaxPeakSN) + { + dblMaxPeakSN = dblMaxPeak; + dblAvgNoisePerBinAtPeak = max(dblAvgNoisePerBin, 1000.0f); + intIatMaxPeak = i + 98; + } + } + + dblMaxPeakSN = (dblMag[intIatMaxPeak - 98] + dblMag[intIatMaxPeak - 94]) / dblAvgNoisePerBinAtPeak; + dblPwrSNdB = 10.0f * log10f(dblMaxPeakSN); + + // Check aquelch + + if ((dblPwrSNdB > (3 * Squelch)) && dblPwrSNPower_dBPrior > (3 * Squelch)) + { + + // Do the interpolation based on the two carriers at nominal 1475 and 1525Hz + + if (((intIatMaxPeak - 99) >= intStartBin) && ((intIatMaxPeak - 103) <= intStopBin)) // check to ensure no index errors + { + // Interpolate the adjacent bins using QuinnSpectralPeakLocator + + dblBinAdj1475 = SpectralPeakLocator( + dblGoertzelReal[intIatMaxPeak - 99], dblGoertzelImag[intIatMaxPeak - 99], + dblGoertzelReal[intIatMaxPeak - 98], dblGoertzelImag[intIatMaxPeak - 98], + dblGoertzelReal[intIatMaxPeak - 97], dblGoertzelImag[intIatMaxPeak - 97], &dblLeftMag, "Hann"); + + dblBinAdj1525 = SpectralPeakLocator( + dblGoertzelReal[intIatMaxPeak - 95], dblGoertzelImag[intIatMaxPeak - 95], + dblGoertzelReal[intIatMaxPeak - 94], dblGoertzelImag[intIatMaxPeak - 94], + dblGoertzelReal[intIatMaxPeak - 93], dblGoertzelImag[intIatMaxPeak - 93], &dblRightMag, "Hann"); + + // Weight the offset calculation by the magnitude of the dblLeftMag and dblRightMag carriers + + dblOffset = 12.5 * (intIatMaxPeak + dblBinAdj1475 * dblLeftMag / (dblLeftMag + dblRightMag) + dblBinAdj1525 * dblRightMag / (dblLeftMag + dblRightMag) - 118); // compute the Coarse tuning offset in Hz + + if (fabsf(dblOffset) > (TuningRange + 7)) // Was 7 caused tuning problems + { + dblPwrSNPower_dBPrior = dblPwrSNdB; + return False; + } + + // recompute the S:N based on the interpolated bins and average with computation 1 and 2 symbols in the future + // Use of Hann window increases sensitivity slightly (1-2 dB) + + GoertzelRealImagHann120(intNewSamples, 0, 960, intIatMaxPeak + dblOffset / 12.5, &dblRealL, &dblImagL); + GoertzelRealImagHann120(intNewSamples, 0, 960, intIatMaxPeak + 4 + dblOffset / 12.5, &dblRealR, &dblImagR); + dblMaxPeakSN = (powf(dblRealL, 2) + powf(dblImagL, 2) + powf(dblRealR, 2) + powf(dblImagR, 2)) / dblAvgNoisePerBinAtPeak; + // now compute for 120 samples later + GoertzelRealImagHann120(intNewSamples, 120, 960, intIatMaxPeak + dblOffset / 12.5, &dblRealL, &dblImagL); + GoertzelRealImagHann120(intNewSamples, 120, 960, intIatMaxPeak + 4 + dblOffset / 12.5, &dblRealR, &dblImagR); + dblMaxPeakSN += (powf(dblRealL, 2) + powf(dblImagL, 2) + powf(dblRealR, 2) + powf(dblImagR, 2)) / dblAvgNoisePerBinAtPeak; + // and a third 240 samples later + GoertzelRealImagHann120(intNewSamples, 240, 960, intIatMaxPeak + dblOffset / 12.5, &dblRealL, &dblImagL); + GoertzelRealImagHann120(intNewSamples, 240, 960, intIatMaxPeak + 4 + dblOffset / 12.5, &dblRealR, &dblImagR); + dblMaxPeakSN += (powf(dblRealL, 2) + powf(dblImagL, 2) + powf(dblRealR, 2) + powf(dblImagR, 2)) / dblAvgNoisePerBinAtPeak; + + dblMaxPeakSN = dblMaxPeakSN / 3; // average the dblMaxPeakSN over the three calculations + // ???? Calc Twice ???? + dblMaxPeakSN = (powf(dblRealL, 2) + powf(dblImagL, 2) + powf(dblRealR, 2) + powf(dblImagR, 2)) / dblAvgNoisePerBinAtPeak; + + + dblPwrSNdB = 10 * log10f(dblMaxPeakSN); + + if (dblPwrSNdB > 3 * Squelch) // This average power now includes two samples from symbols +120 and + 240 samples + { + //strDecodeCapture = "Ldr; S:N(3KHz) Prior=" & Format(dblPwrSNPower_dBPrior, "#.0") & "dB, Current=" & Format(dblPwrSNdB, "#.0") & "dB, Offset=" & Format(dblOffset, "##0.00") & "Hz " + Debugprintf("Ldr; S:N(3KHz) Avg= %f dB, Offset== %f Hz", dblPwrSNdB, dblOffset); + dttStartRmtLeaderMeasure = Now; + if (AccumulateStats) + { + dblLeaderSNAvg = ((dblLeaderSNAvg * intLeaderDetects) + dblPwrSNdB) / (1 + intLeaderDetects); + intLeaderDetects += 1; + } + *dblOffsetHz = dblOffset; + dblNCOFreq = 3000 + *dblOffsetHz; // Set the NCO frequency and phase inc for mixing + dblNCOPhaseInc = dbl2Pi * dblNCOFreq / 12000; + // don't advance the pointer here + State = AcquireSymbolSync; + dttLastLeaderDetect = Now; + dblPhaseDiff1_2Avg = 10; // initialize to 10 to cause initialization of exponential averager in AcquireFrameSyncRSBAvg + *intSN = round(dblPwrSNdB - 20.8); // 20.8dB accomodates ratio of 3Kz BW: (effective Blackman Window bandwidth of ~25 Hz) + return True; + } + else + { + return False; + } + } + } + + dblPwrSNPower_dBPrior = dblPwrSNdB; + + return FALSE; +} + + + + +// Function to look at the 2 tone leader and establishes the Symbol framing using envelope search and minimal phase error. + +BOOL Acquire2ToneLeaderSymbolFraming() +{ + float dblCarPh; + float dblReal, dblImag; + int intLocalPtr = intMFSReadPtr; // try advancing one symbol to minimize initial startup errors + float dblAbsPhErr; + float dblMinAbsPhErr = 5000; // initialize to an excessive value + int intIatMinErr; + float dblPhaseAtMinErr; + int intAbsPeak = 0; + int intJatPeak = 0; + int i; + + // Use Phase of 1500 Hz leader to establish symbol framing. Nominal phase is 0 or 180 degrees + + if ((intFilteredMixedSamplesLength - intLocalPtr) < 960) + return FALSE; // not enough + + intLocalPtr = intMFSReadPtr + EnvelopeCorrelatorNew(); // should position the pointer at the symbol boundary + + if (intLocalPtr < intMFSReadPtr) + return False; // use negative value of EnvelopeCorrelator to indicate insufficient correlation. + + + // Check 2 samples either side of the intLocalPtr for minimum phase error.(closest to Pi or -Pi) + // Could be as much as .4 Radians (~70 degrees) depending on sampling positions. + + for (i = -2; i <= 2; i++) // 0 To 0 ' -2 To 2 ' for just 5 samples + { + // using the full symbol seemed to work best on weak Signals (0 to -5 dB S/N) June 15, 2015 + + GoertzelRealImagHann120(intFilteredMixedSamples, intLocalPtr + i, 240, 30, &dblReal, &dblImag); // Carrier at 1500 Hz nominal Positioning + dblCarPh = atan2f(dblImag, dblReal); + dblAbsPhErr = fabsf(dblCarPh - (ceil(dblCarPh / M_PI) * M_PI)); + if (dblAbsPhErr < dblMinAbsPhErr) + { + dblMinAbsPhErr = dblAbsPhErr; + intIatMinErr = i; + dblPhaseAtMinErr = dblCarPh; + } + } + + intMFSReadPtr = intLocalPtr + intIatMinErr; + Debugprintf("[Acquire2ToneLeaderSymbolFraming] intIatMinError= %d, Leader Length %d mS", intIatMinErr, Now - dttLastLeaderDetect); + State = AcquireFrameSync; + + if (AccumulateStats) + intLeaderSyncs++; + + //Debug.WriteLine(" [Acquire2ToneLeaderSymbolSync] iAtMinError = " & intIatMinErr.ToString & " Ptr = " & intMFSReadPtr.ToString & " MinAbsPhErr = " & Format(dblMinAbsPhErr, "#.00")) + //Debug.WriteLine(" [Acquire2ToneLeaderSymbolSync] Ph1500 @ MinErr = " & Format(dblPhaseAtMinErr, "#.000")) + + //strDecodeCapture &= "Framing; iAtMinErr=" & intIatMinErr.ToString & ", Ptr=" & intMFSReadPtr.ToString & ", MinAbsPhErr=" & Format(dblMinAbsPhErr, "#.00") & ": " + intPhaseError = 0; + return TRUE; +} + +// Function to establish symbol sync +int EnvelopeCorrelator() +{ + // Compute the two symbol correlation with the Two tone leader template. + // slide the correlation one sample and repeat up to 240 steps + // keep the point of maximum or minimum correlation...and use this to identify the the symbol start. + + float dblCorMax = -1000000.0f; // Preset to excessive values + float dblCorMin = 1000000.0f; + int intJatMax = 0, intJatMin = 0; + float dblCorSum, dblCorProduct, dblCorMaxProduct = 0.0; + int i,j; + short int75HzFiltered[720]; + + if (intFilteredMixedSamplesLength < intMFSReadPtr + 720) + return -1; + + Filter75Hz(int75HzFiltered, TRUE, 720); // This filter appears to help reduce avg decode distance (10 frames) by about 14%-19% at WGN-5 May 3, 2015 + + for (j = 0; j < 360; j++) // Over 1.5 symbols + { + dblCorSum = 0; + for (i = 0; i < 240; i++) // over 1 50 baud symbol (may be able to reduce to 1 symbol) + { + dblCorProduct = int50BaudTwoToneLeaderTemplate[i] * int75HzFiltered[120 + i + j]; // note 120 accomdates filter delay of 120 samples + dblCorSum += dblCorProduct; + if (fabsf(dblCorProduct) > dblCorMaxProduct) + dblCorMaxProduct = fabsf(dblCorProduct); + } + + if (fabsf(dblCorSum) > dblCorMax) + { + dblCorMax = fabsf(dblCorSum); + intJatMax = j; + } + } + + if (AccumulateStats) + { + dblAvgCorMaxToMaxProduct = (dblAvgCorMaxToMaxProduct * intEnvelopeCors + (dblCorMax / dblCorMaxProduct)) / (intEnvelopeCors + 1); + intEnvelopeCors++; + } + +// if (dblCorMax > 40 * dblCorMaxProduct) + { + Debugprintf("EnvelopeCorrelator CorMax:MaxProd= %f J= %d", dblCorMax / dblCorMaxProduct, intJatMax); + return intJatMax; + } +// else +// return -1; +} + + +int EnvelopeCorrelatorNew() +{ + // Compute the two symbol correlation with the Two tone leader template. + // slide the correlation one sample and repeat up to 240 steps + // keep the point of maximum or minimum correlation...and use this to identify the the symbol start. + + float dblCorMax = -1000000.0f; // Preset to excessive values + float dblCorMin = 1000000.0f; + int intJatMax = 0, intJatMin = 0; + float dblCorSum, dblCorProduct, dblCorMaxProduct = 0.0; + int i,j; + short int75HzFiltered[960]; + + if (intFilteredMixedSamplesLength < intMFSReadPtr + 960) + return -1; + + Filter75Hz(int75HzFiltered, TRUE, 960); // This filter appears to help reduce avg decode distance (10 frames) by about 14%-19% at WGN-5 May 3, 2015 + + for (j = 360; j < 600; j++) // Over 2 symbols + { + dblCorSum = 0; + for (i = 0; i < 240; i++) // over 1 50 baud symbol (may be able to reduce to 1 symbol) + { + dblCorProduct = int50BaudTwoToneLeaderTemplate[i] * int75HzFiltered[120 + i + j]; // note 120 accomdates filter delay of 120 samples + dblCorSum += dblCorProduct; + if (fabsf(dblCorProduct) > dblCorMaxProduct) + dblCorMaxProduct = fabsf(dblCorProduct); + } + + if (fabsf(dblCorSum) > dblCorMax) + { + dblCorMax = fabsf(dblCorSum); + intJatMax = j; + } + } + + if (AccumulateStats) + { + dblAvgCorMaxToMaxProduct = (dblAvgCorMaxToMaxProduct * intEnvelopeCors + (dblCorMax / dblCorMaxProduct)) / (intEnvelopeCors + 1); + intEnvelopeCors++; + } + + if (dblCorMax > 40 * dblCorMaxProduct) + { + Debugprintf("EnvelopeCorrelator CorMax:MaxProd= %f J= %d", dblCorMax / dblCorMaxProduct, intJatMax); + return intJatMax; + } + + Debugprintf("EnvelopeCorrelator failed %d", dblCorMax / dblCorMaxProduct); + + return -1; +} + + +// Function to acquire the Frame Sync for all Frames + +BOOL AcquireFrameSyncRSB() +{ + // Two improvements could be incorporated into this function: + // 1) Provide symbol tracking until the frame sync is found (small corrections should be less than 1 sample per 4 symbols ~2000 ppm) + // 2) Ability to more accurately locate the symbol center (could be handled by symbol tracking 1) above. + + // This is for acquiring FSKFrameSync After Mixing Tones Mirrored around 1500 Hz. e.g. Reversed Sideband + // Frequency offset should be near 0 (normally within +/- 1 Hz) + // Locate the sync Symbol which has no phase change from the prior symbol (BPSK leader @ 1500 Hz) + + int intLocalPtr = intMFSReadPtr; + int intAvailableSymbols = (intFilteredMixedSamplesLength - intMFSReadPtr) / 240; + float dblPhaseSym1; //' phase of the first symbol + float dblPhaseSym2; //' phase of the second symbol + float dblPhaseSym3; //' phase of the third symbol + + float dblReal, dblImag; + float dblPhaseDiff12, dblPhaseDiff23; + + int i; + + if (intAvailableSymbols < 3) + return FALSE; // must have at least 360 samples to search + + // Calculate the Phase for the First symbol + + GoertzelRealImag(intFilteredMixedSamples, intLocalPtr, 240, 30, &dblReal, &dblImag); // Carrier at 1500 Hz nominal Positioning with no cyclic prefix + dblPhaseSym1 = atan2f(dblImag, dblReal); + intLocalPtr += 240; // advance one symbol + GoertzelRealImag(intFilteredMixedSamples, intLocalPtr, 240, 30, &dblReal, &dblImag); // Carrier at 1500 Hz nominal Positioning with no cyclic prefix + dblPhaseSym2 = atan2f(dblImag, dblReal); + intLocalPtr += 240; // advance one symbol + + for (i = 0; i <= intAvailableSymbols - 3; i++) + { + // Compute the phase of the next symbol + + GoertzelRealImag(intFilteredMixedSamples, intLocalPtr, 240, 30, &dblReal, &dblImag); // Carrier at 1500 Hz nominal Positioning with no cyclic prefix + dblPhaseSym3 = atan2f(dblImag, dblReal); + // Compute the phase differences between sym1-sym2, sym2-sym3 + dblPhaseDiff12 = dblPhaseSym1 - dblPhaseSym2; + if (dblPhaseDiff12 > M_PI) // bound phase diff to +/- Pi + dblPhaseDiff12 -= dbl2Pi; + else if (dblPhaseDiff12 < -M_PI) + dblPhaseDiff12 += dbl2Pi; + + dblPhaseDiff23 = dblPhaseSym2 - dblPhaseSym3; + if (dblPhaseDiff23 > M_PI) // bound phase diff to +/- Pi + dblPhaseDiff23 -= dbl2Pi; + else if (dblPhaseDiff23 < -M_PI) + dblPhaseDiff23 += dbl2Pi; + + if (fabsf(dblPhaseDiff12) > 0.6667f * M_PI && fabsf(dblPhaseDiff23) < 0.3333f * M_PI) // Tighten the margin to 60 degrees + { +// intPSKRefPhase = (short)dblPhaseSym3 * 1000; + + intLeaderRcvdMs = (int)ceil((intLocalPtr - 30) / 12); // 30 is to accomodate offset of inital pointer for filter length. + intMFSReadPtr = intLocalPtr + 240; // Position read pointer to start of the symbol following reference symbol + + if (AccumulateStats) + intFrameSyncs += 1; // accumulate tuning stats + + //strDecodeCapture &= "Sync; Phase1>2=" & Format(dblPhaseDiff12, "0.00") & " Phase2>3=" & Format(dblPhaseDiff23, "0.00") & ": " + + return TRUE; // pointer is pointing to first 4FSK data symbol. (first symbol of frame type) + } + else + { + dblPhaseSym1 = dblPhaseSym2; + dblPhaseSym2 = dblPhaseSym3; + intLocalPtr += 240; // advance one symbol + } + } + + intMFSReadPtr = intLocalPtr - 480; // back up 2 symbols for next attempt (Current Sym2 will become new Sym1) + return FALSE; +} + + + +// Function to acquire the Frame Sync for all Frames using exponential averaging +int AcquireFrameSyncRSBAvg() +{ + // This new routine uses exponential averaging on the ptr reference leader phases to minimize noise contribution + // Needs optimization of filter values and decision thresholds with actual simulator at low S:N and multipath. + + // This is for acquiring FSKFrameSync After Mixing Tones Mirrored around 1500 Hz. e.g. Reversed Sideband + // Frequency offset should be near 0 (normally within +/- 1 Hz) + // Locate the sync Symbol which has no phase change from the prior symbol (50 baud BPSK leader @ 1500 Hz) + + int intLocalPtr = intMFSReadPtr; + int intAvailableSymbols = (intFilteredMixedSamplesLength - intMFSReadPtr) / 240; + float dblPhaseSym1; //' phase of the first symbol + float dblPhaseSym2; //' phase of the second symbol + float dblPhaseSym3; //' phase of the third symbol + + float dblReal, dblImag; + float dblPhaseDiff12, dblPhaseDiff23; + + int i; + + if (intAvailableSymbols < 3) + return FALSE; // must have at least 360 samples to search + + // Calculate the Phase for the First symbol + + GoertzelRealImagHann120(intFilteredMixedSamples, intLocalPtr, 240, 30, &dblReal, &dblImag); // Carrier at 1500 Hz nominal Positioning with no cyclic prefix + dblPhaseSym1 = atan2f(dblImag, dblReal); + intLocalPtr += 240; // advance one symbol + GoertzelRealImagHann120(intFilteredMixedSamples, intLocalPtr, 240, 30, &dblReal, &dblImag); // Carrier at 1500 Hz nominal Positioning with no cyclic prefix + dblPhaseSym2 = atan2f(dblImag, dblReal); + intLocalPtr += 240; // advance one symbol + + for (i = 0; i <= intAvailableSymbols - 3; i++) + { + // Compute the phase of the next symbol + + GoertzelRealImagHann120(intFilteredMixedSamples, intLocalPtr, 240, 30, &dblReal, &dblImag); // Carrier at 1500 Hz nominal Positioning with no cyclic prefix + dblPhaseSym3 = atan2f(dblImag, dblReal); + // Compute the phase differences between sym1-sym2, sym2-sym3 + dblPhaseDiff12 = dblPhaseSym1 - dblPhaseSym2; + if (dblPhaseDiff12 > M_PI) // bound phase diff to +/- Pi + dblPhaseDiff12 -= dbl2Pi; + else if (dblPhaseDiff12 < -M_PI) + dblPhaseDiff12 += dbl2Pi; + + if (dblPhaseDiff1_2Avg > 9) + dblPhaseDiff1_2Avg = fabsf(dblPhaseDiff12); // initialize the difference average after a prior detect + else + dblPhaseDiff1_2Avg = 0.75 * dblPhaseDiff1_2Avg + 0.25 * fabsf(dblPhaseDiff12); // exponential average + + + dblPhaseDiff23 = dblPhaseSym2 - dblPhaseSym3; + if (dblPhaseDiff23 > M_PI) // bound phase diff to +/- Pi + dblPhaseDiff23 -= dbl2Pi; + else if (dblPhaseDiff23 < -M_PI) + dblPhaseDiff23 += dbl2Pi; + + + + if (fabsf(dblPhaseDiff1_2Avg ) > (0.83333 * M_PI) && fabsf(dblPhaseDiff23) < (0.25f * M_PI)) // Margin ~30 deg and 45 degrees + { + intLeaderRcvdMs = (int)ceil((intLocalPtr - 30) / 12); // 30 is to accomodate offset of inital pointer for filter length. + intMFSReadPtr = intLocalPtr + 240; // Position read pointer to start of the symbol following reference symbol + + if (AccumulateStats) + intFrameSyncs += 1; // accumulate tuning stats + + //strDecodeCapture &= "Sync; Phase1>2=" & Format(dblPhaseDiff12, "0.00") & " Phase2>3=" & Format(dblPhaseDiff23, "0.00") & ": " +// dttLastLeaderSync = Now; + dblPwrSNPower_dBPrior = -1000; // Reset the prior Leader power to small value to insure minimum of two symbol passes on next leader detect. + return TRUE; // pointer is pointing to first 4FSK data symbol. (first symbol of frame type) + } + // The following looks for phase errors (which should nomimally be Pi or 180 deg) and counts errors + // abandoning search on the second error, Then advancing the main intMFSReadPtr one symbol (240 samples) and returning to SearchingForLeader state. + + if (fabsf(dblPhaseDiff1_2Avg) < (0.6667 * M_PI) || fabsf(dblPhaseDiff23) < (0.6667 * M_PI)) // Margin 60 deg + { + intPhaseError += 1; + dblPhaseSym1 = dblPhaseSym2; + dblPhaseSym2 = dblPhaseSym3; + intLocalPtr += 240; // advance one symbol + +// if (intPhaseError > 1) // This bailout mechanism for sync failure is superior and doesn't make any assumptions about leader length +// { +// intMFSReadPtr += 240; // advance the MFSReadPointer one symbol and try to search for leader again. +// State = SearchingForLeader; +// return False; +// } + } + else + { + // keep searching available samples + + dblPhaseSym1 = dblPhaseSym2; + dblPhaseSym2 = dblPhaseSym3; + intLocalPtr += 240; // advance one symbol + } + } + + intMFSReadPtr = intLocalPtr - 480; // back up 2 symbols for next attempt (Current Sym2 will become new Sym1) + return FALSE; + +} +// Function to Demod FrameType4FSK + +BOOL DemodFrameType4FSK(int intPtr, short * intSamples, int * intToneMags) +{ + float dblReal, dblImag; + int i; + + if ((intFilteredMixedSamplesLength - intPtr) < 1920) // 8 symbols + return FALSE; + + intToneMagsLength = 8; + + for (i = 0; i < 8; i++) + { + GoertzelRealImagHann120(intSamples, intPtr, 240, 1350 / 50.0f, &dblReal, &dblImag); + intToneMags[4 * i] = (int)powf(dblReal, 2) + powf(dblImag, 2); + GoertzelRealImagHann120(intSamples, intPtr, 240, 1450 / 50.0f, &dblReal, &dblImag); + intToneMags[1 + 4 * i] = (int)powf(dblReal, 2) + powf(dblImag, 2); + GoertzelRealImagHann120(intSamples, intPtr, 240, 1550 / 50.0f, &dblReal, &dblImag); + intToneMags[2 + 4 * i] = (int)powf(dblReal, 2) + powf(dblImag, 2); + GoertzelRealImagHann120(intSamples, intPtr, 240, 1650 / 50.0f, &dblReal, &dblImag); + intToneMags[3 + 4 * i] = (int)powf(dblReal, 2) + powf(dblImag, 2); + intPtr += 240; + } + + return TRUE; +} + +// Function to compute the "distance" from a specific bytFrame Xored by bytID using 1 symbol parity + +float ComputeDecodeDistance(int intTonePtr, int * intToneMags, UCHAR bytFrameType, UCHAR bytID) +{ + // intTonePtr is the offset into the Frame type symbols. 0 for first Frame byte 16 = (4 x 4) for second frame byte + + float dblDistance = 0; + int int4ToneSum; + int intToneIndex; + UCHAR bytMask = 0x30; + int j, k; + + for (j = 0; j <= 3; j++) // over 4 symbols + { + int4ToneSum = 0; + for (k = 0; k <=3; k++) + { + int4ToneSum += intToneMags[intTonePtr + (4 * j) + k]; + } + if (int4ToneSum == 0) + int4ToneSum = 1; // protects against possible overflow + if (j < 3) + intToneIndex = ((bytFrameType ^ bytID) & bytMask) >> (4 - 2 * j); + else + intToneIndex = ComputeTypeParity(bytFrameType ^ bytID); + + dblDistance += 1.0f - ((1.0f * intToneMags[intTonePtr + (4 * j) + (3 - intToneIndex)]) / (1.0f * int4ToneSum)); + bytMask = bytMask >> 2; + } + + dblDistance = dblDistance / 4; // normalize back to 0 to 1 range + return dblDistance; +} + +// A function to check the parity symbol used in the frame type decoding + +BOOL CheckTypeParity(UCHAR bytFrameType) +{ + // Returns True if Parity OK + + UCHAR bytMask = 0x30; // Look at only 6 bits of data (values only 0 to 63) + UCHAR bytParitySum = 3; + UCHAR bytSym = 0; + int k; + + for (k = 0; k < 3; k++) + { + bytSym = (bytMask & bytFrameType) >> (2 * (2 - k)); + bytParitySum = bytParitySum ^ bytSym; + bytMask = bytMask >> 2; + } + + return bytParitySum == ((bytFrameType & 0x0C0) >> 6); + } + + +// Function to check Parity of frame type bytes + +UCHAR GetFrameTypeByte(int intTonePtr, int * intToneMags) +{ + // Demodulate the byte pointed to postion of tone PTR and return it + + UCHAR bytData = 0, bytParity, bytSym; + int intIndex = intTonePtr; + int j; + + for (j = 0; j < 4; j++) + { + // for each 4FSK symbol (2 bits) in a byte + + if (intToneMags[intIndex] > intToneMags[intIndex + 1] && intToneMags[intIndex] > intToneMags[intIndex + 2] && intToneMags[intIndex] > intToneMags[intIndex + 3]) + bytSym = 3; + else if (intToneMags[intIndex + 1] > intToneMags[intIndex] && intToneMags[intIndex + 1] > intToneMags[intIndex + 2] && intToneMags[intIndex + 1] > intToneMags[intIndex + 3]) + bytSym = 2; + else if (intToneMags[intIndex + 2] > intToneMags[intIndex] && intToneMags[intIndex + 2] > intToneMags[intIndex + 1] && intToneMags[intIndex + 2] > intToneMags[intIndex + 3]) + bytSym = 1; + else + bytSym = 0; + + if (j < 3) + bytData = (bytData << 2) + bytSym; + else + bytParity = bytSym << 6; + + intIndex += 4; + } + return bytData | bytParity; +} + + +BOOL CheckFrameTypeParity(int intTonePtr, int * intToneMags) +{ + // Demodulate the byte pointed to postion of tone PTR and check Parity Return True if OK + + UCHAR bytData = GetFrameTypeByte(intTonePtr, intToneMags); + + return CheckTypeParity(bytData); +} +// Function to compute the frame type by selecting the minimal distance from all valid frame types. + +int MinimalDistanceFrameType(int * intToneMags, UCHAR bytSessionID) +{ + float dblMinDistance1 = 5; // minimal distance for the first byte initialize to large value + float dblMinDistance2 = 5; // minimal distance for the second byte initialize to large value + float dblMinDistance3 = 5; // minimal distance for the second byte under exceptional cases initialize to large value + int intIatMinDistance1, intIatMinDistance2, intIatMinDistance3; + float dblDistance1, dblDistance2, dblDistance3; + int i; + + strDecodeCapture[0] = 0; + + if (ProtocolState == ISS) + { + bytValidFrameTypes = bytValidFrameTypesISS; + bytValidFrameTypesLength = bytValidFrameTypesLengthISS; + } + else + { + bytValidFrameTypes = bytValidFrameTypesALL; + bytValidFrameTypesLength = bytValidFrameTypesLengthALL; + } + + // Search through all the valid frame types finding the minimal distance + // This looks like a lot of computation but measured < 1 ms for 135 iterations....RM 11/1/2016 + + for (i = 0; i < bytValidFrameTypesLength; i++) + { + dblDistance1 = ComputeDecodeDistance(0, intToneMags, bytValidFrameTypes[i], 0); + dblDistance2 = ComputeDecodeDistance(16, intToneMags, bytValidFrameTypes[i], bytSessionID); + + if (blnPending) + dblDistance3 = ComputeDecodeDistance(16, intToneMags, bytValidFrameTypes[i], 0x3F); + else + dblDistance3 = ComputeDecodeDistance(16, intToneMags, bytValidFrameTypes[i], bytLastARQSessionID); + + if (dblDistance1 < dblMinDistance1) + { + dblMinDistance1 = dblDistance1; + intIatMinDistance1 = bytValidFrameTypes[i]; + } + if (dblDistance2 < dblMinDistance2) + { + dblMinDistance2 = dblDistance2; + intIatMinDistance2 = bytValidFrameTypes[i]; + } + if (dblDistance3 < dblMinDistance3) + { + dblMinDistance3 = dblDistance3; + intIatMinDistance3 = bytValidFrameTypes[i]; + } + } + + Debugprintf("Frame Decode type %x %x %x Dist %.2f %.2f %.2f Sess %x pend %d conn %d lastsess %d", + intIatMinDistance1, intIatMinDistance2, intIatMinDistance3, + dblMinDistance1, dblMinDistance2, dblMinDistance3, + bytSessionID, blnPending, blnARQConnected, bytLastARQSessionID); + + if (bytSessionID == 0x3F) // ' we are in a FEC QSO, monitoring an ARQ session or have not yet reached the ARQ Pending or Connected status + { + if (intIatMinDistance1 == intIatMinDistance2 && ((dblMinDistance1 < 0.3) || (dblMinDistance2 < 0.3))) + { + sprintf(strDecodeCapture, "%s MD Decode;2 ID=H%X, Type=H%X:%s, D1= %.2f, D2= %.2f", + strDecodeCapture, bytSessionID, intIatMinDistance1, Name(intIatMinDistance1), dblMinDistance1, dblMinDistance2); + Debugprintf("[Frame Type Decode OK ] %s", strDecodeCapture); + dblOffsetLastGoodDecode = dblOffsetHz; + + return intIatMinDistance1; + } + + + if ((dblMinDistance1 < 0.3) && CheckFrameTypeParity(0, intToneMags) && IsDataFrame(intIatMinDistance1) ) // this would handle the case of monitoring an ARQ connection where the SessionID is not 0x3F + { + sprintf(strDecodeCapture, "%s MD Decode;3 ID=H%X, Type=H%X:%s, D1= %.2f, D2= %.2f", + strDecodeCapture, bytSessionID, intIatMinDistance1, Name(intIatMinDistance1), dblMinDistance1, dblMinDistance2); + Debugprintf("[Frame Type Decode OK ] %s", strDecodeCapture); + + return intIatMinDistance1; + } + + if ((dblMinDistance2 < 0.3) && CheckFrameTypeParity(16, intToneMags) && IsDataFrame(intIatMinDistance2)) // this would handle the case of monitoring an FEC transmission that failed above when the session ID is = 03F + { + sprintf(strDecodeCapture, "%s MD Decode;4 ID=H%X, Type=H%X:%s, D1= %.2f, D2= %.2f", + strDecodeCapture, bytSessionID, intIatMinDistance1, Name(intIatMinDistance2), dblMinDistance1, dblMinDistance2); + Debugprintf("[Frame Type Decode OK ] %s", strDecodeCapture); + + return intIatMinDistance2; + } + return -1; // indicates poor quality decode so don't use + + } + + sprintf(strDecodeCapture, "%s MD Decode;12 Type1=H%X: Type2=H%X: , D1= %.2f, D2= %.2f", + strDecodeCapture, intIatMinDistance1 , intIatMinDistance2, dblMinDistance1, dblMinDistance2); + Debugprintf("[Frame Type Decode Fail] %s", strDecodeCapture); + return -1; // indicates poor quality decode so don't use +} + + +// Function to acquire the 4FSK frame type + +int Acquire4FSKFrameType() +{ + // intMFSReadPtr is pointing to start of first symbol of Frame Type (total of 8 4FSK symbols in frame type (2 bytes) + 1 parity symbol per byte + // returns -1 if minimal distance decoding is below threshold (low likelyhood of being correct) + // returns -2 if insufficient samples + // Else returns frame type 0-255 + + int NewType = 0; + char Offset[32]; + + if ((intFilteredMixedSamplesLength - intMFSReadPtr) < (240 * 8)) + return -2; // Check for 8 available 4FSK Symbols + + + if (!DemodFrameType4FSK(intMFSReadPtr, intFilteredMixedSamples, &intToneMags[0][0])) + { + Update4FSKConstellation(&intToneMags[0][0], &intLastRcvdFrameQuality); + intMFSReadPtr += (240 * 8); + return -1; + } + + intRmtLeaderMeasure = (Now - dttStartRmtLeaderMeasure); + dttLastGoodFrameTypeDecode = Now; + + // Now do check received Tone array for testing minimum distance decoder + + if (blnPending) // If we have a pending connection (btween the IRS first decode of ConReq until it receives a ConAck from the iSS) + NewType = MinimalDistanceFrameType(&intToneMags[0][0], bytPendingSessionID); // The pending session ID will become the session ID once connected) + else if (blnARQConnected) // If we are connected then just use the stcConnection.bytSessionID + NewType = MinimalDistanceFrameType(&intToneMags[0][0], bytSessionID); + else // not connected and not pending so use &FF (FEC or ARQ unconnected session ID + NewType = MinimalDistanceFrameType(&intToneMags[0][0], 0x3F); + + + sprintf(Offset, "Offset %5.1f", dblOffsetHz); + SendtoGUI('O', Offset, strlen(Offset)); + + if (NewType >= 0 && IsShortControlFrame(NewType)) // update the constellation if a short frame (no data to follow) + Update4FSKConstellation(&intToneMags[0][0], &intLastRcvdFrameQuality); + + if (AccumulateStats) + if (NewType >= 0) + intGoodFSKFrameTypes++; + else + intFailedFSKFrameTypes++; + + intMFSReadPtr += (240 * 8); // advance to read pointer to the next symbol (if there is one) + + return NewType; +} + + +// Demodulate Functions. These are called repeatedly as samples arrive +// and buld a frame in static array bytFrameData + +// Function to demodulate one carrier for all low baud rate 4FSK frame types + +// Is called repeatedly to decode multitone modes +int Corrections = 0; + +BOOL Demod1Car4FSK() +{ + int Start = 0; + + // We can't wait for the full frame as we don't have enough ram, so + // we do one character at a time, until we run out or end of frame + + // Only continue if we have more than intSampPerSym * 4 chars + + while (State == AcquireFrame) + { + if (intFilteredMixedSamplesLength < ((intSampPerSym * 4) + 20)) // allow for correcrions + { + // Move any unprocessessed data down buffer + + // (while checking process - will use cyclic buffer eventually + +// Debugprintf("Corrections %d", Corrections); + + // If corrections is non-zero, we have to adjust + // number left + + intFilteredMixedSamplesLength -= Corrections; + + if (intFilteredMixedSamplesLength < 0) + Debugprintf("Corrupt intFilteredMixedSamplesLength"); + + Corrections = 0; + + if (intFilteredMixedSamplesLength > 0) + memmove(intFilteredMixedSamples, + &intFilteredMixedSamples[Start], intFilteredMixedSamplesLength * 2); + + return FALSE; + } + + // If this is a multicarrier mode, we must call the + // decode char routing for each carrier + + switch (intNumCar) + { + case 1: + + intCenterFreq = 1500; + if (CarrierOk[0] == FALSE) // Don't redo if already decoded + Demod1Car4FSKChar(Start, bytFrameData[0], 0); + break; + + case 2: + + intCenterFreq = 1750; + if (CarrierOk[0] == FALSE) + Demod1Car4FSKChar(Start, bytFrameData[0], 0); + intCenterFreq = 1250; + if (CarrierOk[1] == FALSE) + Demod1Car4FSKChar(Start, bytFrameData[1], 1); + break; + +/* case 4: + + intCenterFreq = 2250; + if (CarrierOk[0] == FALSE) + Demod1Car4FSKChar(Start, bytFrameData1, 0); + intCenterFreq = 1750; + if (CarrierOk[1] == FALSE) + Demod1Car4FSKChar(Start, bytFrameData2, 1); + intCenterFreq = 1250; + if (CarrierOk[2] == FALSE) + Demod1Car4FSKChar(Start, bytFrameData3, 2); + intCenterFreq = 750; + if (CarrierOk[3] == FALSE) + Demod1Car4FSKChar(Start, bytFrameData4, 3); + break; +*/ + } + + charIndex++; // Index into received chars + SymbolsLeft--; // number still to decode + Start += intSampPerSym * 4; // 4 FSK bit pairs per byte + intFilteredMixedSamplesLength -= intSampPerSym * 4; + + if (intFilteredMixedSamplesLength < 0) + Debugprintf("Corrupt intFilteredMixedSamplesLength"); + + if (SymbolsLeft == 0) + { + //- prepare for next + + // If variable length packet frame header we only have header - leave rx running + + if (intFrameType == PktFrameHeader) + { + State = SearchingForLeader; + + // Save any unused samples + + if (intFilteredMixedSamplesLength > 0 && Start > 0) + memmove(intFilteredMixedSamples, + &intFilteredMixedSamples[Start], intFilteredMixedSamplesLength * 2); + + return TRUE; + } + + DecodeCompleteTime = Now; + DiscardOldSamples(); + ClearAllMixedSamples(); + State = SearchingForLeader; + } + } + return TRUE; +} + +// Function to demodulate one carrier for all low baud rate 4FSK frame types + +void Demod1Car4FSKChar(int Start, UCHAR * Decoded, int Carrier) +{ + // Converts intSamples to an array of bytes demodulating the 4FSK symbols with center freq intCenterFreq + // intPtr should be pointing to the approximate start of the first data symbol + // Updates bytData() with demodulated bytes + // Updates bytMinSymQuality with the minimum (range is 25 to 100) symbol making up each byte. + + float dblReal, dblImag; + float dblSearchFreq; + float dblMagSum = 0; + float dblMag[4]; // The magnitude for each of the 4FSK frequency bins + UCHAR bytSym; + static UCHAR bytSymHistory[3]; + int j; + UCHAR bytData = 0; + + int * intToneMagsptr = &intToneMags[Carrier][intToneMagsIndex[Carrier]]; + + intToneMagsIndex[Carrier] += 16; + + // ReDim intToneMags(4 * intNumOfSymbols - 1) + // ReDim bytData(intNumOfSymbols \ 4 - 1) + + if (intBaud == 100) + dblSearchFreq = intCenterFreq + (1.5f * intBaud); // the highest freq (equiv to lowest sent freq because of sideband reversal) + else + dblSearchFreq = intCenterFreq + (3.0f * intBaud); // the highest freq (equiv to lowest sent freq because of sideband reversal) + + + // Do one symbol + + for (j = 0; j < 4; j++) // for each 4FSK symbol (2 bits) in a byte + { + dblMagSum = 0; + if (intBaud == 100) + { + GoertzelRealImag(intFilteredMixedSamples, Start, intSampPerSym, dblSearchFreq / intBaud, &dblReal, &dblImag); + dblMag[0] = powf(dblReal,2) + powf(dblImag, 2); + dblMagSum += dblMag[0]; + + GoertzelRealImag(intFilteredMixedSamples, Start, intSampPerSym, (dblSearchFreq - intBaud) / intBaud, &dblReal, &dblImag); + dblMag[1] = powf(dblReal,2) + powf(dblImag, 2); + dblMagSum += dblMag[1]; + + GoertzelRealImag(intFilteredMixedSamples, Start, intSampPerSym, (dblSearchFreq - 2 * intBaud) / intBaud, &dblReal, &dblImag); + dblMag[2] = powf(dblReal,2) + powf(dblImag, 2); + dblMagSum += dblMag[2]; + + GoertzelRealImag(intFilteredMixedSamples, Start, intSampPerSym, (dblSearchFreq - 3 * intBaud) / intBaud, &dblReal,& dblImag); + dblMag[3] = powf(dblReal,2) + powf(dblImag, 2); + dblMagSum += dblMag[3]; + } + else + { + dblMagSum = 0; + GoertzelRealImagHann120(intFilteredMixedSamples, Start, intSampPerSym, dblSearchFreq / intBaud, &dblReal, &dblImag); + dblMag[0] = powf(dblReal,2) + powf(dblImag, 2); + dblMagSum += dblMag[0]; + + GoertzelRealImagHann120(intFilteredMixedSamples, Start, intSampPerSym, (dblSearchFreq - 2 * intBaud) / intBaud, &dblReal, &dblImag); + dblMag[1] = powf(dblReal,2) + powf(dblImag, 2); + dblMagSum += dblMag[1]; + + GoertzelRealImagHann120(intFilteredMixedSamples, Start, intSampPerSym, (dblSearchFreq - 4 * intBaud) / intBaud, &dblReal, &dblImag); + dblMag[2] = powf(dblReal,2) + powf(dblImag, 2); + dblMagSum += dblMag[2]; + + GoertzelRealImagHann120(intFilteredMixedSamples, Start, intSampPerSym, (dblSearchFreq - 6 * intBaud) / intBaud, &dblReal,& dblImag); + dblMag[3] = powf(dblReal,2) + powf(dblImag, 2); + dblMagSum += dblMag[3]; + + } + + if (dblMag[0] > dblMag[1] && dblMag[0] > dblMag[2] && dblMag[0] > dblMag[3]) + bytSym = 0; + else if (dblMag[1] > dblMag[0] && dblMag[1] > dblMag[2] && dblMag[1] > dblMag[3]) + bytSym = 1; + else if (dblMag[2] > dblMag[0] && dblMag[2] > dblMag[1] && dblMag[2] > dblMag[3]) + bytSym = 2; + else + bytSym = 3; + + bytData = (bytData << 2) + bytSym; + + // !!!!!!! this needs attention !!!!!!!! + + *intToneMagsptr++ = dblMag[0]; + *intToneMagsptr++ = dblMag[1]; + *intToneMagsptr++ = dblMag[2]; + *intToneMagsptr++ = dblMag[3]; + bytSymHistory[0] = bytSymHistory[1]; + bytSymHistory[1] = bytSymHistory[2]; + bytSymHistory[2] = bytSym; + +// if ((bytSymHistory[0] != bytSymHistory[1]) && (bytSymHistory[1] != bytSymHistory[2])) + { + // only track when adjacent symbols are different (statistically about 56% of the time) + // this should allow tracking over 2000 ppm sampling rate error +// if (Start > intSampPerSym + 2) +// Track1Car4FSK(intFilteredMixedSamples, &Start, intSampPerSym, dblSearchFreq, intBaud, bytSymHistory); + } + Start += intSampPerSym; // advance the pointer one symbol + } + + if (AccumulateStats) + intFSKSymbolCnt += 4; + + Decoded[charIndex] = bytData; + return; +} + +extern int intBW; + +// Function to Demodulate Frame based on frame type +// Will be called repeatedly as new samples arrive + +void DemodulateFrame(int intFrameType) +{ + // Dim stcStatus As Status = Nothing + + int intConstellationQuality = 0; + + // ReDim bytData(-1) + + strRcvFrameTag[0] = 0; + + + switch (intFrameType) + { + case ConReq200: + case ConReq500: + case ConReq2500: + case OConReq500: + case OConReq2500: + case PING: + case IDFRAME: + case PINGACK: + case CQ_de: + case PktFrameHeader: // Experimental Variable Length Frame + case OFDMACK: + + Demod1Car4FSK(); + return; + + case PktFrameData: // Experimantal Variable Length Frame + + if (strcmp(strMod, "4FSK") == 0) + Demod1Car4FSK(); + else if (strcmp(strMod, "16QAM") == 0) + DemodQAM(); + else + DemodPSK(); + return; + } + + switch (intFrameType & 0xFE) // Others are even/odd data frames + { + case D4FSK_500_50_E: + case D4FSK_1000_50_E: + + Demod1Car4FSK(); + break; + + case D4PSK_200_50_E: + case D4PSK_200_100_E: + case D4PSK_500_50_E: + case D4PSK_500_100_E: + case D4PSKR_2500_50_E: + case D4PSK_2500_50_E: + case D4PSK_2500_100_E: + + DemodPSK(); + break; + + case D16QAM_200_100_E: + case D16QAMR_500_100_E: + case D16QAM_500_100_E: + case D16QAMR_2500_100_E: + case D16QAM_2500_100_E: + + DemodQAM(); + break; + + case DOFDM_200_55_E: + case DOFDM_500_55_E: + case DOFDM_2500_55_E: + + DemodOFDM(); + break; + + default: + + Debugprintf("Unsupported frame type %x", intFrameType); + DiscardOldSamples(); + ClearAllMixedSamples(); + State = SearchingForLeader; + + intFilteredMixedSamplesLength = 0; // Testing + } +} + + +int intSNdB = 0, intQuality = 0; + + +BOOL DecodeFrame(int xxx, UCHAR * bytData) +{ + BOOL blnDecodeOK = FALSE; + char strCallerCallsign[10] = ""; + char strTargetCallsign[10] = ""; + char strIDCallSign[11] = ""; + char strGridSQ[20] = ""; + char Reply[80]; + + strRcvFrameTag[0] = 0; + + //DataACK/NAK and short control frames + + if (IsShortControlFrame(intFrameType)) // Short Control Frames + { + blnDecodeOK = TRUE; + DrawRXFrame(1, Name(intFrameType)); + + goto returnframe; + } + + totalRSErrors = 0; + + if (IsDataFrame(intFrameType)) + PrintCarrierFlags(); + + + switch (intFrameType) + { + + case PktFrameHeader: + { + // Variable Length Packet Frame Header + // 6 bits Type 10 Bits Len + + int Len; + int pktNumCar; + int pktDataLen; + int pktRSLen; + + frameLen = CorrectRawDataWithRS(&bytFrameData[0][0], bytData, intDataLen, intRSLen, intFrameType, 0); + + if (CarrierOk[0]) + { + pktRXMode = bytFrameData[0][1] >> 2; + pktNumCar = pktCarriers[pktRXMode]; + + Len = ((bytFrameData[0][1] & 0x3) << 8) | bytFrameData[0][2]; + } + // Now only using one carrier + + // else if (CarrierOk[1]) + // { + // pktRXMode = bytFrameData2[1] >> 5; + // pktNumCar = ((bytFrameData2[1] & 0x1c) >> 2) + 1; + // Len = ((bytFrameData2[1] & 0x3) << 8) | bytFrameData2[2]; + // } + else + { + // Cant decode + + DiscardOldSamples(); + ClearAllMixedSamples(); + break; + } + + strcpy(strMod, &pktMod[pktRXMode][0]); + + // Reset to receive rest of frame + + pktDataLen = (Len + (pktNumCar - 1)) / pktNumCar; // Round up + + // This must match the encode settings + + pktRSLen = pktDataLen >> 2; // Try 25% for now + if (pktRSLen & 1) + pktRSLen++; // Odd RS bytes no use + + if (pktRSLen < 4) + pktRSLen = 4; // At least 4 + + SymbolsLeft = pktDataLen + pktRSLen + 3; // Data has crc + length byte + State = AcquireFrame; + intFrameType = PktFrameData; + CarrierOk[1] = CarrierOk[0] = 0; + charIndex = 0; + frameLen = 0; + intPhasesLen = 0; + memset(intToneMagsIndex, 0, sizeof(intToneMagsIndex)); + intDataLen = pktDataLen; + intRSLen = pktRSLen; + intNumCar = pktNumCar; + PSKInitDone = 0; + + Debugprintf("Pkt Frame Header Type %s Len %d", strMod, Len); + strlop(strMod, '/'); + blnDecodeOK = TRUE; + + return 0; + } + + + case PktFrameData: + { + if (pktFSK[pktRXMode]) + { + // Need to Check RS + + frameLen = CorrectRawDataWithRS(&bytFrameData[0][0], bytData, intDataLen, intRSLen, intFrameType, 0); + if (intNumCar > 1) + frameLen += CorrectRawDataWithRS(&bytFrameData[1][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 1); + + if (intNumCar > 2) + { + frameLen += CorrectRawDataWithRS(&bytFrameData[2][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 2); + frameLen += CorrectRawDataWithRS(&bytFrameData[3][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 3); + } + } + + if (memcmp(CarrierOk, Good, intNumCar) == 0) + { + blnDecodeOK = TRUE; + + // Packet Data - if KISS interface ias active + // Pass to Host as KISS frame, else pass to + // Session code + + // Data in bytData len in frameLen + + ProcessPktFrame(0, bytData, frameLen); +// else +// L2Routine(bytData, frameLen, intLastRcvdFrameQuality, totalRSErrors, intNumCar, pktRXMode); + + } + break; + } + + + default: + + Debugprintf("Unrecognised frame type"); + + } + + if (blnDecodeOK) + { + Debugprintf("[DecodeFrame] Frame: %s Decode PASS, Constellation Quality= %d", Name(intFrameType), intLastRcvdFrameQuality); +#ifdef PLOTCONSTELLATION + if (intFrameType >= 0x30 && intFrameType <= 0x38) + DrawDecode(lastGoodID); // ID or CONREQ + else + DrawDecode("PASS"); + updateDisplay(); +#endif + } + + else + { + Debugprintf("[DecodeFrame] Frame: %s Decode FAIL, Constellation Quality= %d", Name(intFrameType), intLastRcvdFrameQuality); +#ifdef PLOTCONSTELLATION + DrawDecode("FAIL"); + updateDisplay(); +#endif + } + + +returnframe: + + if (blnDecodeOK && IsDataFrame(intFrameType)) + bytLastReceivedDataFrameType = intFrameType; + +// if (DebugLog) +// if (blnDecodeOK) +// Debugprintf("[DecodeFrame] Frame: %s Decode PASS, Constellation Quality= %d", Name(intFrameType), intLastRcvdFrameQuality); +// else +// Debugprintf("[DecodeFrame] Frame: %s Decode FAIL, Constellation Quality= %d", Name(intFrameType), intLastRcvdFrameQuality); + + return blnDecodeOK; +} + +// Subroutine to update the 4FSK Constellation + +void drawFastVLine(int x0, int y0, int length, int color); +void drawFastHLine(int x0, int y0, int length, int color); + +void Update4FSKConstellation(int * intToneMags, int * intQuality) +{ + // Subroutine to update bmpConstellation plot for 4FSK modes... + + int intToneSum = 0; + int intMagMax = 0; + float dblPi4 = 0.25 * M_PI; + float dblDistanceSum = 0; + int intRad = 0; + int i, x, y; + int yCenter = 0; + int xCenter = 0; + +#ifdef PLOTCONSTELLATION + + int clrPixel; + int yCenter = (ConstellationHeight)/ 2; + int xCenter = (ConstellationWidth) / 2; + + clearDisplay(); +#endif + + for (i = 0; i < intToneMagsLength; i += 4) // for the number of symbols represented by intToneMags + { + intToneSum = intToneMags[i] + intToneMags[i + 1] + intToneMags[i + 2] + intToneMags[i + 3]; + + if (intToneMags[i] > intToneMags[i + 1] && intToneMags[i] > intToneMags[i + 2] && intToneMags[i] > intToneMags[i + 3]) + { + if (intToneSum > 0) + intRad = max(5, 42 - 80 * (intToneMags[i + 1] + intToneMags[i + 2] + intToneMags[i + 3]) / intToneSum); + + dblDistanceSum += (42 - intRad); + intRad = (intRad * PLOTRADIUS) / 50; // rescale for OLED (50 instead of 42 as we rotate constellation 35 degrees + x = xCenter + intRad; + y = yCenter + intRad; + } + else if (intToneMags[i + 1] > intToneMags[i] && intToneMags[i + 1] > intToneMags[i + 2] && intToneMags[i + 1] > intToneMags[i + 3]) + { + if (intToneSum > 0) + intRad = max(5, 42 - 80 * (intToneMags[i] + intToneMags[i + 2] + intToneMags[i + 3]) / intToneSum); + + dblDistanceSum += (42 - intRad); + intRad = (intRad * PLOTRADIUS) / 50; // rescale for OLED (50 instead of 42 as we rotate constellation 35 degrees + x = xCenter + intRad; + y = yCenter - intRad; + } + else if (intToneMags[i + 2] > intToneMags[i] && intToneMags[i + 2] > intToneMags[i + 1] && intToneMags[i + 2] > intToneMags[i + 3]) + { + if (intToneSum > 0) + intRad = max(5, 42 - 80 * (intToneMags[i + 1] + intToneMags[i] + intToneMags[i + 3]) / intToneSum); + + dblDistanceSum += (42 - intRad); + intRad = (intRad * PLOTRADIUS) / 50; // rescale for OLED (50 instead of 42 as we rotate constellation 35 degrees + x = xCenter - intRad; + y = yCenter - intRad; + } + else if (intToneSum > 0) + { + intRad = max(5, 42 - 80 * (intToneMags[i + 1] + intToneMags[i + 2] + intToneMags[i]) / intToneSum); + + dblDistanceSum += (42 - intRad); + intRad = (intRad * PLOTRADIUS) / 50; // rescale for OLED (50 instead of 42 as we rotate constellation 35 degrees + x = xCenter - intRad; + y = yCenter + intRad; + } + +#ifdef PLOTCONSTELLATION + if (intRad < 15) + clrPixel = Tomato; + else if (intRad < 30) + clrPixel = Gold; + else + clrPixel = Lime; + + mySetPixel(x, y, clrPixel); +#endif + + } + + *intQuality = 100 - (2.7f * (dblDistanceSum / (intToneMagsLength / 4))); // ' factor 2.7 emperically chosen for calibration (Qual range 25 to 100) + + if (*intQuality < 0) + *intQuality = 0; + else if (*intQuality > 100) + *intQuality = 100; + + if (AccumulateStats) + { + int4FSKQualityCnts += 1; + int4FSKQuality += *intQuality; + } + +#ifdef PLOTCONSTELLATION + DrawAxes(*intQuality, shortName(intFrameType), strMod); +#endif + + return; +} + + + +// Subroutine to update the 16FSK constallation + +void Update16FSKConstellation(int * intToneMags, int * intQuality) +{ + // Subroutine to update bmpConstellation plot for 16FSK modes... + + + int intToneSum = 0; + float intMagMax = 0; + float dblDistanceSum = 0; + float dblPlotRotation = 0; +// Dim stcStatus As Status + int intRad; +// Dim clrPixel As System.Drawing.Color + int intJatMaxMag; + int i, j; + +#ifdef PLOTCONSTELLATION + + float dblRad; + float dblAng; + int x, y,clrPixel; + int yCenter = (ConstellationHeight - 2)/ 2; + int xCenter = (ConstellationWidth - 2) / 2; + + clearDisplay(); +#endif + + + for (i = 0; i< intToneMagsLength; i += 16) // for the number of symbols represented by intToneMags + { + intToneSum = 0; + intMagMax = 0; + + for (j = 0; j < 16; j++) + { + if (intToneMags[i + j] > intMagMax) + { + intMagMax = intToneMags[i + j]; + intJatMaxMag = j; + } + intToneSum += intToneMags[i + j]; + } + intRad = max(5, 42 - 40 * (intToneSum - intMagMax) / intToneSum); + dblDistanceSum += (43 - intRad); + +#ifdef PLOTCONSTELLATION + if (intRad < 15) + clrPixel = Tomato; + else if (intRad < 30) + clrPixel = Gold; + else + clrPixel = Lime; + + // plot the symbols rotated to avoid the axis + + intRad = (intRad * PLOTRADIUS) /42; // rescale for OLED + dblAng = M_PI / 16.0f + (intJatMaxMag * M_PI / 8); + + x = xCenter + intRad * cosf(dblAng); + y = yCenter + intRad * sinf(dblAng); + mySetPixel(x, y, clrPixel); +#endif + + } + + *intQuality = max(0, (100 - 2.2 * (dblDistanceSum / (intToneMagsLength / 16)))); // factor 2.2 emperically chosen for calibration (Qual range 25 to 100) +// *intQuality = max(0, (100 - 1.0 * (dblDistanceSum / (intToneMagsLength / 16)))); // factor 2.2 emperically chosen for calibration (Qual range 25 to 100) + + if(AccumulateStats) + { + int16FSKQualityCnts++; + int16FSKQuality += *intQuality; + } +#ifdef PLOTCONSTELLATION + DrawAxes(*intQuality, shortName(intFrameType), strMod); +#endif +} + +// Subroutine to udpate the 8FSK Constellation + +void Update8FSKConstellation(int * intToneMags, int * intQuality) +{ + // Subroutine to update bmpConstellation plot for 8FSK modes... + + int intToneSum = 0; + int intMagMax = 0; + float dblPi4 = 0.25 * M_PI; + float dblDistanceSum = 0; + int intRad = 0; + int i, j, intJatMaxMag; + +#ifdef PLOTCONSTELLATION + + float dblAng; + int yCenter = (ConstellationHeight - 2)/ 2; + int xCenter = (ConstellationWidth - 2) / 2; + unsigned short clrPixel = WHITE; + unsigned short x, y; + + clearDisplay(); +#endif + + for (i = 0; i < intToneMagsLength; i += 8) // for the number of symbols represented by intToneMags + { + intToneSum = 0; + intMagMax = 0; + + for (j = 0; j < 8; j++) + { + if (intToneMags[i + j] > intMagMax) + { + intMagMax = intToneMags[i + j]; + intJatMaxMag = j; + } + intToneSum += intToneMags[i + j]; + } + + intRad = max(5, 42 - 40 * (intToneSum - intMagMax) / intToneSum); + dblDistanceSum += (43 - intRad); + +#ifdef PLOTCONSTELLATION + if (intRad < 15) + clrPixel = Tomato; + else if (intRad < 30) + clrPixel = Gold; + else + clrPixel = Lime; + + // plot the symbols rotated to avoid the axis + + intRad = (intRad * PLOTRADIUS) /42; // rescale for OLED + + dblAng = M_PI / 9.0f + (intJatMaxMag * M_PI / 4); + + x = xCenter + intRad * cosf(dblAng); + y = yCenter + intRad * sinf(dblAng); + mySetPixel(x, y, clrPixel); +#endif + } + + *intQuality = max(0, (100 - 2.0 * (dblDistanceSum / (intToneMagsLength / 8)))); // factor 2.0 emperically chosen for calibration (Qual range 25 to 100) + + if(AccumulateStats) + { + int8FSKQualityCnts++; + int8FSKQuality += *intQuality; + } +#ifdef PLOTCONSTELLATION + DrawAxes(*intQuality, shortName(intFrameType), strMod); +#endif + return; +} + + + +// Subroutine to Update the PhaseConstellation + +int UpdatePhaseConstellation(short * intPhases, short * intMag, int intPSKPhase, BOOL blnQAM, BOOL OFDM) +{ + // 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; + int intPSKIndex; + float intP = 0; + float dblRad = 0; + float dblAvgRad = 0; + float intMagMax = 0; + float dblPi4 = 0.25 * M_PI; + float dbPhaseStep; + float dblRadError = 0; + float dblPlotRotation = 0; + int intRadInner = 0, intRadOuter = 0; + float dblAvgRadOuter = 0, dblAvgRadInner = 0, dblRadErrorInner = 0, dblRadErrorOuter = 0; + + int i,j, k, intQuality; + +#ifdef PLOTCONSTELLATION + + int intX, intY; + int yCenter = (ConstellationHeight - 2)/ 2; + int xCenter = (ConstellationWidth - 2) / 2; + + unsigned short clrPixel = WHITE; + + clearDisplay(); +#endif + + if (intPSKPhase == 4) + intPSKIndex = 0; + else + intPSKIndex = 1; + + if (blnQAM) + { + intPSKPhase = 8; + intPSKIndex = 1; + dbPhaseStep = 2 * M_PI / intPSKPhase; + for (j = 1; j < intPhasesLen; j++) // skip the magnitude of the reference in calculation + { + intMagMax = max(intMagMax, intMag[j]); // find the max magnitude to auto scale + } + + for (k = 1; k < intPhasesLen; k++) + { + if (intMag[k] < 0.75f * intMagMax) + { + dblAvgRadInner += intMag[k]; + intRadInner++; + } + else + { + dblAvgRadOuter += intMag[k]; + intRadOuter++; + } + } + + dblAvgRadInner = dblAvgRadInner / intRadInner; + dblAvgRadOuter = dblAvgRadOuter / intRadOuter; + } + else + { + dbPhaseStep = 2 * M_PI / intPSKPhase; + for (j = 1; j < intPhasesLen; j++) // skip the magnitude of the reference in calculation + { + intMagMax = max(intMagMax, intMag[j]); // find the max magnitude to auto scale + dblAvgRad += intMag[j]; + } + } + + dblAvgRad = dblAvgRad / (intPhasesLen - 1); // the average radius + + for (i = 1; i < intPhasesLen; i++) // Don't plot the first phase (reference) + { + intP = round((0.001f * intPhases[i]) / dbPhaseStep); + + // compute the Phase and Radius errors + + if (intMag[i] > (dblAvgRadInner + dblAvgRadOuter) / 2) + dblRadErrorOuter += fabsf(dblAvgRadOuter - intMag[i]); + else + dblRadErrorInner += fabsf(dblAvgRadInner - intMag[i]); + + dblPhaseError = fabsf(((0.001 * intPhases[i]) - intP * dbPhaseStep)); // always positive and < .5 * dblPhaseStep + dblPhaseErrorSum += dblPhaseError; + +#ifdef PLOTCONSTELLATION + dblRad = PLOTRADIUS * intMag[i] / intMagMax; // scale the radius dblRad based on intMagMax + intX = xCenter + dblRad * cosf(dblPlotRotation + intPhases[i] / 1000.0f); + intY = yCenter + dblRad * sinf(dblPlotRotation + intPhases[i] / 1000.0f); + + + if (intX > 0 && intY > 0) + if (intX != xCenter && intY != yCenter) + mySetPixel(intX, intY, Yellow); // don't plot on top of axis +#endif + } + + if (blnQAM) + { +// intQuality = max(0, ((100 - 200 * (dblPhaseErrorSum / (intPhasesLen)) / dbPhaseStep))); // ignore radius error for (PSK) but include for QAM + intQuality = max(0, (1 - (dblRadErrorInner / (intRadInner * dblAvgRadInner) + dblRadErrorOuter / (intRadOuter * dblAvgRadOuter))) * (100 - 200 * (dblPhaseErrorSum / intPhasesLen) / dbPhaseStep)); + +// intQuality = max(0, ((100 - 200 * (dblPhaseErrorSum / (intPhasesLen)) / dbPhaseStep))); // ignore radius error for (PSK) but include for QAM + + if (AccumulateStats) + { + if (OFDM) + { + intOFDMQualityCnts[RXOFDMMode] ++; + intOFDMQuality[RXOFDMMode] += intQuality; + intOFDMSymbolsDecoded += intPhasesLen; + } + else + { + intQAMQualityCnts += 1; + intQAMQuality += intQuality; + intQAMSymbolsDecoded += intPhasesLen; + } + } + } + else + { + intQuality = max(0, ((100 - 200 * (dblPhaseErrorSum / (intPhasesLen)) / dbPhaseStep))); // ignore radius error for (PSK) but include for QAM + + if (AccumulateStats) + { + if (OFDM) + { + intOFDMQualityCnts[RXOFDMMode] ++; + intOFDMQuality[RXOFDMMode] += intQuality; + intOFDMSymbolsDecoded += intPhasesLen; + } + else + { + intPSKQualityCnts[intPSKIndex]++; + intPSKQuality[intPSKIndex] += intQuality; + intPSKSymbolsDecoded += intPhasesLen; + } + + } + } +#ifdef PLOTCONSTELLATION + DrawAxes(intQuality, shortName(intFrameType), strMod); +#endif + return intQuality; + +} + + +// Subroutine to track 1 carrier 4FSK. Used for both single and multiple simultaneous carrier 4FSK modes. + + +VOID Track1Car4FSK(short * intSamples, int * intPtr, int intSampPerSymbol, float dblSearchFreq, int intBaud, UCHAR * bytSymHistory) +{ + // look at magnitude of the tone for bytHistory(1) 2 sample2 earlier and 2 samples later. and pick the maximum adjusting intPtr + or - 1 + // this seems to work fine on test Mar 16, 2015. This should handle sample rate offsets (sender to receiver) up to about 2000 ppm + + float dblReal, dblImag, dblMagEarly, dblMag, dblMagLate; + float dblBinToSearch = (dblSearchFreq - (intBaud * bytSymHistory[1])) / intBaud; // select the 2nd last symbol for magnitude comparison + + + GoertzelRealImag(intSamples, (*intPtr - intSampPerSymbol - 2), intSampPerSymbol, dblBinToSearch, &dblReal, &dblImag); + dblMagEarly = powf(dblReal, 2) + powf(dblImag, 2); + GoertzelRealImag(intSamples, (*intPtr - intSampPerSymbol), intSampPerSymbol, dblBinToSearch, &dblReal, &dblImag); + dblMag = powf(dblReal, 2) + powf(dblImag, 2); + GoertzelRealImag(intSamples, (*intPtr - intSampPerSymbol + 2), intSampPerSymbol, dblBinToSearch, &dblReal, &dblImag); + dblMagLate = powf(dblReal, 2) + powf(dblImag, 2); + + if (dblMagEarly > dblMag && dblMagEarly > dblMagLate) + { + *intPtr --; + Corrections--; + if (AccumulateStats) + intAccumFSKTracking--; + } + else if (dblMagLate > dblMag && dblMagLate > dblMagEarly) + { + *intPtr ++; + Corrections++; + if (AccumulateStats) + intAccumFSKTracking++; + } +} + +// Function to Decode one Carrier of PSK modulation + +// Ideally want to be able to call on for each symbol, as I don't have the +// RAM to build whole frame + +// Call for each set of 4 or 8 Phase Values + +int pskStart = 0; + + +VOID Decode1CarPSK(int Carrier, BOOL OFDM) +{ + unsigned int int24Bits; + UCHAR bytRawData; + int k; + int Len = intPhasesLen; + UCHAR * Decoded; + + if (OFDM) + Decoded = &bytFrameData[0][0]; // Always uses same buffer + else + { + if (CarrierOk[Carrier]) + return; // don't do it again + + Decoded = &bytFrameData[Carrier][0]; + } + + pskStart = 0; + charIndex = 0; + + + while (Len >= 0) + { + + // Phase Samples are in intPhases + + switch (intPSKMode) + { + case 2: // process 8 sequential phases per byte (1 bits per phase) + + for (k = 0; k < 8; k++) + { + if (k == 0) + bytRawData = 0; + else + bytRawData <<= 1; + + if (intPhases[Carrier][pskStart] >= 1572 || intPhases[Carrier][pskStart]<= -1572) + bytRawData += 1; + + pskStart++; + } + + Decoded[charIndex++] = bytRawData; + Len -= 8; + break; + + case 4: // process 4 sequential phases per byte (2 bits per phase) + + for (k = 0; k < 4; k++) + { + if (k == 0) + bytRawData = 0; + else + bytRawData <<= 2; + + if (intPhases[Carrier][pskStart] < 786 && intPhases[Carrier][pskStart] > -786) + { + } // Zero so no need to do anything + else if (intPhases[Carrier][pskStart] >= 786 && intPhases[Carrier][pskStart] < 2356) + bytRawData += 1; + else if (intPhases[Carrier][pskStart] >= 2356 || intPhases[Carrier][pskStart] <= -2356) + bytRawData += 2; + else + bytRawData += 3; + + pskStart++; + } + + Decoded[charIndex++] = bytRawData; + Len -= 4; + break; + + case 8: // Process 8 sequential phases (3 bits per phase) for 24 bits or 3 bytes + + // Status verified on 1 Carrier 8PSK with no RS needed for High S/N + + // Assume we check for 8 available phase samples before being called + + int24Bits = 0; + + for (k = 0; k < 8; k++) + { + int24Bits <<= 3; + + if (intPhases[Carrier][pskStart] < 393 && intPhases[Carrier][pskStart] > -393) + { + } // Zero so no need to do anything + else if (intPhases[Carrier][pskStart] >= 393 && intPhases[Carrier][pskStart] < 1179) + int24Bits += 1; + else if (intPhases[Carrier][pskStart] >= 1179 && intPhases[Carrier][pskStart] < 1965) + int24Bits += 2; + else if (intPhases[Carrier][pskStart] >= 1965 && intPhases[Carrier][pskStart] < 2751) + int24Bits += 3; + else if (intPhases[Carrier][pskStart] >= 2751 || intPhases[Carrier][pskStart] < -2751) + int24Bits += 4; + else if (intPhases[Carrier][pskStart] >= -2751 && intPhases[Carrier][pskStart] < -1965) + int24Bits += 5; + else if (intPhases[Carrier][pskStart] >= -1965 && intPhases[Carrier][pskStart] <= -1179) + int24Bits += 6; + else + int24Bits += 7; + + pskStart ++; + + } + Decoded[charIndex++] = int24Bits >> 16; + Decoded[charIndex++] = int24Bits >> 8; + Decoded[charIndex++] = int24Bits; + + Len -= 8; + break; + + case 16: // Process 2 sequential phases (4 bits per phase) for 1 bytes + + + for (k = 0; k < 2; k++) + { + if (k == 0) + bytRawData = 0; + else + bytRawData <<= 4; + + if (intPhases[Carrier][pskStart] < 196 && intPhases[Carrier][pskStart] > -196) + { + } // Zero so no need to do anything + else if (intPhases[Carrier][pskStart] >= 196 && intPhases[Carrier][pskStart] < 589) + bytRawData += 1; + else if (intPhases[Carrier][pskStart] >= 589 && intPhases[Carrier][pskStart] < 981) + bytRawData += 2; + else if (intPhases[Carrier][pskStart] >= 981 && intPhases[Carrier][pskStart] < 1374) + bytRawData += 3; + else if (intPhases[Carrier][pskStart] >= 1374 && intPhases[Carrier][pskStart] < 1766) + bytRawData += 4; + else if (intPhases[Carrier][pskStart] >= 1766 && intPhases[Carrier][pskStart] < 2159) + bytRawData += 5; + else if (intPhases[Carrier][pskStart] >= 2159 && intPhases[Carrier][pskStart] < 2551) + bytRawData += 6; + else if (intPhases[Carrier][pskStart] >= 2551 && intPhases[Carrier][pskStart] < 2944) + bytRawData += 7; + + else if (intPhases[Carrier][pskStart] >= 2944 || intPhases[Carrier][pskStart] < -2944) + bytRawData += 8; + else if (intPhases[Carrier][pskStart] >= -2944 && intPhases[Carrier][pskStart] < -2551) + bytRawData += 9; + else if (intPhases[Carrier][pskStart] >= -2551 && intPhases[Carrier][pskStart] < -2159) + bytRawData += 10; + else if (intPhases[Carrier][pskStart] >= -2159 && intPhases[Carrier][pskStart] < -1766) + bytRawData += 11; + else if (intPhases[Carrier][pskStart] >= -1766 && intPhases[Carrier][pskStart] < -1374) + bytRawData += 12; + else if (intPhases[Carrier][pskStart] >= -1374 && intPhases[Carrier][pskStart] < -981) + bytRawData += 13; + else if (intPhases[Carrier][pskStart] >= -981 && intPhases[Carrier][pskStart] < -589) + bytRawData += 14; + else + bytRawData += 15; + + pskStart ++; + + } + Decoded[charIndex++] = bytRawData; + + Len -= 2; + break; + + default: + return; //???? + } + } + return; +} + +// Function to compute PSK symbol tracking (all PSK modes, used for single or multiple carrier modes) + +int Track1CarPSK(int floatCarFreq, int PSKMode, BOOL QAM, BOOL OFDM, float dblUnfilteredPhase, BOOL blnInit) +{ + // This routine initializes and tracks the phase offset per symbol and adjust intPtr +/-1 when the offset creeps to a threshold value. + // adjusts (by Ref) intPtr 0, -1 or +1 based on a filtering of phase offset. + // this seems to work fine on test Mar 21, 2015. May need optimization after testing with higher sample rate errors. + // This should handle sample rate offsets (sender to receiver) up to about 2000 ppm + + float dblAlpha = 0.3f; // low pass filter constant may want to optimize value after testing with large sample rate error. + // (Affects how much averaging is done) lower values of dblAlpha will minimize adjustments but track more slugishly. + + float dblPhaseOffset; + + static float dblTrackingPhase = 0; + static float dblModFactor; + static float dblRadiansPerSample; // range is .4188 @ car freq = 800 to 1.1195 @ car freq 2200 + static float dblPhaseAtLastTrack; + static int intCountAtLastTrack; + static float dblFilteredPhaseOffset; + + if (blnInit) + { + // dblFilterredPhase = dblUnfilteredPhase; + dblTrackingPhase = dblUnfilteredPhase; + + if (PSKMode == 16) + dblModFactor = M_PI / 8; + else if (PSKMode == 8) + dblModFactor = M_PI / 4; + else if (PSKMode == 4) + dblModFactor = M_PI / 2; + else + dblModFactor = M_PI; // 2PSK + + dblRadiansPerSample = (floatCarFreq * dbl2Pi) / 12000.0f; + dblPhaseOffset = dblUnfilteredPhase - dblModFactor * round(dblUnfilteredPhase / dblModFactor); + dblPhaseAtLastTrack = dblPhaseOffset; + dblFilteredPhaseOffset = dblPhaseOffset; + intCountAtLastTrack = 0; + return 0; + } + + intCountAtLastTrack += 1; + dblPhaseOffset = dblUnfilteredPhase - dblModFactor * round(dblUnfilteredPhase / dblModFactor); + dblFilteredPhaseOffset = (1 - dblAlpha) * dblFilteredPhaseOffset + dblAlpha * dblPhaseOffset; + + if ((dblFilteredPhaseOffset - dblPhaseAtLastTrack) > dblRadiansPerSample) + { + //Debug.WriteLine("Filtered>LastTrack: Cnt=" & intCountAtLastTrack.ToString & " Filtered = " & Format(dblFilteredPhaseOffset, "00.000") & " Offset = " & Format(dblPhaseOffset, "00.000") & " Unfiltered = " & Format(dblUnfilteredPhase, "00.000")) + dblFilteredPhaseOffset = dblPhaseOffset - dblRadiansPerSample; + dblPhaseAtLastTrack = dblFilteredPhaseOffset; + + if (AccumulateStats) + { + if (OFDM) + { + intOFDMTrackAttempts++; + intAccumOFDMTracking--; + } + else + if (QAM) + { + intQAMTrackAttempts++; + intAccumQAMTracking--; + } + else + { + intPSKTrackAttempts++; + intAccumPSKTracking--; + } + } + return -1; + } + + if ((dblPhaseAtLastTrack - dblFilteredPhaseOffset) > dblRadiansPerSample) + { + //'Debug.WriteLine("Filtered 3142 ) + intDiff -= 6284; + + return intDiff; +} + +// Subroutine to "rotate" the phases to try and set the average offset to 0. + +void CorrectPhaseForTuningOffset(short * intPhase, int intPhaseLength, int intPSKMode) +{ + // A tunning error of -1 Hz will rotate the phase calculation Clockwise ~ 64 milliradians (~4 degrees) + // This corrects for: + // 1) Small tuning errors which result in a phase bias (rotation) of then entire constellation + // 2) Small Transmitter/receiver drift during the frame by averaging and adjusting to constellation to the average. + // It only processes phase values close to the nominal to avoid generating too large of a correction from outliers: +/- 30 deg for 4PSK, +/- 15 deg for 8PSK + // Is very affective in handling initial tuning error. + + short intPhaseMargin = 2095 / intPSKMode; // Compute the acceptable phase correction range (+/-30 degrees for 4 PSK) + short intPhaseInc = 6284 / intPSKMode; + int intTest; + int i; + int intOffset, intAvgOffset, intAvgOffsetBeginning, intAvgOffsetEnd; + int intAccOffsetCnt = 0, intAccOffsetCntBeginning = 0, intAccOffsetCntEnd = 0; + int intAccOffsetBeginning = 0, intAccOffsetEnd = 0, intAccOffset = 0; + + + // Note Rev 0.6.2.4 The following phase margin value increased from 2095 (120 deg) to 2793 (160 deg) yielded an improvement in decode at low S:N + + intPhaseMargin = 2793 / intPSKMode; // Compute the acceptable phase correction range (+/-30 degrees for 4 PSK) + intPhaseInc = 6284 / intPSKMode; + + // Compute the average offset (rotation) for all symbols within +/- intPhaseMargin of nominal + + for (i = 0; i < intPhaseLength; i++) + { + intTest = (intPhase[i] / intPhaseInc); + intOffset = intPhase[i] - intTest * intPhaseInc; + + if ((intOffset >= 0 && intOffset <= intPhaseMargin) || (intOffset < 0 && intOffset >= -intPhaseMargin)) + { + intAccOffsetCnt += 1; + intAccOffset += intOffset; + + if (i <= intPhaseLength / 4) + { + intAccOffsetCntBeginning += 1; + intAccOffsetBeginning += intOffset; + } + else if (i >= (3 * intPhaseLength) / 4) + { + intAccOffsetCntEnd += 1; + intAccOffsetEnd += intOffset; + } + } + } + + if (intAccOffsetCnt > 0) + intAvgOffset = (intAccOffset / intAccOffsetCnt); + if (intAccOffsetCntBeginning > 0) + intAvgOffsetBeginning = (intAccOffsetBeginning / intAccOffsetCntBeginning); + if (intAccOffsetCntEnd > 0) + intAvgOffsetEnd = (intAccOffsetEnd / intAccOffsetCntEnd); + + //Debugprintf("[CorrectPhaseForOffset] Beginning: %d End: %d Total: %d", + //intAvgOffsetBeginning, intAvgOffsetEnd, intAvgOffset); + + if ((intAccOffsetCntBeginning > intPhaseLength / 8) && (intAccOffsetCntEnd > intPhaseLength / 8)) + { + for (i = 0; i < intPhaseLength; i++) + { + intPhase[i] = intPhase[i] - ((intAvgOffsetBeginning * (intPhaseLength - i) / intPhaseLength) + (intAvgOffsetEnd * i / intPhaseLength)); + if (intPhase[i] > 3142) + intPhase[i] -= 6284; + else if (intPhase[i] < -3142) + intPhase[i] += 6284; + } + Debugprintf("[CorrectPhaseForTuningOffset] AvgOffsetBeginning=%d AvgOffsetEnd=%d AccOffsetCnt=%d/%d", + intAvgOffsetBeginning, intAvgOffsetEnd, intAccOffsetCnt, intPhaseLength); + } + else if (intAccOffsetCnt > intPhaseLength / 2) + { + for (i = 0; i < intPhaseLength; i++) + { + intPhase[i] -= intAvgOffset; + if (intPhase[i] > 3142) + intPhase[i] -= 6284; + else if (intPhase[i] < -3142) + intPhase[i] += 6284; + } + Debugprintf("[CorrectPhaseForTuningOffset] AvgOffset=%d AccOffsetCnt=%d/%d", + intAvgOffset, intAccOffsetCnt, intPhaseLength); + + } +} + +// Function to Decode one Carrier of 16QAM modulation + +// Call for each set of 4 or 8 Phase Values + +short intCarMagThreshold[MAXCAR] = {0}; + + +VOID Decode1CarQAM(int Carrier) +{ + unsigned int intData; + int k; + float dblAlpha = 0.1f; // this determins how quickly the rolling average dblTrackingThreshold responds. + + // dblAlpha value of .1 seems to work well...needs to be tested on fading channel (e.g. Multipath) + + int Threshold = intCarMagThreshold[Carrier]; + int Len = intPhasesLen; + + UCHAR * Decoded = bytFrameData[Carrier]; + + if (CarrierOk[Carrier]) + return; // don't do it again + pskStart = 0; + charIndex = 0; + + // We calculated initial mag from reference symbol + + // use filtered tracking of refernce phase amplitude + // (should be full amplitude value) + + // On WGN this appears to improve decoding threshold about 1 dB 9/3/2016 + + while (Len >= 0) + { + // Phase Samples are in intPhases + + intData = 0; + + for (k = 0; k < 2; k++) + { + intData <<= 4; + + if (intPhases[Carrier][pskStart] < 393 && intPhases[Carrier][pskStart] > -393) + { + } // Zero so no need to do anything + else if (intPhases[Carrier][pskStart] >= 393 && intPhases[Carrier][pskStart] < 1179) + intData += 1; + else if (intPhases[Carrier][pskStart] >= 1179 && intPhases[Carrier][pskStart] < 1965) + intData += 2; + else if (intPhases[Carrier][pskStart] >= 1965 && intPhases[Carrier][pskStart] < 2751) + intData += 3; + else if (intPhases[Carrier][pskStart] >= 2751 || intPhases[Carrier][pskStart] < -2751) + intData += 4; + else if (intPhases[Carrier][pskStart] >= -2751 && intPhases[Carrier][pskStart] < -1965) + intData += 5; + else if (intPhases[Carrier][pskStart] >= -1965 && intPhases[Carrier][pskStart] <= -1179) + intData += 6; + else + intData += 7; + + if (intMags[Carrier][pskStart] < Threshold) + { + intData += 8; // add 8 to "inner circle" symbols. + Threshold = (Threshold * 900 + intMags[Carrier][pskStart] * 150) / 1000; + } + else + { + Threshold = ( Threshold * 900 + intMags[Carrier][pskStart] * 75) / 1000; + } + + intCarMagThreshold[Carrier] = Threshold; + pskStart++; + } + Decoded[charIndex++] = intData; + Len -=2; + } +} +// Functions to demod all PSKData frames single or multiple carriers + + +VOID InitDemodPSK() +{ + // Called at start of frame + + int i; + float dblPhase, dblReal, dblImag; + + intPSKMode = strMod[0] - '0'; + PSKInitDone = TRUE; + intPhasesLen = 0; + + if (intPSKMode == 8) + dblPhaseInc = 2 * M_PI * 1000 / 8; + else + dblPhaseInc = 2 * M_PI * 1000 / 4; + + if (intBaud == 50) + intSampPerSym = 240; + else + intSampPerSym = 120; + + if (intNumCar == 1) + floatCarFreq = 1500; + else + floatCarFreq = 1400 + (intNumCar / 2) * 200; // start at the highest carrier freq which is actually the lowest transmitted carrier due to Reverse sideband mixing + + for (i= 0; i < intNumCar; i++) + { + if (intBaud == 50) + { + intCP[i] = 0; + intNforGoertzel[i] = 240; + dblFreqBin[i] = floatCarFreq / 50; + } + else if (intBaud == 100) + { + //Experimental use of Hanning Windowing + + intNforGoertzel[i] = 120; + dblFreqBin[i] = floatCarFreq / 100; + intCP[i] = 0; + } + +/* if (intBaud == 100 && floatCarFreq == 1500) + { + intCP[i] = 20; // These values selected for best decode percentage (92%) and best average 4PSK Quality (82) on MPP0dB channel + dblFreqBin[i] = floatCarFreq / 150; + intNforGoertzel[i] = 80; + } + else if (intBaud == 100) + { + intCP[i] = 28; // This value selected for best decoding percentage (56%) and best Averag 4PSK Quality (77) on mpg +5 dB + intNforGoertzel[i] = 60; + dblFreqBin[i] = floatCarFreq / 200; + } + else if (intBaud == 167) + { + intCP[i] = 6; // Need to optimize (little difference between 6 and 12 @ wgn5, 2 Car 500 Hz) + intNforGoertzel[i] = 60; + dblFreqBin[i] = floatCarFreq / 200; + } +*/ + // Get initial Reference Phase + + GoertzelRealImagHann120(intFilteredMixedSamples, 0, intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); + + dblPhase = atan2f(dblImag, dblReal); + Track1CarPSK(floatCarFreq, strMod[0] - '0', FALSE, FALSE, dblPhase, TRUE); + intPSKPhase_1[i] = -1000 * dblPhase; // negative sign compensates for phase reverse after mixing + + // Set initial mag from Reference Phase (which should be full power) + // Done here as well as in initQAM for pkt where we may switch mode midpacket + + intCarMagThreshold[i] = sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); + intCarMagThreshold[i] *= 0.75; + + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + } +} + +int Demod1CarPSKChar(int Start, int Carrier); +void SavePSKSamples(int i); + + +short WeightedAngleAvg(short intAng1, short intAng2); + +int CheckCarrierPairPSK(int Base, int Dup, int frameLen) +{ + int i, Len; + + Debugprintf("DemodPSK Carriers %d and %d", Base, Dup); + + Decode1CarPSK(Base, FALSE); + Len = CorrectRawDataWithRS(&bytFrameData[Base][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, Base); + + if (CarrierOk[Base]) + { + // No need to decode 2nd + + CarrierOk[Dup] = 1; // So FrameOk test is passed + return Len + frameLen; + } + + Debugprintf("DemodPSK Carrier %d bad, trying %d", Base, Dup); + + Decode1CarPSK(Dup, FALSE); // Decode Dup carrier + Len = CorrectRawDataWithRS(&bytFrameData[Dup][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, Base); // Save as carrier 1 + + if (CarrierOk[Base]) + { + CarrierOk[Dup] = 1; // So FrameOk test is passed + bytFrameData[Base][0] = Len; + memcpy(&bytFrameData[Base][1], &bytData[frameLen], Len); // Any retry will use first copy without new decode + return Len + frameLen; + } + + + // Try to average phases for the two carriers + + Debugprintf("DemodPSK both bad, trying average"); + + for (i = 0; i 0 && Start > 0) + memmove(intFilteredMixedSamples, + &intFilteredMixedSamples[Start], intFilteredMixedSamplesLength * 2); + + return; + } + + + if (PSKInitDone == 0) // First time through + { + if (intFilteredMixedSamplesLength < 2 * intPSKMode * intSampPerSym + 10) + return; // Wait for at least 2 chars worth + + InitDemodPSK(); + intFilteredMixedSamplesLength -= intSampPerSym; + if (intFilteredMixedSamplesLength < 0) + Debugprintf("Corrupt intFilteredMixedSamplesLength"); + + Start += intSampPerSym; + } + + // If this is a multicarrier mode, we must call the + // decode char routing for each carrier + + if (intNumCar == 1) + floatCarFreq = 1500; + else + floatCarFreq = 1400 + (intNumCar / 2) * 200; // start at the highest carrier freq which is actually the lowest transmitted carrier due to Reverse sideband mixing + + Used[0] = Demod1CarPSKChar(Start, 0); + + if (intNumCar > 1) + { + intPhasesLen -= intPSKMode; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + + Used[1] = Demod1CarPSKChar(Start, 1); + } + + if (intNumCar > 2) + { + intPhasesLen -= intPSKMode; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Used[2] = Demod1CarPSKChar(Start, 2); + + intPhasesLen -= intPSKMode; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Used[3] = Demod1CarPSKChar(Start, 3); + } + + if (intNumCar > 4) + { + intPhasesLen -= intPSKMode; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + + Used[4] = Demod1CarPSKChar(Start, 4); + intPhasesLen -= intPSKMode; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + + Used[5] = Demod1CarPSKChar(Start, 5); + intPhasesLen -= intPSKMode; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + + Used[6] = Demod1CarPSKChar(Start, 6); + intPhasesLen -= intPSKMode; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + + Used[7] = Demod1CarPSKChar(Start, 7); + intPhasesLen -= intPSKMode; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + + Used[8] = Demod1CarPSKChar(Start, 8); + intPhasesLen -= intPSKMode; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + + Used[9] = Demod1CarPSKChar(Start, 9); + } + + if (intPSKMode == 4) + SymbolsLeft--; // number still to decode + else + SymbolsLeft -=3; + + // If/when we reenable phase correstion we can take average of Used values. + // ?? Should be also keep start value per carrier ?? + + Start += Used[0]; + intFilteredMixedSamplesLength -= Used[0]; + + if (intFilteredMixedSamplesLength < 0) + Debugprintf("Corrupt intFilteredMixedSamplesLength"); + + if (SymbolsLeft > 0) + continue; + + // Decode the phases + + DecodeCompleteTime = Now; + +// CorrectPhaseForTuningOffset(&intPhases[0][0], intPhasesLen, strMod); + +// if (intNumCar > 1) +// CorrectPhaseForTuningOffset(&intPhases[1][0], intPhasesLen, strMod); + + if (intNumCar > 2) + { +// CorrectPhaseForTuningOffset(&intPhases[2][0], intPhasesLen, strMod); +// CorrectPhaseForTuningOffset(&intPhases[3][0], intPhasesLen, strMod); + } + if (intNumCar > 4) + { +// CorrectPhaseForTuningOffset(&intPhases[4][0], intPhasesLen, strMod); +// CorrectPhaseForTuningOffset(&intPhases[5][0], intPhasesLen, strMod); +// CorrectPhaseForTuningOffset(&intPhases[6][0], intPhasesLen, strMod); +// CorrectPhaseForTuningOffset(&intPhases[7][0], intPhasesLen, strMod); + } + + // Rick uses the last carier for Quality + intLastRcvdFrameQuality = UpdatePhaseConstellation(&intPhases[intNumCar - 1][0], &intMags[intNumCar - 1][0], strMod[0] - '0', FALSE, FALSE); + + // prepare for next + + State = SearchingForLeader; + DiscardOldSamples(); + ClearAllMixedSamples(); + + if (strchr(strMod, 'R')) + { + // Robust Mode - data is repeated (1-2 or 1-6, 2-7, etc + + if (intNumCar == 2) + { + frameLen = CheckCarrierPairPSK(0, 1, 0); + return; + } + + //Only have 2 or 10 (500 or 2500 modes) + + + frameLen = CheckCarrierPairPSK(0, 5, 0); + frameLen = CheckCarrierPairPSK(1, 6, frameLen); + frameLen = CheckCarrierPairPSK(2, 7, frameLen); + frameLen = CheckCarrierPairPSK(3, 8, frameLen); + frameLen = CheckCarrierPairPSK(4, 9, frameLen); + + return; + } + + // Non -robust + + frameLen = 0; + + for (i = 0; i < intNumCar; i++) + { + Decode1CarPSK(i, FALSE); + frameLen += CorrectRawDataWithRS(&bytFrameData[i][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, i); + } + + // If variable length packet frame header we only have header - leave rx running + + if (intFrameType == PktFrameHeader) + { + State = SearchingForLeader; + + // Save any unused samples + + if (intFilteredMixedSamplesLength > 0 && Start > 0) + memmove(intFilteredMixedSamples, + &intFilteredMixedSamples[Start], intFilteredMixedSamplesLength * 2); + + return; + } + +#ifdef MEMORYARQ + + for (Carrier = 0; Carrier < intNumCar; Carrier++) + { + if (!CarrierOk[Carrier]) + { + // Decode error - save data for MEM ARQ + + SavePSKSamples(Carrier); + + if (intSumCounts[Carrier] > 1) + { + Decode1CarQAM(Carrier); // try to decode based on the WeightedAveragePhases + MemARQRetries++; + } + } + } + + if (MemARQRetries) + { + // We've retryed to decode - see if ok now + + int OKNow = TRUE; + + Debugprintf("DemodPSK retry RS on MEM ARQ Corrected frames"); + frameLen = 0; + + for (Carrier = 0; Carrier < intNumCar; Carrier++) + { + frameLen += CorrectRawDataWithRS(bytFrameData[Carrier], bytData, intDataLen, intRSLen, intFrameType, Carrier); + if (CarrierOk[Carrier] == 0) + OKNow = FALSE; + } + + if (OKNow && AccumulateStats) + intGoodPSKSummationDecodes++; + } +#endif + } + return; +} + +// Function to demodulate one carrier for all PSK frame types +int Demod1CarPSKChar(int Start, int Carrier) +{ + // Converts intSample to an array of differential phase and magnitude values for the Specific Carrier Freq + // intPtr should be pointing to the approximate start of the first reference/training symbol (1 of 3) + // intPhase() is an array of phase values (in milliradians range of 0 to 6283) for each symbol + // intMag() is an array of Magnitude values (not used in PSK decoding but for constellation plotting or QAM decoding) + // Objective is to use Minimum Phase Error Tracking to maintain optimum pointer position + + // This is called for one DMA buffer of samples (normally 1200) + + float dblReal, dblImag; + int intMiliRadPerSample = floatCarFreq * M_PI / 6; + int i; + int intNumOfSymbols = intPSKMode; + int origStart = Start;; + + if (CarrierOk[Carrier]) // Already decoded this carrier? + { + intPhasesLen += intNumOfSymbols; + return intSampPerSym * intNumOfSymbols; + } + + for (i = 0; i < intNumOfSymbols; i++) + { + GoertzelRealImag(intFilteredMixedSamples, Start, intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); +// GoertzelRealImagHann120(intFilteredMixedSamples, Start, intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); + + intMags[Carrier][intPhasesLen] = sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); + intPSKPhase_0[Carrier] = -1000 * atan2f(dblImag, dblReal); + intPhases[Carrier][intPhasesLen] = (ComputeAng1_Ang2(intPSKPhase_0[Carrier], intPSKPhase_1[Carrier])); + +/* + if (Carrier == 0) + { + Corrections = Track1CarPSK(floatCarFreq, strMod, atan2f(dblImag, dblReal), FALSE); + + if (Corrections != 0) + { + Start += Corrections; + + if (intCP[i] == 0) + GoertzelRealImagHanning(intFilteredMixedSamples, Start, intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); + else + GoertzelRealImag(intFilteredMixedSamples, Start + intCP[Carrier], intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); + + intPSKPhase_0[Carrier] = 1000 * atan2f(dblImag, dblReal); + } + } +*/ + intPSKPhase_1[Carrier] = intPSKPhase_0[Carrier]; + intPhasesLen++; + Start += intSampPerSym; + + } + if (AccumulateStats) + intPSKSymbolCnt += intNumOfSymbols; + + return (Start - origStart); // Symbols we've consumed +} + +VOID InitDemodQAM() +{ + // Called at start of frame + + int i; + float dblPhase, dblReal, dblImag; + + intPSKMode = 8; // 16QAM uses 8 PSK + dblPhaseInc = 2 * M_PI * 1000 / 8; + intPhasesLen = 0; + + PSKInitDone = TRUE; + + intSampPerSym = 120; + + if (intNumCar == 1) + floatCarFreq = 1500; + else + floatCarFreq = 1400 + (intNumCar / 2) * 200; // start at the highest carrier freq which is actually the lowest transmitted carrier due to Reverse sideband mixing + + for (i= 0; i < intNumCar; i++) + { + // Only 100 Hz for QAM + + intCP[i] = 0; + intNforGoertzel[i] = 120; + dblFreqBin[i] = floatCarFreq / 100; + + // Get initial Reference Phase + + GoertzelRealImagHanning(intFilteredMixedSamples, intCP[i], intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); + dblPhase = atan2f(dblImag, dblReal); + + // Set initial mag from Reference Phase (which should be full power) + + intCarMagThreshold[i] = sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); + intCarMagThreshold[i] *= 0.75; + + Track1CarPSK(floatCarFreq, 8, TRUE, FALSE, dblPhase, TRUE); + intPSKPhase_1[i] = 1000 * dblPhase; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + } +} + +int Demod1CarQAMChar(int Start, int Carrier); + + +// Function to average two angles using magnitude weighting + +short WeightedAngleAvg(short intAng1, short intAng2) +{ + // Ang1 and Ang 2 are in the range of -3142 to + 3142 (miliradians) + // works but should come up with a routine that avoids Sin, Cos, Atan2 + // Modified in Rev 0.3.5.1 to "weight" averaging by intMag1 and intMag2 (why!!!) + + float dblSumX, dblSumY; + + dblSumX = cosf(intAng1 / 1000.0) + cosf(intAng2 / 1000.0); + dblSumY = sinf(intAng1 / 1000.0) + sinf(intAng2 / 1000.0); + + return (1000 * atan2f(dblSumY, dblSumX)); +} + +#ifdef MEMORYARQ + +void SaveQAMSamples(int i) +{ + int m; + + if (intSumCounts[i] == 0) + { + // First try - initialize Sum counts Phase average and Mag Average + + for (m = 0; m < intPhasesLen; m++) + { + intCarPhaseAvg[i][m] = intPhases[i][m]; + intCarMagAvg[i][m] = intMags[i][m]; + } + } + else + { + for (m = 0; m < intPhasesLen; m++) + { + intCarPhaseAvg[i][m] = WeightedAngleAvg(intCarPhaseAvg[i][m], intPhases[i][m]); + intPhases[i][m] = intCarPhaseAvg[i][m]; + // Use simple weighted average for Mags + intCarMagAvg[i][m] = (intCarMagAvg[i][m] * intSumCounts[i] + intMags[i][m]) / (intSumCounts[i] + 1); + intMags[i][m] = intCarMagAvg[i][m]; + } + } + intSumCounts[i]++; +} + +void SavePSKSamples(int i) +{ + int m; + + if (intSumCounts[i] == 0) + { + // First try - initialize Sum counts Phase average and Mag Average + + for (m = 0; m < intPhasesLen; m++) + { + intCarPhaseAvg[i][m] = intPhases[i][m]; + } + } + else + { + for (m = 0; m < intPhasesLen; m++) + { + intCarPhaseAvg[i][m] = WeightedAngleAvg(intCarPhaseAvg[i][m], intPhases[i][m]); + intPhases[i][m] = intCarPhaseAvg[i][m]; + } + } + intSumCounts[i]++; +} + +#endif + +int CheckCarrierPair(int Base, int Dup, int frameLen) +{ + int i, Len; + + Debugprintf("DemodQAMR Carriers %d and %d", Base, Dup); + + Decode1CarQAM(Base); + Len = CorrectRawDataWithRS(&bytFrameData[Base][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, Base); + + if (CarrierOk[Base]) + { + // No need to decode 2nd + + CarrierOk[Dup] = 1; // So FrameOk test is passed + Debugprintf("DemodQAMR Returning Len %d", Len); + return Len + frameLen; + } + + Debugprintf("DemodQAMR Carrier %d bad, trying %d", Base, Dup); + + Decode1CarQAM(Dup); // Decode Dup carrier + Len = CorrectRawDataWithRS(&bytFrameData[Dup][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, Base); // Save as carrier 1 + + if (CarrierOk[Base]) + { + CarrierOk[Dup] = 1; // So FrameOk test is passed + bytFrameData[Base][0] = Len; + memcpy(&bytFrameData[Base][1], &bytData[frameLen], Len); // Any retry will use first copy without new decode + Debugprintf("DemodQAMR Returning Len %d", Len); + return Len + frameLen; + } + + // Try to average phases for the two carriers + + Debugprintf("DemodQAMR both bad, trying average"); + + for (i = 0; i 0) + memmove(intFilteredMixedSamples, + &intFilteredMixedSamples[Start], intFilteredMixedSamplesLength * 2); + + return FALSE; + } + + if (PSKInitDone == 0) // First time through + { + if (intFilteredMixedSamplesLength < 9 * intSampPerSym + 10) + return FALSE; // Wait for at least 2 chars worth + + InitDemodQAM(); + intFilteredMixedSamplesLength -= intSampPerSym; + + Start += intSampPerSym; + } + + // If this is a multicarrier mode, we must call the + // decode char routine for each carrier + + if (intNumCar == 1) + floatCarFreq = 1500; + else + floatCarFreq = 1400 + (intNumCar / 2) * 200; // start at the highest carrier freq which is actually the lowest transmitted carrier due to Reverse sideband mixing + + + Used = Demod1CarQAMChar(Start, 0); // demods 2 phase values - enough for one char + + if (intNumCar > 1) + { + intPhasesLen -= 2; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Demod1CarQAMChar(Start, 1); + } + + if (intNumCar > 2) + { + intPhasesLen -= 2; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Demod1CarQAMChar(Start, 2); + intPhasesLen -= 2; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Demod1CarQAMChar(Start, 3); + } + + if (intNumCar > 4) + { + intPhasesLen -= 2; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Demod1CarQAMChar(Start, 4); + + intPhasesLen -= 2; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Demod1CarQAMChar(Start, 5); + + intPhasesLen -= 2; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Demod1CarQAMChar(Start, 6); + + intPhasesLen -= 2; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Demod1CarQAMChar(Start, 7); + + intPhasesLen -= 2; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Demod1CarQAMChar(Start, 8); + + intPhasesLen -= 2; + floatCarFreq -= 200; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + Demod1CarQAMChar(Start, 9); + } + + SymbolsLeft--; // number still to decode - we've done one + + Start += Used; + intFilteredMixedSamplesLength -= Used; + + if (SymbolsLeft <= 0) + { + // Frame complete - decode it + + DecodeCompleteTime = Now; + +// CorrectPhaseForTuningOffset(&intPhases[0][0], intPhasesLen, strMod); + +// if (intNumCar > 1) +// CorrectPhaseForTuningOffset(&intPhases[1][0], intPhasesLen, strMod); + + if (intNumCar > 2) + { +// CorrectPhaseForTuningOffset(&intPhases[2][0], intPhasesLen, strMod); +// CorrectPhaseForTuningOffset(&intPhases[3][0], intPhasesLen, strMod); + } + if (intNumCar > 4) + { +// CorrectPhaseForTuningOffset(&intPhases[4][0], intPhasesLen, strMod); +// CorrectPhaseForTuningOffset(&intPhases[5][0], intPhasesLen, strMod); +// CorrectPhaseForTuningOffset(&intPhases[6][0], intPhasesLen, strMod); +// CorrectPhaseForTuningOffset(&intPhases[7][0], intPhasesLen, strMod); + } + + intLastRcvdFrameQuality = UpdatePhaseConstellation(&intPhases[intNumCar - 1][0], &intMags[intNumCar - 1][0], 8, TRUE, FALSE); + + // prepare for next so we can exit when we have finished decode + + DiscardOldSamples(); + ClearAllMixedSamples(); + State = SearchingForLeader; + + if (strchr(strMod, 'R')) + { + // Robust Mode - data is repeated (1-2 or 1-6, 2-7, etc + + if (intNumCar == 2) + { + frameLen = CheckCarrierPair(0, 1, 0); + return TRUE; + } + + //Only have 2 or 10 (500 or 2500 modes) + + + frameLen = CheckCarrierPair(0, 5, 0); + frameLen = CheckCarrierPair(1, 6, frameLen); + frameLen = CheckCarrierPair(2, 7, frameLen); + frameLen = CheckCarrierPair(3, 8, frameLen); + frameLen = CheckCarrierPair(4, 9, frameLen); + + return TRUE; + } + + // Non -robust + + Decode1CarQAM(0); + frameLen = CorrectRawDataWithRS(&bytFrameData[0][0], bytData, intDataLen, intRSLen, intFrameType, 0); + + + if (intNumCar > 1) + { + Decode1CarQAM(1); + frameLen += CorrectRawDataWithRS(&bytFrameData[1][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 1); + + } + + + if (intNumCar > 2) + { + Decode1CarQAM(2); + Decode1CarQAM(3); + frameLen += CorrectRawDataWithRS(&bytFrameData[2][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 2); + frameLen += CorrectRawDataWithRS(&bytFrameData[3][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 3); + + } + + + if (intNumCar > 4) + { + Decode1CarQAM(4); + Decode1CarQAM(5); + Decode1CarQAM(6); + Decode1CarQAM(7); + Decode1CarQAM(8); + Decode1CarQAM(9); + frameLen += CorrectRawDataWithRS(&bytFrameData[4][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 4); + frameLen += CorrectRawDataWithRS(&bytFrameData[5][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 5); + frameLen += CorrectRawDataWithRS(&bytFrameData[6][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 6); + frameLen += CorrectRawDataWithRS(&bytFrameData[7][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 7); + frameLen += CorrectRawDataWithRS(&bytFrameData[8][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 8); + frameLen += CorrectRawDataWithRS(&bytFrameData[9][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, 9); + + } + + // Check Data + + if (memcmp(CarrierOk, Good, intNumCar) == 0) + return TRUE; + + // Bad decode if we have Memory ARQ try it + +#ifdef MEMORYARQ + + for (i = 0; i < intNumCar; i++) + { + if (!CarrierOk[i] && intFrameType != PktFrameHeader) + { + // Decode error - save data for MEM ARQ + + SaveQAMSamples(i); + + if (intSumCounts[0] > 1) + { + MemARQOk = 1; + Decode1CarQAM(i); // try to decode based on the WeightedAveragePhases + } + } + } + + if (MemARQOk == 0) // Havent averaged yet + return TRUE; + + // We've tried to correct - see if it worked + + Debugprintf("DemodQAM Trying MEM ARQ"); + + // Non -robust + + frameLen = 0; + + for (i = 0; i < intNumCar; i++) + { + frameLen += CorrectRawDataWithRS(&bytFrameData[i][0], &bytData[frameLen], intDataLen, intRSLen, intFrameType, i); + } + + // Check Data + + if (memcmp(CarrierOk, Good, intNumCar) == 0) + { + Debugprintf("DemodQAM MEM ARQ Corrected frame"); + intGoodQAMSummationDecodes++; + } +#endif + } + } + return TRUE; +} + +int Demod1CarQAMChar(int Start, int Carrier) +{ + // Converts intSample to an array of differential phase and magnitude values for the Specific Carrier Freq + // intPtr should be pointing to the approximate start of the first reference/training symbol (1 of 3) + // intPhase() is an array of phase values (in milliradians range of 0 to 6283) for each symbol + // intMag() is an array of Magnitude values (not used in PSK decoding but for constellation plotting or QAM decoding) + // Objective is to use Minimum Phase Error Tracking to maintain optimum pointer position + + // This is called for one DMA buffer of samples (normally 1200) + + float dblReal, dblImag; + int intMiliRadPerSample = floatCarFreq * M_PI / 6; + int i; + int intNumOfSymbols = 2; + int origStart = Start;; + + if (CarrierOk[Carrier]) // Already decoded this carrier? + { + intPhasesLen += intNumOfSymbols; + return intSampPerSym * intNumOfSymbols; + } + + for (i = 0; i < intNumOfSymbols; i++) + { + // GoertzelRealImag(intFilteredMixedSamples, Start + intCP[Carrier], intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); + GoertzelRealImagHanning(intFilteredMixedSamples, Start + intCP[Carrier], intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); + intMags[Carrier][intPhasesLen] = sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); + intPSKPhase_0[Carrier] = 1000 * atan2f(dblImag, dblReal); + intPhases[Carrier][intPhasesLen] = -(ComputeAng1_Ang2(intPSKPhase_0[Carrier], intPSKPhase_1[Carrier])); + + +/* + if (Carrier == 0) + { + Corrections = Track1CarPSK(floatCarFreq, strMod, atan2f(dblImag, dblReal), FALSE); + + if (Corrections != 0) + { + Start += Corrections; + + // GoertzelRealImag(intFilteredMixedSamples, Start + intCP[Carrier], intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); + GoertzelRealImagHanning(intFilteredMixedSamples, Start + intCP[Carrier], intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); + intPSKPhase_0[Carrier] = 1000 * atan2f(dblImag, dblReal); + } + } +*/ + intPSKPhase_1[Carrier] = intPSKPhase_0[Carrier]; + intPhasesLen++; + Start += intSampPerSym; + } + + if (AccumulateStats) + intQAMSymbolCnt += intNumOfSymbols; + + return (Start - origStart); // Symbols we've consumed +} + + +extern int bytQDataInProcessLen; + + +// function to decode one carrier from tones (used to decode from Averaged intToneMags) + +BOOL Decode1Car4FSKFromTones(UCHAR * bytData, int intToneMags) +{ + // Decodes intToneMags() to an array of bytes + // Updates bytData() with decoded + +/* + UCHAR bytSym; + int intIndex; + + ReDim bytData(intToneMags.Length \ 16 - 1) + + For i As Integer = 0 To bytData.Length - 1 ' For each data byte + intIndex = 16 * i + For j As Integer = 0 To 3 ' for each 4FSK symbol (2 bits) in a byte + If intToneMags(intIndex) > intToneMags(intIndex + 1) And intToneMags(intIndex) > intToneMags(intIndex + 2) And intToneMags(intIndex) > intToneMags(intIndex + 3) Then + bytSym = 0 + ElseIf intToneMags(intIndex + 1) > intToneMags(intIndex) And intToneMags(intIndex + 1) > intToneMags(intIndex + 2) And intToneMags(intIndex + 1) > intToneMags(intIndex + 3) Then + bytSym = 1 + ElseIf intToneMags(intIndex + 2) > intToneMags(intIndex) And intToneMags(intIndex + 2) > intToneMags(intIndex + 1) And intToneMags(intIndex + 2) > intToneMags(intIndex + 3) Then + bytSym = 2 + Else + bytSym = 3 + End If + bytData(i) = (bytData(i) << 2) + bytSym + intIndex += 4 + Next j + Next i + Return True + End Function ' Decode1Car4FSKFromTones +*/ + return TRUE; +} + +/* ' Function to decode one carrier from tones (used to decode from Averaged intToneMags) + Private Function Decode1Car8FSKFromTones(ByRef bytData() As Byte, ByRef intToneMags() As Int32) As Boolean + ' Decodes intToneMags() to an array of bytes + ' Updates bytData() with decoded + + Dim bytSym As Byte + Dim intThreeBytes As Int32 + ReDim bytData(3 * intToneMags.Length \ 64 - 1) + Dim intMaxMag As Int32 + For i As Integer = 0 To (bytData.Length \ 3) - 1 ' For each group of 3 bytes data byte + intThreeBytes = 0 + For j As Integer = 0 To 7 ' for each group of 8 symbols (24 bits) + intMaxMag = 0 + For k As Integer = 0 To 7 ' for each of 8 possible tones per symbol + If intToneMags((i * 64) + 8 * j + k) > intMaxMag Then + intMaxMag = intToneMags((i * 64) + 8 * j + k) + bytSym = k + End If + Next k + intThreeBytes = (intThreeBytes << 3) + bytSym + Next j + bytData(3 * i) = (intThreeBytes And &HFF0000) >> 16 + bytData(3 * i + 1) = (intThreeBytes And &HFF00) >> 8 + bytData(3 * i + 2) = (intThreeBytes And &HFF) + Next i + Return True + End Function ' Decode1Car8FSKFromTones + + ' Function to decode one carrier from tones (used to decode from Averaged intToneMags) + Private Function Decode1Car16FSKFromTones(ByRef bytData() As Byte, ByRef intToneMags() As Int32) As Boolean + ' Decodes intToneMags() to an array of bytes + ' Updates bytData() with decoded tones + + Dim bytSym As Byte + Dim intMaxMag As Int32 + ReDim bytData(intToneMags.Length \ 32 - 1) + For i As Integer = 0 To bytData.Length - 1 ' For each data byte + For j As Integer = 0 To 1 ' for each 16FSK symbol (4 bits) in a byte + intMaxMag = 0 + For k As Integer = 0 To 15 + If intToneMags(i * 32 + 16 * j + k) > intMaxMag Then + intMaxMag = intToneMags(i * 32 + 16 * j + k) + bytSym = k + End If + Next k + bytData(i) = (bytData(i) << 4) + bytSym + Next j + Next i + Return True + End Function ' Decode1Car16FSKFromTones + +*/ + + + +// Subroutine to update the Busy detector when not displaying Spectrum or Waterfall (graphics disabled) + +extern int LastBusyCheck; + +extern BOOL blnBusyStatus; + +int intWaterfallRow = 0; + + + +void UpdateBusyDetector(short * bytNewSamples) +{ + float dblReF[1024]; + float dblImF[1024]; + float dblMag[206]; + + float dblMagAvg = 0; + int intTuneLineLow, intTuneLineHi, intDelta; + int i; + int BusyFlag; + + + if (ProtocolState > DISC) // ' Only process busy when in DISC state + return; + + if (State != SearchingForLeader) + return; // only when looking for leader + + if (Now - LastBusyCheck < 100) + return; + + LastBusyCheck = Now; + + FourierTransform(1024, bytNewSamples, &dblReF[0], &dblImF[0], FALSE); + + for (i = 0; i < 206; i++) + { + // starting at ~300 Hz to ~2700 Hz Which puts the center of the signal in the center of the window (~1500Hz) + + dblMag[i] = powf(dblReF[i + 25], 2) + powf(dblImF[i + 25], 2); // first pass + dblMagAvg += dblMag[i]; + } + + // Not sure about this as we use variable bandwidth frames. For now use 500 + + intDelta = (500 / 2 + TuningRange) / 11.719f; + + intTuneLineLow = max((103 - intDelta), 3); + intTuneLineHi = min((103 + intDelta), 203); + + // At the moment we only get here what seaching for leader, + // but if we want to plot spectrum we should call + // it always + + BusyFlag = BusyDetect3(dblMag, intTuneLineLow, intTuneLineHi); + + if (BusyFlag == 0) + { + if (BusyCount == 0) + blnBusyStatus = 0; + else + BusyCount--; + } + else + { + blnBusyStatus = 1; + BusyCount = 10; // Try delaying busy off a bit + } + + if (blnBusyStatus && !blnLastBusyStatus) + { + Debugprintf("BUSY TRUE"); + } + // stcStatus.Text = "True" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY TRUE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + else if (blnLastBusyStatus && !blnBusyStatus) + { + Debugprintf("BUSY FALSE"); + } + + blnLastBusyStatus = blnBusyStatus; + +} + diff --git a/UZ7HOStuff-HPLaptop.h b/UZ7HOStuff-HPLaptop.h new file mode 100644 index 0000000..dfc2886 --- /dev/null +++ b/UZ7HOStuff-HPLaptop.h @@ -0,0 +1,1023 @@ +// +// My port of UZ7HO's Soundmodem +// + +#define VersionString "0.0.0.59" +#define VersionBytes {0, 0, 0, 59} + +// Added FX25. 4x100 FEC and V27 not Working and disabled + +// 0.8 V27 now OK. + +// 0.9 Digipeating added + +// 0.10 Fix second channel tones and calibrate + +// 0.11 Fix allocation of sessions to correct modem +// Fix DCD +// Fix Monitoring of Multiline packets +// Fix possible saving of wrong center freq +// Limit TX sample Q in Linux +// + +// 0.12 Add AGWPE monitoring of received frames +// Fix DCD Threshold +// Fix KISS transparency issue + +// 0.13 Fix sending last few bits in FX.25 Mode + +// 0.14 Add "Copy on Select" to Trace Window + +// 0.15 Limit Trace window to 10000 lines + +// 0.16 Fix overwriting monitor window after scrollback + +// 0.17 Add GPIO and CAT PTT + +// 0.18 Add CM108/119 PTT + +// 0.19 Fix scheduling KISS frames + +// 0.20 Debug code added to RR processing + +// 0.21 Fix AGW monitor of multiple line packets +// Close ax.25 sessions if AGW Host session closes + +// 0.22 Add FEC Count to Session Stats + +// 0.23 Retry DISC until UA received or retry count exceeded + +// 0.24 More fixes to DISC handling + +// 0.26 Add OSS PulseAudio and HAMLIB support + +// 0.27 Dynamically load PulseAudio modules + +// 0.28 Add ARDOPPacket Mode + +// 0.29 Fix saving settings and geometry on close +// 0.30 Retructure code to build with Qt 5.3 +// Fix crash in nogui mode if pulse requested but not available +// Try to fix memory leaks + +// 0.31 Add option to run modems in seprate threads + +// 0.32 Fix timing problem with AGW connect at startup +// Add Memory ARQ +// Add Single bit "Correction" +// Fix error in 31 when using multiple decoders + +// 0.33 Fix Single bit correction +// More memory leak fixes + +// 0.34 Add API to set Modem and Center Frequency +// Fix crash in delete_incoming_mycalls + +// 0.35 Return Version in AGW Extended g response + +// 0.36 Fix timing problem on startup + +// 0.37 Add scrollbars to Device and Modem dialogs + +// 0.38 Change default CM108 name to /dev/hidraw0 on Linux + +// 0.39 Dont try to display Message Boxes in nogui mode. +// Close Device and Modem dialogs on Accept or Reject +// Fix using HAMLIB in nogui mode + +// 0.40 Fix bug in frame optimize when using 6 char calls + +// 0.41 Fix "glitch" on waterfall markers when changing modem freqs + +// 0.42 Add "Minimize to Tray" option + +// 0.43 Add Andy's on_SABM fix. +// Fix Crash if KISS Data sent to AGW port + +// 0.44 Add UDP bridge. + +// 0.45 Add two more modems. +// 0.46 Fix two more modems. + +// 0.47 Fix suprious DM when host connection lost +// Add CWID + +// 0.48 Send FRMR for unrecognised frame types + +// 0.49 Add Andy's FEC Tag correlation coode + +// 0.50 Fix Waterfall display when only using right channel +// Allow 1200 baud fsk at other center freqs +// Add Port numbers to Window title and Try Icon tooltip +// Fix calculation of filters for multiple decoders +// Add RX Offset setting (for satellite operation + +// 0.51 Fix Multithreading with more that 2 modems + +// 0.52 Add Stdin as source on Linux + +// 0.53 Use Byte instead of byte as byte is defined in newer versions of gcc + +// 0.54 Fix for ALSA problem on new pi OS + +// 0.55 Fix for compiler error with newer compiler + +// 0.56 Fix errors in Config.cpp June 22 + +// 0.57 Add Restart Waterfall action August 22 + +// 0.58 Add RSID Sept 2022 + +// 0.59 Add config of Digi Calls Dec 2022 + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define UNUSED(x) (void)(x) + +#ifdef M_PI +#undef M_PI +#endif + +#define M_PI 3.1415926f + +#define pi M_PI + +#ifndef WIN32 +#define _strdup strdup +#endif + + //#define NULL ((void *)0) + + //Delphi Types remember case insensitive + +#define single float +#define boolean int +#define Byte unsigned char // 0 to 255 +#define Word unsigned short // 0 to 65,535 +#define SmallInt short // -32,768 to 32,767 +#define LongWord unsigned int // 0 to 4,294,967,295 + // Int6 : Cardinal; // 0 to 4,294,967,295 +#define LongInt int // -2,147,483,648 to 2,147,483,647 +#define Integer int // -2,147,483,648 to 2,147,483,647 +//#define Int64 long long // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 + +//#define Byte unsigned char // 0 to 255 +#define word unsigned short // 0 to 65,535 +#define smallint short // -32,768 to 32,767 +#define longword unsigned int // 0 to 4,294,967,295 + // Int6 : Cardinal; // 0 to 4,294,967,295 +#define longint int // -2,147,483,648 to 2,147,483,647 +#define integer int // -2,147,483,648 to 2,147,483,647 + +typedef unsigned long ULONG; + +#define UCHAR unsigned char +#define UINT unsigned int +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +// Soundcard Channels + +#define NONE 0 +#define LEFT 1 +#define RIGHT 2 + +#define nr_emph 2 + +#define decodedNormal 4 //'|' +#define decodedFEC 3 //'F' +#define decodedMEM 2 //'#' +#define decodedSingle 1 //'$' + + +// Seems to use Delphi TStringList for a lot of queues. This seems to be a list of pointers and a count +// Each pointer is to a Data/Length pair +//Maybe something like + +typedef struct string_T +{ + unsigned char * Data; + int Length; + int AllocatedLength; // A reasonable sized block is allocated at the start to speed up adding chars + +}string; + +typedef struct TStringList_T +{ + int Count; + string ** Items; + +} TStringList; + +// QPSK struct + +typedef struct TQPSK_t +{ + UCHAR tx[4]; + int count[4]; + UCHAR rx[4]; + UCHAR mode; +} TPQSK; + + +typedef struct TKISSMode_t +{ + string * data_in; + void * Socket; // Used as a key + + // Not sure what rest are used for. Seems to be one per channel + + TStringList buffer[4]; // Outgoing Frames + +} TKISSMode; + +typedef struct TMChannel_t +{ + + single prev_LPF1I_buf[4096]; + single prev_LPF1Q_buf[4096]; + single prev_dLPFI_buf[4096]; + single prev_dLPFQ_buf[4096]; + single prev_AFCI_buf[4096]; + single prev_AFCQ_buf[4096]; + single AngleCorr; + single MUX_osc; + single AFC_IZ1; + single AFC_IZ2; + single AFC_QZ1; + single AFC_QZ2; + single AFC_bit_buf1I[1024]; + single AFC_bit_buf1Q[1024]; + single AFC_bit_buf2[1024]; + single AFC_IIZ1; + single AFC_QQZ1; + +} TMChannel; + +typedef struct TFX25_t +{ + string data; + Byte status; + Byte bit_cnt; + Byte byte_rx; + unsigned long long tag; + Byte size; + Byte rs_size; + Byte size_cnt; +} TFX25; + + + +typedef struct TDetector_t +{ + struct TFX25_t fx25[4]; + TStringList mem_ARQ_F_buf[5]; + TStringList mem_ARQ_buf[5]; + float pll_loop[5]; + float last_sample[5]; + UCHAR ones[5]; + UCHAR zeros[5]; + float bit_buf[5][1024]; + float bit_buf1[5][1024]; + UCHAR sample_cnt[5]; + UCHAR last_bit[5]; + float PSK_IZ1[5]; + float PSK_QZ1[5]; + float PkAmpI[5]; + float PkAmpQ[5]; + float PkAmp[5]; + float PkAmpMax[5]; + int newpkpos[5]; + float AverageAmp[5]; + float AngleCorr[5]; + float MinAmp[5]; + float MaxAmp[5]; + float MUX3_osc[5]; + float MUX3_1_osc[5]; + float MUX3_2_osc[5]; + float Preemphasis6[5]; + float Preemphasis12[5]; + float PSK_AGC[5]; + float AGC[5]; + float AGC1[5]; + float AGC2[5]; + float AGC3[5]; + float AGC_max[5]; + float AGC_min[5]; + float AFC_IZ1[5]; + float AFC_IZ2[5]; + float AFC_QZ1[5]; + float AFC_QZ2[5]; + + UCHAR last_rx_bit[5]; + UCHAR bit_stream[5]; + UCHAR byte_rx[5]; + UCHAR bit_stuff_cnt[5]; + UCHAR bit_cnt[5]; + float bit_osc[5]; + UCHAR frame_status[5]; + string rx_data[5]; + string FEC_rx_data[5]; + // + UCHAR FEC_pol[5]; + unsigned short FEC_err[5]; + unsigned long long FEC_header1[5][2]; + unsigned short FEC_blk_int[5]; + unsigned short FEC_len_int[5]; + unsigned short FEC_len[5]; + + unsigned short FEC_len_cnt[5]; + + UCHAR rx_intv_tbl[5][4]; + UCHAR rx_intv_sym[5]; + UCHAR rx_viterbi[5]; + UCHAR viterbi_cnt[5]; + // SurvivorStates [1..4,0..511] of TSurvivor; + // + TMChannel MChannel[5][4]; + + float AFC_dF_avg[5]; + float AFC_dF[5]; + float AFC_bit_osc[5]; + float AFC_bit_buf[5][1024]; + unsigned short AFC_cnt[5]; + + string raw_bits1[5]; + string raw_bits[5]; + UCHAR last_nrzi_bit[5]; + + float BPF_core[5][2048]; + float LPF_core[5][2048]; + + float src_INTR_buf[5][8192]; + float src_INTRI_buf[5][8192]; + float src_INTRQ_buf[5][8192]; + float src_LPF1I_buf[5][8192]; + float src_LPF1Q_buf[5][8192]; + + float src_BPF_buf[5][2048]; + float src_Loop_buf[5][8192]; + float prev_BPF_buf[5][4096]; + + float prev_LPF1I_buf[5][4096]; + float prev_LPF1Q_buf[5][4096]; + float prev_INTR_buf[5][16384]; + float prev_INTRI_buf[5][16384]; + float prev_INTRQ_buf[5][16384]; + + Byte emph_decoded; + Byte rx_decoded; + + +} TDetector; + + + +typedef struct AGWUser_t +{ + void *socket; + string * data_in; + TStringList AGW_frame_buf; + boolean Monitor; + boolean Monitor_raw; + boolean reportFreqAndModem; // Can report modem and frequency to host + +} AGWUser; + +typedef struct TAX25Info_t +{ + longint stat_s_pkt; + longint stat_s_byte; + longint stat_r_pkt; + longint stat_r_byte; + longint stat_r_fc; + longint stat_fec_count; + time_t stat_begin_ses; + time_t stat_end_ses; + longint stat_l_r_byte; + longint stat_l_s_byte; + +} TAX25Info; + +typedef struct TAX25Port_t +{ + Byte hi_vs; + Byte vs; + Byte vr; + Byte PID; + TStringList in_data_buf; + TStringList frm_collector; + string frm_win[8]; + string out_data_buf; + word t1; + word t2; + word t3; + Byte i_lo; + Byte i_hi; + word n1; + word n2; + word IPOLL_cnt; + TStringList frame_buf; //буфер кадров на передачу + TStringList I_frame_buf; + Byte status; + word clk_frack; + char corrcall[10]; + char mycall[10]; + UCHAR digi[56]; + UCHAR Path[80]; // Path in ax25 format - added to save building it each time + UCHAR ReversePath[80]; + int snd_ch; // Simplifies parameter passing + int port; + int pathLen; + void * socket; + char kind[16]; + TAX25Info info; +} TAX25Port; + + +#define LOGEMERGENCY 0 +#define LOGALERT 1 +#define LOGCRIT 2 +#define LOGERROR 3 +#define LOGWARNING 4 +#define LOGNOTICE 5 +#define LOGINFO 6 +#define LOGDEBUG 7 + +#define PTTRTS 1 +#define PTTDTR 2 +#define PTTCAT 4 +#define PTTCM108 8 +#define PTTHAMLIB 16 + +// Status flags + +#define STAT_NO_LINK 0 +#define STAT_LINK 1 +#define STAT_CHK_LINK 2 +#define STAT_WAIT_ANS 3 +#define STAT_TRY_LINK 4 +#define STAT_TRY_UNLINK 5 + + + // Сmd,Resp,Poll,Final,Digipeater flags +#define SET_P 1 +#define SET_F 0 +#define SET_C 1 +#define SET_R 0 +#define SET_NO_RPT 0 +#define SET_RPT 1 + // Frame ID flags +#define I_FRM 0 +#define S_FRM 1 +#define U_FRM 2 +#define I_I 0 +#define S_RR 1 +#define S_RNR 5 +#define S_REJ 9 +#define S_SREJ 0x0D +#define U_SABM 47 +#define U_DISC 67 +#define U_DM 15 +#define U_UA 99 +#define U_FRMR 135 +#define U_UI 3 + // PID flags +#define PID_X25 0x01 // 00000001-CCIT X25 PLP +#define PID_SEGMENT 0x08 // 00001000-Segmentation fragment +#define PID_TEXNET 0xC3 // 11000011-TEXNET Datagram Protocol +#define PID_LQ 0xC4 // 11001000-Link Quality Protocol +#define PID_APPLETALK 0xCA // 11001010-Appletalk +#define PID_APPLEARP 0xCB // 11001011-Appletalk ARP +#define PID_IP 0xCC // 11001100-ARPA Internet Protocol +#define PID_ARP 0xCD // 11001101-ARPA Address Resolution Protocol +#define PID_NET_ROM 0xCF // 11001111-NET/ROM + + +// Sound interface buffer size + +#define SendSize 1024 // 100 mS for now +#define ReceiveSize 512 // try 100 mS for now +#define NumberofinBuffers 4 + +#define Now getTicks() + +// #defines from all modules (?? is this a good idaa ?? + +#define WIN_MAXIMIZED 0 +#define WIN_MINIMIZED 1 +#define MODEM_CAPTION 'SoundModem by UZ7HO' +#define MODEM_VERSION '1.06' +#define SND_IDLE 0 +#define SND_RX 1 +#define SND_TX 2 +#define BUF_EMPTY 0 +#define BUF_FULL 1 +#define DISP_MONO FALSE +#define DISP_RGB TRUE +#define MOD_IDLE 0 +#define MOD_RX 1 +#define MOD_TX 2 +#define MOD_WAIT 3 +#define TIMER_FREE 0 +#define TIMER_BUSY 1 +#define TIMER_OFF 2 +#define TIMER_EVENT_ON 3 +#define TIMER_EVENT_OFF 4 +#define DEBUG_TIMER 1 +#define DEBUG_WATERFALL 2 +#define DEBUG_DECODE 4 +#define DEBUG_SOUND 8 +#define IS_LAST TRUE +#define IS_NOT_LAST FALSE +#define modes_count 16 +#define SPEED_300 0 +#define SPEED_1200 1 +#define SPEED_600 2 +#define SPEED_2400 3 +#define SPEED_P1200 4 +#define SPEED_P600 5 +#define SPEED_P300 6 +#define SPEED_P2400 7 +#define SPEED_Q4800 8 +#define SPEED_Q3600 9 +#define SPEED_Q2400 10 +#define SPEED_MP400 11 +#define SPEED_DW2400 12 +#define SPEED_8P4800 13 +#define SPEED_AE2400 14 +#define SPEED_ARDOP 15 + +#define MODE_FSK 0 +#define MODE_BPSK 1 +#define MODE_QPSK 2 +#define MODE_MPSK 3 +#define MODE_8PSK 4 +#define MODE_PI4QPSK 5 +#define MODE_ARDOP 6 + +#define QPSK_SM 0 +#define QPSK_V26 1 + + +#define MODEM_8P4800_BPF 3200 +#define MODEM_8P4800_TXBPF 3400 +#define MODEM_8P4800_LPF 1000 +#define MODEM_8P4800_BPF_TAP 64 +#define MODEM_8P4800_LPF_TAP 8 + // +#define MODEM_MP400_BPF 775 +#define MODEM_MP400_TXBPF 850 +#define MODEM_MP400_LPF 70 +#define MODEM_MP400_BPF_TAP 256 +#define MODEM_MP400_LPF_TAP 128 + // +#define MODEM_DW2400_BPF 2400 +#define MODEM_DW2400_TXBPF 2500 +#define MODEM_DW2400_LPF 900 +#define MODEM_DW2400_BPF_TAP 256 //256 +#define MODEM_DW2400_LPF_TAP 32 //128 + // +#define MODEM_Q2400_BPF 2400 +#define MODEM_Q2400_TXBPF 2500 +#define MODEM_Q2400_LPF 900 +#define MODEM_Q2400_BPF_TAP 256 //256 +#define MODEM_Q2400_LPF_TAP 128 //128 + // +#define MODEM_Q3600_BPF 3600 +#define MODEM_Q3600_TXBPF 3750 +#define MODEM_Q3600_LPF 1350 +#define MODEM_Q3600_BPF_TAP 256 +#define MODEM_Q3600_LPF_TAP 128 + // +#define MODEM_Q4800_BPF 4800 +#define MODEM_Q4800_TXBPF 5000 +#define MODEM_Q4800_LPF 1800 +#define MODEM_Q4800_BPF_TAP 256 +#define MODEM_Q4800_LPF_TAP 128 + // +#define MODEM_P2400_BPF 4800 +#define MODEM_P2400_TXBPF 5000 +#define MODEM_P2400_LPF 1800 +#define MODEM_P2400_BPF_TAP 256 +#define MODEM_P2400_LPF_TAP 128 + // +#define MODEM_P1200_BPF 2400 +#define MODEM_P1200_TXBPF 2500 +#define MODEM_P1200_LPF 900 +#define MODEM_P1200_BPF_TAP 256 +#define MODEM_P1200_LPF_TAP 128 + // +#define MODEM_P600_BPF 1200 +#define MODEM_P600_TXBPF 1250 +#define MODEM_P600_LPF 400 +#define MODEM_P600_BPF_TAP 256 +#define MODEM_P600_LPF_TAP 128 + // +#define MODEM_P300_BPF 600 +#define MODEM_P300_TXBPF 625 +#define MODEM_P300_LPF 200 +#define MODEM_P300_BPF_TAP 256 +#define MODEM_P300_LPF_TAP 128 + // +#define MODEM_300_BPF 500 +#define MODEM_300_TXBPF 500 +#define MODEM_300_LPF 155 +#define MODEM_300_BPF_TAP 256 +#define MODEM_300_LPF_TAP 128 + // +#define MODEM_600_BPF 800 +#define MODEM_600_TXBPF 900 +#define MODEM_600_LPF 325 +#define MODEM_600_BPF_TAP 256 +#define MODEM_600_LPF_TAP 128 + // +#define MODEM_1200_BPF 1400 +#define MODEM_1200_TXBPF 1600 +#define MODEM_1200_LPF 650 +#define MODEM_1200_BPF_TAP 256 +#define MODEM_1200_LPF_TAP 128 + // +#define MODEM_2400_BPF 3200 +#define MODEM_2400_TXBPF 3200 +#define MODEM_2400_LPF 1400 +#define MODEM_2400_BPF_TAP 256 +#define MODEM_2400_LPF_TAP 128 + +#define TX_SILENCE 0 +#define TX_DELAY 1 +#define TX_TAIL 2 +#define TX_NO_DATA 3 +#define TX_FRAME 4 +#define TX_WAIT_BPF 5 + + +#define FRAME_WAIT 0 +#define FRAME_LOAD 1 +#define RX_BIT0 0 +#define RX_BIT1 128 +#define DCD_WAIT_SLOT 0 +#define DCD_WAIT_PERSIST 1 + +#define FX25_MODE_NONE 0 +#define FX25_MODE_RX 1 +#define FX25_MODE_TXRX 2 +#define FX25_TAG 0 +#define FX25_LOAD 1 + +#define MODE_OUR 0 +#define MODE_OTHER 1 +#define MODE_RETRY 2 + +#define FRAME_FLAG 126 // 7e + +#define port_num 32 // ?? Max AGW sessions +#define PKT_ERR 17 // Minimum packet size, bytes +#define I_MAX 7 // Maximum number of packets + + + // externs for all modules + +#define ARDOPBufferSize 12000 * 100 + +extern short ARDOPTXBuffer[4][12000 * 100]; // Enough to hold whole frame of samples + +extern int ARDOPTXLen[4]; // Length of frame +extern int ARDOPTXPtr[4]; // Tx Pointer + +extern BOOL KISSServ; +extern int KISSPort; + +extern BOOL AGWServ; +extern int AGWPort; + +extern TStringList KISS_acked[]; +extern TStringList KISS_iacked[]; + +extern TStringList all_frame_buf[5]; + +extern unsigned short pkt_raw_min_len; +extern int stat_r_mem; + +extern UCHAR diddles; + +extern int stdtones; +extern int fullduplex; + +extern struct TQPSK_t qpsk_set[4]; + +extern int NonAX25[5]; + +extern short txtail[5]; +extern short txdelay[5]; + +extern short modem_def[5]; + +extern int emph_db[5]; +extern UCHAR emph_all[5]; + +extern UCHAR modem_mode[5]; + +extern UCHAR RCVR[5]; +extern int soundChannel[5]; +extern int modemtoSoundLR[4]; + +extern short rx_freq[5]; +extern short rx_shift[5]; +extern short rx_baudrate[5]; +extern short rcvr_offset[5]; + +extern int tx_hitoneraisedb[5]; +extern float tx_hitoneraise[5]; + + +extern UCHAR tx_status[5]; +extern float tx_freq[5]; +extern float tx_shift[5]; +extern unsigned short tx_baudrate[5]; + +extern unsigned short bpf[5]; +extern unsigned short lpf[5]; + +extern unsigned short txbpf[5]; + +extern unsigned short tx_BPF_tap[5]; +extern unsigned short tx_BPF_timer[5]; + +extern unsigned short BPF_tap[5]; +extern unsigned short LPF_tap[5]; + +extern float tx_BPF_core[5][32768]; +extern float LPF_core[5][2048]; + +extern UCHAR xData[256]; +extern UCHAR xEncoded[256]; +extern UCHAR xDecoded[256]; + +extern float PI125; +extern float PI375; +extern float PI625; +extern float PI875; +extern float PI5; +extern float PI25; +extern float PI75; + +extern int max_frame_collector[4]; +extern boolean KISS_opt[4]; + +#define MaxErrors 4 + +extern BOOL MinOnStart; + +//RS TReedSolomon; +// Form1 TForm1; +// WaveFormat TWaveFormatEx; + +extern int UDPServ; +extern long long udpServerSeqno; + +extern int Channels; +extern int BitsPerSample; +extern float TX_Samplerate; +extern float RX_Samplerate; +extern int RX_SR; +extern int TX_SR; +extern int RX_PPM; +extern int TX_PPM; +extern int tx_bufsize; +extern int rx_bufsize; +extern int tx_bufcount; +extern int rx_bufcount; +extern int fft_size; +extern int mouse_down[2]; +//UCHAR * RX_pBuf array[257]; +// RX_header array[1..256] of TWaveHdr; +// TX_pBuf array[1..4,1..256] of pointer; +//TX_header array[1..4,1..256] of TWaveHdr; +extern UCHAR calib_mode[5]; +extern UCHAR snd_status[5]; +extern UCHAR buf_status[5]; +extern UCHAR tx_buf_num1[5]; +extern UCHAR tx_buf_num[5]; +extern int speed[5]; +extern int panels[6]; + +extern float fft_window_arr[2048]; +// fft_s,fft_d array[0..2047] of TComplex; +extern short fft_buf[5][2048]; +extern UCHAR fft_disp[5][2048]; +// bm array[1..4] of TBitMap; +// bm1,bm2,bm3 TBitMap; + +// WaveInHandle hWaveIn; +// WaveOutHandle array[1..4] of hWaveOut; +extern int RXBufferLength; + +// data1 PData16; + +extern int grid_time; +extern int fft_mult; +extern int fft_spd; +extern int grid_timer; +extern int stop_wf; +extern int raduga; +extern char snd_rx_device_name[32]; +extern char snd_tx_device_name[32]; +extern int snd_rx_device; +extern int snd_tx_device; +extern UCHAR mod_icon_status; +extern UCHAR last_mod_icon_status; +extern UCHAR icon_timer; +// TelIni TIniFile; +extern char cur_dir[]; +// TimerId1 cardinal; +// TimerId2 cardinal; +extern UCHAR TimerStat1; +extern UCHAR TimerStat2; +extern int stat_log; + +extern char PTTPort[80]; // Port for Hardware PTT - may be same as control port. +extern int PTTMode; +extern int PTTBAUD ; + +extern char PTTOnString[128]; +extern char PTTOffString[128]; + +extern UCHAR PTTOnCmd[64]; +extern UCHAR PTTOnCmdLen; + +extern UCHAR PTTOffCmd[64]; +extern UCHAR PTTOffCmdLen; + +extern int PTT_device; +extern int RX_device; +extern int TX_device; +extern int TX_rotate; +extern int UsingLeft; +extern int UsingRight; +extern int UsingBothChannels; +extern int pttGPIOPin; +extern int pttGPIOPinR; +extern BOOL pttGPIOInvert; +extern BOOL useGPIO; +extern BOOL gotGPIO; +extern int VID; +extern int PID; +extern char CM108Addr[80]; +extern int HamLibPort; +extern char HamLibHost[]; + +extern int SCO; +extern int DualPTT; +extern UCHAR DebugMode; +extern UCHAR TimerEvent; +extern int nr_monitor_lines; +extern int UTC_Tim; +extern int MainPriority; +// MainThreadHandle THandle; +extern UCHAR w_state; + +extern BOOL Firstwaterfall; +extern BOOL Secondwaterfall; + +extern int dcd_threshold; +extern int rxOffset; +extern int chanOffset[4]; + +extern boolean busy; +extern boolean dcd[5]; + +extern struct TKISSMode_t KISS; + +extern boolean dyn_frack[4] ; +extern Byte recovery[4]; +extern Byte users[4]; + +extern int resptime[4]; +extern int slottime[4]; +extern int persist[4]; +extern int fracks[4]; +extern int frack_time[4]; +extern int idletime[4]; +extern int redtime[4]; +extern int IPOLL[4]; +extern int maxframe[4]; +extern int TXFrmMode[4]; + +extern char MyDigiCall[4][512]; +extern char exclude_callsigns[4][512]; +extern char exclude_APRS_frm[4][512]; + +extern TStringList list_exclude_callsigns[4]; +extern TStringList list_exclude_APRS_frm[4]; +extern TStringList list_digi_callsigns[4]; + + +extern int SoundIsPlaying; +extern int Capturing; + +extern struct TDetector_t DET[nr_emph + 1][16]; + +extern char CaptureDevice[80]; +extern char PlaybackDevice[80]; + +extern TAX25Port AX25Port[4][port_num]; + +extern int fx25_mode[4]; + +extern int tx_fx25_size[4]; +extern int tx_fx25_size_cnt[4]; +extern int tx_fx25_mode[4]; + +extern int SatelliteMode; + +// Function prototypes + +void KISS_send_ack(UCHAR port, string * data); +void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame); +void FIR_filter(float * src, unsigned short buf_size, unsigned short tap, float * core, float * dest, float * prev); +void make_core_TXBPF(UCHAR snd_ch, float freq, float width); +void OpenPTTPort(); +void ClosePTTPort(); + +void RadioPTT(int snd_ch, BOOL PTTState); +void put_frame(int snd_ch, string * frame, char * code, int tx_stat, int excluded); +void CloseCOMPort(int fd); +void COMClearRTS(int fd); +void COMClearDTR(int fd); +unsigned int getTicks(); +char * ShortDateTime(); +void write_ax25_info(TAX25Port * AX25Sess); +void reverse_addr(Byte * path, Byte * revpath, int Len); +string * get_mycall(string * path); +TAX25Port * get_user_port_by_calls(int snd_ch, char * CallFrom, char * CallTo); +TAX25Port * get_free_port(int snd_ch); +void * in_list_incoming_mycall(Byte * path); +boolean add_incoming_mycalls(void * socket, char * src_call); +int get_addr(char * Calls, UCHAR * AXCalls); +void reverse_addr(Byte * path, Byte * revpath, int Len); +void set_link(TAX25Port * AX25Sess, UCHAR * axpath); +void rst_timer(TAX25Port * AX25Sess); +void set_unlink(TAX25Port * AX25Sess, Byte * path); +unsigned short get_fcs(UCHAR * Data, unsigned short len); +void KISSSendtoServer(void * sock, Byte * Msg, int Len); +int ConvFromAX25(unsigned char * incall, char * outcall); +BOOL ConvToAX25(char * callsign, unsigned char * ax25call); +void Debugprintf(const char * format, ...); + +double pila(double x); + +void AGW_Raw_monitor(int snd_ch, string * data); + +// Dephi emulation functions + +string * Strings(TStringList * Q, int Index); +void Clear(TStringList * Q); +int Count(TStringList * List); + +string * newString(); +string * copy(string * Source, int StartChar, int Count); +TStringList * newTStringList(); + +void freeString(string * Msg); + +void initString(string * S); +void initTStringList(TStringList* T); + +// Two delete() This is confusing!! +// Not really - one acts on String, other TStringList + +void Delete(TStringList * Q, int Index); +void mydelete(string * Source, int StartChar, int Count); + +void move(UCHAR * SourcePointer, UCHAR * DestinationPointer, int CopyCount); +void fmove(float * SourcePointer, float * DestinationPointer, int CopyCount); + +void setlength(string * Msg, int Count); // Set string length + +string * stringAdd(string * Msg, UCHAR * Chars, int Count); // Extend string + +void Assign(TStringList * to, TStringList * from); // Duplicate from to to + +string * duplicateString(string * in); + +// This looks for a string in a stringlist. Returns inhex if found, otherwise -1 + +int my_indexof(TStringList * l, string * s); + +boolean compareStrings(string * a, string * b); + +int Add(TStringList * Q, string * Entry); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/UZ7HOStuff.h b/UZ7HOStuff.h new file mode 100644 index 0000000..d98da89 --- /dev/null +++ b/UZ7HOStuff.h @@ -0,0 +1,1058 @@ +// +// My port of UZ7HO's Soundmodem +// + +#define VersionString "0.0.0.66" +#define VersionBytes {0, 0, 0, 66} + +// Added FX25. 4x100 FEC and V27 not Working and disabled + +// 0.8 V27 now OK. + +// 0.9 Digipeating added + +// 0.10 Fix second channel tones and calibrate + +// 0.11 Fix allocation of sessions to correct modem +// Fix DCD +// Fix Monitoring of Multiline packets +// Fix possible saving of wrong center freq +// Limit TX sample Q in Linux +// + +// 0.12 Add AGWPE monitoring of received frames +// Fix DCD Threshold +// Fix KISS transparency issue + +// 0.13 Fix sending last few bits in FX.25 Mode + +// 0.14 Add "Copy on Select" to Trace Window + +// 0.15 Limit Trace window to 10000 lines + +// 0.16 Fix overwriting monitor window after scrollback + +// 0.17 Add GPIO and CAT PTT + +// 0.18 Add CM108/119 PTT + +// 0.19 Fix scheduling KISS frames + +// 0.20 Debug code added to RR processing + +// 0.21 Fix AGW monitor of multiple line packets +// Close ax.25 sessions if AGW Host session closes + +// 0.22 Add FEC Count to Session Stats + +// 0.23 Retry DISC until UA received or retry count exceeded + +// 0.24 More fixes to DISC handling + +// 0.26 Add OSS PulseAudio and HAMLIB support + +// 0.27 Dynamically load PulseAudio modules + +// 0.28 Add ARDOPPacket Mode + +// 0.29 Fix saving settings and geometry on close +// 0.30 Retructure code to build with Qt 5.3 +// Fix crash in nogui mode if pulse requested but not available +// Try to fix memory leaks + +// 0.31 Add option to run modems in seprate threads + +// 0.32 Fix timing problem with AGW connect at startup +// Add Memory ARQ +// Add Single bit "Correction" +// Fix error in 31 when using multiple decoders + +// 0.33 Fix Single bit correction +// More memory leak fixes + +// 0.34 Add API to set Modem and Center Frequency +// Fix crash in delete_incoming_mycalls + +// 0.35 Return Version in AGW Extended g response + +// 0.36 Fix timing problem on startup + +// 0.37 Add scrollbars to Device and Modem dialogs + +// 0.38 Change default CM108 name to /dev/hidraw0 on Linux + +// 0.39 Dont try to display Message Boxes in nogui mode. +// Close Device and Modem dialogs on Accept or Reject +// Fix using HAMLIB in nogui mode + +// 0.40 Fix bug in frame optimize when using 6 char calls + +// 0.41 Fix "glitch" on waterfall markers when changing modem freqs + +// 0.42 Add "Minimize to Tray" option + +// 0.43 Add Andy's on_SABM fix. +// Fix Crash if KISS Data sent to AGW port + +// 0.44 Add UDP bridge. + +// 0.45 Add two more modems. +// 0.46 Fix two more modems. + +// 0.47 Fix suprious DM when host connection lost +// Add CWID + +// 0.48 Send FRMR for unrecognised frame types + +// 0.49 Add Andy's FEC Tag correlation coode + +// 0.50 Fix Waterfall display when only using right channel +// Allow 1200 baud fsk at other center freqs +// Add Port numbers to Window title and Try Icon tooltip +// Fix calculation of filters for multiple decoders +// Add RX Offset setting (for satellite operation + +// 0.51 Fix Multithreading with more that 2 modems + +// 0.52 Add Stdin as source on Linux + +// 0.53 Use Byte instead of byte as byte is defined in newer versions of gcc + +// 0.54 Fix for ALSA problem on new pi OS + +// 0.55 Fix for compiler error with newer compiler + +// 0.56 Fix errors in Config.cpp June 22 + +// 0.57 Add Restart Waterfall action August 22 + +// 0.58 Add RSID Sept 2022 + +// 0.59 Add config of Digi Calls Dec 2022 + +// 0.60 Allow ARDOP Packet on modems 2 to 4 March 2023 + +// 0.61 Add il2p support April 2023 + +// 0.62 April 2023 +// Add option to specify sound devices that aren't in list +// Add Save button to Modem dialog to save current tab without closing dialog +// Don't add plug: to Linux device addresses unless addr contains : (allows use of eg ARDOP) + +// 0.64 Fix sending ax.25 (broken in .61) + +// 0.65 Allow Set Modem command to use modem index as well as modem name + +// 0.66 Allow configuration of waterfall span June 23 +// Add Exclude + + + + + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define UNUSED(x) (void)(x) + +#ifdef M_PI +#undef M_PI +#endif + +#define M_PI 3.1415926f + +#define pi M_PI + +#ifndef WIN32 +#define _strdup strdup +#endif + + //#define NULL ((void *)0) + + //Delphi Types remember case insensitive + +#define single float +#define boolean int +#define Byte unsigned char // 0 to 255 +#define Word unsigned short // 0 to 65,535 +#define SmallInt short // -32,768 to 32,767 +#define LongWord unsigned int // 0 to 4,294,967,295 + // Int6 : Cardinal; // 0 to 4,294,967,295 +#define LongInt int // -2,147,483,648 to 2,147,483,647 +#define Integer int // -2,147,483,648 to 2,147,483,647 +//#define Int64 long long // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 + +//#define Byte unsigned char // 0 to 255 +#define word unsigned short // 0 to 65,535 +#define smallint short // -32,768 to 32,767 +#define longword unsigned int // 0 to 4,294,967,295 + // Int6 : Cardinal; // 0 to 4,294,967,295 +#define longint int // -2,147,483,648 to 2,147,483,647 +#define integer int // -2,147,483,648 to 2,147,483,647 + +typedef unsigned long ULONG; + +#define UCHAR unsigned char +#define UINT unsigned int +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +// Soundcard Channels + +#define NONE 0 +#define LEFT 1 +#define RIGHT 2 + +#define nr_emph 2 + +#define decodedNormal 4 //'-' +#define decodedFEC 3 //'F' +#define decodedMEM 2 //'#' +#define decodedSingle 1 //'$' + + +// Think about implications of changing this !! +extern int FFTSize; + +// Seems to use Delphi TStringList for a lot of queues. This seems to be a list of pointers and a count +// Each pointer is to a Data/Length pair +//Maybe something like + +typedef struct string_T +{ + unsigned char * Data; + int Length; + int AllocatedLength; // A reasonable sized block is allocated at the start to speed up adding chars + +}string; + +typedef struct TStringList_T +{ + int Count; + string ** Items; + +} TStringList; + +// QPSK struct + +typedef struct TQPSK_t +{ + UCHAR tx[4]; + int count[4]; + UCHAR rx[4]; + UCHAR mode; +} TPQSK; + + +typedef struct TKISSMode_t +{ + string * data_in; + void * Socket; // Used as a key + + // Not sure what rest are used for. Seems to be one per channel + + TStringList buffer[4]; // Outgoing Frames + +} TKISSMode; + +typedef struct TMChannel_t +{ + + single prev_LPF1I_buf[4096]; + single prev_LPF1Q_buf[4096]; + single prev_dLPFI_buf[4096]; + single prev_dLPFQ_buf[4096]; + single prev_AFCI_buf[4096]; + single prev_AFCQ_buf[4096]; + single AngleCorr; + single MUX_osc; + single AFC_IZ1; + single AFC_IZ2; + single AFC_QZ1; + single AFC_QZ2; + single AFC_bit_buf1I[1024]; + single AFC_bit_buf1Q[1024]; + single AFC_bit_buf2[1024]; + single AFC_IIZ1; + single AFC_QQZ1; + +} TMChannel; + +typedef struct TFX25_t +{ + string data; + Byte status; + Byte bit_cnt; + Byte byte_rx; + unsigned long long tag; + Byte size; + Byte rs_size; + Byte size_cnt; +} TFX25; + + + +typedef struct TDetector_t +{ + struct TFX25_t fx25[4]; + TStringList mem_ARQ_F_buf[5]; + TStringList mem_ARQ_buf[5]; + float pll_loop[5]; + float last_sample[5]; + UCHAR ones[5]; + UCHAR zeros[5]; + float bit_buf[5][1024]; + float bit_buf1[5][1024]; + UCHAR sample_cnt[5]; + UCHAR last_bit[5]; + float PSK_IZ1[5]; + float PSK_QZ1[5]; + float PkAmpI[5]; + float PkAmpQ[5]; + float PkAmp[5]; + float PkAmpMax[5]; + int newpkpos[5]; + float AverageAmp[5]; + float AngleCorr[5]; + float MinAmp[5]; + float MaxAmp[5]; + float MUX3_osc[5]; + float MUX3_1_osc[5]; + float MUX3_2_osc[5]; + float Preemphasis6[5]; + float Preemphasis12[5]; + float PSK_AGC[5]; + float AGC[5]; + float AGC1[5]; + float AGC2[5]; + float AGC3[5]; + float AGC_max[5]; + float AGC_min[5]; + float AFC_IZ1[5]; + float AFC_IZ2[5]; + float AFC_QZ1[5]; + float AFC_QZ2[5]; + + UCHAR last_rx_bit[5]; + UCHAR bit_stream[5]; + UCHAR byte_rx[5]; + UCHAR bit_stuff_cnt[5]; + UCHAR bit_cnt[5]; + float bit_osc[5]; + UCHAR frame_status[5]; + string rx_data[5]; + string FEC_rx_data[5]; + // + UCHAR FEC_pol[5]; + unsigned short FEC_err[5]; + unsigned long long FEC_header1[5][2]; + unsigned short FEC_blk_int[5]; + unsigned short FEC_len_int[5]; + unsigned short FEC_len[5]; + + unsigned short FEC_len_cnt[5]; + + UCHAR rx_intv_tbl[5][4]; + UCHAR rx_intv_sym[5]; + UCHAR rx_viterbi[5]; + UCHAR viterbi_cnt[5]; + // SurvivorStates [1..4,0..511] of TSurvivor; + // + TMChannel MChannel[5][4]; + + float AFC_dF_avg[5]; + float AFC_dF[5]; + float AFC_bit_osc[5]; + float AFC_bit_buf[5][1024]; + unsigned short AFC_cnt[5]; + + string raw_bits1[5]; + string raw_bits[5]; + UCHAR last_nrzi_bit[5]; + + float BPF_core[5][2048]; + float LPF_core[5][2048]; + + float src_INTR_buf[5][8192]; + float src_INTRI_buf[5][8192]; + float src_INTRQ_buf[5][8192]; + float src_LPF1I_buf[5][8192]; + float src_LPF1Q_buf[5][8192]; + + float src_BPF_buf[5][2048]; + float src_Loop_buf[5][8192]; + float prev_BPF_buf[5][4096]; + + float prev_LPF1I_buf[5][4096]; + float prev_LPF1Q_buf[5][4096]; + float prev_INTR_buf[5][16384]; + float prev_INTRI_buf[5][16384]; + float prev_INTRQ_buf[5][16384]; + + Byte emph_decoded; + Byte rx_decoded; + Byte errors; + + +} TDetector; + + + +typedef struct AGWUser_t +{ + void *socket; + string * data_in; + TStringList AGW_frame_buf; + boolean Monitor; + boolean Monitor_raw; + boolean reportFreqAndModem; // Can report modem and frequency to host + +} AGWUser; + +typedef struct TAX25Info_t +{ + longint stat_s_pkt; + longint stat_s_byte; + longint stat_r_pkt; + longint stat_r_byte; + longint stat_r_fc; + longint stat_fec_count; + time_t stat_begin_ses; + time_t stat_end_ses; + longint stat_l_r_byte; + longint stat_l_s_byte; + +} TAX25Info; + +typedef struct TAX25Port_t +{ + Byte hi_vs; + Byte vs; + Byte vr; + Byte PID; + TStringList in_data_buf; + TStringList frm_collector; + string frm_win[8]; + string out_data_buf; + word t1; + word t2; + word t3; + Byte i_lo; + Byte i_hi; + word n1; + word n2; + word IPOLL_cnt; + TStringList frame_buf; //буфер кадров на передачу + TStringList I_frame_buf; + Byte status; + word clk_frack; + char corrcall[10]; + char mycall[10]; + UCHAR digi[56]; + UCHAR Path[80]; // Path in ax25 format - added to save building it each time + UCHAR ReversePath[80]; + int snd_ch; // Simplifies parameter passing + int port; + int pathLen; + void * socket; + char kind[16]; + TAX25Info info; +} TAX25Port; + + +#define LOGEMERGENCY 0 +#define LOGALERT 1 +#define LOGCRIT 2 +#define LOGERROR 3 +#define LOGWARNING 4 +#define LOGNOTICE 5 +#define LOGINFO 6 +#define LOGDEBUG 7 + +#define PTTRTS 1 +#define PTTDTR 2 +#define PTTCAT 4 +#define PTTCM108 8 +#define PTTHAMLIB 16 + +// Status flags + +#define STAT_NO_LINK 0 +#define STAT_LINK 1 +#define STAT_CHK_LINK 2 +#define STAT_WAIT_ANS 3 +#define STAT_TRY_LINK 4 +#define STAT_TRY_UNLINK 5 + + + // Сmd,Resp,Poll,Final,Digipeater flags +#define SET_P 1 +#define SET_F 0 +#define SET_C 1 +#define SET_R 0 +#define SET_NO_RPT 0 +#define SET_RPT 1 + // Frame ID flags +#define I_FRM 0 +#define S_FRM 1 +#define U_FRM 2 +#define I_I 0 +#define S_RR 1 +#define S_RNR 5 +#define S_REJ 9 +#define S_SREJ 0x0D +#define U_SABM 47 +#define U_DISC 67 +#define U_DM 15 +#define U_UA 99 +#define U_FRMR 135 +#define U_UI 3 + // PID flags +#define PID_X25 0x01 // 00000001-CCIT X25 PLP +#define PID_SEGMENT 0x08 // 00001000-Segmentation fragment +#define PID_TEXNET 0xC3 // 11000011-TEXNET Datagram Protocol +#define PID_LQ 0xC4 // 11001000-Link Quality Protocol +#define PID_APPLETALK 0xCA // 11001010-Appletalk +#define PID_APPLEARP 0xCB // 11001011-Appletalk ARP +#define PID_IP 0xCC // 11001100-ARPA Internet Protocol +#define PID_ARP 0xCD // 11001101-ARPA Address Resolution Protocol +#define PID_NET_ROM 0xCF // 11001111-NET/ROM + + +// Sound interface buffer size + +#define SendSize 1024 // 100 mS for now +#define ReceiveSize 512 // try 100 mS for now +#define NumberofinBuffers 4 + +#define Now getTicks() + +// #defines from all modules (?? is this a good idaa ?? + +#define WIN_MAXIMIZED 0 +#define WIN_MINIMIZED 1 +#define MODEM_CAPTION 'SoundModem by UZ7HO' +#define MODEM_VERSION '1.06' +#define SND_IDLE 0 +#define SND_RX 1 +#define SND_TX 2 +#define BUF_EMPTY 0 +#define BUF_FULL 1 +#define DISP_MONO FALSE +#define DISP_RGB TRUE +#define MOD_IDLE 0 +#define MOD_RX 1 +#define MOD_TX 2 +#define MOD_WAIT 3 +#define TIMER_FREE 0 +#define TIMER_BUSY 1 +#define TIMER_OFF 2 +#define TIMER_EVENT_ON 3 +#define TIMER_EVENT_OFF 4 +#define DEBUG_TIMER 1 +#define DEBUG_WATERFALL 2 +#define DEBUG_DECODE 4 +#define DEBUG_SOUND 8 +#define IS_LAST TRUE +#define IS_NOT_LAST FALSE +#define modes_count 16 +#define SPEED_300 0 +#define SPEED_1200 1 +#define SPEED_600 2 +#define SPEED_2400 3 +#define SPEED_P1200 4 +#define SPEED_P600 5 +#define SPEED_P300 6 +#define SPEED_P2400 7 +#define SPEED_Q4800 8 +#define SPEED_Q3600 9 +#define SPEED_Q2400 10 +#define SPEED_MP400 11 +#define SPEED_DW2400 12 +#define SPEED_8P4800 13 +#define SPEED_AE2400 14 +#define SPEED_ARDOP 15 + +#define MODE_FSK 0 +#define MODE_BPSK 1 +#define MODE_QPSK 2 +#define MODE_MPSK 3 +#define MODE_8PSK 4 +#define MODE_PI4QPSK 5 +#define MODE_ARDOP 6 + +#define QPSK_SM 0 +#define QPSK_V26 1 + + +#define MODEM_8P4800_BPF 3200 +#define MODEM_8P4800_TXBPF 3400 +#define MODEM_8P4800_LPF 1000 +#define MODEM_8P4800_BPF_TAP 64 +#define MODEM_8P4800_LPF_TAP 8 + // +#define MODEM_MP400_BPF 775 +#define MODEM_MP400_TXBPF 850 +#define MODEM_MP400_LPF 70 +#define MODEM_MP400_BPF_TAP 256 +#define MODEM_MP400_LPF_TAP 128 + // +#define MODEM_DW2400_BPF 2400 +#define MODEM_DW2400_TXBPF 2500 +#define MODEM_DW2400_LPF 900 +#define MODEM_DW2400_BPF_TAP 256 //256 +#define MODEM_DW2400_LPF_TAP 32 //128 + // +#define MODEM_Q2400_BPF 2400 +#define MODEM_Q2400_TXBPF 2500 +#define MODEM_Q2400_LPF 900 +#define MODEM_Q2400_BPF_TAP 256 //256 +#define MODEM_Q2400_LPF_TAP 128 //128 + // +#define MODEM_Q3600_BPF 3600 +#define MODEM_Q3600_TXBPF 3750 +#define MODEM_Q3600_LPF 1350 +#define MODEM_Q3600_BPF_TAP 256 +#define MODEM_Q3600_LPF_TAP 128 + // +#define MODEM_Q4800_BPF 4800 +#define MODEM_Q4800_TXBPF 5000 +#define MODEM_Q4800_LPF 1800 +#define MODEM_Q4800_BPF_TAP 256 +#define MODEM_Q4800_LPF_TAP 128 + // +#define MODEM_P2400_BPF 4800 +#define MODEM_P2400_TXBPF 5000 +#define MODEM_P2400_LPF 1800 +#define MODEM_P2400_BPF_TAP 256 +#define MODEM_P2400_LPF_TAP 128 + // +#define MODEM_P1200_BPF 2400 +#define MODEM_P1200_TXBPF 2500 +#define MODEM_P1200_LPF 900 +#define MODEM_P1200_BPF_TAP 256 +#define MODEM_P1200_LPF_TAP 128 + // +#define MODEM_P600_BPF 1200 +#define MODEM_P600_TXBPF 1250 +#define MODEM_P600_LPF 400 +#define MODEM_P600_BPF_TAP 256 +#define MODEM_P600_LPF_TAP 128 + // +#define MODEM_P300_BPF 600 +#define MODEM_P300_TXBPF 625 +#define MODEM_P300_LPF 200 +#define MODEM_P300_BPF_TAP 256 +#define MODEM_P300_LPF_TAP 128 + // +#define MODEM_300_BPF 500 +#define MODEM_300_TXBPF 500 +#define MODEM_300_LPF 155 +#define MODEM_300_BPF_TAP 256 +#define MODEM_300_LPF_TAP 128 + // +#define MODEM_600_BPF 800 +#define MODEM_600_TXBPF 900 +#define MODEM_600_LPF 325 +#define MODEM_600_BPF_TAP 256 +#define MODEM_600_LPF_TAP 128 + // +#define MODEM_1200_BPF 1400 +#define MODEM_1200_TXBPF 1600 +#define MODEM_1200_LPF 650 +#define MODEM_1200_BPF_TAP 256 +#define MODEM_1200_LPF_TAP 128 + // +#define MODEM_2400_BPF 3200 +#define MODEM_2400_TXBPF 3200 +#define MODEM_2400_LPF 1400 +#define MODEM_2400_BPF_TAP 256 +#define MODEM_2400_LPF_TAP 128 + +#define TX_SILENCE 0 +#define TX_DELAY 1 +#define TX_TAIL 2 +#define TX_NO_DATA 3 +#define TX_FRAME 4 +#define TX_WAIT_BPF 5 + + +#define FRAME_WAIT 0 +#define FRAME_LOAD 1 +#define RX_BIT0 0 +#define RX_BIT1 128 +#define DCD_WAIT_SLOT 0 +#define DCD_WAIT_PERSIST 1 + +#define FX25_MODE_NONE 0 +#define FX25_MODE_RX 1 +#define FX25_MODE_TXRX 2 +#define FX25_TAG 0 +#define FX25_LOAD 1 + +#define IL2P_MODE_NONE 0 +#define IL2P_MODE_RX 1 // RX il2p + HDLC +#define IL2P_MODE_TXRX 2 +#define IL2P_MODE_ONLY 3 // RX only il2p, TX il2p + + +#define MODE_OUR 0 +#define MODE_OTHER 1 +#define MODE_RETRY 2 + +#define FRAME_FLAG 126 // 7e + +#define port_num 32 // ?? Max AGW sessions +#define PKT_ERR 17 // Minimum packet size, bytes +#define I_MAX 7 // Maximum number of packets + + + // externs for all modules + +#define ARDOPBufferSize 12000 * 100 + +extern short ARDOPTXBuffer[4][12000 * 100]; // Enough to hold whole frame of samples + +extern int ARDOPTXLen[4]; // Length of frame +extern int ARDOPTXPtr[4]; // Tx Pointer + +extern BOOL KISSServ; +extern int KISSPort; + +extern BOOL AGWServ; +extern int AGWPort; + +extern TStringList KISS_acked[]; +extern TStringList KISS_iacked[]; + +extern TStringList all_frame_buf[5]; + +extern unsigned short pkt_raw_min_len; +extern int stat_r_mem; + +extern UCHAR diddles; + +extern int stdtones; +extern int fullduplex; + +extern struct TQPSK_t qpsk_set[4]; + +extern int NonAX25[5]; + +extern short txtail[5]; +extern short txdelay[5]; + +extern short modem_def[5]; + +extern int emph_db[5]; +extern UCHAR emph_all[5]; + +extern UCHAR modem_mode[5]; + +extern UCHAR RCVR[5]; +extern int soundChannel[5]; +extern int modemtoSoundLR[4]; + +extern short rx_freq[5]; +extern short active_rx_freq[5]; +extern short rx_shift[5]; +extern short rx_baudrate[5]; +extern short rcvr_offset[5]; + +extern int tx_hitoneraisedb[5]; +extern float tx_hitoneraise[5]; + + +extern UCHAR tx_status[5]; +extern float tx_freq[5]; +extern float tx_shift[5]; +extern unsigned short tx_baudrate[5]; + +extern unsigned short bpf[5]; +extern unsigned short lpf[5]; + +extern unsigned short txbpf[5]; + +extern unsigned short tx_BPF_tap[5]; +extern unsigned short tx_BPF_timer[5]; + +extern unsigned short BPF_tap[5]; +extern unsigned short LPF_tap[5]; + +extern float tx_BPF_core[5][32768]; +extern float LPF_core[5][2048]; + +extern UCHAR xData[256]; +extern UCHAR xEncoded[256]; +extern UCHAR xDecoded[256]; + +extern float PI125; +extern float PI375; +extern float PI625; +extern float PI875; +extern float PI5; +extern float PI25; +extern float PI75; + +extern int max_frame_collector[4]; +extern boolean KISS_opt[4]; + +#define MaxErrors 4 + +extern BOOL MinOnStart; + +//RS TReedSolomon; +// Form1 TForm1; +// WaveFormat TWaveFormatEx; + +extern int UDPServ; +extern long long udpServerSeqno; + +extern int Channels; +extern int BitsPerSample; +extern float TX_Samplerate; +extern float RX_Samplerate; +extern int RX_SR; +extern int TX_SR; +extern int RX_PPM; +extern int TX_PPM; +extern int tx_bufsize; +extern int rx_bufsize; +extern int tx_bufcount; +extern int rx_bufcount; +extern int fft_size; +extern int mouse_down[2]; +//UCHAR * RX_pBuf array[257]; +// RX_header array[1..256] of TWaveHdr; +// TX_pBuf array[1..4,1..256] of pointer; +//TX_header array[1..4,1..256] of TWaveHdr; +extern UCHAR calib_mode[5]; +extern UCHAR snd_status[5]; +extern UCHAR buf_status[5]; +extern UCHAR tx_buf_num1[5]; +extern UCHAR tx_buf_num[5]; +extern int speed[5]; +extern int panels[6]; + +extern int FFTSize; +#define fft_size FFTSize + +extern float fft_window_arr[2048]; +// fft_s,fft_d array[0..2047] of TComplex; +extern short fft_buf[2][8192]; +extern UCHAR fft_disp[2][1024]; +// bm array[1..4] of TBitMap; +// bm1,bm2,bm3 TBitMap; + +// WaveInHandle hWaveIn; +// WaveOutHandle array[1..4] of hWaveOut; +extern int RXBufferLength; + +// data1 PData16; + +extern int grid_time; +extern int fft_mult; +extern int fft_spd; +extern int grid_timer; +extern int stop_wf; +extern int raduga; +extern char snd_rx_device_name[32]; +extern char snd_tx_device_name[32]; +extern int snd_rx_device; +extern int snd_tx_device; +extern UCHAR mod_icon_status; +extern UCHAR last_mod_icon_status; +extern UCHAR icon_timer; +// TelIni TIniFile; +extern char cur_dir[]; +// TimerId1 cardinal; +// TimerId2 cardinal; +extern UCHAR TimerStat1; +extern UCHAR TimerStat2; +extern int stat_log; + +extern char PTTPort[80]; // Port for Hardware PTT - may be same as control port. +extern int PTTMode; +extern int PTTBAUD ; + +extern char PTTOnString[128]; +extern char PTTOffString[128]; + +extern UCHAR PTTOnCmd[64]; +extern UCHAR PTTOnCmdLen; + +extern UCHAR PTTOffCmd[64]; +extern UCHAR PTTOffCmdLen; + +extern int PTT_device; +extern int RX_device; +extern int TX_device; +extern int TX_rotate; +extern int UsingLeft; +extern int UsingRight; +extern int UsingBothChannels; +extern int pttGPIOPin; +extern int pttGPIOPinR; +extern BOOL pttGPIOInvert; +extern BOOL useGPIO; +extern BOOL gotGPIO; +extern int VID; +extern int PID; +extern char CM108Addr[80]; +extern int HamLibPort; +extern char HamLibHost[]; + +extern int SCO; +extern int DualPTT; +extern UCHAR DebugMode; +extern UCHAR TimerEvent; +extern int nr_monitor_lines; +extern int UTC_Tim; +extern int MainPriority; +// MainThreadHandle THandle; +extern UCHAR w_state; + +extern BOOL Firstwaterfall; +extern BOOL Secondwaterfall; + +extern int dcd_threshold; +extern int rxOffset; +extern int chanOffset[4]; + +extern boolean busy; +extern boolean dcd[5]; + +extern struct TKISSMode_t KISS; + +extern boolean dyn_frack[4] ; +extern Byte recovery[4]; +extern Byte users[4]; + +extern int resptime[4]; +extern int slottime[4]; +extern int persist[4]; +extern int fracks[4]; +extern int frack_time[4]; +extern int idletime[4]; +extern int redtime[4]; +extern int IPOLL[4]; +extern int maxframe[4]; +extern int TXFrmMode[4]; + +extern char MyDigiCall[4][512]; +extern char exclude_callsigns[4][512]; +extern char exclude_APRS_frm[4][512]; + +extern TStringList list_exclude_callsigns[4]; +extern TStringList list_exclude_APRS_frm[4]; +extern TStringList list_digi_callsigns[4]; + + +extern int SoundIsPlaying; +extern int Capturing; + +extern struct TDetector_t DET[nr_emph + 1][16]; + +extern char CaptureDevice[80]; +extern char PlaybackDevice[80]; + +extern TAX25Port AX25Port[4][port_num]; + +extern int fx25_mode[4]; +extern int il2p_mode[4]; + +extern int tx_fx25_size[4]; +extern int tx_fx25_size_cnt[4]; +extern int tx_fx25_mode[4]; + +extern int SatelliteMode; + +// Function prototypes + +void KISS_send_ack(UCHAR port, string * data); +void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame); +void FIR_filter(float * src, unsigned short buf_size, unsigned short tap, float * core, float * dest, float * prev); +void make_core_TXBPF(UCHAR snd_ch, float freq, float width); +void OpenPTTPort(); +void ClosePTTPort(); + +void RadioPTT(int snd_ch, BOOL PTTState); +void put_frame(int snd_ch, string * frame, char * code, int tx_stat, int excluded); +void CloseCOMPort(int fd); +void COMClearRTS(int fd); +void COMClearDTR(int fd); +unsigned int getTicks(); +char * ShortDateTime(); +void write_ax25_info(TAX25Port * AX25Sess); +void reverse_addr(Byte * path, Byte * revpath, int Len); +string * get_mycall(string * path); +TAX25Port * get_user_port_by_calls(int snd_ch, char * CallFrom, char * CallTo); +TAX25Port * get_free_port(int snd_ch); +void * in_list_incoming_mycall(Byte * path); +boolean add_incoming_mycalls(void * socket, char * src_call); +int get_addr(char * Calls, UCHAR * AXCalls); +void reverse_addr(Byte * path, Byte * revpath, int Len); +void set_link(TAX25Port * AX25Sess, UCHAR * axpath); +void rst_timer(TAX25Port * AX25Sess); +void set_unlink(TAX25Port * AX25Sess, Byte * path); +unsigned short get_fcs(UCHAR * Data, unsigned short len); +void KISSSendtoServer(void * sock, Byte * Msg, int Len); +int ConvFromAX25(unsigned char * incall, char * outcall); +BOOL ConvToAX25(char * callsign, unsigned char * ax25call); +void Debugprintf(const char * format, ...); + +double pila(double x); + +void AGW_Raw_monitor(int snd_ch, string * data); + +// Dephi emulation functions + +string * Strings(TStringList * Q, int Index); +void Clear(TStringList * Q); +int Count(TStringList * List); + +string * newString(); +string * copy(string * Source, int StartChar, int Count); +TStringList * newTStringList(); + +void freeString(string * Msg); + +void initString(string * S); +void initTStringList(TStringList* T); + +// Two delete() This is confusing!! +// Not really - one acts on String, other TStringList + +void Delete(TStringList * Q, int Index); +void mydelete(string * Source, int StartChar, int Count); + +void move(UCHAR * SourcePointer, UCHAR * DestinationPointer, int CopyCount); +void fmove(float * SourcePointer, float * DestinationPointer, int CopyCount); + +void setlength(string * Msg, int Count); // Set string length + +string * stringAdd(string * Msg, UCHAR * Chars, int Count); // Extend string + +void Assign(TStringList * to, TStringList * from); // Duplicate from to to + +string * duplicateString(string * in); + +// This looks for a string in a stringlist. Returns inhex if found, otherwise -1 + +int my_indexof(TStringList * l, string * s); + +boolean compareStrings(string * a, string * b); + +int Add(TStringList * Q, string * Entry); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/UZ7HOStuff.h.bak b/UZ7HOStuff.h.bak new file mode 100644 index 0000000..b7e5ec8 --- /dev/null +++ b/UZ7HOStuff.h.bak @@ -0,0 +1,1049 @@ +// +// My port of UZ7HO's Soundmodem +// + +#define VersionString "0.0.0.65" +#define VersionBytes {0, 0, 0, 65} + +// Added FX25. 4x100 FEC and V27 not Working and disabled + +// 0.8 V27 now OK. + +// 0.9 Digipeating added + +// 0.10 Fix second channel tones and calibrate + +// 0.11 Fix allocation of sessions to correct modem +// Fix DCD +// Fix Monitoring of Multiline packets +// Fix possible saving of wrong center freq +// Limit TX sample Q in Linux +// + +// 0.12 Add AGWPE monitoring of received frames +// Fix DCD Threshold +// Fix KISS transparency issue + +// 0.13 Fix sending last few bits in FX.25 Mode + +// 0.14 Add "Copy on Select" to Trace Window + +// 0.15 Limit Trace window to 10000 lines + +// 0.16 Fix overwriting monitor window after scrollback + +// 0.17 Add GPIO and CAT PTT + +// 0.18 Add CM108/119 PTT + +// 0.19 Fix scheduling KISS frames + +// 0.20 Debug code added to RR processing + +// 0.21 Fix AGW monitor of multiple line packets +// Close ax.25 sessions if AGW Host session closes + +// 0.22 Add FEC Count to Session Stats + +// 0.23 Retry DISC until UA received or retry count exceeded + +// 0.24 More fixes to DISC handling + +// 0.26 Add OSS PulseAudio and HAMLIB support + +// 0.27 Dynamically load PulseAudio modules + +// 0.28 Add ARDOPPacket Mode + +// 0.29 Fix saving settings and geometry on close +// 0.30 Retructure code to build with Qt 5.3 +// Fix crash in nogui mode if pulse requested but not available +// Try to fix memory leaks + +// 0.31 Add option to run modems in seprate threads + +// 0.32 Fix timing problem with AGW connect at startup +// Add Memory ARQ +// Add Single bit "Correction" +// Fix error in 31 when using multiple decoders + +// 0.33 Fix Single bit correction +// More memory leak fixes + +// 0.34 Add API to set Modem and Center Frequency +// Fix crash in delete_incoming_mycalls + +// 0.35 Return Version in AGW Extended g response + +// 0.36 Fix timing problem on startup + +// 0.37 Add scrollbars to Device and Modem dialogs + +// 0.38 Change default CM108 name to /dev/hidraw0 on Linux + +// 0.39 Dont try to display Message Boxes in nogui mode. +// Close Device and Modem dialogs on Accept or Reject +// Fix using HAMLIB in nogui mode + +// 0.40 Fix bug in frame optimize when using 6 char calls + +// 0.41 Fix "glitch" on waterfall markers when changing modem freqs + +// 0.42 Add "Minimize to Tray" option + +// 0.43 Add Andy's on_SABM fix. +// Fix Crash if KISS Data sent to AGW port + +// 0.44 Add UDP bridge. + +// 0.45 Add two more modems. +// 0.46 Fix two more modems. + +// 0.47 Fix suprious DM when host connection lost +// Add CWID + +// 0.48 Send FRMR for unrecognised frame types + +// 0.49 Add Andy's FEC Tag correlation coode + +// 0.50 Fix Waterfall display when only using right channel +// Allow 1200 baud fsk at other center freqs +// Add Port numbers to Window title and Try Icon tooltip +// Fix calculation of filters for multiple decoders +// Add RX Offset setting (for satellite operation + +// 0.51 Fix Multithreading with more that 2 modems + +// 0.52 Add Stdin as source on Linux + +// 0.53 Use Byte instead of byte as byte is defined in newer versions of gcc + +// 0.54 Fix for ALSA problem on new pi OS + +// 0.55 Fix for compiler error with newer compiler + +// 0.56 Fix errors in Config.cpp June 22 + +// 0.57 Add Restart Waterfall action August 22 + +// 0.58 Add RSID Sept 2022 + +// 0.59 Add config of Digi Calls Dec 2022 + +// 0.60 Allow ARDOP Packet on modems 2 to 4 March 2023 + +// 0.61 Add il2p support April 2023 + +// 0.62 April 2023 +// Add option to specify sound devices that aren't in list +// Add Save button to Modem dialog to save current tab without closing dialog +// Don't add plug: to Linux device addresses unless addr contains : (allows use of eg ARDOP) + +// 0.64 Fix sending ax.25 (broken in .61) + +// 0.65 Allow Set Modem command to use modem index as well as modem name + + + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define UNUSED(x) (void)(x) + +#ifdef M_PI +#undef M_PI +#endif + +#define M_PI 3.1415926f + +#define pi M_PI + +#ifndef WIN32 +#define _strdup strdup +#endif + + //#define NULL ((void *)0) + + //Delphi Types remember case insensitive + +#define single float +#define boolean int +#define Byte unsigned char // 0 to 255 +#define Word unsigned short // 0 to 65,535 +#define SmallInt short // -32,768 to 32,767 +#define LongWord unsigned int // 0 to 4,294,967,295 + // Int6 : Cardinal; // 0 to 4,294,967,295 +#define LongInt int // -2,147,483,648 to 2,147,483,647 +#define Integer int // -2,147,483,648 to 2,147,483,647 +//#define Int64 long long // -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 + +//#define Byte unsigned char // 0 to 255 +#define word unsigned short // 0 to 65,535 +#define smallint short // -32,768 to 32,767 +#define longword unsigned int // 0 to 4,294,967,295 + // Int6 : Cardinal; // 0 to 4,294,967,295 +#define longint int // -2,147,483,648 to 2,147,483,647 +#define integer int // -2,147,483,648 to 2,147,483,647 + +typedef unsigned long ULONG; + +#define UCHAR unsigned char +#define UINT unsigned int +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +// Soundcard Channels + +#define NONE 0 +#define LEFT 1 +#define RIGHT 2 + +#define nr_emph 2 + +#define decodedNormal 4 //'-' +#define decodedFEC 3 //'F' +#define decodedMEM 2 //'#' +#define decodedSingle 1 //'$' + + +// Seems to use Delphi TStringList for a lot of queues. This seems to be a list of pointers and a count +// Each pointer is to a Data/Length pair +//Maybe something like + +typedef struct string_T +{ + unsigned char * Data; + int Length; + int AllocatedLength; // A reasonable sized block is allocated at the start to speed up adding chars + +}string; + +typedef struct TStringList_T +{ + int Count; + string ** Items; + +} TStringList; + +// QPSK struct + +typedef struct TQPSK_t +{ + UCHAR tx[4]; + int count[4]; + UCHAR rx[4]; + UCHAR mode; +} TPQSK; + + +typedef struct TKISSMode_t +{ + string * data_in; + void * Socket; // Used as a key + + // Not sure what rest are used for. Seems to be one per channel + + TStringList buffer[4]; // Outgoing Frames + +} TKISSMode; + +typedef struct TMChannel_t +{ + + single prev_LPF1I_buf[4096]; + single prev_LPF1Q_buf[4096]; + single prev_dLPFI_buf[4096]; + single prev_dLPFQ_buf[4096]; + single prev_AFCI_buf[4096]; + single prev_AFCQ_buf[4096]; + single AngleCorr; + single MUX_osc; + single AFC_IZ1; + single AFC_IZ2; + single AFC_QZ1; + single AFC_QZ2; + single AFC_bit_buf1I[1024]; + single AFC_bit_buf1Q[1024]; + single AFC_bit_buf2[1024]; + single AFC_IIZ1; + single AFC_QQZ1; + +} TMChannel; + +typedef struct TFX25_t +{ + string data; + Byte status; + Byte bit_cnt; + Byte byte_rx; + unsigned long long tag; + Byte size; + Byte rs_size; + Byte size_cnt; +} TFX25; + + + +typedef struct TDetector_t +{ + struct TFX25_t fx25[4]; + TStringList mem_ARQ_F_buf[5]; + TStringList mem_ARQ_buf[5]; + float pll_loop[5]; + float last_sample[5]; + UCHAR ones[5]; + UCHAR zeros[5]; + float bit_buf[5][1024]; + float bit_buf1[5][1024]; + UCHAR sample_cnt[5]; + UCHAR last_bit[5]; + float PSK_IZ1[5]; + float PSK_QZ1[5]; + float PkAmpI[5]; + float PkAmpQ[5]; + float PkAmp[5]; + float PkAmpMax[5]; + int newpkpos[5]; + float AverageAmp[5]; + float AngleCorr[5]; + float MinAmp[5]; + float MaxAmp[5]; + float MUX3_osc[5]; + float MUX3_1_osc[5]; + float MUX3_2_osc[5]; + float Preemphasis6[5]; + float Preemphasis12[5]; + float PSK_AGC[5]; + float AGC[5]; + float AGC1[5]; + float AGC2[5]; + float AGC3[5]; + float AGC_max[5]; + float AGC_min[5]; + float AFC_IZ1[5]; + float AFC_IZ2[5]; + float AFC_QZ1[5]; + float AFC_QZ2[5]; + + UCHAR last_rx_bit[5]; + UCHAR bit_stream[5]; + UCHAR byte_rx[5]; + UCHAR bit_stuff_cnt[5]; + UCHAR bit_cnt[5]; + float bit_osc[5]; + UCHAR frame_status[5]; + string rx_data[5]; + string FEC_rx_data[5]; + // + UCHAR FEC_pol[5]; + unsigned short FEC_err[5]; + unsigned long long FEC_header1[5][2]; + unsigned short FEC_blk_int[5]; + unsigned short FEC_len_int[5]; + unsigned short FEC_len[5]; + + unsigned short FEC_len_cnt[5]; + + UCHAR rx_intv_tbl[5][4]; + UCHAR rx_intv_sym[5]; + UCHAR rx_viterbi[5]; + UCHAR viterbi_cnt[5]; + // SurvivorStates [1..4,0..511] of TSurvivor; + // + TMChannel MChannel[5][4]; + + float AFC_dF_avg[5]; + float AFC_dF[5]; + float AFC_bit_osc[5]; + float AFC_bit_buf[5][1024]; + unsigned short AFC_cnt[5]; + + string raw_bits1[5]; + string raw_bits[5]; + UCHAR last_nrzi_bit[5]; + + float BPF_core[5][2048]; + float LPF_core[5][2048]; + + float src_INTR_buf[5][8192]; + float src_INTRI_buf[5][8192]; + float src_INTRQ_buf[5][8192]; + float src_LPF1I_buf[5][8192]; + float src_LPF1Q_buf[5][8192]; + + float src_BPF_buf[5][2048]; + float src_Loop_buf[5][8192]; + float prev_BPF_buf[5][4096]; + + float prev_LPF1I_buf[5][4096]; + float prev_LPF1Q_buf[5][4096]; + float prev_INTR_buf[5][16384]; + float prev_INTRI_buf[5][16384]; + float prev_INTRQ_buf[5][16384]; + + Byte emph_decoded; + Byte rx_decoded; + Byte errors; + + +} TDetector; + + + +typedef struct AGWUser_t +{ + void *socket; + string * data_in; + TStringList AGW_frame_buf; + boolean Monitor; + boolean Monitor_raw; + boolean reportFreqAndModem; // Can report modem and frequency to host + +} AGWUser; + +typedef struct TAX25Info_t +{ + longint stat_s_pkt; + longint stat_s_byte; + longint stat_r_pkt; + longint stat_r_byte; + longint stat_r_fc; + longint stat_fec_count; + time_t stat_begin_ses; + time_t stat_end_ses; + longint stat_l_r_byte; + longint stat_l_s_byte; + +} TAX25Info; + +typedef struct TAX25Port_t +{ + Byte hi_vs; + Byte vs; + Byte vr; + Byte PID; + TStringList in_data_buf; + TStringList frm_collector; + string frm_win[8]; + string out_data_buf; + word t1; + word t2; + word t3; + Byte i_lo; + Byte i_hi; + word n1; + word n2; + word IPOLL_cnt; + TStringList frame_buf; //буфер кадров на передачу + TStringList I_frame_buf; + Byte status; + word clk_frack; + char corrcall[10]; + char mycall[10]; + UCHAR digi[56]; + UCHAR Path[80]; // Path in ax25 format - added to save building it each time + UCHAR ReversePath[80]; + int snd_ch; // Simplifies parameter passing + int port; + int pathLen; + void * socket; + char kind[16]; + TAX25Info info; +} TAX25Port; + + +#define LOGEMERGENCY 0 +#define LOGALERT 1 +#define LOGCRIT 2 +#define LOGERROR 3 +#define LOGWARNING 4 +#define LOGNOTICE 5 +#define LOGINFO 6 +#define LOGDEBUG 7 + +#define PTTRTS 1 +#define PTTDTR 2 +#define PTTCAT 4 +#define PTTCM108 8 +#define PTTHAMLIB 16 + +// Status flags + +#define STAT_NO_LINK 0 +#define STAT_LINK 1 +#define STAT_CHK_LINK 2 +#define STAT_WAIT_ANS 3 +#define STAT_TRY_LINK 4 +#define STAT_TRY_UNLINK 5 + + + // Сmd,Resp,Poll,Final,Digipeater flags +#define SET_P 1 +#define SET_F 0 +#define SET_C 1 +#define SET_R 0 +#define SET_NO_RPT 0 +#define SET_RPT 1 + // Frame ID flags +#define I_FRM 0 +#define S_FRM 1 +#define U_FRM 2 +#define I_I 0 +#define S_RR 1 +#define S_RNR 5 +#define S_REJ 9 +#define S_SREJ 0x0D +#define U_SABM 47 +#define U_DISC 67 +#define U_DM 15 +#define U_UA 99 +#define U_FRMR 135 +#define U_UI 3 + // PID flags +#define PID_X25 0x01 // 00000001-CCIT X25 PLP +#define PID_SEGMENT 0x08 // 00001000-Segmentation fragment +#define PID_TEXNET 0xC3 // 11000011-TEXNET Datagram Protocol +#define PID_LQ 0xC4 // 11001000-Link Quality Protocol +#define PID_APPLETALK 0xCA // 11001010-Appletalk +#define PID_APPLEARP 0xCB // 11001011-Appletalk ARP +#define PID_IP 0xCC // 11001100-ARPA Internet Protocol +#define PID_ARP 0xCD // 11001101-ARPA Address Resolution Protocol +#define PID_NET_ROM 0xCF // 11001111-NET/ROM + + +// Sound interface buffer size + +#define SendSize 1024 // 100 mS for now +#define ReceiveSize 512 // try 100 mS for now +#define NumberofinBuffers 4 + +#define Now getTicks() + +// #defines from all modules (?? is this a good idaa ?? + +#define WIN_MAXIMIZED 0 +#define WIN_MINIMIZED 1 +#define MODEM_CAPTION 'SoundModem by UZ7HO' +#define MODEM_VERSION '1.06' +#define SND_IDLE 0 +#define SND_RX 1 +#define SND_TX 2 +#define BUF_EMPTY 0 +#define BUF_FULL 1 +#define DISP_MONO FALSE +#define DISP_RGB TRUE +#define MOD_IDLE 0 +#define MOD_RX 1 +#define MOD_TX 2 +#define MOD_WAIT 3 +#define TIMER_FREE 0 +#define TIMER_BUSY 1 +#define TIMER_OFF 2 +#define TIMER_EVENT_ON 3 +#define TIMER_EVENT_OFF 4 +#define DEBUG_TIMER 1 +#define DEBUG_WATERFALL 2 +#define DEBUG_DECODE 4 +#define DEBUG_SOUND 8 +#define IS_LAST TRUE +#define IS_NOT_LAST FALSE +#define modes_count 16 +#define SPEED_300 0 +#define SPEED_1200 1 +#define SPEED_600 2 +#define SPEED_2400 3 +#define SPEED_P1200 4 +#define SPEED_P600 5 +#define SPEED_P300 6 +#define SPEED_P2400 7 +#define SPEED_Q4800 8 +#define SPEED_Q3600 9 +#define SPEED_Q2400 10 +#define SPEED_MP400 11 +#define SPEED_DW2400 12 +#define SPEED_8P4800 13 +#define SPEED_AE2400 14 +#define SPEED_ARDOP 15 + +#define MODE_FSK 0 +#define MODE_BPSK 1 +#define MODE_QPSK 2 +#define MODE_MPSK 3 +#define MODE_8PSK 4 +#define MODE_PI4QPSK 5 +#define MODE_ARDOP 6 + +#define QPSK_SM 0 +#define QPSK_V26 1 + + +#define MODEM_8P4800_BPF 3200 +#define MODEM_8P4800_TXBPF 3400 +#define MODEM_8P4800_LPF 1000 +#define MODEM_8P4800_BPF_TAP 64 +#define MODEM_8P4800_LPF_TAP 8 + // +#define MODEM_MP400_BPF 775 +#define MODEM_MP400_TXBPF 850 +#define MODEM_MP400_LPF 70 +#define MODEM_MP400_BPF_TAP 256 +#define MODEM_MP400_LPF_TAP 128 + // +#define MODEM_DW2400_BPF 2400 +#define MODEM_DW2400_TXBPF 2500 +#define MODEM_DW2400_LPF 900 +#define MODEM_DW2400_BPF_TAP 256 //256 +#define MODEM_DW2400_LPF_TAP 32 //128 + // +#define MODEM_Q2400_BPF 2400 +#define MODEM_Q2400_TXBPF 2500 +#define MODEM_Q2400_LPF 900 +#define MODEM_Q2400_BPF_TAP 256 //256 +#define MODEM_Q2400_LPF_TAP 128 //128 + // +#define MODEM_Q3600_BPF 3600 +#define MODEM_Q3600_TXBPF 3750 +#define MODEM_Q3600_LPF 1350 +#define MODEM_Q3600_BPF_TAP 256 +#define MODEM_Q3600_LPF_TAP 128 + // +#define MODEM_Q4800_BPF 4800 +#define MODEM_Q4800_TXBPF 5000 +#define MODEM_Q4800_LPF 1800 +#define MODEM_Q4800_BPF_TAP 256 +#define MODEM_Q4800_LPF_TAP 128 + // +#define MODEM_P2400_BPF 4800 +#define MODEM_P2400_TXBPF 5000 +#define MODEM_P2400_LPF 1800 +#define MODEM_P2400_BPF_TAP 256 +#define MODEM_P2400_LPF_TAP 128 + // +#define MODEM_P1200_BPF 2400 +#define MODEM_P1200_TXBPF 2500 +#define MODEM_P1200_LPF 900 +#define MODEM_P1200_BPF_TAP 256 +#define MODEM_P1200_LPF_TAP 128 + // +#define MODEM_P600_BPF 1200 +#define MODEM_P600_TXBPF 1250 +#define MODEM_P600_LPF 400 +#define MODEM_P600_BPF_TAP 256 +#define MODEM_P600_LPF_TAP 128 + // +#define MODEM_P300_BPF 600 +#define MODEM_P300_TXBPF 625 +#define MODEM_P300_LPF 200 +#define MODEM_P300_BPF_TAP 256 +#define MODEM_P300_LPF_TAP 128 + // +#define MODEM_300_BPF 500 +#define MODEM_300_TXBPF 500 +#define MODEM_300_LPF 155 +#define MODEM_300_BPF_TAP 256 +#define MODEM_300_LPF_TAP 128 + // +#define MODEM_600_BPF 800 +#define MODEM_600_TXBPF 900 +#define MODEM_600_LPF 325 +#define MODEM_600_BPF_TAP 256 +#define MODEM_600_LPF_TAP 128 + // +#define MODEM_1200_BPF 1400 +#define MODEM_1200_TXBPF 1600 +#define MODEM_1200_LPF 650 +#define MODEM_1200_BPF_TAP 256 +#define MODEM_1200_LPF_TAP 128 + // +#define MODEM_2400_BPF 3200 +#define MODEM_2400_TXBPF 3200 +#define MODEM_2400_LPF 1400 +#define MODEM_2400_BPF_TAP 256 +#define MODEM_2400_LPF_TAP 128 + +#define TX_SILENCE 0 +#define TX_DELAY 1 +#define TX_TAIL 2 +#define TX_NO_DATA 3 +#define TX_FRAME 4 +#define TX_WAIT_BPF 5 + + +#define FRAME_WAIT 0 +#define FRAME_LOAD 1 +#define RX_BIT0 0 +#define RX_BIT1 128 +#define DCD_WAIT_SLOT 0 +#define DCD_WAIT_PERSIST 1 + +#define FX25_MODE_NONE 0 +#define FX25_MODE_RX 1 +#define FX25_MODE_TXRX 2 +#define FX25_TAG 0 +#define FX25_LOAD 1 + +#define IL2P_MODE_NONE 0 +#define IL2P_MODE_RX 1 // RX il2p + HDLC +#define IL2P_MODE_TXRX 2 +#define IL2P_MODE_ONLY 3 // RX only il2p, TX il2p + + +#define MODE_OUR 0 +#define MODE_OTHER 1 +#define MODE_RETRY 2 + +#define FRAME_FLAG 126 // 7e + +#define port_num 32 // ?? Max AGW sessions +#define PKT_ERR 17 // Minimum packet size, bytes +#define I_MAX 7 // Maximum number of packets + + + // externs for all modules + +#define ARDOPBufferSize 12000 * 100 + +extern short ARDOPTXBuffer[4][12000 * 100]; // Enough to hold whole frame of samples + +extern int ARDOPTXLen[4]; // Length of frame +extern int ARDOPTXPtr[4]; // Tx Pointer + +extern BOOL KISSServ; +extern int KISSPort; + +extern BOOL AGWServ; +extern int AGWPort; + +extern TStringList KISS_acked[]; +extern TStringList KISS_iacked[]; + +extern TStringList all_frame_buf[5]; + +extern unsigned short pkt_raw_min_len; +extern int stat_r_mem; + +extern UCHAR diddles; + +extern int stdtones; +extern int fullduplex; + +extern struct TQPSK_t qpsk_set[4]; + +extern int NonAX25[5]; + +extern short txtail[5]; +extern short txdelay[5]; + +extern short modem_def[5]; + +extern int emph_db[5]; +extern UCHAR emph_all[5]; + +extern UCHAR modem_mode[5]; + +extern UCHAR RCVR[5]; +extern int soundChannel[5]; +extern int modemtoSoundLR[4]; + +extern short rx_freq[5]; +extern short rx_shift[5]; +extern short rx_baudrate[5]; +extern short rcvr_offset[5]; + +extern int tx_hitoneraisedb[5]; +extern float tx_hitoneraise[5]; + + +extern UCHAR tx_status[5]; +extern float tx_freq[5]; +extern float tx_shift[5]; +extern unsigned short tx_baudrate[5]; + +extern unsigned short bpf[5]; +extern unsigned short lpf[5]; + +extern unsigned short txbpf[5]; + +extern unsigned short tx_BPF_tap[5]; +extern unsigned short tx_BPF_timer[5]; + +extern unsigned short BPF_tap[5]; +extern unsigned short LPF_tap[5]; + +extern float tx_BPF_core[5][32768]; +extern float LPF_core[5][2048]; + +extern UCHAR xData[256]; +extern UCHAR xEncoded[256]; +extern UCHAR xDecoded[256]; + +extern float PI125; +extern float PI375; +extern float PI625; +extern float PI875; +extern float PI5; +extern float PI25; +extern float PI75; + +extern int max_frame_collector[4]; +extern boolean KISS_opt[4]; + +#define MaxErrors 4 + +extern BOOL MinOnStart; + +//RS TReedSolomon; +// Form1 TForm1; +// WaveFormat TWaveFormatEx; + +extern int UDPServ; +extern long long udpServerSeqno; + +extern int Channels; +extern int BitsPerSample; +extern float TX_Samplerate; +extern float RX_Samplerate; +extern int RX_SR; +extern int TX_SR; +extern int RX_PPM; +extern int TX_PPM; +extern int tx_bufsize; +extern int rx_bufsize; +extern int tx_bufcount; +extern int rx_bufcount; +extern int fft_size; +extern int mouse_down[2]; +//UCHAR * RX_pBuf array[257]; +// RX_header array[1..256] of TWaveHdr; +// TX_pBuf array[1..4,1..256] of pointer; +//TX_header array[1..4,1..256] of TWaveHdr; +extern UCHAR calib_mode[5]; +extern UCHAR snd_status[5]; +extern UCHAR buf_status[5]; +extern UCHAR tx_buf_num1[5]; +extern UCHAR tx_buf_num[5]; +extern int speed[5]; +extern int panels[6]; + +extern int FFTSize; +#define fft_size FFTSize + +extern float fft_window_arr[2048]; +// fft_s,fft_d array[0..2047] of TComplex; +extern short fft_buf[5][4096]; +extern UCHAR fft_disp[5][4096]; +// bm array[1..4] of TBitMap; +// bm1,bm2,bm3 TBitMap; + +// WaveInHandle hWaveIn; +// WaveOutHandle array[1..4] of hWaveOut; +extern int RXBufferLength; + +// data1 PData16; + +extern int grid_time; +extern int fft_mult; +extern int fft_spd; +extern int grid_timer; +extern int stop_wf; +extern int raduga; +extern char snd_rx_device_name[32]; +extern char snd_tx_device_name[32]; +extern int snd_rx_device; +extern int snd_tx_device; +extern UCHAR mod_icon_status; +extern UCHAR last_mod_icon_status; +extern UCHAR icon_timer; +// TelIni TIniFile; +extern char cur_dir[]; +// TimerId1 cardinal; +// TimerId2 cardinal; +extern UCHAR TimerStat1; +extern UCHAR TimerStat2; +extern int stat_log; + +extern char PTTPort[80]; // Port for Hardware PTT - may be same as control port. +extern int PTTMode; +extern int PTTBAUD ; + +extern char PTTOnString[128]; +extern char PTTOffString[128]; + +extern UCHAR PTTOnCmd[64]; +extern UCHAR PTTOnCmdLen; + +extern UCHAR PTTOffCmd[64]; +extern UCHAR PTTOffCmdLen; + +extern int PTT_device; +extern int RX_device; +extern int TX_device; +extern int TX_rotate; +extern int UsingLeft; +extern int UsingRight; +extern int UsingBothChannels; +extern int pttGPIOPin; +extern int pttGPIOPinR; +extern BOOL pttGPIOInvert; +extern BOOL useGPIO; +extern BOOL gotGPIO; +extern int VID; +extern int PID; +extern char CM108Addr[80]; +extern int HamLibPort; +extern char HamLibHost[]; + +extern int SCO; +extern int DualPTT; +extern UCHAR DebugMode; +extern UCHAR TimerEvent; +extern int nr_monitor_lines; +extern int UTC_Tim; +extern int MainPriority; +// MainThreadHandle THandle; +extern UCHAR w_state; + +extern BOOL Firstwaterfall; +extern BOOL Secondwaterfall; + +extern int dcd_threshold; +extern int rxOffset; +extern int chanOffset[4]; + +extern boolean busy; +extern boolean dcd[5]; + +extern struct TKISSMode_t KISS; + +extern boolean dyn_frack[4] ; +extern Byte recovery[4]; +extern Byte users[4]; + +extern int resptime[4]; +extern int slottime[4]; +extern int persist[4]; +extern int fracks[4]; +extern int frack_time[4]; +extern int idletime[4]; +extern int redtime[4]; +extern int IPOLL[4]; +extern int maxframe[4]; +extern int TXFrmMode[4]; + +extern char MyDigiCall[4][512]; +extern char exclude_callsigns[4][512]; +extern char exclude_APRS_frm[4][512]; + +extern TStringList list_exclude_callsigns[4]; +extern TStringList list_exclude_APRS_frm[4]; +extern TStringList list_digi_callsigns[4]; + + +extern int SoundIsPlaying; +extern int Capturing; + +extern struct TDetector_t DET[nr_emph + 1][16]; + +extern char CaptureDevice[80]; +extern char PlaybackDevice[80]; + +extern TAX25Port AX25Port[4][port_num]; + +extern int fx25_mode[4]; +extern int il2p_mode[4]; + +extern int tx_fx25_size[4]; +extern int tx_fx25_size_cnt[4]; +extern int tx_fx25_mode[4]; + +extern int SatelliteMode; + +// Function prototypes + +void KISS_send_ack(UCHAR port, string * data); +void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame); +void FIR_filter(float * src, unsigned short buf_size, unsigned short tap, float * core, float * dest, float * prev); +void make_core_TXBPF(UCHAR snd_ch, float freq, float width); +void OpenPTTPort(); +void ClosePTTPort(); + +void RadioPTT(int snd_ch, BOOL PTTState); +void put_frame(int snd_ch, string * frame, char * code, int tx_stat, int excluded); +void CloseCOMPort(int fd); +void COMClearRTS(int fd); +void COMClearDTR(int fd); +unsigned int getTicks(); +char * ShortDateTime(); +void write_ax25_info(TAX25Port * AX25Sess); +void reverse_addr(Byte * path, Byte * revpath, int Len); +string * get_mycall(string * path); +TAX25Port * get_user_port_by_calls(int snd_ch, char * CallFrom, char * CallTo); +TAX25Port * get_free_port(int snd_ch); +void * in_list_incoming_mycall(Byte * path); +boolean add_incoming_mycalls(void * socket, char * src_call); +int get_addr(char * Calls, UCHAR * AXCalls); +void reverse_addr(Byte * path, Byte * revpath, int Len); +void set_link(TAX25Port * AX25Sess, UCHAR * axpath); +void rst_timer(TAX25Port * AX25Sess); +void set_unlink(TAX25Port * AX25Sess, Byte * path); +unsigned short get_fcs(UCHAR * Data, unsigned short len); +void KISSSendtoServer(void * sock, Byte * Msg, int Len); +int ConvFromAX25(unsigned char * incall, char * outcall); +BOOL ConvToAX25(char * callsign, unsigned char * ax25call); +void Debugprintf(const char * format, ...); + +double pila(double x); + +void AGW_Raw_monitor(int snd_ch, string * data); + +// Dephi emulation functions + +string * Strings(TStringList * Q, int Index); +void Clear(TStringList * Q); +int Count(TStringList * List); + +string * newString(); +string * copy(string * Source, int StartChar, int Count); +TStringList * newTStringList(); + +void freeString(string * Msg); + +void initString(string * S); +void initTStringList(TStringList* T); + +// Two delete() This is confusing!! +// Not really - one acts on String, other TStringList + +void Delete(TStringList * Q, int Index); +void mydelete(string * Source, int StartChar, int Count); + +void move(UCHAR * SourcePointer, UCHAR * DestinationPointer, int CopyCount); +void fmove(float * SourcePointer, float * DestinationPointer, int CopyCount); + +void setlength(string * Msg, int Count); // Set string length + +string * stringAdd(string * Msg, UCHAR * Chars, int Count); // Extend string + +void Assign(TStringList * to, TStringList * from); // Duplicate from to to + +string * duplicateString(string * in); + +// This looks for a string in a stringlist. Returns inhex if found, otherwise -1 + +int my_indexof(TStringList * l, string * s); + +boolean compareStrings(string * a, string * b); + +int Add(TStringList * Q, string * Entry); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/UZ7HOUtils.c b/UZ7HOUtils.c new file mode 100644 index 0000000..a2a8fe7 --- /dev/null +++ b/UZ7HOUtils.c @@ -0,0 +1,321 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ +#include "UZ7HOStuff.h" + +// TStringlist And String emulation Functions + +// Dephi seems to mix starting counts at 0 or 1. I'll try making everything +// base zero. + +// Initialise a list + +void CreateStringList(TStringList * List) +{ + List->Count = 0; + List->Items = 0; +} + + +int Count(TStringList * List) +{ + return List->Count; +} + +string * newString() +{ + // Creates and Initialises a string + + UCHAR * ptr = malloc(sizeof(string)); // Malloc Data separately so it can be ralloc'ed + string * New = (string *)ptr; + New->Length = 0; + New->AllocatedLength = 256; + New->Data = malloc(256); + + return New; +} + +void initString(string * S) +{ + S->Length = 0; + S->AllocatedLength = 256; + S->Data = malloc(256); +} + +void initTStringList(TStringList* T) +{ + //string * New = newString(); + + T->Count = 0; + T->Items = NULL; + + //Add(T, New); +} + + + +TStringList * newTStringList() +{ + TStringList * T = (TStringList *) malloc(sizeof(TStringList)); + string * New = newString(); + + T->Count = 0; + T->Items = NULL; + + Add(T, New); + + return T; +} + + +void freeString(string * Msg) +{ + if (Msg->Data) + free(Msg->Data); + + free(Msg); +} + +string * Strings(TStringList * Q, int Index) +{ + if (Index >= Q->Count) + return NULL; + + return Q->Items[Index]; +} + +int Add(TStringList * Q, string * Entry) +{ + Q->Items = realloc(Q->Items,(Q->Count + 1) * sizeof(void *)); + Q->Items[Q->Count++] = Entry; + + return (Q->Count); +} + + +void mydelete(string * Source, int StartChar, int Count) +{ + //Description + //The Delete procedure deletes up to Count characters from the passed parameter Source string starting + //from position StartChar. + + if (StartChar > Source->Length) + return; + + int left = Source->Length - StartChar; + + if (Count > left) + Count = left; + + memmove(&Source->Data[StartChar], &Source->Data[StartChar + Count], left - Count); + + Source->Length -= Count; +} + + +void Delete(TStringList * Q, int Index) +{ + // Remove item at Index and move rest up list + // Index starts at zero + + if (Index >= Q->Count) + return; + + // We should free it, so user must duplicate msg if needed after delete + + freeString(Q->Items[Index]); +// free(Q->Items[Index]); + + Q->Count--; + + while (Index < Q->Count) + { + Q->Items[Index] = Q->Items[Index + 1]; + Index++; + } +} + +void setlength(string * Msg, int Count) +{ + // Set length, allocating more space if needed + + if (Count > Msg->AllocatedLength) + { + Msg->AllocatedLength = Count + 256; + Msg->Data = realloc(Msg->Data, Msg->AllocatedLength); + } + + Msg->Length = Count; +} + +string * stringAdd(string * Msg, UCHAR * Chars, int Count) +{ + // Add Chars to string + + if (Msg->Length + Count > Msg->AllocatedLength) + { + Msg->AllocatedLength += Count + 256; + Msg->Data = realloc(Msg->Data, Msg->AllocatedLength); + } + + memcpy(&Msg->Data[Msg->Length], Chars, Count); + Msg->Length += Count; + + return Msg; +} + +void Clear(TStringList * Q) +{ + int i = 0; + + if (Q->Items == NULL) + return; + + while (Q->Count) + { + freeString(Q->Items[i++]); + Q->Count--; + } + + free(Q->Items); + + Q->Items = NULL; +} + +// procedure move ( const SourcePointer; var DestinationPointer; CopyCount : Integer ) ; +// Description +// The move procedure is a badly named method of copying a section of memory from one place to another. + +// CopyCount bytes are copied from storage referenced by SourcePointer and written to DestinationPointer + +void move(UCHAR * SourcePointer, UCHAR * DestinationPointer, int CopyCount) +{ + memmove(DestinationPointer, SourcePointer, CopyCount); +} + +void fmove(float * SourcePointer, float * DestinationPointer, int CopyCount) +{ + memmove(DestinationPointer, SourcePointer, CopyCount); +} + + + +//Description +//The copy function has 2 forms. In the first, it creates a new string from part of an existing string. In the second, it creates a new array from part of an existing array. + +//1.String copy + +//The first character of a string has index = 1. + +//Up to Count characters are copied from the StartChar of the Source string to the returned string. +//Less than Count characters if the end of the Source string is encountered before Count characters have been copied. + + +string * copy(string * Source, int StartChar, int Count) +{ + string * NewString = newString(); + int end = StartChar + Count; + + if (end > Source->Length) + Count = Source->Length - StartChar; + + memcpy(NewString->Data, &Source->Data[StartChar], Count); + + NewString->Length = Count; + + return NewString; +} + +// Duplicate from > to + +void Assign(TStringList * to, TStringList * from) +{ + int i; + + Clear(to); + + if (from->Count == 0) + return; + + // Duplicate each item + + for (i = 0; i < from->Count; i++) + { + string * new = newString(); + + stringAdd(new, from->Items[i]->Data, from->Items[i]->Length); + Add(to, new); + } +} + +string * duplicateString(string * in) +{ + string * new = newString(); + + stringAdd(new, in->Data, in->Length); + + return new; +} + + +double pila(double x) +{ + //x : = frac(x); The frac function returns the fractional part of a floating point number. + + double whole; + double rem; + + rem = modf(x, &whole); // returns fraction, writes whole to whole + + if (rem != rem) + rem = 0; + + if (rem > 0.5) + rem = 1 - rem; + + return 2 * rem; +} + +boolean compareStrings(string * a, string * b) +{ + if (a->Length == b->Length && memcmp(a->Data, b->Data, a->Length) == 0) + return TRUE; + + return FALSE; +} + +// This looks for a string in a stringlist. Returns index if found, otherwise -1 + +int my_indexof(TStringList * l, string * s) +{ + int i; + + for (i = 0; i < l->Count; i++) + { + // Need to compare count and data - C doesn't allow struct compare + + if (l->Items[i]->Length == s->Length && memcmp(l->Items[i]->Data, s->Data, s->Length) == 0) + return i; + } + return -1; +} + + \ No newline at end of file diff --git a/Waveout.c b/Waveout.c new file mode 100644 index 0000000..31a6c72 --- /dev/null +++ b/Waveout.c @@ -0,0 +1,992 @@ +/* +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 +// +// Passes audio samples to the sound interface + +// Windows uses WaveOut + +// Nucleo uses DMA + +// Linux will use ALSA + +// This is the Windows Version + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include + +#include "UZ7HOStuff.h" + +#pragma comment(lib, "winmm.lib") +void printtick(char * msg); +void PollReceivedSamples(); +short * SoundInit(); +void StdinPollReceivedSamples(); + +#include + +void GetSoundDevices(); + +// Windows works with signed samples +- 32767 +// STM32 DAC uses unsigned 0 - 4095 + +// Currently use 1200 samples for TX but 480 for RX to reduce latency + +short buffer[2][SendSize * 2]; // Two Transfer/DMA buffers of 0.1 Sec (x2 for Stereo) +short inbuffer[5][ReceiveSize * 2]; // Input Transfer/ buffers of 0.1 Sec (x2 for Stereo) + +extern short * DMABuffer; +extern int Number; + +int SoundMode = 0; +int stdinMode = 0; + +BOOL Loopback = FALSE; +//BOOL Loopback = TRUE; + +char CaptureDevice[80] = "real"; //"2"; +char PlaybackDevice[80] = "real"; //"1"; + +int CaptureIndex = -1; // Card number +PlayBackIndex = -1; + +BOOL UseLeft = 1; +BOOL UseRight = 1; +char LogDir[256] = ""; + +FILE *logfile[3] = {NULL, NULL, NULL}; +char LogName[3][256] = {"ARDOPDebug", "ARDOPException", "ARDOPSession"}; + +char * CaptureDevices = NULL; +char * PlaybackDevices = NULL; + +int CaptureCount = 0; +int PlaybackCount = 0; + +char CaptureNames[16][256]= {""}; +char PlaybackNames[16][256]= {""}; + +WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 2, 12000, 48000, 4, 16, 0 }; + +HWAVEOUT hWaveOut = 0; +HWAVEIN hWaveIn = 0; + +WAVEHDR header[2] = +{ + {(char *)buffer[0], 0, 0, 0, 0, 0, 0, 0}, + {(char *)buffer[1], 0, 0, 0, 0, 0, 0, 0} +}; + +WAVEHDR inheader[5] = +{ + {(char *)inbuffer[0], 0, 0, 0, 0, 0, 0, 0}, + {(char *)inbuffer[1], 0, 0, 0, 0, 0, 0, 0}, + {(char *)inbuffer[2], 0, 0, 0, 0, 0, 0, 0}, + {(char *)inbuffer[3], 0, 0, 0, 0, 0, 0, 0}, + {(char *)inbuffer[4], 0, 0, 0, 0, 0, 0, 0} +}; + +WAVEOUTCAPSA pwoc; +WAVEINCAPSA pwic; + +unsigned int RTC = 0; + +int InitSound(BOOL Quiet); +void HostPoll(); + +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite); + +int Ticks; + +LARGE_INTEGER Frequency; +LARGE_INTEGER StartTicks; +LARGE_INTEGER NewTicks; + +int LastNow; + +extern BOOL blnDISCRepeating; + +#define TARGET_RESOLUTION 1 // 1-millisecond target resolution + + +VOID __cdecl Debugprintf(const char * format, ...) +{ + char Mess[10000]; + va_list(arglist); + + va_start(arglist, format); + vsprintf(Mess, format, arglist); + WriteDebugLog(Mess); + + return; +} + + +void platformInit() +{ + TIMECAPS tc; + unsigned int wTimerRes; + DWORD t, lastt = 0; + int i = 0; + + + _strupr(CaptureDevice); + _strupr(PlaybackDevice); + + QueryPerformanceFrequency(&Frequency); + Frequency.QuadPart /= 1000; // Microsecs + QueryPerformanceCounter(&StartTicks); + + GetSoundDevices(); + + if (!SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)) + printf("Failed to set High Priority (%d)\n", GetLastError()); +} + +unsigned int getTicks() +{ + return timeGetTime(); +// QueryPerformanceCounter(&NewTicks); +// return (int)(NewTicks.QuadPart - StartTicks.QuadPart) / Frequency.QuadPart; +} + +void printtick(char * msg) +{ + QueryPerformanceCounter(&NewTicks); + Debugprintf("%s %i\r", msg, Now - LastNow); + LastNow = Now; +} + +void txSleep(int mS) +{ + // called while waiting for next TX buffer. Run background processes + + while (mS > 50) + { + if (SoundMode == 3) + UDPPollReceivedSamples(); + else + PollReceivedSamples(); // discard any received samples + + QSleep(50); + mS -= 50; + } + + QSleep(mS); + + if (SoundMode == 3) + UDPPollReceivedSamples(); + else + PollReceivedSamples(); // discard any received samples +} + +int PriorSize = 0; + +int Index = 0; // DMA TX Buffer being used 0 or 1 +int inIndex = 0; // DMA Buffer being used + + +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) +{ + if (SoundMode == 3) // UDP + { + sendSamplestoUDP(buf, n, UDPClientPort); + return buf; + } + + if (SoundMode == 4) // STDOUT + { + sendSamplestoStdout(buf, n); + return buf; + } + + + header[Index].dwBufferLength = n * 4; + + waveOutPrepareHeader(hWaveOut, &header[Index], sizeof(WAVEHDR)); + waveOutWrite(hWaveOut, &header[Index], sizeof(WAVEHDR)); + + // wait till previous buffer is complete + + while (!(header[!Index].dwFlags & WHDR_DONE)) + { + txSleep(10); // Run buckground while waiting + } + + waveOutUnprepareHeader(hWaveOut, &header[!Index], sizeof(WAVEHDR)); + Index = !Index; + + return &buffer[Index][0]; +} + + +// // This generates a nice musical pattern for sound interface testing +// for (t = 0; t < sizeof(buffer); ++t) +// buffer[t] =((((t * (t >> 8 | t >> 9) & 46 & t >> 8)) ^ (t & t >> 13 | t >> 6)) & 0xFF); + +void GetSoundDevices() +{ + int i; + + if (SoundMode == 1) + { + PlaybackCount = 3; + + strcpy(&PlaybackNames[0][0], "/dev/dsp0"); + strcpy(&PlaybackNames[1][0], "/dev/dsp1"); + strcpy(&PlaybackNames[2][0], "/dev/dsp2"); + + CaptureCount = 3; + + strcpy(&CaptureNames[0][0], "/dev/dsp0"); + strcpy(&CaptureNames[1][0], "/dev/dsp1"); + strcpy(&CaptureNames[2][0], "/dev/dsp2"); + return; + } + + else if (SoundMode == 2) // Pulse + { + PlaybackCount = 1; + strcpy(&PlaybackNames[0][0], "Pulse"); + + CaptureCount = 1; + strcpy(&CaptureNames[0][0], "Pulse"); + return; + } + else if (SoundMode == 3) // UDP + { + PlaybackCount = 1; + strcpy(&PlaybackNames[0][0], "UDP"); + + CaptureCount = 1; + strcpy(&CaptureNames[0][0], "UDP"); + return; + } + + Debugprintf("Capture Devices"); + + CaptureCount = waveInGetNumDevs(); + + CaptureDevices = malloc((MAXPNAMELEN + 2) * (CaptureCount + 2)); + CaptureDevices[0] = 0; + + for (i = 0; i < CaptureCount; i++) + { + waveInOpen(&hWaveIn, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveInGetDevCapsA((UINT_PTR)hWaveIn, &pwic, sizeof(WAVEINCAPSA)); + + if (CaptureDevices) + strcat(CaptureDevices, ","); + strcat(CaptureDevices, pwic.szPname); + Debugprintf("%d %s", i, pwic.szPname); + memcpy(&CaptureNames[i][0], pwic.szPname, MAXPNAMELEN); + _strupr(&CaptureNames[i][0]); + } + + CaptureCount++; + Debugprintf("%d %s", i, "STDIN"); + strcpy(&CaptureNames[i][0], "STDIN"); + + Debugprintf("Playback Devices"); + + PlaybackCount = waveOutGetNumDevs(); + + PlaybackDevices = malloc((MAXPNAMELEN + 2) * PlaybackCount); + PlaybackDevices[0] = 0; + + for (i = 0; i < PlaybackCount; i++) + { + waveOutOpen(&hWaveOut, i, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + waveOutGetDevCapsA((UINT_PTR)hWaveOut, &pwoc, sizeof(WAVEOUTCAPSA)); + + if (PlaybackDevices[0]) + strcat(PlaybackDevices, ","); + strcat(PlaybackDevices, pwoc.szPname); + Debugprintf("%i %s", i, pwoc.szPname); + memcpy(&PlaybackNames[i][0], pwoc.szPname, MAXPNAMELEN); + _strupr(&PlaybackNames[i][0]); + waveOutClose(hWaveOut); + } +} + + +HANDLE hStdin; + +int InitSound(BOOL Report) +{ + int i, t, ret; + + if (SoundMode == 4) + { + char fn[] = "D:samples.wav"; + + // create a separate new console window +// AllocConsole(); + + // attach the new console to this application's process +// AttachConsole(GetCurrentProcessId()); + + hStdin = CreateFileA(fn, + GENERIC_READ, + 1, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + // reopen the std I/O streams to redirect I/O to the new console + +// freopen("CON", "r", stdin); + +// i = _setmode(_fileno(stdin), _O_BINARY); +// if (i == -1) +// i = errno; +// else +// printf("'stdin' successfully changed to binary mode\n"); + + return TRUE; + } + + header[0].dwFlags = WHDR_DONE; + header[1].dwFlags = WHDR_DONE; + + if (strlen(PlaybackDevice) <= 2) + PlayBackIndex = atoi(PlaybackDevice); + else + { + // Name instead of number. Look for a substring match + + for (i = 0; i < PlaybackCount; i++) + { + if (strstr(&PlaybackNames[i][0], PlaybackDevice)) + { + PlayBackIndex = i; + break; + } + } + } + + ret = waveOutOpen(&hWaveOut, PlayBackIndex, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + + if (ret) + Debugprintf("Failed to open WaveOut Device %s Error %d", PlaybackDevice, ret); + else + { + ret = waveOutGetDevCapsA((UINT_PTR)hWaveOut, &pwoc, sizeof(WAVEOUTCAPSA)); + if (Report) + Debugprintf("Opened WaveOut Device %s", pwoc.szPname); + } + + if (strlen(CaptureDevice) <= 2) + CaptureIndex = atoi(CaptureDevice); + else + { + // Name instead of number. Look for a substring match + + for (i = 0; i < CaptureCount; i++) + { + if (strstr(&CaptureNames[i][0], CaptureDevice)) + { + CaptureIndex = i; + break; + } + } + } + + if (strcmp(CaptureNames[CaptureIndex], "STDIN") == 0) + { + stdinMode = 1; + char fn[] = "D:samples.wav"; + + hStdin = CreateFileA(fn, + GENERIC_READ, + 1, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + DMABuffer = SoundInit(); + + return TRUE; + } + + ret = waveInOpen(&hWaveIn, CaptureIndex, &wfx, 0, 0, CALLBACK_NULL); //WAVE_MAPPER + if (ret) + Debugprintf("Failed to open WaveIn Device %s Error %d", CaptureDevice, ret); + else + { + ret = waveInGetDevCapsA((UINT_PTR)hWaveIn, &pwic, sizeof(WAVEINCAPSA)); + if (Report) + Debugprintf("Opened WaveIn Device %s", pwic.szPname); + } + +// wavfp1 = fopen("s:\\textxxx.wav", "wb"); + + for (i = 0; i < NumberofinBuffers; i++) + { + inheader[i].dwBufferLength = ReceiveSize * 4; + + ret = waveInPrepareHeader(hWaveIn, &inheader[i], sizeof(WAVEHDR)); + ret = waveInAddBuffer(hWaveIn, &inheader[i], sizeof(WAVEHDR)); + } + + ret = waveInStart(hWaveIn); + + DMABuffer = SoundInit(); + + +// This generates a nice musical pattern for sound interface testing + +/* +for (i = 0; i < 100; i++) +{ + for (t = 0; t < SendSize ; ++t) + SampleSink(((((t * (t >> 8 | t >> 9) & 46 & t >> 8)) ^ (t & t >> 13 | t >> 6)) & 0xff) * 255); +} +*/ + + return TRUE; +} + +static int min = 0, max = 0, lastlevelGUI = 0, lastlevelreport = 0; + +static UCHAR CurrentLevel = 0; // Peak from current samples + +void PollReceivedSamples() +{ + // Process any captured samples + // Ideally call at least every 100 mS, more than 200 will loose data + + // For level display we want a fairly rapid level average but only want to report + // to log every 10 secs or so + + // with Windows we get mono data + + if (stdinMode) + { + StdinPollReceivedSamples(); + return; + } + + if (inheader[inIndex].dwFlags & WHDR_DONE) + { + short * ptr = &inbuffer[inIndex][0]; + int i; + + for (i = 0; i < ReceiveSize; i++) + { + if (*(ptr) < min) + min = *ptr; + else if (*(ptr) > max) + max = *ptr; + ptr++; + } + + CurrentLevel = ((max - min) * 75) /32768; // Scale to 150 max + + if ((Now - lastlevelGUI) > 2000) // 2 Secs + { +// if (WaterfallActive == 0 && SpectrumActive == 0) // Don't need to send as included in Waterfall Line +// SendtoGUI('L', &CurrentLevel, 1); // Signal Level + + lastlevelGUI = Now; + + if ((Now - lastlevelreport) > 10000) // 10 Secs + { + char HostCmd[64]; + lastlevelreport = Now; + + sprintf(HostCmd, "INPUTPEAKS %d %d", min, max); + Debugprintf("Input peaks = %d, %d", min, max); + + } + min = max = 0; + } + +// debugprintf(LOGDEBUG, "Process %d %d", inIndex, inheader[inIndex].dwBytesRecorded/2); +// if (Capturing && Loopback == FALSE) + ProcessNewSamples(&inbuffer[inIndex][0], inheader[inIndex].dwBytesRecorded/4); + + waveInUnprepareHeader(hWaveIn, &inheader[inIndex], sizeof(WAVEHDR)); + inheader[inIndex].dwFlags = 0; + waveInPrepareHeader(hWaveIn, &inheader[inIndex], sizeof(WAVEHDR)); + waveInAddBuffer(hWaveIn, &inheader[inIndex], sizeof(WAVEHDR)); + + inIndex++; + + if (inIndex == NumberofinBuffers) + inIndex = 0; + } +} + + + +/* + +// Pre GUI Version +void PollReceivedSamples() +{ + // Process any captured samples + // Ideally call at least every 100 mS, more than 200 will loose data + + if (inheader[inIndex].dwFlags & WHDR_DONE) + { + short * ptr = &inbuffer[inIndex][0]; + int i; + + for (i = 0; i < ReceiveSize; i++) + { + if (*(ptr) < min) + min = *ptr; + else if (*(ptr) > max) + max = *ptr; + ptr++; + } + leveltimer++; + + if (leveltimer > 100) + { + char HostCmd[64]; + leveltimer = 0; + sprintf(HostCmd, "INPUTPEAKS %d %d", min, max); + QueueCommandToHost(HostCmd); + + debugprintf(LOGDEBUG, "Input peaks = %d, %d", min, max); + min = max = 0; + } + +// debugprintf(LOGDEBUG, "Process %d %d", inIndex, inheader[inIndex].dwBytesRecorded/2); + if (Capturing && Loopback == FALSE) + ProcessNewSamples(&inbuffer[inIndex][0], inheader[inIndex].dwBytesRecorded/2); + + waveInUnprepareHeader(hWaveIn, &inheader[inIndex], sizeof(WAVEHDR)); + inheader[inIndex].dwFlags = 0; + waveInPrepareHeader(hWaveIn, &inheader[inIndex], sizeof(WAVEHDR)); + waveInAddBuffer(hWaveIn, &inheader[inIndex], sizeof(WAVEHDR)); + + inIndex++; + + if (inIndex == NumberofinBuffers) + inIndex = 0; + } +} +*/ + +void StopCapture() +{ + Capturing = FALSE; + +// waveInStop(hWaveIn); +// debugprintf(LOGDEBUG, "Stop Capture"); +} + +void StartCapture() +{ + Capturing = TRUE; +// debugprintf(LOGDEBUG, "Start Capture"); +} +void CloseSound() +{ + waveInClose(hWaveIn); + waveOutClose(hWaveOut); +} + +#include + +VOID CloseDebugLog() +{ + if(logfile[0]) + fclose(logfile[0]); + logfile[0] = NULL; +} + + +FILE *statslogfile = NULL; + +VOID CloseStatsLog() +{ + fclose(statslogfile); + statslogfile = NULL; +} + +VOID WriteSamples(short * buffer, int len) +{ + fwrite(buffer, 1, len * 2, wavfp1); +} + +short * SoundInit() +{ + Index = 0; + return &buffer[0][0]; + + +} + +// Called at end of transmission + +extern int Number; // Number of samples waiting to be sent + +// Subroutine to add trailer before filtering + +void SoundFlush() +{ + // Append Trailer then wait for TX to complete + +// AddTrailer(); // add the trailer. + +// if (Loopback) +// ProcessNewSamples(buffer[Index], Number); + + 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); + + // I think we should turn round the link here. I dont see the point in + // waiting for MainPoll + + SoundIsPlaying = FALSE; + + // Clear buffers + + Number = 0; + memset(buffer, 0, sizeof(buffer)); + DMABuffer = &buffer[0][0]; + +// StartCapture(); + + return; +} + +int CheckAllSent() +{ + if ((header[0].dwFlags & WHDR_DONE) && (header[1].dwFlags & WHDR_DONE)) + return 1; + + return 0; +} + + +void StartCodec(char * strFault) +{ + strFault[0] = 0; + InitSound(FALSE); + +} + +void StopCodec(char * strFault) +{ + CloseSound(); + strFault[0] = 0; +} + +// Serial Port Stuff + +HANDLE OpenCOMPort(char * pPort, int speed, BOOL SetDTR, BOOL SetRTS, BOOL Quiet, int Stopbits) +{ + char szPort[80]; + BOOL fRetVal; + COMMTIMEOUTS CommTimeOuts; + int Err; + char buf[100]; + HANDLE fd; + DCB dcb; + + if (_memicmp(pPort, "COM", 3) == 0) + { + char * pp = (char *)pPort; + int p = atoi(&pp[3]); + sprintf(szPort, "\\\\.\\COM%d", p); + } + else + strcpy(szPort, pPort); + + // open COMM device + + fd = CreateFileA(szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (fd == (HANDLE)-1) + { + if (Quiet == 0) + { + sprintf(buf, " %s could not be opened \r\n ", pPort); + OutputDebugStringA(buf); + } + return (FALSE); + } + + Err = GetFileType(fd); + + // setup device buffers + + SetupComm(fd, 4096, 4096); + + // purge any information in the buffer + + PurgeComm(fd, PURGE_TXABORT | PURGE_RXABORT | + PURGE_TXCLEAR | PURGE_RXCLEAR); + + // set up for overlapped I/O + + CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF; + CommTimeOuts.ReadTotalTimeoutMultiplier = 0; + CommTimeOuts.ReadTotalTimeoutConstant = 0; + CommTimeOuts.WriteTotalTimeoutMultiplier = 0; + // CommTimeOuts.WriteTotalTimeoutConstant = 0 ; + CommTimeOuts.WriteTotalTimeoutConstant = 500; + SetCommTimeouts(fd, &CommTimeOuts); + + dcb.DCBlength = sizeof(DCB); + + GetCommState(fd, &dcb); + + dcb.BaudRate = speed; + dcb.ByteSize = 8; + dcb.Parity = 0; + dcb.StopBits = TWOSTOPBITS; + dcb.StopBits = Stopbits; + + // setup hardware flow control + + dcb.fOutxDsrFlow = 0; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + + dcb.fOutxCtsFlow = 0; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + + // setup software flow control + + dcb.fInX = dcb.fOutX = 0; + dcb.XonChar = 0; + dcb.XoffChar = 0; + dcb.XonLim = 100; + dcb.XoffLim = 100; + + // other various settings + + dcb.fBinary = TRUE; + dcb.fParity = FALSE; + + fRetVal = SetCommState(fd, &dcb); + + if (fRetVal) + { + if (SetDTR) + EscapeCommFunction(fd, SETDTR); + if (SetRTS) + EscapeCommFunction(fd, SETRTS); + } + else + { + sprintf(buf, "%s Setup Failed %d ", pPort, GetLastError()); + + printf(buf); + OutputDebugStringA(buf); + CloseHandle(fd); + return 0; + } + + return fd; + +} + +int ReadCOMBlock(HANDLE fd, char * Block, int MaxLength) +{ + BOOL fReadStat; + COMSTAT ComStat; + DWORD dwErrorFlags; + DWORD dwLength; + + // only try to read number of bytes in queue + + ClearCommError(fd, &dwErrorFlags, &ComStat); + + dwLength = min((DWORD)MaxLength, ComStat.cbInQue); + + if (dwLength > 0) + { + fReadStat = ReadFile(fd, Block, dwLength, &dwLength, NULL); + + if (!fReadStat) + { + dwLength = 0; + ClearCommError(fd, &dwErrorFlags, &ComStat); + } + } + + return dwLength; +} + +BOOL WriteCOMBlock(HANDLE fd, char * Block, int BytesToWrite) +{ + BOOL fWriteStat; + DWORD BytesWritten; + DWORD ErrorFlags; + COMSTAT ComStat; + + fWriteStat = WriteFile(fd, Block, BytesToWrite, + &BytesWritten, NULL); + + if ((!fWriteStat) || (BytesToWrite != BytesWritten)) + { + int Err = GetLastError(); + ClearCommError(fd, &ErrorFlags, &ComStat); + return FALSE; + } + return TRUE; +} + + +VOID CloseCOMPort(int fd) +{ + SetCommMask((HANDLE)fd, 0); + + // drop DTR + + COMClearDTR(fd); + + // purge any outstanding reads/writes and close device handle + + PurgeComm((HANDLE)fd, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); + + CloseHandle((HANDLE)fd); +} + + +VOID COMSetDTR(int fd) +{ + EscapeCommFunction((HANDLE)fd, SETDTR); +} + +VOID COMClearDTR(int fd) +{ + EscapeCommFunction((HANDLE)fd, CLRDTR); +} + +VOID COMSetRTS(int fd) +{ + EscapeCommFunction((HANDLE)fd, SETRTS); +} + +VOID COMClearRTS(int fd) +{ + EscapeCommFunction((HANDLE)fd, CLRRTS); +} + +/* + +void CatWrite(char * Buffer, int Len) +{ + if (hCATDevice) + WriteCOMBlock(hCATDevice, Buffer, Len); +} + +*/ + +void * initPulse() +{ + return NULL; +} + + + +void StdinPollReceivedSamples() +{ + short buffer[2048]; + DWORD out; + int res = ReadFile(hStdin, buffer, 2048, &out, NULL); + if (res <= 0) + { + res = GetLastError(); + printf("\nEnd of file on stdin. Exiting.\n"); + } + + short * ptr = buffer; + int i; + + for (i = 0; i < ReceiveSize; i++) + { + if (*(ptr) < min) + min = *ptr; + else if (*(ptr) > max) + max = *ptr; + ptr++; + } + + CurrentLevel = ((max - min) * 75) / 32768; // Scale to 150 max + + if ((Now - lastlevelGUI) > 2000) // 2 Secs + { + // if (WaterfallActive == 0 && SpectrumActive == 0) // Don't need to send as included in Waterfall Line + // SendtoGUI('L', &CurrentLevel, 1); // Signal Level + + lastlevelGUI = Now; + + if ((Now - lastlevelreport) > 10000) // 10 Secs + { + char HostCmd[64]; + lastlevelreport = Now; + + sprintf(HostCmd, "INPUTPEAKS %d %d", min, max); + Debugprintf("Input peaks = %d, %d", min, max); + + } + min = max = 0; + } + + // debugprintf(LOGDEBUG, "Process %d %d", inIndex, inheader[inIndex].dwBytesRecorded/2); + // if (Capturing && Loopback == FALSE) + ProcessNewSamples(buffer, 512); + + +} + + diff --git a/ardopSampleArrays.c b/ardopSampleArrays.c new file mode 100644 index 0000000..5e913ae --- /dev/null +++ b/ardopSampleArrays.c @@ -0,0 +1,16077 @@ + +#include "ARDOPC.h" + + +// These Templates are used to save lots of calculations when +// generating samples. They are pre-calculated (by Calctemplates.c) +// so they can be stored in ROM on embedded platforms + +CONST short int50BaudTwoToneLeaderTemplate[240] = { // holds just 1 symbol (20 ms) of the 50 Baud leader + + 0, -263, 0, 793, 1496, 1322, 0, -1849, -2988, -2375, + 0, 2901, 4474, 3424, 0, -3944, -5945, -4462, 0, 4978, + 7402, 5489, 0, -5996, -8836, -6499, 0, 6999, 10249, 7493, + 0, -7981, -11631, -8465, 0, 8944, 12984, 9416, 0, -9880, + -14298, -10339, 0, 10791, 15576, 11235, 0, -11670, -16809, -12099, + 0, 12519, 17998, 12931, 0, -13333, -19136, -13726, 0, 14111, + 20223, 14486, 0, -14849, -21252, -15203, 0, 15548, 22226, 15881, + 0, -16202, -23136, -16514, 0, 16814, 23985, 17103, 0, -17378, + -24767, -17643, 0, 17897, 25482, 18137, 0, -18364, -26126, -18580, + 0, 18783, 26700, 18973, 0, -19148, -27199, -19312, 0, 19463, + 27625, 19601, 0, -19723, -27974, -19833, 0, 19930, 28247, 20014, + 0, -20081, -28442, -20137, 0, 20179, 28560, 20208, 0, -20220, + -28599, -20220, 0, 20207, 28560, 20180, 0, -20137, -28442, -20082, + 0, 20013, 28247, 19931, 0, -19833, -27974, -19724, 0, 19600, + 27625, 19464, 0, -19312, -27199, -19149, 0, 18972, 26700, 18783, + 0, -18579, -26126, -18365, 0, 18137, 25482, 17897, 0, -17643, + -24767, -17379, 0, 17102, 23985, 16815, 0, -16513, -23136, -16203, + 0, 15881, 22226, 15548, 0, -15203, -21252, -14849, 0, 14485, + 20223, 14112, 0, -13726, -19136, -13333, 0, 12931, 17998, 12520, + 0, -12098, -16809, -11671, 0, 11235, 15576, 10791, 0, -10338, + -14299, -9880, 0, 9415, 12984, 8944, 0, -8465, -11631, -7982, + 0, 7493, 10249, 6999, 0, -6499, -8836, -5996, 0, 5489, + 7402, 4978, 0, -4462, -5945, -3944, 0, 3424, 4474, 2902, + 0, -2375, -2988, -1849, 0, 1322, 1496, 794, 0, -263}; + +CONST short intFSK50bdCarTemplate[12][240] = { +{ + 0, 15576, 26127, 28247, 21253, 7402, -8837, -22226, -28443, -25482, + -14299, 1496, 16810, 26700, 27975, 20223, 5946, -10249, -23137, -28560, + -24768, -12984, 2989, 17998, 27200, 27625, 19137, 4473, -11632, -23985, + -28599, -23985, -11632, 4474, 19137, 27625, 27200, 17998, 2989, -12984, + -24768, -28560, -23137, -10249, 5946, 20223, 27975, 26700, 16810, 1496, + -14300, -25482, -28443, -22226, -8837, 7402, 21254, 28247, 26127, 15576, + 0, -15576, -26127, -28247, -21253, -7402, 8837, 22226, 28443, 25482, + 14299, -1496, -16810, -26700, -27974, -20223, -5946, 10249, 23137, 28560, + 24768, 12984, -2989, -17998, -27200, -27625, -19137, -4473, 11632, 23986, + 28599, 23985, 11632, -4474, -19137, -27625, -27200, -17998, -2989, 12984, + 24768, 28560, 23137, 10249, -5946, -20223, -27975, -26700, -16810, -1496, + 14300, 25482, 28443, 22226, 8837, -7402, -21254, -28247, -26127, -15576, + 0, 15576, 26127, 28247, 21253, 7402, -8838, -22226, -28443, -25482, + -14299, 1497, 16810, 26700, 27974, 20223, 5946, -10249, -23138, -28560, + -24768, -12983, 2989, 17998, 27200, 27625, 19136, 4473, -11632, -23986, + -28599, -23985, -11632, 4474, 19137, 27625, 27200, 17998, 2989, -12984, + -24768, -28560, -23137, -10249, 5946, 20223, 27975, 26700, 16810, 1496, + -14300, -25482, -28443, -22226, -8837, 7402, 21254, 28247, 26127, 15576, + 0, -15576, -26127, -28247, -21253, -7401, 8838, 22226, 28443, 25482, + 14299, -1497, -16810, -26700, -27974, -20223, -5945, 10249, 23138, 28560, + 24768, 12983, -2989, -17998, -27200, -27625, -19136, -4473, 11632, 23986, + 28599, 23985, 11632, -4474, -19137, -27625, -27200, -17998, -2989, 12984, + 24768, 28560, 23137, 10248, -5946, -20223, -27975, -26700, -16810, -1496, + 14300, 25482, 28443, 22226, 8837, -7402, -21254, -28247, -26127, -15576}, + + { 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810, + 0, 16810, 27200, 27200, 16810, 0, -16810, -27200, -27200, -16810}, + + { 0, 17998, 27975, 25482, 11632, -7402, -23137, -28560, -21253, -4474, + 14299, 26700, 27200, 15576, -2989, -20223, -28443, -23985, -8837, 10249, + 24768, 28247, 19137, 1496, -16810, -27625, -26127, -12984, 5946, 22226, + 28599, 22226, 5946, -12984, -26127, -27625, -16810, 1496, 19137, 28247, + 24768, 10249, -8837, -23985, -28443, -20223, -2989, 15576, 27200, 26700, + 14300, -4474, -21253, -28560, -23137, -7402, 11632, 25482, 27975, 17998, + 0, -17998, -27975, -25482, -11632, 7402, 23137, 28560, 21253, 4474, + -14299, -26700, -27200, -15576, 2989, 20223, 28443, 23985, 8837, -10249, + -24768, -28247, -19137, -1496, 16810, 27625, 26127, 12984, -5946, -22226, + -28599, -22226, -5946, 12984, 26127, 27625, 16810, -1496, -19137, -28247, + -24768, -10249, 8837, 23985, 28443, 20223, 2989, -15576, -27200, -26700, + -14300, 4474, 21253, 28560, 23137, 7402, -11632, -25482, -27975, -17998, + 0, 17998, 27975, 25482, 11632, -7402, -23137, -28560, -21253, -4474, + 14299, 26700, 27200, 15576, -2989, -20223, -28443, -23985, -8837, 10249, + 24768, 28247, 19137, 1496, -16810, -27625, -26127, -12984, 5946, 22226, + 28599, 22226, 5946, -12984, -26127, -27625, -16810, 1496, 19137, 28247, + 24768, 10249, -8837, -23985, -28443, -20223, -2989, 15576, 27200, 26700, + 14300, -4473, -21253, -28560, -23137, -7402, 11632, 25482, 27975, 17998, + 0, -17998, -27975, -25482, -11632, 7402, 23137, 28560, 21253, 4474, + -14299, -26700, -27200, -15576, 2989, 20223, 28443, 23986, 8837, -10249, + -24768, -28247, -19137, -1496, 16810, 27625, 26127, 12984, -5946, -22226, + -28599, -22226, -5946, 12984, 26127, 27625, 16810, -1496, -19137, -28247, + -24768, -10249, 8837, 23985, 28443, 20223, 2989, -15576, -27200, -26700, + -14300, 4473, 21253, 28560, 23137, 7402, -11632, -25482, -27975, -17998}, + + { 0, 19137, 28443, 23137, 5946, -14299, -27200, -26127, -11632, 8837, + 24768, 27975, 16810, -2989, -21253, -28600, -21253, -2989, 16810, 27975, + 24768, 8837, -11632, -26127, -27200, -14299, 5946, 23137, 28443, 19137, + 0, -19137, -28443, -23137, -5946, 14300, 27200, 26127, 11632, -8837, + -24768, -27975, -16810, 2989, 21253, 28599, 21253, 2989, -16810, -27975, + -24768, -8837, 11632, 26127, 27200, 14299, -5946, -23137, -28443, -19137, + 0, 19137, 28443, 23137, 5946, -14300, -27200, -26127, -11632, 8837, + 24768, 27974, 16810, -2989, -21254, -28599, -21253, -2989, 16810, 27975, + 24768, 8837, -11632, -26127, -27200, -14299, 5946, 23137, 28443, 19137, + 0, -19137, -28443, -23137, -5946, 14300, 27200, 26127, 11632, -8838, + -24768, -27974, -16810, 2989, 21254, 28599, 21253, 2989, -16810, -27975, + -24768, -8837, 11632, 26127, 27200, 14299, -5946, -23137, -28443, -19137, + 0, 19137, 28443, 23137, 5946, -14300, -27200, -26127, -11632, 8838, + 24768, 27974, 16810, -2989, -21254, -28599, -21253, -2989, 16810, 27975, + 24768, 8837, -11632, -26127, -27200, -14299, 5946, 23138, 28443, 19136, + 0, -19137, -28443, -23137, -5946, 14300, 27200, 26127, 11632, -8838, + -24768, -27974, -16810, 2989, 21254, 28599, 21253, 2989, -16810, -27975, + -24768, -8837, 11632, 26127, 27200, 14299, -5946, -23138, -28443, -19136, + 0, 19137, 28443, 23137, 5945, -14300, -27200, -26127, -11632, 8838, + 24768, 27974, 16810, -2989, -21254, -28599, -21253, -2989, 16810, 27975, + 24768, 8837, -11632, -26127, -27200, -14299, 5946, 23138, 28443, 19136, + 0, -19137, -28443, -23137, -5945, 14300, 27200, 26127, 11632, -8838, + -24768, -27974, -16810, 2989, 21254, 28599, 21253, 2989, -16810, -27975, + -24768, -8837, 11633, 26127, 27200, 14299, -5946, -23138, -28443, -19136}, + + { 0, 18574, 28247, 24385, 8837, -10944, -25482, -27809, -16810, 2243, + 20223, 28511, 23137, 6676, -12984, -26422, -27200, -14943, 4474, 21747, + 28599, 21747, 4474, -14943, -27200, -26422, -12984, 6676, 23137, 28511, + 20223, 2243, -16810, -27809, -25482, -10944, 8837, 24385, 28247, 18574, + 0, -18574, -28247, -24385, -8837, 10944, 25482, 27809, 16810, -2243, + -20223, -28511, -23137, -6676, 12984, 26422, 27200, 14943, -4473, -21747, + -28599, -21747, -4474, 14943, 27200, 26422, 12984, -6676, -23137, -28511, + -20223, -2244, 16810, 27809, 25482, 10944, -8837, -24385, -28247, -18574, + 0, 18574, 28247, 24385, 8837, -10944, -25482, -27809, -16810, 2243, + 20223, 28511, 23137, 6676, -12984, -26422, -27200, -14943, 4473, 21747, + 28599, 21747, 4474, -14943, -27200, -26422, -12984, 6676, 23137, 28511, + 20223, 2244, -16810, -27809, -25482, -10944, 8837, 24385, 28247, 18574, + 0, -18574, -28247, -24385, -8838, 10944, 25482, 27809, 16810, -2243, + -20223, -28511, -23137, -6676, 12984, 26422, 27200, 14943, -4473, -21747, + -28599, -21747, -4474, 14943, 27200, 26423, 12984, -6676, -23137, -28511, + -20223, -2244, 16810, 27809, 25482, 10944, -8837, -24385, -28247, -18574, + 0, 18574, 28247, 24385, 8838, -10944, -25482, -27809, -16810, 2243, + 20223, 28511, 23137, 6676, -12983, -26422, -27200, -14943, 4473, 21747, + 28599, 21747, 4474, -14943, -27200, -26423, -12984, 6676, 23137, 28511, + 20223, 2244, -16810, -27809, -25482, -10944, 8837, 24385, 28247, 18574, + 0, -18574, -28247, -24385, -8838, 10944, 25482, 27809, 16810, -2243, + -20223, -28511, -23138, -6676, 12983, 26422, 27200, 14943, -4473, -21747, + -28599, -21747, -4474, 14943, 27200, 26423, 12984, -6676, -23137, -28511, + -20223, -2244, 16810, 27809, 25482, 10944, -8837, -24385, -28247, -18574}, + + { 0, 19686, 28560, 21747, 2989, -17410, -28247, -23570, -5946, 14943, + 27625, 25134, 8837, -12312, -26700, -26422, -11632, 9546, 25482, 27422, + 14299, -6676, -23985, -28121, -16810, 3733, 22226, 28511, 19137, -748, + -20223, -28590, -21253, -2243, 17998, 28355, 23137, 5211, -15576, -27809, + -24768, -8122, 12984, 26959, 26127, 10944, -10249, -25813, -27200, -13646, + 7402, 24385, 27975, 16199, -4474, -22689, -28443, -18574, 1496, 20745, + 28599, 20745, 1496, -18574, -28443, -22689, -4473, 16199, 27975, 24385, + 7402, -13646, -27200, -25813, -10249, 10944, 26127, 26959, 12984, -8122, + -24768, -27809, -15576, 5212, 23137, 28355, 17998, -2243, -21253, -28590, + -20223, -748, 19137, 28511, 22226, 3732, -16810, -28121, -23985, -6676, + 14300, 27422, 25482, 9546, -11632, -26422, -26700, -12312, 8837, 25134, + 27625, 14943, -5946, -23570, -28247, -17410, 2989, 21747, 28560, 19686, + 0, -19687, -28560, -21747, -2989, 17410, 28247, 23569, 5946, -14943, + -27625, -25134, -8837, 12312, 26700, 26422, 11632, -9546, -25482, -27422, + -14299, 6676, 23986, 28121, 16810, -3733, -22226, -28511, -19137, 748, + 20223, 28590, 21253, 2243, -17998, -28355, -23137, -5211, 15576, 27809, + 24768, 8122, -12984, -26959, -26127, -10944, 10249, 25813, 27200, 13646, + -7402, -24385, -27974, -16199, 4474, 22689, 28443, 18574, -1496, -20745, + -28599, -20745, -1496, 18574, 28443, 22689, 4473, -16199, -27975, -24385, + -7402, 13646, 27200, 25813, 10249, -10944, -26127, -26959, -12983, 8122, + 24768, 27809, 15576, -5212, -23137, -28355, -17998, 2244, 21254, 28590, + 20223, 748, -19137, -28511, -22226, -3732, 16810, 28121, 23985, 6676, + -14300, -27422, -25482, -9546, 11632, 26423, 26700, 12312, -8838, -25134, + -27625, -14943, 5946, 23570, 28247, 17410, -2989, -21747, -28560, -19686}, + + { 0, 20745, 28560, 18574, -2989, -22689, -28247, -16199, 5946, 24385, + 27625, 13646, -8837, -25813, -26700, -10944, 11632, 26959, 25482, 8122, + -14299, -27809, -23986, -5212, 16810, 28355, 22226, 2244, -19137, -28590, + -20223, 748, 21253, 28511, 17998, -3732, -23137, -28121, -15576, 6676, + 24768, 27422, 12984, -9546, -26127, -26423, -10249, 12312, 27200, 25134, + 7402, -14943, -27974, -23570, -4474, 17410, 28443, 21747, 1497, -19686, + -28599, -19687, 1496, 21747, 28443, 17410, -4473, -23569, -27975, -14943, + 7401, 25134, 27200, 12312, -10249, -26422, -26127, -9547, 12983, 27422, + 24768, 6676, -15576, -28121, -23138, -3733, 17998, 28511, 21254, 749, + -20222, -28590, -19137, 2243, 22226, 28355, 16810, -5211, -23985, -27809, + -14300, 8122, 25482, 26959, 11633, -10944, -26700, -25814, -8838, 13646, + 27625, 24385, 5946, -16198, -28247, -22690, -2989, 18573, 28560, 20746, + 0, -20745, -28560, -18574, 2989, 22689, 28247, 16199, -5945, -24385, + -27625, -13647, 8837, 25813, 26700, 10945, -11632, -26959, -25483, -8123, + 14299, 27809, 23986, 5212, -16810, -28355, -22226, -2244, 19136, 28590, + 20223, -748, -21253, -28511, -17999, 3732, 23137, 28121, 15577, -6675, + -24768, -27422, -12984, 9546, 26127, 26423, 10249, -12312, -27200, -25134, + -7402, 14942, 27974, 23570, 4474, -17410, -28443, -21748, -1497, 19686, + 28599, 19687, -1496, -21747, -28443, -17411, 4473, 23569, 27975, 14944, + -7401, -25133, -27200, -12313, 10248, 26422, 26127, 9547, -12983, -27422, + -24768, -6677, 15575, 28120, 23138, 3733, -17997, -28511, -21254, -749, + 20222, 28590, 19137, -2243, -22225, -28355, -16811, 5211, 23985, 27809, + 14300, -8121, -25482, -26959, -11633, 10943, 26700, 25814, 8838, -13645, + -27625, -24386, -5947, 16198, 28247, 22690, 2990, -18573, -28560, -20746}, + + { 0, 21747, 28247, 14943, -8837, -26422, -25482, -6676, 16810, 28511, + 20223, -2243, -23137, -27809, -12984, 10944, 27200, 24385, 4474, -18574, + -28599, -18574, 4473, 24385, 27200, 10944, -12984, -27809, -23137, -2243, + 20223, 28511, 16810, -6676, -25482, -26422, -8837, 14943, 28247, 21747, + 0, -21747, -28247, -14943, 8837, 26422, 25482, 6676, -16810, -28511, + -20223, 2243, 23137, 27809, 12984, -10944, -27200, -24385, -4474, 18574, + 28599, 18574, -4473, -24385, -27200, -10944, 12984, 27809, 23137, 2244, + -20223, -28511, -16810, 6676, 25482, 26423, 8838, -14943, -28247, -21747, + 0, 21747, 28247, 14943, -8837, -26422, -25482, -6676, 16810, 28511, + 20223, -2243, -23137, -27809, -12984, 10944, 27200, 24385, 4474, -18574, + -28599, -18574, 4473, 24385, 27200, 10944, -12983, -27809, -23138, -2244, + 20223, 28511, 16810, -6676, -25482, -26423, -8838, 14943, 28247, 21747, + 0, -21747, -28247, -14943, 8837, 26422, 25482, 6676, -16810, -28511, + -20223, 2243, 23137, 27809, 12984, -10944, -27200, -24385, -4474, 18573, + 28599, 18574, -4473, -24385, -27200, -10945, 12983, 27809, 23138, 2244, + -20223, -28511, -16810, 6676, 25482, 26423, 8838, -14943, -28247, -21747, + 0, 21747, 28247, 14943, -8837, -26422, -25482, -6676, 16810, 28511, + 20223, -2243, -23137, -27809, -12984, 10944, 27200, 24385, 4474, -18573, + -28599, -18574, 4473, 24385, 27200, 10945, -12983, -27809, -23138, -2244, + 20222, 28511, 16810, -6676, -25482, -26423, -8838, 14943, 28247, 21747, + 0, -21747, -28247, -14943, 8837, 26422, 25482, 6676, -16810, -28511, + -20223, 2243, 23137, 27809, 12984, -10944, -27200, -24385, -4474, 18573, + 28599, 18574, -4473, -24385, -27200, -10945, 12983, 27809, 23138, 2244, + -20222, -28511, -16811, 6676, 25482, 26423, 8838, -14943, -28247, -21747}, + + { 0, 21253, 28443, 16810, -5946, -24768, -27200, -11632, 11632, 27200, + 24768, 5946, -16810, -28443, -21253, 0, 21253, 28443, 16810, -5946, + -24768, -27200, -11632, 11632, 27200, 24768, 5946, -16810, -28443, -21253, + 0, 21253, 28443, 16810, -5946, -24768, -27200, -11632, 11632, 27200, + 24768, 5946, -16810, -28443, -21253, 0, 21253, 28443, 16810, -5946, + -24768, -27200, -11632, 11632, 27200, 24768, 5946, -16810, -28443, -21253, + 0, 21253, 28443, 16810, -5946, -24768, -27200, -11632, 11632, 27200, + 24768, 5946, -16810, -28443, -21253, 0, 21253, 28443, 16810, -5946, + -24768, -27200, -11632, 11632, 27200, 24768, 5946, -16810, -28443, -21253, + 0, 21253, 28443, 16810, -5946, -24768, -27200, -11632, 11632, 27200, + 24768, 5946, -16810, -28443, -21253, 0, 21254, 28443, 16810, -5946, + -24768, -27200, -11632, 11632, 27200, 24768, 5946, -16810, -28443, -21253, + 0, 21254, 28443, 16810, -5946, -24768, -27200, -11632, 11632, 27200, + 24768, 5946, -16810, -28443, -21253, 0, 21254, 28443, 16810, -5946, + -24768, -27200, -11632, 11632, 27200, 24768, 5946, -16810, -28443, -21253, + 0, 21254, 28443, 16810, -5946, -24768, -27200, -11632, 11632, 27200, + 24768, 5946, -16810, -28443, -21253, 0, 21254, 28443, 16810, -5946, + -24768, -27200, -11632, 11632, 27200, 24768, 5946, -16810, -28443, -21253, + 0, 21254, 28443, 16810, -5946, -24768, -27200, -11632, 11632, 27200, + 24768, 5946, -16810, -28443, -21253, 0, 21254, 28443, 16810, -5946, + -24768, -27200, -11632, 11632, 27200, 24768, 5946, -16810, -28443, -21253, + 0, 21254, 28443, 16810, -5946, -24768, -27200, -11632, 11632, 27200, + 24768, 5946, -16810, -28443, -21253, 0, 21254, 28443, 16810, -5946, + -24768, -27200, -11632, 11632, 27200, 24768, 5946, -16810, -28443, -21253}, + + { 0, 22226, 27975, 12984, -11632, -27625, -23137, -1496, 21253, 28247, + 14300, -10249, -27200, -23985, -2989, 20223, 28443, 15576, -8837, -26700, + -24768, -4474, 19137, 28560, 16810, -7402, -26127, -25482, -5946, 17998, + 28599, 17998, -5946, -25482, -26127, -7402, 16810, 28560, 19137, -4473, + -24768, -26700, -8838, 15576, 28443, 20223, -2989, -23985, -27200, -10249, + 14299, 28247, 21254, -1496, -23137, -27625, -11632, 12983, 27974, 22226, + 0, -22226, -27975, -12984, 11632, 27625, 23137, 1496, -21253, -28247, + -14300, 10249, 27200, 23986, 2989, -20223, -28443, -15576, 8837, 26700, + 24768, 4474, -19136, -28560, -16810, 7401, 26127, 25482, 5946, -17998, + -28599, -17998, 5946, 25482, 26127, 7402, -16810, -28560, -19137, 4473, + 24768, 26700, 8838, -15576, -28443, -20223, 2989, 23985, 27200, 10249, + -14299, -28247, -21254, 1496, 23137, 27625, 11632, -12983, -27974, -22226, + 0, 22226, 27975, 12984, -11632, -27625, -23138, -1497, 21253, 28247, + 14300, -10248, -27200, -23986, -2989, 20222, 28443, 15576, -8837, -26700, + -24768, -4474, 19136, 28560, 16810, -7401, -26127, -25482, -5946, 17998, + 28599, 17998, -5945, -25482, -26127, -7402, 16810, 28560, 19137, -4473, + -24768, -26700, -8838, 15576, 28443, 20223, -2989, -23985, -27200, -10249, + 14299, 28247, 21254, -1496, -23137, -27625, -11633, 12983, 27974, 22226, + 0, -22226, -27975, -12984, 11632, 27625, 23138, 1497, -21253, -28247, + -14300, 10248, 27200, 23986, 2990, -20222, -28443, -15577, 8837, 26700, + 24768, 4474, -19136, -28560, -16811, 7401, 26127, 25483, 5946, -17998, + -28599, -17999, 5945, 25482, 26127, 7402, -16810, -28560, -19137, 4473, + 24768, 26700, 8838, -15576, -28443, -20223, 2988, 23985, 27200, 10249, + -14299, -28247, -21254, 1496, 23137, 27625, 11633, -12983, -27974, -22226}, + + { 0, 23137, 27200, 8837, -16810, -28600, -16810, 8837, 27200, 23137, + 0, -23137, -27200, -8837, 16810, 28599, 16810, -8837, -27200, -23137, + 0, 23137, 27200, 8837, -16810, -28599, -16810, 8837, 27200, 23137, + 0, -23137, -27200, -8837, 16810, 28599, 16810, -8837, -27200, -23137, + 0, 23137, 27200, 8837, -16810, -28599, -16810, 8837, 27200, 23137, + 0, -23137, -27200, -8837, 16810, 28599, 16810, -8837, -27200, -23137, + 0, 23137, 27200, 8837, -16810, -28599, -16810, 8837, 27200, 23137, + 0, -23137, -27200, -8837, 16810, 28599, 16810, -8837, -27200, -23137, + 0, 23137, 27200, 8837, -16810, -28599, -16810, 8837, 27200, 23137, + 0, -23137, -27200, -8838, 16810, 28599, 16810, -8837, -27200, -23137, + 0, 23137, 27200, 8838, -16810, -28599, -16810, 8837, 27200, 23137, + 0, -23137, -27200, -8838, 16810, 28599, 16810, -8837, -27200, -23137, + 0, 23137, 27200, 8838, -16810, -28599, -16810, 8837, 27200, 23137, + 0, -23137, -27200, -8838, 16810, 28599, 16810, -8837, -27200, -23137, + 0, 23137, 27200, 8838, -16810, -28599, -16810, 8837, 27200, 23137, + 0, -23137, -27200, -8838, 16810, 28599, 16810, -8837, -27200, -23138, + 0, 23137, 27200, 8838, -16810, -28599, -16810, 8837, 27200, 23138, + 0, -23137, -27200, -8838, 16810, 28599, 16810, -8837, -27200, -23138, + 0, 23137, 27200, 8838, -16810, -28599, -16810, 8837, 27200, 23138, + 0, -23137, -27200, -8838, 16810, 28599, 16810, -8837, -27200, -23138, + 0, 23137, 27200, 8838, -16810, -28599, -16810, 8837, 27200, 23138, + 0, -23137, -27200, -8838, 16810, 28599, 16810, -8837, -27200, -23138, + 0, 23137, 27200, 8838, -16810, -28599, -16810, 8837, 27200, 23138, + 0, -23137, -27200, -8838, 16810, 28599, 16810, -8837, -27200, -23138}, + + { 0, 23985, 26127, 4474, -21253, -27625, -8837, 17998, 28443, 12984, + -14300, -28560, -16810, 10249, 27975, 20223, -5946, -26700, -23137, 1496, + 24768, 25482, 2989, -22226, -27200, -7402, 19137, 28247, 11632, -15576, + -28599, -15576, 11632, 28247, 19137, -7402, -27200, -22226, 2989, 25482, + 24768, 1496, -23137, -26700, -5946, 20223, 27975, 10249, -16810, -28560, + -14299, 12984, 28443, 17998, -8837, -27625, -21253, 4474, 26127, 23985, + 0, -23986, -26127, -4473, 21254, 27625, 8837, -17998, -28443, -12984, + 14300, 28560, 16810, -10249, -27975, -20223, 5946, 26700, 23137, -1496, + -24768, -25482, -2989, 22226, 27200, 7402, -19137, -28247, -11632, 15576, + 28599, 15576, -11632, -28247, -19137, 7402, 27200, 22226, -2989, -25482, + -24768, -1496, 23137, 26700, 5946, -20223, -27974, -10249, 16810, 28560, + 14299, -12984, -28443, -17998, 8838, 27625, 21253, -4474, -26127, -23985, + 0, 23986, 26127, 4473, -21254, -27625, -8837, 17998, 28443, 12983, + -14300, -28560, -16810, 10249, 27975, 20223, -5946, -26700, -23137, 1497, + 24768, 25482, 2989, -22226, -27200, -7401, 19137, 28247, 11632, -15576, + -28599, -15576, 11632, 28247, 19136, -7402, -27200, -22226, 2989, 25482, + 24768, 1496, -23138, -26700, -5946, 20223, 27974, 10249, -16810, -28560, + -14299, 12984, 28443, 17998, -8838, -27625, -21253, 4474, 26127, 23985, + 0, -23986, -26127, -4473, 21254, 27625, 8837, -17998, -28443, -12983, + 14300, 28560, 16810, -10249, -27975, -20223, 5946, 26700, 23137, -1497, + -24768, -25482, -2989, 22226, 27200, 7401, -19137, -28247, -11632, 15576, + 28599, 15576, -11632, -28247, -19136, 7402, 27200, 22226, -2989, -25482, + -24768, -1496, 23138, 26700, 5945, -20223, -27974, -10248, 16810, 28560, + 14299, -12984, -28443, -17998, 8838, 27625, 21253, -4474, -26127, -23985}}; + + +// 100 Baud FSK for one carrier. Used for OFDM ACK + +CONST short intFSK100bdCarTemplate[4][120] = { + {0, 18574, 28247, 24385, 8837, -10944, -25482, -27809, -16810, 2243, + 20223, 28511, 23137, 6676, -12984, -26422, -27200, -14943, 4474, 21747, + 28599, 21747, 4474, -14943, -27200, -26422, -12984, 6676, 23137, 28511, + 20223, 2243, -16810, -27809, -25482, -10944, 8837, 24385, 28247, 18574, + 0, -18574, -28247, -24385, -8837, 10944, 25482, 27809, 16810, -2243, + -20223, -28511, -23137, -6676, 12984, 26422, 27200, 14943, -4473, -21747, + -28599, -21747, -4474, 14943, 27200, 26422, 12984, -6676, -23137, -28511, + -20223, -2244, 16810, 27809, 25482, 10944, -8837, -24385, -28247, -18574, + 0, 18574, 28247, 24385, 8837, -10944, -25482, -27809, -16810, 2243, + 20223, 28511, 23137, 6676, -12984, -26422, -27200, -14943, 4473, 21747, + 28599, 21747, 4474, -14943, -27200, -26422, -12984, 6676, 23137, 28511, + 20223, 2244, -16810, -27809, -25482, -10944, 8837, 24385, 28247, 18574}, + + { 0, 19686, 28560, 21747, 2989, -17410, -28247, -23570, -5946, 14943, + 27625, 25134, 8837, -12312, -26700, -26422, -11632, 9546, 25482, 27422, + 14299, -6676, -23985, -28121, -16810, 3733, 22226, 28511, 19137, -748, + -20223, -28590, -21253, -2243, 17998, 28355, 23137, 5211, -15576, -27809, + -24768, -8122, 12984, 26959, 26127, 10944, -10249, -25813, -27200, -13646, + 7402, 24385, 27974, 16199, -4474, -22689, -28443, -18574, 1496, 20745, + 28599, 20745, 1496, -18574, -28443, -22689, -4473, 16199, 27975, 24385, + 7402, -13646, -27200, -25813, -10249, 10944, 26127, 26959, 12983, -8122, + -24768, -27809, -15576, 5212, 23138, 28355, 17998, -2244, -21254, -28590, + -20223, -748, 19137, 28511, 22226, 3732, -16810, -28121, -23985, -6676, + 14300, 27422, 25482, 9546, -11632, -26423, -26700, -12312, 8838, 25134, + 27625, 14943, -5946, -23570, -28247, -17410, 2989, 21747, 28560, 19686}, + + { 0, 20745, 28560, 18574, -2989, -22689, -28247, -16199, 5946, 24385, + 27625, 13646, -8837, -25813, -26700, -10944, 11632, 26959, 25482, 8122, + -14299, -27809, -23986, -5212, 16810, 28355, 22226, 2244, -19137, -28590, + -20223, 748, 21253, 28511, 17998, -3732, -23137, -28121, -15576, 6676, + 24768, 27422, 12984, -9546, -26127, -26423, -10249, 12312, 27200, 25134, + 7402, -14943, -27974, -23570, -4474, 17410, 28443, 21747, 1496, -19686, + -28599, -19687, 1496, 21747, 28443, 17410, -4473, -23569, -27975, -14943, + 7402, 25134, 27200, 12312, -10249, -26422, -26127, -9547, 12983, 27422, + 24768, 6676, -15576, -28121, -23138, -3733, 17998, 28511, 21254, 748, + -20223, -28590, -19137, 2243, 22226, 28355, 16810, -5211, -23985, -27809, + -14300, 8122, 25482, 26959, 11632, -10944, -26700, -25814, -8838, 13646, + 27625, 24385, 5946, -16198, -28247, -22690, -2989, 18574, 28560, 20745}, + + { 0, 21747, 28247, 14943, -8837, -26422, -25482, -6676, 16810, 28511, + 20223, -2243, -23137, -27809, -12984, 10944, 27200, 24385, 4474, -18574, + -28599, -18574, 4474, 24385, 27200, 10944, -12984, -27809, -23137, -2243, + 20223, 28511, 16810, -6676, -25482, -26422, -8837, 14943, 28247, 21747, + 0, -21747, -28247, -14943, 8837, 26422, 25482, 6676, -16810, -28511, + -20223, 2243, 23137, 27809, 12984, -10944, -27200, -24385, -4474, 18574, + 28599, 18574, -4474, -24385, -27200, -10944, 12984, 27809, 23137, 2243, + -20223, -28511, -16810, 6676, 25482, 26422, 8837, -14943, -28247, -21747, + 0, 21747, 28247, 14943, -8837, -26422, -25482, -6676, 16810, 28511, + 20223, -2243, -23137, -27809, -12984, 10944, 27200, 24385, 4473, -18574, + -28599, -18574, 4474, 24385, 27200, 10944, -12984, -27809, -23137, -2243, + 20223, 28511, 16810, -6676, -25482, -26422, -8837, 14943, 28247, 21747}}; + +CONST short intQAM50bdCarTemplate[11][4][120] = +{ + { + {// Carrier 0 Phase 0 + 0, 8034, 15282, 21034, 24727, 26000, 24727, 21034, 15282, 8034, + 0, -8034, -15282, -21034, -24727, -26000, -24727, -21034, -15282, -8034, + 0, 8034, 15282, 21034, 24727, 26000, 24727, 21034, 15282, 8034, + 0, -8034, -15282, -21034, -24727, -26000, -24727, -21034, -15282, -8034, + 0, 8034, 15282, 21034, 24727, 26000, 24727, 21034, 15282, 8034, + 0, -8034, -15282, -21034, -24727, -26000, -24727, -21034, -15282, -8034, + 0, 8034, 15282, 21034, 24727, 26000, 24727, 21034, 15282, 8034, + 0, -8034, -15282, -21034, -24727, -26000, -24727, -21034, -15282, -8034, + 0, 8034, 15282, 21034, 24727, 26000, 24727, 21034, 15282, 8034, + 0, -8034, -15282, -21034, -24727, -26000, -24727, -21034, -15282, -8034, + 0, 8034, 15282, 21034, 24727, 26000, 24727, 21034, 15282, 8034, + 0, -8034, -15282, -21034, -24727, -26000, -24727, -21034, -15282, -8034}, + {// Carrier 0 Phase 1 + 18384, 23166, 25679, 25679, 23166, 18384, 11803, 4067, -4067, -11803, + -18384, -23166, -25679, -25679, -23166, -18384, -11803, -4067, 4067, 11803, + 18384, 23166, 25679, 25679, 23166, 18384, 11803, 4067, -4067, -11803, + -18384, -23166, -25679, -25679, -23166, -18384, -11803, -4067, 4067, 11803, + 18384, 23166, 25679, 25679, 23166, 18384, 11803, 4067, -4067, -11803, + -18384, -23166, -25679, -25679, -23166, -18384, -11803, -4067, 4067, 11803, + 18384, 23166, 25679, 25679, 23166, 18384, 11803, 4067, -4067, -11803, + -18384, -23166, -25679, -25679, -23166, -18384, -11803, -4067, 4067, 11803, + 18384, 23166, 25679, 25679, 23166, 18384, 11803, 4067, -4067, -11803, + -18384, -23166, -25679, -25679, -23166, -18384, -11803, -4067, 4067, 11803, + 18384, 23166, 25679, 25679, 23166, 18384, 11803, 4067, -4067, -11803, + -18384, -23166, -25679, -25679, -23166, -18384, -11803, -4067, 4067, 11803}, + {// Carrier 0 Phase 2 + 26000, 24727, 21034, 15282, 8034, 0, -8034, -15282, -21034, -24727, + -26000, -24727, -21034, -15282, -8034, 0, 8034, 15282, 21034, 24727, + 26000, 24727, 21034, 15282, 8034, 0, -8034, -15282, -21034, -24727, + -26000, -24727, -21034, -15282, -8034, 0, 8034, 15282, 21034, 24727, + 26000, 24727, 21034, 15282, 8034, 0, -8034, -15282, -21034, -24727, + -26000, -24727, -21034, -15282, -8034, 0, 8034, 15282, 21034, 24727, + 26000, 24727, 21034, 15282, 8034, 0, -8034, -15282, -21034, -24727, + -26000, -24727, -21034, -15282, -8034, 0, 8034, 15282, 21034, 24727, + 26000, 24727, 21034, 15282, 8034, 0, -8034, -15282, -21034, -24727, + -26000, -24727, -21034, -15282, -8034, 0, 8034, 15282, 21034, 24727, + 26000, 24727, 21034, 15282, 8034, 0, -8034, -15282, -21034, -24727, + -26000, -24727, -21034, -15282, -8034, 0, 8034, 15282, 21034, 24727}, + {// Carrier 0 Phase 3 + 18384, 11803, 4067, -4067, -11803, -18384, -23166, -25679, -25679, -23166, + -18384, -11803, -4067, 4067, 11803, 18384, 23166, 25679, 25679, 23166, + 18384, 11803, 4067, -4067, -11803, -18384, -23166, -25679, -25679, -23166, + -18384, -11803, -4067, 4067, 11803, 18384, 23166, 25679, 25679, 23166, + 18384, 11803, 4067, -4067, -11803, -18384, -23166, -25679, -25679, -23166, + -18384, -11803, -4067, 4067, 11803, 18384, 23166, 25679, 25679, 23166, + 18384, 11803, 4067, -4067, -11803, -18384, -23166, -25679, -25679, -23166, + -18384, -11803, -4067, 4067, 11803, 18384, 23166, 25679, 25679, 23166, + 18384, 11803, 4067, -4067, -11803, -18384, -23166, -25679, -25679, -23166, + -18384, -11803, -4067, 4067, 11803, 18384, 23166, 25679, 25679, 23166, + 18384, 11803, 4067, -4067, -11803, -18384, -23166, -25679, -25679, -23166, + -18384, -11803, -4067, 4067, 11803, 18384, 23166, 25679, 25679, 23166}, + }, + + {{// Carrier 1 Phase 0 + 0, 10575, 19321, 24727, 25857, 22516, 15282, 5405, -5405, -15282, + -22516, -25857, -24727, -19321, -10575, 0, 10575, 19321, 24727, 25857, + 22516, 15282, 5405, -5405, -15282, -22516, -25857, -24727, -19321, -10575, + 0, 10575, 19321, 24727, 25857, 22516, 15282, 5405, -5405, -15282, + -22516, -25857, -24727, -19321, -10575, 0, 10575, 19321, 24727, 25857, + 22516, 15282, 5405, -5405, -15282, -22516, -25857, -24727, -19321, -10575, + 0, 10575, 19321, 24727, 25857, 22516, 15282, 5405, -5405, -15282, + -22516, -25857, -24727, -19321, -10575, 0, 10575, 19321, 24727, 25857, + 22516, 15282, 5405, -5405, -15282, -22516, -25857, -24727, -19321, -10575, + 0, 10575, 19321, 24727, 25857, 22516, 15282, 5405, -5405, -15282, + -22516, -25857, -24727, -19321, -10575, 0, 10575, 19321, 24727, 25857, + 22516, 15282, 5405, -5405, -15282, -22516, -25857, -24727, -19321, -10575}, + { +// Carrier 1 Phase 1 + 18384, 24273, 25964, 23166, 16362, 6729, -4067, -14160, -21805, -25679, + -25114, -20205, -11803, -1360, 9317, 18384, 24273, 25964, 23166, 16362, + 6729, -4067, -14160, -21805, -25679, -25114, -20205, -11803, -1360, 9317, + 18384, 24273, 25964, 23166, 16362, 6729, -4067, -14160, -21805, -25679, + -25114, -20205, -11803, -1360, 9317, 18384, 24273, 25964, 23166, 16362, + 6729, -4067, -14160, -21805, -25679, -25114, -20205, -11803, -1360, 9317, + 18384, 24273, 25964, 23166, 16362, 6729, -4067, -14160, -21805, -25679, + -25114, -20205, -11803, -1360, 9317, 18384, 24273, 25964, 23166, 16362, + 6729, -4067, -14160, -21805, -25679, -25114, -20205, -11803, -1360, 9317, + 18384, 24273, 25964, 23166, 16362, 6729, -4067, -14160, -21805, -25679, + -25114, -20205, -11803, -1360, 9317, 18384, 24273, 25964, 23166, 16362, + 6729, -4067, -14160, -21805, -25679, -25114, -20205, -11803, -1360, 9317}, + { +// Carrier 1 Phase 2 + 26000, 23752, 17397, 8034, -2717, -12999, -21034, -25431, -25431, -21034, + -13000, -2717, 8034, 17397, 23752, 26000, 23752, 17397, 8034, -2717, + -13000, -21034, -25431, -25431, -21034, -12999, -2717, 8034, 17397, 23752, + 26000, 23752, 17397, 8034, -2717, -13000, -21034, -25431, -25431, -21034, + -12999, -2717, 8034, 17397, 23752, 26000, 23752, 17397, 8034, -2717, + -13000, -21034, -25431, -25431, -21034, -12999, -2717, 8034, 17397, 23752, + 26000, 23752, 17397, 8034, -2717, -13000, -21034, -25431, -25431, -21034, + -12999, -2717, 8034, 17397, 23752, 26000, 23752, 17397, 8034, -2717, + -13000, -21034, -25431, -25431, -21034, -12999, -2717, 8034, 17397, 23752, + 26000, 23752, 17397, 8034, -2717, -13000, -21034, -25431, -25431, -21034, + -12999, -2717, 8034, 17397, 23752, 26000, 23752, 17397, 8034, -2717, + -13000, -21034, -25431, -25431, -21034, -12999, -2717, 8034, 17397, 23752}, + { +// Carrier 1 Phase 3 + 18384, 9317, -1360, -11803, -20205, -25114, -25679, -21805, -14160, -4067, + 6729, 16362, 23166, 25964, 24273, 18384, 9317, -1360, -11803, -20205, + -25114, -25679, -21805, -14160, -4067, 6729, 16362, 23166, 25964, 24273, + 18384, 9317, -1360, -11803, -20205, -25114, -25679, -21805, -14160, -4067, + 6729, 16362, 23166, 25964, 24273, 18384, 9317, -1360, -11803, -20205, + -25114, -25679, -21805, -14160, -4067, 6729, 16362, 23166, 25964, 24273, + 18384, 9317, -1360, -11803, -20205, -25114, -25679, -21805, -14160, -4067, + 6729, 16362, 23166, 25964, 24273, 18384, 9317, -1360, -11803, -20205, + -25114, -25679, -21805, -14160, -4067, 6729, 16362, 23166, 25964, 24273, + 18384, 9317, -1360, -11803, -20205, -25114, -25679, -21805, -14160, -4067, + 6729, 16362, 23166, 25964, 24273, 18384, 9317, -1360, -11803, -20205, + -25114, -25679, -21805, -14160, -4067, 6729, 16362, 23166, 25964, 24273}, + }, + + {{// Carrier 2 Phase 0 + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, -22516, -26000, + -22516, -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, 22516, 13000, + 0, -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, 0, 12999, + 22516, 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, -22516, -26000, + -22516, -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, 22516, 13000, + 0, -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, 0, 12999, + 22516, 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000}, + { +// Carrier 2 Phase 1 + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, -25114, -18384, + -6729, 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, 6729, -6729, + -18384, -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, 18384, 25114, + 25114, 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, -25114, -18384, + -6729, 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, 6729, -6729, + -18384, -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, 18384, 25114, + 25114, 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729}, + { +// Carrier 2 Phase 2 + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, 0, + 12999, 22516, 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, -22516, + -26000, -22516, -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, 22516, + 13000, 0, -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, 0, + 12999, 22516, 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, -22516, + -26000, -22516, -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, 22516, + 13000, 0, -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516}, + { +// Carrier 2 Phase 3 + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, 18384, + 25114, 25114, 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, -25114, + -18384, -6729, 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, 6729, + -6729, -18384, -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, 18384, + 25114, 25114, 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, -25114, + -18384, -6729, 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, 6729, + -6729, -18384, -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114}, + }, + + {{// Carrier 3 Phase 0 + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282, + 0, 15282, 24727, 24727, 15282, 0, -15282, -24727, -24727, -15282}, + { +// Carrier 3 Phase 1 + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067, + 18384, 25679, 23166, 11803, -4067, -18384, -25679, -23166, -11803, 4067}, + { +// Carrier 3 Phase 2 + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034, + 26000, 21034, 8034, -8034, -21034, -26000, -21034, -8034, 8034, 21034}, + { +// Carrier 3 Phase 3 + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679, + 18384, 4067, -11803, -23166, -25679, -18384, -4067, 11803, 23166, 25679}, + }, + + {{//Carrier 4 Phase 0 + 0, 17397, 25857, 21034, 5405, -12999, -24727, -23752, -10575, 8034, + 22516, 25431, 15282, -2717, -19321, -26000, -19321, -2717, 15282, 25431, + 22516, 8034, -10575, -23752, -24727, -12999, 5405, 21034, 25857, 17397, + 0, -17397, -25857, -21034, -5405, 13000, 24727, 23752, 10575, -8034, + -22516, -25431, -15282, 2717, 19321, 26000, 19321, 2717, -15282, -25431, + -22516, -8034, 10575, 23752, 24727, 12999, -5405, -21034, -25857, -17397, + 0, 17397, 25857, 21034, 5405, -13000, -24727, -23752, -10575, 8034, + 22516, 25431, 15282, -2717, -19321, -26000, -19321, -2717, 15282, 25431, + 22516, 8034, -10575, -23752, -24727, -12999, 5405, 21034, 25857, 17397, + 0, -17397, -25857, -21034, -5405, 13000, 24727, 23752, 10575, -8034, + -22516, -25431, -15282, 2717, 19321, 26000, 19321, 2717, -15282, -25431, + -22516, -8034, 10575, 23752, 24727, 12999, -5405, -21034, -25857, -17397}, + { +// Carrier 4 Phase 1 + 18384, 25964, 20205, 4067, -14160, -25114, -23166, -9317, 9317, 23166, + 25114, 14160, -4067, -20205, -25964, -18384, -1360, 16362, 25679, 21805, + 6729, -11803, -24273, -24273, -11803, 6729, 21805, 25679, 16362, -1360, + -18384, -25964, -20205, -4067, 14160, 25114, 23166, 9317, -9317, -23166, + -25114, -14160, 4067, 20205, 25964, 18384, 1360, -16362, -25679, -21805, + -6729, 11803, 24273, 24273, 11803, -6729, -21805, -25679, -16362, 1360, + 18384, 25964, 20205, 4067, -14160, -25114, -23166, -9317, 9317, 23166, + 25114, 14160, -4067, -20205, -25964, -18384, -1360, 16362, 25679, 21805, + 6729, -11803, -24273, -24273, -11803, 6729, 21805, 25679, 16362, -1360, + -18384, -25964, -20205, -4067, 14160, 25114, 23166, 9317, -9317, -23166, + -25114, -14160, 4067, 20205, 25964, 18384, 1360, -16362, -25679, -21805, + -6729, 11803, 24273, 24273, 11803, -6729, -21805, -25679, -16362, 1360}, + { +// Carrier 4 Phase 2 + 26000, 19321, 2717, -15282, -25431, -22516, -8034, 10575, 23752, 24727, + 12999, -5405, -21034, -25857, -17397, 0, 17397, 25857, 21034, 5405, + -13000, -24727, -23752, -10575, 8034, 22516, 25431, 15282, -2717, -19321, + -26000, -19321, -2717, 15282, 25431, 22516, 8034, -10575, -23752, -24727, + -12999, 5405, 21034, 25857, 17397, 0, -17397, -25857, -21034, -5405, + 13000, 24727, 23752, 10575, -8034, -22516, -25431, -15282, 2717, 19321, + 26000, 19321, 2717, -15282, -25431, -22516, -8034, 10575, 23752, 24727, + 12999, -5405, -21034, -25857, -17397, 0, 17397, 25857, 21034, 5405, + -13000, -24727, -23752, -10575, 8034, 22516, 25431, 15282, -2717, -19321, + -26000, -19321, -2717, 15282, 25431, 22516, 8034, -10575, -23752, -24727, + -12999, 5405, 21034, 25857, 17397, 0, -17397, -25857, -21034, -5405, + 13000, 24727, 23752, 10575, -8034, -22516, -25431, -15282, 2717, 19321}, + { +// Carrier 4 Phase 3 + 18384, 1360, -16362, -25679, -21805, -6729, 11803, 24273, 24273, 11803, + -6729, -21805, -25679, -16362, 1360, 18384, 25964, 20205, 4067, -14160, + -25114, -23166, -9317, 9317, 23166, 25114, 14160, -4067, -20205, -25964, + -18384, -1360, 16362, 25679, 21805, 6729, -11803, -24273, -24273, -11803, + 6729, 21805, 25679, 16362, -1360, -18384, -25964, -20205, -4067, 14160, + 25114, 23166, 9317, -9317, -23166, -25114, -14160, 4067, 20205, 25964, + 18384, 1360, -16362, -25679, -21805, -6729, 11803, 24273, 24273, 11803, + -6729, -21805, -25679, -16362, 1360, 18384, 25964, 20205, 4067, -14160, + -25114, -23166, -9317, 9317, 23166, 25114, 14160, -4067, -20205, -25964, + -18384, -1360, 16362, 25679, 21805, 6729, -11803, -24273, -24273, -11803, + 6729, 21805, 25679, 16362, -1360, -18384, -25964, -20205, -4067, 14160, + 25114, 23166, 9317, -9317, -23166, -25114, -14160, 4067, 20205, 25964}, + }, + + {{// Carrier 5 Phase 0 + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, + 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, + -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, + 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, + -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, + 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, + -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384}, + { +// Carrier 5 Phase 1 + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, + -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, + -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, + -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, + -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, + -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, + -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0}, + { +// Carrier 5 Phase 2 + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, + 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, + -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, + 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, + -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, + 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, + -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384}, + { +// Carrier 5 Phase 3 + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, + -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, + -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, + -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, + -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, + -18384, -26000, -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, + -18384, 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, 26000}, + }, + + {{//Carrier 6 Phase 0 + 0, 19321, 25857, 15282, -5405, -22516, -24727, -10575, 10575, 24727, + 22516, 5405, -15282, -25857, -19321, 0, 19321, 25857, 15282, -5405, + -22516, -24727, -10575, 10575, 24727, 22516, 5405, -15282, -25857, -19321, + 0, 19321, 25857, 15282, -5405, -22516, -24727, -10575, 10575, 24727, + 22516, 5405, -15282, -25857, -19321, 0, 19321, 25857, 15282, -5405, + -22516, -24727, -10575, 10575, 24727, 22516, 5405, -15282, -25857, -19321, + 0, 19321, 25857, 15282, -5405, -22516, -24727, -10575, 10575, 24727, + 22516, 5405, -15282, -25857, -19321, 0, 19321, 25857, 15282, -5405, + -22516, -24727, -10575, 10575, 24727, 22516, 5405, -15282, -25857, -19321, + 0, 19321, 25857, 15282, -5405, -22516, -24727, -10575, 10575, 24727, + 22516, 5405, -15282, -25857, -19321, 0, 19321, 25857, 15282, -5405, + -22516, -24727, -10575, 10575, 24727, 22516, 5405, -15282, -25857, -19321}, + { +// Carrier 6 Phase 1 + 18384, 25964, 16362, -4067, -21805, -25114, -11803, 9317, 24273, 23166, + 6729, -14160, -25679, -20205, -1360, 18384, 25964, 16362, -4067, -21805, + -25114, -11803, 9317, 24273, 23166, 6729, -14160, -25679, -20205, -1360, + 18384, 25964, 16362, -4067, -21805, -25114, -11803, 9317, 24273, 23166, + 6729, -14160, -25679, -20205, -1360, 18384, 25964, 16362, -4067, -21805, + -25114, -11803, 9317, 24273, 23166, 6729, -14160, -25679, -20205, -1360, + 18384, 25964, 16362, -4067, -21805, -25114, -11803, 9317, 24273, 23166, + 6729, -14160, -25679, -20205, -1360, 18384, 25964, 16362, -4067, -21805, + -25114, -11803, 9317, 24273, 23166, 6729, -14160, -25679, -20205, -1360, + 18384, 25964, 16362, -4067, -21805, -25114, -11803, 9317, 24273, 23166, + 6729, -14160, -25679, -20205, -1360, 18384, 25964, 16362, -4067, -21805, + -25114, -11803, 9317, 24273, 23166, 6729, -14160, -25679, -20205, -1360}, + { +// Carrier 6 Phase 2 + 26000, 17397, -2717, -21034, -25431, -13000, 8034, 23752, 23752, 8034, + -13000, -25431, -21034, -2717, 17397, 26000, 17397, -2717, -21034, -25431, + -12999, 8034, 23752, 23752, 8034, -13000, -25431, -21034, -2717, 17397, + 26000, 17397, -2717, -21034, -25431, -12999, 8034, 23752, 23752, 8034, + -13000, -25431, -21034, -2717, 17397, 26000, 17397, -2717, -21034, -25431, + -12999, 8034, 23752, 23752, 8034, -13000, -25431, -21034, -2717, 17397, + 26000, 17397, -2717, -21034, -25431, -12999, 8034, 23752, 23752, 8034, + -13000, -25431, -21034, -2717, 17397, 26000, 17397, -2717, -21034, -25431, + -12999, 8034, 23752, 23752, 8034, -13000, -25431, -21034, -2717, 17397, + 26000, 17397, -2717, -21034, -25431, -12999, 8034, 23752, 23752, 8034, + -13000, -25431, -21034, -2717, 17397, 26000, 17397, -2717, -21034, -25431, + -12999, 8034, 23752, 23752, 8034, -13000, -25431, -21034, -2717, 17397}, + { +// Carrier 6 Phase 3 + 18384, -1360, -20205, -25679, -14160, 6729, 23166, 24273, 9317, -11803, + -25114, -21805, -4067, 16362, 25964, 18384, -1360, -20205, -25679, -14160, + 6729, 23166, 24273, 9317, -11803, -25114, -21805, -4067, 16362, 25964, + 18384, -1360, -20205, -25679, -14160, 6729, 23166, 24273, 9317, -11803, + -25114, -21805, -4067, 16362, 25964, 18384, -1360, -20205, -25679, -14160, + 6729, 23166, 24273, 9317, -11803, -25114, -21805, -4067, 16362, 25964, + 18384, -1360, -20205, -25679, -14160, 6729, 23166, 24273, 9317, -11803, + -25114, -21805, -4067, 16362, 25964, 18384, -1360, -20205, -25679, -14160, + 6729, 23166, 24273, 9317, -11803, -25114, -21805, -4067, 16362, 25964, + 18384, -1360, -20205, -25679, -14160, 6729, 23166, 24273, 9317, -11803, + -25114, -21805, -4067, 16362, 25964, 18384, -1360, -20205, -25679, -14160, + 6729, 23166, 24273, 9317, -11803, -25114, -21805, -4067, 16362, 25964}, + }, + + {{// Carrier 7 Phase 0 + 0, 21034, 24727, 8034, -15282, -26000, -15282, 8034, 24727, 21034, + 0, -21034, -24727, -8034, 15282, 26000, 15282, -8034, -24727, -21034, + 0, 21034, 24727, 8034, -15282, -26000, -15282, 8034, 24727, 21034, + 0, -21034, -24727, -8034, 15282, 26000, 15282, -8034, -24727, -21034, + 0, 21034, 24727, 8034, -15282, -26000, -15282, 8034, 24727, 21034, + 0, -21034, -24727, -8034, 15282, 26000, 15282, -8034, -24727, -21034, + 0, 21034, 24727, 8034, -15282, -26000, -15282, 8034, 24727, 21034, + 0, -21034, -24727, -8034, 15282, 26000, 15282, -8034, -24727, -21034, + 0, 21034, 24727, 8034, -15282, -26000, -15282, 8034, 24727, 21034, + 0, -21034, -24727, -8034, 15282, 26000, 15282, -8034, -24727, -21034, + 0, 21034, 24727, 8034, -15282, -26000, -15282, 8034, 24727, 21034, + 0, -21034, -24727, -8034, 15282, 26000, 15282, -8034, -24727, -21034}, + { +// Carrier 7 Phase 1 + 18384, 25679, 11803, -11803, -25679, -18384, 4067, 23166, 23166, 4067, + -18384, -25679, -11803, 11803, 25679, 18384, -4067, -23166, -23166, -4067, + 18384, 25679, 11803, -11803, -25679, -18384, 4067, 23166, 23166, 4067, + -18384, -25679, -11803, 11803, 25679, 18384, -4067, -23166, -23166, -4067, + 18384, 25679, 11803, -11803, -25679, -18384, 4067, 23166, 23166, 4067, + -18384, -25679, -11803, 11803, 25679, 18384, -4067, -23166, -23166, -4067, + 18384, 25679, 11803, -11803, -25679, -18384, 4067, 23166, 23166, 4067, + -18384, -25679, -11803, 11803, 25679, 18384, -4067, -23166, -23166, -4067, + 18384, 25679, 11803, -11803, -25679, -18384, 4067, 23166, 23166, 4067, + -18384, -25679, -11803, 11803, 25679, 18384, -4067, -23166, -23166, -4067, + 18384, 25679, 11803, -11803, -25679, -18384, 4067, 23166, 23166, 4067, + -18384, -25679, -11803, 11803, 25679, 18384, -4067, -23166, -23166, -4067}, + { +// Carrier 7 Phase 2 + 26000, 15282, -8034, -24727, -21034, 0, 21034, 24727, 8034, -15282, + -26000, -15282, 8034, 24727, 21034, 0, -21034, -24727, -8034, 15282, + 26000, 15282, -8034, -24727, -21034, 0, 21034, 24727, 8034, -15282, + -26000, -15282, 8034, 24727, 21034, 0, -21034, -24727, -8034, 15282, + 26000, 15282, -8034, -24727, -21034, 0, 21034, 24727, 8034, -15282, + -26000, -15282, 8034, 24727, 21034, 0, -21034, -24727, -8034, 15282, + 26000, 15282, -8034, -24727, -21034, 0, 21034, 24727, 8034, -15282, + -26000, -15282, 8034, 24727, 21034, 0, -21034, -24727, -8034, 15282, + 26000, 15282, -8034, -24727, -21034, 0, 21034, 24727, 8034, -15282, + -26000, -15282, 8034, 24727, 21034, 0, -21034, -24727, -8034, 15282, + 26000, 15282, -8034, -24727, -21034, 0, 21034, 24727, 8034, -15282, + -26000, -15282, 8034, 24727, 21034, 0, -21034, -24727, -8034, 15282}, + { +// Carrier 7 Phase 3 + 18384, -4067, -23166, -23166, -4067, 18384, 25679, 11803, -11803, -25679, + -18384, 4067, 23166, 23166, 4067, -18384, -25679, -11803, 11803, 25679, + 18384, -4067, -23166, -23166, -4067, 18384, 25679, 11803, -11803, -25679, + -18384, 4067, 23166, 23166, 4067, -18384, -25679, -11803, 11803, 25679, + 18384, -4067, -23166, -23166, -4067, 18384, 25679, 11803, -11803, -25679, + -18384, 4067, 23166, 23166, 4067, -18384, -25679, -11803, 11803, 25679, + 18384, -4067, -23166, -23166, -4067, 18384, 25679, 11803, -11803, -25679, + -18384, 4067, 23166, 23166, 4067, -18384, -25679, -11803, 11803, 25679, + 18384, -4067, -23166, -23166, -4067, 18384, 25679, 11803, -11803, -25679, + -18384, 4067, 23166, 23166, 4067, -18384, -25679, -11803, 11803, 25679, + 18384, -4067, -23166, -23166, -4067, 18384, 25679, 11803, -11803, -25679, + -18384, 4067, 23166, 23166, 4067, -18384, -25679, -11803, 11803, 25679}, + }, + + {{// Carrier 8 Phase 0 + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, -22516, -22516}, + { +// Carrier 8 Phase 1 + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729}, + { +// Carrier 8 Phase 2 + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999}, + { +// Carrier 8 Phase 3 + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114}, + }, + + {{// Carrier 9 Phase 0 + 0, 23752, 19321, -8034, -25857, -13000, 15282, 25431, 5405, -21034, + -22516, 2717, 24727, 17397, -10575, -26000, -10575, 17397, 24727, 2717, + -22516, -21034, 5405, 25431, 15282, -13000, -25857, -8034, 19321, 23752, + 0, -23752, -19321, 8034, 25857, 12999, -15282, -25431, -5405, 21034, + 22516, -2717, -24727, -17397, 10575, 26000, 10575, -17397, -24727, -2717, + 22516, 21034, -5405, -25431, -15282, 13000, 25857, 8034, -19321, -23752, + 0, 23752, 19321, -8034, -25857, -12999, 15282, 25431, 5405, -21034, + -22516, 2717, 24727, 17397, -10575, -26000, -10575, 17397, 24727, 2717, + -22516, -21034, 5405, 25431, 15282, -13000, -25857, -8034, 19321, 23752, + 0, -23752, -19321, 8034, 25857, 12999, -15282, -25431, -5405, 21034, + 22516, -2717, -24727, -17397, 10575, 26000, 10575, -17397, -24727, -2717, + 22516, 21034, -5405, -25431, -15282, 13000, 25857, 8034, -19321, -23752}, + { +// Carrier 9 Phase 1 + 18384, 24273, 1360, -23166, -20205, 6729, 25679, 14160, -14160, -25679, + -6729, 20205, 23166, -1360, -24273, -18384, 9317, 25964, 11803, -16362, + -25114, -4067, 21805, 21805, -4067, -25114, -16362, 11803, 25964, 9317, + -18384, -24273, -1360, 23166, 20205, -6729, -25679, -14160, 14160, 25679, + 6729, -20205, -23166, 1360, 24273, 18384, -9317, -25964, -11803, 16362, + 25114, 4067, -21805, -21805, 4067, 25114, 16362, -11803, -25964, -9317, + 18384, 24273, 1360, -23166, -20205, 6729, 25679, 14160, -14160, -25679, + -6729, 20205, 23166, -1360, -24273, -18384, 9317, 25964, 11803, -16362, + -25114, -4067, 21805, 21805, -4067, -25114, -16362, 11803, 25964, 9317, + -18384, -24273, -1360, 23166, 20205, -6729, -25679, -14160, 14160, 25679, + 6729, -20205, -23166, 1360, 24273, 18384, -9317, -25964, -11803, 16362, + 25114, 4067, -21805, -21805, 4067, 25114, 16362, -11803, -25964, -9317}, + { +// Carrier 9 Phase 2 + 26000, 10575, -17397, -24727, -2717, 22516, 21034, -5405, -25431, -15282, + 12999, 25857, 8034, -19321, -23752, 0, 23752, 19321, -8034, -25857, + -13000, 15282, 25431, 5405, -21034, -22516, 2717, 24727, 17397, -10575, + -26000, -10575, 17397, 24727, 2717, -22516, -21034, 5405, 25431, 15282, + -13000, -25857, -8034, 19321, 23752, 0, -23752, -19321, 8034, 25857, + 12999, -15282, -25431, -5405, 21034, 22516, -2717, -24727, -17397, 10575, + 26000, 10575, -17397, -24727, -2717, 22516, 21034, -5405, -25431, -15282, + 13000, 25857, 8034, -19321, -23752, 0, 23752, 19321, -8034, -25857, + -12999, 15282, 25431, 5405, -21034, -22516, 2717, 24727, 17397, -10575, + -26000, -10575, 17397, 24727, 2717, -22516, -21034, 5405, 25431, 15282, + -13000, -25857, -8034, 19321, 23752, 0, -23752, -19321, 8034, 25857, + 12999, -15282, -25431, -5405, 21034, 22516, -2717, -24727, -17397, 10575}, + { +// Carrier 9 Phase 3 + 18384, -9317, -25964, -11803, 16362, 25114, 4067, -21805, -21805, 4067, + 25114, 16362, -11803, -25964, -9317, 18384, 24273, 1360, -23166, -20205, + 6729, 25679, 14160, -14160, -25679, -6729, 20205, 23166, -1360, -24273, + -18384, 9317, 25964, 11803, -16362, -25114, -4067, 21805, 21805, -4067, + -25114, -16362, 11803, 25964, 9317, -18384, -24273, -1360, 23166, 20205, + -6729, -25679, -14160, 14160, 25679, 6729, -20205, -23166, 1360, 24273, + 18384, -9317, -25964, -11803, 16362, 25114, 4067, -21805, -21805, 4067, + 25114, 16362, -11803, -25964, -9317, 18384, 24273, 1360, -23166, -20205, + 6729, 25679, 14160, -14160, -25679, -6729, 20205, 23166, -1360, -24273, + -18384, 9317, 25964, 11803, -16362, -25114, -4067, 21805, 21805, -4067, + -25114, -16362, 11803, 25964, 9317, -18384, -24273, -1360, 23166, 20205, + -6729, -25679, -14160, 14160, 25679, 6729, -20205, -23166, 1360, 24273}, + }, + + {{// Carrier 10 Phase 0 + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727, + 0, 24727, 15282, -15282, -24727, 0, 24727, 15282, -15282, -24727}, + { +// Carrier 10 Phase 1 + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803, + 18384, 23166, -4067, -25679, -11803, 18384, 23166, -4067, -25679, -11803}, + { +// Carrier 10 Phase 2 + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034, + 26000, 8034, -21034, -21034, 8034, 26000, 8034, -21034, -21034, 8034}, + { +// Carrier 10 Phase 3 + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11803, -25679, -4067, 23166, + 18384, -11803, -25679, -4067, 23166, 18384, -11804, -25679, -4067, 23166} + }}; + + +CONST short intOFDMTemplate[MAXCAR][8][216] = +{{{ + +// Carrier 0 Phase 0 + 0, 4514, 8892, 12999, 16712, 19917, 22516, 24432, + 25605, 26000, 25605, 24432, 22516, 19917, 16712, 13000, + 8892, 4514, 0, -4514, -8892, -12999, -16712, -19917, + -22516, -24432, -25604, -26000, -25605, -24432, -22516, -19917, + -16712, -13000, -8892, -4514, 0, 4514, 8892, 12999, + 16712, 19917, 22516, 24432, 25604, 26000, 25605, 24432, + 22516, 19917, 16712, 13000, 8892, 4514, 0, -4514, + -8892, -12999, -16712, -19917, -22516, -24431, -25604, -26000, + -25605, -24432, -22516, -19917, -16712, -13000, -8892, -4514, + 0, 4514, 8892, 12999, 16712, 19917, 22516, 24431, + 25604, 26000, 25605, 24432, 22516, 19917, 16712, 13000, + 8892, 4514, 0, -4514, -8892, -12999, -16712, -19917, + -22516, -24431, -25604, -26000, -25605, -24432, -22516, -19917, + -16712, -13000, -8892, -4514, 0, 4514, 8892, 12999, + 16712, 19917, 22516, 24431, 25604, 26000, 25605, 24432, + 22516, 19917, 16712, 13000, 8892, 4514, 0, -4514, + -8892, -12999, -16712, -19917, -22516, -24431, -25604, -26000, + -25605, -24432, -22516, -19917, -16712, -13000, -8892, -4514, + 0, 4514, 8892, 12999, 16712, 19917, 22516, 24431, + 25604, 26000, 25605, 24432, 22516, 19917, 16712, 13000, + 8892, 4514, 0, -4514, -8892, -12999, -16712, -19917, + -22516, -24431, -25604, -26000, -25605, -24432, -22516, -19917, + -16712, -13000, -8892, -4514, 0, 4514, 8892, 12999, + 16712, 19917, 22516, 24431, 25604, 26000, 25605, 24432, + 22516, 19917, 16712, 13000, 8892, 4514, 0, -4514, + -8892, -12999, -16712, -19917, -22516, -24431, -25604, -26000, + -25605, -24432, -22516, -19917, -16712, -13000, -8892, -4514}, + { +// Carrier 0 Phase 1 + 9949, 13969, 17565, 20627, 23062, 24796, 25777, 25975, + 25383, 24020, 21928, 19169, 15827, 12005, 7818, 3393, + -1134, -5627, -9949, -13969, -17565, -20627, -23062, -24796, + -25777, -25975, -25383, -24020, -21928, -19169, -15827, -12005, + -7818, -3393, 1134, 5627, 9949, 13969, 17565, 20627, + 23062, 24796, 25777, 25975, 25383, 24020, 21928, 19169, + 15827, 12005, 7818, 3393, -1134, -5627, -9949, -13969, + -17565, -20627, -23062, -24796, -25777, -25975, -25383, -24020, + -21928, -19169, -15827, -12005, -7818, -3393, 1134, 5627, + 9949, 13969, 17565, 20627, 23062, 24796, 25777, 25975, + 25383, 24020, 21928, 19169, 15827, 12005, 7818, 3393, + -1134, -5627, -9949, -13969, -17565, -20627, -23062, -24796, + -25777, -25975, -25383, -24020, -21928, -19169, -15827, -12005, + -7818, -3393, 1134, 5627, 9949, 13969, 17565, 20627, + 23062, 24796, 25777, 25975, 25383, 24020, 21928, 19169, + 15827, 12005, 7818, 3393, -1134, -5627, -9949, -13969, + -17565, -20627, -23062, -24796, -25777, -25975, -25383, -24020, + -21928, -19169, -15827, -12005, -7818, -3393, 1134, 5627, + 9949, 13969, 17565, 20627, 23062, 24796, 25777, 25975, + 25383, 24020, 21928, 19169, 15827, 12005, 7818, 3393, + -1134, -5627, -9949, -13969, -17565, -20627, -23062, -24796, + -25777, -25975, -25383, -24020, -21928, -19169, -15827, -12005, + -7818, -3393, 1134, 5627, 9949, 13969, 17565, 20627, + 23062, 24796, 25777, 25975, 25383, 24020, 21928, 19169, + 15827, 12005, 7818, 3393, -1134, -5627, -9949, -13969, + -17565, -20627, -23062, -24796, -25777, -25975, -25383, -24020, + -21928, -19169, -15827, -12005, -7818, -3393, 1134, 5627}, + { +// Carrier 0 Phase 2 + 18384, 21297, 23564, 25114, 25901, 25901, 25114, 23564, + 21297, 18384, 14912, 10988, 6729, 2266, -2266, -6729, + -10988, -14912, -18384, -21297, -23563, -25114, -25901, -25901, + -25114, -23564, -21297, -18384, -14913, -10988, -6729, -2266, + 2266, 6729, 10988, 14912, 18384, 21297, 23563, 25114, + 25901, 25901, 25114, 23564, 21297, 18384, 14913, 10988, + 6729, 2266, -2266, -6729, -10988, -14912, -18384, -21297, + -23563, -25114, -25901, -25901, -25114, -23564, -21297, -18384, + -14913, -10988, -6729, -2266, 2266, 6729, 10988, 14912, + 18384, 21297, 23563, 25114, 25901, 25901, 25114, 23564, + 21297, 18384, 14913, 10988, 6729, 2266, -2265, -6729, + -10988, -14912, -18384, -21297, -23563, -25114, -25901, -25901, + -25114, -23564, -21297, -18384, -14913, -10988, -6729, -2266, + 2265, 6729, 10988, 14912, 18384, 21297, 23563, 25114, + 25901, 25901, 25114, 23564, 21297, 18384, 14913, 10988, + 6729, 2266, -2265, -6729, -10988, -14912, -18384, -21297, + -23563, -25114, -25901, -25901, -25114, -23564, -21298, -18384, + -14913, -10988, -6729, -2266, 2265, 6729, 10987, 14912, + 18384, 21297, 23563, 25114, 25901, 25901, 25114, 23564, + 21298, 18384, 14913, 10988, 6729, 2266, -2265, -6729, + -10987, -14912, -18384, -21297, -23563, -25114, -25901, -25901, + -25114, -23564, -21298, -18384, -14913, -10988, -6729, -2266, + 2265, 6729, 10987, 14912, 18384, 21297, 23563, 25114, + 25901, 25901, 25114, 23564, 21298, 18384, 14913, 10988, + 6729, 2266, -2265, -6729, -10987, -14912, -18384, -21297, + -23563, -25114, -25901, -25901, -25114, -23564, -21298, -18384, + -14913, -10988, -6729, -2266, 2265, 6729, 10987, 14912}, + { +// Carrier 0 Phase 3 + 24020, 25383, 25975, 25777, 24796, 23062, 20627, 17565, + 13969, 9949, 5627, 1134, -3393, -7818, -12005, -15827, + -19169, -21928, -24020, -25383, -25975, -25777, -24796, -23062, + -20627, -17565, -13969, -9949, -5627, -1134, 3393, 7818, + 12005, 15827, 19169, 21928, 24020, 25383, 25975, 25777, + 24796, 23062, 20627, 17565, 13969, 9949, 5627, 1134, + -3393, -7818, -12005, -15827, -19169, -21928, -24020, -25383, + -25975, -25777, -24796, -23062, -20627, -17565, -13969, -9949, + -5627, -1134, 3393, 7818, 12005, 15827, 19169, 21928, + 24020, 25383, 25975, 25777, 24796, 23062, 20627, 17565, + 13969, 9949, 5627, 1134, -3393, -7818, -12005, -15827, + -19169, -21928, -24020, -25383, -25975, -25777, -24796, -23062, + -20627, -17565, -13969, -9949, -5627, -1134, 3393, 7818, + 12005, 15827, 19169, 21928, 24020, 25383, 25975, 25777, + 24796, 23062, 20627, 17565, 13969, 9949, 5627, 1134, + -3393, -7818, -12005, -15827, -19169, -21928, -24020, -25383, + -25975, -25777, -24796, -23062, -20627, -17565, -13969, -9949, + -5627, -1134, 3393, 7818, 12005, 15827, 19169, 21928, + 24020, 25383, 25975, 25777, 24796, 23062, 20627, 17565, + 13969, 9949, 5627, 1134, -3393, -7818, -12005, -15827, + -19169, -21928, -24020, -25383, -25975, -25777, -24796, -23062, + -20627, -17565, -13969, -9949, -5627, -1134, 3393, 7818, + 12005, 15827, 19169, 21928, 24020, 25383, 25975, 25777, + 24796, 23062, 20627, 17565, 13969, 9949, 5627, 1134, + -3393, -7818, -12005, -15827, -19169, -21928, -24020, -25383, + -25975, -25777, -24796, -23062, -20627, -17565, -13969, -9949, + -5627, -1134, 3393, 7818, 12005, 15827, 19169, 21928}, + { +// Carrier 0 Phase 4 + 26000, 25605, 24432, 22516, 19917, 16712, 13000, 8892, + 4514, 0, -4514, -8892, -12999, -16712, -19917, -22516, + -24432, -25604, -26000, -25605, -24432, -22516, -19917, -16712, + -13000, -8892, -4514, 0, 4514, 8892, 12999, 16712, + 19917, 22516, 24432, 25604, 26000, 25605, 24432, 22516, + 19917, 16712, 13000, 8892, 4514, 0, -4514, -8892, + -12999, -16712, -19917, -22516, -24431, -25604, -26000, -25605, + -24432, -22516, -19917, -16712, -13000, -8892, -4514, 0, + 4514, 8892, 12999, 16712, 19917, 22516, 24431, 25604, + 26000, 25605, 24432, 22516, 19917, 16712, 13000, 8892, + 4514, 0, -4514, -8892, -12999, -16712, -19917, -22516, + -24431, -25604, -26000, -25605, -24432, -22516, -19917, -16712, + -13000, -8892, -4514, 0, 4514, 8892, 12999, 16712, + 19917, 22516, 24431, 25604, 26000, 25605, 24432, 22516, + 19917, 16712, 13000, 8892, 4514, 0, -4514, -8892, + -12999, -16712, -19917, -22516, -24431, -25604, -26000, -25605, + -24432, -22516, -19917, -16712, -13000, -8892, -4514, 0, + 4514, 8892, 12999, 16712, 19917, 22516, 24431, 25604, + 26000, 25605, 24432, 22516, 19917, 16712, 13000, 8892, + 4514, 0, -4514, -8892, -12999, -16712, -19917, -22516, + -24431, -25604, -26000, -25605, -24432, -22516, -19917, -16712, + -13000, -8892, -4514, 0, 4514, 8892, 12999, 16712, + 19917, 22516, 24431, 25604, 26000, 25605, 24432, 22516, + 19917, 16712, 13000, 8892, 4514, 0, -4514, -8892, + -12999, -16712, -19917, -22516, -24431, -25604, -26000, -25605, + -24432, -22516, -19917, -16712, -13000, -8892, -4514, 0, + 4514, 8892, 12999, 16712, 19917, 22516, 24431, 25604}, + { +// Carrier 0 Phase 5 + 24020, 21928, 19169, 15827, 12005, 7818, 3393, -1134, + -5627, -9949, -13969, -17565, -20627, -23062, -24796, -25777, + -25975, -25383, -24020, -21928, -19169, -15827, -12005, -7818, + -3393, 1134, 5627, 9949, 13969, 17565, 20627, 23062, + 24796, 25777, 25975, 25383, 24020, 21928, 19169, 15827, + 12005, 7818, 3393, -1134, -5627, -9949, -13969, -17565, + -20627, -23062, -24796, -25777, -25975, -25383, -24020, -21928, + -19169, -15827, -12005, -7818, -3393, 1134, 5627, 9949, + 13969, 17565, 20627, 23062, 24796, 25777, 25975, 25383, + 24020, 21928, 19169, 15827, 12005, 7818, 3393, -1134, + -5627, -9949, -13969, -17565, -20627, -23062, -24796, -25777, + -25975, -25383, -24020, -21928, -19169, -15827, -12005, -7818, + -3393, 1134, 5627, 9949, 13969, 17565, 20627, 23062, + 24796, 25777, 25975, 25383, 24020, 21928, 19169, 15827, + 12005, 7818, 3393, -1134, -5627, -9949, -13969, -17565, + -20627, -23062, -24796, -25777, -25975, -25383, -24020, -21928, + -19169, -15827, -12005, -7818, -3393, 1134, 5627, 9949, + 13969, 17565, 20627, 23062, 24796, 25777, 25975, 25383, + 24020, 21928, 19169, 15827, 12005, 7818, 3393, -1134, + -5627, -9949, -13969, -17565, -20627, -23062, -24796, -25777, + -25975, -25383, -24020, -21928, -19169, -15827, -12005, -7818, + -3393, 1134, 5627, 9949, 13969, 17565, 20627, 23062, + 24796, 25777, 25975, 25383, 24020, 21928, 19169, 15827, + 12005, 7818, 3393, -1134, -5627, -9949, -13969, -17565, + -20627, -23062, -24796, -25777, -25975, -25383, -24020, -21928, + -19169, -15827, -12005, -7818, -3393, 1134, 5627, 9949, + 13969, 17565, 20627, 23062, 24796, 25777, 25975, 25383}, + { +// Carrier 0 Phase 6 + 18384, 14912, 10988, 6729, 2266, -2266, -6729, -10988, + -14912, -18384, -21297, -23563, -25114, -25901, -25901, -25114, + -23564, -21297, -18384, -14913, -10988, -6729, -2266, 2266, + 6729, 10988, 14912, 18384, 21297, 23563, 25114, 25901, + 25901, 25114, 23564, 21297, 18384, 14913, 10988, 6729, + 2266, -2266, -6729, -10988, -14912, -18384, -21297, -23563, + -25114, -25901, -25901, -25114, -23564, -21297, -18384, -14913, + -10988, -6729, -2266, 2266, 6729, 10988, 14912, 18384, + 21297, 23563, 25114, 25901, 25901, 25114, 23564, 21297, + 18384, 14913, 10988, 6729, 2266, -2265, -6729, -10988, + -14912, -18384, -21297, -23563, -25114, -25901, -25901, -25114, + -23564, -21297, -18384, -14913, -10988, -6729, -2266, 2265, + 6729, 10988, 14912, 18384, 21297, 23563, 25114, 25901, + 25901, 25114, 23564, 21297, 18384, 14913, 10988, 6729, + 2266, -2265, -6729, -10988, -14912, -18384, -21297, -23563, + -25114, -25901, -25901, -25114, -23564, -21298, -18384, -14913, + -10988, -6729, -2266, 2265, 6729, 10987, 14912, 18384, + 21297, 23563, 25114, 25901, 25901, 25114, 23564, 21298, + 18384, 14913, 10988, 6729, 2266, -2265, -6729, -10987, + -14912, -18384, -21297, -23563, -25114, -25901, -25901, -25114, + -23564, -21298, -18384, -14913, -10988, -6729, -2266, 2265, + 6729, 10987, 14912, 18384, 21297, 23563, 25114, 25901, + 25901, 25114, 23564, 21298, 18384, 14913, 10988, 6729, + 2266, -2265, -6729, -10987, -14912, -18384, -21297, -23563, + -25114, -25901, -25901, -25114, -23564, -21298, -18384, -14913, + -10988, -6729, -2266, 2265, 6729, 10987, 14912, 18384, + 21297, 23563, 25114, 25901, 25901, 25114, 23564, 21298}, + { +// Carrier 0 Phase 7 + 9949, 5627, 1134, -3393, -7818, -12005, -15827, -19169, + -21928, -24020, -25383, -25975, -25777, -24796, -23062, -20627, + -17565, -13969, -9949, -5627, -1134, 3393, 7818, 12005, + 15827, 19169, 21928, 24020, 25383, 25975, 25777, 24796, + 23062, 20627, 17565, 13969, 9949, 5627, 1134, -3393, + -7818, -12005, -15827, -19169, -21928, -24020, -25383, -25975, + -25777, -24796, -23062, -20627, -17565, -13969, -9949, -5627, + -1134, 3393, 7818, 12005, 15827, 19169, 21928, 24020, + 25383, 25975, 25777, 24796, 23062, 20627, 17565, 13969, + 9949, 5627, 1134, -3393, -7818, -12005, -15827, -19169, + -21928, -24020, -25383, -25975, -25777, -24796, -23062, -20627, + -17565, -13969, -9949, -5627, -1134, 3393, 7818, 12005, + 15827, 19169, 21928, 24020, 25383, 25975, 25777, 24796, + 23062, 20627, 17565, 13969, 9949, 5627, 1134, -3393, + -7818, -12005, -15827, -19169, -21928, -24020, -25383, -25975, + -25777, -24796, -23062, -20627, -17565, -13969, -9949, -5627, + -1134, 3393, 7818, 12005, 15827, 19169, 21928, 24020, + 25383, 25975, 25777, 24796, 23062, 20627, 17565, 13969, + 9949, 5627, 1134, -3393, -7818, -12005, -15827, -19169, + -21928, -24020, -25383, -25975, -25777, -24796, -23062, -20627, + -17565, -13969, -9949, -5627, -1134, 3393, 7818, 12005, + 15827, 19169, 21928, 24020, 25383, 25975, 25777, 24796, + 23062, 20627, 17565, 13969, 9949, 5627, 1134, -3393, + -7818, -12005, -15827, -19169, -21928, -24020, -25383, -25975, + -25777, -24796, -23062, -20627, -17565, -13969, -9949, -5627, + -1134, 3393, 7818, 12005, 15827, 19169, 21928, 24020, + 25383, 25975, 25777, 24796, 23062, 20627, 17565, 13969}, + },{{ + +// Carrier 1 Phase 0 + 0, 5257, 10298, 14912, 18911, 22129, 24432, 25725, + 25956, 25114, 23234, 20394, 16712, 12339, 7456, 2266, + -3018, -8178, -12999, -17284, -20855, -23563, -25299, -25989, + -25605, -24163, -21722, -18384, -14287, -9599, -4514, 756, + 5996, 10988, 15526, 19422, 22516, 24680, 25824, 25901, + 24907, 22885, 19917, 16126, 11668, 6729, 1511, -3768, + -8892, -13649, -17842, -21297, -23873, -25462, -26000, -25462, + -23873, -21297, -17842, -13649, -8892, -3768, 1511, 6729, + 11668, 16126, 19917, 22885, 24907, 25901, 25824, 24680, + 22516, 19422, 15526, 10988, 5996, 756, -4514, -9599, + -14287, -18384, -21722, -24163, -25604, -25989, -25299, -23564, + -20855, -17284, -13000, -8178, -3018, 2266, 7456, 12339, + 16712, 20394, 23234, 25114, 25956, 25725, 24432, 22129, + 18911, 14912, 10298, 5257, 0, -5257, -10298, -14912, + -18911, -22129, -24432, -25725, -25956, -25114, -23234, -20394, + -16712, -12339, -7456, -2266, 3018, 8178, 13000, 17284, + 20855, 23564, 25299, 25989, 25604, 24163, 21722, 18384, + 14287, 9599, 4514, -756, -5996, -10988, -15526, -19422, + -22516, -24680, -25824, -25901, -24907, -22885, -19917, -16126, + -11668, -6729, -1511, 3768, 8892, 13649, 17842, 21297, + 23873, 25462, 26000, 25462, 23873, 21297, 17842, 13649, + 8892, 3768, -1511, -6729, -11668, -16126, -19917, -22885, + -24907, -25901, -25824, -24680, -22516, -19422, -15526, -10988, + -5995, -756, 4514, 9599, 14287, 18384, 21722, 24163, + 25605, 25989, 25299, 23563, 20855, 17284, 12999, 8178, + 3018, -2266, -7456, -12339, -16712, -20394, -23234, -25114, + -25956, -25725, -24432, -22129, -18911, -14912, -10298, -5257}, + { +// Carrier 1 Phase 1 + 9949, 14601, 18650, 21928, 24300, 25667, 25975, 25209, + 23401, 20627, 17000, 12671, 7818, 2642, -2642, -7818, + -12671, -17000, -20627, -23401, -25209, -25975, -25667, -24300, + -21928, -18650, -14601, -9949, -4886, 378, 5627, 10644, + 15221, 19169, 22325, 24558, 25777, 25931, 25013, 23062, + 20158, 16421, 12005, 7093, 1889, -3393, -8536, -13326, + -17565, -21078, -23721, -25383, -25997, -25536, -24020, -21512, + -18115, -13969, -9246, -4141, 1134, 6363, 11329, 15827, + 19671, 22703, 24796, 25865, 25865, 24796, 22703, 19671, + 15827, 11329, 6363, 1134, -4141, -9246, -13969, -18115, + -21512, -24020, -25536, -25997, -25383, -23721, -21078, -17565, + -13326, -8536, -3393, 1889, 7093, 12005, 16421, 20158, + 23062, 25013, 25931, 25777, 24558, 22325, 19169, 15221, + 10644, 5627, 378, -4886, -9949, -14601, -18650, -21928, + -24300, -25667, -25975, -25209, -23401, -20627, -17000, -12671, + -7818, -2642, 2642, 7818, 12671, 17000, 20627, 23401, + 25209, 25975, 25667, 24300, 21928, 18650, 14601, 9949, + 4886, -378, -5627, -10644, -15221, -19169, -22325, -24558, + -25777, -25931, -25013, -23062, -20158, -16421, -12005, -7093, + -1889, 3393, 8536, 13326, 17565, 21078, 23721, 25383, + 25997, 25536, 24020, 21512, 18115, 13969, 9246, 4141, + -1134, -6363, -11329, -15827, -19671, -22703, -24796, -25865, + -25865, -24796, -22703, -19671, -15827, -11329, -6363, -1134, + 4142, 9246, 13969, 18115, 21512, 24020, 25536, 25997, + 25383, 23721, 21078, 17565, 13326, 8536, 3393, -1889, + -7093, -12005, -16421, -20158, -23062, -25013, -25931, -25777, + -24558, -22325, -19169, -15221, -10644, -5627, -378, 4886}, + { +// Carrier 1 Phase 2 + 18384, 21722, 24163, 25605, 25989, 25299, 23564, 20855, + 17284, 13000, 8178, 3018, -2266, -7456, -12339, -16712, + -20394, -23234, -25114, -25956, -25725, -24432, -22129, -18911, + -14912, -10298, -5257, 0, 5257, 10298, 14912, 18911, + 22129, 24432, 25725, 25956, 25114, 23234, 20394, 16712, + 12339, 7456, 2266, -3018, -8178, -12999, -17284, -20855, + -23563, -25299, -25989, -25605, -24163, -21722, -18384, -14287, + -9599, -4514, 756, 5996, 10988, 15526, 19422, 22516, + 24680, 25824, 25901, 24907, 22885, 19917, 16126, 11668, + 6729, 1511, -3768, -8892, -13649, -17842, -21297, -23873, + -25462, -26000, -25462, -23873, -21297, -17842, -13649, -8892, + -3768, 1511, 6729, 11668, 16126, 19917, 22885, 24907, + 25901, 25824, 24680, 22516, 19422, 15526, 10988, 5996, + 756, -4514, -9599, -14287, -18384, -21722, -24163, -25604, + -25989, -25299, -23564, -20855, -17284, -12999, -8178, -3018, + 2266, 7456, 12339, 16712, 20394, 23234, 25114, 25956, + 25725, 24432, 22129, 18911, 14912, 10298, 5257, 0, + -5257, -10298, -14912, -18911, -22129, -24432, -25725, -25956, + -25114, -23234, -20394, -16712, -12339, -7456, -2266, 3018, + 8178, 13000, 17284, 20855, 23564, 25299, 25989, 25604, + 24163, 21722, 18384, 14287, 9599, 4514, -756, -5996, + -10988, -15526, -19422, -22516, -24680, -25824, -25901, -24907, + -22885, -19917, -16126, -11668, -6729, -1511, 3768, 8892, + 13649, 17842, 21297, 23873, 25462, 26000, 25462, 23873, + 21297, 17842, 13649, 8892, 3768, -1511, -6729, -11668, + -16126, -19917, -22885, -24907, -25901, -25824, -24680, -22516, + -19422, -15526, -10988, -5995, -756, 4514, 9599, 14287}, + { +// Carrier 1 Phase 3 + 24020, 25536, 25997, 25383, 23721, 21078, 17565, 13326, + 8536, 3393, -1889, -7093, -12005, -16421, -20158, -23062, + -25013, -25931, -25777, -24558, -22325, -19169, -15221, -10644, + -5627, -378, 4886, 9949, 14601, 18650, 21928, 24300, + 25667, 25975, 25209, 23401, 20627, 17000, 12671, 7818, + 2642, -2642, -7818, -12671, -17000, -20627, -23401, -25209, + -25975, -25667, -24300, -21928, -18650, -14601, -9949, -4886, + 378, 5627, 10644, 15221, 19169, 22325, 24558, 25777, + 25931, 25013, 23062, 20158, 16421, 12005, 7093, 1889, + -3393, -8536, -13326, -17565, -21078, -23721, -25383, -25997, + -25536, -24020, -21512, -18115, -13969, -9246, -4141, 1134, + 6363, 11329, 15827, 19672, 22703, 24796, 25865, 25865, + 24796, 22703, 19671, 15827, 11329, 6363, 1134, -4141, + -9246, -13969, -18115, -21512, -24020, -25536, -25997, -25383, + -23721, -21078, -17565, -13326, -8536, -3393, 1889, 7093, + 12005, 16421, 20158, 23062, 25013, 25931, 25777, 24558, + 22325, 19169, 15221, 10644, 5627, 378, -4886, -9949, + -14601, -18650, -21928, -24300, -25667, -25975, -25209, -23401, + -20627, -17000, -12671, -7818, -2642, 2642, 7818, 12671, + 17000, 20627, 23401, 25209, 25975, 25667, 24300, 21928, + 18650, 14601, 9949, 4886, -378, -5627, -10644, -15221, + -19169, -22325, -24558, -25777, -25931, -25013, -23062, -20158, + -16421, -12005, -7093, -1889, 3393, 8536, 13326, 17565, + 21078, 23721, 25383, 25997, 25536, 24020, 21512, 18115, + 13969, 9246, 4141, -1134, -6363, -11329, -15827, -19671, + -22703, -24796, -25865, -25865, -24796, -22703, -19671, -15827, + -11329, -6363, -1134, 4142, 9246, 13969, 18115, 21512}, + { +// Carrier 1 Phase 4 + 26000, 25462, 23873, 21297, 17842, 13649, 8892, 3768, + -1511, -6729, -11668, -16126, -19917, -22885, -24907, -25901, + -25824, -24680, -22516, -19422, -15526, -10988, -5996, -756, + 4514, 9599, 14287, 18384, 21722, 24163, 25605, 25989, + 25299, 23563, 20855, 17284, 13000, 8178, 3018, -2266, + -7456, -12339, -16712, -20394, -23234, -25114, -25956, -25725, + -24432, -22129, -18911, -14912, -10298, -5257, 0, 5257, + 10298, 14913, 18911, 22129, 24432, 25725, 25956, 25114, + 23234, 20394, 16712, 12339, 7456, 2266, -3018, -8178, + -12999, -17284, -20855, -23563, -25299, -25989, -25605, -24163, + -21722, -18384, -14287, -9599, -4514, 756, 5996, 10988, + 15526, 19422, 22516, 24680, 25824, 25901, 24907, 22885, + 19917, 16126, 11668, 6729, 1511, -3768, -8892, -13649, + -17842, -21297, -23873, -25462, -26000, -25462, -23873, -21297, + -17842, -13649, -8892, -3768, 1511, 6729, 11668, 16126, + 19917, 22885, 24907, 25901, 25824, 24680, 22516, 19422, + 15526, 10988, 5995, 756, -4514, -9599, -14287, -18384, + -21722, -24163, -25605, -25989, -25299, -23563, -20855, -17284, + -12999, -8178, -3018, 2266, 7456, 12339, 16712, 20394, + 23234, 25114, 25956, 25725, 24431, 22129, 18911, 14912, + 10298, 5257, 0, -5257, -10298, -14912, -18911, -22129, + -24432, -25725, -25956, -25114, -23234, -20394, -16712, -12339, + -7456, -2266, 3018, 8178, 13000, 17284, 20855, 23564, + 25299, 25989, 25604, 24163, 21722, 18384, 14287, 9599, + 4514, -756, -5996, -10988, -15526, -19422, -22516, -24680, + -25824, -25901, -24907, -22885, -19917, -16126, -11668, -6729, + -1511, 3768, 8892, 13649, 17842, 21297, 23873, 25462}, + { +// Carrier 1 Phase 5 + 24020, 21512, 18115, 13969, 9246, 4141, -1134, -6363, + -11329, -15827, -19671, -22703, -24796, -25865, -25865, -24796, + -22703, -19671, -15827, -11329, -6363, -1134, 4141, 9246, + 13969, 18115, 21512, 24020, 25536, 25997, 25383, 23721, + 21078, 17565, 13326, 8536, 3393, -1889, -7093, -12005, + -16421, -20158, -23062, -25013, -25931, -25777, -24558, -22325, + -19169, -15221, -10644, -5627, -378, 4886, 9949, 14601, + 18650, 21928, 24300, 25667, 25975, 25209, 23401, 20627, + 17000, 12671, 7818, 2642, -2642, -7818, -12671, -17000, + -20627, -23401, -25209, -25975, -25667, -24300, -21928, -18650, + -14601, -9949, -4886, 378, 5627, 10644, 15221, 19169, + 22325, 24558, 25777, 25931, 25013, 23062, 20158, 16421, + 12005, 7093, 1889, -3393, -8536, -13326, -17565, -21078, + -23721, -25383, -25997, -25536, -24020, -21512, -18115, -13969, + -9246, -4141, 1134, 6363, 11329, 15827, 19672, 22703, + 24796, 25865, 25865, 24796, 22703, 19671, 15827, 11329, + 6363, 1134, -4141, -9246, -13969, -18115, -21512, -24020, + -25536, -25997, -25383, -23721, -21078, -17565, -13326, -8536, + -3393, 1889, 7093, 12005, 16421, 20158, 23062, 25013, + 25931, 25777, 24558, 22325, 19169, 15221, 10644, 5627, + 378, -4886, -9949, -14601, -18650, -21928, -24300, -25667, + -25975, -25209, -23401, -20627, -17000, -12671, -7818, -2642, + 2642, 7818, 12671, 17000, 20627, 23401, 25209, 25975, + 25667, 24300, 21928, 18650, 14601, 9949, 4886, -378, + -5627, -10644, -15221, -19169, -22325, -24558, -25777, -25931, + -25013, -23062, -20158, -16421, -12005, -7093, -1889, 3393, + 8536, 13326, 17565, 21078, 23721, 25383, 25997, 25536}, + { +// Carrier 1 Phase 6 + 18384, 14287, 9599, 4514, -756, -5995, -10988, -15526, + -19422, -22516, -24680, -25824, -25901, -24907, -22885, -19917, + -16126, -11668, -6729, -1511, 3768, 8892, 13649, 17842, + 21297, 23873, 25462, 26000, 25462, 23873, 21297, 17842, + 13649, 8892, 3768, -1511, -6729, -11668, -16126, -19917, + -22885, -24907, -25901, -25824, -24680, -22516, -19422, -15526, + -10988, -5995, -756, 4514, 9599, 14287, 18384, 21722, + 24163, 25605, 25989, 25299, 23563, 20855, 17284, 12999, + 8178, 3018, -2266, -7456, -12339, -16712, -20394, -23234, + -25114, -25956, -25725, -24432, -22129, -18911, -14912, -10298, + -5257, 0, 5257, 10298, 14913, 18911, 22129, 24432, + 25725, 25956, 25114, 23234, 20394, 16712, 12339, 7456, + 2266, -3018, -8178, -13000, -17284, -20855, -23564, -25299, + -25989, -25604, -24163, -21722, -18384, -14287, -9599, -4514, + 756, 5996, 10988, 15526, 19422, 22516, 24680, 25824, + 25901, 24907, 22885, 19917, 16126, 11668, 6729, 1511, + -3768, -8892, -13649, -17842, -21297, -23873, -25462, -26000, + -25462, -23873, -21297, -17842, -13649, -8892, -3768, 1511, + 6729, 11668, 16126, 19917, 22885, 24907, 25901, 25824, + 24680, 22516, 19422, 15526, 10988, 5995, 756, -4514, + -9599, -14287, -18384, -21722, -24163, -25605, -25989, -25299, + -23563, -20855, -17284, -12999, -8178, -3018, 2266, 7456, + 12339, 16712, 20394, 23234, 25114, 25956, 25725, 24431, + 22129, 18911, 14912, 10298, 5257, 0, -5257, -10298, + -14913, -18911, -22129, -24432, -25725, -25956, -25114, -23234, + -20394, -16712, -12339, -7456, -2265, 3018, 8178, 13000, + 17284, 20855, 23564, 25299, 25989, 25604, 24163, 21722}, + { +// Carrier 1 Phase 7 + 9949, 4886, -378, -5627, -10644, -15221, -19169, -22325, + -24558, -25777, -25931, -25013, -23062, -20158, -16421, -12005, + -7093, -1889, 3393, 8536, 13326, 17565, 21078, 23721, + 25383, 25997, 25536, 24020, 21512, 18115, 13969, 9246, + 4141, -1134, -6363, -11329, -15827, -19671, -22703, -24796, + -25865, -25865, -24796, -22703, -19671, -15827, -11329, -6363, + -1134, 4142, 9246, 13969, 18115, 21512, 24020, 25536, + 25997, 25383, 23721, 21078, 17565, 13326, 8536, 3393, + -1889, -7093, -12005, -16421, -20158, -23062, -25013, -25931, + -25777, -24558, -22325, -19169, -15221, -10644, -5627, -378, + 4886, 9949, 14601, 18650, 21928, 24300, 25667, 25975, + 25209, 23401, 20627, 17000, 12671, 7818, 2642, -2642, + -7818, -12671, -17000, -20627, -23401, -25209, -25975, -25667, + -24300, -21928, -18650, -14601, -9949, -4886, 378, 5627, + 10644, 15221, 19169, 22325, 24558, 25777, 25931, 25013, + 23062, 20158, 16421, 12005, 7093, 1889, -3393, -8536, + -13326, -17565, -21078, -23721, -25383, -25997, -25536, -24020, + -21512, -18115, -13969, -9246, -4141, 1134, 6363, 11329, + 15827, 19672, 22703, 24796, 25865, 25865, 24796, 22703, + 19671, 15827, 11329, 6363, 1134, -4142, -9246, -13969, + -18115, -21512, -24020, -25536, -25997, -25383, -23721, -21078, + -17565, -13326, -8536, -3393, 1889, 7093, 12005, 16421, + 20158, 23062, 25013, 25931, 25777, 24558, 22325, 19169, + 15221, 10644, 5627, 378, -4886, -9949, -14601, -18650, + -21928, -24300, -25667, -25975, -25209, -23401, -20627, -17000, + -12671, -7818, -2642, 2642, 7818, 12671, 17000, 20627, + 23401, 25209, 25975, 25667, 24300, 21928, 18650, 14601}, + },{{ + +// Carrier 2 Phase 0 + 0, 5996, 11668, 16712, 20855, 23873, 25605, 25956, + 24907, 22516, 18911, 14287, 8892, 3018, -3018, -8892, + -14287, -18911, -22516, -24907, -25956, -25605, -23873, -20855, + -16712, -11668, -5996, 0, 5995, 11668, 16712, 20855, + 23873, 25604, 25956, 24907, 22516, 18911, 14287, 8892, + 3018, -3018, -8892, -14287, -18911, -22516, -24907, -25956, + -25605, -23873, -20855, -16712, -11668, -5996, 0, 5995, + 11668, 16712, 20855, 23873, 25604, 25956, 24907, 22516, + 18911, 14287, 8892, 3018, -3018, -8892, -14287, -18911, + -22516, -24907, -25956, -25605, -23873, -20855, -16712, -11668, + -5996, 0, 5995, 11668, 16712, 20855, 23873, 25604, + 25956, 24907, 22516, 18911, 14287, 8892, 3018, -3018, + -8892, -14287, -18911, -22516, -24907, -25956, -25605, -23873, + -20855, -16712, -11668, -5996, 0, 5995, 11668, 16712, + 20855, 23873, 25604, 25956, 24907, 22516, 18911, 14287, + 8892, 3018, -3018, -8892, -14287, -18911, -22516, -24907, + -25955, -25605, -23873, -20855, -16712, -11668, -5996, 0, + 5995, 11668, 16712, 20855, 23873, 25604, 25956, 24907, + 22516, 18911, 14287, 8892, 3018, -3018, -8892, -14287, + -18911, -22516, -24907, -25955, -25605, -23873, -20855, -16712, + -11669, -5996, 0, 5995, 11668, 16712, 20855, 23873, + 25604, 25956, 24907, 22516, 18911, 14287, 8892, 3018, + -3018, -8892, -14286, -18911, -22516, -24907, -25955, -25605, + -23873, -20855, -16712, -11669, -5996, 0, 5995, 11668, + 16712, 20854, 23873, 25604, 25956, 24907, 22516, 18911, + 14287, 8892, 3018, -3018, -8892, -14286, -18911, -22516, + -24907, -25955, -25605, -23873, -20855, -16712, -11669, -5996}, + { +// Carrier 2 Phase 1 + 9949, 15221, 19671, 23062, 25209, 25997, 25383, 23401, + 20158, 15827, 10644, 4886, -1134, -7093, -12671, -17565, + -21512, -24300, -25777, -25865, -24558, -21928, -18115, -13326, + -7818, -1889, 4141, 9949, 15221, 19671, 23062, 25209, + 25997, 25383, 23401, 20158, 15827, 10644, 4886, -1134, + -7093, -12671, -17565, -21512, -24300, -25777, -25865, -24558, + -21928, -18115, -13326, -7818, -1889, 4141, 9949, 15221, + 19671, 23062, 25209, 25997, 25383, 23401, 20158, 15827, + 10644, 4886, -1133, -7093, -12671, -17565, -21512, -24300, + -25777, -25865, -24558, -21928, -18115, -13326, -7818, -1889, + 4141, 9949, 15221, 19671, 23062, 25209, 25997, 25383, + 23401, 20158, 15827, 10644, 4886, -1133, -7093, -12671, + -17565, -21512, -24300, -25777, -25865, -24558, -21928, -18115, + -13326, -7818, -1889, 4141, 9949, 15221, 19671, 23062, + 25209, 25997, 25383, 23401, 20158, 15827, 10644, 4886, + -1133, -7093, -12670, -17565, -21512, -24300, -25777, -25865, + -24558, -21928, -18115, -13326, -7818, -1889, 4141, 9949, + 15220, 19671, 23062, 25209, 25997, 25383, 23401, 20158, + 15828, 10644, 4887, -1133, -7093, -12670, -17565, -21512, + -24299, -25777, -25865, -24558, -21928, -18115, -13326, -7818, + -1889, 4141, 9949, 15220, 19671, 23062, 25209, 25997, + 25383, 23401, 20158, 15828, 10644, 4887, -1133, -7093, + -12670, -17565, -21512, -24299, -25777, -25865, -24558, -21928, + -18115, -13326, -7818, -1889, 4141, 9949, 15220, 19671, + 23062, 25209, 25997, 25383, 23401, 20158, 15828, 10644, + 4887, -1133, -7093, -12670, -17565, -21512, -24299, -25777, + -25865, -24558, -21928, -18115, -13326, -7818, -1889, 4141}, + { +// Carrier 2 Phase 2 + 18384, 22129, 24680, 25901, 25725, 24163, 21297, 17284, + 12339, 6729, 756, -5257, -10988, -16126, -20394, -23563, + -25462, -25989, -25114, -22885, -19422, -14913, -9599, -3768, + 2265, 8178, 13649, 18384, 22129, 24680, 25901, 25725, + 24163, 21297, 17284, 12339, 6729, 756, -5257, -10988, + -16126, -20394, -23563, -25462, -25989, -25114, -22885, -19422, + -14913, -9599, -3768, 2265, 8178, 13649, 18384, 22128, + 24680, 25901, 25725, 24163, 21298, 17284, 12339, 6729, + 756, -5257, -10987, -16126, -20394, -23563, -25462, -25989, + -25114, -22885, -19422, -14913, -9599, -3768, 2265, 8178, + 13649, 18384, 22128, 24680, 25901, 25725, 24163, 21298, + 17284, 12339, 6729, 756, -5257, -10987, -16126, -20394, + -23563, -25462, -25989, -25114, -22885, -19422, -14913, -9599, + -3768, 2265, 8177, 13649, 18384, 22128, 24680, 25901, + 25725, 24163, 21298, 17284, 12339, 6729, 756, -5257, + -10987, -16125, -20394, -23563, -25462, -25989, -25114, -22885, + -19422, -14913, -9599, -3768, 2265, 8177, 13649, 18384, + 22128, 24680, 25901, 25725, 24163, 21298, 17284, 12339, + 6729, 756, -5257, -10987, -16125, -20394, -23563, -25462, + -25989, -25114, -22885, -19422, -14913, -9599, -3768, 2265, + 8177, 13649, 18384, 22128, 24680, 25901, 25725, 24163, + 21298, 17284, 12339, 6729, 756, -5257, -10987, -16125, + -20394, -23563, -25462, -25989, -25114, -22885, -19422, -14913, + -9599, -3768, 2265, 8177, 13649, 18384, 22128, 24680, + 25901, 25725, 24163, 21298, 17284, 12339, 6729, 756, + -5257, -10987, -16125, -20394, -23563, -25462, -25989, -25114, + -22885, -19422, -14913, -9599, -3768, 2265, 8177, 13649}, + { +// Carrier 2 Phase 3 + 24020, 25667, 25931, 24796, 22325, 18650, 13969, 8536, + 2642, -3393, -9246, -14601, -19169, -22703, -25013, -25975, + -25536, -23721, -20627, -16421, -11329, -5627, 378, 6363, + 12005, 17000, 21078, 24020, 25667, 25931, 24796, 22325, + 18650, 13969, 8536, 2642, -3393, -9246, -14601, -19169, + -22703, -25013, -25975, -25536, -23721, -20627, -16421, -11329, + -5627, 378, 6363, 12005, 17000, 21078, 24020, 25667, + 25931, 24796, 22325, 18650, 13969, 8536, 2642, -3393, + -9246, -14601, -19169, -22703, -25013, -25975, -25536, -23721, + -20627, -16421, -11329, -5627, 377, 6363, 12005, 17000, + 21078, 24020, 25667, 25931, 24796, 22325, 18650, 13969, + 8536, 2642, -3393, -9246, -14601, -19169, -22703, -25013, + -25975, -25536, -23721, -20627, -16421, -11329, -5627, 377, + 6363, 12005, 17000, 21078, 24020, 25667, 25931, 24796, + 22325, 18650, 13969, 8536, 2642, -3393, -9246, -14601, + -19169, -22703, -25013, -25975, -25536, -23721, -20627, -16421, + -11329, -5627, 377, 6363, 12005, 17000, 21078, 24020, + 25667, 25931, 24796, 22325, 18650, 13970, 8536, 2642, + -3393, -9246, -14601, -19169, -22703, -25013, -25975, -25536, + -23721, -20627, -16421, -11329, -5627, 377, 6363, 12005, + 17000, 21078, 24020, 25667, 25931, 24796, 22325, 18650, + 13970, 8536, 2642, -3393, -9246, -14601, -19169, -22703, + -25013, -25975, -25536, -23721, -20627, -16421, -11329, -5627, + 377, 6362, 12005, 17000, 21078, 24020, 25667, 25931, + 24796, 22325, 18650, 13970, 8536, 2642, -3393, -9246, + -14601, -19168, -22703, -25013, -25975, -25536, -23721, -20627, + -16421, -11329, -5627, 377, 6362, 12005, 17000, 21078}, + { +// Carrier 2 Phase 4 + 26000, 25299, 23234, 19917, 15526, 10298, 4514, -1511, + -7456, -12999, -17842, -21722, -24432, -25824, -25824, -24432, + -21722, -17842, -13000, -7456, -1511, 4514, 10298, 15526, + 19917, 23234, 25299, 26000, 25299, 23234, 19917, 15526, + 10298, 4514, -1511, -7456, -12999, -17842, -21722, -24431, + -25824, -25824, -24432, -21722, -17842, -13000, -7456, -1511, + 4514, 10297, 15526, 19917, 23234, 25299, 26000, 25299, + 23234, 19917, 15526, 10298, 4514, -1511, -7456, -12999, + -17842, -21722, -24431, -25824, -25824, -24432, -21722, -17842, + -13000, -7457, -1511, 4514, 10297, 15526, 19917, 23234, + 25299, 26000, 25299, 23234, 19917, 15526, 10298, 4515, + -1511, -7456, -12999, -17842, -21722, -24431, -25824, -25824, + -24432, -21722, -17842, -13000, -7457, -1511, 4514, 10297, + 15525, 19917, 23234, 25299, 26000, 25299, 23234, 19917, + 15526, 10298, 4515, -1511, -7456, -12999, -17842, -21722, + -24431, -25824, -25824, -24432, -21722, -17842, -13000, -7457, + -1512, 4514, 10297, 15525, 19916, 23234, 25299, 26000, + 25299, 23234, 19917, 15526, 10298, 4515, -1511, -7456, + -12999, -17842, -21722, -24431, -25824, -25824, -24432, -21722, + -17842, -13000, -7457, -1512, 4514, 10297, 15525, 19916, + 23234, 25299, 26000, 25299, 23234, 19917, 15526, 10298, + 4515, -1511, -7456, -12999, -17842, -21722, -24431, -25824, + -25824, -24432, -21722, -17842, -13000, -7457, -1512, 4514, + 10297, 15525, 19916, 23234, 25299, 26000, 25299, 23234, + 19917, 15526, 10298, 4515, -1511, -7456, -12999, -17842, + -21722, -24431, -25824, -25824, -24432, -21722, -17842, -13000, + -7457, -1512, 4514, 10297, 15525, 19916, 23234, 25299}, + { +// Carrier 2 Phase 5 + 24020, 21078, 17000, 12005, 6363, 378, -5627, -11329, + -16421, -20627, -23721, -25536, -25975, -25013, -22703, -19169, + -14601, -9246, -3393, 2642, 8536, 13969, 18650, 22325, + 24796, 25931, 25667, 24020, 21078, 17000, 12005, 6363, + 378, -5627, -11329, -16420, -20627, -23721, -25536, -25975, + -25013, -22703, -19169, -14601, -9247, -3393, 2642, 8536, + 13969, 18650, 22325, 24796, 25931, 25667, 24020, 21078, + 17000, 12005, 6363, 378, -5627, -11329, -16420, -20627, + -23721, -25536, -25975, -25013, -22703, -19169, -14601, -9247, + -3393, 2642, 8536, 13969, 18650, 22325, 24796, 25931, + 25667, 24020, 21078, 17000, 12005, 6363, 378, -5627, + -11329, -16420, -20627, -23721, -25536, -25975, -25013, -22703, + -19169, -14601, -9247, -3393, 2642, 8536, 13969, 18650, + 22325, 24796, 25931, 25667, 24020, 21078, 17000, 12005, + 6363, 378, -5627, -11329, -16420, -20627, -23721, -25536, + -25975, -25013, -22703, -19169, -14601, -9247, -3393, 2642, + 8536, 13969, 18650, 22325, 24796, 25931, 25667, 24020, + 21078, 17000, 12005, 6363, 378, -5627, -11329, -16420, + -20627, -23721, -25536, -25975, -25013, -22703, -19169, -14601, + -9247, -3393, 2642, 8535, 13969, 18650, 22325, 24796, + 25931, 25668, 24020, 21078, 17000, 12005, 6363, 378, + -5627, -11329, -16420, -20627, -23721, -25536, -25975, -25013, + -22703, -19169, -14601, -9247, -3394, 2642, 8535, 13969, + 18649, 22325, 24796, 25931, 25668, 24021, 21079, 17000, + 12005, 6363, 378, -5627, -11329, -16420, -20626, -23721, + -25536, -25975, -25013, -22703, -19169, -14601, -9247, -3394, + 2642, 8535, 13969, 18649, 22325, 24796, 25931, 25668}, + { +// Carrier 2 Phase 6 + 18384, 13649, 8178, 2266, -3768, -9599, -14912, -19422, + -22885, -25114, -25989, -25462, -23564, -20394, -16126, -10988, + -5257, 756, 6729, 12339, 17284, 21297, 24163, 25725, + 25901, 24680, 22129, 18384, 13649, 8178, 2266, -3768, + -9599, -14912, -19422, -22885, -25114, -25988, -25462, -23564, + -20394, -16126, -10988, -5257, 756, 6729, 12339, 17284, + 21297, 24163, 25725, 25901, 24680, 22129, 18384, 13649, + 8178, 2266, -3768, -9599, -14912, -19422, -22885, -25114, + -25988, -25462, -23564, -20394, -16126, -10988, -5257, 756, + 6729, 12339, 17284, 21297, 24162, 25725, 25901, 24680, + 22129, 18384, 13649, 8178, 2266, -3768, -9599, -14912, + -19422, -22885, -25114, -25988, -25462, -23564, -20394, -16126, + -10988, -5257, 756, 6729, 12339, 17284, 21297, 24162, + 25725, 25901, 24680, 22129, 18384, 13649, 8178, 2266, + -3768, -9599, -14912, -19422, -22885, -25114, -25988, -25462, + -23564, -20394, -16126, -10988, -5257, 755, 6729, 12339, + 17284, 21297, 24162, 25725, 25901, 24680, 22129, 18384, + 13649, 8178, 2266, -3767, -9599, -14912, -19422, -22885, + -25114, -25988, -25462, -23564, -20394, -16126, -10988, -5257, + 755, 6729, 12339, 17284, 21297, 24162, 25725, 25901, + 24680, 22129, 18384, 13649, 8178, 2266, -3767, -9599, + -14912, -19422, -22885, -25113, -25988, -25462, -23564, -20395, + -16126, -10988, -5257, 755, 6728, 12339, 17284, 21297, + 24162, 25725, 25901, 24680, 22129, 18385, 13649, 8178, + 2266, -3767, -9599, -14912, -19422, -22885, -25113, -25988, + -25462, -23564, -20395, -16126, -10988, -5258, 755, 6728, + 12339, 17284, 21297, 24162, 25725, 25901, 24680, 22129}, + { +// Carrier 2 Phase 7 + 9949, 4141, -1889, -7818, -13326, -18115, -21928, -24558, + -25865, -25777, -24300, -21512, -17565, -12671, -7093, -1134, + 4886, 10644, 15827, 20158, 23401, 25383, 25997, 25209, + 23062, 19672, 15221, 9949, 4142, -1889, -7818, -13326, + -18115, -21928, -24558, -25865, -25777, -24300, -21512, -17565, + -12671, -7093, -1134, 4886, 10644, 15827, 20158, 23401, + 25383, 25997, 25209, 23062, 19672, 15221, 9949, 4142, + -1889, -7818, -13326, -18115, -21928, -24558, -25865, -25777, + -24300, -21512, -17565, -12671, -7093, -1134, 4886, 10644, + 15827, 20158, 23401, 25383, 25997, 25209, 23062, 19672, + 15221, 9949, 4142, -1888, -7818, -13325, -18115, -21928, + -24558, -25865, -25777, -24300, -21512, -17565, -12671, -7094, + -1134, 4886, 10644, 15827, 20157, 23401, 25383, 25997, + 25209, 23062, 19672, 15221, 9949, 4142, -1888, -7818, + -13325, -18115, -21928, -24558, -25865, -25777, -24300, -21512, + -17565, -12671, -7094, -1134, 4886, 10643, 15827, 20157, + 23401, 25383, 25997, 25209, 23062, 19672, 15221, 9950, + 4142, -1888, -7818, -13325, -18115, -21928, -24558, -25865, + -25777, -24300, -21512, -17565, -12671, -7094, -1134, 4886, + 10643, 15827, 20157, 23401, 25383, 25997, 25209, 23062, + 19672, 15221, 9950, 4142, -1888, -7818, -13325, -18115, + -21928, -24558, -25865, -25777, -24300, -21512, -17565, -12671, + -7094, -1134, 4886, 10643, 15827, 20157, 23401, 25383, + 25997, 25209, 23062, 19672, 15221, 9950, 4142, -1888, + -7818, -13325, -18115, -21927, -24558, -25865, -25777, -24300, + -21512, -17565, -12671, -7094, -1134, 4886, 10643, 15827, + 20157, 23401, 25383, 25997, 25209, 23062, 19672, 15221}, + },{{ + +// Carrier 3 Phase 0 + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729}, + { +// Carrier 3 Phase 1 + 9949, 15827, 20627, 24020, 25777, 25777, 24020, 20627, + 15827, 9949, 3393, -3393, -9949, -15827, -20627, -24020, + -25777, -25777, -24020, -20627, -15827, -9949, -3393, 3393, + 9949, 15827, 20627, 24020, 25777, 25777, 24020, 20627, + 15827, 9949, 3393, -3393, -9949, -15827, -20627, -24020, + -25777, -25777, -24020, -20627, -15827, -9949, -3393, 3393, + 9949, 15827, 20627, 24020, 25777, 25777, 24020, 20627, + 15827, 9949, 3393, -3393, -9949, -15827, -20627, -24020, + -25777, -25777, -24020, -20627, -15827, -9949, -3393, 3393, + 9949, 15827, 20627, 24020, 25777, 25777, 24020, 20627, + 15827, 9949, 3393, -3393, -9949, -15827, -20627, -24020, + -25777, -25777, -24020, -20627, -15827, -9949, -3393, 3393, + 9949, 15827, 20627, 24020, 25777, 25777, 24020, 20627, + 15827, 9949, 3393, -3393, -9949, -15827, -20627, -24020, + -25777, -25777, -24020, -20627, -15827, -9949, -3393, 3393, + 9949, 15827, 20627, 24020, 25777, 25777, 24020, 20627, + 15827, 9949, 3393, -3393, -9949, -15827, -20627, -24020, + -25777, -25777, -24020, -20627, -15827, -9949, -3393, 3393, + 9949, 15827, 20627, 24020, 25777, 25777, 24020, 20627, + 15827, 9949, 3393, -3393, -9949, -15827, -20627, -24020, + -25777, -25777, -24020, -20627, -15827, -9949, -3393, 3393, + 9949, 15827, 20627, 24020, 25777, 25777, 24020, 20627, + 15827, 9949, 3393, -3393, -9949, -15827, -20627, -24020, + -25777, -25777, -24020, -20627, -15827, -9949, -3393, 3393, + 9949, 15827, 20627, 24020, 25777, 25777, 24020, 20627, + 15827, 9949, 3393, -3393, -9949, -15827, -20627, -24020, + -25777, -25777, -24020, -20627, -15827, -9949, -3393, 3393}, + { +// Carrier 3 Phase 2 + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999}, + { +// Carrier 3 Phase 3 + 24020, 25777, 25777, 24020, 20627, 15827, 9949, 3393, + -3393, -9949, -15827, -20627, -24020, -25777, -25777, -24020, + -20627, -15827, -9949, -3393, 3393, 9949, 15827, 20627, + 24020, 25777, 25777, 24020, 20627, 15827, 9949, 3393, + -3393, -9949, -15827, -20627, -24020, -25777, -25777, -24020, + -20627, -15827, -9949, -3393, 3393, 9949, 15827, 20627, + 24020, 25777, 25777, 24020, 20627, 15827, 9949, 3393, + -3393, -9949, -15827, -20627, -24020, -25777, -25777, -24020, + -20627, -15827, -9949, -3393, 3393, 9949, 15827, 20627, + 24020, 25777, 25777, 24020, 20627, 15827, 9949, 3393, + -3393, -9949, -15827, -20627, -24020, -25777, -25777, -24020, + -20627, -15827, -9949, -3393, 3393, 9949, 15827, 20627, + 24020, 25777, 25777, 24020, 20627, 15827, 9949, 3393, + -3393, -9949, -15827, -20627, -24020, -25777, -25777, -24020, + -20627, -15827, -9949, -3393, 3393, 9949, 15827, 20627, + 24020, 25777, 25777, 24020, 20627, 15827, 9949, 3393, + -3393, -9949, -15827, -20627, -24020, -25777, -25777, -24020, + -20627, -15827, -9949, -3393, 3393, 9949, 15827, 20627, + 24020, 25777, 25777, 24020, 20627, 15827, 9949, 3393, + -3393, -9949, -15827, -20627, -24020, -25777, -25777, -24020, + -20627, -15827, -9949, -3393, 3393, 9949, 15827, 20627, + 24020, 25777, 25777, 24020, 20627, 15827, 9949, 3393, + -3393, -9949, -15827, -20627, -24020, -25777, -25777, -24020, + -20627, -15827, -9949, -3393, 3393, 9949, 15827, 20627, + 24020, 25777, 25777, 24020, 20627, 15827, 9949, 3393, + -3393, -9949, -15827, -20627, -24020, -25777, -25777, -24020, + -20627, -15827, -9949, -3393, 3393, 9949, 15827, 20627}, + { +// Carrier 3 Phase 4 + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114}, + { +// Carrier 3 Phase 5 + 24020, 20627, 15827, 9949, 3393, -3393, -9949, -15827, + -20627, -24020, -25777, -25777, -24020, -20627, -15827, -9949, + -3393, 3393, 9949, 15827, 20627, 24020, 25777, 25777, + 24020, 20627, 15827, 9949, 3393, -3393, -9949, -15827, + -20627, -24020, -25777, -25777, -24020, -20627, -15827, -9949, + -3393, 3393, 9949, 15827, 20627, 24020, 25777, 25777, + 24020, 20627, 15827, 9949, 3393, -3393, -9949, -15827, + -20627, -24020, -25777, -25777, -24020, -20627, -15827, -9949, + -3393, 3393, 9949, 15827, 20627, 24020, 25777, 25777, + 24020, 20627, 15827, 9949, 3393, -3393, -9949, -15827, + -20627, -24020, -25777, -25777, -24020, -20627, -15827, -9949, + -3393, 3393, 9949, 15827, 20627, 24020, 25777, 25777, + 24020, 20627, 15827, 9949, 3393, -3393, -9949, -15827, + -20627, -24020, -25777, -25777, -24020, -20627, -15827, -9949, + -3393, 3393, 9949, 15827, 20627, 24020, 25777, 25777, + 24020, 20627, 15827, 9949, 3393, -3393, -9949, -15827, + -20627, -24020, -25777, -25777, -24020, -20627, -15827, -9949, + -3393, 3393, 9949, 15827, 20627, 24020, 25777, 25777, + 24020, 20627, 15827, 9949, 3393, -3393, -9949, -15827, + -20627, -24020, -25777, -25777, -24020, -20627, -15827, -9949, + -3393, 3393, 9949, 15827, 20627, 24020, 25777, 25777, + 24020, 20627, 15827, 9949, 3393, -3393, -9949, -15827, + -20627, -24020, -25777, -25777, -24020, -20627, -15827, -9949, + -3393, 3393, 9949, 15827, 20627, 24020, 25777, 25777, + 24020, 20627, 15827, 9949, 3393, -3393, -9949, -15827, + -20627, -24020, -25777, -25777, -24020, -20627, -15827, -9949, + -3393, 3393, 9949, 15827, 20627, 24020, 25777, 25777}, + { +// Carrier 3 Phase 6 + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516}, + { +// Carrier 3 Phase 7 + 9949, 3393, -3393, -9949, -15827, -20627, -24020, -25777, + -25777, -24020, -20627, -15827, -9949, -3393, 3393, 9949, + 15827, 20627, 24020, 25777, 25777, 24020, 20627, 15827, + 9949, 3393, -3393, -9949, -15827, -20627, -24020, -25777, + -25777, -24020, -20627, -15827, -9949, -3393, 3393, 9949, + 15827, 20627, 24020, 25777, 25777, 24020, 20627, 15827, + 9949, 3393, -3393, -9949, -15827, -20627, -24020, -25777, + -25777, -24020, -20627, -15827, -9949, -3393, 3393, 9949, + 15827, 20627, 24020, 25777, 25777, 24020, 20627, 15827, + 9949, 3393, -3393, -9949, -15827, -20627, -24020, -25777, + -25777, -24020, -20627, -15827, -9949, -3393, 3393, 9949, + 15827, 20627, 24020, 25777, 25777, 24020, 20627, 15827, + 9949, 3393, -3393, -9949, -15827, -20627, -24020, -25777, + -25777, -24020, -20627, -15827, -9949, -3393, 3393, 9949, + 15827, 20627, 24020, 25777, 25777, 24020, 20627, 15827, + 9949, 3393, -3393, -9949, -15827, -20627, -24020, -25777, + -25777, -24020, -20627, -15827, -9949, -3393, 3393, 9949, + 15827, 20627, 24020, 25777, 25777, 24020, 20627, 15827, + 9949, 3393, -3393, -9949, -15827, -20627, -24020, -25777, + -25777, -24020, -20627, -15827, -9949, -3393, 3393, 9949, + 15827, 20627, 24020, 25777, 25777, 24020, 20627, 15827, + 9949, 3393, -3393, -9949, -15827, -20627, -24020, -25777, + -25777, -24020, -20627, -15827, -9949, -3393, 3393, 9949, + 15827, 20627, 24020, 25777, 25777, 24020, 20627, 15827, + 9949, 3393, -3393, -9949, -15827, -20627, -24020, -25777, + -25777, -24020, -20627, -15827, -9949, -3393, 3393, 9949, + 15827, 20627, 24020, 25777, 25777, 24020, 20627, 15827}, + },{{ + +// Carrier 4 Phase 0 + 0, 7456, 14287, 19917, 23873, 25824, 25605, 23234, + 18911, 13000, 5996, -1511, -8892, -15526, -20855, -24432, + -25956, -25299, -22516, -17842, -11668, -4514, 3018, 10298, + 16712, 21722, 24907, 26000, 24907, 21722, 16712, 10298, + 3018, -4514, -11668, -17842, -22516, -25299, -25956, -24431, + -20855, -15526, -8892, -1511, 5996, 13000, 18911, 23234, + 25605, 25824, 23873, 19917, 14287, 7456, 0, -7456, + -14287, -19917, -23873, -25824, -25604, -23234, -18911, -12999, + -5995, 1511, 8892, 15526, 20855, 24432, 25956, 25299, + 22516, 17842, 11668, 4514, -3018, -10298, -16712, -21722, + -24907, -26000, -24907, -21722, -16712, -10297, -3018, 4515, + 11668, 17842, 22516, 25299, 25956, 24431, 20855, 15525, + 8892, 1511, -5996, -13000, -18911, -23234, -25605, -25824, + -23873, -19917, -14287, -7456, 0, 7457, 14287, 19917, + 23873, 25824, 25604, 23234, 18911, 12999, 5995, -1511, + -8892, -15526, -20855, -24432, -25956, -25299, -22516, -17842, + -11668, -4514, 3018, 10298, 16712, 21722, 24907, 26000, + 24907, 21722, 16712, 10297, 3018, -4515, -11668, -17842, + -22516, -25299, -25955, -24431, -20855, -15525, -8892, -1511, + 5996, 13000, 18911, 23234, 25605, 25824, 23873, 19916, + 14287, 7456, 0, -7457, -14287, -19917, -23873, -25824, + -25604, -23234, -18911, -12999, -5995, 1512, 8892, 15526, + 20855, 24432, 25956, 25299, 22516, 17842, 11668, 4514, + -3018, -10298, -16712, -21722, -24907, -26000, -24907, -21722, + -16712, -10297, -3018, 4515, 11669, 17842, 22516, 25299, + 25955, 24431, 20854, 15525, 8892, 1511, -5996, -13000, + -18911, -23234, -25605, -25824, -23873, -19916, -14286, -7456}, + { +// Carrier 4 Phase 1 + 9949, 16421, 21512, 24796, 25997, 25013, 21928, 17000, + 10644, 3393, -4141, -11329, -17565, -22325, -25209, -25975, + -24558, -21078, -15827, -9246, -1889, 5627, 12671, 18650, + 23062, 25536, 25865, 24020, 20158, 14601, 7818, 378, + -7093, -13969, -19672, -23721, -25777, -25667, -23401, -19169, + -13326, -6363, 1134, 8536, 15221, 20627, 24300, 25931, + 25383, 22703, 18115, 12005, 4886, -2642, -9949, -16421, + -21512, -24796, -25997, -25013, -21928, -17000, -10644, -3393, + 4142, 11329, 17565, 22325, 25209, 25975, 24558, 21078, + 15827, 9246, 1888, -5627, -12671, -18650, -23062, -25536, + -25865, -24020, -20158, -14601, -7818, -377, 7093, 13969, + 19672, 23721, 25777, 25667, 23401, 19169, 13325, 6363, + -1134, -8536, -15221, -20627, -24300, -25931, -25383, -22703, + -18115, -12005, -4886, 2642, 9949, 16421, 21512, 24796, + 25997, 25013, 21928, 17000, 10644, 3393, -4142, -11329, + -17565, -22325, -25209, -25975, -24558, -21078, -15827, -9246, + -1888, 5627, 12671, 18650, 23062, 25536, 25865, 24020, + 20157, 14601, 7818, 377, -7094, -13969, -19672, -23721, + -25777, -25667, -23401, -19169, -13325, -6363, 1134, 8536, + 15221, 20627, 24300, 25931, 25383, 22703, 18115, 12005, + 4886, -2642, -9950, -16421, -21512, -24796, -25997, -25013, + -21928, -17000, -10643, -3393, 4142, 11329, 17565, 22325, + 25209, 25975, 24558, 21078, 15827, 9246, 1888, -5627, + -12671, -18650, -23062, -25536, -25865, -24020, -20157, -14601, + -7818, -377, 7094, 13970, 19672, 23721, 25777, 25667, + 23401, 19168, 13325, 6362, -1134, -8536, -15221, -20627, + -24300, -25931, -25383, -22703, -18115, -12005, -4886, 2642}, + { +// Carrier 4 Phase 2 + 18384, 22885, 25462, 25901, 24163, 20394, 14912, 8178, + 756, -6729, -13649, -19422, -23564, -25725, -25725, -23563, + -19422, -13649, -6729, 756, 8178, 14913, 20394, 24163, + 25901, 25462, 22885, 18384, 12339, 5257, -2266, -9599, + -16126, -21297, -24680, -25989, -25114, -22129, -17284, -10988, + -3768, 3768, 10988, 17284, 22129, 25114, 25988, 24680, + 21297, 16126, 9599, 2265, -5257, -12339, -18384, -22885, + -25462, -25901, -24162, -20394, -14912, -8178, -756, 6729, + 13649, 19422, 23564, 25725, 25725, 23563, 19422, 13649, + 6729, -756, -8178, -14913, -20394, -24163, -25901, -25462, + -22885, -18384, -12339, -5257, 2266, 9599, 16126, 21298, + 24680, 25989, 25114, 22128, 17284, 10987, 3768, -3768, + -10988, -17284, -22129, -25114, -25988, -24680, -21297, -16125, + -9599, -2265, 5257, 12339, 18384, 22885, 25462, 25901, + 24162, 20394, 14912, 8177, 755, -6729, -13649, -19422, + -23564, -25725, -25725, -23563, -19422, -13649, -6729, 756, + 8178, 14913, 20394, 24163, 25901, 25462, 22885, 18384, + 12339, 5257, -2266, -9599, -16126, -21298, -24680, -25989, + -25114, -22128, -17284, -10987, -3767, 3768, 10988, 17284, + 22129, 25114, 25988, 24680, 21297, 16125, 9599, 2265, + -5257, -12339, -18384, -22885, -25462, -25901, -24162, -20394, + -14912, -8177, -755, 6729, 13649, 19422, 23564, 25725, + 25725, 23563, 19422, 13649, 6728, -756, -8178, -14913, + -20395, -24163, -25901, -25462, -22885, -18384, -12339, -5257, + 2266, 9599, 16126, 21298, 24680, 25989, 25113, 22128, + 17284, 10987, 3767, -3768, -10988, -17284, -22129, -25114, + -25988, -24680, -21297, -16125, -9599, -2265, 5258, 12339}, + { +// Carrier 4 Phase 3 + 24020, 25865, 25536, 23062, 18650, 12671, 5627, -1889, + -9246, -15827, -21078, -24558, -25975, -25209, -22325, -17565, + -11329, -4141, 3393, 10644, 17000, 21928, 25013, 25997, + 24796, 21512, 16421, 9949, 2642, -4886, -12005, -18115, + -22703, -25383, -25931, -24300, -20627, -15221, -8536, -1134, + 6363, 13326, 19169, 23401, 25667, 25777, 23721, 19671, + 13969, 7093, -378, -7818, -14601, -20158, -24020, -25865, + -25536, -23062, -18650, -12671, -5627, 1889, 9247, 15827, + 21078, 24558, 25975, 25209, 22325, 17565, 11329, 4141, + -3393, -10644, -17000, -21928, -25013, -25997, -24796, -21512, + -16420, -9949, -2642, 4886, 12005, 18115, 22703, 25383, + 25931, 24300, 20627, 15221, 8536, 1133, -6363, -13326, + -19169, -23401, -25667, -25777, -23721, -19671, -13969, -7093, + 378, 7818, 14601, 20158, 24020, 25865, 25536, 23062, + 18650, 12670, 5627, -1889, -9247, -15827, -21078, -24558, + -25975, -25209, -22325, -17565, -11329, -4141, 3393, 10644, + 17000, 21928, 25013, 25997, 24796, 21512, 16420, 9949, + 2642, -4887, -12005, -18115, -22703, -25383, -25931, -24299, + -20627, -15220, -8535, -1133, 6363, 13326, 19169, 23401, + 25668, 25777, 23721, 19671, 13969, 7093, -378, -7818, + -14601, -20158, -24020, -25865, -25536, -23062, -18650, -12670, + -5627, 1889, 9247, 15828, 21078, 24558, 25975, 25209, + 22325, 17565, 11329, 4141, -3394, -10644, -17000, -21928, + -25013, -25997, -24796, -21512, -16420, -9949, -2642, 4887, + 12005, 18115, 22703, 25383, 25931, 24299, 20626, 15220, + 8535, 1133, -6363, -13326, -19169, -23401, -25668, -25777, + -23721, -19671, -13969, -7093, 378, 7818, 14601, 20158}, + { +// Carrier 4 Phase 4 + 26000, 24907, 21722, 16712, 10298, 3018, -4514, -11668, + -17842, -22516, -25299, -25956, -24432, -20855, -15526, -8892, + -1511, 5996, 13000, 18911, 23234, 25605, 25824, 23873, + 19917, 14287, 7456, 0, -7456, -14287, -19917, -23873, + -25824, -25604, -23234, -18911, -12999, -5995, 1511, 8892, + 15526, 20855, 24432, 25956, 25299, 22516, 17842, 11668, + 4514, -3018, -10298, -16712, -21722, -24907, -26000, -24907, + -21722, -16712, -10297, -3018, 4514, 11668, 17842, 22516, + 25299, 25956, 24431, 20855, 15526, 8892, 1511, -5996, + -13000, -18911, -23234, -25605, -25824, -23873, -19917, -14287, + -7456, 0, 7457, 14287, 19917, 23873, 25824, 25604, + 23234, 18911, 12999, 5995, -1511, -8892, -15526, -20855, + -24432, -25956, -25299, -22516, -17842, -11668, -4514, 3018, + 10298, 16712, 21722, 24907, 26000, 24907, 21722, 16712, + 10297, 3018, -4515, -11668, -17842, -22516, -25299, -25955, + -24431, -20855, -15525, -8892, -1511, 5996, 13000, 18911, + 23234, 25605, 25824, 23873, 19917, 14287, 7456, 0, + -7457, -14287, -19917, -23873, -25824, -25604, -23234, -18911, + -12999, -5995, 1512, 8892, 15526, 20855, 24432, 25956, + 25299, 22516, 17842, 11668, 4514, -3018, -10298, -16712, + -21722, -24907, -26000, -24907, -21722, -16712, -10297, -3018, + 4515, 11669, 17842, 22516, 25299, 25955, 24431, 20855, + 15525, 8892, 1511, -5996, -13000, -18911, -23234, -25605, + -25824, -23873, -19916, -14286, -7456, 0, 7457, 14287, + 19917, 23873, 25824, 25604, 23234, 18911, 12999, 5995, + -1512, -8892, -15526, -20855, -24432, -25956, -25299, -22516, + -17842, -11668, -4514, 3018, 10298, 16712, 21722, 24907}, + { +// Carrier 4 Phase 5 + 24020, 20158, 14601, 7818, 378, -7093, -13969, -19671, + -23721, -25777, -25667, -23401, -19169, -13326, -6363, 1134, + 8536, 15221, 20627, 24300, 25931, 25383, 22703, 18115, + 12005, 4886, -2642, -9949, -16421, -21512, -24796, -25997, + -25013, -21928, -17000, -10644, -3393, 4142, 11329, 17565, + 22325, 25209, 25975, 24558, 21078, 15827, 9246, 1889, + -5627, -12671, -18650, -23062, -25536, -25865, -24020, -20158, + -14601, -7818, -378, 7093, 13969, 19672, 23721, 25777, + 25667, 23401, 19169, 13326, 6363, -1134, -8536, -15221, + -20627, -24300, -25931, -25383, -22703, -18115, -12005, -4886, + 2642, 9949, 16421, 21512, 24796, 25997, 25013, 21928, + 17000, 10644, 3393, -4142, -11329, -17565, -22325, -25209, + -25975, -24558, -21078, -15827, -9246, -1888, 5627, 12671, + 18650, 23062, 25536, 25865, 24020, 20157, 14601, 7818, + 377, -7094, -13969, -19672, -23721, -25777, -25667, -23401, + -19169, -13325, -6363, 1134, 8536, 15221, 20627, 24300, + 25931, 25383, 22703, 18115, 12005, 4886, -2642, -9949, + -16421, -21512, -24796, -25997, -25013, -21928, -17000, -10643, + -3393, 4142, 11329, 17565, 22325, 25209, 25975, 24558, + 21078, 15827, 9246, 1888, -5627, -12671, -18650, -23062, + -25536, -25865, -24020, -20157, -14601, -7818, -377, 7094, + 13970, 19672, 23721, 25777, 25667, 23401, 19168, 13325, + 6363, -1134, -8536, -15221, -20627, -24300, -25931, -25383, + -22703, -18115, -12005, -4886, 2642, 9950, 16421, 21512, + 24796, 25997, 25013, 21927, 17000, 10643, 3393, -4142, + -11329, -17565, -22325, -25209, -25975, -24558, -21078, -15827, + -9246, -1888, 5627, 12671, 18650, 23062, 25536, 25865}, + { +// Carrier 4 Phase 6 + 18384, 12339, 5257, -2266, -9599, -16126, -21297, -24680, + -25989, -25114, -22129, -17284, -10988, -3768, 3768, 10988, + 17284, 22129, 25114, 25989, 24680, 21297, 16126, 9599, + 2266, -5257, -12339, -18384, -22885, -25462, -25901, -24163, + -20394, -14912, -8178, -756, 6729, 13649, 19422, 23564, + 25725, 25725, 23563, 19422, 13649, 6729, -756, -8178, + -14913, -20394, -24163, -25901, -25462, -22885, -18384, -12339, + -5257, 2266, 9599, 16126, 21298, 24680, 25989, 25114, + 22128, 17284, 10987, 3768, -3768, -10988, -17284, -22129, + -25114, -25988, -24680, -21297, -16126, -9599, -2265, 5257, + 12339, 18384, 22885, 25462, 25901, 24162, 20394, 14912, + 8178, 756, -6729, -13649, -19422, -23564, -25725, -25725, + -23563, -19422, -13649, -6729, 756, 8178, 14913, 20394, + 24163, 25901, 25462, 22885, 18384, 12339, 5257, -2266, + -9599, -16126, -21298, -24680, -25989, -25114, -22128, -17284, + -10987, -3767, 3768, 10988, 17284, 22129, 25114, 25988, + 24680, 21297, 16125, 9599, 2265, -5257, -12339, -18384, + -22885, -25462, -25901, -24162, -20394, -14912, -8177, -755, + 6729, 13649, 19422, 23564, 25725, 25725, 23563, 19422, + 13649, 6729, -756, -8178, -14913, -20394, -24163, -25901, + -25462, -22885, -18384, -12339, -5257, 2266, 9599, 16126, + 21298, 24680, 25989, 25113, 22128, 17284, 10987, 3767, + -3768, -10988, -17284, -22129, -25114, -25988, -24680, -21297, + -16125, -9599, -2265, 5258, 12339, 18385, 22885, 25462, + 25901, 24162, 20394, 14912, 8177, 755, -6729, -13649, + -19422, -23564, -25725, -25725, -23563, -19422, -13649, -6728, + 756, 8178, 14913, 20395, 24163, 25901, 25462, 22885}, + { +// Carrier 4 Phase 7 + 9949, 2642, -4886, -12005, -18115, -22703, -25383, -25931, + -24300, -20627, -15221, -8536, -1134, 6363, 13326, 19169, + 23401, 25667, 25777, 23721, 19671, 13969, 7093, -378, + -7818, -14601, -20158, -24020, -25865, -25536, -23062, -18650, + -12671, -5627, 1889, 9246, 15827, 21078, 24558, 25975, + 25209, 22325, 17565, 11329, 4141, -3393, -10644, -17000, + -21928, -25013, -25997, -24796, -21512, -16420, -9949, -2642, + 4886, 12005, 18115, 22703, 25383, 25931, 24300, 20627, + 15221, 8536, 1133, -6363, -13326, -19169, -23401, -25667, + -25777, -23721, -19671, -13969, -7093, 378, 7818, 14601, + 20158, 24020, 25865, 25536, 23062, 18650, 12671, 5627, + -1889, -9247, -15827, -21078, -24558, -25975, -25209, -22325, + -17565, -11329, -4141, 3393, 10644, 17000, 21928, 25013, + 25997, 24796, 21512, 16420, 9949, 2642, -4886, -12005, + -18115, -22703, -25383, -25931, -24300, -20627, -15220, -8536, + -1133, 6363, 13326, 19169, 23401, 25667, 25777, 23721, + 19671, 13969, 7093, -378, -7818, -14601, -20158, -24020, + -25865, -25536, -23062, -18650, -12670, -5627, 1889, 9247, + 15828, 21078, 24558, 25975, 25209, 22325, 17565, 11329, + 4141, -3393, -10644, -17000, -21928, -25013, -25997, -24796, + -21512, -16420, -9949, -2642, 4887, 12005, 18115, 22703, + 25383, 25931, 24299, 20627, 15220, 8535, 1133, -6363, + -13326, -19169, -23401, -25668, -25777, -23721, -19671, -13969, + -7093, 378, 7818, 14601, 20158, 24021, 25865, 25536, + 23062, 18649, 12670, 5627, -1889, -9247, -15828, -21079, + -24558, -25975, -25209, -22325, -17565, -11329, -4141, 3394, + 10644, 17000, 21928, 25013, 25997, 24796, 21512, 16420}, + },{{ + +// Carrier 5 Phase 0 + 0, 8178, 15526, 21297, 24907, 25989, 24432, 20394, + 14287, 6729, -1511, -9599, -16712, -22129, -25299, -25901, + -23873, -19422, -13000, -5257, 3018, 10988, 17842, 22885, + 25604, 25725, 23234, 18384, 11668, 3768, -4514, -12339, + -18911, -23563, -25824, -25462, -22516, -17284, -10298, -2266, + 5995, 13649, 19917, 24163, 25956, 25114, 21722, 16126, + 8892, 756, -7456, -14912, -20855, -24680, -26000, -24680, + -20855, -14913, -7456, 756, 8892, 16126, 21722, 25114, + 25956, 24163, 19917, 13649, 5996, -2265, -10297, -17284, + -22516, -25462, -25824, -23564, -18911, -12339, -4514, 3768, + 11668, 18384, 23234, 25725, 25605, 22885, 17842, 10988, + 3018, -5257, -12999, -19422, -23873, -25901, -25299, -22129, + -16712, -9599, -1511, 6729, 14287, 20394, 24431, 25988, + 24907, 21298, 15526, 8178, 0, -8177, -15525, -21297, + -24907, -25989, -24432, -20394, -14287, -6729, 1511, 9599, + 16712, 22128, 25299, 25901, 23873, 19422, 13000, 5257, + -3018, -10987, -17842, -22885, -25604, -25725, -23234, -18384, + -11668, -3768, 4514, 12339, 18911, 23563, 25824, 25462, + 22516, 17284, 10298, 2266, -5995, -13649, -19916, -24162, + -25955, -25114, -21722, -16126, -8892, -756, 7456, 14912, + 20855, 24680, 26000, 24680, 20855, 14913, 7457, -755, + -8892, -16125, -21722, -25113, -25956, -24163, -19917, -13649, + -5996, 2265, 10297, 17284, 22516, 25462, 25824, 23564, + 18911, 12339, 4515, -3767, -11668, -18384, -23234, -25725, + -25605, -22885, -17842, -10988, -3018, 5257, 12999, 19422, + 23873, 25901, 25299, 22129, 16712, 9599, 1512, -6728, + -14286, -20394, -24431, -25988, -24907, -21298, -15526, -8178}, + { +// Carrier 5 Phase 1 + 9949, 17000, 22325, 25383, 25865, 23721, 19169, 12671, + 4886, -3393, -11329, -18115, -23062, -25667, -25667, -23062, + -18115, -11329, -3393, 4886, 12671, 19169, 23721, 25865, + 25383, 22325, 17000, 9949, 1889, -6363, -13969, -20158, + -24300, -25975, -25013, -21512, -15827, -8536, -378, 7818, + 15221, 21078, 24796, 25997, 24558, 20627, 14601, 7093, + -1134, -9246, -16420, -21928, -25209, -25931, -24020, -19672, + -13326, -5627, 2642, 10644, 17565, 22703, 25536, 25777, + 23401, 18650, 12005, 4142, -4141, -12005, -18650, -23401, + -25777, -25536, -22703, -17565, -10644, -2642, 5627, 13325, + 19671, 24020, 25931, 25209, 21928, 16421, 9247, 1134, + -7093, -14601, -20627, -24558, -25997, -24796, -21078, -15221, + -7818, 377, 8536, 15827, 21512, 25013, 25975, 24300, + 20158, 13969, 6363, -1888, -9949, -17000, -22325, -25383, + -25865, -23721, -19169, -12671, -4886, 3393, 11329, 18115, + 23062, 25667, 25667, 23062, 18115, 11329, 3393, -4886, + -12670, -19169, -23721, -25865, -25383, -22325, -17000, -9950, + -1889, 6363, 13969, 20157, 24299, 25975, 25013, 21512, + 15828, 8536, 378, -7818, -15220, -21078, -24796, -25997, + -24558, -20627, -14601, -7094, 1133, 9246, 16420, 21928, + 25209, 25931, 24020, 19672, 13326, 5627, -2642, -10643, + -17565, -22703, -25536, -25777, -23401, -18650, -12005, -4142, + 4141, 12005, 18649, 23401, 25777, 25536, 22703, 17565, + 10644, 2642, -5627, -13325, -19671, -24020, -25931, -25209, + -21928, -16421, -9247, -1134, 7093, 14601, 20626, 24558, + 25997, 24796, 21079, 15221, 7818, -377, -8535, -15827, + -21512, -25013, -25975, -24300, -20158, -13970, -6363, 1888}, + { +// Carrier 5 Phase 2 + 18384, 23234, 25725, 25605, 22885, 17842, 10988, 3018, + -5257, -12999, -19422, -23873, -25901, -25299, -22129, -16712, + -9599, -1511, 6729, 14287, 20394, 24431, 25989, 24907, + 21297, 15526, 8178, 0, -8178, -15526, -21297, -24907, + -25989, -24432, -20394, -14287, -6729, 1511, 9599, 16712, + 22128, 25299, 25901, 23873, 19422, 13000, 5257, -3018, + -10988, -17842, -22885, -25604, -25725, -23234, -18384, -11668, + -3768, 4514, 12339, 18911, 23563, 25824, 25462, 22516, + 17284, 10298, 2266, -5995, -13649, -19917, -24162, -25956, + -25114, -21722, -16126, -8892, -756, 7456, 14912, 20855, + 24680, 26000, 24680, 20855, 14913, 7457, -756, -8892, + -16125, -21722, -25114, -25956, -24163, -19917, -13649, -5996, + 2265, 10297, 17284, 22516, 25462, 25824, 23564, 18911, + 12339, 4515, -3768, -11668, -18384, -23234, -25725, -25605, + -22885, -17842, -10988, -3018, 5257, 12999, 19422, 23873, + 25901, 25299, 22129, 16712, 9599, 1512, -6729, -14287, + -20394, -24431, -25988, -24907, -21298, -15526, -8178, 0, + 8177, 15525, 21297, 24907, 25989, 24432, 20394, 14287, + 6729, -1511, -9599, -16712, -22128, -25299, -25901, -23873, + -19422, -13000, -5257, 3018, 10987, 17842, 22885, 25604, + 25725, 23234, 18385, 11669, 3768, -4514, -12339, -18911, + -23563, -25824, -25462, -22516, -17284, -10298, -2266, 5995, + 13649, 19916, 24162, 25955, 25114, 21722, 16126, 8892, + 756, -7456, -14912, -20854, -24680, -26000, -24680, -20855, + -14913, -7457, 755, 8892, 16125, 21722, 25113, 25956, + 24163, 19917, 13649, 5996, -2265, -10297, -17284, -22516, + -25462, -25824, -23564, -18912, -12339, -4515, 3767, 11668}, + { +// Carrier 5 Phase 3 + 24020, 25931, 25209, 21928, 16421, 9246, 1134, -7093, + -14601, -20627, -24558, -25997, -24796, -21078, -15221, -7818, + 378, 8536, 15827, 21512, 25013, 25975, 24300, 20158, + 13969, 6363, -1889, -9949, -17000, -22325, -25383, -25865, + -23721, -19169, -12671, -4886, 3393, 11329, 18115, 23062, + 25667, 25667, 23062, 18115, 11329, 3393, -4886, -12671, + -19169, -23721, -25865, -25383, -22325, -17000, -9949, -1889, + 6363, 13969, 20158, 24300, 25975, 25013, 21512, 15827, + 8536, 378, -7818, -15221, -21078, -24796, -25997, -24558, + -20627, -14601, -7093, 1133, 9246, 16420, 21928, 25209, + 25931, 24020, 19672, 13326, 5627, -2642, -10644, -17565, + -22703, -25536, -25777, -23401, -18650, -12005, -4142, 4141, + 12005, 18650, 23401, 25777, 25536, 22703, 17565, 10644, + 2642, -5627, -13325, -19671, -24020, -25931, -25209, -21928, + -16421, -9247, -1134, 7093, 14601, 20627, 24558, 25997, + 24796, 21078, 15221, 7818, -377, -8536, -15827, -21512, + -25013, -25975, -24300, -20158, -13969, -6363, 1888, 9949, + 17000, 22325, 25383, 25865, 23721, 19169, 12671, 4887, + -3393, -11329, -18115, -23062, -25667, -25667, -23062, -18115, + -11329, -3393, 4886, 12670, 19169, 23721, 25865, 25383, + 22325, 17000, 9950, 1889, -6363, -13969, -20157, -24299, + -25975, -25013, -21512, -15828, -8536, -378, 7818, 15220, + 21078, 24796, 25997, 24558, 20627, 14601, 7094, -1133, + -9246, -16420, -21927, -25209, -25931, -24021, -19672, -13326, + -5627, 2642, 10643, 17565, 22703, 25536, 25777, 23401, + 18650, 12005, 4142, -4141, -12005, -18649, -23401, -25777, + -25536, -22703, -17565, -10644, -2642, 5627, 13325, 19671}, + { +// Carrier 5 Phase 4 + 26000, 24680, 20855, 14912, 7456, -756, -8892, -16126, + -21722, -25114, -25956, -24163, -19917, -13649, -5996, 2266, + 10298, 17284, 22516, 25462, 25824, 23564, 18911, 12339, + 4514, -3768, -11668, -18384, -23234, -25725, -25605, -22885, + -17842, -10988, -3018, 5257, 12999, 19422, 23873, 25901, + 25299, 22129, 16712, 9599, 1511, -6729, -14287, -20394, + -24431, -25988, -24907, -21298, -15526, -8178, 0, 8178, + 15526, 21297, 24907, 25989, 24432, 20394, 14287, 6729, + -1511, -9599, -16712, -22128, -25299, -25901, -23873, -19422, + -13000, -5257, 3018, 10987, 17842, 22885, 25604, 25725, + 23234, 18384, 11668, 3768, -4514, -12339, -18911, -23563, + -25824, -25462, -22516, -17284, -10298, -2266, 5995, 13649, + 19917, 24162, 25956, 25114, 21722, 16126, 8892, 756, + -7456, -14912, -20855, -24680, -26000, -24680, -20855, -14913, + -7457, 755, 8892, 16125, 21722, 25114, 25956, 24163, + 19917, 13649, 5996, -2265, -10297, -17284, -22516, -25462, + -25824, -23564, -18911, -12339, -4515, 3767, 11668, 18384, + 23234, 25725, 25605, 22885, 17842, 10988, 3018, -5257, + -12999, -19422, -23873, -25901, -25299, -22129, -16712, -9599, + -1512, 6729, 14286, 20394, 24431, 25988, 24907, 21298, + 15526, 8178, 0, -8177, -15525, -21297, -24907, -25989, + -24432, -20395, -14287, -6729, 1511, 9599, 16712, 22128, + 25299, 25901, 23873, 19422, 13000, 5257, -3018, -10987, + -17842, -22885, -25604, -25725, -23234, -18385, -11669, -3768, + 4514, 12339, 18911, 23563, 25824, 25462, 22516, 17284, + 10298, 2266, -5995, -13649, -19916, -24162, -25955, -25114, + -21722, -16126, -8892, -756, 7456, 14912, 20854, 24680}, + { +// Carrier 5 Phase 5 + 24020, 19671, 13326, 5627, -2642, -10644, -17565, -22703, + -25536, -25777, -23401, -18650, -12005, -4142, 4141, 12005, + 18650, 23401, 25777, 25536, 22703, 17565, 10644, 2642, + -5627, -13326, -19671, -24020, -25931, -25209, -21928, -16421, + -9246, -1134, 7093, 14601, 20627, 24558, 25997, 24796, + 21078, 15221, 7818, -378, -8536, -15827, -21512, -25013, + -25975, -24300, -20158, -13969, -6363, 1888, 9949, 17000, + 22325, 25383, 25865, 23721, 19169, 12671, 4886, -3393, + -11329, -18115, -23062, -25667, -25667, -23062, -18115, -11329, + -3393, 4886, 12671, 19169, 23721, 25865, 25383, 22325, + 17000, 9949, 1889, -6363, -13969, -20158, -24300, -25975, + -25013, -21512, -15827, -8536, -378, 7818, 15221, 21078, + 24796, 25997, 24558, 20627, 14601, 7094, -1133, -9246, + -16420, -21928, -25209, -25931, -24020, -19672, -13326, -5627, + 2642, 10643, 17565, 22703, 25536, 25777, 23401, 18650, + 12005, 4142, -4141, -12005, -18650, -23401, -25777, -25536, + -22703, -17565, -10644, -2642, 5627, 13325, 19671, 24020, + 25931, 25209, 21928, 16421, 9247, 1134, -7093, -14601, + -20627, -24558, -25997, -24796, -21078, -15221, -7818, 377, + 8535, 15827, 21512, 25013, 25975, 24300, 20158, 13970, + 6363, -1888, -9949, -17000, -22325, -25383, -25865, -23721, + -19169, -12671, -4887, 3393, 11329, 18115, 23062, 25667, + 25668, 23062, 18115, 11329, 3394, -4886, -12670, -19168, + -23721, -25865, -25383, -22325, -17000, -9950, -1889, 6362, + 13969, 20157, 24299, 25975, 25013, 21512, 15828, 8536, + 378, -7817, -15220, -21078, -24796, -25997, -24558, -20627, + -14601, -7094, 1133, 9246, 16420, 21927, 25209, 25931}, + { +// Carrier 5 Phase 6 + 18384, 11668, 3768, -4514, -12339, -18911, -23564, -25824, + -25462, -22516, -17284, -10298, -2266, 5995, 13649, 19917, + 24163, 25956, 25114, 21722, 16126, 8892, 756, -7456, + -14912, -20855, -24680, -26000, -24680, -20855, -14913, -7456, + 756, 8892, 16126, 21722, 25114, 25956, 24163, 19917, + 13649, 5996, -2265, -10298, -17284, -22516, -25462, -25824, + -23564, -18911, -12339, -4514, 3768, 11668, 18384, 23234, + 25725, 25605, 22885, 17842, 10988, 3018, -5257, -12999, + -19422, -23873, -25901, -25299, -22129, -16712, -9599, -1511, + 6729, 14287, 20394, 24431, 25988, 24907, 21298, 15526, + 8178, 0, -8178, -15526, -21297, -24907, -25989, -24432, + -20394, -14287, -6729, 1511, 9599, 16712, 22128, 25299, + 25901, 23873, 19422, 13000, 5257, -3018, -10987, -17842, + -22885, -25604, -25725, -23234, -18384, -11668, -3768, 4514, + 12339, 18911, 23563, 25824, 25462, 22516, 17284, 10298, + 2266, -5995, -13649, -19917, -24162, -25955, -25114, -21722, + -16126, -8892, -756, 7456, 14912, 20855, 24680, 26000, + 24680, 20855, 14913, 7457, -755, -8892, -16125, -21722, + -25114, -25956, -24163, -19917, -13649, -5996, 2265, 10297, + 17284, 22516, 25462, 25824, 23564, 18911, 12339, 4515, + -3767, -11668, -18384, -23234, -25725, -25605, -22885, -17842, + -10988, -3018, 5257, 12999, 19422, 23873, 25901, 25299, + 22129, 16712, 9599, 1512, -6728, -14286, -20394, -24431, + -25988, -24907, -21298, -15526, -8178, 0, 8177, 15525, + 21297, 24907, 25989, 24432, 20395, 14287, 6729, -1511, + -9599, -16712, -22128, -25299, -25901, -23873, -19422, -13000, + -5258, 3018, 10987, 17841, 22885, 25604, 25725, 23234}, + { +// Carrier 5 Phase 7 + 9949, 1889, -6363, -13969, -20158, -24300, -25975, -25013, + -21512, -15827, -8536, -378, 7818, 15221, 21078, 24796, + 25997, 24558, 20627, 14601, 7093, -1134, -9246, -16421, + -21928, -25209, -25931, -24020, -19672, -13326, -5627, 2642, + 10644, 17565, 22703, 25536, 25777, 23401, 18650, 12005, + 4142, -4141, -12005, -18650, -23401, -25777, -25536, -22703, + -17565, -10644, -2642, 5627, 13326, 19671, 24020, 25931, + 25209, 21928, 16421, 9247, 1134, -7093, -14601, -20627, + -24558, -25997, -24796, -21078, -15221, -7818, 377, 8536, + 15827, 21512, 25013, 25975, 24300, 20158, 13969, 6363, + -1888, -9949, -17000, -22325, -25383, -25865, -23721, -19169, + -12671, -4886, 3393, 11329, 18115, 23062, 25667, 25667, + 23062, 18115, 11329, 3393, -4886, -12670, -19169, -23721, + -25865, -25383, -22325, -17000, -9949, -1889, 6363, 13969, + 20157, 24300, 25975, 25013, 21512, 15827, 8536, 378, + -7818, -15220, -21078, -24796, -25997, -24558, -20627, -14601, + -7094, 1133, 9246, 16420, 21928, 25209, 25931, 24020, + 19672, 13326, 5627, -2642, -10643, -17565, -22703, -25536, + -25777, -23401, -18650, -12005, -4142, 4141, 12005, 18650, + 23401, 25777, 25536, 22703, 17565, 10644, 2642, -5627, + -13325, -19671, -24020, -25931, -25209, -21928, -16421, -9247, + -1134, 7093, 14601, 20626, 24558, 25997, 24796, 21079, + 15221, 7818, -377, -8535, -15827, -21512, -25013, -25975, + -24300, -20158, -13970, -6363, 1888, 9949, 17000, 22325, + 25383, 25865, 23721, 19169, 12671, 4887, -3393, -11329, + -18115, -23062, -25667, -25668, -23062, -18115, -11329, -3394, + 4886, 12670, 19168, 23721, 25865, 25383, 22325, 17000}, + },{{ + +// Carrier 6 Phase 0 + 0, 8892, 16712, 22516, 25605, 25605, 22516, 16712, + 8892, 0, -8892, -16712, -22516, -25604, -25605, -22516, + -16712, -8892, 0, 8892, 16712, 22516, 25604, 25605, + 22516, 16712, 8892, 0, -8892, -16712, -22516, -25604, + -25605, -22516, -16712, -8892, 0, 8892, 16712, 22516, + 25604, 25605, 22516, 16712, 8892, 0, -8892, -16712, + -22516, -25604, -25605, -22516, -16712, -8892, 0, 8892, + 16712, 22516, 25604, 25605, 22516, 16712, 8892, 0, + -8892, -16712, -22516, -25604, -25605, -22516, -16712, -8892, + 0, 8892, 16712, 22516, 25604, 25605, 22516, 16712, + 8892, 0, -8892, -16712, -22516, -25604, -25605, -22516, + -16712, -8892, 0, 8892, 16712, 22516, 25604, 25605, + 22516, 16712, 8892, 0, -8892, -16712, -22516, -25604, + -25605, -22516, -16712, -8892, 0, 8892, 16712, 22516, + 25604, 25605, 22516, 16712, 8892, 0, -8892, -16712, + -22516, -25604, -25605, -22516, -16712, -8892, 0, 8892, + 16712, 22516, 25604, 25605, 22516, 16712, 8892, 0, + -8892, -16712, -22516, -25604, -25605, -22516, -16712, -8892, + 0, 8892, 16712, 22516, 25604, 25605, 22516, 16712, + 8892, 0, -8892, -16712, -22516, -25604, -25605, -22516, + -16712, -8892, 0, 8892, 16712, 22516, 25604, 25605, + 22516, 16712, 8892, 0, -8892, -16712, -22516, -25604, + -25605, -22516, -16712, -8892, 0, 8892, 16712, 22516, + 25604, 25605, 22516, 16712, 8892, 0, -8892, -16712, + -22516, -25604, -25605, -22516, -16712, -8892, 0, 8892, + 16712, 22516, 25604, 25605, 22516, 16712, 8892, 0, + -8892, -16712, -22516, -25604, -25605, -22516, -16712, -8892}, + { +// Carrier 6 Phase 1 + 9949, 17565, 23062, 25777, 25383, 21928, 15827, 7818, + -1134, -9949, -17565, -23062, -25777, -25383, -21928, -15827, + -7818, 1134, 9949, 17565, 23062, 25777, 25383, 21928, + 15827, 7818, -1134, -9949, -17565, -23062, -25777, -25383, + -21928, -15827, -7818, 1134, 9949, 17565, 23062, 25777, + 25383, 21928, 15827, 7818, -1134, -9949, -17565, -23062, + -25777, -25383, -21928, -15827, -7818, 1134, 9949, 17565, + 23062, 25777, 25383, 21928, 15827, 7818, -1134, -9949, + -17565, -23062, -25777, -25383, -21928, -15827, -7818, 1134, + 9949, 17565, 23062, 25777, 25383, 21928, 15827, 7818, + -1134, -9949, -17565, -23062, -25777, -25383, -21928, -15827, + -7818, 1134, 9949, 17565, 23062, 25777, 25383, 21928, + 15827, 7818, -1134, -9949, -17565, -23062, -25777, -25383, + -21928, -15827, -7818, 1134, 9949, 17565, 23062, 25777, + 25383, 21928, 15827, 7818, -1134, -9949, -17565, -23062, + -25777, -25383, -21928, -15827, -7818, 1134, 9949, 17565, + 23062, 25777, 25383, 21928, 15827, 7818, -1134, -9949, + -17565, -23062, -25777, -25383, -21928, -15827, -7818, 1133, + 9949, 17565, 23062, 25777, 25383, 21928, 15827, 7818, + -1133, -9949, -17565, -23062, -25777, -25383, -21928, -15827, + -7818, 1133, 9949, 17565, 23062, 25777, 25383, 21928, + 15827, 7818, -1133, -9949, -17565, -23062, -25777, -25383, + -21928, -15827, -7818, 1133, 9949, 17565, 23062, 25777, + 25383, 21928, 15827, 7818, -1133, -9949, -17565, -23062, + -25777, -25383, -21928, -15827, -7818, 1133, 9949, 17565, + 23062, 25777, 25383, 21928, 15827, 7818, -1133, -9949, + -17565, -23062, -25777, -25383, -21928, -15827, -7818, 1133}, + { +// Carrier 6 Phase 2 + 18384, 23564, 25901, 25114, 21297, 14912, 6729, -2266, + -10988, -18384, -23563, -25901, -25114, -21297, -14913, -6729, + 2266, 10988, 18384, 23563, 25901, 25114, 21297, 14913, + 6729, -2266, -10988, -18384, -23563, -25901, -25114, -21297, + -14913, -6729, 2266, 10988, 18384, 23563, 25901, 25114, + 21297, 14913, 6729, -2266, -10988, -18384, -23563, -25901, + -25114, -21297, -14913, -6729, 2266, 10988, 18384, 23563, + 25901, 25114, 21297, 14913, 6729, -2266, -10988, -18384, + -23563, -25901, -25114, -21297, -14913, -6729, 2265, 10988, + 18384, 23563, 25901, 25114, 21297, 14913, 6729, -2265, + -10988, -18384, -23563, -25901, -25114, -21297, -14913, -6729, + 2265, 10988, 18384, 23563, 25901, 25114, 21297, 14913, + 6729, -2265, -10988, -18384, -23563, -25901, -25114, -21297, + -14913, -6729, 2265, 10988, 18384, 23563, 25901, 25114, + 21297, 14913, 6729, -2265, -10987, -18384, -23563, -25901, + -25114, -21298, -14913, -6729, 2265, 10987, 18384, 23563, + 25901, 25114, 21298, 14913, 6729, -2265, -10987, -18384, + -23563, -25901, -25114, -21298, -14913, -6729, 2265, 10987, + 18384, 23563, 25901, 25114, 21298, 14913, 6729, -2265, + -10987, -18384, -23563, -25901, -25114, -21298, -14913, -6729, + 2265, 10987, 18384, 23563, 25901, 25114, 21298, 14913, + 6729, -2265, -10987, -18384, -23563, -25901, -25114, -21298, + -14913, -6729, 2265, 10987, 18384, 23563, 25901, 25114, + 21298, 14913, 6729, -2265, -10987, -18384, -23563, -25901, + -25114, -21298, -14913, -6729, 2265, 10987, 18384, 23563, + 25901, 25114, 21298, 14913, 6729, -2265, -10987, -18384, + -23563, -25901, -25114, -21298, -14913, -6729, 2265, 10987}, + { +// Carrier 6 Phase 3 + 24020, 25975, 24796, 20627, 13969, 5627, -3393, -12005, + -19169, -24020, -25975, -24796, -20627, -13969, -5627, 3393, + 12005, 19169, 24020, 25975, 24796, 20627, 13969, 5627, + -3393, -12005, -19169, -24020, -25975, -24796, -20627, -13969, + -5627, 3393, 12005, 19169, 24020, 25975, 24796, 20627, + 13969, 5627, -3393, -12005, -19169, -24020, -25975, -24796, + -20627, -13969, -5627, 3393, 12005, 19169, 24020, 25975, + 24796, 20627, 13969, 5627, -3393, -12005, -19169, -24020, + -25975, -24796, -20627, -13969, -5627, 3393, 12005, 19169, + 24020, 25975, 24796, 20627, 13969, 5627, -3393, -12005, + -19169, -24020, -25975, -24796, -20627, -13969, -5627, 3393, + 12005, 19169, 24020, 25975, 24796, 20627, 13969, 5627, + -3393, -12005, -19169, -24020, -25975, -24796, -20627, -13969, + -5627, 3393, 12005, 19169, 24020, 25975, 24796, 20627, + 13969, 5627, -3393, -12005, -19169, -24020, -25975, -24796, + -20627, -13969, -5627, 3393, 12005, 19169, 24020, 25975, + 24796, 20627, 13969, 5627, -3393, -12005, -19169, -24020, + -25975, -24796, -20627, -13969, -5627, 3393, 12005, 19169, + 24020, 25975, 24796, 20627, 13969, 5627, -3393, -12005, + -19169, -24020, -25975, -24796, -20627, -13969, -5627, 3393, + 12005, 19169, 24020, 25975, 24796, 20627, 13969, 5627, + -3393, -12005, -19169, -24020, -25975, -24796, -20627, -13969, + -5627, 3393, 12005, 19169, 24020, 25975, 24796, 20627, + 13969, 5627, -3393, -12005, -19169, -24020, -25975, -24796, + -20627, -13969, -5627, 3393, 12005, 19169, 24020, 25975, + 24796, 20627, 13969, 5627, -3393, -12005, -19169, -24020, + -25975, -24796, -20627, -13969, -5627, 3393, 12005, 19169}, + { +// Carrier 6 Phase 4 + 26000, 24432, 19917, 13000, 4514, -4514, -12999, -19917, + -24432, -26000, -24432, -19917, -13000, -4514, 4514, 12999, + 19917, 24432, 26000, 24432, 19917, 13000, 4514, -4514, + -12999, -19917, -24431, -26000, -24432, -19917, -13000, -4514, + 4514, 12999, 19917, 24431, 26000, 24432, 19917, 13000, + 4514, -4514, -12999, -19917, -24431, -26000, -24432, -19917, + -13000, -4514, 4514, 12999, 19917, 24431, 26000, 24432, + 19917, 13000, 4514, -4514, -12999, -19917, -24431, -26000, + -24432, -19917, -13000, -4514, 4514, 12999, 19917, 24431, + 26000, 24432, 19917, 13000, 4514, -4514, -12999, -19917, + -24431, -26000, -24432, -19917, -13000, -4514, 4514, 12999, + 19917, 24431, 26000, 24432, 19917, 13000, 4514, -4514, + -12999, -19917, -24431, -26000, -24432, -19917, -13000, -4514, + 4514, 12999, 19917, 24431, 26000, 24432, 19917, 13000, + 4515, -4514, -12999, -19917, -24431, -26000, -24432, -19917, + -13000, -4515, 4514, 12999, 19917, 24431, 26000, 24432, + 19917, 13000, 4515, -4514, -12999, -19917, -24431, -26000, + -24432, -19917, -13000, -4515, 4514, 12999, 19917, 24431, + 26000, 24432, 19917, 13000, 4515, -4514, -12999, -19917, + -24431, -26000, -24432, -19917, -13000, -4515, 4514, 12999, + 19917, 24431, 26000, 24432, 19917, 13000, 4515, -4514, + -12999, -19917, -24431, -26000, -24432, -19917, -13000, -4515, + 4514, 12999, 19917, 24431, 26000, 24432, 19917, 13000, + 4515, -4514, -12999, -19916, -24431, -26000, -24432, -19917, + -13000, -4515, 4514, 12999, 19916, 24431, 26000, 24432, + 19917, 13000, 4515, -4514, -12999, -19916, -24431, -26000, + -24432, -19917, -13000, -4515, 4514, 12999, 19916, 24431}, + { +// Carrier 6 Phase 5 + 24020, 19169, 12005, 3393, -5627, -13969, -20627, -24796, + -25975, -24020, -19169, -12005, -3393, 5627, 13969, 20627, + 24796, 25975, 24020, 19169, 12005, 3393, -5627, -13969, + -20627, -24796, -25975, -24020, -19169, -12005, -3393, 5627, + 13969, 20627, 24796, 25975, 24020, 19169, 12005, 3393, + -5627, -13969, -20627, -24796, -25975, -24020, -19169, -12005, + -3393, 5627, 13969, 20627, 24796, 25975, 24020, 19169, + 12005, 3393, -5627, -13969, -20627, -24796, -25975, -24020, + -19169, -12005, -3393, 5627, 13969, 20627, 24796, 25975, + 24020, 19169, 12005, 3393, -5627, -13969, -20627, -24796, + -25975, -24020, -19169, -12005, -3393, 5627, 13969, 20627, + 24796, 25975, 24020, 19169, 12005, 3393, -5627, -13969, + -20627, -24796, -25975, -24020, -19169, -12005, -3393, 5627, + 13969, 20627, 24796, 25975, 24020, 19169, 12005, 3393, + -5627, -13969, -20627, -24796, -25975, -24020, -19169, -12005, + -3393, 5627, 13969, 20627, 24796, 25975, 24020, 19169, + 12005, 3393, -5627, -13969, -20627, -24796, -25975, -24020, + -19169, -12005, -3393, 5627, 13969, 20627, 24796, 25975, + 24020, 19169, 12005, 3393, -5627, -13969, -20627, -24796, + -25975, -24020, -19169, -12005, -3393, 5627, 13969, 20627, + 24796, 25975, 24020, 19169, 12005, 3393, -5627, -13969, + -20627, -24796, -25975, -24020, -19169, -12005, -3393, 5627, + 13969, 20627, 24796, 25975, 24020, 19169, 12005, 3393, + -5627, -13969, -20627, -24796, -25975, -24020, -19169, -12005, + -3393, 5627, 13969, 20627, 24796, 25975, 24020, 19169, + 12005, 3393, -5627, -13969, -20627, -24796, -25975, -24020, + -19169, -12005, -3393, 5627, 13969, 20627, 24796, 25975}, + { +// Carrier 6 Phase 6 + 18384, 10988, 2266, -6729, -14912, -21297, -25114, -25901, + -23564, -18384, -10988, -2266, 6729, 14912, 21297, 25114, + 25901, 23564, 18384, 10988, 2266, -6729, -14912, -21297, + -25114, -25901, -23564, -18384, -10988, -2266, 6729, 14912, + 21297, 25114, 25901, 23564, 18384, 10988, 2266, -6729, + -14912, -21297, -25114, -25901, -23564, -18384, -10988, -2266, + 6729, 14912, 21297, 25114, 25901, 23564, 18384, 10988, + 2266, -6729, -14912, -21297, -25114, -25901, -23564, -18384, + -10988, -2266, 6729, 14912, 21297, 25114, 25901, 23564, + 18384, 10988, 2266, -6729, -14912, -21297, -25114, -25901, + -23564, -18384, -10988, -2266, 6729, 14912, 21297, 25114, + 25901, 23564, 18384, 10988, 2266, -6729, -14912, -21297, + -25114, -25901, -23564, -18384, -10988, -2266, 6729, 14912, + 21297, 25114, 25901, 23564, 18384, 10988, 2266, -6729, + -14912, -21297, -25114, -25901, -23564, -18384, -10988, -2266, + 6729, 14912, 21297, 25114, 25901, 23564, 18384, 10988, + 2266, -6729, -14912, -21297, -25114, -25901, -23564, -18384, + -10988, -2266, 6729, 14912, 21297, 25114, 25901, 23564, + 18384, 10988, 2266, -6729, -14912, -21297, -25114, -25901, + -23564, -18384, -10988, -2266, 6729, 14912, 21297, 25114, + 25901, 23564, 18384, 10988, 2266, -6729, -14912, -21297, + -25114, -25901, -23564, -18384, -10988, -2266, 6729, 14912, + 21297, 25114, 25901, 23564, 18384, 10988, 2266, -6729, + -14912, -21297, -25114, -25901, -23564, -18384, -10988, -2266, + 6729, 14912, 21297, 25114, 25901, 23564, 18384, 10988, + 2266, -6729, -14912, -21297, -25113, -25901, -23564, -18384, + -10988, -2266, 6729, 14912, 21297, 25113, 25901, 23564}, + { +// Carrier 6 Phase 7 + 9949, 1134, -7818, -15827, -21928, -25383, -25777, -23062, + -17565, -9949, -1134, 7818, 15827, 21928, 25383, 25777, + 23062, 17565, 9949, 1134, -7818, -15827, -21928, -25383, + -25777, -23062, -17565, -9949, -1134, 7818, 15827, 21928, + 25383, 25777, 23062, 17565, 9949, 1134, -7818, -15827, + -21928, -25383, -25777, -23062, -17565, -9949, -1134, 7818, + 15827, 21928, 25383, 25777, 23062, 17565, 9949, 1134, + -7818, -15827, -21928, -25383, -25777, -23062, -17565, -9949, + -1134, 7818, 15827, 21928, 25383, 25777, 23062, 17565, + 9949, 1134, -7818, -15827, -21928, -25383, -25777, -23062, + -17565, -9949, -1134, 7818, 15827, 21928, 25383, 25777, + 23062, 17565, 9949, 1134, -7818, -15827, -21928, -25383, + -25777, -23062, -17565, -9949, -1134, 7818, 15827, 21928, + 25383, 25777, 23062, 17565, 9949, 1134, -7818, -15827, + -21928, -25383, -25777, -23062, -17565, -9949, -1134, 7818, + 15827, 21928, 25383, 25777, 23062, 17565, 9949, 1134, + -7818, -15827, -21928, -25383, -25777, -23062, -17565, -9949, + -1134, 7818, 15827, 21928, 25383, 25777, 23062, 17565, + 9949, 1134, -7818, -15827, -21928, -25383, -25777, -23062, + -17565, -9949, -1134, 7818, 15827, 21928, 25383, 25777, + 23062, 17565, 9949, 1134, -7818, -15827, -21928, -25383, + -25777, -23062, -17565, -9949, -1134, 7818, 15827, 21928, + 25383, 25777, 23062, 17565, 9949, 1134, -7818, -15827, + -21928, -25383, -25777, -23062, -17565, -9950, -1134, 7818, + 15827, 21928, 25383, 25777, 23062, 17565, 9950, 1134, + -7818, -15827, -21928, -25383, -25777, -23062, -17565, -9950, + -1134, 7818, 15827, 21928, 25383, 25777, 23062, 17565}, + },{{ + +// Carrier 7 Phase 0 + 0, 9599, 17842, 23564, 25956, 24680, 19917, 12339, + 3018, -6729, -15526, -22129, -25605, -25462, -21722, -14912, + -5996, 3768, 13000, 20394, 24907, 25901, 23234, 17284, + 8892, -756, -10298, -18384, -23873, -25989, -24431, -19422, + -11668, -2266, 7456, 16126, 22516, 25725, 25299, 21297, + 14287, 5257, -4514, -13649, -20855, -25114, -25824, -22885, + -16712, -8178, 1511, 10988, 18911, 24163, 26000, 24163, + 18911, 10988, 1511, -8178, -16712, -22885, -25824, -25114, + -20855, -13649, -4514, 5257, 14287, 21298, 25299, 25725, + 22516, 16126, 7456, -2266, -11668, -19422, -24432, -25988, + -23873, -18384, -10297, -756, 8892, 17284, 23234, 25901, + 24907, 20394, 12999, 3768, -5996, -14913, -21722, -25462, + -25604, -22128, -15526, -6729, 3018, 12339, 19917, 24680, + 25956, 23563, 17842, 9599, 0, -9599, -17842, -23564, + -25956, -24680, -19917, -12339, -3018, 6729, 15526, 22129, + 25605, 25462, 21722, 14912, 5995, -3768, -13000, -20394, + -24907, -25901, -23234, -17284, -8892, 756, 10298, 18384, + 23873, 25989, 24431, 19422, 11668, 2265, -7457, -16126, + -22516, -25725, -25299, -21297, -14287, -5257, 4515, 13649, + 20855, 25114, 25824, 22885, 16712, 8177, -1512, -10988, + -18911, -24163, -26000, -24162, -18911, -10987, -1511, 8178, + 16712, 22885, 25824, 25113, 20855, 13649, 4514, -5257, + -14287, -21298, -25299, -25725, -22516, -16125, -7456, 2266, + 11669, 19422, 24432, 25988, 23873, 18384, 10297, 755, + -8892, -17284, -23234, -25901, -24907, -20394, -12999, -3767, + 5996, 14913, 21722, 25462, 25604, 22128, 15525, 6728, + -3018, -12339, -19917, -24680, -25955, -23563, -17842, -9599}, + { +// Carrier 7 Phase 1 + 9949, 18115, 23721, 25975, 24558, 19671, 12005, 2642, + -7093, -15827, -22325, -25667, -25383, -21512, -14601, -5627, + 4142, 13326, 20627, 25013, 25865, 23062, 17000, 8536, + -1134, -10644, -18650, -24020, -25997, -24300, -19169, -11329, + -1889, 7818, 16421, 22703, 25777, 25209, 21078, 13969, + 4886, -4886, -13969, -21078, -25209, -25777, -22703, -16420, + -7818, 1889, 11329, 19169, 24300, 25997, 24020, 18650, + 10644, 1134, -8536, -17000, -23062, -25865, -25013, -20627, + -13326, -4141, 5627, 14601, 21512, 25383, 25667, 22325, + 15827, 7093, -2642, -12005, -19672, -24558, -25975, -23721, + -18115, -9949, -378, 9247, 17565, 23401, 25931, 24796, + 20158, 12671, 3393, -6363, -15221, -21928, -25536, -25536, + -21928, -15221, -6363, 3393, 12671, 20158, 24796, 25931, + 23401, 17565, 9246, -378, -9949, -18115, -23721, -25975, + -24558, -19671, -12005, -2642, 7094, 15827, 22325, 25667, + 25383, 21512, 14601, 5627, -4142, -13326, -20627, -25013, + -25865, -23062, -17000, -8536, 1134, 10644, 18650, 24020, + 25997, 24300, 19169, 11329, 1888, -7818, -16421, -22703, + -25777, -25209, -21078, -13969, -4886, 4887, 13969, 21078, + 25209, 25777, 22703, 16420, 7818, -1889, -11329, -19169, + -24300, -25997, -24020, -18650, -10643, -1133, 8536, 17000, + 23062, 25865, 25013, 20627, 13325, 4141, -5627, -14601, + -21512, -25383, -25667, -22325, -15827, -7093, 2642, 12005, + 19672, 24558, 25975, 23721, 18115, 9949, 377, -9247, + -17565, -23401, -25931, -24796, -20157, -12670, -3393, 6363, + 15221, 21928, 25536, 25536, 21928, 15220, 6363, -3394, + -12671, -20158, -24796, -25931, -23401, -17565, -9246, 378}, + { +// Carrier 7 Phase 2 + 18384, 23873, 25989, 24432, 19422, 11668, 2266, -7456, + -16126, -22516, -25725, -25299, -21297, -14287, -5257, 4514, + 13649, 20855, 25114, 25824, 22885, 16712, 8178, -1511, + -10988, -18911, -24163, -26000, -24163, -18911, -10988, -1511, + 8178, 16712, 22885, 25824, 25114, 20855, 13649, 4514, + -5257, -14287, -21297, -25299, -25725, -22516, -16126, -7456, + 2266, 11668, 19422, 24432, 25988, 23873, 18384, 10298, + 756, -8892, -17284, -23234, -25901, -24907, -20394, -12999, + -3768, 5996, 14913, 21722, 25462, 25604, 22128, 15526, + 6729, -3018, -12339, -19917, -24680, -25956, -23563, -17842, + -9599, 0, 9599, 17842, 23564, 25956, 24680, 19917, + 12339, 3018, -6729, -15526, -22129, -25605, -25462, -21722, + -14912, -5995, 3768, 13000, 20394, 24907, 25901, 23234, + 17284, 8892, -756, -10298, -18384, -23873, -25989, -24431, + -19422, -11668, -2265, 7457, 16126, 22516, 25725, 25299, + 21297, 14287, 5257, -4515, -13649, -20855, -25114, -25824, + -22885, -16712, -8177, 1511, 10988, 18911, 24163, 26000, + 24162, 18911, 10987, 1511, -8178, -16712, -22885, -25824, + -25114, -20855, -13649, -4514, 5257, 14287, 21298, 25299, + 25725, 22516, 16125, 7456, -2266, -11668, -19422, -24432, + -25988, -23873, -18384, -10297, -755, 8892, 17284, 23234, + 25901, 24907, 20394, 12999, 3767, -5996, -14913, -21722, + -25462, -25604, -22128, -15525, -6729, 3018, 12339, 19917, + 24680, 25955, 23563, 17842, 9599, 0, -9599, -17842, + -23564, -25956, -24680, -19916, -12339, -3018, 6729, 15526, + 22129, 25605, 25462, 21722, 14912, 5995, -3768, -13000, + -20395, -24907, -25901, -23234, -17284, -8892, 756, 10298}, + { +// Carrier 7 Phase 3 + 24020, 25997, 24300, 19169, 11329, 1889, -7818, -16421, + -22703, -25777, -25209, -21078, -13969, -4886, 4886, 13969, + 21078, 25209, 25777, 22703, 16421, 7818, -1889, -11329, + -19169, -24300, -25997, -24020, -18650, -10644, -1134, 8536, + 17000, 23062, 25865, 25013, 20627, 13326, 4141, -5627, + -14601, -21512, -25383, -25667, -22325, -15827, -7093, 2642, + 12005, 19672, 24558, 25975, 23721, 18115, 9949, 378, + -9246, -17565, -23401, -25931, -24796, -20158, -12671, -3393, + 6363, 15221, 21928, 25536, 25536, 21928, 15221, 6363, + -3393, -12671, -20158, -24796, -25931, -23401, -17565, -9246, + 378, 9949, 18115, 23721, 25975, 24558, 19671, 12005, + 2642, -7093, -15827, -22325, -25667, -25383, -21512, -14601, + -5627, 4142, 13326, 20627, 25013, 25865, 23062, 17000, + 8536, -1134, -10644, -18650, -24020, -25997, -24300, -19169, + -11329, -1888, 7818, 16421, 22703, 25777, 25209, 21078, + 13969, 4886, -4886, -13969, -21078, -25209, -25777, -22703, + -16420, -7818, 1889, 11329, 19169, 24300, 25997, 24020, + 18650, 10644, 1133, -8536, -17000, -23062, -25865, -25013, + -20627, -13325, -4141, 5627, 14601, 21512, 25383, 25667, + 22325, 15827, 7093, -2642, -12005, -19672, -24558, -25975, + -23721, -18115, -9949, -377, 9247, 17565, 23401, 25931, + 24796, 20157, 12670, 3393, -6363, -15221, -21928, -25536, + -25536, -21928, -15220, -6363, 3393, 12671, 20158, 24796, + 25931, 23401, 17565, 9246, -378, -9950, -18115, -23721, + -25975, -24558, -19671, -12005, -2642, 7094, 15828, 22325, + 25668, 25383, 21512, 14601, 5627, -4142, -13326, -20627, + -25013, -25865, -23062, -17000, -8535, 1134, 10644, 18650}, + { +// Carrier 7 Phase 4 + 26000, 24163, 18911, 10988, 1511, -8178, -16712, -22885, + -25824, -25114, -20855, -13649, -4514, 5257, 14287, 21297, + 25299, 25725, 22516, 16126, 7456, -2266, -11668, -19422, + -24432, -25989, -23873, -18384, -10298, -756, 8892, 17284, + 23234, 25901, 24907, 20394, 12999, 3768, -5996, -14913, + -21722, -25462, -25604, -22128, -15526, -6729, 3018, 12339, + 19917, 24680, 25956, 23563, 17842, 9599, 0, -9599, + -17842, -23564, -25956, -24680, -19917, -12339, -3018, 6729, + 15526, 22129, 25605, 25462, 21722, 14912, 5995, -3768, + -13000, -20394, -24907, -25901, -23234, -17284, -8892, 756, + 10298, 18384, 23873, 25989, 24431, 19422, 11668, 2265, + -7457, -16126, -22516, -25725, -25299, -21297, -14287, -5257, + 4515, 13649, 20855, 25114, 25824, 22885, 16712, 8177, + -1511, -10988, -18911, -24163, -26000, -24162, -18911, -10987, + -1511, 8178, 16712, 22885, 25824, 25114, 20855, 13649, + 4514, -5257, -14287, -21298, -25299, -25725, -22516, -16125, + -7456, 2266, 11668, 19422, 24432, 25988, 23873, 18384, + 10297, 755, -8892, -17284, -23234, -25901, -24907, -20394, + -12999, -3767, 5996, 14913, 21722, 25462, 25604, 22128, + 15525, 6729, -3018, -12339, -19917, -24680, -25955, -23563, + -17842, -9599, 0, 9599, 17842, 23564, 25956, 24680, + 19916, 12339, 3018, -6729, -15526, -22129, -25605, -25462, + -21722, -14912, -5995, 3768, 13000, 20394, 24907, 25901, + 23234, 17284, 8892, -756, -10298, -18384, -23873, -25989, + -24431, -19422, -11668, -2265, 7457, 16126, 22516, 25725, + 25299, 21297, 14286, 5257, -4515, -13649, -20855, -25114, + -25824, -22885, -16712, -8177, 1512, 10988, 18911, 24163}, + { +// Carrier 7 Phase 5 + 24020, 18650, 10644, 1134, -8536, -17000, -23062, -25865, + -25013, -20627, -13326, -4141, 5627, 14601, 21512, 25383, + 25667, 22325, 15827, 7093, -2642, -12005, -19672, -24558, + -25975, -23721, -18115, -9949, -378, 9246, 17565, 23401, + 25931, 24796, 20158, 12671, 3393, -6363, -15221, -21928, + -25536, -25536, -21928, -15221, -6363, 3393, 12671, 20158, + 24796, 25931, 23401, 17565, 9246, -378, -9949, -18115, + -23721, -25975, -24558, -19671, -12005, -2642, 7093, 15827, + 22325, 25667, 25383, 21512, 14601, 5627, -4142, -13326, + -20627, -25013, -25865, -23062, -17000, -8536, 1134, 10644, + 18650, 24020, 25997, 24300, 19169, 11329, 1888, -7818, + -16421, -22703, -25777, -25209, -21078, -13969, -4886, 4886, + 13969, 21078, 25209, 25777, 22703, 16420, 7818, -1889, + -11329, -19169, -24300, -25997, -24020, -18650, -10644, -1133, + 8536, 17000, 23062, 25865, 25013, 20627, 13325, 4141, + -5627, -14601, -21512, -25383, -25667, -22325, -15827, -7093, + 2642, 12005, 19672, 24558, 25975, 23721, 18115, 9949, + 377, -9247, -17565, -23401, -25931, -24796, -20157, -12670, + -3393, 6363, 15221, 21928, 25536, 25536, 21928, 15220, + 6363, -3393, -12671, -20158, -24796, -25931, -23401, -17565, + -9246, 378, 9950, 18115, 23721, 25975, 24558, 19671, + 12005, 2642, -7094, -15828, -22325, -25668, -25383, -21512, + -14601, -5627, 4142, 13326, 20627, 25013, 25865, 23062, + 17000, 8535, -1134, -10644, -18650, -24020, -25997, -24299, + -19168, -11329, -1888, 7818, 16421, 22703, 25777, 25209, + 21078, 13969, 4886, -4887, -13970, -21079, -25209, -25777, + -22703, -16420, -7818, 1889, 11329, 19169, 24300, 25997}, + { +// Carrier 7 Phase 6 + 18384, 10298, 756, -8892, -17284, -23234, -25901, -24907, + -20394, -12999, -3768, 5996, 14913, 21722, 25462, 25604, + 22129, 15526, 6729, -3018, -12339, -19917, -24680, -25956, + -23563, -17842, -9599, 0, 9599, 17842, 23564, 25956, + 24680, 19917, 12339, 3018, -6729, -15526, -22129, -25605, + -25462, -21722, -14912, -5995, 3768, 13000, 20394, 24907, + 25901, 23234, 17284, 8892, -756, -10298, -18384, -23873, + -25989, -24431, -19422, -11668, -2265, 7456, 16126, 22516, + 25725, 25299, 21297, 14287, 5257, -4514, -13649, -20855, + -25114, -25824, -22885, -16712, -8178, 1511, 10988, 18911, + 24163, 26000, 24162, 18911, 10987, 1511, -8178, -16712, + -22885, -25824, -25114, -20855, -13649, -4514, 5257, 14287, + 21298, 25299, 25725, 22516, 16125, 7456, -2266, -11668, + -19422, -24432, -25988, -23873, -18384, -10297, -756, 8892, + 17284, 23234, 25901, 24907, 20394, 12999, 3768, -5996, + -14913, -21722, -25462, -25604, -22128, -15525, -6729, 3018, + 12339, 19917, 24680, 25955, 23563, 17842, 9599, 0, + -9599, -17842, -23564, -25956, -24680, -19916, -12339, -3018, + 6729, 15526, 22129, 25605, 25462, 21722, 14912, 5995, + -3768, -13000, -20394, -24907, -25901, -23234, -17284, -8892, + 756, 10298, 18384, 23873, 25989, 24431, 19422, 11668, + 2265, -7457, -16126, -22516, -25725, -25299, -21297, -14286, + -5257, 4515, 13649, 20855, 25114, 25824, 22885, 16712, + 8177, -1512, -10988, -18911, -24163, -26000, -24162, -18911, + -10987, -1511, 8178, 16712, 22885, 25824, 25113, 20854, + 13649, 4514, -5257, -14287, -21298, -25299, -25725, -22516, + -16125, -7456, 2266, 11669, 19422, 24432, 25988, 23873}, + { +// Carrier 7 Phase 7 + 9949, 378, -9246, -17565, -23401, -25931, -24796, -20158, + -12671, -3393, 6363, 15221, 21928, 25536, 25536, 21928, + 15221, 6363, -3393, -12671, -20158, -24796, -25931, -23401, + -17565, -9246, 378, 9949, 18115, 23721, 25975, 24558, + 19671, 12005, 2642, -7093, -15827, -22325, -25667, -25383, + -21512, -14601, -5627, 4142, 13326, 20627, 25013, 25865, + 23062, 17000, 8536, -1134, -10644, -18650, -24020, -25997, + -24300, -19169, -11329, -1889, 7818, 16421, 22703, 25777, + 25209, 21078, 13969, 4886, -4886, -13969, -21078, -25209, + -25777, -22703, -16420, -7818, 1889, 11329, 19169, 24300, + 25997, 24020, 18650, 10644, 1133, -8536, -17000, -23062, + -25865, -25013, -20627, -13325, -4141, 5627, 14601, 21512, + 25383, 25667, 22325, 15827, 7093, -2642, -12005, -19672, + -24558, -25975, -23721, -18115, -9949, -377, 9247, 17565, + 23401, 25931, 24796, 20157, 12670, 3393, -6363, -15221, + -21928, -25536, -25536, -21928, -15220, -6363, 3393, 12671, + 20158, 24796, 25931, 23401, 17565, 9246, -378, -9949, + -18115, -23721, -25975, -24558, -19671, -12005, -2642, 7094, + 15827, 22325, 25667, 25383, 21512, 14601, 5627, -4142, + -13326, -20627, -25013, -25865, -23062, -17000, -8535, 1134, + 10644, 18650, 24020, 25997, 24299, 19169, 11329, 1888, + -7818, -16421, -22703, -25777, -25209, -21078, -13969, -4886, + 4887, 13970, 21078, 25209, 25777, 22703, 16420, 7818, + -1889, -11329, -19169, -24300, -25997, -24020, -18649, -10643, + -1133, 8536, 17000, 23062, 25865, 25013, 20626, 13325, + 4141, -5627, -14601, -21512, -25383, -25667, -22325, -15827, + -7093, 2642, 12005, 19672, 24558, 25975, 23721, 18115}, + },{{ + +// Carrier 8 Phase 0 + 0, 10298, 18911, 24432, 25956, 23234, 16712, 7456, + -3018, -12999, -20855, -25299, -25605, -21722, -14287, -4514, + 5995, 15526, 22516, 25824, 24907, 19917, 11668, 1511, + -8892, -17842, -23873, -26000, -23873, -17842, -8892, 1511, + 11668, 19917, 24907, 25824, 22516, 15526, 5996, -4514, + -14287, -21722, -25604, -25299, -20855, -13000, -3018, 7456, + 16712, 23234, 25956, 24432, 18911, 10298, 0, -10297, + -18911, -24431, -25956, -23234, -16712, -7457, 3018, 12999, + 20855, 25299, 25605, 21722, 14287, 4514, -5995, -15526, + -22516, -25824, -24907, -19917, -11668, -1511, 8892, 17842, + 23873, 26000, 23873, 17842, 8892, -1511, -11668, -19917, + -24907, -25824, -22516, -15526, -5996, 4514, 14287, 21722, + 25604, 25299, 20855, 13000, 3018, -7456, -16712, -23234, + -25956, -24432, -18911, -10298, 0, 10297, 18911, 24431, + 25956, 23234, 16712, 7457, -3018, -12999, -20855, -25299, + -25605, -21722, -14287, -4515, 5995, 15525, 22516, 25824, + 24907, 19917, 11669, 1512, -8892, -17842, -23873, -26000, + -23873, -17842, -8892, 1511, 11668, 19916, 24907, 25824, + 22516, 15526, 5996, -4514, -14286, -21722, -25604, -25299, + -20855, -13000, -3018, 7456, 16712, 23234, 25955, 24432, + 18911, 10298, 0, -10297, -18911, -24431, -25956, -23234, + -16712, -7457, 3018, 12999, 20854, 25299, 25605, 21722, + 14287, 4515, -5995, -15525, -22516, -25824, -24907, -19917, + -11669, -1512, 8892, 17842, 23873, 26000, 23873, 17842, + 8892, -1511, -11668, -19916, -24907, -25824, -22516, -15526, + -5996, 4514, 14286, 21722, 25604, 25299, 20855, 13000, + 3018, -7456, -16712, -23234, -25955, -24432, -18912, -10298}, + { +// Carrier 8 Phase 1 + 9949, 18650, 24300, 25975, 23401, 17000, 7818, -2642, + -12671, -20627, -25209, -25667, -21928, -14601, -4886, 5627, + 15221, 22325, 25777, 25013, 20158, 12005, 1889, -8536, + -17565, -23721, -25997, -24020, -18115, -9246, 1134, 11329, + 19671, 24796, 25865, 22703, 15827, 6363, -4141, -13969, + -21512, -25536, -25383, -21078, -13326, -3393, 7093, 16420, + 23062, 25931, 24558, 19169, 10644, 378, -9949, -18650, + -24300, -25975, -23401, -17000, -7818, 2642, 12671, 20627, + 25209, 25667, 21928, 14601, 4886, -5627, -15221, -22325, + -25777, -25013, -20158, -12005, -1889, 8536, 17565, 23721, + 25997, 24020, 18115, 9247, -1133, -11329, -19671, -24796, + -25865, -22703, -15827, -6363, 4141, 13969, 21512, 25536, + 25383, 21078, 13326, 3393, -7093, -16420, -23062, -25931, + -24558, -19169, -10644, -378, 9949, 18650, 24300, 25975, + 23401, 17000, 7818, -2642, -12670, -20627, -25209, -25667, + -21928, -14601, -4887, 5627, 15220, 22325, 25777, 25013, + 20158, 12005, 1889, -8536, -17565, -23721, -25997, -24020, + -18115, -9247, 1133, 11329, 19671, 24796, 25865, 22703, + 15828, 6363, -4141, -13969, -21512, -25536, -25383, -21078, + -13326, -3393, 7093, 16420, 23062, 25931, 24558, 19169, + 10644, 378, -9949, -18650, -24299, -25975, -23401, -17000, + -7818, 2642, 12670, 20626, 25209, 25668, 21928, 14601, + 4887, -5627, -15220, -22325, -25777, -25013, -20158, -12005, + -1889, 8535, 17565, 23721, 25997, 24021, 18115, 9247, + -1133, -11329, -19671, -24796, -25865, -22703, -15828, -6363, + 4141, 13969, 21512, 25536, 25383, 21079, 13326, 3394, + -7093, -16420, -23062, -25931, -24558, -19169, -10644, -378}, + { +// Carrier 8 Phase 2 + 18384, 24163, 25989, 23564, 17284, 8178, -2266, -12339, + -20394, -25114, -25725, -22129, -14913, -5257, 5257, 14912, + 22129, 25725, 25114, 20394, 12339, 2266, -8178, -17284, + -23563, -25989, -24163, -18384, -9599, 756, 10988, 19422, + 24680, 25901, 22885, 16126, 6729, -3768, -13649, -21297, + -25462, -25462, -21298, -13649, -3768, 6729, 16126, 22885, + 25901, 24680, 19422, 10988, 756, -9599, -18384, -24162, + -25989, -23564, -17284, -8178, 2265, 12339, 20394, 25114, + 25725, 22129, 14913, 5257, -5257, -14912, -22128, -25725, + -25114, -20394, -12339, -2266, 8178, 17284, 23563, 25988, + 24163, 18384, 9599, -756, -10987, -19422, -24680, -25901, + -22885, -16126, -6729, 3768, 13649, 21297, 25462, 25462, + 21298, 13649, 3768, -6729, -16125, -22885, -25901, -24680, + -19422, -10988, -756, 9599, 18384, 24162, 25989, 23564, + 17284, 8178, -2265, -12339, -20394, -25114, -25725, -22129, + -14913, -5257, 5257, 14912, 22128, 25725, 25114, 20394, + 12339, 2266, -8177, -17284, -23563, -25988, -24163, -18384, + -9599, 755, 10987, 19422, 24680, 25901, 22885, 16126, + 6729, -3767, -13649, -21297, -25462, -25462, -21298, -13649, + -3768, 6728, 16125, 22885, 25901, 24680, 19422, 10988, + 756, -9599, -18384, -24162, -25989, -23564, -17284, -8178, + 2265, 12339, 20394, 25113, 25725, 22129, 14913, 5258, + -5257, -14912, -22128, -25725, -25114, -20395, -12339, -2266, + 8177, 17284, 23563, 25988, 24163, 18385, 9599, -755, + -10987, -19422, -24680, -25901, -22885, -16126, -6729, 3767, + 13649, 21297, 25462, 25462, 21298, 13649, 3768, -6728, + -16125, -22885, -25901, -24680, -19422, -10988, -756, 9598}, + { +// Carrier 8 Phase 3 + 24020, 25997, 23721, 17565, 8536, -1889, -12005, -20158, + -25013, -25777, -22325, -15221, -5627, 4886, 14601, 21928, + 25667, 25209, 20627, 12671, 2642, -7818, -17000, -23401, + -25975, -24300, -18650, -9949, 378, 10644, 19169, 24558, + 25931, 23062, 16421, 7093, -3393, -13326, -21078, -25383, + -25536, -21512, -13969, -4142, 6363, 15827, 22703, 25865, + 24796, 19672, 11329, 1134, -9246, -18115, -24020, -25997, + -23721, -17565, -8536, 1888, 12005, 20158, 25013, 25777, + 22325, 15221, 5627, -4886, -14601, -21928, -25667, -25209, + -20627, -12671, -2642, 7818, 17000, 23401, 25975, 24300, + 18650, 9949, -377, -10644, -19169, -24558, -25931, -23062, + -16421, -7094, 3393, 13325, 21078, 25383, 25536, 21512, + 13969, 4142, -6363, -15827, -22703, -25865, -24796, -19672, + -11329, -1134, 9246, 18115, 24020, 25997, 23721, 17565, + 8536, -1888, -12005, -20157, -25013, -25777, -22325, -15221, + -5627, 4886, 14601, 21928, 25667, 25209, 20627, 12671, + 2642, -7818, -17000, -23401, -25975, -24300, -18650, -9950, + 377, 10643, 19169, 24558, 25931, 23062, 16421, 7094, + -3393, -13325, -21078, -25383, -25536, -21512, -13970, -4142, + 6363, 15827, 22703, 25865, 24796, 19672, 11329, 1134, + -9246, -18115, -24020, -25997, -23721, -17565, -8536, 1888, + 12005, 20157, 25013, 25777, 22325, 15221, 5627, -4886, + -14601, -21927, -25667, -25209, -20627, -12671, -2642, 7817, + 17000, 23401, 25975, 24300, 18650, 9950, -377, -10643, + -19168, -24558, -25931, -23062, -16421, -7094, 3393, 13325, + 21078, 25383, 25536, 21512, 13970, 4142, -6362, -15827, + -22703, -25865, -24796, -19672, -11330, -1134, 9246, 18115}, + { +// Carrier 8 Phase 4 + 26000, 23873, 17842, 8892, -1511, -11668, -19917, -24907, + -25824, -22516, -15526, -5996, 4514, 14287, 21722, 25604, + 25299, 20855, 13000, 3018, -7456, -16712, -23234, -25956, + -24432, -18911, -10298, 0, 10298, 18911, 24431, 25956, + 23234, 16712, 7456, -3018, -12999, -20855, -25299, -25605, + -21722, -14287, -4514, 5995, 15526, 22516, 25824, 24907, + 19917, 11668, 1511, -8892, -17842, -23873, -26000, -23873, + -17842, -8892, 1511, 11668, 19917, 24907, 25824, 22516, + 15526, 5996, -4514, -14287, -21722, -25604, -25299, -20855, + -13000, -3018, 7456, 16712, 23234, 25956, 24432, 18911, + 10298, 0, -10297, -18911, -24431, -25956, -23234, -16712, + -7457, 3018, 12999, 20855, 25299, 25605, 21722, 14287, + 4515, -5995, -15525, -22516, -25824, -24907, -19917, -11668, + -1511, 8892, 17842, 23873, 26000, 23873, 17842, 8892, + -1511, -11668, -19917, -24907, -25824, -22516, -15526, -5996, + 4514, 14287, 21722, 25604, 25299, 20855, 13000, 3018, + -7456, -16712, -23234, -25955, -24432, -18911, -10298, 0, + 10297, 18911, 24431, 25956, 23234, 16712, 7457, -3018, + -12999, -20855, -25299, -25605, -21722, -14287, -4515, 5995, + 15525, 22516, 25824, 24907, 19917, 11669, 1512, -8892, + -17842, -23873, -26000, -23873, -17842, -8892, 1511, 11668, + 19916, 24907, 25824, 22516, 15526, 5996, -4514, -14286, + -21722, -25604, -25299, -20855, -13000, -3018, 7456, 16712, + 23234, 25955, 24432, 18911, 10298, 0, -10297, -18911, + -24431, -25956, -23234, -16712, -7457, 3018, 12999, 20854, + 25299, 25605, 21722, 14287, 4515, -5995, -15525, -22516, + -25824, -24907, -19917, -11669, -1512, 8892, 17841, 23873}, + { +// Carrier 8 Phase 5 + 24020, 18115, 9246, -1134, -11329, -19671, -24796, -25865, + -22703, -15827, -6363, 4141, 13969, 21512, 25536, 25383, + 21078, 13326, 3393, -7093, -16421, -23062, -25931, -24558, + -19169, -10644, -378, 9949, 18650, 24300, 25975, 23401, + 17000, 7818, -2642, -12671, -20627, -25209, -25667, -21928, + -14601, -4886, 5627, 15221, 22325, 25777, 25013, 20158, + 12005, 1889, -8536, -17565, -23721, -25997, -24020, -18115, + -9247, 1133, 11329, 19671, 24796, 25865, 22703, 15827, + 6363, -4141, -13969, -21512, -25536, -25383, -21078, -13326, + -3393, 7093, 16420, 23062, 25931, 24558, 19169, 10644, + 378, -9949, -18650, -24300, -25975, -23401, -17000, -7818, + 2642, 12670, 20627, 25209, 25667, 21928, 14601, 4886, + -5627, -15221, -22325, -25777, -25013, -20158, -12005, -1889, + 8536, 17565, 23721, 25997, 24020, 18115, 9247, -1133, + -11329, -19671, -24796, -25865, -22703, -15827, -6363, 4141, + 13969, 21512, 25536, 25383, 21078, 13326, 3393, -7093, + -16420, -23062, -25931, -24558, -19169, -10644, -378, 9949, + 18650, 24299, 25975, 23401, 17000, 7818, -2642, -12670, + -20627, -25209, -25668, -21928, -14601, -4887, 5627, 15220, + 22325, 25777, 25013, 20158, 12005, 1889, -8535, -17565, + -23721, -25997, -24020, -18115, -9247, 1133, 11329, 19671, + 24796, 25865, 22703, 15828, 6363, -4141, -13969, -21512, + -25536, -25383, -21079, -13326, -3394, 7093, 16420, 23062, + 25931, 24558, 19169, 10644, 378, -9949, -18649, -24299, + -25975, -23401, -17000, -7818, 2642, 12670, 20626, 25209, + 25668, 21928, 14601, 4887, -5627, -15220, -22325, -25777, + -25013, -20158, -12005, -1889, 8535, 17565, 23721, 25997}, + { +// Carrier 8 Phase 6 + 18384, 9599, -756, -10988, -19422, -24680, -25901, -22885, + -16126, -6729, 3768, 13649, 21297, 25462, 25462, 21297, + 13649, 3768, -6729, -16126, -22885, -25901, -24680, -19422, + -10988, -756, 9599, 18384, 24163, 25989, 23564, 17284, + 8178, -2265, -12339, -20394, -25114, -25725, -22129, -14913, + -5257, 5257, 14912, 22128, 25725, 25114, 20394, 12339, + 2266, -8178, -17284, -23563, -25988, -24163, -18384, -9599, + 756, 10987, 19422, 24680, 25901, 22885, 16126, 6729, + -3768, -13649, -21297, -25462, -25462, -21298, -13649, -3768, + 6729, 16125, 22885, 25901, 24680, 19422, 10988, 756, + -9599, -18384, -24162, -25989, -23564, -17284, -8178, 2265, + 12339, 20394, 25114, 25725, 22129, 14913, 5257, -5257, + -14912, -22128, -25725, -25114, -20394, -12339, -2266, 8177, + 17284, 23563, 25988, 24163, 18384, 9599, -755, -10987, + -19422, -24680, -25901, -22885, -16126, -6729, 3767, 13649, + 21297, 25462, 25462, 21298, 13649, 3768, -6729, -16125, + -22885, -25901, -24680, -19422, -10988, -756, 9599, 18384, + 24162, 25989, 23564, 17284, 8178, -2265, -12339, -20394, + -25113, -25725, -22129, -14913, -5257, 5257, 14912, 22128, + 25725, 25114, 20395, 12339, 2266, -8177, -17284, -23563, + -25988, -24163, -18385, -9599, 755, 10987, 19422, 24680, + 25901, 22885, 16126, 6729, -3767, -13649, -21297, -25462, + -25462, -21298, -13649, -3768, 6728, 16125, 22885, 25901, + 24680, 19422, 10988, 756, -9599, -18384, -24162, -25989, + -23564, -17284, -8178, 2265, 12339, 20394, 25113, 25725, + 22129, 14913, 5258, -5257, -14912, -22128, -25725, -25114, + -20395, -12340, -2266, 8177, 17284, 23563, 25988, 24163}, + { +// Carrier 8 Phase 7 + 9949, -378, -10644, -19169, -24558, -25931, -23062, -16421, + -7093, 3393, 13326, 21078, 25383, 25536, 21512, 13969, + 4142, -6363, -15827, -22703, -25865, -24796, -19672, -11329, + -1134, 9246, 18115, 24020, 25997, 23721, 17565, 8536, + -1889, -12005, -20158, -25013, -25777, -22325, -15221, -5627, + 4886, 14601, 21928, 25667, 25209, 20627, 12671, 2642, + -7818, -17000, -23401, -25975, -24300, -18650, -9949, 378, + 10644, 19169, 24558, 25931, 23062, 16421, 7093, -3393, + -13325, -21078, -25383, -25536, -21512, -13969, -4142, 6363, + 15827, 22703, 25865, 24796, 19672, 11329, 1134, -9246, + -18115, -24020, -25997, -23721, -17565, -8536, 1888, 12005, + 20157, 25013, 25777, 22325, 15221, 5627, -4886, -14601, + -21928, -25667, -25209, -20627, -12671, -2642, 7818, 17000, + 23401, 25975, 24300, 18650, 9949, -377, -10643, -19169, + -24558, -25931, -23062, -16421, -7094, 3393, 13325, 21078, + 25383, 25536, 21512, 13970, 4142, -6363, -15827, -22703, + -25865, -24796, -19672, -11329, -1134, 9246, 18115, 24020, + 25997, 23721, 17565, 8536, -1888, -12005, -20157, -25013, + -25777, -22325, -15221, -5627, 4886, 14601, 21928, 25667, + 25209, 20627, 12671, 2642, -7818, -17000, -23401, -25975, + -24300, -18650, -9950, 377, 10643, 19168, 24558, 25931, + 23062, 16421, 7094, -3393, -13325, -21078, -25383, -25536, + -21512, -13970, -4142, 6362, 15827, 22703, 25865, 24796, + 19672, 11329, 1134, -9246, -18115, -24020, -25997, -23721, + -17565, -8536, 1888, 12005, 20157, 25013, 25777, 22325, + 15221, 5627, -4886, -14601, -21927, -25667, -25209, -20627, + -12671, -2642, 7817, 17000, 23401, 25975, 24300, 18650}, + },{{ + +// Carrier 9 Phase 0 + 0, 10988, 19917, 25114, 25605, 21297, 13000, 2266, + -8892, -18384, -24432, -25901, -22516, -14913, -4514, 6729, + 16712, 23563, 26000, 23564, 16712, 6729, -4514, -14912, + -22516, -25901, -24432, -18384, -8892, 2266, 12999, 21297, + 25604, 25114, 19917, 10988, 0, -10988, -19917, -25114, + -25605, -21297, -13000, -2266, 8892, 18384, 24431, 25901, + 22516, 14913, 4514, -6729, -16712, -23563, -26000, -23564, + -16712, -6729, 4514, 14912, 22516, 25901, 24432, 18384, + 8892, -2265, -12999, -21297, -25604, -25114, -19917, -10988, + 0, 10988, 19917, 25114, 25605, 21297, 13000, 2266, + -8892, -18384, -24431, -25901, -22516, -14913, -4514, 6729, + 16712, 23563, 26000, 23564, 16712, 6729, -4514, -14912, + -22516, -25901, -24432, -18384, -8892, 2265, 12999, 21297, + 25604, 25114, 19917, 10988, 0, -10987, -19917, -25114, + -25605, -21298, -13000, -2266, 8892, 18384, 24431, 25901, + 22516, 14913, 4514, -6729, -16712, -23563, -26000, -23564, + -16712, -6729, 4514, 14912, 22516, 25901, 24432, 18384, + 8892, -2265, -12999, -21297, -25604, -25114, -19917, -10988, + 0, 10987, 19917, 25114, 25605, 21298, 13000, 2266, + -8892, -18384, -24431, -25901, -22516, -14913, -4514, 6729, + 16712, 23563, 26000, 23564, 16712, 6729, -4514, -14912, + -22516, -25901, -24432, -18384, -8892, 2265, 12999, 21297, + 25604, 25114, 19917, 10988, 0, -10987, -19917, -25114, + -25605, -21298, -13000, -2266, 8892, 18384, 24431, 25901, + 22516, 14913, 4515, -6729, -16712, -23563, -26000, -23564, + -16712, -6729, 4514, 14912, 22516, 25901, 24432, 18384, + 8892, -2265, -12999, -21297, -25604, -25114, -19917, -10988}, + { +// Carrier 9 Phase 1 + 9949, 19169, 24796, 25777, 21928, 13969, 3393, -7818, + -17565, -24020, -25975, -23062, -15827, -5627, 5627, 15827, + 23062, 25975, 24020, 17565, 7818, -3393, -13969, -21928, + -25777, -24796, -19169, -9949, 1134, 12005, 20627, 25383, + 25383, 20627, 12005, 1134, -9949, -19169, -24796, -25777, + -21928, -13969, -3393, 7818, 17565, 24020, 25975, 23062, + 15827, 5627, -5627, -15827, -23062, -25975, -24020, -17565, + -7818, 3393, 13969, 21928, 25777, 24796, 19169, 9949, + -1134, -12005, -20627, -25383, -25383, -20627, -12005, -1134, + 9949, 19169, 24796, 25777, 21928, 13969, 3393, -7818, + -17565, -24020, -25975, -23062, -15827, -5627, 5627, 15827, + 23062, 25975, 24020, 17565, 7818, -3393, -13969, -21928, + -25777, -24796, -19169, -9949, 1134, 12005, 20627, 25383, + 25383, 20627, 12005, 1134, -9949, -19169, -24796, -25777, + -21928, -13969, -3393, 7818, 17565, 24020, 25975, 23062, + 15827, 5627, -5627, -15827, -23062, -25975, -24020, -17565, + -7818, 3393, 13969, 21928, 25777, 24796, 19169, 9949, + -1133, -12005, -20627, -25383, -25383, -20627, -12005, -1134, + 9949, 19169, 24796, 25777, 21928, 13969, 3393, -7818, + -17565, -24020, -25975, -23062, -15827, -5627, 5627, 15827, + 23062, 25975, 24020, 17565, 7818, -3393, -13969, -21928, + -25777, -24796, -19169, -9949, 1133, 12005, 20627, 25383, + 25383, 20627, 12005, 1134, -9949, -19169, -24796, -25777, + -21928, -13969, -3393, 7818, 17565, 24020, 25975, 23062, + 15827, 5627, -5627, -15827, -23062, -25975, -24020, -17565, + -7818, 3393, 13969, 21928, 25777, 24796, 19169, 9949, + -1133, -12005, -20627, -25383, -25383, -20627, -12005, -1134}, + { +// Carrier 9 Phase 2 + 18384, 24432, 25901, 22516, 14912, 4514, -6729, -16712, + -23563, -26000, -23564, -16712, -6729, 4514, 14912, 22516, + 25901, 24432, 18384, 8892, -2266, -12999, -21297, -25604, + -25114, -19917, -10988, 0, 10988, 19917, 25114, 25605, + 21297, 13000, 2266, -8892, -18384, -24431, -25901, -22516, + -14913, -4514, 6729, 16712, 23563, 26000, 23564, 16712, + 6729, -4514, -14912, -22516, -25901, -24432, -18384, -8892, + 2265, 12999, 21297, 25604, 25114, 19917, 10988, 0, + -10988, -19917, -25114, -25605, -21297, -13000, -2266, 8892, + 18384, 24431, 25901, 22516, 14913, 4514, -6729, -16712, + -23563, -26000, -23564, -16712, -6729, 4514, 14912, 22516, + 25901, 24432, 18384, 8892, -2265, -12999, -21297, -25604, + -25114, -19917, -10988, 0, 10987, 19917, 25114, 25605, + 21298, 13000, 2266, -8892, -18384, -24431, -25901, -22516, + -14913, -4514, 6729, 16712, 23563, 26000, 23564, 16712, + 6729, -4514, -14912, -22516, -25901, -24432, -18384, -8892, + 2265, 12999, 21297, 25604, 25114, 19917, 10988, 0, + -10987, -19917, -25114, -25605, -21298, -13000, -2266, 8892, + 18384, 24431, 25901, 22516, 14913, 4514, -6729, -16712, + -23563, -26000, -23564, -16712, -6729, 4514, 14912, 22516, + 25901, 24432, 18384, 8892, -2265, -12999, -21297, -25604, + -25114, -19917, -10988, 0, 10987, 19917, 25114, 25605, + 21298, 13000, 2266, -8892, -18384, -24431, -25901, -22516, + -14913, -4515, 6729, 16712, 23563, 26000, 23564, 16712, + 6729, -4514, -14912, -22516, -25901, -24432, -18384, -8892, + 2265, 12999, 21297, 25604, 25114, 19917, 10988, 0, + -10987, -19917, -25114, -25605, -21298, -13000, -2266, 8892}, + { +// Carrier 9 Phase 3 + 24020, 25975, 23062, 15827, 5627, -5627, -15827, -23062, + -25975, -24020, -17565, -7818, 3393, 13969, 21928, 25777, + 24796, 19169, 9949, -1134, -12005, -20627, -25383, -25383, + -20627, -12005, -1134, 9949, 19169, 24796, 25777, 21928, + 13969, 3393, -7818, -17565, -24020, -25975, -23062, -15827, + -5627, 5627, 15827, 23062, 25975, 24020, 17565, 7818, + -3393, -13969, -21928, -25777, -24796, -19169, -9949, 1134, + 12005, 20627, 25383, 25383, 20627, 12005, 1134, -9949, + -19169, -24796, -25777, -21928, -13969, -3393, 7818, 17565, + 24020, 25975, 23062, 15827, 5627, -5627, -15827, -23062, + -25975, -24020, -17565, -7818, 3393, 13969, 21928, 25777, + 24796, 19169, 9949, -1134, -12005, -20627, -25383, -25383, + -20627, -12005, -1134, 9949, 19169, 24796, 25777, 21928, + 13969, 3393, -7818, -17565, -24020, -25975, -23062, -15827, + -5627, 5627, 15827, 23062, 25975, 24020, 17565, 7818, + -3393, -13969, -21928, -25777, -24796, -19169, -9949, 1133, + 12005, 20627, 25383, 25383, 20627, 12005, 1134, -9949, + -19169, -24796, -25777, -21928, -13969, -3393, 7818, 17565, + 24020, 25975, 23062, 15827, 5627, -5627, -15827, -23062, + -25975, -24020, -17565, -7818, 3393, 13969, 21928, 25777, + 24796, 19169, 9949, -1133, -12005, -20627, -25383, -25383, + -20627, -12005, -1134, 9949, 19169, 24796, 25777, 21928, + 13969, 3393, -7818, -17565, -24020, -25975, -23062, -15827, + -5627, 5627, 15827, 23062, 25975, 24020, 17565, 7818, + -3393, -13969, -21928, -25777, -24796, -19169, -9949, 1133, + 12005, 20627, 25383, 25383, 20627, 12005, 1134, -9949, + -19169, -24796, -25777, -21928, -13969, -3393, 7818, 17565}, + { +// Carrier 9 Phase 4 + 26000, 23564, 16712, 6729, -4514, -14912, -22516, -25901, + -24432, -18384, -8892, 2266, 12999, 21297, 25604, 25114, + 19917, 10988, 0, -10988, -19917, -25114, -25605, -21297, + -13000, -2266, 8892, 18384, 24431, 25901, 22516, 14913, + 4514, -6729, -16712, -23563, -26000, -23564, -16712, -6729, + 4514, 14912, 22516, 25901, 24432, 18384, 8892, -2266, + -12999, -21297, -25604, -25114, -19917, -10988, 0, 10988, + 19917, 25114, 25605, 21297, 13000, 2266, -8892, -18384, + -24431, -25901, -22516, -14913, -4514, 6729, 16712, 23563, + 26000, 23564, 16712, 6729, -4514, -14912, -22516, -25901, + -24432, -18384, -8892, 2265, 12999, 21297, 25604, 25114, + 19917, 10988, 0, -10987, -19917, -25114, -25605, -21298, + -13000, -2266, 8892, 18384, 24431, 25901, 22516, 14913, + 4514, -6729, -16712, -23563, -26000, -23564, -16712, -6729, + 4514, 14912, 22516, 25901, 24432, 18384, 8892, -2265, + -12999, -21297, -25604, -25114, -19917, -10988, 0, 10987, + 19917, 25114, 25605, 21298, 13000, 2266, -8892, -18384, + -24431, -25901, -22516, -14913, -4514, 6729, 16712, 23563, + 26000, 23564, 16712, 6729, -4514, -14912, -22516, -25901, + -24432, -18384, -8892, 2265, 12999, 21297, 25604, 25114, + 19917, 10988, 0, -10987, -19917, -25114, -25605, -21298, + -13000, -2266, 8892, 18384, 24431, 25901, 22516, 14913, + 4515, -6729, -16712, -23563, -26000, -23564, -16712, -6729, + 4514, 14912, 22516, 25901, 24432, 18384, 8892, -2265, + -12999, -21297, -25604, -25114, -19917, -10988, 0, 10987, + 19917, 25114, 25605, 21298, 13000, 2266, -8892, -18384, + -24431, -25901, -22516, -14913, -4515, 6729, 16712, 23563}, + { +// Carrier 9 Phase 5 + 24020, 17565, 7818, -3393, -13969, -21928, -25777, -24796, + -19169, -9949, 1134, 12005, 20627, 25383, 25383, 20627, + 12005, 1134, -9949, -19169, -24796, -25777, -21928, -13969, + -3393, 7818, 17565, 24020, 25975, 23062, 15827, 5627, + -5627, -15827, -23062, -25975, -24020, -17565, -7818, 3393, + 13969, 21928, 25777, 24796, 19169, 9949, -1134, -12005, + -20627, -25383, -25383, -20627, -12005, -1134, 9949, 19169, + 24796, 25777, 21928, 13969, 3393, -7818, -17565, -24020, + -25975, -23062, -15827, -5627, 5627, 15827, 23062, 25975, + 24020, 17565, 7818, -3393, -13969, -21928, -25777, -24796, + -19169, -9949, 1134, 12005, 20627, 25383, 25383, 20627, + 12005, 1134, -9949, -19169, -24796, -25777, -21928, -13969, + -3393, 7818, 17565, 24020, 25975, 23062, 15827, 5627, + -5627, -15827, -23062, -25975, -24020, -17565, -7818, 3393, + 13969, 21928, 25777, 24796, 19169, 9949, -1133, -12005, + -20627, -25383, -25383, -20627, -12005, -1134, 9949, 19169, + 24796, 25777, 21928, 13969, 3393, -7818, -17565, -24020, + -25975, -23062, -15827, -5627, 5627, 15827, 23062, 25975, + 24020, 17565, 7818, -3393, -13969, -21928, -25777, -24796, + -19169, -9949, 1133, 12005, 20627, 25383, 25383, 20627, + 12005, 1134, -9949, -19169, -24796, -25777, -21928, -13969, + -3393, 7818, 17565, 24020, 25975, 23062, 15827, 5627, + -5627, -15827, -23062, -25975, -24020, -17565, -7818, 3393, + 13969, 21928, 25777, 24796, 19169, 9949, -1133, -12005, + -20627, -25383, -25383, -20627, -12005, -1134, 9949, 19169, + 24796, 25777, 21928, 13969, 3393, -7818, -17565, -24020, + -25975, -23062, -15827, -5627, 5627, 15827, 23062, 25975}, + { +// Carrier 9 Phase 6 + 18384, 8892, -2266, -12999, -21297, -25604, -25114, -19917, + -10988, 0, 10988, 19917, 25114, 25605, 21297, 13000, + 2266, -8892, -18384, -24432, -25901, -22516, -14913, -4514, + 6729, 16712, 23563, 26000, 23564, 16712, 6729, -4514, + -14912, -22516, -25901, -24432, -18384, -8892, 2266, 12999, + 21297, 25604, 25114, 19917, 10988, 0, -10988, -19917, + -25114, -25605, -21297, -13000, -2266, 8892, 18384, 24431, + 25901, 22516, 14913, 4514, -6729, -16712, -23563, -26000, + -23564, -16712, -6729, 4514, 14912, 22516, 25901, 24432, + 18384, 8892, -2265, -12999, -21297, -25604, -25114, -19917, + -10988, 0, 10988, 19917, 25114, 25605, 21297, 13000, + 2266, -8892, -18384, -24431, -25901, -22516, -14913, -4514, + 6729, 16712, 23563, 26000, 23564, 16712, 6729, -4514, + -14912, -22516, -25901, -24432, -18384, -8892, 2265, 12999, + 21297, 25604, 25114, 19917, 10988, 0, -10987, -19917, + -25114, -25605, -21298, -13000, -2266, 8892, 18384, 24431, + 25901, 22516, 14913, 4514, -6729, -16712, -23563, -26000, + -23564, -16712, -6729, 4514, 14912, 22516, 25901, 24432, + 18384, 8892, -2265, -12999, -21297, -25604, -25114, -19917, + -10988, 0, 10987, 19917, 25114, 25605, 21298, 13000, + 2266, -8892, -18384, -24431, -25901, -22516, -14913, -4515, + 6729, 16712, 23563, 26000, 23564, 16712, 6729, -4514, + -14912, -22516, -25901, -24432, -18384, -8892, 2265, 12999, + 21297, 25604, 25114, 19917, 10988, 0, -10987, -19917, + -25114, -25605, -21298, -13000, -2266, 8892, 18384, 24431, + 25901, 22516, 14913, 4515, -6729, -16712, -23563, -26000, + -23564, -16712, -6729, 4514, 14912, 22516, 25901, 24432}, + { +// Carrier 9 Phase 7 + 9949, -1134, -12005, -20627, -25383, -25383, -20627, -12005, + -1134, 9949, 19169, 24796, 25777, 21928, 13969, 3393, + -7818, -17565, -24020, -25975, -23062, -15827, -5627, 5627, + 15827, 23062, 25975, 24020, 17565, 7818, -3393, -13969, + -21928, -25777, -24796, -19169, -9949, 1134, 12005, 20627, + 25383, 25383, 20627, 12005, 1134, -9949, -19169, -24796, + -25777, -21928, -13969, -3393, 7818, 17565, 24020, 25975, + 23062, 15827, 5627, -5627, -15827, -23062, -25975, -24020, + -17565, -7818, 3393, 13969, 21928, 25777, 24796, 19169, + 9949, -1134, -12005, -20627, -25383, -25383, -20627, -12005, + -1134, 9949, 19169, 24796, 25777, 21928, 13969, 3393, + -7818, -17565, -24020, -25975, -23062, -15827, -5627, 5627, + 15827, 23062, 25975, 24020, 17565, 7818, -3393, -13969, + -21928, -25777, -24796, -19169, -9949, 1134, 12005, 20627, + 25383, 25383, 20627, 12005, 1134, -9949, -19169, -24796, + -25777, -21928, -13969, -3393, 7818, 17565, 24020, 25975, + 23062, 15827, 5627, -5627, -15827, -23062, -25975, -24020, + -17565, -7818, 3393, 13969, 21928, 25777, 24796, 19169, + 9949, -1133, -12005, -20627, -25383, -25383, -20627, -12005, + -1134, 9949, 19169, 24796, 25777, 21928, 13969, 3393, + -7818, -17565, -24020, -25975, -23062, -15827, -5627, 5627, + 15827, 23062, 25975, 24020, 17565, 7818, -3393, -13969, + -21928, -25777, -24796, -19169, -9949, 1133, 12005, 20627, + 25383, 25383, 20627, 12005, 1134, -9949, -19169, -24796, + -25777, -21928, -13969, -3393, 7818, 17565, 24020, 25975, + 23062, 15827, 5627, -5627, -15827, -23062, -25975, -24020, + -17565, -7818, 3393, 13969, 21928, 25777, 24796, 19169}, + },{{ + +// Carrier 10 Phase 0 + 0, 11668, 20855, 25605, 24907, 18911, 8892, -3018, + -14287, -22516, -25956, -23873, -16712, -5996, 5996, 16712, + 23873, 25956, 22516, 14287, 3018, -8892, -18911, -24907, + -25604, -20855, -11668, 0, 11668, 20855, 25605, 24907, + 18911, 8892, -3018, -14287, -22516, -25956, -23873, -16712, + -5995, 5996, 16712, 23873, 25956, 22516, 14287, 3018, + -8892, -18911, -24907, -25604, -20855, -11668, 0, 11668, + 20855, 25605, 24907, 18911, 8892, -3018, -14287, -22516, + -25956, -23873, -16712, -5995, 5996, 16712, 23873, 25956, + 22516, 14287, 3018, -8892, -18911, -24907, -25604, -20855, + -11668, 0, 11668, 20855, 25605, 24907, 18911, 8892, + -3018, -14287, -22516, -25956, -23873, -16712, -5995, 5996, + 16712, 23873, 25956, 22516, 14287, 3018, -8892, -18911, + -24907, -25604, -20855, -11668, 0, 11668, 20855, 25605, + 24907, 18911, 8892, -3018, -14287, -22516, -25956, -23873, + -16712, -5995, 5996, 16712, 23873, 25956, 22516, 14287, + 3018, -8892, -18911, -24907, -25604, -20855, -11668, 0, + 11668, 20855, 25605, 24907, 18911, 8892, -3018, -14287, + -22516, -25956, -23873, -16712, -5995, 5996, 16712, 23873, + 25956, 22516, 14287, 3018, -8892, -18911, -24907, -25604, + -20855, -11668, 0, 11668, 20855, 25605, 24907, 18911, + 8892, -3018, -14287, -22516, -25956, -23873, -16712, -5995, + 5996, 16712, 23873, 25955, 22516, 14287, 3018, -8892, + -18911, -24907, -25604, -20855, -11668, 0, 11669, 20855, + 25605, 24907, 18911, 8892, -3018, -14287, -22516, -25956, + -23873, -16712, -5995, 5996, 16712, 23873, 25955, 22516, + 14287, 3018, -8892, -18911, -24907, -25604, -20855, -11668}, + { +// Carrier 10 Phase 1 + 9949, 19671, 25209, 25383, 20158, 10644, -1134, -12671, + -21512, -25777, -24558, -18115, -7818, 4141, 15221, 23062, + 25997, 23401, 15827, 4886, -7093, -17565, -24300, -25865, + -21928, -13326, -1889, 9949, 19672, 25209, 25383, 20158, + 10644, -1134, -12671, -21512, -25777, -24558, -18115, -7818, + 4142, 15221, 23062, 25997, 23401, 15827, 4886, -7093, + -17565, -24300, -25865, -21928, -13326, -1889, 9949, 19672, + 25209, 25383, 20158, 10644, -1134, -12671, -21512, -25777, + -24558, -18115, -7818, 4142, 15221, 23062, 25997, 23401, + 15827, 4886, -7093, -17565, -24300, -25865, -21928, -13326, + -1889, 9949, 19672, 25209, 25383, 20158, 10644, -1134, + -12671, -21512, -25777, -24558, -18115, -7818, 4142, 15221, + 23062, 25997, 23401, 15827, 4886, -7093, -17565, -24300, + -25865, -21928, -13325, -1888, 9949, 19672, 25209, 25383, + 20158, 10644, -1134, -12671, -21512, -25777, -24558, -18115, + -7818, 4142, 15221, 23062, 25997, 23401, 15827, 4886, + -7093, -17565, -24300, -25865, -21928, -13325, -1888, 9949, + 19672, 25209, 25383, 20158, 10644, -1134, -12671, -21512, + -25777, -24558, -18115, -7818, 4142, 15221, 23062, 25997, + 23401, 15827, 4886, -7094, -17565, -24300, -25865, -21928, + -13325, -1888, 9949, 19672, 25209, 25383, 20157, 10644, + -1134, -12671, -21512, -25777, -24558, -18115, -7818, 4142, + 15221, 23062, 25997, 23401, 15827, 4886, -7094, -17565, + -24300, -25865, -21928, -13325, -1888, 9950, 19672, 25209, + 25383, 20157, 10643, -1134, -12671, -21512, -25777, -24558, + -18115, -7818, 4142, 15221, 23062, 25997, 23401, 15827, + 4886, -7094, -17565, -24300, -25865, -21928, -13325, -1888}, + { +// Carrier 10 Phase 2 + 18384, 24680, 25725, 21297, 12339, 756, -10988, -20394, + -25462, -25114, -19422, -9599, 2266, 13649, 22129, 25901, + 24163, 17284, 6729, -5257, -16126, -23564, -25989, -22885, + -14912, -3768, 8178, 18384, 24680, 25725, 21297, 12339, + 756, -10988, -20394, -25462, -25114, -19422, -9599, 2266, + 13649, 22129, 25901, 24163, 17284, 6729, -5257, -16126, + -23564, -25988, -22885, -14912, -3768, 8178, 18384, 24680, + 25725, 21297, 12339, 756, -10988, -20394, -25462, -25114, + -19422, -9599, 2266, 13649, 22129, 25901, 24163, 17284, + 6729, -5257, -16126, -23564, -25988, -22885, -14912, -3768, + 8178, 18384, 24680, 25725, 21297, 12339, 756, -10988, + -20394, -25462, -25114, -19422, -9599, 2266, 13649, 22129, + 25901, 24162, 17284, 6729, -5257, -16126, -23564, -25988, + -22885, -14912, -3768, 8178, 18384, 24680, 25725, 21297, + 12339, 756, -10988, -20394, -25462, -25114, -19422, -9599, + 2266, 13649, 22129, 25901, 24162, 17284, 6729, -5257, + -16126, -23564, -25988, -22885, -14912, -3768, 8178, 18384, + 24680, 25725, 21297, 12339, 756, -10988, -20394, -25462, + -25114, -19422, -9599, 2266, 13649, 22129, 25901, 24162, + 17284, 6729, -5257, -16126, -23564, -25988, -22885, -14912, + -3768, 8178, 18384, 24680, 25725, 21297, 12339, 755, + -10988, -20394, -25462, -25114, -19422, -9599, 2266, 13649, + 22129, 25901, 24162, 17284, 6729, -5257, -16126, -23564, + -25988, -22885, -14912, -3767, 8178, 18384, 24680, 25725, + 21297, 12339, 755, -10988, -20394, -25462, -25114, -19422, + -9599, 2266, 13649, 22129, 25901, 24162, 17284, 6729, + -5257, -16126, -23564, -25988, -22885, -14912, -3767, 8178}, + { +// Carrier 10 Phase 3 + 24020, 25931, 22325, 13969, 2642, -9246, -19169, -25013, + -25536, -20627, -11329, 378, 12005, 21078, 25667, 24796, + 18650, 8536, -3393, -14601, -22703, -25975, -23721, -16421, + -5627, 6363, 17000, 24020, 25931, 22325, 13969, 2642, + -9246, -19169, -25013, -25536, -20627, -11329, 378, 12005, + 21078, 25667, 24796, 18650, 8536, -3393, -14601, -22703, + -25975, -23721, -16420, -5627, 6363, 17000, 24020, 25931, + 22325, 13969, 2642, -9246, -19169, -25013, -25536, -20627, + -11329, 378, 12005, 21078, 25667, 24796, 18650, 8536, + -3393, -14601, -22703, -25975, -23721, -16420, -5627, 6363, + 17000, 24020, 25931, 22325, 13969, 2642, -9247, -19169, + -25013, -25536, -20627, -11329, 378, 12005, 21078, 25667, + 24796, 18650, 8536, -3393, -14601, -22703, -25975, -23721, + -16420, -5627, 6363, 17000, 24020, 25931, 22325, 13969, + 2642, -9247, -19169, -25013, -25536, -20627, -11329, 378, + 12005, 21078, 25667, 24796, 18650, 8536, -3393, -14601, + -22703, -25975, -23721, -16420, -5627, 6363, 17000, 24020, + 25931, 22325, 13969, 2642, -9247, -19169, -25013, -25536, + -20627, -11329, 378, 12005, 21078, 25667, 24796, 18650, + 8536, -3393, -14601, -22703, -25975, -23721, -16420, -5627, + 6363, 17000, 24020, 25931, 22325, 13969, 2642, -9247, + -19169, -25013, -25536, -20627, -11329, 378, 12005, 21078, + 25667, 24796, 18650, 8536, -3393, -14601, -22703, -25975, + -23721, -16420, -5627, 6363, 17000, 24020, 25931, 22325, + 13969, 2642, -9247, -19169, -25013, -25536, -20627, -11329, + 378, 12005, 21078, 25668, 24796, 18650, 8535, -3393, + -14601, -22703, -25975, -23721, -16420, -5627, 6363, 17000}, + { +// Carrier 10 Phase 4 + 26000, 23234, 15526, 4514, -7456, -17842, -24432, -25824, + -21722, -12999, -1511, 10298, 19917, 25299, 25299, 19917, + 10298, -1511, -13000, -21722, -25824, -24432, -17842, -7456, + 4514, 15526, 23234, 26000, 23234, 15526, 4514, -7456, + -17842, -24432, -25824, -21722, -12999, -1511, 10298, 19917, + 25299, 25299, 19917, 10298, -1511, -13000, -21722, -25824, + -24431, -17842, -7456, 4514, 15526, 23234, 26000, 23234, + 15526, 4514, -7456, -17842, -24432, -25824, -21722, -12999, + -1511, 10298, 19917, 25299, 25299, 19917, 10297, -1511, + -13000, -21722, -25824, -24431, -17842, -7456, 4514, 15526, + 23234, 26000, 23234, 15526, 4514, -7456, -17842, -24432, + -25824, -21722, -12999, -1511, 10298, 19917, 25299, 25299, + 19917, 10297, -1511, -13000, -21722, -25824, -24431, -17842, + -7456, 4514, 15526, 23234, 26000, 23234, 15526, 4514, + -7457, -17842, -24432, -25824, -21722, -12999, -1511, 10298, + 19917, 25299, 25299, 19917, 10297, -1511, -13000, -21722, + -25824, -24431, -17842, -7456, 4515, 15526, 23234, 26000, + 23234, 15525, 4514, -7457, -17842, -24432, -25824, -21722, + -12999, -1511, 10298, 19917, 25299, 25299, 19917, 10297, + -1511, -13000, -21722, -25824, -24431, -17842, -7456, 4515, + 15526, 23234, 26000, 23234, 15525, 4514, -7457, -17842, + -24432, -25824, -21722, -12999, -1511, 10298, 19917, 25299, + 25299, 19917, 10297, -1512, -13000, -21722, -25824, -24431, + -17842, -7456, 4515, 15526, 23234, 26000, 23234, 15525, + 4514, -7457, -17842, -24432, -25824, -21722, -12999, -1511, + 10298, 19917, 25299, 25299, 19916, 10297, -1512, -13000, + -21722, -25824, -24431, -17842, -7456, 4515, 15526, 23234}, + { +// Carrier 10 Phase 5 + 24020, 17000, 6363, -5627, -16421, -23721, -25975, -22703, + -14601, -3393, 8536, 18650, 24796, 25667, 21078, 12005, + 378, -11329, -20627, -25536, -25013, -19169, -9246, 2642, + 13969, 22325, 25931, 24020, 17000, 6363, -5627, -16421, + -23721, -25975, -22703, -14601, -3393, 8536, 18650, 24796, + 25667, 21078, 12005, 378, -11329, -20627, -25536, -25013, + -19169, -9246, 2642, 13969, 22325, 25931, 24020, 17000, + 6363, -5627, -16421, -23721, -25975, -22703, -14601, -3393, + 8536, 18650, 24796, 25667, 21078, 12005, 378, -11329, + -20627, -25536, -25013, -19169, -9246, 2642, 13969, 22325, + 25931, 24020, 17000, 6363, -5627, -16421, -23721, -25975, + -22703, -14601, -3393, 8536, 18650, 24796, 25667, 21078, + 12005, 378, -11329, -20627, -25536, -25013, -19169, -9246, + 2642, 13969, 22325, 25931, 24020, 17000, 6363, -5627, + -16421, -23721, -25975, -22703, -14601, -3393, 8536, 18650, + 24796, 25667, 21078, 12005, 377, -11329, -20627, -25536, + -25013, -19169, -9246, 2642, 13969, 22325, 25931, 24020, + 17000, 6363, -5627, -16421, -23721, -25975, -22703, -14601, + -3393, 8536, 18650, 24796, 25667, 21078, 12005, 377, + -11329, -20627, -25536, -25013, -19169, -9246, 2642, 13969, + 22325, 25931, 24020, 17000, 6363, -5627, -16421, -23721, + -25975, -22703, -14601, -3393, 8536, 18650, 24796, 25667, + 21078, 12005, 377, -11329, -20627, -25536, -25013, -19169, + -9246, 2642, 13970, 22325, 25931, 24020, 17000, 6363, + -5627, -16421, -23721, -25975, -22703, -14601, -3393, 8536, + 18650, 24796, 25667, 21078, 12005, 377, -11329, -20627, + -25536, -25013, -19169, -9246, 2642, 13970, 22325, 25931}, + { +// Carrier 10 Phase 6 + 18384, 8178, -3768, -14912, -22885, -25989, -23563, -16126, + -5257, 6729, 17284, 24163, 25901, 22129, 13649, 2266, + -9599, -19422, -25114, -25462, -20394, -10988, 756, 12339, + 21297, 25725, 24680, 18384, 8178, -3768, -14913, -22885, + -25989, -23563, -16126, -5257, 6729, 17284, 24163, 25901, + 22129, 13649, 2265, -9599, -19422, -25114, -25462, -20394, + -10988, 756, 12339, 21297, 25725, 24680, 18384, 8178, + -3768, -14913, -22885, -25989, -23563, -16126, -5257, 6729, + 17284, 24163, 25901, 22128, 13649, 2265, -9599, -19422, + -25114, -25462, -20394, -10987, 756, 12339, 21298, 25725, + 24680, 18384, 8178, -3768, -14913, -22885, -25989, -23563, + -16126, -5257, 6729, 17284, 24163, 25901, 22128, 13649, + 2265, -9599, -19422, -25114, -25462, -20394, -10987, 756, + 12339, 21298, 25725, 24680, 18384, 8178, -3768, -14913, + -22885, -25989, -23563, -16125, -5257, 6729, 17284, 24163, + 25901, 22128, 13649, 2265, -9599, -19422, -25114, -25462, + -20394, -10987, 756, 12339, 21298, 25725, 24680, 18384, + 8177, -3768, -14913, -22885, -25989, -23563, -16125, -5257, + 6729, 17284, 24163, 25901, 22128, 13649, 2265, -9599, + -19422, -25114, -25462, -20394, -10987, 756, 12339, 21298, + 25725, 24680, 18384, 8177, -3768, -14913, -22885, -25989, + -23563, -16125, -5257, 6729, 17284, 24163, 25901, 22128, + 13649, 2265, -9599, -19422, -25114, -25462, -20394, -10987, + 756, 12339, 21298, 25725, 24680, 18384, 8177, -3768, + -14913, -22885, -25989, -23563, -16125, -5257, 6729, 17284, + 24163, 25901, 22128, 13649, 2265, -9599, -19422, -25114, + -25462, -20394, -10987, 756, 12339, 21298, 25725, 24680}, + { +// Carrier 10 Phase 7 + 9949, -1889, -13326, -21928, -25865, -24300, -17565, -7093, + 4886, 15827, 23401, 25997, 23062, 15221, 4141, -7818, + -18115, -24558, -25777, -21512, -12671, -1134, 10644, 20158, + 25383, 25209, 19671, 9949, -1889, -13326, -21928, -25865, + -24300, -17565, -7093, 4886, 15827, 23401, 25997, 23062, + 15221, 4141, -7818, -18115, -24558, -25777, -21512, -12671, + -1134, 10644, 20158, 25383, 25209, 19671, 9949, -1889, + -13326, -21928, -25865, -24300, -17565, -7093, 4886, 15827, + 23401, 25997, 23062, 15221, 4141, -7818, -18115, -24558, + -25777, -21512, -12671, -1134, 10644, 20158, 25383, 25209, + 19671, 9949, -1889, -13326, -21928, -25865, -24300, -17565, + -7093, 4886, 15827, 23401, 25997, 23062, 15221, 4141, + -7818, -18115, -24558, -25777, -21512, -12671, -1133, 10644, + 20158, 25383, 25209, 19671, 9949, -1889, -13326, -21928, + -25865, -24300, -17565, -7093, 4886, 15827, 23401, 25997, + 23062, 15221, 4141, -7818, -18115, -24558, -25777, -21512, + -12670, -1133, 10644, 20158, 25383, 25209, 19671, 9949, + -1889, -13326, -21928, -25865, -24300, -17565, -7093, 4886, + 15827, 23401, 25997, 23062, 15221, 4141, -7818, -18115, + -24558, -25777, -21512, -12670, -1133, 10644, 20158, 25383, + 25209, 19671, 9949, -1889, -13326, -21928, -25865, -24300, + -17565, -7093, 4887, 15827, 23401, 25997, 23062, 15220, + 4141, -7818, -18115, -24558, -25777, -21512, -12670, -1133, + 10644, 20158, 25383, 25209, 19671, 9949, -1889, -13326, + -21928, -25865, -24299, -17565, -7093, 4887, 15828, 23401, + 25997, 23062, 15220, 4141, -7818, -18115, -24558, -25777, + -21512, -12670, -1133, 10644, 20158, 25383, 25209, 19671}, + },{{ + +// Carrier 11 Phase 0 + 0, 12339, 21722, 25901, 23873, 16126, 4514, -8178, + -18911, -25114, -25299, -19422, -8892, 3768, 15526, 23563, + 25956, 22129, 13000, 756, -11668, -21297, -25824, -24163, + -16712, -5257, 7456, 18384, 24907, 25462, 19917, 9599, + -3018, -14912, -23234, -25989, -22516, -13649, -1511, 10987, + 20855, 25725, 24432, 17284, 5996, -6729, -17842, -24680, + -25605, -20394, -10298, 2265, 14287, 22885, 26000, 22885, + 14287, 2266, -10297, -20394, -25604, -24680, -17842, -6729, + 5995, 17284, 24431, 25725, 20855, 10988, -1511, -13649, + -22516, -25988, -23234, -14913, -3018, 9599, 19917, 25462, + 24907, 18384, 7457, -5257, -16712, -24162, -25824, -21298, + -11668, 756, 12999, 22128, 25956, 23564, 15526, 3768, + -8892, -19422, -25299, -25114, -18911, -8178, 4514, 16125, + 23873, 25901, 21722, 12339, 0, -12339, -21722, -25901, + -23873, -16126, -4515, 8177, 18911, 25114, 25299, 19422, + 8892, -3767, -15525, -23563, -25956, -22129, -13000, -756, + 11668, 21297, 25824, 24163, 16712, 5257, -7456, -18384, + -24907, -25462, -19917, -9599, 3018, 14912, 23234, 25989, + 22516, 13649, 1512, -10987, -20855, -25725, -24432, -17284, + -5996, 6728, 17842, 24680, 25605, 20395, 10298, -2265, + -14286, -22885, -26000, -22885, -14287, -2266, 10297, 20394, + 25604, 24680, 17842, 6729, -5995, -17284, -24431, -25725, + -20855, -10988, 1511, 13649, 22516, 25988, 23234, 14913, + 3018, -9598, -19916, -25462, -24907, -18385, -7457, 5257, + 16712, 24162, 25824, 21298, 11669, -755, -12999, -22128, + -25955, -23564, -15526, -3768, 8892, 19422, 25299, 25114, + 18912, 8178, -4514, -16125, -23873, -25901, -21722, -12340}, + { +// Carrier 11 Phase 1 + 9949, 20158, 25536, 24796, 18115, 7093, -5627, -17000, + -24300, -25777, -21078, -11329, 1134, 13326, 22325, 25975, + 23401, 15221, 3393, -9246, -19671, -25383, -25013, -18650, + -7818, 4886, 16420, 24020, 25865, 21512, 12005, -378, + -12671, -21928, -25931, -23721, -15827, -4142, 8536, 19169, + 25209, 25209, 19169, 8536, -4141, -15827, -23721, -25931, + -21928, -12671, -378, 12005, 21512, 25865, 24020, 16421, + 4886, -7818, -18650, -25013, -25383, -19672, -9247, 3393, + 15221, 23401, 25975, 22325, 13326, 1134, -11329, -21078, + -25777, -24300, -17000, -5627, 7093, 18115, 24796, 25536, + 20158, 9949, -2642, -14601, -23062, -25997, -22703, -13969, + -1889, 10644, 20627, 25667, 24558, 17565, 6363, -6363, + -17565, -24558, -25667, -20627, -10644, 1888, 13969, 22703, + 25997, 23062, 14601, 2642, -9949, -20157, -25536, -24796, + -18115, -7094, 5627, 17000, 24300, 25777, 21078, 11329, + -1133, -13325, -22325, -25975, -23401, -15221, -3393, 9246, + 19671, 25383, 25013, 18650, 7818, -4886, -16420, -24020, + -25865, -21512, -12005, 377, 12670, 21928, 25931, 23721, + 15828, 4142, -8535, -19169, -25209, -25209, -19169, -8536, + 4141, 15827, 23721, 25931, 21928, 12671, 378, -12005, + -21512, -25865, -24020, -16421, -4887, 7818, 18649, 25013, + 25383, 19672, 9247, -3393, -15220, -23401, -25975, -22325, + -13326, -1134, 11329, 21078, 25777, 24300, 17000, 5627, + -7093, -18115, -24796, -25536, -20158, -9950, 2642, 14601, + 23062, 25997, 22703, 13970, 1889, -10643, -20626, -25667, + -24558, -17565, -6363, 6362, 17565, 24558, 25668, 20627, + 10644, -1888, -13969, -22703, -25997, -23062, -14602, -2642}, + { +// Carrier 11 Phase 2 + 18384, 24907, 25462, 19917, 9599, -3018, -14912, -23234, + -25989, -22516, -13649, -1511, 10988, 20855, 25725, 24432, + 17284, 5996, -6729, -17842, -24680, -25605, -20394, -10298, + 2266, 14287, 22885, 26000, 22885, 14287, 2266, -10298, + -20394, -25604, -24680, -17842, -6729, 5995, 17284, 24431, + 25725, 20855, 10988, -1511, -13649, -22516, -25988, -23234, + -14913, -3018, 9599, 19917, 25462, 24907, 18384, 7456, + -5257, -16712, -24162, -25824, -21298, -11668, 756, 12999, + 22128, 25956, 23564, 15526, 3768, -8892, -19422, -25299, + -25114, -18911, -8178, 4514, 16126, 23873, 25901, 21722, + 12339, 0, -12339, -21722, -25901, -23873, -16126, -4515, + 8177, 18911, 25114, 25299, 19422, 8892, -3768, -15525, + -23563, -25956, -22129, -13000, -756, 11668, 21297, 25824, + 24163, 16712, 5257, -7456, -18384, -24907, -25462, -19917, + -9599, 3018, 14912, 23234, 25989, 22516, 13649, 1512, + -10987, -20855, -25725, -24432, -17284, -5996, 6729, 17842, + 24680, 25605, 20394, 10298, -2265, -14287, -22885, -26000, + -22885, -14287, -2266, 10297, 20394, 25604, 24680, 17842, + 6729, -5995, -17284, -24431, -25725, -20855, -10988, 1511, + 13649, 22516, 25988, 23234, 14913, 3018, -9599, -19916, + -25462, -24907, -18385, -7457, 5257, 16712, 24162, 25824, + 21298, 11669, -755, -12999, -22128, -25955, -23564, -15526, + -3768, 8892, 19422, 25299, 25114, 18911, 8178, -4514, + -16125, -23873, -25901, -21722, -12339, 0, 12339, 21722, + 25901, 23873, 16126, 4515, -8177, -18911, -25113, -25299, + -19422, -8892, 3767, 15525, 23563, 25956, 22129, 13000, + 756, -11668, -21297, -25824, -24163, -16712, -5258, 7456}, + { +// Carrier 11 Phase 3 + 24020, 25865, 21512, 12005, -378, -12671, -21928, -25931, + -23721, -15827, -4142, 8536, 19169, 25209, 25209, 19169, + 8536, -4141, -15827, -23721, -25931, -21928, -12671, -378, + 12005, 21512, 25865, 24020, 16421, 4886, -7818, -18650, + -25013, -25383, -19672, -9246, 3393, 15221, 23401, 25975, + 22325, 13326, 1134, -11329, -21078, -25777, -24300, -17000, + -5627, 7093, 18115, 24796, 25536, 20158, 9949, -2642, + -14601, -23062, -25997, -22703, -13969, -1889, 10644, 20627, + 25667, 24558, 17565, 6363, -6363, -17565, -24558, -25667, + -20627, -10644, 1888, 13969, 22703, 25997, 23062, 14601, + 2642, -9949, -20158, -25536, -24796, -18115, -7094, 5627, + 17000, 24300, 25777, 21078, 11329, -1133, -13325, -22325, + -25975, -23401, -15221, -3393, 9246, 19671, 25383, 25013, + 18650, 7818, -4886, -16420, -24020, -25865, -21512, -12005, + 377, 12670, 21928, 25931, 23721, 15827, 4142, -8536, + -19169, -25209, -25209, -19169, -8536, 4141, 15827, 23721, + 25931, 21928, 12671, 378, -12005, -21512, -25865, -24020, + -16421, -4887, 7818, 18650, 25013, 25383, 19672, 9247, + -3393, -15220, -23401, -25975, -22325, -13326, -1134, 11329, + 21078, 25777, 24300, 17000, 5627, -7093, -18115, -24796, + -25536, -20158, -9950, 2642, 14601, 23062, 25997, 22703, + 13970, 1889, -10643, -20626, -25667, -24558, -17565, -6363, + 6362, 17565, 24558, 25668, 20627, 10644, -1888, -13969, + -22703, -25997, -23062, -14601, -2642, 9949, 20157, 25536, + 24796, 18115, 7094, -5627, -17000, -24299, -25777, -21079, + -11330, 1133, 13325, 22324, 25975, 23401, 15221, 3394, + -9246, -19671, -25383, -25013, -18650, -7818, 4886, 16420}, + { +// Carrier 11 Phase 4 + 26000, 22885, 14287, 2266, -10298, -20394, -25605, -24680, + -17842, -6729, 5995, 17284, 24432, 25725, 20855, 10988, + -1511, -13649, -22516, -25989, -23234, -14913, -3018, 9599, + 19917, 25462, 24907, 18384, 7456, -5257, -16712, -24163, + -25824, -21297, -11668, 756, 12999, 22128, 25956, 23564, + 15526, 3768, -8892, -19422, -25299, -25114, -18911, -8178, + 4514, 16126, 23873, 25901, 21722, 12339, 0, -12339, + -21722, -25901, -23873, -16126, -4514, 8178, 18911, 25114, + 25299, 19422, 8892, -3768, -15526, -23563, -25956, -22129, + -13000, -756, 11668, 21297, 25824, 24163, 16712, 5257, + -7456, -18384, -24907, -25462, -19917, -9599, 3018, 14912, + 23234, 25989, 22516, 13649, 1511, -10987, -20855, -25725, + -24432, -17284, -5996, 6729, 17842, 24680, 25605, 20394, + 10298, -2265, -14287, -22885, -26000, -22885, -14287, -2266, + 10297, 20394, 25604, 24680, 17842, 6729, -5995, -17284, + -24431, -25725, -20855, -10988, 1511, 13649, 22516, 25988, + 23234, 14913, 3018, -9599, -19916, -25462, -24907, -18384, + -7457, 5257, 16712, 24162, 25824, 21298, 11669, -755, + -12999, -22128, -25955, -23564, -15526, -3768, 8892, 19422, + 25299, 25114, 18911, 8178, -4514, -16125, -23873, -25901, + -21722, -12339, 0, 12339, 21722, 25901, 23873, 16126, + 4515, -8177, -18911, -25113, -25299, -19422, -8892, 3767, + 15525, 23563, 25956, 22129, 13000, 756, -11668, -21297, + -25824, -24163, -16712, -5258, 7456, 18384, 24907, 25462, + 19917, 9599, -3018, -14912, -23234, -25989, -22516, -13649, + -1512, 10987, 20854, 25725, 24432, 17285, 5996, -6728, + -17841, -24680, -25605, -20395, -10298, 2265, 14286, 22885}, + { +// Carrier 11 Phase 5 + 24020, 16421, 4886, -7818, -18650, -25013, -25383, -19672, + -9246, 3393, 15221, 23401, 25975, 22325, 13326, 1134, + -11329, -21078, -25777, -24300, -17000, -5627, 7093, 18115, + 24796, 25536, 20158, 9949, -2642, -14601, -23062, -25997, + -22703, -13969, -1889, 10644, 20627, 25667, 24558, 17565, + 6363, -6363, -17565, -24558, -25667, -20627, -10644, 1888, + 13969, 22703, 25997, 23062, 14601, 2642, -9949, -20158, + -25536, -24796, -18115, -7093, 5627, 17000, 24300, 25777, + 21078, 11329, -1133, -13325, -22325, -25975, -23401, -15221, + -3393, 9246, 19671, 25383, 25013, 18650, 7818, -4886, + -16420, -24020, -25865, -21512, -12005, 377, 12670, 21928, + 25931, 23721, 15827, 4142, -8536, -19169, -25209, -25209, + -19169, -8536, 4141, 15827, 23721, 25931, 21928, 12671, + 378, -12005, -21512, -25865, -24020, -16421, -4887, 7818, + 18650, 25013, 25383, 19672, 9247, -3393, -15220, -23401, + -25975, -22325, -13326, -1134, 11329, 21078, 25777, 24300, + 17000, 5627, -7093, -18115, -24796, -25536, -20158, -9950, + 2642, 14601, 23062, 25997, 22703, 13970, 1889, -10643, + -20626, -25667, -24558, -17565, -6363, 6362, 17565, 24558, + 25668, 20627, 10644, -1888, -13969, -22703, -25997, -23062, + -14601, -2642, 9949, 20157, 25536, 24796, 18115, 7094, + -5627, -17000, -24299, -25777, -21079, -11329, 1133, 13325, + 22325, 25975, 23401, 15221, 3394, -9246, -19671, -25383, + -25013, -18650, -7818, 4886, 16420, 24020, 25865, 21512, + 12005, -377, -12670, -21927, -25931, -23721, -15828, -4142, + 8535, 19168, 25209, 25209, 19169, 8536, -4141, -15827, + -23721, -25931, -21928, -12671, -378, 12005, 21512, 25865}, + { +// Carrier 11 Phase 6 + 18384, 7456, -5257, -16712, -24163, -25824, -21297, -11668, + 756, 12999, 22129, 25956, 23564, 15526, 3768, -8892, + -19422, -25299, -25114, -18911, -8178, 4514, 16126, 23873, + 25901, 21722, 12339, 0, -12339, -21722, -25901, -23873, + -16126, -4514, 8178, 18911, 25114, 25299, 19422, 8892, + -3768, -15526, -23563, -25956, -22129, -13000, -756, 11668, + 21297, 25824, 24163, 16712, 5257, -7456, -18384, -24907, + -25462, -19917, -9599, 3018, 14912, 23234, 25989, 22516, + 13649, 1511, -10987, -20855, -25725, -24432, -17284, -5996, + 6729, 17842, 24680, 25605, 20394, 10298, -2265, -14287, + -22885, -26000, -22885, -14287, -2266, 10297, 20394, 25604, + 24680, 17842, 6729, -5995, -17284, -24431, -25725, -20855, + -10988, 1511, 13649, 22516, 25988, 23234, 14913, 3018, + -9599, -19917, -25462, -24907, -18384, -7457, 5257, 16712, + 24162, 25824, 21298, 11668, -755, -12999, -22128, -25955, + -23564, -15526, -3768, 8892, 19422, 25299, 25114, 18911, + 8178, -4514, -16125, -23873, -25901, -21722, -12339, 0, + 12339, 21722, 25901, 23873, 16126, 4515, -8177, -18911, + -25113, -25299, -19422, -8892, 3767, 15525, 23563, 25956, + 22129, 13000, 756, -11668, -21297, -25824, -24163, -16712, + -5258, 7456, 18384, 24907, 25462, 19917, 9599, -3018, + -14912, -23234, -25989, -22516, -13649, -1512, 10987, 20854, + 25725, 24432, 17284, 5996, -6728, -17841, -24680, -25605, + -20395, -10298, 2265, 14286, 22885, 26000, 22885, 14287, + 2266, -10297, -20394, -25604, -24680, -17842, -6729, 5995, + 17284, 24431, 25725, 20855, 10988, -1511, -13649, -22516, + -25988, -23234, -14913, -3018, 9598, 19916, 25462, 24907}, + { +// Carrier 11 Phase 7 + 9949, -2642, -14601, -23062, -25997, -22703, -13969, -1889, + 10644, 20627, 25667, 24558, 17565, 6363, -6363, -17565, + -24558, -25667, -20627, -10644, 1889, 13969, 22703, 25997, + 23062, 14601, 2642, -9949, -20158, -25536, -24796, -18115, + -7093, 5627, 17000, 24300, 25777, 21078, 11329, -1134, + -13326, -22325, -25975, -23401, -15221, -3393, 9246, 19671, + 25383, 25013, 18650, 7818, -4886, -16420, -24020, -25865, + -21512, -12005, 378, 12671, 21928, 25931, 23721, 15827, + 4142, -8536, -19169, -25209, -25209, -19169, -8536, 4141, + 15827, 23721, 25931, 21928, 12671, 378, -12005, -21512, + -25865, -24020, -16421, -4886, 7818, 18650, 25013, 25383, + 19672, 9247, -3393, -15221, -23401, -25975, -22325, -13326, + -1134, 11329, 21078, 25777, 24300, 17000, 5627, -7093, + -18115, -24796, -25536, -20158, -9949, 2642, 14601, 23062, + 25997, 22703, 13969, 1889, -10643, -20627, -25667, -24558, + -17565, -6363, 6363, 17565, 24558, 25667, 20627, 10644, + -1888, -13969, -22703, -25997, -23062, -14601, -2642, 9949, + 20157, 25536, 24796, 18115, 7094, -5627, -17000, -24299, + -25777, -21078, -11329, 1133, 13325, 22325, 25975, 23401, + 15221, 3393, -9246, -19671, -25383, -25013, -18650, -7818, + 4886, 16420, 24020, 25865, 21512, 12005, -377, -12670, + -21927, -25931, -23721, -15828, -4142, 8535, 19168, 25209, + 25209, 19169, 8536, -4141, -15827, -23721, -25931, -21928, + -12671, -378, 12005, 21512, 25865, 24021, 16421, 4887, + -7817, -18649, -25013, -25383, -19672, -9247, 3393, 15220, + 23401, 25975, 22325, 13326, 1134, -11329, -21078, -25777, + -24300, -17000, -5627, 7093, 18115, 24796, 25536, 20158}, + },{{ + +// Carrier 12 Phase 0 + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000}, + { +// Carrier 12 Phase 1 + 9949, 20627, 25777, 24020, 15827, 3393, -9949, -20627, + -25777, -24020, -15827, -3393, 9949, 20627, 25777, 24020, + 15827, 3393, -9949, -20627, -25777, -24020, -15827, -3393, + 9949, 20627, 25777, 24020, 15827, 3393, -9949, -20627, + -25777, -24020, -15827, -3393, 9949, 20627, 25777, 24020, + 15827, 3393, -9949, -20627, -25777, -24020, -15827, -3393, + 9949, 20627, 25777, 24020, 15827, 3393, -9949, -20627, + -25777, -24020, -15827, -3393, 9949, 20627, 25777, 24020, + 15827, 3393, -9949, -20627, -25777, -24020, -15827, -3393, + 9949, 20627, 25777, 24020, 15827, 3393, -9949, -20627, + -25777, -24020, -15827, -3393, 9949, 20627, 25777, 24020, + 15827, 3393, -9949, -20627, -25777, -24020, -15827, -3393, + 9949, 20627, 25777, 24020, 15827, 3393, -9949, -20627, + -25777, -24020, -15827, -3393, 9949, 20627, 25777, 24020, + 15827, 3393, -9949, -20627, -25777, -24020, -15827, -3393, + 9949, 20627, 25777, 24020, 15827, 3393, -9949, -20627, + -25777, -24020, -15827, -3393, 9949, 20627, 25777, 24020, + 15827, 3393, -9949, -20627, -25777, -24020, -15827, -3393, + 9949, 20627, 25777, 24020, 15827, 3393, -9949, -20627, + -25777, -24020, -15827, -3393, 9949, 20627, 25777, 24020, + 15827, 3393, -9949, -20627, -25777, -24020, -15827, -3393, + 9949, 20627, 25777, 24020, 15827, 3393, -9949, -20627, + -25777, -24020, -15827, -3393, 9949, 20627, 25777, 24020, + 15827, 3393, -9949, -20627, -25777, -24020, -15827, -3393, + 9949, 20627, 25777, 24020, 15827, 3393, -9949, -20627, + -25777, -24020, -15827, -3393, 9949, 20627, 25777, 24020, + 15827, 3393, -9949, -20627, -25777, -24020, -15827, -3393}, + { +// Carrier 12 Phase 2 + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729}, + { +// Carrier 12 Phase 3 + 24020, 25777, 20627, 9949, -3393, -15827, -24020, -25777, + -20627, -9949, 3393, 15827, 24020, 25777, 20627, 9949, + -3393, -15827, -24020, -25777, -20627, -9949, 3393, 15827, + 24020, 25777, 20627, 9949, -3393, -15827, -24020, -25777, + -20627, -9949, 3393, 15827, 24020, 25777, 20627, 9949, + -3393, -15827, -24020, -25777, -20627, -9949, 3393, 15827, + 24020, 25777, 20627, 9949, -3393, -15827, -24020, -25777, + -20627, -9949, 3393, 15827, 24020, 25777, 20627, 9949, + -3393, -15827, -24020, -25777, -20627, -9949, 3393, 15827, + 24020, 25777, 20627, 9949, -3393, -15827, -24020, -25777, + -20627, -9949, 3393, 15827, 24020, 25777, 20627, 9949, + -3393, -15827, -24020, -25777, -20627, -9949, 3393, 15827, + 24020, 25777, 20627, 9949, -3393, -15827, -24020, -25777, + -20627, -9949, 3393, 15827, 24020, 25777, 20627, 9949, + -3393, -15827, -24020, -25777, -20627, -9949, 3393, 15827, + 24020, 25777, 20627, 9949, -3393, -15827, -24020, -25777, + -20627, -9949, 3393, 15827, 24020, 25777, 20627, 9949, + -3393, -15827, -24020, -25777, -20627, -9949, 3393, 15827, + 24020, 25777, 20627, 9949, -3393, -15827, -24020, -25777, + -20627, -9949, 3393, 15827, 24020, 25777, 20627, 9949, + -3393, -15827, -24020, -25777, -20627, -9949, 3393, 15827, + 24020, 25777, 20627, 9949, -3393, -15827, -24020, -25777, + -20627, -9949, 3393, 15827, 24020, 25777, 20627, 9949, + -3393, -15827, -24020, -25777, -20627, -9949, 3393, 15827, + 24020, 25777, 20627, 9949, -3393, -15827, -24020, -25777, + -20627, -9949, 3393, 15827, 24020, 25777, 20627, 9949, + -3393, -15827, -24020, -25777, -20627, -9949, 3393, 15827}, + { +// Carrier 12 Phase 4 + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516}, + { +// Carrier 12 Phase 5 + 24020, 15827, 3393, -9949, -20627, -25777, -24020, -15827, + -3393, 9949, 20627, 25777, 24020, 15827, 3393, -9949, + -20627, -25777, -24020, -15827, -3393, 9949, 20627, 25777, + 24020, 15827, 3393, -9949, -20627, -25777, -24020, -15827, + -3393, 9949, 20627, 25777, 24020, 15827, 3393, -9949, + -20627, -25777, -24020, -15827, -3393, 9949, 20627, 25777, + 24020, 15827, 3393, -9949, -20627, -25777, -24020, -15827, + -3393, 9949, 20627, 25777, 24020, 15827, 3393, -9949, + -20627, -25777, -24020, -15827, -3393, 9949, 20627, 25777, + 24020, 15827, 3393, -9949, -20627, -25777, -24020, -15827, + -3393, 9949, 20627, 25777, 24020, 15827, 3393, -9949, + -20627, -25777, -24020, -15827, -3393, 9949, 20627, 25777, + 24020, 15827, 3393, -9949, -20627, -25777, -24020, -15827, + -3393, 9949, 20627, 25777, 24020, 15827, 3393, -9949, + -20627, -25777, -24020, -15827, -3393, 9949, 20627, 25777, + 24020, 15827, 3393, -9949, -20627, -25777, -24020, -15827, + -3393, 9949, 20627, 25777, 24020, 15827, 3393, -9949, + -20627, -25777, -24020, -15827, -3393, 9949, 20627, 25777, + 24020, 15827, 3393, -9949, -20627, -25777, -24020, -15827, + -3393, 9949, 20627, 25777, 24020, 15827, 3393, -9949, + -20627, -25777, -24020, -15827, -3393, 9949, 20627, 25777, + 24020, 15827, 3393, -9949, -20627, -25777, -24020, -15827, + -3393, 9949, 20627, 25777, 24020, 15827, 3393, -9949, + -20627, -25777, -24020, -15827, -3393, 9949, 20627, 25777, + 24020, 15827, 3393, -9949, -20627, -25777, -24020, -15827, + -3393, 9949, 20627, 25777, 24020, 15827, 3393, -9949, + -20627, -25777, -24020, -15827, -3393, 9949, 20627, 25777}, + { +// Carrier 12 Phase 6 + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114}, + { +// Carrier 12 Phase 7 + 9949, -3393, -15827, -24020, -25777, -20627, -9949, 3393, + 15827, 24020, 25777, 20627, 9949, -3393, -15827, -24020, + -25777, -20627, -9949, 3393, 15827, 24020, 25777, 20627, + 9949, -3393, -15827, -24020, -25777, -20627, -9949, 3393, + 15827, 24020, 25777, 20627, 9949, -3393, -15827, -24020, + -25777, -20627, -9949, 3393, 15827, 24020, 25777, 20627, + 9949, -3393, -15827, -24020, -25777, -20627, -9949, 3393, + 15827, 24020, 25777, 20627, 9949, -3393, -15827, -24020, + -25777, -20627, -9949, 3393, 15827, 24020, 25777, 20627, + 9949, -3393, -15827, -24020, -25777, -20627, -9949, 3393, + 15827, 24020, 25777, 20627, 9949, -3393, -15827, -24020, + -25777, -20627, -9949, 3393, 15827, 24020, 25777, 20627, + 9949, -3393, -15827, -24020, -25777, -20627, -9949, 3393, + 15827, 24020, 25777, 20627, 9949, -3393, -15827, -24020, + -25777, -20627, -9949, 3393, 15827, 24020, 25777, 20627, + 9949, -3393, -15827, -24020, -25777, -20627, -9949, 3393, + 15827, 24020, 25777, 20627, 9949, -3393, -15827, -24020, + -25777, -20627, -9949, 3393, 15827, 24020, 25777, 20627, + 9949, -3393, -15827, -24020, -25777, -20627, -9949, 3393, + 15827, 24020, 25777, 20627, 9949, -3393, -15827, -24020, + -25777, -20627, -9949, 3393, 15827, 24020, 25777, 20627, + 9949, -3393, -15827, -24020, -25777, -20627, -9949, 3393, + 15827, 24020, 25777, 20627, 9949, -3393, -15827, -24020, + -25777, -20627, -9949, 3393, 15827, 24020, 25777, 20627, + 9949, -3393, -15827, -24020, -25777, -20627, -9949, 3393, + 15827, 24020, 25777, 20627, 9949, -3393, -15827, -24020, + -25777, -20627, -9949, 3393, 15827, 24020, 25777, 20627}, + },{{ + +// Carrier 13 Phase 0 + 0, 13649, 23234, 25901, 20855, 9599, -4514, -17284, + -24907, -25114, -17842, -5257, 8892, 20394, 25824, 23563, + 14287, 756, -13000, -22885, -25956, -21297, -10298, 3768, + 16712, 24680, 25299, 18384, 5995, -8178, -19917, -25725, + -23873, -14912, -1511, 12339, 22516, 25989, 21722, 10988, + -3018, -16126, -24432, -25462, -18911, -6729, 7456, 19422, + 25605, 24163, 15526, 2265, -11668, -22129, -26000, -22129, + -11668, 2266, 15526, 24163, 25604, 19422, 7456, -6729, + -18911, -25462, -24431, -16126, -3018, 10988, 21722, 25989, + 22516, 12339, -1511, -14913, -23873, -25725, -19917, -8178, + 5996, 18384, 25299, 24680, 16712, 3768, -10298, -21297, + -25956, -22885, -12999, 756, 14287, 23564, 25824, 20394, + 8892, -5257, -17842, -25114, -24907, -17284, -4514, 9599, + 20855, 25901, 23234, 13649, 0, -13649, -23234, -25901, + -20855, -9599, 4514, 17284, 24907, 25114, 17842, 5257, + -8892, -20394, -25824, -23563, -14287, -756, 13000, 22885, + 25956, 21297, 10297, -3768, -16712, -24680, -25299, -18384, + -5995, 8178, 19917, 25725, 23873, 14912, 1511, -12339, + -22516, -25988, -21722, -10987, 3018, 16126, 24432, 25462, + 18911, 6729, -7457, -19422, -25605, -24162, -15526, -2265, + 11668, 22129, 26000, 22128, 11668, -2266, -15526, -24163, + -25604, -19422, -7456, 6729, 18911, 25462, 24431, 16125, + 3018, -10988, -21722, -25989, -22516, -12339, 1511, 14913, + 23873, 25725, 19917, 8177, -5996, -18384, -25299, -24680, + -16712, -3768, 10298, 21298, 25956, 22885, 12999, -756, + -14287, -23564, -25824, -20394, -8892, 5257, 17842, 25114, + 24907, 17284, 4514, -9599, -20855, -25901, -23234, -13649}, + { +// Carrier 13 Phase 1 + 9949, 21078, 25931, 23062, 13326, -378, -13969, -23401, + -25865, -20627, -9246, 4886, 17565, 25013, 25013, 17565, + 4886, -9246, -20627, -25865, -23401, -13969, -378, 13326, + 23062, 25931, 21078, 9949, -4142, -17000, -24796, -25209, + -18115, -5627, 8536, 20158, 25777, 23721, 14601, 1134, + -12671, -22703, -25975, -21512, -10644, 3393, 16421, 24558, + 25383, 18650, 6363, -7818, -19672, -25667, -24020, -15221, + -1889, 12005, 22325, 25997, 21928, 11329, -2642, -15827, + -24300, -25536, -19169, -7093, 7093, 19169, 25536, 24300, + 15827, 2642, -11329, -21928, -25997, -22325, -12005, 1889, + 15221, 24020, 25667, 19671, 7818, -6363, -18650, -25383, + -24558, -16420, -3393, 10644, 21512, 25975, 22703, 12671, + -1134, -14601, -23721, -25777, -20158, -8536, 5627, 18115, + 25209, 24796, 17000, 4141, -9949, -21078, -25931, -23062, + -13326, 378, 13969, 23401, 25865, 20627, 9246, -4886, + -17565, -25013, -25013, -17565, -4886, 9247, 20627, 25865, + 23401, 13969, 378, -13326, -23062, -25931, -21078, -9949, + 4142, 17000, 24796, 25209, 18115, 5627, -8536, -20158, + -25777, -23721, -14601, -1133, 12671, 22703, 25975, 21512, + 10644, -3393, -16421, -24558, -25383, -18650, -6363, 7818, + 19672, 25667, 24020, 15221, 1888, -12005, -22325, -25997, + -21928, -11329, 2642, 15827, 24300, 25536, 19169, 7093, + -7094, -19169, -25536, -24300, -15827, -2642, 11329, 21928, + 25997, 22325, 12005, -1889, -15221, -24020, -25667, -19671, + -7818, 6363, 18650, 25383, 24558, 16420, 3393, -10644, + -21512, -25975, -22703, -12670, 1134, 14601, 23721, 25777, + 20157, 8536, -5627, -18115, -25209, -24796, -17000, -4141}, + { +// Carrier 13 Phase 2 + 18384, 25299, 24680, 16712, 3768, -10298, -21297, -25956, + -22885, -12999, 756, 14287, 23564, 25824, 20394, 8892, + -5257, -17842, -25114, -24907, -17284, -4514, 9599, 20855, + 25901, 23234, 13649, 0, -13649, -23234, -25901, -20855, + -9599, 4514, 17284, 24907, 25114, 17842, 5257, -8892, + -20394, -25824, -23563, -14287, -756, 13000, 22885, 25956, + 21297, 10298, -3768, -16712, -24680, -25299, -18384, -5995, + 8178, 19917, 25725, 23873, 14912, 1511, -12339, -22516, + -25988, -21722, -10988, 3018, 16126, 24432, 25462, 18911, + 6729, -7456, -19422, -25605, -24163, -15526, -2265, 11668, + 22129, 26000, 22128, 11668, -2266, -15526, -24163, -25604, + -19422, -7456, 6729, 18911, 25462, 24431, 16126, 3018, + -10988, -21722, -25989, -22516, -12339, 1511, 14913, 23873, + 25725, 19917, 8178, -5996, -18384, -25299, -24680, -16712, + -3768, 10298, 21298, 25956, 22885, 12999, -756, -14287, + -23564, -25824, -20394, -8892, 5257, 17842, 25114, 24907, + 17284, 4514, -9599, -20855, -25901, -23234, -13649, 0, + 13649, 23234, 25901, 20855, 9599, -4515, -17284, -24907, + -25114, -17842, -5257, 8892, 20394, 25824, 23563, 14287, + 756, -13000, -22885, -25956, -21297, -10297, 3768, 16712, + 24680, 25299, 18384, 5995, -8178, -19917, -25725, -23873, + -14912, -1511, 12339, 22516, 25988, 21722, 10987, -3018, + -16126, -24432, -25462, -18911, -6729, 7457, 19422, 25605, + 24162, 15525, 2265, -11668, -22129, -26000, -22128, -11668, + 2266, 15526, 24163, 25604, 19422, 7456, -6729, -18911, + -25462, -24431, -16125, -3018, 10988, 21722, 25989, 22516, + 12339, -1511, -14913, -23873, -25725, -19917, -8177, 5996}, + { +// Carrier 13 Phase 3 + 24020, 25667, 19671, 7818, -6363, -18650, -25383, -24558, + -16421, -3393, 10644, 21512, 25975, 22703, 12671, -1134, + -14601, -23721, -25777, -20158, -8536, 5627, 18115, 25209, + 24796, 17000, 4141, -9949, -21078, -25931, -23062, -13326, + 378, 13969, 23401, 25865, 20627, 9246, -4886, -17565, + -25013, -25013, -17565, -4886, 9246, 20627, 25865, 23401, + 13969, 378, -13326, -23062, -25931, -21078, -9949, 4142, + 17000, 24796, 25209, 18115, 5627, -8536, -20158, -25777, + -23721, -14601, -1134, 12671, 22703, 25975, 21512, 10644, + -3393, -16421, -24558, -25383, -18650, -6363, 7818, 19672, + 25667, 24020, 15221, 1889, -12005, -22325, -25997, -21928, + -11329, 2642, 15827, 24300, 25536, 19169, 7093, -7093, + -19169, -25536, -24300, -15827, -2642, 11329, 21928, 25997, + 22325, 12005, -1889, -15221, -24020, -25667, -19671, -7818, + 6363, 18650, 25383, 24558, 16420, 3393, -10644, -21512, + -25975, -22703, -12671, 1134, 14601, 23721, 25777, 20158, + 8536, -5627, -18115, -25209, -24796, -17000, -4141, 9949, + 21078, 25931, 23062, 13325, -378, -13969, -23401, -25865, + -20627, -9246, 4886, 17565, 25013, 25013, 17565, 4886, + -9247, -20627, -25865, -23401, -13969, -377, 13326, 23062, + 25931, 21078, 9949, -4142, -17000, -24796, -25209, -18115, + -5627, 8536, 20158, 25777, 23721, 14601, 1133, -12671, + -22703, -25975, -21512, -10644, 3393, 16421, 24558, 25383, + 18650, 6363, -7818, -19672, -25667, -24020, -15220, -1888, + 12005, 22325, 25997, 21928, 11329, -2642, -15827, -24300, + -25536, -19169, -7093, 7094, 19169, 25536, 24300, 15827, + 2642, -11329, -21928, -25997, -22325, -12005, 1889, 15221}, + { +// Carrier 13 Phase 4 + 26000, 22129, 11668, -2266, -15526, -24163, -25605, -19422, + -7456, 6729, 18911, 25462, 24432, 16126, 3018, -10988, + -21722, -25989, -22516, -12339, 1511, 14913, 23873, 25725, + 19917, 8178, -5996, -18384, -25299, -24680, -16712, -3768, + 10298, 21297, 25956, 22885, 12999, -756, -14287, -23564, + -25824, -20394, -8892, 5257, 17842, 25114, 24907, 17284, + 4514, -9599, -20855, -25901, -23234, -13649, 0, 13649, + 23234, 25901, 20855, 9599, -4514, -17284, -24907, -25114, + -17842, -5257, 8892, 20394, 25824, 23563, 14287, 756, + -13000, -22885, -25956, -21297, -10298, 3768, 16712, 24680, + 25299, 18384, 5995, -8178, -19917, -25725, -23873, -14912, + -1511, 12339, 22516, 25988, 21722, 10987, -3018, -16126, + -24432, -25462, -18911, -6729, 7456, 19422, 25605, 24162, + 15526, 2265, -11668, -22129, -26000, -22128, -11668, 2266, + 15526, 24163, 25604, 19422, 7456, -6729, -18911, -25462, + -24431, -16126, -3018, 10988, 21722, 25989, 22516, 12339, + -1511, -14913, -23873, -25725, -19917, -8178, 5996, 18384, + 25299, 24680, 16712, 3768, -10298, -21298, -25956, -22885, + -12999, 756, 14287, 23564, 25824, 20394, 8892, -5257, + -17842, -25114, -24907, -17284, -4514, 9599, 20855, 25901, + 23234, 13649, 0, -13649, -23234, -25901, -20855, -9599, + 4515, 17284, 24907, 25114, 17842, 5257, -8892, -20394, + -25824, -23563, -14287, -756, 13000, 22885, 25956, 21297, + 10297, -3768, -16712, -24680, -25299, -18384, -5995, 8178, + 19917, 25725, 23873, 14912, 1511, -12339, -22516, -25988, + -21722, -10987, 3018, 16126, 24432, 25462, 18911, 6729, + -7457, -19422, -25605, -24162, -15525, -2265, 11668, 22129}, + { +// Carrier 13 Phase 5 + 24020, 15221, 1889, -12005, -22325, -25997, -21928, -11329, + 2642, 15827, 24300, 25536, 19169, 7093, -7093, -19169, + -25536, -24300, -15827, -2642, 11329, 21928, 25997, 22325, + 12005, -1889, -15221, -24020, -25667, -19671, -7818, 6363, + 18650, 25383, 24558, 16421, 3393, -10644, -21512, -25975, + -22703, -12671, 1134, 14601, 23721, 25777, 20158, 8536, + -5627, -18115, -25209, -24796, -17000, -4141, 9949, 21078, + 25931, 23062, 13326, -378, -13969, -23401, -25865, -20627, + -9246, 4886, 17565, 25013, 25013, 17565, 4886, -9246, + -20627, -25865, -23401, -13969, -378, 13326, 23062, 25931, + 21078, 9949, -4142, -17000, -24796, -25209, -18115, -5627, + 8536, 20158, 25777, 23721, 14601, 1133, -12671, -22703, + -25975, -21512, -10644, 3393, 16421, 24558, 25383, 18650, + 6363, -7818, -19672, -25667, -24020, -15221, -1888, 12005, + 22325, 25997, 21928, 11329, -2642, -15827, -24300, -25536, + -19169, -7093, 7093, 19169, 25536, 24300, 15827, 2642, + -11329, -21928, -25997, -22325, -12005, 1889, 15221, 24020, + 25667, 19671, 7818, -6363, -18650, -25383, -24558, -16420, + -3393, 10644, 21512, 25975, 22703, 12670, -1134, -14601, + -23721, -25777, -20158, -8536, 5627, 18115, 25209, 24796, + 17000, 4141, -9949, -21078, -25931, -23062, -13325, 378, + 13969, 23401, 25865, 20627, 9246, -4886, -17565, -25013, + -25013, -17565, -4886, 9247, 20627, 25865, 23401, 13969, + 377, -13326, -23062, -25931, -21078, -9949, 4142, 17000, + 24796, 25209, 18115, 5627, -8536, -20158, -25777, -23721, + -14601, -1133, 12671, 22703, 25975, 21512, 10643, -3393, + -16421, -24558, -25383, -18650, -6363, 7818, 19672, 25667}, + { +// Carrier 13 Phase 6 + 18384, 5996, -8178, -19917, -25725, -23873, -14912, -1511, + 12339, 22516, 25989, 21722, 10988, -3018, -16126, -24432, + -25462, -18911, -6729, 7456, 19422, 25605, 24163, 15526, + 2266, -11668, -22129, -26000, -22129, -11668, 2266, 15526, + 24163, 25604, 19422, 7456, -6729, -18911, -25462, -24431, + -16126, -3018, 10988, 21722, 25989, 22516, 12339, -1511, + -14913, -23873, -25725, -19917, -8178, 5996, 18384, 25299, + 24680, 16712, 3768, -10298, -21297, -25956, -22885, -12999, + 756, 14287, 23564, 25824, 20394, 8892, -5257, -17842, + -25114, -24907, -17284, -4514, 9599, 20855, 25901, 23234, + 13649, 0, -13649, -23234, -25901, -20855, -9599, 4514, + 17284, 24907, 25114, 17842, 5257, -8892, -20394, -25824, + -23563, -14287, -756, 13000, 22885, 25956, 21297, 10297, + -3768, -16712, -24680, -25299, -18384, -5995, 8178, 19917, + 25725, 23873, 14912, 1511, -12339, -22516, -25988, -21722, + -10987, 3018, 16126, 24432, 25462, 18911, 6729, -7457, + -19422, -25605, -24162, -15526, -2265, 11668, 22129, 26000, + 22128, 11668, -2266, -15526, -24163, -25604, -19422, -7456, + 6729, 18911, 25462, 24431, 16125, 3018, -10988, -21722, + -25989, -22516, -12339, 1511, 14913, 23873, 25725, 19917, + 8177, -5996, -18384, -25299, -24680, -16712, -3768, 10298, + 21298, 25956, 22885, 12999, -756, -14287, -23564, -25824, + -20394, -8892, 5257, 17842, 25114, 24907, 17284, 4514, + -9599, -20855, -25901, -23234, -13649, 0, 13649, 23234, + 25901, 20855, 9599, -4515, -17284, -24907, -25114, -17842, + -5257, 8892, 20394, 25824, 23563, 14287, 755, -13000, + -22885, -25955, -21297, -10297, 3768, 16712, 24680, 25299}, + { +// Carrier 13 Phase 7 + 9949, -4141, -17000, -24796, -25209, -18115, -5627, 8536, + 20158, 25777, 23721, 14601, 1134, -12671, -22703, -25975, + -21512, -10644, 3393, 16421, 24558, 25383, 18650, 6363, + -7818, -19671, -25667, -24020, -15221, -1889, 12005, 22325, + 25997, 21928, 11329, -2642, -15827, -24300, -25536, -19169, + -7093, 7093, 19169, 25536, 24300, 15827, 2642, -11329, + -21928, -25997, -22325, -12005, 1889, 15221, 24020, 25667, + 19671, 7818, -6363, -18650, -25383, -24558, -16420, -3393, + 10644, 21512, 25975, 22703, 12671, -1134, -14601, -23721, + -25777, -20158, -8536, 5627, 18115, 25209, 24796, 17000, + 4141, -9949, -21078, -25931, -23062, -13326, 378, 13969, + 23401, 25865, 20627, 9246, -4886, -17565, -25013, -25013, + -17565, -4886, 9247, 20627, 25865, 23401, 13969, 378, + -13326, -23062, -25931, -21078, -9949, 4142, 17000, 24796, + 25209, 18115, 5627, -8536, -20158, -25777, -23721, -14601, + -1133, 12671, 22703, 25975, 21512, 10644, -3393, -16421, + -24558, -25383, -18650, -6363, 7818, 19672, 25667, 24020, + 15221, 1888, -12005, -22325, -25997, -21928, -11329, 2642, + 15827, 24300, 25536, 19169, 7093, -7093, -19169, -25536, + -24300, -15827, -2642, 11329, 21928, 25997, 22325, 12005, + -1889, -15221, -24020, -25667, -19671, -7818, 6363, 18650, + 25383, 24558, 16420, 3393, -10644, -21512, -25975, -22703, + -12670, 1134, 14601, 23721, 25777, 20157, 8536, -5627, + -18115, -25209, -24796, -17000, -4141, 9949, 21078, 25931, + 23062, 13325, -378, -13969, -23401, -25865, -20627, -9246, + 4886, 17565, 25013, 25013, 17565, 4886, -9247, -20627, + -25865, -23401, -13969, -377, 13326, 23062, 25931, 21078}, + },{{ + +// Carrier 14 Phase 0 + 0, 14287, 23873, 25605, 18911, 5996, -8892, -20855, + -25956, -22516, -11668, 3018, 16712, 24907, 24907, 16712, + 3018, -11668, -22516, -25956, -20855, -8892, 5996, 18911, + 25605, 23873, 14287, 0, -14287, -23873, -25604, -18911, + -5995, 8892, 20855, 25956, 22516, 11668, -3018, -16712, + -24907, -24907, -16712, -3018, 11668, 22516, 25956, 20855, + 8892, -5996, -18911, -25605, -23873, -14287, 0, 14287, + 23873, 25604, 18911, 5995, -8892, -20855, -25956, -22516, + -11668, 3018, 16712, 24907, 24907, 16712, 3018, -11668, + -22516, -25956, -20855, -8892, 5996, 18911, 25605, 23873, + 14287, 0, -14287, -23873, -25604, -18911, -5995, 8892, + 20855, 25956, 22516, 11668, -3018, -16712, -24907, -24907, + -16712, -3018, 11668, 22516, 25956, 20855, 8892, -5996, + -18911, -25605, -23873, -14287, 0, 14287, 23873, 25604, + 18911, 5995, -8892, -20855, -25956, -22516, -11668, 3018, + 16712, 24907, 24907, 16712, 3018, -11668, -22516, -25956, + -20855, -8892, 5996, 18911, 25605, 23873, 14287, 0, + -14287, -23873, -25604, -18911, -5995, 8892, 20855, 25956, + 22516, 11668, -3018, -16712, -24907, -24907, -16712, -3018, + 11668, 22516, 25956, 20855, 8892, -5996, -18911, -25605, + -23873, -14287, 0, 14287, 23873, 25604, 18911, 5995, + -8892, -20855, -25956, -22516, -11668, 3018, 16712, 24907, + 24907, 16712, 3018, -11668, -22516, -25955, -20855, -8892, + 5996, 18911, 25605, 23873, 14287, 0, -14287, -23873, + -25604, -18911, -5995, 8892, 20855, 25956, 22516, 11668, + -3018, -16712, -24907, -24907, -16712, -3018, 11669, 22516, + 25955, 20855, 8892, -5996, -18911, -25605, -23873, -14286}, + { +// Carrier 14 Phase 1 + 9949, 21512, 25997, 21928, 10644, -4141, -17565, -25209, + -24558, -15827, -1889, 12671, 23062, 25865, 20158, 7818, + -7093, -19671, -25777, -23401, -13326, 1134, 15221, 24300, + 25383, 18115, 4886, -9949, -21512, -25997, -21928, -10644, + 4142, 17565, 25209, 24558, 15827, 1889, -12671, -23062, + -25865, -20158, -7818, 7093, 19672, 25777, 23401, 13326, + -1134, -15221, -24300, -25383, -18115, -4886, 9949, 21512, + 25997, 21928, 10644, -4142, -17565, -25209, -24558, -15827, + -1889, 12671, 23062, 25865, 20158, 7818, -7093, -19672, + -25777, -23401, -13326, 1134, 15221, 24300, 25383, 18115, + 4886, -9949, -21512, -25997, -21928, -10644, 4142, 17565, + 25209, 24558, 15827, 1888, -12671, -23062, -25865, -20158, + -7818, 7093, 19672, 25777, 23401, 13325, -1134, -15221, + -24300, -25383, -18115, -4886, 9949, 21512, 25997, 21928, + 10644, -4142, -17565, -25209, -24558, -15827, -1888, 12671, + 23062, 25865, 20158, 7818, -7093, -19672, -25777, -23401, + -13325, 1134, 15221, 24300, 25383, 18115, 4886, -9949, + -21512, -25997, -21928, -10644, 4142, 17565, 25209, 24558, + 15827, 1888, -12671, -23062, -25865, -20157, -7818, 7094, + 19672, 25777, 23401, 13325, -1134, -15221, -24300, -25383, + -18115, -4886, 9949, 21512, 25997, 21928, 10643, -4142, + -17565, -25209, -24558, -15827, -1888, 12671, 23062, 25865, + 20157, 7818, -7094, -19672, -25777, -23401, -13325, 1134, + 15221, 24300, 25383, 18115, 4886, -9949, -21512, -25997, + -21928, -10643, 4142, 17565, 25209, 24558, 15827, 1888, + -12671, -23062, -25865, -20157, -7818, 7094, 19672, 25777, + 23401, 13325, -1134, -15221, -24300, -25383, -18115, -4886}, + { +// Carrier 14 Phase 2 + 18384, 25462, 24163, 14912, 756, -13649, -23563, -25725, + -19422, -6729, 8178, 20394, 25901, 22885, 12339, -2266, + -16126, -24680, -25114, -17284, -3768, 10988, 22129, 25989, + 21297, 9599, -5257, -18384, -25462, -24163, -14912, -756, + 13649, 23564, 25725, 19422, 6729, -8178, -20394, -25901, + -22885, -12339, 2266, 16126, 24680, 25114, 17284, 3768, + -10988, -22129, -25988, -21297, -9599, 5257, 18384, 25462, + 24163, 14912, 756, -13649, -23564, -25725, -19422, -6729, + 8178, 20394, 25901, 22885, 12339, -2266, -16126, -24680, + -25114, -17284, -3768, 10988, 22129, 25988, 21297, 9599, + -5257, -18384, -25462, -24162, -14912, -756, 13649, 23564, + 25725, 19422, 6729, -8178, -20394, -25901, -22885, -12339, + 2266, 16126, 24680, 25114, 17284, 3768, -10988, -22129, + -25988, -21297, -9599, 5257, 18384, 25462, 24162, 14912, + 756, -13649, -23564, -25725, -19422, -6729, 8178, 20394, + 25901, 22885, 12339, -2266, -16126, -24680, -25114, -17284, + -3768, 10988, 22129, 25988, 21297, 9599, -5257, -18384, + -25462, -24162, -14912, -756, 13649, 23564, 25725, 19422, + 6729, -8178, -20394, -25901, -22885, -12339, 2266, 16126, + 24680, 25114, 17284, 3768, -10988, -22129, -25988, -21297, + -9599, 5257, 18384, 25462, 24162, 14912, 755, -13649, + -23564, -25725, -19422, -6729, 8178, 20394, 25901, 22885, + 12339, -2266, -16126, -24680, -25114, -17284, -3767, 10988, + 22129, 25988, 21297, 9599, -5257, -18384, -25462, -24162, + -14912, -755, 13649, 23564, 25725, 19422, 6729, -8178, + -20394, -25901, -22885, -12339, 2266, 16126, 24680, 25113, + 17284, 3767, -10988, -22129, -25988, -21297, -9599, 5257}, + { +// Carrier 14 Phase 3 + 24020, 25536, 18650, 5627, -9246, -21078, -25975, -22325, + -11329, 3393, 17000, 25013, 24796, 16421, 2642, -12005, + -22703, -25931, -20627, -8536, 6363, 19169, 25667, 23721, + 13969, -378, -14601, -24020, -25536, -18650, -5627, 9246, + 21078, 25975, 22325, 11329, -3393, -17000, -25013, -24796, + -16421, -2642, 12005, 22703, 25931, 20627, 8536, -6363, + -19169, -25667, -23721, -13969, 378, 14601, 24020, 25536, + 18650, 5627, -9246, -21078, -25975, -22325, -11329, 3393, + 17000, 25013, 24796, 16420, 2642, -12005, -22703, -25931, + -20627, -8536, 6363, 19169, 25667, 23721, 13969, -378, + -14601, -24020, -25536, -18650, -5627, 9247, 21078, 25975, + 22325, 11329, -3393, -17000, -25013, -24796, -16420, -2642, + 12005, 22703, 25931, 20627, 8536, -6363, -19169, -25667, + -23721, -13969, 378, 14601, 24020, 25536, 18650, 5627, + -9247, -21078, -25975, -22325, -11329, 3393, 17000, 25013, + 24796, 16420, 2642, -12005, -22703, -25931, -20627, -8536, + 6363, 19169, 25667, 23721, 13969, -378, -14601, -24020, + -25536, -18650, -5627, 9247, 21078, 25975, 22325, 11329, + -3393, -17000, -25013, -24796, -16420, -2642, 12005, 22703, + 25931, 20627, 8536, -6363, -19169, -25667, -23721, -13969, + 378, 14601, 24020, 25536, 18650, 5627, -9247, -21078, + -25975, -22325, -11329, 3393, 17000, 25013, 24796, 16420, + 2642, -12005, -22703, -25931, -20627, -8536, 6363, 19169, + 25667, 23721, 13969, -378, -14601, -24020, -25536, -18650, + -5627, 9247, 21078, 25975, 22325, 11329, -3393, -17000, + -25013, -24796, -16420, -2642, 12005, 22703, 25931, 20627, + 8535, -6363, -19169, -25668, -23721, -13969, 378, 14601}, + { +// Carrier 14 Phase 4 + 26000, 21722, 10298, -4514, -17842, -25299, -24432, -15526, + -1511, 13000, 23234, 25824, 19917, 7456, -7456, -19917, + -25824, -23234, -12999, 1511, 15526, 24432, 25299, 17842, + 4514, -10298, -21722, -26000, -21722, -10298, 4514, 17842, + 25299, 24431, 15526, 1511, -13000, -23234, -25824, -19917, + -7456, 7456, 19917, 25824, 23234, 12999, -1511, -15526, + -24432, -25299, -17842, -4514, 10298, 21722, 26000, 21722, + 10298, -4514, -17842, -25299, -24431, -15526, -1511, 13000, + 23234, 25824, 19917, 7456, -7456, -19917, -25824, -23234, + -12999, 1511, 15526, 24432, 25299, 17842, 4514, -10298, + -21722, -26000, -21722, -10297, 4514, 17842, 25299, 24431, + 15526, 1511, -13000, -23234, -25824, -19917, -7456, 7457, + 19917, 25824, 23234, 12999, -1511, -15526, -24432, -25299, + -17842, -4514, 10298, 21722, 26000, 21722, 10297, -4515, + -17842, -25299, -24431, -15526, -1511, 13000, 23234, 25824, + 19917, 7456, -7457, -19917, -25824, -23234, -12999, 1511, + 15526, 24432, 25299, 17842, 4514, -10298, -21722, -26000, + -21722, -10297, 4515, 17842, 25299, 24431, 15525, 1511, + -13000, -23234, -25824, -19917, -7456, 7457, 19917, 25824, + 23234, 12999, -1511, -15526, -24432, -25299, -17842, -4514, + 10298, 21722, 26000, 21722, 10297, -4515, -17842, -25299, + -24431, -15525, -1511, 13000, 23234, 25824, 19917, 7456, + -7457, -19917, -25824, -23234, -12999, 1512, 15526, 24432, + 25299, 17842, 4514, -10298, -21722, -26000, -21722, -10297, + 4515, 17842, 25299, 24431, 15525, 1511, -13000, -23234, + -25824, -19916, -7456, 7457, 19917, 25824, 23234, 12999, + -1512, -15526, -24432, -25299, -17842, -4514, 10298, 21722}, + { +// Carrier 14 Phase 5 + 24020, 14601, 378, -13969, -23721, -25667, -19169, -6363, + 8536, 20627, 25931, 22703, 12005, -2642, -16421, -24796, + -25013, -17000, -3393, 11329, 22325, 25975, 21078, 9246, + -5627, -18650, -25536, -24020, -14601, -378, 13969, 23721, + 25667, 19169, 6363, -8536, -20627, -25931, -22703, -12005, + 2642, 16421, 24796, 25013, 17000, 3393, -11329, -22325, + -25975, -21078, -9246, 5627, 18650, 25536, 24020, 14601, + 378, -13969, -23721, -25667, -19169, -6363, 8536, 20627, + 25931, 22703, 12005, -2642, -16421, -24796, -25013, -17000, + -3393, 11329, 22325, 25975, 21078, 9246, -5627, -18650, + -25536, -24020, -14601, -378, 13969, 23721, 25667, 19169, + 6363, -8536, -20627, -25931, -22703, -12005, 2642, 16421, + 24796, 25013, 17000, 3393, -11329, -22325, -25975, -21078, + -9246, 5627, 18650, 25536, 24020, 14601, 377, -13969, + -23721, -25667, -19169, -6363, 8536, 20627, 25931, 22703, + 12005, -2642, -16421, -24796, -25013, -17000, -3393, 11329, + 22325, 25975, 21078, 9246, -5627, -18650, -25536, -24020, + -14601, -377, 13969, 23721, 25667, 19169, 6363, -8536, + -20627, -25931, -22703, -12005, 2642, 16421, 24796, 25013, + 17000, 3393, -11329, -22325, -25975, -21078, -9246, 5627, + 18650, 25536, 24020, 14601, 377, -13969, -23721, -25667, + -19169, -6363, 8536, 20627, 25931, 22703, 12005, -2642, + -16421, -24796, -25013, -17000, -3393, 11329, 22325, 25975, + 21078, 9246, -5627, -18650, -25536, -24020, -14601, -377, + 13970, 23721, 25667, 19169, 6363, -8536, -20627, -25931, + -22703, -12005, 2642, 16421, 24796, 25013, 17000, 3393, + -11329, -22325, -25975, -21078, -9246, 5627, 18650, 25536}, + { +// Carrier 14 Phase 6 + 18384, 5257, -9599, -21297, -25989, -22129, -10988, 3768, + 17284, 25114, 24680, 16126, 2266, -12339, -22885, -25901, + -20394, -8178, 6729, 19422, 25725, 23563, 13649, -756, + -14913, -24163, -25462, -18384, -5257, 9599, 21297, 25989, + 22129, 10988, -3768, -17284, -25114, -24680, -16126, -2265, + 12339, 22885, 25901, 20394, 8178, -6729, -19422, -25725, + -23563, -13649, 756, 14913, 24163, 25462, 18384, 5257, + -9599, -21297, -25989, -22128, -10988, 3768, 17284, 25114, + 24680, 16126, 2265, -12339, -22885, -25901, -20394, -8178, + 6729, 19422, 25725, 23563, 13649, -756, -14913, -24163, + -25462, -18384, -5257, 9599, 21298, 25989, 22128, 10987, + -3768, -17284, -25114, -24680, -16126, -2265, 12339, 22885, + 25901, 20394, 8178, -6729, -19422, -25725, -23563, -13649, + 756, 14913, 24163, 25462, 18384, 5257, -9599, -21298, + -25989, -22128, -10987, 3768, 17284, 25114, 24680, 16125, + 2265, -12339, -22885, -25901, -20394, -8178, 6729, 19422, + 25725, 23563, 13649, -756, -14913, -24163, -25462, -18384, + -5257, 9599, 21298, 25989, 22128, 10987, -3768, -17284, + -25114, -24680, -16125, -2265, 12339, 22885, 25901, 20394, + 8177, -6729, -19422, -25725, -23563, -13649, 756, 14913, + 24163, 25462, 18384, 5257, -9599, -21298, -25989, -22128, + -10987, 3768, 17284, 25114, 24680, 16125, 2265, -12339, + -22885, -25901, -20394, -8177, 6729, 19422, 25725, 23563, + 13649, -756, -14913, -24163, -25462, -18384, -5257, 9599, + 21298, 25989, 22128, 10987, -3768, -17284, -25114, -24680, + -16125, -2265, 12339, 22885, 25901, 20394, 8177, -6729, + -19422, -25725, -23563, -13649, 756, 14913, 24163, 25462}, + { +// Carrier 14 Phase 7 + 9949, -4886, -18115, -25383, -24300, -15221, -1134, 13326, + 23401, 25777, 19671, 7093, -7818, -20158, -25865, -23062, + -12671, 1889, 15827, 24558, 25209, 17565, 4141, -10644, + -21928, -25997, -21512, -9949, 4886, 18115, 25383, 24300, + 15221, 1134, -13326, -23401, -25777, -19671, -7093, 7818, + 20158, 25865, 23062, 12671, -1889, -15827, -24558, -25209, + -17565, -4141, 10644, 21928, 25997, 21512, 9949, -4886, + -18115, -25383, -24300, -15221, -1134, 13326, 23401, 25777, + 19671, 7093, -7818, -20158, -25865, -23062, -12671, 1889, + 15827, 24558, 25209, 17565, 4141, -10644, -21928, -25997, + -21512, -9949, 4886, 18115, 25383, 24300, 15221, 1133, + -13326, -23401, -25777, -19671, -7093, 7818, 20158, 25865, + 23062, 12671, -1889, -15827, -24558, -25209, -17565, -4141, + 10644, 21928, 25997, 21512, 9949, -4886, -18115, -25383, + -24300, -15221, -1133, 13326, 23401, 25777, 19671, 7093, + -7818, -20158, -25865, -23062, -12670, 1889, 15827, 24558, + 25209, 17565, 4141, -10644, -21928, -25997, -21512, -9949, + 4886, 18115, 25383, 24300, 15221, 1133, -13326, -23401, + -25777, -19671, -7093, 7818, 20158, 25865, 23062, 12670, + -1889, -15827, -24558, -25209, -17565, -4141, 10644, 21928, + 25997, 21512, 9949, -4886, -18115, -25383, -24300, -15220, + -1133, 13326, 23401, 25777, 19671, 7093, -7818, -20158, + -25865, -23062, -12670, 1889, 15828, 24558, 25209, 17565, + 4141, -10644, -21928, -25997, -21512, -9949, 4887, 18115, + 25383, 24299, 15220, 1133, -13326, -23401, -25777, -19671, + -7093, 7818, 20158, 25865, 23062, 12670, -1889, -15828, + -24558, -25209, -17565, -4141, 10644, 21928, 25997, 21512}, + },{{ + +// Carrier 15 Phase 0 + 0, 14912, 24432, 25114, 16712, 2266, -12999, -23563, + -25605, -18384, -4514, 10988, 22516, 25901, 19917, 6729, + -8892, -21297, -26000, -21297, -8892, 6729, 19917, 25901, + 22516, 10988, -4514, -18384, -25604, -23564, -13000, 2265, + 16712, 25114, 24432, 14913, 0, -14912, -24431, -25114, + -16712, -2266, 12999, 23563, 25605, 18384, 4514, -10988, + -22516, -25901, -19917, -6729, 8892, 21297, 26000, 21298, + 8892, -6729, -19917, -25901, -22516, -10988, 4514, 18384, + 25604, 23564, 13000, -2265, -16712, -25114, -24432, -14913, + 0, 14912, 24431, 25114, 16712, 2266, -12999, -23563, + -25605, -18384, -4514, 10987, 22516, 25901, 19917, 6729, + -8892, -21297, -26000, -21298, -8892, 6729, 19917, 25901, + 22516, 10988, -4514, -18384, -25604, -23564, -13000, 2265, + 16712, 25114, 24432, 14913, 0, -14912, -24431, -25114, + -16712, -2266, 12999, 23563, 25605, 18384, 4515, -10987, + -22516, -25901, -19917, -6729, 8892, 21297, 26000, 21298, + 8892, -6729, -19917, -25901, -22516, -10988, 4514, 18384, + 25604, 23564, 13000, -2265, -16712, -25114, -24432, -14913, + 0, 14912, 24431, 25114, 16712, 2266, -12999, -23563, + -25605, -18384, -4515, 10987, 22516, 25901, 19917, 6729, + -8892, -21297, -26000, -21298, -8892, 6729, 19916, 25901, + 22516, 10988, -4514, -18384, -25604, -23564, -13000, 2265, + 16712, 25113, 24432, 14913, 0, -14912, -24431, -25114, + -16712, -2266, 12999, 23563, 25605, 18385, 4515, -10987, + -22516, -25901, -19917, -6729, 8892, 21297, 26000, 21298, + 8892, -6728, -19916, -25901, -22516, -10988, 4514, 18384, + 25604, 23564, 13000, -2265, -16712, -25113, -24432, -14913}, + { +// Carrier 15 Phase 1 + 9949, 21928, 25975, 20627, 7818, -7818, -20627, -25975, + -21928, -9949, 5627, 19169, 25777, 23062, 12005, -3393, + -17565, -25383, -24020, -13969, 1134, 15827, 24796, 24796, + 15827, 1134, -13969, -24020, -25383, -17565, -3393, 12005, + 23062, 25777, 19169, 5627, -9949, -21928, -25975, -20627, + -7818, 7818, 20627, 25975, 21928, 9949, -5627, -19169, + -25777, -23062, -12005, 3393, 17565, 25383, 24020, 13969, + -1134, -15827, -24796, -24796, -15827, -1134, 13969, 24020, + 25383, 17565, 3393, -12005, -23062, -25777, -19169, -5627, + 9949, 21928, 25975, 20627, 7818, -7818, -20627, -25975, + -21928, -9949, 5627, 19169, 25777, 23062, 12005, -3393, + -17565, -25383, -24020, -13969, 1133, 15827, 24796, 24796, + 15827, 1134, -13969, -24020, -25383, -17565, -3393, 12005, + 23062, 25777, 19169, 5627, -9949, -21928, -25975, -20627, + -7818, 7818, 20627, 25975, 21928, 9949, -5627, -19169, + -25777, -23062, -12005, 3393, 17565, 25383, 24020, 13969, + -1133, -15827, -24796, -24796, -15827, -1134, 13969, 24020, + 25383, 17565, 3393, -12005, -23062, -25777, -19169, -5627, + 9949, 21928, 25975, 20627, 7818, -7818, -20627, -25975, + -21928, -9950, 5627, 19169, 25777, 23062, 12005, -3393, + -17565, -25383, -24020, -13970, 1133, 15827, 24796, 24796, + 15828, 1134, -13969, -24020, -25383, -17565, -3393, 12005, + 23062, 25777, 19169, 5627, -9949, -21928, -25975, -20627, + -7818, 7818, 20626, 25975, 21928, 9950, -5627, -19168, + -25777, -23062, -12005, 3393, 17565, 25383, 24020, 13970, + -1133, -15827, -24796, -24796, -15828, -1134, 13969, 24020, + 25383, 17565, 3394, -12005, -23062, -25777, -19169, -5627}, + { +// Carrier 15 Phase 2 + 18384, 25605, 23564, 13000, -2266, -16712, -25114, -24432, + -14913, 0, 14912, 24432, 25114, 16712, 2266, -12999, + -23563, -25605, -18384, -4514, 10988, 22516, 25901, 19917, + 6729, -8892, -21297, -26000, -21297, -8892, 6729, 19917, + 25901, 22516, 10988, -4514, -18384, -25604, -23564, -13000, + 2265, 16712, 25114, 24432, 14913, 0, -14912, -24431, + -25114, -16712, -2266, 12999, 23563, 25605, 18384, 4514, + -10987, -22516, -25901, -19917, -6729, 8892, 21297, 26000, + 21298, 8892, -6729, -19917, -25901, -22516, -10988, 4514, + 18384, 25604, 23564, 13000, -2265, -16712, -25114, -24432, + -14913, 0, 14912, 24431, 25114, 16712, 2266, -12999, + -23563, -25605, -18384, -4515, 10987, 22516, 25901, 19917, + 6729, -8892, -21297, -26000, -21298, -8892, 6729, 19917, + 25901, 22516, 10988, -4514, -18384, -25604, -23564, -13000, + 2265, 16712, 25114, 24432, 14913, 0, -14912, -24431, + -25114, -16712, -2266, 12999, 23563, 25605, 18384, 4515, + -10987, -22516, -25901, -19917, -6729, 8892, 21297, 26000, + 21298, 8892, -6729, -19916, -25901, -22516, -10988, 4514, + 18384, 25604, 23564, 13000, -2265, -16712, -25114, -24432, + -14913, 0, 14912, 24431, 25114, 16712, 2266, -12999, + -23563, -25605, -18384, -4515, 10987, 22516, 25901, 19917, + 6729, -8892, -21297, -26000, -21298, -8892, 6728, 19916, + 25901, 22516, 10988, -4514, -18384, -25604, -23564, -13000, + 2265, 16712, 25113, 24432, 14913, 0, -14912, -24431, + -25114, -16712, -2266, 12999, 23563, 25605, 18385, 4515, + -10987, -22516, -25901, -19917, -6729, 8892, 21297, 26000, + 21298, 8892, -6728, -19916, -25901, -22516, -10988, 4514}, + { +// Carrier 15 Phase 3 + 24020, 25383, 17565, 3393, -12005, -23062, -25777, -19169, + -5627, 9949, 21928, 25975, 20627, 7818, -7818, -20627, + -25975, -21928, -9949, 5627, 19169, 25777, 23062, 12005, + -3393, -17565, -25383, -24020, -13969, 1134, 15827, 24796, + 24796, 15827, 1134, -13969, -24020, -25383, -17565, -3393, + 12005, 23062, 25777, 19169, 5627, -9949, -21928, -25975, + -20627, -7818, 7818, 20627, 25975, 21928, 9949, -5627, + -19169, -25777, -23062, -12005, 3393, 17565, 25383, 24020, + 13969, -1133, -15827, -24796, -24796, -15827, -1134, 13969, + 24020, 25383, 17565, 3393, -12005, -23062, -25777, -19169, + -5627, 9949, 21928, 25975, 20627, 7818, -7818, -20627, + -25975, -21928, -9949, 5627, 19169, 25777, 23062, 12005, + -3393, -17565, -25383, -24020, -13969, 1133, 15827, 24796, + 24796, 15827, 1134, -13969, -24020, -25383, -17565, -3393, + 12005, 23062, 25777, 19169, 5627, -9949, -21928, -25975, + -20627, -7818, 7818, 20627, 25975, 21928, 9949, -5627, + -19169, -25777, -23062, -12005, 3393, 17565, 25383, 24020, + 13969, -1133, -15827, -24796, -24796, -15827, -1134, 13969, + 24020, 25383, 17565, 3393, -12005, -23062, -25777, -19169, + -5627, 9949, 21928, 25975, 20627, 7818, -7818, -20627, + -25975, -21928, -9950, 5627, 19169, 25777, 23062, 12005, + -3393, -17565, -25383, -24020, -13970, 1133, 15827, 24796, + 24796, 15828, 1134, -13969, -24020, -25383, -17565, -3394, + 12005, 23062, 25777, 19169, 5627, -9949, -21927, -25975, + -20627, -7818, 7818, 20626, 25975, 21928, 9950, -5627, + -19168, -25777, -23062, -12005, 3393, 17565, 25383, 24021, + 13970, -1133, -15827, -24796, -24796, -15828, -1134, 13969}, + { +// Carrier 15 Phase 4 + 26000, 21297, 8892, -6729, -19917, -25901, -22516, -10988, + 4514, 18384, 25604, 23564, 13000, -2266, -16712, -25114, + -24432, -14913, 0, 14912, 24431, 25114, 16712, 2266, + -12999, -23563, -25605, -18384, -4514, 10988, 22516, 25901, + 19917, 6729, -8892, -21297, -26000, -21297, -8892, 6729, + 19917, 25901, 22516, 10988, -4514, -18384, -25604, -23564, + -13000, 2265, 16712, 25114, 24432, 14913, 0, -14912, + -24431, -25114, -16712, -2266, 12999, 23563, 25605, 18384, + 4514, -10987, -22516, -25901, -19917, -6729, 8892, 21297, + 26000, 21298, 8892, -6729, -19917, -25901, -22516, -10988, + 4514, 18384, 25604, 23564, 13000, -2265, -16712, -25114, + -24432, -14913, 0, 14912, 24431, 25114, 16712, 2266, + -12999, -23563, -25605, -18384, -4515, 10987, 22516, 25901, + 19917, 6729, -8892, -21297, -26000, -21298, -8892, 6729, + 19917, 25901, 22516, 10988, -4514, -18384, -25604, -23564, + -13000, 2265, 16712, 25114, 24432, 14913, 0, -14912, + -24431, -25114, -16712, -2266, 12999, 23563, 25605, 18384, + 4515, -10987, -22516, -25901, -19917, -6729, 8892, 21297, + 26000, 21298, 8892, -6729, -19916, -25901, -22516, -10988, + 4514, 18384, 25604, 23564, 13000, -2265, -16712, -25114, + -24432, -14913, 0, 14912, 24431, 25114, 16712, 2266, + -12999, -23563, -25605, -18384, -4515, 10987, 22516, 25901, + 19917, 6729, -8892, -21297, -26000, -21298, -8892, 6728, + 19916, 25901, 22516, 10988, -4514, -18384, -25604, -23564, + -13000, 2265, 16712, 25113, 24432, 14913, 0, -14912, + -24431, -25114, -16712, -2266, 12999, 23563, 25605, 18385, + 4515, -10987, -22516, -25901, -19917, -6729, 8892, 21297}, + { +// Carrier 15 Phase 5 + 24020, 13969, -1134, -15827, -24796, -24796, -15827, -1134, + 13969, 24020, 25383, 17565, 3393, -12005, -23062, -25777, + -19169, -5627, 9949, 21928, 25975, 20627, 7818, -7818, + -20627, -25975, -21928, -9949, 5627, 19169, 25777, 23062, + 12005, -3393, -17565, -25383, -24020, -13969, 1134, 15827, + 24796, 24796, 15827, 1134, -13969, -24020, -25383, -17565, + -3393, 12005, 23062, 25777, 19169, 5627, -9949, -21928, + -25975, -20627, -7818, 7818, 20627, 25975, 21928, 9949, + -5627, -19169, -25777, -23062, -12005, 3393, 17565, 25383, + 24020, 13969, -1133, -15827, -24796, -24796, -15827, -1134, + 13969, 24020, 25383, 17565, 3393, -12005, -23062, -25777, + -19169, -5627, 9949, 21928, 25975, 20627, 7818, -7818, + -20627, -25975, -21928, -9949, 5627, 19169, 25777, 23062, + 12005, -3393, -17565, -25383, -24020, -13969, 1133, 15827, + 24796, 24796, 15827, 1134, -13969, -24020, -25383, -17565, + -3393, 12005, 23062, 25777, 19169, 5627, -9949, -21928, + -25975, -20627, -7818, 7818, 20627, 25975, 21928, 9949, + -5627, -19169, -25777, -23062, -12005, 3393, 17565, 25383, + 24020, 13970, -1133, -15827, -24796, -24796, -15828, -1134, + 13969, 24020, 25383, 17565, 3393, -12005, -23062, -25777, + -19169, -5627, 9949, 21928, 25975, 20627, 7818, -7818, + -20626, -25975, -21928, -9950, 5627, 19168, 25777, 23062, + 12005, -3393, -17565, -25383, -24020, -13970, 1133, 15827, + 24796, 24796, 15828, 1134, -13969, -24020, -25383, -17565, + -3394, 12005, 23062, 25777, 19169, 5627, -9949, -21927, + -25975, -20627, -7818, 7818, 20626, 25975, 21928, 9950, + -5627, -19168, -25777, -23062, -12005, 3393, 17565, 25383}, + { +// Carrier 15 Phase 6 + 18384, 4514, -10988, -22516, -25901, -19917, -6729, 8892, + 21297, 26000, 21297, 8892, -6729, -19917, -25901, -22516, + -10988, 4514, 18384, 25604, 23564, 13000, -2266, -16712, + -25114, -24432, -14913, 0, 14912, 24431, 25114, 16712, + 2266, -12999, -23563, -25605, -18384, -4514, 10988, 22516, + 25901, 19917, 6729, -8892, -21297, -26000, -21298, -8892, + 6729, 19917, 25901, 22516, 10988, -4514, -18384, -25604, + -23564, -13000, 2265, 16712, 25114, 24432, 14913, 0, + -14912, -24431, -25114, -16712, -2266, 12999, 23563, 25605, + 18384, 4514, -10987, -22516, -25901, -19917, -6729, 8892, + 21297, 26000, 21298, 8892, -6729, -19917, -25901, -22516, + -10988, 4514, 18384, 25604, 23564, 13000, -2265, -16712, + -25114, -24432, -14913, 0, 14912, 24431, 25114, 16712, + 2266, -12999, -23563, -25605, -18384, -4515, 10987, 22516, + 25901, 19917, 6729, -8892, -21297, -26000, -21298, -8892, + 6729, 19917, 25901, 22516, 10988, -4514, -18384, -25604, + -23564, -13000, 2265, 16712, 25114, 24432, 14913, 0, + -14912, -24431, -25114, -16712, -2266, 12999, 23563, 25605, + 18384, 4515, -10987, -22516, -25901, -19917, -6729, 8892, + 21297, 26000, 21298, 8892, -6729, -19916, -25901, -22516, + -10988, 4514, 18384, 25604, 23564, 13000, -2265, -16712, + -25113, -24432, -14913, 0, 14912, 24431, 25114, 16712, + 2266, -12999, -23563, -25605, -18384, -4515, 10987, 22516, + 25901, 19917, 6729, -8892, -21297, -26000, -21298, -8892, + 6728, 19916, 25901, 22516, 10988, -4514, -18384, -25604, + -23564, -13000, 2265, 16712, 25113, 24432, 14913, 0, + -14912, -24431, -25114, -16712, -2266, 12999, 23563, 25605}, + { +// Carrier 15 Phase 7 + 9949, -5627, -19169, -25777, -23062, -12005, 3393, 17565, + 25383, 24020, 13969, -1134, -15827, -24796, -24796, -15827, + -1134, 13969, 24020, 25383, 17565, 3393, -12005, -23062, + -25777, -19169, -5627, 9949, 21928, 25975, 20627, 7818, + -7818, -20627, -25975, -21928, -9949, 5627, 19169, 25777, + 23062, 12005, -3393, -17565, -25383, -24020, -13969, 1134, + 15827, 24796, 24796, 15827, 1134, -13969, -24020, -25383, + -17565, -3393, 12005, 23062, 25777, 19169, 5627, -9949, + -21928, -25975, -20627, -7818, 7818, 20627, 25975, 21928, + 9949, -5627, -19169, -25777, -23062, -12005, 3393, 17565, + 25383, 24020, 13969, -1133, -15827, -24796, -24796, -15827, + -1134, 13969, 24020, 25383, 17565, 3393, -12005, -23062, + -25777, -19169, -5627, 9949, 21928, 25975, 20627, 7818, + -7818, -20627, -25975, -21928, -9949, 5627, 19169, 25777, + 23062, 12005, -3393, -17565, -25383, -24020, -13969, 1133, + 15827, 24796, 24796, 15827, 1134, -13969, -24020, -25383, + -17565, -3393, 12005, 23062, 25777, 19169, 5627, -9949, + -21928, -25975, -20627, -7818, 7818, 20627, 25975, 21928, + 9949, -5627, -19169, -25777, -23062, -12005, 3393, 17565, + 25383, 24020, 13970, -1133, -15827, -24796, -24796, -15828, + -1134, 13969, 24020, 25383, 17565, 3393, -12005, -23062, + -25777, -19169, -5627, 9949, 21928, 25975, 20627, 7818, + -7818, -20626, -25975, -21928, -9950, 5627, 19168, 25777, + 23062, 12005, -3393, -17565, -25383, -24020, -13970, 1133, + 15827, 24796, 24796, 15828, 1134, -13969, -24020, -25383, + -17565, -3394, 12005, 23062, 25777, 19169, 5627, -9949, + -21927, -25975, -20627, -7818, 7818, 20626, 25975, 21928}, + },{{ + +// Carrier 16 Phase 0 + 0, 15526, 24907, 24432, 14287, -1511, -16712, -25299, + -23873, -13000, 3018, 17842, 25605, 23234, 11668, -4514, + -18911, -25824, -22516, -10298, 5996, 19917, 25956, 21722, + 8892, -7456, -20855, -26000, -20855, -7456, 8892, 21722, + 25956, 19917, 5996, -10298, -22516, -25824, -18911, -4514, + 11668, 23234, 25604, 17842, 3018, -13000, -23873, -25299, + -16712, -1511, 14287, 24432, 24907, 15526, 0, -15526, + -24907, -24432, -14287, 1511, 16712, 25299, 23873, 12999, + -3018, -17842, -25605, -23234, -11668, 4514, 18911, 25824, + 22516, 10298, -5996, -19917, -25956, -21722, -8892, 7456, + 20855, 26000, 20855, 7456, -8892, -21722, -25956, -19917, + -5995, 10298, 22516, 25824, 18911, 4514, -11668, -23234, + -25604, -17842, -3018, 13000, 23873, 25299, 16712, 1511, + -14287, -24432, -24907, -15526, 0, 15526, 24907, 24431, + 14287, -1511, -16712, -25299, -23873, -12999, 3018, 17842, + 25605, 23234, 11668, -4514, -18911, -25824, -22516, -10298, + 5996, 19917, 25956, 21722, 8892, -7456, -20855, -26000, + -20855, -7456, 8892, 21722, 25956, 19917, 5995, -10298, + -22516, -25824, -18911, -4514, 11668, 23234, 25604, 17842, + 3018, -13000, -23873, -25299, -16712, -1511, 14287, 24432, + 24907, 15526, 0, -15526, -24907, -24431, -14287, 1511, + 16712, 25299, 23873, 12999, -3018, -17842, -25605, -23234, + -11668, 4514, 18911, 25824, 22516, 10298, -5996, -19917, + -25956, -21722, -8892, 7456, 20855, 26000, 20855, 7456, + -8892, -21722, -25956, -19917, -5995, 10298, 22516, 25824, + 18911, 4514, -11668, -23234, -25604, -17842, -3018, 13000, + 23873, 25299, 16712, 1511, -14287, -24432, -24907, -15526}, + { +// Carrier 16 Phase 1 + 9949, 22325, 25865, 19169, 4886, -11329, -23062, -25667, + -18115, -3393, 12671, 23721, 25383, 17000, 1889, -13969, + -24300, -25013, -15827, -378, 15221, 24796, 24558, 14601, + -1134, -16421, -25209, -24020, -13326, 2642, 17565, 25536, + 23401, 12005, -4141, -18650, -25777, -22703, -10644, 5627, + 19671, 25931, 21928, 9246, -7093, -20627, -25997, -21078, + -7818, 8536, 21512, 25975, 20158, 6363, -9949, -22325, + -25865, -19169, -4886, 11329, 23062, 25667, 18115, 3393, + -12671, -23721, -25383, -17000, -1889, 13969, 24300, 25013, + 15827, 378, -15221, -24796, -24558, -14601, 1134, 16421, + 25209, 24020, 13326, -2642, -17565, -25536, -23401, -12005, + 4142, 18650, 25777, 22703, 10644, -5627, -19672, -25931, + -21928, -9246, 7093, 20627, 25997, 21078, 7818, -8536, + -21512, -25975, -20158, -6363, 9949, 22325, 25865, 19169, + 4886, -11329, -23062, -25667, -18115, -3393, 12671, 23721, + 25383, 17000, 1889, -13969, -24300, -25013, -15827, -378, + 15221, 24796, 24558, 14601, -1134, -16421, -25209, -24020, + -13326, 2642, 17565, 25536, 23401, 12005, -4142, -18650, + -25777, -22703, -10644, 5627, 19672, 25931, 21928, 9246, + -7093, -20627, -25997, -21078, -7818, 8536, 21512, 25975, + 20158, 6363, -9949, -22325, -25865, -19169, -4886, 11329, + 23062, 25667, 18115, 3393, -12671, -23721, -25383, -17000, + -1889, 13969, 24300, 25013, 15827, 378, -15221, -24796, + -24558, -14601, 1134, 16421, 25209, 24020, 13326, -2642, + -17565, -25536, -23401, -12005, 4142, 18650, 25777, 22703, + 10644, -5627, -19672, -25931, -21928, -9246, 7093, 20627, + 25997, 21078, 7818, -8536, -21512, -25975, -20158, -6363}, + { +// Carrier 16 Phase 2 + 18384, 25725, 22885, 10988, -5257, -19422, -25901, -22129, + -9599, 6729, 20394, 25989, 21297, 8178, -8178, -21297, + -25989, -20394, -6729, 9599, 22129, 25901, 19422, 5257, + -10988, -22885, -25725, -18384, -3768, 12339, 23564, 25462, + 17284, 2266, -13649, -24163, -25114, -16126, -756, 14913, + 24680, 24680, 14912, -756, -16126, -25114, -24163, -13649, + 2266, 17284, 25462, 23563, 12339, -3768, -18384, -25725, + -22885, -10988, 5257, 19422, 25901, 22129, 9599, -6729, + -20394, -25989, -21297, -8178, 8178, 21297, 25989, 20394, + 6729, -9599, -22129, -25901, -19422, -5257, 10988, 22885, + 25725, 18384, 3768, -12339, -23564, -25462, -17284, -2266, + 13649, 24163, 25114, 16126, 756, -14913, -24680, -24680, + -14912, 756, 16126, 25114, 24163, 13649, -2266, -17284, + -25462, -23563, -12339, 3768, 18384, 25725, 22885, 10988, + -5257, -19422, -25901, -22129, -9599, 6729, 20394, 25989, + 21297, 8178, -8178, -21297, -25989, -20394, -6729, 9599, + 22129, 25901, 19422, 5257, -10988, -22885, -25725, -18384, + -3768, 12339, 23564, 25462, 17284, 2266, -13649, -24163, + -25114, -16126, -756, 14913, 24680, 24680, 14912, -756, + -16126, -25114, -24163, -13649, 2266, 17284, 25462, 23563, + 12339, -3768, -18384, -25725, -22885, -10988, 5257, 19422, + 25901, 22129, 9599, -6729, -20394, -25989, -21297, -8178, + 8178, 21297, 25988, 20394, 6729, -9599, -22129, -25901, + -19422, -5257, 10988, 22885, 25725, 18384, 3768, -12339, + -23564, -25462, -17284, -2265, 13649, 24163, 25114, 16126, + 756, -14913, -24680, -24680, -14912, 756, 16126, 25114, + 24163, 13649, -2266, -17284, -25462, -23563, -12339, 3768}, + { +// Carrier 16 Phase 3 + 24020, 25209, 16421, 1134, -14601, -24558, -24796, -15221, + 378, 15827, 25013, 24300, 13969, -1889, -17000, -25383, + -23721, -12671, 3393, 18115, 25667, 23062, 11329, -4886, + -19169, -25865, -22325, -9949, 6363, 20158, 25975, 21512, + 8536, -7818, -21078, -25997, -20627, -7093, 9246, 21928, + 25931, 19671, 5627, -10644, -22703, -25777, -18650, -4141, + 12005, 23401, 25536, 17565, 2642, -13326, -24020, -25209, + -16421, -1134, 14601, 24558, 24796, 15221, -378, -15827, + -25013, -24300, -13969, 1889, 17000, 25383, 23721, 12671, + -3393, -18115, -25667, -23062, -11329, 4886, 19169, 25865, + 22325, 9949, -6363, -20158, -25975, -21512, -8536, 7818, + 21078, 25997, 20627, 7093, -9246, -21928, -25931, -19671, + -5627, 10644, 22703, 25777, 18650, 4141, -12005, -23401, + -25536, -17565, -2642, 13326, 24020, 25209, 16421, 1134, + -14601, -24558, -24796, -15221, 378, 15827, 25013, 24300, + 13969, -1889, -17000, -25383, -23721, -12671, 3393, 18115, + 25667, 23062, 11329, -4886, -19169, -25865, -22325, -9949, + 6363, 20158, 25975, 21512, 8536, -7818, -21078, -25997, + -20627, -7093, 9246, 21928, 25931, 19671, 5627, -10644, + -22703, -25777, -18650, -4141, 12005, 23401, 25536, 17565, + 2642, -13326, -24020, -25209, -16421, -1134, 14601, 24558, + 24796, 15221, -378, -15827, -25013, -24300, -13969, 1889, + 17000, 25383, 23721, 12671, -3393, -18115, -25667, -23062, + -11329, 4886, 19169, 25865, 22325, 9949, -6363, -20158, + -25975, -21512, -8536, 7818, 21078, 25997, 20627, 7093, + -9246, -21928, -25931, -19671, -5627, 10644, 22703, 25777, + 18650, 4141, -12005, -23401, -25536, -17565, -2642, 13326}, + { +// Carrier 16 Phase 4 + 26000, 20855, 7456, -8892, -21722, -25956, -19917, -5996, + 10298, 22516, 25824, 18911, 4514, -11668, -23234, -25605, + -17842, -3018, 13000, 23873, 25299, 16712, 1511, -14287, + -24432, -24907, -15526, 0, 15526, 24907, 24432, 14287, + -1511, -16712, -25299, -23873, -12999, 3018, 17842, 25605, + 23234, 11668, -4514, -18911, -25824, -22516, -10298, 5996, + 19917, 25956, 21722, 8892, -7456, -20855, -26000, -20855, + -7456, 8892, 21722, 25956, 19917, 5995, -10298, -22516, + -25824, -18911, -4514, 11668, 23234, 25604, 17842, 3018, + -13000, -23873, -25299, -16712, -1511, 14287, 24432, 24907, + 15526, 0, -15526, -24907, -24432, -14287, 1511, 16712, + 25299, 23873, 12999, -3018, -17842, -25605, -23234, -11668, + 4514, 18911, 25824, 22516, 10298, -5996, -19917, -25956, + -21722, -8892, 7456, 20855, 26000, 20855, 7456, -8892, + -21722, -25956, -19917, -5995, 10298, 22516, 25824, 18911, + 4514, -11668, -23234, -25604, -17842, -3018, 13000, 23873, + 25299, 16712, 1511, -14287, -24432, -24907, -15526, 0, + 15526, 24907, 24431, 14287, -1511, -16712, -25299, -23873, + -12999, 3018, 17842, 25605, 23234, 11668, -4514, -18911, + -25824, -22516, -10298, 5996, 19917, 25956, 21722, 8892, + -7456, -20855, -26000, -20855, -7456, 8892, 21722, 25956, + 19917, 5995, -10298, -22516, -25824, -18911, -4514, 11668, + 23234, 25604, 17842, 3018, -13000, -23873, -25299, -16712, + -1511, 14287, 24432, 24907, 15526, 0, -15526, -24907, + -24431, -14287, 1511, 16712, 25299, 23873, 12999, -3018, + -17842, -25605, -23234, -11668, 4514, 18911, 25824, 22516, + 10298, -5996, -19917, -25956, -21722, -8892, 7456, 20855}, + { +// Carrier 16 Phase 5 + 24020, 13326, -2642, -17565, -25536, -23401, -12005, 4141, + 18650, 25777, 22703, 10644, -5627, -19671, -25931, -21928, + -9246, 7093, 20627, 25997, 21078, 7818, -8536, -21512, + -25975, -20158, -6363, 9949, 22325, 25865, 19169, 4886, + -11329, -23062, -25667, -18115, -3393, 12671, 23721, 25383, + 17000, 1889, -13969, -24300, -25013, -15827, -378, 15221, + 24796, 24558, 14601, -1134, -16421, -25209, -24020, -13326, + 2642, 17565, 25536, 23401, 12005, -4141, -18650, -25777, + -22703, -10644, 5627, 19672, 25931, 21928, 9246, -7093, + -20627, -25997, -21078, -7818, 8536, 21512, 25975, 20158, + 6363, -9949, -22325, -25865, -19169, -4886, 11329, 23062, + 25667, 18115, 3393, -12671, -23721, -25383, -17000, -1889, + 13969, 24300, 25013, 15827, 378, -15221, -24796, -24558, + -14601, 1134, 16421, 25209, 24020, 13326, -2642, -17565, + -25536, -23401, -12005, 4142, 18650, 25777, 22703, 10644, + -5627, -19672, -25931, -21928, -9246, 7093, 20627, 25997, + 21078, 7818, -8536, -21512, -25975, -20158, -6363, 9949, + 22325, 25865, 19169, 4886, -11329, -23062, -25667, -18115, + -3393, 12671, 23721, 25383, 17000, 1889, -13969, -24300, + -25013, -15827, -378, 15221, 24796, 24558, 14601, -1134, + -16421, -25209, -24020, -13326, 2642, 17565, 25536, 23401, + 12005, -4142, -18650, -25777, -22703, -10644, 5627, 19672, + 25931, 21928, 9246, -7093, -20627, -25997, -21078, -7818, + 8536, 21512, 25975, 20158, 6363, -9949, -22325, -25865, + -19169, -4886, 11329, 23062, 25667, 18115, 3393, -12671, + -23721, -25383, -17000, -1889, 13969, 24300, 25013, 15827, + 378, -15221, -24796, -24558, -14601, 1134, 16421, 25209}, + { +// Carrier 16 Phase 6 + 18384, 3768, -12339, -23564, -25462, -17284, -2266, 13649, + 24163, 25114, 16126, 756, -14912, -24680, -24680, -14912, + 756, 16126, 25114, 24163, 13649, -2266, -17284, -25462, + -23563, -12339, 3768, 18384, 25725, 22885, 10988, -5257, + -19422, -25901, -22129, -9599, 6729, 20394, 25989, 21297, + 8178, -8178, -21297, -25989, -20394, -6729, 9599, 22129, + 25901, 19422, 5257, -10988, -22885, -25725, -18384, -3768, + 12339, 23564, 25462, 17284, 2266, -13649, -24163, -25114, + -16126, -756, 14913, 24680, 24680, 14912, -756, -16126, + -25114, -24163, -13649, 2266, 17284, 25462, 23563, 12339, + -3768, -18384, -25725, -22885, -10988, 5257, 19422, 25901, + 22129, 9599, -6729, -20394, -25989, -21297, -8178, 8178, + 21297, 25989, 20394, 6729, -9599, -22129, -25901, -19422, + -5257, 10988, 22885, 25725, 18384, 3768, -12339, -23564, + -25462, -17284, -2266, 13649, 24163, 25114, 16126, 756, + -14913, -24680, -24680, -14912, 756, 16126, 25114, 24163, + 13649, -2266, -17284, -25462, -23563, -12339, 3768, 18384, + 25725, 22885, 10988, -5257, -19422, -25901, -22129, -9599, + 6729, 20394, 25989, 21297, 8178, -8178, -21297, -25988, + -20394, -6729, 9599, 22129, 25901, 19422, 5257, -10988, + -22885, -25725, -18384, -3768, 12339, 23564, 25462, 17284, + 2265, -13649, -24163, -25114, -16126, -756, 14913, 24680, + 24680, 14912, -756, -16126, -25114, -24163, -13649, 2266, + 17284, 25462, 23563, 12339, -3768, -18384, -25725, -22885, + -10988, 5257, 19422, 25901, 22128, 9599, -6729, -20394, + -25989, -21297, -8178, 8178, 21297, 25988, 20394, 6729, + -9599, -22129, -25901, -19422, -5257, 10988, 22885, 25725}, + { +// Carrier 16 Phase 7 + 9949, -6363, -20158, -25975, -21512, -8536, 7818, 21078, + 25997, 20627, 7093, -9246, -21928, -25931, -19671, -5627, + 10644, 22703, 25777, 18650, 4141, -12005, -23401, -25536, + -17565, -2642, 13326, 24020, 25209, 16421, 1134, -14601, + -24558, -24796, -15221, 378, 15827, 25013, 24300, 13969, + -1889, -17000, -25383, -23721, -12671, 3393, 18115, 25667, + 23062, 11329, -4886, -19169, -25865, -22325, -9949, 6363, + 20158, 25975, 21512, 8536, -7818, -21078, -25997, -20627, + -7093, 9246, 21928, 25931, 19671, 5627, -10644, -22703, + -25777, -18650, -4141, 12005, 23401, 25536, 17565, 2642, + -13326, -24020, -25209, -16421, -1134, 14601, 24558, 24796, + 15221, -378, -15827, -25013, -24300, -13969, 1889, 17000, + 25383, 23721, 12671, -3393, -18115, -25667, -23062, -11329, + 4886, 19169, 25865, 22325, 9949, -6363, -20158, -25975, + -21512, -8536, 7818, 21078, 25997, 20627, 7093, -9246, + -21928, -25931, -19671, -5627, 10644, 22703, 25777, 18650, + 4141, -12005, -23401, -25536, -17565, -2642, 13326, 24020, + 25209, 16421, 1134, -14601, -24558, -24796, -15221, 378, + 15827, 25013, 24300, 13969, -1889, -17000, -25383, -23721, + -12671, 3393, 18115, 25667, 23062, 11329, -4886, -19169, + -25865, -22325, -9949, 6363, 20158, 25975, 21512, 8536, + -7818, -21078, -25997, -20627, -7093, 9246, 21928, 25931, + 19671, 5627, -10644, -22703, -25777, -18650, -4141, 12005, + 23401, 25536, 17565, 2642, -13326, -24020, -25209, -16420, + -1134, 14601, 24558, 24796, 15221, -378, -15827, -25013, + -24300, -13969, 1889, 17000, 25383, 23721, 12671, -3393, + -18115, -25667, -23062, -11329, 4886, 19169, 25865, 22325}, + },{{ + +// Carrier 17 Phase 0 + 0, 16126, 25299, 23564, 11668, -5257, -19917, -25989, + -20855, -6729, 10298, 22885, 25604, 17284, 1511, -14913, + -24907, -24163, -12999, 3768, 18911, 25901, 21722, 8178, + -8892, -22129, -25824, -18384, -3018, 13649, 24432, 24680, + 14287, -2266, -17842, -25725, -22516, -9599, 7456, 21298, + 25956, 19422, 4514, -12339, -23873, -25114, -15526, 756, + 16712, 25462, 23234, 10987, -5996, -20394, -26000, -20394, + -5995, 10988, 23234, 25462, 16712, 756, -15526, -25114, + -23873, -12339, 4515, 19422, 25956, 21297, 7456, -9599, + -22516, -25725, -17842, -2265, 14287, 24680, 24431, 13649, + -3018, -18384, -25824, -22128, -8892, 8178, 21722, 25901, + 18911, 3768, -13000, -24163, -24907, -14912, 1511, 17284, + 25605, 22885, 10297, -6729, -20855, -25988, -19916, -5257, + 11669, 23564, 25299, 16125, 0, -16126, -25299, -23563, + -11668, 5257, 19917, 25989, 20855, 6729, -10298, -22885, + -25604, -17284, -1511, 14913, 24907, 24162, 12999, -3768, + -18911, -25901, -21722, -8177, 8892, 22129, 25824, 18384, + 3018, -13649, -24432, -24680, -14286, 2266, 17842, 25725, + 22516, 9599, -7457, -21298, -25955, -19422, -4514, 12339, + 23873, 25113, 15525, -756, -16712, -25462, -23234, -10987, + 5996, 20395, 26000, 20394, 5995, -10988, -23234, -25462, + -16712, -755, 15526, 25114, 23873, 12339, -4515, -19422, + -25956, -21297, -7456, 9599, 22516, 25725, 17841, 2265, + -14287, -24680, -24431, -13649, 3018, 18385, 25824, 22128, + 8892, -8178, -21722, -25901, -18911, -3767, 13000, 24163, + 24907, 14912, -1512, -17285, -25605, -22885, -10297, 6729, + 20855, 25988, 19916, 5257, -11669, -23564, -25299, -16125}, + { +// Carrier 17 Phase 1 + 9949, 22703, 25667, 17565, 1889, -14601, -24796, -24300, + -13326, 3393, 18650, 25865, 21928, 8536, -8536, -21928, + -25865, -18650, -3393, 13326, 24300, 24796, 14601, -1889, + -17565, -25667, -22703, -9949, 7093, 21078, 25975, 19671, + 4886, -12005, -23721, -25209, -15827, 378, 16421, 25383, + 23401, 11329, -5627, -20158, -25997, -20627, -6363, 10644, + 23062, 25536, 17000, 1133, -15221, -25013, -24020, -12671, + 4142, 19169, 25931, 21512, 7818, -9247, -22325, -25777, + -18115, -2642, 13969, 24558, 24558, 13969, -2642, -18115, + -25777, -22325, -9246, 7818, 21512, 25931, 19169, 4141, + -12671, -24020, -25013, -15221, 1134, 17000, 25536, 23062, + 10644, -6363, -20627, -25997, -20157, -5627, 11329, 23401, + 25383, 16420, 377, -15827, -25209, -23721, -12005, 4887, + 19672, 25975, 21078, 7093, -9949, -22703, -25667, -17565, + -1888, 14601, 24796, 24299, 13325, -3393, -18650, -25865, + -21928, -8535, 8536, 21928, 25865, 18650, 3393, -13326, + -24300, -24796, -14601, 1889, 17565, 25668, 22703, 9949, + -7094, -21078, -25975, -19671, -4886, 12005, 23721, 25209, + 15827, -378, -16421, -25383, -23401, -11329, 5627, 20158, + 25997, 20626, 6362, -10644, -23062, -25536, -17000, -1133, + 15221, 25013, 24020, 12670, -4142, -19169, -25931, -21512, + -7817, 9247, 22325, 25777, 18115, 2642, -13970, -24558, + -24558, -13969, 2642, 18115, 25777, 22324, 9246, -7818, + -21512, -25931, -19168, -4141, 12671, 24021, 25013, 15220, + -1134, -17000, -25536, -23062, -10643, 6363, 20627, 25997, + 20157, 5626, -11330, -23401, -25383, -16420, -377, 15828, + 25209, 23721, 12005, -4887, -19672, -25975, -21078, -7093}, + { +// Carrier 17 Phase 2 + 18384, 25824, 22129, 8892, -8178, -21722, -25901, -18911, + -3768, 13000, 24163, 24907, 14912, -1511, -17284, -25605, + -22885, -10298, 6729, 20855, 25989, 19917, 5257, -11668, + -23564, -25299, -16126, 0, 16126, 25299, 23563, 11668, + -5257, -19917, -25989, -20855, -6729, 10298, 22885, 25604, + 17284, 1511, -14913, -24907, -24163, -12999, 3768, 18911, + 25901, 21722, 8178, -8892, -22129, -25824, -18384, -3018, + 13649, 24432, 24680, 14287, -2266, -17842, -25725, -22516, + -9599, 7457, 21298, 25956, 19422, 4514, -12339, -23873, + -25114, -15525, 756, 16712, 25462, 23234, 10987, -5996, + -20394, -26000, -20394, -5995, 10988, 23234, 25462, 16712, + 755, -15526, -25114, -23873, -12339, 4515, 19422, 25956, + 21297, 7456, -9599, -22516, -25725, -17842, -2265, 14287, + 24680, 24431, 13649, -3018, -18384, -25824, -22128, -8892, + 8178, 21722, 25901, 18911, 3767, -13000, -24163, -24907, + -14912, 1512, 17284, 25605, 22885, 10297, -6729, -20855, + -25988, -19916, -5257, 11669, 23564, 25299, 16125, 0, + -16126, -25299, -23563, -11668, 5257, 19917, 25989, 20854, + 6728, -10298, -22885, -25604, -17284, -1511, 14913, 24907, + 24162, 12999, -3768, -18911, -25901, -21722, -8177, 8892, + 22129, 25824, 18384, 3018, -13649, -24432, -24680, -14286, + 2266, 17842, 25725, 22516, 9598, -7457, -21298, -25955, + -19422, -4514, 12339, 23873, 25113, 15525, -756, -16712, + -25462, -23234, -10987, 5996, 20395, 26000, 20394, 5995, + -10988, -23234, -25462, -16712, -755, 15526, 25114, 23873, + 12339, -4515, -19422, -25956, -21297, -7456, 9599, 22516, + 25725, 17841, 2265, -14287, -24680, -24431, -13648, 3018}, + { +// Carrier 17 Phase 3 + 24020, 25013, 15221, -1134, -17000, -25536, -23062, -10644, + 6363, 20627, 25997, 20158, 5627, -11329, -23401, -25383, + -16421, -378, 15827, 25209, 23721, 12005, -4886, -19672, + -25975, -21078, -7093, 9949, 22703, 25667, 17565, 1889, + -14601, -24796, -24300, -13326, 3393, 18650, 25865, 21928, + 8536, -8536, -21928, -25865, -18650, -3393, 13326, 24300, + 24796, 14601, -1889, -17565, -25667, -22703, -9949, 7093, + 21078, 25975, 19671, 4886, -12005, -23721, -25209, -15827, + 378, 16421, 25383, 23401, 11329, -5627, -20158, -25997, + -20627, -6363, 10644, 23062, 25536, 17000, 1133, -15221, + -25013, -24020, -12670, 4142, 19169, 25931, 21512, 7818, + -9247, -22325, -25777, -18115, -2642, 13969, 24558, 24558, + 13969, -2642, -18115, -25777, -22325, -9246, 7818, 21512, + 25931, 19169, 4141, -12671, -24020, -25013, -15220, 1134, + 17000, 25536, 23062, 10643, -6363, -20627, -25997, -20157, + -5627, 11329, 23401, 25383, 16420, 377, -15828, -25209, + -23721, -12005, 4887, 19672, 25975, 21078, 7093, -9950, + -22703, -25667, -17565, -1888, 14601, 24796, 24299, 13325, + -3394, -18650, -25865, -21927, -8535, 8536, 21928, 25865, + 18649, 3393, -13326, -24300, -24796, -14601, 1889, 17565, + 25668, 22703, 9949, -7094, -21079, -25975, -19671, -4886, + 12005, 23721, 25209, 15827, -378, -16421, -25383, -23401, + -11329, 5627, 20158, 25997, 20626, 6362, -10644, -23062, + -25536, -17000, -1133, 15221, 25013, 24020, 12670, -4142, + -19169, -25931, -21512, -7817, 9247, 22325, 25777, 18115, + 2642, -13970, -24558, -24558, -13969, 2642, 18115, 25777, + 22324, 9246, -7818, -21512, -25931, -19168, -4141, 12671}, + { +// Carrier 17 Phase 4 + 26000, 20394, 5996, -10988, -23234, -25462, -16712, -756, + 15526, 25114, 23873, 12339, -4514, -19422, -25956, -21297, + -7456, 9599, 22516, 25725, 17842, 2266, -14287, -24680, + -24431, -13649, 3018, 18384, 25824, 22129, 8892, -8178, + -21722, -25901, -18911, -3768, 13000, 24163, 24907, 14912, + -1511, -17284, -25605, -22885, -10297, 6729, 20855, 25988, + 19917, 5257, -11668, -23564, -25299, -16126, 0, 16126, + 25299, 23563, 11668, -5257, -19917, -25989, -20855, -6729, + 10298, 22885, 25604, 17284, 1511, -14913, -24907, -24162, + -12999, 3768, 18911, 25901, 21722, 8178, -8892, -22129, + -25824, -18384, -3018, 13649, 24432, 24680, 14287, -2266, + -17842, -25725, -22516, -9599, 7457, 21298, 25956, 19422, + 4514, -12339, -23873, -25114, -15525, 756, 16712, 25462, + 23234, 10987, -5996, -20394, -26000, -20394, -5995, 10988, + 23234, 25462, 16712, 755, -15526, -25114, -23873, -12339, + 4515, 19422, 25956, 21297, 7456, -9599, -22516, -25725, + -17842, -2265, 14287, 24680, 24431, 13649, -3018, -18384, + -25824, -22128, -8892, 8178, 21722, 25901, 18911, 3767, + -13000, -24163, -24907, -14912, 1512, 17284, 25605, 22885, + 10297, -6729, -20855, -25988, -19916, -5257, 11669, 23564, + 25299, 16125, 0, -16126, -25299, -23563, -11668, 5258, + 19917, 25989, 20854, 6728, -10298, -22885, -25604, -17284, + -1511, 14913, 24907, 24162, 12999, -3768, -18912, -25901, + -21722, -8177, 8892, 22129, 25824, 18384, 3017, -13649, + -24432, -24680, -14286, 2266, 17842, 25725, 22516, 9598, + -7457, -21298, -25955, -19422, -4514, 12340, 23873, 25113, + 15525, -756, -16712, -25462, -23234, -10987, 5996, 20395}, + { +// Carrier 17 Phase 5 + 24020, 12671, -4141, -19169, -25931, -21512, -7818, 9246, + 22325, 25777, 18115, 2642, -13969, -24558, -24558, -13969, + 2642, 18115, 25777, 22325, 9246, -7818, -21512, -25931, + -19169, -4141, 12671, 24020, 25013, 15221, -1134, -17000, + -25536, -23062, -10644, 6363, 20627, 25997, 20158, 5627, + -11329, -23401, -25383, -16420, -378, 15827, 25209, 23721, + 12005, -4886, -19672, -25975, -21078, -7093, 9949, 22703, + 25667, 17565, 1888, -14601, -24796, -24300, -13326, 3393, + 18650, 25865, 21928, 8536, -8536, -21928, -25865, -18650, + -3393, 13326, 24300, 24796, 14601, -1889, -17565, -25667, + -22703, -9949, 7094, 21078, 25975, 19671, 4886, -12005, + -23721, -25209, -15827, 378, 16421, 25383, 23401, 11329, + -5627, -20158, -25997, -20627, -6363, 10644, 23062, 25536, + 17000, 1133, -15221, -25013, -24020, -12670, 4142, 19169, + 25931, 21512, 7818, -9247, -22325, -25777, -18115, -2642, + 13970, 24558, 24558, 13969, -2642, -18115, -25777, -22325, + -9246, 7818, 21512, 25931, 19169, 4141, -12671, -24020, + -25013, -15220, 1134, 17000, 25536, 23062, 10643, -6363, + -20627, -25997, -20157, -5627, 11329, 23401, 25383, 16420, + 377, -15828, -25209, -23721, -12005, 4887, 19672, 25975, + 21078, 7093, -9950, -22703, -25667, -17565, -1888, 14601, + 24796, 24299, 13325, -3394, -18650, -25865, -21927, -8535, + 8536, 21928, 25865, 18649, 3393, -13326, -24300, -24796, + -14601, 1889, 17565, 25668, 22703, 9949, -7094, -21079, + -25975, -19671, -4886, 12005, 23721, 25209, 15827, -378, + -16421, -25383, -23401, -11329, 5627, 20158, 25997, 20626, + 6362, -10644, -23062, -25536, -17000, -1133, 15221, 25013}, + { +// Carrier 17 Phase 6 + 18384, 3018, -13649, -24432, -24680, -14287, 2266, 17842, + 25725, 22516, 9599, -7456, -21297, -25956, -19422, -4514, + 12339, 23873, 25114, 15526, -756, -16712, -25462, -23234, + -10988, 5996, 20394, 26000, 20394, 5995, -10988, -23234, + -25462, -16712, -756, 15526, 25114, 23873, 12339, -4514, + -19422, -25956, -21297, -7456, 9599, 22516, 25725, 17842, + 2265, -14287, -24680, -24431, -13649, 3018, 18384, 25824, + 22128, 8892, -8178, -21722, -25901, -18911, -3768, 13000, + 24163, 24907, 14912, -1511, -17284, -25605, -22885, -10297, + 6729, 20855, 25988, 19917, 5257, -11668, -23564, -25299, + -16125, 0, 16126, 25299, 23563, 11668, -5257, -19917, + -25989, -20855, -6729, 10298, 22885, 25604, 17284, 1511, + -14913, -24907, -24162, -12999, 3768, 18911, 25901, 21722, + 8177, -8892, -22129, -25824, -18384, -3018, 13649, 24432, + 24680, 14287, -2266, -17842, -25725, -22516, -9599, 7457, + 21298, 25955, 19422, 4514, -12339, -23873, -25113, -15525, + 756, 16712, 25462, 23234, 10987, -5996, -20395, -26000, + -20394, -5995, 10988, 23234, 25462, 16712, 755, -15526, + -25114, -23873, -12339, 4515, 19422, 25956, 21297, 7456, + -9599, -22516, -25725, -17842, -2265, 14287, 24680, 24431, + 13649, -3018, -18385, -25824, -22128, -8892, 8178, 21722, + 25901, 18911, 3767, -13000, -24163, -24907, -14912, 1512, + 17285, 25605, 22885, 10297, -6729, -20855, -25988, -19916, + -5257, 11669, 23564, 25299, 16125, 0, -16126, -25299, + -23563, -11668, 5258, 19917, 25989, 20854, 6728, -10298, + -22885, -25604, -17284, -1511, 14913, 24907, 24162, 12999, + -3768, -18912, -25901, -21722, -8177, 8893, 22129, 25824}, + { +// Carrier 17 Phase 7 + 9949, -7093, -21078, -25975, -19671, -4886, 12005, 23721, + 25209, 15827, -378, -16421, -25383, -23401, -11329, 5627, + 20158, 25997, 20627, 6363, -10644, -23062, -25536, -17000, + -1134, 15221, 25013, 24020, 12671, -4142, -19169, -25931, + -21512, -7818, 9247, 22325, 25777, 18115, 2642, -13969, + -24558, -24558, -13969, 2642, 18115, 25777, 22325, 9246, + -7818, -21512, -25931, -19169, -4141, 12671, 24020, 25013, + 15221, -1134, -17000, -25536, -23062, -10644, 6363, 20627, + 25997, 20158, 5627, -11329, -23401, -25383, -16420, -377, + 15827, 25209, 23721, 12005, -4886, -19672, -25975, -21078, + -7093, 9949, 22703, 25667, 17565, 1888, -14601, -24796, + -24300, -13325, 3393, 18650, 25865, 21928, 8536, -8536, + -21928, -25865, -18650, -3393, 13326, 24300, 24796, 14601, + -1889, -17565, -25667, -22703, -9949, 7094, 21078, 25975, + 19671, 4886, -12005, -23721, -25209, -15827, 378, 16421, + 25383, 23401, 11329, -5627, -20158, -25997, -20627, -6363, + 10644, 23062, 25536, 17000, 1133, -15221, -25013, -24020, + -12670, 4142, 19169, 25931, 21512, 7818, -9247, -22325, + -25777, -18115, -2642, 13970, 24558, 24558, 13969, -2642, + -18115, -25777, -22325, -9246, 7818, 21512, 25931, 19168, + 4141, -12671, -24021, -25013, -15220, 1134, 17000, 25536, + 23062, 10643, -6363, -20627, -25997, -20157, -5627, 11330, + 23401, 25383, 16420, 377, -15828, -25209, -23721, -12005, + 4887, 19672, 25975, 21078, 7093, -9950, -22703, -25667, + -17565, -1888, 14602, 24796, 24299, 13325, -3394, -18650, + -25865, -21927, -8535, 8536, 21928, 25865, 18649, 3393, + -13326, -24300, -24796, -14601, 1889, 17565, 25668, 22703}, + },{{ + +// Carrier 18 Phase 0 + 0, 16712, 25605, 22516, 8892, -8892, -22516, -25605, + -16712, 0, 16712, 25604, 22516, 8892, -8892, -22516, + -25605, -16712, 0, 16712, 25604, 22516, 8892, -8892, + -22516, -25605, -16712, 0, 16712, 25604, 22516, 8892, + -8892, -22516, -25605, -16712, 0, 16712, 25604, 22516, + 8892, -8892, -22516, -25605, -16712, 0, 16712, 25604, + 22516, 8892, -8892, -22516, -25605, -16712, 0, 16712, + 25604, 22516, 8892, -8892, -22516, -25605, -16712, 0, + 16712, 25604, 22516, 8892, -8892, -22516, -25605, -16712, + 0, 16712, 25604, 22516, 8892, -8892, -22516, -25605, + -16712, 0, 16712, 25604, 22516, 8892, -8892, -22516, + -25605, -16712, 0, 16712, 25604, 22516, 8892, -8892, + -22516, -25605, -16712, 0, 16712, 25604, 22516, 8892, + -8892, -22516, -25605, -16712, 0, 16712, 25604, 22516, + 8892, -8892, -22516, -25605, -16712, 0, 16712, 25604, + 22516, 8892, -8892, -22516, -25605, -16712, 0, 16712, + 25604, 22516, 8892, -8892, -22516, -25605, -16712, 0, + 16712, 25604, 22516, 8892, -8892, -22516, -25605, -16712, + 0, 16712, 25604, 22516, 8892, -8892, -22516, -25605, + -16712, 0, 16712, 25604, 22516, 8892, -8892, -22516, + -25605, -16712, 0, 16712, 25604, 22516, 8892, -8892, + -22516, -25605, -16712, 0, 16712, 25604, 22516, 8892, + -8892, -22516, -25605, -16712, 0, 16712, 25604, 22516, + 8892, -8892, -22516, -25605, -16712, 0, 16712, 25604, + 22516, 8892, -8892, -22516, -25605, -16712, 0, 16712, + 25604, 22516, 8892, -8892, -22516, -25605, -16712, 0, + 16712, 25604, 22516, 8892, -8892, -22516, -25605, -16712}, + { +// Carrier 18 Phase 1 + 9949, 23062, 25383, 15827, -1134, -17565, -25777, -21928, + -7818, 9949, 23062, 25383, 15827, -1134, -17565, -25777, + -21928, -7818, 9949, 23062, 25383, 15827, -1134, -17565, + -25777, -21928, -7818, 9949, 23062, 25383, 15827, -1134, + -17565, -25777, -21928, -7818, 9949, 23062, 25383, 15827, + -1134, -17565, -25777, -21928, -7818, 9949, 23062, 25383, + 15827, -1134, -17565, -25777, -21928, -7818, 9949, 23062, + 25383, 15827, -1134, -17565, -25777, -21928, -7818, 9949, + 23062, 25383, 15827, -1134, -17565, -25777, -21928, -7818, + 9949, 23062, 25383, 15827, -1134, -17565, -25777, -21928, + -7818, 9949, 23062, 25383, 15827, -1133, -17565, -25777, + -21928, -7818, 9949, 23062, 25383, 15827, -1133, -17565, + -25777, -21928, -7818, 9949, 23062, 25383, 15827, -1133, + -17565, -25777, -21928, -7818, 9949, 23062, 25383, 15827, + -1133, -17565, -25777, -21928, -7818, 9949, 23062, 25383, + 15827, -1133, -17565, -25777, -21928, -7818, 9949, 23062, + 25383, 15827, -1133, -17565, -25777, -21928, -7818, 9949, + 23062, 25383, 15827, -1133, -17565, -25777, -21928, -7818, + 9949, 23062, 25383, 15827, -1133, -17565, -25777, -21928, + -7818, 9949, 23062, 25383, 15827, -1133, -17565, -25777, + -21928, -7818, 9949, 23062, 25383, 15827, -1133, -17565, + -25777, -21928, -7818, 9949, 23062, 25383, 15827, -1133, + -17565, -25777, -21928, -7818, 9949, 23062, 25383, 15827, + -1133, -17565, -25777, -21928, -7818, 9949, 23062, 25383, + 15827, -1133, -17565, -25777, -21928, -7818, 9949, 23062, + 25383, 15828, -1133, -17565, -25777, -21928, -7818, 9949, + 23062, 25383, 15828, -1133, -17565, -25777, -21928, -7818}, + { +// Carrier 18 Phase 2 + 18384, 25901, 21297, 6729, -10988, -23563, -25114, -14913, + 2266, 18384, 25901, 21297, 6729, -10988, -23563, -25114, + -14913, 2266, 18384, 25901, 21297, 6729, -10988, -23563, + -25114, -14913, 2266, 18384, 25901, 21297, 6729, -10988, + -23563, -25114, -14913, 2265, 18384, 25901, 21297, 6729, + -10988, -23563, -25114, -14913, 2265, 18384, 25901, 21297, + 6729, -10988, -23563, -25114, -14913, 2265, 18384, 25901, + 21297, 6729, -10988, -23563, -25114, -14913, 2265, 18384, + 25901, 21298, 6729, -10987, -23563, -25114, -14913, 2265, + 18384, 25901, 21298, 6729, -10987, -23563, -25114, -14913, + 2265, 18384, 25901, 21298, 6729, -10987, -23563, -25114, + -14913, 2265, 18384, 25901, 21298, 6729, -10987, -23563, + -25114, -14913, 2265, 18384, 25901, 21298, 6729, -10987, + -23563, -25114, -14913, 2265, 18384, 25901, 21298, 6729, + -10987, -23563, -25114, -14913, 2265, 18384, 25901, 21298, + 6729, -10987, -23563, -25114, -14913, 2265, 18384, 25901, + 21298, 6729, -10987, -23563, -25114, -14913, 2265, 18384, + 25901, 21298, 6729, -10987, -23563, -25114, -14913, 2265, + 18384, 25901, 21298, 6729, -10987, -23563, -25114, -14913, + 2265, 18384, 25901, 21298, 6729, -10987, -23563, -25114, + -14913, 2265, 18384, 25901, 21298, 6729, -10987, -23563, + -25114, -14913, 2265, 18384, 25901, 21298, 6729, -10987, + -23563, -25114, -14913, 2265, 18384, 25901, 21298, 6729, + -10987, -23563, -25114, -14913, 2265, 18384, 25901, 21298, + 6729, -10987, -23563, -25114, -14913, 2265, 18384, 25901, + 21298, 6729, -10987, -23563, -25114, -14913, 2265, 18384, + 25901, 21298, 6729, -10987, -23563, -25114, -14913, 2265}, + { +// Carrier 18 Phase 3 + 24020, 24796, 13969, -3393, -19169, -25975, -20627, -5627, + 12005, 24020, 24796, 13969, -3393, -19169, -25975, -20627, + -5627, 12005, 24020, 24796, 13969, -3393, -19169, -25975, + -20627, -5627, 12005, 24020, 24796, 13969, -3393, -19169, + -25975, -20627, -5627, 12005, 24020, 24796, 13969, -3393, + -19169, -25975, -20627, -5627, 12005, 24020, 24796, 13969, + -3393, -19169, -25975, -20627, -5627, 12005, 24020, 24796, + 13969, -3393, -19169, -25975, -20627, -5627, 12005, 24020, + 24796, 13969, -3393, -19169, -25975, -20627, -5627, 12005, + 24020, 24796, 13969, -3393, -19169, -25975, -20627, -5627, + 12005, 24020, 24796, 13969, -3393, -19169, -25975, -20627, + -5627, 12005, 24020, 24796, 13969, -3393, -19169, -25975, + -20627, -5627, 12005, 24020, 24796, 13969, -3393, -19169, + -25975, -20627, -5627, 12005, 24020, 24796, 13969, -3393, + -19169, -25975, -20627, -5627, 12005, 24020, 24796, 13969, + -3393, -19169, -25975, -20627, -5627, 12005, 24020, 24796, + 13969, -3393, -19169, -25975, -20627, -5627, 12005, 24020, + 24796, 13969, -3393, -19169, -25975, -20627, -5627, 12005, + 24020, 24796, 13969, -3393, -19169, -25975, -20627, -5627, + 12005, 24020, 24796, 13969, -3393, -19169, -25975, -20627, + -5627, 12005, 24020, 24796, 13969, -3393, -19169, -25975, + -20627, -5627, 12005, 24020, 24796, 13969, -3393, -19169, + -25975, -20627, -5627, 12005, 24020, 24796, 13969, -3393, + -19169, -25975, -20627, -5627, 12005, 24020, 24796, 13970, + -3393, -19169, -25975, -20627, -5627, 12005, 24020, 24796, + 13970, -3393, -19169, -25975, -20627, -5627, 12005, 24020, + 24796, 13970, -3393, -19169, -25975, -20627, -5627, 12005}, + { +// Carrier 18 Phase 4 + 26000, 19917, 4514, -12999, -24432, -24432, -13000, 4514, + 19917, 26000, 19917, 4514, -12999, -24432, -24432, -13000, + 4514, 19917, 26000, 19917, 4514, -12999, -24431, -24432, + -13000, 4514, 19917, 26000, 19917, 4514, -12999, -24431, + -24432, -13000, 4514, 19917, 26000, 19917, 4514, -12999, + -24431, -24432, -13000, 4514, 19917, 26000, 19917, 4514, + -12999, -24431, -24432, -13000, 4514, 19917, 26000, 19917, + 4514, -12999, -24431, -24432, -13000, 4514, 19917, 26000, + 19917, 4514, -12999, -24431, -24432, -13000, 4514, 19917, + 26000, 19917, 4514, -12999, -24431, -24432, -13000, 4514, + 19917, 26000, 19917, 4514, -12999, -24431, -24432, -13000, + 4514, 19917, 26000, 19917, 4514, -12999, -24431, -24432, + -13000, 4514, 19917, 26000, 19917, 4514, -12999, -24431, + -24432, -13000, 4514, 19917, 26000, 19917, 4515, -12999, + -24431, -24432, -13000, 4514, 19917, 26000, 19917, 4515, + -12999, -24431, -24432, -13000, 4514, 19917, 26000, 19917, + 4515, -12999, -24431, -24432, -13000, 4514, 19917, 26000, + 19917, 4515, -12999, -24431, -24432, -13000, 4514, 19917, + 26000, 19917, 4515, -12999, -24431, -24432, -13000, 4514, + 19917, 26000, 19917, 4515, -12999, -24431, -24432, -13000, + 4514, 19917, 26000, 19917, 4515, -12999, -24431, -24432, + -13000, 4514, 19917, 26000, 19917, 4515, -12999, -24431, + -24432, -13000, 4514, 19916, 26000, 19917, 4515, -12999, + -24431, -24432, -13000, 4514, 19916, 26000, 19917, 4515, + -12999, -24431, -24432, -13000, 4514, 19916, 26000, 19917, + 4515, -12999, -24431, -24432, -13000, 4514, 19916, 26000, + 19917, 4515, -12999, -24431, -24432, -13000, 4514, 19916}, + { +// Carrier 18 Phase 5 + 24020, 12005, -5627, -20627, -25975, -19169, -3393, 13969, + 24796, 24020, 12005, -5627, -20627, -25975, -19169, -3393, + 13969, 24796, 24020, 12005, -5627, -20627, -25975, -19169, + -3393, 13969, 24796, 24020, 12005, -5627, -20627, -25975, + -19169, -3393, 13969, 24796, 24020, 12005, -5627, -20627, + -25975, -19169, -3393, 13969, 24796, 24020, 12005, -5627, + -20627, -25975, -19169, -3393, 13969, 24796, 24020, 12005, + -5627, -20627, -25975, -19169, -3393, 13969, 24796, 24020, + 12005, -5627, -20627, -25975, -19169, -3393, 13969, 24796, + 24020, 12005, -5627, -20627, -25975, -19169, -3393, 13969, + 24796, 24020, 12005, -5627, -20627, -25975, -19169, -3393, + 13969, 24796, 24020, 12005, -5627, -20627, -25975, -19169, + -3393, 13969, 24796, 24020, 12005, -5627, -20627, -25975, + -19169, -3393, 13969, 24796, 24020, 12005, -5627, -20627, + -25975, -19169, -3393, 13969, 24796, 24020, 12005, -5627, + -20627, -25975, -19169, -3393, 13969, 24796, 24020, 12005, + -5627, -20627, -25975, -19169, -3393, 13969, 24796, 24020, + 12005, -5627, -20627, -25975, -19169, -3393, 13969, 24796, + 24020, 12005, -5627, -20627, -25975, -19169, -3393, 13969, + 24796, 24020, 12005, -5627, -20627, -25975, -19169, -3393, + 13969, 24796, 24020, 12005, -5627, -20627, -25975, -19169, + -3393, 13969, 24796, 24020, 12005, -5627, -20627, -25975, + -19169, -3393, 13969, 24796, 24020, 12005, -5627, -20627, + -25975, -19169, -3393, 13969, 24796, 24020, 12005, -5627, + -20627, -25975, -19169, -3393, 13969, 24796, 24020, 12005, + -5627, -20627, -25975, -19169, -3393, 13969, 24796, 24020, + 12005, -5627, -20627, -25975, -19169, -3393, 13969, 24796}, + { +// Carrier 18 Phase 6 + 18384, 2266, -14912, -25114, -23564, -10988, 6729, 21297, + 25901, 18384, 2266, -14912, -25114, -23564, -10988, 6729, + 21297, 25901, 18384, 2266, -14912, -25114, -23564, -10988, + 6729, 21297, 25901, 18384, 2266, -14912, -25114, -23564, + -10988, 6729, 21297, 25901, 18384, 2266, -14912, -25114, + -23564, -10988, 6729, 21297, 25901, 18384, 2266, -14912, + -25114, -23564, -10988, 6729, 21297, 25901, 18384, 2266, + -14912, -25114, -23564, -10988, 6729, 21297, 25901, 18384, + 2266, -14912, -25114, -23564, -10988, 6729, 21297, 25901, + 18384, 2266, -14912, -25114, -23564, -10988, 6729, 21297, + 25901, 18384, 2266, -14912, -25114, -23564, -10988, 6729, + 21297, 25901, 18384, 2266, -14912, -25114, -23564, -10988, + 6729, 21297, 25901, 18384, 2266, -14912, -25114, -23564, + -10988, 6729, 21297, 25901, 18384, 2266, -14912, -25114, + -23564, -10988, 6729, 21297, 25901, 18384, 2266, -14912, + -25114, -23564, -10988, 6729, 21297, 25901, 18384, 2266, + -14912, -25114, -23564, -10988, 6729, 21297, 25901, 18384, + 2266, -14912, -25114, -23564, -10988, 6729, 21297, 25901, + 18384, 2266, -14912, -25114, -23564, -10988, 6729, 21297, + 25901, 18384, 2266, -14912, -25114, -23564, -10988, 6729, + 21297, 25901, 18384, 2266, -14912, -25114, -23564, -10988, + 6729, 21297, 25901, 18384, 2266, -14912, -25114, -23564, + -10988, 6729, 21297, 25901, 18384, 2266, -14912, -25114, + -23564, -10988, 6729, 21297, 25901, 18384, 2266, -14912, + -25114, -23564, -10988, 6729, 21297, 25901, 18384, 2266, + -14912, -25113, -23564, -10988, 6729, 21297, 25901, 18384, + 2266, -14912, -25113, -23564, -10988, 6729, 21297, 25901}, + { +// Carrier 18 Phase 7 + 9949, -7818, -21928, -25777, -17565, -1134, 15827, 25383, + 23062, 9949, -7818, -21928, -25777, -17565, -1134, 15827, + 25383, 23062, 9949, -7818, -21928, -25777, -17565, -1134, + 15827, 25383, 23062, 9949, -7818, -21928, -25777, -17565, + -1134, 15827, 25383, 23062, 9949, -7818, -21928, -25777, + -17565, -1134, 15827, 25383, 23062, 9949, -7818, -21928, + -25777, -17565, -1134, 15827, 25383, 23062, 9949, -7818, + -21928, -25777, -17565, -1134, 15827, 25383, 23062, 9949, + -7818, -21928, -25777, -17565, -1134, 15827, 25383, 23062, + 9949, -7818, -21928, -25777, -17565, -1134, 15827, 25383, + 23062, 9949, -7818, -21928, -25777, -17565, -1134, 15827, + 25383, 23062, 9949, -7818, -21928, -25777, -17565, -1134, + 15827, 25383, 23062, 9949, -7818, -21928, -25777, -17565, + -1134, 15827, 25383, 23062, 9949, -7818, -21928, -25777, + -17565, -1134, 15827, 25383, 23062, 9949, -7818, -21928, + -25777, -17565, -1134, 15827, 25383, 23062, 9949, -7818, + -21928, -25777, -17565, -1134, 15827, 25383, 23062, 9949, + -7818, -21928, -25777, -17565, -1134, 15827, 25383, 23062, + 9949, -7818, -21928, -25777, -17565, -1134, 15827, 25383, + 23062, 9949, -7818, -21928, -25777, -17565, -1134, 15827, + 25383, 23062, 9949, -7818, -21928, -25777, -17565, -1134, + 15827, 25383, 23062, 9949, -7818, -21928, -25777, -17565, + -1134, 15827, 25383, 23062, 9949, -7818, -21928, -25777, + -17565, -1134, 15827, 25383, 23062, 9950, -7818, -21928, + -25777, -17565, -1134, 15827, 25383, 23062, 9950, -7818, + -21928, -25777, -17565, -1134, 15827, 25383, 23062, 9950, + -7818, -21928, -25777, -17565, -1134, 15827, 25383, 23062}, + },{{ + +// Carrier 19 Phase 0 + 0, 17284, 25824, 21297, 5996, -12339, -24432, -24163, + -11668, 6729, 21722, 25725, 16712, -756, -17842, -25901, + -20855, -5257, 12999, 24680, 23873, 10988, -7456, -22129, + -25605, -16126, 1511, 18384, 25956, 20394, 4514, -13649, + -24907, -23564, -10298, 8178, 22516, 25462, 15526, -2266, + -18911, -25989, -19917, -3768, 14287, 25114, 23234, 9599, + -8892, -22885, -25299, -14912, 3018, 19422, 26000, 19422, + 3018, -14913, -25299, -22885, -8892, 9599, 23234, 25114, + 14287, -3768, -19917, -25989, -18911, -2266, 15526, 25462, + 22516, 8178, -10298, -23564, -24907, -13649, 4514, 20394, + 25956, 18384, 1511, -16126, -25605, -22129, -7456, 10988, + 23873, 24680, 12999, -5257, -20855, -25901, -17842, -756, + 16712, 25725, 21722, 6729, -11668, -24163, -24431, -12339, + 5996, 21297, 25824, 17284, 0, -17284, -25824, -21297, + -5995, 12339, 24432, 24163, 11668, -6729, -21722, -25725, + -16712, 756, 17842, 25901, 20855, 5257, -13000, -24680, + -23873, -10988, 7456, 22129, 25604, 16126, -1511, -18384, + -25956, -20394, -4514, 13649, 24907, 23563, 10297, -8178, + -22516, -25462, -15526, 2266, 18911, 25989, 19917, 3768, + -14287, -25114, -23234, -9599, 8892, 22885, 25299, 14912, + -3018, -19422, -26000, -19422, -3018, 14913, 25299, 22885, + 8892, -9599, -23234, -25114, -14287, 3768, 19917, 25988, + 18911, 2265, -15526, -25462, -22516, -8178, 10298, 23564, + 24907, 13649, -4514, -20394, -25956, -18384, -1511, 16126, + 25605, 22128, 7456, -10988, -23873, -24680, -12999, 5257, + 20855, 25901, 17842, 756, -16712, -25725, -21722, -6729, + 11668, 24163, 24431, 12339, -5996, -21298, -25824, -17284}, + { +// Carrier 19 Phase 1 + 9949, 23401, 25013, 13969, -4141, -20158, -25975, -18650, + -1889, 15827, 25536, 22325, 7818, -10644, -23721, -24796, + -13326, 4886, 20627, 25931, 18115, 1134, -16421, -25667, + -21928, -7093, 11329, 24020, 24558, 12671, -5627, -21078, + -25865, -17565, -378, 17000, 25777, 21512, 6363, -12005, + -24300, -24300, -12005, 6363, 21512, 25777, 17000, -378, + -17565, -25865, -21078, -5627, 12671, 24558, 24020, 11329, + -7093, -21928, -25667, -16421, 1134, 18115, 25931, 20627, + 4886, -13326, -24796, -23721, -10644, 7818, 22325, 25536, + 15827, -1889, -18650, -25975, -20158, -4141, 13969, 25013, + 23401, 9949, -8536, -22703, -25383, -15221, 2642, 19169, + 25997, 19671, 3393, -14601, -25209, -23062, -9246, 9246, + 23062, 25209, 14601, -3393, -19672, -25997, -19169, -2642, + 15221, 25383, 22703, 8536, -9949, -23401, -25013, -13969, + 4142, 20158, 25975, 18650, 1889, -15827, -25536, -22325, + -7818, 10644, 23721, 24796, 13326, -4886, -20627, -25931, + -18115, -1134, 16421, 25667, 21928, 7093, -11329, -24020, + -24558, -12671, 5627, 21078, 25865, 17565, 378, -17000, + -25777, -21512, -6363, 12005, 24300, 24300, 12005, -6363, + -21512, -25777, -17000, 378, 17565, 25865, 21078, 5627, + -12671, -24558, -24020, -11329, 7093, 21928, 25667, 16420, + -1134, -18115, -25931, -20627, -4886, 13326, 24796, 23721, + 10644, -7818, -22325, -25536, -15827, 1889, 18650, 25975, + 20158, 4141, -13969, -25013, -23401, -9949, 8536, 22703, + 25383, 15221, -2642, -19169, -25997, -19671, -3393, 14601, + 25209, 23062, 9246, -9247, -23062, -25209, -14601, 3393, + 19672, 25997, 19169, 2642, -15221, -25383, -22703, -8536}, + { +// Carrier 19 Phase 2 + 18384, 25956, 20394, 4514, -13649, -24907, -23564, -10298, + 8178, 22516, 25462, 15526, -2266, -18911, -25989, -19917, + -3768, 14287, 25114, 23234, 9599, -8892, -22885, -25299, + -14912, 3018, 19422, 26000, 19422, 3018, -14913, -25299, + -22885, -8892, 9599, 23234, 25114, 14287, -3768, -19917, + -25989, -18911, -2266, 15526, 25462, 22516, 8178, -10298, + -23564, -24907, -13649, 4514, 20394, 25956, 18384, 1511, + -16126, -25605, -22129, -7456, 10988, 23873, 24680, 12999, + -5257, -20855, -25901, -17842, -756, 16712, 25725, 21722, + 6729, -11668, -24163, -24431, -12339, 5996, 21297, 25824, + 17284, 0, -17284, -25824, -21297, -5995, 12339, 24432, + 24163, 11668, -6729, -21722, -25725, -16712, 756, 17842, + 25901, 20855, 5257, -13000, -24680, -23873, -10988, 7456, + 22129, 25604, 16126, -1511, -18384, -25956, -20394, -4514, + 13649, 24907, 23563, 10297, -8178, -22516, -25462, -15526, + 2266, 18911, 25989, 19917, 3768, -14287, -25114, -23234, + -9599, 8892, 22885, 25299, 14912, -3018, -19422, -26000, + -19422, -3018, 14913, 25299, 22885, 8892, -9599, -23234, + -25114, -14287, 3768, 19917, 25988, 18911, 2265, -15526, + -25462, -22516, -8178, 10298, 23564, 24907, 13649, -4514, + -20394, -25956, -18384, -1511, 16126, 25605, 22128, 7456, + -10988, -23873, -24680, -12999, 5257, 20855, 25901, 17842, + 756, -16712, -25725, -21722, -6729, 11668, 24163, 24431, + 12339, -5996, -21298, -25824, -17284, 0, 17284, 25824, + 21297, 5995, -12339, -24432, -24162, -11668, 6729, 21722, + 25725, 16712, -756, -17842, -25901, -20855, -5257, 13000, + 24680, 23873, 10987, -7457, -22129, -25604, -16126, 1511}, + { +// Carrier 19 Phase 3 + 24020, 24558, 12671, -5627, -21078, -25865, -17565, -378, + 17000, 25777, 21512, 6363, -12005, -24300, -24300, -12005, + 6363, 21512, 25777, 17000, -378, -17565, -25865, -21078, + -5627, 12671, 24558, 24020, 11329, -7093, -21928, -25667, + -16421, 1134, 18115, 25931, 20627, 4886, -13326, -24796, + -23721, -10644, 7818, 22325, 25536, 15827, -1889, -18650, + -25975, -20158, -4141, 13969, 25013, 23401, 9949, -8536, + -22703, -25383, -15221, 2642, 19169, 25997, 19671, 3393, + -14601, -25209, -23062, -9246, 9246, 23062, 25209, 14601, + -3393, -19672, -25997, -19169, -2642, 15221, 25383, 22703, + 8536, -9949, -23401, -25013, -13969, 4142, 20158, 25975, + 18650, 1889, -15827, -25536, -22325, -7818, 10644, 23721, + 24796, 13326, -4886, -20627, -25931, -18115, -1134, 16421, + 25667, 21928, 7093, -11329, -24020, -24558, -12671, 5627, + 21078, 25865, 17565, 378, -17000, -25777, -21512, -6363, + 12005, 24300, 24300, 12005, -6363, -21512, -25777, -17000, + 378, 17565, 25865, 21078, 5627, -12671, -24558, -24020, + -11329, 7093, 21928, 25667, 16420, -1134, -18115, -25931, + -20627, -4886, 13326, 24796, 23721, 10644, -7818, -22325, + -25536, -15827, 1889, 18650, 25975, 20158, 4141, -13969, + -25013, -23401, -9949, 8536, 22703, 25383, 15221, -2642, + -19169, -25997, -19671, -3393, 14601, 25209, 23062, 9246, + -9247, -23062, -25209, -14601, 3393, 19672, 25997, 19169, + 2642, -15221, -25383, -22703, -8536, 9949, 23401, 25013, + 13969, -4142, -20158, -25975, -18650, -1888, 15827, 25536, + 22325, 7818, -10644, -23721, -24796, -13325, 4886, 20627, + 25931, 18115, 1133, -16421, -25667, -21928, -7093, 11329}, + { +// Carrier 19 Phase 4 + 26000, 19422, 3018, -14912, -25299, -22885, -8892, 9599, + 23234, 25114, 14287, -3768, -19917, -25989, -18911, -2266, + 15526, 25462, 22516, 8178, -10298, -23564, -24907, -13649, + 4514, 20394, 25956, 18384, 1511, -16126, -25605, -22129, + -7456, 10988, 23873, 24680, 12999, -5257, -20855, -25901, + -17842, -756, 16712, 25725, 21722, 6729, -11668, -24163, + -24431, -12339, 5996, 21297, 25824, 17284, 0, -17284, + -25824, -21297, -5995, 12339, 24432, 24163, 11668, -6729, + -21722, -25725, -16712, 756, 17842, 25901, 20855, 5257, + -13000, -24680, -23873, -10988, 7456, 22129, 25604, 16126, + -1511, -18384, -25956, -20394, -4514, 13649, 24907, 23563, + 10298, -8178, -22516, -25462, -15526, 2266, 18911, 25989, + 19917, 3768, -14287, -25114, -23234, -9599, 8892, 22885, + 25299, 14912, -3018, -19422, -26000, -19422, -3018, 14913, + 25299, 22885, 8892, -9599, -23234, -25114, -14287, 3768, + 19917, 25988, 18911, 2265, -15526, -25462, -22516, -8178, + 10298, 23564, 24907, 13649, -4514, -20394, -25956, -18384, + -1511, 16126, 25605, 22128, 7456, -10988, -23873, -24680, + -12999, 5257, 20855, 25901, 17842, 756, -16712, -25725, + -21722, -6729, 11668, 24163, 24431, 12339, -5996, -21298, + -25824, -17284, 0, 17284, 25824, 21297, 5995, -12339, + -24432, -24162, -11668, 6729, 21722, 25725, 16712, -756, + -17842, -25901, -20855, -5257, 13000, 24680, 23873, 10987, + -7457, -22129, -25604, -16126, 1511, 18384, 25956, 20394, + 4514, -13649, -24907, -23563, -10297, 8178, 22516, 25462, + 15526, -2266, -18911, -25989, -19917, -3768, 14287, 25114, + 23234, 9599, -8892, -22885, -25299, -14912, 3018, 19422}, + { +// Carrier 19 Phase 5 + 24020, 11329, -7093, -21928, -25667, -16421, 1134, 18115, + 25931, 20627, 4886, -13326, -24796, -23721, -10644, 7818, + 22325, 25536, 15827, -1889, -18650, -25975, -20158, -4141, + 13969, 25013, 23401, 9949, -8536, -22703, -25383, -15221, + 2642, 19169, 25997, 19671, 3393, -14601, -25209, -23062, + -9246, 9246, 23062, 25209, 14601, -3393, -19672, -25997, + -19169, -2642, 15221, 25383, 22703, 8536, -9949, -23401, + -25013, -13969, 4142, 20158, 25975, 18650, 1889, -15827, + -25536, -22325, -7818, 10644, 23721, 24796, 13326, -4886, + -20627, -25931, -18115, -1134, 16421, 25667, 21928, 7093, + -11329, -24020, -24558, -12671, 5627, 21078, 25865, 17565, + 378, -17000, -25777, -21512, -6363, 12005, 24300, 24300, + 12005, -6363, -21512, -25777, -17000, 378, 17565, 25865, + 21078, 5627, -12671, -24558, -24020, -11329, 7093, 21928, + 25667, 16420, -1134, -18115, -25931, -20627, -4886, 13326, + 24796, 23721, 10644, -7818, -22325, -25536, -15827, 1889, + 18650, 25975, 20158, 4141, -13969, -25013, -23401, -9949, + 8536, 22703, 25383, 15221, -2642, -19169, -25997, -19671, + -3393, 14601, 25209, 23062, 9246, -9247, -23062, -25209, + -14601, 3393, 19672, 25997, 19169, 2642, -15221, -25383, + -22703, -8536, 9949, 23401, 25013, 13969, -4142, -20158, + -25975, -18650, -1888, 15827, 25536, 22325, 7818, -10644, + -23721, -24796, -13325, 4886, 20627, 25931, 18115, 1133, + -16421, -25667, -21928, -7093, 11329, 24020, 24558, 12671, + -5627, -21078, -25865, -17565, -378, 17000, 25777, 21512, + 6363, -12005, -24300, -24300, -12005, 6363, 21512, 25777, + 17000, -378, -17565, -25865, -21078, -5627, 12671, 24558}, + { +// Carrier 19 Phase 6 + 18384, 1511, -16126, -25605, -22129, -7456, 10988, 23873, + 24680, 12999, -5257, -20855, -25901, -17842, -756, 16712, + 25725, 21722, 6729, -11668, -24163, -24432, -12339, 5996, + 21297, 25824, 17284, 0, -17284, -25824, -21297, -5995, + 12339, 24432, 24163, 11668, -6729, -21722, -25725, -16712, + 756, 17842, 25901, 20855, 5257, -13000, -24680, -23873, + -10988, 7456, 22129, 25604, 16126, -1511, -18384, -25956, + -20394, -4514, 13649, 24907, 23563, 10298, -8178, -22516, + -25462, -15526, 2266, 18911, 25989, 19917, 3768, -14287, + -25114, -23234, -9599, 8892, 22885, 25299, 14912, -3018, + -19422, -26000, -19422, -3018, 14913, 25299, 22885, 8892, + -9599, -23234, -25114, -14287, 3768, 19917, 25988, 18911, + 2265, -15526, -25462, -22516, -8178, 10298, 23564, 24907, + 13649, -4514, -20394, -25956, -18384, -1511, 16126, 25605, + 22128, 7456, -10988, -23873, -24680, -12999, 5257, 20855, + 25901, 17842, 756, -16712, -25725, -21722, -6729, 11668, + 24163, 24431, 12339, -5996, -21298, -25824, -17284, 0, + 17284, 25824, 21297, 5995, -12339, -24432, -24162, -11668, + 6729, 21722, 25725, 16712, -756, -17842, -25901, -20855, + -5257, 13000, 24680, 23873, 10987, -7456, -22129, -25604, + -16126, 1511, 18384, 25956, 20394, 4514, -13649, -24907, + -23563, -10297, 8178, 22516, 25462, 15526, -2266, -18911, + -25989, -19917, -3768, 14287, 25114, 23234, 9599, -8892, + -22885, -25299, -14912, 3018, 19422, 26000, 19422, 3018, + -14913, -25299, -22885, -8892, 9599, 23234, 25114, 14287, + -3768, -19917, -25988, -18911, -2265, 15526, 25462, 22516, + 8178, -10298, -23564, -24907, -13649, 4515, 20394, 25956}, + { +// Carrier 19 Phase 7 + 9949, -8536, -22703, -25383, -15221, 2642, 19169, 25997, + 19671, 3393, -14601, -25209, -23062, -9246, 9246, 23062, + 25209, 14601, -3393, -19671, -25997, -19169, -2642, 15221, + 25383, 22703, 8536, -9949, -23401, -25013, -13969, 4142, + 20158, 25975, 18650, 1889, -15827, -25536, -22325, -7818, + 10644, 23721, 24796, 13326, -4886, -20627, -25931, -18115, + -1134, 16421, 25667, 21928, 7093, -11329, -24020, -24558, + -12671, 5627, 21078, 25865, 17565, 378, -17000, -25777, + -21512, -6363, 12005, 24300, 24300, 12005, -6363, -21512, + -25777, -17000, 378, 17565, 25865, 21078, 5627, -12671, + -24558, -24020, -11329, 7093, 21928, 25667, 16420, -1134, + -18115, -25931, -20627, -4886, 13326, 24796, 23721, 10644, + -7818, -22325, -25536, -15827, 1889, 18650, 25975, 20158, + 4141, -13969, -25013, -23401, -9949, 8536, 22703, 25383, + 15221, -2642, -19169, -25997, -19671, -3393, 14601, 25209, + 23062, 9246, -9246, -23062, -25209, -14601, 3393, 19672, + 25997, 19169, 2642, -15221, -25383, -22703, -8536, 9949, + 23401, 25013, 13969, -4142, -20158, -25975, -18650, -1889, + 15827, 25536, 22325, 7818, -10644, -23721, -24796, -13326, + 4886, 20627, 25931, 18115, 1133, -16421, -25667, -21928, + -7093, 11329, 24020, 24558, 12671, -5627, -21078, -25865, + -17565, -378, 17000, 25777, 21512, 6363, -12005, -24300, + -24300, -12005, 6363, 21512, 25777, 17000, -378, -17565, + -25865, -21078, -5627, 12671, 24558, 24020, 11329, -7093, + -21928, -25667, -16420, 1134, 18115, 25931, 20627, 4886, + -13326, -24796, -23721, -10644, 7818, 22325, 25536, 15827, + -1889, -18650, -25975, -20158, -4141, 13969, 25013, 23401}, + },{{ + +// Carrier 20 Phase 0 + 0, 17842, 25956, 19917, 3018, -15526, -25605, -21722, + -5996, 13000, 24907, 23234, 8892, -10298, -23873, -24432, + -11668, 7456, 22516, 25299, 14287, -4514, -20855, -25824, + -16712, 1511, 18911, 26000, 18911, 1511, -16712, -25824, + -20855, -4514, 14287, 25299, 22516, 7456, -11668, -24432, + -23873, -10297, 8892, 23234, 24907, 12999, -5996, -21722, + -25604, -15526, 3018, 19917, 25956, 17842, 0, -17842, + -25956, -19917, -3018, 15526, 25605, 21722, 5995, -13000, + -24907, -23234, -8892, 10298, 23873, 24431, 11668, -7457, + -22516, -25299, -14287, 4515, 20855, 25824, 16712, -1511, + -18911, -26000, -18911, -1511, 16712, 25824, 20855, 4514, + -14287, -25299, -22516, -7456, 11668, 24432, 23873, 10297, + -8892, -23234, -24907, -12999, 5996, 21722, 25604, 15525, + -3018, -19917, -25955, -17842, 0, 17842, 25956, 19916, + 3018, -15526, -25605, -21722, -5995, 13000, 24907, 23234, + 8892, -10298, -23873, -24431, -11668, 7457, 22516, 25299, + 14286, -4515, -20855, -25824, -16712, 1512, 18911, 26000, + 18911, 1511, -16712, -25824, -20855, -4514, 14287, 25299, + 22516, 7456, -11669, -24432, -23873, -10297, 8892, 23234, + 24907, 12999, -5996, -21722, -25604, -15525, 3018, 19917, + 25955, 17842, 0, -17842, -25956, -19916, -3018, 15526, + 25605, 21722, 5995, -13000, -24907, -23234, -8892, 10298, + 23873, 24431, 11668, -7457, -22516, -25299, -14286, 4515, + 20855, 25824, 16712, -1512, -18912, -26000, -18911, -1511, + 16712, 25824, 20854, 4514, -14287, -25299, -22516, -7456, + 11669, 24432, 23873, 10297, -8892, -23234, -24907, -12999, + 5996, 21722, 25604, 15525, -3018, -19917, -25955, -17841}, + { +// Carrier 20 Phase 1 + 9949, 23721, 24558, 12005, -7093, -22325, -25383, -14601, + 4142, 20627, 25865, 17000, -1134, -18650, -25997, -19169, + -1889, 16421, 25777, 21078, 4886, -13969, -25209, -22703, + -7818, 11329, 24300, 24020, 10644, -8536, -23062, -25013, + -13326, 5627, 21512, 25667, 15827, -2642, -19672, -25975, + -18115, -378, 17565, 25931, 20158, 3393, -15221, -25536, + -21928, -6363, 12671, 24796, 23401, 9246, -9949, -23721, + -24558, -12005, 7093, 22325, 25383, 14601, -4142, -20627, + -25865, -17000, 1134, 18650, 25997, 19169, 1888, -16421, + -25777, -21078, -4886, 13969, 25209, 22703, 7818, -11329, + -24300, -24020, -10644, 8536, 23062, 25013, 13325, -5627, + -21512, -25667, -15827, 2642, 19672, 25975, 18115, 377, + -17565, -25931, -20157, -3393, 15221, 25536, 21928, 6363, + -12671, -24796, -23401, -9246, 9950, 23721, 24558, 12005, + -7094, -22325, -25383, -14601, 4142, 20627, 25865, 17000, + -1134, -18650, -25997, -19169, -1888, 16421, 25777, 21078, + 4886, -13970, -25209, -22703, -7818, 11329, 24300, 24020, + 10643, -8536, -23062, -25013, -13325, 5627, 21512, 25667, + 15827, -2642, -19672, -25975, -18115, -377, 17565, 25931, + 20157, 3393, -15221, -25536, -21927, -6362, 12671, 24796, + 23401, 9246, -9950, -23721, -24558, -12005, 7094, 22325, + 25383, 14601, -4142, -20627, -25865, -17000, 1134, 18650, + 25997, 19168, 1888, -16421, -25777, -21078, -4886, 13970, + 25209, 22703, 7817, -11330, -24300, -24020, -10643, 8536, + 23062, 25013, 13325, -5627, -21512, -25667, -15827, 2642, + 19672, 25975, 18115, 377, -17565, -25931, -20157, -3393, + 15221, 25536, 21927, 6362, -12671, -24796, -23401, -9246}, + { +// Carrier 20 Phase 2 + 18384, 25989, 19422, 2266, -16126, -25725, -21297, -5257, + 13649, 25114, 22885, 8178, -10988, -24163, -24163, -10988, + 8178, 22885, 25114, 13649, -5257, -21297, -25725, -16126, + 2266, 19422, 25988, 18384, 756, -17284, -25901, -20394, + -3768, 14913, 25462, 22128, 6729, -12339, -24680, -23563, + -9599, 9599, 23564, 24680, 12339, -6729, -22129, -25462, + -14912, 3768, 20394, 25901, 17284, -756, -18384, -25989, + -19422, -2265, 16126, 25725, 21297, 5257, -13649, -25114, + -22885, -8178, 10988, 24163, 24162, 10987, -8178, -22885, + -25114, -13649, 5257, 21298, 25725, 16125, -2266, -19422, + -25988, -18384, -756, 17284, 25901, 20394, 3768, -14913, + -25462, -22128, -6729, 12339, 24680, 23563, 9599, -9599, + -23564, -24680, -12339, 6729, 22129, 25462, 14912, -3768, + -20394, -25901, -17284, 756, 18384, 25989, 19422, 2265, + -16126, -25725, -21297, -5257, 13649, 25114, 22885, 8177, + -10988, -24163, -24162, -10987, 8178, 22885, 25113, 13649, + -5257, -21298, -25725, -16125, 2266, 19422, 25988, 18384, + 755, -17284, -25901, -20394, -3767, 14913, 25462, 22128, + 6728, -12339, -24680, -23563, -9599, 9599, 23564, 24680, + 12339, -6729, -22129, -25462, -14912, 3768, 20395, 25901, + 17284, -756, -18385, -25989, -19422, -2265, 16126, 25725, + 21297, 5257, -13649, -25114, -22885, -8177, 10988, 24163, + 24162, 10987, -8178, -22885, -25113, -13649, 5258, 21298, + 25725, 16125, -2266, -19422, -25988, -18384, -755, 17285, + 25901, 20394, 3767, -14913, -25462, -22128, -6728, 12340, + 24680, 23563, 9598, -9599, -23564, -24680, -12339, 6729, + 22129, 25462, 14912, -3768, -20395, -25901, -17284, 756}, + { +// Carrier 20 Phase 3 + 24020, 24300, 11329, -7818, -22703, -25209, -13969, 4886, + 21078, 25777, 16421, -1889, -19169, -25997, -18650, -1134, + 17000, 25865, 20627, 4141, -14601, -25383, -22325, -7093, + 12005, 24558, 23721, 9949, -9246, -23401, -24796, -12671, + 6363, 21928, 25536, 15221, -3393, -20158, -25931, -17565, + 378, 18115, 25975, 19671, 2642, -15827, -25667, -21512, + -5627, 13326, 25013, 23062, 8536, -10644, -24020, -24300, + -11329, 7818, 22703, 25209, 13969, -4886, -21078, -25777, + -16420, 1889, 19169, 25997, 18650, 1133, -17000, -25865, + -20627, -4141, 14601, 25383, 22325, 7093, -12005, -24558, + -23721, -9949, 9247, 23401, 24796, 12670, -6363, -21928, + -25536, -15221, 3393, 20158, 25931, 17565, -378, -18115, + -25975, -19671, -2642, 15827, 25667, 21512, 5627, -13326, + -25013, -23062, -8536, 10644, 24020, 24300, 11329, -7818, + -22703, -25209, -13969, 4887, 21078, 25777, 16420, -1889, + -19169, -25997, -18650, -1133, 17000, 25865, 20627, 4141, + -14601, -25383, -22325, -7093, 12005, 24558, 23721, 9949, + -9247, -23401, -24796, -12670, 6363, 21928, 25536, 15220, + -3393, -20158, -25931, -17565, 378, 18115, 25975, 19671, + 2642, -15828, -25668, -21512, -5627, 13326, 25013, 23062, + 8535, -10644, -24021, -24299, -11329, 7818, 22703, 25209, + 13969, -4887, -21079, -25777, -16420, 1889, 19169, 25997, + 18649, 1133, -17000, -25865, -20626, -4141, 14601, 25383, + 22324, 7093, -12005, -24558, -23721, -9949, 9247, 23401, + 24796, 12670, -6363, -21928, -25536, -15220, 3394, 20158, + 25931, 17565, -378, -18115, -25975, -19671, -2642, 15828, + 25668, 21512, 5626, -13326, -25013, -23062, -8535, 10644}, + { +// Carrier 20 Phase 4 + 26000, 18911, 1511, -16712, -25824, -20855, -4514, 14287, + 25299, 22516, 7456, -11668, -24432, -23873, -10298, 8892, + 23234, 24907, 12999, -5996, -21722, -25604, -15526, 3018, + 19917, 25956, 17842, 0, -17842, -25956, -19917, -3018, + 15526, 25605, 21722, 5995, -13000, -24907, -23234, -8892, + 10298, 23873, 24431, 11668, -7456, -22516, -25299, -14287, + 4514, 20855, 25824, 16712, -1511, -18911, -26000, -18911, + -1511, 16712, 25824, 20855, 4514, -14287, -25299, -22516, + -7456, 11668, 24432, 23873, 10297, -8892, -23234, -24907, + -12999, 5996, 21722, 25604, 15525, -3018, -19917, -25956, + -17842, 0, 17842, 25956, 19917, 3018, -15526, -25605, + -21722, -5995, 13000, 24907, 23234, 8892, -10298, -23873, + -24431, -11668, 7457, 22516, 25299, 14287, -4515, -20855, + -25824, -16712, 1512, 18911, 26000, 18911, 1511, -16712, + -25824, -20855, -4514, 14287, 25299, 22516, 7456, -11669, + -24432, -23873, -10297, 8892, 23234, 24907, 12999, -5996, + -21722, -25604, -15525, 3018, 19917, 25955, 17842, 0, + -17842, -25956, -19916, -3018, 15526, 25605, 21722, 5995, + -13000, -24907, -23234, -8892, 10298, 23873, 24431, 11668, + -7457, -22516, -25299, -14286, 4515, 20855, 25824, 16712, + -1512, -18911, -26000, -18911, -1511, 16712, 25824, 20854, + 4514, -14287, -25299, -22516, -7456, 11669, 24432, 23873, + 10297, -8892, -23234, -24907, -12999, 5996, 21722, 25604, + 15525, -3018, -19917, -25955, -17841, 0, 17842, 25956, + 19916, 3017, -15526, -25605, -21722, -5995, 13000, 24907, + 23234, 8892, -10298, -23873, -24431, -11668, 7457, 22516, + 25299, 14286, -4515, -20855, -25824, -16712, 1512, 18912}, + { +// Carrier 20 Phase 5 + 24020, 10644, -8536, -23062, -25013, -13326, 5627, 21512, + 25667, 15827, -2642, -19671, -25975, -18115, -378, 17565, + 25931, 20158, 3393, -15221, -25536, -21928, -6363, 12671, + 24796, 23401, 9246, -9949, -23721, -24558, -12005, 7093, + 22325, 25383, 14601, -4142, -20627, -25865, -17000, 1134, + 18650, 25997, 19169, 1889, -16421, -25777, -21078, -4886, + 13969, 25209, 22703, 7818, -11329, -24300, -24020, -10644, + 8536, 23062, 25013, 13325, -5627, -21512, -25667, -15827, + 2642, 19672, 25975, 18115, 377, -17565, -25931, -20158, + -3393, 15221, 25536, 21928, 6363, -12671, -24796, -23401, + -9246, 9949, 23721, 24558, 12005, -7094, -22325, -25383, + -14601, 4142, 20627, 25865, 17000, -1134, -18650, -25997, + -19169, -1888, 16421, 25777, 21078, 4886, -13969, -25209, + -22703, -7818, 11329, 24300, 24020, 10643, -8536, -23062, + -25013, -13325, 5627, 21512, 25667, 15827, -2642, -19672, + -25975, -18115, -377, 17565, 25931, 20157, 3393, -15221, + -25536, -21928, -6363, 12671, 24796, 23401, 9246, -9950, + -23721, -24558, -12005, 7094, 22325, 25383, 14601, -4142, + -20627, -25865, -17000, 1134, 18650, 25997, 19168, 1888, + -16421, -25777, -21078, -4886, 13970, 25209, 22703, 7818, + -11329, -24300, -24020, -10643, 8536, 23062, 25013, 13325, + -5627, -21512, -25667, -15827, 2642, 19672, 25975, 18115, + 377, -17565, -25931, -20157, -3393, 15221, 25536, 21927, + 6362, -12671, -24796, -23401, -9246, 9950, 23721, 24558, + 12005, -7094, -22325, -25383, -14601, 4142, 20627, 25865, + 17000, -1134, -18650, -25997, -19168, -1888, 16421, 25777, + 21078, 4886, -13970, -25209, -22703, -7817, 11330, 24300}, + { +// Carrier 20 Phase 6 + 18384, 756, -17284, -25901, -20394, -3768, 14913, 25462, + 22129, 6729, -12339, -24680, -23563, -9599, 9599, 23564, + 24680, 12339, -6729, -22129, -25462, -14912, 3768, 20394, + 25901, 17284, -756, -18384, -25989, -19422, -2265, 16126, + 25725, 21297, 5257, -13649, -25114, -22885, -8178, 10988, + 24163, 24163, 10987, -8178, -22885, -25114, -13649, 5257, + 21298, 25725, 16126, -2266, -19422, -25988, -18384, -756, + 17284, 25901, 20394, 3768, -14913, -25462, -22128, -6729, + 12339, 24680, 23563, 9599, -9599, -23564, -24680, -12339, + 6729, 22129, 25462, 14912, -3768, -20394, -25901, -17284, + 756, 18384, 25989, 19422, 2265, -16126, -25725, -21297, + -5257, 13649, 25114, 22885, 8177, -10988, -24163, -24162, + -10987, 8178, 22885, 25114, 13649, -5257, -21298, -25725, + -16125, 2266, 19422, 25988, 18384, 755, -17284, -25901, + -20394, -3767, 14913, 25462, 22128, 6729, -12339, -24680, + -23563, -9599, 9599, 23564, 24680, 12339, -6729, -22129, + -25462, -14912, 3768, 20394, 25901, 17284, -756, -18384, + -25989, -19422, -2265, 16126, 25725, 21297, 5257, -13649, + -25114, -22885, -8177, 10988, 24163, 24162, 10987, -8178, + -22885, -25113, -13649, 5258, 21298, 25725, 16125, -2266, + -19422, -25988, -18384, -755, 17284, 25901, 20394, 3767, + -14913, -25462, -22128, -6728, 12339, 24680, 23563, 9598, + -9599, -23564, -24680, -12339, 6729, 22129, 25462, 14912, + -3768, -20395, -25901, -17284, 756, 18385, 25989, 19422, + 2265, -16126, -25725, -21297, -5257, 13649, 25114, 22885, + 8177, -10988, -24163, -24162, -10987, 8178, 22885, 25113, + 13648, -5258, -21298, -25725, -16125, 2266, 19422, 25988}, + { +// Carrier 20 Phase 7 + 9949, -9246, -23401, -24796, -12671, 6363, 21928, 25536, + 15221, -3393, -20158, -25931, -17565, 378, 18115, 25975, + 19671, 2642, -15827, -25667, -21512, -5627, 13326, 25013, + 23062, 8536, -10644, -24020, -24300, -11329, 7818, 22703, + 25209, 13969, -4886, -21078, -25777, -16420, 1889, 19169, + 25997, 18650, 1134, -17000, -25865, -20627, -4141, 14601, + 25383, 22325, 7093, -12005, -24558, -23721, -9949, 9247, + 23401, 24796, 12671, -6363, -21928, -25536, -15221, 3393, + 20158, 25931, 17565, -378, -18115, -25975, -19671, -2642, + 15827, 25667, 21512, 5627, -13326, -25013, -23062, -8536, + 10644, 24020, 24300, 11329, -7818, -22703, -25209, -13969, + 4886, 21078, 25777, 16420, -1889, -19169, -25997, -18650, + -1133, 17000, 25865, 20627, 4141, -14601, -25383, -22325, + -7093, 12005, 24558, 23721, 9949, -9247, -23401, -24796, + -12670, 6363, 21928, 25536, 15220, -3393, -20158, -25931, + -17565, 378, 18115, 25975, 19671, 2642, -15828, -25668, + -21512, -5627, 13326, 25013, 23062, 8535, -10644, -24020, + -24299, -11329, 7818, 22703, 25209, 13969, -4887, -21078, + -25777, -16420, 1889, 19169, 25997, 18649, 1133, -17000, + -25865, -20626, -4141, 14601, 25383, 22325, 7093, -12005, + -24558, -23721, -9949, 9247, 23401, 24796, 12670, -6363, + -21928, -25536, -15220, 3394, 20158, 25931, 17565, -378, + -18115, -25975, -19671, -2642, 15828, 25668, 21512, 5627, + -13326, -25013, -23062, -8535, 10644, 24021, 24299, 11329, + -7818, -22703, -25209, -13969, 4887, 21079, 25777, 16420, + -1889, -19169, -25997, -18649, -1133, 17000, 25865, 20626, + 4141, -14602, -25383, -22324, -7093, 12005, 24558, 23721}, + },{{ + +// Carrier 21 Phase 0 + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18385, + 0, 18384, 26000, 18385, 0, -18384, -26000, -18385}, + { +// Carrier 21 Phase 1 + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9949, + 9949, 24020, 24020, 9949, -9949, -24020, -24020, -9950, + 9949, 24020, 24020, 9950, -9949, -24020, -24020, -9950, + 9949, 24020, 24020, 9950, -9949, -24020, -24020, -9950, + 9949, 24020, 24020, 9950, -9949, -24020, -24020, -9950, + 9949, 24020, 24020, 9950, -9949, -24020, -24020, -9950, + 9949, 24020, 24020, 9950, -9949, -24020, -24020, -9950, + 9949, 24020, 24020, 9950, -9949, -24020, -24020, -9950}, + { +// Carrier 21 Phase 2 + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18385, 0, + 18384, 26000, 18385, 0, -18384, -26000, -18385, 0}, + { +// Carrier 21 Phase 3 + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9949, 9949, + 24020, 24020, 9949, -9949, -24020, -24020, -9950, 9949, + 24020, 24020, 9950, -9949, -24020, -24020, -9950, 9949, + 24020, 24020, 9950, -9949, -24020, -24020, -9950, 9949, + 24020, 24020, 9950, -9949, -24020, -24020, -9950, 9949, + 24020, 24020, 9950, -9949, -24020, -24020, -9950, 9949, + 24020, 24020, 9950, -9949, -24020, -24020, -9950, 9949, + 24020, 24020, 9950, -9949, -24020, -24020, -9950, 9949}, + { +// Carrier 21 Phase 4 + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18385, 0, 18384, + 26000, 18385, 0, -18384, -26000, -18385, 0, 18384}, + { +// Carrier 21 Phase 5 + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9949, 9949, 24020, + 24020, 9949, -9949, -24020, -24020, -9950, 9949, 24020, + 24020, 9950, -9949, -24020, -24020, -9950, 9949, 24020, + 24020, 9950, -9949, -24020, -24020, -9950, 9949, 24020, + 24020, 9950, -9949, -24020, -24020, -9950, 9949, 24020, + 24020, 9950, -9949, -24020, -24020, -9950, 9949, 24020, + 24020, 9950, -9949, -24020, -24020, -9950, 9949, 24020, + 24020, 9950, -9949, -24020, -24020, -9950, 9949, 24020, + 24020, 9950, -9949, -24020, -24020, -9950, 9949, 24020}, + { +// Carrier 21 Phase 6 + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18385, 0, 18384, 26000, + 18385, 0, -18384, -26000, -18385, 0, 18384, 26000}, + { +// Carrier 21 Phase 7 + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9949, 9949, 24020, 24020, + 9949, -9949, -24020, -24020, -9950, 9949, 24020, 24020, + 9950, -9949, -24020, -24020, -9950, 9949, 24020, 24020, + 9950, -9949, -24020, -24020, -9950, 9949, 24020, 24020, + 9950, -9949, -24020, -24020, -9950, 9949, 24020, 24020, + 9950, -9949, -24020, -24020, -9950, 9949, 24020, 24020, + 9950, -9949, -24020, -24020, -9950, 9949, 24020, 24020, + 9950, -9949, -24020, -24020, -9950, 9949, 24020, 24020}, + },{{ + +// Carrier 22 Phase 0 + 0, 18911, 25956, 16712, -3018, -20855, -25605, -14287, + 5996, 22516, 24907, 11668, -8892, -23873, -23873, -8892, + 11668, 24907, 22516, 5996, -14287, -25605, -20855, -3018, + 16712, 25956, 18911, 0, -18911, -25956, -16712, 3018, + 20855, 25604, 14287, -5996, -22516, -24907, -11668, 8892, + 23873, 23873, 8892, -11668, -24907, -22516, -5996, 14287, + 25605, 20855, 3018, -16712, -25956, -18911, 0, 18911, + 25956, 16712, -3018, -20855, -25604, -14287, 5996, 22516, + 24907, 11668, -8892, -23873, -23873, -8892, 11668, 24907, + 22516, 5995, -14287, -25605, -20855, -3018, 16712, 25956, + 18911, 0, -18911, -25956, -16712, 3018, 20855, 25604, + 14287, -5996, -22516, -24907, -11668, 8892, 23873, 23873, + 8892, -11668, -24907, -22516, -5995, 14287, 25605, 20855, + 3018, -16712, -25956, -18911, 0, 18911, 25956, 16712, + -3018, -20855, -25604, -14287, 5996, 22516, 24907, 11668, + -8892, -23873, -23873, -8892, 11668, 24907, 22516, 5995, + -14287, -25605, -20855, -3018, 16712, 25956, 18911, 0, + -18911, -25956, -16712, 3018, 20855, 25604, 14287, -5996, + -22516, -24907, -11668, 8892, 23873, 23873, 8892, -11668, + -24907, -22516, -5995, 14287, 25605, 20855, 3018, -16712, + -25956, -18911, 0, 18911, 25956, 16712, -3018, -20855, + -25604, -14287, 5996, 22516, 24907, 11668, -8892, -23873, + -23873, -8892, 11668, 24907, 22516, 5995, -14287, -25605, + -20855, -3018, 16712, 25956, 18911, 0, -18911, -25956, + -16712, 3018, 20855, 25604, 14287, -5996, -22516, -24907, + -11668, 8892, 23873, 23873, 8892, -11668, -24907, -22516, + -5995, 14287, 25605, 20855, 3018, -16712, -25956, -18911}, + { +// Carrier 22 Phase 1 + 9949, 24300, 23401, 7818, -12671, -25209, -21928, -4886, + 15221, 25777, 20158, 1889, -17565, -25997, -18115, 1134, + 19671, 25865, 15827, -4141, -21512, -25383, -13326, 7093, + 23062, 24558, 10644, -9949, -24300, -23401, -7818, 12671, + 25209, 21928, 4886, -15221, -25777, -20158, -1889, 17565, + 25997, 18115, -1134, -19671, -25865, -15827, 4142, 21512, + 25383, 13326, -7093, -23062, -24558, -10644, 9949, 24300, + 23401, 7818, -12671, -25209, -21928, -4886, 15221, 25777, + 20158, 1889, -17565, -25997, -18115, 1134, 19672, 25865, + 15827, -4142, -21512, -25383, -13326, 7093, 23062, 24558, + 10644, -9949, -24300, -23401, -7818, 12671, 25209, 21928, + 4886, -15221, -25777, -20158, -1889, 17565, 25997, 18115, + -1134, -19672, -25865, -15827, 4142, 21512, 25383, 13326, + -7093, -23062, -24558, -10644, 9949, 24300, 23401, 7818, + -12671, -25209, -21928, -4886, 15221, 25777, 20158, 1889, + -17565, -25997, -18115, 1134, 19672, 25865, 15827, -4142, + -21512, -25383, -13326, 7093, 23062, 24558, 10644, -9949, + -24300, -23401, -7818, 12671, 25209, 21928, 4886, -15221, + -25777, -20158, -1889, 17565, 25997, 18115, -1134, -19672, + -25865, -15827, 4142, 21512, 25383, 13326, -7093, -23062, + -24558, -10644, 9949, 24300, 23401, 7818, -12671, -25209, + -21928, -4886, 15221, 25777, 20158, 1889, -17565, -25997, + -18115, 1134, 19672, 25865, 15827, -4142, -21512, -25383, + -13326, 7093, 23062, 24558, 10644, -9949, -24300, -23401, + -7818, 12671, 25209, 21928, 4886, -15221, -25777, -20158, + -1889, 17565, 25997, 18115, -1134, -19672, -25865, -15827, + 4142, 21512, 25383, 13326, -7093, -23062, -24558, -10644}, + { +// Carrier 22 Phase 2 + 18384, 25989, 17284, -2266, -20394, -25725, -14912, 5257, + 22129, 25114, 12339, -8178, -23564, -24163, -9599, 10988, + 24680, 22885, 6729, -13649, -25462, -21297, -3768, 16126, + 25901, 19422, 756, -18384, -25989, -17284, 2266, 20394, + 25725, 14912, -5257, -22129, -25114, -12339, 8178, 23564, + 24163, 9599, -10988, -24680, -22885, -6729, 13649, 25462, + 21297, 3768, -16126, -25901, -19422, -756, 18384, 25989, + 17284, -2266, -20394, -25725, -14912, 5257, 22129, 25114, + 12339, -8178, -23564, -24163, -9599, 10988, 24680, 22885, + 6729, -13649, -25462, -21297, -3768, 16126, 25901, 19422, + 756, -18384, -25988, -17284, 2266, 20394, 25725, 14912, + -5257, -22129, -25114, -12339, 8178, 23564, 24163, 9599, + -10988, -24680, -22885, -6729, 13649, 25462, 21297, 3768, + -16126, -25901, -19422, -756, 18384, 25988, 17284, -2266, + -20394, -25725, -14912, 5257, 22129, 25114, 12339, -8178, + -23564, -24163, -9599, 10988, 24680, 22885, 6729, -13649, + -25462, -21297, -3768, 16126, 25901, 19422, 756, -18384, + -25988, -17284, 2266, 20394, 25725, 14912, -5257, -22129, + -25114, -12339, 8178, 23564, 24163, 9599, -10988, -24680, + -22885, -6729, 13649, 25462, 21297, 3768, -16126, -25901, + -19422, -756, 18384, 25988, 17284, -2266, -20394, -25725, + -14912, 5257, 22129, 25114, 12339, -8178, -23564, -24162, + -9599, 10988, 24680, 22885, 6729, -13649, -25462, -21297, + -3768, 16126, 25901, 19422, 756, -18384, -25988, -17284, + 2266, 20394, 25725, 14912, -5257, -22129, -25114, -12339, + 8178, 23564, 24162, 9599, -10988, -24680, -22885, -6729, + 13649, 25462, 21297, 3768, -16126, -25901, -19422, -756}, + { +// Carrier 22 Phase 3 + 24020, 23721, 8536, -12005, -25013, -22325, -5627, 14601, + 25667, 20627, 2642, -17000, -25975, -18650, 378, 19169, + 25931, 16421, -3393, -21078, -25536, -13969, 6363, 22703, + 24796, 11329, -9246, -24020, -23721, -8536, 12005, 25013, + 22325, 5627, -14601, -25667, -20627, -2642, 17000, 25975, + 18650, -378, -19169, -25931, -16421, 3393, 21078, 25536, + 13969, -6363, -22703, -24796, -11329, 9246, 24020, 23721, + 8536, -12005, -25013, -22325, -5627, 14601, 25667, 20627, + 2642, -17000, -25975, -18650, 378, 19169, 25931, 16421, + -3393, -21078, -25536, -13969, 6363, 22703, 24796, 11329, + -9246, -24020, -23721, -8536, 12005, 25013, 22325, 5627, + -14601, -25667, -20627, -2642, 17000, 25975, 18650, -378, + -19169, -25931, -16421, 3393, 21078, 25536, 13969, -6363, + -22703, -24796, -11329, 9246, 24020, 23721, 8536, -12005, + -25013, -22325, -5627, 14601, 25667, 20627, 2642, -17000, + -25975, -18650, 378, 19169, 25931, 16420, -3393, -21078, + -25536, -13969, 6363, 22703, 24796, 11329, -9246, -24020, + -23721, -8536, 12005, 25013, 22325, 5627, -14601, -25667, + -20627, -2642, 17000, 25975, 18650, -378, -19169, -25931, + -16420, 3393, 21078, 25536, 13969, -6363, -22703, -24796, + -11329, 9246, 24020, 23721, 8536, -12005, -25013, -22325, + -5627, 14601, 25667, 20627, 2642, -17000, -25975, -18650, + 378, 19169, 25931, 16420, -3393, -21078, -25536, -13969, + 6363, 22703, 24796, 11329, -9246, -24020, -23721, -8536, + 12005, 25013, 22325, 5627, -14601, -25667, -20627, -2642, + 17000, 25975, 18650, -378, -19169, -25931, -16420, 3393, + 21078, 25536, 13969, -6363, -22703, -24796, -11329, 9247}, + { +// Carrier 22 Phase 4 + 26000, 17842, -1511, -19917, -25824, -15526, 4514, 21722, + 25299, 13000, -7456, -23234, -24432, -10298, 10298, 24432, + 23234, 7456, -12999, -25299, -21722, -4514, 15526, 25824, + 19917, 1511, -17842, -26000, -17842, 1511, 19917, 25824, + 15526, -4514, -21722, -25299, -13000, 7456, 23234, 24432, + 10298, -10298, -24432, -23234, -7456, 13000, 25299, 21722, + 4514, -15526, -25824, -19917, -1511, 17842, 26000, 17842, + -1511, -19917, -25824, -15526, 4514, 21722, 25299, 12999, + -7456, -23234, -24432, -10298, 10298, 24432, 23234, 7456, + -13000, -25299, -21722, -4514, 15526, 25824, 19917, 1511, + -17842, -26000, -17842, 1511, 19917, 25824, 15526, -4514, + -21722, -25299, -12999, 7456, 23234, 24431, 10298, -10298, + -24432, -23234, -7456, 13000, 25299, 21722, 4514, -15526, + -25824, -19917, -1511, 17842, 26000, 17842, -1511, -19917, + -25824, -15526, 4514, 21722, 25299, 12999, -7456, -23234, + -24431, -10298, 10298, 24432, 23234, 7456, -13000, -25299, + -21722, -4514, 15526, 25824, 19917, 1511, -17842, -26000, + -17842, 1511, 19917, 25824, 15526, -4514, -21722, -25299, + -12999, 7456, 23234, 24431, 10298, -10298, -24432, -23234, + -7456, 13000, 25299, 21722, 4514, -15526, -25824, -19917, + -1511, 17842, 26000, 17842, -1511, -19917, -25824, -15526, + 4514, 21722, 25299, 12999, -7456, -23234, -24431, -10298, + 10298, 24432, 23234, 7456, -13000, -25299, -21722, -4514, + 15526, 25824, 19917, 1511, -17842, -26000, -17842, 1511, + 19917, 25824, 15526, -4514, -21722, -25299, -12999, 7456, + 23234, 24431, 10298, -10298, -24432, -23234, -7456, 13000, + 25299, 21722, 4514, -15526, -25824, -19917, -1511, 17842}, + { +// Carrier 22 Phase 5 + 24020, 9246, -11329, -24796, -22703, -6363, 13969, 25536, + 21078, 3393, -16421, -25931, -19169, -378, 18650, 25975, + 17000, -2642, -20627, -25667, -14601, 5627, 22325, 25013, + 12005, -8536, -23721, -24020, -9246, 11329, 24796, 22703, + 6363, -13969, -25536, -21078, -3393, 16421, 25931, 19169, + 378, -18650, -25975, -17000, 2642, 20627, 25667, 14601, + -5627, -22325, -25013, -12005, 8536, 23721, 24020, 9246, + -11329, -24796, -22703, -6363, 13969, 25536, 21078, 3393, + -16421, -25931, -19169, -378, 18650, 25975, 17000, -2642, + -20627, -25667, -14601, 5627, 22325, 25013, 12005, -8536, + -23721, -24020, -9246, 11329, 24796, 22703, 6363, -13969, + -25536, -21078, -3393, 16421, 25931, 19169, 378, -18650, + -25975, -17000, 2642, 20627, 25667, 14601, -5627, -22325, + -25013, -12005, 8536, 23721, 24020, 9246, -11329, -24796, + -22703, -6363, 13969, 25536, 21078, 3393, -16421, -25931, + -19169, -378, 18650, 25975, 17000, -2642, -20627, -25667, + -14601, 5627, 22325, 25013, 12005, -8536, -23721, -24020, + -9246, 11329, 24796, 22703, 6363, -13969, -25536, -21078, + -3393, 16421, 25931, 19169, 378, -18650, -25975, -17000, + 2642, 20627, 25667, 14601, -5627, -22325, -25013, -12005, + 8536, 23721, 24020, 9246, -11329, -24796, -22703, -6363, + 13969, 25536, 21078, 3393, -16421, -25931, -19169, -378, + 18650, 25975, 17000, -2642, -20627, -25667, -14601, 5627, + 22325, 25013, 12005, -8536, -23721, -24020, -9246, 11329, + 24796, 22703, 6363, -13969, -25536, -21078, -3393, 16421, + 25931, 19169, 377, -18650, -25975, -17000, 2642, 20627, + 25667, 14601, -5627, -22325, -25013, -12005, 8536, 23721}, + { +// Carrier 22 Phase 6 + 18384, -756, -19422, -25901, -16126, 3768, 21297, 25462, + 13649, -6729, -22885, -24680, -10988, 9599, 24163, 23564, + 8178, -12339, -25114, -22129, -5257, 14912, 25725, 20394, + 2266, -17284, -25989, -18384, 756, 19422, 25901, 16126, + -3768, -21297, -25462, -13649, 6729, 22885, 24680, 10988, + -9599, -24163, -23563, -8178, 12339, 25114, 22129, 5257, + -14913, -25725, -20394, -2266, 17284, 25989, 18384, -756, + -19422, -25901, -16126, 3768, 21297, 25462, 13649, -6729, + -22885, -24680, -10988, 9599, 24163, 23563, 8178, -12339, + -25114, -22129, -5257, 14913, 25725, 20394, 2266, -17284, + -25989, -18384, 756, 19422, 25901, 16126, -3768, -21297, + -25462, -13649, 6729, 22885, 24680, 10988, -9599, -24163, + -23563, -8178, 12339, 25114, 22129, 5257, -14913, -25725, + -20394, -2266, 17284, 25989, 18384, -756, -19422, -25901, + -16126, 3768, 21297, 25462, 13649, -6729, -22885, -24680, + -10988, 9599, 24163, 23563, 8178, -12339, -25114, -22129, + -5257, 14913, 25725, 20394, 2265, -17284, -25989, -18384, + 756, 19422, 25901, 16126, -3768, -21297, -25462, -13649, + 6729, 22885, 24680, 10988, -9599, -24163, -23563, -8178, + 12339, 25114, 22128, 5257, -14913, -25725, -20394, -2265, + 17284, 25989, 18384, -756, -19422, -25901, -16126, 3768, + 21297, 25462, 13649, -6729, -22885, -24680, -10988, 9599, + 24163, 23563, 8178, -12339, -25114, -22128, -5257, 14913, + 25725, 20394, 2265, -17284, -25989, -18384, 756, 19422, + 25901, 16126, -3768, -21297, -25462, -13649, 6729, 22885, + 24680, 10987, -9599, -24163, -23563, -8178, 12339, 25114, + 22128, 5257, -14913, -25725, -20394, -2265, 17284, 25989}, + { +// Carrier 22 Phase 7 + 9949, -10644, -24558, -23062, -7093, 13326, 25383, 21512, + 4141, -15827, -25865, -19671, -1134, 18115, 25997, 17565, + -1889, -20158, -25777, -15221, 4886, 21928, 25209, 12671, + -7818, -23401, -24300, -9949, 10644, 24558, 23062, 7093, + -13326, -25383, -21512, -4141, 15827, 25865, 19671, 1134, + -18115, -25997, -17565, 1889, 20158, 25777, 15221, -4886, + -21928, -25209, -12671, 7818, 23401, 24300, 9949, -10644, + -24558, -23062, -7093, 13326, 25383, 21512, 4141, -15827, + -25865, -19671, -1134, 18115, 25997, 17565, -1889, -20158, + -25777, -15221, 4886, 21928, 25209, 12671, -7818, -23401, + -24300, -9949, 10644, 24558, 23062, 7093, -13326, -25383, + -21512, -4141, 15827, 25865, 19671, 1134, -18115, -25997, + -17565, 1889, 20158, 25777, 15221, -4886, -21928, -25209, + -12671, 7818, 23401, 24300, 9949, -10644, -24558, -23062, + -7093, 13326, 25383, 21512, 4141, -15827, -25865, -19671, + -1134, 18115, 25997, 17565, -1889, -20158, -25777, -15221, + 4886, 21928, 25209, 12671, -7818, -23401, -24300, -9949, + 10644, 24558, 23062, 7093, -13326, -25383, -21512, -4141, + 15827, 25865, 19671, 1134, -18115, -25997, -17565, 1889, + 20158, 25777, 15221, -4886, -21928, -25209, -12671, 7818, + 23401, 24300, 9949, -10644, -24558, -23062, -7093, 13326, + 25383, 21512, 4141, -15827, -25865, -19671, -1133, 18115, + 25997, 17565, -1889, -20158, -25777, -15221, 4886, 21928, + 25209, 12671, -7818, -23401, -24300, -9949, 10644, 24558, + 23062, 7093, -13326, -25383, -21512, -4141, 15827, 25865, + 19671, 1133, -18115, -25997, -17565, 1889, 20158, 25777, + 15221, -4886, -21928, -25209, -12671, 7818, 23401, 24300}, + },{{ + +// Carrier 23 Phase 0 + 0, 19422, 25824, 14912, -5996, -22885, -24432, -9599, + 11668, 25114, 21722, 3768, -16712, -25989, -17842, 2266, + 20855, 25462, 12999, -8178, -23873, -23563, -7456, 13649, + 25605, 20394, 1511, -18384, -25956, -16126, 4514, 22129, + 24907, 10988, -10298, -24680, -22516, -5257, 15526, 25901, + 18911, -756, -19917, -25725, -14287, 6729, 23234, 24163, + 8892, -12339, -25299, -21297, -3018, 17284, 26000, 17284, + -3018, -21297, -25299, -12339, 8892, 24163, 23234, 6729, + -14287, -25725, -19917, -756, 18911, 25901, 15526, -5257, + -22516, -24680, -10297, 10988, 24907, 22128, 4514, -16126, + -25956, -18384, 1511, 20394, 25604, 13649, -7457, -23564, + -23873, -8178, 13000, 25462, 20855, 2265, -17842, -25988, + -16712, 3768, 21722, 25114, 11668, -9599, -24432, -22885, + -5995, 14913, 25824, 19422, 0, -19422, -25824, -14912, + 5996, 22885, 24431, 9599, -11668, -25114, -21722, -3768, + 16712, 25989, 17842, -2266, -20855, -25462, -12999, 8178, + 23873, 23563, 7456, -13649, -25605, -20394, -1511, 18384, + 25955, 16125, -4515, -22129, -24907, -10987, 10298, 24680, + 22516, 5257, -15526, -25901, -18911, 756, 19917, 25725, + 14287, -6729, -23234, -24162, -8892, 12339, 25299, 21297, + 3018, -17284, -26000, -17284, 3018, 21298, 25299, 12339, + -8892, -24163, -23234, -6729, 14287, 25725, 19916, 755, + -18911, -25901, -15525, 5257, 22516, 24680, 10297, -10988, + -24907, -22128, -4514, 16126, 25956, 18384, -1512, -20394, + -25604, -13649, 7457, 23564, 23873, 8177, -13000, -25462, + -20855, -2265, 17842, 25988, 16712, -3768, -21722, -25113, + -11668, 9599, 24432, 22885, 5995, -14913, -25824, -19422}, + { +// Carrier 23 Phase 1 + 9949, 24558, 22703, 5627, -15221, -25865, -19169, 378, + 19671, 25777, 14601, -6363, -23062, -24300, -9246, 12005, + 25209, 21512, 3393, -17000, -25997, -17565, 2642, 21078, + 25383, 12671, -8536, -24020, -23401, -7093, 13969, 25667, + 20158, 1134, -18650, -25931, -15827, 4886, 22325, 24796, + 10644, -10644, -24796, -22325, -4886, 15827, 25931, 18650, + -1134, -20158, -25667, -13969, 7093, 23401, 24020, 8536, + -12671, -25383, -21078, -2642, 17565, 25997, 17000, -3393, + -21512, -25209, -12005, 9247, 24300, 23062, 6363, -14601, + -25777, -19671, -378, 19169, 25865, 15221, -5627, -22703, + -24558, -9949, 11329, 25013, 21928, 4141, -16421, -25975, + -18115, 1889, 20627, 25536, 13325, -7818, -23721, -23721, + -7818, 13326, 25536, 20627, 1888, -18115, -25975, -16420, + 4142, 21928, 25013, 11329, -9949, -24558, -22703, -5627, + 15221, 25865, 19169, -378, -19672, -25777, -14601, 6363, + 23062, 24300, 9246, -12005, -25209, -21512, -3393, 17000, + 25997, 17565, -2642, -21078, -25383, -12670, 8536, 24020, + 23401, 7093, -13969, -25667, -20157, -1133, 18650, 25931, + 15827, -4886, -22325, -24796, -10643, 10644, 24796, 22325, + 4886, -15827, -25931, -18650, 1134, 20158, 25667, 13969, + -7094, -23401, -24020, -8536, 12671, 25383, 21078, 2642, + -17565, -25997, -17000, 3393, 21512, 25209, 12005, -9247, + -24300, -23062, -6363, 14601, 25777, 19671, 377, -19169, + -25865, -15220, 5627, 22703, 24558, 9949, -11329, -25013, + -21928, -4141, 16421, 25975, 18115, -1889, -20627, -25536, + -13325, 7818, 23721, 23721, 7818, -13326, -25536, -20626, + -1888, 18115, 25975, 16420, -4142, -21928, -25013, -11329}, + { +// Carrier 23 Phase 2 + 18384, 25956, 16126, -4514, -22129, -24907, -10988, 10298, + 24680, 22516, 5257, -15526, -25901, -18911, 756, 19917, + 25725, 14287, -6729, -23234, -24163, -8892, 12339, 25299, + 21297, 3018, -17284, -26000, -17284, 3018, 21297, 25299, + 12339, -8892, -24163, -23234, -6729, 14287, 25725, 19917, + 756, -18911, -25901, -15526, 5257, 22516, 24680, 10298, + -10988, -24907, -22128, -4514, 16126, 25956, 18384, -1511, + -20394, -25604, -13649, 7456, 23564, 23873, 8178, -13000, + -25462, -20855, -2265, 17842, 25988, 16712, -3768, -21722, + -25114, -11668, 9599, 24432, 22885, 5995, -14913, -25824, + -19422, 0, 19422, 25824, 14912, -5996, -22885, -24431, + -9599, 11668, 25114, 21722, 3768, -16712, -25989, -17842, + 2266, 20855, 25462, 12999, -8178, -23873, -23563, -7456, + 13649, 25605, 20394, 1511, -18384, -25956, -16125, 4515, + 22129, 24907, 10987, -10298, -24680, -22516, -5257, 15526, + 25901, 18911, -756, -19917, -25725, -14287, 6729, 23234, + 24162, 8892, -12339, -25299, -21297, -3018, 17284, 26000, + 17284, -3018, -21298, -25299, -12339, 8892, 24163, 23234, + 6729, -14287, -25725, -19917, -755, 18911, 25901, 15525, + -5257, -22516, -24680, -10297, 10988, 24907, 22128, 4514, + -16126, -25956, -18384, 1512, 20394, 25604, 13649, -7457, + -23564, -23873, -8177, 13000, 25462, 20855, 2265, -17842, + -25988, -16712, 3768, 21722, 25113, 11668, -9599, -24432, + -22885, -5995, 14913, 25824, 19422, 0, -19422, -25824, + -14912, 5996, 22885, 24431, 9599, -11669, -25114, -21722, + -3767, 16712, 25989, 17842, -2266, -20855, -25462, -12999, + 8178, 23873, 23563, 7456, -13649, -25605, -20394, -1511}, + { +// Carrier 23 Phase 3 + 24020, 23401, 7093, -13969, -25667, -20158, -1134, 18650, + 25931, 15827, -4886, -22325, -24796, -10644, 10644, 24796, + 22325, 4886, -15827, -25931, -18650, 1134, 20158, 25667, + 13969, -7093, -23401, -24020, -8536, 12671, 25383, 21078, + 2642, -17565, -25997, -17000, 3393, 21512, 25209, 12005, + -9246, -24300, -23062, -6363, 14601, 25777, 19671, 378, + -19169, -25865, -15221, 5627, 22703, 24558, 9949, -11329, + -25013, -21928, -4141, 16421, 25975, 18115, -1889, -20627, + -25536, -13326, 7818, 23721, 23721, 7818, -13326, -25536, + -20627, -1889, 18115, 25975, 16420, -4142, -21928, -25013, + -11329, 9949, 24558, 22703, 5627, -15221, -25865, -19169, + 378, 19672, 25777, 14601, -6363, -23062, -24300, -9246, + 12005, 25209, 21512, 3393, -17000, -25997, -17565, 2642, + 21078, 25383, 12671, -8536, -24020, -23401, -7093, 13969, + 25667, 20158, 1133, -18650, -25931, -15827, 4886, 22325, + 24796, 10644, -10644, -24796, -22325, -4886, 15827, 25931, + 18650, -1134, -20158, -25667, -13969, 7094, 23401, 24020, + 8536, -12671, -25383, -21078, -2642, 17565, 25997, 17000, + -3393, -21512, -25209, -12005, 9247, 24300, 23062, 6363, + -14601, -25777, -19671, -377, 19169, 25865, 15220, -5627, + -22703, -24558, -9949, 11329, 25013, 21928, 4141, -16421, + -25975, -18115, 1889, 20627, 25536, 13325, -7818, -23721, + -23721, -7818, 13326, 25536, 20627, 1888, -18115, -25975, + -16420, 4142, 21928, 25013, 11329, -9950, -24558, -22703, + -5627, 15221, 25865, 19169, -378, -19672, -25777, -14601, + 6363, 23062, 24299, 9246, -12005, -25209, -21512, -3393, + 17000, 25997, 17565, -2642, -21078, -25383, -12670, 8536}, + { +// Carrier 23 Phase 4 + 26000, 17284, -3018, -21297, -25299, -12339, 8892, 24163, + 23234, 6729, -14287, -25725, -19917, -756, 18911, 25901, + 15526, -5257, -22516, -24680, -10298, 10988, 24907, 22129, + 4514, -16126, -25956, -18384, 1511, 20394, 25604, 13649, + -7456, -23564, -23873, -8178, 13000, 25462, 20855, 2265, + -17842, -25988, -16712, 3768, 21722, 25114, 11668, -9599, + -24432, -22885, -5995, 14913, 25824, 19422, 0, -19422, + -25824, -14912, 5996, 22885, 24431, 9599, -11668, -25114, + -21722, -3768, 16712, 25989, 17842, -2266, -20855, -25462, + -12999, 8178, 23873, 23563, 7456, -13649, -25605, -20394, + -1511, 18384, 25956, 16126, -4514, -22129, -24907, -10987, + 10298, 24680, 22516, 5257, -15526, -25901, -18911, 756, + 19917, 25725, 14287, -6729, -23234, -24162, -8892, 12339, + 25299, 21297, 3018, -17284, -26000, -17284, 3018, 21298, + 25299, 12339, -8892, -24163, -23234, -6729, 14287, 25725, + 19917, 756, -18911, -25901, -15525, 5257, 22516, 24680, + 10297, -10988, -24907, -22128, -4514, 16126, 25956, 18384, + -1511, -20394, -25604, -13649, 7457, 23564, 23873, 8177, + -13000, -25462, -20855, -2265, 17842, 25988, 16712, -3768, + -21722, -25114, -11668, 9599, 24432, 22885, 5995, -14913, + -25824, -19422, 0, 19422, 25824, 14912, -5996, -22885, + -24431, -9599, 11669, 25114, 21722, 3767, -16712, -25989, + -17842, 2266, 20855, 25462, 12999, -8178, -23873, -23563, + -7456, 13649, 25605, 20394, 1511, -18384, -25955, -16125, + 4515, 22129, 24907, 10987, -10298, -24680, -22516, -5257, + 15526, 25901, 18911, -756, -19917, -25725, -14286, 6729, + 23234, 24162, 8892, -12339, -25299, -21297, -3018, 17284}, + { +// Carrier 23 Phase 5 + 24020, 8536, -12671, -25383, -21078, -2642, 17565, 25997, + 17000, -3393, -21512, -25209, -12005, 9246, 24300, 23062, + 6363, -14601, -25777, -19671, -378, 19169, 25865, 15221, + -5627, -22703, -24558, -9949, 11329, 25013, 21928, 4141, + -16421, -25975, -18115, 1889, 20627, 25536, 13326, -7818, + -23721, -23721, -7818, 13326, 25536, 20627, 1889, -18115, + -25975, -16420, 4142, 21928, 25013, 11329, -9949, -24558, + -22703, -5627, 15221, 25865, 19169, -378, -19672, -25777, + -14601, 6363, 23062, 24300, 9246, -12005, -25209, -21512, + -3393, 17000, 25997, 17565, -2642, -21078, -25383, -12671, + 8536, 24020, 23401, 7093, -13969, -25667, -20158, -1133, + 18650, 25931, 15827, -4886, -22325, -24796, -10644, 10644, + 24796, 22325, 4886, -15827, -25931, -18650, 1134, 20158, + 25667, 13969, -7093, -23401, -24020, -8536, 12671, 25383, + 21078, 2642, -17565, -25997, -17000, 3393, 21512, 25209, + 12005, -9247, -24300, -23062, -6363, 14601, 25777, 19671, + 377, -19169, -25865, -15220, 5627, 22703, 24558, 9949, + -11329, -25013, -21928, -4141, 16421, 25975, 18115, -1889, + -20627, -25536, -13325, 7818, 23721, 23721, 7818, -13326, + -25536, -20627, -1888, 18115, 25975, 16420, -4142, -21928, + -25013, -11329, 9950, 24558, 22703, 5627, -15221, -25865, + -19169, 378, 19672, 25777, 14601, -6363, -23062, -24299, + -9246, 12005, 25209, 21512, 3393, -17000, -25997, -17565, + 2642, 21078, 25383, 12670, -8536, -24020, -23401, -7093, + 13970, 25668, 20157, 1133, -18650, -25931, -15827, 4887, + 22325, 24796, 10643, -10644, -24796, -22325, -4886, 15828, + 25931, 18649, -1134, -20158, -25667, -13969, 7094, 23401}, + { +// Carrier 23 Phase 6 + 18384, -1511, -20394, -25605, -13649, 7456, 23564, 23873, + 8178, -13000, -25462, -20855, -2266, 17842, 25989, 16712, + -3768, -21722, -25114, -11668, 9599, 24432, 22885, 5995, + -14913, -25824, -19422, 0, 19422, 25824, 14912, -5996, + -22885, -24431, -9599, 11668, 25114, 21722, 3768, -16712, + -25989, -17842, 2266, 20855, 25462, 12999, -8178, -23873, + -23563, -7456, 13649, 25605, 20394, 1511, -18384, -25956, + -16126, 4514, 22129, 24907, 10987, -10298, -24680, -22516, + -5257, 15526, 25901, 18911, -756, -19917, -25725, -14287, + 6729, 23234, 24162, 8892, -12339, -25299, -21297, -3018, + 17284, 26000, 17284, -3018, -21298, -25299, -12339, 8892, + 24163, 23234, 6729, -14287, -25725, -19917, -756, 18911, + 25901, 15525, -5257, -22516, -24680, -10297, 10988, 24907, + 22128, 4514, -16126, -25956, -18384, 1511, 20394, 25604, + 13649, -7457, -23564, -23873, -8177, 13000, 25462, 20855, + 2265, -17842, -25988, -16712, 3768, 21722, 25114, 11668, + -9599, -24432, -22885, -5995, 14913, 25824, 19422, 0, + -19422, -25824, -14912, 5996, 22885, 24431, 9599, -11668, + -25114, -21722, -3767, 16712, 25989, 17842, -2266, -20855, + -25462, -12999, 8178, 23873, 23563, 7456, -13649, -25605, + -20394, -1511, 18384, 25955, 16125, -4515, -22129, -24907, + -10987, 10298, 24680, 22516, 5257, -15526, -25901, -18911, + 756, 19917, 25725, 14286, -6729, -23234, -24162, -8892, + 12339, 25299, 21297, 3018, -17284, -26000, -17284, 3018, + 21298, 25299, 12339, -8892, -24163, -23234, -6728, 14287, + 25725, 19916, 755, -18911, -25901, -15525, 5257, 22516, + 24680, 10297, -10988, -24907, -22128, -4514, 16126, 25956}, + { +// Carrier 23 Phase 7 + 9949, -11329, -25013, -21928, -4141, 16421, 25975, 18115, + -1889, -20627, -25536, -13326, 7818, 23721, 23721, 7818, + -13326, -25536, -20627, -1889, 18115, 25975, 16421, -4142, + -21928, -25013, -11329, 9949, 24558, 22703, 5627, -15221, + -25865, -19169, 378, 19672, 25777, 14601, -6363, -23062, + -24300, -9246, 12005, 25209, 21512, 3393, -17000, -25997, + -17565, 2642, 21078, 25383, 12671, -8536, -24020, -23401, + -7093, 13969, 25667, 20158, 1134, -18650, -25931, -15827, + 4886, 22325, 24796, 10644, -10644, -24796, -22325, -4886, + 15827, 25931, 18650, -1134, -20158, -25667, -13969, 7093, + 23401, 24020, 8536, -12671, -25383, -21078, -2642, 17565, + 25997, 17000, -3393, -21512, -25209, -12005, 9247, 24300, + 23062, 6363, -14601, -25777, -19671, -377, 19169, 25865, + 15221, -5627, -22703, -24558, -9949, 11329, 25013, 21928, + 4141, -16421, -25975, -18115, 1889, 20627, 25536, 13325, + -7818, -23721, -23721, -7818, 13326, 25536, 20627, 1888, + -18115, -25975, -16420, 4142, 21928, 25013, 11329, -9949, + -24558, -22703, -5627, 15221, 25865, 19169, -378, -19672, + -25777, -14601, 6363, 23062, 24300, 9246, -12005, -25209, + -21512, -3393, 17000, 25997, 17565, -2642, -21078, -25383, + -12670, 8536, 24020, 23401, 7093, -13970, -25667, -20157, + -1133, 18650, 25931, 15827, -4887, -22325, -24796, -10643, + 10644, 24796, 22325, 4886, -15828, -25931, -18650, 1134, + 20158, 25667, 13969, -7094, -23401, -24020, -8535, 12671, + 25383, 21078, 2642, -17565, -25997, -17000, 3394, 21512, + 25209, 12005, -9247, -24300, -23062, -6363, 14601, 25777, + 19671, 377, -19169, -25865, -15220, 5627, 22703, 24558}, + },{{ + +// Carrier 24 Phase 0 + 0, 19917, 25605, 13000, -8892, -24432, -22516, -4514, + 16712, 26000, 16712, -4514, -22516, -24432, -8892, 12999, + 25604, 19917, 0, -19917, -25605, -13000, 8892, 24431, + 22516, 4514, -16712, -26000, -16712, 4514, 22516, 24432, + 8892, -12999, -25604, -19917, 0, 19917, 25605, 13000, + -8892, -24431, -22516, -4514, 16712, 26000, 16712, -4514, + -22516, -24432, -8892, 12999, 25604, 19917, 0, -19917, + -25605, -13000, 8892, 24431, 22516, 4514, -16712, -26000, + -16712, 4514, 22516, 24432, 8892, -12999, -25604, -19917, + 0, 19917, 25605, 13000, -8892, -24431, -22516, -4514, + 16712, 26000, 16712, -4514, -22516, -24432, -8892, 12999, + 25604, 19917, 0, -19917, -25605, -13000, 8892, 24431, + 22516, 4515, -16712, -26000, -16712, 4514, 22516, 24432, + 8892, -12999, -25604, -19917, 0, 19917, 25605, 13000, + -8892, -24431, -22516, -4515, 16712, 26000, 16712, -4514, + -22516, -24432, -8892, 12999, 25604, 19917, 0, -19917, + -25605, -13000, 8892, 24431, 22516, 4515, -16712, -26000, + -16712, 4514, 22516, 24432, 8892, -12999, -25604, -19917, + 0, 19916, 25605, 13000, -8892, -24431, -22516, -4515, + 16712, 26000, 16712, -4514, -22516, -24432, -8892, 12999, + 25604, 19917, 0, -19916, -25605, -13000, 8892, 24431, + 22516, 4515, -16712, -26000, -16712, 4514, 22516, 24432, + 8892, -12999, -25604, -19917, 0, 19916, 25605, 13000, + -8892, -24431, -22516, -4515, 16712, 26000, 16712, -4514, + -22516, -24432, -8892, 12999, 25604, 19917, 0, -19916, + -25605, -13000, 8892, 24431, 22516, 4515, -16712, -26000, + -16712, 4514, 22516, 24432, 8892, -12999, -25604, -19917}, + { +// Carrier 24 Phase 1 + 9949, 24796, 21928, 3393, -17565, -25975, -15827, 5627, + 23062, 24020, 7818, -13969, -25777, -19169, 1134, 20627, + 25383, 12005, -9949, -24796, -21928, -3393, 17565, 25975, + 15827, -5627, -23062, -24020, -7818, 13969, 25777, 19169, + -1134, -20627, -25383, -12005, 9949, 24796, 21928, 3393, + -17565, -25975, -15827, 5627, 23062, 24020, 7818, -13969, + -25777, -19169, 1134, 20627, 25383, 12005, -9949, -24796, + -21928, -3393, 17565, 25975, 15827, -5627, -23062, -24020, + -7818, 13969, 25777, 19169, -1133, -20627, -25383, -12005, + 9949, 24796, 21928, 3393, -17565, -25975, -15827, 5627, + 23062, 24020, 7818, -13969, -25777, -19169, 1133, 20627, + 25383, 12005, -9949, -24796, -21928, -3393, 17565, 25975, + 15827, -5627, -23062, -24020, -7818, 13969, 25777, 19169, + -1133, -20627, -25383, -12005, 9949, 24796, 21928, 3393, + -17565, -25975, -15827, 5627, 23062, 24020, 7818, -13969, + -25777, -19169, 1133, 20627, 25383, 12005, -9949, -24796, + -21928, -3393, 17565, 25975, 15827, -5627, -23062, -24020, + -7818, 13969, 25777, 19169, -1133, -20627, -25383, -12005, + 9949, 24796, 21928, 3393, -17565, -25975, -15828, 5627, + 23062, 24020, 7818, -13969, -25777, -19169, 1133, 20627, + 25383, 12005, -9949, -24796, -21928, -3393, 17565, 25975, + 15828, -5627, -23062, -24020, -7818, 13969, 25777, 19169, + -1133, -20626, -25383, -12005, 9949, 24796, 21928, 3393, + -17565, -25975, -15828, 5627, 23062, 24020, 7818, -13969, + -25777, -19169, 1133, 20626, 25383, 12005, -9949, -24796, + -21928, -3394, 17565, 25975, 15828, -5627, -23062, -24021, + -7818, 13969, 25777, 19169, -1133, -20626, -25383, -12005}, + { +// Carrier 24 Phase 2 + 18384, 25901, 14912, -6729, -23563, -23564, -6729, 14912, + 25901, 18384, -2266, -21297, -25114, -10988, 10988, 25114, + 21297, 2266, -18384, -25901, -14913, 6729, 23563, 23564, + 6729, -14912, -25901, -18384, 2265, 21297, 25114, 10988, + -10988, -25114, -21297, -2266, 18384, 25901, 14913, -6729, + -23563, -23564, -6729, 14912, 25901, 18384, -2265, -21297, + -25114, -10988, 10987, 25114, 21298, 2266, -18384, -25901, + -14913, 6729, 23563, 23564, 6729, -14912, -25901, -18384, + 2265, 21297, 25114, 10988, -10987, -25114, -21298, -2266, + 18384, 25901, 14913, -6729, -23563, -23564, -6729, 14912, + 25901, 18384, -2265, -21297, -25114, -10988, 10987, 25114, + 21298, 2266, -18384, -25901, -14913, 6729, 23563, 23564, + 6729, -14912, -25901, -18384, 2265, 21297, 25114, 10988, + -10987, -25114, -21298, -2266, 18384, 25901, 14913, -6729, + -23563, -23564, -6729, 14912, 25901, 18384, -2265, -21297, + -25114, -10988, 10987, 25114, 21298, 2266, -18384, -25901, + -14913, 6729, 23563, 23564, 6729, -14912, -25901, -18384, + 2265, 21297, 25114, 10988, -10987, -25114, -21298, -2266, + 18384, 25901, 14913, -6729, -23563, -23564, -6729, 14912, + 25901, 18384, -2265, -21297, -25114, -10988, 10987, 25113, + 21298, 2266, -18384, -25901, -14913, 6729, 23563, 23564, + 6729, -14912, -25901, -18384, 2265, 21297, 25114, 10988, + -10987, -25113, -21298, -2266, 18384, 25901, 14913, -6728, + -23563, -23564, -6729, 14912, 25901, 18385, -2265, -21297, + -25114, -10988, 10987, 25113, 21298, 2266, -18384, -25901, + -14913, 6728, 23563, 23564, 6729, -14912, -25901, -18385, + 2265, 21297, 25114, 10988, -10987, -25113, -21298, -2266}, + { +// Carrier 24 Phase 3 + 24020, 23062, 5627, -15827, -25975, -17565, 3393, 21928, + 24796, 9949, -12005, -25383, -20627, -1134, 19169, 25777, + 13969, -7818, -24020, -23062, -5627, 15827, 25975, 17565, + -3393, -21928, -24796, -9949, 12005, 25383, 20627, 1134, + -19169, -25777, -13969, 7818, 24020, 23062, 5627, -15827, + -25975, -17565, 3393, 21928, 24796, 9949, -12005, -25383, + -20627, -1134, 19169, 25777, 13969, -7818, -24020, -23062, + -5627, 15827, 25975, 17565, -3393, -21928, -24796, -9949, + 12005, 25383, 20627, 1134, -19169, -25777, -13969, 7818, + 24020, 23062, 5627, -15827, -25975, -17565, 3393, 21928, + 24796, 9949, -12005, -25383, -20627, -1134, 19169, 25777, + 13969, -7818, -24020, -23062, -5627, 15827, 25975, 17565, + -3393, -21928, -24796, -9949, 12005, 25383, 20627, 1134, + -19169, -25777, -13969, 7818, 24020, 23062, 5627, -15827, + -25975, -17565, 3393, 21928, 24796, 9949, -12005, -25383, + -20627, -1134, 19169, 25777, 13969, -7818, -24020, -23062, + -5627, 15827, 25975, 17565, -3393, -21928, -24796, -9949, + 12005, 25383, 20627, 1134, -19169, -25777, -13970, 7818, + 24020, 23062, 5627, -15827, -25975, -17565, 3393, 21928, + 24796, 9950, -12005, -25383, -20627, -1134, 19169, 25777, + 13970, -7818, -24020, -23062, -5627, 15827, 25975, 17565, + -3393, -21928, -24796, -9950, 12005, 25383, 20627, 1134, + -19168, -25777, -13970, 7818, 24020, 23062, 5627, -15827, + -25975, -17565, 3393, 21928, 24796, 9950, -12005, -25383, + -20627, -1134, 19168, 25777, 13970, -7818, -24020, -23062, + -5627, 15827, 25975, 17565, -3393, -21927, -24796, -9950, + 12005, 25383, 20627, 1134, -19168, -25777, -13970, 7817}, + { +// Carrier 24 Phase 4 + 26000, 16712, -4514, -22516, -24432, -8892, 12999, 25604, + 19917, 0, -19917, -25605, -13000, 8892, 24431, 22516, + 4514, -16712, -26000, -16712, 4514, 22516, 24432, 8892, + -12999, -25604, -19917, 0, 19917, 25605, 13000, -8892, + -24431, -22516, -4514, 16712, 26000, 16712, -4514, -22516, + -24432, -8892, 12999, 25604, 19917, 0, -19917, -25605, + -13000, 8892, 24431, 22516, 4514, -16712, -26000, -16712, + 4514, 22516, 24432, 8892, -12999, -25604, -19917, 0, + 19917, 25605, 13000, -8892, -24431, -22516, -4514, 16712, + 26000, 16712, -4514, -22516, -24432, -8892, 12999, 25604, + 19917, 0, -19917, -25605, -13000, 8892, 24431, 22516, + 4515, -16712, -26000, -16712, 4514, 22516, 24432, 8892, + -12999, -25604, -19917, 0, 19917, 25605, 13000, -8892, + -24431, -22516, -4515, 16712, 26000, 16712, -4514, -22516, + -24432, -8892, 12999, 25604, 19917, 0, -19917, -25605, + -13000, 8892, 24431, 22516, 4515, -16712, -26000, -16712, + 4514, 22516, 24432, 8892, -12999, -25604, -19917, 0, + 19917, 25605, 13000, -8892, -24431, -22516, -4515, 16712, + 26000, 16712, -4514, -22516, -24432, -8892, 12999, 25604, + 19917, 0, -19916, -25605, -13000, 8892, 24431, 22516, + 4515, -16712, -26000, -16712, 4514, 22516, 24432, 8892, + -12999, -25604, -19917, 0, 19916, 25605, 13000, -8892, + -24431, -22516, -4515, 16712, 26000, 16712, -4514, -22516, + -24432, -8892, 12999, 25604, 19917, 0, -19916, -25605, + -13000, 8892, 24431, 22516, 4515, -16712, -26000, -16712, + 4514, 22516, 24432, 8892, -12999, -25604, -19917, 0, + 19916, 25605, 13000, -8892, -24431, -22516, -4515, 16712}, + { +// Carrier 24 Phase 5 + 24020, 7818, -13969, -25777, -19169, 1134, 20627, 25383, + 12005, -9949, -24796, -21928, -3393, 17565, 25975, 15827, + -5627, -23062, -24020, -7818, 13969, 25777, 19169, -1134, + -20627, -25383, -12005, 9949, 24796, 21928, 3393, -17565, + -25975, -15827, 5627, 23062, 24020, 7818, -13969, -25777, + -19169, 1134, 20627, 25383, 12005, -9949, -24796, -21928, + -3393, 17565, 25975, 15827, -5627, -23062, -24020, -7818, + 13969, 25777, 19169, -1133, -20627, -25383, -12005, 9949, + 24796, 21928, 3393, -17565, -25975, -15827, 5627, 23062, + 24020, 7818, -13969, -25777, -19169, 1133, 20627, 25383, + 12005, -9949, -24796, -21928, -3393, 17565, 25975, 15827, + -5627, -23062, -24020, -7818, 13969, 25777, 19169, -1133, + -20627, -25383, -12005, 9949, 24796, 21928, 3393, -17565, + -25975, -15827, 5627, 23062, 24020, 7818, -13969, -25777, + -19169, 1133, 20627, 25383, 12005, -9949, -24796, -21928, + -3393, 17565, 25975, 15827, -5627, -23062, -24020, -7818, + 13969, 25777, 19169, -1133, -20627, -25383, -12005, 9949, + 24796, 21928, 3393, -17565, -25975, -15828, 5627, 23062, + 24020, 7818, -13969, -25777, -19169, 1133, 20627, 25383, + 12005, -9949, -24796, -21928, -3393, 17565, 25975, 15828, + -5627, -23062, -24020, -7818, 13969, 25777, 19169, -1133, + -20627, -25383, -12005, 9949, 24796, 21928, 3393, -17565, + -25975, -15828, 5627, 23062, 24020, 7818, -13969, -25777, + -19169, 1133, 20626, 25383, 12005, -9949, -24796, -21928, + -3394, 17565, 25975, 15828, -5627, -23062, -24021, -7818, + 13969, 25777, 19169, -1133, -20626, -25383, -12005, 9949, + 24796, 21928, 3394, -17565, -25975, -15828, 5627, 23062}, + { +// Carrier 24 Phase 6 + 18384, -2266, -21297, -25114, -10988, 10988, 25114, 21297, + 2266, -18384, -25901, -14913, 6729, 23563, 23564, 6729, + -14912, -25901, -18384, 2266, 21297, 25114, 10988, -10988, + -25114, -21297, -2266, 18384, 25901, 14913, -6729, -23563, + -23564, -6729, 14912, 25901, 18384, -2265, -21297, -25114, + -10988, 10988, 25114, 21297, 2266, -18384, -25901, -14913, + 6729, 23563, 23564, 6729, -14912, -25901, -18384, 2265, + 21297, 25114, 10988, -10987, -25114, -21298, -2266, 18384, + 25901, 14913, -6729, -23563, -23564, -6729, 14912, 25901, + 18384, -2265, -21297, -25114, -10988, 10987, 25114, 21298, + 2266, -18384, -25901, -14913, 6729, 23563, 23564, 6729, + -14912, -25901, -18384, 2265, 21297, 25114, 10988, -10987, + -25114, -21298, -2266, 18384, 25901, 14913, -6729, -23563, + -23564, -6729, 14912, 25901, 18384, -2265, -21297, -25114, + -10988, 10987, 25114, 21298, 2266, -18384, -25901, -14913, + 6729, 23563, 23564, 6729, -14912, -25901, -18384, 2265, + 21297, 25114, 10988, -10987, -25114, -21298, -2266, 18384, + 25901, 14913, -6729, -23563, -23564, -6729, 14912, 25901, + 18384, -2265, -21297, -25114, -10988, 10987, 25114, 21298, + 2266, -18384, -25901, -14913, 6729, 23563, 23564, 6729, + -14912, -25901, -18384, 2265, 21297, 25114, 10988, -10987, + -25113, -21298, -2266, 18384, 25901, 14913, -6729, -23563, + -23564, -6729, 14912, 25901, 18384, -2265, -21297, -25114, + -10988, 10987, 25113, 21298, 2266, -18384, -25901, -14913, + 6728, 23563, 23564, 6729, -14912, -25901, -18385, 2265, + 21297, 25114, 10988, -10987, -25113, -21298, -2266, 18384, + 25901, 14913, -6728, -23563, -23564, -6729, 14912, 25901}, + { +// Carrier 24 Phase 7 + 9949, -12005, -25383, -20627, -1134, 19169, 25777, 13969, + -7818, -24020, -23062, -5627, 15827, 25975, 17565, -3393, + -21928, -24796, -9949, 12005, 25383, 20627, 1134, -19169, + -25777, -13969, 7818, 24020, 23062, 5627, -15827, -25975, + -17565, 3393, 21928, 24796, 9949, -12005, -25383, -20627, + -1134, 19169, 25777, 13969, -7818, -24020, -23062, -5627, + 15827, 25975, 17565, -3393, -21928, -24796, -9949, 12005, + 25383, 20627, 1134, -19169, -25777, -13969, 7818, 24020, + 23062, 5627, -15827, -25975, -17565, 3393, 21928, 24796, + 9949, -12005, -25383, -20627, -1134, 19169, 25777, 13969, + -7818, -24020, -23062, -5627, 15827, 25975, 17565, -3393, + -21928, -24796, -9949, 12005, 25383, 20627, 1134, -19169, + -25777, -13969, 7818, 24020, 23062, 5627, -15827, -25975, + -17565, 3393, 21928, 24796, 9949, -12005, -25383, -20627, + -1134, 19169, 25777, 13969, -7818, -24020, -23062, -5627, + 15827, 25975, 17565, -3393, -21928, -24796, -9949, 12005, + 25383, 20627, 1134, -19169, -25777, -13969, 7818, 24020, + 23062, 5627, -15827, -25975, -17565, 3393, 21928, 24796, + 9949, -12005, -25383, -20627, -1134, 19169, 25777, 13970, + -7818, -24020, -23062, -5627, 15827, 25975, 17565, -3393, + -21928, -24796, -9950, 12005, 25383, 20627, 1134, -19169, + -25777, -13970, 7818, 24020, 23062, 5627, -15827, -25975, + -17565, 3393, 21928, 24796, 9950, -12005, -25383, -20627, + -1134, 19168, 25777, 13970, -7818, -24020, -23062, -5627, + 15827, 25975, 17565, -3393, -21927, -24796, -9950, 12005, + 25383, 20627, 1134, -19168, -25777, -13970, 7818, 24020, + 23062, 5627, -15827, -25975, -17565, 3393, 21927, 24796}, + },{{ + +// Carrier 25 Phase 0 + 0, 20394, 25299, 10988, -11668, -25462, -19917, 756, + 20855, 25114, 10298, -12339, -25604, -19422, 1511, 21297, + 24907, 9599, -12999, -25725, -18911, 2266, 21722, 24680, + 8892, -13649, -25824, -18384, 3018, 22129, 24432, 8178, + -14287, -25901, -17842, 3768, 22516, 24163, 7456, -14912, + -25956, -17284, 4514, 22885, 23873, 6729, -15526, -25989, + -16712, 5257, 23234, 23564, 5996, -16126, -26000, -16126, + 5995, 23563, 23234, 5257, -16712, -25989, -15526, 6729, + 23873, 22885, 4514, -17284, -25956, -14913, 7456, 24163, + 22516, 3768, -17842, -25901, -14287, 8178, 24432, 22129, + 3018, -18384, -25824, -13649, 8892, 24680, 21722, 2266, + -18911, -25725, -13000, 9599, 24907, 21297, 1511, -19422, + -25605, -12339, 10298, 25114, 20855, 756, -19917, -25462, + -11668, 10988, 25299, 20394, 0, -20394, -25299, -10988, + 11668, 25462, 19917, -756, -20855, -25114, -10298, 12339, + 25605, 19422, -1511, -21297, -24907, -9599, 13000, 25725, + 18911, -2266, -21722, -24680, -8892, 13649, 25824, 18384, + -3018, -22129, -24432, -8178, 14287, 25901, 17842, -3768, + -22516, -24163, -7456, 14913, 25956, 17284, -4514, -22885, + -23873, -6729, 15526, 25989, 16712, -5257, -23234, -23563, + -5995, 16126, 26000, 16126, -5996, -23564, -23234, -5257, + 16712, 25989, 15526, -6729, -23873, -22885, -4514, 17284, + 25956, 14912, -7456, -24163, -22516, -3768, 17842, 25901, + 14287, -8178, -24432, -22129, -3018, 18384, 25824, 13649, + -8892, -24680, -21722, -2265, 18911, 25725, 12999, -9599, + -24907, -21297, -1511, 19422, 25604, 12339, -10298, -25114, + -20855, -756, 19917, 25462, 11668, -10988, -25299, -20394}, + { +// Carrier 25 Phase 1 + 9949, 25013, 21078, 1134, -19671, -25536, -12005, 10644, + 25209, 20627, 378, -20158, -25383, -11329, 11329, 25383, + 20158, -378, -20627, -25209, -10644, 12005, 25536, 19671, + -1134, -21078, -25013, -9949, 12671, 25667, 19169, -1889, + -21512, -24796, -9246, 13326, 25777, 18650, -2642, -21928, + -24558, -8536, 13969, 25865, 18115, -3393, -22325, -24300, + -7818, 14601, 25931, 17565, -4141, -22703, -24020, -7093, + 15221, 25975, 17000, -4886, -23062, -23721, -6363, 15827, + 25997, 16421, -5627, -23401, -23401, -5627, 16421, 25997, + 15827, -6363, -23721, -23062, -4886, 17000, 25975, 15221, + -7093, -24020, -22703, -4141, 17565, 25931, 14601, -7818, + -24300, -22325, -3393, 18115, 25865, 13969, -8536, -24558, + -21928, -2642, 18650, 25777, 13326, -9246, -24796, -21512, + -1889, 19169, 25667, 12671, -9949, -25013, -21078, -1134, + 19672, 25536, 12005, -10644, -25209, -20627, -378, 20158, + 25383, 11329, -11329, -25383, -20158, 378, 20627, 25209, + 10644, -12005, -25536, -19671, 1134, 21078, 25013, 9949, + -12671, -25667, -19169, 1889, 21512, 24796, 9246, -13326, + -25777, -18650, 2642, 21928, 24558, 8536, -13969, -25865, + -18115, 3393, 22325, 24300, 7818, -14601, -25931, -17565, + 4142, 22703, 24020, 7093, -15221, -25975, -17000, 4886, + 23062, 23721, 6363, -15827, -25997, -16421, 5627, 23401, + 23401, 5627, -16421, -25997, -15827, 6363, 23721, 23062, + 4886, -17000, -25975, -15221, 7093, 24020, 22703, 4141, + -17565, -25931, -14601, 7818, 24300, 22325, 3393, -18115, + -25865, -13969, 8536, 24558, 21928, 2642, -18650, -25777, + -13326, 9246, 24796, 21512, 1889, -19169, -25667, -12671}, + { +// Carrier 25 Phase 2 + 18384, 25824, 13649, -8892, -24680, -21722, -2266, 18911, + 25725, 13000, -9599, -24907, -21297, -1511, 19422, 25605, + 12339, -10298, -25114, -20855, -756, 19917, 25462, 11668, + -10988, -25299, -20394, 0, 20394, 25299, 10988, -11668, + -25462, -19917, 756, 20855, 25114, 10298, -12339, -25604, + -19422, 1511, 21297, 24907, 9599, -12999, -25725, -18911, + 2266, 21722, 24680, 8892, -13649, -25824, -18384, 3018, + 22129, 24432, 8178, -14287, -25901, -17842, 3768, 22516, + 24163, 7456, -14912, -25956, -17284, 4514, 22885, 23873, + 6729, -15526, -25989, -16712, 5257, 23234, 23564, 5996, + -16126, -26000, -16126, 5996, 23564, 23234, 5257, -16712, + -25989, -15526, 6729, 23873, 22885, 4514, -17284, -25956, + -14912, 7456, 24163, 22516, 3768, -17842, -25901, -14287, + 8178, 24432, 22129, 3018, -18384, -25824, -13649, 8892, + 24680, 21722, 2266, -18911, -25725, -12999, 9599, 24907, + 21297, 1511, -19422, -25604, -12339, 10298, 25114, 20855, + 756, -19917, -25462, -11668, 10988, 25299, 20394, 0, + -20394, -25299, -10988, 11668, 25462, 19917, -756, -20855, + -25114, -10298, 12339, 25605, 19422, -1511, -21297, -24907, + -9599, 13000, 25725, 18911, -2266, -21722, -24680, -8892, + 13649, 25824, 18384, -3018, -22129, -24431, -8178, 14287, + 25901, 17842, -3768, -22516, -24163, -7456, 14913, 25956, + 17284, -4514, -22885, -23873, -6729, 15526, 25989, 16712, + -5257, -23234, -23563, -5995, 16126, 26000, 16126, -5996, + -23564, -23234, -5257, 16712, 25988, 15526, -6729, -23873, + -22885, -4514, 17284, 25956, 14912, -7456, -24163, -22516, + -3768, 17842, 25901, 14287, -8178, -24432, -22129, -3018}, + { +// Carrier 25 Phase 3 + 24020, 22703, 4141, -17565, -25931, -14601, 7818, 24300, + 22325, 3393, -18115, -25865, -13969, 8536, 24558, 21928, + 2642, -18650, -25777, -13326, 9246, 24796, 21512, 1889, + -19169, -25667, -12671, 9949, 25013, 21078, 1134, -19671, + -25536, -12005, 10644, 25209, 20627, 378, -20158, -25383, + -11329, 11329, 25383, 20158, -378, -20627, -25209, -10644, + 12005, 25536, 19671, -1134, -21078, -25013, -9949, 12671, + 25667, 19169, -1889, -21512, -24796, -9246, 13326, 25777, + 18650, -2642, -21928, -24558, -8536, 13969, 25865, 18115, + -3393, -22325, -24300, -7818, 14601, 25931, 17565, -4142, + -22703, -24020, -7093, 15221, 25975, 17000, -4886, -23062, + -23721, -6363, 15827, 25997, 16420, -5627, -23401, -23401, + -5627, 16421, 25997, 15827, -6363, -23721, -23062, -4886, + 17000, 25975, 15221, -7093, -24020, -22703, -4141, 17565, + 25931, 14601, -7818, -24300, -22325, -3393, 18115, 25865, + 13969, -8536, -24558, -21928, -2642, 18650, 25777, 13326, + -9246, -24796, -21512, -1889, 19169, 25667, 12671, -9949, + -25013, -21078, -1134, 19672, 25536, 12005, -10644, -25209, + -20627, -378, 20158, 25383, 11329, -11329, -25383, -20158, + 378, 20627, 25209, 10644, -12005, -25536, -19671, 1134, + 21078, 25013, 9949, -12671, -25667, -19169, 1889, 21512, + 24796, 9246, -13326, -25777, -18650, 2642, 21928, 24558, + 8536, -13969, -25865, -18115, 3393, 22325, 24300, 7818, + -14601, -25931, -17565, 4142, 22703, 24020, 7093, -15221, + -25975, -17000, 4886, 23062, 23721, 6363, -15827, -25997, + -16420, 5627, 23401, 23401, 5627, -16421, -25997, -15827, + 6363, 23721, 23062, 4886, -17000, -25975, -15221, 7093}, + { +// Carrier 25 Phase 4 + 26000, 16126, -5996, -23563, -23234, -5257, 16712, 25989, + 15526, -6729, -23873, -22885, -4514, 17284, 25956, 14912, + -7456, -24163, -22516, -3768, 17842, 25901, 14287, -8178, + -24432, -22129, -3018, 18384, 25824, 13649, -8892, -24680, + -21722, -2266, 18911, 25725, 12999, -9599, -24907, -21297, + -1511, 19422, 25604, 12339, -10298, -25114, -20855, -756, + 19917, 25462, 11668, -10988, -25299, -20394, 0, 20394, + 25299, 10988, -11668, -25462, -19917, 756, 20855, 25114, + 10298, -12339, -25605, -19422, 1511, 21297, 24907, 9599, + -12999, -25725, -18911, 2266, 21722, 24680, 8892, -13649, + -25824, -18384, 3018, 22129, 24432, 8178, -14287, -25901, + -17842, 3768, 22516, 24163, 7456, -14912, -25956, -17284, + 4514, 22885, 23873, 6729, -15526, -25989, -16712, 5257, + 23234, 23564, 5996, -16126, -26000, -16126, 5996, 23564, + 23234, 5257, -16712, -25989, -15526, 6729, 23873, 22885, + 4514, -17284, -25956, -14912, 7456, 24163, 22516, 3768, + -17842, -25901, -14287, 8178, 24432, 22129, 3018, -18384, + -25824, -13649, 8892, 24680, 21722, 2266, -18911, -25725, + -12999, 9599, 24907, 21297, 1511, -19422, -25604, -12339, + 10298, 25114, 20855, 756, -19917, -25462, -11668, 10988, + 25299, 20394, 0, -20394, -25299, -10988, 11668, 25462, + 19917, -756, -20855, -25114, -10298, 12339, 25605, 19422, + -1511, -21297, -24907, -9599, 13000, 25725, 18911, -2266, + -21722, -24680, -8892, 13649, 25824, 18384, -3018, -22129, + -24431, -8178, 14287, 25901, 17842, -3768, -22516, -24163, + -7456, 14913, 25956, 17284, -4514, -22885, -23873, -6729, + 15526, 25989, 16712, -5257, -23234, -23563, -5995, 16126}, + { +// Carrier 25 Phase 5 + 24020, 7093, -15221, -25975, -17000, 4886, 23062, 23721, + 6363, -15827, -25997, -16421, 5627, 23401, 23401, 5627, + -16421, -25997, -15827, 6363, 23721, 23062, 4886, -17000, + -25975, -15221, 7093, 24020, 22703, 4141, -17565, -25931, + -14601, 7818, 24300, 22325, 3393, -18115, -25865, -13969, + 8536, 24558, 21928, 2642, -18650, -25777, -13326, 9246, + 24796, 21512, 1889, -19169, -25667, -12671, 9949, 25013, + 21078, 1134, -19672, -25536, -12005, 10644, 25209, 20627, + 378, -20158, -25383, -11329, 11329, 25383, 20158, -378, + -20627, -25209, -10644, 12005, 25536, 19671, -1134, -21078, + -25013, -9949, 12671, 25667, 19169, -1889, -21512, -24796, + -9246, 13326, 25777, 18650, -2642, -21928, -24558, -8536, + 13969, 25865, 18115, -3393, -22325, -24300, -7818, 14601, + 25931, 17565, -4142, -22703, -24020, -7093, 15221, 25975, + 17000, -4886, -23062, -23721, -6363, 15827, 25997, 16420, + -5627, -23401, -23401, -5627, 16421, 25997, 15827, -6363, + -23721, -23062, -4886, 17000, 25975, 15221, -7093, -24020, + -22703, -4141, 17565, 25931, 14601, -7818, -24300, -22325, + -3393, 18115, 25865, 13969, -8536, -24558, -21928, -2642, + 18650, 25777, 13326, -9246, -24796, -21512, -1889, 19169, + 25667, 12671, -9949, -25013, -21078, -1134, 19672, 25536, + 12005, -10644, -25209, -20627, -378, 20158, 25383, 11329, + -11329, -25383, -20158, 378, 20627, 25209, 10644, -12005, + -25536, -19671, 1134, 21078, 25013, 9949, -12671, -25667, + -19169, 1889, 21512, 24796, 9246, -13326, -25777, -18650, + 2642, 21928, 24558, 8536, -13969, -25865, -18115, 3393, + 22325, 24300, 7818, -14601, -25931, -17565, 4142, 22703}, + { +// Carrier 25 Phase 6 + 18384, -3018, -22129, -24432, -8178, 14287, 25901, 17842, + -3768, -22516, -24163, -7456, 14912, 25956, 17284, -4514, + -22885, -23873, -6729, 15526, 25989, 16712, -5257, -23234, + -23563, -5996, 16126, 26000, 16126, -5996, -23564, -23234, + -5257, 16712, 25989, 15526, -6729, -23873, -22885, -4514, + 17284, 25956, 14912, -7456, -24163, -22516, -3768, 17842, + 25901, 14287, -8178, -24432, -22129, -3018, 18384, 25824, + 13649, -8892, -24680, -21722, -2266, 18911, 25725, 12999, + -9599, -24907, -21297, -1511, 19422, 25604, 12339, -10298, + -25114, -20855, -756, 19917, 25462, 11668, -10988, -25299, + -20394, 0, 20394, 25299, 10988, -11668, -25462, -19917, + 756, 20855, 25114, 10298, -12339, -25605, -19422, 1511, + 21297, 24907, 9599, -13000, -25725, -18911, 2266, 21722, + 24680, 8892, -13649, -25824, -18384, 3018, 22129, 24432, + 8178, -14287, -25901, -17842, 3768, 22516, 24163, 7456, + -14913, -25956, -17284, 4514, 22885, 23873, 6729, -15526, + -25989, -16712, 5257, 23234, 23563, 5995, -16126, -26000, + -16126, 5996, 23564, 23234, 5257, -16712, -25989, -15526, + 6729, 23873, 22885, 4514, -17284, -25956, -14912, 7456, + 24163, 22516, 3768, -17842, -25901, -14287, 8178, 24432, + 22129, 3018, -18384, -25824, -13649, 8892, 24680, 21722, + 2266, -18911, -25725, -12999, 9599, 24907, 21297, 1511, + -19422, -25604, -12339, 10298, 25114, 20855, 756, -19917, + -25462, -11668, 10988, 25299, 20394, 0, -20394, -25299, + -10988, 11668, 25462, 19917, -756, -20855, -25114, -10298, + 12339, 25605, 19422, -1511, -21297, -24907, -9599, 13000, + 25725, 18911, -2266, -21722, -24680, -8892, 13649, 25824}, + { +// Carrier 25 Phase 7 + 9949, -12671, -25667, -19169, 1889, 21512, 24796, 9246, + -13326, -25777, -18650, 2642, 21928, 24558, 8536, -13969, + -25865, -18115, 3393, 22325, 24300, 7818, -14601, -25931, + -17565, 4141, 22703, 24020, 7093, -15221, -25975, -17000, + 4886, 23062, 23721, 6363, -15827, -25997, -16421, 5627, + 23401, 23401, 5627, -16421, -25997, -15827, 6363, 23721, + 23062, 4886, -17000, -25975, -15221, 7093, 24020, 22703, + 4141, -17565, -25931, -14601, 7818, 24300, 22325, 3393, + -18115, -25865, -13969, 8536, 24558, 21928, 2642, -18650, + -25777, -13326, 9246, 24796, 21512, 1889, -19169, -25667, + -12671, 9949, 25013, 21078, 1134, -19672, -25536, -12005, + 10644, 25209, 20627, 378, -20158, -25383, -11329, 11329, + 25383, 20158, -378, -20627, -25209, -10644, 12005, 25536, + 19671, -1134, -21078, -25013, -9949, 12671, 25667, 19169, + -1889, -21512, -24796, -9246, 13326, 25777, 18650, -2642, + -21928, -24558, -8536, 13969, 25865, 18115, -3393, -22325, + -24300, -7818, 14601, 25931, 17565, -4142, -22703, -24020, + -7093, 15221, 25975, 17000, -4886, -23062, -23721, -6363, + 15827, 25997, 16420, -5627, -23401, -23401, -5627, 16421, + 25997, 15827, -6363, -23721, -23062, -4886, 17000, 25975, + 15221, -7093, -24020, -22703, -4141, 17565, 25931, 14601, + -7818, -24300, -22325, -3393, 18115, 25865, 13969, -8536, + -24558, -21928, -2642, 18650, 25777, 13326, -9246, -24796, + -21512, -1889, 19169, 25667, 12671, -9949, -25013, -21078, + -1134, 19672, 25536, 12005, -10644, -25209, -20627, -378, + 20158, 25383, 11329, -11329, -25383, -20158, 378, 20627, + 25209, 10644, -12005, -25536, -19671, 1134, 21078, 25013}, + },{{ + +// Carrier 26 Phase 0 + 0, 20855, 24907, 8892, -14287, -25956, -16712, 5996, + 23873, 22516, 3018, -18911, -25604, -11668, 11668, 25605, + 18911, -3018, -22516, -23873, -5995, 16712, 25956, 14287, + -8892, -24907, -20855, 0, 20855, 24907, 8892, -14287, + -25956, -16712, 5996, 23873, 22516, 3018, -18911, -25604, + -11668, 11668, 25605, 18911, -3018, -22516, -23873, -5995, + 16712, 25956, 14287, -8892, -24907, -20855, 0, 20855, + 24907, 8892, -14287, -25956, -16712, 5996, 23873, 22516, + 3018, -18911, -25604, -11668, 11668, 25605, 18911, -3018, + -22516, -23873, -5995, 16712, 25956, 14287, -8892, -24907, + -20855, 0, 20855, 24907, 8892, -14287, -25956, -16712, + 5996, 23873, 22516, 3018, -18911, -25604, -11668, 11669, + 25605, 18911, -3018, -22516, -23873, -5995, 16712, 25955, + 14286, -8892, -24907, -20855, 0, 20855, 24907, 8892, + -14287, -25956, -16712, 5996, 23873, 22516, 3018, -18911, + -25604, -11668, 11669, 25605, 18911, -3018, -22516, -23873, + -5995, 16712, 25955, 14286, -8892, -24907, -20854, 0, + 20855, 24907, 8892, -14287, -25956, -16712, 5996, 23873, + 22516, 3018, -18911, -25604, -11668, 11669, 25605, 18911, + -3018, -22516, -23873, -5995, 16712, 25955, 14286, -8892, + -24907, -20854, 0, 20855, 24907, 8892, -14287, -25956, + -16712, 5996, 23873, 22516, 3017, -18912, -25604, -11668, + 11669, 25605, 18911, -3018, -22516, -23873, -5995, 16712, + 25955, 14286, -8892, -24907, -20854, 0, 20855, 24907, + 8892, -14287, -25956, -16712, 5996, 23873, 22516, 3017, + -18912, -25604, -11668, 11669, 25605, 18911, -3018, -22516, + -23873, -5995, 16712, 25955, 14286, -8893, -24907, -20854}, + { +// Carrier 26 Phase 1 + 9949, 25209, 20158, -1134, -21512, -24558, -7818, 15221, + 25997, 15827, -7093, -24300, -21928, -1889, 19672, 25383, + 10644, -12671, -25777, -18115, 4142, 23062, 23401, 4886, + -17565, -25865, -13326, 9949, 25209, 20158, -1134, -21512, + -24558, -7818, 15221, 25997, 15827, -7093, -24300, -21928, + -1889, 19672, 25383, 10644, -12671, -25777, -18115, 4142, + 23062, 23401, 4886, -17565, -25865, -13325, 9949, 25209, + 20158, -1134, -21512, -24558, -7818, 15221, 25997, 15827, + -7094, -24300, -21928, -1888, 19672, 25383, 10644, -12671, + -25777, -18115, 4142, 23062, 23401, 4886, -17565, -25865, + -13325, 9949, 25209, 20157, -1134, -21512, -24558, -7818, + 15221, 25997, 15827, -7094, -24300, -21928, -1888, 19672, + 25383, 10643, -12671, -25777, -18115, 4142, 23062, 23401, + 4886, -17565, -25865, -13325, 9950, 25209, 20157, -1134, + -21512, -24558, -7818, 15221, 25997, 15827, -7094, -24300, + -21928, -1888, 19672, 25383, 10643, -12671, -25777, -18115, + 4142, 23062, 23401, 4886, -17565, -25865, -13325, 9950, + 25209, 20157, -1134, -21512, -24558, -7817, 15221, 25997, + 15827, -7094, -24300, -21927, -1888, 19672, 25383, 10643, + -12671, -25777, -18115, 4142, 23062, 23401, 4886, -17565, + -25865, -13325, 9950, 25209, 20157, -1134, -21512, -24558, + -7817, 15221, 25997, 15827, -7094, -24300, -21927, -1888, + 19672, 25383, 10643, -12671, -25777, -18115, 4142, 23062, + 23401, 4886, -17565, -25865, -13325, 9950, 25209, 20157, + -1134, -21512, -24558, -7817, 15221, 25997, 15827, -7094, + -24300, -21927, -1888, 19672, 25383, 10643, -12671, -25777, + -18115, 4142, 23062, 23401, 4886, -17565, -25865, -13325}, + { +// Carrier 26 Phase 2 + 18384, 25725, 12339, -10988, -25462, -19422, 2266, 22129, + 24163, 6729, -16126, -25989, -14912, 8178, 24680, 21297, + 756, -20394, -25114, -9599, 13649, 25901, 17284, -5257, + -23564, -22885, -3768, 18384, 25725, 12339, -10988, -25462, + -19422, 2266, 22129, 24162, 6729, -16126, -25988, -14912, + 8178, 24680, 21297, 756, -20394, -25114, -9599, 13649, + 25901, 17284, -5257, -23564, -22885, -3768, 18384, 25725, + 12339, -10988, -25462, -19422, 2266, 22129, 24162, 6729, + -16126, -25988, -14912, 8178, 24680, 21297, 756, -20394, + -25114, -9599, 13649, 25901, 17284, -5257, -23564, -22885, + -3768, 18384, 25725, 12339, -10988, -25462, -19422, 2266, + 22129, 24162, 6729, -16126, -25988, -14912, 8178, 24680, + 21297, 755, -20394, -25114, -9599, 13649, 25901, 17284, + -5257, -23564, -22885, -3767, 18384, 25725, 12339, -10988, + -25462, -19422, 2266, 22129, 24162, 6728, -16126, -25988, + -14912, 8178, 24680, 21297, 755, -20395, -25113, -9599, + 13649, 25901, 17284, -5258, -23564, -22885, -3767, 18385, + 25725, 12339, -10988, -25462, -19422, 2266, 22129, 24162, + 6728, -16126, -25988, -14912, 8178, 24680, 21297, 755, + -20395, -25113, -9598, 13649, 25901, 17284, -5258, -23564, + -22885, -3767, 18385, 25725, 12339, -10988, -25462, -19422, + 2266, 22129, 24162, 6728, -16126, -25988, -14912, 8178, + 24680, 21297, 755, -20395, -25113, -9598, 13649, 25901, + 17284, -5258, -23564, -22884, -3767, 18385, 25725, 12339, + -10988, -25462, -19422, 2266, 22129, 24162, 6728, -16126, + -25988, -14912, 8178, 24680, 21297, 755, -20395, -25113, + -9598, 13649, 25901, 17284, -5258, -23564, -22884, -3767}, + { +// Carrier 26 Phase 3 + 24020, 22325, 2642, -19169, -25536, -11329, 12005, 25667, + 18650, -3393, -22703, -23721, -5627, 17000, 25931, 13969, + -9246, -25013, -20627, 378, 21078, 24796, 8536, -14601, + -25975, -16420, 6363, 24020, 22325, 2642, -19169, -25536, + -11329, 12005, 25667, 18650, -3393, -22703, -23721, -5627, + 17000, 25931, 13969, -9247, -25013, -20627, 378, 21078, + 24796, 8536, -14601, -25975, -16420, 6363, 24020, 22325, + 2642, -19169, -25536, -11329, 12005, 25667, 18650, -3393, + -22703, -23721, -5627, 17000, 25931, 13969, -9247, -25013, + -20627, 378, 21078, 24796, 8536, -14601, -25975, -16420, + 6363, 24020, 22325, 2642, -19169, -25536, -11329, 12005, + 25667, 18650, -3393, -22703, -23721, -5627, 17000, 25931, + 13969, -9247, -25013, -20627, 378, 21078, 24796, 8535, + -14601, -25975, -16420, 6363, 24020, 22325, 2642, -19169, + -25536, -11329, 12005, 25668, 18649, -3394, -22703, -23721, + -5627, 17000, 25931, 13969, -9247, -25013, -20626, 378, + 21079, 24796, 8535, -14601, -25975, -16420, 6363, 24021, + 22325, 2642, -19169, -25536, -11329, 12005, 25668, 18649, + -3394, -22703, -23721, -5627, 17000, 25931, 13969, -9247, + -25013, -20626, 378, 21079, 24796, 8535, -14602, -25975, + -16420, 6363, 24021, 22324, 2642, -19169, -25536, -11329, + 12005, 25668, 18649, -3394, -22703, -23721, -5626, 17000, + 25931, 13969, -9247, -25013, -20626, 378, 21079, 24796, + 8535, -14602, -25975, -16420, 6363, 24021, 22324, 2641, + -19169, -25536, -11329, 12005, 25668, 18649, -3394, -22703, + -23721, -5626, 17000, 25931, 13969, -9247, -25013, -20626, + 378, 21079, 24796, 8535, -14602, -25975, -16420, 6363}, + { +// Carrier 26 Phase 4 + 26000, 15526, -7456, -24432, -21722, -1511, 19917, 25299, + 10298, -13000, -25824, -17842, 4514, 23234, 23234, 4514, + -17842, -25824, -12999, 10298, 25299, 19917, -1511, -21722, + -24431, -7456, 15526, 26000, 15526, -7456, -24432, -21722, + -1511, 19917, 25299, 10297, -13000, -25824, -17842, 4514, + 23234, 23234, 4514, -17842, -25824, -12999, 10298, 25299, + 19917, -1511, -21722, -24431, -7456, 15526, 26000, 15526, + -7457, -24432, -21722, -1511, 19917, 25299, 10297, -13000, + -25824, -17842, 4515, 23234, 23234, 4514, -17842, -25824, + -12999, 10298, 25299, 19917, -1511, -21722, -24431, -7456, + 15526, 26000, 15525, -7457, -24432, -21722, -1511, 19917, + 25299, 10297, -13000, -25824, -17842, 4515, 23234, 23234, + 4514, -17842, -25824, -12999, 10298, 25299, 19916, -1512, + -21722, -24431, -7456, 15526, 26000, 15525, -7457, -24432, + -21722, -1511, 19917, 25299, 10297, -13000, -25824, -17842, + 4515, 23234, 23234, 4514, -17842, -25824, -12999, 10298, + 25299, 19916, -1512, -21722, -24431, -7456, 15526, 26000, + 15525, -7457, -24432, -21722, -1511, 19917, 25299, 10297, + -13000, -25824, -17841, 4515, 23234, 23234, 4514, -17842, + -25824, -12999, 10298, 25299, 19916, -1512, -21722, -24431, + -7456, 15526, 26000, 15525, -7457, -24432, -21722, -1511, + 19917, 25299, 10297, -13000, -25824, -17841, 4515, 23234, + 23234, 4514, -17842, -25824, -12999, 10298, 25299, 19916, + -1512, -21722, -24431, -7456, 15526, 26000, 15525, -7457, + -24432, -21722, -1511, 19917, 25299, 10297, -13000, -25824, + -17841, 4515, 23234, 23234, 4514, -17842, -25824, -12999, + 10298, 25299, 19916, -1512, -21723, -24431, -7456, 15526}, + { +// Carrier 26 Phase 5 + 24020, 6363, -16421, -25975, -14601, 8536, 24796, 21078, + 378, -20627, -25013, -9246, 13969, 25931, 17000, -5627, + -23721, -22703, -3393, 18650, 25667, 12005, -11329, -25536, + -19169, 2642, 22325, 24020, 6363, -16421, -25975, -14601, + 8536, 24796, 21078, 378, -20627, -25013, -9246, 13969, + 25931, 17000, -5627, -23721, -22703, -3393, 18650, 25667, + 12005, -11329, -25536, -19169, 2642, 22325, 24020, 6363, + -16421, -25975, -14601, 8536, 24796, 21078, 377, -20627, + -25013, -9246, 13969, 25931, 17000, -5627, -23721, -22703, + -3393, 18650, 25667, 12005, -11329, -25536, -19169, 2642, + 22325, 24020, 6363, -16421, -25975, -14601, 8536, 24796, + 21078, 377, -20627, -25013, -9246, 13970, 25931, 17000, + -5627, -23721, -22703, -3393, 18650, 25667, 12005, -11329, + -25536, -19169, 2642, 22325, 24020, 6363, -16421, -25975, + -14601, 8536, 24796, 21078, 377, -20627, -25013, -9246, + 13970, 25931, 17000, -5627, -23721, -22703, -3393, 18650, + 25667, 12005, -11329, -25536, -19168, 2642, 22325, 24020, + 6362, -16421, -25975, -14601, 8536, 24796, 21078, 377, + -20627, -25013, -9246, 13970, 25931, 17000, -5627, -23721, + -22703, -3393, 18650, 25667, 12005, -11330, -25536, -19168, + 2642, 22325, 24020, 6362, -16421, -25975, -14601, 8536, + 24796, 21078, 377, -20627, -25013, -9246, 13970, 25931, + 17000, -5627, -23721, -22703, -3393, 18650, 25667, 12005, + -11330, -25536, -19168, 2643, 22325, 24020, 6362, -16421, + -25975, -14601, 8536, 24796, 21078, 377, -20627, -25013, + -9246, 13970, 25931, 16999, -5627, -23721, -22703, -3393, + 18650, 25667, 12004, -11330, -25536, -19168, 2643, 22325}, + { +// Carrier 26 Phase 6 + 18384, -3768, -22885, -23563, -5257, 17284, 25901, 13649, + -9599, -25114, -20394, 756, 21297, 24680, 8178, -14913, + -25989, -16126, 6729, 24163, 22129, 2265, -19422, -25462, + -10988, 12339, 25725, 18384, -3768, -22885, -23563, -5257, + 17284, 25901, 13649, -9599, -25114, -20394, 756, 21298, + 24680, 8178, -14913, -25989, -16126, 6729, 24163, 22128, + 2265, -19422, -25462, -10987, 12339, 25725, 18384, -3768, + -22885, -23563, -5257, 17284, 25901, 13649, -9599, -25114, + -20394, 756, 21298, 24680, 8177, -14913, -25989, -16125, + 6729, 24163, 22128, 2265, -19422, -25462, -10987, 12339, + 25725, 18384, -3768, -22885, -23563, -5257, 17284, 25901, + 13649, -9599, -25114, -20394, 756, 21298, 24680, 8177, + -14913, -25989, -16125, 6729, 24163, 22128, 2265, -19422, + -25462, -10987, 12339, 25725, 18384, -3768, -22885, -23563, + -5257, 17284, 25901, 13649, -9599, -25114, -20394, 756, + 21298, 24680, 8177, -14913, -25989, -16125, 6729, 24163, + 22128, 2265, -19422, -25462, -10987, 12339, 25725, 18384, + -3768, -22885, -23563, -5257, 17284, 25901, 13649, -9599, + -25114, -20394, 756, 21298, 24680, 8177, -14913, -25989, + -16125, 6729, 24163, 22128, 2265, -19422, -25462, -10987, + 12340, 25725, 18384, -3768, -22885, -23563, -5257, 17285, + 25901, 13648, -9599, -25114, -20394, 756, 21298, 24680, + 8177, -14913, -25989, -16125, 6729, 24163, 22128, 2265, + -19422, -25462, -10987, 12340, 25725, 18384, -3768, -22885, + -23563, -5257, 17285, 25901, 13648, -9599, -25114, -20394, + 756, 21298, 24680, 8177, -14913, -25989, -16125, 6729, + 24163, 22128, 2265, -19423, -25462, -10987, 12340, 25725}, + { +// Carrier 26 Phase 7 + 9949, -13326, -25865, -17565, 4886, 23401, 23062, 4141, + -18115, -25777, -12671, 10644, 25383, 19671, -1889, -21928, + -24300, -7093, 15827, 25997, 15221, -7818, -24558, -21512, + -1134, 20158, 25209, 9949, -13326, -25865, -17565, 4886, + 23401, 23062, 4141, -18115, -25777, -12671, 10644, 25383, + 19671, -1889, -21928, -24300, -7093, 15827, 25997, 15221, + -7818, -24558, -21512, -1133, 20158, 25209, 9949, -13326, + -25865, -17565, 4886, 23401, 23062, 4141, -18115, -25777, + -12670, 10644, 25383, 19671, -1889, -21928, -24300, -7093, + 15827, 25997, 15221, -7818, -24558, -21512, -1133, 20158, + 25209, 9949, -13326, -25865, -17565, 4887, 23401, 23062, + 4141, -18115, -25777, -12670, 10644, 25383, 19671, -1889, + -21928, -24299, -7093, 15828, 25997, 15220, -7818, -24558, + -21512, -1133, 20158, 25209, 9949, -13326, -25865, -17565, + 4887, 23401, 23062, 4141, -18115, -25777, -12670, 10644, + 25383, 19671, -1889, -21928, -24299, -7093, 15828, 25997, + 15220, -7818, -24558, -21512, -1133, 20158, 25209, 9949, + -13326, -25865, -17565, 4887, 23401, 23062, 4141, -18115, + -25777, -12670, 10644, 25383, 19671, -1889, -21928, -24299, + -7093, 15828, 25997, 15220, -7818, -24558, -21512, -1133, + 20158, 25209, 9949, -13326, -25865, -17565, 4887, 23401, + 23062, 4141, -18115, -25777, -12670, 10644, 25383, 19671, + -1889, -21928, -24299, -7093, 15828, 25997, 15220, -7818, + -24558, -21512, -1133, 20158, 25209, 9949, -13326, -25865, + -17564, 4887, 23401, 23062, 4141, -18115, -25777, -12670, + 10644, 25383, 19671, -1889, -21928, -24299, -7093, 15828, + 25997, 15220, -7818, -24558, -21512, -1133, 20158, 25209}, + },{{ + +// Carrier 27 Phase 0 + 0, 21297, 24432, 6729, -16712, -25901, -13000, 10988, + 25605, 18384, -4514, -23564, -22516, -2266, 19917, 25114, + 8892, -14912, -26000, -14912, 8892, 25114, 19917, -2266, + -22516, -23564, -4514, 18384, 25605, 10988, -12999, -25901, + -16712, 6729, 24432, 21297, 0, -21297, -24432, -6729, + 16712, 25901, 13000, -10988, -25605, -18384, 4514, 23564, + 22516, 2266, -19917, -25114, -8892, 14912, 26000, 14912, + -8892, -25114, -19917, 2266, 22516, 23564, 4514, -18384, + -25605, -10988, 12999, 25901, 16712, -6729, -24432, -21297, + 0, 21297, 24432, 6729, -16712, -25901, -13000, 10988, + 25605, 18384, -4514, -23564, -22516, -2266, 19917, 25114, + 8892, -14912, -26000, -14912, 8892, 25114, 19917, -2266, + -22516, -23564, -4514, 18384, 25605, 10988, -12999, -25901, + -16712, 6729, 24432, 21297, 0, -21297, -24432, -6729, + 16712, 25901, 13000, -10988, -25605, -18384, 4514, 23564, + 22516, 2266, -19917, -25114, -8892, 14912, 26000, 14912, + -8892, -25114, -19917, 2266, 22516, 23564, 4514, -18384, + -25605, -10988, 12999, 25901, 16712, -6729, -24432, -21297, + 0, 21297, 24432, 6729, -16712, -25901, -13000, 10988, + 25605, 18384, -4514, -23564, -22516, -2266, 19917, 25114, + 8892, -14912, -26000, -14912, 8892, 25114, 19917, -2266, + -22516, -23564, -4514, 18384, 25605, 10988, -12999, -25901, + -16712, 6729, 24432, 21297, 0, -21297, -24432, -6729, + 16712, 25901, 13000, -10988, -25605, -18384, 4514, 23564, + 22516, 2266, -19917, -25114, -8892, 14912, 26000, 14912, + -8892, -25114, -19917, 2266, 22516, 23564, 4514, -18384, + -25605, -10988, 12999, 25901, 16712, -6729, -24432, -21297}, + { +// Carrier 27 Phase 1 + 9949, 25383, 19169, -3393, -23062, -23062, -3393, 19169, + 25383, 9949, -13969, -25975, -15827, 7818, 24796, 20627, + -1134, -21928, -24020, -5627, 17565, 25777, 12005, -12005, + -25777, -17565, 5627, 24020, 21928, 1134, -20627, -24796, + -7818, 15827, 25975, 13969, -9949, -25383, -19169, 3393, + 23062, 23062, 3393, -19169, -25383, -9949, 13969, 25975, + 15827, -7818, -24796, -20627, 1134, 21928, 24020, 5627, + -17565, -25777, -12005, 12005, 25777, 17565, -5627, -24020, + -21928, -1134, 20627, 24796, 7818, -15827, -25975, -13969, + 9949, 25383, 19169, -3393, -23062, -23062, -3393, 19169, + 25383, 9949, -13969, -25975, -15827, 7818, 24796, 20627, + -1134, -21928, -24020, -5627, 17565, 25777, 12005, -12005, + -25777, -17565, 5627, 24020, 21928, 1134, -20627, -24796, + -7818, 15827, 25975, 13969, -9949, -25383, -19169, 3393, + 23062, 23062, 3393, -19169, -25383, -9949, 13969, 25975, + 15827, -7818, -24796, -20627, 1134, 21928, 24020, 5627, + -17565, -25777, -12005, 12005, 25777, 17565, -5627, -24020, + -21928, -1134, 20627, 24796, 7818, -15827, -25975, -13969, + 9949, 25383, 19169, -3393, -23062, -23062, -3393, 19169, + 25383, 9949, -13969, -25975, -15827, 7818, 24796, 20627, + -1134, -21928, -24020, -5627, 17565, 25777, 12005, -12005, + -25777, -17565, 5627, 24020, 21928, 1134, -20627, -24796, + -7818, 15827, 25975, 13969, -9949, -25383, -19169, 3393, + 23062, 23062, 3393, -19169, -25383, -9949, 13969, 25975, + 15827, -7818, -24796, -20627, 1134, 21928, 24020, 5627, + -17565, -25777, -12005, 12005, 25777, 17565, -5627, -24020, + -21928, -1134, 20627, 24796, 7818, -15827, -25975, -13969}, + { +// Carrier 27 Phase 2 + 18384, 25605, 10988, -12999, -25901, -16712, 6729, 24432, + 21297, 0, -21297, -24432, -6729, 16712, 25901, 13000, + -10988, -25605, -18384, 4514, 23564, 22516, 2266, -19917, + -25114, -8892, 14912, 26000, 14912, -8892, -25114, -19917, + 2266, 22516, 23564, 4514, -18384, -25605, -10988, 12999, + 25901, 16712, -6729, -24432, -21297, 0, 21297, 24432, + 6729, -16712, -25901, -13000, 10988, 25605, 18384, -4514, + -23564, -22516, -2266, 19917, 25114, 8892, -14912, -26000, + -14912, 8892, 25114, 19917, -2266, -22516, -23564, -4514, + 18384, 25605, 10988, -12999, -25901, -16712, 6729, 24432, + 21297, 0, -21297, -24432, -6729, 16712, 25901, 13000, + -10988, -25605, -18384, 4514, 23564, 22516, 2266, -19917, + -25114, -8892, 14912, 26000, 14912, -8892, -25114, -19917, + 2266, 22516, 23564, 4514, -18384, -25605, -10988, 12999, + 25901, 16712, -6729, -24432, -21297, 0, 21297, 24432, + 6729, -16712, -25901, -13000, 10988, 25605, 18384, -4514, + -23564, -22516, -2266, 19917, 25114, 8892, -14912, -26000, + -14912, 8892, 25114, 19917, -2266, -22516, -23564, -4514, + 18384, 25605, 10988, -12999, -25901, -16712, 6729, 24432, + 21297, 0, -21297, -24432, -6729, 16712, 25901, 13000, + -10988, -25605, -18384, 4514, 23564, 22516, 2266, -19917, + -25114, -8892, 14912, 26000, 14912, -8892, -25114, -19917, + 2266, 22516, 23564, 4514, -18384, -25605, -10988, 12999, + 25901, 16712, -6729, -24432, -21297, 0, 21297, 24432, + 6729, -16712, -25901, -13000, 10988, 25605, 18384, -4514, + -23564, -22516, -2266, 19917, 25114, 8892, -14912, -26000, + -14912, 8892, 25114, 19917, -2266, -22516, -23564, -4514}, + { +// Carrier 27 Phase 3 + 24020, 21928, 1134, -20627, -24796, -7818, 15827, 25975, + 13969, -9949, -25383, -19169, 3393, 23062, 23062, 3393, + -19169, -25383, -9949, 13969, 25975, 15827, -7818, -24796, + -20627, 1134, 21928, 24020, 5627, -17565, -25777, -12005, + 12005, 25777, 17565, -5627, -24020, -21928, -1134, 20627, + 24796, 7818, -15827, -25975, -13969, 9949, 25383, 19169, + -3393, -23062, -23062, -3393, 19169, 25383, 9949, -13969, + -25975, -15827, 7818, 24796, 20627, -1134, -21928, -24020, + -5627, 17565, 25777, 12005, -12005, -25777, -17565, 5627, + 24020, 21928, 1134, -20627, -24796, -7818, 15827, 25975, + 13969, -9949, -25383, -19169, 3393, 23062, 23062, 3393, + -19169, -25383, -9949, 13969, 25975, 15827, -7818, -24796, + -20627, 1134, 21928, 24020, 5627, -17565, -25777, -12005, + 12005, 25777, 17565, -5627, -24020, -21928, -1134, 20627, + 24796, 7818, -15827, -25975, -13969, 9949, 25383, 19169, + -3393, -23062, -23062, -3393, 19169, 25383, 9949, -13969, + -25975, -15827, 7818, 24796, 20627, -1134, -21928, -24020, + -5627, 17565, 25777, 12005, -12005, -25777, -17565, 5627, + 24020, 21928, 1134, -20627, -24796, -7818, 15827, 25975, + 13969, -9949, -25383, -19169, 3393, 23062, 23062, 3393, + -19169, -25383, -9949, 13969, 25975, 15827, -7818, -24796, + -20627, 1134, 21928, 24020, 5627, -17565, -25777, -12005, + 12005, 25777, 17565, -5627, -24020, -21928, -1134, 20627, + 24796, 7818, -15827, -25975, -13969, 9949, 25383, 19169, + -3393, -23062, -23062, -3393, 19169, 25383, 9949, -13969, + -25975, -15827, 7818, 24796, 20627, -1134, -21928, -24020, + -5627, 17565, 25777, 12005, -12005, -25777, -17565, 5627}, + { +// Carrier 27 Phase 4 + 26000, 14912, -8892, -25114, -19917, 2266, 22516, 23564, + 4514, -18384, -25605, -10988, 12999, 25901, 16712, -6729, + -24432, -21297, 0, 21297, 24432, 6729, -16712, -25901, + -13000, 10988, 25605, 18384, -4514, -23564, -22516, -2266, + 19917, 25114, 8892, -14912, -26000, -14912, 8892, 25114, + 19917, -2266, -22516, -23564, -4514, 18384, 25605, 10988, + -12999, -25901, -16712, 6729, 24432, 21297, 0, -21297, + -24432, -6729, 16712, 25901, 13000, -10988, -25605, -18384, + 4514, 23564, 22516, 2266, -19917, -25114, -8892, 14912, + 26000, 14912, -8892, -25114, -19917, 2266, 22516, 23564, + 4514, -18384, -25605, -10988, 12999, 25901, 16712, -6729, + -24432, -21297, 0, 21297, 24432, 6729, -16712, -25901, + -13000, 10988, 25605, 18384, -4514, -23564, -22516, -2266, + 19917, 25114, 8892, -14912, -26000, -14912, 8892, 25114, + 19917, -2266, -22516, -23564, -4514, 18384, 25605, 10988, + -12999, -25901, -16712, 6729, 24432, 21297, 0, -21297, + -24432, -6729, 16712, 25901, 13000, -10988, -25605, -18384, + 4514, 23564, 22516, 2266, -19917, -25114, -8892, 14912, + 26000, 14912, -8892, -25114, -19917, 2266, 22516, 23564, + 4514, -18384, -25605, -10988, 12999, 25901, 16712, -6729, + -24432, -21297, 0, 21297, 24432, 6729, -16712, -25901, + -13000, 10988, 25605, 18384, -4514, -23564, -22516, -2266, + 19917, 25114, 8892, -14912, -26000, -14912, 8892, 25114, + 19917, -2266, -22516, -23564, -4514, 18384, 25605, 10988, + -12999, -25901, -16712, 6729, 24432, 21297, 0, -21297, + -24432, -6729, 16712, 25901, 13000, -10988, -25605, -18384, + 4514, 23564, 22516, 2266, -19917, -25114, -8892, 14912}, + { +// Carrier 27 Phase 5 + 24020, 5627, -17565, -25777, -12005, 12005, 25777, 17565, + -5627, -24020, -21928, -1134, 20627, 24796, 7818, -15827, + -25975, -13969, 9949, 25383, 19169, -3393, -23062, -23062, + -3393, 19169, 25383, 9949, -13969, -25975, -15827, 7818, + 24796, 20627, -1134, -21928, -24020, -5627, 17565, 25777, + 12005, -12005, -25777, -17565, 5627, 24020, 21928, 1134, + -20627, -24796, -7818, 15827, 25975, 13969, -9949, -25383, + -19169, 3393, 23062, 23062, 3393, -19169, -25383, -9949, + 13969, 25975, 15827, -7818, -24796, -20627, 1134, 21928, + 24020, 5627, -17565, -25777, -12005, 12005, 25777, 17565, + -5627, -24020, -21928, -1134, 20627, 24796, 7818, -15827, + -25975, -13969, 9949, 25383, 19169, -3393, -23062, -23062, + -3393, 19169, 25383, 9949, -13969, -25975, -15827, 7818, + 24796, 20627, -1134, -21928, -24020, -5627, 17565, 25777, + 12005, -12005, -25777, -17565, 5627, 24020, 21928, 1134, + -20627, -24796, -7818, 15827, 25975, 13969, -9949, -25383, + -19169, 3393, 23062, 23062, 3393, -19169, -25383, -9949, + 13969, 25975, 15827, -7818, -24796, -20627, 1134, 21928, + 24020, 5627, -17565, -25777, -12005, 12005, 25777, 17565, + -5627, -24020, -21928, -1134, 20627, 24796, 7818, -15827, + -25975, -13969, 9949, 25383, 19169, -3393, -23062, -23062, + -3393, 19169, 25383, 9949, -13969, -25975, -15827, 7818, + 24796, 20627, -1134, -21928, -24020, -5627, 17565, 25777, + 12005, -12005, -25777, -17565, 5627, 24020, 21928, 1134, + -20627, -24796, -7818, 15827, 25975, 13969, -9949, -25383, + -19169, 3393, 23062, 23062, 3393, -19169, -25383, -9949, + 13969, 25975, 15827, -7818, -24796, -20627, 1134, 21928}, + { +// Carrier 27 Phase 6 + 18384, -4514, -23564, -22516, -2266, 19917, 25114, 8892, + -14912, -26000, -14912, 8892, 25114, 19917, -2266, -22516, + -23564, -4514, 18384, 25605, 10988, -12999, -25901, -16712, + 6729, 24432, 21297, 0, -21297, -24432, -6729, 16712, + 25901, 13000, -10988, -25605, -18384, 4514, 23564, 22516, + 2266, -19917, -25114, -8892, 14912, 26000, 14912, -8892, + -25114, -19917, 2266, 22516, 23564, 4514, -18384, -25605, + -10988, 12999, 25901, 16712, -6729, -24432, -21297, 0, + 21297, 24432, 6729, -16712, -25901, -13000, 10988, 25605, + 18384, -4514, -23564, -22516, -2266, 19917, 25114, 8892, + -14912, -26000, -14912, 8892, 25114, 19917, -2266, -22516, + -23564, -4514, 18384, 25605, 10988, -12999, -25901, -16712, + 6729, 24432, 21297, 0, -21297, -24432, -6729, 16712, + 25901, 13000, -10988, -25605, -18384, 4514, 23564, 22516, + 2266, -19917, -25114, -8892, 14912, 26000, 14912, -8892, + -25114, -19917, 2266, 22516, 23564, 4514, -18384, -25605, + -10988, 12999, 25901, 16712, -6729, -24432, -21297, 0, + 21297, 24432, 6729, -16712, -25901, -13000, 10988, 25605, + 18384, -4514, -23564, -22516, -2266, 19917, 25114, 8892, + -14912, -26000, -14912, 8892, 25114, 19917, -2266, -22516, + -23564, -4514, 18384, 25605, 10988, -12999, -25901, -16712, + 6729, 24432, 21297, 0, -21297, -24432, -6729, 16712, + 25901, 13000, -10988, -25605, -18384, 4514, 23564, 22516, + 2266, -19917, -25114, -8892, 14912, 26000, 14912, -8892, + -25114, -19917, 2266, 22516, 23564, 4514, -18384, -25605, + -10988, 12999, 25901, 16712, -6729, -24432, -21297, 0, + 21297, 24432, 6729, -16712, -25901, -13000, 10988, 25605}, + { +// Carrier 27 Phase 7 + 9949, -13969, -25975, -15827, 7818, 24796, 20627, -1134, + -21928, -24020, -5627, 17565, 25777, 12005, -12005, -25777, + -17565, 5627, 24020, 21928, 1134, -20627, -24796, -7818, + 15827, 25975, 13969, -9949, -25383, -19169, 3393, 23062, + 23062, 3393, -19169, -25383, -9949, 13969, 25975, 15827, + -7818, -24796, -20627, 1134, 21928, 24020, 5627, -17565, + -25777, -12005, 12005, 25777, 17565, -5627, -24020, -21928, + -1134, 20627, 24796, 7818, -15827, -25975, -13969, 9949, + 25383, 19169, -3393, -23062, -23062, -3393, 19169, 25383, + 9949, -13969, -25975, -15827, 7818, 24796, 20627, -1134, + -21928, -24020, -5627, 17565, 25777, 12005, -12005, -25777, + -17565, 5627, 24020, 21928, 1134, -20627, -24796, -7818, + 15827, 25975, 13969, -9949, -25383, -19169, 3393, 23062, + 23062, 3393, -19169, -25383, -9949, 13969, 25975, 15827, + -7818, -24796, -20627, 1134, 21928, 24020, 5627, -17565, + -25777, -12005, 12005, 25777, 17565, -5627, -24020, -21928, + -1134, 20627, 24796, 7818, -15827, -25975, -13969, 9949, + 25383, 19169, -3393, -23062, -23062, -3393, 19169, 25383, + 9949, -13969, -25975, -15827, 7818, 24796, 20627, -1134, + -21928, -24020, -5627, 17565, 25777, 12005, -12005, -25777, + -17565, 5627, 24020, 21928, 1134, -20627, -24796, -7818, + 15827, 25975, 13969, -9949, -25383, -19169, 3393, 23062, + 23062, 3393, -19169, -25383, -9949, 13969, 25975, 15827, + -7818, -24796, -20627, 1134, 21928, 24020, 5627, -17565, + -25777, -12005, 12005, 25777, 17565, -5627, -24020, -21928, + -1134, 20627, 24796, 7818, -15827, -25975, -13969, 9949, + 25383, 19169, -3393, -23062, -23062, -3393, 19169, 25383}, + },{{ + +// Carrier 28 Phase 0 + 0, 21722, 23873, 4514, -18911, -25299, -8892, 15526, + 25956, 13000, -11668, -25824, -16712, 7456, 24907, 19917, + -3018, -23234, -22516, -1511, 20855, 24432, 5996, -17842, + -25605, -10298, 14287, 26000, 14287, -10298, -25605, -17842, + 5996, 24432, 20855, -1511, -22516, -23234, -3018, 19917, + 24907, 7456, -16712, -25824, -11668, 13000, 25956, 15526, + -8892, -25299, -18911, 4514, 23873, 21722, 0, -21722, + -23873, -4514, 18911, 25299, 8892, -15526, -25956, -12999, + 11668, 25824, 16712, -7456, -24907, -19917, 3018, 23234, + 22516, 1511, -20855, -24432, -5996, 17842, 25604, 10298, + -14287, -26000, -14287, 10298, 25605, 17842, -5996, -24432, + -20855, 1511, 22516, 23234, 3018, -19917, -24907, -7456, + 16712, 25824, 11668, -13000, -25956, -15526, 8892, 25299, + 18911, -4514, -23873, -21722, 0, 21722, 23873, 4514, + -18911, -25299, -8892, 15526, 25956, 12999, -11668, -25824, + -16712, 7456, 24907, 19917, -3018, -23234, -22516, -1511, + 20855, 24431, 5995, -17842, -25604, -10298, 14287, 26000, + 14287, -10298, -25605, -17842, 5996, 24432, 20855, -1511, + -22516, -23234, -3018, 19917, 24907, 7456, -16712, -25824, + -11668, 13000, 25956, 15526, -8892, -25299, -18911, 4514, + 23873, 21722, 0, -21722, -23873, -4514, 18911, 25299, + 8892, -15526, -25956, -12999, 11668, 25824, 16712, -7456, + -24907, -19917, 3018, 23234, 22516, 1511, -20855, -24431, + -5995, 17842, 25604, 10298, -14287, -26000, -14287, 10298, + 25605, 17842, -5996, -24432, -20855, 1511, 22516, 23234, + 3018, -19917, -24907, -7456, 16712, 25824, 11668, -13000, + -25956, -15526, 8892, 25299, 18911, -4514, -23873, -21722}, + { +// Carrier 28 Phase 1 + 9949, 25536, 18115, -5627, -24300, -21078, 1134, 22325, + 23401, 3393, -19671, -25013, -7818, 16421, 25865, 12005, + -12671, -25931, -15827, 8536, 25209, 19169, -4141, -23721, + -21928, -378, 21512, 24020, 4886, -18650, -25383, -9246, + 15221, 25975, 13326, -11329, -25777, -17000, 7093, 24796, + 20158, -2642, -23062, -22703, -1889, 20627, 24558, 6363, + -17565, -25667, -10644, 13969, 25997, 14601, -9949, -25536, + -18115, 5627, 24300, 21078, -1134, -22325, -23401, -3393, + 19671, 25013, 7818, -16421, -25865, -12005, 12671, 25931, + 15827, -8536, -25209, -19169, 4142, 23721, 21928, 378, + -21512, -24020, -4886, 18650, 25383, 9246, -15221, -25975, + -13326, 11329, 25777, 17000, -7093, -24796, -20158, 2642, + 23062, 22703, 1889, -20627, -24558, -6363, 17565, 25667, + 10644, -13969, -25997, -14601, 9949, 25536, 18115, -5627, + -24300, -21078, 1134, 22325, 23401, 3393, -19672, -25013, + -7818, 16421, 25865, 12005, -12671, -25931, -15827, 8536, + 25209, 19169, -4142, -23721, -21928, -378, 21512, 24020, + 4886, -18650, -25383, -9246, 15221, 25975, 13326, -11329, + -25777, -17000, 7093, 24796, 20158, -2642, -23062, -22703, + -1889, 20627, 24558, 6363, -17565, -25667, -10644, 13969, + 25997, 14601, -9949, -25536, -18115, 5627, 24300, 21078, + -1134, -22325, -23401, -3393, 19672, 25013, 7818, -16421, + -25865, -12005, 12671, 25931, 15827, -8536, -25209, -19169, + 4142, 23721, 21928, 378, -21512, -24020, -4886, 18650, + 25383, 9246, -15221, -25975, -13326, 11329, 25777, 17000, + -7093, -24796, -20158, 2642, 23062, 22703, 1889, -20627, + -24558, -6363, 17565, 25667, 10644, -13969, -25997, -14601}, + { +// Carrier 28 Phase 2 + 18384, 25462, 9599, -14912, -25989, -13649, 10988, 25725, + 17284, -6729, -24680, -20394, 2266, 22885, 22885, 2266, + -20394, -24680, -6729, 17284, 25725, 10988, -13649, -25989, + -14912, 9599, 25462, 18384, -5257, -24163, -21297, 756, + 22129, 23564, 3768, -19422, -25114, -8178, 16126, 25901, + 12339, -12339, -25901, -16126, 8178, 25114, 19422, -3768, + -23564, -22129, -756, 21297, 24163, 5257, -18384, -25462, + -9599, 14912, 25989, 13649, -10988, -25725, -17284, 6729, + 24680, 20394, -2266, -22885, -22885, -2266, 20394, 24680, + 6729, -17284, -25725, -10988, 13649, 25989, 14912, -9599, + -25462, -18384, 5257, 24163, 21297, -756, -22129, -23563, + -3768, 19422, 25114, 8178, -16126, -25901, -12339, 12339, + 25901, 16126, -8178, -25114, -19422, 3768, 23564, 22129, + 756, -21297, -24163, -5257, 18384, 25462, 9599, -14913, + -25989, -13649, 10988, 25725, 17284, -6729, -24680, -20394, + 2266, 22885, 22885, 2266, -20394, -24680, -6729, 17284, + 25725, 10988, -13649, -25989, -14912, 9599, 25462, 18384, + -5257, -24163, -21297, 756, 22129, 23563, 3768, -19422, + -25114, -8178, 16126, 25901, 12339, -12339, -25901, -16126, + 8178, 25114, 19422, -3768, -23564, -22129, -756, 21297, + 24163, 5257, -18384, -25462, -9599, 14913, 25989, 13649, + -10988, -25725, -17284, 6729, 24680, 20394, -2266, -22885, + -22885, -2266, 20394, 24680, 6729, -17284, -25725, -10988, + 13649, 25989, 14912, -9599, -25462, -18384, 5257, 24163, + 21297, -756, -22129, -23563, -3768, 19422, 25114, 8178, + -16126, -25901, -12339, 12339, 25901, 16126, -8178, -25114, + -19422, 3768, 23564, 22129, 756, -21297, -24163, -5257}, + { +// Carrier 28 Phase 3 + 24020, 21512, -378, -21928, -23721, -4141, 19169, 25209, + 8536, -15827, -25931, -12671, 12005, 25865, 16421, -7818, + -25013, -19671, 3393, 23401, 22325, 1134, -21078, -24300, + -5627, 18115, 25536, 9949, -14601, -25997, -13969, 10644, + 25667, 17565, -6363, -24558, -20627, 1889, 22703, 23062, + 2642, -20158, -24796, -7093, 17000, 25777, 11329, -13326, + -25975, -15221, 9246, 25383, 18650, -4886, -24020, -21512, + 378, 21928, 23721, 4141, -19169, -25209, -8536, 15827, + 25931, 12671, -12005, -25865, -16421, 7818, 25013, 19671, + -3393, -23401, -22325, -1134, 21078, 24300, 5627, -18115, + -25536, -9949, 14601, 25997, 13969, -10644, -25667, -17565, + 6363, 24558, 20627, -1889, -22703, -23062, -2642, 20158, + 24796, 7093, -17000, -25777, -11329, 13326, 25975, 15221, + -9246, -25383, -18650, 4886, 24020, 21512, -378, -21928, + -23721, -4141, 19169, 25209, 8536, -15827, -25931, -12671, + 12005, 25865, 16421, -7818, -25013, -19671, 3393, 23401, + 22325, 1134, -21078, -24300, -5627, 18115, 25536, 9949, + -14601, -25997, -13969, 10644, 25667, 17565, -6363, -24558, + -20627, 1889, 22703, 23062, 2642, -20158, -24796, -7093, + 17000, 25777, 11329, -13326, -25975, -15221, 9246, 25383, + 18650, -4886, -24020, -21512, 378, 21928, 23721, 4141, + -19169, -25209, -8536, 15827, 25931, 12671, -12005, -25865, + -16421, 7818, 25013, 19671, -3393, -23401, -22325, -1134, + 21078, 24300, 5627, -18115, -25536, -9949, 14601, 25997, + 13969, -10644, -25667, -17565, 6363, 24558, 20627, -1889, + -22703, -23062, -2642, 20158, 24796, 7093, -17000, -25777, + -11329, 13326, 25975, 15221, -9246, -25383, -18650, 4886}, + { +// Carrier 28 Phase 4 + 26000, 14287, -10298, -25605, -17842, 5996, 24432, 20855, + -1511, -22516, -23234, -3018, 19917, 24907, 7456, -16712, + -25824, -11668, 13000, 25956, 15526, -8892, -25299, -18911, + 4514, 23873, 21722, 0, -21722, -23873, -4514, 18911, + 25299, 8892, -15526, -25956, -12999, 11668, 25824, 16712, + -7456, -24907, -19917, 3018, 23234, 22516, 1511, -20855, + -24432, -5996, 17842, 25604, 10298, -14287, -26000, -14287, + 10298, 25605, 17842, -5996, -24432, -20855, 1511, 22516, + 23234, 3018, -19917, -24907, -7456, 16712, 25824, 11668, + -13000, -25956, -15526, 8892, 25299, 18911, -4514, -23873, + -21722, 0, 21722, 23873, 4514, -18911, -25299, -8892, + 15526, 25956, 12999, -11668, -25824, -16712, 7456, 24907, + 19917, -3018, -23234, -22516, -1511, 20855, 24431, 5995, + -17842, -25604, -10298, 14287, 26000, 14287, -10298, -25605, + -17842, 5996, 24432, 20855, -1511, -22516, -23234, -3018, + 19917, 24907, 7456, -16712, -25824, -11668, 13000, 25956, + 15526, -8892, -25299, -18911, 4514, 23873, 21722, 0, + -21722, -23873, -4514, 18911, 25299, 8892, -15526, -25956, + -12999, 11668, 25824, 16712, -7456, -24907, -19917, 3018, + 23234, 22516, 1511, -20855, -24431, -5995, 17842, 25604, + 10298, -14287, -26000, -14287, 10298, 25605, 17842, -5996, + -24432, -20855, 1511, 22516, 23234, 3018, -19917, -24907, + -7456, 16712, 25824, 11668, -13000, -25956, -15526, 8892, + 25299, 18911, -4514, -23873, -21722, 0, 21722, 23873, + 4514, -18911, -25299, -8892, 15526, 25956, 12999, -11668, + -25824, -16712, 7456, 24907, 19917, -3018, -23234, -22516, + -1511, 20855, 24431, 5995, -17842, -25604, -10298, 14287}, + { +// Carrier 28 Phase 5 + 24020, 4886, -18650, -25383, -9246, 15221, 25975, 13326, + -11329, -25777, -17000, 7093, 24796, 20158, -2642, -23062, + -22703, -1889, 20627, 24558, 6363, -17565, -25667, -10644, + 13969, 25997, 14601, -9949, -25536, -18115, 5627, 24300, + 21078, -1134, -22325, -23401, -3393, 19671, 25013, 7818, + -16421, -25865, -12005, 12671, 25931, 15827, -8536, -25209, + -19169, 4141, 23721, 21928, 378, -21512, -24020, -4886, + 18650, 25383, 9246, -15221, -25975, -13326, 11329, 25777, + 17000, -7093, -24796, -20158, 2642, 23062, 22703, 1889, + -20627, -24558, -6363, 17565, 25667, 10644, -13969, -25997, + -14601, 9949, 25536, 18115, -5627, -24300, -21078, 1134, + 22325, 23401, 3393, -19671, -25013, -7818, 16421, 25865, + 12005, -12671, -25931, -15827, 8536, 25209, 19169, -4141, + -23721, -21928, -378, 21512, 24020, 4886, -18650, -25383, + -9246, 15221, 25975, 13326, -11329, -25777, -17000, 7093, + 24796, 20158, -2642, -23062, -22703, -1889, 20627, 24558, + 6363, -17565, -25667, -10644, 13969, 25997, 14601, -9949, + -25536, -18115, 5627, 24300, 21078, -1134, -22325, -23401, + -3393, 19672, 25013, 7818, -16421, -25865, -12005, 12671, + 25931, 15827, -8536, -25209, -19169, 4142, 23721, 21928, + 378, -21512, -24020, -4886, 18650, 25383, 9246, -15221, + -25975, -13326, 11329, 25777, 17000, -7093, -24796, -20158, + 2642, 23062, 22703, 1889, -20627, -24558, -6363, 17565, + 25667, 10644, -13969, -25997, -14601, 9949, 25536, 18115, + -5627, -24300, -21078, 1134, 22325, 23401, 3393, -19672, + -25013, -7818, 16421, 25865, 12005, -12671, -25931, -15827, + 8536, 25209, 19169, -4142, -23721, -21928, -378, 21512}, + { +// Carrier 28 Phase 6 + 18384, -5257, -24163, -21297, 756, 22129, 23564, 3768, + -19422, -25114, -8178, 16126, 25901, 12339, -12339, -25901, + -16126, 8178, 25114, 19422, -3768, -23564, -22129, -756, + 21297, 24163, 5257, -18384, -25462, -9599, 14912, 25989, + 13649, -10988, -25725, -17284, 6729, 24680, 20394, -2266, + -22885, -22885, -2266, 20394, 24680, 6729, -17284, -25725, + -10988, 13649, 25989, 14912, -9599, -25462, -18384, 5257, + 24163, 21297, -756, -22129, -23563, -3768, 19422, 25114, + 8178, -16126, -25901, -12339, 12339, 25901, 16126, -8178, + -25114, -19422, 3768, 23564, 22129, 756, -21297, -24163, + -5257, 18384, 25462, 9599, -14913, -25989, -13649, 10988, + 25725, 17284, -6729, -24680, -20394, 2266, 22885, 22885, + 2266, -20394, -24680, -6729, 17284, 25725, 10988, -13649, + -25989, -14912, 9599, 25462, 18384, -5257, -24163, -21297, + 756, 22129, 23563, 3768, -19422, -25114, -8178, 16126, + 25901, 12339, -12339, -25901, -16126, 8178, 25114, 19422, + -3768, -23564, -22129, -756, 21297, 24163, 5257, -18384, + -25462, -9599, 14913, 25989, 13649, -10988, -25725, -17284, + 6729, 24680, 20394, -2266, -22885, -22885, -2266, 20394, + 24680, 6729, -17284, -25725, -10988, 13649, 25989, 14912, + -9599, -25462, -18384, 5257, 24163, 21297, -756, -22129, + -23563, -3768, 19422, 25114, 8178, -16126, -25901, -12339, + 12339, 25901, 16126, -8178, -25114, -19422, 3768, 23564, + 22129, 756, -21297, -24163, -5257, 18384, 25462, 9599, + -14913, -25989, -13649, 10988, 25725, 17284, -6729, -24680, + -20394, 2266, 22885, 22885, 2266, -20394, -24680, -6729, + 17284, 25725, 10988, -13649, -25989, -14912, 9599, 25462}, + { +// Carrier 28 Phase 7 + 9949, -14601, -25997, -13969, 10644, 25667, 17565, -6363, + -24558, -20627, 1889, 22703, 23062, 2642, -20158, -24796, + -7093, 17000, 25777, 11329, -13326, -25975, -15221, 9246, + 25383, 18650, -4886, -24020, -21512, 378, 21928, 23721, + 4141, -19169, -25209, -8536, 15827, 25931, 12671, -12005, + -25865, -16421, 7818, 25013, 19671, -3393, -23401, -22325, + -1134, 21078, 24300, 5627, -18115, -25536, -9949, 14601, + 25997, 13969, -10644, -25667, -17565, 6363, 24558, 20627, + -1889, -22703, -23062, -2642, 20158, 24796, 7093, -17000, + -25777, -11329, 13326, 25975, 15221, -9246, -25383, -18650, + 4886, 24020, 21512, -378, -21928, -23721, -4141, 19169, + 25209, 8536, -15827, -25931, -12671, 12005, 25865, 16421, + -7818, -25013, -19671, 3393, 23401, 22325, 1134, -21078, + -24300, -5627, 18115, 25536, 9949, -14601, -25997, -13969, + 10644, 25667, 17565, -6363, -24558, -20627, 1889, 22703, + 23062, 2642, -20158, -24796, -7093, 17000, 25777, 11329, + -13326, -25975, -15221, 9246, 25383, 18650, -4886, -24020, + -21512, 378, 21928, 23721, 4141, -19169, -25209, -8536, + 15827, 25931, 12671, -12005, -25865, -16421, 7818, 25013, + 19671, -3393, -23401, -22325, -1134, 21078, 24300, 5627, + -18115, -25536, -9949, 14601, 25997, 13969, -10644, -25667, + -17565, 6363, 24558, 20627, -1889, -22703, -23062, -2642, + 20158, 24796, 7093, -17000, -25777, -11329, 13326, 25975, + 15221, -9246, -25383, -18650, 4886, 24020, 21512, -378, + -21928, -23721, -4141, 19169, 25209, 8536, -15827, -25931, + -12671, 12005, 25865, 16420, -7818, -25013, -19671, 3393, + 23401, 22325, 1134, -21078, -24300, -5627, 18115, 25536}, + },{{ + +// Carrier 29 Phase 0 + 0, 22129, 23234, 2266, -20855, -24163, -4514, 19422, + 24907, 6729, -17842, -25462, -8892, 16126, 25824, 10988, + -14287, -25989, -12999, 12339, 25956, 14912, -10298, -25725, + -16712, 8178, 25299, 18384, -5996, -24680, -19917, 3768, + 23873, 21297, -1511, -22885, -22516, -756, 21722, 23563, + 3018, -20394, -24431, -5257, 18911, 25114, 7456, -17284, + -25604, -9599, 15526, 25901, 11668, -13649, -26000, -13649, + 11668, 25901, 15526, -9599, -25605, -17284, 7457, 25114, + 18911, -5257, -24432, -20394, 3018, 23564, 21722, -756, + -22516, -22885, -1511, 21298, 23873, 3768, -19917, -24680, + -5995, 18384, 25299, 8177, -16712, -25725, -10297, 14913, + 25956, 12339, -13000, -25989, -14287, 10988, 25824, 16125, + -8892, -25462, -17842, 6729, 24907, 19422, -4515, -24163, + -20855, 2266, 23234, 22128, 0, -22129, -23234, -2265, + 20855, 24162, 4514, -19422, -24907, -6729, 17842, 25462, + 8892, -16126, -25824, -10987, 14287, 25988, 12999, -12339, + -25956, -14912, 10298, 25725, 16712, -8178, -25299, -18384, + 5996, 24680, 19916, -3768, -23873, -21297, 1512, 22885, + 22516, 755, -21722, -23563, -3018, 20395, 24431, 5257, + -18911, -25113, -7456, 17284, 25604, 9599, -15526, -25901, + -11668, 13649, 26000, 13649, -11669, -25901, -15525, 9599, + 25605, 17284, -7457, -25114, -18911, 5258, 24432, 20394, + -3018, -23564, -21722, 756, 22516, 22885, 1511, -21298, + -23873, -3767, 19917, 24680, 5995, -18385, -25299, -8177, + 16712, 25725, 10297, -14913, -25955, -12339, 13000, 25989, + 14286, -10988, -25824, -16125, 8892, 25462, 17841, -6729, + -24907, -19422, 4515, 24163, 20854, -2266, -23234, -22128}, + { +// Carrier 29 Phase 1 + 9949, 25667, 17000, -7818, -25209, -18650, 5627, 24558, + 20158, -3393, -23721, -21512, 1134, 22703, 22703, 1134, + -21512, -23721, -3393, 20158, 24558, 5627, -18650, -25209, + -7818, 17000, 25667, 9949, -15221, -25931, -12005, 13326, + 25997, 13969, -11329, -25865, -15827, 9247, 25536, 17565, + -7093, -25013, -19169, 4886, 24300, 20627, -2642, -23401, + -21928, 378, 22325, 23062, 1888, -21078, -24020, -4141, + 19672, 24796, 6363, -18115, -25383, -8536, 16421, 25777, + 10644, -14601, -25975, -12671, 12671, 25975, 14601, -10644, + -25777, -16420, 8536, 25383, 18115, -6363, -24796, -19671, + 4142, 24020, 21078, -1889, -23062, -22325, -377, 21928, + 23401, 2642, -20627, -24300, -4886, 19169, 25013, 7093, + -17565, -25536, -9246, 15827, 25865, 11329, -13969, -25997, + -13325, 12005, 25931, 15220, -9949, -25667, -17000, 7818, + 25209, 18650, -5627, -24558, -20157, 3393, 23721, 21512, + -1134, -22703, -22703, -1133, 21512, 23721, 3393, -20158, + -24558, -5627, 18650, 25209, 7818, -17000, -25667, -9949, + 15221, 25931, 12005, -13326, -25997, -13969, 11329, 25865, + 15827, -9247, -25536, -17565, 7094, 25013, 19168, -4887, + -24300, -20626, 2642, 23401, 21927, -378, -22325, -23062, + -1888, 21079, 24020, 4141, -19672, -24796, -6362, 18115, + 25383, 8535, -16421, -25777, -10643, 14601, 25975, 12670, + -12671, -25975, -14601, 10644, 25777, 16420, -8536, -25383, + -18115, 6363, 24796, 19671, -4142, -24021, -21078, 1889, + 23062, 22324, 377, -21928, -23401, -2642, 20627, 24299, + 4886, -19169, -25013, -7093, 17565, 25536, 9246, -15828, + -25865, -11329, 13970, 25997, 13325, -12005, -25931, -15220}, + { +// Carrier 29 Phase 2 + 18384, 25299, 8178, -16712, -25725, -10298, 14913, 25956, + 12339, -13000, -25989, -14287, 10988, 25824, 16126, -8892, + -25462, -17842, 6729, 24907, 19422, -4514, -24163, -20855, + 2266, 23234, 22129, 0, -22129, -23234, -2265, 20855, + 24163, 4514, -19422, -24907, -6729, 17842, 25462, 8892, + -16126, -25824, -10987, 14287, 25988, 12999, -12339, -25956, + -14912, 10298, 25725, 16712, -8178, -25299, -18384, 5996, + 24680, 19917, -3768, -23873, -21297, 1511, 22885, 22516, + 756, -21722, -23563, -3018, 20394, 24431, 5257, -18911, + -25114, -7456, 17284, 25604, 9599, -15526, -25901, -11668, + 13649, 26000, 13649, -11668, -25901, -15525, 9599, 25605, + 17284, -7457, -25114, -18911, 5257, 24432, 20394, -3018, + -23564, -21722, 756, 22516, 22885, 1511, -21298, -23873, + -3767, 19917, 24680, 5995, -18384, -25299, -8177, 16712, + 25725, 10297, -14913, -25955, -12339, 13000, 25989, 14287, + -10988, -25824, -16125, 8892, 25462, 17842, -6729, -24907, + -19422, 4515, 24163, 20855, -2266, -23234, -22128, 0, + 22129, 23234, 2265, -20855, -24162, -4514, 19422, 24907, + 6728, -17842, -25462, -8892, 16126, 25824, 10987, -14287, + -25988, -12999, 12339, 25956, 14912, -10298, -25725, -16712, + 8178, 25299, 18384, -5996, -24680, -19916, 3768, 23873, + 21297, -1512, -22885, -22516, -755, 21722, 23563, 3018, + -20395, -24431, -5257, 18912, 25113, 7456, -17285, -25604, + -9598, 15526, 25901, 11668, -13649, -26000, -13649, 11669, + 25901, 15525, -9599, -25605, -17284, 7457, 25114, 18911, + -5258, -24432, -20394, 3018, 23564, 21722, -756, -22516, + -22885, -1511, 21298, 23873, 3767, -19917, -24680, -5995}, + { +// Carrier 29 Phase 3 + 24020, 21078, -1889, -23062, -22325, -378, 21928, 23401, + 2642, -20627, -24300, -4886, 19169, 25013, 7093, -17565, + -25536, -9246, 15827, 25865, 11329, -13969, -25997, -13326, + 12005, 25931, 15221, -9949, -25667, -17000, 7818, 25209, + 18650, -5627, -24558, -20158, 3393, 23721, 21512, -1134, + -22703, -22703, -1133, 21512, 23721, 3393, -20158, -24558, + -5627, 18650, 25209, 7818, -17000, -25667, -9949, 15221, + 25931, 12005, -13326, -25997, -13969, 11329, 25865, 15827, + -9247, -25536, -17565, 7094, 25013, 19169, -4886, -24300, + -20627, 2642, 23401, 21928, -378, -22325, -23062, -1888, + 21078, 24020, 4141, -19672, -24796, -6363, 18115, 25383, + 8536, -16421, -25777, -10643, 14601, 25975, 12670, -12671, + -25975, -14601, 10644, 25777, 16420, -8536, -25383, -18115, + 6363, 24796, 19671, -4142, -24020, -21078, 1889, 23062, + 22325, 377, -21928, -23401, -2642, 20627, 24299, 4886, + -19169, -25013, -7093, 17565, 25536, 9246, -15828, -25865, + -11329, 13970, 25997, 13325, -12005, -25931, -15220, 9950, + 25668, 17000, -7818, -25209, -18649, 5627, 24558, 20157, + -3394, -23721, -21512, 1134, 22703, 22703, 1133, -21512, + -23721, -3393, 20158, 24558, 5627, -18650, -25209, -7817, + 17000, 25667, 9949, -15221, -25931, -12005, 13326, 25997, + 13969, -11329, -25865, -15827, 9247, 25536, 17565, -7094, + -25013, -19168, 4887, 24300, 20626, -2642, -23401, -21927, + 378, 22325, 23062, 1888, -21079, -24020, -4141, 19672, + 24796, 6362, -18115, -25383, -8535, 16421, 25777, 10643, + -14602, -25975, -12670, 12671, 25975, 14601, -10644, -25777, + -16420, 8536, 25383, 18115, -6363, -24796, -19671, 4142}, + { +// Carrier 29 Phase 4 + 26000, 13649, -11668, -25901, -15526, 9599, 25605, 17284, + -7456, -25114, -18911, 5257, 24432, 20394, -3018, -23564, + -21722, 756, 22516, 22885, 1511, -21297, -23873, -3768, + 19917, 24680, 5995, -18384, -25299, -8178, 16712, 25725, + 10298, -14913, -25956, -12339, 13000, 25989, 14287, -10988, + -25824, -16126, 8892, 25462, 17842, -6729, -24907, -19422, + 4514, 24163, 20855, -2266, -23234, -22128, 0, 22129, + 23234, 2265, -20855, -24162, -4514, 19422, 24907, 6729, + -17842, -25462, -8892, 16126, 25824, 10987, -14287, -25988, + -12999, 12339, 25956, 14912, -10298, -25725, -16712, 8178, + 25299, 18384, -5996, -24680, -19917, 3768, 23873, 21297, + -1511, -22885, -22516, -756, 21722, 23563, 3018, -20394, + -24431, -5257, 18911, 25114, 7456, -17284, -25604, -9599, + 15526, 25901, 11668, -13649, -26000, -13649, 11669, 25901, + 15525, -9599, -25605, -17284, 7457, 25114, 18911, -5257, + -24432, -20394, 3018, 23564, 21722, -756, -22516, -22885, + -1511, 21298, 23873, 3767, -19917, -24680, -5995, 18385, + 25299, 8177, -16712, -25725, -10297, 14913, 25955, 12339, + -13000, -25989, -14286, 10988, 25824, 16125, -8892, -25462, + -17842, 6729, 24907, 19422, -4515, -24163, -20854, 2266, + 23234, 22128, 0, -22129, -23234, -2265, 20855, 24162, + 4514, -19422, -24907, -6728, 17842, 25462, 8892, -16126, + -25824, -10987, 14287, 25988, 12999, -12339, -25956, -14912, + 10298, 25725, 16712, -8178, -25299, -18384, 5996, 24680, + 19916, -3768, -23873, -21297, 1512, 22885, 22516, 755, + -21722, -23563, -3017, 20395, 24431, 5257, -18912, -25113, + -7456, 17285, 25604, 9598, -15526, -25901, -11668, 13649}, + { +// Carrier 29 Phase 5 + 24020, 4141, -19671, -24796, -6363, 18115, 25383, 8536, + -16421, -25777, -10644, 14601, 25975, 12671, -12671, -25975, + -14601, 10644, 25777, 16421, -8536, -25383, -18115, 6363, + 24796, 19671, -4142, -24020, -21078, 1889, 23062, 22325, + 378, -21928, -23401, -2642, 20627, 24300, 4886, -19169, + -25013, -7093, 17565, 25536, 9246, -15827, -25865, -11329, + 13969, 25997, 13326, -12005, -25931, -15221, 9949, 25667, + 17000, -7818, -25209, -18650, 5627, 24558, 20158, -3393, + -23721, -21512, 1134, 22703, 22703, 1133, -21512, -23721, + -3393, 20158, 24558, 5627, -18650, -25209, -7818, 17000, + 25667, 9949, -15221, -25931, -12005, 13326, 25997, 13969, + -11329, -25865, -15827, 9247, 25536, 17565, -7094, -25013, + -19169, 4886, 24300, 20627, -2642, -23401, -21928, 378, + 22325, 23062, 1888, -21078, -24020, -4141, 19672, 24796, + 6363, -18115, -25383, -8536, 16421, 25777, 10643, -14601, + -25975, -12670, 12671, 25975, 14601, -10644, -25777, -16420, + 8536, 25383, 18115, -6363, -24796, -19671, 4142, 24020, + 21078, -1889, -23062, -22325, -377, 21928, 23401, 2642, + -20627, -24299, -4886, 19169, 25013, 7093, -17565, -25536, + -9246, 15828, 25865, 11329, -13970, -25997, -13325, 12005, + 25931, 15220, -9950, -25668, -17000, 7818, 25209, 18649, + -5627, -24558, -20157, 3394, 23721, 21512, -1134, -22703, + -22703, -1133, 21512, 23721, 3393, -20158, -24558, -5627, + 18650, 25209, 7817, -17000, -25667, -9949, 15221, 25931, + 12005, -13326, -25997, -13969, 11330, 25865, 15827, -9247, + -25536, -17565, 7094, 25013, 19168, -4887, -24300, -20626, + 2642, 23401, 21927, -378, -22325, -23062, -1888, 21079}, + { +// Carrier 29 Phase 6 + 18384, -5996, -24680, -19917, 3768, 23873, 21297, -1511, + -22885, -22516, -756, 21722, 23563, 3018, -20394, -24431, + -5257, 18911, 25114, 7456, -17284, -25604, -9599, 15526, + 25901, 11668, -13649, -26000, -13649, 11668, 25901, 15526, + -9599, -25605, -17284, 7456, 25114, 18911, -5257, -24432, + -20394, 3018, 23564, 21722, -756, -22516, -22885, -1511, + 21298, 23873, 3768, -19917, -24680, -5995, 18384, 25299, + 8178, -16712, -25725, -10297, 14913, 25956, 12339, -13000, + -25989, -14287, 10988, 25824, 16125, -8892, -25462, -17842, + 6729, 24907, 19422, -4515, -24163, -20855, 2266, 23234, + 22128, 0, -22129, -23234, -2265, 20855, 24162, 4514, + -19422, -24907, -6729, 17842, 25462, 8892, -16126, -25824, + -10987, 14287, 25988, 12999, -12339, -25956, -14912, 10298, + 25725, 16712, -8178, -25299, -18384, 5996, 24680, 19916, + -3768, -23873, -21297, 1512, 22885, 22516, 755, -21722, + -23563, -3018, 20394, 24431, 5257, -18911, -25113, -7456, + 17284, 25604, 9599, -15526, -25901, -11668, 13649, 26000, + 13649, -11669, -25901, -15525, 9599, 25605, 17284, -7457, + -25114, -18911, 5257, 24432, 20394, -3018, -23564, -21722, + 756, 22516, 22885, 1511, -21298, -23873, -3767, 19917, + 24680, 5995, -18385, -25299, -8177, 16712, 25725, 10297, + -14913, -25955, -12339, 13000, 25989, 14286, -10988, -25824, + -16125, 8892, 25462, 17841, -6729, -24907, -19422, 4515, + 24163, 20854, -2266, -23234, -22128, 0, 22129, 23234, + 2265, -20855, -24162, -4514, 19422, 24907, 6728, -17842, + -25462, -8892, 16126, 25824, 10987, -14287, -25988, -12999, + 12340, 25956, 14912, -10298, -25725, -16712, 8178, 25299}, + { +// Carrier 29 Phase 7 + 9949, -15221, -25931, -12005, 13326, 25997, 13969, -11329, + -25865, -15827, 9246, 25536, 17565, -7093, -25013, -19169, + 4886, 24300, 20627, -2642, -23401, -21928, 378, 22325, + 23062, 1889, -21078, -24020, -4141, 19672, 24796, 6363, + -18115, -25383, -8536, 16421, 25777, 10644, -14601, -25975, + -12671, 12671, 25975, 14601, -10644, -25777, -16420, 8536, + 25383, 18115, -6363, -24796, -19671, 4142, 24020, 21078, + -1889, -23062, -22325, -378, 21928, 23401, 2642, -20627, + -24300, -4886, 19169, 25013, 7093, -17565, -25536, -9246, + 15827, 25865, 11329, -13969, -25997, -13325, 12005, 25931, + 15221, -9949, -25667, -17000, 7818, 25209, 18650, -5627, + -24558, -20157, 3393, 23721, 21512, -1134, -22703, -22703, + -1133, 21512, 23721, 3393, -20158, -24558, -5627, 18650, + 25209, 7818, -17000, -25667, -9949, 15221, 25931, 12005, + -13326, -25997, -13969, 11329, 25865, 15827, -9247, -25536, + -17565, 7094, 25013, 19169, -4887, -24300, -20627, 2642, + 23401, 21928, -378, -22325, -23062, -1888, 21078, 24020, + 4141, -19672, -24796, -6363, 18115, 25383, 8535, -16421, + -25777, -10643, 14601, 25975, 12670, -12671, -25975, -14601, + 10644, 25777, 16420, -8536, -25383, -18115, 6363, 24796, + 19671, -4142, -24021, -21078, 1889, 23062, 22325, 377, + -21928, -23401, -2642, 20627, 24299, 4886, -19169, -25013, + -7093, 17565, 25536, 9246, -15828, -25865, -11329, 13970, + 25997, 13325, -12005, -25931, -15220, 9950, 25668, 17000, + -7818, -25209, -18649, 5627, 24558, 20157, -3394, -23721, + -21512, 1134, 22703, 22703, 1133, -21512, -23721, -3393, + 20158, 24558, 5626, -18650, -25209, -7817, 17000, 25667}, + },{{ + +// Carrier 30 Phase 0 + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22517, + 0, 22516, 22517, 0, -22516, -22517, 0, 22516, + 22517, 0, -22516, -22517, 0, 22516, 22517, 0, + -22516, -22517, 0, 22516, 22517, 0, -22516, -22517, + 0, 22516, 22517, 0, -22516, -22517, 0, 22516, + 22517, 0, -22516, -22517, 0, 22516, 22517, 0, + -22516, -22517, 0, 22516, 22517, 0, -22516, -22517}, + { +// Carrier 30 Phase 1 + 9949, 25777, 15827, -9949, -25777, -15827, 9949, 25777, + 15827, -9949, -25777, -15827, 9949, 25777, 15827, -9949, + -25777, -15827, 9949, 25777, 15827, -9949, -25777, -15827, + 9949, 25777, 15827, -9949, -25777, -15827, 9949, 25777, + 15827, -9949, -25777, -15827, 9949, 25777, 15827, -9949, + -25777, -15827, 9949, 25777, 15827, -9949, -25777, -15827, + 9949, 25777, 15827, -9949, -25777, -15827, 9949, 25777, + 15827, -9949, -25777, -15827, 9949, 25777, 15827, -9949, + -25777, -15827, 9949, 25777, 15827, -9949, -25777, -15827, + 9949, 25777, 15827, -9949, -25777, -15827, 9949, 25777, + 15827, -9949, -25777, -15827, 9949, 25777, 15827, -9949, + -25777, -15827, 9949, 25777, 15827, -9949, -25777, -15827, + 9949, 25777, 15827, -9949, -25777, -15827, 9949, 25777, + 15827, -9949, -25777, -15827, 9949, 25777, 15827, -9949, + -25777, -15827, 9949, 25777, 15827, -9949, -25777, -15828, + 9949, 25777, 15828, -9949, -25777, -15828, 9949, 25777, + 15828, -9949, -25777, -15828, 9949, 25777, 15828, -9949, + -25777, -15828, 9949, 25777, 15828, -9949, -25777, -15828, + 9949, 25777, 15828, -9949, -25777, -15828, 9949, 25777, + 15828, -9949, -25777, -15828, 9949, 25777, 15828, -9949, + -25777, -15828, 9949, 25777, 15828, -9949, -25777, -15828, + 9949, 25777, 15828, -9949, -25777, -15828, 9949, 25777, + 15828, -9949, -25777, -15828, 9949, 25777, 15828, -9949, + -25777, -15828, 9949, 25777, 15828, -9949, -25777, -15828, + 9949, 25777, 15828, -9949, -25777, -15828, 9949, 25777, + 15828, -9949, -25777, -15828, 9949, 25777, 15828, -9949, + -25777, -15828, 9949, 25777, 15828, -9949, -25777, -15828}, + { +// Carrier 30 Phase 2 + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729}, + { +// Carrier 30 Phase 3 + 24020, 20627, -3393, -24020, -20627, 3393, 24020, 20627, + -3393, -24020, -20627, 3393, 24020, 20627, -3393, -24020, + -20627, 3393, 24020, 20627, -3393, -24020, -20627, 3393, + 24020, 20627, -3393, -24020, -20627, 3393, 24020, 20627, + -3393, -24020, -20627, 3393, 24020, 20627, -3393, -24020, + -20627, 3393, 24020, 20627, -3393, -24020, -20627, 3393, + 24020, 20627, -3393, -24020, -20627, 3393, 24020, 20627, + -3393, -24020, -20627, 3393, 24020, 20627, -3393, -24020, + -20627, 3393, 24020, 20627, -3393, -24020, -20627, 3393, + 24020, 20627, -3393, -24020, -20627, 3393, 24020, 20627, + -3393, -24020, -20627, 3393, 24020, 20627, -3393, -24020, + -20627, 3393, 24020, 20627, -3393, -24020, -20627, 3393, + 24020, 20627, -3393, -24020, -20627, 3393, 24020, 20627, + -3393, -24020, -20627, 3393, 24020, 20627, -3393, -24020, + -20627, 3393, 24020, 20627, -3393, -24020, -20627, 3393, + 24020, 20627, -3393, -24020, -20627, 3393, 24020, 20627, + -3393, -24020, -20627, 3393, 24020, 20627, -3393, -24020, + -20627, 3393, 24020, 20627, -3393, -24020, -20627, 3393, + 24020, 20627, -3393, -24020, -20627, 3393, 24020, 20627, + -3393, -24020, -20627, 3393, 24020, 20627, -3393, -24020, + -20627, 3393, 24020, 20627, -3393, -24020, -20627, 3393, + 24020, 20627, -3393, -24020, -20627, 3393, 24020, 20627, + -3393, -24020, -20627, 3393, 24020, 20627, -3393, -24020, + -20627, 3393, 24020, 20627, -3393, -24020, -20627, 3393, + 24020, 20627, -3393, -24020, -20627, 3393, 24020, 20627, + -3393, -24020, -20627, 3393, 24020, 20627, -3393, -24020, + -20627, 3393, 24020, 20627, -3393, -24020, -20627, 3393}, + { +// Carrier 30 Phase 4 + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999}, + { +// Carrier 30 Phase 5 + 24020, 3393, -20627, -24020, -3393, 20627, 24020, 3393, + -20627, -24020, -3393, 20627, 24020, 3393, -20627, -24020, + -3393, 20627, 24020, 3393, -20627, -24020, -3393, 20627, + 24020, 3393, -20627, -24020, -3393, 20627, 24020, 3393, + -20627, -24020, -3393, 20627, 24020, 3393, -20627, -24020, + -3393, 20627, 24020, 3393, -20627, -24020, -3393, 20627, + 24020, 3393, -20627, -24020, -3393, 20627, 24020, 3393, + -20627, -24020, -3393, 20627, 24020, 3393, -20627, -24020, + -3393, 20627, 24020, 3393, -20627, -24020, -3393, 20627, + 24020, 3393, -20627, -24020, -3393, 20627, 24020, 3393, + -20627, -24020, -3393, 20627, 24020, 3393, -20627, -24020, + -3393, 20627, 24020, 3393, -20627, -24020, -3393, 20627, + 24020, 3393, -20627, -24020, -3393, 20627, 24020, 3393, + -20627, -24020, -3393, 20627, 24020, 3393, -20627, -24020, + -3393, 20627, 24020, 3393, -20627, -24020, -3393, 20627, + 24020, 3393, -20627, -24020, -3393, 20627, 24020, 3393, + -20627, -24020, -3393, 20627, 24020, 3393, -20627, -24020, + -3393, 20627, 24020, 3393, -20627, -24020, -3393, 20626, + 24020, 3393, -20626, -24020, -3394, 20626, 24020, 3394, + -20626, -24020, -3394, 20626, 24020, 3394, -20626, -24020, + -3394, 20626, 24021, 3394, -20626, -24021, -3394, 20626, + 24021, 3394, -20626, -24021, -3394, 20626, 24021, 3394, + -20626, -24021, -3394, 20626, 24021, 3394, -20626, -24021, + -3394, 20626, 24021, 3394, -20626, -24021, -3394, 20626, + 24021, 3394, -20626, -24021, -3394, 20626, 24021, 3394, + -20626, -24021, -3394, 20626, 24021, 3394, -20626, -24021, + -3394, 20626, 24021, 3394, -20626, -24021, -3394, 20626}, + { +// Carrier 30 Phase 6 + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25113, 18384, -6729, -25113, -18384, + 6729, 25113, 18384, -6729, -25113, -18384, 6729, 25113, + 18384, -6729, -25113, -18384, 6728, 25113, 18384, -6728, + -25113, -18385, 6728, 25113, 18385, -6728, -25113, -18385, + 6728, 25113, 18385, -6728, -25113, -18385, 6728, 25113, + 18385, -6728, -25113, -18385, 6728, 25113, 18385, -6728, + -25113, -18385, 6728, 25113, 18385, -6728, -25113, -18385, + 6728, 25113, 18385, -6728, -25113, -18385, 6728, 25113, + 18385, -6728, -25113, -18385, 6728, 25113, 18385, -6728, + -25113, -18385, 6728, 25113, 18385, -6728, -25113, -18385, + 6728, 25113, 18385, -6728, -25113, -18385, 6728, 25113}, + { +// Carrier 30 Phase 7 + 9949, -15827, -25777, -9949, 15827, 25777, 9949, -15827, + -25777, -9949, 15827, 25777, 9949, -15827, -25777, -9949, + 15827, 25777, 9949, -15827, -25777, -9949, 15827, 25777, + 9949, -15827, -25777, -9949, 15827, 25777, 9949, -15827, + -25777, -9949, 15827, 25777, 9949, -15827, -25777, -9949, + 15827, 25777, 9949, -15827, -25777, -9949, 15827, 25777, + 9949, -15827, -25777, -9949, 15827, 25777, 9949, -15827, + -25777, -9949, 15827, 25777, 9949, -15827, -25777, -9949, + 15827, 25777, 9949, -15827, -25777, -9949, 15827, 25777, + 9949, -15827, -25777, -9949, 15827, 25777, 9949, -15827, + -25777, -9949, 15827, 25777, 9949, -15827, -25777, -9949, + 15827, 25777, 9949, -15827, -25777, -9949, 15827, 25777, + 9949, -15827, -25777, -9949, 15827, 25777, 9949, -15827, + -25777, -9949, 15827, 25777, 9949, -15827, -25777, -9949, + 15827, 25777, 9949, -15827, -25777, -9949, 15827, 25777, + 9949, -15827, -25777, -9950, 15827, 25777, 9950, -15827, + -25777, -9950, 15827, 25777, 9950, -15827, -25777, -9950, + 15827, 25777, 9950, -15827, -25777, -9950, 15827, 25777, + 9950, -15827, -25777, -9950, 15827, 25777, 9950, -15827, + -25777, -9950, 15827, 25777, 9950, -15827, -25777, -9950, + 15827, 25777, 9950, -15827, -25777, -9950, 15827, 25777, + 9950, -15827, -25777, -9950, 15827, 25777, 9950, -15827, + -25777, -9950, 15827, 25777, 9950, -15827, -25777, -9950, + 15827, 25777, 9950, -15827, -25777, -9950, 15827, 25777, + 9950, -15827, -25777, -9950, 15827, 25777, 9950, -15827, + -25777, -9950, 15827, 25777, 9950, -15827, -25777, -9950, + 15827, 25777, 9950, -15827, -25777, -9950, 15827, 25777}, + },{{ + +// Carrier 31 Phase 0 + 0, 22885, 21722, -2266, -23873, -20394, 4514, 24680, + 18911, -6729, -25299, -17284, 8892, 25725, 15526, -10988, + -25956, -13649, 13000, 25989, 11668, -14913, -25824, -9599, + 16712, 25462, 7456, -18384, -24907, -5257, 19917, 24163, + 3018, -21297, -23234, -756, 22516, 22128, -1511, -23564, + -20855, 3768, 24432, 19422, -5996, -25114, -17842, 8178, + 25605, 16126, -10298, -25901, -14287, 12339, 26000, 12339, + -14287, -25901, -10297, 16126, 25604, 8178, -17842, -25114, + -5995, 19422, 24431, 3768, -20855, -23563, -1511, 22129, + 22516, -756, -23234, -21297, 3018, 24163, 19917, -5257, + -24907, -18384, 7457, 25462, 16712, -9599, -25824, -14912, + 11668, 25989, 12999, -13649, -25956, -10987, 15526, 25725, + 8892, -17284, -25299, -6729, 18911, 24680, 4514, -20394, + -23873, -2265, 21722, 22885, 0, -22885, -21722, 2266, + 23873, 20394, -4515, -24680, -18911, 6729, 25299, 17284, + -8892, -25725, -15525, 10988, 25956, 13649, -13000, -25988, + -11668, 14913, 25824, 9599, -16712, -25462, -7456, 18384, + 24907, 5257, -19917, -24162, -3018, 21298, 23234, 755, + -22516, -22128, 1512, 23564, 20855, -3768, -24432, -19422, + 5996, 25114, 17842, -8178, -25605, -16125, 10298, 25901, + 14287, -12339, -26000, -12339, 14287, 25901, 10297, -16126, + -25604, -8177, 17842, 25113, 5995, -19422, -24431, -3767, + 20855, 23563, 1511, -22129, -22516, 756, 23234, 21297, + -3018, -24163, -19916, 5257, 24907, 18384, -7457, -25462, + -16712, 9599, 25824, 14912, -11669, -25989, -12999, 13649, + 25955, 10987, -15526, -25725, -8892, 17284, 25299, 6728, + -18911, -24680, -4514, 20395, 23873, 2265, -21722, -22885}, + { +// Carrier 31 Phase 1 + 9949, 25865, 14601, -12005, -25997, -12671, 13969, 25931, + 10644, -15827, -25667, -8536, 17565, 25209, 6363, -19169, + -24558, -4141, 20627, 23721, 1889, -21928, -22703, 378, + 23062, 21512, -2642, -24020, -20158, 4886, 24796, 18650, + -7093, -25383, -17000, 9246, 25777, 15221, -11329, -25975, + -13326, 13326, 25975, 11329, -15221, -25777, -9246, 17000, + 25383, 7093, -18650, -24796, -4886, 20158, 24020, 2642, + -21512, -23062, -378, 22703, 21928, -1889, -23721, -20627, + 4142, 24558, 19169, -6363, -25209, -17565, 8536, 25667, + 15827, -10644, -25931, -13969, 12671, 25997, 12005, -14601, + -25865, -9949, 16421, 25536, 7818, -18115, -25013, -5627, + 19672, 24300, 3393, -21078, -23401, -1133, 22325, 22325, + -1134, -23401, -21078, 3393, 24300, 19671, -5627, -25013, + -18115, 7818, 25536, 16420, -9949, -25865, -14601, 12005, + 25997, 12670, -13969, -25931, -10644, 15827, 25667, 8536, + -17565, -25209, -6363, 19169, 24558, 4141, -20627, -23721, + -1888, 21928, 22703, -378, -23062, -21512, 2642, 24020, + 20157, -4887, -24796, -18650, 7094, 25383, 17000, -9247, + -25777, -15220, 11329, 25975, 13325, -13326, -25975, -11329, + 15221, 25777, 9246, -17000, -25383, -7093, 18650, 24796, + 4886, -20158, -24020, -2642, 21512, 23062, 377, -22703, + -21928, 1889, 23721, 20627, -4142, -24558, -19169, 6363, + 25209, 17565, -8536, -25668, -15827, 10644, 25931, 13969, + -12671, -25997, -12005, 14601, 25865, 9949, -16421, -25536, + -7818, 18115, 25013, 5627, -19672, -24299, -3393, 21079, + 23401, 1133, -22325, -22325, 1134, 23401, 21078, -3394, + -24300, -19671, 5627, 25013, 18115, -7818, -25536, -16420}, + { +// Carrier 31 Phase 2 + 18384, 24907, 5257, -19917, -24163, -3018, 21297, 23234, + 756, -22516, -22129, 1511, 23564, 20855, -3768, -24432, + -19422, 5996, 25114, 17842, -8178, -25605, -16126, 10298, + 25901, 14287, -12339, -26000, -12339, 14287, 25901, 10298, + -16126, -25604, -8178, 17842, 25114, 5995, -19422, -24431, + -3768, 20855, 23563, 1511, -22129, -22516, 756, 23234, + 21297, -3018, -24163, -19917, 5257, 24907, 18384, -7456, + -25462, -16712, 9599, 25824, 14912, -11668, -25989, -12999, + 13649, 25956, 10987, -15526, -25725, -8892, 17284, 25299, + 6729, -18911, -24680, -4514, 20394, 23873, 2265, -21722, + -22885, 0, 22885, 21722, -2266, -23873, -20394, 4514, + 24680, 18911, -6729, -25299, -17284, 8892, 25725, 15525, + -10988, -25956, -13649, 13000, 25988, 11668, -14913, -25824, + -9599, 16712, 25462, 7456, -18384, -24907, -5257, 19917, + 24162, 3018, -21298, -23234, -756, 22516, 22128, -1511, + -23564, -20855, 3768, 24432, 19422, -5996, -25114, -17842, + 8178, 25605, 16125, -10298, -25901, -14287, 12339, 26000, + 12339, -14287, -25901, -10297, 16126, 25604, 8177, -17842, + -25114, -5995, 19422, 24431, 3767, -20855, -23563, -1511, + 22129, 22516, -756, -23234, -21297, 3018, 24163, 19916, + -5257, -24907, -18384, 7457, 25462, 16712, -9599, -25824, + -14912, 11669, 25989, 12999, -13649, -25955, -10987, 15526, + 25725, 8892, -17284, -25299, -6729, 18911, 24680, 4514, + -20395, -23873, -2265, 21722, 22885, 0, -22885, -21722, + 2266, 23873, 20394, -4515, -24680, -18911, 6729, 25299, + 17284, -8892, -25725, -15525, 10988, 25956, 13649, -13000, + -25988, -11668, 14913, 25824, 9599, -16712, -25462, -7456}, + { +// Carrier 31 Phase 3 + 24020, 20158, -4886, -24796, -18650, 7093, 25383, 17000, + -9246, -25777, -15221, 11329, 25975, 13326, -13326, -25975, + -11329, 15221, 25777, 9246, -17000, -25383, -7093, 18650, + 24796, 4886, -20158, -24020, -2642, 21512, 23062, 378, + -22703, -21928, 1889, 23721, 20627, -4142, -24558, -19169, + 6363, 25209, 17565, -8536, -25667, -15827, 10644, 25931, + 13969, -12671, -25997, -12005, 14601, 25865, 9949, -16421, + -25536, -7818, 18115, 25013, 5627, -19672, -24300, -3393, + 21078, 23401, 1134, -22325, -22325, 1134, 23401, 21078, + -3393, -24300, -19671, 5627, 25013, 18115, -7818, -25536, + -16420, 9949, 25865, 14601, -12005, -25997, -12671, 13969, + 25931, 10644, -15827, -25667, -8536, 17565, 25209, 6363, + -19169, -24558, -4141, 20627, 23721, 1888, -21928, -22703, + 378, 23062, 21512, -2642, -24020, -20157, 4886, 24796, + 18650, -7094, -25383, -17000, 9247, 25777, 15221, -11329, + -25975, -13325, 13326, 25975, 11329, -15221, -25777, -9246, + 17000, 25383, 7093, -18650, -24796, -4886, 20158, 24020, + 2642, -21512, -23062, -377, 22703, 21928, -1889, -23721, + -20627, 4142, 24558, 19169, -6363, -25209, -17565, 8536, + 25667, 15827, -10644, -25931, -13969, 12671, 25997, 12005, + -14601, -25865, -9949, 16421, 25536, 7818, -18115, -25013, + -5627, 19672, 24299, 3393, -21078, -23401, -1133, 22325, + 22325, -1134, -23401, -21078, 3393, 24300, 19671, -5627, + -25013, -18115, 7818, 25536, 16420, -9950, -25865, -14601, + 12005, 25997, 12670, -13970, -25931, -10643, 15828, 25667, + 8535, -17565, -25209, -6362, 19169, 24558, 4141, -20627, + -23721, -1888, 21928, 22703, -378, -23062, -21512, 2642}, + { +// Carrier 31 Phase 4 + 26000, 12339, -14287, -25901, -10298, 16126, 25605, 8178, + -17842, -25114, -5996, 19422, 24432, 3768, -20855, -23563, + -1511, 22129, 22516, -756, -23234, -21297, 3018, 24163, + 19917, -5257, -24907, -18384, 7456, 25462, 16712, -9599, + -25824, -14912, 11668, 25989, 12999, -13649, -25956, -10988, + 15526, 25725, 8892, -17284, -25299, -6729, 18911, 24680, + 4514, -20394, -23873, -2265, 21722, 22885, 0, -22885, + -21722, 2266, 23873, 20394, -4514, -24680, -18911, 6729, + 25299, 17284, -8892, -25725, -15526, 10988, 25956, 13649, + -13000, -25988, -11668, 14913, 25824, 9599, -16712, -25462, + -7456, 18384, 24907, 5257, -19917, -24162, -3018, 21298, + 23234, 756, -22516, -22128, 1511, 23564, 20855, -3768, + -24432, -19422, 5996, 25114, 17842, -8178, -25605, -16125, + 10298, 25901, 14287, -12339, -26000, -12339, 14287, 25901, + 10297, -16126, -25604, -8177, 17842, 25114, 5995, -19422, + -24431, -3768, 20855, 23563, 1511, -22129, -22516, 756, + 23234, 21297, -3018, -24163, -19917, 5257, 24907, 18384, + -7457, -25462, -16712, 9599, 25824, 14912, -11668, -25989, + -12999, 13649, 25955, 10987, -15526, -25725, -8892, 17284, + 25299, 6729, -18911, -24680, -4514, 20394, 23873, 2265, + -21722, -22885, 0, 22885, 21722, -2266, -23873, -20394, + 4515, 24680, 18911, -6729, -25299, -17284, 8892, 25725, + 15525, -10988, -25956, -13649, 13000, 25988, 11668, -14913, + -25824, -9599, 16712, 25462, 7456, -18385, -24907, -5257, + 19917, 24162, 3018, -21298, -23234, -755, 22516, 22128, + -1512, -23564, -20855, 3768, 24432, 19422, -5996, -25114, + -17842, 8178, 25605, 16125, -10298, -25901, -14286, 12339}, + { +// Carrier 31 Phase 5 + 24020, 2642, -21512, -23062, -378, 22703, 21928, -1889, + -23721, -20627, 4141, 24558, 19169, -6363, -25209, -17565, + 8536, 25667, 15827, -10644, -25931, -13969, 12671, 25997, + 12005, -14601, -25865, -9949, 16421, 25536, 7818, -18115, + -25013, -5627, 19672, 24300, 3393, -21078, -23401, -1134, + 22325, 22325, -1134, -23401, -21078, 3393, 24300, 19671, + -5627, -25013, -18115, 7818, 25536, 16420, -9949, -25865, + -14601, 12005, 25997, 12671, -13969, -25931, -10644, 15827, + 25667, 8536, -17565, -25209, -6363, 19169, 24558, 4141, + -20627, -23721, -1889, 21928, 22703, -378, -23062, -21512, + 2642, 24020, 20158, -4886, -24796, -18650, 7093, 25383, + 17000, -9247, -25777, -15221, 11329, 25975, 13325, -13326, + -25975, -11329, 15221, 25777, 9246, -17000, -25383, -7093, + 18650, 24796, 4886, -20158, -24020, -2642, 21512, 23062, + 377, -22703, -21928, 1889, 23721, 20627, -4142, -24558, + -19169, 6363, 25209, 17565, -8536, -25667, -15827, 10644, + 25931, 13969, -12671, -25997, -12005, 14601, 25865, 9949, + -16421, -25536, -7818, 18115, 25013, 5627, -19672, -24300, + -3393, 21078, 23401, 1133, -22325, -22325, 1134, 23401, + 21078, -3393, -24300, -19671, 5627, 25013, 18115, -7818, + -25536, -16420, 9950, 25865, 14601, -12005, -25997, -12670, + 13970, 25931, 10643, -15828, -25667, -8535, 17565, 25209, + 6363, -19169, -24558, -4141, 20627, 23721, 1888, -21928, + -22703, 378, 23062, 21512, -2642, -24020, -20157, 4887, + 24796, 18649, -7094, -25383, -17000, 9247, 25777, 15220, + -11329, -25975, -13325, 13326, 25975, 11329, -15221, -25777, + -9246, 17000, 25383, 7093, -18650, -24796, -4886, 20158}, + { +// Carrier 31 Phase 6 + 18384, -7456, -25462, -16712, 9599, 25824, 14912, -11668, + -25989, -12999, 13649, 25956, 10988, -15526, -25725, -8892, + 17284, 25299, 6729, -18911, -24680, -4514, 20394, 23873, + 2266, -21722, -22885, 0, 22885, 21722, -2266, -23873, + -20394, 4514, 24680, 18911, -6729, -25299, -17284, 8892, + 25725, 15526, -10988, -25956, -13649, 13000, 25988, 11668, + -14913, -25824, -9599, 16712, 25462, 7456, -18384, -24907, + -5257, 19917, 24162, 3018, -21298, -23234, -756, 22516, + 22128, -1511, -23564, -20855, 3768, 24432, 19422, -5996, + -25114, -17842, 8178, 25605, 16126, -10298, -25901, -14287, + 12339, 26000, 12339, -14287, -25901, -10297, 16126, 25604, + 8178, -17842, -25114, -5995, 19422, 24431, 3768, -20855, + -23563, -1511, 22129, 22516, -756, -23234, -21297, 3018, + 24163, 19917, -5257, -24907, -18384, 7457, 25462, 16712, + -9599, -25824, -14912, 11668, 25989, 12999, -13649, -25956, + -10987, 15526, 25725, 8892, -17284, -25299, -6729, 18911, + 24680, 4514, -20394, -23873, -2265, 21722, 22885, 0, + -22885, -21722, 2266, 23873, 20394, -4515, -24680, -18911, + 6729, 25299, 17284, -8892, -25725, -15525, 10988, 25956, + 13649, -13000, -25988, -11668, 14913, 25824, 9599, -16712, + -25462, -7456, 18384, 24907, 5257, -19917, -24162, -3018, + 21298, 23234, 755, -22516, -22128, 1512, 23564, 20855, + -3768, -24432, -19422, 5996, 25114, 17842, -8178, -25605, + -16125, 10298, 25901, 14286, -12339, -26000, -12339, 14287, + 25901, 10297, -16126, -25604, -8177, 17842, 25113, 5995, + -19422, -24431, -3767, 20855, 23563, 1511, -22129, -22516, + 756, 23234, 21297, -3018, -24163, -19916, 5258, 24907}, + { +// Carrier 31 Phase 7 + 9949, -16421, -25536, -7818, 18115, 25013, 5627, -19671, + -24300, -3393, 21078, 23401, 1134, -22325, -22325, 1134, + 23401, 21078, -3393, -24300, -19671, 5627, 25013, 18115, + -7818, -25536, -16421, 9949, 25865, 14601, -12005, -25997, + -12671, 13969, 25931, 10644, -15827, -25667, -8536, 17565, + 25209, 6363, -19169, -24558, -4141, 20627, 23721, 1889, + -21928, -22703, 378, 23062, 21512, -2642, -24020, -20158, + 4886, 24796, 18650, -7093, -25383, -17000, 9247, 25777, + 15221, -11329, -25975, -13326, 13326, 25975, 11329, -15221, + -25777, -9246, 17000, 25383, 7093, -18650, -24796, -4886, + 20158, 24020, 2642, -21512, -23062, -377, 22703, 21928, + -1889, -23721, -20627, 4142, 24558, 19169, -6363, -25209, + -17565, 8536, 25667, 15827, -10644, -25931, -13969, 12671, + 25997, 12005, -14601, -25865, -9949, 16421, 25536, 7818, + -18115, -25013, -5627, 19672, 24300, 3393, -21078, -23401, + -1133, 22325, 22325, -1134, -23401, -21078, 3393, 24300, + 19671, -5627, -25013, -18115, 7818, 25536, 16420, -9949, + -25865, -14601, 12005, 25997, 12670, -13969, -25931, -10643, + 15827, 25667, 8536, -17565, -25209, -6363, 19169, 24558, + 4141, -20627, -23721, -1888, 21928, 22703, -378, -23062, + -21512, 2642, 24020, 20157, -4887, -24796, -18650, 7094, + 25383, 17000, -9247, -25777, -15220, 11329, 25975, 13325, + -13326, -25975, -11329, 15221, 25777, 9246, -17000, -25383, + -7093, 18650, 24796, 4886, -20158, -24020, -2642, 21512, + 23062, 377, -22703, -21928, 1889, 23721, 20626, -4142, + -24558, -19168, 6363, 25209, 17565, -8536, -25668, -15827, + 10644, 25931, 13969, -12671, -25997, -12005, 14601, 25865}, + },{{ + +// Carrier 32 Phase 0 + 0, 23234, 20855, -4514, -24907, -17842, 8892, 25824, + 14287, -13000, -25956, -10298, 16712, 25299, 5995, -19917, + -23873, -1511, 22516, 21722, -3018, -24432, -18911, 7456, + 25605, 15526, -11668, -26000, -11668, 15526, 25604, 7456, + -18911, -24431, -3018, 21722, 22516, -1511, -23873, -19917, + 5996, 25299, 16712, -10298, -25956, -12999, 14287, 25824, + 8892, -17842, -24907, -4514, 20855, 23234, 0, -23234, + -20855, 4514, 24907, 17842, -8892, -25824, -14287, 13000, + 25956, 10297, -16712, -25299, -5995, 19917, 23873, 1511, + -22516, -21722, 3018, 24432, 18911, -7457, -25605, -15525, + 11668, 26000, 11668, -15526, -25604, -7456, 18911, 24431, + 3018, -21722, -22516, 1511, 23873, 19917, -5996, -25299, + -16712, 10298, 25956, 12999, -14287, -25824, -8892, 17842, + 24907, 4514, -20855, -23234, 0, 23234, 20855, -4515, + -24907, -17842, 8892, 25824, 14287, -13000, -25955, -10297, + 16712, 25299, 5995, -19917, -23873, -1511, 22516, 21722, + -3018, -24432, -18911, 7457, 25605, 15525, -11669, -26000, + -11668, 15526, 25604, 7456, -18911, -24431, -3018, 21722, + 22516, -1512, -23873, -19916, 5996, 25299, 16712, -10298, + -25956, -12999, 14287, 25824, 8892, -17842, -24907, -4514, + 20855, 23234, 0, -23234, -20854, 4515, 24907, 17842, + -8892, -25824, -14286, 13000, 25955, 10297, -16712, -25299, + -5995, 19917, 23873, 1511, -22516, -21722, 3018, 24432, + 18911, -7457, -25605, -15525, 11669, 26000, 11668, -15526, + -25604, -7456, 18911, 24431, 3018, -21722, -22516, 1512, + 23873, 19916, -5996, -25299, -16712, 10298, 25956, 12999, + -14287, -25824, -8892, 17842, 24907, 4514, -20855, -23234}, + { +// Carrier 32 Phase 1 + 9949, 25931, 13326, -13969, -25865, -9246, 17565, 25013, + 4886, -20627, -23401, -378, 23062, 21078, -4142, -24796, + -18115, 8536, 25777, 14601, -12671, -25975, -10644, 16421, + 25383, 6363, -19672, -24020, -1889, 22325, 21928, -2642, + -24300, -19169, 7093, 25536, 15827, -11329, -25997, -12005, + 15221, 25667, 7818, -18650, -24558, -3393, 21512, 22703, + -1134, -23721, -20158, 5627, 25209, 17000, -9949, -25931, + -13325, 13969, 25865, 9246, -17565, -25013, -4886, 20627, + 23401, 377, -23062, -21078, 4142, 24796, 18115, -8536, + -25777, -14601, 12671, 25975, 10644, -16421, -25383, -6363, + 19672, 24020, 1888, -22325, -21928, 2642, 24300, 19169, + -7094, -25536, -15827, 11329, 25997, 12005, -15221, -25667, + -7818, 18650, 24558, 3393, -21512, -22703, 1134, 23721, + 20157, -5627, -25209, -17000, 9949, 25931, 13325, -13969, + -25865, -9246, 17565, 25013, 4886, -20627, -23401, -377, + 23062, 21078, -4142, -24796, -18115, 8536, 25777, 14601, + -12671, -25975, -10643, 16421, 25383, 6363, -19672, -24020, + -1888, 22325, 21928, -2642, -24300, -19168, 7094, 25536, + 15827, -11329, -25997, -12005, 15221, 25667, 7818, -18650, + -24558, -3393, 21512, 22703, -1134, -23721, -20157, 5627, + 25209, 17000, -9950, -25931, -13325, 13970, 25865, 9246, + -17565, -25013, -4886, 20627, 23401, 377, -23062, -21078, + 4142, 24796, 18115, -8536, -25777, -14601, 12671, 25975, + 10643, -16421, -25383, -6362, 19672, 24020, 1888, -22325, + -21927, 2642, 24300, 19168, -7094, -25536, -15827, 11330, + 25997, 12005, -15221, -25667, -7817, 18650, 24558, 3393, + -21512, -22703, 1134, 23721, 20157, -5627, -25209, -17000}, + { +// Carrier 32 Phase 2 + 18384, 24680, 3768, -21297, -22885, 756, 23564, 20394, + -5257, -25114, -17284, 9599, 25901, 13649, -13649, -25901, + -9599, 17284, 25114, 5257, -20394, -23563, -756, 22885, + 21297, -3768, -24680, -18384, 8178, 25725, 14912, -12339, + -25988, -10988, 16126, 25462, 6729, -19422, -24163, -2265, + 22129, 22128, -2266, -24163, -19422, 6729, 25462, 16126, + -10988, -25989, -12339, 14913, 25725, 8178, -18384, -24680, + -3768, 21298, 22885, -756, -23564, -20394, 5257, 25114, + 17284, -9599, -25901, -13649, 13649, 25901, 9599, -17284, + -25114, -5257, 20394, 23563, 756, -22885, -21297, 3768, + 24680, 18384, -8178, -25725, -14912, 12339, 25988, 10987, + -16126, -25462, -6729, 19422, 24162, 2265, -22129, -22128, + 2266, 24163, 19422, -6729, -25462, -16125, 10988, 25989, + 12339, -14913, -25725, -8177, 18384, 24680, 3767, -21298, + -22885, 756, 23564, 20394, -5257, -25114, -17284, 9599, + 25901, 13649, -13649, -25901, -9599, 17284, 25114, 5257, + -20394, -23563, -755, 22885, 21297, -3768, -24680, -18384, + 8178, 25725, 14912, -12339, -25988, -10987, 16126, 25462, + 6728, -19422, -24162, -2265, 22129, 22128, -2266, -24163, + -19422, 6729, 25462, 16125, -10988, -25989, -12339, 14913, + 25725, 8177, -18385, -24680, -3767, 21298, 22885, -756, + -23564, -20394, 5258, 25114, 17284, -9599, -25901, -13649, + 13649, 25901, 9598, -17284, -25113, -5257, 20395, 23563, + 755, -22885, -21297, 3768, 24680, 18384, -8178, -25725, + -14912, 12339, 25988, 10987, -16126, -25462, -6728, 19422, + 24162, 2265, -22129, -22128, 2266, 24163, 19422, -6729, + -25462, -16125, 10988, 25989, 12339, -14913, -25725, -8177}, + { +// Carrier 32 Phase 3 + 24020, 19671, -6363, -25383, -16421, 10644, 25975, 12671, + -14601, -25777, -8536, 18115, 24796, 4141, -21078, -23062, + 378, 23401, 20627, -4886, -25013, -17565, 9246, 25865, + 13969, -13326, -25931, -9949, 17000, 25209, 5627, -20158, + -23721, -1134, 22703, 21512, -3393, -24558, -18650, 7818, + 25667, 15221, -12005, -25997, -11329, 15827, 25536, 7093, + -19169, -24300, -2642, 21928, 22325, -1889, -24020, -19671, + 6363, 25383, 16420, -10644, -25975, -12671, 14601, 25777, + 8536, -18115, -24796, -4141, 21078, 23062, -378, -23401, + -20627, 4886, 25013, 17565, -9247, -25865, -13969, 13326, + 25931, 9949, -17000, -25209, -5627, 20158, 23721, 1133, + -22703, -21512, 3393, 24558, 18650, -7818, -25667, -15220, + 12005, 25997, 11329, -15827, -25536, -7093, 19169, 24300, + 2642, -21928, -22325, 1889, 24020, 19671, -6363, -25383, + -16420, 10644, 25975, 12670, -14601, -25777, -8536, 18115, + 24796, 4141, -21078, -23062, 378, 23401, 20627, -4887, + -25013, -17565, 9247, 25865, 13969, -13326, -25931, -9949, + 17000, 25209, 5627, -20158, -23721, -1133, 22703, 21512, + -3393, -24558, -18650, 7818, 25668, 15220, -12005, -25997, + -11329, 15828, 25536, 7093, -19169, -24299, -2642, 21928, + 22325, -1889, -24020, -19671, 6363, 25383, 16420, -10644, + -25975, -12670, 14601, 25777, 8535, -18115, -24796, -4141, + 21079, 23062, -378, -23401, -20626, 4887, 25013, 17565, + -9247, -25865, -13969, 13326, 25931, 9949, -17000, -25209, + -5627, 20158, 23721, 1133, -22703, -21512, 3394, 24558, + 18649, -7818, -25668, -15220, 12005, 25997, 11329, -15828, + -25536, -7093, 19169, 24299, 2642, -21928, -22324, 1889}, + { +// Carrier 32 Phase 4 + 26000, 11668, -15526, -25605, -7456, 18911, 24432, 3018, + -21722, -22516, 1511, 23873, 19917, -5996, -25299, -16712, + 10298, 25956, 12999, -14287, -25824, -8892, 17842, 24907, + 4514, -20855, -23234, 0, 23234, 20855, -4514, -24907, + -17842, 8892, 25824, 14287, -13000, -25956, -10298, 16712, + 25299, 5995, -19917, -23873, -1511, 22516, 21722, -3018, + -24432, -18911, 7456, 25605, 15526, -11668, -26000, -11668, + 15526, 25604, 7456, -18911, -24431, -3018, 21722, 22516, + -1511, -23873, -19917, 5996, 25299, 16712, -10298, -25956, + -12999, 14287, 25824, 8892, -17842, -24907, -4514, 20855, + 23234, 0, -23234, -20855, 4515, 24907, 17842, -8892, + -25824, -14287, 13000, 25956, 10297, -16712, -25299, -5995, + 19917, 23873, 1511, -22516, -21722, 3018, 24432, 18911, + -7457, -25605, -15525, 11668, 26000, 11668, -15526, -25604, + -7456, 18911, 24431, 3018, -21722, -22516, 1512, 23873, + 19917, -5996, -25299, -16712, 10298, 25956, 12999, -14287, + -25824, -8892, 17842, 24907, 4514, -20855, -23234, 0, + 23234, 20855, -4515, -24907, -17842, 8892, 25824, 14286, + -13000, -25955, -10297, 16712, 25299, 5995, -19917, -23873, + -1511, 22516, 21722, -3018, -24432, -18911, 7457, 25605, + 15525, -11669, -26000, -11668, 15526, 25604, 7456, -18911, + -24431, -3018, 21722, 22516, -1512, -23873, -19916, 5996, + 25299, 16712, -10298, -25956, -12999, 14287, 25824, 8892, + -17842, -24907, -4514, 20855, 23234, 0, -23234, -20854, + 4515, 24907, 17841, -8892, -25824, -14286, 13000, 25955, + 10297, -16712, -25299, -5995, 19917, 23873, 1511, -22516, + -21722, 3018, 24432, 18911, -7457, -25605, -15525, 11669}, + { +// Carrier 32 Phase 5 + 24020, 1889, -22325, -21928, 2642, 24300, 19169, -7093, + -25536, -15827, 11329, 25997, 12005, -15221, -25667, -7818, + 18650, 24558, 3393, -21512, -22703, 1134, 23721, 20158, + -5627, -25209, -17000, 9949, 25931, 13326, -13969, -25865, + -9246, 17565, 25013, 4886, -20627, -23401, -378, 23062, + 21078, -4142, -24796, -18115, 8536, 25777, 14601, -12671, + -25975, -10644, 16421, 25383, 6363, -19672, -24020, -1889, + 22325, 21928, -2642, -24300, -19169, 7093, 25536, 15827, + -11329, -25997, -12005, 15221, 25667, 7818, -18650, -24558, + -3393, 21512, 22703, -1134, -23721, -20158, 5627, 25209, + 17000, -9949, -25931, -13325, 13969, 25865, 9246, -17565, + -25013, -4886, 20627, 23401, 377, -23062, -21078, 4142, + 24796, 18115, -8536, -25777, -14601, 12671, 25975, 10643, + -16421, -25383, -6363, 19672, 24020, 1888, -22325, -21928, + 2642, 24300, 19169, -7094, -25536, -15827, 11329, 25997, + 12005, -15221, -25667, -7818, 18650, 24558, 3393, -21512, + -22703, 1134, 23721, 20157, -5627, -25209, -17000, 9950, + 25931, 13325, -13970, -25865, -9246, 17565, 25013, 4886, + -20627, -23401, -377, 23062, 21078, -4142, -24796, -18115, + 8536, 25777, 14601, -12671, -25975, -10643, 16421, 25383, + 6362, -19672, -24020, -1888, 22325, 21927, -2642, -24300, + -19168, 7094, 25536, 15827, -11329, -25997, -12005, 15221, + 25667, 7817, -18650, -24558, -3393, 21512, 22703, -1134, + -23721, -20157, 5627, 25209, 17000, -9950, -25931, -13325, + 13970, 25865, 9246, -17565, -25013, -4886, 20627, 23401, + 377, -23062, -21078, 4142, 24796, 18115, -8536, -25777, + -14601, 12671, 25975, 10643, -16421, -25383, -6362, 19672}, + { +// Carrier 32 Phase 6 + 18384, -8178, -25725, -14912, 12339, 25989, 10988, -16126, + -25462, -6729, 19422, 24163, 2266, -22129, -22129, 2266, + 24163, 19422, -6729, -25462, -16126, 10988, 25989, 12339, + -14913, -25725, -8178, 18384, 24680, 3768, -21297, -22885, + 756, 23564, 20394, -5257, -25114, -17284, 9599, 25901, + 13649, -13649, -25901, -9599, 17284, 25114, 5257, -20394, + -23563, -756, 22885, 21297, -3768, -24680, -18384, 8178, + 25725, 14912, -12339, -25988, -10987, 16126, 25462, 6729, + -19422, -24162, -2265, 22129, 22128, -2266, -24163, -19422, + 6729, 25462, 16125, -10988, -25989, -12339, 14913, 25725, + 8178, -18384, -24680, -3768, 21298, 22885, -756, -23564, + -20394, 5257, 25114, 17284, -9599, -25901, -13649, 13649, + 25901, 9599, -17284, -25114, -5257, 20394, 23563, 755, + -22885, -21297, 3768, 24680, 18384, -8178, -25725, -14912, + 12339, 25988, 10987, -16126, -25462, -6729, 19422, 24162, + 2265, -22129, -22128, 2266, 24163, 19422, -6729, -25462, + -16125, 10988, 25989, 12339, -14913, -25725, -8177, 18384, + 24680, 3767, -21298, -22885, 756, 23564, 20394, -5257, + -25114, -17284, 9599, 25901, 13649, -13649, -25901, -9599, + 17284, 25113, 5257, -20395, -23563, -755, 22885, 21297, + -3768, -24680, -18384, 8178, 25725, 14912, -12339, -25988, + -10987, 16126, 25462, 6728, -19422, -24162, -2265, 22129, + 22128, -2266, -24163, -19422, 6729, 25462, 16125, -10988, + -25989, -12339, 14913, 25725, 8177, -18385, -24680, -3767, + 21298, 22885, -756, -23564, -20394, 5258, 25114, 17284, + -9599, -25901, -13649, 13649, 25901, 9598, -17285, -25113, + -5257, 20395, 23563, 755, -22885, -21297, 3768, 24680}, + { +// Carrier 32 Phase 7 + 9949, -17000, -25209, -5627, 20158, 23721, 1134, -22703, + -21512, 3393, 24558, 18650, -7818, -25667, -15221, 12005, + 25997, 11329, -15827, -25536, -7093, 19169, 24300, 2642, + -21928, -22325, 1889, 24020, 19671, -6363, -25383, -16421, + 10644, 25975, 12671, -14601, -25777, -8536, 18115, 24796, + 4141, -21078, -23062, 378, 23401, 20627, -4886, -25013, + -17565, 9247, 25865, 13969, -13326, -25931, -9949, 17000, + 25209, 5627, -20158, -23721, -1134, 22703, 21512, -3393, + -24558, -18650, 7818, 25667, 15221, -12005, -25997, -11329, + 15827, 25536, 7093, -19169, -24300, -2642, 21928, 22325, + -1889, -24020, -19671, 6363, 25383, 16420, -10644, -25975, + -12670, 14601, 25777, 8536, -18115, -24796, -4141, 21078, + 23062, -378, -23401, -20627, 4886, 25013, 17565, -9247, + -25865, -13969, 13326, 25931, 9949, -17000, -25209, -5627, + 20158, 23721, 1133, -22703, -21512, 3393, 24558, 18650, + -7818, -25667, -15220, 12005, 25997, 11329, -15827, -25536, + -7093, 19169, 24299, 2642, -21928, -22325, 1889, 24020, + 19671, -6363, -25383, -16420, 10644, 25975, 12670, -14601, + -25777, -8535, 18115, 24796, 4141, -21078, -23062, 378, + 23401, 20627, -4887, -25013, -17565, 9247, 25865, 13969, + -13326, -25931, -9949, 17000, 25209, 5627, -20158, -23721, + -1133, 22703, 21512, -3394, -24558, -18649, 7818, 25668, + 15220, -12005, -25997, -11329, 15828, 25536, 7093, -19169, + -24299, -2642, 21928, 22325, -1889, -24021, -19671, 6363, + 25383, 16420, -10644, -25975, -12670, 14601, 25777, 8535, + -18115, -24796, -4141, 21079, 23062, -378, -23401, -20626, + 4887, 25013, 17565, -9247, -25865, -13969, 13326, 25931}, + },{{ + +// Carrier 33 Phase 0 + 0, 23564, 19917, -6729, -25605, -14912, 13000, 25901, + 8892, -18384, -24432, -2266, 22516, 21297, -4514, -25114, + -16712, 10988, 26000, 10988, -16712, -25114, -4514, 21297, + 22516, -2266, -24432, -18384, 8892, 25901, 12999, -14913, + -25604, -6729, 19917, 23563, 0, -23564, -19917, 6729, + 25605, 14912, -13000, -25901, -8892, 18384, 24431, 2265, + -22516, -21297, 4515, 25114, 16712, -10988, -26000, -10987, + 16712, 25114, 4514, -21298, -22516, 2266, 24432, 18384, + -8892, -25901, -12999, 14913, 25604, 6729, -19917, -23563, + 0, 23564, 19916, -6729, -25605, -14912, 13000, 25901, + 8892, -18384, -24431, -2265, 22516, 21297, -4515, -25114, + -16712, 10988, 26000, 10987, -16712, -25113, -4514, 21298, + 22516, -2266, -24432, -18384, 8892, 25901, 12999, -14913, + -25604, -6728, 19917, 23563, 0, -23564, -19916, 6729, + 25605, 14912, -13000, -25901, -8892, 18385, 24431, 2265, + -22516, -21297, 4515, 25114, 16712, -10988, -26000, -10987, + 16712, 25113, 4514, -21298, -22516, 2266, 24432, 18384, + -8892, -25901, -12999, 14913, 25604, 6728, -19917, -23563, + 0, 23564, 19916, -6729, -25605, -14912, 13000, 25901, + 8892, -18385, -24431, -2265, 22516, 21297, -4515, -25114, + -16712, 10988, 26000, 10987, -16712, -25113, -4514, 21298, + 22516, -2266, -24432, -18384, 8893, 25901, 12999, -14913, + -25604, -6728, 19917, 23563, 0, -23564, -19916, 6729, + 25605, 14912, -13000, -25901, -8891, 18385, 24431, 2265, + -22516, -21297, 4515, 25114, 16711, -10988, -26000, -10987, + 16713, 25113, 4514, -21298, -22516, 2266, 24432, 18384, + -8893, -25901, -12999, 14913, 25604, 6728, -19917, -23563}, + { +// Carrier 33 Phase 1 + 9949, 25975, 12005, -15827, -25383, -5627, 20627, 23062, + -1134, -24020, -19169, 7818, 25777, 13969, -13969, -25777, + -7818, 19169, 24020, 1134, -23062, -20627, 5627, 25383, + 15827, -12005, -25975, -9949, 17565, 24796, 3393, -21928, + -21928, 3393, 24796, 17565, -9949, -25975, -12005, 15827, + 25383, 5627, -20627, -23062, 1134, 24020, 19169, -7818, + -25777, -13969, 13969, 25777, 7818, -19169, -24020, -1133, + 23062, 20627, -5627, -25383, -15827, 12005, 25975, 9949, + -17565, -24796, -3393, 21928, 21928, -3393, -24796, -17565, + 9950, 25975, 12005, -15827, -25383, -5627, 20627, 23062, + -1134, -24020, -19169, 7818, 25777, 13969, -13970, -25777, + -7818, 19169, 24020, 1133, -23062, -20626, 5627, 25383, + 15827, -12005, -25975, -9949, 17565, 24796, 3393, -21928, + -21927, 3394, 24796, 17565, -9950, -25975, -12005, 15828, + 25383, 5627, -20627, -23062, 1134, 24021, 19168, -7818, + -25777, -13969, 13970, 25777, 7817, -19169, -24020, -1133, + 23062, 20626, -5627, -25383, -15827, 12005, 25975, 9949, + -17565, -24796, -3393, 21928, 21927, -3394, -24796, -17564, + 9950, 25975, 12005, -15828, -25383, -5626, 20627, 23062, + -1134, -24021, -19168, 7818, 25777, 13969, -13970, -25777, + -7817, 19169, 24020, 1133, -23062, -20626, 5627, 25383, + 15827, -12005, -25975, -9949, 17565, 24796, 3393, -21928, + -21927, 3394, 24796, 17564, -9950, -25975, -12004, 15828, + 25383, 5626, -20627, -23061, 1134, 24021, 19168, -7818, + -25777, -13969, 13970, 25777, 7817, -19169, -24020, -1133, + 23062, 20626, -5628, -25383, -15827, 12006, 25975, 9949, + -17565, -24796, -3392, 21928, 21927, -3394, -24796, -17564}, + { +// Carrier 33 Phase 2 + 18384, 24432, 2266, -22516, -21297, 4514, 25114, 16712, + -10988, -26000, -10988, 16712, 25114, 4514, -21297, -22516, + 2266, 24432, 18384, -8892, -25901, -12999, 14913, 25604, + 6729, -19917, -23563, 0, 23564, 19917, -6729, -25605, + -14912, 13000, 25901, 8892, -18384, -24431, -2265, 22516, + 21297, -4514, -25114, -16712, 10988, 26000, 10987, -16712, + -25114, -4514, 21298, 22516, -2266, -24432, -18384, 8892, + 25901, 12999, -14913, -25604, -6729, 19917, 23563, 0, + -23564, -19917, 6729, 25605, 14912, -13000, -25901, -8892, + 18384, 24431, 2265, -22516, -21297, 4515, 25114, 16712, + -10988, -26000, -10987, 16712, 25113, 4514, -21298, -22516, + 2266, 24432, 18384, -8892, -25901, -12999, 14913, 25604, + 6728, -19917, -23563, 0, 23564, 19916, -6729, -25605, + -14912, 13000, 25901, 8892, -18385, -24431, -2265, 22516, + 21297, -4515, -25114, -16712, 10988, 26000, 10987, -16712, + -25113, -4514, 21298, 22516, -2266, -24432, -18384, 8892, + 25901, 12999, -14913, -25604, -6728, 19917, 23563, 0, + -23564, -19916, 6729, 25605, 14912, -13000, -25901, -8892, + 18385, 24431, 2265, -22516, -21297, 4515, 25114, 16712, + -10988, -26000, -10987, 16712, 25113, 4514, -21298, -22516, + 2266, 24432, 18384, -8893, -25901, -12999, 14913, 25604, + 6728, -19917, -23563, 0, 23564, 19916, -6729, -25605, + -14912, 13000, 25901, 8891, -18385, -24431, -2265, 22516, + 21297, -4515, -25114, -16711, 10988, 26000, 10987, -16712, + -25113, -4514, 21298, 22516, -2266, -24432, -18384, 8893, + 25901, 12999, -14913, -25604, -6728, 19917, 23563, 0, + -23564, -19916, 6730, 25605, 14912, -13000, -25900, -8891}, + { +// Carrier 33 Phase 3 + 24020, 19169, -7818, -25777, -13969, 13969, 25777, 7818, + -19169, -24020, -1134, 23062, 20627, -5627, -25383, -15827, + 12005, 25975, 9949, -17565, -24796, -3393, 21928, 21928, + -3393, -24796, -17565, 9949, 25975, 12005, -15827, -25383, + -5627, 20627, 23062, -1134, -24020, -19169, 7818, 25777, + 13969, -13969, -25777, -7818, 19169, 24020, 1133, -23062, + -20627, 5627, 25383, 15827, -12005, -25975, -9949, 17565, + 24796, 3393, -21928, -21928, 3393, 24796, 17565, -9949, + -25975, -12005, 15827, 25383, 5627, -20627, -23062, 1134, + 24020, 19169, -7818, -25777, -13969, 13970, 25777, 7818, + -19169, -24020, -1133, 23062, 20627, -5627, -25383, -15827, + 12005, 25975, 9949, -17565, -24796, -3393, 21928, 21927, + -3394, -24796, -17565, 9950, 25975, 12005, -15828, -25383, + -5627, 20627, 23062, -1134, -24021, -19168, 7818, 25777, + 13969, -13970, -25777, -7817, 19169, 24020, 1133, -23062, + -20626, 5627, 25383, 15827, -12005, -25975, -9949, 17565, + 24796, 3393, -21928, -21927, 3394, 24796, 17565, -9950, + -25975, -12005, 15828, 25383, 5626, -20627, -23062, 1134, + 24021, 19168, -7818, -25777, -13969, 13970, 25777, 7817, + -19169, -24020, -1133, 23062, 20626, -5627, -25383, -15827, + 12005, 25975, 9949, -17565, -24796, -3393, 21928, 21927, + -3394, -24796, -17564, 9950, 25975, 12004, -15828, -25383, + -5626, 20627, 23061, -1134, -24021, -19168, 7818, 25777, + 13969, -13970, -25777, -7817, 19169, 24020, 1133, -23062, + -20626, 5628, 25383, 15827, -12006, -25975, -9949, 17565, + 24796, 3392, -21928, -21927, 3394, 24796, 17564, -9950, + -25975, -12004, 15828, 25383, 5626, -20627, -23061, 1134}, + { +// Carrier 33 Phase 4 + 26000, 10988, -16712, -25114, -4514, 21297, 22516, -2266, + -24432, -18384, 8892, 25901, 12999, -14913, -25604, -6729, + 19917, 23563, 0, -23564, -19917, 6729, 25605, 14912, + -13000, -25901, -8892, 18384, 24431, 2265, -22516, -21297, + 4514, 25114, 16712, -10988, -26000, -10987, 16712, 25114, + 4514, -21298, -22516, 2266, 24432, 18384, -8892, -25901, + -12999, 14913, 25604, 6729, -19917, -23563, 0, 23564, + 19917, -6729, -25605, -14912, 13000, 25901, 8892, -18384, + -24431, -2265, 22516, 21297, -4515, -25114, -16712, 10988, + 26000, 10987, -16712, -25114, -4514, 21298, 22516, -2266, + -24432, -18384, 8892, 25901, 12999, -14913, -25604, -6729, + 19917, 23563, 0, -23564, -19916, 6729, 25605, 14912, + -13000, -25901, -8892, 18385, 24431, 2265, -22516, -21297, + 4515, 25114, 16712, -10988, -26000, -10987, 16712, 25113, + 4514, -21298, -22516, 2266, 24432, 18384, -8892, -25901, + -12999, 14913, 25604, 6728, -19917, -23563, 0, 23564, + 19916, -6729, -25605, -14912, 13000, 25901, 8892, -18385, + -24431, -2265, 22516, 21297, -4515, -25114, -16712, 10988, + 26000, 10987, -16712, -25113, -4514, 21298, 22516, -2266, + -24432, -18384, 8893, 25901, 12999, -14913, -25604, -6728, + 19917, 23563, 0, -23564, -19916, 6729, 25605, 14912, + -13000, -25901, -8891, 18385, 24431, 2265, -22516, -21297, + 4515, 25114, 16712, -10988, -26000, -10987, 16712, 25113, + 4514, -21298, -22516, 2266, 24432, 18384, -8893, -25901, + -12999, 14913, 25604, 6728, -19917, -23563, 0, 23564, + 19916, -6729, -25605, -14912, 13000, 25901, 8891, -18385, + -24431, -2265, 22517, 21297, -4515, -25114, -16711, 10988}, + { +// Carrier 33 Phase 5 + 24020, 1134, -23062, -20627, 5627, 25383, 15827, -12005, + -25975, -9949, 17565, 24796, 3393, -21928, -21928, 3393, + 24796, 17565, -9949, -25975, -12005, 15827, 25383, 5627, + -20627, -23062, 1134, 24020, 19169, -7818, -25777, -13969, + 13969, 25777, 7818, -19169, -24020, -1133, 23062, 20627, + -5627, -25383, -15827, 12005, 25975, 9949, -17565, -24796, + -3393, 21928, 21928, -3393, -24796, -17565, 9949, 25975, + 12005, -15827, -25383, -5627, 20627, 23062, -1134, -24020, + -19169, 7818, 25777, 13969, -13969, -25777, -7818, 19169, + 24020, 1133, -23062, -20627, 5627, 25383, 15827, -12005, + -25975, -9949, 17565, 24796, 3393, -21928, -21928, 3393, + 24796, 17565, -9950, -25975, -12005, 15828, 25383, 5627, + -20627, -23062, 1134, 24020, 19168, -7818, -25777, -13969, + 13970, 25777, 7818, -19169, -24020, -1133, 23062, 20626, + -5627, -25383, -15827, 12005, 25975, 9949, -17565, -24796, + -3393, 21928, 21927, -3394, -24796, -17565, 9950, 25975, + 12005, -15828, -25383, -5627, 20627, 23062, -1134, -24021, + -19168, 7818, 25777, 13969, -13970, -25777, -7817, 19169, + 24020, 1133, -23062, -20626, 5627, 25383, 15827, -12005, + -25975, -9949, 17565, 24796, 3393, -21928, -21927, 3394, + 24796, 17564, -9950, -25975, -12004, 15828, 25383, 5626, + -20627, -23062, 1134, 24021, 19168, -7818, -25777, -13969, + 13970, 25777, 7817, -19169, -24020, -1133, 23062, 20626, + -5628, -25383, -15827, 12006, 25975, 9949, -17565, -24796, + -3393, 21928, 21927, -3394, -24796, -17564, 9950, 25975, + 12004, -15828, -25383, -5626, 20627, 23061, -1134, -24021, + -19168, 7819, 25777, 13969, -13970, -25777, -7817, 19169}, + { +// Carrier 33 Phase 6 + 18384, -8892, -25901, -13000, 14912, 25604, 6729, -19917, + -23563, 0, 23564, 19917, -6729, -25605, -14912, 13000, + 25901, 8892, -18384, -24431, -2265, 22516, 21297, -4514, + -25114, -16712, 10988, 26000, 10987, -16712, -25114, -4514, + 21298, 22516, -2266, -24432, -18384, 8892, 25901, 12999, + -14913, -25604, -6729, 19917, 23563, 0, -23564, -19917, + 6729, 25605, 14912, -13000, -25901, -8892, 18384, 24431, + 2265, -22516, -21297, 4515, 25114, 16712, -10988, -26000, + -10987, 16712, 25114, 4514, -21298, -22516, 2266, 24432, + 18384, -8892, -25901, -12999, 14913, 25604, 6729, -19917, + -23563, 0, 23564, 19916, -6729, -25605, -14912, 13000, + 25901, 8892, -18384, -24431, -2265, 22516, 21297, -4515, + -25114, -16712, 10988, 26000, 10987, -16712, -25113, -4514, + 21298, 22516, -2266, -24432, -18384, 8892, 25901, 12999, + -14913, -25604, -6728, 19917, 23563, 0, -23564, -19916, + 6729, 25605, 14912, -13000, -25901, -8892, 18385, 24431, + 2265, -22516, -21297, 4515, 25114, 16712, -10988, -26000, + -10987, 16712, 25113, 4514, -21298, -22516, 2266, 24432, + 18384, -8892, -25901, -12999, 14913, 25604, 6728, -19917, + -23563, 0, 23564, 19916, -6729, -25605, -14912, 13000, + 25901, 8892, -18385, -24431, -2265, 22516, 21297, -4515, + -25114, -16712, 10988, 26000, 10987, -16712, -25113, -4514, + 21298, 22516, -2266, -24432, -18384, 8893, 25901, 12999, + -14913, -25604, -6728, 19917, 23563, 0, -23564, -19916, + 6729, 25605, 14912, -13000, -25901, -8891, 18385, 24431, + 2265, -22516, -21297, 4515, 25114, 16711, -10988, -26000, + -10987, 16713, 25113, 4514, -21298, -22516, 2266, 24432}, + { +// Carrier 33 Phase 7 + 9949, -17565, -24796, -3393, 21928, 21928, -3393, -24796, + -17565, 9949, 25975, 12005, -15827, -25383, -5627, 20627, + 23062, -1134, -24020, -19169, 7818, 25777, 13969, -13969, + -25777, -7818, 19169, 24020, 1134, -23062, -20627, 5627, + 25383, 15827, -12005, -25975, -9949, 17565, 24796, 3393, + -21928, -21928, 3393, 24796, 17565, -9949, -25975, -12005, + 15827, 25383, 5627, -20627, -23062, 1134, 24020, 19169, + -7818, -25777, -13969, 13969, 25777, 7818, -19169, -24020, + -1133, 23062, 20627, -5627, -25383, -15827, 12005, 25975, + 9949, -17565, -24796, -3393, 21928, 21928, -3393, -24796, + -17565, 9950, 25975, 12005, -15828, -25383, -5627, 20627, + 23062, -1134, -24020, -19169, 7818, 25777, 13969, -13970, + -25777, -7818, 19169, 24020, 1133, -23062, -20626, 5627, + 25383, 15827, -12005, -25975, -9949, 17565, 24796, 3393, + -21928, -21927, 3394, 24796, 17565, -9950, -25975, -12005, + 15828, 25383, 5627, -20627, -23062, 1134, 24021, 19168, + -7818, -25777, -13969, 13970, 25777, 7817, -19169, -24020, + -1133, 23062, 20626, -5627, -25383, -15827, 12005, 25975, + 9949, -17565, -24796, -3393, 21928, 21927, -3394, -24796, + -17564, 9950, 25975, 12004, -15828, -25383, -5626, 20627, + 23062, -1134, -24021, -19168, 7818, 25777, 13969, -13970, + -25777, -7817, 19169, 24020, 1133, -23062, -20626, 5628, + 25383, 15827, -12005, -25975, -9949, 17565, 24796, 3393, + -21928, -21927, 3394, 24796, 17564, -9950, -25975, -12004, + 15828, 25383, 5626, -20627, -23061, 1134, 24021, 19168, + -7818, -25777, -13969, 13970, 25777, 7817, -19169, -24020, + -1133, 23062, 20626, -5628, -25383, -15827, 12006, 25975}, + },{{ + +// Carrier 34 Phase 0 + 0, 23873, 18911, -8892, -25956, -11668, 16712, 24907, + 3018, -22516, -20855, 5996, 25605, 14287, -14287, -25605, + -5996, 20855, 22516, -3018, -24907, -16712, 11668, 25956, + 8892, -18911, -23873, 0, 23873, 18911, -8892, -25956, + -11668, 16712, 24907, 3018, -22516, -20855, 5995, 25604, + 14287, -14287, -25605, -5996, 20855, 22516, -3018, -24907, + -16712, 11668, 25956, 8892, -18911, -23873, 0, 23873, + 18911, -8892, -25956, -11668, 16712, 24907, 3018, -22516, + -20855, 5995, 25604, 14287, -14287, -25605, -5996, 20855, + 22516, -3018, -24907, -16712, 11668, 25956, 8892, -18911, + -23873, 0, 23873, 18911, -8892, -25956, -11668, 16712, + 24907, 3018, -22516, -20855, 5995, 25604, 14287, -14287, + -25605, -5996, 20855, 22516, -3018, -24907, -16712, 11668, + 25956, 8892, -18911, -23873, 0, 23873, 18911, -8892, + -25956, -11668, 16712, 24907, 3018, -22516, -20855, 5995, + 25604, 14287, -14287, -25605, -5996, 20855, 22516, -3018, + -24907, -16712, 11668, 25956, 8892, -18911, -23873, 0, + 23873, 18911, -8892, -25956, -11668, 16712, 24907, 3018, + -22516, -20855, 5995, 25604, 14287, -14287, -25605, -5996, + 20855, 22516, -3018, -24907, -16712, 11668, 25956, 8892, + -18911, -23873, 0, 23873, 18911, -8892, -25956, -11668, + 16712, 24907, 3018, -22516, -20855, 5995, 25604, 14287, + -14287, -25605, -5996, 20855, 22516, -3018, -24907, -16712, + 11668, 25956, 8892, -18911, -23873, 0, 23873, 18911, + -8892, -25956, -11668, 16712, 24907, 3018, -22516, -20855, + 5995, 25604, 14287, -14287, -25605, -5996, 20855, 22516, + -3018, -24907, -16712, 11668, 25956, 8892, -18911, -23873}, + { +// Carrier 34 Phase 1 + 9949, 25997, 10644, -17565, -24558, -1889, 23062, 20158, + -7093, -25777, -13326, 15221, 25383, 4886, -21512, -21928, + 4141, 25209, 15827, -12671, -25865, -7818, 19671, 23401, + -1134, -24300, -18115, 9949, 25997, 10644, -17565, -24558, + -1889, 23062, 20158, -7093, -25777, -13326, 15221, 25383, + 4886, -21512, -21928, 4141, 25209, 15827, -12671, -25865, + -7818, 19671, 23401, -1134, -24300, -18115, 9949, 25997, + 10644, -17565, -24558, -1889, 23062, 20158, -7093, -25777, + -13326, 15221, 25383, 4886, -21512, -21928, 4141, 25209, + 15827, -12671, -25865, -7818, 19671, 23401, -1134, -24300, + -18115, 9949, 25997, 10644, -17565, -24558, -1889, 23062, + 20158, -7093, -25777, -13326, 15221, 25383, 4886, -21512, + -21928, 4141, 25209, 15827, -12671, -25865, -7818, 19671, + 23401, -1134, -24300, -18115, 9949, 25997, 10644, -17565, + -24558, -1889, 23062, 20158, -7093, -25777, -13326, 15221, + 25383, 4886, -21512, -21928, 4141, 25209, 15827, -12671, + -25865, -7818, 19671, 23401, -1134, -24300, -18115, 9949, + 25997, 10644, -17565, -24558, -1889, 23062, 20158, -7093, + -25777, -13326, 15221, 25383, 4886, -21512, -21928, 4141, + 25209, 15827, -12671, -25865, -7818, 19671, 23401, -1134, + -24300, -18115, 9949, 25997, 10644, -17565, -24558, -1889, + 23062, 20158, -7093, -25777, -13326, 15221, 25383, 4886, + -21512, -21928, 4141, 25209, 15827, -12671, -25865, -7818, + 19671, 23401, -1134, -24300, -18115, 9949, 25997, 10644, + -17565, -24558, -1889, 23062, 20158, -7093, -25777, -13326, + 15221, 25383, 4886, -21512, -21928, 4141, 25209, 15827, + -12671, -25865, -7818, 19671, 23401, -1134, -24300, -18115}, + { +// Carrier 34 Phase 2 + 18384, 24163, 756, -23563, -19422, 8178, 25901, 12339, + -16126, -25114, -3768, 22129, 21297, -5257, -25462, -14913, + 13649, 25725, 6729, -20394, -22885, 2266, 24680, 17284, + -10988, -25989, -9599, 18384, 24163, 756, -23563, -19422, + 8178, 25901, 12339, -16126, -25114, -3768, 22129, 21297, + -5257, -25462, -14913, 13649, 25725, 6729, -20394, -22885, + 2266, 24680, 17284, -10988, -25989, -9599, 18384, 24163, + 756, -23563, -19422, 8178, 25901, 12339, -16126, -25114, + -3768, 22129, 21297, -5257, -25462, -14913, 13649, 25725, + 6729, -20394, -22885, 2266, 24680, 17284, -10988, -25989, + -9599, 18384, 24163, 756, -23563, -19422, 8178, 25901, + 12339, -16126, -25114, -3768, 22129, 21297, -5257, -25462, + -14913, 13649, 25725, 6729, -20394, -22885, 2265, 24680, + 17284, -10988, -25989, -9599, 18384, 24163, 756, -23563, + -19422, 8178, 25901, 12339, -16126, -25114, -3768, 22129, + 21297, -5257, -25462, -14913, 13649, 25725, 6729, -20394, + -22885, 2265, 24680, 17284, -10988, -25989, -9599, 18384, + 24163, 756, -23563, -19422, 8178, 25901, 12339, -16126, + -25114, -3768, 22128, 21297, -5257, -25462, -14913, 13649, + 25725, 6729, -20394, -22885, 2265, 24680, 17284, -10988, + -25989, -9599, 18384, 24163, 756, -23563, -19422, 8178, + 25901, 12339, -16126, -25114, -3768, 22128, 21298, -5257, + -25462, -14913, 13649, 25725, 6729, -20394, -22885, 2265, + 24680, 17284, -10987, -25989, -9599, 18384, 24163, 756, + -23563, -19422, 8178, 25901, 12339, -16126, -25114, -3768, + 22128, 21298, -5257, -25462, -14913, 13649, 25725, 6729, + -20394, -22885, 2265, 24680, 17284, -10987, -25989, -9599}, + { +// Carrier 34 Phase 3 + 24020, 18650, -9246, -25975, -11329, 17000, 24796, 2642, + -22703, -20627, 6363, 25667, 13969, -14601, -25536, -5627, + 21078, 22325, -3393, -25013, -16421, 12005, 25931, 8536, + -19169, -23721, 378, 24020, 18650, -9246, -25975, -11329, + 17000, 24796, 2642, -22703, -20627, 6363, 25667, 13969, + -14601, -25536, -5627, 21078, 22325, -3393, -25013, -16421, + 12005, 25931, 8536, -19169, -23721, 378, 24020, 18650, + -9246, -25975, -11329, 17000, 24796, 2642, -22703, -20627, + 6363, 25667, 13969, -14601, -25536, -5627, 21078, 22325, + -3393, -25013, -16421, 12005, 25931, 8536, -19169, -23721, + 378, 24020, 18650, -9246, -25975, -11329, 17000, 24796, + 2642, -22703, -20627, 6363, 25667, 13969, -14601, -25536, + -5627, 21078, 22325, -3393, -25013, -16421, 12005, 25931, + 8536, -19169, -23721, 378, 24020, 18650, -9246, -25975, + -11329, 17000, 24796, 2642, -22703, -20627, 6363, 25667, + 13969, -14601, -25536, -5627, 21078, 22325, -3393, -25013, + -16421, 12005, 25931, 8536, -19169, -23721, 378, 24020, + 18650, -9246, -25975, -11329, 17000, 24796, 2642, -22703, + -20627, 6363, 25667, 13969, -14601, -25536, -5627, 21078, + 22325, -3393, -25013, -16421, 12005, 25931, 8536, -19169, + -23721, 378, 24020, 18650, -9246, -25975, -11329, 17000, + 24796, 2642, -22703, -20627, 6363, 25667, 13969, -14601, + -25536, -5627, 21078, 22325, -3393, -25013, -16421, 12005, + 25931, 8536, -19169, -23721, 378, 24020, 18650, -9246, + -25975, -11329, 17000, 24796, 2642, -22703, -20627, 6363, + 25667, 13969, -14601, -25536, -5627, 21078, 22325, -3393, + -25013, -16421, 12005, 25931, 8536, -19169, -23721, 378}, + { +// Carrier 34 Phase 4 + 26000, 10298, -17842, -24432, -1511, 23234, 19917, -7456, + -25824, -13000, 15526, 25299, 4514, -21722, -21722, 4514, + 25299, 15526, -12999, -25824, -7456, 19917, 23234, -1511, + -24432, -17842, 10298, 26000, 10298, -17842, -24432, -1511, + 23234, 19917, -7456, -25824, -13000, 15526, 25299, 4514, + -21722, -21722, 4514, 25299, 15526, -12999, -25824, -7456, + 19917, 23234, -1511, -24432, -17842, 10298, 26000, 10298, + -17842, -24432, -1511, 23234, 19917, -7456, -25824, -13000, + 15526, 25299, 4514, -21722, -21722, 4514, 25299, 15526, + -12999, -25824, -7456, 19917, 23234, -1511, -24431, -17842, + 10298, 26000, 10298, -17842, -24432, -1511, 23234, 19917, + -7456, -25824, -13000, 15526, 25299, 4514, -21722, -21722, + 4514, 25299, 15526, -12999, -25824, -7456, 19917, 23234, + -1511, -24431, -17842, 10298, 26000, 10298, -17842, -24432, + -1511, 23234, 19917, -7456, -25824, -13000, 15526, 25299, + 4514, -21722, -21722, 4514, 25299, 15526, -12999, -25824, + -7456, 19917, 23234, -1511, -24431, -17842, 10298, 26000, + 10298, -17842, -24432, -1511, 23234, 19917, -7456, -25824, + -13000, 15526, 25299, 4514, -21722, -21722, 4514, 25299, + 15526, -12999, -25824, -7456, 19917, 23234, -1511, -24431, + -17842, 10298, 26000, 10298, -17842, -24432, -1511, 23234, + 19917, -7456, -25824, -13000, 15526, 25299, 4514, -21722, + -21722, 4514, 25299, 15526, -12999, -25824, -7456, 19917, + 23234, -1511, -24431, -17842, 10297, 26000, 10298, -17842, + -24432, -1511, 23234, 19917, -7456, -25824, -13000, 15526, + 25299, 4514, -21722, -21722, 4514, 25299, 15526, -12999, + -25824, -7456, 19917, 23234, -1511, -24431, -17842, 10297}, + { +// Carrier 34 Phase 5 + 24020, 378, -23721, -19169, 8536, 25931, 12005, -16421, + -25013, -3393, 22325, 21078, -5627, -25536, -14601, 13969, + 25667, 6363, -20627, -22703, 2642, 24796, 17000, -11329, + -25975, -9246, 18650, 24020, 378, -23721, -19169, 8536, + 25931, 12005, -16421, -25013, -3393, 22325, 21078, -5627, + -25536, -14601, 13969, 25667, 6363, -20627, -22703, 2642, + 24796, 17000, -11329, -25975, -9246, 18650, 24020, 378, + -23721, -19169, 8536, 25931, 12005, -16421, -25013, -3393, + 22325, 21078, -5627, -25536, -14601, 13969, 25667, 6363, + -20627, -22703, 2642, 24796, 17000, -11329, -25975, -9246, + 18650, 24020, 378, -23721, -19169, 8536, 25931, 12005, + -16421, -25013, -3393, 22325, 21078, -5627, -25536, -14601, + 13969, 25667, 6363, -20627, -22703, 2642, 24796, 17000, + -11329, -25975, -9246, 18650, 24020, 378, -23721, -19169, + 8536, 25931, 12005, -16420, -25013, -3393, 22325, 21078, + -5627, -25536, -14601, 13969, 25667, 6363, -20627, -22703, + 2642, 24796, 17000, -11329, -25975, -9246, 18650, 24020, + 378, -23721, -19169, 8536, 25931, 12005, -16420, -25013, + -3393, 22325, 21078, -5627, -25536, -14601, 13969, 25667, + 6363, -20627, -22703, 2642, 24796, 17000, -11329, -25975, + -9246, 18650, 24020, 378, -23721, -19169, 8536, 25931, + 12005, -16420, -25013, -3393, 22325, 21078, -5627, -25536, + -14601, 13969, 25667, 6363, -20627, -22703, 2642, 24796, + 17000, -11329, -25975, -9247, 18650, 24020, 378, -23721, + -19169, 8536, 25931, 12005, -16420, -25013, -3393, 22325, + 21078, -5627, -25536, -14601, 13969, 25667, 6363, -20627, + -22703, 2642, 24796, 17000, -11329, -25975, -9247, 18650}, + { +// Carrier 34 Phase 6 + 18384, -9599, -25989, -10988, 17284, 24680, 2266, -22885, + -20394, 6729, 25725, 13649, -14912, -25462, -5257, 21297, + 22129, -3768, -25114, -16126, 12339, 25901, 8178, -19422, + -23564, 756, 24163, 18384, -9599, -25989, -10988, 17284, + 24680, 2266, -22885, -20394, 6729, 25725, 13649, -14912, + -25462, -5257, 21297, 22129, -3768, -25114, -16126, 12339, + 25901, 8178, -19422, -23564, 756, 24163, 18384, -9599, + -25989, -10988, 17284, 24680, 2266, -22885, -20394, 6729, + 25725, 13649, -14912, -25462, -5257, 21297, 22129, -3768, + -25114, -16126, 12339, 25901, 8178, -19422, -23564, 756, + 24163, 18384, -9599, -25989, -10988, 17284, 24680, 2266, + -22885, -20394, 6729, 25725, 13649, -14912, -25462, -5257, + 21297, 22129, -3768, -25114, -16126, 12339, 25901, 8178, + -19422, -23564, 756, 24163, 18384, -9599, -25988, -10988, + 17284, 24680, 2266, -22885, -20394, 6729, 25725, 13649, + -14912, -25462, -5257, 21297, 22129, -3768, -25114, -16126, + 12339, 25901, 8178, -19422, -23564, 756, 24163, 18384, + -9599, -25988, -10988, 17284, 24680, 2266, -22885, -20394, + 6729, 25725, 13649, -14912, -25462, -5257, 21297, 22129, + -3768, -25114, -16126, 12339, 25901, 8178, -19422, -23564, + 756, 24163, 18384, -9599, -25988, -10988, 17284, 24680, + 2266, -22885, -20394, 6729, 25725, 13649, -14912, -25462, + -5257, 21297, 22129, -3768, -25114, -16126, 12339, 25901, + 8178, -19422, -23564, 756, 24163, 18384, -9599, -25988, + -10988, 17284, 24680, 2266, -22885, -20394, 6729, 25725, + 13649, -14912, -25462, -5257, 21297, 22129, -3768, -25114, + -16126, 12339, 25901, 8178, -19422, -23564, 756, 24162}, + { +// Carrier 34 Phase 7 + 9949, -18115, -24300, -1134, 23401, 19671, -7818, -25865, + -12671, 15827, 25209, 4141, -21928, -21512, 4886, 25383, + 15221, -13326, -25777, -7093, 20158, 23062, -1889, -24558, + -17565, 10644, 25997, 9949, -18115, -24300, -1134, 23401, + 19671, -7818, -25865, -12671, 15827, 25209, 4141, -21928, + -21512, 4886, 25383, 15221, -13326, -25777, -7093, 20158, + 23062, -1889, -24558, -17565, 10644, 25997, 9949, -18115, + -24300, -1134, 23401, 19671, -7818, -25865, -12671, 15827, + 25209, 4142, -21928, -21512, 4886, 25383, 15221, -13326, + -25777, -7093, 20158, 23062, -1889, -24558, -17565, 10644, + 25997, 9949, -18115, -24300, -1134, 23401, 19672, -7818, + -25865, -12671, 15827, 25209, 4142, -21928, -21512, 4886, + 25383, 15221, -13326, -25777, -7093, 20158, 23062, -1889, + -24558, -17565, 10644, 25997, 9949, -18115, -24300, -1134, + 23401, 19672, -7818, -25865, -12671, 15827, 25209, 4142, + -21928, -21512, 4886, 25383, 15221, -13326, -25777, -7093, + 20158, 23062, -1889, -24558, -17565, 10644, 25997, 9949, + -18115, -24300, -1134, 23401, 19672, -7818, -25865, -12671, + 15827, 25209, 4142, -21928, -21512, 4886, 25383, 15221, + -13326, -25777, -7093, 20158, 23062, -1889, -24558, -17565, + 10644, 25997, 9949, -18115, -24300, -1134, 23401, 19672, + -7818, -25865, -12671, 15827, 25209, 4142, -21928, -21512, + 4886, 25383, 15221, -13326, -25777, -7093, 20158, 23062, + -1889, -24558, -17565, 10644, 25997, 9949, -18115, -24300, + -1134, 23401, 19672, -7818, -25865, -12671, 15827, 25209, + 4142, -21928, -21512, 4886, 25383, 15221, -13326, -25777, + -7093, 20158, 23062, -1889, -24558, -17565, 10644, 25997}, + },{{ + +// Carrier 35 Phase 0 + 0, 24163, 17842, -10988, -25956, -8178, 19917, 22885, + -3018, -25114, -15526, 13649, 25604, 5257, -21722, -21297, + 5996, 25725, 12999, -16126, -24907, -2266, 23234, 19422, + -8892, -25989, -10298, 18384, 23873, -756, -24432, -17284, + 11668, 25901, 7456, -20394, -22516, 3768, 25299, 14912, + -14287, -25462, -4514, 22129, 20855, -6729, -25824, -12339, + 16712, 24680, 1511, -23564, -18911, 9599, 26000, 9599, + -18911, -23563, 1511, 24680, 16712, -12339, -25824, -6729, + 20855, 22128, -4514, -25462, -14287, 14913, 25299, 3768, + -22516, -20394, 7457, 25901, 11668, -17284, -24431, -756, + 23873, 18384, -10298, -25988, -8892, 19422, 23234, -2266, + -24907, -16125, 13000, 25725, 5995, -21298, -21722, 5257, + 25605, 13649, -15526, -25114, -3018, 22885, 19917, -8178, + -25956, -10987, 17842, 24162, 0, -24163, -17842, 10988, + 25955, 8177, -19917, -22885, 3018, 25114, 15525, -13649, + -25604, -5257, 21722, 21297, -5996, -25725, -12999, 16126, + 24907, 2265, -23234, -19422, 8892, 25989, 10297, -18384, + -23873, 756, 24432, 17284, -11669, -25901, -7456, 20394, + 22516, -3768, -25299, -14912, 14287, 25462, 4514, -22129, + -20855, 6729, 25824, 12339, -16712, -24680, -1511, 23564, + 18911, -9599, -26000, -9599, 18911, 23563, -1512, -24680, + -16712, 12339, 25824, 6728, -20855, -22128, 4515, 25462, + 14286, -14913, -25299, -3767, 22516, 20394, -7457, -25901, + -11668, 17284, 24431, 755, -23873, -18384, 10298, 25988, + 8892, -19422, -23234, 2266, 24907, 16125, -13000, -25725, + -5995, 21298, 21722, -5258, -25605, -13649, 15526, 25113, + 3018, -22885, -19916, 8178, 25956, 10987, -17842, -24162}, + { +// Carrier 35 Phase 1 + 9949, 25997, 9246, -19169, -23401, 1889, 24796, 16421, + -12671, -25777, -6363, 21078, 21928, -4886, -25536, -13969, + 15221, 25209, 3393, -22703, -20158, 7818, 25931, 11329, + -17565, -24300, -378, 24020, 18115, -10644, -25975, -8536, + 19672, 23062, -2642, -25013, -15827, 13326, 25667, 5627, + -21512, -21512, 5627, 25667, 13326, -15827, -25013, -2642, + 23062, 19671, -8536, -25975, -10644, 18115, 24020, -378, + -24300, -17565, 11329, 25931, 7818, -20158, -22703, 3393, + 25209, 15221, -13969, -25536, -4886, 21928, 21078, -6363, + -25777, -12671, 16421, 24796, 1888, -23401, -19169, 9247, + 25997, 9949, -18650, -23721, 1134, 24558, 17000, -12005, + -25865, -7093, 20627, 22325, -4142, -25383, -14601, 14601, + 25383, 4141, -22325, -20627, 7094, 25865, 12005, -17000, + -24558, -1133, 23721, 18650, -9949, -25997, -9246, 19169, + 23401, -1889, -24796, -16420, 12671, 25777, 6363, -21078, + -21928, 4887, 25536, 13969, -15221, -25209, -3393, 22703, + 20157, -7818, -25931, -11329, 17565, 24299, 377, -24020, + -18115, 10644, 25975, 8535, -19672, -23062, 2642, 25013, + 15827, -13326, -25667, -5627, 21512, 21512, -5627, -25668, + -13325, 15828, 25013, 2642, -23062, -19671, 8536, 25975, + 10643, -18115, -24020, 378, 24300, 17565, -11329, -25931, + -7818, 20158, 22703, -3394, -25209, -15220, 13970, 25536, + 4886, -21928, -21078, 6363, 25777, 12670, -16421, -24796, + -1888, 23401, 19168, -9247, -25997, -9949, 18650, 23721, + -1134, -24558, -17000, 12005, 25865, 7093, -20627, -22325, + 4142, 25383, 14601, -14601, -25383, -4141, 22325, 20626, + -7094, -25865, -12005, 17000, 24558, 1133, -23721, -18649}, + { +// Carrier 35 Phase 2 + 18384, 23873, -756, -24432, -17284, 11668, 25901, 7456, + -20394, -22516, 3768, 25299, 14912, -14287, -25462, -4514, + 22129, 20855, -6729, -25824, -12339, 16712, 24680, 1511, + -23564, -18911, 9599, 26000, 9599, -18911, -23563, 1511, + 24680, 16712, -12339, -25824, -6729, 20855, 22128, -4514, + -25462, -14287, 14913, 25299, 3768, -22516, -20394, 7456, + 25901, 11668, -17284, -24431, -756, 23873, 18384, -10298, + -25988, -8892, 19422, 23234, -2266, -24907, -16126, 13000, + 25725, 5995, -21298, -21722, 5257, 25605, 13649, -15526, + -25114, -3018, 22885, 19917, -8178, -25956, -10987, 17842, + 24162, 0, -24163, -17842, 10988, 25956, 8178, -19917, + -22885, 3018, 25114, 15525, -13649, -25604, -5257, 21722, + 21297, -5996, -25725, -12999, 16126, 24907, 2265, -23234, + -19422, 8892, 25989, 10297, -18384, -23873, 756, 24432, + 17284, -11668, -25901, -7456, 20394, 22516, -3768, -25299, + -14912, 14287, 25462, 4514, -22129, -20855, 6729, 25824, + 12339, -16712, -24680, -1511, 23564, 18911, -9599, -26000, + -9599, 18911, 23563, -1512, -24680, -16712, 12339, 25824, + 6729, -20855, -22128, 4515, 25462, 14286, -14913, -25299, + -3767, 22516, 20394, -7457, -25901, -11668, 17284, 24431, + 755, -23873, -18384, 10298, 25988, 8892, -19422, -23234, + 2266, 24907, 16125, -13000, -25725, -5995, 21298, 21722, + -5257, -25605, -13649, 15526, 25113, 3018, -22885, -19916, + 8178, 25956, 10987, -17842, -24162, 0, 24163, 17842, + -10988, -25955, -8177, 19917, 22885, -3018, -25114, -15525, + 13649, 25604, 5257, -21722, -21297, 5996, 25725, 12999, + -16126, -24907, -2265, 23234, 19422, -8892, -25989, -10297}, + { +// Carrier 35 Phase 3 + 24020, 18115, -10644, -25975, -8536, 19671, 23062, -2642, + -25013, -15827, 13326, 25667, 5627, -21512, -21512, 5627, + 25667, 13326, -15827, -25013, -2642, 23062, 19671, -8536, + -25975, -10644, 18115, 24020, -378, -24300, -17565, 11329, + 25931, 7818, -20158, -22703, 3393, 25209, 15221, -13969, + -25536, -4886, 21928, 21078, -6363, -25777, -12671, 16421, + 24796, 1889, -23401, -19169, 9247, 25997, 9949, -18650, + -23721, 1134, 24558, 17000, -12005, -25865, -7093, 20627, + 22325, -4142, -25383, -14601, 14601, 25383, 4141, -22325, + -20627, 7093, 25865, 12005, -17000, -24558, -1133, 23721, + 18650, -9949, -25997, -9246, 19169, 23401, -1889, -24796, + -16420, 12671, 25777, 6363, -21078, -21928, 4886, 25536, + 13969, -15221, -25209, -3393, 22703, 20157, -7818, -25931, + -11329, 17565, 24300, 377, -24020, -18115, 10644, 25975, + 8536, -19672, -23062, 2642, 25013, 15827, -13326, -25667, + -5627, 21512, 21512, -5627, -25667, -13325, 15828, 25013, + 2642, -23062, -19671, 8536, 25975, 10643, -18115, -24020, + 378, 24300, 17565, -11329, -25931, -7818, 20158, 22703, + -3393, -25209, -15220, 13970, 25536, 4886, -21928, -21078, + 6363, 25777, 12670, -16421, -24796, -1888, 23401, 19168, + -9247, -25997, -9949, 18650, 23721, -1134, -24558, -17000, + 12005, 25865, 7093, -20627, -22325, 4142, 25383, 14601, + -14601, -25383, -4141, 22325, 20626, -7094, -25865, -12005, + 17000, 24558, 1133, -23721, -18649, 9950, 25997, 9246, + -19169, -23401, 1889, 24796, 16420, -12671, -25777, -6362, + 21079, 21927, -4887, -25536, -13969, 15221, 25209, 3393, + -22703, -20157, 7818, 25931, 11329, -17565, -24299, -377}, + { +// Carrier 35 Phase 4 + 26000, 9599, -18911, -23564, 1511, 24680, 16712, -12339, + -25824, -6729, 20855, 22129, -4514, -25462, -14287, 14913, + 25299, 3768, -22516, -20394, 7456, 25901, 11668, -17284, + -24431, -756, 23873, 18384, -10298, -25988, -8892, 19422, + 23234, -2266, -24907, -16126, 13000, 25725, 5995, -21297, + -21722, 5257, 25605, 13649, -15526, -25114, -3018, 22885, + 19917, -8178, -25956, -10987, 17842, 24162, 0, -24163, + -17842, 10988, 25956, 8178, -19917, -22885, 3018, 25114, + 15526, -13649, -25604, -5257, 21722, 21297, -5996, -25725, + -12999, 16126, 24907, 2265, -23234, -19422, 8892, 25989, + 10297, -18384, -23873, 756, 24432, 17284, -11668, -25901, + -7456, 20394, 22516, -3768, -25299, -14912, 14287, 25462, + 4514, -22129, -20855, 6729, 25824, 12339, -16712, -24680, + -1511, 23564, 18911, -9599, -26000, -9599, 18911, 23563, + -1511, -24680, -16712, 12339, 25824, 6729, -20855, -22128, + 4515, 25462, 14287, -14913, -25299, -3767, 22516, 20394, + -7457, -25901, -11668, 17284, 24431, 755, -23873, -18384, + 10298, 25988, 8892, -19422, -23234, 2266, 24907, 16125, + -13000, -25725, -5995, 21298, 21722, -5257, -25605, -13649, + 15526, 25113, 3018, -22885, -19916, 8178, 25956, 10987, + -17842, -24162, 0, 24163, 17842, -10988, -25955, -8177, + 19917, 22885, -3018, -25114, -15525, 13649, 25604, 5257, + -21722, -21297, 5996, 25725, 12999, -16126, -24907, -2265, + 23234, 19422, -8892, -25989, -10297, 18385, 23873, -756, + -24432, -17284, 11669, 25901, 7456, -20395, -22516, 3768, + 25299, 14912, -14287, -25462, -4514, 22129, 20854, -6729, + -25824, -12339, 16712, 24680, 1511, -23564, -18911, 9599}, + { +// Carrier 35 Phase 5 + 24020, -378, -24300, -17565, 11329, 25931, 7818, -20158, + -22703, 3393, 25209, 15221, -13969, -25536, -4886, 21928, + 21078, -6363, -25777, -12671, 16421, 24796, 1889, -23401, + -19169, 9246, 25997, 9949, -18650, -23721, 1134, 24558, + 17000, -12005, -25865, -7093, 20627, 22325, -4142, -25383, + -14601, 14601, 25383, 4141, -22325, -20627, 7093, 25865, + 12005, -17000, -24558, -1134, 23721, 18650, -9949, -25997, + -9246, 19169, 23401, -1889, -24796, -16420, 12671, 25777, + 6363, -21078, -21928, 4886, 25536, 13969, -15221, -25209, + -3393, 22703, 20158, -7818, -25931, -11329, 17565, 24300, + 377, -24020, -18115, 10644, 25975, 8536, -19672, -23062, + 2642, 25013, 15827, -13326, -25667, -5627, 21512, 21512, + -5627, -25667, -13325, 15827, 25013, 2642, -23062, -19671, + 8536, 25975, 10644, -18115, -24020, 378, 24300, 17565, + -11329, -25931, -7818, 20158, 22703, -3393, -25209, -15220, + 13969, 25536, 4886, -21928, -21078, 6363, 25777, 12670, + -16421, -24796, -1888, 23401, 19169, -9247, -25997, -9949, + 18650, 23721, -1134, -24558, -17000, 12005, 25865, 7093, + -20627, -22325, 4142, 25383, 14601, -14601, -25383, -4141, + 22325, 20627, -7094, -25865, -12005, 17000, 24558, 1133, + -23721, -18650, 9950, 25997, 9246, -19169, -23401, 1889, + 24796, 16420, -12671, -25777, -6363, 21078, 21928, -4887, + -25536, -13969, 15221, 25209, 3393, -22703, -20157, 7818, + 25931, 11329, -17565, -24299, -377, 24021, 18115, -10644, + -25975, -8535, 19672, 23062, -2642, -25013, -15827, 13326, + 25667, 5627, -21512, -21512, 5627, 25668, 13325, -15828, + -25013, -2642, 23062, 19671, -8536, -25975, -10643, 18115}, + { +// Carrier 35 Phase 6 + 18384, -10298, -25989, -8892, 19422, 23234, -2266, -24907, + -16126, 13000, 25725, 5995, -21297, -21722, 5257, 25605, + 13649, -15526, -25114, -3018, 22885, 19917, -8178, -25956, + -10988, 17842, 24163, 0, -24163, -17842, 10988, 25956, + 8178, -19917, -22885, 3018, 25114, 15526, -13649, -25604, + -5257, 21722, 21297, -5996, -25725, -12999, 16126, 24907, + 2265, -23234, -19422, 8892, 25989, 10297, -18384, -23873, + 756, 24432, 17284, -11668, -25901, -7456, 20394, 22516, + -3768, -25299, -14912, 14287, 25462, 4514, -22129, -20855, + 6729, 25824, 12339, -16712, -24680, -1511, 23564, 18911, + -9599, -26000, -9599, 18911, 23563, -1511, -24680, -16712, + 12339, 25824, 6729, -20855, -22128, 4515, 25462, 14287, + -14913, -25299, -3768, 22516, 20394, -7457, -25901, -11668, + 17284, 24431, 755, -23873, -18384, 10298, 25988, 8892, + -19422, -23234, 2266, 24907, 16125, -13000, -25725, -5995, + 21298, 21722, -5257, -25605, -13649, 15526, 25114, 3018, + -22885, -19916, 8178, 25956, 10987, -17842, -24162, 0, + 24163, 17842, -10988, -25955, -8177, 19917, 22885, -3018, + -25114, -15525, 13649, 25604, 5257, -21722, -21297, 5996, + 25725, 12999, -16126, -24907, -2265, 23234, 19422, -8892, + -25989, -10297, 18385, 23873, -756, -24432, -17284, 11669, + 25901, 7456, -20395, -22516, 3768, 25299, 14912, -14287, + -25462, -4514, 22129, 20854, -6729, -25824, -12339, 16712, + 24680, 1511, -23564, -18911, 9599, 26000, 9599, -18911, + -23563, 1512, 24680, 16712, -12339, -25824, -6728, 20855, + 22128, -4515, -25462, -14286, 14913, 25299, 3767, -22516, + -20394, 7457, 25901, 11668, -17284, -24431, -755, 23873}, + { +// Carrier 35 Phase 7 + 9949, -18650, -23721, 1134, 24558, 17000, -12005, -25865, + -7093, 20627, 22325, -4141, -25383, -14601, 14601, 25383, + 4141, -22325, -20627, 7093, 25865, 12005, -17000, -24558, + -1134, 23721, 18650, -9949, -25997, -9246, 19169, 23401, + -1889, -24796, -16420, 12671, 25777, 6363, -21078, -21928, + 4886, 25536, 13969, -15221, -25209, -3393, 22703, 20158, + -7818, -25931, -11329, 17565, 24300, 378, -24020, -18115, + 10644, 25975, 8536, -19672, -23062, 2642, 25013, 15827, + -13326, -25667, -5627, 21512, 21512, -5627, -25667, -13325, + 15827, 25013, 2642, -23062, -19671, 8536, 25975, 10644, + -18115, -24020, 378, 24300, 17565, -11329, -25931, -7818, + 20158, 22703, -3393, -25209, -15221, 13969, 25536, 4886, + -21928, -21078, 6363, 25777, 12670, -16421, -24796, -1888, + 23401, 19169, -9247, -25997, -9949, 18650, 23721, -1134, + -24558, -17000, 12005, 25865, 7093, -20627, -22325, 4142, + 25383, 14601, -14601, -25383, -4141, 22325, 20627, -7094, + -25865, -12005, 17000, 24558, 1133, -23721, -18650, 9950, + 25997, 9246, -19169, -23401, 1889, 24796, 16420, -12671, + -25777, -6363, 21078, 21928, -4887, -25536, -13969, 15221, + 25209, 3393, -22703, -20157, 7818, 25931, 11329, -17565, + -24299, -377, 24020, 18115, -10644, -25975, -8535, 19672, + 23062, -2642, -25013, -15827, 13326, 25667, 5627, -21512, + -21512, 5627, 25668, 13325, -15828, -25013, -2642, 23062, + 19671, -8536, -25975, -10643, 18115, 24020, -378, -24300, + -17565, 11329, 25931, 7817, -20158, -22703, 3394, 25209, + 15220, -13970, -25536, -4886, 21928, 21078, -6363, -25777, + -12670, 16421, 24796, 1888, -23401, -19168, 9247, 25997}, + },{{ + +// Carrier 36 Phase 0 + 0, 24432, 16712, -12999, -25605, -4514, 22516, 19917, + -8892, -26000, -8892, 19917, 22516, -4514, -25604, -13000, + 16712, 24432, 0, -24431, -16712, 12999, 25605, 4514, + -22516, -19917, 8892, 26000, 8892, -19917, -22516, 4514, + 25604, 13000, -16712, -24432, 0, 24431, 16712, -12999, + -25605, -4514, 22516, 19917, -8892, -26000, -8892, 19917, + 22516, -4514, -25604, -13000, 16712, 24432, 0, -24431, + -16712, 12999, 25605, 4515, -22516, -19917, 8892, 26000, + 8892, -19917, -22516, 4514, 25604, 13000, -16712, -24432, + 0, 24431, 16712, -12999, -25605, -4515, 22516, 19917, + -8892, -26000, -8892, 19916, 22516, -4514, -25604, -13000, + 16712, 24432, 0, -24431, -16712, 12999, 25605, 4515, + -22516, -19917, 8892, 26000, 8892, -19916, -22516, 4514, + 25604, 13000, -16712, -24432, 0, 24431, 16712, -12999, + -25605, -4515, 22516, 19917, -8892, -26000, -8892, 19916, + 22516, -4514, -25604, -13000, 16712, 24432, 0, -24431, + -16712, 12999, 25605, 4515, -22516, -19917, 8892, 26000, + 8892, -19916, -22516, 4514, 25604, 13000, -16712, -24432, + 0, 24431, 16712, -12999, -25605, -4515, 22516, 19917, + -8892, -26000, -8893, 19916, 22516, -4514, -25604, -13000, + 16712, 24432, 0, -24431, -16712, 12999, 25605, 4515, + -22516, -19917, 8891, 26000, 8893, -19916, -22516, 4514, + 25604, 13000, -16712, -24432, 0, 24431, 16712, -12999, + -25605, -4515, 22516, 19917, -8891, -26000, -8893, 19916, + 22516, -4514, -25604, -13000, 16711, 24432, 0, -24431, + -16713, 12999, 25605, 4515, -22516, -19917, 8891, 26000, + 8893, -19916, -22517, 4514, 25604, 13000, -16711, -24432}, + { +// Carrier 36 Phase 1 + 9949, 25975, 7818, -20627, -21928, 5627, 25777, 12005, + -17565, -24020, 1134, 24796, 15827, -13969, -25383, -3393, + 23062, 19169, -9949, -25975, -7818, 20627, 21928, -5627, + -25777, -12005, 17565, 24020, -1134, -24796, -15827, 13969, + 25383, 3393, -23062, -19169, 9949, 25975, 7818, -20627, + -21928, 5627, 25777, 12005, -17565, -24020, 1133, 24796, + 15827, -13969, -25383, -3393, 23062, 19169, -9949, -25975, + -7818, 20627, 21928, -5627, -25777, -12005, 17565, 24020, + -1133, -24796, -15827, 13969, 25383, 3393, -23062, -19169, + 9949, 25975, 7818, -20627, -21928, 5627, 25777, 12005, + -17565, -24020, 1133, 24796, 15828, -13969, -25383, -3393, + 23062, 19169, -9949, -25975, -7818, 20627, 21928, -5627, + -25777, -12005, 17565, 24020, -1133, -24796, -15828, 13969, + 25383, 3394, -23062, -19169, 9949, 25975, 7818, -20626, + -21928, 5627, 25777, 12005, -17565, -24021, 1133, 24796, + 15828, -13969, -25383, -3394, 23062, 19169, -9949, -25975, + -7818, 20626, 21928, -5627, -25777, -12005, 17565, 24021, + -1133, -24796, -15828, 13969, 25383, 3394, -23062, -19169, + 9949, 25975, 7818, -20626, -21928, 5626, 25777, 12005, + -17564, -24021, 1133, 24796, 15828, -13969, -25383, -3394, + 23062, 19169, -9949, -25975, -7818, 20626, 21928, -5626, + -25777, -12005, 17564, 24021, -1133, -24796, -15828, 13969, + 25383, 3394, -23062, -19169, 9949, 25975, 7818, -20626, + -21928, 5626, 25777, 12005, -17564, -24021, 1133, 24796, + 15828, -13969, -25383, -3394, 23062, 19169, -9949, -25975, + -7818, 20626, 21928, -5626, -25777, -12006, 17564, 24021, + -1133, -24796, -15828, 13969, 25383, 3394, -23061, -19169}, + { +// Carrier 36 Phase 2 + 18384, 23564, -2266, -25114, -14913, 14912, 25114, 2266, + -23563, -18384, 10988, 25901, 6729, -21297, -21297, 6729, + 25901, 10988, -18384, -23564, 2265, 25114, 14913, -14912, + -25114, -2266, 23563, 18384, -10988, -25901, -6729, 21297, + 21298, -6729, -25901, -10988, 18384, 23564, -2265, -25114, + -14913, 14912, 25114, 2266, -23563, -18384, 10987, 25901, + 6729, -21297, -21298, 6729, 25901, 10988, -18384, -23564, + 2265, 25114, 14913, -14912, -25114, -2266, 23563, 18384, + -10987, -25901, -6729, 21297, 21298, -6729, -25901, -10988, + 18384, 23564, -2265, -25114, -14913, 14912, 25114, 2266, + -23563, -18384, 10987, 25901, 6729, -21297, -21298, 6729, + 25901, 10988, -18384, -23564, 2265, 25113, 14913, -14912, + -25114, -2266, 23563, 18385, -10987, -25901, -6729, 21297, + 21298, -6728, -25901, -10988, 18384, 23564, -2265, -25113, + -14913, 14912, 25114, 2266, -23563, -18385, 10987, 25901, + 6729, -21297, -21298, 6728, 25901, 10988, -18384, -23564, + 2265, 25113, 14913, -14912, -25114, -2266, 23563, 18385, + -10987, -25901, -6729, 21297, 21298, -6728, -25901, -10988, + 18384, 23564, -2265, -25113, -14913, 14912, 25114, 2266, + -23563, -18385, 10987, 25901, 6729, -21297, -21298, 6728, + 25901, 10988, -18384, -23564, 2265, 25113, 14913, -14912, + -25114, -2266, 23563, 18385, -10987, -25901, -6729, 21297, + 21298, -6728, -25901, -10988, 18384, 23564, -2265, -25113, + -14913, 14912, 25114, 2266, -23563, -18385, 10987, 25901, + 6729, -21297, -21298, 6728, 25901, 10988, -18384, -23564, + 2265, 25113, 14913, -14912, -25114, -2266, 23563, 18385, + -10987, -25901, -6729, 21297, 21298, -6728, -25900, -10988}, + { +// Carrier 36 Phase 3 + 24020, 17565, -12005, -25777, -5627, 21928, 20627, -7818, + -25975, -9949, 19169, 23062, -3393, -25383, -13969, 15827, + 24796, 1134, -24020, -17565, 12005, 25777, 5627, -21928, + -20627, 7818, 25975, 9949, -19169, -23062, 3393, 25383, + 13969, -15827, -24796, -1134, 24020, 17565, -12005, -25777, + -5627, 21928, 20627, -7818, -25975, -9949, 19169, 23062, + -3393, -25383, -13969, 15827, 24796, 1134, -24020, -17565, + 12005, 25777, 5627, -21928, -20627, 7818, 25975, 9949, + -19169, -23062, 3393, 25383, 13969, -15827, -24796, -1134, + 24020, 17565, -12005, -25777, -5627, 21928, 20627, -7818, + -25975, -9950, 19169, 23062, -3393, -25383, -13970, 15827, + 24796, 1134, -24020, -17565, 12005, 25777, 5627, -21927, + -20627, 7818, 25975, 9950, -19168, -23062, 3393, 25383, + 13970, -15827, -24796, -1134, 24020, 17565, -12005, -25777, + -5627, 21927, 20627, -7817, -25975, -9950, 19168, 23062, + -3393, -25383, -13970, 15827, 24796, 1134, -24020, -17565, + 12005, 25777, 5627, -21927, -20627, 7817, 25975, 9950, + -19168, -23062, 3393, 25383, 13970, -15827, -24796, -1134, + 24020, 17565, -12005, -25777, -5627, 21927, 20627, -7817, + -25975, -9950, 19168, 23062, -3393, -25383, -13970, 15827, + 24796, 1134, -24020, -17565, 12004, 25777, 5628, -21927, + -20627, 7817, 25975, 9950, -19168, -23062, 3393, 25383, + 13970, -15827, -24796, -1134, 24020, 17565, -12004, -25777, + -5628, 21927, 20627, -7817, -25975, -9950, 19168, 23062, + -3393, -25383, -13970, 15827, 24796, 1134, -24020, -17565, + 12004, 25777, 5628, -21927, -20627, 7817, 25975, 9950, + -19168, -23062, 3392, 25383, 13970, -15827, -24796, -1134}, + { +// Carrier 36 Phase 4 + 26000, 8892, -19917, -22516, 4514, 25604, 13000, -16712, + -24432, 0, 24431, 16712, -12999, -25605, -4514, 22516, + 19917, -8892, -26000, -8892, 19917, 22516, -4514, -25604, + -13000, 16712, 24432, 0, -24431, -16712, 12999, 25605, + 4514, -22516, -19917, 8892, 26000, 8892, -19917, -22516, + 4514, 25604, 13000, -16712, -24432, 0, 24431, 16712, + -12999, -25605, -4515, 22516, 19917, -8892, -26000, -8892, + 19917, 22516, -4514, -25604, -13000, 16712, 24432, 0, + -24431, -16712, 12999, 25605, 4515, -22516, -19917, 8892, + 26000, 8892, -19916, -22516, 4514, 25604, 13000, -16712, + -24432, 0, 24431, 16712, -12999, -25605, -4515, 22516, + 19917, -8892, -26000, -8892, 19916, 22516, -4514, -25604, + -13000, 16712, 24432, 0, -24431, -16712, 12999, 25605, + 4515, -22516, -19917, 8892, 26000, 8892, -19916, -22516, + 4514, 25604, 13000, -16712, -24432, 0, 24431, 16712, + -12999, -25605, -4515, 22516, 19917, -8892, -26000, -8892, + 19916, 22516, -4514, -25604, -13000, 16712, 24432, 0, + -24431, -16712, 12999, 25605, 4515, -22516, -19917, 8892, + 26000, 8892, -19916, -22516, 4514, 25604, 13000, -16712, + -24432, 0, 24431, 16712, -12999, -25605, -4515, 22516, + 19917, -8891, -26000, -8893, 19916, 22516, -4514, -25604, + -13000, 16712, 24432, 0, -24431, -16712, 12999, 25605, + 4515, -22516, -19917, 8891, 26000, 8893, -19916, -22516, + 4514, 25604, 13000, -16711, -24432, 0, 24431, 16712, + -12999, -25605, -4515, 22516, 19917, -8891, -26000, -8893, + 19916, 22517, -4514, -25604, -13000, 16711, 24432, 0, + -24431, -16713, 12999, 25605, 4515, -22516, -19917, 8891}, + { +// Carrier 36 Phase 5 + 24020, -1134, -24796, -15827, 13969, 25383, 3393, -23062, + -19169, 9949, 25975, 7818, -20627, -21928, 5627, 25777, + 12005, -17565, -24020, 1134, 24796, 15827, -13969, -25383, + -3393, 23062, 19169, -9949, -25975, -7818, 20627, 21928, + -5627, -25777, -12005, 17565, 24020, -1133, -24796, -15827, + 13969, 25383, 3393, -23062, -19169, 9949, 25975, 7818, + -20627, -21928, 5627, 25777, 12005, -17565, -24020, 1133, + 24796, 15827, -13969, -25383, -3393, 23062, 19169, -9949, + -25975, -7818, 20627, 21928, -5627, -25777, -12005, 17565, + 24020, -1133, -24796, -15827, 13969, 25383, 3393, -23062, + -19169, 9949, 25975, 7818, -20627, -21928, 5627, 25777, + 12005, -17565, -24020, 1133, 24796, 15828, -13969, -25383, + -3393, 23062, 19169, -9949, -25975, -7818, 20626, 21928, + -5627, -25777, -12005, 17565, 24021, -1133, -24796, -15828, + 13969, 25383, 3394, -23062, -19169, 9949, 25975, 7818, + -20626, -21928, 5627, 25777, 12005, -17565, -24021, 1133, + 24796, 15828, -13969, -25383, -3394, 23062, 19169, -9949, + -25975, -7818, 20626, 21928, -5626, -25777, -12005, 17565, + 24021, -1133, -24796, -15828, 13969, 25383, 3394, -23062, + -19169, 9949, 25975, 7818, -20626, -21928, 5626, 25777, + 12005, -17564, -24021, 1133, 24796, 15828, -13969, -25383, + -3394, 23062, 19169, -9949, -25975, -7818, 20626, 21928, + -5626, -25777, -12005, 17564, 24021, -1133, -24796, -15828, + 13969, 25383, 3394, -23062, -19169, 9949, 25975, 7818, + -20626, -21928, 5626, 25777, 12006, -17564, -24021, 1133, + 24796, 15828, -13969, -25383, -3394, 23061, 19169, -9949, + -25975, -7818, 20626, 21928, -5626, -25777, -12006, 17564}, + { +// Carrier 36 Phase 6 + 18384, -10988, -25901, -6729, 21297, 21297, -6729, -25901, + -10988, 18384, 23564, -2265, -25114, -14913, 14912, 25114, + 2266, -23563, -18384, 10988, 25901, 6729, -21297, -21298, + 6729, 25901, 10988, -18384, -23564, 2265, 25114, 14913, + -14912, -25114, -2266, 23563, 18384, -10987, -25901, -6729, + 21297, 21298, -6729, -25901, -10988, 18384, 23564, -2265, + -25114, -14913, 14912, 25114, 2266, -23563, -18384, 10987, + 25901, 6729, -21297, -21298, 6729, 25901, 10988, -18384, + -23564, 2265, 25114, 14913, -14912, -25114, -2266, 23563, + 18384, -10987, -25901, -6729, 21297, 21298, -6729, -25901, + -10988, 18384, 23564, -2265, -25113, -14913, 14912, 25114, + 2266, -23563, -18385, 10987, 25901, 6729, -21297, -21298, + 6728, 25901, 10988, -18384, -23564, 2265, 25113, 14913, + -14912, -25114, -2266, 23563, 18385, -10987, -25901, -6729, + 21297, 21298, -6728, -25901, -10988, 18384, 23564, -2265, + -25113, -14913, 14912, 25114, 2266, -23563, -18385, 10987, + 25901, 6729, -21297, -21298, 6728, 25901, 10988, -18384, + -23564, 2265, 25113, 14913, -14912, -25114, -2266, 23563, + 18385, -10987, -25901, -6729, 21297, 21298, -6728, -25901, + -10988, 18384, 23564, -2265, -25113, -14913, 14912, 25114, + 2266, -23563, -18385, 10987, 25901, 6729, -21297, -21298, + 6728, 25901, 10988, -18384, -23564, 2265, 25113, 14913, + -14912, -25114, -2266, 23563, 18385, -10987, -25901, -6729, + 21297, 21298, -6728, -25901, -10988, 18384, 23564, -2265, + -25113, -14913, 14912, 25114, 2266, -23563, -18385, 10987, + 25901, 6729, -21297, -21298, 6728, 25901, 10988, -18384, + -23564, 2265, 25113, 14913, -14912, -25114, -2266, 23563}, + { +// Carrier 36 Phase 7 + 9949, -19169, -23062, 3393, 25383, 13969, -15827, -24796, + -1134, 24020, 17565, -12005, -25777, -5627, 21928, 20627, + -7818, -25975, -9949, 19169, 23062, -3393, -25383, -13969, + 15827, 24796, 1134, -24020, -17565, 12005, 25777, 5627, + -21928, -20627, 7818, 25975, 9949, -19169, -23062, 3393, + 25383, 13969, -15827, -24796, -1134, 24020, 17565, -12005, + -25777, -5627, 21928, 20627, -7818, -25975, -9949, 19169, + 23062, -3393, -25383, -13969, 15827, 24796, 1134, -24020, + -17565, 12005, 25777, 5627, -21928, -20627, 7818, 25975, + 9949, -19169, -23062, 3393, 25383, 13970, -15827, -24796, + -1134, 24020, 17565, -12005, -25777, -5627, 21928, 20627, + -7818, -25975, -9950, 19168, 23062, -3393, -25383, -13970, + 15827, 24796, 1134, -24020, -17565, 12005, 25777, 5627, + -21927, -20627, 7818, 25975, 9950, -19168, -23062, 3393, + 25383, 13970, -15827, -24796, -1134, 24020, 17565, -12005, + -25777, -5627, 21927, 20627, -7817, -25975, -9950, 19168, + 23062, -3393, -25383, -13970, 15827, 24796, 1134, -24020, + -17565, 12005, 25777, 5627, -21927, -20627, 7817, 25975, + 9950, -19168, -23062, 3393, 25383, 13970, -15827, -24796, + -1134, 24020, 17565, -12004, -25777, -5627, 21927, 20627, + -7817, -25975, -9950, 19168, 23062, -3393, -25383, -13970, + 15827, 24796, 1134, -24020, -17565, 12004, 25777, 5628, + -21927, -20627, 7817, 25975, 9950, -19168, -23062, 3393, + 25383, 13970, -15827, -24796, -1134, 24020, 17565, -12004, + -25777, -5628, 21927, 20627, -7817, -25975, -9950, 19168, + 23062, -3392, -25383, -13970, 15827, 24796, 1134, -24020, + -17565, 12004, 25777, 5628, -21927, -20627, 7817, 25975}, + },{{ + +// Carrier 37 Phase 0 + 0, 24680, 15526, -14912, -24907, -756, 24432, 16126, + -14287, -25114, -1511, 24163, 16712, -13649, -25299, -2266, + 23873, 17284, -12999, -25462, -3018, 23563, 17842, -12339, + -25605, -3768, 23234, 18384, -11668, -25725, -4514, 22885, + 18911, -10988, -25824, -5257, 22516, 19422, -10298, -25901, + -5996, 22129, 19917, -9599, -25956, -6729, 21722, 20394, + -8892, -25989, -7456, 21297, 20855, -8178, -26000, -8178, + 20855, 21297, -7456, -25988, -8892, 20394, 21722, -6729, + -25956, -9599, 19917, 22129, -5995, -25901, -10298, 19422, + 22516, -5257, -25824, -10988, 18911, 22885, -4514, -25725, + -11668, 18384, 23234, -3768, -25604, -12339, 17842, 23564, + -3018, -25462, -13000, 17284, 23873, -2265, -25299, -13649, + 16712, 24163, -1511, -25114, -14287, 16126, 24432, -756, + -24907, -14913, 15526, 24680, 0, -24680, -15526, 14912, + 24907, 756, -24431, -16126, 14287, 25114, 1511, -24162, + -16712, 13649, 25299, 2266, -23873, -17284, 12999, 25462, + 3018, -23563, -17842, 12339, 25605, 3768, -23234, -18384, + 11668, 25725, 4514, -22885, -18911, 10987, 25824, 5257, + -22516, -19422, 10297, 25901, 5996, -22128, -19917, 9599, + 25956, 6729, -21722, -20394, 8892, 25989, 7457, -21297, + -20855, 8178, 26000, 8178, -20855, -21298, 7456, 25988, + 8892, -20394, -21722, 6729, 25956, 9599, -19917, -22129, + 5995, 25901, 10298, -19422, -22516, 5257, 25824, 10988, + -18911, -22885, 4514, 25725, 11668, -18384, -23234, 3768, + 25604, 12339, -17842, -23564, 3018, 25462, 13000, -17284, + -23873, 2265, 25299, 13649, -16712, -24163, 1511, 25114, + 14287, -16125, -24432, 756, 24907, 14913, -15525, -24680}, + { +// Carrier 37 Phase 1 + 9949, 25931, 6363, -21928, -20158, 9246, 25975, 7093, + -21512, -20627, 8536, 25997, 7818, -21078, -21078, 7818, + 25997, 8536, -20627, -21512, 7093, 25975, 9246, -20158, + -21928, 6363, 25931, 9949, -19671, -22325, 5627, 25865, + 10644, -19169, -22703, 4886, 25777, 11329, -18650, -23062, + 4141, 25667, 12005, -18115, -23401, 3393, 25536, 12671, + -17565, -23721, 2642, 25383, 13326, -17000, -24020, 1889, + 25209, 13969, -16421, -24300, 1134, 25013, 14601, -15827, + -24558, 378, 24796, 15221, -15221, -24796, -378, 24558, + 15827, -14601, -25013, -1134, 24300, 16421, -13969, -25209, + -1889, 24020, 17000, -13326, -25383, -2642, 23721, 17565, + -12671, -25536, -3393, 23401, 18115, -12005, -25667, -4142, + 23062, 18650, -11329, -25777, -4886, 22703, 19169, -10644, + -25865, -5627, 22325, 19672, -9949, -25931, -6363, 21928, + 20158, -9246, -25975, -7093, 21512, 20627, -8536, -25997, + -7818, 21078, 21078, -7818, -25997, -8536, 20627, 21512, + -7093, -25975, -9247, 20158, 21928, -6363, -25931, -9949, + 19671, 22325, -5627, -25865, -10644, 19169, 22703, -4886, + -25777, -11329, 18650, 23062, -4141, -25667, -12005, 18115, + 23401, -3393, -25536, -12671, 17565, 23721, -2642, -25383, + -13326, 17000, 24020, -1888, -25209, -13969, 16420, 24300, + -1133, -25013, -14601, 15827, 24558, -378, -24796, -15221, + 15221, 24796, 378, -24558, -15827, 14601, 25013, 1134, + -24300, -16421, 13969, 25209, 1889, -24020, -17000, 13325, + 25383, 2642, -23721, -17565, 12671, 25536, 3393, -23401, + -18115, 12005, 25667, 4142, -23062, -18650, 11329, 25777, + 4886, -22703, -19169, 10644, 25865, 5627, -22325, -19672}, + { +// Carrier 37 Phase 2 + 18384, 23234, -3768, -25604, -12339, 17842, 23564, -3018, + -25462, -13000, 17284, 23873, -2266, -25299, -13649, 16712, + 24163, -1511, -25114, -14287, 16126, 24432, -756, -24907, + -14913, 15526, 24680, 0, -24680, -15526, 14912, 24907, + 756, -24431, -16126, 14287, 25114, 1511, -24163, -16712, + 13649, 25299, 2266, -23873, -17284, 12999, 25462, 3018, + -23563, -17842, 12339, 25605, 3768, -23234, -18384, 11668, + 25725, 4514, -22885, -18911, 10988, 25824, 5257, -22516, + -19422, 10298, 25901, 5996, -22129, -19917, 9599, 25956, + 6729, -21722, -20394, 8892, 25989, 7456, -21297, -20855, + 8178, 26000, 8178, -20855, -21297, 7456, 25988, 8892, + -20394, -21722, 6729, 25956, 9599, -19917, -22129, 5995, + 25901, 10298, -19422, -22516, 5257, 25824, 10988, -18911, + -22885, 4514, 25725, 11668, -18384, -23234, 3768, 25604, + 12339, -17842, -23564, 3018, 25462, 13000, -17284, -23873, + 2265, 25299, 13649, -16712, -24163, 1511, 25114, 14287, + -16126, -24432, 756, 24907, 14913, -15526, -24680, 0, + 24680, 15526, -14912, -24907, -756, 24431, 16126, -14287, + -25114, -1511, 24162, 16712, -13649, -25299, -2266, 23873, + 17284, -12999, -25462, -3018, 23563, 17842, -12339, -25605, + -3768, 23234, 18384, -11668, -25725, -4514, 22885, 18911, + -10987, -25824, -5257, 22516, 19422, -10297, -25901, -5996, + 22128, 19917, -9599, -25956, -6729, 21722, 20394, -8892, + -25989, -7457, 21297, 20855, -8178, -26000, -8178, 20855, + 21298, -7456, -25988, -8892, 20394, 21722, -6729, -25956, + -9599, 19917, 22129, -5995, -25901, -10298, 19422, 22516, + -5257, -25824, -10988, 18911, 22885, -4514, -25725, -11668}, + { +// Carrier 37 Phase 3 + 24020, 17000, -13326, -25383, -2642, 23721, 17565, -12671, + -25536, -3393, 23401, 18115, -12005, -25667, -4141, 23062, + 18650, -11329, -25777, -4886, 22703, 19169, -10644, -25865, + -5627, 22325, 19671, -9949, -25931, -6363, 21928, 20158, + -9246, -25975, -7093, 21512, 20627, -8536, -25997, -7818, + 21078, 21078, -7818, -25997, -8536, 20627, 21512, -7093, + -25975, -9246, 20158, 21928, -6363, -25931, -9949, 19671, + 22325, -5627, -25865, -10644, 19169, 22703, -4886, -25777, + -11329, 18650, 23062, -4141, -25667, -12005, 18115, 23401, + -3393, -25536, -12671, 17565, 23721, -2642, -25383, -13326, + 17000, 24020, -1889, -25209, -13969, 16420, 24300, -1134, + -25013, -14601, 15827, 24558, -378, -24796, -15221, 15221, + 24796, 378, -24558, -15827, 14601, 25013, 1134, -24300, + -16421, 13969, 25209, 1889, -24020, -17000, 13326, 25383, + 2642, -23721, -17565, 12671, 25536, 3393, -23401, -18115, + 12005, 25667, 4142, -23062, -18650, 11329, 25777, 4886, + -22703, -19169, 10644, 25865, 5627, -22325, -19672, 9949, + 25931, 6363, -21928, -20158, 9246, 25975, 7093, -21512, + -20627, 8536, 25997, 7818, -21078, -21078, 7818, 25997, + 8536, -20627, -21512, 7093, 25975, 9247, -20158, -21928, + 6363, 25931, 9949, -19671, -22325, 5627, 25865, 10644, + -19169, -22703, 4886, 25777, 11329, -18650, -23062, 4141, + 25667, 12005, -18115, -23401, 3393, 25536, 12671, -17565, + -23721, 2642, 25383, 13326, -17000, -24020, 1888, 25209, + 13969, -16420, -24300, 1133, 25013, 14601, -15827, -24558, + 377, 24796, 15221, -15221, -24796, -378, 24558, 15827, + -14601, -25013, -1134, 24300, 16421, -13969, -25209, -1889}, + { +// Carrier 37 Phase 4 + 26000, 8178, -20855, -21297, 7456, 25989, 8892, -20394, + -21722, 6729, 25956, 9599, -19917, -22129, 5995, 25901, + 10298, -19422, -22516, 5257, 25824, 10988, -18911, -22885, + 4514, 25725, 11668, -18384, -23234, 3768, 25604, 12339, + -17842, -23564, 3018, 25462, 13000, -17284, -23873, 2266, + 25299, 13649, -16712, -24163, 1511, 25114, 14287, -16126, + -24432, 756, 24907, 14913, -15526, -24680, 0, 24680, + 15526, -14912, -24907, -756, 24431, 16126, -14287, -25114, + -1511, 24163, 16712, -13649, -25299, -2266, 23873, 17284, + -12999, -25462, -3018, 23563, 17842, -12339, -25605, -3768, + 23234, 18384, -11668, -25725, -4514, 22885, 18911, -10988, + -25824, -5257, 22516, 19422, -10297, -25901, -5996, 22128, + 19917, -9599, -25956, -6729, 21722, 20394, -8892, -25989, + -7456, 21297, 20855, -8178, -26000, -8178, 20855, 21298, + -7456, -25988, -8892, 20394, 21722, -6729, -25956, -9599, + 19917, 22129, -5995, -25901, -10298, 19422, 22516, -5257, + -25824, -10988, 18911, 22885, -4514, -25725, -11668, 18384, + 23234, -3768, -25604, -12339, 17842, 23564, -3018, -25462, + -13000, 17284, 23873, -2265, -25299, -13649, 16712, 24163, + -1511, -25114, -14287, 16126, 24432, -756, -24907, -14913, + 15526, 24680, 0, -24680, -15526, 14912, 24907, 756, + -24431, -16126, 14287, 25114, 1511, -24162, -16712, 13649, + 25299, 2266, -23873, -17284, 12999, 25462, 3018, -23563, + -17842, 12339, 25605, 3768, -23234, -18384, 11668, 25725, + 4515, -22885, -18911, 10987, 25824, 5257, -22516, -19422, + 10297, 25901, 5996, -22128, -19917, 9599, 25956, 6729, + -21722, -20394, 8892, 25989, 7457, -21297, -20855, 8177}, + { +// Carrier 37 Phase 5 + 24020, -1889, -25209, -13969, 16421, 24300, -1134, -25013, + -14601, 15827, 24558, -378, -24796, -15221, 15221, 24796, + 378, -24558, -15827, 14601, 25013, 1134, -24300, -16421, + 13969, 25209, 1889, -24020, -17000, 13326, 25383, 2642, + -23721, -17565, 12671, 25536, 3393, -23401, -18115, 12005, + 25667, 4142, -23062, -18650, 11329, 25777, 4886, -22703, + -19169, 10644, 25865, 5627, -22325, -19672, 9949, 25931, + 6363, -21928, -20158, 9246, 25975, 7093, -21512, -20627, + 8536, 25997, 7818, -21078, -21078, 7818, 25997, 8536, + -20627, -21512, 7093, 25975, 9246, -20158, -21928, 6363, + 25931, 9949, -19671, -22325, 5627, 25865, 10644, -19169, + -22703, 4886, 25777, 11329, -18650, -23062, 4141, 25667, + 12005, -18115, -23401, 3393, 25536, 12671, -17565, -23721, + 2642, 25383, 13326, -17000, -24020, 1889, 25209, 13969, + -16420, -24300, 1134, 25013, 14601, -15827, -24558, 378, + 24796, 15221, -15221, -24796, -378, 24558, 15827, -14601, + -25013, -1134, 24300, 16421, -13969, -25209, -1889, 24020, + 17000, -13326, -25383, -2642, 23721, 17565, -12671, -25536, + -3393, 23401, 18115, -12005, -25667, -4142, 23062, 18650, + -11329, -25777, -4886, 22703, 19169, -10644, -25865, -5627, + 22325, 19672, -9949, -25931, -6363, 21928, 20158, -9246, + -25975, -7093, 21512, 20627, -8536, -25997, -7818, 21078, + 21078, -7818, -25997, -8536, 20627, 21512, -7093, -25975, + -9247, 20158, 21928, -6363, -25931, -9949, 19671, 22325, + -5627, -25865, -10644, 19169, 22703, -4886, -25777, -11329, + 18650, 23062, -4141, -25667, -12005, 18115, 23401, -3393, + -25536, -12671, 17565, 23721, -2642, -25383, -13326, 17000}, + { +// Carrier 37 Phase 6 + 18384, -11668, -25725, -4514, 22885, 18911, -10988, -25824, + -5257, 22516, 19422, -10298, -25901, -5996, 22129, 19917, + -9599, -25956, -6729, 21722, 20394, -8892, -25989, -7456, + 21297, 20855, -8178, -26000, -8178, 20855, 21297, -7456, + -25989, -8892, 20394, 21722, -6729, -25956, -9599, 19917, + 22129, -5995, -25901, -10298, 19422, 22516, -5257, -25824, + -10988, 18911, 22885, -4514, -25725, -11668, 18384, 23234, + -3768, -25604, -12339, 17842, 23564, -3018, -25462, -13000, + 17284, 23873, -2265, -25299, -13649, 16712, 24163, -1511, + -25114, -14287, 16126, 24432, -756, -24907, -14913, 15526, + 24680, 0, -24680, -15526, 14912, 24907, 756, -24431, + -16126, 14287, 25114, 1511, -24163, -16712, 13649, 25299, + 2266, -23873, -17284, 12999, 25462, 3018, -23563, -17842, + 12339, 25605, 3768, -23234, -18384, 11668, 25725, 4514, + -22885, -18911, 10987, 25824, 5257, -22516, -19422, 10297, + 25901, 5996, -22128, -19917, 9599, 25956, 6729, -21722, + -20394, 8892, 25989, 7456, -21297, -20855, 8178, 26000, + 8178, -20855, -21298, 7456, 25988, 8892, -20394, -21722, + 6729, 25956, 9599, -19917, -22129, 5995, 25901, 10298, + -19422, -22516, 5257, 25824, 10988, -18911, -22885, 4514, + 25725, 11668, -18384, -23234, 3768, 25604, 12339, -17842, + -23564, 3018, 25462, 13000, -17284, -23873, 2265, 25299, + 13649, -16712, -24163, 1511, 25114, 14287, -16126, -24432, + 756, 24907, 14913, -15526, -24680, 0, 24680, 15526, + -14912, -24907, -756, 24431, 16126, -14287, -25114, -1511, + 24162, 16712, -13649, -25299, -2266, 23873, 17284, -12999, + -25462, -3018, 23563, 17842, -12339, -25605, -3768, 23234}, + { +// Carrier 37 Phase 7 + 9949, -19671, -22325, 5627, 25865, 10644, -19169, -22703, + 4886, 25777, 11329, -18650, -23062, 4141, 25667, 12005, + -18115, -23401, 3393, 25536, 12671, -17565, -23721, 2642, + 25383, 13326, -17000, -24020, 1889, 25209, 13969, -16421, + -24300, 1134, 25013, 14601, -15827, -24558, 378, 24796, + 15221, -15221, -24796, -378, 24558, 15827, -14601, -25013, + -1134, 24300, 16421, -13969, -25209, -1889, 24020, 17000, + -13326, -25383, -2642, 23721, 17565, -12671, -25536, -3393, + 23401, 18115, -12005, -25667, -4142, 23062, 18650, -11329, + -25777, -4886, 22703, 19169, -10644, -25865, -5627, 22325, + 19672, -9949, -25931, -6363, 21928, 20158, -9246, -25975, + -7093, 21512, 20627, -8536, -25997, -7818, 21078, 21078, + -7818, -25997, -8536, 20627, 21512, -7093, -25975, -9246, + 20158, 21928, -6363, -25931, -9949, 19671, 22325, -5627, + -25865, -10644, 19169, 22703, -4886, -25777, -11329, 18650, + 23062, -4141, -25667, -12005, 18115, 23401, -3393, -25536, + -12671, 17565, 23721, -2642, -25383, -13326, 17000, 24020, + -1889, -25209, -13969, 16420, 24300, -1133, -25013, -14601, + 15827, 24558, -378, -24796, -15221, 15221, 24796, 378, + -24558, -15827, 14601, 25013, 1134, -24300, -16421, 13969, + 25209, 1889, -24020, -17000, 13325, 25383, 2642, -23721, + -17565, 12671, 25536, 3393, -23401, -18115, 12005, 25667, + 4142, -23062, -18650, 11329, 25777, 4886, -22703, -19169, + 10644, 25865, 5627, -22325, -19672, 9949, 25931, 6363, + -21928, -20158, 9246, 25975, 7093, -21512, -20627, 8536, + 25997, 7818, -21078, -21078, 7818, 25997, 8536, -20627, + -21512, 7093, 25975, 9247, -20158, -21928, 6363, 25931}, + },{{ + +// Carrier 38 Phase 0 + 0, 24907, 14287, -16712, -23873, 3018, 25605, 11668, + -18911, -22516, 5996, 25956, 8892, -20855, -20855, 8892, + 25956, 5995, -22516, -18911, 11668, 25604, 3018, -23873, + -16712, 14287, 24907, 0, -24907, -14287, 16712, 23873, + -3018, -25605, -11668, 18911, 22516, -5996, -25956, -8892, + 20855, 20855, -8892, -25956, -5995, 22516, 18911, -11668, + -25604, -3018, 23873, 16712, -14287, -24907, 0, 24907, + 14287, -16712, -23873, 3018, 25605, 11668, -18911, -22516, + 5996, 25956, 8892, -20855, -20855, 8892, 25955, 5995, + -22516, -18911, 11668, 25604, 3018, -23873, -16712, 14287, + 24907, 0, -24907, -14287, 16712, 23873, -3018, -25605, + -11668, 18911, 22516, -5996, -25956, -8892, 20855, 20855, + -8892, -25955, -5995, 22516, 18911, -11669, -25604, -3018, + 23873, 16712, -14287, -24907, 0, 24907, 14286, -16712, + -23873, 3018, 25605, 11668, -18911, -22516, 5996, 25956, + 8892, -20855, -20854, 8892, 25955, 5995, -22516, -18911, + 11669, 25604, 3018, -23873, -16712, 14287, 24907, 0, + -24907, -14286, 16712, 23873, -3018, -25605, -11668, 18912, + 22516, -5996, -25956, -8892, 20855, 20854, -8892, -25955, + -5995, 22516, 18911, -11669, -25604, -3017, 23873, 16712, + -14287, -24907, 0, 24907, 14286, -16712, -23873, 3018, + 25605, 11668, -18912, -22516, 5996, 25956, 8892, -20855, + -20854, 8893, 25955, 5995, -22516, -18911, 11669, 25604, + 3017, -23873, -16712, 14287, 24907, 0, -24907, -14286, + 16712, 23873, -3019, -25605, -11668, 18912, 22516, -5996, + -25956, -8891, 20855, 20854, -8893, -25955, -5995, 22516, + 18911, -11669, -25604, -3017, 23873, 16711, -14287, -24907}, + { +// Carrier 38 Phase 1 + 9949, 25865, 4886, -23062, -18115, 12671, 25383, 1889, + -24300, -15827, 15221, 24558, -1134, -25209, -13326, 17565, + 23401, -4142, -25777, -10644, 19672, 21928, -7093, -25997, + -7818, 21512, 20158, -9949, -25865, -4886, 23062, 18115, + -12671, -25383, -1889, 24300, 15827, -15221, -24558, 1134, + 25209, 13325, -17565, -23401, 4142, 25777, 10644, -19672, + -21928, 7093, 25997, 7818, -21512, -20158, 9949, 25865, + 4886, -23062, -18115, 12671, 25383, 1888, -24300, -15827, + 15221, 24558, -1134, -25209, -13325, 17565, 23401, -4142, + -25777, -10643, 19672, 21928, -7094, -25997, -7818, 21512, + 20157, -9950, -25865, -4886, 23062, 18115, -12671, -25383, + -1888, 24300, 15827, -15221, -24558, 1134, 25209, 13325, + -17565, -23401, 4142, 25777, 10643, -19672, -21928, 7094, + 25997, 7818, -21512, -20157, 9950, 25865, 4886, -23062, + -18115, 12671, 25383, 1888, -24300, -15827, 15221, 24558, + -1134, -25209, -13325, 17565, 23401, -4142, -25777, -10643, + 19672, 21927, -7094, -25997, -7817, 21512, 20157, -9950, + -25865, -4886, 23062, 18115, -12671, -25383, -1888, 24300, + 15827, -15221, -24558, 1134, 25209, 13325, -17565, -23401, + 4142, 25777, 10643, -19672, -21927, 7094, 25997, 7817, + -21512, -20157, 9950, 25865, 4886, -23062, -18115, 12671, + 25383, 1888, -24300, -15827, 15221, 24558, -1134, -25209, + -13325, 17565, 23401, -4142, -25777, -10643, 19672, 21927, + -7094, -25997, -7817, 21512, 20157, -9950, -25865, -4886, + 23062, 18114, -12671, -25383, -1888, 24300, 15827, -15221, + -24558, 1134, 25209, 13325, -17565, -23401, 4142, 25777, + 10643, -19672, -21927, 7094, 25997, 7817, -21512, -20157}, + { +// Carrier 38 Phase 2 + 18384, 22885, -5257, -25901, -9599, 20394, 21297, -8178, + -25989, -6729, 22129, 19422, -10988, -25725, -3768, 23564, + 17284, -13649, -25114, -756, 24680, 14912, -16126, -24163, + 2266, 25462, 12339, -18384, -22885, 5257, 25901, 9599, + -20394, -21297, 8178, 25988, 6729, -22129, -19422, 10988, + 25725, 3768, -23564, -17284, 13649, 25114, 756, -24680, + -14912, 16126, 24162, -2266, -25462, -12339, 18384, 22885, + -5257, -25901, -9599, 20394, 21297, -8178, -25988, -6729, + 22129, 19422, -10988, -25725, -3768, 23564, 17284, -13649, + -25114, -755, 24680, 14912, -16126, -24162, 2266, 25462, + 12339, -18384, -22885, 5257, 25901, 9599, -20394, -21297, + 8178, 25988, 6729, -22129, -19422, 10988, 25725, 3767, + -23564, -17284, 13649, 25113, 755, -24680, -14912, 16126, + 24162, -2266, -25462, -12339, 18385, 22885, -5257, -25901, + -9599, 20395, 21297, -8178, -25988, -6728, 22129, 19422, + -10988, -25725, -3767, 23564, 17284, -13649, -25113, -755, + 24680, 14912, -16126, -24162, 2266, 25462, 12339, -18385, + -22885, 5258, 25901, 9598, -20395, -21297, 8178, 25988, + 6728, -22129, -19422, 10988, 25725, 3767, -23564, -17284, + 13649, 25113, 755, -24680, -14912, 16126, 24162, -2266, + -25462, -12339, 18385, 22884, -5258, -25901, -9598, 20395, + 21297, -8178, -25988, -6728, 22129, 19422, -10988, -25725, + -3767, 23564, 17284, -13649, -25113, -755, 24680, 14912, + -16126, -24162, 2266, 25462, 12339, -18385, -22884, 5258, + 25901, 9598, -20395, -21297, 8178, 25988, 6728, -22129, + -19422, 10988, 25725, 3767, -23564, -17284, 13649, 25113, + 755, -24680, -14912, 16126, 24162, -2266, -25462, -12339}, + { +// Carrier 38 Phase 3 + 24020, 16421, -14601, -24796, 378, 25013, 13969, -17000, + -23721, 3393, 25667, 11329, -19169, -22325, 6363, 25975, + 8536, -21078, -20627, 9246, 25931, 5627, -22703, -18650, + 12005, 25536, 2642, -24020, -16420, 14601, 24796, -378, + -25013, -13969, 17000, 23721, -3393, -25667, -11329, 19169, + 22325, -6363, -25975, -8536, 21078, 20627, -9247, -25931, + -5627, 22703, 18650, -12005, -25536, -2642, 24020, 16420, + -14601, -24796, 378, 25013, 13969, -17000, -23721, 3393, + 25667, 11329, -19169, -22325, 6363, 25975, 8536, -21078, + -20627, 9247, 25931, 5627, -22703, -18650, 12005, 25536, + 2642, -24020, -16420, 14601, 24796, -378, -25013, -13969, + 17000, 23721, -3393, -25668, -11329, 19169, 22325, -6363, + -25975, -8535, 21078, 20626, -9247, -25931, -5627, 22703, + 18649, -12005, -25536, -2642, 24021, 16420, -14601, -24796, + 378, 25013, 13969, -17000, -23721, 3394, 25668, 11329, + -19169, -22325, 6363, 25975, 8535, -21079, -20626, 9247, + 25931, 5627, -22703, -18649, 12005, 25536, 2642, -24021, + -16420, 14602, 24796, -378, -25013, -13969, 17000, 23721, + -3394, -25668, -11329, 19169, 22324, -6363, -25975, -8535, + 21079, 20626, -9247, -25931, -5626, 22703, 18649, -12005, + -25536, -2642, 24021, 16420, -14602, -24796, 378, 25013, + 13969, -17000, -23721, 3394, 25668, 11329, -19169, -22324, + 6363, 25975, 8535, -21079, -20626, 9247, 25931, 5626, + -22703, -18649, 12005, 25536, 2641, -24021, -16420, 14602, + 24796, -378, -25013, -13969, 17000, 23721, -3394, -25668, + -11329, 19169, 22324, -6363, -25975, -8535, 21079, 20626, + -9247, -25931, -5626, 22703, 18649, -12006, -25536, -2641}, + { +// Carrier 38 Phase 4 + 26000, 7456, -21722, -19917, 10298, 25824, 4514, -23234, + -17842, 13000, 25299, 1511, -24432, -15526, 15526, 24431, + -1511, -25299, -12999, 17842, 23234, -4514, -25824, -10298, + 19917, 21722, -7456, -26000, -7456, 21722, 19917, -10298, + -25824, -4514, 23234, 17842, -13000, -25299, -1511, 24432, + 15526, -15526, -24431, 1511, 25299, 12999, -17842, -23234, + 4514, 25824, 10297, -19917, -21722, 7457, 26000, 7456, + -21722, -19917, 10298, 25824, 4514, -23234, -17842, 13000, + 25299, 1511, -24432, -15525, 15526, 24431, -1511, -25299, + -12999, 17842, 23234, -4515, -25824, -10297, 19917, 21722, + -7457, -26000, -7456, 21722, 19916, -10298, -25824, -4514, + 23234, 17842, -13000, -25299, -1511, 24432, 15525, -15526, + -24431, 1512, 25299, 12999, -17842, -23234, 4515, 25824, + 10297, -19917, -21722, 7457, 26000, 7456, -21722, -19916, + 10298, 25824, 4514, -23234, -17842, 13000, 25299, 1511, + -24432, -15525, 15526, 24431, -1512, -25299, -12999, 17842, + 23234, -4515, -25824, -10297, 19917, 21722, -7457, -26000, + -7456, 21722, 19916, -10298, -25824, -4514, 23234, 17841, + -13000, -25299, -1511, 24432, 15525, -15526, -24431, 1512, + 25299, 12999, -17842, -23234, 4515, 25824, 10297, -19917, + -21722, 7457, 26000, 7456, -21722, -19916, 10298, 25824, + 4514, -23234, -17841, 13000, 25299, 1511, -24432, -15525, + 15526, 24431, -1512, -25299, -12999, 17842, 23234, -4515, + -25824, -10297, 19917, 21722, -7457, -26000, -7456, 21723, + 19916, -10298, -25824, -4514, 23234, 17841, -13000, -25299, + -1511, 24432, 15525, -15526, -24431, 1512, 25299, 12999, + -17842, -23234, 4515, 25824, 10297, -19917, -21722, 7457}, + { +// Carrier 38 Phase 5 + 24020, -2642, -25536, -12005, 18650, 22703, -5627, -25931, + -9246, 20627, 21078, -8536, -25975, -6363, 22325, 19169, + -11329, -25667, -3393, 23721, 17000, -13969, -25013, -378, + 24796, 14601, -16421, -24020, 2642, 25536, 12005, -18650, + -22703, 5627, 25931, 9246, -20627, -21078, 8536, 25975, + 6363, -22325, -19169, 11329, 25667, 3393, -23721, -17000, + 13969, 25013, 377, -24796, -14601, 16421, 24020, -2642, + -25536, -12005, 18650, 22703, -5627, -25931, -9246, 20627, + 21078, -8536, -25975, -6363, 22325, 19169, -11329, -25667, + -3393, 23721, 17000, -13969, -25013, -377, 24796, 14601, + -16421, -24020, 2642, 25536, 12005, -18650, -22703, 5627, + 25931, 9246, -20627, -21078, 8536, 25975, 6363, -22325, + -19169, 11329, 25667, 3393, -23721, -17000, 13970, 25013, + 377, -24796, -14601, 16421, 24020, -2642, -25536, -12005, + 18650, 22703, -5627, -25931, -9246, 20627, 21078, -8536, + -25975, -6362, 22325, 19168, -11329, -25667, -3393, 23721, + 17000, -13970, -25013, -377, 24796, 14601, -16421, -24020, + 2642, 25536, 12005, -18650, -22703, 5627, 25931, 9246, + -20627, -21078, 8536, 25975, 6362, -22325, -19168, 11330, + 25667, 3393, -23721, -17000, 13970, 25013, 377, -24796, + -14601, 16421, 24020, -2643, -25536, -12005, 18650, 22703, + -5627, -25931, -9246, 20627, 21078, -8536, -25975, -6362, + 22325, 19168, -11330, -25667, -3393, 23721, 16999, -13970, + -25013, -377, 24796, 14601, -16421, -24020, 2643, 25536, + 12004, -18650, -22703, 5628, 25931, 9246, -20627, -21078, + 8536, 25975, 6362, -22325, -19168, 11330, 25667, 3393, + -23721, -16999, 13970, 25013, 377, -24796, -14601, 16421}, + { +// Carrier 38 Phase 6 + 18384, -12339, -25462, -2266, 24163, 16126, -14913, -24680, + 756, 25114, 13649, -17284, -23563, 3768, 25725, 10988, + -19422, -22129, 6729, 25989, 8178, -21297, -20394, 9599, + 25901, 5257, -22885, -18384, 12339, 25462, 2265, -24163, + -16126, 14913, 24680, -756, -25114, -13649, 17284, 23563, + -3768, -25725, -10987, 19422, 22128, -6729, -25989, -8178, + 21298, 20394, -9599, -25901, -5257, 22885, 18384, -12339, + -25462, -2265, 24163, 16125, -14913, -24680, 756, 25114, + 13649, -17284, -23563, 3768, 25725, 10987, -19422, -22128, + 6729, 25989, 8177, -21298, -20394, 9599, 25901, 5257, + -22885, -18384, 12339, 25462, 2265, -24163, -16125, 14913, + 24680, -756, -25114, -13649, 17284, 23563, -3768, -25725, + -10987, 19422, 22128, -6729, -25989, -8177, 21298, 20394, + -9599, -25901, -5257, 22885, 18384, -12339, -25462, -2265, + 24163, 16125, -14913, -24680, 756, 25114, 13649, -17284, + -23563, 3768, 25725, 10987, -19422, -22128, 6729, 25989, + 8177, -21298, -20394, 9599, 25901, 5257, -22885, -18384, + 12339, 25462, 2265, -24163, -16125, 14913, 24680, -756, + -25114, -13648, 17285, 23563, -3768, -25725, -10987, 19422, + 22128, -6729, -25989, -8177, 21298, 20394, -9599, -25901, + -5257, 22885, 18384, -12340, -25462, -2265, 24163, 16125, + -14913, -24680, 756, 25114, 13648, -17285, -23563, 3768, + 25725, 10987, -19423, -22128, 6729, 25989, 8177, -21298, + -20394, 9599, 25901, 5257, -22885, -18384, 12340, 25462, + 2265, -24163, -16125, 14913, 24680, -756, -25114, -13648, + 17285, 23563, -3768, -25725, -10987, 19423, 22128, -6729, + -25989, -8177, 21298, 20394, -9599, -25901, -5256, 22885}, + { +// Carrier 38 Phase 7 + 9949, -20158, -21512, 7818, 25997, 7093, -21928, -19671, + 10644, 25777, 4141, -23401, -17565, 13326, 25209, 1134, + -24558, -15221, 15827, 24300, -1889, -25383, -12671, 18115, + 23062, -4886, -25865, -9949, 20158, 21512, -7818, -25997, + -7093, 21928, 19671, -10644, -25777, -4141, 23401, 17565, + -13326, -25209, -1133, 24558, 15221, -15827, -24300, 1889, + 25383, 12671, -18115, -23062, 4886, 25865, 9949, -20158, + -21512, 7818, 25997, 7093, -21928, -19671, 10644, 25777, + 4141, -23401, -17565, 13326, 25209, 1133, -24558, -15220, + 15827, 24300, -1889, -25383, -12670, 18115, 23062, -4887, + -25865, -9949, 20158, 21512, -7818, -25997, -7093, 21928, + 19671, -10644, -25777, -4141, 23401, 17565, -13326, -25209, + -1133, 24558, 15220, -15828, -24299, 1889, 25383, 12670, + -18115, -23062, 4887, 25865, 9949, -20158, -21512, 7818, + 25997, 7093, -21928, -19671, 10644, 25777, 4141, -23401, + -17565, 13326, 25209, 1133, -24558, -15220, 15828, 24299, + -1889, -25383, -12670, 18115, 23062, -4887, -25865, -9949, + 20158, 21512, -7818, -25997, -7093, 21928, 19671, -10644, + -25777, -4141, 23401, 17564, -13326, -25209, -1133, 24558, + 15220, -15828, -24299, 1889, 25383, 12670, -18115, -23062, + 4887, 25865, 9949, -20158, -21512, 7818, 25997, 7093, + -21928, -19671, 10644, 25777, 4141, -23401, -17564, 13326, + 25209, 1133, -24558, -15220, 15828, 24299, -1889, -25383, + -12670, 18115, 23062, -4887, -25865, -9949, 20158, 21512, + -7818, -25997, -7093, 21928, 19671, -10644, -25777, -4141, + 23401, 17564, -13326, -25209, -1133, 24558, 15220, -15828, + -24299, 1889, 25383, 12670, -18115, -23061, 4887, 25865}, + },{{ + +// Carrier 39 Phase 0 + 0, 25114, 13000, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25113, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6728, + -22516, -18384, 13000, 25113, 0, -25114, -12999, 18385, + 22516, -6729, -26000, -6728, 22516, 18384, -13000, -25113, + 0, 25114, 12999, -18385, -22516, 6729, 26000, 6728, + -22516, -18384, 13000, 25113, 0, -25114, -12999, 18385, + 22516, -6729, -26000, -6728, 22516, 18384, -13000, -25113, + 0, 25114, 12999, -18385, -22516, 6729, 26000, 6728, + -22516, -18384, 13000, 25113, 0, -25114, -12999, 18385, + 22516, -6729, -26000, -6728, 22516, 18384, -13000, -25113}, + { +// Carrier 39 Phase 1 + 9949, 25777, 3393, -24020, -15827, 15827, 24020, -3393, + -25777, -9949, 20627, 20627, -9949, -25777, -3393, 24020, + 15827, -15827, -24020, 3393, 25777, 9949, -20627, -20627, + 9949, 25777, 3393, -24020, -15827, 15827, 24020, -3393, + -25777, -9949, 20627, 20627, -9949, -25777, -3393, 24020, + 15827, -15827, -24020, 3393, 25777, 9949, -20627, -20627, + 9949, 25777, 3393, -24020, -15827, 15827, 24020, -3393, + -25777, -9949, 20627, 20627, -9949, -25777, -3393, 24020, + 15827, -15827, -24020, 3393, 25777, 9949, -20627, -20627, + 9949, 25777, 3393, -24020, -15827, 15827, 24020, -3393, + -25777, -9949, 20627, 20627, -9949, -25777, -3393, 24020, + 15827, -15827, -24020, 3393, 25777, 9949, -20627, -20627, + 9949, 25777, 3393, -24020, -15827, 15827, 24020, -3393, + -25777, -9949, 20627, 20627, -9949, -25777, -3393, 24020, + 15827, -15827, -24020, 3393, 25777, 9949, -20627, -20627, + 9950, 25777, 3393, -24020, -15827, 15828, 24020, -3393, + -25777, -9949, 20627, 20627, -9950, -25777, -3393, 24020, + 15827, -15828, -24020, 3393, 25777, 9949, -20627, -20627, + 9950, 25777, 3393, -24020, -15827, 15828, 24020, -3393, + -25777, -9949, 20627, 20626, -9950, -25777, -3393, 24020, + 15827, -15828, -24020, 3394, 25777, 9949, -20627, -20626, + 9950, 25777, 3393, -24020, -15827, 15828, 24020, -3394, + -25777, -9949, 20627, 20626, -9950, -25777, -3393, 24021, + 15827, -15828, -24020, 3394, 25777, 9949, -20627, -20626, + 9950, 25777, 3393, -24021, -15827, 15828, 24020, -3394, + -25777, -9949, 20627, 20626, -9950, -25777, -3393, 24021, + 15827, -15828, -24020, 3394, 25777, 9949, -20627, -20626}, + { +// Carrier 39 Phase 2 + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25113, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25113, 0, 25114, 12999, -18385, -22516, 6729, 26000, + 6728, -22516, -18384, 13000, 25113, 0, -25114, -12999, + 18385, 22516, -6729, -26000, -6728, 22516, 18384, -13000, + -25113, 0, 25114, 12999, -18385, -22516, 6729, 26000, + 6728, -22516, -18384, 13000, 25113, 0, -25114, -12999, + 18385, 22516, -6729, -26000, -6728, 22516, 18384, -13000, + -25113, 0, 25114, 12999, -18385, -22516, 6729, 26000, + 6728, -22516, -18384, 13000, 25113, 0, -25114, -12999}, + { +// Carrier 39 Phase 3 + 24020, 15827, -15827, -24020, 3393, 25777, 9949, -20627, + -20627, 9949, 25777, 3393, -24020, -15827, 15827, 24020, + -3393, -25777, -9949, 20627, 20627, -9949, -25777, -3393, + 24020, 15827, -15827, -24020, 3393, 25777, 9949, -20627, + -20627, 9949, 25777, 3393, -24020, -15827, 15827, 24020, + -3393, -25777, -9949, 20627, 20627, -9949, -25777, -3393, + 24020, 15827, -15827, -24020, 3393, 25777, 9949, -20627, + -20627, 9949, 25777, 3393, -24020, -15827, 15827, 24020, + -3393, -25777, -9949, 20627, 20627, -9949, -25777, -3393, + 24020, 15827, -15827, -24020, 3393, 25777, 9949, -20627, + -20627, 9949, 25777, 3393, -24020, -15827, 15827, 24020, + -3393, -25777, -9949, 20627, 20627, -9949, -25777, -3393, + 24020, 15827, -15827, -24020, 3393, 25777, 9949, -20627, + -20627, 9949, 25777, 3393, -24020, -15827, 15827, 24020, + -3393, -25777, -9949, 20627, 20627, -9949, -25777, -3393, + 24020, 15827, -15828, -24020, 3393, 25777, 9949, -20627, + -20627, 9950, 25777, 3393, -24020, -15827, 15828, 24020, + -3393, -25777, -9949, 20627, 20627, -9950, -25777, -3393, + 24020, 15827, -15828, -24020, 3393, 25777, 9949, -20627, + -20626, 9950, 25777, 3393, -24020, -15827, 15828, 24020, + -3394, -25777, -9949, 20627, 20626, -9950, -25777, -3393, + 24021, 15827, -15828, -24020, 3394, 25777, 9949, -20627, + -20626, 9950, 25777, 3393, -24021, -15827, 15828, 24020, + -3394, -25777, -9949, 20627, 20626, -9950, -25777, -3393, + 24021, 15827, -15828, -24020, 3394, 25777, 9949, -20627, + -20626, 9950, 25777, 3393, -24021, -15827, 15828, 24020, + -3394, -25777, -9949, 20627, 20626, -9950, -25777, -3393}, + { +// Carrier 39 Phase 4 + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25113, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6728, -22516, -18384, 13000, 25113, 0, -25114, + -12999, 18385, 22516, -6729, -26000, -6728, 22516, 18384, + -13000, -25113, 0, 25114, 12999, -18385, -22516, 6729, + 26000, 6728, -22516, -18384, 13000, 25113, 0, -25114, + -12999, 18385, 22516, -6729, -26000, -6728, 22516, 18384, + -13000, -25113, 0, 25114, 12999, -18385, -22516, 6729, + 26000, 6728, -22516, -18384, 13000, 25113, 0, -25114, + -12999, 18385, 22516, -6729, -26000, -6728, 22516, 18384, + -13000, -25113, 0, 25114, 12999, -18385, -22516, 6729}, + { +// Carrier 39 Phase 5 + 24020, -3393, -25777, -9949, 20627, 20627, -9949, -25777, + -3393, 24020, 15827, -15827, -24020, 3393, 25777, 9949, + -20627, -20627, 9949, 25777, 3393, -24020, -15827, 15827, + 24020, -3393, -25777, -9949, 20627, 20627, -9949, -25777, + -3393, 24020, 15827, -15827, -24020, 3393, 25777, 9949, + -20627, -20627, 9949, 25777, 3393, -24020, -15827, 15827, + 24020, -3393, -25777, -9949, 20627, 20627, -9949, -25777, + -3393, 24020, 15827, -15827, -24020, 3393, 25777, 9949, + -20627, -20627, 9949, 25777, 3393, -24020, -15827, 15827, + 24020, -3393, -25777, -9949, 20627, 20627, -9949, -25777, + -3393, 24020, 15827, -15827, -24020, 3393, 25777, 9949, + -20627, -20627, 9949, 25777, 3393, -24020, -15827, 15827, + 24020, -3393, -25777, -9949, 20627, 20627, -9949, -25777, + -3393, 24020, 15827, -15827, -24020, 3393, 25777, 9949, + -20627, -20627, 9949, 25777, 3393, -24020, -15827, 15827, + 24020, -3393, -25777, -9949, 20627, 20627, -9949, -25777, + -3393, 24020, 15827, -15828, -24020, 3393, 25777, 9949, + -20627, -20627, 9950, 25777, 3393, -24020, -15827, 15828, + 24020, -3393, -25777, -9949, 20627, 20627, -9950, -25777, + -3393, 24020, 15827, -15828, -24020, 3393, 25777, 9949, + -20627, -20626, 9950, 25777, 3393, -24020, -15827, 15828, + 24020, -3394, -25777, -9949, 20627, 20626, -9950, -25777, + -3393, 24021, 15827, -15828, -24020, 3394, 25777, 9949, + -20627, -20626, 9950, 25777, 3393, -24021, -15827, 15828, + 24020, -3394, -25777, -9949, 20627, 20626, -9950, -25777, + -3393, 24021, 15827, -15828, -24020, 3394, 25777, 9949, + -20627, -20626, 9950, 25777, 3393, -24021, -15827, 15828}, + { +// Carrier 39 Phase 6 + 18384, -12999, -25114, 0, 25114, 13000, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25113, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6728, -22516, -18384, 13000, 25113, 0, + -25114, -12999, 18385, 22516, -6729, -26000, -6728, 22516, + 18384, -13000, -25113, 0, 25114, 12999, -18385, -22516, + 6729, 26000, 6728, -22516, -18384, 13000, 25113, 0, + -25114, -12999, 18385, 22516, -6729, -26000, -6728, 22516, + 18384, -13000, -25113, 0, 25114, 12999, -18385, -22516, + 6729, 26000, 6728, -22516, -18384, 13000, 25113, 0, + -25114, -12999, 18385, 22516, -6729, -26000, -6728, 22516}, + { +// Carrier 39 Phase 7 + 9949, -20627, -20627, 9949, 25777, 3393, -24020, -15827, + 15827, 24020, -3393, -25777, -9949, 20627, 20627, -9949, + -25777, -3393, 24020, 15827, -15827, -24020, 3393, 25777, + 9949, -20627, -20627, 9949, 25777, 3393, -24020, -15827, + 15827, 24020, -3393, -25777, -9949, 20627, 20627, -9949, + -25777, -3393, 24020, 15827, -15827, -24020, 3393, 25777, + 9949, -20627, -20627, 9949, 25777, 3393, -24020, -15827, + 15827, 24020, -3393, -25777, -9949, 20627, 20627, -9949, + -25777, -3393, 24020, 15827, -15827, -24020, 3393, 25777, + 9949, -20627, -20627, 9949, 25777, 3393, -24020, -15827, + 15827, 24020, -3393, -25777, -9949, 20627, 20627, -9949, + -25777, -3393, 24020, 15827, -15827, -24020, 3393, 25777, + 9949, -20627, -20627, 9949, 25777, 3393, -24020, -15827, + 15827, 24020, -3393, -25777, -9949, 20627, 20627, -9949, + -25777, -3393, 24020, 15827, -15827, -24020, 3393, 25777, + 9949, -20627, -20627, 9950, 25777, 3393, -24020, -15827, + 15828, 24020, -3393, -25777, -9949, 20627, 20627, -9950, + -25777, -3393, 24020, 15827, -15828, -24020, 3393, 25777, + 9949, -20627, -20627, 9950, 25777, 3393, -24020, -15827, + 15828, 24020, -3393, -25777, -9949, 20627, 20626, -9950, + -25777, -3393, 24020, 15827, -15828, -24020, 3394, 25777, + 9949, -20627, -20626, 9950, 25777, 3393, -24020, -15827, + 15828, 24020, -3394, -25777, -9949, 20627, 20626, -9950, + -25777, -3393, 24021, 15827, -15828, -24020, 3394, 25777, + 9949, -20627, -20626, 9950, 25777, 3393, -24021, -15827, + 15828, 24020, -3394, -25777, -9949, 20627, 20626, -9950, + -25777, -3393, 24021, 15827, -15828, -24020, 3394, 25777}, + },{{ + +// Carrier 40 Phase 0 + 0, 25299, 11668, -19917, -20855, 10298, 25605, 1511, + -24907, -12999, 18911, 21722, -8892, -25824, -3018, 24432, + 14287, -17842, -22516, 7456, 25956, 4514, -23873, -15526, + 16712, 23234, -5996, -26000, -5996, 23234, 16712, -15526, + -23873, 4514, 25956, 7456, -22516, -17842, 14287, 24432, + -3018, -25824, -8892, 21722, 18911, -13000, -24907, 1511, + 25605, 10298, -20855, -19917, 11668, 25299, 0, -25299, + -11668, 19917, 20855, -10298, -25604, -1511, 24907, 12999, + -18911, -21722, 8892, 25824, 3018, -24432, -14287, 17842, + 22516, -7456, -25956, -4514, 23873, 15526, -16712, -23234, + 5996, 26000, 5996, -23234, -16712, 15526, 23873, -4514, + -25956, -7456, 22516, 17842, -14287, -24432, 3018, 25824, + 8892, -21722, -18911, 13000, 24907, -1511, -25605, -10298, + 20855, 19917, -11668, -25299, 0, 25299, 11668, -19917, + -20855, 10298, 25604, 1511, -24907, -12999, 18911, 21722, + -8892, -25824, -3018, 24432, 14287, -17842, -22516, 7456, + 25956, 4514, -23873, -15526, 16712, 23234, -5996, -26000, + -5995, 23234, 16712, -15526, -23873, 4514, 25956, 7456, + -22516, -17842, 14287, 24431, -3018, -25824, -8892, 21722, + 18911, -13000, -24907, 1511, 25605, 10298, -20855, -19917, + 11668, 25299, 0, -25299, -11668, 19917, 20855, -10298, + -25604, -1511, 24907, 12999, -18911, -21722, 8892, 25824, + 3018, -24432, -14287, 17842, 22516, -7456, -25956, -4514, + 23873, 15526, -16712, -23234, 5996, 26000, 5995, -23234, + -16712, 15526, 23873, -4514, -25956, -7456, 22516, 17842, + -14287, -24431, 3018, 25824, 8892, -21722, -18911, 13000, + 24907, -1511, -25605, -10298, 20855, 19917, -11668, -25299}, + { +// Carrier 40 Phase 1 + 9949, 25667, 1889, -24796, -13326, 18650, 21928, -8536, + -25865, -3393, 24300, 14601, -17565, -22703, 7093, 25975, + 4886, -23721, -15827, 16421, 23401, -5627, -25997, -6363, + 23062, 17000, -15221, -24020, 4141, 25931, 7818, -22325, + -18115, 13969, 24558, -2642, -25777, -9246, 21512, 19169, + -12671, -25013, 1134, 25536, 10644, -20627, -20158, 11329, + 25383, 378, -25209, -12005, 19671, 21078, -9949, -25667, + -1889, 24796, 13326, -18650, -21928, 8536, 25865, 3393, + -24300, -14601, 17565, 22703, -7093, -25975, -4886, 23721, + 15827, -16421, -23401, 5627, 25997, 6363, -23062, -17000, + 15221, 24020, -4141, -25931, -7818, 22325, 18115, -13969, + -24558, 2642, 25777, 9246, -21512, -19169, 12671, 25013, + -1134, -25536, -10644, 20627, 20158, -11329, -25383, -378, + 25209, 12005, -19671, -21078, 9949, 25667, 1889, -24796, + -13326, 18650, 21928, -8536, -25865, -3393, 24300, 14601, + -17565, -22703, 7093, 25975, 4886, -23721, -15827, 16421, + 23401, -5627, -25997, -6363, 23062, 17000, -15221, -24020, + 4141, 25931, 7818, -22325, -18115, 13969, 24558, -2642, + -25777, -9246, 21512, 19169, -12671, -25013, 1134, 25536, + 10644, -20627, -20158, 11329, 25383, 378, -25209, -12005, + 19671, 21078, -9949, -25667, -1889, 24796, 13326, -18650, + -21928, 8536, 25865, 3393, -24300, -14601, 17565, 22703, + -7093, -25975, -4886, 23721, 15827, -16421, -23401, 5627, + 25997, 6363, -23062, -17000, 15221, 24020, -4141, -25931, + -7818, 22325, 18115, -13969, -24558, 2642, 25777, 9246, + -21512, -19169, 12671, 25013, -1134, -25536, -10644, 20627, + 20158, -11329, -25383, -378, 25209, 12005, -19671, -21078}, + { +// Carrier 40 Phase 2 + 18384, 22129, -8178, -25901, -3768, 24163, 14912, -17284, + -22885, 6729, 25989, 5257, -23564, -16126, 16126, 23564, + -5257, -25989, -6729, 22885, 17284, -14912, -24163, 3768, + 25901, 8178, -22129, -18384, 13649, 24680, -2266, -25725, + -9599, 21297, 19422, -12339, -25114, 756, 25462, 10988, + -20394, -20394, 10988, 25462, 756, -25114, -12339, 19422, + 21297, -9599, -25725, -2266, 24680, 13649, -18384, -22129, + 8178, 25901, 3768, -24163, -14912, 17284, 22885, -6729, + -25989, -5257, 23564, 16126, -16126, -23564, 5257, 25989, + 6729, -22885, -17284, 14912, 24163, -3768, -25901, -8178, + 22129, 18384, -13649, -24680, 2266, 25725, 9599, -21297, + -19422, 12339, 25114, -756, -25462, -10988, 20394, 20394, + -10988, -25462, -756, 25114, 12339, -19422, -21297, 9599, + 25725, 2266, -24680, -13649, 18384, 22129, -8178, -25901, + -3768, 24163, 14912, -17284, -22885, 6729, 25989, 5257, + -23564, -16126, 16126, 23563, -5257, -25989, -6729, 22885, + 17284, -14912, -24163, 3768, 25901, 8178, -22129, -18384, + 13649, 24680, -2266, -25725, -9599, 21297, 19422, -12339, + -25114, 756, 25462, 10988, -20394, -20394, 10988, 25462, + 756, -25114, -12339, 19422, 21297, -9599, -25725, -2266, + 24680, 13649, -18384, -22129, 8178, 25901, 3768, -24163, + -14912, 17284, 22885, -6729, -25989, -5257, 23564, 16126, + -16126, -23563, 5257, 25989, 6729, -22885, -17284, 14913, + 24163, -3768, -25901, -8178, 22129, 18384, -13649, -24680, + 2266, 25725, 9599, -21297, -19422, 12339, 25114, -756, + -25462, -10988, 20394, 20394, -10988, -25462, -756, 25114, + 12339, -19422, -21297, 9599, 25725, 2266, -24680, -13649}, + { +// Carrier 40 Phase 3 + 24020, 15221, -17000, -23062, 6363, 25997, 5627, -23401, + -16421, 15827, 23721, -4886, -25975, -7093, 22703, 17565, + -14601, -24300, 3393, 25865, 8536, -21928, -18650, 13326, + 24796, -1889, -25667, -9949, 21078, 19671, -12005, -25209, + 378, 25383, 11329, -20158, -20627, 10644, 25536, 1134, + -25013, -12671, 19169, 21512, -9246, -25777, -2642, 24558, + 13969, -18115, -22325, 7818, 25931, 4141, -24020, -15221, + 17000, 23062, -6363, -25997, -5627, 23401, 16421, -15827, + -23721, 4886, 25975, 7093, -22703, -17565, 14601, 24300, + -3393, -25865, -8536, 21928, 18650, -13326, -24796, 1889, + 25667, 9949, -21078, -19671, 12005, 25209, -378, -25383, + -11329, 20158, 20627, -10644, -25536, -1134, 25013, 12671, + -19169, -21512, 9246, 25777, 2642, -24558, -13969, 18115, + 22325, -7818, -25931, -4141, 24020, 15221, -17000, -23062, + 6363, 25997, 5627, -23401, -16421, 15827, 23721, -4886, + -25975, -7093, 22703, 17565, -14601, -24300, 3393, 25865, + 8536, -21928, -18650, 13326, 24796, -1889, -25667, -9949, + 21078, 19671, -12005, -25209, 378, 25383, 11329, -20158, + -20627, 10644, 25536, 1134, -25013, -12671, 19169, 21512, + -9246, -25777, -2642, 24558, 13969, -18115, -22325, 7818, + 25931, 4141, -24020, -15221, 17000, 23062, -6363, -25997, + -5627, 23401, 16421, -15827, -23721, 4886, 25975, 7093, + -22703, -17565, 14601, 24300, -3393, -25865, -8536, 21928, + 18650, -13326, -24796, 1889, 25667, 9949, -21078, -19671, + 12005, 25209, -378, -25383, -11329, 20158, 20627, -10644, + -25536, -1134, 25013, 12671, -19169, -21512, 9246, 25777, + 2642, -24558, -13969, 18115, 22325, -7818, -25931, -4141}, + { +// Carrier 40 Phase 4 + 26000, 5996, -23234, -16712, 15526, 23873, -4514, -25956, + -7456, 22516, 17842, -14287, -24432, 3018, 25824, 8892, + -21722, -18911, 13000, 24907, -1511, -25605, -10298, 20855, + 19917, -11668, -25299, 0, 25299, 11668, -19917, -20855, + 10298, 25604, 1511, -24907, -12999, 18911, 21722, -8892, + -25824, -3018, 24432, 14287, -17842, -22516, 7456, 25956, + 4514, -23873, -15526, 16712, 23234, -5996, -26000, -5996, + 23234, 16712, -15526, -23873, 4514, 25956, 7456, -22516, + -17842, 14287, 24432, -3018, -25824, -8892, 21722, 18911, + -13000, -24907, 1511, 25605, 10298, -20855, -19917, 11668, + 25299, 0, -25299, -11668, 19917, 20855, -10298, -25604, + -1511, 24907, 12999, -18911, -21722, 8892, 25824, 3018, + -24432, -14287, 17842, 22516, -7456, -25956, -4514, 23873, + 15526, -16712, -23234, 5996, 26000, 5995, -23234, -16712, + 15526, 23873, -4514, -25956, -7456, 22516, 17842, -14287, + -24432, 3018, 25824, 8892, -21722, -18911, 13000, 24907, + -1511, -25605, -10298, 20855, 19917, -11668, -25299, 0, + 25299, 11668, -19917, -20855, 10298, 25604, 1511, -24907, + -12999, 18911, 21722, -8892, -25824, -3018, 24432, 14287, + -17842, -22516, 7456, 25956, 4514, -23873, -15526, 16712, + 23234, -5996, -26000, -5995, 23234, 16712, -15526, -23873, + 4514, 25956, 7456, -22516, -17842, 14287, 24431, -3018, + -25824, -8892, 21722, 18911, -13000, -24907, 1511, 25605, + 10298, -20855, -19917, 11668, 25299, 0, -25299, -11668, + 19917, 20855, -10298, -25604, -1511, 24907, 12999, -18911, + -21722, 8892, 25824, 3018, -24432, -14287, 17842, 22516, + -7456, -25956, -4514, 23873, 15526, -16712, -23234, 5996}, + { +// Carrier 40 Phase 5 + 24020, -4141, -25931, -7818, 22325, 18115, -13969, -24558, + 2642, 25777, 9246, -21512, -19169, 12671, 25013, -1134, + -25536, -10644, 20627, 20158, -11329, -25383, -378, 25209, + 12005, -19671, -21078, 9949, 25667, 1889, -24796, -13326, + 18650, 21928, -8536, -25865, -3393, 24300, 14601, -17565, + -22703, 7093, 25975, 4886, -23721, -15827, 16421, 23401, + -5627, -25997, -6363, 23062, 17000, -15221, -24020, 4141, + 25931, 7818, -22325, -18115, 13969, 24558, -2642, -25777, + -9246, 21512, 19169, -12671, -25013, 1134, 25536, 10644, + -20627, -20158, 11329, 25383, 378, -25209, -12005, 19671, + 21078, -9949, -25667, -1889, 24796, 13326, -18650, -21928, + 8536, 25865, 3393, -24300, -14601, 17565, 22703, -7093, + -25975, -4886, 23721, 15827, -16421, -23401, 5627, 25997, + 6363, -23062, -17000, 15221, 24020, -4141, -25931, -7818, + 22325, 18115, -13969, -24558, 2642, 25777, 9246, -21512, + -19169, 12671, 25013, -1134, -25536, -10644, 20627, 20158, + -11329, -25383, -378, 25209, 12005, -19671, -21078, 9949, + 25667, 1889, -24796, -13326, 18650, 21928, -8536, -25865, + -3393, 24300, 14601, -17565, -22703, 7093, 25975, 4886, + -23721, -15827, 16421, 23401, -5627, -25997, -6363, 23062, + 17000, -15221, -24020, 4141, 25931, 7818, -22325, -18115, + 13969, 24558, -2642, -25777, -9246, 21512, 19169, -12671, + -25013, 1134, 25536, 10644, -20627, -20158, 11329, 25383, + 378, -25209, -12005, 19671, 21078, -9949, -25667, -1889, + 24796, 13326, -18650, -21928, 8536, 25865, 3393, -24300, + -14601, 17565, 22703, -7093, -25975, -4886, 23721, 15827, + -16421, -23401, 5627, 25997, 6363, -23062, -17000, 15221}, + { +// Carrier 40 Phase 6 + 18384, -13649, -24680, 2266, 25725, 9599, -21297, -19422, + 12339, 25114, -756, -25462, -10988, 20394, 20394, -10988, + -25462, -756, 25114, 12339, -19422, -21297, 9599, 25725, + 2266, -24680, -13649, 18384, 22129, -8178, -25901, -3768, + 24163, 14912, -17284, -22885, 6729, 25989, 5257, -23564, + -16126, 16126, 23564, -5257, -25989, -6729, 22885, 17284, + -14912, -24163, 3768, 25901, 8178, -22129, -18384, 13649, + 24680, -2266, -25725, -9599, 21297, 19422, -12339, -25114, + 756, 25462, 10988, -20394, -20394, 10988, 25462, 756, + -25114, -12339, 19422, 21297, -9599, -25725, -2266, 24680, + 13649, -18384, -22129, 8178, 25901, 3768, -24163, -14912, + 17284, 22885, -6729, -25989, -5257, 23564, 16126, -16126, + -23564, 5257, 25989, 6729, -22885, -17284, 14912, 24163, + -3768, -25901, -8178, 22129, 18384, -13649, -24680, 2266, + 25725, 9599, -21297, -19422, 12339, 25114, -756, -25462, + -10988, 20394, 20394, -10988, -25462, -756, 25114, 12339, + -19422, -21297, 9599, 25725, 2266, -24680, -13649, 18384, + 22129, -8178, -25901, -3768, 24163, 14912, -17284, -22885, + 6729, 25989, 5257, -23564, -16126, 16126, 23563, -5257, + -25989, -6729, 22885, 17284, -14912, -24163, 3768, 25901, + 8178, -22129, -18384, 13649, 24680, -2266, -25725, -9599, + 21297, 19422, -12339, -25114, 756, 25462, 10988, -20394, + -20394, 10988, 25462, 756, -25114, -12339, 19422, 21297, + -9599, -25725, -2266, 24680, 13649, -18384, -22129, 8178, + 25901, 3768, -24163, -14912, 17284, 22885, -6729, -25989, + -5257, 23564, 16126, -16126, -23563, 5257, 25989, 6729, + -22885, -17284, 14913, 24163, -3768, -25901, -8178, 22129}, + { +// Carrier 40 Phase 7 + 9949, -21078, -19671, 12005, 25209, -378, -25383, -11329, + 20158, 20627, -10644, -25536, -1134, 25013, 12671, -19169, + -21512, 9246, 25777, 2642, -24558, -13969, 18115, 22325, + -7818, -25931, -4141, 24020, 15221, -17000, -23062, 6363, + 25997, 5627, -23401, -16421, 15827, 23721, -4886, -25975, + -7093, 22703, 17565, -14601, -24300, 3393, 25865, 8536, + -21928, -18650, 13326, 24796, -1889, -25667, -9949, 21078, + 19671, -12005, -25209, 378, 25383, 11329, -20158, -20627, + 10644, 25536, 1134, -25013, -12671, 19169, 21512, -9246, + -25777, -2642, 24558, 13969, -18115, -22325, 7818, 25931, + 4141, -24020, -15221, 17000, 23062, -6363, -25997, -5627, + 23401, 16421, -15827, -23721, 4886, 25975, 7093, -22703, + -17565, 14601, 24300, -3393, -25865, -8536, 21928, 18650, + -13326, -24796, 1889, 25667, 9949, -21078, -19671, 12005, + 25209, -378, -25383, -11329, 20158, 20627, -10644, -25536, + -1134, 25013, 12671, -19169, -21512, 9246, 25777, 2642, + -24558, -13969, 18115, 22325, -7818, -25931, -4141, 24020, + 15221, -17000, -23062, 6363, 25997, 5627, -23401, -16421, + 15827, 23721, -4886, -25975, -7093, 22703, 17565, -14601, + -24300, 3393, 25865, 8536, -21928, -18650, 13326, 24796, + -1889, -25667, -9949, 21078, 19671, -12005, -25209, 378, + 25383, 11329, -20158, -20627, 10644, 25536, 1134, -25013, + -12671, 19169, 21512, -9246, -25777, -2642, 24558, 13969, + -18115, -22325, 7818, 25931, 4141, -24020, -15221, 17000, + 23062, -6363, -25997, -5627, 23401, 16421, -15827, -23721, + 4886, 25975, 7093, -22703, -17565, 14601, 24300, -3393, + -25865, -8536, 21928, 18650, -13326, -24796, 1889, 25667}, + },{{ + +// Carrier 41 Phase 0 + 0, 25462, 10298, -21297, -18911, 13649, 24432, -3768, + -25956, -6729, 23234, 16126, -16712, -22885, 7456, 25901, + 3018, -24680, -12999, 19422, 20855, -10988, -25299, 756, + 25605, 9599, -21722, -18384, 14287, 24163, -4514, -25989, + -5995, 23564, 15526, -17284, -22516, 8178, 25824, 2266, + -24907, -12339, 19917, 20394, -11668, -25114, 1511, 25725, + 8892, -22129, -17842, 14913, 23873, -5257, -26000, -5257, + 23873, 14912, -17842, -22129, 8892, 25725, 1511, -25114, + -11668, 20394, 19917, -12339, -24907, 2266, 25824, 8178, + -22516, -17284, 15526, 23563, -5996, -25988, -4514, 24163, + 14287, -18384, -21722, 9599, 25604, 756, -25299, -10987, + 20855, 19422, -13000, -24680, 3018, 25901, 7456, -22885, + -16712, 16126, 23234, -6729, -25956, -3768, 24432, 13649, + -18911, -21297, 10298, 25462, 0, -25462, -10297, 21298, + 18911, -13649, -24431, 3768, 25956, 6729, -23234, -16125, + 16712, 22885, -7457, -25901, -3018, 24680, 12999, -19422, + -20855, 10988, 25299, -756, -25605, -9599, 21722, 18384, + -14287, -24162, 4515, 25989, 5995, -23564, -15525, 17284, + 22516, -8178, -25824, -2265, 24907, 12339, -19917, -20394, + 11668, 25114, -1511, -25725, -8892, 22129, 17842, -14913, + -23873, 5257, 26000, 5257, -23873, -14912, 17842, 22128, + -8892, -25725, -1511, 25114, 11668, -20394, -19916, 12339, + 24907, -2266, -25824, -8177, 22516, 17284, -15526, -23563, + 5996, 25988, 4514, -24163, -14286, 18384, 21722, -9599, + -25604, -755, 25299, 10987, -20855, -19422, 13000, 24680, + -3018, -25901, -7456, 22885, 16712, -16126, -23234, 6729, + 25955, 3767, -24432, -13649, 18911, 21297, -10298, -25462}, + { +// Carrier 41 Phase 1 + 9949, 25536, 378, -25383, -10644, 21078, 19169, -13326, + -24558, 3393, 25931, 7093, -23062, -16421, 16421, 23062, + -7093, -25931, -3393, 24558, 13326, -19169, -21078, 10644, + 25383, -378, -25536, -9949, 21512, 18650, -13969, -24300, + 4142, 25975, 6363, -23401, -15827, 17000, 22703, -7818, + -25865, -2642, 24796, 12671, -19672, -20627, 11329, 25209, + -1134, -25667, -9246, 21928, 18115, -14601, -24020, 4886, + 25997, 5627, -23721, -15221, 17565, 22325, -8536, -25777, + -1889, 25013, 12005, -20158, -20158, 12005, 25013, -1889, + -25777, -8536, 22325, 17565, -15221, -23721, 5627, 25997, + 4886, -24020, -14601, 18115, 21928, -9247, -25667, -1133, + 25209, 11329, -20627, -19671, 12671, 24796, -2642, -25865, + -7818, 22703, 17000, -15827, -23401, 6363, 25975, 4141, + -24300, -13969, 18650, 21512, -9949, -25536, -377, 25383, + 10644, -21078, -19169, 13326, 24558, -3393, -25931, -7093, + 23062, 16420, -16421, -23062, 7094, 25931, 3393, -24558, + -13325, 19169, 21078, -10644, -25383, 378, 25536, 9949, + -21512, -18650, 13969, 24300, -4142, -25975, -6363, 23401, + 15827, -17000, -22703, 7818, 25865, 2642, -24796, -12670, + 19672, 20627, -11329, -25209, 1134, 25667, 9246, -21928, + -18115, 14601, 24020, -4887, -25997, -5627, 23721, 15220, + -17565, -22325, 8536, 25777, 1888, -25013, -12005, 20158, + 20157, -12005, -25013, 1889, 25777, 8535, -22325, -17565, + 15221, 23721, -5627, -25997, -4886, 24020, 14601, -18115, + -21928, 9247, 25667, 1133, -25209, -11329, 20627, 19671, + -12671, -24796, 2642, 25865, 7818, -22703, -17000, 15828, + 23401, -6363, -25975, -4141, 24300, 13969, -18650, -21512}, + { +// Carrier 41 Phase 2 + 18384, 21722, -9599, -25605, -756, 25299, 10988, -20855, + -19422, 13000, 24680, -3018, -25901, -7456, 22885, 16712, + -16126, -23234, 6729, 25956, 3768, -24432, -13649, 18911, + 21297, -10298, -25462, 0, 25462, 10298, -21297, -18911, + 13649, 24431, -3768, -25956, -6729, 23234, 16126, -16712, + -22885, 7456, 25901, 3018, -24680, -12999, 19422, 20855, + -10988, -25299, 756, 25605, 9599, -21722, -18384, 14287, + 24163, -4514, -25989, -5995, 23564, 15526, -17284, -22516, + 8178, 25824, 2265, -24907, -12339, 19917, 20394, -11668, + -25114, 1511, 25725, 8892, -22129, -17842, 14913, 23873, + -5257, -26000, -5257, 23873, 14912, -17842, -22128, 8892, + 25725, 1511, -25114, -11668, 20394, 19917, -12339, -24907, + 2266, 25824, 8178, -22516, -17284, 15526, 23563, -5996, + -25988, -4514, 24163, 14287, -18384, -21722, 9599, 25604, + 756, -25299, -10987, 20855, 19422, -13000, -24680, 3018, + 25901, 7456, -22885, -16712, 16126, 23234, -6729, -25956, + -3768, 24432, 13649, -18911, -21297, 10298, 25462, 0, + -25462, -10297, 21298, 18911, -13649, -24431, 3768, 25956, + 6729, -23234, -16125, 16712, 22885, -7457, -25901, -3018, + 24680, 12999, -19422, -20855, 10988, 25299, -756, -25605, + -9599, 21722, 18384, -14287, -24162, 4515, 25989, 5995, + -23564, -15525, 17284, 22516, -8178, -25824, -2265, 24907, + 12339, -19917, -20394, 11669, 25113, -1512, -25725, -8892, + 22129, 17842, -14913, -23873, 5257, 26000, 5257, -23873, + -14912, 17842, 22128, -8892, -25725, -1511, 25114, 11668, + -20395, -19916, 12339, 24907, -2266, -25824, -8177, 22516, + 17284, -15526, -23563, 5996, 25988, 4514, -24163, -14286}, + { +// Carrier 41 Phase 3 + 24020, 14601, -18115, -21928, 9246, 25667, 1134, -25209, + -11329, 20627, 19671, -12671, -24796, 2642, 25865, 7818, + -22703, -17000, 15827, 23401, -6363, -25975, -4141, 24300, + 13969, -18650, -21512, 9949, 25536, 378, -25383, -10644, + 21078, 19169, -13326, -24558, 3393, 25931, 7093, -23062, + -16420, 16421, 23062, -7093, -25931, -3393, 24558, 13326, + -19169, -21078, 10644, 25383, -378, -25536, -9949, 21512, + 18650, -13969, -24300, 4142, 25975, 6363, -23401, -15827, + 17000, 22703, -7818, -25865, -2642, 24796, 12671, -19672, + -20627, 11329, 25209, -1134, -25667, -9246, 21928, 18115, + -14601, -24020, 4886, 25997, 5627, -23721, -15221, 17565, + 22325, -8536, -25777, -1888, 25013, 12005, -20158, -20158, + 12005, 25013, -1889, -25777, -8536, 22325, 17565, -15221, + -23721, 5627, 25997, 4886, -24020, -14601, 18115, 21928, + -9247, -25667, -1133, 25209, 11329, -20627, -19671, 12671, + 24796, -2642, -25865, -7818, 22703, 17000, -15827, -23401, + 6363, 25975, 4141, -24300, -13969, 18650, 21512, -9949, + -25536, -377, 25383, 10643, -21078, -19169, 13326, 24558, + -3393, -25931, -7093, 23062, 16420, -16421, -23062, 7094, + 25931, 3393, -24558, -13325, 19169, 21078, -10644, -25383, + 378, 25536, 9949, -21512, -18650, 13970, 24299, -4142, + -25975, -6363, 23401, 15827, -17000, -22703, 7818, 25865, + 2642, -24796, -12670, 19672, 20627, -11329, -25209, 1134, + 25668, 9246, -21928, -18115, 14601, 24020, -4887, -25997, + -5627, 23721, 15220, -17565, -22325, 8536, 25777, 1888, + -25013, -12005, 20158, 20157, -12005, -25013, 1889, 25777, + 8535, -22325, -17565, 15221, 23721, -5627, -25997, -4886}, + { +// Carrier 41 Phase 4 + 26000, 5257, -23873, -14912, 17842, 22129, -8892, -25725, + -1511, 25114, 11668, -20394, -19917, 12339, 24907, -2266, + -25824, -8178, 22516, 17284, -15526, -23563, 5996, 25989, + 4514, -24163, -14287, 18384, 21722, -9599, -25604, -756, + 25299, 10988, -20855, -19422, 13000, 24680, -3018, -25901, + -7456, 22885, 16712, -16126, -23234, 6729, 25956, 3768, + -24432, -13649, 18911, 21297, -10298, -25462, 0, 25462, + 10297, -21298, -18911, 13649, 24431, -3768, -25956, -6729, + 23234, 16126, -16712, -22885, 7456, 25901, 3018, -24680, + -12999, 19422, 20855, -10988, -25299, 756, 25605, 9599, + -21722, -18384, 14287, 24162, -4514, -25989, -5995, 23564, + 15526, -17284, -22516, 8178, 25824, 2265, -24907, -12339, + 19917, 20394, -11668, -25114, 1511, 25725, 8892, -22129, + -17842, 14913, 23873, -5257, -26000, -5257, 23873, 14912, + -17842, -22128, 8892, 25725, 1511, -25114, -11668, 20394, + 19917, -12339, -24907, 2266, 25824, 8177, -22516, -17284, + 15526, 23563, -5996, -25988, -4514, 24163, 14287, -18384, + -21722, 9599, 25604, 755, -25299, -10987, 20855, 19422, + -13000, -24680, 3018, 25901, 7456, -22885, -16712, 16126, + 23234, -6729, -25955, -3767, 24432, 13649, -18911, -21297, + 10298, 25462, 0, -25462, -10297, 21298, 18911, -13649, + -24431, 3768, 25956, 6729, -23234, -16125, 16712, 22885, + -7457, -25901, -3018, 24680, 12999, -19422, -20855, 10988, + 25299, -756, -25605, -9599, 21722, 18384, -14287, -24162, + 4515, 25989, 5995, -23564, -15525, 17284, 22516, -8178, + -25824, -2265, 24907, 12339, -19917, -20394, 11669, 25113, + -1512, -25725, -8892, 22129, 17842, -14913, -23873, 5257}, + { +// Carrier 41 Phase 5 + 24020, -4886, -25997, -5627, 23721, 15221, -17565, -22325, + 8536, 25777, 1889, -25013, -12005, 20158, 20158, -12005, + -25013, 1889, 25777, 8536, -22325, -17565, 15221, 23721, + -5627, -25997, -4886, 24020, 14601, -18115, -21928, 9246, + 25667, 1134, -25209, -11329, 20627, 19671, -12671, -24796, + 2642, 25865, 7818, -22703, -17000, 15827, 23401, -6363, + -25975, -4141, 24300, 13969, -18650, -21512, 9949, 25536, + 378, -25383, -10644, 21078, 19169, -13326, -24558, 3393, + 25931, 7093, -23062, -16420, 16421, 23062, -7093, -25931, + -3393, 24558, 13326, -19169, -21078, 10644, 25383, -378, + -25536, -9949, 21512, 18650, -13969, -24300, 4142, 25975, + 6363, -23401, -15827, 17000, 22703, -7818, -25865, -2642, + 24796, 12671, -19672, -20627, 11329, 25209, -1134, -25667, + -9246, 21928, 18115, -14601, -24020, 4886, 25997, 5627, + -23721, -15221, 17565, 22325, -8536, -25777, -1888, 25013, + 12005, -20158, -20157, 12005, 25013, -1889, -25777, -8536, + 22325, 17565, -15221, -23721, 5627, 25997, 4886, -24020, + -14601, 18115, 21928, -9247, -25667, -1133, 25209, 11329, + -20627, -19671, 12671, 24796, -2642, -25865, -7818, 22703, + 17000, -15828, -23401, 6363, 25975, 4141, -24300, -13969, + 18650, 21512, -9950, -25536, -377, 25383, 10643, -21078, + -19169, 13326, 24558, -3393, -25931, -7093, 23062, 16420, + -16421, -23062, 7094, 25931, 3393, -24558, -13325, 19169, + 21078, -10644, -25383, 378, 25536, 9949, -21512, -18649, + 13970, 24299, -4142, -25975, -6363, 23401, 15827, -17000, + -22703, 7818, 25865, 2642, -24796, -12670, 19672, 20626, + -11329, -25209, 1134, 25668, 9246, -21928, -18115, 14601}, + { +// Carrier 41 Phase 6 + 18384, -14287, -24163, 4514, 25989, 5996, -23564, -15526, + 17284, 22516, -8178, -25824, -2266, 24907, 12339, -19917, + -20394, 11668, 25114, -1511, -25725, -8892, 22129, 17842, + -14913, -23873, 5257, 26000, 5257, -23873, -14912, 17842, + 22129, -8892, -25725, -1511, 25114, 11668, -20394, -19917, + 12339, 24907, -2266, -25824, -8178, 22516, 17284, -15526, + -23563, 5996, 25988, 4514, -24163, -14287, 18384, 21722, + -9599, -25604, -756, 25299, 10987, -20855, -19422, 13000, + 24680, -3018, -25901, -7456, 22885, 16712, -16126, -23234, + 6729, 25956, 3768, -24432, -13649, 18911, 21297, -10298, + -25462, 0, 25462, 10297, -21298, -18911, 13649, 24431, + -3768, -25956, -6729, 23234, 16126, -16712, -22885, 7457, + 25901, 3018, -24680, -12999, 19422, 20855, -10988, -25299, + 756, 25605, 9599, -21722, -18384, 14287, 24162, -4515, + -25989, -5995, 23564, 15525, -17284, -22516, 8178, 25824, + 2265, -24907, -12339, 19917, 20394, -11668, -25114, 1511, + 25725, 8892, -22129, -17842, 14913, 23873, -5257, -26000, + -5257, 23873, 14912, -17842, -22128, 8892, 25725, 1511, + -25114, -11668, 20394, 19917, -12339, -24907, 2266, 25824, + 8177, -22516, -17284, 15526, 23563, -5996, -25988, -4514, + 24163, 14287, -18384, -21722, 9599, 25604, 755, -25299, + -10987, 20855, 19422, -13000, -24680, 3018, 25901, 7456, + -22885, -16712, 16126, 23234, -6729, -25955, -3767, 24432, + 13649, -18911, -21297, 10298, 25462, 0, -25462, -10297, + 21298, 18911, -13649, -24431, 3768, 25956, 6728, -23234, + -16125, 16712, 22885, -7457, -25901, -3018, 24680, 12999, + -19422, -20855, 10988, 25299, -756, -25605, -9599, 21722}, + { +// Carrier 41 Phase 7 + 9949, -21512, -18650, 13969, 24300, -4141, -25975, -6363, + 23401, 15827, -17000, -22703, 7818, 25865, 2642, -24796, + -12671, 19671, 20627, -11329, -25209, 1134, 25667, 9246, + -21928, -18115, 14601, 24020, -4886, -25997, -5627, 23721, + 15221, -17565, -22325, 8536, 25777, 1889, -25013, -12005, + 20158, 20158, -12005, -25013, 1889, 25777, 8536, -22325, + -17565, 15221, 23721, -5627, -25997, -4886, 24020, 14601, + -18115, -21928, 9246, 25667, 1134, -25209, -11329, 20627, + 19671, -12671, -24796, 2642, 25865, 7818, -22703, -17000, + 15827, 23401, -6363, -25975, -4141, 24300, 13969, -18650, + -21512, 9949, 25536, 378, -25383, -10644, 21078, 19169, + -13326, -24558, 3393, 25931, 7093, -23062, -16420, 16421, + 23062, -7093, -25931, -3393, 24558, 13325, -19169, -21078, + 10644, 25383, -378, -25536, -9949, 21512, 18650, -13969, + -24300, 4142, 25975, 6363, -23401, -15827, 17000, 22703, + -7818, -25865, -2642, 24796, 12670, -19672, -20627, 11329, + 25209, -1134, -25667, -9246, 21928, 18115, -14601, -24020, + 4886, 25997, 5627, -23721, -15220, 17565, 22325, -8536, + -25777, -1888, 25013, 12005, -20158, -20157, 12005, 25013, + -1889, -25777, -8536, 22325, 17565, -15221, -23721, 5627, + 25997, 4886, -24020, -14601, 18115, 21928, -9247, -25667, + -1133, 25209, 11329, -20627, -19671, 12671, 24796, -2642, + -25865, -7818, 22703, 17000, -15828, -23401, 6363, 25975, + 4141, -24300, -13969, 18650, 21512, -9950, -25536, -377, + 25383, 10643, -21078, -19168, 13326, 24558, -3393, -25931, + -7093, 23062, 16420, -16421, -23062, 7094, 25931, 3393, + -24558, -13325, 19169, 21078, -10644, -25383, 378, 25536}, + },{{ + +// Carrier 42 Phase 0 + 0, 25605, 8892, -22516, -16712, 16712, 22516, -8892, + -25605, 0, 25605, 8892, -22516, -16712, 16712, 22516, + -8892, -25605, 0, 25605, 8892, -22516, -16712, 16712, + 22516, -8892, -25605, 0, 25605, 8892, -22516, -16712, + 16712, 22516, -8892, -25605, 0, 25605, 8892, -22516, + -16712, 16712, 22516, -8892, -25605, 0, 25605, 8892, + -22516, -16712, 16712, 22516, -8892, -25605, 0, 25605, + 8892, -22516, -16712, 16712, 22516, -8892, -25605, 0, + 25605, 8892, -22516, -16712, 16712, 22516, -8892, -25605, + 0, 25605, 8892, -22516, -16712, 16712, 22516, -8892, + -25605, 0, 25605, 8892, -22516, -16712, 16712, 22516, + -8892, -25605, 0, 25605, 8892, -22516, -16712, 16712, + 22516, -8892, -25605, 0, 25605, 8892, -22516, -16712, + 16712, 22516, -8892, -25605, 0, 25605, 8892, -22516, + -16712, 16712, 22516, -8892, -25605, 0, 25605, 8892, + -22516, -16712, 16712, 22516, -8892, -25605, 0, 25605, + 8892, -22516, -16712, 16712, 22516, -8892, -25605, 0, + 25605, 8892, -22516, -16712, 16712, 22516, -8892, -25605, + 0, 25605, 8892, -22516, -16712, 16712, 22516, -8892, + -25605, 0, 25605, 8892, -22516, -16712, 16712, 22516, + -8892, -25605, 0, 25605, 8892, -22516, -16712, 16712, + 22516, -8892, -25605, 0, 25605, 8892, -22516, -16712, + 16712, 22516, -8892, -25605, 0, 25605, 8892, -22516, + -16712, 16712, 22516, -8892, -25605, 0, 25605, 8892, + -22516, -16712, 16712, 22516, -8892, -25605, 0, 25605, + 8892, -22516, -16712, 16712, 22516, -8892, -25605, 0, + 25605, 8892, -22516, -16712, 16712, 22516, -8892, -25605}, + { +// Carrier 42 Phase 1 + 9949, 25383, -1134, -25777, -7818, 23062, 15827, -17565, + -21928, 9949, 25383, -1134, -25777, -7818, 23062, 15827, + -17565, -21928, 9949, 25383, -1134, -25777, -7818, 23062, + 15827, -17565, -21928, 9949, 25383, -1134, -25777, -7818, + 23062, 15827, -17565, -21928, 9949, 25383, -1134, -25777, + -7818, 23062, 15827, -17565, -21928, 9949, 25383, -1134, + -25777, -7818, 23062, 15827, -17565, -21928, 9949, 25383, + -1134, -25777, -7818, 23062, 15827, -17565, -21928, 9949, + 25383, -1134, -25777, -7818, 23062, 15827, -17565, -21928, + 9949, 25383, -1134, -25777, -7818, 23062, 15827, -17565, + -21928, 9949, 25383, -1134, -25777, -7818, 23062, 15827, + -17565, -21928, 9949, 25383, -1134, -25777, -7818, 23062, + 15827, -17565, -21928, 9949, 25383, -1134, -25777, -7818, + 23062, 15827, -17565, -21928, 9949, 25383, -1134, -25777, + -7818, 23062, 15827, -17565, -21928, 9949, 25383, -1134, + -25777, -7818, 23062, 15827, -17565, -21928, 9949, 25383, + -1134, -25777, -7818, 23062, 15827, -17565, -21928, 9949, + 25383, -1134, -25777, -7818, 23062, 15827, -17565, -21928, + 9949, 25383, -1134, -25777, -7818, 23062, 15827, -17565, + -21928, 9949, 25383, -1134, -25777, -7818, 23062, 15827, + -17565, -21928, 9949, 25383, -1134, -25777, -7818, 23062, + 15827, -17565, -21928, 9949, 25383, -1134, -25777, -7818, + 23062, 15827, -17565, -21928, 9949, 25383, -1134, -25777, + -7818, 23062, 15827, -17565, -21928, 9949, 25383, -1134, + -25777, -7818, 23062, 15827, -17565, -21928, 9949, 25383, + -1134, -25777, -7818, 23062, 15827, -17565, -21928, 9949, + 25383, -1134, -25777, -7818, 23062, 15827, -17565, -21928}, + { +// Carrier 42 Phase 2 + 18384, 21297, -10988, -25114, 2266, 25901, 6729, -23564, + -14912, 18384, 21297, -10988, -25114, 2266, 25901, 6729, + -23564, -14912, 18384, 21297, -10988, -25114, 2266, 25901, + 6729, -23564, -14912, 18384, 21297, -10988, -25114, 2266, + 25901, 6729, -23564, -14912, 18384, 21297, -10988, -25114, + 2266, 25901, 6729, -23564, -14912, 18384, 21297, -10988, + -25114, 2266, 25901, 6729, -23564, -14912, 18384, 21297, + -10988, -25114, 2266, 25901, 6729, -23564, -14912, 18384, + 21297, -10988, -25114, 2266, 25901, 6729, -23564, -14912, + 18384, 21297, -10988, -25114, 2266, 25901, 6729, -23564, + -14912, 18384, 21297, -10988, -25114, 2266, 25901, 6729, + -23564, -14912, 18384, 21297, -10988, -25114, 2266, 25901, + 6729, -23564, -14912, 18384, 21297, -10988, -25114, 2266, + 25901, 6729, -23564, -14912, 18384, 21297, -10988, -25114, + 2266, 25901, 6729, -23564, -14912, 18384, 21297, -10988, + -25114, 2266, 25901, 6729, -23564, -14912, 18384, 21297, + -10988, -25114, 2266, 25901, 6729, -23564, -14912, 18384, + 21297, -10988, -25114, 2266, 25901, 6729, -23564, -14912, + 18384, 21297, -10988, -25114, 2266, 25901, 6729, -23564, + -14912, 18384, 21297, -10988, -25114, 2266, 25901, 6729, + -23564, -14912, 18384, 21297, -10988, -25114, 2266, 25901, + 6729, -23564, -14912, 18384, 21297, -10988, -25114, 2266, + 25901, 6729, -23564, -14912, 18384, 21297, -10988, -25114, + 2266, 25901, 6729, -23564, -14912, 18384, 21297, -10988, + -25114, 2266, 25901, 6729, -23564, -14912, 18384, 21297, + -10988, -25114, 2266, 25901, 6729, -23564, -14912, 18384, + 21297, -10988, -25114, 2266, 25901, 6729, -23564, -14912}, + { +// Carrier 42 Phase 3 + 24020, 13969, -19169, -20627, 12005, 24796, -3393, -25975, + -5627, 24020, 13969, -19169, -20627, 12005, 24796, -3393, + -25975, -5627, 24020, 13969, -19169, -20627, 12005, 24796, + -3393, -25975, -5627, 24020, 13969, -19169, -20627, 12005, + 24796, -3393, -25975, -5627, 24020, 13969, -19169, -20627, + 12005, 24796, -3393, -25975, -5627, 24020, 13969, -19169, + -20627, 12005, 24796, -3393, -25975, -5627, 24020, 13969, + -19169, -20627, 12005, 24796, -3393, -25975, -5627, 24020, + 13969, -19169, -20627, 12005, 24796, -3393, -25975, -5627, + 24020, 13969, -19169, -20627, 12005, 24796, -3393, -25975, + -5627, 24020, 13969, -19169, -20627, 12005, 24796, -3393, + -25975, -5627, 24020, 13969, -19169, -20627, 12005, 24796, + -3393, -25975, -5627, 24020, 13969, -19169, -20627, 12005, + 24796, -3393, -25975, -5627, 24020, 13969, -19169, -20627, + 12005, 24796, -3393, -25975, -5627, 24020, 13969, -19169, + -20627, 12005, 24796, -3393, -25975, -5627, 24020, 13969, + -19169, -20627, 12005, 24796, -3393, -25975, -5627, 24020, + 13969, -19169, -20627, 12005, 24796, -3393, -25975, -5627, + 24020, 13969, -19169, -20627, 12005, 24796, -3393, -25975, + -5627, 24020, 13969, -19169, -20627, 12005, 24796, -3393, + -25975, -5627, 24020, 13969, -19169, -20627, 12005, 24796, + -3393, -25975, -5627, 24020, 13969, -19169, -20627, 12005, + 24796, -3393, -25975, -5627, 24020, 13969, -19169, -20627, + 12005, 24796, -3393, -25975, -5627, 24020, 13969, -19169, + -20627, 12005, 24796, -3393, -25975, -5627, 24020, 13969, + -19169, -20627, 12005, 24796, -3393, -25975, -5627, 24020, + 13969, -19169, -20627, 12005, 24796, -3393, -25975, -5627}, + { +// Carrier 42 Phase 4 + 26000, 4514, -24432, -13000, 19917, 19917, -12999, -24432, + 4514, 26000, 4514, -24432, -13000, 19917, 19917, -12999, + -24432, 4514, 26000, 4514, -24432, -13000, 19917, 19917, + -12999, -24432, 4514, 26000, 4514, -24432, -13000, 19917, + 19917, -12999, -24432, 4514, 26000, 4514, -24432, -13000, + 19917, 19917, -12999, -24432, 4514, 26000, 4514, -24432, + -13000, 19917, 19917, -12999, -24432, 4514, 26000, 4514, + -24432, -13000, 19917, 19917, -12999, -24432, 4514, 26000, + 4514, -24432, -13000, 19917, 19917, -12999, -24432, 4514, + 26000, 4514, -24432, -13000, 19917, 19917, -12999, -24432, + 4514, 26000, 4514, -24432, -13000, 19917, 19917, -12999, + -24432, 4514, 26000, 4514, -24432, -13000, 19917, 19917, + -12999, -24432, 4514, 26000, 4514, -24432, -13000, 19917, + 19917, -12999, -24432, 4514, 26000, 4514, -24432, -13000, + 19917, 19917, -12999, -24432, 4514, 26000, 4514, -24432, + -13000, 19917, 19917, -12999, -24432, 4514, 26000, 4514, + -24432, -13000, 19917, 19917, -12999, -24432, 4514, 26000, + 4514, -24432, -13000, 19917, 19917, -12999, -24432, 4514, + 26000, 4514, -24432, -13000, 19917, 19917, -12999, -24432, + 4514, 26000, 4514, -24432, -13000, 19917, 19917, -12999, + -24432, 4514, 26000, 4514, -24432, -13000, 19917, 19917, + -12999, -24432, 4514, 26000, 4514, -24432, -13000, 19917, + 19917, -12999, -24432, 4514, 26000, 4514, -24432, -13000, + 19917, 19917, -12999, -24432, 4514, 26000, 4514, -24432, + -13000, 19917, 19917, -12999, -24432, 4514, 26000, 4514, + -24432, -13000, 19917, 19917, -12999, -24432, 4514, 26000, + 4514, -24432, -13000, 19917, 19917, -12999, -24432, 4514}, + { +// Carrier 42 Phase 5 + 24020, -5627, -25975, -3393, 24796, 12005, -20627, -19169, + 13969, 24020, -5627, -25975, -3393, 24796, 12005, -20627, + -19169, 13969, 24020, -5627, -25975, -3393, 24796, 12005, + -20627, -19169, 13969, 24020, -5627, -25975, -3393, 24796, + 12005, -20627, -19169, 13969, 24020, -5627, -25975, -3393, + 24796, 12005, -20627, -19169, 13969, 24020, -5627, -25975, + -3393, 24796, 12005, -20627, -19169, 13969, 24020, -5627, + -25975, -3393, 24796, 12005, -20627, -19169, 13969, 24020, + -5627, -25975, -3393, 24796, 12005, -20627, -19169, 13969, + 24020, -5627, -25975, -3393, 24796, 12005, -20627, -19169, + 13969, 24020, -5627, -25975, -3393, 24796, 12005, -20627, + -19169, 13969, 24020, -5627, -25975, -3393, 24796, 12005, + -20627, -19169, 13969, 24020, -5627, -25975, -3393, 24796, + 12005, -20627, -19169, 13969, 24020, -5627, -25975, -3393, + 24796, 12005, -20627, -19169, 13969, 24020, -5627, -25975, + -3393, 24796, 12005, -20627, -19169, 13969, 24020, -5627, + -25975, -3393, 24796, 12005, -20627, -19169, 13969, 24020, + -5627, -25975, -3393, 24796, 12005, -20627, -19169, 13969, + 24020, -5627, -25975, -3393, 24796, 12005, -20627, -19169, + 13969, 24020, -5627, -25975, -3393, 24796, 12005, -20627, + -19169, 13969, 24020, -5627, -25975, -3393, 24796, 12005, + -20627, -19169, 13969, 24020, -5627, -25975, -3393, 24796, + 12005, -20627, -19169, 13969, 24020, -5627, -25975, -3393, + 24796, 12005, -20627, -19169, 13969, 24020, -5627, -25975, + -3393, 24796, 12005, -20627, -19169, 13969, 24020, -5627, + -25975, -3393, 24796, 12005, -20627, -19169, 13969, 24020, + -5627, -25975, -3393, 24796, 12005, -20627, -19169, 13969}, + { +// Carrier 42 Phase 6 + 18384, -14912, -23564, 6729, 25901, 2266, -25114, -10988, + 21297, 18384, -14912, -23563, 6729, 25901, 2266, -25114, + -10988, 21297, 18384, -14912, -23563, 6729, 25901, 2266, + -25114, -10988, 21297, 18384, -14912, -23563, 6729, 25901, + 2266, -25114, -10988, 21297, 18384, -14912, -23563, 6729, + 25901, 2266, -25114, -10988, 21297, 18384, -14912, -23563, + 6729, 25901, 2266, -25114, -10988, 21297, 18384, -14912, + -23563, 6729, 25901, 2266, -25114, -10988, 21297, 18384, + -14912, -23563, 6729, 25901, 2266, -25114, -10988, 21297, + 18384, -14912, -23563, 6729, 25901, 2266, -25114, -10988, + 21297, 18384, -14912, -23563, 6729, 25901, 2266, -25114, + -10988, 21297, 18384, -14912, -23563, 6729, 25901, 2266, + -25114, -10988, 21297, 18384, -14912, -23563, 6729, 25901, + 2266, -25114, -10988, 21297, 18384, -14912, -23563, 6729, + 25901, 2266, -25114, -10988, 21297, 18384, -14912, -23563, + 6729, 25901, 2266, -25114, -10988, 21297, 18384, -14912, + -23563, 6729, 25901, 2266, -25114, -10988, 21297, 18384, + -14912, -23563, 6729, 25901, 2266, -25114, -10988, 21297, + 18384, -14912, -23563, 6729, 25901, 2266, -25114, -10988, + 21297, 18384, -14912, -23563, 6729, 25901, 2266, -25114, + -10988, 21297, 18384, -14912, -23563, 6729, 25901, 2266, + -25114, -10988, 21297, 18384, -14912, -23563, 6729, 25901, + 2266, -25114, -10988, 21297, 18384, -14912, -23563, 6729, + 25901, 2266, -25114, -10988, 21297, 18384, -14912, -23563, + 6729, 25901, 2266, -25114, -10988, 21297, 18384, -14912, + -23563, 6729, 25901, 2266, -25114, -10988, 21297, 18384, + -14912, -23563, 6729, 25901, 2266, -25114, -10988, 21297}, + { +// Carrier 42 Phase 7 + 9949, -21928, -17565, 15827, 23062, -7818, -25777, -1134, + 25383, 9949, -21928, -17565, 15827, 23062, -7818, -25777, + -1134, 25383, 9949, -21928, -17565, 15827, 23062, -7818, + -25777, -1134, 25383, 9949, -21928, -17565, 15827, 23062, + -7818, -25777, -1134, 25383, 9949, -21928, -17565, 15827, + 23062, -7818, -25777, -1134, 25383, 9949, -21928, -17565, + 15827, 23062, -7818, -25777, -1134, 25383, 9949, -21928, + -17565, 15827, 23062, -7818, -25777, -1134, 25383, 9949, + -21928, -17565, 15827, 23062, -7818, -25777, -1134, 25383, + 9949, -21928, -17565, 15827, 23062, -7818, -25777, -1134, + 25383, 9949, -21928, -17565, 15827, 23062, -7818, -25777, + -1134, 25383, 9949, -21928, -17565, 15827, 23062, -7818, + -25777, -1134, 25383, 9949, -21928, -17565, 15827, 23062, + -7818, -25777, -1134, 25383, 9949, -21928, -17565, 15827, + 23062, -7818, -25777, -1134, 25383, 9949, -21928, -17565, + 15827, 23062, -7818, -25777, -1134, 25383, 9949, -21928, + -17565, 15827, 23062, -7818, -25777, -1134, 25383, 9949, + -21928, -17565, 15827, 23062, -7818, -25777, -1134, 25383, + 9949, -21928, -17565, 15827, 23062, -7818, -25777, -1134, + 25383, 9949, -21928, -17565, 15827, 23062, -7818, -25777, + -1134, 25383, 9949, -21928, -17565, 15827, 23062, -7818, + -25777, -1134, 25383, 9949, -21928, -17565, 15827, 23062, + -7818, -25777, -1134, 25383, 9949, -21928, -17565, 15827, + 23062, -7818, -25777, -1134, 25383, 9949, -21928, -17565, + 15827, 23062, -7818, -25777, -1134, 25383, 9949, -21928, + -17565, 15827, 23062, -7818, -25777, -1134, 25383, 9949, + -21928, -17565, 15827, 23062, -7818, -25777, -1134, 25383}}}; + + +/* + +8psk values + +// Carrier 0 Phase 0 + 0, 4514, 8892, 12999, 16712, 19917, 22516, 24432, + 25605, 26000, 25605, 24432, 22516, 19917, 16712, 13000, + 8892, 4514, 0, -4514, -8892, -12999, -16712, -19917, + -22516, -24432, -25604, -26000, -25605, -24432, -22516, -19917, + -16712, -13000, -8892, -4514, 0, 4514, 8892, 12999, + 16712, 19917, 22516, 24432, 25604, 26000, 25605, 24432, + 22516, 19917, 16712, 13000, 8892, 4514, 0, -4514, + -8892, -12999, -16712, -19917, -22516, -24431, -25604, -26000, + -25605, -24432, -22516, -19917, -16712, -13000, -8892, -4514, + 0, 4514, 8892, 12999, 16712, 19917, 22516, 24431, + 25604, 26000, 25605, 24432, 22516, 19917, 16712, 13000, + 8892, 4514, 0, -4514, -8892, -12999, -16712, -19917, + -22516, -24431, -25604, -26000, -25605, -24432, -22516, -19917, + -16712, -13000, -8892, -4514, 0, 4514, 8892, 12999, + 16712, 19917, 22516, 24431, 25604, 26000, 25605, 24432, + 22516, 19917, 16712, 13000, 8892, 4514, 0, -4514, + -8892, -12999, -16712, -19917, -22516, -24431, -25604, -26000, + -25605, -24432, -22516, -19917, -16712, -13000, -8892, -4514, + 0, 4514, 8892, 12999, 16712, 19917, 22516, 24431, + 25604, 26000, 25605, 24432, 22516, 19917, 16712, 13000, + 8892, 4514, 0, -4514, -8892, -12999, -16712, -19917, + -22516, -24431, -25604, -26000, -25605, -24432, -22516, -19917, + -16712, -13000, -8892, -4514, 0, 4514, 8892, 12999, + 16712, 19917, 22516, 24431, 25604, 26000, 25605, 24432, + 22516, 19917, 16712, 13000, 8892, 4514, 0, -4514, + -8892, -12999, -16712, -19917, -22516, -24431, -25604, -26000, + -25605, -24432, -22516, -19917, -16712, -13000, -8892, -4514}, + { +// Carrier 0 Phase 1 + 18384, 21297, 23564, 25114, 25901, 25901, 25114, 23564, + 21297, 18384, 14912, 10988, 6729, 2266, -2266, -6729, + -10988, -14912, -18384, -21297, -23563, -25114, -25901, -25901, + -25114, -23564, -21297, -18384, -14913, -10988, -6729, -2266, + 2266, 6729, 10988, 14912, 18384, 21297, 23563, 25114, + 25901, 25901, 25114, 23564, 21297, 18384, 14913, 10988, + 6729, 2266, -2266, -6729, -10988, -14912, -18384, -21297, + -23563, -25114, -25901, -25901, -25114, -23564, -21297, -18384, + -14913, -10988, -6729, -2266, 2266, 6729, 10988, 14912, + 18384, 21297, 23563, 25114, 25901, 25901, 25114, 23564, + 21297, 18384, 14913, 10988, 6729, 2266, -2265, -6729, + -10988, -14912, -18384, -21297, -23563, -25114, -25901, -25901, + -25114, -23564, -21297, -18384, -14913, -10988, -6729, -2266, + 2265, 6729, 10988, 14912, 18384, 21297, 23563, 25114, + 25901, 25901, 25114, 23564, 21297, 18384, 14913, 10988, + 6729, 2266, -2265, -6729, -10988, -14912, -18384, -21297, + -23563, -25114, -25901, -25901, -25114, -23564, -21298, -18384, + -14913, -10988, -6729, -2266, 2265, 6729, 10987, 14912, + 18384, 21297, 23563, 25114, 25901, 25901, 25114, 23564, + 21298, 18384, 14913, 10988, 6729, 2266, -2265, -6729, + -10987, -14912, -18384, -21297, -23563, -25114, -25901, -25901, + -25114, -23564, -21298, -18384, -14913, -10988, -6729, -2266, + 2265, 6729, 10987, 14912, 18384, 21297, 23563, 25114, + 25901, 25901, 25114, 23564, 21298, 18384, 14913, 10988, + 6729, 2266, -2265, -6729, -10987, -14912, -18384, -21297, + -23563, -25114, -25901, -25901, -25114, -23564, -21298, -18384, + -14913, -10988, -6729, -2266, 2265, 6729, 10987, 14912}, + { +// Carrier 0 Phase 2 + 26000, 25605, 24432, 22516, 19917, 16712, 13000, 8892, + 4514, 0, -4514, -8892, -12999, -16712, -19917, -22516, + -24432, -25604, -26000, -25605, -24432, -22516, -19917, -16712, + -13000, -8892, -4514, 0, 4514, 8892, 12999, 16712, + 19917, 22516, 24432, 25604, 26000, 25605, 24432, 22516, + 19917, 16712, 13000, 8892, 4514, 0, -4514, -8892, + -12999, -16712, -19917, -22516, -24431, -25604, -26000, -25605, + -24432, -22516, -19917, -16712, -13000, -8892, -4514, 0, + 4514, 8892, 12999, 16712, 19917, 22516, 24431, 25604, + 26000, 25605, 24432, 22516, 19917, 16712, 13000, 8892, + 4514, 0, -4514, -8892, -12999, -16712, -19917, -22516, + -24431, -25604, -26000, -25605, -24432, -22516, -19917, -16712, + -13000, -8892, -4514, 0, 4514, 8892, 12999, 16712, + 19917, 22516, 24431, 25604, 26000, 25605, 24432, 22516, + 19917, 16712, 13000, 8892, 4514, 0, -4514, -8892, + -12999, -16712, -19917, -22516, -24431, -25604, -26000, -25605, + -24432, -22516, -19917, -16712, -13000, -8892, -4514, 0, + 4514, 8892, 12999, 16712, 19917, 22516, 24431, 25604, + 26000, 25605, 24432, 22516, 19917, 16712, 13000, 8892, + 4514, 0, -4514, -8892, -12999, -16712, -19917, -22516, + -24431, -25604, -26000, -25605, -24432, -22516, -19917, -16712, + -13000, -8892, -4514, 0, 4514, 8892, 12999, 16712, + 19917, 22516, 24431, 25604, 26000, 25605, 24432, 22516, + 19917, 16712, 13000, 8892, 4514, 0, -4514, -8892, + -12999, -16712, -19917, -22516, -24431, -25604, -26000, -25605, + -24432, -22516, -19917, -16712, -13000, -8892, -4514, 0, + 4514, 8892, 12999, 16712, 19917, 22516, 24431, 25604}, + { +// Carrier 0 Phase 3 + 18384, 14912, 10988, 6729, 2266, -2266, -6729, -10988, + -14912, -18384, -21297, -23563, -25114, -25901, -25901, -25114, + -23564, -21297, -18384, -14913, -10988, -6729, -2266, 2266, + 6729, 10988, 14912, 18384, 21297, 23563, 25114, 25901, + 25901, 25114, 23564, 21297, 18384, 14913, 10988, 6729, + 2266, -2266, -6729, -10988, -14912, -18384, -21297, -23563, + -25114, -25901, -25901, -25114, -23564, -21297, -18384, -14913, + -10988, -6729, -2266, 2266, 6729, 10988, 14912, 18384, + 21297, 23563, 25114, 25901, 25901, 25114, 23564, 21297, + 18384, 14913, 10988, 6729, 2266, -2265, -6729, -10988, + -14912, -18384, -21297, -23563, -25114, -25901, -25901, -25114, + -23564, -21297, -18384, -14913, -10988, -6729, -2266, 2265, + 6729, 10988, 14912, 18384, 21297, 23563, 25114, 25901, + 25901, 25114, 23564, 21297, 18384, 14913, 10988, 6729, + 2266, -2265, -6729, -10988, -14912, -18384, -21297, -23563, + -25114, -25901, -25901, -25114, -23564, -21298, -18384, -14913, + -10988, -6729, -2266, 2265, 6729, 10987, 14912, 18384, + 21297, 23563, 25114, 25901, 25901, 25114, 23564, 21298, + 18384, 14913, 10988, 6729, 2266, -2265, -6729, -10987, + -14912, -18384, -21297, -23563, -25114, -25901, -25901, -25114, + -23564, -21298, -18384, -14913, -10988, -6729, -2266, 2265, + 6729, 10987, 14912, 18384, 21297, 23563, 25114, 25901, + 25901, 25114, 23564, 21298, 18384, 14913, 10988, 6729, + 2266, -2265, -6729, -10987, -14912, -18384, -21297, -23563, + -25114, -25901, -25901, -25114, -23564, -21298, -18384, -14913, + -10988, -6729, -2266, 2265, 6729, 10987, 14912, 18384, + 21297, 23563, 25114, 25901, 25901, 25114, 23564, 21298}, + },{{ + +// Carrier 1 Phase 0 + 0, 5257, 10298, 14912, 18911, 22129, 24432, 25725, + 25956, 25114, 23234, 20394, 16712, 12339, 7456, 2266, + -3018, -8178, -12999, -17284, -20855, -23563, -25299, -25989, + -25605, -24163, -21722, -18384, -14287, -9599, -4514, 756, + 5996, 10988, 15526, 19422, 22516, 24680, 25824, 25901, + 24907, 22885, 19917, 16126, 11668, 6729, 1511, -3768, + -8892, -13649, -17842, -21297, -23873, -25462, -26000, -25462, + -23873, -21297, -17842, -13649, -8892, -3768, 1511, 6729, + 11668, 16126, 19917, 22885, 24907, 25901, 25824, 24680, + 22516, 19422, 15526, 10988, 5996, 756, -4514, -9599, + -14287, -18384, -21722, -24163, -25604, -25989, -25299, -23564, + -20855, -17284, -13000, -8178, -3018, 2266, 7456, 12339, + 16712, 20394, 23234, 25114, 25956, 25725, 24432, 22129, + 18911, 14912, 10298, 5257, 0, -5257, -10298, -14912, + -18911, -22129, -24432, -25725, -25956, -25114, -23234, -20394, + -16712, -12339, -7456, -2266, 3018, 8178, 13000, 17284, + 20855, 23564, 25299, 25989, 25604, 24163, 21722, 18384, + 14287, 9599, 4514, -756, -5996, -10988, -15526, -19422, + -22516, -24680, -25824, -25901, -24907, -22885, -19917, -16126, + -11668, -6729, -1511, 3768, 8892, 13649, 17842, 21297, + 23873, 25462, 26000, 25462, 23873, 21297, 17842, 13649, + 8892, 3768, -1511, -6729, -11668, -16126, -19917, -22885, + -24907, -25901, -25824, -24680, -22516, -19422, -15526, -10988, + -5995, -756, 4514, 9599, 14287, 18384, 21722, 24163, + 25605, 25989, 25299, 23563, 20855, 17284, 12999, 8178, + 3018, -2266, -7456, -12339, -16712, -20394, -23234, -25114, + -25956, -25725, -24432, -22129, -18911, -14912, -10298, -5257}, + { +// Carrier 1 Phase 1 + 18384, 21722, 24163, 25605, 25989, 25299, 23564, 20855, + 17284, 13000, 8178, 3018, -2266, -7456, -12339, -16712, + -20394, -23234, -25114, -25956, -25725, -24432, -22129, -18911, + -14912, -10298, -5257, 0, 5257, 10298, 14912, 18911, + 22129, 24432, 25725, 25956, 25114, 23234, 20394, 16712, + 12339, 7456, 2266, -3018, -8178, -12999, -17284, -20855, + -23563, -25299, -25989, -25605, -24163, -21722, -18384, -14287, + -9599, -4514, 756, 5996, 10988, 15526, 19422, 22516, + 24680, 25824, 25901, 24907, 22885, 19917, 16126, 11668, + 6729, 1511, -3768, -8892, -13649, -17842, -21297, -23873, + -25462, -26000, -25462, -23873, -21297, -17842, -13649, -8892, + -3768, 1511, 6729, 11668, 16126, 19917, 22885, 24907, + 25901, 25824, 24680, 22516, 19422, 15526, 10988, 5996, + 756, -4514, -9599, -14287, -18384, -21722, -24163, -25604, + -25989, -25299, -23564, -20855, -17284, -12999, -8178, -3018, + 2266, 7456, 12339, 16712, 20394, 23234, 25114, 25956, + 25725, 24432, 22129, 18911, 14912, 10298, 5257, 0, + -5257, -10298, -14912, -18911, -22129, -24432, -25725, -25956, + -25114, -23234, -20394, -16712, -12339, -7456, -2266, 3018, + 8178, 13000, 17284, 20855, 23564, 25299, 25989, 25604, + 24163, 21722, 18384, 14287, 9599, 4514, -756, -5996, + -10988, -15526, -19422, -22516, -24680, -25824, -25901, -24907, + -22885, -19917, -16126, -11668, -6729, -1511, 3768, 8892, + 13649, 17842, 21297, 23873, 25462, 26000, 25462, 23873, + 21297, 17842, 13649, 8892, 3768, -1511, -6729, -11668, + -16126, -19917, -22885, -24907, -25901, -25824, -24680, -22516, + -19422, -15526, -10988, -5995, -756, 4514, 9599, 14287}, + { +// Carrier 1 Phase 2 + 26000, 25462, 23873, 21297, 17842, 13649, 8892, 3768, + -1511, -6729, -11668, -16126, -19917, -22885, -24907, -25901, + -25824, -24680, -22516, -19422, -15526, -10988, -5996, -756, + 4514, 9599, 14287, 18384, 21722, 24163, 25605, 25989, + 25299, 23563, 20855, 17284, 13000, 8178, 3018, -2266, + -7456, -12339, -16712, -20394, -23234, -25114, -25956, -25725, + -24432, -22129, -18911, -14912, -10298, -5257, 0, 5257, + 10298, 14913, 18911, 22129, 24432, 25725, 25956, 25114, + 23234, 20394, 16712, 12339, 7456, 2266, -3018, -8178, + -12999, -17284, -20855, -23563, -25299, -25989, -25605, -24163, + -21722, -18384, -14287, -9599, -4514, 756, 5996, 10988, + 15526, 19422, 22516, 24680, 25824, 25901, 24907, 22885, + 19917, 16126, 11668, 6729, 1511, -3768, -8892, -13649, + -17842, -21297, -23873, -25462, -26000, -25462, -23873, -21297, + -17842, -13649, -8892, -3768, 1511, 6729, 11668, 16126, + 19917, 22885, 24907, 25901, 25824, 24680, 22516, 19422, + 15526, 10988, 5995, 756, -4514, -9599, -14287, -18384, + -21722, -24163, -25605, -25989, -25299, -23563, -20855, -17284, + -12999, -8178, -3018, 2266, 7456, 12339, 16712, 20394, + 23234, 25114, 25956, 25725, 24431, 22129, 18911, 14912, + 10298, 5257, 0, -5257, -10298, -14912, -18911, -22129, + -24432, -25725, -25956, -25114, -23234, -20394, -16712, -12339, + -7456, -2266, 3018, 8178, 13000, 17284, 20855, 23564, + 25299, 25989, 25604, 24163, 21722, 18384, 14287, 9599, + 4514, -756, -5996, -10988, -15526, -19422, -22516, -24680, + -25824, -25901, -24907, -22885, -19917, -16126, -11668, -6729, + -1511, 3768, 8892, 13649, 17842, 21297, 23873, 25462}, + { +// Carrier 1 Phase 3 + 18384, 14287, 9599, 4514, -756, -5995, -10988, -15526, + -19422, -22516, -24680, -25824, -25901, -24907, -22885, -19917, + -16126, -11668, -6729, -1511, 3768, 8892, 13649, 17842, + 21297, 23873, 25462, 26000, 25462, 23873, 21297, 17842, + 13649, 8892, 3768, -1511, -6729, -11668, -16126, -19917, + -22885, -24907, -25901, -25824, -24680, -22516, -19422, -15526, + -10988, -5995, -756, 4514, 9599, 14287, 18384, 21722, + 24163, 25605, 25989, 25299, 23563, 20855, 17284, 12999, + 8178, 3018, -2266, -7456, -12339, -16712, -20394, -23234, + -25114, -25956, -25725, -24432, -22129, -18911, -14912, -10298, + -5257, 0, 5257, 10298, 14913, 18911, 22129, 24432, + 25725, 25956, 25114, 23234, 20394, 16712, 12339, 7456, + 2266, -3018, -8178, -13000, -17284, -20855, -23564, -25299, + -25989, -25604, -24163, -21722, -18384, -14287, -9599, -4514, + 756, 5996, 10988, 15526, 19422, 22516, 24680, 25824, + 25901, 24907, 22885, 19917, 16126, 11668, 6729, 1511, + -3768, -8892, -13649, -17842, -21297, -23873, -25462, -26000, + -25462, -23873, -21297, -17842, -13649, -8892, -3768, 1511, + 6729, 11668, 16126, 19917, 22885, 24907, 25901, 25824, + 24680, 22516, 19422, 15526, 10988, 5995, 756, -4514, + -9599, -14287, -18384, -21722, -24163, -25605, -25989, -25299, + -23563, -20855, -17284, -12999, -8178, -3018, 2266, 7456, + 12339, 16712, 20394, 23234, 25114, 25956, 25725, 24431, + 22129, 18911, 14912, 10298, 5257, 0, -5257, -10298, + -14913, -18911, -22129, -24432, -25725, -25956, -25114, -23234, + -20394, -16712, -12339, -7456, -2265, 3018, 8178, 13000, + 17284, 20855, 23564, 25299, 25989, 25604, 24163, 21722}, + },{{ + +// Carrier 2 Phase 0 + 0, 5996, 11668, 16712, 20855, 23873, 25605, 25956, + 24907, 22516, 18911, 14287, 8892, 3018, -3018, -8892, + -14287, -18911, -22516, -24907, -25956, -25605, -23873, -20855, + -16712, -11668, -5996, 0, 5995, 11668, 16712, 20855, + 23873, 25604, 25956, 24907, 22516, 18911, 14287, 8892, + 3018, -3018, -8892, -14287, -18911, -22516, -24907, -25956, + -25605, -23873, -20855, -16712, -11668, -5996, 0, 5995, + 11668, 16712, 20855, 23873, 25604, 25956, 24907, 22516, + 18911, 14287, 8892, 3018, -3018, -8892, -14287, -18911, + -22516, -24907, -25956, -25605, -23873, -20855, -16712, -11668, + -5996, 0, 5995, 11668, 16712, 20855, 23873, 25604, + 25956, 24907, 22516, 18911, 14287, 8892, 3018, -3018, + -8892, -14287, -18911, -22516, -24907, -25956, -25605, -23873, + -20855, -16712, -11668, -5996, 0, 5995, 11668, 16712, + 20855, 23873, 25604, 25956, 24907, 22516, 18911, 14287, + 8892, 3018, -3018, -8892, -14287, -18911, -22516, -24907, + -25955, -25605, -23873, -20855, -16712, -11668, -5996, 0, + 5995, 11668, 16712, 20855, 23873, 25604, 25956, 24907, + 22516, 18911, 14287, 8892, 3018, -3018, -8892, -14287, + -18911, -22516, -24907, -25955, -25605, -23873, -20855, -16712, + -11669, -5996, 0, 5995, 11668, 16712, 20855, 23873, + 25604, 25956, 24907, 22516, 18911, 14287, 8892, 3018, + -3018, -8892, -14286, -18911, -22516, -24907, -25955, -25605, + -23873, -20855, -16712, -11669, -5996, 0, 5995, 11668, + 16712, 20854, 23873, 25604, 25956, 24907, 22516, 18911, + 14287, 8892, 3018, -3018, -8892, -14286, -18911, -22516, + -24907, -25955, -25605, -23873, -20855, -16712, -11669, -5996}, + { +// Carrier 2 Phase 1 + 18384, 22129, 24680, 25901, 25725, 24163, 21297, 17284, + 12339, 6729, 756, -5257, -10988, -16126, -20394, -23563, + -25462, -25989, -25114, -22885, -19422, -14913, -9599, -3768, + 2265, 8178, 13649, 18384, 22129, 24680, 25901, 25725, + 24163, 21297, 17284, 12339, 6729, 756, -5257, -10988, + -16126, -20394, -23563, -25462, -25989, -25114, -22885, -19422, + -14913, -9599, -3768, 2265, 8178, 13649, 18384, 22128, + 24680, 25901, 25725, 24163, 21298, 17284, 12339, 6729, + 756, -5257, -10987, -16126, -20394, -23563, -25462, -25989, + -25114, -22885, -19422, -14913, -9599, -3768, 2265, 8178, + 13649, 18384, 22128, 24680, 25901, 25725, 24163, 21298, + 17284, 12339, 6729, 756, -5257, -10987, -16126, -20394, + -23563, -25462, -25989, -25114, -22885, -19422, -14913, -9599, + -3768, 2265, 8177, 13649, 18384, 22128, 24680, 25901, + 25725, 24163, 21298, 17284, 12339, 6729, 756, -5257, + -10987, -16125, -20394, -23563, -25462, -25989, -25114, -22885, + -19422, -14913, -9599, -3768, 2265, 8177, 13649, 18384, + 22128, 24680, 25901, 25725, 24163, 21298, 17284, 12339, + 6729, 756, -5257, -10987, -16125, -20394, -23563, -25462, + -25989, -25114, -22885, -19422, -14913, -9599, -3768, 2265, + 8177, 13649, 18384, 22128, 24680, 25901, 25725, 24163, + 21298, 17284, 12339, 6729, 756, -5257, -10987, -16125, + -20394, -23563, -25462, -25989, -25114, -22885, -19422, -14913, + -9599, -3768, 2265, 8177, 13649, 18384, 22128, 24680, + 25901, 25725, 24163, 21298, 17284, 12339, 6729, 756, + -5257, -10987, -16125, -20394, -23563, -25462, -25989, -25114, + -22885, -19422, -14913, -9599, -3768, 2265, 8177, 13649}, + { +// Carrier 2 Phase 2 + 26000, 25299, 23234, 19917, 15526, 10298, 4514, -1511, + -7456, -12999, -17842, -21722, -24432, -25824, -25824, -24432, + -21722, -17842, -13000, -7456, -1511, 4514, 10298, 15526, + 19917, 23234, 25299, 26000, 25299, 23234, 19917, 15526, + 10298, 4514, -1511, -7456, -12999, -17842, -21722, -24431, + -25824, -25824, -24432, -21722, -17842, -13000, -7456, -1511, + 4514, 10297, 15526, 19917, 23234, 25299, 26000, 25299, + 23234, 19917, 15526, 10298, 4514, -1511, -7456, -12999, + -17842, -21722, -24431, -25824, -25824, -24432, -21722, -17842, + -13000, -7457, -1511, 4514, 10297, 15526, 19917, 23234, + 25299, 26000, 25299, 23234, 19917, 15526, 10298, 4515, + -1511, -7456, -12999, -17842, -21722, -24431, -25824, -25824, + -24432, -21722, -17842, -13000, -7457, -1511, 4514, 10297, + 15525, 19917, 23234, 25299, 26000, 25299, 23234, 19917, + 15526, 10298, 4515, -1511, -7456, -12999, -17842, -21722, + -24431, -25824, -25824, -24432, -21722, -17842, -13000, -7457, + -1512, 4514, 10297, 15525, 19916, 23234, 25299, 26000, + 25299, 23234, 19917, 15526, 10298, 4515, -1511, -7456, + -12999, -17842, -21722, -24431, -25824, -25824, -24432, -21722, + -17842, -13000, -7457, -1512, 4514, 10297, 15525, 19916, + 23234, 25299, 26000, 25299, 23234, 19917, 15526, 10298, + 4515, -1511, -7456, -12999, -17842, -21722, -24431, -25824, + -25824, -24432, -21722, -17842, -13000, -7457, -1512, 4514, + 10297, 15525, 19916, 23234, 25299, 26000, 25299, 23234, + 19917, 15526, 10298, 4515, -1511, -7456, -12999, -17842, + -21722, -24431, -25824, -25824, -24432, -21722, -17842, -13000, + -7457, -1512, 4514, 10297, 15525, 19916, 23234, 25299}, + { +// Carrier 2 Phase 3 + 18384, 13649, 8178, 2266, -3768, -9599, -14912, -19422, + -22885, -25114, -25989, -25462, -23564, -20394, -16126, -10988, + -5257, 756, 6729, 12339, 17284, 21297, 24163, 25725, + 25901, 24680, 22129, 18384, 13649, 8178, 2266, -3768, + -9599, -14912, -19422, -22885, -25114, -25988, -25462, -23564, + -20394, -16126, -10988, -5257, 756, 6729, 12339, 17284, + 21297, 24163, 25725, 25901, 24680, 22129, 18384, 13649, + 8178, 2266, -3768, -9599, -14912, -19422, -22885, -25114, + -25988, -25462, -23564, -20394, -16126, -10988, -5257, 756, + 6729, 12339, 17284, 21297, 24162, 25725, 25901, 24680, + 22129, 18384, 13649, 8178, 2266, -3768, -9599, -14912, + -19422, -22885, -25114, -25988, -25462, -23564, -20394, -16126, + -10988, -5257, 756, 6729, 12339, 17284, 21297, 24162, + 25725, 25901, 24680, 22129, 18384, 13649, 8178, 2266, + -3768, -9599, -14912, -19422, -22885, -25114, -25988, -25462, + -23564, -20394, -16126, -10988, -5257, 755, 6729, 12339, + 17284, 21297, 24162, 25725, 25901, 24680, 22129, 18384, + 13649, 8178, 2266, -3767, -9599, -14912, -19422, -22885, + -25114, -25988, -25462, -23564, -20394, -16126, -10988, -5257, + 755, 6729, 12339, 17284, 21297, 24162, 25725, 25901, + 24680, 22129, 18384, 13649, 8178, 2266, -3767, -9599, + -14912, -19422, -22885, -25113, -25988, -25462, -23564, -20395, + -16126, -10988, -5257, 755, 6728, 12339, 17284, 21297, + 24162, 25725, 25901, 24680, 22129, 18385, 13649, 8178, + 2266, -3767, -9599, -14912, -19422, -22885, -25113, -25988, + -25462, -23564, -20395, -16126, -10988, -5258, 755, 6728, + 12339, 17284, 21297, 24162, 25725, 25901, 24680, 22129}, + },{{ + +// Carrier 3 Phase 0 + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729, + 0, 6729, 12999, 18384, 22516, 25114, 26000, 25114, + 22516, 18384, 13000, 6729, 0, -6729, -12999, -18384, + -22516, -25114, -26000, -25114, -22516, -18384, -13000, -6729}, + { +// Carrier 3 Phase 1 + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999, + 18384, 22516, 25114, 26000, 25114, 22516, 18384, 13000, + 6729, 0, -6729, -12999, -18384, -22516, -25114, -26000, + -25114, -22516, -18384, -13000, -6729, 0, 6729, 12999}, + { +// Carrier 3 Phase 2 + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114, + 26000, 25114, 22516, 18384, 13000, 6729, 0, -6729, + -12999, -18384, -22516, -25114, -26000, -25114, -22516, -18384, + -13000, -6729, 0, 6729, 12999, 18384, 22516, 25114}, + { +// Carrier 3 Phase 3 + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516, + 18384, 13000, 6729, 0, -6729, -12999, -18384, -22516, + -25114, -26000, -25114, -22516, -18384, -13000, -6729, 0, + 6729, 12999, 18384, 22516, 25114, 26000, 25114, 22516}, + },{{ + +// Carrier 4 Phase 0 + 0, 7456, 14287, 19917, 23873, 25824, 25605, 23234, + 18911, 13000, 5996, -1511, -8892, -15526, -20855, -24432, + -25956, -25299, -22516, -17842, -11668, -4514, 3018, 10298, + 16712, 21722, 24907, 26000, 24907, 21722, 16712, 10298, + 3018, -4514, -11668, -17842, -22516, -25299, -25956, -24431, + -20855, -15526, -8892, -1511, 5996, 13000, 18911, 23234, + 25605, 25824, 23873, 19917, 14287, 7456, 0, -7456, + -14287, -19917, -23873, -25824, -25604, -23234, -18911, -12999, + -5995, 1511, 8892, 15526, 20855, 24432, 25956, 25299, + 22516, 17842, 11668, 4514, -3018, -10298, -16712, -21722, + -24907, -26000, -24907, -21722, -16712, -10297, -3018, 4515, + 11668, 17842, 22516, 25299, 25956, 24431, 20855, 15525, + 8892, 1511, -5996, -13000, -18911, -23234, -25605, -25824, + -23873, -19917, -14287, -7456, 0, 7457, 14287, 19917, + 23873, 25824, 25604, 23234, 18911, 12999, 5995, -1511, + -8892, -15526, -20855, -24432, -25956, -25299, -22516, -17842, + -11668, -4514, 3018, 10298, 16712, 21722, 24907, 26000, + 24907, 21722, 16712, 10297, 3018, -4515, -11668, -17842, + -22516, -25299, -25955, -24431, -20855, -15525, -8892, -1511, + 5996, 13000, 18911, 23234, 25605, 25824, 23873, 19916, + 14287, 7456, 0, -7457, -14287, -19917, -23873, -25824, + -25604, -23234, -18911, -12999, -5995, 1512, 8892, 15526, + 20855, 24432, 25956, 25299, 22516, 17842, 11668, 4514, + -3018, -10298, -16712, -21722, -24907, -26000, -24907, -21722, + -16712, -10297, -3018, 4515, 11669, 17842, 22516, 25299, + 25955, 24431, 20854, 15525, 8892, 1511, -5996, -13000, + -18911, -23234, -25605, -25824, -23873, -19916, -14286, -7456}, + { +// Carrier 4 Phase 1 + 18384, 22885, 25462, 25901, 24163, 20394, 14912, 8178, + 756, -6729, -13649, -19422, -23564, -25725, -25725, -23563, + -19422, -13649, -6729, 756, 8178, 14913, 20394, 24163, + 25901, 25462, 22885, 18384, 12339, 5257, -2266, -9599, + -16126, -21297, -24680, -25989, -25114, -22129, -17284, -10988, + -3768, 3768, 10988, 17284, 22129, 25114, 25988, 24680, + 21297, 16126, 9599, 2265, -5257, -12339, -18384, -22885, + -25462, -25901, -24162, -20394, -14912, -8178, -756, 6729, + 13649, 19422, 23564, 25725, 25725, 23563, 19422, 13649, + 6729, -756, -8178, -14913, -20394, -24163, -25901, -25462, + -22885, -18384, -12339, -5257, 2266, 9599, 16126, 21298, + 24680, 25989, 25114, 22128, 17284, 10987, 3768, -3768, + -10988, -17284, -22129, -25114, -25988, -24680, -21297, -16125, + -9599, -2265, 5257, 12339, 18384, 22885, 25462, 25901, + 24162, 20394, 14912, 8177, 755, -6729, -13649, -19422, + -23564, -25725, -25725, -23563, -19422, -13649, -6729, 756, + 8178, 14913, 20394, 24163, 25901, 25462, 22885, 18384, + 12339, 5257, -2266, -9599, -16126, -21298, -24680, -25989, + -25114, -22128, -17284, -10987, -3767, 3768, 10988, 17284, + 22129, 25114, 25988, 24680, 21297, 16125, 9599, 2265, + -5257, -12339, -18384, -22885, -25462, -25901, -24162, -20394, + -14912, -8177, -755, 6729, 13649, 19422, 23564, 25725, + 25725, 23563, 19422, 13649, 6728, -756, -8178, -14913, + -20395, -24163, -25901, -25462, -22885, -18384, -12339, -5257, + 2266, 9599, 16126, 21298, 24680, 25989, 25113, 22128, + 17284, 10987, 3767, -3768, -10988, -17284, -22129, -25114, + -25988, -24680, -21297, -16125, -9599, -2265, 5258, 12339}, + { +// Carrier 4 Phase 2 + 26000, 24907, 21722, 16712, 10298, 3018, -4514, -11668, + -17842, -22516, -25299, -25956, -24432, -20855, -15526, -8892, + -1511, 5996, 13000, 18911, 23234, 25605, 25824, 23873, + 19917, 14287, 7456, 0, -7456, -14287, -19917, -23873, + -25824, -25604, -23234, -18911, -12999, -5995, 1511, 8892, + 15526, 20855, 24432, 25956, 25299, 22516, 17842, 11668, + 4514, -3018, -10298, -16712, -21722, -24907, -26000, -24907, + -21722, -16712, -10297, -3018, 4514, 11668, 17842, 22516, + 25299, 25956, 24431, 20855, 15526, 8892, 1511, -5996, + -13000, -18911, -23234, -25605, -25824, -23873, -19917, -14287, + -7456, 0, 7457, 14287, 19917, 23873, 25824, 25604, + 23234, 18911, 12999, 5995, -1511, -8892, -15526, -20855, + -24432, -25956, -25299, -22516, -17842, -11668, -4514, 3018, + 10298, 16712, 21722, 24907, 26000, 24907, 21722, 16712, + 10297, 3018, -4515, -11668, -17842, -22516, -25299, -25955, + -24431, -20855, -15525, -8892, -1511, 5996, 13000, 18911, + 23234, 25605, 25824, 23873, 19917, 14287, 7456, 0, + -7457, -14287, -19917, -23873, -25824, -25604, -23234, -18911, + -12999, -5995, 1512, 8892, 15526, 20855, 24432, 25956, + 25299, 22516, 17842, 11668, 4514, -3018, -10298, -16712, + -21722, -24907, -26000, -24907, -21722, -16712, -10297, -3018, + 4515, 11669, 17842, 22516, 25299, 25955, 24431, 20855, + 15525, 8892, 1511, -5996, -13000, -18911, -23234, -25605, + -25824, -23873, -19916, -14286, -7456, 0, 7457, 14287, + 19917, 23873, 25824, 25604, 23234, 18911, 12999, 5995, + -1512, -8892, -15526, -20855, -24432, -25956, -25299, -22516, + -17842, -11668, -4514, 3018, 10298, 16712, 21722, 24907}, + { +// Carrier 4 Phase 3 + 18384, 12339, 5257, -2266, -9599, -16126, -21297, -24680, + -25989, -25114, -22129, -17284, -10988, -3768, 3768, 10988, + 17284, 22129, 25114, 25989, 24680, 21297, 16126, 9599, + 2266, -5257, -12339, -18384, -22885, -25462, -25901, -24163, + -20394, -14912, -8178, -756, 6729, 13649, 19422, 23564, + 25725, 25725, 23563, 19422, 13649, 6729, -756, -8178, + -14913, -20394, -24163, -25901, -25462, -22885, -18384, -12339, + -5257, 2266, 9599, 16126, 21298, 24680, 25989, 25114, + 22128, 17284, 10987, 3768, -3768, -10988, -17284, -22129, + -25114, -25988, -24680, -21297, -16126, -9599, -2265, 5257, + 12339, 18384, 22885, 25462, 25901, 24162, 20394, 14912, + 8178, 756, -6729, -13649, -19422, -23564, -25725, -25725, + -23563, -19422, -13649, -6729, 756, 8178, 14913, 20394, + 24163, 25901, 25462, 22885, 18384, 12339, 5257, -2266, + -9599, -16126, -21298, -24680, -25989, -25114, -22128, -17284, + -10987, -3767, 3768, 10988, 17284, 22129, 25114, 25988, + 24680, 21297, 16125, 9599, 2265, -5257, -12339, -18384, + -22885, -25462, -25901, -24162, -20394, -14912, -8177, -755, + 6729, 13649, 19422, 23564, 25725, 25725, 23563, 19422, + 13649, 6729, -756, -8178, -14913, -20394, -24163, -25901, + -25462, -22885, -18384, -12339, -5257, 2266, 9599, 16126, + 21298, 24680, 25989, 25113, 22128, 17284, 10987, 3767, + -3768, -10988, -17284, -22129, -25114, -25988, -24680, -21297, + -16125, -9599, -2265, 5258, 12339, 18385, 22885, 25462, + 25901, 24162, 20394, 14912, 8177, 755, -6729, -13649, + -19422, -23564, -25725, -25725, -23563, -19422, -13649, -6728, + 756, 8178, 14913, 20395, 24163, 25901, 25462, 22885}, + },{{ + +// Carrier 5 Phase 0 + 0, 8178, 15526, 21297, 24907, 25989, 24432, 20394, + 14287, 6729, -1511, -9599, -16712, -22129, -25299, -25901, + -23873, -19422, -13000, -5257, 3018, 10988, 17842, 22885, + 25604, 25725, 23234, 18384, 11668, 3768, -4514, -12339, + -18911, -23563, -25824, -25462, -22516, -17284, -10298, -2266, + 5995, 13649, 19917, 24163, 25956, 25114, 21722, 16126, + 8892, 756, -7456, -14912, -20855, -24680, -26000, -24680, + -20855, -14913, -7456, 756, 8892, 16126, 21722, 25114, + 25956, 24163, 19917, 13649, 5996, -2265, -10297, -17284, + -22516, -25462, -25824, -23564, -18911, -12339, -4514, 3768, + 11668, 18384, 23234, 25725, 25605, 22885, 17842, 10988, + 3018, -5257, -12999, -19422, -23873, -25901, -25299, -22129, + -16712, -9599, -1511, 6729, 14287, 20394, 24431, 25988, + 24907, 21298, 15526, 8178, 0, -8177, -15525, -21297, + -24907, -25989, -24432, -20394, -14287, -6729, 1511, 9599, + 16712, 22128, 25299, 25901, 23873, 19422, 13000, 5257, + -3018, -10987, -17842, -22885, -25604, -25725, -23234, -18384, + -11668, -3768, 4514, 12339, 18911, 23563, 25824, 25462, + 22516, 17284, 10298, 2266, -5995, -13649, -19916, -24162, + -25955, -25114, -21722, -16126, -8892, -756, 7456, 14912, + 20855, 24680, 26000, 24680, 20855, 14913, 7457, -755, + -8892, -16125, -21722, -25113, -25956, -24163, -19917, -13649, + -5996, 2265, 10297, 17284, 22516, 25462, 25824, 23564, + 18911, 12339, 4515, -3767, -11668, -18384, -23234, -25725, + -25605, -22885, -17842, -10988, -3018, 5257, 12999, 19422, + 23873, 25901, 25299, 22129, 16712, 9599, 1512, -6728, + -14286, -20394, -24431, -25988, -24907, -21298, -15526, -8178}, + { +// Carrier 5 Phase 1 + 18384, 23234, 25725, 25605, 22885, 17842, 10988, 3018, + -5257, -12999, -19422, -23873, -25901, -25299, -22129, -16712, + -9599, -1511, 6729, 14287, 20394, 24431, 25989, 24907, + 21297, 15526, 8178, 0, -8178, -15526, -21297, -24907, + -25989, -24432, -20394, -14287, -6729, 1511, 9599, 16712, + 22128, 25299, 25901, 23873, 19422, 13000, 5257, -3018, + -10988, -17842, -22885, -25604, -25725, -23234, -18384, -11668, + -3768, 4514, 12339, 18911, 23563, 25824, 25462, 22516, + 17284, 10298, 2266, -5995, -13649, -19917, -24162, -25956, + -25114, -21722, -16126, -8892, -756, 7456, 14912, 20855, + 24680, 26000, 24680, 20855, 14913, 7457, -756, -8892, + -16125, -21722, -25114, -25956, -24163, -19917, -13649, -5996, + 2265, 10297, 17284, 22516, 25462, 25824, 23564, 18911, + 12339, 4515, -3768, -11668, -18384, -23234, -25725, -25605, + -22885, -17842, -10988, -3018, 5257, 12999, 19422, 23873, + 25901, 25299, 22129, 16712, 9599, 1512, -6729, -14287, + -20394, -24431, -25988, -24907, -21298, -15526, -8178, 0, + 8177, 15525, 21297, 24907, 25989, 24432, 20394, 14287, + 6729, -1511, -9599, -16712, -22128, -25299, -25901, -23873, + -19422, -13000, -5257, 3018, 10987, 17842, 22885, 25604, + 25725, 23234, 18385, 11669, 3768, -4514, -12339, -18911, + -23563, -25824, -25462, -22516, -17284, -10298, -2266, 5995, + 13649, 19916, 24162, 25955, 25114, 21722, 16126, 8892, + 756, -7456, -14912, -20854, -24680, -26000, -24680, -20855, + -14913, -7457, 755, 8892, 16125, 21722, 25113, 25956, + 24163, 19917, 13649, 5996, -2265, -10297, -17284, -22516, + -25462, -25824, -23564, -18912, -12339, -4515, 3767, 11668}, + { +// Carrier 5 Phase 2 + 26000, 24680, 20855, 14912, 7456, -756, -8892, -16126, + -21722, -25114, -25956, -24163, -19917, -13649, -5996, 2266, + 10298, 17284, 22516, 25462, 25824, 23564, 18911, 12339, + 4514, -3768, -11668, -18384, -23234, -25725, -25605, -22885, + -17842, -10988, -3018, 5257, 12999, 19422, 23873, 25901, + 25299, 22129, 16712, 9599, 1511, -6729, -14287, -20394, + -24431, -25988, -24907, -21298, -15526, -8178, 0, 8178, + 15526, 21297, 24907, 25989, 24432, 20394, 14287, 6729, + -1511, -9599, -16712, -22128, -25299, -25901, -23873, -19422, + -13000, -5257, 3018, 10987, 17842, 22885, 25604, 25725, + 23234, 18384, 11668, 3768, -4514, -12339, -18911, -23563, + -25824, -25462, -22516, -17284, -10298, -2266, 5995, 13649, + 19917, 24162, 25956, 25114, 21722, 16126, 8892, 756, + -7456, -14912, -20855, -24680, -26000, -24680, -20855, -14913, + -7457, 755, 8892, 16125, 21722, 25114, 25956, 24163, + 19917, 13649, 5996, -2265, -10297, -17284, -22516, -25462, + -25824, -23564, -18911, -12339, -4515, 3767, 11668, 18384, + 23234, 25725, 25605, 22885, 17842, 10988, 3018, -5257, + -12999, -19422, -23873, -25901, -25299, -22129, -16712, -9599, + -1512, 6729, 14286, 20394, 24431, 25988, 24907, 21298, + 15526, 8178, 0, -8177, -15525, -21297, -24907, -25989, + -24432, -20395, -14287, -6729, 1511, 9599, 16712, 22128, + 25299, 25901, 23873, 19422, 13000, 5257, -3018, -10987, + -17842, -22885, -25604, -25725, -23234, -18385, -11669, -3768, + 4514, 12339, 18911, 23563, 25824, 25462, 22516, 17284, + 10298, 2266, -5995, -13649, -19916, -24162, -25955, -25114, + -21722, -16126, -8892, -756, 7456, 14912, 20854, 24680}, + { +// Carrier 5 Phase 3 + 18384, 11668, 3768, -4514, -12339, -18911, -23564, -25824, + -25462, -22516, -17284, -10298, -2266, 5995, 13649, 19917, + 24163, 25956, 25114, 21722, 16126, 8892, 756, -7456, + -14912, -20855, -24680, -26000, -24680, -20855, -14913, -7456, + 756, 8892, 16126, 21722, 25114, 25956, 24163, 19917, + 13649, 5996, -2265, -10298, -17284, -22516, -25462, -25824, + -23564, -18911, -12339, -4514, 3768, 11668, 18384, 23234, + 25725, 25605, 22885, 17842, 10988, 3018, -5257, -12999, + -19422, -23873, -25901, -25299, -22129, -16712, -9599, -1511, + 6729, 14287, 20394, 24431, 25988, 24907, 21298, 15526, + 8178, 0, -8178, -15526, -21297, -24907, -25989, -24432, + -20394, -14287, -6729, 1511, 9599, 16712, 22128, 25299, + 25901, 23873, 19422, 13000, 5257, -3018, -10987, -17842, + -22885, -25604, -25725, -23234, -18384, -11668, -3768, 4514, + 12339, 18911, 23563, 25824, 25462, 22516, 17284, 10298, + 2266, -5995, -13649, -19917, -24162, -25955, -25114, -21722, + -16126, -8892, -756, 7456, 14912, 20855, 24680, 26000, + 24680, 20855, 14913, 7457, -755, -8892, -16125, -21722, + -25114, -25956, -24163, -19917, -13649, -5996, 2265, 10297, + 17284, 22516, 25462, 25824, 23564, 18911, 12339, 4515, + -3767, -11668, -18384, -23234, -25725, -25605, -22885, -17842, + -10988, -3018, 5257, 12999, 19422, 23873, 25901, 25299, + 22129, 16712, 9599, 1512, -6728, -14286, -20394, -24431, + -25988, -24907, -21298, -15526, -8178, 0, 8177, 15525, + 21297, 24907, 25989, 24432, 20395, 14287, 6729, -1511, + -9599, -16712, -22128, -25299, -25901, -23873, -19422, -13000, + -5258, 3018, 10987, 17841, 22885, 25604, 25725, 23234}, + },{{ + +// Carrier 6 Phase 0 + 0, 8892, 16712, 22516, 25605, 25605, 22516, 16712, + 8892, 0, -8892, -16712, -22516, -25604, -25605, -22516, + -16712, -8892, 0, 8892, 16712, 22516, 25604, 25605, + 22516, 16712, 8892, 0, -8892, -16712, -22516, -25604, + -25605, -22516, -16712, -8892, 0, 8892, 16712, 22516, + 25604, 25605, 22516, 16712, 8892, 0, -8892, -16712, + -22516, -25604, -25605, -22516, -16712, -8892, 0, 8892, + 16712, 22516, 25604, 25605, 22516, 16712, 8892, 0, + -8892, -16712, -22516, -25604, -25605, -22516, -16712, -8892, + 0, 8892, 16712, 22516, 25604, 25605, 22516, 16712, + 8892, 0, -8892, -16712, -22516, -25604, -25605, -22516, + -16712, -8892, 0, 8892, 16712, 22516, 25604, 25605, + 22516, 16712, 8892, 0, -8892, -16712, -22516, -25604, + -25605, -22516, -16712, -8892, 0, 8892, 16712, 22516, + 25604, 25605, 22516, 16712, 8892, 0, -8892, -16712, + -22516, -25604, -25605, -22516, -16712, -8892, 0, 8892, + 16712, 22516, 25604, 25605, 22516, 16712, 8892, 0, + -8892, -16712, -22516, -25604, -25605, -22516, -16712, -8892, + 0, 8892, 16712, 22516, 25604, 25605, 22516, 16712, + 8892, 0, -8892, -16712, -22516, -25604, -25605, -22516, + -16712, -8892, 0, 8892, 16712, 22516, 25604, 25605, + 22516, 16712, 8892, 0, -8892, -16712, -22516, -25604, + -25605, -22516, -16712, -8892, 0, 8892, 16712, 22516, + 25604, 25605, 22516, 16712, 8892, 0, -8892, -16712, + -22516, -25604, -25605, -22516, -16712, -8892, 0, 8892, + 16712, 22516, 25604, 25605, 22516, 16712, 8892, 0, + -8892, -16712, -22516, -25604, -25605, -22516, -16712, -8892}, + { +// Carrier 6 Phase 1 + 18384, 23564, 25901, 25114, 21297, 14912, 6729, -2266, + -10988, -18384, -23563, -25901, -25114, -21297, -14913, -6729, + 2266, 10988, 18384, 23563, 25901, 25114, 21297, 14913, + 6729, -2266, -10988, -18384, -23563, -25901, -25114, -21297, + -14913, -6729, 2266, 10988, 18384, 23563, 25901, 25114, + 21297, 14913, 6729, -2266, -10988, -18384, -23563, -25901, + -25114, -21297, -14913, -6729, 2266, 10988, 18384, 23563, + 25901, 25114, 21297, 14913, 6729, -2266, -10988, -18384, + -23563, -25901, -25114, -21297, -14913, -6729, 2265, 10988, + 18384, 23563, 25901, 25114, 21297, 14913, 6729, -2265, + -10988, -18384, -23563, -25901, -25114, -21297, -14913, -6729, + 2265, 10988, 18384, 23563, 25901, 25114, 21297, 14913, + 6729, -2265, -10988, -18384, -23563, -25901, -25114, -21297, + -14913, -6729, 2265, 10988, 18384, 23563, 25901, 25114, + 21297, 14913, 6729, -2265, -10987, -18384, -23563, -25901, + -25114, -21298, -14913, -6729, 2265, 10987, 18384, 23563, + 25901, 25114, 21298, 14913, 6729, -2265, -10987, -18384, + -23563, -25901, -25114, -21298, -14913, -6729, 2265, 10987, + 18384, 23563, 25901, 25114, 21298, 14913, 6729, -2265, + -10987, -18384, -23563, -25901, -25114, -21298, -14913, -6729, + 2265, 10987, 18384, 23563, 25901, 25114, 21298, 14913, + 6729, -2265, -10987, -18384, -23563, -25901, -25114, -21298, + -14913, -6729, 2265, 10987, 18384, 23563, 25901, 25114, + 21298, 14913, 6729, -2265, -10987, -18384, -23563, -25901, + -25114, -21298, -14913, -6729, 2265, 10987, 18384, 23563, + 25901, 25114, 21298, 14913, 6729, -2265, -10987, -18384, + -23563, -25901, -25114, -21298, -14913, -6729, 2265, 10987}, + { +// Carrier 6 Phase 2 + 26000, 24432, 19917, 13000, 4514, -4514, -12999, -19917, + -24432, -26000, -24432, -19917, -13000, -4514, 4514, 12999, + 19917, 24432, 26000, 24432, 19917, 13000, 4514, -4514, + -12999, -19917, -24431, -26000, -24432, -19917, -13000, -4514, + 4514, 12999, 19917, 24431, 26000, 24432, 19917, 13000, + 4514, -4514, -12999, -19917, -24431, -26000, -24432, -19917, + -13000, -4514, 4514, 12999, 19917, 24431, 26000, 24432, + 19917, 13000, 4514, -4514, -12999, -19917, -24431, -26000, + -24432, -19917, -13000, -4514, 4514, 12999, 19917, 24431, + 26000, 24432, 19917, 13000, 4514, -4514, -12999, -19917, + -24431, -26000, -24432, -19917, -13000, -4514, 4514, 12999, + 19917, 24431, 26000, 24432, 19917, 13000, 4514, -4514, + -12999, -19917, -24431, -26000, -24432, -19917, -13000, -4514, + 4514, 12999, 19917, 24431, 26000, 24432, 19917, 13000, + 4515, -4514, -12999, -19917, -24431, -26000, -24432, -19917, + -13000, -4515, 4514, 12999, 19917, 24431, 26000, 24432, + 19917, 13000, 4515, -4514, -12999, -19917, -24431, -26000, + -24432, -19917, -13000, -4515, 4514, 12999, 19917, 24431, + 26000, 24432, 19917, 13000, 4515, -4514, -12999, -19917, + -24431, -26000, -24432, -19917, -13000, -4515, 4514, 12999, + 19917, 24431, 26000, 24432, 19917, 13000, 4515, -4514, + -12999, -19917, -24431, -26000, -24432, -19917, -13000, -4515, + 4514, 12999, 19917, 24431, 26000, 24432, 19917, 13000, + 4515, -4514, -12999, -19916, -24431, -26000, -24432, -19917, + -13000, -4515, 4514, 12999, 19916, 24431, 26000, 24432, + 19917, 13000, 4515, -4514, -12999, -19916, -24431, -26000, + -24432, -19917, -13000, -4515, 4514, 12999, 19916, 24431}, + { +// Carrier 6 Phase 3 + 18384, 10988, 2266, -6729, -14912, -21297, -25114, -25901, + -23564, -18384, -10988, -2266, 6729, 14912, 21297, 25114, + 25901, 23564, 18384, 10988, 2266, -6729, -14912, -21297, + -25114, -25901, -23564, -18384, -10988, -2266, 6729, 14912, + 21297, 25114, 25901, 23564, 18384, 10988, 2266, -6729, + -14912, -21297, -25114, -25901, -23564, -18384, -10988, -2266, + 6729, 14912, 21297, 25114, 25901, 23564, 18384, 10988, + 2266, -6729, -14912, -21297, -25114, -25901, -23564, -18384, + -10988, -2266, 6729, 14912, 21297, 25114, 25901, 23564, + 18384, 10988, 2266, -6729, -14912, -21297, -25114, -25901, + -23564, -18384, -10988, -2266, 6729, 14912, 21297, 25114, + 25901, 23564, 18384, 10988, 2266, -6729, -14912, -21297, + -25114, -25901, -23564, -18384, -10988, -2266, 6729, 14912, + 21297, 25114, 25901, 23564, 18384, 10988, 2266, -6729, + -14912, -21297, -25114, -25901, -23564, -18384, -10988, -2266, + 6729, 14912, 21297, 25114, 25901, 23564, 18384, 10988, + 2266, -6729, -14912, -21297, -25114, -25901, -23564, -18384, + -10988, -2266, 6729, 14912, 21297, 25114, 25901, 23564, + 18384, 10988, 2266, -6729, -14912, -21297, -25114, -25901, + -23564, -18384, -10988, -2266, 6729, 14912, 21297, 25114, + 25901, 23564, 18384, 10988, 2266, -6729, -14912, -21297, + -25114, -25901, -23564, -18384, -10988, -2266, 6729, 14912, + 21297, 25114, 25901, 23564, 18384, 10988, 2266, -6729, + -14912, -21297, -25114, -25901, -23564, -18384, -10988, -2266, + 6729, 14912, 21297, 25114, 25901, 23564, 18384, 10988, + 2266, -6729, -14912, -21297, -25113, -25901, -23564, -18384, + -10988, -2266, 6729, 14912, 21297, 25113, 25901, 23564}, + },{{ + +// Carrier 7 Phase 0 + 0, 9599, 17842, 23564, 25956, 24680, 19917, 12339, + 3018, -6729, -15526, -22129, -25605, -25462, -21722, -14912, + -5996, 3768, 13000, 20394, 24907, 25901, 23234, 17284, + 8892, -756, -10298, -18384, -23873, -25989, -24431, -19422, + -11668, -2266, 7456, 16126, 22516, 25725, 25299, 21297, + 14287, 5257, -4514, -13649, -20855, -25114, -25824, -22885, + -16712, -8178, 1511, 10988, 18911, 24163, 26000, 24163, + 18911, 10988, 1511, -8178, -16712, -22885, -25824, -25114, + -20855, -13649, -4514, 5257, 14287, 21298, 25299, 25725, + 22516, 16126, 7456, -2266, -11668, -19422, -24432, -25988, + -23873, -18384, -10297, -756, 8892, 17284, 23234, 25901, + 24907, 20394, 12999, 3768, -5996, -14913, -21722, -25462, + -25604, -22128, -15526, -6729, 3018, 12339, 19917, 24680, + 25956, 23563, 17842, 9599, 0, -9599, -17842, -23564, + -25956, -24680, -19917, -12339, -3018, 6729, 15526, 22129, + 25605, 25462, 21722, 14912, 5995, -3768, -13000, -20394, + -24907, -25901, -23234, -17284, -8892, 756, 10298, 18384, + 23873, 25989, 24431, 19422, 11668, 2265, -7457, -16126, + -22516, -25725, -25299, -21297, -14287, -5257, 4515, 13649, + 20855, 25114, 25824, 22885, 16712, 8177, -1512, -10988, + -18911, -24163, -26000, -24162, -18911, -10987, -1511, 8178, + 16712, 22885, 25824, 25113, 20855, 13649, 4514, -5257, + -14287, -21298, -25299, -25725, -22516, -16125, -7456, 2266, + 11669, 19422, 24432, 25988, 23873, 18384, 10297, 755, + -8892, -17284, -23234, -25901, -24907, -20394, -12999, -3767, + 5996, 14913, 21722, 25462, 25604, 22128, 15525, 6728, + -3018, -12339, -19917, -24680, -25955, -23563, -17842, -9599}, + { +// Carrier 7 Phase 1 + 18384, 23873, 25989, 24432, 19422, 11668, 2266, -7456, + -16126, -22516, -25725, -25299, -21297, -14287, -5257, 4514, + 13649, 20855, 25114, 25824, 22885, 16712, 8178, -1511, + -10988, -18911, -24163, -26000, -24163, -18911, -10988, -1511, + 8178, 16712, 22885, 25824, 25114, 20855, 13649, 4514, + -5257, -14287, -21297, -25299, -25725, -22516, -16126, -7456, + 2266, 11668, 19422, 24432, 25988, 23873, 18384, 10298, + 756, -8892, -17284, -23234, -25901, -24907, -20394, -12999, + -3768, 5996, 14913, 21722, 25462, 25604, 22128, 15526, + 6729, -3018, -12339, -19917, -24680, -25956, -23563, -17842, + -9599, 0, 9599, 17842, 23564, 25956, 24680, 19917, + 12339, 3018, -6729, -15526, -22129, -25605, -25462, -21722, + -14912, -5995, 3768, 13000, 20394, 24907, 25901, 23234, + 17284, 8892, -756, -10298, -18384, -23873, -25989, -24431, + -19422, -11668, -2265, 7457, 16126, 22516, 25725, 25299, + 21297, 14287, 5257, -4515, -13649, -20855, -25114, -25824, + -22885, -16712, -8177, 1511, 10988, 18911, 24163, 26000, + 24162, 18911, 10987, 1511, -8178, -16712, -22885, -25824, + -25114, -20855, -13649, -4514, 5257, 14287, 21298, 25299, + 25725, 22516, 16125, 7456, -2266, -11668, -19422, -24432, + -25988, -23873, -18384, -10297, -755, 8892, 17284, 23234, + 25901, 24907, 20394, 12999, 3767, -5996, -14913, -21722, + -25462, -25604, -22128, -15525, -6729, 3018, 12339, 19917, + 24680, 25955, 23563, 17842, 9599, 0, -9599, -17842, + -23564, -25956, -24680, -19916, -12339, -3018, 6729, 15526, + 22129, 25605, 25462, 21722, 14912, 5995, -3768, -13000, + -20395, -24907, -25901, -23234, -17284, -8892, 756, 10298}, + { +// Carrier 7 Phase 2 + 26000, 24163, 18911, 10988, 1511, -8178, -16712, -22885, + -25824, -25114, -20855, -13649, -4514, 5257, 14287, 21297, + 25299, 25725, 22516, 16126, 7456, -2266, -11668, -19422, + -24432, -25989, -23873, -18384, -10298, -756, 8892, 17284, + 23234, 25901, 24907, 20394, 12999, 3768, -5996, -14913, + -21722, -25462, -25604, -22128, -15526, -6729, 3018, 12339, + 19917, 24680, 25956, 23563, 17842, 9599, 0, -9599, + -17842, -23564, -25956, -24680, -19917, -12339, -3018, 6729, + 15526, 22129, 25605, 25462, 21722, 14912, 5995, -3768, + -13000, -20394, -24907, -25901, -23234, -17284, -8892, 756, + 10298, 18384, 23873, 25989, 24431, 19422, 11668, 2265, + -7457, -16126, -22516, -25725, -25299, -21297, -14287, -5257, + 4515, 13649, 20855, 25114, 25824, 22885, 16712, 8177, + -1511, -10988, -18911, -24163, -26000, -24162, -18911, -10987, + -1511, 8178, 16712, 22885, 25824, 25114, 20855, 13649, + 4514, -5257, -14287, -21298, -25299, -25725, -22516, -16125, + -7456, 2266, 11668, 19422, 24432, 25988, 23873, 18384, + 10297, 755, -8892, -17284, -23234, -25901, -24907, -20394, + -12999, -3767, 5996, 14913, 21722, 25462, 25604, 22128, + 15525, 6729, -3018, -12339, -19917, -24680, -25955, -23563, + -17842, -9599, 0, 9599, 17842, 23564, 25956, 24680, + 19916, 12339, 3018, -6729, -15526, -22129, -25605, -25462, + -21722, -14912, -5995, 3768, 13000, 20394, 24907, 25901, + 23234, 17284, 8892, -756, -10298, -18384, -23873, -25989, + -24431, -19422, -11668, -2265, 7457, 16126, 22516, 25725, + 25299, 21297, 14286, 5257, -4515, -13649, -20855, -25114, + -25824, -22885, -16712, -8177, 1512, 10988, 18911, 24163}, + { +// Carrier 7 Phase 3 + 18384, 10298, 756, -8892, -17284, -23234, -25901, -24907, + -20394, -12999, -3768, 5996, 14913, 21722, 25462, 25604, + 22129, 15526, 6729, -3018, -12339, -19917, -24680, -25956, + -23563, -17842, -9599, 0, 9599, 17842, 23564, 25956, + 24680, 19917, 12339, 3018, -6729, -15526, -22129, -25605, + -25462, -21722, -14912, -5995, 3768, 13000, 20394, 24907, + 25901, 23234, 17284, 8892, -756, -10298, -18384, -23873, + -25989, -24431, -19422, -11668, -2265, 7456, 16126, 22516, + 25725, 25299, 21297, 14287, 5257, -4514, -13649, -20855, + -25114, -25824, -22885, -16712, -8178, 1511, 10988, 18911, + 24163, 26000, 24162, 18911, 10987, 1511, -8178, -16712, + -22885, -25824, -25114, -20855, -13649, -4514, 5257, 14287, + 21298, 25299, 25725, 22516, 16125, 7456, -2266, -11668, + -19422, -24432, -25988, -23873, -18384, -10297, -756, 8892, + 17284, 23234, 25901, 24907, 20394, 12999, 3768, -5996, + -14913, -21722, -25462, -25604, -22128, -15525, -6729, 3018, + 12339, 19917, 24680, 25955, 23563, 17842, 9599, 0, + -9599, -17842, -23564, -25956, -24680, -19916, -12339, -3018, + 6729, 15526, 22129, 25605, 25462, 21722, 14912, 5995, + -3768, -13000, -20394, -24907, -25901, -23234, -17284, -8892, + 756, 10298, 18384, 23873, 25989, 24431, 19422, 11668, + 2265, -7457, -16126, -22516, -25725, -25299, -21297, -14286, + -5257, 4515, 13649, 20855, 25114, 25824, 22885, 16712, + 8177, -1512, -10988, -18911, -24163, -26000, -24162, -18911, + -10987, -1511, 8178, 16712, 22885, 25824, 25113, 20854, + 13649, 4514, -5257, -14287, -21298, -25299, -25725, -22516, + -16125, -7456, 2266, 11669, 19422, 24432, 25988, 23873}, + },{{ + +// Carrier 8 Phase 0 + 0, 10298, 18911, 24432, 25956, 23234, 16712, 7456, + -3018, -12999, -20855, -25299, -25605, -21722, -14287, -4514, + 5995, 15526, 22516, 25824, 24907, 19917, 11668, 1511, + -8892, -17842, -23873, -26000, -23873, -17842, -8892, 1511, + 11668, 19917, 24907, 25824, 22516, 15526, 5996, -4514, + -14287, -21722, -25604, -25299, -20855, -13000, -3018, 7456, + 16712, 23234, 25956, 24432, 18911, 10298, 0, -10297, + -18911, -24431, -25956, -23234, -16712, -7457, 3018, 12999, + 20855, 25299, 25605, 21722, 14287, 4514, -5995, -15526, + -22516, -25824, -24907, -19917, -11668, -1511, 8892, 17842, + 23873, 26000, 23873, 17842, 8892, -1511, -11668, -19917, + -24907, -25824, -22516, -15526, -5996, 4514, 14287, 21722, + 25604, 25299, 20855, 13000, 3018, -7456, -16712, -23234, + -25956, -24432, -18911, -10298, 0, 10297, 18911, 24431, + 25956, 23234, 16712, 7457, -3018, -12999, -20855, -25299, + -25605, -21722, -14287, -4515, 5995, 15525, 22516, 25824, + 24907, 19917, 11669, 1512, -8892, -17842, -23873, -26000, + -23873, -17842, -8892, 1511, 11668, 19916, 24907, 25824, + 22516, 15526, 5996, -4514, -14286, -21722, -25604, -25299, + -20855, -13000, -3018, 7456, 16712, 23234, 25955, 24432, + 18911, 10298, 0, -10297, -18911, -24431, -25956, -23234, + -16712, -7457, 3018, 12999, 20854, 25299, 25605, 21722, + 14287, 4515, -5995, -15525, -22516, -25824, -24907, -19917, + -11669, -1512, 8892, 17842, 23873, 26000, 23873, 17842, + 8892, -1511, -11668, -19916, -24907, -25824, -22516, -15526, + -5996, 4514, 14286, 21722, 25604, 25299, 20855, 13000, + 3018, -7456, -16712, -23234, -25955, -24432, -18912, -10298}, + { +// Carrier 8 Phase 1 + 18384, 24163, 25989, 23564, 17284, 8178, -2266, -12339, + -20394, -25114, -25725, -22129, -14913, -5257, 5257, 14912, + 22129, 25725, 25114, 20394, 12339, 2266, -8178, -17284, + -23563, -25989, -24163, -18384, -9599, 756, 10988, 19422, + 24680, 25901, 22885, 16126, 6729, -3768, -13649, -21297, + -25462, -25462, -21298, -13649, -3768, 6729, 16126, 22885, + 25901, 24680, 19422, 10988, 756, -9599, -18384, -24162, + -25989, -23564, -17284, -8178, 2265, 12339, 20394, 25114, + 25725, 22129, 14913, 5257, -5257, -14912, -22128, -25725, + -25114, -20394, -12339, -2266, 8178, 17284, 23563, 25988, + 24163, 18384, 9599, -756, -10987, -19422, -24680, -25901, + -22885, -16126, -6729, 3768, 13649, 21297, 25462, 25462, + 21298, 13649, 3768, -6729, -16125, -22885, -25901, -24680, + -19422, -10988, -756, 9599, 18384, 24162, 25989, 23564, + 17284, 8178, -2265, -12339, -20394, -25114, -25725, -22129, + -14913, -5257, 5257, 14912, 22128, 25725, 25114, 20394, + 12339, 2266, -8177, -17284, -23563, -25988, -24163, -18384, + -9599, 755, 10987, 19422, 24680, 25901, 22885, 16126, + 6729, -3767, -13649, -21297, -25462, -25462, -21298, -13649, + -3768, 6728, 16125, 22885, 25901, 24680, 19422, 10988, + 756, -9599, -18384, -24162, -25989, -23564, -17284, -8178, + 2265, 12339, 20394, 25113, 25725, 22129, 14913, 5258, + -5257, -14912, -22128, -25725, -25114, -20395, -12339, -2266, + 8177, 17284, 23563, 25988, 24163, 18385, 9599, -755, + -10987, -19422, -24680, -25901, -22885, -16126, -6729, 3767, + 13649, 21297, 25462, 25462, 21298, 13649, 3768, -6728, + -16125, -22885, -25901, -24680, -19422, -10988, -756, 9598}, + { +// Carrier 8 Phase 2 + 26000, 23873, 17842, 8892, -1511, -11668, -19917, -24907, + -25824, -22516, -15526, -5996, 4514, 14287, 21722, 25604, + 25299, 20855, 13000, 3018, -7456, -16712, -23234, -25956, + -24432, -18911, -10298, 0, 10298, 18911, 24431, 25956, + 23234, 16712, 7456, -3018, -12999, -20855, -25299, -25605, + -21722, -14287, -4514, 5995, 15526, 22516, 25824, 24907, + 19917, 11668, 1511, -8892, -17842, -23873, -26000, -23873, + -17842, -8892, 1511, 11668, 19917, 24907, 25824, 22516, + 15526, 5996, -4514, -14287, -21722, -25604, -25299, -20855, + -13000, -3018, 7456, 16712, 23234, 25956, 24432, 18911, + 10298, 0, -10297, -18911, -24431, -25956, -23234, -16712, + -7457, 3018, 12999, 20855, 25299, 25605, 21722, 14287, + 4515, -5995, -15525, -22516, -25824, -24907, -19917, -11668, + -1511, 8892, 17842, 23873, 26000, 23873, 17842, 8892, + -1511, -11668, -19917, -24907, -25824, -22516, -15526, -5996, + 4514, 14287, 21722, 25604, 25299, 20855, 13000, 3018, + -7456, -16712, -23234, -25955, -24432, -18911, -10298, 0, + 10297, 18911, 24431, 25956, 23234, 16712, 7457, -3018, + -12999, -20855, -25299, -25605, -21722, -14287, -4515, 5995, + 15525, 22516, 25824, 24907, 19917, 11669, 1512, -8892, + -17842, -23873, -26000, -23873, -17842, -8892, 1511, 11668, + 19916, 24907, 25824, 22516, 15526, 5996, -4514, -14286, + -21722, -25604, -25299, -20855, -13000, -3018, 7456, 16712, + 23234, 25955, 24432, 18911, 10298, 0, -10297, -18911, + -24431, -25956, -23234, -16712, -7457, 3018, 12999, 20854, + 25299, 25605, 21722, 14287, 4515, -5995, -15525, -22516, + -25824, -24907, -19917, -11669, -1512, 8892, 17841, 23873}, + { +// Carrier 8 Phase 3 + 18384, 9599, -756, -10988, -19422, -24680, -25901, -22885, + -16126, -6729, 3768, 13649, 21297, 25462, 25462, 21297, + 13649, 3768, -6729, -16126, -22885, -25901, -24680, -19422, + -10988, -756, 9599, 18384, 24163, 25989, 23564, 17284, + 8178, -2265, -12339, -20394, -25114, -25725, -22129, -14913, + -5257, 5257, 14912, 22128, 25725, 25114, 20394, 12339, + 2266, -8178, -17284, -23563, -25988, -24163, -18384, -9599, + 756, 10987, 19422, 24680, 25901, 22885, 16126, 6729, + -3768, -13649, -21297, -25462, -25462, -21298, -13649, -3768, + 6729, 16125, 22885, 25901, 24680, 19422, 10988, 756, + -9599, -18384, -24162, -25989, -23564, -17284, -8178, 2265, + 12339, 20394, 25114, 25725, 22129, 14913, 5257, -5257, + -14912, -22128, -25725, -25114, -20394, -12339, -2266, 8177, + 17284, 23563, 25988, 24163, 18384, 9599, -755, -10987, + -19422, -24680, -25901, -22885, -16126, -6729, 3767, 13649, + 21297, 25462, 25462, 21298, 13649, 3768, -6729, -16125, + -22885, -25901, -24680, -19422, -10988, -756, 9599, 18384, + 24162, 25989, 23564, 17284, 8178, -2265, -12339, -20394, + -25113, -25725, -22129, -14913, -5257, 5257, 14912, 22128, + 25725, 25114, 20395, 12339, 2266, -8177, -17284, -23563, + -25988, -24163, -18385, -9599, 755, 10987, 19422, 24680, + 25901, 22885, 16126, 6729, -3767, -13649, -21297, -25462, + -25462, -21298, -13649, -3768, 6728, 16125, 22885, 25901, + 24680, 19422, 10988, 756, -9599, -18384, -24162, -25989, + -23564, -17284, -8178, 2265, 12339, 20394, 25113, 25725, + 22129, 14913, 5258, -5257, -14912, -22128, -25725, -25114, + -20395, -12340, -2266, 8177, 17284, 23563, 25988, 24163}, + },{{ + +// Carrier 9 Phase 0 + 0, 10988, 19917, 25114, 25605, 21297, 13000, 2266, + -8892, -18384, -24432, -25901, -22516, -14913, -4514, 6729, + 16712, 23563, 26000, 23564, 16712, 6729, -4514, -14912, + -22516, -25901, -24432, -18384, -8892, 2266, 12999, 21297, + 25604, 25114, 19917, 10988, 0, -10988, -19917, -25114, + -25605, -21297, -13000, -2266, 8892, 18384, 24431, 25901, + 22516, 14913, 4514, -6729, -16712, -23563, -26000, -23564, + -16712, -6729, 4514, 14912, 22516, 25901, 24432, 18384, + 8892, -2265, -12999, -21297, -25604, -25114, -19917, -10988, + 0, 10988, 19917, 25114, 25605, 21297, 13000, 2266, + -8892, -18384, -24431, -25901, -22516, -14913, -4514, 6729, + 16712, 23563, 26000, 23564, 16712, 6729, -4514, -14912, + -22516, -25901, -24432, -18384, -8892, 2265, 12999, 21297, + 25604, 25114, 19917, 10988, 0, -10987, -19917, -25114, + -25605, -21298, -13000, -2266, 8892, 18384, 24431, 25901, + 22516, 14913, 4514, -6729, -16712, -23563, -26000, -23564, + -16712, -6729, 4514, 14912, 22516, 25901, 24432, 18384, + 8892, -2265, -12999, -21297, -25604, -25114, -19917, -10988, + 0, 10987, 19917, 25114, 25605, 21298, 13000, 2266, + -8892, -18384, -24431, -25901, -22516, -14913, -4514, 6729, + 16712, 23563, 26000, 23564, 16712, 6729, -4514, -14912, + -22516, -25901, -24432, -18384, -8892, 2265, 12999, 21297, + 25604, 25114, 19917, 10988, 0, -10987, -19917, -25114, + -25605, -21298, -13000, -2266, 8892, 18384, 24431, 25901, + 22516, 14913, 4515, -6729, -16712, -23563, -26000, -23564, + -16712, -6729, 4514, 14912, 22516, 25901, 24432, 18384, + 8892, -2265, -12999, -21297, -25604, -25114, -19917, -10988}, + { +// Carrier 9 Phase 1 + 18384, 24432, 25901, 22516, 14912, 4514, -6729, -16712, + -23563, -26000, -23564, -16712, -6729, 4514, 14912, 22516, + 25901, 24432, 18384, 8892, -2266, -12999, -21297, -25604, + -25114, -19917, -10988, 0, 10988, 19917, 25114, 25605, + 21297, 13000, 2266, -8892, -18384, -24431, -25901, -22516, + -14913, -4514, 6729, 16712, 23563, 26000, 23564, 16712, + 6729, -4514, -14912, -22516, -25901, -24432, -18384, -8892, + 2265, 12999, 21297, 25604, 25114, 19917, 10988, 0, + -10988, -19917, -25114, -25605, -21297, -13000, -2266, 8892, + 18384, 24431, 25901, 22516, 14913, 4514, -6729, -16712, + -23563, -26000, -23564, -16712, -6729, 4514, 14912, 22516, + 25901, 24432, 18384, 8892, -2265, -12999, -21297, -25604, + -25114, -19917, -10988, 0, 10987, 19917, 25114, 25605, + 21298, 13000, 2266, -8892, -18384, -24431, -25901, -22516, + -14913, -4514, 6729, 16712, 23563, 26000, 23564, 16712, + 6729, -4514, -14912, -22516, -25901, -24432, -18384, -8892, + 2265, 12999, 21297, 25604, 25114, 19917, 10988, 0, + -10987, -19917, -25114, -25605, -21298, -13000, -2266, 8892, + 18384, 24431, 25901, 22516, 14913, 4514, -6729, -16712, + -23563, -26000, -23564, -16712, -6729, 4514, 14912, 22516, + 25901, 24432, 18384, 8892, -2265, -12999, -21297, -25604, + -25114, -19917, -10988, 0, 10987, 19917, 25114, 25605, + 21298, 13000, 2266, -8892, -18384, -24431, -25901, -22516, + -14913, -4515, 6729, 16712, 23563, 26000, 23564, 16712, + 6729, -4514, -14912, -22516, -25901, -24432, -18384, -8892, + 2265, 12999, 21297, 25604, 25114, 19917, 10988, 0, + -10987, -19917, -25114, -25605, -21298, -13000, -2266, 8892}, + { +// Carrier 9 Phase 2 + 26000, 23564, 16712, 6729, -4514, -14912, -22516, -25901, + -24432, -18384, -8892, 2266, 12999, 21297, 25604, 25114, + 19917, 10988, 0, -10988, -19917, -25114, -25605, -21297, + -13000, -2266, 8892, 18384, 24431, 25901, 22516, 14913, + 4514, -6729, -16712, -23563, -26000, -23564, -16712, -6729, + 4514, 14912, 22516, 25901, 24432, 18384, 8892, -2266, + -12999, -21297, -25604, -25114, -19917, -10988, 0, 10988, + 19917, 25114, 25605, 21297, 13000, 2266, -8892, -18384, + -24431, -25901, -22516, -14913, -4514, 6729, 16712, 23563, + 26000, 23564, 16712, 6729, -4514, -14912, -22516, -25901, + -24432, -18384, -8892, 2265, 12999, 21297, 25604, 25114, + 19917, 10988, 0, -10987, -19917, -25114, -25605, -21298, + -13000, -2266, 8892, 18384, 24431, 25901, 22516, 14913, + 4514, -6729, -16712, -23563, -26000, -23564, -16712, -6729, + 4514, 14912, 22516, 25901, 24432, 18384, 8892, -2265, + -12999, -21297, -25604, -25114, -19917, -10988, 0, 10987, + 19917, 25114, 25605, 21298, 13000, 2266, -8892, -18384, + -24431, -25901, -22516, -14913, -4514, 6729, 16712, 23563, + 26000, 23564, 16712, 6729, -4514, -14912, -22516, -25901, + -24432, -18384, -8892, 2265, 12999, 21297, 25604, 25114, + 19917, 10988, 0, -10987, -19917, -25114, -25605, -21298, + -13000, -2266, 8892, 18384, 24431, 25901, 22516, 14913, + 4515, -6729, -16712, -23563, -26000, -23564, -16712, -6729, + 4514, 14912, 22516, 25901, 24432, 18384, 8892, -2265, + -12999, -21297, -25604, -25114, -19917, -10988, 0, 10987, + 19917, 25114, 25605, 21298, 13000, 2266, -8892, -18384, + -24431, -25901, -22516, -14913, -4515, 6729, 16712, 23563}, + { +// Carrier 9 Phase 3 + 18384, 8892, -2266, -12999, -21297, -25604, -25114, -19917, + -10988, 0, 10988, 19917, 25114, 25605, 21297, 13000, + 2266, -8892, -18384, -24432, -25901, -22516, -14913, -4514, + 6729, 16712, 23563, 26000, 23564, 16712, 6729, -4514, + -14912, -22516, -25901, -24432, -18384, -8892, 2266, 12999, + 21297, 25604, 25114, 19917, 10988, 0, -10988, -19917, + -25114, -25605, -21297, -13000, -2266, 8892, 18384, 24431, + 25901, 22516, 14913, 4514, -6729, -16712, -23563, -26000, + -23564, -16712, -6729, 4514, 14912, 22516, 25901, 24432, + 18384, 8892, -2265, -12999, -21297, -25604, -25114, -19917, + -10988, 0, 10988, 19917, 25114, 25605, 21297, 13000, + 2266, -8892, -18384, -24431, -25901, -22516, -14913, -4514, + 6729, 16712, 23563, 26000, 23564, 16712, 6729, -4514, + -14912, -22516, -25901, -24432, -18384, -8892, 2265, 12999, + 21297, 25604, 25114, 19917, 10988, 0, -10987, -19917, + -25114, -25605, -21298, -13000, -2266, 8892, 18384, 24431, + 25901, 22516, 14913, 4514, -6729, -16712, -23563, -26000, + -23564, -16712, -6729, 4514, 14912, 22516, 25901, 24432, + 18384, 8892, -2265, -12999, -21297, -25604, -25114, -19917, + -10988, 0, 10987, 19917, 25114, 25605, 21298, 13000, + 2266, -8892, -18384, -24431, -25901, -22516, -14913, -4515, + 6729, 16712, 23563, 26000, 23564, 16712, 6729, -4514, + -14912, -22516, -25901, -24432, -18384, -8892, 2265, 12999, + 21297, 25604, 25114, 19917, 10988, 0, -10987, -19917, + -25114, -25605, -21298, -13000, -2266, 8892, 18384, 24431, + 25901, 22516, 14913, 4515, -6729, -16712, -23563, -26000, + -23564, -16712, -6729, 4514, 14912, 22516, 25901, 24432}, + },{{ + +// Carrier 10 Phase 0 + 0, 11668, 20855, 25605, 24907, 18911, 8892, -3018, + -14287, -22516, -25956, -23873, -16712, -5996, 5996, 16712, + 23873, 25956, 22516, 14287, 3018, -8892, -18911, -24907, + -25604, -20855, -11668, 0, 11668, 20855, 25605, 24907, + 18911, 8892, -3018, -14287, -22516, -25956, -23873, -16712, + -5995, 5996, 16712, 23873, 25956, 22516, 14287, 3018, + -8892, -18911, -24907, -25604, -20855, -11668, 0, 11668, + 20855, 25605, 24907, 18911, 8892, -3018, -14287, -22516, + -25956, -23873, -16712, -5995, 5996, 16712, 23873, 25956, + 22516, 14287, 3018, -8892, -18911, -24907, -25604, -20855, + -11668, 0, 11668, 20855, 25605, 24907, 18911, 8892, + -3018, -14287, -22516, -25956, -23873, -16712, -5995, 5996, + 16712, 23873, 25956, 22516, 14287, 3018, -8892, -18911, + -24907, -25604, -20855, -11668, 0, 11668, 20855, 25605, + 24907, 18911, 8892, -3018, -14287, -22516, -25956, -23873, + -16712, -5995, 5996, 16712, 23873, 25956, 22516, 14287, + 3018, -8892, -18911, -24907, -25604, -20855, -11668, 0, + 11668, 20855, 25605, 24907, 18911, 8892, -3018, -14287, + -22516, -25956, -23873, -16712, -5995, 5996, 16712, 23873, + 25956, 22516, 14287, 3018, -8892, -18911, -24907, -25604, + -20855, -11668, 0, 11668, 20855, 25605, 24907, 18911, + 8892, -3018, -14287, -22516, -25956, -23873, -16712, -5995, + 5996, 16712, 23873, 25955, 22516, 14287, 3018, -8892, + -18911, -24907, -25604, -20855, -11668, 0, 11669, 20855, + 25605, 24907, 18911, 8892, -3018, -14287, -22516, -25956, + -23873, -16712, -5995, 5996, 16712, 23873, 25955, 22516, + 14287, 3018, -8892, -18911, -24907, -25604, -20855, -11668}, + { +// Carrier 10 Phase 1 + 18384, 24680, 25725, 21297, 12339, 756, -10988, -20394, + -25462, -25114, -19422, -9599, 2266, 13649, 22129, 25901, + 24163, 17284, 6729, -5257, -16126, -23564, -25989, -22885, + -14912, -3768, 8178, 18384, 24680, 25725, 21297, 12339, + 756, -10988, -20394, -25462, -25114, -19422, -9599, 2266, + 13649, 22129, 25901, 24163, 17284, 6729, -5257, -16126, + -23564, -25988, -22885, -14912, -3768, 8178, 18384, 24680, + 25725, 21297, 12339, 756, -10988, -20394, -25462, -25114, + -19422, -9599, 2266, 13649, 22129, 25901, 24163, 17284, + 6729, -5257, -16126, -23564, -25988, -22885, -14912, -3768, + 8178, 18384, 24680, 25725, 21297, 12339, 756, -10988, + -20394, -25462, -25114, -19422, -9599, 2266, 13649, 22129, + 25901, 24162, 17284, 6729, -5257, -16126, -23564, -25988, + -22885, -14912, -3768, 8178, 18384, 24680, 25725, 21297, + 12339, 756, -10988, -20394, -25462, -25114, -19422, -9599, + 2266, 13649, 22129, 25901, 24162, 17284, 6729, -5257, + -16126, -23564, -25988, -22885, -14912, -3768, 8178, 18384, + 24680, 25725, 21297, 12339, 756, -10988, -20394, -25462, + -25114, -19422, -9599, 2266, 13649, 22129, 25901, 24162, + 17284, 6729, -5257, -16126, -23564, -25988, -22885, -14912, + -3768, 8178, 18384, 24680, 25725, 21297, 12339, 755, + -10988, -20394, -25462, -25114, -19422, -9599, 2266, 13649, + 22129, 25901, 24162, 17284, 6729, -5257, -16126, -23564, + -25988, -22885, -14912, -3767, 8178, 18384, 24680, 25725, + 21297, 12339, 755, -10988, -20394, -25462, -25114, -19422, + -9599, 2266, 13649, 22129, 25901, 24162, 17284, 6729, + -5257, -16126, -23564, -25988, -22885, -14912, -3767, 8178}, + { +// Carrier 10 Phase 2 + 26000, 23234, 15526, 4514, -7456, -17842, -24432, -25824, + -21722, -12999, -1511, 10298, 19917, 25299, 25299, 19917, + 10298, -1511, -13000, -21722, -25824, -24432, -17842, -7456, + 4514, 15526, 23234, 26000, 23234, 15526, 4514, -7456, + -17842, -24432, -25824, -21722, -12999, -1511, 10298, 19917, + 25299, 25299, 19917, 10298, -1511, -13000, -21722, -25824, + -24431, -17842, -7456, 4514, 15526, 23234, 26000, 23234, + 15526, 4514, -7456, -17842, -24432, -25824, -21722, -12999, + -1511, 10298, 19917, 25299, 25299, 19917, 10297, -1511, + -13000, -21722, -25824, -24431, -17842, -7456, 4514, 15526, + 23234, 26000, 23234, 15526, 4514, -7456, -17842, -24432, + -25824, -21722, -12999, -1511, 10298, 19917, 25299, 25299, + 19917, 10297, -1511, -13000, -21722, -25824, -24431, -17842, + -7456, 4514, 15526, 23234, 26000, 23234, 15526, 4514, + -7457, -17842, -24432, -25824, -21722, -12999, -1511, 10298, + 19917, 25299, 25299, 19917, 10297, -1511, -13000, -21722, + -25824, -24431, -17842, -7456, 4515, 15526, 23234, 26000, + 23234, 15525, 4514, -7457, -17842, -24432, -25824, -21722, + -12999, -1511, 10298, 19917, 25299, 25299, 19917, 10297, + -1511, -13000, -21722, -25824, -24431, -17842, -7456, 4515, + 15526, 23234, 26000, 23234, 15525, 4514, -7457, -17842, + -24432, -25824, -21722, -12999, -1511, 10298, 19917, 25299, + 25299, 19917, 10297, -1512, -13000, -21722, -25824, -24431, + -17842, -7456, 4515, 15526, 23234, 26000, 23234, 15525, + 4514, -7457, -17842, -24432, -25824, -21722, -12999, -1511, + 10298, 19917, 25299, 25299, 19916, 10297, -1512, -13000, + -21722, -25824, -24431, -17842, -7456, 4515, 15526, 23234}, + { +// Carrier 10 Phase 3 + 18384, 8178, -3768, -14912, -22885, -25989, -23563, -16126, + -5257, 6729, 17284, 24163, 25901, 22129, 13649, 2266, + -9599, -19422, -25114, -25462, -20394, -10988, 756, 12339, + 21297, 25725, 24680, 18384, 8178, -3768, -14913, -22885, + -25989, -23563, -16126, -5257, 6729, 17284, 24163, 25901, + 22129, 13649, 2265, -9599, -19422, -25114, -25462, -20394, + -10988, 756, 12339, 21297, 25725, 24680, 18384, 8178, + -3768, -14913, -22885, -25989, -23563, -16126, -5257, 6729, + 17284, 24163, 25901, 22128, 13649, 2265, -9599, -19422, + -25114, -25462, -20394, -10987, 756, 12339, 21298, 25725, + 24680, 18384, 8178, -3768, -14913, -22885, -25989, -23563, + -16126, -5257, 6729, 17284, 24163, 25901, 22128, 13649, + 2265, -9599, -19422, -25114, -25462, -20394, -10987, 756, + 12339, 21298, 25725, 24680, 18384, 8178, -3768, -14913, + -22885, -25989, -23563, -16125, -5257, 6729, 17284, 24163, + 25901, 22128, 13649, 2265, -9599, -19422, -25114, -25462, + -20394, -10987, 756, 12339, 21298, 25725, 24680, 18384, + 8177, -3768, -14913, -22885, -25989, -23563, -16125, -5257, + 6729, 17284, 24163, 25901, 22128, 13649, 2265, -9599, + -19422, -25114, -25462, -20394, -10987, 756, 12339, 21298, + 25725, 24680, 18384, 8177, -3768, -14913, -22885, -25989, + -23563, -16125, -5257, 6729, 17284, 24163, 25901, 22128, + 13649, 2265, -9599, -19422, -25114, -25462, -20394, -10987, + 756, 12339, 21298, 25725, 24680, 18384, 8177, -3768, + -14913, -22885, -25989, -23563, -16125, -5257, 6729, 17284, + 24163, 25901, 22128, 13649, 2265, -9599, -19422, -25114, + -25462, -20394, -10987, 756, 12339, 21298, 25725, 24680}, + },{{ + +// Carrier 11 Phase 0 + 0, 12339, 21722, 25901, 23873, 16126, 4514, -8178, + -18911, -25114, -25299, -19422, -8892, 3768, 15526, 23563, + 25956, 22129, 13000, 756, -11668, -21297, -25824, -24163, + -16712, -5257, 7456, 18384, 24907, 25462, 19917, 9599, + -3018, -14912, -23234, -25989, -22516, -13649, -1511, 10987, + 20855, 25725, 24432, 17284, 5996, -6729, -17842, -24680, + -25605, -20394, -10298, 2265, 14287, 22885, 26000, 22885, + 14287, 2266, -10297, -20394, -25604, -24680, -17842, -6729, + 5995, 17284, 24431, 25725, 20855, 10988, -1511, -13649, + -22516, -25988, -23234, -14913, -3018, 9599, 19917, 25462, + 24907, 18384, 7457, -5257, -16712, -24162, -25824, -21298, + -11668, 756, 12999, 22128, 25956, 23564, 15526, 3768, + -8892, -19422, -25299, -25114, -18911, -8178, 4514, 16125, + 23873, 25901, 21722, 12339, 0, -12339, -21722, -25901, + -23873, -16126, -4515, 8177, 18911, 25114, 25299, 19422, + 8892, -3767, -15525, -23563, -25956, -22129, -13000, -756, + 11668, 21297, 25824, 24163, 16712, 5257, -7456, -18384, + -24907, -25462, -19917, -9599, 3018, 14912, 23234, 25989, + 22516, 13649, 1512, -10987, -20855, -25725, -24432, -17284, + -5996, 6728, 17842, 24680, 25605, 20395, 10298, -2265, + -14286, -22885, -26000, -22885, -14287, -2266, 10297, 20394, + 25604, 24680, 17842, 6729, -5995, -17284, -24431, -25725, + -20855, -10988, 1511, 13649, 22516, 25988, 23234, 14913, + 3018, -9598, -19916, -25462, -24907, -18385, -7457, 5257, + 16712, 24162, 25824, 21298, 11669, -755, -12999, -22128, + -25955, -23564, -15526, -3768, 8892, 19422, 25299, 25114, + 18912, 8178, -4514, -16125, -23873, -25901, -21722, -12340}, + { +// Carrier 11 Phase 1 + 18384, 24907, 25462, 19917, 9599, -3018, -14912, -23234, + -25989, -22516, -13649, -1511, 10988, 20855, 25725, 24432, + 17284, 5996, -6729, -17842, -24680, -25605, -20394, -10298, + 2266, 14287, 22885, 26000, 22885, 14287, 2266, -10298, + -20394, -25604, -24680, -17842, -6729, 5995, 17284, 24431, + 25725, 20855, 10988, -1511, -13649, -22516, -25988, -23234, + -14913, -3018, 9599, 19917, 25462, 24907, 18384, 7456, + -5257, -16712, -24162, -25824, -21298, -11668, 756, 12999, + 22128, 25956, 23564, 15526, 3768, -8892, -19422, -25299, + -25114, -18911, -8178, 4514, 16126, 23873, 25901, 21722, + 12339, 0, -12339, -21722, -25901, -23873, -16126, -4515, + 8177, 18911, 25114, 25299, 19422, 8892, -3768, -15525, + -23563, -25956, -22129, -13000, -756, 11668, 21297, 25824, + 24163, 16712, 5257, -7456, -18384, -24907, -25462, -19917, + -9599, 3018, 14912, 23234, 25989, 22516, 13649, 1512, + -10987, -20855, -25725, -24432, -17284, -5996, 6729, 17842, + 24680, 25605, 20394, 10298, -2265, -14287, -22885, -26000, + -22885, -14287, -2266, 10297, 20394, 25604, 24680, 17842, + 6729, -5995, -17284, -24431, -25725, -20855, -10988, 1511, + 13649, 22516, 25988, 23234, 14913, 3018, -9599, -19916, + -25462, -24907, -18385, -7457, 5257, 16712, 24162, 25824, + 21298, 11669, -755, -12999, -22128, -25955, -23564, -15526, + -3768, 8892, 19422, 25299, 25114, 18911, 8178, -4514, + -16125, -23873, -25901, -21722, -12339, 0, 12339, 21722, + 25901, 23873, 16126, 4515, -8177, -18911, -25113, -25299, + -19422, -8892, 3767, 15525, 23563, 25956, 22129, 13000, + 756, -11668, -21297, -25824, -24163, -16712, -5258, 7456}, + { +// Carrier 11 Phase 2 + 26000, 22885, 14287, 2266, -10298, -20394, -25605, -24680, + -17842, -6729, 5995, 17284, 24432, 25725, 20855, 10988, + -1511, -13649, -22516, -25989, -23234, -14913, -3018, 9599, + 19917, 25462, 24907, 18384, 7456, -5257, -16712, -24163, + -25824, -21297, -11668, 756, 12999, 22128, 25956, 23564, + 15526, 3768, -8892, -19422, -25299, -25114, -18911, -8178, + 4514, 16126, 23873, 25901, 21722, 12339, 0, -12339, + -21722, -25901, -23873, -16126, -4514, 8178, 18911, 25114, + 25299, 19422, 8892, -3768, -15526, -23563, -25956, -22129, + -13000, -756, 11668, 21297, 25824, 24163, 16712, 5257, + -7456, -18384, -24907, -25462, -19917, -9599, 3018, 14912, + 23234, 25989, 22516, 13649, 1511, -10987, -20855, -25725, + -24432, -17284, -5996, 6729, 17842, 24680, 25605, 20394, + 10298, -2265, -14287, -22885, -26000, -22885, -14287, -2266, + 10297, 20394, 25604, 24680, 17842, 6729, -5995, -17284, + -24431, -25725, -20855, -10988, 1511, 13649, 22516, 25988, + 23234, 14913, 3018, -9599, -19916, -25462, -24907, -18384, + -7457, 5257, 16712, 24162, 25824, 21298, 11669, -755, + -12999, -22128, -25955, -23564, -15526, -3768, 8892, 19422, + 25299, 25114, 18911, 8178, -4514, -16125, -23873, -25901, + -21722, -12339, 0, 12339, 21722, 25901, 23873, 16126, + 4515, -8177, -18911, -25113, -25299, -19422, -8892, 3767, + 15525, 23563, 25956, 22129, 13000, 756, -11668, -21297, + -25824, -24163, -16712, -5258, 7456, 18384, 24907, 25462, + 19917, 9599, -3018, -14912, -23234, -25989, -22516, -13649, + -1512, 10987, 20854, 25725, 24432, 17285, 5996, -6728, + -17841, -24680, -25605, -20395, -10298, 2265, 14286, 22885}, + { +// Carrier 11 Phase 3 + 18384, 7456, -5257, -16712, -24163, -25824, -21297, -11668, + 756, 12999, 22129, 25956, 23564, 15526, 3768, -8892, + -19422, -25299, -25114, -18911, -8178, 4514, 16126, 23873, + 25901, 21722, 12339, 0, -12339, -21722, -25901, -23873, + -16126, -4514, 8178, 18911, 25114, 25299, 19422, 8892, + -3768, -15526, -23563, -25956, -22129, -13000, -756, 11668, + 21297, 25824, 24163, 16712, 5257, -7456, -18384, -24907, + -25462, -19917, -9599, 3018, 14912, 23234, 25989, 22516, + 13649, 1511, -10987, -20855, -25725, -24432, -17284, -5996, + 6729, 17842, 24680, 25605, 20394, 10298, -2265, -14287, + -22885, -26000, -22885, -14287, -2266, 10297, 20394, 25604, + 24680, 17842, 6729, -5995, -17284, -24431, -25725, -20855, + -10988, 1511, 13649, 22516, 25988, 23234, 14913, 3018, + -9599, -19917, -25462, -24907, -18384, -7457, 5257, 16712, + 24162, 25824, 21298, 11668, -755, -12999, -22128, -25955, + -23564, -15526, -3768, 8892, 19422, 25299, 25114, 18911, + 8178, -4514, -16125, -23873, -25901, -21722, -12339, 0, + 12339, 21722, 25901, 23873, 16126, 4515, -8177, -18911, + -25113, -25299, -19422, -8892, 3767, 15525, 23563, 25956, + 22129, 13000, 756, -11668, -21297, -25824, -24163, -16712, + -5258, 7456, 18384, 24907, 25462, 19917, 9599, -3018, + -14912, -23234, -25989, -22516, -13649, -1512, 10987, 20854, + 25725, 24432, 17284, 5996, -6728, -17841, -24680, -25605, + -20395, -10298, 2265, 14286, 22885, 26000, 22885, 14287, + 2266, -10297, -20394, -25604, -24680, -17842, -6729, 5995, + 17284, 24431, 25725, 20855, 10988, -1511, -13649, -22516, + -25988, -23234, -14913, -3018, 9598, 19916, 25462, 24907}, + },{{ + +// Carrier 12 Phase 0 + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000, + 0, 12999, 22516, 26000, 22516, 13000, 0, -12999, + -22516, -26000, -22516, -13000, 0, 12999, 22516, 26000, + 22516, 13000, 0, -12999, -22516, -26000, -22516, -13000}, + { +// Carrier 12 Phase 1 + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729, + 18384, 25114, 25114, 18384, 6729, -6729, -18384, -25114, + -25114, -18384, -6729, 6729, 18384, 25114, 25114, 18384, + 6729, -6729, -18384, -25114, -25114, -18384, -6729, 6729}, + { +// Carrier 12 Phase 2 + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516, + 26000, 22516, 13000, 0, -12999, -22516, -26000, -22516, + -13000, 0, 12999, 22516, 26000, 22516, 13000, 0, + -12999, -22516, -26000, -22516, -13000, 0, 12999, 22516}, + { +// Carrier 12 Phase 3 + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114, + 18384, 6729, -6729, -18384, -25114, -25114, -18384, -6729, + 6729, 18384, 25114, 25114, 18384, 6729, -6729, -18384, + -25114, -25114, -18384, -6729, 6729, 18384, 25114, 25114}, + },{{ + +// Carrier 13 Phase 0 + 0, 13649, 23234, 25901, 20855, 9599, -4514, -17284, + -24907, -25114, -17842, -5257, 8892, 20394, 25824, 23563, + 14287, 756, -13000, -22885, -25956, -21297, -10298, 3768, + 16712, 24680, 25299, 18384, 5995, -8178, -19917, -25725, + -23873, -14912, -1511, 12339, 22516, 25989, 21722, 10988, + -3018, -16126, -24432, -25462, -18911, -6729, 7456, 19422, + 25605, 24163, 15526, 2265, -11668, -22129, -26000, -22129, + -11668, 2266, 15526, 24163, 25604, 19422, 7456, -6729, + -18911, -25462, -24431, -16126, -3018, 10988, 21722, 25989, + 22516, 12339, -1511, -14913, -23873, -25725, -19917, -8178, + 5996, 18384, 25299, 24680, 16712, 3768, -10298, -21297, + -25956, -22885, -12999, 756, 14287, 23564, 25824, 20394, + 8892, -5257, -17842, -25114, -24907, -17284, -4514, 9599, + 20855, 25901, 23234, 13649, 0, -13649, -23234, -25901, + -20855, -9599, 4514, 17284, 24907, 25114, 17842, 5257, + -8892, -20394, -25824, -23563, -14287, -756, 13000, 22885, + 25956, 21297, 10297, -3768, -16712, -24680, -25299, -18384, + -5995, 8178, 19917, 25725, 23873, 14912, 1511, -12339, + -22516, -25988, -21722, -10987, 3018, 16126, 24432, 25462, + 18911, 6729, -7457, -19422, -25605, -24162, -15526, -2265, + 11668, 22129, 26000, 22128, 11668, -2266, -15526, -24163, + -25604, -19422, -7456, 6729, 18911, 25462, 24431, 16125, + 3018, -10988, -21722, -25989, -22516, -12339, 1511, 14913, + 23873, 25725, 19917, 8177, -5996, -18384, -25299, -24680, + -16712, -3768, 10298, 21298, 25956, 22885, 12999, -756, + -14287, -23564, -25824, -20394, -8892, 5257, 17842, 25114, + 24907, 17284, 4514, -9599, -20855, -25901, -23234, -13649}, + { +// Carrier 13 Phase 1 + 18384, 25299, 24680, 16712, 3768, -10298, -21297, -25956, + -22885, -12999, 756, 14287, 23564, 25824, 20394, 8892, + -5257, -17842, -25114, -24907, -17284, -4514, 9599, 20855, + 25901, 23234, 13649, 0, -13649, -23234, -25901, -20855, + -9599, 4514, 17284, 24907, 25114, 17842, 5257, -8892, + -20394, -25824, -23563, -14287, -756, 13000, 22885, 25956, + 21297, 10298, -3768, -16712, -24680, -25299, -18384, -5995, + 8178, 19917, 25725, 23873, 14912, 1511, -12339, -22516, + -25988, -21722, -10988, 3018, 16126, 24432, 25462, 18911, + 6729, -7456, -19422, -25605, -24163, -15526, -2265, 11668, + 22129, 26000, 22128, 11668, -2266, -15526, -24163, -25604, + -19422, -7456, 6729, 18911, 25462, 24431, 16126, 3018, + -10988, -21722, -25989, -22516, -12339, 1511, 14913, 23873, + 25725, 19917, 8178, -5996, -18384, -25299, -24680, -16712, + -3768, 10298, 21298, 25956, 22885, 12999, -756, -14287, + -23564, -25824, -20394, -8892, 5257, 17842, 25114, 24907, + 17284, 4514, -9599, -20855, -25901, -23234, -13649, 0, + 13649, 23234, 25901, 20855, 9599, -4515, -17284, -24907, + -25114, -17842, -5257, 8892, 20394, 25824, 23563, 14287, + 756, -13000, -22885, -25956, -21297, -10297, 3768, 16712, + 24680, 25299, 18384, 5995, -8178, -19917, -25725, -23873, + -14912, -1511, 12339, 22516, 25988, 21722, 10987, -3018, + -16126, -24432, -25462, -18911, -6729, 7457, 19422, 25605, + 24162, 15525, 2265, -11668, -22129, -26000, -22128, -11668, + 2266, 15526, 24163, 25604, 19422, 7456, -6729, -18911, + -25462, -24431, -16125, -3018, 10988, 21722, 25989, 22516, + 12339, -1511, -14913, -23873, -25725, -19917, -8177, 5996}, + { +// Carrier 13 Phase 2 + 26000, 22129, 11668, -2266, -15526, -24163, -25605, -19422, + -7456, 6729, 18911, 25462, 24432, 16126, 3018, -10988, + -21722, -25989, -22516, -12339, 1511, 14913, 23873, 25725, + 19917, 8178, -5996, -18384, -25299, -24680, -16712, -3768, + 10298, 21297, 25956, 22885, 12999, -756, -14287, -23564, + -25824, -20394, -8892, 5257, 17842, 25114, 24907, 17284, + 4514, -9599, -20855, -25901, -23234, -13649, 0, 13649, + 23234, 25901, 20855, 9599, -4514, -17284, -24907, -25114, + -17842, -5257, 8892, 20394, 25824, 23563, 14287, 756, + -13000, -22885, -25956, -21297, -10298, 3768, 16712, 24680, + 25299, 18384, 5995, -8178, -19917, -25725, -23873, -14912, + -1511, 12339, 22516, 25988, 21722, 10987, -3018, -16126, + -24432, -25462, -18911, -6729, 7456, 19422, 25605, 24162, + 15526, 2265, -11668, -22129, -26000, -22128, -11668, 2266, + 15526, 24163, 25604, 19422, 7456, -6729, -18911, -25462, + -24431, -16126, -3018, 10988, 21722, 25989, 22516, 12339, + -1511, -14913, -23873, -25725, -19917, -8178, 5996, 18384, + 25299, 24680, 16712, 3768, -10298, -21298, -25956, -22885, + -12999, 756, 14287, 23564, 25824, 20394, 8892, -5257, + -17842, -25114, -24907, -17284, -4514, 9599, 20855, 25901, + 23234, 13649, 0, -13649, -23234, -25901, -20855, -9599, + 4515, 17284, 24907, 25114, 17842, 5257, -8892, -20394, + -25824, -23563, -14287, -756, 13000, 22885, 25956, 21297, + 10297, -3768, -16712, -24680, -25299, -18384, -5995, 8178, + 19917, 25725, 23873, 14912, 1511, -12339, -22516, -25988, + -21722, -10987, 3018, 16126, 24432, 25462, 18911, 6729, + -7457, -19422, -25605, -24162, -15525, -2265, 11668, 22129}, + { +// Carrier 13 Phase 3 + 18384, 5996, -8178, -19917, -25725, -23873, -14912, -1511, + 12339, 22516, 25989, 21722, 10988, -3018, -16126, -24432, + -25462, -18911, -6729, 7456, 19422, 25605, 24163, 15526, + 2266, -11668, -22129, -26000, -22129, -11668, 2266, 15526, + 24163, 25604, 19422, 7456, -6729, -18911, -25462, -24431, + -16126, -3018, 10988, 21722, 25989, 22516, 12339, -1511, + -14913, -23873, -25725, -19917, -8178, 5996, 18384, 25299, + 24680, 16712, 3768, -10298, -21297, -25956, -22885, -12999, + 756, 14287, 23564, 25824, 20394, 8892, -5257, -17842, + -25114, -24907, -17284, -4514, 9599, 20855, 25901, 23234, + 13649, 0, -13649, -23234, -25901, -20855, -9599, 4514, + 17284, 24907, 25114, 17842, 5257, -8892, -20394, -25824, + -23563, -14287, -756, 13000, 22885, 25956, 21297, 10297, + -3768, -16712, -24680, -25299, -18384, -5995, 8178, 19917, + 25725, 23873, 14912, 1511, -12339, -22516, -25988, -21722, + -10987, 3018, 16126, 24432, 25462, 18911, 6729, -7457, + -19422, -25605, -24162, -15526, -2265, 11668, 22129, 26000, + 22128, 11668, -2266, -15526, -24163, -25604, -19422, -7456, + 6729, 18911, 25462, 24431, 16125, 3018, -10988, -21722, + -25989, -22516, -12339, 1511, 14913, 23873, 25725, 19917, + 8177, -5996, -18384, -25299, -24680, -16712, -3768, 10298, + 21298, 25956, 22885, 12999, -756, -14287, -23564, -25824, + -20394, -8892, 5257, 17842, 25114, 24907, 17284, 4514, + -9599, -20855, -25901, -23234, -13649, 0, 13649, 23234, + 25901, 20855, 9599, -4515, -17284, -24907, -25114, -17842, + -5257, 8892, 20394, 25824, 23563, 14287, 755, -13000, + -22885, -25955, -21297, -10297, 3768, 16712, 24680, 25299}, + },{{ + +// Carrier 14 Phase 0 + 0, 14287, 23873, 25605, 18911, 5996, -8892, -20855, + -25956, -22516, -11668, 3018, 16712, 24907, 24907, 16712, + 3018, -11668, -22516, -25956, -20855, -8892, 5996, 18911, + 25605, 23873, 14287, 0, -14287, -23873, -25604, -18911, + -5995, 8892, 20855, 25956, 22516, 11668, -3018, -16712, + -24907, -24907, -16712, -3018, 11668, 22516, 25956, 20855, + 8892, -5996, -18911, -25605, -23873, -14287, 0, 14287, + 23873, 25604, 18911, 5995, -8892, -20855, -25956, -22516, + -11668, 3018, 16712, 24907, 24907, 16712, 3018, -11668, + -22516, -25956, -20855, -8892, 5996, 18911, 25605, 23873, + 14287, 0, -14287, -23873, -25604, -18911, -5995, 8892, + 20855, 25956, 22516, 11668, -3018, -16712, -24907, -24907, + -16712, -3018, 11668, 22516, 25956, 20855, 8892, -5996, + -18911, -25605, -23873, -14287, 0, 14287, 23873, 25604, + 18911, 5995, -8892, -20855, -25956, -22516, -11668, 3018, + 16712, 24907, 24907, 16712, 3018, -11668, -22516, -25956, + -20855, -8892, 5996, 18911, 25605, 23873, 14287, 0, + -14287, -23873, -25604, -18911, -5995, 8892, 20855, 25956, + 22516, 11668, -3018, -16712, -24907, -24907, -16712, -3018, + 11668, 22516, 25956, 20855, 8892, -5996, -18911, -25605, + -23873, -14287, 0, 14287, 23873, 25604, 18911, 5995, + -8892, -20855, -25956, -22516, -11668, 3018, 16712, 24907, + 24907, 16712, 3018, -11668, -22516, -25955, -20855, -8892, + 5996, 18911, 25605, 23873, 14287, 0, -14287, -23873, + -25604, -18911, -5995, 8892, 20855, 25956, 22516, 11668, + -3018, -16712, -24907, -24907, -16712, -3018, 11669, 22516, + 25955, 20855, 8892, -5996, -18911, -25605, -23873, -14286}, + { +// Carrier 14 Phase 1 + 18384, 25462, 24163, 14912, 756, -13649, -23563, -25725, + -19422, -6729, 8178, 20394, 25901, 22885, 12339, -2266, + -16126, -24680, -25114, -17284, -3768, 10988, 22129, 25989, + 21297, 9599, -5257, -18384, -25462, -24163, -14912, -756, + 13649, 23564, 25725, 19422, 6729, -8178, -20394, -25901, + -22885, -12339, 2266, 16126, 24680, 25114, 17284, 3768, + -10988, -22129, -25988, -21297, -9599, 5257, 18384, 25462, + 24163, 14912, 756, -13649, -23564, -25725, -19422, -6729, + 8178, 20394, 25901, 22885, 12339, -2266, -16126, -24680, + -25114, -17284, -3768, 10988, 22129, 25988, 21297, 9599, + -5257, -18384, -25462, -24162, -14912, -756, 13649, 23564, + 25725, 19422, 6729, -8178, -20394, -25901, -22885, -12339, + 2266, 16126, 24680, 25114, 17284, 3768, -10988, -22129, + -25988, -21297, -9599, 5257, 18384, 25462, 24162, 14912, + 756, -13649, -23564, -25725, -19422, -6729, 8178, 20394, + 25901, 22885, 12339, -2266, -16126, -24680, -25114, -17284, + -3768, 10988, 22129, 25988, 21297, 9599, -5257, -18384, + -25462, -24162, -14912, -756, 13649, 23564, 25725, 19422, + 6729, -8178, -20394, -25901, -22885, -12339, 2266, 16126, + 24680, 25114, 17284, 3768, -10988, -22129, -25988, -21297, + -9599, 5257, 18384, 25462, 24162, 14912, 755, -13649, + -23564, -25725, -19422, -6729, 8178, 20394, 25901, 22885, + 12339, -2266, -16126, -24680, -25114, -17284, -3767, 10988, + 22129, 25988, 21297, 9599, -5257, -18384, -25462, -24162, + -14912, -755, 13649, 23564, 25725, 19422, 6729, -8178, + -20394, -25901, -22885, -12339, 2266, 16126, 24680, 25113, + 17284, 3767, -10988, -22129, -25988, -21297, -9599, 5257}, + { +// Carrier 14 Phase 2 + 26000, 21722, 10298, -4514, -17842, -25299, -24432, -15526, + -1511, 13000, 23234, 25824, 19917, 7456, -7456, -19917, + -25824, -23234, -12999, 1511, 15526, 24432, 25299, 17842, + 4514, -10298, -21722, -26000, -21722, -10298, 4514, 17842, + 25299, 24431, 15526, 1511, -13000, -23234, -25824, -19917, + -7456, 7456, 19917, 25824, 23234, 12999, -1511, -15526, + -24432, -25299, -17842, -4514, 10298, 21722, 26000, 21722, + 10298, -4514, -17842, -25299, -24431, -15526, -1511, 13000, + 23234, 25824, 19917, 7456, -7456, -19917, -25824, -23234, + -12999, 1511, 15526, 24432, 25299, 17842, 4514, -10298, + -21722, -26000, -21722, -10297, 4514, 17842, 25299, 24431, + 15526, 1511, -13000, -23234, -25824, -19917, -7456, 7457, + 19917, 25824, 23234, 12999, -1511, -15526, -24432, -25299, + -17842, -4514, 10298, 21722, 26000, 21722, 10297, -4515, + -17842, -25299, -24431, -15526, -1511, 13000, 23234, 25824, + 19917, 7456, -7457, -19917, -25824, -23234, -12999, 1511, + 15526, 24432, 25299, 17842, 4514, -10298, -21722, -26000, + -21722, -10297, 4515, 17842, 25299, 24431, 15525, 1511, + -13000, -23234, -25824, -19917, -7456, 7457, 19917, 25824, + 23234, 12999, -1511, -15526, -24432, -25299, -17842, -4514, + 10298, 21722, 26000, 21722, 10297, -4515, -17842, -25299, + -24431, -15525, -1511, 13000, 23234, 25824, 19917, 7456, + -7457, -19917, -25824, -23234, -12999, 1512, 15526, 24432, + 25299, 17842, 4514, -10298, -21722, -26000, -21722, -10297, + 4515, 17842, 25299, 24431, 15525, 1511, -13000, -23234, + -25824, -19916, -7456, 7457, 19917, 25824, 23234, 12999, + -1512, -15526, -24432, -25299, -17842, -4514, 10298, 21722}, + { +// Carrier 14 Phase 3 + 18384, 5257, -9599, -21297, -25989, -22129, -10988, 3768, + 17284, 25114, 24680, 16126, 2266, -12339, -22885, -25901, + -20394, -8178, 6729, 19422, 25725, 23563, 13649, -756, + -14913, -24163, -25462, -18384, -5257, 9599, 21297, 25989, + 22129, 10988, -3768, -17284, -25114, -24680, -16126, -2265, + 12339, 22885, 25901, 20394, 8178, -6729, -19422, -25725, + -23563, -13649, 756, 14913, 24163, 25462, 18384, 5257, + -9599, -21297, -25989, -22128, -10988, 3768, 17284, 25114, + 24680, 16126, 2265, -12339, -22885, -25901, -20394, -8178, + 6729, 19422, 25725, 23563, 13649, -756, -14913, -24163, + -25462, -18384, -5257, 9599, 21298, 25989, 22128, 10987, + -3768, -17284, -25114, -24680, -16126, -2265, 12339, 22885, + 25901, 20394, 8178, -6729, -19422, -25725, -23563, -13649, + 756, 14913, 24163, 25462, 18384, 5257, -9599, -21298, + -25989, -22128, -10987, 3768, 17284, 25114, 24680, 16125, + 2265, -12339, -22885, -25901, -20394, -8178, 6729, 19422, + 25725, 23563, 13649, -756, -14913, -24163, -25462, -18384, + -5257, 9599, 21298, 25989, 22128, 10987, -3768, -17284, + -25114, -24680, -16125, -2265, 12339, 22885, 25901, 20394, + 8177, -6729, -19422, -25725, -23563, -13649, 756, 14913, + 24163, 25462, 18384, 5257, -9599, -21298, -25989, -22128, + -10987, 3768, 17284, 25114, 24680, 16125, 2265, -12339, + -22885, -25901, -20394, -8177, 6729, 19422, 25725, 23563, + 13649, -756, -14913, -24163, -25462, -18384, -5257, 9599, + 21298, 25989, 22128, 10987, -3768, -17284, -25114, -24680, + -16125, -2265, 12339, 22885, 25901, 20394, 8177, -6729, + -19422, -25725, -23563, -13649, 756, 14913, 24163, 25462}, + },{{ + +// Carrier 15 Phase 0 + 0, 14912, 24432, 25114, 16712, 2266, -12999, -23563, + -25605, -18384, -4514, 10988, 22516, 25901, 19917, 6729, + -8892, -21297, -26000, -21297, -8892, 6729, 19917, 25901, + 22516, 10988, -4514, -18384, -25604, -23564, -13000, 2265, + 16712, 25114, 24432, 14913, 0, -14912, -24431, -25114, + -16712, -2266, 12999, 23563, 25605, 18384, 4514, -10988, + -22516, -25901, -19917, -6729, 8892, 21297, 26000, 21298, + 8892, -6729, -19917, -25901, -22516, -10988, 4514, 18384, + 25604, 23564, 13000, -2265, -16712, -25114, -24432, -14913, + 0, 14912, 24431, 25114, 16712, 2266, -12999, -23563, + -25605, -18384, -4514, 10987, 22516, 25901, 19917, 6729, + -8892, -21297, -26000, -21298, -8892, 6729, 19917, 25901, + 22516, 10988, -4514, -18384, -25604, -23564, -13000, 2265, + 16712, 25114, 24432, 14913, 0, -14912, -24431, -25114, + -16712, -2266, 12999, 23563, 25605, 18384, 4515, -10987, + -22516, -25901, -19917, -6729, 8892, 21297, 26000, 21298, + 8892, -6729, -19917, -25901, -22516, -10988, 4514, 18384, + 25604, 23564, 13000, -2265, -16712, -25114, -24432, -14913, + 0, 14912, 24431, 25114, 16712, 2266, -12999, -23563, + -25605, -18384, -4515, 10987, 22516, 25901, 19917, 6729, + -8892, -21297, -26000, -21298, -8892, 6729, 19916, 25901, + 22516, 10988, -4514, -18384, -25604, -23564, -13000, 2265, + 16712, 25113, 24432, 14913, 0, -14912, -24431, -25114, + -16712, -2266, 12999, 23563, 25605, 18385, 4515, -10987, + -22516, -25901, -19917, -6729, 8892, 21297, 26000, 21298, + 8892, -6728, -19916, -25901, -22516, -10988, 4514, 18384, + 25604, 23564, 13000, -2265, -16712, -25113, -24432, -14913}, + { +// Carrier 15 Phase 1 + 18384, 25605, 23564, 13000, -2266, -16712, -25114, -24432, + -14913, 0, 14912, 24432, 25114, 16712, 2266, -12999, + -23563, -25605, -18384, -4514, 10988, 22516, 25901, 19917, + 6729, -8892, -21297, -26000, -21297, -8892, 6729, 19917, + 25901, 22516, 10988, -4514, -18384, -25604, -23564, -13000, + 2265, 16712, 25114, 24432, 14913, 0, -14912, -24431, + -25114, -16712, -2266, 12999, 23563, 25605, 18384, 4514, + -10987, -22516, -25901, -19917, -6729, 8892, 21297, 26000, + 21298, 8892, -6729, -19917, -25901, -22516, -10988, 4514, + 18384, 25604, 23564, 13000, -2265, -16712, -25114, -24432, + -14913, 0, 14912, 24431, 25114, 16712, 2266, -12999, + -23563, -25605, -18384, -4515, 10987, 22516, 25901, 19917, + 6729, -8892, -21297, -26000, -21298, -8892, 6729, 19917, + 25901, 22516, 10988, -4514, -18384, -25604, -23564, -13000, + 2265, 16712, 25114, 24432, 14913, 0, -14912, -24431, + -25114, -16712, -2266, 12999, 23563, 25605, 18384, 4515, + -10987, -22516, -25901, -19917, -6729, 8892, 21297, 26000, + 21298, 8892, -6729, -19916, -25901, -22516, -10988, 4514, + 18384, 25604, 23564, 13000, -2265, -16712, -25114, -24432, + -14913, 0, 14912, 24431, 25114, 16712, 2266, -12999, + -23563, -25605, -18384, -4515, 10987, 22516, 25901, 19917, + 6729, -8892, -21297, -26000, -21298, -8892, 6728, 19916, + 25901, 22516, 10988, -4514, -18384, -25604, -23564, -13000, + 2265, 16712, 25113, 24432, 14913, 0, -14912, -24431, + -25114, -16712, -2266, 12999, 23563, 25605, 18385, 4515, + -10987, -22516, -25901, -19917, -6729, 8892, 21297, 26000, + 21298, 8892, -6728, -19916, -25901, -22516, -10988, 4514}, + { +// Carrier 15 Phase 2 + 26000, 21297, 8892, -6729, -19917, -25901, -22516, -10988, + 4514, 18384, 25604, 23564, 13000, -2266, -16712, -25114, + -24432, -14913, 0, 14912, 24431, 25114, 16712, 2266, + -12999, -23563, -25605, -18384, -4514, 10988, 22516, 25901, + 19917, 6729, -8892, -21297, -26000, -21297, -8892, 6729, + 19917, 25901, 22516, 10988, -4514, -18384, -25604, -23564, + -13000, 2265, 16712, 25114, 24432, 14913, 0, -14912, + -24431, -25114, -16712, -2266, 12999, 23563, 25605, 18384, + 4514, -10987, -22516, -25901, -19917, -6729, 8892, 21297, + 26000, 21298, 8892, -6729, -19917, -25901, -22516, -10988, + 4514, 18384, 25604, 23564, 13000, -2265, -16712, -25114, + -24432, -14913, 0, 14912, 24431, 25114, 16712, 2266, + -12999, -23563, -25605, -18384, -4515, 10987, 22516, 25901, + 19917, 6729, -8892, -21297, -26000, -21298, -8892, 6729, + 19917, 25901, 22516, 10988, -4514, -18384, -25604, -23564, + -13000, 2265, 16712, 25114, 24432, 14913, 0, -14912, + -24431, -25114, -16712, -2266, 12999, 23563, 25605, 18384, + 4515, -10987, -22516, -25901, -19917, -6729, 8892, 21297, + 26000, 21298, 8892, -6729, -19916, -25901, -22516, -10988, + 4514, 18384, 25604, 23564, 13000, -2265, -16712, -25114, + -24432, -14913, 0, 14912, 24431, 25114, 16712, 2266, + -12999, -23563, -25605, -18384, -4515, 10987, 22516, 25901, + 19917, 6729, -8892, -21297, -26000, -21298, -8892, 6728, + 19916, 25901, 22516, 10988, -4514, -18384, -25604, -23564, + -13000, 2265, 16712, 25113, 24432, 14913, 0, -14912, + -24431, -25114, -16712, -2266, 12999, 23563, 25605, 18385, + 4515, -10987, -22516, -25901, -19917, -6729, 8892, 21297}, + { +// Carrier 15 Phase 3 + 18384, 4514, -10988, -22516, -25901, -19917, -6729, 8892, + 21297, 26000, 21297, 8892, -6729, -19917, -25901, -22516, + -10988, 4514, 18384, 25604, 23564, 13000, -2266, -16712, + -25114, -24432, -14913, 0, 14912, 24431, 25114, 16712, + 2266, -12999, -23563, -25605, -18384, -4514, 10988, 22516, + 25901, 19917, 6729, -8892, -21297, -26000, -21298, -8892, + 6729, 19917, 25901, 22516, 10988, -4514, -18384, -25604, + -23564, -13000, 2265, 16712, 25114, 24432, 14913, 0, + -14912, -24431, -25114, -16712, -2266, 12999, 23563, 25605, + 18384, 4514, -10987, -22516, -25901, -19917, -6729, 8892, + 21297, 26000, 21298, 8892, -6729, -19917, -25901, -22516, + -10988, 4514, 18384, 25604, 23564, 13000, -2265, -16712, + -25114, -24432, -14913, 0, 14912, 24431, 25114, 16712, + 2266, -12999, -23563, -25605, -18384, -4515, 10987, 22516, + 25901, 19917, 6729, -8892, -21297, -26000, -21298, -8892, + 6729, 19917, 25901, 22516, 10988, -4514, -18384, -25604, + -23564, -13000, 2265, 16712, 25114, 24432, 14913, 0, + -14912, -24431, -25114, -16712, -2266, 12999, 23563, 25605, + 18384, 4515, -10987, -22516, -25901, -19917, -6729, 8892, + 21297, 26000, 21298, 8892, -6729, -19916, -25901, -22516, + -10988, 4514, 18384, 25604, 23564, 13000, -2265, -16712, + -25113, -24432, -14913, 0, 14912, 24431, 25114, 16712, + 2266, -12999, -23563, -25605, -18384, -4515, 10987, 22516, + 25901, 19917, 6729, -8892, -21297, -26000, -21298, -8892, + 6728, 19916, 25901, 22516, 10988, -4514, -18384, -25604, + -23564, -13000, 2265, 16712, 25113, 24432, 14913, 0, + -14912, -24431, -25114, -16712, -2266, 12999, 23563, 25605}, + },{{ + +// Carrier 16 Phase 0 + 0, 15526, 24907, 24432, 14287, -1511, -16712, -25299, + -23873, -13000, 3018, 17842, 25605, 23234, 11668, -4514, + -18911, -25824, -22516, -10298, 5996, 19917, 25956, 21722, + 8892, -7456, -20855, -26000, -20855, -7456, 8892, 21722, + 25956, 19917, 5996, -10298, -22516, -25824, -18911, -4514, + 11668, 23234, 25604, 17842, 3018, -13000, -23873, -25299, + -16712, -1511, 14287, 24432, 24907, 15526, 0, -15526, + -24907, -24432, -14287, 1511, 16712, 25299, 23873, 12999, + -3018, -17842, -25605, -23234, -11668, 4514, 18911, 25824, + 22516, 10298, -5996, -19917, -25956, -21722, -8892, 7456, + 20855, 26000, 20855, 7456, -8892, -21722, -25956, -19917, + -5995, 10298, 22516, 25824, 18911, 4514, -11668, -23234, + -25604, -17842, -3018, 13000, 23873, 25299, 16712, 1511, + -14287, -24432, -24907, -15526, 0, 15526, 24907, 24431, + 14287, -1511, -16712, -25299, -23873, -12999, 3018, 17842, + 25605, 23234, 11668, -4514, -18911, -25824, -22516, -10298, + 5996, 19917, 25956, 21722, 8892, -7456, -20855, -26000, + -20855, -7456, 8892, 21722, 25956, 19917, 5995, -10298, + -22516, -25824, -18911, -4514, 11668, 23234, 25604, 17842, + 3018, -13000, -23873, -25299, -16712, -1511, 14287, 24432, + 24907, 15526, 0, -15526, -24907, -24431, -14287, 1511, + 16712, 25299, 23873, 12999, -3018, -17842, -25605, -23234, + -11668, 4514, 18911, 25824, 22516, 10298, -5996, -19917, + -25956, -21722, -8892, 7456, 20855, 26000, 20855, 7456, + -8892, -21722, -25956, -19917, -5995, 10298, 22516, 25824, + 18911, 4514, -11668, -23234, -25604, -17842, -3018, 13000, + 23873, 25299, 16712, 1511, -14287, -24432, -24907, -15526}, + { +// Carrier 16 Phase 1 + 18384, 25725, 22885, 10988, -5257, -19422, -25901, -22129, + -9599, 6729, 20394, 25989, 21297, 8178, -8178, -21297, + -25989, -20394, -6729, 9599, 22129, 25901, 19422, 5257, + -10988, -22885, -25725, -18384, -3768, 12339, 23564, 25462, + 17284, 2266, -13649, -24163, -25114, -16126, -756, 14913, + 24680, 24680, 14912, -756, -16126, -25114, -24163, -13649, + 2266, 17284, 25462, 23563, 12339, -3768, -18384, -25725, + -22885, -10988, 5257, 19422, 25901, 22129, 9599, -6729, + -20394, -25989, -21297, -8178, 8178, 21297, 25989, 20394, + 6729, -9599, -22129, -25901, -19422, -5257, 10988, 22885, + 25725, 18384, 3768, -12339, -23564, -25462, -17284, -2266, + 13649, 24163, 25114, 16126, 756, -14913, -24680, -24680, + -14912, 756, 16126, 25114, 24163, 13649, -2266, -17284, + -25462, -23563, -12339, 3768, 18384, 25725, 22885, 10988, + -5257, -19422, -25901, -22129, -9599, 6729, 20394, 25989, + 21297, 8178, -8178, -21297, -25989, -20394, -6729, 9599, + 22129, 25901, 19422, 5257, -10988, -22885, -25725, -18384, + -3768, 12339, 23564, 25462, 17284, 2266, -13649, -24163, + -25114, -16126, -756, 14913, 24680, 24680, 14912, -756, + -16126, -25114, -24163, -13649, 2266, 17284, 25462, 23563, + 12339, -3768, -18384, -25725, -22885, -10988, 5257, 19422, + 25901, 22129, 9599, -6729, -20394, -25989, -21297, -8178, + 8178, 21297, 25988, 20394, 6729, -9599, -22129, -25901, + -19422, -5257, 10988, 22885, 25725, 18384, 3768, -12339, + -23564, -25462, -17284, -2265, 13649, 24163, 25114, 16126, + 756, -14913, -24680, -24680, -14912, 756, 16126, 25114, + 24163, 13649, -2266, -17284, -25462, -23563, -12339, 3768}, + { +// Carrier 16 Phase 2 + 26000, 20855, 7456, -8892, -21722, -25956, -19917, -5996, + 10298, 22516, 25824, 18911, 4514, -11668, -23234, -25605, + -17842, -3018, 13000, 23873, 25299, 16712, 1511, -14287, + -24432, -24907, -15526, 0, 15526, 24907, 24432, 14287, + -1511, -16712, -25299, -23873, -12999, 3018, 17842, 25605, + 23234, 11668, -4514, -18911, -25824, -22516, -10298, 5996, + 19917, 25956, 21722, 8892, -7456, -20855, -26000, -20855, + -7456, 8892, 21722, 25956, 19917, 5995, -10298, -22516, + -25824, -18911, -4514, 11668, 23234, 25604, 17842, 3018, + -13000, -23873, -25299, -16712, -1511, 14287, 24432, 24907, + 15526, 0, -15526, -24907, -24432, -14287, 1511, 16712, + 25299, 23873, 12999, -3018, -17842, -25605, -23234, -11668, + 4514, 18911, 25824, 22516, 10298, -5996, -19917, -25956, + -21722, -8892, 7456, 20855, 26000, 20855, 7456, -8892, + -21722, -25956, -19917, -5995, 10298, 22516, 25824, 18911, + 4514, -11668, -23234, -25604, -17842, -3018, 13000, 23873, + 25299, 16712, 1511, -14287, -24432, -24907, -15526, 0, + 15526, 24907, 24431, 14287, -1511, -16712, -25299, -23873, + -12999, 3018, 17842, 25605, 23234, 11668, -4514, -18911, + -25824, -22516, -10298, 5996, 19917, 25956, 21722, 8892, + -7456, -20855, -26000, -20855, -7456, 8892, 21722, 25956, + 19917, 5995, -10298, -22516, -25824, -18911, -4514, 11668, + 23234, 25604, 17842, 3018, -13000, -23873, -25299, -16712, + -1511, 14287, 24432, 24907, 15526, 0, -15526, -24907, + -24431, -14287, 1511, 16712, 25299, 23873, 12999, -3018, + -17842, -25605, -23234, -11668, 4514, 18911, 25824, 22516, + 10298, -5996, -19917, -25956, -21722, -8892, 7456, 20855}, + { +// Carrier 16 Phase 3 + 18384, 3768, -12339, -23564, -25462, -17284, -2266, 13649, + 24163, 25114, 16126, 756, -14912, -24680, -24680, -14912, + 756, 16126, 25114, 24163, 13649, -2266, -17284, -25462, + -23563, -12339, 3768, 18384, 25725, 22885, 10988, -5257, + -19422, -25901, -22129, -9599, 6729, 20394, 25989, 21297, + 8178, -8178, -21297, -25989, -20394, -6729, 9599, 22129, + 25901, 19422, 5257, -10988, -22885, -25725, -18384, -3768, + 12339, 23564, 25462, 17284, 2266, -13649, -24163, -25114, + -16126, -756, 14913, 24680, 24680, 14912, -756, -16126, + -25114, -24163, -13649, 2266, 17284, 25462, 23563, 12339, + -3768, -18384, -25725, -22885, -10988, 5257, 19422, 25901, + 22129, 9599, -6729, -20394, -25989, -21297, -8178, 8178, + 21297, 25989, 20394, 6729, -9599, -22129, -25901, -19422, + -5257, 10988, 22885, 25725, 18384, 3768, -12339, -23564, + -25462, -17284, -2266, 13649, 24163, 25114, 16126, 756, + -14913, -24680, -24680, -14912, 756, 16126, 25114, 24163, + 13649, -2266, -17284, -25462, -23563, -12339, 3768, 18384, + 25725, 22885, 10988, -5257, -19422, -25901, -22129, -9599, + 6729, 20394, 25989, 21297, 8178, -8178, -21297, -25988, + -20394, -6729, 9599, 22129, 25901, 19422, 5257, -10988, + -22885, -25725, -18384, -3768, 12339, 23564, 25462, 17284, + 2265, -13649, -24163, -25114, -16126, -756, 14913, 24680, + 24680, 14912, -756, -16126, -25114, -24163, -13649, 2266, + 17284, 25462, 23563, 12339, -3768, -18384, -25725, -22885, + -10988, 5257, 19422, 25901, 22128, 9599, -6729, -20394, + -25989, -21297, -8178, 8178, 21297, 25988, 20394, 6729, + -9599, -22129, -25901, -19422, -5257, 10988, 22885, 25725}, + },{{ + +// Carrier 17 Phase 0 + 0, 16126, 25299, 23564, 11668, -5257, -19917, -25989, + -20855, -6729, 10298, 22885, 25604, 17284, 1511, -14913, + -24907, -24163, -12999, 3768, 18911, 25901, 21722, 8178, + -8892, -22129, -25824, -18384, -3018, 13649, 24432, 24680, + 14287, -2266, -17842, -25725, -22516, -9599, 7456, 21298, + 25956, 19422, 4514, -12339, -23873, -25114, -15526, 756, + 16712, 25462, 23234, 10987, -5996, -20394, -26000, -20394, + -5995, 10988, 23234, 25462, 16712, 756, -15526, -25114, + -23873, -12339, 4515, 19422, 25956, 21297, 7456, -9599, + -22516, -25725, -17842, -2265, 14287, 24680, 24431, 13649, + -3018, -18384, -25824, -22128, -8892, 8178, 21722, 25901, + 18911, 3768, -13000, -24163, -24907, -14912, 1511, 17284, + 25605, 22885, 10297, -6729, -20855, -25988, -19916, -5257, + 11669, 23564, 25299, 16125, 0, -16126, -25299, -23563, + -11668, 5257, 19917, 25989, 20855, 6729, -10298, -22885, + -25604, -17284, -1511, 14913, 24907, 24162, 12999, -3768, + -18911, -25901, -21722, -8177, 8892, 22129, 25824, 18384, + 3018, -13649, -24432, -24680, -14286, 2266, 17842, 25725, + 22516, 9599, -7457, -21298, -25955, -19422, -4514, 12339, + 23873, 25113, 15525, -756, -16712, -25462, -23234, -10987, + 5996, 20395, 26000, 20394, 5995, -10988, -23234, -25462, + -16712, -755, 15526, 25114, 23873, 12339, -4515, -19422, + -25956, -21297, -7456, 9599, 22516, 25725, 17841, 2265, + -14287, -24680, -24431, -13649, 3018, 18385, 25824, 22128, + 8892, -8178, -21722, -25901, -18911, -3767, 13000, 24163, + 24907, 14912, -1512, -17285, -25605, -22885, -10297, 6729, + 20855, 25988, 19916, 5257, -11669, -23564, -25299, -16125}, + { +// Carrier 17 Phase 1 + 18384, 25824, 22129, 8892, -8178, -21722, -25901, -18911, + -3768, 13000, 24163, 24907, 14912, -1511, -17284, -25605, + -22885, -10298, 6729, 20855, 25989, 19917, 5257, -11668, + -23564, -25299, -16126, 0, 16126, 25299, 23563, 11668, + -5257, -19917, -25989, -20855, -6729, 10298, 22885, 25604, + 17284, 1511, -14913, -24907, -24163, -12999, 3768, 18911, + 25901, 21722, 8178, -8892, -22129, -25824, -18384, -3018, + 13649, 24432, 24680, 14287, -2266, -17842, -25725, -22516, + -9599, 7457, 21298, 25956, 19422, 4514, -12339, -23873, + -25114, -15525, 756, 16712, 25462, 23234, 10987, -5996, + -20394, -26000, -20394, -5995, 10988, 23234, 25462, 16712, + 755, -15526, -25114, -23873, -12339, 4515, 19422, 25956, + 21297, 7456, -9599, -22516, -25725, -17842, -2265, 14287, + 24680, 24431, 13649, -3018, -18384, -25824, -22128, -8892, + 8178, 21722, 25901, 18911, 3767, -13000, -24163, -24907, + -14912, 1512, 17284, 25605, 22885, 10297, -6729, -20855, + -25988, -19916, -5257, 11669, 23564, 25299, 16125, 0, + -16126, -25299, -23563, -11668, 5257, 19917, 25989, 20854, + 6728, -10298, -22885, -25604, -17284, -1511, 14913, 24907, + 24162, 12999, -3768, -18911, -25901, -21722, -8177, 8892, + 22129, 25824, 18384, 3018, -13649, -24432, -24680, -14286, + 2266, 17842, 25725, 22516, 9598, -7457, -21298, -25955, + -19422, -4514, 12339, 23873, 25113, 15525, -756, -16712, + -25462, -23234, -10987, 5996, 20395, 26000, 20394, 5995, + -10988, -23234, -25462, -16712, -755, 15526, 25114, 23873, + 12339, -4515, -19422, -25956, -21297, -7456, 9599, 22516, + 25725, 17841, 2265, -14287, -24680, -24431, -13648, 3018}, + { +// Carrier 17 Phase 2 + 26000, 20394, 5996, -10988, -23234, -25462, -16712, -756, + 15526, 25114, 23873, 12339, -4514, -19422, -25956, -21297, + -7456, 9599, 22516, 25725, 17842, 2266, -14287, -24680, + -24431, -13649, 3018, 18384, 25824, 22129, 8892, -8178, + -21722, -25901, -18911, -3768, 13000, 24163, 24907, 14912, + -1511, -17284, -25605, -22885, -10297, 6729, 20855, 25988, + 19917, 5257, -11668, -23564, -25299, -16126, 0, 16126, + 25299, 23563, 11668, -5257, -19917, -25989, -20855, -6729, + 10298, 22885, 25604, 17284, 1511, -14913, -24907, -24162, + -12999, 3768, 18911, 25901, 21722, 8178, -8892, -22129, + -25824, -18384, -3018, 13649, 24432, 24680, 14287, -2266, + -17842, -25725, -22516, -9599, 7457, 21298, 25956, 19422, + 4514, -12339, -23873, -25114, -15525, 756, 16712, 25462, + 23234, 10987, -5996, -20394, -26000, -20394, -5995, 10988, + 23234, 25462, 16712, 755, -15526, -25114, -23873, -12339, + 4515, 19422, 25956, 21297, 7456, -9599, -22516, -25725, + -17842, -2265, 14287, 24680, 24431, 13649, -3018, -18384, + -25824, -22128, -8892, 8178, 21722, 25901, 18911, 3767, + -13000, -24163, -24907, -14912, 1512, 17284, 25605, 22885, + 10297, -6729, -20855, -25988, -19916, -5257, 11669, 23564, + 25299, 16125, 0, -16126, -25299, -23563, -11668, 5258, + 19917, 25989, 20854, 6728, -10298, -22885, -25604, -17284, + -1511, 14913, 24907, 24162, 12999, -3768, -18912, -25901, + -21722, -8177, 8892, 22129, 25824, 18384, 3017, -13649, + -24432, -24680, -14286, 2266, 17842, 25725, 22516, 9598, + -7457, -21298, -25955, -19422, -4514, 12340, 23873, 25113, + 15525, -756, -16712, -25462, -23234, -10987, 5996, 20395}, + { +// Carrier 17 Phase 3 + 18384, 3018, -13649, -24432, -24680, -14287, 2266, 17842, + 25725, 22516, 9599, -7456, -21297, -25956, -19422, -4514, + 12339, 23873, 25114, 15526, -756, -16712, -25462, -23234, + -10988, 5996, 20394, 26000, 20394, 5995, -10988, -23234, + -25462, -16712, -756, 15526, 25114, 23873, 12339, -4514, + -19422, -25956, -21297, -7456, 9599, 22516, 25725, 17842, + 2265, -14287, -24680, -24431, -13649, 3018, 18384, 25824, + 22128, 8892, -8178, -21722, -25901, -18911, -3768, 13000, + 24163, 24907, 14912, -1511, -17284, -25605, -22885, -10297, + 6729, 20855, 25988, 19917, 5257, -11668, -23564, -25299, + -16125, 0, 16126, 25299, 23563, 11668, -5257, -19917, + -25989, -20855, -6729, 10298, 22885, 25604, 17284, 1511, + -14913, -24907, -24162, -12999, 3768, 18911, 25901, 21722, + 8177, -8892, -22129, -25824, -18384, -3018, 13649, 24432, + 24680, 14287, -2266, -17842, -25725, -22516, -9599, 7457, + 21298, 25955, 19422, 4514, -12339, -23873, -25113, -15525, + 756, 16712, 25462, 23234, 10987, -5996, -20395, -26000, + -20394, -5995, 10988, 23234, 25462, 16712, 755, -15526, + -25114, -23873, -12339, 4515, 19422, 25956, 21297, 7456, + -9599, -22516, -25725, -17842, -2265, 14287, 24680, 24431, + 13649, -3018, -18385, -25824, -22128, -8892, 8178, 21722, + 25901, 18911, 3767, -13000, -24163, -24907, -14912, 1512, + 17285, 25605, 22885, 10297, -6729, -20855, -25988, -19916, + -5257, 11669, 23564, 25299, 16125, 0, -16126, -25299, + -23563, -11668, 5258, 19917, 25989, 20854, 6728, -10298, + -22885, -25604, -17284, -1511, 14913, 24907, 24162, 12999, + -3768, -18912, -25901, -21722, -8177, 8893, 22129, 25824}, + },{{ + +// Carrier 18 Phase 0 + 0, 16712, 25605, 22516, 8892, -8892, -22516, -25605, + -16712, 0, 16712, 25604, 22516, 8892, -8892, -22516, + -25605, -16712, 0, 16712, 25604, 22516, 8892, -8892, + -22516, -25605, -16712, 0, 16712, 25604, 22516, 8892, + -8892, -22516, -25605, -16712, 0, 16712, 25604, 22516, + 8892, -8892, -22516, -25605, -16712, 0, 16712, 25604, + 22516, 8892, -8892, -22516, -25605, -16712, 0, 16712, + 25604, 22516, 8892, -8892, -22516, -25605, -16712, 0, + 16712, 25604, 22516, 8892, -8892, -22516, -25605, -16712, + 0, 16712, 25604, 22516, 8892, -8892, -22516, -25605, + -16712, 0, 16712, 25604, 22516, 8892, -8892, -22516, + -25605, -16712, 0, 16712, 25604, 22516, 8892, -8892, + -22516, -25605, -16712, 0, 16712, 25604, 22516, 8892, + -8892, -22516, -25605, -16712, 0, 16712, 25604, 22516, + 8892, -8892, -22516, -25605, -16712, 0, 16712, 25604, + 22516, 8892, -8892, -22516, -25605, -16712, 0, 16712, + 25604, 22516, 8892, -8892, -22516, -25605, -16712, 0, + 16712, 25604, 22516, 8892, -8892, -22516, -25605, -16712, + 0, 16712, 25604, 22516, 8892, -8892, -22516, -25605, + -16712, 0, 16712, 25604, 22516, 8892, -8892, -22516, + -25605, -16712, 0, 16712, 25604, 22516, 8892, -8892, + -22516, -25605, -16712, 0, 16712, 25604, 22516, 8892, + -8892, -22516, -25605, -16712, 0, 16712, 25604, 22516, + 8892, -8892, -22516, -25605, -16712, 0, 16712, 25604, + 22516, 8892, -8892, -22516, -25605, -16712, 0, 16712, + 25604, 22516, 8892, -8892, -22516, -25605, -16712, 0, + 16712, 25604, 22516, 8892, -8892, -22516, -25605, -16712}, + { +// Carrier 18 Phase 1 + 18384, 25901, 21297, 6729, -10988, -23563, -25114, -14913, + 2266, 18384, 25901, 21297, 6729, -10988, -23563, -25114, + -14913, 2266, 18384, 25901, 21297, 6729, -10988, -23563, + -25114, -14913, 2266, 18384, 25901, 21297, 6729, -10988, + -23563, -25114, -14913, 2265, 18384, 25901, 21297, 6729, + -10988, -23563, -25114, -14913, 2265, 18384, 25901, 21297, + 6729, -10988, -23563, -25114, -14913, 2265, 18384, 25901, + 21297, 6729, -10988, -23563, -25114, -14913, 2265, 18384, + 25901, 21298, 6729, -10987, -23563, -25114, -14913, 2265, + 18384, 25901, 21298, 6729, -10987, -23563, -25114, -14913, + 2265, 18384, 25901, 21298, 6729, -10987, -23563, -25114, + -14913, 2265, 18384, 25901, 21298, 6729, -10987, -23563, + -25114, -14913, 2265, 18384, 25901, 21298, 6729, -10987, + -23563, -25114, -14913, 2265, 18384, 25901, 21298, 6729, + -10987, -23563, -25114, -14913, 2265, 18384, 25901, 21298, + 6729, -10987, -23563, -25114, -14913, 2265, 18384, 25901, + 21298, 6729, -10987, -23563, -25114, -14913, 2265, 18384, + 25901, 21298, 6729, -10987, -23563, -25114, -14913, 2265, + 18384, 25901, 21298, 6729, -10987, -23563, -25114, -14913, + 2265, 18384, 25901, 21298, 6729, -10987, -23563, -25114, + -14913, 2265, 18384, 25901, 21298, 6729, -10987, -23563, + -25114, -14913, 2265, 18384, 25901, 21298, 6729, -10987, + -23563, -25114, -14913, 2265, 18384, 25901, 21298, 6729, + -10987, -23563, -25114, -14913, 2265, 18384, 25901, 21298, + 6729, -10987, -23563, -25114, -14913, 2265, 18384, 25901, + 21298, 6729, -10987, -23563, -25114, -14913, 2265, 18384, + 25901, 21298, 6729, -10987, -23563, -25114, -14913, 2265}, + { +// Carrier 18 Phase 2 + 26000, 19917, 4514, -12999, -24432, -24432, -13000, 4514, + 19917, 26000, 19917, 4514, -12999, -24432, -24432, -13000, + 4514, 19917, 26000, 19917, 4514, -12999, -24431, -24432, + -13000, 4514, 19917, 26000, 19917, 4514, -12999, -24431, + -24432, -13000, 4514, 19917, 26000, 19917, 4514, -12999, + -24431, -24432, -13000, 4514, 19917, 26000, 19917, 4514, + -12999, -24431, -24432, -13000, 4514, 19917, 26000, 19917, + 4514, -12999, -24431, -24432, -13000, 4514, 19917, 26000, + 19917, 4514, -12999, -24431, -24432, -13000, 4514, 19917, + 26000, 19917, 4514, -12999, -24431, -24432, -13000, 4514, + 19917, 26000, 19917, 4514, -12999, -24431, -24432, -13000, + 4514, 19917, 26000, 19917, 4514, -12999, -24431, -24432, + -13000, 4514, 19917, 26000, 19917, 4514, -12999, -24431, + -24432, -13000, 4514, 19917, 26000, 19917, 4515, -12999, + -24431, -24432, -13000, 4514, 19917, 26000, 19917, 4515, + -12999, -24431, -24432, -13000, 4514, 19917, 26000, 19917, + 4515, -12999, -24431, -24432, -13000, 4514, 19917, 26000, + 19917, 4515, -12999, -24431, -24432, -13000, 4514, 19917, + 26000, 19917, 4515, -12999, -24431, -24432, -13000, 4514, + 19917, 26000, 19917, 4515, -12999, -24431, -24432, -13000, + 4514, 19917, 26000, 19917, 4515, -12999, -24431, -24432, + -13000, 4514, 19917, 26000, 19917, 4515, -12999, -24431, + -24432, -13000, 4514, 19916, 26000, 19917, 4515, -12999, + -24431, -24432, -13000, 4514, 19916, 26000, 19917, 4515, + -12999, -24431, -24432, -13000, 4514, 19916, 26000, 19917, + 4515, -12999, -24431, -24432, -13000, 4514, 19916, 26000, + 19917, 4515, -12999, -24431, -24432, -13000, 4514, 19916}, + { +// Carrier 18 Phase 3 + 18384, 2266, -14912, -25114, -23564, -10988, 6729, 21297, + 25901, 18384, 2266, -14912, -25114, -23564, -10988, 6729, + 21297, 25901, 18384, 2266, -14912, -25114, -23564, -10988, + 6729, 21297, 25901, 18384, 2266, -14912, -25114, -23564, + -10988, 6729, 21297, 25901, 18384, 2266, -14912, -25114, + -23564, -10988, 6729, 21297, 25901, 18384, 2266, -14912, + -25114, -23564, -10988, 6729, 21297, 25901, 18384, 2266, + -14912, -25114, -23564, -10988, 6729, 21297, 25901, 18384, + 2266, -14912, -25114, -23564, -10988, 6729, 21297, 25901, + 18384, 2266, -14912, -25114, -23564, -10988, 6729, 21297, + 25901, 18384, 2266, -14912, -25114, -23564, -10988, 6729, + 21297, 25901, 18384, 2266, -14912, -25114, -23564, -10988, + 6729, 21297, 25901, 18384, 2266, -14912, -25114, -23564, + -10988, 6729, 21297, 25901, 18384, 2266, -14912, -25114, + -23564, -10988, 6729, 21297, 25901, 18384, 2266, -14912, + -25114, -23564, -10988, 6729, 21297, 25901, 18384, 2266, + -14912, -25114, -23564, -10988, 6729, 21297, 25901, 18384, + 2266, -14912, -25114, -23564, -10988, 6729, 21297, 25901, + 18384, 2266, -14912, -25114, -23564, -10988, 6729, 21297, + 25901, 18384, 2266, -14912, -25114, -23564, -10988, 6729, + 21297, 25901, 18384, 2266, -14912, -25114, -23564, -10988, + 6729, 21297, 25901, 18384, 2266, -14912, -25114, -23564, + -10988, 6729, 21297, 25901, 18384, 2266, -14912, -25114, + -23564, -10988, 6729, 21297, 25901, 18384, 2266, -14912, + -25114, -23564, -10988, 6729, 21297, 25901, 18384, 2266, + -14912, -25113, -23564, -10988, 6729, 21297, 25901, 18384, + 2266, -14912, -25113, -23564, -10988, 6729, 21297, 25901}, + },{{ + +// Carrier 19 Phase 0 + 0, 17284, 25824, 21297, 5996, -12339, -24432, -24163, + -11668, 6729, 21722, 25725, 16712, -756, -17842, -25901, + -20855, -5257, 12999, 24680, 23873, 10988, -7456, -22129, + -25605, -16126, 1511, 18384, 25956, 20394, 4514, -13649, + -24907, -23564, -10298, 8178, 22516, 25462, 15526, -2266, + -18911, -25989, -19917, -3768, 14287, 25114, 23234, 9599, + -8892, -22885, -25299, -14912, 3018, 19422, 26000, 19422, + 3018, -14913, -25299, -22885, -8892, 9599, 23234, 25114, + 14287, -3768, -19917, -25989, -18911, -2266, 15526, 25462, + 22516, 8178, -10298, -23564, -24907, -13649, 4514, 20394, + 25956, 18384, 1511, -16126, -25605, -22129, -7456, 10988, + 23873, 24680, 12999, -5257, -20855, -25901, -17842, -756, + 16712, 25725, 21722, 6729, -11668, -24163, -24431, -12339, + 5996, 21297, 25824, 17284, 0, -17284, -25824, -21297, + -5995, 12339, 24432, 24163, 11668, -6729, -21722, -25725, + -16712, 756, 17842, 25901, 20855, 5257, -13000, -24680, + -23873, -10988, 7456, 22129, 25604, 16126, -1511, -18384, + -25956, -20394, -4514, 13649, 24907, 23563, 10297, -8178, + -22516, -25462, -15526, 2266, 18911, 25989, 19917, 3768, + -14287, -25114, -23234, -9599, 8892, 22885, 25299, 14912, + -3018, -19422, -26000, -19422, -3018, 14913, 25299, 22885, + 8892, -9599, -23234, -25114, -14287, 3768, 19917, 25988, + 18911, 2265, -15526, -25462, -22516, -8178, 10298, 23564, + 24907, 13649, -4514, -20394, -25956, -18384, -1511, 16126, + 25605, 22128, 7456, -10988, -23873, -24680, -12999, 5257, + 20855, 25901, 17842, 756, -16712, -25725, -21722, -6729, + 11668, 24163, 24431, 12339, -5996, -21298, -25824, -17284}, + { +// Carrier 19 Phase 1 + 18384, 25956, 20394, 4514, -13649, -24907, -23564, -10298, + 8178, 22516, 25462, 15526, -2266, -18911, -25989, -19917, + -3768, 14287, 25114, 23234, 9599, -8892, -22885, -25299, + -14912, 3018, 19422, 26000, 19422, 3018, -14913, -25299, + -22885, -8892, 9599, 23234, 25114, 14287, -3768, -19917, + -25989, -18911, -2266, 15526, 25462, 22516, 8178, -10298, + -23564, -24907, -13649, 4514, 20394, 25956, 18384, 1511, + -16126, -25605, -22129, -7456, 10988, 23873, 24680, 12999, + -5257, -20855, -25901, -17842, -756, 16712, 25725, 21722, + 6729, -11668, -24163, -24431, -12339, 5996, 21297, 25824, + 17284, 0, -17284, -25824, -21297, -5995, 12339, 24432, + 24163, 11668, -6729, -21722, -25725, -16712, 756, 17842, + 25901, 20855, 5257, -13000, -24680, -23873, -10988, 7456, + 22129, 25604, 16126, -1511, -18384, -25956, -20394, -4514, + 13649, 24907, 23563, 10297, -8178, -22516, -25462, -15526, + 2266, 18911, 25989, 19917, 3768, -14287, -25114, -23234, + -9599, 8892, 22885, 25299, 14912, -3018, -19422, -26000, + -19422, -3018, 14913, 25299, 22885, 8892, -9599, -23234, + -25114, -14287, 3768, 19917, 25988, 18911, 2265, -15526, + -25462, -22516, -8178, 10298, 23564, 24907, 13649, -4514, + -20394, -25956, -18384, -1511, 16126, 25605, 22128, 7456, + -10988, -23873, -24680, -12999, 5257, 20855, 25901, 17842, + 756, -16712, -25725, -21722, -6729, 11668, 24163, 24431, + 12339, -5996, -21298, -25824, -17284, 0, 17284, 25824, + 21297, 5995, -12339, -24432, -24162, -11668, 6729, 21722, + 25725, 16712, -756, -17842, -25901, -20855, -5257, 13000, + 24680, 23873, 10987, -7457, -22129, -25604, -16126, 1511}, + { +// Carrier 19 Phase 2 + 26000, 19422, 3018, -14912, -25299, -22885, -8892, 9599, + 23234, 25114, 14287, -3768, -19917, -25989, -18911, -2266, + 15526, 25462, 22516, 8178, -10298, -23564, -24907, -13649, + 4514, 20394, 25956, 18384, 1511, -16126, -25605, -22129, + -7456, 10988, 23873, 24680, 12999, -5257, -20855, -25901, + -17842, -756, 16712, 25725, 21722, 6729, -11668, -24163, + -24431, -12339, 5996, 21297, 25824, 17284, 0, -17284, + -25824, -21297, -5995, 12339, 24432, 24163, 11668, -6729, + -21722, -25725, -16712, 756, 17842, 25901, 20855, 5257, + -13000, -24680, -23873, -10988, 7456, 22129, 25604, 16126, + -1511, -18384, -25956, -20394, -4514, 13649, 24907, 23563, + 10298, -8178, -22516, -25462, -15526, 2266, 18911, 25989, + 19917, 3768, -14287, -25114, -23234, -9599, 8892, 22885, + 25299, 14912, -3018, -19422, -26000, -19422, -3018, 14913, + 25299, 22885, 8892, -9599, -23234, -25114, -14287, 3768, + 19917, 25988, 18911, 2265, -15526, -25462, -22516, -8178, + 10298, 23564, 24907, 13649, -4514, -20394, -25956, -18384, + -1511, 16126, 25605, 22128, 7456, -10988, -23873, -24680, + -12999, 5257, 20855, 25901, 17842, 756, -16712, -25725, + -21722, -6729, 11668, 24163, 24431, 12339, -5996, -21298, + -25824, -17284, 0, 17284, 25824, 21297, 5995, -12339, + -24432, -24162, -11668, 6729, 21722, 25725, 16712, -756, + -17842, -25901, -20855, -5257, 13000, 24680, 23873, 10987, + -7457, -22129, -25604, -16126, 1511, 18384, 25956, 20394, + 4514, -13649, -24907, -23563, -10297, 8178, 22516, 25462, + 15526, -2266, -18911, -25989, -19917, -3768, 14287, 25114, + 23234, 9599, -8892, -22885, -25299, -14912, 3018, 19422}, + { +// Carrier 19 Phase 3 + 18384, 1511, -16126, -25605, -22129, -7456, 10988, 23873, + 24680, 12999, -5257, -20855, -25901, -17842, -756, 16712, + 25725, 21722, 6729, -11668, -24163, -24432, -12339, 5996, + 21297, 25824, 17284, 0, -17284, -25824, -21297, -5995, + 12339, 24432, 24163, 11668, -6729, -21722, -25725, -16712, + 756, 17842, 25901, 20855, 5257, -13000, -24680, -23873, + -10988, 7456, 22129, 25604, 16126, -1511, -18384, -25956, + -20394, -4514, 13649, 24907, 23563, 10298, -8178, -22516, + -25462, -15526, 2266, 18911, 25989, 19917, 3768, -14287, + -25114, -23234, -9599, 8892, 22885, 25299, 14912, -3018, + -19422, -26000, -19422, -3018, 14913, 25299, 22885, 8892, + -9599, -23234, -25114, -14287, 3768, 19917, 25988, 18911, + 2265, -15526, -25462, -22516, -8178, 10298, 23564, 24907, + 13649, -4514, -20394, -25956, -18384, -1511, 16126, 25605, + 22128, 7456, -10988, -23873, -24680, -12999, 5257, 20855, + 25901, 17842, 756, -16712, -25725, -21722, -6729, 11668, + 24163, 24431, 12339, -5996, -21298, -25824, -17284, 0, + 17284, 25824, 21297, 5995, -12339, -24432, -24162, -11668, + 6729, 21722, 25725, 16712, -756, -17842, -25901, -20855, + -5257, 13000, 24680, 23873, 10987, -7456, -22129, -25604, + -16126, 1511, 18384, 25956, 20394, 4514, -13649, -24907, + -23563, -10297, 8178, 22516, 25462, 15526, -2266, -18911, + -25989, -19917, -3768, 14287, 25114, 23234, 9599, -8892, + -22885, -25299, -14912, 3018, 19422, 26000, 19422, 3018, + -14913, -25299, -22885, -8892, 9599, 23234, 25114, 14287, + -3768, -19917, -25988, -18911, -2265, 15526, 25462, 22516, + 8178, -10298, -23564, -24907, -13649, 4515, 20394, 25956}, + },{{ + +// Carrier 20 Phase 0 + 0, 17842, 25956, 19917, 3018, -15526, -25605, -21722, + -5996, 13000, 24907, 23234, 8892, -10298, -23873, -24432, + -11668, 7456, 22516, 25299, 14287, -4514, -20855, -25824, + -16712, 1511, 18911, 26000, 18911, 1511, -16712, -25824, + -20855, -4514, 14287, 25299, 22516, 7456, -11668, -24432, + -23873, -10297, 8892, 23234, 24907, 12999, -5996, -21722, + -25604, -15526, 3018, 19917, 25956, 17842, 0, -17842, + -25956, -19917, -3018, 15526, 25605, 21722, 5995, -13000, + -24907, -23234, -8892, 10298, 23873, 24431, 11668, -7457, + -22516, -25299, -14287, 4515, 20855, 25824, 16712, -1511, + -18911, -26000, -18911, -1511, 16712, 25824, 20855, 4514, + -14287, -25299, -22516, -7456, 11668, 24432, 23873, 10297, + -8892, -23234, -24907, -12999, 5996, 21722, 25604, 15525, + -3018, -19917, -25955, -17842, 0, 17842, 25956, 19916, + 3018, -15526, -25605, -21722, -5995, 13000, 24907, 23234, + 8892, -10298, -23873, -24431, -11668, 7457, 22516, 25299, + 14286, -4515, -20855, -25824, -16712, 1512, 18911, 26000, + 18911, 1511, -16712, -25824, -20855, -4514, 14287, 25299, + 22516, 7456, -11669, -24432, -23873, -10297, 8892, 23234, + 24907, 12999, -5996, -21722, -25604, -15525, 3018, 19917, + 25955, 17842, 0, -17842, -25956, -19916, -3018, 15526, + 25605, 21722, 5995, -13000, -24907, -23234, -8892, 10298, + 23873, 24431, 11668, -7457, -22516, -25299, -14286, 4515, + 20855, 25824, 16712, -1512, -18912, -26000, -18911, -1511, + 16712, 25824, 20854, 4514, -14287, -25299, -22516, -7456, + 11669, 24432, 23873, 10297, -8892, -23234, -24907, -12999, + 5996, 21722, 25604, 15525, -3018, -19917, -25955, -17841}, + { +// Carrier 20 Phase 1 + 18384, 25989, 19422, 2266, -16126, -25725, -21297, -5257, + 13649, 25114, 22885, 8178, -10988, -24163, -24163, -10988, + 8178, 22885, 25114, 13649, -5257, -21297, -25725, -16126, + 2266, 19422, 25988, 18384, 756, -17284, -25901, -20394, + -3768, 14913, 25462, 22128, 6729, -12339, -24680, -23563, + -9599, 9599, 23564, 24680, 12339, -6729, -22129, -25462, + -14912, 3768, 20394, 25901, 17284, -756, -18384, -25989, + -19422, -2265, 16126, 25725, 21297, 5257, -13649, -25114, + -22885, -8178, 10988, 24163, 24162, 10987, -8178, -22885, + -25114, -13649, 5257, 21298, 25725, 16125, -2266, -19422, + -25988, -18384, -756, 17284, 25901, 20394, 3768, -14913, + -25462, -22128, -6729, 12339, 24680, 23563, 9599, -9599, + -23564, -24680, -12339, 6729, 22129, 25462, 14912, -3768, + -20394, -25901, -17284, 756, 18384, 25989, 19422, 2265, + -16126, -25725, -21297, -5257, 13649, 25114, 22885, 8177, + -10988, -24163, -24162, -10987, 8178, 22885, 25113, 13649, + -5257, -21298, -25725, -16125, 2266, 19422, 25988, 18384, + 755, -17284, -25901, -20394, -3767, 14913, 25462, 22128, + 6728, -12339, -24680, -23563, -9599, 9599, 23564, 24680, + 12339, -6729, -22129, -25462, -14912, 3768, 20395, 25901, + 17284, -756, -18385, -25989, -19422, -2265, 16126, 25725, + 21297, 5257, -13649, -25114, -22885, -8177, 10988, 24163, + 24162, 10987, -8178, -22885, -25113, -13649, 5258, 21298, + 25725, 16125, -2266, -19422, -25988, -18384, -755, 17285, + 25901, 20394, 3767, -14913, -25462, -22128, -6728, 12340, + 24680, 23563, 9598, -9599, -23564, -24680, -12339, 6729, + 22129, 25462, 14912, -3768, -20395, -25901, -17284, 756}, + { +// Carrier 20 Phase 2 + 26000, 18911, 1511, -16712, -25824, -20855, -4514, 14287, + 25299, 22516, 7456, -11668, -24432, -23873, -10298, 8892, + 23234, 24907, 12999, -5996, -21722, -25604, -15526, 3018, + 19917, 25956, 17842, 0, -17842, -25956, -19917, -3018, + 15526, 25605, 21722, 5995, -13000, -24907, -23234, -8892, + 10298, 23873, 24431, 11668, -7456, -22516, -25299, -14287, + 4514, 20855, 25824, 16712, -1511, -18911, -26000, -18911, + -1511, 16712, 25824, 20855, 4514, -14287, -25299, -22516, + -7456, 11668, 24432, 23873, 10297, -8892, -23234, -24907, + -12999, 5996, 21722, 25604, 15525, -3018, -19917, -25956, + -17842, 0, 17842, 25956, 19917, 3018, -15526, -25605, + -21722, -5995, 13000, 24907, 23234, 8892, -10298, -23873, + -24431, -11668, 7457, 22516, 25299, 14287, -4515, -20855, + -25824, -16712, 1512, 18911, 26000, 18911, 1511, -16712, + -25824, -20855, -4514, 14287, 25299, 22516, 7456, -11669, + -24432, -23873, -10297, 8892, 23234, 24907, 12999, -5996, + -21722, -25604, -15525, 3018, 19917, 25955, 17842, 0, + -17842, -25956, -19916, -3018, 15526, 25605, 21722, 5995, + -13000, -24907, -23234, -8892, 10298, 23873, 24431, 11668, + -7457, -22516, -25299, -14286, 4515, 20855, 25824, 16712, + -1512, -18911, -26000, -18911, -1511, 16712, 25824, 20854, + 4514, -14287, -25299, -22516, -7456, 11669, 24432, 23873, + 10297, -8892, -23234, -24907, -12999, 5996, 21722, 25604, + 15525, -3018, -19917, -25955, -17841, 0, 17842, 25956, + 19916, 3017, -15526, -25605, -21722, -5995, 13000, 24907, + 23234, 8892, -10298, -23873, -24431, -11668, 7457, 22516, + 25299, 14286, -4515, -20855, -25824, -16712, 1512, 18912}, + { +// Carrier 20 Phase 3 + 18384, 756, -17284, -25901, -20394, -3768, 14913, 25462, + 22129, 6729, -12339, -24680, -23563, -9599, 9599, 23564, + 24680, 12339, -6729, -22129, -25462, -14912, 3768, 20394, + 25901, 17284, -756, -18384, -25989, -19422, -2265, 16126, + 25725, 21297, 5257, -13649, -25114, -22885, -8178, 10988, + 24163, 24163, 10987, -8178, -22885, -25114, -13649, 5257, + 21298, 25725, 16126, -2266, -19422, -25988, -18384, -756, + 17284, 25901, 20394, 3768, -14913, -25462, -22128, -6729, + 12339, 24680, 23563, 9599, -9599, -23564, -24680, -12339, + 6729, 22129, 25462, 14912, -3768, -20394, -25901, -17284, + 756, 18384, 25989, 19422, 2265, -16126, -25725, -21297, + -5257, 13649, 25114, 22885, 8177, -10988, -24163, -24162, + -10987, 8178, 22885, 25114, 13649, -5257, -21298, -25725, + -16125, 2266, 19422, 25988, 18384, 755, -17284, -25901, + -20394, -3767, 14913, 25462, 22128, 6729, -12339, -24680, + -23563, -9599, 9599, 23564, 24680, 12339, -6729, -22129, + -25462, -14912, 3768, 20394, 25901, 17284, -756, -18384, + -25989, -19422, -2265, 16126, 25725, 21297, 5257, -13649, + -25114, -22885, -8177, 10988, 24163, 24162, 10987, -8178, + -22885, -25113, -13649, 5258, 21298, 25725, 16125, -2266, + -19422, -25988, -18384, -755, 17284, 25901, 20394, 3767, + -14913, -25462, -22128, -6728, 12339, 24680, 23563, 9598, + -9599, -23564, -24680, -12339, 6729, 22129, 25462, 14912, + -3768, -20395, -25901, -17284, 756, 18385, 25989, 19422, + 2265, -16126, -25725, -21297, -5257, 13649, 25114, 22885, + 8177, -10988, -24163, -24162, -10987, 8178, 22885, 25113, + 13648, -5258, -21298, -25725, -16125, 2266, 19422, 25988}, + },{{ + +// Carrier 21 Phase 0 + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18384, + 0, 18384, 26000, 18384, 0, -18384, -26000, -18385, + 0, 18384, 26000, 18385, 0, -18384, -26000, -18385}, + { +// Carrier 21 Phase 1 + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18384, 0, + 18384, 26000, 18384, 0, -18384, -26000, -18385, 0, + 18384, 26000, 18385, 0, -18384, -26000, -18385, 0}, + { +// Carrier 21 Phase 2 + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18384, 0, 18384, + 26000, 18384, 0, -18384, -26000, -18385, 0, 18384, + 26000, 18385, 0, -18384, -26000, -18385, 0, 18384}, + { +// Carrier 21 Phase 3 + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18384, 0, 18384, 26000, + 18384, 0, -18384, -26000, -18385, 0, 18384, 26000, + 18385, 0, -18384, -26000, -18385, 0, 18384, 26000}, + },{{ + +// Carrier 22 Phase 0 + 0, 18911, 25956, 16712, -3018, -20855, -25605, -14287, + 5996, 22516, 24907, 11668, -8892, -23873, -23873, -8892, + 11668, 24907, 22516, 5996, -14287, -25605, -20855, -3018, + 16712, 25956, 18911, 0, -18911, -25956, -16712, 3018, + 20855, 25604, 14287, -5996, -22516, -24907, -11668, 8892, + 23873, 23873, 8892, -11668, -24907, -22516, -5996, 14287, + 25605, 20855, 3018, -16712, -25956, -18911, 0, 18911, + 25956, 16712, -3018, -20855, -25604, -14287, 5996, 22516, + 24907, 11668, -8892, -23873, -23873, -8892, 11668, 24907, + 22516, 5995, -14287, -25605, -20855, -3018, 16712, 25956, + 18911, 0, -18911, -25956, -16712, 3018, 20855, 25604, + 14287, -5996, -22516, -24907, -11668, 8892, 23873, 23873, + 8892, -11668, -24907, -22516, -5995, 14287, 25605, 20855, + 3018, -16712, -25956, -18911, 0, 18911, 25956, 16712, + -3018, -20855, -25604, -14287, 5996, 22516, 24907, 11668, + -8892, -23873, -23873, -8892, 11668, 24907, 22516, 5995, + -14287, -25605, -20855, -3018, 16712, 25956, 18911, 0, + -18911, -25956, -16712, 3018, 20855, 25604, 14287, -5996, + -22516, -24907, -11668, 8892, 23873, 23873, 8892, -11668, + -24907, -22516, -5995, 14287, 25605, 20855, 3018, -16712, + -25956, -18911, 0, 18911, 25956, 16712, -3018, -20855, + -25604, -14287, 5996, 22516, 24907, 11668, -8892, -23873, + -23873, -8892, 11668, 24907, 22516, 5995, -14287, -25605, + -20855, -3018, 16712, 25956, 18911, 0, -18911, -25956, + -16712, 3018, 20855, 25604, 14287, -5996, -22516, -24907, + -11668, 8892, 23873, 23873, 8892, -11668, -24907, -22516, + -5995, 14287, 25605, 20855, 3018, -16712, -25956, -18911}, + { +// Carrier 22 Phase 1 + 18384, 25989, 17284, -2266, -20394, -25725, -14912, 5257, + 22129, 25114, 12339, -8178, -23564, -24163, -9599, 10988, + 24680, 22885, 6729, -13649, -25462, -21297, -3768, 16126, + 25901, 19422, 756, -18384, -25989, -17284, 2266, 20394, + 25725, 14912, -5257, -22129, -25114, -12339, 8178, 23564, + 24163, 9599, -10988, -24680, -22885, -6729, 13649, 25462, + 21297, 3768, -16126, -25901, -19422, -756, 18384, 25989, + 17284, -2266, -20394, -25725, -14912, 5257, 22129, 25114, + 12339, -8178, -23564, -24163, -9599, 10988, 24680, 22885, + 6729, -13649, -25462, -21297, -3768, 16126, 25901, 19422, + 756, -18384, -25988, -17284, 2266, 20394, 25725, 14912, + -5257, -22129, -25114, -12339, 8178, 23564, 24163, 9599, + -10988, -24680, -22885, -6729, 13649, 25462, 21297, 3768, + -16126, -25901, -19422, -756, 18384, 25988, 17284, -2266, + -20394, -25725, -14912, 5257, 22129, 25114, 12339, -8178, + -23564, -24163, -9599, 10988, 24680, 22885, 6729, -13649, + -25462, -21297, -3768, 16126, 25901, 19422, 756, -18384, + -25988, -17284, 2266, 20394, 25725, 14912, -5257, -22129, + -25114, -12339, 8178, 23564, 24163, 9599, -10988, -24680, + -22885, -6729, 13649, 25462, 21297, 3768, -16126, -25901, + -19422, -756, 18384, 25988, 17284, -2266, -20394, -25725, + -14912, 5257, 22129, 25114, 12339, -8178, -23564, -24162, + -9599, 10988, 24680, 22885, 6729, -13649, -25462, -21297, + -3768, 16126, 25901, 19422, 756, -18384, -25988, -17284, + 2266, 20394, 25725, 14912, -5257, -22129, -25114, -12339, + 8178, 23564, 24162, 9599, -10988, -24680, -22885, -6729, + 13649, 25462, 21297, 3768, -16126, -25901, -19422, -756}, + { +// Carrier 22 Phase 2 + 26000, 17842, -1511, -19917, -25824, -15526, 4514, 21722, + 25299, 13000, -7456, -23234, -24432, -10298, 10298, 24432, + 23234, 7456, -12999, -25299, -21722, -4514, 15526, 25824, + 19917, 1511, -17842, -26000, -17842, 1511, 19917, 25824, + 15526, -4514, -21722, -25299, -13000, 7456, 23234, 24432, + 10298, -10298, -24432, -23234, -7456, 13000, 25299, 21722, + 4514, -15526, -25824, -19917, -1511, 17842, 26000, 17842, + -1511, -19917, -25824, -15526, 4514, 21722, 25299, 12999, + -7456, -23234, -24432, -10298, 10298, 24432, 23234, 7456, + -13000, -25299, -21722, -4514, 15526, 25824, 19917, 1511, + -17842, -26000, -17842, 1511, 19917, 25824, 15526, -4514, + -21722, -25299, -12999, 7456, 23234, 24431, 10298, -10298, + -24432, -23234, -7456, 13000, 25299, 21722, 4514, -15526, + -25824, -19917, -1511, 17842, 26000, 17842, -1511, -19917, + -25824, -15526, 4514, 21722, 25299, 12999, -7456, -23234, + -24431, -10298, 10298, 24432, 23234, 7456, -13000, -25299, + -21722, -4514, 15526, 25824, 19917, 1511, -17842, -26000, + -17842, 1511, 19917, 25824, 15526, -4514, -21722, -25299, + -12999, 7456, 23234, 24431, 10298, -10298, -24432, -23234, + -7456, 13000, 25299, 21722, 4514, -15526, -25824, -19917, + -1511, 17842, 26000, 17842, -1511, -19917, -25824, -15526, + 4514, 21722, 25299, 12999, -7456, -23234, -24431, -10298, + 10298, 24432, 23234, 7456, -13000, -25299, -21722, -4514, + 15526, 25824, 19917, 1511, -17842, -26000, -17842, 1511, + 19917, 25824, 15526, -4514, -21722, -25299, -12999, 7456, + 23234, 24431, 10298, -10298, -24432, -23234, -7456, 13000, + 25299, 21722, 4514, -15526, -25824, -19917, -1511, 17842}, + { +// Carrier 22 Phase 3 + 18384, -756, -19422, -25901, -16126, 3768, 21297, 25462, + 13649, -6729, -22885, -24680, -10988, 9599, 24163, 23564, + 8178, -12339, -25114, -22129, -5257, 14912, 25725, 20394, + 2266, -17284, -25989, -18384, 756, 19422, 25901, 16126, + -3768, -21297, -25462, -13649, 6729, 22885, 24680, 10988, + -9599, -24163, -23563, -8178, 12339, 25114, 22129, 5257, + -14913, -25725, -20394, -2266, 17284, 25989, 18384, -756, + -19422, -25901, -16126, 3768, 21297, 25462, 13649, -6729, + -22885, -24680, -10988, 9599, 24163, 23563, 8178, -12339, + -25114, -22129, -5257, 14913, 25725, 20394, 2266, -17284, + -25989, -18384, 756, 19422, 25901, 16126, -3768, -21297, + -25462, -13649, 6729, 22885, 24680, 10988, -9599, -24163, + -23563, -8178, 12339, 25114, 22129, 5257, -14913, -25725, + -20394, -2266, 17284, 25989, 18384, -756, -19422, -25901, + -16126, 3768, 21297, 25462, 13649, -6729, -22885, -24680, + -10988, 9599, 24163, 23563, 8178, -12339, -25114, -22129, + -5257, 14913, 25725, 20394, 2265, -17284, -25989, -18384, + 756, 19422, 25901, 16126, -3768, -21297, -25462, -13649, + 6729, 22885, 24680, 10988, -9599, -24163, -23563, -8178, + 12339, 25114, 22128, 5257, -14913, -25725, -20394, -2265, + 17284, 25989, 18384, -756, -19422, -25901, -16126, 3768, + 21297, 25462, 13649, -6729, -22885, -24680, -10988, 9599, + 24163, 23563, 8178, -12339, -25114, -22128, -5257, 14913, + 25725, 20394, 2265, -17284, -25989, -18384, 756, 19422, + 25901, 16126, -3768, -21297, -25462, -13649, 6729, 22885, + 24680, 10987, -9599, -24163, -23563, -8178, 12339, 25114, + 22128, 5257, -14913, -25725, -20394, -2265, 17284, 25989}, + },{{ + +// Carrier 23 Phase 0 + 0, 19422, 25824, 14912, -5996, -22885, -24432, -9599, + 11668, 25114, 21722, 3768, -16712, -25989, -17842, 2266, + 20855, 25462, 12999, -8178, -23873, -23563, -7456, 13649, + 25605, 20394, 1511, -18384, -25956, -16126, 4514, 22129, + 24907, 10988, -10298, -24680, -22516, -5257, 15526, 25901, + 18911, -756, -19917, -25725, -14287, 6729, 23234, 24163, + 8892, -12339, -25299, -21297, -3018, 17284, 26000, 17284, + -3018, -21297, -25299, -12339, 8892, 24163, 23234, 6729, + -14287, -25725, -19917, -756, 18911, 25901, 15526, -5257, + -22516, -24680, -10297, 10988, 24907, 22128, 4514, -16126, + -25956, -18384, 1511, 20394, 25604, 13649, -7457, -23564, + -23873, -8178, 13000, 25462, 20855, 2265, -17842, -25988, + -16712, 3768, 21722, 25114, 11668, -9599, -24432, -22885, + -5995, 14913, 25824, 19422, 0, -19422, -25824, -14912, + 5996, 22885, 24431, 9599, -11668, -25114, -21722, -3768, + 16712, 25989, 17842, -2266, -20855, -25462, -12999, 8178, + 23873, 23563, 7456, -13649, -25605, -20394, -1511, 18384, + 25955, 16125, -4515, -22129, -24907, -10987, 10298, 24680, + 22516, 5257, -15526, -25901, -18911, 756, 19917, 25725, + 14287, -6729, -23234, -24162, -8892, 12339, 25299, 21297, + 3018, -17284, -26000, -17284, 3018, 21298, 25299, 12339, + -8892, -24163, -23234, -6729, 14287, 25725, 19916, 755, + -18911, -25901, -15525, 5257, 22516, 24680, 10297, -10988, + -24907, -22128, -4514, 16126, 25956, 18384, -1512, -20394, + -25604, -13649, 7457, 23564, 23873, 8177, -13000, -25462, + -20855, -2265, 17842, 25988, 16712, -3768, -21722, -25113, + -11668, 9599, 24432, 22885, 5995, -14913, -25824, -19422}, + { +// Carrier 23 Phase 1 + 18384, 25956, 16126, -4514, -22129, -24907, -10988, 10298, + 24680, 22516, 5257, -15526, -25901, -18911, 756, 19917, + 25725, 14287, -6729, -23234, -24163, -8892, 12339, 25299, + 21297, 3018, -17284, -26000, -17284, 3018, 21297, 25299, + 12339, -8892, -24163, -23234, -6729, 14287, 25725, 19917, + 756, -18911, -25901, -15526, 5257, 22516, 24680, 10298, + -10988, -24907, -22128, -4514, 16126, 25956, 18384, -1511, + -20394, -25604, -13649, 7456, 23564, 23873, 8178, -13000, + -25462, -20855, -2265, 17842, 25988, 16712, -3768, -21722, + -25114, -11668, 9599, 24432, 22885, 5995, -14913, -25824, + -19422, 0, 19422, 25824, 14912, -5996, -22885, -24431, + -9599, 11668, 25114, 21722, 3768, -16712, -25989, -17842, + 2266, 20855, 25462, 12999, -8178, -23873, -23563, -7456, + 13649, 25605, 20394, 1511, -18384, -25956, -16125, 4515, + 22129, 24907, 10987, -10298, -24680, -22516, -5257, 15526, + 25901, 18911, -756, -19917, -25725, -14287, 6729, 23234, + 24162, 8892, -12339, -25299, -21297, -3018, 17284, 26000, + 17284, -3018, -21298, -25299, -12339, 8892, 24163, 23234, + 6729, -14287, -25725, -19917, -755, 18911, 25901, 15525, + -5257, -22516, -24680, -10297, 10988, 24907, 22128, 4514, + -16126, -25956, -18384, 1512, 20394, 25604, 13649, -7457, + -23564, -23873, -8177, 13000, 25462, 20855, 2265, -17842, + -25988, -16712, 3768, 21722, 25113, 11668, -9599, -24432, + -22885, -5995, 14913, 25824, 19422, 0, -19422, -25824, + -14912, 5996, 22885, 24431, 9599, -11669, -25114, -21722, + -3767, 16712, 25989, 17842, -2266, -20855, -25462, -12999, + 8178, 23873, 23563, 7456, -13649, -25605, -20394, -1511}, + { +// Carrier 23 Phase 2 + 26000, 17284, -3018, -21297, -25299, -12339, 8892, 24163, + 23234, 6729, -14287, -25725, -19917, -756, 18911, 25901, + 15526, -5257, -22516, -24680, -10298, 10988, 24907, 22129, + 4514, -16126, -25956, -18384, 1511, 20394, 25604, 13649, + -7456, -23564, -23873, -8178, 13000, 25462, 20855, 2265, + -17842, -25988, -16712, 3768, 21722, 25114, 11668, -9599, + -24432, -22885, -5995, 14913, 25824, 19422, 0, -19422, + -25824, -14912, 5996, 22885, 24431, 9599, -11668, -25114, + -21722, -3768, 16712, 25989, 17842, -2266, -20855, -25462, + -12999, 8178, 23873, 23563, 7456, -13649, -25605, -20394, + -1511, 18384, 25956, 16126, -4514, -22129, -24907, -10987, + 10298, 24680, 22516, 5257, -15526, -25901, -18911, 756, + 19917, 25725, 14287, -6729, -23234, -24162, -8892, 12339, + 25299, 21297, 3018, -17284, -26000, -17284, 3018, 21298, + 25299, 12339, -8892, -24163, -23234, -6729, 14287, 25725, + 19917, 756, -18911, -25901, -15525, 5257, 22516, 24680, + 10297, -10988, -24907, -22128, -4514, 16126, 25956, 18384, + -1511, -20394, -25604, -13649, 7457, 23564, 23873, 8177, + -13000, -25462, -20855, -2265, 17842, 25988, 16712, -3768, + -21722, -25114, -11668, 9599, 24432, 22885, 5995, -14913, + -25824, -19422, 0, 19422, 25824, 14912, -5996, -22885, + -24431, -9599, 11669, 25114, 21722, 3767, -16712, -25989, + -17842, 2266, 20855, 25462, 12999, -8178, -23873, -23563, + -7456, 13649, 25605, 20394, 1511, -18384, -25955, -16125, + 4515, 22129, 24907, 10987, -10298, -24680, -22516, -5257, + 15526, 25901, 18911, -756, -19917, -25725, -14286, 6729, + 23234, 24162, 8892, -12339, -25299, -21297, -3018, 17284}, + { +// Carrier 23 Phase 3 + 18384, -1511, -20394, -25605, -13649, 7456, 23564, 23873, + 8178, -13000, -25462, -20855, -2266, 17842, 25989, 16712, + -3768, -21722, -25114, -11668, 9599, 24432, 22885, 5995, + -14913, -25824, -19422, 0, 19422, 25824, 14912, -5996, + -22885, -24431, -9599, 11668, 25114, 21722, 3768, -16712, + -25989, -17842, 2266, 20855, 25462, 12999, -8178, -23873, + -23563, -7456, 13649, 25605, 20394, 1511, -18384, -25956, + -16126, 4514, 22129, 24907, 10987, -10298, -24680, -22516, + -5257, 15526, 25901, 18911, -756, -19917, -25725, -14287, + 6729, 23234, 24162, 8892, -12339, -25299, -21297, -3018, + 17284, 26000, 17284, -3018, -21298, -25299, -12339, 8892, + 24163, 23234, 6729, -14287, -25725, -19917, -756, 18911, + 25901, 15525, -5257, -22516, -24680, -10297, 10988, 24907, + 22128, 4514, -16126, -25956, -18384, 1511, 20394, 25604, + 13649, -7457, -23564, -23873, -8177, 13000, 25462, 20855, + 2265, -17842, -25988, -16712, 3768, 21722, 25114, 11668, + -9599, -24432, -22885, -5995, 14913, 25824, 19422, 0, + -19422, -25824, -14912, 5996, 22885, 24431, 9599, -11668, + -25114, -21722, -3767, 16712, 25989, 17842, -2266, -20855, + -25462, -12999, 8178, 23873, 23563, 7456, -13649, -25605, + -20394, -1511, 18384, 25955, 16125, -4515, -22129, -24907, + -10987, 10298, 24680, 22516, 5257, -15526, -25901, -18911, + 756, 19917, 25725, 14286, -6729, -23234, -24162, -8892, + 12339, 25299, 21297, 3018, -17284, -26000, -17284, 3018, + 21298, 25299, 12339, -8892, -24163, -23234, -6728, 14287, + 25725, 19916, 755, -18911, -25901, -15525, 5257, 22516, + 24680, 10297, -10988, -24907, -22128, -4514, 16126, 25956}, + },{{ + +// Carrier 24 Phase 0 + 0, 19917, 25605, 13000, -8892, -24432, -22516, -4514, + 16712, 26000, 16712, -4514, -22516, -24432, -8892, 12999, + 25604, 19917, 0, -19917, -25605, -13000, 8892, 24431, + 22516, 4514, -16712, -26000, -16712, 4514, 22516, 24432, + 8892, -12999, -25604, -19917, 0, 19917, 25605, 13000, + -8892, -24431, -22516, -4514, 16712, 26000, 16712, -4514, + -22516, -24432, -8892, 12999, 25604, 19917, 0, -19917, + -25605, -13000, 8892, 24431, 22516, 4514, -16712, -26000, + -16712, 4514, 22516, 24432, 8892, -12999, -25604, -19917, + 0, 19917, 25605, 13000, -8892, -24431, -22516, -4514, + 16712, 26000, 16712, -4514, -22516, -24432, -8892, 12999, + 25604, 19917, 0, -19917, -25605, -13000, 8892, 24431, + 22516, 4515, -16712, -26000, -16712, 4514, 22516, 24432, + 8892, -12999, -25604, -19917, 0, 19917, 25605, 13000, + -8892, -24431, -22516, -4515, 16712, 26000, 16712, -4514, + -22516, -24432, -8892, 12999, 25604, 19917, 0, -19917, + -25605, -13000, 8892, 24431, 22516, 4515, -16712, -26000, + -16712, 4514, 22516, 24432, 8892, -12999, -25604, -19917, + 0, 19916, 25605, 13000, -8892, -24431, -22516, -4515, + 16712, 26000, 16712, -4514, -22516, -24432, -8892, 12999, + 25604, 19917, 0, -19916, -25605, -13000, 8892, 24431, + 22516, 4515, -16712, -26000, -16712, 4514, 22516, 24432, + 8892, -12999, -25604, -19917, 0, 19916, 25605, 13000, + -8892, -24431, -22516, -4515, 16712, 26000, 16712, -4514, + -22516, -24432, -8892, 12999, 25604, 19917, 0, -19916, + -25605, -13000, 8892, 24431, 22516, 4515, -16712, -26000, + -16712, 4514, 22516, 24432, 8892, -12999, -25604, -19917}, + { +// Carrier 24 Phase 1 + 18384, 25901, 14912, -6729, -23563, -23564, -6729, 14912, + 25901, 18384, -2266, -21297, -25114, -10988, 10988, 25114, + 21297, 2266, -18384, -25901, -14913, 6729, 23563, 23564, + 6729, -14912, -25901, -18384, 2265, 21297, 25114, 10988, + -10988, -25114, -21297, -2266, 18384, 25901, 14913, -6729, + -23563, -23564, -6729, 14912, 25901, 18384, -2265, -21297, + -25114, -10988, 10987, 25114, 21298, 2266, -18384, -25901, + -14913, 6729, 23563, 23564, 6729, -14912, -25901, -18384, + 2265, 21297, 25114, 10988, -10987, -25114, -21298, -2266, + 18384, 25901, 14913, -6729, -23563, -23564, -6729, 14912, + 25901, 18384, -2265, -21297, -25114, -10988, 10987, 25114, + 21298, 2266, -18384, -25901, -14913, 6729, 23563, 23564, + 6729, -14912, -25901, -18384, 2265, 21297, 25114, 10988, + -10987, -25114, -21298, -2266, 18384, 25901, 14913, -6729, + -23563, -23564, -6729, 14912, 25901, 18384, -2265, -21297, + -25114, -10988, 10987, 25114, 21298, 2266, -18384, -25901, + -14913, 6729, 23563, 23564, 6729, -14912, -25901, -18384, + 2265, 21297, 25114, 10988, -10987, -25114, -21298, -2266, + 18384, 25901, 14913, -6729, -23563, -23564, -6729, 14912, + 25901, 18384, -2265, -21297, -25114, -10988, 10987, 25113, + 21298, 2266, -18384, -25901, -14913, 6729, 23563, 23564, + 6729, -14912, -25901, -18384, 2265, 21297, 25114, 10988, + -10987, -25113, -21298, -2266, 18384, 25901, 14913, -6728, + -23563, -23564, -6729, 14912, 25901, 18385, -2265, -21297, + -25114, -10988, 10987, 25113, 21298, 2266, -18384, -25901, + -14913, 6728, 23563, 23564, 6729, -14912, -25901, -18385, + 2265, 21297, 25114, 10988, -10987, -25113, -21298, -2266}, + { +// Carrier 24 Phase 2 + 26000, 16712, -4514, -22516, -24432, -8892, 12999, 25604, + 19917, 0, -19917, -25605, -13000, 8892, 24431, 22516, + 4514, -16712, -26000, -16712, 4514, 22516, 24432, 8892, + -12999, -25604, -19917, 0, 19917, 25605, 13000, -8892, + -24431, -22516, -4514, 16712, 26000, 16712, -4514, -22516, + -24432, -8892, 12999, 25604, 19917, 0, -19917, -25605, + -13000, 8892, 24431, 22516, 4514, -16712, -26000, -16712, + 4514, 22516, 24432, 8892, -12999, -25604, -19917, 0, + 19917, 25605, 13000, -8892, -24431, -22516, -4514, 16712, + 26000, 16712, -4514, -22516, -24432, -8892, 12999, 25604, + 19917, 0, -19917, -25605, -13000, 8892, 24431, 22516, + 4515, -16712, -26000, -16712, 4514, 22516, 24432, 8892, + -12999, -25604, -19917, 0, 19917, 25605, 13000, -8892, + -24431, -22516, -4515, 16712, 26000, 16712, -4514, -22516, + -24432, -8892, 12999, 25604, 19917, 0, -19917, -25605, + -13000, 8892, 24431, 22516, 4515, -16712, -26000, -16712, + 4514, 22516, 24432, 8892, -12999, -25604, -19917, 0, + 19917, 25605, 13000, -8892, -24431, -22516, -4515, 16712, + 26000, 16712, -4514, -22516, -24432, -8892, 12999, 25604, + 19917, 0, -19916, -25605, -13000, 8892, 24431, 22516, + 4515, -16712, -26000, -16712, 4514, 22516, 24432, 8892, + -12999, -25604, -19917, 0, 19916, 25605, 13000, -8892, + -24431, -22516, -4515, 16712, 26000, 16712, -4514, -22516, + -24432, -8892, 12999, 25604, 19917, 0, -19916, -25605, + -13000, 8892, 24431, 22516, 4515, -16712, -26000, -16712, + 4514, 22516, 24432, 8892, -12999, -25604, -19917, 0, + 19916, 25605, 13000, -8892, -24431, -22516, -4515, 16712}, + { +// Carrier 24 Phase 3 + 18384, -2266, -21297, -25114, -10988, 10988, 25114, 21297, + 2266, -18384, -25901, -14913, 6729, 23563, 23564, 6729, + -14912, -25901, -18384, 2266, 21297, 25114, 10988, -10988, + -25114, -21297, -2266, 18384, 25901, 14913, -6729, -23563, + -23564, -6729, 14912, 25901, 18384, -2265, -21297, -25114, + -10988, 10988, 25114, 21297, 2266, -18384, -25901, -14913, + 6729, 23563, 23564, 6729, -14912, -25901, -18384, 2265, + 21297, 25114, 10988, -10987, -25114, -21298, -2266, 18384, + 25901, 14913, -6729, -23563, -23564, -6729, 14912, 25901, + 18384, -2265, -21297, -25114, -10988, 10987, 25114, 21298, + 2266, -18384, -25901, -14913, 6729, 23563, 23564, 6729, + -14912, -25901, -18384, 2265, 21297, 25114, 10988, -10987, + -25114, -21298, -2266, 18384, 25901, 14913, -6729, -23563, + -23564, -6729, 14912, 25901, 18384, -2265, -21297, -25114, + -10988, 10987, 25114, 21298, 2266, -18384, -25901, -14913, + 6729, 23563, 23564, 6729, -14912, -25901, -18384, 2265, + 21297, 25114, 10988, -10987, -25114, -21298, -2266, 18384, + 25901, 14913, -6729, -23563, -23564, -6729, 14912, 25901, + 18384, -2265, -21297, -25114, -10988, 10987, 25114, 21298, + 2266, -18384, -25901, -14913, 6729, 23563, 23564, 6729, + -14912, -25901, -18384, 2265, 21297, 25114, 10988, -10987, + -25113, -21298, -2266, 18384, 25901, 14913, -6729, -23563, + -23564, -6729, 14912, 25901, 18384, -2265, -21297, -25114, + -10988, 10987, 25113, 21298, 2266, -18384, -25901, -14913, + 6728, 23563, 23564, 6729, -14912, -25901, -18385, 2265, + 21297, 25114, 10988, -10987, -25113, -21298, -2266, 18384, + 25901, 14913, -6728, -23563, -23564, -6729, 14912, 25901}, + },{{ + +// Carrier 25 Phase 0 + 0, 20394, 25299, 10988, -11668, -25462, -19917, 756, + 20855, 25114, 10298, -12339, -25604, -19422, 1511, 21297, + 24907, 9599, -12999, -25725, -18911, 2266, 21722, 24680, + 8892, -13649, -25824, -18384, 3018, 22129, 24432, 8178, + -14287, -25901, -17842, 3768, 22516, 24163, 7456, -14912, + -25956, -17284, 4514, 22885, 23873, 6729, -15526, -25989, + -16712, 5257, 23234, 23564, 5996, -16126, -26000, -16126, + 5995, 23563, 23234, 5257, -16712, -25989, -15526, 6729, + 23873, 22885, 4514, -17284, -25956, -14913, 7456, 24163, + 22516, 3768, -17842, -25901, -14287, 8178, 24432, 22129, + 3018, -18384, -25824, -13649, 8892, 24680, 21722, 2266, + -18911, -25725, -13000, 9599, 24907, 21297, 1511, -19422, + -25605, -12339, 10298, 25114, 20855, 756, -19917, -25462, + -11668, 10988, 25299, 20394, 0, -20394, -25299, -10988, + 11668, 25462, 19917, -756, -20855, -25114, -10298, 12339, + 25605, 19422, -1511, -21297, -24907, -9599, 13000, 25725, + 18911, -2266, -21722, -24680, -8892, 13649, 25824, 18384, + -3018, -22129, -24432, -8178, 14287, 25901, 17842, -3768, + -22516, -24163, -7456, 14913, 25956, 17284, -4514, -22885, + -23873, -6729, 15526, 25989, 16712, -5257, -23234, -23563, + -5995, 16126, 26000, 16126, -5996, -23564, -23234, -5257, + 16712, 25989, 15526, -6729, -23873, -22885, -4514, 17284, + 25956, 14912, -7456, -24163, -22516, -3768, 17842, 25901, + 14287, -8178, -24432, -22129, -3018, 18384, 25824, 13649, + -8892, -24680, -21722, -2265, 18911, 25725, 12999, -9599, + -24907, -21297, -1511, 19422, 25604, 12339, -10298, -25114, + -20855, -756, 19917, 25462, 11668, -10988, -25299, -20394}, + { +// Carrier 25 Phase 1 + 18384, 25824, 13649, -8892, -24680, -21722, -2266, 18911, + 25725, 13000, -9599, -24907, -21297, -1511, 19422, 25605, + 12339, -10298, -25114, -20855, -756, 19917, 25462, 11668, + -10988, -25299, -20394, 0, 20394, 25299, 10988, -11668, + -25462, -19917, 756, 20855, 25114, 10298, -12339, -25604, + -19422, 1511, 21297, 24907, 9599, -12999, -25725, -18911, + 2266, 21722, 24680, 8892, -13649, -25824, -18384, 3018, + 22129, 24432, 8178, -14287, -25901, -17842, 3768, 22516, + 24163, 7456, -14912, -25956, -17284, 4514, 22885, 23873, + 6729, -15526, -25989, -16712, 5257, 23234, 23564, 5996, + -16126, -26000, -16126, 5996, 23564, 23234, 5257, -16712, + -25989, -15526, 6729, 23873, 22885, 4514, -17284, -25956, + -14912, 7456, 24163, 22516, 3768, -17842, -25901, -14287, + 8178, 24432, 22129, 3018, -18384, -25824, -13649, 8892, + 24680, 21722, 2266, -18911, -25725, -12999, 9599, 24907, + 21297, 1511, -19422, -25604, -12339, 10298, 25114, 20855, + 756, -19917, -25462, -11668, 10988, 25299, 20394, 0, + -20394, -25299, -10988, 11668, 25462, 19917, -756, -20855, + -25114, -10298, 12339, 25605, 19422, -1511, -21297, -24907, + -9599, 13000, 25725, 18911, -2266, -21722, -24680, -8892, + 13649, 25824, 18384, -3018, -22129, -24431, -8178, 14287, + 25901, 17842, -3768, -22516, -24163, -7456, 14913, 25956, + 17284, -4514, -22885, -23873, -6729, 15526, 25989, 16712, + -5257, -23234, -23563, -5995, 16126, 26000, 16126, -5996, + -23564, -23234, -5257, 16712, 25988, 15526, -6729, -23873, + -22885, -4514, 17284, 25956, 14912, -7456, -24163, -22516, + -3768, 17842, 25901, 14287, -8178, -24432, -22129, -3018}, + { +// Carrier 25 Phase 2 + 26000, 16126, -5996, -23563, -23234, -5257, 16712, 25989, + 15526, -6729, -23873, -22885, -4514, 17284, 25956, 14912, + -7456, -24163, -22516, -3768, 17842, 25901, 14287, -8178, + -24432, -22129, -3018, 18384, 25824, 13649, -8892, -24680, + -21722, -2266, 18911, 25725, 12999, -9599, -24907, -21297, + -1511, 19422, 25604, 12339, -10298, -25114, -20855, -756, + 19917, 25462, 11668, -10988, -25299, -20394, 0, 20394, + 25299, 10988, -11668, -25462, -19917, 756, 20855, 25114, + 10298, -12339, -25605, -19422, 1511, 21297, 24907, 9599, + -12999, -25725, -18911, 2266, 21722, 24680, 8892, -13649, + -25824, -18384, 3018, 22129, 24432, 8178, -14287, -25901, + -17842, 3768, 22516, 24163, 7456, -14912, -25956, -17284, + 4514, 22885, 23873, 6729, -15526, -25989, -16712, 5257, + 23234, 23564, 5996, -16126, -26000, -16126, 5996, 23564, + 23234, 5257, -16712, -25989, -15526, 6729, 23873, 22885, + 4514, -17284, -25956, -14912, 7456, 24163, 22516, 3768, + -17842, -25901, -14287, 8178, 24432, 22129, 3018, -18384, + -25824, -13649, 8892, 24680, 21722, 2266, -18911, -25725, + -12999, 9599, 24907, 21297, 1511, -19422, -25604, -12339, + 10298, 25114, 20855, 756, -19917, -25462, -11668, 10988, + 25299, 20394, 0, -20394, -25299, -10988, 11668, 25462, + 19917, -756, -20855, -25114, -10298, 12339, 25605, 19422, + -1511, -21297, -24907, -9599, 13000, 25725, 18911, -2266, + -21722, -24680, -8892, 13649, 25824, 18384, -3018, -22129, + -24431, -8178, 14287, 25901, 17842, -3768, -22516, -24163, + -7456, 14913, 25956, 17284, -4514, -22885, -23873, -6729, + 15526, 25989, 16712, -5257, -23234, -23563, -5995, 16126}, + { +// Carrier 25 Phase 3 + 18384, -3018, -22129, -24432, -8178, 14287, 25901, 17842, + -3768, -22516, -24163, -7456, 14912, 25956, 17284, -4514, + -22885, -23873, -6729, 15526, 25989, 16712, -5257, -23234, + -23563, -5996, 16126, 26000, 16126, -5996, -23564, -23234, + -5257, 16712, 25989, 15526, -6729, -23873, -22885, -4514, + 17284, 25956, 14912, -7456, -24163, -22516, -3768, 17842, + 25901, 14287, -8178, -24432, -22129, -3018, 18384, 25824, + 13649, -8892, -24680, -21722, -2266, 18911, 25725, 12999, + -9599, -24907, -21297, -1511, 19422, 25604, 12339, -10298, + -25114, -20855, -756, 19917, 25462, 11668, -10988, -25299, + -20394, 0, 20394, 25299, 10988, -11668, -25462, -19917, + 756, 20855, 25114, 10298, -12339, -25605, -19422, 1511, + 21297, 24907, 9599, -13000, -25725, -18911, 2266, 21722, + 24680, 8892, -13649, -25824, -18384, 3018, 22129, 24432, + 8178, -14287, -25901, -17842, 3768, 22516, 24163, 7456, + -14913, -25956, -17284, 4514, 22885, 23873, 6729, -15526, + -25989, -16712, 5257, 23234, 23563, 5995, -16126, -26000, + -16126, 5996, 23564, 23234, 5257, -16712, -25989, -15526, + 6729, 23873, 22885, 4514, -17284, -25956, -14912, 7456, + 24163, 22516, 3768, -17842, -25901, -14287, 8178, 24432, + 22129, 3018, -18384, -25824, -13649, 8892, 24680, 21722, + 2266, -18911, -25725, -12999, 9599, 24907, 21297, 1511, + -19422, -25604, -12339, 10298, 25114, 20855, 756, -19917, + -25462, -11668, 10988, 25299, 20394, 0, -20394, -25299, + -10988, 11668, 25462, 19917, -756, -20855, -25114, -10298, + 12339, 25605, 19422, -1511, -21297, -24907, -9599, 13000, + 25725, 18911, -2266, -21722, -24680, -8892, 13649, 25824}, + },{{ + +// Carrier 26 Phase 0 + 0, 20855, 24907, 8892, -14287, -25956, -16712, 5996, + 23873, 22516, 3018, -18911, -25604, -11668, 11668, 25605, + 18911, -3018, -22516, -23873, -5995, 16712, 25956, 14287, + -8892, -24907, -20855, 0, 20855, 24907, 8892, -14287, + -25956, -16712, 5996, 23873, 22516, 3018, -18911, -25604, + -11668, 11668, 25605, 18911, -3018, -22516, -23873, -5995, + 16712, 25956, 14287, -8892, -24907, -20855, 0, 20855, + 24907, 8892, -14287, -25956, -16712, 5996, 23873, 22516, + 3018, -18911, -25604, -11668, 11668, 25605, 18911, -3018, + -22516, -23873, -5995, 16712, 25956, 14287, -8892, -24907, + -20855, 0, 20855, 24907, 8892, -14287, -25956, -16712, + 5996, 23873, 22516, 3018, -18911, -25604, -11668, 11669, + 25605, 18911, -3018, -22516, -23873, -5995, 16712, 25955, + 14286, -8892, -24907, -20855, 0, 20855, 24907, 8892, + -14287, -25956, -16712, 5996, 23873, 22516, 3018, -18911, + -25604, -11668, 11669, 25605, 18911, -3018, -22516, -23873, + -5995, 16712, 25955, 14286, -8892, -24907, -20854, 0, + 20855, 24907, 8892, -14287, -25956, -16712, 5996, 23873, + 22516, 3018, -18911, -25604, -11668, 11669, 25605, 18911, + -3018, -22516, -23873, -5995, 16712, 25955, 14286, -8892, + -24907, -20854, 0, 20855, 24907, 8892, -14287, -25956, + -16712, 5996, 23873, 22516, 3017, -18912, -25604, -11668, + 11669, 25605, 18911, -3018, -22516, -23873, -5995, 16712, + 25955, 14286, -8892, -24907, -20854, 0, 20855, 24907, + 8892, -14287, -25956, -16712, 5996, 23873, 22516, 3017, + -18912, -25604, -11668, 11669, 25605, 18911, -3018, -22516, + -23873, -5995, 16712, 25955, 14286, -8893, -24907, -20854}, + { +// Carrier 26 Phase 1 + 18384, 25725, 12339, -10988, -25462, -19422, 2266, 22129, + 24163, 6729, -16126, -25989, -14912, 8178, 24680, 21297, + 756, -20394, -25114, -9599, 13649, 25901, 17284, -5257, + -23564, -22885, -3768, 18384, 25725, 12339, -10988, -25462, + -19422, 2266, 22129, 24162, 6729, -16126, -25988, -14912, + 8178, 24680, 21297, 756, -20394, -25114, -9599, 13649, + 25901, 17284, -5257, -23564, -22885, -3768, 18384, 25725, + 12339, -10988, -25462, -19422, 2266, 22129, 24162, 6729, + -16126, -25988, -14912, 8178, 24680, 21297, 756, -20394, + -25114, -9599, 13649, 25901, 17284, -5257, -23564, -22885, + -3768, 18384, 25725, 12339, -10988, -25462, -19422, 2266, + 22129, 24162, 6729, -16126, -25988, -14912, 8178, 24680, + 21297, 755, -20394, -25114, -9599, 13649, 25901, 17284, + -5257, -23564, -22885, -3767, 18384, 25725, 12339, -10988, + -25462, -19422, 2266, 22129, 24162, 6728, -16126, -25988, + -14912, 8178, 24680, 21297, 755, -20395, -25113, -9599, + 13649, 25901, 17284, -5258, -23564, -22885, -3767, 18385, + 25725, 12339, -10988, -25462, -19422, 2266, 22129, 24162, + 6728, -16126, -25988, -14912, 8178, 24680, 21297, 755, + -20395, -25113, -9598, 13649, 25901, 17284, -5258, -23564, + -22885, -3767, 18385, 25725, 12339, -10988, -25462, -19422, + 2266, 22129, 24162, 6728, -16126, -25988, -14912, 8178, + 24680, 21297, 755, -20395, -25113, -9598, 13649, 25901, + 17284, -5258, -23564, -22884, -3767, 18385, 25725, 12339, + -10988, -25462, -19422, 2266, 22129, 24162, 6728, -16126, + -25988, -14912, 8178, 24680, 21297, 755, -20395, -25113, + -9598, 13649, 25901, 17284, -5258, -23564, -22884, -3767}, + { +// Carrier 26 Phase 2 + 26000, 15526, -7456, -24432, -21722, -1511, 19917, 25299, + 10298, -13000, -25824, -17842, 4514, 23234, 23234, 4514, + -17842, -25824, -12999, 10298, 25299, 19917, -1511, -21722, + -24431, -7456, 15526, 26000, 15526, -7456, -24432, -21722, + -1511, 19917, 25299, 10297, -13000, -25824, -17842, 4514, + 23234, 23234, 4514, -17842, -25824, -12999, 10298, 25299, + 19917, -1511, -21722, -24431, -7456, 15526, 26000, 15526, + -7457, -24432, -21722, -1511, 19917, 25299, 10297, -13000, + -25824, -17842, 4515, 23234, 23234, 4514, -17842, -25824, + -12999, 10298, 25299, 19917, -1511, -21722, -24431, -7456, + 15526, 26000, 15525, -7457, -24432, -21722, -1511, 19917, + 25299, 10297, -13000, -25824, -17842, 4515, 23234, 23234, + 4514, -17842, -25824, -12999, 10298, 25299, 19916, -1512, + -21722, -24431, -7456, 15526, 26000, 15525, -7457, -24432, + -21722, -1511, 19917, 25299, 10297, -13000, -25824, -17842, + 4515, 23234, 23234, 4514, -17842, -25824, -12999, 10298, + 25299, 19916, -1512, -21722, -24431, -7456, 15526, 26000, + 15525, -7457, -24432, -21722, -1511, 19917, 25299, 10297, + -13000, -25824, -17841, 4515, 23234, 23234, 4514, -17842, + -25824, -12999, 10298, 25299, 19916, -1512, -21722, -24431, + -7456, 15526, 26000, 15525, -7457, -24432, -21722, -1511, + 19917, 25299, 10297, -13000, -25824, -17841, 4515, 23234, + 23234, 4514, -17842, -25824, -12999, 10298, 25299, 19916, + -1512, -21722, -24431, -7456, 15526, 26000, 15525, -7457, + -24432, -21722, -1511, 19917, 25299, 10297, -13000, -25824, + -17841, 4515, 23234, 23234, 4514, -17842, -25824, -12999, + 10298, 25299, 19916, -1512, -21723, -24431, -7456, 15526}, + { +// Carrier 26 Phase 3 + 18384, -3768, -22885, -23563, -5257, 17284, 25901, 13649, + -9599, -25114, -20394, 756, 21297, 24680, 8178, -14913, + -25989, -16126, 6729, 24163, 22129, 2265, -19422, -25462, + -10988, 12339, 25725, 18384, -3768, -22885, -23563, -5257, + 17284, 25901, 13649, -9599, -25114, -20394, 756, 21298, + 24680, 8178, -14913, -25989, -16126, 6729, 24163, 22128, + 2265, -19422, -25462, -10987, 12339, 25725, 18384, -3768, + -22885, -23563, -5257, 17284, 25901, 13649, -9599, -25114, + -20394, 756, 21298, 24680, 8177, -14913, -25989, -16125, + 6729, 24163, 22128, 2265, -19422, -25462, -10987, 12339, + 25725, 18384, -3768, -22885, -23563, -5257, 17284, 25901, + 13649, -9599, -25114, -20394, 756, 21298, 24680, 8177, + -14913, -25989, -16125, 6729, 24163, 22128, 2265, -19422, + -25462, -10987, 12339, 25725, 18384, -3768, -22885, -23563, + -5257, 17284, 25901, 13649, -9599, -25114, -20394, 756, + 21298, 24680, 8177, -14913, -25989, -16125, 6729, 24163, + 22128, 2265, -19422, -25462, -10987, 12339, 25725, 18384, + -3768, -22885, -23563, -5257, 17284, 25901, 13649, -9599, + -25114, -20394, 756, 21298, 24680, 8177, -14913, -25989, + -16125, 6729, 24163, 22128, 2265, -19422, -25462, -10987, + 12340, 25725, 18384, -3768, -22885, -23563, -5257, 17285, + 25901, 13648, -9599, -25114, -20394, 756, 21298, 24680, + 8177, -14913, -25989, -16125, 6729, 24163, 22128, 2265, + -19422, -25462, -10987, 12340, 25725, 18384, -3768, -22885, + -23563, -5257, 17285, 25901, 13648, -9599, -25114, -20394, + 756, 21298, 24680, 8177, -14913, -25989, -16125, 6729, + 24163, 22128, 2265, -19423, -25462, -10987, 12340, 25725}, + },{{ + +// Carrier 27 Phase 0 + 0, 21297, 24432, 6729, -16712, -25901, -13000, 10988, + 25605, 18384, -4514, -23564, -22516, -2266, 19917, 25114, + 8892, -14912, -26000, -14912, 8892, 25114, 19917, -2266, + -22516, -23564, -4514, 18384, 25605, 10988, -12999, -25901, + -16712, 6729, 24432, 21297, 0, -21297, -24432, -6729, + 16712, 25901, 13000, -10988, -25605, -18384, 4514, 23564, + 22516, 2266, -19917, -25114, -8892, 14912, 26000, 14912, + -8892, -25114, -19917, 2266, 22516, 23564, 4514, -18384, + -25605, -10988, 12999, 25901, 16712, -6729, -24432, -21297, + 0, 21297, 24432, 6729, -16712, -25901, -13000, 10988, + 25605, 18384, -4514, -23564, -22516, -2266, 19917, 25114, + 8892, -14912, -26000, -14912, 8892, 25114, 19917, -2266, + -22516, -23564, -4514, 18384, 25605, 10988, -12999, -25901, + -16712, 6729, 24432, 21297, 0, -21297, -24432, -6729, + 16712, 25901, 13000, -10988, -25605, -18384, 4514, 23564, + 22516, 2266, -19917, -25114, -8892, 14912, 26000, 14912, + -8892, -25114, -19917, 2266, 22516, 23564, 4514, -18384, + -25605, -10988, 12999, 25901, 16712, -6729, -24432, -21297, + 0, 21297, 24432, 6729, -16712, -25901, -13000, 10988, + 25605, 18384, -4514, -23564, -22516, -2266, 19917, 25114, + 8892, -14912, -26000, -14912, 8892, 25114, 19917, -2266, + -22516, -23564, -4514, 18384, 25605, 10988, -12999, -25901, + -16712, 6729, 24432, 21297, 0, -21297, -24432, -6729, + 16712, 25901, 13000, -10988, -25605, -18384, 4514, 23564, + 22516, 2266, -19917, -25114, -8892, 14912, 26000, 14912, + -8892, -25114, -19917, 2266, 22516, 23564, 4514, -18384, + -25605, -10988, 12999, 25901, 16712, -6729, -24432, -21297}, + { +// Carrier 27 Phase 1 + 18384, 25605, 10988, -12999, -25901, -16712, 6729, 24432, + 21297, 0, -21297, -24432, -6729, 16712, 25901, 13000, + -10988, -25605, -18384, 4514, 23564, 22516, 2266, -19917, + -25114, -8892, 14912, 26000, 14912, -8892, -25114, -19917, + 2266, 22516, 23564, 4514, -18384, -25605, -10988, 12999, + 25901, 16712, -6729, -24432, -21297, 0, 21297, 24432, + 6729, -16712, -25901, -13000, 10988, 25605, 18384, -4514, + -23564, -22516, -2266, 19917, 25114, 8892, -14912, -26000, + -14912, 8892, 25114, 19917, -2266, -22516, -23564, -4514, + 18384, 25605, 10988, -12999, -25901, -16712, 6729, 24432, + 21297, 0, -21297, -24432, -6729, 16712, 25901, 13000, + -10988, -25605, -18384, 4514, 23564, 22516, 2266, -19917, + -25114, -8892, 14912, 26000, 14912, -8892, -25114, -19917, + 2266, 22516, 23564, 4514, -18384, -25605, -10988, 12999, + 25901, 16712, -6729, -24432, -21297, 0, 21297, 24432, + 6729, -16712, -25901, -13000, 10988, 25605, 18384, -4514, + -23564, -22516, -2266, 19917, 25114, 8892, -14912, -26000, + -14912, 8892, 25114, 19917, -2266, -22516, -23564, -4514, + 18384, 25605, 10988, -12999, -25901, -16712, 6729, 24432, + 21297, 0, -21297, -24432, -6729, 16712, 25901, 13000, + -10988, -25605, -18384, 4514, 23564, 22516, 2266, -19917, + -25114, -8892, 14912, 26000, 14912, -8892, -25114, -19917, + 2266, 22516, 23564, 4514, -18384, -25605, -10988, 12999, + 25901, 16712, -6729, -24432, -21297, 0, 21297, 24432, + 6729, -16712, -25901, -13000, 10988, 25605, 18384, -4514, + -23564, -22516, -2266, 19917, 25114, 8892, -14912, -26000, + -14912, 8892, 25114, 19917, -2266, -22516, -23564, -4514}, + { +// Carrier 27 Phase 2 + 26000, 14912, -8892, -25114, -19917, 2266, 22516, 23564, + 4514, -18384, -25605, -10988, 12999, 25901, 16712, -6729, + -24432, -21297, 0, 21297, 24432, 6729, -16712, -25901, + -13000, 10988, 25605, 18384, -4514, -23564, -22516, -2266, + 19917, 25114, 8892, -14912, -26000, -14912, 8892, 25114, + 19917, -2266, -22516, -23564, -4514, 18384, 25605, 10988, + -12999, -25901, -16712, 6729, 24432, 21297, 0, -21297, + -24432, -6729, 16712, 25901, 13000, -10988, -25605, -18384, + 4514, 23564, 22516, 2266, -19917, -25114, -8892, 14912, + 26000, 14912, -8892, -25114, -19917, 2266, 22516, 23564, + 4514, -18384, -25605, -10988, 12999, 25901, 16712, -6729, + -24432, -21297, 0, 21297, 24432, 6729, -16712, -25901, + -13000, 10988, 25605, 18384, -4514, -23564, -22516, -2266, + 19917, 25114, 8892, -14912, -26000, -14912, 8892, 25114, + 19917, -2266, -22516, -23564, -4514, 18384, 25605, 10988, + -12999, -25901, -16712, 6729, 24432, 21297, 0, -21297, + -24432, -6729, 16712, 25901, 13000, -10988, -25605, -18384, + 4514, 23564, 22516, 2266, -19917, -25114, -8892, 14912, + 26000, 14912, -8892, -25114, -19917, 2266, 22516, 23564, + 4514, -18384, -25605, -10988, 12999, 25901, 16712, -6729, + -24432, -21297, 0, 21297, 24432, 6729, -16712, -25901, + -13000, 10988, 25605, 18384, -4514, -23564, -22516, -2266, + 19917, 25114, 8892, -14912, -26000, -14912, 8892, 25114, + 19917, -2266, -22516, -23564, -4514, 18384, 25605, 10988, + -12999, -25901, -16712, 6729, 24432, 21297, 0, -21297, + -24432, -6729, 16712, 25901, 13000, -10988, -25605, -18384, + 4514, 23564, 22516, 2266, -19917, -25114, -8892, 14912}, + { +// Carrier 27 Phase 3 + 18384, -4514, -23564, -22516, -2266, 19917, 25114, 8892, + -14912, -26000, -14912, 8892, 25114, 19917, -2266, -22516, + -23564, -4514, 18384, 25605, 10988, -12999, -25901, -16712, + 6729, 24432, 21297, 0, -21297, -24432, -6729, 16712, + 25901, 13000, -10988, -25605, -18384, 4514, 23564, 22516, + 2266, -19917, -25114, -8892, 14912, 26000, 14912, -8892, + -25114, -19917, 2266, 22516, 23564, 4514, -18384, -25605, + -10988, 12999, 25901, 16712, -6729, -24432, -21297, 0, + 21297, 24432, 6729, -16712, -25901, -13000, 10988, 25605, + 18384, -4514, -23564, -22516, -2266, 19917, 25114, 8892, + -14912, -26000, -14912, 8892, 25114, 19917, -2266, -22516, + -23564, -4514, 18384, 25605, 10988, -12999, -25901, -16712, + 6729, 24432, 21297, 0, -21297, -24432, -6729, 16712, + 25901, 13000, -10988, -25605, -18384, 4514, 23564, 22516, + 2266, -19917, -25114, -8892, 14912, 26000, 14912, -8892, + -25114, -19917, 2266, 22516, 23564, 4514, -18384, -25605, + -10988, 12999, 25901, 16712, -6729, -24432, -21297, 0, + 21297, 24432, 6729, -16712, -25901, -13000, 10988, 25605, + 18384, -4514, -23564, -22516, -2266, 19917, 25114, 8892, + -14912, -26000, -14912, 8892, 25114, 19917, -2266, -22516, + -23564, -4514, 18384, 25605, 10988, -12999, -25901, -16712, + 6729, 24432, 21297, 0, -21297, -24432, -6729, 16712, + 25901, 13000, -10988, -25605, -18384, 4514, 23564, 22516, + 2266, -19917, -25114, -8892, 14912, 26000, 14912, -8892, + -25114, -19917, 2266, 22516, 23564, 4514, -18384, -25605, + -10988, 12999, 25901, 16712, -6729, -24432, -21297, 0, + 21297, 24432, 6729, -16712, -25901, -13000, 10988, 25605}, + },{{ + +// Carrier 28 Phase 0 + 0, 21722, 23873, 4514, -18911, -25299, -8892, 15526, + 25956, 13000, -11668, -25824, -16712, 7456, 24907, 19917, + -3018, -23234, -22516, -1511, 20855, 24432, 5996, -17842, + -25605, -10298, 14287, 26000, 14287, -10298, -25605, -17842, + 5996, 24432, 20855, -1511, -22516, -23234, -3018, 19917, + 24907, 7456, -16712, -25824, -11668, 13000, 25956, 15526, + -8892, -25299, -18911, 4514, 23873, 21722, 0, -21722, + -23873, -4514, 18911, 25299, 8892, -15526, -25956, -12999, + 11668, 25824, 16712, -7456, -24907, -19917, 3018, 23234, + 22516, 1511, -20855, -24432, -5996, 17842, 25604, 10298, + -14287, -26000, -14287, 10298, 25605, 17842, -5996, -24432, + -20855, 1511, 22516, 23234, 3018, -19917, -24907, -7456, + 16712, 25824, 11668, -13000, -25956, -15526, 8892, 25299, + 18911, -4514, -23873, -21722, 0, 21722, 23873, 4514, + -18911, -25299, -8892, 15526, 25956, 12999, -11668, -25824, + -16712, 7456, 24907, 19917, -3018, -23234, -22516, -1511, + 20855, 24431, 5995, -17842, -25604, -10298, 14287, 26000, + 14287, -10298, -25605, -17842, 5996, 24432, 20855, -1511, + -22516, -23234, -3018, 19917, 24907, 7456, -16712, -25824, + -11668, 13000, 25956, 15526, -8892, -25299, -18911, 4514, + 23873, 21722, 0, -21722, -23873, -4514, 18911, 25299, + 8892, -15526, -25956, -12999, 11668, 25824, 16712, -7456, + -24907, -19917, 3018, 23234, 22516, 1511, -20855, -24431, + -5995, 17842, 25604, 10298, -14287, -26000, -14287, 10298, + 25605, 17842, -5996, -24432, -20855, 1511, 22516, 23234, + 3018, -19917, -24907, -7456, 16712, 25824, 11668, -13000, + -25956, -15526, 8892, 25299, 18911, -4514, -23873, -21722}, + { +// Carrier 28 Phase 1 + 18384, 25462, 9599, -14912, -25989, -13649, 10988, 25725, + 17284, -6729, -24680, -20394, 2266, 22885, 22885, 2266, + -20394, -24680, -6729, 17284, 25725, 10988, -13649, -25989, + -14912, 9599, 25462, 18384, -5257, -24163, -21297, 756, + 22129, 23564, 3768, -19422, -25114, -8178, 16126, 25901, + 12339, -12339, -25901, -16126, 8178, 25114, 19422, -3768, + -23564, -22129, -756, 21297, 24163, 5257, -18384, -25462, + -9599, 14912, 25989, 13649, -10988, -25725, -17284, 6729, + 24680, 20394, -2266, -22885, -22885, -2266, 20394, 24680, + 6729, -17284, -25725, -10988, 13649, 25989, 14912, -9599, + -25462, -18384, 5257, 24163, 21297, -756, -22129, -23563, + -3768, 19422, 25114, 8178, -16126, -25901, -12339, 12339, + 25901, 16126, -8178, -25114, -19422, 3768, 23564, 22129, + 756, -21297, -24163, -5257, 18384, 25462, 9599, -14913, + -25989, -13649, 10988, 25725, 17284, -6729, -24680, -20394, + 2266, 22885, 22885, 2266, -20394, -24680, -6729, 17284, + 25725, 10988, -13649, -25989, -14912, 9599, 25462, 18384, + -5257, -24163, -21297, 756, 22129, 23563, 3768, -19422, + -25114, -8178, 16126, 25901, 12339, -12339, -25901, -16126, + 8178, 25114, 19422, -3768, -23564, -22129, -756, 21297, + 24163, 5257, -18384, -25462, -9599, 14913, 25989, 13649, + -10988, -25725, -17284, 6729, 24680, 20394, -2266, -22885, + -22885, -2266, 20394, 24680, 6729, -17284, -25725, -10988, + 13649, 25989, 14912, -9599, -25462, -18384, 5257, 24163, + 21297, -756, -22129, -23563, -3768, 19422, 25114, 8178, + -16126, -25901, -12339, 12339, 25901, 16126, -8178, -25114, + -19422, 3768, 23564, 22129, 756, -21297, -24163, -5257}, + { +// Carrier 28 Phase 2 + 26000, 14287, -10298, -25605, -17842, 5996, 24432, 20855, + -1511, -22516, -23234, -3018, 19917, 24907, 7456, -16712, + -25824, -11668, 13000, 25956, 15526, -8892, -25299, -18911, + 4514, 23873, 21722, 0, -21722, -23873, -4514, 18911, + 25299, 8892, -15526, -25956, -12999, 11668, 25824, 16712, + -7456, -24907, -19917, 3018, 23234, 22516, 1511, -20855, + -24432, -5996, 17842, 25604, 10298, -14287, -26000, -14287, + 10298, 25605, 17842, -5996, -24432, -20855, 1511, 22516, + 23234, 3018, -19917, -24907, -7456, 16712, 25824, 11668, + -13000, -25956, -15526, 8892, 25299, 18911, -4514, -23873, + -21722, 0, 21722, 23873, 4514, -18911, -25299, -8892, + 15526, 25956, 12999, -11668, -25824, -16712, 7456, 24907, + 19917, -3018, -23234, -22516, -1511, 20855, 24431, 5995, + -17842, -25604, -10298, 14287, 26000, 14287, -10298, -25605, + -17842, 5996, 24432, 20855, -1511, -22516, -23234, -3018, + 19917, 24907, 7456, -16712, -25824, -11668, 13000, 25956, + 15526, -8892, -25299, -18911, 4514, 23873, 21722, 0, + -21722, -23873, -4514, 18911, 25299, 8892, -15526, -25956, + -12999, 11668, 25824, 16712, -7456, -24907, -19917, 3018, + 23234, 22516, 1511, -20855, -24431, -5995, 17842, 25604, + 10298, -14287, -26000, -14287, 10298, 25605, 17842, -5996, + -24432, -20855, 1511, 22516, 23234, 3018, -19917, -24907, + -7456, 16712, 25824, 11668, -13000, -25956, -15526, 8892, + 25299, 18911, -4514, -23873, -21722, 0, 21722, 23873, + 4514, -18911, -25299, -8892, 15526, 25956, 12999, -11668, + -25824, -16712, 7456, 24907, 19917, -3018, -23234, -22516, + -1511, 20855, 24431, 5995, -17842, -25604, -10298, 14287}, + { +// Carrier 28 Phase 3 + 18384, -5257, -24163, -21297, 756, 22129, 23564, 3768, + -19422, -25114, -8178, 16126, 25901, 12339, -12339, -25901, + -16126, 8178, 25114, 19422, -3768, -23564, -22129, -756, + 21297, 24163, 5257, -18384, -25462, -9599, 14912, 25989, + 13649, -10988, -25725, -17284, 6729, 24680, 20394, -2266, + -22885, -22885, -2266, 20394, 24680, 6729, -17284, -25725, + -10988, 13649, 25989, 14912, -9599, -25462, -18384, 5257, + 24163, 21297, -756, -22129, -23563, -3768, 19422, 25114, + 8178, -16126, -25901, -12339, 12339, 25901, 16126, -8178, + -25114, -19422, 3768, 23564, 22129, 756, -21297, -24163, + -5257, 18384, 25462, 9599, -14913, -25989, -13649, 10988, + 25725, 17284, -6729, -24680, -20394, 2266, 22885, 22885, + 2266, -20394, -24680, -6729, 17284, 25725, 10988, -13649, + -25989, -14912, 9599, 25462, 18384, -5257, -24163, -21297, + 756, 22129, 23563, 3768, -19422, -25114, -8178, 16126, + 25901, 12339, -12339, -25901, -16126, 8178, 25114, 19422, + -3768, -23564, -22129, -756, 21297, 24163, 5257, -18384, + -25462, -9599, 14913, 25989, 13649, -10988, -25725, -17284, + 6729, 24680, 20394, -2266, -22885, -22885, -2266, 20394, + 24680, 6729, -17284, -25725, -10988, 13649, 25989, 14912, + -9599, -25462, -18384, 5257, 24163, 21297, -756, -22129, + -23563, -3768, 19422, 25114, 8178, -16126, -25901, -12339, + 12339, 25901, 16126, -8178, -25114, -19422, 3768, 23564, + 22129, 756, -21297, -24163, -5257, 18384, 25462, 9599, + -14913, -25989, -13649, 10988, 25725, 17284, -6729, -24680, + -20394, 2266, 22885, 22885, 2266, -20394, -24680, -6729, + 17284, 25725, 10988, -13649, -25989, -14912, 9599, 25462}, + },{{ + +// Carrier 29 Phase 0 + 0, 22129, 23234, 2266, -20855, -24163, -4514, 19422, + 24907, 6729, -17842, -25462, -8892, 16126, 25824, 10988, + -14287, -25989, -12999, 12339, 25956, 14912, -10298, -25725, + -16712, 8178, 25299, 18384, -5996, -24680, -19917, 3768, + 23873, 21297, -1511, -22885, -22516, -756, 21722, 23563, + 3018, -20394, -24431, -5257, 18911, 25114, 7456, -17284, + -25604, -9599, 15526, 25901, 11668, -13649, -26000, -13649, + 11668, 25901, 15526, -9599, -25605, -17284, 7457, 25114, + 18911, -5257, -24432, -20394, 3018, 23564, 21722, -756, + -22516, -22885, -1511, 21298, 23873, 3768, -19917, -24680, + -5995, 18384, 25299, 8177, -16712, -25725, -10297, 14913, + 25956, 12339, -13000, -25989, -14287, 10988, 25824, 16125, + -8892, -25462, -17842, 6729, 24907, 19422, -4515, -24163, + -20855, 2266, 23234, 22128, 0, -22129, -23234, -2265, + 20855, 24162, 4514, -19422, -24907, -6729, 17842, 25462, + 8892, -16126, -25824, -10987, 14287, 25988, 12999, -12339, + -25956, -14912, 10298, 25725, 16712, -8178, -25299, -18384, + 5996, 24680, 19916, -3768, -23873, -21297, 1512, 22885, + 22516, 755, -21722, -23563, -3018, 20395, 24431, 5257, + -18911, -25113, -7456, 17284, 25604, 9599, -15526, -25901, + -11668, 13649, 26000, 13649, -11669, -25901, -15525, 9599, + 25605, 17284, -7457, -25114, -18911, 5258, 24432, 20394, + -3018, -23564, -21722, 756, 22516, 22885, 1511, -21298, + -23873, -3767, 19917, 24680, 5995, -18385, -25299, -8177, + 16712, 25725, 10297, -14913, -25955, -12339, 13000, 25989, + 14286, -10988, -25824, -16125, 8892, 25462, 17841, -6729, + -24907, -19422, 4515, 24163, 20854, -2266, -23234, -22128}, + { +// Carrier 29 Phase 1 + 18384, 25299, 8178, -16712, -25725, -10298, 14913, 25956, + 12339, -13000, -25989, -14287, 10988, 25824, 16126, -8892, + -25462, -17842, 6729, 24907, 19422, -4514, -24163, -20855, + 2266, 23234, 22129, 0, -22129, -23234, -2265, 20855, + 24163, 4514, -19422, -24907, -6729, 17842, 25462, 8892, + -16126, -25824, -10987, 14287, 25988, 12999, -12339, -25956, + -14912, 10298, 25725, 16712, -8178, -25299, -18384, 5996, + 24680, 19917, -3768, -23873, -21297, 1511, 22885, 22516, + 756, -21722, -23563, -3018, 20394, 24431, 5257, -18911, + -25114, -7456, 17284, 25604, 9599, -15526, -25901, -11668, + 13649, 26000, 13649, -11668, -25901, -15525, 9599, 25605, + 17284, -7457, -25114, -18911, 5257, 24432, 20394, -3018, + -23564, -21722, 756, 22516, 22885, 1511, -21298, -23873, + -3767, 19917, 24680, 5995, -18384, -25299, -8177, 16712, + 25725, 10297, -14913, -25955, -12339, 13000, 25989, 14287, + -10988, -25824, -16125, 8892, 25462, 17842, -6729, -24907, + -19422, 4515, 24163, 20855, -2266, -23234, -22128, 0, + 22129, 23234, 2265, -20855, -24162, -4514, 19422, 24907, + 6728, -17842, -25462, -8892, 16126, 25824, 10987, -14287, + -25988, -12999, 12339, 25956, 14912, -10298, -25725, -16712, + 8178, 25299, 18384, -5996, -24680, -19916, 3768, 23873, + 21297, -1512, -22885, -22516, -755, 21722, 23563, 3018, + -20395, -24431, -5257, 18912, 25113, 7456, -17285, -25604, + -9598, 15526, 25901, 11668, -13649, -26000, -13649, 11669, + 25901, 15525, -9599, -25605, -17284, 7457, 25114, 18911, + -5258, -24432, -20394, 3018, 23564, 21722, -756, -22516, + -22885, -1511, 21298, 23873, 3767, -19917, -24680, -5995}, + { +// Carrier 29 Phase 2 + 26000, 13649, -11668, -25901, -15526, 9599, 25605, 17284, + -7456, -25114, -18911, 5257, 24432, 20394, -3018, -23564, + -21722, 756, 22516, 22885, 1511, -21297, -23873, -3768, + 19917, 24680, 5995, -18384, -25299, -8178, 16712, 25725, + 10298, -14913, -25956, -12339, 13000, 25989, 14287, -10988, + -25824, -16126, 8892, 25462, 17842, -6729, -24907, -19422, + 4514, 24163, 20855, -2266, -23234, -22128, 0, 22129, + 23234, 2265, -20855, -24162, -4514, 19422, 24907, 6729, + -17842, -25462, -8892, 16126, 25824, 10987, -14287, -25988, + -12999, 12339, 25956, 14912, -10298, -25725, -16712, 8178, + 25299, 18384, -5996, -24680, -19917, 3768, 23873, 21297, + -1511, -22885, -22516, -756, 21722, 23563, 3018, -20394, + -24431, -5257, 18911, 25114, 7456, -17284, -25604, -9599, + 15526, 25901, 11668, -13649, -26000, -13649, 11669, 25901, + 15525, -9599, -25605, -17284, 7457, 25114, 18911, -5257, + -24432, -20394, 3018, 23564, 21722, -756, -22516, -22885, + -1511, 21298, 23873, 3767, -19917, -24680, -5995, 18385, + 25299, 8177, -16712, -25725, -10297, 14913, 25955, 12339, + -13000, -25989, -14286, 10988, 25824, 16125, -8892, -25462, + -17842, 6729, 24907, 19422, -4515, -24163, -20854, 2266, + 23234, 22128, 0, -22129, -23234, -2265, 20855, 24162, + 4514, -19422, -24907, -6728, 17842, 25462, 8892, -16126, + -25824, -10987, 14287, 25988, 12999, -12339, -25956, -14912, + 10298, 25725, 16712, -8178, -25299, -18384, 5996, 24680, + 19916, -3768, -23873, -21297, 1512, 22885, 22516, 755, + -21722, -23563, -3017, 20395, 24431, 5257, -18912, -25113, + -7456, 17285, 25604, 9598, -15526, -25901, -11668, 13649}, + { +// Carrier 29 Phase 3 + 18384, -5996, -24680, -19917, 3768, 23873, 21297, -1511, + -22885, -22516, -756, 21722, 23563, 3018, -20394, -24431, + -5257, 18911, 25114, 7456, -17284, -25604, -9599, 15526, + 25901, 11668, -13649, -26000, -13649, 11668, 25901, 15526, + -9599, -25605, -17284, 7456, 25114, 18911, -5257, -24432, + -20394, 3018, 23564, 21722, -756, -22516, -22885, -1511, + 21298, 23873, 3768, -19917, -24680, -5995, 18384, 25299, + 8178, -16712, -25725, -10297, 14913, 25956, 12339, -13000, + -25989, -14287, 10988, 25824, 16125, -8892, -25462, -17842, + 6729, 24907, 19422, -4515, -24163, -20855, 2266, 23234, + 22128, 0, -22129, -23234, -2265, 20855, 24162, 4514, + -19422, -24907, -6729, 17842, 25462, 8892, -16126, -25824, + -10987, 14287, 25988, 12999, -12339, -25956, -14912, 10298, + 25725, 16712, -8178, -25299, -18384, 5996, 24680, 19916, + -3768, -23873, -21297, 1512, 22885, 22516, 755, -21722, + -23563, -3018, 20394, 24431, 5257, -18911, -25113, -7456, + 17284, 25604, 9599, -15526, -25901, -11668, 13649, 26000, + 13649, -11669, -25901, -15525, 9599, 25605, 17284, -7457, + -25114, -18911, 5257, 24432, 20394, -3018, -23564, -21722, + 756, 22516, 22885, 1511, -21298, -23873, -3767, 19917, + 24680, 5995, -18385, -25299, -8177, 16712, 25725, 10297, + -14913, -25955, -12339, 13000, 25989, 14286, -10988, -25824, + -16125, 8892, 25462, 17841, -6729, -24907, -19422, 4515, + 24163, 20854, -2266, -23234, -22128, 0, 22129, 23234, + 2265, -20855, -24162, -4514, 19422, 24907, 6728, -17842, + -25462, -8892, 16126, 25824, 10987, -14287, -25988, -12999, + 12340, 25956, 14912, -10298, -25725, -16712, 8178, 25299}, + },{{ + +// Carrier 30 Phase 0 + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22516, + 0, 22516, 22516, 0, -22516, -22516, 0, 22516, + 22516, 0, -22516, -22516, 0, 22516, 22516, 0, + -22516, -22516, 0, 22516, 22516, 0, -22516, -22517, + 0, 22516, 22517, 0, -22516, -22517, 0, 22516, + 22517, 0, -22516, -22517, 0, 22516, 22517, 0, + -22516, -22517, 0, 22516, 22517, 0, -22516, -22517, + 0, 22516, 22517, 0, -22516, -22517, 0, 22516, + 22517, 0, -22516, -22517, 0, 22516, 22517, 0, + -22516, -22517, 0, 22516, 22517, 0, -22516, -22517}, + { +// Carrier 30 Phase 1 + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729, + 18384, 25114, 6729, -18384, -25114, -6729, 18384, 25114, + 6729, -18384, -25114, -6729, 18384, 25114, 6729, -18384, + -25114, -6729, 18384, 25114, 6729, -18384, -25114, -6729}, + { +// Carrier 30 Phase 2 + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999, + 26000, 13000, -12999, -26000, -13000, 12999, 26000, 13000, + -12999, -26000, -13000, 12999, 26000, 13000, -12999, -26000, + -13000, 12999, 26000, 13000, -12999, -26000, -13000, 12999}, + { +// Carrier 30 Phase 3 + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25114, 18384, -6729, -25114, -18384, + 6729, 25114, 18384, -6729, -25114, -18384, 6729, 25114, + 18384, -6729, -25114, -18384, 6729, 25114, 18384, -6729, + -25114, -18384, 6729, 25113, 18384, -6729, -25113, -18384, + 6729, 25113, 18384, -6729, -25113, -18384, 6729, 25113, + 18384, -6729, -25113, -18384, 6728, 25113, 18384, -6728, + -25113, -18385, 6728, 25113, 18385, -6728, -25113, -18385, + 6728, 25113, 18385, -6728, -25113, -18385, 6728, 25113, + 18385, -6728, -25113, -18385, 6728, 25113, 18385, -6728, + -25113, -18385, 6728, 25113, 18385, -6728, -25113, -18385, + 6728, 25113, 18385, -6728, -25113, -18385, 6728, 25113, + 18385, -6728, -25113, -18385, 6728, 25113, 18385, -6728, + -25113, -18385, 6728, 25113, 18385, -6728, -25113, -18385, + 6728, 25113, 18385, -6728, -25113, -18385, 6728, 25113}, + },{{ + +// Carrier 31 Phase 0 + 0, 22885, 21722, -2266, -23873, -20394, 4514, 24680, + 18911, -6729, -25299, -17284, 8892, 25725, 15526, -10988, + -25956, -13649, 13000, 25989, 11668, -14913, -25824, -9599, + 16712, 25462, 7456, -18384, -24907, -5257, 19917, 24163, + 3018, -21297, -23234, -756, 22516, 22128, -1511, -23564, + -20855, 3768, 24432, 19422, -5996, -25114, -17842, 8178, + 25605, 16126, -10298, -25901, -14287, 12339, 26000, 12339, + -14287, -25901, -10297, 16126, 25604, 8178, -17842, -25114, + -5995, 19422, 24431, 3768, -20855, -23563, -1511, 22129, + 22516, -756, -23234, -21297, 3018, 24163, 19917, -5257, + -24907, -18384, 7457, 25462, 16712, -9599, -25824, -14912, + 11668, 25989, 12999, -13649, -25956, -10987, 15526, 25725, + 8892, -17284, -25299, -6729, 18911, 24680, 4514, -20394, + -23873, -2265, 21722, 22885, 0, -22885, -21722, 2266, + 23873, 20394, -4515, -24680, -18911, 6729, 25299, 17284, + -8892, -25725, -15525, 10988, 25956, 13649, -13000, -25988, + -11668, 14913, 25824, 9599, -16712, -25462, -7456, 18384, + 24907, 5257, -19917, -24162, -3018, 21298, 23234, 755, + -22516, -22128, 1512, 23564, 20855, -3768, -24432, -19422, + 5996, 25114, 17842, -8178, -25605, -16125, 10298, 25901, + 14287, -12339, -26000, -12339, 14287, 25901, 10297, -16126, + -25604, -8177, 17842, 25113, 5995, -19422, -24431, -3767, + 20855, 23563, 1511, -22129, -22516, 756, 23234, 21297, + -3018, -24163, -19916, 5257, 24907, 18384, -7457, -25462, + -16712, 9599, 25824, 14912, -11669, -25989, -12999, 13649, + 25955, 10987, -15526, -25725, -8892, 17284, 25299, 6728, + -18911, -24680, -4514, 20395, 23873, 2265, -21722, -22885}, + { +// Carrier 31 Phase 1 + 18384, 24907, 5257, -19917, -24163, -3018, 21297, 23234, + 756, -22516, -22129, 1511, 23564, 20855, -3768, -24432, + -19422, 5996, 25114, 17842, -8178, -25605, -16126, 10298, + 25901, 14287, -12339, -26000, -12339, 14287, 25901, 10298, + -16126, -25604, -8178, 17842, 25114, 5995, -19422, -24431, + -3768, 20855, 23563, 1511, -22129, -22516, 756, 23234, + 21297, -3018, -24163, -19917, 5257, 24907, 18384, -7456, + -25462, -16712, 9599, 25824, 14912, -11668, -25989, -12999, + 13649, 25956, 10987, -15526, -25725, -8892, 17284, 25299, + 6729, -18911, -24680, -4514, 20394, 23873, 2265, -21722, + -22885, 0, 22885, 21722, -2266, -23873, -20394, 4514, + 24680, 18911, -6729, -25299, -17284, 8892, 25725, 15525, + -10988, -25956, -13649, 13000, 25988, 11668, -14913, -25824, + -9599, 16712, 25462, 7456, -18384, -24907, -5257, 19917, + 24162, 3018, -21298, -23234, -756, 22516, 22128, -1511, + -23564, -20855, 3768, 24432, 19422, -5996, -25114, -17842, + 8178, 25605, 16125, -10298, -25901, -14287, 12339, 26000, + 12339, -14287, -25901, -10297, 16126, 25604, 8177, -17842, + -25114, -5995, 19422, 24431, 3767, -20855, -23563, -1511, + 22129, 22516, -756, -23234, -21297, 3018, 24163, 19916, + -5257, -24907, -18384, 7457, 25462, 16712, -9599, -25824, + -14912, 11669, 25989, 12999, -13649, -25955, -10987, 15526, + 25725, 8892, -17284, -25299, -6729, 18911, 24680, 4514, + -20395, -23873, -2265, 21722, 22885, 0, -22885, -21722, + 2266, 23873, 20394, -4515, -24680, -18911, 6729, 25299, + 17284, -8892, -25725, -15525, 10988, 25956, 13649, -13000, + -25988, -11668, 14913, 25824, 9599, -16712, -25462, -7456}, + { +// Carrier 31 Phase 2 + 26000, 12339, -14287, -25901, -10298, 16126, 25605, 8178, + -17842, -25114, -5996, 19422, 24432, 3768, -20855, -23563, + -1511, 22129, 22516, -756, -23234, -21297, 3018, 24163, + 19917, -5257, -24907, -18384, 7456, 25462, 16712, -9599, + -25824, -14912, 11668, 25989, 12999, -13649, -25956, -10988, + 15526, 25725, 8892, -17284, -25299, -6729, 18911, 24680, + 4514, -20394, -23873, -2265, 21722, 22885, 0, -22885, + -21722, 2266, 23873, 20394, -4514, -24680, -18911, 6729, + 25299, 17284, -8892, -25725, -15526, 10988, 25956, 13649, + -13000, -25988, -11668, 14913, 25824, 9599, -16712, -25462, + -7456, 18384, 24907, 5257, -19917, -24162, -3018, 21298, + 23234, 756, -22516, -22128, 1511, 23564, 20855, -3768, + -24432, -19422, 5996, 25114, 17842, -8178, -25605, -16125, + 10298, 25901, 14287, -12339, -26000, -12339, 14287, 25901, + 10297, -16126, -25604, -8177, 17842, 25114, 5995, -19422, + -24431, -3768, 20855, 23563, 1511, -22129, -22516, 756, + 23234, 21297, -3018, -24163, -19917, 5257, 24907, 18384, + -7457, -25462, -16712, 9599, 25824, 14912, -11668, -25989, + -12999, 13649, 25955, 10987, -15526, -25725, -8892, 17284, + 25299, 6729, -18911, -24680, -4514, 20394, 23873, 2265, + -21722, -22885, 0, 22885, 21722, -2266, -23873, -20394, + 4515, 24680, 18911, -6729, -25299, -17284, 8892, 25725, + 15525, -10988, -25956, -13649, 13000, 25988, 11668, -14913, + -25824, -9599, 16712, 25462, 7456, -18385, -24907, -5257, + 19917, 24162, 3018, -21298, -23234, -755, 22516, 22128, + -1512, -23564, -20855, 3768, 24432, 19422, -5996, -25114, + -17842, 8178, 25605, 16125, -10298, -25901, -14286, 12339}, + { +// Carrier 31 Phase 3 + 18384, -7456, -25462, -16712, 9599, 25824, 14912, -11668, + -25989, -12999, 13649, 25956, 10988, -15526, -25725, -8892, + 17284, 25299, 6729, -18911, -24680, -4514, 20394, 23873, + 2266, -21722, -22885, 0, 22885, 21722, -2266, -23873, + -20394, 4514, 24680, 18911, -6729, -25299, -17284, 8892, + 25725, 15526, -10988, -25956, -13649, 13000, 25988, 11668, + -14913, -25824, -9599, 16712, 25462, 7456, -18384, -24907, + -5257, 19917, 24162, 3018, -21298, -23234, -756, 22516, + 22128, -1511, -23564, -20855, 3768, 24432, 19422, -5996, + -25114, -17842, 8178, 25605, 16126, -10298, -25901, -14287, + 12339, 26000, 12339, -14287, -25901, -10297, 16126, 25604, + 8178, -17842, -25114, -5995, 19422, 24431, 3768, -20855, + -23563, -1511, 22129, 22516, -756, -23234, -21297, 3018, + 24163, 19917, -5257, -24907, -18384, 7457, 25462, 16712, + -9599, -25824, -14912, 11668, 25989, 12999, -13649, -25956, + -10987, 15526, 25725, 8892, -17284, -25299, -6729, 18911, + 24680, 4514, -20394, -23873, -2265, 21722, 22885, 0, + -22885, -21722, 2266, 23873, 20394, -4515, -24680, -18911, + 6729, 25299, 17284, -8892, -25725, -15525, 10988, 25956, + 13649, -13000, -25988, -11668, 14913, 25824, 9599, -16712, + -25462, -7456, 18384, 24907, 5257, -19917, -24162, -3018, + 21298, 23234, 755, -22516, -22128, 1512, 23564, 20855, + -3768, -24432, -19422, 5996, 25114, 17842, -8178, -25605, + -16125, 10298, 25901, 14286, -12339, -26000, -12339, 14287, + 25901, 10297, -16126, -25604, -8177, 17842, 25113, 5995, + -19422, -24431, -3767, 20855, 23563, 1511, -22129, -22516, + 756, 23234, 21297, -3018, -24163, -19916, 5258, 24907}, + },{{ + +// Carrier 32 Phase 0 + 0, 23234, 20855, -4514, -24907, -17842, 8892, 25824, + 14287, -13000, -25956, -10298, 16712, 25299, 5995, -19917, + -23873, -1511, 22516, 21722, -3018, -24432, -18911, 7456, + 25605, 15526, -11668, -26000, -11668, 15526, 25604, 7456, + -18911, -24431, -3018, 21722, 22516, -1511, -23873, -19917, + 5996, 25299, 16712, -10298, -25956, -12999, 14287, 25824, + 8892, -17842, -24907, -4514, 20855, 23234, 0, -23234, + -20855, 4514, 24907, 17842, -8892, -25824, -14287, 13000, + 25956, 10297, -16712, -25299, -5995, 19917, 23873, 1511, + -22516, -21722, 3018, 24432, 18911, -7457, -25605, -15525, + 11668, 26000, 11668, -15526, -25604, -7456, 18911, 24431, + 3018, -21722, -22516, 1511, 23873, 19917, -5996, -25299, + -16712, 10298, 25956, 12999, -14287, -25824, -8892, 17842, + 24907, 4514, -20855, -23234, 0, 23234, 20855, -4515, + -24907, -17842, 8892, 25824, 14287, -13000, -25955, -10297, + 16712, 25299, 5995, -19917, -23873, -1511, 22516, 21722, + -3018, -24432, -18911, 7457, 25605, 15525, -11669, -26000, + -11668, 15526, 25604, 7456, -18911, -24431, -3018, 21722, + 22516, -1512, -23873, -19916, 5996, 25299, 16712, -10298, + -25956, -12999, 14287, 25824, 8892, -17842, -24907, -4514, + 20855, 23234, 0, -23234, -20854, 4515, 24907, 17842, + -8892, -25824, -14286, 13000, 25955, 10297, -16712, -25299, + -5995, 19917, 23873, 1511, -22516, -21722, 3018, 24432, + 18911, -7457, -25605, -15525, 11669, 26000, 11668, -15526, + -25604, -7456, 18911, 24431, 3018, -21722, -22516, 1512, + 23873, 19916, -5996, -25299, -16712, 10298, 25956, 12999, + -14287, -25824, -8892, 17842, 24907, 4514, -20855, -23234}, + { +// Carrier 32 Phase 1 + 18384, 24680, 3768, -21297, -22885, 756, 23564, 20394, + -5257, -25114, -17284, 9599, 25901, 13649, -13649, -25901, + -9599, 17284, 25114, 5257, -20394, -23563, -756, 22885, + 21297, -3768, -24680, -18384, 8178, 25725, 14912, -12339, + -25988, -10988, 16126, 25462, 6729, -19422, -24163, -2265, + 22129, 22128, -2266, -24163, -19422, 6729, 25462, 16126, + -10988, -25989, -12339, 14913, 25725, 8178, -18384, -24680, + -3768, 21298, 22885, -756, -23564, -20394, 5257, 25114, + 17284, -9599, -25901, -13649, 13649, 25901, 9599, -17284, + -25114, -5257, 20394, 23563, 756, -22885, -21297, 3768, + 24680, 18384, -8178, -25725, -14912, 12339, 25988, 10987, + -16126, -25462, -6729, 19422, 24162, 2265, -22129, -22128, + 2266, 24163, 19422, -6729, -25462, -16125, 10988, 25989, + 12339, -14913, -25725, -8177, 18384, 24680, 3767, -21298, + -22885, 756, 23564, 20394, -5257, -25114, -17284, 9599, + 25901, 13649, -13649, -25901, -9599, 17284, 25114, 5257, + -20394, -23563, -755, 22885, 21297, -3768, -24680, -18384, + 8178, 25725, 14912, -12339, -25988, -10987, 16126, 25462, + 6728, -19422, -24162, -2265, 22129, 22128, -2266, -24163, + -19422, 6729, 25462, 16125, -10988, -25989, -12339, 14913, + 25725, 8177, -18385, -24680, -3767, 21298, 22885, -756, + -23564, -20394, 5258, 25114, 17284, -9599, -25901, -13649, + 13649, 25901, 9598, -17284, -25113, -5257, 20395, 23563, + 755, -22885, -21297, 3768, 24680, 18384, -8178, -25725, + -14912, 12339, 25988, 10987, -16126, -25462, -6728, 19422, + 24162, 2265, -22129, -22128, 2266, 24163, 19422, -6729, + -25462, -16125, 10988, 25989, 12339, -14913, -25725, -8177}, + { +// Carrier 32 Phase 2 + 26000, 11668, -15526, -25605, -7456, 18911, 24432, 3018, + -21722, -22516, 1511, 23873, 19917, -5996, -25299, -16712, + 10298, 25956, 12999, -14287, -25824, -8892, 17842, 24907, + 4514, -20855, -23234, 0, 23234, 20855, -4514, -24907, + -17842, 8892, 25824, 14287, -13000, -25956, -10298, 16712, + 25299, 5995, -19917, -23873, -1511, 22516, 21722, -3018, + -24432, -18911, 7456, 25605, 15526, -11668, -26000, -11668, + 15526, 25604, 7456, -18911, -24431, -3018, 21722, 22516, + -1511, -23873, -19917, 5996, 25299, 16712, -10298, -25956, + -12999, 14287, 25824, 8892, -17842, -24907, -4514, 20855, + 23234, 0, -23234, -20855, 4515, 24907, 17842, -8892, + -25824, -14287, 13000, 25956, 10297, -16712, -25299, -5995, + 19917, 23873, 1511, -22516, -21722, 3018, 24432, 18911, + -7457, -25605, -15525, 11668, 26000, 11668, -15526, -25604, + -7456, 18911, 24431, 3018, -21722, -22516, 1512, 23873, + 19917, -5996, -25299, -16712, 10298, 25956, 12999, -14287, + -25824, -8892, 17842, 24907, 4514, -20855, -23234, 0, + 23234, 20855, -4515, -24907, -17842, 8892, 25824, 14286, + -13000, -25955, -10297, 16712, 25299, 5995, -19917, -23873, + -1511, 22516, 21722, -3018, -24432, -18911, 7457, 25605, + 15525, -11669, -26000, -11668, 15526, 25604, 7456, -18911, + -24431, -3018, 21722, 22516, -1512, -23873, -19916, 5996, + 25299, 16712, -10298, -25956, -12999, 14287, 25824, 8892, + -17842, -24907, -4514, 20855, 23234, 0, -23234, -20854, + 4515, 24907, 17841, -8892, -25824, -14286, 13000, 25955, + 10297, -16712, -25299, -5995, 19917, 23873, 1511, -22516, + -21722, 3018, 24432, 18911, -7457, -25605, -15525, 11669}, + { +// Carrier 32 Phase 3 + 18384, -8178, -25725, -14912, 12339, 25989, 10988, -16126, + -25462, -6729, 19422, 24163, 2266, -22129, -22129, 2266, + 24163, 19422, -6729, -25462, -16126, 10988, 25989, 12339, + -14913, -25725, -8178, 18384, 24680, 3768, -21297, -22885, + 756, 23564, 20394, -5257, -25114, -17284, 9599, 25901, + 13649, -13649, -25901, -9599, 17284, 25114, 5257, -20394, + -23563, -756, 22885, 21297, -3768, -24680, -18384, 8178, + 25725, 14912, -12339, -25988, -10987, 16126, 25462, 6729, + -19422, -24162, -2265, 22129, 22128, -2266, -24163, -19422, + 6729, 25462, 16125, -10988, -25989, -12339, 14913, 25725, + 8178, -18384, -24680, -3768, 21298, 22885, -756, -23564, + -20394, 5257, 25114, 17284, -9599, -25901, -13649, 13649, + 25901, 9599, -17284, -25114, -5257, 20394, 23563, 755, + -22885, -21297, 3768, 24680, 18384, -8178, -25725, -14912, + 12339, 25988, 10987, -16126, -25462, -6729, 19422, 24162, + 2265, -22129, -22128, 2266, 24163, 19422, -6729, -25462, + -16125, 10988, 25989, 12339, -14913, -25725, -8177, 18384, + 24680, 3767, -21298, -22885, 756, 23564, 20394, -5257, + -25114, -17284, 9599, 25901, 13649, -13649, -25901, -9599, + 17284, 25113, 5257, -20395, -23563, -755, 22885, 21297, + -3768, -24680, -18384, 8178, 25725, 14912, -12339, -25988, + -10987, 16126, 25462, 6728, -19422, -24162, -2265, 22129, + 22128, -2266, -24163, -19422, 6729, 25462, 16125, -10988, + -25989, -12339, 14913, 25725, 8177, -18385, -24680, -3767, + 21298, 22885, -756, -23564, -20394, 5258, 25114, 17284, + -9599, -25901, -13649, 13649, 25901, 9598, -17285, -25113, + -5257, 20395, 23563, 755, -22885, -21297, 3768, 24680}, + },{{ + +// Carrier 33 Phase 0 + 0, 23564, 19917, -6729, -25605, -14912, 13000, 25901, + 8892, -18384, -24432, -2266, 22516, 21297, -4514, -25114, + -16712, 10988, 26000, 10988, -16712, -25114, -4514, 21297, + 22516, -2266, -24432, -18384, 8892, 25901, 12999, -14913, + -25604, -6729, 19917, 23563, 0, -23564, -19917, 6729, + 25605, 14912, -13000, -25901, -8892, 18384, 24431, 2265, + -22516, -21297, 4515, 25114, 16712, -10988, -26000, -10987, + 16712, 25114, 4514, -21298, -22516, 2266, 24432, 18384, + -8892, -25901, -12999, 14913, 25604, 6729, -19917, -23563, + 0, 23564, 19916, -6729, -25605, -14912, 13000, 25901, + 8892, -18384, -24431, -2265, 22516, 21297, -4515, -25114, + -16712, 10988, 26000, 10987, -16712, -25113, -4514, 21298, + 22516, -2266, -24432, -18384, 8892, 25901, 12999, -14913, + -25604, -6728, 19917, 23563, 0, -23564, -19916, 6729, + 25605, 14912, -13000, -25901, -8892, 18385, 24431, 2265, + -22516, -21297, 4515, 25114, 16712, -10988, -26000, -10987, + 16712, 25113, 4514, -21298, -22516, 2266, 24432, 18384, + -8892, -25901, -12999, 14913, 25604, 6728, -19917, -23563, + 0, 23564, 19916, -6729, -25605, -14912, 13000, 25901, + 8892, -18385, -24431, -2265, 22516, 21297, -4515, -25114, + -16712, 10988, 26000, 10987, -16712, -25113, -4514, 21298, + 22516, -2266, -24432, -18384, 8893, 25901, 12999, -14913, + -25604, -6728, 19917, 23563, 0, -23564, -19916, 6729, + 25605, 14912, -13000, -25901, -8891, 18385, 24431, 2265, + -22516, -21297, 4515, 25114, 16711, -10988, -26000, -10987, + 16713, 25113, 4514, -21298, -22516, 2266, 24432, 18384, + -8893, -25901, -12999, 14913, 25604, 6728, -19917, -23563}, + { +// Carrier 33 Phase 1 + 18384, 24432, 2266, -22516, -21297, 4514, 25114, 16712, + -10988, -26000, -10988, 16712, 25114, 4514, -21297, -22516, + 2266, 24432, 18384, -8892, -25901, -12999, 14913, 25604, + 6729, -19917, -23563, 0, 23564, 19917, -6729, -25605, + -14912, 13000, 25901, 8892, -18384, -24431, -2265, 22516, + 21297, -4514, -25114, -16712, 10988, 26000, 10987, -16712, + -25114, -4514, 21298, 22516, -2266, -24432, -18384, 8892, + 25901, 12999, -14913, -25604, -6729, 19917, 23563, 0, + -23564, -19917, 6729, 25605, 14912, -13000, -25901, -8892, + 18384, 24431, 2265, -22516, -21297, 4515, 25114, 16712, + -10988, -26000, -10987, 16712, 25113, 4514, -21298, -22516, + 2266, 24432, 18384, -8892, -25901, -12999, 14913, 25604, + 6728, -19917, -23563, 0, 23564, 19916, -6729, -25605, + -14912, 13000, 25901, 8892, -18385, -24431, -2265, 22516, + 21297, -4515, -25114, -16712, 10988, 26000, 10987, -16712, + -25113, -4514, 21298, 22516, -2266, -24432, -18384, 8892, + 25901, 12999, -14913, -25604, -6728, 19917, 23563, 0, + -23564, -19916, 6729, 25605, 14912, -13000, -25901, -8892, + 18385, 24431, 2265, -22516, -21297, 4515, 25114, 16712, + -10988, -26000, -10987, 16712, 25113, 4514, -21298, -22516, + 2266, 24432, 18384, -8893, -25901, -12999, 14913, 25604, + 6728, -19917, -23563, 0, 23564, 19916, -6729, -25605, + -14912, 13000, 25901, 8891, -18385, -24431, -2265, 22516, + 21297, -4515, -25114, -16711, 10988, 26000, 10987, -16712, + -25113, -4514, 21298, 22516, -2266, -24432, -18384, 8893, + 25901, 12999, -14913, -25604, -6728, 19917, 23563, 0, + -23564, -19916, 6730, 25605, 14912, -13000, -25900, -8891}, + { +// Carrier 33 Phase 2 + 26000, 10988, -16712, -25114, -4514, 21297, 22516, -2266, + -24432, -18384, 8892, 25901, 12999, -14913, -25604, -6729, + 19917, 23563, 0, -23564, -19917, 6729, 25605, 14912, + -13000, -25901, -8892, 18384, 24431, 2265, -22516, -21297, + 4514, 25114, 16712, -10988, -26000, -10987, 16712, 25114, + 4514, -21298, -22516, 2266, 24432, 18384, -8892, -25901, + -12999, 14913, 25604, 6729, -19917, -23563, 0, 23564, + 19917, -6729, -25605, -14912, 13000, 25901, 8892, -18384, + -24431, -2265, 22516, 21297, -4515, -25114, -16712, 10988, + 26000, 10987, -16712, -25114, -4514, 21298, 22516, -2266, + -24432, -18384, 8892, 25901, 12999, -14913, -25604, -6729, + 19917, 23563, 0, -23564, -19916, 6729, 25605, 14912, + -13000, -25901, -8892, 18385, 24431, 2265, -22516, -21297, + 4515, 25114, 16712, -10988, -26000, -10987, 16712, 25113, + 4514, -21298, -22516, 2266, 24432, 18384, -8892, -25901, + -12999, 14913, 25604, 6728, -19917, -23563, 0, 23564, + 19916, -6729, -25605, -14912, 13000, 25901, 8892, -18385, + -24431, -2265, 22516, 21297, -4515, -25114, -16712, 10988, + 26000, 10987, -16712, -25113, -4514, 21298, 22516, -2266, + -24432, -18384, 8893, 25901, 12999, -14913, -25604, -6728, + 19917, 23563, 0, -23564, -19916, 6729, 25605, 14912, + -13000, -25901, -8891, 18385, 24431, 2265, -22516, -21297, + 4515, 25114, 16712, -10988, -26000, -10987, 16712, 25113, + 4514, -21298, -22516, 2266, 24432, 18384, -8893, -25901, + -12999, 14913, 25604, 6728, -19917, -23563, 0, 23564, + 19916, -6729, -25605, -14912, 13000, 25901, 8891, -18385, + -24431, -2265, 22517, 21297, -4515, -25114, -16711, 10988}, + { +// Carrier 33 Phase 3 + 18384, -8892, -25901, -13000, 14912, 25604, 6729, -19917, + -23563, 0, 23564, 19917, -6729, -25605, -14912, 13000, + 25901, 8892, -18384, -24431, -2265, 22516, 21297, -4514, + -25114, -16712, 10988, 26000, 10987, -16712, -25114, -4514, + 21298, 22516, -2266, -24432, -18384, 8892, 25901, 12999, + -14913, -25604, -6729, 19917, 23563, 0, -23564, -19917, + 6729, 25605, 14912, -13000, -25901, -8892, 18384, 24431, + 2265, -22516, -21297, 4515, 25114, 16712, -10988, -26000, + -10987, 16712, 25114, 4514, -21298, -22516, 2266, 24432, + 18384, -8892, -25901, -12999, 14913, 25604, 6729, -19917, + -23563, 0, 23564, 19916, -6729, -25605, -14912, 13000, + 25901, 8892, -18384, -24431, -2265, 22516, 21297, -4515, + -25114, -16712, 10988, 26000, 10987, -16712, -25113, -4514, + 21298, 22516, -2266, -24432, -18384, 8892, 25901, 12999, + -14913, -25604, -6728, 19917, 23563, 0, -23564, -19916, + 6729, 25605, 14912, -13000, -25901, -8892, 18385, 24431, + 2265, -22516, -21297, 4515, 25114, 16712, -10988, -26000, + -10987, 16712, 25113, 4514, -21298, -22516, 2266, 24432, + 18384, -8892, -25901, -12999, 14913, 25604, 6728, -19917, + -23563, 0, 23564, 19916, -6729, -25605, -14912, 13000, + 25901, 8892, -18385, -24431, -2265, 22516, 21297, -4515, + -25114, -16712, 10988, 26000, 10987, -16712, -25113, -4514, + 21298, 22516, -2266, -24432, -18384, 8893, 25901, 12999, + -14913, -25604, -6728, 19917, 23563, 0, -23564, -19916, + 6729, 25605, 14912, -13000, -25901, -8891, 18385, 24431, + 2265, -22516, -21297, 4515, 25114, 16711, -10988, -26000, + -10987, 16713, 25113, 4514, -21298, -22516, 2266, 24432}, + },{{ + +// Carrier 34 Phase 0 + 0, 23873, 18911, -8892, -25956, -11668, 16712, 24907, + 3018, -22516, -20855, 5996, 25605, 14287, -14287, -25605, + -5996, 20855, 22516, -3018, -24907, -16712, 11668, 25956, + 8892, -18911, -23873, 0, 23873, 18911, -8892, -25956, + -11668, 16712, 24907, 3018, -22516, -20855, 5995, 25604, + 14287, -14287, -25605, -5996, 20855, 22516, -3018, -24907, + -16712, 11668, 25956, 8892, -18911, -23873, 0, 23873, + 18911, -8892, -25956, -11668, 16712, 24907, 3018, -22516, + -20855, 5995, 25604, 14287, -14287, -25605, -5996, 20855, + 22516, -3018, -24907, -16712, 11668, 25956, 8892, -18911, + -23873, 0, 23873, 18911, -8892, -25956, -11668, 16712, + 24907, 3018, -22516, -20855, 5995, 25604, 14287, -14287, + -25605, -5996, 20855, 22516, -3018, -24907, -16712, 11668, + 25956, 8892, -18911, -23873, 0, 23873, 18911, -8892, + -25956, -11668, 16712, 24907, 3018, -22516, -20855, 5995, + 25604, 14287, -14287, -25605, -5996, 20855, 22516, -3018, + -24907, -16712, 11668, 25956, 8892, -18911, -23873, 0, + 23873, 18911, -8892, -25956, -11668, 16712, 24907, 3018, + -22516, -20855, 5995, 25604, 14287, -14287, -25605, -5996, + 20855, 22516, -3018, -24907, -16712, 11668, 25956, 8892, + -18911, -23873, 0, 23873, 18911, -8892, -25956, -11668, + 16712, 24907, 3018, -22516, -20855, 5995, 25604, 14287, + -14287, -25605, -5996, 20855, 22516, -3018, -24907, -16712, + 11668, 25956, 8892, -18911, -23873, 0, 23873, 18911, + -8892, -25956, -11668, 16712, 24907, 3018, -22516, -20855, + 5995, 25604, 14287, -14287, -25605, -5996, 20855, 22516, + -3018, -24907, -16712, 11668, 25956, 8892, -18911, -23873}, + { +// Carrier 34 Phase 1 + 18384, 24163, 756, -23563, -19422, 8178, 25901, 12339, + -16126, -25114, -3768, 22129, 21297, -5257, -25462, -14913, + 13649, 25725, 6729, -20394, -22885, 2266, 24680, 17284, + -10988, -25989, -9599, 18384, 24163, 756, -23563, -19422, + 8178, 25901, 12339, -16126, -25114, -3768, 22129, 21297, + -5257, -25462, -14913, 13649, 25725, 6729, -20394, -22885, + 2266, 24680, 17284, -10988, -25989, -9599, 18384, 24163, + 756, -23563, -19422, 8178, 25901, 12339, -16126, -25114, + -3768, 22129, 21297, -5257, -25462, -14913, 13649, 25725, + 6729, -20394, -22885, 2266, 24680, 17284, -10988, -25989, + -9599, 18384, 24163, 756, -23563, -19422, 8178, 25901, + 12339, -16126, -25114, -3768, 22129, 21297, -5257, -25462, + -14913, 13649, 25725, 6729, -20394, -22885, 2265, 24680, + 17284, -10988, -25989, -9599, 18384, 24163, 756, -23563, + -19422, 8178, 25901, 12339, -16126, -25114, -3768, 22129, + 21297, -5257, -25462, -14913, 13649, 25725, 6729, -20394, + -22885, 2265, 24680, 17284, -10988, -25989, -9599, 18384, + 24163, 756, -23563, -19422, 8178, 25901, 12339, -16126, + -25114, -3768, 22128, 21297, -5257, -25462, -14913, 13649, + 25725, 6729, -20394, -22885, 2265, 24680, 17284, -10988, + -25989, -9599, 18384, 24163, 756, -23563, -19422, 8178, + 25901, 12339, -16126, -25114, -3768, 22128, 21298, -5257, + -25462, -14913, 13649, 25725, 6729, -20394, -22885, 2265, + 24680, 17284, -10987, -25989, -9599, 18384, 24163, 756, + -23563, -19422, 8178, 25901, 12339, -16126, -25114, -3768, + 22128, 21298, -5257, -25462, -14913, 13649, 25725, 6729, + -20394, -22885, 2265, 24680, 17284, -10987, -25989, -9599}, + { +// Carrier 34 Phase 2 + 26000, 10298, -17842, -24432, -1511, 23234, 19917, -7456, + -25824, -13000, 15526, 25299, 4514, -21722, -21722, 4514, + 25299, 15526, -12999, -25824, -7456, 19917, 23234, -1511, + -24432, -17842, 10298, 26000, 10298, -17842, -24432, -1511, + 23234, 19917, -7456, -25824, -13000, 15526, 25299, 4514, + -21722, -21722, 4514, 25299, 15526, -12999, -25824, -7456, + 19917, 23234, -1511, -24432, -17842, 10298, 26000, 10298, + -17842, -24432, -1511, 23234, 19917, -7456, -25824, -13000, + 15526, 25299, 4514, -21722, -21722, 4514, 25299, 15526, + -12999, -25824, -7456, 19917, 23234, -1511, -24431, -17842, + 10298, 26000, 10298, -17842, -24432, -1511, 23234, 19917, + -7456, -25824, -13000, 15526, 25299, 4514, -21722, -21722, + 4514, 25299, 15526, -12999, -25824, -7456, 19917, 23234, + -1511, -24431, -17842, 10298, 26000, 10298, -17842, -24432, + -1511, 23234, 19917, -7456, -25824, -13000, 15526, 25299, + 4514, -21722, -21722, 4514, 25299, 15526, -12999, -25824, + -7456, 19917, 23234, -1511, -24431, -17842, 10298, 26000, + 10298, -17842, -24432, -1511, 23234, 19917, -7456, -25824, + -13000, 15526, 25299, 4514, -21722, -21722, 4514, 25299, + 15526, -12999, -25824, -7456, 19917, 23234, -1511, -24431, + -17842, 10298, 26000, 10298, -17842, -24432, -1511, 23234, + 19917, -7456, -25824, -13000, 15526, 25299, 4514, -21722, + -21722, 4514, 25299, 15526, -12999, -25824, -7456, 19917, + 23234, -1511, -24431, -17842, 10297, 26000, 10298, -17842, + -24432, -1511, 23234, 19917, -7456, -25824, -13000, 15526, + 25299, 4514, -21722, -21722, 4514, 25299, 15526, -12999, + -25824, -7456, 19917, 23234, -1511, -24431, -17842, 10297}, + { +// Carrier 34 Phase 3 + 18384, -9599, -25989, -10988, 17284, 24680, 2266, -22885, + -20394, 6729, 25725, 13649, -14912, -25462, -5257, 21297, + 22129, -3768, -25114, -16126, 12339, 25901, 8178, -19422, + -23564, 756, 24163, 18384, -9599, -25989, -10988, 17284, + 24680, 2266, -22885, -20394, 6729, 25725, 13649, -14912, + -25462, -5257, 21297, 22129, -3768, -25114, -16126, 12339, + 25901, 8178, -19422, -23564, 756, 24163, 18384, -9599, + -25989, -10988, 17284, 24680, 2266, -22885, -20394, 6729, + 25725, 13649, -14912, -25462, -5257, 21297, 22129, -3768, + -25114, -16126, 12339, 25901, 8178, -19422, -23564, 756, + 24163, 18384, -9599, -25989, -10988, 17284, 24680, 2266, + -22885, -20394, 6729, 25725, 13649, -14912, -25462, -5257, + 21297, 22129, -3768, -25114, -16126, 12339, 25901, 8178, + -19422, -23564, 756, 24163, 18384, -9599, -25988, -10988, + 17284, 24680, 2266, -22885, -20394, 6729, 25725, 13649, + -14912, -25462, -5257, 21297, 22129, -3768, -25114, -16126, + 12339, 25901, 8178, -19422, -23564, 756, 24163, 18384, + -9599, -25988, -10988, 17284, 24680, 2266, -22885, -20394, + 6729, 25725, 13649, -14912, -25462, -5257, 21297, 22129, + -3768, -25114, -16126, 12339, 25901, 8178, -19422, -23564, + 756, 24163, 18384, -9599, -25988, -10988, 17284, 24680, + 2266, -22885, -20394, 6729, 25725, 13649, -14912, -25462, + -5257, 21297, 22129, -3768, -25114, -16126, 12339, 25901, + 8178, -19422, -23564, 756, 24163, 18384, -9599, -25988, + -10988, 17284, 24680, 2266, -22885, -20394, 6729, 25725, + 13649, -14912, -25462, -5257, 21297, 22129, -3768, -25114, + -16126, 12339, 25901, 8178, -19422, -23564, 756, 24162}, + },{{ + +// Carrier 35 Phase 0 + 0, 24163, 17842, -10988, -25956, -8178, 19917, 22885, + -3018, -25114, -15526, 13649, 25604, 5257, -21722, -21297, + 5996, 25725, 12999, -16126, -24907, -2266, 23234, 19422, + -8892, -25989, -10298, 18384, 23873, -756, -24432, -17284, + 11668, 25901, 7456, -20394, -22516, 3768, 25299, 14912, + -14287, -25462, -4514, 22129, 20855, -6729, -25824, -12339, + 16712, 24680, 1511, -23564, -18911, 9599, 26000, 9599, + -18911, -23563, 1511, 24680, 16712, -12339, -25824, -6729, + 20855, 22128, -4514, -25462, -14287, 14913, 25299, 3768, + -22516, -20394, 7457, 25901, 11668, -17284, -24431, -756, + 23873, 18384, -10298, -25988, -8892, 19422, 23234, -2266, + -24907, -16125, 13000, 25725, 5995, -21298, -21722, 5257, + 25605, 13649, -15526, -25114, -3018, 22885, 19917, -8178, + -25956, -10987, 17842, 24162, 0, -24163, -17842, 10988, + 25955, 8177, -19917, -22885, 3018, 25114, 15525, -13649, + -25604, -5257, 21722, 21297, -5996, -25725, -12999, 16126, + 24907, 2265, -23234, -19422, 8892, 25989, 10297, -18384, + -23873, 756, 24432, 17284, -11669, -25901, -7456, 20394, + 22516, -3768, -25299, -14912, 14287, 25462, 4514, -22129, + -20855, 6729, 25824, 12339, -16712, -24680, -1511, 23564, + 18911, -9599, -26000, -9599, 18911, 23563, -1512, -24680, + -16712, 12339, 25824, 6728, -20855, -22128, 4515, 25462, + 14286, -14913, -25299, -3767, 22516, 20394, -7457, -25901, + -11668, 17284, 24431, 755, -23873, -18384, 10298, 25988, + 8892, -19422, -23234, 2266, 24907, 16125, -13000, -25725, + -5995, 21298, 21722, -5258, -25605, -13649, 15526, 25113, + 3018, -22885, -19916, 8178, 25956, 10987, -17842, -24162}, + { +// Carrier 35 Phase 1 + 18384, 23873, -756, -24432, -17284, 11668, 25901, 7456, + -20394, -22516, 3768, 25299, 14912, -14287, -25462, -4514, + 22129, 20855, -6729, -25824, -12339, 16712, 24680, 1511, + -23564, -18911, 9599, 26000, 9599, -18911, -23563, 1511, + 24680, 16712, -12339, -25824, -6729, 20855, 22128, -4514, + -25462, -14287, 14913, 25299, 3768, -22516, -20394, 7456, + 25901, 11668, -17284, -24431, -756, 23873, 18384, -10298, + -25988, -8892, 19422, 23234, -2266, -24907, -16126, 13000, + 25725, 5995, -21298, -21722, 5257, 25605, 13649, -15526, + -25114, -3018, 22885, 19917, -8178, -25956, -10987, 17842, + 24162, 0, -24163, -17842, 10988, 25956, 8178, -19917, + -22885, 3018, 25114, 15525, -13649, -25604, -5257, 21722, + 21297, -5996, -25725, -12999, 16126, 24907, 2265, -23234, + -19422, 8892, 25989, 10297, -18384, -23873, 756, 24432, + 17284, -11668, -25901, -7456, 20394, 22516, -3768, -25299, + -14912, 14287, 25462, 4514, -22129, -20855, 6729, 25824, + 12339, -16712, -24680, -1511, 23564, 18911, -9599, -26000, + -9599, 18911, 23563, -1512, -24680, -16712, 12339, 25824, + 6729, -20855, -22128, 4515, 25462, 14286, -14913, -25299, + -3767, 22516, 20394, -7457, -25901, -11668, 17284, 24431, + 755, -23873, -18384, 10298, 25988, 8892, -19422, -23234, + 2266, 24907, 16125, -13000, -25725, -5995, 21298, 21722, + -5257, -25605, -13649, 15526, 25113, 3018, -22885, -19916, + 8178, 25956, 10987, -17842, -24162, 0, 24163, 17842, + -10988, -25955, -8177, 19917, 22885, -3018, -25114, -15525, + 13649, 25604, 5257, -21722, -21297, 5996, 25725, 12999, + -16126, -24907, -2265, 23234, 19422, -8892, -25989, -10297}, + { +// Carrier 35 Phase 2 + 26000, 9599, -18911, -23564, 1511, 24680, 16712, -12339, + -25824, -6729, 20855, 22129, -4514, -25462, -14287, 14913, + 25299, 3768, -22516, -20394, 7456, 25901, 11668, -17284, + -24431, -756, 23873, 18384, -10298, -25988, -8892, 19422, + 23234, -2266, -24907, -16126, 13000, 25725, 5995, -21297, + -21722, 5257, 25605, 13649, -15526, -25114, -3018, 22885, + 19917, -8178, -25956, -10987, 17842, 24162, 0, -24163, + -17842, 10988, 25956, 8178, -19917, -22885, 3018, 25114, + 15526, -13649, -25604, -5257, 21722, 21297, -5996, -25725, + -12999, 16126, 24907, 2265, -23234, -19422, 8892, 25989, + 10297, -18384, -23873, 756, 24432, 17284, -11668, -25901, + -7456, 20394, 22516, -3768, -25299, -14912, 14287, 25462, + 4514, -22129, -20855, 6729, 25824, 12339, -16712, -24680, + -1511, 23564, 18911, -9599, -26000, -9599, 18911, 23563, + -1511, -24680, -16712, 12339, 25824, 6729, -20855, -22128, + 4515, 25462, 14287, -14913, -25299, -3767, 22516, 20394, + -7457, -25901, -11668, 17284, 24431, 755, -23873, -18384, + 10298, 25988, 8892, -19422, -23234, 2266, 24907, 16125, + -13000, -25725, -5995, 21298, 21722, -5257, -25605, -13649, + 15526, 25113, 3018, -22885, -19916, 8178, 25956, 10987, + -17842, -24162, 0, 24163, 17842, -10988, -25955, -8177, + 19917, 22885, -3018, -25114, -15525, 13649, 25604, 5257, + -21722, -21297, 5996, 25725, 12999, -16126, -24907, -2265, + 23234, 19422, -8892, -25989, -10297, 18385, 23873, -756, + -24432, -17284, 11669, 25901, 7456, -20395, -22516, 3768, + 25299, 14912, -14287, -25462, -4514, 22129, 20854, -6729, + -25824, -12339, 16712, 24680, 1511, -23564, -18911, 9599}, + { +// Carrier 35 Phase 3 + 18384, -10298, -25989, -8892, 19422, 23234, -2266, -24907, + -16126, 13000, 25725, 5995, -21297, -21722, 5257, 25605, + 13649, -15526, -25114, -3018, 22885, 19917, -8178, -25956, + -10988, 17842, 24163, 0, -24163, -17842, 10988, 25956, + 8178, -19917, -22885, 3018, 25114, 15526, -13649, -25604, + -5257, 21722, 21297, -5996, -25725, -12999, 16126, 24907, + 2265, -23234, -19422, 8892, 25989, 10297, -18384, -23873, + 756, 24432, 17284, -11668, -25901, -7456, 20394, 22516, + -3768, -25299, -14912, 14287, 25462, 4514, -22129, -20855, + 6729, 25824, 12339, -16712, -24680, -1511, 23564, 18911, + -9599, -26000, -9599, 18911, 23563, -1511, -24680, -16712, + 12339, 25824, 6729, -20855, -22128, 4515, 25462, 14287, + -14913, -25299, -3768, 22516, 20394, -7457, -25901, -11668, + 17284, 24431, 755, -23873, -18384, 10298, 25988, 8892, + -19422, -23234, 2266, 24907, 16125, -13000, -25725, -5995, + 21298, 21722, -5257, -25605, -13649, 15526, 25114, 3018, + -22885, -19916, 8178, 25956, 10987, -17842, -24162, 0, + 24163, 17842, -10988, -25955, -8177, 19917, 22885, -3018, + -25114, -15525, 13649, 25604, 5257, -21722, -21297, 5996, + 25725, 12999, -16126, -24907, -2265, 23234, 19422, -8892, + -25989, -10297, 18385, 23873, -756, -24432, -17284, 11669, + 25901, 7456, -20395, -22516, 3768, 25299, 14912, -14287, + -25462, -4514, 22129, 20854, -6729, -25824, -12339, 16712, + 24680, 1511, -23564, -18911, 9599, 26000, 9599, -18911, + -23563, 1512, 24680, 16712, -12339, -25824, -6728, 20855, + 22128, -4515, -25462, -14286, 14913, 25299, 3767, -22516, + -20394, 7457, 25901, 11668, -17284, -24431, -755, 23873}, + },{{ + +// Carrier 36 Phase 0 + 0, 24432, 16712, -12999, -25605, -4514, 22516, 19917, + -8892, -26000, -8892, 19917, 22516, -4514, -25604, -13000, + 16712, 24432, 0, -24431, -16712, 12999, 25605, 4514, + -22516, -19917, 8892, 26000, 8892, -19917, -22516, 4514, + 25604, 13000, -16712, -24432, 0, 24431, 16712, -12999, + -25605, -4514, 22516, 19917, -8892, -26000, -8892, 19917, + 22516, -4514, -25604, -13000, 16712, 24432, 0, -24431, + -16712, 12999, 25605, 4515, -22516, -19917, 8892, 26000, + 8892, -19917, -22516, 4514, 25604, 13000, -16712, -24432, + 0, 24431, 16712, -12999, -25605, -4515, 22516, 19917, + -8892, -26000, -8892, 19916, 22516, -4514, -25604, -13000, + 16712, 24432, 0, -24431, -16712, 12999, 25605, 4515, + -22516, -19917, 8892, 26000, 8892, -19916, -22516, 4514, + 25604, 13000, -16712, -24432, 0, 24431, 16712, -12999, + -25605, -4515, 22516, 19917, -8892, -26000, -8892, 19916, + 22516, -4514, -25604, -13000, 16712, 24432, 0, -24431, + -16712, 12999, 25605, 4515, -22516, -19917, 8892, 26000, + 8892, -19916, -22516, 4514, 25604, 13000, -16712, -24432, + 0, 24431, 16712, -12999, -25605, -4515, 22516, 19917, + -8892, -26000, -8893, 19916, 22516, -4514, -25604, -13000, + 16712, 24432, 0, -24431, -16712, 12999, 25605, 4515, + -22516, -19917, 8891, 26000, 8893, -19916, -22516, 4514, + 25604, 13000, -16712, -24432, 0, 24431, 16712, -12999, + -25605, -4515, 22516, 19917, -8891, -26000, -8893, 19916, + 22516, -4514, -25604, -13000, 16711, 24432, 0, -24431, + -16713, 12999, 25605, 4515, -22516, -19917, 8891, 26000, + 8893, -19916, -22517, 4514, 25604, 13000, -16711, -24432}, + { +// Carrier 36 Phase 1 + 18384, 23564, -2266, -25114, -14913, 14912, 25114, 2266, + -23563, -18384, 10988, 25901, 6729, -21297, -21297, 6729, + 25901, 10988, -18384, -23564, 2265, 25114, 14913, -14912, + -25114, -2266, 23563, 18384, -10988, -25901, -6729, 21297, + 21298, -6729, -25901, -10988, 18384, 23564, -2265, -25114, + -14913, 14912, 25114, 2266, -23563, -18384, 10987, 25901, + 6729, -21297, -21298, 6729, 25901, 10988, -18384, -23564, + 2265, 25114, 14913, -14912, -25114, -2266, 23563, 18384, + -10987, -25901, -6729, 21297, 21298, -6729, -25901, -10988, + 18384, 23564, -2265, -25114, -14913, 14912, 25114, 2266, + -23563, -18384, 10987, 25901, 6729, -21297, -21298, 6729, + 25901, 10988, -18384, -23564, 2265, 25113, 14913, -14912, + -25114, -2266, 23563, 18385, -10987, -25901, -6729, 21297, + 21298, -6728, -25901, -10988, 18384, 23564, -2265, -25113, + -14913, 14912, 25114, 2266, -23563, -18385, 10987, 25901, + 6729, -21297, -21298, 6728, 25901, 10988, -18384, -23564, + 2265, 25113, 14913, -14912, -25114, -2266, 23563, 18385, + -10987, -25901, -6729, 21297, 21298, -6728, -25901, -10988, + 18384, 23564, -2265, -25113, -14913, 14912, 25114, 2266, + -23563, -18385, 10987, 25901, 6729, -21297, -21298, 6728, + 25901, 10988, -18384, -23564, 2265, 25113, 14913, -14912, + -25114, -2266, 23563, 18385, -10987, -25901, -6729, 21297, + 21298, -6728, -25901, -10988, 18384, 23564, -2265, -25113, + -14913, 14912, 25114, 2266, -23563, -18385, 10987, 25901, + 6729, -21297, -21298, 6728, 25901, 10988, -18384, -23564, + 2265, 25113, 14913, -14912, -25114, -2266, 23563, 18385, + -10987, -25901, -6729, 21297, 21298, -6728, -25900, -10988}, + { +// Carrier 36 Phase 2 + 26000, 8892, -19917, -22516, 4514, 25604, 13000, -16712, + -24432, 0, 24431, 16712, -12999, -25605, -4514, 22516, + 19917, -8892, -26000, -8892, 19917, 22516, -4514, -25604, + -13000, 16712, 24432, 0, -24431, -16712, 12999, 25605, + 4514, -22516, -19917, 8892, 26000, 8892, -19917, -22516, + 4514, 25604, 13000, -16712, -24432, 0, 24431, 16712, + -12999, -25605, -4515, 22516, 19917, -8892, -26000, -8892, + 19917, 22516, -4514, -25604, -13000, 16712, 24432, 0, + -24431, -16712, 12999, 25605, 4515, -22516, -19917, 8892, + 26000, 8892, -19916, -22516, 4514, 25604, 13000, -16712, + -24432, 0, 24431, 16712, -12999, -25605, -4515, 22516, + 19917, -8892, -26000, -8892, 19916, 22516, -4514, -25604, + -13000, 16712, 24432, 0, -24431, -16712, 12999, 25605, + 4515, -22516, -19917, 8892, 26000, 8892, -19916, -22516, + 4514, 25604, 13000, -16712, -24432, 0, 24431, 16712, + -12999, -25605, -4515, 22516, 19917, -8892, -26000, -8892, + 19916, 22516, -4514, -25604, -13000, 16712, 24432, 0, + -24431, -16712, 12999, 25605, 4515, -22516, -19917, 8892, + 26000, 8892, -19916, -22516, 4514, 25604, 13000, -16712, + -24432, 0, 24431, 16712, -12999, -25605, -4515, 22516, + 19917, -8891, -26000, -8893, 19916, 22516, -4514, -25604, + -13000, 16712, 24432, 0, -24431, -16712, 12999, 25605, + 4515, -22516, -19917, 8891, 26000, 8893, -19916, -22516, + 4514, 25604, 13000, -16711, -24432, 0, 24431, 16712, + -12999, -25605, -4515, 22516, 19917, -8891, -26000, -8893, + 19916, 22517, -4514, -25604, -13000, 16711, 24432, 0, + -24431, -16713, 12999, 25605, 4515, -22516, -19917, 8891}, + { +// Carrier 36 Phase 3 + 18384, -10988, -25901, -6729, 21297, 21297, -6729, -25901, + -10988, 18384, 23564, -2265, -25114, -14913, 14912, 25114, + 2266, -23563, -18384, 10988, 25901, 6729, -21297, -21298, + 6729, 25901, 10988, -18384, -23564, 2265, 25114, 14913, + -14912, -25114, -2266, 23563, 18384, -10987, -25901, -6729, + 21297, 21298, -6729, -25901, -10988, 18384, 23564, -2265, + -25114, -14913, 14912, 25114, 2266, -23563, -18384, 10987, + 25901, 6729, -21297, -21298, 6729, 25901, 10988, -18384, + -23564, 2265, 25114, 14913, -14912, -25114, -2266, 23563, + 18384, -10987, -25901, -6729, 21297, 21298, -6729, -25901, + -10988, 18384, 23564, -2265, -25113, -14913, 14912, 25114, + 2266, -23563, -18385, 10987, 25901, 6729, -21297, -21298, + 6728, 25901, 10988, -18384, -23564, 2265, 25113, 14913, + -14912, -25114, -2266, 23563, 18385, -10987, -25901, -6729, + 21297, 21298, -6728, -25901, -10988, 18384, 23564, -2265, + -25113, -14913, 14912, 25114, 2266, -23563, -18385, 10987, + 25901, 6729, -21297, -21298, 6728, 25901, 10988, -18384, + -23564, 2265, 25113, 14913, -14912, -25114, -2266, 23563, + 18385, -10987, -25901, -6729, 21297, 21298, -6728, -25901, + -10988, 18384, 23564, -2265, -25113, -14913, 14912, 25114, + 2266, -23563, -18385, 10987, 25901, 6729, -21297, -21298, + 6728, 25901, 10988, -18384, -23564, 2265, 25113, 14913, + -14912, -25114, -2266, 23563, 18385, -10987, -25901, -6729, + 21297, 21298, -6728, -25901, -10988, 18384, 23564, -2265, + -25113, -14913, 14912, 25114, 2266, -23563, -18385, 10987, + 25901, 6729, -21297, -21298, 6728, 25901, 10988, -18384, + -23564, 2265, 25113, 14913, -14912, -25114, -2266, 23563}, + },{{ + +// Carrier 37 Phase 0 + 0, 24680, 15526, -14912, -24907, -756, 24432, 16126, + -14287, -25114, -1511, 24163, 16712, -13649, -25299, -2266, + 23873, 17284, -12999, -25462, -3018, 23563, 17842, -12339, + -25605, -3768, 23234, 18384, -11668, -25725, -4514, 22885, + 18911, -10988, -25824, -5257, 22516, 19422, -10298, -25901, + -5996, 22129, 19917, -9599, -25956, -6729, 21722, 20394, + -8892, -25989, -7456, 21297, 20855, -8178, -26000, -8178, + 20855, 21297, -7456, -25988, -8892, 20394, 21722, -6729, + -25956, -9599, 19917, 22129, -5995, -25901, -10298, 19422, + 22516, -5257, -25824, -10988, 18911, 22885, -4514, -25725, + -11668, 18384, 23234, -3768, -25604, -12339, 17842, 23564, + -3018, -25462, -13000, 17284, 23873, -2265, -25299, -13649, + 16712, 24163, -1511, -25114, -14287, 16126, 24432, -756, + -24907, -14913, 15526, 24680, 0, -24680, -15526, 14912, + 24907, 756, -24431, -16126, 14287, 25114, 1511, -24162, + -16712, 13649, 25299, 2266, -23873, -17284, 12999, 25462, + 3018, -23563, -17842, 12339, 25605, 3768, -23234, -18384, + 11668, 25725, 4514, -22885, -18911, 10987, 25824, 5257, + -22516, -19422, 10297, 25901, 5996, -22128, -19917, 9599, + 25956, 6729, -21722, -20394, 8892, 25989, 7457, -21297, + -20855, 8178, 26000, 8178, -20855, -21298, 7456, 25988, + 8892, -20394, -21722, 6729, 25956, 9599, -19917, -22129, + 5995, 25901, 10298, -19422, -22516, 5257, 25824, 10988, + -18911, -22885, 4514, 25725, 11668, -18384, -23234, 3768, + 25604, 12339, -17842, -23564, 3018, 25462, 13000, -17284, + -23873, 2265, 25299, 13649, -16712, -24163, 1511, 25114, + 14287, -16125, -24432, 756, 24907, 14913, -15525, -24680}, + { +// Carrier 37 Phase 1 + 18384, 23234, -3768, -25604, -12339, 17842, 23564, -3018, + -25462, -13000, 17284, 23873, -2266, -25299, -13649, 16712, + 24163, -1511, -25114, -14287, 16126, 24432, -756, -24907, + -14913, 15526, 24680, 0, -24680, -15526, 14912, 24907, + 756, -24431, -16126, 14287, 25114, 1511, -24163, -16712, + 13649, 25299, 2266, -23873, -17284, 12999, 25462, 3018, + -23563, -17842, 12339, 25605, 3768, -23234, -18384, 11668, + 25725, 4514, -22885, -18911, 10988, 25824, 5257, -22516, + -19422, 10298, 25901, 5996, -22129, -19917, 9599, 25956, + 6729, -21722, -20394, 8892, 25989, 7456, -21297, -20855, + 8178, 26000, 8178, -20855, -21297, 7456, 25988, 8892, + -20394, -21722, 6729, 25956, 9599, -19917, -22129, 5995, + 25901, 10298, -19422, -22516, 5257, 25824, 10988, -18911, + -22885, 4514, 25725, 11668, -18384, -23234, 3768, 25604, + 12339, -17842, -23564, 3018, 25462, 13000, -17284, -23873, + 2265, 25299, 13649, -16712, -24163, 1511, 25114, 14287, + -16126, -24432, 756, 24907, 14913, -15526, -24680, 0, + 24680, 15526, -14912, -24907, -756, 24431, 16126, -14287, + -25114, -1511, 24162, 16712, -13649, -25299, -2266, 23873, + 17284, -12999, -25462, -3018, 23563, 17842, -12339, -25605, + -3768, 23234, 18384, -11668, -25725, -4514, 22885, 18911, + -10987, -25824, -5257, 22516, 19422, -10297, -25901, -5996, + 22128, 19917, -9599, -25956, -6729, 21722, 20394, -8892, + -25989, -7457, 21297, 20855, -8178, -26000, -8178, 20855, + 21298, -7456, -25988, -8892, 20394, 21722, -6729, -25956, + -9599, 19917, 22129, -5995, -25901, -10298, 19422, 22516, + -5257, -25824, -10988, 18911, 22885, -4514, -25725, -11668}, + { +// Carrier 37 Phase 2 + 26000, 8178, -20855, -21297, 7456, 25989, 8892, -20394, + -21722, 6729, 25956, 9599, -19917, -22129, 5995, 25901, + 10298, -19422, -22516, 5257, 25824, 10988, -18911, -22885, + 4514, 25725, 11668, -18384, -23234, 3768, 25604, 12339, + -17842, -23564, 3018, 25462, 13000, -17284, -23873, 2266, + 25299, 13649, -16712, -24163, 1511, 25114, 14287, -16126, + -24432, 756, 24907, 14913, -15526, -24680, 0, 24680, + 15526, -14912, -24907, -756, 24431, 16126, -14287, -25114, + -1511, 24163, 16712, -13649, -25299, -2266, 23873, 17284, + -12999, -25462, -3018, 23563, 17842, -12339, -25605, -3768, + 23234, 18384, -11668, -25725, -4514, 22885, 18911, -10988, + -25824, -5257, 22516, 19422, -10297, -25901, -5996, 22128, + 19917, -9599, -25956, -6729, 21722, 20394, -8892, -25989, + -7456, 21297, 20855, -8178, -26000, -8178, 20855, 21298, + -7456, -25988, -8892, 20394, 21722, -6729, -25956, -9599, + 19917, 22129, -5995, -25901, -10298, 19422, 22516, -5257, + -25824, -10988, 18911, 22885, -4514, -25725, -11668, 18384, + 23234, -3768, -25604, -12339, 17842, 23564, -3018, -25462, + -13000, 17284, 23873, -2265, -25299, -13649, 16712, 24163, + -1511, -25114, -14287, 16126, 24432, -756, -24907, -14913, + 15526, 24680, 0, -24680, -15526, 14912, 24907, 756, + -24431, -16126, 14287, 25114, 1511, -24162, -16712, 13649, + 25299, 2266, -23873, -17284, 12999, 25462, 3018, -23563, + -17842, 12339, 25605, 3768, -23234, -18384, 11668, 25725, + 4515, -22885, -18911, 10987, 25824, 5257, -22516, -19422, + 10297, 25901, 5996, -22128, -19917, 9599, 25956, 6729, + -21722, -20394, 8892, 25989, 7457, -21297, -20855, 8177}, + { +// Carrier 37 Phase 3 + 18384, -11668, -25725, -4514, 22885, 18911, -10988, -25824, + -5257, 22516, 19422, -10298, -25901, -5996, 22129, 19917, + -9599, -25956, -6729, 21722, 20394, -8892, -25989, -7456, + 21297, 20855, -8178, -26000, -8178, 20855, 21297, -7456, + -25989, -8892, 20394, 21722, -6729, -25956, -9599, 19917, + 22129, -5995, -25901, -10298, 19422, 22516, -5257, -25824, + -10988, 18911, 22885, -4514, -25725, -11668, 18384, 23234, + -3768, -25604, -12339, 17842, 23564, -3018, -25462, -13000, + 17284, 23873, -2265, -25299, -13649, 16712, 24163, -1511, + -25114, -14287, 16126, 24432, -756, -24907, -14913, 15526, + 24680, 0, -24680, -15526, 14912, 24907, 756, -24431, + -16126, 14287, 25114, 1511, -24163, -16712, 13649, 25299, + 2266, -23873, -17284, 12999, 25462, 3018, -23563, -17842, + 12339, 25605, 3768, -23234, -18384, 11668, 25725, 4514, + -22885, -18911, 10987, 25824, 5257, -22516, -19422, 10297, + 25901, 5996, -22128, -19917, 9599, 25956, 6729, -21722, + -20394, 8892, 25989, 7456, -21297, -20855, 8178, 26000, + 8178, -20855, -21298, 7456, 25988, 8892, -20394, -21722, + 6729, 25956, 9599, -19917, -22129, 5995, 25901, 10298, + -19422, -22516, 5257, 25824, 10988, -18911, -22885, 4514, + 25725, 11668, -18384, -23234, 3768, 25604, 12339, -17842, + -23564, 3018, 25462, 13000, -17284, -23873, 2265, 25299, + 13649, -16712, -24163, 1511, 25114, 14287, -16126, -24432, + 756, 24907, 14913, -15526, -24680, 0, 24680, 15526, + -14912, -24907, -756, 24431, 16126, -14287, -25114, -1511, + 24162, 16712, -13649, -25299, -2266, 23873, 17284, -12999, + -25462, -3018, 23563, 17842, -12339, -25605, -3768, 23234}, + },{{ + +// Carrier 38 Phase 0 + 0, 24907, 14287, -16712, -23873, 3018, 25605, 11668, + -18911, -22516, 5996, 25956, 8892, -20855, -20855, 8892, + 25956, 5995, -22516, -18911, 11668, 25604, 3018, -23873, + -16712, 14287, 24907, 0, -24907, -14287, 16712, 23873, + -3018, -25605, -11668, 18911, 22516, -5996, -25956, -8892, + 20855, 20855, -8892, -25956, -5995, 22516, 18911, -11668, + -25604, -3018, 23873, 16712, -14287, -24907, 0, 24907, + 14287, -16712, -23873, 3018, 25605, 11668, -18911, -22516, + 5996, 25956, 8892, -20855, -20855, 8892, 25955, 5995, + -22516, -18911, 11668, 25604, 3018, -23873, -16712, 14287, + 24907, 0, -24907, -14287, 16712, 23873, -3018, -25605, + -11668, 18911, 22516, -5996, -25956, -8892, 20855, 20855, + -8892, -25955, -5995, 22516, 18911, -11669, -25604, -3018, + 23873, 16712, -14287, -24907, 0, 24907, 14286, -16712, + -23873, 3018, 25605, 11668, -18911, -22516, 5996, 25956, + 8892, -20855, -20854, 8892, 25955, 5995, -22516, -18911, + 11669, 25604, 3018, -23873, -16712, 14287, 24907, 0, + -24907, -14286, 16712, 23873, -3018, -25605, -11668, 18912, + 22516, -5996, -25956, -8892, 20855, 20854, -8892, -25955, + -5995, 22516, 18911, -11669, -25604, -3017, 23873, 16712, + -14287, -24907, 0, 24907, 14286, -16712, -23873, 3018, + 25605, 11668, -18912, -22516, 5996, 25956, 8892, -20855, + -20854, 8893, 25955, 5995, -22516, -18911, 11669, 25604, + 3017, -23873, -16712, 14287, 24907, 0, -24907, -14286, + 16712, 23873, -3019, -25605, -11668, 18912, 22516, -5996, + -25956, -8891, 20855, 20854, -8893, -25955, -5995, 22516, + 18911, -11669, -25604, -3017, 23873, 16711, -14287, -24907}, + { +// Carrier 38 Phase 1 + 18384, 22885, -5257, -25901, -9599, 20394, 21297, -8178, + -25989, -6729, 22129, 19422, -10988, -25725, -3768, 23564, + 17284, -13649, -25114, -756, 24680, 14912, -16126, -24163, + 2266, 25462, 12339, -18384, -22885, 5257, 25901, 9599, + -20394, -21297, 8178, 25988, 6729, -22129, -19422, 10988, + 25725, 3768, -23564, -17284, 13649, 25114, 756, -24680, + -14912, 16126, 24162, -2266, -25462, -12339, 18384, 22885, + -5257, -25901, -9599, 20394, 21297, -8178, -25988, -6729, + 22129, 19422, -10988, -25725, -3768, 23564, 17284, -13649, + -25114, -755, 24680, 14912, -16126, -24162, 2266, 25462, + 12339, -18384, -22885, 5257, 25901, 9599, -20394, -21297, + 8178, 25988, 6729, -22129, -19422, 10988, 25725, 3767, + -23564, -17284, 13649, 25113, 755, -24680, -14912, 16126, + 24162, -2266, -25462, -12339, 18385, 22885, -5257, -25901, + -9599, 20395, 21297, -8178, -25988, -6728, 22129, 19422, + -10988, -25725, -3767, 23564, 17284, -13649, -25113, -755, + 24680, 14912, -16126, -24162, 2266, 25462, 12339, -18385, + -22885, 5258, 25901, 9598, -20395, -21297, 8178, 25988, + 6728, -22129, -19422, 10988, 25725, 3767, -23564, -17284, + 13649, 25113, 755, -24680, -14912, 16126, 24162, -2266, + -25462, -12339, 18385, 22884, -5258, -25901, -9598, 20395, + 21297, -8178, -25988, -6728, 22129, 19422, -10988, -25725, + -3767, 23564, 17284, -13649, -25113, -755, 24680, 14912, + -16126, -24162, 2266, 25462, 12339, -18385, -22884, 5258, + 25901, 9598, -20395, -21297, 8178, 25988, 6728, -22129, + -19422, 10988, 25725, 3767, -23564, -17284, 13649, 25113, + 755, -24680, -14912, 16126, 24162, -2266, -25462, -12339}, + { +// Carrier 38 Phase 2 + 26000, 7456, -21722, -19917, 10298, 25824, 4514, -23234, + -17842, 13000, 25299, 1511, -24432, -15526, 15526, 24431, + -1511, -25299, -12999, 17842, 23234, -4514, -25824, -10298, + 19917, 21722, -7456, -26000, -7456, 21722, 19917, -10298, + -25824, -4514, 23234, 17842, -13000, -25299, -1511, 24432, + 15526, -15526, -24431, 1511, 25299, 12999, -17842, -23234, + 4514, 25824, 10297, -19917, -21722, 7457, 26000, 7456, + -21722, -19917, 10298, 25824, 4514, -23234, -17842, 13000, + 25299, 1511, -24432, -15525, 15526, 24431, -1511, -25299, + -12999, 17842, 23234, -4515, -25824, -10297, 19917, 21722, + -7457, -26000, -7456, 21722, 19916, -10298, -25824, -4514, + 23234, 17842, -13000, -25299, -1511, 24432, 15525, -15526, + -24431, 1512, 25299, 12999, -17842, -23234, 4515, 25824, + 10297, -19917, -21722, 7457, 26000, 7456, -21722, -19916, + 10298, 25824, 4514, -23234, -17842, 13000, 25299, 1511, + -24432, -15525, 15526, 24431, -1512, -25299, -12999, 17842, + 23234, -4515, -25824, -10297, 19917, 21722, -7457, -26000, + -7456, 21722, 19916, -10298, -25824, -4514, 23234, 17841, + -13000, -25299, -1511, 24432, 15525, -15526, -24431, 1512, + 25299, 12999, -17842, -23234, 4515, 25824, 10297, -19917, + -21722, 7457, 26000, 7456, -21722, -19916, 10298, 25824, + 4514, -23234, -17841, 13000, 25299, 1511, -24432, -15525, + 15526, 24431, -1512, -25299, -12999, 17842, 23234, -4515, + -25824, -10297, 19917, 21722, -7457, -26000, -7456, 21723, + 19916, -10298, -25824, -4514, 23234, 17841, -13000, -25299, + -1511, 24432, 15525, -15526, -24431, 1512, 25299, 12999, + -17842, -23234, 4515, 25824, 10297, -19917, -21722, 7457}, + { +// Carrier 38 Phase 3 + 18384, -12339, -25462, -2266, 24163, 16126, -14913, -24680, + 756, 25114, 13649, -17284, -23563, 3768, 25725, 10988, + -19422, -22129, 6729, 25989, 8178, -21297, -20394, 9599, + 25901, 5257, -22885, -18384, 12339, 25462, 2265, -24163, + -16126, 14913, 24680, -756, -25114, -13649, 17284, 23563, + -3768, -25725, -10987, 19422, 22128, -6729, -25989, -8178, + 21298, 20394, -9599, -25901, -5257, 22885, 18384, -12339, + -25462, -2265, 24163, 16125, -14913, -24680, 756, 25114, + 13649, -17284, -23563, 3768, 25725, 10987, -19422, -22128, + 6729, 25989, 8177, -21298, -20394, 9599, 25901, 5257, + -22885, -18384, 12339, 25462, 2265, -24163, -16125, 14913, + 24680, -756, -25114, -13649, 17284, 23563, -3768, -25725, + -10987, 19422, 22128, -6729, -25989, -8177, 21298, 20394, + -9599, -25901, -5257, 22885, 18384, -12339, -25462, -2265, + 24163, 16125, -14913, -24680, 756, 25114, 13649, -17284, + -23563, 3768, 25725, 10987, -19422, -22128, 6729, 25989, + 8177, -21298, -20394, 9599, 25901, 5257, -22885, -18384, + 12339, 25462, 2265, -24163, -16125, 14913, 24680, -756, + -25114, -13648, 17285, 23563, -3768, -25725, -10987, 19422, + 22128, -6729, -25989, -8177, 21298, 20394, -9599, -25901, + -5257, 22885, 18384, -12340, -25462, -2265, 24163, 16125, + -14913, -24680, 756, 25114, 13648, -17285, -23563, 3768, + 25725, 10987, -19423, -22128, 6729, 25989, 8177, -21298, + -20394, 9599, 25901, 5257, -22885, -18384, 12340, 25462, + 2265, -24163, -16125, 14913, 24680, -756, -25114, -13648, + 17285, 23563, -3768, -25725, -10987, 19423, 22128, -6729, + -25989, -8177, 21298, 20394, -9599, -25901, -5256, 22885}, + },{{ + +// Carrier 39 Phase 0 + 0, 25114, 13000, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25114, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6729, + -22516, -18384, 13000, 25114, 0, -25114, -12999, 18384, + 22516, -6729, -26000, -6729, 22516, 18384, -13000, -25113, + 0, 25114, 12999, -18384, -22516, 6729, 26000, 6728, + -22516, -18384, 13000, 25113, 0, -25114, -12999, 18385, + 22516, -6729, -26000, -6728, 22516, 18384, -13000, -25113, + 0, 25114, 12999, -18385, -22516, 6729, 26000, 6728, + -22516, -18384, 13000, 25113, 0, -25114, -12999, 18385, + 22516, -6729, -26000, -6728, 22516, 18384, -13000, -25113, + 0, 25114, 12999, -18385, -22516, 6729, 26000, 6728, + -22516, -18384, 13000, 25113, 0, -25114, -12999, 18385, + 22516, -6729, -26000, -6728, 22516, 18384, -13000, -25113}, + { +// Carrier 39 Phase 1 + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25114, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25114, 0, 25114, 12999, -18384, -22516, 6729, 26000, + 6729, -22516, -18384, 13000, 25113, 0, -25114, -12999, + 18384, 22516, -6729, -26000, -6729, 22516, 18384, -13000, + -25113, 0, 25114, 12999, -18385, -22516, 6729, 26000, + 6728, -22516, -18384, 13000, 25113, 0, -25114, -12999, + 18385, 22516, -6729, -26000, -6728, 22516, 18384, -13000, + -25113, 0, 25114, 12999, -18385, -22516, 6729, 26000, + 6728, -22516, -18384, 13000, 25113, 0, -25114, -12999, + 18385, 22516, -6729, -26000, -6728, 22516, 18384, -13000, + -25113, 0, 25114, 12999, -18385, -22516, 6729, 26000, + 6728, -22516, -18384, 13000, 25113, 0, -25114, -12999}, + { +// Carrier 39 Phase 2 + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25114, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6729, -22516, -18384, 13000, 25114, 0, -25114, + -12999, 18384, 22516, -6729, -26000, -6729, 22516, 18384, + -13000, -25113, 0, 25114, 12999, -18384, -22516, 6729, + 26000, 6728, -22516, -18384, 13000, 25113, 0, -25114, + -12999, 18385, 22516, -6729, -26000, -6728, 22516, 18384, + -13000, -25113, 0, 25114, 12999, -18385, -22516, 6729, + 26000, 6728, -22516, -18384, 13000, 25113, 0, -25114, + -12999, 18385, 22516, -6729, -26000, -6728, 22516, 18384, + -13000, -25113, 0, 25114, 12999, -18385, -22516, 6729, + 26000, 6728, -22516, -18384, 13000, 25113, 0, -25114, + -12999, 18385, 22516, -6729, -26000, -6728, 22516, 18384, + -13000, -25113, 0, 25114, 12999, -18385, -22516, 6729}, + { +// Carrier 39 Phase 3 + 18384, -12999, -25114, 0, 25114, 13000, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25114, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6729, -22516, -18384, 13000, 25114, 0, + -25114, -12999, 18384, 22516, -6729, -26000, -6729, 22516, + 18384, -13000, -25113, 0, 25114, 12999, -18384, -22516, + 6729, 26000, 6728, -22516, -18384, 13000, 25113, 0, + -25114, -12999, 18385, 22516, -6729, -26000, -6728, 22516, + 18384, -13000, -25113, 0, 25114, 12999, -18385, -22516, + 6729, 26000, 6728, -22516, -18384, 13000, 25113, 0, + -25114, -12999, 18385, 22516, -6729, -26000, -6728, 22516, + 18384, -13000, -25113, 0, 25114, 12999, -18385, -22516, + 6729, 26000, 6728, -22516, -18384, 13000, 25113, 0, + -25114, -12999, 18385, 22516, -6729, -26000, -6728, 22516}, + },{{ + +// Carrier 40 Phase 0 + 0, 25299, 11668, -19917, -20855, 10298, 25605, 1511, + -24907, -12999, 18911, 21722, -8892, -25824, -3018, 24432, + 14287, -17842, -22516, 7456, 25956, 4514, -23873, -15526, + 16712, 23234, -5996, -26000, -5996, 23234, 16712, -15526, + -23873, 4514, 25956, 7456, -22516, -17842, 14287, 24432, + -3018, -25824, -8892, 21722, 18911, -13000, -24907, 1511, + 25605, 10298, -20855, -19917, 11668, 25299, 0, -25299, + -11668, 19917, 20855, -10298, -25604, -1511, 24907, 12999, + -18911, -21722, 8892, 25824, 3018, -24432, -14287, 17842, + 22516, -7456, -25956, -4514, 23873, 15526, -16712, -23234, + 5996, 26000, 5996, -23234, -16712, 15526, 23873, -4514, + -25956, -7456, 22516, 17842, -14287, -24432, 3018, 25824, + 8892, -21722, -18911, 13000, 24907, -1511, -25605, -10298, + 20855, 19917, -11668, -25299, 0, 25299, 11668, -19917, + -20855, 10298, 25604, 1511, -24907, -12999, 18911, 21722, + -8892, -25824, -3018, 24432, 14287, -17842, -22516, 7456, + 25956, 4514, -23873, -15526, 16712, 23234, -5996, -26000, + -5995, 23234, 16712, -15526, -23873, 4514, 25956, 7456, + -22516, -17842, 14287, 24431, -3018, -25824, -8892, 21722, + 18911, -13000, -24907, 1511, 25605, 10298, -20855, -19917, + 11668, 25299, 0, -25299, -11668, 19917, 20855, -10298, + -25604, -1511, 24907, 12999, -18911, -21722, 8892, 25824, + 3018, -24432, -14287, 17842, 22516, -7456, -25956, -4514, + 23873, 15526, -16712, -23234, 5996, 26000, 5995, -23234, + -16712, 15526, 23873, -4514, -25956, -7456, 22516, 17842, + -14287, -24431, 3018, 25824, 8892, -21722, -18911, 13000, + 24907, -1511, -25605, -10298, 20855, 19917, -11668, -25299}, + { +// Carrier 40 Phase 1 + 18384, 22129, -8178, -25901, -3768, 24163, 14912, -17284, + -22885, 6729, 25989, 5257, -23564, -16126, 16126, 23564, + -5257, -25989, -6729, 22885, 17284, -14912, -24163, 3768, + 25901, 8178, -22129, -18384, 13649, 24680, -2266, -25725, + -9599, 21297, 19422, -12339, -25114, 756, 25462, 10988, + -20394, -20394, 10988, 25462, 756, -25114, -12339, 19422, + 21297, -9599, -25725, -2266, 24680, 13649, -18384, -22129, + 8178, 25901, 3768, -24163, -14912, 17284, 22885, -6729, + -25989, -5257, 23564, 16126, -16126, -23564, 5257, 25989, + 6729, -22885, -17284, 14912, 24163, -3768, -25901, -8178, + 22129, 18384, -13649, -24680, 2266, 25725, 9599, -21297, + -19422, 12339, 25114, -756, -25462, -10988, 20394, 20394, + -10988, -25462, -756, 25114, 12339, -19422, -21297, 9599, + 25725, 2266, -24680, -13649, 18384, 22129, -8178, -25901, + -3768, 24163, 14912, -17284, -22885, 6729, 25989, 5257, + -23564, -16126, 16126, 23563, -5257, -25989, -6729, 22885, + 17284, -14912, -24163, 3768, 25901, 8178, -22129, -18384, + 13649, 24680, -2266, -25725, -9599, 21297, 19422, -12339, + -25114, 756, 25462, 10988, -20394, -20394, 10988, 25462, + 756, -25114, -12339, 19422, 21297, -9599, -25725, -2266, + 24680, 13649, -18384, -22129, 8178, 25901, 3768, -24163, + -14912, 17284, 22885, -6729, -25989, -5257, 23564, 16126, + -16126, -23563, 5257, 25989, 6729, -22885, -17284, 14913, + 24163, -3768, -25901, -8178, 22129, 18384, -13649, -24680, + 2266, 25725, 9599, -21297, -19422, 12339, 25114, -756, + -25462, -10988, 20394, 20394, -10988, -25462, -756, 25114, + 12339, -19422, -21297, 9599, 25725, 2266, -24680, -13649}, + { +// Carrier 40 Phase 2 + 26000, 5996, -23234, -16712, 15526, 23873, -4514, -25956, + -7456, 22516, 17842, -14287, -24432, 3018, 25824, 8892, + -21722, -18911, 13000, 24907, -1511, -25605, -10298, 20855, + 19917, -11668, -25299, 0, 25299, 11668, -19917, -20855, + 10298, 25604, 1511, -24907, -12999, 18911, 21722, -8892, + -25824, -3018, 24432, 14287, -17842, -22516, 7456, 25956, + 4514, -23873, -15526, 16712, 23234, -5996, -26000, -5996, + 23234, 16712, -15526, -23873, 4514, 25956, 7456, -22516, + -17842, 14287, 24432, -3018, -25824, -8892, 21722, 18911, + -13000, -24907, 1511, 25605, 10298, -20855, -19917, 11668, + 25299, 0, -25299, -11668, 19917, 20855, -10298, -25604, + -1511, 24907, 12999, -18911, -21722, 8892, 25824, 3018, + -24432, -14287, 17842, 22516, -7456, -25956, -4514, 23873, + 15526, -16712, -23234, 5996, 26000, 5995, -23234, -16712, + 15526, 23873, -4514, -25956, -7456, 22516, 17842, -14287, + -24432, 3018, 25824, 8892, -21722, -18911, 13000, 24907, + -1511, -25605, -10298, 20855, 19917, -11668, -25299, 0, + 25299, 11668, -19917, -20855, 10298, 25604, 1511, -24907, + -12999, 18911, 21722, -8892, -25824, -3018, 24432, 14287, + -17842, -22516, 7456, 25956, 4514, -23873, -15526, 16712, + 23234, -5996, -26000, -5995, 23234, 16712, -15526, -23873, + 4514, 25956, 7456, -22516, -17842, 14287, 24431, -3018, + -25824, -8892, 21722, 18911, -13000, -24907, 1511, 25605, + 10298, -20855, -19917, 11668, 25299, 0, -25299, -11668, + 19917, 20855, -10298, -25604, -1511, 24907, 12999, -18911, + -21722, 8892, 25824, 3018, -24432, -14287, 17842, 22516, + -7456, -25956, -4514, 23873, 15526, -16712, -23234, 5996}, + { +// Carrier 40 Phase 3 + 18384, -13649, -24680, 2266, 25725, 9599, -21297, -19422, + 12339, 25114, -756, -25462, -10988, 20394, 20394, -10988, + -25462, -756, 25114, 12339, -19422, -21297, 9599, 25725, + 2266, -24680, -13649, 18384, 22129, -8178, -25901, -3768, + 24163, 14912, -17284, -22885, 6729, 25989, 5257, -23564, + -16126, 16126, 23564, -5257, -25989, -6729, 22885, 17284, + -14912, -24163, 3768, 25901, 8178, -22129, -18384, 13649, + 24680, -2266, -25725, -9599, 21297, 19422, -12339, -25114, + 756, 25462, 10988, -20394, -20394, 10988, 25462, 756, + -25114, -12339, 19422, 21297, -9599, -25725, -2266, 24680, + 13649, -18384, -22129, 8178, 25901, 3768, -24163, -14912, + 17284, 22885, -6729, -25989, -5257, 23564, 16126, -16126, + -23564, 5257, 25989, 6729, -22885, -17284, 14912, 24163, + -3768, -25901, -8178, 22129, 18384, -13649, -24680, 2266, + 25725, 9599, -21297, -19422, 12339, 25114, -756, -25462, + -10988, 20394, 20394, -10988, -25462, -756, 25114, 12339, + -19422, -21297, 9599, 25725, 2266, -24680, -13649, 18384, + 22129, -8178, -25901, -3768, 24163, 14912, -17284, -22885, + 6729, 25989, 5257, -23564, -16126, 16126, 23563, -5257, + -25989, -6729, 22885, 17284, -14912, -24163, 3768, 25901, + 8178, -22129, -18384, 13649, 24680, -2266, -25725, -9599, + 21297, 19422, -12339, -25114, 756, 25462, 10988, -20394, + -20394, 10988, 25462, 756, -25114, -12339, 19422, 21297, + -9599, -25725, -2266, 24680, 13649, -18384, -22129, 8178, + 25901, 3768, -24163, -14912, 17284, 22885, -6729, -25989, + -5257, 23564, 16126, -16126, -23563, 5257, 25989, 6729, + -22885, -17284, 14913, 24163, -3768, -25901, -8178, 22129}, + },{{ + +// Carrier 41 Phase 0 + 0, 25462, 10298, -21297, -18911, 13649, 24432, -3768, + -25956, -6729, 23234, 16126, -16712, -22885, 7456, 25901, + 3018, -24680, -12999, 19422, 20855, -10988, -25299, 756, + 25605, 9599, -21722, -18384, 14287, 24163, -4514, -25989, + -5995, 23564, 15526, -17284, -22516, 8178, 25824, 2266, + -24907, -12339, 19917, 20394, -11668, -25114, 1511, 25725, + 8892, -22129, -17842, 14913, 23873, -5257, -26000, -5257, + 23873, 14912, -17842, -22129, 8892, 25725, 1511, -25114, + -11668, 20394, 19917, -12339, -24907, 2266, 25824, 8178, + -22516, -17284, 15526, 23563, -5996, -25988, -4514, 24163, + 14287, -18384, -21722, 9599, 25604, 756, -25299, -10987, + 20855, 19422, -13000, -24680, 3018, 25901, 7456, -22885, + -16712, 16126, 23234, -6729, -25956, -3768, 24432, 13649, + -18911, -21297, 10298, 25462, 0, -25462, -10297, 21298, + 18911, -13649, -24431, 3768, 25956, 6729, -23234, -16125, + 16712, 22885, -7457, -25901, -3018, 24680, 12999, -19422, + -20855, 10988, 25299, -756, -25605, -9599, 21722, 18384, + -14287, -24162, 4515, 25989, 5995, -23564, -15525, 17284, + 22516, -8178, -25824, -2265, 24907, 12339, -19917, -20394, + 11668, 25114, -1511, -25725, -8892, 22129, 17842, -14913, + -23873, 5257, 26000, 5257, -23873, -14912, 17842, 22128, + -8892, -25725, -1511, 25114, 11668, -20394, -19916, 12339, + 24907, -2266, -25824, -8177, 22516, 17284, -15526, -23563, + 5996, 25988, 4514, -24163, -14286, 18384, 21722, -9599, + -25604, -755, 25299, 10987, -20855, -19422, 13000, 24680, + -3018, -25901, -7456, 22885, 16712, -16126, -23234, 6729, + 25955, 3767, -24432, -13649, 18911, 21297, -10298, -25462}, + { +// Carrier 41 Phase 1 + 18384, 21722, -9599, -25605, -756, 25299, 10988, -20855, + -19422, 13000, 24680, -3018, -25901, -7456, 22885, 16712, + -16126, -23234, 6729, 25956, 3768, -24432, -13649, 18911, + 21297, -10298, -25462, 0, 25462, 10298, -21297, -18911, + 13649, 24431, -3768, -25956, -6729, 23234, 16126, -16712, + -22885, 7456, 25901, 3018, -24680, -12999, 19422, 20855, + -10988, -25299, 756, 25605, 9599, -21722, -18384, 14287, + 24163, -4514, -25989, -5995, 23564, 15526, -17284, -22516, + 8178, 25824, 2265, -24907, -12339, 19917, 20394, -11668, + -25114, 1511, 25725, 8892, -22129, -17842, 14913, 23873, + -5257, -26000, -5257, 23873, 14912, -17842, -22128, 8892, + 25725, 1511, -25114, -11668, 20394, 19917, -12339, -24907, + 2266, 25824, 8178, -22516, -17284, 15526, 23563, -5996, + -25988, -4514, 24163, 14287, -18384, -21722, 9599, 25604, + 756, -25299, -10987, 20855, 19422, -13000, -24680, 3018, + 25901, 7456, -22885, -16712, 16126, 23234, -6729, -25956, + -3768, 24432, 13649, -18911, -21297, 10298, 25462, 0, + -25462, -10297, 21298, 18911, -13649, -24431, 3768, 25956, + 6729, -23234, -16125, 16712, 22885, -7457, -25901, -3018, + 24680, 12999, -19422, -20855, 10988, 25299, -756, -25605, + -9599, 21722, 18384, -14287, -24162, 4515, 25989, 5995, + -23564, -15525, 17284, 22516, -8178, -25824, -2265, 24907, + 12339, -19917, -20394, 11669, 25113, -1512, -25725, -8892, + 22129, 17842, -14913, -23873, 5257, 26000, 5257, -23873, + -14912, 17842, 22128, -8892, -25725, -1511, 25114, 11668, + -20395, -19916, 12339, 24907, -2266, -25824, -8177, 22516, + 17284, -15526, -23563, 5996, 25988, 4514, -24163, -14286}, + { +// Carrier 41 Phase 2 + 26000, 5257, -23873, -14912, 17842, 22129, -8892, -25725, + -1511, 25114, 11668, -20394, -19917, 12339, 24907, -2266, + -25824, -8178, 22516, 17284, -15526, -23563, 5996, 25989, + 4514, -24163, -14287, 18384, 21722, -9599, -25604, -756, + 25299, 10988, -20855, -19422, 13000, 24680, -3018, -25901, + -7456, 22885, 16712, -16126, -23234, 6729, 25956, 3768, + -24432, -13649, 18911, 21297, -10298, -25462, 0, 25462, + 10297, -21298, -18911, 13649, 24431, -3768, -25956, -6729, + 23234, 16126, -16712, -22885, 7456, 25901, 3018, -24680, + -12999, 19422, 20855, -10988, -25299, 756, 25605, 9599, + -21722, -18384, 14287, 24162, -4514, -25989, -5995, 23564, + 15526, -17284, -22516, 8178, 25824, 2265, -24907, -12339, + 19917, 20394, -11668, -25114, 1511, 25725, 8892, -22129, + -17842, 14913, 23873, -5257, -26000, -5257, 23873, 14912, + -17842, -22128, 8892, 25725, 1511, -25114, -11668, 20394, + 19917, -12339, -24907, 2266, 25824, 8177, -22516, -17284, + 15526, 23563, -5996, -25988, -4514, 24163, 14287, -18384, + -21722, 9599, 25604, 755, -25299, -10987, 20855, 19422, + -13000, -24680, 3018, 25901, 7456, -22885, -16712, 16126, + 23234, -6729, -25955, -3767, 24432, 13649, -18911, -21297, + 10298, 25462, 0, -25462, -10297, 21298, 18911, -13649, + -24431, 3768, 25956, 6729, -23234, -16125, 16712, 22885, + -7457, -25901, -3018, 24680, 12999, -19422, -20855, 10988, + 25299, -756, -25605, -9599, 21722, 18384, -14287, -24162, + 4515, 25989, 5995, -23564, -15525, 17284, 22516, -8178, + -25824, -2265, 24907, 12339, -19917, -20394, 11669, 25113, + -1512, -25725, -8892, 22129, 17842, -14913, -23873, 5257}, + { +// Carrier 41 Phase 3 + 18384, -14287, -24163, 4514, 25989, 5996, -23564, -15526, + 17284, 22516, -8178, -25824, -2266, 24907, 12339, -19917, + -20394, 11668, 25114, -1511, -25725, -8892, 22129, 17842, + -14913, -23873, 5257, 26000, 5257, -23873, -14912, 17842, + 22129, -8892, -25725, -1511, 25114, 11668, -20394, -19917, + 12339, 24907, -2266, -25824, -8178, 22516, 17284, -15526, + -23563, 5996, 25988, 4514, -24163, -14287, 18384, 21722, + -9599, -25604, -756, 25299, 10987, -20855, -19422, 13000, + 24680, -3018, -25901, -7456, 22885, 16712, -16126, -23234, + 6729, 25956, 3768, -24432, -13649, 18911, 21297, -10298, + -25462, 0, 25462, 10297, -21298, -18911, 13649, 24431, + -3768, -25956, -6729, 23234, 16126, -16712, -22885, 7457, + 25901, 3018, -24680, -12999, 19422, 20855, -10988, -25299, + 756, 25605, 9599, -21722, -18384, 14287, 24162, -4515, + -25989, -5995, 23564, 15525, -17284, -22516, 8178, 25824, + 2265, -24907, -12339, 19917, 20394, -11668, -25114, 1511, + 25725, 8892, -22129, -17842, 14913, 23873, -5257, -26000, + -5257, 23873, 14912, -17842, -22128, 8892, 25725, 1511, + -25114, -11668, 20394, 19917, -12339, -24907, 2266, 25824, + 8177, -22516, -17284, 15526, 23563, -5996, -25988, -4514, + 24163, 14287, -18384, -21722, 9599, 25604, 755, -25299, + -10987, 20855, 19422, -13000, -24680, 3018, 25901, 7456, + -22885, -16712, 16126, 23234, -6729, -25955, -3767, 24432, + 13649, -18911, -21297, 10298, 25462, 0, -25462, -10297, + 21298, 18911, -13649, -24431, 3768, 25956, 6728, -23234, + -16125, 16712, 22885, -7457, -25901, -3018, 24680, 12999, + -19422, -20855, 10988, 25299, -756, -25605, -9599, 21722}, + },{{ + +// Carrier 42 Phase 0 + 0, 25605, 8892, -22516, -16712, 16712, 22516, -8892, + -25605, 0, 25605, 8892, -22516, -16712, 16712, 22516, + -8892, -25605, 0, 25605, 8892, -22516, -16712, 16712, + 22516, -8892, -25605, 0, 25605, 8892, -22516, -16712, + 16712, 22516, -8892, -25605, 0, 25605, 8892, -22516, + -16712, 16712, 22516, -8892, -25605, 0, 25605, 8892, + -22516, -16712, 16712, 22516, -8892, -25605, 0, 25605, + 8892, -22516, -16712, 16712, 22516, -8892, -25605, 0, + 25605, 8892, -22516, -16712, 16712, 22516, -8892, -25605, + 0, 25605, 8892, -22516, -16712, 16712, 22516, -8892, + -25605, 0, 25605, 8892, -22516, -16712, 16712, 22516, + -8892, -25605, 0, 25605, 8892, -22516, -16712, 16712, + 22516, -8892, -25605, 0, 25605, 8892, -22516, -16712, + 16712, 22516, -8892, -25605, 0, 25605, 8892, -22516, + -16712, 16712, 22516, -8892, -25605, 0, 25605, 8892, + -22516, -16712, 16712, 22516, -8892, -25605, 0, 25605, + 8892, -22516, -16712, 16712, 22516, -8892, -25605, 0, + 25605, 8892, -22516, -16712, 16712, 22516, -8892, -25605, + 0, 25605, 8892, -22516, -16712, 16712, 22516, -8892, + -25605, 0, 25605, 8892, -22516, -16712, 16712, 22516, + -8892, -25605, 0, 25605, 8892, -22516, -16712, 16712, + 22516, -8892, -25605, 0, 25605, 8892, -22516, -16712, + 16712, 22516, -8892, -25605, 0, 25605, 8892, -22516, + -16712, 16712, 22516, -8892, -25605, 0, 25605, 8892, + -22516, -16712, 16712, 22516, -8892, -25605, 0, 25605, + 8892, -22516, -16712, 16712, 22516, -8892, -25605, 0, + 25605, 8892, -22516, -16712, 16712, 22516, -8892, -25605}, + { +// Carrier 42 Phase 1 + 18384, 21297, -10988, -25114, 2266, 25901, 6729, -23564, + -14912, 18384, 21297, -10988, -25114, 2266, 25901, 6729, + -23564, -14912, 18384, 21297, -10988, -25114, 2266, 25901, + 6729, -23564, -14912, 18384, 21297, -10988, -25114, 2266, + 25901, 6729, -23564, -14912, 18384, 21297, -10988, -25114, + 2266, 25901, 6729, -23564, -14912, 18384, 21297, -10988, + -25114, 2266, 25901, 6729, -23564, -14912, 18384, 21297, + -10988, -25114, 2266, 25901, 6729, -23564, -14912, 18384, + 21297, -10988, -25114, 2266, 25901, 6729, -23564, -14912, + 18384, 21297, -10988, -25114, 2266, 25901, 6729, -23564, + -14912, 18384, 21297, -10988, -25114, 2266, 25901, 6729, + -23564, -14912, 18384, 21297, -10988, -25114, 2266, 25901, + 6729, -23564, -14912, 18384, 21297, -10988, -25114, 2266, + 25901, 6729, -23564, -14912, 18384, 21297, -10988, -25114, + 2266, 25901, 6729, -23564, -14912, 18384, 21297, -10988, + -25114, 2266, 25901, 6729, -23564, -14912, 18384, 21297, + -10988, -25114, 2266, 25901, 6729, -23564, -14912, 18384, + 21297, -10988, -25114, 2266, 25901, 6729, -23564, -14912, + 18384, 21297, -10988, -25114, 2266, 25901, 6729, -23564, + -14912, 18384, 21297, -10988, -25114, 2266, 25901, 6729, + -23564, -14912, 18384, 21297, -10988, -25114, 2266, 25901, + 6729, -23564, -14912, 18384, 21297, -10988, -25114, 2266, + 25901, 6729, -23564, -14912, 18384, 21297, -10988, -25114, + 2266, 25901, 6729, -23564, -14912, 18384, 21297, -10988, + -25114, 2266, 25901, 6729, -23564, -14912, 18384, 21297, + -10988, -25114, 2266, 25901, 6729, -23564, -14912, 18384, + 21297, -10988, -25114, 2266, 25901, 6729, -23564, -14912}, + { +// Carrier 42 Phase 2 + 26000, 4514, -24432, -13000, 19917, 19917, -12999, -24432, + 4514, 26000, 4514, -24432, -13000, 19917, 19917, -12999, + -24432, 4514, 26000, 4514, -24432, -13000, 19917, 19917, + -12999, -24432, 4514, 26000, 4514, -24432, -13000, 19917, + 19917, -12999, -24432, 4514, 26000, 4514, -24432, -13000, + 19917, 19917, -12999, -24432, 4514, 26000, 4514, -24432, + -13000, 19917, 19917, -12999, -24432, 4514, 26000, 4514, + -24432, -13000, 19917, 19917, -12999, -24432, 4514, 26000, + 4514, -24432, -13000, 19917, 19917, -12999, -24432, 4514, + 26000, 4514, -24432, -13000, 19917, 19917, -12999, -24432, + 4514, 26000, 4514, -24432, -13000, 19917, 19917, -12999, + -24432, 4514, 26000, 4514, -24432, -13000, 19917, 19917, + -12999, -24432, 4514, 26000, 4514, -24432, -13000, 19917, + 19917, -12999, -24432, 4514, 26000, 4514, -24432, -13000, + 19917, 19917, -12999, -24432, 4514, 26000, 4514, -24432, + -13000, 19917, 19917, -12999, -24432, 4514, 26000, 4514, + -24432, -13000, 19917, 19917, -12999, -24432, 4514, 26000, + 4514, -24432, -13000, 19917, 19917, -12999, -24432, 4514, + 26000, 4514, -24432, -13000, 19917, 19917, -12999, -24432, + 4514, 26000, 4514, -24432, -13000, 19917, 19917, -12999, + -24432, 4514, 26000, 4514, -24432, -13000, 19917, 19917, + -12999, -24432, 4514, 26000, 4514, -24432, -13000, 19917, + 19917, -12999, -24432, 4514, 26000, 4514, -24432, -13000, + 19917, 19917, -12999, -24432, 4514, 26000, 4514, -24432, + -13000, 19917, 19917, -12999, -24432, 4514, 26000, 4514, + -24432, -13000, 19917, 19917, -12999, -24432, 4514, 26000, + 4514, -24432, -13000, 19917, 19917, -12999, -24432, 4514}, + { +// Carrier 42 Phase 3 + 18384, -14912, -23564, 6729, 25901, 2266, -25114, -10988, + 21297, 18384, -14912, -23563, 6729, 25901, 2266, -25114, + -10988, 21297, 18384, -14912, -23563, 6729, 25901, 2266, + -25114, -10988, 21297, 18384, -14912, -23563, 6729, 25901, + 2266, -25114, -10988, 21297, 18384, -14912, -23563, 6729, + 25901, 2266, -25114, -10988, 21297, 18384, -14912, -23563, + 6729, 25901, 2266, -25114, -10988, 21297, 18384, -14912, + -23563, 6729, 25901, 2266, -25114, -10988, 21297, 18384, + -14912, -23563, 6729, 25901, 2266, -25114, -10988, 21297, + 18384, -14912, -23563, 6729, 25901, 2266, -25114, -10988, + 21297, 18384, -14912, -23563, 6729, 25901, 2266, -25114, + -10988, 21297, 18384, -14912, -23563, 6729, 25901, 2266, + -25114, -10988, 21297, 18384, -14912, -23563, 6729, 25901, + 2266, -25114, -10988, 21297, 18384, -14912, -23563, 6729, + 25901, 2266, -25114, -10988, 21297, 18384, -14912, -23563, + 6729, 25901, 2266, -25114, -10988, 21297, 18384, -14912, + -23563, 6729, 25901, 2266, -25114, -10988, 21297, 18384, + -14912, -23563, 6729, 25901, 2266, -25114, -10988, 21297, + 18384, -14912, -23563, 6729, 25901, 2266, -25114, -10988, + 21297, 18384, -14912, -23563, 6729, 25901, 2266, -25114, + -10988, 21297, 18384, -14912, -23563, 6729, 25901, 2266, + -25114, -10988, 21297, 18384, -14912, -23563, 6729, 25901, + 2266, -25114, -10988, 21297, 18384, -14912, -23563, 6729, + 25901, 2266, -25114, -10988, 21297, 18384, -14912, -23563, + 6729, 25901, 2266, -25114, -10988, 21297, 18384, -14912, + -23563, 6729, 25901, 2266, -25114, -10988, 21297, 18384, + -14912, -23563, 6729, 25901, 2266, -25114, -10988, 21297}}}; +*/ diff --git a/audio.c b/audio.c new file mode 100644 index 0000000..cd2fa16 --- /dev/null +++ b/audio.c @@ -0,0 +1,327 @@ + + +// +// This file is part of Dire Wolf, an amateur radio packet TNC. +// +// Copyright (C) 2011, 2012, 2013, 2014, 2015 John Langner, WB2OSZ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// I've extracted the OSS bits from Direwolf's audio.c for use in QtSoundModem + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#ifdef __OpenBSD__ +#include +#else +#include +#endif + +void Debugprintf(const char * format, ...); + +extern int Closing; + +int oss_fd = -1; /* Single device, both directions. */ + +int insize = 0; +short rxbuffer[2400]; // 1200 stereo samples + +int num_channels = 2; /* Should be 1 for mono or 2 for stereo. */ +int samples_per_sec = 12000; /* Audio sampling rate. Typically 11025, 22050, or 44100. */ +int bits_per_sample = 16; /* 8 (unsigned char) or 16 (signed short). */ + +// Originally 40. Version 1.2, try 10 for lower latency. + +#define ONE_BUF_TIME 10 + +static int set_oss_params(int fd); + +#define roundup1k(n) (((n) + 0x3ff) & ~0x3ff) + +static int calcbufsize(int rate, int chans, int bits) +{ + int size1 = (rate * chans * bits / 8 * ONE_BUF_TIME) / 1000; + int size2 = roundup1k(size1); +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + printf("audio_open: calcbufsize (rate=%d, chans=%d, bits=%d) calc size=%d, round up to %d\n", + rate, chans, bits, size1, size2); +#endif + return (size2); +} + + +int oss_audio_open(char * adevice_in, char * adevice_out) +{ + char audio_in_name[30]; + char audio_out_name[30]; + + strcpy(audio_in_name, adevice_in); + strcpy(audio_out_name, adevice_out); + + if (strcmp(audio_in_name, audio_out_name) == 0) + { + printf("Audio device for both receive and transmit: %s \n", audio_in_name); + } + else + { + printf("Audio input device for receive: %s\n", audio_in_name); + printf("Audio out device for transmit: %s\n", audio_out_name); + } + + oss_fd = open(audio_in_name, O_RDWR); + + if (oss_fd < 0) + { + printf("Could not open audio device %s\n", audio_in_name); + return 0; + } + else + printf("OSS fd = %d\n", oss_fd); + + return set_oss_params(oss_fd); +} + + +static int set_oss_params(int fd) +{ + int err; + int devcaps; + int asked_for; + int ossbuf_size_in_bytes; + int frag = (5 << 16) | (11); + + err = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag); + + if (err == -1) + { + perror("Not able to set fragment size"); + // ossbuf_size_in_bytes = 2048; /* pick something reasonable */ + } + + err = ioctl(fd, SNDCTL_DSP_CHANNELS, &num_channels); + if (err == -1) + { + perror("Not able to set audio device number of channels"); + return (0); + } + + asked_for = samples_per_sec; + + err = ioctl(fd, SNDCTL_DSP_SPEED, &samples_per_sec); + if (err == -1) + { + + perror("Not able to set audio device sample rate"); + return (0); + } + + printf("Asked for %d samples/sec but actually using %d.\n", asked_for, samples_per_sec); + + + /* This is actually a bit mask but it happens that */ + /* 0x8 is unsigned 8 bit samples and */ + /* 0x10 is signed 16 bit little endian. */ + + err = ioctl(fd, SNDCTL_DSP_SETFMT, &bits_per_sample); + + if (err == -1) + { + perror("Not able to set audio device sample size"); + return (0); + } + + /* + * Determine capabilities. + */ + err = ioctl(fd, SNDCTL_DSP_GETCAPS, &devcaps); + if (err == -1) + { + perror("Not able to get audio device capabilities"); + // Is this fatal? // return (-1); + } + + + + printf("audio_open(): devcaps = %08x\n", devcaps); + if (devcaps & DSP_CAP_DUPLEX) printf("Full duplex record/playback.\n"); + if (devcaps & DSP_CAP_BATCH) printf("Device has some kind of internal buffers which may cause delays.\n"); + if (devcaps & ~(DSP_CAP_DUPLEX | DSP_CAP_BATCH)) printf("Others...\n"); + + if (!(devcaps & DSP_CAP_DUPLEX)) + { + printf("Audio device does not support full duplex\n"); + // Do we care? // return (-1); + } + + err = ioctl(fd, SNDCTL_DSP_SETDUPLEX, NULL); + if (err == -1) + { + perror("Not able to set audio full duplex mode"); + // Unfortunate but not a disaster. + } + + /* + * Get preferred block size. + * Presumably this will provide the most efficient transfer. + * + * In my particular situation, this turned out to be + * 2816 for 11025 Hz 16 bit mono + * 5568 for 11025 Hz 16 bit stereo + * 11072 for 44100 Hz 16 bit mono + * + * This was long ago under different conditions. + * Should study this again some day. + * + * Your milage may vary. + */ + + + err = ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &ossbuf_size_in_bytes); + if (err == -1) + { + perror("Not able to get audio block size"); + ossbuf_size_in_bytes = 2048; /* pick something reasonable */ + } + + printf("audio_open(): suggestd block size is %d\n", ossbuf_size_in_bytes); + + /* + * That's 1/8 of a second which seems rather long if we want to + * respond quickly. + */ + + ossbuf_size_in_bytes = calcbufsize(samples_per_sec, num_channels, bits_per_sample); + + printf("audio_open(): using block size of %d\n", ossbuf_size_in_bytes); + + /* Version 1.3 - after a report of this situation for Mac OSX version. */ + if (ossbuf_size_in_bytes < 256 || ossbuf_size_in_bytes > 32768) + { + printf("Audio buffer has unexpected extreme size of %d bytes.\n", ossbuf_size_in_bytes); + printf("Detected at %s, line %d.\n", __FILE__, __LINE__); + printf("This might be caused by unusual audio device configuration values.\n"); + ossbuf_size_in_bytes = 2048; + printf("Using %d to attempt recovery.\n", ossbuf_size_in_bytes); + } + return (ossbuf_size_in_bytes); +} + + + + + +int oss_read(short * samples, int nSamples) +{ + int n; + int nBytes = nSamples * 4; + + if (oss_fd < 0) + return 0; + + // printf("audio_get(): read %d\n", nBytes - insize); + + n = read(oss_fd, &rxbuffer[insize], nBytes - insize); + + if (n < 0) + { + perror("Can't read from audio device"); + insize = 0; + + return (0); + } + + insize += n; + + if (n == nSamples * 4) + { + memcpy(samples, rxbuffer, insize); + insize = 0; + return nSamples; + } + + return 0; +} + + +int oss_write(short * ptr, int len) +{ + int k; + +// int delay; +// ioctl(oss_fd, SNDCTL_DSP_GETODELAY, &delay); +// Debugprintf("Delay %d", delay); + + k = write(oss_fd, ptr, len * 4); + +// + if (k < 0) + { + perror("Can't write to audio device"); + return (-1); + } + if (k < len * 4) + { + printf("oss_write(): write %d returns %d\n", len * 4, k); + /* presumably full but didn't block. */ + usleep(10000); + } + ptr += k; + len -= k; + + return 0; +} + +void oss_flush() +{ + int delay; + + if (oss_fd < 0) + { + Debugprintf("OSS Flush Called when OSS closed"); + return; + } + + ioctl(oss_fd, SNDCTL_DSP_GETODELAY, &delay); + Debugprintf("OSS Flush Delay %d", delay); + + while (delay) + { + Sleep(10); + ioctl(oss_fd, SNDCTL_DSP_GETODELAY, &delay); +// Debugprintf("Flush Delay %d", delay); + } +} + +void oss_audio_close(void) +{ + if (oss_fd > 0) + { + close(oss_fd); + oss_fd = -1; + } + return; +} + diff --git a/ax25.c b/ax25.c new file mode 100644 index 0000000..0dfd22c --- /dev/null +++ b/ax25.c @@ -0,0 +1,3294 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" + +#ifdef WIN32 + +__declspec(dllimport) unsigned short __stdcall htons(__in unsigned short hostshort); +__declspec(dllimport) unsigned short __stdcall ntohs(__in unsigned short hostshort); + +#else + +#define strtok_s strtok_r +#include +#endif + +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); + +/* + +unit ax25; + +interface + +uses classes,sysutils,windows; + + procedure get_exclude_list(line: string; var list: TStringList); + procedure get_exclude_frm(line: string; var list: TStringList); + procedure get_monitor_path(path: string; var mycall,corrcall,digi: string); + procedure get_call(src_call: string; var call: string); + procedure get_call_fm_path(path: string; var callto,callfrom: string); + procedure set_corrcall(snd_ch,port: byte; path: string); + procedure set_mycall(snd_ch,port: byte; path: string); + procedure set_digi(snd_ch,port: byte; path: string); + procedure decode_frame(frame: string; var path,data: string; var pid,nr,ns,f_type,f_id: byte; var rpt,pf,cr: boolean); + procedure del_incoming_mycalls(src_call: string); + procedure del_incoming_mycalls_by_sock(socket: integer); + procedure ax25_info_init(snd_ch,port: byte); + procedure write_ax25_info(snd_ch,port: byte); + procedure clr_frm_win(snd_ch,port: byte); + procedure ax25_init; + procedure ax25_free; + function dec2hex(value: byte): string; + function get_corrcall(path: string): string; + function get_mycall(path: string): string; + function get_digi(path: string): string; + function is_excluded_call(snd_ch: byte; path: string): boolean; + function is_excluded_frm(snd_ch,f_id: byte; data: string): boolean; + function is_last_digi(path: string): boolean; + function is_digi(snd_ch,port: byte; path: string): boolean; + function is_corrcall(snd_ch,port: byte; path: string): boolean; + function is_mycall(snd_ch,port: byte; path: string): boolean; + function is_correct_path(path: string; pid: byte): boolean; + function direct_addr(path: string): string; + function reverse_addr(path: string): string; + function reverse_digi(path: string): string; + function number_digi(path: string): byte; + function info_pid(pid: byte): string; + function get_fcs(var data: string; len: word): word; + function set_addr(path: string; rpt,cr: boolean): string; + function set_ctrl(nr,ns,f_type,f_id: byte; pf: boolean): byte; + function make_frame(data,path: string; pid,nr,ns,f_type,f_id: byte; rpt,pf,cr: boolean): string; + function get_incoming_socket_by_call(src_call: string): integer; + function add_incoming_mycalls(socket: integer; src_call: string): boolean; + function in_list_incoming_mycall(path: string): boolean; + function get_UTC_time: string; + function parse_NETROM(data: string; f_id: byte): string; + function parse_IP(data: string): string; + function parse_ARP(data: string): string; + function add_raw_frames(snd_ch: byte; frame: string; var buf: TStringList): boolean; + function scrambler(in_buf: string): string; + function my_indexof(var l: TStringList; s: string): integer; + + +const + port_num=32; + PKT_ERR=17; //Minimum packet size, bytes + I_MAX=7; //Maximum number of packets + B_IDX_MAX=256; + ADDR_MAX_LEN=10; + FRAME_FLAG=126; + // Status flags + STAT_NO_LINK=0; + STAT_LINK=1; + STAT_CHK_LINK=2; + STAT_WAIT_ANS=3; + STAT_TRY_LINK=4; + STAT_TRY_UNLINK=5; + // Сmd,Resp,Poll,Final,Digipeater flags + SET_P=TRUE; + SET_F=FALSE; + SET_C=TRUE; + SET_R=FALSE; + SET_NO_RPT=FALSE; + SET_RPT=TRUE; + // Frame ID flags + I_FRM=0; + S_FRM=1; + U_FRM=2; + I_I=0; + S_RR=1; + S_RNR=5; + S_REJ=9; + U_SABM=47; + U_DISC=67; + U_DM=15; + U_UA=99; + U_FRMR=135; + U_UI=3; + // PID flags + PID_X25=$01; // 00000001-CCIT X25 PLP + PID_SEGMENT=$08; // 00001000-Segmentation fragment + PID_TEXNET=$C3; // 11000011-TEXNET Datagram Protocol + PID_LQ=$C4; // 11001000-Link Quality Protocol + PID_APPLETALK=$CA; // 11001010-Appletalk + PID_APPLEARP=$CB; // 11001011-Appletalk ARP + PID_IP=$CC; // 11001100-ARPA Internet Protocol + PID_ARP=$CD; // 11001101-ARPA Address Resolution Protocol + PID_NET_ROM=$CF; // 11001111-NET/ROM +*/ + +#define ADDR_MAX_LEN 10 +#define PID_NO_L3 0xF0; // 11110000-No Level 3 Protocol + + +unsigned short CRCTable[256] = { + 0, 4489, 8978, 12955, 17956, 22445, 25910, 29887, + 35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735, + 4225, 264, 13203, 8730, 22181, 18220, 30135, 25662, + 40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510, + 8450, 12427, 528, 5017, 26406, 30383, 17460, 21949, + 44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797, + 12675, 8202, 4753, 792, 30631, 26158, 21685, 17724, + 48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572, + 16900, 21389, 24854, 28831, 1056, 5545, 10034, 14011, + 52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859, + 21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786, + 57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634, + 25350, 29327, 16404, 20893, 9506, 13483, 1584, 6073, + 61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921, + 29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848, + 65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696, + 33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623, + 2112, 6601, 11090, 15067, 20068, 24557, 28022, 31999, + 38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398, + 6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774, + 42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685, + 10562, 14539, 2640, 7129, 28518, 32495, 19572, 24061, + 46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460, + 14787, 10314, 6865, 2904, 32743, 28270, 23797, 19836, + 50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747, + 19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123, + 54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522, + 23237, 19276, 31191, 26718, 7393, 3432, 16371, 11898, + 59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809, + 27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185, + 63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584, + 31687, 27214, 22741, 18780, 15843, 11370, 7921, 3960 }; + + +unsigned short pkt_raw_min_len = 8; +int stat_r_mem = 0; + +extern struct TKISSMode_t KISS; + +TAX25Port AX25Port[4][port_num]; + +TStringList KISS_acked[4]; +TStringList KISS_iacked[4]; + +// I think this is the queue for a soundcard channel, so can only be +// two of them (L and R) + +TStringList all_frame_buf[5]; + +typedef struct registeredCalls_t +{ + UCHAR myCall[7]; // call in ax.25 + void * socket; + +} registeredCalls; + + +TStringList list_incoming_mycalls; // list strings containing a registered call + + +boolean busy = 0; +boolean dcd[5] = { 0 ,0 ,0, 0 }; + +boolean tx = 0; + +int stdtones = 0; +int fullduplex = 0; + +UCHAR diddles = 0; +struct TQPSK_t qpsk_set[4]; + +word MEMRecovery[5] = { 200,200,200,200 }; +int NonAX25[5] = { 0 }; + +boolean dyn_frack[4] = { FALSE,FALSE,FALSE,FALSE }; +Byte recovery[4] = { 0,0,0,0 }; +Byte users[4] = { 0,0,0,0 }; + +short txtail[5] = { 50, 50, 50, 50, 50 }; +short txdelay[5] = { 400, 400, 400, 400, 400 }; + +short modem_def[5] = { 1, 1, 1, 1, 1 }; + +int emph_db[5] = { 0, 0, 0, 0, 0 }; +UCHAR emph_all[5] = { 0, 0, 0, 0, 0 }; + +boolean KISS_opt[4] = { FALSE, FALSE, FALSE, FALSE }; +int resptime[4] = { 0,0,0,0 }; +int slottime[4] = { 0,0,0,0 }; +int persist[4] = { 100,100,100,100 }; +int fracks[4] = { 0,0,0,0 }; +int frack_time[4] = { 0,0,0,0 }; +int idletime[4] = { 0,0,0,0 }; +int redtime[4] = { 0,0,0,0 }; +int IPOLL[4] = { 30,30,30,30 }; +int maxframe[4] = { 0,0,0,0 }; +int TXFrmMode[4] = { 0,0,0,0 }; +int max_frame_collector[4] = { 0,0,0,0 }; + +char MyDigiCall[4][512] = { "","","","" }; +char exclude_callsigns[4][512] = { "","","","" }; +char exclude_APRS_frm[4][512] = { "","","","" }; + +TStringList list_exclude_callsigns[4]; +TStringList list_exclude_APRS_frm[4]; +TStringList list_digi_callsigns[4]; + +Byte xData[256]; +Byte xEncoded[256]; +Byte xDecoded[256]; + +int frame_count = 0; +int single_frame_count = 0; + +/* + mydigi: string; + //// end of user params + addr: string[70]; + ctrl: byte; + pid: byte=PID_NO_L3; + fcs: word; + data: string; + frame: string; + +implementation + +uses ax25_l2,sm_main; + +*/ + +void scrambler(UCHAR * in_buf, int Len) +{ + integer i; + word sreg; + Byte a = 0, k; + + sreg = 0x1ff; + + for (i = 0; i < Len; i++) + { + for (k = 0; k < 8; k++) + { + + // a: = (a shr 1) or (sreg and 1 shl 7); + + a = (a >> 1) | ((sreg & 1) << 7); + + // sreg: = (sreg shl 4 and $200) xor (sreg shl 8 and $200) or (sreg shr 1); + + + sreg = (((sreg << 4) & 0x200) ^ ((sreg << 8) & 0x200)) | (sreg >> 1); + } + in_buf[i] = in_buf[i] ^ a; + } +} + + +/* +function parse_ARP(data: string): string; + +function get_callsign(data: string): string; +var + i: integer; + s: string; + a: byte; +begin + s:=''; + if length(data)=7 then + begin + for i:=1 to 6 do + begin + a:=ord(data[i]) shr 1; + if (a in [$30..$39,$41..$5A]) then s:=s+chr(a); + end; + a:=ord(data[7]) shr 1 and 15; + if a>0 then s:=s+'-'+inttostr(a); + end + else + begin + if length(data)>0 then + begin + for i:=1 to length(data) do + if i=1 then s:=dec2hex(ord(data[i])) else s:=s+':'+dec2hex(ord(data[i])); + end; + end; + if s<>'' then s:=s+' '; + result:=s; +end; + +function get_IP(data: string): string; +var + i: integer; + s: string; +begin + s:=''; + if length(data)>0 then + begin + for i:=1 to length(data) do + if i=1 then s:=inttostr(ord(data[i])) else s:=s+'.'+inttostr(ord(data[i])); + end; + if s<>'' then s:=s+' '; + result:=s; +end; + +const + opcode: array [0..3] of string = ('ARP Request','ARP Response','RARP Request','RARP Response'); +var + oper: word; + hlen,plen: byte; + sha,spa,tha,tpa: string; + s: string; + i: word; +begin + s:=data; + if length(data)>7 then + begin + hlen:=ord(data[5]); + plen:=ord(data[6]); + oper:=(ord(data[7]) shl 8 or ord(data[8])) and 2; + i:=9; sha:=get_callsign(copy(data,i,hlen)); + i:=i+hlen; spa:=get_ip(copy(data,i,plen)); + i:=i+plen; tha:=get_callsign(copy(data,i,hlen)); + i:=i+hlen; tpa:=get_ip(copy(data,i,plen)); + s:=' [ARP] '+opcode[oper]+' from '+sha+spa+'to '+tha+tpa; + end; + result:=s; +end; + +function parse_NETROM(data: string; f_id: byte): string; + + function deshift_AX25(data: string): string; + var + i: byte; + call: string[6]; + ssid: string[2]; + begin + result:=''; + if length(data)<7 then exit; + for i:=1 to 7 do data[i]:=chr(ord(data[i]) shr 1); + call:=trim(copy(data,1,6)); + ssid:=trim(inttostr(ord(data[7]) and 15)); + if ssid='0' then result:=call else result:=call+'-'+ssid; + end; + + function con_req_info(data: string): string; + var + s_call: string; + d_call: string; + w: byte; + t_o: byte; + begin + result:=''; + if length(data)>14 then + begin + w:=ord(data[1]); + s_call:=deshift_AX25(copy(data,2,7)); + d_call:=deshift_AX25(copy(data,9,7)); + result:=' w='+inttostr(w)+' '+s_call+' at '+d_call; + end; + if length(data)>15 then + begin + t_o:=ord(data[16]); + result:=result+' t/o '+inttostr(t_o); + end; + end; + + function con_ack_info(data: string): string; + var + w: byte; + begin + result:=''; + if length(data)>0 then + begin + w:=ord(data[1]); + result:=' w='+inttostr(w); + end; + end; + +const + opcode_arr: array[0..7] of string = ('PE','CON REQ','CON ACK','DISC REQ','DISQ ACK','INFO','INFO ACK','RST'); +var + s: string; + netrom_header: string; + c_idx: byte; + c_ID: byte; + TX_nr: byte; + RX_nr: byte; + opcode: byte; + s_call: string; + s_node: string; + d_call: string; + d_node: string; + b_call: string; + r_s_nr: string; + opc_flags: string; + quality: byte; + ttl: byte; + hops: byte; + rtt: word; + inp3_nr_field: byte; + inp3_field_len: byte; + inp3_ext_fields: boolean; +begin + s:=data; + if length(data)>0 then + begin + if data[1]=#$FF then + begin + delete(data,1,1); + //Nodes broadcasting + if (f_id=U_UI) and (length(data)>5) then + begin + s_node:=copy(data,1,6); + delete(data,1,6); + s:='NODES broadcast from '+s_node+#13#10; + while length(data)>20 do + begin + d_call:=deshift_AX25(copy(data,1,7)); + d_node:=copy(data,8,6); + b_call:=deshift_AX25(copy(data,14,7)); + quality:=ord(data[21]); + delete(data,1,21); + s:=s+' '+d_node+':'+d_call+' via '+b_call+' Q='+inttostr(quality)+#13#10; + end; + end; + // INP3 RIF + if (f_id=I_I) and (length(data)>10) then + begin + s:='[INP3 RIF]'+#13#10; + while length(data)>10 do + begin + d_call:=deshift_AX25(copy(data,1,7)); + hops:=ord(data[8]); + rtt:=(ord(data[9]) shl 8) or ord(data[10]); + delete(data,1,10); + inp3_ext_fields:=TRUE; + inp3_nr_field:=0; + while (length(data)>0) and inp3_ext_fields do + begin + inp3_field_len:=ord(data[1]); + if inp3_field_len>0 then + begin + if (inp3_nr_field=0) and (length(data)>1) then + begin + if data[2]=#0 then d_call:=copy(data,3,inp3_field_len-2)+':'+d_call; // Copy alias + end; + delete(data,1,inp3_field_len); + inc(inp3_nr_field); + end + else inp3_ext_fields:=FALSE; + end; + delete(data,1,1); + s:=s+d_call+' hops='+inttostr(hops)+' rtt='+inttostr(rtt)+#13#10; + end; + end; + end + else + begin + // NETROM frames + if length(data)>19 then + begin + s_call:=deshift_AX25(copy(data,1,7)); + d_call:=deshift_AX25(copy(data,8,7)); + ttl:=ord(data[15]); + netrom_header:=copy(data,16,5); + delete(data,1,20); + c_idx:=ord(netrom_header[1]); + c_ID:=ord(netrom_header[2]); + TX_nr:=ord(netrom_header[3]); + RX_nr:=ord(netrom_header[4]); + opcode:=ord(netrom_header[5]); + // Opcode flags + opc_flags:=''; + if opcode and 128 = 128 then opc_flags:=opc_flags+' C'; + if opcode and 64 = 64 then opc_flags:=opc_flags+' N'; + // + s:=' [NETROM] '+s_call+' to '+d_call+' ttl='+inttostr(ttl)+' cct='+dec2hex(c_idx)+dec2hex(c_ID); + r_s_nr:=' S'+inttostr(TX_nr)+' R'+inttostr(RX_nr); + case (opcode and 7) of + 0 : s:=s+' <'+opcode_arr[opcode and 7]+r_s_nr+'>'+#13#10+data; + 1 : s:=s+' <'+opcode_arr[opcode and 7]+'>'+con_req_info(data); + 2 : s:=s+' <'+opcode_arr[opcode and 7]+'>'+con_ack_info(data)+' my cct='+dec2hex(TX_nr)+dec2hex(RX_nr); + 3 : s:=s+' <'+opcode_arr[opcode and 7]+'>'; + 4 : s:=s+' <'+opcode_arr[opcode and 7]+'>'; + 5 : s:=s+' <'+opcode_arr[opcode and 7]+r_s_nr+'>:'+#13#10+data; + 6 : s:=s+' <'+opcode_arr[opcode and 7]+' R'+inttostr(RX_nr)+'>'+opc_flags; + 7 : s:=s+' <'+opcode_arr[opcode and 7]+r_s_nr+'>'+#13#10+data; + end; + end; + end; + end; + result:=s; +end; + +function parse_IP(data: string): string; + + function parse_ICMP(var data: string): string; + var + ICMP_type: byte; + ICMP_code: byte; + s: string; + begin + result:=''; + if length(data)>3 then + begin + ICMP_type:=ord(data[1]); + ICMP_code:=ord(data[2]); + delete(data,1,4); + s:=' [ICMP] Type='+inttostr(ICMP_type)+' Code='+inttostr(ICMP_code)+#13#10; + result:=s; + end; + end; + + function parse_TCP(var data: string): string; + var + s: string; + src_port: string; + dest_port: string; + wnd: string; + ihl: word; + idl: word; + flags: byte; + seq: string; + ack: string; + s_flags: string; + s_idl: string; + begin + result:=''; + if length(data)>19 then + begin + src_port:=' src_port:'+inttostr((ord(data[1]) shl 8)+ord(data[2])); + dest_port:=' dest_port:'+inttostr((ord(data[3]) shl 8)+ord(data[4])); + seq:=' seq='+dec2hex(ord(data[5]))+dec2hex(ord(data[6]))+dec2hex(ord(data[7]))+dec2hex(ord(data[8])); + ack:=' ack='+dec2hex(ord(data[9]))+dec2hex(ord(data[10]))+dec2hex(ord(data[11]))+dec2hex(ord(data[12])); + ihl:=(ord(data[13]) shr 4)*4; + idl:=length(data)-ihl; + flags:=ord(data[14]); + wnd:=' wnd='+inttostr((ord(data[15]) shl 8)+ord(data[16])); + delete(data,1,ihl); + // + s_flags:=' '; + if (flags and 32)=32 then s_flags:=s_flags+'URG '; + if (flags and 16)=16 then s_flags:=s_flags+'ACK '; + if (flags and 8)=8 then s_flags:=s_flags+'PSH '; + if (flags and 4)=4 then s_flags:=s_flags+'RST '; + if (flags and 2)=2 then s_flags:=s_flags+'SYN '; + if (flags and 1)=1 then s_flags:=s_flags+'FIN '; + // + if idl>0 then s_idl:=' data='+inttostr(idl) else s_idl:=''; + if (flags and 16)<>16 then ack:=''; + // + s:=' [TCP]'+src_port+dest_port+seq+ack+wnd+s_idl+s_flags+#13#10; + result:=s; + end; + end; + + function parse_UDP(var data: string): string; + var + s: string; + src_port: string; + dest_port: string; + idl: word; + len: word; + s_idl: string; + begin + result:=''; + if length(data)>7 then + begin + src_port:=' src_port:'+inttostr((ord(data[1]) shl 8)+ord(data[2])); + dest_port:=' dest_port:'+inttostr((ord(data[3]) shl 8)+ord(data[4])); + len:=(ord(data[5]) shl 8)+ord(data[6]); + idl:=len-8; + delete(data,1,8); + // + if idl>0 then s_idl:=' data='+inttostr(idl) else s_idl:=''; + // + s:=' [UDP]'+src_port+dest_port+' len='+inttostr(len)+s_idl+#13#10; + result:=s; + end; + end; + +const + prot_idx=#1#6#17; + prot_name: array [1..3] of string = ('ICMP','TCP','UDP'); +var + s: string; + src_ip: string; + dest_ip: string; + s_prot: string; + len: string; + c_prot: char; + ttl: string; + offset: string; + ihl: byte; + p: byte; + fragment_offset: word; +begin + s:=data; + if length(data)>19 then + begin + ihl:=(ord(data[1]) and 15)*4; + len:=' len='+inttostr((ord(data[3]) shl 8)+ord(data[4])); + fragment_offset:=((ord(data[7]) shl 8)+ord(data[8])) shl 3; + ttl:=' ttl='+inttostr(ord(data[9])); + c_prot:=data[10]; + src_ip:=' Fm '+inttostr(ord(data[13]))+'.'+inttostr(ord(data[14]))+'.'+inttostr(ord(data[15]))+'.'+inttostr(ord(data[16])); + dest_ip:=' To '+inttostr(ord(data[17]))+'.'+inttostr(ord(data[18]))+'.'+inttostr(ord(data[19]))+'.'+inttostr(ord(data[20])); + delete(data,1,ihl); + // + p:=pos(c_prot,prot_idx); + if p>0 then s_prot:=' prot='+prot_name[p] else s_prot:=' prot=Type'+inttostr(ord(c_prot)); + if fragment_offset>0 then offset:=' offset='+inttostr(fragment_offset) else offset:=''; + s:=' [IP]'+src_ip+dest_ip+s_prot+ttl+len+offset+#13#10; + if fragment_offset=0 then + case p of + 1 : s:=s+parse_ICMP(data); + 2 : s:=s+parse_TCP(data); + 3 : s:=s+parse_UDP(data); + end; + s:=s+data; + end; + result:=s; +end; + +function get_UTC_time: string; +var + st: TSYSTEMTIME; + sec,hour,minute: string; +begin + GetSystemTime(st); + if st.wSecond<10 then sec:='0'+inttostr(st.wSecond) else sec:=inttostr(st.wSecond); + if st.wMinute<10 then minute:='0'+inttostr(st.wMinute) else minute:=inttostr(st.wMinute); + if st.wHour<10 then Hour:='0'+inttostr(st.wHour) else Hour:=inttostr(st.wHour); + result:=hour+':'+minute+':'+sec; +end; + +function dec2hex(value: byte): string; +const + hex='0123456789ABCDEF'; +var + lo,hi: byte; +begin + lo:=value and 15; + hi:=value shr 4; + result:=hex[hi+1]+hex[lo+1]; +end; + +*/ + +unsigned short get_fcs(UCHAR * Data, unsigned short len) +{ + unsigned short i; + unsigned short result; + + result = 0xFFFF; + + if (len == 0) + return result; + + for (i = 0; i < len; i++) + result = (result >> 8) ^ CRCTable[(result ^ Data[i]) & 0xff]; + + + result ^= 0xffff; + + return result; +} + + +unsigned short CRCTAB[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, +0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, +0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, +0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, +0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, +0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, +0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, +0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, +0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, +0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, +0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, +0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, +0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, +0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, +0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, +0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, +0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, +0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, +0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, +0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, +0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, +0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, +0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, +0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, +0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, +0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, +0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +unsigned short int compute_crc(unsigned char *buf, int len) +{ + unsigned short fcs = 0xffff; + int i; + + for (i = 0; i < len; i++) + fcs = (fcs >> 8) ^ CRCTAB[(fcs ^ buf[i]) & 0xff]; + + fcs ^= 0xffff; + + return fcs; +} + +/* + +function info_pid(pid: byte): string; +begin + if ((pid and 48=1) or (pid and 48=2)) then result:='0'; + if pid=204 then result:='1'; + if pid=221 then result:='2'; + if pid=240 then result:='3'; + if pid=255 then result:='4'; +end; + +procedure explode(var a: array of string; border, s: string; p1: word); +var + s2: string; + i: integer; +begin + i:=0; + s2:=s+border; + repeat + a[i]:=copy(s2,0,pos(border,s2)-1); + delete(s2,1,length(a[i]+border)); + inc(i); + until (s2='') or (i=p1); +end; + +function set_addr(path: string; rpt,cr: boolean): string; +const + A_RX=0; + A_TX=1; + C_BIT=64; + H_BIT=64; + RR_BIT=48; +var + cnt,i: byte; + a_call: array [0..1] of string; + a_path: array [0..ADDR_MAX_LEN-1] of string; + calls: array [0..ADDR_MAX_LEN-1] of string; + ssids: array [0..ADDR_MAX_LEN-1] of byte; + addr: string; + n: word; +begin + for i:=0 to ADDR_MAX_LEN-1 do + begin + a_path[i]:=''; + calls[i]:=''; + ssids[i]:=0; + end; + //Разделяем позывные + try explode(a_path,',',path,ADDR_MAX_LEN); except end; + //Разделяем позывные и ssid + cnt:=0; + repeat + a_call[0]:=''; a_call[1]:=''; + try explode(a_call,'-',a_path[cnt],2); except end; + if a_call[0]<>'' then + begin + calls[cnt]:=copy(a_call[0]+' ',1,6); + if a_call[1]='' then a_call[1]:='0'; + try ssids[cnt]:=strtoint(a_call[1]); except ssids[cnt]:=0; end; + inc(cnt); + end; + until (a_call[0]='') or (cnt=ADDR_MAX_LEN); + //Формируем адресную строку + addr:=''; + if cnt>1 then + begin + for i:=0 to cnt-1 do + begin + if (cr=SET_C) and (i=A_RX) then ssids[i]:=ssids[i]+C_BIT; //Добавляем C в CRRSSID1 + if (cr=SET_R) and (i=A_TX) then ssids[i]:=ssids[i]+C_BIT; //Добавляем C в CRRSSID1 + if rpt and (i>A_TX) then + if calls[i]=mydigi then ssids[i]:=ssids[i]+H_BIT; //Добавляем H в HRRSSID1 + ssids[i]:=ssids[i]+RR_BIT; //Добавляем RR в xRRSSID1 + addr:=addr+calls[i]+chr(ssids[i]); + end; + for n:=1 to length(addr) do addr[n]:=chr(ord(addr[n]) shl 1); //сдвигаем на 1 бит октеты + addr[length(addr)]:=chr(ord(addr[length(addr)]) xor 1); //устанавливаем конец адреса + end; + result:=addr; +end; +*/ + +int get_addr(char * Calls, UCHAR * AXCalls) +{ + // CONVERT CALL + OPTIONAL DIGI STRING TO AX25, RETURN + // CONVERTED STRING IN AXCALLS. Return FALSE if invalied + + Byte * axptr = AXCalls; + char * ptr, *Context; + int n = 8; // Max digis + + memset(AXCalls, 0, 70); + + ptr = strtok_s(Calls, " ,", &Context); + + if (ptr == NULL) + return FALSE; + + // First field is Call + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + + ptr = strtok_s(NULL, " ,", &Context); + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + + ptr = strtok_s(NULL, " ,", &Context); + + while (ptr && n--) + { + // NEXT FIELD = COULD BE CALLSIGN, VIA, + + if (memcmp(ptr, "VIA", (int)strlen(ptr)) == 0) + { + } //skip via + else + { + // Convert next digi + + if (ConvToAX25(ptr, axptr) == 0) + return FALSE; + + axptr += 7; + } + + ptr = strtok_s(NULL, " ,", &Context); + } + + axptr[-1] |= 1; // Set end of address + + return axptr - AXCalls; +} + +Byte set_ctrl(Byte nr, Byte ns, Byte f_type, Byte f_id, boolean pf) +{ + Byte pf_bit, ctrl; + + ctrl = 0; + pf_bit = 0; + + if (pf) + pf_bit = 16; + + switch (f_type) + { + case I_FRM: + + ctrl = (nr << 5) + pf_bit + (ns << 1); + break; + + case S_FRM: + + ctrl = (nr << 5) + pf_bit + f_id; + break; + + case U_FRM: + + ctrl = f_id + pf_bit; + } + + return ctrl; +} + +string * make_frame(string * data, Byte * axaddr, Byte pid, Byte nr, Byte ns, Byte f_type, Byte f_id, boolean rpr, boolean pf, boolean cr) +{ + UNUSED(rpr); + + Byte ctrl; + + string * frame = newString(); + int addrlen; + Byte addr[80]; + + unsigned short CRC; + UCHAR CRCString[2]; + + frame->Data[0] = 0; // Lower software expects a kiss control byte here + frame->Length = 1; + + ctrl = set_ctrl(nr, ns, f_type, f_id, pf); + + addrlen = strlen((char *)axaddr); + + memcpy(addr, axaddr, addrlen); + + if (cr) + addr[6] |= 0x80; // Set Command Bit + else + addr[13] |= 0x80; // Set Response Bit + + + switch (f_type) + { + case I_FRM: + + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + stringAdd(frame, (Byte *)&pid, 1); + stringAdd(frame, data->Data, data->Length); + + break; + + + case S_FRM: + + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + + break; + + case U_FRM: + + if (f_id == U_UI) + { + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + stringAdd(frame, (Byte *)&pid, 1); + stringAdd(frame, data->Data, data->Length); + } + else if (f_id == U_FRMR) + { + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + stringAdd(frame, data->Data, data->Length); + } + else + { + stringAdd(frame, addr, addrlen); + stringAdd(frame, (Byte *)&ctrl, 1); + } + } + + CRC = get_fcs(&frame->Data[1], frame->Length - 1); + + CRCString[0] = CRC & 0xff; + CRCString[1] = CRC >> 8; + stringAdd(frame, CRCString, 2); + + return frame; +} + + +int add_raw_frames(int snd_ch, string * frame, TStringList * buf) +{ + string *s_data = newString(); + Byte s_pid, s_nr, s_ns, s_f_type, s_f_id; + Byte s_rpt, s_cr, s_pf; + string *d_data = newString(); + Byte d_pid, d_nr, d_ns, d_f_type, d_f_id; + Byte d_rpt, d_cr, d_pf; + + Byte d_path[80]; + Byte s_path[80]; + + boolean found_I; + int i; + + unsigned char * framecontents; + int Length; + + boolean result = TRUE; + + // Have to be careful as at this point frames have KISS Header and maybe trailer + + if (buf->Count > 0) + { + // Check for duplicate. Ok to just compare as copy will have same header + + if (my_indexof(buf, frame) >= 0) + { + Debugprintf("KISSOptimise discarding duplicate frame"); + return FALSE; + } + + // Need to adjust for KISS bytes + + // Normally one, but ackmode has 3 on front and sizeof(void *) on end + + framecontents = frame->Data; + Length = frame->Length; + + if ((framecontents[0] & 15) == 12) // Ackmode + { + framecontents += 3; + Length -= (3 + sizeof(void *)); + } + else + { + framecontents++; + Length--; + } + + decode_frame(framecontents, Length, s_path, s_data, &s_pid, &s_nr, &s_ns, &s_f_type, &s_f_id, &s_rpt, &s_pf, &s_cr); + + found_I = FALSE; + + // check for multiple RR (r) + + if (s_f_id == S_FRM && s_cr == SET_R) + { + for (i = 0; i < buf->Count; i++) + { + framecontents = buf->Items[i]->Data; + Length = buf->Items[i]->Length; + + if ((framecontents[0] & 15) == 12) // Ackmode + { + framecontents += 3; + Length -= (3 + sizeof(void *)); + } + else + { + framecontents++; + Length--; + } + + decode_frame(framecontents, Length, d_path, d_data, &d_pid, &d_nr, &d_ns, &d_f_type, &d_f_id, &d_rpt, &d_pf, &d_cr); + + if (d_f_id == S_FRM && d_cr == SET_R && strcmp((char *)s_path, (char *)d_path) == 0) + { + Delete(buf, i); + Debugprintf("KISSOptimise discarding unneeded RR(R%d)", d_nr); + + break; + } + } + } + + + // check for RR after I Frame + + if (s_f_id == S_FRM && s_cr == SET_C) + { + for (i = 0; i < buf->Count; i++) + { + framecontents = buf->Items[i]->Data; + Length = buf->Items[i]->Length; + + if ((framecontents[0] & 15) == 12) // Ackmode + { + framecontents += 3; + Length -= (3 + sizeof(void *)); + } + else + { + framecontents++; + Length--; + } + + decode_frame(framecontents, Length, d_path, d_data, &d_pid, &d_nr, &d_ns, &d_f_type, &d_f_id, &d_rpt, &d_pf, &d_cr); + + if (d_f_id == I_FRM && strcmp((char *)s_path, (char *)d_path) == 0) + { + found_I = TRUE; + break; + } + } + + if (found_I) + { + Debugprintf("KISSOptimise discarding unneeded RR(C %d) after I frame", s_nr); + result = FALSE; + } + } + + // check on I + + if (s_f_id == I_FRM) + { + for (i = 0; i < buf->Count; i++) + { + framecontents = buf->Items[i]->Data; + Length = buf->Items[i]->Length; + + if ((framecontents[0] & 15) == 12) // Ackmode + { + framecontents += 3; + Length -= (3 + sizeof(void *)); + } + else + { + framecontents++; + Length--; + } + + decode_frame(framecontents, Length, d_path, d_data, &d_pid, &d_nr, &d_ns, &d_f_type, &d_f_id, &d_rpt, &d_pf, &d_cr); + + if (strcmp((char *)s_path, (char *)d_path) == 0 && d_f_id == S_FRM && d_cr == SET_C) + { + Delete(buf, i); + Debugprintf("KISSOptimise discarding unneeded RR(C %d)", d_nr); + i--; // i was removed + } + } + } + } + + freeString(d_data); + freeString(s_data); + + return result; +} + +//////////////////////// Register incoming callsign //////////////////////////// + +// I think a call should only be registered on one socket (or we won't know where to send +// incoming calls + +boolean add_incoming_mycalls(void * socket, char * src_call) +{ + registeredCalls * reg = malloc(sizeof(struct registeredCalls_t)); + int i = 0; + + // Build a string containing Call and Socket + + ConvToAX25(src_call, reg->myCall); + reg->socket = socket; + + if (list_incoming_mycalls.Count > 0) + { + for (i = 0; i < list_incoming_mycalls.Count; i++) + { + registeredCalls * check = (registeredCalls *)list_incoming_mycalls.Items[i]; + + if (memcmp(check->myCall, reg->myCall, 7) == 0) + { + // Update socket + + check->socket = socket; + return FALSE; + } + } + } + + Add(&list_incoming_mycalls, (string *)reg); + return TRUE; +} + + + +void del_incoming_mycalls(char * src_call) +{ + int i = 0; + Byte axcall[7]; + registeredCalls * reg; + + ConvToAX25(src_call, axcall); + + while (i < list_incoming_mycalls.Count) + { + reg = (registeredCalls *)list_incoming_mycalls.Items[i]; + { + if (memcmp(reg->myCall, axcall, 7) == 0) + { + // cant use Delete as stringlist doesn't contain strings + + TStringList * Q = &list_incoming_mycalls; + int Index = i; + + free(Q->Items[Index]); + + Q->Count--; + + while (Index < Q->Count) + { + Q->Items[Index] = Q->Items[Index + 1]; + Index++; + } + + return; + } + } + i++; + } +} + + +void del_incoming_mycalls_by_sock(void * socket) +{ + int i = 0, snd_ch, port; + registeredCalls * reg; + + while (i < list_incoming_mycalls.Count) + { + reg = (registeredCalls *)list_incoming_mycalls.Items[i]; + { + if (reg->socket == socket) + { + // cant use Delete as stringlist doesn't contain strings + + TStringList * Q = &list_incoming_mycalls; + int Index = i; + + free(Q->Items[Index]); + + Q->Count--; + + while (Index < Q->Count) + { + Q->Items[Index] = Q->Items[Index + 1]; + Index++; + } + + //Delete(&list_incoming_mycalls, i); + } + else + i++; + } + } + + // Should clear all connections on socket + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (port = 0; port < port_num; port++) + { + TAX25Port * AX25Sess = &AX25Port[snd_ch][port]; + + if (AX25Sess->socket == socket) + { + if (AX25Sess->status != STAT_NO_LINK) + { + // Shouldn't we send DM? -0 try it + + set_DM(snd_ch, AX25Sess->ReversePath); + + rst_timer(AX25Sess); + rst_values(AX25Sess); + + AX25Sess->status = STAT_NO_LINK; + } + AX25Sess->socket = 0; + } + } + } +} + + +/* +function get_incoming_socket_by_call(src_call: string): integer; +var + i: integer; + found: boolean; + socket: integer; + call,ssid: string; + a_call: array[0..1] of string; +begin + socket:=-1; + i:=0; + found:=FALSE; + try explode(a_call,'-',src_call,2); except end; + call:=trim(a_call[0]); + if a_call[1]<>'' then ssid:=a_call[1] else ssid:='0'; + if list_incoming_mycalls.Count>0 then + repeat + if (call+'-'+ssid)=list_incoming_mycalls.Strings[i] then + begin socket:=strtoint(list_incoming_mycalls_sock.Strings[i]); found:=TRUE; end; + inc(i); + until found or (i=list_incoming_mycalls.Count); + result:=socket; +end; +*/ + + + +void * in_list_incoming_mycall(Byte * path) +{ + // See if to call is in registered calls list + + int i = 0; + registeredCalls * check; // list_incoming_mycalls contains registeredCalls, not Strings + + while (i < list_incoming_mycalls.Count) + { + check = (registeredCalls *)list_incoming_mycalls.Items[i]; + + if (memcmp(check->myCall, path, 7) == 0) + return check->socket; + + i++; + } + + return NULL; +} + +/* +//////////////////////////////////////////////////////////////////////////////// + +function is_corrcall(snd_ch,port: byte; path: string): boolean; +var + call,ssid: string; +begin + call:=trim(copy(path,8,6)); + ssid:=copy(path,14,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) else ssid:='0'; + call:=call+'-'+ssid; + if call=AX25Sess->corrcall then result:=TRUE else result:=FALSE; +end; + +function is_mycall(snd_ch,port: byte; path: string): boolean; +var + call,ssid: string; +begin + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) else ssid:='0'; + call:=call+'-'+ssid; + if call=AX25Sess->mycall then result:=TRUE else result:=FALSE; +end; + +function is_digi(snd_ch,port: byte; path: string): boolean; +var + digi,call,ssid: string; +begin + digi:=''; + if length(path)>14 then + begin + delete(path,1,14); + repeat + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + delete(path,1,7); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + if path<>'' then digi:=digi+call+'-'+ssid+',' + else digi:=digi+call+'-'+ssid; + until path=''; + end; + if digi=AX25Sess->digi then result:=TRUE else result:=FALSE; +end; +*/ + +// Check if laast digi used + +boolean is_last_digi(Byte *path) +{ + int len = strlen(path); + + if (len == 14) + return TRUE; + + if ((path[len - 1] & 128) == 128) + return TRUE; + + return FALSE; +} + + + +/* +function get_corrcall(path: string): string; +var + call,ssid: string; +begin + call:=trim(copy(path,8,6)); + ssid:=copy(path,14,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) else ssid:='0'; + call:=call+'-'+ssid; + result:=call; +end; + +function get_mycall(path: string): string; +var + call,ssid: string; +begin + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) else ssid:='0'; + call:=call+'-'+ssid; + result:=call; +end; + +function get_digi(path: string): string; +var + digi,call,ssid: string; +begin + digi:=''; + if length(path)>14 then + begin + delete(path,1,14); + repeat + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + delete(path,1,7); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + if path<>'' then digi:=digi+call+'-'+ssid+',' + else digi:=digi+call+'-'+ssid; + until path=''; + end; + result:=digi; +end; +*/ + +boolean is_correct_path(Byte * path, Byte pid) +{ + Byte networks[] = { 6, 7, 8, 0xc4, 0xcc, 0xcd, 0xce, 0xcf, 0xf0 , 0 }; + Byte call[10]; + int i; + + + if (pid == 0 || strchr(networks, pid)) + { + // Validate calls + + // I think checking bottom bit of first 13 bytes is enough + + for (i = 0; i < 13; i++) + { + if ((*(path) & 1)) + return FALSE; + + path++; + } + return TRUE; + } + return FALSE; +} + + +void get_exclude_list(char * line, TStringList * list) +{ + // Convert comma separated list of calls to ax25 format in list + // Convert to 6 chars - SSID is ignored + + string axcall; + + char copy[512]; + + char * ptr, *Context; + + if (line[0] == 0) + return; + + strcpy(copy, line); // copy as strtok messes with it + strcat(copy, ","); + + axcall.Length = 6; + axcall.AllocatedLength = 8; + axcall.Data = malloc(8); + + memset(axcall.Data, 0, 8); + + ptr = strtok_s(copy, " ,", &Context); + + while (ptr) + { + if (ConvToAX25(ptr, axcall.Data) == 0) + return; + + axcall.Data[6] = 0; + + Add(list, duplicateString(&axcall)); + + ptr = strtok_s(NULL, " ,", &Context); + } +} + + + +void get_exclude_frm(char * line, TStringList * list) +{ + /* + + s: string; + p: integer; + n: integer; +begin + list.Clear; + if line='' then exit; + repeat + p:=pos(',',line); + if p>0 then + begin + s:=trim(copy(line,1,p-1)); + if s<>'' then + begin + try n:=strtoint(s); except n:=-1; end; + if n in [0..255] then list.Add(chr(n)); + end; + delete(line,1,p); + end + else + begin + s:=trim(line); + if s<>'' then + begin + try n:=strtoint(s); except n:=-1; end; + if n in [0..255] then list.Add(chr(n)); + end; + end; + until p=0; +end; +*/ +} + +/* + +function is_excluded_call(snd_ch: byte; path: string): boolean; +var + excluded: boolean; + call: string; + ssid: string; +begin + excluded:=FALSE; + if (list_exclude_callsigns[snd_ch].Count>0) and (length(path)>13) then + begin + // Copy sender + call:=trim(copy(path,8,6)); + ssid:=copy(path,14,1); + if ssid<>'' then call:=call+'-'+inttostr((ord(ssid[1]) and 15)); + if list_exclude_callsigns[snd_ch].IndexOf(call)>-1 then excluded:=TRUE; + end; + result:=excluded; +end; + +function is_excluded_frm(snd_ch,f_id: byte; data: string): boolean; +var + excluded: boolean; +begin + excluded:=FALSE; + if list_exclude_APRS_frm[snd_ch].Count>0 then + if f_id=U_UI then + if length(data)>0 then + if list_exclude_APRS_frm[snd_ch].IndexOf(data[1])>=0 then excluded:=TRUE; + result:=excluded; +end; + +procedure set_corrcall(snd_ch,port: byte; path: string); +var + call,ssid: string; +begin + call:=trim(copy(path,8,6)); + ssid:=copy(path,14,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + AX25Sess->corrcall:=call+'-'+ssid; +end; + +procedure set_mycall(snd_ch,port: byte; path: string); +var + call,ssid: string; +begin + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + AX25Sess->mycall:=call+'-'+ssid; +end; + +procedure set_digi(snd_ch,port: byte; path: string); +var + digi,call,ssid: string; +begin + digi:=''; + if length(path)>14 then + begin + delete(path,1,14); + repeat + call:=trim(copy(path,1,6)); + ssid:=copy(path,7,1); + delete(path,1,7); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)) + else ssid:='0'; + if path<>'' then digi:=digi+call+'-'+ssid+',' + else digi:=digi+call+'-'+ssid; + until path=''; + end; + AX25Sess->digi:=digi; +end; + +procedure get_call_fm_path(path: string; var callto,callfrom: string); +var + a_path: array [0..ADDR_MAX_LEN-1] of string; + i: byte; +begin + for i:=0 to ADDR_MAX_LEN-1 do a_path[i]:=''; + try explode(a_path,',',path,ADDR_MAX_LEN); except end; + callto:=a_path[0]; + callfrom:=a_path[1]; +end; + +procedure get_call(src_call: string; var call: string); +var + a_call: array[0..1] of string; + ssid: string; +begin + try explode(a_call,'-',src_call,2); except end; + call:=trim(a_call[0]); + if a_call[1]<>'' then ssid:=trim(a_call[1]) else ssid:='0'; + call:=call+'-'+ssid; +end; +*/ + + + +int is_excluded_call(int snd_ch, unsigned char * path) +{ + string * call = newString(); + int Excluded = FALSE; + + stringAdd(call, &path[7], 6); + + if (list_exclude_callsigns[snd_ch].Count > 0) + if (my_indexof(&list_exclude_callsigns[snd_ch], call) > -1) + Excluded = TRUE; + + freeString(call); + return Excluded; +} + + +int is_excluded_frm(int snd_ch, int f_id, string * data) +{ + if (f_id == U_UI) + if (data->Length > 0) + if (my_indexof(&list_exclude_APRS_frm[snd_ch], data) >= 0) + return TRUE; + + return FALSE; +} + + + +int number_digi(string path) +{ + int n = 0; + + // a_path: array [0..ADDR_MAX_LEN-3] of string; + int i; + + // for i:=0 to ADDR_MAX_LEN-3 do a_path[i]:=''; + // try explode(a_path,',',path,ADDR_MAX_LEN-2); except end; + // for i:=0 to ADDR_MAX_LEN-3 do if a_path[i]<>'' then inc(n); + + return n; +} + + + +get_monitor_path(Byte * path, char * mycall, char * corrcall, char * digi) +{ + Byte * digiptr = digi; + + digi[0] = 0; + + mycall[ConvFromAX25(path, mycall)] = 0; + path += 7; + corrcall[ConvFromAX25(path, corrcall)] = 0; + + while ((path[6] & 1) == 0) // End of call bit + { + if (digi != digiptr) + *(digi++) = ','; + + path += 7; + digi += ConvFromAX25(path, digi); + + if (((path[6] & 128) == 128)) // Digi'd + *(digi++) = '*'; + } + *digi = 0; +} + + +/* + +function reverse_digi(path: string): string; +var + digi: string; + a_path: array [0..ADDR_MAX_LEN-3] of string; + i: word; +begin + digi:=''; + for i:=0 to ADDR_MAX_LEN-3 do a_path[i]:=''; + try explode(a_path,',',path,ADDR_MAX_LEN-2); except end; + for i:=0 to ADDR_MAX_LEN-3 do + if a_path[i]<>'' then + begin + if digi='' then digi:=a_path[i]+digi + else digi:=a_path[i]+','+digi; + end; + result:=digi; +end; + +function direct_addr(path: string): string; +var + s,call,ssid: string; +begin + s:=''; + repeat + call:=copy(path,1,6); + delete(path,1,6); + ssid:=copy(path,1,1); + delete(path,1,1); + if ssid<>'' then ssid:=inttostr((ord(ssid[1]) and 15)); + if s='' then s:=call+'-'+ssid else s:=s+','+call+'-'+ssid; + until path=''; + result:=s; +end; +*/ + +void reverse_addr(Byte * path, Byte * revpath, int Len) +{ + Byte * ptr = path; + Byte * copy = revpath; + int endbit = Len - 1; + int numdigis = (Len - 14) / 7; + int i; + + if (Len < 14) + return; + + Byte digis[57]; // 8 * 7 + null terminator + memset(digis, 0, 57); + Byte * digiptr = digis + 49; // Last Digi + + // remove end of address bit + + path[endbit] &= 0xFE; + + // first reverse dest and origin + + memcpy(copy + 7, ptr, 7); + memcpy(copy, ptr + 7, 7); + + Len -= 14; + ptr += 14; + + for (i = 0; i < numdigis; i++) + { + memcpy(digiptr, ptr, 7); + ptr += 7; + digiptr -= 7; + } + + // Digiptr now points to new first digi + + memcpy(©[14], &digiptr[7], 7 * numdigis); + + path[endbit] |= 1; // restore original end bit + + copy[endbit++] |= 1; + copy[endbit] = 0; // Null terminate + + return; +} + + + +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) +{ + int i; + int addr_end; + Byte ctrl; + Byte * savepath = path; + + i = 0; + addr_end = FALSE; + + *cr = SET_R; + *pf = SET_F; + data->Length = 0; + ctrl = 0; + *pid = 0; + *nr = 0; + *ns = 0; + *f_type = 0; + *f_id = 0; + *rpt = FALSE; + + if (len < PKT_ERR) + return; + + if ((frame[6] & 128) == 128 && (frame[13] & 128) == 0) + *cr = SET_C; + + while (len > i && i < ADDR_MAX_LEN * 7) + { + *path++ = frame[i]; + if ((frame[i] & 1) == 1) + { + addr_end = TRUE; + break; + } + i++; + } + + if (addr_end == 0) + return; + + // clear the c and r bits from address + + savepath[6] &= 0x7f; // Mask + savepath[13] &= 0x7f; // Mask + + *path = 0; // add null terminate + + i++; // Points to ctrl byte + + ctrl = frame[i]; + + if ((ctrl & 16) == 16) + *pf = SET_P; + + if ((ctrl & 1) == 0) // I frame + { + *f_type = I_FRM; + *f_id = I_I; + *nr = (ctrl >> 5); + *ns = (ctrl >> 1) & 7; + } + else + { + // Not I + + *f_type = U_FRM; + + *f_id = ctrl & 239; + + switch (ctrl & 15) + { + case S_RR: + case S_RNR: + case S_REJ: + case S_SREJ: + + *f_type = S_FRM; + } + + if (*f_type == S_FRM) + { + *f_id = ctrl & 15; + *nr = ctrl >> 5; + } + } + + + if (*f_id == I_I || *f_id == U_UI) + { + i++; + *pid = frame[i]; + i++; + if (len > i) + stringAdd(data, &frame[i], len - i - 2); // Exclude FCS + } + else if (*f_id == U_FRMR) + { + *pid = 0; + i++; + if (len > i) + stringAdd(data, &frame[i], len - i - 2); // Exclude FCS + } +} +void ax25_info_init(TAX25Port * AX25Sess) +{ + AX25Sess->info.stat_s_pkt = 0; + AX25Sess->info.stat_s_byte = 0; + AX25Sess->info.stat_r_pkt = 0; + AX25Sess->info.stat_r_byte = 0; + AX25Sess->info.stat_r_fc = 0; + AX25Sess->info.stat_fec_count = 0; + AX25Sess->info.stat_l_r_byte = 0; + AX25Sess->info.stat_l_s_byte = 0; + AX25Sess->info.stat_begin_ses = 0; + AX25Sess->info.stat_end_ses = 0; +} + + +void clr_frm_win(TAX25Port * AX25Sess) +{ + int i; + + for (i = 0; i < 8; i++) + initString(&AX25Sess->frm_win[i]); +} + +void ax25_init() +{ + int snd_ch, port, i; + + for (i = 0; i < 4; i++) + { + initTStringList(&all_frame_buf[i]); + initTStringList(&list_exclude_callsigns[i]); + initTStringList(&list_exclude_APRS_frm[i]); + initTStringList(&list_digi_callsigns[i]); + initTStringList(&KISS_acked[i]); + + get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]); + get_exclude_list(exclude_callsigns[i], &list_exclude_callsigns[i]); + get_exclude_frm(exclude_APRS_frm[i], &list_exclude_APRS_frm[i]); + + } + + initTStringList(&list_incoming_mycalls); +// initTStringList(&list_incoming_mycalls_sock); + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + for (port = 0; port < port_num; port++) + { + TAX25Port * AX25Sess = &AX25Port[snd_ch][port]; + + AX25Sess->hi_vs = 0; + AX25Sess->vs = 0; + AX25Sess->vr = 0; + AX25Sess->PID = PID_NO_L3; + initTStringList(&AX25Sess->in_data_buf); + initString(&AX25Sess->out_data_buf); + AX25Sess->t1 = 0; + AX25Sess->t2 = 0; + AX25Sess->t3 = 0; + AX25Sess->i_lo = 0; + AX25Sess->i_hi = 0; + AX25Sess->n1 = 0; + AX25Sess->n2 = 0; + AX25Sess->status = 0; + AX25Sess->clk_frack = 0; + initTStringList(&AX25Sess->frame_buf); + initTStringList(&AX25Sess->I_frame_buf); + initTStringList(&AX25Sess->frm_collector); + AX25Sess->corrcall[0] = 0; + AX25Sess->mycall[0] = 0; + AX25Sess->digi[0] = 0; + AX25Sess->Path[0] = 0; + AX25Sess->kind[0] = 0; + AX25Sess->socket = NULL; + ax25_info_init(AX25Sess); + clr_frm_win(AX25Sess); + } + } +} +/* + +procedure ax25_free; +var + snd_ch,port,i: byte; +begin + for snd_ch:=1 to 4 do + for port:=0 to port_num-1 do + begin + AX25Sess->in_data_buf.Free; + AX25Sess->frame_buf.Free; + AX25Sess->I_frame_buf.Free; + AX25Sess->frm_collector.Free; + end; + for i:=1 to 4 do + begin + all_frame_buf[i].Free; + list_exclude_callsigns[i].Free; + list_exclude_APRS_frm[i].Free; + list_digi_callsigns[i].Free; + end; + list_incoming_mycalls.Free; + list_incoming_mycalls_sock.Free; +end; +*/ +void write_ax25_info(TAX25Port * AX25Sess) +{} + +/*var + new: boolean; + t: text; + s: string; + time_ses: tdatetime; + time_ses_sec: extended; + call,mycall,spkt,sbyte,rpkt,rbyte,rfc,tcps,rcps,acps,startses,timeses: string; +begin + if stat_log then + begin + time_ses:=AX25Sess->info.stat_end_ses-AX25Sess->info.stat_begin_ses; + time_ses_sec:=time_ses*86400; //время сессии в секундах + if time_ses_sec<1 then exit; + mycall:=copy(AX25Sess->mycall+' ',1,9); + call:=copy(AX25Sess->corrcall+' ',1,9); + spkt:=copy(inttostr(AX25Sess->info.stat_s_pkt)+' ',1,6); + sbyte:=copy(inttostr(AX25Sess->info.stat_s_byte)+' ',1,9); + rpkt:=copy(inttostr(AX25Sess->info.stat_r_pkt)+' ',1,6); + rbyte:=copy(inttostr(AX25Sess->info.stat_r_byte)+' ',1,9); + rfc:=copy(inttostr(AX25Sess->info.stat_r_fc)+' ',1,6); + tcps:=copy(inttostr(round(AX25Sess->info.stat_s_byte/time_ses_sec))+' ',1,5); + rcps:=copy(inttostr(round(AX25Sess->info.stat_r_byte/time_ses_sec))+' ',1,5); + acps:=copy(inttostr(round(AX25Sess->info.stat_s_byte/time_ses_sec+AX25Sess->info.stat_r_byte/time_ses_sec))+' ',1,5); + timeses:=FormatDateTime('hh:mm:ss',time_ses); + startses:=FormatDateTime('dd-mm-yy hh:mm:ss',AX25Sess->info.stat_begin_ses); + s:=mycall+' '+call+' '+spkt+' '+sbyte+' '+rpkt+' '+rbyte+' '+rfc+' '+tcps+' '+rcps+' '+acps+' '+startses+' '+timeses; + assignfile(t,'log.txt'); + if FileSearch('log.txt','')='' then new:=TRUE else new:=FALSE; + if new then + begin + rewrite(t); + writeln(t,'Mycall CorrCall TXPkt TXByte RXPkt RXByte FCPkt TXCPS RXCPS T.CPS Begin session SesTime'); + writeln(t,'-------- --------- ------ --------- ------ --------- ------ ----- ----- ----- ----------------- --------'); + end + else append(t); + if (AX25Sess->info.stat_s_byte>0) or (AX25Sess->info.stat_r_byte>0) then writeln(t,s); + closefile(t); + end; +end; + +end. +*/ + + +/* +Copyright 2001-2018 John Wiseman G8BPQ + +This file is part of LinBPQ/BPQ32. + +LinBPQ/BPQ32 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. + +LinBPQ/BPQ32 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 LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses +*/ + + + +// 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 PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +/* + +DllExport int APIENTRY SetTraceOptions(int mask, int mtxparam, int mcomparam) +{ + + // Sets the tracing options for DecodeFrame. Mask is a bit + // mask of ports to monitor (ie 101 binary will monitor ports + // 1 and 3). MTX enables monitoring on transmitted frames. MCOM + // enables monitoring of protocol control frames (eg SABM, UA, RR), + // as well as info frames. + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + + return (0); +} + +DllExport int APIENTRY SetTraceOptionsEx(int mask, int mtxparam, int mcomparam, int monUIOnly) +{ + + // Sets the tracing options for DecodeFrame. Mask is a bit + // mask of ports to monitor (ie 101 binary will monitor ports + // 1 and 3). MTX enables monitoring on transmitted frames. MCOM + // enables monitoring of protocol control frames (eg SABM, UA, RR), + // as well as info frames. + + + MMASK = mask; + MTX = mtxparam; + MCOM = mcomparam; + MUIONLY = monUIOnly; + + return 0; +} + +*/ + +#define USHORT unsigned short + +UCHAR MCOM = 1; +UCHAR MTX = 1; +ULONG MMASK = 0xF; +UCHAR MUIONLY = 0; + +#define SREJ 0x0D +#define SABME 0x6F +#define XID 0xAF +#define TEST 0xE3 + +#define L4BUSY 0x80 // BNA - DONT SEND ANY MORE +#define L4NAK 0x40 // NEGATIVE RESPONSE FLAG +#define L4MORE 0x20 // MORE DATA FOLLOWS - FRAGMENTATION FLAG + +#define L4CREQ 1 // CONNECT REQUEST +#define L4CACK 2 // CONNECT ACK +#define L4DREQ 3 // DISCONNECT REQUEST +#define L4DACK 4 // DISCONNECT ACK +#define L4INFO 5 // INFORMATION +#define L4IACK 6 // INFORMATION ACK + +#pragma pack(1) + +struct myin_addr { + union { + struct { unsigned char s_b1, s_b2, s_b3, s_b4; } S_un_b; + struct { unsigned short s_w1, s_w2; } S_un_w; + unsigned long addr; + }; +}; + + +typedef struct _IPMSG +{ + // FORMAT OF IP HEADER + // + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + UCHAR VERLEN; // 4 BITS VERSION, 4 BITS LENGTH + UCHAR TOS; // TYPE OF SERVICE + USHORT IPLENGTH; // DATAGRAM LENGTH + USHORT IPID; // IDENTIFICATION + USHORT FRAGWORD; // 3 BITS FLAGS, 13 BITS OFFSET + UCHAR IPTTL; + UCHAR IPPROTOCOL; // HIGHER LEVEL PROTOCOL + USHORT IPCHECKSUM; // HEADER CHECKSUM + struct myin_addr IPSOURCE; + struct myin_addr IPDEST; + + UCHAR Data; + +} IPMSG, *PIPMSG; + + +typedef struct _TCPMSG +{ + + // FORMAT OF TCP HEADER WITHIN AN IP DATAGRAM + + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + USHORT SOURCEPORT; + USHORT DESTPORT; + + ULONG SEQNUM; + ULONG ACKNUM; + + UCHAR TCPCONTROL; // 4 BITS DATA OFFSET 4 RESERVED + UCHAR TCPFLAGS; // (2 RESERVED) URG ACK PSH RST SYN FIN + + USHORT WINDOW; + USHORT CHECKSUM; + USHORT URGPTR; + + +} TCPMSG, *PTCPMSG; + +typedef struct _UDPMSG +{ + + // FORMAT OF UDP HEADER WITHIN AN IP DATAGRAM + + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + USHORT SOURCEPORT; + USHORT DESTPORT; + USHORT LENGTH; + USHORT CHECKSUM; + UCHAR UDPData[0]; + +} UDPMSG, *PUDPMSG; + +// ICMP MESSAGE STRUCTURE + +typedef struct _ICMPMSG +{ + // FORMAT OF ICMP HEADER WITHIN AN IP DATAGRAM + + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + UCHAR ICMPTYPE; + UCHAR ICMPCODE; + USHORT ICMPCHECKSUM; + + USHORT ICMPID; + USHORT ICMPSEQUENCE; + UCHAR ICMPData[0]; + +} ICMPMSG, *PICMPMSG; + + +typedef struct _L3MESSAGE +{ + // + // NETROM LEVEL 3 MESSAGE - WITHOUT L2 INFO + // + UCHAR L3SRCE[7]; // ORIGIN NODE + UCHAR L3DEST[7]; // DEST NODE + UCHAR L3TTL; // TX MONITOR FIELD - TO PREVENT MESSAGE GOING // ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP +// +// NETROM LEVEL 4 DATA +// + UCHAR L4INDEX; // TRANSPORT SESSION INDEX + UCHAR L4ID; // TRANSPORT SESSION ID + UCHAR L4TXNO; // TRANSMIT SEQUENCE NUMBER + UCHAR L4RXNO; // RECEIVE (ACK) SEQ NUMBER + UCHAR L4FLAGS; // FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS + + UCHAR L4DATA[236]; //DATA + +} L3MESSAGE, *PL3MESSAGE; + + +typedef struct _MESSAGE +{ + // BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + + // MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + + union + { /* array named screen */ + UCHAR L2DATA[256]; + struct _L3MESSAGE L3MSG; + }; + + +}MESSAGE, *PMESSAGE; + +#pragma pack() + +char * strlop(char * buf, char delim); +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, unsigned int msglen); +char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen); +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen); +char * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output); + +int CountBits(unsigned long in) +{ + int n = 0; + while (in) + { + if (in & 1) n++; + in >>= 1; + } + return n; +} + +BOOL ConvToAX25(char * callsign, unsigned char * ax25call) +{ + int i; + + memset(ax25call, 0x40, 6); // in case short + ax25call[6] = 0x60; // default SSID + + for (i = 0; i < 7; i++) + { + if (callsign[i] == '-') + { + // + // process ssid and return + // + i = atoi(&callsign[i + 1]); + + if (i < 16) + { + ax25call[6] |= i << 1; + return (TRUE); + } + return (FALSE); + } + + if (callsign[i] == 0 || callsign[i] == 13 || callsign[i] == ' ' || callsign[i] == ',') + { + // + // End of call - no ssid + // + return (TRUE); + } + + ax25call[i] = callsign[i] << 1; + } + + // + // Too many chars + // + + return (FALSE); +} + + +int ConvFromAX25(unsigned char * incall, char * outcall) +{ + int in, out = 0; + unsigned char chr; + + memset(outcall, 0x20, 10); + + for (in = 0; in < 6; in++) + { + chr = incall[in]; + if (chr == 0x40) + break; + chr >>= 1; + outcall[out++] = chr; + } + + chr = incall[6]; // ssid + + if (chr == 0x42) + { + outcall[out++] = '-'; + outcall[out++] = 'T'; + return out; + } + + if (chr == 0x44) + { + outcall[out++] = '-'; + outcall[out++] = 'R'; + return out; + } + + chr >>= 1; + chr &= 15; + + if (chr > 0) + { + outcall[out++] = '-'; + if (chr > 9) + { + chr -= 10; + outcall[out++] = '1'; + } + chr += 48; + outcall[out++] = chr; + } + return (out); +} + + +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, UINT Mask, BOOL APRS, BOOL MCTL); + +int APRSDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, UINT Mask) +{ + return IntDecodeFrame(msg, buffer, Stamp, Mask, TRUE, FALSE); +} + +int myDecodeFrame(MESSAGE * msg, char * buffer, int Stamp) +{ + return IntDecodeFrame(msg, buffer, Stamp, MMASK, FALSE, FALSE); +} + +int IntDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, UINT Mask, BOOL APRS, BOOL MINI) +{ + UCHAR * ptr; + int n; + MESSAGE * ADJBUFFER; + ptrdiff_t Work; + UCHAR CTL; + BOOL PF = 0; + char CRCHAR[3] = " "; + char PFCHAR[3] = " "; + int Port; + int MSGFLAG = 0; //CR and V1 flags + char * Output = buffer; + int HH, MM, SS; + char TR = 'R'; + char From[10], To[10]; + BOOL Info = 0; + BOOL FRMRFLAG = 0; + BOOL XIDFLAG = 0; + BOOL TESTFLAG = 0; + + size_t MsgLen = msg->LENGTH; + + // MINI mode is for Node Listen (remote monitor) Mode. Keep info to minimum +/* +KO6IZ*>K7TMG-1: +/ex +KO6IZ*>K7TMG-1: +b +KO6IZ*>K7TMG-1 (UA) +W0TX*>KC6OAR>KB9KC>ID: +W0TX/R DRC/D W0TX-2/G W0TX-1/B W0TX-7/N +KC6OAR*>ID: +*/ + +// GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED + + n = 8; // MAX DIGIS + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + ptr += 7; + n--; + + if (n == 0) + { + return 0; // Corrupt - no end of address bit + } + } + + // Reached End of digis + + Work = ptr - &msg->ORIGIN[6]; // Work is length of digis + + MsgLen -= Work; + + ADJBUFFER = (MESSAGE *)((UCHAR *)msg + Work); // ADJBUFFER points to CTL, etc. allowing for digis + + CTL = ADJBUFFER->CTL; + + if (CTL & PFBIT) + PF = TRUE; + + CTL &= ~PFBIT; + + if (MUIONLY) + if (CTL != 3) + return 0; + + if ((CTL & 1) == 0 || CTL == FRMR || CTL == 3) + { + } + else + { + if (((CTL & 2) && MINI) == 0) // Want Control (but not super unless MCOM + if (MCOM == 0) + return 0; // Dont do control + } + + + Port = msg->PORT; + + if (Port & 0x80) + { + if (MTX == 0) + return 0; // TRANSMITTED FRAME - SEE IF MTX ON + + TR = 'T'; + } + + Port &= 0x7F; + + if (((1 << (Port - 1)) & Mask) == 0) // Check MMASK + return 0; + + + Stamp = Stamp % 86400; // Secs + HH = (int)(Stamp / 3600); + + Stamp -= HH * 3600; + MM = (int)(Stamp / 60); + + SS = (int)(Stamp - MM * 60); + + // Add Port: if MINI mode and monitoring more than one port + + if (MINI == 0) + Output += sprintf((char *)Output, "%02d:%02d:%02d%c ", HH, MM, SS, TR); + else + if (CountBits(Mask) > 1) + Output += sprintf((char *)Output, "%d:", Port); + + From[ConvFromAX25(msg->ORIGIN, From)] = 0; + To[ConvFromAX25(msg->DEST, To)] = 0; + + Output += sprintf((char *)Output, "%s>%s", From, To); + + // Display any Digi-Peaters + + n = 8; // Max number of digi-peaters + ptr = &msg->ORIGIN[6]; // End of Address bit + + while ((*ptr & 1) == 0) + { + // MORE TO COME + + From[ConvFromAX25(ptr + 1, From)] = 0; + Output += sprintf((char *)Output, ",%s", From); + + ptr += 7; + n--; + + if (n == 0) + break; + + // See if digi actioned - put a * on last actioned + + if (*ptr & 0x80) + { + if (*ptr & 1) // if last address, must need * + *(Output++) = '*'; + else + if ((ptr[7] & 0x80) == 0) // Repeased by next? + *(Output++) = '*'; // No, so need * + } + } + + if (MINI == 0) + Output += sprintf((char *)Output, " Port=%d ", Port); + + // Set up CR and PF + + CRCHAR[0] = 0; + PFCHAR[0] = 0; + + if (msg->DEST[6] & 0x80) + { + if (msg->ORIGIN[6] & 0x80) // Both set, assume V1 + MSGFLAG |= VER1; + else + { + MSGFLAG |= CMDBIT; + CRCHAR[0] = ' '; + CRCHAR[1] = 'C'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'P'; + } + } + } + else + { + if (msg->ORIGIN[6] & 0x80) // Only Origin Set + { + MSGFLAG |= RESP; + CRCHAR[0] = ' '; + CRCHAR[1] = 'R'; + if (PF) // If FP set + { + PFCHAR[0] = ' '; + PFCHAR[1] = 'F'; + } + } + else + MSGFLAG |= VER1; // Neither, assume V1 + } + + if ((CTL & 1) == 0) // I frame + { + int NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S) + int NR = (CTL >> 5) & 7; + + Info = 1; + + if (MINI == 0) + Output += sprintf((char *)Output, "", CRCHAR, PFCHAR, NS, NR); + } + else if (CTL == 3) + { + // Un-numbered Information Frame + + Output += sprintf((char *)Output, "", CRCHAR); + Info = 1; + } + else if (CTL & 2) + { + // UN Numbered + + char SUP[6] = "??"; + + switch (CTL) + { + case SABM: + + strcpy(SUP, "C"); + break; + + case SABME: + + strcpy(SUP, "SABME"); + break; + + case XID: + + strcpy(SUP, "XID"); + XIDFLAG = 1; + break; + + case TEST: + + strcpy(SUP, "TEST"); + TESTFLAG = 1; + break; + + case DISC: + + strcpy(SUP, "D"); + break; + + case DM: + + strcpy(SUP, "DM"); + break; + + case UA: + + strcpy(SUP, "UA"); + break; + + + case FRMR: + + strcpy(SUP, "FRMR"); + FRMRFLAG = 1; + break; + } + + if (MINI) + Output += sprintf((char *)Output, "<%s>", SUP); + else + Output += sprintf((char *)Output, "<%s%s%s>", SUP, CRCHAR, PFCHAR); + } + else + { + // Super + + int NR = (CTL >> 5) & 7; + char SUP[5] = "??"; + + switch (CTL & 0x0F) + { + case RR: + + strcpy(SUP, "RR"); + break; + + case RNR: + + strcpy(SUP, "RNR"); + break; + + case REJ: + + strcpy(SUP, "REJ"); + break; + case SREJ: + + strcpy(SUP, "SREJ"); + break; + } + + Output += sprintf((char *)Output, "<%s%s%s R%d>", SUP, CRCHAR, PFCHAR, NR); + + } + + if (FRMRFLAG) + Output += sprintf((char *)Output, " %02X %02X %02X", ADJBUFFER->PID, ADJBUFFER->L2DATA[0], ADJBUFFER->L2DATA[1]); + + if (XIDFLAG) + { + // Decode and display XID + + UCHAR * ptr = &ADJBUFFER->PID; + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + +// G8BPQ-2>G8BPQ:(XID cmd, p=1) Half-Duplex SREJ modulo-128 I-Field-Length-Rx=256 Window-Size-Rx=32 Ack-Timer=3000 Retries=10 + + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<= 8; + value += *ptr++; + } + switch (Type) + { + case 2: //Bin fields + case 3: + + Output += sprintf((char *)Output, " %d=%x", Type, value); + break; + + case 6: //RX Size + + Output += sprintf((char *)Output, " RX Paclen=%d", value / 8); + break; + + case 8: //RX Window + + Output += sprintf((char *)Output, " RX Window=%d", value); + break; + } + } + } + } + if (Info) + { + // We have an info frame + + switch (ADJBUFFER->PID) + { + case 0xF0: // Normal Data + { + char Infofield[257]; + char * ptr1 = Infofield; + char * ptr2 = ADJBUFFER->L2DATA; + UCHAR C; + size_t len; + + MsgLen = MsgLen - (19 + sizeof(void *)); + + if (MsgLen < 0 || MsgLen > 257) + return 0; // Duff + + while (MsgLen--) + { + C = *(ptr2++); + + if (APRS) + *(ptr1++) = C; // MIC-E needs all chars + else + { + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + + break; + } + case NETROM_PID: + + Output = DISPLAY_NETROM(ADJBUFFER, Output, (int)MsgLen); + break; + + case IP_PID: + + Output += sprintf((char *)Output, " \r"); + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[0], Output, (int)MsgLen); + break; + + case ARP_PID: + + Output = DISPLAYARPDATAGRAM(&ADJBUFFER->L2DATA[0], Output); + break; + + case 8: // Fragmented IP + + n = ADJBUFFER->L2DATA[0]; // Frag Count + + Output += sprintf((char *)Output, "\r", n); + + if (ADJBUFFER->L2DATA[0] & 0x80) // First Frag - Display Header + { + Output = DISPLAYIPDATAGRAM((IPMSG *)&ADJBUFFER->L2DATA[2], Output, (int)MsgLen - 1); + } + + break; + } + } + + if (Output[-1] != 13) + Output += sprintf((char *)Output, "\r"); + + return (int)(Output - buffer); + +} +// Display NET/ROM data + +char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen) +{ + char Alias[7] = ""; + char Dest[10]; + char Node[10]; + UCHAR TTL, Index, ID, TXNO, RXNO, OpCode, Flags, Window; + UCHAR * ptr = &ADJBUFFER->L2DATA[0]; + + if (ADJBUFFER->L2DATA[0] == NODES_SIG) + { + // Display NODES + + + // If an INP3 RIF (type <> UI) decode as such + + if (ADJBUFFER->CTL != 3) // UI + return DisplayINP3RIF(&ADJBUFFER->L2DATA[1], Output, MsgLen - 24); + + memcpy(Alias, ++ptr, 6); + + ptr += 6; + + Output += sprintf((char *)Output, " NODES broadcast from %s\r", Alias); + + MsgLen -= 30; //Header, mnemonic and signature length + + while (MsgLen > 20) // Entries are 21 bytes + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr += 7; + memcpy(Alias, ptr, 6); + ptr += 6; + strlop(Alias, ' '); + Node[ConvFromAX25(ptr, Node)] = 0; + ptr += 7; + + Output += sprintf((char *)Output, " %s:%s via %s qlty=%d\r", Alias, Dest, Node, ptr[0]); + ptr++; + MsgLen -= 21; + } + return Output; + } + + // Display normal NET/ROM transmissions + + Output += sprintf((char *)Output, " NET/ROM\r "); + + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr += 7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr += 7; + + TTL = *(ptr++); + Index = *(ptr++); + ID = *(ptr++); + TXNO = *(ptr++); + RXNO = *(ptr++); + OpCode = Flags = *(ptr++); + + OpCode &= 15; // Remove Flags + + Output += sprintf((char *)Output, "%s to %s ttl %d cct=%02X%02X ", Dest, Node, TTL, Index, ID); + MsgLen -= 20; + + switch (OpCode) + { + case L4CREQ: + + Window = *(ptr++); + Dest[ConvFromAX25(ptr, Dest)] = 0; + ptr += 7; + Node[ConvFromAX25(ptr, Node)] = 0; + ptr += 7; + + Output += sprintf((char *)Output, " w=%d %s at %s", Window, Dest, Node); + + if (MsgLen > 38) // BPQ Extended Params + { + short Timeout = (short)*ptr; + Output += sprintf((char *)Output, " t/o %d", Timeout); + } + + return Output; + + case L4CACK: + + if (Flags & L4BUSY) // BUSY RETURNED + return Output + sprintf((char *)Output, " - BUSY"); + + return Output + sprintf((char *)Output, " w=%d my cct=%02X%02X", ptr[1], TXNO, RXNO); + + case L4DREQ: + + return Output + sprintf((char *)Output, " "); + + case L4DACK: + + return Output + sprintf((char *)Output, " "); + + case L4INFO: + { + char Infofield[257]; + char * ptr1 = Infofield; + UCHAR C; + size_t len; + + Output += sprintf((char *)Output, " ", TXNO, RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + MsgLen = MsgLen - (19 + sizeof(void *)); + + if (MsgLen < 0 || MsgLen > 257) + return Output; // Duff + + while (MsgLen--) + { + C = *(ptr++); + + // Convert to printable + + C &= 0x7F; + + if (C == 13 || C == 10 || C > 31) + *(ptr1++) = C; + } + + len = ptr1 - Infofield; + + Output[0] = ':'; + Output[1] = 13; + memcpy(&Output[2], Infofield, len); + Output += (len + 2); + } + + return Output; + + case L4IACK: + + Output += sprintf((char *)Output, " ", RXNO); + + if (Flags & L4BUSY) + *(Output++) = 'B'; + + if (Flags & L4NAK) + *(Output++) = 'N'; + + if (Flags & L4MORE) + *(Output++) = 'M'; + + return Output; + + + case 0: + + // OPcode zero is used for several things + + if (Index == 0x0c && ID == 0x0c) // IP + { + *(Output++) = 13; + *(Output++) = ' '; + Output = DISPLAYIPDATAGRAM((IPMSG *)ptr, Output, MsgLen); + return Output; + } + + if (Index == 0 && ID == 1) // NRR + { + Output += sprintf((char *)Output, " \r"); + + MsgLen -= 23; + + while (MsgLen > 6) + { + Dest[ConvFromAX25(ptr, Dest)] = 0; + + if (ptr[7] & 0x80) + Output += sprintf((char *)Output, "%s* ", Dest); + else + Output += sprintf((char *)Output, "%s ", Dest); + + ptr += 8; + MsgLen -= 8; + } + + return Output; + } + } + + Output += sprintf((char *)Output, " "); + return Output; +} + +/* + + PUBLIC L3IP +L3IP: +; +; TCP/IP DATAGRAM +; + mov EBX,OFFSET IP_MSG + call NORMSTR +; + INC ESI ; NOW POINTING TO IP HEADER + +*/ +UCHAR * DISPLAYIPDATAGRAM(IPMSG * IP, UCHAR * Output, int MsgLen) +{ + UCHAR * ptr; + USHORT FRAGWORD; + + ptr = (UCHAR *)&IP->IPSOURCE; + Output += sprintf((char *)Output, "%d.%d.%d.%d>", ptr[0], ptr[1], ptr[2], ptr[3]); + + ptr = (UCHAR *)&IP->IPDEST; + Output += sprintf((char *)Output, "%d.%d.%d.%d LEN:%d ", ptr[0], ptr[1], ptr[2], ptr[3], htons(IP->IPLENGTH)); + + FRAGWORD = ntohs(IP->FRAGWORD); + + if (FRAGWORD) + { + // If nonzero, check which bits are set + + //Bit 0: reserved, must be zero + //Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment. + //Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments. + //Fragment Offset: 13 bits + + if (FRAGWORD & (1 << 14)) + Output += sprintf((char *)Output, "DF "); + + if (FRAGWORD & (1 << 13)) + Output += sprintf((char *)Output, "MF "); + + FRAGWORD &= 0xfff; + + if (FRAGWORD) + { + Output += sprintf((char *)Output, "Offset %d ", FRAGWORD * 8); + return Output; // Cant display proto fields + } + } + + if (IP->IPPROTOCOL == 6) + { + PTCPMSG TCP = (PTCPMSG)&IP->Data; + + Output += sprintf((char *)Output, "TCP Src %d Dest %d ", ntohs(TCP->SOURCEPORT), ntohs(TCP->DESTPORT)); + return Output; + } + + if (IP->IPPROTOCOL == 1) + { + PICMPMSG ICMPptr = (PICMPMSG)&IP->Data; + + Output += sprintf((char *)Output, "ICMP "); + + if (ICMPptr->ICMPTYPE == 8) + Output += sprintf((char *)Output, "Echo Request "); + else + if (ICMPptr->ICMPTYPE == 0) + Output += sprintf((char *)Output, "Echo Reply "); + else + Output += sprintf((char *)Output, "Code %d", ICMPptr->ICMPTYPE); + + return Output; + } + + /* + MOV AL,IPPROTOCOL[ESI] + CMP AL,6 + JNE @F + + MOV EBX, OFFSET TCP + CALL NORMSTR + JMP ADD_CR + @@: + + CMP AL,1 + JNE @F + + MOV EBX, OFFSET ICMP + CALL NORMSTR + JMP ADD_CR + @@: + + CALL DISPLAY_BYTE_1 ; DISPLAY PROTOCOL TYPE + + ; mov AL,CR + ; call PUTCHAR + ; + ; MOV ECX,39 ; TESTING + ;IPLOOP: + ; lodsb + ; CALL BYTE_TO_HEX + ; + ; LOOP IPLOOP + + JMP ADD_CR + + + */ + return Output; +} + +char * DISPLAYARPDATAGRAM(UCHAR * Datagram, UCHAR * Output) +{ + UCHAR * ptr = Datagram; + char Dest[10]; + + if (ptr[7] == 1) // Request + return Output + sprintf((char *)Output, " ARP Request who has %d.%d.%d.%d? Tell %d.%d.%d.%d", + ptr[26], ptr[27], ptr[28], ptr[29], ptr[15], ptr[16], ptr[17], ptr[18]); + + // Response + + Dest[ConvFromAX25(&ptr[8], Dest)] = 0; + + return Output + sprintf((char *)Output, " ARP Reply %d.%d.%d.%d is at %s Tell %d.%d.%d.%d", + ptr[15], ptr[16], ptr[17], ptr[18], Dest, ptr[26], ptr[27], ptr[28], ptr[29]); + +} + + +UCHAR * DisplayINP3RIF(UCHAR * ptr1, UCHAR * ptr2, unsigned int msglen) +{ + char call[10]; + int calllen; + int hops; + unsigned short rtt; + unsigned int len; + unsigned int opcode; + char alias[10] = ""; + UCHAR IP[6]; + int i; + + ptr2 += sprintf(ptr2, " INP3 RIF:\r Alias Call Hops RTT\r"); + + while (msglen > 0) + { + calllen = ConvFromAX25(ptr1, call); + call[calllen] = 0; + + // Validate the call + + for (i = 0; i < calllen; i++) + { + if (!isupper(call[i]) && !isdigit(call[i]) && call[i] != '-') + { + ptr2 += sprintf(ptr2, " Corrupt RIF\r"); + return ptr2; + } + } + + ptr1 += 7; + + hops = *ptr1++; + rtt = (*ptr1++ << 8); + rtt += *ptr1++; + + IP[0] = 0; + strcpy(alias, " "); + + msglen -= 10; + + while (*ptr1 && msglen > 0) + { + len = *ptr1; + opcode = *(ptr1 + 1); + + if (len < 2 || len > msglen) + { + ptr2 += sprintf(ptr2, " Corrupt RIF\r"); + return ptr2; + } + if (opcode == 0 && len < 9) + { + memcpy(&alias[6 - (len - 2)], ptr1 + 2, len - 2); // Right Justiify + } + else + if (opcode == 1 && len < 8) + { + memcpy(IP, ptr1 + 2, len - 2); + } + + ptr1 += len; + msglen -= len; + } + + if (IP[0]) + ptr2 += sprintf(ptr2, " %s:%s %d %4.2d %d.%d.%d.%d\r", alias, call, hops, rtt, IP[0], IP[1], IP[2], IP[3]); + else + ptr2 += sprintf(ptr2, " %s:%s %d %4.2d\r", alias, call, hops, rtt); + + ptr1++; + msglen--; // EOP + } + + return ptr2; +} + + diff --git a/ax25_agw.c b/ax25_agw.c new file mode 100644 index 0000000..00574ec --- /dev/null +++ b/ax25_agw.c @@ -0,0 +1,1500 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" + +extern char modes_name[modes_count][20]; +extern int RSID_SABM[4]; +extern int RSID_UI[4]; +extern int RSID_SetModem[4]; +extern int needRSID[4]; + +/* + + +unit ax25_agw; + +interface + +uses sysutils,classes; + + void AGW_init; + void AGW_free; + void AGW_add_socket(socket integer); + void AGW_del_socket(socket integer); + void AGW_send_to_app(socket integer; data string); + void AGW_AX25_frame_analiz(snd_ch byte; RX boolean; frame string); + void AGW_frame_analiz(Socket Integer; frame string); + void AGW_explode_frame(Socket Integer; data string); + void AGW_AX25_conn(socket integer; snd_ch, mode byte; path string); + void AGW_AX25_disc(socket integer; snd_ch, mode byte; path string); + void AGW_AX25_data_in(socket integer; snd_ch,PID byte; path,data string); + void AGW_Raw_monitor(snd_ch byte; data string); + void AGW_frame_header(AGWPort,DataKind,PID,CallFrom,CallTo string; Len word) string; + void AGW_C_Frame(port char; CallFrom,CallTo,Conn_MSG string) string; + void erase_zero_ssid(call string) string; + void AGW_get_socket(socket integer) integer; + void clr_zero(data string) string; + +type TAGWUser = record + socket TStringList; + AGW_frame_buf TStringList; + Monitor TStringList; + Monitor_raw TStringList; +}; +*/ + +void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame); + +void AGW_frame_analiz(AGWUser * AGW); +void AGW_send_to_app(void * socket, string * data); +string * make_frame(string * data, Byte * path, Byte pid, Byte nr, Byte ns, Byte f_type, Byte f_id, boolean rpt, boolean pf, boolean cr); +void del_incoming_mycalls_by_sock(void * socket); +void del_incoming_mycalls(char * src_call); +void send_data_buf(TAX25Port * AX25Sess, int nr); +void get_monitor_path(Byte * path, char * mycall, char * corrcall, char * digi); +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); + + +int AGWVersion[2] = {2019, 'B'}; // QTSM Signature + + //ports_info1='1;Port1 with LoopBack Port; + +#define LSB 29 +#define MSB 30 +#define MON_ON '1' +#define MON_OFF '0' +#define MODE_OUR 0 +#define MODE_OTHER 1 +#define MODE_RETRY 2 + + +AGWUser ** AGWUsers = NULL; // List of currently connected clients +int AGWConCount = 0; + + +struct AGWHeader +{ + int Port; + unsigned char DataKind; + unsigned char filler2; + unsigned char PID; + unsigned char filler3; + char callfrom[10]; + char callto[10]; + int DataLength; + int reserved; +}; + +#define AGWHDDRRLEN sizeof(struct AGWHeader) + + +struct AGWSocketConnectionInfo +{ + int Number; // Number of record - for AGWConnections display + void *socket; +// SOCKADDR_IN sin; + BOOL SocketActive; + BOOL RawFlag; + BOOL MonFlag; + unsigned char CallSign1[10]; + unsigned char CallSign2[10]; + BOOL GotHeader; + int MsgDataLength; + struct AGWHeader AGWRXHeader; +}; + + +AGWUser * AGW_get_socket(void * socket) +{ + int i; + AGWUser * AGW = NULL; + + if (AGWConCount == 0) + return NULL; + + for (i = 0; i < AGWConCount; i++) + { + if (AGWUsers[i]->socket == socket) + { + AGW = AGWUsers[i]; + break; + } + } + return AGW; +} + + +void AGW_del_socket(void * socket) +{ + AGWUser * AGW = AGW_get_socket(socket); + TAX25Port * AX25Sess = NULL; + + int i = 0; + int port = 0; + + // Close any connections + + for (port = 0; port < 4; port++) + { + for (i = 0; i < port_num; i++) + { + AX25Sess = &AX25Port[port][i]; + + if (AX25Sess->status != STAT_NO_LINK && AX25Sess->socket == socket) + { + rst_timer(AX25Sess); + set_unlink(AX25Sess, AX25Sess->Path); + } + } + } + + // Clear registrations + + del_incoming_mycalls_by_sock(socket); + + if (AGW == NULL) + return; + + Clear(&AGW->AGW_frame_buf); + freeString(AGW->data_in); + AGW->Monitor = 0; + AGW->Monitor_raw = 0; + AGW->reportFreqAndModem = 0; +} + + + +void AGW_add_socket(void * socket) +{ + AGWUser * User = (struct AGWUser_t *)malloc(sizeof(struct AGWUser_t)); // One Client + memset(User, 0, sizeof(struct AGWUser_t)); + + AGWUsers = realloc(AGWUsers, (AGWConCount + 1) * sizeof(void *)); + + AGWUsers[AGWConCount++] = User; + + User->data_in = newString(); + User->socket = socket; + + User->Monitor = 0; + User->Monitor_raw = 0; + User->reportFreqAndModem = 0; +}; + + + + +void agw_free() +{ + // AGWUser.AGW_frame_buf.Free; + // AGWUser.Monitor.Free; + // AGWUser.Monitor_raw.Free; + // AGWUser.socket.Free; +}; + +/* +void erase_zero_ssid(call string) string; +var + p byte; +{ + p = pos('-0',call); + if (p>0 ) delete(call,p,2); + result = call; +}; +*/ + +string * AGW_frame_header(char AGWPort, char DataKind, UCHAR PID, char * CallFrom, char * CallTo, int Len ) +{ + string * Msg = newString(); + + struct AGWHeader * Hddr = (struct AGWHeader *)Msg->Data; + memset(Hddr, 0, sizeof(struct AGWHeader)); + + Hddr->Port = AGWPort; + Hddr->DataLength = Len; + Hddr->DataKind = DataKind; + Hddr->PID = PID; + strcpy(Hddr->callfrom, CallFrom); + strcpy(Hddr->callto, CallTo); + + Msg->Length = sizeof(struct AGWHeader); + return Msg; +}; + + + +// AGW to APP frames + +string * AGW_R_Frame() +{ + string * Msg = AGW_frame_header(0, 'R', 0, "", "", 8); + + stringAdd(Msg, (UCHAR *)AGWVersion, 8); + + return Msg; +}; + + +string * AGW_X_Frame(char * CallFrom, UCHAR reg_call) +{ + string * Msg = AGW_frame_header(0, 'x', 0, CallFrom, "", 1); + + stringAdd(Msg, (UCHAR *)®_call, 1); + + return Msg; + +}; + +string * AGW_G_Frame() +{ + char Ports[256] = "0;"; + char portMsg[64]; + + string * Msg; + + for (int i = 0; i < 4; i++) + { + Ports[0]++; + if (soundChannel[i]) + sprintf(portMsg, "Port%c with SoundCard Ch %c;", Ports[0], 'A' + i); + else + sprintf(portMsg, "Port%c Disabled;", Ports[0]); + + strcat(Ports, portMsg); + } + + + Msg = AGW_frame_header(0, 'G', 0, "", "", strlen(Ports) + 1); + + stringAdd(Msg, (UCHAR *)Ports, strlen(Ports) + 1); + + return Msg; +}; + + + +string * AGW_Gs_Frame(int port, Byte * port_info, int Len) +{ + string * Msg; + + Msg = AGW_frame_header(port, 'g', 0, "", "", Len); + stringAdd(Msg, port_info, Len); + return Msg; +}; + + +/* +void AGW_Ys_Frame(port char; frame_outstanding string) string; +var + DataLen word; +{ + DataLen = 4; + result = AGW_frame_header(port,'y','','','',DataLen)+frame_outstanding; +}; + +/ +void AGW_Y_Frame(port char; CallFrom,CallTo,frame_outstanding string) string; +var + DataLen word; +{ + DataLen = 4; + result = AGW_frame_header(port,'Y','',CallFrom,CallTo,DataLen)+frame_outstanding; +}; + +*/ + + +string * AGW_Y_Frame(int port, char * CallFrom, char *CallTo, int frame_outstanding) +{ + string * Msg; + + Msg = AGW_frame_header(port, 'Y', 0, CallFrom, CallTo, 4); + + stringAdd(Msg, (UCHAR *)&frame_outstanding, 4); + return Msg; +} + +/* + +void AGW_H_Frame(port char; heard string) string; +var + DataLen word; +{ + DataLen = length(heard); + result = AGW_frame_header(port,'H','','','',DataLen)+heard; +}; +*/ + + +string * AGW_C_Frame(int port, char * CallFrom, char * CallTo, string * Conn_MSG) +{ + string * Msg; + int DataLen; + + DataLen = Conn_MSG->Length; + + Msg = AGW_frame_header(port, 'C', 240, CallFrom, CallTo, DataLen); + + stringAdd(Msg, Conn_MSG->Data, Conn_MSG->Length); + + freeString(Conn_MSG); + + return Msg; +} + +string * AGW_Ds_Frame(int port, char * CallFrom, char * CallTo, string * Disc_MSG) +{ + string * Msg; + int DataLen; + + DataLen = Disc_MSG->Length; + + Msg = AGW_frame_header(port, 'd', 240, CallFrom, CallTo, DataLen); + + stringAdd(Msg, Disc_MSG->Data, Disc_MSG->Length); + + freeString(Disc_MSG); + + return Msg; +}; + + +string * AGW_D_Frame(int port, int PID, char * CallFrom, char * CallTo, string * Data) +{ + string * Msg; + int DataLen; + + DataLen = Data->Length; + + Msg = AGW_frame_header(port, 'D', PID, CallFrom, CallTo, DataLen); + + stringAdd(Msg, Data->Data, Data->Length); + + freeString(Data); + + return Msg; +} + + + +string * AGW_I_Frame(int port, char * CallFrom, char * CallTo, char * Monitor) +{ + string * Msg; + int DataLen; + + DataLen = strlen(Monitor); + Msg = AGW_frame_header(port, 'I', 0, CallFrom, CallTo, DataLen); + + stringAdd(Msg, (Byte *)Monitor, DataLen); + return Msg; +} + +string * AGW_S_Frame(int port, char * CallFrom, char * CallTo, char * Monitor) +{ + string * Msg; + int DataLen; + + DataLen = strlen(Monitor); + Msg = AGW_frame_header(port, 'S', 0, CallFrom, CallTo, DataLen); + + stringAdd(Msg, (Byte *)Monitor, DataLen); + return Msg; +}; + +string * AGW_U_Frame(int port, char * CallFrom, char * CallTo, char * Monitor) +{ + string * Msg; + int DataLen; + + DataLen = strlen(Monitor); + Msg = AGW_frame_header(port, 'U', 0, CallFrom, CallTo, DataLen); + + stringAdd(Msg, (Byte *)Monitor, DataLen); + return Msg; +} + + +string * AGW_T_Frame(int port, char * CallFrom, char * CallTo, char * Data) +{ + string * Msg; + int DataLen; + + DataLen = strlen(Data); + Msg = AGW_frame_header(port, 'T', 0, CallFrom, CallTo, DataLen); + + stringAdd(Msg, (Byte *)Data, DataLen); + + return Msg; +} + +// Raw Monitor +string * AGW_K_Frame(int port, int PID, char * CallFrom, char * CallTo, string * Data) +{ + string * Msg; + int DataLen; + + DataLen = Data->Length; + + Msg = AGW_frame_header(port, 'K', PID, CallFrom, CallTo, DataLen); + + stringAdd(Msg, Data->Data, Data->Length); + + freeString(Data); + + return Msg; +} + +// APP to AGW frames + +void on_AGW_P_frame(AGWUser * AGW) +{ + UNUSED(AGW); +} + +void on_AGW_X_frame(AGWUser * AGW, char * CallFrom) +{ + Byte reg_call; + + if (add_incoming_mycalls(AGW->socket, CallFrom)) + reg_call = 1; + else + reg_call = 0; + + AGW_send_to_app(AGW->socket, AGW_X_Frame(CallFrom, reg_call)); +} + + +void on_AGW_Xs_frame(char * CallFrom) +{ + del_incoming_mycalls(CallFrom); +}; + +void on_AGW_G_frame(AGWUser * AGW) +{ + AGW_send_to_app(AGW->socket, AGW_G_Frame()); +}; + + +void on_AGW_Ms_frame(AGWUser * AGW) +{ + AGW->Monitor ^= 1; // Flip State +} + + +void on_AGW_R_frame(AGWUser * AGW) +{ + AGW_send_to_app(AGW->socket, AGW_R_Frame()); +} + +int refreshModems = 0; + + +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[44] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature + int Len = 12; + + if (Frame->DataLength == 32) + { + // BPQ to QTSM private Format. + + int Freq; + Byte versionBytes[4] = VersionBytes; + + AGW->reportFreqAndModem = 1; // Can report frequency and Modem + + memcpy(&Freq, Data, 4); + + if (Freq) + { + // Set Frequency + + memcpy(&rx_freq[Frame->Port], Data, 2); + refreshModems = 1; + } + + if (Data[4]) + { + // New Modem Name. Need to convert to index unless numeric + + int n; + + if (strlen(&Data[4]) < 3) + { + n = atoi(&Data[4]); + if (n < modes_count) + { + speed[Frame->Port] = n; + refreshModems = 1; + } + } + else + { + for (n = 0; n < modes_count; n++) + { + if (strcmp(modes_name[n], &Data[4]) == 0) + { + // Found it + + speed[Frame->Port] = n; + refreshModems = 1; + break; + } + } + + } + } + + // Return Freq and Modem + + memcpy(&info[12], &rx_freq[Frame->Port], 2); + memcpy(&info[16], modes_name[speed[Frame->Port]], 20); + info[37] = speed[Frame->Port]; // Index + memcpy(&info[38], versionBytes, 4); + + Len = 44; + AGW_send_to_app(AGW->socket, AGW_Gs_Frame(Frame->Port, info, Len)); + return; + } + AGW_send_to_app(AGW->socket, AGW_Gs_Frame(Frame->Port, info, Len)); +}; +/* +void on_AGW_H_Frame(socket integer; port char); +{ +}; + +void on_AGW_Ys_Frame(socket integer; port char); +var + snd_ch,i byte; + info string; + frames word; +{ + frames = 0; + //for i = 0 to port_num-1 do frames = frames+AX25port[i].frame_buf.Count; + snd_ch = ord(Port)+1; + for i = 0 to port_num-1 do frames = frames+AX25port[snd_ch][i].in_data_buf.Count+AX25port[snd_ch][i].I_frame_buf.Count; + info = chr(lo(frames))+chr(hi(frames))+#0#0; + AGW_send_to_app(socket,AGW_Ys_Frame(port,info)); +}; + +*/ + +void on_AGW_Y_frame(void * socket, int snd_ch, char * CallFrom, char * CallTo) +{ + int frames; + TAX25Port * AX25Sess; + + AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo); + + if (AX25Sess) + { + //frames = AX25port[snd_ch][user_port].in_data_buf.Count; + frames = AX25Sess->in_data_buf.Count + AX25Sess->I_frame_buf.Count; +; + AGW_send_to_app(socket, AGW_Y_Frame(snd_ch, CallFrom, CallTo, frames)); + } +} + +// UI Transmit + +void on_AGW_M_frame(int port, Byte PID, char * CallFrom, char *CallTo, Byte * Msg, int MsgLen) +{ + Byte path[80]; + char Calls[80]; + string * Data = newString(); + + stringAdd(Data, Msg, MsgLen); + + sprintf(Calls, "%s,%s", CallTo, CallFrom); + + get_addr(Calls, path); + + Add(&all_frame_buf[port], + make_frame(Data, path, PID, 0, 0, U_FRM, U_UI, FALSE, SET_F, SET_C)); + +} + + +void on_AGW_C_frame(AGWUser * AGW, struct AGWHeader * Frame) +{ + int snd_ch = Frame->Port; + char * CallFrom = Frame->callfrom; + char * CallTo = Frame->callto; + + char path[128]; + Byte axpath[80]; + + TAX25Port * AX25Sess; + + // Also used for 'v' - connect via digis + + AX25Sess = get_free_port(snd_ch); + + if (AX25Sess) + { + AX25Sess->snd_ch = snd_ch; + + strcpy(AX25Sess->mycall, CallFrom); + strcpy(AX25Sess->corrcall, CallTo); + + sprintf(path, "%s,%s", CallTo, CallFrom); + + + if (Frame->DataLength) + { + // Have digis + + char * Digis = (char *)Frame + 36; + int nDigis = Digis[0]; + + Digis++; + + while(nDigis--) + { + sprintf(path, "%s,%s", path, Digis); + Digis += 10; + } + } + + AX25Sess->digi[0] = 0; + +// rst_timer(snd_ch, free_port); + + strcpy(AX25Sess->kind, "Outgoing"); + AX25Sess->socket = AGW->socket; + + AX25Sess->pathLen = get_addr(path, axpath); + + if (AX25Sess->pathLen == 0) + return; // Invalid Path + + strcpy((char *)AX25Sess->Path, (char *)axpath); + reverse_addr(axpath, AX25Sess->ReversePath, AX25Sess->pathLen); + + if (RSID_SABM[snd_ch]) // Send RSID before SABM/UA + needRSID[snd_ch] = 1; + + set_link(AX25Sess, AX25Sess->Path); + }; +}; + + + + + +void on_AGW_D_frame(int snd_ch, char * CallFrom, char * CallTo, Byte * Msg, int Len) +{ + TAX25Port * AX25Sess; + + AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo); + + if (AX25Sess) + { + string * data = newString(); + + stringAdd(data, Msg, Len); + + Add(&AX25Sess->in_data_buf, data); + + send_data_buf(AX25Sess, AX25Sess->vs); + } +} + +void on_AGW_Ds_frame(void * socket, int snd_ch, char * CallFrom, char * CallTo) +{ + TAX25Port * AX25Sess; + + AX25Sess = get_user_port_by_calls(snd_ch, CallFrom, CallTo); + + if (AX25Sess) + { + rst_timer(AX25Sess); + + set_unlink(AX25Sess, AX25Sess->Path); + } + else + { + string * Msg = newString(); + + Msg->Length = sprintf((char *)Msg->Data, "*** DISCONNECTED From Station %s\r", CallTo); + Msg->Length++; // Include the terminating NULL + + //del_outgoing_mycalls(CallTo); + + AGW_send_to_app(socket, AGW_Ds_Frame(snd_ch, CallTo, CallFrom, Msg)); + } +} + + +/* + +void on_AGW_Vs_Frame(socket integer; port char; CallFrom,CallTo,Data string); +var + snd_ch,num_digi,free_port byte; + need_free_port boolean; + digi,call,call1 string; +{ + snd_ch = ord(Port)+1; + num_digi = 0; + need_free_port = get_free_port(snd_ch,free_port); + if (need_free_port ) + { + digi = ''; + get_call(CallFrom,AX25Port[snd_ch][free_port].mycall); + get_call(CallTo,AX25Port[snd_ch][free_port].corrcall); + if (length(data)>0 ) { num_digi = ord(data[1]); delete(data,1,1); }; + if ((num_digi in [1..7]) and (length(data)>=num_digi*10) ) + { + repeat + call = clr_zero(copy(data,1,10)); delete(data,1,10); + if (call<>'' ) + { + get_call(call,call1); + if (digi='' ) digi = call1 + else digi = digi+','+call1; + }; + until data=''; + AX25Port[snd_ch][free_port].digi = reverse_digi(digi); + rst_timer(snd_ch,free_port); + AX25Port[snd_ch][free_port].kind = 'Outgoing'; + AX25Port[snd_ch][free_port].socket = socket; + set_link(snd_ch,free_port,CallTo+','+CallFrom); + }; + }; +}; + +void on_AGW_V_Frame(socket integer; port char; PID,CallFrom,CallTo,Data string); +var + call,call1,digi,path string; + snd_ch byte; + num_digi byte; + i byte; +{ + digi = ''; + snd_ch = ord(port)+1; + num_digi = 0; + if (length(data)>0 ) { num_digi = ord(data[1]); delete(data,1,1); }; + if ((num_digi in [1..7]) and (length(data)>=num_digi*10) ) + { + for i = 1 to num_digi do + { + call = clr_zero(copy(data,1,10)); delete(data,1,10); + if (call<>'' ) + { + get_call(call,call1); + if (digi='' ) digi = call1 + else digi = digi+','+call1; + }; + }; + }; + path = CallTo+','+CallFrom+','+digi; + all_frame_buf[snd_ch].Add(make_frame(Data,path,ord(PID[1]),0,0,U_FRM,U_UI,FALSE,SET_F,SET_C)); +}; + +void on_AGW_Cs_Frame(socket integer; port char; PID,CallFrom,CallTo string); +{ +}; +*/ + +void on_AGW_K_frame(struct AGWHeader * Frame) +{ + // KISS frame + + unsigned short CRC; + UCHAR CRCString[2]; + string * TXMSG; + + UCHAR * Data = (UCHAR * )Frame; + int Len = Frame->DataLength; + + Data = &Data[AGWHDDRRLEN]; + + TXMSG = newString(); + + stringAdd(TXMSG, Data, Len); // include Control + + CRC = get_fcs(&Data[1], Len - 1); + + CRCString[0] = CRC & 0xff; + CRCString[1] = CRC >> 8; + + stringAdd(TXMSG, CRCString, 2); + + Add(&all_frame_buf[Frame->Port], TXMSG); + + // for now assume only used for sending UI + + if (RSID_UI[Frame->Port]) // Send RSID before UI + needRSID[Frame->Port] = 1; + +} + +void on_AGW_Ks_frame(AGWUser * AGW) +{ + AGW->Monitor_raw ^= 1; // Flip State +} + +// Analiz incoming frames + +void AGW_explode_frame(void * socket, UCHAR * data, int length) +{ + int AGWHeaderLen = sizeof(struct AGWHeader); + int i; + + AGWUser * AGW = NULL; + + if (AGWConCount == 0) + return; + + for (i = 0; i < AGWConCount; i++) + { + if (AGWUsers[i]->socket == socket) + { + AGW = AGWUsers[i]; + break; + } + } + + if (AGW == NULL) + return; + + stringAdd(AGW->data_in, data, length); + + while (AGW->data_in->Length >= AGWHeaderLen) // Make sure have at least header + { + struct AGWHeader * Hddr = (struct AGWHeader *)AGW->data_in->Data; + + int AgwLen = Hddr->DataLength + AGWHeaderLen; + + if (AgwLen < AGWHeaderLen) // Corrupt + { + AGW->data_in->Length = 0; + return; + } + + if (AGW->data_in->Length >= AgwLen) + { + // Have frame as well + + if (AGW->data_in->Data[0] == 0xC0) // Getting KISS Data on AGW Port + { + AGW->data_in->Length = 0; // Delete data + return; + } + + AGW_frame_analiz(AGW); + mydelete(AGW->data_in, 0, AgwLen); + } + else + return; // Wait for the data + } + + + + + /* + idx = AGW_get_socket(socket); + if (idx>=0 ) + { + AGWUser.AGW_frame_buf.Strings[idx] = AGWUser.AGW_frame_buf.Strings[idx]+data; + str_buf = AGWUser.AGW_frame_buf.Strings[idx]; + repeat + done = FALSE; + BufLen = length(str_buf); + if (BufLen>=HEADER_SIZE ) + { + DataLen = ord(str_buf[LSB])+ord(str_buf[MSB])*256; + FrameLen = HEADER_SIZE+DataLen; + if (length(str_buf)>=FrameLen ) + { + frame = copy(str_buf,1,FrameLen); + delete(str_buf,1,FrameLen); + done = TRUE; + AGW_frame_analiz(socket,frame); + }; + }; + until not done; + // Check if (socket is still present and has same index + if ((AGWUser.socket.Count>idx) and (AGWUser.socket.Strings[idx]=inttostr(socket)) ) + AGWUser.AGW_frame_buf.Strings[idx] = str_buf; + }; + */ +}; + +/* +void clr_zero(data string) string; +var + p,i word; + s string; +{ + s = ''; + p = pos(#0,data); + if (p>1 ) data = copy(data,1,p-1); + if (length(data)>0 ) for i = 1 to length(data) do if (data[i]>#31 ) s = s+data[i]; + result = s; +}; + +void AGW_parse_frame(frame string; var DataKind,PID,AGWPort char; var Pass,CallFrom,CallTo,DataLen,Data string); +{ + DataKind = frame[5]; + PID = frame[7]; + AGWPort = frame[1]; + Pass = ''; + CallFrom = clr_zero(copy(frame,9,10)); + CallTo = clr_zero(copy(frame,19,10)); + get_call(CallFrom,CallFrom); + get_call(CallTo,CallTo); + DataLen = inttostr(ord(frame[LSB])+ord(frame[MSB])*256); + if (length(frame)>HEADER_SIZE ) Data = copy(frame,37,strtoint(DataLen)) else Data = ''; +}; + +*/ + +void AGW_send_to_app(void * socket, string * data) +{ + char * Msg = malloc(data->Length); + memcpy(Msg, data->Data, data->Length); + // can use KISS proc as it just sends to the supplied socket but need copy of message + KISSSendtoServer(socket, (Byte *)Msg, data->Length); + freeString(data); +}; + + +void AGW_AX25_data_in(void * socket, int snd_ch, int PID, Byte * path, string * data) +{ + int len = 250, sendlen; + + char CallFrom[10]; + char CallTo[10]; + + string * pkt; + + CallTo[ConvFromAX25(&path[7], CallTo)] = 0; + CallFrom[ConvFromAX25(path, CallFrom)] = 0; + + while (data->Length) + { + if (data->Length > len) + sendlen = len; + else + sendlen = data->Length; + + pkt = copy(data, 0, sendlen); + mydelete(data, 0, sendlen); + + AGW_send_to_app(socket, AGW_D_Frame(snd_ch, PID, CallFrom, CallTo, pkt)); + } + +} + +void AGW_AX25_conn(TAX25Port * AX25Sess, int snd_ch, Byte mode) +{ + string * Msg = newString(); + + switch (mode) + { + case MODE_OTHER: + + Msg->Length = sprintf((char *)Msg->Data, "*** CONNECTED To Station %s\r", AX25Sess->corrcall); + break; + + case MODE_OUR: + + Msg->Length = sprintf((char *)Msg->Data, "*** CONNECTED With Station %s\r", AX25Sess->corrcall); + break; + + }; + + Msg->Length++; // Include the terminating NULL + + AGW_send_to_app(AX25Sess->socket, AGW_C_Frame(snd_ch, AX25Sess->corrcall, AX25Sess->mycall, Msg)); +}; + + + +void AGW_AX25_disc(TAX25Port * AX25Sess, Byte mode) +{ + string * Msg = newString(); + + switch (mode) + { + + case MODE_OTHER: + case MODE_OUR: + + Msg->Length = sprintf((char *)Msg->Data, "*** DISCONNECTED From Station %s\r", AX25Sess->corrcall); + break; + + case MODE_RETRY: + + Msg->Length = sprintf((char *)Msg->Data, "*** DISCONNECTED RETRYOUT With Station %s\r", AX25Sess->corrcall); + break; + + }; + + Msg->Length++; // Include the terminating NULL + + //del_outgoing_mycalls(CallTo); + + AGW_send_to_app(AX25Sess->socket, AGW_Ds_Frame(AX25Sess->snd_ch, AX25Sess->corrcall, AX25Sess->mycall, Msg)); +}; + + +void AGW_frame_monitor(Byte snd_ch, Byte * path, string * data, Byte pid, Byte nr, Byte ns, Byte f_type, Byte f_id, Byte rpt, Byte pf, Byte cr, Byte RX) +{ + char mon_frm[512]; + char AGW_path[256]; + string * AGW_data = NULL; + + const char * frm; + Byte * datap = data->Data; + Byte _data[512]; + Byte * p_data = _data; + int _datalen; + + char agw_port; + char CallFrom[10], CallTo[10], Digi[80]; + + integer i; + const char * ctrl; + int len; + + AGWUser * AGW; + + UNUSED(rpt); + + 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; + } + + agw_port = snd_ch; + + get_monitor_path(path, CallTo, CallFrom, Digi); + + ctrl = ""; + frm = ""; + + switch (f_id) + { + case I_I: + + frm = "I"; + if (cr == SET_C && pf == SET_P) + ctrl = " P"; + + break; + + case S_RR: + + frm = "RR"; + if (pf == SET_P) + ctrl = " P/F"; + + break; + + case S_RNR: + + frm = "RNR"; + if (pf == SET_P) + ctrl = " P/F"; + + break; + + case S_REJ: + + frm = "REJ"; + if (pf == SET_P) + ctrl = " P/F"; + + break; + + + case S_SREJ: + + frm = "SREJ"; + if (pf == SET_P) + ctrl = " P/F"; + + break; + + case U_SABM: + + frm = "SABM"; + if (cr == SET_C && pf == SET_P) + ctrl = " P"; + + break; + + case U_DISC: + + frm = "DISC"; + if (cr == SET_C && pf == SET_P) + ctrl = " P"; + break; + + case U_DM: + + frm = "DM"; + if ((cr == SET_R) && (pf == SET_P)) + ctrl = " F "; + break; + + case U_UA: + + frm = "UA"; + if ((cr == SET_R) && (pf == SET_P)) + ctrl = " F "; + + break; + + case U_FRMR: + + frm = "FRMR"; + if ((cr == SET_R) && (pf == SET_P)) + ctrl = " F "; + break; + + case U_UI: + + frm = "UI"; + if ((pf == SET_P)) + ctrl = " P/F"; + } + + if (Digi[0]) + sprintf(AGW_path, " %d:Fm %s To %s Via %s <%s", snd_ch + 1, CallFrom, CallTo, Digi, frm); + else + sprintf(AGW_path, " %d:Fm %s To %s <%s", snd_ch + 1, CallFrom, CallTo, frm); + + + 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%s R%d S%d pid=%X Len=%d >[%s]\r%s\r", AGW_path, ctrl, nr, ns, pid, len, ShortDateTime(), _data); + + break; + + case U_FRM: + + if (f_id == U_UI) + { + sprintf(mon_frm, "%s pid=%X Len=%d >[%s]\r%s\r", AGW_path, pid, len, ShortDateTime(), _data); // "= AGW_path + ctrl + '>' + time_now + #13; + } + else if (f_id == U_FRMR) + { + sprintf(mon_frm, "%s%s>%02x %02x %02x[%s]\r", AGW_path, ctrl, datap[0], datap[1], datap[2], ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13; + } + else + sprintf(mon_frm, "%s%s>[%s]\r", AGW_path, ctrl, ShortDateTime()); // "= 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%s R%d>[%s]\r", AGW_path, ctrl, nr, ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13; + + break; + + } + +// stringAdd(mon_frm, "", 1); // Add 0 at the end of each frame + + // I think we send to all AGW sockets + + for (i = 0; i < AGWConCount; i++) + { + AGW = AGWUsers[i]; + + if (AGW->Monitor) + { + if (RX) + { + switch (f_id) + { + + case I_I: + AGW_data = AGW_I_Frame(agw_port, CallFrom, CallTo, mon_frm); + break; + + case S_RR: + case S_RNR: + case S_REJ: + case S_SREJ: + + AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm); + break; + + case U_SABM: + AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm); + break; + + case U_DISC: + AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm); + break; + + case U_DM: + AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm); + break; + + case U_UA: + AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm); + break; + + case U_FRMR: + AGW_data = AGW_S_Frame(agw_port, CallFrom, CallTo, mon_frm); + break; + + case U_UI: + AGW_data = AGW_U_Frame(agw_port, CallFrom, CallTo, mon_frm); + } + if (AGW_data) + AGW_send_to_app(AGW->socket, AGW_data); + } + + else + { + AGW_data = AGW_T_Frame(agw_port, CallFrom, CallTo, mon_frm); + AGW_send_to_app(AGW->socket, AGW_data); + } + } + } +} + +void AGW_Report_Modem_Change(int port) +{ + // Send modem change report to all sockets that support it + + int i; + AGWUser * AGW; + string * pkt; + + // I think we send to all AGW sockets + + for (i = 0; i < AGWConCount; i++) + { + AGW = AGWUsers[i]; + + if (AGW->reportFreqAndModem) + { + // QTSM 's' Message with a data field is used by QtSM to set/read Modem Params + + Byte info[44] = { 0, 255, 24, 3, 100, 15, 6, 0, 1, 0, 0, 0 }; //QTSM Signature + + // Return Freq and Modem + + memcpy(&info[12], &rx_freq[port], 2); + memcpy(&info[16], modes_name[speed[port]], 20); + info[37] = speed[port]; // Index + AGW_send_to_app(AGW->socket, AGW_Gs_Frame(port, info, 44)); + } + } +} + + +void AGW_Raw_monitor(int snd_ch, string * data) +{ + int i; + AGWUser * AGW; + string * pkt; + + // I think we send to all AGW sockets + + for (i = 0; i < AGWConCount; i++) + { + AGW = AGWUsers[i]; + + if (AGW->Monitor_raw) + { + pkt = newString(); + + pkt->Data[0] = snd_ch << 4; // KISS Address + pkt->Length++; + + stringAdd(pkt, data->Data, data->Length - 2); // Exclude CRC + + AGW_send_to_app(AGW->socket, AGW_K_Frame(snd_ch, 0, "", "", pkt)); + } + } +} + +void AGW_AX25_frame_analiz(int snd_ch, int RX, string * frame) +{ + // path,data string; + Byte pid, nr, ns, f_type, f_id; + Byte rpt, cr, pf; + Byte path[80]; + string * data = newString(); + + decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + AGW_frame_monitor(snd_ch, path, data, pid, nr, ns, f_type, f_id, rpt, pf, cr, RX); + +// if (RX) +// AGW_Raw_monitor(snd_ch, frame); +}; + + +void AGW_frame_analiz(AGWUser * AGW) +{ + struct AGWHeader * Frame = (struct AGWHeader *)AGW->data_in->Data; + Byte * Data = &AGW->data_in->Data[36]; + + if (Frame->Port < 0 || Frame->Port > 3) + return; + + if (soundChannel[Frame->Port] == 0) + return; + + if (Frame->Port > 3) + return; + + switch (Frame->DataKind) + { + case 'P': + + on_AGW_P_frame(AGW); + return; + + case 'X': + + on_AGW_X_frame(AGW, Frame->callfrom); + return; + + + case 'x': + + on_AGW_Xs_frame(Frame->callfrom); + return; + + case 'G': + + on_AGW_G_frame(AGW); + return; + + case 'm': + + on_AGW_Ms_frame(AGW); + return; + + case 'R': + + on_AGW_R_frame(AGW); + return; + + case 'g': + + on_AGW_Gs_frame(AGW, Frame, Data); + return; +// 'H': on_AGW_H_frame(AGW,Frame->Port); +// 'y': on_AGW_Ys_frame(AGW,Frame->Port); + + case 'Y': + on_AGW_Y_frame(AGW->socket, Frame->Port, Frame->callfrom, Frame->callto); + break; + + case 'M': + + on_AGW_M_frame(Frame->Port,Frame->PID, Frame->callfrom, Frame->callto, Data, Frame->DataLength); + break; + + case 'C': + case 'v': // Call with digis + + on_AGW_C_frame(AGW, Frame); + return; + + case 'D': + + on_AGW_D_frame(Frame->Port, Frame->callfrom, Frame->callto, Data, Frame->DataLength); + return; + + case 'd': + on_AGW_Ds_frame(AGW->socket, Frame->Port, Frame->callfrom, Frame->callto); + return; + +// 'V': on_AGW_V_frame(AGW,Frame->Port,PID,CallFrom,CallTo,Data); +// 'c': on_AGW_Cs_frame(sAGWocket,Frame->Port,PID,CallFrom,CallTo); + + + case 'K': + + on_AGW_K_frame(Frame); + return; + + case 'k': + on_AGW_Ks_frame(AGW); + return; + + default: + Debugprintf("AGW %c", Frame->DataKind); + } +} diff --git a/ax25_demod.c b/ax25_demod.c new file mode 100644 index 0000000..eec2f9f --- /dev/null +++ b/ax25_demod.c @@ -0,0 +1,4313 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" + +extern int blnBusyStatus; +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); + +float GuessCentreFreq(int i); + +/* + +unit ax25_demod; + +interface + +uses math,sysutils,Graphics,classes; + + procedure detector_init; + procedure detector_free; + procedure Mux3(snd_ch,rcvr_nr,emph: byte; src1,core: array of single; var dest,prevI,prevQ: array of single; tap,buf_size: word); + procedure Mux3_PSK(snd_ch,rcvr_nr,emph: byte; src1,core: array of single; var destI,destQ,prevI,prevQ: array of single; tap,buf_size: word); + procedure make_core_intr(snd_ch: byte); + procedure make_core_LPF(snd_ch: byte; width: single); + procedure make_core_BPF(snd_ch: byte; freq,width: single); + procedure make_core_TXBPF(snd_ch: byte; freq,width: single); + procedure init_BPF(freq1,freq2: single; tap: word; samplerate: single; var buf: array of single); + procedure FIR_filter(src: array of single; buf_size,tap: word; core: array of single; var dest,prev: array of single); + procedure Demodulator(snd_ch,rcvr_nr: byte; src_buf: array of single; last: boolean); + function memory_ARQ(buf: TStringList; data: string): string; + +type TSurvivor = record + BitEstimates: int64; + Pathdistance: integer; +} + +type TMChannel = record + prev_LPF1I_buf : array [0..4095] of single; + prev_LPF1Q_buf : array [0..4095] of single; + prev_dLPFI_buf : array [0..4095] of single; + prev_dLPFQ_buf : array [0..4095] of single; + prev_AFCI_buf : array [0..4095] of single; + prev_AFCQ_buf : array [0..4095] of single; + AngleCorr : single; + MUX_osc : single; + AFC_IZ1 : single; + AFC_IZ2 : single; + AFC_QZ1 : single; + AFC_QZ2 : single; + AFC_bit_buf1I : array [0..1023] of single; + AFC_bit_buf1Q : array [0..1023] of single; + AFC_bit_buf2 : array [0..1023] of single; + AFC_IIZ1 : single; + AFC_QQZ1 : single; +} +*/ + + +#define sbc 175 + +single ch_offset[4] = { -sbc * 1.5,-sbc * 0.5,sbc*0.5,sbc*1.5 }; + + + +float PI125 = 0.125f * M_PI; +float PI375 = 0.375f * M_PI; +float PI625 = 0.625f * M_PI; +float PI875 = 0.875f * M_PI; +float PI5 = 0.5f * M_PI; +float PI25 = 0.25f * M_PI; +float PI75 = 0.75f * M_PI; + +unsigned char modem_mode[5] ={0,0,0,0}; + +unsigned short bpf[5] = { 500, 500, 500, 500,500 }; +unsigned short lpf[5] = { 150, 150, 150, 150, 150 }; + +float BIT_AFC = 32; +float slottime_tick[5] = { 0 }; +float resptime_tick[5] = { 0 }; +int dcd_threshold = 128; +int rxOffset = 0; +int chanOffset[4] = { 0,0,0,0 }; + +float DCD_LastPkPos[5] = { 0 }; +float DCD_LastPerc[5] = { 0 }; +int dcd_bit_cnt[5] = { 0 }; +Byte DCD_status[5] = { 0 }; +float DCD_persist[5] = { 0 }; +int dcd_bit_sync[5] = { 0 }; +Byte dcd_hdr_cnt[5] = { 0 }; +longword DCD_header[5] = { 0 }; +int dcd_on_hdr[5] = { 0 }; + +extern int centreFreq[4]; + + +unsigned short n_INTR[5] = { 1,1,1,1,1 }; +unsigned short INTR_tap[5] = { 16, 16,16,16,16 }; +unsigned short BPF_tap[5] = { 256, 256,256,256,256 }; // 256 default +unsigned short LPF_tap[5] = { 128, 128,128,128,128 }; // 128 + + + +short rx_freq[5] = { 1700, 1700,1700,1700,1700 }; +short rx_shift[5] = { 200, 200, 200, 200, 200 }; +short rx_baudrate[5] = { 300, 300, 300, 300, 300 }; +short rcvr_offset[5] = { 30, 30, 30, 30,30 }; + +// rx_freq is configured freq. We shouldn't change it so need a sparate variable +// for the actual demod freq when using multiple decoders + +short active_rx_freq[5] = { 1700, 1700,1700,1700,1700 }; + +int fx25_mode[4] = { 0, 0, 0, 0 }; +int il2p_mode[4] = { 0, 0, 0, 0 }; + +int pnt_change[5] = { 0 }; +float src_buf[5][2048]; + +float INTR_core[5][2048]; +float AFC_core[5][2048]; +float LPF_core[5][2048]; + +int new_tx_port[4] = { 0,0,0,0 }; +UCHAR RCVR[5] = { 0 }; + +// We allow two (or more!) ports to be assigned to the same soundcard channel + +int soundChannel[5] = { 0 }; // 0 = Unused 1 = Left 2 = Right 3 = Mono +int modemtoSoundLR[4] = { 0 }; + +struct TDetector_t DET[nr_emph + 1][16]; + +TStringList detect_list_l[5]; +TStringList detect_list[5]; +TStringList detect_list_c[5]; + +int lastDCDState[4] = { 0,0,0,0 }; +/* + +implementation + +uses sm_main,ax25,ax25_l2,ax25_mod,ax25_agw,rsunit,kiss_mode; +*/ + +void detector_init() +{ + int i, k, j; + + for (k = 0; k < 16; k++) + { + for (i = 1; i <= 4; i++) + { + for (j = 0; j <= nr_emph; j++) + { + struct TDetector_t * pDET = &DET[j][k]; + + pDET->fx25[i].status = FX25_TAG; + pDET->AngleCorr[i] = 0; + pDET->last_sample[i] = 0; + pDET->sample_cnt[i] = 0; + pDET->last_bit[i] = 0; + pDET->PkAmp[i] = 0; + pDET->PkAmpMax[i] = 0; + pDET->newpkpos[i] = 0; + pDET->ones[i] = 0; + pDET->zeros[i] = 0; + pDET->MinAmp[i] = 0; + pDET->MaxAmp[i] = 0; + pDET->MUX3_osc[i] = 0; + pDET->Preemphasis6[i] = 0; + pDET->Preemphasis12[i] = 0; + pDET->PSK_AGC[i] = 0; + pDET->AGC[i] = 0; + pDET->AGC1[i] = 0; + pDET->AGC2[i] = 0; + pDET->AGC3[i] = 0; + pDET->AGC_max[i] = 0; + pDET->AGC_min[i] = 0; + pDET->AFC_IZ1[i] = 0; + pDET->AFC_IZ2[i] = 0; + pDET->AFC_QZ1[i] = 0; + pDET->AFC_QZ2[i] = 0; + pDET->AFC_dF[i] = 0; + pDET->AFC_cnt[i] = 0; + pDET->PSK_IZ1[i] = 0; + pDET->PSK_QZ1[i] = 0; + pDET->PkAmpI[i] = 0; + pDET->PkAmpQ[i] = 0; + pDET->last_rx_bit[i] = 0; + pDET->bit_stream[i] = 0; + pDET->byte_rx[i] = 0; + pDET->bit_stuff_cnt[i] = 0; + pDET->bit_cnt[i] = 0; + pDET->bit_osc[i] = 0; + pDET->frame_status[i] = 0; + initString(&pDET->FEC_rx_data[i]); + initString(&pDET->rx_data[i]); + initString(&pDET->raw_bits[i]); + initTStringList(&pDET->mem_ARQ_buf[i]); + initTStringList(&pDET->mem_ARQ_F_buf[i]); + pDET->rx_decoded = 0; + pDET->emph_decoded = 0; + } + } + } + + for (i = 1; i <= 4; i++) + { + initTStringList(&detect_list[i]); + initTStringList(&detect_list_l[i]); + initTStringList(&detect_list_c[i]); + } +} + + +/* +procedure detector_free; +var + i,k,j: word; +{ + for i = 1 to 4 do + { + detect_list[i].Free; + detect_list_l[i].Free; + detect_list_c[i].Free; + } + for k = 0 to 16 do + for i = 1 to 4 do + for j = 0 to nr_emph do + { + DET[j,k].mem_ARQ_buf[i].Free; + DET[j,k].mem_ARQ_F_buf[i].Free; + } +} +*/ + +void FIR_filter(float * src, unsigned short buf_size, unsigned short tap, float * core, float * dest, float * prev) +{ + float accum = 0.0f; + float fp1; + + int eax, ebx; + float * edi; + + fmove(&prev[buf_size], &prev[0], tap * 4); + fmove(&src[0], &prev[tap], buf_size * 4); + + eax = 0; + + // ; shl ecx, 2; + // ; shl edx, 2; + +cfir_i: + edi = prev; + + edi += eax; + + ebx = 0; + accum = 0.0f; + +cfir_k: + + // FLD pushes operand onto stack, so old value goes to fp1 + + fp1 = accum; + accum = edi[ebx]; + accum *= core[ebx]; + accum += fp1; + + ebx++; + if (ebx != tap) + goto cfir_k; + + dest[eax] = accum; + + eax++; + + if (eax != buf_size) + goto cfir_i; + +} + + +float get_persist(int snd_ch, int persist) +{ + single x, x1 ; + + x = 256 / persist; + + x1 = round(x*x) * rand() / RAND_MAX; + + return x1 * 0.5 * slottime[snd_ch]; +} + +void chk_dcd1(int snd_ch, int buf_size) +{ + // This seems to schedule all TX, but is only called when a frame has been processed + // ? does this work as Andy passes aborted frames to decoder + + Byte port; + word i; + single tick; + word active; + boolean ind_dcd; + boolean dcd_sync; + longint n; + + TAX25Port * AX25Sess; + + dcd[snd_ch] = 1; + + ind_dcd = 0; + + tick = 1000 / RX_Samplerate; + + if (modem_mode[snd_ch] == MODE_ARDOP) + { + dcd_bit_sync[snd_ch] = blnBusyStatus; + } + else + { + if (dcd_bit_cnt[snd_ch] > 0) + dcd_bit_sync[snd_ch] = 0; + else + dcd_bit_sync[snd_ch] = 1; + + if (dcd_on_hdr[snd_ch]) + dcd_bit_sync[snd_ch] = 1; + + if (modem_mode[snd_ch] == MODE_MPSK && DET[0][0].frame_status[snd_ch] == FRAME_LOAD) + dcd_bit_sync[snd_ch] = 1; + } + + if (lastDCDState[snd_ch] != dcd_bit_sync[snd_ch]) + { + updateDCD(snd_ch, dcd_bit_sync[snd_ch]); + lastDCDState[snd_ch] = dcd_bit_sync[snd_ch]; + } + + if (resptime_tick[snd_ch] < resptime[snd_ch]) + resptime_tick[snd_ch] = resptime_tick[snd_ch] + tick * buf_size; + + slottime_tick[snd_ch] = slottime_tick[snd_ch] + tick * buf_size; + + if (dcd_bit_sync[snd_ch]) // reset the slottime timer + { + slottime_tick[snd_ch] = 0; + DCD_status[snd_ch] = DCD_WAIT_SLOT; + } + + switch (DCD_status[snd_ch]) + { + case DCD_WAIT_SLOT: + + if (slottime_tick[snd_ch] >= slottime[snd_ch]) + { + DCD_status[snd_ch] = DCD_WAIT_PERSIST; + DCD_persist[snd_ch] = get_persist(snd_ch, persist[snd_ch]); + } + break; + + case DCD_WAIT_PERSIST: + + if (slottime_tick[snd_ch] >= slottime[snd_ch] + DCD_persist[snd_ch]) + { + dcd[snd_ch] = FALSE; + slottime_tick[snd_ch] = 0; + DCD_status[snd_ch] = DCD_WAIT_SLOT; + } + break; + } + + active = 0; + + for (i = 0; i < port_num; i++) + { + if (AX25Port[snd_ch][i].status != STAT_NO_LINK) + active++; + + if (active < 2) + resptime_tick[snd_ch] = resptime[snd_ch]; + + if (TX_rotate) + { + for (int i = 0; i < 4; i++) + { + if (snd_status[i] == SND_TX) + dcd[snd_ch] = TRUE; + } + } + + if (snd_ch == 1) + snd_ch = 1; + + if (!dcd[snd_ch] && resptime_tick[snd_ch] >= resptime[snd_ch]) + { + i = 0; + + port = new_tx_port[snd_ch]; + do + { + AX25Sess = &AX25Port[snd_ch][port]; + + if (AX25Sess->frame_buf.Count > 0) + Frame_Optimize(AX25Sess, &AX25Sess->frame_buf); + + if (AX25Sess->frame_buf.Count > 0) + { + for (n = 0; n < AX25Sess->frame_buf.Count; n++) + { + Add(&all_frame_buf[snd_ch], duplicateString(Strings(&AX25Sess->frame_buf, n))); + } + + Clear(&AX25Sess->frame_buf); + } + + port++; + + if (port >= port_num) + port = 0; + + if (all_frame_buf[snd_ch].Count > 0) + new_tx_port[snd_ch] = port; + + i++; + + } while (all_frame_buf[snd_ch].Count == 0 && i < port_num); + + // Add KISS frames + + if (KISSServ) + { + // KISS monitor outgoing AGW frames + + if (all_frame_buf[snd_ch].Count > 0) + { + for (n = 0; n < all_frame_buf[snd_ch].Count; n++) + { + KISS_on_data_out(snd_ch, Strings(&all_frame_buf[snd_ch], n), 1); // Mon TX + } + } + + // Add outgoing KISS frames to TX Q + + if (KISS.buffer[snd_ch].Count > 0) + { + for (n = 0; n < KISS.buffer[snd_ch].Count; n++) + { + if (AGWServ) + AGW_Raw_monitor(snd_ch, Strings(&KISS.buffer[snd_ch], n)); + + // Need to add copy as clear will free original + + Add(&all_frame_buf[snd_ch], duplicateString(Strings(&KISS.buffer[snd_ch], n))); + } + Clear(&KISS.buffer[snd_ch]); + } + } + + if (all_frame_buf[snd_ch].Count > 0 && snd_status[snd_ch] == SND_IDLE) + { + resptime_tick[snd_ch] = 0; + RX2TX(snd_ch); // Do TX + return; + } + } + } +} + + +string * get_pkt_data(string * stream) +{ + Byte bitstuff_cnt; + Byte bits_cnt; + word i; + string * s = newString(); + + Byte bit; + Byte raw_bit; + Byte sym; + + bits_cnt = 0; + bitstuff_cnt = 0; + sym = 0; + + if (stream->Length > 0) + { + for (i = 0; i < stream->Length; i++) + { + if (stream->Data[i] == '1') + bit = RX_BIT1; + else + bit = RX_BIT0; + + if (bitstuff_cnt < 5) + { + sym = (sym >> 1) | bit; + bits_cnt++; + } + + if (bitstuff_cnt == 5 || bit == RX_BIT0) + bitstuff_cnt = 0; + + if (bit == RX_BIT1) + bitstuff_cnt++; + + if (bits_cnt == 8) + { + stringAdd(s, &sym, 1); + sym = 0; + bits_cnt = 0; + } + } + } + + return s; +} + +string * get_pkt_data2(string * stream, Byte last_nrzi_bit) +{ + Byte bitstuff_cnt; + Byte bits_cnt; + word i; + string * s = newString(); + + Byte pkt[350]; + + Byte bit; + Byte raw_bit; + Byte sym; + int n = 0; + + bits_cnt = 0; + bitstuff_cnt = 0; + sym = 0; + + if (stream->Length > 0) + { + for (i = 0; i < stream->Length; i++) + { + if (stream->Data[i] == '1') raw_bit = RX_BIT1; else raw_bit = RX_BIT0; + if (raw_bit == last_nrzi_bit) bit = RX_BIT1; else bit = RX_BIT0; + + last_nrzi_bit = raw_bit; + + if (bitstuff_cnt < 5) + { + sym = (sym >> 1) | bit; + bits_cnt++; + } + + if (bitstuff_cnt == 5 || bit == RX_BIT0) + bitstuff_cnt = 0; + + if (bit == RX_BIT1) + bitstuff_cnt++; + + if (bits_cnt == 8) + { + if (n < 330) + pkt[n++] = sym; + + sym = 0; + bits_cnt = 0; + } + } + } + + stringAdd(s, pkt, n); + return s; +} + +string * get_NRZI_data(string * stream, UCHAR last_nrzi_bit) +{ + longword len; + word i; + string * s = NULL; + Byte raw_bit; + + len = stream->Length; + + if (len > 65535) + len = 65535; + + if (len > 0) + { + s = newString(); + + setlength(s, len); + + for (i = 0; i < len; i++) + { + if (stream->Data[i] == '1') + raw_bit = RX_BIT1; + else + raw_bit = RX_BIT0; + + if (raw_bit == last_nrzi_bit) + s->Data[i] = '1'; + else + s->Data[i] = '0'; + + last_nrzi_bit = raw_bit; + } + } + return s; +} +/* + +function invert_NRZI_data(stream: string; last_nrzi_bit: byte): string; +var + len: longword; + i: word; + s: string; +{ + s = ''; + len = length(stream); + if len>65535 then len = 65535; + if len>0 then + { + setlength(s,len); + for i = 1 to len do + if last_nrzi_bit=RX_BIT0 then + { + if stream[i]='1' then s[i] = '0' else s[i] = '1'; + end + else s[i] = stream[i]; + } + result = s; +} +*/ + +void make_rx_frame(int snd_ch, int rcvr_nr, int emph, Byte last_nrzi_bit, string * raw_data, string * raw_data1) +{ + int swap_i, swap_k; + string * data; + string * nrzi_data; + longword raw_len; + word len, crc1, crc2; + int arq_mem = 0; + string s; + int i, k, n; + unsigned char * raw; + unsigned char * raw1; + char Mode[16] = ""; + + struct TDetector_t * pDET = &DET[emph][rcvr_nr]; + + // Decode RAW-stream + + raw_len = raw_data->Length; + + if (raw_len < 80) + return; + + mydelete(raw_data, raw_len - 6, 7); // Does this remove trailing flag + raw_len = raw_data->Length; + + nrzi_data = get_NRZI_data(raw_data, last_nrzi_bit); + + if (nrzi_data == NULL) + return; + +// data = newString(); + data = get_pkt_data(nrzi_data); + + len = data->Length; + + if (len < pkt_raw_min_len) + { + freeString(nrzi_data); + freeString(data); + return; + } + + crc1 = get_fcs(data->Data, len - 2); + crc2 = (data->Data[len - 1] << 8) | data->Data[len - 2]; + + // MEM recovery + + arq_mem = FALSE; + + if (raw_len > 2970) + freeString(nrzi_data); + else + { + Add(&pDET->mem_ARQ_buf[snd_ch], nrzi_data); + + if (pDET->mem_ARQ_buf[snd_ch].Count > MEMRecovery[snd_ch]) + Delete(&pDET->mem_ARQ_buf[snd_ch], 0); + + if (crc1 != crc2) + { + freeString(data); + data = get_pkt_data(memory_ARQ(&pDET->mem_ARQ_buf[snd_ch], nrzi_data)); + crc1 = get_fcs(data->Data, len - 2); + arq_mem = TRUE; + } + } + + if (crc1 == crc2) + { + if (arq_mem) + { + Debugprintf("Good CRC after Memory ARQ correction %x Len %d chan %d rcvr %d emph %d", crc1, len, snd_ch, rcvr_nr, emph); + stat_r_mem++; + + pDET->emph_decoded = 2; //MEM + pDET->rx_decoded = decodedMEM; + } + else + { + Debugprintf("Good CRC %x Len %d chan %d rcvr %d emph %d", crc1, len, snd_ch, rcvr_nr, emph); + + pDET->rx_decoded = decodedNormal; + pDET->emph_decoded = 4; //Normal + } + + + if (detect_list[snd_ch].Count > 0 && + my_indexof(&detect_list[snd_ch], data) >= 0) + { + // Already have a copy of this frame + + freeString(data); + Debugprintf("Discarding copy rcvr %d emph %d", rcvr_nr, emph); + return; + } + + string * xx = newString(); + memset(xx->Data, 0, 16); + + sprintf(Mode, "AX25 %d", centreFreq[snd_ch]); + + + Add(&detect_list_c[snd_ch], xx); + Add(&detect_list[snd_ch], data); + + if (arq_mem) + stringAdd(xx, "MEM", 3); + else + stringAdd(xx, "", 0); + + sprintf(Mode, "AX25 %d", centreFreq[snd_ch]); + + stringAdd(xx, Mode, strlen(Mode)); + + + return; + + } + + // Single bit recovery + + freeString(data); // finished with original + + if (recovery[snd_ch] == 0 || raw_len > 2970) + return; + + raw = raw_data->Data; + raw1 = raw_data1->Data; + + for (i = 0; i < raw_len; i++) + { + if (raw[i] != raw1[i]) + { + //change bit + raw[i] ^= 1; + + // get new data + + data = get_pkt_data2(raw_data, last_nrzi_bit); + + //restore bit + + raw[i] ^= 1; + + len = data->Length; + + if (len > pkt_raw_min_len) + { + crc1 = get_fcs(data->Data, len - 2); + crc2 = (data->Data[len - 1] << 8) | data->Data[len - 2]; + + if (crc1 == crc2) + { + Debugprintf("Good CRC after single bit correction %x Len %d chan %d rcvr %d emph %d", crc1, len, snd_ch, rcvr_nr, emph); + + if (detect_list[snd_ch].Count > 0 && + my_indexof(&detect_list[snd_ch], data) >=- 0) + { + // Already have a copy of this frame + + Debugprintf("Discarding copy rcvr %d, emph %d", rcvr_nr, emph); + freeString(data); + return; + } + string * xx = newString(); + memset(xx->Data, 0, 16); + + Add(&detect_list_c[snd_ch], xx); + Add(&detect_list[snd_ch], data); + stringAdd(xx, "SINGLE", 3); + + pDET->rx_decoded = decodedSingle; + pDET->emph_decoded = 1; //SINGLE + + return; + } + } + freeString(data); // finished with original + } + } +} + + + +int lastcrc = 0; + + +void make_rx_frame_PSK(int snd_ch, int rcvr_nr, int emph, string * data) +{ + word len, crc1, crc2; + + len = data->Length; + + if (len < pkt_raw_min_len) + return; + + crc1 = get_fcs(data->Data, len - 2); + crc2 = (data->Data[len - 1] << 8) | data->Data[len - 2]; + + if (crc1 == crc2) + { + struct TDetector_t * pDET = &DET[emph][rcvr_nr]; + + Debugprintf("Good CRC %x Len %d chan %d rcvr %d emph %d", crc1, len, snd_ch, rcvr_nr, emph); + + pDET->rx_decoded = decodedNormal; + pDET->emph_decoded = 4; //Normal + + if (detect_list[snd_ch].Count > 0 && + my_indexof(&detect_list[snd_ch], data) >= 0) + { + // Already have a copy of this frame + + Debugprintf("Discarding copy rcvr %d emph %d", rcvr_nr, emph); + return; + } + + string * xx = newString(); + + memset(xx->Data, 0, 16); + Add(&detect_list_c[snd_ch], xx); + xx = duplicateString(data); + Add(&detect_list[snd_ch], xx); + } +} + + + /* + +function memory_ARQ_FEC(buf: TStringList; data: string): string; +var + len,i,k: integer; + s,temp: string; + new_blk,temp_blk: TStringList; + n,err: byte; + done: boolean; +{ + s = ''; + if data='' then { result = s; exit; } + new_blk = TStringList.Create; + temp_blk = TStringList.Create; + temp = data; + len = length(data); + // Split new data; + repeat + n = ord(temp[1]) and $7F; + err = ord(temp[1]) and $80; + if err=0 then new_blk.Add(copy(temp,2,n)) else new_blk.Add(''); + delete(temp,1,n+1); + until temp=''; + // Search blocks + if (buf.Count>0) and (new_blk.Count>0) then + { + i = 0; + repeat + // If length is the same + if length(buf.Strings[i])=len then + { + temp = buf.Strings[i]; + // If last 4 bytes is the same + if copy(temp,len-3,4)=copy(data,len-3,4) then + { + temp_blk.Clear; + repeat + n = ord(temp[1]) and $7F; + err = ord(temp[1]) and $80; + if err=0 then temp_blk.Add(copy(temp,2,n)) else temp_blk.Add(''); + delete(temp,1,n+1); + until temp=''; + // Add new parts + if new_blk.Count=temp_blk.Count then + { + done = TRUE; + for k = 0 to new_blk.Count-1 do + { + if (new_blk.Strings[k]='') and (temp_blk.Strings[k]<>'') then + new_blk.Strings[k] = temp_blk.Strings[k]; + // Check if no empty data + if new_blk.Strings[k]='' then done = FALSE; + } + } + } + } + inc(i); + until (i=buf.Count) or done; + if done then for k = 0 to new_blk.Count-1 do s = s+new_blk.Strings[k]; + } + result = s; + new_blk.Free; + temp_blk.Free +} + +procedure add_to_ARQ_FEC(buf: TStringList; data: string); +{ + if buf.Count=50 then buf.Delete(0); + buf.Add(data); +} +*/ + +void make_rx_frame_FEC(int snd_ch, int rcvr_nr, string * data, string * fec_data, word nErr) +{ +} + +/*var + len,crc1,crc2: word; + s: string; + i,k,n: word; +{ + len = length(data); + if len<17 then exit; + crc1 = get_fcs(data,len-2); + crc2 = (ord(data[len]) shl 8) or ord(data[len-1]); + if crc1=crc2 then + { + if detect_list[snd_ch].Count>0 then + { + //if detect_list[snd_ch].IndexOf(data)<0 then + if my_indexof(detect_list[snd_ch],data)<0 then + { + detect_list[snd_ch].Add(data); + detect_list_c[snd_ch].Add('Err: '+inttostr(nErr)); + } + end + else + { + detect_list[snd_ch].Add(data); + detect_list_c[snd_ch].Add('Err: '+inttostr(nErr)); + } + add_to_ARQ_FEC(DET[0,rcvr_nr].mem_ARQ_F_buf[snd_ch],fec_data); + } + if crc1<>crc2 then + { + data = memory_ARQ_FEC(DET[0,rcvr_nr].mem_ARQ_F_buf[snd_ch],fec_data); + add_to_ARQ_FEC(DET[0,rcvr_nr].mem_ARQ_F_buf[snd_ch],fec_data); + if data<>'' then + { + len = length(data); + crc1 = get_fcs(data,len-2); + crc2 = (ord(data[len]) shl 8) or ord(data[len-1]); + if crc1=crc2 then + { + if detect_list[snd_ch].Count>0 then + { + //if detect_list[snd_ch].IndexOf(data)<0 then + if my_indexof(detect_list[snd_ch],data)<0 then + { + detect_list[snd_ch].Add(data); + detect_list_c[snd_ch].Add('MEM Err: '+inttostr(nErr)); + } + end + else + { + detect_list[snd_ch].Add(data); + detect_list_c[snd_ch].Add('MEM Err: '+inttostr(nErr)); + } + } + } + } +} + +*/ +//////////////////////////// PLL-Peak-detector //////////////////////////// + +void Mux3(int snd_ch, int rcvr_nr, int emph, float * src1, float * core, float *dest, float * prevI, float * prevQ, int tap, int buf_size) +{ + float pi2 = 2 * pi; + + int i; + float x; + float acc1, acc2, acc3, mag; + int tap4; + int tap_cnt; + unsigned int ii, kk; + + float Preemphasis6, Preemphasis12, MUX3_osc, AGC; + float AFC_IZ1, AFC_QZ1, AFC_IZ2, AFC_QZ2; + + // looks like this is an LPF + + // Get local copy of this detectors variables + + struct TDetector_t * pDET = &DET[emph][rcvr_nr]; + + Preemphasis6 = pDET->Preemphasis6[snd_ch]; + Preemphasis12 = pDET->Preemphasis12[snd_ch]; + MUX3_osc = pDET->MUX3_osc[snd_ch]; + AGC = pDET->AGC[snd_ch]; + AFC_IZ2 = pDET->AFC_IZ2[snd_ch]; + AFC_QZ2 = pDET->AFC_QZ2[snd_ch]; + AFC_QZ1 = pDET->AFC_QZ1[snd_ch]; + AFC_IZ1 = pDET->AFC_IZ1[snd_ch]; + // + tap4 = tap * 4; + x = active_rx_freq[snd_ch] * pi2 / RX_Samplerate; + + fmove(&prevI[buf_size], &prevI[0], tap4); + fmove(&prevQ[buf_size], &prevQ[0], tap4); + tap_cnt = tap; + + if (prevI[128] != prevI[128]) + prevI[128] = 0; + + for (i = 0; i < buf_size; i++) + { + // Pre-emphasis 6dB + if (emph > 0) + { + acc1 = Preemphasis6 - src1[i]; + Preemphasis6 = src1[i]; + src1[i] = acc1; + } + // Pre-emphasis 12dB + if (emph > 1) + { + acc1 = Preemphasis12 - src1[i]; + Preemphasis12 = src1[i]; + src1[i] = acc1; + } + // + MUX3_osc = MUX3_osc + x; + + if (MUX3_osc > pi2) + MUX3_osc = MUX3_osc - pi2; + + if (src1[i] != src1[i]) + src1[i] = 0; + + if (prevI[128] != prevI[128]) + prevI[128] = 0; + + + prevI[tap_cnt] = src1[i] * sinf(MUX3_osc); + prevQ[tap_cnt] = src1[i] * cosf(MUX3_osc); + + if (prevI[128] != prevI[128]) + prevI[tap_cnt] = src1[i] * sinf(MUX3_osc); + + if (prevI[128] != prevI[128]) + prevI[128] = 0; + /* + + mag = sqrtf(prevI[tap_cnt] * prevI[tap_cnt] + prevQ[tap_cnt] * prevQ[tap_cnt]); + DET[emph][rcvr_nr].AGC1[snd_ch] = 0.5*DET[emph][rcvr_nr].AGC1[snd_ch] + 0.5*mag; + AGC = 0.5*AGC + 0.5*DET[emph][rcvr_nr].AGC1[snd_ch]; + if (AGC > 1) + begin + prevI[tap_cnt] = prevI[tap_cnt] / AGC; + prevQ[tap_cnt] = prevQ[tap_cnt] / AGC; + end + */ + + + // Fast AGC + + mag = sqrtf(prevI[tap_cnt] * prevI[tap_cnt] + prevQ[tap_cnt] * prevQ[tap_cnt]); + + AGC = 0.5 * AGC + 0.5 *mag; + + if (AGC > 1) + { + prevI[tap_cnt] = prevI[tap_cnt] / AGC; + prevQ[tap_cnt] = prevQ[tap_cnt] / AGC; + } + + ii = i << 2; + kk = tap << 2; + + // C version of delphi asm code below + { + float accum = 0.0f; + float fp1; + + int ebx; + float * edi; + + edi = &prevI[i]; + ebx = 0; + accum = 0.0f; + + fsk_k1: + + // FLD pushes operand onto stack, so old value goes to fp1 + + fp1 = accum; + accum = edi[ebx]; + if (accum != accum) + accum = 0; + + accum *= core[ebx]; + if (accum != accum) + accum = 0; + accum += fp1; + if (accum != accum) + accum = 0; + + ebx++; + if (ebx != tap) + goto fsk_k1; + + acc1 = accum; + + if (acc1 != acc1) + acc1 = 0; + + edi = &prevQ[i]; + + ebx = 0; + accum = 0.0f; + + fsk_k2: + + fp1 = accum; + accum = edi[ebx]; + accum *= core[ebx]; + accum += fp1; + + ebx++; + if (ebx != tap) + goto fsk_k2; + + acc2 = accum; + } + + if (acc1 != acc1) + acc1 = 0; + + + tap_cnt++; + + /// PLL-Detector /// + + + dest[i] = (acc1 - AFC_IZ2)*AFC_QZ1 - (acc2 - AFC_QZ2)*AFC_IZ1; + + // Check for NAN + + if (dest[i] != dest[i]) + dest[i] = 0.0f; + + AFC_IZ2 = AFC_IZ1; + AFC_QZ2 = AFC_QZ1; + AFC_IZ1 = acc1; + AFC_QZ1 = acc2; + } + + pDET->Preemphasis6[snd_ch] = Preemphasis6; + pDET->Preemphasis12[snd_ch] = Preemphasis12; + pDET->MUX3_osc[snd_ch] = MUX3_osc; + pDET->AGC[snd_ch] = AGC; + pDET->AFC_IZ2[snd_ch] = AFC_IZ2; + pDET->AFC_QZ2[snd_ch] = AFC_QZ2; + pDET->AFC_QZ1[snd_ch] = AFC_QZ1; + pDET->AFC_IZ1[snd_ch] = AFC_IZ1; +} + + + +void Mux3_PSK(int snd_ch, int rcvr_nr, int emph, float * src1, float * core, float *destI, float *destQ, float * prevI, float * prevQ, int tap, int buf_size) +{ + float pi2 = 2 * pi; + + int i; + float x; + float acc1, acc2, mag; + int tap4; + int prev_cnt, tap_cnt; + + float Preemphasis6, Preemphasis12, MUX3_osc; + + // looks like this is an LPF + + // Get local copy of this detectors variables + + struct TDetector_t * pDET = &DET[emph][rcvr_nr]; + + Preemphasis6 = pDET->Preemphasis6[snd_ch]; + Preemphasis12 = pDET->Preemphasis12[snd_ch]; + MUX3_osc = pDET->MUX3_osc[snd_ch]; + + tap4 = tap * 4; + + x = active_rx_freq[snd_ch] * pi2 / RX_Samplerate; + + fmove(&prevI[buf_size], &prevI[0], tap4); + fmove(&prevQ[buf_size], &prevQ[0], tap4); + + tap_cnt = tap; + + if (prevI[128] != prevI[128]) + prevI[128] = 0; + + for (i = 0; i < buf_size; i++) + { + // Pre-emphasis 6dB + if (emph > 0) + { + acc1 = Preemphasis6 - src1[i]; + Preemphasis6 = src1[i]; + src1[i] = acc1; + } + // Pre-emphasis 12dB + if (emph > 1) + { + acc1 = Preemphasis12 - src1[i]; + Preemphasis12 = src1[i]; + src1[i] = acc1; + } + + MUX3_osc = MUX3_osc + x; + + if (MUX3_osc > pi2) + MUX3_osc = MUX3_osc - pi2; + + prevI[tap_cnt] = src1[i] * sinf(MUX3_osc); + prevQ[tap_cnt] = src1[i] * cosf(MUX3_osc); + + + // C version of delphi asm code + { + float accum = 0.0f; + float fp1; + + int ebx; + float * edi; + + edi = &prevI[i]; + ebx = 0; + accum = 0.0f; + + fsk_k1: + + // FLD pushes operand onto stack, so old value goes to fp1 + + fp1 = accum; + accum = edi[ebx]; + accum *= core[ebx]; + accum += fp1; + + ebx++; + + if (ebx != tap) + goto fsk_k1; + + acc1 = accum; + + edi = &prevQ[i]; + + ebx = 0; + accum = 0.0f; + + fsk_k2: + + fp1 = accum; + accum = edi[ebx]; + accum *= core[ebx]; + accum += fp1; + + ebx++; + if (ebx != tap) + goto fsk_k2; + + acc2 = accum; + } + + if (acc1 != acc1) + acc1 = 0; + + tap_cnt++; + + destI[i] = acc1; + destQ[i] = acc2; + } + + pDET->Preemphasis6[snd_ch] = Preemphasis6; + pDET->Preemphasis12[snd_ch] = Preemphasis12; + pDET->MUX3_osc[snd_ch] = MUX3_osc; + +} + +int stats[2] = { 0 }; + +#define dcd_corr 0.11111f + +void decode_stream_MPSK(int snd_ch, int rcvr_nr, float * src, int buf_size, int last) +{ + +#ifndef WIN32 + + // Until ASM is converted + + return; +} +#else + + float pi2 = 2 * pi; + +#define NR_FEC_CH 3 + + float agc_fast = 0.01f; + float agc_fast1 = 1 - agc_fast; + float agc_slow = agc_fast / 4; + float agc_slow1 = 1 - agc_slow; + + word dcnt, dsize; + word i, k, j, j1, j2, j3; + single x, x1; + single amp, acc1, acc2; + single sumI, sumQ, sumIQ, muxI, muxQ; + word tap_cnt, tap_cnt1; + single afc_lim; + word i_tap, tap; + single AFC, k1, k2, freq; + single maxval, div_bit_afc, baudrate; + word max_cnt; + single AmpI, AmpQ, angle, muxI1, muxQ1, muxI2, muxQ2, sumIQ1, sumIQ2; + single AFC_acc1, AFC_acc2; + single BIT_acc1, BIT_acc2; + integer AFC_newpkpos; + // + single threshol; + single tr; + Byte fec_ch, bit; + longword ii, kk; + single * core, *prevI, *prevQ; + // + unsigned long long bit64 = 0; + boolean hdr_ok; + Byte fec_code; + string fec_data_blk; + string line1; + + Byte line[512]; + int linelen = 0; + + integer nErr; + word crc1, crc2, size; + + Byte hdr_byte[15] = ""; + + tr = dcd_threshold * dcd_corr; + + if (last) + { + if (dcd_hdr_cnt[snd_ch] == 0) + dcd_on_hdr[snd_ch] = 0; + + dcd_bit_cnt[snd_ch] = 0; + } + + baudrate = 400; + div_bit_afc = 1.0f / roundf(BIT_AFC*(RX_Samplerate / 11025)); + x1 = baudrate / RX_Samplerate; + max_cnt = roundf(RX_Samplerate / baudrate); + // + + afc_lim = rx_baudrate[snd_ch] * 0.1f; + dsize = buf_size / n_INTR[snd_ch]; + tap = LPF_tap[snd_ch]; + i_tap = INTR_tap[snd_ch]; + freq = active_rx_freq[snd_ch]; + + + for (fec_ch = 0; fec_ch <= NR_FEC_CH; fec_ch++) + { + struct TMChannel_t * pMChan = &DET[0][rcvr_nr].MChannel[snd_ch][fec_ch]; + + fmove(&pMChan->prev_dLPFI_buf[buf_size], &pMChan->prev_dLPFI_buf[0], i_tap * 4); + fmove(&pMChan->prev_dLPFQ_buf[buf_size], &pMChan->prev_dLPFQ_buf[0], i_tap * 4); + fmove(&pMChan->prev_LPF1I_buf[dsize], &pMChan->prev_LPF1I_buf[0], tap * 4); + fmove(&pMChan->prev_LPF1Q_buf[dsize], &pMChan->prev_LPF1Q_buf[0], tap * 4); + fmove(&pMChan->prev_AFCI_buf[dsize], &pMChan->prev_AFCI_buf[0], tap * 4); + fmove(&pMChan->prev_AFCQ_buf[dsize], &pMChan->prev_AFCQ_buf[0], tap * 4); + } + + tap_cnt = i_tap; + tap_cnt1 = tap; + dcnt = 0; + k = 0; + + for (i = 0; i < buf_size; i++) + { + for (fec_ch = 0; fec_ch <= NR_FEC_CH; fec_ch++) + { + struct TDetector_t * pDET = &DET[0][rcvr_nr]; + + x = (freq + pDET->AFC_dF[snd_ch] + ch_offset[fec_ch])*pi2 / RX_Samplerate; + + struct TMChannel_t * pMChan = &pDET->MChannel[snd_ch][fec_ch]; + { + pMChan->MUX_osc = pMChan->MUX_osc + x; + + if (pMChan->MUX_osc > pi2) + pMChan->MUX_osc = pMChan->MUX_osc - pi2; + + pMChan->prev_dLPFI_buf[tap_cnt] = src[i] * sinf(pMChan->MUX_osc); + pMChan->prev_dLPFQ_buf[tap_cnt] = src[i] * cosf(pMChan->MUX_osc); + prevI = pMChan->prev_dLPFI_buf; + prevQ = pMChan->prev_dLPFQ_buf; + core = INTR_core[snd_ch]; + // Decimation filter + ii = i << 2; + kk = i_tap << 2; + + _asm + { + push eax; + push ebx; + push edi; + push esi; + mov edi, prevI; + mov esi, core; + add edi, ii; + mov eax, kk; + xor ebx, ebx; + fldz; + lk1: + fld dword ptr[edi + ebx]; + fmul dword ptr[esi + ebx]; + fadd; + add ebx, 4; + cmp ebx, eax; + jne lk1; + fstp dword ptr acc1; + wait; + mov edi, prevQ; + add edi, ii; + xor ebx, ebx; + fldz; + lk2: + fld dword ptr[edi + ebx]; + fmul dword ptr[esi + ebx]; + fadd; + add ebx, 4; + cmp ebx, eax; + jne lk2; + fstp dword ptr acc2; + wait; + pop esi; + pop edi; + pop ebx; + pop eax; + } + } + + if (fec_ch == NR_FEC_CH) + tap_cnt++; + + // Decimation + + if (dcnt == 0) + { + { + pMChan->prev_LPF1I_buf[tap_cnt1] = acc1; + pMChan->prev_LPF1Q_buf[tap_cnt1] = acc2; + pMChan->prev_AFCI_buf[tap_cnt1] = acc1; + pMChan->prev_AFCQ_buf[tap_cnt1] = acc2; + // Bit-filter + prevI = pMChan->prev_LPF1I_buf; + prevQ = pMChan->prev_LPF1Q_buf; + core = LPF_core[snd_ch]; + ii = k << 2; + kk = tap << 2; + + __asm + { + push eax; + push ebx; + push edi; + push esi; + mov edi, prevI; + mov esi, core; + add edi, ii; + mov eax, kk; + xor ebx, ebx; + fldz; + xk1: + fld dword ptr[edi + ebx]; + fmul dword ptr[esi + ebx]; + fadd; + add ebx, 4; + cmp ebx, eax; + jne xk1; + fstp dword ptr BIT_acc1; + wait; + mov edi, prevQ; + add edi, ii; + xor ebx, ebx; + fldz; + xk2: + fld dword ptr[edi + ebx]; + fmul dword ptr[esi + ebx]; + fadd; + add ebx, 4; + cmp ebx, eax; + jne xk2; + fstp dword ptr BIT_acc2; + wait; + pop esi; + pop edi; + pop ebx; + pop eax; + } + + // AFC-filter + prevI = pMChan->prev_AFCI_buf; + prevQ = pMChan->prev_AFCQ_buf; + core = AFC_core[snd_ch]; + ii = k << 2; + kk = tap << 2; + _asm + { + push eax; + push ebx; + push edi; + push esi; + mov edi, prevI; + mov esi, core; + add edi, ii; + mov eax, kk; + xor ebx, ebx; + fldz; + xxk1: + fld dword ptr[edi + ebx]; + fmul dword ptr[esi + ebx]; + fadd; + add ebx, 4; + cmp ebx, eax; + jne xxk1; + fstp dword ptr AFC_acc1; + wait; + mov edi, prevQ; + add edi, ii; + xor ebx, ebx; + fldz; + xxk2: + fld dword ptr[edi + ebx]; + fmul dword ptr[esi + ebx]; + fadd; + add ebx, 4; + cmp ebx, eax; + jne xxk2; + fstp dword ptr AFC_acc2; + wait; + pop esi; + pop edi; + pop ebx; + pop eax; + } + } + + // AGC + + amp = sqrtf(BIT_acc1*BIT_acc1 + BIT_acc2 * BIT_acc2); + if (amp > pDET->PSK_AGC[snd_ch]) + pDET->PSK_AGC[snd_ch] = pDET->PSK_AGC[snd_ch] * agc_fast1 + amp*agc_fast; + else + pDET->PSK_AGC[snd_ch] = pDET->PSK_AGC[snd_ch] * agc_slow1 + amp*agc_slow; + + if (pDET->PSK_AGC[snd_ch] > 1) + { + BIT_acc1 = BIT_acc1 / pDET->PSK_AGC[snd_ch]; + BIT_acc2 = BIT_acc2 / pDET->PSK_AGC[snd_ch]; + AFC_acc1 = AFC_acc1 / pDET->PSK_AGC[snd_ch]; + AFC_acc2 = AFC_acc2 / pDET->PSK_AGC[snd_ch]; + amp = amp / pDET->PSK_AGC[snd_ch]; + } + + // AFC Correction + + + sumIQ = (AFC_acc1 - pMChan->AFC_IZ2)*pMChan->AFC_QZ1 - (AFC_acc2 - pMChan->AFC_QZ2)*pMChan->AFC_IZ1; + pMChan->AFC_IZ2 = pMChan->AFC_IZ1; + pMChan->AFC_QZ2 = pMChan->AFC_QZ1; + pMChan->AFC_IZ1 = AFC_acc1; + pMChan->AFC_QZ1 = AFC_acc2; + + pDET->AFC_dF[snd_ch] = pDET->AFC_dF[snd_ch] - sumIQ * 0.07f; // AFC_LPF=1 + + if (pDET->AFC_dF[snd_ch] > afc_lim) + pDET->AFC_dF[snd_ch] = afc_lim; + + if (pDET->AFC_dF[snd_ch] < -afc_lim) + pDET->AFC_dF[snd_ch] = -afc_lim; + + + pMChan->AFC_bit_buf1I[pDET->AFC_cnt[snd_ch]] = BIT_acc1; + pMChan->AFC_bit_buf1Q[pDET->AFC_cnt[snd_ch]] = BIT_acc2; + pMChan->AFC_bit_buf2[pDET->AFC_cnt[snd_ch]] = amp; + + if (fec_ch == NR_FEC_CH) + { + pDET->AFC_cnt[snd_ch]++; + pDET->AFC_bit_osc[snd_ch] = pDET->AFC_bit_osc[snd_ch] + x1; + + if (pDET->AFC_bit_osc[snd_ch] >= 1) + { + // Find the maximum in the synchronization buffer + + for (j = 0; j <= NR_FEC_CH; j++) + { + struct TMChannel_t * pMChan = &pDET->MChannel[snd_ch][j]; + + maxval = 0; + + for (j1 = 0; j1 < pDET->AFC_cnt[snd_ch]; j1++) + { + amp = pMChan->AFC_bit_buf2[j1]; + + pDET->AFC_bit_buf[snd_ch][j1] = pDET->AFC_bit_buf[snd_ch][j1] * 0.95 + amp*0.05; + + if (pDET->AFC_bit_buf[snd_ch][j1] > maxval) + { + { + AFC_newpkpos = j1; + maxval = pDET->AFC_bit_buf[snd_ch][j1]; + } + } + + k1 = 1.0f *AFC_newpkpos / (pDET->AFC_cnt[snd_ch] - 1); + k2 = pila(k1) - 1; + + + + //AFC = div_bit_afc*k2; + AFC = div_bit_afc * k2*0.25; //for 4 carriers + if (k1 > 0.5) + pDET->AFC_bit_osc[snd_ch] = pDET->AFC_bit_osc[snd_ch] + AFC; + else + pDET->AFC_bit_osc[snd_ch] = pDET->AFC_bit_osc[snd_ch] - AFC; + + //DCD feature + + if (last) + { + DCD_LastPkPos[snd_ch] = DCD_LastPkPos[snd_ch] * 0.96f + AFC_newpkpos * 0.04f; + DCD_LastPerc[snd_ch] = DCD_LastPerc[snd_ch] * 0.96f + abs(AFC_newpkpos - DCD_LastPkPos[snd_ch])*0.04f; + if (DCD_LastPerc[snd_ch] >= tr || DCD_LastPerc[snd_ch] < 0.00001f) + dcd_bit_cnt[snd_ch] = dcd_bit_cnt[snd_ch] + 1; + else + dcd_bit_cnt[snd_ch] = dcd_bit_cnt[snd_ch] - 1; + } + + } + // Bit-detector + + AmpI = pMChan->AFC_bit_buf1I[AFC_newpkpos]; + AmpQ = pMChan->AFC_bit_buf1Q[AFC_newpkpos]; + muxI1 = AmpI * pMChan->AFC_IIZ1; + muxI2 = AmpQ * pMChan->AFC_IIZ1; + muxQ1 = AmpQ * pMChan->AFC_QQZ1; + muxQ2 = AmpI * pMChan->AFC_QQZ1; + sumIQ1 = muxI1 + muxQ1; + sumIQ2 = muxI2 - muxQ2; + angle = atan2f(sumIQ2, sumIQ1); + pMChan->AFC_IIZ1 = AmpI; + pMChan->AFC_QQZ1 = AmpQ; + + // Phase corrector + + if (fabsf(angle) < PI5) + pMChan->AngleCorr = pMChan->AngleCorr * 0.9f - angle * 0.1f; + else + { + if (angle > 0) + pMChan->AngleCorr = pMChan->AngleCorr * 0.9f + (pi - angle)*0.1f; + else + pMChan->AngleCorr = pMChan->AngleCorr * 0.9f + (-pi - angle)*0.1f; + } + angle = angle + pMChan->AngleCorr; + + + if (fabsf(angle) < PI5) + bit = RX_BIT1; + else + bit = RX_BIT0; + + // DCD on flag + + if (last) + { + if (dcd_hdr_cnt[snd_ch] > 0) + dcd_hdr_cnt[snd_ch]--; + + DCD_header[snd_ch] = (DCD_header[snd_ch] >> 1) | (bit << 24); + + if ((DCD_header[snd_ch] & 0xFFFF0000) == 0x7E7E0000 || + (DCD_header[snd_ch] & 0xFFFFFF00) == 0x7E000000 || + (DCD_header[snd_ch] & 0xFFFFFF00) == 0x00000000) + { + dcd_hdr_cnt[snd_ch] = 48; + dcd_on_hdr[snd_ch] = TRUE; + } + } + + // header stream + bit64 = bit; + bit64 <<= 56; + + pDET->FEC_header1[snd_ch][1] = (pDET->FEC_header1[snd_ch][1] >> 1) | (pDET->FEC_header1[snd_ch][0] << 63); + pDET->FEC_header1[snd_ch][0] = (pDET->FEC_header1[snd_ch][0] >> 1) | bit64; + + // copy body + if (pDET->frame_status[snd_ch] == FRAME_LOAD) + { + pDET->bit_stream[snd_ch] = (pDET->bit_stream[snd_ch] >> 1) + bit; + pDET->bit_cnt[snd_ch]++; + if (pDET->bit_cnt[snd_ch] == 8) + { + pDET->bit_cnt[snd_ch] = 0; + pDET->FEC_len_cnt[snd_ch]++; + + stringAdd(&pDET->FEC_rx_data[snd_ch], &pDET->bit_stream[snd_ch], 1); + + if (pDET->FEC_len_cnt[snd_ch] == pDET->FEC_len[snd_ch]) + { + // descrambler + scrambler(pDET->FEC_rx_data[snd_ch].Data, pDET->FEC_rx_data[snd_ch].Length); + // deinterleave + pDET->FEC_blk_int[snd_ch] = ((pDET->FEC_len[snd_ch] - 1) / 16) + 1; + + linelen = pDET->FEC_rx_data[snd_ch].Length; + + memcpy(line, pDET->FEC_rx_data[snd_ch].Data, linelen); + + j3 = 1; + + for (j1 = 0; j1 < 16; j1++) + { + for (j2 = 0; j2 < pDET->FEC_blk_int[snd_ch]; j2++) + { + if ((j2 * 16 + j1) <= pDET->FEC_len[snd_ch] && j3 <= pDET->FEC_len[snd_ch]) + { + pDET->FEC_rx_data[snd_ch].Data[j2 * 16 + j1] = line[j3]; + j3++; + } + } + } + + // RS-decode + + /* + + line = pDET->FEC_rx_data[snd_ch]; + pDET->FEC_rx_data[snd_ch].Length = 0; + do + { + line1 = copy(line, 1, 16); + size = length(line1); + FillChar(xEncoded, SizeOf(xEncoded), 0); + FillChar(xDecoded, SizeOf(xDecoded), 0); + move(line1[1], xEncoded[0], size); + RS.InitBuffers; + nErr = RS.DecodeRS(xEncoded, xDecoded); + line1 = ''; + for j1 = MaxErrors * 2 to size - 1 do line1 = line1 + chr(xDecoded[j1]); + pDET->FEC_rx_data[snd_ch] = FEC_rx_data[snd_ch] + line1; + if nErr >= 0 then FEC_err[snd_ch] = FEC_err[snd_ch] + nErr; + // For MEM-ARQ + fec_code = length(line1) and $7F; + if nErr < 0 then fec_code = fec_code or $80; + fec_data_blk = fec_data_blk + chr(fec_code) + line1; + delete(line, 1, 16); + } while(line.Count); + */ + + + make_rx_frame_FEC(snd_ch, rcvr_nr, &pDET->FEC_rx_data[snd_ch], &fec_data_blk, pDET->FEC_err[snd_ch]); + pDET->FEC_rx_data[snd_ch].Length = 0; + pDET->frame_status[snd_ch] = FRAME_WAIT; + pDET->FEC_header1[snd_ch][0] = 0; + pDET->FEC_header1[snd_ch][1] = 0; + } + } + } + + hdr_ok = FALSE; + + // I think FEC_header1[0] and FEC_header1[1] form the 128 bit header + // We look for a pair of flags, but allow a few bits to be wrong + // as FEC may fix them + + if (pDET->frame_status[snd_ch] == FRAME_WAIT) + { + j1 = (pDET->FEC_header1[snd_ch][1] >> 16) ^ 0x7E7E; + /*_asm + { + push ax; + push bx; + push cx; + mov ax, 15; + mov bx, j1; + zloop: + mov cx, bx; + and cx, 1; + cmp cx, 1; + jne is_zero; + inc ah; // count damaged bits + is_zero: + shr bx, 1; + dec al; + jnz zloop; + cmp ah, 5; // greater than 4 bits + jnb greater; + mov hdr_ok, TRUE; + greater: + pop cx; + pop bx; + pop ax; + } + */ + } + //if (FEC_header1[snd_ch][1] shr 24 and $FF=$7E) and (frame_status[snd_ch]=FRAME_WAIT) then + + if (hdr_ok) + { + // Have up to 4 bits wrong in 7E7E pattern + + // Extract header, check crc then try RS + + hdr_ok = FALSE; + +// if ((pDET->FEC_header1[snd_ch][1] & 0xffff0000) == 0x7E7E0000) +// { + + hdr_byte[13] = (pDET->FEC_header1[snd_ch][1] >> 24) & 0xFF; + hdr_byte[14] = (pDET->FEC_header1[snd_ch][1] >> 16) & 0xFF; + + if (hdr_byte[13] == 0x7E && hdr_byte[14] == 0x7E) + { + hdr_byte[1] = (pDET->FEC_header1[snd_ch][0] >> 56) & 0xFF; + hdr_byte[2] = (pDET->FEC_header1[snd_ch][0] >> 48) & 0xFF; + hdr_byte[3] = (pDET->FEC_header1[snd_ch][0] >> 40) & 0xFF; + hdr_byte[4] = (pDET->FEC_header1[snd_ch][0] >> 32) & 0xFF; + hdr_byte[5] = (pDET->FEC_header1[snd_ch][0] >> 24) & 0xFF; + hdr_byte[6] = (pDET->FEC_header1[snd_ch][0] >> 16) & 0xFF; + hdr_byte[7] = (pDET->FEC_header1[snd_ch][0] >> 8) & 0xFF; + hdr_byte[8] = pDET->FEC_header1[snd_ch][0] & 0xFF; + hdr_byte[9] = (pDET->FEC_header1[snd_ch][1] >> 56) & 0xFF; + hdr_byte[10] = (pDET->FEC_header1[snd_ch][1] >> 48) & 0xFF; + hdr_byte[11] = (pDET->FEC_header1[snd_ch][1] >> 40) & 0xFF; + hdr_byte[12] = (pDET->FEC_header1[snd_ch][1] >> 32) & 0xFF; + + pDET->FEC_len[snd_ch] = hdr_byte[12] << 8 + hdr_byte[11]; + line[0] = 0x7E; + line[1] = 0x7E; + line[2] = hdr_byte[12]; + line[3] = hdr_byte[11]; + + crc1 = (hdr_byte[10] << 8) + hdr_byte[9]; + crc2 = get_fcs(line, 4); + + if (crc1 == crc2) + hdr_ok = TRUE; + + Debugprintf("Len %d CRC %x %x", pDET->FEC_len[snd_ch], crc1, crc2); + } + /* if (!hdr_ok) + { + linelen = 0; + for (j1 = 14; j1 > 0; j1-) + line[linelen++] = hdr_byte[j1); + + + + FillChar(xEncoded, SizeOf(xEncoded), 0); + FillChar(xDecoded, SizeOf(xDecoded), 0); + line = copy(&line, 7, 8) + copy(&line, 1, 6); + move(&line[1], xEncoded[0], 14); + RS.InitBuffers; + nErr = RS.DecodeRS(xEncoded, xDecoded); + if (nErr > -1) + { + line.Length = 0; + + for (j1 = 8; j1 < 13; j1++) + stringAdd(&line, &xDecoded[j1], 1); + + if (line[1] == 0x7E && line[2] == 0x7E) + { + FEC_len[snd_ch] = ord(line[3]) shl 8 + ord(line[4]); + crc1 = (line[5] << 8) + line[6]); + line = copy(line, 1, 4); + crc2 = get_fcs(line, 4); + if (crc1 == crc2) + hdr_ok = TRUE; + } + } + } + */ + if (hdr_ok) + { + pDET->FEC_len[snd_ch] = pDET->FEC_len[snd_ch] & 1023; //limit of length + if (pDET->FEC_len[snd_ch] > 0) + { + pDET->frame_status[snd_ch] = FRAME_LOAD; + pDET->FEC_len_cnt[snd_ch] = 0; + pDET->bit_cnt[snd_ch] = 0; + pDET->FEC_err[snd_ch] = 0; + pDET->FEC_rx_data[snd_ch].Length = 0; + fec_data_blk.Length = 0; + } + } + } + } + // Finalize + if (pDET->AFC_cnt[snd_ch] <= max_cnt) + for (j = pDET->AFC_cnt[snd_ch]; j <= max_cnt + 5; j++) + pDET->AFC_bit_buf[snd_ch][j] = 0.95f*pDET->AFC_bit_buf[snd_ch][j]; + + pDET->AFC_cnt[snd_ch] = 0; + pDET->AFC_bit_osc[snd_ch] = pDET->AFC_bit_osc[snd_ch] - 1; + } + } + if (fec_ch == NR_FEC_CH) + { + tap_cnt1++; + k++; + } + } + } + dcnt = (dcnt + 1) % n_INTR[snd_ch]; + } +} + +#endif + +void make_rx_frame_FX25(int snd_ch, int rcvr_nr, int emph, string * data) +{ + struct TDetector_t * pDET = &DET[emph][rcvr_nr]; + + word len, crc1, crc2; + + len = data->Length; + + if (len < pkt_raw_min_len) + { + free(data); + return; + } + + crc1 = get_fcs(data->Data, len - 2); + crc2 = (data->Data[len - 1] << 8) | data->Data[len - 2]; + + if (crc1 != crc2) + { + freeString(data); + return; + } + Debugprintf("FEC Good CRC %x Len %d chan %d rcvr %d emph %d", crc1, len, snd_ch, rcvr_nr, emph); + + pDET->rx_decoded = decodedFEC; + + if (detect_list[snd_ch].Count > 0) + { + //if detect_list[snd_ch].IndexOf(data)<0 then + + if (my_indexof(&detect_list[snd_ch], data) < 0) + { + string * xx = newString(); + xx->Length = sprintf(xx->Data, "FX25 %d", centreFreq[snd_ch]); + + + Add(&detect_list_c[snd_ch], xx); + Add(&detect_list[snd_ch], data); + + stringAdd(xx, "", 0); + } + else + { + // Should check if previous decode was Single or MEM and if so replace + + Debugprintf("Discarding copy rcvr %d", rcvr_nr); + freeString(data); + } + } + else + { + string * xx = newString(); + xx->Length = sprintf(xx->Data, "FX25 %d", centreFreq[snd_ch]); + + Add(&detect_list_c[snd_ch], xx); + Add(&detect_list[snd_ch], data); + + if (rcvr_nr == 0) + pDET->emph_decoded = 3; //FX.25 + } + +} + + +string * decode_FX25_data(TFX25 fx25) +{ + integer eras_pos = 0, i, j, len, rs_res; + Byte a, k; + Byte bit, byte_rx, bit_stuff_cnt, bit_cnt = 0, frame_status, bit_stream; + + string * data = newString(); + + int done; + Byte rs_block[256]; + int RSOK; + + bit_stream = 0; + len = fx25.size - fx25.rs_size; + frame_status = FRAME_WAIT; + + done = 0; + + // RS FEC + + memset(rs_block, 0, 255); + memcpy(rs_block, fx25.data.Data, len); + memcpy(&rs_block[255 - fx25.rs_size], &fx25.data.Data[len], fx25.rs_size); + + rs_res = fx25_decode_rs(rs_block, &eras_pos, 0, 0, fx25.rs_size); + + if (rs_res == -1) + { + Debugprintf("RS Correction Failed"); + return data; + } + + if (rs_res == 0) + Debugprintf("RS No Errors"); + else + Debugprintf("RS %d Errors Corrected", rs_res); + + // need to do ax.25 decode of bit stream + + i = 0; + + while (i < len) + { + a = rs_block[i]; + i++; + for (k = 0; k < 8; k++) + { + bit = a << 7; + a = a >> 1; + + bit_stream = (bit_stream >> 1) | bit; + + if ((bit_stream & FRAME_FLAG) == FRAME_FLAG && frame_status == FRAME_LOAD) + { + frame_status = FRAME_WAIT; + + if (bit_cnt == 6 && data->Length) + return data; + } + + if (frame_status == FRAME_LOAD) + { + if (bit_stuff_cnt == 5) + bit_stuff_cnt = 0; + else + { + if (bit == RX_BIT1) + bit_stuff_cnt++; + else + bit_stuff_cnt = 0; + + byte_rx = (byte_rx >> 1) | bit; + bit_cnt++; + } + + if (bit_cnt == 8) + { + stringAdd(data, &byte_rx, 1); + bit_cnt = 0; + } + } + + if ((bit_stream && FRAME_FLAG == FRAME_FLAG) && frame_status == FRAME_WAIT) + { + frame_status = FRAME_LOAD; + bit_cnt = 0; + bit_stuff_cnt = 0; + data->Length = 0; + } + } + } + return data; +} + +int FX25_corr[4] = {1, 1, 1, 1}; + +#define tags_nr 11 +#define tt 8 + +unsigned long long tags[tags_nr] = +{ + 0xB74DB7DF8A532F3E, 0x26FF60A600CC8FDE, 0xC7DC0508F3D9B09E, 0x8F056EB4369660EE, + 0x6E260B1AC5835FAE, 0xFF94DC634F1CFF4E, 0x1EB7B9CDBC09C00E, 0xDBF869BD2DBB1776, + 0x3ADB0C13DEAE2836, 0xAB69DB6A543188D6, 0x4A4ABEC4A724B796 +}; + +int sizes[tags_nr] = { 255, 144, 80, 48, 255, 160, 96, 64, 255, 192, 128 }; +int rs_sizes[tags_nr] = { 16, 16, 16, 16, 32, 32, 32, 32, 64, 64, 64 }; + +/* +unsigned char get_corr_arm(unsigned long long n) +{ + unsigned char max_corr; + unsigned char result = 255; + int i = 0; + + while (i < tags_nr) + { + if (__builtin_popcountll(n ^ tags[i] <= tt)) + return i; + } + + return 255; +} +*/ + +char errors; + + +unsigned char get_corr(unsigned long long val) +{ + unsigned long v; + unsigned long long n; + int i = 0; + + while (i < tags_nr) + { + n = val ^ tags[i]; + + v = n; + + v = v - ((v >> 1) & 0x55555555); // reuse input as temporary + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp + errors = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count + + if (errors > tt) + { + i++; + continue; + } + + v = n >> 32; + + v = v - ((v >> 1) & 0x55555555); // reuse input as temporary + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp + errors += ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count + + if (errors <= tt) + return i; + + i++; + } + return 255; +} + + + +void decode_stream_FSK(int last, int snd_ch, int rcvr_nr, int emph, float * src_buf, float * bit_buf, int buf_size, string * data) +{ + int i, k, j, n; + UCHAR bit; + UCHAR raw_bit; + UCHAR raw_bit1; + UCHAR raw_bit2; + float AFC, x, amp, k1, k2; + float baudrate; + float div_bit_afc; + word max_cnt; + float threshold; + float tr; + float Freq; + + Byte sample_cnt; + float PkAmp, PkAmpMax = 0, MaxAmp, MinAmp, AverageAmp; + int newpkpos; + float bit_osc; + Byte last_rx_bit, bit_stream, frame_status; + + TFX25 fx25; + unsigned long long tag64; + boolean rx_fx25_mode; + + // get saved values to local variables to speed up access + + struct TDetector_t * pDET = &DET[emph][rcvr_nr]; + + last_rx_bit = pDET->last_rx_bit[snd_ch]; + sample_cnt = pDET->sample_cnt[snd_ch]; + PkAmp = pDET->PkAmp[snd_ch]; + PkAmpMax = pDET->PkAmpMax[snd_ch]; + newpkpos = pDET->newpkpos[snd_ch]; + bit_osc = pDET->bit_osc[snd_ch]; + MaxAmp = pDET->MaxAmp[snd_ch]; + MinAmp = pDET->MinAmp[snd_ch]; + AverageAmp = pDET->AverageAmp[snd_ch]; + bit_stream = pDET->bit_stream[snd_ch]; + frame_status = pDET->frame_status[snd_ch]; + + fx25 = pDET->fx25[snd_ch]; + + if (fx25_mode[snd_ch] == FX25_MODE_NONE) + { + rx_fx25_mode = FALSE; + fx25.status = FX25_TAG; + } + else + rx_fx25_mode = TRUE; + + + tr = dcd_threshold * dcd_corr; + + if (last) + { + // Update DCD status + + if (dcd_hdr_cnt[snd_ch] == 0) + dcd_on_hdr[snd_ch] = 0; + + dcd_bit_cnt[snd_ch] = 0; + } + + // src_buf is input samples processed in some way. + // not sure what bit_buf is, but guess bits extracted from samples + // but then why floats ?? + + baudrate = 300; + + div_bit_afc = 1.0f / roundf(BIT_AFC*(RX_Samplerate / 11025.0f)); + + x = baudrate / RX_Samplerate; + + // I guess max_cnt is samples per bit + + //was - why + 1 then round?? + max_cnt = roundf(RX_Samplerate / baudrate) + 1; + + max_cnt = (RX_Samplerate / baudrate); + + for (i = 0; i < buf_size; i++) + { + // Seems to be accumulating squares of all samples in the input for one bit period + + bit_buf[sample_cnt] = 0.95*bit_buf[sample_cnt] + 0.05*src_buf[i] * src_buf[i]; + + // Check for NAN + + if (bit_buf[sample_cnt] != bit_buf[sample_cnt]) + bit_buf[sample_cnt] = 0.0f; + + // Находим максимум в буфере синхронизации + // Find the maximum in the synchronization buffer + + if (bit_buf[sample_cnt] > PkAmpMax) + { + PkAmp = src_buf[i]; + PkAmpMax = bit_buf[sample_cnt]; + newpkpos = sample_cnt; + } + sample_cnt++; + + bit_osc = bit_osc + x; + + // This seems to be how it does samples to bits + + + if (bit_osc >= 0.99f) // Allow for rounding errors + { + if (sample_cnt <= max_cnt) + for (k = sample_cnt; k <= max_cnt; k++) + bit_buf[k] = 0.95f*bit_buf[k]; + + k1 = (1.0f * newpkpos) / (sample_cnt - 1); + k2 = pila(k1) - 1; + AFC = div_bit_afc * k2; + + if (k1 > 0.5f) + bit_osc = bit_osc + AFC; + else + bit_osc = bit_osc - AFC; + + PkAmpMax = 0; + sample_cnt = 0; + + // Not sure about this, but if bit_buf gets to NaN it stays there + + bit_osc = bit_osc - 1; + //DCD feature + if (last) + { + DCD_LastPkPos[snd_ch] = DCD_LastPkPos[snd_ch] * 0.96f + newpkpos * 0.04f; + DCD_LastPerc[snd_ch] = DCD_LastPerc[snd_ch] * 0.96f + fabsf(newpkpos - DCD_LastPkPos[snd_ch])*0.04f; + + if (DCD_LastPerc[snd_ch] >= tr || DCD_LastPerc[snd_ch] < 0.00001f) + dcd_bit_cnt[snd_ch]++; + else + dcd_bit_cnt[snd_ch]--; + } + + amp = PkAmp; + + if (amp > 0) + raw_bit1 = RX_BIT1; + else + raw_bit1 = RX_BIT0; + // + if (amp > 0) + MaxAmp = MaxAmp * 0.9f + amp*0.1f; //0.9 + else + MinAmp = MinAmp * 0.9f + amp*0.1f; + + amp = amp - (MaxAmp + MinAmp)*0.5f; + + // Bit-detector + + AverageAmp = AverageAmp * 0.5f + amp*0.5f; + threshold = 0.5f * AverageAmp; + + if (amp > threshold) + raw_bit = RX_BIT1; + else + raw_bit = RX_BIT0; + + // 0.75 + + if (amp > 0.75*AverageAmp) + raw_bit2 = RX_BIT1; + else + raw_bit2 = RX_BIT0; + + if (raw_bit != raw_bit2) + raw_bit1 = raw_bit2; + + // look for il2p before nrzi + + if (il2p_mode[snd_ch]) + { + il2p_rec_bit(snd_ch, rcvr_nr, emph, raw_bit); + if (il2p_mode[snd_ch] == IL2P_MODE_ONLY) // Dont try HDLC decode + continue; + } + //NRZI + + if (raw_bit == last_rx_bit) + bit = RX_BIT1; + else + bit = RX_BIT0; + + last_rx_bit = raw_bit; + // + bit_stream = (bit_stream >> 1) | bit; + + // DCD on flag + + if (last) + { + if (dcd_hdr_cnt[snd_ch] > 0) + dcd_hdr_cnt[snd_ch]--; + + DCD_header[snd_ch] = (DCD_header[snd_ch] >> 1) | (bit << 24); + + if (((DCD_header[snd_ch] & 0xFFFF0000) == 0x7E7E0000) || + ((DCD_header[snd_ch] & 0xFFFFFF00) == 0x7E000000) || + ((DCD_header[snd_ch] & 0xFFFFFF00) == 0x00000000)) + { + dcd_hdr_cnt[snd_ch] = 48; + dcd_on_hdr[snd_ch] = 1; + } + } + + // FX25 process + + if (rx_fx25_mode) + { + if (fx25.status == FX25_LOAD) + { + //if last then DCD_on_hdr[snd_ch]:=true; + + fx25.byte_rx = (fx25.byte_rx >> 1) | bit; + fx25.bit_cnt++; + + if (fx25.bit_cnt == 8) + { + fx25.bit_cnt = 0; + stringAdd(&fx25.data, &fx25.byte_rx, 1); + fx25.size_cnt++; + if (fx25.size == fx25.size_cnt) + { + fx25.status = FX25_TAG; + make_rx_frame_FX25(snd_ch, rcvr_nr, emph, decode_FX25_data(fx25)); + //if last and (DCD_hdr_cnt[snd_ch]=0) then DCD_on_hdr[snd_ch]:=false; + } + } + } + else + { + fx25.size = 0; + + fx25.tag = (fx25.tag >> 1); + if (bit) + fx25.tag |= 0x8000000000000000; + + tag64 = fx25.tag & 0XFFFFFFFFFFFFFFFE; + + // FX25 tag correlation + + if (FX25_corr[snd_ch]) + { + unsigned char res; + + res = get_corr(tag64); + + if (res < tags_nr) + { + Debugprintf("Got FEC Tag %d Errors %d", res, errors); + fx25.size = sizes[res]; + fx25.rs_size = rs_sizes[res]; + } + } + else + { + if (tag64 == 0xB74DB7DF8A532F3E) + { + fx25.size = 255; + fx25.rs_size = 16; + } + if (tag64 == 0x26FF60A600CC8FDE) + { + fx25.size = 144; + fx25.rs_size = 16; + } + if (tag64 == 0xC7DC0508F3D9B09E) + { + fx25.size = 80; + fx25.rs_size = 16; + } + if (tag64 == 0x8F056EB4369660EE) + { + fx25.size = 48; + fx25.rs_size = 16; + } + if (tag64 == 0x6E260B1AC5835FAE) + { + fx25.size = 255; + fx25.rs_size = 32; + } + if (tag64 == 0xFF94DC634F1CFF4E) + { + fx25.size = 160; + fx25.rs_size = 32; + } + if (tag64 == 0x1EB7B9CDBC09C00E) + { + fx25.size = 96; + fx25.rs_size = 32; + } + if (tag64 == 0xDBF869BD2DBB1776) + { + fx25.size = 64; + fx25.rs_size = 32; + } + if (tag64 == 0x3ADB0C13DEAE2836) + { + fx25.size = 255; + fx25.rs_size = 64; + } + if (tag64 == 0xAB69DB6A543188D6) + { + fx25.size = 192; + fx25.rs_size = 64; + } + if (tag64 == 0x4A4ABEC4A724B796) + { + fx25.size = 128; + fx25.rs_size = 64; + } + } + if (fx25.size != 0) + { + fx25.status = FX25_LOAD; + fx25.data.Length = 0; + fx25.bit_cnt = 0; + fx25.size_cnt = 0; + centreFreq[snd_ch] = GuessCentreFreq(snd_ch); + } + } + } + // + + + if (bit_stream == 0xFF || bit_stream == 0x7F || bit_stream == 0xFE) + { + // All have 7 or more 1 bits + + if (frame_status == FRAME_LOAD) + { + // Have started receiving frame + +// Debugprintf("Frame Abort len= %d bits", pDET->raw_bits[snd_ch].Length); + + frame_status = FRAME_WAIT; + + // Raw stream init + + pDET->raw_bits[snd_ch].Length = 0; + pDET->raw_bits1[snd_ch].Length = 0; + pDET->last_nrzi_bit[snd_ch] = raw_bit; + +// dcd_hdr_cnt[snd_ch] = 48; +// dcd_on_hdr[snd_ch] = 1; + + + if (last) + chk_dcd1(snd_ch, buf_size); + } + continue; + } + + if (((bit_stream & FRAME_FLAG) == FRAME_FLAG) && (frame_status == FRAME_LOAD)) + { + frame_status = FRAME_WAIT; + + if (pDET->raw_bits[snd_ch].Length == 7) // Another flag + { + // Raw stream init + + pDET->raw_bits[snd_ch].Length = 0; + pDET->raw_bits1[snd_ch].Length = 0; + pDET->last_nrzi_bit[snd_ch] = raw_bit; + } + + if (pDET->raw_bits[snd_ch].Length > 7) + { +//b Debugprintf("Got Frame len = %d AFC %f", pDET->raw_bits[snd_ch].Length, AFC); + centreFreq[snd_ch] = GuessCentreFreq(snd_ch); + make_rx_frame(snd_ch, rcvr_nr, emph, pDET->last_nrzi_bit[snd_ch], &pDET->raw_bits[snd_ch], &pDET->raw_bits1[snd_ch]); + } + } + + + if (frame_status == FRAME_LOAD) + { + //Raw stream + + if (pDET->raw_bits[snd_ch].Length < 36873) + { + if (raw_bit == RX_BIT1) + stringAdd(&pDET->raw_bits[snd_ch], "1", 1); + else + stringAdd(&pDET->raw_bits[snd_ch], "0", 1); + } + + if (pDET->raw_bits1[snd_ch].Length < 36873) + { + if (raw_bit1 == RX_BIT1) + stringAdd(&pDET->raw_bits1[snd_ch], "1", 1); + else + stringAdd(&pDET->raw_bits1[snd_ch], "0", 1); + } + // + } + + if (((bit_stream & FRAME_FLAG) == FRAME_FLAG) && (frame_status == FRAME_WAIT)) + { + frame_status = FRAME_LOAD; + + // Raw stream init + + pDET->raw_bits[snd_ch].Length = 0; + pDET->raw_bits1[snd_ch].Length = 0; + pDET->last_nrzi_bit[snd_ch] = raw_bit; + + // Debugprintf("New Frame"); + } + + } + } + + pDET->sample_cnt[snd_ch] = sample_cnt; + pDET->PkAmp[snd_ch] = PkAmp; + pDET->PkAmpMax[snd_ch] = PkAmpMax; + pDET->newpkpos[snd_ch] = newpkpos; + pDET->bit_osc[snd_ch] = bit_osc; + pDET->MaxAmp[snd_ch] = MaxAmp; + pDET->MinAmp[snd_ch] = MinAmp; + pDET->AverageAmp[snd_ch] = AverageAmp; + pDET->bit_stream[snd_ch] = bit_stream; + pDET->frame_status[snd_ch] = frame_status; + pDET->last_rx_bit[snd_ch] = last_rx_bit; + pDET->fx25[snd_ch] = fx25; +} + + +void decode_stream_BPSK(int last, int snd_ch, int rcvr_nr, int emph, float * srcI, float * srcQ, float * bit_buf, int buf_size, string * data) +{ + float agc_fast = 0.01f; + float agc_fast1 = 1 - agc_fast; + float agc_slow = agc_fast / 4; + float agc_slow1 = 1 - agc_slow; + + int i, k, j, n; + Byte dibit, bit; + single afc, x, amp, k1, k2; + single baudrate; + single div_bit_afc; + word max_cnt; + single threshold; + single tr; + single KCorr, AngleCorr, angle, muxI1, muxQ1, muxI2, muxQ2, sumIQ1, sumIQ2; + Byte newpkpos, sample_cnt; + single PkAmpI, PkAmpQ, PkAmpMax, PSK_AGC; + single PSK_IZ1, PSK_QZ1; + single bit_osc; + Byte bit_stuff_cnt, last_rx_bit, frame_status, bit_cnt, bit_stream, byte_rx; + + // get saved values to local variables to speed up access + + struct TDetector_t * pDET = &DET[emph][rcvr_nr]; + + // global -> local + + AngleCorr = pDET->AngleCorr[snd_ch]; + bit_stuff_cnt = pDET->bit_stuff_cnt[snd_ch]; + sample_cnt = pDET->sample_cnt[snd_ch]; + PSK_AGC = pDET->PSK_AGC[snd_ch]; + PkAmpI = pDET->PkAmpI[snd_ch]; + PkAmpQ = pDET->PkAmpQ[snd_ch]; + PkAmpMax = pDET->PkAmpMax[snd_ch]; + newpkpos = pDET->newpkpos[snd_ch]; + PSK_IZ1 = pDET->PSK_IZ1[snd_ch]; + PSK_QZ1 = pDET->PSK_QZ1[snd_ch]; + bit_osc = pDET->bit_osc[snd_ch]; + frame_status = pDET->frame_status[snd_ch]; + bit_cnt = pDET->bit_cnt[snd_ch]; + bit_stream = pDET->bit_stream[snd_ch]; + byte_rx = pDET->byte_rx[snd_ch]; + + // + tr = dcd_threshold * dcd_corr; + + if (last) + { + // Update DCD status + + if (dcd_hdr_cnt[snd_ch] == 0) + dcd_on_hdr[snd_ch] = 0; + + dcd_bit_cnt[snd_ch] = 0; + } + + baudrate = 300; + div_bit_afc = 1.0f / round(BIT_AFC*(RX_Samplerate / 11025)); + x = baudrate / RX_Samplerate; + +// max_cnt = round(RX_Samplerate / baudrate) + 1; + max_cnt = round(RX_Samplerate / baudrate) + 1; + + for (i = 0; i < buf_size - 1; i++) + { + // AGC + amp = sqrt(srcI[i] * srcI[i] + srcQ[i] * srcQ[i]); + + if (amp > PSK_AGC) + + PSK_AGC = PSK_AGC * agc_fast1 + amp*agc_fast; + else + PSK_AGC = PSK_AGC * agc_slow1 + amp*agc_slow; + + if (PSK_AGC > 1) + + { + srcI[i] = srcI[i] / PSK_AGC; + srcQ[i] = srcQ[i] / PSK_AGC; + amp = amp / PSK_AGC; // Вместо SQRT + } + // + bit_buf[sample_cnt] = 0.95*bit_buf[sample_cnt] + 0.05*amp; + // Находим максимум в буфере синхронизации + if (bit_buf[sample_cnt] > PkAmpMax) + { + PkAmpI = srcI[i]; + PkAmpQ = srcQ[i]; + PkAmpMax = bit_buf[sample_cnt]; + newpkpos = sample_cnt; + } + + sample_cnt++; + + bit_osc = bit_osc + x; + + if (bit_osc >= 1) + { + if (sample_cnt <= max_cnt) + for (k = sample_cnt; k <= max_cnt; k++) + bit_buf[k] = 0.95*bit_buf[k]; + + k1 = (1.0f * newpkpos) / (sample_cnt - 1); + k2 = pila(k1) - 1; + + afc = div_bit_afc * k2; + + if (k1 > 0.5f) + bit_osc = bit_osc + afc; + else + bit_osc = bit_osc - afc; + + PkAmpMax = 0; + sample_cnt = 0; + bit_osc = bit_osc - 1; + + //DCD feature + if (last) + { + DCD_LastPkPos[snd_ch] = DCD_LastPkPos[snd_ch] * 0.96f + newpkpos * 0.04f; + DCD_LastPerc[snd_ch] = DCD_LastPerc[snd_ch] * 0.96f + fabsf(newpkpos - DCD_LastPkPos[snd_ch])*0.04f; + + if (DCD_LastPerc[snd_ch] >= tr || DCD_LastPerc[snd_ch] < 0.00001f) + dcd_bit_cnt[snd_ch] = dcd_bit_cnt[snd_ch] + 1; + else + dcd_bit_cnt[snd_ch] = dcd_bit_cnt[snd_ch] - 1; + } + + // Bit-detector + + muxI1 = PkAmpI * PSK_IZ1; + muxI2 = PkAmpQ * PSK_IZ1; + muxQ1 = PkAmpQ * PSK_QZ1; + muxQ2 = PkAmpI * PSK_QZ1; + sumIQ1 = muxI1 + muxQ1; + sumIQ2 = muxI2 - muxQ2; + angle = atan2f(sumIQ2, sumIQ1); + PSK_IZ1 = PkAmpI; + PSK_QZ1 = PkAmpQ; + // Phase corrector + + if (fabsf(angle) < PI5) + AngleCorr = AngleCorr * 0.95f - angle * 0.05f; + else + { + if (angle > 0) + AngleCorr = AngleCorr * 0.95f + (pi - angle)*0.05f; + else + AngleCorr = AngleCorr * 0.95f - (pi + angle)*0.05f; + } + + angle = angle + AngleCorr; + // + + if (fabsf(angle) < PI5) + bit = RX_BIT1; + else + bit = RX_BIT0; + // + + if (il2p_mode[snd_ch]) + il2p_rec_bit(snd_ch, rcvr_nr, emph, bit); + if (il2p_mode[snd_ch] == IL2P_MODE_ONLY) // Dont try HDLC decode + continue; + + if (bit) + stats[1]++; + else + stats[0]++; + + bit_stream = (bit_stream >> 1) | bit; + + // DCD on flag + + if (last) + { + if (dcd_hdr_cnt[snd_ch] > 0) + dcd_hdr_cnt[snd_ch]--; + + DCD_header[snd_ch] = (DCD_header[snd_ch] >> 1) | (bit << 24); + + if (((DCD_header[snd_ch] & 0xFFFF0000) == 0x7E7E0000) || + ((DCD_header[snd_ch] & 0xFFFFFF00) == 0x7E000000) || + ((DCD_header[snd_ch] & 0xFFFFFF00) == 0x00000000)) + { + dcd_hdr_cnt[snd_ch] = 48; + dcd_on_hdr[snd_ch] = 1; + } + } + + + // I think Andy looks for both flag and abort here. I think it would be + // clearer to detect abort separately + + // This may not be optimun but should work + + if (bit_stream == 0xFF || bit_stream == 0x7F || bit_stream == 0xFE) + { + // All have 7 or more 1 bits + + if (frame_status == FRAME_LOAD) + { + // Have started receiving frame + +// Debugprintf("Frame Abort len= %d bytes", data->Length); + + frame_status = FRAME_WAIT; + + // Raw stream init + + bit_cnt = 0; + bit_stuff_cnt = 0; + data->Length = 0; + } + continue; + } + + + if ((bit_stream & FRAME_FLAG) == FRAME_FLAG && frame_status == FRAME_LOAD) + { + frame_status = FRAME_WAIT; + // if (bit_cnt == 6) + make_rx_frame_PSK(snd_ch, rcvr_nr, emph, data); + } + + if (frame_status == FRAME_LOAD) + { + if (bit_stuff_cnt == 5) + bit_stuff_cnt = 0; + else + { + if (bit == RX_BIT1) + bit_stuff_cnt++; + else + bit_stuff_cnt = 0; + + byte_rx = (byte_rx >> 1) + bit; + bit_cnt++; + } + + if (bit_cnt == 8) + { + if (data->Length < 4097) + stringAdd(data, &byte_rx, 1); + bit_cnt = 0; + } + } + if ((bit_stream & FRAME_FLAG) == FRAME_FLAG && frame_status == FRAME_WAIT) + { + frame_status = FRAME_LOAD; + bit_cnt = 0; + bit_stuff_cnt = 0; + data->Length = 0; + } + } + } + + pDET->sample_cnt[snd_ch] = sample_cnt; + pDET->PSK_AGC[snd_ch] = PSK_AGC; + pDET->PkAmpI[snd_ch] = PkAmpI; + pDET->PkAmpQ[snd_ch] = PkAmpQ; + pDET->PkAmpMax[snd_ch] = PkAmpMax; + pDET->newpkpos[snd_ch] = newpkpos; + pDET->PSK_IZ1[snd_ch] = PSK_IZ1; + pDET->PSK_QZ1[snd_ch] = PSK_QZ1; + pDET->bit_osc[snd_ch] = bit_osc; + pDET->frame_status[snd_ch] = frame_status; + pDET->bit_cnt[snd_ch] = bit_cnt; + pDET->bit_stream[snd_ch] = bit_stream; + pDET->byte_rx[snd_ch] = byte_rx; + pDET->bit_stuff_cnt[snd_ch] = bit_stuff_cnt; + pDET->AngleCorr[snd_ch] = AngleCorr; +} + + +void decode_stream_QPSK(int last, int snd_ch, int rcvr_nr, int emph, float * srcI, float * srcQ, float * bit_buf, int buf_size, string * data) +{ + float agc_fast = 0.01f; + float agc_fast1 = 1 - agc_fast; + float agc_slow = agc_fast / 4; + float agc_slow1 = 1 - agc_slow; + + int i, k, j, n; + Byte dibit = 0, bit; + single afc, x, amp, k1, k2; + single baudrate; + single div_bit_afc; + word max_cnt; + single threshold; + single tr; + single KCorr = 0, AngleCorr, angle, muxI1, muxQ1, muxI2, muxQ2, sumIQ1, sumIQ2; + Byte newpkpos, sample_cnt; + single PkAmpI, PkAmpQ, PkAmpMax, PSK_AGC; + single PSK_IZ1, PSK_QZ1; + single bit_osc; + Byte bit_stuff_cnt, last_rx_bit, frame_status, bit_cnt, bit_stream, byte_rx; + + // get saved values to local variables to speed up access + + struct TDetector_t * pDET = &DET[emph][rcvr_nr]; + + + bit_stuff_cnt = pDET->bit_stuff_cnt[snd_ch]; + last_rx_bit = pDET->last_rx_bit[snd_ch]; + sample_cnt = pDET->sample_cnt[snd_ch]; + PSK_AGC = pDET->PSK_AGC[snd_ch]; + PkAmpI = pDET->PkAmpI[snd_ch]; + PkAmpQ = pDET->PkAmpQ[snd_ch]; + PkAmpMax = pDET->PkAmpMax[snd_ch]; + newpkpos = pDET->newpkpos[snd_ch]; + PSK_IZ1 = pDET->PSK_IZ1[snd_ch]; + PSK_QZ1 = pDET->PSK_QZ1[snd_ch]; + bit_osc = pDET->bit_osc[snd_ch]; + frame_status = pDET->frame_status[snd_ch]; + bit_cnt = pDET->bit_cnt[snd_ch]; + bit_stream = pDET->bit_stream[snd_ch]; + byte_rx = pDET->byte_rx[snd_ch]; + AngleCorr = pDET->AngleCorr[snd_ch]; + + tr = dcd_threshold * dcd_corr; + + if (last) + { + // Update DCD status + + if (dcd_hdr_cnt[snd_ch] == 0) + dcd_on_hdr[snd_ch] = 0; + + dcd_bit_cnt[snd_ch] = 0; + } + + // I think this works because of upsampling - 1200 = 4x 300 and we upsampled by 4 + + baudrate = 300; + + div_bit_afc = 1.0f / roundf(BIT_AFC*(RX_Samplerate / 11025.0f)); + + x = baudrate / RX_Samplerate; + + max_cnt = roundf(RX_Samplerate / baudrate) + 1; + + for (i = 0; i < buf_size; i++) + { + // AGC + amp = sqrt(srcI[i] * srcI[i] + srcQ[i] * srcQ[i]); + + if (amp > PSK_AGC) + PSK_AGC = PSK_AGC * agc_fast1 + amp * agc_fast; + else + PSK_AGC = PSK_AGC * agc_slow1 + amp * agc_slow; + + if (PSK_AGC > 1) + { + srcI[i] = srcI[i] / PSK_AGC; + srcQ[i] = srcQ[i] / PSK_AGC; + + amp = amp / PSK_AGC; // Вместо SQRT + } + + bit_buf[sample_cnt] = 0.95 * bit_buf[sample_cnt] + 0.05 * amp; + + // Check for NAN + + if (bit_buf[sample_cnt] != bit_buf[sample_cnt]) + bit_buf[sample_cnt] = 0.0f; + + // Find the maximum in the synchronization buffer + + if (bit_buf[sample_cnt] > PkAmpMax) + { + PkAmpI = srcI[i]; + PkAmpQ = srcQ[i]; + PkAmpMax = bit_buf[sample_cnt]; + newpkpos = sample_cnt; + } + + sample_cnt++; + + bit_osc = bit_osc + x; + + // This seems to be how it does samples to bits + + if (bit_osc >= 1) + { + if (sample_cnt <= max_cnt) + for (k = sample_cnt; k <= max_cnt; k++) + bit_buf[k] = 0.95*bit_buf[k]; + + k1 = (1.0f * newpkpos) / (sample_cnt - 1); + k2 = pila(k1) - 1; + afc = div_bit_afc * k2; + + if (k1 > 0.5) + bit_osc = bit_osc + afc; + else + bit_osc = bit_osc - afc; + + PkAmpMax = 0; + sample_cnt = 0; + bit_osc = bit_osc - 1; + //DCD feature + + if (last) + { + DCD_LastPkPos[snd_ch] = DCD_LastPkPos[snd_ch] * 0.96 + newpkpos * 0.04; + DCD_LastPerc[snd_ch] = DCD_LastPerc[snd_ch] * 0.96 + fabsf(newpkpos - DCD_LastPkPos[snd_ch])*0.04; + + if (DCD_LastPerc[snd_ch] >= tr || DCD_LastPerc[snd_ch] < 0.00001f) + dcd_bit_cnt[snd_ch] = dcd_bit_cnt[snd_ch] + 1; + else + dcd_bit_cnt[snd_ch] = dcd_bit_cnt[snd_ch] - 1; + } + + // Bit-detector + + muxI1 = PkAmpI * PSK_IZ1; + muxI2 = PkAmpQ * PSK_IZ1; + muxQ1 = PkAmpQ * PSK_QZ1; + muxQ2 = PkAmpI * PSK_QZ1; + sumIQ1 = muxI1 + muxQ1; + sumIQ2 = muxI2 - muxQ2; + angle = atan2f(sumIQ2, sumIQ1); + PSK_IZ1 = PkAmpI; + PSK_QZ1 = PkAmpQ; + + if (angle > pi || angle < -pi) + angle = angle; + + if (modem_mode[snd_ch] == MODE_PI4QPSK) + { + // Phase corrector + + // I'm pretty sure we send 4 phases starting 45 degrees from 0 so .25, .75 - .25 - .75 + + if (angle >= 0 && angle <= PI5) + KCorr = angle - PI25; + + if (angle > PI5) + KCorr = angle - PI75; + + if (angle < -PI5) + KCorr = angle + PI75; + + if (angle < 0 && angle >= -PI5) + KCorr = angle + PI25; + + AngleCorr = AngleCorr * 0.95f - KCorr * 0.05f; + angle = angle + AngleCorr; + + if (angle >= 0 && angle <= PI5) + { + dibit = qpsk_set[snd_ch].rx[0]; // 00 - 0 + qpsk_set[snd_ch].count[0]++; + } + else if (angle > PI5) + { + dibit = qpsk_set[snd_ch].rx[1]; // 01 - PI/2 + qpsk_set[snd_ch].count[1]++; + } + else if (angle < -PI5) + { + dibit = qpsk_set[snd_ch].rx[2]; // 10 - PI + qpsk_set[snd_ch].count[2]++; + } + else if (angle < 0 && angle >= -PI5) + { + dibit = qpsk_set[snd_ch].rx[3]; // 11 - -PI/2 + qpsk_set[snd_ch].count[3]++; + } + } + else + { + // Phase corrector + + // I think this sends 0 90 180 270 + + if (fabsf(angle) < PI25) + KCorr = angle; + + if (angle >= PI25 && angle <= PI75) + KCorr = angle - PI5; + + if (angle <= -PI25 && angle >= -PI75) + KCorr = angle + PI5; + + if (fabsf(angle) > PI75) + { + if (angle > 0) + KCorr = -M_PI + angle; + else + KCorr = M_PI + angle; + } + + AngleCorr = AngleCorr * 0.95 - KCorr * 0.05; + angle = angle + AngleCorr; + + if (fabsf(angle) < PI25) + dibit = qpsk_set[snd_ch].rx[0]; // 00 - 0 + else if (angle >= PI25 && angle <= PI75) + dibit = qpsk_set[snd_ch].rx[1]; // 01 - PI/2 + else if (fabsf(angle) > PI75) + dibit = qpsk_set[snd_ch].rx[2]; // 10 - PI + else if (angle <= -PI25 && angle >= -PI75) + dibit = qpsk_set[snd_ch].rx[3]; // 11 - -PI/2 + } + + for (j = 0; j < 2; j++) + { + dibit = dibit << 1; + + // NRZI + + if (last_rx_bit == (dibit & RX_BIT1)) + bit = RX_BIT1; + else + bit = RX_BIT0; + + last_rx_bit = dibit & RX_BIT1; + + bit_stream = (bit_stream >> 1) | bit; + + // DCD on flag + + if (last) + { + if (dcd_hdr_cnt[snd_ch] > 0) + dcd_hdr_cnt[snd_ch]--; + + DCD_header[snd_ch] = (DCD_header[snd_ch] >> 1) | (bit << 24); + + if (((DCD_header[snd_ch] & 0xFFFF0000) == 0x7E7E0000) || + ((DCD_header[snd_ch] & 0xFFFFFF00) == 0x7E000000) || + ((DCD_header[snd_ch] & 0xFFFFFF00) == 0x00000000)) + { + dcd_hdr_cnt[snd_ch] = 48; + dcd_on_hdr[snd_ch] = 1; + } + } + + + // I think Andy looks for both flag and abort here. I think it would be + // clearer to detect abort separately + + // This may not be optimun but should work + + if (bit_stream == 0xFF || bit_stream == 0x7F || bit_stream == 0xFE) + { + // All have 7 or more 1 bits + + if (frame_status == FRAME_LOAD) + { + // Have started receiving frame + + // Debugprintf("Frame Abort len= %d bytes", data->Length); + + frame_status = FRAME_WAIT; + + // Raw stream init + + bit_cnt = 0; + bit_stuff_cnt = 0; + data->Length = 0; + } + continue; + } + + if ((bit_stream & FRAME_FLAG) == FRAME_FLAG && frame_status == FRAME_LOAD) + { + frame_status = FRAME_WAIT; + // if (bit_cnt == 6) + make_rx_frame_PSK(snd_ch, rcvr_nr, emph, data); + } + + if (frame_status == FRAME_LOAD) + { + if (bit_stuff_cnt == 5) + bit_stuff_cnt = 0; + else + { + if (bit == RX_BIT1) + bit_stuff_cnt++; + else + bit_stuff_cnt = 0; + + byte_rx = (byte_rx >> 1) + bit; + bit_cnt++; + + } + + if (bit_cnt == 8) + { + if (data->Length < 4097) + stringAdd(data, &byte_rx, 1); + bit_cnt = 0; + } + } + if ((bit_stream & FRAME_FLAG) == FRAME_FLAG && frame_status == FRAME_WAIT) + { + frame_status = FRAME_LOAD; + bit_cnt = 0; + bit_stuff_cnt = 0; + data->Length = 0; + } + } + } + } + + pDET->sample_cnt[snd_ch] = sample_cnt; + pDET->PSK_AGC[snd_ch] = PSK_AGC; + pDET->PkAmpI[snd_ch] = PkAmpI; + pDET->PkAmpQ[snd_ch] = PkAmpQ; + pDET->PkAmpMax[snd_ch] = PkAmpMax; + pDET->newpkpos[snd_ch] = newpkpos; + pDET->PSK_IZ1[snd_ch] = PSK_IZ1; + pDET->PSK_QZ1[snd_ch] = PSK_QZ1; + pDET->bit_osc[snd_ch] = bit_osc; + pDET->frame_status[snd_ch] = frame_status; + pDET->bit_cnt[snd_ch] = bit_cnt; + pDET->bit_stream[snd_ch] = bit_stream; + pDET->byte_rx[snd_ch] = byte_rx; + pDET->last_rx_bit[snd_ch] = last_rx_bit; + pDET->bit_stuff_cnt[snd_ch] = bit_stuff_cnt; + pDET->AngleCorr[snd_ch] = AngleCorr; +} + +void decode_stream_8PSK(int last, int snd_ch, int rcvr_nr, int emph, float * srcI, float * srcQ, float * bit_buf, int buf_size, string * data) +{ + float agc_fast = 0.01f; + float agc_fast1 = 1 - agc_fast; + float agc_slow = agc_fast / 4; + float agc_slow1 = 1 - agc_slow; + + int i, k, j, n; + Byte tribit = 0, bit; + single afc, x, amp, k1, k2; + single baudrate; + single div_bit_afc; + word max_cnt; + single threshold; + single tr; + single KCorr = 0, AngleCorr, angle, muxI1, muxQ1, muxI2, muxQ2, sumIQ1, sumIQ2; + Byte newpkpos, sample_cnt; + single PkAmpI, PkAmpQ, PkAmpMax, PSK_AGC; + single PSK_IZ1, PSK_QZ1; + single bit_osc; + Byte bit_stuff_cnt, last_rx_bit, frame_status, bit_cnt, bit_stream, byte_rx; + + // get saved values to local variables to speed up access + + struct TDetector_t * pDET = &DET[emph][rcvr_nr]; + + bit_stuff_cnt = pDET->bit_stuff_cnt[snd_ch]; + last_rx_bit = pDET->last_rx_bit[snd_ch]; + sample_cnt = pDET->sample_cnt[snd_ch]; + PSK_AGC = pDET->PSK_AGC[snd_ch]; + PkAmpI = pDET->PkAmpI[snd_ch]; + PkAmpQ = pDET->PkAmpQ[snd_ch]; + PkAmpMax = pDET->PkAmpMax[snd_ch]; + newpkpos = pDET->newpkpos[snd_ch]; + PSK_IZ1 = pDET->PSK_IZ1[snd_ch]; + PSK_QZ1 = pDET->PSK_QZ1[snd_ch]; + bit_osc = pDET->bit_osc[snd_ch]; + frame_status = pDET->frame_status[snd_ch]; + bit_cnt = pDET->bit_cnt[snd_ch]; + bit_stream = pDET->bit_stream[snd_ch]; + byte_rx = pDET->byte_rx[snd_ch]; + AngleCorr = pDET->AngleCorr[snd_ch]; + + tr = dcd_threshold * dcd_corr; + + if (last) + { + // Update DCD status + + if (dcd_hdr_cnt[snd_ch] == 0) + dcd_on_hdr[snd_ch] = 0; + + dcd_bit_cnt[snd_ch] = 0; + } + + baudrate = 1600 / 6; + div_bit_afc = 1.0 / round(BIT_AFC*(RX_Samplerate / 11025)); + x = baudrate / RX_Samplerate; + max_cnt = round(RX_Samplerate / baudrate) + 1; + for (i = 0; i < buf_size; i++) + { + // AGC + amp = sqrt(srcI[i] * srcI[i] + srcQ[i] * srcQ[i]); + if (amp > PSK_AGC) + PSK_AGC = PSK_AGC * agc_fast1 + amp*agc_fast; + else + PSK_AGC = PSK_AGC * agc_slow1 + amp*agc_slow; + + if (PSK_AGC > 1) + { + srcI[i] = srcI[i] / PSK_AGC; + srcQ[i] = srcQ[i] / PSK_AGC; + amp = amp / PSK_AGC; // Вместо SQRT + } + + bit_buf[sample_cnt] = 0.95*bit_buf[sample_cnt] + 0.05*amp; + + // Находим максимум в буфере синхронизации + if (bit_buf[sample_cnt] > PkAmpMax) + { + PkAmpI = srcI[i]; + PkAmpQ = srcQ[i]; + PkAmpMax = bit_buf[sample_cnt]; + newpkpos = sample_cnt; + } + // + + sample_cnt++; + + bit_osc = bit_osc + x; + + if (bit_osc >= 1) + { + if (sample_cnt <= max_cnt) + for (k = sample_cnt; k <= max_cnt; k++) + bit_buf[k] = 0.95*bit_buf[k]; + + k1 = (1.0f * newpkpos) / (sample_cnt - 1); + k2 = pila(k1) - 1; + + afc = div_bit_afc * k2; + if (k1 > 0.5) + bit_osc = bit_osc + afc; + else + bit_osc = bit_osc - afc; + + PkAmpMax = 0; + sample_cnt = 0; + bit_osc = bit_osc - 1; + //DCD feature + if (last) + { + DCD_LastPkPos[snd_ch] = DCD_LastPkPos[snd_ch] * 0.96 + newpkpos * 0.04; + DCD_LastPerc[snd_ch] = DCD_LastPerc[snd_ch] * 0.96 + abs(newpkpos - DCD_LastPkPos[snd_ch])*0.04; + if (DCD_LastPerc[snd_ch] >= tr || DCD_LastPerc[snd_ch] < 0.00001) + dcd_bit_cnt[snd_ch] = dcd_bit_cnt[snd_ch] + 1; + else + dcd_bit_cnt[snd_ch] = dcd_bit_cnt[snd_ch] - 1; + } + // Bit-detector + muxI1 = PkAmpI * PSK_IZ1; + muxI2 = PkAmpQ * PSK_IZ1; + muxQ1 = PkAmpQ * PSK_QZ1; + muxQ2 = PkAmpI * PSK_QZ1; + sumIQ1 = muxI1 + muxQ1; + sumIQ2 = muxI2 - muxQ2; + angle = atan2f(sumIQ2, sumIQ1); + PSK_IZ1 = PkAmpI; + PSK_QZ1 = PkAmpQ; + + // Phase corrector + + if (fabsf(angle) < PI125) + KCorr = angle; + + if (angle >= PI125 && angle <= PI375) + KCorr = angle - PI25; + if (angle >= PI375 && angle < PI625) + KCorr = angle - PI5; + if (angle >= PI625 && angle <= PI875) + KCorr = angle - PI75; + if (angle <= -PI125 && angle > -PI375) + KCorr = angle + PI25; + if (angle <= -PI375 && angle > -PI625) + KCorr = angle + PI5; + if (angle <= -PI625 && angle >= -PI875) + KCorr = angle + PI75; + + if (fabsf(angle) > PI875) + { + if (angle > 0) + KCorr = angle - pi; + else + KCorr = angle + pi; + } + + AngleCorr = AngleCorr * 0.95 - KCorr * 0.05; + angle = angle + AngleCorr; + // + + if (fabsf(angle) < PI125) + tribit = 1; + if (angle >= PI125 && angle < PI375) + tribit = 0; + if (angle >= PI375 && angle < PI625) + tribit = 2; + if (angle >= PI625 && angle <= PI875) + tribit = 3; + if (fabsf(angle) > PI875) + tribit = 7; + if (angle <= -PI625 && angle >= -PI875) + tribit = 6; + if (angle <= -PI375 && angle > -PI625) + tribit = 4; + if (angle <= -PI125 && angle > -PI375) + tribit = 5; + + tribit = tribit << 4; + + for (j = 0; j < 3; j++) + { + tribit = tribit << 1; + //NRZI + + if (last_rx_bit == (tribit & RX_BIT1)) + bit = RX_BIT1; + else + bit = RX_BIT0; + + last_rx_bit = tribit & RX_BIT1; + // + bit_stream = (bit_stream >> 1) | bit; + + // DCD on flag + + if (last) + { + if (dcd_hdr_cnt[snd_ch] > 0) + dcd_hdr_cnt[snd_ch]--; + + DCD_header[snd_ch] = (DCD_header[snd_ch] >> 1) | (bit << 24); + + if (((DCD_header[snd_ch] & 0xFFFF0000) == 0x7E7E0000) || + ((DCD_header[snd_ch] & 0xFFFFFF00) == 0x7E000000) || + ((DCD_header[snd_ch] & 0xFFFFFF00) == 0x00000000)) + { + dcd_hdr_cnt[snd_ch] = 48; + dcd_on_hdr[snd_ch] = 1; + } + } + + + // I think Andy looks for both flag and abort here. I think it would be + // clearer to detect abort separately + + // This may not be optimun but should work + + if (bit_stream == 0xFF || bit_stream == 0x7F || bit_stream == 0xFE) + { + // All have 7 or more 1 bits + + if (frame_status == FRAME_LOAD) + { + // Have started receiving frame + + // Debugprintf("Frame Abort len= %d bytes", data->Length); + + frame_status = FRAME_WAIT; + + // Raw stream init + + bit_cnt = 0; + bit_stuff_cnt = 0; + data->Length = 0; + } + continue; + } + + + if ((bit_stream & FRAME_FLAG) == FRAME_FLAG && frame_status == FRAME_LOAD) + { + frame_status = FRAME_WAIT; + if (bit_cnt == 6) + make_rx_frame_PSK(snd_ch, rcvr_nr, emph, data); + } + if (frame_status == FRAME_LOAD) + { + if (bit_stuff_cnt == 5) + bit_stuff_cnt = 0; + else + { + if (bit == RX_BIT1) + bit_stuff_cnt++; + else + bit_stuff_cnt = 0; + + byte_rx = (byte_rx >> 1) + bit; + bit_cnt++; + } + if (bit_cnt == 8) + { + if (data->Length < 4097) + stringAdd(data, &byte_rx, 1); + bit_cnt = 0; + } + } + + if ((bit_stream & FRAME_FLAG) == FRAME_FLAG && frame_status == FRAME_WAIT) + { + frame_status = FRAME_LOAD; + bit_cnt = 0; + bit_stuff_cnt = 0; + data->Length = 0; + } + } + } + } + + pDET->sample_cnt[snd_ch] = sample_cnt; + pDET->PSK_AGC[snd_ch] = PSK_AGC; + pDET->PkAmpI[snd_ch] = PkAmpI; + pDET->PkAmpQ[snd_ch] = PkAmpQ; + pDET->PkAmpMax[snd_ch] = PkAmpMax; + pDET->newpkpos[snd_ch] = newpkpos; + pDET->PSK_IZ1[snd_ch] = PSK_IZ1; + pDET->PSK_QZ1[snd_ch] = PSK_QZ1; + pDET->bit_osc[snd_ch] = bit_osc; + pDET->frame_status[snd_ch] = frame_status; + pDET->bit_cnt[snd_ch] = bit_cnt; + pDET->bit_stream[snd_ch] = bit_stream; + pDET->byte_rx[snd_ch] = byte_rx; + pDET->last_rx_bit[snd_ch] = last_rx_bit; + pDET->bit_stuff_cnt[snd_ch] = bit_stuff_cnt; + pDET->AngleCorr[snd_ch] = AngleCorr; + +} + +/* + +//////////////////////////////////////////////////////// + +function blackman(i,tap: word): single; +var + a0,a1,a2,a: single; +{ + a = 0.16; + a0 = (1-a)/2; + a1 = 1/2; + a2 = a/2; + result = a0-a1*cos(2*pi*i/(tap-1))+a2*cos(4*pi*i/(tap-1)); +} + +function nuttal(i,tap: word): single; +var + a0,a1,a2,a3: single; +{ + a0 = 0.355768; + a1 = 0.487396; + a2 = 0.144232; + a3 = 0.012604; + result = a0-a1*cos(2*pi*i/(tap-1))+a2*cos(4*pi*i/(tap-1))-a3*cos(6*pi*i/(tap-1)); +} + +function flattop(i,tap: word): single; +var + a0,a1,a2,a3,a4: single; +{ + a0 = 1; + a1 = 1.93; + a2 = 1.29; + a3 = 0.388; + a4 = 0.032; + result = a0-a1*cos(2*pi*i/(tap-1))+a2*cos(4*pi*i/(tap-1))-a3*cos(6*pi*i/(tap-1))+a4*cos(8*pi*i/(tap-1)); +} +*/ + + +void init_BPF(float freq1, float freq2, unsigned short tap, float samplerate, float * buf) +{ + unsigned short tap1, i; + float tap12, ham, acc1, acc2; + float bpf_l[2048]; + float bpf_h[2048]; + float itap12, pi2, x1, x2; + + acc1 = 0; + acc2 = 0; + tap1 = tap - 1; + tap12 = tap1 / 2; + pi2 = 2 * pi; + x1 = pi2 * freq1 / samplerate; + x2 = pi2 * freq2 / samplerate; + for (i = 0; i <= tap1; i++) + { +// float x = (pi2 * i) / tap1; +// x = cosf(x); +// ham = 0.5 - 0.5 * x; + + ham = 0.5 - 0.5 * cosf((pi2 * i) / tap1); //old + + if (ham != ham) // check for NaN + ham = 0.0f; + + itap12 = i - tap12; + + if (itap12 == 0) + { + bpf_l[i] = x1; + bpf_h[i] = x2; + } + else + { + bpf_l[i] = sinf(x1*itap12) / itap12; + bpf_h[i] = sinf(x2*itap12) / itap12; + } + + bpf_l[i] = bpf_l[i] * ham; + bpf_h[i] = bpf_h[i] * ham; + acc1 = acc1 + bpf_l[i]; + acc2 = acc2 + bpf_h[i]; + } + + for (i = 0; i <= tap1; i++) + { + bpf_l[i] = bpf_l[i] / acc1; + bpf_h[i] = -(bpf_h[i] / acc2); + }; + + bpf_h[tap / 2] = bpf_h[tap / 2] + 1; + + for (i = 0; i <= tap; i++) + { + buf[i] = -(bpf_l[i] + bpf_h[i]); + } + buf[tap / 2] = buf[tap / 2] + 1; +} + + + +void init_LPF(float width, unsigned short tap, float samplerate, float * buf) +{ + float acc1, ham; + unsigned short tap1, i; + float itap12, tap12, x1, pi2; + + acc1 = 0; + tap1 = tap - 1; + tap12 = tap1 / 2; + pi2 = 2 * pi; + x1 = pi2 * width / samplerate; + + for (i = 0; i <= tap1; i++) + { + ham = 0.53836f - 0.46164f * cosf(pi2 * i / tap1); //old + + if (ham != ham) // check for NaN + ham = 0.0f; + + //ham = 0.5-0.5*cos(pi2*i/tap1); + //ham = 0.5*(1-cos(pi2*i/tap1)); //hann + + //ham = blackman(i,tap); //blackman + //ham = nuttal(i,tap); + + itap12 = i - tap12; + + if (itap12 == 0) + buf[i] = x1; + else + buf[i] = sinf(x1*itap12) / itap12; + + buf[i] = buf[i] * ham; + acc1 = acc1 + buf[i]; + } + for (i = 0; i <= tap1; i++) + buf[i] = buf[i] / acc1; +} + +void make_core_INTR(UCHAR snd_ch) +{ + float width; + + width = roundf(RX_Samplerate / 2); + + n_INTR[snd_ch] = 1; + + switch (speed[snd_ch]) + { + case SPEED_300: + + width = roundf(RX_Samplerate / 2); + n_INTR[snd_ch] = 1; + break; + + case SPEED_P300: + + width = roundf(RX_Samplerate / 2); + n_INTR[snd_ch] = 1; + break; + + case SPEED_600: + + width = roundf(RX_Samplerate / 4); + n_INTR[snd_ch] = 2; + break; + + case SPEED_P600: + + width = roundf(RX_Samplerate / 4); + n_INTR[snd_ch] = 2; + break; + + case SPEED_1200: + width = roundf(RX_Samplerate / 8); + n_INTR[snd_ch] = 4; + break; + + case SPEED_P1200: + width = roundf(RX_Samplerate / 8); + n_INTR[snd_ch] = 4; + break; + + case SPEED_Q2400: + width = 300; + n_INTR[snd_ch] = 4; + break; //8 + + case SPEED_DW2400: + + width = 300; + n_INTR[snd_ch] = 4; + break; + + case SPEED_AE2400: + + width = 300; + n_INTR[snd_ch] = 4; + break; + + case SPEED_MP400: + + width = round(RX_Samplerate / 8); + n_INTR[snd_ch] = 4; + break; + + case SPEED_Q3600: + width = 300; + n_INTR[snd_ch] = 6;//12 + break; + + case SPEED_8P4800: + width = 100; + n_INTR[snd_ch] = 6; + break; + + case SPEED_2400: + + width = round(RX_Samplerate / 16); + n_INTR[snd_ch] = 8; + break; + + case SPEED_P2400: + width = round(RX_Samplerate / 16); + n_INTR[snd_ch] = 8; + break; + + case SPEED_Q4800: + width = 300; + n_INTR[snd_ch] = 8;//16 + break; + } + + + init_LPF(width, INTR_tap[snd_ch], RX_Samplerate, INTR_core[snd_ch]); +} + +void make_core_LPF(UCHAR snd_ch, short width) +{ + if (modem_mode[snd_ch] == MODE_MPSK) + { + init_LPF(width, LPF_tap[snd_ch], RX_Samplerate / n_INTR[snd_ch], LPF_core[snd_ch]); + init_LPF(rx_baudrate[snd_ch], LPF_tap[snd_ch], RX_Samplerate / n_INTR[snd_ch], AFC_core[snd_ch]); + } + else + init_LPF(width, LPF_tap[snd_ch], RX_Samplerate, LPF_core[snd_ch]); +} + + +void make_core_BPF(UCHAR snd_ch, short freq, short width) +{ + float old_freq, width2, rx_samplerate2, freq1, freq2; + + UCHAR i; + + freq = freq + rxOffset + chanOffset[snd_ch]; + + // I want to run decoders lowest to highest to simplify my display, + // so filters must be calculated in same order + + int offset = -(RCVR[snd_ch] * rcvr_offset[snd_ch]); // lowest + + rx_samplerate2 = 0.5 * RX_Samplerate; + width2 = 0.5 * width; + old_freq = freq; + + for (i = 0; i <= RCVR[snd_ch] << 1; i++) + { + freq = old_freq + offset; + + freq1 = freq - width2; + freq2 = freq + width2; + if (freq1 < 1) + freq1 = 1; + + if (freq2 < 1) + freq2 = 1; + + if (freq1 > rx_samplerate2) + freq1 = rx_samplerate2; + + if (freq2 > rx_samplerate2) + freq2 = rx_samplerate2; + + init_BPF(freq1, freq2, BPF_tap[snd_ch], RX_Samplerate, &DET[0][i].BPF_core[snd_ch][0]); + + offset += rcvr_offset[snd_ch]; + } +} + + + +void make_core_TXBPF(UCHAR snd_ch, float freq, float width) +{ + float freq1, freq2; + + freq1 = freq - width / 2; + freq2 = freq + width / 2; + + if (freq1 < 1) + freq1 = 1; + + if (freq2 < 1) + freq2 = 1; + + if (freq1 > TX_Samplerate / 2) + freq1 = TX_Samplerate / 2; + + if (freq2 > TX_Samplerate / 2) + freq2 = TX_Samplerate / 2; + + init_BPF(freq1, freq2, tx_BPF_tap[snd_ch], TX_Samplerate, tx_BPF_core[snd_ch]); +} + + + + +void interpolation(int snd_ch, int rcvr_nr, int emph, float * dest_buf, float * src_buf, int buf_size) +{ + int n_intr1, buf_size1, k, i, j; + float buf[8192]; + + buf_size1 = buf_size - 1; + n_intr1 = n_INTR[snd_ch] - 1; + k = 0; + + for (i = 0; i <= buf_size1; i++) + { + for (j = 0; j <= n_intr1; j++) + { + buf[k] = src_buf[i]; + k++; + } + } + FIR_filter(buf, buf_size *n_INTR[snd_ch], INTR_tap[snd_ch], INTR_core[snd_ch], dest_buf, DET[emph][rcvr_nr].prev_INTR_buf[snd_ch]); +} + +void interpolation_PSK(int snd_ch, int rcvr_nr, int emph, float * destI, float * destQ, float * srcI, float * srcQ, int buf_size) +{ + word n_intr1, buf_size1, k, i, j; + single bufI[8192], bufQ[8192]; + + buf_size1 = buf_size - 1; + n_intr1 = n_INTR[snd_ch] - 1; + + k = 0; + + for (i = 0; i <= buf_size1; i++) + { + for (j = 0; j <= n_intr1; j++) + { + bufI[k] = srcI[i]; + bufQ[k] = srcQ[i]; + k++; + } + } + + FIR_filter(bufI, buf_size*n_INTR[snd_ch], INTR_tap[snd_ch], INTR_core[snd_ch], destI, DET[emph][rcvr_nr].prev_INTRI_buf[snd_ch]); + FIR_filter(bufQ, buf_size*n_INTR[snd_ch], INTR_tap[snd_ch], INTR_core[snd_ch], destQ, DET[emph][rcvr_nr].prev_INTRQ_buf[snd_ch]); +} + + +void FSK_Demodulator(int snd_ch, int rcvr_nr, int emph, int last) +{ + // filtered samples in src_BPF_buf, output in src_Loop_buf + + Mux3(snd_ch,rcvr_nr,emph, &DET[0][rcvr_nr].src_BPF_buf[snd_ch][0], &LPF_core[snd_ch][0], &DET[emph][rcvr_nr].src_Loop_buf[snd_ch][0], + &DET[emph][rcvr_nr].prev_LPF1I_buf[snd_ch][0], &DET[emph][rcvr_nr].prev_LPF1Q_buf[snd_ch][0], LPF_tap[snd_ch], rx_bufsize); + + if (n_INTR[snd_ch] > 1) + { + interpolation(snd_ch, rcvr_nr, emph, DET[emph][rcvr_nr].src_INTR_buf[snd_ch], DET[emph][rcvr_nr].src_Loop_buf[snd_ch], rx_bufsize); + decode_stream_FSK(last, snd_ch, rcvr_nr, emph, &DET[emph][rcvr_nr].src_INTR_buf[snd_ch][0], &DET[emph][rcvr_nr].bit_buf[snd_ch][0], rx_bufsize*n_INTR[snd_ch], &DET[emph][rcvr_nr].rx_data[snd_ch]); + } + else + decode_stream_FSK(last,snd_ch,rcvr_nr,emph,DET[emph][rcvr_nr].src_Loop_buf[snd_ch], &DET[emph][rcvr_nr].bit_buf[snd_ch][0], rx_bufsize, &DET[emph][rcvr_nr].rx_data[snd_ch]); +} + +void BPSK_Demodulator(int snd_ch, int rcvr_nr, int emph, int last) +{ + Mux3_PSK(snd_ch, rcvr_nr, emph, + DET[0][rcvr_nr].src_BPF_buf[snd_ch], + LPF_core[snd_ch], + DET[emph][rcvr_nr].src_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1Q_buf[snd_ch], + DET[emph][rcvr_nr].prev_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].prev_LPF1Q_buf[snd_ch], + LPF_tap[snd_ch], rx_bufsize); + + if (n_INTR[snd_ch] > 1) + { + interpolation_PSK(snd_ch, rcvr_nr, emph, + DET[emph][rcvr_nr].src_INTRI_buf[snd_ch], + DET[emph][rcvr_nr].src_INTRQ_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1Q_buf[snd_ch], rx_bufsize); + + decode_stream_BPSK(last, snd_ch, rcvr_nr, emph, + DET[emph][rcvr_nr].src_INTRI_buf[snd_ch], + DET[emph][rcvr_nr].src_INTRQ_buf[snd_ch], + DET[emph][rcvr_nr].bit_buf[snd_ch], + rx_bufsize*n_INTR[snd_ch], + &DET[emph][rcvr_nr].rx_data[snd_ch]); + + } + else + decode_stream_BPSK(last, snd_ch, rcvr_nr, emph, + DET[emph][rcvr_nr].src_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1Q_buf[snd_ch], + DET[emph][rcvr_nr].bit_buf[snd_ch], + rx_bufsize, + &DET[emph][rcvr_nr].rx_data[snd_ch]); +} + +void QPSK_Demodulator(int snd_ch, int rcvr_nr, int emph, int last) +{ + Mux3_PSK(snd_ch, rcvr_nr, emph, + DET[0][rcvr_nr].src_BPF_buf[snd_ch], + LPF_core[snd_ch], + DET[emph][rcvr_nr].src_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1Q_buf[snd_ch], + DET[emph][rcvr_nr].prev_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].prev_LPF1Q_buf[snd_ch], + LPF_tap[snd_ch], rx_bufsize); + + if (n_INTR[snd_ch] > 1) + { + interpolation_PSK(snd_ch, rcvr_nr, emph, + DET[emph][rcvr_nr].src_INTRI_buf[snd_ch], + DET[emph][rcvr_nr].src_INTRQ_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1Q_buf[snd_ch], rx_bufsize); + + decode_stream_QPSK(last, snd_ch, rcvr_nr, emph, + DET[emph][rcvr_nr].src_INTRI_buf[snd_ch], + DET[emph][rcvr_nr].src_INTRQ_buf[snd_ch], + DET[emph][rcvr_nr].bit_buf[snd_ch], + rx_bufsize*n_INTR[snd_ch], + &DET[emph][rcvr_nr].rx_data[snd_ch]); + + } + else decode_stream_QPSK(last, snd_ch, rcvr_nr, emph, + DET[emph][rcvr_nr].src_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1Q_buf[snd_ch], + DET[emph][rcvr_nr].bit_buf[snd_ch], + rx_bufsize, + &DET[emph][rcvr_nr].rx_data[snd_ch]); + +} + + + + +void PSK8_Demodulator(int snd_ch, int rcvr_nr, int emph, boolean last) +{ + Mux3_PSK(snd_ch, rcvr_nr, emph, + DET[0][rcvr_nr].src_BPF_buf[snd_ch], + LPF_core[snd_ch], + DET[emph][rcvr_nr].src_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1Q_buf[snd_ch], + DET[emph][rcvr_nr].prev_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].prev_LPF1Q_buf[snd_ch], + LPF_tap[snd_ch], rx_bufsize); + + if (n_INTR[snd_ch] > 1) + { + interpolation_PSK(snd_ch, rcvr_nr, emph, + DET[emph][rcvr_nr].src_INTRI_buf[snd_ch], + DET[emph][rcvr_nr].src_INTRQ_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1Q_buf[snd_ch], rx_bufsize); + + decode_stream_8PSK(last, snd_ch, rcvr_nr, emph, + DET[emph][rcvr_nr].src_INTRI_buf[snd_ch], + DET[emph][rcvr_nr].src_INTRQ_buf[snd_ch], + DET[emph][rcvr_nr].bit_buf[snd_ch], + rx_bufsize*n_INTR[snd_ch], + &DET[emph][rcvr_nr].rx_data[snd_ch]); + + } + else + decode_stream_8PSK(last, snd_ch, rcvr_nr, emph, + DET[emph][rcvr_nr].src_LPF1I_buf[snd_ch], + DET[emph][rcvr_nr].src_LPF1Q_buf[snd_ch], + DET[emph][rcvr_nr].bit_buf[snd_ch], + rx_bufsize, + &DET[emph][rcvr_nr].rx_data[snd_ch]); +} + + +void Demodulator(int snd_ch, int rcvr_nr, float * src_buf, int last, int xcenter) +{ + // called once per decoder (current one in rcvr_nr) + + int i, k; + string rec_code; + UCHAR emph; + int found; + string * s_emph; + struct TDetector_t * pDET = &DET[0][rcvr_nr]; + + // looks like this filters to src_BPF_buf + + FIR_filter(src_buf, rx_bufsize, BPF_tap[snd_ch], pDET->BPF_core[snd_ch], pDET->src_BPF_buf[snd_ch], pDET->prev_BPF_buf[snd_ch]); + + // AFSK demodulator + + if (modem_mode[snd_ch] == MODE_FSK) + { + if (emph_all[snd_ch]) + { + for (emph = 1; emph <= nr_emph; emph++) + FSK_Demodulator(snd_ch, rcvr_nr, emph, FALSE); + + FSK_Demodulator(snd_ch, rcvr_nr, 0, last); + } + else + FSK_Demodulator(snd_ch, rcvr_nr, emph_db[snd_ch], last); + } + + // BPSK demodulator + if (modem_mode[snd_ch] == MODE_BPSK) + { + if (emph_all[snd_ch]) + { + for (emph = 1; emph <= nr_emph; emph++) + BPSK_Demodulator(snd_ch, rcvr_nr, emph, FALSE); + + BPSK_Demodulator(snd_ch, rcvr_nr, 0, last); + } + else + BPSK_Demodulator(snd_ch, rcvr_nr, emph_db[snd_ch], last); + + } + + // QPSK demodulator + if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK) + { + if (emph_all[snd_ch]) + { + for (emph = 1; emph <= nr_emph; emph++) + QPSK_Demodulator(snd_ch, rcvr_nr, emph, FALSE); + + QPSK_Demodulator(snd_ch, rcvr_nr, 0, last); + } + else + QPSK_Demodulator(snd_ch, rcvr_nr, emph_db[snd_ch], last); + } + + // QPSK demodulator + + if (modem_mode[snd_ch]==MODE_8PSK) + { + if (emph_all[snd_ch]) + { + for (emph = 1; emph <= nr_emph; emph++) + PSK8_Demodulator(snd_ch, rcvr_nr, emph, FALSE); + + PSK8_Demodulator(snd_ch, rcvr_nr, 0, last); + } + else + PSK8_Demodulator(snd_ch,rcvr_nr,emph_db[snd_ch],last); + } + + // MPSK demodulator + + if (modem_mode[snd_ch] == MODE_MPSK) + { + decode_stream_MPSK(snd_ch, rcvr_nr, DET[0][rcvr_nr].src_BPF_buf[snd_ch], rx_bufsize, last); + } + + +// I think this handles multiple decoders and passes packet on to next level + +// Packet manager + + if (last) + { + boolean fecflag = 0; + char indicators[5] = "-$#F+"; // None, Single, MEM, FEC, Normal + + // Work out which decoder and which emph settings worked. + + if (detect_list[snd_ch].Count > 0) // no point if nothing decoded + { + char decoded[32] = ""; + char indicators[5] = "-$#F+"; // None, Single, MEM, FEC, Normal + char s_emph[4] = ""; + int emph[4] = { 0 }; + char report[32] = ""; + int il2perrors = 255; + + // The is one DET for each Decoder for each Emph setting + + struct TDetector_t * pDET; + int i = 0, j; + int maxemph = nr_emph; + + for (i = 0; i <= nr_emph; i++) + { + for (j = 0; j <= RCVR[snd_ch] * 2; j++) + { + pDET = &DET[i][j]; + + if (pDET->rx_decoded > decoded[j]) // Better than other one (| is higher than F) + decoded[j] = pDET->rx_decoded; + + if (pDET->emph_decoded > emph[i]) + emph[i] = pDET->emph_decoded; + + if (il2perrors > pDET->errors) + il2perrors = pDET->errors; + + pDET->rx_decoded = 0; + pDET->emph_decoded = 0; // Ready for next time + pDET->errors = 255; + } + if (emph_all[snd_ch] == 0) + break; + } + + decoded[j] = 0; + + for (j--; j >= 0; j--) + decoded[j] = indicators[decoded[j]]; + + if (emph_all[snd_ch]) + { + for (i = 0; i <= nr_emph; i++) + { + s_emph[i] = indicators[emph[i]]; + } + sprintf(report, "%s][%s", s_emph, decoded); + } + + else + strcpy(report, decoded); + + if (detect_list_c[snd_ch].Items[0]->Length) + { + if (il2perrors < 255 && il2perrors > 0) + sprintf(detect_list_c[snd_ch].Items[0]->Data, "%s-%d", detect_list_c[snd_ch].Items[0]->Data, il2perrors); + + strcat(report, "]["); + strcat(report, detect_list_c[snd_ch].Items[0]->Data); + } + + if (detect_list[snd_ch].Count > 0) + { + for (i = 0; i < detect_list[snd_ch].Count; i++) + { + found = 0; + + // if (detect_list_l[snd_ch].Count > 0) + // if (my_indexof(&detect_list_l[snd_ch], detect_list[snd_ch].Items[i]) > -1) + // found = 1; + + if (found == 0) + { + if (modem_mode[snd_ch] == MODE_MPSK) + { + // analiz_frame(snd_ch, detect_list[snd_ch].Items[i]->Data, [snd_ch].Items[i]->Data + ' dF: ' + FloatToStrF(DET[0, 0].AFC_dF[snd_ch], ffFixed, 0, 1)); + } + else + { + analiz_frame(snd_ch, detect_list[snd_ch].Items[i], report, fecflag); + } + } + } + + // Cancel FX25 decode + + if (fx25_mode[snd_ch] != FX25_MODE_NONE) + { + int e; + + for (i = 0; i < 16; i++) + for (e = 0; e <= nr_emph; e++) + DET[e][i].fx25[snd_ch].status = FX25_TAG; + } + } + + // Assign(&detect_list_l[snd_ch], &detect_list[snd_ch]); // Duplicate detect_list to detect_list_l + + Clear(&detect_list[snd_ch]); + Clear(&detect_list_c[snd_ch]); + } + chk_dcd1(snd_ch, rx_bufsize); + } +} + +string * memory_ARQ(TStringList * buf, string * data) +{ + unsigned char crc[32]; + string * s; + string * frame; + word k, len, i; + + Byte zeros, ones; + TStringList need_frames; + + s = data; + + CreateStringList(&need_frames); + len = data->Length; + + memcpy(crc, &data->Data[data->Length - 18], 18); + + if (buf->Count > 0) + { + for (i = 0; i < buf->Count; i++) + { + if (buf->Items[i]->Length == len) + if (memcmp(&buf->Items[i]->Data[len - 18], crc, 18) == 0) + Add(&need_frames, buf->Items[i]); + } + } + + if (need_frames.Count > 2) + { + for (i = 0; i < len - 18; i++) + { + zeros = 0; + ones = 0; + + for (k = 0; k < need_frames.Count; k++) + { + frame = need_frames.Items[k]; + if (frame->Data[i] == '1') ones++; else zeros++; + } + if (ones > zeros) s->Data[i] = '1'; else s->Data[i] = '0'; + } + } + +// Clear(&need_frames); + return s; +} + + diff --git a/ax25_fec.c b/ax25_fec.c new file mode 100644 index 0000000..2fe920e --- /dev/null +++ b/ax25_fec.c @@ -0,0 +1,417 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" + +//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); + +#define FX25_FCR 1 +#define FX25_PRIM 1 +#define FX25_IPRIM 1 +#define FX25_MM 8 +#define FX25_NN 255 +#define FX25_A0 FX25_NN + +Byte FX25_ALPHA_TO[256] = { + + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26, + 0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, + 0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23, + 0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1, + 0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, + 0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2, + 0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce, + 0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc, + 0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54, + 0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73, + 0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff, + 0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41, + 0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6, + 0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09, + 0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16, + 0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x00 +}; + +Byte FX25_INDEX_OF[256] = { + +255, 0, 1, 25, 2, 50, 26,198, 3,223, 51,238, 27,104,199, 75, + 4,100,224, 14, 52,141,239,129, 28,193,105,248,200, 8, 76,113, + 5,138,101, 47,225, 36, 15, 33, 53,147,142,218,240, 18,130, 69, + 29,181,194,125,106, 39,249,185,201,154, 9,120, 77,228,114,166, + 6,191,139, 98,102,221, 48,253,226,152, 37,179, 16,145, 34,136, + 54,208,148,206,143,150,219,189,241,210, 19, 92,131, 56, 70, 64, + 30, 66,182,163,195, 72,126,110,107, 58, 40, 84,250,133,186, 61, +202, 94,155,159, 10, 21,121, 43, 78,212,229,172,115,243,167, 87, + 7,112,192,247,140,128, 99, 13,103, 74,222,237, 49,197,254, 24, +227,165,153,119, 38,184,180,124, 17, 68,146,217, 35, 32,137, 46, + 55, 63,209, 91,149,188,207,205,144,135,151,178,220,252,190, 97, +242, 86,211,171, 20, 42, 93,158,132, 60, 57, 83, 71,109, 65,162, + 31, 45, 67,216,183,123,164,118,196, 23, 73,236,127, 12,111,246, +108,161, 59, 82, 41,157, 85,170,251, 96,134,177,187,204, 62, 90, +203, 89, 95,176,156,169,160, 81, 11,245, 22,235,122,117, 44,215, + 79,174,213,233,230,231,173,232,116,214,244,234,168, 80, 88,175 +}; + + +Byte FX25_CCSDS_poly_8[9] = +{ 29, 188, 142, 221, 118, 206, 52, 168, 0 }; + +Byte FX25_CCSDS_poly_16[17] = + {136,240,208,195,181,158,201,100, 11, 83,167,107,113,110,106,121, 0 }; + + +Byte FX25_CCSDS_poly_32[33] = { + 18,251,215, 28, 80,107,248, 53, 84,194, 91, 59,176, 99,203,137, + 43,104,137, 0, 44,149,148,218, 75, 11,173,254,194,109, 8, 11, +0 }; + +Byte FX25_CCSDS_poly_64[65] = { + 40, 21,218, 23, 48,237, 69, 6, 87, 42, 29,193,160,150,113, 32, + 35,172,241,240,184, 90,188,225, 87,130,254, 41,245,253,184,241, +188,176, 54, 58,240,226,119,185, 77,150, 48,140,169,160, 96,217, + 15,202,218,190,135,103,129, 77, 57,166,164, 12, 13,178, 53, 46, +0 }; + + + +integer FX25_NROOTS ; +Byte FX25_GENPOLY[256]; + +Byte MODNN(int x) +{ + return x % 255; +} + + +void encode_rs(Byte * data, Byte * parity, int pad) +{ + int i, j; + Byte feedback; + + memset(parity, 0, FX25_NROOTS); + + i = 0; + + while (i < FX25_NN - FX25_NROOTS - pad) + { + feedback = FX25_INDEX_OF[data[i] ^ parity[0]]; + + if (feedback != FX25_A0) + { + j = 1; + while (j < FX25_NROOTS) + { + parity[j] = parity[j] ^ FX25_ALPHA_TO[MODNN(feedback + FX25_GENPOLY[FX25_NROOTS - j])]; + j++; + } + } + move(&parity[1], &parity[0], FX25_NROOTS - 1); + + if (feedback != FX25_A0) + parity[FX25_NROOTS - 1] = FX25_ALPHA_TO[MODNN(feedback + FX25_GENPOLY[0])]; + else + parity[FX25_NROOTS - 1] = 0; + i++; + } +} + + +int FX25_MIN(int a, int b) +{ + if (a > b) + return b; + else + return a; +} + + +int decode_rs(Byte * data, int * eras_pos, int no_eras, int pad) +{ + int deg_lambda, el, deg_omega; + int i, j, r, k; + Byte q, tmp, num1, num2, den, discr_r; + Byte s[256]; + Byte lambda[256]; + Byte b[256]; + Byte t[256]; + Byte omega[256]; + Byte root[256]; + Byte reg[256]; + Byte loc[256]; + int syn_error, count = 0; + + if (pad < 0 || pad>238) + return -1; + + i = 0; + while (i < FX25_NROOTS) + { + s[i] = data[0]; + i++; + + } + j = 1; + while (j < FX25_NN - pad) + { + i = 0; + while (i < FX25_NROOTS) + { + if (s[i] == 0) + s[i] = data[j]; + else + s[i] = data[j] ^ FX25_ALPHA_TO[MODNN(FX25_INDEX_OF[s[i]] + (FX25_FCR + i)*FX25_PRIM)]; + i++; + } + j++; + + } + syn_error = 0; + i = 0; + while (i < FX25_NROOTS) + { + syn_error = syn_error | s[i]; + s[i] = FX25_INDEX_OF[s[i]]; + i++; + } + if (syn_error == 0) + return count; + + memset(&lambda[1], 0, FX25_NROOTS); + lambda[0] = 1; + i = 0; + while (i < FX25_NROOTS + 1) + { + b[i] = FX25_INDEX_OF[lambda[i]]; + i++; + } + + r = no_eras; + el = no_eras; + r++; + while (r <= FX25_NROOTS) + { + discr_r = 0; + i = 0; + while (i < r) + { + if (lambda[i] != 0 && s[r - i - 1] != FX25_A0) + discr_r = discr_r ^ FX25_ALPHA_TO[MODNN(FX25_INDEX_OF[lambda[i]] + s[r - i - 1])]; + i++; + } + discr_r = FX25_INDEX_OF[discr_r]; + if (discr_r == FX25_A0) + { + move(&b[0], &b[1], FX25_NROOTS); + b[0] = FX25_A0; + } + else + { + t[0] = lambda[0]; + i = 0; + while (i < FX25_NROOTS) + { + if (b[i] != FX25_A0) + t[i + 1] = lambda[i + 1] ^ FX25_ALPHA_TO[MODNN(discr_r + b[i])]; + else + t[i + 1] = lambda[i + 1]; + i++; + } + + if (2 * el <= r + no_eras - 1) + { + el = r + no_eras - el; + i = 0; + while (i <= FX25_NROOTS) + { + if (lambda[i] == 0) + b[i] = FX25_A0; + else + b[i] = MODNN(FX25_INDEX_OF[lambda[i]] - discr_r + FX25_NN); + i++; + } + } + else + { + move(&b[0], &b[1], FX25_NROOTS); + b[0] = FX25_A0; + } + move(t, lambda, FX25_NROOTS + 1); + } + r++; + } + deg_lambda = 0; + i = 0; + while (i < FX25_NROOTS + 1) + { + lambda[i] = FX25_INDEX_OF[lambda[i]]; + if (lambda[i] != FX25_A0) + deg_lambda = i; + i++; + } + move(&lambda[1], ®[1], FX25_NROOTS); + count = 0; + i = 1; + k = FX25_IPRIM - 1; + while (i <= FX25_NN) + { + q = 1; + j = deg_lambda; + while (j > 0) + { + if (reg[j] != FX25_A0) + { + reg[j] = MODNN(reg[j] + j); + q = q ^ FX25_ALPHA_TO[reg[j]]; + } + j--; + } + if (q == 0) + { + root[count] = i; + loc[count] = k; + count++; + if (count == deg_lambda) + break; + } + i++; + k = MODNN(k + FX25_IPRIM); + } + if (deg_lambda != count) + return -1; + + deg_omega = deg_lambda - 1; + i = 0; + while (i <= deg_omega) + { + tmp = 0; + j = i; + while (j >= 0) + { + if (s[i - j] != FX25_A0 && lambda[j] != FX25_A0) + tmp = tmp ^ FX25_ALPHA_TO[MODNN(s[i - j] + lambda[j])]; + j--; + } + omega[i] = FX25_INDEX_OF[tmp]; + i++; + } + j = count - 1; + while (j >= 0) + { + num1 = 0; + i = deg_omega; + while (i >= 0) + { + if (omega[i] != FX25_A0) + num1 = num1 ^ FX25_ALPHA_TO[MODNN(omega[i] + i * root[j])]; + i--; + } + + num2 = FX25_ALPHA_TO[MODNN(root[j] * (FX25_FCR - 1) + FX25_NN)]; + den = 0; + i = FX25_MIN(deg_lambda, FX25_NROOTS - 1) & 0xFE; + while (i >= 0) + { + if (lambda[i + 1] != FX25_A0) + den = den ^ FX25_ALPHA_TO[MODNN(lambda[i + 1] + i * root[j])]; + i = i - 2; + } + if (num1 != 0 && loc[j] >= pad) + data[loc[j] - pad] = data[loc[j] - pad] ^ FX25_ALPHA_TO[MODNN(FX25_INDEX_OF[num1] + FX25_INDEX_OF[num2] + FX25_NN - FX25_INDEX_OF[den])]; + j--; + } + + return count; +} + + +void fx25_encode_rs(Byte * data, Byte *parity, int pad, int rs_size) +{ + switch (rs_size) + { + case 8: + move(&FX25_CCSDS_poly_8[0], &FX25_GENPOLY[0], 9); + FX25_NROOTS = rs_size; + encode_rs(data, parity, 0); + return; + + case 16: + move(&FX25_CCSDS_poly_16[0], &FX25_GENPOLY[0], 17); + FX25_NROOTS = rs_size; + encode_rs(data, parity, 0); + return; + + + + case 32: + + move(&FX25_CCSDS_poly_32[0], &FX25_GENPOLY[0], 33); + FX25_NROOTS = rs_size; + encode_rs(data, parity, 0); + return; + + case 64: + + move(&FX25_CCSDS_poly_64[0], &FX25_GENPOLY[0], 65); + FX25_NROOTS = rs_size; + encode_rs(data, parity, 0); + return; + + } +} + +int fx25_decode_rs(Byte * data, int * eras_pos, int no_eras, int pad, int rs_size) +{ + switch (rs_size) + { + case 8: + + move(&FX25_CCSDS_poly_8[0], &FX25_GENPOLY[0], 9); + FX25_NROOTS = rs_size; + return decode_rs(data, eras_pos, no_eras, pad); + + case 16: + + move(&FX25_CCSDS_poly_16[0], &FX25_GENPOLY[0], 17); + FX25_NROOTS = rs_size; + return decode_rs(data, eras_pos, no_eras, pad); + + case 32: + + move(&FX25_CCSDS_poly_32[0], &FX25_GENPOLY[0], 33); + FX25_NROOTS = rs_size; + return decode_rs(data, eras_pos, no_eras, pad); + + case 64: + + move(&FX25_CCSDS_poly_64[0], &FX25_GENPOLY[0], 65); + FX25_NROOTS = rs_size; + return decode_rs(data, eras_pos, no_eras, pad); + + default: + + return -1; + } +} + + diff --git a/ax25_l2.c b/ax25_l2.c new file mode 100644 index 0000000..a99a613 --- /dev/null +++ b/ax25_l2.c @@ -0,0 +1,1626 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" + +extern int RSID_SABM[4]; +extern int RSID_UI[4]; +extern int RSID_SetModem[4]; +extern int needRSID[4]; +extern int needRSID[4]; + + +/* + +unit ax25_l2; + +interface + +uses sysutils,classes; + + procedure frame_optimize(snd_ch,port: byte; var buf: TStringList); + procedure analiz_frame(snd_ch: byte; frame,code: string); + procedure send_data_buf(snd_ch,port: byte; path: string; nr: byte); + procedure add_pkt_buf(snd_ch,port: byte; data: string); + procedure set_link(snd_ch,port: byte; path: string); + procedure set_unlink(socket: integer; snd_ch,port: byte; path: string); + procedure set_chk_link(snd_ch,port: byte; path: string); + procedure UpdateActiveConnects(snd_ch: byte); + procedure timer_event; + procedure rst_values(snd_ch,port: byte); + procedure rst_timer(snd_ch,port: byte); + function get_free_port(snd_ch: byte; var port: byte): boolean; + function get_user_port_by_calls(snd_ch: byte; var port: byte; CallFrom,CallTo: string): boolean; + +implementation + +uses ax25,ax25_agw,sm_main,kiss_mode; +*/ + +string * make_frame(string * data, Byte * path, Byte pid, Byte nr, Byte ns, Byte f_type, Byte f_id, boolean rpt, boolean pf, boolean cr); +void rst_t3(TAX25Port * AX25Sess); + +TAX25Port * get_user_port(int snd_ch, Byte * path); + +void inc_frack(TAX25Port * AX25Sess) +{ + AX25Sess->clk_frack++; +} + + +void rst_frack(TAX25Port * AX25Sess) +{ + AX25Sess->clk_frack = 0; +} + +void inc_t1(TAX25Port * AX25Sess) +{ + AX25Sess->t1++; +} + +void rst_t1(TAX25Port * AX25Sess) +{ + AX25Sess->t1 = 0; +} + +void inc_t3(TAX25Port * AX25Sess) +{ + AX25Sess->t3++; +} + +void rst_t3(TAX25Port * AX25Sess) +{ + AX25Sess->t3 = 0; +} + + +void rst_values(TAX25Port * AX25Sess) +{ + AX25Sess->IPOLL_cnt = 0; + AX25Sess->hi_vs = 0; + AX25Sess->vs = 0; + AX25Sess->vr = 0; + Clear(&AX25Sess->I_frame_buf); + Clear(&AX25Sess->in_data_buf); + Clear(&AX25Sess->frm_collector); + + ax25_info_init(AX25Sess); + clr_frm_win(AX25Sess); +} + + +void rst_timer(TAX25Port * AX25Sess) +{ + rst_frack(AX25Sess); + rst_t1(AX25Sess); + rst_t3(AX25Sess); +} + +void upd_i_lo(TAX25Port * AX25Sess, int n) //Update the counter of the first frame in the I-frame buffer +{ + AX25Sess->i_lo = n; +} + +void upd_i_hi(TAX25Port * AX25Sess, int n) //Update last frame counter in I-frame buffer +{ + AX25Sess->i_hi = n; +} + +void upd_vs(TAX25Port * AX25Sess, int n) //Update the counter of the next frame to transmit +{ + AX25Sess->vs = ++n & 7; +} + +void upd_vr(TAX25Port * AX25Sess, int n) //Refresh the counter of the next frame at the reception +{ + AX25Sess->vr = ++n & 7; +} + + +void Frame_Optimize(TAX25Port * AX25Sess, TStringList * buf) +{ + // I think this removes redundant frames from the TX Queue (eg repeated RR frames) + + string * frame; + Byte path[80]; + string * data = newString(); + + Byte pid, nr, ns, f_type, f_id, rpt, cr, pf; + boolean curr_req, optimize; + int i, k; + char need_frm[8] = ""; + int index = 0; + boolean PollRR; + boolean PollREJ; + + PollRR = FALSE; + PollREJ = FALSE; + curr_req = FALSE; + + // Check Poll RR and REJ frame + + i = 0; + + while (i < buf->Count && !PollREJ) + { + frame = Strings(buf, i); + // TX frame has kiss control on front + + decode_frame(frame->Data + 1, frame->Length - 1, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + if (cr == SET_R && pf == SET_P) + { + if (f_id == S_REJ) + PollREJ = TRUE; + else if (f_id == S_RR && nr == AX25Sess->vr) + PollRR = TRUE; + } + i++; + } + + // Performance of the REJ Cards: Optional Rej Cards + + i = 0; + + while (i < buf->Count) + { + optimize = TRUE; + frame = Strings(buf, i); + decode_frame(frame->Data + 1, frame->Length - 1, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + if (f_id == S_REJ && cr == SET_R) + { + if ((pf == SET_F && PollREJ) || nr != AX25Sess->vr) + { + Debugprintf("Optimizer dropping REJ nr %d vr %d pf %d PollREJ %d", nr, AX25Sess->vr, pf, PollREJ); + Delete(buf, i); + optimize = FALSE; + } + if (nr == AX25Sess->vr) + curr_req = TRUE; + } + if (optimize) + i++; + } + + // Performance Options + + i = 0; + + while (i Count) + { + need_frm[0] = 0; + index = 0; + k = AX25Sess->i_lo; + + while (k != AX25Sess->vs) + { + need_frm[index++] = k + 'A'; + k = (++k) & 7; + } + + optimize = TRUE; + + frame = Strings(buf, i); + + decode_frame(frame->Data +1 , frame->Length - 1, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + if (f_id == S_RR) + { + // RR Cards Methods: Optional RR, F Cards + if (cr == SET_R) + { + if (nr != AX25Sess->vr || ((pf == SET_F) && PollRR) || curr_req) + { + Debugprintf("Optimizer dropping RR nr %d vr %d pf %d PollRR %d", nr, AX25Sess->vr, pf, PollRR); + + Delete(buf, i); + optimize = FALSE; + } + } + + + // RR Cards Methods : Optional RR, P Cards + if (cr == SET_C) + { + if (AX25Sess->status == STAT_LINK || AX25Sess->status == STAT_WAIT_ANS) + { + Debugprintf("Optimizer dropping RR nr %d vr %d pf %d PollRR %d", nr, AX25Sess->vr, pf, PollRR); + Delete(buf, i); + optimize = FALSE; + } + } + } + // I - Cards Methods : Options for I - Cards + else if (f_id == I_I) + { + if (strchr(need_frm, ns + 'A') == 0) + { + Delete(buf, i); + optimize = FALSE; + } + else + { + if (nr != AX25Sess->vr) + buf->Items[i] = make_frame(data, path, pid, AX25Sess->vr, ns, f_type, f_id, rpt, pf, cr); + } + } + + // SABM Applications + + if (f_id == U_SABM) + { + if (AX25Sess->status != STAT_TRY_LINK) + { + Delete(buf, i); + optimize = FALSE; + } + } + + if (optimize) + i++; + } +} + +void add_pkt_buf(TAX25Port * AX25Sess, string * data) +{ + boolean found = 0; + string * frm; + int i = 0; + + while (i < AX25Sess->frame_buf.Count && !found) + { + found = compareStrings(Strings(&AX25Sess->frame_buf, i++), data); + } + + if (found) + freeString(data); + else + Add(&AX25Sess->frame_buf, data); +} + +void add_I_FRM(TAX25Port * AX25Sess, Byte * path) +{ + string * data; + int i; + + upd_i_lo(AX25Sess, AX25Sess->vs); + + while (AX25Sess->in_data_buf.Count > 0 && AX25Sess->I_frame_buf.Count != maxframe[AX25Sess->snd_ch]) + { + data = duplicateString(Strings(&AX25Sess->in_data_buf, 0)); + Delete(&AX25Sess->in_data_buf, 0); + Add(&AX25Sess->I_frame_buf, data); + } + if (AX25Sess->I_frame_buf.Count > 0) + { + for (i = 0; i < AX25Sess->I_frame_buf.Count; i++) + { + upd_i_hi(AX25Sess, AX25Sess->vs); + upd_vs(AX25Sess, AX25Sess->vs); + AX25Sess->hi_vs = AX25Sess->vs; // Last transmitted frame + } + } +} + + +void delete_I_FRM(TAX25Port * AX25Sess, int nr) +{ + int i; + + i = AX25Sess->i_lo; + + while (i != nr) + { + if (AX25Sess->I_frame_buf.Count > 0) + { + AX25Sess->info.stat_s_pkt++; + AX25Sess->info.stat_s_byte += Strings(&AX25Sess->I_frame_buf, 0)->Length; + Delete(&AX25Sess->I_frame_buf, 0); + } + + i++; + i &= 7; + } + upd_i_lo(AX25Sess, nr); +} + +void delete_I_FRM_port(TAX25Port * AX25Sess) +{ + string * frame; + string path = { 0 }; + string data= { 0 }; + + Byte pid, nr, ns, f_type, f_id, rpt, cr, pf; + boolean optimize; + int i = 0; + + while (i < AX25Sess->frame_buf.Count) + { + optimize = TRUE; + frame = Strings(&AX25Sess->frame_buf, i); + + decode_frame(frame->Data, frame->Length, &path, &data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + if (f_id == I_I) + { + Delete(&AX25Sess->frame_buf, i); + optimize = FALSE; + } + if (optimize) + i++; + } +} + +void send_data_buf(TAX25Port * AX25Sess, int nr) +{ + int i; + boolean new_frames; + boolean PF_bit; + + if (AX25Sess->status != STAT_LINK) + return; + + AX25Sess->IPOLL_cnt = 0; + AX25Sess->vs = nr; + delete_I_FRM(AX25Sess, nr); // ?? free acked frames +// delete_I_FRM_port(AX25Sess); + + if (TXFrmMode[AX25Sess->snd_ch] == 1) + { + new_frames = FALSE; + + if (AX25Sess->I_frame_buf.Count < 2) + { + add_I_FRM(AX25Sess, AX25Sess->Path); + AX25Sess->status = STAT_LINK; + new_frames = TRUE; + } + + if (AX25Sess->I_frame_buf.Count > 0) + { + if (new_frames) + { + for (i = 0; i < AX25Sess->I_frame_buf.Count; i++) + { + if (i == AX25Sess->I_frame_buf.Count - 1) + PF_bit = SET_P; + else + PF_bit = SET_F; + + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, i), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, ((AX25Sess->i_lo + i) & 7), I_FRM, I_I, FALSE, PF_bit, SET_C)); + } + } + if (!new_frames) + { + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, 0), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, AX25Sess->i_lo, I_FRM, I_I, FALSE, SET_P, SET_C)); //SET_P + upd_vs(AX25Sess, AX25Sess->vs); + } + AX25Sess->status = STAT_WAIT_ANS; + rst_timer(AX25Sess); + } + } + + if (TXFrmMode[AX25Sess->snd_ch] == 0) + { + add_I_FRM(AX25Sess, AX25Sess->Path); + AX25Sess->status = STAT_LINK; + + if (AX25Sess->I_frame_buf.Count > 0) + { + for (i = 0; i < AX25Sess->I_frame_buf.Count; i++) + { + if (i == AX25Sess->I_frame_buf.Count - 1) + PF_bit = SET_P; + else + PF_bit = SET_F; + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, i), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, ((AX25Sess->i_lo + i) & 7), I_FRM, I_I, FALSE, PF_bit, SET_C)); + } + AX25Sess->status = STAT_WAIT_ANS; + rst_timer(AX25Sess); + } + } +} + + +void send_data_buf_srej(TAX25Port * AX25Sess, int nr) +{ + // Similar to send_data_buf but only sends the requested I frame with P set + + int i = 0; + boolean new_frames; + boolean PF_bit; + + if (AX25Sess->status != STAT_LINK) + return; + + AX25Sess->IPOLL_cnt = 0; + AX25Sess->vs = nr; + delete_I_FRM(AX25Sess, nr); // ?? free acked frames + + new_frames = FALSE; + + add_I_FRM(AX25Sess, AX25Sess->Path); + AX25Sess->status = STAT_LINK; + new_frames = TRUE; + + if (AX25Sess->I_frame_buf.Count > 0) + { + if (new_frames) + { + PF_bit = SET_P; + + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, i), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, ((AX25Sess->i_lo + i) & 7), I_FRM, I_I, FALSE, PF_bit, SET_C)); + } + else + { + + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, 0), AX25Sess->Path, AX25Sess->PID, AX25Sess->vr, AX25Sess->i_lo, I_FRM, I_I, FALSE, SET_P, SET_C)); //SET_P + upd_vs(AX25Sess, AX25Sess->vs); + } + } + AX25Sess->status = STAT_WAIT_ANS; + rst_timer(AX25Sess); +} + +void write_frame_collector(TAX25Port * AX25Sess, int ns, string * data) +{ + Byte i; + char frm_ns; + string * frm; + boolean found ; + boolean need_frm; + + if (max_frame_collector[AX25Sess->snd_ch] < 1) + return; + + need_frm = FALSE; + i = 1; + do + { + if (ns == (AX25Sess->vr + i) & 7) + need_frm = TRUE; + + i++; + + } while (i <= max_frame_collector[AX25Sess->snd_ch] && !need_frm); + + if (need_frm) + { + frm_ns = ns; + found = FALSE; + i = 0; + + if (AX25Sess->frm_collector.Count > 0) + { + do + { + frm = Strings(&AX25Sess->frm_collector, i); + + if (frm_ns == frm->Data[0]) + found = TRUE; + + i++; + } + while (!found && i != AX25Sess->frm_collector.Count); + + } + + if (!found) + { + string * frm = newString(); + + stringAdd(frm, (char *)&frm_ns, 1); + stringAdd(frm, data->Data, data->Length); + Add(&AX25Sess->frm_collector, frm); + } + } +} + +string * read_frame_collector(TAX25Port * AX25Sess, boolean fecflag) +{ + // Look for required frame no in saved frames + + string * frm; + string * data = newString(); + + int i = 0; + boolean found = FALSE; + Byte frm_ns; + + while (i < AX25Sess->frm_collector.Count) + { + frm = duplicateString(Strings(&AX25Sess->frm_collector, i)); + + frm_ns = frm->Data[0]; + + if (frm_ns == AX25Sess->vr) + { + Delete(&AX25Sess->frm_collector, i); + + upd_vr(AX25Sess, AX25Sess->vr); + + mydelete(frm, 0, 1); // Remove vr from front + + stringAdd(data, frm->Data, frm->Length); + + found = TRUE; + + AX25Sess->info.stat_r_pkt++; + AX25Sess->info.stat_r_fc++; + + if (fecflag) + AX25Sess->info.stat_fec_count++; + + AX25Sess->info.stat_r_byte += frm->Length; + AX25Sess->frm_win[frm_ns].Length = frm->Length; //Save the frame to the window buffer + AX25Sess->frm_win[frm_ns].Data = frm->Data; //Save the frame to the window buffer + } + + i++; + } + return data; +} + + +/////////////////////////// SET-FRAMES ////////////////////////////////// + +void set_chk_link(TAX25Port * AX25Sess, Byte * path) +{ + boolean b_IPOLL; + int len; + + AX25Sess->status = STAT_CHK_LINK; + + // if AX25Sess->digi<>'' then path:=path+','+reverse_digi(AX25Sess->digi); + + b_IPOLL = FALSE; + + if (AX25Sess->I_frame_buf.Count > 0 && AX25Sess->IPOLL_cnt < 2) + { + len = Strings(&AX25Sess->I_frame_buf, 0)->Length; + + if (len > 0 && len <= IPOLL[AX25Sess->snd_ch]) + { + + b_IPOLL = TRUE; + AX25Sess->IPOLL_cnt++; + } + } + if (b_IPOLL) + add_pkt_buf(AX25Sess, make_frame(Strings(&AX25Sess->I_frame_buf, 0), path, AX25Sess->PID, AX25Sess->vr, AX25Sess->i_lo, I_FRM, I_I, FALSE, SET_P, SET_C)); + else + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0xF0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, SET_P, SET_C)); + + inc_frack(AX25Sess); +} + + + +// This seems to start a connection + +void set_link(TAX25Port * AX25Sess, UCHAR * axpath) +{ + if (AX25Sess->status != STAT_LINK) + { + string nullstring; + nullstring.Length = 0; + + rst_values(AX25Sess); + + AX25Sess->status = STAT_TRY_LINK; + + // if (AX25Sess->digi[0] != 0) + // path: = path + ',' + reverse_digi(AX25Sess->digi); + + add_pkt_buf(AX25Sess, make_frame(&nullstring, axpath, 0, 0, 0, U_FRM, U_SABM, SET_NO_RPT, SET_P, SET_C)); + + inc_frack(AX25Sess); + } +} + +#define MODE_OUR 0 + +void set_try_unlink(TAX25Port * AX25Sess, Byte * path) +{ + string nullstring; + nullstring.Length = 0; + + AX25Sess->status = STAT_TRY_UNLINK; + add_pkt_buf(AX25Sess, make_frame(&nullstring, path, 0, 0, 0, U_FRM, U_DISC, SET_NO_RPT, SET_P, SET_C)); + inc_frack(AX25Sess); +} + + +void set_unlink(TAX25Port * AX25Sess, Byte * path) +{ + if (AX25Sess->status == STAT_TRY_UNLINK + || AX25Sess->status == STAT_TRY_LINK + || AX25Sess->status == STAT_NO_LINK) + { + string nullstring; + nullstring.Length = 0; + + // if (AX25Sess->digi[0] != 0) + // path: = path + ',' + reverse_digi(AX25Sess->digi); + + AGW_AX25_disc(AX25Sess, MODE_OUR); + + if (AX25Sess->status != STAT_TRY_UNLINK) + add_pkt_buf(AX25Sess, make_frame(&nullstring, path, 0, 0, 0, U_FRM, U_DISC, SET_NO_RPT, SET_P, SET_C)); + + AX25Sess->info.stat_end_ses = time(NULL); + + write_ax25_info(AX25Sess); + rst_values(AX25Sess); + + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + AX25Sess->status = STAT_NO_LINK; + + } + else + set_try_unlink(AX25Sess, AX25Sess->Path); +} + +void set_FRMR(int snd_ch, Byte * path, unsigned char frameType) +{ + //We may not have a session when sending FRMR so reverse path and send + + Byte revpath[80]; + string * Data = newString(); + + Data->Data[0] = frameType; + Data->Data[1] = 0; + Data->Data[2] = 1; // Invalid CTL Byte + Data->Length = 3; + + reverse_addr(path, revpath, strlen(path)); + + add_pkt_buf(&AX25Port[snd_ch][0], make_frame(Data, revpath, 0, 0, 0, U_FRM, U_FRMR, FALSE, SET_P, SET_R)); + + freeString(Data); +} + +void set_DM(int snd_ch, Byte * path) +{ + //We may not have a session when sending DM so reverse path and send + + Byte revpath[80]; + + reverse_addr(path, revpath, strlen(path)); + + add_pkt_buf(&AX25Port[snd_ch][0], make_frame(NULL, revpath, 0, 0, 0, U_FRM,U_DM,FALSE,SET_P,SET_R)); +} + +/////////////////////////// S-FRAMES //////////////////////////////////// + + +void on_RR(TAX25Port * AX25Sess, Byte * path, int nr, int pf, int cr) +{ + char need_frame[16] = ""; + int index = 0; + + int i; + + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + if (cr == SET_C) + set_DM(AX25Sess->snd_ch, path); + + return; + } + + if (cr == SET_R) + { + // Determine which frames could get into the user’s frame buffer. + i = AX25Sess->vs; + + need_frame[index++] = i + '0'; + +// Debugprintf("RR Rxed vs = %d hi_vs = %d", AX25Sess->vs, AX25Sess->hi_vs); + while (i != AX25Sess->hi_vs) + { + i = (i++) & 7; + need_frame[index++] = i + '0'; + if (index > 10) + { + Debugprintf("Index corrupt %d need_frame = %s", index, need_frame); + break; + } + } + + //Clear the received frames from the transmission buffer. + + if (AX25Sess->status == STAT_WAIT_ANS) + delete_I_FRM(AX25Sess, nr); + + // We restore the link if the number is valid + + if (AX25Sess->status == STAT_CHK_LINK || strchr(need_frame, nr + '0') > 0) + { + rst_timer(AX25Sess); + AX25Sess->status = STAT_LINK; + send_data_buf(AX25Sess, nr); + } + } + + if (cr == SET_C) + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, pf, SET_R)); + + rst_t3(AX25Sess); +} + + +void on_RNR(TAX25Port * AX25Sess, Byte * path, int nr, int pf, int cr) +{ + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + if (cr == SET_C) + set_DM(AX25Sess->snd_ch, path); + + return; + } + + if (cr == SET_R) + { + rst_timer(AX25Sess); + + if (AX25Sess->status == STAT_WAIT_ANS) + delete_I_FRM(AX25Sess, nr); + + AX25Sess->status = STAT_CHK_LINK; + } + + if (cr == SET_C) + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, SET_P, SET_R)); + + rst_t3(AX25Sess); +} + + +void on_REJ(TAX25Port * AX25Sess, Byte * path, int nr, int pf, int cr) +{ + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + if (cr == SET_C) + set_DM(AX25Sess->snd_ch, path); + + return; + } + + if (cr == SET_R) + { + rst_timer(AX25Sess); + AX25Sess->status = STAT_LINK; + + send_data_buf(AX25Sess, nr); + } + + if (cr == SET_C) + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, SET_P, SET_R)); +} + + +void on_SREJ(TAX25Port * AX25Sess, Byte * path, int nr, int pf, int cr) +{ + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + if (cr == SET_C) + set_DM(AX25Sess->snd_ch, path); + + return; + } + + if (cr == SET_R) + { + rst_timer(AX25Sess); + AX25Sess->status = STAT_LINK; + + send_data_buf_srej(AX25Sess, nr); + } + + if (cr == SET_C) + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, SET_P, SET_R)); +} +/////////////////////////// I-FRAMES //////////////////////////////////// + +void on_I(void * socket, TAX25Port * AX25Sess, int PID, Byte * path, string * data, int nr, int ns, int pf, int cr, boolean fecflag) +{ + string * collector_data; + int i; + Byte need_frame[16] = ""; + int index = 0; + { + if (AX25Sess->status == STAT_TRY_LINK) + return; + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_UNLINK) + { + set_DM(0, path); + return; + } + + if (busy) + { + add_pkt_buf(AX25Sess, make_frame(NULL, path, PID, AX25Sess->vr, 0, S_FRM, S_RNR, FALSE, pf, SET_R)); + return; + } + + // Determine which frames could get into the user’s frame buffer. + + i = AX25Sess->vs; + + need_frame[index++] = i + '0'; + +// Debugprintf("I Rxed vs = %d hi_vs = %d", AX25Sess->vs, AX25Sess->hi_vs); + + while (i != AX25Sess->hi_vs) + { + i = (i++) & 7; + need_frame[index++] = i + '0'; + if (index > 10) + { + Debugprintf("Index corrupt %d need_frame = %s", index, need_frame); + break; + } + } + + //Clear received frames from the transmission buffer. + + if (AX25Sess->status == STAT_WAIT_ANS) + delete_I_FRM(AX25Sess, nr); + + //We restore the link if the number is valid + + if (AX25Sess->status == STAT_CHK_LINK || strchr(need_frame, nr + '0') > 0) + { + //We restore the link if the number is valid + + rst_timer(AX25Sess); + AX25Sess->status = STAT_LINK; + send_data_buf(AX25Sess, nr); + } + + if (ns == AX25Sess->vr) + { + //If the expected frame, send RR, F + + AX25Sess->info.stat_r_pkt++; + AX25Sess->info.stat_r_byte += data->Length; + + if (fecflag) + AX25Sess->info.stat_fec_count++; + + upd_vr(AX25Sess, AX25Sess->vr); + + AX25Sess->frm_win[ns].Length = data->Length; //Save the frame to the window buffer + AX25Sess->frm_win[ns].Data = data->Data; //Save the frame to the window buffer + + collector_data = read_frame_collector(AX25Sess, fecflag); + + AGW_AX25_data_in(socket, AX25Sess->snd_ch, PID, path, stringAdd(data, collector_data->Data, collector_data->Length)); + + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_RR, FALSE, pf, SET_R)); + } + else + { + // If the frame is not expected, send REJ, F + + if ((AX25Sess->frm_win[ns].Length != data->Length) && + memcmp(&AX25Sess->frm_win[ns].Data, data->Data, data->Length) != 0) + + write_frame_collector(AX25Sess, ns, data); + + add_pkt_buf(AX25Sess, make_frame(NULL, path, 0, AX25Sess->vr, 0, S_FRM, S_REJ, FALSE, pf, SET_R)); + } + rst_t3(AX25Sess); + } +} +/////////////////////////// U-FRAMES //////////////////////////////////// + + +void on_SABM(void * socket, TAX25Port * AX25Sess) +{ + if (AX25Sess->status == STAT_TRY_UNLINK) + { + AX25Sess->info.stat_end_ses = time(NULL); + + write_ax25_info(AX25Sess); + rst_values(AX25Sess); + + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + + AGW_AX25_disc(AX25Sess, MODE_OTHER); + Clear(&AX25Sess->frame_buf); + + AX25Sess->status = STAT_NO_LINK; + } + + if (AX25Sess->status == STAT_TRY_LINK) + { + AGW_AX25_disc(AX25Sess, MODE_OTHER); + + rst_timer(AX25Sess); + rst_values(AX25Sess); + Clear(&AX25Sess->frame_buf); + AX25Sess->status = STAT_NO_LINK; + } + + if (AX25Sess->status != STAT_NO_LINK) + { + if ((strcmp(AX25Sess->kind, "Outgoing") == 0) || + AX25Sess->status == STAT_TRY_UNLINK || AX25Sess->info.stat_s_byte > 0 || + AX25Sess->info.stat_r_byte > 0 || AX25Sess->frm_collector.Count > 0) + { + AX25Sess->info.stat_end_ses = time(NULL); + AGW_AX25_disc(AX25Sess, MODE_OTHER); + write_ax25_info(AX25Sess); + rst_timer(AX25Sess); + rst_values(AX25Sess); + Clear(&AX25Sess->frame_buf); + AX25Sess->status = STAT_NO_LINK; + } + } + + if (AX25Sess->status == STAT_NO_LINK) + { + AGW_AX25_conn(AX25Sess, AX25Sess->snd_ch, MODE_OTHER); + AX25Sess->vr = 0; + AX25Sess->vs = 0; + AX25Sess->hi_vs = 0; + Clear(&AX25Sess->frm_collector); + clr_frm_win(AX25Sess); + AX25Sess->status = STAT_LINK; + AX25Sess->info.stat_begin_ses = time(NULL); + strcpy(AX25Sess->kind, "Incoming"); + AX25Sess->socket = socket; + + if (RSID_SABM[AX25Sess->snd_ch]) // Send RSID before SABM/UA + needRSID[AX25Sess->snd_ch] = 1; + + } + + add_pkt_buf(AX25Sess, make_frame(NULL, AX25Sess->Path, 0, 0, 0, U_FRM, U_UA, FALSE, SET_P, SET_R)); +} + +void on_DISC(void * socket, TAX25Port * AX25Sess) +{ + if (AX25Sess->status != STAT_NO_LINK) + { + AX25Sess->info.stat_end_ses = time(NULL); + AGW_AX25_disc(AX25Sess, MODE_OTHER); + write_ax25_info(AX25Sess); + } + + if (AX25Sess->status == STAT_NO_LINK || AX25Sess->status == STAT_TRY_LINK) + set_DM(AX25Sess->snd_ch, AX25Sess->Path); + else + { + add_pkt_buf(AX25Sess, make_frame(NULL, AX25Sess->Path, 0, 0, 0, U_FRM, U_UA, FALSE, SET_P, SET_R)); + + if (RSID_SABM[AX25Sess->snd_ch]) // Send RSID before SABM/UA + needRSID[AX25Sess->snd_ch] = 1; + } + + rst_values(AX25Sess); + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + AX25Sess->status = STAT_NO_LINK; +} + +void on_DM(void * socket, TAX25Port * AX25Sess) +{ + if (AX25Sess->status != STAT_NO_LINK) + { + AX25Sess->info.stat_end_ses = time(NULL); + AGW_AX25_disc(AX25Sess, MODE_OTHER); + write_ax25_info(AX25Sess); + } + + rst_timer(AX25Sess); + rst_values(AX25Sess); + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + AX25Sess->status = STAT_NO_LINK; +} + + +void on_UA(void *socket, TAX25Port * AX25Sess) +{ + switch (AX25Sess->status) + { + case STAT_TRY_LINK: + + AX25Sess->info.stat_begin_ses = time(NULL); + AX25Sess->status = STAT_LINK; + AGW_AX25_conn(AX25Sess, AX25Sess->snd_ch, MODE_OUR); + break; + + case STAT_TRY_UNLINK: + + AX25Sess->info.stat_end_ses = time(NULL); + AGW_AX25_disc(AX25Sess, MODE_OUR); + write_ax25_info(AX25Sess); + + rst_values(AX25Sess); + AX25Sess->status = STAT_NO_LINK; + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + break; + } + + rst_timer(AX25Sess); +} + +void on_UI(TAX25Port * AX25Sess, int pf, int cr) +{ +} + +void on_FRMR(void * socket, TAX25Port * AX25Sess, Byte * path) +{ + if (AX25Sess->status != STAT_NO_LINK) + { + AX25Sess->info.stat_end_ses = time(NULL); + + AGW_AX25_disc(socket, AX25Sess->snd_ch, MODE_OTHER, path); + write_ax25_info(AX25Sess); + } + + set_DM(AX25Sess->snd_ch, path); + + rst_values(AX25Sess); + memset(AX25Sess->corrcall, 0, 10); + memset(AX25Sess->mycall, 0, 10); + AX25Sess->digi[0] = 0; + AX25Sess->status = STAT_NO_LINK; +} + + + +void UpdateActiveConnects(int snd_ch) +{ + int port; + + users[snd_ch] = 0; + + for (port = 0; port < port_num; port++) + if (AX25Port[snd_ch][port].status != STAT_NO_LINK) + users[snd_ch]++; +} + + +void timer_event() +{ + int snd_ch, port; + void * socket; + single frack; + Byte active; + TAX25Port * AX25Sess; + + TimerEvent = TIMER_EVENT_OFF; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + //reset the slottime timer + if (dyn_frack[snd_ch]) + { + UpdateActiveConnects(snd_ch); + if (users[snd_ch] > 0) + active = users[snd_ch] - 1; + else + active = 0; + + frack = frack_time[snd_ch] + frack_time[snd_ch] * active * 0.5; + } + else + frack = frack_time[snd_ch]; + + // + for (port = 0; port < port_num; port++) + { + AX25Sess = &AX25Port[snd_ch][port]; + + if (AX25Sess->status == STAT_NO_LINK) + continue; + + if (snd_status[snd_ch] != SND_TX) //when at the reception + if (AX25Sess->frame_buf.Count == 0) //when the transfer buffer is empty + inc_t1(AX25Sess); // we consider time of the timer of repeated requests + + // Wouldn't it make more sense to keep path in ax.25 struct? + + if (AX25Sess->t1 >= frack * 10 + (number_digi(AX25Sess->digi) * frack * 20)) + { + if (AX25Sess->clk_frack >= fracks[snd_ch]) + { + // This disconnects after retries expires + + rst_frack(AX25Sess); + + //socket:=get_incoming_socket_by_call(AX25Sess->mycall); + + set_unlink(AX25Sess, AX25Sess->Path); + } + + switch (AX25Sess->status) + { + case STAT_TRY_LINK: + + set_link(AX25Sess, AX25Sess->Path); + break; + + case STAT_TRY_UNLINK: + + set_try_unlink(AX25Sess, AX25Sess->Path); + break; + + case STAT_WAIT_ANS: + + set_chk_link(AX25Sess, AX25Sess->Path); + break; + + case STAT_CHK_LINK: + + set_chk_link(AX25Sess, AX25Sess->Path); + break; + } + + rst_t1(AX25Sess); + } + + + if (AX25Sess->t3 >= idletime[snd_ch] * 10) + { + set_chk_link(AX25Sess, AX25Sess->Path); + rst_t1(AX25Sess); + rst_t3(AX25Sess); + } + + if (AX25Sess->status == STAT_LINK) + inc_t3(AX25Sess); + + } + } + // KISS ACKMODE + //if (snd_status[snd_ch]<>SND_TX) and KISSServ then KISS_send_ack1(snd_ch); +} + +TAX25Port * get_free_port(int snd_ch) +{ + int i; + int need_free_port; + + i = 0; + + need_free_port = FALSE; + + while (i < port_num) + { + if (AX25Port[snd_ch][i].status == STAT_NO_LINK) + return &AX25Port[snd_ch][i]; + + i++; + } + return FALSE; +} + + + +TAX25Port * get_user_port(int snd_ch, Byte * path) + { + TAX25Port * AX25Sess = NULL; + + int i = 0; + int port = 0; + + + while (i < port_num) + { + AX25Sess = &AX25Port[snd_ch][i]; + + if (AX25Sess->status != STAT_NO_LINK && memcmp(AX25Sess->ReversePath, path, AX25Sess->pathLen) == 0) + return AX25Sess; + + i++; + } + + return FALSE; +} + +boolean get_user_dupe(int snd_ch, Byte * path) +{ + int i = 0; + TAX25Port * AX25Sess; + + while (i < port_num) + { + AX25Sess = &AX25Port[snd_ch][i]; + + if (AX25Sess->status != STAT_NO_LINK && memcmp(AX25Sess->ReversePath, path, AX25Sess->pathLen) == 0) + return TRUE; + + i++; + } + + return FALSE; +} + +TAX25Port * get_user_port_by_calls(int snd_ch, char * CallFrom, char * CallTo) +{ + int i = 0; + TAX25Port * AX25Sess = NULL; + + while (i < port_num) + { + AX25Sess = &AX25Port[snd_ch][i]; + + if (AX25Sess->status != STAT_NO_LINK && + strcmp(CallFrom, AX25Sess->mycall) == 0 && strcmp(CallTo, AX25Sess->corrcall) == 0) + + return AX25Sess; + + i++; + } + + return NULL; +} + +void * get_sock_by_port(TAX25Port * AX25Sess) +{ + void * socket = (void *)-1; + + if (AX25Sess) + socket = AX25Sess->socket; + + return socket; +} + +void Digipeater(int snd_ch, string * frame) +{ + boolean addr_end, flag_replaced, digi_stop; + word crc; + char call[16]; + Byte * addr = &frame->Data[7]; // Origon + int len = frame->Length - 2; // not FCS + string * frameCopy; + + int n = 8; // Max digis + + if (list_digi_callsigns[snd_ch].Count == 0) + return; + + // Look for first unused digi + + while ((addr[6] & 1) == 0 && n--) // until end of address + { + addr += 7; + + if ((addr[6] & 128) == 0) + { + // unused digi - is it addressed to us? + + UCHAR CRCString[2]; + + memcpy(call, addr, 7); + call[6] &= 0x7E; // Mask end of call + + // See if in digi list + + int i; + + for (i = 0; i < list_digi_callsigns->Count; i++) + { + if (memcmp(list_digi_callsigns->Items[i]->Data, call, 7) == 0) + { + // for us + + addr[6] |= 128; // set digi'ed + + // TX Frames need a KISS control on front + + frameCopy = newString(); + + frameCopy->Data[0] = 0; + frameCopy->Length = 1; + + stringAdd(frameCopy, frame->Data, frame->Length - 2); // Exclude CRC + + addr[6] &= 0x7f; // clear digi'ed from original; + + // Need to redo crc + + crc = get_fcs(frameCopy->Data, len); + + CRCString[0] = crc & 0xff; + CRCString[1] = crc >> 8; + + stringAdd(frameCopy, CRCString, 2); + + Add(&all_frame_buf[snd_ch], frameCopy); + + return; + } + } + } + } +} + +void analiz_frame(int snd_ch, string * frame, char * code, boolean fecflag) +{ + int port, free_port; + Byte path[80]; + string *data = newString(); + Byte pid, nr, ns, f_type, f_id, rpt, cr, pf; + boolean need_free_port; + void * socket = NULL; + boolean incoming_conn = 0; + Byte revpath[80]; + int pathlen; + Byte * ptr; + + int excluded = 0; + int len; + + TAX25Port * AX25Sess; + + // mod_icon_status = mod_rx; + + len = frame->Length; + + if (len < PKT_ERR) + return; + + 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; + + // if is_excluded_frm(snd_ch,f_id,data) then excluded:=TRUE; + + + if (excluded) + return; + + if (NonAX25[snd_ch]) + { + if (AGWServ) + AGW_Raw_monitor(snd_ch, frame); + if (KISSServ) + KISS_on_data_out(snd_ch, frame, 0); + } + + // CRC Collision Check + + if (!is_correct_path(path, pid)) + { + // Duff path - if Non-AX25 filter active log and discard + + if (NonAX25[snd_ch]) + { + put_frame(snd_ch, frame, "NON-AX25", FALSE, excluded); + return; + } + } + + put_frame(snd_ch, frame, code, 0, excluded); // Monitor + + if (!NonAX25[snd_ch]) + { + if (AGWServ) + AGW_Raw_monitor(snd_ch, frame); + + if (KISSServ) + KISS_on_data_out(snd_ch, frame, 0); + } + + // Digipeat frame + + Digipeater(snd_ch, frame); + + if (!is_last_digi(path)) + return; // Don't process if still unused digis + + // Clear reapeated bits from digi path + + ptr = &path[13]; + + while ((*ptr & 1) == 0) // end of address + { + ptr += 7; + *(ptr) &= 0x7f; // Clear digi'd bit + } + + // search for port of correspondent + + AX25Sess = get_user_port(snd_ch, path); + + // if not an active session, AX25Sess will be NULL + + if (AX25Sess == NULL) + socket = in_list_incoming_mycall(path); + else + socket = get_sock_by_port(AX25Sess); + + // link analysis + + if (AX25Sess == NULL) + { + // No Session. If socket is set (so call is in incoming calls list) and SABM set up session + + if (socket == NULL) + return; // not for us + + if (f_id != U_SABM) // Not SABM + { + // // send DM if P set + + if (cr == SET_C) + { + switch (f_id) + { + case U_DISC: + case S_RR: + case S_REJ: + case S_RNR: + case I_I: + + set_DM(snd_ch, path); + break; + + case U_UI: + break; + + default: + set_FRMR(snd_ch, path, f_id); + } + + + } + return; + } + + // Must be SABM. See if it would duplicate an existing session (but could it - wouldn't that be found earlier ?? + + if (get_user_dupe(snd_ch, path)) // Not SABM or a duplicate call pair + return; + + AX25Sess = get_free_port(snd_ch); + + if (AX25Sess == NULL) + { + // if there are no free ports for connection - beat off + + Byte Rev[80]; + + reverse_addr(path, Rev, strlen(path)); + set_DM(snd_ch, Rev); + return; + } + + // initialise new session + + AX25Sess->snd_ch = snd_ch; + + AX25Sess->corrcall[ConvFromAX25(&path[7], AX25Sess->corrcall)] = 0; + AX25Sess->mycall[ConvFromAX25(path, AX25Sess->mycall)] = 0; + AX25Sess->digi[0] = 0; + + // rst_timer(snd_ch, free_port); + + strcpy(AX25Sess->kind, "Incoming"); + AX25Sess->socket = socket; + + Debugprintf("incoming call socket = %x", socket); + + // I think we need to reverse the path + + AX25Sess->pathLen = strlen(path); + strcpy(AX25Sess->ReversePath, path); + reverse_addr(path, AX25Sess->Path, strlen(path)); + } + + // we process a packet on the necessary port + + memcpy(path, AX25Sess->Path, AX25Sess->pathLen); + + switch (f_id) + { + case I_I: + + on_I(socket, AX25Sess, pid, path, data, nr, ns, pf, cr, fecflag); + break; + + case S_RR: + + on_RR(AX25Sess, path, nr, pf, cr); + break; + + case S_RNR: + + on_RNR(AX25Sess, path, nr, pf, cr); + break; + + case S_REJ: + + on_REJ(AX25Sess, path, nr, pf, cr); + break; + + case S_SREJ: + + on_SREJ(AX25Sess, path, nr, pf, cr); + break; + + case U_SABM: + + on_SABM(socket, AX25Sess); + break; + + case U_DISC: + + on_DISC(socket, AX25Sess); + break; + + case U_UA: + + on_UA(socket, AX25Sess); + break; + + case U_DM: + + on_DM(socket, AX25Sess); + break; + + case U_UI: + + on_UI(AX25Sess, pf, cr); + break; + + case U_FRMR: + + 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 new file mode 100644 index 0000000..64d21e1 --- /dev/null +++ b/ax25_mod.c @@ -0,0 +1,1743 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + + +#include "UZ7HOStuff.h" + +// I assume this modulates (and sends?} frames + +int RSEncode(UCHAR * bytToRS, UCHAR * RSBytes, int DataLen, int RSLen); + +//unit ax25_mod; + +//interface + +//uses sysutils,classes,math + +extern int SampleNo; + +extern BOOL KISSServ; + +extern TStringList KISS_acked[]; +extern TStringList KISS_iacked[]; + +extern UCHAR modem_mode[]; + +#define sbc 175 + +extern single ch_offset[4]; + +#define COS45 0.70710676908493f + +#define TX_SILENCE 0 +#define TX_DELAY 1 +#define TX_TAIL 2 +#define TX_NO_DATA 3 +#define TX_FRAME 4 +#define TX_WAIT_BPF 5 + + +#define TX_BIT0 0 +#define TX_BIT1 1 +#define FRAME_EMPTY 0 +#define FRAME_FULL 1 +#define FRAME_NO_FRAME 2 +#define FRAME_NEW_FRAME 3 +#define BYTE_EMPTY 0 +#define BYTE_FULL 1 + + +UCHAR gray_8PSK[8] = {7,0,6,5,2,1,3,4}; // ?? was 1::8 + +UCHAR gray_PI4QPSK[4] = {3,1,5,7}; + + +float audio_buf[5][32768]; // [1..4,0..32767] +float tx_src_BPF_buf[5][32768]; +float tx_BPF_buf[5][32768]; +float tx_prev_BPF_buf[5][32768]; +float tx_BPF_core[5][32768]; + +long tx_delay_cnt[5] = {0}; // : array[1..4] of longword=(0,0,0,0}; +long tx_tail_cnt[5] = {0}; + +int tx_hitoneraisedb[5] = {0}; // : array[1..4] of integer=(0,0,0,0}; +float tx_hitoneraise[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_freq[5] = { 1000, 1000, 1000, 1000, 1000}; // : array[1..4] of single=(1000,1000,1000,1000}; +float tx_shift[5] = { 200, 200, 200, 200, 200}; // : array[1..4] of single=(200,200,200,200}; +float tx_bit_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_osc[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_bit_osc[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +unsigned short txbpf[5] = { 400, 400, 400, 400, 400}; // : array[1..4] of word=(400,400,400,400}; +unsigned short tx_BPF_tap[5] = { 256, 256, 256, 256, 256}; // : array[1..4] of word=(256,256,256,256}; +unsigned short tx_baudrate[5] = { 300, 300, 300, 300, 300}; // : array[1..4] of word=(300,300,300,300}; +unsigned short tx_BPF_timer[5] = {0}; // : array[1..4] of word=(0,0,0,0}; +UCHAR tx_pol[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_last_pol[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_last_diddle[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_flag_cnt[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_frame_status[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_byte_status[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_status[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_bit_stuff_cnt[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_bit_cnt[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_last_bit[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_bit_stream[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; + +UCHAR tx_8PSK[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; +UCHAR tx_QPSK[5] = {0}; // : array[1..4] of byte=(0,0,0,0}; + +float tx_I_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_Q_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_QPSK_avg_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_avg_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_df_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_df_Q [5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_old_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_QPSK_old_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_avg_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_avg_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_df_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_df_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_old_I[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_8PSK_old_Q[5] = {0}; // : array[1..4] of single=(0,0,0,0}; + +float tx_osc1[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_osc2[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_osc3[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +float tx_osc4[5] = {0}; // : array[1..4] of single=(0,0,0,0}; +short tx_inv1[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_inv2[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_inv3[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_inv4[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_old_inv1[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_old_inv2[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_old_inv3[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +short tx_old_inv4[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +float tx_bit1_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_bit2_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_bit3_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +float tx_bit4_mod[5] = {1, 1, 1, 1, 1}; // : array[1..4] of single=(1,1,1,1}; +UINT tx_viterbi[5] = {0}; // : array[1..4] of word=(0,0,0,0}; +UCHAR tx_intv_tbl[5][4]; // : array[1..4,0..3] of byte; + +short tx_inv[5] = {1, 1, 1, 1, 1}; // : array[1..4] of shortint=(1,1,1,1}; +BOOL tx_change_phase[5] = {0}; // : array[1..4] of boolean=(FALSE,FALSE,FALSE,FALSE}; +BOOL tx_bs_bit[5] = {0}; // : array[1..4] of boolean=(FALSE,FALSE,FALSE,FALSE}; + +string * tx_data[5] = {0}; // : array[1..4] of string=('','','',''}; +int tx_data_len[5] = {0}; + +int tx_fx25_size[4] = { 0, 0, 0, 0 }; +int tx_fx25_size_cnt[4] = { 0, 0, 0, 0 }; +int tx_fx25_mode[4] = { 0, 0, 0, 0 }; + + +// uses sm_main,ax25,ax25_agw,ax25_demod,rsunit; + +UCHAR tx_nrzi(UCHAR snd_ch, UCHAR bit) +{ +// Debugprintf("Before NRZI %d", bit); + + if (bit == TX_BIT0) + { + // Zero so switch bit + + tx_last_bit[snd_ch] ^= 1; + } + return tx_last_bit[snd_ch]; +} + +BOOL tx_bit_stuffing(UCHAR snd_ch, UCHAR bit) +{ + // result = FALSE; + // if bit=TX_BIT1 then inc(tx_bit_stuff_cnt[snd_ch]}; + // if bit=TX_BIT0 then tx_bit_stuff_cnt[snd_ch] = 0; + // if tx_bit_stuff_cnt[snd_ch]=5 then begin tx_bit_stuff_cnt[snd_ch] = 0; result = TRUE; end; +//end; + + if (bit == TX_BIT1) + tx_bit_stuff_cnt[snd_ch]++; + + if (bit == TX_BIT0) + tx_bit_stuff_cnt[snd_ch] = 0; + + if (tx_bit_stuff_cnt[snd_ch] == 5) + { + tx_bit_stuff_cnt[snd_ch] = 0; + return TRUE; + } + + return FALSE; +} + + + + +void interleave(char *s, int len) +{ +// var + // data: string; + // i,k,len: word; + // nr_blocks: word; +//begin{ +// data = ''; + // len = length(s}; + // if len>0 then nr_blocks = ((len-1} div 16}+1 else nr_blocks = 1; + // for i = 1 to 16 do + // for k = 0 to nr_blocks-1 do + // if (i+k*16}<=len then data = data+s[i+k*16]; + // result = data; +//end; + + char data[1024]; + + UINT i,k; + UINT nr_blocks; + int n = 0; + + if (len > 0) + nr_blocks = ((len - 1) / 16) + 1; + else + nr_blocks = 1; + + for (i = 0; i < 16; i++) + { + for (k = 0; k < nr_blocks; k++) + { + if ((i + k * 16) <= len) + data[n++] = s[i + k * 16]; + } + } + + memcpy(s, data, len); +} + +//procedure get_new_frame(snd_ch: byte; var frame_stream: TStringList}; +//var +// header,line,temp: string; +// len,i,size: word; + // crc: word; +//begin + +void get_new_frame(UCHAR snd_ch, TStringList * frame_stream) +{ + UCHAR header[256]; + UCHAR line[1024]; + + int LineLen; + + string ** Items; + + string * myTemp; + + UCHAR temp[1024]; + + UINT len, i, size; + UINT crc; + + tx_bs_bit[snd_ch] = FALSE; + tx_bit_cnt[snd_ch] = 0; + tx_flag_cnt[snd_ch] = 0; + tx_bit_stuff_cnt[snd_ch] = 0; + tx_bit_stream[snd_ch] = FRAME_FLAG; + tx_frame_status[snd_ch] = FRAME_NEW_FRAME; + tx_byte_status[snd_ch] = BYTE_EMPTY; + + if (frame_stream->Count == 0) + { + tx_frame_status[snd_ch] = FRAME_NO_FRAME; + return; + } + + // We now pass control byte and ack bytes on front and pointer to socket on end if ackmode + + myTemp = Strings(frame_stream, 0); // get message + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[snd_ch], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + tx_data[snd_ch] = duplicateString(myTemp); // so can free original below + + Delete(frame_stream, 0); // This will invalidate temp + + AGW_AX25_frame_analiz(snd_ch, FALSE, tx_data[snd_ch]); + + put_frame(snd_ch, tx_data[snd_ch], "", TRUE, FALSE); + + if (tx_data[snd_ch]->Length == 0 || modem_mode[snd_ch] != MODE_MPSK) + return; + + // Reformat MPSK Data + + //Take data 8 bytes at a time and add 8 bytes of RS data + + LineLen = 0; + + while (tx_data[snd_ch]->Length > 0) + { + size = tx_data[snd_ch]->Length; + + if (size > 8) + size = 8; + + memcpy(temp, tx_data[snd_ch]->Data, size); + + // Delete the chars from tx_data + + mydelete(tx_data[snd_ch], 0, 8); + + memset(xData, 0, sizeof(xData)); + memset(xEncoded, 0, sizeof(xEncoded)); + + memcpy(xData, temp, size); + + InitBuffers(); + EncodeRS(xData, xEncoded); // This puts the 8 RS bytes in xEncoded + + memcpy(&line[LineLen], xData, size); + memcpy(&line[LineLen + size], xEncoded, MaxErrors * 2); + + LineLen += size + (MaxErrors * 2); + } + + + + + len = LineLen; + + interleave(line, LineLen); + scrambler(line, LineLen); + + header[0] = 0x7e; + header[1] = 0x7e; + header[2] = len >> 8; + header[3] = len; + + crc = get_fcs(header, 4); + + header[4] = crc >> 8; + header[5] = crc; + + memset(xData, 0, sizeof(xData)); + memset(xEncoded, 0, sizeof(xEncoded)); + memmove(xData, header, 6); + + + // RSEncode(xData, xEncoded, 6 + (MaxErrors * 2), MaxErrors * 2); + + InitBuffers(); + EncodeRS(xData, xEncoded); + + fx25_encode_rs(xData, xEncoded, 0, 8); + + + // We should now have RS Encoded Header in xEncoded; + + // I think we send encoded header then line + + tx_data[snd_ch]->Length = 0; + + stringAdd(tx_data[snd_ch], xData, 6); + stringAdd(tx_data[snd_ch], xEncoded, MaxErrors * 2); + stringAdd(tx_data[snd_ch], line, LineLen); + + // For testing, descramble and de-interleve + + scrambler(line, LineLen); // should look like interleaved + { + Byte unscrambled[1024]; + int count, len; + int origlen; + + len = LineLen; + count = (len + 15) / 16; + + int j1, j2, j3, i, j; + + j3 = 0; + + for (j1 = 0; j1 < 16; j1++) + { + // Each char in block + + for (j2 = 0; j2 < count; j2++) + { + // Blocks + + unscrambled[j2 * 16 + j1] = line[j3]; + j3++; + } + } + + // Now remove RS (will check later) + + i = 0; + j = 0; + + while (j < len) + { + Byte line1[256]; + int nErr, eras_pos = 0; + Byte rs_block[256]; + + memcpy(line1, &unscrambled[j], 16); + + memset(xEncoded, 0, sizeof(xEncoded)); + memset(xDecoded, 0, sizeof(xDecoded)); + + memcpy(xEncoded, &unscrambled[j], 16); + +// nErr = DecodeRS(xEncoded, xDecoded); + + memset(rs_block, 0, 255); + memcpy(rs_block, &unscrambled[j], 8); + memcpy(&rs_block[255 - 8], &unscrambled[j+8], 8); + + nErr = fx25_decode_rs(rs_block, &eras_pos, 0, 0, 8); + + +// line1 = ''; +// for j1 = MaxErrors * 2 to size - 1 do line1 = line1 + chr(xDecoded[j1]); + + + memcpy(&unscrambled[i], &unscrambled[j], 8); + i += 8; + j += 16; + } + + j3 = j3; + + } + +} + + + +int get_new_bit(Byte snd_ch, Byte bit) +{ + unsigned short len; + string * s; + + if (tx_frame_status[snd_ch] == FRAME_FULL) + { + if (tx_byte_status[snd_ch] == BYTE_EMPTY) + { + len = tx_data[snd_ch]->Length; + + if (len > 0) + { + s = tx_data[snd_ch]; + tx_bit_stream[snd_ch] = (s->Data[0]); + tx_frame_status[snd_ch] = FRAME_FULL; + tx_byte_status[snd_ch] = BYTE_FULL; + tx_bit_cnt[snd_ch] = 0; + mydelete(tx_data[snd_ch], 0, 1); + } + + else tx_frame_status[snd_ch] = FRAME_EMPTY; + } + + if (tx_byte_status[snd_ch] == BYTE_FULL) + bit = tx_bit_stream[snd_ch] & TX_BIT1; + + if (modem_mode[snd_ch] == MODE_MPSK) + { + tx_bit_cnt[snd_ch]++; + tx_bit_stream[snd_ch] = tx_bit_stream[snd_ch] >> 1; + if (tx_bit_cnt[snd_ch] >= 8) + tx_byte_status[snd_ch] = BYTE_EMPTY; + + } + else + { + if (tx_bs_bit[snd_ch]) + bit = TX_BIT0; + + tx_bs_bit[snd_ch] = tx_bit_stuffing(snd_ch, bit); + + if (!tx_bs_bit[snd_ch]) + { + tx_bit_cnt[snd_ch]++; + tx_bit_stream[snd_ch] >>= 1; + if (tx_bit_cnt[snd_ch] >= 8 && !tx_bs_bit[snd_ch]) + tx_byte_status[snd_ch] = BYTE_EMPTY; + } + } + } + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + + if ((tx_frame_status[snd_ch] == FRAME_NEW_FRAME) || (tx_frame_status[snd_ch] == FRAME_NO_FRAME)) + { + bit = tx_bit_stream[snd_ch] & TX_BIT1; + tx_flag_cnt[snd_ch]++; + tx_bit_stream[snd_ch] >>= 1; + + if (tx_flag_cnt[snd_ch] == 8) + { + switch (tx_frame_status[snd_ch]) + { + case FRAME_NEW_FRAME: + + tx_frame_status[snd_ch] = FRAME_FULL; + break; + + case FRAME_NO_FRAME: + + tx_tail_cnt[snd_ch] = 0; + tx_frame_status[snd_ch] = FRAME_EMPTY; + tx_status[snd_ch] = TX_TAIL; + + break; + } + } + } + return bit; +} + +////// FX.25 ////// + + +void bit_to_fx25(Byte * tx_byte, Byte * bit_cnt, Byte bit, string * data, int * data_cnt) +{ + *tx_byte = (*tx_byte >> 1) | (bit << 7); + (*bit_cnt)++; + + if (*bit_cnt == 8) + { + stringAdd(data, tx_byte, 1); + *bit_cnt = 0; + } + (*data_cnt)++; +} + +string * fill_fx25_data(int snd_ch, string * data) +{ +#define nr_tags 5 + + string * result; + + Byte rs_roots[nr_tags + 1] = { 16, 32, 64, 32, 16, 16 }; + word rs_payload[nr_tags + 1] = { 1912, 1784, 1528, 1024, 512, 256 }; // 239, 233, 191, 128, 64, 32 + + unsigned long long rs_tag[nr_tags + 1] = + { + 0xB74DB7DF8A532F3E, // 255 / 16 (239) + 0x6E260B1AC5835FAE, // 255 / 32 (223) + 0x3ADB0C13DEAE2836, // 255 / 64 (191) + 0xFF94DC634F1CFF4E, // 160 / 32 (128) + 0xC7DC0508F3D9B09E, // 80 / 16 (64) + 0x8F056EB4369660EE // 48 / 16 (32) + }; + +// 0x26FF60A600CC8FDE) 144; = 16; +// 0x1EB7B9CDBC09C00E) 96; 32; +// 0xDBF869BD2DBB1776) 64;= 32; +// 0xAB69DB6A543188D6) 192; = 64; +// 0x4A4ABEC4A724B796) 128; = 64; + + string * ax25_data = newString(); + + int i, ax25_size; + Byte a, bit, bit_cnt, bit_cnt1, bs, tx_byte; + Byte rs_id; + Byte rs_block[256], parity[256]; + + ax25_size = 0; + bs = 0; + tx_byte = 0; + bit_cnt = 0; + + // Load start flag + a = FRAME_FLAG; + + for (i = 0; i < 8; i++) + { + bit = a & 1; + a = a >> 1; + bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size); + } + + // Load body + for (i = 0; i < data->Length; i++) + { + bit_cnt1 = 0; + a = data->Data[i]; + do + { + if (bs == 5) + { + bit = TX_BIT0; + bs = 0; + } + else + { + bit = a & 1; + a = a >> 1; + bit_cnt1++; + + if (bit == TX_BIT1) + bs++; + else + bs = 0; + } + + bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size); + + } while (bit_cnt1 != 8 || bs == 5); + } + + // Load close flag + + a = FRAME_FLAG; + + for (i = 0; i < 8; i++) + { + bit = a & 1; + a = a >> 1; + bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size); + } + + a = FRAME_FLAG; + + // if too short or too long + + if (ax25_size < 168 || ax25_size > 1912) // < 21 or > 239 + { + // Send as normal ax25 packet + + if (bit_cnt > 0) + { + do + { + tx_byte = tx_byte >> 1; + bit_cnt++; + if (bit_cnt == 8) + stringAdd(ax25_data, &tx_byte, 1); + } while (bit_cnt < 8); + } + tx_fx25_size[snd_ch] = ax25_size; + return ax25_data; + } + + // Send as FX25 Message + + // find RS block size + + rs_id = 0; + + for (i = 0; i <= nr_tags; i++) + if (ax25_size <= rs_payload[i]) + rs_id = i; + + // Padding to block size + + while (ax25_size != rs_payload[rs_id]) + { + bit = a & 1; + a = (a >> 1) | (bit << 7); + bit_to_fx25(&tx_byte, &bit_cnt, bit, ax25_data, &ax25_size); + } + + memset(rs_block, 0, 255); + move(&ax25_data->Data[0], &rs_block[0], ax25_data->Length); + + fx25_encode_rs(rs_block, parity, 0, rs_roots[rs_id]); + + result = newString(); + + stringAdd(result, (Byte *)&rs_tag[rs_id], 8); + stringAdd(result, ax25_data->Data, ax25_data->Length); + stringAdd(result, parity, rs_roots[rs_id]); + + tx_fx25_size[snd_ch] = result->Length << 3; + + freeString(ax25_data); + return result; +} + +void fx25_get_new_frame(int snd_ch, TStringList * frame_stream) +{ + string * myTemp; + + tx_bs_bit[snd_ch] = 0; + tx_bit_cnt[snd_ch] = 0; + tx_flag_cnt[snd_ch] = 0; + tx_bit_stuff_cnt[snd_ch] = 0; + tx_fx25_size_cnt[snd_ch] = 0; + tx_fx25_size[snd_ch] = 1; + tx_frame_status[snd_ch] = FRAME_NEW_FRAME; + tx_byte_status[snd_ch] = BYTE_EMPTY; + if (frame_stream->Count == 0) + tx_frame_status[snd_ch] = FRAME_NO_FRAME; + else + { + // We now pass control byte and ack bytes on front and pointer to socket on end if ackmode + + myTemp = Strings(frame_stream, 0); // get message + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[snd_ch], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + AGW_AX25_frame_analiz(snd_ch, FALSE, myTemp); + put_frame(snd_ch, myTemp, "", TRUE, FALSE); + + tx_data[snd_ch] = fill_fx25_data(snd_ch, myTemp); + + Delete(frame_stream, 0); // This will invalidate temp + } +} + +int fx25_get_new_bit(int snd_ch, Byte bit) +{ + string *s; + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + { + fx25_get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + if (tx_frame_status[snd_ch] == FRAME_NEW_FRAME) + tx_frame_status[snd_ch] = FRAME_FULL; + } + + if (tx_frame_status[snd_ch] == FRAME_FULL) + { + if (tx_byte_status[snd_ch] == BYTE_EMPTY) + { + if (tx_data[snd_ch]->Length) + { + s = tx_data[snd_ch]; + + tx_bit_stream[snd_ch] = s->Data[0]; + tx_frame_status[snd_ch] = FRAME_FULL; + tx_byte_status[snd_ch] = BYTE_FULL; + tx_bit_cnt[snd_ch] = 0; + mydelete(tx_data[snd_ch], 0, 1); + } + else + tx_frame_status[snd_ch] = FRAME_EMPTY; + } + if (tx_byte_status[snd_ch] == BYTE_FULL) + { + bit = tx_bit_stream[snd_ch] & TX_BIT1; + tx_bit_stream[snd_ch] = tx_bit_stream[snd_ch] >> 1; + tx_bit_cnt[snd_ch]++; + tx_fx25_size_cnt[snd_ch]++; + if (tx_bit_cnt[snd_ch] >= 8) + tx_byte_status[snd_ch] = BYTE_EMPTY; + if (tx_fx25_size_cnt[snd_ch] == tx_fx25_size[snd_ch]) + tx_frame_status[snd_ch] = FRAME_EMPTY; + } + } + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + { + fx25_get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + + switch (tx_frame_status[snd_ch]) + { + case FRAME_NEW_FRAME: + tx_frame_status[snd_ch] = FRAME_FULL; + break; + + case FRAME_NO_FRAME: + tx_tail_cnt[snd_ch] = 0; + tx_frame_status[snd_ch] = FRAME_EMPTY; + tx_status[snd_ch] = TX_TAIL; + break; + } + } + return bit; +} + + +////////////////// + + +int get_new_bit_tail(UCHAR snd_ch, UCHAR bit) +{ + long _txtail = 0; + UCHAR _diddles; + + if (modem_mode[snd_ch] == MODE_FSK) + _diddles = diddles; + else + _diddles = 0; + + if (modem_mode[snd_ch] == MODE_FSK) + _txtail = txtail[snd_ch]; + + else if (modem_mode[snd_ch] == MODE_BPSK) + _txtail = txtail[snd_ch]; + + else if (modem_mode[snd_ch] == MODE_8PSK) + _txtail = txtail[snd_ch] * 3; + + else if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK) + _txtail = txtail[snd_ch] << 1; + + else if (modem_mode[snd_ch] == MODE_MPSK) + _txtail = txtail[snd_ch] << 2; + + _txtail = (_txtail * tx_baudrate[snd_ch]) / 1000; + + if (qpsk_set[snd_ch].mode == QPSK_V26 || modem_mode[snd_ch] == MODE_8PSK) + _diddles = 2; + + switch (_diddles) + { + case 0: + + if (tx_tail_cnt[snd_ch] < _txtail) + { + bit = TX_BIT0; + tx_tail_cnt[snd_ch]++; + } + else + { + tx_status[snd_ch] = TX_WAIT_BPF; + } + + break; + + case 1: + + if (tx_tail_cnt[snd_ch] < _txtail) + { + if (tx_last_diddle[snd_ch] == TX_BIT0) + bit = TX_BIT1; + else + bit = TX_BIT0; + + tx_tail_cnt[snd_ch]++; + tx_last_diddle[snd_ch] = bit; + } + else + { + Debugprintf("End TXTAIL %d", SampleNo); + tx_status[snd_ch] = TX_WAIT_BPF; + } + + break; + + case 2: + + if (tx_tail_cnt[snd_ch] < _txtail) + { + bit = FRAME_FLAG >> (tx_tail_cnt[snd_ch] % 8) & 1; + tx_tail_cnt[snd_ch]++; + } + else + { + Debugprintf("End TXTAIL %d", SampleNo); + tx_status[snd_ch] = TX_WAIT_BPF; + } + break; + } + return bit; +} + +int get_new_bit_delay(UCHAR snd_ch, UCHAR bit) +{ + ULONG _txdelay = 0; + UCHAR _diddles; + + _diddles = 0; + + switch (modem_mode[snd_ch]) + { + case MODE_FSK: + + _diddles = diddles; + break; + + case MODE_PI4QPSK: + case MODE_8PSK: + + _diddles = 2; + break; + + case MODE_QPSK: + + if (qpsk_set[snd_ch].mode == QPSK_V26) + _diddles = 2; + break; + } + + if (modem_mode[snd_ch] == MODE_FSK) + _txdelay = txdelay[snd_ch]; + + else if (modem_mode[snd_ch] == MODE_BPSK) + _txdelay = txdelay[snd_ch]; + + else if (modem_mode[snd_ch] == MODE_8PSK) + _txdelay = txdelay[snd_ch] * 3; + + else if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK) + _txdelay = txdelay[snd_ch] << 1; + + else if (modem_mode[snd_ch] == MODE_MPSK) + { + if (txdelay[snd_ch] < 400) + _txdelay = 400 << 2; //AFC delay + else + _txdelay = txdelay[snd_ch] << 2; + } + + _txdelay = (_txdelay * tx_baudrate[snd_ch]) / 1000; + + switch (_diddles) + { + case 0: + + if (tx_delay_cnt[snd_ch] < _txdelay) + { + bit = TX_BIT0; + tx_delay_cnt[snd_ch]++; + } + else + { + tx_status[snd_ch] = TX_FRAME; + } + + break; + + case 1: + + if (tx_delay_cnt[snd_ch] < _txdelay) + { + if (tx_last_diddle[snd_ch] == TX_BIT0) + bit = TX_BIT1; + else + bit = TX_BIT0; + + tx_delay_cnt[snd_ch]++; + tx_last_diddle[snd_ch] = bit; + } + else + { + tx_status[snd_ch] = TX_FRAME; + Debugprintf("End TXD %d", SampleNo); + } + break; + + case 2: + + // Send Flags + + if (tx_delay_cnt[snd_ch] < _txdelay) + { + bit = FRAME_FLAG >> ((8 - (_txdelay % 8) + tx_delay_cnt[snd_ch]) % 8) & 1; + tx_delay_cnt[snd_ch]++; + } + else + { + tx_status[snd_ch] = TX_FRAME; + Debugprintf("End TXD %d", SampleNo); + } + break; + } + return bit; +} + +// is this waiting for the filter to fill? +// No, flushing BPF + +void get_wait_bpf(UCHAR snd_ch) +{ + tx_BPF_timer[snd_ch]++; + + if (tx_BPF_timer[snd_ch] == tx_BPF_tap[snd_ch] ) + { + tx_status[snd_ch] = TX_NO_DATA; + tx_BPF_timer[snd_ch] = 0; + } +} + + +//procedure modulator(snd_ch: byte; var buf: array of single; buf_size: word}; +//{ +/* +function filter(x,k: single}: single; +begin + result = k*cos(x}; + if result>1 then result = 1; + if result<-1 then result = -1; +end; +} +*/ + +single filter(single x) +{ + if (x <= PI25) + return 1.0f; + + if (x >= PI75) + return -1.0f; + + return cosf(2.0f * x -PI5); +} + + +// make_samples return one sample of the waveform + +// But seems to be called only once per bit ?? + +// No, but needs to preserve bit between calls + +float make_samples(unsigned char snd_ch, unsigned char * bitptr) +{ + float pi2, x1, x; + Byte i,qbit,tribit,dibit; + float z1,z2,z3,z4; + unsigned short b, msb, lsb; + unsigned char bit = *bitptr; + + float amp = 0; + + pi2 = 2 * pi / TX_Samplerate; + x1 = pi * tx_baudrate[snd_ch] / TX_Samplerate; + + if (modem_mode[snd_ch] == MODE_FSK) + { + if (bit == TX_BIT0) + x = pi2*(tx_freq[snd_ch] + 0.5f * tx_shift[snd_ch]); + else + x = pi2*(tx_freq[snd_ch] - 0.5f * tx_shift[snd_ch]); + + amp = 1.0f; + + if (tx_baudrate[snd_ch] > 600) + { + if (tx_hitoneraisedb[snd_ch] < 0 && bit == TX_BIT0) + amp = tx_hitoneraise[snd_ch]; + + if (tx_hitoneraisedb[snd_ch] > 0 && bit == TX_BIT1) + amp = tx_hitoneraise[snd_ch]; + } + + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + + if (tx_osc[snd_ch] > 2*pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2*pi; + } + + else if (modem_mode[snd_ch] == MODE_BPSK) + { + if (tx_change_phase[snd_ch]) + tx_bit_mod[snd_ch] = tx_inv[snd_ch] * cos(tx_bit_osc[snd_ch]); + + x = pi2 * (tx_freq[snd_ch]); + + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + + if (tx_osc[snd_ch] > 2 * pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi; + } + + else if (modem_mode[snd_ch] == MODE_QPSK) + { + if (tx_QPSK_old_I[snd_ch] != tx_QPSK_I[snd_ch]) + + tx_I_mod[snd_ch] = tx_QPSK_avg_I[snd_ch] + tx_QPSK_df_I[snd_ch] * filter(tx_bit_osc[snd_ch]); + else + tx_I_mod[snd_ch] = tx_QPSK_I[snd_ch]; + + if (tx_QPSK_old_Q[snd_ch] != tx_QPSK_Q[snd_ch]) + tx_Q_mod[snd_ch] = tx_QPSK_avg_Q[snd_ch] + tx_QPSK_df_Q[snd_ch] * filter(tx_bit_osc[snd_ch]); + else + tx_Q_mod[snd_ch] = tx_QPSK_Q[snd_ch]; + + x = pi2 * (tx_freq[snd_ch]); + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + if (tx_osc[snd_ch] > 2 * pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi; + } + + else if (modem_mode[snd_ch] == MODE_8PSK || modem_mode[snd_ch] == MODE_PI4QPSK) + { + if (tx_8PSK_old_I[snd_ch] != tx_8PSK_I[snd_ch]) + tx_I_mod[snd_ch] = tx_8PSK_avg_I[snd_ch] + tx_8PSK_df_I[snd_ch] * filter(tx_bit_osc[snd_ch]); + else + tx_I_mod[snd_ch] = tx_8PSK_I[snd_ch]; + + if (tx_8PSK_old_Q[snd_ch] != tx_8PSK_Q[snd_ch]) + tx_Q_mod[snd_ch] = tx_8PSK_avg_Q[snd_ch] + tx_8PSK_df_Q[snd_ch] * filter(tx_bit_osc[snd_ch]); + else + tx_Q_mod[snd_ch] = tx_8PSK_Q[snd_ch]; + + x = pi2 * (tx_freq[snd_ch]); + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + + if (tx_osc[snd_ch] > 2 * pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi; + + } + + else if (modem_mode[snd_ch] == MODE_MPSK) + { + z1 = pi2 * (tx_freq[snd_ch] + ch_offset[0]); + z2 = pi2 * (tx_freq[snd_ch] + ch_offset[1]); + z3 = pi2 * (tx_freq[snd_ch] + ch_offset[2]); + z4 = pi2 * (tx_freq[snd_ch] + ch_offset[3]); + + tx_osc1[snd_ch] = tx_osc1[snd_ch] + z1; + tx_osc2[snd_ch] = tx_osc2[snd_ch] + z2; + tx_osc3[snd_ch] = tx_osc3[snd_ch] + z3; + tx_osc4[snd_ch] = tx_osc4[snd_ch] + z4; + + if (tx_osc1[snd_ch] > 2 * pi) + tx_osc1[snd_ch] = tx_osc1[snd_ch] - 2 * pi; + + if (tx_osc2[snd_ch] > 2 * pi) + tx_osc2[snd_ch] = tx_osc2[snd_ch] - 2 * pi; + + if (tx_osc3[snd_ch] > 2 * pi) + tx_osc3[snd_ch] = tx_osc3[snd_ch] - 2 * pi; + + if (tx_osc4[snd_ch] > 2 * pi) + tx_osc4[snd_ch] = tx_osc4[snd_ch] - 2 * pi; + + if (tx_old_inv1[snd_ch] != tx_inv1[snd_ch]) + tx_bit1_mod[snd_ch] = tx_inv1[snd_ch] * cos(tx_bit_osc[snd_ch]); + else + tx_bit1_mod[snd_ch] = -tx_inv1[snd_ch]; + + if (tx_old_inv2[snd_ch] != tx_inv2[snd_ch]) + tx_bit2_mod[snd_ch] = tx_inv2[snd_ch] * cos(tx_bit_osc[snd_ch]); + else + tx_bit2_mod[snd_ch] = -tx_inv2[snd_ch]; + + if (tx_old_inv3[snd_ch] != tx_inv3[snd_ch]) + tx_bit3_mod[snd_ch] = tx_inv3[snd_ch] * cos(tx_bit_osc[snd_ch]); + else + tx_bit3_mod[snd_ch] = -tx_inv3[snd_ch]; + + if (tx_old_inv4[snd_ch] != tx_inv4[snd_ch]) + tx_bit4_mod[snd_ch] = tx_inv4[snd_ch] * cos(tx_bit_osc[snd_ch]); + else + tx_bit4_mod[snd_ch] = -tx_inv4[snd_ch]; + } + + tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] + x1; + + if (tx_bit_osc[snd_ch] > pi) + { + // This seems to get the next bit, + // but why?? - end of samples for last bit + + tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] - pi; + + // FSK Mode + if (modem_mode[snd_ch] == MODE_FSK) + { + bit = 0; + + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + tx_status[snd_ch] = TX_DELAY; + } + + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + { + if (tx_status[snd_ch] == TX_DELAY) + tx_status[snd_ch] = TX_FRAME; // il2p generates TXDELAY as part of the frame, so go straight to TX_FRAME + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = il2p_get_new_bit(snd_ch, bit); + + + // No nrzi for il2p + + *bitptr = bit; + } + else + { + // ax25/fx25 + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + { + if (tx_fx25_mode[snd_ch]) + bit = fx25_get_new_bit(snd_ch, bit); + else + bit = get_new_bit(snd_ch, bit); + } + + *bitptr = tx_nrzi(snd_ch, bit); + + } + } + + // BPSK Mode + if (modem_mode[snd_ch] == MODE_BPSK) + { + bit = 0; + + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + Debugprintf("Start TXD"); + tx_status[snd_ch] = TX_DELAY; + } + + // il2p generates TXDELAY as part of the frame, so go straight too TX_FRAME + + if (tx_status[snd_ch] == TX_DELAY) + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + tx_status[snd_ch] = TX_FRAME; + else + bit = get_new_bit_delay(snd_ch, bit); + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + { + if (il2p_mode[snd_ch] >= IL2P_MODE_TXRX) + bit = il2p_get_new_bit(snd_ch, bit); + else if (tx_fx25_mode[snd_ch]) + bit = fx25_get_new_bit(snd_ch, bit); + else + bit = get_new_bit(snd_ch, bit); + } + // ?? *bitptr = tx_nrzi(snd_ch, bit); + + if (bit == 0) + { + tx_inv[snd_ch] = -tx_inv[snd_ch]; + tx_change_phase[snd_ch] = TRUE; + } + else + tx_change_phase[snd_ch] = FALSE; + } + + // QPSK Mode + + if (modem_mode[snd_ch] == MODE_QPSK) + { + dibit = 0; + for (i = 0; i < 2; i++) + { + bit = 0; + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + tx_status[snd_ch] = TX_DELAY; + } + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + dibit = (dibit << 1) | tx_nrzi(snd_ch, bit); + } + + + dibit = qpsk_set[snd_ch].tx[dibit & 3]; + tx_QPSK[snd_ch] = (tx_QPSK[snd_ch] + dibit) & 3; + tx_QPSK_old_I[snd_ch] = tx_QPSK_I[snd_ch]; + tx_QPSK_old_Q[snd_ch] = tx_QPSK_Q[snd_ch]; + + switch (tx_QPSK[snd_ch]) + { + case 0: + + tx_QPSK_I[snd_ch] = COS45; + tx_QPSK_Q[snd_ch] = COS45; + break; + + case 1: + + tx_QPSK_I[snd_ch] = -COS45; + tx_QPSK_Q[snd_ch] = COS45; + break; + + case 2: + + tx_QPSK_I[snd_ch] = -COS45; + tx_QPSK_Q[snd_ch] = -COS45; + break; + + case 3: + + tx_QPSK_I[snd_ch] = COS45; + tx_QPSK_Q[snd_ch] = -COS45; + break; + } + + tx_QPSK_avg_I[snd_ch] = 0.5f*(tx_QPSK_old_I[snd_ch] + tx_QPSK_I[snd_ch]); + tx_QPSK_df_I[snd_ch] = 0.5f*(tx_QPSK_old_I[snd_ch] - tx_QPSK_I[snd_ch]); + tx_QPSK_avg_Q[snd_ch] = 0.5f*(tx_QPSK_old_Q[snd_ch] + tx_QPSK_Q[snd_ch]); + tx_QPSK_df_Q[snd_ch] = 0.5f*(tx_QPSK_old_Q[snd_ch] - tx_QPSK_Q[snd_ch]); + } + + // PI/4 QPSK Mode + + if (modem_mode[snd_ch] == MODE_PI4QPSK) + { + dibit = 0; + + for (i = 0; i < 2; i++) + { + bit = 0; + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + Debugprintf("Start TXD"); + tx_status[snd_ch] = TX_DELAY; + } + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + + *bitptr = tx_nrzi(snd_ch, bit); + + dibit = (dibit << 1) | *bitptr; + + } + + // This returns 3,1,5 or 7 so we use the odd enties in the 8PSK table + + dibit = gray_PI4QPSK[dibit & 3]; + + tx_8PSK[snd_ch] = (tx_8PSK[snd_ch] + dibit) & 7; + tx_8PSK_old_I[snd_ch] = tx_8PSK_I[snd_ch]; + tx_8PSK_old_Q[snd_ch] = tx_8PSK_Q[snd_ch]; + + switch (tx_8PSK[snd_ch]) + { + case 0: + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = 1; + break; + + case 1: + tx_8PSK_I[snd_ch] = COS45; + tx_8PSK_Q[snd_ch] = COS45; + break; + + case 2: + tx_8PSK_I[snd_ch] = 1; + tx_8PSK_Q[snd_ch] = 0; + break; + + case 3: + tx_8PSK_I[snd_ch] = COS45; + tx_8PSK_Q[snd_ch] = -COS45; + break; + + case 4: + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = -1; + break; + + case 5: + tx_8PSK_I[snd_ch] = -COS45; + tx_8PSK_Q[snd_ch] = -COS45; + break; + + case 6: + tx_8PSK_I[snd_ch] = -1; + tx_8PSK_Q[snd_ch] = 0; + break; + + case 7: + tx_8PSK_I[snd_ch] = -COS45; + tx_8PSK_Q[snd_ch] = COS45; + break; + + } + + tx_8PSK_avg_I[snd_ch] = 0.5*(tx_8PSK_old_I[snd_ch] + tx_8PSK_I[snd_ch]); + tx_8PSK_df_I[snd_ch] = 0.5*(tx_8PSK_old_I[snd_ch] - tx_8PSK_I[snd_ch]); + tx_8PSK_avg_Q[snd_ch] = 0.5*(tx_8PSK_old_Q[snd_ch] + tx_8PSK_Q[snd_ch]); + tx_8PSK_df_Q[snd_ch] = 0.5*(tx_8PSK_old_Q[snd_ch] - tx_8PSK_Q[snd_ch]); + + } + + // 8PSK Mode + + if (modem_mode[snd_ch] == MODE_8PSK) + { + tribit = 0; + for (i = 0; i < 3; i++) + { + bit = 0; + + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + tx_status[snd_ch] = TX_DELAY; + } + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + + tribit = (tribit << 1) | tx_nrzi(snd_ch, bit); + } + + tribit = gray_8PSK[tribit & 7]; + + tx_8PSK[snd_ch] = (tx_8PSK[snd_ch] + tribit) & 7; + tx_8PSK_old_I[snd_ch] = tx_8PSK_I[snd_ch]; + tx_8PSK_old_Q[snd_ch] = tx_8PSK_Q[snd_ch]; + + switch (tx_8PSK[snd_ch]) + { + case 0: + + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = 1; + break; + + case 1: + + tx_8PSK_I[snd_ch] = COS45; + tx_8PSK_Q[snd_ch] = COS45; + break; + + case 2: + + tx_8PSK_I[snd_ch] = 1; + tx_8PSK_Q[snd_ch] = 0; + break; + + case 3: + + tx_8PSK_I[snd_ch] = COS45; + tx_8PSK_Q[snd_ch] = -COS45; + break; + + case 4: + + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = -1; + break; + + case 5: + + tx_8PSK_I[snd_ch] = -COS45; + tx_8PSK_Q[snd_ch] = -COS45; + break; + + case 6: + + tx_8PSK_I[snd_ch] = -1; + tx_8PSK_Q[snd_ch] = 0; + break; + + case 7: + + tx_8PSK_I[snd_ch] = -COS45; + tx_8PSK_Q[snd_ch] = COS45; + break; + + } + + tx_8PSK_avg_I[snd_ch] = 0.5f*(tx_8PSK_old_I[snd_ch] + tx_8PSK_I[snd_ch]); + tx_8PSK_df_I[snd_ch] = 0.5f*(tx_8PSK_old_I[snd_ch] - tx_8PSK_I[snd_ch]); + tx_8PSK_avg_Q[snd_ch] = 0.5f*(tx_8PSK_old_Q[snd_ch] + tx_8PSK_Q[snd_ch]); + tx_8PSK_df_Q[snd_ch] = 0.5f*(tx_8PSK_old_Q[snd_ch] - tx_8PSK_Q[snd_ch]); + } + + if (modem_mode[snd_ch] == MODE_MPSK) + { + qbit = 0; + + // get the bits for each of 4 carriers + + for (i = 1; i <= 4; i++) + { + bit = 0; + + if (tx_status[snd_ch] == TX_SILENCE) + { + tx_delay_cnt[snd_ch] = 0; + Debugprintf("Start TXD"); + tx_status[snd_ch] = TX_DELAY; + } + + if (tx_status[snd_ch] == TX_DELAY) + bit = get_new_bit_delay(snd_ch, bit); + + if (tx_status[snd_ch] == TX_TAIL) + bit = get_new_bit_tail(snd_ch, bit); + + if (tx_status[snd_ch] == TX_FRAME) + bit = get_new_bit(snd_ch, bit); + + qbit = (qbit << 1) | bit; + } + + tx_old_inv1[snd_ch] = tx_inv1[snd_ch]; + tx_old_inv2[snd_ch] = tx_inv2[snd_ch]; + tx_old_inv3[snd_ch] = tx_inv3[snd_ch]; + tx_old_inv4[snd_ch] = tx_inv4[snd_ch]; + + if ((qbit & 8) == 0) + tx_inv1[snd_ch] = -tx_inv1[snd_ch]; + if ((qbit & 4) == 0) + tx_inv2[snd_ch] = -tx_inv2[snd_ch]; + if ((qbit & 2) == 0) + tx_inv3[snd_ch] = -tx_inv3[snd_ch]; + if ((qbit & 1) == 0) + tx_inv4[snd_ch] = -tx_inv4[snd_ch]; + + } + } + + if (tx_status[snd_ch] == TX_WAIT_BPF) + get_wait_bpf(snd_ch); + + if (modem_mode[snd_ch] == MODE_FSK) + return amp * sinf(tx_osc[snd_ch]); + + if (modem_mode[snd_ch] == MODE_BPSK) + return sinf(tx_osc[snd_ch]) * tx_bit_mod[snd_ch]; + + if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_8PSK || modem_mode[snd_ch] == MODE_PI4QPSK) + return sin(tx_osc[snd_ch]) * tx_I_mod[snd_ch] + cos(tx_osc[snd_ch]) * tx_Q_mod[snd_ch]; + + if (modem_mode[snd_ch] == MODE_MPSK) + return 0.35*(sinf(tx_osc1[snd_ch])*tx_bit1_mod[snd_ch] + + sinf(tx_osc2[snd_ch])*tx_bit2_mod[snd_ch] + + sinf(tx_osc3[snd_ch])*tx_bit3_mod[snd_ch] + + sinf(tx_osc4[snd_ch])*tx_bit4_mod[snd_ch]); + + return 0.0f; +} + +float make_samples_calib(UCHAR snd_ch, UCHAR tones) +{ + float amp, pi2, x, x1; + + x1 = pi * tx_baudrate[snd_ch] / TX_Samplerate; + pi2 = 2 * pi / TX_Samplerate; + + switch (tones) + { + case 1: + + tx_last_bit[snd_ch] = 1; + break; + + case 2: + + tx_last_bit[snd_ch] = 0; + break; + + case 3: + + tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] + x1; + + if (tx_bit_osc[snd_ch] > pi) + { + tx_bit_osc[snd_ch] = tx_bit_osc[snd_ch] - pi; + tx_last_bit[snd_ch] = tx_last_bit[snd_ch] ^ 1; + } + break; + } + + amp = 1; + + if (tx_baudrate[snd_ch] > 600) + { + if (tx_hitoneraisedb[snd_ch] < 0 && tx_last_bit[snd_ch] == 0) + amp = tx_hitoneraise[snd_ch]; + + if (tx_hitoneraisedb[snd_ch] > 0 && tx_last_bit[snd_ch] == 1) + amp = tx_hitoneraise[snd_ch]; + } + + if (tx_last_bit[snd_ch] == 0) + x = pi2*(tx_freq[snd_ch] + 0.5f * tx_shift[snd_ch]); + else + x = pi2*(tx_freq[snd_ch] - 0.5f * tx_shift[snd_ch]); + + tx_osc[snd_ch] = tx_osc[snd_ch] + x; + + if (tx_osc[snd_ch] > 2*pi) + tx_osc[snd_ch] = tx_osc[snd_ch] - 2 * pi; + + return amp * sinf(tx_osc[snd_ch]); +} + +int amplitude = 22000; + +void modulator(UCHAR snd_ch, int buf_size) +{ + // We feed samples to samplesink instead of buffering them + + // I think this is the top of the TX hierarchy + + int i; + short Sample; + + if (calib_mode[snd_ch] > 0) + { + if (calib_mode[snd_ch] == 4) // CWID + { + if (tx_status[snd_ch] == TX_SILENCE) + { + SoundIsPlaying = TRUE; + Debugprintf("Start CWID Chan %d", snd_ch); + RadioPTT(snd_ch, 1); + + tx_status[snd_ch] = 6; + } + + if (ARDOPSendToCard(snd_ch, SendSize) == 1) + { + // End of TX + + tx_status[snd_ch] = TX_SILENCE; // Stop TX + Flush(); + RadioPTT(snd_ch, 0); + Debugprintf("End CWID"); + calib_mode[snd_ch] = 0; + } + return; + } + + + if (tx_status[snd_ch] == TX_SILENCE) + { + SoundIsPlaying = TRUE; + Debugprintf("Start Calib Chan %d", snd_ch); + RadioPTT(snd_ch, 1); + + tx_bit_osc[snd_ch] = 0; + tx_last_bit[snd_ch] = 0; + + // fill filter + + for (i = 0; i < tx_BPF_tap[snd_ch]; i++) + tx_prev_BPF_buf[snd_ch][buf_size + i] = make_samples_calib(snd_ch,calib_mode[snd_ch]); + } + tx_status[snd_ch] = TX_WAIT_BPF; + + for (i = 0; i < buf_size; i++) + tx_src_BPF_buf[snd_ch][i] = make_samples_calib(snd_ch, calib_mode[snd_ch]); + + FIR_filter(tx_src_BPF_buf[snd_ch],buf_size,tx_BPF_tap[snd_ch],tx_BPF_core[snd_ch],tx_BPF_buf[snd_ch],tx_prev_BPF_buf[snd_ch]); + + for (i = 0; i < buf_size; i++) + { + Sample = tx_BPF_buf[snd_ch][i] * amplitude; + SampleSink(modemtoSoundLR[snd_ch], Sample); + } + } + else + { + if (tx_status[snd_ch] == TX_SILENCE) + { + if (fx25_mode[snd_ch] == FX25_MODE_TXRX) + tx_fx25_mode[snd_ch] = 1; + else + tx_fx25_mode[snd_ch] = 0; + + tx_bit_osc[snd_ch] = 0; + tx_8PSK[snd_ch] = 0; + tx_QPSK[snd_ch] = 0; + tx_last_bit[snd_ch] = 0; + tx_inv1[snd_ch] = 1; + tx_inv2[snd_ch] = 1; + tx_inv3[snd_ch] = 1; + tx_inv4[snd_ch] = 1; + tx_8PSK_I[snd_ch] = 0; + tx_8PSK_Q[snd_ch] = 1; + tx_8PSK_old_I[snd_ch] = 0; + tx_8PSK_old_Q[snd_ch] = 1; + tx_QPSK_I[snd_ch] = COS45; + tx_QPSK_Q[snd_ch] = COS45; + tx_QPSK_old_I[snd_ch] = COS45; + tx_QPSK_old_Q[snd_ch] = COS45; + + for (i = 0; i < tx_BPF_tap[snd_ch]; i++) + tx_prev_BPF_buf[snd_ch][buf_size+i] = make_samples(snd_ch, &tx_pol[snd_ch]); + } + + for (i = 0; i < buf_size; i++) + tx_src_BPF_buf[snd_ch][i] = make_samples(snd_ch, &tx_pol[snd_ch]); + + FIR_filter(tx_src_BPF_buf[snd_ch], buf_size, tx_BPF_tap[snd_ch], tx_BPF_core[snd_ch], tx_BPF_buf[snd_ch], tx_prev_BPF_buf[snd_ch]); + + for (i = 0; i < buf_size; i++) + { + Sample = tx_BPF_buf[snd_ch][i] * 20000.0f; + SampleSink(modemtoSoundLR[snd_ch], Sample); + } + } +} + + diff --git a/berlekamp.c b/berlekamp.c new file mode 100644 index 0000000..d115dd7 --- /dev/null +++ b/berlekamp.c @@ -0,0 +1,329 @@ +/*********************************************************************** + * Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009 + * + * This software library is licensed under terms of the GNU GENERAL + * PUBLIC LICENSE + * + * + * RSCODE 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. + * + * RSCODE 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 Rscode. If not, see . + * + * Commercial licensing is available under a separate license, please + * contact author for details. + * + * Source code is available at http://rscode.sourceforge.net + * Berlekamp-Peterson and Berlekamp-Massey Algorithms for error-location + * + * From Cain, Clark, "Error-Correction Coding For Digital Communications", pp. 205. + * + * This finds the coefficients of the error locator polynomial. + * + * The roots are then found by looking for the values of a^n + * where evaluating the polynomial yields zero. + * + * Error correction is done using the error-evaluator equation on pp 207. + * + */ + +#include +#include "ecc.h" + +/* The Error Locator Polynomial, also known as Lambda or Sigma. Lambda[0] == 1 */ +static int Lambda[MAXDEG]; + +/* The Error Evaluator Polynomial */ +static int Omega[MAXDEG]; + +/* local ANSI declarations */ +static int compute_discrepancy(int lambda[], int S[], int L, int n); +static void init_gamma(int gamma[]); +static void compute_modified_omega (void); +static void mul_z_poly (int src[]); + +/* error locations found using Chien's search*/ +static int ErrorLocs[256]; +int NErrors; + +extern int xMaxErrors; + +/* erasure flags */ +static int ErasureLocs[256]; +static int NErasures; + +/* From Cain, Clark, "Error-Correction Coding For Digital Communications", pp. 216. */ +void +Modified_Berlekamp_Massey (void) +{ + int n, L, L2, k, d, i; + int psi[MAXDEG], psi2[MAXDEG], D[MAXDEG]; + int gamma[MAXDEG]; + + /* initialize Gamma, the erasure locator polynomial */ + init_gamma(gamma); + + /* initialize to z */ + copy_poly(D, gamma); + mul_z_poly(D); + + copy_poly(psi, gamma); + k = -1; L = NErasures; + + for (n = NErasures; n < NPAR; n++) { + + d = compute_discrepancy(psi, synBytes, L, n); + + if (d != 0) { + + /* psi2 = psi - d*D */ + for (i = 0; i < NPAR*2; i++) psi2[i] = psi[i] ^ gmult(d, D[i]); + + + if (L < (n-k)) { + L2 = n-k; + k = n-L; + /* D = scale_poly(ginv(d), psi); */ + for (i = 0; i < NPAR*2; i++) D[i] = gmult(psi[i], ginv(d)); + L = L2; + } + + /* psi = psi2 */ + for (i = 0; i < NPAR*2; i++) psi[i] = psi2[i]; + } + + mul_z_poly(D); + } + + for(i = 0; i < NPAR*2; i++) Lambda[i] = psi[i]; + compute_modified_omega(); + + +} + +/* given Psi (called Lambda in Modified_Berlekamp_Massey) and synBytes, + compute the combined erasure/error evaluator polynomial as + Psi*S mod z^4 + */ +void +compute_modified_omega () +{ + int i; + int product[MAXDEG*2]; + + mult_polys(product, Lambda, synBytes); + zero_poly(Omega); + for(i = 0; i < NPAR; i++) Omega[i] = product[i]; + +} + +/* polynomial multiplication */ +void +mult_polys (int dst[], int p1[], int p2[]) +{ + int i, j; + int tmp1[MAXDEG*2]; + + for (i=0; i < (NPAR*2*2); i++) dst[i] = 0; + + for (i = 0; i < NPAR*2; i++) { + for(j=NPAR*2; j<(NPAR*2*2); j++) tmp1[j]=0; + + /* scale tmp1 by p1[i] */ + for(j=0; j= i; j--) tmp1[j] = tmp1[j-i]; + for (j = 0; j < i; j++) tmp1[j] = 0; + + /* add into partial product */ + for(j=0; j < (NPAR*2*2); j++) dst[j] ^= tmp1[j]; + } +} + + + +/* gamma = product (1-z*a^Ij) for erasure locs Ij */ +void +init_gamma (int gamma[]) +{ + int e, tmp[MAXDEG]; + + zero_poly(gamma); + zero_poly(tmp); + gamma[0] = 1; + + for (e = 0; e < NErasures; e++) { + copy_poly(tmp, gamma); + scale_poly(gexp[ErasureLocs[e]], tmp); + mul_z_poly(tmp); + add_polys(gamma, tmp); + } +} + + + +void +compute_next_omega (int d, int A[], int dst[], int src[]) +{ + int i; + for ( i = 0; i < NPAR*2; i++) { + dst[i] = src[i] ^ gmult(d, A[i]); + } +} + + + +int +compute_discrepancy (int lambda[], int S[], int L, int n) +{ + int i, sum=0; + + for (i = 0; i <= L; i++) + sum ^= gmult(lambda[i], S[n-i]); + return (sum); +} + +/********** polynomial arithmetic *******************/ + +void add_polys (int dst[], int src[]) +{ + int i; + for (i = 0; i < NPAR*2; i++) dst[i] ^= src[i]; +} + +void copy_poly (int dst[], int src[]) +{ + int i; + for (i = 0; i < NPAR*2; i++) dst[i] = src[i]; +} + +void scale_poly (int k, int poly[]) +{ + int i; + for (i = 0; i < NPAR*2; i++) poly[i] = gmult(k, poly[i]); +} + + +void zero_poly (int poly[]) +{ + int i; + for (i = 0; i < NPAR*2; i++) poly[i] = 0; +} + + +/* multiply by z, i.e., shift right by 1 */ +static void mul_z_poly (int src[]) +{ + int i; + for (i = NPAR*2-1; i > 0; i--) src[i] = src[i-1]; + src[0] = 0; +} + + +/* Finds all the roots of an error-locator polynomial with coefficients + * Lambda[j] by evaluating Lambda at successive values of alpha. + * + * This can be tested with the decoder's equations case. + */ + + +void +Find_Roots (void) +{ + int sum, r, k; + NErrors = 0; + + for (r = 1; r < 256; r++) { + sum = 0; + /* evaluate lambda at r */ + for (k = 0; k < NPAR+1; k++) { + sum ^= gmult(gexp[(k*r)%255], Lambda[k]); + } + if (sum == 0) + { + ErrorLocs[NErrors] = (255-r); NErrors++; + if (DEBUG) fprintf(stderr, "Root found at r = %d, (255-r) = %d\n", r, (255-r)); + } + } +} + +/* Combined Erasure And Error Magnitude Computation + * + * Pass in the codeword, its size in bytes, as well as + * an array of any known erasure locations, along the number + * of these erasures. + * + * Evaluate Omega(actually Psi)/Lambda' at the roots + * alpha^(-i) for error locs i. + * + * returns 1 if everything ok, or 0 if an out-of-bounds error is found + * + */ + +int +correct_errors_erasures (unsigned char codeword[], + int csize, + int nerasures, + int erasures[]) +{ + int r, i, j, err; + + /* If you want to take advantage of erasure correction, be sure to + set NErasures and ErasureLocs[] with the locations of erasures. + */ + NErasures = nerasures; + for (i = 0; i < NErasures; i++) ErasureLocs[i] = erasures[i]; + + Modified_Berlekamp_Massey(); + Find_Roots(); + + + if (DEBUG) fprintf(stderr, "RS found %d errors\n", NErrors); + + + if ((NErrors <= xMaxErrors) && NErrors > 0) { + + /* first check for illegal error locs */ + for (r = 0; r < NErrors; r++) { + if (ErrorLocs[r] >= csize) { + if (DEBUG) fprintf(stderr, "Error loc i=%d outside of codeword length %d\n", i, csize); + return(0); + } + } + + for (r = 0; r < NErrors; r++) { + int num, denom; + i = ErrorLocs[r]; + /* evaluate Omega at alpha^(-i) */ + + num = 0; + for (j = 0; j < NPAR*2; j++) + num ^= gmult(Omega[j], gexp[((255-i)*j)%255]); + + /* evaluate Lambda' (derivative) at alpha^(-i) ; all odd powers disappear */ + denom = 0; + for (j = 1; j < NPAR*2; j += 2) { + denom ^= gmult(Lambda[j], gexp[((255-i)*(j-1)) % 255]); + } + + err = gmult(num, ginv(denom)); + if (DEBUG) fprintf(stderr, "Error magnitude %#x at loc %d\n", err, csize-i); + + codeword[csize-i-1] ^= err; + } + return(1); + } + else { + if (DEBUG && NErrors) fprintf(stderr, "Uncorrectable codeword\n"); + return(0); + } +} + diff --git a/calibrateDialog.ui b/calibrateDialog.ui new file mode 100644 index 0000000..66ef990 --- /dev/null +++ b/calibrateDialog.ui @@ -0,0 +1,285 @@ + + + calDialog + + + + 0 + 0 + 270 + 411 + + + + Dialog + + + + + 10 + 10 + 120 + 195 + + + + Channel A + + + + + 26 + 26 + 75 + 23 + + + + Low Tone + + + + + + 26 + 66 + 75 + 23 + + + + High Tone + + + + + + 26 + 106 + 75 + 23 + + + + Both Tones + + + + + + 26 + 146 + 75 + 23 + + + + Stop TX + + + + + + + 140 + 10 + 120 + 195 + + + + Channel B + + + false + + + + + 26 + 26 + 75 + 23 + + + + Low Tone + + + + + + 26 + 66 + 75 + 23 + + + + High Tone + + + + + + 26 + 106 + 75 + 23 + + + + Both Tones + + + + + + 28 + 146 + 75 + 23 + + + + Stop TX + + + + + + + 10 + 210 + 120 + 195 + + + + Channel C + + + + + 26 + 70 + 75 + 23 + + + + High Tone + + + + + + 26 + 110 + 75 + 23 + + + + Both Tones + + + + + + 26 + 30 + 75 + 23 + + + + Low Tone + + + + + + 26 + 150 + 75 + 23 + + + + Stop TX + + + + + + + 140 + 210 + 120 + 195 + + + + Channel D + + + + + 26 + 70 + 75 + 23 + + + + High Tone + + + + + + 25 + 110 + 75 + 23 + + + + Both Tones + + + + + + 26 + 30 + 75 + 23 + + + + Low Tone + + + + + + 26 + 150 + 75 + 23 + + + + Stop TX + + + + + + + + buttonClick() + + diff --git a/config b/config new file mode 100644 index 0000000..1c75084 --- /dev/null +++ b/config @@ -0,0 +1,9 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = true + symlinks = false + ignorecase = true +[remote "origin"] + url = ssh://git@vps1.g8bpq.net:7322/home/git/QtSM + fetch = +refs/heads/*:refs/remotes/origin/* diff --git a/description b/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/devicesDialog.ui b/devicesDialog.ui new file mode 100644 index 0000000..0f46639 --- /dev/null +++ b/devicesDialog.ui @@ -0,0 +1,976 @@ + + + devicesDialog + + + + 0 + 0 + 535 + 698 + + + + Dialog + + + true + + + + + 5 + 10 + 512 + 671 + + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAsNeeded + + + false + + + + + 0 + 0 + 508 + 668 + + + + + + 5 + 10 + 541 + 331 + + + + Sound Card + + + + + 108 + 25 + 261 + 22 + + + + true + + + + + + 108 + 55 + 261 + 22 + + + + true + + + + + + 13 + 25 + 91 + 19 + + + + OutputDevice + + + + + + 13 + 55 + 71 + 19 + + + + Input Device + + + + + + 20 + 204 + 101 + 17 + + + + TX Rotation + + + + + + 20 + 254 + 143 + 17 + + + + Colour Waterfall + + + + + + 20 + 279 + 251 + 17 + + + + Stop Waterfall on Minimize + + + + + + 20 + 304 + 251 + 17 + + + + Minimize window on startup + + + + + + 108 + 124 + 63 + 20 + + + + 12000 + + + + + + 304 + 124 + 63 + 20 + + + + 12000 + + + + + + 13 + 125 + 91 + 18 + + + + TX SampleRate + + + + + + 190 + 125 + 91 + 18 + + + + RX SampleRate + + + + + + 70 + 164 + 86 + 20 + + + + + None + + + + + Left/Mono + + + + + Right + + + + + + + + + + + + 12 + 166 + 61 + 16 + + + + Modem 1 + + + + + + 180 + 164 + 86 + 20 + + + + + None + + + + + Left/Mono + + + + + Right + + + + + + + 165 + 166 + 21 + 16 + + + + 2 + + + + + + 20 + 229 + 221 + 17 + + + + Single Channel Output + + + + + + 140 + 201 + 91 + 21 + + + + <html><head/><body><p>Use separate PTT signals for the two Modems. </p></body></html> + + + Dual PTT + + + + + + 30 + 90 + 71 + 20 + + + + ALSA + + + + + + 100 + 90 + 61 + 20 + + + + OSS + + + + + + 168 + 90 + 91 + 20 + + + + Pulse Audio + + + + + + 260 + 205 + 191 + 17 + + + + <html><head/><body><p>Check to run each modem in a separate thread. This may improve performace on multicore processors like the Pi3 and 4 but will reduce performance on a single core procssor</p></body></html> + + + use multiple threads + + + + + + 334 + 90 + 51 + 20 + + + + UDP + + + + + + 434 + 90 + 41 + 20 + + + + 8888 + + + + + + 290 + 164 + 86 + 20 + + + + + None + + + + + Left/Mono + + + + + Right + + + + + + + 272 + 166 + 21 + 16 + + + + 3 + + + + + + 385 + 166 + 21 + 16 + + + + 4 + + + + + + 400 + 164 + 86 + 20 + + + + + None + + + + + Left/Mono + + + + + Right + + + + + + + 384 + 92 + 51 + 16 + + + + Port + + + + + + 262 + 252 + 63 + 22 + + + + + 0 + + + + + 100 + + + + + 200 + + + + + 300 + + + + + + + 376 + 252 + 63 + 22 + + + + + 2500 + + + + + 2800 + + + + + 3000 + + + + + 3300 + + + + + 5500 + + + + + + + 158 + 254 + 107 + 17 + + + + Waterfall Range + + + + + + 348 + 254 + 31 + 17 + + + + to + + + + + + + 10 + 360 + 481 + 101 + + + + Server Setup + + + + + 22 + 24 + 121 + 18 + + + + AGWPE Server Port + + + + + + 160 + 24 + 47 + 20 + + + + + + + 160 + 48 + 47 + 20 + + + + + + + 22 + 48 + 111 + 18 + + + + KISS Server Port + + + + + + 230 + 22 + 70 + 25 + + + + Enabled + + + + + + 230 + 46 + 70 + 23 + + + + Enabled + + + + + + 380 + 74 + 70 + 23 + + + + Enabled + + + + + + 160 + 73 + 111 + 20 + + + + + + + 280 + 74 + 31 + 16 + + + + Port + + + + + + 320 + 74 + 47 + 20 + + + + + + + 20 + 74 + 111 + 18 + + + + UDP Sound Server Host + + + + + + + 10 + 470 + 481 + 151 + + + + PTT Port + + + + + 122 + 20 + 81 + 22 + + + + + + + 20 + 22 + 101 + 16 + + + + Select PTT Port + + + + + + 20 + 82 + 83 + 16 + + + + PTT On String + + + + + + 122 + 80 + 247 + 20 + + + + + + + 122 + 112 + 247 + 20 + + + + + + + 20 + 112 + 83 + 16 + + + + PTT Off String + + + + + + 20 + 53 + 83 + 16 + + + + GPIO Pin Left + + + + + + 122 + 50 + 25 + 20 + + + + + + + 262 + 50 + 25 + 20 + + + + + + + 165 + 50 + 83 + 18 + + + + GPIO Pin Right + + + + + + 18 + 52 + 99 + 18 + + + + CAT Port Speed + + + + + + 122 + 50 + 47 + 20 + + + + + + + 220 + 22 + 86 + 18 + + + + RTS/DTR + + + + + + 315 + 22 + 53 + 18 + + + + CAT + + + + + + 122 + 50 + 96 + 20 + + + + + + + 20 + 52 + 95 + 18 + + + + CM108 VID/PID + + + + + + + 140 + 626 + 215 + 33 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + + + diff --git a/ecc.h b/ecc.h new file mode 100644 index 0000000..6a7a393 --- /dev/null +++ b/ecc.h @@ -0,0 +1,102 @@ +/* Reed Solomon Coding for glyphs + * Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009 + * + * This software library is licensed under terms of the GNU GENERAL + * PUBLIC LICENSE + * + * RSCODE 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. + * + * RSCODE 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 Rscode. If not, see . + * + * Source code is available at http://rscode.sourceforge.net + * + * Commercial licensing is available under a separate license, please + * contact author for details. + * + */ + +/**************************************************************** + + Below is NPAR, the only compile-time parameter you should have to + modify. + + It is the number of parity bytes which will be appended to + your data to create a codeword. + + Note that the maximum codeword size is 255, so the + sum of your message length plus parity should be less than + or equal to this maximum limit. + + In practice, you will get slooow error correction and decoding + if you use more than a reasonably small number of parity bytes. + (say, 10 or 20) + + ****************************************************************/ + +#define MAXNPAR 64 // Sets size of static tables + +extern int NPAR; // Currently used number + +/****************************************************************/ + + + + +#define TRUE 1 +#define FALSE 0 + +typedef unsigned long BIT32; +typedef unsigned short BIT16; + +/* **************************************************************** */ + +/* Maximum degree of various polynomials. */ +#define MAXDEG (MAXNPAR*2) + +/*************************************/ +/* Encoder parity bytes */ +extern int pBytes[MAXDEG]; + +/* Decoder syndrome bytes */ +extern int synBytes[MAXDEG]; + +/* print debugging info */ +extern int DEBUG; + +/* Reed Solomon encode/decode routines */ +void initialize_ecc (void); +int check_syndrome (void); +void decode_data (unsigned char data[], int nbytes); +void encode_data (unsigned char msg[], int nbytes, unsigned char dst[]); + +/* CRC-CCITT checksum generator */ +BIT16 crc_ccitt(unsigned char *msg, int len); + +/* galois arithmetic tables */ +extern int gexp[]; +extern int glog[]; + +void init_galois_tables (void); +int ginv(int elt); +int gmult(int a, int b); + + +/* Error location routines */ +int correct_errors_erasures (unsigned char codeword[], int csize,int nerasures, int erasures[]); + +/* polynomial arithmetic */ +void add_polys(int dst[], int src[]) ; +void scale_poly(int k, int poly[]); +void mult_polys(int dst[], int p1[], int p2[]); + +void copy_poly(int dst[], int src[]); +void zero_poly(int poly[]); diff --git a/fftw3.f b/fftw3.f new file mode 100644 index 0000000..862a109 --- /dev/null +++ b/fftw3.f @@ -0,0 +1,72 @@ + INTEGER FFTW_R2HC + PARAMETER (FFTW_R2HC=0) + INTEGER FFTW_HC2R + PARAMETER (FFTW_HC2R=1) + INTEGER FFTW_DHT + PARAMETER (FFTW_DHT=2) + INTEGER FFTW_REDFT00 + PARAMETER (FFTW_REDFT00=3) + INTEGER FFTW_REDFT01 + PARAMETER (FFTW_REDFT01=4) + INTEGER FFTW_REDFT10 + PARAMETER (FFTW_REDFT10=5) + INTEGER FFTW_REDFT11 + PARAMETER (FFTW_REDFT11=6) + INTEGER FFTW_RODFT00 + PARAMETER (FFTW_RODFT00=7) + INTEGER FFTW_RODFT01 + PARAMETER (FFTW_RODFT01=8) + INTEGER FFTW_RODFT10 + PARAMETER (FFTW_RODFT10=9) + INTEGER FFTW_RODFT11 + PARAMETER (FFTW_RODFT11=10) + INTEGER FFTW_FORWARD + PARAMETER (FFTW_FORWARD=-1) + INTEGER FFTW_BACKWARD + PARAMETER (FFTW_BACKWARD=+1) + INTEGER FFTW_MEASURE + PARAMETER (FFTW_MEASURE=0) + INTEGER FFTW_DESTROY_INPUT + PARAMETER (FFTW_DESTROY_INPUT=1) + INTEGER FFTW_UNALIGNED + PARAMETER (FFTW_UNALIGNED=2) + INTEGER FFTW_CONSERVE_MEMORY + PARAMETER (FFTW_CONSERVE_MEMORY=4) + INTEGER FFTW_EXHAUSTIVE + PARAMETER (FFTW_EXHAUSTIVE=8) + INTEGER FFTW_PRESERVE_INPUT + PARAMETER (FFTW_PRESERVE_INPUT=16) + INTEGER FFTW_PATIENT + PARAMETER (FFTW_PATIENT=32) + INTEGER FFTW_ESTIMATE + PARAMETER (FFTW_ESTIMATE=64) + INTEGER FFTW_WISDOM_ONLY + PARAMETER (FFTW_WISDOM_ONLY=2097152) + INTEGER FFTW_ESTIMATE_PATIENT + PARAMETER (FFTW_ESTIMATE_PATIENT=128) + INTEGER FFTW_BELIEVE_PCOST + PARAMETER (FFTW_BELIEVE_PCOST=256) + INTEGER FFTW_NO_DFT_R2HC + PARAMETER (FFTW_NO_DFT_R2HC=512) + INTEGER FFTW_NO_NONTHREADED + PARAMETER (FFTW_NO_NONTHREADED=1024) + INTEGER FFTW_NO_BUFFERING + PARAMETER (FFTW_NO_BUFFERING=2048) + INTEGER FFTW_NO_INDIRECT_OP + PARAMETER (FFTW_NO_INDIRECT_OP=4096) + INTEGER FFTW_ALLOW_LARGE_GENERIC + PARAMETER (FFTW_ALLOW_LARGE_GENERIC=8192) + INTEGER FFTW_NO_RANK_SPLITS + PARAMETER (FFTW_NO_RANK_SPLITS=16384) + INTEGER FFTW_NO_VRANK_SPLITS + PARAMETER (FFTW_NO_VRANK_SPLITS=32768) + INTEGER FFTW_NO_VRECURSE + PARAMETER (FFTW_NO_VRECURSE=65536) + INTEGER FFTW_NO_SIMD + PARAMETER (FFTW_NO_SIMD=131072) + INTEGER FFTW_NO_SLOW + PARAMETER (FFTW_NO_SLOW=262144) + INTEGER FFTW_NO_FIXED_RADIX_LARGE_N + PARAMETER (FFTW_NO_FIXED_RADIX_LARGE_N=524288) + INTEGER FFTW_ALLOW_PRUNING + PARAMETER (FFTW_ALLOW_PRUNING=1048576) diff --git a/fftw3.h b/fftw3.h new file mode 100644 index 0000000..8f18408 --- /dev/null +++ b/fftw3.h @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2003, 2007-14 Matteo Frigo + * Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology + * + * The following statement of license applies *only* to this header file, + * and *not* to the other files distributed with FFTW or derived therefrom: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/***************************** NOTE TO USERS ********************************* + * + * THIS IS A HEADER FILE, NOT A MANUAL + * + * If you want to know how to use FFTW, please read the manual, + * online at http://www.fftw.org/doc/ and also included with FFTW. + * For a quick start, see the manual's tutorial section. + * + * (Reading header files to learn how to use a library is a habit + * stemming from code lacking a proper manual. Arguably, it's a + * *bad* habit in most cases, because header files can contain + * interfaces that are not part of the public, stable API.) + * + ****************************************************************************/ + +#ifndef FFTW3_H +#define FFTW3_H + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* If is included, use the C99 complex type. Otherwise + define a type bit-compatible with C99 complex */ +#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I) +# define FFTW_DEFINE_COMPLEX(R, C) typedef R _Complex C +#else +# define FFTW_DEFINE_COMPLEX(R, C) typedef R C[2] +#endif + +#define FFTW_CONCAT(prefix, name) prefix ## name +#define FFTW_MANGLE_DOUBLE(name) FFTW_CONCAT(fftw_, name) +#define FFTW_MANGLE_FLOAT(name) FFTW_CONCAT(fftwf_, name) +#define FFTW_MANGLE_LONG_DOUBLE(name) FFTW_CONCAT(fftwl_, name) +#define FFTW_MANGLE_QUAD(name) FFTW_CONCAT(fftwq_, name) + +/* IMPORTANT: for Windows compilers, you should add a line +*/ +//#define FFTW_DLL +/* + here and in kernel/ifftw.h if you are compiling/using FFTW as a + DLL, in order to do the proper importing/exporting, or + alternatively compile with -DFFTW_DLL or the equivalent + command-line flag. This is not necessary under MinGW/Cygwin, where + libtool does the imports/exports automatically. */ +#if defined(FFTW_DLL) && (defined(_WIN32) || defined(__WIN32__)) + /* annoying Windows syntax for shared-library declarations */ +# if defined(COMPILING_FFTW) /* defined in api.h when compiling FFTW */ +# define FFTW_EXTERN extern __declspec(dllexport) +# else /* user is calling FFTW; import symbol */ +# define FFTW_EXTERN extern __declspec(dllimport) +# endif +#else +# define FFTW_EXTERN extern +#endif + +enum fftw_r2r_kind_do_not_use_me { + FFTW_R2HC=0, FFTW_HC2R=1, FFTW_DHT=2, + FFTW_REDFT00=3, FFTW_REDFT01=4, FFTW_REDFT10=5, FFTW_REDFT11=6, + FFTW_RODFT00=7, FFTW_RODFT01=8, FFTW_RODFT10=9, FFTW_RODFT11=10 +}; + +struct fftw_iodim_do_not_use_me { + int n; /* dimension size */ + int is; /* input stride */ + int os; /* output stride */ +}; + +#include /* for ptrdiff_t */ +struct fftw_iodim64_do_not_use_me { + ptrdiff_t n; /* dimension size */ + ptrdiff_t is; /* input stride */ + ptrdiff_t os; /* output stride */ +}; + +typedef void (*fftw_write_char_func_do_not_use_me)(char c, void *); +typedef int (*fftw_read_char_func_do_not_use_me)(void *); + +/* + huge second-order macro that defines prototypes for all API + functions. We expand this macro for each supported precision + + X: name-mangling macro + R: real data type + C: complex data type +*/ + +#define FFTW_DEFINE_API(X, R, C) \ + \ +FFTW_DEFINE_COMPLEX(R, C); \ + \ +typedef struct X(plan_s) *X(plan); \ + \ +typedef struct fftw_iodim_do_not_use_me X(iodim); \ +typedef struct fftw_iodim64_do_not_use_me X(iodim64); \ + \ +typedef enum fftw_r2r_kind_do_not_use_me X(r2r_kind); \ + \ +typedef fftw_write_char_func_do_not_use_me X(write_char_func); \ +typedef fftw_read_char_func_do_not_use_me X(read_char_func); \ + \ +FFTW_EXTERN void X(execute)(const X(plan) p); \ + \ +FFTW_EXTERN X(plan) X(plan_dft)(int rank, const int *n, \ + C *in, C *out, int sign, unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_dft_1d)(int n, C *in, C *out, int sign, \ + unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_dft_2d)(int n0, int n1, \ + C *in, C *out, int sign, unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_dft_3d)(int n0, int n1, int n2, \ + C *in, C *out, int sign, unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_many_dft)(int rank, const int *n, \ + int howmany, \ + C *in, const int *inembed, \ + int istride, int idist, \ + C *out, const int *onembed, \ + int ostride, int odist, \ + int sign, unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_guru_dft)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + C *in, C *out, \ + int sign, unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_guru_split_dft)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *ri, R *ii, R *ro, R *io, \ + unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_guru64_dft)(int rank, \ + const X(iodim64) *dims, \ + int howmany_rank, \ + const X(iodim64) *howmany_dims, \ + C *in, C *out, \ + int sign, unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_guru64_split_dft)(int rank, \ + const X(iodim64) *dims, \ + int howmany_rank, \ + const X(iodim64) *howmany_dims, \ + R *ri, R *ii, R *ro, R *io, \ + unsigned flags); \ + \ +FFTW_EXTERN void X(execute_dft)(const X(plan) p, C *in, C *out); \ +FFTW_EXTERN void X(execute_split_dft)(const X(plan) p, R *ri, R *ii, \ + R *ro, R *io); \ + \ +FFTW_EXTERN X(plan) X(plan_many_dft_r2c)(int rank, const int *n, \ + int howmany, \ + R *in, const int *inembed, \ + int istride, int idist, \ + C *out, const int *onembed, \ + int ostride, int odist, \ + unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_dft_r2c)(int rank, const int *n, \ + R *in, C *out, unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_dft_r2c_1d)(int n,R *in,C *out,unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_dft_r2c_2d)(int n0, int n1, \ + R *in, C *out, unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_dft_r2c_3d)(int n0, int n1, \ + int n2, \ + R *in, C *out, unsigned flags); \ + \ + \ +FFTW_EXTERN X(plan) X(plan_many_dft_c2r)(int rank, const int *n, \ + int howmany, \ + C *in, const int *inembed, \ + int istride, int idist, \ + R *out, const int *onembed, \ + int ostride, int odist, \ + unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_dft_c2r)(int rank, const int *n, \ + C *in, R *out, unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_dft_c2r_1d)(int n,C *in,R *out,unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_dft_c2r_2d)(int n0, int n1, \ + C *in, R *out, unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_dft_c2r_3d)(int n0, int n1, \ + int n2, \ + C *in, R *out, unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_guru_dft_r2c)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *in, C *out, \ + unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_guru_dft_c2r)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + C *in, R *out, \ + unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_guru_split_dft_r2c)( \ + int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *in, R *ro, R *io, \ + unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_guru_split_dft_c2r)( \ + int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *ri, R *ii, R *out, \ + unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_guru64_dft_r2c)(int rank, \ + const X(iodim64) *dims, \ + int howmany_rank, \ + const X(iodim64) *howmany_dims, \ + R *in, C *out, \ + unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_guru64_dft_c2r)(int rank, \ + const X(iodim64) *dims, \ + int howmany_rank, \ + const X(iodim64) *howmany_dims, \ + C *in, R *out, \ + unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_guru64_split_dft_r2c)( \ + int rank, const X(iodim64) *dims, \ + int howmany_rank, \ + const X(iodim64) *howmany_dims, \ + R *in, R *ro, R *io, \ + unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_guru64_split_dft_c2r)( \ + int rank, const X(iodim64) *dims, \ + int howmany_rank, \ + const X(iodim64) *howmany_dims, \ + R *ri, R *ii, R *out, \ + unsigned flags); \ + \ +FFTW_EXTERN void X(execute_dft_r2c)(const X(plan) p, R *in, C *out); \ +FFTW_EXTERN void X(execute_dft_c2r)(const X(plan) p, C *in, R *out); \ + \ +FFTW_EXTERN void X(execute_split_dft_r2c)(const X(plan) p, \ + R *in, R *ro, R *io); \ +FFTW_EXTERN void X(execute_split_dft_c2r)(const X(plan) p, \ + R *ri, R *ii, R *out); \ + \ +FFTW_EXTERN X(plan) X(plan_many_r2r)(int rank, const int *n, \ + int howmany, \ + R *in, const int *inembed, \ + int istride, int idist, \ + R *out, const int *onembed, \ + int ostride, int odist, \ + const X(r2r_kind) *kind, unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_r2r)(int rank, const int *n, R *in, R *out, \ + const X(r2r_kind) *kind, unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_r2r_1d)(int n, R *in, R *out, \ + X(r2r_kind) kind, unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_r2r_2d)(int n0, int n1, R *in, R *out, \ + X(r2r_kind) kind0, X(r2r_kind) kind1, \ + unsigned flags); \ +FFTW_EXTERN X(plan) X(plan_r2r_3d)(int n0, int n1, int n2, \ + R *in, R *out, X(r2r_kind) kind0, \ + X(r2r_kind) kind1, X(r2r_kind) kind2, \ + unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_guru_r2r)(int rank, const X(iodim) *dims, \ + int howmany_rank, \ + const X(iodim) *howmany_dims, \ + R *in, R *out, \ + const X(r2r_kind) *kind, unsigned flags); \ + \ +FFTW_EXTERN X(plan) X(plan_guru64_r2r)(int rank, const X(iodim64) *dims, \ + int howmany_rank, \ + const X(iodim64) *howmany_dims, \ + R *in, R *out, \ + const X(r2r_kind) *kind, unsigned flags); \ + \ +FFTW_EXTERN void X(execute_r2r)(const X(plan) p, R *in, R *out); \ + \ +FFTW_EXTERN void X(destroy_plan)(X(plan) p); \ +FFTW_EXTERN void X(forget_wisdom)(void); \ +FFTW_EXTERN void X(cleanup)(void); \ + \ +FFTW_EXTERN void X(set_timelimit)(double t); \ + \ +FFTW_EXTERN void X(plan_with_nthreads)(int nthreads); \ +FFTW_EXTERN int X(init_threads)(void); \ +FFTW_EXTERN void X(cleanup_threads)(void); \ +FFTW_EXTERN void X(make_planner_thread_safe)(void); \ + \ +FFTW_EXTERN int X(export_wisdom_to_filename)(const char *filename); \ +FFTW_EXTERN void X(export_wisdom_to_file)(FILE *output_file); \ +FFTW_EXTERN char *X(export_wisdom_to_string)(void); \ +FFTW_EXTERN void X(export_wisdom)(X(write_char_func) write_char, \ + void *data); \ +FFTW_EXTERN int X(import_system_wisdom)(void); \ +FFTW_EXTERN int X(import_wisdom_from_filename)(const char *filename); \ +FFTW_EXTERN int X(import_wisdom_from_file)(FILE *input_file); \ +FFTW_EXTERN int X(import_wisdom_from_string)(const char *input_string); \ +FFTW_EXTERN int X(import_wisdom)(X(read_char_func) read_char, void *data); \ + \ +FFTW_EXTERN void X(fprint_plan)(const X(plan) p, FILE *output_file); \ +FFTW_EXTERN void X(print_plan)(const X(plan) p); \ +FFTW_EXTERN char *X(sprint_plan)(const X(plan) p); \ + \ +FFTW_EXTERN void *X(malloc)(size_t n); \ +FFTW_EXTERN R *X(alloc_real)(size_t n); \ +FFTW_EXTERN C *X(alloc_complex)(size_t n); \ +FFTW_EXTERN void X(free)(void *p); \ + \ +FFTW_EXTERN void X(flops)(const X(plan) p, \ + double *add, double *mul, double *fmas); \ +FFTW_EXTERN double X(estimate_cost)(const X(plan) p); \ +FFTW_EXTERN double X(cost)(const X(plan) p); \ + \ +FFTW_EXTERN int X(alignment_of)(R *p); \ +FFTW_EXTERN const char X(version)[]; \ +FFTW_EXTERN const char X(cc)[]; \ +FFTW_EXTERN const char X(codelet_optim)[]; + + +/* end of FFTW_DEFINE_API macro */ + +FFTW_DEFINE_API(FFTW_MANGLE_DOUBLE, double, fftw_complex) +FFTW_DEFINE_API(FFTW_MANGLE_FLOAT, float, fftwf_complex) +FFTW_DEFINE_API(FFTW_MANGLE_LONG_DOUBLE, long double, fftwl_complex) + +/* __float128 (quad precision) is a gcc extension on i386, x86_64, and ia64 + for gcc >= 4.6 (compiled in FFTW with --enable-quad-precision) */ +#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) \ + && !(defined(__ICC) || defined(__INTEL_COMPILER) || defined(__CUDACC__) || defined(__PGI)) \ + && (defined(__i386__) || defined(__x86_64__) || defined(__ia64__)) +# if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I) +/* note: __float128 is a typedef, which is not supported with the _Complex + keyword in gcc, so instead we use this ugly __attribute__ version. + However, we can't simply pass the __attribute__ version to + FFTW_DEFINE_API because the __attribute__ confuses gcc in pointer + types. Hence redefining FFTW_DEFINE_COMPLEX. Ugh. */ +# undef FFTW_DEFINE_COMPLEX +# define FFTW_DEFINE_COMPLEX(R, C) typedef _Complex float __attribute__((mode(TC))) C +# endif +FFTW_DEFINE_API(FFTW_MANGLE_QUAD, __float128, fftwq_complex) +#endif + +#define FFTW_FORWARD (-1) +#define FFTW_BACKWARD (+1) + +#define FFTW_NO_TIMELIMIT (-1.0) + +/* documented flags */ +#define FFTW_MEASURE (0U) +#define FFTW_DESTROY_INPUT (1U << 0) +#define FFTW_UNALIGNED (1U << 1) +#define FFTW_CONSERVE_MEMORY (1U << 2) +#define FFTW_EXHAUSTIVE (1U << 3) /* NO_EXHAUSTIVE is default */ +#define FFTW_PRESERVE_INPUT (1U << 4) /* cancels FFTW_DESTROY_INPUT */ +#define FFTW_PATIENT (1U << 5) /* IMPATIENT is default */ +#define FFTW_ESTIMATE (1U << 6) +#define FFTW_WISDOM_ONLY (1U << 21) + +/* undocumented beyond-guru flags */ +#define FFTW_ESTIMATE_PATIENT (1U << 7) +#define FFTW_BELIEVE_PCOST (1U << 8) +#define FFTW_NO_DFT_R2HC (1U << 9) +#define FFTW_NO_NONTHREADED (1U << 10) +#define FFTW_NO_BUFFERING (1U << 11) +#define FFTW_NO_INDIRECT_OP (1U << 12) +#define FFTW_ALLOW_LARGE_GENERIC (1U << 13) /* NO_LARGE_GENERIC is default */ +#define FFTW_NO_RANK_SPLITS (1U << 14) +#define FFTW_NO_VRANK_SPLITS (1U << 15) +#define FFTW_NO_VRECURSE (1U << 16) +#define FFTW_NO_SIMD (1U << 17) +#define FFTW_NO_SLOW (1U << 18) +#define FFTW_NO_FIXED_RADIX_LARGE_N (1U << 19) +#define FFTW_ALLOW_PRUNING (1U << 20) + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* FFTW3_H */ diff --git a/filterWindow.ui b/filterWindow.ui new file mode 100644 index 0000000..ffdd2e3 --- /dev/null +++ b/filterWindow.ui @@ -0,0 +1,106 @@ + + + Dialog + + + + 0 + 0 + 673 + 391 + + + + Dialog + + + + + 160 + 345 + 351 + 33 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + OK + + + + + + + Cancel + + + + + + + + + 15 + 15 + 642 + 312 + + + + TextLabel + + + + + + + okButton + clicked() + Dialog + accept() + + + 278 + 253 + + + 96 + 254 + + + + + cancelButton + clicked() + Dialog + reject() + + + 369 + 253 + + + 179 + 282 + + + + + diff --git a/galois.c b/galois.c new file mode 100644 index 0000000..c5ef231 --- /dev/null +++ b/galois.c @@ -0,0 +1,113 @@ +/***************************** + * Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009 + * + * This software library is licensed under terms of the GNU GENERAL + * PUBLIC LICENSE + * + * RSCODE 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. + * RSCODE 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 Rscode. If not, see . + + * Commercial licensing is available under a separate license, please + * contact author for details. + * + * Source code is available at http://rscode.sourceforge.net + * + * + * Multiplication and Arithmetic on Galois Field GF(256) + * + * From Mee, Daniel, "Magnetic Recording, Volume III", Ch. 5 by Patel. + * + * + ******************************/ + + +#include +#include +#include "ecc.h" + +/* This is one of 14 irreducible polynomials + * of degree 8 and cycle length 255. (Ch 5, pp. 275, Magnetic Recording) + * The high order 1 bit is implicit */ +/* x^8 + x^4 + x^3 + x^2 + 1 */ +#define PPOLY 0x1D + + +int gexp[512]; +int glog[256]; + + +static void init_exp_table (void); + + +void +init_galois_tables (void) +{ + /* initialize the table of powers of alpha */ + init_exp_table(); +} + + +static void +init_exp_table (void) +{ + int i, z; + int pinit,p1,p2,p3,p4,p5,p6,p7,p8; + + pinit = p2 = p3 = p4 = p5 = p6 = p7 = p8 = 0; + p1 = 1; + + gexp[0] = 1; + gexp[255] = gexp[0]; + glog[0] = 0; /* shouldn't log[0] be an error? */ + +// Private pp8() As Integer = {1, 0, 1, 1, 1, 0, 0, 0, 1} 'specify irreducible polynomial coeffts */ + + for (i = 1; i < 256; i++) { + pinit = p8; + p8 = p7; + p7 = p6; + p6 = p5; + p5 = p4 ^ pinit; + p4 = p3 ^ pinit; + p3 = p2 ^ pinit; + p2 = p1; + p1 = pinit; + gexp[i] = p1 + p2*2 + p3*4 + p4*8 + p5*16 + p6*32 + p7*64 + p8*128; + gexp[i+255] = gexp[i]; + } + + for (i = 1; i < 256; i++) { + for (z = 0; z < 256; z++) { + if (gexp[z] == i) { + glog[i] = z; + break; + } + } + } +} + +/* multiplication using logarithms */ +int gmult(int a, int b) +{ + int i,j; + if (a==0 || b == 0) return (0); + i = glog[a]; + j = glog[b]; + return (gexp[i+j]); +} + + +int ginv (int elt) +{ + return (gexp[255-glog[elt]]); +} + diff --git a/globals.h b/globals.h new file mode 100644 index 0000000..97d93a8 --- /dev/null +++ b/globals.h @@ -0,0 +1,337 @@ +// ---------------------------------------------------------------------------- +// globals.h -- constants, variables, arrays & functions that need to be +// outside of any thread +// +// Copyright (C) 2006-2007 +// Dave Freese, W1HKJ +// Copyright (C) 2007-2010 +// Stelios Bounanos, M0GLD +// +// This file is part of fldigi. Adapted in part from code contained in gmfsk +// source code distribution. +// +// Fldigi 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. +// +// Fldigi 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 fldigi. If not, see . +// ---------------------------------------------------------------------------- + +#ifndef _GLOBALS_H +#define _GLOBALS_H + +#include +//#include + +enum state_t { + STATE_PAUSE = 0, + STATE_RX, + STATE_TX, + STATE_RESTART, + STATE_TUNE, + STATE_ABORT, + STATE_FLUSH, + STATE_NOOP, + STATE_EXIT, + STATE_ENDED, + STATE_IDLE, + STATE_NEW_MODEM +}; + +enum { + MODE_PREV = -2, + MODE_NEXT, + + MODE_NULL, + + MODE_CW, + + MODE_CONTESTIA, + MODE_CONTESTIA_4_125, MODE_CONTESTIA_4_250, + MODE_CONTESTIA_4_500, MODE_CONTESTIA_4_1000, MODE_CONTESTIA_4_2000, + MODE_CONTESTIA_8_125, MODE_CONTESTIA_8_250, + MODE_CONTESTIA_8_500, MODE_CONTESTIA_8_1000, MODE_CONTESTIA_8_2000, + MODE_CONTESTIA_16_250, MODE_CONTESTIA_16_500, + MODE_CONTESTIA_16_1000, MODE_CONTESTIA_16_2000, + MODE_CONTESTIA_32_1000, MODE_CONTESTIA_32_2000, + MODE_CONTESTIA_64_500, MODE_CONTESTIA_64_1000, MODE_CONTESTIA_64_2000, + MODE_CONTESTIA_FIRST = MODE_CONTESTIA_4_125, + MODE_CONTESTIA_LAST = MODE_CONTESTIA_64_2000, + + MODE_DOMINOEXMICRO, + MODE_DOMINOEX4, + MODE_DOMINOEX5, + MODE_DOMINOEX8, + MODE_DOMINOEX11, + MODE_DOMINOEX16, + MODE_DOMINOEX22, + MODE_DOMINOEX44, + MODE_DOMINOEX88, + MODE_DOMINOEX_FIRST = MODE_DOMINOEXMICRO, + MODE_DOMINOEX_LAST = MODE_DOMINOEX88, + + MODE_FELDHELL, + MODE_SLOWHELL, + MODE_HELLX5, + MODE_HELLX9, + MODE_FSKH245, + MODE_FSKH105, + MODE_HELL80, + MODE_HELL_FIRST = MODE_FELDHELL, + MODE_HELL_LAST = MODE_HELL80, + + MODE_MFSK8, + MODE_MFSK16, + MODE_MFSK32, + MODE_MFSK4, + MODE_MFSK11, + MODE_MFSK22, + MODE_MFSK31, + MODE_MFSK64, + MODE_MFSK128, + MODE_MFSK64L, + MODE_MFSK128L, + MODE_MFSK_FIRST = MODE_MFSK8, + MODE_MFSK_LAST = MODE_MFSK128L, + + MODE_WEFAX_576, + MODE_WEFAX_288, + MODE_WEFAX_FIRST = MODE_WEFAX_576, + MODE_WEFAX_LAST = MODE_WEFAX_288, + + MODE_NAVTEX, + MODE_SITORB, + MODE_NAVTEX_FIRST = MODE_NAVTEX, + MODE_NAVTEX_LAST = MODE_SITORB, + + MODE_MT63_500S, + MODE_MT63_500L, + MODE_MT63_1000S, + MODE_MT63_1000L, + MODE_MT63_2000S, + MODE_MT63_2000L, + MODE_MT63_FIRST = MODE_MT63_500S, + MODE_MT63_LAST = MODE_MT63_2000L, + + MODE_PSK31, + MODE_PSK63, + MODE_PSK63F, + MODE_PSK125, + MODE_PSK250, + MODE_PSK500, + MODE_PSK1000, + + MODE_12X_PSK125, + MODE_6X_PSK250, + MODE_2X_PSK500, + MODE_4X_PSK500, + MODE_2X_PSK800, + MODE_2X_PSK1000, + + MODE_PSK_FIRST = MODE_PSK31, + MODE_PSK_LAST = MODE_2X_PSK1000, + + MODE_QPSK31, + MODE_QPSK63, + MODE_QPSK125, + MODE_QPSK250, + MODE_QPSK500, + + MODE_QPSK_FIRST = MODE_QPSK31, + MODE_QPSK_LAST = MODE_QPSK500, + + MODE_8PSK125, + MODE_8PSK125FL, + MODE_8PSK125F, + MODE_8PSK250, + MODE_8PSK250FL, + MODE_8PSK250F, + MODE_8PSK500, + MODE_8PSK500F, + MODE_8PSK1000, + MODE_8PSK1000F, + MODE_8PSK1200F, + MODE_8PSK_FIRST = MODE_8PSK125, + MODE_8PSK_LAST = MODE_8PSK1200F, + + MODE_OFDM_500F, + MODE_OFDM_750F, + MODE_OFDM_2000F, + MODE_OFDM_2000, + MODE_OFDM_3500, + + MODE_OLIVIA, + MODE_OLIVIA_4_125, + MODE_OLIVIA_4_250, + MODE_OLIVIA_4_500, + MODE_OLIVIA_4_1000, + MODE_OLIVIA_4_2000, + MODE_OLIVIA_8_125, + MODE_OLIVIA_8_250, + MODE_OLIVIA_8_500, + MODE_OLIVIA_8_1000, + MODE_OLIVIA_8_2000, + MODE_OLIVIA_16_500, + MODE_OLIVIA_16_1000, + MODE_OLIVIA_16_2000, + MODE_OLIVIA_32_1000, + MODE_OLIVIA_32_2000, + MODE_OLIVIA_64_500, + MODE_OLIVIA_64_1000, + MODE_OLIVIA_64_2000, + MODE_OLIVIA_FIRST = MODE_OLIVIA, + MODE_OLIVIA_LAST = MODE_OLIVIA_64_2000, + + MODE_RTTY, + + MODE_THORMICRO, + MODE_THOR4, + MODE_THOR5, + MODE_THOR8, + MODE_THOR11, + MODE_THOR16, + MODE_THOR22, + MODE_THOR25x4, + MODE_THOR50x1, + MODE_THOR50x2, + MODE_THOR100, + MODE_THOR_FIRST = MODE_THORMICRO, + MODE_THOR_LAST = MODE_THOR100, + + MODE_THROB1, + MODE_THROB2, + MODE_THROB4, + MODE_THROBX1, + MODE_THROBX2, + MODE_THROBX4, + MODE_THROB_FIRST = MODE_THROB1, + MODE_THROB_LAST = MODE_THROBX4, + +// MODE_PACKET, +// high speed && multiple carrier modes + + MODE_PSK125R, + MODE_PSK250R, + MODE_PSK500R, + MODE_PSK1000R, + + MODE_4X_PSK63R, + MODE_5X_PSK63R, + MODE_10X_PSK63R, + MODE_20X_PSK63R, + MODE_32X_PSK63R, + + MODE_4X_PSK125R, + MODE_5X_PSK125R, + MODE_10X_PSK125R, + MODE_12X_PSK125R, + MODE_16X_PSK125R, + + MODE_2X_PSK250R, + MODE_3X_PSK250R, + MODE_5X_PSK250R, + MODE_6X_PSK250R, + MODE_7X_PSK250R, + + MODE_2X_PSK500R, + MODE_3X_PSK500R, + MODE_4X_PSK500R, + + MODE_2X_PSK800R, + MODE_2X_PSK1000R, + + MODE_PSKR_FIRST = MODE_PSK125R, + MODE_PSKR_LAST = MODE_2X_PSK1000R, + + MODE_FSQ, + MODE_IFKP, + + MODE_SSB, + MODE_WWV, + MODE_ANALYSIS, + MODE_FMT, + + MODE_EOT, // a dummy mode used to invoke transmission of RsID-EOT code + NUM_MODES, + NUM_RXTX_MODES = MODE_SSB +}; + +typedef intptr_t trx_mode; + +struct mode_info_t { + trx_mode mode; + const char *sname; + const char *name; + const char *pskmail_name; + const char *adif_name; + const char *export_mode; + const char *export_submode; + const char *vid_name; + const unsigned int iface_io; // Some modes are not usable for a given interface. +}; +extern const struct mode_info_t mode_info[NUM_MODES]; + +/* + +class qrg_mode_t +{ +public: + long long rfcarrier; + std::string rmode; + int carrier; + trx_mode mode; + std::string usage; + + qrg_mode_t() : + rfcarrier(0), + rmode("NONE"), + carrier(0), + mode(NUM_MODES), + usage("") { } + qrg_mode_t(long long rfc_, std::string rm_, int c_, trx_mode m_, std::string use_ = "") + : rfcarrier(rfc_), rmode(rm_), carrier(c_), mode(m_), usage(use_) { } + bool operator<(const qrg_mode_t& rhs) const + { + return rfcarrier < rhs.rfcarrier; + } + bool operator==(const qrg_mode_t& rhs) const + { + return rfcarrier == rhs.rfcarrier && rmode == rhs.rmode && + carrier == rhs.carrier && mode == rhs.mode; + } + std::string str(void); +}; +std::ostream& operator<<(std::ostream& s, const qrg_mode_t& m); +std::istream& operator>>(std::istream& s, qrg_mode_t& m); + +#include +class mode_set_t : public std::bitset {}; +*/ + +enum band_t { + BAND_160M, BAND_80M, BAND_75M, BAND_60M, BAND_40M, BAND_30M, BAND_20M, + BAND_17M, BAND_15M, BAND_12M, BAND_10M, BAND_6M, BAND_4M, BAND_2M, BAND_125CM, + BAND_70CM, BAND_33CM, BAND_23CM, BAND_13CM, BAND_9CM, BAND_6CM, BAND_3CM, BAND_125MM, + BAND_6MM, BAND_4MM, BAND_2P5MM, BAND_2MM, BAND_1MM, BAND_OTHER, NUM_BANDS +}; +/* + +band_t band(long long freq_hz); +band_t band(const char* freq_mhz); +const char* band_name(band_t b); +const char* band_name(const char* freq_mhz); +const char* band_freq(band_t b); +const char* band_freq(const char* band_name); +*/ +// psk_browser enums +enum { VIEWER_LABEL_OFF, VIEWER_LABEL_AF, VIEWER_LABEL_RF, VIEWER_LABEL_CH, VIEWER_LABEL_NTYPES }; + + +#endif diff --git a/hid.c b/hid.c new file mode 100644 index 0000000..cbe63f7 --- /dev/null +++ b/hid.c @@ -0,0 +1,910 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +// Hacked about a bit for BPQ32. John Wiseman G8BPQ April 2018 + + + +#include + +#ifndef _NTDEF_ +typedef LONG NTSTATUS; +#endif + +#ifdef __MINGW32__ +#include +#include +#endif + +#ifdef __CYGWIN__ +#include +#define _wcsdup wcsdup +#endif + +//#define HIDAPI_USE_DDK + +#ifdef __cplusplus +extern "C" { +#endif + #include + #include + #ifdef HIDAPI_USE_DDK + #include + #endif + + // Copied from inc/ddk/hidclass.h, part of the Windows DDK. + #define HID_OUT_CTL_CODE(id) \ + CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS) + #define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100) + +#ifdef __cplusplus +} // extern "C" +#endif + +#include +#include + + +#include "hidapi.h" + +#ifdef _MSC_VER + // Thanks Microsoft, but I know how to use strncpy(). + #pragma warning(disable:4996) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HIDAPI_USE_DDK + // Since we're not building with the DDK, and the HID header + // files aren't part of the SDK, we have to define all this + // stuff here. In lookup_functions(), the function pointers + // defined below are set. + typedef struct _HIDD_ATTRIBUTES{ + ULONG Size; + USHORT VendorID; + USHORT ProductID; + USHORT VersionNumber; + } HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES; + + typedef USHORT USAGE; + typedef struct _HIDP_CAPS { + USAGE Usage; + USAGE UsagePage; + USHORT InputReportByteLength; + USHORT OutputReportByteLength; + USHORT FeatureReportByteLength; + USHORT Reserved[17]; + USHORT fields_not_used_by_hidapi[10]; + } HIDP_CAPS, *PHIDP_CAPS; + typedef char* HIDP_PREPARSED_DATA; + #define HIDP_STATUS_SUCCESS 0x0 + + typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib); + typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length); + typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len); + typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, HIDP_PREPARSED_DATA **preparsed_data); + typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(HIDP_PREPARSED_DATA *preparsed_data); + typedef BOOLEAN (__stdcall *HidP_GetCaps_)(HIDP_PREPARSED_DATA *preparsed_data, HIDP_CAPS *caps); + + static HidD_GetAttributes_ HidD_GetAttributes; + static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; + static HidD_GetManufacturerString_ HidD_GetManufacturerString; + static HidD_GetProductString_ HidD_GetProductString; + static HidD_SetFeature_ HidD_SetFeature; + static HidD_GetFeature_ HidD_GetFeature; + static HidD_GetIndexedString_ HidD_GetIndexedString; + static HidD_GetPreparsedData_ HidD_GetPreparsedData; + static HidD_FreePreparsedData_ HidD_FreePreparsedData; + static HidP_GetCaps_ HidP_GetCaps; + + static HMODULE lib_handle = NULL; + static BOOLEAN initialized = FALSE; +#endif // HIDAPI_USE_DDK + +struct hid_device_ { + HANDLE device_handle; + BOOL blocking; + int input_report_length; + void *last_error_str; + DWORD last_error_num; + BOOL read_pending; + char *read_buf; + OVERLAPPED ol; +}; + +static hid_device *new_hid_device() +{ + hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); + dev->device_handle = INVALID_HANDLE_VALUE; + dev->blocking = TRUE; + dev->input_report_length = 0; + dev->last_error_str = NULL; + dev->last_error_num = 0; + dev->read_pending = FALSE; + dev->read_buf = NULL; + memset(&dev->ol, 0, sizeof(dev->ol)); + dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); + + return dev; +} + + +static void register_error(hid_device *device, const char *op) +{ + WCHAR *ptr, *msg; + + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, 0/*sz*/, + NULL); + + // Get rid of the CR and LF that FormatMessage() sticks at the + // end of the message. Thanks Microsoft! + ptr = msg; + while (*ptr) { + if (*ptr == '\r') { + *ptr = 0x0000; + break; + } + ptr++; + } + + // Store the message off in the Device entry so that + // the hid_error() function can pick it up. + LocalFree(device->last_error_str); + device->last_error_str = msg; +} + +#ifndef HIDAPI_USE_DDK +static int lookup_functions() +{ + lib_handle = LoadLibraryA("hid.dll"); + if (lib_handle) { +#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; + RESOLVE(HidD_GetAttributes); + RESOLVE(HidD_GetSerialNumberString); + RESOLVE(HidD_GetManufacturerString); + RESOLVE(HidD_GetProductString); + RESOLVE(HidD_SetFeature); + RESOLVE(HidD_GetFeature); + RESOLVE(HidD_GetIndexedString); + RESOLVE(HidD_GetPreparsedData); + RESOLVE(HidD_FreePreparsedData); + RESOLVE(HidP_GetCaps); +#undef RESOLVE + } + else + return -1; + + return 0; +} +#endif + +static HANDLE open_device(const char *path) +{ + HANDLE handle; + + /* First, try to open with sharing mode turned off. This will make it so + that a HID device can only be opened once. This is to be consistent + with the behavior on the other platforms. */ + handle = CreateFileA(path, + GENERIC_WRITE |GENERIC_READ, + 0, /*share mode*/ + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, + 0); + + if (handle == INVALID_HANDLE_VALUE) { + /* Couldn't open the device. Some devices must be opened + with sharing enabled (even though they are only opened once), + so try it here. */ + handle = CreateFileA(path, + GENERIC_WRITE |GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, /*share mode*/ + NULL, + OPEN_EXISTING, + FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, + 0); + } + + return handle; +} + +int HID_API_EXPORT hid_init(void) +{ +#ifndef HIDAPI_USE_DDK + if (!initialized) { + if (lookup_functions() < 0) { + hid_exit(); + return -1; + } + initialized = TRUE; + } +#endif + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ +#ifndef HIDAPI_USE_DDK + if (lib_handle) + FreeLibrary(lib_handle); + lib_handle = NULL; + initialized = FALSE; +#endif + return 0; +} + +struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + BOOL res; + struct hid_device_info *root = NULL; // return object + struct hid_device_info *cur_dev = NULL; + + // Windows objects for interacting with the driver. + GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; + SP_DEVINFO_DATA devinfo_data; + SP_DEVICE_INTERFACE_DATA device_interface_data; + SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL; + HDEVINFO device_info_set = INVALID_HANDLE_VALUE; + int device_index = 0; + + if (hid_init() < 0) + return NULL; + + // Initialize the Windows objects. + devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); + device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + // Get information for all the devices belonging to the HID class. + device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + // Iterate over each device in the HID class, looking for the right one. + + for (;;) { + HANDLE write_handle = INVALID_HANDLE_VALUE; + DWORD required_size = 0; + HIDD_ATTRIBUTES attrib; + + res = SetupDiEnumDeviceInterfaces(device_info_set, + NULL, + &InterfaceClassGuid, + device_index, + &device_interface_data); + + if (!res) { + // A return of FALSE from this function means that + // there are no more devices. + break; + } + + // Call with 0-sized detail size, and let the function + // tell us how long the detail struct needs to be. The + // size is put in &required_size. + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + NULL, + 0, + &required_size, + NULL); + + // Allocate a long enough structure for device_interface_detail_data. + device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size); + device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + // Get the detailed data for this device. The detail data gives us + // the device path for this device, which is then passed into + // CreateFile() to get a handle to the device. + res = SetupDiGetDeviceInterfaceDetailA(device_info_set, + &device_interface_data, + device_interface_detail_data, + required_size, + NULL, + NULL); + + if (!res) { + //register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); + // Continue to the next device. + goto cont; + } + + //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); + + // Open a handle to the device + write_handle = open_device(device_interface_detail_data->DevicePath); + + // Check validity of write_handle. + if (write_handle == INVALID_HANDLE_VALUE) { + // Unable to open the device. + //register_error(dev, "CreateFile"); + goto cont_close; + } + + + // Get the Vendor ID and Product ID for this device. + attrib.Size = sizeof(HIDD_ATTRIBUTES); + HidD_GetAttributes(write_handle, &attrib); + //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); + + // Check the VID/PID to see if we should add this + // device to the enumeration list. + if ((vendor_id == 0x0 && product_id == 0x0) || + (attrib.VendorID == vendor_id && attrib.ProductID == product_id)) + { + #define WSTR_LEN 512 + const char *str; + struct hid_device_info *tmp; + HIDP_PREPARSED_DATA *pp_data = NULL; + HIDP_CAPS caps; + BOOLEAN res; + NTSTATUS nt_res; + wchar_t wstr[WSTR_LEN]; // TODO: Determine Size + int len; + + /* VID/PID match. Create the record. */ + tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + // Get the Usage Page and Usage for this device. + res = HidD_GetPreparsedData(write_handle, &pp_data); + if (res) { + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res == HIDP_STATUS_SUCCESS) { + cur_dev->usage_page = caps.UsagePage; + cur_dev->usage = caps.Usage; + } + + HidD_FreePreparsedData(pp_data); + } + + /* Fill out the record */ + cur_dev->next = NULL; + str = device_interface_detail_data->DevicePath; + if (str) { + len = (int)strlen(str); + cur_dev->path = (char*) calloc(len+1, sizeof(char)); + strncpy(cur_dev->path, str, len+1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->serial_number = _wcsdup(wstr); + } + + /* Manufacturer String */ + res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->manufacturer_string = _wcsdup(wstr); + } + + /* Product String */ + res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); + wstr[WSTR_LEN-1] = 0x0000; + if (res) { + cur_dev->product_string = _wcsdup(wstr); + } + + /* VID/PID */ + cur_dev->vendor_id = attrib.VendorID; + cur_dev->product_id = attrib.ProductID; + + /* Release Number */ + cur_dev->release_number = attrib.VersionNumber; + + /* Interface Number. It can sometimes be parsed out of the path + on Windows if a device has multiple interfaces. See + http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or + search for "Hardware IDs for HID Devices" at MSDN. If it's not + in the path, it's set to -1. */ + cur_dev->interface_number = -1; + if (cur_dev->path) { + char *interface_component = strstr(cur_dev->path, "&mi_"); + if (interface_component) { + char *hex_str = interface_component + 4; + char *endptr = NULL; + cur_dev->interface_number = strtol(hex_str, &endptr, 16); + if (endptr == hex_str) { + /* The parsing failed. Set interface_number to -1. */ + cur_dev->interface_number = -1; + } + } + } + } + +cont_close: + CloseHandle(write_handle); +cont: + // We no longer need the detail data. It can be freed + free(device_interface_detail_data); + + device_index++; + + } + + // Close the device information handle. + SetupDiDestroyDeviceInfoList(device_info_set); + + return root; + +} + +void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) +{ + // TODO: Merge this with the Linux version. This function is platform-independent. + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + + +HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) +{ + // TODO: Merge this functions with the Linux version. This function should be platform independent. + struct hid_device_info *devs, *cur_dev; + const char *path_to_open = NULL; + hid_device *handle = NULL; + + devs = hid_enumerate(vendor_id, product_id); + cur_dev = devs; + while (cur_dev) { + if (cur_dev->vendor_id == vendor_id && + cur_dev->product_id == product_id) { + if (serial_number) { + if (wcscmp(serial_number, cur_dev->serial_number) == 0) { + path_to_open = cur_dev->path; + break; + } + } + else { + path_to_open = cur_dev->path; + break; + } + } + cur_dev = cur_dev->next; + } + + if (path_to_open) { + /* Open the device */ + handle = hid_open_path(path_to_open); + } + + hid_free_enumeration(devs); + + return handle; +} + +HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) +{ + hid_device *dev; + HIDP_CAPS caps; + HIDP_PREPARSED_DATA *pp_data = NULL; + BOOLEAN res; + NTSTATUS nt_res; + + if (hid_init() < 0) { + return NULL; + } + + dev = new_hid_device(); + + // Open a handle to the device + dev->device_handle = open_device(path); + + // Check validity of write_handle. + if (dev->device_handle == INVALID_HANDLE_VALUE) { + // Unable to open the device. + register_error(dev, "CreateFile"); + goto err; + } + + // Get the Input Report length for the device. + res = HidD_GetPreparsedData(dev->device_handle, &pp_data); + if (!res) { + register_error(dev, "HidD_GetPreparsedData"); + goto err; + } + nt_res = HidP_GetCaps(pp_data, &caps); + if (nt_res != HIDP_STATUS_SUCCESS) { + register_error(dev, "HidP_GetCaps"); + goto err_pp_data; + } + dev->input_report_length = caps.InputReportByteLength; + HidD_FreePreparsedData(pp_data); + + dev->read_buf = (char*) malloc(dev->input_report_length); + + return dev; + +err_pp_data: + HidD_FreePreparsedData(pp_data); +err: + CloseHandle(dev->device_handle); + free(dev); + return NULL; +} + +int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + DWORD bytes_written; + BOOL res; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = WriteFile(dev->device_handle, data, length, NULL, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + // WriteFile() failed. Return error. + register_error(dev, "WriteFile"); + return -1; + } + } + + // Wait here until the write is done. This makes + // hid_write() synchronous. + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); + if (!res) { + // The Write operation failed. + register_error(dev, "WriteFile"); + return -1; + } + + return bytes_written; +} + +int HID_API_EXPORT HID_API_CALL hid_set_ptt(int state) +{ + int res; + hid_device *handle; + unsigned char buf[16]; + + handle = hid_open(0xd8c, 0x8, NULL); + if (!handle) { + printf("unable to open device\n"); + return 1; + } + + + // Toggle PTT + + buf[0] = 0; + buf[1] = 0; + buf[2]= 1 << (3 - 1); + buf[3] = state << (3 - 1); + buf[4] = 0; + + + res = hid_write(handle, buf, 5); + if (res < 0) + { + printf("Unable to write()\n"); + printf("Error: %ls\n", hid_error(handle)); + } + + hid_close(handle); + return res; +} + + +int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + DWORD bytes_read = 0; + BOOL res; + + // Copy the handle for convenience. + HANDLE ev = dev->ol.hEvent; + + if (!dev->read_pending) { + // Start an Overlapped I/O read. + dev->read_pending = TRUE; + ResetEvent(ev); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + // ReadFile() has failed. + // Clean up and return error. + CancelIo(dev->device_handle); + dev->read_pending = FALSE; + goto end_of_function; + } + } + } + + if (milliseconds >= 0) { + // See if there is any data yet. + res = WaitForSingleObject(ev, milliseconds); + if (res != WAIT_OBJECT_0) { + // There was no data this time. Return zero bytes available, + // but leave the Overlapped I/O running. + return 0; + } + } + + // Either WaitForSingleObject() told us that ReadFile has completed, or + // we are in non-blocking mode. Get the number of bytes read. The actual + // data has been copied to the data[] array which was passed to ReadFile(). + res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); + + // Set pending back to false, even if GetOverlappedResult() returned error. + dev->read_pending = FALSE; + + if (res && bytes_read > 0) { + if (dev->read_buf[0] == 0x0) { + /* If report numbers aren't being used, but Windows sticks a report + number (0x0) on the beginning of the report anyway. To make this + work like the other platforms, and to make it work more like the + HID spec, we'll skip over this byte. */ + bytes_read--; + memcpy(data, dev->read_buf+1, length); + } + else { + /* Copy the whole buffer, report number and all. */ + memcpy(data, dev->read_buf, length); + } + } + +end_of_function: + if (!res) { + register_error(dev, "GetOverlappedResult"); + return -1; + } + + return bytes_read; +} + +int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) +{ + return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); +} + +int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) +{ + dev->blocking = !nonblock; + return 0; /* Success */ +} + +int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); + if (!res) { + register_error(dev, "HidD_SetFeature"); + return -1; + } + + return length; +} + + +int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + BOOL res; +#if 0 + res = HidD_GetFeature(dev->device_handle, data, length); + if (!res) { + register_error(dev, "HidD_GetFeature"); + return -1; + } + return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ +#else + DWORD bytes_returned; + + OVERLAPPED ol; + memset(&ol, 0, sizeof(ol)); + + res = DeviceIoControl(dev->device_handle, + IOCTL_HID_GET_FEATURE, + data, length, + data, length, + &bytes_returned, &ol); + + if (!res) { + if (GetLastError() != ERROR_IO_PENDING) { + // DeviceIoControl() failed. Return error. + register_error(dev, "Send Feature Report DeviceIoControl"); + return -1; + } + } + + // Wait here until the write is done. This makes + // hid_get_feature_report() synchronous. + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); + if (!res) { + // The operation failed. + register_error(dev, "Send Feature Report GetOverLappedResult"); + return -1; + } + return bytes_returned; +#endif +} + +void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) +{ + if (!dev) + return; + CancelIo(dev->device_handle); + CloseHandle(dev->ol.hEvent); + CloseHandle(dev->device_handle); + LocalFree(dev->last_error_str); + free(dev->read_buf); + free(dev); +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetManufacturerString(dev->device_handle, string, 2 * maxlen); + if (!res) { + register_error(dev, "HidD_GetManufacturerString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetProductString(dev->device_handle, string, 2 * maxlen); + if (!res) { + register_error(dev, "HidD_GetProductString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetSerialNumberString(dev->device_handle, string, 2 * maxlen); + if (!res) { + register_error(dev, "HidD_GetSerialNumberString"); + return -1; + } + + return 0; +} + +int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + BOOL res; + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, 2 * maxlen); + if (!res) { + register_error(dev, "HidD_GetIndexedString"); + return -1; + } + + return 0; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return (wchar_t*)dev->last_error_str; +} + + +//#define PICPGM +//#define S11 +#define P32 +#ifdef S11 + unsigned short VendorID = 0xa0a0; + unsigned short ProductID = 0x0001; +#endif + +#ifdef P32 + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x3f; +#endif + + +#ifdef PICPGM + unsigned short VendorID = 0x04d8; + unsigned short ProductID = 0x0033; +#endif + + +#if 0 +int __cdecl main(int argc, char* argv[]) +{ + int res; + unsigned char buf[65]; + + UNREFERENCED_PARAMETER(argc); + UNREFERENCED_PARAMETER(argv); + + // Set up the command buffer. + memset(buf,0x00,sizeof(buf)); + buf[0] = 0; + buf[1] = 0x81; + + + // Open the device. + int handle = open(VendorID, ProductID, L"12345"); + if (handle < 0) + printf("unable to open device\n"); + + + // Toggle LED (cmd 0x80) + buf[1] = 0x80; + res = write(handle, buf, 65); + if (res < 0) + printf("Unable to write()\n"); + + // Request state (cmd 0x81) + buf[1] = 0x81; + write(handle, buf, 65); + if (res < 0) + printf("Unable to write() (2)\n"); + + // Read requested state + read(handle, buf, 65); + if (res < 0) + printf("Unable to read()\n"); + + // Print out the returned buffer. + for (int i = 0; i < 4; i++) + printf("buf[%d]: %d\n", i, buf[i]); + + return 0; +} +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/hidapi.h b/hidapi.h new file mode 100644 index 0000000..e8ad2ca --- /dev/null +++ b/hidapi.h @@ -0,0 +1,386 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + + Copyright 2009, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + http://github.com/signal11/hidapi . +********************************************************/ + +/** @file + * @defgroup API hidapi API + */ + +#ifndef HIDAPI_H__ +#define HIDAPI_H__ + +#include + +#ifdef _WIN32 +// #define HID_API_EXPORT __declspec(dllexport) // BPQ + #define HID_API_EXPORT + #define HID_API_CALL +#else + #define HID_API_EXPORT /**< API export macro */ + #define HID_API_CALL /**< API call macro */ +#endif + +#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct hid_device_; + typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ + + /** hidapi info structure */ + struct hid_device_info { + /** Platform-specific device path */ + char *path; + /** Device Vendor ID */ + unsigned short vendor_id; + /** Device Product ID */ + unsigned short product_id; + /** Serial Number */ + wchar_t *serial_number; + /** Device Release Number in binary-coded decimal, + also known as Device Version Number */ + unsigned short release_number; + /** Manufacturer String */ + wchar_t *manufacturer_string; + /** Product string */ + wchar_t *product_string; + /** Usage Page for this Device/Interface + (Windows/Mac only). */ + unsigned short usage_page; + /** Usage for this Device/Interface + (Windows/Mac only).*/ + unsigned short usage; + /** The USB interface which this logical device + represents. Valid on both Linux implementations + in all cases, and valid on the Windows implementation + only if the device contains more than one interface. */ + int interface_number; + + /** Pointer to the next device */ + struct hid_device_info *next; + }; + + + /** @brief Initialize the HIDAPI library. + + This function initializes the HIDAPI library. Calling it is not + strictly necessary, as it will be called automatically by + hid_enumerate() and any of the hid_open_*() functions if it is + needed. This function should be called at the beginning of + execution however, if there is a chance of HIDAPI handles + being opened by different threads simultaneously. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_init(void); + + /** @brief Finalize the HIDAPI library. + + This function frees all of the static data associated with + HIDAPI. It should be called at the end of execution to avoid + memory leaks. + + @ingroup API + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_exit(void); + + /** @brief Enumerate the HID Devices. + + This function returns a linked list of all the HID devices + attached to the system which match vendor_id and product_id. + If @p vendor_id and @p product_id are both set to 0, then + all HID devices will be returned. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the types of device + to open. + @param product_id The Product ID (PID) of the types of + device to open. + + @returns + This function returns a pointer to a linked list of type + struct #hid_device, containing information about the HID devices + attached to the system, or NULL in the case of failure. Free + this linked list by calling hid_free_enumeration(). + */ + struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); + + /** @brief Free an enumeration Linked List + + This function frees a linked list created by hid_enumerate(). + + @ingroup API + @param devs Pointer to a list of struct_device returned from + hid_enumerate(). + */ + void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); + + /** @brief Open a HID device using a Vendor ID (VID), Product ID + (PID) and optionally a serial number. + + If @p serial_number is NULL, the first device with the + specified VID and PID is opened. + + @ingroup API + @param vendor_id The Vendor ID (VID) of the device to open. + @param product_id The Product ID (PID) of the device to open. + @param serial_number The Serial Number of the device to open + (Optionally NULL). + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number); + + /** @brief Open a HID device by its path name. + + The path name be determined by calling hid_enumerate(), or a + platform-specific path name can be used (eg: /dev/hidraw0 on + Linux). + + @ingroup API + @param path The path name of the device to open + + @returns + This function returns a pointer to a #hid_device object on + success or NULL on failure. + */ + HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); + + /** @brief Write an Output report to a HID device. + + The first byte of @p data[] must contain the Report ID. For + devices which only support a single report, this must be set + to 0x0. The remaining bytes contain the report data. Since + the Report ID is mandatory, calls to hid_write() will always + contain one more byte than the report contains. For example, + if a hid report is 16 bytes long, 17 bytes must be passed to + hid_write(), the Report ID (or 0x0, for devices with a + single report), followed by the report data (16 bytes). In + this example, the length passed in would be 17. + + hid_write() will send the data on the first OUT endpoint, if + one exists. If it does not, it will send the data through + the Control Endpoint (Endpoint 0). + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Read an Input report from a HID device with timeout. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + @param milliseconds timeout in milliseconds or -1 for blocking wait. + + @returns + This function returns the actual number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); + + /** @brief Read an Input report from a HID device. + + Input reports are returned + to the host through the INTERRUPT IN endpoint. The first byte will + contain the Report number if the device uses numbered reports. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into. + @param length The number of bytes to read. For devices with + multiple reports, make sure to read an extra byte for + the report number. + + @returns + This function returns the actual number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); + + /** @brief Set the device handle to be non-blocking. + + In non-blocking mode calls to hid_read() will return + immediately with a value of 0 if there is no data to be + read. In blocking mode, hid_read() will wait (block) until + there is data to read before returning. + + Nonblocking can be turned on and off at any time. + + @ingroup API + @param device A device handle returned from hid_open(). + @param nonblock enable or not the nonblocking reads + - 1 to enable nonblocking + - 0 to disable nonblocking. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); + + /** @brief Send a Feature report to the device. + + Feature reports are sent over the Control endpoint as a + Set_Report transfer. The first byte of @p data[] must + contain the Report ID. For devices which only support a + single report, this must be set to 0x0. The remaining bytes + contain the report data. Since the Report ID is mandatory, + calls to hid_send_feature_report() will always contain one + more byte than the report contains. For example, if a hid + report is 16 bytes long, 17 bytes must be passed to + hid_send_feature_report(): the Report ID (or 0x0, for + devices which do not use numbered reports), followed by the + report data (16 bytes). In this example, the length passed + in would be 17. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data The data to send, including the report number as + the first byte. + @param length The length in bytes of the data to send, including + the report number. + + @returns + This function returns the actual number of bytes written and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); + + /** @brief Get a feature report from a HID device. + + Make sure to set the first byte of @p data[] to the Report + ID of the report to be read. Make sure to allow space for + this extra byte in @p data[]. + + @ingroup API + @param device A device handle returned from hid_open(). + @param data A buffer to put the read data into, including + the Report ID. Set the first byte of @p data[] to the + Report ID of the report to be read. + @param length The number of bytes to read, including an + extra byte for the report ID. The buffer can be longer + than the actual report. + + @returns + This function returns the number of bytes read and + -1 on error. + */ + int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); + + /** @brief Close a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + */ + void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); + + /** @brief Get The Manufacturer String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Product String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get The Serial Number String from a HID device. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); + + /** @brief Get a string from a HID device, based on its string index. + + @ingroup API + @param device A device handle returned from hid_open(). + @param string_index The index of the string to get. + @param string A wide string buffer to put the data into. + @param maxlen The length of the buffer in multiples of wchar_t. + + @returns + This function returns 0 on success and -1 on error. + */ + int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); + + /** @brief Get a string describing the last error which occurred. + + @ingroup API + @param device A device handle returned from hid_open(). + + @returns + This function returns a string containing the last error + which occurred or NULL if none has occurred. + */ + HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); + + int HID_API_EXPORT HID_API_CALL hid_set_ptt(int state); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/il2p.c b/il2p.c new file mode 100644 index 0000000..bea8521 --- /dev/null +++ b/il2p.c @@ -0,0 +1,4507 @@ +/* +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 + +// IL2P code. Based on Direwolf code, under the following copyright + +// +// Copyright (C) 2021 John Langner, WB2OSZ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + + +// IP2P receive code (il2p_rec_bit) is called from the bit receiving code in ax25_demod.c, so includes parallel decoders + + + + +#include "UZ7HOStuff.h" + +void Debugprintf(const char * format, ...); + +#define MAX_ADEVS 3 + +#define MAX_RADIO_CHANS ((MAX_ADEVS) * 2) + +#define MAX_CHANS MAX_RADIO_CHANS // TODO: Replace all former with latter to avoid confusion with following. + +#define MAX_TOTAL_CHANS 16 // v1.7 allows additional virtual channels which are connected + // to something other than radio modems. + // Total maximum channels is based on the 4 bit KISS field. + // Someone with very unusual requirements could increase this and + // use only the AGW network protocol. + + +#define MAX_SUBCHANS 9 + +#define MAX_SLICERS 9 + +#define max(x, y) ((x) > (y) ? (x) : (y)) +#define min(x, y) ((x) < (y) ? (x) : (y)) + +/* For option to try fixing frames with bad CRC. */ + +typedef enum retry_e { + RETRY_NONE = 0, + RETRY_INVERT_SINGLE = 1, + RETRY_INVERT_DOUBLE = 2, + RETRY_INVERT_TRIPLE = 3, + RETRY_INVERT_TWO_SEP = 4, + RETRY_MAX = 5 +} retry_t; + +typedef struct alevel_s { + int rec; + int mark; + int space; + //float ms_ratio; // TODO: take out after temporary investigation. +} alevel_t; + + +alevel_t demod_get_audio_level(int chan, int subchan); +void tone_gen_put_bit(int chan, int dat); + +int ax25memdebug = 1; + +// Code to try to determine centre freq + +float MagOut[4096]; +float MaxMagOut = 0; +int MaxMagIndex = 0; + +// FFT Bin Size is 12000 / FFTSize + +#ifndef FX25_H +#define FX25_H + +#include // for uint64_t + + +/* Reed-Solomon codec control block */ +struct rs { + unsigned int mm; /* Bits per symbol */ + unsigned int nn; /* Symbols per block (= (1<mm) +#define NN (rs->nn) +#define ALPHA_TO (rs->alpha_to) +#define INDEX_OF (rs->index_of) +#define GENPOLY (rs->genpoly) +#define NROOTS (rs->nroots) +#define FCR (rs->fcr) +#define PRIM (rs->prim) +#define IPRIM (rs->iprim) +#define A0 (NN) + +int __builtin_popcountll(unsigned long long int i) +{ + return 0; +} + +int __builtin_popcount(unsigned int n) +{ + unsigned int count = 0; + while (n) + { + count += n & 1; + n >>= 1; + } + return count; +} + +static inline int modnn(struct rs *rs, int x) { + while (x >= rs->nn) { + x -= rs->nn; + x = (x >> rs->mm) + (x & rs->nn); + } + return x; +} + +#define MODNN(x) modnn(rs,x) + + +#define ENCODE_RS encode_rs_char +#define DECODE_RS decode_rs_char +#define INIT_RS init_rs_char +#define FREE_RS free_rs_char + +#define DTYPE unsigned char + +void ENCODE_RS(struct rs *rs, DTYPE *data, DTYPE *bb); + +int DECODE_RS(struct rs *rs, DTYPE *data, int *eras_pos, int no_eras); + +struct rs *INIT_RS(unsigned int symsize, unsigned int gfpoly, + unsigned int fcr, unsigned int prim, unsigned int nroots); + +void FREE_RS(struct rs *rs); + + + +// These 3 are the external interface. +// Maybe these should be in a different file, separated from the internal stuff. + +void fx25_init(int debug_level); +int fx25_send_frame(int chan, unsigned char *fbuf, int flen, int fx_mode); +void fx25_rec_bit(int chan, int subchan, int slice, int dbit); +int fx25_rec_busy(int chan); + + +// Other functions in fx25_init.c. + +struct rs *fx25_get_rs(int ctag_num); +uint64_t fx25_get_ctag_value(int ctag_num); +int fx25_get_k_data_radio(int ctag_num); +int fx25_get_k_data_rs(int ctag_num); +int fx25_get_nroots(int ctag_num); +int fx25_get_debug(void); +int fx25_tag_find_match(uint64_t t); +int fx25_pick_mode(int fx_mode, int dlen); + +void fx_hex_dump(unsigned char *x, int len); + +/*------------------------------------------------------------------- + * + * Name: ax25_pad.h + * + * Purpose: Header file for using ax25_pad.c + * + *------------------------------------------------------------------*/ + +#ifndef AX25_PAD_H +#define AX25_PAD_H 1 + + +#define AX25_MAX_REPEATERS 8 +#define AX25_MIN_ADDRS 2 /* Destination & Source. */ +#define AX25_MAX_ADDRS 10 /* Destination, Source, 8 digipeaters. */ + +#define AX25_DESTINATION 0 /* Address positions in frame. */ +#define AX25_SOURCE 1 +#define AX25_REPEATER_1 2 +#define AX25_REPEATER_2 3 +#define AX25_REPEATER_3 4 +#define AX25_REPEATER_4 5 +#define AX25_REPEATER_5 6 +#define AX25_REPEATER_6 7 +#define AX25_REPEATER_7 8 +#define AX25_REPEATER_8 9 + +#define AX25_MAX_ADDR_LEN 12 /* In theory, you would expect the maximum length */ + /* to be 6 letters, dash, 2 digits, and nul for a */ + /* total of 10. However, object labels can be 10 */ + /* characters so throw in a couple extra bytes */ + /* to be safe. */ + +#define AX25_MIN_INFO_LEN 0 /* Previously 1 when considering only APRS. */ + +#define AX25_MAX_INFO_LEN 2048 /* Maximum size for APRS. */ + /* AX.25 starts out with 256 as the default max */ + /* length but the end stations can negotiate */ + /* something different. */ + /* version 0.8: Change from 256 to 2028 to */ + /* handle the larger paclen for Linux AX25. */ + + /* These don't include the 2 bytes for the */ + /* HDLC frame FCS. */ + +/* + * Previously, for APRS only. + * #define AX25_MIN_PACKET_LEN ( 2 * 7 + 2 + AX25_MIN_INFO_LEN) + * #define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + AX25_MAX_INFO_LEN) + */ + + /* The more general case. */ + /* An AX.25 frame can have a control byte and no protocol. */ + +#define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 ) + +#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + 3 + AX25_MAX_INFO_LEN) + + +/* + * packet_t is a pointer to a packet object. + * + * The actual implementation is not visible outside ax25_pad.c. + */ + +#define AX25_UI_FRAME 3 /* Control field value. */ + +#define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */ +#define AX25_PID_SEGMENTATION_FRAGMENT 0x08 +#define AX25_PID_ESCAPE_CHARACTER 0xff + +struct packet_s { + + int magic1; /* for error checking. */ + + int seq; /* unique sequence number for debugging. */ + + double release_time; /* Time stamp in format returned by dtime_now(). */ + /* When to release from the SATgate mode delay queue. */ + +#define MAGIC 0x41583235 + + struct packet_s *nextp; /* Pointer to next in queue. */ + + int num_addr; /* Number of addresses in frame. */ + /* Range of AX25_MIN_ADDRS .. AX25_MAX_ADDRS for AX.25. */ + /* It will be 0 if it doesn't look like AX.25. */ + /* -1 is used temporarily at allocation to mean */ + /* not determined yet. */ + + + + /* + * The 7th octet of each address contains: + * + * Bits: H R R SSID 0 + * + * H for digipeaters set to 0 initially. + * Changed to 1 when position has been used. + * + * for source & destination it is called + * command/response. Normally both 1 for APRS. + * They should be opposites for connected mode. + * + * R R Reserved. Normally set to 1 1. + * + * SSID Substation ID. Range of 0 - 15. + * + * 0 Usually 0 but 1 for last address. + */ + + +#define SSID_H_MASK 0x80 +#define SSID_H_SHIFT 7 + +#define SSID_RR_MASK 0x60 +#define SSID_RR_SHIFT 5 + +#define SSID_SSID_MASK 0x1e +#define SSID_SSID_SHIFT 1 + +#define SSID_LAST_MASK 0x01 + + + int frame_len; /* Frame length without CRC. */ + + int modulo; /* I & S frames have sequence numbers of either 3 bits (modulo 8) */ + /* or 7 bits (modulo 128). This is conveyed by either 1 or 2 */ + /* control bytes. Unfortunately, we can't determine this by looking */ + /* at an isolated frame. We need to know about the context. If we */ + /* are part of the conversation, we would know. But if we are */ + /* just listening to others, this would be more difficult to determine. */ + + /* For U frames: set to 0 - not applicable */ + /* For I & S frames: 8 or 128 if known. 0 if unknown. */ + + unsigned char frame_data[AX25_MAX_PACKET_LEN + 1]; + /* Raw frame contents, without the CRC. */ + + + int magic2; /* Will get stomped on if above overflows. */ +}; + + + +typedef struct packet_s *packet_t; + +typedef enum cmdres_e { cr_00 = 2, cr_cmd = 1, cr_res = 0, cr_11 = 3 } cmdres_t; + + +extern packet_t ax25_new(void); + + +#ifdef AX25_PAD_C /* Keep this hidden - implementation could change. */ + + +/* + * APRS always has one control octet of 0x03 but the more + * general AX.25 case is one or two control bytes depending on + * whether "modulo 128 operation" is in effect. + */ + + //#define DEBUGX 1 + +static inline int ax25_get_control_offset(packet_t this_p) +{ + return (this_p->num_addr * 7); +} + +static inline int ax25_get_num_control(packet_t this_p) +{ + int c; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0) { /* I xxxx xxx0 */ +#if DEBUGX + Debugprintf("ax25_get_num_control, %02x is I frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1); +#endif + return ((this_p->modulo == 128) ? 2 : 1); + } + + if ((c & 0x03) == 1) { /* S xxxx xx01 */ +#if DEBUGX + Debugprintf("ax25_get_num_control, %02x is S frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1); +#endif + return ((this_p->modulo == 128) ? 2 : 1); + } + +#if DEBUGX + Debugprintf("ax25_get_num_control, %02x is U frame, always returns 1.\n", c); +#endif + + return (1); /* U xxxx xx11 */ +} + + + +/* + * APRS always has one protocol octet of 0xF0 meaning no level 3 + * protocol but the more general case is 0, 1 or 2 protocol ID octets. + */ + +static inline int ax25_get_pid_offset(packet_t this_p) +{ + return (ax25_get_control_offset(this_p) + ax25_get_num_control(this_p)); +} + +static int ax25_get_num_pid(packet_t this_p) +{ + int c; + int pid; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0 || /* I xxxx xxx0 */ + c == 0x03 || c == 0x13) { /* UI 000x 0011 */ + + pid = this_p->frame_data[ax25_get_pid_offset(this_p)]; +#if DEBUGX + Debugprintf("ax25_get_num_pid, %02x is I or UI frame, pid = %02x, returns %d\n", c, pid, (pid == AX25_PID_ESCAPE_CHARACTER) ? 2 : 1); +#endif + if (pid == AX25_PID_ESCAPE_CHARACTER) { + return (2); /* pid 1111 1111 means another follows. */ + } + return (1); + } +#if DEBUGX + Debugprintf("ax25_get_num_pid, %02x is neither I nor UI frame, returns 0\n", c); +#endif + return (0); +} + + +/* + * AX.25 has info field for 5 frame types depending on the control field. + * + * xxxx xxx0 I + * 000x 0011 UI (which includes APRS) + * 101x 1111 XID + * 111x 0011 TEST + * 100x 0111 FRMR + * + * APRS always has an Information field with at least one octet for the Data Type Indicator. + */ + +static inline int ax25_get_info_offset(packet_t this_p) +{ + int offset = ax25_get_control_offset(this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p); +#if DEBUGX + Debugprintf("ax25_get_info_offset, returns %d\n", offset); +#endif + return (offset); +} + +static inline int ax25_get_num_info(packet_t this_p) +{ + int len; + + /* assuming AX.25 frame. */ + + len = this_p->frame_len - this_p->num_addr * 7 - ax25_get_num_control(this_p) - ax25_get_num_pid(this_p); + if (len < 0) { + len = 0; /* print error? */ + } + + return (len); +} + +#endif + + +typedef enum ax25_modulo_e { modulo_unknown = 0, modulo_8 = 8, modulo_128 = 128 } ax25_modulo_t; + +typedef enum ax25_frame_type_e { + + frame_type_I = 0, // Information + + frame_type_S_RR, // Receive Ready - System Ready To Receive + frame_type_S_RNR, // Receive Not Ready - TNC Buffer Full + frame_type_S_REJ, // Reject Frame - Out of Sequence or Duplicate + frame_type_S_SREJ, // Selective Reject - Request single frame repeat + + frame_type_U_SABME, // Set Async Balanced Mode, Extended + frame_type_U_SABM, // Set Async Balanced Mode + frame_type_U_DISC, // Disconnect + frame_type_U_DM, // Disconnect Mode + frame_type_U_UA, // Unnumbered Acknowledge + frame_type_U_FRMR, // Frame Reject + frame_type_U_UI, // Unnumbered Information + frame_type_U_XID, // Exchange Identification + frame_type_U_TEST, // Test + frame_type_U, // other Unnumbered, not used by AX.25. + + frame_not_AX25 // Could not get control byte from frame. + // This must be last because value plus 1 is + // for the size of an array. + +} ax25_frame_type_t; + + +/* + * Originally this was a single number. + * Let's try something new in version 1.2. + * Also collect AGC values from the mark and space filters. + */ + +#ifndef AXTEST +// TODO: remove this? +#define AX25MEMDEBUG 1 +#endif + + + +extern packet_t ax25_from_text(char *monitor, int strict); + +extern packet_t ax25_from_frame(unsigned char *data, int len, alevel_t alevel); + +extern packet_t ax25_dup(packet_t copy_from); + +extern void ax25_delete(packet_t pp); + + + +extern int ax25_parse_addr(int position, char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard); +extern int ax25_check_addresses(packet_t pp); + +extern packet_t ax25_unwrap_third_party(packet_t from_pp); + +extern void ax25_set_addr(packet_t pp, int, char *); +extern void ax25_insert_addr(packet_t this_p, int n, char *ad); +extern void ax25_remove_addr(packet_t this_p, int n); + +extern int ax25_get_num_addr(packet_t pp); +extern int ax25_get_num_repeaters(packet_t this_p); + +extern void ax25_get_addr_with_ssid(packet_t pp, int n, char *station); +extern void ax25_get_addr_no_ssid(packet_t pp, int n, char *station); + +extern int ax25_get_ssid(packet_t pp, int n); +extern void ax25_set_ssid(packet_t this_p, int n, int ssid); + +extern int ax25_get_h(packet_t pp, int n); + +extern void ax25_set_h(packet_t pp, int n); + +extern int ax25_get_heard(packet_t this_p); + +extern int ax25_get_first_not_repeated(packet_t pp); + +extern int ax25_get_rr(packet_t this_p, int n); + +extern int ax25_get_info(packet_t pp, unsigned char **paddr); +extern void ax25_set_info(packet_t pp, unsigned char *info_ptr, int info_len); +extern int ax25_cut_at_crlf(packet_t this_p); + +extern void ax25_set_nextp(packet_t this_p, packet_t next_p); + +extern int ax25_get_dti(packet_t this_p); + +extern packet_t ax25_get_nextp(packet_t this_p); + +extern void ax25_set_release_time(packet_t this_p, double release_time); +extern double ax25_get_release_time(packet_t this_p); + +extern void ax25_set_modulo(packet_t this_p, int modulo); +extern int ax25_get_modulo(packet_t this_p); + +extern void ax25_format_addrs(packet_t pp, char *); +extern void ax25_format_via_path(packet_t this_p, char *result, size_t result_size); + +extern int ax25_pack(packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]); + +extern ax25_frame_type_t ax25_frame_type(packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns); + +extern void ax25_hex_dump(packet_t this_p); + +extern int ax25_is_aprs(packet_t pp); +extern int ax25_is_null_frame(packet_t this_p); + +extern int ax25_get_control(packet_t this_p); +extern int ax25_get_c2(packet_t this_p); + +extern int ax25_get_pid(packet_t this_p); + +extern int ax25_get_frame_len(packet_t this_p); +extern unsigned char *ax25_get_frame_data_ptr(packet_t this_p); + +extern unsigned short ax25_dedupe_crc(packet_t pp); + +extern unsigned short ax25_m_m_crc(packet_t pp); + +extern void ax25_safe_print(char *, int, int ascii_only); + +#define AX25_ALEVEL_TO_TEXT_SIZE 40 // overkill but safe. +extern int ax25_alevel_to_text(alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE]); + + +#endif /* AX25_PAD_H */ + +/* end ax25_pad.h */ + + + + +#define CTAG_MIN 0x01 +#define CTAG_MAX 0x0B + +// Maximum sizes of "data" and "check" parts. + +#define FX25_MAX_DATA 239 // i.e. RS(255,239) +#define FX25_MAX_CHECK 64 // e.g. RS(255, 191) +#define FX25_BLOCK_SIZE 255 // Block size always 255 for 8 bit symbols. + +#endif // FX25_H + +#ifndef IL2P_H +#define IL2P_H 1 + + +#define IL2P_PREAMBLE 0x55 + +#define IL2P_SYNC_WORD 0xF15E48 + +#define IL2P_SYNC_WORD_SIZE 3 +#define IL2P_HEADER_SIZE 13 // Does not include 2 parity. +#define IL2P_HEADER_PARITY 2 + +#define IL2P_MAX_PAYLOAD_SIZE 1023 +#define IL2P_MAX_PAYLOAD_BLOCKS 5 +#define IL2P_MAX_PARITY_SYMBOLS 16 // For payload only. +#define IL2P_MAX_ENCODED_PAYLOAD_SIZE (IL2P_MAX_PAYLOAD_SIZE + IL2P_MAX_PAYLOAD_BLOCKS * IL2P_MAX_PARITY_SYMBOLS) + +#define IL2P_MAX_PACKET_SIZE (IL2P_SYNC_WORD_SIZE + IL2P_HEADER_SIZE + IL2P_HEADER_PARITY + IL2P_MAX_ENCODED_PAYLOAD_SIZE) + + +float GuessCentreFreq(int i) +{ + float Freq = 0; + float Start; + float End; + int n; + float Max = 0; + int Index = 0; + float BinSize = 12000.0 / FFTSize; + + Start = (rx_freq[i] - RCVR[i] * rcvr_offset[i]) / BinSize; + End = (rx_freq[i] + RCVR[i] * rcvr_offset[i]) / BinSize; + + Start = (active_rx_freq[i] - RCVR[i] * rcvr_offset[i]) / BinSize; + End = (active_rx_freq[i] + RCVR[i] * rcvr_offset[i]) / BinSize; + + + for (n = Start; n <= End; n++) + { + if (MagOut[n] > Max) + { + Max = MagOut[n]; + Index = n; + } + } + + Freq = Index * BinSize; + + return Freq; +} + +/*------------------------------------------------------------------------------ + * + * Name: ax25_new + * + * Purpose: Allocate memory for a new packet object. + * + * Returns: Identifier for a new packet object. + * In the current implementation this happens to be a pointer. + * + *------------------------------------------------------------------------------*/ + +int last_seq_num = 0; +int new_count = 0; +int delete_count = 0; + +packet_t ax25_new(void) +{ + struct packet_s *this_p; + + +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + Debugprintf("ax25_new(): before alloc, new=%d, delete=%d\n", new_count, delete_count); +#endif + + last_seq_num++; + new_count++; + + /* + * check for memory leak. + */ + + // version 1.4 push up the threshold. We could have considerably more with connected mode. + + //if (new_count > delete_count + 100) { + if (new_count > delete_count + 256) { + + Debugprintf("Report to WB2OSZ - Memory leak for packet objects. new=%d, delete=%d\n", new_count, delete_count); +#if AX25MEMDEBUG +#endif + } + + this_p = calloc(sizeof(struct packet_s), (size_t)1); + + if (this_p == NULL) { + Debugprintf("ERROR - can't allocate memory in ax25_new.\n"); + } + +// assert(this_p != NULL); + + this_p->magic1 = MAGIC; + this_p->seq = last_seq_num; + this_p->magic2 = MAGIC; + this_p->num_addr = (-1); + + return (this_p); +} + +/*------------------------------------------------------------------------------ + * + * Name: ax25_delete + * + * Purpose: Destroy a packet object, freeing up memory it was using. + * + *------------------------------------------------------------------------------*/ + +void ax25_delete(packet_t this_p) +{ + if (this_p == NULL) { + Debugprintf("ERROR - NULL pointer passed to ax25_delete.\n"); + return; + } + + delete_count++; + +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + this_p->magic1 = 0; + this_p->magic1 = 0; + + free(this_p); +} + + + + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_s_frame + * + * Purpose: Construct an S frame. + * + * Input: addrs - Array of addresses. + * + * num_addr - Number of addresses, range 2 .. 10. + * + * cr - cr_cmd command frame, cr_res for a response frame. + * + * ftype - One of: + * frame_type_S_RR, // Receive Ready - System Ready To Receive + * frame_type_S_RNR, // Receive Not Ready - TNC Buffer Full + * frame_type_S_REJ, // Reject Frame - Out of Sequence or Duplicate + * frame_type_S_SREJ, // Selective Reject - Request single frame repeat + * + * modulo - 8 or 128. Determines if we have 1 or 2 control bytes. + * + * nr - N(R) field --- describe. + * + * pf - Poll/Final flag. + * + * pinfo - Pointer to data for Info field. Allowed only for SREJ. + * + * info_len - Length for Info field. + * + * + * Returns: Pointer to new packet object. + * + *------------------------------------------------------------------------------*/ + + +packet_t ax25_s_frame(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, unsigned char *pinfo, int info_len) +{ + packet_t this_p; + unsigned char *p; + int ctrl = 0; + + this_p = ax25_new(); + + if (this_p == NULL) return (NULL); + + if (!set_addrs(this_p, addrs, num_addr, cr)) { + Debugprintf("Internal error in %s: Could not set addresses for S frame.\n", __func__); + ax25_delete(this_p); + return (NULL); + } + + if (modulo != 8 && modulo != 128) { + Debugprintf("Internal error in %s: Invalid modulo %d for S frame.\n", __func__, modulo); + modulo = 8; + } + this_p->modulo = modulo; + + if (nr < 0 || nr >= modulo) { + Debugprintf("Internal error in %s: Invalid N(R) %d for S frame.\n", __func__, nr); + nr &= (modulo - 1); + } + + // Erratum: The AX.25 spec is not clear about whether SREJ should be command, response, or both. + // The underlying X.25 spec clearly says it is response only. Let's go with that. + + if (ftype == frame_type_S_SREJ && cr != cr_res) { + Debugprintf("Internal error in %s: SREJ must be response.\n", __func__); + } + + switch (ftype) { + + case frame_type_S_RR: ctrl = 0x01; break; + case frame_type_S_RNR: ctrl = 0x05; break; + case frame_type_S_REJ: ctrl = 0x09; break; + case frame_type_S_SREJ: ctrl = 0x0d; break; + + default: + Debugprintf("Internal error in %s: Invalid ftype %d for S frame.\n", __func__, ftype); + ax25_delete(this_p); + return (NULL); + break; + } + + p = this_p->frame_data + this_p->frame_len; + + if (modulo == 8) { + if (pf) ctrl |= 0x10; + ctrl |= nr << 5; + *p++ = ctrl; + this_p->frame_len++; + } + else { + *p++ = ctrl; + this_p->frame_len++; + + ctrl = pf & 1; + ctrl |= nr << 1; + *p++ = ctrl; + this_p->frame_len++; + } + + if (ftype == frame_type_S_SREJ) { + if (pinfo != NULL && info_len > 0) { + if (info_len > AX25_MAX_INFO_LEN) { + Debugprintf("Internal error in %s: SREJ frame, Invalid information field length %d.\n", __func__, info_len); + info_len = AX25_MAX_INFO_LEN; + } + memcpy(p, pinfo, info_len); + p += info_len; + this_p->frame_len += info_len; + } + } + else { + if (pinfo != NULL || info_len != 0) { + Debugprintf("Internal error in %s: Info part not allowed for RR, RNR, REJ frame.\n", __func__); + } + } + *p = '\0'; + + + return (this_p); + +} /* end ax25_s_frame */ + + + + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_i_frame + * + * Purpose: Construct an I frame. + * + * Input: addrs - Array of addresses. + * + * num_addr - Number of addresses, range 2 .. 10. + * + * cr - cr_cmd command frame, cr_res for a response frame. + * + * modulo - 8 or 128. + * + * nr - N(R) field --- describe. + * + * ns - N(S) field --- describe. + * + * pf - Poll/Final flag. + * + * pid - Protocol ID. + * Normally 0xf0 meaning no level 3. + * Could be other values for NET/ROM, etc. + * + * pinfo - Pointer to data for Info field. + * + * info_len - Length for Info field. + * + * + * Returns: Pointer to new packet object. + * + *------------------------------------------------------------------------------*/ + +packet_t ax25_i_frame(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len) +{ + packet_t this_p; + unsigned char *p; + int ctrl = 0; + + this_p = ax25_new(); + + if (this_p == NULL) return (NULL); + + if (!set_addrs(this_p, addrs, num_addr, cr)) { + Debugprintf("Internal error in %s: Could not set addresses for I frame.\n", __func__); + ax25_delete(this_p); + return (NULL); + } + + if (modulo != 8 && modulo != 128) { + Debugprintf("Internal error in %s: Invalid modulo %d for I frame.\n", __func__, modulo); + modulo = 8; + } + this_p->modulo = modulo; + + if (nr < 0 || nr >= modulo) { + Debugprintf("Internal error in %s: Invalid N(R) %d for I frame.\n", __func__, nr); + nr &= (modulo - 1); + } + + if (ns < 0 || ns >= modulo) { + Debugprintf("Internal error in %s: Invalid N(S) %d for I frame.\n", __func__, ns); + ns &= (modulo - 1); + } + + p = this_p->frame_data + this_p->frame_len; + + if (modulo == 8) { + ctrl = (nr << 5) | (ns << 1); + if (pf) ctrl |= 0x10; + *p++ = ctrl; + this_p->frame_len++; + } + else { + ctrl = ns << 1; + *p++ = ctrl; + this_p->frame_len++; + + ctrl = nr << 1; + if (pf) ctrl |= 0x01; + *p++ = ctrl; + this_p->frame_len++; + } + + // Definitely don't want pid value of 0 (not in valid list) + // or 0xff (which means more bytes follow). + + if (pid < 0 || pid == 0 || pid == 0xff) { + Debugprintf("Warning: Client application provided invalid PID value, 0x%02x, for I frame.\n", pid); + pid = AX25_PID_NO_LAYER_3; + } + *p++ = pid; + this_p->frame_len++; + + if (pinfo != NULL && info_len > 0) { + if (info_len > AX25_MAX_INFO_LEN) { + Debugprintf("Internal error in %s: I frame, Invalid information field length %d.\n", __func__, info_len); + info_len = AX25_MAX_INFO_LEN; + } + memcpy(p, pinfo, info_len); + p += info_len; + this_p->frame_len += info_len; + } + + *p = '\0'; + + + return (this_p); + +} /* end ax25_i_frame */ + + + + + +extern TStringList detect_list[5]; +extern TStringList detect_list_c[5]; + +void multi_modem_process_rec_packet(int snd_ch, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25, int emph, int centreFreq) +{ + // Convert to QtSM internal format + + struct TDetector_t * pDET = &DET[emph][subchan]; + string * data = newString(); + char Mode[16] = "IL2P"; + + sprintf(Mode, "IL2P %d", centreFreq); + + stringAdd(data, pp->frame_data, pp->frame_len + 2); // QTSM assumes a CRC + + ax25_delete(pp); + + if (retries) + { + pDET->rx_decoded = decodedFEC; + pDET->emph_decoded = decodedFEC; + pDET->errors = retries; + } + else + { + pDET->rx_decoded = decodedNormal; + pDET->emph_decoded = decodedNormal; + pDET->errors = 0; + } + + if (detect_list[snd_ch].Count > 0 && + my_indexof(&detect_list[snd_ch], data) >= 0) + { + // Already have a copy of this frame + + freeString(data); + Debugprintf("Discarding copy rcvr %d emph %d", subchan, 0); + return; + } + + string * xx = newString(); + memset(xx->Data, 0, 16); + + Add(&detect_list_c[snd_ch], xx); + Add(&detect_list[snd_ch], data); + +// if (retries) +// sprintf(Mode, "IP2P-%d", retries); + + stringAdd(xx, Mode, strlen(Mode)); + return; + +} + + + + +alevel_t demod_get_audio_level(int chan, int subchan) +{ + alevel_t alevel; + alevel.rec = 0; + alevel.mark = 0; + alevel.space = 0; + return (alevel); +} + +void ax25_hex_dump(packet_t this_p) +{} + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_from_frame + * + * Purpose: Split apart an HDLC frame to components. + * + * Inputs: fbuf - Pointer to beginning of frame. + * + * flen - Length excluding the two FCS bytes. + * + * alevel - Audio level of received signal. + * Maximum range 0 - 100. + * -1 might be used when not applicable. + * + * Returns: Pointer to new packet object or NULL if error. + * + * Outputs: Use the "get" functions to retrieve information in different ways. + * + *------------------------------------------------------------------------------*/ + + +packet_t ax25_from_frame(unsigned char *fbuf, int flen, alevel_t alevel) +{ + packet_t this_p; + + + /* + * First make sure we have an acceptable length: + * + * We are not concerned with the FCS (CRC) because someone else checked it. + * + * Is is possible to have zero length for info? + * + * In the original version, assuming APRS, the answer was no. + * We always had at least 3 octets after the address part: + * control, protocol, and first byte of info part for data type. + * + * In later versions, this restriction was relaxed so other + * variations of AX.25 could be used. Now the minimum length + * is 7+7 for addresses plus 1 for control. + * + */ + + + if (flen < AX25_MIN_PACKET_LEN || flen > AX25_MAX_PACKET_LEN) + { + Debugprintf("Frame length %d not in allowable range of %d to %d.", flen, AX25_MIN_PACKET_LEN, AX25_MAX_PACKET_LEN); + return (NULL); + } + + this_p = ax25_new(); + + /* Copy the whole thing intact. */ + + memcpy(this_p->frame_data, fbuf, flen); + this_p->frame_data[flen] = 0; + this_p->frame_len = flen; + + /* Find number of addresses. */ + + this_p->num_addr = (-1); + (void)ax25_get_num_addr(this_p); + + return (this_p); +} + + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_get_num_addr + * + * Purpose: Return number of addresses in current packet. + * + * Assumption: ax25_from_text or ax25_from_frame was called first. + * + * Returns: Number of addresses in the current packet. + * Should be in the range of 2 .. AX25_MAX_ADDRS. + * + * Version 0.9: Could be zero for a non AX.25 frame in KISS mode. + * + *------------------------------------------------------------------------------*/ + +int ax25_get_num_addr(packet_t this_p) +{ + //unsigned char *pf; + int a; + int addr_bytes; + + +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + /* Use cached value if already set. */ + + if (this_p->num_addr >= 0) { + return (this_p->num_addr); + } + + /* Otherwise, determine the number ofaddresses. */ + + this_p->num_addr = 0; /* Number of addresses extracted. */ + + addr_bytes = 0; + for (a = 0; a < this_p->frame_len && addr_bytes == 0; a++) { + if (this_p->frame_data[a] & SSID_LAST_MASK) { + addr_bytes = a + 1; + } + } + + if (addr_bytes % 7 == 0) { + int addrs = addr_bytes / 7; + if (addrs >= AX25_MIN_ADDRS && addrs <= AX25_MAX_ADDRS) { + this_p->num_addr = addrs; + } + } + + return (this_p->num_addr); +} + + + +void ax25_get_addr_with_ssid(packet_t pp, int n, char *station) +{} + +/*------------------------------------------------------------------------------ + * + * Name: ax25_get_addr_no_ssid + * + * Purpose: Return specified address WITHOUT any SSID. + * + * Inputs: n - Index of address. Use the symbols + * AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc. + * + * Outputs: station - String representation of the station, WITHOUT the SSID. + * e.g. "WB2OSZ" + * Usually variables will be AX25_MAX_ADDR_LEN bytes + * but 7 would be adequate. + * + * Bugs: No bounds checking is performed. Be careful. + * + * Assumption: ax25_from_text or ax25_from_frame was called first. + * + * Returns: Character string in usual human readable format, + * + * + *------------------------------------------------------------------------------*/ + +void ax25_get_addr_no_ssid(packet_t this_p, int n, char *station) +{ + int i; + + //assert(this_p->magic1 == MAGIC); + //assert(this_p->magic2 == MAGIC); + + + if (n < 0) { + Debugprintf("Internal error detected in ax25_get_addr_no_ssid, %s, line %d.\n", __FILE__, __LINE__); + Debugprintf("Address index, %d, is less than zero.\n", n); + strcpy(station, "??????"); + return; + } + + if (n >= this_p->num_addr) { + Debugprintf("Internal error detected in ax25_get_no_with_ssid, %s, line %d.\n", __FILE__, __LINE__); + Debugprintf("Address index, %d, is too large for number of addresses, %d.\n", n, this_p->num_addr); + strcpy(station, "??????"); + return; + } + + // At one time this would stop at the first space, on the assumption we would have only trailing spaces. + // Then there was a forum discussion where someone encountered the address " WIDE2" with a leading space. + // In that case, we would have returned a zero length string here. + // Now we return exactly what is in the address field and trim trailing spaces. + // This will provide better information for troubleshooting. + + for (i = 0; i < 6; i++) { + station[i] = (this_p->frame_data[n * 7 + i] >> 1) & 0x7f; + } + station[6] = '\0'; + + for (i = 5; i >= 0; i--) { + if (station[i] == ' ') + station[i] = '\0'; + else + break; + } + + if (strlen(station) == 0) { + Debugprintf("Station address, in position %d, is empty! This is not a valid AX.25 frame.\n", n); + } + +} /* end ax25_get_addr_no_ssid */ + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_get_ssid + * + * Purpose: Return SSID of specified address in current packet. + * + * Inputs: n - Index of address. Use the symbols + * AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc. + * + * Assumption: ax25_from_text or ax25_from_frame was called first. + * + * Returns: Substation id, as integer 0 .. 15. + * + *------------------------------------------------------------------------------*/ + +int ax25_get_ssid(packet_t this_p, int n) +{ + +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + if (n >= 0 && n < this_p->num_addr) { + return ((this_p->frame_data[n * 7 + 6] & SSID_SSID_MASK) >> SSID_SSID_SHIFT); + } + else { + Debugprintf("Internal error: ax25_get_ssid(%d), num_addr=%d\n", n, this_p->num_addr); + return (0); + } +} + + + +static inline int ax25_get_pid_offset(packet_t this_p) +{ + return (ax25_get_control_offset(this_p) + ax25_get_num_control(this_p)); +} + +static int ax25_get_num_pid(packet_t this_p) +{ + int c; + int pid; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0 || /* I xxxx xxx0 */ + c == 0x03 || c == 0x13) { /* UI 000x 0011 */ + + pid = this_p->frame_data[ax25_get_pid_offset(this_p)]; + if (pid == AX25_PID_ESCAPE_CHARACTER) { + return (2); /* pid 1111 1111 means another follows. */ + } + return (1); + } + return (0); +} + + +inline int ax25_get_control_offset(packet_t this_p) +{ + return (this_p->num_addr * 7); +} + +inline int ax25_get_num_control(packet_t this_p) +{ + int c; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0) { /* I xxxx xxx0 */ + return ((this_p->modulo == 128) ? 2 : 1); + } + + if ((c & 0x03) == 1) { /* S xxxx xx01 */ + return ((this_p->modulo == 128) ? 2 : 1); + } + + return (1); /* U xxxx xx11 */ +} + + + + +int ax25_get_info_offset(packet_t this_p) +{ + int offset = ax25_get_control_offset(this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p); + return (offset); +} + +int ax25_get_num_info(packet_t this_p) +{ + int len; + + /* assuming AX.25 frame. */ + + len = this_p->frame_len - this_p->num_addr * 7 - ax25_get_num_control(this_p) - ax25_get_num_pid(this_p); + if (len < 0) { + len = 0; /* print error? */ + } + + return (len); +} + + + + + + /*------------------------------------------------------------------------------ + * + * Name: ax25_get_info + * + * Purpose: Obtain Information part of current packet. + * + * Inputs: this_p - Packet object pointer. + * + * Outputs: paddr - Starting address of information part is returned here. + * + * Assumption: ax25_from_text or ax25_from_frame was called first. + * + * Returns: Number of octets in the Information part. + * Should be in the range of AX25_MIN_INFO_LEN .. AX25_MAX_INFO_LEN. + * + *------------------------------------------------------------------------------*/ + +int ax25_get_info(packet_t this_p, unsigned char **paddr) +{ + unsigned char *info_ptr; + int info_len; + + + //assert(this_p->magic1 == MAGIC); + //assert(this_p->magic2 == MAGIC); + + if (this_p->num_addr >= 2) { + + /* AX.25 */ + + info_ptr = this_p->frame_data + ax25_get_info_offset(this_p); + info_len = ax25_get_num_info(this_p); + } + else { + + /* Not AX.25. Treat Whole packet as info. */ + + info_ptr = this_p->frame_data; + info_len = this_p->frame_len; + } + + /* Add nul character in case caller treats as printable string. */ + +// assert(info_len >= 0); + + info_ptr[info_len] = '\0'; + + *paddr = info_ptr; + return (info_len); + +} /* end ax25_get_info */ + + + + +void ax25_set_info(packet_t this_p, unsigned char *new_info_ptr, int new_info_len) +{ + unsigned char *old_info_ptr; + int old_info_len = ax25_get_info(this_p, &old_info_ptr); + this_p->frame_len -= old_info_len; + + if (new_info_len < 0) new_info_len = 0; + if (new_info_len > AX25_MAX_INFO_LEN) new_info_len = AX25_MAX_INFO_LEN; + memcpy(old_info_ptr, new_info_ptr, new_info_len); + this_p->frame_len += new_info_len; +} + +int ax25_get_pid(packet_t this_p) +{ +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + // TODO: handle 2 control byte case. + // TODO: sanity check: is it I or UI frame? + + if (this_p->frame_len == 0) return(-1); + + if (this_p->num_addr >= 2) { + return (this_p->frame_data[ax25_get_pid_offset(this_p)]); + } + return (-1); +} + + +int ax25_get_frame_len(packet_t this_p) +{ +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + +// assert(this_p->frame_len >= 0 && this_p->frame_len <= AX25_MAX_PACKET_LEN); + + return (this_p->frame_len); + +} /* end ax25_get_frame_len */ + + +unsigned char *ax25_get_frame_data_ptr(packet_t this_p) +{ +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + return (this_p->frame_data); + +} /* end ax25_get_frame_data_ptr */ + + +int ax25_get_modulo(packet_t this_p) +{ + return 7; +} + + +/*------------------------------------------------------------------ + * + * Function: ax25_get_control + ax25_get_c2 + * + * Purpose: Get Control field from packet. + * + * Inputs: this_p - pointer to packet object. + * + * Returns: APRS uses AX25_UI_FRAME. + * This could also be used in other situations. + * + *------------------------------------------------------------------*/ + + +int ax25_get_control(packet_t this_p) +{ +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + if (this_p->frame_len == 0) return(-1); + + if (this_p->num_addr >= 2) { + return (this_p->frame_data[ax25_get_control_offset(this_p)]); + } + return (-1); +} + + +/*------------------------------------------------------------------ +* +* Function: ax25_frame_type +* +* Purpose : Extract the type of frame. +* This is derived from the control byte(s) but +* is an enumerated type for easier handling. +* +* Inputs : this_p - pointer to packet object. +* +* Outputs : desc - Text description such as "I frame" or +*"U frame SABME". +* Supply 56 bytes to be safe. +* +* cr - Command or response ? +* +* pf - P / F - Poll / Final or -1 if not applicable +* +* nr - N(R) - receive sequence or -1 if not applicable. +* +* ns - N(S) - send sequence or -1 if not applicable. +* +* Returns: Frame type from enum ax25_frame_type_e. +* +*------------------------------------------------------------------*/ + +// TODO: need someway to ensure caller allocated enough space. +// Should pass in as parameter. + +#define DESC_SIZ 56 + + +ax25_frame_type_t ax25_frame_type(packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns) +{ + int c; // U frames are always one control byte. + int c2 = 0; // I & S frames can have second Control byte. + +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + + strcpy(desc, "????"); + *cr = cr_11; + *pf = -1; + *nr = -1; + *ns = -1; + + c = ax25_get_control(this_p); + if (c < 0) { + strcpy(desc, "Not AX.25"); + return (frame_not_AX25); + } + + /* + * TERRIBLE HACK :-( for display purposes. + * + * I and S frames can have 1 or 2 control bytes but there is + * no good way to determine this without dipping into the data + * link state machine. Can we guess? + * + * S frames have no protocol id or information so if there is one + * more byte beyond the control field, we could assume there are + * two control bytes. + * + * For I frames, the protocol id will usually be 0xf0. If we find + * that as the first byte of the information field, it is probably + * the pid and not part of the information. Ditto for segments 0x08. + * Not fool proof but good enough for troubleshooting text out. + * + * If we have a link to the peer station, this will be set properly + * before it needs to be used for other reasons. + * + * Setting one of the RR bits (find reference!) is sounding better and better. + * It's in common usage so I should lobby to get that in the official protocol spec. + */ + + // Dont support mod 128 +/* + if (this_p->modulo == 0 && (c & 3) == 1 && ax25_get_c2(this_p) != -1) { + this_p->modulo = modulo_128; + } + else if (this_p->modulo == 0 && (c & 1) == 0 && this_p->frame_data[ax25_get_info_offset(this_p)] == 0xF0) { + this_p->modulo = modulo_128; + } + else if (this_p->modulo == 0 && (c & 1) == 0 && this_p->frame_data[ax25_get_info_offset(this_p)] == 0x08) { // same for segments + this_p->modulo = modulo_128; + } + + + if (this_p->modulo == modulo_128) { + c2 = ax25_get_c2(this_p); + } +*/ + + int dst_c = this_p->frame_data[AX25_DESTINATION * 7 + 6] & SSID_H_MASK; + int src_c = this_p->frame_data[AX25_SOURCE * 7 + 6] & SSID_H_MASK; + + char cr_text[8]; + char pf_text[8]; + + if (dst_c) { + if (src_c) { *cr = cr_11; strcpy(cr_text, "cc=11"); strcpy(pf_text, "p/f"); } + else { *cr = cr_cmd; strcpy(cr_text, "cmd"); strcpy(pf_text, "p"); } + } + else { + if (src_c) { *cr = cr_res; strcpy(cr_text, "res"); strcpy(pf_text, "f"); } + else { *cr = cr_00; strcpy(cr_text, "cc=00"); strcpy(pf_text, "p/f"); } + } + + if ((c & 1) == 0) { + + // Information rrr p sss 0 or sssssss 0 rrrrrrr p + + if (this_p->modulo == modulo_128) { + *ns = (c >> 1) & 0x7f; + *pf = c2 & 1; + *nr = (c2 >> 1) & 0x7f; + } + else { + *ns = (c >> 1) & 7; + *pf = (c >> 4) & 1; + *nr = (c >> 5) & 7; + } + + //snprintf (desc, DESC_SIZ, "I %s, n(s)=%d, n(r)=%d, %s=%d", cr_text, *ns, *nr, pf_text, *pf); + sprintf(desc, "I %s, n(s)=%d, n(r)=%d, %s=%d, pid=0x%02x", cr_text, *ns, *nr, pf_text, *pf, ax25_get_pid(this_p)); + return (frame_type_I); + } + else if ((c & 2) == 0) { + + // Supervisory rrr p/f ss 0 1 or 0000 ss 0 1 rrrrrrr p/f + + if (this_p->modulo == modulo_128) { + *pf = c2 & 1; + *nr = (c2 >> 1) & 0x7f; + } + else { + *pf = (c >> 4) & 1; + *nr = (c >> 5) & 7; + } + + + switch ((c >> 2) & 3) { + case 0: sprintf(desc, "RR %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_RR); break; + case 1: sprintf(desc, "RNR %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_RNR); break; + case 2: sprintf(desc, "REJ %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_REJ); break; + case 3: sprintf(desc, "SREJ %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_SREJ); break; + } + } + else { + + // Unnumbered mmm p/f mm 1 1 + + *pf = (c >> 4) & 1; + + switch (c & 0xef) { + + case 0x6f: sprintf(desc, "SABME %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_SABME); break; + case 0x2f: sprintf(desc, "SABM %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_SABM); break; + case 0x43: sprintf(desc, "DISC %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_DISC); break; + case 0x0f: sprintf(desc, "DM %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_DM); break; + case 0x63: sprintf(desc, "UA %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_UA); break; + case 0x87: sprintf(desc, "FRMR %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_FRMR); break; + case 0x03: sprintf(desc, "UI %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_UI); break; + case 0xaf: sprintf(desc, "XID %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_XID); break; + case 0xe3: sprintf(desc, "TEST %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_TEST); break; + default: sprintf(desc, "U other???"); return (frame_type_U); break; + } + } + + // Should be unreachable but compiler doesn't realize that. + // Here only to suppress "warning: control reaches end of non-void function" + + return (frame_not_AX25); + +} /* end ax25_frame_type */ + + + +packet_t ax25_u_frame(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len) +{ + packet_t this_p; + unsigned char *p; + int ctrl = 0; + unsigned int t = 999; // 1 = must be cmd, 0 = must be response, 2 = can be either. + int i = 0; // Is Info part allowed? + + this_p = ax25_new(); + + if (this_p == NULL) return (NULL); + + this_p->modulo = 0; + + if (!set_addrs(this_p, addrs, num_addr, cr)) { + Debugprintf("Internal error in %s: Could not set addresses for U frame.\n", __func__); + ax25_delete(this_p); + return (NULL); + } + + switch (ftype) { + // 1 = cmd only, 0 = res only, 2 = either + case frame_type_U_SABME: ctrl = 0x6f; t = 1; break; + case frame_type_U_SABM: ctrl = 0x2f; t = 1; break; + case frame_type_U_DISC: ctrl = 0x43; t = 1; break; + case frame_type_U_DM: ctrl = 0x0f; t = 0; break; + case frame_type_U_UA: ctrl = 0x63; t = 0; break; + case frame_type_U_FRMR: ctrl = 0x87; t = 0; i = 1; break; + case frame_type_U_UI: ctrl = 0x03; t = 2; i = 1; break; + case frame_type_U_XID: ctrl = 0xaf; t = 2; i = 1; break; + case frame_type_U_TEST: ctrl = 0xe3; t = 2; i = 1; break; + + default: + Debugprintf("Internal error in %s: Invalid ftype %d for U frame.\n", __func__, ftype); + ax25_delete(this_p); + return (NULL); + break; + } + if (pf) ctrl |= 0x10; + + if (t != 2) { + if (cr != t) { + Debugprintf("Internal error in %s: U frame, cr is %d but must be %d. ftype=%d\n", __func__, cr, t, ftype); + } + } + + p = this_p->frame_data + this_p->frame_len; + *p++ = ctrl; + this_p->frame_len++; + + if (ftype == frame_type_U_UI) { + + // Definitely don't want pid value of 0 (not in valid list) + // or 0xff (which means more bytes follow). + + if (pid < 0 || pid == 0 || pid == 0xff) { + Debugprintf("Internal error in %s: U frame, Invalid pid value 0x%02x.\n", __func__, pid); + pid = AX25_PID_NO_LAYER_3; + } + *p++ = pid; + this_p->frame_len++; + } + + if (i) { + if (pinfo != NULL && info_len > 0) { + if (info_len > AX25_MAX_INFO_LEN) { + + Debugprintf("Internal error in %s: U frame, Invalid information field length %d.\n", __func__, info_len); + info_len = AX25_MAX_INFO_LEN; + } + memcpy(p, pinfo, info_len); + p += info_len; + this_p->frame_len += info_len; + } + } + else { + if (pinfo != NULL && info_len > 0) { + Debugprintf("Internal error in %s: Info part not allowed for U frame type.\n", __func__); + } + } + *p = '\0'; + + //assert(p == this_p->frame_data + this_p->frame_len); + //assert(this_p->magic1 == MAGIC); + //assert(this_p->magic2 == MAGIC); + +#if PAD2TEST + ax25_frame_type_t check_ftype; + cmdres_t check_cr; + char check_desc[80]; + int check_pf; + int check_nr; + int check_ns; + + check_ftype = ax25_frame_type(this_p, &check_cr, check_desc, &check_pf, &check_nr, &check_ns); + + text_color_set(DW_COLOR_DEBUG); + Debugprintf("check: ftype=%d, desc=\"%s\", pf=%d\n", check_ftype, check_desc, check_pf); + + assert(check_cr == cr); + assert(check_ftype == ftype); + assert(check_pf == pf); + assert(check_nr == -1); + assert(check_ns == -1); + +#endif + + return (this_p); + +} /* end ax25_u_frame */ + + + + + + +static const char *position_name[1 + AX25_MAX_ADDRS] = { + "", "Destination ", "Source ", + "Digi1 ", "Digi2 ", "Digi3 ", "Digi4 ", + "Digi5 ", "Digi6 ", "Digi7 ", "Digi8 " }; + +int ax25_parse_addr(int position, char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard) +{ + char *p; + char sstr[8]; /* Should be 1 or 2 digits for SSID. */ + int i, j, k; + int maxlen; + + *out_addr = '\0'; + *out_ssid = 0; + *out_heard = 0; + + // Debugprintf ("ax25_parse_addr in: position=%d, '%s', strict=%d\n", position, in_addr, strict); + + if (position < -1) position = -1; + if (position > AX25_REPEATER_8) position = AX25_REPEATER_8; + position++; /* Adjust for position_name above. */ + + if (strlen(in_addr) == 0) { + Debugprintf("%sAddress \"%s\" is empty.\n", position_name[position], in_addr); + return 0; + } + + if (strict && strlen(in_addr) >= 2 && strncmp(in_addr, "qA", 2) == 0) { + + Debugprintf("%sAddress \"%s\" is a \"q-construct\" used for communicating with\n", position_name[position], in_addr); + Debugprintf("APRS Internet Servers. It should never appear when going over the radio.\n"); + } + + // Debugprintf ("ax25_parse_addr in: %s\n", in_addr); + + maxlen = strict ? 6 : (AX25_MAX_ADDR_LEN - 1); + p = in_addr; + i = 0; + for (p = in_addr; *p != '\0' && *p != '-' && *p != '*'; p++) { + if (i >= maxlen) { + Debugprintf("%sAddress is too long. \"%s\" has more than %d characters.\n", position_name[position], in_addr, maxlen); + return 0; + } + if (!isalnum(*p)) { + Debugprintf("%sAddress, \"%s\" contains character other than letter or digit in character position %d.\n", position_name[position], in_addr, (int)(long)(p - in_addr) + 1); + return 0; + } + + out_addr[i++] = *p; + out_addr[i] = '\0'; + +#if DECAMAIN // Hack when running in decode_aprs utility. + // Exempt the "qA..." case because it was already mentioned. + + if (strict && islower(*p) && strncmp(in_addr, "qA", 2) != 0) { + text_color_set(DW_COLOR_ERROR); + Debugprintf("%sAddress has lower case letters. \"%s\" must be all upper case.\n", position_name[position], in_addr); + } +#else + if (strict && islower(*p)) { + Debugprintf("%sAddress has lower case letters. \"%s\" must be all upper case.\n", position_name[position], in_addr); + return 0; + } +#endif + } + + j = 0; + sstr[j] = '\0'; + if (*p == '-') { + for (p++; isalnum(*p); p++) { + if (j >= 2) { + Debugprintf("%sSSID is too long. SSID part of \"%s\" has more than 2 characters.\n", position_name[position], in_addr); + return 0; + } + sstr[j++] = *p; + sstr[j] = '\0'; + if (strict && !isdigit(*p)) { + Debugprintf("%sSSID must be digits. \"%s\" has letters in SSID.\n", position_name[position], in_addr); + return 0; + } + } + k = atoi(sstr); + if (k < 0 || k > 15) { + Debugprintf("%sSSID out of range. SSID of \"%s\" not in range of 0 to 15.\n", position_name[position], in_addr); + return 0; + } + *out_ssid = k; + } + + if (*p == '*') { + *out_heard = 1; + p++; + if (strict == 2) { + Debugprintf("\"*\" is not allowed at end of address \"%s\" here.\n", in_addr); + return 0; + } + } + + if (*p != '\0') { + Debugprintf("Invalid character \"%c\" found in %saddress \"%s\".\n", *p, position_name[position], in_addr); + return 0; + } + + // Debugprintf ("ax25_parse_addr out: '%s' %d %d\n", out_addr, *out_ssid, *out_heard); + + return (1); + +} /* end ax25_parse_addr */ + + + +int set_addrs(packet_t pp, char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr) +{ + int n; + + //assert(pp->frame_len == 0); + //assert(cr == cr_cmd || cr == cr_res); + + if (num_addr < AX25_MIN_ADDRS || num_addr > AX25_MAX_ADDRS) { + Debugprintf("INTERNAL ERROR: %s %s %d, num_addr = %d\n", __FILE__, __func__, __LINE__, num_addr); + return (0); + } + + for (n = 0; n < num_addr; n++) { + + unsigned char *pa = pp->frame_data + n * 7; + int ok; + int strict = 1; + char oaddr[AX25_MAX_ADDR_LEN]; + int ssid; + int heard; + int j; + + ok = ax25_parse_addr(n, addrs[n], strict, oaddr, &ssid, &heard); + + if (!ok) return (0); + + // Fill in address. + + memset(pa, ' ' << 1, 6); + for (j = 0; oaddr[j]; j++) { + pa[j] = oaddr[j] << 1; + } + pa += 6; + + // Fill in SSID. + + *pa = 0x60 | ((ssid & 0xf) << 1); + + // Command / response flag. + + switch (n) { + case AX25_DESTINATION: + if (cr == cr_cmd) *pa |= 0x80; + break; + case AX25_SOURCE: + if (cr == cr_res) *pa |= 0x80; + break; + default: + break; + } + + // Is this the end of address field? + + if (n == num_addr - 1) { + *pa |= 1; + } + + pp->frame_len += 7; + } + + pp->num_addr = num_addr; + return (1); + +} /* end set_addrs */ + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_init.c +// +/////////////////////////////////////////////////////////////////////////////// + + +// Init must be called at start of application. + +extern void il2p_init(int debug); + +extern struct rs *il2p_find_rs(int nparity); // Internal later? + +extern void il2p_encode_rs(unsigned char *tx_data, int data_size, int num_parity, unsigned char *parity_out); + +extern int il2p_decode_rs(unsigned char *rec_block, int data_size, int num_parity, unsigned char *out); + +extern int il2p_get_debug(void); +extern void il2p_set_debug(int debug); + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_rec.c +// +/////////////////////////////////////////////////////////////////////////////// + +// Receives a bit stream from demodulator. + +extern void il2p_rec_bit(int chan, int subchan, int slice, int dbit); + + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_send.c +// +/////////////////////////////////////////////////////////////////////////////// + + +// Send bit stream to modulator. + +string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity); + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_codec.c +// +/////////////////////////////////////////////////////////////////////////////// + + +extern int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout); + +packet_t il2p_decode_frame(unsigned char *irec); + +packet_t il2p_decode_header_payload(unsigned char* uhdr, unsigned char *epayload, int *symbols_corrected); + + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_header.c +// +/////////////////////////////////////////////////////////////////////////////// + + +extern int il2p_type_1_header(packet_t pp, int max_fec, unsigned char *hdr); + +extern packet_t il2p_decode_header_type_1(unsigned char *hdr, int num_sym_changed); + + +extern int il2p_type_0_header(packet_t pp, int max_fec, unsigned char *hdr); + +extern int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_descrambled_hdr); + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_scramble.c +// +/////////////////////////////////////////////////////////////////////////////// + +extern void il2p_scramble_block(unsigned char *in, unsigned char *out, int len); + +extern void il2p_descramble_block(unsigned char *in, unsigned char *out, int len); + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_payload.c +// +/////////////////////////////////////////////////////////////////////////////// + + +typedef struct { + int payload_byte_count; // Total size, 0 thru 1023 + int payload_block_count; + int small_block_size; + int large_block_size; + int large_block_count; + int small_block_count; + int parity_symbols_per_block; // 2, 4, 6, 8, 16 +} il2p_payload_properties_t; + +extern int il2p_payload_compute(il2p_payload_properties_t *p, int payload_size, int max_fec); + +extern int il2p_encode_payload(unsigned char *payload, int payload_size, int max_fec, unsigned char *enc); + +extern int il2p_decode_payload(unsigned char *received, int payload_size, int max_fec, unsigned char *payload_out, int *symbols_corrected); + +extern int il2p_get_header_attributes(unsigned char *hdr, int *hdr_type, int *max_fec); + +#endif + + + +// Interesting related stuff: +// https://www.kernel.org/doc/html/v4.15/core-api/librs.html +// https://berthub.eu/articles/posts/reed-solomon-for-programmers/ + + +#define MAX_NROOTS 16 + +#define NTAB 5 + +static struct { + int symsize; // Symbol size, bits (1-8). Always 8 for this application. + int genpoly; // Field generator polynomial coefficients. + int fcs; // First root of RS code generator polynomial, index form. + // FX.25 uses 1 but IL2P uses 0. + int prim; // Primitive element to generate polynomial roots. + int nroots; // RS code generator polynomial degree (number of roots). + // Same as number of check bytes added. + struct rs *rs; // Pointer to RS codec control block. Filled in at init time. +} Tab[NTAB] = { + {8, 0x11d, 0, 1, 2, NULL }, // 2 parity + {8, 0x11d, 0, 1, 4, NULL }, // 4 parity + {8, 0x11d, 0, 1, 6, NULL }, // 6 parity + {8, 0x11d, 0, 1, 8, NULL }, // 8 parity + {8, 0x11d, 0, 1, 16, NULL }, // 16 parity +}; + + + +static int g_il2p_debug = 0; + + +/*------------------------------------------------------------- + * + * Name: il2p_init + * + * Purpose: This must be called at application start up time. + * It sets up tables for the Reed-Solomon functions. + * + * Inputs: debug - Enable debug output. + * + *--------------------------------------------------------------*/ + +void il2p_init(int il2p_debug) +{ + g_il2p_debug = il2p_debug; + + for (int i = 0; i < NTAB; i++) { + //assert(Tab[i].nroots <= MAX_NROOTS); + Tab[i].rs = INIT_RS(Tab[i].symsize, Tab[i].genpoly, Tab[i].fcs, Tab[i].prim, Tab[i].nroots); + if (Tab[i].rs == NULL) { + Debugprintf("IL2P internal error: init_rs_char failed!\n"); + exit(0); + } + } + +} // end il2p_init + + +int il2p_get_debug(void) +{ + return (g_il2p_debug); +} +void il2p_set_debug(int debug) +{ + g_il2p_debug = debug; +} + + +// Find RS codec control block for specified number of parity symbols. + +struct rs *il2p_find_rs(int nparity) +{ + for (int n = 0; n < NTAB; n++) { + if (Tab[n].nroots == nparity) { + return (Tab[n].rs); + } + } + Debugprintf("IL2P INTERNAL ERROR: il2p_find_rs: control block not found for nparity = %d.\n", nparity); + return (Tab[0].rs); +} + + +/*------------------------------------------------------------- + * + * Name: void il2p_encode_rs + * + * Purpose: Add parity symbols to a block of data. + * + * Inputs: tx_data Header or other data to transmit. + * data_size Number of data bytes in above. + * num_parity Number of parity symbols to add. + * Maximum of IL2P_MAX_PARITY_SYMBOLS. + * + * Outputs: parity_out Specified number of parity symbols + * + * Restriction: data_size + num_parity <= 255 which is the RS block size. + * The caller must ensure this. + * + *--------------------------------------------------------------*/ + +void il2p_encode_rs(unsigned char *tx_data, int data_size, int num_parity, unsigned char *parity_out) +{ + //assert(data_size >= 1); + //assert(num_parity == 2 || num_parity == 4 || num_parity == 6 || num_parity == 8 || num_parity == 16); + //assert(data_size + num_parity <= 255); + + unsigned char rs_block[FX25_BLOCK_SIZE]; + memset(rs_block, 0, sizeof(rs_block)); + memcpy(rs_block + sizeof(rs_block) - data_size - num_parity, tx_data, data_size); + ENCODE_RS(il2p_find_rs(num_parity), rs_block, parity_out); +} + +/*------------------------------------------------------------- + * + * Name: void il2p_decode_rs + * + * Purpose: Check and attempt to fix block with FEC. + * + * Inputs: rec_block Received block composed of data and parity. + * Total size is sum of following two parameters. + * data_size Number of data bytes in above. + * num_parity Number of parity symbols (bytes) in above. + * + * Outputs: out Original with possible corrections applied. + * data_size bytes. + * + * Returns: -1 for unrecoverable. + * >= 0 for success. Number of symbols corrected. + * + *--------------------------------------------------------------*/ + +int il2p_decode_rs(unsigned char *rec_block, int data_size, int num_parity, unsigned char *out) +{ + + // Use zero padding in front if data size is too small. + + int n = data_size + num_parity; // total size in. + + unsigned char rs_block[FX25_BLOCK_SIZE]; + + // We could probably do this more efficiently by skipping the + // processing of the bytes known to be zero. Good enough for now. + + memset(rs_block, 0, sizeof(rs_block) - n); + memcpy(rs_block + sizeof(rs_block) - n, rec_block, n); + + if (il2p_get_debug() >= 3) { + Debugprintf("============================== il2p_decode_rs ==============================\n"); + Debugprintf("%d filler zeros, %d data, %d parity\n", (int)(sizeof(rs_block) - n), data_size, num_parity); + fx_hex_dump(rs_block, sizeof(rs_block)); + } + + int derrlocs[FX25_MAX_CHECK]; // Half would probably be OK. + + int derrors = DECODE_RS(il2p_find_rs(num_parity), rs_block, derrlocs, 0); + memcpy(out, rs_block + sizeof(rs_block) - n, data_size); + + if (il2p_get_debug() >= 3) { + if (derrors == 0) { + Debugprintf("No errors reported for RS block.\n"); + } + else if (derrors > 0) { + Debugprintf("%d errors fixed in positions:\n", derrors); + for (int j = 0; j < derrors; j++) { + Debugprintf(" %3d (0x%02x)\n", derrlocs[j], derrlocs[j]); + } + fx_hex_dump(rs_block, sizeof(rs_block)); + } + } + + // It is possible to have a situation where too many errors are + // present but the algorithm could get a good code block by "fixing" + // one of the padding bytes that should be 0. + + for (int i = 0; i < derrors; i++) { + if (derrlocs[i] < sizeof(rs_block) - n) { + if (il2p_get_debug() >= 3) { + Debugprintf("RS DECODE ERROR! Padding position %d should be 0 but it was set to %02x.\n", derrlocs[i], rs_block[derrlocs[i]]); + } + derrors = -1; + break; + } + } + + if (il2p_get_debug() >= 3) { + Debugprintf("============================== il2p_decode_rs returns %d ==============================\n", derrors); + } + return (derrors); +} + +// end il2p_init.c + + + + + + + + + + + + + +void ENCODE_RS(struct rs * rs, DTYPE * data, DTYPE * bb) +{ + + int i, j; + DTYPE feedback; + + memset(bb, 0, NROOTS * sizeof(DTYPE)); // clear out the FEC data area + + for (i = 0; i < NN - NROOTS; i++) { + feedback = INDEX_OF[data[i] ^ bb[0]]; + if (feedback != A0) { /* feedback term is non-zero */ + for (j = 1; j < NROOTS; j++) + bb[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS - j])]; + } + /* Shift */ + memmove(&bb[0], &bb[1], sizeof(DTYPE)*(NROOTS - 1)); + if (feedback != A0) + bb[NROOTS - 1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])]; + else + bb[NROOTS - 1] = 0; + } +} + + + + +int DECODE_RS(struct rs * rs, DTYPE * data, int *eras_pos, int no_eras) { + + int deg_lambda, el, deg_omega; + int i, j, r, k; + DTYPE u, q, tmp, num1, num2, den, discr_r; + // DTYPE lambda[NROOTS+1], s[NROOTS]; /* Err+Eras Locator poly and syndrome poly */ + // DTYPE b[NROOTS+1], t[NROOTS+1], omega[NROOTS+1]; + // DTYPE root[NROOTS], reg[NROOTS+1], loc[NROOTS]; + DTYPE lambda[FX25_MAX_CHECK + 1], s[FX25_MAX_CHECK]; /* Err+Eras Locator poly and syndrome poly */ + DTYPE b[FX25_MAX_CHECK + 1], t[FX25_MAX_CHECK + 1], omega[FX25_MAX_CHECK + 1]; + DTYPE root[FX25_MAX_CHECK], reg[FX25_MAX_CHECK + 1], loc[FX25_MAX_CHECK]; + int syn_error, count; + + /* form the syndromes; i.e., evaluate data(x) at roots of g(x) */ + for (i = 0; i < NROOTS; i++) + s[i] = data[0]; + + for (j = 1; j < NN; j++) { + for (i = 0; i < NROOTS; i++) { + if (s[i] == 0) { + s[i] = data[j]; + } + else { + s[i] = data[j] ^ ALPHA_TO[MODNN(INDEX_OF[s[i]] + (FCR + i)*PRIM)]; + } + } + } + + /* Convert syndromes to index form, checking for nonzero condition */ + syn_error = 0; + for (i = 0; i < NROOTS; i++) { + syn_error |= s[i]; + s[i] = INDEX_OF[s[i]]; + } + + // fprintf(stderr,"syn_error = %4x\n",syn_error); + if (!syn_error) { + /* if syndrome is zero, data[] is a codeword and there are no + * errors to correct. So return data[] unmodified + */ + count = 0; + goto finish; + } + memset(&lambda[1], 0, NROOTS * sizeof(lambda[0])); + lambda[0] = 1; + + if (no_eras > 0) { + /* Init lambda to be the erasure locator polynomial */ + lambda[1] = ALPHA_TO[MODNN(PRIM*(NN - 1 - eras_pos[0]))]; + for (i = 1; i < no_eras; i++) { + u = MODNN(PRIM*(NN - 1 - eras_pos[i])); + for (j = i + 1; j > 0; j--) { + tmp = INDEX_OF[lambda[j - 1]]; + if (tmp != A0) + lambda[j] ^= ALPHA_TO[MODNN(u + tmp)]; + } + } + +#if DEBUG >= 1 + /* Test code that verifies the erasure locator polynomial just constructed + Needed only for decoder debugging. */ + + /* find roots of the erasure location polynomial */ + for (i = 1; i <= no_eras; i++) + reg[i] = INDEX_OF[lambda[i]]; + + count = 0; + for (i = 1, k = IPRIM - 1; i <= NN; i++, k = MODNN(k + IPRIM)) { + q = 1; + for (j = 1; j <= no_eras; j++) + if (reg[j] != A0) { + reg[j] = MODNN(reg[j] + j); + q ^= ALPHA_TO[reg[j]]; + } + if (q != 0) + continue; + /* store root and error location number indices */ + root[count] = i; + loc[count] = k; + count++; + } + if (count != no_eras) { + fprintf(stderr, "count = %d no_eras = %d\n lambda(x) is WRONG\n", count, no_eras); + count = -1; + goto finish; + } +#if DEBUG >= 2 + fprintf(stderr, "\n Erasure positions as determined by roots of Eras Loc Poly:\n"); + for (i = 0; i < count; i++) + fprintf(stderr, "%d ", loc[i]); + fprintf(stderr, "\n"); +#endif +#endif + } + for (i = 0; i < NROOTS + 1; i++) + b[i] = INDEX_OF[lambda[i]]; + + /* + * Begin Berlekamp-Massey algorithm to determine error+erasure + * locator polynomial + */ + r = no_eras; + el = no_eras; + while (++r <= NROOTS) { /* r is the step number */ + /* Compute discrepancy at the r-th step in poly-form */ + discr_r = 0; + for (i = 0; i < r; i++) { + if ((lambda[i] != 0) && (s[r - i - 1] != A0)) { + discr_r ^= ALPHA_TO[MODNN(INDEX_OF[lambda[i]] + s[r - i - 1])]; + } + } + discr_r = INDEX_OF[discr_r]; /* Index form */ + if (discr_r == A0) { + /* 2 lines below: B(x) <-- x*B(x) */ + memmove(&b[1], b, NROOTS * sizeof(b[0])); + b[0] = A0; + } + else { + /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */ + t[0] = lambda[0]; + for (i = 0; i < NROOTS; i++) { + if (b[i] != A0) + t[i + 1] = lambda[i + 1] ^ ALPHA_TO[MODNN(discr_r + b[i])]; + else + t[i + 1] = lambda[i + 1]; + } + if (2 * el <= r + no_eras - 1) { + el = r + no_eras - el; + /* + * 2 lines below: B(x) <-- inv(discr_r) * + * lambda(x) + */ + for (i = 0; i <= NROOTS; i++) + b[i] = (lambda[i] == 0) ? A0 : MODNN(INDEX_OF[lambda[i]] - discr_r + NN); + } + else { + /* 2 lines below: B(x) <-- x*B(x) */ + memmove(&b[1], b, NROOTS * sizeof(b[0])); + b[0] = A0; + } + memcpy(lambda, t, (NROOTS + 1) * sizeof(t[0])); + } + } + + /* Convert lambda to index form and compute deg(lambda(x)) */ + deg_lambda = 0; + for (i = 0; i < NROOTS + 1; i++) { + lambda[i] = INDEX_OF[lambda[i]]; + if (lambda[i] != A0) + deg_lambda = i; + } + /* Find roots of the error+erasure locator polynomial by Chien search */ + memcpy(®[1], &lambda[1], NROOTS * sizeof(reg[0])); + count = 0; /* Number of roots of lambda(x) */ + for (i = 1, k = IPRIM - 1; i <= NN; i++, k = MODNN(k + IPRIM)) { + q = 1; /* lambda[0] is always 0 */ + for (j = deg_lambda; j > 0; j--) { + if (reg[j] != A0) { + reg[j] = MODNN(reg[j] + j); + q ^= ALPHA_TO[reg[j]]; + } + } + if (q != 0) + continue; /* Not a root */ + /* store root (index-form) and error location number */ +#if DEBUG>=2 + fprintf(stderr, "count %d root %d loc %d\n", count, i, k); +#endif + root[count] = i; + loc[count] = k; + /* If we've already found max possible roots, + * abort the search to save time + */ + if (++count == deg_lambda) + break; + } + if (deg_lambda != count) { + /* + * deg(lambda) unequal to number of roots => uncorrectable + * error detected + */ + count = -1; + goto finish; + } + /* + * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo + * x**NROOTS). in index form. Also find deg(omega). + */ + deg_omega = 0; + for (i = 0; i < NROOTS; i++) { + tmp = 0; + j = (deg_lambda < i) ? deg_lambda : i; + for (; j >= 0; j--) { + if ((s[i - j] != A0) && (lambda[j] != A0)) + tmp ^= ALPHA_TO[MODNN(s[i - j] + lambda[j])]; + } + if (tmp != 0) + deg_omega = i; + omega[i] = INDEX_OF[tmp]; + } + omega[NROOTS] = A0; + + /* + * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + * inv(X(l))**(FCR-1) and den = lambda_pr(inv(X(l))) all in poly-form + */ + for (j = count - 1; j >= 0; j--) { + num1 = 0; + for (i = deg_omega; i >= 0; i--) { + if (omega[i] != A0) + num1 ^= ALPHA_TO[MODNN(omega[i] + i * root[j])]; + } + num2 = ALPHA_TO[MODNN(root[j] * (FCR - 1) + NN)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ + for (i = min(deg_lambda, NROOTS - 1) & ~1; i >= 0; i -= 2) { + if (lambda[i + 1] != A0) + den ^= ALPHA_TO[MODNN(lambda[i + 1] + i * root[j])]; + } + if (den == 0) { +#if DEBUG >= 1 + fprintf(stderr, "\n ERROR: denominator = 0\n"); +#endif + count = -1; + goto finish; + } + /* Apply error to data */ + if (num1 != 0) { + data[loc[j]] ^= ALPHA_TO[MODNN(INDEX_OF[num1] + INDEX_OF[num2] + NN - INDEX_OF[den])]; + } + } +finish: + if (eras_pos != NULL) { + for (i = 0; i < count; i++) + eras_pos[i] = loc[i]; + } + return count; +} + + + + +struct rs *INIT_RS(unsigned int symsize, unsigned int gfpoly, unsigned fcr, unsigned prim, + unsigned int nroots) { + struct rs *rs; + int i, j, sr, root, iprim; + + if (symsize > 8 * sizeof(DTYPE)) + return NULL; /* Need version with ints rather than chars */ + + if (fcr >= (1 << symsize)) + return NULL; + if (prim == 0 || prim >= (1 << symsize)) + return NULL; + if (nroots >= (1 << symsize)) + return NULL; /* Can't have more roots than symbol values! */ + + rs = (struct rs *)calloc(1, sizeof(struct rs)); + if (rs == NULL) { + Debugprintf("FATAL ERROR: Out of memory.\n"); + exit(0); + } + rs->mm = symsize; + rs->nn = (1 << symsize) - 1; + + rs->alpha_to = (DTYPE *)calloc((rs->nn + 1), sizeof(DTYPE)); + if (rs->alpha_to == NULL) { + Debugprintf("FATAL ERROR: Out of memory.\n"); + exit(0); + } + rs->index_of = (DTYPE *)calloc((rs->nn + 1), sizeof(DTYPE)); + if (rs->index_of == NULL) { + Debugprintf("FATAL ERROR: Out of memory.\n"); + exit(0); + } + + /* Generate Galois field lookup tables */ + rs->index_of[0] = A0; /* log(zero) = -inf */ + rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */ + sr = 1; + for (i = 0; i < rs->nn; i++) { + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr <<= 1; + if (sr & (1 << symsize)) + sr ^= gfpoly; + sr &= rs->nn; + } + if (sr != 1) { + /* field generator polynomial is not primitive! */ + free(rs->alpha_to); + free(rs->index_of); + free(rs); + return NULL; + } + + /* Form RS code generator polynomial from its roots */ + rs->genpoly = (DTYPE *)calloc((nroots + 1), sizeof(DTYPE)); + if (rs->genpoly == NULL) { + Debugprintf("FATAL ERROR: Out of memory.\n"); + exit(0); + } + rs->fcr = fcr; + rs->prim = prim; + rs->nroots = nroots; + + /* Find prim-th root of 1, used in decoding */ + for (iprim = 1; (iprim % prim) != 0; iprim += rs->nn) + ; + rs->iprim = iprim / prim; + + rs->genpoly[0] = 1; + for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) { + rs->genpoly[i + 1] = 1; + + /* Multiply rs->genpoly[] by @**(root + x) */ + for (j = i; j > 0; j--) { + if (rs->genpoly[j] != 0) + rs->genpoly[j] = rs->genpoly[j - 1] ^ rs->alpha_to[modnn(rs, rs->index_of[rs->genpoly[j]] + root)]; + else + rs->genpoly[j] = rs->genpoly[j - 1]; + } + /* rs->genpoly[0] can never be zero */ + rs->genpoly[0] = rs->alpha_to[modnn(rs, rs->index_of[rs->genpoly[0]] + root)]; + } + /* convert rs->genpoly[] to index form for quicker encoding */ + for (i = 0; i <= nroots; i++) { + rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; + } + + // diagnostic prints +#if 0 + printf("Alpha To:\n\r"); + for (i = 0; i < sizeof(DTYPE)*(rs->nn + 1); i++) + printf("0x%2x,", rs->alpha_to[i]); + printf("\n\r"); + + printf("Index Of:\n\r"); + for (i = 0; i < sizeof(DTYPE)*(rs->nn + 1); i++) + printf("0x%2x,", rs->index_of[i]); + printf("\n\r"); + + printf("GenPoly:\n\r"); + for (i = 0; i <= nroots; i++) + printf("0x%2x,", rs->genpoly[i]); + printf("\n\r"); +#endif + return rs; +} + + +// TEMPORARY!!! +// FIXME: We already have multiple copies of this. +// Consolidate them into one somewhere. + +void fx_hex_dump(unsigned char *p, int len) +{ + int n, i, offset; + + offset = 0; + while (len > 0) { + n = len < 16 ? len : 16; + Debugprintf(" %03x: ", offset); + for (i = 0; i < n; i++) { + Debugprintf(" %02x", p[i]); + } + for (i = n; i < 16; i++) { + Debugprintf(" "); + } + Debugprintf(" "); + for (i = 0; i < n; i++) { + Debugprintf("%c", isprint(p[i]) ? p[i] : '.'); + } + Debugprintf("\n"); + p += 16; + offset += 16; + len -= 16; + } +} + + +/*------------------------------------------------------------- + * + * File: il2p_codec.c + * + * Purpose: Convert IL2P encoded format from and to direwolf internal packet format. + * + *--------------------------------------------------------------*/ + + + /*------------------------------------------------------------- + * + * Name: il2p_encode_frame + * + * Purpose: Convert AX.25 frame to IL2P encoding. + * + * Inputs: chan - Audio channel number, 0 = first. + * + * pp - Packet object pointer. + * + * max_fec - 1 to send maximum FEC size rather than automatic. + * + * Outputs: iout - Encoded result, excluding the 3 byte sync word. + * Caller should provide IL2P_MAX_PACKET_SIZE bytes. + * + * Returns: Number of bytes for transmission. + * -1 is returned for failure. + * + * Description: Encode into IL2P format. + * + * Errors: If something goes wrong, return -1. + * + * Most likely reason is that the frame is too large. + * IL2P has a max payload size of 1023 bytes. + * For a type 1 header, this is the maximum AX.25 Information part size. + * For a type 0 header, this is the entire AX.25 frame. + * + *--------------------------------------------------------------*/ + +int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout) +{ + + // Can a type 1 header be used? + + unsigned char hdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; + int e; + int out_len = 0; + + e = il2p_type_1_header(pp, max_fec, hdr); + if (e >= 0) { + il2p_scramble_block(hdr, iout, IL2P_HEADER_SIZE); + il2p_encode_rs(iout, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, iout + IL2P_HEADER_SIZE); + out_len = IL2P_HEADER_SIZE + IL2P_HEADER_PARITY; + + if (e == 0) { + // Success. No info part. + return (out_len); + } + + // Payload is AX.25 info part. + unsigned char *pinfo; + int info_len; + info_len = ax25_get_info(pp, &pinfo); + + int k = il2p_encode_payload(pinfo, info_len, max_fec, iout + out_len); + if (k > 0) { + out_len += k; + // Success. Info part was <= 1023 bytes. + return (out_len); + } + + // Something went wrong with the payload encoding. + return (-1); + } + else if (e == -1) { + + // Could not use type 1 header for some reason. + // e.g. More than 2 addresses, extended (mod 128) sequence numbers, etc. + + e = il2p_type_0_header(pp, max_fec, hdr); + if (e > 0) { + + il2p_scramble_block(hdr, iout, IL2P_HEADER_SIZE); + il2p_encode_rs(iout, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, iout + IL2P_HEADER_SIZE); + out_len = IL2P_HEADER_SIZE + IL2P_HEADER_PARITY; + + // Payload is entire AX.25 frame. + + unsigned char *frame_data_ptr = ax25_get_frame_data_ptr(pp); + int frame_len = ax25_get_frame_len(pp); + int k = il2p_encode_payload(frame_data_ptr, frame_len, max_fec, iout + out_len); + if (k > 0) { + out_len += k; + // Success. Entire AX.25 frame <= 1023 bytes. + return (out_len); + } + // Something went wrong with the payload encoding. + return (-1); + } + else if (e == 0) { + // Impossible condition. Type 0 header must have payload. + return (-1); + } + else { + // AX.25 frame is too large. + return (-1); + } + } + + // AX.25 Information part is too large. + return (-1); +} + + + +/*------------------------------------------------------------- + * + * Name: il2p_decode_frame + * + * Purpose: Convert IL2P encoding to AX.25 frame. + * This is only used during testing, with a whole encoded frame. + * During reception, the header would have FEC and descrambling + * applied first so we would know how much to collect for the payload. + * + * Inputs: irec - Received IL2P frame excluding the 3 byte sync word. + * + * Future Out: Number of symbols corrected. + * + * Returns: Packet pointer or NULL for error. + * + *--------------------------------------------------------------*/ + +packet_t il2p_decode_frame(unsigned char *irec) +{ + unsigned char uhdr[IL2P_HEADER_SIZE]; // After FEC and descrambling. + int e = il2p_clarify_header(irec, uhdr); + + // TODO?: for symmetry we might want to clarify the payload before combining. + + return (il2p_decode_header_payload(uhdr, irec + IL2P_HEADER_SIZE + IL2P_HEADER_PARITY, &e)); +} + + +/*------------------------------------------------------------- + * + * Name: il2p_decode_header_payload + * + * Purpose: Convert IL2P encoding to AX.25 frame + * + * Inputs: uhdr - Received header after FEC and descrambling. + * epayload - Encoded payload. + * + * In/Out: symbols_corrected - Symbols (bytes) corrected in the header. + * Should be 0 or 1 because it has 2 parity symbols. + * Here we add number of corrections for the payload. + * + * Returns: Packet pointer or NULL for error. + * + *--------------------------------------------------------------*/ + +packet_t il2p_decode_header_payload(unsigned char* uhdr, unsigned char *epayload, int *symbols_corrected) +{ + int hdr_type; + int max_fec; + int payload_len = il2p_get_header_attributes(uhdr, &hdr_type, &max_fec); + + packet_t pp = NULL; + + if (hdr_type == 1) { + + // Header type 1. Any payload is the AX.25 Information part. + + pp = il2p_decode_header_type_1(uhdr, *symbols_corrected); + if (pp == NULL) { + // Failed for some reason. + return (NULL); + } + + if (payload_len > 0) { + // This is the AX.25 Information part. + + unsigned char extracted[IL2P_MAX_PAYLOAD_SIZE]; + int e = il2p_decode_payload(epayload, payload_len, max_fec, extracted, symbols_corrected); + + // It would be possible to have a good header but too many errors in the payload. + + if (e <= 0) { + ax25_delete(pp); + pp = NULL; + return (pp); + } + + if (e != payload_len) { + Debugprintf("IL2P Internal Error: %s(): hdr_type=%d, max_fec=%d, payload_len=%d, e=%d.\n", __func__, hdr_type, max_fec, payload_len, e); + } + + ax25_set_info(pp, extracted, payload_len); + } + return (pp); + } + else { + + // Header type 0. The payload is the entire AX.25 frame. + + unsigned char extracted[IL2P_MAX_PAYLOAD_SIZE]; + int e = il2p_decode_payload(epayload, payload_len, max_fec, extracted, symbols_corrected); + + if (e <= 0) { // Payload was not received correctly. + return (NULL); + } + + if (e != payload_len) { + Debugprintf("IL2P Internal Error: %s(): hdr_type=%d, e=%d, payload_len=%d\n", __func__, hdr_type, e, payload_len); + return (NULL); + } + + alevel_t alevel; + memset(&alevel, 0, sizeof(alevel)); + //alevel = demod_get_audio_level (chan, subchan); // What TODO? We don't know channel here. + // I think alevel gets filled in somewhere later making + // this redundant. + + pp = ax25_from_frame(extracted, payload_len, alevel); + return (pp); + } + +} // end il2p_decode_header_payload + +// end il2p_codec.c + + + +/*-------------------------------------------------------------------------------- + * + * File: il2p_header.c + * + * Purpose: Functions to deal with the IL2P header. + * + * Reference: http://tarpn.net/t/il2p/il2p-specification0-4.pdf + * + *--------------------------------------------------------------------------------*/ + + + + // Convert ASCII to/from DEC SIXBIT as defined here: + // https://en.wikipedia.org/wiki/Six-bit_character_code#DEC_six-bit_code + +static inline int ascii_to_sixbit(int a) +{ + if (a >= ' ' && a <= '_') return (a - ' '); + return (31); // '?' for any invalid. +} + +static inline int sixbit_to_ascii(int s) +{ + return (s + ' '); +} + +// Functions for setting the various header fields. +// It is assumed that it was zeroed first so only the '1' bits are set. + +static void set_field(unsigned char *hdr, int bit_num, int lsb_index, int width, int value) +{ + while (width > 0 && value != 0) { + //assert(lsb_index >= 0 && lsb_index <= 11); + if (value & 1) { + hdr[lsb_index] |= 1 << bit_num; + } + value >>= 1; + lsb_index--; + width--; + } + //assert(value == 0); +} + +#define SET_UI(hdr,val) set_field(hdr, 6, 0, 1, val) + +#define SET_PID(hdr,val) set_field(hdr, 6, 4, 4, val) + +#define SET_CONTROL(hdr,val) set_field(hdr, 6, 11, 7, val) + + +#define SET_FEC_LEVEL(hdr,val) set_field(hdr, 7, 0, 1, val) + +#define SET_HDR_TYPE(hdr,val) set_field(hdr, 7, 1, 1, val) + +#define SET_PAYLOAD_BYTE_COUNT(hdr,val) set_field(hdr, 7, 11, 10, val) + + +// Extracting the fields. + +static int get_field(unsigned char *hdr, int bit_num, int lsb_index, int width) +{ + int result = 0; + lsb_index -= width - 1; + while (width > 0) { + result <<= 1; + //assert(lsb_index >= 0 && lsb_index <= 11); + if (hdr[lsb_index] & (1 << bit_num)) { + result |= 1; + } + lsb_index++; + width--; + } + return (result); +} + +#define GET_UI(hdr) get_field(hdr, 6, 0, 1) + +#define GET_PID(hdr) get_field(hdr, 6, 4, 4) + +#define GET_CONTROL(hdr) get_field(hdr, 6, 11, 7) + + +#define GET_FEC_LEVEL(hdr) get_field(hdr, 7, 0, 1) + +#define GET_HDR_TYPE(hdr) get_field(hdr, 7, 1, 1) + +#define GET_PAYLOAD_BYTE_COUNT(hdr) get_field(hdr, 7, 11, 10) + + + +// AX.25 'I' and 'UI' frames have a protocol ID which determines how the +// information part should be interpreted. +// Here we squeeze the most common cases down to 4 bits. +// Return -1 if translation is not possible. Fall back to type 0 header in this case. + +static int encode_pid(packet_t pp) +{ + int pid = ax25_get_pid(pp); + + if ((pid & 0x30) == 0x20) return (0x2); // AX.25 Layer 3 + if ((pid & 0x30) == 0x10) return (0x2); // AX.25 Layer 3 + if (pid == 0x01) return (0x3); // ISO 8208 / CCIT X.25 PLP + if (pid == 0x06) return (0x4); // Compressed TCP/IP + if (pid == 0x07) return (0x5); // Uncompressed TCP/IP + if (pid == 0x08) return (0x6); // Segmentation fragmen + if (pid == 0xcc) return (0xb); // ARPA Internet Protocol + if (pid == 0xcd) return (0xc); // ARPA Address Resolution + if (pid == 0xce) return (0xd); // FlexNet + if (pid == 0xcf) return (0xe); // TheNET + if (pid == 0xf0) return (0xf); // No L3 + return (-1); +} + +// Convert IL2P 4 bit PID to AX.25 8 bit PID. + + +static int decode_pid(int pid) +{ + static const unsigned char axpid[16] = { + 0xf0, // Should not happen. 0 is for 'S' frames. + 0xf0, // Should not happen. 1 is for 'U' frames (but not UI). + 0x20, // AX.25 Layer 3 + 0x01, // ISO 8208 / CCIT X.25 PLP + 0x06, // Compressed TCP/IP + 0x07, // Uncompressed TCP/IP + 0x08, // Segmentation fragment + 0xf0, // Future + 0xf0, // Future + 0xf0, // Future + 0xf0, // Future + 0xcc, // ARPA Internet Protocol + 0xcd, // ARPA Address Resolution + 0xce, // FlexNet + 0xcf, // TheNET + 0xf0 }; // No L3 + + //assert(pid >= 0 && pid <= 15); + return (axpid[pid]); +} + + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_type_1_header + * + * Purpose: Attempt to create type 1 header from packet object. + * + * Inputs: pp - Packet object. + * + * max_fec - 1 to use maximum FEC symbols , 0 for automatic. + * + * Outputs: hdr - IL2P header with no scrambling or parity symbols. + * Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes. + * + * Returns: Number of bytes for information part or -1 for failure. + * In case of failure, fall back to type 0 transparent encapsulation. + * + * Description: Type 1 Headers do not support AX.25 repeater callsign addressing, + * Modulo-128 extended mode window sequence numbers, nor any callsign + * characters that cannot translate to DEC SIXBIT. + * If these cases are encountered during IL2P packet encoding, + * the encoder switches to Type 0 Transparent Encapsulation. + * SABME can't be handled by type 1. + * + *--------------------------------------------------------------------------------*/ + +int il2p_type_1_header(packet_t pp, int max_fec, unsigned char *hdr) +{ + memset(hdr, 0, IL2P_HEADER_SIZE); + + if (ax25_get_num_addr(pp) != 2) { + // Only two addresses are allowed for type 1 header. + return (-1); + } + + // Check does not apply for 'U' frames but put in one place rather than two. + + if (ax25_get_modulo(pp) == 128) return(-1); + + // Destination and source addresses go into low bits 0-5 for bytes 0-11. + + char dst_addr[AX25_MAX_ADDR_LEN]; + char src_addr[AX25_MAX_ADDR_LEN]; + + ax25_get_addr_no_ssid(pp, AX25_DESTINATION, dst_addr); + int dst_ssid = ax25_get_ssid(pp, AX25_DESTINATION); + + ax25_get_addr_no_ssid(pp, AX25_SOURCE, src_addr); + int src_ssid = ax25_get_ssid(pp, AX25_SOURCE); + + unsigned char *a = (unsigned char *)dst_addr; + for (int i = 0; *a != '\0'; i++, a++) { + if (*a < ' ' || *a > '_') { + // Shouldn't happen but follow the rule. + return (-1); + } + hdr[i] = ascii_to_sixbit(*a); + } + + a = (unsigned char *)src_addr; + for (int i = 6; *a != '\0'; i++, a++) { + if (*a < ' ' || *a > '_') { + // Shouldn't happen but follow the rule. + return (-1); + } + hdr[i] = ascii_to_sixbit(*a); + } + + // Byte 12 has DEST SSID in upper nybble and SRC SSID in lower nybble and + hdr[12] = (dst_ssid << 4) | src_ssid; + + ax25_frame_type_t frame_type; + cmdres_t cr; // command or response. + char description[64]; + int pf; // Poll/Final. + int nr, ns; // Sequence numbers. + + frame_type = ax25_frame_type(pp, &cr, description, &pf, &nr, &ns); + + //Debugprintf ("%s(): %s-%d>%s-%d: %s\n", __func__, src_addr, src_ssid, dst_addr, dst_ssid, description); + + switch (frame_type) { + + case frame_type_S_RR: // Receive Ready - System Ready To Receive + case frame_type_S_RNR: // Receive Not Ready - TNC Buffer Full + case frame_type_S_REJ: // Reject Frame - Out of Sequence or Duplicate + case frame_type_S_SREJ: // Selective Reject - Request single frame repeat + + // S frames (RR, RNR, REJ, SREJ), mod 8, have control N(R) P/F S S 0 1 + // These are mapped into P/F N(R) C S S + // Bit 6 is not mentioned in documentation but it is used for P/F for the other frame types. + // C is copied from the C bit in the destination addr. + // C from source is not used here. Reception assumes it is the opposite. + // PID is set to 0, meaning none, for S frames. + + SET_UI(hdr, 0); + SET_PID(hdr, 0); + SET_CONTROL(hdr, (pf << 6) | (nr << 3) | (((cr == cr_cmd) | (cr == cr_11)) << 2)); + + // This gets OR'ed into the above. + switch (frame_type) { + case frame_type_S_RR: SET_CONTROL(hdr, 0); break; + case frame_type_S_RNR: SET_CONTROL(hdr, 1); break; + case frame_type_S_REJ: SET_CONTROL(hdr, 2); break; + case frame_type_S_SREJ: SET_CONTROL(hdr, 3); break; + default: break; + } + + break; + + case frame_type_U_SABM: // Set Async Balanced Mode + case frame_type_U_DISC: // Disconnect + case frame_type_U_DM: // Disconnect Mode + case frame_type_U_UA: // Unnumbered Acknowledge + case frame_type_U_FRMR: // Frame Reject + case frame_type_U_UI: // Unnumbered Information + case frame_type_U_XID: // Exchange Identification + case frame_type_U_TEST: // Test + + // The encoding allows only 3 bits for frame type and SABME got left out. + // Control format: P/F opcode[3] C n/a n/a + // The grayed out n/a bits are observed as 00 in the example. + // The header UI field must also be set for UI frames. + // PID is set to 1 for all U frames other than UI. + + if (frame_type == frame_type_U_UI) { + SET_UI(hdr, 1); // I guess this is how we distinguish 'I' and 'UI' + // on the receiving end. + int pid = encode_pid(pp); + if (pid < 0) return (-1); + SET_PID(hdr, pid); + } + else { + SET_PID(hdr, 1); // 1 for 'U' other than 'UI'. + } + + // Each of the destination and source addresses has a "C" bit. + // They should normally have the opposite setting. + // IL2P has only a single bit to represent 4 possbilities. + // + // dst src il2p meaning + // --- --- ---- ------- + // 0 0 0 Not valid (earlier protocol version) + // 1 0 1 Command (v2) + // 0 1 0 Response (v2) + // 1 1 1 Not valid (earlier protocol version) + // + // APRS does not mention how to set these bits and all 4 combinations + // are seen in the wild. Apparently these are ignored on receive and no + // one cares. Here we copy from the C bit in the destination address. + // It should be noted that the case of both C bits being the same can't + // be represented so the il2p encode/decode bit not produce exactly the + // same bits. We see this in the second example in the protocol spec. + // The original UI frame has both C bits of 0 so it is received as a response. + + SET_CONTROL(hdr, (pf << 6) | (((cr == cr_cmd) | (cr == cr_11)) << 2)); + + // This gets OR'ed into the above. + switch (frame_type) { + case frame_type_U_SABM: SET_CONTROL(hdr, 0 << 3); break; + case frame_type_U_DISC: SET_CONTROL(hdr, 1 << 3); break; + case frame_type_U_DM: SET_CONTROL(hdr, 2 << 3); break; + case frame_type_U_UA: SET_CONTROL(hdr, 3 << 3); break; + case frame_type_U_FRMR: SET_CONTROL(hdr, 4 << 3); break; + case frame_type_U_UI: SET_CONTROL(hdr, 5 << 3); break; + case frame_type_U_XID: SET_CONTROL(hdr, 6 << 3); break; + case frame_type_U_TEST: SET_CONTROL(hdr, 7 << 3); break; + default: break; + } + break; + + case frame_type_I: // Information + + // I frames (mod 8 only) + // encoded control: P/F N(R) N(S) + + SET_UI(hdr, 0); + + int pid2 = encode_pid(pp); + if (pid2 < 0) return (-1); + SET_PID(hdr, pid2); + + SET_CONTROL(hdr, (pf << 6) | (nr << 3) | ns); + break; + + case frame_type_U_SABME: // Set Async Balanced Mode, Extended + case frame_type_U: // other Unnumbered, not used by AX.25. + case frame_not_AX25: // Could not get control byte from frame. + default: + + // Fall back to the header type 0 for these. + return (-1); + } + + // Common for all header type 1. + + // Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10] + + SET_FEC_LEVEL(hdr, max_fec); + SET_HDR_TYPE(hdr, 1); + + unsigned char *pinfo; + int info_len; + + info_len = ax25_get_info(pp, &pinfo); + if (info_len < 0 || info_len > IL2P_MAX_PAYLOAD_SIZE) { + return (-2); + } + + SET_PAYLOAD_BYTE_COUNT(hdr, info_len); + return (info_len); +} + + +// This should create a packet from the IL2P header. +// The information part will not be filled in. + +static void trim(char *stuff) +{ + char *p = stuff + strlen(stuff) - 1; + while (strlen(stuff) > 0 && (*p == ' ')) { + *p = '\0'; + p--; + } +} + + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_decode_header_type_1 + * + * Purpose: Attempt to convert type 1 header to a packet object. + * + * Inputs: hdr - IL2P header with no scrambling or parity symbols. + * + * num_sym_changed - Number of symbols changed by FEC in the header. + * Should be 0 or 1. + * + * Returns: Packet Object or NULL for failure. + * + * Description: A later step will process the payload for the information part. + * + *--------------------------------------------------------------------------------*/ + +packet_t il2p_decode_header_type_1(unsigned char *hdr, int num_sym_changed) +{ + + if (GET_HDR_TYPE(hdr) != 1) { + Debugprintf("IL2P Internal error. Should not be here: %s, when header type is 0.\n", __func__); + return (NULL); + } + + // First get the addresses including SSID. + + char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; + int num_addr = 2; + memset(addrs, 0, 2 * AX25_MAX_ADDR_LEN); + + // The IL2P header uses 2 parity symbols which means a single corrupted symbol (byte) + // can always be corrected. + // However, I have seen cases, where the error rate is very high, where the RS decoder + // thinks it found a valid code block by changing one symbol but it was the wrong one. + // The result is trash. This shows up as address fields like 'R&G4"A' and 'TEW\ !'. + // I added a sanity check here to catch characters other than upper case letters and digits. + // The frame should be rejected in this case. The question is whether to discard it + // silently or print a message so the user can see that something strange is happening? + // My current thinking is that it should be silently ignored if the header has been + // modified (correctee or more likely, made worse in this cases). + // If no changes were made, something weird is happening. We should mention it for + // troubleshooting rather than sweeping it under the rug. + + // The same thing has been observed with the payload, under very high error conditions, + // and max_fec==0. Here I don't see a good solution. AX.25 information can contain + // "binary" data so I'm not sure what sort of sanity check could be added. + // This was not observed with max_fec==1. If we make that the default, same as Nino TNC, + // it would be extremely extremely unlikely unless someone explicitly selects weaker FEC. + + // TODO: We could do something similar for header type 0. + // The address fields should be all binary zero values. + // Someone overly ambitious might check the addresses found in the first payload block. + + for (int i = 0; i <= 5; i++) { + addrs[AX25_DESTINATION][i] = sixbit_to_ascii(hdr[i] & 0x3f); + } + trim(addrs[AX25_DESTINATION]); + for (int i = 0; i < strlen(addrs[AX25_DESTINATION]); i++) { + if (!isupper(addrs[AX25_DESTINATION][i]) && !isdigit(addrs[AX25_DESTINATION][i])) { + if (num_sym_changed == 0) { + // This can pop up sporadically when receiving random noise. + // Would be better to show only when debug is enabled but variable not available here. + // TODO: For now we will just suppress it. + //text_color_set(DW_COLOR_ERROR); + //Debugprintf ("IL2P: Invalid character '%c' in destination address '%s'\n", addrs[AX25_DESTINATION][i], addrs[AX25_DESTINATION]); + } + return (NULL); + } + } + sprintf(addrs[AX25_DESTINATION] + strlen(addrs[AX25_DESTINATION]), "-%d", (hdr[12] >> 4) & 0xf); + + for (int i = 0; i <= 5; i++) { + addrs[AX25_SOURCE][i] = sixbit_to_ascii(hdr[i + 6] & 0x3f); + } + trim(addrs[AX25_SOURCE]); + for (int i = 0; i < strlen(addrs[AX25_SOURCE]); i++) { + if (!isupper(addrs[AX25_SOURCE][i]) && !isdigit(addrs[AX25_SOURCE][i])) { + if (num_sym_changed == 0) { + // This can pop up sporadically when receiving random noise. + // Would be better to show only when debug is enabled but variable not available here. + // TODO: For now we will just suppress it. + //text_color_set(DW_COLOR_ERROR); + //Debugprintf ("IL2P: Invalid character '%c' in source address '%s'\n", addrs[AX25_SOURCE][i], addrs[AX25_SOURCE]); + } + return (NULL); + } + } + sprintf(addrs[AX25_SOURCE] + strlen(addrs[AX25_SOURCE]), "-%d", hdr[12] & 0xf); + + // The PID field gives us the general type. + // 0 = 'S' frame. + // 1 = 'U' frame other than UI. + // others are either 'UI' or 'I' depending on the UI field. + + int pid = GET_PID(hdr); + int ui = GET_UI(hdr); + + if (pid == 0) { + + // 'S' frame. + // The control field contains: P/F N(R) C S S + + int control = GET_CONTROL(hdr); + cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res; + ax25_frame_type_t ftype; + switch (control & 0x03) { + case 0: ftype = frame_type_S_RR; break; + case 1: ftype = frame_type_S_RNR; break; + case 2: ftype = frame_type_S_REJ; break; + default: ftype = frame_type_S_SREJ; break; + } + int modulo = 8; + int nr = (control >> 3) & 0x07; + int pf = (control >> 6) & 0x01; + unsigned char *pinfo = NULL; // Any info for SREJ will be added later. + int info_len = 0; + return (ax25_s_frame(addrs, num_addr, cr, ftype, modulo, nr, pf, pinfo, info_len)); + } + else if (pid == 1) { + + // 'U' frame other than 'UI'. + // The control field contains: P/F OPCODE{3) C x x + + int control = GET_CONTROL(hdr); + cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res; + int axpid = 0; // unused for U other than UI. + ax25_frame_type_t ftype; + switch ((control >> 3) & 0x7) { + case 0: ftype = frame_type_U_SABM; break; + case 1: ftype = frame_type_U_DISC; break; + case 2: ftype = frame_type_U_DM; break; + case 3: ftype = frame_type_U_UA; break; + case 4: ftype = frame_type_U_FRMR; break; + case 5: ftype = frame_type_U_UI; axpid = 0xf0; break; // Should not happen with IL2P pid == 1. + case 6: ftype = frame_type_U_XID; break; + default: ftype = frame_type_U_TEST; break; + } + int pf = (control >> 6) & 0x01; + unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later. + int info_len = 0; + return (ax25_u_frame(addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len)); + } + else if (ui) { + + // 'UI' frame. + // The control field contains: P/F OPCODE{3) C x x + + int control = GET_CONTROL(hdr); + cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res; + ax25_frame_type_t ftype = frame_type_U_UI; + int pf = (control >> 6) & 0x01; + int axpid = decode_pid(GET_PID(hdr)); + unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later. + int info_len = 0; + return (ax25_u_frame(addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len)); + } + else { + + // 'I' frame. + // The control field contains: P/F N(R) N(S) + + int control = GET_CONTROL(hdr); + cmdres_t cr = cr_cmd; // Always command. + int pf = (control >> 6) & 0x01; + int nr = (control >> 3) & 0x7; + int ns = control & 0x7; + int modulo = 8; + int axpid = decode_pid(GET_PID(hdr)); + unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later. + int info_len = 0; + return (ax25_i_frame(addrs, num_addr, cr, modulo, nr, ns, pf, axpid, pinfo, info_len)); + } + return (NULL); // unreachable but avoid warning. + +} // end + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_type_0_header + * + * Purpose: Attempt to create type 0 header from packet object. + * + * Inputs: pp - Packet object. + * + * max_fec - 1 to use maximum FEC symbols, 0 for automatic. + * + * Outputs: hdr - IL2P header with no scrambling or parity symbols. + * Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes. + * + * Returns: Number of bytes for information part or -1 for failure. + * In case of failure, fall back to type 0 transparent encapsulation. + * + * Description: The type 0 header is used when it is not one of the restricted cases + * covered by the type 1 header. + * The AX.25 frame is put in the payload. + * This will cover: more than one address, mod 128 sequences, etc. + * + *--------------------------------------------------------------------------------*/ + +int il2p_type_0_header(packet_t pp, int max_fec, unsigned char *hdr) +{ + memset(hdr, 0, IL2P_HEADER_SIZE); + + // Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10] + + SET_FEC_LEVEL(hdr, max_fec); + SET_HDR_TYPE(hdr, 0); + + int frame_len = ax25_get_frame_len(pp); + + if (frame_len < 14 || frame_len > IL2P_MAX_PAYLOAD_SIZE) { + return (-2); + } + + SET_PAYLOAD_BYTE_COUNT(hdr, frame_len); + return (frame_len); +} + + +/*********************************************************************************** + * + * Name: il2p_get_header_attributes + * + * Purpose: Extract a few attributes from an IL2p header. + * + * Inputs: hdr - IL2P header structure. + * + * Outputs: hdr_type - 0 or 1. + * + * max_fec - 0 for automatic or 1 for fixed maximum size. + * + * Returns: Payload byte count. (actual payload size, not the larger encoded format) + * + ***********************************************************************************/ + + +int il2p_get_header_attributes(unsigned char *hdr, int *hdr_type, int *max_fec) +{ + *hdr_type = GET_HDR_TYPE(hdr); + *max_fec = GET_FEC_LEVEL(hdr); + return(GET_PAYLOAD_BYTE_COUNT(hdr)); +} + + +/*********************************************************************************** + * + * Name: il2p_clarify_header + * + * Purpose: Convert received header to usable form. + * This involves RS FEC then descrambling. + * + * Inputs: rec_hdr - Header as received over the radio. + * + * Outputs: corrected_descrambled_hdr - After RS FEC and unscrambling. + * + * Returns: Number of symbols that were corrected: + * 0 = No errors + * 1 = Single symbol corrected. + * <0 = Unable to obtain good header. + * + ***********************************************************************************/ + +int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_descrambled_hdr) +{ + unsigned char corrected[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; + + int e = il2p_decode_rs(rec_hdr, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, corrected); + + il2p_descramble_block(corrected, corrected_descrambled_hdr, IL2P_HEADER_SIZE); + + return (e); +} + +// end il2p_header.c + + +/*-------------------------------------------------------------------------------- + * + * File: il2p_payload.c + * + * Purpose: Functions dealing with the payload. + * + *--------------------------------------------------------------------------------*/ + + + /*-------------------------------------------------------------------------------- + * + * Function: il2p_payload_compute + * + * Purpose: Compute number and sizes of data blocks based on total size. + * + * Inputs: payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE) + * max_fec true for 16 parity symbols, false for automatic. + * + * Outputs: *p Payload block sizes and counts. + * Number of parity symbols per block. + * + * Returns: Number of bytes in the encoded format. + * Could be 0 for no payload blocks. + * -1 for error (i.e. invalid unencoded size: <0 or >1023) + * + *--------------------------------------------------------------------------------*/ + +int il2p_payload_compute(il2p_payload_properties_t *p, int payload_size, int max_fec) +{ + memset(p, 0, sizeof(il2p_payload_properties_t)); + + if (payload_size < 0 || payload_size > IL2P_MAX_PAYLOAD_SIZE) { + return (-1); + } + if (payload_size == 0) { + return (0); + } + + if (max_fec) { + p->payload_byte_count = payload_size; + p->payload_block_count = (p->payload_byte_count + 238) / 239; + p->small_block_size = p->payload_byte_count / p->payload_block_count; + p->large_block_size = p->small_block_size + 1; + p->large_block_count = p->payload_byte_count - (p->payload_block_count * p->small_block_size); + p->small_block_count = p->payload_block_count - p->large_block_count; + p->parity_symbols_per_block = 16; + } + else { + p->payload_byte_count = payload_size; + p->payload_block_count = (p->payload_byte_count + 246) / 247; + p->small_block_size = p->payload_byte_count / p->payload_block_count; + p->large_block_size = p->small_block_size + 1; + p->large_block_count = p->payload_byte_count - (p->payload_block_count * p->small_block_size); + p->small_block_count = p->payload_block_count - p->large_block_count; + //p->parity_symbols_per_block = (p->small_block_size / 32) + 2; // Looks like error in documentation + + // It would work if the number of parity symbols was based on large block size. + + if (p->small_block_size <= 61) p->parity_symbols_per_block = 2; + else if (p->small_block_size <= 123) p->parity_symbols_per_block = 4; + else if (p->small_block_size <= 185) p->parity_symbols_per_block = 6; + else if (p->small_block_size <= 247) p->parity_symbols_per_block = 8; + else { + // Should not happen. But just in case... + Debugprintf("IL2P parity symbol per payload block error. small_block_size = %d\n", p->small_block_size); + return (-1); + } + } + + // Return the total size for the encoded format. + + return (p->small_block_count * (p->small_block_size + p->parity_symbols_per_block) + + p->large_block_count * (p->large_block_size + p->parity_symbols_per_block)); + +} // end il2p_payload_compute + + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_encode_payload + * + * Purpose: Split payload into multiple blocks such that each set + * of data and parity symbols fit into a 255 byte RS block. + * + * Inputs: *payload Array of bytes. + * payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE) + * max_fec true for 16 parity symbols, false for automatic. + * + * Outputs: *enc Encoded payload for transmission. + * Up to IL2P_MAX_ENCODED_SIZE bytes. + * + * Returns: -1 for error (i.e. invalid size) + * 0 for no blocks. (i.e. size zero) + * Number of bytes generated. Maximum IL2P_MAX_ENCODED_SIZE. + * + * Note: I interpreted the protocol spec as saying the LFSR state is retained + * between data blocks. During interoperability testing, I found that + * was not the case. It is reset for each data block. + * + *--------------------------------------------------------------------------------*/ + + +int il2p_encode_payload(unsigned char *payload, int payload_size, int max_fec, unsigned char *enc) +{ + if (payload_size > IL2P_MAX_PAYLOAD_SIZE) return (-1); + if (payload_size == 0) return (0); + + // Determine number of blocks and sizes. + + il2p_payload_properties_t ipp; + int e; + e = il2p_payload_compute(&ipp, payload_size, max_fec); + if (e <= 0) { + return (e); + } + + unsigned char *pin = payload; + unsigned char *pout = enc; + int encoded_length = 0; + unsigned char scram[256]; + unsigned char parity[IL2P_MAX_PARITY_SYMBOLS]; + + // First the large blocks. + + for (int b = 0; b < ipp.large_block_count; b++) { + + il2p_scramble_block(pin, scram, ipp.large_block_size); + memcpy(pout, scram, ipp.large_block_size); + pin += ipp.large_block_size; + pout += ipp.large_block_size; + encoded_length += ipp.large_block_size; + il2p_encode_rs(scram, ipp.large_block_size, ipp.parity_symbols_per_block, parity); + memcpy(pout, parity, ipp.parity_symbols_per_block); + pout += ipp.parity_symbols_per_block; + encoded_length += ipp.parity_symbols_per_block; + } + + // Then the small blocks. + + for (int b = 0; b < ipp.small_block_count; b++) { + + il2p_scramble_block(pin, scram, ipp.small_block_size); + memcpy(pout, scram, ipp.small_block_size); + pin += ipp.small_block_size; + pout += ipp.small_block_size; + encoded_length += ipp.small_block_size; + il2p_encode_rs(scram, ipp.small_block_size, ipp.parity_symbols_per_block, parity); + memcpy(pout, parity, ipp.parity_symbols_per_block); + pout += ipp.parity_symbols_per_block; + encoded_length += ipp.parity_symbols_per_block; + } + + return (encoded_length); + +} // end il2p_encode_payload + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_decode_payload + * + * Purpose: Extract original data from encoded payload. + * + * Inputs: received Array of bytes. Size is unknown but in practice it + * must not exceed IL2P_MAX_ENCODED_SIZE. + * payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE) + * Expected result size based on header. + * max_fec true for 16 parity symbols, false for automatic. + * + * Outputs: payload_out Recovered payload. + * + * In/Out: symbols_corrected Number of symbols corrected. + * + * + * Returns: Number of bytes extracted. Should be same as payload_size going in. + * -3 for unexpected internal inconsistency. + * -2 for unable to recover from signal corruption. + * -1 for invalid size. + * 0 for no blocks. (i.e. size zero) + * + * Description: Each block is scrambled separately but the LSFR state is carried + * from the first payload block to the next. + * + *--------------------------------------------------------------------------------*/ + +int il2p_decode_payload(unsigned char *received, int payload_size, int max_fec, unsigned char *payload_out, int *symbols_corrected) +{ + // Determine number of blocks and sizes. + + il2p_payload_properties_t ipp; + int e; + e = il2p_payload_compute(&ipp, payload_size, max_fec); + if (e <= 0) { + return (e); + } + + unsigned char *pin = received; + unsigned char *pout = payload_out; + int decoded_length = 0; + int failed = 0; + + // First the large blocks. + + for (int b = 0; b < ipp.large_block_count; b++) { + unsigned char corrected_block[255]; + int e = il2p_decode_rs(pin, ipp.large_block_size, ipp.parity_symbols_per_block, corrected_block); + + // Debugprintf ("%s:%d: large block decode_rs returned status = %d\n", __FILE__, __LINE__, e); + + if (e < 0) failed = 1; + *symbols_corrected += e; + + il2p_descramble_block(corrected_block, pout, ipp.large_block_size); + + if (il2p_get_debug() >= 2) { + + Debugprintf("Descrambled large payload block, %d bytes:\n", ipp.large_block_size); + fx_hex_dump(pout, ipp.large_block_size); + } + + pin += ipp.large_block_size + ipp.parity_symbols_per_block; + pout += ipp.large_block_size; + decoded_length += ipp.large_block_size; + } + + // Then the small blocks. + + for (int b = 0; b < ipp.small_block_count; b++) { + unsigned char corrected_block[255]; + int e = il2p_decode_rs(pin, ipp.small_block_size, ipp.parity_symbols_per_block, corrected_block); + + // Debugprintf ("%s:%d: small block decode_rs returned status = %d\n", __FILE__, __LINE__, e); + + if (e < 0) failed = 1; + *symbols_corrected += e; + + il2p_descramble_block(corrected_block, pout, ipp.small_block_size); + + if (il2p_get_debug() >= 2) { + + Debugprintf("Descrambled small payload block, %d bytes:\n", ipp.small_block_size); + fx_hex_dump(pout, ipp.small_block_size); + } + + pin += ipp.small_block_size + ipp.parity_symbols_per_block; + pout += ipp.small_block_size; + decoded_length += ipp.small_block_size; + } + + if (failed) { + //Debugprintf ("%s:%d: failed = %0x\n", __FILE__, __LINE__, failed); + return (-2); + } + + if (decoded_length != payload_size) { + Debugprintf("IL2P Internal error: decoded_length = %d, payload_size = %d\n", decoded_length, payload_size); + return (-3); + } + + return (decoded_length); + +} // end il2p_decode_payload + +// end il2p_payload.c + + + +struct il2p_context_s { + + enum { IL2P_SEARCHING = 0, IL2P_HEADER, IL2P_PAYLOAD, IL2P_DECODE } state; + + unsigned int acc; // Accumulate most recent 24 bits for sync word matching. + // Lower 8 bits are also used for accumulating bytes for + // the header and payload. + + int bc; // Bit counter so we know when a complete byte has been accumulated. + + int polarity; // 1 if opposite of expected polarity. + + unsigned char shdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; + // Scrambled header as received over the radio. Includes parity. + int hc; // Number if bytes placed in above. + + unsigned char uhdr[IL2P_HEADER_SIZE]; // Header after FEC and unscrambling. + + int eplen; // Encoded payload length. This is not the nuumber from + // from the header but rather the number of encoded bytes to gather. + + unsigned char spayload[IL2P_MAX_ENCODED_PAYLOAD_SIZE]; + // Scrambled and encoded payload as received over the radio. + int pc; // Number of bytes placed in above. + + int corrected; // Number of symbols corrected by RS FEC. +}; + +static struct il2p_context_s *il2p_context[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS]; + + + +/*********************************************************************************** + * + * Name: il2p_rec_bit + * + * Purpose: Extract FX.25 packets from a stream of bits. + * + * Inputs: chan - Channel number. + * + * subchan - This allows multiple demodulators per channel. + * + * slice - Allows multiple slicers per demodulator (subchannel). + * + * dbit - One bit from the received data stream. + * + * Description: This is called once for each received bit. + * For each valid packet, process_rec_frame() is called for further processing. + * It can gather multiple candidates from different parallel demodulators + * ("subchannels") and slicers, then decide which one is the best. + * + ***********************************************************************************/ + +int centreFreq[4] = { 0, 0, 0, 0 }; + +void il2p_rec_bit(int chan, int subchan, int slice, int dbit) +{ + // Allocate context blocks only as needed. + + if (dbit) + dbit = 1; + + struct il2p_context_s *F = il2p_context[chan][subchan][slice]; + if (F == NULL) { + //assert(chan >= 0 && chan < MAX_CHANS); + //assert(subchan >= 0 && subchan < MAX_SUBCHANS); + //assert(slice >= 0 && slice < MAX_SLICERS); + F = il2p_context[chan][subchan][slice] = (struct il2p_context_s *)malloc(sizeof(struct il2p_context_s)); + //assert(F != NULL); + memset(F, 0, sizeof(struct il2p_context_s)); + } + + // Accumulate most recent 24 bits received. Most recent is LSB. + + F->acc = ((F->acc << 1) | (dbit & 1)) & 0x00ffffff; + + // State machine to look for sync word then gather appropriate number of header and payload bytes. + + switch (F->state) { + + case IL2P_SEARCHING: // Searching for the sync word. + + if (__builtin_popcount(F->acc ^ IL2P_SYNC_WORD) <= 1) { // allow single bit mismatch + //text_color_set (DW_COLOR_INFO); + //Debugprintf ("IL2P header has normal polarity\n"); + F->polarity = 0; + F->state = IL2P_HEADER; + F->bc = 0; + F->hc = 0; + + // Determine Centre Freq + + centreFreq[chan] = GuessCentreFreq(chan); + } + else if (__builtin_popcount((~F->acc & 0x00ffffff) ^ IL2P_SYNC_WORD) <= 1) { + // FIXME - this pops up occasionally with random noise. Find better way to convey information. + // This also happens for each slicer - to noisy. + //Debugprintf ("IL2P header has reverse polarity\n"); + F->polarity = 1; + F->state = IL2P_HEADER; + F->bc = 0; + F->hc = 0; + centreFreq[chan] = GuessCentreFreq(chan); + } + + break; + + case IL2P_HEADER: // Gathering the header. + + F->bc++; + if (F->bc == 8) { // full byte has been collected. + F->bc = 0; + if (!F->polarity) { + F->shdr[F->hc++] = F->acc & 0xff; + } + else { + F->shdr[F->hc++] = (~F->acc) & 0xff; + } + if (F->hc == IL2P_HEADER_SIZE + IL2P_HEADER_PARITY) { // Have all of header + + //if (il2p_get_debug() >= 1) + //{ + // Debugprintf("IL2P header as received [%d.%d.%d]:\n", chan, subchan, slice); + // fx_hex_dump(F->shdr, IL2P_HEADER_SIZE + IL2P_HEADER_PARITY); + //} + + // Fix any errors and descramble. + F->corrected = il2p_clarify_header(F->shdr, F->uhdr); + + if (F->corrected >= 0) { // Good header. + // How much payload is expected? + il2p_payload_properties_t plprop; + int hdr_type, max_fec; + int len = il2p_get_header_attributes(F->uhdr, &hdr_type, &max_fec); + + F->eplen = il2p_payload_compute(&plprop, len, max_fec); + + if (il2p_get_debug() >= 1) + { + Debugprintf("Header type %d, max fec = %d", hdr_type, max_fec); + Debugprintf("Need to collect %d encoded bytes for %d byte payload.", F->eplen, len); + Debugprintf("%d small blocks of %d and %d large blocks of %d. %d parity symbols per block", + plprop.small_block_count, plprop.small_block_size, + plprop.large_block_count, plprop.large_block_size, plprop.parity_symbols_per_block); + } + + if (F->eplen >= 1) { // Need to gather payload. + F->pc = 0; + F->state = IL2P_PAYLOAD; + } + else if (F->eplen == 0) { // No payload. + F->pc = 0; + F->state = IL2P_DECODE; + } + else { // Error. + + if (il2p_get_debug() >= 1) { + Debugprintf("IL2P header INVALID.\n"); + } + + F->state = IL2P_SEARCHING; + } + } // good header after FEC. + else { + F->state = IL2P_SEARCHING; // Header failed FEC check. + } + } // entire header has been collected. + } // full byte collected. + break; + + case IL2P_PAYLOAD: // Gathering the payload, if any. + + F->bc++; + if (F->bc == 8) { // full byte has been collected. + F->bc = 0; + if (!F->polarity) { + F->spayload[F->pc++] = F->acc & 0xff; + } + else { + F->spayload[F->pc++] = (~F->acc) & 0xff; + } + if (F->pc == F->eplen) { + + // TODO?: for symmetry it seems like we should clarify the payload before combining. + + F->state = IL2P_DECODE; + } + } + break; + + case IL2P_DECODE: + // We get here after a good header and any payload has been collected. + // Processing is delayed by one bit but I think it makes the logic cleaner. + // During unit testing be sure to send an extra bit to flush it out at the end. + + // in uhdr[IL2P_HEADER_SIZE]; // Header after FEC and descrambling. + + // TODO?: for symmetry, we might decode the payload here and later build the frame. + + { + packet_t pp = il2p_decode_header_payload(F->uhdr, F->spayload, &(F->corrected)); + + if (il2p_get_debug() >= 1) + { + if (pp == NULL) + { + // Most likely too many FEC errors. + Debugprintf("FAILED to construct frame in %s.\n", __func__); + } + } + + if (pp != NULL) { + alevel_t alevel = demod_get_audio_level(chan, subchan); + retry_t retries = F->corrected; + int is_fx25 = 1; // FIXME: distinguish fx.25 and IL2P. + // Currently this just means that a FEC mode was used. + + // TODO: Could we put last 3 arguments in packet object rather than passing around separately? + + multi_modem_process_rec_packet(chan, subchan, slice, pp, alevel, retries, is_fx25, slice, centreFreq[chan]); + } + } // end block for local variables. + + if (il2p_get_debug() >= 1) { + + Debugprintf("-----"); + } + + F->state = IL2P_SEARCHING; + break; + + } // end of switch + +} // end il2p_rec_bit + + + + + + + +// Scramble bits for il2p transmit. + +// Note that there is a delay of 5 until the first bit comes out. +// So we need to need to ignore the first 5 out and stick in +// an extra 5 filler bits to flush at the end. + +#define INIT_TX_LSFR 0x00f + +static inline int scramble_bit(int in, int *state) +{ + int out = ((*state >> 4) ^ *state) & 1; + *state = ((((in ^ *state) & 1) << 9) | (*state ^ ((*state & 1) << 4))) >> 1; + return (out); +} + + +// Undo data scrambling for il2p receive. + +#define INIT_RX_LSFR 0x1f0 + +static inline int descramble_bit(int in, int *state) +{ + int out = (in ^ *state) & 1; + *state = ((*state >> 1) | ((in & 1) << 8)) ^ ((in & 1) << 3); + return (out); +} + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_scramble_block + * + * Purpose: Scramble a block before adding RS parity. + * + * Inputs: in Array of bytes. + * len Number of bytes both in and out. + * + * Outputs: out Array of bytes. + * + *--------------------------------------------------------------------------------*/ + +void il2p_scramble_block(unsigned char *in, unsigned char *out, int len) +{ + int tx_lfsr_state = INIT_TX_LSFR; + + memset(out, 0, len); + + int skipping = 1; // Discard the first 5 out. + int ob = 0; // Index to output byte. + int om = 0x80; // Output bit mask; + for (int ib = 0; ib < len; ib++) { + for (int im = 0x80; im != 0; im >>= 1) { + int s = scramble_bit((in[ib] & im) != 0, &tx_lfsr_state); + if (ib == 0 && im == 0x04) skipping = 0; + if (!skipping) { + if (s) { + out[ob] |= om; + } + om >>= 1; + if (om == 0) { + om = 0x80; + ob++; + } + } + } + } + // Flush it. + + // This is a relic from when I thought the state would need to + // be passed along for the next block. + // Preserve the LSFR state from before flushing. + // This might be needed as the initial state for later payload blocks. + int x = tx_lfsr_state; + for (int n = 0; n < 5; n++) { + int s = scramble_bit(0, &x); + if (s) { + out[ob] |= om; + } + om >>= 1; + if (om == 0) { + om = 0x80; + ob++; + } + } + +} // end il2p_scramble_block + + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_descramble_block + * + * Purpose: Descramble a block after removing RS parity. + * + * Inputs: in Array of bytes. + * len Number of bytes both in and out. + * + * Outputs: out Array of bytes. + * + *--------------------------------------------------------------------------------*/ + +void il2p_descramble_block(unsigned char *in, unsigned char *out, int len) +{ + int rx_lfsr_state = INIT_RX_LSFR; + + memset(out, 0, len); + + for (int b = 0; b < len; b++) { + for (int m = 0x80; m != 0; m >>= 1) { + int d = descramble_bit((in[b] & m) != 0, &rx_lfsr_state); + if (d) { + out[b] |= m; + } + } + } +} + +// end il2p_scramble.c + + + + +static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "il2p_send_frame" + +static void send_bytes(int chan, unsigned char *b, int count, int polarity); +static void send_bit(int chan, int b, int polarity); + + + +/*------------------------------------------------------------- + * + * Name: il2p_send_frame + * + * Purpose: Convert frames to a stream of bits in IL2P format. + * + * Inputs: chan - Audio channel number, 0 = first. + * + * pp - Pointer to packet object. + * + * max_fec - 1 to force 16 parity symbols for each payload block. + * 0 for automatic depending on block size. + * + * polarity - 0 for normal. 1 to invert signal. + * 2 special case for testing - introduce some errors to test FEC. + * + * Outputs: Bits are shipped out by calling tone_gen_put_bit(). + * + * Returns: Number of bits sent including + * - Preamble (01010101...) + * - 3 byte Sync Word. + * - 15 bytes for Header. + * - Optional payload. + * The required time can be calculated by dividing this + * number by the transmit rate of bits/sec. + * -1 is returned for failure. + * + * Description: Generate an IL2P encoded frame. + * + * Assumptions: It is assumed that the tone_gen module has been + * properly initialized so that bits sent with + * tone_gen_put_bit() are processed correctly. + * + * Errors: Return -1 for error. Probably frame too large. + * + * Note: Inconsistency here. ax25 version has just a byte array + * and length going in. Here we need the full packet object. + * + *--------------------------------------------------------------*/ + +string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity) +{ + unsigned char encoded[IL2P_MAX_PACKET_SIZE]; + string * packet = newString(); + int preamblecount; + unsigned char preamble[1024]; + + + encoded[0] = (IL2P_SYNC_WORD >> 16) & 0xff; + encoded[1] = (IL2P_SYNC_WORD >> 8) & 0xff; + encoded[2] = (IL2P_SYNC_WORD) & 0xff; + + int elen = il2p_encode_frame(pp, max_fec, encoded + IL2P_SYNC_WORD_SIZE); + if (elen <= 0) { + Debugprintf("IL2P: Unable to encode frame into IL2P.\n"); + return (packet); + } + + elen += IL2P_SYNC_WORD_SIZE; + + number_of_bits_sent[chan] = 0; + + if (il2p_get_debug() >= 1) { + Debugprintf("IL2P frame, max_fec = %d, %d encoded bytes total", max_fec, elen); +// fx_hex_dump(encoded, elen); + } + + // Send bits to modulator. + + // Try using preaamble for txdelay + + preamblecount = (txdelay[chan] * tx_baudrate[chan]) / 8000; // 8 for bits, 1000 for mS + + if (preamblecount > 1024) + preamblecount = 1024; + + memset(preamble, IL2P_PREAMBLE, preamblecount); + + stringAdd(packet, preamble, preamblecount); + stringAdd(packet, encoded, elen); + + tx_fx25_size[chan] = packet->Length * 8; + + return packet; +} + + + +// TX Code. Builds whole packet then sends a bit at a time + +#define TX_SILENCE 0 +#define TX_DELAY 1 +#define TX_TAIL 2 +#define TX_NO_DATA 3 +#define TX_FRAME 4 +#define TX_WAIT_BPF 5 + + +#define TX_BIT0 0 +#define TX_BIT1 1 +#define FRAME_EMPTY 0 +#define FRAME_FULL 1 +#define FRAME_NO_FRAME 2 +#define FRAME_NEW_FRAME 3 +#define BYTE_EMPTY 0 +#define BYTE_FULL 1 + +extern UCHAR tx_frame_status[5]; +extern UCHAR tx_byte_status[5]; +extern string * tx_data[5]; +extern int tx_data_len[5]; +extern UCHAR tx_bit_stream[5]; +extern UCHAR tx_bit_cnt[5]; +extern long tx_tail_cnt[5]; +extern BOOL tx_bs_bit[5]; + +string * fill_il2p_data(int snd_ch, string * data) +{ + string * result; + packet_t pp = ax25_new(); + + + // Call il2p_send_frame to build the bit stream + + pp->frame_len = data->Length - 2; // Included CRC + memcpy(pp->frame_data, data->Data, data->Length); + + result = il2p_send_frame(snd_ch, pp, 1, 0); + + return result; +} + + + +void il2p_get_new_frame(int snd_ch, TStringList * frame_stream) +{ + string * myTemp; + + tx_bs_bit[snd_ch] = 0; + tx_bit_cnt[snd_ch] = 0; + tx_fx25_size_cnt[snd_ch] = 0; + tx_fx25_size[snd_ch] = 1; + tx_frame_status[snd_ch] = FRAME_NEW_FRAME; + tx_byte_status[snd_ch] = BYTE_EMPTY; + + if (frame_stream->Count == 0) + tx_frame_status[snd_ch] = FRAME_NO_FRAME; + else + { + // We now pass control byte and ack bytes on front and pointer to socket on end if ackmode + + myTemp = Strings(frame_stream, 0); // get message + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[snd_ch], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + AGW_AX25_frame_analiz(snd_ch, FALSE, myTemp); + put_frame(snd_ch, myTemp, "", TRUE, FALSE); + + tx_data[snd_ch] = fill_il2p_data(snd_ch, myTemp); + + Delete(frame_stream, 0); // This will invalidate temp + } +} + + + +// Original code + +/* +static void send_bytes(int chan, unsigned char *b, int count, int polarity) +{ + for (int j = 0; j < count; j++) { + unsigned int x = b[j]; + for (int k = 0; k < 8; k++) { + send_bit(chan, (x & 0x80) != 0, polarity); + x <<= 1; + } + } +} + +// NRZI would be applied for AX.25 but IL2P does not use it. +// However we do have an option to invert the signal. +// The direwolf receive implementation will automatically compensate +// for either polarity but other implementations might not. + +static void send_bit(int chan, int b, int polarity) +{ + tone_gen_put_bit(chan, (b ^ polarity) & 1); + number_of_bits_sent[chan]++; +} +*/ + + + + +int il2p_get_new_bit(int snd_ch, Byte bit) +{ + string *s; + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + { + il2p_get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + if (tx_frame_status[snd_ch] == FRAME_NEW_FRAME) + tx_frame_status[snd_ch] = FRAME_FULL; + } + + if (tx_frame_status[snd_ch] == FRAME_FULL) + { + if (tx_byte_status[snd_ch] == BYTE_EMPTY) + { + if (tx_data[snd_ch]->Length) + { + s = tx_data[snd_ch]; + + tx_bit_stream[snd_ch] = s->Data[0]; + tx_frame_status[snd_ch] = FRAME_FULL; + tx_byte_status[snd_ch] = BYTE_FULL; + tx_bit_cnt[snd_ch] = 0; + mydelete(tx_data[snd_ch], 0, 1); + } + else + tx_frame_status[snd_ch] = FRAME_EMPTY; + } + if (tx_byte_status[snd_ch] == BYTE_FULL) + { + // il2p sends high order bit first + + bit = tx_bit_stream[snd_ch] >> 7; // top bit to bottom + + tx_bit_stream[snd_ch] = tx_bit_stream[snd_ch] << 1; + tx_bit_cnt[snd_ch]++; + tx_fx25_size_cnt[snd_ch]++; + if (tx_bit_cnt[snd_ch] >= 8) + tx_byte_status[snd_ch] = BYTE_EMPTY; + if (tx_fx25_size_cnt[snd_ch] == tx_fx25_size[snd_ch]) + tx_frame_status[snd_ch] = FRAME_EMPTY; + } + } + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + { + il2p_get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + + switch (tx_frame_status[snd_ch]) + { + case FRAME_NEW_FRAME: + tx_frame_status[snd_ch] = FRAME_FULL; + break; + + case FRAME_NO_FRAME: + tx_tail_cnt[snd_ch] = 0; + tx_frame_status[snd_ch] = FRAME_EMPTY; + tx_status[snd_ch] = TX_TAIL; + break; + } + } + return bit; +} + + + diff --git a/il2p.c.bak b/il2p.c.bak new file mode 100644 index 0000000..2c81201 --- /dev/null +++ b/il2p.c.bak @@ -0,0 +1,4502 @@ +/* +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 + +// IL2P code. Based on Direwolf code, under the following copyright + +// +// Copyright (C) 2021 John Langner, WB2OSZ +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + + +// IP2P receive code (il2p_rec_bit) is called from the bit receiving code in ax25_demod.c, so includes parallel decoders + + + + +#include "UZ7HOStuff.h" + +void Debugprintf(const char * format, ...); + +#define MAX_ADEVS 3 + +#define MAX_RADIO_CHANS ((MAX_ADEVS) * 2) + +#define MAX_CHANS MAX_RADIO_CHANS // TODO: Replace all former with latter to avoid confusion with following. + +#define MAX_TOTAL_CHANS 16 // v1.7 allows additional virtual channels which are connected + // to something other than radio modems. + // Total maximum channels is based on the 4 bit KISS field. + // Someone with very unusual requirements could increase this and + // use only the AGW network protocol. + + +#define MAX_SUBCHANS 9 + +#define MAX_SLICERS 9 + +#define max(x, y) ((x) > (y) ? (x) : (y)) +#define min(x, y) ((x) < (y) ? (x) : (y)) + +/* For option to try fixing frames with bad CRC. */ + +typedef enum retry_e { + RETRY_NONE = 0, + RETRY_INVERT_SINGLE = 1, + RETRY_INVERT_DOUBLE = 2, + RETRY_INVERT_TRIPLE = 3, + RETRY_INVERT_TWO_SEP = 4, + RETRY_MAX = 5 +} retry_t; + +typedef struct alevel_s { + int rec; + int mark; + int space; + //float ms_ratio; // TODO: take out after temporary investigation. +} alevel_t; + + +alevel_t demod_get_audio_level(int chan, int subchan); +void tone_gen_put_bit(int chan, int dat); + +int ax25memdebug = 1; + +// Code to try to determine centre freq + +float MagOut[4096]; +float MaxMagOut = 0; +int MaxMagIndex = 0; + +// FFT Bin Size is 12000 / FFTSize + +#ifndef FX25_H +#define FX25_H + +#include // for uint64_t + + +/* Reed-Solomon codec control block */ +struct rs { + unsigned int mm; /* Bits per symbol */ + unsigned int nn; /* Symbols per block (= (1<mm) +#define NN (rs->nn) +#define ALPHA_TO (rs->alpha_to) +#define INDEX_OF (rs->index_of) +#define GENPOLY (rs->genpoly) +#define NROOTS (rs->nroots) +#define FCR (rs->fcr) +#define PRIM (rs->prim) +#define IPRIM (rs->iprim) +#define A0 (NN) + +int __builtin_popcountll(unsigned long long int i) +{ + return 0; +} + +int __builtin_popcount(unsigned int n) +{ + unsigned int count = 0; + while (n) + { + count += n & 1; + n >>= 1; + } + return count; +} + +static inline int modnn(struct rs *rs, int x) { + while (x >= rs->nn) { + x -= rs->nn; + x = (x >> rs->mm) + (x & rs->nn); + } + return x; +} + +#define MODNN(x) modnn(rs,x) + + +#define ENCODE_RS encode_rs_char +#define DECODE_RS decode_rs_char +#define INIT_RS init_rs_char +#define FREE_RS free_rs_char + +#define DTYPE unsigned char + +void ENCODE_RS(struct rs *rs, DTYPE *data, DTYPE *bb); + +int DECODE_RS(struct rs *rs, DTYPE *data, int *eras_pos, int no_eras); + +struct rs *INIT_RS(unsigned int symsize, unsigned int gfpoly, + unsigned int fcr, unsigned int prim, unsigned int nroots); + +void FREE_RS(struct rs *rs); + + + +// These 3 are the external interface. +// Maybe these should be in a different file, separated from the internal stuff. + +void fx25_init(int debug_level); +int fx25_send_frame(int chan, unsigned char *fbuf, int flen, int fx_mode); +void fx25_rec_bit(int chan, int subchan, int slice, int dbit); +int fx25_rec_busy(int chan); + + +// Other functions in fx25_init.c. + +struct rs *fx25_get_rs(int ctag_num); +uint64_t fx25_get_ctag_value(int ctag_num); +int fx25_get_k_data_radio(int ctag_num); +int fx25_get_k_data_rs(int ctag_num); +int fx25_get_nroots(int ctag_num); +int fx25_get_debug(void); +int fx25_tag_find_match(uint64_t t); +int fx25_pick_mode(int fx_mode, int dlen); + +void fx_hex_dump(unsigned char *x, int len); + +/*------------------------------------------------------------------- + * + * Name: ax25_pad.h + * + * Purpose: Header file for using ax25_pad.c + * + *------------------------------------------------------------------*/ + +#ifndef AX25_PAD_H +#define AX25_PAD_H 1 + + +#define AX25_MAX_REPEATERS 8 +#define AX25_MIN_ADDRS 2 /* Destination & Source. */ +#define AX25_MAX_ADDRS 10 /* Destination, Source, 8 digipeaters. */ + +#define AX25_DESTINATION 0 /* Address positions in frame. */ +#define AX25_SOURCE 1 +#define AX25_REPEATER_1 2 +#define AX25_REPEATER_2 3 +#define AX25_REPEATER_3 4 +#define AX25_REPEATER_4 5 +#define AX25_REPEATER_5 6 +#define AX25_REPEATER_6 7 +#define AX25_REPEATER_7 8 +#define AX25_REPEATER_8 9 + +#define AX25_MAX_ADDR_LEN 12 /* In theory, you would expect the maximum length */ + /* to be 6 letters, dash, 2 digits, and nul for a */ + /* total of 10. However, object labels can be 10 */ + /* characters so throw in a couple extra bytes */ + /* to be safe. */ + +#define AX25_MIN_INFO_LEN 0 /* Previously 1 when considering only APRS. */ + +#define AX25_MAX_INFO_LEN 2048 /* Maximum size for APRS. */ + /* AX.25 starts out with 256 as the default max */ + /* length but the end stations can negotiate */ + /* something different. */ + /* version 0.8: Change from 256 to 2028 to */ + /* handle the larger paclen for Linux AX25. */ + + /* These don't include the 2 bytes for the */ + /* HDLC frame FCS. */ + +/* + * Previously, for APRS only. + * #define AX25_MIN_PACKET_LEN ( 2 * 7 + 2 + AX25_MIN_INFO_LEN) + * #define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + AX25_MAX_INFO_LEN) + */ + + /* The more general case. */ + /* An AX.25 frame can have a control byte and no protocol. */ + +#define AX25_MIN_PACKET_LEN ( 2 * 7 + 1 ) + +#define AX25_MAX_PACKET_LEN ( AX25_MAX_ADDRS * 7 + 2 + 3 + AX25_MAX_INFO_LEN) + + +/* + * packet_t is a pointer to a packet object. + * + * The actual implementation is not visible outside ax25_pad.c. + */ + +#define AX25_UI_FRAME 3 /* Control field value. */ + +#define AX25_PID_NO_LAYER_3 0xf0 /* protocol ID used for APRS */ +#define AX25_PID_SEGMENTATION_FRAGMENT 0x08 +#define AX25_PID_ESCAPE_CHARACTER 0xff + +struct packet_s { + + int magic1; /* for error checking. */ + + int seq; /* unique sequence number for debugging. */ + + double release_time; /* Time stamp in format returned by dtime_now(). */ + /* When to release from the SATgate mode delay queue. */ + +#define MAGIC 0x41583235 + + struct packet_s *nextp; /* Pointer to next in queue. */ + + int num_addr; /* Number of addresses in frame. */ + /* Range of AX25_MIN_ADDRS .. AX25_MAX_ADDRS for AX.25. */ + /* It will be 0 if it doesn't look like AX.25. */ + /* -1 is used temporarily at allocation to mean */ + /* not determined yet. */ + + + + /* + * The 7th octet of each address contains: + * + * Bits: H R R SSID 0 + * + * H for digipeaters set to 0 initially. + * Changed to 1 when position has been used. + * + * for source & destination it is called + * command/response. Normally both 1 for APRS. + * They should be opposites for connected mode. + * + * R R Reserved. Normally set to 1 1. + * + * SSID Substation ID. Range of 0 - 15. + * + * 0 Usually 0 but 1 for last address. + */ + + +#define SSID_H_MASK 0x80 +#define SSID_H_SHIFT 7 + +#define SSID_RR_MASK 0x60 +#define SSID_RR_SHIFT 5 + +#define SSID_SSID_MASK 0x1e +#define SSID_SSID_SHIFT 1 + +#define SSID_LAST_MASK 0x01 + + + int frame_len; /* Frame length without CRC. */ + + int modulo; /* I & S frames have sequence numbers of either 3 bits (modulo 8) */ + /* or 7 bits (modulo 128). This is conveyed by either 1 or 2 */ + /* control bytes. Unfortunately, we can't determine this by looking */ + /* at an isolated frame. We need to know about the context. If we */ + /* are part of the conversation, we would know. But if we are */ + /* just listening to others, this would be more difficult to determine. */ + + /* For U frames: set to 0 - not applicable */ + /* For I & S frames: 8 or 128 if known. 0 if unknown. */ + + unsigned char frame_data[AX25_MAX_PACKET_LEN + 1]; + /* Raw frame contents, without the CRC. */ + + + int magic2; /* Will get stomped on if above overflows. */ +}; + + + +typedef struct packet_s *packet_t; + +typedef enum cmdres_e { cr_00 = 2, cr_cmd = 1, cr_res = 0, cr_11 = 3 } cmdres_t; + + +extern packet_t ax25_new(void); + + +#ifdef AX25_PAD_C /* Keep this hidden - implementation could change. */ + + +/* + * APRS always has one control octet of 0x03 but the more + * general AX.25 case is one or two control bytes depending on + * whether "modulo 128 operation" is in effect. + */ + + //#define DEBUGX 1 + +static inline int ax25_get_control_offset(packet_t this_p) +{ + return (this_p->num_addr * 7); +} + +static inline int ax25_get_num_control(packet_t this_p) +{ + int c; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0) { /* I xxxx xxx0 */ +#if DEBUGX + Debugprintf("ax25_get_num_control, %02x is I frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1); +#endif + return ((this_p->modulo == 128) ? 2 : 1); + } + + if ((c & 0x03) == 1) { /* S xxxx xx01 */ +#if DEBUGX + Debugprintf("ax25_get_num_control, %02x is S frame, returns %d\n", c, (this_p->modulo == 128) ? 2 : 1); +#endif + return ((this_p->modulo == 128) ? 2 : 1); + } + +#if DEBUGX + Debugprintf("ax25_get_num_control, %02x is U frame, always returns 1.\n", c); +#endif + + return (1); /* U xxxx xx11 */ +} + + + +/* + * APRS always has one protocol octet of 0xF0 meaning no level 3 + * protocol but the more general case is 0, 1 or 2 protocol ID octets. + */ + +static inline int ax25_get_pid_offset(packet_t this_p) +{ + return (ax25_get_control_offset(this_p) + ax25_get_num_control(this_p)); +} + +static int ax25_get_num_pid(packet_t this_p) +{ + int c; + int pid; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0 || /* I xxxx xxx0 */ + c == 0x03 || c == 0x13) { /* UI 000x 0011 */ + + pid = this_p->frame_data[ax25_get_pid_offset(this_p)]; +#if DEBUGX + Debugprintf("ax25_get_num_pid, %02x is I or UI frame, pid = %02x, returns %d\n", c, pid, (pid == AX25_PID_ESCAPE_CHARACTER) ? 2 : 1); +#endif + if (pid == AX25_PID_ESCAPE_CHARACTER) { + return (2); /* pid 1111 1111 means another follows. */ + } + return (1); + } +#if DEBUGX + Debugprintf("ax25_get_num_pid, %02x is neither I nor UI frame, returns 0\n", c); +#endif + return (0); +} + + +/* + * AX.25 has info field for 5 frame types depending on the control field. + * + * xxxx xxx0 I + * 000x 0011 UI (which includes APRS) + * 101x 1111 XID + * 111x 0011 TEST + * 100x 0111 FRMR + * + * APRS always has an Information field with at least one octet for the Data Type Indicator. + */ + +static inline int ax25_get_info_offset(packet_t this_p) +{ + int offset = ax25_get_control_offset(this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p); +#if DEBUGX + Debugprintf("ax25_get_info_offset, returns %d\n", offset); +#endif + return (offset); +} + +static inline int ax25_get_num_info(packet_t this_p) +{ + int len; + + /* assuming AX.25 frame. */ + + len = this_p->frame_len - this_p->num_addr * 7 - ax25_get_num_control(this_p) - ax25_get_num_pid(this_p); + if (len < 0) { + len = 0; /* print error? */ + } + + return (len); +} + +#endif + + +typedef enum ax25_modulo_e { modulo_unknown = 0, modulo_8 = 8, modulo_128 = 128 } ax25_modulo_t; + +typedef enum ax25_frame_type_e { + + frame_type_I = 0, // Information + + frame_type_S_RR, // Receive Ready - System Ready To Receive + frame_type_S_RNR, // Receive Not Ready - TNC Buffer Full + frame_type_S_REJ, // Reject Frame - Out of Sequence or Duplicate + frame_type_S_SREJ, // Selective Reject - Request single frame repeat + + frame_type_U_SABME, // Set Async Balanced Mode, Extended + frame_type_U_SABM, // Set Async Balanced Mode + frame_type_U_DISC, // Disconnect + frame_type_U_DM, // Disconnect Mode + frame_type_U_UA, // Unnumbered Acknowledge + frame_type_U_FRMR, // Frame Reject + frame_type_U_UI, // Unnumbered Information + frame_type_U_XID, // Exchange Identification + frame_type_U_TEST, // Test + frame_type_U, // other Unnumbered, not used by AX.25. + + frame_not_AX25 // Could not get control byte from frame. + // This must be last because value plus 1 is + // for the size of an array. + +} ax25_frame_type_t; + + +/* + * Originally this was a single number. + * Let's try something new in version 1.2. + * Also collect AGC values from the mark and space filters. + */ + +#ifndef AXTEST +// TODO: remove this? +#define AX25MEMDEBUG 1 +#endif + + + +extern packet_t ax25_from_text(char *monitor, int strict); + +extern packet_t ax25_from_frame(unsigned char *data, int len, alevel_t alevel); + +extern packet_t ax25_dup(packet_t copy_from); + +extern void ax25_delete(packet_t pp); + + + +extern int ax25_parse_addr(int position, char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard); +extern int ax25_check_addresses(packet_t pp); + +extern packet_t ax25_unwrap_third_party(packet_t from_pp); + +extern void ax25_set_addr(packet_t pp, int, char *); +extern void ax25_insert_addr(packet_t this_p, int n, char *ad); +extern void ax25_remove_addr(packet_t this_p, int n); + +extern int ax25_get_num_addr(packet_t pp); +extern int ax25_get_num_repeaters(packet_t this_p); + +extern void ax25_get_addr_with_ssid(packet_t pp, int n, char *station); +extern void ax25_get_addr_no_ssid(packet_t pp, int n, char *station); + +extern int ax25_get_ssid(packet_t pp, int n); +extern void ax25_set_ssid(packet_t this_p, int n, int ssid); + +extern int ax25_get_h(packet_t pp, int n); + +extern void ax25_set_h(packet_t pp, int n); + +extern int ax25_get_heard(packet_t this_p); + +extern int ax25_get_first_not_repeated(packet_t pp); + +extern int ax25_get_rr(packet_t this_p, int n); + +extern int ax25_get_info(packet_t pp, unsigned char **paddr); +extern void ax25_set_info(packet_t pp, unsigned char *info_ptr, int info_len); +extern int ax25_cut_at_crlf(packet_t this_p); + +extern void ax25_set_nextp(packet_t this_p, packet_t next_p); + +extern int ax25_get_dti(packet_t this_p); + +extern packet_t ax25_get_nextp(packet_t this_p); + +extern void ax25_set_release_time(packet_t this_p, double release_time); +extern double ax25_get_release_time(packet_t this_p); + +extern void ax25_set_modulo(packet_t this_p, int modulo); +extern int ax25_get_modulo(packet_t this_p); + +extern void ax25_format_addrs(packet_t pp, char *); +extern void ax25_format_via_path(packet_t this_p, char *result, size_t result_size); + +extern int ax25_pack(packet_t pp, unsigned char result[AX25_MAX_PACKET_LEN]); + +extern ax25_frame_type_t ax25_frame_type(packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns); + +extern void ax25_hex_dump(packet_t this_p); + +extern int ax25_is_aprs(packet_t pp); +extern int ax25_is_null_frame(packet_t this_p); + +extern int ax25_get_control(packet_t this_p); +extern int ax25_get_c2(packet_t this_p); + +extern int ax25_get_pid(packet_t this_p); + +extern int ax25_get_frame_len(packet_t this_p); +extern unsigned char *ax25_get_frame_data_ptr(packet_t this_p); + +extern unsigned short ax25_dedupe_crc(packet_t pp); + +extern unsigned short ax25_m_m_crc(packet_t pp); + +extern void ax25_safe_print(char *, int, int ascii_only); + +#define AX25_ALEVEL_TO_TEXT_SIZE 40 // overkill but safe. +extern int ax25_alevel_to_text(alevel_t alevel, char text[AX25_ALEVEL_TO_TEXT_SIZE]); + + +#endif /* AX25_PAD_H */ + +/* end ax25_pad.h */ + + + + +#define CTAG_MIN 0x01 +#define CTAG_MAX 0x0B + +// Maximum sizes of "data" and "check" parts. + +#define FX25_MAX_DATA 239 // i.e. RS(255,239) +#define FX25_MAX_CHECK 64 // e.g. RS(255, 191) +#define FX25_BLOCK_SIZE 255 // Block size always 255 for 8 bit symbols. + +#endif // FX25_H + +#ifndef IL2P_H +#define IL2P_H 1 + + +#define IL2P_PREAMBLE 0x55 + +#define IL2P_SYNC_WORD 0xF15E48 + +#define IL2P_SYNC_WORD_SIZE 3 +#define IL2P_HEADER_SIZE 13 // Does not include 2 parity. +#define IL2P_HEADER_PARITY 2 + +#define IL2P_MAX_PAYLOAD_SIZE 1023 +#define IL2P_MAX_PAYLOAD_BLOCKS 5 +#define IL2P_MAX_PARITY_SYMBOLS 16 // For payload only. +#define IL2P_MAX_ENCODED_PAYLOAD_SIZE (IL2P_MAX_PAYLOAD_SIZE + IL2P_MAX_PAYLOAD_BLOCKS * IL2P_MAX_PARITY_SYMBOLS) + +#define IL2P_MAX_PACKET_SIZE (IL2P_SYNC_WORD_SIZE + IL2P_HEADER_SIZE + IL2P_HEADER_PARITY + IL2P_MAX_ENCODED_PAYLOAD_SIZE) + + +float GuessCentreFreq(int i) +{ + float Freq = 0; + float Start; + float End; + int n; + float Max = 0; + int Index = 0; + + Start = (rx_freq[i] - RCVR[i] * rcvr_offset[i]) / BinSize; + End = (rx_freq[i] + RCVR[i] * rcvr_offset[i]) / BinSize; + + for (n = Start; n <= End; n++) + { + if (MagOut[n] > Max) + { + Max = MagOut[n]; + Index = n; + } + } + + Freq = Index * BinSize; + + return Freq; +} + +/*------------------------------------------------------------------------------ + * + * Name: ax25_new + * + * Purpose: Allocate memory for a new packet object. + * + * Returns: Identifier for a new packet object. + * In the current implementation this happens to be a pointer. + * + *------------------------------------------------------------------------------*/ + +int last_seq_num = 0; +int new_count = 0; +int delete_count = 0; + +packet_t ax25_new(void) +{ + struct packet_s *this_p; + + +#if DEBUG + text_color_set(DW_COLOR_DEBUG); + Debugprintf("ax25_new(): before alloc, new=%d, delete=%d\n", new_count, delete_count); +#endif + + last_seq_num++; + new_count++; + + /* + * check for memory leak. + */ + + // version 1.4 push up the threshold. We could have considerably more with connected mode. + + //if (new_count > delete_count + 100) { + if (new_count > delete_count + 256) { + + Debugprintf("Report to WB2OSZ - Memory leak for packet objects. new=%d, delete=%d\n", new_count, delete_count); +#if AX25MEMDEBUG +#endif + } + + this_p = calloc(sizeof(struct packet_s), (size_t)1); + + if (this_p == NULL) { + Debugprintf("ERROR - can't allocate memory in ax25_new.\n"); + } + +// assert(this_p != NULL); + + this_p->magic1 = MAGIC; + this_p->seq = last_seq_num; + this_p->magic2 = MAGIC; + this_p->num_addr = (-1); + + return (this_p); +} + +/*------------------------------------------------------------------------------ + * + * Name: ax25_delete + * + * Purpose: Destroy a packet object, freeing up memory it was using. + * + *------------------------------------------------------------------------------*/ + +void ax25_delete(packet_t this_p) +{ + if (this_p == NULL) { + Debugprintf("ERROR - NULL pointer passed to ax25_delete.\n"); + return; + } + + delete_count++; + +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + this_p->magic1 = 0; + this_p->magic1 = 0; + + free(this_p); +} + + + + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_s_frame + * + * Purpose: Construct an S frame. + * + * Input: addrs - Array of addresses. + * + * num_addr - Number of addresses, range 2 .. 10. + * + * cr - cr_cmd command frame, cr_res for a response frame. + * + * ftype - One of: + * frame_type_S_RR, // Receive Ready - System Ready To Receive + * frame_type_S_RNR, // Receive Not Ready - TNC Buffer Full + * frame_type_S_REJ, // Reject Frame - Out of Sequence or Duplicate + * frame_type_S_SREJ, // Selective Reject - Request single frame repeat + * + * modulo - 8 or 128. Determines if we have 1 or 2 control bytes. + * + * nr - N(R) field --- describe. + * + * pf - Poll/Final flag. + * + * pinfo - Pointer to data for Info field. Allowed only for SREJ. + * + * info_len - Length for Info field. + * + * + * Returns: Pointer to new packet object. + * + *------------------------------------------------------------------------------*/ + + +packet_t ax25_s_frame(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int modulo, int nr, int pf, unsigned char *pinfo, int info_len) +{ + packet_t this_p; + unsigned char *p; + int ctrl = 0; + + this_p = ax25_new(); + + if (this_p == NULL) return (NULL); + + if (!set_addrs(this_p, addrs, num_addr, cr)) { + Debugprintf("Internal error in %s: Could not set addresses for S frame.\n", __func__); + ax25_delete(this_p); + return (NULL); + } + + if (modulo != 8 && modulo != 128) { + Debugprintf("Internal error in %s: Invalid modulo %d for S frame.\n", __func__, modulo); + modulo = 8; + } + this_p->modulo = modulo; + + if (nr < 0 || nr >= modulo) { + Debugprintf("Internal error in %s: Invalid N(R) %d for S frame.\n", __func__, nr); + nr &= (modulo - 1); + } + + // Erratum: The AX.25 spec is not clear about whether SREJ should be command, response, or both. + // The underlying X.25 spec clearly says it is response only. Let's go with that. + + if (ftype == frame_type_S_SREJ && cr != cr_res) { + Debugprintf("Internal error in %s: SREJ must be response.\n", __func__); + } + + switch (ftype) { + + case frame_type_S_RR: ctrl = 0x01; break; + case frame_type_S_RNR: ctrl = 0x05; break; + case frame_type_S_REJ: ctrl = 0x09; break; + case frame_type_S_SREJ: ctrl = 0x0d; break; + + default: + Debugprintf("Internal error in %s: Invalid ftype %d for S frame.\n", __func__, ftype); + ax25_delete(this_p); + return (NULL); + break; + } + + p = this_p->frame_data + this_p->frame_len; + + if (modulo == 8) { + if (pf) ctrl |= 0x10; + ctrl |= nr << 5; + *p++ = ctrl; + this_p->frame_len++; + } + else { + *p++ = ctrl; + this_p->frame_len++; + + ctrl = pf & 1; + ctrl |= nr << 1; + *p++ = ctrl; + this_p->frame_len++; + } + + if (ftype == frame_type_S_SREJ) { + if (pinfo != NULL && info_len > 0) { + if (info_len > AX25_MAX_INFO_LEN) { + Debugprintf("Internal error in %s: SREJ frame, Invalid information field length %d.\n", __func__, info_len); + info_len = AX25_MAX_INFO_LEN; + } + memcpy(p, pinfo, info_len); + p += info_len; + this_p->frame_len += info_len; + } + } + else { + if (pinfo != NULL || info_len != 0) { + Debugprintf("Internal error in %s: Info part not allowed for RR, RNR, REJ frame.\n", __func__); + } + } + *p = '\0'; + + + return (this_p); + +} /* end ax25_s_frame */ + + + + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_i_frame + * + * Purpose: Construct an I frame. + * + * Input: addrs - Array of addresses. + * + * num_addr - Number of addresses, range 2 .. 10. + * + * cr - cr_cmd command frame, cr_res for a response frame. + * + * modulo - 8 or 128. + * + * nr - N(R) field --- describe. + * + * ns - N(S) field --- describe. + * + * pf - Poll/Final flag. + * + * pid - Protocol ID. + * Normally 0xf0 meaning no level 3. + * Could be other values for NET/ROM, etc. + * + * pinfo - Pointer to data for Info field. + * + * info_len - Length for Info field. + * + * + * Returns: Pointer to new packet object. + * + *------------------------------------------------------------------------------*/ + +packet_t ax25_i_frame(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, int modulo, int nr, int ns, int pf, int pid, unsigned char *pinfo, int info_len) +{ + packet_t this_p; + unsigned char *p; + int ctrl = 0; + + this_p = ax25_new(); + + if (this_p == NULL) return (NULL); + + if (!set_addrs(this_p, addrs, num_addr, cr)) { + Debugprintf("Internal error in %s: Could not set addresses for I frame.\n", __func__); + ax25_delete(this_p); + return (NULL); + } + + if (modulo != 8 && modulo != 128) { + Debugprintf("Internal error in %s: Invalid modulo %d for I frame.\n", __func__, modulo); + modulo = 8; + } + this_p->modulo = modulo; + + if (nr < 0 || nr >= modulo) { + Debugprintf("Internal error in %s: Invalid N(R) %d for I frame.\n", __func__, nr); + nr &= (modulo - 1); + } + + if (ns < 0 || ns >= modulo) { + Debugprintf("Internal error in %s: Invalid N(S) %d for I frame.\n", __func__, ns); + ns &= (modulo - 1); + } + + p = this_p->frame_data + this_p->frame_len; + + if (modulo == 8) { + ctrl = (nr << 5) | (ns << 1); + if (pf) ctrl |= 0x10; + *p++ = ctrl; + this_p->frame_len++; + } + else { + ctrl = ns << 1; + *p++ = ctrl; + this_p->frame_len++; + + ctrl = nr << 1; + if (pf) ctrl |= 0x01; + *p++ = ctrl; + this_p->frame_len++; + } + + // Definitely don't want pid value of 0 (not in valid list) + // or 0xff (which means more bytes follow). + + if (pid < 0 || pid == 0 || pid == 0xff) { + Debugprintf("Warning: Client application provided invalid PID value, 0x%02x, for I frame.\n", pid); + pid = AX25_PID_NO_LAYER_3; + } + *p++ = pid; + this_p->frame_len++; + + if (pinfo != NULL && info_len > 0) { + if (info_len > AX25_MAX_INFO_LEN) { + Debugprintf("Internal error in %s: I frame, Invalid information field length %d.\n", __func__, info_len); + info_len = AX25_MAX_INFO_LEN; + } + memcpy(p, pinfo, info_len); + p += info_len; + this_p->frame_len += info_len; + } + + *p = '\0'; + + + return (this_p); + +} /* end ax25_i_frame */ + + + + + +extern TStringList detect_list[5]; +extern TStringList detect_list_c[5]; + +void multi_modem_process_rec_packet(int snd_ch, int subchan, int slice, packet_t pp, alevel_t alevel, retry_t retries, int is_fx25, int emph, int centreFreq) +{ + // Convert to QtSM internal format + + struct TDetector_t * pDET = &DET[emph][subchan]; + string * data = newString(); + char Mode[16] = "IL2P"; + + sprintf(Mode, "IL2P %d", centreFreq); + + stringAdd(data, pp->frame_data, pp->frame_len + 2); // QTSM assumes a CRC + + ax25_delete(pp); + + if (retries) + { + pDET->rx_decoded = decodedFEC; + pDET->emph_decoded = decodedFEC; + pDET->errors = retries; + } + else + { + pDET->rx_decoded = decodedNormal; + pDET->emph_decoded = decodedNormal; + pDET->errors = 0; + } + + if (detect_list[snd_ch].Count > 0 && + my_indexof(&detect_list[snd_ch], data) >= 0) + { + // Already have a copy of this frame + + freeString(data); + Debugprintf("Discarding copy rcvr %d emph %d", subchan, 0); + return; + } + + string * xx = newString(); + memset(xx->Data, 0, 16); + + Add(&detect_list_c[snd_ch], xx); + Add(&detect_list[snd_ch], data); + +// if (retries) +// sprintf(Mode, "IP2P-%d", retries); + + stringAdd(xx, Mode, strlen(Mode)); + return; + +} + + + + +alevel_t demod_get_audio_level(int chan, int subchan) +{ + alevel_t alevel; + alevel.rec = 0; + alevel.mark = 0; + alevel.space = 0; + return (alevel); +} + +void ax25_hex_dump(packet_t this_p) +{} + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_from_frame + * + * Purpose: Split apart an HDLC frame to components. + * + * Inputs: fbuf - Pointer to beginning of frame. + * + * flen - Length excluding the two FCS bytes. + * + * alevel - Audio level of received signal. + * Maximum range 0 - 100. + * -1 might be used when not applicable. + * + * Returns: Pointer to new packet object or NULL if error. + * + * Outputs: Use the "get" functions to retrieve information in different ways. + * + *------------------------------------------------------------------------------*/ + + +packet_t ax25_from_frame(unsigned char *fbuf, int flen, alevel_t alevel) +{ + packet_t this_p; + + + /* + * First make sure we have an acceptable length: + * + * We are not concerned with the FCS (CRC) because someone else checked it. + * + * Is is possible to have zero length for info? + * + * In the original version, assuming APRS, the answer was no. + * We always had at least 3 octets after the address part: + * control, protocol, and first byte of info part for data type. + * + * In later versions, this restriction was relaxed so other + * variations of AX.25 could be used. Now the minimum length + * is 7+7 for addresses plus 1 for control. + * + */ + + + if (flen < AX25_MIN_PACKET_LEN || flen > AX25_MAX_PACKET_LEN) + { + Debugprintf("Frame length %d not in allowable range of %d to %d.", flen, AX25_MIN_PACKET_LEN, AX25_MAX_PACKET_LEN); + return (NULL); + } + + this_p = ax25_new(); + + /* Copy the whole thing intact. */ + + memcpy(this_p->frame_data, fbuf, flen); + this_p->frame_data[flen] = 0; + this_p->frame_len = flen; + + /* Find number of addresses. */ + + this_p->num_addr = (-1); + (void)ax25_get_num_addr(this_p); + + return (this_p); +} + + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_get_num_addr + * + * Purpose: Return number of addresses in current packet. + * + * Assumption: ax25_from_text or ax25_from_frame was called first. + * + * Returns: Number of addresses in the current packet. + * Should be in the range of 2 .. AX25_MAX_ADDRS. + * + * Version 0.9: Could be zero for a non AX.25 frame in KISS mode. + * + *------------------------------------------------------------------------------*/ + +int ax25_get_num_addr(packet_t this_p) +{ + //unsigned char *pf; + int a; + int addr_bytes; + + +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + /* Use cached value if already set. */ + + if (this_p->num_addr >= 0) { + return (this_p->num_addr); + } + + /* Otherwise, determine the number ofaddresses. */ + + this_p->num_addr = 0; /* Number of addresses extracted. */ + + addr_bytes = 0; + for (a = 0; a < this_p->frame_len && addr_bytes == 0; a++) { + if (this_p->frame_data[a] & SSID_LAST_MASK) { + addr_bytes = a + 1; + } + } + + if (addr_bytes % 7 == 0) { + int addrs = addr_bytes / 7; + if (addrs >= AX25_MIN_ADDRS && addrs <= AX25_MAX_ADDRS) { + this_p->num_addr = addrs; + } + } + + return (this_p->num_addr); +} + + + +void ax25_get_addr_with_ssid(packet_t pp, int n, char *station) +{} + +/*------------------------------------------------------------------------------ + * + * Name: ax25_get_addr_no_ssid + * + * Purpose: Return specified address WITHOUT any SSID. + * + * Inputs: n - Index of address. Use the symbols + * AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc. + * + * Outputs: station - String representation of the station, WITHOUT the SSID. + * e.g. "WB2OSZ" + * Usually variables will be AX25_MAX_ADDR_LEN bytes + * but 7 would be adequate. + * + * Bugs: No bounds checking is performed. Be careful. + * + * Assumption: ax25_from_text or ax25_from_frame was called first. + * + * Returns: Character string in usual human readable format, + * + * + *------------------------------------------------------------------------------*/ + +void ax25_get_addr_no_ssid(packet_t this_p, int n, char *station) +{ + int i; + + //assert(this_p->magic1 == MAGIC); + //assert(this_p->magic2 == MAGIC); + + + if (n < 0) { + Debugprintf("Internal error detected in ax25_get_addr_no_ssid, %s, line %d.\n", __FILE__, __LINE__); + Debugprintf("Address index, %d, is less than zero.\n", n); + strcpy(station, "??????"); + return; + } + + if (n >= this_p->num_addr) { + Debugprintf("Internal error detected in ax25_get_no_with_ssid, %s, line %d.\n", __FILE__, __LINE__); + Debugprintf("Address index, %d, is too large for number of addresses, %d.\n", n, this_p->num_addr); + strcpy(station, "??????"); + return; + } + + // At one time this would stop at the first space, on the assumption we would have only trailing spaces. + // Then there was a forum discussion where someone encountered the address " WIDE2" with a leading space. + // In that case, we would have returned a zero length string here. + // Now we return exactly what is in the address field and trim trailing spaces. + // This will provide better information for troubleshooting. + + for (i = 0; i < 6; i++) { + station[i] = (this_p->frame_data[n * 7 + i] >> 1) & 0x7f; + } + station[6] = '\0'; + + for (i = 5; i >= 0; i--) { + if (station[i] == ' ') + station[i] = '\0'; + else + break; + } + + if (strlen(station) == 0) { + Debugprintf("Station address, in position %d, is empty! This is not a valid AX.25 frame.\n", n); + } + +} /* end ax25_get_addr_no_ssid */ + + +/*------------------------------------------------------------------------------ + * + * Name: ax25_get_ssid + * + * Purpose: Return SSID of specified address in current packet. + * + * Inputs: n - Index of address. Use the symbols + * AX25_DESTINATION, AX25_SOURCE, AX25_REPEATER1, etc. + * + * Assumption: ax25_from_text or ax25_from_frame was called first. + * + * Returns: Substation id, as integer 0 .. 15. + * + *------------------------------------------------------------------------------*/ + +int ax25_get_ssid(packet_t this_p, int n) +{ + +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + if (n >= 0 && n < this_p->num_addr) { + return ((this_p->frame_data[n * 7 + 6] & SSID_SSID_MASK) >> SSID_SSID_SHIFT); + } + else { + Debugprintf("Internal error: ax25_get_ssid(%d), num_addr=%d\n", n, this_p->num_addr); + return (0); + } +} + + + +static inline int ax25_get_pid_offset(packet_t this_p) +{ + return (ax25_get_control_offset(this_p) + ax25_get_num_control(this_p)); +} + +static int ax25_get_num_pid(packet_t this_p) +{ + int c; + int pid; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0 || /* I xxxx xxx0 */ + c == 0x03 || c == 0x13) { /* UI 000x 0011 */ + + pid = this_p->frame_data[ax25_get_pid_offset(this_p)]; + if (pid == AX25_PID_ESCAPE_CHARACTER) { + return (2); /* pid 1111 1111 means another follows. */ + } + return (1); + } + return (0); +} + + +inline int ax25_get_control_offset(packet_t this_p) +{ + return (this_p->num_addr * 7); +} + +inline int ax25_get_num_control(packet_t this_p) +{ + int c; + + c = this_p->frame_data[ax25_get_control_offset(this_p)]; + + if ((c & 0x01) == 0) { /* I xxxx xxx0 */ + return ((this_p->modulo == 128) ? 2 : 1); + } + + if ((c & 0x03) == 1) { /* S xxxx xx01 */ + return ((this_p->modulo == 128) ? 2 : 1); + } + + return (1); /* U xxxx xx11 */ +} + + + + +int ax25_get_info_offset(packet_t this_p) +{ + int offset = ax25_get_control_offset(this_p) + ax25_get_num_control(this_p) + ax25_get_num_pid(this_p); + return (offset); +} + +int ax25_get_num_info(packet_t this_p) +{ + int len; + + /* assuming AX.25 frame. */ + + len = this_p->frame_len - this_p->num_addr * 7 - ax25_get_num_control(this_p) - ax25_get_num_pid(this_p); + if (len < 0) { + len = 0; /* print error? */ + } + + return (len); +} + + + + + + /*------------------------------------------------------------------------------ + * + * Name: ax25_get_info + * + * Purpose: Obtain Information part of current packet. + * + * Inputs: this_p - Packet object pointer. + * + * Outputs: paddr - Starting address of information part is returned here. + * + * Assumption: ax25_from_text or ax25_from_frame was called first. + * + * Returns: Number of octets in the Information part. + * Should be in the range of AX25_MIN_INFO_LEN .. AX25_MAX_INFO_LEN. + * + *------------------------------------------------------------------------------*/ + +int ax25_get_info(packet_t this_p, unsigned char **paddr) +{ + unsigned char *info_ptr; + int info_len; + + + //assert(this_p->magic1 == MAGIC); + //assert(this_p->magic2 == MAGIC); + + if (this_p->num_addr >= 2) { + + /* AX.25 */ + + info_ptr = this_p->frame_data + ax25_get_info_offset(this_p); + info_len = ax25_get_num_info(this_p); + } + else { + + /* Not AX.25. Treat Whole packet as info. */ + + info_ptr = this_p->frame_data; + info_len = this_p->frame_len; + } + + /* Add nul character in case caller treats as printable string. */ + +// assert(info_len >= 0); + + info_ptr[info_len] = '\0'; + + *paddr = info_ptr; + return (info_len); + +} /* end ax25_get_info */ + + + + +void ax25_set_info(packet_t this_p, unsigned char *new_info_ptr, int new_info_len) +{ + unsigned char *old_info_ptr; + int old_info_len = ax25_get_info(this_p, &old_info_ptr); + this_p->frame_len -= old_info_len; + + if (new_info_len < 0) new_info_len = 0; + if (new_info_len > AX25_MAX_INFO_LEN) new_info_len = AX25_MAX_INFO_LEN; + memcpy(old_info_ptr, new_info_ptr, new_info_len); + this_p->frame_len += new_info_len; +} + +int ax25_get_pid(packet_t this_p) +{ +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + // TODO: handle 2 control byte case. + // TODO: sanity check: is it I or UI frame? + + if (this_p->frame_len == 0) return(-1); + + if (this_p->num_addr >= 2) { + return (this_p->frame_data[ax25_get_pid_offset(this_p)]); + } + return (-1); +} + + +int ax25_get_frame_len(packet_t this_p) +{ +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + +// assert(this_p->frame_len >= 0 && this_p->frame_len <= AX25_MAX_PACKET_LEN); + + return (this_p->frame_len); + +} /* end ax25_get_frame_len */ + + +unsigned char *ax25_get_frame_data_ptr(packet_t this_p) +{ +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + return (this_p->frame_data); + +} /* end ax25_get_frame_data_ptr */ + + +int ax25_get_modulo(packet_t this_p) +{ + return 7; +} + + +/*------------------------------------------------------------------ + * + * Function: ax25_get_control + ax25_get_c2 + * + * Purpose: Get Control field from packet. + * + * Inputs: this_p - pointer to packet object. + * + * Returns: APRS uses AX25_UI_FRAME. + * This could also be used in other situations. + * + *------------------------------------------------------------------*/ + + +int ax25_get_control(packet_t this_p) +{ +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + if (this_p->frame_len == 0) return(-1); + + if (this_p->num_addr >= 2) { + return (this_p->frame_data[ax25_get_control_offset(this_p)]); + } + return (-1); +} + + +/*------------------------------------------------------------------ +* +* Function: ax25_frame_type +* +* Purpose : Extract the type of frame. +* This is derived from the control byte(s) but +* is an enumerated type for easier handling. +* +* Inputs : this_p - pointer to packet object. +* +* Outputs : desc - Text description such as "I frame" or +*"U frame SABME". +* Supply 56 bytes to be safe. +* +* cr - Command or response ? +* +* pf - P / F - Poll / Final or -1 if not applicable +* +* nr - N(R) - receive sequence or -1 if not applicable. +* +* ns - N(S) - send sequence or -1 if not applicable. +* +* Returns: Frame type from enum ax25_frame_type_e. +* +*------------------------------------------------------------------*/ + +// TODO: need someway to ensure caller allocated enough space. +// Should pass in as parameter. + +#define DESC_SIZ 56 + + +ax25_frame_type_t ax25_frame_type(packet_t this_p, cmdres_t *cr, char *desc, int *pf, int *nr, int *ns) +{ + int c; // U frames are always one control byte. + int c2 = 0; // I & S frames can have second Control byte. + +// assert(this_p->magic1 == MAGIC); +// assert(this_p->magic2 == MAGIC); + + + strcpy(desc, "????"); + *cr = cr_11; + *pf = -1; + *nr = -1; + *ns = -1; + + c = ax25_get_control(this_p); + if (c < 0) { + strcpy(desc, "Not AX.25"); + return (frame_not_AX25); + } + + /* + * TERRIBLE HACK :-( for display purposes. + * + * I and S frames can have 1 or 2 control bytes but there is + * no good way to determine this without dipping into the data + * link state machine. Can we guess? + * + * S frames have no protocol id or information so if there is one + * more byte beyond the control field, we could assume there are + * two control bytes. + * + * For I frames, the protocol id will usually be 0xf0. If we find + * that as the first byte of the information field, it is probably + * the pid and not part of the information. Ditto for segments 0x08. + * Not fool proof but good enough for troubleshooting text out. + * + * If we have a link to the peer station, this will be set properly + * before it needs to be used for other reasons. + * + * Setting one of the RR bits (find reference!) is sounding better and better. + * It's in common usage so I should lobby to get that in the official protocol spec. + */ + + // Dont support mod 128 +/* + if (this_p->modulo == 0 && (c & 3) == 1 && ax25_get_c2(this_p) != -1) { + this_p->modulo = modulo_128; + } + else if (this_p->modulo == 0 && (c & 1) == 0 && this_p->frame_data[ax25_get_info_offset(this_p)] == 0xF0) { + this_p->modulo = modulo_128; + } + else if (this_p->modulo == 0 && (c & 1) == 0 && this_p->frame_data[ax25_get_info_offset(this_p)] == 0x08) { // same for segments + this_p->modulo = modulo_128; + } + + + if (this_p->modulo == modulo_128) { + c2 = ax25_get_c2(this_p); + } +*/ + + int dst_c = this_p->frame_data[AX25_DESTINATION * 7 + 6] & SSID_H_MASK; + int src_c = this_p->frame_data[AX25_SOURCE * 7 + 6] & SSID_H_MASK; + + char cr_text[8]; + char pf_text[8]; + + if (dst_c) { + if (src_c) { *cr = cr_11; strcpy(cr_text, "cc=11"); strcpy(pf_text, "p/f"); } + else { *cr = cr_cmd; strcpy(cr_text, "cmd"); strcpy(pf_text, "p"); } + } + else { + if (src_c) { *cr = cr_res; strcpy(cr_text, "res"); strcpy(pf_text, "f"); } + else { *cr = cr_00; strcpy(cr_text, "cc=00"); strcpy(pf_text, "p/f"); } + } + + if ((c & 1) == 0) { + + // Information rrr p sss 0 or sssssss 0 rrrrrrr p + + if (this_p->modulo == modulo_128) { + *ns = (c >> 1) & 0x7f; + *pf = c2 & 1; + *nr = (c2 >> 1) & 0x7f; + } + else { + *ns = (c >> 1) & 7; + *pf = (c >> 4) & 1; + *nr = (c >> 5) & 7; + } + + //snprintf (desc, DESC_SIZ, "I %s, n(s)=%d, n(r)=%d, %s=%d", cr_text, *ns, *nr, pf_text, *pf); + sprintf(desc, "I %s, n(s)=%d, n(r)=%d, %s=%d, pid=0x%02x", cr_text, *ns, *nr, pf_text, *pf, ax25_get_pid(this_p)); + return (frame_type_I); + } + else if ((c & 2) == 0) { + + // Supervisory rrr p/f ss 0 1 or 0000 ss 0 1 rrrrrrr p/f + + if (this_p->modulo == modulo_128) { + *pf = c2 & 1; + *nr = (c2 >> 1) & 0x7f; + } + else { + *pf = (c >> 4) & 1; + *nr = (c >> 5) & 7; + } + + + switch ((c >> 2) & 3) { + case 0: sprintf(desc, "RR %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_RR); break; + case 1: sprintf(desc, "RNR %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_RNR); break; + case 2: sprintf(desc, "REJ %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_REJ); break; + case 3: sprintf(desc, "SREJ %s, n(r)=%d, %s=%d", cr_text, *nr, pf_text, *pf); return (frame_type_S_SREJ); break; + } + } + else { + + // Unnumbered mmm p/f mm 1 1 + + *pf = (c >> 4) & 1; + + switch (c & 0xef) { + + case 0x6f: sprintf(desc, "SABME %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_SABME); break; + case 0x2f: sprintf(desc, "SABM %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_SABM); break; + case 0x43: sprintf(desc, "DISC %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_DISC); break; + case 0x0f: sprintf(desc, "DM %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_DM); break; + case 0x63: sprintf(desc, "UA %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_UA); break; + case 0x87: sprintf(desc, "FRMR %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_FRMR); break; + case 0x03: sprintf(desc, "UI %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_UI); break; + case 0xaf: sprintf(desc, "XID %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_XID); break; + case 0xe3: sprintf(desc, "TEST %s, %s=%d", cr_text, pf_text, *pf); return (frame_type_U_TEST); break; + default: sprintf(desc, "U other???"); return (frame_type_U); break; + } + } + + // Should be unreachable but compiler doesn't realize that. + // Here only to suppress "warning: control reaches end of non-void function" + + return (frame_not_AX25); + +} /* end ax25_frame_type */ + + + +packet_t ax25_u_frame(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr, ax25_frame_type_t ftype, int pf, int pid, unsigned char *pinfo, int info_len) +{ + packet_t this_p; + unsigned char *p; + int ctrl = 0; + unsigned int t = 999; // 1 = must be cmd, 0 = must be response, 2 = can be either. + int i = 0; // Is Info part allowed? + + this_p = ax25_new(); + + if (this_p == NULL) return (NULL); + + this_p->modulo = 0; + + if (!set_addrs(this_p, addrs, num_addr, cr)) { + Debugprintf("Internal error in %s: Could not set addresses for U frame.\n", __func__); + ax25_delete(this_p); + return (NULL); + } + + switch (ftype) { + // 1 = cmd only, 0 = res only, 2 = either + case frame_type_U_SABME: ctrl = 0x6f; t = 1; break; + case frame_type_U_SABM: ctrl = 0x2f; t = 1; break; + case frame_type_U_DISC: ctrl = 0x43; t = 1; break; + case frame_type_U_DM: ctrl = 0x0f; t = 0; break; + case frame_type_U_UA: ctrl = 0x63; t = 0; break; + case frame_type_U_FRMR: ctrl = 0x87; t = 0; i = 1; break; + case frame_type_U_UI: ctrl = 0x03; t = 2; i = 1; break; + case frame_type_U_XID: ctrl = 0xaf; t = 2; i = 1; break; + case frame_type_U_TEST: ctrl = 0xe3; t = 2; i = 1; break; + + default: + Debugprintf("Internal error in %s: Invalid ftype %d for U frame.\n", __func__, ftype); + ax25_delete(this_p); + return (NULL); + break; + } + if (pf) ctrl |= 0x10; + + if (t != 2) { + if (cr != t) { + Debugprintf("Internal error in %s: U frame, cr is %d but must be %d. ftype=%d\n", __func__, cr, t, ftype); + } + } + + p = this_p->frame_data + this_p->frame_len; + *p++ = ctrl; + this_p->frame_len++; + + if (ftype == frame_type_U_UI) { + + // Definitely don't want pid value of 0 (not in valid list) + // or 0xff (which means more bytes follow). + + if (pid < 0 || pid == 0 || pid == 0xff) { + Debugprintf("Internal error in %s: U frame, Invalid pid value 0x%02x.\n", __func__, pid); + pid = AX25_PID_NO_LAYER_3; + } + *p++ = pid; + this_p->frame_len++; + } + + if (i) { + if (pinfo != NULL && info_len > 0) { + if (info_len > AX25_MAX_INFO_LEN) { + + Debugprintf("Internal error in %s: U frame, Invalid information field length %d.\n", __func__, info_len); + info_len = AX25_MAX_INFO_LEN; + } + memcpy(p, pinfo, info_len); + p += info_len; + this_p->frame_len += info_len; + } + } + else { + if (pinfo != NULL && info_len > 0) { + Debugprintf("Internal error in %s: Info part not allowed for U frame type.\n", __func__); + } + } + *p = '\0'; + + //assert(p == this_p->frame_data + this_p->frame_len); + //assert(this_p->magic1 == MAGIC); + //assert(this_p->magic2 == MAGIC); + +#if PAD2TEST + ax25_frame_type_t check_ftype; + cmdres_t check_cr; + char check_desc[80]; + int check_pf; + int check_nr; + int check_ns; + + check_ftype = ax25_frame_type(this_p, &check_cr, check_desc, &check_pf, &check_nr, &check_ns); + + text_color_set(DW_COLOR_DEBUG); + Debugprintf("check: ftype=%d, desc=\"%s\", pf=%d\n", check_ftype, check_desc, check_pf); + + assert(check_cr == cr); + assert(check_ftype == ftype); + assert(check_pf == pf); + assert(check_nr == -1); + assert(check_ns == -1); + +#endif + + return (this_p); + +} /* end ax25_u_frame */ + + + + + + +static const char *position_name[1 + AX25_MAX_ADDRS] = { + "", "Destination ", "Source ", + "Digi1 ", "Digi2 ", "Digi3 ", "Digi4 ", + "Digi5 ", "Digi6 ", "Digi7 ", "Digi8 " }; + +int ax25_parse_addr(int position, char *in_addr, int strict, char *out_addr, int *out_ssid, int *out_heard) +{ + char *p; + char sstr[8]; /* Should be 1 or 2 digits for SSID. */ + int i, j, k; + int maxlen; + + *out_addr = '\0'; + *out_ssid = 0; + *out_heard = 0; + + // Debugprintf ("ax25_parse_addr in: position=%d, '%s', strict=%d\n", position, in_addr, strict); + + if (position < -1) position = -1; + if (position > AX25_REPEATER_8) position = AX25_REPEATER_8; + position++; /* Adjust for position_name above. */ + + if (strlen(in_addr) == 0) { + Debugprintf("%sAddress \"%s\" is empty.\n", position_name[position], in_addr); + return 0; + } + + if (strict && strlen(in_addr) >= 2 && strncmp(in_addr, "qA", 2) == 0) { + + Debugprintf("%sAddress \"%s\" is a \"q-construct\" used for communicating with\n", position_name[position], in_addr); + Debugprintf("APRS Internet Servers. It should never appear when going over the radio.\n"); + } + + // Debugprintf ("ax25_parse_addr in: %s\n", in_addr); + + maxlen = strict ? 6 : (AX25_MAX_ADDR_LEN - 1); + p = in_addr; + i = 0; + for (p = in_addr; *p != '\0' && *p != '-' && *p != '*'; p++) { + if (i >= maxlen) { + Debugprintf("%sAddress is too long. \"%s\" has more than %d characters.\n", position_name[position], in_addr, maxlen); + return 0; + } + if (!isalnum(*p)) { + Debugprintf("%sAddress, \"%s\" contains character other than letter or digit in character position %d.\n", position_name[position], in_addr, (int)(long)(p - in_addr) + 1); + return 0; + } + + out_addr[i++] = *p; + out_addr[i] = '\0'; + +#if DECAMAIN // Hack when running in decode_aprs utility. + // Exempt the "qA..." case because it was already mentioned. + + if (strict && islower(*p) && strncmp(in_addr, "qA", 2) != 0) { + text_color_set(DW_COLOR_ERROR); + Debugprintf("%sAddress has lower case letters. \"%s\" must be all upper case.\n", position_name[position], in_addr); + } +#else + if (strict && islower(*p)) { + Debugprintf("%sAddress has lower case letters. \"%s\" must be all upper case.\n", position_name[position], in_addr); + return 0; + } +#endif + } + + j = 0; + sstr[j] = '\0'; + if (*p == '-') { + for (p++; isalnum(*p); p++) { + if (j >= 2) { + Debugprintf("%sSSID is too long. SSID part of \"%s\" has more than 2 characters.\n", position_name[position], in_addr); + return 0; + } + sstr[j++] = *p; + sstr[j] = '\0'; + if (strict && !isdigit(*p)) { + Debugprintf("%sSSID must be digits. \"%s\" has letters in SSID.\n", position_name[position], in_addr); + return 0; + } + } + k = atoi(sstr); + if (k < 0 || k > 15) { + Debugprintf("%sSSID out of range. SSID of \"%s\" not in range of 0 to 15.\n", position_name[position], in_addr); + return 0; + } + *out_ssid = k; + } + + if (*p == '*') { + *out_heard = 1; + p++; + if (strict == 2) { + Debugprintf("\"*\" is not allowed at end of address \"%s\" here.\n", in_addr); + return 0; + } + } + + if (*p != '\0') { + Debugprintf("Invalid character \"%c\" found in %saddress \"%s\".\n", *p, position_name[position], in_addr); + return 0; + } + + // Debugprintf ("ax25_parse_addr out: '%s' %d %d\n", out_addr, *out_ssid, *out_heard); + + return (1); + +} /* end ax25_parse_addr */ + + + +int set_addrs(packet_t pp, char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, cmdres_t cr) +{ + int n; + + //assert(pp->frame_len == 0); + //assert(cr == cr_cmd || cr == cr_res); + + if (num_addr < AX25_MIN_ADDRS || num_addr > AX25_MAX_ADDRS) { + Debugprintf("INTERNAL ERROR: %s %s %d, num_addr = %d\n", __FILE__, __func__, __LINE__, num_addr); + return (0); + } + + for (n = 0; n < num_addr; n++) { + + unsigned char *pa = pp->frame_data + n * 7; + int ok; + int strict = 1; + char oaddr[AX25_MAX_ADDR_LEN]; + int ssid; + int heard; + int j; + + ok = ax25_parse_addr(n, addrs[n], strict, oaddr, &ssid, &heard); + + if (!ok) return (0); + + // Fill in address. + + memset(pa, ' ' << 1, 6); + for (j = 0; oaddr[j]; j++) { + pa[j] = oaddr[j] << 1; + } + pa += 6; + + // Fill in SSID. + + *pa = 0x60 | ((ssid & 0xf) << 1); + + // Command / response flag. + + switch (n) { + case AX25_DESTINATION: + if (cr == cr_cmd) *pa |= 0x80; + break; + case AX25_SOURCE: + if (cr == cr_res) *pa |= 0x80; + break; + default: + break; + } + + // Is this the end of address field? + + if (n == num_addr - 1) { + *pa |= 1; + } + + pp->frame_len += 7; + } + + pp->num_addr = num_addr; + return (1); + +} /* end set_addrs */ + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_init.c +// +/////////////////////////////////////////////////////////////////////////////// + + +// Init must be called at start of application. + +extern void il2p_init(int debug); + +extern struct rs *il2p_find_rs(int nparity); // Internal later? + +extern void il2p_encode_rs(unsigned char *tx_data, int data_size, int num_parity, unsigned char *parity_out); + +extern int il2p_decode_rs(unsigned char *rec_block, int data_size, int num_parity, unsigned char *out); + +extern int il2p_get_debug(void); +extern void il2p_set_debug(int debug); + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_rec.c +// +/////////////////////////////////////////////////////////////////////////////// + +// Receives a bit stream from demodulator. + +extern void il2p_rec_bit(int chan, int subchan, int slice, int dbit); + + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_send.c +// +/////////////////////////////////////////////////////////////////////////////// + + +// Send bit stream to modulator. + +string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity); + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_codec.c +// +/////////////////////////////////////////////////////////////////////////////// + + +extern int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout); + +packet_t il2p_decode_frame(unsigned char *irec); + +packet_t il2p_decode_header_payload(unsigned char* uhdr, unsigned char *epayload, int *symbols_corrected); + + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_header.c +// +/////////////////////////////////////////////////////////////////////////////// + + +extern int il2p_type_1_header(packet_t pp, int max_fec, unsigned char *hdr); + +extern packet_t il2p_decode_header_type_1(unsigned char *hdr, int num_sym_changed); + + +extern int il2p_type_0_header(packet_t pp, int max_fec, unsigned char *hdr); + +extern int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_descrambled_hdr); + + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_scramble.c +// +/////////////////////////////////////////////////////////////////////////////// + +extern void il2p_scramble_block(unsigned char *in, unsigned char *out, int len); + +extern void il2p_descramble_block(unsigned char *in, unsigned char *out, int len); + + +/////////////////////////////////////////////////////////////////////////////// +// +// il2p_payload.c +// +/////////////////////////////////////////////////////////////////////////////// + + +typedef struct { + int payload_byte_count; // Total size, 0 thru 1023 + int payload_block_count; + int small_block_size; + int large_block_size; + int large_block_count; + int small_block_count; + int parity_symbols_per_block; // 2, 4, 6, 8, 16 +} il2p_payload_properties_t; + +extern int il2p_payload_compute(il2p_payload_properties_t *p, int payload_size, int max_fec); + +extern int il2p_encode_payload(unsigned char *payload, int payload_size, int max_fec, unsigned char *enc); + +extern int il2p_decode_payload(unsigned char *received, int payload_size, int max_fec, unsigned char *payload_out, int *symbols_corrected); + +extern int il2p_get_header_attributes(unsigned char *hdr, int *hdr_type, int *max_fec); + +#endif + + + +// Interesting related stuff: +// https://www.kernel.org/doc/html/v4.15/core-api/librs.html +// https://berthub.eu/articles/posts/reed-solomon-for-programmers/ + + +#define MAX_NROOTS 16 + +#define NTAB 5 + +static struct { + int symsize; // Symbol size, bits (1-8). Always 8 for this application. + int genpoly; // Field generator polynomial coefficients. + int fcs; // First root of RS code generator polynomial, index form. + // FX.25 uses 1 but IL2P uses 0. + int prim; // Primitive element to generate polynomial roots. + int nroots; // RS code generator polynomial degree (number of roots). + // Same as number of check bytes added. + struct rs *rs; // Pointer to RS codec control block. Filled in at init time. +} Tab[NTAB] = { + {8, 0x11d, 0, 1, 2, NULL }, // 2 parity + {8, 0x11d, 0, 1, 4, NULL }, // 4 parity + {8, 0x11d, 0, 1, 6, NULL }, // 6 parity + {8, 0x11d, 0, 1, 8, NULL }, // 8 parity + {8, 0x11d, 0, 1, 16, NULL }, // 16 parity +}; + + + +static int g_il2p_debug = 0; + + +/*------------------------------------------------------------- + * + * Name: il2p_init + * + * Purpose: This must be called at application start up time. + * It sets up tables for the Reed-Solomon functions. + * + * Inputs: debug - Enable debug output. + * + *--------------------------------------------------------------*/ + +void il2p_init(int il2p_debug) +{ + g_il2p_debug = il2p_debug; + + for (int i = 0; i < NTAB; i++) { + //assert(Tab[i].nroots <= MAX_NROOTS); + Tab[i].rs = INIT_RS(Tab[i].symsize, Tab[i].genpoly, Tab[i].fcs, Tab[i].prim, Tab[i].nroots); + if (Tab[i].rs == NULL) { + Debugprintf("IL2P internal error: init_rs_char failed!\n"); + exit(0); + } + } + +} // end il2p_init + + +int il2p_get_debug(void) +{ + return (g_il2p_debug); +} +void il2p_set_debug(int debug) +{ + g_il2p_debug = debug; +} + + +// Find RS codec control block for specified number of parity symbols. + +struct rs *il2p_find_rs(int nparity) +{ + for (int n = 0; n < NTAB; n++) { + if (Tab[n].nroots == nparity) { + return (Tab[n].rs); + } + } + Debugprintf("IL2P INTERNAL ERROR: il2p_find_rs: control block not found for nparity = %d.\n", nparity); + return (Tab[0].rs); +} + + +/*------------------------------------------------------------- + * + * Name: void il2p_encode_rs + * + * Purpose: Add parity symbols to a block of data. + * + * Inputs: tx_data Header or other data to transmit. + * data_size Number of data bytes in above. + * num_parity Number of parity symbols to add. + * Maximum of IL2P_MAX_PARITY_SYMBOLS. + * + * Outputs: parity_out Specified number of parity symbols + * + * Restriction: data_size + num_parity <= 255 which is the RS block size. + * The caller must ensure this. + * + *--------------------------------------------------------------*/ + +void il2p_encode_rs(unsigned char *tx_data, int data_size, int num_parity, unsigned char *parity_out) +{ + //assert(data_size >= 1); + //assert(num_parity == 2 || num_parity == 4 || num_parity == 6 || num_parity == 8 || num_parity == 16); + //assert(data_size + num_parity <= 255); + + unsigned char rs_block[FX25_BLOCK_SIZE]; + memset(rs_block, 0, sizeof(rs_block)); + memcpy(rs_block + sizeof(rs_block) - data_size - num_parity, tx_data, data_size); + ENCODE_RS(il2p_find_rs(num_parity), rs_block, parity_out); +} + +/*------------------------------------------------------------- + * + * Name: void il2p_decode_rs + * + * Purpose: Check and attempt to fix block with FEC. + * + * Inputs: rec_block Received block composed of data and parity. + * Total size is sum of following two parameters. + * data_size Number of data bytes in above. + * num_parity Number of parity symbols (bytes) in above. + * + * Outputs: out Original with possible corrections applied. + * data_size bytes. + * + * Returns: -1 for unrecoverable. + * >= 0 for success. Number of symbols corrected. + * + *--------------------------------------------------------------*/ + +int il2p_decode_rs(unsigned char *rec_block, int data_size, int num_parity, unsigned char *out) +{ + + // Use zero padding in front if data size is too small. + + int n = data_size + num_parity; // total size in. + + unsigned char rs_block[FX25_BLOCK_SIZE]; + + // We could probably do this more efficiently by skipping the + // processing of the bytes known to be zero. Good enough for now. + + memset(rs_block, 0, sizeof(rs_block) - n); + memcpy(rs_block + sizeof(rs_block) - n, rec_block, n); + + if (il2p_get_debug() >= 3) { + Debugprintf("============================== il2p_decode_rs ==============================\n"); + Debugprintf("%d filler zeros, %d data, %d parity\n", (int)(sizeof(rs_block) - n), data_size, num_parity); + fx_hex_dump(rs_block, sizeof(rs_block)); + } + + int derrlocs[FX25_MAX_CHECK]; // Half would probably be OK. + + int derrors = DECODE_RS(il2p_find_rs(num_parity), rs_block, derrlocs, 0); + memcpy(out, rs_block + sizeof(rs_block) - n, data_size); + + if (il2p_get_debug() >= 3) { + if (derrors == 0) { + Debugprintf("No errors reported for RS block.\n"); + } + else if (derrors > 0) { + Debugprintf("%d errors fixed in positions:\n", derrors); + for (int j = 0; j < derrors; j++) { + Debugprintf(" %3d (0x%02x)\n", derrlocs[j], derrlocs[j]); + } + fx_hex_dump(rs_block, sizeof(rs_block)); + } + } + + // It is possible to have a situation where too many errors are + // present but the algorithm could get a good code block by "fixing" + // one of the padding bytes that should be 0. + + for (int i = 0; i < derrors; i++) { + if (derrlocs[i] < sizeof(rs_block) - n) { + if (il2p_get_debug() >= 3) { + Debugprintf("RS DECODE ERROR! Padding position %d should be 0 but it was set to %02x.\n", derrlocs[i], rs_block[derrlocs[i]]); + } + derrors = -1; + break; + } + } + + if (il2p_get_debug() >= 3) { + Debugprintf("============================== il2p_decode_rs returns %d ==============================\n", derrors); + } + return (derrors); +} + +// end il2p_init.c + + + + + + + + + + + + + +void ENCODE_RS(struct rs * rs, DTYPE * data, DTYPE * bb) +{ + + int i, j; + DTYPE feedback; + + memset(bb, 0, NROOTS * sizeof(DTYPE)); // clear out the FEC data area + + for (i = 0; i < NN - NROOTS; i++) { + feedback = INDEX_OF[data[i] ^ bb[0]]; + if (feedback != A0) { /* feedback term is non-zero */ + for (j = 1; j < NROOTS; j++) + bb[j] ^= ALPHA_TO[MODNN(feedback + GENPOLY[NROOTS - j])]; + } + /* Shift */ + memmove(&bb[0], &bb[1], sizeof(DTYPE)*(NROOTS - 1)); + if (feedback != A0) + bb[NROOTS - 1] = ALPHA_TO[MODNN(feedback + GENPOLY[0])]; + else + bb[NROOTS - 1] = 0; + } +} + + + + +int DECODE_RS(struct rs * rs, DTYPE * data, int *eras_pos, int no_eras) { + + int deg_lambda, el, deg_omega; + int i, j, r, k; + DTYPE u, q, tmp, num1, num2, den, discr_r; + // DTYPE lambda[NROOTS+1], s[NROOTS]; /* Err+Eras Locator poly and syndrome poly */ + // DTYPE b[NROOTS+1], t[NROOTS+1], omega[NROOTS+1]; + // DTYPE root[NROOTS], reg[NROOTS+1], loc[NROOTS]; + DTYPE lambda[FX25_MAX_CHECK + 1], s[FX25_MAX_CHECK]; /* Err+Eras Locator poly and syndrome poly */ + DTYPE b[FX25_MAX_CHECK + 1], t[FX25_MAX_CHECK + 1], omega[FX25_MAX_CHECK + 1]; + DTYPE root[FX25_MAX_CHECK], reg[FX25_MAX_CHECK + 1], loc[FX25_MAX_CHECK]; + int syn_error, count; + + /* form the syndromes; i.e., evaluate data(x) at roots of g(x) */ + for (i = 0; i < NROOTS; i++) + s[i] = data[0]; + + for (j = 1; j < NN; j++) { + for (i = 0; i < NROOTS; i++) { + if (s[i] == 0) { + s[i] = data[j]; + } + else { + s[i] = data[j] ^ ALPHA_TO[MODNN(INDEX_OF[s[i]] + (FCR + i)*PRIM)]; + } + } + } + + /* Convert syndromes to index form, checking for nonzero condition */ + syn_error = 0; + for (i = 0; i < NROOTS; i++) { + syn_error |= s[i]; + s[i] = INDEX_OF[s[i]]; + } + + // fprintf(stderr,"syn_error = %4x\n",syn_error); + if (!syn_error) { + /* if syndrome is zero, data[] is a codeword and there are no + * errors to correct. So return data[] unmodified + */ + count = 0; + goto finish; + } + memset(&lambda[1], 0, NROOTS * sizeof(lambda[0])); + lambda[0] = 1; + + if (no_eras > 0) { + /* Init lambda to be the erasure locator polynomial */ + lambda[1] = ALPHA_TO[MODNN(PRIM*(NN - 1 - eras_pos[0]))]; + for (i = 1; i < no_eras; i++) { + u = MODNN(PRIM*(NN - 1 - eras_pos[i])); + for (j = i + 1; j > 0; j--) { + tmp = INDEX_OF[lambda[j - 1]]; + if (tmp != A0) + lambda[j] ^= ALPHA_TO[MODNN(u + tmp)]; + } + } + +#if DEBUG >= 1 + /* Test code that verifies the erasure locator polynomial just constructed + Needed only for decoder debugging. */ + + /* find roots of the erasure location polynomial */ + for (i = 1; i <= no_eras; i++) + reg[i] = INDEX_OF[lambda[i]]; + + count = 0; + for (i = 1, k = IPRIM - 1; i <= NN; i++, k = MODNN(k + IPRIM)) { + q = 1; + for (j = 1; j <= no_eras; j++) + if (reg[j] != A0) { + reg[j] = MODNN(reg[j] + j); + q ^= ALPHA_TO[reg[j]]; + } + if (q != 0) + continue; + /* store root and error location number indices */ + root[count] = i; + loc[count] = k; + count++; + } + if (count != no_eras) { + fprintf(stderr, "count = %d no_eras = %d\n lambda(x) is WRONG\n", count, no_eras); + count = -1; + goto finish; + } +#if DEBUG >= 2 + fprintf(stderr, "\n Erasure positions as determined by roots of Eras Loc Poly:\n"); + for (i = 0; i < count; i++) + fprintf(stderr, "%d ", loc[i]); + fprintf(stderr, "\n"); +#endif +#endif + } + for (i = 0; i < NROOTS + 1; i++) + b[i] = INDEX_OF[lambda[i]]; + + /* + * Begin Berlekamp-Massey algorithm to determine error+erasure + * locator polynomial + */ + r = no_eras; + el = no_eras; + while (++r <= NROOTS) { /* r is the step number */ + /* Compute discrepancy at the r-th step in poly-form */ + discr_r = 0; + for (i = 0; i < r; i++) { + if ((lambda[i] != 0) && (s[r - i - 1] != A0)) { + discr_r ^= ALPHA_TO[MODNN(INDEX_OF[lambda[i]] + s[r - i - 1])]; + } + } + discr_r = INDEX_OF[discr_r]; /* Index form */ + if (discr_r == A0) { + /* 2 lines below: B(x) <-- x*B(x) */ + memmove(&b[1], b, NROOTS * sizeof(b[0])); + b[0] = A0; + } + else { + /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */ + t[0] = lambda[0]; + for (i = 0; i < NROOTS; i++) { + if (b[i] != A0) + t[i + 1] = lambda[i + 1] ^ ALPHA_TO[MODNN(discr_r + b[i])]; + else + t[i + 1] = lambda[i + 1]; + } + if (2 * el <= r + no_eras - 1) { + el = r + no_eras - el; + /* + * 2 lines below: B(x) <-- inv(discr_r) * + * lambda(x) + */ + for (i = 0; i <= NROOTS; i++) + b[i] = (lambda[i] == 0) ? A0 : MODNN(INDEX_OF[lambda[i]] - discr_r + NN); + } + else { + /* 2 lines below: B(x) <-- x*B(x) */ + memmove(&b[1], b, NROOTS * sizeof(b[0])); + b[0] = A0; + } + memcpy(lambda, t, (NROOTS + 1) * sizeof(t[0])); + } + } + + /* Convert lambda to index form and compute deg(lambda(x)) */ + deg_lambda = 0; + for (i = 0; i < NROOTS + 1; i++) { + lambda[i] = INDEX_OF[lambda[i]]; + if (lambda[i] != A0) + deg_lambda = i; + } + /* Find roots of the error+erasure locator polynomial by Chien search */ + memcpy(®[1], &lambda[1], NROOTS * sizeof(reg[0])); + count = 0; /* Number of roots of lambda(x) */ + for (i = 1, k = IPRIM - 1; i <= NN; i++, k = MODNN(k + IPRIM)) { + q = 1; /* lambda[0] is always 0 */ + for (j = deg_lambda; j > 0; j--) { + if (reg[j] != A0) { + reg[j] = MODNN(reg[j] + j); + q ^= ALPHA_TO[reg[j]]; + } + } + if (q != 0) + continue; /* Not a root */ + /* store root (index-form) and error location number */ +#if DEBUG>=2 + fprintf(stderr, "count %d root %d loc %d\n", count, i, k); +#endif + root[count] = i; + loc[count] = k; + /* If we've already found max possible roots, + * abort the search to save time + */ + if (++count == deg_lambda) + break; + } + if (deg_lambda != count) { + /* + * deg(lambda) unequal to number of roots => uncorrectable + * error detected + */ + count = -1; + goto finish; + } + /* + * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo + * x**NROOTS). in index form. Also find deg(omega). + */ + deg_omega = 0; + for (i = 0; i < NROOTS; i++) { + tmp = 0; + j = (deg_lambda < i) ? deg_lambda : i; + for (; j >= 0; j--) { + if ((s[i - j] != A0) && (lambda[j] != A0)) + tmp ^= ALPHA_TO[MODNN(s[i - j] + lambda[j])]; + } + if (tmp != 0) + deg_omega = i; + omega[i] = INDEX_OF[tmp]; + } + omega[NROOTS] = A0; + + /* + * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = + * inv(X(l))**(FCR-1) and den = lambda_pr(inv(X(l))) all in poly-form + */ + for (j = count - 1; j >= 0; j--) { + num1 = 0; + for (i = deg_omega; i >= 0; i--) { + if (omega[i] != A0) + num1 ^= ALPHA_TO[MODNN(omega[i] + i * root[j])]; + } + num2 = ALPHA_TO[MODNN(root[j] * (FCR - 1) + NN)]; + den = 0; + + /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */ + for (i = min(deg_lambda, NROOTS - 1) & ~1; i >= 0; i -= 2) { + if (lambda[i + 1] != A0) + den ^= ALPHA_TO[MODNN(lambda[i + 1] + i * root[j])]; + } + if (den == 0) { +#if DEBUG >= 1 + fprintf(stderr, "\n ERROR: denominator = 0\n"); +#endif + count = -1; + goto finish; + } + /* Apply error to data */ + if (num1 != 0) { + data[loc[j]] ^= ALPHA_TO[MODNN(INDEX_OF[num1] + INDEX_OF[num2] + NN - INDEX_OF[den])]; + } + } +finish: + if (eras_pos != NULL) { + for (i = 0; i < count; i++) + eras_pos[i] = loc[i]; + } + return count; +} + + + + +struct rs *INIT_RS(unsigned int symsize, unsigned int gfpoly, unsigned fcr, unsigned prim, + unsigned int nroots) { + struct rs *rs; + int i, j, sr, root, iprim; + + if (symsize > 8 * sizeof(DTYPE)) + return NULL; /* Need version with ints rather than chars */ + + if (fcr >= (1 << symsize)) + return NULL; + if (prim == 0 || prim >= (1 << symsize)) + return NULL; + if (nroots >= (1 << symsize)) + return NULL; /* Can't have more roots than symbol values! */ + + rs = (struct rs *)calloc(1, sizeof(struct rs)); + if (rs == NULL) { + Debugprintf("FATAL ERROR: Out of memory.\n"); + exit(0); + } + rs->mm = symsize; + rs->nn = (1 << symsize) - 1; + + rs->alpha_to = (DTYPE *)calloc((rs->nn + 1), sizeof(DTYPE)); + if (rs->alpha_to == NULL) { + Debugprintf("FATAL ERROR: Out of memory.\n"); + exit(0); + } + rs->index_of = (DTYPE *)calloc((rs->nn + 1), sizeof(DTYPE)); + if (rs->index_of == NULL) { + Debugprintf("FATAL ERROR: Out of memory.\n"); + exit(0); + } + + /* Generate Galois field lookup tables */ + rs->index_of[0] = A0; /* log(zero) = -inf */ + rs->alpha_to[A0] = 0; /* alpha**-inf = 0 */ + sr = 1; + for (i = 0; i < rs->nn; i++) { + rs->index_of[sr] = i; + rs->alpha_to[i] = sr; + sr <<= 1; + if (sr & (1 << symsize)) + sr ^= gfpoly; + sr &= rs->nn; + } + if (sr != 1) { + /* field generator polynomial is not primitive! */ + free(rs->alpha_to); + free(rs->index_of); + free(rs); + return NULL; + } + + /* Form RS code generator polynomial from its roots */ + rs->genpoly = (DTYPE *)calloc((nroots + 1), sizeof(DTYPE)); + if (rs->genpoly == NULL) { + Debugprintf("FATAL ERROR: Out of memory.\n"); + exit(0); + } + rs->fcr = fcr; + rs->prim = prim; + rs->nroots = nroots; + + /* Find prim-th root of 1, used in decoding */ + for (iprim = 1; (iprim % prim) != 0; iprim += rs->nn) + ; + rs->iprim = iprim / prim; + + rs->genpoly[0] = 1; + for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) { + rs->genpoly[i + 1] = 1; + + /* Multiply rs->genpoly[] by @**(root + x) */ + for (j = i; j > 0; j--) { + if (rs->genpoly[j] != 0) + rs->genpoly[j] = rs->genpoly[j - 1] ^ rs->alpha_to[modnn(rs, rs->index_of[rs->genpoly[j]] + root)]; + else + rs->genpoly[j] = rs->genpoly[j - 1]; + } + /* rs->genpoly[0] can never be zero */ + rs->genpoly[0] = rs->alpha_to[modnn(rs, rs->index_of[rs->genpoly[0]] + root)]; + } + /* convert rs->genpoly[] to index form for quicker encoding */ + for (i = 0; i <= nroots; i++) { + rs->genpoly[i] = rs->index_of[rs->genpoly[i]]; + } + + // diagnostic prints +#if 0 + printf("Alpha To:\n\r"); + for (i = 0; i < sizeof(DTYPE)*(rs->nn + 1); i++) + printf("0x%2x,", rs->alpha_to[i]); + printf("\n\r"); + + printf("Index Of:\n\r"); + for (i = 0; i < sizeof(DTYPE)*(rs->nn + 1); i++) + printf("0x%2x,", rs->index_of[i]); + printf("\n\r"); + + printf("GenPoly:\n\r"); + for (i = 0; i <= nroots; i++) + printf("0x%2x,", rs->genpoly[i]); + printf("\n\r"); +#endif + return rs; +} + + +// TEMPORARY!!! +// FIXME: We already have multiple copies of this. +// Consolidate them into one somewhere. + +void fx_hex_dump(unsigned char *p, int len) +{ + int n, i, offset; + + offset = 0; + while (len > 0) { + n = len < 16 ? len : 16; + Debugprintf(" %03x: ", offset); + for (i = 0; i < n; i++) { + Debugprintf(" %02x", p[i]); + } + for (i = n; i < 16; i++) { + Debugprintf(" "); + } + Debugprintf(" "); + for (i = 0; i < n; i++) { + Debugprintf("%c", isprint(p[i]) ? p[i] : '.'); + } + Debugprintf("\n"); + p += 16; + offset += 16; + len -= 16; + } +} + + +/*------------------------------------------------------------- + * + * File: il2p_codec.c + * + * Purpose: Convert IL2P encoded format from and to direwolf internal packet format. + * + *--------------------------------------------------------------*/ + + + /*------------------------------------------------------------- + * + * Name: il2p_encode_frame + * + * Purpose: Convert AX.25 frame to IL2P encoding. + * + * Inputs: chan - Audio channel number, 0 = first. + * + * pp - Packet object pointer. + * + * max_fec - 1 to send maximum FEC size rather than automatic. + * + * Outputs: iout - Encoded result, excluding the 3 byte sync word. + * Caller should provide IL2P_MAX_PACKET_SIZE bytes. + * + * Returns: Number of bytes for transmission. + * -1 is returned for failure. + * + * Description: Encode into IL2P format. + * + * Errors: If something goes wrong, return -1. + * + * Most likely reason is that the frame is too large. + * IL2P has a max payload size of 1023 bytes. + * For a type 1 header, this is the maximum AX.25 Information part size. + * For a type 0 header, this is the entire AX.25 frame. + * + *--------------------------------------------------------------*/ + +int il2p_encode_frame(packet_t pp, int max_fec, unsigned char *iout) +{ + + // Can a type 1 header be used? + + unsigned char hdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; + int e; + int out_len = 0; + + e = il2p_type_1_header(pp, max_fec, hdr); + if (e >= 0) { + il2p_scramble_block(hdr, iout, IL2P_HEADER_SIZE); + il2p_encode_rs(iout, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, iout + IL2P_HEADER_SIZE); + out_len = IL2P_HEADER_SIZE + IL2P_HEADER_PARITY; + + if (e == 0) { + // Success. No info part. + return (out_len); + } + + // Payload is AX.25 info part. + unsigned char *pinfo; + int info_len; + info_len = ax25_get_info(pp, &pinfo); + + int k = il2p_encode_payload(pinfo, info_len, max_fec, iout + out_len); + if (k > 0) { + out_len += k; + // Success. Info part was <= 1023 bytes. + return (out_len); + } + + // Something went wrong with the payload encoding. + return (-1); + } + else if (e == -1) { + + // Could not use type 1 header for some reason. + // e.g. More than 2 addresses, extended (mod 128) sequence numbers, etc. + + e = il2p_type_0_header(pp, max_fec, hdr); + if (e > 0) { + + il2p_scramble_block(hdr, iout, IL2P_HEADER_SIZE); + il2p_encode_rs(iout, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, iout + IL2P_HEADER_SIZE); + out_len = IL2P_HEADER_SIZE + IL2P_HEADER_PARITY; + + // Payload is entire AX.25 frame. + + unsigned char *frame_data_ptr = ax25_get_frame_data_ptr(pp); + int frame_len = ax25_get_frame_len(pp); + int k = il2p_encode_payload(frame_data_ptr, frame_len, max_fec, iout + out_len); + if (k > 0) { + out_len += k; + // Success. Entire AX.25 frame <= 1023 bytes. + return (out_len); + } + // Something went wrong with the payload encoding. + return (-1); + } + else if (e == 0) { + // Impossible condition. Type 0 header must have payload. + return (-1); + } + else { + // AX.25 frame is too large. + return (-1); + } + } + + // AX.25 Information part is too large. + return (-1); +} + + + +/*------------------------------------------------------------- + * + * Name: il2p_decode_frame + * + * Purpose: Convert IL2P encoding to AX.25 frame. + * This is only used during testing, with a whole encoded frame. + * During reception, the header would have FEC and descrambling + * applied first so we would know how much to collect for the payload. + * + * Inputs: irec - Received IL2P frame excluding the 3 byte sync word. + * + * Future Out: Number of symbols corrected. + * + * Returns: Packet pointer or NULL for error. + * + *--------------------------------------------------------------*/ + +packet_t il2p_decode_frame(unsigned char *irec) +{ + unsigned char uhdr[IL2P_HEADER_SIZE]; // After FEC and descrambling. + int e = il2p_clarify_header(irec, uhdr); + + // TODO?: for symmetry we might want to clarify the payload before combining. + + return (il2p_decode_header_payload(uhdr, irec + IL2P_HEADER_SIZE + IL2P_HEADER_PARITY, &e)); +} + + +/*------------------------------------------------------------- + * + * Name: il2p_decode_header_payload + * + * Purpose: Convert IL2P encoding to AX.25 frame + * + * Inputs: uhdr - Received header after FEC and descrambling. + * epayload - Encoded payload. + * + * In/Out: symbols_corrected - Symbols (bytes) corrected in the header. + * Should be 0 or 1 because it has 2 parity symbols. + * Here we add number of corrections for the payload. + * + * Returns: Packet pointer or NULL for error. + * + *--------------------------------------------------------------*/ + +packet_t il2p_decode_header_payload(unsigned char* uhdr, unsigned char *epayload, int *symbols_corrected) +{ + int hdr_type; + int max_fec; + int payload_len = il2p_get_header_attributes(uhdr, &hdr_type, &max_fec); + + packet_t pp = NULL; + + if (hdr_type == 1) { + + // Header type 1. Any payload is the AX.25 Information part. + + pp = il2p_decode_header_type_1(uhdr, *symbols_corrected); + if (pp == NULL) { + // Failed for some reason. + return (NULL); + } + + if (payload_len > 0) { + // This is the AX.25 Information part. + + unsigned char extracted[IL2P_MAX_PAYLOAD_SIZE]; + int e = il2p_decode_payload(epayload, payload_len, max_fec, extracted, symbols_corrected); + + // It would be possible to have a good header but too many errors in the payload. + + if (e <= 0) { + ax25_delete(pp); + pp = NULL; + return (pp); + } + + if (e != payload_len) { + Debugprintf("IL2P Internal Error: %s(): hdr_type=%d, max_fec=%d, payload_len=%d, e=%d.\n", __func__, hdr_type, max_fec, payload_len, e); + } + + ax25_set_info(pp, extracted, payload_len); + } + return (pp); + } + else { + + // Header type 0. The payload is the entire AX.25 frame. + + unsigned char extracted[IL2P_MAX_PAYLOAD_SIZE]; + int e = il2p_decode_payload(epayload, payload_len, max_fec, extracted, symbols_corrected); + + if (e <= 0) { // Payload was not received correctly. + return (NULL); + } + + if (e != payload_len) { + Debugprintf("IL2P Internal Error: %s(): hdr_type=%d, e=%d, payload_len=%d\n", __func__, hdr_type, e, payload_len); + return (NULL); + } + + alevel_t alevel; + memset(&alevel, 0, sizeof(alevel)); + //alevel = demod_get_audio_level (chan, subchan); // What TODO? We don't know channel here. + // I think alevel gets filled in somewhere later making + // this redundant. + + pp = ax25_from_frame(extracted, payload_len, alevel); + return (pp); + } + +} // end il2p_decode_header_payload + +// end il2p_codec.c + + + +/*-------------------------------------------------------------------------------- + * + * File: il2p_header.c + * + * Purpose: Functions to deal with the IL2P header. + * + * Reference: http://tarpn.net/t/il2p/il2p-specification0-4.pdf + * + *--------------------------------------------------------------------------------*/ + + + + // Convert ASCII to/from DEC SIXBIT as defined here: + // https://en.wikipedia.org/wiki/Six-bit_character_code#DEC_six-bit_code + +static inline int ascii_to_sixbit(int a) +{ + if (a >= ' ' && a <= '_') return (a - ' '); + return (31); // '?' for any invalid. +} + +static inline int sixbit_to_ascii(int s) +{ + return (s + ' '); +} + +// Functions for setting the various header fields. +// It is assumed that it was zeroed first so only the '1' bits are set. + +static void set_field(unsigned char *hdr, int bit_num, int lsb_index, int width, int value) +{ + while (width > 0 && value != 0) { + //assert(lsb_index >= 0 && lsb_index <= 11); + if (value & 1) { + hdr[lsb_index] |= 1 << bit_num; + } + value >>= 1; + lsb_index--; + width--; + } + //assert(value == 0); +} + +#define SET_UI(hdr,val) set_field(hdr, 6, 0, 1, val) + +#define SET_PID(hdr,val) set_field(hdr, 6, 4, 4, val) + +#define SET_CONTROL(hdr,val) set_field(hdr, 6, 11, 7, val) + + +#define SET_FEC_LEVEL(hdr,val) set_field(hdr, 7, 0, 1, val) + +#define SET_HDR_TYPE(hdr,val) set_field(hdr, 7, 1, 1, val) + +#define SET_PAYLOAD_BYTE_COUNT(hdr,val) set_field(hdr, 7, 11, 10, val) + + +// Extracting the fields. + +static int get_field(unsigned char *hdr, int bit_num, int lsb_index, int width) +{ + int result = 0; + lsb_index -= width - 1; + while (width > 0) { + result <<= 1; + //assert(lsb_index >= 0 && lsb_index <= 11); + if (hdr[lsb_index] & (1 << bit_num)) { + result |= 1; + } + lsb_index++; + width--; + } + return (result); +} + +#define GET_UI(hdr) get_field(hdr, 6, 0, 1) + +#define GET_PID(hdr) get_field(hdr, 6, 4, 4) + +#define GET_CONTROL(hdr) get_field(hdr, 6, 11, 7) + + +#define GET_FEC_LEVEL(hdr) get_field(hdr, 7, 0, 1) + +#define GET_HDR_TYPE(hdr) get_field(hdr, 7, 1, 1) + +#define GET_PAYLOAD_BYTE_COUNT(hdr) get_field(hdr, 7, 11, 10) + + + +// AX.25 'I' and 'UI' frames have a protocol ID which determines how the +// information part should be interpreted. +// Here we squeeze the most common cases down to 4 bits. +// Return -1 if translation is not possible. Fall back to type 0 header in this case. + +static int encode_pid(packet_t pp) +{ + int pid = ax25_get_pid(pp); + + if ((pid & 0x30) == 0x20) return (0x2); // AX.25 Layer 3 + if ((pid & 0x30) == 0x10) return (0x2); // AX.25 Layer 3 + if (pid == 0x01) return (0x3); // ISO 8208 / CCIT X.25 PLP + if (pid == 0x06) return (0x4); // Compressed TCP/IP + if (pid == 0x07) return (0x5); // Uncompressed TCP/IP + if (pid == 0x08) return (0x6); // Segmentation fragmen + if (pid == 0xcc) return (0xb); // ARPA Internet Protocol + if (pid == 0xcd) return (0xc); // ARPA Address Resolution + if (pid == 0xce) return (0xd); // FlexNet + if (pid == 0xcf) return (0xe); // TheNET + if (pid == 0xf0) return (0xf); // No L3 + return (-1); +} + +// Convert IL2P 4 bit PID to AX.25 8 bit PID. + + +static int decode_pid(int pid) +{ + static const unsigned char axpid[16] = { + 0xf0, // Should not happen. 0 is for 'S' frames. + 0xf0, // Should not happen. 1 is for 'U' frames (but not UI). + 0x20, // AX.25 Layer 3 + 0x01, // ISO 8208 / CCIT X.25 PLP + 0x06, // Compressed TCP/IP + 0x07, // Uncompressed TCP/IP + 0x08, // Segmentation fragment + 0xf0, // Future + 0xf0, // Future + 0xf0, // Future + 0xf0, // Future + 0xcc, // ARPA Internet Protocol + 0xcd, // ARPA Address Resolution + 0xce, // FlexNet + 0xcf, // TheNET + 0xf0 }; // No L3 + + //assert(pid >= 0 && pid <= 15); + return (axpid[pid]); +} + + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_type_1_header + * + * Purpose: Attempt to create type 1 header from packet object. + * + * Inputs: pp - Packet object. + * + * max_fec - 1 to use maximum FEC symbols , 0 for automatic. + * + * Outputs: hdr - IL2P header with no scrambling or parity symbols. + * Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes. + * + * Returns: Number of bytes for information part or -1 for failure. + * In case of failure, fall back to type 0 transparent encapsulation. + * + * Description: Type 1 Headers do not support AX.25 repeater callsign addressing, + * Modulo-128 extended mode window sequence numbers, nor any callsign + * characters that cannot translate to DEC SIXBIT. + * If these cases are encountered during IL2P packet encoding, + * the encoder switches to Type 0 Transparent Encapsulation. + * SABME can't be handled by type 1. + * + *--------------------------------------------------------------------------------*/ + +int il2p_type_1_header(packet_t pp, int max_fec, unsigned char *hdr) +{ + memset(hdr, 0, IL2P_HEADER_SIZE); + + if (ax25_get_num_addr(pp) != 2) { + // Only two addresses are allowed for type 1 header. + return (-1); + } + + // Check does not apply for 'U' frames but put in one place rather than two. + + if (ax25_get_modulo(pp) == 128) return(-1); + + // Destination and source addresses go into low bits 0-5 for bytes 0-11. + + char dst_addr[AX25_MAX_ADDR_LEN]; + char src_addr[AX25_MAX_ADDR_LEN]; + + ax25_get_addr_no_ssid(pp, AX25_DESTINATION, dst_addr); + int dst_ssid = ax25_get_ssid(pp, AX25_DESTINATION); + + ax25_get_addr_no_ssid(pp, AX25_SOURCE, src_addr); + int src_ssid = ax25_get_ssid(pp, AX25_SOURCE); + + unsigned char *a = (unsigned char *)dst_addr; + for (int i = 0; *a != '\0'; i++, a++) { + if (*a < ' ' || *a > '_') { + // Shouldn't happen but follow the rule. + return (-1); + } + hdr[i] = ascii_to_sixbit(*a); + } + + a = (unsigned char *)src_addr; + for (int i = 6; *a != '\0'; i++, a++) { + if (*a < ' ' || *a > '_') { + // Shouldn't happen but follow the rule. + return (-1); + } + hdr[i] = ascii_to_sixbit(*a); + } + + // Byte 12 has DEST SSID in upper nybble and SRC SSID in lower nybble and + hdr[12] = (dst_ssid << 4) | src_ssid; + + ax25_frame_type_t frame_type; + cmdres_t cr; // command or response. + char description[64]; + int pf; // Poll/Final. + int nr, ns; // Sequence numbers. + + frame_type = ax25_frame_type(pp, &cr, description, &pf, &nr, &ns); + + //Debugprintf ("%s(): %s-%d>%s-%d: %s\n", __func__, src_addr, src_ssid, dst_addr, dst_ssid, description); + + switch (frame_type) { + + case frame_type_S_RR: // Receive Ready - System Ready To Receive + case frame_type_S_RNR: // Receive Not Ready - TNC Buffer Full + case frame_type_S_REJ: // Reject Frame - Out of Sequence or Duplicate + case frame_type_S_SREJ: // Selective Reject - Request single frame repeat + + // S frames (RR, RNR, REJ, SREJ), mod 8, have control N(R) P/F S S 0 1 + // These are mapped into P/F N(R) C S S + // Bit 6 is not mentioned in documentation but it is used for P/F for the other frame types. + // C is copied from the C bit in the destination addr. + // C from source is not used here. Reception assumes it is the opposite. + // PID is set to 0, meaning none, for S frames. + + SET_UI(hdr, 0); + SET_PID(hdr, 0); + SET_CONTROL(hdr, (pf << 6) | (nr << 3) | (((cr == cr_cmd) | (cr == cr_11)) << 2)); + + // This gets OR'ed into the above. + switch (frame_type) { + case frame_type_S_RR: SET_CONTROL(hdr, 0); break; + case frame_type_S_RNR: SET_CONTROL(hdr, 1); break; + case frame_type_S_REJ: SET_CONTROL(hdr, 2); break; + case frame_type_S_SREJ: SET_CONTROL(hdr, 3); break; + default: break; + } + + break; + + case frame_type_U_SABM: // Set Async Balanced Mode + case frame_type_U_DISC: // Disconnect + case frame_type_U_DM: // Disconnect Mode + case frame_type_U_UA: // Unnumbered Acknowledge + case frame_type_U_FRMR: // Frame Reject + case frame_type_U_UI: // Unnumbered Information + case frame_type_U_XID: // Exchange Identification + case frame_type_U_TEST: // Test + + // The encoding allows only 3 bits for frame type and SABME got left out. + // Control format: P/F opcode[3] C n/a n/a + // The grayed out n/a bits are observed as 00 in the example. + // The header UI field must also be set for UI frames. + // PID is set to 1 for all U frames other than UI. + + if (frame_type == frame_type_U_UI) { + SET_UI(hdr, 1); // I guess this is how we distinguish 'I' and 'UI' + // on the receiving end. + int pid = encode_pid(pp); + if (pid < 0) return (-1); + SET_PID(hdr, pid); + } + else { + SET_PID(hdr, 1); // 1 for 'U' other than 'UI'. + } + + // Each of the destination and source addresses has a "C" bit. + // They should normally have the opposite setting. + // IL2P has only a single bit to represent 4 possbilities. + // + // dst src il2p meaning + // --- --- ---- ------- + // 0 0 0 Not valid (earlier protocol version) + // 1 0 1 Command (v2) + // 0 1 0 Response (v2) + // 1 1 1 Not valid (earlier protocol version) + // + // APRS does not mention how to set these bits and all 4 combinations + // are seen in the wild. Apparently these are ignored on receive and no + // one cares. Here we copy from the C bit in the destination address. + // It should be noted that the case of both C bits being the same can't + // be represented so the il2p encode/decode bit not produce exactly the + // same bits. We see this in the second example in the protocol spec. + // The original UI frame has both C bits of 0 so it is received as a response. + + SET_CONTROL(hdr, (pf << 6) | (((cr == cr_cmd) | (cr == cr_11)) << 2)); + + // This gets OR'ed into the above. + switch (frame_type) { + case frame_type_U_SABM: SET_CONTROL(hdr, 0 << 3); break; + case frame_type_U_DISC: SET_CONTROL(hdr, 1 << 3); break; + case frame_type_U_DM: SET_CONTROL(hdr, 2 << 3); break; + case frame_type_U_UA: SET_CONTROL(hdr, 3 << 3); break; + case frame_type_U_FRMR: SET_CONTROL(hdr, 4 << 3); break; + case frame_type_U_UI: SET_CONTROL(hdr, 5 << 3); break; + case frame_type_U_XID: SET_CONTROL(hdr, 6 << 3); break; + case frame_type_U_TEST: SET_CONTROL(hdr, 7 << 3); break; + default: break; + } + break; + + case frame_type_I: // Information + + // I frames (mod 8 only) + // encoded control: P/F N(R) N(S) + + SET_UI(hdr, 0); + + int pid2 = encode_pid(pp); + if (pid2 < 0) return (-1); + SET_PID(hdr, pid2); + + SET_CONTROL(hdr, (pf << 6) | (nr << 3) | ns); + break; + + case frame_type_U_SABME: // Set Async Balanced Mode, Extended + case frame_type_U: // other Unnumbered, not used by AX.25. + case frame_not_AX25: // Could not get control byte from frame. + default: + + // Fall back to the header type 0 for these. + return (-1); + } + + // Common for all header type 1. + + // Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10] + + SET_FEC_LEVEL(hdr, max_fec); + SET_HDR_TYPE(hdr, 1); + + unsigned char *pinfo; + int info_len; + + info_len = ax25_get_info(pp, &pinfo); + if (info_len < 0 || info_len > IL2P_MAX_PAYLOAD_SIZE) { + return (-2); + } + + SET_PAYLOAD_BYTE_COUNT(hdr, info_len); + return (info_len); +} + + +// This should create a packet from the IL2P header. +// The information part will not be filled in. + +static void trim(char *stuff) +{ + char *p = stuff + strlen(stuff) - 1; + while (strlen(stuff) > 0 && (*p == ' ')) { + *p = '\0'; + p--; + } +} + + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_decode_header_type_1 + * + * Purpose: Attempt to convert type 1 header to a packet object. + * + * Inputs: hdr - IL2P header with no scrambling or parity symbols. + * + * num_sym_changed - Number of symbols changed by FEC in the header. + * Should be 0 or 1. + * + * Returns: Packet Object or NULL for failure. + * + * Description: A later step will process the payload for the information part. + * + *--------------------------------------------------------------------------------*/ + +packet_t il2p_decode_header_type_1(unsigned char *hdr, int num_sym_changed) +{ + + if (GET_HDR_TYPE(hdr) != 1) { + Debugprintf("IL2P Internal error. Should not be here: %s, when header type is 0.\n", __func__); + return (NULL); + } + + // First get the addresses including SSID. + + char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN]; + int num_addr = 2; + memset(addrs, 0, 2 * AX25_MAX_ADDR_LEN); + + // The IL2P header uses 2 parity symbols which means a single corrupted symbol (byte) + // can always be corrected. + // However, I have seen cases, where the error rate is very high, where the RS decoder + // thinks it found a valid code block by changing one symbol but it was the wrong one. + // The result is trash. This shows up as address fields like 'R&G4"A' and 'TEW\ !'. + // I added a sanity check here to catch characters other than upper case letters and digits. + // The frame should be rejected in this case. The question is whether to discard it + // silently or print a message so the user can see that something strange is happening? + // My current thinking is that it should be silently ignored if the header has been + // modified (correctee or more likely, made worse in this cases). + // If no changes were made, something weird is happening. We should mention it for + // troubleshooting rather than sweeping it under the rug. + + // The same thing has been observed with the payload, under very high error conditions, + // and max_fec==0. Here I don't see a good solution. AX.25 information can contain + // "binary" data so I'm not sure what sort of sanity check could be added. + // This was not observed with max_fec==1. If we make that the default, same as Nino TNC, + // it would be extremely extremely unlikely unless someone explicitly selects weaker FEC. + + // TODO: We could do something similar for header type 0. + // The address fields should be all binary zero values. + // Someone overly ambitious might check the addresses found in the first payload block. + + for (int i = 0; i <= 5; i++) { + addrs[AX25_DESTINATION][i] = sixbit_to_ascii(hdr[i] & 0x3f); + } + trim(addrs[AX25_DESTINATION]); + for (int i = 0; i < strlen(addrs[AX25_DESTINATION]); i++) { + if (!isupper(addrs[AX25_DESTINATION][i]) && !isdigit(addrs[AX25_DESTINATION][i])) { + if (num_sym_changed == 0) { + // This can pop up sporadically when receiving random noise. + // Would be better to show only when debug is enabled but variable not available here. + // TODO: For now we will just suppress it. + //text_color_set(DW_COLOR_ERROR); + //Debugprintf ("IL2P: Invalid character '%c' in destination address '%s'\n", addrs[AX25_DESTINATION][i], addrs[AX25_DESTINATION]); + } + return (NULL); + } + } + sprintf(addrs[AX25_DESTINATION] + strlen(addrs[AX25_DESTINATION]), "-%d", (hdr[12] >> 4) & 0xf); + + for (int i = 0; i <= 5; i++) { + addrs[AX25_SOURCE][i] = sixbit_to_ascii(hdr[i + 6] & 0x3f); + } + trim(addrs[AX25_SOURCE]); + for (int i = 0; i < strlen(addrs[AX25_SOURCE]); i++) { + if (!isupper(addrs[AX25_SOURCE][i]) && !isdigit(addrs[AX25_SOURCE][i])) { + if (num_sym_changed == 0) { + // This can pop up sporadically when receiving random noise. + // Would be better to show only when debug is enabled but variable not available here. + // TODO: For now we will just suppress it. + //text_color_set(DW_COLOR_ERROR); + //Debugprintf ("IL2P: Invalid character '%c' in source address '%s'\n", addrs[AX25_SOURCE][i], addrs[AX25_SOURCE]); + } + return (NULL); + } + } + sprintf(addrs[AX25_SOURCE] + strlen(addrs[AX25_SOURCE]), "-%d", hdr[12] & 0xf); + + // The PID field gives us the general type. + // 0 = 'S' frame. + // 1 = 'U' frame other than UI. + // others are either 'UI' or 'I' depending on the UI field. + + int pid = GET_PID(hdr); + int ui = GET_UI(hdr); + + if (pid == 0) { + + // 'S' frame. + // The control field contains: P/F N(R) C S S + + int control = GET_CONTROL(hdr); + cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res; + ax25_frame_type_t ftype; + switch (control & 0x03) { + case 0: ftype = frame_type_S_RR; break; + case 1: ftype = frame_type_S_RNR; break; + case 2: ftype = frame_type_S_REJ; break; + default: ftype = frame_type_S_SREJ; break; + } + int modulo = 8; + int nr = (control >> 3) & 0x07; + int pf = (control >> 6) & 0x01; + unsigned char *pinfo = NULL; // Any info for SREJ will be added later. + int info_len = 0; + return (ax25_s_frame(addrs, num_addr, cr, ftype, modulo, nr, pf, pinfo, info_len)); + } + else if (pid == 1) { + + // 'U' frame other than 'UI'. + // The control field contains: P/F OPCODE{3) C x x + + int control = GET_CONTROL(hdr); + cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res; + int axpid = 0; // unused for U other than UI. + ax25_frame_type_t ftype; + switch ((control >> 3) & 0x7) { + case 0: ftype = frame_type_U_SABM; break; + case 1: ftype = frame_type_U_DISC; break; + case 2: ftype = frame_type_U_DM; break; + case 3: ftype = frame_type_U_UA; break; + case 4: ftype = frame_type_U_FRMR; break; + case 5: ftype = frame_type_U_UI; axpid = 0xf0; break; // Should not happen with IL2P pid == 1. + case 6: ftype = frame_type_U_XID; break; + default: ftype = frame_type_U_TEST; break; + } + int pf = (control >> 6) & 0x01; + unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later. + int info_len = 0; + return (ax25_u_frame(addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len)); + } + else if (ui) { + + // 'UI' frame. + // The control field contains: P/F OPCODE{3) C x x + + int control = GET_CONTROL(hdr); + cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res; + ax25_frame_type_t ftype = frame_type_U_UI; + int pf = (control >> 6) & 0x01; + int axpid = decode_pid(GET_PID(hdr)); + unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later. + int info_len = 0; + return (ax25_u_frame(addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len)); + } + else { + + // 'I' frame. + // The control field contains: P/F N(R) N(S) + + int control = GET_CONTROL(hdr); + cmdres_t cr = cr_cmd; // Always command. + int pf = (control >> 6) & 0x01; + int nr = (control >> 3) & 0x7; + int ns = control & 0x7; + int modulo = 8; + int axpid = decode_pid(GET_PID(hdr)); + unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later. + int info_len = 0; + return (ax25_i_frame(addrs, num_addr, cr, modulo, nr, ns, pf, axpid, pinfo, info_len)); + } + return (NULL); // unreachable but avoid warning. + +} // end + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_type_0_header + * + * Purpose: Attempt to create type 0 header from packet object. + * + * Inputs: pp - Packet object. + * + * max_fec - 1 to use maximum FEC symbols, 0 for automatic. + * + * Outputs: hdr - IL2P header with no scrambling or parity symbols. + * Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes. + * + * Returns: Number of bytes for information part or -1 for failure. + * In case of failure, fall back to type 0 transparent encapsulation. + * + * Description: The type 0 header is used when it is not one of the restricted cases + * covered by the type 1 header. + * The AX.25 frame is put in the payload. + * This will cover: more than one address, mod 128 sequences, etc. + * + *--------------------------------------------------------------------------------*/ + +int il2p_type_0_header(packet_t pp, int max_fec, unsigned char *hdr) +{ + memset(hdr, 0, IL2P_HEADER_SIZE); + + // Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10] + + SET_FEC_LEVEL(hdr, max_fec); + SET_HDR_TYPE(hdr, 0); + + int frame_len = ax25_get_frame_len(pp); + + if (frame_len < 14 || frame_len > IL2P_MAX_PAYLOAD_SIZE) { + return (-2); + } + + SET_PAYLOAD_BYTE_COUNT(hdr, frame_len); + return (frame_len); +} + + +/*********************************************************************************** + * + * Name: il2p_get_header_attributes + * + * Purpose: Extract a few attributes from an IL2p header. + * + * Inputs: hdr - IL2P header structure. + * + * Outputs: hdr_type - 0 or 1. + * + * max_fec - 0 for automatic or 1 for fixed maximum size. + * + * Returns: Payload byte count. (actual payload size, not the larger encoded format) + * + ***********************************************************************************/ + + +int il2p_get_header_attributes(unsigned char *hdr, int *hdr_type, int *max_fec) +{ + *hdr_type = GET_HDR_TYPE(hdr); + *max_fec = GET_FEC_LEVEL(hdr); + return(GET_PAYLOAD_BYTE_COUNT(hdr)); +} + + +/*********************************************************************************** + * + * Name: il2p_clarify_header + * + * Purpose: Convert received header to usable form. + * This involves RS FEC then descrambling. + * + * Inputs: rec_hdr - Header as received over the radio. + * + * Outputs: corrected_descrambled_hdr - After RS FEC and unscrambling. + * + * Returns: Number of symbols that were corrected: + * 0 = No errors + * 1 = Single symbol corrected. + * <0 = Unable to obtain good header. + * + ***********************************************************************************/ + +int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_descrambled_hdr) +{ + unsigned char corrected[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; + + int e = il2p_decode_rs(rec_hdr, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, corrected); + + il2p_descramble_block(corrected, corrected_descrambled_hdr, IL2P_HEADER_SIZE); + + return (e); +} + +// end il2p_header.c + + +/*-------------------------------------------------------------------------------- + * + * File: il2p_payload.c + * + * Purpose: Functions dealing with the payload. + * + *--------------------------------------------------------------------------------*/ + + + /*-------------------------------------------------------------------------------- + * + * Function: il2p_payload_compute + * + * Purpose: Compute number and sizes of data blocks based on total size. + * + * Inputs: payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE) + * max_fec true for 16 parity symbols, false for automatic. + * + * Outputs: *p Payload block sizes and counts. + * Number of parity symbols per block. + * + * Returns: Number of bytes in the encoded format. + * Could be 0 for no payload blocks. + * -1 for error (i.e. invalid unencoded size: <0 or >1023) + * + *--------------------------------------------------------------------------------*/ + +int il2p_payload_compute(il2p_payload_properties_t *p, int payload_size, int max_fec) +{ + memset(p, 0, sizeof(il2p_payload_properties_t)); + + if (payload_size < 0 || payload_size > IL2P_MAX_PAYLOAD_SIZE) { + return (-1); + } + if (payload_size == 0) { + return (0); + } + + if (max_fec) { + p->payload_byte_count = payload_size; + p->payload_block_count = (p->payload_byte_count + 238) / 239; + p->small_block_size = p->payload_byte_count / p->payload_block_count; + p->large_block_size = p->small_block_size + 1; + p->large_block_count = p->payload_byte_count - (p->payload_block_count * p->small_block_size); + p->small_block_count = p->payload_block_count - p->large_block_count; + p->parity_symbols_per_block = 16; + } + else { + p->payload_byte_count = payload_size; + p->payload_block_count = (p->payload_byte_count + 246) / 247; + p->small_block_size = p->payload_byte_count / p->payload_block_count; + p->large_block_size = p->small_block_size + 1; + p->large_block_count = p->payload_byte_count - (p->payload_block_count * p->small_block_size); + p->small_block_count = p->payload_block_count - p->large_block_count; + //p->parity_symbols_per_block = (p->small_block_size / 32) + 2; // Looks like error in documentation + + // It would work if the number of parity symbols was based on large block size. + + if (p->small_block_size <= 61) p->parity_symbols_per_block = 2; + else if (p->small_block_size <= 123) p->parity_symbols_per_block = 4; + else if (p->small_block_size <= 185) p->parity_symbols_per_block = 6; + else if (p->small_block_size <= 247) p->parity_symbols_per_block = 8; + else { + // Should not happen. But just in case... + Debugprintf("IL2P parity symbol per payload block error. small_block_size = %d\n", p->small_block_size); + return (-1); + } + } + + // Return the total size for the encoded format. + + return (p->small_block_count * (p->small_block_size + p->parity_symbols_per_block) + + p->large_block_count * (p->large_block_size + p->parity_symbols_per_block)); + +} // end il2p_payload_compute + + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_encode_payload + * + * Purpose: Split payload into multiple blocks such that each set + * of data and parity symbols fit into a 255 byte RS block. + * + * Inputs: *payload Array of bytes. + * payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE) + * max_fec true for 16 parity symbols, false for automatic. + * + * Outputs: *enc Encoded payload for transmission. + * Up to IL2P_MAX_ENCODED_SIZE bytes. + * + * Returns: -1 for error (i.e. invalid size) + * 0 for no blocks. (i.e. size zero) + * Number of bytes generated. Maximum IL2P_MAX_ENCODED_SIZE. + * + * Note: I interpreted the protocol spec as saying the LFSR state is retained + * between data blocks. During interoperability testing, I found that + * was not the case. It is reset for each data block. + * + *--------------------------------------------------------------------------------*/ + + +int il2p_encode_payload(unsigned char *payload, int payload_size, int max_fec, unsigned char *enc) +{ + if (payload_size > IL2P_MAX_PAYLOAD_SIZE) return (-1); + if (payload_size == 0) return (0); + + // Determine number of blocks and sizes. + + il2p_payload_properties_t ipp; + int e; + e = il2p_payload_compute(&ipp, payload_size, max_fec); + if (e <= 0) { + return (e); + } + + unsigned char *pin = payload; + unsigned char *pout = enc; + int encoded_length = 0; + unsigned char scram[256]; + unsigned char parity[IL2P_MAX_PARITY_SYMBOLS]; + + // First the large blocks. + + for (int b = 0; b < ipp.large_block_count; b++) { + + il2p_scramble_block(pin, scram, ipp.large_block_size); + memcpy(pout, scram, ipp.large_block_size); + pin += ipp.large_block_size; + pout += ipp.large_block_size; + encoded_length += ipp.large_block_size; + il2p_encode_rs(scram, ipp.large_block_size, ipp.parity_symbols_per_block, parity); + memcpy(pout, parity, ipp.parity_symbols_per_block); + pout += ipp.parity_symbols_per_block; + encoded_length += ipp.parity_symbols_per_block; + } + + // Then the small blocks. + + for (int b = 0; b < ipp.small_block_count; b++) { + + il2p_scramble_block(pin, scram, ipp.small_block_size); + memcpy(pout, scram, ipp.small_block_size); + pin += ipp.small_block_size; + pout += ipp.small_block_size; + encoded_length += ipp.small_block_size; + il2p_encode_rs(scram, ipp.small_block_size, ipp.parity_symbols_per_block, parity); + memcpy(pout, parity, ipp.parity_symbols_per_block); + pout += ipp.parity_symbols_per_block; + encoded_length += ipp.parity_symbols_per_block; + } + + return (encoded_length); + +} // end il2p_encode_payload + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_decode_payload + * + * Purpose: Extract original data from encoded payload. + * + * Inputs: received Array of bytes. Size is unknown but in practice it + * must not exceed IL2P_MAX_ENCODED_SIZE. + * payload_size 0 to 1023. (IL2P_MAX_PAYLOAD_SIZE) + * Expected result size based on header. + * max_fec true for 16 parity symbols, false for automatic. + * + * Outputs: payload_out Recovered payload. + * + * In/Out: symbols_corrected Number of symbols corrected. + * + * + * Returns: Number of bytes extracted. Should be same as payload_size going in. + * -3 for unexpected internal inconsistency. + * -2 for unable to recover from signal corruption. + * -1 for invalid size. + * 0 for no blocks. (i.e. size zero) + * + * Description: Each block is scrambled separately but the LSFR state is carried + * from the first payload block to the next. + * + *--------------------------------------------------------------------------------*/ + +int il2p_decode_payload(unsigned char *received, int payload_size, int max_fec, unsigned char *payload_out, int *symbols_corrected) +{ + // Determine number of blocks and sizes. + + il2p_payload_properties_t ipp; + int e; + e = il2p_payload_compute(&ipp, payload_size, max_fec); + if (e <= 0) { + return (e); + } + + unsigned char *pin = received; + unsigned char *pout = payload_out; + int decoded_length = 0; + int failed = 0; + + // First the large blocks. + + for (int b = 0; b < ipp.large_block_count; b++) { + unsigned char corrected_block[255]; + int e = il2p_decode_rs(pin, ipp.large_block_size, ipp.parity_symbols_per_block, corrected_block); + + // Debugprintf ("%s:%d: large block decode_rs returned status = %d\n", __FILE__, __LINE__, e); + + if (e < 0) failed = 1; + *symbols_corrected += e; + + il2p_descramble_block(corrected_block, pout, ipp.large_block_size); + + if (il2p_get_debug() >= 2) { + + Debugprintf("Descrambled large payload block, %d bytes:\n", ipp.large_block_size); + fx_hex_dump(pout, ipp.large_block_size); + } + + pin += ipp.large_block_size + ipp.parity_symbols_per_block; + pout += ipp.large_block_size; + decoded_length += ipp.large_block_size; + } + + // Then the small blocks. + + for (int b = 0; b < ipp.small_block_count; b++) { + unsigned char corrected_block[255]; + int e = il2p_decode_rs(pin, ipp.small_block_size, ipp.parity_symbols_per_block, corrected_block); + + // Debugprintf ("%s:%d: small block decode_rs returned status = %d\n", __FILE__, __LINE__, e); + + if (e < 0) failed = 1; + *symbols_corrected += e; + + il2p_descramble_block(corrected_block, pout, ipp.small_block_size); + + if (il2p_get_debug() >= 2) { + + Debugprintf("Descrambled small payload block, %d bytes:\n", ipp.small_block_size); + fx_hex_dump(pout, ipp.small_block_size); + } + + pin += ipp.small_block_size + ipp.parity_symbols_per_block; + pout += ipp.small_block_size; + decoded_length += ipp.small_block_size; + } + + if (failed) { + //Debugprintf ("%s:%d: failed = %0x\n", __FILE__, __LINE__, failed); + return (-2); + } + + if (decoded_length != payload_size) { + Debugprintf("IL2P Internal error: decoded_length = %d, payload_size = %d\n", decoded_length, payload_size); + return (-3); + } + + return (decoded_length); + +} // end il2p_decode_payload + +// end il2p_payload.c + + + +struct il2p_context_s { + + enum { IL2P_SEARCHING = 0, IL2P_HEADER, IL2P_PAYLOAD, IL2P_DECODE } state; + + unsigned int acc; // Accumulate most recent 24 bits for sync word matching. + // Lower 8 bits are also used for accumulating bytes for + // the header and payload. + + int bc; // Bit counter so we know when a complete byte has been accumulated. + + int polarity; // 1 if opposite of expected polarity. + + unsigned char shdr[IL2P_HEADER_SIZE + IL2P_HEADER_PARITY]; + // Scrambled header as received over the radio. Includes parity. + int hc; // Number if bytes placed in above. + + unsigned char uhdr[IL2P_HEADER_SIZE]; // Header after FEC and unscrambling. + + int eplen; // Encoded payload length. This is not the nuumber from + // from the header but rather the number of encoded bytes to gather. + + unsigned char spayload[IL2P_MAX_ENCODED_PAYLOAD_SIZE]; + // Scrambled and encoded payload as received over the radio. + int pc; // Number of bytes placed in above. + + int corrected; // Number of symbols corrected by RS FEC. +}; + +static struct il2p_context_s *il2p_context[MAX_CHANS][MAX_SUBCHANS][MAX_SLICERS]; + + + +/*********************************************************************************** + * + * Name: il2p_rec_bit + * + * Purpose: Extract FX.25 packets from a stream of bits. + * + * Inputs: chan - Channel number. + * + * subchan - This allows multiple demodulators per channel. + * + * slice - Allows multiple slicers per demodulator (subchannel). + * + * dbit - One bit from the received data stream. + * + * Description: This is called once for each received bit. + * For each valid packet, process_rec_frame() is called for further processing. + * It can gather multiple candidates from different parallel demodulators + * ("subchannels") and slicers, then decide which one is the best. + * + ***********************************************************************************/ + +int centreFreq[4] = { 0, 0, 0, 0 }; + +void il2p_rec_bit(int chan, int subchan, int slice, int dbit) +{ + // Allocate context blocks only as needed. + + if (dbit) + dbit = 1; + + struct il2p_context_s *F = il2p_context[chan][subchan][slice]; + if (F == NULL) { + //assert(chan >= 0 && chan < MAX_CHANS); + //assert(subchan >= 0 && subchan < MAX_SUBCHANS); + //assert(slice >= 0 && slice < MAX_SLICERS); + F = il2p_context[chan][subchan][slice] = (struct il2p_context_s *)malloc(sizeof(struct il2p_context_s)); + //assert(F != NULL); + memset(F, 0, sizeof(struct il2p_context_s)); + } + + // Accumulate most recent 24 bits received. Most recent is LSB. + + F->acc = ((F->acc << 1) | (dbit & 1)) & 0x00ffffff; + + // State machine to look for sync word then gather appropriate number of header and payload bytes. + + switch (F->state) { + + case IL2P_SEARCHING: // Searching for the sync word. + + if (__builtin_popcount(F->acc ^ IL2P_SYNC_WORD) <= 1) { // allow single bit mismatch + //text_color_set (DW_COLOR_INFO); + //Debugprintf ("IL2P header has normal polarity\n"); + F->polarity = 0; + F->state = IL2P_HEADER; + F->bc = 0; + F->hc = 0; + + // Determine Centre Freq + + centreFreq[chan] = GuessCentreFreq(chan); + } + else if (__builtin_popcount((~F->acc & 0x00ffffff) ^ IL2P_SYNC_WORD) <= 1) { + // FIXME - this pops up occasionally with random noise. Find better way to convey information. + // This also happens for each slicer - to noisy. + //Debugprintf ("IL2P header has reverse polarity\n"); + F->polarity = 1; + F->state = IL2P_HEADER; + F->bc = 0; + F->hc = 0; + centreFreq[chan] = GuessCentreFreq(chan); + } + + break; + + case IL2P_HEADER: // Gathering the header. + + F->bc++; + if (F->bc == 8) { // full byte has been collected. + F->bc = 0; + if (!F->polarity) { + F->shdr[F->hc++] = F->acc & 0xff; + } + else { + F->shdr[F->hc++] = (~F->acc) & 0xff; + } + if (F->hc == IL2P_HEADER_SIZE + IL2P_HEADER_PARITY) { // Have all of header + + //if (il2p_get_debug() >= 1) + //{ + // Debugprintf("IL2P header as received [%d.%d.%d]:\n", chan, subchan, slice); + // fx_hex_dump(F->shdr, IL2P_HEADER_SIZE + IL2P_HEADER_PARITY); + //} + + // Fix any errors and descramble. + F->corrected = il2p_clarify_header(F->shdr, F->uhdr); + + if (F->corrected >= 0) { // Good header. + // How much payload is expected? + il2p_payload_properties_t plprop; + int hdr_type, max_fec; + int len = il2p_get_header_attributes(F->uhdr, &hdr_type, &max_fec); + + F->eplen = il2p_payload_compute(&plprop, len, max_fec); + + if (il2p_get_debug() >= 1) + { + Debugprintf("Header type %d, max fec = %d", hdr_type, max_fec); + Debugprintf("Need to collect %d encoded bytes for %d byte payload.", F->eplen, len); + Debugprintf("%d small blocks of %d and %d large blocks of %d. %d parity symbols per block", + plprop.small_block_count, plprop.small_block_size, + plprop.large_block_count, plprop.large_block_size, plprop.parity_symbols_per_block); + } + + if (F->eplen >= 1) { // Need to gather payload. + F->pc = 0; + F->state = IL2P_PAYLOAD; + } + else if (F->eplen == 0) { // No payload. + F->pc = 0; + F->state = IL2P_DECODE; + } + else { // Error. + + if (il2p_get_debug() >= 1) { + Debugprintf("IL2P header INVALID.\n"); + } + + F->state = IL2P_SEARCHING; + } + } // good header after FEC. + else { + F->state = IL2P_SEARCHING; // Header failed FEC check. + } + } // entire header has been collected. + } // full byte collected. + break; + + case IL2P_PAYLOAD: // Gathering the payload, if any. + + F->bc++; + if (F->bc == 8) { // full byte has been collected. + F->bc = 0; + if (!F->polarity) { + F->spayload[F->pc++] = F->acc & 0xff; + } + else { + F->spayload[F->pc++] = (~F->acc) & 0xff; + } + if (F->pc == F->eplen) { + + // TODO?: for symmetry it seems like we should clarify the payload before combining. + + F->state = IL2P_DECODE; + } + } + break; + + case IL2P_DECODE: + // We get here after a good header and any payload has been collected. + // Processing is delayed by one bit but I think it makes the logic cleaner. + // During unit testing be sure to send an extra bit to flush it out at the end. + + // in uhdr[IL2P_HEADER_SIZE]; // Header after FEC and descrambling. + + // TODO?: for symmetry, we might decode the payload here and later build the frame. + + { + packet_t pp = il2p_decode_header_payload(F->uhdr, F->spayload, &(F->corrected)); + + if (il2p_get_debug() >= 1) + { + if (pp == NULL) + { + // Most likely too many FEC errors. + Debugprintf("FAILED to construct frame in %s.\n", __func__); + } + } + + if (pp != NULL) { + alevel_t alevel = demod_get_audio_level(chan, subchan); + retry_t retries = F->corrected; + int is_fx25 = 1; // FIXME: distinguish fx.25 and IL2P. + // Currently this just means that a FEC mode was used. + + // TODO: Could we put last 3 arguments in packet object rather than passing around separately? + + multi_modem_process_rec_packet(chan, subchan, slice, pp, alevel, retries, is_fx25, slice, centreFreq[chan]); + } + } // end block for local variables. + + if (il2p_get_debug() >= 1) { + + Debugprintf("-----"); + } + + F->state = IL2P_SEARCHING; + break; + + } // end of switch + +} // end il2p_rec_bit + + + + + + + +// Scramble bits for il2p transmit. + +// Note that there is a delay of 5 until the first bit comes out. +// So we need to need to ignore the first 5 out and stick in +// an extra 5 filler bits to flush at the end. + +#define INIT_TX_LSFR 0x00f + +static inline int scramble_bit(int in, int *state) +{ + int out = ((*state >> 4) ^ *state) & 1; + *state = ((((in ^ *state) & 1) << 9) | (*state ^ ((*state & 1) << 4))) >> 1; + return (out); +} + + +// Undo data scrambling for il2p receive. + +#define INIT_RX_LSFR 0x1f0 + +static inline int descramble_bit(int in, int *state) +{ + int out = (in ^ *state) & 1; + *state = ((*state >> 1) | ((in & 1) << 8)) ^ ((in & 1) << 3); + return (out); +} + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_scramble_block + * + * Purpose: Scramble a block before adding RS parity. + * + * Inputs: in Array of bytes. + * len Number of bytes both in and out. + * + * Outputs: out Array of bytes. + * + *--------------------------------------------------------------------------------*/ + +void il2p_scramble_block(unsigned char *in, unsigned char *out, int len) +{ + int tx_lfsr_state = INIT_TX_LSFR; + + memset(out, 0, len); + + int skipping = 1; // Discard the first 5 out. + int ob = 0; // Index to output byte. + int om = 0x80; // Output bit mask; + for (int ib = 0; ib < len; ib++) { + for (int im = 0x80; im != 0; im >>= 1) { + int s = scramble_bit((in[ib] & im) != 0, &tx_lfsr_state); + if (ib == 0 && im == 0x04) skipping = 0; + if (!skipping) { + if (s) { + out[ob] |= om; + } + om >>= 1; + if (om == 0) { + om = 0x80; + ob++; + } + } + } + } + // Flush it. + + // This is a relic from when I thought the state would need to + // be passed along for the next block. + // Preserve the LSFR state from before flushing. + // This might be needed as the initial state for later payload blocks. + int x = tx_lfsr_state; + for (int n = 0; n < 5; n++) { + int s = scramble_bit(0, &x); + if (s) { + out[ob] |= om; + } + om >>= 1; + if (om == 0) { + om = 0x80; + ob++; + } + } + +} // end il2p_scramble_block + + + +/*-------------------------------------------------------------------------------- + * + * Function: il2p_descramble_block + * + * Purpose: Descramble a block after removing RS parity. + * + * Inputs: in Array of bytes. + * len Number of bytes both in and out. + * + * Outputs: out Array of bytes. + * + *--------------------------------------------------------------------------------*/ + +void il2p_descramble_block(unsigned char *in, unsigned char *out, int len) +{ + int rx_lfsr_state = INIT_RX_LSFR; + + memset(out, 0, len); + + for (int b = 0; b < len; b++) { + for (int m = 0x80; m != 0; m >>= 1) { + int d = descramble_bit((in[b] & m) != 0, &rx_lfsr_state); + if (d) { + out[b] |= m; + } + } + } +} + +// end il2p_scramble.c + + + + +static int number_of_bits_sent[MAX_CHANS]; // Count number of bits sent by "il2p_send_frame" + +static void send_bytes(int chan, unsigned char *b, int count, int polarity); +static void send_bit(int chan, int b, int polarity); + + + +/*------------------------------------------------------------- + * + * Name: il2p_send_frame + * + * Purpose: Convert frames to a stream of bits in IL2P format. + * + * Inputs: chan - Audio channel number, 0 = first. + * + * pp - Pointer to packet object. + * + * max_fec - 1 to force 16 parity symbols for each payload block. + * 0 for automatic depending on block size. + * + * polarity - 0 for normal. 1 to invert signal. + * 2 special case for testing - introduce some errors to test FEC. + * + * Outputs: Bits are shipped out by calling tone_gen_put_bit(). + * + * Returns: Number of bits sent including + * - Preamble (01010101...) + * - 3 byte Sync Word. + * - 15 bytes for Header. + * - Optional payload. + * The required time can be calculated by dividing this + * number by the transmit rate of bits/sec. + * -1 is returned for failure. + * + * Description: Generate an IL2P encoded frame. + * + * Assumptions: It is assumed that the tone_gen module has been + * properly initialized so that bits sent with + * tone_gen_put_bit() are processed correctly. + * + * Errors: Return -1 for error. Probably frame too large. + * + * Note: Inconsistency here. ax25 version has just a byte array + * and length going in. Here we need the full packet object. + * + *--------------------------------------------------------------*/ + +string * il2p_send_frame(int chan, packet_t pp, int max_fec, int polarity) +{ + unsigned char encoded[IL2P_MAX_PACKET_SIZE]; + string * packet = newString(); + int preamblecount; + unsigned char preamble[1024]; + + + encoded[0] = (IL2P_SYNC_WORD >> 16) & 0xff; + encoded[1] = (IL2P_SYNC_WORD >> 8) & 0xff; + encoded[2] = (IL2P_SYNC_WORD) & 0xff; + + int elen = il2p_encode_frame(pp, max_fec, encoded + IL2P_SYNC_WORD_SIZE); + if (elen <= 0) { + Debugprintf("IL2P: Unable to encode frame into IL2P.\n"); + return (packet); + } + + elen += IL2P_SYNC_WORD_SIZE; + + number_of_bits_sent[chan] = 0; + + if (il2p_get_debug() >= 1) { + Debugprintf("IL2P frame, max_fec = %d, %d encoded bytes total", max_fec, elen); +// fx_hex_dump(encoded, elen); + } + + // Send bits to modulator. + + // Try using preaamble for txdelay + + preamblecount = (txdelay[chan] * tx_baudrate[chan]) / 8000; // 8 for bits, 1000 for mS + + if (preamblecount > 1024) + preamblecount = 1024; + + memset(preamble, IL2P_PREAMBLE, preamblecount); + + stringAdd(packet, preamble, preamblecount); + stringAdd(packet, encoded, elen); + + tx_fx25_size[chan] = packet->Length * 8; + + return packet; +} + + + +// TX Code. Builds whole packet then sends a bit at a time + +#define TX_SILENCE 0 +#define TX_DELAY 1 +#define TX_TAIL 2 +#define TX_NO_DATA 3 +#define TX_FRAME 4 +#define TX_WAIT_BPF 5 + + +#define TX_BIT0 0 +#define TX_BIT1 1 +#define FRAME_EMPTY 0 +#define FRAME_FULL 1 +#define FRAME_NO_FRAME 2 +#define FRAME_NEW_FRAME 3 +#define BYTE_EMPTY 0 +#define BYTE_FULL 1 + +extern UCHAR tx_frame_status[5]; +extern UCHAR tx_byte_status[5]; +extern string * tx_data[5]; +extern int tx_data_len[5]; +extern UCHAR tx_bit_stream[5]; +extern UCHAR tx_bit_cnt[5]; +extern long tx_tail_cnt[5]; +extern BOOL tx_bs_bit[5]; + +string * fill_il2p_data(int snd_ch, string * data) +{ + string * result; + packet_t pp = ax25_new(); + + + // Call il2p_send_frame to build the bit stream + + pp->frame_len = data->Length - 2; // Included CRC + memcpy(pp->frame_data, data->Data, data->Length); + + result = il2p_send_frame(snd_ch, pp, 1, 0); + + return result; +} + + + +void il2p_get_new_frame(int snd_ch, TStringList * frame_stream) +{ + string * myTemp; + + tx_bs_bit[snd_ch] = 0; + tx_bit_cnt[snd_ch] = 0; + tx_fx25_size_cnt[snd_ch] = 0; + tx_fx25_size[snd_ch] = 1; + tx_frame_status[snd_ch] = FRAME_NEW_FRAME; + tx_byte_status[snd_ch] = BYTE_EMPTY; + + if (frame_stream->Count == 0) + tx_frame_status[snd_ch] = FRAME_NO_FRAME; + else + { + // We now pass control byte and ack bytes on front and pointer to socket on end if ackmode + + myTemp = Strings(frame_stream, 0); // get message + + if ((myTemp->Data[0] & 0x0f) == 12) // ACKMODE + { + // Save copy then copy data up 3 bytes + + Add(&KISS_acked[snd_ch], duplicateString(myTemp)); + + mydelete(myTemp, 0, 3); + myTemp->Length -= sizeof(void *); + } + else + { + // Just remove control + + mydelete(myTemp, 0, 1); + } + + AGW_AX25_frame_analiz(snd_ch, FALSE, myTemp); + put_frame(snd_ch, myTemp, "", TRUE, FALSE); + + tx_data[snd_ch] = fill_il2p_data(snd_ch, myTemp); + + Delete(frame_stream, 0); // This will invalidate temp + } +} + + + +// Original code + +/* +static void send_bytes(int chan, unsigned char *b, int count, int polarity) +{ + for (int j = 0; j < count; j++) { + unsigned int x = b[j]; + for (int k = 0; k < 8; k++) { + send_bit(chan, (x & 0x80) != 0, polarity); + x <<= 1; + } + } +} + +// NRZI would be applied for AX.25 but IL2P does not use it. +// However we do have an option to invert the signal. +// The direwolf receive implementation will automatically compensate +// for either polarity but other implementations might not. + +static void send_bit(int chan, int b, int polarity) +{ + tone_gen_put_bit(chan, (b ^ polarity) & 1); + number_of_bits_sent[chan]++; +} +*/ + + + + +int il2p_get_new_bit(int snd_ch, Byte bit) +{ + string *s; + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + { + il2p_get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + if (tx_frame_status[snd_ch] == FRAME_NEW_FRAME) + tx_frame_status[snd_ch] = FRAME_FULL; + } + + if (tx_frame_status[snd_ch] == FRAME_FULL) + { + if (tx_byte_status[snd_ch] == BYTE_EMPTY) + { + if (tx_data[snd_ch]->Length) + { + s = tx_data[snd_ch]; + + tx_bit_stream[snd_ch] = s->Data[0]; + tx_frame_status[snd_ch] = FRAME_FULL; + tx_byte_status[snd_ch] = BYTE_FULL; + tx_bit_cnt[snd_ch] = 0; + mydelete(tx_data[snd_ch], 0, 1); + } + else + tx_frame_status[snd_ch] = FRAME_EMPTY; + } + if (tx_byte_status[snd_ch] == BYTE_FULL) + { + // il2p sends high order bit first + + bit = tx_bit_stream[snd_ch] >> 7; // top bit to bottom + + tx_bit_stream[snd_ch] = tx_bit_stream[snd_ch] << 1; + tx_bit_cnt[snd_ch]++; + tx_fx25_size_cnt[snd_ch]++; + if (tx_bit_cnt[snd_ch] >= 8) + tx_byte_status[snd_ch] = BYTE_EMPTY; + if (tx_fx25_size_cnt[snd_ch] == tx_fx25_size[snd_ch]) + tx_frame_status[snd_ch] = FRAME_EMPTY; + } + } + + if (tx_frame_status[snd_ch] == FRAME_EMPTY) + { + il2p_get_new_frame(snd_ch, &all_frame_buf[snd_ch]); + + switch (tx_frame_status[snd_ch]) + { + case FRAME_NEW_FRAME: + tx_frame_status[snd_ch] = FRAME_FULL; + break; + + case FRAME_NO_FRAME: + tx_tail_cnt[snd_ch] = 0; + tx_frame_status[snd_ch] = FRAME_EMPTY; + tx_status[snd_ch] = TX_TAIL; + break; + } + } + return bit; +} + + + diff --git a/kiss_mode.c b/kiss_mode.c new file mode 100644 index 0000000..eb21953 --- /dev/null +++ b/kiss_mode.c @@ -0,0 +1,490 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" + +/* +uses sysutils,classes; + + procedure KISS_init; + procedure KISS_free; + procedure KISS_add_stream(socket: integer); + procedure KISS_del_stream(socket: integer); + procedure KISS_on_data_in(socket: integer; data: string); + procedure KISS_on_data_out(port: byte; frame: string); + procedure KISS_send_ack(port: byte; data: string); + procedure KISS_send_ack1(port: byte); +*/ +// I don't like this. maybe fine for Dephi but overcomlicated for C + +// I think I need a struct for each connection, but a simple array of entries should be fine +// My normal ** and count system +// Each needs an input buffer of max size kiss frame and length (or maybe string is a good idea) + +TKISSMode ** KissConnections = NULL; +int KISSConCount = 0; + +#define FEND 0xc0 +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD +#define KISS_ACKMODE 0x0C +#define KISS_DATA 0 + +struct TKISSMode_t KISS; + +int KISS_encode(UCHAR * KISSBuffer, int port, string * frame, int TXMON); + +void KISS_init() +{ + int i; + + KISS.data_in = newString(); + +// initTStringList(KISS.socket); + + for (i = 0; i < 4; i++) + { + initTStringList(&KISS.buffer[i]); + } +} + + +/* +procedure KISS_free; +var + i: byte; +begin + KISS.data_in.Free; + KISS.socket.Free; + for i:=1 to 4 do + begin + KISS.buffer[i].Free; + KISS.request[i].Free; + KISS.acked[i].Free; + KISS.irequest[i].Free; + KISS.iacked[i].Free; + end; +end; +*/ + +void KISS_add_stream(void * Socket) +{ + // Add a new connection. Called when QT accepts an incoming call} + + TKISSMode * KISS; + + KissConnections = realloc(KissConnections, (KISSConCount + 1) * sizeof(void *)); + + KISS = KissConnections[KISSConCount++] = malloc(sizeof(KISS)); + + KISS->Socket = Socket; + KISS->data_in = newString(); + +} + +void KISS_del_socket(void * socket) +{ + int i; + + TKISSMode * KISS = NULL; + + if (KISSConCount == 0) + return; + + for (i = 0; i < KISSConCount; i++) + { + if (KissConnections[i]->Socket == socket) + { + KISS = KissConnections[i]; + break; + } + } + + if (KISS == NULL) + return; + + // Need to remove entry and move others down + + KISSConCount--; + + while (i < KISSConCount) + { + KissConnections[i] = KissConnections[i + 1]; + i++; + } +} + + +void KISS_on_data_out(int port, string * frame, int TX) +{ + int Len; + UCHAR * KISSFrame = (UCHAR *)malloc(512); // cant pass local data via signal/slot + + Len = KISS_encode(KISSFrame, port, frame, TX); + + KISSSendtoServer(NULL, KISSFrame, Len); // Send to all open sockets +} + +void ProcessKISSFrame(void * socket, UCHAR * Msg, int Len) +{ + int n = Len; + UCHAR c; + int ESCFLAG = 0; + UCHAR * ptr1, *ptr2; + int Chan; + int Opcode; + string * TXMSG; + unsigned short CRC; + UCHAR CRCString[2]; + + ptr1 = ptr2 = Msg; + + while (n--) + { + c = *(ptr1++); + + if (ESCFLAG) + { + // + // FESC received - next should be TFESC or TFEND + + ESCFLAG = 0; + + if (c == TFESC) + c = FESC; + + if (c == TFEND) + c = FEND; + + } + else + { + switch (c) + { + case FEND: + + // + // Either start of message or message complete + // + + // npKISSINFO->MSGREADY = TRUE; + return; + + case FESC: + + ESCFLAG = 1; + continue; + + } + } + + // + // Ok, a normal char + // + + *(ptr2++) = c; + + } + Len = ptr2 - Msg; + + Chan = (Msg[0] >> 4); + Opcode = Msg[0] & 0x0f; + + if (Chan > 3) + return; + + switch (Opcode) + { + case KISS_ACKMODE: + + // How best to do ACKMODE?? I think pass whole frame including CMD and ack bytes to all_frame_buf + + // But ack should only be sent to client that sent the message - needs more thought! + + TXMSG = newString(); + stringAdd(TXMSG, &Msg[0], Len); // include Control + + CRC = get_fcs(&Msg[3], Len - 3); // exclude control and ack bytes + + CRCString[0] = CRC & 0xff; + CRCString[1] = CRC >> 8; + + stringAdd(TXMSG, CRCString, 2); + + // Ackmode needs to know where to send ack back to, so save socket on end of data + + stringAdd(TXMSG, (unsigned char * )&socket, sizeof(socket)); + + // if KISS Optimise see if frame is really needed + + if (!KISS_opt[Chan]) + Add(&KISS.buffer[Chan], TXMSG); + else + { + if (add_raw_frames(Chan, TXMSG, &KISS.buffer[Chan])) + Add(&KISS.buffer[Chan], TXMSG); + } + + + return; + + case KISS_DATA: + + TXMSG = newString(); + stringAdd(TXMSG, &Msg[0], Len); // include Control + + CRC = get_fcs(&Msg[1], Len - 1); + + CRCString[0] = CRC & 0xff; + CRCString[1] = CRC >> 8; + + stringAdd(TXMSG, CRCString, 2); + + // if KISS Optimise see if frame is really needed + + if (!KISS_opt[Chan]) + Add(&KISS.buffer[Chan], TXMSG); + else + { + if (add_raw_frames(Chan, TXMSG, &KISS.buffer[Chan])) + Add(&KISS.buffer[Chan], TXMSG); + } + + + return; + } + + // Still need to process kiss control frames +} + + + + +void KISSDataReceived(void * socket, UCHAR * data, int length) +{ + int i; + UCHAR * ptr1, * ptr2; + int Length; + + TKISSMode * KISS = NULL; + + if (KISSConCount == 0) + return; + + for (i = 0; i < KISSConCount; i++) + { + if (KissConnections[i]->Socket == socket) + { + KISS = KissConnections[i]; + break; + } + } + + if (KISS == NULL) + return; + + stringAdd(KISS->data_in, data, length); + + if (KISS->data_in->Length > 10000) // Probably AGW Data on KISS Port + { + KISS->data_in->Length = 0; + return; + } + + ptr1 = KISS->data_in->Data; + Length = KISS->data_in->Length; + + + while ((ptr2 = memchr(ptr1, FEND, Length))) + { + int Len = (ptr2 - ptr1); + + if (Len == 0) + { + // Start of frame + + mydelete(KISS->data_in, 0, 1); + + ptr1 = KISS->data_in->Data; + Length = KISS->data_in->Length; + + continue; + } + + // Process Frame + + if (Len < 350) // Drop obviously corrupt frames + ProcessKISSFrame(socket, ptr1, Len); + + mydelete(KISS->data_in, 0, Len + 1); + + ptr1 = KISS->data_in->Data; + Length = KISS->data_in->Length; + + } + + /* if (length(KISS.data_in.Strings[idx]) > 65535) + if Form1.ServerSocket2.Socket.ActiveConnections > 0) + + for i:=0 to Form1.ServerSocket2.Socket.ActiveConnections-1 do + if Form1.ServerSocket2.Socket.Connections[i].SocketHandle=socket then + try Form1.ServerSocket2.Socket.Connections[i].Close; except end; + */ + + +} + + + +int KISS_encode(UCHAR * KISSBuffer, int port, string * frame, int TXMON) +{ + + // Encode frame + + UCHAR * ptr1 = frame->Data; + UCHAR TXCCC = 0; + int Len = frame->Length - 2; // frame includes CRC + UCHAR * ptr2 = &KISSBuffer[2]; + UCHAR c; + + if (TXMON) + { + // TX Frame has control byte on front + + ptr1++; + Len--; + } + + KISSBuffer[0] = FEND; + KISSBuffer[1] = port << 4; + + TXCCC ^= KISSBuffer[1]; + + while (Len--) + { + c = *(ptr1++); + TXCCC ^= c; + + switch (c) + { + case FEND: + (*ptr2++) = FESC; + (*ptr2++) = TFEND; + break; + + case FESC: + + (*ptr2++) = FESC; + (*ptr2++) = TFESC; + break; + + // Drop through + + default: + + (*ptr2++) = c; + } + } + + // If using checksum, send it +/* + + if (KISSFLAGS & CHECKSUM) + { + c = (UCHAR)KISS->TXCCC; + + // On TNC-X based boards, it is difficult to cope with an encoded CRC, so if + // CRC is FEND, send it as 0xc1. This means we have to accept 00 or 01 as valid. + // which is a slight loss in robustness + + if (c == FEND && (PORT->KISSFLAGS & TNCX)) + { + (*ptr2++) = FEND + 1; + } + else + { + switch (c) + { + case FEND: + (*ptr2++) = FESC; + (*ptr2++) = TFEND; + break; + + case FESC: + (*ptr2++) = FESC; + (*ptr2++) = TFESC; + break; + + default: + (*ptr2++) = c; + } + } + } + */ + + (*ptr2++) = FEND; + + return (int)(ptr2 - KISSBuffer); +} + + +void sendAckModeAcks(int snd_ch) +{ + // format and send any outstanding acks + + string * temp; + UCHAR * Msg; + void * socket; + + while (KISS_acked[snd_ch].Count) + { + UCHAR * ACK = (UCHAR *)malloc(15); + UCHAR * ackptr = ACK; + + temp = Strings(&KISS_acked[snd_ch], 0); // get first + Msg = temp->Data; + + *ackptr++ = FEND; + *ackptr++ = Msg[0]; // opcode and channel + + *ackptr++ = Msg[1]; + *ackptr++ = Msg[2]; // ACK Bytes + *ackptr++ = FEND; + + // Socket to reply to is on end + + Msg += (temp->Length - 4); + + memcpy(&socket, Msg, sizeof(void *)); + + KISSSendtoServer(socket, ACK, 5); + Delete(&KISS_acked[snd_ch], 0); // This will invalidate temp + } +} + + + + + + + diff --git a/libfftw3f-3.def b/libfftw3f-3.def new file mode 100644 index 0000000..b950a52 --- /dev/null +++ b/libfftw3f-3.def @@ -0,0 +1,1017 @@ +LIBRARY libfftw3f-3.dll +EXPORTS +fftwf_alignment_of +fftwf_alloc_complex +fftwf_alloc_real +fftwf_assertion_failed +fftwf_bufdist +fftwf_check_alignment_of_sse2_pm +fftwf_choose_radix +fftwf_cleanup +fftwf_cleanup_threads +fftwf_codelet_e01_8 +fftwf_codelet_e10_8 +fftwf_codelet_hb_10 +fftwf_codelet_hb_12 +fftwf_codelet_hb_15 +fftwf_codelet_hb_16 +fftwf_codelet_hb_2 +fftwf_codelet_hb_20 +fftwf_codelet_hb2_16 +fftwf_codelet_hb2_20 +fftwf_codelet_hb2_25 +fftwf_codelet_hb2_32 +fftwf_codelet_hb2_4 +fftwf_codelet_hb_25 +fftwf_codelet_hb2_5 +fftwf_codelet_hb2_8 +fftwf_codelet_hb_3 +fftwf_codelet_hb_32 +fftwf_codelet_hb_4 +fftwf_codelet_hb_5 +fftwf_codelet_hb_6 +fftwf_codelet_hb_64 +fftwf_codelet_hb_7 +fftwf_codelet_hb_8 +fftwf_codelet_hb_9 +fftwf_codelet_hc2cb_10 +fftwf_codelet_hc2cb_12 +fftwf_codelet_hc2cb_16 +fftwf_codelet_hc2cb_2 +fftwf_codelet_hc2cb_20 +fftwf_codelet_hc2cb2_16 +fftwf_codelet_hc2cb2_20 +fftwf_codelet_hc2cb2_32 +fftwf_codelet_hc2cb2_4 +fftwf_codelet_hc2cb2_8 +fftwf_codelet_hc2cb_32 +fftwf_codelet_hc2cb_4 +fftwf_codelet_hc2cb_6 +fftwf_codelet_hc2cb_8 +fftwf_codelet_hc2cbdft_10 +fftwf_codelet_hc2cbdft_12 +fftwf_codelet_hc2cbdft_16 +fftwf_codelet_hc2cbdft_2 +fftwf_codelet_hc2cbdft_20 +fftwf_codelet_hc2cbdft2_16 +fftwf_codelet_hc2cbdft2_20 +fftwf_codelet_hc2cbdft2_32 +fftwf_codelet_hc2cbdft2_4 +fftwf_codelet_hc2cbdft2_8 +fftwf_codelet_hc2cbdft_32 +fftwf_codelet_hc2cbdft_4 +fftwf_codelet_hc2cbdft_6 +fftwf_codelet_hc2cbdft_8 +fftwf_codelet_hc2cbdftv_10_avx +fftwf_codelet_hc2cbdftv_10_sse2 +fftwf_codelet_hc2cbdftv_12_avx +fftwf_codelet_hc2cbdftv_12_sse2 +fftwf_codelet_hc2cbdftv_16_avx +fftwf_codelet_hc2cbdftv_16_sse2 +fftwf_codelet_hc2cbdftv_20_avx +fftwf_codelet_hc2cbdftv_20_sse2 +fftwf_codelet_hc2cbdftv_2_avx +fftwf_codelet_hc2cbdftv_2_sse2 +fftwf_codelet_hc2cbdftv_32_avx +fftwf_codelet_hc2cbdftv_32_sse2 +fftwf_codelet_hc2cbdftv_4_avx +fftwf_codelet_hc2cbdftv_4_sse2 +fftwf_codelet_hc2cbdftv_6_avx +fftwf_codelet_hc2cbdftv_6_sse2 +fftwf_codelet_hc2cbdftv_8_avx +fftwf_codelet_hc2cbdftv_8_sse2 +fftwf_codelet_hc2cf_10 +fftwf_codelet_hc2cf_12 +fftwf_codelet_hc2cf_16 +fftwf_codelet_hc2cf_2 +fftwf_codelet_hc2cf_20 +fftwf_codelet_hc2cf2_16 +fftwf_codelet_hc2cf2_20 +fftwf_codelet_hc2cf2_32 +fftwf_codelet_hc2cf2_4 +fftwf_codelet_hc2cf2_8 +fftwf_codelet_hc2cf_32 +fftwf_codelet_hc2cf_4 +fftwf_codelet_hc2cf_6 +fftwf_codelet_hc2cf_8 +fftwf_codelet_hc2cfdft_10 +fftwf_codelet_hc2cfdft_12 +fftwf_codelet_hc2cfdft_16 +fftwf_codelet_hc2cfdft_2 +fftwf_codelet_hc2cfdft_20 +fftwf_codelet_hc2cfdft2_16 +fftwf_codelet_hc2cfdft2_20 +fftwf_codelet_hc2cfdft2_32 +fftwf_codelet_hc2cfdft2_4 +fftwf_codelet_hc2cfdft2_8 +fftwf_codelet_hc2cfdft_32 +fftwf_codelet_hc2cfdft_4 +fftwf_codelet_hc2cfdft_6 +fftwf_codelet_hc2cfdft_8 +fftwf_codelet_hc2cfdftv_10_avx +fftwf_codelet_hc2cfdftv_10_sse2 +fftwf_codelet_hc2cfdftv_12_avx +fftwf_codelet_hc2cfdftv_12_sse2 +fftwf_codelet_hc2cfdftv_16_avx +fftwf_codelet_hc2cfdftv_16_sse2 +fftwf_codelet_hc2cfdftv_20_avx +fftwf_codelet_hc2cfdftv_20_sse2 +fftwf_codelet_hc2cfdftv_2_avx +fftwf_codelet_hc2cfdftv_2_sse2 +fftwf_codelet_hc2cfdftv_32_avx +fftwf_codelet_hc2cfdftv_32_sse2 +fftwf_codelet_hc2cfdftv_4_avx +fftwf_codelet_hc2cfdftv_4_sse2 +fftwf_codelet_hc2cfdftv_6_avx +fftwf_codelet_hc2cfdftv_6_sse2 +fftwf_codelet_hc2cfdftv_8_avx +fftwf_codelet_hc2cfdftv_8_sse2 +fftwf_codelet_hf_10 +fftwf_codelet_hf_12 +fftwf_codelet_hf_15 +fftwf_codelet_hf_16 +fftwf_codelet_hf_2 +fftwf_codelet_hf_20 +fftwf_codelet_hf2_16 +fftwf_codelet_hf2_20 +fftwf_codelet_hf2_25 +fftwf_codelet_hf2_32 +fftwf_codelet_hf2_4 +fftwf_codelet_hf_25 +fftwf_codelet_hf2_5 +fftwf_codelet_hf2_8 +fftwf_codelet_hf_3 +fftwf_codelet_hf_32 +fftwf_codelet_hf_4 +fftwf_codelet_hf_5 +fftwf_codelet_hf_6 +fftwf_codelet_hf_64 +fftwf_codelet_hf_7 +fftwf_codelet_hf_8 +fftwf_codelet_hf_9 +fftwf_codelet_n1_10 +fftwf_codelet_n1_11 +fftwf_codelet_n1_12 +fftwf_codelet_n1_13 +fftwf_codelet_n1_14 +fftwf_codelet_n1_15 +fftwf_codelet_n1_16 +fftwf_codelet_n1_2 +fftwf_codelet_n1_20 +fftwf_codelet_n1_25 +fftwf_codelet_n1_3 +fftwf_codelet_n1_32 +fftwf_codelet_n1_4 +fftwf_codelet_n1_5 +fftwf_codelet_n1_6 +fftwf_codelet_n1_64 +fftwf_codelet_n1_7 +fftwf_codelet_n1_8 +fftwf_codelet_n1_9 +fftwf_codelet_n1bv_10_avx +fftwf_codelet_n1bv_10_sse2 +fftwf_codelet_n1bv_11_avx +fftwf_codelet_n1bv_11_sse2 +fftwf_codelet_n1bv_128_avx +fftwf_codelet_n1bv_128_sse2 +fftwf_codelet_n1bv_12_avx +fftwf_codelet_n1bv_12_sse2 +fftwf_codelet_n1bv_13_avx +fftwf_codelet_n1bv_13_sse2 +fftwf_codelet_n1bv_14_avx +fftwf_codelet_n1bv_14_sse2 +fftwf_codelet_n1bv_15_avx +fftwf_codelet_n1bv_15_sse2 +fftwf_codelet_n1bv_16_avx +fftwf_codelet_n1bv_16_sse2 +fftwf_codelet_n1bv_20_avx +fftwf_codelet_n1bv_20_sse2 +fftwf_codelet_n1bv_25_avx +fftwf_codelet_n1bv_25_sse2 +fftwf_codelet_n1bv_2_avx +fftwf_codelet_n1bv_2_sse2 +fftwf_codelet_n1bv_32_avx +fftwf_codelet_n1bv_32_sse2 +fftwf_codelet_n1bv_3_avx +fftwf_codelet_n1bv_3_sse2 +fftwf_codelet_n1bv_4_avx +fftwf_codelet_n1bv_4_sse2 +fftwf_codelet_n1bv_5_avx +fftwf_codelet_n1bv_5_sse2 +fftwf_codelet_n1bv_64_avx +fftwf_codelet_n1bv_64_sse2 +fftwf_codelet_n1bv_6_avx +fftwf_codelet_n1bv_6_sse2 +fftwf_codelet_n1bv_7_avx +fftwf_codelet_n1bv_7_sse2 +fftwf_codelet_n1bv_8_avx +fftwf_codelet_n1bv_8_sse2 +fftwf_codelet_n1bv_9_avx +fftwf_codelet_n1bv_9_sse2 +fftwf_codelet_n1fv_10_avx +fftwf_codelet_n1fv_10_sse2 +fftwf_codelet_n1fv_11_avx +fftwf_codelet_n1fv_11_sse2 +fftwf_codelet_n1fv_128_avx +fftwf_codelet_n1fv_128_sse2 +fftwf_codelet_n1fv_12_avx +fftwf_codelet_n1fv_12_sse2 +fftwf_codelet_n1fv_13_avx +fftwf_codelet_n1fv_13_sse2 +fftwf_codelet_n1fv_14_avx +fftwf_codelet_n1fv_14_sse2 +fftwf_codelet_n1fv_15_avx +fftwf_codelet_n1fv_15_sse2 +fftwf_codelet_n1fv_16_avx +fftwf_codelet_n1fv_16_sse2 +fftwf_codelet_n1fv_20_avx +fftwf_codelet_n1fv_20_sse2 +fftwf_codelet_n1fv_25_avx +fftwf_codelet_n1fv_25_sse2 +fftwf_codelet_n1fv_2_avx +fftwf_codelet_n1fv_2_sse2 +fftwf_codelet_n1fv_32_avx +fftwf_codelet_n1fv_32_sse2 +fftwf_codelet_n1fv_3_avx +fftwf_codelet_n1fv_3_sse2 +fftwf_codelet_n1fv_4_avx +fftwf_codelet_n1fv_4_sse2 +fftwf_codelet_n1fv_5_avx +fftwf_codelet_n1fv_5_sse2 +fftwf_codelet_n1fv_64_avx +fftwf_codelet_n1fv_64_sse2 +fftwf_codelet_n1fv_6_avx +fftwf_codelet_n1fv_6_sse2 +fftwf_codelet_n1fv_7_avx +fftwf_codelet_n1fv_7_sse2 +fftwf_codelet_n1fv_8_avx +fftwf_codelet_n1fv_8_sse2 +fftwf_codelet_n1fv_9_avx +fftwf_codelet_n1fv_9_sse2 +fftwf_codelet_n2bv_10_avx +fftwf_codelet_n2bv_10_sse2 +fftwf_codelet_n2bv_12_avx +fftwf_codelet_n2bv_12_sse2 +fftwf_codelet_n2bv_14_avx +fftwf_codelet_n2bv_14_sse2 +fftwf_codelet_n2bv_16_avx +fftwf_codelet_n2bv_16_sse2 +fftwf_codelet_n2bv_20_avx +fftwf_codelet_n2bv_20_sse2 +fftwf_codelet_n2bv_2_avx +fftwf_codelet_n2bv_2_sse2 +fftwf_codelet_n2bv_32_avx +fftwf_codelet_n2bv_32_sse2 +fftwf_codelet_n2bv_4_avx +fftwf_codelet_n2bv_4_sse2 +fftwf_codelet_n2bv_64_avx +fftwf_codelet_n2bv_64_sse2 +fftwf_codelet_n2bv_6_avx +fftwf_codelet_n2bv_6_sse2 +fftwf_codelet_n2bv_8_avx +fftwf_codelet_n2bv_8_sse2 +fftwf_codelet_n2fv_10_avx +fftwf_codelet_n2fv_10_sse2 +fftwf_codelet_n2fv_12_avx +fftwf_codelet_n2fv_12_sse2 +fftwf_codelet_n2fv_14_avx +fftwf_codelet_n2fv_14_sse2 +fftwf_codelet_n2fv_16_avx +fftwf_codelet_n2fv_16_sse2 +fftwf_codelet_n2fv_20_avx +fftwf_codelet_n2fv_20_sse2 +fftwf_codelet_n2fv_2_avx +fftwf_codelet_n2fv_2_sse2 +fftwf_codelet_n2fv_32_avx +fftwf_codelet_n2fv_32_sse2 +fftwf_codelet_n2fv_4_avx +fftwf_codelet_n2fv_4_sse2 +fftwf_codelet_n2fv_64_avx +fftwf_codelet_n2fv_64_sse2 +fftwf_codelet_n2fv_6_avx +fftwf_codelet_n2fv_6_sse2 +fftwf_codelet_n2fv_8_avx +fftwf_codelet_n2fv_8_sse2 +fftwf_codelet_n2sv_16_avx +fftwf_codelet_n2sv_16_sse2 +fftwf_codelet_n2sv_32_avx +fftwf_codelet_n2sv_32_sse2 +fftwf_codelet_n2sv_4_avx +fftwf_codelet_n2sv_4_sse2 +fftwf_codelet_n2sv_64_avx +fftwf_codelet_n2sv_64_sse2 +fftwf_codelet_n2sv_8_avx +fftwf_codelet_n2sv_8_sse2 +fftwf_codelet_q1_2 +fftwf_codelet_q1_3 +fftwf_codelet_q1_4 +fftwf_codelet_q1_5 +fftwf_codelet_q1_6 +fftwf_codelet_q1_8 +fftwf_codelet_q1bv_2_avx +fftwf_codelet_q1bv_2_sse2 +fftwf_codelet_q1bv_4_avx +fftwf_codelet_q1bv_4_sse2 +fftwf_codelet_q1bv_5_avx +fftwf_codelet_q1bv_5_sse2 +fftwf_codelet_q1bv_8_avx +fftwf_codelet_q1bv_8_sse2 +fftwf_codelet_q1fv_2_avx +fftwf_codelet_q1fv_2_sse2 +fftwf_codelet_q1fv_4_avx +fftwf_codelet_q1fv_4_sse2 +fftwf_codelet_q1fv_5_avx +fftwf_codelet_q1fv_5_sse2 +fftwf_codelet_q1fv_8_avx +fftwf_codelet_q1fv_8_sse2 +fftwf_codelet_r2cb_10 +fftwf_codelet_r2cb_11 +fftwf_codelet_r2cb_12 +fftwf_codelet_r2cb_128 +fftwf_codelet_r2cb_13 +fftwf_codelet_r2cb_14 +fftwf_codelet_r2cb_15 +fftwf_codelet_r2cb_16 +fftwf_codelet_r2cb_2 +fftwf_codelet_r2cb_20 +fftwf_codelet_r2cb_25 +fftwf_codelet_r2cb_3 +fftwf_codelet_r2cb_32 +fftwf_codelet_r2cb_4 +fftwf_codelet_r2cb_5 +fftwf_codelet_r2cb_6 +fftwf_codelet_r2cb_64 +fftwf_codelet_r2cb_7 +fftwf_codelet_r2cb_8 +fftwf_codelet_r2cb_9 +fftwf_codelet_r2cbIII_10 +fftwf_codelet_r2cbIII_12 +fftwf_codelet_r2cbIII_15 +fftwf_codelet_r2cbIII_16 +fftwf_codelet_r2cbIII_2 +fftwf_codelet_r2cbIII_20 +fftwf_codelet_r2cbIII_25 +fftwf_codelet_r2cbIII_3 +fftwf_codelet_r2cbIII_32 +fftwf_codelet_r2cbIII_4 +fftwf_codelet_r2cbIII_5 +fftwf_codelet_r2cbIII_6 +fftwf_codelet_r2cbIII_64 +fftwf_codelet_r2cbIII_7 +fftwf_codelet_r2cbIII_8 +fftwf_codelet_r2cbIII_9 +fftwf_codelet_r2cf_10 +fftwf_codelet_r2cf_11 +fftwf_codelet_r2cf_12 +fftwf_codelet_r2cf_128 +fftwf_codelet_r2cf_13 +fftwf_codelet_r2cf_14 +fftwf_codelet_r2cf_15 +fftwf_codelet_r2cf_16 +fftwf_codelet_r2cf_2 +fftwf_codelet_r2cf_20 +fftwf_codelet_r2cf_25 +fftwf_codelet_r2cf_3 +fftwf_codelet_r2cf_32 +fftwf_codelet_r2cf_4 +fftwf_codelet_r2cf_5 +fftwf_codelet_r2cf_6 +fftwf_codelet_r2cf_64 +fftwf_codelet_r2cf_7 +fftwf_codelet_r2cf_8 +fftwf_codelet_r2cf_9 +fftwf_codelet_r2cfII_10 +fftwf_codelet_r2cfII_12 +fftwf_codelet_r2cfII_15 +fftwf_codelet_r2cfII_16 +fftwf_codelet_r2cfII_2 +fftwf_codelet_r2cfII_20 +fftwf_codelet_r2cfII_25 +fftwf_codelet_r2cfII_3 +fftwf_codelet_r2cfII_32 +fftwf_codelet_r2cfII_4 +fftwf_codelet_r2cfII_5 +fftwf_codelet_r2cfII_6 +fftwf_codelet_r2cfII_64 +fftwf_codelet_r2cfII_7 +fftwf_codelet_r2cfII_8 +fftwf_codelet_r2cfII_9 +fftwf_codelet_t1_10 +fftwf_codelet_t1_12 +fftwf_codelet_t1_15 +fftwf_codelet_t1_16 +fftwf_codelet_t1_2 +fftwf_codelet_t1_20 +fftwf_codelet_t1_25 +fftwf_codelet_t1_3 +fftwf_codelet_t1_32 +fftwf_codelet_t1_4 +fftwf_codelet_t1_5 +fftwf_codelet_t1_6 +fftwf_codelet_t1_64 +fftwf_codelet_t1_7 +fftwf_codelet_t1_8 +fftwf_codelet_t1_9 +fftwf_codelet_t1buv_10_avx +fftwf_codelet_t1buv_10_sse2 +fftwf_codelet_t1buv_2_avx +fftwf_codelet_t1buv_2_sse2 +fftwf_codelet_t1buv_3_avx +fftwf_codelet_t1buv_3_sse2 +fftwf_codelet_t1buv_4_avx +fftwf_codelet_t1buv_4_sse2 +fftwf_codelet_t1buv_5_avx +fftwf_codelet_t1buv_5_sse2 +fftwf_codelet_t1buv_6_avx +fftwf_codelet_t1buv_6_sse2 +fftwf_codelet_t1buv_7_avx +fftwf_codelet_t1buv_7_sse2 +fftwf_codelet_t1buv_8_avx +fftwf_codelet_t1buv_8_sse2 +fftwf_codelet_t1buv_9_avx +fftwf_codelet_t1buv_9_sse2 +fftwf_codelet_t1bv_10_avx +fftwf_codelet_t1bv_10_sse2 +fftwf_codelet_t1bv_12_avx +fftwf_codelet_t1bv_12_sse2 +fftwf_codelet_t1bv_15_avx +fftwf_codelet_t1bv_15_sse2 +fftwf_codelet_t1bv_16_avx +fftwf_codelet_t1bv_16_sse2 +fftwf_codelet_t1bv_20_avx +fftwf_codelet_t1bv_20_sse2 +fftwf_codelet_t1bv_25_avx +fftwf_codelet_t1bv_25_sse2 +fftwf_codelet_t1bv_2_avx +fftwf_codelet_t1bv_2_sse2 +fftwf_codelet_t1bv_32_avx +fftwf_codelet_t1bv_32_sse2 +fftwf_codelet_t1bv_3_avx +fftwf_codelet_t1bv_3_sse2 +fftwf_codelet_t1bv_4_avx +fftwf_codelet_t1bv_4_sse2 +fftwf_codelet_t1bv_5_avx +fftwf_codelet_t1bv_5_sse2 +fftwf_codelet_t1bv_64_avx +fftwf_codelet_t1bv_64_sse2 +fftwf_codelet_t1bv_6_avx +fftwf_codelet_t1bv_6_sse2 +fftwf_codelet_t1bv_7_avx +fftwf_codelet_t1bv_7_sse2 +fftwf_codelet_t1bv_8_avx +fftwf_codelet_t1bv_8_sse2 +fftwf_codelet_t1bv_9_avx +fftwf_codelet_t1bv_9_sse2 +fftwf_codelet_t1fuv_10_avx +fftwf_codelet_t1fuv_10_sse2 +fftwf_codelet_t1fuv_2_avx +fftwf_codelet_t1fuv_2_sse2 +fftwf_codelet_t1fuv_3_avx +fftwf_codelet_t1fuv_3_sse2 +fftwf_codelet_t1fuv_4_avx +fftwf_codelet_t1fuv_4_sse2 +fftwf_codelet_t1fuv_5_avx +fftwf_codelet_t1fuv_5_sse2 +fftwf_codelet_t1fuv_6_avx +fftwf_codelet_t1fuv_6_sse2 +fftwf_codelet_t1fuv_7_avx +fftwf_codelet_t1fuv_7_sse2 +fftwf_codelet_t1fuv_8_avx +fftwf_codelet_t1fuv_8_sse2 +fftwf_codelet_t1fuv_9_avx +fftwf_codelet_t1fuv_9_sse2 +fftwf_codelet_t1fv_10_avx +fftwf_codelet_t1fv_10_sse2 +fftwf_codelet_t1fv_12_avx +fftwf_codelet_t1fv_12_sse2 +fftwf_codelet_t1fv_15_avx +fftwf_codelet_t1fv_15_sse2 +fftwf_codelet_t1fv_16_avx +fftwf_codelet_t1fv_16_sse2 +fftwf_codelet_t1fv_20_avx +fftwf_codelet_t1fv_20_sse2 +fftwf_codelet_t1fv_25_avx +fftwf_codelet_t1fv_25_sse2 +fftwf_codelet_t1fv_2_avx +fftwf_codelet_t1fv_2_sse2 +fftwf_codelet_t1fv_32_avx +fftwf_codelet_t1fv_32_sse2 +fftwf_codelet_t1fv_3_avx +fftwf_codelet_t1fv_3_sse2 +fftwf_codelet_t1fv_4_avx +fftwf_codelet_t1fv_4_sse2 +fftwf_codelet_t1fv_5_avx +fftwf_codelet_t1fv_5_sse2 +fftwf_codelet_t1fv_64_avx +fftwf_codelet_t1fv_64_sse2 +fftwf_codelet_t1fv_6_avx +fftwf_codelet_t1fv_6_sse2 +fftwf_codelet_t1fv_7_avx +fftwf_codelet_t1fv_7_sse2 +fftwf_codelet_t1fv_8_avx +fftwf_codelet_t1fv_8_sse2 +fftwf_codelet_t1fv_9_avx +fftwf_codelet_t1fv_9_sse2 +fftwf_codelet_t1sv_16_avx +fftwf_codelet_t1sv_16_sse2 +fftwf_codelet_t1sv_2_avx +fftwf_codelet_t1sv_2_sse2 +fftwf_codelet_t1sv_32_avx +fftwf_codelet_t1sv_32_sse2 +fftwf_codelet_t1sv_4_avx +fftwf_codelet_t1sv_4_sse2 +fftwf_codelet_t1sv_8_avx +fftwf_codelet_t1sv_8_sse2 +fftwf_codelet_t2_10 +fftwf_codelet_t2_16 +fftwf_codelet_t2_20 +fftwf_codelet_t2_25 +fftwf_codelet_t2_32 +fftwf_codelet_t2_4 +fftwf_codelet_t2_5 +fftwf_codelet_t2_64 +fftwf_codelet_t2_8 +fftwf_codelet_t2bv_10_avx +fftwf_codelet_t2bv_10_sse2 +fftwf_codelet_t2bv_16_avx +fftwf_codelet_t2bv_16_sse2 +fftwf_codelet_t2bv_20_avx +fftwf_codelet_t2bv_20_sse2 +fftwf_codelet_t2bv_25_avx +fftwf_codelet_t2bv_25_sse2 +fftwf_codelet_t2bv_2_avx +fftwf_codelet_t2bv_2_sse2 +fftwf_codelet_t2bv_32_avx +fftwf_codelet_t2bv_32_sse2 +fftwf_codelet_t2bv_4_avx +fftwf_codelet_t2bv_4_sse2 +fftwf_codelet_t2bv_5_avx +fftwf_codelet_t2bv_5_sse2 +fftwf_codelet_t2bv_64_avx +fftwf_codelet_t2bv_64_sse2 +fftwf_codelet_t2bv_8_avx +fftwf_codelet_t2bv_8_sse2 +fftwf_codelet_t2fv_10_avx +fftwf_codelet_t2fv_10_sse2 +fftwf_codelet_t2fv_16_avx +fftwf_codelet_t2fv_16_sse2 +fftwf_codelet_t2fv_20_avx +fftwf_codelet_t2fv_20_sse2 +fftwf_codelet_t2fv_25_avx +fftwf_codelet_t2fv_25_sse2 +fftwf_codelet_t2fv_2_avx +fftwf_codelet_t2fv_2_sse2 +fftwf_codelet_t2fv_32_avx +fftwf_codelet_t2fv_32_sse2 +fftwf_codelet_t2fv_4_avx +fftwf_codelet_t2fv_4_sse2 +fftwf_codelet_t2fv_5_avx +fftwf_codelet_t2fv_5_sse2 +fftwf_codelet_t2fv_64_avx +fftwf_codelet_t2fv_64_sse2 +fftwf_codelet_t2fv_8_avx +fftwf_codelet_t2fv_8_sse2 +fftwf_codelet_t2sv_16_avx +fftwf_codelet_t2sv_16_sse2 +fftwf_codelet_t2sv_32_avx +fftwf_codelet_t2sv_32_sse2 +fftwf_codelet_t2sv_4_avx +fftwf_codelet_t2sv_4_sse2 +fftwf_codelet_t2sv_8_avx +fftwf_codelet_t2sv_8_sse2 +fftwf_codelet_t3bv_10_avx +fftwf_codelet_t3bv_10_sse2 +fftwf_codelet_t3bv_16_avx +fftwf_codelet_t3bv_16_sse2 +fftwf_codelet_t3bv_20_avx +fftwf_codelet_t3bv_20_sse2 +fftwf_codelet_t3bv_25_avx +fftwf_codelet_t3bv_25_sse2 +fftwf_codelet_t3bv_32_avx +fftwf_codelet_t3bv_32_sse2 +fftwf_codelet_t3bv_4_avx +fftwf_codelet_t3bv_4_sse2 +fftwf_codelet_t3bv_5_avx +fftwf_codelet_t3bv_5_sse2 +fftwf_codelet_t3bv_8_avx +fftwf_codelet_t3bv_8_sse2 +fftwf_codelet_t3fv_10_avx +fftwf_codelet_t3fv_10_sse2 +fftwf_codelet_t3fv_16_avx +fftwf_codelet_t3fv_16_sse2 +fftwf_codelet_t3fv_20_avx +fftwf_codelet_t3fv_20_sse2 +fftwf_codelet_t3fv_25_avx +fftwf_codelet_t3fv_25_sse2 +fftwf_codelet_t3fv_32_avx +fftwf_codelet_t3fv_32_sse2 +fftwf_codelet_t3fv_4_avx +fftwf_codelet_t3fv_4_sse2 +fftwf_codelet_t3fv_5_avx +fftwf_codelet_t3fv_5_sse2 +fftwf_codelet_t3fv_8_avx +fftwf_codelet_t3fv_8_sse2 +fftwf_compute_tilesz +fftwf_configure_planner +fftwf_cost +fftwf_cpy1d +fftwf_cpy2d +fftwf_cpy2d_ci +fftwf_cpy2d_co +fftwf_cpy2d_pair +fftwf_cpy2d_pair_ci +fftwf_cpy2d_pair_co +fftwf_cpy2d_tiled +fftwf_cpy2d_tiledbuf +fftwf_ct_applicable +fftwf_ct_genericbuf_register +fftwf_ct_generic_register +fftwf_ct_uglyp +fftwf_destroy_plan +fftwf_dft_bluestein_register +fftwf_dft_buffered_register +fftwf_dft_conf_standard +fftwf_dft_generic_register +fftwf_dft_indirect_register +fftwf_dft_indirect_transpose_register +fftwf_dft_nop_register +fftwf_dft_r2hc_register +fftwf_dft_rader_register +fftwf_dft_rank_geq2_register +fftwf_dft_solve +fftwf_dft_thr_vrank_geq1_register +fftwf_dft_vrank_geq1_register +fftwf_dft_zerotens +fftwf_dht_r2hc_register +fftwf_dht_rader_register +fftwf_dimcmp +fftwf_elapsed_since +fftwf_estimate_cost +fftwf_execute +fftwf_execute_dft +fftwf_execute_dft_c2r +fftwf_execute_dft_r2c +fftwf_execute_r2r +fftwf_execute_split_dft +fftwf_execute_split_dft_c2r +fftwf_execute_split_dft_r2c +fftwf_export_wisdom +fftwf_export_wisdom_to_file +fftwf_export_wisdom_to_filename +fftwf_export_wisdom_to_string +fftwf_extract_reim +fftwf_factors_into +fftwf_factors_into_small_primes +fftwf_find_generator +fftwf_first_divisor +fftwf_flops +fftwf_forget_wisdom +fftwf_fprint_plan +fftwf_free +fftwf_get_crude_time +fftwf_guru64_kosherp +fftwf_guru_kosherp +fftwf_hash +fftwf_have_simd_avx +fftwf_have_simd_sse2 +fftwf_hc2hc_applicable +fftwf_hc2hc_generic_register +fftwf_iabs +fftwf_ialignment_of +fftwf_iestimate_cost +fftwf_ifree +fftwf_ifree0 +fftwf_imax +fftwf_imin +fftwf_import_system_wisdom +fftwf_import_wisdom +fftwf_import_wisdom_from_file +fftwf_import_wisdom_from_filename +fftwf_import_wisdom_from_string +fftwf_init_threads +fftwf_is_prime +fftwf_isqrt +fftwf_ithreads_init +fftwf_join_taint +fftwf_kdft_dif_register +fftwf_kdft_difsq_register +fftwf_kdft_dit_register +fftwf_kdft_register +fftwf_kernel_free +fftwf_kernel_malloc +fftwf_khc2c_register +fftwf_khc2hc_register +fftwf_kr2c_register +fftwf_kr2r_register +fftwf_make_planner_thread_safe +fftwf_malloc +fftwf_malloc_plain +fftwf_many_kosherp +fftwf_mapflags +fftwf_map_r2r_kind +fftwf_md5begin +fftwf_md5end +fftwf_md5int +fftwf_md5INT +fftwf_md5putb +fftwf_md5putc +fftwf_md5puts +fftwf_md5unsigned +fftwf_measure_execution_time +fftwf_mkapiplan +fftwf_mkplan +fftwf_mkplan_d +fftwf_mkplan_dft +fftwf_mkplan_dftw +fftwf_mkplan_f_d +fftwf_mkplan_hc2c +fftwf_mkplan_hc2hc +fftwf_mkplanner +fftwf_mkplan_rdft +fftwf_mkplan_rdft2 +fftwf_mkprinter +fftwf_mkprinter_cnt +fftwf_mkprinter_file +fftwf_mkprinter_str +fftwf_mkproblem +fftwf_mkproblem_dft +fftwf_mkproblem_dft_d +fftwf_mkproblem_rdft +fftwf_mkproblem_rdft_0_d +fftwf_mkproblem_rdft_1 +fftwf_mkproblem_rdft_1_d +fftwf_mkproblem_rdft2 +fftwf_mkproblem_rdft2_d +fftwf_mkproblem_rdft2_d_3pointers +fftwf_mkproblem_rdft_d +fftwf_mkproblem_unsolvable +fftwf_mkscanner +fftwf_mksolver +fftwf_mksolver_ct +fftwf_mksolver_ct_threads +fftwf_mksolver_dft_direct +fftwf_mksolver_dft_directbuf +fftwf_mksolver_hc2c +fftwf_mksolver_hc2hc +fftwf_mksolver_hc2hc_threads +fftwf_mksolver_rdft2_direct +fftwf_mksolver_rdft_r2c_direct +fftwf_mksolver_rdft_r2c_directbuf +fftwf_mksolver_rdft_r2r_direct +fftwf_mkstride +fftwf_mktensor +fftwf_mktensor_0d +fftwf_mktensor_1d +fftwf_mktensor_2d +fftwf_mktensor_3d +fftwf_mktensor_4d +fftwf_mktensor_5d +fftwf_mktensor_iodims +fftwf_mktensor_iodims64 +fftwf_mktensor_rowmajor +fftwf_mktriggen +fftwf_modulo +fftwf_nbuf +fftwf_nbuf_redundant +fftwf_next_prime +fftwf_null_awake +fftwf_ops_add +fftwf_ops_add2 +fftwf_ops_cpy +fftwf_ops_madd +fftwf_ops_madd2 +fftwf_ops_other +fftwf_ops_zero +fftwf_pickdim +fftwf_plan_awake +fftwf_plan_destroy_internal +fftwf_plan_dft +fftwf_plan_dft_1d +fftwf_plan_dft_2d +fftwf_plan_dft_3d +fftwf_plan_dft_c2r +fftwf_plan_dft_c2r_1d +fftwf_plan_dft_c2r_2d +fftwf_plan_dft_c2r_3d +fftwf_plan_dft_r2c +fftwf_plan_dft_r2c_1d +fftwf_plan_dft_r2c_2d +fftwf_plan_dft_r2c_3d +fftwf_plan_guru64_dft +fftwf_plan_guru64_dft_c2r +fftwf_plan_guru64_dft_r2c +fftwf_plan_guru64_r2r +fftwf_plan_guru64_split_dft +fftwf_plan_guru64_split_dft_c2r +fftwf_plan_guru64_split_dft_r2c +fftwf_plan_guru_dft +fftwf_plan_guru_dft_c2r +fftwf_plan_guru_dft_r2c +fftwf_plan_guru_r2r +fftwf_plan_guru_split_dft +fftwf_plan_guru_split_dft_c2r +fftwf_plan_guru_split_dft_r2c +fftwf_plan_many_dft +fftwf_plan_many_dft_c2r +fftwf_plan_many_dft_r2c +fftwf_plan_many_r2r +fftwf_planner_destroy +fftwf_plan_null_destroy +fftwf_plan_r2r +fftwf_plan_r2r_1d +fftwf_plan_r2r_2d +fftwf_plan_r2r_3d +fftwf_plan_with_nthreads +fftwf_power_mod +fftwf_printer_destroy +fftwf_print_plan +fftwf_problem_destroy +fftwf_rader_tl_delete +fftwf_rader_tl_find +fftwf_rader_tl_insert +fftwf_rdft2_buffered_register +fftwf_rdft2_complex_n +fftwf_rdft2_inplace_strides +fftwf_rdft2_nop_register +fftwf_rdft2_pad +fftwf_rdft2_rank0_register +fftwf_rdft2_rank_geq2_register +fftwf_rdft2_rdft_register +fftwf_rdft2_solve +fftwf_rdft2_strides +fftwf_rdft2_tensor_max_index +fftwf_rdft2_thr_vrank_geq1_register +fftwf_rdft2_vrank_geq1_register +fftwf_rdft_buffered_register +fftwf_rdft_conf_standard +fftwf_rdft_dht_register +fftwf_rdft_generic_register +fftwf_rdft_indirect_register +fftwf_rdft_kind_str +fftwf_rdft_nop_register +fftwf_rdft_rank0_register +fftwf_rdft_rank_geq2_register +fftwf_rdft_solve +fftwf_rdft_thr_vrank_geq1_register +fftwf_rdft_vrank3_transpose_register +fftwf_rdft_vrank_geq1_register +fftwf_rdft_zerotens +fftwf_redft00e_r2hc_pad_register +fftwf_regsolver_ct_directw +fftwf_regsolver_ct_directwsq +fftwf_regsolver_hc2c_direct +fftwf_regsolver_hc2hc_direct +fftwf_reodft00e_splitradix_register +fftwf_reodft010e_r2hc_register +fftwf_reodft11e_r2hc_odd_register +fftwf_reodft11e_radix2_r2hc_register +fftwf_reodft_conf_standard +fftwf_rodft00e_r2hc_pad_register +fftwf_safe_mulmod +fftwf_scanner_destroy +fftwf_set_planner_hooks +fftwf_set_timelimit +fftwf_solver_destroy +fftwf_solver_register +fftwf_solver_use +fftwf_solvtab_exec +fftwf_spawn_loop +fftwf_sprint_plan +fftwf_stride_destroy +fftwf_taint +fftwf_tensor_append +fftwf_tensor_compress +fftwf_tensor_compress_contiguous +fftwf_tensor_copy +fftwf_tensor_copy_except +fftwf_tensor_copy_inplace +fftwf_tensor_copy_sub +fftwf_tensor_destroy +fftwf_tensor_destroy2 +fftwf_tensor_destroy4 +fftwf_tensor_equal +fftwf_tensor_inplace_locations +fftwf_tensor_inplace_strides +fftwf_tensor_inplace_strides2 +fftwf_tensor_kosherp +fftwf_tensor_max_index +fftwf_tensor_md5 +fftwf_tensor_min_istride +fftwf_tensor_min_ostride +fftwf_tensor_min_stride +fftwf_tensor_print +fftwf_tensor_split +fftwf_tensor_strides_decrease +fftwf_tensor_sz +fftwf_tensor_tornk1 +fftwf_the_planner +fftwf_threads_cleanup +fftwf_threads_conf_standard +fftwf_threads_register_planner_hooks +fftwf_tile2d +fftwf_toobig +fftwf_transpose +fftwf_transpose_tiled +fftwf_transpose_tiledbuf +fftwf_triggen_destroy +fftwf_twiddle_awake +fftwf_twiddle_length +fftwf_zero1d_pair +sfftw_cleanup_ +sfftw_cleanup__ +sfftw_cleanup_threads_ +sfftw_cleanup_threads__ +sfftw_cost_ +sfftw_cost__ +sfftw_destroy_plan_ +sfftw_destroy_plan__ +sfftw_estimate_cost_ +sfftw_estimate_cost__ +sfftw_execute_ +sfftw_execute__ +sfftw_execute_dft_ +sfftw_execute_dft__ +sfftw_execute_dft_c2r_ +sfftw_execute_dft_c2r__ +sfftw_execute_dft_r2c_ +sfftw_execute_dft_r2c__ +sfftw_execute_r2r_ +sfftw_execute_r2r__ +sfftw_execute_split_dft_ +sfftw_execute_split_dft__ +sfftw_execute_split_dft_c2r_ +sfftw_execute_split_dft_c2r__ +sfftw_execute_split_dft_r2c_ +sfftw_execute_split_dft_r2c__ +sfftw_export_wisdom_ +sfftw_export_wisdom__ +sfftw_flops_ +sfftw_flops__ +sfftw_forget_wisdom_ +sfftw_forget_wisdom__ +sfftw_import_system_wisdom_ +sfftw_import_system_wisdom__ +sfftw_import_wisdom_ +sfftw_import_wisdom__ +sfftw_init_threads_ +sfftw_init_threads__ +sfftw_plan_dft_ +sfftw_plan_dft__ +sfftw_plan_dft_1d_ +sfftw_plan_dft_1d__ +sfftw_plan_dft_2d_ +sfftw_plan_dft_2d__ +sfftw_plan_dft_3d_ +sfftw_plan_dft_3d__ +sfftw_plan_dft_c2r_ +sfftw_plan_dft_c2r__ +sfftw_plan_dft_c2r_1d_ +sfftw_plan_dft_c2r_1d__ +sfftw_plan_dft_c2r_2d_ +sfftw_plan_dft_c2r_2d__ +sfftw_plan_dft_c2r_3d_ +sfftw_plan_dft_c2r_3d__ +sfftw_plan_dft_r2c_ +sfftw_plan_dft_r2c__ +sfftw_plan_dft_r2c_1d_ +sfftw_plan_dft_r2c_1d__ +sfftw_plan_dft_r2c_2d_ +sfftw_plan_dft_r2c_2d__ +sfftw_plan_dft_r2c_3d_ +sfftw_plan_dft_r2c_3d__ +sfftw_plan_guru_dft_ +sfftw_plan_guru_dft__ +sfftw_plan_guru_dft_c2r_ +sfftw_plan_guru_dft_c2r__ +sfftw_plan_guru_dft_r2c_ +sfftw_plan_guru_dft_r2c__ +sfftw_plan_guru_r2r_ +sfftw_plan_guru_r2r__ +sfftw_plan_guru_split_dft_ +sfftw_plan_guru_split_dft__ +sfftw_plan_guru_split_dft_c2r_ +sfftw_plan_guru_split_dft_c2r__ +sfftw_plan_guru_split_dft_r2c_ +sfftw_plan_guru_split_dft_r2c__ +sfftw_plan_many_dft_ +sfftw_plan_many_dft__ +sfftw_plan_many_dft_c2r_ +sfftw_plan_many_dft_c2r__ +sfftw_plan_many_dft_r2c_ +sfftw_plan_many_dft_r2c__ +sfftw_plan_many_r2r_ +sfftw_plan_many_r2r__ +sfftw_plan_r2r_ +sfftw_plan_r2r__ +sfftw_plan_r2r_1d_ +sfftw_plan_r2r_1d__ +sfftw_plan_r2r_2d_ +sfftw_plan_r2r_2d__ +sfftw_plan_r2r_3d_ +sfftw_plan_r2r_3d__ +sfftw_plan_with_nthreads_ +sfftw_plan_with_nthreads__ +sfftw_print_plan_ +sfftw_print_plan__ +sfftw_set_timelimit_ +sfftw_set_timelimit__ diff --git a/libfftw3f-3.dll b/libfftw3f-3.dll new file mode 100644 index 0000000000000000000000000000000000000000..b0a053a8289504cb3aa33ff7ca377628226e16ce GIT binary patch literal 2391615 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 l$B!};iav;+|PQt#Y; z64rI@fC;LsIr_3H29~2KTXQf%dMI%_&4NoJh_iVMJw*Gf|C7qGxsXV-1x}A-1KMx;0}Wtp@}s zi%07aVoMw*8?Vji`eBTL$D(hgMRBnWA90NWR5LR`B*GEFgUPVW^z zVd08TS$&c0aq`2FOtg+xi*qWru_U91uIR82(*K(Wh@5R|%TE@5{C|Mu=R)W%N~c4x zcC|p|wTzR*i@y_wVXmCOCfgDlZPs$|q(bu9p0#J)NIaQSK!r(IjNK(L2Z{RDp69MW za8O)2IFwtINm^(0>f9KosUb3$S5L-91$FzZTjAr0nJEfwibBPJdY&^h;L{=wHq&FLcy-!z=h)@U zI9fOin{wgFLhglCO_h}n7&PmM#X;6C3DRSt4YVE&1|_*djo-m0M`U{)8#%Pe0dxjn z+IBj-D6xhm(-Q|hB&!5E4D&!Nw4iEOJhAqCVzHrlCmD%B_cakv0eM=h*~S7w_M#oo zIZbAVA9@)Lu?XE%k!a&+Br*Dh-(^qGDD27JCs`eA@D6sy#0B3TIyxKsnd5;%Twalj zF8N@0Bo>4=^gm_u>iYiE`OYXf4dIkI7Q4`}Y#2U8ofG~_#Ui&kLfa_b!A%|=q5H!i zQ7pWQM|Y4sO7e;T9!8Z1i0k6r5ZDeW;L^oE>@?_Uv=NA5gV*bjjm%+5PV#CdCmuoBehZZ^xF8gwQ*UJ5XL0N=?K?hppWt1C@ZhDyBF^q%q3mp;L4zsVdbTI zd?`zw&Yu;2NW8!g`jU_7b$amGC(DZP(0Ah&<>M5I)#=|nTJukB!oWK3`t&ooKK<`c zJ#>9Kj1!Eeru|88;!^W~pUP9|>40T6W*QyjskTHN%36AFLCDHmm)`gj0cp%E*nuAc zn(;vTzNfd`sMo;g$COg6(U8Z6#>qtovTSTz;dpa~B=L&hh}@|F;&P$&bh84g^M@l9 zx#ne7!m;yRkR-TLU;tOV05K3C!~^rfVQsvIgEn(*P zaiBgcmx4*_>7IgdX?k1iJoh<$#V6VN`_&zX?3G+L&rdSeL3iRleJ|mlsF6W&ZfnBG| z*KJy1(wk&oCopDD#k#2AL*o+r?;4O1oysH9WB-Dhl5v@z#&;UuU)SnX(9^Y4w{i8~ z`o>*G#gIFwko{Edk74NtDm&>ljko@OKkX-9fZ(pB(SJ=nq+n_4uXvFCbj*qL-RaXs zw8nBpZ+vqOLd+^IF*y$wG@UI&#Dc(10X~?L%}|*^>$3-FVW#%-(28rgIg+wu)A(Im zFZ6fmv1f6f0NWgMg4D;d5aEf9@6+HM?W?Bg%Ok+;6I?czTCDX#AO6z<8FLt=Ard9Z zTJ6|+!D|V6UBSGWtHuH0RXWfZMK0f5+tTyHETS%lfjMXazf|idgDw?z!0wvIT6XYt z=tl92&~Jpo5W@7*DGcUe5?Ka~3g~#+FIZ`xU_6qyTrD-eN7#`!>FH?LZpjAu``HJ( z;hJOxw9Ym@S{ez0lz!#aRkq!D5BTE=e}_MIM9+`MLg)1lwI8z_&5s4_#g8`chp2DC zYOp@Ub!gL)DR^r;HNJbzn+GPX@(8iiy(O>oywIXr4t69#+hLIlqUp&Dk3s^ySstO) zgGj7qFC^YaM>_-s-CbrqfLeu8yUmtT1loqDV`Nga_>=dx z?T?j$p{565Mnt8IBnfi?nMGA|X|x0K-A5X~3!wQbkCh3tvaLCWMG-Z;C!&|wS(ywP z9JX}$E%xzL%Ae`_E%uwQM7jE5>D(Er1DWBWmj%(wpjZum@0cs&xnQABz)8XB-z!oW zY;?q|71Xj>WfYE*hEhJp4|H~|CGV%l#D!IuhL!#wW^P7v)Avp%|P9ZkvCm@wGB~2hR>mN@SOg-w~^{GD5bAv9>hj zq3u0dqA2TI_7}cZ7x2a(w%-BrCONzkiMv+J!(u113rRj#P3!QzXTe7urpD`XYM#bv z0xJj@GPzEgQ;d5oklm_@aWAP5rt-JqO)V1IM@CT%>!->hnPn`*7~b6`81{qWt^l+* z_tMm~Qop&*<<&@460FwTs9N(fv!2*QtB@HGfVZi8pNE%NV&)st7h_aDd4_`_GDsef zLdSB6$7(;z;~v4DA0d0xY#`*OD~aO_)+xwLUY~h7K2#}W?I7z_ha<|X&|DNyaJU0R zRwr?a#O+mWN077`s}TgsT8rF~tin^jPcFh?Bl%67-IYm+u`)_nLB)i zIb2R}lhawCl4CTedZ&&QtHSsgSU(8uAg?1$4v-%Vmi|NajAzk>eYKW zYO9i}>o#C;6?XW%t^N;LRgBY-Tw_8GTX>Fb96Wv5axURZ9F|zQynm^l7bAH; z$!h4V8@DSq5%DJiNpGaXw+o! zhTcG?yOyq#X*y5`ok5zMt2e2plsF?~?5t$8k|lXtwH_Yl#m5$B0YA$0K2$Bu^@Md(<$fCVDf(r$jxX?it__Lo-b|GV`j z%XA<*yU4p)&wCI$>D`H&o_Qkr*a#S3U3H zNX!NjbY3F|ZlB>DV@`wXU^==Q5$MnQgAnyfk#c&aj$sbtgrF)`*VRcB9mvdqjP_PX zH!@ul?_jXcH^~a?N6XOf)Fz(i==!;GYnogzNbPHHHgYY)kA@M+`sCumWMYhB!U-+k zwHY|-)j{WRs_6saM|TXh>#n>*|MhJ{;D*V`%dq|>9J%g3tm0qX{a=>&mlF^0FMjz; z2XFU#Qi-j=VRmVUTF;FW!qQ_{%3^cqdw?@pgYr z0DT3(5RQi7EZ?a-zDU|S0DYkQ@%8dOIcg8HMB7f&qpe-ztxr<4zqQLI`&zpu`RV=Y zz8x!1{nMZRmFBkp>!)Ac#GmsFMi0M>{X<)yJ!@3V$6r6VI{Z^-m;T-+;BBKTgRN&K zK>ya8x9@%2dS+bY53X+esrUXh?@K|brPKd~^veEQVV*u>!CRV&Z&Q%hA>l-2y8Yh2 zCGmsDj@JMC@gTqqUDy|UXt+sQ2UM4fuYL8Mk3ZhJ>GxM3nSadegRh<-x$*NoTrzoZ zM`Qkz2Ull4zY;^&`a;jaCmTQioz@rn4_%!9;e#JFUwo(aGks6~Oz-@b1Ml{|`0u#g z6fBypXT3t~)~k>G;NwFpKlg82Uud>|w)y1UfA}w2&-Ao59a_nTem~mpd+~pR@=;}K zXuH+EO0~t;7w`V!_x^D4{1WCdAba_px@b&2QqjuIjm_w!Zy4n5aw zZ9Dh}jrqqu|C=Z^2SEMN`f|^~|EBTtAGE&Qf9O}|pE&sa=8M18`eNUcU+hJ=riO-p z|EvE8K>J?2REcD$#gLgl^T`m)S6W|g{>anKfB3TDy$aw70!In zJotN!gWqgqyYqi;Q_)A={h{WlDh`%g`;$Srp~Y!gb9iyO_aL_@ulpWni~-FITK*S* z8^(%)XzPKqb!~O@XzHsGROC1sZwj3?-TKlbN^hzEe<{pA(fWeAbE*G-R05mAbANqlEJ7F$-fC?9ahS+gO{}f|H4GGOyg``r7#N_Lfcfl?KAY{CMLdX45Ac-~SOdk6wJ^ z;Oa=uYp3GFqc!^M48?-Oe_Szb{ncr?Vmzs3A3p920)?8-{*KYtosM|l!S6S=E}RFc zL%+K9kn1Ey>8m@qO!?=xF8r%_-PZbIf9uPAt-sOxxeK5B4Y&w}wCT+EA31Z^OM;s) zbI+}R`6(HbM`pVoggH3;mp;ol4f8m1=-XR=LG$?FO3&wi^Yb5rUa;Z^z3l&;Z5&+b zee#3(ztrkG^s7I6r!{%+d%uy1i_;$-d=E-~{c}#k(|5o38(V+*(&F^p&wcI54_^E_ zqsx%QJ`2twK=y&wrp;gOX9!0?tatNsy@X*0e)LpEIPm3;IPLO%{1LFW$XS35oATCw z4$J=L)&;MQ24v4G&!>p(OkK8%n{W1M=)r%{=#ql;Ny?!+pe5#nHv8AOhi=xyhot%$Dd#M{I7P0{=F}tp%(xB=YETJ zKlsOsZ+`Av0sX=MUIe-DHz1p>aSfK{dwn+%>P-F*F=+Z#&TBz)AeD52L zwWa=l^lH!*25u~VZ|#xKoewwVAK7j61#+45<2dZhIy8b_7B$>eqNEywwb=wK#cX;7gdhC%&ktH++8vYM1 zJ3%?Xxm!X?c-qm4-=6Q%!3^L#R91_al^jL0I%CcTXD7y3)ZI4x+)1HrJ6{`rcj2{A z|G8vtE@ls#;mTP`p+_)m0{JrTTw5qPg=d`c`5cf_(5BvD!hiY4`hXmvx}I<|@5AHO z1qM1A`5dQZUw*V0H{k_rz+uNs4f!?$<`l{+eP9#g&zZC*EDYFTmYV*y}_$Zk-} zBBoJjRll zw$QmF6LFd_>J)xYD=eF|Jf1t%v>{zFHrF)Z8X8d3|FCHQnAq$y@wspcpO(k-BYJW3z<}##LcDCNC+n6 z=Kcz+ue8CcCzvN6*Eke(m$A>68hxLCrf?N9{V7@DjGwb!Jq6`p<}~EC-zWtJy}VDUBUPq+-#CaiGIM4*iBu0Crm!gE@Rm2l;rR%9fjXz^D&qm{ z!S??e4(|~TW;(ou#=RL1*HV+d&7uPb#HJFh{zZJFpISwQ8JK8BrBUf0TKvYCh=l~R zOpPxbPSLsRbQkpyV_!v1hf@O zbKLE9@}mfmhR`Fs&yVRd3|jx#W`6NMr_iRtuhkvgeSQu3jCJ7d!$3YCooYnfNU`hP zlM|6?du${G)tv3p)bQc&8JY}&(*BW9{;ARjWVcR=a_L`APoUj#`WT1BT0Lp<=Xo{` zmHrnhtgL_gSaBbqUFbrYPIaqVScoKBXZyOFCbO^72YHM zFPQPrybrkxGleaE#xhmwmCak2QI*eZC|-4F%iWu~T0ZIa#%K?=LQrE#71`hR@H`ZX`8=wP{rwV#LAECUYuGC8Ez{*4fP@ zVi&2qbo~)kj91@|P-hODir(-G5p&F_ze)FgL0KsYn=I3r20Z$guTHk)67~HFKGdsq zlu_J40|Gz(eKkbv8c+C3E;^wC!=!#?Z2{#S>VKayF{M0+2v*t9-E{}oGd=IRTJ}N9 z-T6PDXd3vEp##l$q#vz*mnj%C#ForZR|Iq5S1XVy`%2eO`D0)*Xcv}7_BXr;HAhN& zH5J+)*dt|oPl)?=cuLUuPdW5UQiHn!{2lS?e7r$NX8XC#W(2DdD31RwvdKWSjt@Iu zXgA)Dls7-)eX1RJHQxLbk7}+g=TXIVx;Gy<1jFH=RPV&Nik!}pGf@>Dw7nadJfUE0 z?APln?c&HH20A>VA-#-%)_d75uQvrZR(qK&uxq?*H*aw-)8QpMNl3W&$~l%x=*D%I zu3Y{z=%ZV&sxX)0EB^+GQiIiRCZO&pUKIm{g7#-3@DEmtxIq`$?vcMeT**43MjnuD zY865y!4~{i>NdB~)Ky#Dy&CF&MXl6X??J9E$?uj_m+35#WKTli8y9Z(B3t71nsS+y;UnaX>V<42(FCNKthcS z5DZDsiJJi^HmpxfF4H)vF>REwJl#f)S>;QdcGiY=xWjcjTkuq^8d58JsOvvzrST7z z`7sa=#Rn*6=O0&_pK7X|e_YD@h3l<9@%8HmH}O@N1%(IgYeHZR04*mhC(+V#rDs?0 z8P{5x-oHjpMklwRak6ng9GEb}01^DXpH7SA7w)G9TJ{W2T9$C(pvU-K*s?Nkgc$x{ zW#A%Bhgbiwu&N@I|86md0CGgNNO2{%Ddia68ld3n-%yDzPK#swTcH<{GaevjF-k@U z|5n1n?oa|TEs;qJM^Yzp@=G#I+a%f4MphmSpoYumCA=XcZU7?@(|3q5}JE9T#f=sZD!Iv3|aBEgK2`&vLM-%X=MoSn*Y4Y3gn-&-~ti9nP zsrpE039z1mBj!2pIn$_w7Bse~1^9%c8(*@V8xv*cfvQ1cn?b_ZXhz(2KyA~6I3$u# zRnONO1V_~yqJLn~UMpn85|+ya_D8>`;%%xnfzcuJV)Ks;AEywb?KdBI6*9ZS(TxY; z0r}@L#nw&o7ji*dN2R*u$%vWRVsp7ju@E>ku?7ok(Ad-7+SH`1A)x?rXQXE zI=rfd7cQRYz-C%$diRcsPks4N&M=3bu7GM2x033j@FE&yB*>v>oPiuSgZP zh22kQ3L|0n6G?ZGoD(T`QI-b`u+?4E#jkev;tuoD%}c$zs1u$whJm{sud(RwLZ$QX zVYzZq*ytj9Fv#OjBn63-nZ=S1?an(EnA@x?kY%vOn$)?1Ne~wI!K!TOl$%5Tq+VM(xR@pdN z8sR12Ja5gdK;FM479IEosozG*R73|h9hwqNgWutIX}w7ZTk4sPI0`#sa3~-4ma;rl zkw4K$ufcZZ3naIJJn>y#*9&>he3&6s7B~i$m587H5sbbx%oo-_<=-tRXOj7?Kh4h28HJ#tP z!1t%$lh^Y;HvL|^p7-s%`~Uuxxh!twPx8;9&(IXH0K(9<-@m~B7CCI4Ww<}tc!9s3 z)QR8BUXLm~TThQRc-ahnT;*j`JmOw9$s^`vWk*E3Y&#DzUNcXJXOkPprs*hbsUI=R z?+RYfxYx4!TjegOVKj^weQr7|wE7vON`v=_u-DkK+{y3q=GV6@jOQN!PElSXJGQFC zSG;QkIsVnqjdtB$R$80rR>&Pr{*4s`8>)>U=qA}ep^+}f86@KkPeH_O>+l%1V(dkR ze;#}V61TN`iy|`;hfHF&*d_tSa!QIvxJdkk84%&(`C*NTSD>1}x^11Zg1u4uD*c6( zSIinw=B;2QDtYsP(^TTR?J!1LlVO5=rRPwS82oY8y-qbccqU(eC?vj)4t{_yrOo6l z^G7fv`y41*2e37I&f|Sn{Die%>gkjuNT$4Vl3s(ipi}(c>v+vxvR)W%Yevxa8NzDd zn&=J*X0h)Y=t!Q)hv-ft#Ad+{{Mol#V#_Du-=$e1rZEelJhUu2<;`Bd9{gn;CJ|Q| zzNWm+UdWX2I(vB3c%5B5s%>CNl?^i?+C;uqqD}gw7>d<5^A_>z{%$@J$tMUk+7k)cHKC4HLl9nl)oSDXc;B8z$0HF;6_8U*tR-Kn`fD72AUEKn$v`no5KItoe*wu%Dbx9o9}p^ zbj)Jm@WGSlo6WA@c(BfI*&MxIdIwy)|LEim@J^{i4Bl@@;ZD9e{z;%-UPuj=Ax+Df zRh5Z@+*DpeUnXY!oJn8QZqd1xMA7$17MOxOGv2C9f5jbiFkfC0BjV{@+BkY%81Wq) z_(upu$zTezUSF2zOa2*;HLiJ~)lXU({-`&<)~je)5YO*KEF!h*#hXHf-?cm?$rC&s ze-qv#B>A_gu*zEyYgrJ94t`nH&`xT+UR|@mN<|bkUHVQ$zcQoWE-hUQ*G#XUu_J(k|_Ns$|O2?hJG%z$V3N|yt$G87@KyD%rg#*p(r_9ChYXPsPC$9qI#EXG4%n+B>b@PGGo6fuv7rmE` zX)+tyAKiFa7gDPV7hct_g*Cg+twHIvH#r?28N@TnUFh$Afzs9=ai95_QR=Aq*{+{=nx9$y{J!}qv2AttH+T=$OIsP% zcKj_`;eEu*#L(9)lrP|rLN8hCUEJlZ?(`}IB!XpwKGeXM=I%hq#vrxP+)qtwZX1lD z_$jS>*Lt7s@-FW5R%fA5y;tF_Ha#)aVED5Bjo^utqVAnX!aOqG(tR3}&t;vvPiwi$ zo^qG$ansvTAH{5C;mO0rOXuKJ(*!-e1#^c<+O;ohObva!uxFqD)(+NLmu;`xQuhMV z;V<1~_mEl`I;pT{533mWMc2KDIk0<>wQDy&#92dx6R$5pTmti>xNtIjTXnm=_p}2I+s0F zZmemcbOxod;Xx0bT2{|R=W1`+F4D@{1B1)Cns*rxr5M7I_?`52#Jjg@msx&=z-p~* z7(-o2_q+EK2EA&F`ud7b7C-Z%u_34J1sYxTEri7=km~li>3bM>H=HPBfh1(Gu-!j-Csb1{~e`NjQ3mIQn_R(Mx7=^j*<)r--9pHta0p=HAjh z^y&=!{%DLj&6P*kBF4;{=F2^XbT|D_f~|+szr>h3ijf<=rM>Qwr@SS5w0ai5QGB{= zkGJfemYvb!s0QWIPS(yyH73dHtT|P$ktwb#k4`f_=sq(nul|7*rNeIe0oY%bTY48< zE-P-yuCf8QT|b6l@w=nN9foUmz}r>~AbB)^8P{Tb<_-oGBLZe|dD#FJIx}tL(p#ke zVZ$73c;J-u*cll>aiz9BHzNH&ph_C2RmsQ_2HvVj-InpW47_(t>5=gmp$E}*A0%fn zIFaKOchysst0D&;ahHu1KRH<_Rqiag%XYY{cDV<*-m)-^%6i2tC>EQ3fv9uO+vS}% zW~adfx(rpWdTP95$6=!Ejt)IGHZ)$nqp&%|`rN;Jt74<+r$iKwp>f`>@sqq&4~#!o zyy7rl81ljObJ2}dmfsUPaNsc)-$y=HZ2D4}f5vUd)A(>;Z;YqzKRDJ-WkfpNe|RjS zsvd*;^S>P*t~_t7Ky_nJAmGPOs$8`PjNV(B-^JHWyT{h9dO$Mxa$}#pv|$EIISB@` z|Mw(8Fvd3eY&Cd)l`3pbGN8gq4X6%QvI$X?16cEp$nXS#4`$DYRs*ym;ooE#U#EE9{O`Q1g3O+);&BV*TG^u={O_D;UcC6Ep4xB8%tE59hzc{*^n1f~ix6Hf+k~ zm`6E@PKrktT2KBN?_nshtLBrA_o=XVdSo=2Le2g82|2zPruH;`$;y4 z9GUIcHk%`)0{`yGWGhUqciZ`OmzTqnqrFINDesIPuRr6RrPYNdudRdbYW2=g7-WOj z)@>Hd*yAOG)p%ktkJf2_w7a9uk8UJUwzM2-{A5KTI8~f!K+1hk!QT(=WT~c)P$}oT zA{F~D@amTZCyNY*9dJ|@wuZ3#H4m_>(e>n$pQV+jc&3?W%sZo(pEj)jjOE@jQmVy> z!rQcv?(mbT_2gMXCWGeEyVR~+bZ0lS?At!CFVT>{o((nDlZbpb4q{J=@iL{I){Z@}zYFRxDirGeRjk8Wim z7u|S&VMUc%7{MkmR>z4$AB-`kj?xBAm+|m}?}#V!UsKJtLQC-#pre(s0TG>icdub> zP|J=-e)zsVjQa0ldj~}lm1 zY^eyUy)LNs1NT|5fE@T=0%ni3~Y7JhfAFnYA9rA7#0%}pWezf~zP zmY;_y;lY)Njy`vo<0{su8aS`886Ux4FqG8aO)Wd31CJn?{j~Bc3Jg;kgd5bQWu$3? z|I!Cvhw!C4W%USM#kqoC_Z{o6V4|Kh^T;iBY^|VLss4V=(Crw%`yL!LLb=;Z#z)i3 z$x-2+*Y2j1qyXr9yvGN!DEG+)+%1No6gw@Jk&3yDjY)> z|4ACtkyz=j%DCx1vL)Pfs&Q!WJ`$VpHo$Z%-E?>35U$%M-ibS7I+gD!<`r?`e|w{$ zMXhFr#rbAt;iY>{Np~ZVdw+D}SmRK1BT@r4P9W(LyV5ng&#Q6MO^gcHH$%$*M>ZjpHel#k zvDU(ATG6i#6(){qysvQRW6>{de&3FXU(c9dOwrWf=daYB20FTAd>@yq-x6v|0{b6X zsLt@K_+7e8K9JY#=X})|?HQGL!Tjvi&ruT^-Uk7!r{gEQ2lh;P{1f!+EzNnCwc}~? z&a6ViYk;P3KxY10%r!CgeHMdA8H1f=3~YZ*t(P@0Jevw5^5L^Y{D8=~ z=lk%yc^%X1PF}smd%UH)r0Pehhn4u9!+TE(9YoV>Mb^ybA)wqxMz~uKzW*>gQVMnv z&f0UFuusQ&%fcRef3EEI&dGY0cDm`?yagF|)lJ@&J#P9|Z$XE<>P|QP9dAKQ|32wICd$m)3sgh*l7B8 zF;sc_yulq)fab`~L(z?Y@Xoube0O>J7N&%uvAPjA{T27TA9?8w%rq-;mZmq!WlDFd zOT6@L>QO@R(_d*x--*Q?UH4g4!@TYOiDLtPlfNYWqe}i~`pRT-VV`%U!-|RAuXu&z za9FknPBAmcV&vbn51?|%F365zBHenGmfZt=tFY{FxOhwCI*F^kjoWQhR9J44?ZR-$ zcD^~_<)(-H+nL9COYRXYFTJT;iGWgx36D82?%s{JK)~H)TY!&OWmrBHDsUs1>D$#> z>&0^XZUb)&0M^rl563P6#Jt6Q$|T>$VyS6Eo?X0$MWUrii$#PA%2;^mp)wpZe6*y* z{Mj^2E+8#Ugr^NC2T>jCgb>V7Ieb65|Q%lTBqDpHxwOKpy;0TuIR=W z@D)-=6~{ZNThn*eZTFVIArE-V9^foD+d+neTX>=6yLdSMd;UqSxkq)B;qxr_M~o@q zPH)*J(H;LMJze*L`w$R$ldGT%7J&Z!-%!Ku6ctc+jHMwlhDIR#w``1GF||=*X~A-Q zw}N2$QhvY5Gm~E0U;!JI5%HF6DMK@S30~?{hIvpJ(8g(aP*sB>ji9)wO};V|fiT@- zdQ_|`*{FEE2;}hZG-%%A*_dX(>DxozdAEBDx{UWR(sJH+qBndVy02Ze z-uUzy%dQD8eUrx8aH)Y#70WN%3nj zYe77`VFd<*A{Mccyh`+sHdlkF{s-mu`+6r9RGtFG|Oy41Plx;`4LuWRrd&h9HJX})`xgA{1xFHXKU9AU`vP&4D z4x=!?D>?;!)f64Go0y-G&kobYr-gRdxks$%{66Rc%@}H+wHk|Q8V;!^DdF^g+Uhru za|(QS@8+Dz*x>K`KhiivOf#EgM#fVkPGKlycRK|;p5DdLlpGC&BZJ+?Eq?w1CK1xI z5uWjo$kUMsx5N|qeh~HOq*H$5@wzd)CeM_tOx*-4d$t#vSOTcqQ9h+Nm*_4w zy?glIb(@j`nVYzQM?1SW+g{*|f`VVU(`TuQ;?3^%17r3>kGrof=&2;L#(g%m6*^_$ zoSMc*L9?3?)=9Cd&^9c_rvBs%d#Kvo2VJXLN~?~)kofb$u+E}m8~At?>ky^<@5OJ0 zkvSnGWF;GvRDSh#U}mVH1GqIb-AL}xK59_^bcOc898JQrA-e8b5x$RZWpChm%2g3A zU%l#+)Tu48#_FdEZ4s~L=BVNE3c~L>%w-N2(N%J7mKbI;N4;=fOz5DwB8R=MW`o?j zLx(*p z9S_FeqNzSp4X?A?_(zFuv>R(DD0$;lgO*(&d>YByUQ{c(HZqq~z_PgMho<3}axahMGDS^ZdtK%>FFnW<^ANLG zsG!#Px84fL*xYmr&&HvNUzFvOSzl1(*urj=Tg7L)FWSyDKgYWrXhHnuto_u5ksf*t z!~dYlu2mK0^{`$q6{w}VyoGyUBy}4d6Lx3ww(-&+2 zGT5*AC1-yPs7uED_pq4<{NM+Z08699RjKAJ!5*!p1R)_!ShA8dQNUJNnrPehRqfto z8O1YQBzavASEm%)bCongD!TDr_Z5=jp;tLJVx7*<2<|n*AOQcQXvz2X^3unl1CP2p z*dHsAXHOrl+gyx)fhM*V<92c7eqO4J@rnbF$|4Y{5ua4u5vU)tTZBDQv(yg<`jpjA z7dppm=hl$iUe-j-+BP*@{*XZ()UzscE9fC+TTurwk19afTGJd`XS7Fx)K2rOHn1lc zb?A%us?*`|S`7(uBs30z6Q9x-m>9Y;YlQn_te((GvC%w^t;UR@ zF>BD6^{elu%A|BpJK$tSC8@HNDpS$H$Iw58V1L{Z>(gcahf?8+>#X!pC&PJKz4P0D zik&R6Lul%T-%OjvAAFMn7{`I%`ciTiru^3BsNmAMT9uss3+(kAlv-)%U&Ba&G>-pI z9Hdi*^(VkmBH#QwW~9G$M*5`}!kVxb*p}_%z4sCkGyBi9SJsiu>8Q6|Mr-atYMUy} z@dH1!W$peSO(Ez&Sme#lPphS{JW4apP&4{-s(k`r+t{Y?*qQ2|h+x`23qWv&+U1=0 zCR4+zclMoO^RkM`gEdf=G1<>-oI(u}D{<8yo_u?Ou<5D!P`Q7DprWs8VTfQb5LMrl zids9Gxqe~wy)1fd)JS>@gVS?z^;$Q1+u(iuAMlpOT9!tl12;-fF5(QNQm&k=Dq(R8kgB`R(xurgHb`eDCS*8^nhl! zNY=1A=TFh_5^@Y{^Zrg)(|nc*%J)S-ja zhOaM&nAvH9ZQnP66N?T$%{UAQa1-8b2-2S`ho9f2Z=z;t6ThbVk7MHSBVR^DH*u73UG*+3HH$iau&Tk>DT*Za_F8!5BaMboS7KiA%6_|3R zOZo8T``vC;RT`x;&&#`k#3SU1;dk+R1HC9p(((!&_O|dvRr$FG?3$NS3E5e=Pl=r* z!u>Je&)O)TJ&vN*%{WDmnegS(abTGl@Q(}ND|HI~L1^(t@b|$DN5U_U7lH1uOkmdU zn(hYiWcVu@Xd*G<@J1yak?lB}EvIaO9Q<37rsd$s5%hvTG=jso27tW^wqx1>Sze!& zFz39e@Y;{jO{;SsQV8BLMDvBc&PYpVEXQ$SRjg^*{SZh2-``$5 zQl@V94bCSk(|7g&|C2{e4_TRX^BP{t`-iPO(&WwVzis0LC#+1zJ3r=q7;F%Q^%xkn zbt8U=a;VPw;X`v=&ej(O%y$|e1veukD^8+Qq*^-?vB?K@;1x?p{U>>;uf?H~6X!Dr zbx~9WCf`Yhma5fHAeys$?sz+&y}3cm^W>hg{aBWloK_?tRm#PA!y1)O+z3ADSaDUn zB^@Cldeczjql!c&pZ}uy2nxJa3yr|P>^Wa2tME{+d&$L4@#?%UM>Vv6&uVC6yzq|y zeG6a7N6$$92Mf8J>lAuR;j=lA!WanTaw$%-_+u>_lyo+l@w&fK5gj;u6!V*Pe?Z=; zX=|U=S~2@PIvohH(wwgLZF0zCu~jnhA@2S74q5O770-N)8fpteL=3-5vX%sjXC3Cu z{;%*0s*`@Gc$Vx-Es7+I{Vx~KI>_Ay*DK>02S`^}#<>=811U^<2YBd@UXuwMfzxUW zcbfXKfjn1D~=`<;~HbJRkPRUCJy6WKJM21|(RikrdetRif*fRE@<}=^?K(LD($( z*c%)pPXTHV z0CkbC2S8cHx_*&z>nYR$q;c?xuIrtK)DiMlv?>k?`#(A`#%GICa{ws=BpIcx>gfi` z1^fxTB=Al*@LnZzfElJ|27{!Xu4;%5v`oRP3E;`BRwzJ16x&MTG`xD?DLN{;?(e7J z)zi{oAAMDH;Q#R1;zx%Vl9ihfX$m{L)R`h_33UeJ4#(CqT zcnszL3%k!V#zL==IpVY5Xp$rNd@N`ugw-S1PKYG~tmmryKapG`!&*(KkxefQ+7K_HOFw;ZtvPPyu}f>pBeK z2)Rwf2wsUmm^YAELrQe;MrO5SThB{~iQ%n0$!1}77 zP4VgVX2R8GmA}%K&j#hMv9CSBtIiE|4~d1%LJ_@Sz#R!Bt8518zgBsca08@|%(%dU z&jsMm&?`O_HP?K?`(sL46e%4V=2)fP{NBI-tfhy<#>C$KLP|ffk zYD6`7&1)GlSTIWqlNGtM`rEZmo(UHwL%DMqmZxfn6^3Gky&=wHVO&lJ8HK0&A3zaz z+~78C#R+ z@e%3Avi%`fwDym26cBhyGHPC?U3RsIQ2BEbjr)k_jt+d3(Sb)H!FEi3dG8Q&^ywGb zii;iEcCgs`<%xG#E01;*hMdB1q~&0)mYqjlZ1uD5gN3IL6<)2(Jz045*xa7NGshHO znS1$z<$T0_I{&6f+rj;JRPiA21zqB72W6M8IaV_oOoXleh9NrXqi)&}$LwO%!D8{^ zQm66JV(a&X^~5Rby^p!Muf~f_=OPXX19JNv*0WtAjIRv4{e-SMh0T$96w95=vAJI3 z>fg8r6d^QOncG^JJT~`OvHw6}a_;4urgP>Wf(s_{}cu%yht)KB6%dk2K@*CWkmVtD;u!<-~m zyRWkJcRKY_w7Qsea2LK~ia+2VDAKZ>&idG#{PSMdV7z$QK~9ak{HVeqC(q*Rpt^<# z2AeNG2FQB#%N&M`n-25JV!knD+%(~6twG?8N!J{gtfS|cFslS1LpxBRraMHYtuY>*8gmxGNLijgTVDj&6p$IE!Ct^MZ)i}fz7H_W&)M;! z79W8SK4mZlFJtD4P&&X{c!mAHn}yFlNK)7`8=vXF{dBYUIy8`1?q7Hi3{caOi^y9uUceilbrUYVQ|{rZp` zy8-?G>*f3ZOXZhRd8Bd3J15q1P9!(y+!2^I?0qW5$=Cfa6b{YFUFv-@+w#fw{L>sf zF)^nw)Y*97M0^?_kI1sfyv)IT8`=tuytlY8RE(diVH){jlGd=y${yfB^Ao#CRfStV z70!QARemaxFA_o7GCy`Mvm22|6H&$MRWv?2@e`_%{Nw;`W{E^@A_AMdbh4PwK>CPh z&S#eBs;EJ9P0hQ~DaQQ=1OD=Kvb6FXL!QF6$W*qL^HQsyM2I!;v1u+JH94!65%UJ; z8C4r*C!=>hd!5u%xywbNONyy;UqK$Jb)lDnIhI>spmKozxvv%-`(+y!yo-Y`)|btu zc`XmG*0ywGG%?B3*H7vzhtryu6fZ3!(XS0D6NCXTqfdC99e=6;LKn&uhmBC?B2DSW zlTN5G`ZJhclci=Ri@m>aWh&HfP9GJqy#+~Oa1wqp9pnFXbp3a}cmwKzR1>(4_dY#YN7~gI@dy9Mw~Jxh9t?ULtwk<24i`rx)9= z?{+UCe!y`riFnJy?j;=Qk1C7IJ89J^g;zuQy9z6uP=2JaA{@&9oMW@OseWSjh3nUD z5iVsCnD{`&*EvNR=yK`<`#2eBHrEv0X81#<`6*`%@&@+>D)o=2DROt+FJj2nuzt}9 zd@<^n%kyY{*gs=0BECQDju7~5nw(6j#ok=noG+dow0UUav?+PdN{QOuN!zVRcP~qM zm%bsfa$X`A^p#&Zn-2(STi+U63KtbyEM{rNi4rkV-l;TA~*-|RuwyZ~$7+`0U)+-2^o z`zO9^oeGC3NQcz0zU~MWWiU&S}i|QI3xI3Z+@CR3p_?%8k%l?IpD)oQn?Z!(&2^{*+5r%hBXWRcb^S9Y|QY4v)OUQ&x3ebIpjXa|hmC(np(@KV<> zTc(6cZc#~l-&;^Wbqv{W*V%6ZPR;?n1r3_ZX*6DMJG;o>yRPU!jp|^gE-o<60#iqp z5@OPBDiAD=_B1I-LsC0Q6-<71l;L71bP!sleaozWMhC8fEOdLTx?LwdTHG596r2JS z>zoEEmDo&D2hXNi^1-mwF`O>wEhn^CtMAp(frp_5x#c=sSU#0_Wukfb={aUiQMQde zKT!TrR?g(@*b6eX*g%x4@fCl~;$gzL7sv5dG+0MQhe50Xk3|zvP|eH{Tcd-QGla>K zRhAy0bORzwmJYIXlBFv;cmT7LEWNsAbciP6nlQbiBpXB#y2;Ry?0wO}<;rgFfRqw{A{F)!t|+j6(@pcb(e<-~GShLhtXwva}e3Ta?b zC>U)f^(Hg0D0HhWl#332N#^RbpXDiA=pH?S)SK)j;PzlFYFZH;e1)hO3SCbw)ih=c z?XZPR>P_atOKOrZq0sW^;Q16nrpAUKP7V*7N$dX_cp>5qhDxw<6C=-)ChZZo$wnEws|!w&{;wB~o0<^;grBVf0f zP7L75B@v8*YIn^2gE=6nMmBa~G!&YUy)EQMPB6$-bF)Gh@E)W)G}aFD1b{~96uetZ zcT-9(1B)4up3?+GkUlR+OfP}d2c$a!NY7YE>i8Lu!~ky!>BD76HsmP(Na>pau=^~q z4YPrXG2Rr|lgCe?wGy<}2atYlAxU)4KO6V5LtZ3u>{iQQ0I4z&f zT1x2%5;_K(aj;jaC^v8t7SK{2%h;e>noHD5V9zgex0)&1O2k+*UQVOd+F7xG1$*Uf z4udOQfx`nC-wBztYEWBT*2kkB^KM-piOPC{DYO^t^#g_5Tp1nsriA$Q#t%y2E*199 zR*xf* zIyyS=Et^YQLwYSN?QF^lmh4OAnj0NBQ@J|KGE$$(^@&y*)S)48kay(@M+aV`0JK!h zN~^6=Dj}&F#mMFPHkpr!4*at+wVlqf0qV6gqZ*U`%KT%tZpNhlJkQ4tYA2r&CaFGN}Uek7O&@z{?T&10;!J z-lAR}5ol%x%%W}{@^m!0gmgX#{YpPm{ZqNSZ0=5*TS{KJlQ#F+nUWme)4Ane1a`a4 zEk0204x9TEt!1xyfJw^cPTJgBWl`>|&Aq6h^o3cln{966{qPpa<)Yl}Huu?y(zUa4 z*W28+Ji^{0RjAyV0CrW$^utsuYi#ano4YE=oeE$_N*B+99k;n-Hh0A2wyM<@m|N;5 zKYY!0m$1p3c1+%M#SwWQE}cuxnf_WAntpHnURu|BBDo2#5G1;Ircz@cGF92R{9T{TzW08gj!5>NVs*h5^3R+v z%67HJDRp$Km=hgnRIz1j7{o@m(!9srF?06EG(?hxBE2etZup+m)Ut$%EVD)WOc7}# z{gE|CkzSt5RRl$GiYd~dBF|dUoijyb3+RumD=D&^=L!`;Yuu(rEHjgbf-SPr6p_8E zKPF=XJSepyn3t(wI682mDVR}RVHLC!75KWt6hjb_w1(dhu~TKxCtp-M(j6k+t$YD! z7X?!M6l(3f>wNDF_nVO)?Q*>GFOzm57*s+S4fAo0KS0YE52hTHlhc)PC8;FechWa2 zS!t;nFWp;gy>{Mt-7D{+ZiZ{dTh<9MsTIQLQQcPQklbYjd%NVZ4&d>YDQdFQODivH z*-S+^sE#DbS(3B%*t#x>`4g2t-5SJ}d&`m-=pv$KDtZ>@>;*IQOiDJdtF($kibImE{rTdGf`{>GGwP^z2LxanZr5y@tps6}NMbbOXG>#dx&0@|2v=eX9mXI5!E*iQg6>ZhHeyMS-WH zz*AA+sk?#K$pd(j>w>3L!RvHU#sR!#nbO@N(iA*p0-lNjPep+zxeh!b1iTCn8N-G* z1Tq*m(@@i`rCVn~6#$?L08j-0s3{&mP4WP0D-U70lBcj-F$2qVL+MMi;HfC^R1|nB z3OrE+cuhQj*T6&YL~y}dJ`-M|v~m_a6$PG(0#8MOSI+}@wLE~A;30T@JO!_JCcLVW zVYMkLswnVO6nH8Myc!?g zhk9iTT*puKigEW$M(#0WUm>_=#q zFkhIJFuy3X@}5GlJA=L!pRu>1iwB$7J9)5^Ueyo~n&rV^cI`Zf_tjo9#dq@-_P^DO zov>-zp8I3qza|0xs}3L%7XFXp-}b-$`1tREPyC;ABHbSW|KEP5jQ?3ne^&86R^IF- z;uo%`=bb(!jA%1d98UC4B!=8u`l4BeKd5E+rH9%3LqD2u{fqdz_K=FO7^K*csjaFe zF#o?BUq358UOr>uN5D>97ALm0}$8N1ByL?mWa zl1UsS9u@-6_Sv}YxPVcJWh1_}&O16<3@Hh-(!23i5OH^|eL`m>t$lJ0Z@IVP?C?I) zas?ah#*q(epFoROti9j0fzC$m*ogdZerr03QZ+zm7Stl?-r|@!e?^<(YKt8OhI#~u_2f&CPa>fl z*nh@t2BV8@|53Uzx8qCx&TLUY^*Iuq+@(WkH^KQpu3@+L6Vr63L_xx}5jhYc1kV!y? zhP!`ZW2-m7WDJNb$-aLdslW`4?!TpeFa}qVTRPGb zC!k02dr}qcb3A7e1ocuiluQ)GZ#xAWA(eIO$C6B0g}jOeSooJ-hZ zOi_&Pc3JT}{U2%Op~^fh)3%q{^1a^y4+m3ieB;!YrJ3M<+XXuI zwEBx8%J@+g@BiKf#mGr67u*)(*P2Dh@sNhpfw8-|-d0 zw(I)S`p3qvDS82oZ@P+$4A58FS3=dM(&h2eDfm3~d@!jGRpFgj*`c@8IkaiCtdDkA}qTmm8)n;jt%J zme7?=vGEh@MnfgFwi5XQsUL1Rn17cBAcZ)4jP2~yFz(#QYZbSCQlGezfNYsM3bm8r zkTsMrzL?291_^5@1o1V<^I8f9`=OI&KQu@GYa_I_KCju#*5_ZthMTqroO+*l^2n{v z)BbH>nXceb3dZZy`+CN2nf{uL%HMRYWHUDw6O!f-F_()Lrq4Pz^JC)E8VC0i(0^In z?xD`_CQx8b|HIsqcZ_Z>8PpOaV<;8|5?gm@Gv<*z*$#N}7WS`w#0o);p2gQ#0hs%e zw=}*eGHkEJ$Hdk9*%9Y0L z?v*KbK}NxzT;ttluJLX&*Lb&?YrNacHQsIJ8t*o9jdz=_@zyy)=BnC#+-lr32nH3Q zA^L6Rt;XKM`0g{!wZ^*_gvYq*J4`&vF}oJTcApcQs9ew4hF3NV;oR}N$JqE?_PUk# zgA|?!jUVMOtn=aBW8+7K`SsYaxhZ+#e$f{asf|)qe#Hhz!+nCWGBrg?5m4L8%+*GY z-#QKf%

tcDBLL-}R*pawh)hPlP}5H-h(~rJ1_KuVZBpC{jT+E~3)qov~@1ebdC+h8}>`PQL2CK{t z)kh2En>gNhe__@;T?!Br%Is;0LF0h)UT;55&o6pt17~oBy*bP%PG!Ed`icc&EaHh) zw8;JeSxf=l5C-}|I2~!U(%M0=nWzfSLDZ)%kgm-WAbG@jedv8}y)l4xm@^{R{5F(3 zmTl+Wu`OqW^EsZ1S#BTxcuPwEi=T&go25WOq@~Kb~ip z#9CkL^kOXs|AoKiH(S(XM);q(8Ea`Ydj03*OA-oZ%Od#J8YK2_nUi~)(hHk5z#`lq zkWxlnCd6q7%|b>d7D08bSC{QQMmB)&Y?y0gv&=uW9^#PtEv4!VI}xpHEIqF+c#3PW zC~dw4Zq@33A9`@J?u9baAx0@lc|%P9xQAw&OYgZ_t(CbXpB9`Q?jE%UOar})ROl3k z|LNTzf40NLsj5%M0Gy5a!a_^LU7jrx?8VF$TVqeR880l*g?As-NqKhWGun-P!?YND z{M`^y{QwwA!?!(}Ozm1yySuZtY{jg-(pf{5tAATgfM_LcA}G&4&8;c9>U|&YW&2)(IBA<`CM`W29yFSif3yO_q+MT<=C(T# z++}y)50&AFLLat;=vrAbGYbX&|9&zQX6<>l<3r<7HjLzg(RNHtiq+>9<7XC{=M?L| z;^m!U>w53gPHuHEetOYaFL8{?$dO$_XWZR45w_#o-FfqYm)-50Z}*Zr#!mn?aQ<0z zBev@tYN@e<12gf-aQ^KrkLBK5csbO6B5qqJ<=O@m>zbGxnvLY=~md z;F&0RO^JC|g!7~G@{#;zZ;1nvUOwD1o~w#(tWK~u^$+=fjc$zS*C!$bflhpN-W8Gj zzqdTU`f+#P%?DZ&Mn{A1j)?+lweYM?gXWr!WOU<#6j$ha3o?bF_p+B?Eq#=KCjYJl zHM`HLDQt-x882)7FG1+C*OsAF9I43zIU;tAcx}re9hyu{>84}pP13`Y@9^4si0pow zUi7Wcep7nyo22)>VLAfVM4es+0I#wTTyAgmMVa3?7|vxNf9P*u$%Vc8`F``WhuUcu zTd&{F`y1}J?NRO~zA}~=`=;;*?JMSZp9P>z7BLbnf9c2}4Wl3&!&{M}Y>K0w+(Q_V zTszNPSe9xvZy7GIL5EPso;FE1;B~qCLStMz^vp@29UT3qV=0KLCV)TUv*#ytkWvyv zS$CffE|I~9Z?(9B2op9_rFdn=B#MGTg#eGdc)SE&HU6dOx*N3|ffZZKOv*$jF7fM| z2ky&&BfgjaPqf;_GI9 z5Z(A2sF?(MYjoq@qO;C@IlA#zBCzXl9_~7*@A#Mf0s4}w=zNcJa~zJ7xmyMFxjc8x z23bmJtFhGPTH0Pu0~(-ghoR(Kg^4Jc;)kIkD=&bm9Y1=!di!{rsF)q^+V1Fm2k+xu zJCxSU2A9t0x&!JZO;8@0ikA(&q_8Bv?qJK_XuIDsky}WcYgT9I#2xPD7Qg?aEfcFg z?D|5-eQ?5Q`PHi9-RE!S=)(g995KVek~^6n!O|5DZmka$gN zRYi2*Y5QRvL^ljD?tEqZy4}Amb$e`;Xh{FvtL`W|*P*7{ zT?DlH?kCTiv7xgh$gzzAnx~QL8h&P#i&5|2NG`i>FMu7i5aXph{K$MY4Ukq9Siff) zq{#%X3RI-g#Lg zE^4)E!aOp%@em(5Mc+;ysbk#&NPu{EQAn>uH`1dTbVS-m*L_L6lJ4;`It;a1i<9ze zb3HZb6QO$6zx}aol@pjM?F9x~KkEmg&zFcP(!PR+EmLdqlc=n}OJ_+|iA%(t-@P?n&bZ)Zp<=*0nhr4APD6KXfyd8m&yM5LeO@$y9lEtI?w=ug;9ebaMV7)`+s5k ztyPG!Ok4b>2>PSM8a=dIxlP8=Y-&QnVv{75!kL?kt!w$2a<}WWLfkR@lZ#Y+BrT`) zErrlWsL+2Iw6aKW$PN3&b9vpW{rPG&6q!96%- z-`}sB>c<4CWjuQQTEG;Z3*{X4xQ-bxA~Id*f?*a6)t&4{d<(l~b4N3Z+VlTHHmQMT zGn_TjL`8QuWU71BeMV!I!@QLE8ok&I*QpiF;+wj=Ny&27eP~B8a!bggsgsxXATF?i z1X8+iEIOzylcmo{9YPECK5>|U`&@M6mKilZU|gOpdpWhwR5hCfg@HPn#3JD8i6 zCMgb8YiTW2_PB_`hbYfZIblj!b#A>;I?seeOLHt6h|teV0Hzae8c_3!$gg+z!c*3t zgG4Lq5A#A3(uPf%E4CPYRZm9d1wR-=EbnGH0!V|Cd6o7F@aACK@E ztMft1*4O7koRH(9P(1f+zC?Y^2f5<<+f+J{GTwzdSK4Fj(ABmX7h|v?8UEB=` zn>aU_j-a-NV*6kPdQ9h!8gF#9RM1GVkJP=CY=V*wa5YfzgZQ7M8sl%q^e7yf%a+xiHB4hepk_j+&(`U#UZ7F~CYwE<~dKsyQ(XcM5fO=uPC*AhPeoV(Td z8&&_-Q0Q@CfaA@7_;(fqY>`<0iPBH8QeSUhb%D+b7Q(&7?cPOM?=mfnw|ZA)%3Izc z*uoZgjj_oWrpgbROTu(oI)j_tWzls@rLYw-Ce_e^lElYIvA*}tRdTCqe3~*H6`VX< z)NhCE5nyrg7Nx8BXEdd{+cZ$%Y*mh5&F(XRB!Own3MnUCDAOB|#IRCq;qU^*@XtrT=9eQ^7WT!7NR!Y6lQ=r2C)yJCRiyvrR-3o&imvioY5>qZ^q)g!V&@Rw&szMM;76 z&-hEJzOHH2w;sUeOOyRl_WexBS9qe7V+@q#k(IZ?#~fn_5(&sqi-%>}f7SFAub*A~ z>v8l&2Ru=f-I2_CtPczM=)BY2s=H)-Mr{4ggN32WVV`S4GHzNTw%W7K)U9iuHkbZ{ z?fG$z(ba{Q!~H8-9?t!~z*d+1@8&HI=bxN+MkK%6`#Xmlxm#Y%T@>B;a!Y?W|0{aH zG_+-LB>#(vUj(C$x`#RJIIzRYRxK|2FkF`%5Z9gclZ|Mq2$F}gi zh3t85*}B@r`DUtPDl@2%#M0^fH|5WV%!nS1KNhUGpC$|yBvOz)4kkvPVO@N zf78A`W<~V8*uGXe5^@Z!jb-@5Y8 zDUpP4t3p&69asm9^;$2UdF?h#S!G%nSkV(V1Tg(M0mK8qyat%jjlNd?P(5p36&d=! zG2vXe-hKGy1C#DS*(c?Kcl{NfQYUc?)tMpqQ{Gz+_7Bd&X7DnR4?VBV<&pJVzod<4 z(ZMbyLnt<&@~DO!^Okeio$*?5j{hZ&*Bfp*kb56DIz&JmsIT{Jb*QJ3?3(XLIE^B#Z$6V4dAd+da|~ zHcBjGj_B$r_;c_PTw%^DY~0VV?QmZzW)g)VNw+NRmI#+%%M??eWAi(sS&KTIbUCr8 zny$A`ydN{hn;)JxKN20d74eOyq)>_!wnZRCeRROVAJ(!vSK6e-@jAb5jIOMgnr_J7 zC0_7O02M?rDb2cAXFVY3&Q^7&sn}uL{?+`F&S;vK)uvslotKTUS)X#)HOq?cMN8{hx)lrg;Op=oGL^EuMqM_T21VIdow^B5%s#G3d@LmmIq88`_O zBU)5=e}GFMEl4&9u)y<9Zjdg^@M^!>DLSjY+||~-&DkOD;v`$ib8j&u6QQnlbpNu#E)VEtAjt|=9X7-HS~KE zo-=K*EUn-_2+*{E8ZC>ooN#8Owc%)@_bnu_Rro<%Cc=LolUSe3IdG!fmn0X5#`QtR~6)r!pNFB)clFq==D2Cad z`aDuH{wUnOZXv8uIu=l(?-B7UH>nfp%~Cc^8aE5?8~|rUl6+fmw8ph%Dsix zMXYFjq6x;6^iO&7S6VdiFgOl+%{(T0i!FVn%z zFqtmuY>IBo%6(VwWx9=B4S9G?`8)qCFr7973VX8G%B_SqzsJgzCemw>A`Q{&b-g;R ztS>rm*OfJDFCgsC6tmk`-QQTONtO**PcJEsGz{W83qn#e;>PtU#)7y>WgwDE%hytr z<`e17p$9_`o7^_mM}m+ULTDtUBVFo4+uhA|cMGXiH)+Yj%&Xg7di+c^M>HY+HXw__ z^dT5E$Zlnsh?V#*8&fme-ua`@_mU6&Pto^zA^?8%I{NCE97|tW39qLw6B0vT`JgO) zmAf_cQ0RVgYnG;1sxo~?Lt1~HP2chkUqjy)>i>V}JH0-Zp+f&S$t!!``5g08Y;4!Z zUcd%4}eh4Nqr_ijjf8t^QK!k)Uynx@b+|T8?c8dU({YDGJ-bGHKIkfsmg;f=y{P*^! zIrrnX0}HElLqh)R6fWYefcYau3<7?48M!NS|26r=(jvY84!8p|KaTXi4d52PWMClK zb00v|EK&1}bRi?G7*r?9_~6nW1LzSNAaVWysqckx!Y!f$Q4Z56TvhFGKnk0x3acWl z%T-$SZY%p1Rfcl!5AqNGPTG^$4en3^wG{zGU1LG2lD?RO8nnn5eB3L?1wu-9+8|jd4P(WG+Hp{lSJX9rYBL+B!N!{T!&}Y7 zyB_^uWROIxA&CE3NI{o{F}x%*Ybj5V{fzZ}f02mA%!)e0G((ZXRb;N9Mx!KzgJh;Q z6-Yu7KI8v8;m9;8hJIHUiYHV=73oPOj!TS2mtr(%CvmG=qXWNmKp`j6$hidSRcchT zP$yN0O?`>$%X@93gmTiLEWfc0lc1S6w%qNyacydDr3KswwCSSp4!^_xEn<{yewv3t zUA4+RVOplvNcY6mB7QgU4EuGjP(++$M}qLU@Y7SRNGbXj$LFL5>)+LqzOdR`tc}1@ zv<+0A(Kh!zb3((Fri;1kK<*7WIlDs7S6(JpL{hksV8@MR8JDGqUtDIYP6b^4X^aMg zmQ<5WEBB?LAiy?fb0_x=Au35H`!{ zF#jWOr=WCNt^0RLz$e`EGlh{b>m;OgxLhODmKDlkyEZAPF1NqKyma%@?6&ptU|FSy zN4?wD$)N03Gj)54VZ@@fgq}GSOQ_A-fAm(6&7qVAjtjM~BZn1n?!L0aoKYbyoPs@d z;fWaSqpYGdkX78zugk9`a^%RPY7J3e_(UY&%NA5&{gAHyIt%qM`p1Gdlj1N6$(G6COeMWE0%JyBGO;aooSGE4JWGhU?oav@}3F}N&5qOw+UZ$M~hsR~qE&P+( ztF}AU#9djE#9it&+26|M@JzX!#+L0UY-xRi`9~|`Py6e;y?oYNmC+8z991oA$0n!- zD|tmF)Y*O~=@{WP_C{<4!%QygAinj_yd`Y$HE{%uLz+fR#rtyHu=5t0TQMrPNJZ#= zg2tZ#D%b~G% z!2@2}X}s&UP(B(sH+9D~aE6N)h26zn_z7I3TM{3y;!2U3#jIJt_Er!*nz{MFYGcGD zoB7^GbMo&XH8iBQJ<31pLmpjdX7Z3RaEz(Bw?F^-=6;^;G0nvpP+duzb#bO?cJvx8 zDZy{$)_w1@L6gI!u*}c4u*U)3Q^BZqKw`CA)1(0z9rQF+*Fhdr{7@c0!8Voe0&r@J z3PE*W<$cz8v;Mw%7+N-4$~!0HHE=1YMsTmgx%NqT0WS2N+o+@urlpa}XmmOTuEuGP z>{dASp+eb}n){QAV81~SAHLppKdPp}KZebV)K_Cqq~$g%9TZvK)pBG!BXucsE_{pX z5F7lb_dlHKWvaQ~`E4GD-@Y9`Y}m`hTR6~iEnI$dMFh_*=#t3^hGYF|E&S+UDGb)@@zto=Ud- zmK~d>WF~QhlQ?t~8!!+OCo%E;{?^+6nUNpRUgs0df4|pWd%gDBFUCE74`RD#m3%5) z^#`T|WHB^?mQtQ@xrdJLENNQUYlkPb4N=cgG7ZQ01FsEc*=I&ySy;PX@0W$eKkOi^ zQ_h^<&tHE@F`X|=48MyTk0fAI1TshU&4#l4NosE-wmyFZTz7evy8Ia$Omp<)@=wETpiCg?e}}j*#=)<%%oT)Ozlnng4!x(y7ngD=U+{UGkcS7*LUKjs6D%w z^_n~XirypZ)4%$=dzg_DziU8aA> zuSisSy;-;gvB|7q^Ir0X_qy`C|H-gfzj*4IB-boz`Y=bcqq~y6y5Pvzo0PwDL1N$D zU*~PPWK0a~7Syiq@njSj%Ecc+QSBbfZ7sc*aIJzgvuBXL?yI;+H5Xfg~qXmhh zi%;u5ZQow(U?0JMD6InewExr**OSEhpO%A^c zxx^Vs33GojV04ou4g}J zPThLo8oE_DIU)aMz&&8wE6#J9VQssJHps8@(|4Sgj(md@8p)Q8Zsz){;WumOgq~?h zETkewG%}U-6jX8{p$&G~G4k6KCXMoQ4}?)SXz&R=sod$`FwEyHgibyalV zR$9;Rv*ntRXX=LEtVK!lyYAXx8O&;@DL+i}zzCG@26DP&lHtCS}E!@M5>9KLrwrI%) zB-whlUq*`7=>Z7vy42d=$6E82nzg{cbiTrhEm=sez zW=|Vcv`|F{fV5Y&a4Qy^&9kiT7iBLfGwKn@^?0GA7v;6(u7$GNekY%8E5E4{TXhy# zWqx%?=|*+|_z`G-KP|(X9plg0fHy=RlNqi-YO*fTmB(aZI$Yjs!AJbJ+-zy3d2bi9 zQDA7sfaIp>WNCfL%1VH)Qklay5w_z*8`a+XGv7S^64GphKav71&gGJzTz1!~b%j*v zeW;7_$>W=)eBltJ z28ce-y$ad5A&)ir0HhkbbT{+;vud4zHn_96pOquqkMH*rhyj`4*a-Rg=#A7vFk3c0 z<#4{6$e(>ax?30l`W(Mi=cB)kA3`UJBnd7!AAJ?>qf4HTmVE92GLdhv$3F8Lbdf#$ z7kz(go;=Q5?jJ12+g?T^xcV7Q0_PF>ZW}y#hhYd*6 zymW4VJ%M;W=lBG1YGRNkkW`Htq`!SDP@B7&P`S}$>7d2eU5$65H9K`i>*2}LcP+Bv zY8-r($fFi%?xyEoOOw7)ru2EQAP>2Irz-lB7A;0A?W>CJ_nH;`jjHHQi_XM#{uc|1 zObwj9aw8!;hRr19^+^%K-w+w!zkW&rumjdU!mU#K#3~!JY^hlAUK#4OV>i^`ls~h8 z+m2C)wd>zakil%{Q7Pq3Fk`_Hzs=SyYR5i79$mWpYUT1A@`8s|?J>VLdZS&V$~9&W zQi1+WEYZ{K(&OzDpMGUx*-xqOh~htemiM-1_#(n?!&>(8#HU|k@P>7FHeTV0WiL*A z`V0}$s$#ut6>OKWn!ba=^hc}A(NH?-tD~le2-J?wZB0n`kGkM zCb!zx%UYH8M@ZH`+StMm?W-`M`#hwU2#xVW`x*|luVC%i{-oN!mHI;aN{(;piuzh; zUz-a1T5HE-`m6D`S2fPzR8`0L>9KmX$LA2As-4e4HzSzC56hW3$kUN@wRXL%duxyc zU642w^|f15%cH(r?bu^vQd3*V7WXsk;HA<5N)GYc$L}D&y}CcHU-kEL^Pbh;RjHo~ zI%Pf$b?Ll5v!vRwY^eQL=hfaCIwHroJoq_&Wqq}2e#3-Q^|#fI{XV^~UB92qq_-fB_4qZ=d&AD zK2AytJ0CI$uGg+7EtB}S_u!{mc@JihF1%|^@M>5a39bC*Nb0H`x8*$E5+k`s$-N}^ z@T=>&m)DMuY3+`S=}2CoY6nSP!S4{s+*@6vWPf*WbSyhU=M&ueE7E{MEa>+>a}T#j zymtI{ZLQd7Ttor!Rf3zhMZXlVpx^&Zfd{O>gW9{{H)2y@TPPrgqksiBW+w?zz=X&6 z0u|V7;*0_hT7k_jWkS&Jil%^uuXg6U5VI!(0mr6pgS*xdF_jf3oAKsa3(Q1(f!^9i*at&uGe-XPoC4; zVd3)*KJ7}4t2Fq~Ad*)1El(c(KEoaOt6cwMq5dNC=D9ov3HR~TN?T%*7Mg3|uadZkk_;;e~JRkd2_&qdqowf4Gd*H1B?q3*xu6BFn)=q9W8^Iq@Z zym~Lh*G20ewECCVt{auTM!|D)83{Wu))MJJ~ z_TnE|==rV9Ra^sFX{gZ~s*C&dYhJtP^*>X)@>6?Yg7q(JJ;Hl(y7KwDxC{SCZn_{E z|Iaj{{MLvu&icKTns@NY$owTE&d=-P&it*nBfa^bX-E039pkt48!GLv=UWYYh6ebN z50@di>-{_-B1_}pqu*!0B=44SzmK!Z&b=slD%;Cpa9Cwm;&ER0^L-Yz>zO$P%Bo^c zUI&`x8lrn_zwDY*M!MU0f&hf=upMMb(F{x5ZNzZl_YAcMgC%~j^zA5E&a&+r)uW{; zv^|%66BWHDdVi7*@^ZE;uhf3!9wcgUPHEek?}Nfm^WLL#y>tKKlKj) zn&{USJ`r|D_(_~W&1(P%t8eAYO{ScIz_h#(&B^48Llf;ixQNC&2mtS!hsA9qqMPmlfvSiDoSI~P=lUA-DZy% zNW~A300a{5`sWRv;fqMoto}jnN~aAql{iA(qAtQ}B5{N^g>ALDji1`F=ctqiUZ5ju zb5$0PJh&{8sNvf~0@Y6w)IV?YIA26=Pz?Vd6Q$FJno69e?j0Uh6N%HbX;&H$KS@OG z*#Bj1UK_XRFRJ<*6vADTx4DnJ`sZz);EOglS`>}B;Z2)riqUjiYSIEUXctQmIUGN| z`WN)tv1+fb4)ZD_DE$(#)Q`O}sER4DitiQrM%7D44_Vz(=EFuKBT6G*U+viM0VlR~ zT(O_=XQz?yc4fKZo=`yd`F86YE1-w!dKnxH2)_Cl77Pc~iq-TSC2Q9kLPZ4eBdm50 z@&8M42e&2EDraA_zGHnreLA4jDl-Z}z1!@u+Of6Nde^NacCqU);yf=~C-CHSQk%>p znr;YgtmKvGutwco!<2KP>x?J6(U0kdqV@AfU-31jIsr{*>nth z;}nrpB02A5sJA|jdrna$mXHugPSJ7g_(weFIwdh-5YJGj1=ktBkh5SpCE=;_KFhho z*qNLbbZ#c+G0XX8&pBo}*O~Akrv?4BS8_gLIqxyMNFvtkBCwH>Pi2mNHWN-Dz#(U@LGG%pYnI~#boW+SMB5S+OfO&;Cb7gGbn-B`?2a) z_de_`-GP6MJs0scP`A{B_|d1%qBQVdVKOia!AGK7@8;e~R^~6!Ep;4gi9VU~Yz7>Ce3a9Vz z>;LerT4Z_4=$XrhuefHC%V@W~m99ueB{8wemx1h31be&|@?-wZ_~FM;pRY%?lS>zP z_c2~8VE-O5G;!unr?+C@4B-KO8?PzYJE@Y%`O_irZ#0c4Ne<%?+MCQa5-eSN0|?s9 zvwqyoVADA6l@j5St@KQyCEx#ks-iE7yS9(G+tdDrXC}Y0p7%RRYs`15 ztJ!>KW^_7!&9iK;i!Ab)Z0Wi0UamFGU30I~08#Th1ypD9(@+&X+${Ag?DCu1^f}!6M|1)1cT>v>S8xn)Chx5kwF*?+eZHEmI?;V1)hl( zlBOI&a0dtmOuidR_i>us@Ymq@+g^pgb;@V>Yfw1=60et*YD*8jy#0NKzYfJE)UB0i zPEK~pi`(t!>&bU&ired0>;sF0wEUh_nMKqyI^7)hp@~1}aqM>6@{Hk>n7PY9mCdh( z>w=Hz$$QCPdXI3h=j2}o`c|deJ$-IT6@I}hx}Xaj{Dif#H=?$B7n+NIluZo6G}koU z%N7aUu@s}+-0gHEjKHhOc0LW?$(WAX92{1S~rPkxE0xi`OL-Z+d?Ph}jU>&`kht~;wD-v99*B793T+yDZ)tL2IA z3bQ_)emaa-_uM1oEss}|XlO}tZW-f|2k3?n{td$Juhxl*mZ+z}g&;R;*ZWtRr&(h` zX_rLAckJEF5?r(PT*UH8>wCAdZrBlhpey>rws24868V|3s z`v=!N&nYe$?re2Bc|}=1F3kbUa)yN0lIVOk@k8^T^I(E~X_=;vHz8T5;j7oalH9+? z2`_dWG;u<|apgY+me+fY^ZfC)Q%+mA=H0({pF84#rG z*aH>AKOyFV)Pk#>g&lv_mbi;w#CKc0t@Hhz7pNWU$Lgftt0G^t+qvc`9RquQno~g9 z7`z5CqYLg-)TKM7nGq2&B zi}y0Xgi5uzaW(+z{=KUq3014zL1U9H0ay86`OAi1TfTiy8(`d?1k{cRZGhyRXqGql z)Q-oLDffG5pm}sM>FpSg-1%zjT{rsQW3}|wE~sAnB*ZHv((MVxUauS{($~_uJi1PK z|J|co$sMAt$45N6jcAQQY%aV(>vvpJX&pV)4U?)#kOAg&ErVJD3T-CQVxan=1v)JT zrc1}z%4l_k6}k4`RAs`>0$5DqW%6~KKFV+M0avYk3`}XyGnCrP{eFPb%toU8r+X$o zoyyR;t z_&ODB;g7v9!CsP_x;%FT(jSE0{-FM}+$DHU5UlN#=z4;Qp9mz*XeCwH)MR#3XVboW4(m0D7GlY95GEMxr9g0t#?+iM<@iHzl!+-laNe;M-*WDu~s}rs7NK7U6UNiao z8;Z%x*{wA?mn?qdY%#eoQ7V2ox%efn>lK&7kBb@1mA^zNN5KY8mlSy;h02Q@f7hW! zz|cbpYP=rrdqd)7c86u&1GM3oZ9kJu@Xr6eIeatbS7Abx;6{Rw?# z`I5xwCRfK*1z_Nkq?h0r)j)~T@4xV}4p*zjK|P)7gGx*fP7k99A9Z5-Ni@Px!+~nF zVP&<9P;OL4hA;Rg=vgrLYET{Y=W1rcRrD3pkPIe2UK*`h4t%=ys#|2lvLOjuG#*A# zy1cn38WLgUL`d4LJJgfc5%MhSJ^VCAL;cnC)$zR1S0D7%Tf6Qn0F?isu9i6@r#*<~ z^<`?;Nyilv-M_A1E|avKgY;2)v}vL_4mRScD=oYPeQ~YH-aucFY=yqu;9kz43V6w8 znoVhJPQ%%981yGj)8{U5Zj|^K;PBl;HeDH9XW+?JbCpqF)<+r2YhQeW6Xb&8n|*6@ z?fOK8EE6Z^5oUQlQM@dWXXf(awaH=z3WYp1m#>g#BSpmovQ!$1Nj*eMrk;i5UW@*7P4uf8Q6tx6w#iM8&IRVjvc zb2xWmAHcS22w}Bii}PAJXHGiCV-Y)E}!;&&kxC z`02vWr3s|0E3P^~6r61KSUdGg7`QokKL(0`mh1wP+T0Z&mN}`DMRi2C&;xDqN|q*m z5X?!ZcRE*k_HER-R~F?E$hQ7qtMHpl|C%{#R?yAT@Z%KJiQ#O1Wo~p&Dt~9k=-yPm zQ{u5B-zk~Ebuaz=Fz;VJ!3C1OFR$Nv^8Vz+wMo7(i8FcHd|oR#Y+>Ta8elf(m-ggu z(h7gtYLhtrwtDo{7hX~B!sSMvS#7#w3*KeSjXSyA$cZ@nxL_(B>uh}DcG!GSYEBcc z*dNBEOt*N^`EZc)?f-^N&x}=_(e$v%Ae03os@?n^jh~T;oeeOuiM?;sA}nX~7{;F@ zqew--id2MnKo-2KJ`1V%W7wea(g)P2<$g9AIk|ip5g~H<+z%j^&)Jbj;EO({T6M!! zi^%20g`d;$o_3I?o3UD=&xve|QgrU?P~u^ALP#tV%)#F>g5e4d6A*)3#uy03Ph<`n z%v4%UFq(Tf!SsLc0)qKRj-82MhOei0sV#51kXV)!MJ#iN;Q+Ffk6~?qz9=SE=$QL6 z`pYm~b8hZ`@X%yg{~?-;5crjG$5sBgE2~N8pIF=>QFJAFCOXr7|MN3_g-o>4qtW=| zb1!qHOb4T;lW(TX))KXN&t0z-Ao*A38UQ7pKNHofQUhc`-*L;DZcYBt(U;_f{dVnN zpC&`UGF)$U+`U5ucuxv>ulC`*Up_|I!F;(Y`FT9l7wn;`zzL+^>fWg z{qyu&*pHoYR+bnq-S{q}2sPbE4W+*zJQ|;1iTx=cFRJ4<{o3`1C^ONQ{13T+iCI!M|fAwY18?Jh2u1D9@ElDK49ld(-k=5H5KYHiK z6w4rr^)#5qirk}u6JcVB4|*7m-L%dDg8Z(jlUF9>vu-}BeKn)doHm@zFR--isR?p$u;&m75<}ZzGYLoW(GUQHCNG<6H8JjUtgxj z2+B&or|S$il+b^OVDZ1+eKPtGuOLWb{t0pK1?lYZ`x2jPP@*3C?}Wx5U-`>`#8im6 z#69W7tM5s%?{SnfMzw!GDJdm(Z`NS!)v>nJMDLA1jrunvC$zGGe8)*giKMb0dRwoHNsQ*GrH;IF)_(w z#v}}K;#;~^lv?g)j}LE9-9Jf(=LW#ch7Gr1BT8P$k0#YrKmPbIdkRzeH^_py?6zG| zUsB&GeKSULSGt zk3TM`Ppg|*6&GgYjq&3EH1Hp%iDNqV8l1~v#ap*+{2vF-ih6KAS?A-A(eOvz<84$tCMe@05r~{7x zd81!=??Z3j^wHs#_x4=P&sE!-ks%HwZ!Wh`cqM`Ed@1&4I&Db9Rw7cJ^=i-@C=Z8(7`8 zrS#x``$bQTy`>@{ae}N9%5>47O8Coh-};B3)+(lL?Jt}1;UralFCD@tpoBecxdoCI zE_z%q_JESo7muJ@<&s^cXWcMvhr+XN0(ye0t;p|qYb%W-0F^hot~GysD^wL9Q`lRR zqb~icy&{~?s*IBkS^V5_GRsD=sgg^LliWCAoDLXSBvy{=IaM2@dY%Z5{;P2P|7(nV zWWq4n82v*_JVv@66KpV4Hb$!%!2jD5|3hQ+j=#TPj6U|J>M?qX&ctIRu`_q8j36Vv zj{FPJP4$qO@Sb?VNXz;MQq4gif&;irAUED15v=tQBgZ_>djkW!CC2$1Awog}FvkmTod`a)AH)E}c%XWW$398oGwq?szc>f;v}&0}x0 zI&~8Tq{uaMB1Iel$7udj6%^flv$N$9Jii=q8& zd1$}zj`@8xiaf8cZ~AW+^mWr4Dt#S(Z9JS(CUf`U-(@=mw!x3#AfQo|rGMv}jfl&b z2>5xIM$!Lr=@mraSKn^c>7X^E!m$_G4b7asmeG2V^D!TG-n{tvm{>mq#Sr#DyP%Iy zsvlq(QLavw5+%h@w7urr=<+M4+c|r+{zYKcV$3E7f*(}$Gh{Tsb?;O zNP6;zbT#Usqd(=sO^y#64iSi4gJ9|X+@-d+YQ8FEje=$M(R8fjw=5A??r=1iX@E)O zjFH-QieEawXE&IJ^#|?tzPgF}%L#LX{z8&;ZV@|DF-c;;|2ft3;P%RNU2gj=Tq zB4B09$1V2iDmKXGz_xIUnU6G0IKlINDn%m5FX008{nRt!0$|qA)m7e+4-M$`u33uMo;v zpJt_!+8RX!z$L#-hMF3viJ(+*b!}Ax*hkWN14wPq%0&7d6ZKcy;Q7Fr`-e7V3$kdf z-X{(w4zuTnMT)lCaog=%ag&^9pWXf^cE;V-#aqCuvFwj4pIESh!O3}-|1ixJn#ezI51eoRE&O6xMt z{1R@QK;ABGev$n?yqyVLnw_>&pPFlfR>WA^^;i`6Mx*vObnB2Q`#EM1>>SRu9Y8E`E;57uJ_n(MvIW7j+i?mOiIeKQ{^{41) zon}ztMkz3Pck5;4Nf6jxi&Uu<;78rwo@YNhKlZ~LLpZZXL}x1-m^3d8fzJA)FW3IH zi@kB}%WR1j48L))4vCEAP=hj&eQDRLRm#d|!3Ss5($P?oThxbFK89tis=+|LWYNTj z5XJg!o8?>!N$+gwSuM~!ypjv9rHu;s)ydAuB(M4hea@Cv4FDcW#OJ15!%**n$nSayUq<*M2V*d4mdjt9{6tezjIjK@PaAW+fyK&m}! zj0VmFSa8ZEXl;^~qaE9{^0Hiyz9s+pk^hIt#PIb(0B9c}q&t~cjo#UipzbsTu2m;0 zNxfn6*XkHiVv{usKvZ7x;#n;T)sC;gXbQtCQ|5$Ier%w>k5n?BhnCVi6^#wQC(f>#-mJ)7rL|1R zpLbf(?#Qm%G5H%yLqyZHF8X74uc{v={24m+%lc_ut}C_14{?U`t#t2;S}3$@0ryc` zbW;Ose&ZARgwM9;T73q(&u5^k=$UfXeijrR_=#I=$(bxK`^BN$%>oF z{_fm3as6fa&J#>vYX5rWiLeoz2WFE$Q6I6ump+v59C`1#a|o-o|E&hLoNOt5d4yN( zR&Km%dZHuy?$P6?M<=fwJ$^jjSlfxb$F_sYb3FCfxt05h;`;)~&FiRXDXBdRJL~c*AIx_?z_tC1S)JdUmi7=cby-9B z#Wpa8vTi%a7I!{WyIyymR_0In&PSq^TX{Zb;mSvJPuY*KS@e|8(rxnIbsiTYhO-SI z)+YVBz1>vH!cG7?rUwxo!$pwq+(%oDl0AzRkZ73*KD^0a z)kH9)qMIfYN1~-OHWjp)RJ3%Oywa7WuN0xZg=@|KhoK)%8)zH#MXrWQs zRsc%5FWtsZ16Uywl?`$y8jqP-*(3ab;$tcvb$ zABj32xoQ{hUG36r(MDa^zUi2d);Y=816Lz22`$|bElnpL6BTG|fK=n*UEN{)!*?>e zY5RHOuYte+Q4RbNANWZDqps% zE!|g5VH($D>D_9ja~d)Eag8JrWFoy=yeK$tdazuyU`5&f@e4^>O->|#0ahygE z!d+YtbS1M>7c_C~g~CK~H(aLkekSF43y^YaC`YSw%4lt2=R?s_NLVD40k{0jr?_6E zur#QVo%jnH(Y#3Hwsg{|qfBn9$EZVusp*zcoZ&g}TskX!@2JMccJif4|3^VM->eD+ zo6*`*kyJLiNt}{_s`&x9?mYI6){Xp8`)K7hug$67#7*g+47c|o6cRfZc1og)xl;n4 z|6$&BK^!evKH9k@5v|-JS=O0O@bt*ajl3DVq-XB+i0(Caj!2|_>I(04=}Qm4d~V|R zo-<{&xAPIqf(3J}*sQ|r+enL+Zj^Za)?E)H&FAh%^O%3o=XaT?-~8C=Bk$hyQRaGD zWYWTw9381>A^M|{`UGF;uTD|ts!YDGes8XUNQ;#Y|C`riG>{z}c{THj4ObTvaGG~d#me!VHA?QqTXW69T#o%mlL)eKtzOG6~H5#z(Y z2D~uw{GpggyjWbAcscq|vN%uq2V8vgUTK-nQ zd#C4yP3AXT!}$-OtpctLUz&#X+J!d%`-{25zzb+7+V%I)ThepIn$y%+2DC$8VAQ(d z-||0b;*c#aC?+_UTuk0qoQ!@aS=d>eEF3DFDrP=kJP>^#IXhQa^c-~+`zIDe&@Clq zr;y-~bMcMr+Bm>hN?w}8!*sGevGT8;;6Ayu(Z+e}TUGs?4E;SF`uoKjer|so>2KC( z1&dvMwN0P&_2>1nDIOn|x{8+xQ^n*T)5)FOZ&>WOe*xY8u(~}n`@P~!F|$dph_j1x z_GIxuG4nNE1JYnBKBJyLmUsffNHVCb;o1(^6klzL*+kL%|JAtn+^;5oej`MhYJ8iI z@9`x*zF+UWWPi2flw*9G>2C{7X>8lo-|zU?b`)zKqP8%iWaffN4Q{Gxa78284Lab! z9w7!ed7no3ayl}laZawM&mXWplc9}%?<(vn9+-WyI9+(Sn8_Avw$Vaif1x>R!~OxG zU{CRlg~P>}!Xl;3P8Sba((fc^pDZlePE>J!VbKmHJ!{QmJ|F!~YIY7X5~)BoO-3}S z#DT;iV$#I4K{g_q91C?ls1^~97P{F3=q+CrsEmCTK;rqq@zgti|6n!F1G@lv+RTq6KRX9Y6=o6{gsbdF<2MTkR+$y>| zo%j*?lEvE>Bwk4TgwGV)eA^N~PW;fwu7W{lJ-bh&g1_Sde|7J3{Jn)umY2X^gGdV8 znM?t1lFuyt)UIEz=^yTDCpVE;kAK!xQJ$7go8$S*&1vBt3@$7tH$ZYXi{#*yJY6jD)V|I4opaj)QzD z$7;ho$813U-Xcn~STmxinIaa*>`XD4Ei4)%LR4KqtrYveyr8&0`cS&CNNtPgRUgUL z!tBA~LEzf2x}iz>a7(&ypg^iBDa?57g*3wkh^i}@)o^kaMVZvJGFfIo^gM8UTIa(7 zc|XPhs(N;3;!#2j_`eaXCoz{en)o)u-37yZ4ckBtZDW^hZ-uTouT_>eIoc4v^t|84 z`{PrdB zFGHeNG9LVEpG*h1?*h;s@mx1EA=3Mt(Zrhl{+X%5eLer_=R74PkWDdw zQ-!Asi+U+t%&dogx{-dnA^029v%88ls;13VNKjad7s*;C%_*#q8bx_MxBPRJM| z!|Z;E54wWEn%I|knBNRm*4V}%bi{utLRk}p9}H=6D-IESy!SanPkz2 zAvs9{TB!tL2s8v3`=b)mEqv?aMLg1>q_%|rxs ziF9_+enaul>?sY`X^q{%V$E~>Le61yBtm+RF$4%05)dU&IP8O9N)~q(P84g-5Y3n# zlD0UjveRxziB2xz!wx%7@#W$46Z?@W(d2T1xDPUV7zG|q*}Xt z-&V)iqu>67(hVl%>XCBPnVQ`XErA`%LxsnTJ832QNS(oYrAmE`mNJPaOw=_Z@@5I^ zC>N)KP0sZ}F#WHH2%22Z4Rb$PMZZkaG11G=%}y^zf9sO;(jp>&UPLQ;8@mTu(Om)k zl5ZdP&CUZ(BqEFz{@GMTFJ*h;x+Eq$hei7wU5=czgr2%jat0Q2MBx?WTLeF2#-gV;C{8Fc?gUA~x~wk4we?qMR)1Av z;gA}`^JbkkoYP1;uEd7!UV~?6VK>X!#hv_TOa@GtkGuKVUwm_@o=Uwrq1DC@;!tz5 z2ffSQWMu@Ed-kA%Q16vM7Y6{|ii-@a2s9!b3Tsx~0q>*8fWp}Xmb*QT*Ob_k&>^K} zV)_z06T46W%i-v6vKlvPXLN7h#r^mder^N+@$;5jg8YB?&*5kG=9=v12p$aCD_%b3 zvVVY0K`-PbPkM?qN2vE_^R_x&o-d@Bqb9!+-3(iQ1^%`9f*TwNWsD7_BoY(bLK$_+jbRUsAkO*=PqAoS%zO#4Oh}wt7@)42N9ca^L1}zRh3}cIlKGPI z$bg7G$YT#*68Q{BalwZtxIF@roI*8GJBQ!Z1#3oDM~3Qq8f1RJ&>@F-YConb#Z9do zBw2IMkn9p)bqPw@Bq$4h8>0`JTLT3b;F+2Qo;nX@d3W(qN;L7l<-hW)i>vyXdmIU5 zr1g7E)^*tX_R9VGRrIta(9_VZPEX%xy(B$#Kmo((hkod3Ir?FM23J5mL!zm>Tt8^_ z3>QZmSy%gKdzF6384bz)t;0q@HMr&4fweg$I~QCq%>$EkUoIc4C2}emGq@qpg3sun zm%^f!tKml3Vnz>G!BCIe!e&%U*NA%J5_R)RKpmgq9fYuf!gOH~JEPtG1hWJ_fUI3!w5sVKo>O^1NOBNV+~99Prj!E~{vTUmCSoh92& z(~_DV;xM)j7ZxcW!cGEkN?}z4q*MfKyO}8-7Iz{jcQHA{(E5UG`)Tkg^}v+cV(Mm} z0la7I+bu@r)BI%;eCoS_+B09EP#t=SCd`m;LG{@;M8Pqg!a%mUu&7@E>@4hJ0QU>j z<%B^bvw{JA?ILF@lHAH6AqNQ~5C%PLbDZ6@d0=)AJ_aHY@G6FWAY%%DgEHcNtF$1J zvuVnpCT!7-$PP`l27&!)E@k0|Zd;7WmKz)5&=k`!@1Um=hR^lg#*9JExORDO`OPt7 z7z{IpVbsN6nY!q315o$Dl>G|}08jd2^25RYYq=zUi)mMsZ9k?W@OB@Knf=$|{5|MQ z-AvXY|7@w^?^dFhYrcTkM?d@#Tz#{=1YyP?sUCCz2@NE~T!ADY1$wn25(IGyaipMT zEJc$H*@6-gQa#iXQtGS9ly!)esh(Pp`zxr7DJ0hb1z;kCK6PbKS@13+nugYQL8USk z8w<~(7&X4I!z21NbEKfJhedy13eksEU$Mp#4xlVZ^K$Zmk?WAPw@6iq?vxKTM6Q{_ zBGv0EQ%VUvhRhcn{5Am~hQ%?px8HfW=D6x$qEf6mp)au^yz8nF{Ui47|dXz2mD(|K1)dTnwaFNqVQx9bv?B@_0C8T%{@xL<6Ssz1Tu7aXlNd(bh)cYaO zw0ri1oF}H3n}Hd82c?S#F!8Uk*RK`LI}kMQ3OMpb3%>~68mjHZM;syMTYXRRO2KbHS- z{;>G7tGk@xUODP|`YH_iXK*|Q5Cq*?Xcg(+tjrD}t{}qZ)%ee@%PvQJ|7?MQo z&F(gB)nrWD%m7Ld%OxPy+(-v@Lk$2X(%`p6ic+01k1;G~9g`P`^+T&8B{owm<>ltt zZ_3zWaEA@jv0fbia$-oI?l%HDz+c6utsZ7b22;C?#Ak6hQJEya9BDOus(Cv4fT`bp zZ>CO+JA0VQnD()*GbNA2a?42E{R1LmJvg%A8-bYp|5c!r8T zu##z>ix&B33yTCO4Eq6O*s((-HXQpdF5aiDZ87#6&C5gk`)wo_4Jm((kb<*ajzi5o zo55gHVbQSCwBBX2`K)a0Hc};WTIuK^IwfIy7{turNI6N|iQq_EMcPx*jnJOM4F=G+ zkY@;@Kv^QNKu#;40MikcwYt!ttK^5nQeF+e&KbXk{oPgivk~aTuPwjp{QAYKF3GQM zAM5b}ZHd>FHLKxSm{a`vko$*M@#=xRhQW_=NjrH-UGcJF@*d9e(Oo94e4)GUfmN4? zRUt$p+(z|SOdFfHtO7&vFee32gH|9Dcw2F>XgN-fGMTNW`m7@HibkU52)zd1Qnd*9 zatJoo+hY8MMFGUswBl&X;$mf6^N2>(?8rj+a!Cvg?qY?~OjL+_1{)LuoiHlc%-&W> zCaX)9gS4CKr}K*IRQ+z2AEq8HAWgyT4`a_A!la}}w1O7XUOfoaXWK&r8w$_hvngiQ z@LdOGNr7lRWe6ivM=r%TYZRV+lKZMXpHy&?e3`;7_D9kX;_=?7Q^boiC+4h~sI#7o z_3)*q2YXdHTvf?EGK3V#uvgE}TWJZ?P!NeX+4tnmK@mO6CPMnLlc0vS_D7vQeYAF) z$t}}h&NHcYIXXlqm6|as*SN_*VM)S@oFCS_^%DHB=9Kn>qL8-hXQSO0#unf$3i++>Y{Gp&p>V_a zgLd<_c;x|Yo%d^h-Nw{oBa>uRXe>LzS6Q{W!i$zP27ak4^Gi!0b@>m?B+mv1!t#r9 zOH;_+R>dtb%e50HZc#Pj3Cwza2Q-*W-hLfQM?;BI^lf}2-s6G2P^X*|(yoeYwm88g zFs#A`r0o))^sOk?w5qiv z8bjQ)aeWIol%{TV;}S_9ORjd&XTT=pNIsUKvb*~;}M&~kqyZj6H9pPXELF@T9!HlGG?&;y7*>BA9c3VXK7z?vg?Fq!@ee5 zsbkMFLVK`$s{0L`?drwh2(1))J~OZ9$*P|3RIkx(-fQm#ojVv8V7eFOvexgcMppe! z(smucvejf++pZ7)3i3fyY3Rs%xhH4MwA5VecnSAGRoRaVK+Rt8 zc=``qU*3G>CH19V?x4AL{cL27fS+}EcKO*>{iumFaeL8^gMdBXlu( zm6FuWWTZFpqxs(*Ip4)G8Qn1yk16{7iVuY1!wD9`mrmqK8BpnyzfNcMoR2&wRz7ibH%bvo7a2LG%$&sS)J!5k77XZkO`s{vsz^gSZr)BDknf&TrBAoN9QbVby@mLUx?z< z!7kQ3Q8P=qE^E{q^t+spSg-&gyA>}-xfjxHjUY}?Xs+lI!z2zdvZgm@k6YJhoXH}M zSHayB-GSOAr9Xfb&zBX~9pVN9sHUiiMKee1C zp(3@d^>|wn-p0U5LT}fv^cF^el(pjZt9YwZ@J4rZ7=+f5q3Dj`&=*WAgF9Q*mw^!H z>)LV7-Vp2CdY)x&fRZfZU+l5m=o}+bzvbS`yj&5?)Is_*WC~!Io@twon@&T4ywsNZ z>3yPa)<=0LdBN|zo~gg9fhoG9D?p~z6qQz7Ux;f8aenH}l2(MIRaM#B6z2yGA&vco zt=SPq2?#ZI_Nh6UKZR3pNlJpGC@lYd6g?}mp~wCn92|tUH}o)vBd^Ynh5n#jY|Lb= z+E>x9{=}N_4$l~g?xNIbm2$7;W_$D`9@u!Gg|L7at7`T@bjK?2B^KOl52_H@Z0{Kv z7t&GVtR_p>NkB{2c4kWlIhNi~W?{b-*AhXhC6;HguqwzWF7U7S%TLml`$8ve7!Q}b z9fPO)maX|MYTge3f_^`yX~eM;*taT|dZU?D+;8@p+`%p_qw!N^q?14vK*IZHlyQ^9v;VXewi&?3u!uPUNe9A4+IJ- zDROsLz`YxGWZTh4Y!er<&6 z$%MuK)o9srpnQX74O^WLHSZWex}fHW649WE*@aC3Tn~Z>3{;QKivs%hd{o z)Ux1$D#O60%|=Tbv)72IUQZ3t(lEgHmisU8C4Xc>+X$_5lC@zgj+v6Q83U5R(%BJW zf`FSmqh&)zuwmL@(Q$F*wZ%CvMQ)9sk^c7>DB+AfL(EJDvcqLy{4pj#9TBKvP+1aM z?ydA%p2pjTtY?R!WwvRkk}4k-`@9VEJ{D3%(QPf3pGn3tpOa<2O1>~yPie4vtjIFy zP|{>?Z?$u@eD(w4>S);t8|i>5%^e1C-ue++KC!ZjJL!?^4V!P(ehP*LMe4 zk@}m|uT%soPv4?wr9nBGJ#KXl4V)nVtCv6sTMed8&nqeC$}ZdAD?)3SHAi(!4N8k{ zYv70Va&`-bu0vVTZ7Zr{G+Dw*if(IyN2+pSeMPtRIg;z||Z(``l${e9sGcA3xo_orOjE60$7iS+bkZ z(4;*ei#dF>Y*i>iQDtQIW8U(4h3DfdbuoOI>0FHk!`G_n7~v}^e6?K!UtNwbc}S}8 z)fVIHtcZLPz-Rctv=HsIkXMGHKmKQ_k0@16RGD~YZaWd+ybff+8Z-$;A{3OVCar9g z!cJ3Y%FVILMxS1wZZB+g`6vi>P^K`D?I_mFfG;)>)Ep6_@qXD6A7&BMcp&(|SgbJm zH2^{@gE9+3Y`3>Ps|}r6?Uzq3w%+|kMaEZBHG@^6qs**7(pt1^7(fBMpr%<`4U>o_ zt%Af$Aczsl0{Rw}^Wp%mNp$rB#r|3OZ^o>HG}aCxH5c}d(vcauCQIf3`8a0)_MC28 zWV`Z>V0WJ{tRxr8ewPZi{~c>Q@DKdP$jhRx;uN;s4=YS9bp^V4urR<$fgp^?-a4O9 zo)%+uhXQQmj+S*(pxK0<7`fG{MGOIHADfDn$sSEniFPz8hi@7&snu)!>>*cZPkLo{ zbeksJ7~~bkH<>qB9pgJ&QqgVrIxYfXZ-B7gBov!Kqh-Bi7@Hl&F`HQ$&7Nj-0?kT1 zXu{%8Wm@VPrB>)&s>e87J{V(IGHdQ(dIwKcNL(JcnO6cBI(1*-LxbZSBa4E;mYXWa zP)`zMEjrL~vF5nGSP7AKrbG%d&=R|r_BFGFs?`pg3YWoGCN_XabDr=`^8t_*bV;r+ z5w;hu2Vxn93>`ui02&Kh?aB(a2T-XPsuTcW(R7BAY)!-aBv8w@DSuQx- zpJGgiIc^u@kZtG}q3PB0X<80hXd3>%NZyVzO(R{SWgX;Amtkx-7->%)9Uk_6j8FM; zct~4@ZK6n6&K$xr`4Y2zZsOpq;EZbX- zsORhJI<4#J4Ft>QKi7^xTXN!Pd1LKy0th=?&3GDE_P}YQ_$4_a&8Kirw04p_A-b_D zdP^m`?7yn^Rb?u^Ds}+813r{@M-Gy7aFA#lANrSF1Ei96g0%X;ehuhX#!DLlf;&|p zZpMjdz(i}sP64VGpt1=!z{+?C(LGhsN6OJ_PxDC%{2KZ^WcfqSw9(KM8IAA3v|{U$ z>AOa=;hBW1ed#M{sKbEIl56OIB)~y-*J&GsIwToT)617!^?mxaz3}#h8Dl9pxahc` ziM>FWkIEr4QaM~6`$f%0l8#IEZ47)M%UOYA)HW$QbQfxD$iOq zI%Yt5LG%jywKD89DJ)W0ze)#OIknX1Rq8XOAFop4NK*V->7bAldv>%ysiaLI32+M) zb1{`mOFK})6^A=ZJfH#f6pdrD$PePp$z>sx7%o#1gPXUsqEBKRlPm6g% zy3if1wfV6=WE>d95m+MZ3WIWR%mnQ!O~P!!=q@-%1hu=xFtKA16Z=W1)8=YEsJ*lt z%QIQuYCr%h&N&f6F;dkMia*ap?fy8teMpa!Ip)Lz(c0&TsiI%^5UE#7hOPQ&Nh>YH zf@$0Nty*hH1|MK>GDCA>M>-wGRkBgZ$V8vy4;9_%hg4i`@VoQRiAD{`uc7Jp&azah z9+q{Yt55?oU|_3AE6+D~ysDtQye_AmCk-fZ$a0c^(j~D$G%0 zi*ky0wQ&mX7fst_nz3^zL?dSGXqd1p57A4{yQz|Qv+`0U{OoQTs?(OSiLmbJ(D zB$q12qiq#yEy3J7fjW(QKigR zDdOkg{W}Q#>{Ah~9#(=6=faRA`?-?@TIUc}iFeF0Sr*RQg&p2^t z2r47D`#h>X66e;fK~!bMblX)NXv36&SFrS^-vbn zRE(g;pnxXan(zg4F!xm1pg1jn-JlSNT6Ye@yhPA$0{|ln6TTS!jk<+xO%x51Eo|+5tEf5qI#(89|nyiyz6mrk-Q`qVv z?n0v8r`#HSWh3`-<#vJs%rRdobV!6N%c?wN)rgqoAi}`XjXkTTWVF<#c=Jz!MG@Ik zWHEHw{&hSXo2`zJ<*dkW%-1+4`8egZ9>TVej{$!H^Jkl0zz?eVsiJnuKS|3{rAei# zBO)QhPdpPt8g<$DC`!GRXkyAOKp071U^1^{MLWLUn5&2*oS&z95}7_d|pR`3VBbj8Gp}ZF~31(G9BG zMMQ>A-%uRSjUG7TCI*Jm)gBN#`%0B3q!+Z+hk*Oof9l$)*b6t z*24l3+Idc$8?$@%3tOF%=FcFtPP5rI0ELMVjoL&=Jld?ff~^x6Yn?A)2YaeKpI>MG zBI`iFR22A2XV!J`F9kizEqYM42-iDq|zi$sd$fB2sXhIAQHrAYcSz z2lTHflRM6220(=?jO?8&)Qe!r8RZJw28q~YqeUad<5-tk~>8mD|nPO!YxzhKS9abEbW%kF>CjpHdC0XUmm<7t(VZR`EM3wAHwc zwv5q45Jv1VK@2F5`)jIMZH4MPpVe%HVpf|ebD&{5X0<^I8mkSdhB2ca255}4|1@-Q zRvT8*d>GWHRjNUIFlPVHD7u^Ks{F+S;4v(v(?H{Fe;?Ws9m3)ZeyhF1!^ z?04p|dSIE>`Hql;OCcJuiZW{eCk%rP%7JKnfk2ot1(~^*k2m9}#^u6Ja!1!dwmlj@ zE--X{FYGu8+aDYOg|KgSHr|F;A5-nzWH`u*lr+MUPW8wNwtZeFg{y- zeBe)${IN$s=OoiJ&svY2pmzm1Hc%fN@ES=XS9e4;dxu;AQcCNx{a=Jp3 z(>hI^e_Qi~B5YWU1s-*vlfr4#|3{Z`=D%f5$7)F^V%&Zw62Ys=b*;7}do`X~fBGzmU?38=|q__U4BdK@$sFu_iS z99rS+fZ==-lwC9sHkFRWo`{qNRV3Q#MEi&nZOtGONmlZZ{Zg@HAreMm8Td+v7-1pG z4mo9P7o@(%51p94^zzseR-d~%Mbzh2BxPuH<=SzbPbr;9*O}V}2t19)FkF|?(mDaV z-=^xm`MK9ju~>YNL)~K*D7!)(1j9AMf*hk{mG=xqqc8Q%QnV2vFi#B3wE7hTGebaJ zX!g4SBZY3e>O@(-`Fb_T*JiXFztSz0(;BKg5SY+a*lNf#p(unAp0w5k6Wbof#n=ur zn)uOz*u^0BnFtzSCPV>GBc51hnxz@_Oz#9(N)z<^ zV?l4U_<*1xvoRIOj>Z)O&?8ESA<&60(=}R!FbWlcnJkn3nTp0s%DyY8hX(W77*B0d ze9*X4aTkKfkpP*}$s}3Y75*`vvig84@X@nzQJR}Q73H)>H@`O~BCIt^u@u9IQa?L= z#w{|I5!zH@J<&}H%xuWi#~1d7!>mhS+3!NTM;JVQ&d`AZ+J(;+A5LTUNiI=jK-n}^ zGos%^cpt)+6V^nvhf8GV=EgQv5$0;DXVB}}A=GGlif%y@&&M^H5E0eMO}xVuDM4OCRQi7q7j(2c6* z5y!31iAtY|LoOaje12B>q$y6&k1%ED7)b3W}6 z8ncNk&X||k2g$Meq>0d!54ecmhdLXR z62d%XL=-awhLUjR2iq!8YfwMXEaiiP_E?iMh_g~bE|*q zd?S+rN@;iGH1h`8RjiCtsdg8%@LO+{Lk(7YhEbl0ThJcD{j8Yje~DvZ*N?e6gK4^0 z*qU9T#_))Gn=ZM>INoiE1D?3e6H&0#Q?pLwkLK7Q!mn&vV~@zjkg_RGaUyGHd$qQ+ zEmBF77#zYiCWaVQCa}F0h13} zpBCT}6W~4;4ER3~8EoqyZ0&1VI_dL7ky6+W#)xXvjKccJk%B5QgH{S7Hj0dGJZn+-(A)t znOKiN)MVkvQw4%{ftMo2)^4JBb{XXc%&;`A5!;+>upHT3&JO)Y;}%3VvA3wb zY=;fRxT`A+u(gMS!uF0lEmt3JdsX=#W>8E0}KuCtLaty3&xF zB!+wEvx*M=1y<3RSFwuk(R0g&EPa--z0BR3xzu0Cs;cB-tTK3UR@qq2Zmgo9*u?n& zXa{39(YbNXHvq_TzF2Z`Mt0+>$yGVDwSiHDJKPEhN3-yQnNhONR*cG7wW1Ry26BS9 zBEc)fmvpZVP`B_qz^}Ha$rX??Q;tjWRG+wIj9<8A17M3=EIPI;yg9Hf!vtwd25nXj z>SK({uEZ&XAFz+vT~t11eoE|=6^dhiX-TwgtA7uwHCW{#kzR#W>N((}6dK;HlyIdP zt+7+Fut=q(V}dbhGc@IgWwd_jhEC8eW42mC!~3k^X#A`OM9ph0v7Ja@3mWEFHEdJT zBnF>MzZf2=bS{};L_5lmJk0gMGn`_yy(EHzfh_A65mapGB;%pakq*051a)I!4>!(S zNdE%XY0q|3#-uvPMK!tAWWQiaFcu2L3}azgEhos$i7g0lwbPl{HqHOOBRk8c=kaM3 zj?Jz13RiJZiHz;lnk6HE+b;%zRZ`|~D1gKF+RuDZ&AFr8zcklcj9YM$VdQ2U+p{lt zJ7TQ0Lbr7QvVvb3nmoth3 z@a^^yI7W&Gs_ar6js;7CWNmGJ2hE9NCi!KoVtVJw%W0+HyV$3c3dhug&P6I!#WCCk z%DE-Q#k>A9G$a6NRvL!TiMUzk6k{9LFeIL?Bt8jw!zl)raf-nqnbQ(D#V%Vj2O$6T@|OK<1rWd=W$Ad2oJFdKB|Z;8WHdp<&?T8H+(TVT^9;Cr)aciPekJu8*|F= zd`@YE0mf%kObUiG4B;*&U2q{qZi)q81BuycUa1$zTARCoTdlIHZaC#fIZ5XWGH-K$ zqj7x{24G&J(5NSYA*uqp*4O!;Ooa;IX8Oo_pJtKek0&<#n~#ZG+WKFfDr|Em_tP80d7eW zbho3<1LWs8D!Orazh(z;_?w`}@j!N|@T2p&n-@*7CHL);h2%~*U^ z#2|Ni?hm=SbV*6%;ucaj6sl_y>oi3lY-G?7nO33XT%|R0GFctEaTK&uH$5_BbswA4 zrJyx!w4f7#Bu{edkB5*9$DW4pcB?-rkwAl*tflMT^t9YY10=E4+Im^+&fVfrvpEDZ3fM^BcFqK+BeLoEIf z0NNU-sx$POg+so=;U>QHn1VHemoG+PrLA__;(}HgFrhhkOiIsgb*3ey$fv}N5C4Pw z`MSDKhr}x=6&%qAxtS4E9*hSS$f6Hst8!rna2>l!acv^Er|J&#ey&&5J4(wF;U)G* zxz5}#A3%WpkXH^zrw1;1zG(sYk^DOH4VPa}K6(lHwdT|&2`k@92!IbNw9b}95M&a& zT)QJb5Hd@qisV`C6;?u$TsP+<(T-AblAxbqlFXt;`9nQImUUUKxwO=KU%HA2dA3H^ z3ffkXPWjzzquyxLiTqi^xO65%T+IghWIQ*ZP0||`5^t_xP$Ym@(`Ck5ShPtAveX&3 z&2gq4?0jYJW`tzVvUQ{rVMWu;p=q5LQ7j^fW(iDkWat@Gl9?b|s7R*yR{gqPG)XSq z;MmM)a{baGCCNTk*5DpZi30*e{0bTf)@KqJe-y5ha0CiEs|{z|q3BQ`u&wh#h$Or2 z2)`5{n5qUym)g1r6!kv?idKW7!C(L%^uHyvtWBGO415@58it>T5~(Gjg!O`pLels% zAkmRAQ#!PXY5@{P#&BRGLo7G}lAntZ-H^*L7$Ugyz=lH)CH7-Lhs`ubBJqHnLU6H5 z-UvfeMorDT3R)#-=0t?iSwYSsGxCiH3Fhhnm-j-#*33mtjfF*`Q5Ih$z1C7g54xgT z^+BNr_3Dd^Yw64yfgqYBL4pZs8R#Rd4Bi`E*JSfLv#{ib;6e)u*E~Q$YHXFs|Db=j z@GXH{^AKVDJh&;h`B#Qmk^P|km1kZGv1Yqck#sxkmpoE@HIrT@jakhXZRi~c32N?I zwtna!P|ZH2i5MwAQ~8c>2JGSNy4M2~kNB6|ZM}8_0rxzB-Orv~G`?n0)VJ%;ipHR7*;$Ii;NPDH{adW5LCjdMrayC&Fq@D-J{bIr}B0=8Q@* zbuBOao%Lc`*cm}eS!vzcqX3;=PDk157?$!xzI>V|pCKqLTF_#kwiFg6`Jy8>JPwcs z#{g32aqa5@j+FA_kxeW2sbWnXzvKbs_5PKl4%_%@&wizO5eSG58Eq!PNro?xkg{O^ z2BO4{AVQPr<4$&6K;F;WK`s}|`JnAt*3~qhT;)?u9?5ACC|cGKeaqQflW|bUYc@-t zQ#FOORx#)tAw{U#&4!_tP)0s`*@!v;#j-%)hwC)4>>+~nTrmoTr^sYY)BAq<43)<) zI3E9G-@)w4S*%X&IX*9}pKgz{gB5lBHmdzU= zwb`&4-bNEcdY!bZ5f+14HNMm+9*eG1k?Fz-)x#S-s+-%BuEQvc`z3}6sfwa|0|8P$ zH*Y(SWUz>x>4aE~Oibqk`!$J*M5e@2Z=g@zr7uR#+20QG$mFbPWT@zfOqH6ff6GL& zcb6Wb4$|tBw#~nFjf9!7)YN6Fv^!|3j5Dj)?+AJAI>MSJ)u=02r;9bseCwWW%t~Dt z-UCly0z<>@IEX13h(*b?t4EHc%1vkmmTnR(VsrHv@)0Skwm#V0m{f}Ww5K;{1%JC> zp^OPCmC<0U&K4I$u@Hi#bv)Uir0x_paFEPgE6#Er*Jw;5WwoFrVb_Yd7gxs)a70BR zKox{iH)nk3iZ$X_9|v(PUm6FgpqS6wT?mt+dqr$K_~r&2+3Xq-LN@0J<@9E+rVTuu z#$RqTMx?;L5+*@qB%|w6G{IE3if?rXa~2~)QWMPfllC*~?! z7@-&f*~nBp_dOaiRpJa{my3vw1NWw5fr8SaB#J^M**IoqZ(OkywXc{q2R=(`(xb5k zGTonqI21QkYC}?Pwwf{$dd3$Fv?^z1aE_sI#;g(n$|Hx31JDAVo&|%}^HhP^S!`5A z*JXmd7>q>%!1asHtO_JJ-*SdAJBeKzWxj=ql*_uuWI-(m>9SwB4|*4$vRv(LDz{n_ zPF0EBiJjQuT4Kc(f7egsOpqCU>H9C2(~Ru#4Syf*znr;*JwAF$uamZNZ@S(z&EimK znrPmAh8H|}0-=T~QwFMNl;sp!4tJ+P?shUf;~ zk1i$_4rkNY;S=qbCHC>^2Xne7^nY@6SHij5HWnN}p=u@7ua$knLg?1;^~Qy~Icqk{ zmEfo0J(=vvKOYUx9$)&s?Bx)n`rEiO^!MN;`>RJn+v#sJozi=*Z4+JRh{{bpnFNT| z#iZ6wyb9+Vk>OqN^a}+XC-a{1wfV#i$7rZjbCwrQ-&_R9Z{u(yw` z^GXx@#1Sd0U3$e}i4E;x#tc9t430NeTxZQwtY!f2HN_2IE7(Ow7ZruU{=MYiOP?9nciz?&$E zw(0NpJm-Dyy_D1l5byhOKA!V@pXWU9Ibi4`b|l`~jb3iP{6ty&Lu3MWf6DcpZK9K3 zwq9#}y!$x2mblRU_AUiqT|#7%q4ly5O3V6zQTwaF`K#hwYy=wKc{NUQ%|mp%&O_TQ z_#U`k=6hQ&#J7hQ&ca)s-8vy7t<6mRG5DWb>%I0m?%}?*za+Bx)c)%IZ=E6Z@-P1% z{th?(RIpY&8sPwq|M#z-qw>tD%^?tuRojEk+uQi>_O1>715vv+`3r0Pzu0;SuIz91 zoKYuEJ#I~bXKS%T?qBh^quXo!L5Q`-!nqrTd;>?FI|G|5$ zgAYJIML|DNXwi={8Kej;51rv~S_a)IGkPY<&B4d5gVMWH9kA-!;5)$`y}w9hef2i| z5XyhQrF;&w4nAoegp5^95BsP)?W(gy$0Kxh@L}uVqt-55mFua@nu@S{s%LO;1b0`n zJ3{EtZ!n3-NliXacgmR&Jy(oAio?rB;&1<5_i)pFp(Lvm*Qv>rcodt-iwd0KbTenTL-T)@Dxp8+6Z^tGrlwotOL8{@Y~Wa<KOWG z51biXxx43aOO79glaAm&zwxm3hT4OYuAL;$*&e{+Et3Ki2;DP*+#}tUsOm znKLT+eb-j<``^6wLVjN#^8Rep|7zR>Z??w`)o2_Xi!GP!1+HkQ6--!kf>lM5PfDwSGz0zH+p4Rl^ z*0dIdaBDXZ93@Z#pktP@Ism%}z+jo=js_U&3jny+npS4s^yFrsp#VKVOo5cs>7`H}NwBAQN>4En|4yBFXhCWxa1s`E;V z*mqHhvG{?AZuD(%Pad&GqVGN5VH%KgDcEzzq!r*RNb!=>%Ibw6W^`o$04IGkSTD3#=HZspND@VX>4cAm84QH+9>1LPg(1F zC{`|$3`RTsEMd*DT+-RrLbO$3V2t;sxsBx5OWXS?6HwGpZFApmnF5syMz7Sc9H5ms zN(^T!1*)964lp)!mPoV_){CMMjp8db^6k12Vj`uYQF63S>}u&5o}T*{DaGClp!E;{ zC;F$#gb`J>h@-#6bZwc&4f$(TQ?~7+lJ9rE8|Q7Ugk9}CUuY4!s$U-8PW|#He)2;7 z;`+1kDu#RvK#t=Q{x_QwXp|Gmxf(@9Lys2PxAtGAMsZ@RJ~{KdsZr)bqjar4pQEa( z>eTfKbjnKv4i)^tJ!}2=<=D!x@axBG7qMjXZ#9;O(*pfAm3QeWK0!|$VBA{R{k25L zmwcnawc1m^K*9i0tac!a8`y3*+|RY%Y*@8+=0i1^P1nu#=z2C=2cI>xm9V;aoSK1* z!_Wx>5EwmC228*xM~aHmnV1b{Vy1QQ)7HVy&>1y6vqJ`5E9Z1AhTH#(J^z|v!s86r z{a>qTHS?3e1%5GY9tq$01=_^*?gY$bOLeEhrMnS+_0877?Jh9F;$QvzTWh^&ur6e% zxzah_skw66qEpM~323ae_CqNei65PTu&Z;auqc&Iq(m#P4%a%xxXcIzdifoO87C<8 zCHF3RsEOGSZ0J8kdqIVArT_T~Z?0?nRp75dh9RoB9;&h;qO`-=c27qMD3N*^8< zMMQuq0yBia5J{0do2B=0WKo9iv2E1}lFn~_-Ej*6oku_yVp2CiV=m0|eNX^MR(ZMd zvu+)Q#hxU&uoig&aHC4MTCd{Ds_tH`Bt$c{%NPH@Z z+KtFjp7|GLeu|wvb$u$MDc=iELRk)x2u~sy9$ue7GSbO7_!%sZF9CxyTYtumhG@oA z?ONk|IueCtE4xqSZliy-b%Qw*alm784q5^V+$W=&JKRyV1vc?`a257))|B_=pXYf$ z%#_a!n$O)0SJjwo$U~8z@9BH=7O7R2XnAyL7Q&lQ2&+JZ(S!iL%!a==Le;@a;HkKn z8t+e1d#OtRqgK&l-|Ow}kj_{Xc#i9$H-Y&K(GU!UD3W>@-pesuo}jplVka^@#q~=D zKFBk1 zinTfu{o}MyDYYiWk9KQaU7!ks9RYzIjGxK6?<1hv33)%Q_fq39*D4JT)ueXc3413LaN1wjqx#DjyQlkn9T(*HBW=mEl$8J*&a)w{so z7okE`R*ST28$*nMz`Stqbc!c)sr>hY41Av&8GeJnoQgtAq3mr$r!7%}!l&v$=-0k; zIau6NBI|kW7gBM4bD+tUF6PpSD`ML;wTz}#RT;xLzWd7P39ff%f}9}}k#GZIlcHJ( zCyK^l2M#hT@YJJ%;Kb&Il`fvL`Oa1@WH+jTp3BjMPM zR$f%hks9BN$Y2-%b>ukHK3qWX()PS$lsZsSeY29;_7M>*swJ%juGAVjW)7*mMoR~3 zt)QB&w1&#hxbDFZS_iMFpb(=q#jA_xWoE3`g_Q$Q)W&ljKy-ZEIC9ssv0L z3J$>xm87{dwSoGsta4}Fr3h9o7EQivz0l&8U8U{{JkH*%Y0Zl6{tf5E#>MeJ0r7me z?hxth=n3c@-E`u$jIo%)-xwA@8H(@UQ*cVg4bA98-MtEf&|}*mw)Or)oQSiOF#!8K zAG2HWOQa0TvjcnBl2reRy`jKe`9R#AG0!FF;>|Qlz2Tqo*tIs0f=xnT$H~ahD~d4g z0FU!4W21jkAd?`fU1E(W(Xnw@4XShG!;p+P)f3f~1n7IpRR*_=;?`*oyAJrc)z`~! z=9&}JMS=UADP9D^bQX_?a&bDT@;CuxGz#W;wI7P=tiBdpG1MxXDs0lpG7;)NVPNS4 zOPEj)fQ*U>U~T+=$`R+8Ed%*c)((MNFmF7!SV;+zyTW9m-s1fL{hM&gQ~0 z2&X4xPW1x3W(-_j4yd=P;}(6(*a7JV7X0+ z{&1ux0-S0#EZc}1x|!L1IsDhVpJM^kR~QA=TMw2%eMP;DJZKO^Tu@xZEkshO^YLd% zXs|I&wX(Me7)u3TThO68PP+gb7}S~x4>J(tSz5h{QVtnVkX??epjQcV`^xJ@5x?`> zVHQ;9F`e&TpDXrY;2)q~R0T}0zKWAez9{l}#+{MIx9!LKHH(>`Jb3ykO z-M2QE#|3}m%YSs{cL4jLm%oSTBBEEg4r8#%MTara(A4?m(*vh|IjJ_&AYb%`RLkQpV3gIL4B~B>W6N&4mrX8YvHd)ywpJG4T_`pqe87! z+tx2?zSlZ*$(mtrI%?*=%XQ_V*V>htY1RWa*Ioa`AsaPq4?mT9U|F#~Jo-D=?sLpBvBN3oz=7YIR4i~l=Dr07 zW567VH^CoTj17k(4#s&k$q?xGgVrIPhiTl>I@bJJ&(DDCvT)r1ETy*0`sQjc!mzkI z_a1uEI`mW=gq5tQ+N0(BAR5@=3`D2R)}d!3P`V18q@L*-rp`hPli37%mBe^m2jOAS z#SzB(iHT#n6HCpP(1RBRTw9?3nlYm>9*YJZ8>l=++2!V&e5FVU0l19`aN~#5S%%om z-JhcTPspluQd%b^{o|BnJzJ%!#;ERLrc7w| zz4OoGNM9{c<}6(PGhBAAe@(%S&TmHUr9Jzb|Jw`g*)Fq5PFDx{4tlpf>gRq;YDV}8 zv$l#^8|Eg=+87srGRn`swU7S2sP5;g-P0QlU0AdyTy$8pr`%@Pjn$=7*H;_g8_VfU zPlm3RTm*~I4bJ~!>)`u@u385#=VW9VF@MvfhLx~#dcq*5t*7;SE#A!Hs{}kKd=xS6 zwrKKW%tD)L@t720(c;D^mc-iwd%EC@`*BCvuH#m5x7F0f4Itz7e5A~Q-eeBc_>J)} z(-~UFWg0hzPkrP|jmGc&>Hu{4@9oMF?+Z;6bO(nAJ>}`1}eNW5hA* z)hKaA$`PTv(7GZ12dlYVN6?Bn;n|!WxCGAh?tCzN7YWs=5x8m{e6Mv7YyBM3=`=gX zRc!hCk{bI%aQy@#rxM8FelFFsyvg&>O)Nzpgf1w@L#P4D8KR#QIa2)$q#8bD)*SkTh88mwA@7)y@0*gCk;fw%~;)m$^hM*@{MgKE$) z={-lXQ-4_NFgRqio2`TR(>s_?xo|;5jh`&|Q?6&YPIsPe%dYoVd9Fp#9XP`EER-yV z4ZFHjwzzIHo&L}`<>s49jH2QOnOy45^JY9ahK%ftwFO^Q%4r4MC$G$6SC)cb7H}4t z46?bA+lMD7R{S*w2RVz9zow!*j!eyAP?h(GyEx21jMz!VHgs1<2D`w>@ zv5a$oKEtQf+--!>;)pj(5Qlwa9T0o>Ym%=J44>nnR1;zsywXe25eTCWi#geIHAOw|YRS}$z;}!rqT_2>>MIM9b{ailqaw9*&j|qFiiGe7v zfr@IvJ{TrhPau3P!`ylt$HHH6b-J2-Dkj{jMVx;x@H@)yBqL)*K6)tgaIfjfeaCc7 z&;;@me83{vA_KT%XE-H5suRrAU*&hp<=BG$YGjXyxr(k=K$W3`^a_v0hoTbX;*eL;|mYPUTBu19}_~tKM~*Wf^dNHy_D3^QF+PY3$#ebru^&x>1U|9pdbSY z9pbljiM3QOCE8KqFkds~WyY6|XgGDb;5bCDWArx8FA4KP1k5XN(^Vq+QYsR2ea`p{ zDWr=xL3xxYPo{V>%f3K~tlJUi=UjrqzxPoauSCZNvvs>dJNej~PsQ8kyqDamISO&7 z=pa4XG%U{}uKu+y9qf96A6!a3rye3I-*CJ8N^Dh!XQ^Yvyva#|oX{&L=vS&0G+>n{ z{dqFCwxaNKCz{`-l`@hqNtiH|S(x?N&|MC}h{;*|%9we$aqq1D3uET`f|=`J{%J?N z{34kB2%J9t{q!gQ^xu7X8k`d(}LL01EjPy4(EoY@H9vATiG9Z~6v??E}8%V_>wYa0Kq1HQ3N z_&SGwJen`ESfxeeAU`Dyt*em?>_0QeE1{7~JF?;qNJoIsl&{eeFTaXn*Ryn?Li-mG zmn^b2!G@j>@uiB5Z^1O7ExLhzzcqci3+TztcG{G=l2$_8mg%qUI7Lv^>RZYNdXMP1 z$QBaVi7=Xz33AE4YlA%g+^z~@)CM(TbU9qahZ9QDUAvW%sI!Ap$-^&^X5pX3mFHJDkVhJ(MZ~B(5fd%YlmyJ>b*r{ znP1p}+0hp@7Um+kDa=U4mC?-2*7SB4OeGz-f{76|fuU$J=~Q#d?$-2st?5fTxGjQc zAXm?8i(Gm`37Ji=Tk0Z=NWbG)ytu~&WXZ!^D10O+!yX*Gdsr2El!f#)^^(NEmk{Mk z5*ytV_;Hlu#gUKE^r0cO5zr-Na^FW<1$c`+pNMHe=^f{jQ>-Tf6Fo`x`GD2TftpnBh+ z#ZQ5k;a#d?NG2TIrn`l31Y+PzacOp_?*9V=X%!}}WbjX+)<1M??l&cW87v!Tk9Tkl z=e23;JH4zzb#~P`C(MwShl31K$%2@hKFysKOwA9XG9tOkj{$?|W}f1x7pl$WQwpgI z;wwQDH8VjYDxiz1!iP~CRU=fi3A}#Ugw-_|^+90fHNZPTV+ii=GrDcSQ`W*m&JTv9 z`d-3}<)E-Hz{Gc2Mj3it#v(d*DHogYz^SaV0J%q#D~{10$Ry~uM5t2QFY$rk3a+)( zi9Nqz5FQ$<=;s&DG^EJT=se?mKy#50Jj;#xkvC?&S< zvMdqrfx*bG(i_}x)=}yTeh1mcv3$+om#@-Zv&GMPc)ZQ~=r?sJPO2Vb&+a_sMmY(d z|2AXPoqS4}o(u_cr(?!lxMHVc=epO6bN9|%_?7yE8L50hzWxXV`v@#g{j>Bhul|oO z^e^2YO)c!t+1O1A;N3|If{gHkxA|XhJ^?;eDEAq%QkPEZ2t zPyYR8xVpSHh?(p7y~Tj&&cGJCu^Kmm@APN22}%G^d`_j8ubygRCQqbT)HqoYmoH~?bJb5h8dECF(8sCq#yjTW6)M#j;tD(uA4Fxez zPDB%v(L_kW&TJO#sC^ED4{i1J@K9xIBdB_=&bs>%ku-f(es?uL(P8Z;&JwE!l=ibz z{p9_3owN@(b%V0Cp~m;gTJK^hyERTexlem*dX4 zNPdKCFgZuR7!56zh7NZ&R2cejG)2!xvYj>|T$Hb-vfp3rtQs)Pcfhbv!7y3#L5kA7 z786-{c#63L+6QKF)<69Tq&-;I2sJ1l2uuwYOf7c~w`l64 z^bneQwd$&K-TAQ16iM+jErflVbsN>pG2wk)*cc0OHc!_<;L4=@S6H=g{P0+pjW}KP zd?I`9qEXf7$-}^&UpAgR;xKN}^*Os{*N>RFd2WO=xA~xfPJ7PW~vhI)X?=WTkr z%de|BNUnz*Av&(jNaxB0f_b=|rkz5V&>sYY%fhEkexZ?P`7gbZk$JFzA zjO_xV5Jg(*+yf=gj&xCLn<-antVA1~)y*@b<_!#CdT1Hk)^~;2l*OaJ`F$eguR-^CQt1p?xGq?($RWNc1O z?>9-UD5xgP*VP`7F2N)DN%6cbmM6iRHJc9nfq> z%1ch#VRbWfT(cXsmMY6yliuX&m|LMul;P$K7})etomEk$j#)XIW;ZACLI5K7!;E39 zXWODfXjnj}2yO@a*xk#kMcS#Ju9T-GK`_tV={Wq_(b2lp0W!CsmIPd~7tsToV*&GF z{D#F8na!7df%bpd7Xh;ar0S4A;#IMPE%fp?GCN|Ebx=#w++&+_>5gOppZV*v5(&K- zgDOK|P-S?F^j{6oPuS<|D_@qeWsnTZ46Dd{al}gwW^Wk;kr!e2@$5 z&7ywi6eE?X9_yOe^Ds7Afqa<`saM1i<1CthdS7 zj4YF6 zHz9_Z#4|vcKk;bQM_3<*_P+LY^fZQlbPwl z8hqXt>{gPI97=?V7a|HV+g$1}jd#S6GV5Zg6Rh6a6cF$Zt~7p*Q3(EseJ#t=W%|sT zd!wcffqn)M5Ut=w_k^2%xh!nhcCia?ByL}%n(eT0DItS7JoF|P;rE=bPHnwln?Mtm zjIEKvQf#rMVX+YY#=Df#!Fmjgri{QTWHqv-`sp%b%+a{*Ls_uZA%fk*G8f<@{$R)j z0SVywjlsFVXdb0;o{jWP4fbqgws`;y!|covK654@K3{VJwz=>4i8L}w}N@!(yTcTe*?c)w<- z1JQi$Z*O=j`G13Q6n_deh?alDPwV<_7TtK<{DKseCtMKhH!yCJ^HZBG0`iE$6}P%% zF#xqV?Q;~!9l zY1*;uW%@YHYFUUkr#wEVqV{kgat%&~PCh9EA`aO2cEqlK6k*EzhaPykS}%y`y=o`d z3VsRhGfEwMsPo&8di!${pXc|3JB(l#;com$Qg1bPmz}!;WZ?bG?l*zA=|I(OL!oa< z>dHiGnt)*ACl?v0F}fIW4)+GN?^wym^gS+UzdaERCm_c;VHYPS{Yy*tp5M}gs-=fJ zTVjvTX81^8$?eCS(5m~)*Esn>v>mj(*7(t4H;4fE$oEc}A4mILAZ7!A(>i&@zcxSC z_|X8Hm;XJdVO^s&-YZ$3*v=L$hx}qdz$vN1HNA`uX=i^2Z9aZU=#^0y_Q31OAxoVcj z&?v@$X9}riF%l+(1im#&t&{8iz1H~gr~G!cer2>jTR$BK)^xR=^#io^7Y##g#wsg5 zj)uqqwPfRv3!rN|bMmYD`@SU2Yj8Fn|Xhyhnr zJlSN#MN!@1l3rYC1&2S{4_RGheU7p*Y*@Isb+QS}DY+-_a0!5#oaZt_(s8lSajCc< z%b^%nLYrzQj8M(DrWcgz_k)mK5~F@_fj9KSelDIR=6n!y;onV6w%!T5wiu*bg>*|n z%A;IW5nDF;2vXuI1SxZ7z}^>Kgp?JJ!yp7}!_Y66S|l=bFu=0DQPkr%7$DW?3RGIO z_Y4fu)?!e0squrog;Wp;Q6mBfSq_M$m%zh>7wlR+-7nY21f!NINIB(e@ioasgnl!u z3@Dc3jXc&2Knt&lSu*s8y`E-}?S&jgj4 z!9hk-;;UV}BgBPwoZtYd$3%rfJZx9b2Kx;vhIuZ4 zViTs@X8hZsH*HOS83me|Y@shpaQA#hM9SRD0i3Too}O(@lR($_$$LC`#yBiE1~uAZ zbp9-V&IZ_X3HDDOKHJ9W(dBI8%IH#e8&&RNiGCY-Eadm6XMVXL4r7Uah<4Ic^=P?#vwS}CVS@CZ3b*oise*fYxi!6_{C&WaZlLMn za`ts)^!;c1lE@mBE+e?i`1NK^u6Pj+%?Gw|4ZyG1#mJUDdzYUB6Q>tIS+v zTUhVWMtD+1sH2tqlqmBpqHQdz`hT8&Pa z^6;9B@8eXJC5c>@iK%GjyEg4wEpUg6VBvc{DZvhy%8p7vAMv8#gQ~{Es>Y2h6DnmR zzjmvIM1moastex-vjz1M5;ZG=C9{QNoh({t9#2c0rv#}DUTjhr<=pnQOY&8xkVm&! zhb}QqbYeYHN8zVhJzU`tluG4rD~K1Ra@^Dj6Q)wxWQLGp5<`gR8TzKwXSU3Qj34gq z@YDG5Q=e@@_2h`D*bu#F;9vY@C}!A{?s;{7VX+CqcdyiUbjA<=Y}eM#`Ov%91>6Z zd4lvsKgs5y>!w#*(?``AqH~ANV9r5Dp`Z5JV|Q;t{lK7-)B9V~%n}vz4zp=&tTql6=!3Ji^r`5AssEnsJm9Lu{ znOwcez1-J)6p_32(Q)7k5Uy`b5bxiLD-ZCtETPi$@SIDe7)*YvH-mV^ZS;zH{lDN!&;>T zRH?LpF*t3L0zV7=aiBuNL?S^+qLgyQxMW=Jf{_!$zE7oWH`4K2t?6DF_Ovyh4e!i2 z7=4P_?5Qmv@>$LICzb8i$-pOm1xcWdzjuQcJ8=};bU52%_OP?P^O;?!z$pNQUNMsM zng5tHrrU8l1NeGse8R5u3LHgx1xILKr8f#M&)cc?vB*zmZq`#P4SrE-Cuo@p3sydP z+HRf~DLJ1Rm5Jcs!9el(Tn^NxO*#VopopUwd|b3iW`I+z=^5bq@dl8#ZFb;hSul;N zEO9~YHm*b9rUd8YOaRsR@q=gUuU7R}I_pYGrV1*G(>bMi{KXK9rn(p zCH>*!_VyJQ(a7oKfE3H7CfvfrWsG_m? zcSUYO8)yH%kN$l{i9lDbVY5DUgHRH~sYO-~aEBKl%hEyAb7D(S& zf(e~XRU(U5aL)VbkrWN~u42CA6OH{k7tb;F!9X#_lR+p{L1d4$tuBpRv=J@KhK+RF zZ<%5}SN@ISu}0t!T>n~6nerlrWmnUSt?4C|msaRG%Im%8fSxXK@vJ~C+V;sM|2|L3 z-ufj&$%{hJQLycJUy#O1RN!pHEpU)5u7D2pzT zt+@*lY_i|~`A9X0kh*Fio>w`6(dOyQVcF!=n%l$+g2b+WCBc;p5RXHx~l0>xtJNZzopssgOUXU`5&5la z4~e!CbXQlXG^v=WxK z1j|tNMTuwEL<+BMQ8{?h3()}%)X_c*^2f6rEG2>m2sHB+42YH&LLV>7GF%t*fj1Ue zbDIwHau1hIYgXGU7ZDoDZesdQ#6Itavr~^3Rdo6(3Q67FnoLA~U7pP^iIh5v3}kD^ z4405jV(5ceYTiZQ%OR&hE#o_4AE(2A#Ze@zoLxK9CBG#CFtTW6!%CCZfczGJK!FifO*@EvXe>XUScuL&$q! zeL3+QgZhecV=AM}^$O!85gYSr zC2)7BuBk6e?pYn@q#oNydu+5C_`62EM|R66)KM=Fk_&JK>P}4JoiRxRCNn#6zJxRi z35v2HoxHikN@b?0@j-NmQ&2-~Dt+lzZ(bFWcNh8L?zDF(gC!TPQ3lKW;!why&6j=V zug`*DH?z*VbxKsU5zpI2`Z6GqS;yQeam%$>DkKx zrYFZFe5Bz8e40rB3cw&o0CvrD&jU1m1r2t3OrtowMHeKWap#DUMtckH>aVzqDuY}@ zm@g?NH-pY#mb&aio4i^v|1hPSwMi;!C8VIPaIdaVYZ^r`WM|Zl1tfMcFsZZIQCbEF zkySHAGN`d;02AN9Zw4EAn`($}^Hg#*A1OpxYT4>AWmN8z*iD|~5bUuxhhUqOwi4*B z71*cV%a4Jmo8Fl#U-UseNF&+VWUpy{qX^zne%XH_D8E&ug&w7g4H}_=h+l4UKbQ9r zx7=ZbV+}i{LHU;HcBCT|S;l(NotX_o{CZ-aNh_;pum#KztD&OA&W>{t#-yyQ+)#kc z$8?th`Feq^eVAW(Z4Ox_pCw+hF**;lv@R_69b| zNUM7rXNHdZS?)709PHRW73__>A@81ba$=j83MG=Vm(uNwnQkdEVB&E4Q>Vh**%_e!hxDED>`MB?dx1QRb_7hvB)h)Kl;c7swqrSH= z3e@j+{d_eHb8TRAR`-EbGD-oki`~GU?S!OE6yw z?Tt!mmMtS@VCJHe)%LD#!wai$eKCHE7EBFKE2_`KlsoL0bE#S7SZob1-m|}SO1Zn> z-4_&;sY0+k+u=q^qBNbYK&4C&Pw()3fw&(!v6w!U#C#VZhk?>B@Av{Dd#uH3s%{dI z0^AY6UZnI?&F>w8#Y35x-p|oYC0H={y9&XW1hE&j>yO6@srS*eOug6|E4c3b^=$$GPnFPJud4 zM@TuEbjkG^xSg%Vur&$nEg-Q~Sw1w)5r8&L@id#t!Zrv*-%hFr1UnW+fsGF(kWGx7-buyOOeeO)n<4!)Vz9!T{4ws0lZOz ztB}o6o775;;}9!C^>=KS+Tc=Z#y-9V*wMBo*{Iu@;X98dcH;2s{Tb4YyJTpF-Xxj&ot8+jj0=yWPr%+y4de7sg`%b>JV=c#O3 zhd6AuuHXmU9)i@a6*7!D;e@!qnRqre!&;o+EugNqA|^(iWEOjP*$2tu?30S z#qh;i%vAVz6jmLBxXKHDiqaEeq&#u2bft&EtRxj!rs>F-45V1HwUCR5oZ|N#SlX?{RuwI#8y-ILh3U10J7~q9nt^{*iM0#?E!!P+LhbU1no zHLvKCt$6nU&gkd=7&hX=^q)TF-3>Xd%puUaJXxZ2uysFYRvIb~QQEc~2kmMgl(XJZ ziXNpXBkmHWD;*6oPp0rlh{lGv(mV|_)x)Sk?r3;Y;A(*yiU7lm?Iwr&xn9SFZoO{Z zfgKT4C7!aKL50hfO4~@OIeRdupu<;-)1|s4qJ1>7pGI<<2#eIyCWC-Wv6bLnekY4K z4F|K1K>HJrx5+YuRLwY@IyFQB}5oD z4Q36SW~yTNkTJ>3&EFjN0zn4tWVr)9hFxd=q7f_nAxN3dCcdV+#kzw ztNbLls?oJSYIx2OCoM{i&H?GE|FsByI#+Cw{23*EI_90F9l7?Ph`hr7Ap6~Azu4#O z^#kvOE^THr%X}kN@ba_FrK(KW%&>22z44Ivmp1!!qK3V4%h1R6t+^Tqs>Rm% ze68B~jp#dlKlN+X5gLB({^=tS;3E{vr@!!b{;M;+qYpp-&tCYwsw_y#T?YR~vk)v!Q(sCu4@*R%A#wF@st)eF9?&yLT@M0~Eg#Eai*f3CXBEj^v(=Qpq0 zaAT?*#l!g~afCekoN;muXQ98(2|O%oXJInzmdmcizfCQVkoS)^X=X};7tcoqsio5UMuaWt!gi`g*KwbtJFM74aRx+nM~ zgJ(st^+HZpE&c+^`%&!uWwx3g>=@CC!cPLz z@+=pFEsrz3RgLRvFa@@jhg#9tt;{oaXovHMF2?yo2RFK~H-kO9-EN@0W}N32^>B2~ zW2ztHFcffe{29ckj&g7E5k{%UAwu+lrb1DTwcK6l;KMldvl=T@EBlE*hiZH}10{oS zd8&rAJ5Yg`TgQ2nw8Qh*9w+9lgBQ-6O;_vSde`{MrsL6%ZFc1kEHKl`xyB465$M_Y zI~QzGbr2rg!JBjt#^>PnGe~e{)bm|V9dj$FQ@L^E4i0J~YJWl0B;h12$bsl{q9CWS zyuV_Ba)x@^F0z)(ai$0JJeUq-mTmeInf-gD_}S5m6&A||Sk>O&9&U5WZRc92+;Q%? zgVER-DR#le)3i~&s3tAYW6PB9#+!Sp?E65-<$A25M7M1*8C~m7 zob>+u+TgxO>JO*N^Tfu5;9wrw)gdQy9m)>nff8w>a=-YC;Ww72DwZnRCWfjid_r#1 zfoS{bGjh`w@Tnr;Gey8Fr4pL(UhCk4F8Y=uC2}m|dwT3+q6Vf=^G(yH55Dk#v`%?d zW>a3}_x<=M&!%J}da(D5yvzGsQ;&%s5*u=<8u#O1xAJh3rmPYpqQVr{BM~awPs6h! zW19^Frou*p`9E=zVodcNAQ=1IB?}?u1RtRq2 zQ?BzM=L~Ms2>ZCQvuNM0|0KQJUVhn3i1g$;+bzz@@;N<+15h0qndu@rZ5Nem=LbyU zWNKY(pDS>T-G$76*9m|c0*#sQ+~P1H&kRe5@{A~nxRF_&^%oGKjq)Vr0FXv;{RIk; zK0#28W#$=C8?+Aq=7qEA4?q;=@=|3$RT;or}5^p7~VZ%k!)7>?^=DZ%iAYhb$KKFXRLk zYr30IiKT2*DYfrW3D_?2Q<%;ZrG5vU*GX4XBbJz4+e#iAk(`BRCVD_au?Q8zofY0h zTu6`<)oOJhE(}MUOBPZfQTI|x3kE=s1*nqel!gj07_9=1E@Zg90oV}$_v7Oa-F*v~UsV?nVOTK6z^5g1a=B$#1vl%!3G3K^rA6m5MhL&TZioq9U<$ z)D557Y#0vGuH;@oY)ln0>aIW+ge^ZS@Ky`Pm^_*S`%f?0XJ}f46#1$IG-Ev@Kr|}- z>sYs*1%q6qv0+{?^E=0nM{O^#RUw!hVS7c4N>f+ z;?1(H=Tw3X-B^9j=C84%5$9~ZUgl-*{LlZ~$>9nw%jEF#&huI~;n|OvAD;O2oF88O z(AxbFKBIM>W{D9+ruPrlZP=$+~xvctg>(Im-6(3Pks4E zP7_H##`s;}J|>ClTy#tlFLJw<@N!=3Z(iG!OEg? zxfh=6sBS~#A``2mC&K&?5AxT}%crM=dVGWszHE^gdf65gB}iRxLg}oxRkg!V)A!F> zsux>{W>s5_xBDC4o9#l^Zs?${3I{N>RqROK%FW2HHQpZS#xLZ4VsYuG=?$mXsdTkN zfU#<@T<_5BoX%~(PdcsRbaxmkEC|PVF3^UWA(GaM|LnxO-PB0SgYARsrf*WZm(m=& z1Q6P*4j*y>2dkK)KCAiu+3k8O-E#u;$I8e@euoOe`{KXf3T zzzinaO^E*~)YR=7A^41!2le&sK0zybT9z6eF6J-uH28M{FG0Fza6u}5NUf1V!%w(o z^(^=y>)<7MCkq4-zozB;-USO16O{Q->8|s+$HarM8KS>z6&03)sAl7BPWYht@$9OQ zA6WJWX76l)SUs8S9pnsogYz0k^$;|HY2lVQggmF^4++L7uht_L1iRGF}0qT?LRfGpIkx`DFK6)6PoXj*mqu zWfbGk$`BzU)N;1+9NR7IhP|iH#j$?Da)%4c9q#Bwr~Lucn8WG57A!Z<-3*y#ZY3ql z&Bjz?4i;O&PbU!Pe46K?AveU{q(*hv^Gq<~GEb%gt!zFeT|KZXn9n6!6N+R#gXH{j zZrcN|chK8K27Gp4{W9o03G~u^t|)5#v;|_0(LVjnn{^WH&WtW_9l2axyp{7svsjiG zOP~FjLK2K_EN=zE>ulTS{a8?#3QfI$)Bi<81Q#&Z?55Osdtj}<(BbFTdiS-Y^`x}k z1%FR@(Y5<{<84ljX?$<->iuu6^?s-4-%xtiLaK_HpkRz;se`eWnNf0B#ECp3=xx5d z>C~&#JDd&VoUW+Jm5v5h<>5+Aa`Xf`lA|X=K_0Q{HGt6RtSzeR~NrY;A&!gvz&~hw6VYp*m47UQSnYWrcZhiCw!o!2r_#7qX9J1YN zt?6{;33l=9C=s8hn=j7r&E>r!^uv7+oq zy@3AC32!hb^!$X3o*s>9ECXu1xwphwj%Np8rj^TIm9|T74@@nIspQCehz?jz`8&EO z-z40JyuT0m?5+8TZr`&8KCbtE9CgY2kE!nys8Kw96_Zb$_@ZY%Lg(d%UnM|t+Ogs8 z`???s7oR)o@r{{}Q!BD;C||}Fv?gD1-QiUcsV`a)z6}oLgJ&zt5Gtt$(Ox{d2Hz&y zU=-7-FH#U~RrY>LC7Jur)Os>^e0d>P-r{SR_i3fTkb07!zk|yoRXen$AGODL^9u_R zS-eVd80=|V$m(o!2}vzT*3(Q>)Ly2Om8^nXc1f+D?&t|O_t4Brn4m&PT_Gj6jj@FG z7OEy!bG`$qnX_TnoI$F6L!`Y?UjwV}y>&hUyv{BM=)>9e4I$q|Vdb}=F&c|eq?aNt zFK@Ps6zI!FNPX{0U{5#dJDXRYFw}Q0ui4n|d|oZ#_&9{V3*MhCnJDXdBW8KucQMP7 z#=MbNgQb=nSsZxRchbJx{i={|=NE2$?@=4z*;1v|cPX#gKhueGf$Je0+R&s z(wemDfkN(D4dMzq0Wd%<0A`e|f++zk&=&ry;2Eny)nybUap&s1h-)*D9=C>|!U*&$ zT~A7hLN(}DD7jQxt0*%BD}_r?5si%v=!X5P3r$08D{2RU0SBTC63N(RiTN7*$m)(# zRFGut!fEMc7{`K96Tv7PIgf5*@**pL$~|vGX08WEk5Nj_<>eq)=2=P~?sq666RlA; zA$GWF;hsGzE)u8jP?N!9&8)jgBR6@Ha)iT`D6b^x~%;nE`DFy8<5Q_zH{lXLGeh5#HiP54*E- zz*qd$f)0<{ck{OGFk*mXliMH|;ky0pzt|Np)Dj4cK!MNjlCFK=X#m?rZwTS^; zF4(J}n?B|7JA(JvI+d4jb|)mzB_PRdmJ=fg8Tz;AAC>@W{3j4kx`7zB=LF(_mtO&d z+H(m9zG%;OsG#5^r>@=h2vBtwgZ%gCqWwWD%XPGL!DmiTO<7GqZ(~#20+o7LG;Ir^ z`P>=lI~%@i9qeoJmR>4#yjb7|=mCIM9Y3_b!)6uVnP)~%mO>9dsNR{G>PzAzMH4O7 zl79q*RAPxAw~?Cc-R#(gOMc+Xn=(k>jbsz;7fKQpT|}N4Z&3pibQMN9Cf)tm9KFYp zj?qW@Pq3kiB{k{X=zLM5bIfW`Dxn$X^j(V)I3Y;#*XOmmTjx=&oWsnzWI9R)k}DAb zaR5UwIcBqk$R#KnaB@C}1Z52vCZ;3Q&rPDyAVpDQY-CF|lnl zfe3I+-V>jxVvXW>E_%+q5H0{1O(k2b2P7)QIwC%TFmvQJB(ciZ1S&nxzeGJ^pEd#? z5{es11cN=|$NGgj(5X;tq9Bv-2L_Jb0d7Zm;#_wYbGYz|4~~^jruXS z#Z%_=7vj2lz(LANr!XTAg|cj=R47c#QFCp*_IzzJ$RtA%8l|gnq2QNPB~BE082XF> zd>b%_ipq8=V5sgX6vngeTBWc04NbZ<*zO>Yj2g)fZqV#4qSN0D%07)e!Yas-}$+(jN5PUGUE299u2d&F!B*>F-7D2_Fi1b zc>SMv^#$?zQ;$ZeGut>iI>AZNlWX(;mfN|j_j`8}j(VLp`_}#{?ep;PnSaGZh;*MJ z!tgL=9fPOKwKz7pwfofd0aAim2cKf8;0qEK4?f}Buc^IvuR4jE+|zTDsvI_W<~Uk) zJKi#)J!sTc4>Hjo+j4AT#i)(2?Im~NaBpL(AJ*8u`o*`Rkzee&#fiB+Hv=Im+FL|Z z7;~VNyfENi~~-^?y$%&q3HJ>gXzAfh{s=v4`auk!voB^i;) z-WQ<Ze1^Uw=q(?NsZ~x0%yC=W|+qERfT<`VwqUuYa2=7}d8UtR0{pLn#3;8f2; zo(;B$Kmb1hz*71Qk=^FUjl=!0)ArhMmhOM8=VxdBF=L&AO>Wl=X6OpEY&GWhilp0h z*i6Jga(RySW}CnM7|-t4zrh}<;S8x8+D*$nRB}Ux1*Ha~dAl(`#Nb9`;C8!fbA7vL zku&K5=18!zQwgK5Gb+_;$hTx=gs>M7Zb7Ri2WV`?pn+?iMSw48_l8bQW}exN0nbs& z>plpIw5wSF5;mrsR&2o-4(3sPHsZdXuNatga&dhV1)yxH*>0ihdc{{gaB^?y+Qby# zgLHul<>l4Jk(hINsQV)ta8YS2AK&(E+{%kG?sSIj`t*4kd5^3!^yp>^{z>ite1R`Fk}WOAWHrQv!6? zgMSratPwm(?;3#+=6C+;Q?w+QhjH0BEK5t@Zz0RM0O`kK{^PEY)g9Yi&{vubSR_>e@*2 zS6Nzk*!2IPSe~HZTmj?Lzh%wjj0j!@MI5kidQdTV~y}2>ti-N zEVbEW>NJmmdJBT8Ig!L9j7J5F>Evz^QHz;6Q-7jvi$EBM#cx>J!``*1y z^D=h57U{lR%qXa&?m&l>@u7!TId`x$M4r*@uEw-wcZJpYAUHC94lk(?H602oW+o_@ zhvTSl>bC7lD^4F{RS(y?u;HA;PU+!dbVVOAV(nd5I}_Q?M7y1uCJfqXLun^wF0MOf zQlrL7#@CenES?Io!6Ml((K6fJmD&oBhuU3r67W*HE9WO@-WudIalsA_eJa}_6+V~& zEO&zJjK6^VLDf!zOfv!UQ~^1edxm~6bOAEbquoem7*}X8jL}KE3)v8~UZL&guRg(0 z{OZ%fT6roCu4nhK=M&uFHn{b$3!Ix3oP|WgZ7Ak-8jjFHZAlMQOphTgF~eJ7uAIp4 zfgRCNGWxJ8%jF=J^3_5B`UpU;hmuj#YsnAO`fziJU)Km--;}2F(AZ`11)j|FcA?;e zM~mq8`CcXgbEfQ<#l0G$FImsoW-zg2_o*0CeawMxmM)1?-gKaCZdnR$Sq_su{h(ve z2U^qjI((9=KsoP>T51@~BQ{Y%oV1Bd!48sif;EEg-{lRCu zD=nl$bu6@F#Rl_(YPz<6)B($#fW_YRk$zPlN#{egt%@8f(>*3lL7xHM8&bLsc0WQF zF*)aNNlU47PF&%;5x#}*9y&@d9@&?L9{a?le^)UdFt7(P6Uzm@RRW>+sSJzqt8LKC zk2aUkOipl={=U)I*^OvDv~JPxsdiQ=GC@&JHnIqJM(sy{*5$Kr1c3O;7_aAj=yaMi z8P!kDL&}+J1LZ16_<~}Yh04vNY~h3uYaye(P4BMKl6pMnOZrUs&kS;}$zDDe?8L?# zuS^qG{@mkhEZ#Y!lE13chOF~SVan0ER)EM*M0ycK4*IFh4TyOHbiktSV5Gkf(6^bE z9ghf9ya_ADOeFlbIoge_nEa&iHHOW`veZOqOxN@f+i`ju<*^ErAt)SBHYAWqA1}!d z30k(tCD1d?rHT>Y0R6xvjLs$>Z;N4_nL=pZtr!6gVjF-Vs#l0cNIwXhnQE~*=J_f& zK;MIWg>+Sg^;W7zsAv;Re+_!&lC2((pN3sz0Ko#^K`=!F2nv{_Gd?n7ROp&t;8q@m z3*NK*cUro@y<~&3^HiJQVOir7JYDkezNZ*xKh@T)CRO75CLQIaaJRTCSyzh}sXsCz(@TN>e}DX5@y8eccKYLgbMK4&@ozB#+V=;4+#hs}KbPZ-vbJ~5v-Hln z5F&o|t^J>`Ms>dy5`&G7#<6Bd!MGO1E(^uAFa=w%+*02KCIn^4VNm zS$8U~tUJ_t+|j)&jG&?j_vflwU)8gPC>BC#dm z(Ue8;H=g2Gzwrc3k#6Z~G#H|X*j4rSX8oK>94m_CI1lgMYJT@g2vteO5JvSJo#K)4 z*DqrA>XcJL=TXOIKaN()Sw29wjewh8B`g8I$!I15I=K(%spRC{ycU!FAj(s~_Hrg` z>Q#$-b(=Flbi} zJrd}{35l0SD)`GcWRk#1n3#YFOw8{5jOO0>=I*xClH7j&E>lJVZnnfoRiKo68j&P8 z7CI82-=^EJFBYE9!jAO2Pk`=rGX`-4&n`xg>VA=)p<@LElnfwXIr-mif^)x-4IX#7 z45Rch5`8od1^ogFCw7Co*T!FJ92(&}Ce;`dG#xha%3DC(6pNb9pc~{!0L!X)u$*=Y zsKs)6j+Cd^Q$ZJ~V(9Fv=LxeLoI;I5>w4n{i*&0o1g+Q83&R*~#eY9?B;hN*RHDJ9 zwej7Y^}&A97Qf|(cjYs=>k*Q@h0k&;2!L!Trv9Lpi!mmpZ!J}yIgSGZ0H{g< zZHop~yUbHrFz0V|(&>0>cldwqz93rt3~fEU1SYK4_`ud>jLZctF7spI`lT>&*MvWivO-h@_b`OilY?pFn%MbNjFvdwZ zA<{P+KUm<**|&_|#I&PbIT&QpPLTrYyLv-|OE7b~oXcG#%Vapn278{*O-oovkZ?0r zoFQ5Q`kzJmoJe=~k20&|)?wPIGuH?Kb#d!rw8T)C zyr|}l@@j?xApBda0mxz}rvpag&|VO=Y&F8lpnZi)czv;T8&wzq=C%ssY)0NP;{6B! zv$dKE2Z9Rwiwx}NT`!ED`}L_v()7nts+Dh0U@w2g^?Dv+VX4KyC`(Wvy$ay?C4_S2 z$8Iu&@WPB+#e>QXIJRK$6*Sw`OEB!Xj&x8}K?XnwduvNtSr75ZKjn$N-L zOkgm^b1Rp2CyMd`B{@Bx2!a@c&j>y4XjEFaF@_1Uf(;qeS=39V4c_B*;|CLgFJ}&^ z3BpiXtl+zNhoFjO#sV$ax@6lKIRg2ZPR-x@0(}bbqh&?BPu9o-iHmVR8mHjP0Px34hac4402p5`l^f`%9K=`JZ7xg zP3;Je&soJ)O@%_9FVnx0BfCW^kVQ%Zd=zVrVn=U?`;U&5_ar!5}eL+$pAP8xUXt-yJV1(Ml4Hgp?3=haA!X zFOa{6(4B0$#{3D>++jZO6WxXi2aA(BObtJKaCur87%6KMqxERG5OREe_rdwneFU2? zZx9h^ywzWEd(J#SrFtY3!CaOBNB00XeaILlt5+#ISpbsRI#Bf4Cf;iC!rMCIjYkan zA+3Zz^neQ|jK$S3PWoNrq=|@0c=gZY;4X{5{X1soOVt#jwBGvmmU6iSnmbKbkl?!3S(`50nMrjNrmYeEF zmZAwIg$C=#oAJ25y}>PV;A@Lc#&mddz>PupI(fk=f87G7=BW0>!jB`UAPueiC z$jBI+fyK%(@#whX$nR0X;Z=s)jE3A32BU3YgWE~b>qbhx`%G}1Tz{LEie`HdZ$7n_ z+)nV8f(Mc))|o4pbhsCs##FXqw(?zGI*_&(SiWalT5Q8h?V^u-oNC5PFj=NFeeW4E z)yNQgyW}-EqF179PJGg3F$n8B^104io0)THECCOV^~C1OLIr*qmr?_WPxzwmee06y zbUA*bhJ$Rr>WnTpNVvO2j`JAw(-F-n2VMvdfq(SjCV{FPu-#NRv-cs0sz%nY08lZM2xHe;UEOU zX8}Ytf!N4da$j7+q{fmcjli$Ugdvvl8$r@gq4BoqaId3E+&nu|pTG(0`z4M6?*a$x9J`WFWk-rgjP0=ZgtVz{{MAJGb=q!jq$gNOvo5MCWQsC} z`5otXF<3q)y{@b*VTRBE{9>3xLAc^F$GuVS<1~3$RM@0)r0pE^d`~R}wun@Yb|iig zu+)LIyk;z(!G*$?7&)|3T?OZci1XB|hNh`X92ukfoVUxq&HfkyKz^M_&>U6Rd?<|K z{3s7W=;_Rm^vP3RK?QL!fx5u#267EQ;@VQ+f>kn5y^&wgM65an=sNz_WjME@4X4sB ze=t$yYM!fwB8H?xvIB>MjO9B+Z_Sq#P#H&9oz{)oDOc%mQ`>dQ3Z2F_Dzo64))MZ* z>1vWb(q!Ez>p(Yoypk)(BmS<{DHkHacbU(r0~;0(!UEGMb+E)Rnjv_d(l3fBG1!U{YU zPjF4YAsOHy3;Iz0doAfp%ga za?7Vm(C{sdjsy`^gh(2dw-bWXxjzaJ(CK?NqAU#GeZ&a+ZNr#Sf%OCLQ)>VOZw$fF zIz@-Aso;=Qm_u=KkWA`et$OLr#FgG4h}70J0@>cW)Z<`n(5+)A|L%3dRg4@SW`A(g zS*xLhgXsB*4^1l2GQ8DPgPWEjG{B|q375-^Jspatq27iy!}8#yOSxWQ}Kv2<;B+GBYc zp9G?IaP+5gKrSMkPKrOE-MuW%O8a2wvsomrZ+iU&fErGyOKg@#sM*_GLKw`cIPXJK z+%X{Z5Ym7Gq>MS^y`qoZTj9~m6r}usKV|c?>+Ci8g!|?fyE&$u3zx*c#i|x5&hDRS zYwqy6i&s_K%1}QfgoXLfhUK-T+ptt}xemxB8xCDI6iGuQwbAd8g+jb2a>W~?BTmj8 zrYX0Y*>O65Jz?4R&hal}F^xyDn0Br+pADt<5!iVAPOhK*@*n*1zdiN*{8$rim&tFQ zXJL4uLCi81%zAg@7oWM#yOq~@zi;h_bi>^$^AS=OCL$Gf^0%tZC^3_N{aic0nN7c* zwyrkbB(1k1L^i(>D;`*BP!zd#APisAH!Y9v&XBE8njrfwA8PZjiWS-u%8 z5e$2ffSWPJ9-`WtdvliUwt^j#>YS}P2ha3TfW2knXeMwf8Ky=2K8+1Ff97Cijz0t( zmEiyi|8}J9y5orxY!jt5eX>qJ*Y5K-NaNSJ?}e4V)9M@ki+Z}Pz7c+7PpxaYrz` z$GzM`TK5DQn3E(3K$cD3VmC?0oJxqOH56;{*VqRoV8kJI8y=F*Lr6u%66*gpaNK9q znZs`yC%f!$A94X>83!~6JlXA?_y5?e&6#BEvfkRk-}efC@Bgim|NZp~`1>~>tsqNC zj^>Z`VXbB(zq_m#hPAe_Fs#Zzx-7bHZDn87yWq>;BMtm_<^0pJ5m77*>q^)Be>xnk z*1^4}u8%Z-jos=VHh=8_yLVQ9?}a6mhoz}OH*p@>0J8VNZ=xW7+47UWG{E0S>>?W1 z{LMvT?`f95CU5>z8o~#RiMM{}KxA0Ck(2?F@e=W4IvFNx#u8;lVrpZ~OQP>BNlUBM zYV3R|HpfHAdCg4ydESJ6wSGzv#cyX* zPl{Bn*po^vfbtzzm_K-AW9}c$D@CF0cQijOVyMRn}?Y0W5u{wEot13ye|19757 z!!zBOnoUg2R%5^BHj~t?FqPY~QhlSw)J!L)+|w3JDW1;L>0p&kFL{}Y9IFGAh7>Ce zol>I>QXH#2Cs&b_vfF^09H9R?0Bx-59Ips#HN+$+c4WEJH1N5>N~?#ma~-w0J)Fa2 zSKhx5Xt)FrSmv#UnOnL7xuJn%i2lxDW{6E>d2XUJ%Mg)V)`!An9bUd+?@bz}&qaV) z>;R)d$}tlcKwjt=NGJ4E4Zb`02!zMfe+#^B)01x##fEi3GS~%)6G9D*ahr(VoHfcLxywrErDVlkSQykz5kP(_waxSJ!$FR`@pNY*+-q9Pwoe-(DH5gTth1gaB&B zXsq_~WGNVOFl3{#I=#oIl(`av?@T7LinT=VWEw28|&PgcT_mnFghTVo%7v#PF?HL!|u`%7t5fu&3agrK$(`?a8VZFpxpLKCPz$$GgW8VS!RY? zuqd%mPy(wY%0x}H9yyW%vsX+UR7IUa%_xBW@E_WRlT}csQMQysf9S)q262*mr;Z}q znu%Rg+Jq>hjKN|8rT&F35l^7sC9aJM?U57gMvdHo z>qyS$w_FNgp-+>V8EI736InzLl^6<71a1}!T%M%OCka7<# zU`$u=+z_5N*+cvkE~_G-RWy^9=&4dvZXv^ zpn|R&YaaHiD{hBJ61oczO~jSpR|2yxpt*$Q8=b{qvmOuPpBEYKc%U>K!#H9WEh8p1MUhJA&0bqfQ+xeg?vHaH z)Yy8!?u9#B4%oeSzP|+KUO}k)Pvw07pM3R!^ZiqFK~JyxbHDR-JJbGkO2oAP3KyTq z8avi5KN{7q`Lb);{~Y%*?LWaq*R=m6x1D_OzhL^0*O}J%?!BA%1kN=7g6aRD&O&GU zuR{085{;P~t0r;74G2_`q#^DNs|`ah-M7u9n-OR?$bAGXt64nL`DzwV=%5o1P>+7< z1Go)$A)?7FWBA~*K1n8AjKd5(iIR6h)~ktsb@pTf2GS79G#+n1%S77pV8h^Ug+g zl!fWNN`vwl*|3Jm1_D``8XnX`pg{F-byTVBpG~C; z1`#!KL4Mfn7I2nGT9B$rtU^c(N36q2I!GbWs=?PH@bSq-n$ZPNMG?&dH^cCA1}sO~ zojHz6vN@SNK!xxG))|Xl>RnSy3A_iuKdEM9W>XTV6peB8BpUjge zU_9TgAL?c4D7TFy0lA-zt#9&y2R3}BUeOR%lC*(-HgO-zGvaU{7|ftJNCN6zFVkr< zEQ@)vCLF>thO-!CGnX_yD%uEf12%LRxd33yCO8Q|_Evy!y)VN1oE;WiSQwSbuA4hJ z!?pAV6P4cj=>?CANz8j;fV>xNd&6@ANx?GvlgHSD8}skxX9k;}YgFScJbNS$R48!; zG_*5x*esRzL0i~lv(bvpMnuRZ*$YICa>E$385!v?gk3S%s0`Mw5`deZF|V?CzcT1n zjirkumxA(f8!dZ_vaOs*FDNi$G!cF$thd=j36K~!=y2WwC^WdMZwVyMO)v6ysuSs& z3f41K7;6`SSlLe3{g9&Mv6s=4vkG%dL$m8xj)H0v&dR5+Dm4n?b9=T2rrHEp*`VzM z!H_OQ9goMfaFC8TkuekyAXV&ONVzbA2^-0F??dWJFK)5{DIUQF?9L4GPlZ2nBHcrs zT*D9w0tcK$H;FdRM)*ws+Cb>8?5PllaFC1$O&j2AZTkDSL5U3pfyaC=YvOY9s#Q`_ zdeq>|t3x4^zIMoWD-2S3SO;0}fCoK*j0PW!SmxcE$CAc4EzQr2z$o}FFv+>MSWx6p z7hstNETEeW5@Qg$ve4@o!&*96_!vfLl2|%XjCT%?qxrd|O>RM1<;3#(yR;bId)bTE z#qzlrP;tGHiD2JwIAmV~%b%R2{z5>6b~U6Ng4@xn>%4Y4(Rz&zm$=Zw*o+`?SddyH zN4^-`xzyp#rQps*xHBcF^*-p6K^LL4?C5lccR`3h;{G}BuflphfoJqei+xv3R&p4; zn_1K_P<H&2FB))9;hBYr`H*J={%S(&W;|Fbt;AYw!vtRHA{pA zZ`f>HSe@-0u5-l-hkndn$t0&OdlBAY^OHkiSc!M!Vs)7OBboM>&cCOBrCIGO^c;ti zKj{%3ppI0;CFOf+zZ-lh@w@zLUaNF`AI03v+3rqX-?s z1Np54F$fTbx-$(xwU;3==MWpA>%JCN*)O2TbM_OgX47#Iw9(*{d`Qu^TsbLFAcvny zB8R`}K$gs?kAbx%Q0#h{Ck711_+h_pQNYI{6F>ONsM}kV`C4Scga&n?#k80W!6LrL zdvBeN0Hw(aMtkSrnspvgS8G__mMn8(gA{SEM!3ycB%E~hZ#?Un&_nV=ojsFzn7=JJ}T)SkR@U#`JxQGVakpJPUmSqMS;XJq-F zd1?dgAqEtjKHYj`fk$=`(B(>TJAp1=Un+ntX2+B5o+Yo2ecQ{q|po30Vt)U7-~6D=iq`6GG6@34@lZnh@3t_hGu^Jh-xx@h+P&z+O@Nm_{s_ zkVn_DBq>xpJCnr*vREn@h5l6wZ3flHZ%_?HJy!a-Sm`!iGrZ7gW%?;PN0a8k04d=#p_@%F88oDq_vqBzp0}fe zZ3#)$b+quy=8jHN+(Xmph;;Oy5`m^u&E)9OyJ6IswaEwpQ{M<<4?)go(%S)p z19y;bip0&~>qeE~>nza-FxVtxkjcBux)O1JHA}O15_bs!ObrN=n8dbP|wq2bTN>;K+{E*mLf5= zaIQ!dx+_AbnCS!gV2?oQn|ssZEf7|kSMShzv_AmAYTjEpy3Tpeb>6M;PL+GJdS?>2 z(X`$o<|SLElJo`IOj)ds)(;(eEY~)Qc3Vj*ouo}rl6J23$UdH0yJ%}>uc56u967*N zf}Q2M*U@?I5Pm}H4+1gV8yHRV%LKgWPr@5%6r; zgi~ohpeX8BC?Mi(fjJkzc!KiY4%RlcYjyfF0$;$H2+SmfDst@w4h*Gp)>NVuxk8T? zav8Dni&vmU;$SCE(m&o-CgXURb0HDLDrDOac;>>R5Q&i}u9vRAqD$8S#|6hY`dO~j ztK`0)|9)lOyhRn}xQ{vC3eN8>Jc>%-ML5Zoo>L3%vt&w@E}X|jNrcVGY$ezl@pO$- zwYd{T2=?26mi;mE(29TSqE>QKrBjf?P-=UlyXXSp+@jH{Pzny3SPfAnXvxG>L7E5w zt`xZ)uSCZzZRjAUR%ZY%4t*e%;|%D~7Vgm*?DQMibq~_&WWeV};o&XaxpXre-O*3( zVw>dF?y{dUpdrS1q^x7wHE@q?QJh7A#nhu3p^NXml$@l?O^t1rqtPWp{fGgzE- zPgt0Gl^++Tp5u2rvntC07TMS*xs0Q!ulVdSB>mA(l%<@bD_`Go{Bw7H^o_EfwCzl; zC;boW4_HqkDSCnx*Krc02RJ8baBb|jfo&U)CrO1JB29Mq+#fx3bp5Hv$$x&h_Ko)T z_Ql%!?MG|hY2Ro+R{IlHn||t#B8&C^=9%}wik(|xVe=X;o($KJx2qKFB zT7QNN?SD|O{bz(v12FV2*mu}BQghqwxz^>{s}!#@h{tQl#sob;{l{vT4zCcf`l&O2 z&^o-)n`bzEaofG;scxgZrFMtnlxp9oeG3YHle^v;4b=X)_HBkeRr{$gJM6w{*j@R^ zOJ%$-yq@&@%5Oh#y#0=cagH+HF~&X4cqiy8#`_03-fDaloz0>4b`4dGjjVlfTk~fP zEY)9Z*MGD5v*sMVy-rAqk z{*2$g8vWG%wDw()*Qo6{I>(3q4sY})`u1-*&wHKb7z5`VvR_AkKSL5NLm05}{6 zm*Q_qetGA=6@R1R??>zH`g6@6Wlzv^Ao3jd@X8~5{uhR*GyAc2{mb$8%Y;gphUifF z^7_T*7d(xb?aK3(y~5-I9J}Kehx@g_*y6w4*8Ioy*dsmkZ>-EDfZd2+sDrF{#~vjs z$J`c<3)K}R>$N|uwW!fs`-9q7`R%JwyVkB3Yf^T$E+uU8Klo=<;0o~E2mQ`W`Jo)>@}X(g`IbB4>vvFmx!D-HGya&+=J z?=8wxtY^IUP?w>9$0jNs!H0>*lox+v zTk|K~({qtME^-3*`=Rii+PC=aM+JSG-vN})bnTn9KZfG7$jzxZ2Y)c-CcY%F_8;K$ z?(?e;0jl&K|DB}wKm5%H(%Ww!%+X8I8np)P8_;`JdVf0UT@jh0^GWZ4E_%x`(P5sXx>Fk$Mo`tG+ZXKY|V-F8238Zc@9)YohQEx9h)vx9p4a)RRn;-du=Gw##7o z3PI)#N<~}00ME>^Hu75Yhx=&BSehTI??cd>AIdUl|NK5d28xD$vH2;HY*@+ljXKKD z*-&UPrUGjy&5_nJPKalfPqQ=eht-}p+e5#IHSo!~<`1JCX{Hy4LT<@K9BMW{rGfB* zRp#2OwX6IN)LyT>#_u4ubGmk^b{V?OLAN)9ZbSFeciR}S^cw!%q}Q+hy9d&16l^b` z>!y%R<#djo_DHLr${ksYtKZ;lu^19}DJeGGMKNt=mvWT=I0^NuYlIiE1y;B z1>*TD&Cja~o1gD5?=>U(2AUC9Z2DCF&q_wZnL&A~DD)^$RpdNazJQXm*;3Frw;7=~ zTaO%<%0sBsIv)Smc)o#I=Na@&rTC*IdF9_CMWuT7OC;wx3&JWV{Jm^n%`Zsgm@l0y zFHglw1&ek4p69uPdU6c9;8676|`N8I@}Vjxc&gXTvVs#@3Z%M` zW9ZAcDS;~0RE3A;kM=ny(sa-e3%dO6cm5+HR6SWmyJSj(ulurtXxMRyc$IEIMV>;m zZb&vtfJiJBv*g~w~;(X!f26|{3o}Q(r;OYN2_0K}Gu!?3eFNeCgd6>tO-TG%5iPr2?+Nb#={ZI*S zDD+Y|x%%x2|SFvmodww0uI63Xk{(oce;VEtk6H zT#?=fRpEn|Qf)979e@T}`yrK`eS|@p^lAeU_SCc==6u^X+0KN0c%KO|uim`M3R$cV zReSdER)5jr0MJT?=xCt!@8(zkR<3A-LAts3K>{)e~ za(Mx@=tmg&z{M{#Kb0HAyz)q1e${HPhISi5%c<>#(QV1Q zCV!l4Cx86fuRoAK#-Z*K{m2t@@X0(pvY@_uB=z00$F%ak*eKa)Z}P}!7mtkbc)IzI zG*I~u*23-jth8J8eq0$k)GpXorPr(APW&2W8kqHm1+Y~8OEYA7!!{@*2eP4 zK}`O@ui4z{A(ZOBj4J%~8U@Rq)8sm}R5&-!*&W*Gguhpm+mp7ZB9GM3sL;o7iV1G|*d8EJj*?u52LBQBz z%y@pucQkRa`B`k-FY&U@OFGpgk~t*b4$!IFMJqPZ2GG2lhFBtRezup2(F}^E`Pn{> zhlbCyDcKO^qdn$SLNoMf4#nznlUCgwWu2yOHa|OHQ=j9@1sW8)&|SPyyH@)hu1A@M z{eJB$T#eP}y!N}b-$M^A!F7K%xNbbSuFAjc)<>XAep~;K$!|N^PkQ+N`=4jwlmpn- zMR;lnep{xuee&Bs-5$R0!PRf9I-4m>HbtD`5YAV!T%(KSrfF!U$?@&eXi~m%1Bb9I1G+R~0cEu}Q>Z9vs&Qgz&)R zSJ7~W15t0KhZrsl-!m*Ams{__qvvB3#63sy+1IYW74mD)aKOEdvNtgUPULJYdsqf0 z5@mb2%v~dB$2zTD4t<&6yB9b{z04BWw*4!V|Gd)8l|tlFrZM()T_?O3>miBy2bq+r zB}h;Ro@8XLms&b18SqkxyjwbSpDqpS%E>=^k-rEb zf02f#Z3=sE-==O-OTn~_>WH1(KQx5)umwQkFCaxbSccLLfr>7GT-L}|h&F^q@Klw^ z=w?7b@pD)_M{*wcbf=&5=AM}sWruR`GyV9z=VFen&EZI6+Vy)6IbMZVnB5%=D@FL9 z4rM9S(+L|#X6)!>UXO-)Q>|p0baJy&?g0gG^$l(NUQItazBxrL=2*O5-^2elm~7{f-_R+u&lp)%Lht z`|~uW#7^*Q-UBm_QwjMP4%-Z(^L*YCN1&qeeXf zoV*(DsiU~UP=^1EE!Je#ZE`2k_=?O*HVX|A9vs+kYS zgI6h?51!$wV{kK%thsx*$Sf`%Y|96geojG);6wjT_%AXPyn=ndx6D#~G$dqz((H>t z+Ij|Ug77FDglX~x)Nr<7XOpEfjyY-fWF@F|k-JhBGHb$+?O+QDgjWM7S1eay>qqgT!saVtLxM{0!<%sG2Mps|;x+n~()yMNC$cgYf643| zu^xWtX#4^-{sk*;wn9Chh9(;em=a3#8Swc8JXXLJjTnG>kjZ3=sSv+Y&D~RsJmI@c z1rI9H*c(*Fs|m>Lbpoh3RzIZqeo*;Ak-MRq3SH=T3L9}{Txc$?fNoPi22kAqszJf^ z(-pDQh(Z=K@u2M`s=J~TrYQnG-&4~#;g*|%`(%J z&J8a)=XVl!vv5G}v(}hifQ~N7C#UQwoH9>~9+1{RqZoZM2hAQ#m=(T$u9o5omJdj` zuzcYdOTyD#BX-N#{YMNp(@qSRMdn~ZjcD(zy5Nt;0odBbGWMKDNo8sn7&0w z`xZbibq#V!JqMu6QMMXne8YsNdnvur`T^Q^rlAR33DspHedu>rc}-?uagqcj6+cP3 z!AA#qGtpXxtJ5v>1E}WE9YRA;pl3{$L2OwY^XjGM3`PiGP9!%wkcoYs-@wY<~yS^Q`8K* zQ)+hduHSDqrqtKs#ILCMaUJc>Aira+WkjNyaLRm`I^K=)8%Jn0XC`C31Wg?5ghO*X@qg&3{&G(BE42gsJQ*lPo?y8u21c^vb;u#%pokf+maj+cE7&BkPmw=!az z;|=Q>w#OOw%m{rk7wzDR6$75|G1L)VS+PnCeXI;UX6F@V9D2VmB)m?!L|$fDa3-B_)=iXlXVU{b zuk-8CiDNN(dAVmg$GhU#XY+cgjJ*+KpWEV<5cbDXpi#n0lI}|}_LVYr7Af@Mwvun7 z_4Q_BB8IPT3!|G|V9u4Inyw9P_BLmj6>T9r4j0GtL`^@JvW4X7_|VW18VbK86PUs^ zNyA>tUcx~+9o_`ToNcZP{yVuum6o2K=*UJ3=fBmxf}aZEPOyv3}}Jo=KY{)?rua|#JKK)$bQ*|C%4`Q zB9dnA15tV#!_*|80905x;3;#b00VD^{(8K|sWUQSxjmNa*>Hg8aReRxtKQr_yU`oc zi)rr0%s5hytI80+j++x4R8Z@o=I(>M$PcIF!7!k)^Qn{)3LwaSuKjW>?v+pFT?{Qz z&V#X`zd>`VhIhj9S07NpnB^%hjD)t3z5GasiE{?@dAkjM2@5}!xWJa{^XS1ecQ1yu zb{6+mHzu47798opGS6Ub4KYFs~DwKahOqPy_?}^HEB7`eIsZz!qs$K zEhn|QhdYZ0w}lhtqojWwGpGCALt4iK?x6nuQtDuoN}osu&;!&?=zFF33He+=r+0># zy<=}N= z7%~42D#u8ma)+*aw~W|P1eK%QeW}cQs4S;DmETHYa{`y?R;TqqhrlZ_*nI$eAO^h5 z)&9I|N=X1lB>@3koqYF<6JW6lmCswg}D~hQ*!H#SH@wHjMyDFmUC} zm2sC1P_it79%aYxLtft_*71Imr43#1ACeRYtOhr3l;M_+lx@vXP^+^UDUDGXdVx2| zT>!EbrX*N*T0K!kw8*ZfU+hPwmk{ay0B)eMv!A~I9i*`hRN z+F{w3X?lChaqMv=(J#9YJfqzCUnoyi9v&gF}8 zKbHG#(&m&c2&m~${I6;F@{L|+V#~Z~`Y0!0-D^9t(*hHVq+#R)+3h?ht*qRYy&QL& z5@3&>6Oht(f=Hj|K=WO@Ujk!@A~-||@po@vFYIzh#okSlb)X22DH|lZNn+q48ggcp zvAeLY(78jB?)GlNNJ$WBToELCco0E0acMGl5agP@Rzu&2n{Wckks=*}bbNz~8gJNw z)G+hAW66nR1lr##EO0D*F{TX_H4HTY0C>7{?VfA;0?z-mIMwqNl~;Jl%E5^| z& z^<>*UZ`y{d!)5n<&yCV358N_9`CAlZzewC=DHahg$53?ZucBQUVi~OFX$~A*W#B8k zA$U>VsK=*4;Cg)gar*k#IU~!OsTBc8F5+%Iy9@BxiAR}b0Q3_q%UrK)IbYY!2Wo?u zn-nB1JB8DG%E%89Ocbz3RbHU*&Fn*3VkyhmbP$;9IXPO8f;th8Hya1Bu^Jdbl(JON z(O}j@ptBLBILoSaDjW3>!i@7e(VPnHFm$+das9p0c3_9NXrmKVAU;~UWlk2;a&7z( zQt<4tIkQBS6-plo`U1m7%yd|0B_17WNKN@{tL@m__`=T(@k857Z?Z*D=#3V{ zIC&UhZsNU*tz~*$N3@nlma#Z2>>MU;QASZ!aJ#L|ahy(L5e$R0Zs?6l0Bz2b^H3_N zt(&6DoTiTVnJDjw%pGs97mrhdQ%wK3=cY`k5r@lf<@JFwyJVv~Z)U@3mTa(CkfK{Ec$&@E})jE~>DC5A~9SeX-T&28c z9Mk)9yXQk|R=Xa2Bj!ph%;h(bE^-Al}eZGYtXZN#GJEZq4H(5m{7Gdt!^ zJ;a?oHfNT>BZ1Z!g^CV$uK=A#|LG=|^OeNw^VVHv+{`E#tzH)goUo(%3UyUO={heH zWC|ie>Wqx=as^%~Oq7Q-vEbOSWXMQ|+!1++=m~An7on@>%mNoHS&$}k6$lx)%wxhl z6&EozU!Y$?Q%X|d#b__WG!T~|9AXHv{{cW+;9g3>{Cd5%*qkvHjw>~gf_;+r+>CXg zZ#E9x59Dg0Vj7rcfouiwzL3&CGg;W(Ez0$KsrMBD}WImqf(TLuoxiR7KG&Y z>7dapFZ)~Q>^y3WckeNCj#u?|&qs1iiJWcQ71sv~kLPyxJzYHR$Eck>sEZ7MK*4b? zU&u?+@awc7gZJ7}#o*2g;BMb_akoc>9kJr6*rwK;N%e9qABFSS(`OaU!lEXppF4v3 zF3371a`JyU8q5jRD8m3){w{Wyd|xp6!48v;6()bvUb26{h(7Z$f#mQQMshg8_W-=& z@NwXE=%EW^;6^GpIownuQ&hk?|EFOVIkThNXA*_MGNZ6FL9=8%g`nA3xm%f)@R*D0 zsM1S$ux+O%@Y|P!u%SIV_J=j_%R#SZn zCj$SY*ld+@73{9Q6s|OMn0WvWL8&!Zw32fSa;i*w%5#m_{!TzJ6G-H(KDs(3M>rcV zCrvV?XX&Qmzn)>QHWO3&JHChYl>t3BIO;iBqijVymEQ;I8L3)v@vCRlJJdAw27yPh z^(eA|EiJO_R$$&wnWb2+sGiJ+SJQ~2nmlY`D55QkkUqO_oeE%@Gy8d23%u^-|0w(;V-K`m@>_Bc*fm#m zLy&q9gCf^=m|NC%R=34YyCiITv=7(JwwA#|r2{AEihNBK>pB zg_A#2nlg8P>)qzed~z%hj&4FoY`v1Zn-r|KmWfw4=I<36qqh_ox_+TKv$xF%k-1B; zo}s15sIEPMR9z05s-VPPvD6t|y2M=`^F^Y@-n=zdS^~dDNlW0@2x%<`S^~d|KG|{f zrS}Z{8mi4a!^nc~TAx0IB7cSc$G?&B-hcQn9uV(6e0wjz$f%@;93xD+maYiRjsLs_fZw z;4XwX8W()cmE7xQBG`tpGGOcUr8AQ~IdYC`S6H0~J3Rh^0{-^%*RlDPBh%9^w69Wn zymM~j@Evx*nXyM&A&Pvk@f>p0 z^Fp?=h_bNn{96}-bIo1GQN^Qggp+5xO@BAGll`R|XRAc~N29zlLK(~&)QdgBs^%foNG=g z*i}wg<`CC0aH?*_-BXN>lIm26CnNC&KweG-wYwiWzLxSH06W^Z)eN!PdsNX1P~jlg z!V=g~8bX&D7fWxC`zOGG?DfBQs-OmOgJN_Ub>*W~$P6hP01>EG8x8~H2*2|*rTSvx zzup;V?n3qOW;L14oQ0e-xFT5RNgq6)b0v|R@$9!_q{hM{%jf5bz~{1Hhb+uMUDQ+` zix_~c#tG1*DgZROCC=+8nIzj?4=D;o85j|-rWrPd?`fePE#l|Z&ei-r&r+DrT{ z*Akm()mILm4i$&uJn*OPx!W6BvrAttfrVGlm&5gc^G{dnBe(wQ(Ff?ur*G?~GJrGw zWDt`zFegZ8&6ED*(ef;lIX_Tqe)!)-jTgC1HF<#gx{Y~v@K-A}nNy6_TdRLS%laVA zpBeQd#M8gc6$;`-gz#quieSb=Br_gX(#om|S@=)Fx)J7>;L1tbq`xU~6HG83@t$>Z z{>EN6-Ole4k%HaRuv3)M zw7)tILnF;$P?#2B6^Rm8mBBvTvA35Kx#g=rMYY2WaYe zK(HKe5Mji66BLp+?g51ir27IgWpZtFkBSL^a!)5VApj{5AVUH&R{E{eZ~evQ&kTCM z4yllvdr^WTdLKB3?hD880}+y|;G+q^Q2wB~WBlV4LNFsdxGy&5?hD1(Jy4WP;BH*t z2sp=Se#wmC?wrTz(BpG0r8HM{urA4S$z_pqF!Wo=LBvU5Z4*h2HsUet_ciI&U2+RZCXYCDr z<>scjydE$jLeZW*+@s%^QqJeqFq9T|Ffmf$I#tg?z9C}2m{-p9dzQTB(6#0GaV<^)wj`WB zm%fTMPz|Roe;W@aOM&+@dF2Ko^R98r?1Mh9HlJdX4uwqx)KE7xd>jmJ`iPr(9_Pwf zmD`hS9-Hxl%-8(M=(Hbp&;7R?TC+Td?x5vEsEEzs@h2p$edDHOWtVkJ(- z)eMs@OHN1G{L%nAJbQx^q9HS92^MDqo01rCU=xI_`K8ft4igaj0M_2-Qy#+6podJn zNjWUd^K`$AT!!Q6sK!b*S}`2(=Zgv!kn)sscLppNDWwML%owlubu2lUp2y{x7pYGt zeULCtnd~_$SmLKjNV`IHP+NrEy}jy^P-t7X`&O>q=+!QNidffCo|%a3ey_5^(nzhTeVpqE&_^yjSFk zz`~iT8@etUWt0;SI*LYYsa^sb*_HSR98qOoUiTG67AK2Y{IXNE3t@z%PG3=JM#1WV zeKd8N0-lVtmf3y%CBmQ@fN3n~Q^T#V7jY#*(NXcq&sREvk-G2efCwvXach7Nwl#d^ z%p!L)2D_1hK_7me^=@J9GodoOLR2R@qB?-bS#fs*N?&YUfW`zjI-**QB*lykLCr6T z`Jy6ztNGMmHcQ9uBJ^m9GPoEJkN0#*&|FB+Ti-*1w`DojKPk(#QUrK-b*Z%nf{B6&BVGQhL8D_uC z2zyyZ*T!BV?d{LElfC_SA?5$|lfW{?p);$0^6js#4ver}ZSn`s|Ih1u(yPw(5P^qs z9z^@0_V#wI{cwBwrEUDLA6~D)i%w&m92=|Efa$nBzCE@>zSDkUJBOiYjybx9GLP22 zLznfR`i;ZYVJ~_7z}dgfAiK^V=<}BG^=_Naajp;KIRDc3fA`N1Zwy_CJiUMPzkTk1 zJ$&2UyLtY=9A`~1l(qTBpYd#ZZQvR|dp^PI9cz<>dbq1jABfM54t6FEOg)XS;PV_l zKga-q-YVH8vo<99M|97CNDpJaIg zjBoJ9z281GEaI0mTgw=4Ki%h4_~XQ)LS-7Be{<%z6rZFedtdC zkn@Sp^SKOGOFlt?9czmZ1%>wKq;4W8G}%R=sb?uWfB5_;M44|(tiA1z72@lrXMlPAf3n-oAQ4 zq9xs%hBgZ6Hye;g+PhlqU2on;Fmh$H53=2)y=wKx@C2cFn1n%b5Xv|$*FNO3KC)K- zzS_I(T{qghZU&bmqiy!aIJ5uaUb}BV3+aH62Ro*-o*7#Ahi!m5JzzUObB0+{KZnKI?2ly@# zd%){t>7VNGsU)O3kD?lZKUZq>i_Dob@pyD_hMHBGr z{PlNZ3(_PCh7Cv%e2S-g`AyzBdOl7WXk@Inqt7|eal8*Idr&J6_{{r2l*Tna9-z2cfsehPQ z(B>YE0od3urVhIL+;_9Q`*`Yf;~2{zMtFTS7To_BfdCHK2~kv(mm^M^B@R$IM6j2~ zBeY#d-53}|Ug>4fUN{GM2FZ^pZ@G5zYzLWQ%K@*Pk|&qfXfDp@fhNub^@yA$*3l@6 zu^TlFX)Z&rm?K8Zgvv_0<^)*(V9*$yLY++RaC@Y0_F#lGJoAX`0sbH4dZAYMf2%MZ z{c7wfTnstx-rw7i-(kk}3efdGmh#)~yGwqbz8#^)%*7zoxXu{y!Hh<(O|O4jn{)ww zzrsBt{3<_u!l@@j*oE!YuPVeT&hhysqpKZJ&V``O^YL;q|M2-)!jVRPLaq&C#>EB~ zSS6hc&R4$>Mr(v1sF6+BeXMp4r4pqzY@@}l_+fH$(eGCG+O95P?lxU1%)73&cOg80 zVh5cT6u-)EQ3R2M_O2J(yUs$eN&sA3<@?+IPtgQJ0sWb1i=zjMN}zF{<092w)?MNh zy7t33O%s)!V%s@7>P!r4tuNF_!Aad)EfqE0NZ^$Y}Q$GTu#u?d^xxCU&%U zz1rTzD)aW_32Jz<`tTazqvQPq&}-i z+26#L54F=3I_o*h75)h)*f_O7Sx}o<_j)`gv|zzl8)LzrxR=k-?w8cKp|#L_U!%>p z<4&H+!$&0{iYt^8G{sMdS0#rT;*?qwkiOuV z`=58%u_uSXoA%_|UrBqi`tUvWB-BA>p@u8CfYyGK4#JMy;sQHzlb;=H=N=C5ZgZ1X z}=^kb-!#rBiy?I+PM6uLnn z)a+09mkR2Pwelh4Y^nD9RNM=4FV%jJ%F7*}@L>$VlQ;OXaWrJ79ZRvPAaE8~dH5tO z6;3-#W^vPYa};xzytq@1iUdfeyb+qY^A6<;2eAI86-mxt8Y>OdZRLm5#^4a6#-SW6*ovMC>Uca$-8ht0@NbQ2WUj z+fSZlNcnvPWTEiZC2Xl2RC}dxl>^12(&cugYty7o(+b~M`p|DnZt~XVkUoY9eX`Yl z^3C9?`v{7Y0d#AJbLMEK5*rgqLU+m>G5>DRy&J#EpqW;9FhC}Fth7|ZCjlFkISLDj zaoWx#8Y$YzxEn+D?xVObo#N$ZVbiWr*_l?>B0fekMOqqO3`CB_#p|zt52AJzPrb|$ zw2xwmjn_Uw&SVN|8VY(EUHvZ3#<7Iv?XV(CSlkP#wt01kSD?YgqzGkUOX+N!H^Gb* zFuFg^V^FmGx+{4iw7)3A6mzc&XYz$EUglJ2YVZem?vzAR6U!ouLvvO(>A21QQI@#a zk{&=mim~@Re{HTnfe-HYRu+&(-igY`gmjUlGqtM>z%sh%4GD}|2Wf)lj z#3X7{YT-r%JLv!GQ-pqWO7G2S%cKc(I9!IJEe13hiHA|=<7D_`S_V&UP?0;$SNpL~ z6a#kjpM6K``63S=6c5}6KfeMQhey(0{^*}~#ifncK7HGL8Z_+*`-tbDKc_hpn=u(k zIe=W6wfBkBQDVp1hkq#w7{6Vuk9ZdO>G%xBxBq~Pj|8)GF8S%$;kx>})%k7oVp<@+ zcXMhard%fckr}i$kujC&zvC2TDMBgBrF891-0p4fo^0=)3gNbvk(J)6LCtCU{;Pkw zHurb7)$Y*D3Tk9Ota1r2eP2gGo`aZvHKN|WaQz(^N+U>Ei(eO4Wc=D{8&$Gr5wswyGPr*$7t+qY^Q2n ztZCd#LZ!?}d(*_^$wi&!sz{MLoHO_}3H0wVcgeIJ@@Rm+L4cbM9zsrHr`GO$?cMv^ z=WCnx$f<5+vxY{B^+bg19xv%nzOAEi6Wx&DpchHR)SD%9ur2QWFNu=eWc`O?j`iS7# zx0!?&6sQm>&Mr25o6w{hV)-U;Cp4qPF_sFs(Qc|h+CQIMV7 z)%Af;^qkOL@-R|zdrszcrU`OeY@rt8&fVQ%(1P82al8kTtbWABV*Qq&8D<)xH z#4|E(@ECXRFhjip>*%k9NAU6Z`vjP}!y`PR(64G8p9v<ufhRX4Ocs4@uW44DqVaNx5e}!4l7>HW}y&K&+%kL<7^jz1eG$#m8fX2OOful<(*KrZc(m>R3+m$~5M2Vuz=wm3T`0A=$@mh!0)RpF37`dMEfz zU(i{!OSO8+qx|ajR(|`tkXdPE6Uq&n&5#Fo`eX%{mQhrB8lv0TcDP}gYHYF}?q-bc z@t7vmadH7yovD8e&+BO7cP<059wI)cP8(}=hwE^o4(G=y^@7dH2Pql4ogeb3b|jlR zS9=NXHFu8kEbQlz5D*@2v%PeAxYG&zM0T>{=->aZ`kHkfWu#_*_xC;e=pga~mf)*b z7|Wi&nf~UxAKc?_enb;AKFabk9_Ej&3mg`5nEMFPjB!D)zTdI-Hy@3{fN(_8lAR7c8vF&yH(Ds>aV| zug>P>CRp)O?R8oV4R7{AYF&amh6fJ4NSXE_Epmi)j5s(JPBhGhV+#yzz zi;2I`dKl)|2;NN2&J%9{=Lad|H-7zWsJfI}cND8aY2ZWTRfL;A$^@= zZZ5X;$Oi0MHKJ>6L76ICkZtWoef8B^yS-lf5Es(j*LY$_@_X-@j=>N&4Y|JVw$(fB zTKnhM1>)yJV!EBJKBAxIs9jpU9H~1Fo0}7{m%R2m?*>4lo3w#sySO`KLx+y|S8@m} z9^z$OFzZq?cKGWd9*eni0_$!8OvT_r>I3 z%^SSRq!HG6=!fBAt5wy@>alS8`^w~GNViEYz)nzbIEs&Kvbn%F|G`v2KVPlpFn5O# z!~Gb;3JbU$WQ2J<6!bk5r0wF$e>PT763jX9e}koMwco*u6<;*sL@;{hn91TO=`e3E z3`f!P?d_Cv-q1c2is+C^8dRfV0@bC1=7F43hB)VJd*?!Xr*qg&+kGR=&yC)<`7`-_$#C{NG`y?L zdE0#YIlyQ?tvS29<;j3}A|S>oQLP2$zXcO62z8|mor8s{{ua}!TJm&FlSPU zBEhdj3c-mu6)tePfc19AyAi`#3h0dM;YET1#NHEfoYTtOkg^!ln;6Yk3fM6BLa`Y3FV2r^7ip z-~23jROj9{pufVjS^P*6+CEUVy|>f$L%6|~67*>HAZ6N5KNIcgDsLe}31WBC7WmgG z79_qzABXJY9ujw;K9W!eP&-LK$tcAkT0nOHuw{7oq{?S)mo*AUX`r)7ta) z)3^O=c*Su5_*V)BLB?_6&&lP9kwccNbYUJn1*6zt`|0O>_w;H1cC}=JLZ}Ghxuqq{ z>xmpsSs1PwjFxo75}SM5I|;zlT0|0}1>)OOV~HDwc@wBjQ-~g3z1-e8+TKa11+Ry_ zP)E}Ai+UV|OSKQTr>+Hr=LO+1%J_6#1m<#`2x^pQxN{(vujL+)T-Kng^D)feF$de3 zphSAsQfkX`6qlR9F11c2qK!r6a3dcsceU{y-$PVFR~ND+hN0Yv@T>{Ve5}25xV@7( zdl?JF@k{qF+!q0q2}^P-yvH+?S$(zr^hvvT`jme+9hFVAU1AIXUCzx;XUPR}LatG> z%u_B!L?k?tvm-CseSW##;h>q%`lO_=80L8TM)Z0!dM)-3*#GnP)9KZm9a*=Aa(sGZ zgG&XjBWIC5TOuV;vK;av8OxC$SyUlnZU`ho3M3MuEObEcEQ^ubV0Kte@A9iBWx`d? z5Xy5?puZld=SsaQG)c$cY3D;34yQmIh{DGqb~9N$mU$j$g2{>%r@kUki&-)5-m!QI zg09hO)`REX;D(aABgCa*I+^cyc*X9Sv)TnS-124lScL0uKS{id*`(2*q*Tp7TcJwJ zR>dd>Vr3)U7&SNbkUZKxo8~&An*SVm(m#ZqwEh>>!aCyqwusB*1_qjeA6L72JAt2*G#POL@WAg_=rSdyb3oD{m+M(-vNP*FVGS(9Jvn!^04{N#ux8$hF@}z zuY9(G0Uo4#hBqq|*>USsfWhDhDaKHpH@b3C&C%m zvLD|Pv)p@Sn5aw#dz)V zZdk6-(hsc-Q^5$*aUOBIz<*DgvD?Qw-AnI4O@ z%(_sA%|8MbWOkFyC5ySJ>>CU%*f~JUFl3&tC7bUp9L@SXXnz+*I6+I$C6g38ER<}X z83ALw)J356DWEIidly}Tm?(6Po+{iiE_+L2evO`{Dj#W`%BZ}ty>k52dh#60ugP=z zMXFJ@nQsvj;}8cEE@(5KnBB{o+u4{bL%lPShm%c{4ykC5<&5S^D-(M#G#);#p?IGG zypW^OXE!>>z=j+{Va{AA2h#+N{1ku}M`@bNaJY)JR05yMJ)noVL&#O%RT_~y zD=NnJ%e_WPdrJT`w$jN1IxKympr;*HMl=}fY4394!w`&7ry5_(Q9nk>16)#D+f*_8 z#{;P*7O8iKG4c)-8ACN6%lMY6IdhQrW>c2z58!a(xaKr4xc(Y1LyhB*g(sGZ{C$#~ zml0bT`O)aiHKZXHw{xCF%{$aQd?c1LYAmhj+3{oCnU^2%k~@W$A3gi)Z}a#=9zR{L zmTe5`@z8aV;Q0`{7DYljK9OzjI9mH77id1>c`nd=@qYU^ZG+X2X`F6+J!S9PQ%6ldP$)Ax3w7^AoB9L@ieTinD@kL{x zWc=`8zi;?jb;0E+Slv9vmMIbAwM%7ECMr1w^n-ItvJ1L>NqCS}F|}oG-U$CfhSES8d{a#7XNt zL~C>qwYP()YJjD&mG)7m7wx0rG

Pk#xvHBVcU-oN-KJY#m)`He~ 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

Mi}38IE!M;G0cy?lX}g_H~)Vp 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�Y0o;Iwb495TMf71UBbll! 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%0AW&U#%#GL~_?Te%RalAK<_Qlcu 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

WJ;E`0 zoLp}?7+c#wnM7i#NbjzJBU4H41Tz1;(hhq%k4!{qtqGZ@=kycirM$1%R=B2brF}Ko zneRZvyP{)ngSWo~Bzw2_5+3Z&#GaRuiwrUBXw9ZnD&ovfpvRt(ZjjUDDy$w`0y9Q6 zT-JhK1fp1yG57z>nn<*t?->1RR3|C2oUQQdbc74!xVdS`z zjOR zW=w1#&#?o{@dRtzue2tB%pbB-Ku`C$KL({?2!ULum5TV_DdfLDk+y?b{KywIeqil! z7qF+I&Na?4@*OE~?@L8_nk9HzZS_%lxc;U-Ut|b%!)?}h*4>n0y~?R7N3ciQh@CZSz& zS#x|J@%-m}-(ecKW`&R|Grv^CBWng$I*8|aD#ED4p8VaZ;x;8lPltUI&fMAfn;{ll zniZij@_4u4Q_Y84xQSJL#lltgVD{PXE(4D<^abCRRWQfL$*{=es29`2DfH%Uqc8c{ ziL66?!`bGTZg<9KCM&zYXF4iDY97vB&3GbO$I?^5K@FySVTL6d1(4Hnk zy9|ERtE{yyqg|?9NmlL__=qc5aZ?G`{~5u2?({tKc%f z2Kb)nz;FH0l$XuF%$U`lS#`5rlWgS^&Y)mT`0Ty1>nag_!KJ760Q#mmKFmdCajOh? z+Sit8hn%)2@Y+OL>Fo93Fsb?}L+t1H+Q<>R6gkdH;KNJNF8TYp43bq zX*|L^t7C<~Sy-0aJ%VNx^Rxf16Ruau;&%zz%mYm^!#Hk7c6+XaE3iUtue_@4AQr65 zs2K+X{TDR%pM0}v3@gSt_h5ECJ;UcXS~|+j%J5HbCe!f{EYxs)_f4>7-eW}9b+3g< z*UU}EZ(R_Lk^}CQd=KXhzGF}fJ6JtJQFOe+`e2?T(EMs(@U|o zWRgBj|4?7RNP2_0Xh+!HWq4l`$=4mQ4#6=`;44R|cDj8RoWlj=uh+8H>=2{mYF37a z$YonNudl(`{t0Ve1dn_+p6+k%?I1$){AbAwy$v$C*RRR5^nqhK4%A{cs6}ho3QO@# z%0!%ulOT@edD@ z9Z4Oux`hsWkgWY;_;G7kXLSG_6Q`A8W_M#9b2?*v8qd8LYqA+ON|?`Cs#hf&GKx1C zXVx)&u&u4|u-o(fry;CviYK#N39tBl=PFp1!|~1@2ABMS=PC9{g14Mr~$*bDLD3U=FX!zxYBT*`P*PD5yqZqewX2Q+=SRlKIAwUTRCz& zb;(B!iM~HJm6aXOfKC@xFH`GbADXKjSZVw)an%a-Ww4@8ng5rNCBIHn7kLYP`v{p49o=jc5G#dn|1MhKv#6T#%W+y9&w=e#)|J^Q}zYh7#o zrZo#r`3|0?3cq?!_rN@gnxBeB6xaVmxeybUa=gwmocq$=Rz0ea6TOr3amdp%(lUF{ z!M+Di-<=uI^Iej>ez)}RM7r0B*>`3yjB1}F*>lzIR4nmZ)HNgX#^Yxn(&w*m-|ojI z49&mSZ)@nxoJw6bR`$(I9L%m{1Njvjxlq~{w#1P4qS+tEfiVl-^F^`mzeDlwtBDUP zhh5C_b@k(oU+LK&rsMq=TYs`dm6JW|3~G|$8Dpprc45+g!FklddbH&PYLIoCry72D zpXdg@DtjgFFZBD%%uuiC^2-1z)Q_3eubYR7NjV(7z8BmBzuzgE)uRC-? zgZ#<|i?hgjm5ct6z>5w^TnAB^?->?yZLe{cw&D;zSAJLU&^|&1T9nZT9$gE6+|zVF zCi7lj^yx zj53^w-7x@{x^rjH`3pOd2d+A}8qcAOSl^8d>3W&T`=b@@}D z(h;{bL$~>!nX_bJEsRB9KIW@+5L;pUL5UXnuU?qUuZkM9A7S?5!iRem|4nV(ORrOV zf5eUcec}Xa-M%o-n{|he>sUkT;sLtGtE8Gdu6x}nb0d-QIvsK=NsEWwNNuxT&U#yJ z-xqQvs%1AcEnN`ZtWQt{ehRVvA-kVx;|>$(8k5F>oMSx1+j8r=pO)jSJGhB%r{6e+ zsZCARaTj&eq0e&1E=a!q-&b`k*`(ky)7~q3gr5tJE40D4s&Cckt5eab-RkzXsvE7^t%7#r*ez*PmVNK z91qK!P6u{^w{V$iJd;bhr{DT&(UziJ?v!7OLIPfYii8$X?IU2wPu)VIMswQ zpPKI@Jn*rYqLmM~A8RUnhp(ZW-tmT*9&%{oN|@&X5{xxu5777+q`-@0;9TTwmH zh!XOsj-xAOLNdLx46WJvh;RpFyKno(l!UHWznYlz)Ab5NqBg!h{QXZkVHI+!$#E;p znV564>iU@a8hVElRgrphHutz)`pm=$thljTzOI&SkQXV&Q`cPD)o}4)b-1{ zEbD4XE^l!Z@1S@6Q(x3p!e9?4?=qO(OVoFtWPhXAJtoDnO-?SPVp`4|h}d$y;#M{E z2dG$z-h5u}V$Am4nBKSYy5^sSiMSmr+BSJM{lcR+-3?oU@|V0 zkT{U4cuGuVStFH&gF-J|F8XiE(x}yR{?gr#9TKORFD7BB7bTYCYCiGTSJN?0qEg+Q z)+X}OHQ|qy;mm2czw^Aed$JzMT0srJ#cgx|9vX5Z`kS&RnD^#ozs1?KU)4M@r#e+& zC*SH|?$KLJb|27#|0s1Jm9w{H^pBU8aWVliegTKYy=J|~rc+uE36LAy z`(MLT?{yy2d8f8>BjjYZq(P{V-QBr*2%h^w_Qhu9!_L%+xz+x^rnke1bzxckp z{d@Y-^7+~D=krv)gUKNjhIhe~-gj<()!8*uPZnX&4;1D@nI^-ZAEPLK*)88d(&)Y6 zcbbr9``;le?1seUl(_SB-Y=!Usq@~hZl4AjoJx-#CX1VVS&K54ODk!Sm7v5uO|r~* zyu>(c)jatet0@C2Q^dC9^*WzkWNgl0c*6s5(<;(a?xBQunkM!K-^&1Z{-*^yoQD(W zO7EkAe7^8&$mUySh4S(IhTO~x;g>HbDqx?wPc zSBrwaYeRUZ4a&#fX#s6do@B)fOgY=BqK>4M#s#Kl^upQ9G?S-h)#OxoSReIV*2?Uo z+4pc}HS>PYfj~W>D?SVXua)17hp!*+>}0d{ohs&IF;}66TdzOW;!tzTtrR!U>QOrR z9?yeHE!2@hk&1d^nT@F7AHp`h zj=joI8&1<MUCHh4PBK`#L*wek7cpYqD?Tm3UK4 z+m>D1`(C1YEzlLO$FqNo`K||39OfAx$jiWn%%sR!4z#gv5KX}(%@9Ya0 zmUbuI@Y_(zU3$NuRq7zO=VRCvClD+MjmPg&zI zD=YCTA9D7bkW*Lg&HkKROv`Sy6dSMxUN^|ERCdP=VxN0YTK{}V2zQZ%j&V11de zz7KI`|G=G_7ZmDiYq?V=LEFwMx{f#N1ulwCIE8`!rdwp%?SMslLqp2P*`=hMlTe%c zMP9Hxb~3}?HAGh6H(Ub~brz|dOiT4ex#pJhW3Jc(lo~7WR28xsZq&v8hPFz@xo-tU)E)0;La%iR1bwDDUQ=`3FS?cx2+ zE^nVxO3KL;YlVLA`b5LDw2afKNJp#mH)YI*d+o^hmVWo&j8qQup?=#8=@HBP!gV_G zKO~>kmoAr))yWNYKJM>nPSZ{Q-yNK$I~Wa@*e+9|W6lJQ*v0|HZkmu zs8%KZt_kw^mg?2ErBCrJ>qAPmW!^~nuu$K!k=kn~{X;3gY7w63=j=3jdbe@xuHwGj zA~~QfZ_9G?Pb1xPj#;f)Q5vSKYs~yyNH_h9XZN(Za|2g=NajgdQT)zJ&`e$eZ7;UYQ%=bKkrVrh6Qny%cZ)tR1-H~j-+rSB z`K#PhV#;j8{4P|o_o(N4WLTyaHHgo22<-l6X+-5IQCoP|!+7A<@R>^g*-}1cj+RbT zcA5g3a|z`BSy_AoBcEDE_SKd8lJ)7cFo9q4Y0ReDea5NzP)0*%y~stryK7YN{jz1R zq#rHH>&DwM#$0qKcf@M_Ts`-Fq5J-V{L$|Fa+u?B(8zA)!>y`71s&ZBu+|b#`VLT= z&~5gnBPc#Em#W6syJwz&e63CU#k`uDUWcN*K#uUuroSg3+aGeSA5O0>AFoq9Q6pl1 zH%lJfM{4)KQlLT}?xiZ=Y;LYa=7*|oq9=3$|He+Bk#FE~U+2BJ;d;^>PUXG$i8tvk zzrU5F`#Jd!@e!OSoA$=&Yx}+MTIv6_Vs6T*f!jP(+>cb0ZP_HzH!(n$H{99TNymSf zf~^Yu{$tXgj+)hO^EIybH5TJ^4RAPj%Oo!o^Wx@G>c4|G_{+Izha-83mvA5_*0!9< zvQYx#JcI6dKBwmI*t$$;?RFUfp(|<_b8v@2Yv)eqv5zN`da(d(Y`FEEm&c@~~bASH;&kd~VXzzMC)aekE{ZyU%h5F59SzkjCf61z$3;sT5 z7-j6;wD(o>+Ta!E=ziD21UBV;EwQUKmB4a1Y}w>6_3_SlWgiX1Vdjxhd_OZ%G_VTt+>ERW%4TB$?*3on6&T|-~9Lh4GMX}oX5!mgEJvKp3nq$n$(|N7wy zktYc=#_fsi>f*!cgDDNxXO7OA<)n_zc|do)Ny=U;YL9?Ry&Z9ze{|U=s~Vq~_gnj~ zUnpHK_#&SPoNb~)WLBF z{dLaNlJsZcDQ~5}>)HN=Q!kg5(*LaAw7UY?P zw-@~`fxD${^7h!Bt)jEt8{De;vkI}NEQL>K&YRZJS8*Sm%E!>eL!8AMIRB6Gel}L2 z?u_YI!KE3TgU4ec}s4jl>d>#K!kK!E0xFhbM zRj3ek(+RA5Z@Q;pZpJlHExy$TkW$hshHy;Wpf~(bSF~3qTW6_B=j61?OXmR?4ON(h z=?E;skCFQxn17K9KS8SC+mg$y5BiYs8HU^?yPH#B~_I?o4dZtk6ICJ9@5GSnUC(m*QD>KK)e4x*7r{4SNvoiLAvqE}YOk*5Od=1~IJ*g%9Za zKgE-k&A=OFK%I&h6meV`!q!Sq^h3h4VXUN`bEpfdw9BGz` zD`b!sqh!bNt2dRpcbfCDIQtihu~+G)yJJB$ zye-w~M?KI9oP>?cT4%@1_DQPeUC_Xnd|6dhoc46pBkA^JN6r$!ofe zHoBmcEyZWHJbzz)qDP`Jz4MvSgi*XBqwql=Wi-H4j>o(n2Oa9?eB7e%{w%9pcI)h3 zFvja7>VE|vs+QA{-tG?Q+d8MSj;Y~ZewlxGWzLfs_6baJvS<1&ead*hX_f51W68C+ zms&h&XB1vb4|jl8EPTBU-;{X3;vw~zY1MhYlz<2^i6N&SCGovl>e-+tb?J+d)1&_zLk(aA5yVGiqud@(@M4V zsM>lVMD;3+z;e!mpwfsqL`ub2TeED&y3q7{Rbm>2a!;LU$StZNsj0Orz8PkykM(=M zNFhFv=Dk=m@JQVH@{xWzRG3auGgd=>JE<=Zz)dPc$)@E!royDk%?i zyP#QN8plq*n2vJ2PJgR6R0D&Q#>H@s+jNxO1kX#z_zf>wnGU8sr_u}x)0d0qmDGwU z&CToC(bahXW;WFOd7Nv1y)O0yi8iM~?_Z<@2s>*U!0$WY%P;m99L#7ex9$Q@`gYyw zvzf2b#hxO4;$GPw-@>`eWLL>PDW*KnbHnX151h(Fb0t5-Ov>nWdYo#xE!+?J5}${f zvX(gECG#rD|44_z4UO#dT7KnSln~|f19QJSA$AhdBB3pkV7D zEZv}l!(?pEm236^7NmT>VFbit&E0vhQoJ7X$mg*e&wkCXl18DX+ti{}ZG{TErSp`(||GnfTk`X#4N z?gA-(17$-^G3PanxlsL+m#D?h_)F7iuII@Y=!BDbzOYKsCD__E;oK!EMs0P_3Kha{ z+o@gs(&oC;AF(ZBDi!X}oFMfney*jjS*A9Np@PtDxs{;iIf;n}%IBw^Laujjv>BkAoq+*BReMpwzfycP1h z32Oa!LCEk}p$;99igkrvw`}{`T+7+s+QsDRSP@g5 z@Lq4jDJ{!;MThh~k9BQGWw z>0MsMI&I9)z|0<59id3U)u%nvO86r7qP}6_wMi9YB4dP5I#6s zhR98JPArA8?86_QfzmmDs~Z!WcDKwJvwIL_upn=j4uZT|Bk_7t_C>L|ohjpQep zZcZro|fs8c-0292PlPnW6m zb?lm+r6)TfzgF@)-A*^X=C}NFT`<~{ic#Cy6{B9NoQ)}cRkoW<-TSE3yYN(vOPdV~ zT7mV+X%!BC*`zYgy!l(Dd!ck>9gXRluEVhZ*-Fp8gYPL}i3l`^`& ziR>y^>}m-SzvqTbnz>k(O){_k^1JGq$LGlPc_;r&-R4Ad!EB1X*K|Y8s3nHcfE~s9 zo#9C?jP9y3u-5@amvU82HYq-0PAu2HDqX>gAzx)-@f|z~6aN)OQhwUC933UyW8e z*9^2Zt31cySvtJQIG6jO2Wzsc$)&np@9-(!rC4{9^af^NU;fE$E1&xYo!(qu_j2>l zyI7@AX?vK4X1G<$Q*<;-<|QwuO`by2@e~h!;FL~)g?Hx2y3Cz3$JFY(##rxGiqAG(WzB6wQRQFDxQUof5OXk9Gzk-dj&6)@p^USZWc>P`UnsWwo|7|MTJtkzrQO^_CrEN@j6XdMGJTUoklnVEox>75b1r6*V{m3#t`#*Tce|O&Nc`Et1w*69z3nj_~#ol&2-$1ICXW+kCQo2`#TW3qf zs2w=juCWpPY15&<^Ab<-avkNDuja2xQ?XB{iMTE8AxPVEQGu3$3w%WaS?i4JGoGfZ zY)VlXxZcf~`(h5+YjTl#!TfK76#NDa?i1hdyY#)s`>pMqu0h#x=g7Ts!u0n4dv7hJs+}u)u@y$@Iy~VlNaq)LwWswrpFj_P;R2eh zcE68b+ac|%HcvyC=ni8pE^@ZE$Tw^%b#j#c?`D6&0*KAd=*6vQ+hLgBd&6jh*C+79 zPnh0IawE>9ZriEv=ne5+MX^!`cGXS3cTo2>#Jb-XH)xbLqxA7DE-(55=YCTAkRn-x zp&1dK$Md-g--3Sc318x&;`)~owGbgkx=@13P)fna<-u7Mzk+8I8#5(s)Fmj5L38_nj_WSbCthv13s#ml!8m)1TV@a_z}wy?%EgsbFV!p zkKhEk(r00EU$s@Dx%8boAUVJ5>jzLbyrNI7=kHjoM_q3Q3Y!~$)ya2kKiFR~9&5AM zjj|3S^065tq~uqM^Y=;qhW@_3XA+z385o7f-Hg9Y`qtitLsx@9U!^a5hJHC!g5QA5 z>)fw*t1yQ$8%x5!LXPTfI{9Z{dOPAAyVvb9CTIG8o$39)vR|cnT;-j7mRA8nbsEHc zmD%y1{K`&CNBcXb=*YLJ9d$UYdeA$*2QTaE8(XD&t1oA531!s~GvnssZz{D?#D3H5 zta=oivnR0y^KgRm-knZkvwP=vT|#LZqI{G0P&3qG7(zSx;{ni_*}kyPIbD9s?90pY zh%EWqu!G)ug3F{_Jxm|*FXW@6-ye|GPBsfaMT1zu>^B`Uyd4{Ti<|veem1YkO6R-= zwaGQkMH4wukKy~ON1k@K5-@t@5(uxt(qf{4yqh^p@fLd44r2_eqi<*I<5jI1K?k=xM9NMBQZ2KdtdKed&9u z$EiJDHo?0#hgCB5e6OFZV8(fcPN7oN;hjd|8TkKA5-N7azvwLA&GfXP_5Ms;UWY37%4F*Xy3huVheo}LBM8p(kdK*Z7QdCQ<{iB4 zVC>qLoQRX0iTiRN_V*oc>T6F~bET^LlD^<4C;d1W&-uFLvE0^A>X~1K*ym6i&ZVQQ zU62pe3A*HT*hET)N>R)7f-Kk1V#3^S>8DFXzBeZKua`7bk1x6xh1QtJt%MDgTcvs) zm4h}Y`w7aQcGY6*zX!*Rd?jUFY8aP>*;lx3`<~6<))G@TUZxD5T(y z(6juWpMc{`fZ?p5t$ow}*PoIF?#laBqc6E(8$!3U+>^cOm5SN)9k`GRHjWhO#fH%S ze(YU@DJG+RH|a}Px4((=;|Z8Tm&yclveX(a)*i?x`_V&VR2{CT8Zxj zQ^r<``F1IZ(h@Z?A%uf%f_v4o&xMuDmHyjW;>1YLY=|e(6{mPRP4he|m&tKLScQQ< zu3&+;|B=0=p%-k7Lk?>73dOZ#N~Pi~1Kj2r>d8Q;(K~Qc_o=?8fBF!riSZchzwNHf zlbA9}?VbjGd=y9bC)TjNT#l7`!(LefD6bMu+BftOc=TF}%<=9|}fGCr{lpg9hz3sqov2iAD7w#_5V-G;i@xS*?b80N_UFTS#>TGn zd0pw=@Xal(6!%9~a?&ohp>98RxhKW=S7{kM0$0E(vm$dlI0JJ_pF2wju*jVmSg)|( zHRQ$qgvV*X?~+FAcp=B#Q+n~&{l-18fm0>k-ma#E&2C4e^i|C}Gj5We$cyo?%J&r~ z#@~3V#^&!+)xN7F7Oat4xsfyDpqsU}eQ%wenh)jQ{E=+RC6i;b$%S#J%+q@E9}7zJ z_jZDHU5p!?g>87w?)pYjO>?M>ZjE2|`)0qN?Dno@t9Msu%}r8G9+txrGK;?c&jppT zqWHblrUB_-%g#9X?LFSx+wO!+O4wnP4D;z2-lgmL$!t|Uy-E7n-1}G1R4#K%Y=L#{ zF((F`Ke&Z9sW!N5JZ@!D`>Zx4c1`PY%_}=S9KMw=sB=*Hwx-$~4pKRHtSn1<}gPOi1`7O!Y6^ zw3(RyXHCgFu>aL``4g#%Khg;t$G6+lTe+UYyc9ja_2!}v;2&*x`|7Cb^C4|V`Nl@d zy9oPlo-C~6?B;M9j*~0*rB19w`x-jSJnn-*vc~3{L48MCil1K%EPGLWb}!-Aw$r3_ zqx^XQligOjSONXVSeY#|ZH_<8iI8PePSE?D$bFb0AJ@@$d6mu6Ay=*|%wi*?DGg@$NV1Jo%?GgB6(xe- z&NW-g^m&bW?3eI+cUFpgQ$4f)qZB`>P>8W<+xTIsb9bEX-WX!yza9s;i8rP-=W+*p z!#VEL1+p)`pwv2o0j?^kr6ne3JQivfzj5=Jqw*!~?@(A~1%3NiRsZSyZ=xrq4%gRY z^?wWHYzpo#m%g)y=lrxzrBmTh@A?5O{CeJjkQ)^C3cTuhAIHhrT3rrLdq%N}@ld6d z#_;Pb-R|X-%L{4n-;DY19VN{4kuYn=YHD9> z-xwLG5AlC~9J8%!$4#%fQQtH(>YLt%zx=`ddlfI|OL(d;s0}J{P_*I&8xd8H&%&L4 z%>F%AfHXSp(^aQ8;4laI_!~sr=}H=yJ7QPy9!=VA_u^lP{uz@Q}WDw*0*>qzD|*Wu2qC zOn|0`#Fp+92DVqErbgfQS-!iFD_S906H9nbGB_$vllbw0e>YdnJ<}%iw=hFLMupUg z(r$X%UUq?u+S#00lnD(||K(A-*ppIO>dxgxs0lDsw|_$xX(>^^b(R6 zYsu#y5H~n(aMx9LHil$Prl0tLL+1x?qJ}ro6&i9ON7PL^%{RP_Pu&(Za~jJ-4t{}B zc_9Jj_xyaix1eRH>*P)f-+v-Cdf~gs#JwT=5%b(%cW=`Ar zl1FA>Oc!`lp@VrbWFB`&O=;l&{6wwM%r>KYI14sOCVazm`E&U1_eza3StcF&F34Kl zh(=#*Qh1cI{$)%{(2SHxuL7kyOODgU>EC0X8^{AZ6FW59WV4eF;TY^Mbk^tk0>fVQ zMgHb2KA&r{8lu{-kTp_&8d-hZ$T+Ei|gnQ?9fE|x<4Z;y+NJtM|XO; zZscy>$t3ks*n%FA+JLgO$_l@CHirHmJ0)wX!9%={@2Q%P(74pdnP&&u@zUlzpRuXY zXWU75w*xLel{)wMj3(TeKW1ELviK{rE^c^VR?3v(-?NtApd<`_o*t(vzT^}L$ltlS zy7MD>2~}pHs#hjiL^-&Q;RPGg&Xi4 z)2QDwJ2hrNKfud?O9WgayU25c`N=O)Kf=TV~#4N@&eeUL+?u3`1rJcwZx(ilb z$jLU}*OEdFknOqs?D;2R=RQGB_$;&L;+!X`3*Urwei>avg&ah8VjdUUydHL>yc>7g zl%;}P47;dIC-;DxI<=q&gkm)%X?j!SY717-wf;a0Jfw?ja1miJx@+r zcRIQIAt{G=FZ{~P)^XNuClp3@5$|Ro7dBNZHi5sMpB$y`VsvdOB!*Dmh>K`kZsS@yj z{&vYsa-RRns|kro(4e2=ubde20VrHPhA@3ciC#6?SqDEWrfV$5 zVWAcDl_{x}<1Ud0=_kUs_zS)0G#gC!;|4-!+>Zh!WSWGGn~(!if*NLmJ~X94<(SJ5 z=DZx6TL$YwrrEvJDm^`BiMEG2JgN)XOy60|pO@y-Zj_l0&+RWibR{-uFSP1U)u944 zU3=_EHcm4oQ90K4^;pQpirb;ejk!gG4tr+K9)7!^6fU&&;cV}uJHN(*`KQwE zg{-`g&k*z`C)&SSIo^H8n~7TKu=}X5BX}0J+AMy+R8-NVdkRGLPF;4$Az2^YX0N0N zy-W?7!NZ>9FZLuPC_w+rEyMBHLW02Gd1dts4P`x?W||wWV>qG;*5*D)hhF!!%`qiz ztIIXTjV$O6i<=*6?~ux|j;w}Z)l54%Dof7CDAv={7tm*%1N#ZiU)SA#liSUGb2+}@c60EPp6Ca(mw)Gt^j+R! zDqm(t=|(D;gLL;b`OR;pFnW?NepAdut69)QcG32N-74c2I#C-)$5xIxD33;#V0Yr6 z49gE9_J1gSI|o;r%c?TcHn*V!t^xGqcheC(%bmg108y$c%`PoJ4{~!c zBz%gkT#w3-emnmMIjNVG=?uhGTTU3s<;Y18WLO0yYF2!ppcE86l zl+LVWM$LksJZ(b#$hRQBN&kCo*6nWRXYJqkK#pw}U)n(U{ZuNPpWPj`B#9K!LJTqy zmNb3zpeQ_=zazP#V3q9Z-wTJ(@6C%j#vy6-%c8|n)7HyW-KCeHLzrFsEmq|NF7!P< zU@F_0c!=IOFur@zvz&xWbwHD47|wxZJ!d2OaS*H@bOMzk(s_(3?I=aPggwk{%EHP~ z%h}8pxv&S~9bTU{di#*S_z_P`d8(n4bWmA%rQqD#V}7g5v6sz*dAi%SQ+`+7UsI}! zQP`mevZh!j}o)$|K1HyP<6<`DG-hH_Y!&2{GUMdKOc-9eZBv037EAnn}&^Pt>A?ExG6L zWcWAMRgQU8kC_Hu*IE9k$Ev5NNYm|)FhJN+OM*Hx5YcHa?E$SEpd`WsIT2+$GBUv?anxZ(&HR@)a&G< zo=&THp;;m1pDpDrT+Q#WBd440|5-`JyK;Y#XjV?wU)SH%Q8!;I=5M#LJz|`W`@@1U z?u2_()M1nz)AY>CO*rZ0Kk6c11}Yx54z+Q@odxntDU_ zR*vWc@}g5|UyI^q?Mq=g+mqAie>|f@m18d-GQBq72}(f@PqKyNd6Q9*6vN@(LfGc_ z8trD-EHH*wzY@3XtI=W!Ten3I5{wthLn0+jNb;*_%+sX_%$jFHr3Ra=9t{ zI(Zjsoc3>$mCc@~OYj`|--huQV%@3a8z_Vh_JpRqZL0ZS4svQyEvIrn-HGSdph~Rc zfdjjNzHy-`bEOmXIYjWUwDu6#s*>)9WQM%Wo9+Ex=2v}4n^}pX|1xL)1p15S(n7+{ z&g-DdtMksyKS>pA32WtLmpibm2~*ZXZ}|w{#7GV62-se zTpHdvX{Yn&P0VNk;XTz^euL-bYs}7>|7EC#%)sz`{>>_Fer*8*52>e<{Hm+<(rD1Fj~^n81H&X0`!H55e4GQw8EP(4F$@QeFkqAv0$ zd{h%GNGJKggH`8`vy1F0sS#c5C+4;1Fuj7Sa~_tXL%~!tO8DYJvi&J>ui{xnBTOqd zxf8;+t4%P>zx1x>ISrv-ys!Prpg8b`v%kuY~%C zbg-itElp~d;BBT;x<3^+8HZ&1RLN!`uk4(x5m_bG{T6z)LUY3PdYh*p8K3E|OYq<~ zk;JtWvj3ajqZ(d$VD!s8s4|CKuF*P;x#r*kZnJ^V;zuacSJMmJz)x`i9(jTc=HkkHe z{^0B?@s4k3LU}Bzm1m~igI|2s>HU=7>4%uOI+!|S2Is_sFyF9svL>x<((~F6Va& zq7{8o7v0Pd9{Ml!Q%95KC0(Y&hz3!rTvPBZM`H>5;Tlkmp33JrQj*eb-pfg_&2))0 z!(ca4{k})-2B-^f%ZHW^@vVh~_cgfYI3us968o`HHlw_fNRvYMSb94%=ZMUkGq05_ zTnLL9V#`qQTD_7pA+MLLvh{Kb%fXaB;O8w}SP3h3w(Urlxxwegy-TlnE88WCRiZBr zY4d|=S0BfY2dg(!7{l1i=191z`=$N?uFogFdlT&`& zk2BGhxjRg1$4lmEW{x?ZGH^`*KCL&bnW| za%Zm1&vjv8A3;C>+RIuW#DO#~IgVcJPU^aul5H0FdxI+IK)7FaR*850i1h2zH)4%X z!^{t)FW)1T<#gVITR5Cn;YE+w8C%YLSC=B!ZukSz6TX!pUYVb+R8X89Dn3)S zY;h@~wi$;-ywxs;2Ke1S(x#}5A?@~0xl&a!>N{Oe@xn@t|bDrxQ~YP64UN8cAvZ`%j#n2!L2%u)to6ioP~dK?vR?e-Cpjq@)sApRq%Pd&wnc{ zOPQU(eERzmRU_9p9p7>;mT#u4@`ri3cfmM+holXn-fu*GcrmBU+iKZiOk87}PuOcW z9+oi29rzpt&v)KQsuLYh+o2SsGidc!!;w-tRE<4fGwQ10p4+wd+^kbU!*=^d>e(J{ z&02Yl;R?BVT)WEZNt4}icgo+^DpM72vRb{3A`@u!&yX`loB_K^@Mj{2pOv^+bNx< z_k5naNz_%h)3|&XoT}#4sd`Qv{mbhuwOMV>`p&PRp=h;!jpOZ%*_u=8C&Knc#}sh z?8K;3*oSXyby3&$JB#(3W2?p<-~{O@Vejd3Rq*(<=Dzj<&tf>-VkX}3Ab% zRBx;WK5{~`F0Q7(bbuo^QiZ)S-SDZG;zmdSFCSun2$7a6 zgaK4ZwRqn9QN`SoneA;1!CIHhsrTP}d=8A^)ttAZVyB7?Tc`La-ms&T3r z3KO1+y;$ou`WcSjP>y1_69)5rOtFpOZ8*g-j=DB6@#%t$vNS*8cI;`d=#)5(eD2qk zk@S)N-=6NP^jk~qt^bX}uLgx*hj?~wrUhCA+xWulSc-RL4h&^AlyQsN--Lp!BNSz- zZJ41${yzD$TV+;EPTY#M?4sYe$0WP9=xlH4Myj!x1-h>4f7omnX`rL9XA5MZzr_jo z3LHLc7;Wez50YSfcf22dkm8p?e{dlTZZCwn0`zT*^xzAmmn=3>w1LqMiTaQ-xRVSX zyes`B^RV>q<;;U6|B)Lu3GR@abCjyGAEtK?pH|pC*CO&(r`hP+PzQ3UUmEVfsI%Hq zHF|3D;DLRlR*sQdc^jnSF9|+1-6E}^RQ=K~id^&Rj0A7oU=zhps()Lz?vq&|{XX2i zp%2-RGtIyMsF~tLn*4q6pt{hqPSRLI|9xNnE0ROM!n2pB_`J*gztXe(BtFX&`oeK> z`dLdo68fjpsq-$;H{BX>qMM8I;TucM9APF-*;!m0>PDw}j&t}L-sgxMsgZiw7od=V z=dB4}scI%%o7GC@OOjq~yYF;}Iruu-hKhEuHno@I5=rgXP)YrybExKv@0WK86m7h_ zxVoM9w_!NGiu&_1X0&!RQU6GG_^0qUJ#_<}TNjMm&2d&wIaK_cvJ!(J2S-)LezF8} zGESCrnWO{in>oT^d;JtlPROS!hpj)JIft^JRW*D?)hK8xozA= zGaUN%WmY%y4NtbPR1d< zkLlSTcz_1g;vLh4((4l%s1kU?3~5HY({i9asoYpMI)_1D`&nihbyi1s_bAyKkUt#e zVLWu$RD1@-@YuMUU_)*OmEDBc_m;IEYAGbGO)@L)V4SQ6{jA_-{q|EuujsgU+EP+S z=iiAU?P67Z4h+(!LH$X!m=97yMs9tb&>8OEm;Jt=aV}5KbCKS73M}MNp1e2GDq}P= z%xwcC63$EyzS@acgKdC!#px&WNg+?ZzEGeRKIT9^$JaemTENk) zra0eTnC`$}ETC?EhugjlrPp3qZ$)P^$8>NZ*7|LCcZykOaoho1F75<7-JXOsaHgJS zu?wXi&ZZH4!A$nG8a&PuObe+N`mAcPmnd&{S6j-hbN<8iKJ=!u;Hkmk{!W}3!dCZ2 z&SPnvYG;|~@5g?-ysgg7-E*JFovrA$9Yx!@A#brtP?B5m3=WBlB?^YAc&U3k*Q|GG z(HMB^bSda>7uA3npW(X;o!>olkhElDtHliL1gvdu;={;@REnF!UdyVCiCdgIKd)}G zk#sY^eM+@Bdo0IQpMpCYWY@xbHpc!Td9geUxP3-HTt;iySipU*q)?xY^ZPuroVo0& zAQQF-LWQ5=D z_FiZ9+@asE#Nl=_ZQ6IanbO6=M$mD%x;tp{KgtW*u5G5x@NEpSvuq|rZ81Mz*sZn? z3*R7~zkd3e;=eu2e?zeU5AmTd=av2epAhi*_EL~88LV=>F4ge>U3n+`U0`~;)#47VGKu@K$&E~c-Tj+y zW?h~A0li8KUGiD9D#QH+kK@VKQH~tTEANXspH3xA3k&lr7q;_m$GaKB-1Z#QXP-TY z4dVWgu6#R>Y@*F)&YCYk0o(@y_y8f}@8?u`wjSHE9| z!s8iNP(Q4rF-oEL92jS;DbBzetni1}gQJ{ZmE>#}ceQs&OId2W*=>I7=cfHqc1UFN zbB%Q4&!PTNRNe>ln{+Frj)=6dFuj_Bdw{v>bAccLuPr6$x!mBdr z=f=|y&VkFX=YFH~*jX*k>%rr8g4F2*59m;sRhUiJP);B8{Z*znx-{cz9=By!tNre< z3YlSp*+lrtlm1Jabk7psVe`e`aM-h%A9uOlErIu&#Ltp!E79Qfbq`&Y7SM(}xt_nF z=L!2%>#AdCxXJ&ALGI0L0@3V*k-Cw;{;@3o-IVH)dkNacj+`igH98Q_MMma+cxC&n zK3Qw!ZC0RBY@Rnow^lB{W~>`M^+}i8-y2jf&v0daR?yaL{(F44>E7vE?ybNJ=#W#Z zr%drLJ%axo&6n6k9XmozR>9w%PQQO@#uKo$SNZ$C_5^;BhEe2KUoLrLj$htNcGL}7 zBfZNe-rPS}fp>E+<5l_|Qd1i9vztn(lsjOSRL}Eb9%&ai_({%a;0Gp#TG60-yrW90 zT_;I#?~>@HY79+`hIuT};jWQzw_6>n1pD|xHsEpIRm12+ucoUVjmv8a|82k_yFELD z%Jd;_x()E??Gm()cY^-Lq@4u058V6Nar@sYOnB%jf}UocJe6>pz6!r?wZw#-8aM2YpyJprMm^sx5=vzC~SGCaX?XdQ6)C+?gND4fe4~Dql@_#Ecnc`Z^SY(`m6k z&AlkE0etd3&#^KbCnQq$<9DAy?fabR@^c=iYIb8KApZSnrN7kemW7flqFxV@cBWY&3bEB=71DZjOECh;yih z?&of-Azx#ln`4Wfy}HixIkje!jJhnz3BzPbZ>76vOhfw_6rvEeybx0s_E(0k_w0f( zF}-(|8|+#7rLaA@zWp`>^#jXz-j5Y^wvqEyJV5o>2UN7tWgsQ%Haga7Y3VwZJ1Ne- z;t%{etsTwTeP*>ECEg$73hsjG4at_r>#iPfigF}KErz+~VoL6#@@>w)G{<-Gwap$K z&B#xxu3J^e{ka`zB$h~=Il?_w$=MlePWxW6`uPyE=VCV3BtGU`*)dPZ)+t8`ehpk^ zZ_)kqR@EbqRt}=ljK};wO6gB=7ssWYm{!+QJlp(mGj^$r9igKs7&q`@{7NZRS+c+l z&eLBR>29<~Z93eTc`!4DmcJe*>`lpB?IIt%*(^NQ7f`_4_YHI?Wo-3`X`Tnmx;^gR zc`0$oEtw_PbE-uAhq!;fk?Q&1AUh1D==6X^?T!kX^kq4GV?t{!(lq+&8>NdPWs z>YOTr`gfhgHR&DYGG80s!-Q(CZqf=%V~-)yQ&xNJAeO`IHjvTft&_P)1+j+g}2!g4Qjp9&@UXPgF?M z=3fYUllSP6|8(!RmY@;v>}8ZD8)WwX<&-vea~1GdUdi<{pFV3nKDcT|K}JvP*+ggJ zF?>-+KQdCxQeVfLFv~=Fv2N>UdW!3~$LjLAOqXjEl$8I&@b_~|{7X5qP%iCD zw1l&ZGd0##kA32aGL{PTipxz%U&E_I0)4i-`+W1`8<69A=Gwb_?dxQb{zP$g9G&(3 z85>L_=S3dnepAvq`E}nw$A-ESS4X7a+U&34|MjF$g&LhscXcJUChTxd*-|}v=mPe4 z2W0duTVf_sn6HkzJa+QQR-}F!p793cVYdp=)YP1f6Mh&1Ut8jK8(mXJsLk2(50B6R z4vjlO+j1#91u^*mIu_j86=}@UA!HBJM(xROVj?ROcZ~&g*)YDR#U_br63PNA5D@4d z+#%;n4_ipfQdt)De0guHig~x4)&I*I4)^o$s1?|*-c?Mi;ofK`{peXr!N3~ql0|Yf z?Mxn#xja8zbv!R*RKSZ3$5G77tR%~~byjB96Ert3Qf9v&Ik}_K69-fD2L0Z$xCyKb zHgmGsGSxhCgKw-Zt=1@hi+cK-jgRMm;$3Ct2ZW+O|yCm}2pHj{I#ua~_9G=jrKSwS80bN>sy?9stsjG36^KCVI z2`*U~P8M>rZkJe>#H!v&HPs6WJ&sEEatT$}^Sa(kDK$b8;S2PpUr@_0v?2C;sXXQI zccDYAY->n+&-hLrm*t%0@A6&m=U?h#e!4?G&3nb(mXsPXm$ihwW_f%Pf&aM+X8pc1 zu#Zo^0jK;~5yhWnYWkF>rF42FI|nw9*194ECk=nKOB zvfv_nS|_qSx~zL(0H2ln8C3EoQf6oJVqPL|c9BW-bGPTeTo^5AicW<*kB;@=Q9Aiw z;&$GXWJPwOa-0qUC_&>`7`5?>?b8Un$uG1lP5B#&^rCmhecqv7{-#=vvOQuAv?-{@ z+Cc@raQIaf8|7wPOPkq_uca*I$T*6P z;ImE>6EdN=W^Mwk5F{I7=A~C8nO4Qm*}o%njOZ)I`A$g!TnH+q-^a4k_(PfAas)& zI@B$BGX(h&=ukOX3xgp-b~Yz!#6Ej!;w2qbdA02^F8vDlmRr;Bp(0wVLkTH1>r6|}wrgPqEb3DjUKL$*EBc39oY{}{|6yzDnUY1W^yPlUd6s~r&7@0v z+GfU&Wr>^s?+a-vpFn2Y;v!tyY@-;>0kfEXENFFp`VSXN)1P;h%KR`+bFEb7Z|Foy%Kz_erhJd*{RFE>; zmsZm0c9fA2G^hb*9moywO!}`*UquMve2Ma5;(tDSy1hW_+(}{g=dXF^>9ViSe@G(H z3fNZV~=Bb-i^MUhh$c~reoO?_u`etvWM;3 zgPiV_rlnt@y0v54?rZ5kazxdTNYd2wnJ*#nF(>D12<5@(t*#w4cb{ipWTW)Exl{D%4=Vjv4^MC`pBj&&48;S;pKn`qQi z(#qI0m2EF}P!-Je7WdO%cC|aHY**BXv(bF$z-2n8UunKivD0!6KjT_G+IHPy zS&5;!(Cn}wEJXsuWWJoa=EOC!a6X~@9VKDw)9lQgu6zTdcn;^v#$3q@mO(8&Bkn*u zD{r5=a0^|~uArP-S0mykK^xHyXFM72b{i+eI_ECMELYZshkE+7G<{lEJ;S4Lnt+L@g1pezk{)Rp{iuj;IW zR`oKcc`iL%@Gl0fZ!<_mAGv%t*%BA-svkI#12g+Q&(fvX@T2x8o>i2xvqsEtd^01D zb856i#L$;4p??fpGm7-{LpfotmTR%a7h2715?GVK#7o1$KT36-l`B|EBaRlVxy&j{y^zj zP5*VH1pP;Gc-y2j9m-DO5AP}A=t7E;h4{QzI8AnA%})CNS*$ou=6%?8LkY^y(5LQ- z2x@Iv)4kQ13G`B%%+-4(Dc6ymnB*G2Ms`zBm#4Ya)>A4}gK$*E$SjTti8Uj>aUAZb zb<7+5I&muHSgEKvY6=Mqb+BCATslVL(TUOL)+9S8X2u-M?xX6K$}6wtCe49W`6HX< zKc8$=@FrdCeKd4+RFQ6Y<8`$1KZJiTwPrlSfvajpvvYyWzI$}MjWEcMNa}upyFO?% z{!P1(*KiZmXH3Se^2g?Lo_$~*{63?-IqXLLUy{!6wyfRy);1>6j=sel6?KZqa-oy7 zoC0GV?^x6lHmDi->L!UWyKIm7>1A+W-OOQkt0E=rOss`(YOZ&_%{JMuG8@7$`kGXp z;#qt}c3C*(t=0T9=^sXMrQEFUzn4=xvWvGu9}BR+7w0uIv7{u+NRMkt>DNzY(}d^< ztp_P6TUc4Dax03J0Z|qE1T|0{NXJyUOrb~0@=Xq>t9g%l`^SKnCW?P^*j=5=j}msG zzbAj-*TgXzt5kRK-PAOHrd@2O*c<7Mq1Simke`v2ml+ew!&kRf`sjCh|I%5NOg5?w~`>*z^3HK3rA4iz8#1%KUc5PFl?#T!;Z5y;s-r zB*kR)tcH|LJ)DLi&cn@cnvbPLRKuZNfYS~h!oiY?+eq~7EAu00aQen%_}lY}`0#J! zR(XQTZn4CqwGz3%<+`0;@aX@~V1daRr60Y4_UaY61Fh*NZ=hCxh+}!Pw9wxo<`DXU z0k$XJLCL>H-oT-d`_-#v%8&k(QigZ|?6$oBpK=`RhP>4Oj4q)VxW$LefX z;bpXdcSuV3#;kHwC1{Nk?*py73p28kJ3HNdbh*EMAq;RQMQ<$?Il&t>BjT6Oz!28^ z>jQdrLUv=l?O0gx0^ahEOrwAJ_VRskVe>&y++^~S_RhVWt0!>i2k^0#IO%fv%|=N~ zeFPq}F{XJ;mW4JCYjQAImF9PSochz`!v+lYpP~$?{KNXYn$crcn#15@Ir#s|diWr! zYrmJ`6BG#N(YJ=Kr!?o*WE}QJnwV4VwpeTjN4S+AftTK4k_sB=(96|~J=NWMl#u53 zFl@Y*Q+2AeuQ%*X`%U$z!hhPsw>Sd3yo4?w%l!Ggw6Bd^+Myq)?M_~5?yT=`yqPlX zDG5zqOJ?{tcaRS2O`TPD4z77RsaqhXOC&UIpa=Pewz9jFrUfw0#)Z=;uwIa%^A-ix zw}AtiQe1QT#BS=^{}gKLV?x&Ty#HI$_NQ0oMm*cjqbqEVnc+D;pI1Z5VBlrX+Jbho9v>y#X0&(I^h@&}ToWj@S~4u!V((>Lv=_bg`Dj2SVXCaOS~n(Fv- z+!|;7r$CzX-_-TCp5)6LacY{Fh1mxaGCsH3Z^A`+qGhWXN{S>{&9$0rh%Bf=g-c+c?YP$V1-3POH)mM3P zyOSkk`ktaA86#)@4yy3iP3p&BMS;ifYg&F;>e%s;YtO_5-^6+I3M}IjQ*&)u1!*=H z^_RJOrSr4M{QPz}KMQL{O=b<4=IIi8HPb7O?cGBU^eSywaS4NvQz!Ul-7|FIp1 zvg_&PM(PObo7PXKq8UZuwT@fsb6UWk=`b5OZ<)}bX}tFL=5>~-wG1;EdXLI7GESF| zd?g0t9%=e(WwQLlm62E2kacdsT_?ZtUr0l)H_fRIgwl3?`>@l=%_hdho9Wau2!!GATHKS*KC+wzXvUT!x zNgw+Qsucbs6)al@Qc6m#$l27lM<}pLS0|>J+P{dIEJva83H#hS*ynbWWbvT(JP*FG zG`@+08O>;#PM6AkhZ|sNoLfUS*uR-AeD$YNQB0$v2yD*T&i`F7=+EdT0!!bCE^ssq zE@a-W^ecl$tEJ4XLOkCr%CQCZf(4XmL-Njo%;-0}LLNe`=&8S{U!fGDH+KF{k{dZmHb;+ zv*=9cC8a~H=tI9>0(VNUS}E21|1V2Eua0!53|>@}GNe}21K*lBIqmcGYcrNb)UA{J z*YmkLu2KD-!)@k6)6c{QUPRflly13B(J4i3?LX=y75hqgmUd^4trhhdCnOqBduH0T zJKRRh15xW;Rzgf`zrJsJ=;xEklqt2MKlp7N!SUIx3U=uMPRh?u4y0wA zMSuHF!P`MAMq%30`A?Z${QK*vgL?`#{dRy!`gZbApzmsy=vybhmYqk$V-U?5q|2jtA%>o4{?ebPE$zs3+8%9qDD3 zXd-F&+q9+Tz&SAZNl@Q&v!+Ny9w$L9AA(y+7Q--U&eux~*bsGhf6!dqZFAn!W`%?C zX|GQ1l`LPz7Wq@~%mq$Cf*XE}e)=AF{0lN)0u|ru(2GGdXmfRjYw)EXQSLuupK|dN>J7~s68xsP@q9B)RqFccZl@#I9=?Fo z+VR;;#dwdz>U8ukjPozNrXSv%xf_#|l2zAzkfj&ttCEhAt9CG}SWWqkJ9j6~N_mMy z0S)g;TN&`%agf`)_#j?~-u_7Uc>E@Hbr+5G?xG5)iHgQakK7EW)=kH z&|&*(JemfzyB^SIM|Jn9&g0bTyqrecj+jxm9CmbeE}kAs4^A zO25QD=3nftu5GrM#3i{SxgM8R4eOYas2#bp7PR(vOEFzyE984=)9EvVf23xHZ&ZTH z%Q>x8&!2sxttr^AfXX#Yw$R6n=O=7IOMA5{vKfzAAG7jGoZjz{ZFxgrU^~=~btE4S zbuoqbGoI-Nsrc{7Yx?a!H$fk1n)hJbQ+gHumhg9GV=u1vR;NlZ4|?P6c==pB&%@Fg z+DeHJNcZOWq<%4-mV*hN%=g));1FF#S(D>9{O==D_d+ihP^hTq8B#me&gwSqrHri| z>qyD8a#Y_#(}G6+BsmZxDL$`<<}~2I9}f#W4-5Hx-toKTn<}zGw`aCZ5uiPp+fdbRg zCA#k${7W-w$R5obAW?j{ZIEALjDMl3E}v{DP5fzJ#341lypF3LBxpEa%S>Lh)dg?B zn*V_`r^;_T#e8@L&&d4fzFKMD#Kyv*?gWZ6J!S1!qx;Zq9y6hjqwsqSGF*egC|{c5 zT_(P+-0R=@cXubRu%CO0^q9N($!n(s3El+gZACi9scPdYNij|F$7j&)F4Aq?i2V)s z;aWQr*X0jQE=u+*2uiAy6}3~k!AOGU@DXfC&^{a=72Xro*eA?9zv$a)*q@aS<+~6H z61eEK(OLQ>B~e8Q&bd*=d4bfpQR>~*F{Nz|WNI0lvJoXzHz)lq*x=u|)DAYeKZQeq zEI_C~#d(&;&Nnw{pGkK;PWt_FchY)$2|}VS=a#e4P$q2>S~bZ#co0jsO6trfvIWXY z7RZOb4U_({z>T>lzXYX0E2@ZIcFPQg``;fnh1$FRD?b0Yg0kLBmMP*q*z}Fw&9mOk z23nin^>8P8H+AWy+sT1HT~cI2m~s{_>w%(YsjS|kS2%1(+6j`!((EhfitWD_>hX*j zs|E(^lI=5&Q_B%2N$aso!D$C&*T+!SmIWS2R1L zDE7qt?L@ysvQN;L&B&`Q^ppKqisA#JM9wO3rjs@lgb~^tdz`B znPoD|XQoo+){+6(fJ3*rZ!Q5Z$)(ZlklDo>?qSO9mw7HeY>0Y39M(4$+IKZQ!W4MT zEs&MjaMSy#$ri%@9=A7llb-uy*jNL}M9s0q2{>gg#X<*lxtl4m7j4P8oWDb0gg0V< zZj)SeKlI{bNb;A`{W?JZyTJx~NiR9qhTS1Jzu~ycv2Nt6u>w=%Vcg=oo=w;At$y-H zJi#A19r&KQahi|hu>Z;Pm_qpfXo0BIO2qA$0fu@vFNJ4-X zN_d3a0D(Z#Bv7cWmymn&D0wvx9xb*^8(Va02b5}O1`Dm|RD~98Ri?!mv}vW4QN~v7 zI8_-%r#h&$cAWYc`~SYR&pG$p5GX3uncsf`=bpXSUi-E7+H38{+WUx|r-jklAS`MJ zaCSWc+e-^=I*1k-#5`sIr=%h{!)f6QgoCKl0QM6jD0u|72i4HzHl)oUzTb3m!2s-? z??o;vuIpTO$9rYq@KjnzLJ>wkl|Iq>9sQsCsZ1E}e}*hyP~)2-G* zdtt_K`G?1;C%~`g=D!5DbF9Gq9JN@XT95B>ZG_EED|WNGukOP>{SM4o2Cy=GFHR2b z#r^90aMS)loGv~DYttuSZ+Z;3#OpGif+l|nvi$VbXRx+<7OShTV>RWsSYx%Wv2jOH z7*@KuxJRP^J4w?Z>r1c~F%LURzYkYEfOWtF*g<#%`t1nz5st#D^*Gw)1m^06kXkcf zt3D4qlIw9_*+#jWXcty)c4J-RCD{F*#<{7pu;`ott3~RO<4((+!WrS0a7y?z*0o;2 z%GRrpPj6s!rpl}`8~P($mR*)xmXDib3c*V=FkYJjy;+7;I!*Ih+%$Xu-#T~%=U1O_ zS|E&_T5>`y?rppg778b^j~T{&x49V2t$;m9Eq0XqaJSVC%pCT?F60Qt+DGwa?7>AR z7oUO*dx`v6FitHIT@Z#g$i<$I=9@6?QO|`1f5EImoVJ^RdEgw(1j{fNj9^4k4U5(W z+z@*s_B&%Zh1ZQ)>_Kq$A?#nAfu-kJ*fqQ{s{|v$d9ZMbz@B6W&S(vQ2ltjffcu({ z!t(4mEbd>%4VGsxZc?+Y**0v(m%tv7Jlf#Nx4oWJJLX9)+mliePZm7_*>hwLZ3r^v zhH?9R?%V=T*2KW$ryyZ6a4%O3d^QN)DTI_6KpUUA#)j-@g_X)HI5D~cJpao4LiD?X z;Cc%dYzNWr3c>XUF-Ni%PQ!hPkKp^BD`4p`4f_qdu%B@vNm2d{kuw?0`S*9JtcoRK~e!}zHky7?8{uo{CT zZHFz{EBLBI414nJj--oV=b;+wx+h@4t!l@q2f(|U_B(OPQ1`^W=!^Ss|I0zh(L;_t zIEM8eU7l0;DvR4wNk{65H*oK?ns15J3u6|L3%lcj`Gv4O*8O-Mdh(L_x+b+aTd*Fs zLmOd*ptVaM^ve#+76x#C{=HbS-HTbNt8WfL<2->q^kdjX*EG=D=Vj=hGtzdiVqg6Y zM;F=n0#g`gP;(uvG!3V9OK>~oJnV}t!Kh~i^g*qst8}Y(V9Uis!+#N$_%GvT|1-ibuX_4Tw@4VX^xOsc3knt#g8yfT&WoXy2GK@^ z=m`UmL1*wi$%v!t3eg)5LJL{w5d(`Kp}opR=mCfp8#DPwpogXjU!8$HZGyL^VLY)5 zdj`}(F|3=nJGw}7{y~xcmdNx0(f69KVm|)Yt)XeEDKjXVDh4aXc1Ow{6nSZhz8b)u z^SzGN8bCj|7bUhAXQ0fxs5`rLKaQXuA4D%^sblEDgOFy0*i|}+)l3UIdjRt249=lM z&@ax+u~GJ2b1n2%3;Krgq);@(fN8HA!2Ql_I1j)h{9l+?~-X#9FLQ!jr-KL!A@x>T6`Bq9lOi+;trU7o)$U+J#@6}7}iAwAsbI(UG&AWmteSiy-gG;D2$t}BaQ^y0M zM-O6jcgWMG#~sOZ3id)T!S?brZajPi_i??7d#nl}tqwv~p^bKrlRfik*pV&4y50Ho z>VRksS`fViE8)}7re|?_J0GY03$eDh(a|J*PHP^4Ce5E+FuM?(Su#6mr? zW79oH_Zi(!bPpMnQF27|@PPE}i0Cv6H>PDc{n(No91%{|d7#ZtkCVB*RbrQ9VC2v7 zz#!zvNlnRaE_Lo$2j=Cl~tpSlp1EPxuq_=ytDHt#IVgnc<+*=Y6sba~P z#!sP$%UghJZ)7N#iL_PqxQPaw4FB#|Plypn$+Bnn|nWt$yD048x z?{?w~eFk+)i=<6zZ#^pVaY{tSG>E@*ysRf`KGe0+sp!%U$|y{8T4` z24_g!uzlPH%h{b68&aa|F4+s~tbMpWm}UheA-)&dL1iLzr|TVOG`q`LM+TAEJp)dn44LgNha2aj*l04KZvoW z1s!KWuUQyVMyPYCv!Npgsk^wMbZWfpMG`jxJwHGl@957#=<@-zraCf&0^^AU^{6eK3snk`U>;Sr=4!T6Wu zWEmou`3+L1Bi#t~Ey`*+vjG*(*ZK#oAHhli({SStVmvU2xnqQ56qd)giWWgj4RUN! z;KUcLV9Dq;BD3a*=$t{(4Vu!H)(tpmS-l`4dLD%?$(ON>B|3Oe#>SRUe+>v94eE6; zt%+q8sCj2lbm)Mqr`BUkc zQ?Pe`3ExXPjdieBO3q?O>~(yH;kSR_dT2XVMEhXPy#sf&=vLm1UEBw-kL#|J9>GfK zQJgzJ?yS(C!hO*0j8CthzK#{t-_BB{y2pia!YkwYXxr_4at7Mr z?6qfPWw21jJF0tv=#?|FrmABM$e38iJ1A>^XJm!#pp0;1dITV&A@J4C334Vw$6tb- zspHNX|6A$vFm|MKu_s+Huh7{In1lU*GTa`frI_-qBf4vb4c?mIBeM7OGFJayneVRd zmC5RDH`eiXU|nc8*6&ch@I=uE8Ca9gr!p_leoORNn2`Vb`+t}M)cq8H7hPy#J-qTZE_|C@Dpf7rWudpE%s~LLykkH39XJtKD!nNT+)vEE08j4}9iu(-=1pZykO;G?)%Qj_|%< z0zrqrzs3z;^3>s`D>Q)wN!RD-^=`NuQ^WVJ9}3s$9pB)>yFY)pX_e{5a?A5yo89nN zj~{Nb6Twb-mv*}0>I=h#@9cKN!wCP-IdptziyMCAi-()ied+(&Ygx-!zGiJfMZuh+ z`9-q|W_HXj#ZO;gg+Xo>+T(QIjg}>*y}t)Zf+~i}}!I7nK&3XmD{G zzJDZfC6(EwGtU7aK}fCyrv{8rQ)^?7GJD%@jYs>Gb(=vZ4Ol7toxN=>o$**fTW25q zL3ssIdRx1C`U-{yws&>5Xhb_G?gYR}VbXouyW>n%;K{KK+w)6NEGvp;2u3T{S5($E zRMo7B)>T&2)J0USdCVRIjtW(7Yl?UGwRLq?bj9Mz{Ji~n@6KvP^A1&eY|Bk%0EXi?xlAbymQ67o8C%zuNLoKdL!^&Bi;u>m9aKu&KJ+V zP-QPX*NW#rsB+UbWiAlUL7>4?CZ0z^l}+$06wjehWg|R`#B&63x|O+DJWqrw+u&Iu zo}}_gJXXq0v(DA4kr>tq{*3aqEE_5$_4KM_;@%7LV1%TiSYcQ&pyX4K+Wh zQm?OCv$UqBS_M0Q0{%!{x}l*`1#hKcRchpVa#V1~Y45FH~ z+ShGt1QS|TC4S8n<2)?gV)U0i&T7!^>0)GX>G*lkbh2^!SmYos zBt1;TgMkykvaEjm2BXWWYs!(}@@Reey2@yIU1hoIHYcR~9Y`sx+tby4z4V@mLU|Rc z?`!Pq@71tL0<%I5jV&5bAOLi+o}R9rn&#%-c%L#S3ot_i>*MY5roJwSo+$zbTQ~MK zx~P{44BQP^0<%z{xgoc)%X{LD%Dh|(nIEd?DOuCKjl}}{PZiX5mGLR`J@$mmD?pbT zWe!tM^NN(OfChrfew;1)#rh<7TN5midj$u^|Z1HR#C7{AOk~J>5O=Ey{dJ zJVWHx@~ZlVI*>IX#BB5Hf;f`ts*!a_z(N}^RV4qd%pFW|MPp~IJr2_RYbKjC-0J#Y z7C$k6lKBWRm$gAokx0t?X(k&zybj_@WYW(vMZyH5YZ{^rRZCUA`AX)J6u+FTMvqbE zk95J=o=uyVHui#eKbE8{4XSSIXam}R2+R)E_iw7-j-J-h(6&jLX9b!uQi>fa@7dJW z*VEXueQhtKXBT=3EAex|$wtp@Z&v1OQixpCnH4ad!kNF2`k1w@|Mko`%4%85@N3pq ztu9|)8EvSpk5-gdS1&EESOvM1caLDa55J=x#*b!tR$4PXasYHRJ4V2^VqN{4+M$Co zEa|k4*RoCmCwoj^V@pL%)tah?rRDXN%1XKaO@MO}py=wg)eTh*E6^<>l@X*kb{)FN znzgHyWvjpk(ty0KAzHb*wgCecI@heNu2u$JRi!E*?>WlkyKauMQtJbP_CuZg>qDtuW3*j^sHU8YE8}cYg8sZ zk;)3tty*Q#Q#A{heGmhVk=jwJMrp?!HCj;Ssca3Hr^W~X$>wMXTx01H`It)=NKp@( zs62Wo@5a$lU%sreysoZ%gBnk7g?8uDy|%uxE>gJ+GAp7c5KvvqdQQ~N>*=0Ex1;R~ z>8=7hFzw6fS?l_y(zmRty0W2ab)`Bk^j=zBvs8USoJgs%e6{+bX6%9Bz$)xHVYU&cOX9ULdT&!mWQn5^ZNjs|Rp(IX-dtGH+J$g4{JGnlmW5XAf zBE2bfT8GTgbUO=}ou_RO;Ag!MyNP(UZ%rk(&dSwNgL)6EbQv+TYpM+1A(A z*p87|Ody^g!%whnH0l9>5I`d%e(L_ih(0bFh188T_r|tT9R$8(QLCB77#ojwq_+dh z?}5;F+=y01Y0%!)v^gHb01hIp1-`g^dlr?{7zJkzceRlN|Oxtzi*)2^iRGOtWJd3mlX1wBc9|rqsgfq)st~Ft+9YkIj71c59NW{F!PxgL#JwEtrHmzX@^5Mwp@&8* zbpn7Z0wA@$Q%xHO;4=UUpciPCMR|5*w4#n3u&O~Bt33CTS*41j*(Hd?*`2ZPdn1&Z zUW91Is%BItut!a=6dwg~V2?F@jrd*=pM4cPsyPcqgPc^x9URnL3D=CDfyVDdq6Gx+ zJdml>%vmVqa(HJ>hNA_Jq8vDiZ}nlOO`mxjUcq_{4#D~e99WjzIdX4PGaMDnO$hN` z65<|)eE?zQaQq7S%HD0d)$Hx4@LI%Y+Gg4-rkeI~ zK*l+_8JJ7YsYR-qR_jQsGw{x8rqXXiajz#R4-7o3GY*vu$a*pXmjmX$fXtppYEZI3 zJdIqgegjl+LFOBwnWKTS4Y=31zEVhVX=@yww!l_Yvo5D=7zlSN3a6e)~B;0%w&P8wtCxM!9vwx1#3?PV06^e*Way-$H@NO!2SS=ZR~q-q*4otSfR(r{{Ic&0^$G1$p4GcJ`2`J zC_BJkz&i_4v5E6m<5NuOX@JVwqS5v^XKlteKF4z0kDoOtVCl!<*i6UQ9mv$5z;PP1 z`fCu8bO>za%L?@JWe57%%SZ2R`fEfwc7Bx0fx=Y2dw!INlS%*;JWoc=PAY}TC`O4} zl+s&)@dAo6^*%UQk(wc>vs~1vk2x5(48iacoBAv;Sg#M97vtN&V6{GTK8$IN$Q5B)9D zoIo{yE(&$8N`3hAz_6^R@w+5}gB7L@FgjIxFBUXX4}PENa49I`022EJeC14XTU)eI z8Be}2ER~c>UX{Z%T{S^7V&8wdvfvD$8 zx&G;_#6N_Fbk{%soC}_&<*h*dVQWs7(>1t$JMBSeprA!-Ed#+mujsb{`)apo&;%zXLg%vau+`SQ(~FW-9ROP8Mc z3e21N(&x;41x04Qba^vhdc2u0J>JZh9?pCPgEL?4z|2=WF!L1@%zQNfGhYF4=Bpu` z`O>9lzI1srUwUNbONVdfORqcgr8{ZnOMo-;)lNP0rQ4nP((TT5FQ+Fl^QF%>^QG6B z`O@jle6`1&`6hxAv)n=D;v`_t2rPN#ODviBio==t(ml+~mteM_JM-0O&diq{Z{{nQ z?#!1S|IC+uXR6DYug~kvd!ih?gDM)2>Er0nT3FjJU^fgdc$8Gl1Ng zNPE;_z?ctHV3+4Aa5@G7R6E8z_Wk3)m>`w@I^sE%o<}@Xdh}Njx)1I|8riOnbrf$d zYaKLCeB)D1UXYL@PX`PlUE=Z|+c48az^sxFlH) z9V#y>BNSr{>660>AWk#}%`F;08RMD7eGH@{M_l|tS5zQqIM|06VjOLQqKFy`k9L+_Kr?~{v)m-%W zH5dIp&E@lYnu{>M=1Qe1ilT!mBmMngNy~Zwzri#JzOHCXd)KDM_Gk>#fL>)}v&+vw z?=|+F1Wq~@BXa1#l<x=F!JstO4oG9| zfHW2qNMj9vG!_7*v4&6@)1{>`U7j?iN2D2;(rot`w-9#y3W3#KcL>G4Zr`hC*a=k=s9VSZ^Gf~*5?efVLr<2n0efOt|)_R0JJtk4Biip1Yf zZnn>}L}zCHGeU#rDGZ)dna{b1GDbDCekFJ(ftO~z33qw|6N6&^a-EreIYeX7v|l*f zlzIYHNMLShYHG&wl_RG_j-e_-scQ-7@E}=wq4IevD_5Tr) zHDP1m8WasNHZZL%l!?Pwwgrya1z3#=Vgt};*%tV`{WvtT53CKN0{0?SR|e!h|{eCi^cOqkalc=CE_{CY+=V1 zC>QUspkv1tSStSG$Qig5;u$0^?AQVk@t#0S47FnmRHpm@HHRIW7_OPOE<`gshHK`I zY4H4EtA(ka7O>`i4GiS7P_wRKvW|sX;4E1(O>EKvA>&P;ibn&UGtz;YZU_FwxS4XQ zx@#-UC9xm%EkhV0^q1t#e>DmbXc6z*28Xu^yzlt&zU$y+M61i|VCe&`Rk^$pMlgYw zj4vZv=2HA*^{c>X$t~-;{>GTj?t8{81fgU}@dDqM+^k`(+5!_&o}{=WBfC~@fr-)% zDu{hP*QzZrNnlp6!Le!!6bJxa&1cmXm@L2y4fJf)0#gJGCQn$k1uhd9c-?Q+7AO>G zZZOHJEpWLMGC%0IY70yiR4fs(C$e9J^U(@5Ds336w!jrB{|3?s>MwyhlBlq1OLI+A z1ItpTu!U#^EI*3Gt!No$bm znC8A{FE*=SDs4=D8Bqn$*VEsG#kMKT7fCf>8g&_*FrOKA-^3`T&N>8KW8c?-H?~Rd zrx48~0QX47G?LE;mp5&$aB^>9OJhYa*DX$4FL1CR znfGqQDuJ7c8zn05e)uDfKWN_j-cL2DyvG3BpkY>M7Q+_^(d_#A*!F}hGASTiOk zTx6(;%szK{ebm!*PGcLF%SxfCFx+9dqh;LCsbon`baki~+c7f4Y+DH-VZexWrJO6n6YDGJ2aQpx7V9 zfawwrmfTYy?Wn<943^A(*ll8k0IP+82MunM{s9wH%X*d(e{bXepcN8>X=Ub?LAHck z6K=EfJ4i1i=G-i|pVI zL%hS;rUvKd6+^OG)?ECiti+G~Ux;eo%OAR6otH3ajZ|U6h9!^Ckg7|yJ2 zy`9rQ?5u9cg0(+DAd+4N0G6)Yl(~+kjQf}}`$Gj&*mpUEUs$rZk6Azth9#H#m<#OJ z!jjQ_%qXzO9{C0$-i1yy%Gr6r+cNi9A-uKCks5sia!q8)86!8{!#FQ>yX46Avh02! zA+PM6^0u=7Qr`BM58^%MBlyWFM}#Se@F}2=W$=r15dRfAn80u0$aV4Z7&QzrnaAiQ za6sqZi$sPN*O-+kEUY>E+I!+Hnl!!aSi2Dg34k;v(ipF4+e=cSyec2&9I+^jJ1PkY znuRd0&@h;IEYq-QfK700Jjj5?l`&8cvKg{n!)0|%8O75Dt_oay*ltj?U*RECBr4+gwU4YL4K|Al^SsgJ(>4X0VDHQtU@# zR(Ef_Kh`x<>?|*#gDY~UG+RmePft>H5q9%3?c#69Z=C*FguK~AQ zZRY|56HbyY&dF@^HlqNPwT7I`w)DyDAV}<(c$l9wN{BeDi3iZ-_#38pIJq|Dtaiwf za`+}~$nD%CuGY{K+bLE+mME=TKZ1+{aGg`W-(9G-Q32 zh}8~kCt>i3xUhEdw(^jb$AJy#<3EpU`Oo88=RJ=bNSwb7Uc*uZ>Z@+RQ9>2`KrK=Y z=!`;VnNezVb>(Wz!=g9@SXBWLvaF_VwVd9>IqJ$V2a{<$An7i!hn9uAkcLVtr**L$ z1()-aq2&U{M9hQNVpNFZt}3)5%zaTijf;I=F-PS&))hPfS*NCjVmTj0-%eYDBl0Q~&yl+s(pIobMG4Rb1gS;_f z;F}``zV(QKEx5>+Z!>^ zlNd43=NmE5>x>xa^hONY80a2m#6Ylrr(B~s zBL;fB5rbg5BL;f>BL@0?BL<(>8!-^(A2E!IwsmdlZ&n4Vi8Y#Rq!-H zy`<)g8X6tl#53io6BuNVjyB_R4wX8QVHxnX;Y@bwB>KXLhQ%f(np!|WHlp@7L;b1L z$@J%rj^YNC=1vNn)G16ZUj^7SDz%Wk0&DdPHnA*?v6w6*lu@Xu(O602xl&^xy_O2N zM=bORK%caT;s9VrbjvIpqg;R_vNMcjN10Ubb{y7No}A7KNoO=Gg0-e9`p_>gkZP$i zR^F{stsae4@zEWPEp1K6Yqd^Tyg*u`6W-#S;5TY6l=K!vsC^l!j(HrF%rHLQ!<4() zV>~t_y2kjVP9A#^`k0V14xJ$hMz=QhwkqQ=<+rVJ7~gob6w${z^xjI zmP2rXnA$yng(tWyV-|jGW0jhe@!7*oe*v5;H4YXURfu6Y?WZP>MBpI=qBQdnK80hq z%W%=qW^O+5S{iA~MqYXVY7GFTP&eZfLA2cEXsyq(u`UNEe2$!OIjjJWvY)A`??O!r zkbDggjeR#};+Wd~0M$cJUGXp+e^1AwaQqyOX-~ot*b|vHDDNd#zKHjgSR$Ri2zg$K zWOgD=Bg^<0PCH$3BVbhsyo$hSt?(i0mAByi7@$}3aPP0+n6w4)K8xDrBNE9Iz|q^u z4+3}wz!@juz=^$b9cWp5j@==IF{;(f-+T@mQaZp6*h>&OgY=qIaP)A~C(*XII4NSC z<}Wefn6%u1zsG~W$c?R{>m4|A51LOnjl*(aUo?nJKJP>Th>e20CtVHjZ&1K9NkJ&z z6S{oXo}+h!)QlFOp-gXTEU%O}f;j0;CxxjXx>Ikw&Pn00PT^XOmBG`CMO{e+W+fQY zsAl1$*q7mwBP@ZVdv{^3&=!xmoB}cS9fN1C$pfd!17weX6ZY`>^7llV6kp(=+Lw%O z6k$-9=02P(E!o}|4 z2GLffqxmxlKUXp6${BDiMTBV!;UMFz#(N6@S8jsiI32gZ@k=;nzw7U@S&2x`X92nc z&}{VCt4;ti?H>3Ldk*>hAka<&XtWr@(u;v5OkPh1*}j$Wh%%R)K9_7RJboMCMNc3) z`&I#VD1-}*>x?kWZx#>?tu2&IlNcyt?FM4^H^k=#0pGM$3rvH@j6F2=z z#1aZ^po0|Jj^`?(4N3oAz>EIdN&jl2b|{f1b%J{AlR-2%xjksc*e8qT;PN=aHLQd% z5R`)G3y86v1v+gEAktNIUwJzM4j`{-_rO6>v|HXwW@aMqn{?XjpEDQ1->Ll>fjvu9 zQDIU>((xf@9snzj$3ldf*w>09w!*DP4kBLA+y@2@S=6-?I~uovktu1x%%5Vf37QWe zIGqEcuK*i2OgqP6Fji8j?>G%u$a)&T^P7DWPCB{N`S7Ld*&!Ne(uAXwJXI`sje2n^qg8N#k z9vp23T5V+&3oR37OB^UOK|=d|zu%>=LdTxtAqIDVT;_$rrfI;P%g-a}xxsMv0C1pQ129S>rKp z`%k*vjrcieByS@==xxN`KydQf%|+g&7EG+&Sl2LheO+2GB(`V1jd*4e(w(PrO+(!V zSVL9>O2KL=%i7yIH=~H+Qz=Wew^F1PASe{AYpBM)VO2vFE}V+G z^ZZEV(zVM~LGX_J3XGgmWIZOhGk-H;sFVtvoriYizIp?;%;V1H`(H?K0KvF%2LoZJ zd#K=@`TvGQtQ0KsG@+MN<8*$n+{hKYD}Nr!Zl}m8Zq4Gsf&8t=C7dF5F*qX5%MZ!> z!FNkQHfqZ0Nbqj)<_=#Pnn0=GJyN3llzLvAqwm4nC7_AtsW+?={zeL$5{Yl>Z=tQI z3jS^WzoYmnh2?gNvM>L8D5I4Eqn&=agv^0|Sm1U_%~sqy)Y8z(`-4>Q!Tg^AJ3|tv zk9Rb7<7UaY3hozh7^TC#8@h@I^8Xo_*=R13-K@Pp+XUZ}pAK1LTdP1pb2vm5d`Ma^ zIJ9+D=zSv*^}IVhK}Yqj@ox5GTf$lr=J4#whM8)pD7H!3@JICieX)D~=$}Rw+X^2KKo^mMcjXOp0}3n!kWK$8;6k0@@Y{ zc}_8VFu%p+8OTZwSK*E%mb3(sKP)AE)+^}}r=)qjzBafE`MPjeiud94M^pr{M6_fMp$KgMa&tAlNCOen`sqVGty@6&E|`n*x=w zoqdS}buREQZq?*TjDTTxQ&bf8#`lp{H~hw5)grA4bWS9DjK3cD?2@-I z^&;MX0&f@b-o1@@bZWe~hnF)6&U+E+t48)}z*GrMFUysib(&Jb>^5E-&DAkCe|C>_?<(##a$Ve$-^Spd;n8hVEqQ zC4^X5jl}QUd*m0RgpQ_5dg9@87=HykYnF@`Q4`#60ux13ZvrV=Z(C~7+g~^T=Gjc{ zZC%2#(66MH7Z@KqL*Eomn1bdPzyYSab@mp+YFkG+PFXfvHX3jK6VM7Bu<23~$h`@> z63Yj}3>N?V_c!|ni$7r-Ip-HvfIHoRE!7PL*L+{>FZwqyZxX*9+^!c0Cf*i3^bh2>bpQ5o-sP4RZcA~p5yj@Xjm%h?~m z_#_SMD_q}I!Ea~NKAwQ4P3_$(_#N>F=!fsS;g< z7IMMw2`r7U#%&Jl`vS`V4707^8S!P&hirZzzA$`Ez5N|3_(SoJf*&VWg8weQYzbAt zSHzbCUwd<-6zWF;$pxfqOD9HsKNkNu_@P%+@IR!+`3#PC;6}ROSqU$605VYgiGZf+ zD5Wa+(`?!&PJ5D&AIYy;Eng?IaW}~^jzO@7UDU-XJTj!5Rs*y^Td32vXHbfV=luBlnj#wy(fY7Fm70I`NEh z>+3Iduzt*0RLiXp_VLQ2p8YVUJ@<@x19eIlIk!u4gefsMnS<}q#dZ@N5;OSTF`q|7 zH{BY!cpWnzXSers@V+r?p;tjH;q2WGsa7}M+|`4<)6MZ7_uk>)o-ynz0c-_#o)h+d z2{RpQ;|kc5U>{x=%z;%UE+*9$&wC}zrNcMADay1|OD+&2d|Z%Qh(v{<{~p5wIH+_BZZqH^py2PM>^Lh1wsqysn<4|Pw4w4y<**jHZUqu4v+PFB zml5ceeQ4NC$TnmNhCU*YKv2Pq9G2?bs4AG5Q;v`fE7+WK5{}HFHY&lE9Ln8Hzl}<; zRbU&&&ESJ>s z4(se-W8Qs8X#_8sR9YoAOC&1|Yrb0>d(f58t8tHDuvOqthTO6bwuvtT{Flh?W`TuW zK07zN;ASbe%Q3?swC8;l#R*v~)OipjF-bWxeo(XF3-)2E^F(I)mFNT zh{jB2a|FQ)C8a{_dyJF1%w%1#ATYjX3%0{&hQawyAkloOOEK&6U3!l(S_uTLsn3b{bvr=be zY~n@7UVUS%?^L09oD|>h*64$Xc$772{RkrJ8qq(rMo$7SQKQMp~2$5SkO-%|g74kZHmRcQXZ}PP5)U zAu23zE>BL?xBUe7B~rtp^!tjmJTrI-N2eIbm8KNF4DsckbFLXP#eu< z9Mt*HJv5&~L-VQXUDMUcjWv$@t2&*F<3+l_&W4iZIHEL2rf(wC3Ed5j9nv9Y?D5Q_ z^pR)W;K#^0dEjB)Q;v{6GvJl3@kOM`WBJD8XOX5ZF8xD``|YISPX0B#=V2roOmdf! zCx+YGHu38nxLddbgB{~IJ97cPtM#{1iyxzJjfa82adO&9lt|O@i>!d=ucz6xQ*~mI zIJB%W_$ZjI&#TNctjzo2b&z^7zU%3$TUV}(XQ`4tiXf@yG~~)n5aT&su&wJ!|Im7_ z1YV-~CwIW>)>FDV3_oy_eH(|Y-uCDyPARAN_Vv(sDRea%l@j7A)>h2`yt4@=MWO1S zz$-xgLp-IHqY##QjEhHa@A4t8c)>~Jp6!=#WzGFWS^yPw7mog%+lu(Kf*j}#^qdA| zExjBR*fEve!@WYqa@KwVyQ&HP!}bvqA5oG-Jj>i6_p^jBAMs5djm#uJ4{@$R8C)tF z`%?TX@R{7LMz2nZZLQOnXd79w^|=Q&^w>ObSK&u+sU55E*xD9W;+@%VKPz40%O~BB z={^2Gj+1!toX1I*lho&O(kg=gSWc2eF2qTz(M1Roc!@N-W{ z7CVP01cn}WF^Nsw?;1SE`TJj1U@-w&iP~lw+b`I+ zg~mb(mu$f&UK~k=Ka+k-g@}JEZnMQEuVuUjSW4fm9Ch~h#e28HHV?=9j2|GTz+=5a zc4dVG2E*l~6E94ww;QS3!5c~-N!-kX?^x<)-pR~^H|or7D^>RTL${HF&OJh*9XA2l z>moxt#d%NFXhV#YkPz9tzG6jrol40v&LU;px#NDB$qxO~ODtLPjVz(Ba7mWXq#Id6 zUnSUoBTMMi1B68DH`F&&u9j~-h3#3lLwN_cZiwS17Q2)-9`LQkO+Ky4o=tBch^rE| z*EC-rk8f7?9D*zXDSIwGq2SgPUHyoBHGMWB3;mUS4gG1st(aSN;af2FJo+;j-i{qE z0ro0;KA~AE)mJF{+E)Ox@R2o`bKQWCK_#6WI+QwQ0S4~DrEQJ9_>9-PfGW2Pb;dAL zmPu3USRz=wdPsm+TY`amE-M=GfI4~}qc`CGpzTa=ppKr; z=#Qwh_u{n*k*Kpo!c+45Xu-y4rb^68s5gf zS}b#A^}$MEBwbLDSvc%Ga-F!X5!^*EMl*&A+eY@!QK*rfy|YS6lrg3Wr5xD|Q;8gM z5w7~_?Aoe~vD}q5_Z+=_R0Hz70lmq8cbSpPdYO9$Zy()vE5@pMWU8YWRc6maiOP{H zH>DY_a#{eRI*T^xvj{`snA?@L0B-1 zVok4MO1M8KHxu8Cy$49l1%ABbt_Te8(dk>ZMw7Ey!fa|0MQ6h-miV$*;?PX8Sn6ic zalR~+Tn?vxmJQZ2zK6*IKVbV59xvCCx`S8tcKO2D$KNNhX4Z&%~KoD7ch$ zT#>OjPZNPj7YN4qpjTx~aB)h_BhrMqQlE)HU=l*ONuz;KhT;?yspPbb1#l!?4M?L>kf)Co>c6e56lSEUs#&9%XAvE+De@yI zRg#W!j0!R8EQeC43h_%(6vZ;a%w3m?NOPDGY_ORMBf8mK?E`=R-6G z>z-@*yK2zw^dYxvrRc7OgUpT)5GF`u#A_b<(3Zyo7M>rv$W(TTKmx5)OAahM)N*Od0tXQ--4uD<0~Kj^`=vVqqps#E-; z+V&TOwNBN-IL+nZboN(&8A!6B<#3U$WGxp=XdbE@Dm_QQU#!yeH-7qPpHj;h>vkon zVkXrZ_&<>$xZkCC*wv3CUHv%9E2r?yXeSzEEDM(HXio_nlL&({^jZnbo5VZ5{y;IE zn;iF#;O#2G^!hbBv?(y^}6&2{x)o~s9wq|%U4f;(`b21dH> z(8cji7}_KgbyH39!}UPM9Tb6gsMeAjYiO79w?(3%9I@wec8bf{=XB$X>9H7|dV)fD zJ?ILMgj|)zPDyT7&G?R#1AFMvm8MRrzuF|%um$l~&Ls1hHoLEt7)1yDj2d#~i#tx9 zige$CAzcz>xFpK-kBO57MKTq_E)_<4aG}Cm2n|O%X`zA6;*+>JsEju_9_uhYI;oXbaS7b;q3)|BsK1*!1_J$FLap}O#i zZE{wDm%p!-6Z201hn3?AYG^fWCfd#4tRnL-5|>p!kB&&JGz?_{w~2_q8xP)q0Zg4g|J68X*DfSMkZ7`w0NS{dNAyXM+}5r>EdV@ zjDz)DIY&1UhFaR6t6S9>U?Yi%{JcFJzmgsQL|OTV4~bcc9_2U; z?1;FFkdqi}_{o#z_NH{VH)XgTEi*x^#MIpxnc`@b+mPfdpUBJyRl@#B2Uh7^)TF+` zc)qD4na7c!^quQBg7e%R2i^oQ|A{Q((n-F1Xeni*IUc>@3CwN5%cUJ;iNT&%0XU zN-DEUi`zPz+WTYi;_lwqW*m`gx)h12;R@xl^^Zy)o!F8?|~&MeAq4# zA5Qdbj>kOgUKFNnlg7q9LOe08J1sq zs0`!xquYzjHO*$}gMgd}JqgHXx1U+_k~!+h4dzpK*ahaGDl?DWu^z$iO)WCdzN;B1 z??a^YGILw=JMIDe6^XWc`-3aYjG}LS2zT(%qjyHi+#d$zy!y#wa051MKAD0`u~*zP+y zvjtnRMyM!szZqU{zvn{-%#CL@Ec(O^cbVH)n)y%KtL$QP^OJTlvf5yu+_1rJnOWRw zx8UF3nHvIE84EvL9evKc)gBc{2RjDQZUM!Ekbg=By)aMtzUGb6C2<6q%H;^Sz4nIN z#~t7|X>fd%(41`4Jb!I<>r?hvS?2$lHe>s!>->p?^6&6@K2i)OYl=HoXN-?6x{R8oAAa%k9Qri{C=Nb!pR5XEb7 zxP9MTJh#bzBbE8|aFrP@!%HM=l}h?>84K))J(SQd>flX?x?viTpM5b)jqPjybBkHf zXU6XQ<*LME>Q67R>t$qpa4<+x@D&#r)_Uc3(|bP4R&n%&hh1 zwkJ0{)H3~Xv-gCVW1lg{8UYjaT`cvT|NGQe`R<@cY1L;Xi3fk5BrZj_AX{K#KXIcv z%5otwM-uUtWplKV{mX1=Z1i3mhUCjuZAVdr@F&p1ByR z*_^T7EVFlSnNbZsFY7ngyk=(GWsA)5@Yr83Ha~y6U9hu%fq9Q%7X`v-`3AIqKz;d7 z&cBJfjZlC;pwYExpfuF2>(}FKPu@sk{+lvxeLnWXEl-u)H2^xA=_`L>K5@sPF=lwT z+0j>HFPvFCYxOO~TUysGZoOr3%gjY)-+D9r+M0$n=D3w++nFynl~OMkBv{C&&4FM(nf!U}${sh@= zrWypSZs~G zZT>8?`$;og^!QZs)*=kT%gnE+L-xB?uj<{fXF>6rT=S7Tip+1TL*}g~YM$ztX6}Xe zjM`mb?z>~9`CC;^)J?#X=fH9Nf}%9g|@C* zYTmlO-He?u^NRu@u+m{+gb)468xONG_hbN?Z-_1)qJC+tZjLar12zi$2`TUg{~QH& z?9bn_yflx_)1`fv^bKD7by0edrplLOQhW(ISTT-XWAK=4H;sz;<*C>z!+2QtBHB+T zT+TfA{7pMl$}&0)i}iFF>YBZhHkjr}Fm~!Us_na~?<>J{cGc^5{rstK2BwTbA1r9$ zeBm9-SKBwN`m5p}p8i$B8At)q+k{=#Tr9gt132R!R9&nK5f??(mW~1d5m+?ZB4Sok zjoEN!fpjLq|5yFNqEfwyKg=+9*cq0!3ssxu)&T1f9K)7p*>?phvI;uVvh0edv#j#; ziY$8>90+T5aF+o03bYt-?+KJ=W!!C+XNB*w5UV#UAHV%+S>f`hvogw`&9ckClx0?grH_C5Re&h1{PWf%{{caF9&XMcC zaJHrk%H5QfWv#@Dr#c@USCCaO`Q@a~p<_wlLk=OArq`liLP*%RD&igo?gY4zb^{4K zT)M5gJt(K zp9wmnO&vN{WG!h1g^_F$h0D`dXO$tWLw;L_!C0D(EAVvs?$#7-=(_A4^KR>|VBkN}o-Tj3{7dD}pr4Z$hN};Ug}+68+$;Bn zD>r)A>Z}=6StU1Q&49aAek;yp29}3uxmV|*JR(Q>vhr7C6|Bq3ugoeyg*@IYbMV~@ z>|9#b0*7*^3e1&Xl~vFxl7lfd^c~{Ya*O%77tDcI8o?{&={F0)P2xrpfDc=DX>ar+ zjsw{TUH@+QN5Nl!hc{y!fqx;~DBs;K=JC~9;@cGfpOgf z&r*+S)}t!TsYq3NpxSj;q$3@tdiYj>j_V#pmV9f03Nl}hU-nm~?VcL0&&t>Wx-X}U zY;eNdv$%-s=nBYh>WbVt$b68+Qv>Dcfp_}UKou0na*!j80(m&Y^D*0Q5NFHWV~)e| zI&oeBj(eDGIGhNAGjNBWdlDp3X3=lUvQ8V(te1@nyxX03;D#Z!VC5;Rn2vk?LTE0$ zgC2B8T2={D`@8wZaUS1EQ5=lMy+HD{AmyDE^_>vq#z#~xEj(Xh-q(+vuOssum3$4|my0h! zFOyyzZIa7dyW!v!vWiD&cwPuck8tFM4Es$@T}?0g*$-8vM* zXD@t~FMO64z6ba`fREJqdR3h3+}IDp`)L8y^Ti{|rs}o!)A6E)yQrt(9h&*`2;_|n zED+zX;z35g!z)!{*D?k#Oa>G{`TIJ4EdkXt^jF&H1S&U_NfK*r0+Z z_ZUQWm9D-FV_A`A32jF(YPr<>m`btEvj#A=_lAtle&tGB|hvo3MN;mu*6d@D7 zo)hQwe0>>@rG8=5?x*WT38>}Eglsp|^~g&8n8IEOt>f!U;;iS(O`*=0LR}&SkFOFb zT*nlimlW3W<)(0*FNNz8DXjIE;ZfiR)U5q{pO7Th@x>%iIlf-SLkzxO*I9D68tV$Ts!&d6z4j=wreO~_iHF${D?E6@$~}@YnA*nF{}L#kJGt1I@?eS_ogho5f7PDMEUM4^|(a6nrDqPH1O zhQo#;e6q~Qx@#tPf*MI5&zTWTx=-LRrL=3*P)Trg_F%3nE^0yJc_uw&99bZH?6+Y0C zh*jst5;$<=ZzF!+gU3+U@#Vxj>Wg=sFW%8RfFpnJM8SBc7v%>_&w{c)x#4Wlx4+h0 zV|K(MUvp3hNTU&`JSzex%YKh`^7TP+*7Efs4fU6o1YmvmruIVLVzrnYoJ^6)`6VWg z@cyd~b`!b-Jy`elbq`xg@!E4V-cRx3{nK#bETAgZPB%^l;#`IYxopAfuy)>sIJ+cz zEmQgz?PN+Y@*^%&D$`CkrCLOdO7wcZTJV5h`}Q*(CIY^UAx59X@`Nr?b(!mlv^Y8R z^9X$j590FmIv%9CZ9g5HU-@{Qw|Ej1P;)#^aA9D(V;=hu_zQ{ai7yw=PhWmi{#$qg zD$z{|)3m7Li@`}rC&Im?6UijhBR@|_0{WSjFiqP!z8IX8bRyhKI+08w>A8qU8v3a~ zm~N{&z8IX8bRyhKI+08wX#=%Q#X}VpfY$D3aFMZYE1AW ze&vsX^kF<;EWW-g&id1M{Xm?xeEqvPJ;vdsHl@<@OZM|!jt3HGJzmY)xf3tBp&bQA zT={cj0h5ug70g<`7K*cuFP&5^lj;y?Et7h;IP3WOOFR(zyepkfW`d%-dGHPXB8aaE zc(ka6L;eWO!~+DrvhbvHgoFQ=m4&A4C%`unW&%m!@XMczXR7Yp{ft1&OkYZu<$4t_ zKu{4LXd-#ff*bksRqBB8lD|UyUL@)gpmHdVR4u-5!-H1m>s<~QFZoMo__`!zyDjc`s@e$OGO3q_kJ@~)Qjqw~D-;o#`C&=GR_~k9rC<|X%x`Fu0(NG6Z zxpA2^(~$f6)H-605loLqsdY4Cc@2F4prd$5Q@%4q9M@;zHAXw#Z2kqIui$~I^MigW zkxTaToh~tI897@!UCh6L_dUrV-Z!yFgZ>7uhm%8(z?%ssW4ZV)!Gqp*EnZdH=_Z{5 zFEKzTzBsf*&u~Nk+eydQ6h8upk$heIDxfdI11##quW2WKf6u>n0-vh88_&n_>;zGM zh{ye7_(nWC@NnMe{=EpCZ{RsS1pj#;Gy>+%X#yQ* zHHI4wYjE6}qIEc|{c&HY!WVPgQ0<1Dx+wNiyj|JtVhk;-3+Jb$58)YwIu`iq`5D0e z0S{qU_~3toh#$ps1P>e9{i8^og=Yz#m3Z909=N&XxD(HNeQ-D5PXWfeL5|~j-WPru z@+`u`dtu6a`PadH6CU1T(&2+2h5HM5p271aJg4w{3(x=k{qLeck$k!q=RHvD-@Sp870pmI_mS&L-an7wAWg7pm zj0>FKWrIrM_y2su@h~mZ_^D6QB(Tmn&amL{BMayL{mcQS;cXfP_|=qz!~1jP=SVYRAN>EQj10r-f(h}0N+m(@B=Q6a`{Kc zVtF{~W$PyXejolJNAMYz$9fWsh++e0N+J`Vi!?lV0U8$F+|)z#MBQDAEe2Fm4A(xl zbZRpBQsz?Y5x4Hc(8r~Q#$gGQTZTzpDrzt-*7WFwYU)Uv8sNJ1!bZV4^|}}79KS%l znAZW|;Ap)gHwZZl2z7L_B+==x?zaFZL`3iJQq{f4;e-G9Y)Qj^cT`znNzB%3#=a)Bqe?LGv2QE-A zqW&B>wP;4o;Sw3ba%U2RbD7;9#N`B?GO5vvaB79b-=(I8edzr9!j)_T$X-(e-%6b| zgcJxXL@5g(tj;I--Fj&p*6S1JH1nmdSN=uR>jVCJmBwavH@5Zo+j$o1bt?7z^U!428w{RhJyxNPXm&RdxUG7Hp z-j}*wdoQA1-}UO%*0`zn+?ziy)+K%#d%rs-=(hCBNtJxvnYb@JDcU(c|&V9 z5%t=AfqJn9vw?Hm#qoNaj>Vc*0tf8u`@7UNJ$4Z_{Vnp>HSLJaojGf0Mb$FY;eO`iTWAmd`|}ZAA5K@*`JPkN)zvlK#0QHd0j6u# zQ8b^92cH+gXT!&#gRAGsv<<7Py+|8Hsrj@L5#itX7ydW020v6@baFEADg|=zIrQN;NED?-s!16D;+bAhh8k9bg>*r&RDB|cE; zfDP_OCEb^68mTp5bsRQ5wlV!nokcBtMIVFyt0)>&9C`Yzho`i5qw;4}BV~XL9wwZ7 z=6m`iY$|M%_(BV5b)oD6B@)WsP-3A>gVGbqp-?IlIqNv1g2aXx9yh?gXoeGz=7b<^ z?Thba=s~7p*jT>!v?cb0Afa&#yr{Dm11;{)(3B0;MXT!aDB7=zhhMb%ru|y6XmIf< zSOmYSuguSlRj_HWP2$p}P+CIyB9yjKILfMIqH#6OqZss$qG`^wSy$Yfzub3YGjgN-m4` zJO`ydSJ|kv+y$4tmQe7H>SX&|-wV78I;bCPpY<1OlscQ1T z1U1SWMa7-37c{|1+FS||T{OozXcV3AksDDzH&$aW^1wiPDvIu~r?5>s&V^0*dZs}Z zABGaW!B(z<(s-k-d;>~XDBq`YLfd9Dl(tYFhY}oW+x#z-s!(>q8dBvj+h$KFHK7~= zB{bQ>5mcU11aC*F(%qsXXlgIfQujnD68<`AAEXR8IvgQfKtU`tju{;G7-BT#ON7P|0tSRjpJ3S!{@j4E7E@vv4#h+ zFImJe&s=KV1}7!G2cFior)k8<=mW56uuXE-r=T>2at)N0P;Q12YeiAL8%kFwzkw1Q z#hj_=zmkH3&cH0Uv1kbfFFSg2{KX=D6R+(wQHtpCCHg&d13=W0T zHVXG{RSx+@o6V{9{e$_C-wEfGy7=^e80lO_wT_@`MzecJ4>NYldM>^^3=+k zbgxWl&&Zi{9{3!sZEy#a2tj87E7YEapg~)w`*%Rgp;V2+d{kY;XLrCMFZ)N)e6IE2 zLpyaepH(Z)988V~UWDvL3Lk2H6E=0WN!X{eHk#<@68_12^S{G(_Z zu?LN=pn>~br?31NUc~H03Jvy4*^plcu{-A9Mf>%DhhP8G_X}^eSTgX~YzzDf-lDTN z#V>07Q?RMAjTsNsS>!1_NgqxGc|3g3jEAz{Q0wTRF=49*jWKAZ%K5cRSFTw#Fd%L} z9{J3kX3%&UY+7uS)P4YzwopPSJ)s;!CFAjSf0Q^CN^BI)A5}Y1rQKdTCFS!!ipn3R z+BH-wT!r{IQGL#XD(#E6sk*Ci=fTrvEkDOT3Y#|Dm>g4Q5smq1*>G}w@{OsLkdw9w=N$$K7O8r=l zGD&lxbcAvYl-6;!O@qo2%EzEIFu=-eQSBjy^y@Kshf~xjL2agfvA~1j(GJ5|4#Vr< zMg8sh48IGTm~E)?;6W(S@$5S(e}WP&w3VIk;H7O8Gm0kV5Fh&0o}S=S0XT~0Q}^IQ zJ7w(=@<4~r-teM%g7Rx@MsJ!8n~t_oy=ESr^$wfHiBM`M+RA&OG_Y-`f8F^wlvpTV zq_f_|Hq219hZsJCn;k5zT1U|g*LpB~x5IFb!|;3XqB)|y$YA&&`OY?GL{evwr>Dcm z;bzOl9-f}*csked^eK1|)U!S12r3)$>u@V}>d&Ghc%_G57xn$Z=8eH^AKPOmOD)GQ zYWYiH(>cw}Rdp75dKLb=41Up`wmm$(-17AF!CR|fDq0z=$c`X+dN^#VY(qk@5K7A^ zCd!FIzF&x1CnT2qkD`3%86z!wU_eD(yKxob-*mmW(}T~(J|DbeVohTe-lw=&m&AK} zqLqqI)_6#}eL4+olbm}d_*7Qg$_Joy&eWU}n@>WiW6NECJp3w@>N%`1RaHBA`a%5n zmk2M5qDqAlvCSUDZn20hmz!dew*3IDEaO(1{K_KsOV~8nCOPW~C_SP4FBJOWSW-@6 zvkg+JER~KBl6x7gM^F zgJSWxM=a}rJ{7}fKI+*;)J6Y?_yk`Y{VwSt!{_7`XW(@(OPBW_ z5{~Hg@X80Wb9H1IW@KY+IPz%?A>N@r!7+FLcg^{b5 z@L6Q@G1$V3bbveNqI!S$GOoRZhe0&R zItP*8&4c_ieewf)iJ5$)eNP)Dn0%LQ`s6>t44G=wS9C05qfqlVfhs*uO0E})Ket2a z2<1lUj*l;XVn<*CiqU#Qw*7gI*VwW3=^6R_K%`z&~!Mdz0^V*<#mv1rOZ6UgT}-@ z4fzmFHyxiC2`cM#j-}8bvzNi9!8UkOlRSJ2I(=Ju3A)PYoX z4vJUJBVP2T83KeuG$|--@<*eF6oM{(}oJ$nVc>#M-s-ivV zoX_^){7%XF)TP+Wv)uX-+QaZP)}ChNJdI=3YndVOp;4=86sFqkE1cn9wa@wk&bpTU zGSR7gjcxcUT+oSD-$nzN|(XHib1ZTh`Hc|p;+8AF8JaaeS8&gb?dYxwEW zxzr;%Yf__w-%Oz&Fx9B7`Q-G1m~9A8uOk(oI5x4Ijgy7*HYB1vi^0pp>ke< za$>DuXuxp@lxI!Jq`V2rZriI2?a%O4n<*AM;95J849!t=Ebip?lvbdQ^jNe!V)1YN zSlAbaq}@CsHNTM^3o@juLzit#1Y6%^syyefY%+?zcGrwT<(vqrOAJ*~=vvUdjn5+G z6xf8ZQdD%_2W71|>ryBUW5d^O{tLmr=r8|WE9@H==~*S@EbCl{ZEP0ThXUX|}LdF~hW zzQS`_qfj~9V6os{_RB;txQojnn=))dq3i{vC6s-k(3^Ss_sbM7dNu=R^%ycAHWSf5 z`pR)oo-;NaQ{_9w^e>pg4!1XL;@V3Mv=fs(=b+iwgB~%x&%~4p_$<*7#|V_V=93mv zzBV=QX7U`tPG8Y8EBYdiT6yE%6dHvpU5>hr6P(vVsr|%`4&6a7F*b&L(n2D?C$0fB zPwF2<_ogXadve83?@c`q^1n3XPs2?J?m?Wp=r(+h*!PqxKKrh0$oGBWv@|TrccMud zH4ZN&N_s)cJ27|{!D9n{>B+nf$q(UI+ruwq^TPPGhlgMEdk=M=qT}V<=|2L$x*mQh zn-|8f{XG1lcd_>U8eGZwEBq?|RQExdUP(kb50wr1brf_AfYwoT9)=!%y%9 zaNOcKKwbNlH?lR@#>@elMj;n>DdRn1JSHSQ?*%2e-yXGQLAlh}Ffmm-5yLQux5wH? zQN(!Vq`|e9rl6YiQR_Gls(Tr#^fL+vD@qRGX_bA?$rfcpzVD5*q+wCM6HgkomU;L^ zzrs@4f)CU>`?~pxR@Z*zWlNK7f{}BVu3m)}R3A{8fZf;hGh--`QK*H-fLQ%!oGrXV zdlSctOoCHk)A~8vnEXlVB8DGC>nX1NqbP=)KUZ*jN;6Rjs&fwFe7*0+ylWCFE$UQgOY^ds$x8PSs(KE)XQRvhQ!0>7I!ps=kzcy#BI(_M>i`E2Y?JKG8 z5}eiPDjRigwE@bw9YG!cy?>vp+Dx(75gmC}axVYl?~BDCV~)kW>_urFsz~q4O^;Z- z(~8B))25!f^gaC@f6O`fjpmaU3%$ZwXB)G^(CRBX79FDymFvK;Y8dirPwNqm1=-vT zo32(A`CmX;V9p|&N1=p9;eMgoOtF{&4=ZG7j^`7L@u(IBpd53$`GaFo=Gt>+gBv|! zu^bht@n;R~S05Nyd*tb(IuFy@qu;ThY^cqzH*MuVit?SxqyE0h!>eM+Cvl4wHynRhn=nFEPW5sH42?I0cZ8JqH=6sDCWQvX83CJ0%u}1V; zO)MqHh{in7i2kT-`YW#LETZwyc(SsOqO-2+;pu0Lr-M_JkyWQPsC~63vpwY~D;x6b zNZY04@1p$Tk)&m!Ob7?@Q;&Pv!}n{9@6HVhN@d?$9=W&WsgUKll|1$N32W@u4emOZ%S1rvpB{zqx$qXEc(JDu?*cyD{aot!O^6;j=E2 z&w39&Q*1uY4+n_PT$EnH+$nj=fKfH=cm#aP_z|~y;=|YXcz};8hxqJ@UOhz=RWzT7 zYcI7>PkR1bHrn?rK6|4rT}=5TpI}#)k1B`wOhW$P0&E>c_pjLS$(|oR z;=zY@GMn*vnw&rDG z?nOQ6XmC(sEWOrUGp_bfh(z^Asa%O}DBEcvK%h|eVC&y=C^3Ay$hJ{1o>R|r0G z0-f-|1Uk~bXYo0c`WHSDBl}nQ5|@uEhxm*|{$PS@9Yv2nw6$D~&zGW>^!_!|gU>ZK zpBaPW^Ni`}U)uL9J|703&Y>=!;H54fRSxl4jM>RTi%#+5Pi**LLv;q9`5t^W*?eXX zjz6<8{sf09-?R9%z$aMY@`(rdsB(zUq}K((6GP=g?`T!|gN=U~d`|MyiOJFK0gZwczrV_&r=?6kd&2B@?kNC<2p4>nME0=Gv1L4`L^eNw~Yb!h_iV*~H|8 z5xdBa7HmAXdj`&18?Gmlh;3rQcU3#6qJmF;y}VI~MK5ZHRRgO)NIVny4k6 z*tH(ScEr_K&9Q+;4&ezg8p4~JMi#N{FrO{sb2^e(Z1^xS+K{doGBH&$5!(P_!_BdX z+mn-Hu?Mk@92V;VsAQ*B~$nwb#8G&AUF8d<~+0I?Qcpeb`K+>0}ciA6?XVya{!c0Y&>cV(M? z+M8r}a%D^>w$+2!8*F0k$}Ku7t=x7rjVxk^gIMrMml!=fSACF)g+^gws$?QYYfZQr zvyP&3EH=bis3o1)a~{OrY7=wUazShkO0Q}fS;US9vG%83VzJ@F#DZ5cLnfw5CSn`l z0&W1UqiA9gx2HsRH|fO6H;#UHT(^n2tGggZtGiuIBa7H+AXfXlODr;cm{@ESCZZyGSh0TSw737IAw@E!2`ej@3PgZ4|`h^dcdK=|!YzWD)x|S|_<1R)V1xGk4yRiG3?<=Fx&!o*a`MC?Xfl%E?avDgs1Gn3er9>jiO6LZ(TK#bPDf?XAjEMk8J zv3g~I*sBMK8HI_dl8M+wc+`deY#l}S%9!JobFFN95Zfw<4UYV@8rIY_vWV@1i(Gk* zON_qaF%|^+y^?+|sT3xrN+x0(u=;tk#jE&U8F70`o0C?F%J6?CmGCAA4`Tn@CpLX> z?E(_Y3N?)^Vy^|U?%V-lh7XTpG)0xfRLMl_4zzciaH8lOi?}^GbNcv$3c2>2Yvtn}#P+m_O><`# z(?_OZc2U(dvU2RLlwiP-&^j}3QrK@TmH_LN!(F#RPD>XpGw zqu*E3Hy%um4a_d4C$o$4QTaKx1jKqvTw=jKF0s%kOiYzb#Kr>GLBfTidu42hwNXzx zvE4j~z1}7^FuRzZ%q}WR^NF1UVwJDB#A3sTdu4#9XDaKMm@1iwO*&%Ovx|t^Q+gni z*c1<9Z?TEVOC@HW)03UCHBBR{SALXo?5hLB3?D;mUzeCFnTXvnX4q@65x1vwWhSvh zJcy0h#0J(bFpgn9R=F-e$G!k!t(#n8k>SI{VxyQGOG+kUbSFOCwTqBz&l$&R9>f|p zv4I)v^kfFx(KNDh?3=CKefmiK&u_*x}gCIoug+z_sVxB`xtF_C7&O zc1!A&G=r_&nV)0#f>`<2F0o*$ODr@B6H_G^*}| zcu7L(p{9|QW4{5hCSH>&b1c|@fS6I3m@1iwtr_|ZHs*NcJbk;=gV+X}nESjE#Abq6 ztZ8Hsdj`a!$6R8u;lsT$IDi>4F;y}V+k$ZnlUVC0dUg?Udvb`iJc!+76LaVMAVzck zwx*FqY$)GLG0%?F*)!D zBOr!>KZrGrEMn6@th$#=j259KvCt@nSW+?(yL{&$z~FBkMfb|s5W51kP|M^o2_xx) z9>o6JCMIty6JqpCysl|v5&IX)v1u-`*zntQggP+{iy`{E_9$y6Bs z;yd8>ltQSe&N(Pvdw9gFV&X+F5?d`o#_w2YYfsbikfYPU%WP@RhInc6Eu&D=t_QK> zxXtDr3$=sISvrDsqtIEm;jD&MaC3^|XHb@ya+thoA@!8sBeWPct>gKSpMXk;d77t8 zKIGa<7(6q`@8>~&R-gQ`Eb=X;foQr1ldl|N80vme>nlvPujuGBj6&shL3JJbl~nY5 zD9zW~y&*E~D_+7go7l#XKiVidf}v5U9Qt;{JUqATUqKe4o15JfG;4Jw~l(O>~Y!9qG|o<`JFMCOYZgZnzYl zw%PZzQEQEIF+P|XG7WVW(Re>h{{AuChepJ)D0RUOkE5Jp5?X$~2aOLJ8tR?4xPY$a zY~`zGc#O`dG#Wa`BDP7|aWxpW4q-kfSC2KBsb@sNajej3*W;|ZR^SwlpHM7JIm}tL zhdAT0MNm#G`5#4RUBtDQ@JB}0H9a_gVUV+!T0RI*n@l5xGx?W{0Bn;uycK8F{?%sq zCnENCR(K{;UC0WZHV#je$GzT-PW?o-;W4Uh6gulgIID3I+mJFDN@c`WW(A&Ic@uNwjp%%X8X!YTiJ?|+pCq*f7_U9A(7t$S8`Nr z9YvF0!G4vNp%$7k$b+sj_j-_j#3AqY1^W6%dyV!zqZiOUb>$4^W2l~G6dj%5Y*whP z+afh5U}Bf zW4`)oGBiig(Rsi`=OomT9-Xd7bb2N_r=>1=8~{&i7idpYqQhf?vLWB8k4nR$d?%Qc zbx(Wv^<3Yt)zih*mP!eNBj8uv!!Kn+el5aTc+g-SMf)}W8>6pKjmKPD-Tj?r&DQ#j zIuU+#Jp58Nmm$PeVD*luYH&7Z$D% z3VmT=tx>ptDc>nxN8=`T5}j&}qTeTN-$Q;sJgux#RL7+B6!u-&knek=J<_l!-w7vW%R&#oPU`!W`5y9`_G_$c zE>+X-?`&h7#N0HQTg*1$|#|fUj`d2N~zfeowX;F z;3Jwm+Oud0^`Ev zF@cuf5>HDHqL#sRG>Y;Hk0@VcqD(JEmhWMp=6?d71|Qe4NQpApTusE-hJ@f|A|{ld zKxr9;N0RU|U2<~P7TCl>`7@Psxoxuz9$`j8c`20GD12|#TyLNCI@nAI?cA6JrTGcl z=FL!QpR|>CK&kw@t(;CaLOB~s`wOoTy@CK6#jK<7 z>zYR17}a7MbI;iAD|${+#+P)|Dmu0CC1BVx z40%q{z23HY6>R8TLu&UCokj0OxXqkJ%3Gkcjl!`|ZKhbzZtw~jnxp7gL|l8$JH6L? z#G>7gMe-G)W%fg8w4T;UB26PL7UUqUss%UN3{N1=M&WUzeG}X8U1WfJ5dDlp-_e)* zijH8#C{)+GQSu|~I|;!VP=asT@%ku~r;QCqM|nZf`3ja2IFe=J-Dwsxq+dTaGsqriOt^RfGS6bF_Ms?Z7 z%xNo|IHRaNUm-q+=~2s!60KXG8PW=vg+ImMdBsx|r}68}TK->QkFYK-r^5=>wFWk4GiMTl3!ro#P&Q){Wh0dJ!scsGE;h;tCa@!?E?yEG zZt{?n#b>O2(AZExx8STzM&XQ7Lis6_+HdSA|B)19!_iS*P;|~isc)vj%u#f7%3OOX`|0ho9?^NFiH>?+ z*^f>C+vRQTd)nAb4pJ=24;%8DbIT}X^Bjmp+-5WOHXku(=}{s!3Z1nbX0eq=ZSs?$ zG)*~7UbT?Oe|oH&t3{Jv!nG$~vPfTZn1Cx08iMw`nA{Ktf7x)lA&-?D8#}D|=}Ard zo<^SgPMvMcRWC9MReS)5UBhioT5=GS;4z!(aZolH8>XsiCog`56L4X$j-shv$+hRK z{rec!$hj7D*~Y9;g}-Npd@YFjiXM^LMj=jL1l6jc%5%Tq zf4Cg7X~QPeii$-C$^vs1+5DPpjKX~?tH{tCMMtO1wddUZ&G(4T3KJb# z1+{yPYWi>CX^Uy3M2CG>Hst%>I8hoFAW*{ed__eO@ms$n2T;ox+!*Q3*O&sTp zs`NNF_}h!MzM{W)75>rKoT;Sx{~1NEi*|x>*4Xk_Q0eK^Afgliir(xEk|sidVxUUQH9P zP=iKLB;Sr15Oe~TaLJ|hEPfuT$t2=69DLFeMVvDoYpi)%e%ahHh&wREg}=mOf4|w?1HGZkSgV!rBN2+u83El?3 z8ZRH+FJ(i1(S8Ye6dkXv9)3M;{Gxq3gBx5Z=GW#1h+t`3!CpjiH!}963SDG&UEZ&qJ0DXqp0Zc)oKd2 zCuilqj5X?HoX=S+*wc-mwbzno4n6H@Rs_#Ll*7qxEP~eRf6pJ_GU1)Di&gFj;9g7v*!qN&

mk( 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 Zw6j`EIb(&|vFr)9i!!tBYdl(${|CIWbE*IU literal 0 HcmV?d00001 diff --git a/libfftw3f-3.lib b/libfftw3f-3.lib new file mode 100644 index 0000000000000000000000000000000000000000..e60053265e58e087a0504008b49f5c0cf8ea3e5a GIT binary patch literal 252722 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 S2Pzmr#(P5khNu#|^8Wzt7r^TP literal 0 HcmV?d00001 diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..34973b9 --- /dev/null +++ b/main.cpp @@ -0,0 +1,88 @@ +/* +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 "QtSoundModem.h" +#include +#include "UZ7HOStuff.h" + +extern "C" int nonGUIMode; + +extern void getSettings(); +extern void saveSettings(); +extern int Closing; + +workerThread *t; +mynet m1; + +QCoreApplication * a; + +QtSoundModem * w; + +int main(int argc, char *argv[]) +{ + char Title[128]; + + if (argc > 1 && strcmp(argv[1], "nogui") == 0) + nonGUIMode = 1; + + if (nonGUIMode) + sprintf(Title, "QtSoundModem Version %s Running in non-GUI Mode", VersionString); + else + sprintf(Title, "QtSoundModem Version %s Running in GUI Mode", VersionString); + + qDebug() << Title; + + if (nonGUIMode) + a = new QCoreApplication(argc, argv); + else + a = new QApplication(argc, argv); // GUI version + + getSettings(); + + t = new workerThread; + + if (nonGUIMode == 0) + { + w = new QtSoundModem(); + + char Title[128]; + sprintf(Title, "QtSoundModem Version %s Ports %d/%d", VersionString, AGWPort, KISSPort); + w->setWindowTitle(Title); + + w->show(); + } + + QObject::connect(&m1, SIGNAL(HLSetPTT(int)), &m1, SLOT(doHLSetPTT(int)), Qt::QueuedConnection); + QObject::connect(&m1, SIGNAL(startTimer(int)), &m1, SLOT(dostartTimer(int)), Qt::QueuedConnection); + QObject::connect(&m1, SIGNAL(stopTimer()), &m1, SLOT(dostopTimer()), Qt::QueuedConnection); + + t->start(); // This runs init + + m1.start(); // Start TCP + + return a->exec(); + +} + + diff --git a/makeit b/makeit new file mode 100644 index 0000000..7563469 --- /dev/null +++ b/makeit @@ -0,0 +1,11 @@ +cp --preserve /mnt/Source/QT/QtSoundModem/*.cpp ./ +cp --preserve /mnt/Source/QT/QtSoundModem/*.c ./ +cp --preserve /mnt/Source/QT/QtSoundModem/*.h ./ +cp --preserve /mnt/Source/QT/QtSoundModem/*.ui ./ +cp --preserve /mnt/Source/QT/QtSoundModem/*.cxx ./ +cp --preserve /mnt/Source/QT/QtSoundModem/*.pro ./ +cp --preserve /mnt/Source/QT/QtSoundModem/*.qrc ./ +cp --preserve /mnt/Source/QT/QtSoundModem/*.ico ./ +qmake +make -j4 +cp QtSoundModem /mnt/Source diff --git a/ofdm.c b/ofdm.c new file mode 100644 index 0000000..8472304 --- /dev/null +++ b/ofdm.c @@ -0,0 +1,1555 @@ +// +// OFDM Module for ARDOP +// +/* + +Thoughts on OFDM + +We have lots (?43) carriers, so requiring all to be good is unlikely to succeed + +If we ack each carrier separately we need some form of block number, so we can infill bits of data that were missed. + +Always sending first block on first carrier seems a bad idea (but look at redundancy ideas below) + +We could send the same data (with same block number) on multiple carriers for resilience + +We need a ack frame with one bit per carrier (? 6 bytes) which is about 1 sec with 50 baud (do we need fec or just crc??). +Could send ACK at 100 baud, shortening it a bit, but is overall throughput increace worth it? + +Using one byte block number but limit to 0 - 127. Window is 10160 in 16QAM or 12700 for 32QAM +unless we use a different block length for each mode. Takes 10% of 2FSK throughput. + +Receiver must be able to hold window of frames (may be a problem with Teensy) + +Must handle missed ack. ? base next ack on combination of repeats ? + +Should we wait to pass to host till next seq frame received or all received ok? + +Should we have multiple frame types for each mod mode, or add a frame type to carrier? + +Ideally would like 2, 4, 8 PSK, 16, 32QAM. Too many if we stick with ARDOP2 frame structure, so need frame type + +Frame type has to be sent in fixed mode (not same as data) we need 2 or 3 bits. With 2FSK that is about 60 mS. +Need to validate. If we use same type for all carriers (any good reason not to?) we can decode type byte on all +frames and compare. Very unlikely to choose wrong one, even if some are different. Could even try more than one ( +may be a problem with Teensy). + +Could use combination of redundancy and mode to give very wide speed (and hopefully resilience) ratio, but gear +shifting may be a nightmare. + +Is reducing carriers and therefore increasing power per carrier better than massive redundacy with lots of carriers? + +Would dividing single carrier into multiple RS blocks be beneficial? Adds 3 byte overhead per block (Len and CRC) +if done with slow carriers would limit window size, so may need different block size per mode, which makes reassembly tricky + +For a block of 4-5 secs we get + +16OFDM 80 bytes/carrier, 3440 bytes per frame, approx 4600 BPS Net + 8OFDM 60 bytes/carrier, 2580 bytes per frame, approx 3440 BPS Net + 4OFDM 40 bytes/carrier, 1720 bytes per frame, approx 2300 BPS Net + 2OFDM 20 bytes/carrier, 860 bytes per frame, approx 1150 BPS Net + +For Comparison 16QAM.2500.100 + +120 bytes/carrier, 1200 bytes per frame, approx 2225 BPS Net + + +*/ + + + +#ifdef WIN32 +#define _CRT_SECURE_NO_DEPRECATE + +#include +#else +#define SOCKET int +#include +#define closesocket close +#endif + +#include + +#include "ARDOPC.h" + +#pragma warning(disable : 4244) // Code does lots of float to int + +int OFDMMode; // OFDM can use various modulation modes and redundancy levels +int LastSentOFDMMode; // For retries +int LastSentOFDMType; // For retries + +int SavedOFDMMode = -1; // used if we switch to a more robust mode cos we don't have much to send +int SavedFrameType; + + +extern UCHAR bytCurrentFrameType; + +int RXOFDMMode = 0; + +const char OFDMModes[8][6] = {"PSK2", "PSK4", "PSK8", "QAM16", "PSK16", "QAM32", "PSK4S", "Undef"}; + +int OFDMFrameLen[8] = {19, 40, 57, 80, 80}; // Bytes per carrier for each submode + +int OFDMCarriersReceived[8] = {0}; +int OFDMCarriersDecoded[8] = {0}; + +int OFDMCarriersNaked[8] = {0}; +int OFDMCarriersAcked[8] = {0}; + +// Functions to encode data for all OFDM frame types + +// For the moment will will send all carriers with the same mode (I don't think there is an advantage being different), +// So we can use a different block length in each mode. We need to keep record of which blocks within bytDataToSend have +// been acked. Note that the first block is always unacked - acked data at the front of buffer is removed. + +// Although we have an 8 bit sequence, I don't see the need for more than 128 outstanding blocks (carriers). If we miss +// just the first block of a 43 frame transmission, next time we send block 1 and 44 - 86. If 1 is still the only one +// unacked I will repeat it several times in the next transmission, which will be the repeats plus 87 - 127. + +// Unfortunately this means bytDataToSend must be at least 128 * max block size (80) = 10240, which is a lot on a Teensy. +// Maybe can come upwith better design! + + +UCHAR UnackedOFDMBlocks[128] = {0}; // This is bit list of outstanding blocks. +UCHAR UnackedOFDMBlockLen[128] = {0}; // Length of each block. do we need both (ie can we send a block of zero length ??) + +int NextOFDMBlock = 0; +int UnAckedBlockPtr = 0; + +int CarriersSent; +int BytesSent = 0; // Sent but not acked + +int CarriersACKed; +int CarriersNAKed; + +int lastOFDMRXMode; +int LastDemodType = 0; + +int OFDMLevel; // % "compression" + + +// This is used if we send a partial block. Will normally only happen +// at end of transmission, but could due to flow control +// +// Also used if we want to change mode + +int DontSendNewData = 0; // Dont send new till all acked +int LimitNewData = 0; // Repeat unacked several times till all acked +int NeedShiftDown = 0; // Shift down once all sent +int Duplicate = 0; // Send data twice +int firstNewCarrier = 0; + +UCHAR SentOFDMBlocks[MAXCAR]; +UCHAR SentOFDMBlockLen[MAXCAR]; // Must match actual carrier number + +UCHAR OFDMBlocks[MAXCAR]; // Build the carrier set in here +UCHAR OFDMBlockLen[MAXCAR]; + + +UCHAR goodReceivedBlocks[128]; +UCHAR goodReceivedBlockLen[128]; + + +#define MAX_RAW_LENGTH_FSK 43 // 1 + 32 + 8 + 2 +#define MAX_RAW_LENGTH 163 // Len Byte + Data + RS + CRC I think! + +int BytesSenttoHost = 0; + + +extern UCHAR bytData[128 * 80]; +extern int bytQDataInProcessLen; + +extern UCHAR bytSessionID; +extern UCHAR bytFrameData[10][MAX_RAW_LENGTH + 10]; // Received chars + +extern char CarrierOk[MAXCAR]; // RS OK Flags per carrier + +extern double dblPhaseInc; // in milliradians +extern short intNforGoertzel[MAXCAR]; +extern short intPSKPhase_1[MAXCAR], intPSKPhase_0[MAXCAR]; +extern short intCP[MAXCAR]; // Cyclic prefix offset +extern float dblFreqBin[MAXCAR]; +extern short intFilteredMixedSamples[]; // Get Frame Type need 2400 and we may add 1200 +extern int intFilteredMixedSamplesLength; +extern int MaxFilteredMixedSamplesLength; +extern short intCarMagThreshold[MAXCAR]; +extern short ** intMags; +extern short ** intPhases; +extern float floatCarFreq; //(was int) // Are these the same ?? +extern int intNumCar; +extern int intSampPerSym; +extern int intDataLen; +extern int intRSLen; +extern int SymbolsLeft; +extern int intPhasesLen; +extern int intPhasesLen; +extern int intPSKMode; +extern int intSymbolsPerByte; +extern int PSKInitDone; +extern int intLastRcvdFrameQuality; +extern int frameLen; +extern int intFrameType; +extern const char Good[MAXCAR]; +extern const char Bad[MAXCAR]; +extern int pskStart; +extern int charIndex; // Index into received chars +extern int RepeatedFrame; // set if this data frame is a repeat +extern int intShiftUpDn; +extern int dttTimeoutTrip; +extern int LastDataFrameType; // Last data frame processed (for Memory ARQ, etc) +extern int intNAKctr; +extern int intACKctr; +extern int intTimeouts; + +extern UCHAR goodReceivedBlocks[128]; +extern UCHAR goodReceivedBlockLen[128]; + +void GoertzelRealImag(short intRealIn[], int intPtr, int N, float m, float * dblReal, float * dblImag); +int ComputeAng1_Ang2(int intAng1, int intAng2); +int Demod1CarOFDMChar(int Start, int Carrier, int intNumOfSymbols); +VOID Decode1CarPSK(int Carrier, BOOL OFDM); +int CorrectRawDataWithRS(UCHAR * bytRawData, UCHAR * bytCorrectedData, int intDataLen, int intRSLen, int bytFrameType, int Carrier); +UCHAR GetSym8PSK(int intDataPtr, int k, int intCar, UCHAR * bytEncodedBytes, int intDataBytesPerCar); +int Track1CarPSK(int floatCarFreq, int PSKMode, BOOL QAM, BOOL OFDM, float dblUnfilteredPhase, BOOL blnInit); +void SendLeaderAndSYNC(UCHAR * bytEncodedBytes, int intLeaderLen); +void ARDOPFlush(); +BOOL CheckCRC16(unsigned char * Data, int Length); +void CorrectPhaseForTuningOffset(short * intPhase, int intPhaseLength, int intPSKMode); +BOOL DemodOFDM(); + +void GenCRC16Normal(char * Data, int Length) +{ + unsigned int CRC = GenCRC16(Data, Length); + + // Put the two CRC bytes after the stop index + + Data[Length++] = (CRC >> 8); // MS 8 bits of Register + Data[Length] = (CRC & 0xFF); // LS 8 bits of Register +} + + +void ClearOFDMVariables() +{ + OFDMMode = PSK4; + memset(UnackedOFDMBlocks, 0, sizeof(UnackedOFDMBlocks)); + memset(UnackedOFDMBlockLen, 0, sizeof(UnackedOFDMBlockLen)); + NextOFDMBlock = 0; + BytesSent = 0; + SavedOFDMMode = -1; + + DontSendNewData = LimitNewData = Duplicate = 0; + + memset(SentOFDMBlocks, 0, sizeof(SentOFDMBlocks)); + memset(SentOFDMBlockLen, 0, sizeof(SentOFDMBlockLen)); + + CarriersACKed = CarriersNAKed = NeedShiftDown = 0; + lastOFDMRXMode = LastDemodType = 0; +} + +int GetNextOFDMBlockNumber(int * Len) +{ + BOOL Looping = 0; +resend: + while (UnAckedBlockPtr >= 0) + { + if (UnackedOFDMBlocks[UnAckedBlockPtr]) + { + *Len = UnackedOFDMBlockLen[UnAckedBlockPtr--]; + return UnAckedBlockPtr + 1; // We send unacked blocks backwards + } + UnAckedBlockPtr--; + } + + if (LimitNewData) + { + Debugprintf("LimitNewData Set - repeating unacked blocks"); + UnAckedBlockPtr = 127; // Send unacked again + LimitNewData--; + goto resend; + } + + if (DontSendNewData && Looping == 0) + { + Debugprintf("DontSendNewData Set - repeating unacked blocks"); + UnAckedBlockPtr = 127; // Send unacked again + Looping = 1; // Protect against loop + goto resend; + } + + // No unacked blocks, send new + + NextOFDMBlock++; + *Len = -1; + return NextOFDMBlock - 1; +} + +UCHAR * GetNextOFDMBlock(int Block, int intDataLen) +{ + return 0; &bytDataToSend[Block * intDataLen]; +} + +void GetOFDMFrameInfo(int OFDMMode, int * intDataLen, int * intRSLen, int * Mode, int * Symbols) +{ + switch (OFDMMode) + { + case PSK2: + + *intDataLen = 19; + *intRSLen = 6; // Must be even + *Symbols = 8; + *Mode = 2; + + break; + + case PSK4: + + *intDataLen = 40; + *intRSLen = 10; + *Symbols = 4; + *Mode = 4; + break; + + case PSK8: + + *intDataLen = 57; // Must be multiple of 3 + *intRSLen = 18; // Must be multiple of 3 and even (so multiple of 6) + *Symbols = 8; // Actually 8 symbols for 3 bytes + *Mode = 8; + break; + + case PSK16: + + *intDataLen = 80; + *intRSLen = 20; + *Symbols = 2; + *Mode = 16; + + break; + + case QAM16: + + *intDataLen = 80; + *intRSLen = 20; + *Symbols = 2; + *Mode = 8; + break; + + case QAM32: + + *intDataLen = 100; + *intRSLen = 25; + *Symbols = 8; // Actually 8 symbols for 3 bytes + *Mode = 16; + break; + + case PSK4S: + + *intDataLen = 12; + *intRSLen = 4; + *Symbols = 4; + *Mode = 4; + break; + + + default: + + *intDataLen = *intRSLen = 0; + } +} + +int EncodeOFDMData(UCHAR bytFrameType, UCHAR * bytDataToSend, int Length, unsigned char * bytEncodedBytes) +{ + // Output is a byte array which includes: + // 1) A 2 byte Header which include the Frame ID. This will be sent using 4FSK at 50 baud. It will include the Frame ID and ID Xored by the Session bytID. + // 2) n sections one for each carrier that will include all data (with FEC appended) for the entire frame. Each block will be identical in length. + + // Each carrier starts with an 8 bit block number, which may not be sequential (each carrier is ack'ed separately) + // and may be repeated (for redundancy) + + // OFDM Has several modes, selected by OFDMMode not Frame Type (all are OFDM.500 or OFDM.2500 + + // For the moment will will send all carriers wirh the same mode (I don't think there is an advantage being different), + // So we can use a different block length in each mode. We need to keep record of which blocks within bytDataToSend have + // been acked. Note that the first block is always unacked - acked data at the front of buffer is removed. + + + int intNumCar, intBaud, intDataLen, intRSLen, bytDataToSendLengthPtr, intEncodedDataPtr; + + int intCarDataCnt; + BOOL blnOdd; + char strType[18]; + char strMod[16]; + BOOL blnFrameTypeOK; + UCHAR bytQualThresh; + int i, j, Dummy; + UCHAR * bytToRS = &bytEncodedBytes[2]; + int RepeatIndex = 0; // used to duplicate data if too short to fill frame + + blnFrameTypeOK = FrameInfo(bytFrameType, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytQualThresh, strType); + + if (intDataLen == 0 || Length == 0 || !blnFrameTypeOK) + return 0; + + GetOFDMFrameInfo(OFDMMode, &intDataLen, &intRSLen, &Dummy, &Dummy); + + // Generate the 2 bytes for the frame type data: + + CarriersSent = intNumCar; + + bytEncodedBytes[0] = bytFrameType; + bytEncodedBytes[1] = bytFrameType ^ bytSessionID; + + bytDataToSendLengthPtr = 0; + firstNewCarrier = -1; + + intEncodedDataPtr = 2; + + UnAckedBlockPtr = 127; // We send unacked blocks backwards + + // Length is data still queued. BytesSent is unacked data + + Length -= BytesSent; // New data to send + + if (Length == 0) + DontSendNewData = 1; + + Debugprintf("OFDM Bytes to Send %d DontSendNewData %d", Length, DontSendNewData); + + // Often the first carrier is the only one missed, and if we repeat it first it will always + // fail. So it would be good if logical block number 0 isn't always sent on carrier 0 + + // The carrier number must match the block number so we can ack it. + + + + for (i = 0; i < intNumCar; i++) // across all carriers + { + int blkLen; + int blkNum; + + intCarDataCnt = Length - bytDataToSendLengthPtr; + + // If we have no new data to send we would repeat the last sent blocks from + // SentOFDMBlocks which is wrong if there is no new data. So in that case just + // send outstanding data. + + if (DontSendNewData && BytesSent) // Just send outstanding data repeatedly if necessary + { + OFDMBlocks[i] = blkNum = GetNextOFDMBlockNumber(&blkLen); + OFDMBlockLen[i] = UnackedOFDMBlockLen[blkNum] = blkLen; + UnackedOFDMBlocks[blkNum] = 1; + + } + else if (Duplicate & (i >= ((intNumCar - firstNewCarrier) /2))) + goto repeatblocks; + + else if (intCarDataCnt > intDataLen) // why not > ?? + { + // Won't all fit +tryagain: + OFDMBlocks[i] = blkNum = GetNextOFDMBlockNumber(&blkLen); + + if (blkLen == -1) + { + // New Block. Make sure it will fit in window + + int limit; + + if (intNumCar == 9) + limit = 24; // Don't want too many outstanding or shift up will be slow + else + limit = 125; + + if (firstNewCarrier == -1) + firstNewCarrier = i; + + if ((NextOFDMBlock + (intNumCar - i)) > limit) + { + // no room + + NextOFDMBlock-- ; // we aren't going to send it + UnAckedBlockPtr = 127; // send unacked again + goto tryagain; + } + + blkLen = intDataLen; + bytDataToSendLengthPtr += intDataLen; // Don't reduce bytes to send if repeating + BytesSent += intDataLen; + } + + OFDMBlockLen[i] = UnackedOFDMBlockLen[blkNum] = blkLen; + UnackedOFDMBlocks[blkNum] = 1; + } + else + { + // Last bit + + memset(&bytToRS[0], 0, intDataLen); + + bytToRS[0] = intCarDataCnt; // Could be 0 if insuffient data for # of carriers + + if (intCarDataCnt > 0) + { + OFDMBlocks[i] = blkNum = GetNextOFDMBlockNumber(&blkLen); + if (blkLen == -1) + { + if (firstNewCarrier == -1) + firstNewCarrier = i; + + blkLen = intCarDataCnt; // Could be 0 if insuffient data for # of carriers + bytDataToSendLengthPtr += intCarDataCnt; // Don't reduce bytes to send if repeating + BytesSent += intCarDataCnt; + if (intCarDataCnt < intDataLen) + DontSendNewData = TRUE; // sending a part block so mustnt send more till all acked + } + + UnackedOFDMBlockLen[blkNum] = OFDMBlockLen[i] = blkLen; + UnackedOFDMBlocks[blkNum] = 1; + } + else + { + // No more data to send - duplicate sent carriers. Gives extra redundancy +repeatblocks: + blkNum = OFDMBlocks[RepeatIndex]; + blkLen = OFDMBlockLen[RepeatIndex++]; + OFDMBlocks[i] = blkNum; + OFDMBlockLen[i] = blkLen; + UnackedOFDMBlockLen[blkNum] = blkLen; + UnackedOFDMBlocks[blkNum] = 1; + } + } + } + + // We now have pointers to the logical blocks in OFDMBlocks/Len. We don't + // have to modulate in that order, but must update SentOFDMBlocks with the real + // Carrier number + + j = rand() % intNumCar; + + for (i = 0; i < intNumCar; i++) + { + if (j >= intNumCar) + j = 0; + + SentOFDMBlockLen[i] = bytToRS[0] = OFDMBlockLen[j]; + SentOFDMBlocks[i] = bytToRS[1] = OFDMBlocks[j++]; + + Debugprintf("Sending OFDM Carrier %d Block %d Len %d", i,bytToRS[1], bytToRS[0]); + memcpy(&bytToRS[2], GetNextOFDMBlock(bytToRS[1], intDataLen), bytToRS[0]); + + GenCRC16Normal(bytToRS, intDataLen + 2); // calculate the CRC on the byte count + data bytes + + // Data + RS + 1 byte byteCount + 1 byte blockno + 2 Byte CRC + + RSEncode(bytToRS, bytToRS+intDataLen+4, intDataLen + 4, intRSLen); // Generate the RS encoding + + intEncodedDataPtr += intDataLen + 4 + intRSLen; + + bytToRS += intDataLen + 4 + intRSLen; + } + + return intEncodedDataPtr; +} + + +// OFDM RX Routines + +extern int NErrors; + +BOOL Decode4FSKOFDMACK() +{ + BOOL FrameOK; + BOOL blnRSOK; + + // 6 Byte payload, 2 CRC 4 RS + + if (CheckCRC16(&bytFrameData[0][0], 6)) + { + Debugprintf("OFDMACK Decode OK"); + return TRUE; + } + + // Try RS Correction + + + FrameOK = RSDecode(&bytFrameData[0][0], 12, 4, &blnRSOK); + + if (FrameOK && blnRSOK == FALSE) + { + // RS Claims to have corrected it, but check + + Debugprintf("OFDMACK %d Errors Corrected by RS", NErrors); + + if (CheckCRC16(&bytFrameData[0][0], 6)) + { + Debugprintf("OFDMACK Corrected by RS OK"); + return TRUE; + } + } + Debugprintf("OFDMACK Decode Failed after RS"); + + return FALSE; +} + + + +void RemoveProcessedOFDMData() +{ + // ISS has changed toggle, so last ack was processed. + + // if the last frame wasn't completely decoded then we need to remove any data sent to host and corresponding + // entries in goodReceivedBlocks and goodReceivedBlockLen + + // This allows us to accumulate carriers from repeated frames. This could be good for FEC, but I think it is + // of limited value for ARQ. Is it worth it ??? + + + int i, n, Len = 0; + + for (i = 0; i < 128; i++) + { + n = goodReceivedBlockLen[i]; + + if (n) + Len += n; + else + break; // exit loop on first missed block. + } + + // i is number of blocks to remove + + if (i == 0) + return; + + Debugprintf("Removing %d received OFDM blocks Length %d", i, Len); + + memmove(goodReceivedBlocks, &goodReceivedBlocks[i], 128 - i); + memmove(goodReceivedBlockLen, &goodReceivedBlockLen[i], 128 - i); + memset(&goodReceivedBlocks[128 - i], 0, i); + memset(&goodReceivedBlockLen[128 - i], 0, i); + memmove(bytData, &bytData[Len], sizeof(bytData) - Len); +} + + +VOID InitDemodOFDM() +{ + // Called at start of frame + + int i; + float dblPhase, dblReal, dblImag; + short modePhase[MAXCAR][3]; + int OFDMType[MAXCAR] = {0}; + int ModeCount[8] = {0}; + int MaxModeCount = 0; + char Msg[64]; + + intSampPerSym = 240; + + floatCarFreq = 1500.0f + ((intNumCar /2) * 10000.0f) / 180.0f; // Top freq (spacing is 10000/180) + + for (i= 0; i < intNumCar; i++) + { + // OFDM uses 55.5555 Hz carrier interval + + intCP[i] = 24; //CP length + intNforGoertzel[i] = 216; + dblFreqBin[i] = floatCarFreq / 55.5555f; + + // Get initial Reference Phase + + GoertzelRealImag(intFilteredMixedSamples, intCP[i], intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); + dblPhase = atan2f(dblImag, dblReal); + + // Set initial mag from Reference Phase and Mode Bits (which should be full power) + + intCarMagThreshold[i] = sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); + + intPSKPhase_1[i] = 1000 * dblPhase; + + // Get the 3 OFDM mode bits + + GoertzelRealImag(intFilteredMixedSamples + 240, intCP[i], intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); + dblPhase = atan2f(dblImag, dblReal); + + intPSKPhase_0[i] = 1000 * atan2f(dblImag, dblReal); + modePhase[i][0] = -(ComputeAng1_Ang2(intPSKPhase_0[i], intPSKPhase_1[i])); + intPSKPhase_1[i] = intPSKPhase_0[i]; + + intCarMagThreshold[i] += sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); + + + GoertzelRealImag(intFilteredMixedSamples + 480, intCP[i], intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); + dblPhase = atan2f(dblImag, dblReal); + + intPSKPhase_0[i] = 1000 * atan2f(dblImag, dblReal); + modePhase[i][1] = -(ComputeAng1_Ang2(intPSKPhase_0[i], intPSKPhase_1[i])); + intPSKPhase_1[i] = intPSKPhase_0[i]; + + intCarMagThreshold[i] += sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); + + GoertzelRealImag(intFilteredMixedSamples + 720, intCP[i], intNforGoertzel[i], dblFreqBin[i], &dblReal, &dblImag); + dblPhase = atan2f(dblImag, dblReal); + + intPSKPhase_0[i] = 1000 * atan2f(dblImag, dblReal); + modePhase[i][2] = -(ComputeAng1_Ang2(intPSKPhase_0[i], intPSKPhase_1[i])); + intPSKPhase_1[i] = intPSKPhase_0[i]; + + intCarMagThreshold[i] += sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); + intCarMagThreshold[i] *= 0.75f; + + // We have accumulated 4 values so divide by 4 + + intCarMagThreshold[i] /= 4.0f; + + if (modePhase[i][0] >= 1572 || modePhase[i][0] <= -1572) + OFDMType[i] |= 1; + + if (modePhase[i][1] >= 1572 || modePhase[i][1] <= -1572) + OFDMType[i] |= 2; + + if (modePhase[i][2] >= 1572 || modePhase[i][2] <= -1572) + OFDMType[i] |= 4; + + floatCarFreq -= 55.555664f; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + } + + // Get RX Mode. May be corrupt on some carriers, so go with majority + // But incorrectly seeing a change will cause corruption, so perhaps + // need more than simple majority + + // Or maybe don't actually clear old data until we decode at least + // one frame. That way an incorrect frame type won't cause a problem + // (as frame won't decode). But what if type is correct and frame still + // won't decode ?? + + // So if almost all types aren't the same, and type is new, discard. + + + + for (i = 0; i < intNumCar; i++) + ModeCount[OFDMType[i]]++; + + for (i = 0; i < 8; i++) + { + if (ModeCount[i] > MaxModeCount) + { + MaxModeCount = ModeCount[i]; + RXOFDMMode = i; + } + } + + if (MaxModeCount != intNumCar) + Debugprintf("Not all OFDM Types the same (%d)", intNumCar - MaxModeCount); + + + if (RXOFDMMode != lastOFDMRXMode) + { + // has changed. Only accept if all ok + // ?? is this a bit extreme ??. Try 1 error + + if (MaxModeCount < (intNumCar - 1)) + { + // Not sure. Safer to assume wrong + // if it really has changed decode will fail + // and frame repeat + + RXOFDMMode = lastOFDMRXMode; + + Debugprintf("New OFDM Mode but more than 1 carrier different (%d) - assume corrupt and don't change", intNumCar - MaxModeCount); + } + } + + GetOFDMFrameInfo(RXOFDMMode, &intDataLen, &intRSLen, &intPSKMode, &intSymbolsPerByte); + + // if OFDM mode (or frame type) has changed clear any received but unprocessed data + + // If we aren't going to decode because it is a repeat we don't need to + // check, as type can't have changed, and new type might be corrupt + + if (!RepeatedFrame || (memcmp(CarrierOk, Bad, intNumCar) == 0)) + { + // We are going to decode it, so check + + if (RXOFDMMode != lastOFDMRXMode || (intFrameType & 0xFE) != (LastDemodType & 0xFE)) + { + memset(goodReceivedBlocks, 0, sizeof(goodReceivedBlocks)); + memset(goodReceivedBlockLen, 0, sizeof(goodReceivedBlockLen)); + BytesSenttoHost = 0; + + lastOFDMRXMode = RXOFDMMode; + + if ((intFrameType & 0xFE) != (LastDemodType & 0xFE)) + Debugprintf("New OFDM Mode - clear any received data"); + else + Debugprintf("New Frame Type - clear any received data"); + } + } + + Track1CarPSK(floatCarFreq, intPSKMode, FALSE, TRUE, dblPhase, TRUE); + + SymbolsLeft = intDataLen + intRSLen + 4; // Data has length Blockno and CRC + + dblPhaseInc = 2 * M_PI * 1000 / intPSKMode; + intPhasesLen = 0; + + PSKInitDone = TRUE; + + Debugprintf("OFDM Mode %s", OFDMModes[RXOFDMMode]); + + sprintf(Msg, "%s/%s", Name(intFrameType), OFDMModes[RXOFDMMode]); + DrawRXFrame(0, Msg); +} + +VOID Decode1CarOFDM(int Carrier) +{ + unsigned int intData; + int k; + float dblAlpha = 0.1f; // this determins how quickly the rolling average dblTrackingThreshold responds. + + // dblAlpha value of .1 seems to work well...needs to be tested on fading channel (e.g. Multipath) + + int Threshold = intCarMagThreshold[Carrier]; + int Len = intPhasesLen; + + UCHAR * Decoded = bytFrameData[0]; // Always use first buffer + + pskStart = 0; + charIndex = 0; + + // We calculated initial mag from reference symbol + + // use filtered tracking of refernce phase amplitude + // (should be full amplitude value) + + // On WGN this appears to improve decoding threshold about 1 dB 9/3/2016 + + while (Len >= 0) + { + // Phase Samples are in intPhases + + intData = 0; + + for (k = 0; k < 2; k++) + { + intData <<= 4; + + if (intPhases[Carrier][pskStart] < 393 && intPhases[Carrier][pskStart] > -393) + { + } // Zero so no need to do anything + else if (intPhases[Carrier][pskStart] >= 393 && intPhases[Carrier][pskStart] < 1179) + intData += 1; + else if (intPhases[Carrier][pskStart] >= 1179 && intPhases[Carrier][pskStart] < 1965) + intData += 2; + else if (intPhases[Carrier][pskStart] >= 1965 && intPhases[Carrier][pskStart] < 2751) + intData += 3; + else if (intPhases[Carrier][pskStart] >= 2751 || intPhases[Carrier][pskStart] < -2751) + intData += 4; + else if (intPhases[Carrier][pskStart] >= -2751 && intPhases[Carrier][pskStart] < -1965) + intData += 5; + else if (intPhases[Carrier][pskStart] >= -1965 && intPhases[Carrier][pskStart] <= -1179) + intData += 6; + else + intData += 7; + + if (intMags[Carrier][pskStart] < Threshold) + { + intData += 8; // add 8 to "inner circle" symbols. + Threshold = (Threshold * 900 + intMags[Carrier][pskStart] * 150) / 1000; + } + else + { + Threshold = ( Threshold * 900 + intMags[Carrier][pskStart] * 75) / 1000; + } + + intCarMagThreshold[Carrier] = Threshold; + pskStart++; + } + Decoded[charIndex++] = intData; + Len -=2; + } +} + +BOOL DemodOFDM() +{ + int Used = 0; + int Start = 0; + int i, n, MemARQOk = 0; + int skip = rand() % intNumCar; + + // We can't wait for the full frame as we don't have enough RAM, so + // we do one DMA Buffer at a time, until we run out or end of frame + + // Only continue if we have enough samples + + while (State == AcquireFrame) + { + if (PSKInitDone == 0) // First time through + { + if (intFilteredMixedSamplesLength < (240 * 4)) // Reference and 3 Mode bits + return FALSE; + + InitDemodOFDM(); + intFilteredMixedSamplesLength -= 4 * intSampPerSym; + + if (intFilteredMixedSamplesLength < 0) + Debugprintf("Corrupt intFilteredMixedSamplesLength"); + + Start += 4 * intSampPerSym; + + // We normally don't decode Repeated frames. But if all carriers failed to + // decode we should + + if (RepeatedFrame) + { + if (memcmp(CarrierOk, Bad, intNumCar) == 0) + RepeatedFrame = FALSE; + } + } + + if (intFilteredMixedSamplesLength < intSymbolsPerByte * intSampPerSym + 10) + { + // Move any unprocessessed data down buffer + + // (while checking process - will use cyclic buffer eventually + + if (intFilteredMixedSamplesLength > 0) + memmove(intFilteredMixedSamples, + &intFilteredMixedSamples[Start], intFilteredMixedSamplesLength * 2); + + return FALSE; + } + + + // call the decode char routine for each carrier + + // start at the highest carrier freq which is actually the lowest transmitted carrier due to Reverse sideband mixing + + floatCarFreq = 1500.0f + ((intNumCar / 2) * 10000.0f) / 180.0f; // spacing is 10000/180 = 55.5555555 + + for (i = 0; i < intNumCar; i++) + { + Used = Demod1CarOFDMChar(Start, i, intSymbolsPerByte); // demods 2 phase values - enough for one char + intPhasesLen -= intSymbolsPerByte; + floatCarFreq -= 55.555664f; // Step through each carrier Highest to lowest which is equivalent to lowest to highest before RSB mixing. + } + + intPhasesLen += intSymbolsPerByte; + + if (RXOFDMMode == PSK8) + SymbolsLeft -=3; + else + SymbolsLeft--; // number still to decode + + + Start += Used; + intFilteredMixedSamplesLength -= Used; + + if (intFilteredMixedSamplesLength < 0) + Debugprintf("Corrupt intFilteredMixedSamplesLength"); + + + if (SymbolsLeft <= 0) + { + // Frame complete - decode it + + DecodeCompleteTime = Now; + + // prepare for next so we can exit when we have finished decode + + DiscardOldSamples(); + ClearAllMixedSamples(); + State = SearchingForLeader; + + // Rick uses the last carrier for Quality + // Get quality from middle carrier (?? is this best ?? + + intLastRcvdFrameQuality = UpdatePhaseConstellation(&intPhases[intNumCar/2][0], &intMags[intNumCar/2][0], intPSKMode, FALSE, TRUE); + + // Decode the phases. Mode was determined from header + + frameLen = 0; + + if (RepeatedFrame) + { + Debugprintf("Repeated frame - discard"); + + frameLen = BytesSenttoHost; + return TRUE; + } + + for (i = 0; i < intNumCar; i++) + { + UCHAR decodeBuff[256]; // 82 doesnt seem to be enough ??Max length of OFDM block + int decodeLen, ofdmBlock;; + + CarrierOk[i] = 0; // Always reprocess carriers + + if (RXOFDMMode == QAM16) + Decode1CarOFDM(i); + else + Decode1CarPSK(i, TRUE); + + // with OFDM each carrier has a sequence number, as we can do selective repeats if a carrier is missed. + // so decode into a separate buffer, and copy good data into the correct place in the received data buffer. + + decodeLen = CorrectRawDataWithRS(&bytFrameData[0][0], decodeBuff , intDataLen + 1, intRSLen, intFrameType, i); + + // if decode fails try with a tuning offset correction + + if (CarrierOk[i] == 0) + { + CorrectPhaseForTuningOffset(&intPhases[i][0], intPhasesLen, intPSKMode); + + if (RXOFDMMode == QAM16) + Decode1CarOFDM(i); + else + Decode1CarPSK(i, TRUE); + + decodeLen = CorrectRawDataWithRS(&bytFrameData[0][0], decodeBuff , intDataLen + 1, intRSLen, intFrameType, i); + } + + OFDMCarriersReceived[RXOFDMMode]++; + + if (CarrierOk[i]) + { + ofdmBlock = decodeBuff[0]; + + // CRC check isn't perfect. At least we can check that Block and Length + // are reasonable + + if (ofdmBlock < 128 && decodeLen <= intDataLen) + { + // copy data to correct place in bytData + + OFDMCarriersDecoded[RXOFDMMode]++; + + if (goodReceivedBlocks[ofdmBlock] == 0) + { + memcpy(&bytData[intDataLen * ofdmBlock], &decodeBuff[1], decodeLen); + goodReceivedBlocks[ofdmBlock] = 1; + goodReceivedBlockLen[ofdmBlock] = decodeLen; + } + } + } + } + + // Pass any contiguous blocks starting from 0 to host (may need to reconsider!) + + for (i = 0; i < 128; i++) + { + n = goodReceivedBlockLen[i]; + + if (n) + frameLen += n; + else + break; // exit loop on first missed block. + } + + // If this is a repeated frame, we should only send any data that is beyond what we sent at last try + + BytesSenttoHost = frameLen; + } + + // if all carriers have been decoded we must have passed all data to the host, so clear partial receive info + + if (memcmp(CarrierOk, Good, intNumCar) == 0) + { + memset(goodReceivedBlocks, 0, sizeof(goodReceivedBlocks)); + memset(goodReceivedBlockLen, 0, sizeof(goodReceivedBlockLen)); + BytesSenttoHost = 0; + } + } + return TRUE; +} + +int Demod1CarOFDMChar(int Start, int Carrier, int intNumOfSymbols) +{ + // Converts intSample to an array of differential phase and magnitude values for the Specific Carrier Freq + // intPtr should be pointing to the approximate start of the first reference/training symbol (1 of 3) + // intPhase() is an array of phase values (in milliradians range of 0 to 6283) for each symbol + // intMag() is an array of Magnitude values (not used in PSK decoding but for constellation plotting or QAM decoding) + // Objective is to use Minimum Phase Error Tracking to maintain optimum pointer position + + // It demodulates one byte's worth of samples (2 or 4) + + float dblReal, dblImag, dblPhase; + int intMiliRadPerSample = floatCarFreq * M_PI / 6; + int i; + int origStart = Start; +// int Corrections; + + // With OFDM we save received data in Receive buffer, so don't keep + // the raw frames. So we must always decode + + if (RepeatedFrame) // We just repeat previous ack/nak, so don't bother to decode + { + intPhasesLen += intNumOfSymbols; + return intSampPerSym * intNumOfSymbols; + } + + for (i = 0; i < intNumOfSymbols; i++) + { + GoertzelRealImag(intFilteredMixedSamples, Start + intCP[Carrier], intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); + intMags[Carrier][intPhasesLen] = sqrtf(powf(dblReal, 2) + powf(dblImag, 2)); + dblPhase = atan2f(dblImag, dblReal); + intPSKPhase_0[Carrier] = 1000 * dblPhase; + intPhases[Carrier][intPhasesLen] = -(ComputeAng1_Ang2(intPSKPhase_0[Carrier], intPSKPhase_1[Carrier])); + + + // Should we track each carrier ?? +/* + if (Carrier == 0) + { + Corrections = Track1CarPSK(floatCarFreq, intPSKMode, FALSE, TRUE, dblPhase, FALSE); + + if (Corrections != 0) + { + Start += Corrections; + + GoertzelRealImag(intFilteredMixedSamples, Start + intCP[Carrier], intNforGoertzel[Carrier], dblFreqBin[Carrier], &dblReal, &dblImag); + intPSKPhase_0[Carrier] = 1000 * atan2f(dblImag, dblReal); + } + } +*/ + intPSKPhase_1[Carrier] = intPSKPhase_0[Carrier]; + intPhasesLen++; + Start += intSampPerSym; + } + + if (AccumulateStats) + intOFDMSymbolCnt += intNumOfSymbols; + + return (Start - origStart); // Symbols we've consumed +} + + +VOID EncodeAndSendOFDMACK(UCHAR bytSessionID, int LeaderLength, int Chan) +{ + // OFDMACK has one bit per carrier. As this needs 43 bits meassage is 6 bytes. The spare 5 bits are + // used to send quality + + unsigned long long val = intLastRcvdFrameQuality >> 2; + int i; + + // Not sure if best to use CRC or FEC. Could use both, but message gets a bit long. + // Lets go with crc for now + // Now try CRC and 4 RS. OTT but see if it reduces number + // of failed OFDMACKs + + bytEncodedBytes[0] = OFDMACK; + bytEncodedBytes[1] = OFDMACK ^ bytSessionID; + + for (i = MAXCAR - 1; i >= 0; i--) + { + val <<= 1; + + if (CarrierOk[i]) + val |= 1; + } + + for (i = 2; i < 8; i++) + { + bytEncodedBytes[i] = val & 0xff; + val >>= 8; + } + + GenCRC16Normal(&bytEncodedBytes[2], 6); // calculate the CRC + + RSEncode(&bytEncodedBytes[2], &bytEncodedBytes[10], 8, 4); // Generate the RS encoding ...now 14 bytes total + + Mod4FSKDataAndPlay(&bytEncodedBytes[0], 14, LeaderLength, Chan); + +} + +UCHAR bytLastSym[43]; + +float dblOFDMCarRatio = 0.5f; + + +void SendOFDM2PSK(int symbol, int intNumCar) +{ + int intCarIndex = (MAXCAR - intNumCar) / 2; + int intSample; + int OFDMFrame[240] = {0}; // accumulated samples for each carrier + short OFDMSamples[240]; // 216 data, 24 CP + int i, n, p, q; // start at 24, copy CP later + + for (i = 0; i < intNumCar; i++) // across all active carriers + { + p = 24; + memset(OFDMSamples, 0, sizeof(OFDMSamples)); + + for (n = 0; n < 216; n++) + { + if (symbol) + OFDMSamples[p++] -= intOFDMTemplate[intCarIndex][0][n]; + else + OFDMSamples[p++] += intOFDMTemplate[intCarIndex][0][n]; + } + + // we now have the 216 samples. Copy last 24 to front as CP + + memcpy(OFDMSamples, &OFDMSamples[216], 24 * 2); + + // and add into the multicarrier value + + for (q = 0; q < 240; q++) + OFDMFrame[q] += OFDMSamples[q]; + + // now do the next carrier + + bytLastSym[intCarIndex] = symbol; + intCarIndex++; + } + + // Done all carriers - send sample + + for (q = 0; q < 240; q++) + { + intSample = OFDMFrame[q] / intNumCar; + ARDOPSampleSink((intSample * OFDMLevel)/100); + } +} + + + +void ModOFDMDataAndPlay(unsigned char * bytEncodedBytes, int Len, int intLeaderLen, int Chan) +{ + int intNumCar, intBaud, intDataLen, intRSLen, intDataPtr, intSampPerSym, intDataBytesPerCar; + BOOL blnOdd; + int Type = bytEncodedBytes[0]; + + int intSample; + char strType[18] = ""; + char strMod[16] = ""; + UCHAR bytSym, bytSymToSend, bytMinQualThresh; + float dblCarScalingFactor; + int intMask = 0; + int intLeaderLenMS; + int i, j, k, s, n; + int intCarStartIndex; + int intPeakAmp; + int intCarIndex; + BOOL QAM = 0; + int OFDMFrame[240] = { 0 }; // accumulated samples for each carrier + short OFDMSamples[240]; // 216 data, 24 CP + int p, q; // start at 24, copy CP later + char fType[64]; + + if (!FrameInfo(Type, &blnOdd, &intNumCar, strMod, &intBaud, &intDataLen, &intRSLen, &bytMinQualThresh, strType)) + return; + + intDataBytesPerCar = (Len - 2) / intNumCar; // We queue the samples here, so dont copy below + + intCarIndex = intCarStartIndex = (MAXCAR - intNumCar) / 2; + + switch (OFDMMode) + { + case PSK2: + + s = 8; // 8 symbols per byte + break; + + case PSK4: + case PSK4S: + + s = 4; // 4 symbols per byte + break; + + case PSK8: + + s = 8; // 8 symbols for 3 bytes + break; + + case PSK16: + + s = 2; // 2 symbols per byte + break; + + case QAM16: + + s = 2; // 2 symbols per byte + QAM = 1; + break; + + default: + + Debugprintf("Undefined OFDM Mode %d", OFDMMode); + return; + } + + intSampPerSym = 216; // 55 baud + + if (Type == PktFrameData) + { + intDataBytesPerCar = pktDataLen + pktRSLen + 3; + intDataPtr = 11; // Over Header + goto PktLoopBack; + } + + Debugprintf("Sending Frame Type %s Mode %s", strType, OFDMModes[OFDMMode]); + sprintf(fType, "%s/%s", strType, OFDMModes[OFDMMode]); + DrawTXFrame(fType); + + if (intNumCar == 3) + { + initFilter(500, 1500, Chan); + OFDMLevel = 80; + } + else if (intNumCar == 9) + { + initFilter(500, 1500, Chan); + OFDMLevel = 100; + } + else + { + initFilter(2500, 1500, Chan); + OFDMLevel = 125; + } + + if (intLeaderLen == 0) + intLeaderLenMS = LeaderLength; + else + intLeaderLenMS = intLeaderLen; + + // Create the leader + + SendLeaderAndSYNC(bytEncodedBytes, intLeaderLen); + + intPeakAmp = 0; + + intDataPtr = 2; // initialize pointer to start of data. + +PktLoopBack: // Reenter here to send rest of variable length packet frame + + + // Now create a reference symbol for each carrier + + // We have to do each carrier for each sample, as we write + // the sample immediately + + SendOFDM2PSK(0, intNumCar); // Reference symbol is always zero, so same in any mode + + // Now send OFDM Type as 3 x 2PSK symbols. Same value sent on all carriers. The Type is send as 2PSK + // bytLastSym ends up correct + + + bytSym = (OFDMMode) & 1; + bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 1); // Values 0=1 + SendOFDM2PSK(bytSymToSend, intNumCar); + + bytSym = (OFDMMode >> 1) & 1; + bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 1); // Values 0-1 + SendOFDM2PSK(bytSymToSend, intNumCar); + + bytSym = (OFDMMode >> 2) & 1; + bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 1); // Values 0-1 + SendOFDM2PSK(bytSymToSend, intNumCar); + + // Correct bytLastSYM to match PSK level of actual mode + + for (i = intCarStartIndex; i < intCarStartIndex + intNumCar; i++) + { + if (OFDMMode == PSK4 || OFDMMode == PSK4S) + bytLastSym[i] <<= 1; + else if (OFDMMode == PSK8 || OFDMMode == QAM16) + bytLastSym[i] <<= 2; + if (OFDMMode == PSK16) + bytLastSym[i] <<= 3; + } + + // Unlike ARDOP_WIN we send samples as they are created, + // so we loop through carriers, then data bytes + + for (j = 0; j < intDataBytesPerCar; j++) // for each referance and data symbol + { + // Loop through each symbol of byte (4 for PSK 2 for QAM + + for (k = 0; k < s; k++) + { + // with OFDM we must create separate samples for each + // carrier, so we can add the cyclic prefix + + intCarIndex = intCarStartIndex; // initialize the carrrier index + + for (i = 0; i < intNumCar; i++) // across all active carriers + { + if (OFDMMode == PSK2) + { + bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> ((7 - k))) & 1; + bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 1); // Values 0-1 + } + else if (OFDMMode == PSK4 || OFDMMode == PSK4S) + { + bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> (2 * (3 - k))) & 3; + bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 3); // Values 0-3 + } + else if (OFDMMode == PSK8) + { + // More complex ...must go through data in 3 byte chunks creating 8 Three bit symbols for each 3 bytes of data. + + bytSym = GetSym8PSK(intDataPtr, k, i, bytEncodedBytes, intDataBytesPerCar); + bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 7); // mod 8 + } + else if (OFDMMode == PSK16) + { + bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> (4 * (1 - k))) & 15; + bytSymToSend = ((bytLastSym[intCarIndex] + bytSym) & 15); // Values 0-3 + } + else + { + // 16QAM + + bytSym = (bytEncodedBytes[intDataPtr + i * intDataBytesPerCar] >> (4 * (1 - k))) & 15; + bytSymToSend = ((bytLastSym[intCarIndex] & 7) + (bytSym & 7) & 7); // Compute the differential phase to send + bytSymToSend = bytSymToSend | (bytSym & 8); // add in the amplitude bit directly from symbol + } + p = 24; + memset(OFDMSamples, 0, sizeof(OFDMSamples)); + + for (n = 0; n < intSampPerSym; n++) + { + if (OFDMMode == PSK2) + { + if (bytSymToSend) // This uses the symmetry of the symbols to reduce the table size by a factor of 2 + OFDMSamples[p++] -= intOFDMTemplate[intCarIndex][0][n]; + else + OFDMSamples[p++] += intOFDMTemplate[intCarIndex][0][n]; + } + else if (OFDMMode == PSK4 || OFDMMode == PSK4S) + { + if (bytSymToSend < 2) // This uses the symmetry of the symbols to reduce the table size by a factor of 2 + OFDMSamples[p++] += intOFDMTemplate[intCarIndex][4 * bytSymToSend][n]; // double the symbol value during template lookup for 4PSK. (skips over odd PSK 8 symbols) + else + OFDMSamples[p++] -= intOFDMTemplate[intCarIndex][4 * (bytSymToSend - 2)][n]; // subtract 2 from the symbol value before doubling and subtract value of table + } + else if (OFDMMode == PSK16) + { + if (bytSymToSend < 8) // This uses the symmetry of the symbols to reduce the table size by a factor of 2 + OFDMSamples[p++] += intOFDMTemplate[intCarIndex][bytSymToSend][n]; // double the symbol value during template lookup for 4PSK. (skips over odd PSK 8 symbols) + else + OFDMSamples[p++] -= intOFDMTemplate[intCarIndex][(bytSymToSend - 8)][n]; // subtract 2 from the symbol value before doubling and subtract value of table + } + else + { + // This works for both 8PSK and 16QAM as 8PSK does'nt have the ampiltude bit + // 4bits/symbol (use table symbol values 0, 1, 2, 3, -0, -1, -2, -3) and modulate amplitude with MSB + + if (bytSymToSend < 4)// This uses the symmetry of the symbols to reduce the table size by a factor of 2 + OFDMSamples[p++] = intOFDMTemplate[intCarIndex][2 * bytSymToSend][n]; // double the symbol value during template lookup for 4PSK. (skips over odd PSK 8 symbols) + else if (bytSymToSend < 8) + OFDMSamples[p++] = -intOFDMTemplate[intCarIndex][2 * (bytSymToSend - 4)][n]; // subtract 4 from the symbol value before doubling and subtract value of table + else if (bytSymToSend < 12) + OFDMSamples[p++] = dblOFDMCarRatio * intOFDMTemplate[intCarIndex][2 * (bytSymToSend - 8)][n]; // subtract 4 from the symbol value before doubling and subtract value of table + else + OFDMSamples[p++] = -dblOFDMCarRatio * intOFDMTemplate[intCarIndex][2 * (bytSymToSend - 12)][n]; // subtract 4 from the symbol value before doubling and subtract value of table + } + } + + // we now have the 216 samples. Copy last 24 to front as CP + + memcpy(OFDMSamples, &OFDMSamples[216], 24 * 2); + + // and add into the multicarrier value + + for (q = 0; q < 240; q++) + OFDMFrame[q] += OFDMSamples[q]; + + // now do the next carrier + + bytLastSym[intCarIndex] = bytSymToSend; + intCarIndex++; + } + + + // Done all carriers - send sample + + for (q = 0; q < 240; q++) + { + intSample = OFDMFrame[q] / intNumCar; + ARDOPSampleSink((intSample * OFDMLevel) / 100); + OFDMFrame[q] = 0; + } + + } + if (OFDMMode == PSK8) + { + intDataPtr += 3; + j += 2; // We've used 3 bytes + } + else + intDataPtr++; + } + + if (Type == PktFrameHeader) + { + // just sent packet header. Send rest in current mode + + Type = 0; // Prevent reentry + + strcpy(strMod, &pktMod[pktMode][0]); + intDataBytesPerCar = pktDataLen + pktRSLen + 3; + intDataPtr = 11; // Over Header + intNumCar = pktCarriers[pktMode]; + + switch (intNumCar) + { + case 1: + intCarStartIndex = 4; + // dblCarScalingFactor = 1.0f; // Starting at 1500 Hz (scaling factors determined emperically to minimize crest factor) TODO: needs verification + dblCarScalingFactor = 1.2f; // Starting at 1500 Hz Selected to give < 13% clipped values yielding a PAPR = 1.6 Constellation Quality >98 + case 2: + intCarStartIndex = 3; + // dblCarScalingFactor = 0.53f; + if (strcmp(strMod, "16QAM") == 0) + dblCarScalingFactor = 0.67f; // Carriers at 1400 and 1600 Selected to give < 2.5% clipped values yielding a PAPR = 2.17, Constellation Quality >92 + else + dblCarScalingFactor = 0.65f; // Carriers at 1400 and 1600 Selected to give < 4% clipped values yielding a PAPR = 2.0, Constellation Quality >95 + break; + case 4: + intCarStartIndex = 2; + // dblCarScalingFactor = 0.29f; // Starting at 1200 Hz + dblCarScalingFactor = 0.4f; // Starting at 1200 Hz Selected to give < 3% clipped values yielding a PAPR = 2.26, Constellation Quality >95 + break; + case 8: + intCarStartIndex = 0; + // dblCarScalingFactor = 0.17f; // Starting at 800 Hz + if (strcmp(strMod, "16QAM") == 0) + dblCarScalingFactor = 0.27f; // Starting at 800 Hz Selected to give < 1% clipped values yielding a PAPR = 2.64, Constellation Quality >94 + else + dblCarScalingFactor = 0.25f; // Starting at 800 Hz Selected to give < 2% clipped values yielding a PAPR = 2.5, Constellation Quality >95 + } + goto PktLoopBack; // Reenter to send rest of variable length packet frame + } + ARDOPFlush(); +} + + + +// Function to compute a 16 bit CRC value and check it against the last 2 bytes of Data (the CRC) + +unsigned short int compute_crc(unsigned char *buf,int len); + +BOOL CheckCRC16(unsigned char * Data, int Length) +{ + // returns TRUE if CRC matches, else FALSE + // For CRC-16-CCITT = x^16 + x^12 +x^5 + 1 intPoly = 1021 Init FFFF + // intSeed is the seed value for the shift register and must be in the range 0-0xFFFF + + unsigned int CRC = GenCRC16(Data, Length); + unsigned short CRC2 = compute_crc(Data, Length); + CRC2 ^= 0xffff; + + // Compare the register with the last two bytes of Data (the CRC) + + if ((CRC >> 8) == Data[Length]) + if ((CRC & 0xFF) == Data[Length + 1]) + return TRUE; + + return FALSE; +} + +// Subroutine to get intDataLen bytes from outbound queue (bytDataToSend) + + + diff --git a/pktARDOP.c b/pktARDOP.c new file mode 100644 index 0000000..2f38059 --- /dev/null +++ b/pktARDOP.c @@ -0,0 +1,198 @@ +// +// Code for Packet using ARDOP like frames. +// +// This Module handles frame level stuff, and can be used +// with a KISS interface. Module pktSession inplements an +// ax.25 like Level 2, with dynamic parameter updating +// +// This uses Special Variable Length frames + +// Packet has header of 6 bytes sent in 4FSK.500.100. +// Header is 6 bits Type 10 Bits Len 2 bytes CRC 2 bytes RS +// Once we have that we receive the rest of the packet in the +// mode defined in the header. +// Uses Frame Type 0xC0, symbolic name PktFrameHeader + + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#else +#define HANDLE int +#endif + +#include "ARDOPC.h" + + +extern UCHAR KISSBUFFER[500]; // Long enough for stuffed KISS frame +extern int KISSLength; + + +VOID EncodePacket(UCHAR * Data, int Len); +VOID AddTxByteDirect(UCHAR Byte); +VOID AddTxByteStuffed(UCHAR Byte); +unsigned short int compute_crc(unsigned char *buf,int len); +void PacketStartTX(); +BOOL GetNextKISSFrame(); +VOID SendAckModeAck(); + +extern unsigned char bytEncodedBytes[4500]; // I think the biggest is 600 bd 768 + overhead +extern int EncLen; + +extern UCHAR PacketMon[360]; +extern int PacketMonMore; +extern int PacketMonLength; + +#define ARDOPBufferSize 12000 * 100 + +short ARDOPTXBuffer[4][12000 * 100]; // Enough to hold whole frame of samples + +int ARDOPTXLen[4] = { 0,0,0,0 }; // Length of frame +int ARDOPTXPtr[4] = { 0,0,0,0 }; // Tx Pointer + +int pktBandwidth = 4; +int pktMaxBandwidth = 8; +int pktMaxFrame = 4; +int pktPacLen = 80; + +int pktMode = 0; +int pktRXMode; // Currently receiving mode + +int pktDataLen; +int pktRSLen; + +// Now use Mode number to encode type and bandwidth + +const char pktMod[16][12] = { + "4PSK/200", + "4FSK/500", "4PSK/500", "8PSK/500", "16QAM/500", + "4FSK/1000", "4PSK/1000", "8PSK/1000", "16QAM/1000", + "4FSK/2000", "4PSK/2000", "8PSK/2000", "16QAM/2000", +}; + +// Note FSK modes, though identified as 200 500 or 1000 actually +// occupy 500, 1000 or 2000 BW + +const int pktBW[16] = {200, + 500, 500, 500, 500, + 1000, 1000, 1000, 1000, + 2000, 2500, 2500, 2500}; + +const int pktCarriers[16] = { + 1, + 1, 2, 2, 2, + 2, 4, 4, 4, + 4, 10, 10, 10}; + +const BOOL pktFSK[16] = {0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}; + +int pktModeLen = 13; + +VOID PktARDOPEncode(UCHAR * Data, int Len, int Chan) +{ + unsigned char DataToSend[4]; + int pktNumCar = pktCarriers[pktMode]; + + // Header now sent as 4FSK.500.100 + // 6 Bits Mode, 10 Bits Length + + // 2 Bytes Header 2 Bytes CRC 2 Bytes RS + + if (Len > 1023) + return; + + DataToSend[0] = (pktMode << 2)|(Len >> 8); + DataToSend[1] = Len & 0xff; + + // Calc Data and RS Length + + pktDataLen = (Len + (pktNumCar - 1))/pktNumCar; // Round up + + pktRSLen = pktDataLen >> 2; // Try 25% for now + + if (pktRSLen & 1) + pktRSLen++; // Odd RS bytes no use + + if (pktRSLen < 4) + pktRSLen = 4; // At least 4 + + // Encode Header + + EncLen = EncodeFSKData(PktFrameHeader, DataToSend, 2, bytEncodedBytes); + + // Encode Data + + if (pktFSK[pktMode]) + EncodeFSKData(PktFrameData, Data, Len, &bytEncodedBytes[EncLen]); + else + EncodePSKData(PktFrameData, Data, Len, &bytEncodedBytes[EncLen]); + + // Header is FSK + + Mod4FSKDataAndPlay(bytEncodedBytes, EncLen, intCalcLeader, Chan); // Modulate Data frame +} + +// Called when link idle to see if any packet frames to send + +void PktARDOPStartTX() +{ +/* +if (GetNextKISSFrame() == FALSE) + return; // nothing to send + + while (TRUE) // loop till we run out of packets + { + switch(KISSBUFFER[0]) + { + case 0: // Normal Data + + WriteDebugLog(LOGALERT, "Sending Packet Frame Len %d", KISSLength - 1); + + PktARDOPEncode(KISSBUFFER + 1, KISSLength - 1); + + // Trace it + + if (PacketMonLength == 0) // Ingore if one queued + { + PacketMon[0] = 0x80; // TX Flag + memcpy(&PacketMon[1], &KISSBUFFER[1], KISSLength); + + PacketMonLength = KISSLength; + } + + break; + + case 6: // HW Paramters. Set Mode and Bandwidth + + pktMode = KISSBUFFER[1]; + break; + + case 12: + + // Ackmode frame. Return ACK Bytes (first 2) to host when TX complete + + WriteDebugLog(LOGALERT, "Sending Packet Frame Len %d", KISSLength - 3); + PktARDOPEncode(KISSBUFFER + 3, KISSLength - 3); + + // Returns when Complete so can send ACK + + SendAckModeAck(); + break; + } + + // See if any more + + if (GetNextKISSFrame() == FALSE) + break; // no more to send + } +*/ +} + +VOID SendAckModeAck() +{ + +} + diff --git a/pulse.c b/pulse.c new file mode 100644 index 0000000..2fd35ca --- /dev/null +++ b/pulse.c @@ -0,0 +1,518 @@ +// Pulse Audio bits for QtSoundmodem + + +#include +#include +#include +#include +#include + +#define UNUSED(x) (void)(x) + +extern char CaptureNames[16][256]; +extern char PlaybackNames[16][256]; + +extern int PlaybackCount; +extern int CaptureCount; + +#include + +void *handle = NULL; +void *shandle = NULL; + +pa_mainloop * (*ppa_mainloop_new)(void); +pa_mainloop_api * (*ppa_mainloop_get_api)(pa_mainloop * m); +pa_context * (*ppa_context_new)(pa_mainloop_api *mainloop, const char *name); +int (*ppa_context_connect)(pa_context * c, const char * server, pa_context_flags_t flags, const pa_spawn_api * api); +void (*ppa_context_set_state_callback)(pa_context * c, pa_context_notify_cb_t cb, void * userdata); +int (*ppa_mainloop_iterate)(pa_mainloop * m, int block, int * retval); +void (*ppa_mainloop_free)(pa_mainloop * m); +void (*ppa_context_disconnect)(pa_context * c); +void (*ppa_context_unref)(pa_context * c); +const char * (*ppa_strerror)(int error); +pa_context_state_t(*ppa_context_get_state)(const pa_context *c); +pa_operation * (*ppa_context_get_sink_info_list)(pa_context * c, pa_sink_info_cb_t cb, void * userdata); +pa_operation * (*ppa_context_get_source_info_list)(pa_context * c, pa_source_info_cb_t cb, void * userdata); +void (*ppa_operation_unref)(pa_operation * o); +pa_operation_state_t(*ppa_operation_get_state)(const pa_operation * o); + + +pa_simple * (*ppa_simple_new)(const char * server, + const char * name, + pa_stream_direction_t dir, + const char * dev, + const char * stream_name, + const pa_sample_spec * ss, + const pa_channel_map * map, + const pa_buffer_attr * attr, + int * error) = NULL; + +pa_usec_t(*ppa_simple_get_latency)(pa_simple * s, int * error); +int(*ppa_simple_read)(pa_simple * s, void * data, size_t bytes, int * error); +int(*ppa_simple_write)(pa_simple * s, void * data, size_t bytes, int * error); + +int(*ppa_simple_flush)(pa_simple * s, int * error); +void(*ppa_simple_free)(pa_simple * s); +int(*ppa_simple_drain)(pa_simple * s, int * error); + +void * getModule(void *handle, char * sym) +{ + return dlsym(handle, sym); +} + +void * initPulse() +{ + // Load the pulse libraries + + if (handle) + return handle; // already done + + handle = dlopen("libpulse.so", RTLD_LAZY); + + if (!handle) + { + fputs(dlerror(), stderr); + return NULL; + } + + if ((ppa_mainloop_new = getModule(handle, "pa_mainloop_new")) == NULL) return NULL; + if ((ppa_mainloop_get_api = getModule(handle, "pa_mainloop_get_api")) == NULL) return NULL; + if ((ppa_context_new = getModule(handle, "pa_context_new")) == NULL) return NULL; + if ((ppa_context_connect = getModule(handle, "pa_context_connect")) == NULL) return NULL; + if ((ppa_context_set_state_callback = getModule(handle, "pa_context_set_state_callback")) == NULL) return NULL; + if ((ppa_mainloop_iterate = getModule(handle, "pa_mainloop_iterate")) == NULL) return NULL; + if ((ppa_mainloop_free = getModule(handle, "pa_mainloop_free")) == NULL) return NULL; + if ((ppa_context_disconnect = getModule(handle, "pa_context_disconnect")) == NULL) return NULL; + if ((ppa_context_unref = getModule(handle, "pa_context_unref")) == NULL) return NULL; + if ((ppa_strerror = getModule(handle, "pa_strerror")) == NULL) return NULL; + if ((ppa_context_get_state = getModule(handle, "pa_context_get_state")) == NULL) return NULL; + if ((ppa_context_get_sink_info_list = getModule(handle, "pa_context_get_sink_info_list")) == NULL) return NULL; + if ((ppa_context_get_source_info_list = getModule(handle, "pa_context_get_source_info_list")) == NULL) return NULL; + if ((ppa_operation_unref = getModule(handle, "pa_operation_unref")) == NULL) return NULL; + if ((ppa_operation_get_state = getModule(handle, "pa_operation_get_state")) == NULL) return NULL; + + shandle = dlopen("libpulse-simple.so", RTLD_LAZY); + + if (!shandle) + { + fputs(dlerror(), stderr); + return NULL; + } + + if ((ppa_simple_new = getModule(shandle, "pa_simple_new")) == NULL) return NULL; + if ((ppa_simple_get_latency = getModule(shandle, "pa_simple_get_latency")) == NULL) return NULL; + if ((ppa_simple_read = dlsym(shandle, "pa_simple_read")) == NULL) return NULL; + if ((ppa_simple_write = dlsym(shandle, "pa_simple_write")) == NULL) return NULL; + if ((ppa_simple_flush = dlsym(shandle, "pa_simple_flush")) == NULL) return NULL; + if ((ppa_simple_drain = dlsym(shandle, "pa_simple_drain")) == NULL) return NULL; + if ((ppa_simple_free = dlsym(shandle, "pa_simple_free")) == NULL) return NULL; + + return shandle; +} + + + + + +// Field list is here: http://0pointer.de/lennart/projects/pulseaudio/doxygen/structpa__sink__info.html +typedef struct pa_devicelist { + uint8_t initialized; + char name[512]; + uint32_t index; + char description[256]; +} pa_devicelist_t; + +void pa_state_cb(pa_context *c, void *userdata); +void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata); +void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata); +int pa_get_devicelist(pa_devicelist_t *input, pa_devicelist_t *output); + +// This callback gets called when our context changes state. We really only +// care about when it's ready or if it has failed +void pa_state_cb(pa_context *c, void *userdata) { + pa_context_state_t state; + int *pa_ready = userdata; + + state = ppa_context_get_state(c); + switch (state) { + // There are just here for reference + case PA_CONTEXT_UNCONNECTED: + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + default: + break; + case PA_CONTEXT_FAILED: + case PA_CONTEXT_TERMINATED: + *pa_ready = 2; + break; + case PA_CONTEXT_READY: + *pa_ready = 1; + break; + } +} + +// pa_mainloop will call this function when it's ready to tell us about a sink. +// Since we're not threading, there's no need for mutexes on the devicelist +// structure +void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) +{ + UNUSED(c); + + pa_devicelist_t *pa_devicelist = userdata; + int ctr = 0; + + // If eol is set to a positive number, you're at the end of the list + if (eol > 0) { + return; + } + + // We know we've allocated 16 slots to hold devices. Loop through our + // structure and find the first one that's "uninitialized." Copy the + // contents into it and we're done. If we receive more than 16 devices, + // they're going to get dropped. You could make this dynamically allocate + // space for the device list, but this is a simple example. + for (ctr = 0; ctr < 16; ctr++) { + if (!pa_devicelist[ctr].initialized) { + strncpy(pa_devicelist[ctr].name, l->name, 511); + strncpy(pa_devicelist[ctr].description, l->description, 255); + pa_devicelist[ctr].index = l->index; + pa_devicelist[ctr].initialized = 1; + break; + } + } +} + +// See above. This callback is pretty much identical to the previous +void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) +{ + UNUSED(c); + + pa_devicelist_t *pa_devicelist = userdata; + int ctr = 0; + + if (eol > 0) { + return; + } + + for (ctr = 0; ctr < 16; ctr++) { + if (!pa_devicelist[ctr].initialized) { + strncpy(pa_devicelist[ctr].name, l->name, 511); + strncpy(pa_devicelist[ctr].description, l->description, 255); + pa_devicelist[ctr].index = l->index; + pa_devicelist[ctr].initialized = 1; + break; + } + } +} + +int pa_get_devicelist(pa_devicelist_t *input, pa_devicelist_t *output) { + // Define our pulse audio loop and connection variables + pa_mainloop *pa_ml; + pa_mainloop_api *pa_mlapi; + pa_operation *pa_op; + pa_context *pa_ctx; + + + // We'll need these state variables to keep track of our requests + int state = 0; + int pa_ready = 0; + + // Initialize our device lists + memset(input, 0, sizeof(pa_devicelist_t) * 16); + memset(output, 0, sizeof(pa_devicelist_t) * 16); + + // Create a mainloop API and connection to the default server + pa_ml = ppa_mainloop_new(); + pa_mlapi = ppa_mainloop_get_api(pa_ml); + pa_ctx = ppa_context_new(pa_mlapi, "test"); + + // This function connects to the pulse server + ppa_context_connect(pa_ctx, NULL, 0, NULL); + + + // This function defines a callback so the server will tell us it's state. + // Our callback will wait for the state to be ready. The callback will + // modify the variable to 1 so we know when we have a connection and it's + // ready. + // If there's an error, the callback will set pa_ready to 2 + ppa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready); + + // Now we'll enter into an infinite loop until we get the data we receive + // or if there's an error + for (;;) { + // We can't do anything until PA is ready, so just iterate the mainloop + // and continue + if (pa_ready == 0) { + ppa_mainloop_iterate(pa_ml, 1, NULL); + continue; + } + // We couldn't get a connection to the server, so exit out + if (pa_ready == 2) { + ppa_context_disconnect(pa_ctx); + ppa_context_unref(pa_ctx); + ppa_mainloop_free(pa_ml); + return -1; + } + // At this point, we're connected to the server and ready to make + // requests + switch (state) { + // State 0: we haven't done anything yet + case 0: + // This sends an operation to the server. pa_sinklist_info is + // our callback function and a pointer to our devicelist will + // be passed to the callback The operation ID is stored in the + // pa_op variable + pa_op = ppa_context_get_sink_info_list(pa_ctx, + pa_sinklist_cb, + output + ); + + // Update state for next iteration through the loop + state++; + break; + case 1: + // Now we wait for our operation to complete. When it's + // complete our pa_output_devicelist is filled out, and we move + // along to the next state + if (ppa_operation_get_state(pa_op) == PA_OPERATION_DONE) { + ppa_operation_unref(pa_op); + + // Now we perform another operation to get the source + // (input device) list just like before. This time we pass + // a pointer to our input structure + pa_op = ppa_context_get_source_info_list(pa_ctx, + pa_sourcelist_cb, + input + ); + // Update the state so we know what to do next + state++; + } + break; + case 2: + if (ppa_operation_get_state(pa_op) == PA_OPERATION_DONE) { + // Now we're done, clean up and disconnect and return + ppa_operation_unref(pa_op); + ppa_context_disconnect(pa_ctx); + ppa_context_unref(pa_ctx); + ppa_mainloop_free(pa_ml); + return 0; + } + break; + default: + // We should never see this state + fprintf(stderr, "in state %d\n", state); + return -1; + } + // Iterate the main loop and go again. The second argument is whether + // or not the iteration should block until something is ready to be + // done. Set it to zero for non-blocking. + ppa_mainloop_iterate(pa_ml, 1, NULL); + } +} + +int listpulse() +{ + int ctr; + + PlaybackCount = 0; + CaptureCount = 0; + + + // This is where we'll store the input device list + pa_devicelist_t pa_input_devicelist[16]; + + // This is where we'll store the output device list + pa_devicelist_t pa_output_devicelist[16]; + + if (pa_get_devicelist(pa_input_devicelist, pa_output_devicelist) < 0) { + fprintf(stderr, "failed to get device list\n"); + return 1; + } + + printf("Pulse Playback Devices\n\n"); + + for (ctr = 0; ctr < 16; ctr++) + { + if (!pa_output_devicelist[ctr].initialized) + break; + + printf("Name: %s\n", pa_output_devicelist[ctr].name); + strcpy(&PlaybackNames[PlaybackCount++][0], pa_output_devicelist[ctr].name); + + } + + printf("Pulse Capture Devices\n\n"); + + for (ctr = 0; ctr < 16; ctr++) + { + if (!pa_input_devicelist[ctr].initialized) + break; + + printf("Name: %s\n", pa_input_devicelist[ctr].name); + strcpy(&CaptureNames[CaptureCount++][0], pa_input_devicelist[ctr].name); + } + return 0; +} + + +pa_simple * OpenPulsePlayback(char * Server) +{ + pa_simple * s; + pa_sample_spec ss; + ss.format = PA_SAMPLE_S16NE; + ss.channels = 2; + ss.rate = 12000; + int error; + + + s = (*ppa_simple_new)(NULL, // Use the default server. + "QtSM", // Our application's name. + PA_STREAM_PLAYBACK, + Server, + + "Playback", // Description of our stream. + &ss, // Our sample format. + NULL, // Use default channel map + NULL, // Use default buffering attributes. + &error + ); + + if (s == 0) + printf("Playback pa_simple_new() failed: %s\n", ppa_strerror(error)); + else + printf("Playback Handle %x\n", (unsigned int)s); + + return s; +} + +pa_simple * OpenPulseCapture(char * Server) +{ + pa_simple * s; + pa_sample_spec ss; + ss.format = PA_SAMPLE_S16NE; + ss.channels = 2; + ss.rate = 12000; + int error; + + pa_buffer_attr attr; + + attr.maxlength = -1; + attr.tlength = -1; + attr.prebuf = -1; + attr.minreq = -1; + attr.fragsize = 512; + + + s = (*ppa_simple_new)(NULL, // Use the default server. + "QtSM", // Our application's name. + PA_STREAM_RECORD, + Server, + "Capture", // Description of our stream. + &ss, // Our sample format. + NULL, // Use default channel map + &attr, + &error + ); + + if (s == 0) + printf("Capture pa_simple_new() failed: %s\n", ppa_strerror(error)); + else + printf("Capture Handle %x\n", (unsigned int)s); + + return s; +} + +pa_simple * spc = 0; // Capure Handle +pa_simple * spp = 0; // Playback Handle + +int pulse_audio_open(char * CaptureDevice, char * PlaybackDevice) +{ + pa_usec_t latency; + int error; + + spc = OpenPulseCapture(CaptureDevice); + spp = OpenPulsePlayback(PlaybackDevice); + + if (spc && spp) + { + if ((latency = ppa_simple_get_latency(spc, &error)) == (pa_usec_t)-1) { + printf("cap simple_get_latency() failed: %s\n", ppa_strerror(error)); + } + else + printf("cap %0.0f usec \n", (float)latency); + + if ((latency = ppa_simple_get_latency(spp, &error)) == (pa_usec_t)-1) { + printf("play simple_get_latency() failed: %s\n", ppa_strerror(error)); + } + else + printf("play %0.0f usec \n", (float)latency); + + return 1; + } + else + return 0; + +} + +void pulse_audio_close() +{ + int error; + + ppa_simple_flush(spc, &error); + ppa_simple_free(spc); + spc = 0; + + ppa_simple_drain(spp, &error); + ppa_simple_free(spp); + spp = 0; +} + + +int pulse_read(short * samples, int nSamples) +{ + int error; + int nBytes = nSamples * 4; + + if (spc == 0) + return 0; + + if (ppa_simple_read(spc, samples, nBytes, &error) < 0) + { + printf("Pulse pa_simple_read() failed: %s\n", ppa_strerror(error)); + return 0; + } + + return nSamples; +} + + +int pulse_write(short * ptr, int len) +{ + int k; + int error; + + if (spp == 0) + return 0; + + k = ppa_simple_write(spp, ptr, len * 4, &error); + + if (k < 0) + { + printf("Pulse pa_simple_write() failed: %s\n", ppa_strerror(error)); + return -1; + } + + return 0; +} + +void pulse_flush() +{ + int error; + + if (spp == 0) + return; + + if (ppa_simple_flush(spp, &error) < 0) + printf("Pulse pa_simple_flush() failed: %s\n", ppa_strerror(error)); +} diff --git a/resource1.h b/resource1.h new file mode 100644 index 0000000..8ad984b --- /dev/null +++ b/resource1.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Resource.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/rs.c b/rs.c new file mode 100644 index 0000000..5f0735a --- /dev/null +++ b/rs.c @@ -0,0 +1,214 @@ +/* + * Reed Solomon Encoder/Decoder + * + * Copyright Henry Minsky (hqm@alum.mit.edu) 1991-2009 + * + * This software library is licensed under terms of the GNU GENERAL + * PUBLIC LICENSE + * + * RSCODE 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. + * + * RSCODE 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 Rscode. If not, see . + + * Commercial licensing is available under a separate license, please + * contact author for details. + * + * Source code is available at http://rscode.sourceforge.net + */ + +#define LOGEMERGENCY 0 +#define LOGALERT 1 +#define LOGCRIT 2 +#define LOGERROR 3 +#define LOGWARNING 4 +#define LOGNOTICE 5 +#define LOGINFO 6 +#define LOGDEBUG 7 + + +#include +#include +#include +#include "ecc.h" + +void Debugprintf(const char * format, ...); + +/* Encoder parity bytes */ +int pBytes[MAXDEG]; + +/* Decoder syndrome bytes */ +int synBytes[MAXDEG]; + +/* generator polynomial */ +int genPoly[MAXDEG*2]; + +int DEBUG = FALSE; //RUE; + +static void +compute_genpoly (int nbytes, int genpoly[]); + +/* Initialize lookup tables, polynomials, etc. */ +void +initialize_ecc () +{ + /* Initialize the galois field arithmetic tables */ + init_galois_tables(); + + /* Compute the encoder generator polynomial */ + compute_genpoly(NPAR, genPoly); +} + +void +zero_fill_from (unsigned char buf[], int from, int to) +{ + int i; + for (i = from; i < to; i++) buf[i] = 0; +} + +/* debugging routines */ +void +print_parity (void) +{ + int i; + Debugprintf("Parity Bytes: "); + for (i = 0; i < NPAR; i++) + Debugprintf("[%d]:%x, ",i,pBytes[i]); + Debugprintf("\n"); +} + + +void +print_syndrome (void) +{ + int i; + Debugprintf("Syndrome Bytes: "); + for (i = 0; i < NPAR; i++) + Debugprintf("[%d]:%x, ",i,synBytes[i]); + Debugprintf("\n"); +} + + +/********************************************************** + * Reed Solomon Decoder + * + * Computes the syndrome of a codeword. Puts the results + * into the synBytes[] array. + */ + +void +decode_data(unsigned char data[], int nbytes) +{ + int i, j, sum; + for (j = 0; j < NPAR; j++) + { + sum = 0; + for (i = 0; i < nbytes; i++) + { + sum = data[i] ^ gmult(gexp[j+1], sum); + } + + synBytes[j] = sum; + +// Debugprintf("%d %d %d\r\n", i, synBytes[i], index_of[s[i]]); + + } +} + + +/* Check if the syndrome is zero */ +int +check_syndrome (void) +{ + int i, nz = 0; + for (i =0 ; i < NPAR; i++) { + if (synBytes[i] != 0) { + nz = 1; + break; + } + } + return nz; +} + + +void +debug_check_syndrome (void) +{ + int i; + + for (i = 0; i < 3; i++) { + Debugprintf(" inv log S[%d]/S[%d] = %d\n", i, i+1, + glog[gmult(synBytes[i], ginv(synBytes[i+1]))]); + } +} + + +/* Create a generator polynomial for an n byte RS code. + * The coefficients are returned in the genPoly arg. + * Make sure that the genPoly array which is passed in is + * at least n+1 bytes long. + */ + +static void +compute_genpoly (int nbytes, int genpoly[]) +{ + int i, tp[256], tp1[256]; + + /* multiply (x + a^n) for n = 1 to nbytes */ + + zero_poly(tp1); + tp1[0] = 1; + + for (i = 1; i <= nbytes; i++) { + zero_poly(tp); + tp[0] = gexp[i]; /* set up x+a^n */ + tp[1] = 1; + + mult_polys(genpoly, tp, tp1); + copy_poly(tp1, genpoly); + } +} + +/* Simulate a LFSR with generator polynomial for n byte RS code. + * Pass in a pointer to the data array, and amount of data. + * + * The parity bytes are deposited into pBytes[], and the whole message + * and parity are copied to dest to make a codeword. + * + */ + +void +encode_data (unsigned char msg[], int nbytes, unsigned char dst[]) +{ + int i ,dbyte, j; + unsigned char LFSR[MAXNPAR+1]; + + for(i=0; i < NPAR+1; i++) + LFSR[i]=0; + + // for (i = 0; i < nbytes; i++) + for (i = nbytes-1; i >= 0; i--) // Order reversed for compatibility wiyh Rick' Code + { + dbyte = msg[i] ^ LFSR[NPAR-1]; + for (j = NPAR-1; j > 0; j--) + { + LFSR[j] = LFSR[j-1] ^ gmult(genPoly[j], dbyte); + } + LFSR[0] = gmult(genPoly[0], dbyte); + } + + // return the parity bytes + + memcpy(dst, LFSR, NPAR); +} + + + diff --git a/rsid.c b/rsid.c new file mode 100644 index 0000000..63b09c0 --- /dev/null +++ b/rsid.c @@ -0,0 +1,733 @@ +// +// Derived from fldigi rdid.cxx by John G8BPQ September 22 +// +// ---------------------------------------------------------------------------- +// +// rsid.cxx +// +// Copyright (C) 2008-2012 +// Dave Freese, W1HKJ +// Copyright (C) 2009-2012 +// Stelios Bounanos, M0GLD +// Copyright (C) 2012 +// John Douyere, VK2ETA +// +// This file is part of fldigi. +// +// Fldigi 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. +// +// Fldigi 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 fldigi. If not, see . +// ---------------------------------------------------------------------------- + + +#include + +#include "rsid.h" +#include +#include "fftw3.h" + +#include "UZ7HOStuff.h" + +#define M_PI 3.1415926f + +#define true 1 +#define false 0 + +#define TRUE 1 +#define FALSE 0 + +#define WORD unsigned int +#define BYTE unsigned char +#define byte unsigned char + +void SampleSink(int LR, short Sample); +void Flush(); +void extSetOffset(int rxOffset); +void mon_rsid(int snd_ch, char * RSID); + +extern int RSID_SABM[4]; +extern int RSID_UI[4]; +extern int RSID_SetModem[4]; + +struct RSIDs { + unsigned short rs; + trx_mode mode; + const char* name; +}; + +extern int SampleNo; +extern int Number; + +int len; +int symlen; + +#include "rsid_defs.cxx" + +#define RSWINDOW 1 + + +const int Squares[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 2, 4, 6, 8,10,12,14, 9,11,13,15, 1, 3, 5, 7, + 0, 3, 6, 5,12,15,10, 9, 1, 2, 7, 4,13,14,11, 8, + 0, 4, 8,12, 9,13, 1, 5,11,15, 3, 7, 2, 6,10,14, + 0, 5,10,15,13, 8, 7, 2, 3, 6, 9,12,14,11, 4, 1, + 0, 6,12,10, 1, 7,13,11, 2, 4,14, 8, 3, 5,15, 9, + 0, 7,14, 9, 5, 2,11,12,10,13, 4, 3,15, 8, 1, 6, + 0, 8, 9, 1,11, 3, 2,10,15, 7, 6,14, 4,12,13, 5, + 0, 9,11, 2,15, 6, 4,13, 7,14,12, 5, 8, 1, 3,10, + 0,10,13, 7, 3, 9,14, 4, 6,12,11, 1, 5,15, 8, 2, + 0,11,15, 4, 7,12, 8, 3,14, 5, 1,10, 9, 2, 6,13, + 0,12, 1,13, 2,14, 3,15, 4, 8, 5, 9, 6,10, 7,11, + 0,13, 3,14, 6,11, 5, 8,12, 1,15, 2,10, 7, 9, 4, + 0,14, 5,11,10, 4,15, 1,13, 3, 8, 6, 7, 9, 2,12, + 0,15, 7, 8,14, 1, 9, 6, 5,10, 2,13,11, 4,12, 3 +}; + +const int indices[] = { + 2, 4, 8, 9, 11, 15, 7, 14, 5, 10, 13, 3 +}; + +int rmode, rmode2; + +void Encode(int code, unsigned char *rsid) +{ + rsid[0] = code >> 8; + rsid[1] = (code >> 4) & 0x0f; + rsid[2] = code & 0x0f; + for (int i = 3; i < RSID_NSYMBOLS; i++) + rsid[i] = 0; + for (int i = 0; i < 12; i++) { + for (int j = RSID_NSYMBOLS - 1; j > 0; j--) + rsid[j] = rsid[j - 1] ^ Squares[(rsid[j] << 4) + indices[i]]; + rsid[0] = Squares[(rsid[0] << 4) + indices[i]]; + } +} + +//============================================================================= +// transmit rsid code for current mode +//============================================================================= + +float sampd; +short samps; + +// Each symbol is transmitted using MFSK modulation.There are 16 possibilities of frequencies separated by +// 11025 / 1024 = 10.766 Hz. Each symbol transmission being done on only one frequency for a duration equal +// to 1024 / 11025 x 1000 = 92.88 ms.The entire RSID sequence of 15 symbols is transmitted in 15 x 1024 / 11025 = 1393 ms. + +// The analysis is based on a Fast Fourier transform of 2048 points at 11025 samples / sec, regularly done at each +// semi - step of time(46.44 ms). + +// For each semi - step of time(46.44 ms) and for each semi - step of frequency(5.38 Hz),the program attempts to detect +// an RSID extending for the last 1.393 seconds.So each second, about 8500 possible RSID +// (depending on the selected bandwidth) are tested + +// But we are working at 12000 samples/sec so 92.88 mS = 1114.56 samples, so I think we run fft every 557.28 samples (46.44 ms) + +// How do we get 5.28 Hz buckets at 12000? Can we run fft of length 2,272.727 (say 2273) length? + +// Actually not sure we need to. We can interpolate freq and so long as we can get within a few Hz should be ok + + +// Spec says tone spacing ia 10.766 (11025 / 1024) + +double toneinterval = RSID_SAMPLE_RATE / 1024; + +// fftw library interface + +static fftwf_complex * in = 0, *out; +static fftwf_plan p; + +#define N 4096 + +short savedSamples[N + 1000]; // At least N + max input length (currently uses 512); +int savedSampLen = 0; + +int firstBin = (300 * 2048) / 12000; // Search Lowest (300 Hz) +int lastBin = (3000 * 2048) / 12000;; // Seach Highest (3000 Hz) + +double avmag; // Average magnitude over spectrum + +int fft_buckets[RSID_NTIMES][RSID_FFT_SIZE]; // This seems to have last 30 sets of values + +float aFFTAmpl[RSID_FFT_SIZE]; // Amplitude samples from fft + +// Table of precalculated Reed Solomon symbols +unsigned char pCodes1[256][16]; +unsigned char pCodes2[256][16]; + +int found1; +int found2; + +double rsid_secondary_time_out; + + +int bPrevTimeSliceValid; +int iPrevDistance; +int iPrevBin; +int iPrevSymbol; + +int fft_buckets[RSID_NTIMES][RSID_FFT_SIZE]; + +int bPrevTimeSliceValid2; +int iPrevDistance2; +int iPrevBin2; +int iPrevSymbol2; + +int hamming_resolution = 2; + +int needRSID[4] = { 0,0,0,0 }; // Request TX scheduler to send RSID + +int needSetOffset[4] = { 0,0,0,0 }; + +void CalculateBuckets(const float *pSpectrum, int iBegin, int iEnd); +void Search(); + +void RSIDinitfft() +{ + unsigned char * c; + + in = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * N); + out = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * N); + p = fftwf_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_MEASURE); + + // Initialization of assigned mode/submode IDs. + + for (int i = 0; i < rsid_ids_size1; i++) + { + c = &pCodes1[i][0]; + Encode(rsid_ids_1[i].rs, c); + } +} + +void reset() +{ + iPrevDistance = iPrevDistance2 = 99; + bPrevTimeSliceValid = bPrevTimeSliceValid2 = false; + found1 = found2 = false; + rsid_secondary_time_out = 0; +} + +// Compute fft of last 557 * 2 values and return bucket number of peak + +static int dofft(short * inp, float * mags) +{ + int i; + float mag; + float max = 0; + int maxindex = 0; + + memset(in, 0, sizeof(fftwf_complex) * N); + avmag = 0; + + + for (i = 0; i < 512 * 2; i++) + { + // in[i][0] = inp[i] * 1.0f; + + // Hamming Window + + in[i][0] = inp[i]; + in[i][0] = (float)((0.53836f - (0.46164f * cos(2 * M_PI * i / (float)(557.0 * 2.0 - 1)))) * inp[i]); + in[i][1] = 0; + } + + fftwf_execute(p); + + for (i = firstBin; i < lastBin; i++) // only need buckets up to 3000 + { + // Convert Real/Imag to amplitude + + mag = powf(out[i][0], 2); + mag += powf(out[i][1], 2); + mag = sqrtf(mag); + mags[i] = mag; + avmag += mag; + + if (mag > max) + { + max = mag; + maxindex = i; + } + } + avmag /= (lastBin - firstBin); + return maxindex; +} + +void RSIDProcessSamples(short * Samples, int nSamples) +{ + // Add to saved samples, and if we have more than 557 run FFT and save remaining + + // We process last 557 + new 557 + zero padding + + // Trying with 512 @ 12000 + + // savedSampLen is number of shorts not bytes + + if (in == 0) // Not initialised + return; + + memcpy(&savedSamples[savedSampLen], Samples, nSamples * sizeof(short)); + savedSampLen += nSamples; + + if (savedSampLen >= 512 * 2) // Old + New + { + int peakBucket; + + peakBucket = dofft(savedSamples, aFFTAmpl); + + if (peakBucket > firstBin && peakBucket < lastBin) + { +// float freq; +// freq = peakBucket * 12000.0f / 2048; +// Debugprintf("%d %f %f %f", peakBucket, freq, aFFTAmpl[peakBucket], avmag); + } + + savedSampLen -= 512; + memmove(savedSamples, &savedSamples[512 * sizeof(short)], savedSampLen * sizeof(short)); + + Search(); + } +} + + +int HammingDistance(int iBucket, unsigned char *p2) +{ + int dist = 0; + + for (int i = 0, j = 0; i < RSID_NSYMBOLS; i++, j += 2) + { + if (fft_buckets[j][iBucket] != p2[i]) + { + ++dist; + if (dist > hamming_resolution) + break; + } + } + return dist; +} + +int iDistanceMin = 1000; // infinity + +int search_amp(int *bin_out, int *symbol_out) +{ + int i, j; + int iDistance = 1000; + int iBin = -1; + int iSymbol = -1; + + int tblsize; + + iDistanceMin = 1000; // infinity + + tblsize = rsid_ids_size1; + + unsigned char *pc = 0; + + for (i = 0; i < tblsize; i++) + { + pc = &pCodes1[i][0]; + + for (j = firstBin; j < lastBin - RSID_NTIMES; j++) + { + iDistance = HammingDistance(j, pc); + + if (iDistance < iDistanceMin) + { + iDistanceMin = iDistance; + iSymbol = i; + iBin = j; + if (iDistanceMin == 0) break; + } + } + } + + if (iDistanceMin <= hamming_resolution) + { + *symbol_out = iSymbol; + *bin_out = iBin; + return true; + } + + return false; +} + +void apply(int iBin, int iSymbol) +{ + // Does something with the found id + + const struct RSIDs *prsid = &rsid_ids_1[iSymbol]; + int Freq = (int)(iBin + 15) * 12000.0f / 2048; + char Msg[128]; + int Offset = Freq - rx_freq[0]; + int i; + int nearest = -1, minOffset = 9999, absOffset; + + // If there is more than one decoder with update from rsid set update the nearest + + for (i = 0; i < 4; i++) + { + if (RSID_SetModem[i]) + { + absOffset = abs(Freq - rx_freq[i]); + + if (absOffset < minOffset) + { + // Nearer + + nearest = i; + minOffset = absOffset; + } + } + } + + // We don't run this unless at least one modem has RSID_SetModem set. + + Offset = Freq - rx_freq[nearest]; + + sprintf(Msg, "RSID %s %d %d Nearest Modem %c Offset %d", prsid->name, iDistanceMin, Freq, nearest + 'A', Offset); + + mon_rsid(0, Msg); + + // Set Modem RX Offset to match received freq + + chanOffset[nearest] = Offset; + needSetOffset[nearest] = 1; // Update GUI + +} + + +void Search() +{ + int symbol = -1; + int bin = -1; + + // We have just calculated a new set of fft amplitude bins in aFFTAmpl + + // we find peak bin, and store in fft_buckets array. This has 30 sets of 1024 buckets (though we only use first 512, as we limit search to 3 KHz) + + // We move previous 29 entries to start of array and add new values on end so samples corresponding to first bit of rsid msg are at start + + memmove(fft_buckets, &(fft_buckets[1][0]), (RSID_NTIMES - 1) * RSID_FFT_SIZE * sizeof(int)); + memset(&(fft_buckets[RSID_NTIMES - 1][0]), 0, RSID_FFT_SIZE * sizeof(int)); + + // We process even then odd bins, using alternate bins to get resolution to 1/2 bin + + CalculateBuckets(aFFTAmpl, firstBin, lastBin - RSID_NTIMES); + CalculateBuckets(aFFTAmpl, firstBin + 1, lastBin - RSID_NTIMES); + + // Now have 30 sets of 512 bit valies (0-15). We look for a set of 15 that match an ID + + found1 = search_amp(&bin, &symbol); + + if (found1) + { + apply(bin, symbol); + reset(); + } +} + + + +void CalculateBuckets(const float *pSpectrum, int iBegin, int iEnd) +{ + float Amp = 0.0f, AmpMax = 0.0f; + + int iBucketMax = iBegin - 2; + int j; + + // This searches odd and even pairs of amps, hence the += 2 + + for (int i = iBegin; i < iEnd; i += 2) + { + if (iBucketMax == i - 2) + { + // if max was first in grooup of 15 redo full search + + AmpMax = pSpectrum[i]; + iBucketMax = i; + for (j = i + 2; j < i + RSID_NTIMES + 2; j += 2) + { + Amp = pSpectrum[j]; + if (Amp > AmpMax) + { + AmpMax = Amp; + iBucketMax = j; + } + } + } + else + { + // Max wasn't first, so must be in next 14, so we can just check if new last is > max + + j = i + RSID_NTIMES; + Amp = pSpectrum[j]; + if (Amp > AmpMax) + { + AmpMax = Amp; + iBucketMax = j; + } + } + fft_buckets[RSID_NTIMES - 1][i] = (iBucketMax - i) >> 1; + } +} + + + + + + + + +void sendRSID(int Chan, int dropTX) +{ + unsigned char rsid[RSID_NSYMBOLS]; + float sr; + int iTone; + float freq, phaseincr; + float fr; + float phase; + + int Mode = speed[Chan]; + int Freq = rx_freq[Chan]; + + rmode2 = 687; + rmode = 35 + Mode; // Packet 300 or 1200 + + Encode(rmode, rsid); + + sr = 12000; + symlen = (size_t)floor(RSID_SYMLEN * sr); + + SampleNo = 0; + + SoundIsPlaying = TRUE; + RadioPTT(Chan, 1); + Number = 0; + + // transmit 5 symbol periods of silence at beginning of rsid + + for (int i = 0; i < 5 * symlen; i++) + SampleSink(0, 0); + + // transmit sequence of 15 symbols (tones) + + fr = 1.0f * Freq - (RSID_SAMPLE_RATE * 7 / 1024); + phase = 0.0f; + + for (int i = 0; i < 15; i++) + { + iTone = rsid[i]; + freq = fr + iTone * RSID_SAMPLE_RATE / 1024; + phaseincr = 2.0f * M_PI * freq / sr; + + for (int j = 0; j < symlen; j++) + { + phase += phaseincr; + if (phase > 2.0 * M_PI) phase -= 2.0 * M_PI; + + sampd = sinf(phase); + sampd = sampd * 32767.0f; + samps = (short)sampd; + SampleSink(0, samps); + } + } + + // 5 symbol periods of silence at end of transmission + // and between RsID and the data signal + int nperiods = 5; + + for (int i = 0; i < nperiods * symlen; i++) + SampleSink(modemtoSoundLR[Chan], 0); + + tx_status[Chan] = TX_SILENCE; // Stop TX + Flush(); + if (dropTX) + RadioPTT(Chan, 0); +} + +// Experimental Busy Detect, based on ARDOP code + + +static int LastBusyCheck = 0; +static int BusyCount = 0; +static int BusyStatus = 0; +static int LastBusyStatus = 0; + +static int BusyDet = 5; // DCD Threshold +int LastStart = 0; +int LastStop = 0; +extern int LastBusyOn; +extern int LastBusyOff; + +static int LastBusy = FALSE; + +extern float dblAvgStoNSlowNarrow; +extern float dblAvgStoNFastNarrow; +extern float dblAvgStoNSlowWide; +extern float dblAvgStoNFastWide; +int BusyOnCnt = 0; // used to filter Busy ON detections +int BusyOffCnt = 0; // used to filter Busy OFF detections +unsigned int LastBusyTrip = 0; +unsigned int PriorLastBusyTrip = 0; +unsigned int LastBusyClear = 0; +unsigned int LastTrip; + +void SortSignals(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin); +void SortSignals2(float * dblMag, int intStartBin, int intStopBin, int intNumBins, float * dblAVGSignalPerBin, float * dblAVGBaselinePerBin); + +static 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/2048 or 5.86 Hz + // First sort signals and look at highes signals:baseline ratio.. + + float dblAVGSignalPerBinNarrow, dblAVGSignalPerBinWide, dblAVGBaselineNarrow, dblAVGBaselineWide; + float dblSlowAlpha = 0.2f; + float dblAvgStoNNarrow = 0, dblAvgStoNWide = 0; + int intNarrow = 16; // 16 x 5.86 about 94 z + int intWide = ((intStop - intStart) * 2) / 3; //* 0.66); + int blnBusy = FALSE; + int BusyDet4th = BusyDet * BusyDet * BusyDet * BusyDet; + int BusyDet = 5; + unsigned int HoldMs = 5000; + + // First sort signals and look at highest signals:baseline ratio.. + // First narrow band (~94Hz) + + SortSignals2(dblMag, intStart, intStop, intNarrow, &dblAVGSignalPerBinNarrow, &dblAVGBaselineNarrow); + + if (LastStart == intStart && LastStop == intStop) + dblAvgStoNNarrow = (1 - dblSlowAlpha) * dblAvgStoNNarrow + dblSlowAlpha * dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; + else + { + // This initializes the Narrow average after a bandwidth change + + dblAvgStoNNarrow = dblAVGSignalPerBinNarrow / dblAVGBaselineNarrow; + LastStart = intStart; + LastStop = intStop; + } + + // Wide band (66% of current bandwidth) + + SortSignals2(dblMag, intStart, intStop, intWide, &dblAVGSignalPerBinWide, &dblAVGBaselineWide); + + if (LastStart == intStart && LastStop == intStop) + dblAvgStoNWide = (1 - dblSlowAlpha) * dblAvgStoNWide + dblSlowAlpha * dblAVGSignalPerBinWide / dblAVGBaselineWide; + else + { + // This initializes the Wide average after a bandwidth change + + dblAvgStoNWide = dblAVGSignalPerBinWide / dblAVGBaselineWide; + LastStart = intStart; + LastStop = intStop; + } + + // Preliminary calibration...future a function of bandwidth and BusyDet. + + blnBusy = (dblAvgStoNNarrow > (3 + 0.008 * BusyDet4th)) || (dblAvgStoNWide > (5 + 0.016 * BusyDet4th)); + + if (BusyDet == 0) + blnBusy = FALSE; // 0 Disables check ?? Is this the best place to do this? + +// WriteDebugLog(LOGDEBUG, "Busy %d Wide %f Narrow %f", blnBusy, dblAvgStoNWide, dblAvgStoNNarrow); + + if (blnBusy) + { + // This requires multiple adjacent busy conditions to skip over one nuisance Busy trips. + // Busy must be present at least 3 consecutive times ( ~250 ms) to be reported + + BusyOnCnt += 1; + BusyOffCnt = 0; + if (BusyOnCnt > 3) + LastTrip = Now; + } + else + { + BusyOffCnt += 1; + BusyOnCnt = 0; + } + + if (LastBusy == 0 && BusyOnCnt >= 3) + { + PriorLastBusyTrip = LastBusyTrip; // save old dttLastBusyTrip for use in BUSYBLOCKING function + LastBusyTrip = Now; + LastBusy = TRUE; + } + else + { + if (LastBusy && (Now - LastTrip) > HoldMs && BusyOffCnt >= 3) + { + LastBusyClear = Now; + LastBusy = FALSE; + } + } + return LastBusy; +} + + +static void UpdateBusyDetector() +{ + // Use applitude bins in aFFTAmpl + + float dblMagAvg = 0; + int intTuneLineLow, intTuneLineHi; + int i; + int BusyFlag; + + if (Now - LastBusyCheck < 100) + return; + + LastBusyCheck = Now; + + for (i = 52; i < 512; i++) + { + // starting at ~300 Hz to ~3000 Hz Which puts the center of the signal in the center of the window (~1500Hz) + + dblMagAvg += aFFTAmpl[i]; + } + + intTuneLineLow = 52; + intTuneLineHi = 512; + + BusyFlag = BusyDetect3(aFFTAmpl, intTuneLineLow, intTuneLineHi); + + if (BusyFlag == 0) + { + if (BusyCount == 0) + BusyStatus = 0; + else + BusyCount--; + } + else + { + BusyStatus = 1; + BusyCount = 10; // Try delaying busy off a bit + } + + if (BusyStatus && !LastBusyStatus) + { + Debugprintf("BUSY TRUE"); + } + // stcStatus.Text = "True" + // queTNCStatus.Enqueue(stcStatus) + // 'Debug.WriteLine("BUSY TRUE @ " & Format(DateTime.UtcNow, "HH:mm:ss")) + + else if (LastBusyStatus && !BusyStatus) + { + Debugprintf("BUSY FALSE"); + } + + LastBusyStatus = BusyStatus; + +} + diff --git a/rsid.cxx b/rsid.cxx new file mode 100644 index 0000000..1346893 --- /dev/null +++ b/rsid.cxx @@ -0,0 +1,1096 @@ +// ---------------------------------------------------------------------------- +// +// rsid.cxx +// +// Copyright (C) 2008-2012 +// Dave Freese, W1HKJ +// Copyright (C) 2009-2012 +// Stelios Bounanos, M0GLD +// Copyright (C) 2012 +// John Douyere, VK2ETA +// +// This file is part of fldigi. +// +// Fldigi 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. +// +// Fldigi 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 fldigi. If not, see . +// ---------------------------------------------------------------------------- + +#include + +#include +#include +#include +#include +#include + +#include "rsid.h" +#include "filters.h" +/* +#include "misc.h" +#include "trx.h" +#include "fl_digi.h" +#include "configuration.h" +#include "confdialog.h" +#include "qrunner.h" +#include "notify.h" +#include "debug.h" + +#include "main.h" +#include "arq_io.h" +#include "data_io.h" +#include "audio_alert.h" +*/ + + +struct RSIDs { unsigned short rs; trx_mode mode; const char* name; }; + +#include "rsid_defs.cxx" + +#define RSWINDOW 1 + +const int cRsId::Squares[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, + 0, 2, 4, 6, 8,10,12,14, 9,11,13,15, 1, 3, 5, 7, + 0, 3, 6, 5,12,15,10, 9, 1, 2, 7, 4,13,14,11, 8, + 0, 4, 8,12, 9,13, 1, 5,11,15, 3, 7, 2, 6,10,14, + 0, 5,10,15,13, 8, 7, 2, 3, 6, 9,12,14,11, 4, 1, + 0, 6,12,10, 1, 7,13,11, 2, 4,14, 8, 3, 5,15, 9, + 0, 7,14, 9, 5, 2,11,12,10,13, 4, 3,15, 8, 1, 6, + 0, 8, 9, 1,11, 3, 2,10,15, 7, 6,14, 4,12,13, 5, + 0, 9,11, 2,15, 6, 4,13, 7,14,12, 5, 8, 1, 3,10, + 0,10,13, 7, 3, 9,14, 4, 6,12,11, 1, 5,15, 8, 2, + 0,11,15, 4, 7,12, 8, 3,14, 5, 1,10, 9, 2, 6,13, + 0,12, 1,13, 2,14, 3,15, 4, 8, 5, 9, 6,10, 7,11, + 0,13, 3,14, 6,11, 5, 8,12, 1,15, 2,10, 7, 9, 4, + 0,14, 5,11,10, 4,15, 1,13, 3, 8, 6, 7, 9, 2,12, + 0,15, 7, 8,14, 1, 9, 6, 5,10, 2,13,11, 4,12, 3 +}; + +const int cRsId::indices[] = { + 2, 4, 8, 9, 11, 15, 7, 14, 5, 10, 13, 3 +}; + +cRsId::cRsId() +{ + int error; + src_state = src_new(progdefaults.sample_converter, 1, &error); + if (error) { + LOG_ERROR("src_new error %d: %s", error, src_strerror(error)); + abort(); + } + src_data.end_of_input = 0; + + reset(); + + rsfft = new g_fft(RSID_ARRAY_SIZE); + + memset(fftwindow, 0, sizeof(fftwindow)); + + if (RSWINDOW) { + for (int i = 0; i < RSID_ARRAY_SIZE; i++) +// fftwindow[i] = blackman ( 1.0 * i / RSID_ARRAY_SIZE ); + fftwindow[i] = hamming ( 1.0 * i / RSID_ARRAY_SIZE ); +// fftwindow[i] = hanning ( 1.0 * i / RSID_ARRAY_SIZE ); +// fftwindow[i] = 1.0; + } + + pCodes1 = new unsigned char[rsid_ids_size1 * RSID_NSYMBOLS]; + memset(pCodes1, 0, sizeof(pCodes1) * sizeof(unsigned char)); + + pCodes2 = new unsigned char[rsid_ids_size2 * RSID_NSYMBOLS]; + memset(pCodes2, 0, sizeof(pCodes2) * sizeof(unsigned char)); + + // Initialization of assigned mode/submode IDs. + unsigned char* c; + for (int i = 0; i < rsid_ids_size1; i++) { + c = pCodes1 + i * RSID_NSYMBOLS; + Encode(rsid_ids_1[i].rs, c); + } + + for (int i = 0; i < rsid_ids_size2; i++) { + c = pCodes2 + i * RSID_NSYMBOLS; + Encode(rsid_ids_2[i].rs, c); + } + +#if 0 + printf("pcode 1\n"); + printf(",rs, name, mode,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14\n"); + for (int i = 0; i < rsid_ids_size1; i++) { + printf("%d,%d,%s,%d", i, rsid_ids_1[i].rs, rsid_ids_1[i].name, rsid_ids_1[i].mode); + for (int j = 0; j < RSID_NSYMBOLS + 1; j++) + printf(",%d", pCodes1[i*(RSID_NSYMBOLS + 1) + j]); + printf("\n"); + } + printf("\npcode 2\n"); + printf(", rs, name, mode,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14\n"); + for (int i = 0; i < rsid_ids_size2; i++) { + printf("%d,%d,%s,%d", i, rsid_ids_2[i].rs, rsid_ids_2[i].name, rsid_ids_2[i].mode); + for (int j = 0; j < RSID_NSYMBOLS + 1; j++) + printf(",%d", pCodes2[i*(RSID_NSYMBOLS+ 1) + j]); + printf("\n"); + } +#endif + + nBinLow = 3; + nBinHigh = RSID_FFT_SIZE - 32; // - RSID_NTIMES - 2 + + outbuf = 0; + symlen = 0; + + reset(); + +} + +cRsId::~cRsId() +{ + delete [] pCodes1; + delete [] pCodes2; + + delete [] outbuf; + delete rsfft; + src_delete(src_state); +} + +void cRsId::reset() +{ + iPrevDistance = iPrevDistance2 = 99; + bPrevTimeSliceValid = bPrevTimeSliceValid2 = false; + found1 = found2 = false; + rsid_secondary_time_out = 0; + + memset(aInputSamples, 0, (RSID_ARRAY_SIZE * 2) * sizeof(float)); + memset(aFFTAmpl, 0, RSID_FFT_SIZE * sizeof(rs_fft_type)); + memset(fft_buckets, 0, RSID_NTIMES * RSID_FFT_SIZE * sizeof(int)); + + for (int n = 0; n < RSID_ARRAY_SIZE; n++) + aFFTcmplx[n] = cmplx(0,0); + + int error = src_reset(src_state); + if (error) + LOG_ERROR("src_reset error %d: %s", error, src_strerror(error)); + src_data.src_ratio = 0.0; + inptr = RSID_FFT_SIZE; + hamming_resolution = progdefaults.RsID_label_type; +} + +void cRsId::Encode(int code, unsigned char *rsid) +{ + rsid[0] = code >> 8; + rsid[1] = (code >> 4) & 0x0f; + rsid[2] = code & 0x0f; + for (int i = 3; i < RSID_NSYMBOLS; i++) + rsid[i] = 0; + for (int i = 0; i < 12; i++) { + for (int j = RSID_NSYMBOLS - 1; j > 0; j--) + rsid[j] = rsid[j - 1] ^ Squares[(rsid[j] << 4) + indices[i]]; + rsid[0] = Squares[(rsid[0] << 4) + indices[i]]; + } +} + +void cRsId::CalculateBuckets(const rs_fft_type *pSpectrum, int iBegin, int iEnd) +{ + rs_fft_type Amp = 0.0, AmpMax = 0.0; + int iBucketMax = iBegin - 2; + int j; + + for (int i = iBegin; i < iEnd; i += 2) { + if (iBucketMax == i - 2) { + AmpMax = pSpectrum[i]; + iBucketMax = i; + for (j = i + 2; j < i + RSID_NTIMES + 2; j += 2) { + Amp = pSpectrum[j]; + if (Amp > AmpMax) { + AmpMax = Amp; + iBucketMax = j; + } + } + } + else { + j = i + RSID_NTIMES; + Amp = pSpectrum[j]; + if (Amp > AmpMax) { + AmpMax = Amp; + iBucketMax = j; + } + } + fft_buckets[RSID_NTIMES - 1][i] = (iBucketMax - i) >> 1; + } +} + +void cRsId::receive(const float* buf, size_t len) +{ + + if (len == 0) return; + + int srclen = static_cast(len); + double src_ratio = RSID_SAMPLE_RATE / active_modem->get_samplerate(); + + if (rsid_secondary_time_out > 0) { + rsid_secondary_time_out -= 1.0 * len / active_modem->get_samplerate(); + if (rsid_secondary_time_out <= 0) { + LOG_INFO("%s", "Secondary RsID timed out"); + reset(); + } + } + + if (src_data.src_ratio != src_ratio) { + src_data.src_ratio = src_ratio; + src_set_ratio(src_state, src_data.src_ratio); + } + + while (srclen > 0) { + src_data.data_in = const_cast(buf); + src_data.input_frames = srclen; + src_data.data_out = &aInputSamples[inptr]; + src_data.output_frames = RSID_ARRAY_SIZE * 2 - inptr; + src_data.input_frames_used = 0; + int error = src_process(src_state, &src_data); + if (unlikely(error)) { + LOG_ERROR("src_process error %d: %s", error, src_strerror(error)); + return; + } + size_t gend = src_data.output_frames_gen; + size_t used = src_data.input_frames_used; + inptr += gend; + buf += used; + srclen -= used; + + while (inptr >= RSID_ARRAY_SIZE) { + search(); + memmove(&aInputSamples[0], &aInputSamples[RSID_FFT_SAMPLES], + (RSID_BUFFER_SIZE - RSID_FFT_SAMPLES)*sizeof(float)); + inptr -= RSID_FFT_SAMPLES; + } + } +} + +void cRsId::search(void) +{ + if (progdefaults.rsidWideSearch) { + nBinLow = 3; + nBinHigh = RSID_FFT_SIZE - 32; + } + else { + float centerfreq = active_modem->get_freq(); + float bpf = 1.0 * RSID_ARRAY_SIZE / RSID_SAMPLE_RATE; + nBinLow = (int)((centerfreq - 100.0 * 2) * bpf); + nBinHigh = (int)((centerfreq + 100.0 * 2) * bpf); + } + if (nBinLow < 3) nBinLow = 3; + if (nBinHigh > RSID_FFT_SIZE - 32) nBinHigh = RSID_FFT_SIZE - 32; + + bool bReverse = !(wf->Reverse() ^ wf->USB()); + if (bReverse) { + nBinLow = RSID_FFT_SIZE - nBinHigh; + nBinHigh = RSID_FFT_SIZE - nBinLow; + } + + if (RSWINDOW) { + for (int i = 0; i < RSID_ARRAY_SIZE; i++) + aFFTcmplx[i] = cmplx(aInputSamples[i] * fftwindow[i], 0); + } else { + for (int i = 0; i < RSID_ARRAY_SIZE; i++) + aFFTcmplx[i] = cmplx(aInputSamples[i], 0); + } + + rsfft->ComplexFFT(aFFTcmplx); + + memset(aFFTAmpl, 0, sizeof(aFFTAmpl)); + + static const double pscale = 4.0 / (RSID_FFT_SIZE * RSID_FFT_SIZE); + + if (unlikely(bReverse)) { + for (int i = 0; i < RSID_FFT_SIZE; i++) + aFFTAmpl[RSID_FFT_SIZE - 1 - i] = norm(aFFTcmplx[i]) * pscale; + } else { + for (int i = 0; i < RSID_FFT_SIZE; i++) + aFFTAmpl[i] = norm(aFFTcmplx[i]) * pscale; + } + + int bucket_low = 3; + int bucket_high = RSID_FFT_SIZE - 32; + if (bReverse) { + bucket_low = RSID_FFT_SIZE - bucket_high; + bucket_high = RSID_FFT_SIZE - bucket_low; + } + + memmove(fft_buckets, + &(fft_buckets[1][0]), + (RSID_NTIMES - 1) * RSID_FFT_SIZE * sizeof(int)); + memset(&(fft_buckets[RSID_NTIMES - 1][0]), 0, RSID_FFT_SIZE * sizeof(int)); + + CalculateBuckets ( aFFTAmpl, bucket_low, bucket_high - RSID_NTIMES); + CalculateBuckets ( aFFTAmpl, bucket_low + 1, bucket_high - RSID_NTIMES); + + int symbol_out_1 = -1; + int bin_out_1 = -1; + int symbol_out_2 = -1; + int bin_out_2 = -1; + + if (rsid_secondary_time_out <= 0) { + found1 = search_amp(bin_out_1, symbol_out_1, pCodes1); + if (found1) { + if (symbol_out_1 != RSID_ESCAPE) { + if (bReverse) + bin_out_1 = 1024 - bin_out_1 - 31; + apply(bin_out_1, symbol_out_1, 0); + reset(); + return; + } else { + // 10 rsid_gap + 15 symbols + 2 for timing errors + rsid_secondary_time_out = 27 * RSID_SYMLEN; + return; + } + } else + return; + } + + found2 = search_amp(bin_out_2, symbol_out_2, pCodes2); + if (found2) { + if (symbol_out_2 != RSID_NONE2) { + if (bReverse) + bin_out_2 = 1024 - bin_out_2 - 31; + apply(bin_out_2, symbol_out_2, 1); + } + reset(); + } + +} + +void cRsId::setup_mode(int iSymbol) +{ + switch (iSymbol) { + case RSID_RTTY_ASCII_7: + progdefaults.rtty_baud = 5; + progdefaults.rtty_bits = 1; + progdefaults.rtty_shift = 9; + REQ(&set_rtty_tab_widgets); + break; + case RSID_RTTY_ASCII_8: + progdefaults.rtty_baud = 5; + progdefaults.rtty_bits = 2; + progdefaults.rtty_shift = 9; + REQ(&set_rtty_tab_widgets); + break; + case RSID_RTTY_45: + progdefaults.rtty_baud = 1; + progdefaults.rtty_bits = 0; + progdefaults.rtty_shift = 3; + REQ(&set_rtty_tab_widgets); + break; + case RSID_RTTY_50: + progdefaults.rtty_baud = 2; + progdefaults.rtty_bits = 0; + progdefaults.rtty_shift = 3; + REQ(&set_rtty_tab_widgets); + break; + case RSID_RTTY_75: + progdefaults.rtty_baud = 4; + progdefaults.rtty_bits = 0; + progdefaults.rtty_shift = 9; + REQ(&set_rtty_tab_widgets); + break; +// DominoEX / FEC + case RSID_DOMINOEX_4: case RSID_DOMINOEX_5: case RSID_DOMINOEX_8: + case RSID_DOMINOEX_11: case RSID_DOMINOEX_16: case RSID_DOMINOEX_22: + progdefaults.DOMINOEX_FEC = false; + REQ(&set_dominoex_tab_widgets); + break; + case RSID_DOMINOEX_4_FEC: case RSID_DOMINOEX_5_FEC: case RSID_DOMINOEX_8_FEC: + case RSID_DOMINOEX_11_FEC: case RSID_DOMINOEX_16_FEC: case RSID_DOMINOEX_22_FEC: + progdefaults.DOMINOEX_FEC = true; + REQ(&set_dominoex_tab_widgets); + break; +// olivia parameters + case RSID_OLIVIA_4_125: + progdefaults.oliviatones = 1; + progdefaults.oliviabw = 0; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_4_250: + progdefaults.oliviatones = 1; + progdefaults.oliviabw = 1; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_4_500: + progdefaults.oliviatones = 1; + progdefaults.oliviabw = 2; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_4_1000: + progdefaults.oliviatones = 1; + progdefaults.oliviabw = 3; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_4_2000: + progdefaults.oliviatones = 1; + progdefaults.oliviabw = 4; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_8_125: + progdefaults.oliviatones = 2; + progdefaults.oliviabw = 0; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_8_250: + progdefaults.oliviatones = 2; + progdefaults.oliviabw = 1; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_8_500: + progdefaults.oliviatones = 2; + progdefaults.oliviabw = 2; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_8_1000: + progdefaults.oliviatones = 2; + progdefaults.oliviabw = 3; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_8_2000: + progdefaults.oliviatones = 2; + progdefaults.oliviabw = 4; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_16_500: + progdefaults.oliviatones = 3; + progdefaults.oliviabw = 2; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_16_1000: + progdefaults.oliviatones = 3; + progdefaults.oliviabw = 3; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_16_2000: + progdefaults.oliviatones = 3; + progdefaults.oliviabw = 4; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_32_1000: + progdefaults.oliviatones = 4; + progdefaults.oliviabw = 3; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_32_2000: + progdefaults.oliviatones = 4; + progdefaults.oliviabw = 4; + REQ(&set_olivia_tab_widgets); + break; + case RSID_OLIVIA_64_2000: + progdefaults.oliviatones = 5; + progdefaults.oliviabw = 4; + REQ(&set_olivia_tab_widgets); + break; +// contestia parameters + case RSID_CONTESTIA_4_125: + progdefaults.contestiatones = 1; + progdefaults.contestiabw = 0; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_4_250: + progdefaults.contestiatones = 1; + progdefaults.contestiabw = 1; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_4_500: + progdefaults.contestiatones = 1; + progdefaults.contestiabw = 2; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_4_1000: + progdefaults.contestiatones = 1; + progdefaults.contestiabw = 3; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_4_2000: + progdefaults.contestiatones = 1; + progdefaults.contestiabw = 4; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_8_125: + progdefaults.contestiatones = 2; + progdefaults.contestiabw = 0; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_8_250: + progdefaults.contestiatones = 2; + progdefaults.contestiabw = 1; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_8_500: + progdefaults.contestiatones = 2; + progdefaults.contestiabw = 2; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_8_1000: + progdefaults.contestiatones = 2; + progdefaults.contestiabw = 3; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_8_2000: + progdefaults.contestiatones = 2; + progdefaults.contestiabw = 4; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_16_500: + progdefaults.contestiatones = 3; + progdefaults.contestiabw = 2; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_16_1000: + progdefaults.contestiatones = 3; + progdefaults.contestiabw = 3; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_16_2000: + progdefaults.contestiatones = 3; + progdefaults.contestiabw = 4; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_32_1000: + progdefaults.contestiatones = 4; + progdefaults.contestiabw = 3; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_32_2000: + progdefaults.contestiatones = 4; + progdefaults.contestiabw = 4; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_64_500: + progdefaults.contestiatones = 5; + progdefaults.contestiabw = 2; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_64_1000: + progdefaults.contestiatones = 5; + progdefaults.contestiabw = 3; + REQ(&set_contestia_tab_widgets); + break; + case RSID_CONTESTIA_64_2000: + progdefaults.contestiatones = 5; + progdefaults.contestiabw = 4; + REQ(&set_contestia_tab_widgets); + break; + default: + break; + } // switch (iSymbol) +} + +void cRsId::apply(int iBin, int iSymbol, int extended) +{ + ENSURE_THREAD(TRX_TID); + + double rsidfreq = 0, currfreq = 0; + int n, mbin = NUM_MODES; + + int tblsize; + const RSIDs *p_rsid; + + if (extended) { + tblsize = rsid_ids_size2; + p_rsid = rsid_ids_2; + } + else { + tblsize = rsid_ids_size1; + p_rsid = rsid_ids_1; + } + + currfreq = active_modem->get_freq(); + rsidfreq = (iBin + RSID_NSYMBOLS - 0.5) * RSID_SAMPLE_RATE / 2048.0; + + for (n = 0; n < tblsize; n++) { + if (p_rsid[n].rs == iSymbol) { + mbin = p_rsid[n].mode; + break; + } + } + + if (mbin == NUM_MODES) { + char msg[50]; + if (n < tblsize) // RSID known but unimplemented + snprintf(msg, sizeof(msg), "RSID: %s unimplemented", + p_rsid[n].name); + else // RSID unknown; shouldn't happen + snprintf(msg, sizeof(msg), "RSID: code %d unknown", iSymbol); + put_status(msg, 4.0); + LOG_VERBOSE("%s", msg); + return; + } + + if (progdefaults.rsid_rx_modes.test(mbin)) { + char msg[50]; + snprintf(msg, sizeof(msg), "RSID: %s @ %0.1f Hz", p_rsid[n].name, rsidfreq); + LOG_VERBOSE("%s", msg); + } + else { + char msg[50]; + snprintf(msg, sizeof(msg), "Ignoring RSID: %s @ %0.1f Hz", p_rsid[n].name, rsidfreq); + LOG_DEBUG("%s", msg); + return; + } + + if (progdefaults.ENABLE_RSID_MATCH) + audio_alert->alert(progdefaults.RSID_MATCH); + + if (mailclient || mailserver) + REQ(pskmail_notify_rsid, mbin); + + if (progdefaults.rsid_auto_disable) + REQ(toggleRSID); + + if (iSymbol == RSID_EOT) { + if (progdefaults.rsid_eot_squelch) { + REQ(rsid_eot_squelch); + if (!progdefaults.disable_rsid_warning_dialog_box) + REQ(notify_rsid_eot, mbin, rsidfreq); + } + return; + } + + if (!progdefaults.disable_rsid_warning_dialog_box) + REQ(notify_rsid, mbin, rsidfreq); + + if (progdefaults.rsid_notify_only) { + if (data_io_enabled == KISS_IO) { + bcast_rsid_kiss_frame(rsidfreq, mbin, (int) active_modem->get_txfreq_woffset(), + active_modem->get_mode(), RSID_KISS_NOTIFY); + } + return; + } + + if (progdefaults.rsid_mark) // mark current modem & freq + REQ(note_qrg, false, "\nBefore RSID: ", "\n\n", + active_modem->get_mode(), 0LL, currfreq); + + if(active_modem) // Currently only effects Olivia, Contestia and MT63. + active_modem->rx_flush(); + + setup_mode(iSymbol); + + if (progdefaults.rsid_squelch) + REQ(init_modem_squelch, mbin, progdefaults.disable_rsid_freq_change ? currfreq : rsidfreq); + else + REQ(init_modem, mbin, progdefaults.disable_rsid_freq_change ? currfreq : rsidfreq); + +} + +inline int cRsId::HammingDistance(int iBucket, unsigned char *p2) +{ + int dist = 0; + for (int i = 0, j = 1; i < RSID_NSYMBOLS; i++, j += 2) { + if (fft_buckets[j][iBucket] != p2[i]) { + ++dist; + if (dist > hamming_resolution) + break; + } + } + return dist; +} + +bool cRsId::search_amp( int &bin_out, int &symbol_out, unsigned char *pcode) +{ + int i, j; + int iDistanceMin = 1000; // infinity + int iDistance = 1000; + int iBin = -1; + int iSymbol = -1; + + int tblsize; + const RSIDs *prsid; + + if (pcode == pCodes1) { + tblsize = rsid_ids_size1; + prsid = rsid_ids_1; + } else { + tblsize = rsid_ids_size2; + prsid = rsid_ids_2; + } + + unsigned char *pc = 0; + for (i = 0; i < tblsize; i++) { + pc = pcode + i * RSID_NSYMBOLS; + for (j = nBinLow; j < nBinHigh - RSID_NTIMES; j++) { + iDistance = HammingDistance(j, pc); + if (iDistance < iDistanceMin) { + iDistanceMin = iDistance; + iSymbol = prsid[i].rs; + iBin = j; + if (iDistanceMin == 0) break; + } + } + } + + if (iDistanceMin <= hamming_resolution) { + symbol_out = iSymbol; + bin_out = iBin; + return true; + } + + return false; +} + +//============================================================================= +// transmit rsid code for current mode +//============================================================================= + +bool cRsId::assigned(trx_mode mode) { + + rmode = RSID_NONE; + rmode2 = RSID_NONE2; + + switch (mode) { + case MODE_EOT : + rmode = RSID_EOT; +std::cout << "send RSID_EOT" << std::endl; + return true; + case MODE_RTTY : + if (progdefaults.rtty_baud == 5 && progdefaults.rtty_bits == 1 && progdefaults.rtty_shift == 9) + rmode = RSID_RTTY_ASCII_7; + else if (progdefaults.rtty_baud == 5 && progdefaults.rtty_bits == 1 && progdefaults.rtty_shift == 9) + rmode = RSID_RTTY_ASCII_8; + else if (progdefaults.rtty_baud == 1 && progdefaults.rtty_bits == 0 && progdefaults.rtty_shift == 3) + rmode = RSID_RTTY_45; + else if (progdefaults.rtty_baud == 2 && progdefaults.rtty_bits == 0 && progdefaults.rtty_shift == 3) + rmode = RSID_RTTY_50; + else if (progdefaults.rtty_baud == 4 && progdefaults.rtty_bits == 0 && progdefaults.rtty_shift == 9) + rmode = RSID_RTTY_75; + else + return false; + return true; + break; + + case MODE_OLIVIA: + case MODE_OLIVIA_4_250: + case MODE_OLIVIA_8_250: + case MODE_OLIVIA_4_500: + case MODE_OLIVIA_8_500: + case MODE_OLIVIA_16_500: + case MODE_OLIVIA_8_1000: + case MODE_OLIVIA_16_1000: + case MODE_OLIVIA_32_1000: + case MODE_OLIVIA_64_2000: + if (progdefaults.oliviatones == 1 && progdefaults.oliviabw == 0) + rmode = RSID_OLIVIA_4_125; + else if (progdefaults.oliviatones == 1 && progdefaults.oliviabw == 1) + rmode = RSID_OLIVIA_4_250; + else if (progdefaults.oliviatones == 1 && progdefaults.oliviabw == 2) + rmode = RSID_OLIVIA_4_500; + else if (progdefaults.oliviatones == 1 && progdefaults.oliviabw == 3) + rmode = RSID_OLIVIA_4_1000; + else if (progdefaults.oliviatones == 1 && progdefaults.oliviabw == 4) + rmode = RSID_OLIVIA_4_2000; + + else if (progdefaults.oliviatones == 2 && progdefaults.oliviabw == 0) + rmode = RSID_OLIVIA_8_125; + else if (progdefaults.oliviatones == 2 && progdefaults.oliviabw == 1) + rmode = RSID_OLIVIA_8_250; + else if (progdefaults.oliviatones == 2 && progdefaults.oliviabw == 2) + rmode = RSID_OLIVIA_8_500; + else if (progdefaults.oliviatones == 2 && progdefaults.oliviabw == 3) + rmode = RSID_OLIVIA_8_1000; + else if (progdefaults.oliviatones == 2 && progdefaults.oliviabw == 4) + rmode = RSID_OLIVIA_8_2000; + + else if (progdefaults.oliviatones == 3 && progdefaults.oliviabw == 2) + rmode = RSID_OLIVIA_16_500; + else if (progdefaults.oliviatones == 3 && progdefaults.oliviabw == 3) + rmode = RSID_OLIVIA_16_1000; + else if (progdefaults.oliviatones == 3 && progdefaults.oliviabw == 4) + rmode = RSID_OLIVIA_16_2000; + + else if (progdefaults.oliviatones == 4 && progdefaults.oliviabw == 3) + rmode = RSID_OLIVIA_32_1000; + else if (progdefaults.oliviatones == 4 && progdefaults.oliviabw == 4) + rmode = RSID_OLIVIA_32_2000; + + else if (progdefaults.oliviatones == 5 && progdefaults.oliviabw == 4) + rmode = RSID_OLIVIA_64_2000; + + else + return false; + return true; + break; + + case MODE_CONTESTIA: + case MODE_CONTESTIA_4_125: + case MODE_CONTESTIA_4_250: + case MODE_CONTESTIA_8_250: + case MODE_CONTESTIA_4_500: + case MODE_CONTESTIA_8_500: + case MODE_CONTESTIA_16_500: + case MODE_CONTESTIA_8_1000: + case MODE_CONTESTIA_16_1000: + case MODE_CONTESTIA_32_1000: + case MODE_CONTESTIA_64_2000: + if (progdefaults.contestiatones == 1 && progdefaults.contestiabw == 0) + rmode = RSID_CONTESTIA_4_125; + else if (progdefaults.contestiatones == 1 && progdefaults.contestiabw == 1) + rmode = RSID_CONTESTIA_4_250; + else if (progdefaults.contestiatones == 1 && progdefaults.contestiabw == 2) + rmode = RSID_CONTESTIA_4_500; + else if (progdefaults.contestiatones == 1 && progdefaults.contestiabw == 3) + rmode = RSID_CONTESTIA_4_1000; + else if (progdefaults.contestiatones == 1 && progdefaults.contestiabw == 4) + rmode = RSID_CONTESTIA_4_2000; + + else if (progdefaults.contestiatones == 2 && progdefaults.contestiabw == 0) + rmode = RSID_CONTESTIA_8_125; + else if (progdefaults.contestiatones == 2 && progdefaults.contestiabw == 1) + rmode = RSID_CONTESTIA_8_250; + else if (progdefaults.contestiatones == 2 && progdefaults.contestiabw == 2) + rmode = RSID_CONTESTIA_8_500; + else if (progdefaults.contestiatones == 2 && progdefaults.contestiabw == 3) + rmode = RSID_CONTESTIA_8_1000; + else if (progdefaults.contestiatones == 2 && progdefaults.contestiabw == 4) + rmode = RSID_CONTESTIA_8_2000; + + else if (progdefaults.contestiatones == 3 && progdefaults.contestiabw == 2) + rmode = RSID_CONTESTIA_16_500; + else if (progdefaults.contestiatones == 3 && progdefaults.contestiabw == 3) + rmode = RSID_CONTESTIA_16_1000; + else if (progdefaults.contestiatones == 3 && progdefaults.contestiabw == 4) + rmode = RSID_CONTESTIA_16_2000; + + else if (progdefaults.contestiatones == 4 && progdefaults.contestiabw == 3) + rmode = RSID_CONTESTIA_32_1000; + else if (progdefaults.contestiatones == 4 && progdefaults.contestiabw == 4) + rmode = RSID_CONTESTIA_32_2000; + + else if (progdefaults.contestiatones == 5 && progdefaults.contestiabw == 2) + rmode = RSID_CONTESTIA_64_500; + else if (progdefaults.contestiatones == 5 && progdefaults.contestiabw == 3) + rmode = RSID_CONTESTIA_64_1000; + else if (progdefaults.contestiatones == 5 && progdefaults.contestiabw == 4) + rmode = RSID_CONTESTIA_64_2000; + + else + return false; + return true; + break; + + case MODE_DOMINOEX4: + if (progdefaults.DOMINOEX_FEC) + rmode = RSID_DOMINOEX_4_FEC; + break; + case MODE_DOMINOEX5: + if (progdefaults.DOMINOEX_FEC) + rmode = RSID_DOMINOEX_5_FEC; + break; + case MODE_DOMINOEX8: + if (progdefaults.DOMINOEX_FEC) + rmode = RSID_DOMINOEX_8_FEC; + break; + case MODE_DOMINOEX11: + if (progdefaults.DOMINOEX_FEC) + rmode = RSID_DOMINOEX_11_FEC; + break; + case MODE_DOMINOEX16: + if (progdefaults.DOMINOEX_FEC) + rmode = RSID_DOMINOEX_16_FEC; + break; + case MODE_DOMINOEX22: + if (progdefaults.DOMINOEX_FEC) + rmode = RSID_DOMINOEX_22_FEC; + break; + + case MODE_MT63_500S: + rmode = RSID_MT63_500_ST; + break; + case MODE_MT63_500L: + rmode = RSID_MT63_500_LG; + break; + case MODE_MT63_1000S: + rmode = RSID_MT63_1000_ST; + break; + case MODE_MT63_1000L: + rmode = RSID_MT63_1000_LG; + break; + case MODE_MT63_2000S: + rmode = RSID_MT63_2000_ST; + break; + case MODE_MT63_2000L: + rmode = RSID_MT63_2000_LG; + break; + } + +// if rmode is still unset, look it up +// Try secondary table first + if (rmode == RSID_NONE) { + for (size_t i = 0; i < sizeof(rsid_ids_2)/sizeof(*rsid_ids_2); i++) { + if (mode == rsid_ids_2[i].mode) { + rmode = RSID_ESCAPE; + rmode2 = rsid_ids_2[i].rs; + break; + } + } + if (rmode2 == RSID_NONE2) { + for (size_t i = 0; i < sizeof(rsid_ids_1)/sizeof(*rsid_ids_1); i++) { + if (mode == rsid_ids_1[i].mode) { + rmode = rsid_ids_1[i].rs; + break; + } + } + } + } + if (rmode == RSID_NONE) { + LOG_DEBUG("%s mode is not assigned an RSID", mode_info[mode].sname); + return false; + } + return true; +} + +void cRsId::send_eot() +{ + unsigned char rsid[RSID_NSYMBOLS]; + double sr; + size_t len; + int iTone; + double freq, phaseincr; + double fr; + double phase; + + Encode(RSID_EOT, rsid); + sr = active_modem->get_samplerate(); + len = (size_t)floor(RSID_SYMLEN * sr); + if (unlikely(len != symlen)) { + symlen = len; + delete [] outbuf; + outbuf = new double[symlen]; + } + +// transmit 5 symbol periods of silence at beginning of rsid + memset(outbuf, 0, symlen * sizeof(*outbuf)); + for (int i = 0; i < 5; i++) + active_modem->ModulateXmtr(outbuf, symlen); + +// transmit sequence of 15 symbols (tones) + fr = 1.0 * active_modem->get_txfreq_woffset() - (RSID_SAMPLE_RATE * 7 / 1024); + phase = 0.0; + + for (int i = 0; i < 15; i++) { + iTone = rsid[i]; + if (active_modem->get_reverse()) + iTone = 15 - iTone; + freq = fr + iTone * RSID_SAMPLE_RATE / 1024; + phaseincr = 2.0 * M_PI * freq / sr; + + for (size_t j = 0; j < symlen; j++) { + phase += phaseincr; + if (phase > 2.0 * M_PI) phase -= 2.0 * M_PI; + outbuf[j] = sin(phase); + } + active_modem->ModulateXmtr(outbuf, symlen); + } +} + +void cRsId::send(bool preRSID) +{ + trx_mode mode = active_modem->get_mode(); + + if (!progdefaults.rsid_tx_modes.test(mode)) { + LOG_DEBUG("Mode %s excluded, not sending RSID", mode_info[mode].sname); + return; + } + + if (!progdefaults.rsid_post && !preRSID) + return; + + if (!assigned(mode)) return; + + unsigned char rsid[RSID_NSYMBOLS]; + double sr; + size_t len; + int iTone; + double freq, phaseincr; + double fr; + double phase; + + Encode(rmode, rsid); + sr = active_modem->get_samplerate(); + len = (size_t)floor(RSID_SYMLEN * sr); + if (unlikely(len != symlen)) { + symlen = len; + delete [] outbuf; + outbuf = new double[symlen]; + } + +// transmit 5 symbol periods of silence at beginning of rsid + memset(outbuf, 0, symlen * sizeof(*outbuf)); + for (int i = 0; i < 5; i++) + active_modem->ModulateXmtr(outbuf, symlen); + +// transmit sequence of 15 symbols (tones) + fr = 1.0 * active_modem->get_txfreq_woffset() - (RSID_SAMPLE_RATE * 7 / 1024); + phase = 0.0; + + for (int i = 0; i < 15; i++) { + iTone = rsid[i]; + if (active_modem->get_reverse()) + iTone = 15 - iTone; + freq = fr + iTone * RSID_SAMPLE_RATE / 1024; + phaseincr = 2.0 * M_PI * freq / sr; + + for (size_t j = 0; j < symlen; j++) { + phase += phaseincr; + if (phase > 2.0 * M_PI) phase -= 2.0 * M_PI; + outbuf[j] = sin(phase); + } + active_modem->ModulateXmtr(outbuf, symlen); + } + + if (rmode == RSID_ESCAPE && rmode2 != RSID_NONE2) { +// transmit 10 symbol periods of silence between rsid sequences + memset(outbuf, 0, symlen * sizeof(*outbuf)); + for (int i = 0; i < 10; i++) + active_modem->ModulateXmtr(outbuf, symlen); + + Encode(rmode2, rsid); + sr = active_modem->get_samplerate(); + len = (size_t)floor(RSID_SYMLEN * sr); + if (unlikely(len != symlen)) { + symlen = len; + delete [] outbuf; + outbuf = new double[symlen]; + } +// transmit sequence of 15 symbols (tones) + fr = 1.0 * active_modem->get_txfreq_woffset() - (RSID_SAMPLE_RATE * 7 / 1024); + phase = 0.0; + + for (int i = 0; i < 15; i++) { + iTone = rsid[i]; + if (active_modem->get_reverse()) + iTone = 15 - iTone; + freq = fr + iTone * RSID_SAMPLE_RATE / 1024; + phaseincr = 2.0 * M_PI * freq / sr; + + for (size_t j = 0; j < symlen; j++) { + phase += phaseincr; + if (phase > 2.0 * M_PI) phase -= 2.0 * M_PI; + outbuf[j] = sin(phase); + } + active_modem->ModulateXmtr(outbuf, symlen); + } + } + + // 5 symbol periods of silence at end of transmission + // and between RsID and the data signal + int nperiods = 5; + memset(outbuf, 0, symlen * sizeof(*outbuf)); + for (int i = 0; i < nperiods; i++) + active_modem->ModulateXmtr(outbuf, symlen); + +} + diff --git a/rsid.h b/rsid.h new file mode 100644 index 0000000..867b784 --- /dev/null +++ b/rsid.h @@ -0,0 +1,78 @@ +// ---------------------------------------------------------------------------- +// +// rsid.h +// +// Copyright (C) 2008, 2009 +// Dave Freese, W1HKJ +// Copyright (C) 2009 +// Stelios Bounanos, M0GLD +// +// This file is part of fldigi. +// +// Fldigi 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. +// +// Fldigi 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 fldigi. If not, see . +// ---------------------------------------------------------------------------- + +// ---------------------------------------------------------------------------- +// Tone separation: 10.766Hz +// Integer tone separator (x 16): 172 +// Error on 16 tones: 0.25Hz + +// Tone duration: 0.093 sec +// Tone duration, #samples at 8ksps: 743 +// Error on 15 tones: negligible + +// 1024 samples -> 512 tones +// 2048 samples, second half zeros + +// each 512 samples new FFT +// ---------------------------------------------------------------------------- + +// This code has been modified to work with QtSOundModem by John Wiseman G8BPQ + +// Main change is to run at 12000 samples/sec and only support QtSM Modes. This +// makes it incompatble with MultiPSK and fldigi + +// Needed code has been extracted and converted to C + +#ifndef RSID_H +#define RSID_H + +#include "globals.h" +//#include "gfft.h" + +//#define RSID_SAMPLE_RATE 11025.0f +#define RSID_SAMPLE_RATE 12000.0f + +#define RSID_FFT_SAMPLES 512 +#define RSID_FFT_SIZE 1024 +#define RSID_ARRAY_SIZE (RSID_FFT_SIZE * 2) +#define RSID_BUFFER_SIZE (RSID_ARRAY_SIZE * 2) + +#define RSID_NSYMBOLS 15 +#define RSID_NTIMES (RSID_NSYMBOLS * 2) +#define RSID_PRECISION 2.7 // detected frequency precision in Hz + +// each rsid symbol has a duration equal to 1024 samples at 11025 Hz smpl rate +#define RSID_SYMLEN (1024.0 / RSID_SAMPLE_RATE) // 0.09288 // duration of each rsid symbol + +enum { + RSID_BANDWIDTH_500 = 0, + RSID_BANDWIDTH_1K, + RSID_BANDWIDTH_WIDE, +}; + +typedef double rs_fft_type; + + +#endif diff --git a/rsid_defs.cxx b/rsid_defs.cxx new file mode 100644 index 0000000..3bbe71a --- /dev/null +++ b/rsid_defs.cxx @@ -0,0 +1,53 @@ +// ---------------------------------------------------------------------------- +// Copyright (C) 2014 +// David Freese, W1HKJ +// +// This file is part of fldigi +// +// fldigi 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. +// +// fldigi is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ---------------------------------------------------------------------------- + +// Syntax: ELEM_(rsid_code, rsid_tag, fldigi_mode) +// fldigi_mode is NUM_MODES if mode is not available in fldigi, +// otherwise one of the tags defined in globals.h. +// rsid_tag is stringified and may be shown to the user. + + +/* + ELEM_(263, ESCAPE, NUM_MODES) \ +*/ +#undef ELEM_ +#define RSID_LIST \ + \ +/* ESCAPE used to transition to 2nd RSID set */ \ + \ + ELEM_(263, EOT, MODE_EOT) \ + ELEM_(35, PACKET_300, NUM_MODES) \ + ELEM_(36, PACKET_1200, NUM_MODES) \ + ELEM_(155, PACKET_PSK1200, NUM_MODES) \ + \ + /* NONE must be the last element */ \ + ELEM_(0, NONE, NUM_MODES) + +#define ELEM_(code_, tag_, mode_) RSID_ ## tag_ = code_, +enum { RSID_LIST }; +#undef ELEM_ + +#define ELEM_(code_, tag_, mode_) { RSID_ ## tag_, mode_, #tag_ }, + +const struct RSIDs rsid_ids_1[] = { RSID_LIST }; + +#undef ELEM_ + +const int rsid_ids_size1 = sizeof(rsid_ids_1)/sizeof(*rsid_ids_1) - 1; diff --git a/sm_main - Copy.c b/sm_main - Copy.c new file mode 100644 index 0000000..9114a25 --- /dev/null +++ b/sm_main - Copy.c @@ -0,0 +1,2850 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +newsamp + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" + +void make_core_BPF(UCHAR snd_ch, short freq, short width); +void make_core_TXBPF(UCHAR snd_ch, float freq, float width); +void make_core_INTR(UCHAR snd_ch); +void make_core_LPF(UCHAR snd_ch, short width); +void wf_pointer(int snd_ch); + +char modes_name[modes_count][20] = +{ + "AFSK AX.25 300bd","AFSK AX.25 1200bd","AFSK AX.25 600bd","AFSK AX.25 2400bd", + "BPSK AX.25 1200bd","BPSK AX.25 600bd","BPSK AX.25 300bd","BPSK AX.25 2400bd", + "QPSK AX.25 4800bd","QPSK AX.25 3600bd","QPSK AX.25 2400bd","BPSK FEC 4x100bd", + "DW QPSK V26A 2400bd","DW 8PSK V27 4800bd","DW QPSK V26B 2400bd", "ARDOP Packet" +}; + +typedef struct wavehdr_tag { + unsigned short * lpData; /* pointer to locked data buffer */ + int dwBufferLength; /* length of data buffer */ + int dwBytesRecorded; /* used for input only */ + int * dwUser; /* for client's use */ + int dwFlags; /* assorted flags (see defines) */ + int dwLoops; /* loop control counter */ + struct wavehdr_tag *lpNext; /* reserved for driver */ + int * reserved; /* reserved for driver */ +} WAVEHDR, *PWAVEHDR, * NPWAVEHDR, * LPWAVEHDR; + +extern int pnt_change[5]; +int debugmode = 0; +extern float src_buf[5][2048]; +extern Byte RCVR[5]; + +int SatelliteMode = 0; + +int UDPServerPort = 8884; +int UDPClientPort = 8888; +int TXPort = 8884; + +BOOL Firstwaterfall = 1; +BOOL Secondwaterfall = 1; + +BOOL multiCore = FALSE; + +/* +type + TComboBox = class(StdCtrls.TComboBox) + private + procedure CMMouseWheel(var msg TCMMouseWheel); message CM_MOUSEWHEEL; + end; + TData16 = array [0..4095] of smallint; + PData16 = ^TData16; + TWaveHeader = record + RIFF dword; + ChunkLen integer; + WAVE dword; + fmt dword; + FormatLen integer; + Format word; + Channels word; + Frequency integer; + BytesPS integer; + BlockAlign word; + BitsPS word; + data dword; + DataLen integer + end; + TForm1 = class(TForm) + Panel5 TPanel; + ServerSocket1 TServerSocket; + MainMenu1 TMainMenu; + Settings1 TMenuItem; + OutputVolume1 TMenuItem; + InputVolume1 TMenuItem; + CoolTrayIcon1 TCoolTrayIcon; + ImageList1 TImageList; + ABout1 TMenuItem; + Panel1 TPanel; + Panel2 TPanel; + View1 TMenuItem; + Firstwaterfall1 TMenuItem; + Secondwaterfall1 TMenuItem; + Panel3 TPanel; + StringGrid1 TStringGrid; + Devices1 TMenuItem; + Statustable1 TMenuItem; + Monitor1 TMenuItem; + Panel4 TPanel; + PaintBox2 TPaintBox; + Filters1 TMenuItem; + Clearmonitor1 TMenuItem; + RxRichEdit1 TRxRichEdit; + MemoPopupMenu1 TPopupMenu; + Copytext1 TMenuItem; + Label1 TLabel; + Label5 TLabel; + ApplicationEvents1 TApplicationEvents; + PaintBox1 TPaintBox; + PaintBox3 TPaintBox; + ServerSocket2 TServerSocket; + Font1 TMenuItem; + FontDialog1 TFontDialog; + N1 TMenuItem; + Calibration1 TMenuItem; + Panel9 TPanel; + Panel6 TPanel; + Label4 TLabel; + Shape2 TShape; + ComboBox2 TComboBox; + SpinEdit2 TSpinEdit; + Panel7 TPanel; + Label3 TLabel; + Shape1 TShape; + ComboBox1 TComboBox; + SpinEdit1 TSpinEdit; + Panel8 TPanel; + Label2 TLabel; + TrackBar1 TTrackBar; + CheckBox1 TCheckBox; + OpenDialog1 TOpenDialog; + procedure FormCreate(Sender TObject); + procedure TrackBar1Change(Sender TObject); + procedure PaintBox1MouseMove(Sender TObject; Shift TShiftState; X, + Y Integer); + procedure PaintBox1MouseDown(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); + procedure ServerSocket1ClientRead(Sender TObject; + Socket TCustomWinSocket); + procedure ServerSocket1ClientError(Sender TObject; + Socket TCustomWinSocket; ErrorEvent TErrorEvent; + var ErrorCode Integer); + procedure OutputVolume1Click(Sender TObject); + procedure InputVolume1Click(Sender TObject); + procedure ServerSocket1ClientConnect(Sender TObject; + Socket TCustomWinSocket); + procedure ServerSocket1ClientDisconnect(Sender TObject; + Socket TCustomWinSocket); + procedure CoolTrayIcon1Click(Sender TObject); + procedure CoolTrayIcon1Cycle(Sender TObject; NextIndex Integer); + procedure ABout1Click(Sender TObject); + procedure PaintBox3MouseDown(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); + procedure PaintBox3MouseMove(Sender TObject; Shift TShiftState; X, + Y Integer); + procedure Firstwaterfall1Click(Sender TObject); + procedure Secondwaterfall1Click(Sender TObject); + procedure Devices1Click(Sender TObject); + procedure Statustable1Click(Sender TObject); + procedure Monitor1Click(Sender TObject); + procedure FormPaint(Sender TObject); + procedure Filters1Click(Sender TObject); + procedure SpinEdit1Change(Sender TObject); + procedure SpinEdit2Change(Sender TObject); + procedure Clearmonitor1Click(Sender TObject); + procedure Copytext1Click(Sender TObject); + procedure PaintBox3MouseUp(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); + procedure PaintBox1MouseUp(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); + procedure ApplicationEvents1Minimize(Sender TObject); + procedure ApplicationEvents1Restore(Sender TObject); + procedure ServerSocket2ClientConnect(Sender TObject; + Socket TCustomWinSocket); + procedure ServerSocket2ClientDisconnect(Sender TObject; + Socket TCustomWinSocket); + procedure ServerSocket2ClientError(Sender TObject; + Socket TCustomWinSocket; ErrorEvent TErrorEvent; + var ErrorCode Integer); + procedure ServerSocket2ClientRead(Sender TObject; + Socket TCustomWinSocket); + procedure Font1Click(Sender TObject); + procedure Calibration1Click(Sender TObject); + procedure ComboBox1Change(Sender TObject); + procedure ComboBox1KeyDown(Sender TObject; var Key Word; + Shift TShiftState); + procedure ComboBox1KeyPress(Sender TObject; var Key Char); + procedure ComboBox2Change(Sender TObject); + procedure FormDestroy(Sender TObject); + private + { Private declarations } + procedure BufferFull(var Msg TMessage); Message MM_WIM_DATA; + procedure BufferFull1(var Msg TMessage); Message MM_WOM_DONE; + procedure make_wave_buf(snd_ch byte; buf PChar); + procedure disp2(snd_ch byte); + procedure create_timer1; + procedure free_timer1; + procedure show_panels; + procedure show_combobox; + procedure Timer_Event2; + procedure waterfall_init; + procedure waterfall_free; + public + { Public declarations } + function get_idx_by_name(name string) word; + function frame_monitor(s,code string; tx_stat boolean) string; + procedure ChangePriority; + procedure put_frame(snd_ch byte; frame,code string; tx_stat,excluded boolean); + procedure show_grid; + procedure RX2TX(snd_ch byte); + procedure TX2RX(snd_ch byte); + procedure WriteIni; + procedure ReadIni; + procedure init_8P4800(snd_ch byte); + procedure init_DW2400(snd_ch byte); + procedure init_AE2400(snd_ch byte); + procedure init_MP400(snd_ch byte); + procedure init_Q4800(snd_ch byte); + procedure init_Q3600(snd_ch byte); + procedure init_Q2400(snd_ch byte); + procedure init_P2400(snd_ch byte); + procedure init_P1200(snd_ch byte); + procedure init_P600(snd_ch byte); + procedure init_P300(snd_ch byte); + procedure init_300(snd_ch byte); + procedure init_600(snd_ch byte); + procedure init_1200(snd_ch byte); + procedure init_2400(snd_ch byte); + procedure init_speed(snd_ch byte); + procedure get_filter_values(idx byte; var dbpf,dtxbpf,dbpftap,dlpf,dlpftap word); + procedure show_mode_panels; + procedure show_modes; + procedure show_freq_a; + procedure show_freq_b; + procedure ChkSndDevName; + procedure StartRx; + procedure StartTx(snd_ch byte); + procedure StopRx; + procedure StopTx(snd_ch byte); + procedure StartAGW; + procedure StartKISS; + procedure wf_scale; + procedure wf_pointer(snd_ch byte); + end; + +var +/*/ + +BOOL MinOnStart = 0; +//RS TReedSolomon; +// Form1 TForm1; +// WaveFormat TWaveFormatEx; +int Channels = 2; +int BitsPerSample = 16; +float TX_Samplerate = 12000; +float RX_Samplerate = 12000; +int RX_SR = 11025; +int TX_SR = 11025; +int RX_PPM = 0; +int TX_PPM = 0; +int tx_bufsize = 512; +int rx_bufsize = 512; +int tx_bufcount = 16; +int rx_bufcount = 16; +int mouse_down[2] = {0, 0}; +//UCHAR * RX_pBuf array[257]; +// RX_header array[1..256] of TWaveHdr; +// TX_pBuf array[1..4,1..256] of pointer; +//TX_header array[1..4,1..256] of TWaveHdr; +UCHAR calib_mode[5] = {0,0,0,0}; +UCHAR snd_status [5] = {0,0,0,0}; +UCHAR buf_status [5] = {0,0,0,0}; +UCHAR tx_buf_num1 [5] = {0,0,0,0}; +UCHAR tx_buf_num [5] = {0,0,0,0}; + +extern short active_rx_freq[5]; + + + +int speed[5] = {0,0,0,0}; +int panels[6] = {1,1,1,1,1}; + +short fft_buf[5][4096]; +UCHAR fft_disp[5][4096]; +int fftCount = 0; // FTF samples collected + +// bm array[1..4] of TBitMap; +// bm1,bm2,bm3 TBitMap; + +// WaveInHandle hWaveIn; +// WaveOutHandle array[1..4] of hWaveOut; +int RXBufferLength; + +int grid_time = 0; +int fft_mult = 0; +int fft_spd = 3; +int grid_timer = 0; +int stop_wf = 0; +int raduga = DISP_RGB; +char snd_rx_device_name[32] = ""; +char snd_tx_device_name[32] = ""; +int snd_rx_device = 0; +int snd_tx_device = 0; +UCHAR mod_icon_status = MOD_IDLE; +UCHAR last_mod_icon_status = MOD_IDLE; +UCHAR icon_timer = 0; +// TelIni TIniFile; +char cur_dir[] = ""; +// TimerId1 cardinal; +// TimerId2 cardinal; +UCHAR TimerStat1 = TIMER_FREE; +UCHAR TimerStat2 = TIMER_FREE; +int stat_log = FALSE; + +int PTT_device = FALSE; +int RX_device = FALSE; +int TX_device = FALSE; +int TX_rotate = FALSE; +int UsingBothChannels = FALSE; +int UsingLeft = FALSE; +int UsingRight = FALSE; + +int SCO = FALSE; +int DualPTT = TRUE; +UCHAR DebugMode = 0; +UCHAR TimerEvent = TIMER_EVENT_OFF; +int nr_monitor_lines = 50; +int UTC_Time = FALSE; +int MainPriority = 0; +// MainThreadHandle THandle; +UCHAR w_state = WIN_MAXIMIZED; + + /* +implementation + +{$R *.DFM} + +uses ax25_mod, ax25_demod, ax25, ax25_l2, ax25_ptt, ax25_agw, ax25_about, rgb_rad, + AX25_set, ax25_filter, AX25_modem_set, kiss_mode, ax25_calibration; + +procedure TComboBox.CMMouseWheel(var msg TCMMouseWheel); +begin + if SendMessage(GetFocus, CB_GETDROPPEDSTATE, 0, 0) = 0 then msg.Result = 1; +end; + +procedure TForm1.ChangePriority; +begin + case MainPriority of + 0 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_NORMAL); + 1 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_ABOVE_NORMAL); + 2 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_HIGHEST); + 3 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_TIME_CRITICAL); + end; +end; + +procedure TForm1.show_modes; +var + s string; +begin + s = MODEM_CAPTION+" - Ver "+MODEM_VERSION+" - ["+modes_name[Speed[1]]; + if dualchan then s = s+" - "+modes_name[Speed[2]]; + form1.Caption = s+"]"; +end; + +procedure TForm1.show_freq_a; +begin + SpinEdit1.Value = round(rx_freq[1]); + SpinEdit1.Refresh; +end; + +procedure TForm1.show_freq_b; +begin + SpinEdit2.Value = round(rx_freq[2]); + SpinEdit2.Refresh; +end; +*/ +void get_filter_values(UCHAR snd_ch) +{ + //, unsigned short dbpf, +//unsigned short dtxbpf, +//unsigned short dbpftap, +//unsigned short dlpf, +//unsigned short dlpftap) +// speed[snd_ch], bpf[snd_ch], txbpf[snd_ch], bpf_tap[snd_ch], lpf[snd_ch], lpf_tap[snd_ch]); + + switch (speed[snd_ch]) + { + case SPEED_8P4800: + + lpf[snd_ch] = MODEM_8P4800_LPF; + bpf[snd_ch] = MODEM_8P4800_BPF; + txbpf[snd_ch] = MODEM_8P4800_TXBPF; + BPF_tap[snd_ch] = MODEM_8P4800_BPF_TAP; + LPF_tap[snd_ch] = MODEM_8P4800_LPF_TAP; + break; + + case SPEED_MP400: + + lpf[snd_ch] = MODEM_MP400_LPF; + bpf[snd_ch] = MODEM_MP400_BPF; + txbpf[snd_ch] = MODEM_MP400_TXBPF; + BPF_tap[snd_ch] = MODEM_MP400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_MP400_LPF_TAP; + + break; + + + case SPEED_Q4800: + + lpf[snd_ch] = MODEM_Q4800_LPF; + bpf[snd_ch] = MODEM_Q4800_BPF; + txbpf[snd_ch] = MODEM_Q4800_TXBPF; + BPF_tap[snd_ch] = MODEM_Q4800_BPF_TAP; + LPF_tap[snd_ch] = MODEM_Q4800_LPF_TAP; + + break; + + case SPEED_Q3600: + + lpf[snd_ch] = MODEM_Q3600_LPF; + bpf[snd_ch] = MODEM_Q3600_BPF; + txbpf[snd_ch] = MODEM_Q3600_TXBPF; + BPF_tap[snd_ch] = MODEM_Q3600_BPF_TAP; + LPF_tap[snd_ch] = MODEM_Q3600_LPF_TAP; + break; + + case SPEED_Q2400: + + lpf[snd_ch] = MODEM_Q2400_LPF; + bpf[snd_ch] = MODEM_Q2400_BPF; + txbpf[snd_ch] = MODEM_Q2400_TXBPF; + BPF_tap[snd_ch] = MODEM_Q2400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_Q2400_LPF_TAP; + + break; + + case SPEED_DW2400: + case SPEED_AE2400: + + + lpf[snd_ch] = MODEM_DW2400_LPF; + bpf[snd_ch] = MODEM_DW2400_BPF; + txbpf[snd_ch] = MODEM_DW2400_TXBPF; + BPF_tap[snd_ch] = MODEM_DW2400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_DW2400_LPF_TAP; + break; + + case SPEED_P2400: + + lpf[snd_ch] = MODEM_P2400_LPF; + bpf[snd_ch] = MODEM_P2400_BPF; + txbpf[snd_ch] = MODEM_P2400_TXBPF; + BPF_tap[snd_ch] = MODEM_P2400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P2400_LPF_TAP; + break; + + case SPEED_P1200: + + lpf[snd_ch] = MODEM_P1200_LPF; + bpf[snd_ch] = MODEM_P1200_BPF; + txbpf[snd_ch] = MODEM_P1200_TXBPF; + BPF_tap[snd_ch] = MODEM_P1200_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P1200_LPF_TAP; + break; + + case SPEED_P600: + + lpf[snd_ch] = MODEM_P600_LPF; + bpf[snd_ch] = MODEM_P600_BPF; + txbpf[snd_ch] = MODEM_P600_TXBPF; + BPF_tap[snd_ch] = MODEM_P600_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P600_LPF_TAP; + break; + + case SPEED_P300: + + lpf[snd_ch] = MODEM_P300_LPF; + bpf[snd_ch] = MODEM_P300_BPF; + txbpf[snd_ch] = MODEM_P300_TXBPF; + BPF_tap[snd_ch] = MODEM_P300_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P300_LPF_TAP; + break; + + case SPEED_300: + + lpf[snd_ch] = MODEM_300_LPF; + bpf[snd_ch] = MODEM_300_BPF; + txbpf[snd_ch] = MODEM_300_TXBPF; + BPF_tap[snd_ch] = MODEM_300_BPF_TAP; + LPF_tap[snd_ch] = MODEM_300_LPF_TAP; + + break; + + case SPEED_600: + + lpf[snd_ch] = MODEM_600_LPF; + bpf[snd_ch] = MODEM_600_BPF; + txbpf[snd_ch] = MODEM_600_TXBPF; + BPF_tap[snd_ch] = MODEM_600_BPF_TAP; + LPF_tap[snd_ch] = MODEM_600_LPF_TAP; + + break; + + case SPEED_1200: + + lpf[snd_ch] = MODEM_1200_LPF; + bpf[snd_ch] = MODEM_1200_BPF; + txbpf[snd_ch] = MODEM_1200_TXBPF; + BPF_tap[snd_ch] = MODEM_1200_BPF_TAP; + LPF_tap[snd_ch] = MODEM_1200_LPF_TAP; + break; + + case SPEED_2400: + + lpf[snd_ch] = MODEM_2400_LPF; + bpf[snd_ch] = MODEM_2400_BPF; + txbpf[snd_ch] = MODEM_2400_TXBPF; + BPF_tap[snd_ch] = MODEM_2400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_2400_LPF_TAP; + break; + } + +} + + +void init_2400(int snd_ch) +{ + modem_mode[snd_ch] = MODE_FSK; + rx_shift[snd_ch] = 1805; + rx_baudrate[snd_ch] = 2400; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_1200(int snd_ch) +{ + modem_mode[snd_ch] = MODE_FSK; + rx_shift[snd_ch] = 1000; + + if (stdtones) + rx_freq[snd_ch] = 1700; + + rx_baudrate[snd_ch] = 1200; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_600(int snd_ch) +{ + modem_mode[snd_ch] = MODE_FSK; + rx_shift[snd_ch] = 450; + + rx_baudrate[snd_ch] = 600; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_300(int snd_ch) +{ + modem_mode[snd_ch] = MODE_FSK; + rx_shift[snd_ch] = 200; + rx_baudrate[snd_ch] = 300; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_MP400(int snd_ch) +{ + modem_mode[snd_ch] = MODE_MPSK; + rx_shift[snd_ch] = 175 /*sbc*/ * 3; + rx_baudrate[snd_ch] = 100; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + + +void init_8P4800(int snd_ch) +{ + modem_mode[snd_ch] = MODE_8PSK; + if (stdtones) + rx_freq[snd_ch] = 1800; + + rx_shift[snd_ch] = 1600; + rx_baudrate[snd_ch] = 1600; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_AE2400(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_V26; + modem_mode[snd_ch] = MODE_PI4QPSK; + + if (stdtones) + rx_freq[snd_ch] = 1800; + + rx_shift[snd_ch] = 1200; + rx_baudrate[snd_ch] = 1200; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_DW2400(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_V26; + modem_mode[snd_ch] = MODE_QPSK; + + if (stdtones) + rx_freq[snd_ch] = 1800; + + rx_shift[snd_ch] = 1200; + rx_baudrate[snd_ch] = 1200; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_Q4800(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_SM; + modem_mode[snd_ch] = MODE_QPSK; + rx_shift[snd_ch] = 2400; + rx_baudrate[snd_ch] = 2400; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_Q3600(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_SM; + modem_mode[snd_ch] = MODE_QPSK; + rx_shift[snd_ch] = 1800; + rx_baudrate[snd_ch] = 1800; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_Q2400(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_SM; + modem_mode[snd_ch] = MODE_QPSK; + rx_shift[snd_ch] = 1200; + rx_baudrate[snd_ch] = 1200; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_P2400(int snd_ch) +{ + modem_mode[snd_ch] = MODE_BPSK; + rx_shift[snd_ch] = 2400; + rx_baudrate[snd_ch] = 2400; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_P1200(int snd_ch) +{ + modem_mode[snd_ch] = MODE_BPSK; + rx_shift[snd_ch] = 1200; + rx_baudrate[snd_ch] = 1200; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_P600(int snd_ch) +{ + modem_mode[snd_ch] = MODE_BPSK; + rx_shift[snd_ch] = 600; + rx_baudrate[snd_ch] = 600; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_P300(int snd_ch) +{ + modem_mode[snd_ch] = MODE_BPSK; + rx_shift[snd_ch] = 300; + rx_baudrate[snd_ch] = 300; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_ARDOP(int snd_ch) +{ + modem_mode[snd_ch] = MODE_ARDOP; + rx_shift[snd_ch] = 500; + rx_freq[snd_ch] = 1500; + rx_baudrate[snd_ch] = 500; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + + +void init_speed(int snd_ch); + +void set_speed(int snd_ch, int Modem) +{ + speed[snd_ch] = Modem; + + init_speed(snd_ch); + +} + +void init_speed(int snd_ch) +{ + int low, high; + + /* + + if (BPF[snd_ch]>round(rx_samplerate/2) then BPF[snd_ch] = round(rx_samplerate/2); + if TXBPF[snd_ch]>round(rx_samplerate/2) then TXBPF[snd_ch] = round(rx_samplerate/2); + if LPF[snd_ch]>round(rx_samplerate/2) then LPF[snd_ch] = round(rx_samplerate/2); + if BPF[snd_ch]<1 then BPF[snd_ch] = 1; + if TXBPF[snd_ch]<1 then TXBPF[snd_ch] = 1; + if LPF[snd_ch]<1 then LPF[snd_ch] = 1; + if TXDelay[snd_ch]<1 then TXDelay[snd_ch] = 1; + if TXTail[snd_ch]<1 then TXTail[snd_ch] = 1; + if BPF_tap[snd_ch]>1024 then BPF_tap[snd_ch] = 1024; + if LPF_tap[snd_ch]>512 then LPF_tap[snd_ch] = 512; + if BPF_tap[snd_ch]<8 then BPF_tap[snd_ch] = 8; + if LPF_tap[snd_ch]<8 then LPF_tap[snd_ch] = 8; + if not (RCVR[snd_ch] in [0..8]) then RCVR[snd_ch] = 0; + if not (rcvr_offset[snd_ch] in [0..100]) then rcvr_offset[snd_ch] = 30; + if not (speed[snd_ch] in [0..modes_count]) then speed[snd_ch] = SPEED_300; +*/ + switch (speed[snd_ch]) + { + case SPEED_8P4800: + init_8P4800(snd_ch); + break; + + case SPEED_AE2400: + init_AE2400(snd_ch); + break; + + case SPEED_DW2400: + init_DW2400(snd_ch); + break; + + case SPEED_MP400: + init_MP400(snd_ch); + break; + case SPEED_Q4800: + init_Q4800(snd_ch); + break; + + case SPEED_Q3600: + init_Q3600(snd_ch); + break; + + case SPEED_Q2400: + init_Q2400(snd_ch); + break; + + case SPEED_P2400: + init_P2400(snd_ch); + break; + + case SPEED_P1200: + init_P1200(snd_ch); + break; + + case SPEED_P600: + init_P600(snd_ch); + break; + + case SPEED_P300: + init_P300(snd_ch); + break; + + case SPEED_300: + + init_300(snd_ch); + break; + + case SPEED_600: + + init_600(snd_ch); + break; + + case SPEED_1200: + + init_1200(snd_ch); + break; + + case SPEED_2400: + + init_2400(snd_ch); + break; + + case SPEED_ARDOP: + + init_ARDOP(snd_ch); + break; + } + + //QPSK_SM: begin move(#0#1#2#3, tx[0], 4); move(#0#32#64#96, rx[0], 4); end; + //QPSK_V26: begin move(#2#3#1#0, tx[0], 4); move(#96#64#0#32, rx[0], 4); end; + + + if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK) + { + switch (qpsk_set[snd_ch].mode) + { + case QPSK_SM: + + memcpy(&qpsk_set[snd_ch].tx[0], "\0\1\2\3", 4); + memcpy(&qpsk_set[snd_ch].rx[0], "\x0\x20\x40\x60", 4); + break; + + case QPSK_V26: + + memcpy(&qpsk_set[snd_ch].tx[0], "\2\3\1\0", 4); + memcpy(&qpsk_set[snd_ch].rx[0], "\x60\x40\0\x20", 4); + break; + } + } + + tx_shift[snd_ch] = rx_shift[snd_ch]; + tx_baudrate[snd_ch] = rx_baudrate[snd_ch]; + low = roundf(rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch] + 1); + high = roundf(RX_Samplerate / 2 - (rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch])); + + if (rx_freq[snd_ch] - low < 0) rx_freq[snd_ch] = low; + if (high - rx_freq[snd_ch] < 0) rx_freq[snd_ch] = high; + + tx_freq[snd_ch] = rx_freq[snd_ch]; + + make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); + make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]); + make_core_INTR(snd_ch); + make_core_LPF(snd_ch, lpf[snd_ch]); + + /* + for i = 0 to 16 do + for j = 0 to nr_emph do with DET[j,i] do + begin + minamp[snd_ch] = 0; + maxamp[snd_ch] = 0; + ones[snd_ch] = 0; + zeros[snd_ch] = 0; + sample_cnt[snd_ch] = 0; + bit_cnt[snd_ch] = 0; + bit_osc[snd_ch] = 0; + frame_status[snd_ch] = FRAME_WAIT; + end; + form1.show_modes; + form1.show_freq_a; + form1.show_freq_b; + */ + wf_pointer(soundChannel[snd_ch]); + + +} + +/* +procedure TForm1.show_combobox; +var + i word; +begin + for i = 0 to length(modes_name)-1 do + begin + ComboBox1.Items.Add(modes_name[i]); + ComboBox2.Items.Add(modes_name[i]); + end; + ComboBox1.ItemIndex = ComboBox1.Items.IndexOf(modes_name[Speed[1]]); + ComboBox2.ItemIndex = ComboBox2.Items.IndexOf(modes_name[Speed[2]]); +end; + +function TForm1.get_idx_by_name(name string) word; +var + i word; + found boolean; +begin + i = 0; + found = FALSE; + result = 0; + repeat + if name = modes_name[i] then + begin + found = TRUE; + result = i; + end + else inc(i); + until found or (i = length(modes_name)); +end; + +procedure TForm1.ReadIni; +var + snd_ch byte; +begin + TelIni = TIniFile.Create(cur_dir+"soundmodem.ini"); + with TelIni do + begin + UTC_Time = ReadBool("Init","UTCTime",FALSE); + MainPriority = ReadInteger("Init","Priority",2); + nr_monitor_lines = ReadInteger("Init","NRMonitorLines",500); + ptt = ReadString("Init","PTT","NONE"); + stop_wf = ReadBool("Init","StopWF",FALSE); + raduga = ReadBool("Init","DispMode",DISP_MONO); + stat_log = ReadBool("Init","StatLog",FALSE); + SND_RX_DEVICE = ReadInteger("Init","SndRXDevice",0); + SND_TX_DEVICE = ReadInteger("Init","SndTXDevice",0); + snd_rx_device_name = ReadString("Init","SndRXDeviceName",""); + snd_tx_device_name = ReadString("Init","SndTXDeviceName",""); + RX_SR = ReadInteger("Init","RXSampleRate",11025); + TX_SR = ReadInteger("Init","TXSampleRate",11025); + RX_PPM = ReadInteger("Init","RX_corr_PPM",0); + TX_PPM = ReadInteger("Init","TX_corr_PPM",0); + tx_bufcount = ReadInteger("Init","TXBufNumber",32); + rx_bufcount = ReadInteger("Init","RXBufNumber",32); + DebugMode = ReadInteger("Init","DisableUnit",0); + TX_rotate = ReadBool("Init","TXRotate",FALSE); + DualChan = ReadBool("Init","DualChan",FALSE); + DualPTT = ReadBool("Init","DualPTT",TRUE); + SCO = ReadBool("Init","SCO",FALSE); + stdtones = ReadBool("Init","UseStandardTones",TRUE); + // Channel A settings + maxframe[1] = ReadInteger("AX25_A","Maxframe",3); + fracks[1] = ReadInteger("AX25_A","Retries",15); + frack_time[1] = ReadInteger("AX25_A","FrackTime",5); + idletime[1] = ReadInteger("AX25_A","IdleTime",180); + slottime[1] = ReadInteger("AX25_A","SlotTime",100); + persist[1] = ReadInteger("AX25_A","Persist",128); + resptime[1] = ReadInteger("AX25_A","RespTime",1500); + TXFrmMode[1] = ReadInteger("AX25_A","TXFrmMode",1); + max_frame_collector[1] = ReadInteger("AX25_A","FrameCollector",6); + exclude_callsigns[1] = ReadString("AX25_A","ExcludeCallsigns",""); + exclude_APRS_frm[1] = ReadString("AX25_A","ExcludeAPRSFrmType",""); + KISS_opt[1] = ReadBool("AX25_A","KISSOptimization",FALSE); + dyn_frack[1] = ReadBool("AX25_A","DynamicFrack",FALSE); + recovery[1] = ReadInteger("AX25_A","BitRecovery",0); + NonAX25[1] = ReadBool("AX25_A","NonAX25Frm",FALSE); + MEMRecovery[1] = ReadInteger("AX25_A","MEMRecovery",200); + IPOLL[1] = ReadInteger("AX25_A","IPOLL",80); + MyDigiCall[1] = ReadString("AX25_A","MyDigiCall",""); + tx_hitoneraisedb[1] = ReadInteger("AX25_A","HiToneRaise",0); + // Channel B settings + maxframe[2] = ReadInteger("AX25_B","Maxframe",3); + fracks[2] = ReadInteger("AX25_B","Retries",15); + frack_time[2] = ReadInteger("AX25_B","FrackTime",5); + idletime[2] = ReadInteger("AX25_B","IdleTime",180); + slottime[2] = ReadInteger("AX25_B","SlotTime",100); + persist[2] = ReadInteger("AX25_B","Persist",128); + resptime[2] = ReadInteger("AX25_B","RespTime",1500); + TXFrmMode[2] = ReadInteger("AX25_B","TXFrmMode",1); + max_frame_collector[2] = ReadInteger("AX25_B","FrameCollector",6); + exclude_callsigns[2] = ReadString("AX25_B","ExcludeCallsigns",""); + exclude_APRS_frm[2] = ReadString("AX25_B","ExcludeAPRSFrmType",""); + KISS_opt[2] = ReadBool("AX25_B","KISSOptimization",FALSE); + dyn_frack[2] = ReadBool("AX25_B","DynamicFrack",FALSE); + recovery[2] = ReadInteger("AX25_B","BitRecovery",0); + NonAX25[2] = ReadBool("AX25_B","NonAX25Frm",FALSE); + MEMRecovery[2] = ReadInteger("AX25_B","MEMRecovery",200); + IPOLL[2] = ReadInteger("AX25_B","IPOLL",80); + MyDigiCall[2] = ReadString("AX25_B","MyDigiCall",""); + tx_hitoneraisedb[2] = ReadInteger("AX25_B","HiToneRaise",0); + // Modem settings + pkt_raw_min_len = ReadInteger("Modem","RawPktMinLen",17); + swap_ptt = ReadBool("Modem","SwapPTTPins",FALSE); + inv_ptt = ReadBool("Modem","InvPTTPins",FALSE); + Emph_all[1] = ReadBool("Modem","PreEmphasisAll1",TRUE); + Emph_all[2] = ReadBool("Modem","PreEmphasisAll2",TRUE); + emph_db[1] = ReadInteger("Modem","PreEmphasisDB1",0); + emph_db[2] = ReadInteger("Modem","PreEmphasisDB2",0); + txbpf[1] = ReadInteger("Modem","TXBPF1",500); + txbpf[2] = ReadInteger("Modem","TXBPF2",500); + bpf[1] = ReadInteger("Modem","BPF1",500); + bpf[2] = ReadInteger("Modem","BPF2",500); + lpf[1] = ReadInteger("Modem","LPF1",150); + lpf[2] = ReadInteger("Modem","LPF2",150); + BPF_tap[1] = ReadInteger("Modem","BPFTap1",256); + BPF_tap[2] = ReadInteger("Modem","BPFTap2",256); + LPF_tap[1] = ReadInteger("Modem","LPFTap1",128); + LPF_tap[2] = ReadInteger("Modem","LPFTap2",128); + DCD_threshold = ReadInteger("Modem","DCDThreshold",32); + rx_freq[1] = ReadFloat("Modem","RXFreq1",1700); + rx_freq[2] = ReadFloat("Modem","RXFreq2",1700); + CheckBox1.Checked = ReadBool("Modem","HoldPnt",FALSE); + BIT_AFC = ReadInteger("Modem","AFC",32); + txdelay[1] = ReadInteger("Modem","TxDelay1",250); + txdelay[2] = ReadInteger("Modem","TxDelay2",250); + txtail[1] = ReadInteger("Modem","TxTail1",50); + txtail[2] = ReadInteger("Modem","TxTail2",50); + diddles = ReadInteger("Modem","Diddles",0); + RCVR[1] = ReadInteger("Modem","NRRcvrPairs1",0); + RCVR[2] = ReadInteger("Modem","NRRcvrPairs2",0); + rcvr_offset[1] = ReadInteger("Modem","RcvrShift1",30); + rcvr_offset[2] = ReadInteger("Modem","RcvrShift2",30); + speed[1] = ReadInteger("Modem","ModemType1",SPEED_1200); + speed[2] = ReadInteger("Modem","ModemType2",SPEED_1200); + modem_def[1] = ReadBool("Modem","Default1",TRUE); + modem_def[2] = ReadBool("Modem","Default2",TRUE); + AGWServ = ReadBool("AGWHost","Server",TRUE); + AGWPort = ReadInteger("AGWHost","Port",8000); + KISSServ = ReadBool("KISS","Server",FALSE); + KISSPort = ReadInteger("KISS","Port",8100); + Form1.Top = ReadInteger("Window","Top",0); + Form1.Left = ReadInteger("Window","Left",0); + Form1.Height = ReadInteger("Window","Height",656); + Form1.Width = ReadInteger("Window","Width",764); + MinOnStart = ReadBool("Window","MinimizedOnStartup",FALSE); + Firstwaterfall1.checked = ReadBool("Window","Waterfall1",TRUE); + Secondwaterfall1.checked = ReadBool("Window","Waterfall2",FALSE); + Statustable1.checked = ReadBool("Window","StatTable",TRUE); + Monitor1.checked = ReadBool("Window","Monitor",TRUE); + RXRichEdit1.Font.Size = ReadInteger("Font","Size",RXRichEdit1.Font.Size); + RXRichEdit1.Font.Name = ReadString("Font","Name",RXRichEdit1.Font.Name); + end; + TelIni.Free; + newAGWPort = AGWPort; + newAGWServ = AGWServ; + newKISSPort = KISSPort; + newKISSServ = KISSServ; + + RX_SampleRate = RX_SR+RX_SR*0.000001*RX_PPM; + TX_SampleRate = TX_SR+TX_SR*0.000001*TX_PPM; + + panels[4] = Monitor1.Checked; + panels[3] = Statustable1.Checked; + panels[2] = Firstwaterfall1.Checked; + panels[1] = Secondwaterfall1.Checked; + + if tx_bufcount>255 then tx_bufcount = 255; + if tx_bufcount<2 then tx_bufcount = 2; + if rx_bufcount>255 then rx_bufcount = 255; + if rx_bufcount<2 then rx_bufcount = 2; + + if not (diddles in [0..2]) then diddles = 0; + + if nr_monitor_lines>65535 then nr_monitor_lines = 65535; + if nr_monitor_lines<10 then nr_monitor_lines = 10; + + if not (MainPriority in [0..3]) then MainPriority = 2; + + for snd_ch = 1 to 2 do + begin + + tx_hitoneraise[snd_ch] = power(10,-abs(tx_hitoneraisedb[snd_ch])/20); + + if IPOLL[snd_ch]<0 then IPOLL[snd_ch] = 0; + if IPOLL[snd_ch]>65535 then IPOLL[snd_ch] = 65535; + + if MEMRecovery[snd_ch]<1 then MEMRecovery[snd_ch] = 1; + if MEMRecovery[snd_ch]>65535 then MEMRecovery[snd_ch] = 65535; + + get_exclude_list(AnsiUpperCase(MyDigiCall[snd_ch]),list_digi_callsigns[snd_ch]); + get_exclude_list(AnsiUpperCase(exclude_callsigns[snd_ch]),list_exclude_callsigns[snd_ch]); + get_exclude_frm(exclude_APRS_frm[snd_ch],list_exclude_APRS_frm[snd_ch]); + + if resptime[snd_ch]<0 then resptime[snd_ch] = 0; + if resptime[snd_ch]>65535 then resptime[snd_ch] = 65535; + if persist[snd_ch]>255 then persist[snd_ch] = 255; + if persist[snd_ch]<32 then persist[snd_ch] = 32; + if fracks[snd_ch]<1 then fracks[snd_ch] = 1; + if frack_time[snd_ch]<1 then frack_time[snd_ch] = 1; + if idletime[snd_ch]0 then + for k = 0 to numdevs-1 do + begin + waveOutGetDevCaps(k,@DevOutCaps,sizeof(DevOutCaps)); + TXDevList.Add(DevOutCaps.szpname); + end; + numdevs = WaveInGetNumDevs; + if numdevs>0 then + for k = 0 to numdevs-1 do + begin + waveInGetDevCaps(k,@DevInCaps,sizeof(DevInCaps)); + RXDevList.Add(DevInCaps.szpname); + end; + // TX Dev + if (snd_tx_device<0) or (snd_tx_device> = TXDevList.Count) then snd_tx_device = 0; + if TXDevList.Count>0 then + if TXDevList.Strings[snd_tx_device]<>snd_tx_device_name then + begin + i = TXDevList.IndexOf(snd_tx_device_name); + if i> = 0 then snd_tx_device = i else snd_tx_device_name = TXDevList.Strings[snd_tx_device]; + end; + // RX Dev + if (snd_rx_device<0) or (snd_rx_device> = RXDevList.Count) then snd_rx_device = 0; + if RXDevList.Count>0 then + if RXDevList.Strings[snd_rx_device]<>snd_rx_device_name then + begin + i = RXDevList.IndexOf(snd_rx_device_name); + if i> = 0 then snd_rx_device = i else snd_rx_device_name = RXDevList.Strings[snd_rx_device]; + end; + RXDevList.Free; + TXDevList.Free; +end; + +procedure TForm1.startrx; +var + OpenResult MMRESULT; + Loop integer; + ErrorText string; +Begin + RX_device = TRUE; + RXBufferLength = rx_bufsize * Channels * (BitsPerSample div 8); + with WaveFormat do + begin + wFormatTag = WAVE_FORMAT_PCM; + nChannels = Channels; + nSamplesPerSec = RX_SR; + nAvgBytesPerSec = RX_SR * Channels * (BitsPerSample div 8); + nBlockAlign = Channels * (BitsPerSample div 8); + wBitsPerSample = BitsPerSample; + cbSize = 0; + end; + OpenResult = waveInOpen (@WaveInHandle,SND_RX_DEVICE,@WaveFormat,integer(Self.Handle),0,CALLBACK_WINDOW); + if OpenResult = MMSYSERR_NOERROR then + begin + for Loop = 1 to rx_bufcount do + begin + GetMem(RX_pbuf[Loop], RXBufferLength); + RX_header[Loop].lpData = RX_pbuf[Loop]; + RX_header[Loop].dwBufferLength = RXBufferLength; + RX_header[Loop].dwUser = Loop; + RX_header[Loop].dwFlags = 0; + RX_header[Loop].dwLoops = 0; + OpenResult = WaveInPrepareHeader(WaveInhandle, @RX_header[Loop], sizeof(TWaveHdr)); + if OpenResult = MMSYSERR_NOERROR then WaveInAddBuffer(WaveInHandle, @RX_header[Loop], sizeof(TWaveHdr)) + else + begin + case OpenResult of + MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; + MMSYSERR_NODRIVER ErrorText = "no device driver present"; + MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; + else ErrorText = "unknown error"; + end; + MessageDlg(format("Error adding buffer %d device (%s)",[Loop, ErrorText]), mtError, [mbOk], 0); + end; + end; + WaveInStart(WaveInHandle); + end + else + begin + case OpenResult of + MMSYSERR_ERROR ErrorText = "unspecified error"; + MMSYSERR_BADDEVICEID ErrorText = "device ID out of range"; + MMSYSERR_NOTENABLED ErrorText = "driver failed enable"; + MMSYSERR_ALLOCATED ErrorText = "device already allocated"; + MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; + MMSYSERR_NODRIVER ErrorText = "no device driver present"; + MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; + MMSYSERR_NOTSUPPORTED ErrorText = "function isn""t supported"; + MMSYSERR_BADERRNUM ErrorText = "error value out of range"; + MMSYSERR_INVALFLAG ErrorText = "invalid flag passed"; + MMSYSERR_INVALPARAM ErrorText = "invalid parameter passed"; + MMSYSERR_HANDLEBUSY ErrorText = "handle being used simultaneously on another thread (eg callback)"; + MMSYSERR_INVALIDALIAS ErrorText = "specified alias not found"; + MMSYSERR_BADDB ErrorText = "bad registry database"; + MMSYSERR_KEYNOTFOUND ErrorText = "registry key not found"; + MMSYSERR_READERROR ErrorText = "registry read error"; + MMSYSERR_WRITEERROR ErrorText = "registry write error"; + MMSYSERR_DELETEERROR ErrorText = "registry delete error"; + MMSYSERR_VALNOTFOUND ErrorText = "registry value not found"; + MMSYSERR_NODRIVERCB ErrorText = "driver does not call DriverCallback"; + else ErrorText = "unknown error"; + end; + MessageDlg(format("Error opening wave input device (%s)",[ErrorText]), mtError, [mbOk], 0); + RX_device = FALSE; + end; +end; + +procedure TForm1.stoprx; +var + Loop integer; +begin + if not RX_device then exit; + WaveInStop(WaveInHandle); + WaveInReset(WaveInHandle); + for Loop = 1 to rx_bufcount do + WaveInUnPrepareHeader(WaveInHandle, @RX_header[Loop], sizeof(TWaveHdr)); + WaveInClose(WaveInHandle); + for Loop = 1 to rx_bufcount do + begin + if RX_pbuf[Loop]<>nil then + begin + FreeMem(RX_pbuf[Loop]); + RX_pbuf[Loop] = nil; + end; + end; + RX_device = FALSE; +end; + +procedure TForm1.make_wave_buf(snd_ch byte; buf PChar); +const + amplitude = 22000; +var + i word; +begin + modulator(snd_ch,audio_buf[snd_ch],tx_bufsize); + if tx_status[snd_ch] = TX_NO_DATA then buf_status[snd_ch] = BUF_EMPTY; + for i = 0 to tx_bufsize-1 do + begin + case snd_ch of + 1 + begin + // left channel + PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]); + Inc(PSmallInt(Buf)); + // right channel + if SCO then PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]) else PSmallInt(buf)^ = 0; + Inc(PSmallInt(Buf)); + end; + 2 + begin + // left channel + if SCO then PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]) else PSmallInt(buf)^ = 0; + Inc(PSmallInt(Buf)); + // right channel + PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]); + Inc(PSmallInt(Buf)); + end; + end; + end; +end; + +procedure TForm1.starttx(snd_ch byte); +var + OpenResult MMRESULT; + Loop integer; + ErrorText string; + BufferLength longint; +Begin + if snd_status[snd_ch]<>SND_IDLE then exit; + BufferLength = tx_bufsize * Channels * (BitsPerSample div 8); + with WaveFormat do + begin + wFormatTag = WAVE_FORMAT_PCM; + nChannels = Channels; + nSamplesPerSec = TX_SR; + nAvgBytesPerSec = TX_SR * Channels * (BitsPerSample div 8); + nBlockAlign = Channels * (BitsPerSample div 8); + wBitsPerSample = BitsPerSample; + cbSize = 0; + end; + OpenResult = WaveOutOpen (@WaveOutHandle[snd_ch],SND_TX_DEVICE,@WaveFormat,integer(Self.Handle),0,CALLBACK_WINDOW); + if OpenResult = MMSYSERR_NOERROR then + begin + snd_status[snd_ch] = SND_TX; + buf_status[snd_ch] = BUF_FULL; + tx_status[snd_ch] = TX_SILENCE; + tx_buf_num[snd_ch] = 0; + tx_buf_num1[snd_ch] = 0; + for Loop = 1 to tx_bufcount do + begin + GetMem(TX_pbuf[snd_ch][Loop], BufferLength); + TX_header[snd_ch][Loop].lpData = TX_pbuf[snd_ch][Loop]; + TX_header[snd_ch][Loop].dwBufferLength = BufferLength; + TX_header[snd_ch][Loop].dwUser = 0; + TX_header[snd_ch][Loop].dwFlags = 0; + TX_header[snd_ch][Loop].dwLoops = 0; + OpenResult = WaveOutPrepareHeader(WaveOuthandle[snd_ch], @TX_header[snd_ch][Loop], sizeof(TWaveHdr)); + if OpenResult = MMSYSERR_NOERROR then + begin + // Заполнить буфер на передачу + if buf_status[snd_ch] = BUF_FULL then + begin + make_wave_buf(snd_ch,TX_pbuf[snd_ch][Loop]); + WaveOutWrite(WaveOutHandle[snd_ch],@TX_header[snd_ch][Loop],sizeof(TWaveHdr)); + inc(tx_buf_num1[snd_ch]); + end; + end + else + begin + case OpenResult of + MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; + MMSYSERR_NODRIVER ErrorText = "no device driver present"; + MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; + else ErrorText = "unknown error"; + end; + MessageDlg(format("Error adding buffer %d device (%s)",[Loop, ErrorText]), mtError, [mbOk], 0); + end; + end; + end + else + begin + case OpenResult of + MMSYSERR_ERROR ErrorText = "unspecified error"; + MMSYSERR_BADDEVICEID ErrorText = "device ID out of range"; + MMSYSERR_NOTENABLED ErrorText = "driver failed enable"; + MMSYSERR_ALLOCATED ErrorText = "device already allocated"; + MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; + MMSYSERR_NODRIVER ErrorText = "no device driver present"; + MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; + MMSYSERR_NOTSUPPORTED ErrorText = "function isn""t supported"; + MMSYSERR_BADERRNUM ErrorText = "error value out of range"; + MMSYSERR_INVALFLAG ErrorText = "invalid flag passed"; + MMSYSERR_INVALPARAM ErrorText = "invalid parameter passed"; + MMSYSERR_HANDLEBUSY ErrorText = "handle being used simultaneously on another thread (eg callback)"; + MMSYSERR_INVALIDALIAS ErrorText = "specified alias not found"; + MMSYSERR_BADDB ErrorText = "bad registry database"; + MMSYSERR_KEYNOTFOUND ErrorText = "registry key not found"; + MMSYSERR_READERROR ErrorText = "registry read error"; + MMSYSERR_WRITEERROR ErrorText = "registry write error"; + MMSYSERR_DELETEERROR ErrorText = "registry delete error"; + MMSYSERR_VALNOTFOUND ErrorText = "registry value not found"; + MMSYSERR_NODRIVERCB ErrorText = "driver does not call DriverCallback"; + else ErrorText = "unknown error"; + end; + MessageDlg(format("Error opening wave output device (%s)",[ErrorText]), mtError, [mbOk], 0); + end; +end; + +procedure TForm1.stoptx(snd_ch byte); +var + Loop integer; +begin + if snd_status[snd_ch]<>SND_TX then exit; + WaveOutReset(WaveOutHandle[snd_ch]); + for Loop = 1 to tx_bufcount do + WaveOutUnPrepareHeader(WaveOutHandle[snd_ch], @TX_header[snd_ch][Loop], sizeof(TWaveHdr)); + WaveOutClose(WaveOutHandle[snd_ch]); + for Loop = 1 to tx_bufcount do + begin + if TX_pbuf[snd_ch][Loop]<>nil then + begin + FreeMem(TX_pbuf[snd_ch][Loop]); + TX_pbuf[snd_ch][Loop] = nil; + end; + end; + WaveOutHandle[snd_ch] = 0; + snd_status[snd_ch] = SND_IDLE; +end; + +procedure show_grid_title; +const + title array [0..10] of string = ("MyCall","DestCall","Status","Sent pkts","Sent bytes","Rcvd pkts","Rcvd bytes","Rcvd FC","CPS TX","CPS RX","Direction"); +var + i byte; +begin + for i = 0 to 10 do Form1.StringGrid1.Cells[i,0] = title[i]; +end; +*/ +/* + +procedure disp1(src1,src2 array of single); +var + i,n word; + k,k1,amp1,amp2,amp3,amp4 single; + bm TBitMap; +begin + bm = TBitMap.Create; + bm.pixelformat = pf32bit; + //bm.pixelformat = pf24bit; + bm.Width = Form1.PaintBox2.Width; + bm.Height = Form1.PaintBox2.Height; + amp1 = 0; + amp3 = 0; + //k = 0.20; + k = 50000; + k1 = 0; + //k = 1000; + //k = 0.00001; + bm.Canvas.MoveTo(0,50); + bm.Canvas.LineTo(512,50); + n = 0; + for i = 0 to RX_Bufsize-1 do + begin + begin + amp2 = src1[i]; + amp4 = src2[i]; + bm.Canvas.Pen.Color = clRed; + bm.Canvas.MoveTo(n,50-round(amp1*k1)); + bm.Canvas.LineTo(n+1,50-round(amp2*k1)); + bm.Canvas.Pen.Color = clBlue; + bm.Canvas.MoveTo(n,50-round(amp3*k)); + bm.Canvas.LineTo(n+1,50-round(amp4*k)); + bm.Canvas.Pen.Color = clBlack; + inc(n); + amp1 = amp2; + amp3 = amp4; + end; + end; + Form1.PaintBox2.Canvas.Draw(0,0,bm); + bm.Free; +end; +*/ + +/* + +procedure TForm1.wf_pointer(snd_ch byte); +var + x single; + x1,x2,y,k,pos1,pos2,pos3 word; +begin + k = 24; + x = FFTSize/RX_SampleRate; + pos1 = round((rx_freq[snd_ch]-0.5*rx_shift[snd_ch])*x)-5; + pos2 = round((rx_freq[snd_ch]+0.5*rx_shift[snd_ch])*x)-5; + pos3 = round(rx_freq[snd_ch]*x); + x1 = pos1+5; + x2 = pos2+5; + y = k+5; + with bm3.Canvas do + begin + Draw(0,20,bm[snd_ch]); + Pen.Color = clWhite; + Brush.Color = clRed; + Polygon([Point(x1+3,y),Point(x1,y-7),Point(x1-3,y),Point(x2+3,y),Point(x2,y-7),Point(x2-3,y)]); + Brush.Color = clBlue; + Polygon([Point(x1+3,y),Point(x1,y+7),Point(x1-3,y),Point(x2+3,y),Point(x2,y+7),Point(x2-3,y)]); + Polyline([Point(pos3,k+1),Point(pos3,k+9)]); + Pen.Color = clBlack; + end; + case snd_ch of + 1 PaintBox1.Canvas.Draw(0,0,bm3); + 2 PaintBox3.Canvas.Draw(0,0,bm3); + end; +end; + +procedure TForm1.wf_Scale; +var + k single; + max_freq,x,i word; +begin + max_freq = round(RX_SampleRate*0.005); + k = 100*FFTSize/RX_SampleRate; + with bm1.Canvas do + begin + Brush.Color = clBlack; + FillRect(ClipRect); + Pen.Color = clWhite; + Font.Color = clWhite; + Font.Size = 8; + for i = 0 to max_freq do + begin + x = round(k*i); + if x<1025 then + begin + if (i mod 5) = 0 then + PolyLine([Point(x,20),Point(x,13)]) + else + PolyLine([Point(x,20),Point(x,16)]); + if (i mod 10) = 0 then TextOut(x-12,1,inttostr(i*100)); + end; + end; + Pen.Color = clBlack; + end; + bm3.Canvas.Draw(0,0,bm1); +end; +*/ + +void chk_snd_buf(float * buf, int len) +{ + word i; + boolean good; + single prev_amp; + + if (len < 2) + return; + + good = FALSE; + i = 1; + prev_amp = buf[0]; + do + { + if (buf[i++] != prev_amp) + good = TRUE; + + } while (good == FALSE && i < len); + + // Make noise + if (!good) + for (i = 0; i < len; i++) + buf[i] = rand(); +} + +#ifdef WIN32 + +typedef void *HANDLE; +typedef unsigned long DWORD; + +#define WINAPI __stdcall +__declspec(dllimport) +DWORD +WINAPI +WaitForSingleObject( + __in HANDLE hHandle, + __in DWORD dwMilliseconds +); + + + + +#define pthread_t uintptr_t + +uintptr_t _beginthread(void(__cdecl *start_address)(void *), unsigned stack_size, void *arglist); +#else + +#include + +pthread_t _beginthread(void(*start_address)(void *), unsigned stack_size, void * arglist) +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*)arglist) != 0) + perror("New Thread"); + + return thread; +} + +#endif + +void runModemthread(void * param); + +void runModems() +{ + int snd_ch, res; + pthread_t thread[4] = { 0,0,0,0 }; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + if (soundChannel[snd_ch] == 0) // Unused channed + continue; + + if (modem_mode[snd_ch] == MODE_ARDOP) + continue; // Processed above + + // do we need to do this again ?? +// make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); + + if (multiCore) // Run modems in separate threads + thread[snd_ch] = _beginthread(runModemthread, 0, (void *)(size_t)snd_ch); + else + runModemthread((void *)(size_t)snd_ch); + } + + if (multiCore) + { +#ifdef WIN32 + if (thread[0]) WaitForSingleObject(&thread[0], 2000); + if (thread[1]) WaitForSingleObject(&thread[1], 2000); + if (thread[2]) WaitForSingleObject(&thread[2], 2000); + if (thread[3]) WaitForSingleObject(&thread[3], 2000); +#else + if (thread[0]) pthread_join(thread[0], &res); + if (thread[1]) pthread_join(thread[1], &res); + if (thread[2]) pthread_join(thread[2], &res); + if (thread[3]) pthread_join(thread[3], &res); +#endif + } +} + +Byte rcvr_idx; + +void runModemthread(void * param) +{ + int snd_ch = (int)(size_t)param; + + // I want to run lowest to highest to simplify my display + + int offset = -(RCVR[snd_ch] * rcvr_offset[snd_ch]); // lowest + int lastrx = RCVR[snd_ch] * 2; + + if (soundChannel[snd_ch] == 0) // Unused channed + return; + + for (rcvr_idx = 0; rcvr_idx <= lastrx; rcvr_idx++) + { + active_rx_freq[snd_ch] = rxOffset + chanOffset[snd_ch] + rx_freq[snd_ch] + offset; + offset += rcvr_offset[snd_ch]; + + Demodulator(snd_ch, rcvr_idx, src_buf[modemtoSoundLR[snd_ch]], rcvr_idx == lastrx, offset == 0); + } +} + +// I think this processes a buffer of samples + +void BufferFull(short * Samples, int nSamples) // These are Stereo Samples +{ + word i, i1; + Byte snd_ch, rcvr_idx; + int buf_offset; + int Needed; + short * data1; + short * data2 = 0; + + // if UDP server active send as UDP Datagram + + if (UDPServ) // Extract just left + { + short Buff[1024]; + + i1 = 0; + + for (i = 0; i < rx_bufsize; i++) + { + Buff[i] = Samples[i1]; + i1 += 2; + } + + sendSamplestoUDP(Buff, 512, TXPort); + } + + // Do RSID processing (can we also use this for waterfall?? + + RSIDProcessSamples(Samples, nSamples); + + // Do FFT on every 4th buffer (2048 samples) + + // if in Satellite Mode look for a Tuning signal + +// if (SatelliteMode) +// { +// doTuning(Samples, nSamples); +// } + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + if (soundChannel[snd_ch] == 0) // Unused channed + continue; + + if (pnt_change[snd_ch]) + { + make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); + make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]); + pnt_change[snd_ch] = FALSE; + } + + } + + // I don't think we should process RX if either is sending + + if (snd_status[0] != SND_TX && snd_status[1] != SND_TX && snd_status[2] != SND_TX && snd_status[3] != SND_TX) + { + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + if (soundChannel[snd_ch] == 0) // Unused channed + continue; + + if (modem_mode[snd_ch] == MODE_ARDOP) + { + short ardopbuff[1200]; + i1 = 0; + + for (i = 0; i < rx_bufsize; i++) + { + ardopbuff[i] = Samples[i1]; + i1++; + i1++; + } + + ARDOPProcessNewSamples(ardopbuff, nSamples); + } + } + + // extract mono samples from data. + + data1 = Samples; + + i1 = 0; + + // src_buf[0] is left data,. src_buf[1] right + + // We could skip extracting other channel if only one in use - is it worth it?? + + if (UsingBothChannels) + { + for (i = 0; i < rx_bufsize; i++) + { + src_buf[0][i] = data1[i1]; + i1++; + src_buf[1][i] = data1[i1]; + i1++; + } + } + else if (UsingRight) + { + // Extract just right + + i1 = 1; + + for (i = 0; i < rx_bufsize; i++) + { + src_buf[1][i] = data1[i1]; + i1 += 2; + } + } + else + { + // Extract just left + + for (i = 0; i < rx_bufsize; i++) + { + src_buf[0][i] = data1[i1]; + i1 += 2; + } + } + + // Run modems before waterfall so fft buffer has values from before sync was detected + + runModems(); + + // Do whichever waterfall is needed + + // We need to run the waterfall FFT for the frequency guessing to work + + int FirstWaterfallChan = 0; + short * ptr1 = &fft_buf[0][fftCount]; + short * ptr2 = &fft_buf[1][fftCount]; + + int remainingSamples = rx_bufsize; + + if (UsingLeft == 0) + { + FirstWaterfallChan = 1; + data1++; // to Right Samples + } + + if (UsingBothChannels) // Second is always Right + data2 = &Samples[1]; // to Right Samples + + + // FFT size isn't necessarily a multiple of rx_bufsize, so this is a bit more complicated + // Save FFTSize samples then process. Put the unused bits back in the fft buffer + + // Collect samples for both channels if needed + + Needed = FFTSize - fftCount; + + if (Needed <= rx_bufsize) + { + // add Needed samples to fft_buf and process. Copy rest to start of fft_buf + + for (i = 0; i < Needed; i++) + { + *ptr1++ = *data1; + data1 += 2; + } + + doWaterfall(FirstWaterfallChan); + + if (data2) + { + for (i = 0; i < Needed; i++) + { + *ptr2++ = *data2; + data2 += 2; + } + doWaterfall(1); + } + + remainingSamples = rx_bufsize - Needed; + fftCount = 0; + + ptr1 = &fft_buf[0][0]; + ptr2 = &fft_buf[1][0]; + } + + for (i = 0; i < remainingSamples; i++) + { + *ptr1++ = *data1; + data1 += 2; + } + + if (data2) + { + for (i = 0; i < remainingSamples; i++) + { + *ptr2++ = *data2; + data2 += 2; + } + } + fftCount += remainingSamples; + } + + if (TimerEvent == TIMER_EVENT_ON) + { + timer_event(); +// timer_event2(); + } +} + + /* + +procedure TForm1.BufferFull1(var Msg TMessage); +var + i,snd_ch byte; +begin + for snd_ch = 1 to 2 do + if pnt_change[snd_ch] then + begin + make_core_BPF(snd_ch,rx_freq[snd_ch],bpf[snd_ch]); + make_core_TXBPF(snd_ch,tx_freq[snd_ch],txbpf[snd_ch]); + pnt_change[snd_ch] = FALSE; + end; + snd_ch = 0; + for i = 1 to 2 do if msg.WParam = WaveOutHandle[i] then snd_ch = i; + if (snd_ch = 0) then exit; + if (snd_status[snd_ch]<>SND_TX) then exit; + inc(tx_buf_num[snd_ch]); if tx_buf_num[snd_ch]>tx_bufcount then tx_buf_num[snd_ch] = 1; + if (buf_status[snd_ch] = BUF_EMPTY) and (tx_buf_num[snd_ch] = tx_buf_num1[snd_ch]) then TX2RX(snd_ch); + if buf_status[snd_ch] = BUF_FULL then + beginf + make_wave_buf(snd_ch,TX_pbuf[snd_ch][tx_buf_num[snd_ch]]); + WaveOutWrite(WaveOutHandle[snd_ch],@TX_header[snd_ch][tx_buf_num[snd_ch]],sizeof(TWaveHdr)); + inc(tx_buf_num1[snd_ch]); if tx_buf_num1[snd_ch]>tx_bufcount then tx_buf_num1[snd_ch] = 1; + end; +end; + +procedure TForm1.TX2RX(snd_ch byte); +begin + if snd_status[snd_ch] = SND_TX then stoptx(snd_ch); + if snd_status[snd_ch] = SND_IDLE then begin pttoff(snd_ch); end; +end; +*/ + + +// Monitor Code - from moncode.asm + + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +#define SREJ 0x0D +#define SABME 0x6F +#define XID 0xAF +#define TEST 0xE3 + + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +char FrameData[1024] = ""; + +char * frame_monitor(string * frame, char * code, int tx_stat) +{ + char mon_frm[512]; + char AGW_path[256]; + string * AGW_data; + + const Byte * frm = "???"; + Byte * datap; + Byte _data[512] = ""; + Byte * p_data = _data; + int _datalen; + + char agw_port; + char CallFrom[10], CallTo[10], Digi[80]; + + char TR = 'R'; + char codestr[64] = ""; + + integer i; + char time_now[32]; + int len; + + AGWUser * AGW; + + Byte pid, nr, ns, f_type, f_id; + Byte rpt, cr, pf; + Byte path[80]; + char c; + const char * p; + + string * data = newString(); + + if (code[0] && strlen(code) < 60) + sprintf(codestr, "[%s]", code); + + if (tx_stat) + TR = 'T'; + + decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + datap = data->Data; + + len = data->Length; + + // if (pid == 0xCF) + // data = parse_NETROM(data, f_id); + // IP parsing + // else if (pid == 0xCC) + // data = parse_IP(data); + // ARP parsing + // else if (pid == 0xCD) + // data = parse_ARP(data); + // + + if (len > 0) + { + for (i = 0; i < len; i++) + { + if (datap[i] > 31 || datap[i] == 13 || datap[i] == 9) + *(p_data++) = datap[i]; + } + } + + _datalen = p_data - _data; + + if (_datalen) + { + Byte * ptr = _data; + i = 0; + + // remove successive cr or cr on end while (i < _datalen) + + while (i < _datalen) + { + if ((_data[i] == 13) && (_data[i + 1] == 13)) + i++; + else + *(ptr++) = _data[i++]; + } + + if (*(ptr - 1) == 13) + ptr--; + + *ptr = 0; + + _datalen = ptr - _data; + } + + get_monitor_path(path, CallTo, CallFrom, Digi); + + if (cr) + { + c = 'C'; + if (pf) + p = " P"; + else p = ""; + } + else + { + c = 'R'; + if (pf) + p = " F"; + else + p = ""; + } + + switch (f_id) + { + case I_I: + + frm = "I"; + break; + + case S_RR: + + frm = "RR"; + break; + + case S_RNR: + + frm = "RNR"; + break; + + case S_REJ: + + frm = "REJ"; + break; + + case S_SREJ: + + frm = "SREJ"; + break; + + case U_SABM: + + frm = "SABM"; + break; + + case SABME: + + frm = "SABME"; + break; + + case U_DISC: + + frm = "DISC"; + break; + + case U_DM: + + frm = "DM"; + break; + + case U_UA: + + frm = "UA"; + break; + + case U_FRMR: + + frm = "FRMR"; + break; + + case U_UI: + + frm = "UI"; + } + + if (Digi[0]) + sprintf(AGW_path, "Fm %s To %s Via %s <%s %c%s",CallFrom, CallTo, Digi, frm, c, p); + else + sprintf(AGW_path, "Fm %s To %s <%s %c %s", CallFrom, CallTo, frm, c, p); + + + switch (f_type) + { + case I_FRM: + + //mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' S' + inttostr(ns) + ' pid=' + dec2hex(pid) + ' Len=' + inttostr(len) + ' >' + time_now + #13 + _data + #13#13; + sprintf(mon_frm, "%s R%d S%d pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, nr, ns, pid, len, ShortDateTime(), TR, codestr, _data); + + break; + + case U_FRM: + + if (f_id == U_UI) + { + sprintf(mon_frm, "%s pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, pid, len, ShortDateTime(), TR, codestr, _data); // "= AGW_path + ctrl + '>' + time_now + #13; + } + else if (f_id == U_FRMR) + { + sprintf(mon_frm, "%s>%02x %02x %02x[%s]\r", AGW_path, datap[0], datap[1], datap[2], ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13; + } + else + sprintf(mon_frm, "%s>[%s%c]%s\r", AGW_path, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13; + + break; + + case S_FRM: + + // mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' >' + time_now + #13; + sprintf(mon_frm, "%s R%d>[%s%c]%s\r", AGW_path, nr, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13; + + break; + + } + sprintf(FrameData, "%s", mon_frm); + return FrameData; +} + + +/* +procedure TForm1.RX2TX(snd_ch byte); +begin + if snd_status[snd_ch] = SND_IDLE then begin ptton(snd_ch); starttx(snd_ch); end; +end; + +function TForm1.frame_monitor(s,code string; tx_stat boolean) string; +var + len word; + s_tx_stat string; + time_now,s1,c,p string; + callfrom,callto,digi,path,data,frm string; + frm_body string; + pid,nr,ns,f_type,f_id byte; + rpt,cr,pf boolean; + i word; +begin + decode_frame(s,path,data,pid,nr,ns,f_type,f_id,rpt,pf,cr); + len = length(data); + // NETROM parsing + if pid = $CF then data = parse_NETROM(data,f_id); + // IP parsing + if pid = $CC then data = parse_IP(data); + // ARP parsing + if pid = $CD then data = parse_ARP(data); + // + get_monitor_path(path,CallTo,CallFrom,Digi); + if cr then + begin + c = "C"; + if pf then p = " P" else p = ""; + end + else + begin + c = "R"; + if pf then p = " F" else p = ""; + end; + frm = "UNKN"; + case f_id of + I_I frm = "I"; + S_RR frm = "RR"; + S_RNR frm = "RNR"; + S_REJ frm = "REJ"; + U_SABM frm = "SABM"; + U_DISC frm = "DISC"; + U_DM frm = "DM"; + U_UA frm = "UA"; + U_FRMR frm = "FRMR"; + U_UI frm = "UI"; + end; + case tx_stat of + TRUE s_tx_stat = "T"; + FALSE s_tx_stat = "R"; + end; + s1 = ""; + + if code<>"" then code = " ["+code+"]"; + if UTC_Time then time_now = " [UTC"+get_UTC_time+s_tx_stat+"]" + else time_now = " ["+FormatDateTime("hhmmss",now)+s_tx_stat+"]"; + + if digi = "" then frm_body = "Fm "+CallFrom+" To "+CallTo+" <"+frm+" "+c+p + else frm_body = "Fm "+CallFrom+" To "+CallTo+" Via "+Digi+" <"+frm+" "+c+p; + case f_type of + I_FRM frm_body = frm_body+" R"+inttostr(nr)+" S"+inttostr(ns)+" Pid = "+dec2hex(pid)+" Len = "+inttostr(len)+">"+time_now+code+#13+data; + U_FRM if f_id = U_UI then frm_body = frm_body+" Pid = "+dec2hex(pid)+" Len = "+inttostr(len)+">"+time_now+code+#13+data + else if f_id = U_FRMR then begin data = copy(data+#0#0#0,1,3); frm_body = frm_body+">"+time_now+code+#13+inttohex((byte(data[1]) shl 16) or (byte(data[2]) shl 8) or byte(data[3]),6) end + else frm_body = frm_body+">"+time_now+code; + S_FRM frm_body = frm_body+" R"+inttostr(nr)+">"+time_now+code; + end; + for i = 1 to length(frm_body) do + begin + if frm_body[i]>#31 then s1 = s1+frm_body[i]; + if frm_body[i] = #13 then s1 = s1+#13#10; + if frm_body[i] = #10 then s1 = s1+""; + if frm_body[i] = #9 then s1 = s1+#9; + end; + result = s1; +end; + +procedure TForm1.waterfall_init; +begin + bm[1] = TBitMap.Create; + bm[2] = TBitMap.Create; + bm1 = TBitMap.Create; + bm2 = TBitMap.Create; + bm3 = TBitMap.Create; + bm[1].pixelformat = pf32bit; + bm[2].pixelformat = pf32bit; + bm1.pixelformat = pf32bit; + bm2.pixelformat = pf32bit; + bm3.pixelformat = pf32bit; + bm[1].Height = PaintBox1.Height-20; + bm[1].Width = PaintBox1.width; + bm[2].Height = PaintBox1.Height-20; + bm[2].Width = PaintBox1.width; + bm1.Height = 20; + bm1.Width = PaintBox1.width; + bm3.Height = PaintBox1.Height; + bm3.Width = PaintBox1.width; +end; + +procedure TForm1.waterfall_free; +begin + bm[1].Free; + bm[2].Free; + bm1.Free; + bm2.Free; + bm3.Free; +end; + +procedure TForm1.StartAGW; +begin + try + ServerSocket1.Port = AGWPort; + ServerSocket1.Active = AGWServ; + except + ServerSocket1.Active = FALSE; + MessageDlg("AGW host port is busy!", mtWarning,[mbOk],0); + end; +end; + +procedure TForm1.StartKISS; +begin + try + ServerSocket2.Port = KISSPort; + ServerSocket2.Active = KISSServ; + except + ServerSocket2.Active = FALSE; + MessageDlg("KISS port is busy!", mtWarning,[mbOk],0); + end; +end; + +procedure fft_window_init; +var + mag single; + i word; +begin + mag = 2*pi/(FFTSize-1); + for i = 0 to FFTSize-1 do fft_window_arr[i] = 0.5-0.5*cos(i*mag); //hann +end; + +procedure TForm1.FormCreate(Sender TObject); +begin + if hPrevInst <> 0 then begin + MessageDlg("Программа уже запущена!", mtError, [mbOk], 0); + Application.Terminate; + end; + RS = TReedSolomon.Create(Self); + MainThreadHandle = GetCurrentThread; + form1.Caption = MODEM_CAPTION+" - Ver "+MODEM_VERSION; + cur_dir = ExtractFilePath(Application.ExeName); + fft_window_init; + detector_init; + kiss_init; + agw_init; + ax25_init; + init_raduga; + waterfall_init; + ReadIni; + show_combobox; + show_grid_title; + show_mode_panels; + show_panels; + wf_pointer(1); + wf_pointer(2); + wf_Scale; + ChangePriority; + Visible = TRUE; + StartAGW; + StartKISS; + PTTOpen; + startrx; + TimerEvent = TIMER_EVENT_OFF; + if (debugmode and DEBUG_TIMER) = 0 then create_timer1; + if MinOnStart then WindowState = wsMinimized; +end; + +procedure TForm1.TrackBar1Change(Sender TObject); +begin + dcd_threshold = TrackBar1.position; +end; +*/ + +void Timer_Event2() +{ + if (TimerStat2 == TIMER_BUSY || TimerStat2 == TIMER_OFF) + return; + + TimerStat2 = TIMER_BUSY; + +// show_grid(); + + /* + + if (mod_icon_status = MOD_WAIT) then inc(icon_timer); + if icon_timer = 10 then mod_icon_status = MOD_IDLE; + if (mod_icon_status<>MOD_WAIT) and (mod_icon_status<>last_mod_icon_status) then + begin + icon_timer = 0; + case mod_icon_status of + MOD_IDLE form1.CoolTrayIcon1.IconIndex = 0; + MOD_RX begin form1.CoolTrayIcon1.IconIndex = 1; mod_icon_status = MOD_WAIT; end; + MOD_TX form1.CoolTrayIcon1.IconIndex = 2; + end; + last_mod_icon_status = mod_icon_status; + end; + //*/ + + TimerStat2 = TIMER_FREE; +} + +/* + +procedure TimeProc1(uTimerId, uMesssage UINT; dwUser, dw1, dw2 DWORD); stdcall; +begin + TimerEvent = TIMER_EVENT_ON; +end; + +procedure TForm1.create_timer1; +var + TimeEpk cardinal; +begin + TimeEpk = 100; + TimerId1 = TimeSetEvent(TimeEpk,0,@TimeProc1,0,TIME_PERIODIC); +end; + +procedure TForm1.free_timer1; +begin + TimerStat1 = TIMER_OFF; + timeKillEvent(TimerId1); +end; + +*/ + +/* + + +procedure TForm1.PaintBox1MouseMove(Sender TObject; Shift TShiftState; X, + Y Integer); +var + low,high word; +begin + if CheckBox1.Checked then exit; + if not mouse_down[1] then Exit; + rx_freq[1] = round(x*RX_SampleRate/FFTSize); + low = round(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1]+1); + high = round(rx_samplerate/2-(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1])); + if (rx_freq[1]-low)<0 then rx_freq[1] = low; + if (high-rx_freq[1])<0 then rx_freq[1] = high; + tx_freq[1] = rx_freq[1]; + show_freq_a; +end; + +procedure TForm1.PaintBox3MouseMove(Sender TObject; Shift TShiftState; X, + Y Integer); +var + low,high word; +begin + if CheckBox1.Checked then exit; + if not mouse_down[2] then Exit; + rx_freq[2] = round(x*RX_SampleRate/FFTSize); + low = round(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2]+1); + high = round(rx_samplerate/2-(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2])); + if (rx_freq[2]-low)<0 then rx_freq[2] = low; + if (high-rx_freq[2])<0 then rx_freq[2] = high; + tx_freq[2] = rx_freq[2]; + show_freq_b; +end; + +procedure TForm1.PaintBox1MouseUp(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); +begin + mouse_down[1] = FALSE; +end; + +procedure TForm1.PaintBox3MouseUp(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); +begin + mouse_down[2] = FALSE; +end; + +procedure TForm1.PaintBox1MouseDown(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); +var + low,high word; +begin + if CheckBox1.Checked then exit; + if not (ssLeft in shift) then Exit; + mouse_down[1] = TRUE; + rx_freq[1] = round(x*RX_SampleRate/FFTSize); + low = round(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1]+1); + high = round(rx_samplerate/2-(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1])); + if (rx_freq[1]-low)<0 then rx_freq[1] = low; + if (high-rx_freq[1])<0 then rx_freq[1] = high; + tx_freq[1] = rx_freq[1]; + show_freq_a; +end; + +procedure TForm1.PaintBox3MouseDown(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); +var + low,high word; +begin + if CheckBox1.Checked then exit; + if not (ssLeft in shift) then Exit; + mouse_down[2] = TRUE; + rx_freq[2] = round(x*RX_SampleRate/FFTSize); + low = round(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2]+1); + high = round(rx_samplerate/2-(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2])); + if (rx_freq[2]-low)<0 then rx_freq[2] = low; + if (high-rx_freq[2])<0 then rx_freq[2] = high; + tx_freq[2] = rx_freq[2]; + show_freq_b; +end; + +procedure TForm1.ServerSocket1ClientRead(Sender TObject; + Socket TCustomWinSocket); +var + s string; +begin + s = Socket.ReceiveText; + AGW_explode_frame(Socket.sockethandle,s); +end; + +procedure TForm1.ServerSocket1ClientDisconnect(Sender TObject; + Socket TCustomWinSocket); +begin + del_incoming_mycalls_by_sock(socket.SocketHandle); + AGW_del_socket(socket.SocketHandle); +end; + +procedure TForm1.ServerSocket1ClientError(Sender TObject; + Socket TCustomWinSocket; ErrorEvent TErrorEvent; + var ErrorCode Integer); +begin + del_incoming_mycalls_by_sock(socket.SocketHandle); + AGW_del_socket(socket.SocketHandle); + ErrorCode = 0; +end; + +procedure TForm1.ServerSocket1ClientConnect(Sender TObject; + Socket TCustomWinSocket); +begin + agw_add_socket(Socket.sockethandle); +end; + +procedure TForm1.OutputVolume1Click(Sender TObject); +var + s string; +begin + s = "SndVol32.exe -D"+inttostr(SND_TX_DEVICE); + WinExec(pchar(s),SW_SHOWNORMAL); +end; + +procedure TForm1.InputVolume1Click(Sender TObject); +var + s string; +begin + s = "SndVol32.exe -R -D"+inttostr(SND_RX_DEVICE); + WinExec(pchar(s),SW_SHOWNORMAL); +end; + +procedure TForm1.CoolTrayIcon1Click(Sender TObject); +begin + CoolTrayIcon1.ShowMainForm; +end; + +procedure TForm1.CoolTrayIcon1Cycle(Sender TObject; NextIndex Integer); +begin + CoolTrayIcon1.IconIndex = 2; + CoolTrayIcon1.CycleIcons = FALSE; +end; + +procedure TForm1.ABout1Click(Sender TObject); +begin + Form2.ShowModal; +end; + +procedure TForm1.put_frame(snd_ch byte; frame,code string; tx_stat,excluded boolean); +var + s string; +begin + if RxRichedit1.Focused then Windows.SetFocus(0); + if code = "NON-AX25" then + s = inttostr(snd_ch)+" ["+FormatDateTime("hhmmss",now)+"R]" + else + s = inttostr(snd_ch)+""+frame_monitor(frame,code,tx_stat); + //RxRichedit1.Lines.BeginUpdate; + RxRichedit1.SelStart = length(RxRichedit1.text); + RxRichEdit1.SelLength = length(s); + case tx_stat of + TRUE RxRichEdit1.SelAttributes.Color = clMaroon; + FALSE RxRichEdit1.SelAttributes.Color = clBlack; + end; + if excluded then RxRichEdit1.SelAttributes.Color = clGreen; + RxRichedit1.SelText = s+#10; + if RxRichedit1.Lines.Count>nr_monitor_lines then + repeat + RxRichedit1.Lines.Delete(0); + until RxRichedit1.Lines.Count = nr_monitor_lines; + RxRichedit1.HideSelection = FALSE; + RxRichedit1.SelStart = length(RxRichedit1.text); + RxRichedit1.SelLength = 0; + RxRichedit1.HideSelection = TRUE; + //RxRichedit1.Lines.EndUpdate; +end; + +procedure TForm1.show_mode_panels; +begin + panel8.Align = alNone; + panel6.Align = alNone; + if dualchan then panel6.Visible = TRUE else panel6.Visible = FALSE; + panel8.Align = alLeft; + panel6.Align = alLeft; +end; + +procedure TForm1.show_panels; +var + i byte; +begin + panel1.Align = alNone; + panel2.Align = alNone; + panel3.Align = alNone; + panel4.Align = alNone; + panel5.Align = alNone; + for i = 1 to 5 do + case i of + 1 panel1.Visible = panels[i]; + 2 panel5.Visible = panels[i]; + 3 panel3.Visible = panels[i]; + 4 panel4.Visible = panels[i]; + 5 panel2.Visible = panels[i]; + end; + panel1.Align = alBottom; + panel5.Align = alBottom; + panel3.Align = alBottom; + panel2.Align = alTop; + panel4.Align = alClient; +end; + +procedure TForm1.Secondwaterfall1Click(Sender TObject); +begin + case Secondwaterfall1.Checked of + TRUE Secondwaterfall1.Checked = FALSE; + FALSE Secondwaterfall1.Checked = TRUE; + end; + panels[1] = Secondwaterfall1.Checked; + show_panels; +end; + + + +procedure TForm1.Firstwaterfall1Click(Sender TObject); +begin + case Firstwaterfall1.Checked of + TRUE Firstwaterfall1.Checked = FALSE; + FALSE Firstwaterfall1.Checked = TRUE; + end; + panels[2] = Firstwaterfall1.Checked; + show_panels; +end; + +procedure TForm1.Statustable1Click(Sender TObject); +begin + case Statustable1.Checked of + TRUE Statustable1.Checked = FALSE; + FALSE Statustable1.Checked = TRUE; + end; + panels[3] = Statustable1.Checked; + show_panels; +end; + +procedure TForm1.Monitor1Click(Sender TObject); +begin + case Monitor1.Checked of + TRUE Monitor1.Checked = FALSE; + FALSE Monitor1.Checked = TRUE; + end; + panels[4] = Monitor1.Checked; + show_panels; +end; + +procedure TForm1.Devices1Click(Sender TObject); +begin + if (ptt = "EXT") or (ptt = "CAT") then Form3.Button3.Enabled = TRUE else Form3.Button3.Enabled = FALSE; + Form3.GetDeviceInfo; + form3.ShowModal; +end; + +procedure TForm1.FormPaint(Sender TObject); +begin + RxRichedit1.HideSelection = FALSE; + RxRichedit1.SelStart = length(RxRichedit1.text); + RxRichedit1.SelLength = 0; + RxRichedit1.HideSelection = TRUE; +end; + +procedure TForm1.Filters1Click(Sender TObject); +begin + Form5.Show_modem_settings; +end; + +procedure TForm1.Clearmonitor1Click(Sender TObject); +begin + RxRichEdit1.Clear; + frame_count = 0; + single_frame_count = 0; +end; + +procedure TForm1.Copytext1Click(Sender TObject); +begin + RxRichEdit1.CopyToClipboard; +end; + +procedure TForm1.ApplicationEvents1Minimize(Sender TObject); +begin + if stop_wf then w_state = WIN_MINIMIZED; +end; + +procedure TForm1.ApplicationEvents1Restore(Sender TObject); +begin + w_state = WIN_MAXIMIZED; +end; + +procedure TForm1.ServerSocket2ClientConnect(Sender TObject; + Socket TCustomWinSocket); +begin + KISS_add_stream(socket.sockethandle); +end; + +procedure TForm1.ServerSocket2ClientDisconnect(Sender TObject; + Socket TCustomWinSocket); +begin + KISS_del_stream(socket.sockethandle); +end; + +procedure TForm1.ServerSocket2ClientError(Sender TObject; + Socket TCustomWinSocket; ErrorEvent TErrorEvent; + var ErrorCode Integer); +begin + KISS_del_stream(socket.sockethandle); + ErrorCode = 0; +end; + +procedure TForm1.ServerSocket2ClientRead(Sender TObject; + Socket TCustomWinSocket); +var + data string; +begin + data = socket.ReceiveText; + KISS_on_data_in(socket.sockethandle,data); +end; + +procedure TForm1.Font1Click(Sender TObject); +begin + FontDialog1.Font = RXRichEdit1.Font; + if FontDialog1.Execute then + begin + RXRichEdit1.SelStart = 0; + RXRichEdit1.SelLength = Length(RXRichEdit1.Text); + RXRichEdit1.SelAttributes.Size = FontDialog1.Font.Size; + RXRichEdit1.SelAttributes.Name = FontDialog1.Font.Name; + RXRichEdit1.Font.Size = FontDialog1.Font.Size; + RXRichEdit1.Font.Name = FontDialog1.Font.Name; + WriteIni; + end; +end; + +procedure TForm1.Calibration1Click(Sender TObject); +begin + Form6.ShowModal; +end; + +procedure TForm1.ComboBox1Change(Sender TObject); +begin + Speed[1] = get_idx_by_name(ComboBox1.Text); + init_speed(1); + windows.setfocus(0); +end; + +procedure TForm1.ComboBox1KeyDown(Sender TObject; var Key Word; + Shift TShiftState); +begin + key = 0; + windows.SetFocus(0); +end; + +procedure TForm1.ComboBox1KeyPress(Sender TObject; var Key Char); +begin + key = #0; + windows.SetFocus(0); +end; + +procedure TForm1.ComboBox2Change(Sender TObject); +begin + Speed[2] = get_idx_by_name(ComboBox2.Text); + init_speed(2); + windows.setfocus(0); +end; + +procedure TForm1.FormDestroy(Sender TObject); +var + snd_ch byte; +begin + stoprx; + for snd_ch = 1 to 2 do if snd_status[snd_ch] = SND_TX then stoptx(snd_ch); + if (debugmode and DEBUG_TIMER) = 0 then free_timer1; + TimerStat2 = TIMER_OFF; + PTTClose; + ax25_free; + agw_free; + kiss_free; + detector_free; + RS.Free; + waterfall_free; + WriteIni; +end; + +end. +*/ \ No newline at end of file diff --git a/sm_main.c b/sm_main.c new file mode 100644 index 0000000..0e08aad --- /dev/null +++ b/sm_main.c @@ -0,0 +1,2852 @@ +/* +Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO + +This file is part of QtSoundModem + +QtSoundModem is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +QtSoundModem is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +newsamp + +You should have received a copy of the GNU General Public License +along with QtSoundModem. If not, see http://www.gnu.org/licenses + +*/ + +// UZ7HO Soundmodem Port by John Wiseman G8BPQ + +#include "UZ7HOStuff.h" + +void make_core_BPF(UCHAR snd_ch, short freq, short width); +void make_core_TXBPF(UCHAR snd_ch, float freq, float width); +void make_core_INTR(UCHAR snd_ch); +void make_core_LPF(UCHAR snd_ch, short width); +void wf_pointer(int snd_ch); + +char modes_name[modes_count][20] = +{ + "AFSK AX.25 300bd","AFSK AX.25 1200bd","AFSK AX.25 600bd","AFSK AX.25 2400bd", + "BPSK AX.25 1200bd","BPSK AX.25 600bd","BPSK AX.25 300bd","BPSK AX.25 2400bd", + "QPSK AX.25 4800bd","QPSK AX.25 3600bd","QPSK AX.25 2400bd","BPSK FEC 4x100bd", + "DW QPSK V26A 2400bd","DW 8PSK V27 4800bd","DW QPSK V26B 2400bd", "ARDOP Packet" +}; + +typedef struct wavehdr_tag { + unsigned short * lpData; /* pointer to locked data buffer */ + int dwBufferLength; /* length of data buffer */ + int dwBytesRecorded; /* used for input only */ + int * dwUser; /* for client's use */ + int dwFlags; /* assorted flags (see defines) */ + int dwLoops; /* loop control counter */ + struct wavehdr_tag *lpNext; /* reserved for driver */ + int * reserved; /* reserved for driver */ +} WAVEHDR, *PWAVEHDR, * NPWAVEHDR, * LPWAVEHDR; + +extern int pnt_change[5]; +int debugmode = 0; +extern float src_buf[5][2048]; +extern Byte RCVR[5]; + +int SatelliteMode = 0; + +int UDPServerPort = 8884; +int UDPClientPort = 8888; +int TXPort = 8884; + +BOOL Firstwaterfall = 1; +BOOL Secondwaterfall = 1; + +int multiCore = FALSE; + +/* +type + TComboBox = class(StdCtrls.TComboBox) + private + procedure CMMouseWheel(var msg TCMMouseWheel); message CM_MOUSEWHEEL; + end; + TData16 = array [0..4095] of smallint; + PData16 = ^TData16; + TWaveHeader = record + RIFF dword; + ChunkLen integer; + WAVE dword; + fmt dword; + FormatLen integer; + Format word; + Channels word; + Frequency integer; + BytesPS integer; + BlockAlign word; + BitsPS word; + data dword; + DataLen integer + end; + TForm1 = class(TForm) + Panel5 TPanel; + ServerSocket1 TServerSocket; + MainMenu1 TMainMenu; + Settings1 TMenuItem; + OutputVolume1 TMenuItem; + InputVolume1 TMenuItem; + CoolTrayIcon1 TCoolTrayIcon; + ImageList1 TImageList; + ABout1 TMenuItem; + Panel1 TPanel; + Panel2 TPanel; + View1 TMenuItem; + Firstwaterfall1 TMenuItem; + Secondwaterfall1 TMenuItem; + Panel3 TPanel; + StringGrid1 TStringGrid; + Devices1 TMenuItem; + Statustable1 TMenuItem; + Monitor1 TMenuItem; + Panel4 TPanel; + PaintBox2 TPaintBox; + Filters1 TMenuItem; + Clearmonitor1 TMenuItem; + RxRichEdit1 TRxRichEdit; + MemoPopupMenu1 TPopupMenu; + Copytext1 TMenuItem; + Label1 TLabel; + Label5 TLabel; + ApplicationEvents1 TApplicationEvents; + PaintBox1 TPaintBox; + PaintBox3 TPaintBox; + ServerSocket2 TServerSocket; + Font1 TMenuItem; + FontDialog1 TFontDialog; + N1 TMenuItem; + Calibration1 TMenuItem; + Panel9 TPanel; + Panel6 TPanel; + Label4 TLabel; + Shape2 TShape; + ComboBox2 TComboBox; + SpinEdit2 TSpinEdit; + Panel7 TPanel; + Label3 TLabel; + Shape1 TShape; + ComboBox1 TComboBox; + SpinEdit1 TSpinEdit; + Panel8 TPanel; + Label2 TLabel; + TrackBar1 TTrackBar; + CheckBox1 TCheckBox; + OpenDialog1 TOpenDialog; + procedure FormCreate(Sender TObject); + procedure TrackBar1Change(Sender TObject); + procedure PaintBox1MouseMove(Sender TObject; Shift TShiftState; X, + Y Integer); + procedure PaintBox1MouseDown(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); + procedure ServerSocket1ClientRead(Sender TObject; + Socket TCustomWinSocket); + procedure ServerSocket1ClientError(Sender TObject; + Socket TCustomWinSocket; ErrorEvent TErrorEvent; + var ErrorCode Integer); + procedure OutputVolume1Click(Sender TObject); + procedure InputVolume1Click(Sender TObject); + procedure ServerSocket1ClientConnect(Sender TObject; + Socket TCustomWinSocket); + procedure ServerSocket1ClientDisconnect(Sender TObject; + Socket TCustomWinSocket); + procedure CoolTrayIcon1Click(Sender TObject); + procedure CoolTrayIcon1Cycle(Sender TObject; NextIndex Integer); + procedure ABout1Click(Sender TObject); + procedure PaintBox3MouseDown(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); + procedure PaintBox3MouseMove(Sender TObject; Shift TShiftState; X, + Y Integer); + procedure Firstwaterfall1Click(Sender TObject); + procedure Secondwaterfall1Click(Sender TObject); + procedure Devices1Click(Sender TObject); + procedure Statustable1Click(Sender TObject); + procedure Monitor1Click(Sender TObject); + procedure FormPaint(Sender TObject); + procedure Filters1Click(Sender TObject); + procedure SpinEdit1Change(Sender TObject); + procedure SpinEdit2Change(Sender TObject); + procedure Clearmonitor1Click(Sender TObject); + procedure Copytext1Click(Sender TObject); + procedure PaintBox3MouseUp(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); + procedure PaintBox1MouseUp(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); + procedure ApplicationEvents1Minimize(Sender TObject); + procedure ApplicationEvents1Restore(Sender TObject); + procedure ServerSocket2ClientConnect(Sender TObject; + Socket TCustomWinSocket); + procedure ServerSocket2ClientDisconnect(Sender TObject; + Socket TCustomWinSocket); + procedure ServerSocket2ClientError(Sender TObject; + Socket TCustomWinSocket; ErrorEvent TErrorEvent; + var ErrorCode Integer); + procedure ServerSocket2ClientRead(Sender TObject; + Socket TCustomWinSocket); + procedure Font1Click(Sender TObject); + procedure Calibration1Click(Sender TObject); + procedure ComboBox1Change(Sender TObject); + procedure ComboBox1KeyDown(Sender TObject; var Key Word; + Shift TShiftState); + procedure ComboBox1KeyPress(Sender TObject; var Key Char); + procedure ComboBox2Change(Sender TObject); + procedure FormDestroy(Sender TObject); + private + { Private declarations } + procedure BufferFull(var Msg TMessage); Message MM_WIM_DATA; + procedure BufferFull1(var Msg TMessage); Message MM_WOM_DONE; + procedure make_wave_buf(snd_ch byte; buf PChar); + procedure disp2(snd_ch byte); + procedure create_timer1; + procedure free_timer1; + procedure show_panels; + procedure show_combobox; + procedure Timer_Event2; + procedure waterfall_init; + procedure waterfall_free; + public + { Public declarations } + function get_idx_by_name(name string) word; + function frame_monitor(s,code string; tx_stat boolean) string; + procedure ChangePriority; + procedure put_frame(snd_ch byte; frame,code string; tx_stat,excluded boolean); + procedure show_grid; + procedure RX2TX(snd_ch byte); + procedure TX2RX(snd_ch byte); + procedure WriteIni; + procedure ReadIni; + procedure init_8P4800(snd_ch byte); + procedure init_DW2400(snd_ch byte); + procedure init_AE2400(snd_ch byte); + procedure init_MP400(snd_ch byte); + procedure init_Q4800(snd_ch byte); + procedure init_Q3600(snd_ch byte); + procedure init_Q2400(snd_ch byte); + procedure init_P2400(snd_ch byte); + procedure init_P1200(snd_ch byte); + procedure init_P600(snd_ch byte); + procedure init_P300(snd_ch byte); + procedure init_300(snd_ch byte); + procedure init_600(snd_ch byte); + procedure init_1200(snd_ch byte); + procedure init_2400(snd_ch byte); + procedure init_speed(snd_ch byte); + procedure get_filter_values(idx byte; var dbpf,dtxbpf,dbpftap,dlpf,dlpftap word); + procedure show_mode_panels; + procedure show_modes; + procedure show_freq_a; + procedure show_freq_b; + procedure ChkSndDevName; + procedure StartRx; + procedure StartTx(snd_ch byte); + procedure StopRx; + procedure StopTx(snd_ch byte); + procedure StartAGW; + procedure StartKISS; + procedure wf_scale; + procedure wf_pointer(snd_ch byte); + end; + +var +/*/ + +BOOL MinOnStart = 0; +//RS TReedSolomon; +// Form1 TForm1; +// WaveFormat TWaveFormatEx; +int Channels = 2; +int BitsPerSample = 16; +float TX_Samplerate = 12000; +float RX_Samplerate = 12000; +int RX_SR = 11025; +int TX_SR = 11025; +int RX_PPM = 0; +int TX_PPM = 0; +int tx_bufsize = 512; +int rx_bufsize = 512; +int tx_bufcount = 16; +int rx_bufcount = 16; +int mouse_down[2] = {0, 0}; +//UCHAR * RX_pBuf array[257]; +// RX_header array[1..256] of TWaveHdr; +// TX_pBuf array[1..4,1..256] of pointer; +//TX_header array[1..4,1..256] of TWaveHdr; +UCHAR calib_mode[5] = {0,0,0,0}; +UCHAR snd_status [5] = {0,0,0,0}; +UCHAR buf_status [5] = {0,0,0,0}; +UCHAR tx_buf_num1 [5] = {0,0,0,0}; +UCHAR tx_buf_num [5] = {0,0,0,0}; + +extern short active_rx_freq[5]; + + + +int speed[5] = {0,0,0,0}; +int panels[6] = {1,1,1,1,1}; + +short fft_buf[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; + + /* +implementation + +{$R *.DFM} + +uses ax25_mod, ax25_demod, ax25, ax25_l2, ax25_ptt, ax25_agw, ax25_about, rgb_rad, + AX25_set, ax25_filter, AX25_modem_set, kiss_mode, ax25_calibration; + +procedure TComboBox.CMMouseWheel(var msg TCMMouseWheel); +begin + if SendMessage(GetFocus, CB_GETDROPPEDSTATE, 0, 0) = 0 then msg.Result = 1; +end; + +procedure TForm1.ChangePriority; +begin + case MainPriority of + 0 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_NORMAL); + 1 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_ABOVE_NORMAL); + 2 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_HIGHEST); + 3 SetThreadPriority(MainThreadHandle,THREAD_PRIORITY_TIME_CRITICAL); + end; +end; + +procedure TForm1.show_modes; +var + s string; +begin + s = MODEM_CAPTION+" - Ver "+MODEM_VERSION+" - ["+modes_name[Speed[1]]; + if dualchan then s = s+" - "+modes_name[Speed[2]]; + form1.Caption = s+"]"; +end; + +procedure TForm1.show_freq_a; +begin + SpinEdit1.Value = round(rx_freq[1]); + SpinEdit1.Refresh; +end; + +procedure TForm1.show_freq_b; +begin + SpinEdit2.Value = round(rx_freq[2]); + SpinEdit2.Refresh; +end; +*/ +void get_filter_values(UCHAR snd_ch) +{ + //, unsigned short dbpf, +//unsigned short dtxbpf, +//unsigned short dbpftap, +//unsigned short dlpf, +//unsigned short dlpftap) +// speed[snd_ch], bpf[snd_ch], txbpf[snd_ch], bpf_tap[snd_ch], lpf[snd_ch], lpf_tap[snd_ch]); + + switch (speed[snd_ch]) + { + case SPEED_8P4800: + + lpf[snd_ch] = MODEM_8P4800_LPF; + bpf[snd_ch] = MODEM_8P4800_BPF; + txbpf[snd_ch] = MODEM_8P4800_TXBPF; + BPF_tap[snd_ch] = MODEM_8P4800_BPF_TAP; + LPF_tap[snd_ch] = MODEM_8P4800_LPF_TAP; + break; + + case SPEED_MP400: + + lpf[snd_ch] = MODEM_MP400_LPF; + bpf[snd_ch] = MODEM_MP400_BPF; + txbpf[snd_ch] = MODEM_MP400_TXBPF; + BPF_tap[snd_ch] = MODEM_MP400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_MP400_LPF_TAP; + + break; + + + case SPEED_Q4800: + + lpf[snd_ch] = MODEM_Q4800_LPF; + bpf[snd_ch] = MODEM_Q4800_BPF; + txbpf[snd_ch] = MODEM_Q4800_TXBPF; + BPF_tap[snd_ch] = MODEM_Q4800_BPF_TAP; + LPF_tap[snd_ch] = MODEM_Q4800_LPF_TAP; + + break; + + case SPEED_Q3600: + + lpf[snd_ch] = MODEM_Q3600_LPF; + bpf[snd_ch] = MODEM_Q3600_BPF; + txbpf[snd_ch] = MODEM_Q3600_TXBPF; + BPF_tap[snd_ch] = MODEM_Q3600_BPF_TAP; + LPF_tap[snd_ch] = MODEM_Q3600_LPF_TAP; + break; + + case SPEED_Q2400: + + lpf[snd_ch] = MODEM_Q2400_LPF; + bpf[snd_ch] = MODEM_Q2400_BPF; + txbpf[snd_ch] = MODEM_Q2400_TXBPF; + BPF_tap[snd_ch] = MODEM_Q2400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_Q2400_LPF_TAP; + + break; + + case SPEED_DW2400: + case SPEED_AE2400: + + + lpf[snd_ch] = MODEM_DW2400_LPF; + bpf[snd_ch] = MODEM_DW2400_BPF; + txbpf[snd_ch] = MODEM_DW2400_TXBPF; + BPF_tap[snd_ch] = MODEM_DW2400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_DW2400_LPF_TAP; + break; + + case SPEED_P2400: + + lpf[snd_ch] = MODEM_P2400_LPF; + bpf[snd_ch] = MODEM_P2400_BPF; + txbpf[snd_ch] = MODEM_P2400_TXBPF; + BPF_tap[snd_ch] = MODEM_P2400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P2400_LPF_TAP; + break; + + case SPEED_P1200: + + lpf[snd_ch] = MODEM_P1200_LPF; + bpf[snd_ch] = MODEM_P1200_BPF; + txbpf[snd_ch] = MODEM_P1200_TXBPF; + BPF_tap[snd_ch] = MODEM_P1200_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P1200_LPF_TAP; + break; + + case SPEED_P600: + + lpf[snd_ch] = MODEM_P600_LPF; + bpf[snd_ch] = MODEM_P600_BPF; + txbpf[snd_ch] = MODEM_P600_TXBPF; + BPF_tap[snd_ch] = MODEM_P600_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P600_LPF_TAP; + break; + + case SPEED_P300: + + lpf[snd_ch] = MODEM_P300_LPF; + bpf[snd_ch] = MODEM_P300_BPF; + txbpf[snd_ch] = MODEM_P300_TXBPF; + BPF_tap[snd_ch] = MODEM_P300_BPF_TAP; + LPF_tap[snd_ch] = MODEM_P300_LPF_TAP; + break; + + case SPEED_300: + + lpf[snd_ch] = MODEM_300_LPF; + bpf[snd_ch] = MODEM_300_BPF; + txbpf[snd_ch] = MODEM_300_TXBPF; + BPF_tap[snd_ch] = MODEM_300_BPF_TAP; + LPF_tap[snd_ch] = MODEM_300_LPF_TAP; + + break; + + case SPEED_600: + + lpf[snd_ch] = MODEM_600_LPF; + bpf[snd_ch] = MODEM_600_BPF; + txbpf[snd_ch] = MODEM_600_TXBPF; + BPF_tap[snd_ch] = MODEM_600_BPF_TAP; + LPF_tap[snd_ch] = MODEM_600_LPF_TAP; + + break; + + case SPEED_1200: + + lpf[snd_ch] = MODEM_1200_LPF; + bpf[snd_ch] = MODEM_1200_BPF; + txbpf[snd_ch] = MODEM_1200_TXBPF; + BPF_tap[snd_ch] = MODEM_1200_BPF_TAP; + LPF_tap[snd_ch] = MODEM_1200_LPF_TAP; + break; + + case SPEED_2400: + + lpf[snd_ch] = MODEM_2400_LPF; + bpf[snd_ch] = MODEM_2400_BPF; + txbpf[snd_ch] = MODEM_2400_TXBPF; + BPF_tap[snd_ch] = MODEM_2400_BPF_TAP; + LPF_tap[snd_ch] = MODEM_2400_LPF_TAP; + break; + } + +} + + +void init_2400(int snd_ch) +{ + modem_mode[snd_ch] = MODE_FSK; + rx_shift[snd_ch] = 1805; + rx_baudrate[snd_ch] = 2400; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_1200(int snd_ch) +{ + modem_mode[snd_ch] = MODE_FSK; + rx_shift[snd_ch] = 1000; + + if (stdtones) + rx_freq[snd_ch] = 1700; + + rx_baudrate[snd_ch] = 1200; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_600(int snd_ch) +{ + modem_mode[snd_ch] = MODE_FSK; + rx_shift[snd_ch] = 450; + + rx_baudrate[snd_ch] = 600; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_300(int snd_ch) +{ + modem_mode[snd_ch] = MODE_FSK; + rx_shift[snd_ch] = 200; + rx_baudrate[snd_ch] = 300; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_MP400(int snd_ch) +{ + modem_mode[snd_ch] = MODE_MPSK; + rx_shift[snd_ch] = 175 /*sbc*/ * 3; + rx_baudrate[snd_ch] = 100; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + + +void init_8P4800(int snd_ch) +{ + modem_mode[snd_ch] = MODE_8PSK; + if (stdtones) + rx_freq[snd_ch] = 1800; + + rx_shift[snd_ch] = 1600; + rx_baudrate[snd_ch] = 1600; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_AE2400(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_V26; + modem_mode[snd_ch] = MODE_PI4QPSK; + + if (stdtones) + rx_freq[snd_ch] = 1800; + + rx_shift[snd_ch] = 1200; + rx_baudrate[snd_ch] = 1200; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_DW2400(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_V26; + modem_mode[snd_ch] = MODE_QPSK; + + if (stdtones) + rx_freq[snd_ch] = 1800; + + rx_shift[snd_ch] = 1200; + rx_baudrate[snd_ch] = 1200; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_Q4800(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_SM; + modem_mode[snd_ch] = MODE_QPSK; + rx_shift[snd_ch] = 2400; + rx_baudrate[snd_ch] = 2400; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_Q3600(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_SM; + modem_mode[snd_ch] = MODE_QPSK; + rx_shift[snd_ch] = 1800; + rx_baudrate[snd_ch] = 1800; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_Q2400(int snd_ch) +{ + qpsk_set[snd_ch].mode = QPSK_SM; + modem_mode[snd_ch] = MODE_QPSK; + rx_shift[snd_ch] = 1200; + rx_baudrate[snd_ch] = 1200; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_P2400(int snd_ch) +{ + modem_mode[snd_ch] = MODE_BPSK; + rx_shift[snd_ch] = 2400; + rx_baudrate[snd_ch] = 2400; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_P1200(int snd_ch) +{ + modem_mode[snd_ch] = MODE_BPSK; + rx_shift[snd_ch] = 1200; + rx_baudrate[snd_ch] = 1200; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_P600(int snd_ch) +{ + modem_mode[snd_ch] = MODE_BPSK; + rx_shift[snd_ch] = 600; + rx_baudrate[snd_ch] = 600; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_P300(int snd_ch) +{ + modem_mode[snd_ch] = MODE_BPSK; + rx_shift[snd_ch] = 300; + rx_baudrate[snd_ch] = 300; + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + +void init_ARDOP(int snd_ch) +{ + modem_mode[snd_ch] = MODE_ARDOP; + rx_shift[snd_ch] = 500; + rx_freq[snd_ch] = 1500; + rx_baudrate[snd_ch] = 500; + + if (modem_def[snd_ch]) + get_filter_values(snd_ch); +} + + +void init_speed(int snd_ch); + +void set_speed(int snd_ch, int Modem) +{ + speed[snd_ch] = Modem; + + init_speed(snd_ch); + +} + +void init_speed(int snd_ch) +{ + int low, high; + + /* + + if (BPF[snd_ch]>round(rx_samplerate/2) then BPF[snd_ch] = round(rx_samplerate/2); + if TXBPF[snd_ch]>round(rx_samplerate/2) then TXBPF[snd_ch] = round(rx_samplerate/2); + if LPF[snd_ch]>round(rx_samplerate/2) then LPF[snd_ch] = round(rx_samplerate/2); + if BPF[snd_ch]<1 then BPF[snd_ch] = 1; + if TXBPF[snd_ch]<1 then TXBPF[snd_ch] = 1; + if LPF[snd_ch]<1 then LPF[snd_ch] = 1; + if TXDelay[snd_ch]<1 then TXDelay[snd_ch] = 1; + if TXTail[snd_ch]<1 then TXTail[snd_ch] = 1; + if BPF_tap[snd_ch]>1024 then BPF_tap[snd_ch] = 1024; + if LPF_tap[snd_ch]>512 then LPF_tap[snd_ch] = 512; + if BPF_tap[snd_ch]<8 then BPF_tap[snd_ch] = 8; + if LPF_tap[snd_ch]<8 then LPF_tap[snd_ch] = 8; + if not (RCVR[snd_ch] in [0..8]) then RCVR[snd_ch] = 0; + if not (rcvr_offset[snd_ch] in [0..100]) then rcvr_offset[snd_ch] = 30; + if not (speed[snd_ch] in [0..modes_count]) then speed[snd_ch] = SPEED_300; +*/ + switch (speed[snd_ch]) + { + case SPEED_8P4800: + init_8P4800(snd_ch); + break; + + case SPEED_AE2400: + init_AE2400(snd_ch); + break; + + case SPEED_DW2400: + init_DW2400(snd_ch); + break; + + case SPEED_MP400: + init_MP400(snd_ch); + break; + case SPEED_Q4800: + init_Q4800(snd_ch); + break; + + case SPEED_Q3600: + init_Q3600(snd_ch); + break; + + case SPEED_Q2400: + init_Q2400(snd_ch); + break; + + case SPEED_P2400: + init_P2400(snd_ch); + break; + + case SPEED_P1200: + init_P1200(snd_ch); + break; + + case SPEED_P600: + init_P600(snd_ch); + break; + + case SPEED_P300: + init_P300(snd_ch); + break; + + case SPEED_300: + + init_300(snd_ch); + break; + + case SPEED_600: + + init_600(snd_ch); + break; + + case SPEED_1200: + + init_1200(snd_ch); + break; + + case SPEED_2400: + + init_2400(snd_ch); + break; + + case SPEED_ARDOP: + + init_ARDOP(snd_ch); + break; + } + + //QPSK_SM: begin move(#0#1#2#3, tx[0], 4); move(#0#32#64#96, rx[0], 4); end; + //QPSK_V26: begin move(#2#3#1#0, tx[0], 4); move(#96#64#0#32, rx[0], 4); end; + + + if (modem_mode[snd_ch] == MODE_QPSK || modem_mode[snd_ch] == MODE_PI4QPSK) + { + switch (qpsk_set[snd_ch].mode) + { + case QPSK_SM: + + memcpy(&qpsk_set[snd_ch].tx[0], "\0\1\2\3", 4); + memcpy(&qpsk_set[snd_ch].rx[0], "\x0\x20\x40\x60", 4); + break; + + case QPSK_V26: + + memcpy(&qpsk_set[snd_ch].tx[0], "\2\3\1\0", 4); + memcpy(&qpsk_set[snd_ch].rx[0], "\x60\x40\0\x20", 4); + break; + } + } + + tx_shift[snd_ch] = rx_shift[snd_ch]; + tx_baudrate[snd_ch] = rx_baudrate[snd_ch]; + low = roundf(rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch] + 1); + high = roundf(RX_Samplerate / 2 - (rx_shift[snd_ch] / 2 + RCVR[snd_ch] * rcvr_offset[snd_ch])); + + if (rx_freq[snd_ch] - low < 0) rx_freq[snd_ch] = low; + if (high - rx_freq[snd_ch] < 0) rx_freq[snd_ch] = high; + + tx_freq[snd_ch] = rx_freq[snd_ch]; + + make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); + make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]); + make_core_INTR(snd_ch); + make_core_LPF(snd_ch, lpf[snd_ch]); + + /* + for i = 0 to 16 do + for j = 0 to nr_emph do with DET[j,i] do + begin + minamp[snd_ch] = 0; + maxamp[snd_ch] = 0; + ones[snd_ch] = 0; + zeros[snd_ch] = 0; + sample_cnt[snd_ch] = 0; + bit_cnt[snd_ch] = 0; + bit_osc[snd_ch] = 0; + frame_status[snd_ch] = FRAME_WAIT; + end; + form1.show_modes; + form1.show_freq_a; + form1.show_freq_b; + */ + wf_pointer(soundChannel[snd_ch]); + + +} + +/* +procedure TForm1.show_combobox; +var + i word; +begin + for i = 0 to length(modes_name)-1 do + begin + ComboBox1.Items.Add(modes_name[i]); + ComboBox2.Items.Add(modes_name[i]); + end; + ComboBox1.ItemIndex = ComboBox1.Items.IndexOf(modes_name[Speed[1]]); + ComboBox2.ItemIndex = ComboBox2.Items.IndexOf(modes_name[Speed[2]]); +end; + +function TForm1.get_idx_by_name(name string) word; +var + i word; + found boolean; +begin + i = 0; + found = FALSE; + result = 0; + repeat + if name = modes_name[i] then + begin + found = TRUE; + result = i; + end + else inc(i); + until found or (i = length(modes_name)); +end; + +procedure TForm1.ReadIni; +var + snd_ch byte; +begin + TelIni = TIniFile.Create(cur_dir+"soundmodem.ini"); + with TelIni do + begin + UTC_Time = ReadBool("Init","UTCTime",FALSE); + MainPriority = ReadInteger("Init","Priority",2); + nr_monitor_lines = ReadInteger("Init","NRMonitorLines",500); + ptt = ReadString("Init","PTT","NONE"); + stop_wf = ReadBool("Init","StopWF",FALSE); + raduga = ReadBool("Init","DispMode",DISP_MONO); + stat_log = ReadBool("Init","StatLog",FALSE); + SND_RX_DEVICE = ReadInteger("Init","SndRXDevice",0); + SND_TX_DEVICE = ReadInteger("Init","SndTXDevice",0); + snd_rx_device_name = ReadString("Init","SndRXDeviceName",""); + snd_tx_device_name = ReadString("Init","SndTXDeviceName",""); + RX_SR = ReadInteger("Init","RXSampleRate",11025); + TX_SR = ReadInteger("Init","TXSampleRate",11025); + RX_PPM = ReadInteger("Init","RX_corr_PPM",0); + TX_PPM = ReadInteger("Init","TX_corr_PPM",0); + tx_bufcount = ReadInteger("Init","TXBufNumber",32); + rx_bufcount = ReadInteger("Init","RXBufNumber",32); + DebugMode = ReadInteger("Init","DisableUnit",0); + TX_rotate = ReadBool("Init","TXRotate",FALSE); + DualChan = ReadBool("Init","DualChan",FALSE); + DualPTT = ReadBool("Init","DualPTT",TRUE); + SCO = ReadBool("Init","SCO",FALSE); + stdtones = ReadBool("Init","UseStandardTones",TRUE); + // Channel A settings + maxframe[1] = ReadInteger("AX25_A","Maxframe",3); + fracks[1] = ReadInteger("AX25_A","Retries",15); + frack_time[1] = ReadInteger("AX25_A","FrackTime",5); + idletime[1] = ReadInteger("AX25_A","IdleTime",180); + slottime[1] = ReadInteger("AX25_A","SlotTime",100); + persist[1] = ReadInteger("AX25_A","Persist",128); + resptime[1] = ReadInteger("AX25_A","RespTime",1500); + TXFrmMode[1] = ReadInteger("AX25_A","TXFrmMode",1); + max_frame_collector[1] = ReadInteger("AX25_A","FrameCollector",6); + exclude_callsigns[1] = ReadString("AX25_A","ExcludeCallsigns",""); + exclude_APRS_frm[1] = ReadString("AX25_A","ExcludeAPRSFrmType",""); + KISS_opt[1] = ReadBool("AX25_A","KISSOptimization",FALSE); + dyn_frack[1] = ReadBool("AX25_A","DynamicFrack",FALSE); + recovery[1] = ReadInteger("AX25_A","BitRecovery",0); + NonAX25[1] = ReadBool("AX25_A","NonAX25Frm",FALSE); + MEMRecovery[1] = ReadInteger("AX25_A","MEMRecovery",200); + IPOLL[1] = ReadInteger("AX25_A","IPOLL",80); + MyDigiCall[1] = ReadString("AX25_A","MyDigiCall",""); + tx_hitoneraisedb[1] = ReadInteger("AX25_A","HiToneRaise",0); + // Channel B settings + maxframe[2] = ReadInteger("AX25_B","Maxframe",3); + fracks[2] = ReadInteger("AX25_B","Retries",15); + frack_time[2] = ReadInteger("AX25_B","FrackTime",5); + idletime[2] = ReadInteger("AX25_B","IdleTime",180); + slottime[2] = ReadInteger("AX25_B","SlotTime",100); + persist[2] = ReadInteger("AX25_B","Persist",128); + resptime[2] = ReadInteger("AX25_B","RespTime",1500); + TXFrmMode[2] = ReadInteger("AX25_B","TXFrmMode",1); + max_frame_collector[2] = ReadInteger("AX25_B","FrameCollector",6); + exclude_callsigns[2] = ReadString("AX25_B","ExcludeCallsigns",""); + exclude_APRS_frm[2] = ReadString("AX25_B","ExcludeAPRSFrmType",""); + KISS_opt[2] = ReadBool("AX25_B","KISSOptimization",FALSE); + dyn_frack[2] = ReadBool("AX25_B","DynamicFrack",FALSE); + recovery[2] = ReadInteger("AX25_B","BitRecovery",0); + NonAX25[2] = ReadBool("AX25_B","NonAX25Frm",FALSE); + MEMRecovery[2] = ReadInteger("AX25_B","MEMRecovery",200); + IPOLL[2] = ReadInteger("AX25_B","IPOLL",80); + MyDigiCall[2] = ReadString("AX25_B","MyDigiCall",""); + tx_hitoneraisedb[2] = ReadInteger("AX25_B","HiToneRaise",0); + // Modem settings + pkt_raw_min_len = ReadInteger("Modem","RawPktMinLen",17); + swap_ptt = ReadBool("Modem","SwapPTTPins",FALSE); + inv_ptt = ReadBool("Modem","InvPTTPins",FALSE); + Emph_all[1] = ReadBool("Modem","PreEmphasisAll1",TRUE); + Emph_all[2] = ReadBool("Modem","PreEmphasisAll2",TRUE); + emph_db[1] = ReadInteger("Modem","PreEmphasisDB1",0); + emph_db[2] = ReadInteger("Modem","PreEmphasisDB2",0); + txbpf[1] = ReadInteger("Modem","TXBPF1",500); + txbpf[2] = ReadInteger("Modem","TXBPF2",500); + bpf[1] = ReadInteger("Modem","BPF1",500); + bpf[2] = ReadInteger("Modem","BPF2",500); + lpf[1] = ReadInteger("Modem","LPF1",150); + lpf[2] = ReadInteger("Modem","LPF2",150); + BPF_tap[1] = ReadInteger("Modem","BPFTap1",256); + BPF_tap[2] = ReadInteger("Modem","BPFTap2",256); + LPF_tap[1] = ReadInteger("Modem","LPFTap1",128); + LPF_tap[2] = ReadInteger("Modem","LPFTap2",128); + DCD_threshold = ReadInteger("Modem","DCDThreshold",32); + rx_freq[1] = ReadFloat("Modem","RXFreq1",1700); + rx_freq[2] = ReadFloat("Modem","RXFreq2",1700); + CheckBox1.Checked = ReadBool("Modem","HoldPnt",FALSE); + BIT_AFC = ReadInteger("Modem","AFC",32); + txdelay[1] = ReadInteger("Modem","TxDelay1",250); + txdelay[2] = ReadInteger("Modem","TxDelay2",250); + txtail[1] = ReadInteger("Modem","TxTail1",50); + txtail[2] = ReadInteger("Modem","TxTail2",50); + diddles = ReadInteger("Modem","Diddles",0); + RCVR[1] = ReadInteger("Modem","NRRcvrPairs1",0); + RCVR[2] = ReadInteger("Modem","NRRcvrPairs2",0); + rcvr_offset[1] = ReadInteger("Modem","RcvrShift1",30); + rcvr_offset[2] = ReadInteger("Modem","RcvrShift2",30); + speed[1] = ReadInteger("Modem","ModemType1",SPEED_1200); + speed[2] = ReadInteger("Modem","ModemType2",SPEED_1200); + modem_def[1] = ReadBool("Modem","Default1",TRUE); + modem_def[2] = ReadBool("Modem","Default2",TRUE); + AGWServ = ReadBool("AGWHost","Server",TRUE); + AGWPort = ReadInteger("AGWHost","Port",8000); + KISSServ = ReadBool("KISS","Server",FALSE); + KISSPort = ReadInteger("KISS","Port",8100); + Form1.Top = ReadInteger("Window","Top",0); + Form1.Left = ReadInteger("Window","Left",0); + Form1.Height = ReadInteger("Window","Height",656); + Form1.Width = ReadInteger("Window","Width",764); + MinOnStart = ReadBool("Window","MinimizedOnStartup",FALSE); + Firstwaterfall1.checked = ReadBool("Window","Waterfall1",TRUE); + Secondwaterfall1.checked = ReadBool("Window","Waterfall2",FALSE); + Statustable1.checked = ReadBool("Window","StatTable",TRUE); + Monitor1.checked = ReadBool("Window","Monitor",TRUE); + RXRichEdit1.Font.Size = ReadInteger("Font","Size",RXRichEdit1.Font.Size); + RXRichEdit1.Font.Name = ReadString("Font","Name",RXRichEdit1.Font.Name); + end; + TelIni.Free; + newAGWPort = AGWPort; + newAGWServ = AGWServ; + newKISSPort = KISSPort; + newKISSServ = KISSServ; + + RX_SampleRate = RX_SR+RX_SR*0.000001*RX_PPM; + TX_SampleRate = TX_SR+TX_SR*0.000001*TX_PPM; + + panels[4] = Monitor1.Checked; + panels[3] = Statustable1.Checked; + panels[2] = Firstwaterfall1.Checked; + panels[1] = Secondwaterfall1.Checked; + + if tx_bufcount>255 then tx_bufcount = 255; + if tx_bufcount<2 then tx_bufcount = 2; + if rx_bufcount>255 then rx_bufcount = 255; + if rx_bufcount<2 then rx_bufcount = 2; + + if not (diddles in [0..2]) then diddles = 0; + + if nr_monitor_lines>65535 then nr_monitor_lines = 65535; + if nr_monitor_lines<10 then nr_monitor_lines = 10; + + if not (MainPriority in [0..3]) then MainPriority = 2; + + for snd_ch = 1 to 2 do + begin + + tx_hitoneraise[snd_ch] = power(10,-abs(tx_hitoneraisedb[snd_ch])/20); + + if IPOLL[snd_ch]<0 then IPOLL[snd_ch] = 0; + if IPOLL[snd_ch]>65535 then IPOLL[snd_ch] = 65535; + + if MEMRecovery[snd_ch]<1 then MEMRecovery[snd_ch] = 1; + if MEMRecovery[snd_ch]>65535 then MEMRecovery[snd_ch] = 65535; + + get_exclude_list(AnsiUpperCase(MyDigiCall[snd_ch]),list_digi_callsigns[snd_ch]); + get_exclude_list(AnsiUpperCase(exclude_callsigns[snd_ch]),list_exclude_callsigns[snd_ch]); + get_exclude_frm(exclude_APRS_frm[snd_ch],list_exclude_APRS_frm[snd_ch]); + + if resptime[snd_ch]<0 then resptime[snd_ch] = 0; + if resptime[snd_ch]>65535 then resptime[snd_ch] = 65535; + if persist[snd_ch]>255 then persist[snd_ch] = 255; + if persist[snd_ch]<32 then persist[snd_ch] = 32; + if fracks[snd_ch]<1 then fracks[snd_ch] = 1; + if frack_time[snd_ch]<1 then frack_time[snd_ch] = 1; + if idletime[snd_ch]0 then + for k = 0 to numdevs-1 do + begin + waveOutGetDevCaps(k,@DevOutCaps,sizeof(DevOutCaps)); + TXDevList.Add(DevOutCaps.szpname); + end; + numdevs = WaveInGetNumDevs; + if numdevs>0 then + for k = 0 to numdevs-1 do + begin + waveInGetDevCaps(k,@DevInCaps,sizeof(DevInCaps)); + RXDevList.Add(DevInCaps.szpname); + end; + // TX Dev + if (snd_tx_device<0) or (snd_tx_device> = TXDevList.Count) then snd_tx_device = 0; + if TXDevList.Count>0 then + if TXDevList.Strings[snd_tx_device]<>snd_tx_device_name then + begin + i = TXDevList.IndexOf(snd_tx_device_name); + if i> = 0 then snd_tx_device = i else snd_tx_device_name = TXDevList.Strings[snd_tx_device]; + end; + // RX Dev + if (snd_rx_device<0) or (snd_rx_device> = RXDevList.Count) then snd_rx_device = 0; + if RXDevList.Count>0 then + if RXDevList.Strings[snd_rx_device]<>snd_rx_device_name then + begin + i = RXDevList.IndexOf(snd_rx_device_name); + if i> = 0 then snd_rx_device = i else snd_rx_device_name = RXDevList.Strings[snd_rx_device]; + end; + RXDevList.Free; + TXDevList.Free; +end; + +procedure TForm1.startrx; +var + OpenResult MMRESULT; + Loop integer; + ErrorText string; +Begin + RX_device = TRUE; + RXBufferLength = rx_bufsize * Channels * (BitsPerSample div 8); + with WaveFormat do + begin + wFormatTag = WAVE_FORMAT_PCM; + nChannels = Channels; + nSamplesPerSec = RX_SR; + nAvgBytesPerSec = RX_SR * Channels * (BitsPerSample div 8); + nBlockAlign = Channels * (BitsPerSample div 8); + wBitsPerSample = BitsPerSample; + cbSize = 0; + end; + OpenResult = waveInOpen (@WaveInHandle,SND_RX_DEVICE,@WaveFormat,integer(Self.Handle),0,CALLBACK_WINDOW); + if OpenResult = MMSYSERR_NOERROR then + begin + for Loop = 1 to rx_bufcount do + begin + GetMem(RX_pbuf[Loop], RXBufferLength); + RX_header[Loop].lpData = RX_pbuf[Loop]; + RX_header[Loop].dwBufferLength = RXBufferLength; + RX_header[Loop].dwUser = Loop; + RX_header[Loop].dwFlags = 0; + RX_header[Loop].dwLoops = 0; + OpenResult = WaveInPrepareHeader(WaveInhandle, @RX_header[Loop], sizeof(TWaveHdr)); + if OpenResult = MMSYSERR_NOERROR then WaveInAddBuffer(WaveInHandle, @RX_header[Loop], sizeof(TWaveHdr)) + else + begin + case OpenResult of + MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; + MMSYSERR_NODRIVER ErrorText = "no device driver present"; + MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; + else ErrorText = "unknown error"; + end; + MessageDlg(format("Error adding buffer %d device (%s)",[Loop, ErrorText]), mtError, [mbOk], 0); + end; + end; + WaveInStart(WaveInHandle); + end + else + begin + case OpenResult of + MMSYSERR_ERROR ErrorText = "unspecified error"; + MMSYSERR_BADDEVICEID ErrorText = "device ID out of range"; + MMSYSERR_NOTENABLED ErrorText = "driver failed enable"; + MMSYSERR_ALLOCATED ErrorText = "device already allocated"; + MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; + MMSYSERR_NODRIVER ErrorText = "no device driver present"; + MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; + MMSYSERR_NOTSUPPORTED ErrorText = "function isn""t supported"; + MMSYSERR_BADERRNUM ErrorText = "error value out of range"; + MMSYSERR_INVALFLAG ErrorText = "invalid flag passed"; + MMSYSERR_INVALPARAM ErrorText = "invalid parameter passed"; + MMSYSERR_HANDLEBUSY ErrorText = "handle being used simultaneously on another thread (eg callback)"; + MMSYSERR_INVALIDALIAS ErrorText = "specified alias not found"; + MMSYSERR_BADDB ErrorText = "bad registry database"; + MMSYSERR_KEYNOTFOUND ErrorText = "registry key not found"; + MMSYSERR_READERROR ErrorText = "registry read error"; + MMSYSERR_WRITEERROR ErrorText = "registry write error"; + MMSYSERR_DELETEERROR ErrorText = "registry delete error"; + MMSYSERR_VALNOTFOUND ErrorText = "registry value not found"; + MMSYSERR_NODRIVERCB ErrorText = "driver does not call DriverCallback"; + else ErrorText = "unknown error"; + end; + MessageDlg(format("Error opening wave input device (%s)",[ErrorText]), mtError, [mbOk], 0); + RX_device = FALSE; + end; +end; + +procedure TForm1.stoprx; +var + Loop integer; +begin + if not RX_device then exit; + WaveInStop(WaveInHandle); + WaveInReset(WaveInHandle); + for Loop = 1 to rx_bufcount do + WaveInUnPrepareHeader(WaveInHandle, @RX_header[Loop], sizeof(TWaveHdr)); + WaveInClose(WaveInHandle); + for Loop = 1 to rx_bufcount do + begin + if RX_pbuf[Loop]<>nil then + begin + FreeMem(RX_pbuf[Loop]); + RX_pbuf[Loop] = nil; + end; + end; + RX_device = FALSE; +end; + +procedure TForm1.make_wave_buf(snd_ch byte; buf PChar); +const + amplitude = 22000; +var + i word; +begin + modulator(snd_ch,audio_buf[snd_ch],tx_bufsize); + if tx_status[snd_ch] = TX_NO_DATA then buf_status[snd_ch] = BUF_EMPTY; + for i = 0 to tx_bufsize-1 do + begin + case snd_ch of + 1 + begin + // left channel + PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]); + Inc(PSmallInt(Buf)); + // right channel + if SCO then PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]) else PSmallInt(buf)^ = 0; + Inc(PSmallInt(Buf)); + end; + 2 + begin + // left channel + if SCO then PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]) else PSmallInt(buf)^ = 0; + Inc(PSmallInt(Buf)); + // right channel + PSmallInt(buf)^ = round(amplitude*audio_buf[snd_ch][i]); + Inc(PSmallInt(Buf)); + end; + end; + end; +end; + +procedure TForm1.starttx(snd_ch byte); +var + OpenResult MMRESULT; + Loop integer; + ErrorText string; + BufferLength longint; +Begin + if snd_status[snd_ch]<>SND_IDLE then exit; + BufferLength = tx_bufsize * Channels * (BitsPerSample div 8); + with WaveFormat do + begin + wFormatTag = WAVE_FORMAT_PCM; + nChannels = Channels; + nSamplesPerSec = TX_SR; + nAvgBytesPerSec = TX_SR * Channels * (BitsPerSample div 8); + nBlockAlign = Channels * (BitsPerSample div 8); + wBitsPerSample = BitsPerSample; + cbSize = 0; + end; + OpenResult = WaveOutOpen (@WaveOutHandle[snd_ch],SND_TX_DEVICE,@WaveFormat,integer(Self.Handle),0,CALLBACK_WINDOW); + if OpenResult = MMSYSERR_NOERROR then + begin + snd_status[snd_ch] = SND_TX; + buf_status[snd_ch] = BUF_FULL; + tx_status[snd_ch] = TX_SILENCE; + tx_buf_num[snd_ch] = 0; + tx_buf_num1[snd_ch] = 0; + for Loop = 1 to tx_bufcount do + begin + GetMem(TX_pbuf[snd_ch][Loop], BufferLength); + TX_header[snd_ch][Loop].lpData = TX_pbuf[snd_ch][Loop]; + TX_header[snd_ch][Loop].dwBufferLength = BufferLength; + TX_header[snd_ch][Loop].dwUser = 0; + TX_header[snd_ch][Loop].dwFlags = 0; + TX_header[snd_ch][Loop].dwLoops = 0; + OpenResult = WaveOutPrepareHeader(WaveOuthandle[snd_ch], @TX_header[snd_ch][Loop], sizeof(TWaveHdr)); + if OpenResult = MMSYSERR_NOERROR then + begin + // Заполнить буфер на передачу + if buf_status[snd_ch] = BUF_FULL then + begin + make_wave_buf(snd_ch,TX_pbuf[snd_ch][Loop]); + WaveOutWrite(WaveOutHandle[snd_ch],@TX_header[snd_ch][Loop],sizeof(TWaveHdr)); + inc(tx_buf_num1[snd_ch]); + end; + end + else + begin + case OpenResult of + MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; + MMSYSERR_NODRIVER ErrorText = "no device driver present"; + MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; + else ErrorText = "unknown error"; + end; + MessageDlg(format("Error adding buffer %d device (%s)",[Loop, ErrorText]), mtError, [mbOk], 0); + end; + end; + end + else + begin + case OpenResult of + MMSYSERR_ERROR ErrorText = "unspecified error"; + MMSYSERR_BADDEVICEID ErrorText = "device ID out of range"; + MMSYSERR_NOTENABLED ErrorText = "driver failed enable"; + MMSYSERR_ALLOCATED ErrorText = "device already allocated"; + MMSYSERR_INVALHANDLE ErrorText = "device handle is invalid"; + MMSYSERR_NODRIVER ErrorText = "no device driver present"; + MMSYSERR_NOMEM ErrorText = "memory allocation error, could be incorrect samplerate"; + MMSYSERR_NOTSUPPORTED ErrorText = "function isn""t supported"; + MMSYSERR_BADERRNUM ErrorText = "error value out of range"; + MMSYSERR_INVALFLAG ErrorText = "invalid flag passed"; + MMSYSERR_INVALPARAM ErrorText = "invalid parameter passed"; + MMSYSERR_HANDLEBUSY ErrorText = "handle being used simultaneously on another thread (eg callback)"; + MMSYSERR_INVALIDALIAS ErrorText = "specified alias not found"; + MMSYSERR_BADDB ErrorText = "bad registry database"; + MMSYSERR_KEYNOTFOUND ErrorText = "registry key not found"; + MMSYSERR_READERROR ErrorText = "registry read error"; + MMSYSERR_WRITEERROR ErrorText = "registry write error"; + MMSYSERR_DELETEERROR ErrorText = "registry delete error"; + MMSYSERR_VALNOTFOUND ErrorText = "registry value not found"; + MMSYSERR_NODRIVERCB ErrorText = "driver does not call DriverCallback"; + else ErrorText = "unknown error"; + end; + MessageDlg(format("Error opening wave output device (%s)",[ErrorText]), mtError, [mbOk], 0); + end; +end; + +procedure TForm1.stoptx(snd_ch byte); +var + Loop integer; +begin + if snd_status[snd_ch]<>SND_TX then exit; + WaveOutReset(WaveOutHandle[snd_ch]); + for Loop = 1 to tx_bufcount do + WaveOutUnPrepareHeader(WaveOutHandle[snd_ch], @TX_header[snd_ch][Loop], sizeof(TWaveHdr)); + WaveOutClose(WaveOutHandle[snd_ch]); + for Loop = 1 to tx_bufcount do + begin + if TX_pbuf[snd_ch][Loop]<>nil then + begin + FreeMem(TX_pbuf[snd_ch][Loop]); + TX_pbuf[snd_ch][Loop] = nil; + end; + end; + WaveOutHandle[snd_ch] = 0; + snd_status[snd_ch] = SND_IDLE; +end; + +procedure show_grid_title; +const + title array [0..10] of string = ("MyCall","DestCall","Status","Sent pkts","Sent bytes","Rcvd pkts","Rcvd bytes","Rcvd FC","CPS TX","CPS RX","Direction"); +var + i byte; +begin + for i = 0 to 10 do Form1.StringGrid1.Cells[i,0] = title[i]; +end; +*/ +/* + +procedure disp1(src1,src2 array of single); +var + i,n word; + k,k1,amp1,amp2,amp3,amp4 single; + bm TBitMap; +begin + bm = TBitMap.Create; + bm.pixelformat = pf32bit; + //bm.pixelformat = pf24bit; + bm.Width = Form1.PaintBox2.Width; + bm.Height = Form1.PaintBox2.Height; + amp1 = 0; + amp3 = 0; + //k = 0.20; + k = 50000; + k1 = 0; + //k = 1000; + //k = 0.00001; + bm.Canvas.MoveTo(0,50); + bm.Canvas.LineTo(512,50); + n = 0; + for i = 0 to RX_Bufsize-1 do + begin + begin + amp2 = src1[i]; + amp4 = src2[i]; + bm.Canvas.Pen.Color = clRed; + bm.Canvas.MoveTo(n,50-round(amp1*k1)); + bm.Canvas.LineTo(n+1,50-round(amp2*k1)); + bm.Canvas.Pen.Color = clBlue; + bm.Canvas.MoveTo(n,50-round(amp3*k)); + bm.Canvas.LineTo(n+1,50-round(amp4*k)); + bm.Canvas.Pen.Color = clBlack; + inc(n); + amp1 = amp2; + amp3 = amp4; + end; + end; + Form1.PaintBox2.Canvas.Draw(0,0,bm); + bm.Free; +end; +*/ + +/* + +procedure TForm1.wf_pointer(snd_ch byte); +var + x single; + x1,x2,y,k,pos1,pos2,pos3 word; +begin + k = 24; + x = FFTSize/RX_SampleRate; + pos1 = round((rx_freq[snd_ch]-0.5*rx_shift[snd_ch])*x)-5; + pos2 = round((rx_freq[snd_ch]+0.5*rx_shift[snd_ch])*x)-5; + pos3 = round(rx_freq[snd_ch]*x); + x1 = pos1+5; + x2 = pos2+5; + y = k+5; + with bm3.Canvas do + begin + Draw(0,20,bm[snd_ch]); + Pen.Color = clWhite; + Brush.Color = clRed; + Polygon([Point(x1+3,y),Point(x1,y-7),Point(x1-3,y),Point(x2+3,y),Point(x2,y-7),Point(x2-3,y)]); + Brush.Color = clBlue; + Polygon([Point(x1+3,y),Point(x1,y+7),Point(x1-3,y),Point(x2+3,y),Point(x2,y+7),Point(x2-3,y)]); + Polyline([Point(pos3,k+1),Point(pos3,k+9)]); + Pen.Color = clBlack; + end; + case snd_ch of + 1 PaintBox1.Canvas.Draw(0,0,bm3); + 2 PaintBox3.Canvas.Draw(0,0,bm3); + end; +end; + +procedure TForm1.wf_Scale; +var + k single; + max_freq,x,i word; +begin + max_freq = round(RX_SampleRate*0.005); + k = 100*FFTSize/RX_SampleRate; + with bm1.Canvas do + begin + Brush.Color = clBlack; + FillRect(ClipRect); + Pen.Color = clWhite; + Font.Color = clWhite; + Font.Size = 8; + for i = 0 to max_freq do + begin + x = round(k*i); + if x<1025 then + begin + if (i mod 5) = 0 then + PolyLine([Point(x,20),Point(x,13)]) + else + PolyLine([Point(x,20),Point(x,16)]); + if (i mod 10) = 0 then TextOut(x-12,1,inttostr(i*100)); + end; + end; + Pen.Color = clBlack; + end; + bm3.Canvas.Draw(0,0,bm1); +end; +*/ + +void chk_snd_buf(float * buf, int len) +{ + word i; + boolean good; + single prev_amp; + + if (len < 2) + return; + + good = FALSE; + i = 1; + prev_amp = buf[0]; + do + { + if (buf[i++] != prev_amp) + good = TRUE; + + } while (good == FALSE && i < len); + + // Make noise + if (!good) + for (i = 0; i < len; i++) + buf[i] = rand(); +} + +#ifdef WIN32 + +typedef void *HANDLE; +typedef unsigned long DWORD; + +#define WINAPI __stdcall +__declspec(dllimport) +DWORD +WINAPI +WaitForSingleObject( + __in HANDLE hHandle, + __in DWORD dwMilliseconds +); + + + + +#define pthread_t uintptr_t + +uintptr_t _beginthread(void(__cdecl *start_address)(void *), unsigned stack_size, void *arglist); +#else + +#include + +pthread_t _beginthread(void(*start_address)(void *), unsigned stack_size, void * arglist) +{ + pthread_t thread; + + if (pthread_create(&thread, NULL, (void * (*)(void *))start_address, (void*)arglist) != 0) + perror("New Thread"); + + return thread; +} + +#endif + +void runModemthread(void * param); + +void runModems() +{ + int snd_ch, res; + pthread_t thread[4] = { 0,0,0,0 }; + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + if (soundChannel[snd_ch] == 0) // Unused channed + continue; + + if (modem_mode[snd_ch] == MODE_ARDOP) + continue; // Processed above + + // do we need to do this again ?? +// make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); + + if (multiCore) // Run modems in separate threads + thread[snd_ch] = _beginthread(runModemthread, 0, (void *)(size_t)snd_ch); + else + runModemthread((void *)(size_t)snd_ch); + } + + if (multiCore) + { +#ifdef WIN32 + if (thread[0]) WaitForSingleObject(&thread[0], 2000); + if (thread[1]) WaitForSingleObject(&thread[1], 2000); + if (thread[2]) WaitForSingleObject(&thread[2], 2000); + if (thread[3]) WaitForSingleObject(&thread[3], 2000); +#else + if (thread[0]) pthread_join(thread[0], &res); + if (thread[1]) pthread_join(thread[1], &res); + if (thread[2]) pthread_join(thread[2], &res); + if (thread[3]) pthread_join(thread[3], &res); +#endif + } +} + +Byte rcvr_idx; + +void runModemthread(void * param) +{ + int snd_ch = (int)(size_t)param; + + // I want to run lowest to highest to simplify my display + + int offset = -(RCVR[snd_ch] * rcvr_offset[snd_ch]); // lowest + int lastrx = RCVR[snd_ch] * 2; + + if (soundChannel[snd_ch] == 0) // Unused channed + return; + + for (rcvr_idx = 0; rcvr_idx <= lastrx; rcvr_idx++) + { + active_rx_freq[snd_ch] = rxOffset + chanOffset[snd_ch] + rx_freq[snd_ch] + offset; + offset += rcvr_offset[snd_ch]; + + Demodulator(snd_ch, rcvr_idx, src_buf[modemtoSoundLR[snd_ch]], rcvr_idx == lastrx, offset == 0); + } +} + +// I think this processes a buffer of samples + +void BufferFull(short * Samples, int nSamples) // These are Stereo Samples +{ + word i, i1; + Byte snd_ch, rcvr_idx; + int buf_offset; + int Needed; + short * data1; + short * data2 = 0; + + // if UDP server active send as UDP Datagram + + if (UDPServ) // Extract just left + { + short Buff[1024]; + + i1 = 0; + + for (i = 0; i < rx_bufsize; i++) + { + Buff[i] = Samples[i1]; + i1 += 2; + } + + sendSamplestoUDP(Buff, 512, TXPort); + } + + // Do RSID processing (can we also use this for waterfall?? + + RSIDProcessSamples(Samples, nSamples); + + // Do FFT on every 4th buffer (2048 samples) + + // if in Satellite Mode look for a Tuning signal + +// if (SatelliteMode) +// { +// doTuning(Samples, nSamples); +// } + + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + if (soundChannel[snd_ch] == 0) // Unused channed + continue; + + if (pnt_change[snd_ch]) + { + make_core_BPF(snd_ch, rx_freq[snd_ch], bpf[snd_ch]); + make_core_TXBPF(snd_ch, tx_freq[snd_ch], txbpf[snd_ch]); + pnt_change[snd_ch] = FALSE; + } + + } + + // I don't think we should process RX if either is sending + + if (snd_status[0] != SND_TX && snd_status[1] != SND_TX && snd_status[2] != SND_TX && snd_status[3] != SND_TX) + { + for (snd_ch = 0; snd_ch < 4; snd_ch++) + { + if (soundChannel[snd_ch] == 0) // Unused channed + continue; + + if (modem_mode[snd_ch] == MODE_ARDOP) + { + short ardopbuff[1200]; + i1 = 0; + + for (i = 0; i < rx_bufsize; i++) + { + ardopbuff[i] = Samples[i1]; + i1++; + i1++; + } + + ARDOPProcessNewSamples(ardopbuff, nSamples); + } + } + + // extract mono samples from data. + + data1 = Samples; + + i1 = 0; + + // src_buf[0] is left data,. src_buf[1] right + + // We could skip extracting other channel if only one in use - is it worth it?? + + if (UsingBothChannels) + { + for (i = 0; i < rx_bufsize; i++) + { + src_buf[0][i] = data1[i1]; + i1++; + src_buf[1][i] = data1[i1]; + i1++; + } + } + else if (UsingRight) + { + // Extract just right + + i1 = 1; + + for (i = 0; i < rx_bufsize; i++) + { + src_buf[1][i] = data1[i1]; + i1 += 2; + } + } + else + { + // Extract just left + + for (i = 0; i < rx_bufsize; i++) + { + src_buf[0][i] = data1[i1]; + i1 += 2; + } + } + + // Run modems before waterfall so fft buffer has values from before sync was detected + + runModems(); + + // Do whichever waterfall is needed + + // We need to run the waterfall FFT for the frequency guessing to work + + int FirstWaterfallChan = 0; + short * ptr1 = &fft_buf[0][fftCount]; + short * ptr2 = &fft_buf[1][fftCount]; + + int remainingSamples = rx_bufsize; + + if (UsingLeft == 0) + { + FirstWaterfallChan = 1; + data1++; // to Right Samples + } + + if (UsingBothChannels) // Second is always Right + data2 = &Samples[1]; // to Right Samples + + + // FFT size isn't necessarily a multiple of rx_bufsize, so this is a bit more complicated + // Save FFTSize samples then process. Put the unused bits back in the fft buffer + + // Collect samples for both channels if needed + + Needed = FFTSize - fftCount; + + if (Needed <= rx_bufsize) + { + // add Needed samples to fft_buf and process. Copy rest to start of fft_buf + + for (i = 0; i < Needed; i++) + { + *ptr1++ = *data1; + data1 += 2; + } + + doWaterfall(FirstWaterfallChan); + + if (data2) + { + for (i = 0; i < Needed; i++) + { + *ptr2++ = *data2; + data2 += 2; + } + doWaterfall(1); + } + + remainingSamples = rx_bufsize - Needed; + fftCount = 0; + + ptr1 = &fft_buf[0][0]; + ptr2 = &fft_buf[1][0]; + } + + for (i = 0; i < remainingSamples; i++) + { + *ptr1++ = *data1; + data1 += 2; + } + + if (data2) + { + for (i = 0; i < remainingSamples; i++) + { + *ptr2++ = *data2; + data2 += 2; + } + } + fftCount += remainingSamples; + + } + + + if (TimerEvent == TIMER_EVENT_ON) + { + timer_event(); +// timer_event2(); + } +} + + /* + +procedure TForm1.BufferFull1(var Msg TMessage); +var + i,snd_ch byte; +begin + for snd_ch = 1 to 2 do + if pnt_change[snd_ch] then + begin + make_core_BPF(snd_ch,rx_freq[snd_ch],bpf[snd_ch]); + make_core_TXBPF(snd_ch,tx_freq[snd_ch],txbpf[snd_ch]); + pnt_change[snd_ch] = FALSE; + end; + snd_ch = 0; + for i = 1 to 2 do if msg.WParam = WaveOutHandle[i] then snd_ch = i; + if (snd_ch = 0) then exit; + if (snd_status[snd_ch]<>SND_TX) then exit; + inc(tx_buf_num[snd_ch]); if tx_buf_num[snd_ch]>tx_bufcount then tx_buf_num[snd_ch] = 1; + if (buf_status[snd_ch] = BUF_EMPTY) and (tx_buf_num[snd_ch] = tx_buf_num1[snd_ch]) then TX2RX(snd_ch); + if buf_status[snd_ch] = BUF_FULL then + beginf + make_wave_buf(snd_ch,TX_pbuf[snd_ch][tx_buf_num[snd_ch]]); + WaveOutWrite(WaveOutHandle[snd_ch],@TX_header[snd_ch][tx_buf_num[snd_ch]],sizeof(TWaveHdr)); + inc(tx_buf_num1[snd_ch]); if tx_buf_num1[snd_ch]>tx_bufcount then tx_buf_num1[snd_ch] = 1; + end; +end; + +procedure TForm1.TX2RX(snd_ch byte); +begin + if snd_status[snd_ch] = SND_TX then stoptx(snd_ch); + if snd_status[snd_ch] = SND_IDLE then begin pttoff(snd_ch); end; +end; +*/ + + +// Monitor Code - from moncode.asm + + +#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND +#define RESP 2 // CURRENT MSG IS RESPONSE +#define VER1 1 // CURRENT MSG IS VERSION 1 + + +#define UI 3 +#define SABM 0x2F +#define DISC 0x43 +#define DM 0x0F +#define UA 0x63 +#define FRMR 0x87 +#define RR 1 +#define RNR 5 +#define REJ 9 + +#define SREJ 0x0D +#define SABME 0x6F +#define XID 0xAF +#define TEST 0xE3 + + +#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE + +#define NETROM_PID 0xCF +#define IP_PID 0xCC +#define ARP_PID 0xCD + +#define NODES_SIG 0xFF + +char FrameData[1024] = ""; + +char * frame_monitor(string * frame, char * code, int tx_stat) +{ + char mon_frm[512]; + char AGW_path[256]; + string * AGW_data; + + const Byte * frm = "???"; + Byte * datap; + Byte _data[512] = ""; + Byte * p_data = _data; + int _datalen; + + char agw_port; + char CallFrom[10], CallTo[10], Digi[80]; + + char TR = 'R'; + char codestr[64] = ""; + + integer i; + char time_now[32]; + int len; + + AGWUser * AGW; + + Byte pid, nr, ns, f_type, f_id; + Byte rpt, cr, pf; + Byte path[80]; + char c; + const char * p; + + string * data = newString(); + + if (code[0] && strlen(code) < 60) + sprintf(codestr, "[%s]", code); + + if (tx_stat) + TR = 'T'; + + decode_frame(frame->Data, frame->Length, path, data, &pid, &nr, &ns, &f_type, &f_id, &rpt, &pf, &cr); + + datap = data->Data; + + len = data->Length; + + // if (pid == 0xCF) + // data = parse_NETROM(data, f_id); + // IP parsing + // else if (pid == 0xCC) + // data = parse_IP(data); + // ARP parsing + // else if (pid == 0xCD) + // data = parse_ARP(data); + // + + if (len > 0) + { + for (i = 0; i < len; i++) + { + if (datap[i] > 31 || datap[i] == 13 || datap[i] == 9) + *(p_data++) = datap[i]; + } + } + + _datalen = p_data - _data; + + if (_datalen) + { + Byte * ptr = _data; + i = 0; + + // remove successive cr or cr on end while (i < _datalen) + + while (i < _datalen) + { + if ((_data[i] == 13) && (_data[i + 1] == 13)) + i++; + else + *(ptr++) = _data[i++]; + } + + if (*(ptr - 1) == 13) + ptr--; + + *ptr = 0; + + _datalen = ptr - _data; + } + + get_monitor_path(path, CallTo, CallFrom, Digi); + + if (cr) + { + c = 'C'; + if (pf) + p = " P"; + else p = ""; + } + else + { + c = 'R'; + if (pf) + p = " F"; + else + p = ""; + } + + switch (f_id) + { + case I_I: + + frm = "I"; + break; + + case S_RR: + + frm = "RR"; + break; + + case S_RNR: + + frm = "RNR"; + break; + + case S_REJ: + + frm = "REJ"; + break; + + case S_SREJ: + + frm = "SREJ"; + break; + + case U_SABM: + + frm = "SABM"; + break; + + case SABME: + + frm = "SABME"; + break; + + case U_DISC: + + frm = "DISC"; + break; + + case U_DM: + + frm = "DM"; + break; + + case U_UA: + + frm = "UA"; + break; + + case U_FRMR: + + frm = "FRMR"; + break; + + case U_UI: + + frm = "UI"; + } + + if (Digi[0]) + sprintf(AGW_path, "Fm %s To %s Via %s <%s %c%s",CallFrom, CallTo, Digi, frm, c, p); + else + sprintf(AGW_path, "Fm %s To %s <%s %c %s", CallFrom, CallTo, frm, c, p); + + + switch (f_type) + { + case I_FRM: + + //mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' S' + inttostr(ns) + ' pid=' + dec2hex(pid) + ' Len=' + inttostr(len) + ' >' + time_now + #13 + _data + #13#13; + sprintf(mon_frm, "%s R%d S%d pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, nr, ns, pid, len, ShortDateTime(), TR, codestr, _data); + + break; + + case U_FRM: + + if (f_id == U_UI) + { + sprintf(mon_frm, "%s pid=%X Len=%d>[%s%c]%s\r%s\r", AGW_path, pid, len, ShortDateTime(), TR, codestr, _data); // "= AGW_path + ctrl + '>' + time_now + #13; + } + else if (f_id == U_FRMR) + { + sprintf(mon_frm, "%s>%02x %02x %02x[%s]\r", AGW_path, datap[0], datap[1], datap[2], ShortDateTime()); // "= AGW_path + ctrl + '>' + time_now + #13; + } + else + sprintf(mon_frm, "%s>[%s%c]%s\r", AGW_path, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13; + + break; + + case S_FRM: + + // mon_frm = AGW_path + ctrl + ' R' + inttostr(nr) + ' >' + time_now + #13; + sprintf(mon_frm, "%s R%d>[%s%c]%s\r", AGW_path, nr, ShortDateTime(), TR, codestr); // "= AGW_path + ctrl + '>' + time_now + #13; + + break; + + } + sprintf(FrameData, "%s", mon_frm); + return FrameData; +} + + +/* +procedure TForm1.RX2TX(snd_ch byte); +begin + if snd_status[snd_ch] = SND_IDLE then begin ptton(snd_ch); starttx(snd_ch); end; +end; + +function TForm1.frame_monitor(s,code string; tx_stat boolean) string; +var + len word; + s_tx_stat string; + time_now,s1,c,p string; + callfrom,callto,digi,path,data,frm string; + frm_body string; + pid,nr,ns,f_type,f_id byte; + rpt,cr,pf boolean; + i word; +begin + decode_frame(s,path,data,pid,nr,ns,f_type,f_id,rpt,pf,cr); + len = length(data); + // NETROM parsing + if pid = $CF then data = parse_NETROM(data,f_id); + // IP parsing + if pid = $CC then data = parse_IP(data); + // ARP parsing + if pid = $CD then data = parse_ARP(data); + // + get_monitor_path(path,CallTo,CallFrom,Digi); + if cr then + begin + c = "C"; + if pf then p = " P" else p = ""; + end + else + begin + c = "R"; + if pf then p = " F" else p = ""; + end; + frm = "UNKN"; + case f_id of + I_I frm = "I"; + S_RR frm = "RR"; + S_RNR frm = "RNR"; + S_REJ frm = "REJ"; + U_SABM frm = "SABM"; + U_DISC frm = "DISC"; + U_DM frm = "DM"; + U_UA frm = "UA"; + U_FRMR frm = "FRMR"; + U_UI frm = "UI"; + end; + case tx_stat of + TRUE s_tx_stat = "T"; + FALSE s_tx_stat = "R"; + end; + s1 = ""; + + if code<>"" then code = " ["+code+"]"; + if UTC_Time then time_now = " [UTC"+get_UTC_time+s_tx_stat+"]" + else time_now = " ["+FormatDateTime("hhmmss",now)+s_tx_stat+"]"; + + if digi = "" then frm_body = "Fm "+CallFrom+" To "+CallTo+" <"+frm+" "+c+p + else frm_body = "Fm "+CallFrom+" To "+CallTo+" Via "+Digi+" <"+frm+" "+c+p; + case f_type of + I_FRM frm_body = frm_body+" R"+inttostr(nr)+" S"+inttostr(ns)+" Pid = "+dec2hex(pid)+" Len = "+inttostr(len)+">"+time_now+code+#13+data; + U_FRM if f_id = U_UI then frm_body = frm_body+" Pid = "+dec2hex(pid)+" Len = "+inttostr(len)+">"+time_now+code+#13+data + else if f_id = U_FRMR then begin data = copy(data+#0#0#0,1,3); frm_body = frm_body+">"+time_now+code+#13+inttohex((byte(data[1]) shl 16) or (byte(data[2]) shl 8) or byte(data[3]),6) end + else frm_body = frm_body+">"+time_now+code; + S_FRM frm_body = frm_body+" R"+inttostr(nr)+">"+time_now+code; + end; + for i = 1 to length(frm_body) do + begin + if frm_body[i]>#31 then s1 = s1+frm_body[i]; + if frm_body[i] = #13 then s1 = s1+#13#10; + if frm_body[i] = #10 then s1 = s1+""; + if frm_body[i] = #9 then s1 = s1+#9; + end; + result = s1; +end; + +procedure TForm1.waterfall_init; +begin + bm[1] = TBitMap.Create; + bm[2] = TBitMap.Create; + bm1 = TBitMap.Create; + bm2 = TBitMap.Create; + bm3 = TBitMap.Create; + bm[1].pixelformat = pf32bit; + bm[2].pixelformat = pf32bit; + bm1.pixelformat = pf32bit; + bm2.pixelformat = pf32bit; + bm3.pixelformat = pf32bit; + bm[1].Height = PaintBox1.Height-20; + bm[1].Width = PaintBox1.width; + bm[2].Height = PaintBox1.Height-20; + bm[2].Width = PaintBox1.width; + bm1.Height = 20; + bm1.Width = PaintBox1.width; + bm3.Height = PaintBox1.Height; + bm3.Width = PaintBox1.width; +end; + +procedure TForm1.waterfall_free; +begin + bm[1].Free; + bm[2].Free; + bm1.Free; + bm2.Free; + bm3.Free; +end; + +procedure TForm1.StartAGW; +begin + try + ServerSocket1.Port = AGWPort; + ServerSocket1.Active = AGWServ; + except + ServerSocket1.Active = FALSE; + MessageDlg("AGW host port is busy!", mtWarning,[mbOk],0); + end; +end; + +procedure TForm1.StartKISS; +begin + try + ServerSocket2.Port = KISSPort; + ServerSocket2.Active = KISSServ; + except + ServerSocket2.Active = FALSE; + MessageDlg("KISS port is busy!", mtWarning,[mbOk],0); + end; +end; + +procedure fft_window_init; +var + mag single; + i word; +begin + mag = 2*pi/(FFTSize-1); + for i = 0 to FFTSize-1 do fft_window_arr[i] = 0.5-0.5*cos(i*mag); //hann +end; + +procedure TForm1.FormCreate(Sender TObject); +begin + if hPrevInst <> 0 then begin + MessageDlg("Программа уже запущена!", mtError, [mbOk], 0); + Application.Terminate; + end; + RS = TReedSolomon.Create(Self); + MainThreadHandle = GetCurrentThread; + form1.Caption = MODEM_CAPTION+" - Ver "+MODEM_VERSION; + cur_dir = ExtractFilePath(Application.ExeName); + fft_window_init; + detector_init; + kiss_init; + agw_init; + ax25_init; + init_raduga; + waterfall_init; + ReadIni; + show_combobox; + show_grid_title; + show_mode_panels; + show_panels; + wf_pointer(1); + wf_pointer(2); + wf_Scale; + ChangePriority; + Visible = TRUE; + StartAGW; + StartKISS; + PTTOpen; + startrx; + TimerEvent = TIMER_EVENT_OFF; + if (debugmode and DEBUG_TIMER) = 0 then create_timer1; + if MinOnStart then WindowState = wsMinimized; +end; + +procedure TForm1.TrackBar1Change(Sender TObject); +begin + dcd_threshold = TrackBar1.position; +end; +*/ + +void Timer_Event2() +{ + if (TimerStat2 == TIMER_BUSY || TimerStat2 == TIMER_OFF) + return; + + TimerStat2 = TIMER_BUSY; + +// show_grid(); + + /* + + if (mod_icon_status = MOD_WAIT) then inc(icon_timer); + if icon_timer = 10 then mod_icon_status = MOD_IDLE; + if (mod_icon_status<>MOD_WAIT) and (mod_icon_status<>last_mod_icon_status) then + begin + icon_timer = 0; + case mod_icon_status of + MOD_IDLE form1.CoolTrayIcon1.IconIndex = 0; + MOD_RX begin form1.CoolTrayIcon1.IconIndex = 1; mod_icon_status = MOD_WAIT; end; + MOD_TX form1.CoolTrayIcon1.IconIndex = 2; + end; + last_mod_icon_status = mod_icon_status; + end; + //*/ + + TimerStat2 = TIMER_FREE; +} + +/* + +procedure TimeProc1(uTimerId, uMesssage UINT; dwUser, dw1, dw2 DWORD); stdcall; +begin + TimerEvent = TIMER_EVENT_ON; +end; + +procedure TForm1.create_timer1; +var + TimeEpk cardinal; +begin + TimeEpk = 100; + TimerId1 = TimeSetEvent(TimeEpk,0,@TimeProc1,0,TIME_PERIODIC); +end; + +procedure TForm1.free_timer1; +begin + TimerStat1 = TIMER_OFF; + timeKillEvent(TimerId1); +end; + +*/ + +/* + + +procedure TForm1.PaintBox1MouseMove(Sender TObject; Shift TShiftState; X, + Y Integer); +var + low,high word; +begin + if CheckBox1.Checked then exit; + if not mouse_down[1] then Exit; + rx_freq[1] = round(x*RX_SampleRate/FFTSize); + low = round(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1]+1); + high = round(rx_samplerate/2-(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1])); + if (rx_freq[1]-low)<0 then rx_freq[1] = low; + if (high-rx_freq[1])<0 then rx_freq[1] = high; + tx_freq[1] = rx_freq[1]; + show_freq_a; +end; + +procedure TForm1.PaintBox3MouseMove(Sender TObject; Shift TShiftState; X, + Y Integer); +var + low,high word; +begin + if CheckBox1.Checked then exit; + if not mouse_down[2] then Exit; + rx_freq[2] = round(x*RX_SampleRate/FFTSize); + low = round(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2]+1); + high = round(rx_samplerate/2-(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2])); + if (rx_freq[2]-low)<0 then rx_freq[2] = low; + if (high-rx_freq[2])<0 then rx_freq[2] = high; + tx_freq[2] = rx_freq[2]; + show_freq_b; +end; + +procedure TForm1.PaintBox1MouseUp(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); +begin + mouse_down[1] = FALSE; +end; + +procedure TForm1.PaintBox3MouseUp(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); +begin + mouse_down[2] = FALSE; +end; + +procedure TForm1.PaintBox1MouseDown(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); +var + low,high word; +begin + if CheckBox1.Checked then exit; + if not (ssLeft in shift) then Exit; + mouse_down[1] = TRUE; + rx_freq[1] = round(x*RX_SampleRate/FFTSize); + low = round(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1]+1); + high = round(rx_samplerate/2-(rx_shift[1]/2+Rcvr[1]*rcvr_offset[1])); + if (rx_freq[1]-low)<0 then rx_freq[1] = low; + if (high-rx_freq[1])<0 then rx_freq[1] = high; + tx_freq[1] = rx_freq[1]; + show_freq_a; +end; + +procedure TForm1.PaintBox3MouseDown(Sender TObject; Button TMouseButton; + Shift TShiftState; X, Y Integer); +var + low,high word; +begin + if CheckBox1.Checked then exit; + if not (ssLeft in shift) then Exit; + mouse_down[2] = TRUE; + rx_freq[2] = round(x*RX_SampleRate/FFTSize); + low = round(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2]+1); + high = round(rx_samplerate/2-(rx_shift[2]/2+Rcvr[2]*rcvr_offset[2])); + if (rx_freq[2]-low)<0 then rx_freq[2] = low; + if (high-rx_freq[2])<0 then rx_freq[2] = high; + tx_freq[2] = rx_freq[2]; + show_freq_b; +end; + +procedure TForm1.ServerSocket1ClientRead(Sender TObject; + Socket TCustomWinSocket); +var + s string; +begin + s = Socket.ReceiveText; + AGW_explode_frame(Socket.sockethandle,s); +end; + +procedure TForm1.ServerSocket1ClientDisconnect(Sender TObject; + Socket TCustomWinSocket); +begin + del_incoming_mycalls_by_sock(socket.SocketHandle); + AGW_del_socket(socket.SocketHandle); +end; + +procedure TForm1.ServerSocket1ClientError(Sender TObject; + Socket TCustomWinSocket; ErrorEvent TErrorEvent; + var ErrorCode Integer); +begin + del_incoming_mycalls_by_sock(socket.SocketHandle); + AGW_del_socket(socket.SocketHandle); + ErrorCode = 0; +end; + +procedure TForm1.ServerSocket1ClientConnect(Sender TObject; + Socket TCustomWinSocket); +begin + agw_add_socket(Socket.sockethandle); +end; + +procedure TForm1.OutputVolume1Click(Sender TObject); +var + s string; +begin + s = "SndVol32.exe -D"+inttostr(SND_TX_DEVICE); + WinExec(pchar(s),SW_SHOWNORMAL); +end; + +procedure TForm1.InputVolume1Click(Sender TObject); +var + s string; +begin + s = "SndVol32.exe -R -D"+inttostr(SND_RX_DEVICE); + WinExec(pchar(s),SW_SHOWNORMAL); +end; + +procedure TForm1.CoolTrayIcon1Click(Sender TObject); +begin + CoolTrayIcon1.ShowMainForm; +end; + +procedure TForm1.CoolTrayIcon1Cycle(Sender TObject; NextIndex Integer); +begin + CoolTrayIcon1.IconIndex = 2; + CoolTrayIcon1.CycleIcons = FALSE; +end; + +procedure TForm1.ABout1Click(Sender TObject); +begin + Form2.ShowModal; +end; + +procedure TForm1.put_frame(snd_ch byte; frame,code string; tx_stat,excluded boolean); +var + s string; +begin + if RxRichedit1.Focused then Windows.SetFocus(0); + if code = "NON-AX25" then + s = inttostr(snd_ch)+" ["+FormatDateTime("hhmmss",now)+"R]" + else + s = inttostr(snd_ch)+""+frame_monitor(frame,code,tx_stat); + //RxRichedit1.Lines.BeginUpdate; + RxRichedit1.SelStart = length(RxRichedit1.text); + RxRichEdit1.SelLength = length(s); + case tx_stat of + TRUE RxRichEdit1.SelAttributes.Color = clMaroon; + FALSE RxRichEdit1.SelAttributes.Color = clBlack; + end; + if excluded then RxRichEdit1.SelAttributes.Color = clGreen; + RxRichedit1.SelText = s+#10; + if RxRichedit1.Lines.Count>nr_monitor_lines then + repeat + RxRichedit1.Lines.Delete(0); + until RxRichedit1.Lines.Count = nr_monitor_lines; + RxRichedit1.HideSelection = FALSE; + RxRichedit1.SelStart = length(RxRichedit1.text); + RxRichedit1.SelLength = 0; + RxRichedit1.HideSelection = TRUE; + //RxRichedit1.Lines.EndUpdate; +end; + +procedure TForm1.show_mode_panels; +begin + panel8.Align = alNone; + panel6.Align = alNone; + if dualchan then panel6.Visible = TRUE else panel6.Visible = FALSE; + panel8.Align = alLeft; + panel6.Align = alLeft; +end; + +procedure TForm1.show_panels; +var + i byte; +begin + panel1.Align = alNone; + panel2.Align = alNone; + panel3.Align = alNone; + panel4.Align = alNone; + panel5.Align = alNone; + for i = 1 to 5 do + case i of + 1 panel1.Visible = panels[i]; + 2 panel5.Visible = panels[i]; + 3 panel3.Visible = panels[i]; + 4 panel4.Visible = panels[i]; + 5 panel2.Visible = panels[i]; + end; + panel1.Align = alBottom; + panel5.Align = alBottom; + panel3.Align = alBottom; + panel2.Align = alTop; + panel4.Align = alClient; +end; + +procedure TForm1.Secondwaterfall1Click(Sender TObject); +begin + case Secondwaterfall1.Checked of + TRUE Secondwaterfall1.Checked = FALSE; + FALSE Secondwaterfall1.Checked = TRUE; + end; + panels[1] = Secondwaterfall1.Checked; + show_panels; +end; + + + +procedure TForm1.Firstwaterfall1Click(Sender TObject); +begin + case Firstwaterfall1.Checked of + TRUE Firstwaterfall1.Checked = FALSE; + FALSE Firstwaterfall1.Checked = TRUE; + end; + panels[2] = Firstwaterfall1.Checked; + show_panels; +end; + +procedure TForm1.Statustable1Click(Sender TObject); +begin + case Statustable1.Checked of + TRUE Statustable1.Checked = FALSE; + FALSE Statustable1.Checked = TRUE; + end; + panels[3] = Statustable1.Checked; + show_panels; +end; + +procedure TForm1.Monitor1Click(Sender TObject); +begin + case Monitor1.Checked of + TRUE Monitor1.Checked = FALSE; + FALSE Monitor1.Checked = TRUE; + end; + panels[4] = Monitor1.Checked; + show_panels; +end; + +procedure TForm1.Devices1Click(Sender TObject); +begin + if (ptt = "EXT") or (ptt = "CAT") then Form3.Button3.Enabled = TRUE else Form3.Button3.Enabled = FALSE; + Form3.GetDeviceInfo; + form3.ShowModal; +end; + +procedure TForm1.FormPaint(Sender TObject); +begin + RxRichedit1.HideSelection = FALSE; + RxRichedit1.SelStart = length(RxRichedit1.text); + RxRichedit1.SelLength = 0; + RxRichedit1.HideSelection = TRUE; +end; + +procedure TForm1.Filters1Click(Sender TObject); +begin + Form5.Show_modem_settings; +end; + +procedure TForm1.Clearmonitor1Click(Sender TObject); +begin + RxRichEdit1.Clear; + frame_count = 0; + single_frame_count = 0; +end; + +procedure TForm1.Copytext1Click(Sender TObject); +begin + RxRichEdit1.CopyToClipboard; +end; + +procedure TForm1.ApplicationEvents1Minimize(Sender TObject); +begin + if stop_wf then w_state = WIN_MINIMIZED; +end; + +procedure TForm1.ApplicationEvents1Restore(Sender TObject); +begin + w_state = WIN_MAXIMIZED; +end; + +procedure TForm1.ServerSocket2ClientConnect(Sender TObject; + Socket TCustomWinSocket); +begin + KISS_add_stream(socket.sockethandle); +end; + +procedure TForm1.ServerSocket2ClientDisconnect(Sender TObject; + Socket TCustomWinSocket); +begin + KISS_del_stream(socket.sockethandle); +end; + +procedure TForm1.ServerSocket2ClientError(Sender TObject; + Socket TCustomWinSocket; ErrorEvent TErrorEvent; + var ErrorCode Integer); +begin + KISS_del_stream(socket.sockethandle); + ErrorCode = 0; +end; + +procedure TForm1.ServerSocket2ClientRead(Sender TObject; + Socket TCustomWinSocket); +var + data string; +begin + data = socket.ReceiveText; + KISS_on_data_in(socket.sockethandle,data); +end; + +procedure TForm1.Font1Click(Sender TObject); +begin + FontDialog1.Font = RXRichEdit1.Font; + if FontDialog1.Execute then + begin + RXRichEdit1.SelStart = 0; + RXRichEdit1.SelLength = Length(RXRichEdit1.Text); + RXRichEdit1.SelAttributes.Size = FontDialog1.Font.Size; + RXRichEdit1.SelAttributes.Name = FontDialog1.Font.Name; + RXRichEdit1.Font.Size = FontDialog1.Font.Size; + RXRichEdit1.Font.Name = FontDialog1.Font.Name; + WriteIni; + end; +end; + +procedure TForm1.Calibration1Click(Sender TObject); +begin + Form6.ShowModal; +end; + +procedure TForm1.ComboBox1Change(Sender TObject); +begin + Speed[1] = get_idx_by_name(ComboBox1.Text); + init_speed(1); + windows.setfocus(0); +end; + +procedure TForm1.ComboBox1KeyDown(Sender TObject; var Key Word; + Shift TShiftState); +begin + key = 0; + windows.SetFocus(0); +end; + +procedure TForm1.ComboBox1KeyPress(Sender TObject; var Key Char); +begin + key = #0; + windows.SetFocus(0); +end; + +procedure TForm1.ComboBox2Change(Sender TObject); +begin + Speed[2] = get_idx_by_name(ComboBox2.Text); + init_speed(2); + windows.setfocus(0); +end; + +procedure TForm1.FormDestroy(Sender TObject); +var + snd_ch byte; +begin + stoprx; + for snd_ch = 1 to 2 do if snd_status[snd_ch] = SND_TX then stoptx(snd_ch); + if (debugmode and DEBUG_TIMER) = 0 then free_timer1; + TimerStat2 = TIMER_OFF; + PTTClose; + ax25_free; + agw_free; + kiss_free; + detector_free; + RS.Free; + waterfall_free; + WriteIni; +end; + +end. +*/ \ No newline at end of file diff --git a/soundmodem.ico b/soundmodem.ico new file mode 100644 index 0000000000000000000000000000000000000000..c8243311c2ccb529cb00645c570b6e6a4c56bf60 GIT binary patch literal 766 zcmZQzU<5)11py$*!tjELfkBLcfk6X^6@b_Qh(Y4$KmaPh!p6hU($>PzbL}H!sNp+WzrO|3IAyhJ|HKL@i)w12 z^2`agwIE;s<~xAG@B$DpFmOQ32ipL)1IR~r&woZqK`4+E0J-;r9E>jrbS@Au!uVim zpaP&nLBJIvjW7|cdWaG~7#R40QN#X#fq@wx2B`sJexQ2!KMV}^{}>qRfk8^NuL1V1 B!^;2w literal 0 HcmV?d00001 diff --git a/tcpCode.cpp b/tcpCode.cpp new file mode 100644 index 0000000..97d5ab0 --- /dev/null +++ b/tcpCode.cpp @@ -0,0 +1,759 @@ +/* +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 "QtSoundModem.h" +#include "UZ7HOStuff.h" +#include + +#define CONNECT(sndr, sig, rcvr, slt) connect(sndr, SIGNAL(sig), rcvr, SLOT(slt)) + +QList _KISSSockets; +QList _AGWSockets; + +QTcpServer * _KISSserver; +QTcpServer * _AGWserver; + +extern workerThread *t; +extern mynet m1; + +extern "C" int KISSPort; +extern "C" void * initPulse(); +extern "C" int SoundMode; + +extern "C" int UDPClientPort; +extern "C" int UDPServerPort; +extern "C" int TXPort; + +char UDPHost[64] = ""; + +int UDPServ = 0; // UDP Server Active (ie broadcast sound frams as UDP packets) + +QMutex mutex; + +extern void saveSettings(); + +extern int Closing; // Set to stop background thread + +extern "C" +{ + void KISSDataReceived(void * sender, char * data, int length); + void AGW_explode_frame(void * soket, char * data, int len); + void KISS_add_stream(void * Socket); + void KISS_del_socket(void * Socket); + void AGW_add_socket(void * Socket); + void AGW_del_socket(void * socket); + void Debugprintf(const char * format, ...); + int InitSound(BOOL Report); + void soundMain(); + void MainLoop(); + void set_speed(int snd_ch, int Modem); + void init_speed(int snd_ch); + +} + +extern "C" int nonGUIMode; + +QTimer *timer; +QTimer *timercopy; + +void mynet::start() +{ + if (SoundMode == 3) + OpenUDP(); + + if (UDPServ) + OpenUDP(); + + if (KISSServ) + { + _KISSserver = new(QTcpServer); + + if (_KISSserver->listen(QHostAddress::Any, KISSPort)) + connect(_KISSserver, SIGNAL(newConnection()), this, SLOT(onKISSConnection())); + else + { + if (nonGUIMode) + Debugprintf("Listen failed for KISS Port"); + else + { + QMessageBox msgBox; + msgBox.setText("Listen failed for KISS Port."); + msgBox.exec(); + } + } + } + + if (AGWServ) + { + _AGWserver = new(QTcpServer); + if (_AGWserver->listen(QHostAddress::Any, AGWPort)) + connect(_AGWserver, SIGNAL(newConnection()), this, SLOT(onAGWConnection())); + else + { + if (nonGUIMode) + Debugprintf("Listen failed for AGW Port"); + else + { + QMessageBox msgBox; + msgBox.setText("Listen failed for AGW 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); +} + +void mynet::MyTimerSlot() +{ + // 100 mS Timer Event + + TimerEvent = TIMER_EVENT_ON; +} + + +void mynet::onAGWConnection() +{ + QTcpSocket *clientSocket = _AGWserver->nextPendingConnection(); + connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onAGWReadyRead())); + connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onAGWSocketStateChanged(QAbstractSocket::SocketState))); + + _AGWSockets.push_back(clientSocket); + + AGW_add_socket(clientSocket); + + Debugprintf("AGW Connect Sock %x", clientSocket); +} + + + +void mynet::onAGWSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + if (socketState == QAbstractSocket::UnconnectedState) + { + QTcpSocket* sender = static_cast(QObject::sender()); + + AGW_del_socket(sender); + + _AGWSockets.removeOne(sender); + } +} + +void mynet::onAGWReadyRead() +{ + QTcpSocket* sender = static_cast(QObject::sender()); + QByteArray datas = sender->readAll(); + + AGW_explode_frame(sender, datas.data(), datas.length()); +} + + +void mynet::onKISSConnection() +{ + QTcpSocket *clientSocket = _KISSserver->nextPendingConnection(); + connect(clientSocket, SIGNAL(readyRead()), this, SLOT(onKISSReadyRead())); + connect(clientSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onKISSSocketStateChanged(QAbstractSocket::SocketState))); + + _KISSSockets.push_back(clientSocket); + + KISS_add_stream(clientSocket); + + Debugprintf("KISS Connect Sock %x", clientSocket); +} + +void mynet::onKISSSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + if (socketState == QAbstractSocket::UnconnectedState) + { + QTcpSocket* sender = static_cast(QObject::sender()); + + KISS_del_socket(sender); + + _KISSSockets.removeOne(sender); + } +} + +void mynet::onKISSReadyRead() +{ + QTcpSocket* sender = static_cast(QObject::sender()); + QByteArray datas = sender->readAll(); + + KISSDataReceived(sender, datas.data(), datas.length()); +} + + + +void mynet::displayError(QAbstractSocket::SocketError socketError) +{ + if (socketError == QTcpSocket::RemoteHostClosedError) + return; + + qDebug() << tcpClient->errorString(); + + tcpClient->close(); + tcpServer->close(); +} + + + +void mynet::sendtoKISS(void * sock, unsigned char * Msg, int Len) +{ + if (sock == NULL) + { + for (QTcpSocket* socket : _KISSSockets) + { + socket->write((char *)Msg, Len); + } + } + else + { + QTcpSocket* socket = (QTcpSocket*)sock; + socket->write((char *)Msg, Len); + } +// free(Msg); +} + + + +QTcpSocket * HAMLIBsock; +int HAMLIBConnected = 0; +int HAMLIBConnecting = 0; + +void mynet::HAMLIBdisplayError(QAbstractSocket::SocketError socketError) +{ + switch (socketError) + { + case QAbstractSocket::RemoteHostClosedError: + break; + + case QAbstractSocket::HostNotFoundError: + if (nonGUIMode) + qDebug() << "HAMLIB host was not found. Please check the host name and port settings."; + else + { + QMessageBox::information(nullptr, tr("QtSM"), + tr("HAMLIB host was not found. Please check the " + "host name and port settings.")); + } + + break; + + case QAbstractSocket::ConnectionRefusedError: + + qDebug() << "HAMLIB Connection Refused"; + break; + + default: + + qDebug() << "HAMLIB Connection Failed"; + break; + + } + + HAMLIBConnecting = 0; + HAMLIBConnected = 0; +} + +void mynet::HAMLIBreadyRead() +{ + unsigned char Buffer[4096]; + QTcpSocket* Socket = static_cast(QObject::sender()); + + // read the data from the socket. Don't do anyhing with it at the moment + + Socket->read((char *)Buffer, 4095); +} + +void mynet::onHAMLIBSocketStateChanged(QAbstractSocket::SocketState socketState) +{ + if (socketState == QAbstractSocket::UnconnectedState) + { + // Close any connections + + HAMLIBConnected = 0; + qDebug() << "HAMLIB Connection Closed"; + } + else if (socketState == QAbstractSocket::ConnectedState) + { + HAMLIBConnected = 1; + HAMLIBConnecting = 0; + qDebug() << "HAMLIB Connected"; + } +} + + +void mynet::ConnecttoHAMLIB() +{ + delete(HAMLIBsock); + + HAMLIBConnected = 0; + HAMLIBConnecting = 1; + + HAMLIBsock = new QTcpSocket(); + + connect(HAMLIBsock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(HAMLIBdisplayError(QAbstractSocket::SocketError))); + connect(HAMLIBsock, SIGNAL(readyRead()), this, SLOT(HAMLIBreadyRead())); + connect(HAMLIBsock, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onHAMLIBSocketStateChanged(QAbstractSocket::SocketState))); + + HAMLIBsock->connectToHost(HamLibHost, HamLibPort); + + return; +} + +extern "C" void HAMLIBSetPTT(int PTTState) +{ + // Won't work in non=gui mode + + emit m1.HLSetPTT(PTTState); +} + +extern "C" void startTimer(int Time) +{ + // Won't work in non=gui mode + + emit m1.startTimer(Time); +} + +void mynet::dostartTimer(int Time) +{ + timercopy->start(Time); +} + +extern "C" void stopTimer() +{ + // Won't work in non=gui mode + + emit m1.stopTimer(); +} + +void mynet::dostopTimer() +{ + timercopy->stop(); +} + +void mynet::doHLSetPTT(int c) +{ + char Msg[16]; + + if (HAMLIBsock == nullptr || HAMLIBsock->state() != QAbstractSocket::ConnectedState) + ConnecttoHAMLIB(); + + sprintf(Msg, "T %d\r\n", c); + HAMLIBsock->write(Msg); + + HAMLIBsock->waitForBytesWritten(30000); + + QByteArray datas = HAMLIBsock->readAll(); + + qDebug(datas.data()); + +} + + + + + +extern "C" void KISSSendtoServer(void * sock, Byte * Msg, int Len) +{ + emit t->sendtoKISS(sock, Msg, Len); +} + + +void workerThread::run() +{ + if (SoundMode == 2) // Pulse + { + if (initPulse() == nullptr) + { + if (nonGUIMode) + { + qDebug() << "PulseAudio requested but pulseaudio libraries not found\nMode set to ALSA\n"; + } + else + { + QMessageBox msgBox; + msgBox.setText("PulseAudio requested but pulseaudio libraries not found\nMode set to ALSA"); + msgBox.exec(); + } + SoundMode = 0; + saveSettings(); + } + } + + soundMain(); + + if (SoundMode != 3) + { + if (!InitSound(1)) + { + // QMessageBox msgBox; + // msgBox.setText("Open Sound Card Failed"); + // msgBox.exec(); + } + } + + // Initialise Modems + + init_speed(0); + init_speed(1); + init_speed(2); + init_speed(3); + + // emit t->openSockets(); + + while (Closing == 0) + { + // Run scheduling loop + + MainLoop(); + + this->msleep(10); + } + + qDebug() << "Saving Settings"; + + saveSettings(); + + qDebug() << "Main Loop exited"; + + qApp->exit(); + +}; + +// Audio over UDP Code. + +// Code can either send audio blocks from the sound card as UDP packets or use UDP packets from +// a suitable source (maybe another copy of QtSM) and process them instead of input frm a sound card/ + +// ie act as a server or client for UDP audio. + +// of course we need bidirectional audio, so even when we are a client we send modem generated samples +// to the server and as a server pass received smaples to modem + +// It isn't logical to run as both client and server, so probably can use just one socket + + +QUdpSocket * udpSocket; + +qint64 udpServerSeqno= 0; +qint64 udpClientLastSeq = 0; +qint64 udpServerLastSeq = 0; +int droppedPackets = 0; +extern "C" int UDPSoundIsPlaying; + +QQueue queue; + + +void mynet::OpenUDP() +{ + udpSocket = new QUdpSocket(); + + if (UDPServ) + { + udpSocket->bind(QHostAddress("0.0.0.0"), UDPServerPort); + QTimer *timer = new QTimer(this); + timercopy = timer; + connect(timer, SIGNAL(timeout()), this, SLOT(dropPTT())); + } + else + udpSocket->bind(QHostAddress("0.0.0.0"), UDPClientPort); + + connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readPendingDatagrams())); +} + +extern "C" void Flush(); + +void mynet::dropPTT() +{ + timercopy->stop(); + + if (UDPSoundIsPlaying) + { + // Drop PTT when all sent + + Flush(); + UDPSoundIsPlaying = 0; + Debugprintf("PTT Off"); + RadioPTT(0, 0); + } +} + +void mynet::readPendingDatagrams() +{ + while (udpSocket->hasPendingDatagrams()) + { + QHostAddress Addr; + quint16 rxPort; + char copy[1501]; + + // We expect datagrams of 1040 bytes containing a 16 byte header and 512 16 bit samples + // We should get a datagram every 43 mS. We need to use a timeout to drop PTT if running as server + + if (UDPServ) + timercopy->start(200); + + int Len = udpSocket->readDatagram(copy, 1500, &Addr, &rxPort); + + if (Len == 1040) + { + qint64 Seq; + + memcpy(&Seq, copy, sizeof(udpServerSeqno)); + + if (Seq < udpClientLastSeq || udpClientLastSeq == 0) + + // Client or Server Restarted + + udpClientLastSeq = Seq; + + else + { + int Missed = Seq - udpClientLastSeq; + + if (Missed > 100) // probably stopped in debug + Missed = 1; + + while (--Missed) + { + droppedPackets++; + + // insert silence to maintain timing + + unsigned char * pkt = (unsigned char *)malloc(1024); + + memset(pkt, 0, 1024); + + mutex.lock(); + queue.append(pkt); + mutex.unlock(); + + } + } + + udpClientLastSeq = Seq; + + unsigned char * pkt = (unsigned char *)malloc(1024); + + memcpy(pkt, ©[16], 1024); + + mutex.lock(); + queue.append(pkt); + mutex.unlock(); + } + } +} + +void mynet::socketError() +{ + char errMsg[80]; + sprintf(errMsg, "%d %s", udpSocket->state(), udpSocket->errorString().toLocal8Bit().constData()); + // qDebug() << errMsg; + // QMessageBox::question(NULL, "ARDOP GUI", errMsg, QMessageBox::Yes | QMessageBox::No); +} + +extern "C" void sendSamplestoStdout(short * Samples, int nSamples) +{ + +} + + +extern "C" void sendSamplestoUDP(short * Samples, int nSamples, int Port) +{ + if (udpSocket == nullptr) + return; + + unsigned char txBuff[1048]; + + memcpy(txBuff, &udpServerSeqno, sizeof(udpServerSeqno)); + udpServerSeqno++; + + if (nSamples > 512) + nSamples = 512; + + nSamples <<= 1; // short to byte + + memcpy(&txBuff[16], Samples, nSamples); + + udpSocket->writeDatagram((char *)txBuff, nSamples + 16, QHostAddress(UDPHost), Port); +} + +static int min = 0, max = 0, lastlevelGUI = 0, lastlevelreport = 0; + +static UCHAR CurrentLevel = 0; // Peak from current samples + +extern "C" int SoundIsPlaying; +extern "C" short * SendtoCard(short * buf, int n); +extern "C" short * DMABuffer; + + +extern "C" void ProcessNewSamples(short * Samples, int nSamples); + + +extern "C" void UDPPollReceivedSamples() +{ + if (queue.isEmpty()) + return; + + short * ptr; + short * save; + + // If we are using UDP for output (sound server) send samples to sound card. + // If for input (virtual sound card) then pass to modem + + if (UDPServ) + { + // We only get packets if TX active (sound VOX) so raise PTT and start sending + // ?? do we ignore if local modem is already sending ?? + + if (SoundIsPlaying) + { + mutex.lock(); + save = ptr = (short *)queue.dequeue(); + mutex.unlock(); + free(save); + } + + if (UDPSoundIsPlaying == 0) + { + // Wait for a couple of packets to reduce risk of underrun (but not too many or delay will be excessive + + if (queue.count() < 3) + return; + + UDPSoundIsPlaying = 1; + Debugprintf("PTT On"); + RadioPTT(0, 1); // UDP only use one channel + + /// !! how do we drop ptt ?? + } + + while (queue.count() > 1) + { + short * outptr = DMABuffer; + boolean dropPTT1 = 1; + boolean dropPTT2 = 1; + + // We get mono samples but soundcard expects stereo + // Sound card needs 1024 samples so send two packets + + mutex.lock(); + save = ptr = (short *)queue.dequeue(); + mutex.unlock(); + + for (int n = 0; n < 512; n++) + { + *(outptr++) = *ptr; + *(outptr++) = *ptr++; // Duplicate + if (*ptr) + dropPTT1 = 0; // Drop PTT if all samples zero + } + + free(save); + + mutex.lock(); + save = ptr = (short *)queue.dequeue(); + mutex.unlock(); + + for (int n = 0; n < 512; n++) + { + *(outptr++) = *ptr; + *(outptr++) = *ptr++; // Duplicate + if (*ptr) + dropPTT2 = 0; // Drop PTT if all samples zero + } + + free(save); + + if (dropPTT1 && dropPTT2) + { + startTimer(1); // All zeros so no need to send + return; + } + + DMABuffer = SendtoCard(DMABuffer, 1024); + + if (dropPTT2) // 2nd all zeros + startTimer(1); + } + return; + } + + mutex.lock(); + save = ptr = (short *)queue.dequeue(); + mutex.unlock(); + + // We get mono samples but modem expects stereo + + short Buff[2048]; + short * inptr = (short *)ptr; + short * outptr = Buff; + + int i; + + for (i = 0; i < ReceiveSize; i++) + { + if (*(ptr) < min) + min = *ptr; + else if (*(ptr) > max) + max = *ptr; + ptr++; + } + + CurrentLevel = ((max - min) * 75) / 32768; // Scale to 150 max + + if ((Now - lastlevelGUI) > 2000) // 2 Secs + { + lastlevelGUI = Now; + + if ((Now - lastlevelreport) > 10000) // 10 Secs + { + char HostCmd[64]; + lastlevelreport = Now; + + sprintf(HostCmd, "INPUTPEAKS %d %d", min, max); + Debugprintf("Input peaks = %d, %d", min, max); + } + + min = max = 0; + } + + for (int n = 0; n < 512; n++) + { + *(outptr++) = *inptr; + *(outptr++) = *inptr++; // Duplicate + } + + ProcessNewSamples(Buff, 512); + free(save); +} + + + + + + diff --git a/tcpCode.h b/tcpCode.h new file mode 100644 index 0000000..8d15a05 --- /dev/null +++ b/tcpCode.h @@ -0,0 +1,76 @@ +#include +#include +//#include + +#define CONNECT(sndr, sig, rcvr, slt) connect(sndr, SIGNAL(sig), rcvr, SLOT(slt)) + +class mynet : public QObject +{ + Q_OBJECT + +signals: + + void HLSetPTT(int c); + void startTimer(int Time); + void stopTimer(); + +public: + void start(); + void OpenUDP(); + + +public slots: + void onAGWReadyRead(); + void onKISSSocketStateChanged(QAbstractSocket::SocketState socketState); + void onKISSReadyRead(); + void onAGWSocketStateChanged(QAbstractSocket::SocketState socketState); + void onKISSConnection(); + void MyTimerSlot(); + void onAGWConnection(); + void dropPTT(); + + void displayError(QAbstractSocket::SocketError socketError); + + void sendtoKISS(void * sock, unsigned char * Msg, int Len); + + void HAMLIBdisplayError(QAbstractSocket::SocketError socketError); + void HAMLIBreadyRead(); + void onHAMLIBSocketStateChanged(QAbstractSocket::SocketState socketState); + void ConnecttoHAMLIB(); + void dostartTimer(int Time); + void dostopTimer(); + void doHLSetPTT(int c); + + void readPendingDatagrams(); + void socketError(); + +private: + QTcpServer* tcpServer; + QTcpSocket* tcpClient; + QTcpSocket* tcpServerConnection; + int bytesToWrite; + int bytesWritten; + int bytesReceived; + int TotalBytes; + int PayloadSize; +}; + + +class workerThread : public QThread +{ + Q_OBJECT +signals: + void updateDCD(int, int); + void sendtoTrace(char *, int); + void sendtoKISS(void *, unsigned char *, int); + void openSockets(); + +private: + void run(); +public: + +}; + + + +

SytT<7qq*)CArEw#G@Qel2k0^nYCLRDWQcIL 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