/* Copyright (C) 2019-2020 Andrei Kopanchuk UZ7HO This file is part of QtSoundModem QtSoundModem is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. QtSoundModem is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with QtSoundModem. If not, see http://www.gnu.org/licenses */ // UZ7HO Soundmodem Port by John Wiseman G8BPQ // UZ7HO Soundmodem Port // Not Working 4psk100 FEC // Thoughts on Waterfall Display. // Original used a 2048 sample FFT giving 5.859375 Hz bins. We plotted 1024 points, giving a 0 to 6000 specrum // If we want say 300 to 3300 we need about half the bin size so twice the fft size. But should we also fit required range to window size? // Unless we resize the most displayed bit of the screen in around 900 pixels. So each bin should be 3300 / 900 = 3.66667 Hz or a FFT size of around 3273 #include "QtSoundModem.h" #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "UZ7HOStuff.h" #include QImage *Constellation[4]; QImage *Waterfall = 0; QLabel *DCDLabel[4]; QLineEdit *chanOffsetLabel[4]; QImage *DCDLed[4]; QImage *RXLevel; QImage *RXLevel2; QLabel *WaterfallCopy; QLabel * RXLevelCopy; QLabel * RXLevel2Copy; QTextEdit * monWindowCopy; extern workerThread *t; extern QtSoundModem * w; extern QCoreApplication * a; QList Ports = QSerialPortInfo::availablePorts(); void saveSettings(); void getSettings(); void DrawModemFreqRange(); extern "C" void CloseSound(); extern "C" void GetSoundDevices(); extern "C" char modes_name[modes_count][21]; extern "C" int speed[5]; extern "C" int KISSPort; extern "C" short rx_freq[5]; extern "C" int CaptureCount; extern "C" int PlaybackCount; extern "C" int CaptureIndex; // Card number extern "C" int PlayBackIndex; extern "C" char CaptureNames[16][256]; extern "C" char PlaybackNames[16][256]; extern "C" int SoundMode; extern "C" int onlyMixSnoop; extern "C" int multiCore; extern "C" int refreshModems; int NeedPSKRefresh; extern "C" int pnt_change[5]; extern "C" int needRSID[4]; extern "C" int needSetOffset[4]; extern "C" float MagOut[4096]; extern "C" float MaxMagOut; extern "C" int MaxMagIndex; extern "C" int using48000; // Set if using 48K sample rate (ie RUH Modem active) extern "C" int ReceiveSize; extern "C" int SendSize; // 100 mS for now extern "C" int txLatency; extern "C" { int InitSound(BOOL Report); void soundMain(); void MainLoop(); void modulator(UCHAR snd_ch, int buf_size); void SampleSink(int LR, short Sample); void doCalib(int Port, int Act); int Freq_Change(int Chan, int Freq); void set_speed(int snd_ch, int Modem); void init_speed(int snd_ch); void FourierTransform(int NumSamples, short * RealIn, float * RealOut, float * ImagOut, int InverseTransform); void dofft(short * in, float * outr, float * outi); void init_raduga(); void DrawFreqTicks(); void AGW_Report_Modem_Change(int port); char * strlop(char * buf, char delim); void sendRSID(int Chan, int dropTX); void RSIDinitfft(); void il2p_init(int il2p_debug); void closeTraceLog(); } void make_graph_buf(float * buf, short tap, QPainter * bitmap); int ModemA = 2; int ModemB = 2; int ModemC = 2; int ModemD = 2; int FreqA = 1500; int FreqB = 1500; int FreqC = 1500; int FreqD = 1500; int DCD = 50; char CWIDCall[128] = ""; extern "C" char CWIDMark[32]; int CWIDInterval = 0; int CWIDLeft = 0; int CWIDRight = 0; int CWIDType = 1; // on/off bool afterTraffic = 0; bool cwidtimerisActive = false; int WaterfallMin = 00; int WaterfallMax = 6000; int Configuring = 0; bool lockWaterfall = false; bool inWaterfall = false; extern "C" int NeedWaterfallHeaders; extern "C" float BinSize; extern "C" { int RSID_SABM[4]; } extern "C" { int RSID_UI[4]; } extern "C" { int RSID_SetModem[4]; } extern "C" unsigned int pskStates[4]; int Closing = FALSE; // Set to stop background thread QRgb white = qRgb(255, 255, 255); QRgb black = qRgb(0, 0, 0); QRgb green = qRgb(0, 255, 0); QRgb red = qRgb(255, 0, 0); QRgb yellow = qRgb(255, 255, 0); QRgb cyan = qRgb(0, 255, 255); QRgb txText = qRgb(192, 0, 0); QRgb rxText = qRgb(0, 0, 192); bool darkTheme = true; bool minimizeonStart = true; // Indexed colour list from ARDOPC #define WHITE 0 #define Tomato 1 #define Gold 2 #define Lime 3 #define Yellow 4 #define Orange 5 #define Khaki 6 #define Cyan 7 #define DeepSkyBlue 8 #define RoyalBlue 9 #define Navy 10 #define Black 11 #define Goldenrod 12 #define Fuchsia 13 QRgb vbColours[16] = { qRgb(255, 255, 255), qRgb(255, 99, 71), qRgb(255, 215, 0), qRgb(0, 255, 0), qRgb(255, 255, 0), qRgb(255, 165, 0), qRgb(240, 240, 140), qRgb(0, 255, 255), qRgb(0, 191, 255), qRgb(65, 105, 225), qRgb(0, 0, 128), qRgb(0, 0, 0), qRgb(218, 165, 32), qRgb(255, 0, 255) }; unsigned char WaterfallLines[2][80][4096] = { 0 }; int NextWaterfallLine[2] = {0, 0}; unsigned int LastLevel = 255; unsigned int LastBusy = 255; extern "C" int UDPClientPort; extern "C" int UDPServerPort; extern "C" int TXPort; extern char UDPHost[64]; QTimer *cwidtimer; QTimer *PTTWatchdog; QWidget * mythis; QElapsedTimer pttOnTimer; 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 modemBoxHeight = 34; int Width = r.width(); int Height = r.height() - 25; int monitorTop; int monitorHeight; int sessionTop; int sessionHeight = 0; int waterfallsTop; int waterfallsHeight = WaterfallImageHeight; ui.modeB->setVisible(soundChannel[1]); ui.centerB->setVisible(soundChannel[1]); ui.labelB->setVisible(soundChannel[1]); DCDLabel[1]->setVisible(soundChannel[1]); ui.RXOffsetB->setVisible(soundChannel[1]); ui.modeC->setVisible(soundChannel[2]); ui.centerC->setVisible(soundChannel[2]); ui.labelC->setVisible(soundChannel[2]); DCDLabel[2]->setVisible(soundChannel[2]); ui.RXOffsetC->setVisible(soundChannel[2]); ui.modeD->setVisible(soundChannel[3]); ui.centerD->setVisible(soundChannel[3]); ui.labelD->setVisible(soundChannel[3]); DCDLabel[3]->setVisible(soundChannel[3]); ui.RXOffsetD->setVisible(soundChannel[3]); if (soundChannel[2] || soundChannel[3]) modemBoxHeight = 60; ui.Waterfall->setVisible(0); monitorTop = modemBoxHeight + 1; // Now have one waterfall label containing headers and waterfalls if (Firstwaterfall || Secondwaterfall) ui.Waterfall->setVisible(1); if (AGWServ) { sessionTable->setVisible(true); sessionHeight = 150; } else { sessionTable->setVisible(false); } // if only displaying one Waterfall, change height of waterfall area if (UsingBothChannels == 0 || (Firstwaterfall == 0) || (Secondwaterfall == 0)) { waterfallsHeight /= 2; } if ((Firstwaterfall == 0) && (Secondwaterfall == 0)) waterfallsHeight = 0; monitorHeight = Height - sessionHeight - waterfallsHeight - modemBoxHeight; waterfallsTop = Height - waterfallsHeight; sessionTop = Height - (sessionHeight + waterfallsHeight); ui.monWindow->setGeometry(QRect(0, monitorTop, Width, monitorHeight)); if (AGWServ) sessionTable->setGeometry(QRect(0, sessionTop, Width, sessionHeight)); if (waterfallsHeight) ui.Waterfall->setGeometry(QRect(0, waterfallsTop, Width, waterfallsHeight + 2)); } QAction * setupMenuLine(QMenu * Menu, char * Label, QObject * parent, int State) { QAction * Act = new QAction(Label, parent); Menu->addAction(Act); Act->setCheckable(true); if (State) Act->setChecked(true); parent->connect(Act, SIGNAL(triggered()), parent, SLOT(menuChecked())); return Act; } void QtSoundModem::menuChecked() { QAction * Act = static_cast(QObject::sender()); int state = Act->isChecked(); if (Act == actWaterfall1) Firstwaterfall = state; else if (Act == actWaterfall2) Secondwaterfall = state; initWaterfall(Firstwaterfall | Secondwaterfall); saveSettings(); } void QtSoundModem::initWaterfall(int state) { if (state == 1) { // if (ui.Waterfall) // { // delete ui.Waterfall; // ui.Waterfall = new QLabel(ui.centralWidget); // } WaterfallCopy = ui.Waterfall; Waterfall = new QImage(1024, WaterfallImageHeight + 2, QImage::Format_RGB32); NeedWaterfallHeaders = 1; } else { delete(Waterfall); Waterfall = 0; } QSize Size(800, 602); // Not actually used, but Event constructor needs it QResizeEvent *event = new QResizeEvent(Size, Size); QApplication::sendEvent(this, event); } QRect PSKRect = { 100,100,100,100 }; QDialog * constellationDialog; QLabel * constellationLabel[4]; QLabel * QualLabel[4]; // Local copies QLabel *RXOffsetLabel; QSlider *RXOffset; QFont Font; extern "C" void CheckPSKWindows() { NeedPSKRefresh = 1; } void DoPSKWindows() { // Display Constellation for PSK Window; int NextX = 0; int i; for (i = 0; i < 4; i++) { if (soundChannel[i] && pskStates[i]) { constellationLabel[i]->setGeometry(QRect(NextX, 19, 121, 121)); QualLabel[i]->setGeometry(QRect(1 + NextX, 1, 120, 15)); constellationLabel[i]->setVisible(1); QualLabel[i]->setVisible(1); NextX += 122; } else { constellationLabel[i]->setVisible(0); QualLabel[i]->setVisible(0); } } constellationDialog->resize(NextX, 140); } QTimer *wftimer; extern "C" struct timespec pttclk; QtSoundModem::QtSoundModem(QWidget *parent) : QMainWindow(parent) { QString family; int csize; QFont::Weight weight; #ifndef WIN32 clock_getres(CLOCK_MONOTONIC, &pttclk); printf("CLOCK_MONOTONIC %d, %d\n", pttclk.tv_sec, pttclk.tv_nsec); #endif ui.setupUi(this); mythis = this; getSettings(); QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); family = mysettings.value("FontFamily", "Courier New").toString(); csize = mysettings.value("PointSize", 0).toInt(); weight = (QFont::Weight)mysettings.value("Weight", 50).toInt(); Font = QFont(family); Font.setPointSize(csize); Font.setWeight(weight); QApplication::setFont(Font); constellationDialog = new QDialog(nullptr, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); constellationDialog->resize(488, 140); constellationDialog->setGeometry(PSKRect); QFont f("Arial", 8, QFont::Normal); for (int i = 0; i < 4; i++) { char Text[16]; sprintf(Text, "Chan %c", i + 'A'); constellationLabel[i] = new QLabel(constellationDialog); constellationDialog->setWindowTitle("PSK Constellations"); QualLabel[i] = new QLabel(constellationDialog); QualLabel[i]->setText(Text); QualLabel[i]->setFont(f); Constellation[i] = new QImage(121, 121, QImage::Format_RGB32); Constellation[i]->fill(black); constellationLabel[i]->setPixmap(QPixmap::fromImage(*Constellation[i])); } constellationDialog->show(); if (MintoTray) { char popUp[256]; sprintf(popUp, "QtSoundModem %d %d", AGWPort, KISSPort); trayIcon = new QSystemTrayIcon(QIcon(":/QtSoundModem/soundmodem.ico"), this); trayIcon->setToolTip(popUp); trayIcon->show(); connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(TrayActivated(QSystemTrayIcon::ActivationReason))); } using48000 = 0; // Set if using 48K sample rate (ie RUH Modem active) ReceiveSize = 512; SendSize = 1024; // 100 mS for now for (int i = 0; i < 4; i++) { if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96)) { using48000 = 1; // Set if using 48K sample rate (ie RUH Modem active) ReceiveSize = 2048; SendSize = 4096; // 100 mS for now } } float FFTCalc = 12000.0f / ((WaterfallMax - WaterfallMin) / 900.0f); FFTSize = FFTCalc + 0.4999; if (FFTSize > 8191) FFTSize = 8190; if (FFTSize & 1) // odd FFTSize--; BinSize = 12000.0 / FFTSize; restoreGeometry(mysettings.value("geometry").toByteArray()); restoreState(mysettings.value("windowState").toByteArray()); sessionTable = new QTableWidget(ui.centralWidget); sessionTable->verticalHeader()->setVisible(FALSE); sessionTable->verticalHeader()->setDefaultSectionSize(20); sessionTable->horizontalHeader()->setDefaultSectionSize(68); sessionTable->setRowCount(1); sessionTable->setColumnCount(12); m_TableHeader << "MyCall" << "DestCall" << "Status" << "Sent pkts" << "Sent Bytes" << "Rcvd pkts" << "Rcvd bytes" << "Rcvd FC" << "FEC corr" << "CPS TX" << "CPS RX" << "Direction"; mysetstyle(); sessionTable->setHorizontalHeaderLabels(m_TableHeader); sessionTable->setColumnWidth(0, 80); sessionTable->setColumnWidth(1, 80); sessionTable->setColumnWidth(4, 76); sessionTable->setColumnWidth(5, 76); sessionTable->setColumnWidth(6, 80); sessionTable->setColumnWidth(11, 72); for (int i = 0; i < modes_count; i++) { ui.modeA->addItem(modes_name[i]); ui.modeB->addItem(modes_name[i]); ui.modeC->addItem(modes_name[i]); ui.modeD->addItem(modes_name[i]); } // Set up Menus setupMenu = ui.menuBar->addMenu(tr("Settings")); actDevices = new QAction("Setup Devices", this); setupMenu->addAction(actDevices); connect(actDevices, SIGNAL(triggered()), this, SLOT(clickedSlot())); actDevices->setObjectName("actDevices"); actModems = new QAction("Setup Modems", this); actModems->setObjectName("actModems"); setupMenu->addAction(actModems); connect(actModems, SIGNAL(triggered()), this, SLOT(clickedSlot())); actFont = new QAction("Setup Font", this); actFont->setObjectName("actFont"); setupMenu->addAction(actFont); connect(actFont, SIGNAL(triggered()), this, SLOT(clickedSlot())); actMintoTray = setupMenu->addAction("Minimize to Tray", this, SLOT(MinimizetoTray())); actMintoTray->setCheckable(1); actMintoTray->setChecked(MintoTray); viewMenu = ui.menuBar->addMenu(tr("&View")); actWaterfall1 = setupMenuLine(viewMenu, (char *)"First waterfall", this, Firstwaterfall); actWaterfall2 = setupMenuLine(viewMenu, (char *)"Second Waterfall", this, Secondwaterfall); actCalib = ui.menuBar->addAction("&Calibration"); connect(actCalib, SIGNAL(triggered()), this, SLOT(doCalibrate())); actRestartWF = ui.menuBar->addAction("Restart Waterfall"); connect(actRestartWF, SIGNAL(triggered()), this, SLOT(doRestartWF())); actAbout = ui.menuBar->addAction("&About"); connect(actAbout, SIGNAL(triggered()), this, SLOT(doAbout())); RXLevel = new QImage(150, 10, QImage::Format_RGB32); RXLevel->fill(white); ui.RXLevel->setPixmap(QPixmap::fromImage(*RXLevel)); RXLevelCopy = ui.RXLevel; RXLevel2 = new QImage(150, 10, QImage::Format_RGB32); RXLevel2->fill(white); ui.RXLevel2->setPixmap(QPixmap::fromImage(*RXLevel2)); RXLevel2Copy = ui.RXLevel2; DCDLabel[0] = new QLabel(this); DCDLabel[0]->setObjectName(QString::fromUtf8("DCDLedA")); DCDLabel[0]->setGeometry(QRect(280, 31, 12, 12)); DCDLabel[0]->setVisible(TRUE); DCDLabel[1] = new QLabel(this); DCDLabel[1]->setObjectName(QString::fromUtf8("DCDLedB")); DCDLabel[1]->setGeometry(QRect(575, 31, 12, 12)); DCDLabel[1]->setVisible(TRUE); DCDLabel[2] = new QLabel(this); DCDLabel[2]->setObjectName(QString::fromUtf8("DCDLedC")); DCDLabel[2]->setGeometry(QRect(280, 61, 12, 12)); DCDLabel[2]->setVisible(FALSE); DCDLabel[3] = new QLabel(this); DCDLabel[3]->setObjectName(QString::fromUtf8("DCDLedD")); DCDLabel[3]->setGeometry(QRect(575, 61, 12, 12)); DCDLabel[3]->setVisible(FALSE); DCDLed[0] = new QImage(12, 12, QImage::Format_RGB32); DCDLed[1] = new QImage(12, 12, QImage::Format_RGB32); DCDLed[2] = new QImage(12, 12, QImage::Format_RGB32); DCDLed[3] = new QImage(12, 12, QImage::Format_RGB32); DCDLed[0]->fill(red); DCDLed[1]->fill(red); DCDLed[2]->fill(red); DCDLed[3]->fill(red); DCDLabel[0]->setPixmap(QPixmap::fromImage(*DCDLed[0])); DCDLabel[1]->setPixmap(QPixmap::fromImage(*DCDLed[1])); DCDLabel[2]->setPixmap(QPixmap::fromImage(*DCDLed[2])); DCDLabel[3]->setPixmap(QPixmap::fromImage(*DCDLed[3])); chanOffsetLabel[0] = ui.RXOffsetA; chanOffsetLabel[1] = ui.RXOffsetB; chanOffsetLabel[2] = ui.RXOffsetC; chanOffsetLabel[3] = ui.RXOffsetD; WaterfallCopy = ui.Waterfall; initWaterfall(Firstwaterfall | Secondwaterfall); monWindowCopy = ui.monWindow; ui.monWindow->document()->setMaximumBlockCount(10000); // connect(ui.monWindow, SIGNAL(selectionChanged()), this, SLOT(onTEselectionChanged())); connect(ui.modeA, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); connect(ui.modeB, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); connect(ui.modeC, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); connect(ui.modeD, SIGNAL(currentIndexChanged(int)), this, SLOT(clickedSlotI(int))); ModemA = speed[0]; ModemB = speed[1]; ModemC = speed[2]; ModemD = speed[3]; ui.modeA->setCurrentIndex(speed[0]); ui.modeB->setCurrentIndex(speed[1]); ui.modeC->setCurrentIndex(speed[2]); ui.modeD->setCurrentIndex(speed[3]); ModemA = ui.modeA->currentIndex(); ui.centerA->setValue(rx_freq[0]); ui.centerB->setValue(rx_freq[1]); ui.centerC->setValue(rx_freq[2]); ui.centerD->setValue(rx_freq[3]); connect(ui.centerA, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); connect(ui.centerB, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); connect(ui.centerC, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); connect(ui.centerD, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); ui.DCDSlider->setValue(dcd_threshold); char valChar[32]; sprintf(valChar, "RX Offset %d", rxOffset); ui.RXOffsetLabel->setText(valChar); ui.RXOffset->setValue(rxOffset); RXOffsetLabel = ui.RXOffsetLabel; RXOffset = ui.RXOffset; connect(ui.DCDSlider, SIGNAL(sliderMoved(int)), this, SLOT(clickedSlotI(int))); connect(ui.RXOffset, SIGNAL(valueChanged(int)), this, SLOT(clickedSlotI(int))); QObject::connect(t, SIGNAL(sendtoTrace(char *, int)), this, SLOT(sendtoTrace(char *, int)), Qt::QueuedConnection); QObject::connect(t, SIGNAL(updateDCD(int, int)), this, SLOT(doupdateDCD(int, int)), Qt::QueuedConnection); QObject::connect(t, SIGNAL(startCWIDTimer()), this, SLOT(startCWIDTimerSlot()), Qt::QueuedConnection); QObject::connect(t, SIGNAL(setWaterfallImage()), this, SLOT(setWaterfallImage()), Qt::QueuedConnection); QObject::connect(t, SIGNAL(setLevelImage()), this, SLOT(setLevelImage()), Qt::QueuedConnection); QObject::connect(t, SIGNAL(setConstellationImage(int, int)), this, SLOT(setConstellationImage(int, int)), Qt::QueuedConnection); QObject::connect(t, SIGNAL(startWatchdog()), this, SLOT(StartWatchdog()), Qt::QueuedConnection); QObject::connect(t, SIGNAL(stopWatchdog()), this, SLOT(StopWatchdog()), Qt::QueuedConnection); connect(ui.RXOffsetA, SIGNAL(returnPressed()), this, SLOT(returnPressed())); connect(ui.RXOffsetB, SIGNAL(returnPressed()), this, SLOT(returnPressed())); connect(ui.RXOffsetC, SIGNAL(returnPressed()), this, SLOT(returnPressed())); connect(ui.RXOffsetD, SIGNAL(returnPressed()), this, SLOT(returnPressed())); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(MyTimerSlot())); timer->start(100); wftimer = new QTimer(this); connect(wftimer, SIGNAL(timeout()), this, SLOT(doRestartWF())); // wftimer->start(1000 * 300); cwidtimer = new QTimer(this); connect(cwidtimer, SIGNAL(timeout()), this, SLOT(CWIDTimer())); PTTWatchdog = new QTimer(this); connect(PTTWatchdog, SIGNAL(timeout()), this, SLOT(PTTWatchdogExpired())); if (CWIDInterval && afterTraffic == false) cwidtimer->start(CWIDInterval * 60000); if (RSID_SetModem[0]) { RSID_WF = 1; RSIDinitfft(); } // il2p_init(1); QTimer::singleShot(200, this, &QtSoundModem::updateFont); } void QtSoundModem::updateFont() { QApplication::setFont(Font); } void QtSoundModem::MinimizetoTray() { MintoTray = actMintoTray->isChecked(); saveSettings(); QMessageBox::about(this, tr("QtSoundModem"), tr("Program must be restarted to change Minimize mode")); } void QtSoundModem::TrayActivated(QSystemTrayIcon::ActivationReason reason) { if (reason == 3) { showNormal(); w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); } } extern "C" void sendCWID(char * strID, BOOL blnPlay, int Chan); extern "C" void checkforCWID() { emit(t->startCWIDTimer()); }; extern "C" void QtSoundModem::startCWIDTimerSlot() { if (CWIDInterval && afterTraffic == 1 && cwidtimerisActive == false) { cwidtimerisActive = true; QTimer::singleShot(CWIDInterval * 60000, this, &QtSoundModem::CWIDTimer); } } void QtSoundModem::CWIDTimer() { cwidtimerisActive = false; sendCWID(CWIDCall, CWIDType, 0); calib_mode[0] = 4; } void extSetOffset(int chan) { char valChar[32]; sprintf(valChar, "%d", chanOffset[chan]); chanOffsetLabel[chan]->setText(valChar); NeedWaterfallHeaders = true; pnt_change[0] = 1; pnt_change[1] = 1; pnt_change[2] = 1; pnt_change[3] = 1; return; } void QtSoundModem::MyTimerSlot() { // 100 mS Timer Event for (int i = 0; i < 4; i++) { if (needSetOffset[i]) { needSetOffset[i] = 0; extSetOffset(i); // Update GUI } } if (refreshModems) { refreshModems = 0; ui.modeA->setCurrentIndex(speed[0]); ui.modeB->setCurrentIndex(speed[1]); ui.modeC->setCurrentIndex(speed[2]); ui.modeD->setCurrentIndex(speed[3]); ui.centerA->setValue(rx_freq[0]); ui.centerB->setValue(rx_freq[1]); ui.centerC->setValue(rx_freq[2]); ui.centerD->setValue(rx_freq[3]); } if (NeedPSKRefresh) { NeedPSKRefresh = 0; DoPSKWindows(); } if (NeedWaterfallHeaders) { NeedWaterfallHeaders = 0; if (Waterfall) { Waterfall->fill(black); DrawModemFreqRange(); DrawFreqTicks(); } } show_grid(); } void QtSoundModem::returnPressed() { char Name[32]; int Chan; QString val; strcpy(Name, sender()->objectName().toUtf8()); Chan = Name[8] - 'A'; val = chanOffsetLabel[Chan]->text(); chanOffset[Chan] = val.toInt(); needSetOffset[Chan] = 1; // Update GUI } void CheckforChanges(int Mode, int OldMode) { int old48000 = using48000; if (OldMode != Mode && Mode == 15) { QMessageBox msgBox; msgBox.setText("Warning!!\nARDOP Packet is NOT the same as ARDOP\n" "It is an experimental mode for sending ax.25 frames using ARDOP packet formats\n"); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.exec(); } // See if need to switch beween 12000 and 48000 using48000 = 0; // Set if using 48K sample rate (ie RUH Modem active) ReceiveSize = 512; SendSize = 1024; // 100 mS for now for (int i = 0; i < 4; i++) { if (soundChannel[i] && (speed[i] == SPEED_RUH48 || speed[i] == SPEED_RUH96)) { using48000 = 1; // Set if using 48K sample rate (ie RUH Modem active) ReceiveSize = 2048; SendSize = 4096; // 100 mS for now } } if (using48000 != old48000) { InitSound(1); } } void QtSoundModem::clickedSlotI(int i) { char Name[32]; strcpy(Name, sender()->objectName().toUtf8()); if (strcmp(Name, "modeA") == 0) { int OldModem = ModemA; ModemA = ui.modeA->currentIndex(); set_speed(0, ModemA); CheckforChanges(ModemA, OldModem); saveSettings(); AGW_Report_Modem_Change(0); return; } if (strcmp(Name, "modeB") == 0) { int OldModem = ModemB; ModemB = ui.modeB->currentIndex(); set_speed(1, ModemB); CheckforChanges(ModemB, OldModem); saveSettings(); AGW_Report_Modem_Change(1); return; } if (strcmp(Name, "modeC") == 0) { int OldModem = ModemC; ModemC = ui.modeC->currentIndex(); set_speed(2, ModemC); CheckforChanges(ModemC, OldModem); saveSettings(); AGW_Report_Modem_Change(2); return; } if (strcmp(Name, "modeD") == 0) { int OldModem = ModemD; ModemD = ui.modeD->currentIndex(); set_speed(3, ModemD); CheckforChanges(ModemD, OldModem); saveSettings(); AGW_Report_Modem_Change(3); return; } if (strcmp(Name, "centerA") == 0) { if (i > 299) { QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); ui.centerA->setValue(Freq_Change(0, i)); settings->setValue("Modem/RXFreq1", ui.centerA->value()); AGW_Report_Modem_Change(0); } return; } if (strcmp(Name, "centerB") == 0) { if (i > 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 > 299) { QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); ui.centerC->setValue(Freq_Change(2, i)); settings->setValue("Modem/RXFreq3", ui.centerC->value()); AGW_Report_Modem_Change(2); } return; } if (strcmp(Name, "centerD") == 0) { if (i > 299) { QSettings * settings = new QSettings("QtSoundModem.ini", QSettings::IniFormat); ui.centerD->setValue(Freq_Change(3, i)); settings->setValue("Modem/RXFreq4", ui.centerD->value()); AGW_Report_Modem_Change(3); } return; } if (strcmp(Name, "DCDSlider") == 0) { dcd_threshold = i; saveSettings(); return; } if (strcmp(Name, "RXOffset") == 0) { char valChar[32]; rxOffset = i; sprintf(valChar, "RX Offset %d",rxOffset); ui.RXOffsetLabel->setText(valChar); NeedWaterfallHeaders = true; pnt_change[0] = 1; pnt_change[1] = 1; pnt_change[2] = 1; pnt_change[3] = 1; saveSettings(); return; } QMessageBox msgBox; msgBox.setWindowTitle("MessageBox Title"); msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); msgBox.exec(); } void QtSoundModem::clickedSlot() { char Name[32]; strcpy(Name, sender()->objectName().toUtf8()); if (strcmp(Name, "actDevices") == 0) { doDevices(); return; } if (strcmp(Name, "actModems") == 0) { doModems(); return; } if (strcmp(Name, "showBPF_A") == 0) { doFilter(0, 0); return; } if (strcmp(Name, "showTXBPF_A") == 0) { doFilter(0, 1); return; } if (strcmp(Name, "showLPF_A") == 0) { doFilter(0, 2); return; } if (strcmp(Name, "showBPF_B") == 0) { doFilter(1, 0); return; } if (strcmp(Name, "showTXBPF_B") == 0) { doFilter(1, 1); return; } if (strcmp(Name, "showLPF_B") == 0) { doFilter(1, 2); return; } if (strcmp(Name, "Low_A") == 0) { handleButton(0, 1); return; } if (strcmp(Name, "High_A") == 0) { handleButton(0, 2); return; } if (strcmp(Name, "Both_A") == 0) { handleButton(0, 3); return; } if (strcmp(Name, "Stop_A") == 0) { handleButton(0, 0); return; } if (strcmp(Name, "Low_B") == 0) { handleButton(1, 1); return; } if (strcmp(Name, "High_B") == 0) { handleButton(1, 2); return; } if (strcmp(Name, "Both_B") == 0) { handleButton(1, 3); return; } if (strcmp(Name, "Stop_B") == 0) { handleButton(1, 0); return; } if (strcmp(Name, "Low_C") == 0) { handleButton(2, 1); return; } if (strcmp(Name, "High_C") == 0) { handleButton(2, 2); return; } if (strcmp(Name, "Both_C") == 0) { handleButton(2, 3); return; } if (strcmp(Name, "Stop_C") == 0) { handleButton(2, 0); return; } if (strcmp(Name, "Low_D") == 0) { handleButton(3, 1); return; } if (strcmp(Name, "High_D") == 0) { handleButton(3, 2); return; } if (strcmp(Name, "Both_D") == 0) { handleButton(3, 3); return; } if (strcmp(Name, "Stop_D") == 0) { handleButton(3, 0); return; } if (strcmp(Name, "actFont") == 0) { bool ok; Font = QFontDialog::getFont(&ok, QFont(Font, this)); if (ok) { // the user clicked OK and font is set to the font the user selected QApplication::setFont(Font); sessionTable->horizontalHeader()->setFont(Font); saveSettings(); } else { // the user canceled the dialog; font is set to the initial // value, in this case Helvetica [Cronyx], 10 // QApplication::setFont(Font); } return; } QMessageBox msgBox; msgBox.setWindowTitle("MessageBox Title"); msgBox.setText("You Clicked " + ((QPushButton*)sender())->objectName()); msgBox.exec(); } Ui_ModemDialog * Dlg; QDialog * modemUI; QDialog * deviceUI; void QtSoundModem::doModems() { Dlg = new(Ui_ModemDialog); QDialog UI; char valChar[10]; Dlg->setupUi(&UI); modemUI = &UI; deviceUI = 0; myResize *resize = new myResize(); UI.installEventFilter(resize); sprintf(valChar, "%d", bpf[0]); Dlg->BPFWidthA->setText(valChar); sprintf(valChar, "%d", bpf[1]); Dlg->BPFWidthB->setText(valChar); sprintf(valChar, "%d", bpf[2]); Dlg->BPFWidthC->setText(valChar); sprintf(valChar, "%d", bpf[3]); Dlg->BPFWidthD->setText(valChar); sprintf(valChar, "%d", txbpf[0]); Dlg->TXBPFWidthA->setText(valChar); sprintf(valChar, "%d", txbpf[1]); Dlg->TXBPFWidthB->setText(valChar); sprintf(valChar, "%d", txbpf[2]); Dlg->TXBPFWidthC->setText(valChar); sprintf(valChar, "%d", txbpf[3]); Dlg->TXBPFWidthD->setText(valChar); sprintf(valChar, "%d", lpf[0]); Dlg->LPFWidthA->setText(valChar); sprintf(valChar, "%d", lpf[1]); Dlg->LPFWidthB->setText(valChar); sprintf(valChar, "%d", lpf[2]); Dlg->LPFWidthC->setText(valChar); sprintf(valChar, "%d", lpf[4]); Dlg->LPFWidthD->setText(valChar); sprintf(valChar, "%d", BPF_tap[0]); Dlg->BPFTapsA->setText(valChar); sprintf(valChar, "%d", BPF_tap[1]); Dlg->BPFTapsB->setText(valChar); sprintf(valChar, "%d", BPF_tap[2]); Dlg->BPFTapsC->setText(valChar); sprintf(valChar, "%d", BPF_tap[3]); Dlg->BPFTapsD->setText(valChar); sprintf(valChar, "%d", LPF_tap[0]); Dlg->LPFTapsA->setText(valChar); sprintf(valChar, "%d", LPF_tap[1]); Dlg->LPFTapsB->setText(valChar); sprintf(valChar, "%d", LPF_tap[2]); Dlg->LPFTapsC->setText(valChar); sprintf(valChar, "%d", LPF_tap[3]); Dlg->LPFTapsD->setText(valChar); Dlg->preEmphAllA->setChecked(emph_all[0]); if (emph_all[0]) Dlg->preEmphA->setDisabled(TRUE); else Dlg->preEmphA->setCurrentIndex(emph_db[0]); Dlg->preEmphAllB->setChecked(emph_all[1]); if (emph_all[1]) Dlg->preEmphB->setDisabled(TRUE); else Dlg->preEmphB->setCurrentIndex(emph_db[1]); Dlg->preEmphAllC->setChecked(emph_all[2]); if (emph_all[2]) Dlg->preEmphC->setDisabled(TRUE); else Dlg->preEmphC->setCurrentIndex(emph_db[2]); Dlg->preEmphAllD->setChecked(emph_all[3]); if (emph_all[3]) Dlg->preEmphD->setDisabled(TRUE); else Dlg->preEmphD->setCurrentIndex(emph_db[3]); Dlg->nonAX25A->setChecked(NonAX25[0]); Dlg->nonAX25B->setChecked(NonAX25[1]); Dlg->nonAX25C->setChecked(NonAX25[2]); Dlg->nonAX25D->setChecked(NonAX25[3]); Dlg->KISSOptA->setChecked(KISS_opt[0]); Dlg->KISSOptB->setChecked(KISS_opt[1]); Dlg->KISSOptC->setChecked(KISS_opt[2]); Dlg->KISSOptD->setChecked(KISS_opt[3]); sprintf(valChar, "%d", maxframe[0]); Dlg->MaxFrameA->setText(valChar); sprintf(valChar, "%d", maxframe[1]); Dlg->MaxFrameB->setText(valChar); sprintf(valChar, "%d", maxframe[2]); Dlg->MaxFrameC->setText(valChar); sprintf(valChar, "%d", maxframe[3]); Dlg->MaxFrameD->setText(valChar); sprintf(valChar, "%d", txdelay[0]); Dlg->TXDelayA->setText(valChar); sprintf(valChar, "%d", txdelay[1]); Dlg->TXDelayB->setText(valChar); sprintf(valChar, "%d", txdelay[2]); Dlg->TXDelayC->setText(valChar); sprintf(valChar, "%d", txdelay[3]); Dlg->TXDelayD->setText(valChar); sprintf(valChar, "%d", txtail[0]); Dlg->TXTailA->setText(valChar); sprintf(valChar, "%d", txtail[1]); Dlg->TXTailB->setText(valChar); sprintf(valChar, "%d", txtail[2]); Dlg->TXTailC->setText(valChar); sprintf(valChar, "%d", txtail[3]); Dlg->TXTailD->setText(valChar); Dlg->FrackA->setText(QString::number(frack_time[0])); Dlg->FrackB->setText(QString::number(frack_time[1])); Dlg->FrackC->setText(QString::number(frack_time[2])); Dlg->FrackD->setText(QString::number(frack_time[3])); Dlg->RetriesA->setText(QString::number(fracks[0])); Dlg->RetriesB->setText(QString::number(fracks[1])); Dlg->RetriesC->setText(QString::number(fracks[2])); Dlg->RetriesD->setText(QString::number(fracks[3])); sprintf(valChar, "%d", RCVR[0]); Dlg->AddRXA->setText(valChar); sprintf(valChar, "%d", RCVR[1]); Dlg->AddRXB->setText(valChar); sprintf(valChar, "%d", RCVR[2]); Dlg->AddRXC->setText(valChar); sprintf(valChar, "%d", RCVR[3]); Dlg->AddRXD->setText(valChar); sprintf(valChar, "%d", rcvr_offset[0]); Dlg->RXShiftA->setText(valChar); sprintf(valChar, "%d", rcvr_offset[1]); Dlg->RXShiftB->setText(valChar); sprintf(valChar, "%d", rcvr_offset[2]); Dlg->RXShiftC->setText(valChar); sprintf(valChar, "%d", rcvr_offset[3]); Dlg->RXShiftD->setText(valChar); // speed[1] // speed[2]; Dlg->recoverBitA->setCurrentIndex(recovery[0]); Dlg->recoverBitB->setCurrentIndex(recovery[1]); Dlg->recoverBitC->setCurrentIndex(recovery[2]); Dlg->recoverBitD->setCurrentIndex(recovery[3]); Dlg->fx25ModeA->setCurrentIndex(fx25_mode[0]); Dlg->fx25ModeB->setCurrentIndex(fx25_mode[1]); Dlg->fx25ModeC->setCurrentIndex(fx25_mode[2]); Dlg->fx25ModeD->setCurrentIndex(fx25_mode[3]); Dlg->IL2PModeA->setCurrentIndex(il2p_mode[0]); Dlg->IL2PModeB->setCurrentIndex(il2p_mode[1]); Dlg->IL2PModeC->setCurrentIndex(il2p_mode[2]); Dlg->IL2PModeD->setCurrentIndex(il2p_mode[3]); Dlg->CRCTX_A->setChecked((il2p_crc[0] & 1)); Dlg->CRCRX_A->setChecked((il2p_crc[0] & 2)); Dlg->CRCTX_B->setChecked((il2p_crc[1] & 1)); Dlg->CRCRX_B->setChecked((il2p_crc[1] & 2)); Dlg->CRCTX_C->setChecked((il2p_crc[2] & 1)); Dlg->CRCRX_C->setChecked((il2p_crc[2] & 2)); Dlg->CRCTX_D->setChecked((il2p_crc[3] & 1)); Dlg->CRCRX_D->setChecked((il2p_crc[3] & 2)); Dlg->CWIDCall->setText(CWIDCall); Dlg->CWIDInterval->setText(QString::number(CWIDInterval)); Dlg->CWIDMark->setText(CWIDMark); if (CWIDType) Dlg->radioButton_2->setChecked(1); else Dlg->CWIDType->setChecked(1); Dlg->afterTraffic->setChecked(afterTraffic); Dlg->RSIDSABM_A->setChecked(RSID_SABM[0]); Dlg->RSIDSABM_B->setChecked(RSID_SABM[1]); Dlg->RSIDSABM_C->setChecked(RSID_SABM[2]); Dlg->RSIDSABM_D->setChecked(RSID_SABM[3]); Dlg->RSIDUI_A->setChecked(RSID_UI[0]); Dlg->RSIDUI_B->setChecked(RSID_UI[1]); Dlg->RSIDUI_C->setChecked(RSID_UI[2]); Dlg->RSIDUI_D->setChecked(RSID_UI[3]); Dlg->DigiCallsA->setText(MyDigiCall[0]); Dlg->DigiCallsB->setText(MyDigiCall[1]); Dlg->DigiCallsC->setText(MyDigiCall[2]); Dlg->DigiCallsD->setText(MyDigiCall[3]); Dlg->RSID_1_SETMODEM->setChecked(RSID_SetModem[0]); Dlg->RSID_2_SETMODEM->setChecked(RSID_SetModem[1]); Dlg->RSID_3_SETMODEM->setChecked(RSID_SetModem[2]); Dlg->RSID_4_SETMODEM->setChecked(RSID_SetModem[3]); connect(Dlg->showBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showTXBPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showLPF_A, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showTXBPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showLPF_B, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showTXBPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showLPF_C, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showTXBPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->showLPF_D, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Dlg->okButton, SIGNAL(clicked()), this, SLOT(modemaccept())); connect(Dlg->modemSave, SIGNAL(clicked()), this, SLOT(modemSave())); connect(Dlg->cancelButton, SIGNAL(clicked()), this, SLOT(modemreject())); connect(Dlg->SendRSID_1, SIGNAL(clicked()), this, SLOT(doRSIDA())); connect(Dlg->SendRSID_2, SIGNAL(clicked()), this, SLOT(doRSIDB())); connect(Dlg->SendRSID_3, SIGNAL(clicked()), this, SLOT(doRSIDC())); connect(Dlg->SendRSID_4, SIGNAL(clicked()), this, SLOT(doRSIDD())); connect(Dlg->preEmphAllA, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllAChanged(int))); connect(Dlg->preEmphAllB, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllBChanged(int))); connect(Dlg->preEmphAllC, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllCChanged(int))); connect(Dlg->preEmphAllD, SIGNAL(stateChanged(int)), this, SLOT(preEmphAllDChanged(int))); UI.exec(); } void QtSoundModem::preEmphAllAChanged(int state) { Dlg->preEmphA->setDisabled(state); } void QtSoundModem::preEmphAllBChanged(int state) { Dlg->preEmphB->setDisabled(state); } void QtSoundModem::preEmphAllCChanged(int state) { Dlg->preEmphC->setDisabled(state); } void QtSoundModem::preEmphAllDChanged(int state) { Dlg->preEmphD->setDisabled(state); } extern "C" void get_exclude_list(char * line, TStringList * list); void QtSoundModem::modemaccept() { modemSave(); AGW_Report_Modem_Change(0); AGW_Report_Modem_Change(1); AGW_Report_Modem_Change(2); AGW_Report_Modem_Change(3); delete(Dlg); saveSettings(); modemUI->accept(); } void QtSoundModem::modemSave() { QVariant Q; emph_all[0] = Dlg->preEmphAllA->isChecked(); emph_db[0] = Dlg->preEmphA->currentIndex(); emph_all[1] = Dlg->preEmphAllB->isChecked(); emph_db[1] = Dlg->preEmphB->currentIndex(); emph_all[2] = Dlg->preEmphAllC->isChecked(); emph_db[2] = Dlg->preEmphC->currentIndex(); emph_all[3] = Dlg->preEmphAllD->isChecked(); emph_db[3] = Dlg->preEmphD->currentIndex(); NonAX25[0] = Dlg->nonAX25A->isChecked(); NonAX25[1] = Dlg->nonAX25B->isChecked(); NonAX25[2] = Dlg->nonAX25C->isChecked(); NonAX25[3] = Dlg->nonAX25D->isChecked(); KISS_opt[0] = Dlg->KISSOptA->isChecked(); KISS_opt[1] = Dlg->KISSOptB->isChecked(); KISS_opt[2] = Dlg->KISSOptC->isChecked(); KISS_opt[3] = Dlg->KISSOptD->isChecked(); if (emph_db[0] < 0 || emph_db[0] > nr_emph) emph_db[0] = 0; if (emph_db[1] < 0 || emph_db[1] > nr_emph) emph_db[1] = 0; if (emph_db[2] < 0 || emph_db[2] > nr_emph) emph_db[2] = 0; if (emph_db[3] < 0 || emph_db[3] > nr_emph) emph_db[3] = 0; Q = Dlg->TXDelayA->text(); txdelay[0] = Q.toInt(); Q = Dlg->TXDelayB->text(); txdelay[1] = Q.toInt(); Q = Dlg->TXDelayC->text(); txdelay[2] = Q.toInt(); Q = Dlg->TXDelayD->text(); txdelay[3] = Q.toInt(); Q = Dlg->MaxFrameA->text(); maxframe[0] = Q.toInt(); Q = Dlg->MaxFrameB->text(); maxframe[1] = Q.toInt(); Q = Dlg->MaxFrameC->text(); maxframe[2] = Q.toInt(); Q = Dlg->MaxFrameD->text(); maxframe[3] = Q.toInt(); if (maxframe[0] == 0 || maxframe[0] > 7) maxframe[0] = 3; if (maxframe[1] == 0 || maxframe[1] > 7) maxframe[1] = 3; if (maxframe[2] == 0 || maxframe[2] > 7) maxframe[2] = 3; if (maxframe[3] == 0 || maxframe[3] > 7) maxframe[3] = 3; Q = Dlg->TXTailA->text(); txtail[0] = Q.toInt(); Q = Dlg->TXTailB->text(); txtail[1] = Q.toInt(); Q = Dlg->TXTailC->text(); txtail[2] = Q.toInt(); txtail[3] = Dlg->TXTailD->text().toInt(); frack_time[0] = Dlg->FrackA->text().toInt(); frack_time[1] = Dlg->FrackB->text().toInt(); frack_time[2] = Dlg->FrackC->text().toInt(); frack_time[3] = Dlg->FrackD->text().toInt(); fracks[0] = Dlg->RetriesA->text().toInt(); fracks[1] = Dlg->RetriesB->text().toInt(); fracks[2] = Dlg->RetriesC->text().toInt(); fracks[3] = Dlg->RetriesD->text().toInt(); Q = Dlg->AddRXA->text(); RCVR[0] = Q.toInt(); Q = Dlg->AddRXB->text(); RCVR[1] = Q.toInt(); Q = Dlg->AddRXC->text(); RCVR[2] = Q.toInt(); Q = Dlg->AddRXD->text(); RCVR[3] = Q.toInt(); Q = Dlg->RXShiftA->text(); rcvr_offset[0] = Q.toInt(); Q = Dlg->RXShiftB->text(); rcvr_offset[1] = Q.toInt(); Q = Dlg->RXShiftC->text(); rcvr_offset[2] = Q.toInt(); Q = Dlg->RXShiftD->text(); rcvr_offset[3] = Q.toInt(); fx25_mode[0] = Dlg->fx25ModeA->currentIndex(); fx25_mode[1] = Dlg->fx25ModeB->currentIndex(); fx25_mode[2] = Dlg->fx25ModeC->currentIndex(); fx25_mode[3] = Dlg->fx25ModeD->currentIndex(); il2p_mode[0] = Dlg->IL2PModeA->currentIndex(); il2p_mode[1] = Dlg->IL2PModeB->currentIndex(); il2p_mode[2] = Dlg->IL2PModeC->currentIndex(); il2p_mode[3] = Dlg->IL2PModeD->currentIndex(); il2p_crc[0] = Dlg->CRCTX_A->isChecked(); if (Dlg->CRCRX_A->isChecked()) il2p_crc[0] |= 2; il2p_crc[1] = Dlg->CRCTX_B->isChecked(); if (Dlg->CRCRX_B->isChecked()) il2p_crc[1] |= 2; il2p_crc[2] = Dlg->CRCTX_C->isChecked(); if (Dlg->CRCRX_C->isChecked()) il2p_crc[2] |= 2; il2p_crc[3] = Dlg->CRCTX_D->isChecked(); if (Dlg->CRCRX_D->isChecked()) il2p_crc[3] |= 2; recovery[0] = Dlg->recoverBitA->currentIndex(); recovery[1] = Dlg->recoverBitB->currentIndex(); recovery[2] = Dlg->recoverBitC->currentIndex(); recovery[3] = Dlg->recoverBitD->currentIndex(); strcpy(CWIDCall, Dlg->CWIDCall->text().toUtf8().toUpper()); strcpy(CWIDMark, Dlg->CWIDMark->text().toUtf8().toUpper()); CWIDInterval = Dlg->CWIDInterval->text().toInt(); CWIDType = Dlg->radioButton_2->isChecked(); afterTraffic = Dlg->afterTraffic->isChecked(); if (CWIDInterval && afterTraffic == false) cwidtimer->start(CWIDInterval * 60000); else cwidtimer->stop(); RSID_SABM[0] = Dlg->RSIDSABM_A->isChecked(); RSID_SABM[1] = Dlg->RSIDSABM_B->isChecked(); RSID_SABM[2] = Dlg->RSIDSABM_C->isChecked(); RSID_SABM[3] = Dlg->RSIDSABM_D->isChecked(); RSID_UI[0] = Dlg->RSIDUI_A->isChecked(); RSID_UI[1] = Dlg->RSIDUI_B->isChecked(); RSID_UI[2] = Dlg->RSIDUI_C->isChecked(); RSID_UI[3] = Dlg->RSIDUI_D->isChecked(); RSID_SetModem[0] = Dlg->RSID_1_SETMODEM->isChecked(); RSID_SetModem[1] = Dlg->RSID_2_SETMODEM->isChecked(); RSID_SetModem[2] = Dlg->RSID_3_SETMODEM->isChecked(); RSID_SetModem[3] = Dlg->RSID_4_SETMODEM->isChecked(); Q = Dlg->DigiCallsA->text(); strcpy(MyDigiCall[0], Q.toString().toUtf8().toUpper()); Q = Dlg->DigiCallsB->text(); strcpy(MyDigiCall[1], Q.toString().toUtf8().toUpper()); Q = Dlg->DigiCallsC->text(); strcpy(MyDigiCall[2], Q.toString().toUtf8().toUpper()); Q = Dlg->DigiCallsD->text(); strcpy(MyDigiCall[3], Q.toString().toUtf8().toUpper()); int i; for (i = 0; i < 4; i++) { initTStringList(&list_digi_callsigns[i]); get_exclude_list(MyDigiCall[i], &list_digi_callsigns[i]); } /* Q = Dlg->LPFWidthA->text(); lpf[0] = Q.toInt(); Q = Dlg->LPFWidthB->text(); lpf[1] = Q.toInt(); Q = Dlg->LPFWidthC->text(); lpf[2] = Q.toInt(); Q = Dlg->LPFWidthD->text(); lpf[3] = Q.toInt(); */ } void QtSoundModem::modemreject() { delete(Dlg); modemUI->reject(); } void QtSoundModem::doRSIDA() { needRSID[0] = 1; } void QtSoundModem::doRSIDB() { needRSID[1] = 1; } void QtSoundModem::doRSIDC() { needRSID[2] = 1; } void QtSoundModem::doRSIDD() { needRSID[3] = 1; } void QtSoundModem::doFilter(int Chan, int Filter) { Ui_Dialog Dev; QImage * bitmap; QDialog UI; Dev.setupUi(&UI); bitmap = new QImage(642, 312, QImage::Format_RGB32); bitmap->fill(qRgb(255, 255, 255)); QPainter qPainter(bitmap); qPainter.setBrush(Qt::NoBrush); qPainter.setPen(Qt::black); if (Filter == 0) make_graph_buf(DET[0][0].BPF_core[Chan], BPF_tap[Chan], &qPainter); else if (Filter == 1) make_graph_buf(tx_BPF_core[Chan], tx_BPF_tap[Chan], &qPainter); else make_graph_buf(LPF_core[Chan], LPF_tap[Chan], &qPainter); qPainter.end(); Dev.label->setPixmap(QPixmap::fromImage(*bitmap)); UI.exec(); } Ui_devicesDialog * Dev; char NewPTTPort[80]; int newSoundMode = 0; int oldSoundMode = 0; int oldSnoopMix = 0; int newSnoopMix = 0; void QtSoundModem::SoundModeChanged(bool State) { UNUSED(State); // Mustn't change SoundMode until dialog is accepted newSnoopMix = Dev->onlyMixSnoop->isChecked(); 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 if (strcmp(NewPTTPort, "FLRIG") == 0) { Dev->CM108Label->setVisible(true); Dev->CM108Label->setText("FLRig Port"); Dev->VIDPID->setText(QString::number(FLRigPort)); Dev->VIDPID->setVisible(true); Dev->PTTOnLab->setText("FLRig Host"); Dev->PTTOnLab->setVisible(true); Dev->PTTOn->setText(FLRigHost); Dev->PTTOn->setVisible(true); } else { Dev->RTSDTR->setVisible(true); 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; oldSnoopMix = newSnoopMix = onlyMixSnoop; #ifdef WIN32 Dev->ALSA->setText("WaveOut"); Dev->OSS->setVisible(0); Dev->PULSE->setVisible(0); Dev->onlyMixSnoop->setVisible(0); Dev->ALSA->setChecked(1); #else if (SoundMode == 0) { Dev->onlyMixSnoop->setVisible(1); Dev->ALSA->setChecked(1); } else if (SoundMode == 1) Dev->OSS->setChecked(1); else if (SoundMode == 2) Dev->PULSE->setChecked(1); else if (SoundMode == 2) Dev->UDP->setChecked(1); #endif Dev->onlyMixSnoop->setChecked(onlyMixSnoop); connect(Dev->ALSA, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); connect(Dev->OSS, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); connect(Dev->PULSE, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); connect(Dev->UDP, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); connect(Dev->onlyMixSnoop, SIGNAL(toggled(bool)), this, SLOT(SoundModeChanged(bool))); for (i = 0; i < PlaybackCount; i++) Dev->outputDevice->addItem(&PlaybackNames[i][0]); 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->txLatency->setText(QString::number(txLatency)); Dev->Modem_1_Chan->setCurrentIndex(soundChannel[0]); Dev->Modem_2_Chan->setCurrentIndex(soundChannel[1]); Dev->Modem_3_Chan->setCurrentIndex(soundChannel[2]); 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"); Dev->PTTPort->addItem("FLRIG"); for (const QString &info : items) { Dev->PTTPort->addItem(info); } // If we are using a user specifed device add it i = Dev->PTTPort->findText(PTTPort, Qt::MatchFixedString); if (i == -1) { // Add our device to list Dev->PTTPort->insertItem(0, PTTPort); i = Dev->PTTPort->findText(PTTPort, Qt::MatchContains); } Dev->PTTPort->setCurrentIndex(i); PTTPortChanged(0); // Force reevaluation Dev->txRotation->setChecked(TX_rotate); Dev->DualPTT->setChecked(DualPTT); Dev->multiCore->setChecked(multiCore); Dev->darkTheme->setChecked(darkTheme); Dev->WaterfallMin->setCurrentIndex(Dev->WaterfallMin->findText(QString::number(WaterfallMin), Qt::MatchFixedString)); Dev->WaterfallMax->setCurrentIndex(Dev->WaterfallMax->findText(QString::number(WaterfallMax), Qt::MatchFixedString)); QObject::connect(Dev->okButton, SIGNAL(clicked()), this, SLOT(deviceaccept())); QObject::connect(Dev->cancelButton, SIGNAL(clicked()), this, SLOT(devicereject())); UI.exec(); } void QtSoundModem::mysetstyle() { if (darkTheme) { qApp->setStyleSheet( "QWidget {color: white; background-color: black}" "QTabBar::tab {color: rgb(127, 127, 127); background-color: black}" "QTabBar::tab::selected {color: white}" "QPushButton {border-style: outset; border-width: 2px; border-color: rgb(127, 127, 127)}" "QPushButton::default {border-style: outset; border-width: 2px; border-color: white}"); sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(40, 40, 40) }"); txText = qRgb(255, 127, 127); rxText = qRgb(173, 216, 230); } else { qApp->setStyleSheet(""); sessionTable->setStyleSheet("QHeaderView::section { background-color:rgb(224, 224, 224) }"); txText = qRgb(192, 0, 0); rxText = qRgb(0, 0, 192); } } void QtSoundModem::deviceaccept() { QVariant Q = Dev->inputDevice->currentText(); int cardChanged = 0; char portString[32]; int newMax; int newMin; if (Dev->UDP->isChecked()) { // cant have server and slave if (Dev->UDPEnabled->isChecked()) { QMessageBox::about(this, tr("QtSoundModem"), tr("Can't have UDP sound source and UDP server at same time")); return; } } if (oldSoundMode != newSoundMode || oldSnoopMix != newSnoopMix) { 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; onlyMixSnoop = newSnoopMix; 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; } if (onlyMixSnoop != Dev->onlyMixSnoop->isChecked()) { onlyMixSnoop = Dev->onlyMixSnoop->isChecked(); 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(); Q = Dev->txLatency->text(); txLatency = Q.toInt(); soundChannel[0] = Dev->Modem_1_Chan->currentIndex(); soundChannel[1] = Dev->Modem_2_Chan->currentIndex(); soundChannel[2] = Dev->Modem_3_Chan->currentIndex(); soundChannel[3] = Dev->Modem_4_Chan->currentIndex(); UsingLeft = 0; UsingRight = 0; UsingBothChannels = 0; for (int i = 0; i < 4; i++) { if (soundChannel[i] == LEFT) { UsingLeft = 1; modemtoSoundLR[i] = 0; } else if (soundChannel[i] == RIGHT) { UsingRight = 1; modemtoSoundLR[i] = 1; } } if (UsingLeft && UsingRight) UsingBothChannels = 1; SCO = Dev->singleChannelOutput->isChecked(); raduga = Dev->colourWaterfall->isChecked(); AGWServ = Dev->AGWEnabled->isChecked(); KISSServ = Dev->KISSEnabled->isChecked(); Q = Dev->KISSPort->text(); KISSPort = Q.toInt(); Q = Dev->AGWPort->text(); AGWPort = Q.toInt(); Q = Dev->PTTPort->currentText(); char temp[256]; strcpy(temp, Q.toString().toUtf8()); if (strlen(temp)) strcpy(PTTPort, temp); DualPTT = Dev->DualPTT->isChecked(); TX_rotate = Dev->txRotation->isChecked(); multiCore = Dev->multiCore->isChecked(); darkTheme = Dev->darkTheme->isChecked(); mysetstyle(); if (Dev->CAT->isChecked()) PTTMode = PTTCAT; else PTTMode = PTTRTS; Q = Dev->PTTOn->text(); strcpy(PTTOnString, Q.toString().toUtf8()); Q = Dev->PTTOff->text(); strcpy(PTTOffString, Q.toString().toUtf8()); Q = Dev->CATSpeed->text(); PTTBAUD = Q.toInt(); Q = Dev->UDPPort->text(); UDPClientPort = Q.toInt(); Q = Dev->UDPTXPort->text(); strcpy(portString, Q.toString().toUtf8()); UDPServerPort = atoi(portString); if (strchr(portString, '/')) { char * ptr = strlop(portString, '/'); TXPort = atoi(ptr); } else TXPort = UDPServerPort; Q = Dev->UDPTXHost->text(); strcpy(UDPHost, Q.toString().toUtf8()); UDPServ = Dev->UDPEnabled->isChecked(); Q = Dev->GPIOLeft->text(); pttGPIOPin = Q.toInt(); Q = Dev->GPIORight->text(); pttGPIOPinR = Q.toInt(); Q = Dev->VIDPID->text(); if (strcmp(PTTPort, "CM108") == 0) strcpy(CM108Addr, Q.toString().toUtf8()); else if (strcmp(PTTPort, "HAMLIB") == 0) { HamLibPort = Q.toInt(); Q = Dev->PTTOn->text(); strcpy(HamLibHost, Q.toString().toUtf8()); } else if (strcmp(PTTPort, "FLRIG") == 0) { FLRigPort = Q.toInt(); Q = Dev->PTTOn->text(); strcpy(FLRigHost, Q.toString().toUtf8()); } Q = Dev->WaterfallMax->currentText(); newMax = Q.toInt(); Q = Dev->WaterfallMin->currentText(); newMin = Q.toInt(); if (newMax != WaterfallMax || newMin != WaterfallMin) { QMessageBox msgBox; msgBox.setText("QtSoundModem must restart to change Waterfall range. Program will close if you hit Ok\n" "It may take up to 30 seconds for the program to start for the first time after changing settings"); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); int i = msgBox.exec(); if (i == QMessageBox::Ok) { Configuring = 1; // Stop Waterfall WaterfallMax = newMax; WaterfallMin = newMin; saveSettings(); Closing = 1; return; } } ClosePTTPort(); OpenPTTPort(); NeedWaterfallHeaders = true; delete(Dev); saveSettings(); deviceUI->accept(); if (cardChanged) { InitSound(1); } // Reset title and tooltip in case ports changed char Title[128]; sprintf(Title, "QtSoundModem Version %s Ports %d%s/%d%s", VersionString, AGWPort, AGWServ ? "*": "", KISSPort, KISSServ ? "*" : ""); w->setWindowTitle(Title); sprintf(Title, "QtSoundModem %d %d", AGWPort, KISSPort); if (trayIcon) trayIcon->setToolTip(Title); QSize newSize(this->size()); QSize oldSize(this->size()); QResizeEvent *myResizeEvent = new QResizeEvent(newSize, oldSize); QCoreApplication::postEvent(this, myResizeEvent); } void QtSoundModem::devicereject() { delete(Dev); deviceUI->reject(); } void QtSoundModem::handleButton(int Port, int Type) { // interlock calib with CWID if (calib_mode[0] == 4) // CWID return; doCalib(Port, Type); } void QtSoundModem::doRestartWF() { return; if (((tx_status[0] | tx_status[1] | tx_status[2] | tx_status[3]) != TX_SILENCE) || inWaterfall) { // in waterfall update thread wftimer->start(5000); return; } wftimer->start(1000 * 300); lockWaterfall = true; if (Firstwaterfall | Secondwaterfall) { initWaterfall(0); initWaterfall(1); } delete(RXLevel); delete(ui.RXLevel); ui.RXLevel = new QLabel(ui.centralWidget); RXLevelCopy = ui.RXLevel; ui.RXLevel->setGeometry(QRect(780, 14, 150, 11)); ui.RXLevel->setFrameShape(QFrame::Box); ui.RXLevel->setFrameShadow(QFrame::Sunken); delete(RXLevel2); delete(ui.RXLevel2); ui.RXLevel2 = new QLabel(ui.centralWidget); RXLevel2Copy = ui.RXLevel2; ui.RXLevel2->setGeometry(QRect(780, 23, 150, 11)); ui.RXLevel2->setFrameShape(QFrame::Box); ui.RXLevel2->setFrameShadow(QFrame::Sunken); RXLevel = new QImage(150, 10, QImage::Format_RGB32); RXLevel->fill(cyan); RXLevel2 = new QImage(150, 10, QImage::Format_RGB32); RXLevel2->fill(white); ui.RXLevel->setVisible(1); ui.RXLevel2->setVisible(1); lockWaterfall = false; } void QtSoundModem::doAbout() { QMessageBox::about(this, tr("About"), tr("G8BPQ's port of UZ7HO's Soundmodem\n\nCopyright (C) 2019-2020 Andrei Kopanchuk UZ7HO")); } void QtSoundModem::doCalibrate() { Ui_calDialog Calibrate; { QDialog UI; Calibrate.setupUi(&UI); connect(Calibrate.Low_A, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Both_A, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Stop_A, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Low_B, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.High_B, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Both_B, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Stop_B, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Low_C, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.High_C, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Both_C, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Stop_C, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Low_D, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.High_D, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Both_D, SIGNAL(released()), this, SLOT(clickedSlot())); connect(Calibrate.Stop_D, SIGNAL(released()), this, SLOT(clickedSlot())); /* connect(Calibrate.Low_A, &QPushButton::released, this, [=] { handleButton(0, 1); }); connect(Calibrate.High_A, &QPushButton::released, this, [=] { handleButton(0, 2); }); connect(Calibrate.Both_A, &QPushButton::released, this, [=] { handleButton(0, 3); }); connect(Calibrate.Stop_A, &QPushButton::released, this, [=] { handleButton(0, 0); }); connect(Calibrate.Low_B, &QPushButton::released, this, [=] { handleButton(1, 1); }); connect(Calibrate.High_B, &QPushButton::released, this, [=] { handleButton(1, 2); }); connect(Calibrate.Both_B, &QPushButton::released, this, [=] { handleButton(1, 3); }); connect(Calibrate.Stop_B, &QPushButton::released, this, [=] { handleButton(1, 0); }); // connect(Calibrate.High_A, SIGNAL(released()), this, SLOT(handleButton(1, 2))); */ UI.exec(); } } void QtSoundModem::RefreshSpectrum(unsigned char * Data) { int i; // Last 4 bytes are level busy and Tuning lines Waterfall->fill(Black); if (Data[206] != LastLevel) { LastLevel = Data[206]; // RefreshLevel(LastLevel); } if (Data[207] != LastBusy) { LastBusy = Data[207]; // Busy->setVisible(LastBusy); } for (i = 0; i < 205; i++) { int val = Data[0]; if (val > 63) val = 63; Waterfall->setPixel(i, val, Yellow); if (val < 62) Waterfall->setPixel(i, val + 1, Gold); Data++; } ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall)); } void RefreshLevel(unsigned int Level, unsigned int LevelR) { // Redraw the RX Level Bar Graph unsigned int x, y; for (x = 0; x < 150; x++) { for (y = 0; y < 10; y++) { if (x < Level) { if (Level < 50) RXLevel->setPixel(x, y, yellow); else if (Level > 135) RXLevel->setPixel(x, y, red); else RXLevel->setPixel(x, y, green); } else RXLevel->setPixel(x, y, white); } } // RXLevelCopy->setPixmap(QPixmap::fromImage(*RXLevel)); for (x = 0; x < 150; x++) { for (y = 0; y < 10; y++) { if (x < LevelR) { if (LevelR < 50) RXLevel2->setPixel(x, y, yellow); else if (LevelR > 135) RXLevel2->setPixel(x, y, red); else RXLevel2->setPixel(x, y, green); } else RXLevel2->setPixel(x, y, white); } } emit t->setLevelImage(); /// RXLevel2Copy->setPixmap(QPixmap::fromImage(*RXLevel2)); } extern "C" unsigned char CurrentLevel; extern "C" unsigned char CurrentLevelR; void QtSoundModem::RefreshWaterfall(int snd_ch, unsigned char * Data) { int j; unsigned char * Line; int len = Waterfall->bytesPerLine(); int TopLine = NextWaterfallLine[snd_ch]; // Write line to cyclic buffer then draw starting with the line just written // Length is 208 bytes, including Level and Busy flags memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Data, 206); if (NextWaterfallLine[snd_ch] > 63) NextWaterfallLine[snd_ch] = 0; for (j = 63; j > 0; j--) { Line = Waterfall->scanLine(j); memcpy(Line, &WaterfallLines[snd_ch][TopLine++][0], len); if (TopLine > 63) TopLine = 0; } ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall)); } void QtSoundModem::sendtoTrace(char * Msg, int tx) { const QTextCursor old_cursor = monWindowCopy->textCursor(); const int old_scrollbar_value = monWindowCopy->verticalScrollBar()->value(); const bool is_scrolled_down = old_scrollbar_value == monWindowCopy->verticalScrollBar()->maximum(); // Move the cursor to the end of the document. monWindowCopy->moveCursor(QTextCursor::End); // Insert the text at the position of the cursor (which is the end of the document). if (tx) monWindowCopy->setTextColor(txText); else monWindowCopy->setTextColor(rxText); monWindowCopy->textCursor().insertText(Msg); if (old_cursor.hasSelection() || !is_scrolled_down) { // The user has selected text or scrolled away from the bottom: maintain position. monWindowCopy->setTextCursor(old_cursor); monWindowCopy->verticalScrollBar()->setValue(old_scrollbar_value); } else { // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. monWindowCopy->moveCursor(QTextCursor::End); monWindowCopy->verticalScrollBar()->setValue(monWindowCopy->verticalScrollBar()->maximum()); } free(Msg); } // I think this does the waterfall typedef struct TRGBQ_t { Byte b, g, r, re; } TRGBWQ; typedef struct tagRECT { int left; int top; int right; int bottom; } RECT; unsigned int RGBWF[256] ; extern "C" void init_raduga() { Byte offset[6] = {0, 51, 102, 153, 204}; Byte i, n; for (n = 0; n < 52; n++) { i = n * 5; RGBWF[n + offset[0]] = qRgb(0, 0, i); RGBWF[n + offset[1]] = qRgb(0, i, 255); RGBWF[n + offset[2]] = qRgb(0, 255, 255 - i); RGBWF[n + offset[3]] = qRgb(1, 255, 0); RGBWF[n + offset[4]] = qRgb(255, 255 - 1, 0); } } extern "C" int nonGUIMode; // This draws the Frequency Scale on Waterfall extern "C" void DrawFreqTicks() { if (nonGUIMode) return; // Draw Frequency Markers on waterfall header(s); int x, i; char Textxx[20]; QImage * bm = Waterfall; QPainter qPainter(bm); qPainter.setBrush(Qt::black); qPainter.setPen(Qt::white); int Chan; #ifdef WIN32 int Top = 3; #else int Top = 4; #endif int Base = 0; for (Chan = 0; Chan < 2; Chan++) { if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1))) sprintf(Textxx, "Right"); else sprintf(Textxx, "Left"); qPainter.drawText(2, Top, 100, 20, 0, Textxx); // We drew markers every 100 Hz or 100 / binsize pixels int Markers = ((WaterfallMax - WaterfallMin) / 100) + 5; // Number of Markers to draw int Freq = WaterfallMin; float PixelsPerMarker = 100.0 / BinSize; for (i = 0; i < Markers; i++) { x = round(PixelsPerMarker * i); if (x < 1025) { if ((Freq % 500) == 0) qPainter.drawLine(x, Base + 22, x, Base + 15); else qPainter.drawLine(x, Base + 22, x, Base + 18); if ((Freq % 500) == 0) { sprintf(Textxx, "%d", Freq); if (x < 924) qPainter.drawText(x - 12, Top, 100, 20, 0, Textxx); } } Freq += 100; } if (UsingBothChannels == 0) break; Top += WaterfallTotalPixels; Base = WaterfallTotalPixels; } } // These draws the frequency Markers on the Waterfall void DrawModemFreqRange() { if (nonGUIMode) return; // Draws the modem freq bars on waterfall header(s) int x1, x2, k, pos1, pos2, pos3; QImage * bm = Waterfall; QPainter qPainter(bm); qPainter.setBrush(Qt::NoBrush); qPainter.setPen(Qt::white); int Chan; int LRtoDisplay = LEFT; int top = 0; for (Chan = 0; Chan < 2; Chan++) { if (Chan == 1 || ((UsingBothChannels == 0) && (UsingRight == 1))) LRtoDisplay = RIGHT; // bm->fill(black); // qPainter.fillRect(top, 23, 1024, 10, Qt::black); // We drew markers every 100 Hz or 100 / binsize pixels float PixelsPerHz = 1.0 / BinSize; k = 26 + top; // draw all enabled ports on the ports on this soundcard // First Modem is always on the first waterfall // If second is enabled it is on the first unless different // channel from first for (int i = 0; i < 4; i++) { if (soundChannel[i] != LRtoDisplay) continue; pos1 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) - 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5; pos2 = roundf((((rxOffset + chanOffset[i] + rx_freq[i]) + 0.5*rx_shift[i]) - WaterfallMin) * PixelsPerHz) - 5; pos3 = roundf(((rxOffset + chanOffset[i] + rx_freq[i])) - WaterfallMin * PixelsPerHz); x1 = pos1 + 5; x2 = pos2 + 5; qPainter.setPen(Qt::white); qPainter.drawLine(x1, k, x2, k); qPainter.drawLine(x1, k - 3, x1, k + 3); qPainter.drawLine(x2, k - 3, x2, k + 3); // qPainter.drawLine(pos3, k - 3, pos3, k + 3); if (rxOffset || chanOffset[i]) { // Draw TX posn if rxOffset used pos3 = roundf((rx_freq[i] - WaterfallMin) * PixelsPerHz); qPainter.setPen(Qt::magenta); qPainter.drawLine(pos3, k - 3, pos3, k + 3); qPainter.drawLine(pos3, k - 3, pos3, k + 3); qPainter.drawLine(pos3 - 2, k - 3, pos3 + 2, k - 3); } k += 3; } if (UsingBothChannels == 0) break; LRtoDisplay = RIGHT; top = WaterfallTotalPixels; } } void doWaterfallThread(void * param); extern "C" void doWaterfall(int snd_ch) { if (nonGUIMode) return; if (Closing) return; if (lockWaterfall) return; // if (multiCore) // Run modems in separate threads // _beginthread(doWaterfallThread, 0, xx); // else doWaterfallThread((void *)(size_t)snd_ch); } extern "C" void displayWaterfall() { // if we are using both channels but only want right need to extract correct half of Image if (Waterfall == nullptr) return; if (UsingBothChannels && (Firstwaterfall == 0)) WaterfallCopy->setAlignment(Qt::AlignBottom | Qt::AlignLeft); else WaterfallCopy->setAlignment(Qt::AlignTop | Qt::AlignLeft); // WaterfallCopy->setPixmap(QPixmap::fromImage(*Waterfall)); emit t->setWaterfallImage(); } extern "C" float aFFTAmpl[1024]; extern "C" void SMUpdateBusyDetector(int LR, float * Real, float *Imag); void doWaterfallThread(void * param) { int snd_ch = (int)(size_t)param; if (lockWaterfall) return; if (Configuring) return; if (inWaterfall) return; inWaterfall = true; // don't allow restart waterfall if (snd_ch == 1 && UsingLeft == 0) // Only using right snd_ch = 0; // Samples are in first buffer QImage * bm = Waterfall; int i; single mag; UCHAR * p; UCHAR Line[4096] = ""; // 4 bytes per pixel int lineLen, Start, End; word hFFTSize; Byte n; float RealOut[8192] = { 0 }; float ImagOut[8192]; RefreshLevel(CurrentLevel, CurrentLevelR); // Signal Level hFFTSize = FFTSize / 2; // I think an FFT should produce n/2 bins, each of Samp/n Hz // Looks like my code only works with n a power of 2 // So can use 1024 or 4096. 1024 gives 512 bins of 11.71875 and a 512 pixel // display (is this enough?) Start = (WaterfallMin / BinSize); // First and last bins to process End = (WaterfallMax / BinSize); if (0) //RSID_WF { // Use the Magnitudes in float aFFTAmpl[RSID_FFT_SIZE]; for (i = 0; i < hFFTSize; i++) { mag = aFFTAmpl[i]; mag *= 0.00000042f; if (mag < 0.00001f) mag = 0.00001f; if (mag > 1.0f) mag = 1.0f; mag = 22 * log2f(mag) + 255; if (mag < 0) mag = 0; fft_disp[snd_ch][i] = round(mag); } } else { dofft(&fft_buf[snd_ch][0], RealOut, ImagOut); // FourierTransform(1024, &fft_buf[snd_ch][0], RealOut, ImagOut, 0); for (i = Start; i < End; i++) { //mag: = ComplexMag(fft_d[i])*0.00000042; // mag = sqrtf(powf(RealOut[i], 2) + powf(ImagOut[i], 2)) * 0.00000042f; mag = powf(RealOut[i], 2); mag += powf(ImagOut[i], 2); mag = sqrtf(mag); mag *= 0.00000042f; if (mag > MaxMagOut) { MaxMagOut = mag; MaxMagIndex = i; } if (mag < 0.00001f) mag = 0.00001f; if (mag > 1.0f) mag = 1.0f; mag = 22 * log2f(mag) + 255; if (mag < 0) mag = 0; MagOut[i] = mag; // for Freq Guess fft_disp[snd_ch][i] = round(mag); } } SMUpdateBusyDetector(snd_ch, RealOut, ImagOut); // we always do fft so we can get centre freq and do busy detect. But only update waterfall if on display if (bm == 0) { inWaterfall = false; return; } if ((Firstwaterfall == 0 && snd_ch == 0) || (Secondwaterfall == 0 && snd_ch == 1)) { inWaterfall = false; return; } p = Line; lineLen = 4096; if (raduga == DISP_MONO) { for (i = Start; i < End; i++) { n = fft_disp[snd_ch][i]; *(p++) = n; // all colours the same *(p++) = n; *(p++) = n; p++; } } else { for (i = Start; i < End; i++) { n = fft_disp[snd_ch][i]; memcpy(p, &RGBWF[n], 4); p += 4; } } // Scroll int TopLine = NextWaterfallLine[snd_ch]; int TopScanLine = WaterfallHeaderPixels; if (snd_ch) TopScanLine += WaterfallTotalPixels; // Write line to cyclic buffer then draw starting with the line just written memcpy(&WaterfallLines[snd_ch][NextWaterfallLine[snd_ch]++][0], Line, 4096); if (NextWaterfallLine[snd_ch] > 79) NextWaterfallLine[snd_ch] = 0; // Sanity check if ((79 + TopScanLine) >= bm->height()) { printf("Invalid WFMaxLine %d \n", bm->height()); exit(1); } for (int j = 79; j > 0; j--) { p = bm->scanLine(j + TopScanLine); if (p == nullptr) { printf("Invalid WF Pointer \n"); exit(1); } memcpy(p, &WaterfallLines[snd_ch][TopLine][0], lineLen); TopLine++; if (TopLine > 79) TopLine = 0; } inWaterfall = false; } void QtSoundModem::setWaterfallImage() { ui.Waterfall->setPixmap(QPixmap::fromImage(*Waterfall)); } void QtSoundModem::setLevelImage() { RXLevelCopy->setPixmap(QPixmap::fromImage(*RXLevel)); RXLevel2Copy->setPixmap(QPixmap::fromImage(*RXLevel2)); } void QtSoundModem::setConstellationImage(int chan, int Qual) { char QualText[64]; sprintf(QualText, "Chan %c Qual = %d", chan + 'A', Qual); QualLabel[chan]->setText(QualText); constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan])); } void QtSoundModem::changeEvent(QEvent* e) { 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"; closeTraceLog(); QSettings mysettings("QtSoundModem.ini", QSettings::IniFormat); mysettings.setValue("geometry", saveGeometry()); mysettings.setValue("windowState", saveState()); saveSettings(); Closing = TRUE; qDebug() << "Closing"; QThread::msleep(100); } extern "C" void QSleep(int ms) { QThread::msleep(ms); } int upd_time = 30; void QtSoundModem::show_grid() { // This refeshes the session list int snd_ch, i, num_rows, row_idx; QTableWidgetItem *item; const char * msg = ""; int speed_tx, speed_rx; if (grid_time < 10) { grid_time++; return; } grid_time = 0; //label7.Caption = inttostr(stat_r_mem); mem_arq num_rows = 0; row_idx = 0; for (snd_ch = 0; snd_ch < 4; snd_ch++) { for (i = 0; i < port_num; i++) { if (AX25Port[snd_ch][i].status != STAT_NO_LINK) num_rows++; } } if (num_rows == 0) { sessionTable->clearContents(); sessionTable->setRowCount(0); sessionTable->setRowCount(1); } else sessionTable->setRowCount(num_rows); for (snd_ch = 0; snd_ch < 4; snd_ch++) { for (i = 0; i < port_num; i++) { if (AX25Port[snd_ch][i].status != STAT_NO_LINK) { switch (AX25Port[snd_ch][i].status) { case STAT_NO_LINK: msg = "No link"; break; case STAT_LINK: msg = "Link"; break; case STAT_CHK_LINK: msg = "Chk link"; break; case STAT_WAIT_ANS: msg = "Wait ack"; break; case STAT_TRY_LINK: msg = "Try link"; break; case STAT_TRY_UNLINK: msg = "Try unlink"; } item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].mycall); sessionTable->setItem(row_idx, 0, item); item = new QTableWidgetItem(AX25Port[snd_ch][i].kind); sessionTable->setItem(row_idx, 11, item); item = new QTableWidgetItem((char *)AX25Port[snd_ch][i].corrcall); sessionTable->setItem(row_idx, 1, item); item = new QTableWidgetItem(msg); sessionTable->setItem(row_idx, 2, item); item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_pkt)); sessionTable->setItem(row_idx, 3, item); item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_s_byte)); sessionTable->setItem(row_idx, 4, item); item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_pkt)); sessionTable->setItem(row_idx, 5, item); item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_byte)); sessionTable->setItem(row_idx, 6, item); item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_r_fc)); sessionTable->setItem(row_idx, 7, item); item = new QTableWidgetItem(QString::number(AX25Port[snd_ch][i].info.stat_fec_count)); sessionTable->setItem(row_idx, 8, item); if (grid_timer != upd_time) grid_timer++; else { grid_timer = 0; speed_tx = round(abs(AX25Port[snd_ch][i].info.stat_s_byte - AX25Port[snd_ch][i].info.stat_l_s_byte) / upd_time); speed_rx = round(abs(AX25Port[snd_ch][i].info.stat_r_byte - AX25Port[snd_ch][i].info.stat_l_r_byte) / upd_time); item = new QTableWidgetItem(QString::number(speed_tx)); sessionTable->setItem(row_idx, 9, item); item = new QTableWidgetItem(QString::number(speed_rx)); sessionTable->setItem(row_idx, 10, item); AX25Port[snd_ch][i].info.stat_l_r_byte = AX25Port[snd_ch][i].info.stat_r_byte; AX25Port[snd_ch][i].info.stat_l_s_byte = AX25Port[snd_ch][i].info.stat_s_byte; } row_idx++; } } } } // "Copy on Select" Code void QtSoundModem::onTEselectionChanged() { QTextEdit * x = static_cast(QObject::sender()); x->copy(); } #define ConstellationHeight 121 #define ConstellationWidth 121 #define PLOTRADIUS 60 #define MAX(x, y) ((x) > (y) ? (x) : (y)) extern "C" int SMUpdatePhaseConstellation(int chan, float * Phases, float * Mags, int intPSKPhase, int Count) { // Subroutine to update Constellation plot for PSK modes... // Skip plotting and calculations of intPSKPhase(0) as this is a reference phase (9/30/2014) float dblPhaseError; float dblPhaseErrorSum = 0; float intP = 0; float dblRad = 0; float dblAvgRad = 0; float dbPhaseStep; float MagMax = 0; float dblPlotRotation = 0; int i, intQuality; int intX, intY; int yCenter = (ConstellationHeight - 2) / 2; int xCenter = (ConstellationWidth - 2) / 2; if (Count == 0) return 0; if (nonGUIMode == 0) { Constellation[chan]->fill(black); for (i = 0; i < 120; i++) { Constellation[chan]->setPixel(xCenter, i, cyan); Constellation[chan]->setPixel(i, xCenter, cyan); } } dbPhaseStep = 2 * M_PI / intPSKPhase; for (i = 1; i < Count; i++) // Don't plot the first phase (reference) { MagMax = MAX(MagMax, Mags[i]); // find the max magnitude to auto scale dblAvgRad += Mags[i]; } dblAvgRad = dblAvgRad / Count; // the average radius for (i = 0; i < Count; i++) { dblRad = PLOTRADIUS * Mags[i] / MagMax; // scale the radius dblRad based on intMag intP = round((Phases[i]) / dbPhaseStep); // compute the Phase error dblPhaseError = fabsf(Phases[i] - intP * dbPhaseStep); // always positive and < .5 * dblPhaseStep dblPhaseErrorSum += dblPhaseError; if (nonGUIMode == 0) { intX = xCenter + dblRad * cosf(dblPlotRotation + Phases[i]); intY = yCenter + dblRad * sinf(dblPlotRotation + Phases[i]); if (intX > 0 && intY > 0) if (intX != xCenter && intY != yCenter) Constellation[chan]->setPixel(intX, intY, yellow); } } dblAvgRad = dblAvgRad / Count; // the average radius intQuality = MAX(0, ((100 - 200 * (dblPhaseErrorSum / (Count)) / dbPhaseStep))); // ignore radius error for (PSK) but include for QAM if (nonGUIMode == 0) { emit t->setConstellationImage(chan, intQuality); // char QualText[64]; // sprintf(QualText, "Chan %c Qual = %d", chan + 'A', intQuality); // QualLabel[chan]->setText(QualText); // constellationLabel[chan]->setPixmap(QPixmap::fromImage(*Constellation[chan])); } return intQuality; } QFile tracefile("Tracelog.txt"); extern "C" int openTraceLog() { if (!tracefile.open(QIODevice::Append | QIODevice::Text)) return 0; return 1; } extern "C" qint64 writeTraceLog(char * Data, char Dirn) { return tracefile.write(Data); } extern "C" void closeTraceLog() { tracefile.close(); } extern "C" void debugTimeStamp(char * Text, char Dirn) { #ifndef LOGTX if (Dirn == 'T') return; #endif #ifndef LOGRX if (Dirn == 'R') return; #endif QTime Time(QTime::currentTime()); QString String = Time.toString("hh:mm:ss.zzz"); char Msg[2048]; sprintf(Msg, "%s %s\n", String.toUtf8().data(), Text); qint64 ret = writeTraceLog(Msg, Dirn); } // Timer functions need to run in GUI Thread extern "C" int SampleNo; extern "C" int pttOnTime() { return pttOnTimer.elapsed(); } extern "C" void startpttOnTimer() { pttOnTimer.start(); } extern "C" void StartWatchdog() { // Get Monotonic clock for PTT drop time calculation #ifndef WIN32 clock_gettime(CLOCK_MONOTONIC, &pttclk); #endif debugTimeStamp((char *)"PTT On", 'T'); emit t->startWatchdog(); pttOnTimer.start(); } extern "C" void StopWatchdog() { int txlenMs = (1000 * SampleNo / TX_Samplerate); Debugprintf("Samples Sent %d, Calc Time %d, PTT Time %d", SampleNo, txlenMs, pttOnTime()); debugTimeStamp((char *)"PTT Off", 'T'); closeTraceLog(); openTraceLog(); debugTimeStamp((char *)"Log Reopened", 'T'); emit t->stopWatchdog(); } void QtSoundModem::StartWatchdog() { PTTWatchdog->start(60 * 1000); } void QtSoundModem::StopWatchdog() { PTTWatchdog->stop(); } void QtSoundModem::PTTWatchdogExpired() { PTTWatchdog->stop(); }