From 2171b79a94ea11447665894871d2eca8d0825809 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 24 Sep 2012 19:11:31 +0000 Subject: [PATCH] Morphing toward a usable WSJT-X with JT8 modes. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@2594 ab8295b8-cf94-4d9e-aec4-7959e3be5d79 --- jt8.txt | 31 ++++++++++++ libm65/genjt8.f90 | 96 +++++++++++++++++++++++++++++++++++++ mainwindow.cpp | 118 ++++++++++++++++++++++++++++++++++++---------- mainwindow.h | 13 ++++- mainwindow.ui | 45 ++++++++++++++++++ soundin.cpp | 4 ++ soundin.h | 3 ++ soundout.cpp | 5 ++ soundout.h | 3 ++ widegraph.cpp | 5 ++ widegraph.h | 2 + 11 files changed, 298 insertions(+), 27 deletions(-) create mode 100644 jt8.txt create mode 100644 libm65/genjt8.f90 diff --git a/jt8.txt b/jt8.txt new file mode 100644 index 000000000..ed7067af3 --- /dev/null +++ b/jt8.txt @@ -0,0 +1,31 @@ +JT8 is a mode designed for amateur QSOs and beacon-like transmissions +at MF and LF. The mode uses the same 72-bit user messages as JT65, +augmented by a 12-bit cyclic redundancy check (CRC). Error-control +coding uses a convolutional code with constraint length K=16, rate +r=1/2, and a zero tail, leading to an encoded message length of +(72+12+15)*2 = 198 bits. Modulation is 8-FSK, so a transmission +requires 198/3 = 66 information-carrying channel symbols. Two 8x8 +Costas arrays are added to each transmission for purposes of time and +frequency synchronization. A full transmission thus contains 66 + 16 += 82 symbols. Tone spacing df of the 8-FSK modulation is equal to the +keying rate; symbol duration tsym = 1/df, and the total occupied +bandwidth is 8*df. The actual transmission length TxT is slightly +less than the T/R sequence time, to allow for possible message +decoding before the next transmission starts. + +Parameters of the five JT8 sub-modes are summarized in the following +table, along with S/N thresholds measured by simulation on an AWGN +(additive white Gaussian noise) channel. Parameter nsps is the number +of samples per symbol at sample rate 1500 Hz. + +----------------------------------------------------- +Mode T/R TxT nsps df tsym BW S/N* + (m) (s) (Hz) (s) (Hz) (dB) +----------------------------------------------------- +JT8-1 1 52.5 960 1.562 0.64 12.6 -26.9 +JT8-2 2 112.0 2048 0.732 1.37 5.9 -30.2 +JT8-5 5 293.9 5376 0.279 3.58 2.2 -34.4 +JT8-10 10 587.8 10752 0.140 7.17 1.1 -37.5 +JT8-30 30 1791.3 32768 0.046 21.85 0.4 -42.3 +----------------------------------------------------- +* Noise power measured in a 2500 Hz bandwidth. diff --git a/libm65/genjt8.f90 b/libm65/genjt8.f90 new file mode 100644 index 000000000..b98bc379a --- /dev/null +++ b/libm65/genjt8.f90 @@ -0,0 +1,96 @@ +subroutine genjt8(message,iwave,nwave,nbit,msgsent) + +! Generate a JT8 wavefile. + + parameter (NMAX=60*12000) !Max length of wave file + character*24 message !Message to be generated + character*24 msgsent !Message as it will be received + character cmode*5 + real*8 t,dt,phi,f,f0,dfgen,dphi,twopi,tsymbol + integer*2 iwave(NMAX) !Generated wave file + integer iu(3) + integer gsym(372) !372 is needed for JT8 mode + integer sent(144) + integer ic8(8) + data ic8/3,6,2,4,5,0,7,1/ + data nsps/4096/ + data twopi/6.283185307d0/ + save + + cmode='JT8' !### temp ? ### + call srcenc(cmode,message,nbit,iu) +! In JT8 mode, message length is always nbit=78 + if(nbit.ne.78) then + print*,'genjt8, nbit=',nbit + stop + endif + +! Apply FEC and do the channel encoding + call chenc(cmode,nbit,iu,gsym) + +! Remove source encoding, recover the human-readable message. + call srcdec(cmode,nbit,iu,msgsent) + +! Insert 8x8 Costas array at beginning and end of array sent(). + sent(1:8)=ic8 + sent(135:142)=ic8 +! Insert two symbols after each Costas array to specify message length. + if(nbit.eq.30) then + sent(9)=2 + sent(10)=2 + sent(143)=2 + sent(144)=2 + else if(nbit.eq.48) then + sent(9)=3 + sent(10)=3 + sent(143)=3 + sent(144)=3 + else + sent(9)=6 + sent(10)=6 + sent(143)=6 + sent(144)=6 + endif + +! Insert the 3-bit data symbols + sent(11:134)=gsym(1:124) + +! Use the four free symbols in 30-bit mode + if(nbit.eq.30) then + sent(121)=sent(20) + sent(122)=sent(45) + sent(123)=sent(70) + sent(124)=sent(95) + endif + +! Set up necessary constants + nsym=144 + tsymbol=nsps/12000.d0 + dt=1.d0/12000.d0 + f0=1270.46d0 + dfgen=12000.d0/nsps + t=0.d0 + phi=0.d0 + k=0 + j0=0 + ndata=(nsym*12000.d0*tsymbol)/2 + ndata=2*ndata + do i=1,ndata + t=t+dt + j=int(t/tsymbol) + 1 !Symbol number, 1-nsym + if(j.ne.j0) then + f=f0 + k=k+1 + if(k.le.144) f=f0+(sent(k))*dfgen !### Fix need for this ### + dphi=twopi*dt*f + j0=j + endif + phi=phi+dphi + iwave(i)=32767.0*sin(phi) + enddo + + iwave(ndata+1:)=0 + nwave=ndata+6000 !0.5 s buffer before CW ID + + return +end subroutine genjt8 diff --git a/mainwindow.cpp b/mainwindow.cpp index 238cbad98..86be79937 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -42,9 +42,11 @@ MainWindow::MainWindow(QWidget *parent) : ui->actionBlue->setActionGroup(paletteGroup); QActionGroup* modeGroup = new QActionGroup(this); - ui->actionJT65A->setActionGroup(modeGroup); - ui->actionJT65B->setActionGroup(modeGroup); - ui->actionJT65C->setActionGroup(modeGroup); + ui->actionJT8_1->setActionGroup(modeGroup); + ui->actionJT8_2->setActionGroup(modeGroup); + ui->actionJT8_5->setActionGroup(modeGroup); + ui->actionJT8_10->setActionGroup(modeGroup); + ui->actionJT8_30->setActionGroup(modeGroup); QActionGroup* saveGroup = new QActionGroup(this); ui->actionSave_all->setActionGroup(saveGroup); @@ -94,7 +96,7 @@ MainWindow::MainWindow(QWidget *parent) : m_myCall="K1JT"; m_myGrid="FN20qi"; m_appDir = QApplication::applicationDirPath(); - m_saveDir="/users/joe/wsjt-x/install/save"; + m_saveDir="/users/joe/wsjtx/install/save"; m_txFreq=125; m_setftx=0; m_loopall=false; @@ -106,7 +108,8 @@ MainWindow::MainWindow(QWidget *parent) : m_RxLog=1; //Write Date and Time to RxLog m_nutc0=9999; m_NB=false; - m_mode="JTMSK"; + m_mode="JT8-1"; + m_TRperiod=60; m_colors="000066ff0000ffff00969696646464"; ui->xThermo->setFillBrush(Qt::green); @@ -126,8 +129,13 @@ MainWindow::MainWindow(QWidget *parent) : border-style: outset; border-width: 1px; border-radius: 5px; \ border-color: black; min-width: 5em; padding: 3px;}"; - genStdMsgs("26"); + genStdMsgs("-30"); on_actionWide_Waterfall_triggered(); //### + if(m_mode=="JT8-1") on_actionJT8_1_triggered(); + if(m_mode=="JT8-2") on_actionJT8_2_triggered(); + if(m_mode=="JT8-5") on_actionJT8_5_triggered(); + if(m_mode=="JT8-10") on_actionJT8_10_triggered(); + if(m_mode=="JT8-30") on_actionJT8_30_triggered(); future1 = new QFuture; watcher1 = new QFutureWatcher; @@ -194,7 +202,7 @@ MainWindow::~MainWindow() //-------------------------------------------------------- writeSettings() void MainWindow::writeSettings() { - QString inifile = m_appDir + "/wsjt-x.ini"; + QString inifile = m_appDir + "/wsjtx.ini"; QSettings settings(inifile, QSettings::IniFormat); settings.beginGroup("MainWindow"); @@ -261,7 +269,7 @@ void MainWindow::writeSettings() //---------------------------------------------------------- readSettings() void MainWindow::readSettings() { - QString inifile = m_appDir + "/wsjt-x.ini"; + QString inifile = m_appDir + "/wsjtx.ini"; QSettings settings(inifile, QSettings::IniFormat); settings.beginGroup("MainWindow"); restoreGeometry(settings.value("geometry").toByteArray()); @@ -307,7 +315,7 @@ void MainWindow::readSettings() "PaletteAFMHot",false).toBool()); ui->actionBlue->setChecked(settings.value( "PaletteBlue",false).toBool()); - m_mode=settings.value("Mode","JTMSK").toString(); + m_mode=settings.value("Mode","JT8-1").toString(); ui->actionNone->setChecked(settings.value("SaveNone",true).toBool()); ui->actionSave_all->setChecked(settings.value("SaveAll",false).toBool()); m_saveAll=ui->actionSave_all->isChecked(); @@ -556,13 +564,13 @@ void MainWindow::keyPressEvent( QKeyEvent *e ) //keyPressEvent break; case Qt::Key_G: if(e->modifiers() & Qt::AltModifier) { - genStdMsgs("26"); + genStdMsgs("-30"); break; } case Qt::Key_L: if(e->modifiers() & Qt::ControlModifier) { lookup(); - genStdMsgs("26"); + genStdMsgs("-30"); break; } } @@ -616,13 +624,13 @@ void MainWindow::createStatusBar() //createStatusBar lab3->setFrameStyle(QFrame::Panel | QFrame::Sunken); statusBar()->addWidget(lab3); - /* lab4 = new QLabel(""); lab4->setAlignment(Qt::AlignHCenter); lab4->setMinimumSize(QSize(80,10)); lab4->setFrameStyle(QFrame::Panel | QFrame::Sunken); statusBar()->addWidget(lab4); +/* lab5 = new QLabel(""); lab5->setAlignment(Qt::AlignHCenter); lab5->setMinimumSize(QSize(50,10)); @@ -898,19 +906,18 @@ void MainWindow::guiUpdate() static char msgsent[29]; static int nsendingsh=0; int khsym=0; - double trperiod=30.0; double tx1=0.0; - double tx2=trperiod; + double tx2=m_TRperiod; if(!m_txFirst) { - tx1 += trperiod; - tx2 += trperiod; + tx1 += m_TRperiod; + tx2 += m_TRperiod; } qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000; int nsec=ms/1000; double tsec=0.001*ms; - double t2p=fmod(tsec,2*trperiod); + double t2p=fmod(tsec,2*m_TRperiod); bool bTxTime = t2p >= tx1 && t2p < tx2; if(m_auto) { @@ -948,7 +955,7 @@ void MainWindow::guiUpdate() int len1=28; genmsk_(message,iwave,&nwave,len1); if(m_restart) { - QFile f("wsjt-x_tx.log"); + QFile f("wsjtx_tx.log"); f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append); QTextStream out(&f); out << QDateTime::currentDateTimeUtc().toString("yyyy-MMM-dd hh:mm") @@ -969,7 +976,7 @@ void MainWindow::guiUpdate() btxok=true; m_transmitting=true; - QFile f("wsjt-x_tx.log"); + QFile f("wsjtx_tx.log"); f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append); QTextStream out(&f); out << QDateTime::currentDateTimeUtc().toString("yyyy-MMM-dd hh:mm") @@ -1010,7 +1017,6 @@ void MainWindow::guiUpdate() } if(nsec != m_sec0) { //Once per second - if(m_transmitting) { if(nsendingsh==1) { lab1->setStyleSheet("QLabel{background-color: #66ffff}"); @@ -1158,7 +1164,7 @@ void MainWindow::doubleClickOnCall(QString hiscall, bool ctrl) QString rpt=""; if(ctrl) rpt=t2.mid(23,3); lookup(); - rpt="26"; + rpt="-30"; genStdMsgs(rpt); if(t2.indexOf(m_myCall)>0) { m_ntx=2; @@ -1408,7 +1414,7 @@ void MainWindow::on_dxGridEntry_textChanged(const QString &t) //dxGrid changed void MainWindow::on_genStdMsgsPushButton_clicked() //genStdMsgs button { - genStdMsgs("26"); + genStdMsgs("-30"); } void MainWindow::on_logQSOButton_clicked() //Log QSO button @@ -1433,10 +1439,10 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button void MainWindow::on_actionErase_wsjtx_rx_log_triggered() //Erase Rx log { int ret = QMessageBox::warning(this, "Confirm Erase", - "Are you sure you want to erase file wsjt-x_rx.log ?", + "Are you sure you want to erase file wsjtx_rx.log ?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if(ret==QMessageBox::Yes) { - m_RxLog |= 2; // Rewind wsjt-x_rx.log + m_RxLog |= 2; // Rewind wsjtx_rx.log } } */ @@ -1444,10 +1450,70 @@ void MainWindow::on_actionErase_wsjtx_rx_log_triggered() //Erase Rx log void MainWindow::on_actionErase_wsjtx_tx_log_triggered() //Erase Tx log { int ret = QMessageBox::warning(this, "Confirm Erase", - "Are you sure you want to erase file wsjt-x_tx.log ?", + "Are you sure you want to erase file wsjtx_tx.log ?", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if(ret==QMessageBox::Yes) { - QFile f("wsjt-x_tx.log"); + QFile f("wsjtx_tx.log"); f.remove(); } } + +void MainWindow::on_actionJT8_1_triggered() +{ + m_mode="JT8-1"; + m_TRperiod=60; + soundInThread.setPeriod(m_TRperiod); + soundOutThread.setPeriod(m_TRperiod); + g_pWideGraph->setPeriod(m_TRperiod); + lab4->setStyleSheet("QLabel{background-color: #ff6ec7}"); + lab4->setText(m_mode); + ui->actionJT8_1->setChecked(true); +} + +void MainWindow::on_actionJT8_2_triggered() +{ + m_mode="JT8-2"; + m_TRperiod=120; + soundInThread.setPeriod(m_TRperiod); + soundOutThread.setPeriod(m_TRperiod); + g_pWideGraph->setPeriod(m_TRperiod); + lab4->setStyleSheet("QLabel{background-color: #ffff00}"); + lab4->setText(m_mode); + ui->actionJT8_2->setChecked(true); +} + +void MainWindow::on_actionJT8_5_triggered() +{ + m_mode="JT8-5"; + m_TRperiod=300; + soundInThread.setPeriod(m_TRperiod); + soundOutThread.setPeriod(m_TRperiod); + g_pWideGraph->setPeriod(m_TRperiod); + lab4->setStyleSheet("QLabel{background-color: #ffa500}"); + lab4->setText(m_mode); + ui->actionJT8_5->setChecked(true); +} + +void MainWindow::on_actionJT8_10_triggered() +{ + m_mode="JT8-10"; + m_TRperiod=600; + soundInThread.setPeriod(m_TRperiod); + soundOutThread.setPeriod(m_TRperiod); + g_pWideGraph->setPeriod(m_TRperiod); + lab4->setStyleSheet("QLabel{background-color: #7fff00}"); + lab4->setText(m_mode); + ui->actionJT8_10->setChecked(true); +} + +void MainWindow::on_actionJT8_30_triggered() +{ + m_mode="JT8-30"; + m_TRperiod=1800; + soundInThread.setPeriod(m_TRperiod); + soundOutThread.setPeriod(m_TRperiod); + g_pWideGraph->setPeriod(m_TRperiod); + lab4->setStyleSheet("QLabel{background-color: #97ffff}"); + lab4->setText(m_mode); + ui->actionJT8_30->setChecked(true); +} diff --git a/mainwindow.h b/mainwindow.h index 781da3fa2..e8c9545a2 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -98,11 +98,21 @@ private slots: void on_genStdMsgsPushButton_clicked(); void bumpDF(int n); void on_logQSOButton_clicked(); -// void on_actionErase_wsjt-x_rx_log_triggered(); +// void on_actionErase_wsjtx_rx_log_triggered(); void on_actionErase_wsjtx_tx_log_triggered(); void on_actionAFMHot_triggered(); void on_actionBlue_triggered(); + void on_actionJT8_2_triggered(); + + void on_actionJT8_1_triggered(); + + void on_actionJT8_5_triggered(); + + void on_actionJT8_30_triggered(); + + void on_actionJT8_10_triggered(); + private: Ui::MainWindow *ui; qint32 m_nDevIn; @@ -133,6 +143,7 @@ private: qint32 m_adjustIQ; qint32 m_applyIQcal; qint32 m_mult570; + qint32 m_TRperiod; double m_fAdd; double m_IQamp; diff --git a/mainwindow.ui b/mainwindow.ui index 29acac49c..a19270e07 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -1126,6 +1126,11 @@ p, li { white-space: pre-wrap; } Mode + + + + + @@ -1469,6 +1474,46 @@ p, li { white-space: pre-wrap; } FUNcube Dongle Settings + + + true + + + JT8-1 + + + + + true + + + JT8-2 + + + + + true + + + JT8-5 + + + + + true + + + JT8-10 + + + + + true + + + JT8-30 + + diff --git a/soundin.cpp b/soundin.cpp index e9e5057a7..c0cce4240 100644 --- a/soundin.cpp +++ b/soundin.cpp @@ -156,6 +156,10 @@ void SoundInThread::setMonitoring(bool b) //setMonitoring() m_monitoring = b; } +void SoundInThread::setPeriod(int n) +{ + m_TRperiod=n; +} int SoundInThread::mstep() { diff --git a/soundin.h b/soundin.h index dbd38f02b..a2e06e4b3 100644 --- a/soundin.h +++ b/soundin.h @@ -26,6 +26,7 @@ public: void setInputDevice(qint32 n); void setMonitoring(bool b); + void setPeriod(int n); int mstep(); signals: @@ -41,6 +42,8 @@ private: bool m_monitoring; qint32 m_step; qint32 m_nDevIn; + qint32 m_TRperiod; + qint32 m_TRperiod0; }; diff --git a/soundout.cpp b/soundout.cpp index 473fd8f64..e3809296a 100644 --- a/soundout.cpp +++ b/soundout.cpp @@ -103,3 +103,8 @@ void SoundOutThread::setOutputDevice(int n) //setOutputDevice() if (isRunning()) return; this->m_nDevOut=n; } + +void SoundOutThread::setPeriod(int n) +{ + m_TRperiod=n; +} diff --git a/soundout.h b/soundout.h index e0a420e57..b74f98938 100644 --- a/soundout.h +++ b/soundout.h @@ -25,6 +25,7 @@ public: public: void setOutputDevice(qint32 n); + void setPeriod(int n); bool quitExecution; //If true, thread exits gracefully @@ -33,6 +34,8 @@ private: qint32 m_nDevOut; //Output device number bool m_txOK; //Enable Tx audio bool m_txMute; //Mute temporarily + qint32 m_TRperiod; //T/R period (30 or 60 s) + }; #endif diff --git a/widegraph.cpp b/widegraph.cpp index 1ce5a9171..f6f080fd6 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -208,6 +208,11 @@ void WideGraph::setPalette(QString palette) ui->widePlot->setPalette(palette); } +void WideGraph::setPeriod(int n) +{ + m_TRperiod=n; +} + void WideGraph::on_cbSpec2d_toggled(bool b) { ui->widePlot->set2Dspec(b); diff --git a/widegraph.h b/widegraph.h index 887145acf..62f71c0b8 100644 --- a/widegraph.h +++ b/widegraph.h @@ -32,6 +32,7 @@ public: void setFcal(int n); void setPalette(QString palette); void setMode65(int n); + void setPeriod(int n); qint32 m_qsoFreq; @@ -58,6 +59,7 @@ private: qint32 m_fCal; qint32 m_fSample; qint32 m_mode65; + qint32 m_TRperiod; Ui::WideGraph *ui; };