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
This commit is contained in:
Joe Taylor 2012-09-24 19:11:31 +00:00
parent 0ae89c922f
commit 000502af77
11 changed files with 298 additions and 27 deletions

31
jt8.txt Normal file
View File

@ -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.

96
libm65/genjt8.f90 Normal file
View File

@ -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

View File

@ -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<void>;
watcher1 = new QFutureWatcher<void>;
@ -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);
}

View File

@ -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;

View File

@ -1126,6 +1126,11 @@ p, li { white-space: pre-wrap; }
<property name="title">
<string>Mode</string>
</property>
<addaction name="actionJT8_1"/>
<addaction name="actionJT8_2"/>
<addaction name="actionJT8_5"/>
<addaction name="actionJT8_10"/>
<addaction name="actionJT8_30"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuSetup"/>
@ -1469,6 +1474,46 @@ p, li { white-space: pre-wrap; }
<string>FUNcube Dongle Settings</string>
</property>
</action>
<action name="actionJT8_1">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>JT8-1</string>
</property>
</action>
<action name="actionJT8_2">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>JT8-2</string>
</property>
</action>
<action name="actionJT8_5">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>JT8-5</string>
</property>
</action>
<action name="actionJT8_10">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>JT8-10</string>
</property>
</action>
<action name="actionJT8_30">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>JT8-30</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -156,6 +156,10 @@ void SoundInThread::setMonitoring(bool b) //setMonitoring()
m_monitoring = b;
}
void SoundInThread::setPeriod(int n)
{
m_TRperiod=n;
}
int SoundInThread::mstep()
{

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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;
};