From 61f023ddd1427a12655b97a21c143c36acbdb3ba Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 5 Aug 2013 13:57:55 +0000 Subject: [PATCH] Most of these changes are thanks to G4WJS. Audio I/O is now done using Qt's built-in QAudio calls rather than PortAudio. Also includes some refactoring of the arrangement for these calls, and more use of C++ style. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@3523 ab8295b8-cf94-4d9e-aec4-7959e3be5d79 --- commons.h | 9 +- devsetup.cpp | 162 ++++------- devsetup.h | 17 +- lib/Makefile.MinGW | 11 +- lib/usleep.c | 5 + main.cpp | 10 +- mainwindow.cpp | 216 +++++++++------ mainwindow.h | 15 +- meterwidget.cpp | 102 +++---- meterwidget.h | 60 ++-- psk_reporter.cpp | 224 +++++++-------- psk_reporter.h | 84 +++--- rigclass.cpp | 664 ++++++++++++++++++++++----------------------- rigclass.h | 196 ++++++------- signalmeter.cpp | 106 ++++---- signalmeter.h | 64 ++--- soundin.cpp | 452 +++++++----------------------- soundin.h | 147 ++-------- soundout.cpp | 267 ++++++------------ soundout.h | 78 ++---- wsjtx.pro | 47 ++-- 21 files changed, 1227 insertions(+), 1709 deletions(-) create mode 100644 lib/usleep.c diff --git a/commons.h b/commons.h index 17c5c24ef..37447fb58 100644 --- a/commons.h +++ b/commons.h @@ -3,13 +3,12 @@ #define NSMAX 6827 #define NTMAX 120 +#define RX_SAMPLE_RATE 12000 -extern "C" { - -extern struct { +extern struct FortranCommon { float ss[184*NSMAX]; //This is "common/jt9com/..." in fortran float savg[NSMAX]; - short int d2[NTMAX*12000]; + short int d2[NTMAX*RX_SAMPLE_RATE]; int nutc; //UTC as integer, HHMM int ndiskdat; //1 ==> data read from *.wav file int ntrperiod; //TR period (seconds) @@ -29,6 +28,4 @@ extern struct { char datetime[20]; } jt9com_; -} - #endif // COMMONS_H diff --git a/devsetup.cpp b/devsetup.cpp index 2d1d926a8..55535cb9c 100644 --- a/devsetup.cpp +++ b/devsetup.cpp @@ -1,7 +1,6 @@ #include "devsetup.h" #include #include -#include #include #include @@ -12,7 +11,10 @@ qint32 g2_iptt; qint32 g2_COMportOpen; //----------------------------------------------------------- DevSetup() -DevSetup::DevSetup(QWidget *parent) : QDialog(parent) +DevSetup::DevSetup(QWidget *parent) + : QDialog(parent) + , m_audioInputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioInput)) + , m_audioOutputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioOutput)) { ui.setupUi(this); //setup the dialog form m_restartSoundIn=false; @@ -37,109 +39,43 @@ void DevSetup::initDlg() QString catPortDriver = settings.value("CATdriver","None").toString(); settings.endGroup(); -/* - QList InDevices; - QList OutDevices; - QAudioDeviceInfo deviceInfo; - - InDevices = deviceInfo.availableDevices(QAudio::AudioInput); - OutDevices = deviceInfo.availableDevices(QAudio::AudioOutput); - - foreach (const QAudioDeviceInfo &deviceInfo, InDevices) { - ui.comboBoxSndIn->addItem(deviceInfo.deviceName(), - qVariantFromValue(deviceInfo)); - } - - foreach (const QAudioDeviceInfo &deviceInfo, OutDevices) { - ui.comboBoxSndOut->addItem(deviceInfo.deviceName(), - qVariantFromValue(deviceInfo)); - } -*/ - - int k,id; - int numDevices=Pa_GetDeviceCount(); - - const PaDeviceInfo *pdi; - int nchin; - int nchout; - char pa_device_name[128]; - char pa_device_hostapi[128]; - - k=0; - for(id=0; idmaxInputChannels; - if(nchin>0) { - m_inDevList[k]=id; - if (id == m_paInDevice) - m_nDevIn = k; - k++; - sprintf((char*)(pa_device_name),"%s",pdi->name); - sprintf((char*)(pa_device_hostapi),"%s", - Pa_GetHostApiInfo(pdi->hostApi)->name); - -#ifdef WIN32 - char *p,*p1; - char p2[50]; - p1=(char*)""; - p=strstr(pa_device_hostapi,"MME"); - if(p!=NULL) p1=(char*)"MME"; - p=strstr(pa_device_hostapi,"Direct"); - if(p!=NULL) p1=(char*)"DirectX"; - p=strstr(pa_device_hostapi,"WASAPI"); - if(p!=NULL) p1=(char*)"WASAPI"; - p=strstr(pa_device_hostapi,"ASIO"); - if(p!=NULL) p1=(char*)"ASIO"; - p=strstr(pa_device_hostapi,"WDM-KS"); - if(p!=NULL) p1=(char*)"WDM-KS"; - - sprintf(p2,"%2d %d %-8s %-39s",id,nchin,p1,pa_device_name); - QString t(p2); -#else - QString t; - t.sprintf("%2d %d %-8s %-39s",id,nchin, - Pa_GetHostApiInfo(pdi->hostApi)->name,pdi->name); -#endif - ui.comboBoxSndIn->addItem(t); - } + // + // loaad combo boxes with setup choices + // + { + int currentIndex = -1; + int defaultIndex = 0; + for (AudioDevices::const_iterator p = m_audioInputDevices.begin (); p != m_audioInputDevices.end (); ++p) + { + ui.comboBoxSndIn->addItem (p->deviceName ()); + if (*p == m_audioInputDevice) + { + currentIndex = p - m_audioInputDevices.begin (); + } + else if (*p == QAudioDeviceInfo::defaultInputDevice ()) + { + defaultIndex = p - m_audioInputDevices.begin (); + } + } + ui.comboBoxSndIn->setCurrentIndex (currentIndex != -1 ? currentIndex : defaultIndex); } - k=0; - for(id=0; idmaxOutputChannels; - if(nchout>0) { - m_outDevList[k]=id; - if (id == m_paOutDevice) - m_nDevOut = k; - k++; - sprintf((char*)(pa_device_name),"%s",pdi->name); - sprintf((char*)(pa_device_hostapi),"%s", - Pa_GetHostApiInfo(pdi->hostApi)->name); - -#ifdef WIN32 - char *p,*p1; - char p2[50]; - p1=(char*)""; - p=strstr(pa_device_hostapi,"MME"); - if(p!=NULL) p1=(char*)"MME"; - p=strstr(pa_device_hostapi,"Direct"); - if(p!=NULL) p1=(char*)"DirectX"; - p=strstr(pa_device_hostapi,"WASAPI"); - if(p!=NULL) p1=(char*)"WASAPI"; - p=strstr(pa_device_hostapi,"ASIO"); - if(p!=NULL) p1=(char*)"ASIO"; - p=strstr(pa_device_hostapi,"WDM-KS"); - if(p!=NULL) p1=(char*)"WDM-KS"; - sprintf(p2,"%2d %d %-8s %-39s",id,nchout,p1,pa_device_name); - QString t(p2); -#else - QString t; - t.sprintf("%2d %d %-8s %-39s",id,nchout, - Pa_GetHostApiInfo(pdi->hostApi)->name,pdi->name); -#endif - ui.comboBoxSndOut->addItem(t); - } + { + int currentIndex = -1; + int defaultIndex = 0; + for (AudioDevices::const_iterator p = m_audioOutputDevices.begin (); p != m_audioOutputDevices.end (); ++p) + { + ui.comboBoxSndOut->addItem (p->deviceName ()); + if (*p == m_audioOutputDevice) + { + currentIndex = p - m_audioOutputDevices.begin (); + } + else if (*p == QAudioDeviceInfo::defaultOutputDevice ()) + { + defaultIndex = p - m_audioOutputDevices.begin (); + } + } + ui.comboBoxSndOut->setCurrentIndex (currentIndex != -1 ? currentIndex : defaultIndex); } connect(&p4, SIGNAL(readyReadStandardOutput()), @@ -167,8 +103,6 @@ void DevSetup::initDlg() ui.idIntSpinBox->setValue(m_idInt); ui.pttMethodComboBox->setCurrentIndex(m_pttMethodIndex); ui.saveDirEntry->setText(m_saveDir); - ui.comboBoxSndIn->setCurrentIndex(m_nDevIn); - ui.comboBoxSndOut->setCurrentIndex(m_nDevOut); ui.cbID73->setChecked(m_After73); ui.cbPSKReporter->setChecked(m_pskReporter); ui.cbSplit->setChecked(m_bSplit and m_catEnabled); @@ -302,11 +236,17 @@ void DevSetup::accept() // Check to see whether SoundInThread must be restarted, // and save user parameters. - if(m_nDevIn!=ui.comboBoxSndIn->currentIndex() or - m_paInDevice!=m_inDevList[m_nDevIn]) m_restartSoundIn=true; + if (m_audioInputDevice != m_audioInputDevices[ui.comboBoxSndIn->currentIndex ()]) + { + m_audioInputDevice = m_audioInputDevices[ui.comboBoxSndIn->currentIndex ()]; + m_restartSoundIn = true; + } - if(m_nDevOut!=ui.comboBoxSndOut->currentIndex() or - m_paOutDevice!=m_outDevList[m_nDevOut]) m_restartSoundOut=true; + if (m_audioOutputDevice != m_audioOutputDevices[ui.comboBoxSndOut->currentIndex ()]) + { + m_audioOutputDevice = m_audioOutputDevices[ui.comboBoxSndOut->currentIndex ()]; + m_restartSoundOut = true; + } m_myCall=ui.myCallEntry->text(); m_myGrid=ui.myGridEntry->text(); @@ -314,10 +254,6 @@ void DevSetup::accept() m_pttMethodIndex=ui.pttMethodComboBox->currentIndex(); m_pttPort=ui.pttComboBox->currentIndex(); m_saveDir=ui.saveDirEntry->text(); - m_nDevIn=ui.comboBoxSndIn->currentIndex(); - m_paInDevice=m_inDevList[m_nDevIn]; - m_nDevOut=ui.comboBoxSndOut->currentIndex(); - m_paOutDevice=m_outDevList[m_nDevOut]; m_macro.clear(); m_macro.append(ui.macro1->text()); diff --git a/devsetup.h b/devsetup.h index 9bf129532..6e0d52c6e 100644 --- a/devsetup.h +++ b/devsetup.h @@ -1,10 +1,13 @@ #ifndef DEVSETUP_H #define DEVSETUP_H +#include "ui_devsetup.h" + #include #include #include -#include "ui_devsetup.h" +#include + #include "rigclass.h" class DevSetup : public QDialog @@ -19,12 +22,6 @@ public: qint32 m_idInt; qint32 m_pttMethodIndex; qint32 m_pttPort; - qint32 m_nDevIn; - qint32 m_nDevOut; - qint32 m_inDevList[100]; - qint32 m_outDevList[100]; - qint32 m_paInDevice; - qint32 m_paOutDevice; qint32 m_catPortIndex; qint32 m_rig; qint32 m_rigIndex; @@ -39,8 +36,14 @@ public: qint32 m_poll; qint32 m_tmp; + typedef QList AudioDevices; + AudioDevices m_audioInputDevices; /* available input devices */ + AudioDevices m_audioOutputDevices; /* available output devices */ + QAudioDeviceInfo m_audioInputDevice; /* selected input device */ + QAudioDeviceInfo m_audioOutputDevice; /* selected output device */ bool m_restartSoundIn; bool m_restartSoundOut; + bool m_pskReporter; bool m_firstCall; bool m_catEnabled; diff --git a/lib/Makefile.MinGW b/lib/Makefile.MinGW index 1668e762a..03e12dd89 100644 --- a/lib/Makefile.MinGW +++ b/lib/Makefile.MinGW @@ -9,8 +9,8 @@ EXE_DIR = ../../wsjtx_install # -I'c:/QtSDK/Desktop/Qt/4.7.4/mingw/include/ActiveQt' \ # -I'release' -I'.' -I'c:/QtSDK/Desktop/Qt/4.7.4/mingw/mkspecs/win32-g++' -INCPATH = -I'C:/Qt/Qt5.0.2/5.0.2/mingw47_32/include/QtCore' \ - -I'C:/Qt/Qt5.0.2/5.0.2/mingw47_32/include' \ +INCPATH = -I'C:/Qt/Qt5.1.0/5.1.0/mingw48_32/include/QtCore' \ + -I'C:/Qt/Qt5.1.0/5.1.0/mingw48_32/include' \ # Compilers CC = gcc @@ -57,11 +57,10 @@ libjt9.a: $(OBJS1) ranlib libjt9.a OBJS2 = jt9.o jt9a.o jt9b.o jt9c.o ipcomm.o sec_midn.o usleep.o -#LIBS2 = -L'c:/QtSDK/Desktop/Qt/4.7.4/mingw/lib' -lQtCore4 -LIBS2 = -L'C:/Qt/Qt5.0.2/5.0.2/mingw47_32/lib' -lQt5Core +LIBS2 = -L'C:/Qt/Qt5.1.0/5.1.0/mingw48_32/lib' -lQt5Core jt9.exe: $(OBJS2) libjt9.a - $(CXX) -o jt9.exe $(OBJS2) $(LIBS2) libjt9.a ../libfftw3f_win.a \ - c:/MinGW/lib/libf95.a + $(CXX) -o jt9.exe -static $(OBJS2) $(LIBS2) libjt9.a \ + ../libfftw3f_win.a c:/MinGW/lib/libf95.a mkdir -p $(EXE_DIR) cp jt9.exe $(EXE_DIR) diff --git a/lib/usleep.c b/lib/usleep.c new file mode 100644 index 000000000..df01f92e0 --- /dev/null +++ b/lib/usleep.c @@ -0,0 +1,5 @@ +/* usleep(3) */ +void usleep_(unsigned long *microsec) +{ + usleep(*microsec); +} diff --git a/main.cpp b/main.cpp index a05bbc6f8..b6abef9c9 100644 --- a/main.cpp +++ b/main.cpp @@ -4,9 +4,10 @@ #include #endif #include -#include + #include "mainwindow.h" + // Multiple instances: QSharedMemory mem_jt9; QUuid my_uuid; @@ -48,13 +49,6 @@ int main(int argc, char *argv[]) } memset(to,0,size); //Zero all decoding params in shared memory - //Initialize Portaudio - PaError paerr=Pa_Initialize(); - if(paerr!=paNoError) { - QMessageBox::critical( 0, "Error", "Unable to initialize PortAudio."); - exit(1); - } - // Multiple instances: Call MainWindow() with the UUID key MainWindow w(&mem_jt9, &my_key, fontSize2, fontWeight2); w.show(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 11eae45d3..d3b25779c 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,13 +1,17 @@ //--------------------------------------------------------------- MainWindow #include "mainwindow.h" #include "ui_mainwindow.h" + +#include + +#include + #include "devsetup.h" #include "plotter.h" #include "about.h" #include "widegraph.h" #include "sleep.h" #include "getfile.h" -#include #include "logqso.h" #ifdef QT5 @@ -15,8 +19,14 @@ #include #endif -int itone[126]; //Audio tones for all Tx symbols -int icw[250]; //Dits for CW ID +#define NUM_JT65_SYMBOLS 126 +#define NUM_JT9_SYMBOLS 85 +#define NUM_CW_SYMBOLS 250 +#define TX_SAMPLE_RATE 48000 + +int itone[NUM_JT65_SYMBOLS]; //Audio tones for all Tx symbols +int icw[NUM_CW_SYMBOLS]; //Dits for CW ID + int outBufSize; int rc; qint32 g_COMportOpen; @@ -41,10 +51,16 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \ qint32 fontSize2, qint32 fontWeight2, \ QWidget *parent) : QMainWindow(parent), - ui(new Ui::MainWindow) + ui(new Ui::MainWindow), + m_audioInputDevice (QAudioDeviceInfo::defaultInputDevice ()), // start with default + m_detector (RX_SAMPLE_RATE, NTMAX / 2, 6912 / 2 * sizeof (jt9com_.d2[0]), this), + m_audioOutputDevice (QAudioDeviceInfo::defaultOutputDevice ()), // start with default + m_modulator (TX_SAMPLE_RATE, NTMAX / 2, this) { ui->setupUi(this); + m_detector.open (); + on_EraseButton_clicked(); QActionGroup* paletteGroup = new QActionGroup(this); ui->actionCuteSDR->setActionGroup(paletteGroup); @@ -83,12 +99,15 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \ SLOT(doubleClickOnCall2(bool,bool))); setWindowTitle(Program_Title_Version); - connect(&m_soundInput, SIGNAL(readyForFFT(int)), - this, SLOT(dataSink(int))); + connect(&m_detector, &Detector::bytesWritten, this, &MainWindow::dataSink); connect(&m_soundInput, SIGNAL(error(QString)), this, SLOT(showSoundInError(QString))); + connect(&m_soundOutput, SIGNAL(error(QString)), this, + SLOT(showSoundOutError(QString))); connect(&m_soundInput, SIGNAL(status(QString)), this, SLOT(showStatusMessage(QString))); + // connect(&m_soundOutput, SIGNAL(status(QString)), this, + // SLOT(showStatusMessage(QString))); createStatusBar(); connect(&proc_jt9, SIGNAL(readyReadStandardOutput()), @@ -146,7 +165,7 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \ m_auto=false; m_waterfallAvg = 1; m_txFirst=false; - m_soundOutput.mute(false); + m_modulator.mute(false); m_btxMute=false; m_btxok=false; m_restart=false; @@ -299,11 +318,11 @@ MainWindow::MainWindow(QSharedMemory *shdmem, QString *thekey, \ watcher2 = new QFutureWatcher; connect(watcher2, SIGNAL(finished()),this,SLOT(diskWriteFinished())); - m_soundInput.start(m_paInDevice); - m_soundOutput.setTxFreq(m_txFreq); - m_soundOutput.tune(false); + m_soundInput.start(m_audioInputDevice, RX_SAMPLE_RATE / 10, &m_detector); + m_modulator.setFrequency(m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0)); + m_modulator.tune(false); m_monitoring=!m_monitorStartOFF; // Start with Monitoring ON/OFF - m_soundInput.setMonitoring(m_monitoring); + m_detector.setMonitoring(m_monitoring); m_diskData=false; // Create "m_worked", a dictionary of all calls in wsjtx.log @@ -371,10 +390,12 @@ MainWindow::~MainWindow() { writeSettings(); m_soundOutput.stop(); + m_modulator.close(); if(!m_decoderBusy) { QFile lockFile(m_appDir + "/.lock"); lockFile.remove(); } + m_detector.close (); delete ui; } @@ -404,19 +425,8 @@ void MainWindow::writeSettings() settings.setValue("PTTmethod",m_pttMethodIndex); settings.setValue("PTTport",m_pttPort); settings.setValue("SaveDir",m_saveDir); - char soundName[128]; - if (Pa_GetDeviceInfo( m_paInDevice)) { // store name, number may be different next time - snprintf( soundName, sizeof( soundName), "%s:%s", - Pa_GetHostApiInfo( Pa_GetDeviceInfo( m_paInDevice)->hostApi)->name, - Pa_GetDeviceInfo( m_paInDevice)->name); - settings.setValue("SoundInName", soundName); - } - if (Pa_GetDeviceInfo( m_paOutDevice)) { // store name, number may be different next time - snprintf( soundName, sizeof( soundName), "%s:%s", - Pa_GetHostApiInfo( Pa_GetDeviceInfo( m_paOutDevice)->hostApi)->name, - Pa_GetDeviceInfo( m_paOutDevice)->name); - settings.setValue("SoundOutName", soundName); - } + settings.setValue("SoundInName", m_audioInputDevice.deviceName ()); + settings.setValue("SoundOutName", m_audioOutputDevice.deviceName ()); settings.setValue("PaletteCuteSDR",ui->actionCuteSDR->isChecked()); settings.setValue("PaletteLinrad",ui->actionLinrad->isChecked()); settings.setValue("PaletteAFMHot",ui->actionAFMHot->isChecked()); @@ -511,33 +521,34 @@ void MainWindow::readSettings() m_pttPort=settings.value("PTTport",0).toInt(); m_saveDir=settings.value("SaveDir",m_appDir + "/save").toString(); - char soundName[128]; - QString savedName = settings.value( "SoundInName", "default").toString(); - for (m_paInDevice = Pa_GetDeviceCount() - 1; m_paInDevice >= 0; m_paInDevice--) { - snprintf( soundName, sizeof( soundName), "%s:%s", - Pa_GetHostApiInfo( Pa_GetDeviceInfo( m_paInDevice)->hostApi)->name, - Pa_GetDeviceInfo( m_paInDevice)->name); - if ((savedName == soundName) && (Pa_GetDeviceInfo(m_paInDevice)->maxInputChannels > 0)) - break; - } - if (m_paInDevice < 0) { // no match for device name? - m_paInDevice = Pa_GetDefaultInputDevice(); - if (m_paInDevice == paNoDevice) // no default input device? - m_paInDevice = 0; + { + // + // retrieve audio input device + // + QString savedName = settings.value( "SoundInName", "default").toString(); + QList audioInputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioInput)); // available audio input devices + for (QList::const_iterator p = audioInputDevices.begin (); p != audioInputDevices.end (); ++p) + { + if (p->deviceName () == savedName) + { + m_audioInputDevice = *p; + } + } } - savedName = settings.value("SoundOutName", "default").toString(); - for (m_paOutDevice = Pa_GetDeviceCount() - 1; m_paOutDevice >= 0; m_paOutDevice--) { - snprintf( soundName, sizeof( soundName), "%s:%s", - Pa_GetHostApiInfo( Pa_GetDeviceInfo( m_paOutDevice)->hostApi)->name, - Pa_GetDeviceInfo( m_paOutDevice)->name); - if ((savedName == soundName) && (Pa_GetDeviceInfo(m_paOutDevice)->maxOutputChannels > 0)) - break; - } - if (m_paOutDevice < 0) { // no match for device name? - m_paOutDevice = Pa_GetDefaultOutputDevice(); - if (m_paOutDevice == paNoDevice) // no default output device? - m_paOutDevice = 0; + { + // + // retrieve audio output device + // + QString savedName = settings.value("SoundOutName", "default").toString(); + QList audioOutputDevices (QAudioDeviceInfo::availableDevices (QAudio::AudioOutput)); // available audio output devices + for (QList::const_iterator p = audioOutputDevices.begin (); p != audioOutputDevices.end (); ++p) + { + if (p->deviceName () == savedName) + { + m_audioOutputDevice = *p; + } + } } ui->actionCuteSDR->setChecked(settings.value( @@ -563,7 +574,7 @@ void MainWindow::readSettings() ui->RxFreqSpinBox->setValue(m_rxFreq); m_txFreq=settings.value("TxFreq",1500).toInt(); ui->TxFreqSpinBox->setValue(m_txFreq); - m_soundOutput.setTxFreq(m_txFreq); + m_modulator.setFrequency(m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0)); m_saveDecoded=ui->actionSave_decoded->isChecked(); m_saveAll=ui->actionSave_all->isChecked(); m_ndepth=settings.value("NDepth",3).toInt(); @@ -655,7 +666,7 @@ void MainWindow::readSettings() } //-------------------------------------------------------------- dataSink() -void MainWindow::dataSink(int k) +void MainWindow::dataSink(qint64 bytes) { static float s[NSMAX]; static int ihsym=0; @@ -676,6 +687,7 @@ void MainWindow::dataSink(int k) trmin=m_TRperiod/60; slope=0.0; if(g_pWideGraph!=NULL) slope=(float)g_pWideGraph->getSlope(); + int k (bytes / sizeof (jt9com_.d2[0]) - 1); symspec_(&k,&trmin,&m_nsps,&m_inGain,&slope,&px,s,&df3,&ihsym,&npts8); if(ihsym <=0) return; QString t; @@ -710,7 +722,10 @@ void MainWindow::dataSink(int k) } void MainWindow::showSoundInError(const QString& errorMsg) - {QMessageBox::critical(this, tr("Error in SoundIn"), errorMsg);} + {QMessageBox::critical(this, tr("Error in SoundInput"), errorMsg);} + +void MainWindow::showSoundOutError(const QString& errorMsg) + {QMessageBox::critical(this, tr("Error in SoundOutput"), errorMsg);} void MainWindow::showStatusMessage(const QString& statusMsg) {statusBar()->showMessage(statusMsg);} @@ -724,8 +739,8 @@ void MainWindow::on_actionDeviceSetup_triggered() //Setup Dialog dlg.m_pttMethodIndex=m_pttMethodIndex; dlg.m_pttPort=m_pttPort; dlg.m_saveDir=m_saveDir; - dlg.m_paInDevice=m_paInDevice; - dlg.m_paOutDevice=m_paOutDevice; + dlg.m_audioInputDevice = m_audioInputDevice; + dlg.m_audioOutputDevice = m_audioOutputDevice; dlg.m_pskReporter=m_pskReporter; dlg.m_After73=m_After73; dlg.m_macro=m_macro; @@ -769,8 +784,8 @@ void MainWindow::on_actionDeviceSetup_triggered() //Setup Dialog m_pttMethodIndex=dlg.m_pttMethodIndex; m_pttPort=dlg.m_pttPort; m_saveDir=dlg.m_saveDir; - m_paInDevice=dlg.m_paInDevice; - m_paOutDevice=dlg.m_paOutDevice; + m_audioInputDevice = dlg.m_audioInputDevice; + m_audioOutputDevice = dlg.m_audioOutputDevice; m_macro=dlg.m_macro; m_dFreq=dlg.m_dFreq; m_antDescription=dlg.m_antDescription; @@ -826,11 +841,11 @@ void MainWindow::on_actionDeviceSetup_triggered() //Setup Dialog m_After73=dlg.m_After73; if(dlg.m_restartSoundIn) { - m_soundInput.start(m_paInDevice); + m_soundInput.start(m_audioInputDevice, RX_SAMPLE_RATE / 10, &m_detector); } if(dlg.m_restartSoundOut) { - m_soundOutput.start(m_paOutDevice,m_modeTx,m_TRperiod,m_nsps,m_txFreq,m_bSplit || m_bXIT ? m_XIT : 0); + transmit (); } } m_catEnabled=dlg.m_catEnabled; @@ -859,7 +874,7 @@ void MainWindow::on_actionDeviceSetup_triggered() //Setup Dialog void MainWindow::on_monitorButton_clicked() //Monitor { m_monitoring=true; - m_soundInput.setMonitoring(true); + m_detector.setMonitoring(true); m_diskData=false; } @@ -901,7 +916,7 @@ void MainWindow::on_autoButton_clicked() //Auto ui->autoButton->setStyleSheet(m_pbAutoOn_style); } else { m_btxok=false; - m_soundOutput.mute(); + m_modulator.mute(); ui->autoButton->setStyleSheet(""); on_monitorButton_clicked(); m_repeatMsg=0; @@ -1122,7 +1137,7 @@ void MainWindow::OnExit() void MainWindow::on_stopButton_clicked() //stopButton { m_monitoring=false; - m_soundInput.setMonitoring(m_monitoring); + m_detector.setMonitoring(m_monitoring); m_loopall=false; } @@ -1163,7 +1178,7 @@ void MainWindow::on_actionWide_Waterfall_triggered() //Display Waterfalls void MainWindow::on_actionOpen_triggered() //Open File { m_monitoring=false; - m_soundInput.setMonitoring(m_monitoring); + m_detector.setMonitoring(m_monitoring); QString fname; fname=QFileDialog::getOpenFileName(this, "Open File", m_path, "WSJT Files (*.wav)"); @@ -1223,7 +1238,7 @@ void MainWindow::diskDat() //diskDat() for(int n=1; n<=m_hsymStop; n++) { // Do the half-symbol FFTs k=(n+1)*kstep; jt9com_.npts8=k/8; - dataSink(k); + dataSink(k * sizeof (jt9com_.d2[0])); if(n%10 == 1 or n == m_hsymStop) qApp->processEvents(); //Keep GUI responsive } @@ -1718,7 +1733,7 @@ void MainWindow::guiUpdate() } if(!bTxTime || m_btxMute) { m_btxok=false; - m_soundOutput.mute(); + m_modulator.mute(); } } @@ -1815,9 +1830,9 @@ void MainWindow::guiUpdate() signalMeter->setValue(0); m_monitoring=false; - m_soundInput.setMonitoring(false); + m_detector.setMonitoring(false); m_btxok=true; - m_soundOutput.mute(false); + m_modulator.mute(false); m_transmitting=true; ui->pbTxMode->setEnabled(false); if(!m_tune) { @@ -1940,12 +1955,12 @@ void MainWindow::startTx2() QString t=ui->tx6->text(); double snr=t.mid(1,5).toDouble(); if(snr>0.0 or snr < -50.0) snr=99.0; - m_soundOutput.start(m_paOutDevice,m_modeTx,m_TRperiod,m_nsps,m_txFreq,m_bSplit || m_bXIT ? m_XIT : 0,snr); + transmit (snr); signalMeter->setValue(0); m_monitoring=false; - m_soundInput.setMonitoring(false); + m_detector.setMonitoring(false); m_btxok=true; - m_soundOutput.mute(false); + m_modulator.mute(false); m_transmitting=true; ui->pbTxMode->setEnabled(false); } @@ -1954,6 +1969,7 @@ void MainWindow::startTx2() void MainWindow::stopTx() { m_soundOutput.stop(); + m_modulator.close (); m_transmitting=false; ui->pbTxMode->setEnabled(true); g_iptt=0; @@ -1961,7 +1977,7 @@ void MainWindow::stopTx() lab1->setText(""); ptt0Timer->start(200); //Sequencer delay m_monitoring=true; - m_soundInput.setMonitoring(true); + m_detector.setMonitoring(true); } void MainWindow::stopTx2() @@ -2573,7 +2589,6 @@ void MainWindow::on_actionJT9_1_triggered() m_TRperiod=60; m_nsps=6912; m_hsymStop=173; - m_soundInput.setPeriod(m_TRperiod,m_nsps); lab3->setStyleSheet("QLabel{background-color: #ff6ec7}"); lab3->setText(m_mode); ui->actionJT9_1->setChecked(true); @@ -2591,7 +2606,6 @@ void MainWindow::on_actionJT65_triggered() m_TRperiod=60; m_nsps=6912; //For symspec only m_hsymStop=173; - m_soundInput.setPeriod(m_TRperiod,m_nsps); lab3->setStyleSheet("QLabel{background-color: #ffff00}"); lab3->setText(m_mode); ui->actionJT65->setChecked(true); @@ -2609,7 +2623,6 @@ void MainWindow::on_actionJT9_JT65_triggered() m_TRperiod=60; m_nsps=6912; m_hsymStop=173; - m_soundInput.setPeriod(m_TRperiod,m_nsps); lab3->setStyleSheet("QLabel{background-color: #ffa500}"); lab3->setText(m_mode); ui->actionJT9_JT65->setChecked(true); @@ -2624,7 +2637,7 @@ void MainWindow::on_TxFreqSpinBox_valueChanged(int n) m_txFreq=n; if(g_pWideGraph!=NULL) g_pWideGraph->setTxFreq(n); if(m_lockTxFreq) ui->RxFreqSpinBox->setValue(n); - m_soundOutput.setTxFreq(n); + m_modulator.setFrequency(m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0)); } void MainWindow::on_RxFreqSpinBox_valueChanged(int n) @@ -2954,7 +2967,7 @@ void MainWindow::on_tuneButton_clicked() } else { m_tune=true; m_sent73=false; - m_soundOutput.tune(m_tune); + m_modulator.tune(); m_repeatMsg=0; ui->tuneButton->setStyleSheet(m_pbTune_style); } @@ -2964,11 +2977,11 @@ void MainWindow::on_stopTxButton_clicked() //Stop Tx { if(m_tune) { m_tune=false; - m_soundOutput.tune(m_tune); + m_modulator.tune(m_tune); } if(m_auto) on_autoButton_clicked(); m_btxok=false; - m_soundOutput.mute(); + m_modulator.mute(); m_repeatMsg=0; ui->tuneButton->setStyleSheet(""); } @@ -3024,15 +3037,20 @@ void MainWindow::rigOpen() } else { ui->readFreq->setStyleSheet("QPushButton{background-color: orange; \ border-width: 0px; border-radius: 5px;}"); -} -if(m_bSplit) ui->readFreq->setText("S"); -if(!m_bSplit) ui->readFreq->setText(""); -} else { -if(m_CATerror) ui->readFreq->setStyleSheet("QPushButton{background-color: red; \ + } + + QFont font=ui->readFreq->font(); + font.setPointSize(9); + font.setWeight(75); + ui->readFreq->setFont(font); + if(m_bSplit) ui->readFreq->setText("S"); + if(!m_bSplit) ui->readFreq->setText(""); + } else { + if(m_CATerror) ui->readFreq->setStyleSheet("QPushButton{background-color: red; \ border-width: 0px; border-radius: 5px;}"); -if(!m_CATerror) ui->readFreq->setStyleSheet(""); -ui->readFreq->setText(""); -} + if(!m_CATerror) ui->readFreq->setStyleSheet(""); + ui->readFreq->setText(""); + } } void MainWindow::on_actionAllow_multiple_instances_triggered(bool checked) @@ -3104,8 +3122,7 @@ void MainWindow::setXIT(int n) ret=rig->setSplitFreq(MHz(m_dialFreq)+m_XIT,RIG_VFO_B); } } - if(m_bSplit) m_soundOutput.setXIT(m_XIT); - if(!m_bSplit) m_soundOutput.setXIT(0); + m_modulator.setFrequency(m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0)); } void MainWindow::setFreq4(int rxFreq, int txFreq) @@ -3151,10 +3168,29 @@ void MainWindow::pollRigFreq() m_catEnabled=false; ui->readFreq->setStyleSheet("QPushButton{background-color: red; \ border-width: 0px; border-radius: 5px;}"); + } + } else { + int ndiff=1000000.0*(fMHz-m_dialFreq); + if(ndiff!=0) dialFreqChanged2(fMHz); } - } else { - int ndiff=1000000.0*(fMHz-m_dialFreq); - if(ndiff!=0) dialFreqChanged2(fMHz); } } + +void MainWindow::transmit (double snr) +{ + QScopedPointer > cw (new std::vector (NUM_CW_SYMBOLS)); + cw->assign (icw, icw + NUM_CW_SYMBOLS); // load data + if (m_modeTx == "JT65") + { + QScopedPointer > symbols (new std::vector (NUM_JT65_SYMBOLS)); + symbols->assign (itone, itone + NUM_JT65_SYMBOLS); // load data + m_modulator.open (symbols.take (), cw.take (), 4096.0 * 12000.0 / 11025.0, m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0), snr); + } + else + { + QScopedPointer > symbols (new std::vector (NUM_JT65_SYMBOLS)); + symbols->assign (itone, itone + NUM_JT9_SYMBOLS); // load data + m_modulator.open (symbols.take (), cw.take (), m_nsps, m_txFreq - (m_bSplit || m_bXIT ? m_XIT : 0), snr); + } + m_soundOutput.start(m_audioOutputDevice, &m_modulator); } diff --git a/mainwindow.h b/mainwindow.h index f42d982f9..81eb76924 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -7,6 +7,9 @@ #endif #include #include +#include +#include + #include "soundin.h" #include "soundout.h" #include "commons.h" @@ -14,6 +17,8 @@ #include "rigclass.h" #include "signalmeter.h" #include "logbook/logbook.h" +#include "Detector.hpp" +#include "Modulator.hpp" #ifdef WIN32 #include "PSKReporter.h" @@ -37,8 +42,9 @@ public: public slots: void showSoundInError(const QString& errorMsg); + void showSoundOutError(const QString& errorMsg); void showStatusMessage(const QString& statusMsg); - void dataSink(int k); + void dataSink(qint64 bytes); void diskDat(); void diskWriteFinished(); void freezeDecode(int n); @@ -191,8 +197,10 @@ private: qint32 m_nutc0; qint32 m_nrx; qint32 m_hsym; - qint32 m_paInDevice; - qint32 m_paOutDevice; + QAudioDeviceInfo m_audioInputDevice; + Detector m_detector; + QAudioDeviceInfo m_audioOutputDevice; + Modulator m_modulator; qint32 m_TRperiod; qint32 m_nsps; qint32 m_hsymStop; @@ -372,6 +380,7 @@ private: void pollRigFreq(); bool gridOK(QString g); QString baseCall(QString t); + void transmit (double snr = 99.); }; extern void getfile(QString fname, int ntrperiod); diff --git a/meterwidget.cpp b/meterwidget.cpp index b48aa65d0..6a464a015 100644 --- a/meterwidget.cpp +++ b/meterwidget.cpp @@ -1,51 +1,51 @@ -// Simple bargraph meter -// Implemented by Edson Pereira PY2SDR - -#include "meterwidget.h" - -MeterWidget::MeterWidget(QWidget *parent) : - QWidget(parent), - m_signal(0) -{ - for ( int i = 0; i < 10; i++ ) { - signalQueue.enqueue(0); - } -} - -void MeterWidget::setValue(int value) -{ - m_signal = value; - signalQueue.enqueue(value); - signalQueue.dequeue(); - - // Get signal peak - int tmp = 0; - for (int i = 0; i < signalQueue.size(); ++i) { - if (signalQueue.at(i) > tmp) - tmp = signalQueue.at(i); - } - m_sigPeak = tmp; - - update(); -} - -void MeterWidget::paintEvent( QPaintEvent * ) -{ - int pos; - QPainter p; - - p.begin(this); - - // Sanitize - m_signal = m_signal < 0 ? 0 : m_signal; - m_signal = m_signal > 60 ? 60 : m_signal; - - pos = m_signal * 2; - QRect r(0, height() - pos, width(), pos ); - p.fillRect(r, QColor( 255, 150, 0 )); - - // Draw peak hold indicator - p.setPen(Qt::black); - pos = m_sigPeak * 2; - p.drawLine(0, height() - pos, 10, height() - pos); -} +// Simple bargraph meter +// Implemented by Edson Pereira PY2SDR + +#include "meterwidget.h" + +MeterWidget::MeterWidget(QWidget *parent) : + QWidget(parent), + m_signal(0) +{ + for ( int i = 0; i < 10; i++ ) { + signalQueue.enqueue(0); + } +} + +void MeterWidget::setValue(int value) +{ + m_signal = value; + signalQueue.enqueue(value); + signalQueue.dequeue(); + + // Get signal peak + int tmp = 0; + for (int i = 0; i < signalQueue.size(); ++i) { + if (signalQueue.at(i) > tmp) + tmp = signalQueue.at(i); + } + m_sigPeak = tmp; + + update(); +} + +void MeterWidget::paintEvent( QPaintEvent * ) +{ + int pos; + QPainter p; + + p.begin(this); + + // Sanitize + m_signal = m_signal < 0 ? 0 : m_signal; + m_signal = m_signal > 60 ? 60 : m_signal; + + pos = m_signal * 2; + QRect r(0, height() - pos, width(), pos ); + p.fillRect(r, QColor( 255, 150, 0 )); + + // Draw peak hold indicator + p.setPen(Qt::black); + pos = m_sigPeak * 2; + p.drawLine(0, height() - pos, 10, height() - pos); +} diff --git a/meterwidget.h b/meterwidget.h index 7b51efb65..af491fd23 100644 --- a/meterwidget.h +++ b/meterwidget.h @@ -1,30 +1,30 @@ -#ifndef METERWIDGET_H -#define METERWIDGET_H - -#include -#include -#include - -class MeterWidget : public QWidget -{ - Q_OBJECT -public: - explicit MeterWidget(QWidget *parent = 0); - -signals: - -public slots: - void setValue(int value); - -private: - QQueue signalQueue; - - int m_signal; - int m_sigPeak; - -protected: - void paintEvent( QPaintEvent * ); - -}; - -#endif // METERWIDGET_H +#ifndef METERWIDGET_H +#define METERWIDGET_H + +#include +#include +#include + +class MeterWidget : public QWidget +{ + Q_OBJECT +public: + explicit MeterWidget(QWidget *parent = 0); + +signals: + +public slots: + void setValue(int value); + +private: + QQueue signalQueue; + + int m_signal; + int m_sigPeak; + +protected: + void paintEvent( QPaintEvent * ); + +}; + +#endif // METERWIDGET_H diff --git a/psk_reporter.cpp b/psk_reporter.cpp index c991de57b..cedf9b5a7 100644 --- a/psk_reporter.cpp +++ b/psk_reporter.cpp @@ -1,112 +1,112 @@ -// KISS Interface for posting spots to PSK Reporter web site -// Implemented by Edson Pereira PY2SDR -// -// Reports will be sent in batch mode every 5 minutes. - -#include "psk_reporter.h" - -PSK_Reporter::PSK_Reporter(QObject *parent) : - QObject(parent), - m_sequenceNumber(0) -{ - m_header_h = "000Allllttttttttssssssssiiiiiiii"; - - // We use 50E2 and 50E3 for link Id - m_rxInfoDescriptor_h = "0003002C50E200040000" - "8002FFFF0000768F" // 2. Rx Call - "8004FFFF0000768F" // 4. Rx Grid - "8008FFFF0000768F" // 8. Rx Soft - "8009FFFF0000768F" // 9. Rx Antenna - "0000"; - - m_txInfoDescriptor_h = "0002003C50E30007" - "8001FFFF0000768F" // 1. Tx Call - "800500040000768F" // 5. Tx Freq - "800600010000768F" // 6. Tx snr - "800AFFFF0000768F" // 10. Tx Mode - "8003FFFF0000768F" // 3. Tx Grid - "800B00010000768F" // 11. Tx info src - "00960004"; // Report time - - - qsrand(QDateTime::currentDateTime().toTime_t()); - m_randomId_h = QString("%1").arg(qrand(),8,16,QChar('0')); - - m_udpSocket = new QUdpSocket(this); - - reportTimer = new QTimer(this); - connect(reportTimer, SIGNAL(timeout()), this, SLOT(sendReport())); - reportTimer->start(5*60*1000); // 5 minutes; -} - -void PSK_Reporter::setLocalStation(QString call, QString gridSquare, QString antenna, QString programInfo) -{ - m_rxCall = call; - m_rxGrid = gridSquare; - m_rxAnt = antenna; - m_progId = programInfo; - //qDebug() << "PSK_Reporter::setLocalStation. Antenna:" << antenna; -} - -void PSK_Reporter::addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time ) -{ - QHash spot; - spot["call"] = call; - spot["grid"] = grid; - spot["snr"] = snr; - spot["freq"] = freq; - spot["mode"] = mode; - spot["time"] = time; - m_spotQueue.enqueue(spot); -} - -void PSK_Reporter::sendReport() -{ - QString report_h; - - // Header - QString header_h = m_header_h; - header_h.replace("tttttttt", QString("%1").arg(QDateTime::currentDateTime().toTime_t(),8,16,QChar('0'))); - header_h.replace("ssssssss", QString("%1").arg(++m_sequenceNumber,8,16,QChar('0'))); - header_h.replace("iiiiiiii", m_randomId_h); - - // Receiver information - QString rxInfoData_h = "50E2llll"; - rxInfoData_h += QString("%1").arg(m_rxCall.length(),2,16,QChar('0')) + m_rxCall.toUtf8().toHex(); - rxInfoData_h += QString("%1").arg(m_rxGrid.length(),2,16,QChar('0')) + m_rxGrid.toUtf8().toHex(); - rxInfoData_h += QString("%1").arg(m_progId.length(),2,16,QChar('0')) + m_progId.toUtf8().toHex(); - rxInfoData_h += QString("%1").arg(m_rxAnt.length(),2,16,QChar('0')) + m_rxAnt.toUtf8().toHex(); - rxInfoData_h += "0000"; - rxInfoData_h.replace("50E2llll", "50E2" + QString("%1").arg(rxInfoData_h.length()/2,4,16,QChar('0'))); - - // Sender information - if (! m_spotQueue.isEmpty()) { - QString txInfoData_h = "50E3llll"; - while (!m_spotQueue.isEmpty()) { - QHash spot = m_spotQueue.dequeue(); - txInfoData_h += QString("%1").arg(spot["call"].length(),2,16,QChar('0')) + spot["call"].toUtf8().toHex(); - txInfoData_h += QString("%1").arg(spot["freq"].toLongLong(),8,16,QChar('0')); - txInfoData_h += QString("%1").arg(spot["snr"].toInt(),8,16,QChar('0')).right(2); - txInfoData_h += QString("%1").arg(spot["mode"].length(),2,16,QChar('0')) + spot["mode"].toUtf8().toHex(); - txInfoData_h += QString("%1").arg(spot["grid"].length(),2,16,QChar('0')) + spot["grid"].toUtf8().toHex(); - txInfoData_h += QString("%1").arg(1,2,16,QChar('0')); // REPORTER_SOURCE_AUTOMATIC - txInfoData_h += QString("%1").arg(spot["time"].toInt(),8,16,QChar('0')); - } - txInfoData_h += "0000"; - txInfoData_h.replace("50E3llll", "50E3" + QString("%1").arg(txInfoData_h.length()/2,4,16,QChar('0'))); - report_h = header_h + m_rxInfoDescriptor_h + m_txInfoDescriptor_h + rxInfoData_h + txInfoData_h; - //qDebug() << "Sending Report TX: "; - } else { - report_h = header_h + m_rxInfoDescriptor_h + rxInfoData_h; - //qDebug() << "Sending Report RX: "; - } - - report_h.replace("000Allll", "000A" + QString("%1").arg(report_h.length()/2,4,16,QChar('0'))); - QByteArray report = QByteArray::fromHex(report_h.toUtf8()); - - // Get IP address for pskreporter.info and send report via UDP - QHostInfo info = QHostInfo::fromName("report.pskreporter.info"); - m_udpSocket->writeDatagram(report,info.addresses().at(0),4739); -} - - +// KISS Interface for posting spots to PSK Reporter web site +// Implemented by Edson Pereira PY2SDR +// +// Reports will be sent in batch mode every 5 minutes. + +#include "psk_reporter.h" + +PSK_Reporter::PSK_Reporter(QObject *parent) : + QObject(parent), + m_sequenceNumber(0) +{ + m_header_h = "000Allllttttttttssssssssiiiiiiii"; + + // We use 50E2 and 50E3 for link Id + m_rxInfoDescriptor_h = "0003002C50E200040000" + "8002FFFF0000768F" // 2. Rx Call + "8004FFFF0000768F" // 4. Rx Grid + "8008FFFF0000768F" // 8. Rx Soft + "8009FFFF0000768F" // 9. Rx Antenna + "0000"; + + m_txInfoDescriptor_h = "0002003C50E30007" + "8001FFFF0000768F" // 1. Tx Call + "800500040000768F" // 5. Tx Freq + "800600010000768F" // 6. Tx snr + "800AFFFF0000768F" // 10. Tx Mode + "8003FFFF0000768F" // 3. Tx Grid + "800B00010000768F" // 11. Tx info src + "00960004"; // Report time + + + qsrand(QDateTime::currentDateTime().toTime_t()); + m_randomId_h = QString("%1").arg(qrand(),8,16,QChar('0')); + + m_udpSocket = new QUdpSocket(this); + + reportTimer = new QTimer(this); + connect(reportTimer, SIGNAL(timeout()), this, SLOT(sendReport())); + reportTimer->start(5*60*1000); // 5 minutes; +} + +void PSK_Reporter::setLocalStation(QString call, QString gridSquare, QString antenna, QString programInfo) +{ + m_rxCall = call; + m_rxGrid = gridSquare; + m_rxAnt = antenna; + m_progId = programInfo; + //qDebug() << "PSK_Reporter::setLocalStation. Antenna:" << antenna; +} + +void PSK_Reporter::addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time ) +{ + QHash spot; + spot["call"] = call; + spot["grid"] = grid; + spot["snr"] = snr; + spot["freq"] = freq; + spot["mode"] = mode; + spot["time"] = time; + m_spotQueue.enqueue(spot); +} + +void PSK_Reporter::sendReport() +{ + QString report_h; + + // Header + QString header_h = m_header_h; + header_h.replace("tttttttt", QString("%1").arg(QDateTime::currentDateTime().toTime_t(),8,16,QChar('0'))); + header_h.replace("ssssssss", QString("%1").arg(++m_sequenceNumber,8,16,QChar('0'))); + header_h.replace("iiiiiiii", m_randomId_h); + + // Receiver information + QString rxInfoData_h = "50E2llll"; + rxInfoData_h += QString("%1").arg(m_rxCall.length(),2,16,QChar('0')) + m_rxCall.toUtf8().toHex(); + rxInfoData_h += QString("%1").arg(m_rxGrid.length(),2,16,QChar('0')) + m_rxGrid.toUtf8().toHex(); + rxInfoData_h += QString("%1").arg(m_progId.length(),2,16,QChar('0')) + m_progId.toUtf8().toHex(); + rxInfoData_h += QString("%1").arg(m_rxAnt.length(),2,16,QChar('0')) + m_rxAnt.toUtf8().toHex(); + rxInfoData_h += "0000"; + rxInfoData_h.replace("50E2llll", "50E2" + QString("%1").arg(rxInfoData_h.length()/2,4,16,QChar('0'))); + + // Sender information + if (! m_spotQueue.isEmpty()) { + QString txInfoData_h = "50E3llll"; + while (!m_spotQueue.isEmpty()) { + QHash spot = m_spotQueue.dequeue(); + txInfoData_h += QString("%1").arg(spot["call"].length(),2,16,QChar('0')) + spot["call"].toUtf8().toHex(); + txInfoData_h += QString("%1").arg(spot["freq"].toLongLong(),8,16,QChar('0')); + txInfoData_h += QString("%1").arg(spot["snr"].toInt(),8,16,QChar('0')).right(2); + txInfoData_h += QString("%1").arg(spot["mode"].length(),2,16,QChar('0')) + spot["mode"].toUtf8().toHex(); + txInfoData_h += QString("%1").arg(spot["grid"].length(),2,16,QChar('0')) + spot["grid"].toUtf8().toHex(); + txInfoData_h += QString("%1").arg(1,2,16,QChar('0')); // REPORTER_SOURCE_AUTOMATIC + txInfoData_h += QString("%1").arg(spot["time"].toInt(),8,16,QChar('0')); + } + txInfoData_h += "0000"; + txInfoData_h.replace("50E3llll", "50E3" + QString("%1").arg(txInfoData_h.length()/2,4,16,QChar('0'))); + report_h = header_h + m_rxInfoDescriptor_h + m_txInfoDescriptor_h + rxInfoData_h + txInfoData_h; + //qDebug() << "Sending Report TX: "; + } else { + report_h = header_h + m_rxInfoDescriptor_h + rxInfoData_h; + //qDebug() << "Sending Report RX: "; + } + + report_h.replace("000Allll", "000A" + QString("%1").arg(report_h.length()/2,4,16,QChar('0'))); + QByteArray report = QByteArray::fromHex(report_h.toUtf8()); + + // Get IP address for pskreporter.info and send report via UDP + QHostInfo info = QHostInfo::fromName("report.pskreporter.info"); + m_udpSocket->writeDatagram(report,info.addresses().at(0),4739); +} + + diff --git a/psk_reporter.h b/psk_reporter.h index ce1b01969..79fc703f1 100644 --- a/psk_reporter.h +++ b/psk_reporter.h @@ -1,42 +1,42 @@ -#ifndef PSK_REPORTER_H -#define PSK_REPORTER_H - -#include -#include -#include - -class PSK_Reporter : public QObject -{ - Q_OBJECT -public: - explicit PSK_Reporter(QObject *parent = 0); - void setLocalStation(QString call, QString grid, QString antenna, QString programInfo); - void addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time); - -signals: - -public slots: - void sendReport(); - -private: - QString m_header_h; - QString m_rxInfoDescriptor_h; - QString m_txInfoDescriptor_h; - QString m_randomId_h; - QString m_linkId_h; - - QString m_rxCall; - QString m_rxGrid; - QString m_rxAnt; - QString m_progId; - - QQueue< QHash > m_spotQueue; - - QUdpSocket *m_udpSocket; - - QTimer *reportTimer; - - int m_sequenceNumber; -}; - -#endif // PSK_REPORTER_H +#ifndef PSK_REPORTER_H +#define PSK_REPORTER_H + +#include +#include +#include + +class PSK_Reporter : public QObject +{ + Q_OBJECT +public: + explicit PSK_Reporter(QObject *parent = 0); + void setLocalStation(QString call, QString grid, QString antenna, QString programInfo); + void addRemoteStation(QString call, QString grid, QString freq, QString mode, QString snr, QString time); + +signals: + +public slots: + void sendReport(); + +private: + QString m_header_h; + QString m_rxInfoDescriptor_h; + QString m_txInfoDescriptor_h; + QString m_randomId_h; + QString m_linkId_h; + + QString m_rxCall; + QString m_rxGrid; + QString m_rxAnt; + QString m_progId; + + QQueue< QHash > m_spotQueue; + + QUdpSocket *m_udpSocket; + + QTimer *reportTimer; + + int m_sequenceNumber; +}; + +#endif // PSK_REPORTER_H diff --git a/rigclass.cpp b/rigclass.cpp index b17f2000d..51683ccae 100644 --- a/rigclass.cpp +++ b/rigclass.cpp @@ -1,332 +1,332 @@ -/** - * \file src/rigclass.cc - * \brief Ham Radio Control Libraries C++ interface - * \author Stephane Fillod - * \date 2001-2003 - * - * Hamlib C++ interface is a frontend implementing wrapper functions. - */ - -/** - * - * Hamlib C++ bindings - main file - * Copyright (c) 2001-2003 by Stephane Fillod - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include "rigclass.h" -#include -#include - -#define NUMTRIES 5 - -static int hamlibpp_freq_event(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg); - -static int hamlibpp_freq_event(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg) -{ - if (!rig || !rig->state.obj) - return -RIG_EINVAL; - -/* assert rig == ((Rig*)rig->state.obj).theRig */ - return ((Rig*)rig->state.obj)->FreqEvent(vfo, freq, arg); -} - -Rig::Rig() -{ - rig_set_debug_level( RIG_DEBUG_WARN); -} - -Rig::~Rig() { - theRig->state.obj = NULL; - rig_cleanup(theRig); - caps = NULL; -} - -int Rig::init(rig_model_t rig_model) -{ - int initOk; - - theRig = rig_init(rig_model); - if (!theRig) - initOk = false; - else - initOk = true; - - caps = theRig->caps; - theRig->callbacks.freq_event = &hamlibpp_freq_event; - theRig->state.obj = (rig_ptr_t)this; - - return initOk; -} - -int Rig::open(int n) { - m_hrd=false; - m_cmndr=false; - if(n<9900) { - if(n==-99999) return -1; //Silence compiler warning - return rig_open(theRig); - } - -#ifdef WIN32 // Ham radio Deluxe or Commander (Windows only) - if(n==9999) { - m_hrd=true; - bool bConnect=false; - bConnect = HRDInterfaceConnect(L"localhost",7809); - if(bConnect) { - const wchar_t* context=HRDInterfaceSendMessage(L"Get Context"); - m_context="[" + QString::fromWCharArray (context,-1) + "] "; - HRDInterfaceFreeString(context); - return 0; - } else { - m_hrd=false; - return -1; - } - } - if(n==9998) { - if(commanderSocket->state()==QAbstractSocket::ConnectedState) { - commanderSocket->abort(); - } - - if(commanderSocket->state()==QAbstractSocket::UnconnectedState) { - commanderSocket->connectToHost(QHostAddress::LocalHost, 52002); - if(!commanderSocket->waitForConnected(1000)) { - return -1; - } - } - QString t; - t="CmdGetFreq"; - QByteArray ba = t.toLocal8Bit(); - const char* buf=ba.data(); - commanderSocket->write(buf); - commanderSocket->waitForReadyRead(1000); - QByteArray reply=commanderSocket->read(128); - if(reply.indexOf("close(); - return 0; - } else -#endif - { - return rig_close(theRig); - } -} - -int Rig::setConf(const char *name, const char *val) -{ - return rig_set_conf(theRig, tokenLookup(name), val); -} - -int Rig::setFreq(freq_t freq, vfo_t vfo) { -#ifdef WIN32 // Ham Radio Deluxe (only on Windows) - if(m_hrd) { - QString t; - int nhz=(int)freq; - t=m_context + "Set Frequency-Hz " + QString::number(nhz); - const wchar_t* cmnd = (const wchar_t*) t.utf16(); - const wchar_t* result=HRDInterfaceSendMessage(cmnd); - QString t2=QString::fromWCharArray (result,-1); - HRDInterfaceFreeString(result); - if(t2=="OK") { - return 0; - } else { - return -1; - } - } else if(m_cmndr) { - QString t; - double f=0.001*freq; - t.sprintf("CmdSetFreq%10.3f",f); - QLocale locale; - t.replace(".",locale.decimalPoint()); - QByteArray ba = t.toLocal8Bit(); - const char* buf=ba.data(); - commanderSocket->write(buf); - commanderSocket->waitForBytesWritten(1000); - return 0; - } else -#endif - { - return rig_set_freq(theRig, vfo, freq); - } -} - -int Rig::setXit(shortfreq_t xit, vfo_t vfo) -{ - return rig_set_xit(theRig, vfo, xit); -} - -int Rig::setVFO(vfo_t vfo) -{ - return rig_set_vfo(theRig, vfo); -} - -vfo_t Rig::getVFO() -{ - vfo_t vfo; - rig_get_vfo(theRig, &vfo); - return vfo; -} - -int Rig::setSplitFreq(freq_t tx_freq, vfo_t vfo) { -#ifdef WIN32 // Ham Radio Deluxe only on Windows - if(m_hrd) { - QString t; - int nhz=(int)tx_freq; - t=m_context + "Set Frequency-Hz " + QString::number(nhz); - const wchar_t* cmnd = (const wchar_t*) t.utf16(); - const wchar_t* result=HRDInterfaceSendMessage(cmnd); - QString t2=QString::fromWCharArray (result,-1); - HRDInterfaceFreeString(result); - if(t2=="OK") { - return 0; - } else { - return -1; - } - } else if(m_cmndr) { - QString t; - double f=0.001*tx_freq; - t.sprintf("CmdSetTxFreq%10.3f",f); - QLocale locale; - t.replace(".",locale.decimalPoint()); - QByteArray ba = t.toLocal8Bit(); - const char* buf=ba.data(); - commanderSocket->write(buf); - commanderSocket->waitForBytesWritten(1000); - return 0; - } else -#endif - { - return rig_set_split_freq(theRig, vfo, tx_freq); - } -} - -freq_t Rig::getFreq(vfo_t vfo) -{ - freq_t freq; -#ifdef WIN32 // Ham Radio Deluxe (only on Windows) - if(m_hrd) { - const wchar_t* cmnd = (const wchar_t*) (m_context+"Get Frequency").utf16(); - const wchar_t* freqString=HRDInterfaceSendMessage(cmnd); - QString t2=QString::fromWCharArray (freqString,-1); - HRDInterfaceFreeString(freqString); - freq=t2.toDouble(); - return freq; - } else if(m_cmndr) { - QString t; - t="CmdGetFreq"; - QByteArray ba = t.toLocal8Bit(); - const char* buf=ba.data(); - commanderSocket->write(buf); - commanderSocket->waitForReadyRead(1000); - QByteArray reply=commanderSocket->read(128); - QString t2(reply); - if(t2.indexOf(""); - t2=t2.mid(i1+1).replace(",",""); - freq=1000.0*t2.toDouble(); - return freq; - } else { - return -1.0; - } - } else -#endif - { - freq=-1.0; - for(int i=0; i0) t="CmdTX"; - QByteArray ba = t.toLocal8Bit(); - const char* buf=ba.data(); - commanderSocket->write(buf); - commanderSocket->waitForBytesWritten(1000); - return 0; - } else -#endif - { - return rig_set_ptt(theRig, vfo, ptt); - } -} - -ptt_t Rig::getPTT(vfo_t vfo) -{ - ptt_t ptt; - rig_get_ptt(theRig, vfo, &ptt); - return ptt; -} - -token_t Rig::tokenLookup(const char *name) -{ - return rig_token_lookup(theRig, name); -} +/** + * \file src/rigclass.cc + * \brief Ham Radio Control Libraries C++ interface + * \author Stephane Fillod + * \date 2001-2003 + * + * Hamlib C++ interface is a frontend implementing wrapper functions. + */ + +/** + * + * Hamlib C++ bindings - main file + * Copyright (c) 2001-2003 by Stephane Fillod + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "rigclass.h" +#include +#include + +#define NUMTRIES 5 + +static int hamlibpp_freq_event(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg); + +static int hamlibpp_freq_event(RIG *rig, vfo_t vfo, freq_t freq, rig_ptr_t arg) +{ + if (!rig || !rig->state.obj) + return -RIG_EINVAL; + +/* assert rig == ((Rig*)rig->state.obj).theRig */ + return ((Rig*)rig->state.obj)->FreqEvent(vfo, freq, arg); +} + +Rig::Rig() +{ + rig_set_debug_level( RIG_DEBUG_WARN); +} + +Rig::~Rig() { + theRig->state.obj = NULL; + rig_cleanup(theRig); + caps = NULL; +} + +int Rig::init(rig_model_t rig_model) +{ + int initOk; + + theRig = rig_init(rig_model); + if (!theRig) + initOk = false; + else + initOk = true; + + caps = theRig->caps; + theRig->callbacks.freq_event = &hamlibpp_freq_event; + theRig->state.obj = (rig_ptr_t)this; + + return initOk; +} + +int Rig::open(int n) { + m_hrd=false; + m_cmndr=false; + if(n<9900) { + if(n==-99999) return -1; //Silence compiler warning + return rig_open(theRig); + } + +#ifdef WIN32 // Ham radio Deluxe or Commander (Windows only) + if(n==9999) { + m_hrd=true; + bool bConnect=false; + bConnect = HRDInterfaceConnect(L"localhost",7809); + if(bConnect) { + const wchar_t* context=HRDInterfaceSendMessage(L"Get Context"); + m_context="[" + QString::fromWCharArray (context,-1) + "] "; + HRDInterfaceFreeString(context); + return 0; + } else { + m_hrd=false; + return -1; + } + } + if(n==9998) { + if(commanderSocket->state()==QAbstractSocket::ConnectedState) { + commanderSocket->abort(); + } + + if(commanderSocket->state()==QAbstractSocket::UnconnectedState) { + commanderSocket->connectToHost(QHostAddress::LocalHost, 52002); + if(!commanderSocket->waitForConnected(1000)) { + return -1; + } + } + QString t; + t="CmdGetFreq"; + QByteArray ba = t.toLocal8Bit(); + const char* buf=ba.data(); + commanderSocket->write(buf); + commanderSocket->waitForReadyRead(1000); + QByteArray reply=commanderSocket->read(128); + if(reply.indexOf("close(); + return 0; + } else +#endif + { + return rig_close(theRig); + } +} + +int Rig::setConf(const char *name, const char *val) +{ + return rig_set_conf(theRig, tokenLookup(name), val); +} + +int Rig::setFreq(freq_t freq, vfo_t vfo) { +#ifdef WIN32 // Ham Radio Deluxe (only on Windows) + if(m_hrd) { + QString t; + int nhz=(int)freq; + t=m_context + "Set Frequency-Hz " + QString::number(nhz); + const wchar_t* cmnd = (const wchar_t*) t.utf16(); + const wchar_t* result=HRDInterfaceSendMessage(cmnd); + QString t2=QString::fromWCharArray (result,-1); + HRDInterfaceFreeString(result); + if(t2=="OK") { + return 0; + } else { + return -1; + } + } else if(m_cmndr) { + QString t; + double f=0.001*freq; + t.sprintf("CmdSetFreq%10.3f",f); + QLocale locale; + t.replace(".",locale.decimalPoint()); + QByteArray ba = t.toLocal8Bit(); + const char* buf=ba.data(); + commanderSocket->write(buf); + commanderSocket->waitForBytesWritten(1000); + return 0; + } else +#endif + { + return rig_set_freq(theRig, vfo, freq); + } +} + +int Rig::setXit(shortfreq_t xit, vfo_t vfo) +{ + return rig_set_xit(theRig, vfo, xit); +} + +int Rig::setVFO(vfo_t vfo) +{ + return rig_set_vfo(theRig, vfo); +} + +vfo_t Rig::getVFO() +{ + vfo_t vfo; + rig_get_vfo(theRig, &vfo); + return vfo; +} + +int Rig::setSplitFreq(freq_t tx_freq, vfo_t vfo) { +#ifdef WIN32 // Ham Radio Deluxe only on Windows + if(m_hrd) { + QString t; + int nhz=(int)tx_freq; + t=m_context + "Set Frequency-Hz " + QString::number(nhz); + const wchar_t* cmnd = (const wchar_t*) t.utf16(); + const wchar_t* result=HRDInterfaceSendMessage(cmnd); + QString t2=QString::fromWCharArray (result,-1); + HRDInterfaceFreeString(result); + if(t2=="OK") { + return 0; + } else { + return -1; + } + } else if(m_cmndr) { + QString t; + double f=0.001*tx_freq; + t.sprintf("CmdSetTxFreq%10.3f",f); + QLocale locale; + t.replace(".",locale.decimalPoint()); + QByteArray ba = t.toLocal8Bit(); + const char* buf=ba.data(); + commanderSocket->write(buf); + commanderSocket->waitForBytesWritten(1000); + return 0; + } else +#endif + { + return rig_set_split_freq(theRig, vfo, tx_freq); + } +} + +freq_t Rig::getFreq(vfo_t vfo) +{ + freq_t freq; +#ifdef WIN32 // Ham Radio Deluxe (only on Windows) + if(m_hrd) { + const wchar_t* cmnd = (const wchar_t*) (m_context+"Get Frequency").utf16(); + const wchar_t* freqString=HRDInterfaceSendMessage(cmnd); + QString t2=QString::fromWCharArray (freqString,-1); + HRDInterfaceFreeString(freqString); + freq=t2.toDouble(); + return freq; + } else if(m_cmndr) { + QString t; + t="CmdGetFreq"; + QByteArray ba = t.toLocal8Bit(); + const char* buf=ba.data(); + commanderSocket->write(buf); + commanderSocket->waitForReadyRead(1000); + QByteArray reply=commanderSocket->read(128); + QString t2(reply); + if(t2.indexOf(""); + t2=t2.mid(i1+1).replace(",",""); + freq=1000.0*t2.toDouble(); + return freq; + } else { + return -1.0; + } + } else +#endif + { + freq=-1.0; + for(int i=0; i0) t="CmdTX"; + QByteArray ba = t.toLocal8Bit(); + const char* buf=ba.data(); + commanderSocket->write(buf); + commanderSocket->waitForBytesWritten(1000); + return 0; + } else +#endif + { + return rig_set_ptt(theRig, vfo, ptt); + } +} + +ptt_t Rig::getPTT(vfo_t vfo) +{ + ptt_t ptt; + rig_get_ptt(theRig, vfo, &ptt); + return ptt; +} + +token_t Rig::tokenLookup(const char *name) +{ + return rig_token_lookup(theRig, name); +} diff --git a/rigclass.h b/rigclass.h index 77522129e..786f223db 100644 --- a/rigclass.h +++ b/rigclass.h @@ -1,98 +1,98 @@ -/* - * Hamlib C++ bindings - API header - * Copyright (c) 2001-2002 by Stephane Fillod - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#ifndef _RIGCLASS_H -#define _RIGCLASS_H 1 - -#include -#include -#include -#include - -extern QTcpSocket* commanderSocket; - -class BACKEND_IMPEXP Rig { -private: - RIG* theRig; // Global ref. to the rig - bool m_hrd; - bool m_cmndr; - QString m_context; - - -protected: -public: - Rig(); - virtual ~Rig(); - - const struct rig_caps *caps; - - // Initialize rig - int init(rig_model_t rig_model); - - // This method open the communication port to the rig - int open(int n); - - // This method close the communication port to the rig - int close(void); - - int setConf(const char *name, const char *val); - token_t tokenLookup(const char *name); - - int setFreq(freq_t freq, vfo_t vfo = RIG_VFO_CURR); - freq_t getFreq(vfo_t vfo = RIG_VFO_CURR); - int setMode(rmode_t, pbwidth_t width = RIG_PASSBAND_NORMAL, vfo_t vfo = RIG_VFO_CURR); - rmode_t getMode(pbwidth_t&, vfo_t vfo = RIG_VFO_CURR); - int setVFO(vfo_t); - vfo_t getVFO(); - int setXit(shortfreq_t xit, vfo_t vfo); - int setSplitFreq(freq_t tx_freq, vfo_t vfo = RIG_VFO_CURR); - int setPTT (ptt_t ptt, vfo_t vfo = RIG_VFO_CURR); - ptt_t getPTT (vfo_t vfo = RIG_VFO_CURR); - - // callbacks available in your derived object - virtual int FreqEvent(vfo_t, freq_t, rig_ptr_t) const { - return RIG_OK; - } - virtual int ModeEvent(vfo_t, rmode_t, pbwidth_t, rig_ptr_t) const { - return RIG_OK; - } - virtual int VFOEvent(vfo_t, rig_ptr_t) const { - return RIG_OK; - } - virtual int PTTEvent(vfo_t, ptt_t, rig_ptr_t) const { - return RIG_OK; - } - virtual int DCDEvent(vfo_t, dcd_t, rig_ptr_t) const { - return RIG_OK; - } -}; - -#ifdef WIN32 -extern "C" { - bool HRDInterfaceConnect(const wchar_t *host, const ushort); - void HRDInterfaceDisconnect(); - bool HRDInterfaceIsConnected(); - wchar_t* HRDInterfaceSendMessage(const wchar_t *msg); - void HRDInterfaceFreeString(const wchar_t *lstring); -} -#endif - -#endif // _RIGCLASS_H +/* + * Hamlib C++ bindings - API header + * Copyright (c) 2001-2002 by Stephane Fillod + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _RIGCLASS_H +#define _RIGCLASS_H 1 + +#include +#include +#include +#include + +extern QTcpSocket* commanderSocket; + +class BACKEND_IMPEXP Rig { +private: + RIG* theRig; // Global ref. to the rig + bool m_hrd; + bool m_cmndr; + QString m_context; + + +protected: +public: + Rig(); + virtual ~Rig(); + + const struct rig_caps *caps; + + // Initialize rig + int init(rig_model_t rig_model); + + // This method open the communication port to the rig + int open(int n); + + // This method close the communication port to the rig + int close(void); + + int setConf(const char *name, const char *val); + token_t tokenLookup(const char *name); + + int setFreq(freq_t freq, vfo_t vfo = RIG_VFO_CURR); + freq_t getFreq(vfo_t vfo = RIG_VFO_CURR); + int setMode(rmode_t, pbwidth_t width = RIG_PASSBAND_NORMAL, vfo_t vfo = RIG_VFO_CURR); + rmode_t getMode(pbwidth_t&, vfo_t vfo = RIG_VFO_CURR); + int setVFO(vfo_t); + vfo_t getVFO(); + int setXit(shortfreq_t xit, vfo_t vfo); + int setSplitFreq(freq_t tx_freq, vfo_t vfo = RIG_VFO_CURR); + int setPTT (ptt_t ptt, vfo_t vfo = RIG_VFO_CURR); + ptt_t getPTT (vfo_t vfo = RIG_VFO_CURR); + + // callbacks available in your derived object + virtual int FreqEvent(vfo_t, freq_t, rig_ptr_t) const { + return RIG_OK; + } + virtual int ModeEvent(vfo_t, rmode_t, pbwidth_t, rig_ptr_t) const { + return RIG_OK; + } + virtual int VFOEvent(vfo_t, rig_ptr_t) const { + return RIG_OK; + } + virtual int PTTEvent(vfo_t, ptt_t, rig_ptr_t) const { + return RIG_OK; + } + virtual int DCDEvent(vfo_t, dcd_t, rig_ptr_t) const { + return RIG_OK; + } +}; + +#ifdef WIN32 +extern "C" { + bool HRDInterfaceConnect(const wchar_t *host, const ushort); + void HRDInterfaceDisconnect(); + bool HRDInterfaceIsConnected(); + wchar_t* HRDInterfaceSendMessage(const wchar_t *msg); + void HRDInterfaceFreeString(const wchar_t *lstring); +} +#endif + +#endif // _RIGCLASS_H diff --git a/signalmeter.cpp b/signalmeter.cpp index 537b900f8..7fb126d50 100644 --- a/signalmeter.cpp +++ b/signalmeter.cpp @@ -1,53 +1,53 @@ -// Simple bargraph dB meter -// Implemented by Edson Pereira PY2SDR -// -// Limits and geometry are hardcded for now. - -#include "signalmeter.h" - -SignalMeter::SignalMeter(QWidget *parent) : - QWidget(parent) -{ - resize(parent->size()); - - m_meter = new MeterWidget(this); - m_meter->setGeometry(10, 10, 10, 120); - - m_label = new QLabel(this); - m_label->setGeometry(10, 135, 20, 20); - - QLabel *dbLabel = new QLabel(this); - dbLabel->setText("dB"); - dbLabel->setGeometry(30, 135, 20, 20); -} - -SignalMeter::~SignalMeter() -{ - -} - -void SignalMeter::paintEvent( QPaintEvent * ) -{ - QPainter p; - p.begin(this); - p.drawLine(22, 10, 22, 130); - - for ( int i = 0; i <= 60; i += 10 ) { - p.drawLine(22, i*2 + 10, 25, i*2 + 10); - } - - for ( int i = 10; i < 60; i += 10 ) { - p.drawText(30, i*2 + 15, QString::number(60 - i)); - } -} - -void SignalMeter::setValue(int value) -{ - m_meter->setValue(value); - m_label->setText(QString::number(value)); -} - -void SignalMeter::resizeEvent(QResizeEvent *s) -{ - resize(s->size()); -} +// Simple bargraph dB meter +// Implemented by Edson Pereira PY2SDR +// +// Limits and geometry are hardcded for now. + +#include "signalmeter.h" + +SignalMeter::SignalMeter(QWidget *parent) : + QWidget(parent) +{ + resize(parent->size()); + + m_meter = new MeterWidget(this); + m_meter->setGeometry(10, 10, 10, 120); + + m_label = new QLabel(this); + m_label->setGeometry(10, 135, 20, 20); + + QLabel *dbLabel = new QLabel(this); + dbLabel->setText("dB"); + dbLabel->setGeometry(30, 135, 20, 20); +} + +SignalMeter::~SignalMeter() +{ + +} + +void SignalMeter::paintEvent( QPaintEvent * ) +{ + QPainter p; + p.begin(this); + p.drawLine(22, 10, 22, 130); + + for ( int i = 0; i <= 60; i += 10 ) { + p.drawLine(22, i*2 + 10, 25, i*2 + 10); + } + + for ( int i = 10; i < 60; i += 10 ) { + p.drawText(30, i*2 + 15, QString::number(60 - i)); + } +} + +void SignalMeter::setValue(int value) +{ + m_meter->setValue(value); + m_label->setText(QString::number(value)); +} + +void SignalMeter::resizeEvent(QResizeEvent *s) +{ + resize(s->size()); +} diff --git a/signalmeter.h b/signalmeter.h index ea1aa6295..bdf06a4d6 100644 --- a/signalmeter.h +++ b/signalmeter.h @@ -1,32 +1,32 @@ -#ifndef SIGNALMETER_H -#define SIGNALMETER_H - -#include -#include -#include - -class SignalMeter : public QWidget -{ - Q_OBJECT - -public: - explicit SignalMeter(QWidget *parent = 0); - ~SignalMeter(); - -public slots: - void setValue(int value); - -private: - MeterWidget *m_meter; - - QLabel *m_label; - - int m_signal; - int m_sigPeak; - -protected: - void paintEvent( QPaintEvent * ); - void resizeEvent(QResizeEvent *s); -}; - -#endif // SIGNALMETER_H +#ifndef SIGNALMETER_H +#define SIGNALMETER_H + +#include +#include +#include + +class SignalMeter : public QWidget +{ + Q_OBJECT + +public: + explicit SignalMeter(QWidget *parent = 0); + ~SignalMeter(); + +public slots: + void setValue(int value); + +private: + MeterWidget *m_meter; + + QLabel *m_label; + + int m_signal; + int m_sigPeak; + +protected: + void paintEvent( QPaintEvent * ); + void resizeEvent(QResizeEvent *s); +}; + +#endif // SIGNALMETER_H diff --git a/soundin.cpp b/soundin.cpp index 7e943b2bd..17066c97d 100644 --- a/soundin.cpp +++ b/soundin.cpp @@ -1,375 +1,127 @@ -#ifndef QAUDIO_INPUT #include "soundin.h" -#include +#include +#include +#include #include -#define FRAMES_PER_BUFFER 1024 -#define NSMAX 6827 -#define NTMAX 120 - -extern "C" { -#include -extern struct { - float ss[184*NSMAX]; //This is "common/jt9com/..." in fortran - float savg[NSMAX]; -// float c0[2*NTMAX*1500]; - short int d2[NTMAX*12000]; - int nutc; //UTC as integer, HHMM - int ndiskdat; //1 ==> data read from *.wav file - int ntrperiod; //TR period (seconds) - int mousefqso; //User-selected QSO freq (kHz) - int newdat; //1 ==> new data, must do long FFT - int npts8; //npts in c0() array - int nfa; //Low decode limit (Hz) - int nfb; //High decode limit (Hz) - int ntol; //+/- decoding range around fQSO (Hz) - int kin; - int nzhsym; - int nsave; - int nagain; - int ndepth; - int ntxmode; - int nmode; - char datetime[20]; -} jt9com_; -} - -//--------------------------------------------------------------- a2dCallback -int a2dCallback( const void *inputBuffer, void * /* outputBuffer */, - unsigned long framesToProcess, - const PaStreamCallbackTimeInfo * /* timeInfo */, - PaStreamCallbackFlags statusFlags, - void *userData ) - -// This routine called by the PortAudio engine when samples are available. -// It may be called at interrupt level, so don't do anything -// that could mess up the system like calling malloc() or free(). - +bool SoundInput::audioError () const { - SoundInput::CallbackData * udata = reinterpret_cast(userData); - int nbytes,k; + bool result (true); - if( (statusFlags&paInputOverflow) != 0) { - qDebug() << "Input Overflow in a2dCallback"; - } - if(udata->bzero) - { //Start of a new Rx sequence - udata->kin = 0; //Reset buffer pointer - udata->bzero = false; + Q_ASSERT_X (m_stream, "SoundInput", "programming error"); + if (m_stream) + { + switch (m_stream->error ()) + { + case QAudio::OpenError: + Q_EMIT error (tr ("An error opening the audio input device has occurred.")); + break; + + case QAudio::IOError: + Q_EMIT error (tr ("An error occurred during read from the audio input device.")); + break; + + case QAudio::UnderrunError: + Q_EMIT error (tr ("Audio data not being fed to the audio input device fast enough.")); + break; + + case QAudio::FatalError: + Q_EMIT error (tr ("Non-recoverable error, audio input device not usable at this time.")); + break; + + case QAudio::NoError: + result = false; + break; + } } - - nbytes=2*framesToProcess; //Bytes per frame - k=udata->kin; - if(udata->monitoring) { - memcpy(&jt9com_.d2[k],inputBuffer,nbytes); //Copy all samples to d2 - } - udata->kin+=framesToProcess; - jt9com_.kin=udata->kin; // we are the only writer to jt9com_ so no MT issue here - - return paContinue; + return result; } -SoundInput::SoundInput() - : m_inStream(0), - m_TRperiod(60), - m_nsps(6912), - m_monitoring(false), - m_intervalTimer(this) -{ - connect(&m_intervalTimer, SIGNAL(timeout()), this,SLOT(intervalNotify())); -} - -void SoundInput::start(qint32 device) +bool SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, QIODevice * sink) { stop(); -//---------------------------------------------------- Soundcard Setup - PaError paerr; - PaStreamParameters inParam; + QAudioFormat format (device.preferredFormat()); + format.setChannelCount (1); + format.setCodec ("audio/pcm"); + format.setSampleRate (12000); + format.setSampleType (QAudioFormat::SignedInt); + format.setSampleSize (16); - m_callbackData.kin = 0; //Buffer pointer - m_callbackData.bzero = false; //Flag to request reset of kin - m_callbackData.monitoring = m_monitoring; - - inParam.device=device; //### Input Device Number ### - inParam.channelCount=1; //Number of analog channels - inParam.sampleFormat=paInt16; //Get i*2 from Portaudio - inParam.suggestedLatency=0.05; - inParam.hostApiSpecificStreamInfo=NULL; - - paerr=Pa_IsFormatSupported(&inParam,NULL,12000.0); - if(paerr<0) { - emit error("PortAudio says requested soundcard format not supported."); - } - paerr=Pa_OpenStream(&m_inStream, //Input stream - &inParam, //Input parameters - NULL, //No output parameters - 12000.0, //Sample rate - FRAMES_PER_BUFFER, //Frames per buffer -// paClipOff+paDitherOff, //No clipping or dithering - paClipOff, //No clipping - a2dCallback, //Input callback routine - &m_callbackData); //userdata - paerr=Pa_StartStream(m_inStream); - if(paerr<0) { - emit error("Failed to start audio input stream."); - return; - } - m_ntr0 = 99; // initial value higher than any expected - m_intervalTimer.start(100); - m_ms0 = QDateTime::currentMSecsSinceEpoch(); - m_nsps0 = 0; -} - -void SoundInput::intervalNotify() -{ - m_callbackData.monitoring = m_monitoring; // update monitoring - // status - - qint64 ms = QDateTime::currentMSecsSinceEpoch(); - ms=ms % 86400000; - int nsec = ms/1000; // Time according to this computer - int ntr = nsec % m_TRperiod; - - int k=m_callbackData.kin; // get a copy of kin to mitigate the - // potential race condition with the - // callback handler when a buffer - // reset is requested below - - // Reset buffer pointer and symbol number at start of minute - if(ntr < m_ntr0 or !m_monitoring or m_nsps!=m_nsps0) { - m_nstep0=0; - m_nsps0=m_nsps; - m_callbackData.bzero = true; // request callback to reset buffer pointer - } - - if(m_monitoring) { - int kstep=m_nsps/2; - // m_step=k/kstep; - m_step=(k-1)/kstep; - if(m_step != m_nstep0) { - emit readyForFFT(k-1); //Signal to compute new FFTs - m_nstep0=m_step; - } - } - m_ntr0=ntr; -} - -SoundInput::~SoundInput() -{ - if (m_inStream) + if (!format.isValid ()) { - Pa_CloseStream(m_inStream), m_inStream = 0; + Q_EMIT error (tr ("Requested input audio format is not valid.")); + return false; + } + + // this function lies! + // if (!device.isFormatSupported (format)) + // { + // Q_EMIT error (tr ("Requested input audio format is not supported on device.")); + // return false; + // } + + m_stream.reset (new QAudioInput (device, format, this)); + if (audioError ()) + { + return false; + } + + connect (m_stream.data(), &QAudioInput::stateChanged, this, &SoundInput::handleStateChanged); + + m_stream->setBufferSize (m_stream->format ().bytesForFrames (framesPerBuffer)); + + m_stream->start (sink); + + qDebug () << "audio input buffer size = " << m_stream->bufferSize () << " bytes\n"; + + return audioError () ? false : true; +} + +void SoundInput::handleStateChanged (QAudio::State newState) const +{ + switch (newState) + { + case QAudio::IdleState: + qDebug () << "SoundInput idle\n"; + Q_EMIT status (tr ("Idle")); + break; + + case QAudio::ActiveState: + qDebug () << "SoundInput active\n"; + Q_EMIT status (tr ("Receiving")); + break; + + case QAudio::SuspendedState: + qDebug () << "SoundInput suspended\n"; + Q_EMIT status (tr ("Suspended")); + break; + + case QAudio::StoppedState: + if (audioError ()) + { + qDebug () << "SoundInput error\n"; + Q_EMIT status (tr ("Error")); + } + else + { + qDebug () << "SoundInput stopped\n"; + Q_EMIT status (tr ("Stopped")); + } + break; } } void SoundInput::stop() { - m_intervalTimer.stop(); - if (m_inStream) + if (m_stream) { - Pa_StopStream(m_inStream); - Pa_CloseStream(m_inStream), m_inStream = 0; + m_stream->stop (); } + m_stream.reset (); } -#else // QAUDIO_INPUT - -#include "soundin.h" - -#include - -#define FRAMES_PER_BUFFER 1024 -#define NSMAX 6827 -#define NTMAX 120 - -extern "C" { -#include -extern struct { - float ss[184*NSMAX]; //This is "common/jt9com/..." in fortran - float savg[NSMAX]; -// float c0[2*NTMAX*1500]; - short int d2[NTMAX*12000]; - int nutc; //UTC as integer, HHMM - int ndiskdat; //1 ==> data read from *.wav file - int ntrperiod; //TR period (seconds) - int mousefqso; //User-selected QSO freq (kHz) - int newdat; //1 ==> new data, must do long FFT - int npts8; //npts in c0() array - int nfa; //Low decode limit (Hz) - int nfb; //High decode limit (Hz) - int ntol; //+/- decoding range around fQSO (Hz) - int kin; - int nzhsym; - int nsave; - int nagain; - int ndepth; - int ntxmode; - int nmode; - char datetime[20]; -} jt9com_; -} - -QString reportAudioError(QAudio::Error audioError) +SoundInput::~SoundInput () { - switch (audioError) { - case QAudio::NoError: Q_ASSERT(false); - case QAudio::OpenError: return QObject::tr( - "An error opening the audio device has occurred."); - case QAudio::IOError: return QObject::tr( - "An error occurred during read/write of audio device."); - case QAudio::UnderrunError: return QObject::tr( - "Audio data not being fed to the audio device fast enough."); - case QAudio::FatalError: return QObject::tr( - "Non-recoverable error, audio device not usable at this time."); - } - Q_ASSERT(false); - return ""; } - -SoundInput::SoundInput() - : m_dataSinkBusy(false), - m_TRperiod(60), - m_nsps(6912), - m_monitoring(false), - m_intervalTimer(this) -{ -// qDebug() << "A"; - connect(&m_intervalTimer, SIGNAL(timeout()), this,SLOT(intervalNotify())); -} - -void SoundInput::start(qint32 device) -{ - stop(); - -//---------------------------------------------------- Soundcard Setup - m_callbackData.kin=0; //Buffer pointer - m_callbackData.ncall=0; //Number of callbacks - m_callbackData.bzero=false; //Flag to request reset of kin - m_callbackData.monitoring=m_monitoring; - - //### Temporary: hardwired device selection - QAudioDeviceInfo DeviceInfo; - QList m_InDevices; - QAudioDeviceInfo m_InDeviceInfo; - m_InDevices = DeviceInfo.availableDevices(QAudio::AudioInput); - inputDevice = m_InDevices.at(0); - //### -// qDebug() << "B" << m_InDevices.length() << inputDevice.deviceName(); - - const char* pcmCodec = "audio/pcm"; - QAudioFormat audioFormat = inputDevice.preferredFormat(); - audioFormat.setChannelCount(1); - audioFormat.setCodec(pcmCodec); - audioFormat.setSampleRate(12000); - audioFormat.setSampleType(QAudioFormat::SignedInt); - audioFormat.setSampleSize(16); - -// qDebug() << "C" << audioFormat << audioFormat.isValid(); - - if (!audioFormat.isValid()) { - emit error(tr("Requested audio format is not available.")); - return; - } - - audioInput = new QAudioInput(inputDevice, audioFormat); -// qDebug() << "D" << audioInput->error() << QAudio::NoError; - if (audioInput->error() != QAudio::NoError) { - emit error(reportAudioError(audioInput->error())); - return; - } - - stream = audioInput->start(); -// qDebug() << "E" << stream->errorString(); - - m_ntr0 = 99; // initial value higher than any expected - m_nBusy = 0; - m_intervalTimer.start(100); - m_ms0 = QDateTime::currentMSecsSinceEpoch(); - m_nsps0 = 0; -} - -void SoundInput::intervalNotify() -{ - m_callbackData.monitoring=m_monitoring; - qint64 ms = QDateTime::currentMSecsSinceEpoch(); - ms=ms % 86400000; - int nsec = ms/1000; // Time according to this computer - int ntr = nsec % m_TRperiod; - static int k=0; - -// qDebug() << "a" << ms << nsec; - // Reset buffer pointer and symbol number at start of minute - if(ntr < m_ntr0 or !m_monitoring or m_nsps!=m_nsps0) { - m_nstep0=0; - m_nsps0=m_nsps; - m_callbackData.bzero=true; - k=0; - } -// int k=m_callbackData.kin; - -// How many new samples are available? - const qint32 bytesReady = audioInput->bytesReady(); -// qDebug() << "b" << bytesReady; - Q_ASSERT(bytesReady >= 0); - Q_ASSERT(bytesReady % 2 == 0); - if (bytesReady == 0) { - return; - } - - qint32 bytesRead; - bytesRead = stream->read((char*)&jt9com_.d2[k], bytesReady); // Get the new samples - k += bytesRead/2; -// qDebug() << "c" << bytesReady << bytesRead; - Q_ASSERT(bytesRead <= bytesReady); - if (bytesRead < 0) { - emit error(tr("audio stream QIODevice::read returned -1.")); - return; - } - Q_ASSERT(bytesRead % 2 == 0); - - if(m_monitoring) { - int kstep=m_nsps/2; - m_step=(k-1)/kstep; - if(m_step != m_nstep0) { - if(m_dataSinkBusy) { - m_nBusy++; - } else { - emit readyForFFT(k-1); //Signal to compute new FFTs - } - m_nstep0=m_step; - } - } - m_ntr0=ntr; -} - -SoundInput::~SoundInput() -{ -/* - if (m_inStream) - { - Pa_CloseStream(m_inStream), m_inStream = 0; - } -*/ -} -/* -// memcpy(jt9com_.d2[k],buf0,bytesRead); -// k+=bytesRead/2; - for(int i=0; i +#ifndef SOUNDIN_H__ +#define SOUNDIN_H__ #include -#include #include +#include +#include -extern "C" int a2dCallback( const void *, void *, unsigned long, PaStreamCallbackTimeInfo const *, PaStreamCallbackFlags, void *); +class QAudioDeviceInfo; +class QAudioInput; +class QIODevice; -// Gets audio data from soundcard and signals when a buffer of -// specified size is available. +// Gets audio data from sound sample source and passes it to a sink device class SoundInput : public QObject { - Q_OBJECT + Q_OBJECT; -public: - SoundInput(); - ~SoundInput(); + private: + Q_DISABLE_COPY (SoundInput); - int mstep() const {return m_step;} - - /* these can be called while processing samples */ - void setMonitoring(bool b) {m_monitoring = b;} - void setPeriod(int ntrperiod, int nsps) + public: + SoundInput (QObject * parent = 0) + : QObject (parent) { - m_TRperiod=ntrperiod; - m_nsps=nsps; } -signals: - void readyForFFT(int k); - void error(const QString& message); - void status(const QString& message); + ~SoundInput (); -public slots: - void start(qint32 device); +Q_SIGNALS: + void error (QString message) const; + void status (QString message) const; + +public Q_SLOTS: + // sink must exist from the start call to any following stop () call + bool start(QAudioDeviceInfo const&, int framesPerBuffer, QIODevice * sink); void stop(); private: - PaStream * m_inStream; - qint32 m_step; - qint32 m_TRperiod; - qint32 m_TRperiod0; - qint32 m_nsps; - bool m_monitoring; - qint64 m_ms0; - int m_ntr0; - int m_nstep0; - int m_nsps0; + bool audioError () const; - QTimer m_intervalTimer; + QScopedPointer m_stream; - struct CallbackData - { - //Parameters sent to/from the portaudio callback function - int volatile kin; - bool volatile bzero; - bool volatile monitoring; - } m_callbackData; - -private slots: - void intervalNotify(); - - friend int a2dCallback(void const *, void *, unsigned long, PaStreamCallbackTimeInfo const *, PaStreamCallbackFlags, void *); +private Q_SLOTS: + void handleStateChanged (QAudio::State) const; }; -#endif // SOUNDIN_H - -#else // QAUDIO_INPUT -#ifndef SOUNDIN_H -#define SOUNDIN_H - -#include -#include -#include -#include - -// Gets audio data from soundcard and signals when a buffer of -// specified size is available. -class SoundInput : public QObject -{ - Q_OBJECT - -public: - SoundInput(); - ~SoundInput(); - - void setMonitoring(bool b) {m_monitoring = b;} - void setPeriod(int ntrperiod, int nsps) /* this can be called while processing samples */ - { - m_TRperiod=ntrperiod; - m_nsps=nsps; - } - int mstep() const {return m_step;} - double samFacIn() const {return m_SamFacIn;} - -signals: - void readyForFFT(int k); - void error(const QString& message); - void status(const QString& message); - -public slots: - void start(qint32 device); - void stop(); - -private: - bool m_dataSinkBusy; - double m_SamFacIn; //(Input sample rate)/12000.0 - qint32 m_step; - qint32 m_TRperiod; - qint32 m_TRperiod0; - qint32 m_nsps; - bool m_monitoring; - qint64 m_ms0; - int m_ntr0; - int m_nBusy; - int m_nstep0; - int m_nsps0; - - QTimer m_intervalTimer; - QAudioDeviceInfo inputDevice; // audioinput device name - QAudioInput* audioInput; - QIODevice* stream; - - struct CallbackData - { - int kin; - int ncall; - bool bzero; - bool monitoring; - } m_callbackData; //Parameters sent to/from the Notify function - -private slots: - void intervalNotify(); -}; -#endif // SOUNDIN_H -#endif // QAUDIO_INPUT +#endif diff --git a/soundout.cpp b/soundout.cpp index 88c143ba6..abee2abf9 100644 --- a/soundout.cpp +++ b/soundout.cpp @@ -1,217 +1,112 @@ #include "soundout.h" -#include -#include - #include +#include +#include + #include -//#define FRAMES_PER_BUFFER 1024 - -extern float gran(); //Noise generator (for tests only) -extern int itone[126]; //Audio tones for all Tx symbols -extern int icw[250]; //Dits for CW ID -extern int outBufSize; - - -//--------------------------------------------------------------- d2aCallback -int d2aCallback(const void *inputBuffer, void *outputBuffer, - unsigned long framesToProcess, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) +bool SoundOutput::audioError () const { - SoundOutput::CallbackData * udata = reinterpret_cast(userData); - short * wptr = reinterpret_cast(outputBuffer); + bool result (true); - static double twopi=2.0*3.141592653589793238462; - static double baud; - static double phi=0.0; - static double dphi; - static double freq; - static double snr; - static double fac; - static double amp; - static int ic=0,j=0; - static int isym0=-999; - static short int i2; - int isym,nspd; + Q_ASSERT_X (m_stream, "SoundOutput", "programming error"); + if (m_stream) + { + switch (m_stream->error ()) + { + case QAudio::OpenError: + Q_EMIT error (tr ("An error opening the audio output device has occurred.")); + break; - udata->ncall++; - if(udata->bRestart) { - // Time according to this computer - qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000; - int mstr = ms % (1000*udata->ntrperiod ); - if(mstr<1000) - { - std::memset(wptr, 0, framesToProcess * sizeof(*wptr)); // output silence - return paContinue; - } - ic=(mstr-1000)*48; - udata->bRestart=false; - srand(mstr); //Initialize random seed - } - isym=ic/(4.0*udata->dnsps); //Actual fsample=48000 - if(udata->tune) isym=0; //If tuning, send pure tone - if(udata->txsnrdb < 0.0) { - snr=std::pow(10.0,0.05*(udata->txsnrdb-6.0)); - fac=3000.0; - if(snr>1.0) fac=3000.0/snr; - } + case QAudio::IOError: + Q_EMIT error (tr ("An error occurred during write to the audio output device.")); + break; - if(isym>=udata->nsym and icw[0]>0) { //Output the CW ID - freq=udata->ntxfreq - udata->xit; - dphi=twopi*freq/48000.0; + case QAudio::UnderrunError: + Q_EMIT error (tr ("Audio data not being fed to the audio output device fast enough.")); + break; -// float wpm=20.0; -// int nspd=1.2*48000.0/wpm; -// nspd=3072; //18.75 WPM - nspd=2048 + 512; //22.5 WPM - int ic0=udata->nsym*4*udata->dnsps; - for(uint i=0 ; itwopi) phi -= twopi; - i2=32767.0*std::sin(phi); - j=(ic-ic0)/nspd + 1; - if(icw[j]==0) i2=0; - if(udata->txsnrdb < 0.0) { - int i4=fac*(gran() + i2*snr/32768.0); - if(i4>32767) i4=32767; - if(i4<-32767) i4=-32767; - i2=i4; - } - if(udata->mute) i2=0; - *wptr++ = i2; //left -#ifdef UNIX - *wptr++ = i2; //right -#endif - ic++; + case QAudio::FatalError: + Q_EMIT error (tr ("Non-recoverable error, audio output device not usable at this time.")); + break; + + case QAudio::NoError: + result = false; + break; + } } - if(j>icw[0]) return paComplete; - if(statusFlags==999999 and timeInfo==NULL and - inputBuffer==NULL) return paContinue; //Silence compiler warning: - return paContinue; - } - - baud=12000.0/udata->dnsps; - amp=32767.0; - int i0=(udata->nsym-0.017)*4.0*udata->dnsps; - int i1=udata->nsym*4.0*udata->dnsps; - bool tune = udata->tune; - if(tune) { //If tuning, no ramp down - i0=999*udata->dnsps; - i1=i0; - } - for(uint i=0 ; idnsps); //Actual fsample=48000 - if(tune) isym=0; //If tuning, send pure tone - if(isym!=isym0) { - freq=udata->ntxfreq + itone[isym]*baud - udata->xit; - dphi=twopi*freq/48000.0; - isym0=isym; - } - phi += dphi; - if(phi>twopi) phi -= twopi; - if(ic>i0) amp=0.98*amp; - if(ic>i1) amp=0.0; - i2=amp*std::sin(phi); - if(udata->txsnrdb < 0.0) { - int i4=fac*(gran() + i2*snr/32768.0); - if(i4>32767) i4=32767; - if(i4<-32767) i4=-32767; - i2=i4; - } - if(udata->mute) i2=0; - *wptr++ = i2; //left -#ifdef UNIX - *wptr++ = i2; //right -#endif - ic++; - } - if(amp==0.0) { - if(icw[0]==0) return paComplete; - phi=0.0; - } - return paContinue; + return result; } -SoundOutput::SoundOutput() - : m_stream(0) - , m_outputLatency(0.) - , m_active(false) +bool SoundOutput::start(QAudioDeviceInfo const& device, QIODevice * source) { -} + Q_ASSERT (source); -void SoundOutput::start(qint32 deviceNumber,QString const& mode,int TRPeriod - ,int nsps,int txFreq,int xit,double txsnrdb) -{ stop(); - PaStreamParameters outParam; + QAudioFormat format (device.preferredFormat()); + format.setChannelCount (1); + format.setCodec ("audio/pcm"); + format.setSampleRate (48000); + format.setSampleType (QAudioFormat::SignedInt); + format.setSampleSize (16); + if (!format.isValid ()) + { + Q_EMIT error (tr ("Requested output audio format is not valid.")); + return false; + } + if (!device.isFormatSupported (format)) + { + Q_EMIT error (tr ("Requested output audio format is not supported on device.")); + return false; + } - outParam.device=deviceNumber; //Output device number - outParam.channelCount=1; //Number of analog channels -#ifdef UNIX - outParam.channelCount=2; //Number of analog channels -#endif - outParam.sampleFormat=paInt16; //Send short ints to PortAudio - outParam.suggestedLatency=0.05; - outParam.hostApiSpecificStreamInfo=NULL; + m_stream.reset (new QAudioOutput (device, format, this)); + if (audioError ()) + { + return false; + } + connect (m_stream.data(), &QAudioOutput::stateChanged, this, &SoundOutput::handleStateChanged); - PaError paerr = Pa_IsFormatSupported(NULL,&outParam,48000.0); - if(paerr<0) { - qDebug() << "PortAudio says requested output format not supported."; - qDebug() << paerr << deviceNumber; - return; - } + m_stream->start (source); + if (audioError ()) // start the input stream + { + return false; + } - m_callbackData.txsnrdb=txsnrdb; - m_callbackData.dnsps=nsps; - m_callbackData.nsym=85; - if(mode=="JT65") { - m_callbackData.dnsps=4096.0*12000.0/11025.0; - m_callbackData.nsym=126; - } - m_callbackData.ntrperiod=TRPeriod; - m_callbackData.ntxfreq=txFreq; - m_callbackData.xit=xit; - m_callbackData.ncall=0; - m_callbackData.bRestart=true; - - paerr=Pa_OpenStream(&m_stream, //Output stream - NULL, //No input parameters - &outParam, //Output parameters - 48000.0, //Sample rate - outBufSize, //Frames per buffer - paClipOff, //No clipping - d2aCallback, //output callbeck routine - &m_callbackData); //userdata - - paerr=Pa_StartStream(m_stream); - if(paerr<0) { - qDebug() << "Failed to start audio output stream."; - return; - } - const PaStreamInfo* p=Pa_GetStreamInfo(m_stream); - m_outputLatency = p->outputLatency; - m_ms0 = QDateTime::currentMSecsSinceEpoch(); m_active = true; + return true; +} + +void SoundOutput::handleStateChanged (QAudio::State newState) const +{ + switch (newState) + { + case QAudio::IdleState: Q_EMIT status (tr ("Idle")); break; + case QAudio::ActiveState: Q_EMIT status (tr ("Sending")); break; + case QAudio::SuspendedState: Q_EMIT status (tr ("Suspended")); break; + + case QAudio::StoppedState: + if (audioError ()) + { + Q_EMIT status (tr ("Error")); + } + else + { + Q_EMIT status (tr ("Stopped")); + } + break; + } } void SoundOutput::stop() { - if (m_stream) - { - Pa_StopStream(m_stream); - Pa_CloseStream(m_stream), m_stream = 0; - } + m_stream.reset (); m_active = false; } SoundOutput::~SoundOutput() { - if (m_stream) - { - Pa_CloseStream(m_stream), m_stream = 0; - } + stop (); } diff --git a/soundout.h b/soundout.h index 4b428a77b..0a4318117 100644 --- a/soundout.h +++ b/soundout.h @@ -1,76 +1,52 @@ -#ifndef SOUNDOUT_H -#define SOUNDOUT_H - -#include +#ifndef SOUNDOUT_H__ +#define SOUNDOUT_H__ #include #include +#include -extern "C" int d2aCallback(const void *, void *, - unsigned long, - PaStreamCallbackTimeInfo const *, - PaStreamCallbackFlags, - void *); +#include "Modulator.hpp" + +class QAudioDeviceInfo; // An instance of this sends audio data to a specified soundcard. -// Output can be muted while underway, preserving waveform timing when -// transmission is resumed. class SoundOutput : public QObject { Q_OBJECT; Q_PROPERTY(bool running READ isRunning); - Q_PROPERTY(bool mute READ isMuted WRITE mute); - Q_PROPERTY(bool tune READ isTuning WRITE tune); -public: - SoundOutput(); - ~SoundOutput(); + private: + Q_DISABLE_COPY (SoundOutput); + + public: + SoundOutput () + : m_active(false) + { + } + ~SoundOutput (); bool isRunning() const {return m_active;} - bool isMuted() const {return m_callbackData.mute;} - bool isTuning() const {return m_callbackData.tune;} - double outputLatency() const {return m_outputLatency;} - // the following can be called while the stream is running - void setTxFreq(int n) {m_callbackData.ntxfreq = n;} - void setXIT(int n) {m_callbackData.xit = n;} - void mute(bool b = true) {m_callbackData.mute = b;} - void tune(bool b = true) {m_callbackData.tune = b;} - -public slots: - void start(qint32 deviceNumber, QString const& mode,int TRPeriod,int nsps,int txFreq,int xit,double txsnrdb = 99.); +public Q_SLOTS: + bool start(QAudioDeviceInfo const& device, QIODevice * source); void stop(); -// Private members +Q_SIGNALS: + void error (QString message) const; + void status (QString message) const; + private: - PaStream * m_stream; - PaTime m_outputLatency; + bool audioError () const; - struct CallbackData - { - //Parameters sent to or received from callback function - double volatile txsnrdb; - double volatile dnsps; //Samples per symbol (at 12000 Hz) - int volatile ntrperiod; //T/R period (s) - int volatile ntxfreq; - int volatile xit; - int volatile ncall; - int volatile nsym; - bool volatile mute; - bool volatile bRestart; - bool volatile tune; - } m_callbackData; +private Q_SLOTS: + void handleStateChanged (QAudio::State) const; + + private: + QScopedPointer m_stream; - qint64 m_ms0; bool m_active; - - friend int d2aCallback(const void *, void *, - unsigned long, - PaStreamCallbackTimeInfo const *, - PaStreamCallbackFlags, - void *); }; #endif diff --git a/wsjtx.pro b/wsjtx.pro index 45bf7656d..f695d3333 100644 --- a/wsjtx.pro +++ b/wsjtx.pro @@ -4,7 +4,7 @@ # #------------------------------------------------- -QT += core gui network multimedia +QT += network multimedia greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += thread #CONFIG += console @@ -16,7 +16,6 @@ VERSION = 1.1 TEMPLATE = app #DEFINES = QT4 DEFINES = QT5 -#DEFINES += QAUDIO_INPUT win32 { DEFINES += WIN32 @@ -36,16 +35,31 @@ gfortran.input = F90_SOURCES QMAKE_EXTRA_COMPILERS += gfortran } -SOURCES += main.cpp mainwindow.cpp plotter.cpp about.cpp \ - soundin.cpp soundout.cpp devsetup.cpp widegraph.cpp \ - getfile.cpp displaytext.cpp getdev.cpp logqso.cpp \ - psk_reporter.cpp rigclass.cpp \ - signalmeter.cpp \ - meterwidget.cpp \ - logbook/logbook.cpp \ - logbook/countrydat.cpp \ - logbook/countriesworked.cpp \ - logbook/adif.cpp +# +# Order matters here as the link is in this order so referrers need to be after referred +# +SOURCES += \ + logbook/adif.cpp \ + logbook/countrydat.cpp \ + logbook/countriesworked.cpp \ + logbook/logbook.cpp \ + rigclass.cpp \ + psk_reporter.cpp \ + Modulator.cpp \ + Detector.cpp \ + logqso.cpp \ + displaytext.cpp \ + getfile.cpp \ + soundout.cpp \ + soundin.cpp \ + meterwidget.cpp \ + signalmeter.cpp \ + plotter.cpp \ + widegraph.cpp \ + devsetup.cpp \ + about.cpp \ + mainwindow.cpp \ + main.cpp win32 { SOURCES += killbyname.cpp @@ -54,7 +68,7 @@ SOURCES += killbyname.cpp HEADERS += mainwindow.h plotter.h soundin.h soundout.h \ about.h devsetup.h widegraph.h getfile.h \ commons.h sleep.h displaytext.h logqso.h \ - psk_reporter.h rigclass.h \ + Detector.hpp Modulator.hpp psk_reporter.h rigclass.h \ signalmeter.h \ meterwidget.h \ logbook/logbook.h \ @@ -62,9 +76,6 @@ HEADERS += mainwindow.h plotter.h soundin.h soundout.h \ logbook/countriesworked.h \ logbook/adif.h -# (Is the following really needed???) -#DEFINES += __cplusplus - FORMS += mainwindow.ui about.ui devsetup.ui widegraph.ui \ logqso.ui @@ -73,7 +84,7 @@ RC_FILE = wsjtx.rc unix { LIBS += ../wsjtx/lib/libjt9.a LIBS += -lhamlib -LIBS += -lportaudio -lgfortran -lfftw3f +LIBS += -lgfortran -lfftw3f } win32 { @@ -84,8 +95,6 @@ LIBS += ../wsjtx/lib/libjt9.a LIBS += ../wsjtx/libfftw3f_win.a LIBS += ../wsjtx/libpskreporter.a LIBS += ../wsjtx/libHRDInterface001.a -#LIBS += ../QtSupport/palir-02.dll -LIBS += /users/joe/wsjt/QtSupport/palir-02.dll LIBS += libwsock32 LIBS += C:/MinGW/lib/libf95.a