diff --git a/CMakeLists.txt b/CMakeLists.txt
index e4e02b066..9f2d3fc01 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -232,6 +232,7 @@ set (wsjtx_CXXSRCS
mainwindow.cpp
Configuration.cpp
main.cpp
+ wsprnet.cpp
)
if (WIN32)
@@ -288,6 +289,7 @@ set (wsjt_FSRCS
lib/fchisq.f90
lib/fchisq65.f90
lib/fil3.f90
+ lib/fil3c.f90
lib/fil4.f90
lib/fil6521.f90
lib/filbig.f90
@@ -302,20 +304,26 @@ set (wsjt_FSRCS
lib/gen4.f90
lib/gen65.f90
lib/gen9.f90
+ lib/genwspr.f90
lib/geodist.f90
lib/getlags.f90
lib/graycode.f90
lib/graycode65.f90
+ lib/grayline.f90
lib/grid2deg.f90
+ lib/hash.f90
+ lib/hopping.f90
lib/image.f90
lib/indexx.f90
lib/interleave4.f90
lib/interleave63.f90
lib/interleave9.f90
+ lib/inter_wspr.f90
lib/jt4.f90
lib/jt4a.f90
lib/jt65a.f90
lib/lpf1.f90
+ lib/mixlpf.f90
lib/moon2.f90
lib/moondop.f90
lib/morse.f90
@@ -328,6 +336,7 @@ set (wsjt_FSRCS
lib/polyfit.f90
lib/prog_args.f90
lib/ps4.f90
+ lib/savec2.f90
lib/sec_midn.f90
lib/setup65.f90
lib/sleep_msec.f90
@@ -344,6 +353,7 @@ set (wsjt_FSRCS
lib/sync4.f90
lib/sync9.f90
lib/timer.f90
+ lib/timf2.f90
lib/tm2.f90
lib/toxyz.f90
lib/twkfreq.f90
@@ -355,6 +365,8 @@ set (wsjt_FSRCS
lib/xcor4.f90
lib/zplt.f90
lib/wavhdr.f90
+ lib/wqencode.f90
+ lib/wspr_downsample.f90
lib/zplot9.f90
)
@@ -364,6 +376,7 @@ set (wsjt_CSRCS
lib/gran.c
lib/igray.c
lib/init_rs.c
+ lib/wsprd/nhash.c
lib/tmoonsub.c
lib/usleep.c
lib/wisdom.c
@@ -657,7 +670,6 @@ if (NOT "${QT_LIBRARY_DIR}" STREQUAL "/lib" AND NOT "${QT_LIBRARY_DIR}" STREQUAL
set (QT_NEED_RPATH TRUE)
endif ()
-
#
# OpenMP
#
@@ -666,10 +678,9 @@ find_package (OpenMP)
#
# fftw3 single precsion library
#
-find_package (FFTW3 COMPONENTS single threads REQUIRED)
+find_package (FFTW3 COMPONENTS double single threads REQUIRED)
include_directories (${FFTW3_INCLUDE_DIRS})
-
#
# libhamlib setup
#
@@ -838,6 +849,9 @@ target_link_libraries (jt65code wsjt_fort wsjt_cxx)
add_executable (jt9code lib/jt9code.f90 wsjtx.rc)
target_link_libraries (jt9code wsjt_fort wsjt_cxx)
+add_executable (wsprd lib/wsprd/wsprd.c lib/wsprd/wsprd_utils.c lib/wsprd/fano.c lib/wsprd/tab.c lib/wsprd/nhash.c)
+target_link_libraries (wsprd ${FFTW3_LIBRARIES})
+
add_executable (jt4code lib/jt4code.f90 wsjtx.rc)
target_link_libraries (jt4code wsjt_fort wsjt_cxx)
@@ -946,7 +960,7 @@ install (TARGETS wsjtx
BUNDLE DESTINATION . COMPONENT runtime
)
-install (TARGETS jt9 jt65code jt9code jt4code message_aggregator
+install (TARGETS jt9 jt65code jt9code jt4code wsprd message_aggregator
RUNTIME DESTINATION ${WSJT_BIN_DESTINATION} COMPONENT runtime
BUNDLE DESTINATION ${WSJT_BIN_DESTINATION} COMPONENT runtime
)
diff --git a/Configuration.cpp b/Configuration.cpp
index 2ddc86388..0804dbbc0 100644
--- a/Configuration.cpp
+++ b/Configuration.cpp
@@ -615,7 +615,8 @@ bool Configuration::enable_VHF_features () const {return m_->enable_VHF_features
bool Configuration::decode_at_52s () const {return m_->decode_at_52s_;}
bool Configuration::split_mode () const
{
- return !m_->rig_is_dummy_ && m_->rig_params_.split_mode != TransceiverFactory::split_mode_none;
+ return !m_->rig_is_dummy_ and
+ (m_->rig_params_.split_mode != TransceiverFactory::split_mode_none);
}
QString Configuration::udp_server_name () const {return m_->udp_server_name_;}
auto Configuration::udp_server_port () const -> port_type {return m_->udp_server_port_;}
@@ -704,9 +705,11 @@ Configuration::impl::impl (Configuration * self, QSettings * settings, QWidget *
, settings_ {settings}
, doc_dir_ {QApplication::applicationDirPath ()}
, frequencies_ {
- { 136130, 474200, 1838000, 3576000, 5357000, 7076000, 10138000, 14076000, 18102000,
- 21076000, 24917000, 28076000, 50276000, 70091000, 144000000, 144489000, 222000000,
- 432000000, 902000000, 1296000000, 2301000000, 2304000000, 2320000000, 3400000000,
+ { 136000, 136130, 474200, 1836600, 1838000, 3576000, 3592600, 5287200, 5357000,
+ 7038600, 7076000, 10138000, 10138700, 14076000, 14095600, 18102000, 18104600,
+ 21076000, 21094600, 24917000, 24924600, 28076000, 28124600, 50276000, 50293000,
+ 70091000, 144000000, 144489000, 222000000, 432000000, 432300000,
+ 902000000, 1296000000, 1296500000, 2301000000, 2304000000, 2320000000, 3400000000,
3456000000, 5760000000,10368000000, 24048000000 }
}
, stations_ {&bands_}
diff --git a/Detector.hpp b/Detector.hpp
index a90875a43..0ef171eae 100644
--- a/Detector.hpp
+++ b/Detector.hpp
@@ -27,7 +27,7 @@ public:
Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned samplesPerFFT, unsigned downSampleFactor = 4u, QObject * parent = 0);
Q_SIGNAL void framesWritten (qint64) const;
-
+ void setPeriod(unsigned p) {m_period=p;}
bool reset () override;
protected:
diff --git a/Modulator.cpp b/Modulator.cpp
index 8806a63a1..dce905b4f 100644
--- a/Modulator.cpp
+++ b/Modulator.cpp
@@ -24,7 +24,8 @@ double const Modulator::m_twoPi = 2.0 * 3.141592653589793238462;
// m_nspd=3072; //18.75 WPM
unsigned const Modulator::m_nspd = 2048 + 512; // 22.5 WPM
-Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObject * parent)
+Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds,
+ QObject * parent)
: AudioDevice {parent}
, m_stream {nullptr}
, m_quickClose {false}
@@ -41,7 +42,10 @@ Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds, QObjec
m_itone0=0;
}
-void Modulator::start (unsigned symbolsLength, double framesPerSymbol, unsigned frequency, double toneSpacing, SoundOutput * stream, Channel channel, bool synchronize, double dBSNR)
+void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
+ unsigned frequency, double toneSpacing,
+ SoundOutput * stream, Channel channel,
+ bool synchronize, double dBSNR)
{
Q_ASSERT (stream);
@@ -264,6 +268,7 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
m_itone0=itone[0];
*/
m_frequency0 = m_frequency;
+// qDebug() << "a" << m_frequency << m_nsps << m_toneSpacing << toneFrequency0 << baud << isym;
// done for this chunk - continue on next call
return framesGenerated * bytesPerFrame ();
diff --git a/Modulator.hpp b/Modulator.hpp
index c7d7fcc30..630d33e81 100644
--- a/Modulator.hpp
+++ b/Modulator.hpp
@@ -30,6 +30,7 @@ public:
unsigned frequency () const {return m_frequency;}
bool isActive () const {return m_state != Idle;}
void setSpread(double s) {m_fSpread=s;}
+ void setPeriod(unsigned p) {m_period=p;}
Q_SLOT void start (unsigned symbolsLength, double framesPerSymbol, unsigned frequency, double toneSpacing, SoundOutput *, Channel = Mono, bool synchronize = true, double dBSNR = 99.);
Q_SLOT void stop (bool quick = false);
diff --git a/about.cpp b/about.cpp
index 99f51941a..5416580c0 100644
--- a/about.cpp
+++ b/about.cpp
@@ -23,8 +23,9 @@ CAboutDlg::CAboutDlg(QWidget *parent) :
"Amateur Radio communication.
"
"© 2001-2015 by Joe Taylor, K1JT, with grateful
"
"acknowledgment for contributions from AC6SL, AE4JY,
"
- "DJ0OT, G4KLA, G4WJS, K3WYC, KA6MAL, KA9Q, KB1ZMX,
"
- "KI7MT, KK1D, PY2SDR, VK3ACF, VK4BDJ, W4TI, W4TV, and W9MDB.
");
+ "DJ0OT, G4KLA, G4WJS, K3WYC, K9AN, KA6MAL, KA9Q,
"
+ "KB1ZMX, KD6EKQ, KI7MT, KK1D, ND0B, PY2SDR,
"
+ "VK3ACF, VK4BDJ, W4TI, W4TV, and W9MDB.
");
}
CAboutDlg::~CAboutDlg()
diff --git a/astro.cpp b/astro.cpp
index f1c9822a9..384844365 100644
--- a/astro.cpp
+++ b/astro.cpp
@@ -29,6 +29,7 @@ Astro::Astro(QSettings * settings, QWidget * parent)
setWindowTitle(QApplication::applicationName () + " - " + tr ("Astronomical Data"));
setStyleSheet ("QWidget {background: white;}");
read_settings ();
+ m_Hz=0;
ui_->text_label->clear();
}
@@ -60,9 +61,8 @@ void Astro::read_settings ()
m_kHz=settings_->value("kHzAdd",100).toInt();
ui_->kHzSpinBox->setValue(m_kHz);
m_bRxAudioTrack=settings_->value("RxAudioTrack",false).toBool();
- ui_->cbRxTrack->setChecked(m_bRxAudioTrack);
m_bTxAudioTrack=settings_->value("TxAudioTrack",false).toBool();
- ui_->cbTxTrack->setChecked(m_bTxAudioTrack);
+ ui_->cbTxAudioTrack->setChecked(m_bTxAudioTrack);
move (settings_->value ("window/pos", pos ()).toPoint ());
settings_->endGroup ();
}
@@ -82,10 +82,10 @@ void Astro::write_settings ()
}
void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid, qint64 freqMoon,
- qint32* ndop, qint32* ndop00)
+ qint32* ndop, qint32* ndop00, bool bTx)
{
double azsun,elsun,azmoon,elmoon,azmoondx,elmoondx;
- double ramoon,decmoon,dgrd,poloffset,xnr,techo;
+ double ramoon,decmoon,dgrd,poloffset,xnr,techo,width1,width2;
int ntsky;
QString date = t.date().toString("yyyy MMM dd").trimmed ();
QString utc = t.time().toString().trimmed ();
@@ -95,16 +95,19 @@ void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid, qint64 fre
int nhr=t.time().hour();
int nmin=t.time().minute();
double sec=t.time().second() + 0.001*t.time().msec();
- int isec=sec;
double uth=nhr + nmin/60.0 + sec/3600.0;
if(freqMoon < 1) freqMoon=144000000;
int nfreq=freqMoon/1000000;
double freq8=(double)freqMoon;
+ QDir dataDir = QStandardPaths::writableLocation (QStandardPaths::DataLocation);
+ QString fname = QDir::toNativeSeparators(dataDir.absoluteFilePath ("azel.dat"));
+
astrosub_(&nyear, &month, &nday, &uth, &freq8, mygrid.toLatin1(),
hisgrid.toLatin1(), &azsun, &elsun, &azmoon, &elmoon,
&azmoondx, &elmoondx, &ntsky, ndop, ndop00, &ramoon, &decmoon,
- &dgrd, &poloffset, &xnr, &techo, 6, 6);
+ &dgrd, &poloffset, &xnr, &techo, &width1, &width2, &bTx,
+ fname.toLatin1(), 6, 6, fname.length());
QString message;
{
@@ -117,13 +120,15 @@ void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid, qint64 fre
<< qSetRealNumberPrecision (1)
<< "Az: " << azmoon << "\n"
"El: " << elmoon << "\n"
- "MyDop: " << *ndop00 << "\n"
+ "Dop: " << *ndop00 << "\n"
+ "Width: " << int(width1) << "\n"
<< qSetRealNumberPrecision (2)
<< "Delay: " << techo << "\n"
<< qSetRealNumberPrecision (1)
<< "DxAz: " << azmoondx << "\n"
"DxEl: " << elmoondx << "\n"
"DxDop: " << *ndop << "\n"
+ "DxWid: " << int(width2) << "\n"
"Dec: " << decmoon << "\n"
"SunAz: " << azsun << "\n"
"SunEl: " << elsun << "\n"
@@ -134,6 +139,7 @@ void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid, qint64 fre
}
ui_->text_label->setText(message);
+ /*
static QFile f {QDir {QStandardPaths::writableLocation (
QStandardPaths::DataLocation)}.absoluteFilePath ("azel.dat")};
if (!f.open (QIODevice::WriteOnly | QIODevice::Text)) {
@@ -185,13 +191,14 @@ void Astro::astroUpdate(QDateTime t, QString mygrid, QString hisgrid, qint64 fre
<< qSetFieldWidth (0) << ",Doppler";
}
f.close();
+ */
}
void Astro::on_cbDopplerTracking_toggled(bool b)
{
QRect g=this->geometry();
if(b) {
- g.setWidth(460);
+ g.setWidth(430);
} else {
g.setWidth(200);
}
@@ -228,15 +235,9 @@ void Astro::on_rb10Hz_clicked()
void Astro::on_rb100Hz_clicked()
{
m_stepHz=100;
-
}
-void Astro::on_cbRxTrack_toggled(bool b)
-{
- m_bRxAudioTrack=b;
-}
-
-void Astro::on_cbTxTrack_toggled(bool b)
+void Astro::on_cbTxAudioTrack_toggled(bool b)
{
m_bTxAudioTrack=b;
}
@@ -245,3 +246,8 @@ void Astro::on_kHzSpinBox_valueChanged(int n)
{
m_kHz=n;
}
+
+void Astro::on_HzSpinBox_valueChanged(int n)
+{
+ m_Hz=n;
+}
diff --git a/astro.h b/astro.h
index 90e2fd728..dcf18a8fd 100644
--- a/astro.h
+++ b/astro.h
@@ -23,7 +23,7 @@ public:
explicit Astro(QSettings * settings, QWidget * parent = nullptr);
~Astro ();
void astroUpdate(QDateTime t, QString mygrid, QString hisgrid, qint64 freqMoon,
- qint32* ndop, qint32 *ndop00);
+ qint32* ndop, qint32 *ndop00, bool bTx);
bool m_bDopplerTracking;
bool m_bRxAudioTrack;
@@ -31,6 +31,7 @@ public:
qint32 m_DopplerMethod;
qint32 m_kHz;
+ qint32 m_Hz;
qint32 m_stepHz;
protected:
@@ -44,9 +45,9 @@ private slots:
void on_rb1Hz_clicked();
void on_rb10Hz_clicked();
void on_rb100Hz_clicked();
- void on_cbRxTrack_toggled(bool b);
- void on_cbTxTrack_toggled(bool b);
+ void on_cbTxAudioTrack_toggled(bool b);
void on_kHzSpinBox_valueChanged(int n);
+ void on_HzSpinBox_valueChanged(int n);
private:
void read_settings ();
@@ -63,7 +64,8 @@ extern "C" {
double* elsun, double* azmoon, double* elmoon, double* azmoondx,
double* elmoondx, int* ntsky, int* ndop, int* ndop00,
double* ramoon, double* decmoon, double* dgrd, double* poloffset,
- double* xnr, double* techo, int len1, int len2);
+ double* xnr, double* techo, double* width1, double* width2,
+ bool* bTx, const char* fname, int len1, int len2, int len3);
}
#endif // ASTRO_H
diff --git a/astro.ui b/astro.ui
index 8ecf07d99..926a9774b 100644
--- a/astro.ui
+++ b/astro.ui
@@ -6,8 +6,8 @@
0
0
- 460
- 420
+ 400
+ 440
@@ -19,7 +19,7 @@
200
- 420
+ 440
@@ -31,7 +31,7 @@
0
0
201
- 361
+ 400
@@ -48,7 +48,7 @@
- Courier New
+ Courier
14
75
false
@@ -71,222 +71,11 @@
6
-
-
-
- 219
- 19
- 221
- 361
-
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
-
- 20
- 20
- 185
- 96
-
-
-
-
- 185
- 0
-
-
-
- Doppler tracking
-
-
- -
-
-
- Full Doppler to DX Grid
-
-
- true
-
-
-
- -
-
-
- Constant frequency on Moon
-
-
- false
-
-
-
- -
-
-
- None
-
-
- false
-
-
-
-
-
-
-
-
- 20
- 130
- 185
- 96
-
-
-
-
- 185
- 0
-
-
-
- Transceiver step size
-
-
-
-
- 10
- 23
- 61
- 17
-
-
-
- 1 Hz
-
-
- true
-
-
-
-
-
- 10
- 46
- 71
- 17
-
-
-
- 10 Hz
-
-
-
-
-
- 10
- 69
- 71
- 17
-
-
-
- 100 Hz
-
-
-
-
-
- false
-
-
-
- 20
- 230
- 185
- 73
-
-
-
-
- 185
- 0
-
-
-
- Audio frequency tracking
-
-
-
-
- 10
- 23
- 36
- 17
-
-
-
- Rx
-
-
-
-
-
- 10
- 46
- 35
- 17
-
-
-
- Tx
-
-
-
-
-
-
- 20
- 310
- 185
- 51
-
-
-
-
- 185
- 0
-
-
-
- kHz above nominal band edge
-
-
-
-
- 50
- 20
- 51
- 22
-
-
-
- 999
-
-
- 100
-
-
-
-
1
- 386
+ 410
195
22
@@ -327,6 +116,259 @@
+
+
+
+ 200
+ 12
+ 198
+ 411
+
+
+
+ -
+
+
+
+ 196
+ 0
+
+
+
+
+ 16777215
+ 60
+
+
+
+ Frequency above nominal band edge
+
+
+
+
+ 20
+ 20
+ 75
+ 22
+
+
+
+
+ 75
+ 0
+
+
+
+ Qt::AlignCenter
+
+
+ kHz
+
+
+ 999
+
+
+ 200
+
+
+
+
+
+ 100
+ 20
+ 75
+ 22
+
+
+
+
+ 75
+ 0
+
+
+
+ Qt::AlignCenter
+
+
+ Hz
+
+
+ -2000
+
+
+ 2000
+
+
+ 100
+
+
+
+
+ -
+
+
+
+ 196
+ 0
+
+
+
+
+ 16777215
+ 100
+
+
+
+ Doppler tracking
+
+
+
-
+
+
+ Full Doppler to DX Grid
+
+
+ true
+
+
+
+ -
+
+
+ Constant frequency on Moon
+
+
+ false
+
+
+
+ -
+
+
+ None
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+
+ 196
+ 0
+
+
+
+
+ 16777215
+ 90
+
+
+
+ Transceiver step size
+
+
+
+
+ 10
+ 23
+ 61
+ 17
+
+
+
+ 1 Hz
+
+
+ true
+
+
+
+
+
+ 10
+ 46
+ 71
+ 17
+
+
+
+ 10 Hz
+
+
+
+
+
+ 10
+ 69
+ 71
+ 17
+
+
+
+ 100 Hz
+
+
+
+
+ -
+
+
+
+ 196
+ 0
+
+
+
+
+ 16777215
+ 60
+
+
+
+ Tx audio tracking
+
+
+
+ false
+
+
+
+ 20
+ 20
+ 105
+ 17
+
+
+
+ Enable
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 44
+
+
+
+
+
+
diff --git a/commons.h b/commons.h
index a9dd67be2..d572e1d3c 100644
--- a/commons.h
+++ b/commons.h
@@ -2,7 +2,7 @@
#define COMMONS_H
#define NSMAX 6827
-#define NTMAX 60
+#define NTMAX 120
#define RX_SAMPLE_RATE 12000
extern struct FortranCommon {
@@ -28,6 +28,7 @@ extern struct FortranCommon {
int nmode;
int minw;
int nclearave;
+ int minSync;
float emedelay;
float dttol;
int nlist;
diff --git a/doc/common/license.adoc b/doc/common/license.adoc
index 2d1d32cc0..887e6682f 100644
--- a/doc/common/license.adoc
+++ b/doc/common/license.adoc
@@ -13,4 +13,4 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this documentation. If not, see {gnu_gpl}.
-Copyright (C) 2001-2014 Joseph H Taylor, Jr, {joe_taylor}.
+Copyright (C) 2001-2015 Joseph H Taylor, Jr, {joe_taylor}.
diff --git a/doc/user_guide/acknowledgements.adoc b/doc/user_guide/acknowledgements.adoc
index d5f03ee51..b9f73bb7e 100644
--- a/doc/user_guide/acknowledgements.adoc
+++ b/doc/user_guide/acknowledgements.adoc
@@ -8,10 +8,10 @@ suggestions and advice that have greatly aided the development of
_WSJT_ and its sister programs.
For _WSJT-X_ in particular, we acknowledge contributions from *AC6SL,
-AE4JY, DJ0OT, G4KLA, G4WJS, K3WYC, KA6MAL, KA9Q, KB1ZMX, KI7MT, KK1D,
-PY2SDR, VK3ACF, VK4BDJ, W4TI, W4TV, and W9MDB*. Each of these
-amateurs has helped to bring the program’s design, code, and
-documentation to its present state.
+AE4JY, DJ0OT, G4KLA, G4WJS, K3WYC, K9AN, KA6MAL, KA9Q, KB1ZMX, KD6EKQ,
+KI7MT, KK1D, ND0B, PY2SDR, VK3ACF, VK4BDJ, W4TI, W4TV, and W9MDB*.
+Each of these amateurs has helped to bring the program’s design, code,
+and documentation to its present state.
Most of the color palettes for the _WSJT-X_ waterfall were copied from
the excellent, well documented, open-source program _fldigi_, by *W1HKJ*
diff --git a/lib/astro0.f90 b/lib/astro0.f90
index 847197cac..0075e1ffd 100644
--- a/lib/astro0.f90
+++ b/lib/astro0.f90
@@ -31,7 +31,7 @@ subroutine astro0(nyear,month,nday,uth8,freq8,mygrid,hisgrid, &
call tm2(day8,xlat2,xlon2,xl2,b2)
call tm2(day8+1.d0/1440.0,xlat1,xlon1,xl1a,b1a)
call tm2(day8+1.d0/1440.0,xlat2,xlon2,xl2a,b2a)
- fghz=0.001*nfreq
+ fghz=1.d-9*freq8
dldt1=DEGS*(xl1a-xl1)
dbdt1=DEGS*(b1a-b1)
dldt2=DEGS*(xl2a-xl2)
diff --git a/lib/astrosub.f90 b/lib/astrosub.f90
index 41c9717f4..db424356c 100644
--- a/lib/astrosub.f90
+++ b/lib/astrosub.f90
@@ -1,14 +1,48 @@
subroutine astrosub(nyear,month,nday,uth8,freq8,mygrid,hisgrid, &
AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8,ntsky,ndop,ndop00, &
- RAMoon8,DecMoon8,Dgrd8,poloffset8,xnr8,techo8)
+ RAMoon8,DecMoon8,Dgrd8,poloffset8,xnr8,techo8,width1,width2,bTx,fname)
implicit real*8 (a-h,o-z)
- character*6 mygrid,hisgrid
+ character*6 mygrid,hisgrid,fname*(*),c1*1
+ logical*1 bTx
call astro0(nyear,month,nday,uth8,freq8,mygrid,hisgrid, &
AzSun8,ElSun8,AzMoon8,ElMoon8,AzMoonB8,ElMoonB8,ntsky,ndop,ndop00, &
dbMoon8,RAMoon8,DecMoon8,HA8,Dgrd8,sd8,poloffset8,xnr8,dfdt,dfdt0, &
width1,width2,w501,w502,xlst8,techo8)
- return
+ imin=60*uth8
+ isec=3600*uth8
+ ih=uth8
+ im=mod(imin,60)
+ is=mod(isec,60)
+ open(15,file=fname,status='unknown',err=900)
+ c1='R'
+ nRx=1
+ if(bTx) then
+ c1='T'
+ nRx=0
+ endif
+ AzAux=0.
+ ElAux=0.
+ nfreq=freq8/1000000
+ doppler=ndop
+ doppler00=ndop00
+ write(15,1010,err=10) ih,im,is,AzMoon8,ElMoon8, &
+ ih,im,is,AzSun8,ElSun8, &
+ ih,im,is,AzAux,ElAux, &
+ nfreq,doppler,dfdt,doppler00,dfdt0,c1
+! TXFirst,TRPeriod,poloffset,Dgrd,xnr,ave,rms,nRx
+1010 format( &
+ i2.2,':',i2.2,':',i2.2,',',f5.1,',',f5.1,',Moon'/ &
+ i2.2,':',i2.2,':',i2.2,',',f5.1,',',f5.1,',Sun'/ &
+ i2.2,':',i2.2,':',i2.2,',',f5.1,',',f5.1,',Source'/ &
+ i5,',',f8.1,',',f8.2,',',f8.1,',',f8.2,',Doppler, ',a1)
+! i1,',',i3,',',f8.1,','f8.1,',',f8.1,',',f12.3,',',f12.3,',',i1,',RPol')
+10 close(15)
+ go to 999
+
+900 print*,'Error opening azel.dat'
+
+999 return
end subroutine astrosub
diff --git a/lib/avg4.f90 b/lib/avg4.f90
index d97fe7f67..da9c9358b 100644
--- a/lib/avg4.f90
+++ b/lib/avg4.f90
@@ -69,7 +69,7 @@ subroutine avg4(nutc,snrsync,dtxx,flip,nfreq,mode4,ntol,ndepth,neme, &
do i=1,nsave
csync='*'
if(flipsave(i).lt.0.0) csync='#'
- write(14,1000) cused(i),iutc(i),syncsave(i),dtsave(i),nfsave(i),csync
+ write(14,1000) cused(i),iutc(i),syncsave(i)-5.0,dtsave(i),nfsave(i),csync
1000 format(a1,i5.4,f6.1,f6.2,i6,1x,a1)
enddo
diff --git a/lib/code426.f90 b/lib/code426.f90
index 0ded58889..7acf03acd 100644
--- a/lib/code426.f90
+++ b/lib/code426.f90
@@ -39,15 +39,18 @@ program code426
do j=5,nmsgs !Find codewords up to j=nmsgs with maximum
npk=0 !distance from all the rest
do i=1,iters
- call random_number(c)
- ic(1:MZ,j)=int(4*c)
- nd=MZ
- do k=1,j-1 !Test candidate against all others in list
- nd=min(nd,count(ic(1:MZ,j).ne.ic(1:MZ,k)))
- enddo
- if(nd.gt.npk) then
- npk=nd
+ call random_number(c) !Generate a random codeword candidate
+ ic(1:MZ,j)=int(4*c) !Convert real to integer
+! nd=MZ
+! do k=1,j-1 !Test candidate against all others in list
+! n=count(ic(1:MZ,j).ne.ic(1:MZ,k))
+! nd=min(n,nd)
+! enddo
+ call dist426(ic,j,mind)
+ if(mind.gt.npk) then
+ npk=mind
icsave=ic(1:MZ,j) !Best candidate so far, save it
+! if(npk.ge.19) exit !It won't get any better...
endif
enddo
write(*,1000) j,npk,ic(1:MZ,j)
diff --git a/lib/constants.f90 b/lib/constants.f90
index 321bd24e7..e9a4c034a 100644
--- a/lib/constants.f90
+++ b/lib/constants.f90
@@ -1,4 +1,4 @@
- parameter (NTMAX=60)
+ parameter (NTMAX=120)
parameter (NMAX=NTMAX*12000) !Total sample intervals (one minute)
parameter (NDMAX=NTMAX*1500) !Sample intervals at 1500 Hz rate
parameter (NSMAX=6827) !Max length of saved spectra
diff --git a/lib/decode4.f90 b/lib/decode4.f90
index 1efdbc547..0ad4e7700 100644
--- a/lib/decode4.f90
+++ b/lib/decode4.f90
@@ -23,7 +23,8 @@ subroutine decode4(dat,npts,dtx,nfreq,flip,mode4,ndepth,neme,minw, &
istart=nint((dtx+0.8)/dt) !Start index for synced FFTs
if(istart.lt.0) istart=0
nchips=0
- qbest=0.0
+ qbest=0.
+ qtop=0.
deepmsg=' '
ichbest=-1
c0=0.
diff --git a/lib/decoder.f90 b/lib/decoder.f90
index 3448e36f7..fbaabc0e6 100644
--- a/lib/decoder.f90
+++ b/lib/decoder.f90
@@ -11,7 +11,8 @@ subroutine decoder(ss,id2,nfsample)
character datetime*20,mycall*12,mygrid*6,hiscall*12,hisgrid*6
common/npar/nutc,ndiskdat,ntrperiod,nfqso,newdat,npts8,nfa,nfsplit,nfb, &
ntol,kin,nzhsym,nsubmode,nagain,ndepth,ntxmode,nmode,minw,nclearave, &
- emedelay,dttol,nlist,listutc(10),datetime,mycall,mygrid,hiscall,hisgrid
+ minsync,emedelay,dttol,nlist,listutc(10),datetime,mycall,mygrid, &
+ hiscall,hisgrid
common/tracer/limtrace,lu
integer onlevel(0:10)
@@ -41,9 +42,9 @@ subroutine decoder(ss,id2,nfsample)
if(nfsample.eq.12000) call wav11(id2,jz,dd)
if(nfsample.eq.11025) dd(1:jz)=id2(1:jz)
endif
- call jt4a(dd,jz,nutc,nfqso,newdat,nfa,nfb,ntol,emedelay,dttol, &
- nagain,ndepth,nclearave,minw,nsubmode,mycall,mygrid,hiscall, &
- hisgrid,nlist,listutc)
+ call jt4a(dd,jz,nutc,nfqso,ntol,emedelay,dttol,nagain,ndepth, &
+ nclearave,minsync,minw,nsubmode,mycall,hiscall,hisgrid, &
+ nlist,listutc)
go to 800
endif
diff --git a/lib/encode232.f90 b/lib/encode232.f90
index bc904b488..491f204ab 100644
--- a/lib/encode232.f90
+++ b/lib/encode232.f90
@@ -3,7 +3,7 @@ subroutine encode232(dat,nsym,symbol)
! Convolutional encoder for a K=32, r=1/2 code.
integer*1 dat(13) !User data, packed 8 bits per byte
- integer*1 symbol(500) !Channel symbols, one bit per byte
+ integer*1 symbol(206) !Channel symbols, one bit per byte
integer*1 i1
include 'conv232.f90'
diff --git a/lib/fil3c.f90 b/lib/fil3c.f90
new file mode 100644
index 000000000..6000253f8
--- /dev/null
+++ b/lib/fil3c.f90
@@ -0,0 +1,72 @@
+subroutine fil3c(c1,n1,c2,n2)
+
+! FIR complex-to-complex low-pass filter designed with ScopeFIR
+!
+!-----------------------------------------------
+! fsample (Hz) 12000 Input sample rate
+! Ntaps 113 Number of filter taps
+! fc (Hz) 500 Cutoff frequency
+! fstop (Hz) 750 Lower limit of stopband
+! Ripple (dB) 0.2 Ripple in passband
+! Stop Atten (dB) 50 Stopband attenuation
+! fout (Hz) 1500 Output sample rate
+
+! Suggest calling with n1 = 8*n2 + 105, where n2 is the desired number
+! of 1500 Hz output samples.
+
+ parameter (NTAPS=113)
+ parameter (NH=NTAPS/2)
+ parameter (NDOWN=8) !Downsample ratio = 1/8
+ complex c1(n1)
+ complex c2(n1/NDOWN)
+ complex z
+
+! Filter coefficients:
+ real a(-NH:NH)
+ data a/ &
+ -0.001818142144,-0.000939132050,-0.001044063556,-0.001042685542, &
+ -0.000908957610,-0.000628132309,-0.000202701465, 0.000346307629, &
+ 0.000978154552, 0.001634336295, 0.002243121592, 0.002726064379, &
+ 0.003006201675, 0.003018055983, 0.002717699575, 0.002091546534, &
+ 0.001162489032,-0.000007904811,-0.001321554806,-0.002649908053, &
+ -0.003843608784,-0.004747338068,-0.005218967042,-0.005148229529, &
+ -0.004470167307,-0.003177923811,-0.001335998901, 0.000915924193, &
+ 0.003386100636, 0.005818719744, 0.007939147967, 0.009465071347, &
+ 0.010145641899, 0.009787447819, 0.008285915754, 0.005645995244, &
+ 0.001995842303,-0.002410369720,-0.007202515555,-0.011916811719, &
+ -0.016028350845,-0.018993391440,-0.020297455955,-0.019503792208, &
+ -0.016298136197,-0.010526834635,-0.002223837363, 0.008378305829, &
+ 0.020854478160, 0.034608532659, 0.048909701463, 0.062944127288, &
+ 0.075874892030, 0.086903764340, 0.095332017649, 0.100619428175, &
+ 0.102420526192, 0.100619428175, 0.095332017649, 0.086903764340, &
+ 0.075874892030, 0.062944127288, 0.048909701463, 0.034608532659, &
+ 0.020854478160, 0.008378305829,-0.002223837363,-0.010526834635, &
+ -0.016298136197,-0.019503792208,-0.020297455955,-0.018993391440, &
+ -0.016028350845,-0.011916811719,-0.007202515555,-0.002410369720, &
+ 0.001995842303, 0.005645995244, 0.008285915754, 0.009787447819, &
+ 0.010145641899, 0.009465071347, 0.007939147967, 0.005818719744, &
+ 0.003386100636, 0.000915924193,-0.001335998901,-0.003177923811, &
+ -0.004470167307,-0.005148229529,-0.005218967042,-0.004747338068, &
+ -0.003843608784,-0.002649908053,-0.001321554806,-0.000007904811, &
+ 0.001162489032, 0.002091546534, 0.002717699575, 0.003018055983, &
+ 0.003006201675, 0.002726064379, 0.002243121592, 0.001634336295, &
+ 0.000978154552, 0.000346307629,-0.000202701465,-0.000628132309, &
+ -0.000908957610,-0.001042685542,-0.001044063556,-0.000939132050, &
+ -0.001818142144/
+ save a
+
+ n2=(n1-NTAPS+NDOWN)/NDOWN
+ k0=NH-NDOWN+1
+
+! Loop over all output samples
+ do i=1,n2
+ z=0.
+ k=k0 + NDOWN*i
+ do j=-NH,NH
+ z=z + c1(j+k)*a(j)
+ enddo
+ c2(i)=z
+ enddo
+
+ return
+end subroutine fil3c
diff --git a/lib/fillcom.f90 b/lib/fillcom.f90
index df7018edc..b5ff419fb 100644
--- a/lib/fillcom.f90
+++ b/lib/fillcom.f90
@@ -5,7 +5,8 @@ subroutine fillcom(nutc0,ndepth0,nrxfreq,mode,tx9,flow,fsplit,fhigh)
character datetime*20,mycall*12,mygrid*6,hiscall*12,hisgrid*6
common/npar/nutc,ndiskdat,ntrperiod,nfqso,newdat,npts8,nfa,nfsplit,nfb, &
ntol,kin,nzhsym,nsubmode,nagain,ndepth,ntxmode,nmode,minw,nclearave, &
- emedelay,dttol,nlist,listutc(10),datetime,mycall,mygrid,hiscall,hisgrid
+ minsync,emedelay,dttol,nlist,listutc(10),datetime,mycall,mygrid, &
+ hiscall,hisgrid
save
nutc=nutc0
diff --git a/lib/flat1.f90 b/lib/flat1.f90
index b9dfdd1d5..1de8b95c3 100644
--- a/lib/flat1.f90
+++ b/lib/flat1.f90
@@ -12,14 +12,10 @@ subroutine flat1(savg,iz,nsmo,syellow)
call pctile(savg(i-nsmo/2),nsmo,50,x(i))
x(i-nh:i+nh-1)=x(i)
enddo
- do i=1,ia-1
- x(i)=x(ia)
- enddo
- do i=ib+1,iz
- x(i)=x(ib)
- enddo
+ x(1:ia-1)=x(ia)
+ x(ib+1:iz)=x(ib)
- x0=0.001*maxval(x(1:iz))
+ x0=0.001*maxval(x(iz/10:(9*iz)/10))
syellow(1:iz)=savg(1:iz)/(x(1:iz)+x0)
return
diff --git a/lib/genwspr.f90 b/lib/genwspr.f90
new file mode 100644
index 000000000..8d395dfe2
--- /dev/null
+++ b/lib/genwspr.f90
@@ -0,0 +1,31 @@
+subroutine genwspr(message,msgsent,itone)
+! Encode a WSPR message and generate the array of channel symbols.
+
+ character*22 message,msgsent
+ parameter (MAXSYM=176)
+ integer*1 symbol(MAXSYM)
+ integer*1 data0(11)
+ integer*4 itone(162)
+ integer npr3(162)
+ data npr3/ &
+ 1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0, &
+ 0,1,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1, &
+ 0,0,0,0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0,1, &
+ 1,0,1,0,0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1, &
+ 0,0,1,0,1,1,0,0,0,1,1,0,1,0,1,0,0,0,1,0, &
+ 0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,0,1,1, &
+ 0,1,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,0,1,1, &
+ 0,0,0,0,0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0, &
+ 0,0/
+
+ call wqencode(message,ntype,data0) !Source encoding
+ nbytes=(50+31+7)/8
+ call encode232(data0,162,symbol) !Convolutional encoding
+ call inter_wspr(symbol,1) !Interleaving
+ do i=1,162
+ itone(i)=npr3(i) + 2*symbol(i)
+ enddo
+ msgsent=message !### To be fixed... ?? ###
+
+ return
+end subroutine genwspr
diff --git a/lib/grayline.f90 b/lib/grayline.f90
new file mode 100644
index 000000000..cc1aeeea2
--- /dev/null
+++ b/lib/grayline.f90
@@ -0,0 +1,32 @@
+subroutine grayline(nyear,month,nday,uth,mygrid,nduration,isun)
+
+ character*6 mygrid
+ real LST
+ real lat,lon
+
+ call grid2deg(MyGrid,elon,lat)
+ lon=-elon
+
+ uth0=uth-0.5*nduration/60.0
+ uth1=uth+0.5*nduration/60.0
+
+ call sun(nyear,month,nday,uth0,lon,lat,RASun,DecSun,LST, &
+ AzSun,ElSun0,mjd,day)
+ call sun(nyear,month,nday,uth1,lon,lat,RASun,DecSun,LST, &
+ AzSun,ElSun1,mjd,day)
+
+ elchk=-0.8333
+ isun=-1
+ if(elsun0.lt.elchk .and. elsun1.ge.elchk) then
+ isun=0
+ else if(elsun0.gt.elchk .and. elsun1.le.elchk) then
+ isun=2
+ else if(elsun1.gt.elchk) then
+ isun=1
+ else
+ isun=3
+ endif
+
+ return
+end subroutine grayline
+
diff --git a/lib/hash.f90 b/lib/hash.f90
new file mode 100644
index 000000000..679e00c0a
--- /dev/null
+++ b/lib/hash.f90
@@ -0,0 +1,15 @@
+subroutine hash(string,len,ihash)
+
+ parameter (MASK15=32767)
+ character*(*) string
+ integer*1 ic(12)
+
+ do i=1,len
+ ic(i)=ichar(string(i:i))
+ enddo
+ i=nhash(ic,len,146)
+ ihash=iand(i,MASK15)
+
+! print*,'C',ihash,len,string
+ return
+end subroutine hash
diff --git a/lib/hopping.f90 b/lib/hopping.f90
new file mode 100644
index 000000000..cdca2fc02
--- /dev/null
+++ b/lib/hopping.f90
@@ -0,0 +1,81 @@
+subroutine hopping(nyear,month,nday,uth,mygrid,nduration,npctx,isun, &
+ iband,ntxnext)
+
+! Determine Rx or Tx in coordinated hopping mode.
+
+ character*6 mygrid
+ integer tx(10,6) !T/R array for 2 hours: 10 bands, 6 time slots
+ real r(6) !Random numbers
+ integer ii(1)
+ data n2hr0/-999/
+ save n2hr0,tx
+
+ call grayline(nyear,month,nday,uth,mygrid,nduration,isun)
+
+ ns0=uth*3600.0
+ pctx=npctx
+ nrx=0
+ ntxnext=0
+ nsec=(ns0+10)/120 !Round up to start of next 2-min slot
+ nsec=nsec*120
+ n2hr=nsec/7200 !2-hour slot number
+
+ if(n2hr.ne.n2hr0) then
+! Compute a new Rx/Tx pattern for this 2-hour interval
+ n2hr0=n2hr !Mark this one as done
+ tx=0 !Clear the tx array
+ do j=1,10 !Loop over all 10 bands
+ call random_number(r)
+ do i=1,6,2 !Select one each of 3 pairs of the
+ if(r(i).gt.r(i+1)) then ! 6 slots for Tx
+ tx(j,i)=1
+ r(i+1)=0.
+ else
+ tx(j,i+1)=1
+ r(i)=0.
+ endif
+ enddo
+
+ if(pctx.lt.50.0) then !If pctx < 50, we may kill one Tx slot
+ ii=maxloc(r)
+ i=ii(1)
+ call random_number(rr)
+ rrtest=(50.0-pctx)/16.667
+ if(rr.lt.rrtest) then
+ tx(j,i)=0
+ r(i)=0.
+ endif
+ endif
+
+ if(pctx.lt.33.333) then !If pctx < 33, may kill another
+ ii=maxloc(r)
+ i=ii(1)
+ call random_number(rr)
+ rrtest=(33.333-pctx)/16.667
+ if(rr.lt.rrtest) then
+ tx(j,i)=0
+ r(i)=0.
+ endif
+ endif
+ enddo
+
+! We now have 1 to 3 Tx periods per band in the 2-hour interval.
+ endif
+
+ iband=mod(nsec/120,10) + 1
+ iseq=mod(nsec/1200,6) + 1
+ if(iseq.lt.1) iseq=1
+ if(tx(iband,iseq).eq.1) then
+ ntxnext=1
+ else
+ nrx=1
+ endif
+ iband=iband-1
+
+! write(*,3000) iband,iseq,nrx,ntxnext
+!3000 format('Fortran iband, iseq,nrx,ntxnext:',4i5)
+! write(*,3001) int(tx)
+!3001 format(10i2)
+
+ return
+end subroutine hopping
diff --git a/lib/indexx.f90 b/lib/indexx.f90
index 57c1ec075..f4dd07043 100644
--- a/lib/indexx.f90
+++ b/lib/indexx.f90
@@ -1,19 +1,91 @@
-subroutine indexx(n,arr,indx)
+subroutine indexx(arr,n,indx)
- parameter (NMAX=3000)
- integer indx(n)
- real arr(n)
- real brr(NMAX)
- if(n.gt.NMAX) then
- print*,'n=',n,' too big in indexx.'
- stop
- endif
- do i=1,n
- brr(i)=arr(i)
- indx(i)=i
+ parameter (M=7,NSTACK=50)
+ integer n,indx(n)
+ integer arr(n)
+ integer i,indxt,ir,itemp,j,jstack,k,l,istack(NSTACK)
+ real a
+
+ do j=1,n
+ indx(j)=j
enddo
- call ssort(brr,indx,n,2)
- return
+ jstack=0
+ l=1
+ ir=n
+1 if(ir-l.lt.M) then
+ do j=l+1,ir
+ indxt=indx(j)
+ a=arr(indxt)
+ do i=j-1,1,-1
+ if(arr(indx(i)).le.a) goto 2
+ indx(i+1)=indx(i)
+ enddo
+ i=0
+2 indx(i+1)=indxt
+ enddo
+ if(jstack.eq.0) return
+
+ ir=istack(jstack)
+ l=istack(jstack-1)
+ jstack=jstack-2
+
+ else
+ k=(l+ir)/2
+ itemp=indx(k)
+ indx(k)=indx(l+1)
+ indx(l+1)=itemp
+
+ if(arr(indx(l+1)).gt.arr(indx(ir))) then
+ itemp=indx(l+1)
+ indx(l+1)=indx(ir)
+ indx(ir)=itemp
+ endif
+
+ if(arr(indx(l)).gt.arr(indx(ir))) then
+ itemp=indx(l)
+ indx(l)=indx(ir)
+ indx(ir)=itemp
+ endif
+
+ if(arr(indx(l+1)).gt.arr(indx(l))) then
+ itemp=indx(l+1)
+ indx(l+1)=indx(l)
+ indx(l)=itemp
+ endif
+
+ i=l+1
+ j=ir
+ indxt=indx(l)
+ a=arr(indxt)
+3 continue
+ i=i+1
+ if(arr(indx(i)).lt.a) goto 3
+
+4 continue
+ j=j-1
+ if(arr(indx(j)).gt.a) goto 4
+ if(j.lt.i) goto 5
+ itemp=indx(i)
+ indx(i)=indx(j)
+ indx(j)=itemp
+ goto 3
+
+5 indx(l)=indx(j)
+ indx(j)=indxt
+ jstack=jstack+2
+ if(jstack.gt.NSTACK) stop 'NSTACK too small in indexx'
+ if(ir-i+1.ge.j-l)then
+ istack(jstack)=ir
+ istack(jstack-1)=i
+ ir=j-1
+ else
+ istack(jstack)=j-1
+ istack(jstack-1)=l
+ l=i
+ endif
+ endif
+ goto 1
+
end subroutine indexx
diff --git a/lib/inter_wspr.f90 b/lib/inter_wspr.f90
new file mode 100644
index 000000000..9f980454c
--- /dev/null
+++ b/lib/inter_wspr.f90
@@ -0,0 +1,45 @@
+subroutine inter_wspr(id,ndir)
+
+! Interleave (ndir=1) or de-interleave (ndir=-1) the array id.
+
+ integer*1 id(0:161),itmp(0:161)
+ integer j0(0:161)
+ logical first
+ data first/.true./
+ save
+
+ if(first) then
+! Compute the interleave table using bit reversal.
+ k=-1
+ do i=0,255
+ n=0
+ ii=i
+ do j=0,7
+ n=n+n
+ if(iand(ii,1).ne.0) n=n+1
+ ii=ii/2
+ enddo
+ if(n.le.161) then
+ k=k+1
+ j0(k)=n
+ endif
+ enddo
+ first=.false.
+ endif
+
+ if(ndir.eq.1) then
+ do i=0,161
+ itmp(j0(i))=id(i)
+ enddo
+ else
+ do i=0,161
+ itmp(i)=id(j0(i))
+ enddo
+ endif
+
+ do i=0,161
+ id(i)=itmp(i)
+ enddo
+
+ return
+end subroutine inter_wspr
diff --git a/lib/jt4a.f90 b/lib/jt4a.f90
index 81dec920a..9fd283f29 100644
--- a/lib/jt4a.f90
+++ b/lib/jt4a.f90
@@ -1,6 +1,5 @@
-subroutine jt4a(dd,jz,nutc,nfqso,newdat,nfa,nfb,ntol0,emedelay,dttol, &
- nagain,ndepth,nclearave,minw,nsubmode,mycall,mygrid,hiscall,hisgrid, &
- nlist0,listutc0)
+subroutine jt4a(dd,jz,nutc,nfqso,ntol0,emedelay,dttol,nagain,ndepth, &
+ nclearave,minsync,minw,nsubmode,mycall,hiscall,hisgrid,nlist0,listutc0)
use jt4
integer listutc0(10)
@@ -8,7 +7,7 @@ subroutine jt4a(dd,jz,nutc,nfqso,newdat,nfa,nfb,ntol0,emedelay,dttol, &
real*4 dat(30*12000)
character*6 cfile6
character*12 mycall,hiscall
- character*6 mygrid,hisgrid
+ character*6 hisgrid
mode4=nch(nsubmode+1)
ntol=ntol0
@@ -35,7 +34,7 @@ subroutine jt4a(dd,jz,nutc,nfqso,newdat,nfa,nfb,ntol0,emedelay,dttol, &
cfile6(5:6)=' '
call timer('wsjt4 ',0)
- call wsjt4(dat,jz2,nutc,NClearAve,ntol,emedelay,dttol,mode4,minw, &
+ call wsjt4(dat,jz2,nutc,NClearAve,minsync,ntol,emedelay,dttol,mode4,minw, &
mycall,hiscall,hisgrid,nfqso,NAgain,ndepth,neme)
call timer('wsjt4 ',1)
diff --git a/lib/jt9.f90 b/lib/jt9.f90
index 5cfa742af..6a791d082 100644
--- a/lib/jt9.f90
+++ b/lib/jt9.f90
@@ -52,8 +52,9 @@ program jt9
character datetime*20,mycall*12,mygrid*6,hiscall*12,hisgrid*6
common/jt9com/ss(184,NSMAX),savg(NSMAX),id2(NMAX),nutc,ndiskdat, &
ntr,mousefqso,newdat,npts8a,nfa,nfsplit,nfb,ntol,kin,nzhsym, &
- nsubmode,nagain,ndepth,ntxmode,nmode,minw,nclearave,emedelay, &
- dttol,nlist,listutc(10),datetime,mycall,mygrid,hiscall,hisgrid
+ nsubmode,nagain,ndepth,ntxmode,nmode,minw,nclearave,minsync, &
+ emedelay,dttol,nlist,listutc(10),datetime,mycall,mygrid, &
+ hiscall,hisgrid
common/tracer/limtrace,lu
common/patience/npatience,nthreads
@@ -219,7 +220,8 @@ program jt9
! Compute rough symbol spectra for the JT9 decoder
ingain=0
call timer('symspec ',0)
- call symspec(k,ntrperiod,nsps,ingain,pxdb,s,df3, &
+ nminw=1
+ call symspec(k,ntrperiod,nsps,ingain,nminw,pxdb,s,df3, &
ihsym,npts8)
call timer('symspec ',1)
endif
@@ -227,8 +229,7 @@ program jt9
if(nhsym.ge.181) exit
endif
enddo
-
-10 close(10)
+ close(10)
call fillcom(nutc0,ndepth,nrxfreq,mode,tx9,flow,fsplit,fhigh)
call decoder(ss,id2,nfsample)
enddo
diff --git a/lib/jt9c.f90 b/lib/jt9c.f90
index a64629478..305e784da 100644
--- a/lib/jt9c.f90
+++ b/lib/jt9c.f90
@@ -4,11 +4,12 @@ subroutine jt9c(ss,savg,id2,nparams0)
real*4 ss(184*NSMAX),savg(NSMAX)
integer*2 id2(NTMAX*12000)
- integer nparams0(46),nparams(46)
+ integer nparams0(47),nparams(47)
character datetime*20,mycall*12,mygrid*6,hiscall*12,hisgrid*6
common/npar/nutc,ndiskdat,ntrperiod,nfqso,newdat,npts8,nfa,nfsplit,nfb, &
ntol,kin,nzhsym,nsave,nagain,ndepth,ntxmode,nmode,minw,nclearave, &
- emedelay,dttol,nlist,listutc(10),datetime,mycall,mygrid,hiscall,hisgrid
+ minsync,emedelay,dttol,nlist,listutc(10),datetime,mycall,mygrid, &
+ hiscall,hisgrid
common/patience/npatience,nthreads
equivalence (nparams,nutc)
diff --git a/lib/mixlpf.f90 b/lib/mixlpf.f90
new file mode 100644
index 000000000..cd775ab87
--- /dev/null
+++ b/lib/mixlpf.f90
@@ -0,0 +1,25 @@
+subroutine mixlpf(x1,nbfo,c0)
+
+ real*4 x1(512)
+ real*8 twopi,phi,dphi
+ complex c1(512),c2(105+512)
+ complex c0(64)
+ data phi/0.d0/
+ save phi,c2
+
+ twopi=8.d0*atan(1.d0)
+ dphi=twopi*nbfo/12000.d0
+
+ do i=1,512
+ phi=phi+dphi
+ if(phi.gt.twopi) phi=phi-twopi
+ xphi=phi
+ c1(i)=x1(i)*cmplx(cos(xphi),sin(xphi))
+ enddo
+ c2(106:105+512)=c1
+
+ call fil3c(c2,105+512,c0,n2)
+ c2(1:105)=c1(512-104:512) !Save 105 trailing samples
+
+ return
+end subroutine mixlpf
diff --git a/lib/packjt.f90 b/lib/packjt.f90
index 4aed6fe31..11090fe2c 100644
--- a/lib/packjt.f90
+++ b/lib/packjt.f90
@@ -4,898 +4,984 @@ module packjt
subroutine packbits(dbits,nsymd,m0,sym)
-! Pack 0s and 1s from dbits() into sym() with m0 bits per word.
-! NB: nsymd is the number of packed output words.
+ ! Pack 0s and 1s from dbits() into sym() with m0 bits per word.
+ ! NB: nsymd is the number of packed output words.
- integer sym(nsymd)
- integer*1 dbits(*)
+ integer sym(nsymd)
+ integer*1 dbits(*)
- k=0
- do i=1,nsymd
- n=0
- do j=1,m0
- k=k+1
- m=dbits(k)
- n=ior(ishft(n,1),m)
- enddo
- sym(i)=n
- enddo
+ k=0
+ do i=1,nsymd
+ n=0
+ do j=1,m0
+ k=k+1
+ m=dbits(k)
+ n=ior(ishft(n,1),m)
+ enddo
+ sym(i)=n
+ enddo
- return
-end subroutine packbits
+ return
+ end subroutine packbits
-subroutine unpackbits(sym,nsymd,m0,dbits)
+ subroutine unpackbits(sym,nsymd,m0,dbits)
-! Unpack bits from sym() into dbits(), one bit per byte.
-! NB: nsymd is the number of input words, and m0 their length.
-! there will be m0*nsymd output bytes, each 0 or 1.
+ ! Unpack bits from sym() into dbits(), one bit per byte.
+ ! NB: nsymd is the number of input words, and m0 their length.
+ ! there will be m0*nsymd output bytes, each 0 or 1.
- integer sym(nsymd)
- integer*1 dbits(*)
+ integer sym(nsymd)
+ integer*1 dbits(*)
- k=0
- do i=1,nsymd
- mask=ishft(1,m0-1)
- do j=1,m0
- k=k+1
- dbits(k)=0
- if(iand(mask,sym(i)).ne.0) dbits(k)=1
- mask=ishft(mask,-1)
- enddo
- enddo
+ k=0
+ do i=1,nsymd
+ mask=ishft(1,m0-1)
+ do j=1,m0
+ k=k+1
+ dbits(k)=0
+ if(iand(mask,sym(i)).ne.0) dbits(k)=1
+ mask=ishft(mask,-1)
+ enddo
+ enddo
- return
-end subroutine unpackbits
+ return
+ end subroutine unpackbits
-subroutine packcall(callsign,ncall,text)
+ subroutine packcall(callsign,ncall,text)
-! Pack a valid callsign into a 28-bit integer.
+ ! Pack a valid callsign into a 28-bit integer.
- parameter (NBASE=37*36*10*27*27*27)
- character callsign*6,c*1,tmp*6
+ parameter (NBASE=37*36*10*27*27*27)
+ character callsign*6,c*1,tmp*6
+ logical text
+
+ text=.false.
+
+ ! Work-around for Swaziland prefix:
+ if(callsign(1:4).eq.'3DA0') callsign='3D0'//callsign(5:6)
+
+ if(callsign(1:3).eq.'CQ ') then
+ ncall=NBASE + 1
+ if(callsign(4:4).ge.'0' .and. callsign(4:4).le.'9' .and. &
+ callsign(5:5).ge.'0' .and. callsign(5:5).le.'9' .and. &
+ callsign(6:6).ge.'0' .and. callsign(6:6).le.'9') then
+ read(callsign(4:6),*) nfreq
+ ncall=NBASE + 3 + nfreq
+ endif
+ return
+ else if(callsign(1:4).eq.'QRZ ') then
+ ncall=NBASE + 2
+ return
+ else if(callsign(1:3).eq.'DE ') then
+ ncall=267796945
+ return
+ endif
+
+ tmp=' '
+ if(callsign(3:3).ge.'0' .and. callsign(3:3).le.'9') then
+ tmp=callsign
+ else if(callsign(2:2).ge.'0' .and. callsign(2:2).le.'9') then
+ if(callsign(6:6).ne.' ') then
+ text=.true.
+ return
+ endif
+ tmp=' '//callsign(:5)
+ else
+ text=.true.
+ return
+ endif
+
+ do i=1,6
+ c=tmp(i:i)
+ if(c.ge.'a' .and. c.le.'z') &
+ tmp(i:i)=char(ichar(c)-ichar('a')+ichar('A'))
+ enddo
+
+ n1=0
+ if((tmp(1:1).ge.'A'.and.tmp(1:1).le.'Z').or.tmp(1:1).eq.' ') n1=1
+ if(tmp(1:1).ge.'0' .and. tmp(1:1).le.'9') n1=1
+ n2=0
+ if(tmp(2:2).ge.'A' .and. tmp(2:2).le.'Z') n2=1
+ if(tmp(2:2).ge.'0' .and. tmp(2:2).le.'9') n2=1
+ n3=0
+ if(tmp(3:3).ge.'0' .and. tmp(3:3).le.'9') n3=1
+ n4=0
+ if((tmp(4:4).ge.'A'.and.tmp(4:4).le.'Z').or.tmp(4:4).eq.' ') n4=1
+ n5=0
+ if((tmp(5:5).ge.'A'.and.tmp(5:5).le.'Z').or.tmp(5:5).eq.' ') n5=1
+ n6=0
+ if((tmp(6:6).ge.'A'.and.tmp(6:6).le.'Z').or.tmp(6:6).eq.' ') n6=1
+
+ if(n1+n2+n3+n4+n5+n6 .ne. 6) then
+ text=.true.
+ return
+ endif
+
+ ncall=nchar(tmp(1:1))
+ ncall=36*ncall+nchar(tmp(2:2))
+ ncall=10*ncall+nchar(tmp(3:3))
+ ncall=27*ncall+nchar(tmp(4:4))-10
+ ncall=27*ncall+nchar(tmp(5:5))-10
+ ncall=27*ncall+nchar(tmp(6:6))-10
+
+ return
+ end subroutine packcall
+
+ subroutine unpackcall(ncall,word,iv2,psfx)
+
+ parameter (NBASE=37*36*10*27*27*27)
+ character word*12,c*37,psfx*4
+
+ data c/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ '/
+
+ word='......'
+ psfx=' '
+ n=ncall
+ iv2=0
+ if(n.ge.262177560) go to 20
+ word='......'
+ ! if(n.ge.262177560) go to 999 !Plain text message ...
+ i=mod(n,27)+11
+ word(6:6)=c(i:i)
+ n=n/27
+ i=mod(n,27)+11
+ word(5:5)=c(i:i)
+ n=n/27
+ i=mod(n,27)+11
+ word(4:4)=c(i:i)
+ n=n/27
+ i=mod(n,10)+1
+ word(3:3)=c(i:i)
+ n=n/10
+ i=mod(n,36)+1
+ word(2:2)=c(i:i)
+ n=n/36
+ i=n+1
+ word(1:1)=c(i:i)
+ do i=1,4
+ if(word(i:i).ne.' ') go to 10
+ enddo
+ go to 999
+ 10 word=word(i:)
+ go to 999
+
+ 20 if(n.ge.267796946) go to 999
+
+ ! We have a JT65v2 message
+ if((n.ge.262178563) .and. (n.le.264002071)) then
+ ! CQ with prefix
+ iv2=1
+ n=n-262178563
+ i=mod(n,37)+1
+ psfx(4:4)=c(i:i)
+ n=n/37
+ i=mod(n,37)+1
+ psfx(3:3)=c(i:i)
+ n=n/37
+ i=mod(n,37)+1
+ psfx(2:2)=c(i:i)
+ n=n/37
+ i=n+1
+ psfx(1:1)=c(i:i)
+
+ else if((n.ge.264002072) .and. (n.le.265825580)) then
+ ! QRZ with prefix
+ iv2=2
+ n=n-264002072
+ i=mod(n,37)+1
+ psfx(4:4)=c(i:i)
+ n=n/37
+ i=mod(n,37)+1
+ psfx(3:3)=c(i:i)
+ n=n/37
+ i=mod(n,37)+1
+ psfx(2:2)=c(i:i)
+ n=n/37
+ i=n+1
+ psfx(1:1)=c(i:i)
+
+ else if((n.ge.265825581) .and. (n.le.267649089)) then
+ ! DE with prefix
+ iv2=3
+ n=n-265825581
+ i=mod(n,37)+1
+ psfx(4:4)=c(i:i)
+ n=n/37
+ i=mod(n,37)+1
+ psfx(3:3)=c(i:i)
+ n=n/37
+ i=mod(n,37)+1
+ psfx(2:2)=c(i:i)
+ n=n/37
+ i=n+1
+ psfx(1:1)=c(i:i)
+
+ else if((n.ge.267649090) .and. (n.le.267698374)) then
+ ! CQ with suffix
+ iv2=4
+ n=n-267649090
+ i=mod(n,37)+1
+ psfx(3:3)=c(i:i)
+ n=n/37
+ i=mod(n,37)+1
+ psfx(2:2)=c(i:i)
+ n=n/37
+ i=n+1
+ psfx(1:1)=c(i:i)
+
+ else if((n.ge.267698375) .and. (n.le.267747659)) then
+ ! QRZ with suffix
+ iv2=5
+ n=n-267698375
+ i=mod(n,37)+1
+ psfx(3:3)=c(i:i)
+ n=n/37
+ i=mod(n,37)+1
+ psfx(2:2)=c(i:i)
+ n=n/37
+ i=n+1
+ psfx(1:1)=c(i:i)
+
+ else if((n.ge.267747660) .and. (n.le.267796944)) then
+ ! DE with suffix
+ iv2=6
+ n=n-267747660
+ i=mod(n,37)+1
+ psfx(3:3)=c(i:i)
+ n=n/37
+ i=mod(n,37)+1
+ psfx(2:2)=c(i:i)
+ n=n/37
+ i=n+1
+ psfx(1:1)=c(i:i)
+
+ else if(n.eq.267796945) then
+ ! DE with no prefix or suffix
+ iv2=7
+ psfx = ' '
+ endif
+
+ 999 if(word(1:3).eq.'3D0') word='3DA0'//word(4:)
+
+ return
+ end subroutine unpackcall
+
+ subroutine packgrid(grid,ng,text)
+
+ parameter (NGBASE=180*180)
+ character*4 grid
+ character*1 c1
+ logical text
+
+ text=.false.
+ if(grid.eq.' ') go to 90 !Blank grid is OK
+
+ ! First, handle signal reports in the original range, -01 to -30 dB
+ if(grid(1:1).eq.'-') then
+ read(grid(2:3),*,err=800,end=800) n
+ if(n.ge.1 .and. n.le.30) then
+ ng=NGBASE+1+n
+ go to 900
+ endif
+ go to 10
+ else if(grid(1:2).eq.'R-') then
+ read(grid(3:4),*,err=800,end=800) n
+ if(n.ge.1 .and. n.le.30) then
+ ng=NGBASE+31+n
+ go to 900
+ endif
+ go to 10
+ ! Now check for RO, RRR, or 73 in the message field normally used for grid
+ else if(grid(1:4).eq.'RO ') then
+ ng=NGBASE+62
+ go to 900
+ else if(grid(1:4).eq.'RRR ') then
+ ng=NGBASE+63
+ go to 900
+ else if(grid(1:4).eq.'73 ') then
+ ng=NGBASE+64
+ go to 900
+ endif
+
+ ! Now check for extended-range signal reports: -50 to -31, and 0 to +49.
+ 10 n=99
+ c1=grid(1:1)
+ read(grid,*,err=20,end=20) n
+ go to 30
+ 20 read(grid(2:4),*,err=30,end=30) n
+ 30 if(n.ge.-50 .and. n.le.49) then
+ if(c1.eq.'R') then
+ write(grid,1002) n+50
+ 1002 format('LA',i2.2)
+ else
+ write(grid,1003) n+50
+ 1003 format('KA',i2.2)
+ endif
+ go to 40
+ endif
+
+ ! Maybe it's free text ?
+ if(grid(1:1).lt.'A' .or. grid(1:1).gt.'R') text=.true.
+ if(grid(2:2).lt.'A' .or. grid(2:2).gt.'R') text=.true.
+ if(grid(3:3).lt.'0' .or. grid(3:3).gt.'9') text=.true.
+ if(grid(4:4).lt.'0' .or. grid(4:4).gt.'9') text=.true.
+ if(text) go to 900
+
+ ! OK, we have a properly formatted grid locator
+ 40 call grid2deg(grid//'mm',dlong,dlat)
+ long=int(dlong)
+ lat=int(dlat+ 90.0)
+ ng=((long+180)/2)*180 + lat
+ go to 900
+
+ 90 ng=NGBASE + 1
+ go to 900
+
+ 800 text=.true.
+ 900 continue
+
+ return
+ end subroutine packgrid
+
+ subroutine unpackgrid(ng,grid)
+
+ parameter (NGBASE=180*180)
+ character grid*4,grid6*6
+
+ grid=' '
+ if(ng.ge.32400) go to 10
+ dlat=mod(ng,180)-90
+ dlong=(ng/180)*2 - 180 + 2
+ call deg2grid(dlong,dlat,grid6)
+ grid=grid6(:4)
+ if(grid(1:2).eq.'KA') then
+ read(grid(3:4),*) n
+ n=n-50
+ write(grid,1001) n
+ 1001 format(i3.2)
+ if(grid(1:1).eq.' ') grid(1:1)='+'
+ else if(grid(1:2).eq.'LA') then
+ read(grid(3:4),*) n
+ n=n-50
+ write(grid,1002) n
+ 1002 format('R',i3.2)
+ if(grid(2:2).eq.' ') grid(2:2)='+'
+ endif
+ go to 900
+
+ 10 n=ng-NGBASE-1
+ if(n.ge.1 .and.n.le.30) then
+ write(grid,1012) -n
+ 1012 format(i3.2)
+ else if(n.ge.31 .and.n.le.60) then
+ n=n-30
+ write(grid,1022) -n
+ 1022 format('R',i3.2)
+ else if(n.eq.61) then
+ grid='RO'
+ else if(n.eq.62) then
+ grid='RRR'
+ else if(n.eq.63) then
+ grid='73'
+ endif
+
+ 900 return
+ end subroutine unpackgrid
+
+ subroutine packmsg(msg,dat,itype)
+
+ ! Packs a JT4/JT9/JT65 message into twelve 6-bit symbols
+
+ ! itype Message Type
+ !--------------------
+ ! 1 Standardd message
+ ! 2 Type 1 prefix
+ ! 3 Type 1 suffix
+ ! 4 Type 2 prefix
+ ! 5 Type 2 suffix
+ ! 6 Free text
+ ! -1 Does not decode correctly
+
+ parameter (NBASE=37*36*10*27*27*27)
+ parameter (NBASE2=262178562)
+ character*22 msg
+ integer dat(12)
+ character*12 c1,c2
+ character*4 c3
+ character*6 grid6
+ logical text1,text2,text3
+
+ itype=1
+ call fmtmsg(msg,iz)
+
+ if(msg(1:6).eq.'CQ DX ') msg(3:3)='9'
+
+ ! See if it's a CQ message
+ if(msg(1:3).eq.'CQ ') then
+ i=3
+ ! ... and if so, does it have a reply frequency?
+ if(msg(4:4).ge.'0' .and. msg(4:4).le.'9' .and. &
+ msg(5:5).ge.'0' .and. msg(5:5).le.'9' .and. &
+ msg(6:6).ge.'0' .and. msg(6:6).le.'9') i=7
+ go to 1
+ endif
+
+ do i=1,22
+ if(msg(i:i).eq.' ') go to 1 !Get 1st blank
+ enddo
+ go to 10 !Consider msg as plain text
+
+ 1 ia=i
+ c1=msg(1:ia-1)
+ do i=ia+1,22
+ if(msg(i:i).eq.' ') go to 2 !Get 2nd blank
+ enddo
+ go to 10 !Consider msg as plain text
+
+ 2 ib=i
+ c2=msg(ia+1:ib-1)
+
+ do i=ib+1,22
+ if(msg(i:i).eq.' ') go to 3 !Get 3rd blank
+ enddo
+ go to 10 !Consider msg as plain text
+
+ 3 ic=i
+ c3=' '
+ if(ic.ge.ib+1) c3=msg(ib+1:ic)
+ if(c3.eq.'OOO ') c3=' ' !Strip out the OOO flag
+ call getpfx1(c1,k1,nv2a)
+ if(nv2a.ge.4) go to 10
+ call packcall(c1,nc1,text1)
+ if(text1) go to 10
+ call getpfx1(c2,k2,nv2b)
+ call packcall(c2,nc2,text2)
+ if(text2) go to 10
+ if(nv2a.eq.2 .or. nv2a.eq.3 .or. nv2b.eq.2 .or. nv2b.eq.3) then
+ if(k1.lt.0 .or. k2.lt.0 .or. k1*k2.ne.0) go to 10
+ if(k2.gt.0) k2=k2+450
+ k=max(k1,k2)
+ if(k.gt.0) then
+ call k2grid(k,grid6)
+ c3=grid6(:4)
+ endif
+ endif
+ call packgrid(c3,ng,text3)
+
+ if(nv2a.lt.4 .and. nv2b.lt.4 .and. (.not.text1) .and. (.not.text2) .and. &
+ (.not.text3)) go to 20
+
+ nc1=0
+ if(nv2b.eq.4) then
+ if(c1(1:3).eq.'CQ ') nc1=262178563 + k2
+ if(c1(1:4).eq.'QRZ ') nc1=264002072 + k2
+ if(c1(1:3).eq.'DE ') nc1=265825581 + k2
+ else if(nv2b.eq.5) then
+ if(c1(1:3).eq.'CQ ') nc1=267649090 + k2
+ if(c1(1:4).eq.'QRZ ') nc1=267698375 + k2
+ if(c1(1:3).eq.'DE ') nc1=267747660 + k2
+ endif
+ if(nc1.ne.0) go to 20
+
+ ! The message will be treated as plain text.
+ 10 itype=6
+ call packtext(msg,nc1,nc2,ng)
+ ng=ng+32768
+
+ ! Encode data into 6-bit words
+ 20 continue
+ if(itype.ne.6) itype=max(nv2a,nv2b)
+ dat(1)=iand(ishft(nc1,-22),63) !6 bits
+ dat(2)=iand(ishft(nc1,-16),63) !6 bits
+ dat(3)=iand(ishft(nc1,-10),63) !6 bits
+ dat(4)=iand(ishft(nc1, -4),63) !6 bits
+ dat(5)=4*iand(nc1,15)+iand(ishft(nc2,-26),3) !4+2 bits
+ dat(6)=iand(ishft(nc2,-20),63) !6 bits
+ dat(7)=iand(ishft(nc2,-14),63) !6 bits
+ dat(8)=iand(ishft(nc2, -8),63) !6 bits
+ dat(9)=iand(ishft(nc2, -2),63) !6 bits
+ dat(10)=16*iand(nc2,3)+iand(ishft(ng,-12),15) !2+4 bits
+ dat(11)=iand(ishft(ng,-6),63)
+ dat(12)=iand(ng,63)
+
+ return
+ end subroutine packmsg
+
+ subroutine unpackmsg(dat,msg)
+
+ parameter (NBASE=37*36*10*27*27*27)
+ parameter (NGBASE=180*180)
+ integer dat(12)
+ character c1*12,c2*12,grid*4,msg*22,grid6*6,psfx*4,junk2*4
+ logical cqnnn
+
+ cqnnn=.false.
+ nc1=ishft(dat(1),22) + ishft(dat(2),16) + ishft(dat(3),10)+ &
+ ishft(dat(4),4) + iand(ishft(dat(5),-2),15)
+
+ nc2=ishft(iand(dat(5),3),26) + ishft(dat(6),20) + &
+ ishft(dat(7),14) + ishft(dat(8),8) + ishft(dat(9),2) + &
+ iand(ishft(dat(10),-4),3)
+
+ ng=ishft(iand(dat(10),15),12) + ishft(dat(11),6) + dat(12)
+
+ if(ng.ge.32768) then
+ call unpacktext(nc1,nc2,ng,msg)
+ go to 100
+ endif
+
+ call unpackcall(nc1,c1,iv2,psfx)
+ if(iv2.eq.0) then
+ ! This is an "original JT65" message
+ if(nc1.eq.NBASE+1) c1='CQ '
+ if(nc1.eq.NBASE+2) c1='QRZ '
+ nfreq=nc1-NBASE-3
+ if(nfreq.ge.0 .and. nfreq.le.999) then
+ write(c1,1002) nfreq
+ 1002 format('CQ ',i3.3)
+ cqnnn=.true.
+ endif
+ endif
+
+ call unpackcall(nc2,c2,junk1,junk2)
+ call unpackgrid(ng,grid)
+
+ if(iv2.gt.0) then
+ ! This is a JT65v2 message
+ do i=1,4
+ if(ichar(psfx(i:i)).eq.0) psfx(i:i)=' '
+ enddo
+
+ n1=len_trim(psfx)
+ n2=len_trim(c2)
+ if(iv2.eq.1) msg='CQ '//psfx(:n1)//'/'//c2(:n2)//' '//grid
+ if(iv2.eq.2) msg='QRZ '//psfx(:n1)//'/'//c2(:n2)//' '//grid
+ if(iv2.eq.3) msg='DE '//psfx(:n1)//'/'//c2(:n2)//' '//grid
+ if(iv2.eq.4) msg='CQ '//c2(:n2)//'/'//psfx(:n1)//' '//grid
+ if(iv2.eq.5) msg='QRZ '//c2(:n2)//'/'//psfx(:n1)//' '//grid
+ if(iv2.eq.6) msg='DE '//c2(:n2)//'/'//psfx(:n1)//' '//grid
+ if(iv2.eq.7) msg='DE '//c2(:n2)//' '//grid
+ if(iv2.eq.8) msg=' '
+ go to 100
+ else
+
+ endif
+
+ grid6=grid//'ma'
+ call grid2k(grid6,k)
+ if(k.ge.1 .and. k.le.450) call getpfx2(k,c1)
+ if(k.ge.451 .and. k.le.900) call getpfx2(k,c2)
+
+ i=index(c1,char(0))
+ if(i.ge.3) c1=c1(1:i-1)//' '
+ i=index(c2,char(0))
+ if(i.ge.3) c2=c2(1:i-1)//' '
+
+ msg=' '
+ j=0
+ if(cqnnn) then
+ msg=c1//' '
+ j=7 !### ??? ###
+ go to 10
+ endif
+
+ do i=1,12
+ j=j+1
+ msg(j:j)=c1(i:i)
+ if(c1(i:i).eq.' ') go to 10
+ enddo
+ j=j+1
+ msg(j:j)=' '
+
+ 10 do i=1,12
+ if(j.le.21) j=j+1
+ msg(j:j)=c2(i:i)
+ if(c2(i:i).eq.' ') go to 20
+ enddo
+ if(j.le.21) j=j+1
+ msg(j:j)=' '
+
+ 20 if(k.eq.0) then
+ do i=1,4
+ if(j.le.21) j=j+1
+ msg(j:j)=grid(i:i)
+ enddo
+ if(j.le.21) j=j+1
+ msg(j:j)=' '
+ endif
+
+ 100 continue
+ if(msg(1:6).eq.'CQ9DX ') msg(3:3)=' '
+
+ return
+ end subroutine unpackmsg
+
+ subroutine packtext(msg,nc1,nc2,nc3)
+
+ parameter (MASK28=2**28 - 1)
+ character*13 msg
+ character*42 c
+ data c/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ +-./?'/
+
+ nc1=0
+ nc2=0
+ nc3=0
+
+ do i=1,5 !First 5 characters in nc1
+ do j=1,42 !Get character code
+ if(msg(i:i).eq.c(j:j)) go to 10
+ enddo
+ j=37
+ 10 j=j-1 !Codes should start at zero
+ nc1=42*nc1 + j
+ enddo
+
+ do i=6,10 !Characters 6-10 in nc2
+ do j=1,42 !Get character code
+ if(msg(i:i).eq.c(j:j)) go to 20
+ enddo
+ j=37
+ 20 j=j-1 !Codes should start at zero
+ nc2=42*nc2 + j
+ enddo
+
+ do i=11,13 !Characters 11-13 in nc3
+ do j=1,42 !Get character code
+ if(msg(i:i).eq.c(j:j)) go to 30
+ enddo
+ j=37
+ 30 j=j-1 !Codes should start at zero
+ nc3=42*nc3 + j
+ enddo
+
+ ! We now have used 17 bits in nc3. Must move one each to nc1 and nc2.
+ nc1=nc1+nc1
+ if(iand(nc3,32768).ne.0) nc1=nc1+1
+ nc2=nc2+nc2
+ if(iand(nc3,65536).ne.0) nc2=nc2+1
+ nc3=iand(nc3,32767)
+
+ return
+ end subroutine packtext
+
+ subroutine unpacktext(nc1,nc2,nc3,msg)
+
+ character*22 msg
+ character*44 c
+ data c/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ +-./?'/
+
+ nc3=iand(nc3,32767) !Remove the "plain text" bit
+ if(iand(nc1,1).ne.0) nc3=nc3+32768
+ nc1=nc1/2
+ if(iand(nc2,1).ne.0) nc3=nc3+65536
+ nc2=nc2/2
+
+ do i=5,1,-1
+ j=mod(nc1,42)+1
+ msg(i:i)=c(j:j)
+ nc1=nc1/42
+ enddo
+
+ do i=10,6,-1
+ j=mod(nc2,42)+1
+ msg(i:i)=c(j:j)
+ nc2=nc2/42
+ enddo
+
+ do i=13,11,-1
+ j=mod(nc3,42)+1
+ msg(i:i)=c(j:j)
+ nc3=nc3/42
+ enddo
+ msg(14:22) = ' '
+
+ return
+ end subroutine unpacktext
+
+ subroutine getpfx1(callsign,k,nv2)
+
+ character*12 callsign0,callsign,lof,rof
+ character*8 c
+ character addpfx*8,tpfx*4,tsfx*3
+ logical ispfx,issfx,invalid
+ common/pfxcom/addpfx
+ include 'pfx.f90'
+
+ callsign0=callsign
+ nv2=1
+ iz=index(callsign,' ') - 1
+ if(iz.lt.0) iz=12
+ islash=index(callsign(1:iz),'/')
+ k=0
+ ! if(k.eq.0) go to 10 !Tnx to DL9RDZ for reminder:this was for tests only!
+ c=' '
+ if(islash.gt.0 .and. islash.le.(iz-4)) then
+ ! Add-on prefix
+ c=callsign(1:islash-1)
+ callsign=callsign(islash+1:iz)
+ do i=1,NZ
+ if(pfx(i)(1:4).eq.c) then
+ k=i
+ nv2=2
+ go to 10
+ endif
+ enddo
+ if(addpfx.eq.c) then
+ k=449
+ nv2=2
+ go to 10
+ endif
+
+ else if(islash.eq.(iz-1)) then
+ ! Add-on suffix
+ c=callsign(islash+1:iz)
+ callsign=callsign(1:islash-1)
+ do i=1,NZ2
+ if(sfx(i).eq.c(1:1)) then
+ k=400+i
+ nv2=3
+ go to 10
+ endif
+ enddo
+ endif
+
+ 10 if(islash.ne.0 .and.k.eq.0) then
+ ! Original JT65 would force this compound callsign to be treated as
+ ! plain text. In JT65v2, we will encode the prefix or suffix into nc1.
+ ! The task here is to compute the proper value of k.
+ lof=callsign0(:islash-1)
+ rof=callsign0(islash+1:)
+ llof=len_trim(lof)
+ lrof=len_trim(rof)
+ ispfx=(llof.gt.0 .and. llof.le.4)
+ issfx=(lrof.gt.0 .and. lrof.le.3)
+ invalid=.not.(ispfx.or.issfx)
+ if(ispfx.and.issfx) then
+ if(llof.lt.3) issfx=.false.
+ if(lrof.lt.3) ispfx=.false.
+ if(ispfx.and.issfx) then
+ i=ichar(callsign0(islash-1:islash-1))
+ if(i.ge.ichar('0') .and. i.le.ichar('9')) then
+ issfx=.false.
+ else
+ ispfx=.false.
+ endif
+ endif
+ endif
+
+ if(invalid) then
+ k=-1
+ else
+ if(ispfx) then
+ tpfx=lof(1:4)
+ k=nchar(tpfx(1:1))
+ k=37*k + nchar(tpfx(2:2))
+ k=37*k + nchar(tpfx(3:3))
+ k=37*k + nchar(tpfx(4:4))
+ nv2=4
+ i=index(callsign0,'/')
+ callsign=callsign0(:i-1)
+ callsign=callsign0(i+1:)
+ endif
+ if(issfx) then
+ tsfx=rof(1:3)
+ k=nchar(tsfx(1:1))
+ k=37*k + nchar(tsfx(2:2))
+ k=37*k + nchar(tsfx(3:3))
+ nv2=5
+ i=index(callsign0,'/')
+ callsign=callsign0(:i-1)
+ endif
+ endif
+ endif
+
+ return
+ end subroutine getpfx1
+
+ subroutine getpfx2(k0,callsign)
+
+ character callsign*12
+ include 'pfx.f90'
+ character addpfx*8
+ common/pfxcom/addpfx
+
+ k=k0
+ if(k.gt.450) k=k-450
+ if(k.ge.1 .and. k.le.NZ) then
+ iz=index(pfx(k),' ') - 1
+ callsign=pfx(k)(1:iz)//'/'//callsign
+ else if(k.ge.401 .and. k.le.400+NZ2) then
+ iz=index(callsign,' ') - 1
+ callsign=callsign(1:iz)//'/'//sfx(k-400)
+ else if(k.eq.449) then
+ iz=index(addpfx,' ') - 1
+ if(iz.lt.1) iz=8
+ callsign=addpfx(1:iz)//'/'//callsign
+ endif
+
+ return
+ end subroutine getpfx2
+
+ subroutine grid2k(grid,k)
+
+ character*6 grid
+
+ call grid2deg(grid,xlong,xlat)
+ nlong=nint(xlong)
+ nlat=nint(xlat)
+ k=0
+ if(nlat.ge.85) k=5*(nlong+179)/2 + nlat-84
+
+ return
+ end subroutine grid2k
+
+ subroutine k2grid(k,grid)
+ character grid*6
+
+ nlong=2*mod((k-1)/5,90)-179
+ if(k.gt.450) nlong=nlong+180
+ nlat=mod(k-1,5)+ 85
+ dlat=nlat
+ dlong=nlong
+ call deg2grid(dlong,dlat,grid)
+
+ return
+ end subroutine k2grid
+
+ subroutine grid2n(grid,n)
+ character*4 grid
+
+ i1=ichar(grid(1:1))-ichar('A')
+ i2=ichar(grid(3:3))-ichar('0')
+ i=10*i1 + i2
+ n=-i - 31
+
+ return
+ end subroutine grid2n
+
+ subroutine n2grid(n,grid)
+ character*4 grid
+
+ if(n.gt.-31 .or. n.lt.-70) stop 'Error in n2grid'
+ i=-(n+31) !NB: 0 <= i <= 39
+ i1=i/10
+ i2=mod(i,10)
+ grid(1:1)=char(ichar('A')+i1)
+ grid(2:2)='A'
+ grid(3:3)=char(ichar('0')+i2)
+ grid(4:4)='0'
+
+ return
+ end subroutine n2grid
+
+ function nchar(c)
+
+ ! Convert ascii number, letter, or space to 0-36 for callsign packing.
+
+ character c*1
+
+ n=0 !Silence compiler warning
+ if(c.ge.'0' .and. c.le.'9') then
+ n=ichar(c)-ichar('0')
+ else if(c.ge.'A' .and. c.le.'Z') then
+ n=ichar(c)-ichar('A') + 10
+ else if(c.ge.'a' .and. c.le.'z') then
+ n=ichar(c)-ichar('a') + 10
+ else if(c.ge.' ') then
+ n=36
+ else
+ Print*,'Invalid character in callsign ',c,' ',ichar(c)
+ stop
+ endif
+ nchar=n
+
+ return
+ end function nchar
+
+ subroutine pack50(n1,n2,dat)
+
+ integer*1 dat(11),i1
+
+ i1=iand(ishft(n1,-20),255) !8 bits
+ dat(1)=i1
+ i1=iand(ishft(n1,-12),255) !8 bits
+ dat(2)=i1
+ i1=iand(ishft(n1, -4),255) !8 bits
+ dat(3)=i1
+ i1=16*iand(n1,15)+iand(ishft(n2,-18),15) !4+4 bits
+ dat(4)=i1
+ i1=iand(ishft(n2,-10),255) !8 bits
+ dat(5)=i1
+ i1=iand(ishft(n2, -2),255) !8 bits
+ dat(6)=i1
+ i1=64*iand(n2,3) !2 bits
+ dat(7)=i1
+ dat(8)=0
+ dat(9)=0
+ dat(10)=0
+ dat(11)=0
+
+ return
+ end subroutine pack50
+
+subroutine packpfx(call1,n1,ng,nadd)
+
+ character*12 call1,call0
+ character*3 pfx
logical text
- text=.false.
-
-! Work-around for Swaziland prefix:
- if(callsign(1:4).eq.'3DA0') callsign='3D0'//callsign(5:6)
-
- if(callsign(1:3).eq.'CQ ') then
- ncall=NBASE + 1
- if(callsign(4:4).ge.'0' .and. callsign(4:4).le.'9' .and. &
- callsign(5:5).ge.'0' .and. callsign(5:5).le.'9' .and. &
- callsign(6:6).ge.'0' .and. callsign(6:6).le.'9') then
- read(callsign(4:6),*) nfreq
- ncall=NBASE + 3 + nfreq
- endif
- return
- else if(callsign(1:4).eq.'QRZ ') then
- ncall=NBASE + 2
- return
- else if(callsign(1:3).eq.'DE ') then
- ncall=267796945
- return
- endif
-
- tmp=' '
- if(callsign(3:3).ge.'0' .and. callsign(3:3).le.'9') then
- tmp=callsign
- else if(callsign(2:2).ge.'0' .and. callsign(2:2).le.'9') then
- if(callsign(6:6).ne.' ') then
- text=.true.
- return
- endif
- tmp=' '//callsign(:5)
- else
- text=.true.
- return
- endif
-
- do i=1,6
- c=tmp(i:i)
- if(c.ge.'a' .and. c.le.'z') &
- tmp(i:i)=char(ichar(c)-ichar('a')+ichar('A'))
- enddo
-
- n1=0
- if((tmp(1:1).ge.'A'.and.tmp(1:1).le.'Z').or.tmp(1:1).eq.' ') n1=1
- if(tmp(1:1).ge.'0' .and. tmp(1:1).le.'9') n1=1
- n2=0
- if(tmp(2:2).ge.'A' .and. tmp(2:2).le.'Z') n2=1
- if(tmp(2:2).ge.'0' .and. tmp(2:2).le.'9') n2=1
- n3=0
- if(tmp(3:3).ge.'0' .and. tmp(3:3).le.'9') n3=1
- n4=0
- if((tmp(4:4).ge.'A'.and.tmp(4:4).le.'Z').or.tmp(4:4).eq.' ') n4=1
- n5=0
- if((tmp(5:5).ge.'A'.and.tmp(5:5).le.'Z').or.tmp(5:5).eq.' ') n5=1
- n6=0
- if((tmp(6:6).ge.'A'.and.tmp(6:6).le.'Z').or.tmp(6:6).eq.' ') n6=1
-
- if(n1+n2+n3+n4+n5+n6 .ne. 6) then
- text=.true.
- return
- endif
-
- ncall=nchar(tmp(1:1))
- ncall=36*ncall+nchar(tmp(2:2))
- ncall=10*ncall+nchar(tmp(3:3))
- ncall=27*ncall+nchar(tmp(4:4))-10
- ncall=27*ncall+nchar(tmp(5:5))-10
- ncall=27*ncall+nchar(tmp(6:6))-10
-
- return
-end subroutine packcall
-
-subroutine unpackcall(ncall,word,iv2,psfx)
-
- parameter (NBASE=37*36*10*27*27*27)
- character word*12,c*37,psfx*4
-
- data c/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ '/
-
- word='......'
- psfx=' '
- n=ncall
- iv2=0
- if(n.ge.262177560) go to 20
- word='......'
-! if(n.ge.262177560) go to 999 !Plain text message ...
- i=mod(n,27)+11
- word(6:6)=c(i:i)
- n=n/27
- i=mod(n,27)+11
- word(5:5)=c(i:i)
- n=n/27
- i=mod(n,27)+11
- word(4:4)=c(i:i)
- n=n/27
- i=mod(n,10)+1
- word(3:3)=c(i:i)
- n=n/10
- i=mod(n,36)+1
- word(2:2)=c(i:i)
- n=n/36
- i=n+1
- word(1:1)=c(i:i)
- do i=1,4
- if(word(i:i).ne.' ') go to 10
- enddo
- go to 999
-10 word=word(i:)
- go to 999
-
-20 if(n.ge.267796946) go to 999
-
-! We have a JT65v2 message
- if((n.ge.262178563) .and. (n.le.264002071)) then
-! CQ with prefix
- iv2=1
- n=n-262178563
- i=mod(n,37)+1
- psfx(4:4)=c(i:i)
- n=n/37
- i=mod(n,37)+1
- psfx(3:3)=c(i:i)
- n=n/37
- i=mod(n,37)+1
- psfx(2:2)=c(i:i)
- n=n/37
- i=n+1
- psfx(1:1)=c(i:i)
-
- else if((n.ge.264002072) .and. (n.le.265825580)) then
-! QRZ with prefix
- iv2=2
- n=n-264002072
- i=mod(n,37)+1
- psfx(4:4)=c(i:i)
- n=n/37
- i=mod(n,37)+1
- psfx(3:3)=c(i:i)
- n=n/37
- i=mod(n,37)+1
- psfx(2:2)=c(i:i)
- n=n/37
- i=n+1
- psfx(1:1)=c(i:i)
-
- else if((n.ge.265825581) .and. (n.le.267649089)) then
-! DE with prefix
- iv2=3
- n=n-265825581
- i=mod(n,37)+1
- psfx(4:4)=c(i:i)
- n=n/37
- i=mod(n,37)+1
- psfx(3:3)=c(i:i)
- n=n/37
- i=mod(n,37)+1
- psfx(2:2)=c(i:i)
- n=n/37
- i=n+1
- psfx(1:1)=c(i:i)
-
- else if((n.ge.267649090) .and. (n.le.267698374)) then
-! CQ with suffix
- iv2=4
- n=n-267649090
- i=mod(n,37)+1
- psfx(3:3)=c(i:i)
- n=n/37
- i=mod(n,37)+1
- psfx(2:2)=c(i:i)
- n=n/37
- i=n+1
- psfx(1:1)=c(i:i)
-
- else if((n.ge.267698375) .and. (n.le.267747659)) then
-! QRZ with suffix
- iv2=5
- n=n-267698375
- i=mod(n,37)+1
- psfx(3:3)=c(i:i)
- n=n/37
- i=mod(n,37)+1
- psfx(2:2)=c(i:i)
- n=n/37
- i=n+1
- psfx(1:1)=c(i:i)
-
- else if((n.ge.267747660) .and. (n.le.267796944)) then
-! DE with suffix
- iv2=6
- n=n-267747660
- i=mod(n,37)+1
- psfx(3:3)=c(i:i)
- n=n/37
- i=mod(n,37)+1
- psfx(2:2)=c(i:i)
- n=n/37
- i=n+1
- psfx(1:1)=c(i:i)
-
- else if(n.eq.267796945) then
-! DE with no prefix or suffix
- iv2=7
- psfx = ' '
- endif
-
-999 if(word(1:3).eq.'3D0') word='3DA0'//word(4:)
-
- return
-end subroutine unpackcall
-
-subroutine packgrid(grid,ng,text)
-
- parameter (NGBASE=180*180)
- character*4 grid
- character*1 c1
- logical text
-
- text=.false.
- if(grid.eq.' ') go to 90 !Blank grid is OK
-
-! First, handle signal reports in the original range, -01 to -30 dB
- if(grid(1:1).eq.'-') then
- read(grid(2:3),*,err=800,end=800) n
- if(n.ge.1 .and. n.le.30) then
- ng=NGBASE+1+n
- go to 900
- endif
- go to 10
- else if(grid(1:2).eq.'R-') then
- read(grid(3:4),*,err=800,end=800) n
- if(n.ge.1 .and. n.le.30) then
- ng=NGBASE+31+n
- go to 900
- endif
- go to 10
-! Now check for RO, RRR, or 73 in the message field normally used for grid
- else if(grid(1:4).eq.'RO ') then
- ng=NGBASE+62
- go to 900
- else if(grid(1:4).eq.'RRR ') then
- ng=NGBASE+63
- go to 900
- else if(grid(1:4).eq.'73 ') then
- ng=NGBASE+64
- go to 900
- endif
-
-! Now check for extended-range signal reports: -50 to -31, and 0 to +49.
-10 n=99
- c1=grid(1:1)
- read(grid,*,err=20,end=20) n
- go to 30
-20 read(grid(2:4),*,err=30,end=30) n
-30 if(n.ge.-50 .and. n.le.49) then
- if(c1.eq.'R') then
- write(grid,1002) n+50
-1002 format('LA',i2.2)
+ i1=index(call1,'/')
+ if(call1(i1+2:i1+2).eq.' ') then
+! Single-character add-on suffix (maybe also fourth suffix letter?)
+ call0=call1(:i1-1)
+ call packcall(call0,n1,text)
+ nadd=1
+ nc=ichar(call1(i1+1:i1+1))
+ if(nc.ge.48 .and. nc.le.57) then
+ n=nc-48
+ else if(nc.ge.65 .and. nc.le.90) then
+ n=nc-65+10
else
- write(grid,1003) n+50
-1003 format('KA',i2.2)
+ n=38
endif
- go to 40
- endif
-
-! Maybe it's free text ?
- if(grid(1:1).lt.'A' .or. grid(1:1).gt.'R') text=.true.
- if(grid(2:2).lt.'A' .or. grid(2:2).gt.'R') text=.true.
- if(grid(3:3).lt.'0' .or. grid(3:3).gt.'9') text=.true.
- if(grid(4:4).lt.'0' .or. grid(4:4).gt.'9') text=.true.
- if(text) go to 900
-
-! OK, we have a properly formatted grid locator
-40 call grid2deg(grid//'mm',dlong,dlat)
- long=int(dlong)
- lat=int(dlat+ 90.0)
- ng=((long+180)/2)*180 + lat
- go to 900
-
-90 ng=NGBASE + 1
- go to 900
-
-800 text=.true.
-900 continue
-
- return
-end subroutine packgrid
-
-subroutine unpackgrid(ng,grid)
-
- parameter (NGBASE=180*180)
- character grid*4,grid6*6
-
- grid=' '
- if(ng.ge.32400) go to 10
- dlat=mod(ng,180)-90
- dlong=(ng/180)*2 - 180 + 2
- call deg2grid(dlong,dlat,grid6)
- grid=grid6(:4)
- if(grid(1:2).eq.'KA') then
- read(grid(3:4),*) n
- n=n-50
- write(grid,1001) n
-1001 format(i3.2)
- if(grid(1:1).eq.' ') grid(1:1)='+'
- else if(grid(1:2).eq.'LA') then
- read(grid(3:4),*) n
- n=n-50
- write(grid,1002) n
-1002 format('R',i3.2)
- if(grid(2:2).eq.' ') grid(2:2)='+'
- endif
- go to 900
-
-10 n=ng-NGBASE-1
- if(n.ge.1 .and.n.le.30) then
- write(grid,1012) -n
-1012 format(i3.2)
- else if(n.ge.31 .and.n.le.60) then
- n=n-30
- write(grid,1022) -n
-1022 format('R',i3.2)
- else if(n.eq.61) then
- grid='RO'
- else if(n.eq.62) then
- grid='RRR'
- else if(n.eq.63) then
- grid='73'
- endif
-
-900 return
-end subroutine unpackgrid
-
-subroutine packmsg(msg,dat,itype)
-
-! Packs a JT4/JT9/JT65 message into twelve 6-bit symbols
-
-! itype Message Type
-!--------------------
-! 1 Standardd message
-! 2 Type 1 prefix
-! 3 Type 1 suffix
-! 4 Type 2 prefix
-! 5 Type 2 suffix
-! 6 Free text
-! -1 Does not decode correctly
-
- parameter (NBASE=37*36*10*27*27*27)
- parameter (NBASE2=262178562)
- character*22 msg
- integer dat(12)
- character*12 c1,c2
- character*4 c3
- character*6 grid6
- logical text1,text2,text3
-
- itype=1
- call fmtmsg(msg,iz)
-
- if(msg(1:6).eq.'CQ DX ') msg(3:3)='9'
-
-! See if it's a CQ message
- if(msg(1:3).eq.'CQ ') then
- i=3
-! ... and if so, does it have a reply frequency?
- if(msg(4:4).ge.'0' .and. msg(4:4).le.'9' .and. &
- msg(5:5).ge.'0' .and. msg(5:5).le.'9' .and. &
- msg(6:6).ge.'0' .and. msg(6:6).le.'9') i=7
- go to 1
- endif
-
- do i=1,22
- if(msg(i:i).eq.' ') go to 1 !Get 1st blank
- enddo
- go to 10 !Consider msg as plain text
-
-1 ia=i
- c1=msg(1:ia-1)
- do i=ia+1,22
- if(msg(i:i).eq.' ') go to 2 !Get 2nd blank
- enddo
- go to 10 !Consider msg as plain text
-
-2 ib=i
- c2=msg(ia+1:ib-1)
-
- do i=ib+1,22
- if(msg(i:i).eq.' ') go to 3 !Get 3rd blank
- enddo
- go to 10 !Consider msg as plain text
-
-3 ic=i
- c3=' '
- if(ic.ge.ib+1) c3=msg(ib+1:ic)
- if(c3.eq.'OOO ') c3=' ' !Strip out the OOO flag
- call getpfx1(c1,k1,nv2a)
- if(nv2a.ge.4) go to 10
- call packcall(c1,nc1,text1)
- if(text1) go to 10
- call getpfx1(c2,k2,nv2b)
- call packcall(c2,nc2,text2)
- if(text2) go to 10
- if(nv2a.eq.2 .or. nv2a.eq.3 .or. nv2b.eq.2 .or. nv2b.eq.3) then
- if(k1.lt.0 .or. k2.lt.0 .or. k1*k2.ne.0) go to 10
- if(k2.gt.0) k2=k2+450
- k=max(k1,k2)
- if(k.gt.0) then
- call k2grid(k,grid6)
- c3=grid6(:4)
- endif
- endif
- call packgrid(c3,ng,text3)
-
- if(nv2a.lt.4 .and. nv2b.lt.4 .and. (.not.text1) .and. (.not.text2) .and. &
- (.not.text3)) go to 20
-
- nc1=0
- if(nv2b.eq.4) then
- if(c1(1:3).eq.'CQ ') nc1=262178563 + k2
- if(c1(1:4).eq.'QRZ ') nc1=264002072 + k2
- if(c1(1:3).eq.'DE ') nc1=265825581 + k2
- else if(nv2b.eq.5) then
- if(c1(1:3).eq.'CQ ') nc1=267649090 + k2
- if(c1(1:4).eq.'QRZ ') nc1=267698375 + k2
- if(c1(1:3).eq.'DE ') nc1=267747660 + k2
- endif
- if(nc1.ne.0) go to 20
-
-! The message will be treated as plain text.
-10 itype=6
- call packtext(msg,nc1,nc2,ng)
- ng=ng+32768
-
-! Encode data into 6-bit words
-20 continue
- if(itype.ne.6) itype=max(nv2a,nv2b)
- dat(1)=iand(ishft(nc1,-22),63) !6 bits
- dat(2)=iand(ishft(nc1,-16),63) !6 bits
- dat(3)=iand(ishft(nc1,-10),63) !6 bits
- dat(4)=iand(ishft(nc1, -4),63) !6 bits
- dat(5)=4*iand(nc1,15)+iand(ishft(nc2,-26),3) !4+2 bits
- dat(6)=iand(ishft(nc2,-20),63) !6 bits
- dat(7)=iand(ishft(nc2,-14),63) !6 bits
- dat(8)=iand(ishft(nc2, -8),63) !6 bits
- dat(9)=iand(ishft(nc2, -2),63) !6 bits
- dat(10)=16*iand(nc2,3)+iand(ishft(ng,-12),15) !2+4 bits
- dat(11)=iand(ishft(ng,-6),63)
- dat(12)=iand(ng,63)
-
- return
-end subroutine packmsg
-
-subroutine unpackmsg(dat,msg)
-
- parameter (NBASE=37*36*10*27*27*27)
- parameter (NGBASE=180*180)
- integer dat(12)
- character c1*12,c2*12,grid*4,msg*22,grid6*6,psfx*4,junk2*4
- logical cqnnn
-
- cqnnn=.false.
- nc1=ishft(dat(1),22) + ishft(dat(2),16) + ishft(dat(3),10)+ &
- ishft(dat(4),4) + iand(ishft(dat(5),-2),15)
-
- nc2=ishft(iand(dat(5),3),26) + ishft(dat(6),20) + &
- ishft(dat(7),14) + ishft(dat(8),8) + ishft(dat(9),2) + &
- iand(ishft(dat(10),-4),3)
-
- ng=ishft(iand(dat(10),15),12) + ishft(dat(11),6) + dat(12)
-
- if(ng.ge.32768) then
- call unpacktext(nc1,nc2,ng,msg)
- go to 100
- endif
-
- call unpackcall(nc1,c1,iv2,psfx)
- if(iv2.eq.0) then
-! This is an "original JT65" message
- if(nc1.eq.NBASE+1) c1='CQ '
- if(nc1.eq.NBASE+2) c1='QRZ '
- nfreq=nc1-NBASE-3
- if(nfreq.ge.0 .and. nfreq.le.999) then
- write(c1,1002) nfreq
-1002 format('CQ ',i3.3)
- cqnnn=.true.
- endif
- endif
-
- call unpackcall(nc2,c2,junk1,junk2)
- call unpackgrid(ng,grid)
-
- if(iv2.gt.0) then
-! This is a JT65v2 message
- do i=1,4
- if(ichar(psfx(i:i)).eq.0) psfx(i:i)=' '
- enddo
-
- n1=len_trim(psfx)
- n2=len_trim(c2)
- if(iv2.eq.1) msg='CQ '//psfx(:n1)//'/'//c2(:n2)//' '//grid
- if(iv2.eq.2) msg='QRZ '//psfx(:n1)//'/'//c2(:n2)//' '//grid
- if(iv2.eq.3) msg='DE '//psfx(:n1)//'/'//c2(:n2)//' '//grid
- if(iv2.eq.4) msg='CQ '//c2(:n2)//'/'//psfx(:n1)//' '//grid
- if(iv2.eq.5) msg='QRZ '//c2(:n2)//'/'//psfx(:n1)//' '//grid
- if(iv2.eq.6) msg='DE '//c2(:n2)//'/'//psfx(:n1)//' '//grid
- if(iv2.eq.7) msg='DE '//c2(:n2)//' '//grid
- if(iv2.eq.8) msg=' '
- go to 100
+ nadd=1
+ ng=60000-32768+n
+ else if(call1(i1+3:i1+3).eq.' ') then
+! Two-character numerical suffix, /10 to /99
+ call0=call1(:i1-1)
+ call packcall(call0,n1,text)
+ nadd=1
+ n=10*(ichar(call1(i1+1:i1+1))-48) + ichar(call1(i1+2:i1+2)) - 48
+ nadd=1
+ ng=60000 + 26 + n
else
-
- endif
+! Prefix of 1 to 3 characters
+ pfx=call1(:i1-1)
+ if(pfx(3:3).eq.' ') pfx=' '//pfx(1:2)
+ if(pfx(3:3).eq.' ') pfx=' '//pfx(1:2)
+ call0=call1(i1+1:)
+ call packcall(call0,n1,text)
- grid6=grid//'ma'
- call grid2k(grid6,k)
- if(k.ge.1 .and. k.le.450) call getpfx2(k,c1)
- if(k.ge.451 .and. k.le.900) call getpfx2(k,c2)
-
- i=index(c1,char(0))
- if(i.ge.3) c1=c1(1:i-1)//' '
- i=index(c2,char(0))
- if(i.ge.3) c2=c2(1:i-1)//' '
-
- msg=' '
- j=0
- if(cqnnn) then
- msg=c1//' '
- j=7 !### ??? ###
- go to 10
- endif
-
- do i=1,12
- j=j+1
- msg(j:j)=c1(i:i)
- if(c1(i:i).eq.' ') go to 10
- enddo
- j=j+1
- msg(j:j)=' '
-
-10 do i=1,12
- if(j.le.21) j=j+1
- msg(j:j)=c2(i:i)
- if(c2(i:i).eq.' ') go to 20
- enddo
- if(j.le.21) j=j+1
- msg(j:j)=' '
-
-20 if(k.eq.0) then
- do i=1,4
- if(j.le.21) j=j+1
- msg(j:j)=grid(i:i)
- enddo
- if(j.le.21) j=j+1
- msg(j:j)=' '
- endif
-
-100 continue
- if(msg(1:6).eq.'CQ9DX ') msg(3:3)=' '
-
- return
-end subroutine unpackmsg
-
-subroutine packtext(msg,nc1,nc2,nc3)
-
- parameter (MASK28=2**28 - 1)
- character*13 msg
- character*42 c
- data c/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ +-./?'/
-
- nc1=0
- nc2=0
- nc3=0
-
- do i=1,5 !First 5 characters in nc1
- do j=1,42 !Get character code
- if(msg(i:i).eq.c(j:j)) go to 10
- enddo
- j=37
-10 j=j-1 !Codes should start at zero
- nc1=42*nc1 + j
- enddo
-
- do i=6,10 !Characters 6-10 in nc2
- do j=1,42 !Get character code
- if(msg(i:i).eq.c(j:j)) go to 20
- enddo
- j=37
-20 j=j-1 !Codes should start at zero
- nc2=42*nc2 + j
- enddo
-
- do i=11,13 !Characters 11-13 in nc3
- do j=1,42 !Get character code
- if(msg(i:i).eq.c(j:j)) go to 30
- enddo
- j=37
-30 j=j-1 !Codes should start at zero
- nc3=42*nc3 + j
- enddo
-
-! We now have used 17 bits in nc3. Must move one each to nc1 and nc2.
- nc1=nc1+nc1
- if(iand(nc3,32768).ne.0) nc1=nc1+1
- nc2=nc2+nc2
- if(iand(nc3,65536).ne.0) nc2=nc2+1
- nc3=iand(nc3,32767)
-
- return
-end subroutine packtext
-
-subroutine unpacktext(nc1,nc2,nc3,msg)
-
- character*22 msg
- character*44 c
- data c/'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ +-./?'/
-
- nc3=iand(nc3,32767) !Remove the "plain text" bit
- if(iand(nc1,1).ne.0) nc3=nc3+32768
- nc1=nc1/2
- if(iand(nc2,1).ne.0) nc3=nc3+65536
- nc2=nc2/2
-
- do i=5,1,-1
- j=mod(nc1,42)+1
- msg(i:i)=c(j:j)
- nc1=nc1/42
- enddo
-
- do i=10,6,-1
- j=mod(nc2,42)+1
- msg(i:i)=c(j:j)
- nc2=nc2/42
- enddo
-
- do i=13,11,-1
- j=mod(nc3,42)+1
- msg(i:i)=c(j:j)
- nc3=nc3/42
- enddo
- msg(14:22) = ' '
-
- return
-end subroutine unpacktext
-
-subroutine getpfx1(callsign,k,nv2)
-
- character*12 callsign0,callsign,lof,rof
- character*8 c
- character addpfx*8,tpfx*4,tsfx*3
- logical ispfx,issfx,invalid
- common/pfxcom/addpfx
- include 'pfx.f90'
-
- callsign0=callsign
- nv2=1
- iz=index(callsign,' ') - 1
- if(iz.lt.0) iz=12
- islash=index(callsign(1:iz),'/')
- k=0
-! if(k.eq.0) go to 10 !Tnx to DL9RDZ for reminder:this was for tests only!
- c=' '
- if(islash.gt.0 .and. islash.le.(iz-4)) then
-! Add-on prefix
- c=callsign(1:islash-1)
- callsign=callsign(islash+1:iz)
- do i=1,NZ
- if(pfx(i)(1:4).eq.c) then
- k=i
- nv2=2
- go to 10
+ ng=0
+ do i=1,3
+ nc=ichar(pfx(i:i))
+ if(nc.ge.48 .and. nc.le.57) then
+ n=nc-48
+ else if(nc.ge.65 .and. nc.le.90) then
+ n=nc-65+10
+ else
+ n=36
endif
+ ng=37*ng + n
enddo
- if(addpfx.eq.c) then
- k=449
- nv2=2
- go to 10
- endif
-
- else if(islash.eq.(iz-1)) then
-! Add-on suffix
- c=callsign(islash+1:iz)
- callsign=callsign(1:islash-1)
- do i=1,NZ2
- if(sfx(i).eq.c(1:1)) then
- k=400+i
- nv2=3
- go to 10
- endif
- enddo
- endif
-
-10 if(islash.ne.0 .and.k.eq.0) then
-! Original JT65 would force this compound callsign to be treated as
-! plain text. In JT65v2, we will encode the prefix or suffix into nc1.
-! The task here is to compute the proper value of k.
- lof=callsign0(:islash-1)
- rof=callsign0(islash+1:)
- llof=len_trim(lof)
- lrof=len_trim(rof)
- ispfx=(llof.gt.0 .and. llof.le.4)
- issfx=(lrof.gt.0 .and. lrof.le.3)
- invalid=.not.(ispfx.or.issfx)
- if(ispfx.and.issfx) then
- if(llof.lt.3) issfx=.false.
- if(lrof.lt.3) ispfx=.false.
- if(ispfx.and.issfx) then
- i=ichar(callsign0(islash-1:islash-1))
- if(i.ge.ichar('0') .and. i.le.ichar('9')) then
- issfx=.false.
- else
- ispfx=.false.
- endif
- endif
- endif
-
- if(invalid) then
- k=-1
- else
- if(ispfx) then
- tpfx=lof(1:4)
- k=nchar(tpfx(1:1))
- k=37*k + nchar(tpfx(2:2))
- k=37*k + nchar(tpfx(3:3))
- k=37*k + nchar(tpfx(4:4))
- nv2=4
- i=index(callsign0,'/')
- callsign=callsign0(:i-1)
- callsign=callsign0(i+1:)
- endif
- if(issfx) then
- tsfx=rof(1:3)
- k=nchar(tsfx(1:1))
- k=37*k + nchar(tsfx(2:2))
- k=37*k + nchar(tsfx(3:3))
- nv2=5
- i=index(callsign0,'/')
- callsign=callsign0(:i-1)
- endif
+ nadd=0
+ if(ng.ge.32768) then
+ ng=ng-32768
+ nadd=1
endif
endif
return
-end subroutine getpfx1
-
-subroutine getpfx2(k0,callsign)
-
- character callsign*12
- include 'pfx.f90'
- character addpfx*8
- common/pfxcom/addpfx
-
- k=k0
- if(k.gt.450) k=k-450
- if(k.ge.1 .and. k.le.NZ) then
- iz=index(pfx(k),' ') - 1
- callsign=pfx(k)(1:iz)//'/'//callsign
- else if(k.ge.401 .and. k.le.400+NZ2) then
- iz=index(callsign,' ') - 1
- callsign=callsign(1:iz)//'/'//sfx(k-400)
- else if(k.eq.449) then
- iz=index(addpfx,' ') - 1
- if(iz.lt.1) iz=8
- callsign=addpfx(1:iz)//'/'//callsign
- endif
-
- return
-end subroutine getpfx2
-
-subroutine grid2k(grid,k)
-
- character*6 grid
-
- call grid2deg(grid,xlong,xlat)
- nlong=nint(xlong)
- nlat=nint(xlat)
- k=0
- if(nlat.ge.85) k=5*(nlong+179)/2 + nlat-84
-
- return
-end subroutine grid2k
-
-subroutine k2grid(k,grid)
- character grid*6
-
- nlong=2*mod((k-1)/5,90)-179
- if(k.gt.450) nlong=nlong+180
- nlat=mod(k-1,5)+ 85
- dlat=nlat
- dlong=nlong
- call deg2grid(dlong,dlat,grid)
-
- return
-end subroutine k2grid
-
-subroutine grid2n(grid,n)
- character*4 grid
-
- i1=ichar(grid(1:1))-ichar('A')
- i2=ichar(grid(3:3))-ichar('0')
- i=10*i1 + i2
- n=-i - 31
-
- return
-end subroutine grid2n
-
-subroutine n2grid(n,grid)
- character*4 grid
-
- if(n.gt.-31 .or. n.lt.-70) stop 'Error in n2grid'
- i=-(n+31) !NB: 0 <= i <= 39
- i1=i/10
- i2=mod(i,10)
- grid(1:1)=char(ichar('A')+i1)
- grid(2:2)='A'
- grid(3:3)=char(ichar('0')+i2)
- grid(4:4)='0'
-
- return
-end subroutine n2grid
-
-function nchar(c)
-
-! Convert ascii number, letter, or space to 0-36 for callsign packing.
-
- character c*1
-
- n=0 !Silence compiler warning
- if(c.ge.'0' .and. c.le.'9') then
- n=ichar(c)-ichar('0')
- else if(c.ge.'A' .and. c.le.'Z') then
- n=ichar(c)-ichar('A') + 10
- else if(c.ge.'a' .and. c.le.'z') then
- n=ichar(c)-ichar('a') + 10
- else if(c.ge.' ') then
- n=36
- else
- Print*,'Invalid character in callsign ',c,' ',ichar(c)
- stop
- endif
- nchar=n
-
- return
-end function nchar
+end subroutine packpfx
end module packjt
diff --git a/lib/savec2.f90 b/lib/savec2.f90
new file mode 100644
index 000000000..55b4ff476
--- /dev/null
+++ b/lib/savec2.f90
@@ -0,0 +1,54 @@
+subroutine savec2(c2name,ntrseconds,f0m1500)
+
+! Array c0() has complex samples at 1500 Hz sample rate.
+! WSPR-2: downsample by 1/4 to produce c2, centered at 1500 Hz
+! WSPR-15: downsample by 1/32 to produce c2, centered at 1612.5 Hz
+
+ parameter (NDMAX=120*1500) !Sample intervals at 1500 Hz rate
+ parameter (MAXFFT=256*1024)
+
+ character*(*) c2name
+ character*14 outfile
+ real*8 f0m1500
+ complex c0
+ complex c1(0:MAXFFT-1)
+ complex c2(0:65535)
+ common/c0com/c0(0:NDMAX-1)
+
+ ntrminutes=ntrseconds/60
+ npts=114*1500
+ nfft1=262144
+ if(ntrminutes.eq.15) then
+ npts=890*1500
+ nfft1=MAXFFT
+ endif
+ df1=1500.0/nfft1
+ fac=1.0/nfft1
+ c1(0:npts-1)=fac*c0(0:npts-1)
+ c1(npts:nfft1-1)=0.
+
+ call four2a(c1,nfft1,1,1,1) !Complex FFT to frequency domain
+
+! Select the desired frequency range
+ nfft2=65536
+ nh2=nfft2/2
+ if(ntrminutes.eq.2) then
+ c2(0:nh2)=c1(0:nh2)
+ c2(nh2+1:nfft2-1)=c1(nfft1-nh2+1:nfft1-1)
+ else
+ i0=nint(112.5/df1)
+ c2(0:nh2)=c1(i0:i0+nh2)
+ c2(nh2+1:nfft2-1)=c1(i0-nh2+1:i0-1)
+ endif
+
+ call four2a(c2,nfft2,1,-1,1) !Shorter complex FFT, back to time domain
+
+! Write complex time-domain data to disk.
+ i1=index(c2name,'.c2')
+ outfile=c2name(i1-11:i1+2)
+ open(18,file=c2name,status='unknown',access='stream')
+ write(18) outfile,ntrminutes,f0m1500,c2(0:45000-1)
+ close(18)
+
+ return
+end subroutine savec2
diff --git a/lib/smo.f90 b/lib/smo.f90
index 94a337848..c42de7c60 100644
--- a/lib/smo.f90
+++ b/lib/smo.f90
@@ -11,13 +11,9 @@ subroutine smo(x,npts,y,nadd)
enddo
y(i)=sum
enddo
- y(:nh)=0.
- y(npts-nh+1:)=0.
-
- fac=1.0/nadd
- do i=1,npts
- x(i)=fac*y(i)
- enddo
+ x=y
+ x(:nh)=0.
+ x(npts-nh+1:)=0.
return
end subroutine smo
diff --git a/lib/symspec.f90 b/lib/symspec.f90
index 1b342a202..b00ef6ae1 100644
--- a/lib/symspec.f90
+++ b/lib/symspec.f90
@@ -1,4 +1,4 @@
-subroutine symspec(k,ntrperiod,nsps,ingain,pxdb,s,df3,ihsym,npts8)
+subroutine symspec(k,ntrperiod,nsps,ingain,nminw,pxdb,s,df3,ihsym,npts8)
! Input:
! k pointer to the most recent new data
@@ -11,7 +11,7 @@ subroutine symspec(k,ntrperiod,nsps,ingain,pxdb,s,df3,ihsym,npts8)
! Output:
! pxdb power (0-60 dB)
! s() current spectrum for waterfall display
-! ihsym index number of this half-symbol (1-184)
+! ihsym index number of this half-symbol (1-184) for 60 s modes
! jt9com
! ss() JT9 symbol spectra at half-symbol steps
@@ -25,15 +25,18 @@ subroutine symspec(k,ntrperiod,nsps,ingain,pxdb,s,df3,ihsym,npts8)
real*4 tmp(NSMAX)
complex cx(0:MAXFFT3/2)
integer*2 id2
+ integer nch(7)
character datetime*20,mycall*12,mygrid*6,hiscall*12,hisgrid*6
common/jt9com/ss(184,NSMAX),savg(NSMAX),id2(NMAX),nutc,ndiskdat, &
ntr,mousefqso,newdat,npts8a,nfa,nfsplit,nfb,ntol,kin,nzhsym, &
- nsubmode,nagain,ndepth,ntxmode,nmode,minw,nclearave,emedelay, &
- dttol,nlist,listutc(10),datetime,mycall,mygrid,hiscall,hisgrid
+ nsubmode,nagain,ndepth,ntxmode,nmode,minw,nclearave,minsync, &
+ emedelay,dttol,nlist,listutc(10),datetime,mycall,mygrid, &
+ hiscall,hisgrid
common/jt9w/syellow(NSMAX)
data rms/999.0/,k0/99999999/,nfft3z/0/
+ data nch/1,2,4,9,18,36,72/
equivalence (xc,cx)
save
@@ -84,34 +87,33 @@ subroutine symspec(k,ntrperiod,nsps,ingain,pxdb,s,df3,ihsym,npts8)
xc(i)=0.
if(j.ge.1 .and.j.le.NMAX) xc(i)=fac0*id2(j)
enddo
- if(ihsym.lt.184) ihsym=ihsym+1
+ ihsym=ihsym+1
xc(0:nfft3-1)=w3(1:nfft3)*xc(0:nfft3-1) !Apply window w3
call four2a(xc,nfft3,1,-1,0) !Real-to-complex FFT
- n=min(184,ihsym)
df3=12000.0/nfft3 !JT9-1: 0.732 Hz = 0.42 * tone spacing
-! i0=nint(1000.0/df3)
- i0=0
iz=min(NSMAX,nint(5000.0/df3))
fac=(1.0/nfft3)**2
do i=1,iz
- j=i0+i-1
+ j=i-1
if(j.lt.0) j=j+nfft3
sx=fac*(real(cx(j))**2 + aimag(cx(j))**2)
- ss(n,i)=sx
+ if(ihsym.le.184) ss(ihsym,i)=sx
ssum(i)=ssum(i) + sx
s(i)=1000.0*gain*sx
enddo
savg=ssum/ihsym
- if(mod(n,10).eq.0) then
- mode4=36
+ if(mod(ihsym,10).eq.0) then
+ mode4=nch(nminw+1)
nsmo=min(10*mode4,150)
nsmo=4*nsmo
call flat1(savg,iz,nsmo,syellow)
- if(mode4.ge.9) call smo(syellow,iz,tmp,mode4)
+ if(mode4.ge.2) call smo(syellow,iz,tmp,mode4)
+ if(mode4.ge.2) call smo(syellow,iz,tmp,mode4)
+ syellow(1:250)=0.
ia=500./df3
ib=2700.0/df3
smin=minval(syellow(ia:ib))
diff --git a/lib/sync4.f90 b/lib/sync4.f90
index 38623e072..68de1a345 100644
--- a/lib/sync4.f90
+++ b/lib/sync4.f90
@@ -9,7 +9,6 @@ subroutine sync4(dat,jz,mode4,minw)
real dat(jz)
real psavg(NHMAX) !Average spectrum of whole record
real s2(NHMAX,NSMAX) !2d spectrum, stepped by half-symbols
- real ccfblue(65) !CCF with pseudorandom sequence
real tmp(1260)
save
diff --git a/lib/timf2.f90 b/lib/timf2.f90
new file mode 100644
index 000000000..07f509831
--- /dev/null
+++ b/lib/timf2.f90
@@ -0,0 +1,142 @@
+subroutine timf2(x0,k,nfft,nwindow,nb,peaklimit,x1, &
+ slimit,lstrong,px,nzap)
+
+! Sequential processing of time-domain I/Q data, using Linrad-like
+! "first FFT" and "first backward FFT", treating frequencies with
+! strong signals differently. Noise blanking is applied to weak
+! signals only.
+
+! x0 - real input data
+! nfft - length of FFTs
+! nwindow - 0 for no window, 2 for sin^2 window
+! x1 - real output data
+
+! Non-windowed processing means no overlap, so kstep=nfft.
+! Sin^2 window has 50% overlap, kstep=nfft/2.
+
+! Frequencies with strong signals are identified and separated. Back
+! transforms are done separately for weak and strong signals, so that
+! noise blanking can be applied to the weak-signal portion. Strong and
+! weak are finally re-combined, in the time domain.
+
+ parameter (MAXFFT=1024,MAXNH=MAXFFT/2)
+ parameter (MAXSIGS=100)
+ real x0(0:nfft-1),x1(0:nfft-1)
+ real x(0:MAXFFT-1),xw(0:MAXFFT-1),xs(0:MAXFFT-1)
+ real xwov(0:MAXNH-1),xsov(0:MAXNH-1)
+ complex cx(0:MAXFFT-1),cxt(0:MAXFFT-1)
+ complex cxs(0:MAXFFT-1) !Strong signals
+ complex cxw(0:MAXFFT-1) !Weak signals
+ real*4 w(0:MAXFFT-1)
+ real*4 s(0:MAXNH)
+ logical*1 lstrong(0:MAXNH),lprev
+ integer ia(MAXSIGS),ib(MAXSIGS)
+ logical first
+ equivalence (x,cx),(xw,cxw),(xs,cxs)
+ data first/.true./
+ data k0/99999999/
+ save
+
+ if(first) then
+ pi=4.0*atan(1.0)
+ do i=0,nfft-1
+ w(i)=(sin(i*pi/nfft))**2
+ enddo
+ s=0.
+ nh=nfft/2
+ kstep=nfft
+ if(nwindow.eq.2) kstep=nh
+ fac=1.0/nfft
+ slimit=1.e30
+ first=.false.
+ endif
+
+ if(k.lt.k0) then
+ xsov=0.
+ xwov=0.
+ endif
+ k0=k
+
+ x(0:nfft-1)=x0
+ if(nwindow.eq.2) x(0:nfft-1)=w(0:nfft-1)*x(0:nfft-1)
+ call four2a(x,nfft,1,-1,0) !First forward FFT, r2c
+ cxt(0:nh)=cx(0:nh)
+
+! Identify frequencies with strong signals.
+ do i=0,nh
+ p=real(cxt(i))**2 + aimag(cxt(i))**2
+ s(i)=p
+ enddo
+ ave=sum(s(0:nh))/nh
+ lstrong(0:nh)=s(0:nh).gt.10.0*ave
+
+ nsigs=0
+ lprev=.false.
+ iwid=1
+ ib=-99
+ do i=0,nh
+ if(lstrong(i) .and. (.not.lprev)) then
+ if(nsigs.lt.MAXSIGS) nsigs=nsigs+1
+ ia(nsigs)=i-iwid
+ if(ia(nsigs).lt.0) ia(nsigs)=0
+ endif
+ if(.not.lstrong(i) .and. lprev) then
+ ib(nsigs)=i-1+iwid
+ if(ib(nsigs).gt.nh) ib(nsigs)=nh
+ endif
+ lprev=lstrong(i)
+ enddo
+
+ if(nsigs.gt.0) then
+ do i=1,nsigs
+ ja=ia(i)
+ jb=ib(i)
+ if(ja.lt.0 .or. ja.gt.nh .or. jb.lt.0 .or. jb.gt.nh) then
+ cycle
+ endif
+ if(jb.eq.-99) jb=ja + min(2*iwid,nh)
+ lstrong(ja:jb)=.true.
+ enddo
+ endif
+
+! Copy frequency-domain data into array cs (strong) or cw (weak).
+ do i=0,nh
+ if(lstrong(i)) then
+ cxs(i)=fac*cxt(i)
+ cxw(i)=0.
+ else
+ cxw(i)=fac*cxt(i)
+ cxs(i)=0.
+ endif
+ enddo
+
+ call four2a(cxw,nfft,1,1,-1) !Transform weak and strong back
+ call four2a(cxs,nfft,1,1,-1) !to time domain, separately (c2r)
+
+ if(nwindow.eq.2) then
+ xw(0:nh-1)=xw(0:nh-1)+xwov(0:nh-1) !Add previous segment's 2nd half
+ xwov(0:nh-1)=xw(nh:nfft-1) !Save 2nd half
+ xs(0:nh-1)=xs(0:nh-1)+xsov(0:nh-1) !Ditto for strong signals
+ xsov(0:nh-1)=xs(nh:nfft-1)
+ endif
+
+! Apply noise blanking to weak data
+ if(nb.ne.0) then
+ do i=0,kstep-1
+ peak=abs(xw(i))
+ if(peak.gt.peaklimit) then
+ xw(i)=0.
+ nzap=nzap+1
+ endif
+ enddo
+ endif
+
+! Compute power levels from weak data only
+ do i=0,kstep-1
+ px=px + xw(i)**2
+ enddo
+
+ x1(0:kstep-1)=xw(0:kstep-1) + xs(0:kstep-1) !Recombine weak + strong
+
+ return
+end subroutine timf2
diff --git a/lib/wqencode.f90 b/lib/wqencode.f90
new file mode 100644
index 000000000..b5f453c6a
--- /dev/null
+++ b/lib/wqencode.f90
@@ -0,0 +1,65 @@
+subroutine wqencode(msg,ntype,data0)
+
+! Parse and encode a WSPR message.
+
+ use packjt
+ parameter (MASK15=32767)
+ character*22 msg
+ character*12 call1,call2
+ character grid4*4,grid6*6
+ logical lbad1,lbad2
+ integer*1 data0(11)
+ integer nu(0:9)
+ data nu/0,-1,1,0,-1,2,1,0,-1,1/
+
+! Standard WSPR message (types 0 3 7 10 13 17 ... 60)
+ i1=index(msg,' ')
+ i2=index(msg,'/')
+ i3=index(msg,'<')
+ call1=msg(:i1-1)
+ if(i1.lt.3 .or. i1.gt.7 .or. i2.gt.0 .or. i3.gt.0) go to 10
+ grid4=msg(i1+1:i1+4)
+ call packcall(call1,n1,lbad1)
+ call packgrid(grid4,ng,lbad2)
+ if(lbad1 .or. lbad2) go to 10
+ ndbm=0
+ read(msg(i1+5:),*) ndbm
+ if(ndbm.lt.0) ndbm=0
+ if(ndbm.gt.60) ndbm=60
+ ndbm=ndbm+nu(mod(ndbm,10))
+ n2=128*ng + (ndbm+64)
+ call pack50(n1,n2,data0)
+ ntype=ndbm
+ go to 900
+
+10 if(i2.ge.2 .and. i3.lt.1) then
+ call packpfx(call1,n1,ng,nadd)
+ ndbm=0
+ read(msg(i1+1:),*) ndbm
+ if(ndbm.lt.0) ndbm=0
+ if(ndbm.gt.60) ndbm=60
+ ndbm=ndbm+nu(mod(ndbm,10))
+ ntype=ndbm + 1 + nadd
+ n2=128*ng + ntype + 64
+ call pack50(n1,n2,data0)
+ else if(i3.eq.1) then
+ i4=index(msg,'>')
+ call1=msg(2:i4-1)
+ call hash(call1,i4-2,ih)
+ grid6=msg(i1+1:i1+6)
+ call2=grid6(2:6)//grid6(1:1)//' '
+ call packcall(call2,n1,lbad1)
+ ndbm=0
+ read(msg(i1+8:),*) ndbm
+ if(ndbm.lt.0) ndbm=0
+ if(ndbm.gt.60) ndbm=60
+ ndbm=ndbm+nu(mod(ndbm,10))
+ ntype=-(ndbm+1)
+ n2=128*ih + ntype + 64
+ call pack50(n1,n2,data0)
+ endif
+ go to 900
+
+900 continue
+ return
+end subroutine wqencode
diff --git a/lib/wsjt4.f90 b/lib/wsjt4.f90
index 7d7119392..12726ff91 100644
--- a/lib/wsjt4.f90
+++ b/lib/wsjt4.f90
@@ -1,4 +1,4 @@
-subroutine wsjt4(dat,npts,nutc,NClearAve,ntol,emedelay,dttol, &
+subroutine wsjt4(dat,npts,nutc,NClearAve,minsync,ntol,emedelay,dttol, &
mode4,minw,mycall,hiscall,hisgrid,nfqso,NAgain,ndepth,neme)
! Orchestrates the process of decoding JT4 messages, using data that
@@ -30,8 +30,7 @@ subroutine wsjt4(dat,npts,nutc,NClearAve,ntol,emedelay,dttol, &
endif
zz=0.
-! syncmin=1.0
- syncmin=7.0
+ syncmin=5.0 + minsync
naggressive=0
if(ndepth.ge.2) naggressive=1
nq1=3
@@ -102,7 +101,7 @@ subroutine wsjt4(dat,npts,nutc,NClearAve,ntol,emedelay,dttol, &
! Fano succeeded: display the message and return FANO OK
write(*,1010) nutc,nsnr,dtx,nfreq,csync,decoded,' *', &
char(ichar('A')+ich-1)
-1010 format(i4.4,i4,f5.2,i5,a1,1x,a22,a2,1x,a1,i3)
+1010 format(i4.4,i4,f5.2,i5,1x,a1,1x,a22,a2,1x,a1,i3)
nsave=0
go to 990
@@ -174,6 +173,5 @@ subroutine wsjt4(dat,npts,nutc,NClearAve,ntol,emedelay,dttol, &
deepave,cqual,char(ichar('A')+ich-1),ndeepave
endif
-990 return
+990 return
end subroutine wsjt4
-
diff --git a/lib/wspr_downsample.f90 b/lib/wspr_downsample.f90
new file mode 100644
index 000000000..c2691866f
--- /dev/null
+++ b/lib/wspr_downsample.f90
@@ -0,0 +1,76 @@
+subroutine wspr_downsample(id2,k)
+
+! Input:
+! id2 raw 16-bit integer data, 12000 Hz sample rate
+! k pointer to the most recent new data
+
+! Output (in common/c0com)
+! c0 complex data downsampled to 1500 Hz
+
+ parameter (NMAX=120*12000) !Total sample intervals per 30 minutes
+ parameter (NDMAX=120*1500) !Sample intervals at 1500 Hz rate
+ parameter (NSMAX=1366) !Max length of saved spectra
+ parameter (NFFT1=1024)
+ parameter (MAXFFT3=32768)
+ real*4 w3(MAXFFT3)
+ real*4 x0(NFFT1),x1(NFFT1)
+ real*4 x2(NFFT1+105)
+ real*4 ssum(NSMAX)
+ logical*1 lstrong(0:1023) !Should be (0:512)
+ integer*2 id2(NMAX)
+ complex c0
+ common/c0com/c0(NDMAX)
+ data rms/999.0/,k0/99999999/,nfft3z/0/,nsps/8192/,nbfo/1500/
+ save
+
+ nfft3=nsps/4
+ jstep=nsps/16
+ if(k.gt.NMAX) go to 999
+ if(k.lt.nfft3) go to 999 !Wait for enough samples to start
+ if(nfft3.ne.nfft3z) then
+ pi=4.0*atan(1.0)
+ do i=1,nfft3
+ w3(i)=2.0*(sin(i*pi/nfft3))**2 !Window for nfft3
+ enddo
+ nfft3z=nfft3
+ endif
+
+ if(k.lt.k0) then
+ ja=0
+ ssum=0.
+ k1=0
+ k8=0
+ x2=0.
+! if(ndiskdat.eq.0) then
+! id2(k+1:)=0
+! c0=0. !This is necessary to prevent "ghosts". Not sure why.
+! endif
+ endif
+ k0=k
+
+ nzap=0
+ nbslider=0
+ sigmas=1.0*(10.0**(0.01*nbslider)) + 0.7
+ peaklimit=sigmas*max(10.0,rms)
+ px=0.
+
+ nwindow=2
+ kstep1=NFFT1
+ if(nwindow.ne.0) kstep1=NFFT1/2
+ fac=2.0/NFFT1
+ nblks=(k-k1)/kstep1
+ gain=1.0
+ do nblk=1,nblks
+ do i=1,NFFT1
+ x0(i)=gain*id2(k1+i)
+ enddo
+ call timf2(x0,k,NFFT1,nwindow,nb,peaklimit,x1, &
+ slimit,lstrong,px,nzap)
+! Mix at nbfo Hz, lowpass at +/-750 Hz, and downsample to 1500 Hz complex.
+ call mixlpf(x1,nbfo,c0(k8+1))
+ k1=k1+kstep1
+ k8=k8+kstep1/8
+ enddo
+
+999 return
+end subroutine wspr_downsample
diff --git a/lib/wsprd/Makefile b/lib/wsprd/Makefile
new file mode 100644
index 000000000..8095bbcbe
--- /dev/null
+++ b/lib/wsprd/Makefile
@@ -0,0 +1,39 @@
+#CC = gcc
+CC = clang
+FC = gfortran
+
+FFLAGS = -O2 -Wall -Wno-conversion
+CFLAGS= -I/usr/include -Wall -Wno-missing-braces -O2
+LDFLAGS = -L/usr/lib
+LIBS = -lfftw3 -lm
+
+# Default rules
+%.o: %.c $(DEPS)
+ ${CC} ${CFLAGS} -c $<
+%.o: %.f
+ ${FC} ${FFLAGS} -c $<
+%.o: %.F
+ ${FC} ${FFLAGS} -c $<
+%.o: %.f90
+ ${FC} ${FFLAGS} -c $<
+%.o: %.F90
+ ${FC} ${FFLAGS} -c $<
+
+all: wsprd WSPRcode test_wspr
+
+DEPS = fano.h
+OBJS1 = wsprd.o wsprd_utils.o fano.o tab.o nhash.o
+wsprd: $(OBJS1)
+ $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)
+
+OBJS2 = test_wspr.o unpk.o wsprd_utils.o nhash.o
+test_wspr: $(OBJS2) libwspr.a
+ $(FC) -o test_wspr $(FFLAGS) $(OBJS2) libwspr.a
+
+
+OBJS3 = WSPRcode.o
+WSPRcode: $(OBJS3) libwspr.a
+ $(FC) -o WSPRcode $(FFLAGS) $(OBJS3) libwspr.a
+
+clean:
+ rm *.o wsprd
diff --git a/lib/wsprd/Makefile.MinGW b/lib/wsprd/Makefile.MinGW
new file mode 100644
index 000000000..d9fd6b83c
--- /dev/null
+++ b/lib/wsprd/Makefile.MinGW
@@ -0,0 +1,30 @@
+CC = gcc
+#CC = clang
+FC = gfortran
+
+FFLAGS = -O2 -Wall -Wno-conversion
+CFLAGS= -Wall -Wno-missing-braces -O2
+#LDFLAGS = -L/JTSDK/fftw3f
+LIBS = c:/JTSDK/fftw3f/libfftw3-3.dll -lm
+
+# Default rules
+%.o: %.c $(DEPS)
+ ${CC} ${CFLAGS} -c $<
+%.o: %.f
+ ${FC} ${FFLAGS} -c $<
+%.o: %.F
+ ${FC} ${FFLAGS} -c $<
+%.o: %.f90
+ ${FC} ${FFLAGS} -c $<
+%.o: %.F90
+ ${FC} ${FFLAGS} -c $<
+
+all: wsprd
+
+DEPS = fano.h
+OBJS1 = wsprd.o wsprd_utils.o fano.o tab.o nhash.o
+wsprd: $(OBJS1)
+ $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)
+
+clean:
+ rm *.o wsprd
diff --git a/lib/wsprd/WSPRcode.f90 b/lib/wsprd/WSPRcode.f90
new file mode 100644
index 000000000..1ec91712b
--- /dev/null
+++ b/lib/wsprd/WSPRcode.f90
@@ -0,0 +1,132 @@
+program wsprcode
+
+! This program provides examples of the source encoding, convolutional
+! error-control coding, bit and symbol ordering, and synchronizing
+! information contained in WSPR messages.
+
+ parameter (NSYM=162)
+ parameter (MAXSYM=176)
+ character*22 msg,msg2
+ integer*1 data0(7)
+ integer*1 data1(7)
+ integer*1 dat(NSYM)
+ integer*1 softsym(NSYM)
+
+! Define the sync vector:
+ integer*1 sync(NSYM)
+ data sync/ &
+ 1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0, &
+ 0,1,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1, &
+ 0,0,0,0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0,1, &
+ 1,0,1,0,0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1, &
+ 0,0,1,0,1,1,0,0,0,1,1,0,1,0,1,0,0,0,1,0, &
+ 0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,0,1,1, &
+ 0,1,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,0,1,1, &
+ 0,0,0,0,0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0, &
+ 0,0/
+
+! Metric table for decoding from soft symbols
+ integer mettab(0:255,0:1)
+ data mettab/ &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, &
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, &
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, &
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, &
+ 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, &
+ -1, -1, -1, -2, -2, -3, -4, -4, -5, -6, &
+ -7, -7, -8, -9, -10, -11, -12, -12, -13, -14, &
+ -15, -16, -17, -17, -18, -19, -20, -21, -22, -22, &
+ -23, -24, -25, -26, -26, -27, -28, -29, -30, -30, &
+ -31, -32, -33, -33, -34, -35, -36, -36, -37, -38, &
+ -38, -39, -40, -41, -41, -42, -43, -43, -44, -45, &
+ -45, -46, -47, -47, -48, -49, -49, -50, -51, -51, &
+ -52, -53, -53, -54, -54, -55, -56, -56, -57, -57, &
+ -58, -59, -59, -60, -60, -61, -62, -62, -62, -63, &
+ -64, -64, -65, -65, -66, -67, -67, -67, -68, -69, &
+ -69, -70, -70, -71, -72, -72, -72, -72, -73, -74, &
+ -75, -75, -75, -77, -76, -76, -78, -78, -80, -81, &
+ -80, -79, -83, -82, -81, -82, -82, -83, -84, -84, &
+ -84, -87, -86, -87, -88, -89, -89, -89, -88, -87, &
+ -86, -87, -84, -84, -84, -83, -82, -82, -81, -82, &
+ -83, -79, -80, -81, -80, -78, -78, -76, -76, -77, &
+ -75, -75, -75, -74, -73, -72, -72, -72, -72, -71, &
+ -70, -70, -69, -69, -68, -67, -67, -67, -66, -65, &
+ -65, -64, -64, -63, -62, -62, -62, -61, -60, -60, &
+ -59, -59, -58, -57, -57, -56, -56, -55, -54, -54, &
+ -53, -53, -52, -51, -51, -50, -49, -49, -48, -47, &
+ -47, -46, -45, -45, -44, -43, -43, -42, -41, -41, &
+ -40, -39, -38, -38, -37, -36, -36, -35, -34, -33, &
+ -33, -32, -31, -30, -30, -29, -28, -27, -26, -26, &
+ -25, -24, -23, -22, -22, -21, -20, -19, -18, -17, &
+ -17, -16, -15, -14, -13, -12, -12, -11, -10, -9, &
+ -8, -7, -7, -6, -5, -4, -4, -3, -2, -2, &
+ -1, -1, -1, 0, 0, 1, 1, 1, 1, 2, &
+ 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, &
+ 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, &
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, &
+ 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5/
+
+! Get command-line argument(s)
+ nargs=iargc()
+ if(nargs.ne.1) then
+ print*,'Usage: WSPRcode "message"'
+ go to 999
+ endif
+ call getarg(1,msg) !Get message from command line
+ write(*,1000) msg
+1000 format('Message: ',a22)
+
+ nbits=50+31 !User bits=50, constraint length=32
+ nbytes=(nbits+7)/8
+ ndelta=50
+ limit=20000
+
+ data0=0
+ call wqencode(msg,ntype0,data0) !Source encoding
+ write(*,1002) data0
+1002 format(/'Source-encoded message (50 bits, hex):',7z3.2)
+
+ call encode232(data0,nbytes,dat,MAXSYM) !Convolutional encoding
+ call inter_mept(dat,1) !Interleaving
+
+ write(*,1004)
+1004 format(/'Data symbols:')
+ write(*,1006) (dat(i),i=1,NSYM)
+1006 format(5x,30i2)
+
+ write(*,1008)
+1008 format(/'Sync symbols:')
+ write(*,1006) (sync(i),i=1,NSYM)
+
+ write(*,1010)
+1010 format(/'Channel symbols:')
+ write(*,1006) (2*dat(i)+sync(i),i=1,NSYM)
+
+ call inter_mept(dat,-1) !Remove interleaving
+ softsym=-dat !Simulate soft symbols
+
+! Call the sequential (Fano algorithm) decoder
+ call fano232(softsym,nbits,mettab,ndelta,limit,data1,ncycles,metric,nerr)
+ call wqdecode(data1,msg2,ntype1)
+
+ write(*,1020) ntype1
+1020 format(/'Message type: ',i7)
+ write(*,1030) msg2
+1030 format('Decoded message: ',a22)
+
+999 end program wsprcode
diff --git a/lib/wsprd/fano.c b/lib/wsprd/fano.c
new file mode 100644
index 000000000..e68305065
--- /dev/null
+++ b/lib/wsprd/fano.c
@@ -0,0 +1,255 @@
+/*
+ This file is part of wsprd.
+
+ File name: fano.c
+
+ Description: Soft decision Fano sequential decoder for K=32 r=1/2
+ convolutional code.
+
+ Copyright 1994, Phil Karn, KA9Q
+ Minor modifications by Joe Taylor, K1JT
+*/
+
+#define LL 1 // Select Layland-Lushbaugh code
+#include
+#include
+#include
+#include "fano.h"
+
+struct node {
+ unsigned long encstate; // Encoder state of next node
+ long gamma; // Cumulative metric to this node
+ int metrics[4]; // Metrics indexed by all possible tx syms
+ int tm[2]; // Sorted metrics for current hypotheses
+ int i; // Current branch being tested
+};
+
+// Convolutional coding polynomials. All are rate 1/2, K=32
+#ifdef NASA_STANDARD
+/* "NASA standard" code by Massey & Costello
+ * Nonsystematic, quick look-in, dmin=11, dfree=23
+ * used on Pioneer 10-12, Helios A,B
+ */
+#define POLY1 0xbbef6bb7
+#define POLY2 0xbbef6bb5
+#endif
+
+#ifdef MJ
+/* Massey-Johannesson code
+ * Nonsystematic, quick look-in, dmin=13, dfree>=23
+ * Purported to be more computationally efficient than Massey-Costello
+ */
+#define POLY1 0xb840a20f
+#define POLY2 0xb840a20d
+#endif
+
+#ifdef LL
+/* Layland-Lushbaugh code
+ * Nonsystematic, non-quick look-in, dmin=?, dfree=?
+ */
+#define POLY1 0xf2d05351
+#define POLY2 0xe4613c47
+#endif
+
+/* Convolutional encoder macro. Takes the encoder state, generates
+ * a rate 1/2 symbol pair and stores it in 'sym'. The symbol generated from
+ * POLY1 goes into the 2-bit of sym, and the symbol generated from POLY2
+ * goes into the 1-bit.
+ */
+#define ENCODE(sym,encstate) {\
+ unsigned long _tmp;\
+\
+ _tmp = (encstate) & POLY1;\
+ _tmp ^= _tmp >> 16;\
+ (sym) = Partab[(_tmp ^ (_tmp >> 8)) & 0xff] << 1;\
+ _tmp = (encstate) & POLY2;\
+ _tmp ^= _tmp >> 16;\
+ (sym) |= Partab[(_tmp ^ (_tmp >> 8)) & 0xff];\
+}
+
+
+/* Convolutionally encode a packet. The input data bytes are read
+ * high bit first and the encoded packet is written into 'symbols',
+ * one symbol per byte. The first symbol is generated from POLY1,
+ * the second from POLY2.
+ *
+ * Storing only one symbol per byte uses more space, but it is faster
+ * and easier than trying to pack them more compactly.
+ */
+int encode(
+ unsigned char *symbols, // Output buffer, 2*nbytes
+ unsigned char *data, // Input buffer, nbytes
+ unsigned int nbytes) // Number of bytes in data
+{
+ unsigned long encstate;
+ int sym;
+ int i;
+
+ encstate = 0;
+ while(nbytes-- != 0) {
+ for(i=7;i>=0;i--) {
+ encstate = (encstate << 1) | ((*data >> i) & 1);
+ ENCODE(sym,encstate);
+ *symbols++ = sym >> 1;
+ *symbols++ = sym & 1;
+ }
+ data++;
+ }
+ return 0;
+}
+
+/* Decode packet with the Fano algorithm.
+ * Return 0 on success, -1 on timeout
+ */
+int fano(
+ unsigned int *metric, // Final path metric (returned value)
+ unsigned int *cycles, // Cycle count (returned value)
+ unsigned int *maxnp, // Progress before timeout (returned value)
+ unsigned char *data, // Decoded output data
+ unsigned char *symbols, // Raw deinterleaved input symbols
+ unsigned int nbits, // Number of output bits
+ int mettab[2][256], // Metric table, [sent sym][rx symbol]
+ int delta, // Threshold adjust parameter
+ unsigned int maxcycles) // Decoding timeout in cycles per bit
+{
+ struct node *nodes; // First node
+ struct node *np; // Current node
+ struct node *lastnode; // Last node
+ struct node *tail; // First node of tail
+ int t; // Threshold
+ int m0,m1;
+ int ngamma;
+ unsigned int lsym;
+ unsigned int i;
+
+ if((nodes = (struct node *)malloc(nbits*sizeof(struct node))) == NULL) {
+ printf("malloc failed\n");
+ return 0;
+ }
+ lastnode = &nodes[nbits-1];
+ tail = &nodes[nbits-31];
+ *maxnp = 0;
+
+/* Compute all possible branch metrics for each symbol pair
+ * This is the only place we actually look at the raw input symbols
+ */
+ for(np=nodes;np <= lastnode;np++) {
+ np->metrics[0] = mettab[0][symbols[0]] + mettab[0][symbols[1]];
+ np->metrics[1] = mettab[0][symbols[0]] + mettab[1][symbols[1]];
+ np->metrics[2] = mettab[1][symbols[0]] + mettab[0][symbols[1]];
+ np->metrics[3] = mettab[1][symbols[0]] + mettab[1][symbols[1]];
+ symbols += 2;
+ }
+ np = nodes;
+ np->encstate = 0;
+
+// Compute and sort branch metrics from root node */
+ ENCODE(lsym,np->encstate); // 0-branch (LSB is 0)
+ m0 = np->metrics[lsym];
+
+/* Now do the 1-branch. To save another ENCODE call here and
+ * inside the loop, we assume that both polynomials are odd,
+ * providing complementary pairs of branch symbols.
+
+ * This code should be modified if a systematic code were used.
+ */
+
+ m1 = np->metrics[3^lsym];
+ if(m0 > m1) {
+ np->tm[0] = m0; // 0-branch has better metric
+ np->tm[1] = m1;
+ } else {
+ np->tm[0] = m1; // 1-branch is better
+ np->tm[1] = m0;
+ np->encstate++; // Set low bit
+ }
+ np->i = 0; // Start with best branch
+ maxcycles *= nbits;
+ np->gamma = t = 0;
+
+ // Start the Fano decoder
+ for(i=1;i <= maxcycles;i++) {
+ if((int)(np-nodes) > (int)*maxnp) *maxnp=(int)(np-nodes);
+#ifdef debug
+ printf("k=%ld, g=%ld, t=%d, m[%d]=%d, maxnp=%d\n",
+ np-nodes,np->gamma,t,np->i,np->tm[np->i],*maxnp);
+#endif
+// Look forward */
+ ngamma = np->gamma + np->tm[np->i];
+ if(ngamma >= t) {
+ if(np->gamma < t + delta) { // Node is acceptable
+ /* First time we've visited this node;
+ * Tighten threshold.
+ *
+ * This loop could be replaced with
+ * t += delta * ((ngamma - t)/delta);
+ * but the multiply and divide are slower.
+ */
+ while(ngamma >= t + delta) t += delta;
+ }
+ np[1].gamma = ngamma; // Move forward
+ np[1].encstate = np->encstate << 1;
+ if(++np == lastnode) {
+ break; // Done!
+ }
+
+ /* Compute and sort metrics, starting with the
+ * zero branch
+ */
+ ENCODE(lsym,np->encstate);
+ if(np >= tail) {
+ /* The tail must be all zeroes, so don't
+ * bother computing the 1-branches here.
+ */
+ np->tm[0] = np->metrics[lsym];
+ } else {
+ m0 = np->metrics[lsym];
+ m1 = np->metrics[3^lsym];
+ if(m0 > m1) {
+ np->tm[0] = m0; // 0-branch is better
+ np->tm[1] = m1;
+ } else {
+ np->tm[0] = m1; // 1-branch is better
+ np->tm[1] = m0;
+ np->encstate++; // Set low bit
+ }
+ }
+ np->i = 0; // Start with best branch
+ continue;
+ }
+ // Threshold violated, can't go forward
+ for(;;) { // Look backward
+ if(np == nodes || np[-1].gamma < t) {
+ /* Can't back up either.
+ * Relax threshold and and look
+ * forward again to better branch.
+ */
+ t -= delta;
+ if(np->i != 0) {
+ np->i = 0;
+ np->encstate ^= 1;
+ }
+ break;
+ }
+ // Back up
+ if(--np < tail && np->i != 1) {
+ np->i++; // Search next best branch
+ np->encstate ^= 1;
+ break;
+ } // else keep looking back
+ }
+ }
+ *metric = np->gamma; // Return the final path metric
+
+ // Copy decoded data to user's buffer
+ nbits >>= 3;
+ np = &nodes[7];
+ while(nbits-- != 0) {
+ *data++ = np->encstate;
+ np += 8;
+ }
+ *cycles = i+1;
+ free(nodes);
+ if(i >= maxcycles) return -1; // Decoder timed out
+ return 0; // Successful completion
+}
diff --git a/lib/wsprd/fano.h b/lib/wsprd/fano.h
new file mode 100644
index 000000000..76001626b
--- /dev/null
+++ b/lib/wsprd/fano.h
@@ -0,0 +1,21 @@
+/*
+ This file is part of wsprd.
+
+ File name: fano.h
+
+ Description: Header file for sequential Fano decoder.
+
+ Copyright 1994, Phil Karn, KA9Q
+ Minor modifications by Joe Taylor, K1JT
+*/
+
+int fano(unsigned int *metric, unsigned int *cycles, unsigned int *maxnp,
+ unsigned char *data,unsigned char *symbols, unsigned int nbits,
+ int mettab[2][256],int delta,unsigned int maxcycles);
+
+int encode(unsigned char *symbols,unsigned char *data,unsigned int nbytes);
+
+extern unsigned char Partab[];
+
+
+
diff --git a/lib/wsprd/fftw3.h b/lib/wsprd/fftw3.h
new file mode 100644
index 000000000..58a2c73df
--- /dev/null
+++ b/lib/wsprd/fftw3.h
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2003, 2007-11 Matteo Frigo
+ * Copyright (c) 2003, 2007-11 Massachusetts Institute of Technology
+ *
+ * The following statement of license applies *only* to this header file,
+ * and *not* to the other files distributed with FFTW or derived therefrom:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/***************************** NOTE TO USERS *********************************
+ *
+ * THIS IS A HEADER FILE, NOT A MANUAL
+ *
+ * If you want to know how to use FFTW, please read the manual,
+ * online at http://www.fftw.org/doc/ and also included with FFTW.
+ * For a quick start, see the manual's tutorial section.
+ *
+ * (Reading header files to learn how to use a library is a habit
+ * stemming from code lacking a proper manual. Arguably, it's a
+ * *bad* habit in most cases, because header files can contain
+ * interfaces that are not part of the public, stable API.)
+ *
+ ****************************************************************************/
+
+#ifndef FFTW3_H
+#define FFTW3_H
+
+#include
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+/* If is included, use the C99 complex type. Otherwise
+ define a type bit-compatible with C99 complex */
+#if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
+# define FFTW_DEFINE_COMPLEX(R, C) typedef R _Complex C
+#else
+# define FFTW_DEFINE_COMPLEX(R, C) typedef R C[2]
+#endif
+
+#define FFTW_CONCAT(prefix, name) prefix ## name
+#define FFTW_MANGLE_DOUBLE(name) FFTW_CONCAT(fftw_, name)
+#define FFTW_MANGLE_FLOAT(name) FFTW_CONCAT(fftwf_, name)
+#define FFTW_MANGLE_LONG_DOUBLE(name) FFTW_CONCAT(fftwl_, name)
+#define FFTW_MANGLE_QUAD(name) FFTW_CONCAT(fftwq_, name)
+
+/* IMPORTANT: for Windows compilers, you should add a line
+ #define FFTW_DLL
+ here and in kernel/ifftw.h if you are compiling/using FFTW as a
+ DLL, in order to do the proper importing/exporting, or
+ alternatively compile with -DFFTW_DLL or the equivalent
+ command-line flag. This is not necessary under MinGW/Cygwin, where
+ libtool does the imports/exports automatically. */
+#if defined(FFTW_DLL) && (defined(_WIN32) || defined(__WIN32__))
+ /* annoying Windows syntax for shared-library declarations */
+# if defined(COMPILING_FFTW) /* defined in api.h when compiling FFTW */
+# define FFTW_EXTERN extern __declspec(dllexport)
+# else /* user is calling FFTW; import symbol */
+# define FFTW_EXTERN extern __declspec(dllimport)
+# endif
+#else
+# define FFTW_EXTERN extern
+#endif
+
+enum fftw_r2r_kind_do_not_use_me {
+ FFTW_R2HC=0, FFTW_HC2R=1, FFTW_DHT=2,
+ FFTW_REDFT00=3, FFTW_REDFT01=4, FFTW_REDFT10=5, FFTW_REDFT11=6,
+ FFTW_RODFT00=7, FFTW_RODFT01=8, FFTW_RODFT10=9, FFTW_RODFT11=10
+};
+
+struct fftw_iodim_do_not_use_me {
+ int n; /* dimension size */
+ int is; /* input stride */
+ int os; /* output stride */
+};
+
+#include /* for ptrdiff_t */
+struct fftw_iodim64_do_not_use_me {
+ ptrdiff_t n; /* dimension size */
+ ptrdiff_t is; /* input stride */
+ ptrdiff_t os; /* output stride */
+};
+
+typedef void (*fftw_write_char_func_do_not_use_me)(char c, void *);
+typedef int (*fftw_read_char_func_do_not_use_me)(void *);
+
+/*
+ huge second-order macro that defines prototypes for all API
+ functions. We expand this macro for each supported precision
+
+ X: name-mangling macro
+ R: real data type
+ C: complex data type
+*/
+
+#define FFTW_DEFINE_API(X, R, C) \
+ \
+FFTW_DEFINE_COMPLEX(R, C); \
+ \
+typedef struct X(plan_s) *X(plan); \
+ \
+typedef struct fftw_iodim_do_not_use_me X(iodim); \
+typedef struct fftw_iodim64_do_not_use_me X(iodim64); \
+ \
+typedef enum fftw_r2r_kind_do_not_use_me X(r2r_kind); \
+ \
+typedef fftw_write_char_func_do_not_use_me X(write_char_func); \
+typedef fftw_read_char_func_do_not_use_me X(read_char_func); \
+ \
+FFTW_EXTERN void X(execute)(const X(plan) p); \
+ \
+FFTW_EXTERN X(plan) X(plan_dft)(int rank, const int *n, \
+ C *in, C *out, int sign, unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_dft_1d)(int n, C *in, C *out, int sign, \
+ unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_dft_2d)(int n0, int n1, \
+ C *in, C *out, int sign, unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_dft_3d)(int n0, int n1, int n2, \
+ C *in, C *out, int sign, unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_many_dft)(int rank, const int *n, \
+ int howmany, \
+ C *in, const int *inembed, \
+ int istride, int idist, \
+ C *out, const int *onembed, \
+ int ostride, int odist, \
+ int sign, unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_guru_dft)(int rank, const X(iodim) *dims, \
+ int howmany_rank, \
+ const X(iodim) *howmany_dims, \
+ C *in, C *out, \
+ int sign, unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_guru_split_dft)(int rank, const X(iodim) *dims, \
+ int howmany_rank, \
+ const X(iodim) *howmany_dims, \
+ R *ri, R *ii, R *ro, R *io, \
+ unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_guru64_dft)(int rank, \
+ const X(iodim64) *dims, \
+ int howmany_rank, \
+ const X(iodim64) *howmany_dims, \
+ C *in, C *out, \
+ int sign, unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_guru64_split_dft)(int rank, \
+ const X(iodim64) *dims, \
+ int howmany_rank, \
+ const X(iodim64) *howmany_dims, \
+ R *ri, R *ii, R *ro, R *io, \
+ unsigned flags); \
+ \
+FFTW_EXTERN void X(execute_dft)(const X(plan) p, C *in, C *out); \
+FFTW_EXTERN void X(execute_split_dft)(const X(plan) p, R *ri, R *ii, \
+ R *ro, R *io); \
+ \
+FFTW_EXTERN X(plan) X(plan_many_dft_r2c)(int rank, const int *n, \
+ int howmany, \
+ R *in, const int *inembed, \
+ int istride, int idist, \
+ C *out, const int *onembed, \
+ int ostride, int odist, \
+ unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_dft_r2c)(int rank, const int *n, \
+ R *in, C *out, unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_dft_r2c_1d)(int n,R *in,C *out,unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_dft_r2c_2d)(int n0, int n1, \
+ R *in, C *out, unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_dft_r2c_3d)(int n0, int n1, \
+ int n2, \
+ R *in, C *out, unsigned flags); \
+ \
+ \
+FFTW_EXTERN X(plan) X(plan_many_dft_c2r)(int rank, const int *n, \
+ int howmany, \
+ C *in, const int *inembed, \
+ int istride, int idist, \
+ R *out, const int *onembed, \
+ int ostride, int odist, \
+ unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_dft_c2r)(int rank, const int *n, \
+ C *in, R *out, unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_dft_c2r_1d)(int n,C *in,R *out,unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_dft_c2r_2d)(int n0, int n1, \
+ C *in, R *out, unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_dft_c2r_3d)(int n0, int n1, \
+ int n2, \
+ C *in, R *out, unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_guru_dft_r2c)(int rank, const X(iodim) *dims, \
+ int howmany_rank, \
+ const X(iodim) *howmany_dims, \
+ R *in, C *out, \
+ unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_guru_dft_c2r)(int rank, const X(iodim) *dims, \
+ int howmany_rank, \
+ const X(iodim) *howmany_dims, \
+ C *in, R *out, \
+ unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_guru_split_dft_r2c)( \
+ int rank, const X(iodim) *dims, \
+ int howmany_rank, \
+ const X(iodim) *howmany_dims, \
+ R *in, R *ro, R *io, \
+ unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_guru_split_dft_c2r)( \
+ int rank, const X(iodim) *dims, \
+ int howmany_rank, \
+ const X(iodim) *howmany_dims, \
+ R *ri, R *ii, R *out, \
+ unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_guru64_dft_r2c)(int rank, \
+ const X(iodim64) *dims, \
+ int howmany_rank, \
+ const X(iodim64) *howmany_dims, \
+ R *in, C *out, \
+ unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_guru64_dft_c2r)(int rank, \
+ const X(iodim64) *dims, \
+ int howmany_rank, \
+ const X(iodim64) *howmany_dims, \
+ C *in, R *out, \
+ unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_guru64_split_dft_r2c)( \
+ int rank, const X(iodim64) *dims, \
+ int howmany_rank, \
+ const X(iodim64) *howmany_dims, \
+ R *in, R *ro, R *io, \
+ unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_guru64_split_dft_c2r)( \
+ int rank, const X(iodim64) *dims, \
+ int howmany_rank, \
+ const X(iodim64) *howmany_dims, \
+ R *ri, R *ii, R *out, \
+ unsigned flags); \
+ \
+FFTW_EXTERN void X(execute_dft_r2c)(const X(plan) p, R *in, C *out); \
+FFTW_EXTERN void X(execute_dft_c2r)(const X(plan) p, C *in, R *out); \
+ \
+FFTW_EXTERN void X(execute_split_dft_r2c)(const X(plan) p, \
+ R *in, R *ro, R *io); \
+FFTW_EXTERN void X(execute_split_dft_c2r)(const X(plan) p, \
+ R *ri, R *ii, R *out); \
+ \
+FFTW_EXTERN X(plan) X(plan_many_r2r)(int rank, const int *n, \
+ int howmany, \
+ R *in, const int *inembed, \
+ int istride, int idist, \
+ R *out, const int *onembed, \
+ int ostride, int odist, \
+ const X(r2r_kind) *kind, unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_r2r)(int rank, const int *n, R *in, R *out, \
+ const X(r2r_kind) *kind, unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_r2r_1d)(int n, R *in, R *out, \
+ X(r2r_kind) kind, unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_r2r_2d)(int n0, int n1, R *in, R *out, \
+ X(r2r_kind) kind0, X(r2r_kind) kind1, \
+ unsigned flags); \
+FFTW_EXTERN X(plan) X(plan_r2r_3d)(int n0, int n1, int n2, \
+ R *in, R *out, X(r2r_kind) kind0, \
+ X(r2r_kind) kind1, X(r2r_kind) kind2, \
+ unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_guru_r2r)(int rank, const X(iodim) *dims, \
+ int howmany_rank, \
+ const X(iodim) *howmany_dims, \
+ R *in, R *out, \
+ const X(r2r_kind) *kind, unsigned flags); \
+ \
+FFTW_EXTERN X(plan) X(plan_guru64_r2r)(int rank, const X(iodim64) *dims, \
+ int howmany_rank, \
+ const X(iodim64) *howmany_dims, \
+ R *in, R *out, \
+ const X(r2r_kind) *kind, unsigned flags); \
+ \
+FFTW_EXTERN void X(execute_r2r)(const X(plan) p, R *in, R *out); \
+ \
+FFTW_EXTERN void X(destroy_plan)(X(plan) p); \
+FFTW_EXTERN void X(forget_wisdom)(void); \
+FFTW_EXTERN void X(cleanup)(void); \
+ \
+FFTW_EXTERN void X(set_timelimit)(double t); \
+ \
+FFTW_EXTERN void X(plan_with_nthreads)(int nthreads); \
+FFTW_EXTERN int X(init_threads)(void); \
+FFTW_EXTERN void X(cleanup_threads)(void); \
+ \
+FFTW_EXTERN int X(export_wisdom_to_filename)(const char *filename); \
+FFTW_EXTERN void X(export_wisdom_to_file)(FILE *output_file); \
+FFTW_EXTERN char *X(export_wisdom_to_string)(void); \
+FFTW_EXTERN void X(export_wisdom)(X(write_char_func) write_char, \
+ void *data); \
+FFTW_EXTERN int X(import_system_wisdom)(void); \
+FFTW_EXTERN int X(import_wisdom_from_filename)(const char *filename); \
+FFTW_EXTERN int X(import_wisdom_from_file)(FILE *input_file); \
+FFTW_EXTERN int X(import_wisdom_from_string)(const char *input_string); \
+FFTW_EXTERN int X(import_wisdom)(X(read_char_func) read_char, void *data); \
+ \
+FFTW_EXTERN void X(fprint_plan)(const X(plan) p, FILE *output_file); \
+FFTW_EXTERN void X(print_plan)(const X(plan) p); \
+ \
+FFTW_EXTERN void *X(malloc)(size_t n); \
+FFTW_EXTERN R *X(alloc_real)(size_t n); \
+FFTW_EXTERN C *X(alloc_complex)(size_t n); \
+FFTW_EXTERN void X(free)(void *p); \
+ \
+FFTW_EXTERN void X(flops)(const X(plan) p, \
+ double *add, double *mul, double *fmas); \
+FFTW_EXTERN double X(estimate_cost)(const X(plan) p); \
+FFTW_EXTERN double X(cost)(const X(plan) p); \
+ \
+FFTW_EXTERN const char X(version)[]; \
+FFTW_EXTERN const char X(cc)[]; \
+FFTW_EXTERN const char X(codelet_optim)[];
+
+
+/* end of FFTW_DEFINE_API macro */
+
+FFTW_DEFINE_API(FFTW_MANGLE_DOUBLE, double, fftw_complex)
+FFTW_DEFINE_API(FFTW_MANGLE_FLOAT, float, fftwf_complex)
+FFTW_DEFINE_API(FFTW_MANGLE_LONG_DOUBLE, long double, fftwl_complex)
+
+/* __float128 (quad precision) is a gcc extension on i386, x86_64, and ia64
+ for gcc >= 4.6 (compiled in FFTW with --enable-quad-precision) */
+#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) \
+ && !(defined(__ICC) || defined(__INTEL_COMPILER)) \
+ && (defined(__i386__) || defined(__x86_64__) || defined(__ia64__))
+# if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I)
+/* note: __float128 is a typedef, which is not supported with the _Complex
+ keyword in gcc, so instead we use this ugly __attribute__ version.
+ However, we can't simply pass the __attribute__ version to
+ FFTW_DEFINE_API because the __attribute__ confuses gcc in pointer
+ types. Hence redefining FFTW_DEFINE_COMPLEX. Ugh. */
+# undef FFTW_DEFINE_COMPLEX
+# define FFTW_DEFINE_COMPLEX(R, C) typedef _Complex float __attribute__((mode(TC))) C
+# endif
+FFTW_DEFINE_API(FFTW_MANGLE_QUAD, __float128, fftwq_complex)
+#endif
+
+#define FFTW_FORWARD (-1)
+#define FFTW_BACKWARD (+1)
+
+#define FFTW_NO_TIMELIMIT (-1.0)
+
+/* documented flags */
+#define FFTW_MEASURE (0U)
+#define FFTW_DESTROY_INPUT (1U << 0)
+#define FFTW_UNALIGNED (1U << 1)
+#define FFTW_CONSERVE_MEMORY (1U << 2)
+#define FFTW_EXHAUSTIVE (1U << 3) /* NO_EXHAUSTIVE is default */
+#define FFTW_PRESERVE_INPUT (1U << 4) /* cancels FFTW_DESTROY_INPUT */
+#define FFTW_PATIENT (1U << 5) /* IMPATIENT is default */
+#define FFTW_ESTIMATE (1U << 6)
+#define FFTW_WISDOM_ONLY (1U << 21)
+
+/* undocumented beyond-guru flags */
+#define FFTW_ESTIMATE_PATIENT (1U << 7)
+#define FFTW_BELIEVE_PCOST (1U << 8)
+#define FFTW_NO_DFT_R2HC (1U << 9)
+#define FFTW_NO_NONTHREADED (1U << 10)
+#define FFTW_NO_BUFFERING (1U << 11)
+#define FFTW_NO_INDIRECT_OP (1U << 12)
+#define FFTW_ALLOW_LARGE_GENERIC (1U << 13) /* NO_LARGE_GENERIC is default */
+#define FFTW_NO_RANK_SPLITS (1U << 14)
+#define FFTW_NO_VRANK_SPLITS (1U << 15)
+#define FFTW_NO_VRECURSE (1U << 16)
+#define FFTW_NO_SIMD (1U << 17)
+#define FFTW_NO_SLOW (1U << 18)
+#define FFTW_NO_FIXED_RADIX_LARGE_N (1U << 19)
+#define FFTW_ALLOW_PRUNING (1U << 20)
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* FFTW3_H */
diff --git a/lib/wsprd/genmet.f90 b/lib/wsprd/genmet.f90
new file mode 100644
index 000000000..e5dcdabaf
--- /dev/null
+++ b/lib/wsprd/genmet.f90
@@ -0,0 +1,50 @@
+program genmet
+
+ character*12 arg
+ integer hist(-128:128)
+ lim(x)=min(127,max(-128,nint(scale*x)))
+
+ nargs=iargc()
+ if(nargs.ne.4) then
+ print*,'Usage: genmet bw scale snr iters'
+ print*,'Example: genmet 1.46 20 -24 1000000'
+ go to 999
+ endif
+ call getarg(1,arg)
+ read(arg,*) bw
+ call getarg(2,arg)
+ read(arg,*) scale
+ call getarg(3,arg)
+ read(arg,*) snr
+ call getarg(4,arg)
+ read(arg,*) iters
+
+ hist=0
+ s=sqrt(2500.0/bw) * 10.0**(0.05*snr)
+ fac=1.0/sqrt(2.0)
+ do iter=1,iters
+ x1=fac*gran()
+ y1=fac*gran()
+ x0=fac*gran()
+ y0=fac*gran()
+ r=(x1+s)**2 + y1*y1 - x0*x0 - y0*y0
+ hist(lim(r))=hist(lim(r))+1
+ enddo
+
+ xln2=log(2.0)
+ do i=-128,127
+ p1=hist(i)/dfloat(iters)
+ j=-i
+ if(j.gt.127) j=127
+ p0=hist(j)/dfloat(iters)
+ xlhd0=log(max(0.001,2.0*p0/(p0+p1)))/xln2
+ xlhd1=log(max(0.001,2.0*p1/(p0+p1)))/xln2
+ write(13,1010) i/scale,hist(i)/dfloat(iters)
+1010 format(f8.3,f12.9)
+ write(14,1012) i+128,xlhd0,xlhd1
+1012 format(i4,2f8.3)
+ enddo
+
+999 end program genmet
+
+
diff --git a/lib/wsprd/gran.c b/lib/wsprd/gran.c
new file mode 100644
index 000000000..24b986503
--- /dev/null
+++ b/lib/wsprd/gran.c
@@ -0,0 +1,28 @@
+#include
+#include
+
+/* Generate gaussian random float with mean=0 and std_dev=1 */
+float gran_()
+{
+ float fac,rsq,v1,v2;
+ static float gset;
+ static int iset;
+
+ if(iset){
+ /* Already got one */
+ iset = 0;
+ return gset;
+ }
+ /* Generate two evenly distributed numbers between -1 and +1
+ * that are inside the unit circle
+ */
+ do {
+ v1 = 2.0 * (float)rand() / RAND_MAX - 1;
+ v2 = 2.0 * (float)rand() / RAND_MAX - 1;
+ rsq = v1*v1 + v2*v2;
+ } while(rsq >= 1.0 || rsq == 0.0);
+ fac = sqrt(-2.0*log(rsq)/rsq);
+ gset = v1*fac;
+ iset++;
+ return v2*fac;
+}
diff --git a/lib/wsprd/metric_tables.c b/lib/wsprd/metric_tables.c
new file mode 100644
index 000000000..5be334edb
--- /dev/null
+++ b/lib/wsprd/metric_tables.c
@@ -0,0 +1,111 @@
+/*******************************************************************************
+* 4 metric tables calculated via simulation for 2-FSK with Es/No=0,3,6,9 dB
+* tables were calculated for constant rms noise level of 50. The symbol vector
+* should be normalized to have rms amplitude equal to "symbol_scale".
+********************************************************************************/
+//float symbol_scale[4]={42.6, 53.3, 72.7, 100.2};
+float metric_tables[4][256]={
+ 0.9782, 0.9695, 0.9689, 0.9669, 0.9666, 0.9653, 0.9638, 0.9618, 0.9599, 0.9601,
+ 0.9592, 0.9570, 0.9556, 0.9540, 0.9525, 0.9527, 0.9486, 0.9477, 0.9450, 0.9436,
+ 0.9424, 0.9400, 0.9381, 0.9360, 0.9340, 0.9316, 0.9301, 0.9272, 0.9254, 0.9224,
+ 0.9196, 0.9171, 0.9154, 0.9123, 0.9076, 0.9061, 0.9030, 0.9000, 0.8965, 0.8934,
+ 0.8903, 0.8874, 0.8834, 0.8792, 0.8760, 0.8726, 0.8685, 0.8639, 0.8599, 0.8550,
+ 0.8504, 0.8459, 0.8422, 0.8364, 0.8320, 0.8262, 0.8215, 0.8159, 0.8111, 0.8052,
+ 0.7996, 0.7932, 0.7878, 0.7812, 0.7745, 0.7685, 0.7616, 0.7550, 0.7479, 0.7405,
+ 0.7336, 0.7255, 0.7184, 0.7102, 0.7016, 0.6946, 0.6860, 0.6769, 0.6687, 0.6598,
+ 0.6503, 0.6416, 0.6325, 0.6219, 0.6122, 0.6016, 0.5920, 0.5818, 0.5711, 0.5606,
+ 0.5487, 0.5374, 0.5266, 0.5142, 0.5020, 0.4908, 0.4784, 0.4663, 0.4532, 0.4405,
+ 0.4271, 0.4144, 0.4006, 0.3865, 0.3731, 0.3594, 0.3455, 0.3304, 0.3158, 0.3009,
+ 0.2858, 0.2708, 0.2560, 0.2399, 0.2233, 0.2074, 0.1919, 0.1756, 0.1590, 0.1427,
+ 0.1251, 0.1074, 0.0905, 0.0722, 0.0550, 0.0381, 0.0183, 0.0000, -0.0185, -0.0391,
+ -0.0571, -0.0760, -0.0966, -0.1160, -0.1370, -0.1584, -0.1787, -0.1999, -0.2214, -0.2423,
+ -0.2643, -0.2879, -0.3114, -0.3336, -0.3568, -0.3806, -0.4050, -0.4293, -0.4552, -0.4798,
+ -0.5046, -0.5296, -0.5564, -0.5836, -0.6093, -0.6372, -0.6645, -0.6933, -0.7208, -0.7495,
+ -0.7763, -0.8065, -0.8378, -0.8660, -0.8964, -0.9293, -0.9592, -0.9907, -1.0214, -1.0509,
+ -1.0850, -1.1168, -1.1528, -1.1847, -1.2157, -1.2511, -1.2850, -1.3174, -1.3540, -1.3900,
+ -1.4201, -1.4580, -1.4956, -1.5292, -1.5683, -1.6030, -1.6411, -1.6789, -1.7147, -1.7539,
+ -1.7887, -1.8289, -1.8699, -1.9043, -1.9469, -1.9849, -2.0267, -2.0610, -2.1028, -2.1391,
+ -2.1855, -2.2215, -2.2712, -2.3033, -2.3440, -2.3870, -2.4342, -2.4738, -2.5209, -2.5646,
+ -2.6016, -2.6385, -2.6868, -2.7356, -2.7723, -2.8111, -2.8524, -2.9009, -2.9428, -2.9879,
+ -3.0103, -3.0832, -3.1340, -3.1628, -3.2049, -3.2557, -3.3101, -3.3453, -3.4025, -3.4317,
+ -3.4828, -3.5270, -3.5745, -3.6181, -3.6765, -3.7044, -3.7410, -3.8118, -3.8368, -3.9549,
+ -3.9488, -3.9941, -4.0428, -4.0892, -4.1648, -4.1965, -4.1892, -4.2565, -4.3356, -4.3948,
+ -4.4481, -4.4607, -4.5533, -4.5809, -4.5927, -5.1047,
+ 0.9978, 0.9962, 0.9961, 0.9959, 0.9958, 0.9954, 0.9949, 0.9950, 0.9947, 0.9942,
+ 0.9940, 0.9939, 0.9933, 0.9931, 0.9928, 0.9924, 0.9921, 0.9916, 0.9911, 0.9909,
+ 0.9903, 0.9900, 0.9892, 0.9887, 0.9883, 0.9877, 0.9869, 0.9863, 0.9857, 0.9848,
+ 0.9842, 0.9835, 0.9825, 0.9817, 0.9808, 0.9799, 0.9791, 0.9777, 0.9767, 0.9757,
+ 0.9744, 0.9729, 0.9716, 0.9704, 0.9690, 0.9674, 0.9656, 0.9641, 0.9625, 0.9609,
+ 0.9587, 0.9567, 0.9548, 0.9524, 0.9501, 0.9478, 0.9453, 0.9426, 0.9398, 0.9371,
+ 0.9339, 0.9311, 0.9277, 0.9242, 0.9206, 0.9168, 0.9131, 0.9087, 0.9043, 0.8999,
+ 0.8953, 0.8907, 0.8857, 0.8803, 0.8747, 0.8690, 0.8632, 0.8572, 0.8507, 0.8439,
+ 0.8368, 0.8295, 0.8217, 0.8138, 0.8058, 0.7972, 0.7883, 0.7784, 0.7694, 0.7597,
+ 0.7489, 0.7378, 0.7269, 0.7152, 0.7030, 0.6911, 0.6782, 0.6643, 0.6506, 0.6371,
+ 0.6211, 0.6054, 0.5897, 0.5740, 0.5565, 0.5393, 0.5214, 0.5027, 0.4838, 0.4643,
+ 0.4436, 0.4225, 0.4004, 0.3787, 0.3562, 0.3324, 0.3089, 0.2839, 0.2584, 0.2321,
+ 0.2047, 0.1784, 0.1499, 0.1213, 0.0915, 0.0628, 0.0314, 0.0000, -0.0321, -0.0657,
+ -0.0977, -0.1324, -0.1673, -0.2036, -0.2387, -0.2768, -0.3150, -0.3538, -0.3936, -0.4327,
+ -0.4739, -0.5148, -0.5561, -0.6000, -0.6438, -0.6889, -0.7331, -0.7781, -0.8247, -0.8712,
+ -0.9177, -0.9677, -1.0142, -1.0631, -1.1143, -1.1686, -1.2169, -1.2680, -1.3223, -1.3752,
+ -1.4261, -1.4806, -1.5356, -1.5890, -1.6462, -1.7041, -1.7591, -1.8124, -1.8735, -1.9311,
+ -1.9891, -2.0459, -2.1048, -2.1653, -2.2248, -2.2855, -2.3466, -2.4079, -2.4668, -2.5263,
+ -2.5876, -2.6507, -2.7142, -2.7761, -2.8366, -2.8995, -2.9620, -3.0279, -3.0973, -3.1576,
+ -3.2238, -3.2890, -3.3554, -3.4215, -3.4805, -3.5518, -3.6133, -3.6812, -3.7473, -3.8140,
+ -3.8781, -3.9450, -4.0184, -4.0794, -4.1478, -4.2241, -4.2853, -4.3473, -4.4062, -4.4839,
+ -4.5539, -4.6202, -4.6794, -4.7478, -4.8309, -4.9048, -4.9669, -5.0294, -5.1194, -5.1732,
+ -5.2378, -5.3094, -5.3742, -5.4573, -5.5190, -5.5728, -5.6637, -5.7259, -5.7843, -5.8854,
+ -5.9553, -6.0054, -6.0656, -6.1707, -6.2241, -6.3139, -6.3393, -6.4356, -6.5153, -6.5758,
+ -6.6506, -6.7193, -6.7542, -6.8942, -6.9219, -6.9605, -7.1013, -7.1895, -7.1549, -7.2799,
+ -7.4119, -7.4608, -7.5256, -7.5879, -7.7598, -8.4120,
+ 0.9999, 0.9998, 0.9998, 0.9998, 0.9998, 0.9998, 0.9997, 0.9997, 0.9997, 0.9997,
+ 0.9997, 0.9996, 0.9996, 0.9996, 0.9995, 0.9995, 0.9994, 0.9994, 0.9994, 0.9993,
+ 0.9993, 0.9992, 0.9991, 0.9991, 0.9990, 0.9989, 0.9988, 0.9988, 0.9988, 0.9986,
+ 0.9985, 0.9984, 0.9983, 0.9982, 0.9980, 0.9979, 0.9977, 0.9976, 0.9974, 0.9971,
+ 0.9969, 0.9968, 0.9965, 0.9962, 0.9960, 0.9957, 0.9953, 0.9950, 0.9947, 0.9941,
+ 0.9937, 0.9933, 0.9928, 0.9922, 0.9917, 0.9911, 0.9904, 0.9897, 0.9890, 0.9882,
+ 0.9874, 0.9863, 0.9855, 0.9843, 0.9832, 0.9819, 0.9806, 0.9792, 0.9777, 0.9760,
+ 0.9743, 0.9724, 0.9704, 0.9683, 0.9659, 0.9634, 0.9609, 0.9581, 0.9550, 0.9516,
+ 0.9481, 0.9446, 0.9406, 0.9363, 0.9317, 0.9270, 0.9218, 0.9160, 0.9103, 0.9038,
+ 0.8972, 0.8898, 0.8822, 0.8739, 0.8647, 0.8554, 0.8457, 0.8357, 0.8231, 0.8115,
+ 0.7984, 0.7854, 0.7704, 0.7556, 0.7391, 0.7210, 0.7038, 0.6840, 0.6633, 0.6408,
+ 0.6174, 0.5939, 0.5678, 0.5410, 0.5137, 0.4836, 0.4524, 0.4193, 0.3850, 0.3482,
+ 0.3132, 0.2733, 0.2315, 0.1891, 0.1435, 0.0980, 0.0493, 0.0000, -0.0510, -0.1052,
+ -0.1593, -0.2177, -0.2759, -0.3374, -0.4005, -0.4599, -0.5266, -0.5935, -0.6626, -0.7328,
+ -0.8051, -0.8757, -0.9498, -1.0271, -1.1019, -1.1816, -1.2642, -1.3459, -1.4295, -1.5077,
+ -1.5958, -1.6818, -1.7647, -1.8548, -1.9387, -2.0295, -2.1152, -2.2154, -2.3011, -2.3904,
+ -2.4820, -2.5786, -2.6730, -2.7652, -2.8616, -2.9546, -3.0526, -3.1445, -3.2445, -3.3416,
+ -3.4357, -3.5325, -3.6324, -3.7313, -3.8225, -3.9209, -4.0248, -4.1278, -4.2261, -4.3193,
+ -4.4220, -4.5262, -4.6214, -4.7242, -4.8234, -4.9245, -5.0298, -5.1250, -5.2232, -5.3267,
+ -5.4332, -5.5342, -5.6431, -5.7270, -5.8401, -5.9350, -6.0407, -6.1418, -6.2363, -6.3384,
+ -6.4536, -6.5429, -6.6582, -6.7433, -6.8438, -6.9478, -7.0789, -7.1894, -7.2714, -7.3815,
+ -7.4810, -7.5575, -7.6852, -7.8071, -7.8580, -7.9724, -8.1000, -8.2207, -8.2867, -8.4017,
+ -8.5287, -8.6347, -8.7082, -8.8319, -8.9448, -9.0355, -9.1885, -9.2095, -9.2863, -9.4186,
+ -9.5064, -9.6386, -9.7207, -9.8286, -9.9453, -10.0701, -10.1735, -10.3001, -10.2858, -10.5427,
+ -10.5982, -10.7361, -10.7042, -10.9212, -11.0097, -11.0469, -11.1155, -11.2812, -11.3472, -11.4988,
+ -11.5327, -11.6692, -11.9376, -11.8606, -12.1372, -13.2539,
+ 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000,
+ 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000,
+ 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000,
+ 0.9999, 0.9999, 0.9999, 0.9999, 0.9999, 0.9999, 0.9999, 0.9999, 0.9999, 0.9999,
+ 0.9999, 0.9998, 0.9998, 0.9998, 0.9998, 0.9997, 0.9997, 0.9997, 0.9997, 0.9996,
+ 0.9996, 0.9995, 0.9995, 0.9994, 0.9994, 0.9993, 0.9992, 0.9991, 0.9991, 0.9989,
+ 0.9988, 0.9986, 0.9985, 0.9983, 0.9981, 0.9980, 0.9977, 0.9974, 0.9971, 0.9968,
+ 0.9965, 0.9962, 0.9956, 0.9950, 0.9948, 0.9941, 0.9933, 0.9926, 0.9919, 0.9910,
+ 0.9899, 0.9889, 0.9877, 0.9863, 0.9845, 0.9829, 0.9811, 0.9791, 0.9769, 0.9741,
+ 0.9716, 0.9684, 0.9645, 0.9611, 0.9563, 0.9519, 0.9463, 0.9406, 0.9344, 0.9272,
+ 0.9197, 0.9107, 0.9016, 0.8903, 0.8791, 0.8653, 0.8523, 0.8357, 0.8179, 0.7988,
+ 0.7779, 0.7562, 0.7318, 0.7024, 0.6753, 0.6435, 0.6089, 0.5700, 0.5296, 0.4860,
+ 0.4366, 0.3855, 0.3301, 0.2735, 0.2114, 0.1443, 0.0682, 0.0000, -0.0715, -0.1604,
+ -0.2478, -0.3377, -0.4287, -0.5277, -0.6291, -0.7384, -0.8457, -0.9559, -1.0742, -1.1913,
+ -1.3110, -1.4238, -1.5594, -1.6854, -1.8093, -1.9414, -2.0763, -2.2160, -2.3611, -2.4876,
+ -2.6374, -2.7710, -2.9225, -3.0591, -3.2077, -3.3452, -3.4916, -3.6316, -3.7735, -3.9296,
+ -4.0682, -4.2334, -4.3607, -4.5270, -4.6807, -4.8108, -4.9753, -5.1212, -5.2631, -5.4042,
+ -5.5510, -5.7227, -5.8794, -6.0244, -6.1677, -6.3271, -6.4862, -6.6130, -6.7449, -6.9250,
+ -7.1232, -7.1736, -7.3628, -7.5596, -7.6906, -7.8129, -7.9817, -8.1440, -8.3016, -8.4797,
+ -8.5734, -8.7692, -8.9198, -9.0610, -9.1746, -9.3536, -9.5939, -9.6957, -9.8475, -9.9639,
+ -10.1730, -10.2427, -10.4573, -10.5413, -10.7303, -10.9339, -11.0215, -11.2047, -11.2894, -11.4572,
+ -11.6256, -11.7794, -11.8801, -12.1717, -12.2354, -12.3686, -12.6195, -12.6527, -12.8247, -12.9560,
+ -13.3265, -13.1667, -13.4274, -13.6064, -13.5515, -13.9501, -13.9926, -14.4049, -14.1653, -14.4348,
+ -14.7983, -14.7807, -15.2349, -15.3536, -15.3026, -15.2739, -15.7170, -16.2161, -15.9185, -15.9490,
+ -16.6258, -16.5568, -16.4318, -16.7999, -16.4101, -17.6393, -17.7643, -17.2644, -17.5973, -17.0403,
+ -17.7039, -18.0073, -18.1840, -18.3848, -18.6286, -20.7063};
diff --git a/lib/wsprd/mettab.c b/lib/wsprd/mettab.c
new file mode 100644
index 000000000..f188c0cc7
--- /dev/null
+++ b/lib/wsprd/mettab.c
@@ -0,0 +1,76 @@
+/*
+ This file is part of wsprd.
+
+ File name: mettab.c
+ Description: Metric table for sequential Fano decoder.
+
+ Copyright 2008-2015, Joseph Taylor, K1JT
+ License: GNU GPL v3
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+int mettab[2][256]={
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 2,
+ 2, 2, 2, 2, 1, 1, 1, 1, 0, 0,
+ -1, -1, -1, -2, -2, -3, -4, -4, -5, -6,
+ -7, -7, -8, -9, -10, -11, -12, -12, -13, -14,
+ -15, -16, -17, -17, -18, -19, -20, -21, -22, -22,
+ -23, -24, -25, -26, -26, -27, -28, -29, -30, -30,
+ -31, -32, -33, -33, -34, -35, -36, -36, -37, -38,
+ -38, -39, -40, -41, -41, -42, -43, -43, -44, -45,
+ -45, -46, -47, -47, -48, -49, -49, -50, -51, -51,
+ -52, -53, -53, -54, -54, -55, -56, -56, -57, -57,
+ -58, -59, -59, -60, -60, -61, -62, -62, -62, -63,
+ -64, -64, -65, -65, -66, -67, -67, -67, -68, -69,
+ -69, -70, -70, -71, -72, -72, -72, -72, -73, -74,
+ -75, -75, -75, -77, -76, -76, -78, -78, -80, -81,
+ -80, -79, -83, -82, -81, -82, -82, -83, -84, -84,
+ -84, -87, -86, -87, -88,-105, -94,-105, -88, -87,
+ -86, -87, -84, -84, -84, -83, -82, -82, -81, -82,
+ -83, -79, -80, -81, -80, -78, -78, -76, -76, -77,
+ -75, -75, -75, -74, -73, -72, -72, -72, -72, -71,
+ -70, -70, -69, -69, -68, -67, -67, -67, -66, -65,
+ -65, -64, -64, -63, -62, -62, -62, -61, -60, -60,
+ -59, -59, -58, -57, -57, -56, -56, -55, -54, -54,
+ -53, -53, -52, -51, -51, -50, -49, -49, -48, -47,
+ -47, -46, -45, -45, -44, -43, -43, -42, -41, -41,
+ -40, -39, -38, -38, -37, -36, -36, -35, -34, -33,
+ -33, -32, -31, -30, -30, -29, -28, -27, -26, -26,
+ -25, -24, -23, -22, -22, -21, -20, -19, -18, -17,
+ -17, -16, -15, -14, -13, -12, -12, -11, -10, -9,
+ -8, -7, -7, -6, -5, -4, -4, -3, -2, -2,
+ -1, -1, -1, 0, 0, 1, 1, 1, 1, 2,
+ 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5 };
diff --git a/lib/wsprd/nhash.c b/lib/wsprd/nhash.c
new file mode 100644
index 000000000..1985da9ef
--- /dev/null
+++ b/lib/wsprd/nhash.c
@@ -0,0 +1,376 @@
+/*
+ This file is part of wsprd.
+
+ File name: nhash.c
+
+ *------------------------------------------------------------------------------
+ *
+ * This file is part of the WSPR application, Weak Signal Propogation Reporter
+ *
+ * File Name: nhash.c
+ * Description: Functions to produce 32-bit hashes for hash table lookup
+ *
+ * Copyright (C) 2008-2014 Joseph Taylor, K1JT
+ * License: GNU GPL v3+
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ * Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Files: lookup3.c
+ * Copyright: Copyright (C) 2006 Bob Jenkins
+ * License: public-domain
+ * You may use this code any way you wish, private, educational, or commercial.
+ * It's free.
+ *
+ *-------------------------------------------------------------------------------
+*/
+
+/*
+These are functions for producing 32-bit hashes for hash table lookup.
+hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+are externally useful functions. Routines to test the hash are included
+if SELF_TEST is defined. You can use this free for any purpose. It's in
+the public domain. It has no warranty.
+
+You probably want to use hashlittle(). hashlittle() and hashbig()
+hash byte arrays. hashlittle() is is faster than hashbig() on
+little-endian machines. Intel and AMD are little-endian machines.
+On second thought, you probably want hashlittle2(), which is identical to
+hashlittle() except it returns two 32-bit hashes for the price of one.
+You could implement hashbig2() if you wanted but I haven't bothered here.
+
+If you want to find a hash of, say, exactly 7 integers, do
+ a = i1; b = i2; c = i3;
+ mix(a,b,c);
+ a += i4; b += i5; c += i6;
+ mix(a,b,c);
+ a += i7;
+ final(a,b,c);
+then use c as the hash value. If you have a variable length array of
+4-byte integers to hash, use hashword(). If you have a byte array (like
+a character string), use hashlittle(). If you have several byte arrays, or
+a mix of things, see the comments above hashlittle().
+
+Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
+then mix those integers. This is fast (you can do a lot more thorough
+mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+*/
+
+#define SELF_TEST 1
+
+#include /* defines printf for tests */
+#include /* defines time_t for timings in the test */
+#ifdef Win32
+#include "win_stdint.h" /* defines uint32_t etc */
+#else
+#include /* defines uint32_t etc */
+#endif
+//#include /* attempt to define endianness */
+//#ifdef linux
+//# include /* attempt to define endianness */
+//#endif
+
+#define HASH_LITTLE_ENDIAN 1
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+-------------------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+
+This is reversible, so any information in (a,b,c) before mix() is
+still in (a,b,c) after mix().
+
+If four pairs of (a,b,c) inputs are run through mix(), or through
+mix() in reverse, there are at least 32 bits of the output that
+are sometimes the same for one pair and different for another pair.
+This was tested for:
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+satisfy this are
+ 4 6 8 16 19 4
+ 9 15 3 18 27 15
+ 14 9 3 7 17 3
+Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+for "differ" defined as + with a one-bit base and a two-bit delta. I
+used http://burtleburtle.net/bob/hash/avalanche.html to choose
+the operations, constants, and arrangements of the variables.
+
+This does not achieve avalanche. There are input bits of (a,b,c)
+that fail to affect some output bits of (a,b,c), especially of a. The
+most thoroughly mixed value is c, but it doesn't really even achieve
+avalanche in c.
+
+This allows some parallelism. Read-after-writes are good at doubling
+the number of bits affected, so the goal of mixing pulls in the opposite
+direction as the goal of parallelism. I did what I could. Rotates
+seem to cost as much as shifts on every machine I could lay my hands
+on, and rotates are much kinder to the top and bottom bits, so I used
+rotates.
+-------------------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/*
+-------------------------------------------------------------------------------
+final -- final mixing of 3 32-bit values (a,b,c) into c
+
+Pairs of (a,b,c) values differing in only a few bits will usually
+produce values of c that look totally different. This was tested for
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+These constants passed:
+ 14 11 25 16 4 14 24
+ 12 14 25 16 4 14 24
+and these came close:
+ 4 8 15 26 3 22 24
+ 10 8 15 26 3 22 24
+ 11 8 15 26 3 22 24
+-------------------------------------------------------------------------------
+*/
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/*
+-------------------------------------------------------------------------------
+hashlittle() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ length : the length of the key, counting by bytes
+ initval : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Two keys differing by one or two bits will have
+totally different hash values.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+ for (i=0, h=0; i 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ */
+#ifndef VALGRIND
+
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : return c;
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : return c; /* zero length requires no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ c=(32767&c);
+
+ return c;
+}
diff --git a/lib/wsprd/t1.f90 b/lib/wsprd/t1.f90
new file mode 100644
index 000000000..a56260e8e
--- /dev/null
+++ b/lib/wsprd/t1.f90
@@ -0,0 +1,63 @@
+program t1
+
+ integer mettab(0:255,0:1)
+ data mettab/ &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, &
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, &
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, &
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, &
+ 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, &
+ -1, -1, -1, -2, -2, -3, -4, -4, -5, -6, &
+ -7, -7, -8, -9, -10, -11, -12, -12, -13, -14, &
+ -15, -16, -17, -17, -18, -19, -20, -21, -22, -22, &
+ -23, -24, -25, -26, -26, -27, -28, -29, -30, -30, &
+ -31, -32, -33, -33, -34, -35, -36, -36, -37, -38, &
+ -38, -39, -40, -41, -41, -42, -43, -43, -44, -45, &
+ -45, -46, -47, -47, -48, -49, -49, -50, -51, -51, &
+ -52, -53, -53, -54, -54, -55, -56, -56, -57, -57, &
+ -58, -59, -59, -60, -60, -61, -62, -62, -62, -63, &
+ -64, -64, -65, -65, -66, -67, -67, -67, -68, -69, &
+ -69, -70, -70, -71, -72, -72, -72, -72, -73, -74, &
+ -75, -75, -75, -77, -76, -76, -78, -78, -80, -81, &
+ -80, -79, -83, -82, -81, -82, -82, -83, -84, -84, &
+ -84, -87, -86, -87, -88, -89, -89, -89, -88, -87, &
+ -86, -87, -84, -84, -84, -83, -82, -82, -81, -82, &
+ -83, -79, -80, -81, -80, -78, -78, -76, -76, -77, &
+ -75, -75, -75, -74, -73, -72, -72, -72, -72, -71, &
+ -70, -70, -69, -69, -68, -67, -67, -67, -66, -65, &
+ -65, -64, -64, -63, -62, -62, -62, -61, -60, -60, &
+ -59, -59, -58, -57, -57, -56, -56, -55, -54, -54, &
+ -53, -53, -52, -51, -51, -50, -49, -49, -48, -47, &
+ -47, -46, -45, -45, -44, -43, -43, -42, -41, -41, &
+ -40, -39, -38, -38, -37, -36, -36, -35, -34, -33, &
+ -33, -32, -31, -30, -30, -29, -28, -27, -26, -26, &
+ -25, -24, -23, -22, -22, -21, -20, -19, -18, -17, &
+ -17, -16, -15, -14, -13, -12, -12, -11, -10, -9, &
+ -8, -7, -7, -6, -5, -4, -4, -3, -2, -2, &
+ -1, -1, -1, 0, 0, 1, 1, 1, 1, 2, &
+ 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, &
+ 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, &
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, &
+ 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, &
+ 5, 5/
+
+ do i=0,255
+ write(*,1010) i,mettab(i,0),mettab(i,1)
+1010 format(3i6)
+ enddo
+
+end program t1
diff --git a/lib/wsprd/t2.f90 b/lib/wsprd/t2.f90
new file mode 100644
index 000000000..0f214db41
--- /dev/null
+++ b/lib/wsprd/t2.f90
@@ -0,0 +1,11 @@
+program t2
+
+ df=375.0/65536.0
+ do i=1,65536
+ w=1.0/(1.0 + ((i-32768)/26214.0)**20)
+ f=(i-32768)*df
+ write(13,1010) f,w
+1010 format(2f15.6)
+ enddo
+
+end program t2
diff --git a/lib/wsprd/tab.c b/lib/wsprd/tab.c
new file mode 100644
index 000000000..e330c7576
--- /dev/null
+++ b/lib/wsprd/tab.c
@@ -0,0 +1,41 @@
+/*
+ This file is part of wsprd.
+
+ File name: tab.c
+ Description: 8-bit parity lookup table.
+*/
+unsigned char Partab[] = {
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 0, 1, 1, 0, 1, 0, 0, 1,
+ 1, 0, 0, 1, 0, 1, 1, 0,
+};
+
diff --git a/lib/wsprd/test_wspr.f90 b/lib/wsprd/test_wspr.f90
new file mode 100644
index 000000000..86e291868
--- /dev/null
+++ b/lib/wsprd/test_wspr.f90
@@ -0,0 +1,61 @@
+program test_wspr
+
+! This program provides examples of the source encoding, convolutional
+! error-control coding, bit and symbol ordering, and synchronizing
+! information contained in WSPR messages.
+
+ character*22 msg,msg2
+ character*23 msg3
+ character*1 err2,err3
+ integer*1 data0(11)
+ logical lfile
+
+! Get command-line argument(s)
+ nargs=iargc()
+ if(nargs.ne.1) then
+ print*,'Usage: test_wspr "message"'
+ go to 999
+ endif
+ call getarg(1,msg) !Get message from command line
+ call unpk(data0,1,msg3) !Read the C hashtable
+ lfile=msg(1:2).eq."-t"
+ if(lfile) open(10,file="messages.txt",status="old")
+
+ do imsg=1,999
+ if(lfile) read(10,1001,end=900) msg
+1001 format(a22)
+
+ data0=0
+ call wqencode(msg,ntype0,data0) !Source encoding
+! write(*,1002) data0(1:7)
+!1002 format('Source-encoded message (50 bits, hex):',7z3.2)
+! data0(8:11)=0
+
+ call wqdecode(data0,msg2,ntype1)
+
+! write(*,1020) ntype1
+!1020 format('Message type: ',i7)
+! write(*,1030) msg2
+!1030 format('Decoded message: ',a22)
+
+ call unpk(data0,0,msg3)
+ do i=1,23
+ if(ichar(msg3(i:i)).eq.0) then
+ msg3(i:)=" "
+ exit
+ endif
+ enddo
+
+ err2=' '
+ err3=' '
+ if(msg2.ne.msg) err2='*'
+ if(msg3.ne.msg) err3='*'
+
+ write(*,1040) msg,err2,msg2,err3,msg3
+1040 format(a22,1x,a1,1x,a22,1x,a1,1x,a22)
+ if(.not.lfile) exit
+ enddo
+900 call unpk(data0,2,msg3)
+
+
+999 end program test_wspr
diff --git a/lib/wsprd/unpk.c b/lib/wsprd/unpk.c
new file mode 100644
index 000000000..51207cb98
--- /dev/null
+++ b/lib/wsprd/unpk.c
@@ -0,0 +1,123 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "wsprd_utils.h"
+
+unsigned int nhash_( const void *key, size_t length, uint32_t initval);
+
+void unpk_(signed char message[], int *nhashtab, char call_loc_pow[])
+{
+ int i,n1,n2,n3,ndbm,ihash,nadd,noprint,nh;
+ char callsign[13],grid[5],grid6[7],cdbm[3];
+ static char hashtab[32768][13];
+ FILE *fhash;
+
+ if(*nhashtab==1) {
+ char line[80], hcall[12];
+ if( (fhash=fopen("hashtable.txt","r+")) ) {
+ while (fgets(line, sizeof(line), fhash) != NULL) {
+ sscanf(line,"%d %s",&nh,hcall);
+ strcpy(*hashtab+nh*13,hcall);
+ }
+ } else {
+ fhash=fopen("hashtable.txt","w+");
+ }
+ fclose(fhash);
+ return;
+ }
+
+ if(*nhashtab==2) {
+ fhash=fopen("hashtable.txt","w");
+ for (i=0; i<32768; i++) {
+ if( strncmp(hashtab[i],"\0",1) != 0 ) {
+ fprintf(fhash,"%5d %s\n",i,*hashtab+i*13);
+ }
+ }
+ fclose(fhash);
+ return;
+ }
+
+ unpack50(message,&n1,&n2);
+ unpackcall(n1,callsign);
+ unpackgrid(n2, grid);
+ int ntype = (n2&127) - 64;
+ callsign[12]=0;
+ grid[4]=0;
+
+/*
+ Based on the value of ntype, decide whether this is a Type 1, 2, or
+ 3 message.
+
+ * Type 1: 6 digit call, grid, power - ntype is positive and is a member
+ of the set {0,3,7,10,13,17,20...60}
+
+ * Type 2: extended callsign, power - ntype is positive but not
+ a member of the set of allowed powers
+
+ * Type 3: hash, 6 digit grid, power - ntype is negative.
+*/
+
+ if( (ntype >= 0) && (ntype <= 62) ) {
+ int nu=ntype%10;
+ if( nu == 0 || nu == 3 || nu == 7 ) {
+ ndbm=ntype;
+ memset(call_loc_pow,0,sizeof(char)*23);
+ sprintf(cdbm,"%2d",ndbm);
+ strncat(call_loc_pow,callsign,strlen(callsign));
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,grid,4);
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,cdbm,2);
+ strncat(call_loc_pow,"\0",1);
+ ihash=nhash_(callsign,strlen(callsign),(uint32_t)146);
+ strcpy(*hashtab+ihash*13,callsign);
+ } else {
+ nadd=nu;
+ if( nu > 3 ) nadd=nu-3;
+ if( nu > 7 ) nadd=nu-7;
+ n3=n2/128+32768*(nadd-1);
+ unpackpfx(n3,callsign);
+ ndbm=ntype-nadd;
+ memset(call_loc_pow,0,sizeof(char)*23);
+ sprintf(cdbm,"%2d",ndbm);
+ strncat(call_loc_pow,callsign,strlen(callsign));
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,cdbm,2);
+ strncat(call_loc_pow,"\0",1);
+ ihash=nhash_(callsign,strlen(callsign),(uint32_t)146);
+ strcpy(*hashtab+ihash*13,callsign);
+ noprint=0;
+ }
+ } else if ( ntype < 0 ) {
+ ndbm=-(ntype+1);
+ memset(grid6,0,sizeof(char)*7);
+ strncat(grid6,callsign+5,1);
+ strncat(grid6,callsign,5);
+ ihash=(n2-ntype-64)/128;
+ if( strncmp(hashtab[ihash],"\0",1) != 0 ) {
+ sprintf(callsign,"<%s>",hashtab[ihash]);
+ } else {
+ sprintf(callsign,"%5s","<...>");
+ }
+
+ memset(call_loc_pow,0,sizeof(char)*23);
+ sprintf(cdbm,"%2d",ndbm);
+ strncat(call_loc_pow,callsign,strlen(callsign));
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,grid6,strlen(grid6));
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,cdbm,2);
+ strncat(call_loc_pow,"\0",1);
+
+ noprint=0;
+
+// I don't know what to do with these... They show up as "A000AA" grids.
+ if( ntype == -64 ) noprint=1;
+ }
+ // printf("\nUnpacked in C: %s\n",call_loc_pow);
+}
diff --git a/lib/wsprd/wsprd.c b/lib/wsprd/wsprd.c
new file mode 100644
index 000000000..db0c29ecf
--- /dev/null
+++ b/lib/wsprd/wsprd.c
@@ -0,0 +1,935 @@
+/*
+ This file is part of program wsprd, a detector/demodulator/decoder
+ for the Weak Signal Propagation Reporter (WSPR) mode.
+
+ File name: wsprd.c
+
+ Copyright 2001-2015, Joe Taylor, K1JT
+
+ Much of the present code is based on work by Steven Franke, K9AN,
+ which in turn was based on earlier work by K1JT.
+
+ Copyright 2014-2015, Steven Franke, K9AN
+
+ License: GNU GPL v3
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "fano.h"
+#include "wsprd_utils.h"
+
+#define max(x,y) ((x) > (y) ? (x) : (y))
+// Possible PATIENCE options: FFTW_ESTIMATE, FFTW_ESTIMATE_PATIENT,
+// FFTW_MEASURE, FFTW_PATIENT, FFTW_EXHAUSTIVE
+#define PATIENCE FFTW_ESTIMATE
+fftw_plan PLAN1,PLAN2,PLAN3;
+
+unsigned char pr3[162]=
+{1,1,0,0,0,0,0,0,1,0,0,0,1,1,1,0,0,0,1,0,
+ 0,1,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,
+ 0,0,0,0,0,0,1,0,1,1,0,0,1,1,0,1,0,0,0,1,
+ 1,0,1,0,0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,
+ 0,0,1,0,1,1,0,0,0,1,1,0,1,0,1,0,0,0,1,0,
+ 0,0,0,0,1,0,0,1,0,0,1,1,1,0,1,1,0,0,1,1,
+ 0,1,0,0,0,1,1,1,0,0,0,0,0,1,0,1,0,0,1,1,
+ 0,0,0,0,0,0,0,1,1,0,1,0,1,1,0,0,0,1,1,0,
+ 0,0};
+
+unsigned long nr;
+
+//***************************************************************************
+unsigned long readc2file(char *ptr_to_infile, double *idat, double *qdat,
+ double *freq, int *wspr_type)
+{
+ float buffer[2*65536];
+ double dfreq;
+ int i,ntrmin;
+ char *c2file[15];
+ FILE* fp;
+
+ fp = fopen(ptr_to_infile,"rb");
+ if (fp == NULL) {
+ fprintf(stderr, "Cannot open data file '%s'\n", ptr_to_infile);
+ return 1;
+ }
+ unsigned long nread=fread(c2file,sizeof(char),14,fp);
+ nread=fread(&ntrmin,sizeof(int),1,fp);
+ nread=fread(&dfreq,sizeof(double),1,fp);
+ *freq=dfreq;
+ nread=fread(buffer,sizeof(float),2*45000,fp);
+
+ *wspr_type=ntrmin;
+
+ for(i=0; i<45000; i++) {
+ idat[i]=buffer[2*i];
+ qdat[i]=-buffer[2*i+1];
+ }
+
+ if( nread == 2*45000 ) {
+ return nread/2;
+ } else {
+ return 1;
+ }
+}
+
+//***************************************************************************
+unsigned long readwavfile(char *ptr_to_infile, int ntrmin, double *idat, double *qdat )
+{
+ unsigned long i, j, npoints;
+ int nfft1, nfft2, nh2, i0;
+ double df;
+
+ nfft2=46080; //this is the number of downsampled points that will be returned
+ nh2=nfft2/2;
+
+ if( ntrmin == 2 ) {
+ nfft1=nfft2*32; //need to downsample by a factor of 32
+ df=12000.0/nfft1;
+ i0=1500.0/df+0.5;
+ npoints=114*12000;
+ } else if ( ntrmin == 15 ) {
+ nfft1=nfft2*8*32;
+ df=12000.0/nfft1;
+ i0=(1500.0+112.5)/df+0.5;
+ npoints=8*114*12000;
+ } else {
+ fprintf(stderr,"This should not happen\n");
+ return 1;
+ }
+
+ double *realin;
+ fftw_complex *fftin, *fftout;
+
+ FILE *fp;
+ short int *buf2;
+ buf2 = malloc(npoints*sizeof(short int));
+
+ fp = fopen(ptr_to_infile,"rb");
+ if (fp == NULL) {
+ fprintf(stderr, "Cannot open data file '%s'\n", ptr_to_infile);
+ return 1;
+ }
+ nr=fread(buf2,2,22,fp); //Read and ignore header
+ nr=fread(buf2,2,npoints,fp); //Read raw data
+ fclose(fp);
+
+ realin=(double*) fftw_malloc(sizeof(double)*nfft1);
+ fftout=(fftw_complex*) fftw_malloc(sizeof(fftw_complex)*nfft1);
+ PLAN1 = fftw_plan_dft_r2c_1d(nfft1, realin, fftout, PATIENCE);
+
+ for (i=0; inh2 ) j=j-nfft2;
+ fftin[i][0]=fftout[j][0];
+ fftin[i][1]=fftout[j][1];
+ }
+
+ fftw_free(fftout);
+ fftout=(fftw_complex*) fftw_malloc(sizeof(fftw_complex)*nfft2);
+ PLAN2 = fftw_plan_dft_1d(nfft2, fftin, fftout, FFTW_BACKWARD, PATIENCE);
+ fftw_execute(PLAN2);
+
+ for (i=0; i0) & (k syncmax ) { //Save best parameters
+ syncmax=ss/totp;
+ best_shift=lag;
+ fbest=f0;
+ }
+ } // lag loop
+ } //freq loop
+
+ if( mode <=1 ) { //Send best params back to caller
+ *sync=syncmax;
+ *shift1=best_shift;
+ *f1=fbest;
+ return;
+ }
+
+ if( mode == 2 ) {
+ *sync=syncmax;
+ for (i=0; i<162; i++) { //Normalize the soft symbols
+ fsum=fsum+fsymb[i]/162.0;
+ f2sum=f2sum+fsymb[i]*fsymb[i]/162.0;
+ }
+ fac=sqrt(f2sum-fsum*fsum);
+ for (i=0; i<162; i++) {
+ fsymb[i]=symfac*fsymb[i]/fac;
+ if( fsymb[i] > 127) fsymb[i]=127.0;
+ if( fsymb[i] < -128 ) fsymb[i]=-128.0;
+ symbols[i]=fsymb[i] + 128;
+ }
+ return;
+ }
+ return;
+}
+
+//***************************************************************************
+void usage(void)
+{
+ printf("Usage: wsprd [options...] infile\n");
+ printf(" infile must have suffix .wav or .c2\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -a path to writeable data files, default=\".\"\n");
+ printf(" -e x (x is transceiver dial frequency error in Hz)\n");
+ printf(" -f x (x is transceiver dial frequency in MHz)\n");
+ // blanking is not yet implemented. The options are accepted for compatibility
+ // with development version of wsprd.
+ // printf(" -t n (n is blanking duration in milliseconds)\n");
+ // printf(" -b n (n is pct of time that is blanked)\n");
+ printf(" -H do not use (or update) the hash table\n");
+ printf(" -m decode wspr-15 .wav file\n");
+ printf(" -n write noise estimates to file noise.dat\n");
+ printf(" -q quick mode - doesn't dig deep for weak signals\n");
+ printf(" -s slow mode - much slower, yields a few more decodes\n");
+ printf(" -v verbose mode\n");
+ printf(" -w wideband mode - decode signals within +/- 150 Hz of center\n");
+ printf(" -z x (x is fano metric table bias, default is 0.42)\n");
+}
+
+//***************************************************************************
+int main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ int i,j,k;
+ unsigned char *symbols, *decdata;
+ signed char message[]={-9,13,-35,123,57,-39,64,0,0,0,0};
+ char *callsign,*grid,*grid6, *call_loc_pow, *cdbm;
+ char *ptr_to_infile,*ptr_to_infile_suffix;
+ char *data_dir=NULL;
+ char wisdom_fname[200],all_fname[200],spots_fname[200];
+ char timer_fname[200],hash_fname[200];
+ char uttime[5],date[7];
+ int c,delta,maxpts=65536,verbose=0,quickmode=0,writenoise=0,usehashtable=1,wspr_type=2;
+ int shift1, lagmin, lagmax, lagstep, worth_a_try, not_decoded;
+ unsigned int nbits;
+ unsigned int npoints, metric, maxcycles, cycles, maxnp;
+ float df=375.0/256.0/2;
+ float freq0[200],snr0[200],drift0[200],sync0[200];
+ int shift0[200];
+ float dt=1.0/375.0, dt_print;
+ double dialfreq_cmdline=0.0, dialfreq, freq_print;
+ float dialfreq_error=0.0;
+ float fmin=-110, fmax=110;
+ float f1, fstep, sync1, drift1, tblank=0, fblank=0;
+ double *idat, *qdat;
+ clock_t t0,t00;
+ double tfano=0.0,treadwav=0.0,tcandidates=0.0,tsync0=0.0;
+ double tsync1=0.0,tsync2=0.0,ttotal=0.0;
+
+ // Parameters used for performance-tuning:
+ maxcycles=10000; //Fano timeout limit
+ double minsync1=0.10; //First sync limit
+ double minsync2=0.12; //Second sync limit
+ int iifac=3; //Step size in final DT peakup
+ int symfac=50; //Soft-symbol normalizing factor
+ int maxdrift=4; //Maximum (+/-) drift
+ double minrms=52.0 * (symfac/64.0); //Final test for plausible decoding
+ delta=60; //Fano threshold step
+
+ t00=clock();
+ fftw_complex *fftin, *fftout;
+#include "./metric_tables.c"
+
+ int mettab[2][256];
+ float bias=0.42;
+
+ idat=malloc(sizeof(double)*maxpts);
+ qdat=malloc(sizeof(double)*maxpts);
+
+ while ( (c = getopt(argc, argv, "a:b:e:f:Hmnqst:wvz:")) !=-1 ) {
+ switch (c) {
+ case 'a':
+ data_dir = optarg;
+ break;
+ case 'b':
+ fblank = strtof(optarg,NULL);
+ break;
+ case 'e':
+ dialfreq_error = strtof(optarg,NULL); // units of Hz
+ // dialfreq_error = dial reading - actual, correct frequency
+ break;
+ case 'f':
+ dialfreq_cmdline = strtod(optarg,NULL); // units of MHz
+ break;
+ case 'H':
+ usehashtable = 0;
+ break;
+ case 'm':
+ wspr_type = 15;
+ break;
+ case 'n':
+ writenoise = 1;
+ break;
+ case 'q':
+ quickmode = 1;
+ break;
+ case 's':
+ maxcycles=20000;
+ iifac=1;
+ break;
+ case 't':
+ tblank = strtof(optarg,NULL);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'w':
+ fmin=-150.0;
+ fmax=150.0;
+ break;
+ case 'z':
+ bias=strtof(optarg,NULL); //fano metric bias (default is 0.42)
+ break;
+ case '?':
+ usage();
+ return 1;
+ }
+ }
+
+ if( optind+1 > argc) {
+ usage();
+ return 1;
+ } else {
+ ptr_to_infile=argv[optind];
+ }
+
+ // setup metric table
+ for(i=0; i<256; i++) {
+ mettab[0][i]=round( 10*(metric_tables[2][i]-bias) );
+ mettab[1][i]=round( 10*(metric_tables[2][255-i]-bias) );
+ }
+
+ FILE *fp_fftw_wisdom_file, *fall_wspr, *fwsprd, *fhash, *ftimer;
+ strcpy(wisdom_fname,".");
+ strcpy(all_fname,".");
+ strcpy(spots_fname,".");
+ strcpy(timer_fname,".");
+ strcpy(hash_fname,".");
+ if(data_dir != NULL) {
+ strcpy(wisdom_fname,data_dir);
+ strcpy(all_fname,data_dir);
+ strcpy(spots_fname,data_dir);
+ strcpy(timer_fname,data_dir);
+ strcpy(hash_fname,data_dir);
+ }
+ strncat(wisdom_fname,"/wspr_wisdom.dat",20);
+ strncat(all_fname,"/ALL_WSPR.TXT",20);
+ strncat(spots_fname,"/wspr_spots.txt",20);
+ strncat(timer_fname,"/wspr_timer.out",20);
+ strncat(hash_fname,"/hashtable.txt",20);
+ if ((fp_fftw_wisdom_file = fopen(wisdom_fname, "r"))) { //Open FFTW wisdom
+ fftw_import_wisdom_from_file(fp_fftw_wisdom_file);
+ fclose(fp_fftw_wisdom_file);
+ }
+
+ fall_wspr=fopen(all_fname,"a");
+ fwsprd=fopen(spots_fname,"w");
+ // FILE *fdiag;
+ // fdiag=fopen("wsprd_diag","a");
+
+ if((ftimer=fopen(timer_fname,"r"))) {
+ //Accumulate timing data
+ nr=fscanf(ftimer,"%lf %lf %lf %lf %lf %lf %lf",
+ &treadwav,&tcandidates,&tsync0,&tsync1,&tsync2,&tfano,&ttotal);
+ fclose(ftimer);
+ }
+ ftimer=fopen(timer_fname,"w");
+
+ if( strstr(ptr_to_infile,".wav") ) {
+ ptr_to_infile_suffix=strstr(ptr_to_infile,".wav");
+
+ t0 = clock();
+ npoints=readwavfile(ptr_to_infile, wspr_type, idat, qdat);
+ treadwav += (double)(clock()-t0)/CLOCKS_PER_SEC;
+
+ if( npoints == 1 ) {
+ return 1;
+ }
+ dialfreq=dialfreq_cmdline - (dialfreq_error*1.0e-06);
+ } else if ( strstr(ptr_to_infile,".c2") !=0 ) {
+ ptr_to_infile_suffix=strstr(ptr_to_infile,".c2");
+ npoints=readc2file(ptr_to_infile, idat, qdat, &dialfreq, &wspr_type);
+ if( npoints == 1 ) {
+ return 1;
+ }
+ dialfreq -= (dialfreq_error*1.0e-06);
+ } else {
+ printf("Error: Failed to open %s\n",ptr_to_infile);
+ printf("WSPR file must have suffix .wav or .c2\n");
+ return 1;
+ }
+
+ // Parse date and time from given filename
+ strncpy(date,ptr_to_infile_suffix-11,6);
+ strncpy(uttime,ptr_to_infile_suffix-4,4);
+ date[6]='\0';
+ uttime[4]='\0';
+
+ // Do windowed ffts over 2 symbols, stepped by half symbols
+ int nffts=4*floor(npoints/512)-1;
+ fftin=(fftw_complex*) fftw_malloc(sizeof(fftw_complex)*512);
+ fftout=(fftw_complex*) fftw_malloc(sizeof(fftw_complex)*512);
+ PLAN3 = fftw_plan_dft_1d(512, fftin, fftout, FFTW_FORWARD, PATIENCE);
+
+ float ps[512][nffts];
+ float w[512];
+ for(i=0; i<512; i++) {
+ w[i]=sin(0.006147931*i);
+ }
+
+ memset(ps,0.0, sizeof(float)*512*nffts);
+ for (i=0; i511 )
+ k=k-512;
+ ps[j][i]=fftout[k][0]*fftout[k][0]+fftout[k][1]*fftout[k][1];
+ }
+ }
+
+ fftw_free(fftin);
+ fftw_free(fftout);
+
+ // Compute average spectrum
+ float psavg[512];
+ memset(psavg,0.0, sizeof(float)*512);
+ for (i=0; ismspec[j-1]) && (smspec[j]>smspec[j+1]) && (npk<200)) {
+ freq0[npk]=(j-205)*df;
+ snr0[npk]=10*log10(smspec[j])-snr_scaling_factor;
+ npk++;
+ }
+ }
+
+ // Compute corrected fmin, fmax, accounting for dial frequency error
+ fmin += dialfreq_error; // dialfreq_error is in units of Hz
+ fmax += dialfreq_error;
+
+ // Don't waste time on signals outside of the range [fmin,fmax].
+ i=0;
+ for( j=0; j= fmin && freq0[j] <= fmax ) {
+ freq0[i]=freq0[j];
+ snr0[i]=snr0[j];
+ i++;
+ }
+ }
+ npk=i;
+
+ t0=clock();
+ /* Make coarse estimates of shift (DT), freq, and drift
+
+ * Look for time offsets up to +/- 8 symbols (about +/- 5.4 s) relative
+ to nominal start time, which is 2 seconds into the file
+
+ * Calculates shift relative to the beginning of the file
+
+ * Negative shifts mean that signal started before start of file
+
+ * The program prints DT = shift-2 s
+
+ * Shifts that cause sync vector to fall off of either end of the data
+ vector are accommodated by "partial decoding", such that missing
+ symbols produce a soft-decision symbol value of 128
+
+ * The frequency drift model is linear, deviation of +/- drift/2 over the
+ span of 162 symbols, with deviation equal to 0 at the center of the
+ signal vector.
+ */
+
+ int idrift,ifr,if0,ifd,k0;
+ int kindex;
+ float smax,ss,pow,p0,p1,p2,p3;
+ for(j=0; j smax ) { //Save coarse parameters
+ smax=sync1;
+ shift0[j]=128*(k0+1);
+ drift0[j]=idrift;
+ freq0[j]=(ifr-256)*df;
+ sync0[j]=sync1;
+ }
+ }
+ }
+ }
+ }
+ tcandidates += (double)(clock()-t0)/CLOCKS_PER_SEC;
+
+ nbits=81;
+ symbols=malloc(sizeof(char)*nbits*2);
+ memset(symbols,0,sizeof(char)*nbits*2);
+ decdata=malloc((nbits+7)/8);
+ grid=malloc(sizeof(char)*5);
+ grid6=malloc(sizeof(char)*7);
+ callsign=malloc(sizeof(char)*13);
+ call_loc_pow=malloc(sizeof(char)*23);
+ cdbm=malloc(sizeof(char)*3);
+ float allfreqs[npk];
+ memset(allfreqs,0,sizeof(float)*npk);
+ char allcalls[npk][13];
+ memset(allcalls,0,sizeof(char)*npk*13);
+ memset(grid,0,sizeof(char)*5);
+ memset(grid6,0,sizeof(char)*7);
+ memset(callsign,0,sizeof(char)*13);
+ memset(call_loc_pow,0,sizeof(char)*23);
+ memset(cdbm,0,sizeof(char)*3);
+ char hashtab[32768][13];
+ memset(hashtab,0,sizeof(char)*32768*13);
+ uint32_t nhash_( const void *, size_t, uint32_t);
+ int nh;
+
+ if( usehashtable ) {
+ char line[80], hcall[12];
+ if( (fhash=fopen(hash_fname,"r+")) ) {
+ while (fgets(line, sizeof(line), fhash) != NULL) {
+ sscanf(line,"%d %s",&nh,hcall);
+ strcpy(*hashtab+nh*13,hcall);
+ }
+ } else {
+ fhash=fopen(hash_fname,"w+");
+ }
+ fclose(fhash);
+ }
+
+ int uniques=0, noprint=0;
+ /*
+ Refine the estimates of freq, shift using sync as a metric.
+ Sync is calculated such that it is a float taking values in the range
+ [0.0,1.0].
+
+ Function sync_and_demodulate has three modes of operation
+ mode is the last argument:
+
+ 0 = no frequency or drift search. find best time lag.
+ 1 = no time lag or drift search. find best frequency.
+ 2 = no frequency or time lag search. Calculate soft-decision
+ symbols using passed frequency and shift.
+
+ NB: best possibility for OpenMP may be here: several worker threads
+ could each work on one candidate at a time.
+ */
+
+ for (j=0; j minsync1 ) {
+ worth_a_try = 1;
+ } else {
+ worth_a_try = 0;
+ }
+
+ int idt=0, ii=0, jiggered_shift;
+ double y,sq,rms;
+ not_decoded=1;
+
+ while ( worth_a_try && not_decoded && idt<=(128/iifac)) {
+ ii=(idt+1)/2;
+ if( idt%2 == 1 ) ii=-ii;
+ ii=iifac*ii;
+ jiggered_shift=shift1+ii;
+
+ // Use mode 2 to get soft-decision symbols
+ t0 = clock();
+ sync_and_demodulate(idat, qdat, npoints, symbols, &f1, fstep,
+ &jiggered_shift, lagmin, lagmax, lagstep, &drift1, symfac,
+ &sync1, 2);
+ tsync2 += (double)(clock()-t0)/CLOCKS_PER_SEC;
+
+ sq=0.0;
+ for(i=0; i<162; i++) {
+ y=(double)symbols[i] - 128.0;
+ sq += y*y;
+ }
+ rms=sqrt(sq/162.0);
+
+ if((sync1 > minsync2) && (rms > minrms)) {
+ deinterleave(symbols);
+ t0 = clock();
+ not_decoded = fano(&metric,&cycles,&maxnp,decdata,symbols,nbits,
+ mettab,delta,maxcycles);
+ tfano += (double)(clock()-t0)/CLOCKS_PER_SEC;
+
+ /* ### Used for timing tests:
+ if(not_decoded) fprintf(fdiag,
+ "%6s %4s %4.1f %3.0f %4.1f %10.7f %-18s %2d %5u %4d %6.1f %2d\n",
+ date,uttime,sync1*10,snr0[j], shift1*dt-2.0, dialfreq+(1500+f1)/1e6,
+ "@ ", (int)drift1, cycles/81, ii, rms, maxnp);
+ */
+ }
+ idt++;
+ if( quickmode ) break;
+ }
+
+ if( worth_a_try && !not_decoded ) {
+ for(i=0; i<11; i++) {
+ if( decdata[i]>127 ) {
+ message[i]=decdata[i]-256;
+ } else {
+ message[i]=decdata[i];
+ }
+ }
+
+ // Unpack the decoded message, update the hashtable, apply
+ // sanity checks on grid and power, and return
+ // call_loc_pow string and also callsign (for de-duping).
+ noprint=unpk_(message,hashtab,call_loc_pow,callsign);
+
+ // Remove dupes (same callsign and freq within 1 Hz)
+ int dupe=0;
+ for (i=0; i\n");
+
+ if ((fp_fftw_wisdom_file = fopen(wisdom_fname, "w"))) {
+ fftw_export_wisdom_to_file(fp_fftw_wisdom_file);
+ fclose(fp_fftw_wisdom_file);
+ }
+
+ ttotal += (double)(clock()-t00)/CLOCKS_PER_SEC;
+
+ fprintf(ftimer,"%7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f\n\n",
+ treadwav,tcandidates,tsync0,tsync1,tsync2,tfano,ttotal);
+
+ fprintf(ftimer,"Code segment Seconds Frac\n");
+ fprintf(ftimer,"-----------------------------------\n");
+ fprintf(ftimer,"readwavfile %7.2f %7.2f\n",treadwav,treadwav/ttotal);
+ fprintf(ftimer,"Coarse DT f0 f1 %7.2f %7.2f\n",tcandidates,
+ tcandidates/ttotal);
+ fprintf(ftimer,"sync_and_demod(0) %7.2f %7.2f\n",tsync0,tsync0/ttotal);
+ fprintf(ftimer,"sync_and_demod(1) %7.2f %7.2f\n",tsync1,tsync1/ttotal);
+ fprintf(ftimer,"sync_and_demod(2) %7.2f %7.2f\n",tsync2,tsync2/ttotal);
+ fprintf(ftimer,"Fano decoder %7.2f %7.2f\n",tfano,tfano/ttotal);
+ fprintf(ftimer,"-----------------------------------\n");
+ fprintf(ftimer,"Total %7.2f %7.2f\n",ttotal,1.0);
+
+ fclose(fall_wspr);
+ fclose(fwsprd);
+ // fclose(fdiag);
+ fclose(ftimer);
+ fftw_destroy_plan(PLAN1);
+ fftw_destroy_plan(PLAN2);
+ fftw_destroy_plan(PLAN3);
+
+ if( usehashtable ) {
+ fhash=fopen(hash_fname,"w");
+ for (i=0; i<32768; i++) {
+ if( strncmp(hashtab[i],"\0",1) != 0 ) {
+ fprintf(fhash,"%5d %s\n",i,*hashtab+i*13);
+ }
+ }
+ fclose(fhash);
+ }
+ if(fblank+tblank+writenoise == 999) return -1; //Silence compiler warning
+ return 0;
+}
diff --git a/lib/wsprd/wsprd_stats.txt b/lib/wsprd/wsprd_stats.txt
new file mode 100644
index 000000000..0d925aa66
--- /dev/null
+++ b/lib/wsprd/wsprd_stats.txt
@@ -0,0 +1,24 @@
+ Linux Windows
+Program Time Decodes Time Decodes
+-------------------------------------------------
+wsprd (Mar 2013) 2413 1451 2718 1451
+
+k9an-wsprd 1800 2122
+k9an_wsprd -q 354 1939
+
+wsprd 399 2190 356 2190
+wsprd -q 214 2034 192 2034
+
+wsprd* 1240 2215
+wsprd# 1599 2220
+
+-------------------------------------------------
+* maxcycles=30000
+# maxcycles=20000, iifac=1
+-------------------------------------------------
+Test data: 638 *.wav files (recorded by WSJT-X)
+-------------------------------------------------
+Linux machine: Core 2 Duo, E6750 CPU
+Windows machine: 4-Core i5-2500 CPU
+wsprd git commit: eecc274
+-------------------------------------------------
diff --git a/lib/wsprd/wsprd_utils.c b/lib/wsprd/wsprd_utils.c
new file mode 100644
index 000000000..37f6b8351
--- /dev/null
+++ b/lib/wsprd/wsprd_utils.c
@@ -0,0 +1,322 @@
+/*
+ This file is part of program wsprd, a detector/demodulator/decoder
+ for the Weak Signal Propagation Reporter (WSPR) mode.
+
+ File name: wsprd_utils.c
+
+ Copyright 2001-2015, Joe Taylor, K1JT
+
+ Most of the code is based on work by Steven Franke, K9AN, which
+ in turn was based on earlier work by K1JT.
+
+ Copyright 2014-2015, Steven Franke, K9AN
+
+ License: GNU GPL v3
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+#include "wsprd_utils.h"
+
+#ifndef int32_t
+#define int32_t int
+#endif
+
+void unpack50( signed char *dat, int32_t *n1, int32_t *n2 )
+{
+ int32_t i,i4;
+
+ i=dat[0];
+ i4=i&255;
+ *n1=i4<<20;
+
+ i=dat[1];
+ i4=i&255;
+ *n1=*n1+(i4<<12);
+
+ i=dat[2];
+ i4=i&255;
+ *n1=*n1+(i4<<4);
+
+ i=dat[3];
+ i4=i&255;
+ *n1=*n1+((i4>>4)&15);
+ *n2=(i4&15)<<18;
+
+ i=dat[4];
+ i4=i&255;
+ *n2=*n2+(i4<<10);
+
+ i=dat[5];
+ i4=i&255;
+ *n2=*n2+(i4<<2);
+
+ i=dat[6];
+ i4=i&255;
+ *n2=*n2+((i4>>6)&3);
+}
+
+void unpackcall( int32_t ncall, char *call )
+{
+ char c[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E',
+ 'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T',
+ 'U','V','W','X','Y','Z',' '};
+ int32_t n;
+ int i;
+ char tmp[7];
+
+ n=ncall;
+ strcpy(call,"......");
+ if (n < 262177560 ) {
+ i=n%27+10;
+ tmp[5]=c[i];
+ n=n/27;
+ i=n%27+10;
+ tmp[4]=c[i];
+ n=n/27;
+ i=n%27+10;
+ tmp[3]=c[i];
+ n=n/27;
+ i=n%10;
+ tmp[2]=c[i];
+ n=n/10;
+ i=n%36;
+ tmp[1]=c[i];
+ n=n/36;
+ i=n;
+ tmp[0]=c[i];
+ tmp[6]='\0';
+ // remove leading whitespace
+ for(i=0; i<5; i++) {
+ if( tmp[i] != c[36] )
+ break;
+ }
+ sprintf(call,"%-6s",&tmp[i]);
+ // remove trailing whitespace
+ for(i=0; i<6; i++) {
+ if( call[i] == c[36] ) {
+ call[i]='\0';
+ }
+ }
+ }
+}
+
+void unpackgrid( int32_t ngrid, char *grid)
+{
+ char c[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E',
+ 'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T',
+ 'U','V','W','X','Y','Z',' '};
+ int dlat, dlong;
+
+ ngrid=ngrid>>7;
+ if( ngrid < 32400 ) {
+ dlat=(ngrid%180)-90;
+ dlong=(ngrid/180)*2 - 180 + 2;
+ if( dlong < -180 )
+ dlong=dlong+360;
+ if( dlong > 180 )
+ dlong=dlong+360;
+ int nlong = 60.0*(180.0-dlong)/5.0;
+ int n1 = nlong/240;
+ int n2 = (nlong - 240*n1)/24;
+ grid[0] = c[10+n1];
+ grid[2]= c[n2];
+
+ int nlat = 60.0*(dlat+90)/2.5;
+ n1 = nlat/240;
+ n2 = (nlat-240*n1)/24;
+ grid[1]=c[10+n1];
+ grid[3]=c[n2];
+ } else {
+ strcpy(grid,"XXXX");
+ }
+}
+
+void unpackpfx( int32_t nprefix, char *call)
+{
+ char nc, pfx[4]="", tmpcall[7]="";
+ int i;
+ int32_t n;
+
+ strcpy(tmpcall,call);
+
+ if( nprefix < 60000 ) {
+ // add a prefix of 1 to 3 characters
+ n=nprefix;
+ for (i=2; i>=0; i--) {
+ nc=n%37;
+ if( (nc >= 0) & (nc <= 9) ) {
+ pfx[i]=nc+48;
+ }
+ else if( (nc >= 10) & (nc <= 35) ) {
+ pfx[i]=nc+55;
+ }
+ else {
+ pfx[i]=' ';
+ }
+ n=n/37;
+ }
+
+ strcpy(call,pfx);
+ strncat(call,"/",1);
+ strncat(call,tmpcall,strlen(tmpcall));
+
+ } else {
+ // add a suffix of 1 or 2 characters
+ nc=nprefix-60000;
+ if( (nc >= 0) & (nc <= 9) ) {
+ pfx[0]=nc+48;
+ strcpy(call,tmpcall);
+ strncat(call,"/",1);
+ strncat(call,pfx,1);
+ }
+ else if( (nc >= 10) & (nc <= 35) ) {
+ pfx[0]=nc+55;
+ strcpy(call,tmpcall);
+ strncat(call,"/",1);
+ strncat(call,pfx,1);
+ }
+ else if( (nc >= 36) & (nc <= 125) ) {
+ pfx[0]=(nc-26)/10+48;
+ pfx[1]=(nc-26)%10+48;
+ strcpy(call,tmpcall);
+ strncat(call,"/",1);
+ strncat(call,pfx,2);
+ }
+ }
+}
+
+void deinterleave(unsigned char *sym)
+{
+ unsigned char tmp[162];
+ unsigned char p, i, j;
+
+ p=0;
+ i=0;
+ while (p<162) {
+ j=((i * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
+ if (j < 162 ) {
+ tmp[p]=sym[j];
+ p=p+1;
+ }
+ i=i+1;
+ }
+ for (i=0; i<162; i++) {
+ sym[i]=tmp[i];
+ }
+}
+
+// used by qsort
+int floatcomp(const void* elem1, const void* elem2)
+{
+ if(*(const float*)elem1 < *(const float*)elem2)
+ return -1;
+ return *(const float*)elem1 > *(const float*)elem2;
+}
+
+int unpk_(signed char *message, char hashtab[32768][13], char *call_loc_pow, char *callsign)
+{
+ int n1,n2,n3,ndbm,ihash,nadd,noprint=0;
+ char grid[5],grid6[7],cdbm[3];
+
+ unpack50(message,&n1,&n2);
+ unpackcall(n1,callsign);
+ unpackgrid(n2, grid);
+ int ntype = (n2&127) - 64;
+ callsign[12]=0;
+ grid[4]=0;
+
+ /*
+ Based on the value of ntype, decide whether this is a Type 1, 2, or
+ 3 message.
+
+ * Type 1: 6 digit call, grid, power - ntype is positive and is a member
+ of the set {0,3,7,10,13,17,20...60}
+
+ * Type 2: extended callsign, power - ntype is positive but not
+ a member of the set of allowed powers
+
+ * Type 3: hash, 6 digit grid, power - ntype is negative.
+ */
+
+ if( (ntype >= 0) && (ntype <= 62) ) {
+ int nu=ntype%10;
+ if( nu == 0 || nu == 3 || nu == 7 ) {
+ ndbm=ntype;
+ memset(call_loc_pow,0,sizeof(char)*23);
+ sprintf(cdbm,"%2d",ndbm);
+ strncat(call_loc_pow,callsign,strlen(callsign));
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,grid,4);
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,cdbm,2);
+ strncat(call_loc_pow,"\0",1);
+ ihash=nhash_(callsign,strlen(callsign),(uint32_t)146);
+ strcpy(*hashtab+ihash*13,callsign);
+ } else {
+ nadd=nu;
+ if( nu > 3 ) nadd=nu-3;
+ if( nu > 7 ) nadd=nu-7;
+ n3=n2/128+32768*(nadd-1);
+ unpackpfx(n3,callsign);
+ ndbm=ntype-nadd;
+ memset(call_loc_pow,0,sizeof(char)*23);
+ sprintf(cdbm,"%2d",ndbm);
+ strncat(call_loc_pow,callsign,strlen(callsign));
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,cdbm,2);
+ strncat(call_loc_pow,"\0",1);
+ int nu=ndbm%10;
+ if( nu == 0 || nu == 3 || nu == 7 || nu == 10 ) { //make sure power is OK
+ ihash=nhash_(callsign,strlen(callsign),(uint32_t)146);
+ strcpy(*hashtab+ihash*13,callsign);
+ } else noprint=1;
+ }
+ } else if ( ntype < 0 ) {
+ ndbm=-(ntype+1);
+ memset(grid6,0,sizeof(char)*7);
+ strncat(grid6,callsign+5,1);
+ strncat(grid6,callsign,5);
+ int nu=ndbm%10;
+ if( (nu == 0 || nu == 3 || nu == 7 || nu == 10) && \
+ (isalpha(grid6[0]) && isalpha(grid6[1]) && \
+ isdigit(grid6[2]) && isdigit(grid6[3]) ) ) {
+ // not testing 4'th and 5'th chars because of this case: JO33 40
+ // grid is only 4 chars even though this is a hashed callsign...
+ // isalpha(grid6[4]) && isalpha(grid6[5]) ) ) {
+ ihash=nhash_(callsign,strlen(callsign),(uint32_t)146);
+ strcpy(*hashtab+ihash*13,callsign);
+ } else noprint=1;
+
+ ihash=(n2-ntype-64)/128;
+ if( strncmp(hashtab[ihash],"\0",1) != 0 ) {
+ sprintf(callsign,"<%s>",hashtab[ihash]);
+ } else {
+ sprintf(callsign,"%5s","<...>");
+ }
+
+ memset(call_loc_pow,0,sizeof(char)*23);
+ sprintf(cdbm,"%2d",ndbm);
+ strncat(call_loc_pow,callsign,strlen(callsign));
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,grid6,strlen(grid6));
+ strncat(call_loc_pow," ",1);
+ strncat(call_loc_pow,cdbm,2);
+ strncat(call_loc_pow,"\0",1);
+
+
+ // I don't know what to do with these... They show up as "A000AA" grids.
+ if( ntype == -64 ) noprint=1;
+ }
+ return noprint;
+}
diff --git a/lib/wsprd/wsprd_utils.h b/lib/wsprd/wsprd_utils.h
new file mode 100644
index 000000000..b24e9044d
--- /dev/null
+++ b/lib/wsprd/wsprd_utils.h
@@ -0,0 +1,24 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+void unpack50( signed char *dat, int32_t *n1, int32_t *n2 );
+
+void unpackcall( int32_t ncall, char *call );
+
+void unpackgrid( int32_t ngrid, char *grid);
+
+void unpackpfx( int32_t nprefix, char *call);
+
+void deinterleave(unsigned char *sym);
+
+// used by qsort
+int floatcomp(const void* elem1, const void* elem2);
+
+unsigned int nhash_( const void *key, size_t length, uint32_t initval);
+
+int unpk_( signed char *message, char hashtab[32768][13], char *call_loc_pow, char *callsign);
\ No newline at end of file
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 32718a1f8..5bdda3d82 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -111,6 +111,7 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
m_bandEdited {false},
m_splitMode {false},
m_monitoring {false},
+
m_transmitting {false},
m_tune {false},
m_lastMonitoredFrequency {default_frequency},
@@ -222,6 +223,8 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
ui->actionJT65->setActionGroup(modeGroup);
ui->actionJT9_JT65->setActionGroup(modeGroup);
ui->actionJT4->setActionGroup(modeGroup);
+ ui->actionWSPR_2->setActionGroup(modeGroup);
+ ui->actionWSPR_15->setActionGroup(modeGroup);
QActionGroup* saveGroup = new QActionGroup(this);
ui->actionNone->setActionGroup(saveGroup);
@@ -257,14 +260,17 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
setWindowTitle (program_title ());
createStatusBar();
- connect(&proc_jt9, SIGNAL(readyReadStandardOutput()),
- this, SLOT(readFromStdout()));
+ connect(&proc_jt9, SIGNAL(readyReadStandardOutput()),this, SLOT(readFromStdout()));
+ connect(&proc_jt9, SIGNAL(error(QProcess::ProcessError)),this, SLOT(jt9_error(QProcess::ProcessError)));
+ connect(&proc_jt9, SIGNAL(readyReadStandardError()),this, SLOT(readFromStderr()));
- connect(&proc_jt9, SIGNAL(error(QProcess::ProcessError)),
- this, SLOT(jt9_error(QProcess::ProcessError)));
+ connect(&p1, SIGNAL(readyReadStandardOutput()),this, SLOT(p1ReadFromStdout()));
+ connect(&p1, SIGNAL(error(QProcess::ProcessError)),this, SLOT(p1Error(QProcess::ProcessError)));
+ connect(&p1, SIGNAL(readyReadStandardError()),this, SLOT(p1ReadFromStderr()));
- connect(&proc_jt9, SIGNAL(readyReadStandardError()),
- this, SLOT(readFromStderr()));
+// connect(&p3, SIGNAL(readyReadStandardOutput()),this, SLOT(p3ReadFromStdout()));
+ connect(&p3, SIGNAL(error(QProcess::ProcessError)),this, SLOT(p3Error(QProcess::ProcessError)));
+ connect(&p3, SIGNAL(readyReadStandardError()),this, SLOT(p3ReadFromStderr()));
// Hook up working frequencies.
ui->bandComboBox->setModel (m_config.frequencies ());
@@ -321,7 +327,7 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
ui->readFreq->setFont(font);
connect(&m_guiTimer, &QTimer::timeout, this, &MainWindow::guiUpdate);
- m_guiTimer.start(100); //Don't change the 100 ms!
+ m_guiTimer.start(100); //### Don't change the 100 ms! ###
ptt0Timer = new QTimer(this);
ptt0Timer->setSingleShot(true);
@@ -338,10 +344,18 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
tuneButtonTimer->setSingleShot(true);
connect(tuneButtonTimer, &QTimer::timeout, this, &MainWindow::on_stopTxButton_clicked);
+ tuneATU_Timer= new QTimer(this);
+ tuneATU_Timer->setSingleShot(true);
+ connect(tuneATU_Timer, &QTimer::timeout, this, &MainWindow::stopTuneATU);
+
killFileTimer = new QTimer(this);
killFileTimer->setSingleShot(true);
connect(killFileTimer, &QTimer::timeout, this, &MainWindow::killFile);
+ uploadTimer = new QTimer(this);
+ uploadTimer->setSingleShot(true);
+ connect(uploadTimer, SIGNAL(timeout()), this, SLOT(uploadSpots()));
+
m_auto=false;
m_waterfallAvg = 1;
m_txFirst=false;
@@ -350,6 +364,15 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
m_killAll=false;
m_widebandDecode=false;
m_ntx=1;
+
+ m_nrx=1;
+ m_tx=0;
+ m_txNext=false;
+ m_uploading=false;
+ m_grid6=false;
+ m_nseq=0;
+ m_ntr=0;
+
m_loopall=false;
m_startAnother=false;
m_saveDecoded=false;
@@ -382,17 +405,47 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
m_MinW=0;
m_nSubMode=0;
m_tol=500;
- m_DTtol=0.2;
+ m_DTtol=0.5;
m_wideGraph->setTol(m_tol);
m_bShMsgs=false;
m_bDopplerTracking0=false;
+ m_uploading=false;
+ m_hopTest=false;
+ m_bTxTime=false;
+ m_band00=-1;
+ m_rxDone=false;
+
+ m_fWSPR["160"]=1.8366; //WSPR frequencies
+ m_fWSPR["80"]=3.5926;
+ m_fWSPR["60"]=5.2872;
+ m_fWSPR["40"]=7.0386;
+ m_fWSPR["30"]=10.1387;
+ m_fWSPR["20"]=14.0956;
+ m_fWSPR["17"]=18.1046;
+ m_fWSPR["15"]=21.0946;
+ m_fWSPR["12"]=24.9246;
+ m_fWSPR["10"]=28.1246;
signalMeter = new SignalMeter(ui->meterFrame);
signalMeter->resize(50, 160);
+ for(int i=0; i<28; i++) { //Initialize dBm values
+ float dbm=(10.0*i)/3.0 - 30.0;
+ int ndbm=0;
+ if(dbm<0) ndbm=int(dbm-0.5);
+ if(dbm>=0) ndbm=int(dbm+0.5);
+ QString t;
+ t.sprintf("%d dBm",ndbm);
+ ui->TxPowerComboBox->addItem(t);
+ }
+
ui->labAz->setStyleSheet("border: 0px;");
ui->labDist->setStyleSheet("border: 0px;");
+ auto t = "UTC dB DT Freq Message";
+ ui->decodedTextLabel->setText(t);
+ ui->decodedTextLabel2->setText(t);
+
readSettings(); //Restore user's setup params
// start the audio thread
m_audioThread->start (m_audioThreadPriority);
@@ -434,7 +487,7 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
"-s", QApplication::applicationName () // shared memory key,
// includes rig-name
#ifdef NDEBUG
- , "-w", "2" //FFTW patience - release
+ , "-w", "1" //FFTW patience - release
#else
, "-w", "1" //FFTW patience - debug builds for speed
#endif
@@ -461,8 +514,8 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
genStdMsgs(m_rpt);
m_ntx=6;
ui->txrb6->setChecked(true);
- if(m_mode!="JT9" and m_mode!="JT9W-1" and m_mode!="JT65" and
- m_mode!="JT9+JT65" and m_mode!="JT4") m_mode="JT9";
+ if(m_mode!="JT9" and m_mode!="JT9W-1" and m_mode!="JT65" and m_mode!="JT9+JT65" and
+ m_mode!="JT4" and m_mode!="WSPR-2" and m_mode!="WSPR-15") m_mode="JT9";
on_actionWide_Waterfall_triggered(); //###
connect(m_wideGraph.data (), SIGNAL(setFreq3(int,int)),this,
@@ -473,6 +526,8 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
if(m_mode=="JT9W-1") on_actionJT9W_1_triggered();
if(m_mode=="JT65") on_actionJT65_triggered();
if(m_mode=="JT9+JT65") on_actionJT9_JT65_triggered();
+ if(m_mode=="WSPR-2") on_actionWSPR_2_triggered();
+ if(m_mode=="WSPR-15") on_actionWSPR_15_triggered();
m_wideGraph->setLockTxFreq(m_lockTxFreq);
m_wideGraph->setMode(m_mode);
@@ -488,13 +543,8 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
Q_EMIT startAudioInputStream (m_config.audio_input_device (), m_framesAudioInputBuffered, &m_detector, m_downSampleFactor, m_config.audio_input_channel ());
Q_EMIT initializeAudioOutputStream (m_config.audio_output_device (), AudioDevice::Mono == m_config.audio_output_channel () ? 1 : 2, m_msAudioOutputBuffered);
-
Q_EMIT transmitFrequency (ui->TxFreqSpinBox->value () - m_XIT);
- auto t = "UTC dB DT Freq Message";
- ui->decodedTextLabel->setText(t);
- ui->decodedTextLabel2->setText(t);
-
enable_DXCC_entity (m_config.DXCC ()); // sets text window proportions and (re)inits the logbook
ui->label_9->setStyleSheet("QLabel{background-color: #aabec8}");
@@ -516,8 +566,29 @@ MainWindow::MainWindow(bool multiple, QSettings * settings, QSharedMemory *shdme
bool b=m_config.enable_VHF_features() and (m_mode=="JT4" or m_mode=="JT65");
VHF_controls_visible(b);
- m_hsymStop=173;
- if(m_config.decode_at_52s()) m_hsymStop=181;
+ m_ntx=1;
+ ui->txrb1->setChecked(true);
+
+ if(m_mode.mid(0,4)=="WSPR" and m_pctx>0) {
+ QPalette* palette = new QPalette();
+ palette->setColor(QPalette::Base,Qt::yellow);
+ ui->sbTxPercent->setPalette(*palette);
+ delete palette;
+ }
+ if(m_mode=="WSPR-2") {
+ m_hsymStop=396;
+ } else if(m_mode=="WSPR-15") {
+ m_hsymStop=3090;
+ } else {
+ m_hsymStop=173;
+ if(m_config.decode_at_52s()) m_hsymStop=181;
+ }
+ m_modulator.setPeriod(60);
+ m_dialFreqRxWSPR=0;
+ wsprNet = new WSPRNet(this);
+ connect( wsprNet, SIGNAL(uploadStatus(QString)), this, SLOT(uploadResponse(QString)));
+
+//### Remove this stuff!
#if !WSJT_ENABLE_EXPERIMENTAL_FEATURES
ui->actionJT9W_1->setEnabled (false);
#endif
@@ -557,10 +628,12 @@ void MainWindow::writeSettings()
m_settings->setValue("NDepth",m_ndepth);
m_settings->setValue("RxFreq",ui->RxFreqSpinBox->value());
m_settings->setValue("TxFreq",ui->TxFreqSpinBox->value());
+ m_settings->setValue("WSPRfreq",ui->WSPRfreqSpinBox->value());
m_settings->setValue("minW",ui->sbMinW->value());
m_settings->setValue("SubMode",ui->sbSubmode->value());
m_settings->setValue("DTtol",m_DTtol);
m_settings->setValue("Ftol",ui->sbTol->value());
+ m_settings->setValue("MinSync",m_minSync);
m_settings->setValue("EME",m_bEME);
m_settings->setValue ("DialFreq", QVariant::fromValue(m_lastMonitoredFrequency));
m_settings->setValue("InGain",m_inGain);
@@ -570,7 +643,16 @@ void MainWindow::writeSettings()
m_settings->setValue("OutBufSize",outBufSize);
m_settings->setValue("LockTxFreq",m_lockTxFreq);
m_settings->setValue("Plus2kHz",m_plus2kHz);
-
+ m_settings->setValue("PctTx",m_pctx);
+ m_settings->setValue("dBm",m_dBm);
+ m_settings->setValue("UploadSpots",m_uploadSpots);
+ m_settings->setValue("BandHopping",m_bandHopping);
+ m_settings->setValue("SunriseBands",ui->sunriseBands->text());
+ m_settings->setValue("DayBands",ui->dayBands->text());
+ m_settings->setValue("SunsetBands",ui->sunsetBands->text());
+ m_settings->setValue("NightBands",ui->nightBands->text());
+ m_settings->setValue("TuneBands",ui->tuneBands->text());
+ m_settings->setValue("GrayLineDuration",ui->graylineDuration->text());
m_settings->endGroup();
}
@@ -595,31 +677,32 @@ void MainWindow::readSettings()
if (displayAstro) on_actionAstronomical_data_triggered ();
if (displayMsgAvg) on_actionMessage_averaging_triggered();
m_settings->beginGroup("Common");
- morse_(const_cast (m_config.my_callsign ().toLatin1().constData())
- , const_cast (icw)
- , &m_ncw
- , m_config.my_callsign ().length());
+ morse_(const_cast (m_config.my_callsign ().toLatin1().constData()),
+ const_cast (icw), &m_ncw, m_config.my_callsign ().length());
m_mode=m_settings->value("Mode","JT9").toString();
m_modeTx=m_settings->value("ModeTx","JT9").toString();
if(m_modeTx.mid(0,3)=="JT9") ui->pbTxMode->setText("Tx JT9 @");
if(m_modeTx=="JT65") ui->pbTxMode->setText("Tx JT65 #");
ui->actionNone->setChecked(m_settings->value("SaveNone",true).toBool());
- ui->actionSave_decoded->setChecked(m_settings->value(
- "SaveDecoded",false).toBool());
+ ui->actionSave_decoded->setChecked(m_settings->value("SaveDecoded",false).toBool());
ui->actionSave_all->setChecked(m_settings->value("SaveAll",false).toBool());
ui->RxFreqSpinBox->setValue(0); // ensure a change is signaled
ui->RxFreqSpinBox->setValue(m_settings->value("RxFreq",1500).toInt());
m_nSubMode=m_settings->value("SubMode",0).toInt();
ui->sbSubmode->setValue(m_nSubMode);
ui->sbMinW->setMaximum(m_nSubMode);
- m_DTtol=m_settings->value("DTtol",0.2).toFloat();
+ m_DTtol=m_settings->value("DTtol",0.5).toFloat();
ui->sbDT->setValue(m_DTtol);
ui->sbTol->setValue(m_settings->value("Ftol",4).toInt());
+ ui->syncSpinBox->setValue(m_settings->value("MinSync",0).toInt());
m_bEME=m_settings->value("EME",false).toBool();
ui->cbEME->setChecked(m_bEME);
m_MinW=m_settings->value("minW",0).toInt();
ui->sbMinW->setValue(m_MinW);
- m_lastMonitoredFrequency = m_settings->value ("DialFreq", QVariant::fromValue (default_frequency)).value ();
+ m_lastMonitoredFrequency = m_settings->value ("DialFreq",
+ QVariant::fromValue (default_frequency)).value ();
+ ui->WSPRfreqSpinBox->setValue(0); // ensure a change is signaled
+ ui->WSPRfreqSpinBox->setValue(m_settings->value("WSPRfreq",1500).toInt());
ui->TxFreqSpinBox->setValue(0); // ensure a change is signaled
ui->TxFreqSpinBox->setValue(m_settings->value("TxFreq",1500).toInt());
Q_EMIT transmitFrequency (ui->TxFreqSpinBox->value () - m_XIT);
@@ -628,7 +711,17 @@ void MainWindow::readSettings()
m_ndepth=m_settings->value("NDepth",3).toInt();
m_inGain=m_settings->value("InGain",0).toInt();
ui->inGain->setValue(m_inGain);
-
+ m_pctx=m_settings->value("PctTx",20).toInt();
+ m_rxavg=1.0;
+ if(m_pctx>0) m_rxavg=100.0/m_pctx - 1.0; //Average # of Rx's per Tx
+ ui->sbTxPercent->setValue(m_pctx);
+ m_dBm=m_settings->value("dBm",37).toInt();
+ ui->TxPowerComboBox->setCurrentIndex(int(0.3*(m_dBm + 30.0)+0.2));
+ m_uploadSpots=m_settings->value("UploadSpots",false).toBool();
+ ui->cbUploadWSPR_Spots->setChecked(m_uploadSpots);
+ if(!m_uploadSpots) ui->cbUploadWSPR_Spots->setStyleSheet("QCheckBox{background-color: yellow}");
+ m_bandHopping=m_settings->value("BandHopping",false).toBool();
+ ui->cbBandHop->setChecked(m_bandHopping);
// setup initial value of tx attenuator
ui->outAttenuation->setValue (m_settings->value ("OutAttenuation", 0).toInt ());
on_outAttenuation_valueChanged (ui->outAttenuation->value ());
@@ -641,6 +734,18 @@ void MainWindow::readSettings()
ui->cbTxLock->setChecked(m_lockTxFreq);
m_plus2kHz=m_settings->value("Plus2kHz",false).toBool();
ui->cbPlus2kHz->setChecked(m_plus2kHz);
+ ui->sunriseBands->setText(m_settings->value("SunriseBands","").toString());
+ on_sunriseBands_editingFinished();
+ ui->dayBands->setText(m_settings->value("DayBands","").toString());
+ on_dayBands_editingFinished();
+ ui->sunsetBands->setText(m_settings->value("SunsetBands","").toString());
+ on_sunsetBands_editingFinished();
+ ui->nightBands->setText(m_settings->value("NightBands","").toString());
+ on_nightBands_editingFinished();
+ ui->tuneBands->setText(m_settings->value("TuneBands","").toString());
+ on_tuneBands_editingFinished();
+ ui->graylineDuration->setText(m_settings->value("GraylineDuration","").toString());
+ on_graylineDuration_editingFinished();
m_settings->endGroup();
// use these initialisation settings to tune the audio o/p buffer
@@ -692,7 +797,9 @@ void MainWindow::dataSink(qint64 frames)
int k (frames);
jt9com_.nfa=m_wideGraph->nStartFreq();
jt9com_.nfb=m_wideGraph->Fmax();
- symspec_(&k,&trmin,&m_nsps,&m_inGain,&px,s,&df3,&ihsym,&npts8);
+ int nsmo=m_wideGraph->smoothYellow()-1;
+ symspec_(&k,&trmin,&m_nsps,&m_inGain,&nsmo,&px,s,&df3,&ihsym,&npts8);
+ if(m_mode=="WSPR-2") wspr_downsample_(jt9com_.d2,&k); //###
if(ihsym <=0) return;
QString t;
m_pctZap=nzap*100.0/m_nsps;
@@ -702,17 +809,30 @@ void MainWindow::dataSink(qint64 frames)
m_wideGraph->dataSink2(s,df3,ihsym,m_diskData);
}
+ if(m_mode=="WSPR-2") {
+ m_hsymStop=396;
+ } else if(m_mode=="WSPR-15") {
+ m_hsymStop=3090;
+ } else {
+ m_hsymStop=173;
+ if(m_config.decode_at_52s()) m_hsymStop=181;
+ }
+
+ if(ihsym==3*m_hsymStop/4) {
+ m_dialFreqRxWSPR=m_dialFreq;
+ }
+
if(ihsym == m_hsymStop) {
+ if( m_dialFreqRxWSPR==0) m_dialFreqRxWSPR=m_dialFreq;
m_dataAvailable=true;
jt9com_.npts8=(ihsym*m_nsps)/16;
jt9com_.newdat=1;
jt9com_.nagain=0;
- if(!m_config.decode_at_52s()) m_hsymStop=173;
- if(m_config.decode_at_52s()) m_hsymStop=181;
jt9com_.nzhsym=m_hsymStop;
QDateTime t = QDateTime::currentDateTimeUtc();
m_dateTime=t.toString("yyyy-MMM-dd hh:mm");
- decode(); //Start decoder
+ if(m_mode.mid(0,4)!="WSPR") decode(); //Start decoder
+
if(!m_diskData) { //Always save; may delete later
int ihr=t.time().toString("hh").toInt();
int imin=t.time().toString("mm").toInt();
@@ -723,7 +843,43 @@ void MainWindow::dataSink(qint64 frames)
"_" + t2 + ".wav");
*future2 = QtConcurrent::run(savewav, m_fname, m_TRperiod);
watcher2->setFuture(*future2);
+
+ if(m_mode.mid(0,4)=="WSPR") {
+ m_c2name=m_config.save_directory ().absoluteFilePath (t.date().toString("yyMMdd") +
+ "_" + t2 + ".c2");
+ int len1=m_c2name.length();
+ char c2name[80];
+ strcpy(c2name,m_c2name.toLatin1());
+ int nsec=120;
+ int nbfo=1500;
+ double f0m1500=m_dialFreq/1000000.0 + nbfo - 1500;
+ savec2_(c2name,&nsec,&f0m1500,len1);
+ }
}
+
+ if(m_mode.mid(0,4)=="WSPR") {
+ QString t2,cmnd;
+ double f0m1500=m_dialFreqRxWSPR/1000000.0; // + 0.000001*(m_BFO - 1500);
+ t2.sprintf(" -f %.6f ",f0m1500);
+
+ if(m_diskData) {
+// cmnd='"' + m_appDir + '"' + "/wsprd " + m_path;
+ cmnd='"' + m_appDir + '"' + "/wsprd -a \"" +
+ QDir::toNativeSeparators(m_dataDir.absolutePath()) + "\" " + m_path;
+// if(m_TRseconds==900) cmnd='"' + m_appDir + '"' + "/wsprd -m 15" + t2 +
+// m_path + '"';
+ } else {
+ cmnd='"' + m_appDir + '"' + "/wsprd -a \"" +
+ QDir::toNativeSeparators(m_dataDir.absolutePath()) + "\" " +
+ t2 + '"' + m_fname + '"';
+ }
+ QString t3=cmnd;
+ int i1=cmnd.indexOf("/wsprd ");
+ cmnd=t3.mid(0,i1+7) + t3.mid(i1+7);
+ ui->DecodeButton->setChecked (true);
+ p1.start(QDir::toNativeSeparators(cmnd));
+ }
+ m_rxDone=true;
}
}
@@ -810,14 +966,8 @@ void MainWindow::on_monitorButton_clicked (bool checked)
}
}
- Q_EMIT m_config.sync_transceiver (true, checked); // gets
- // Configuration
- // in/out of
- // strict
- // split and
- // mode
- // checking
-
+//Get Configuration in/out of strict split and mode checking
+ Q_EMIT m_config.sync_transceiver (true, checked);
}
else
{
@@ -828,17 +978,11 @@ void MainWindow::on_monitorButton_clicked (bool checked)
void MainWindow::monitor (bool state)
{
ui->monitorButton->setChecked (state);
- if (state)
- {
- if (!m_monitoring)
- {
- Q_EMIT resumeAudioInputStream ();
- }
- }
- else
- {
- Q_EMIT suspendAudioInputStream ();
- }
+ if (state) {
+ if (!m_monitoring) Q_EMIT resumeAudioInputStream ();
+ } else {
+ Q_EMIT suspendAudioInputStream ();
+ }
m_monitoring = state;
}
@@ -850,11 +994,27 @@ void MainWindow::on_actionAbout_triggered() //Display "About"
void MainWindow::on_autoButton_clicked (bool checked)
{
m_auto = checked;
- m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting);
+// qDebug() << "autoButton_clicked" << m_auto << m_tuneup;
+
+ m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall,
+ QString::number (ui->rptSpinBox->value ()),
+ m_modeTx, ui->autoButton->isChecked (),
+ m_transmitting);
+ if(m_mode.mid(0,4)=="WSPR") {
+ QPalette* palette = new QPalette();
+ if(m_auto or m_pctx==0) {
+ palette->setColor(QPalette::Base,Qt::white);
+ } else {
+ palette->setColor(QPalette::Base,Qt::yellow);
+ }
+ ui->sbTxPercent->setPalette(*palette);
+ delete palette;
+ }
}
void MainWindow::auto_tx_mode (bool state)
{
+// qDebug() << "auto_tx_mode" << state << m_tuneup;
ui->autoButton->setChecked (state);
on_autoButton_clicked (state);
}
@@ -984,27 +1144,24 @@ void MainWindow::qsy (Frequency f)
if (m_dialFreq != f)
{
m_dialFreq = f;
-
m_repeatMsg=0;
m_secBandChanged=QDateTime::currentMSecsSinceEpoch()/1000;
-
- QFile f2 {m_dataDir.absoluteFilePath ("ALL.TXT")};
- if (f2.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
- {
+ if(m_dialFreq/1000000 < 30 and m_mode.mid(0,4)!="WSPR") {
+// Write freq changes to ALL.TXT only below 30 MHz.
+ QFile f2 {m_dataDir.absoluteFilePath ("ALL.TXT")};
+ if (f2.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
QTextStream out(&f2);
out << QDateTime::currentDateTimeUtc().toString("yyyy-MMM-dd hh:mm")
<< " " << (m_dialFreq / 1.e6) << " MHz " << m_mode << endl;
f2.close();
- }
- else
- {
+ } else {
msgBox("Cannot open \"" + f2.fileName () + "\" for append:" + f2.errorString ());
}
- if (m_config.spot_to_psk_reporter ())
- {
- pskSetLocal ();
- }
+ }
+ if (m_config.spot_to_psk_reporter ()) {
+ pskSetLocal ();
+ }
displayDialFrequency ();
statusChanged();
m_wideGraph->setDialFreq(m_dialFreq / 1.e6);
@@ -1016,11 +1173,14 @@ void MainWindow::displayDialFrequency ()
{
// lookup band
auto bands_model = m_config.bands ();
- ui->bandComboBox->setCurrentText (bands_model->data (bands_model->find (m_dialFreq)).toString ());
+ QString t {bands_model->data(bands_model->find(m_dialFreq)).toString()};
+ ui->bandComboBox->setCurrentText (t);
+ m_wideGraph->setRxBand(t);
// search working frequencies for one we are within 10kHz of
auto frequencies = m_config.frequencies ();
bool valid {false};
+ quint64 min_offset=99999999;
for (int row = 0; row < frequencies->rowCount (); ++row) {
// we need to do specific checks for above and below here to
@@ -1028,11 +1188,13 @@ void MainWindow::displayDialFrequency ()
// potentially use the full 64-bit unsigned range.
auto working_frequency = frequencies->data (frequencies->index (row, 0)).value ();
auto offset = m_dialFreq > working_frequency ? m_dialFreq - working_frequency : working_frequency - m_dialFreq;
- if ((offset < 10000u) or (m_config.enable_VHF_features() and offset < 1000000u)) {
- m_freqNominal=working_frequency;
- valid = true;
+ if(offsetlabDialFreq->setProperty ("oob", !valid);
// the following sequence is necessary to update the style
@@ -1044,7 +1206,9 @@ void MainWindow::displayDialFrequency ()
void MainWindow::statusChanged()
{
- m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting);
+ m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall,
+ QString::number (ui->rptSpinBox->value ()),
+ m_modeTx, ui->autoButton->isChecked (), m_transmitting);
QFile f {m_config.temp_dir ().absoluteFilePath ("wsjtx_status.txt")};
if(f.open(QFile::WriteOnly | QIODevice::Text)) {
@@ -1094,6 +1258,9 @@ void MainWindow::createStatusBar() //createStatusBar
auto_tx_label->setMinimumSize(QSize(150,18));
auto_tx_label->setFrameStyle(QFrame::Panel | QFrame::Sunken);
statusBar()->addWidget(auto_tx_label);
+
+ progressBar = new QProgressBar;
+ statusBar()->addWidget(progressBar);
}
void MainWindow::closeEvent(QCloseEvent * e)
@@ -1380,6 +1547,7 @@ void MainWindow::decode() //decode()
{
if(!m_dataAvailable) return;
ui->DecodeButton->setChecked (true);
+
if(jt9com_.newdat==1 && (!m_diskData)) {
qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000;
int imin=ms/60000;
@@ -1415,9 +1583,14 @@ void MainWindow::decode() //decode()
jt9com_.nsubmode=m_nSubMode;
jt9com_.minw=m_MinW;
jt9com_.nclearave=m_nclearave;
+ if(m_nclearave!=0) {
+ QFile f(m_config.temp_dir ().absoluteFilePath ("avemsg.txt"));
+ f.remove();
+ }
jt9com_.dttol=m_DTtol;
jt9com_.emedelay=0.0;
if(m_bEME) jt9com_.emedelay=2.5;
+ jt9com_.minSync=m_minSync;
strncpy(jt9com_.datetime, m_dateTime.toLatin1(), 20);
strncpy(jt9com_.mycall, (m_config.my_callsign()+" ").toLatin1(),12);
@@ -1463,7 +1636,7 @@ void MainWindow::readFromStdout() //readFromStdout
{
while(proc_jt9.canReadLine()) {
QByteArray t=proc_jt9.readLine();
- bool baveJT4msg=(t.length()>48);
+ bool baveJT4msg=(t.length()>49);
if(m_mode=="JT4") t=t.mid(0,39) + t.mid(42,t.length()-42);
if(t.indexOf("") >= 0) {
m_bdecoded = (t.mid(23,1).toInt()==1);
@@ -1495,17 +1668,15 @@ void MainWindow::readFromStdout() //readFromStdout
msgBox("Cannot open \"" + f.fileName () + "\" for append:" + f.errorString ());
}
- if (m_config.insert_blank () && m_blankLine)
- {
- QString band;
- if (QDateTime::currentMSecsSinceEpoch() / 1000 - m_secBandChanged > 50)
- {
- auto const& bands_model = m_config.bands ();
- band = ' ' + bands_model->data (bands_model->find (m_dialFreq + ui->TxFreqSpinBox->value ())).toString ();
- }
- ui->decodedTextBrowser->insertLineSpacer (band.rightJustified (40, '-'));
- m_blankLine = false;
- }
+ if (m_config.insert_blank () && m_blankLine) {
+ QString band;
+ if (QDateTime::currentMSecsSinceEpoch() / 1000 - m_secBandChanged > 50) {
+ auto const& bands_model = m_config.bands ();
+ band = ' ' + bands_model->data (bands_model->find (m_dialFreq + ui->TxFreqSpinBox->value ())).toString ();
+ }
+ ui->decodedTextBrowser->insertLineSpacer (band.rightJustified (40, '-'));
+ m_blankLine = false;
+ }
DecodedText decodedtext;
decodedtext = t.replace("\n",""); //t.replace("\n","").mid(0,t.length()-4);
@@ -1595,12 +1766,16 @@ void MainWindow::on_EraseButton_clicked() //Erase
{
qint64 ms=QDateTime::currentMSecsSinceEpoch();
ui->decodedTextBrowser2->clear();
- m_QSOText.clear();
- if((ms-m_msErase)<500) {
+ if(m_mode.mid(0,4)=="WSPR") {
ui->decodedTextBrowser->clear();
- m_messageClient->clear_decodes ();
- QFile f(m_config.temp_dir ().absoluteFilePath ("decoded.txt"));
- if(f.exists()) f.remove();
+ } else {
+ m_QSOText.clear();
+ if((ms-m_msErase)<500) {
+ ui->decodedTextBrowser->clear();
+ m_messageClient->clear_decodes ();
+ QFile f(m_config.temp_dir ().absoluteFilePath ("decoded.txt"));
+ if(f.exists()) f.remove();
+ }
}
m_msErase=ms;
}
@@ -1650,11 +1825,14 @@ void MainWindow::guiUpdate()
static double onAirFreq0=0.0;
QString rt;
- double tx1=0.0;
- double tx2=1.0 + 85.0*m_nsps/12000.0 + icw[0]*2560.0/48000.0;
- if(m_modeTx=="JT65") tx2=1.0 + 126*4096/11025.0 + icw[0]*2560.0/48000.0;
+ double txDuration=1.0 + 85.0*m_nsps/12000.0; // JT9
+ if(m_modeTx=="JT65") txDuration=1.0 + 126*4096/11025.0; // JT65
+ if(m_mode=="WSPR-2") txDuration=2.0 + 162*8192/12000.0; // WSPR
+//### if(m_mode=="WSPR-15") tx2=...
- if(!m_txFirst) {
+ double tx1=0.0;
+ double tx2=txDuration + + icw[0]*2560.0/48000.0; //Full length including CW ID
+ if(!m_txFirst and m_mode.mid(0,4)!="WSPR") {
tx1 += m_TRperiod;
tx2 += m_TRperiod;
}
@@ -1662,54 +1840,124 @@ void MainWindow::guiUpdate()
int nsec=ms/1000;
double tsec=0.001*ms;
double t2p=fmod(tsec,2*m_TRperiod);
- bool bTxTime = ((t2p >= tx1) and (t2p < tx2)) or m_tune;
+ m_nseq = nsec % m_TRperiod;
- if(m_transmitting or m_auto or m_tune) {
- QFile f(m_appDir + "/txboth");
- if(f.exists() and fmod(tsec,m_TRperiod) < (1.0 + 85.0*m_nsps/12000.0)) {
- bTxTime=true;
+ if(m_mode.mid(0,4)=="WSPR") {
+ if(m_nseq==0 and m_ntr==0) {
+ m_tuneup=false;
+ if(m_pctx==0) m_nrx=1; //Always receive if pctx=0
+ if((m_auto and (m_pctx>0) and (m_txNext or ((m_nrx<=0) and
+ (m_ntr!=-1)))) or ((m_auto and (m_pctx==100)))) {
+// This will be a WSPR Tx sequence. Compute # of Rx's that should follow.
+ float x=(float)rand()/RAND_MAX;
+ if(m_pctx<50) {
+ m_nrx=int(m_rxavg + 3.0*(x-0.5) + 0.5);
+ } else {
+ m_nrx=0;
+ if(xpbTxNext->setChecked(false);
+ m_bTxTime=true; //Start a WSPR Tx sequence
+ } else {
+// This will be a WSPR Rx sequence.
+ m_ntr=1; //This says we will have received
+ m_bTxTime=false; //Start a WSPR Rx sequence
+ }
}
- Frequency onAirFreq = m_dialFreq + ui->TxFreqSpinBox->value ();
- if (onAirFreq > 10139900 && onAirFreq < 10140320) {
- bTxTime=false;
+ } else {
+ m_bTxTime = (t2p >= tx1) and (t2p < tx2); // For all modes other than WSPR
+ }
+ if(m_tune) m_bTxTime=true; //"Tune" takes precedence
+
+ if(m_transmitting or m_auto or m_tune) {
+
+// Check for "txboth" (testing purposes only)
+ QFile f(m_appDir + "/txboth");
+ if(f.exists() and fmod(tsec,m_TRperiod) < (1.0 + 85.0*m_nsps/12000.0)) {
+ m_bTxTime=true;
+ }
+
+// Don't transmit another mode in the 30 m WSPR sub-band
+ Frequency onAirFreq = m_dialFreq + ui->TxFreqSpinBox->value();
+ if ((onAirFreq > 10139900 and onAirFreq < 10140320) and m_mode.mid(0,4)!="WSPR") {
+ m_bTxTime=false;
if (m_tune) stop_tuning ();
if (m_auto) auto_tx_mode (false);
-
if(onAirFreq!=onAirFreq0) {
onAirFreq0=onAirFreq;
QString t="Please choose another Tx frequency.\n";
- t+="WSJT-X will not knowingly transmit\n";
- t+="in the WSPR sub-band on 30 m.";
+ t+="WSJT-X will not knowingly transmit another\n";
+ t+="mode in the WSPR sub-band on 30 m.";
msgBox(t);
}
}
float fTR=float((nsec%m_TRperiod))/m_TRperiod;
- if(g_iptt==0 and ((bTxTime and fTR<0.4) or m_tune )) {
+// if(g_iptt==0 and ((m_bTxTime and fTR<0.4) or m_tune )) {
+ if(g_iptt==0 and ((m_bTxTime and fTR<99) or m_tune )) { //### allow late starts ###
icw[0]=m_ncw;
g_iptt = 1;
- setXIT (ui->TxFreqSpinBox->value ()); // ensure correct offset
- Q_EMIT m_config.transceiver_ptt (true);
- ptt1Timer->start(200); //Sequencer delay
+ setXIT (ui->TxFreqSpinBox->value ()); //Ensure correct offset
+ Q_EMIT m_config.transceiver_ptt (true); //Assert the PTT
+ ptt1Timer->start(200); //Sequencer delay
+ }
+ if(!m_bTxTime and !m_tune) m_btxok=false; //Time to stop transmitting
+ }
+
+ if(m_mode.mid(0,4)=="WSPR" and
+ ((m_ntr==1 and m_rxDone) or (m_ntr==-1 and m_nseq>tx2))) {
+ if(m_monitoring) {
+ m_nrx=m_nrx-1; //Decrement the Rx-sequence count
+ m_rxDone=false;
+ }
+ if(m_transmitting) {
+ m_bTxTime=false; //Time to stop a WSPR transmission
+ m_btxok=false;
+ }
+ if(m_bandHopping and m_ntr==1) {
+// qDebug() << "Call bandHopping after Rx" << m_nseq << m_ntr << m_nrx << m_rxDone;
+ bandHopping();
+ m_ntr=0; //This WSPR Rx sequence is complete
}
- if(!bTxTime and !m_tune) m_btxok=false;
}
// Calculate Tx tones when needed
if((g_iptt==1 && iptt0==0) || m_restart) {
QByteArray ba;
- if(m_ntx == 1) ba=ui->tx1->text().toLocal8Bit();
- if(m_ntx == 2) ba=ui->tx2->text().toLocal8Bit();
- if(m_ntx == 3) ba=ui->tx3->text().toLocal8Bit();
- if(m_ntx == 4) ba=ui->tx4->text().toLocal8Bit();
- if(m_ntx == 5) ba=ui->tx5->currentText().toLocal8Bit();
- if(m_ntx == 6) ba=ui->tx6->text().toLocal8Bit();
- if(m_ntx == 7) ba=ui->genMsg->text().toLocal8Bit();
- if(m_ntx == 8) ba=ui->freeTextMsg->currentText().toLocal8Bit();
+
+ if(m_mode.mid(0,4)=="WSPR") {
+ QString sdBm,msg0,msg1,msg2;
+ sdBm.sprintf(" %d",m_dBm);
+ m_tx=1-m_tx;
+ int i2=m_config.my_callsign().indexOf("/");
+ if(i2>0 or m_grid6) {
+ if(i2<0) { // "Type 2" WSPR message
+ msg1=m_config.my_callsign() + " " + m_config.my_grid().mid(0,4) + sdBm;
+ } else {
+ msg1=m_config.my_callsign() + sdBm;
+ }
+ msg0="<" + m_config.my_callsign() + "> " + m_config.my_grid()+ sdBm;
+ if(m_tx==0) msg2=msg0;
+ if(m_tx==1) msg2=msg1;
+ } else {
+ msg2=m_config.my_callsign() + " " + m_config.my_grid().mid(0,4) + sdBm; // Normal WSPR message
+ }
+ ba=msg2.toLatin1();
+ } else {
+ if(m_ntx == 1) ba=ui->tx1->text().toLocal8Bit();
+ if(m_ntx == 2) ba=ui->tx2->text().toLocal8Bit();
+ if(m_ntx == 3) ba=ui->tx3->text().toLocal8Bit();
+ if(m_ntx == 4) ba=ui->tx4->text().toLocal8Bit();
+ if(m_ntx == 5) ba=ui->tx5->currentText().toLocal8Bit();
+ if(m_ntx == 6) ba=ui->tx6->text().toLocal8Bit();
+ if(m_ntx == 7) ba=ui->genMsg->text().toLocal8Bit();
+ if(m_ntx == 8) ba=ui->freeTextMsg->currentText().toLocal8Bit();
+ }
ba2msg(ba,message);
- // ba2msg(ba,msgsent);
int len1=22;
int ichk=0;
if (m_lastMessageSent != m_currentMessage
@@ -1722,28 +1970,16 @@ void MainWindow::guiUpdate()
if(m_tune) {
itone[0]=0;
} else {
- if(m_modeTx=="JT4") gen4_(message
- , &ichk
- , msgsent
- , const_cast (itone)
- , &m_currentMessageType
- , len1
- , len1);
- if(m_modeTx=="JT9") gen9_(message
- , &ichk
- , msgsent
- , const_cast (itone)
- , &m_currentMessageType
- , len1
- , len1);
- if(m_modeTx=="JT65") gen65_(message
- , &ichk
- , msgsent
- , const_cast (itone)
- , &m_currentMessageType
- , len1
- , len1);
+ if(m_modeTx=="JT4") gen4_(message, &ichk , msgsent, const_cast (itone),
+ &m_currentMessageType, len1, len1);
+ if(m_modeTx=="JT9") gen9_(message, &ichk, msgsent, const_cast (itone),
+ &m_currentMessageType, len1, len1);
+ if(m_modeTx=="JT65") gen65_(message, &ichk, msgsent, const_cast (itone),
+ &m_currentMessageType, len1, len1);
+ if(m_mode.mid(0,4)=="WSPR") genwspr_(message, msgsent, const_cast (itone),
+ len1, len1);
}
+
msgsent[22]=0;
m_currentMessage = QString::fromLatin1(msgsent);
if (m_tune)
@@ -1829,9 +2065,7 @@ void MainWindow::guiUpdate()
}
}
m_restart=false;
- }
- else
- {
+ } else {
if (!m_auto && m_sentFirst73)
{
m_sentFirst73 = false;
@@ -1852,38 +2086,29 @@ void MainWindow::guiUpdate()
if (g_iptt == 1 && iptt0 == 0)
{
QString t=QString::fromLatin1(msgsent);
- if(t==m_msgSent0)
- {
- m_repeatMsg++;
- }
- else
- {
- m_repeatMsg=0;
- m_msgSent0=t;
+ if(t==m_msgSent0) {
+ m_repeatMsg++;
+ } else {
+ m_repeatMsg=0;
+ m_msgSent0=t;
+ }
+ if(!m_tune) {
+ QFile f {m_dataDir.absoluteFilePath ("ALL.TXT")};
+ if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
+ QTextStream out(&f);
+ out << QDateTime::currentDateTimeUtc().toString("hhmm")
+ << " Transmitting " << (m_dialFreq / 1.e6) << " MHz " << m_modeTx
+ << ": " << m_currentMessage << endl;
+ f.close();
+ } else {
+ msgBox("Cannot open \"" + f.fileName () + "\" for append:" + f.errorString ());
}
+ }
- if(!m_tune)
- {
- QFile f {m_dataDir.absoluteFilePath ("ALL.TXT")};
- if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
- {
- QTextStream out(&f);
- out << QDateTime::currentDateTimeUtc().toString("hhmm")
- << " Transmitting " << (m_dialFreq / 1.e6) << " MHz " << m_modeTx
- << ": " << m_currentMessage << endl;
- f.close();
- }
- else
- {
- msgBox("Cannot open \"" + f.fileName () + "\" for append:" + f.errorString ());
- }
- }
-
- if (m_config.TX_messages () && !m_tune)
- {
- ui->decodedTextBrowser2->displayTransmittedText(t,m_modeTx,
+ if (m_config.TX_messages () && !m_tune) {
+ ui->decodedTextBrowser2->displayTransmittedText(t,m_modeTx,
ui->TxFreqSpinBox->value(),m_config.color_TxMsg());
- }
+ }
m_transmitting = true;
transmitDisplay (true);
@@ -1905,11 +2130,11 @@ void MainWindow::guiUpdate()
(m_DopplerMethod==0 and m_DopplerMethod0>0)) {
//Doppler tracking has just been turned off. Reset dial frequency to "nominal + kHz"
if(m_transmitting) {
- m_dialFreqTx=m_freqNominal + 1000*m_astroWidget->m_kHz;
+ m_dialFreqTx=m_freqNominal + 1000*m_astroWidget->m_kHz + m_astroWidget->m_Hz;
ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreqTx));
Q_EMIT m_config.transceiver_tx_frequency (m_dialFreqTx);
} else {
- f=m_freqNominal + 1000*m_astroWidget->m_kHz;
+ f=m_freqNominal + 1000*m_astroWidget->m_kHz + m_astroWidget->m_Hz;
Q_EMIT m_config.transceiver_frequency(f);
}
}
@@ -1918,36 +2143,39 @@ void MainWindow::guiUpdate()
}
if(nsec != m_sec0) { //Once per second
+ int ipct=0;
+ if(m_monitoring or m_transmitting) ipct=int(100*m_nseq/txDuration);
+ progressBar->setValue(ipct);
QDateTime t = QDateTime::currentDateTimeUtc();
if(m_astroWidget) {
- m_freqMoon=m_dialFreq + 1000*m_astroWidget->m_kHz;
+ m_freqMoon=m_dialFreq + 1000*m_astroWidget->m_kHz + m_astroWidget->m_Hz;
int ndop,ndop00;
- m_astroWidget->astroUpdate(t, m_config.my_grid (), m_hisGrid,m_freqMoon, &ndop, &ndop00);
- if(m_freqNominal>144000000) {
-//Apply Doppler corrections only for 144 MHz and above
- if(m_astroWidget->m_bDopplerTracking and (m_DopplerMethod==1)) {
-// All Doppler correction will be done here; DX station stays at nominal dial frequency.
- int ndopr=m_astroWidget->m_stepHz*qRound(double(ndop)/double(m_astroWidget->m_stepHz));
- if(m_transmitting) {
- m_dialFreqTx=m_freqNominal + 1000*m_astroWidget->m_kHz - ndopr;
- ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreqTx));
- Q_EMIT m_config.transceiver_tx_frequency (m_dialFreqTx);
- } else {
- f=m_freqNominal + 1000*m_astroWidget->m_kHz + ndopr;
- Q_EMIT m_config.transceiver_frequency(f);
- }
- }
+ m_astroWidget->astroUpdate(t, m_config.my_grid (), m_hisGrid,m_freqMoon,
+ &ndop, &ndop00, m_transmitting);
+
+//Apply Doppler corrections only for 50 MHz and above
+ if(m_freqNominal>=50000000) {
+
+ if(m_astroWidget->m_bDopplerTracking) {
+
+ int ndopr=0; // No Doppler Correction
+ if(m_DopplerMethod==1) {
+ // All Doppler correction done here; DX station stays at nominal dial frequency.
+ ndopr=m_astroWidget->m_stepHz*qRound(double(ndop)/double(m_astroWidget->m_stepHz));
+ }
+ if(m_DopplerMethod==2) {
+ // Doppler correction to constant frequency on Moon
+ ndopr=m_astroWidget->m_stepHz*qRound(double(ndop00/2.0)/double(m_astroWidget->m_stepHz));
+ }
- if(m_astroWidget->m_bDopplerTracking and (m_DopplerMethod==2)) {
-// Doppler correction to constant frequency on the Moon
- int ndopr=m_astroWidget->m_stepHz*qRound(double(ndop00/2.0)/double(m_astroWidget->m_stepHz));
if(m_transmitting) {
- m_dialFreqTx=m_freqNominal + 1000*m_astroWidget->m_kHz - ndopr;
+ m_dialFreqTx=m_freqNominal + 1000*m_astroWidget->m_kHz + m_astroWidget->m_Hz - ndopr;
ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreqTx));
Q_EMIT m_config.transceiver_tx_frequency (m_dialFreqTx);
} else {
- f=m_freqNominal + 1000*m_astroWidget->m_kHz + ndopr;
- Q_EMIT m_config.transceiver_frequency(f);
+ m_dialFreq=m_freqNominal + 1000*m_astroWidget->m_kHz + m_astroWidget->m_Hz + ndopr;
+ ui->labDialFreq->setText (Radio::pretty_frequency_MHz_string (m_dialFreq));
+ Q_EMIT m_config.transceiver_frequency(m_dialFreq);
}
}
}
@@ -1972,7 +2200,10 @@ void MainWindow::guiUpdate()
}
} else if(m_monitoring) {
tx_status_label->setStyleSheet("QLabel{background-color: #00ff00}");
- tx_status_label->setText("Receiving ");
+ QString t="Receiving ";
+ if(m_auto and (m_mode.mid(0,4)=="WSPR")) t += QString::number(m_nrx);
+ tx_status_label->setText(t);
+ transmitDisplay(false);
} else if (!m_diskData) {
tx_status_label->setStyleSheet("");
tx_status_label->setText("");
@@ -2006,6 +2237,23 @@ void MainWindow::startTx2()
if(snr>0.0 or snr < -50.0) snr=99.0;
transmit (snr);
signalMeter->setValue(0);
+ if(m_mode.mid(0,4)=="WSPR" and !m_tune) {
+ auto const& bands_model = m_config.bands ();
+ t = " Transmiting " + m_mode + " ----------------------- " +
+ bands_model->data(bands_model->find(m_dialFreq)).toString ();
+ ui->decodedTextBrowser->append(t.rightJustified (71, '-'));
+
+ QFile f {m_dataDir.absoluteFilePath ("ALL_WSPR.TXT")};
+ if (f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
+ QTextStream out(&f);
+ out << QDateTime::currentDateTimeUtc().toString("yyMMdd hhmm")
+ << " Transmitting " << (m_dialFreq / 1.e6) << " MHz: "
+ << m_currentMessage << " " + m_mode << endl;
+ f.close();
+ } else {
+ msgBox("Cannot open \"" + f.fileName () + "\" for append:" + f.errorString ());
+ }
+ }
}
}
@@ -2019,21 +2267,25 @@ void MainWindow::stopTx()
tx_status_label->setText("");
ptt0Timer->start(200); //Sequencer delay
monitor (true);
- m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall, QString::number (ui->rptSpinBox->value ()), m_modeTx, ui->autoButton->isChecked (), m_transmitting);
+ m_messageClient->status_update (m_dialFreq, m_mode, m_hisCall,
+ QString::number (ui->rptSpinBox->value ()),
+ m_modeTx, ui->autoButton->isChecked (), m_transmitting);
}
void MainWindow::stopTx2()
{
- QString rt;
- //Lower PTT
- Q_EMIT m_config.transceiver_ptt (false);
-
- if (m_config.watchdog () && m_repeatMsg>=m_watchdogLimit-1)
- {
- on_stopTxButton_clicked();
- msgBox("Runaway Tx watchdog");
- m_repeatMsg=0;
- }
+ Q_EMIT m_config.transceiver_ptt (false); //Lower PTT
+ if (m_mode.mid(0,4)!="WSPR" and m_config.watchdog() and
+ m_repeatMsg>=m_watchdogLimit-1) {
+ on_stopTxButton_clicked();
+ msgBox("Runaway Tx watchdog");
+ m_repeatMsg=0;
+ }
+ if(m_mode.mid(0,4)=="WSPR" and m_ntr==-1 and m_bandHopping and !m_tuneup) {
+// qDebug () << "Call bandHopping after Tx" << m_tuneup;
+ bandHopping();
+ m_ntr=0;
+ }
}
void MainWindow::ba2msg(QByteArray ba, char message[]) //ba2msg()
@@ -2148,19 +2400,9 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl
decodedtext = decodedtext.string ().left (eom_pos + 1); // remove DXCC entity and worked B4 status. TODO need a better way to do this
}
- /*
- // if(decodedtext.indexOf("Tx")==6) return; //Ignore Tx line
- // int i4=t.mid(i1).length();
- // if(i4>55) i4=55;
- // QString t3=t.mid(i1,i4);
auto t3 = decodedtext.string ();
auto t4 = t3.replace (" CQ DX ", " CQ_DX ").split (" ", QString::SkipEmptyParts);
if(t4.size () < 6) return; //Skip the rest if no decoded text
-*/
- auto t3 = decodedtext.string ();
- auto t4 = t3.replace (" CQ DX ", " CQ_DX ").split (" ", QString::SkipEmptyParts);
- if(t4.size () <5) return; //Skip the rest if no decoded text
-
QString hiscall;
QString hisgrid;
@@ -2188,8 +2430,8 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl
ui->pbTxMode->setText("Tx JT65 #");
m_wideGraph->setModeTx(m_modeTx);
}
- } else if ((decodedtext.isJT9 () && m_modeTx != "JT9") ||
- (decodedtext.isJT65 () && m_modeTx != "JT65")) {
+ } else if ((decodedtext.isJT9 () and m_modeTx != "JT9" and m_mode != "JT4") or
+ (decodedtext.isJT65 () and m_modeTx != "JT65" and m_mode != "JT4")) {
// if we are not allowing mode change then don't process decode
return;
}
@@ -2198,17 +2440,14 @@ void MainWindow::processMessage(QString const& messages, int position, bool ctrl
QString firstcall = decodedtext.call();
// Don't change Tx freq if a station is calling me, unless m_lockTxFreq
// is true or CTRL is held down
- if ((firstcall!=m_config.my_callsign () && firstcall != m_baseCall) || m_lockTxFreq or ctrl)
- {
- if (ui->TxFreqSpinBox->isEnabled ())
- {
- ui->TxFreqSpinBox->setValue(frequency);
- }
- else
- {
- return;
- }
+ if ((firstcall!=m_config.my_callsign () and firstcall != m_baseCall) or
+ m_lockTxFreq or ctrl) {
+ if (ui->TxFreqSpinBox->isEnabled ()) {
+ ui->TxFreqSpinBox->setValue(frequency);
+ } else if(m_mode!="JT4") {
+ return;
}
+ }
int i9=m_QSOText.indexOf(decodedtext.string());
if (i9<0 and !decodedtext.isTX())
@@ -2679,8 +2918,8 @@ void MainWindow::on_dxGridEntry_textChanged(const QString &t) //dxGrid changed
qint64 nsec = QDateTime::currentMSecsSinceEpoch() % 86400;
double utch=nsec/3600.0;
int nAz,nEl,nDmiles,nDkm,nHotAz,nHotABetter;
-
- azdist_(const_cast (m_config.my_grid ().toLatin1().constData()),const_cast (m_hisGrid.toLatin1().constData()),&utch,
+ azdist_(const_cast (m_config.my_grid ().toLatin1().constData()),
+ const_cast (m_hisGrid.toLatin1().constData()),&utch,
&nAz,&nEl,&nDmiles,&nDkm,&nHotAz,&nHotABetter,6,6);
QString t;
t.sprintf("Az: %d",nAz);
@@ -2757,6 +2996,8 @@ void MainWindow::on_actionJT9_1_triggered()
if(m_modeTx!="JT9") on_pbTxMode_clicked();
statusChanged();
m_TRperiod=60;
+ m_modulator.setPeriod(m_TRperiod);
+ m_detector.setPeriod(m_TRperiod);
m_nsps=6912;
m_hsymStop=173;
if(m_config.decode_at_52s()) m_hsymStop=181;
@@ -2771,6 +3012,7 @@ void MainWindow::on_actionJT9_1_triggered()
m_wideGraph->setModeTx(m_modeTx);
ui->pbTxMode->setEnabled(false);
VHF_controls_visible(false);
+ WSPR_config(false);
ui->label_6->setText("Band Activity");
ui->label_7->setText("Rx Frequency");
}
@@ -2781,6 +3023,8 @@ void MainWindow::on_actionJT9W_1_triggered()
if(m_modeTx!="JT9") on_pbTxMode_clicked();
statusChanged();
m_TRperiod=60;
+ m_modulator.setPeriod(m_TRperiod);
+ m_detector.setPeriod(m_TRperiod);
m_nsps=6912;
m_hsymStop=173;
if(m_config.decode_at_52s()) m_hsymStop=181;
@@ -2795,6 +3039,7 @@ void MainWindow::on_actionJT9W_1_triggered()
m_wideGraph->setModeTx(m_modeTx);
ui->pbTxMode->setEnabled(false);
VHF_controls_visible(false);
+ WSPR_config(false);
ui->label_6->setText("Band Activity");
ui->label_7->setText("Rx Frequency");
}
@@ -2810,6 +3055,8 @@ void MainWindow::on_actionJT65_triggered()
if(m_modeTx!="JT65") on_pbTxMode_clicked();
statusChanged();
m_TRperiod=60;
+ m_modulator.setPeriod(m_TRperiod);
+ m_detector.setPeriod(m_TRperiod);
m_nsps=6912; //For symspec only
m_hsymStop=173;
if(m_config.decode_at_52s()) m_hsymStop=181;
@@ -2826,6 +3073,7 @@ void MainWindow::on_actionJT65_triggered()
ui->pbTxMode->setEnabled(false);
bool bVHF=m_config.enable_VHF_features();
VHF_controls_visible(bVHF);
+ WSPR_config(false);
ui->sbSubmode->setMaximum(2);
if(bVHF) {
ui->sbSubmode->setValue(m_nSubMode);
@@ -2845,6 +3093,8 @@ void MainWindow::on_actionJT9_JT65_triggered()
m_nSubMode=0; //Dual-mode always means JT9 and JT65A
statusChanged();
m_TRperiod=60;
+ m_modulator.setPeriod(m_TRperiod);
+ m_detector.setPeriod(m_TRperiod);
m_nsps=6912;
m_hsymStop=173;
if(m_config.decode_at_52s()) m_hsymStop=181;
@@ -2859,6 +3109,7 @@ void MainWindow::on_actionJT9_JT65_triggered()
m_wideGraph->setModeTx(m_modeTx);
ui->pbTxMode->setEnabled(true);
VHF_controls_visible(false);
+ WSPR_config(false);
ui->label_6->setText("Band Activity");
ui->label_7->setText("Rx Frequency");
}
@@ -2869,6 +3120,8 @@ void MainWindow::on_actionJT4_triggered()
m_modeTx="JT4";
statusChanged();
m_TRperiod=60;
+ m_modulator.setPeriod(m_TRperiod);
+ m_detector.setPeriod(m_TRperiod);
m_nsps=6912; //For symspec only
m_hsymStop=181;
// if(m_config.decode_at_52s()) m_hsymStop=181;
@@ -2885,6 +3138,7 @@ void MainWindow::on_actionJT4_triggered()
ui->pbTxMode->setEnabled(false);
bool bVHF=m_config.enable_VHF_features();
VHF_controls_visible(bVHF);
+ WSPR_config(false);
ui->sbSubmode->setMaximum(6);
ui->label_6->setText("Single-Period Decodes");
ui->label_7->setText("Average Decodes");
@@ -2898,6 +3152,70 @@ void MainWindow::on_actionJT4_triggered()
if(m_MinW > m_nSubMode) ui->sbMinW->setValue(m_nSubMode);
}
+void MainWindow::on_actionWSPR_2_triggered()
+{
+ m_mode="WSPR-2";
+ m_modeTx="WSPR-2"; //### not needed ?? ###
+ statusChanged();
+ m_TRperiod=120;
+ m_modulator.setPeriod(m_TRperiod);
+ m_detector.setPeriod(m_TRperiod);
+ m_nsps=6912; //For symspec only
+ m_hsymStop=396;
+ m_toneSpacing=12000.0/8192.0;
+ mode_label->setStyleSheet("QLabel{background-color: #ff00ff}");
+ mode_label->setText(m_mode);
+ ui->actionWSPR_2->setChecked(true);
+ VHF_features_enabled(false);
+ ui->ClrAvgButton->setVisible(false);
+ m_wideGraph->setPeriod(m_TRperiod,m_nsps);
+ m_wideGraph->setMode(m_mode);
+ m_wideGraph->setModeTx(m_modeTx);
+ VHF_controls_visible(false);
+ WSPR_config(true);
+}
+
+void MainWindow::on_actionWSPR_15_triggered()
+{
+ msgBox("WSPR-15 is not yet available");
+}
+
+
+void MainWindow::WSPR_config(bool b)
+{
+ ui->decodedTextBrowser2->setVisible(!b);
+ ui->decodedTextLabel2->setVisible(!b);
+ ui->label_6->setVisible(!b);
+ ui->label_7->setVisible(!b);
+ ui->pbTxMode->setVisible(!b);
+ ui->TxFreqSpinBox->setVisible(!b);
+ ui->RxFreqSpinBox->setVisible(!b);
+ ui->cbTxLock->setVisible(!b);
+ ui->txFirstCheckBox->setVisible(!b);
+ ui->pbR2T->setVisible(!b);
+ ui->pbT2R->setVisible(!b);
+ ui->rptSpinBox->setVisible(!b);
+ ui->label_8->setVisible(!b);
+ ui->labAz->setVisible(!b);
+ ui->labDist->setVisible(!b);
+ ui->logQSOButton->setVisible(!b);
+ ui->label_3->setVisible(!b);
+ ui->label_4->setVisible(!b);
+ ui->dxCallEntry->setVisible(!b);
+ ui->dxGridEntry->setVisible(!b);
+ ui->lookupButton->setVisible(!b);
+ ui->addButton->setVisible(!b);
+ ui->DecodeButton->setEnabled(!b);
+ if(b) {
+ ui->decodedTextLabel->setText(
+ "UTC dB DT Freq Drift Call Grid dBm Dist");
+ auto_tx_label->setText("");
+ } else {
+ ui->decodedTextLabel->setText("UTC dB DT Freq Message");
+ auto_tx_label->setText (m_config.quick_call () ? "Tx-Enable Armed" : "Tx-Enable Disarmed");
+ }
+}
+
void MainWindow::on_TxFreqSpinBox_valueChanged(int n)
{
m_wideGraph->setTxFreq(n);
@@ -2998,49 +3316,34 @@ void MainWindow::on_bandComboBox_activated (int index)
{
auto frequencies = m_config.frequencies ();
auto frequency = frequencies->data (frequencies->index (index, 0));
-
// Lookup band
auto bands = m_config.bands ();
auto band_index = bands->find (frequency);
- if (band_index.isValid ())
- {
- ui->bandComboBox->lineEdit ()->setStyleSheet ({});
- ui->bandComboBox->setCurrentText (band_index.data ().toString ());
- }
- else
- {
- ui->bandComboBox->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}");
- ui->bandComboBox->setCurrentText (bands->data (QModelIndex {}).toString ());
- }
-
+ if (band_index.isValid ()) {
+ ui->bandComboBox->lineEdit ()->setStyleSheet ({});
+ ui->bandComboBox->setCurrentText (band_index.data ().toString ());
+ } else {
+ ui->bandComboBox->lineEdit ()->setStyleSheet ("QLineEdit {color: yellow; background-color : red;}");
+ ui->bandComboBox->setCurrentText (bands->data (QModelIndex {}).toString ());
+ }
auto f = frequency.value ();
- if (m_plus2kHz)
- {
- f += 2000;
- }
-
+ if (m_plus2kHz) f += 2000;
m_bandEdited = true;
band_changed (f);
+ m_wideGraph->setRxBand(band_index.data().toString());
+// qDebug() << "bandComboBox_activated" << index << 0.000001*f;
}
void MainWindow::band_changed (Frequency f)
{
- if (m_bandEdited)
- {
- m_bandEdited = false;
-
- // Upload any queued spots before changing band
- psk_Reporter->sendReport();
-
- if (!m_transmitting)
- {
- monitor (true);
- }
-
- Q_EMIT m_config.transceiver_frequency (f);
- qsy (f);
- setXIT (ui->TxFreqSpinBox->value ());
- }
+ if (m_bandEdited) {
+ m_bandEdited = false;
+ psk_Reporter->sendReport(); // Upload any queued spots before changing band
+ if (!m_transmitting) monitor (true);
+ Q_EMIT m_config.transceiver_frequency (f);
+ qsy (f);
+ setXIT (ui->TxFreqSpinBox->value ());
+ }
}
void MainWindow::enable_DXCC_entity (bool on)
@@ -3167,17 +3470,14 @@ void MainWindow::on_rptSpinBox_valueChanged(int n)
void MainWindow::on_tuneButton_clicked (bool checked)
{
- if (m_tune)
- {
- tuneButtonTimer->start(250);
- }
- else
- {
- m_sentFirst73=false;
- m_repeatMsg=0;
- itone[0]=0;
- on_monitorButton_clicked (true);
- }
+ if (m_tune) {
+ tuneButtonTimer->start(250);
+ } else {
+ m_sentFirst73=false;
+ m_repeatMsg=0;
+ itone[0]=0;
+ on_monitorButton_clicked (true);
+ }
m_tune = checked;
Q_EMIT tune (checked);
}
@@ -3188,10 +3488,17 @@ void MainWindow::stop_tuning ()
on_tuneButton_clicked (false);
}
+void MainWindow::stopTuneATU()
+{
+ on_tuneButton_clicked(false);
+ m_tune=false;
+ m_bTxTime=false;
+}
+
void MainWindow::on_stopTxButton_clicked() //Stop Tx
{
if (m_tune) stop_tuning ();
- if (m_auto) auto_tx_mode (false);
+ if (m_auto and !m_tuneup) auto_tx_mode (false);
m_btxok=false;
m_repeatMsg=0;
}
@@ -3208,10 +3515,7 @@ void MainWindow::rigOpen ()
void MainWindow::on_pbR2T_clicked()
{
- if (ui->TxFreqSpinBox->isEnabled ())
- {
- ui->TxFreqSpinBox->setValue(ui->RxFreqSpinBox->value ());
- }
+ if (ui->TxFreqSpinBox->isEnabled ()) ui->TxFreqSpinBox->setValue(ui->RxFreqSpinBox->value ());
}
void MainWindow::on_pbT2R_clicked()
@@ -3249,12 +3553,12 @@ void MainWindow::on_pbTxMode_clicked()
void MainWindow::setXIT(int n)
{
m_XIT = 0;
- if (m_config.split_mode ())
+ if (m_config.split_mode () and (m_mode != "JT4")) //Don't use XIT in JT4 mode
{
m_XIT=(n/500)*500 - 1500;
}
- if (m_monitoring || m_transmitting)
+ if (m_monitoring or m_transmitting)
{
if (m_config.transceiver_online ())
{
@@ -3264,7 +3568,6 @@ void MainWindow::setXIT(int n)
}
}
}
-
Q_EMIT transmitFrequency (ui->TxFreqSpinBox->value () - m_XIT);
}
@@ -3292,26 +3595,17 @@ void MainWindow::on_cbPlus2kHz_toggled(bool checked)
{
m_plus2kHz = checked;
- if (m_config.transceiver_online (false)) // only update state if not
- // starting up
- {
- // Upload any queued spots before changing band
- psk_Reporter->sendReport();
-
+ if (m_config.transceiver_online (false)) { // update state only if not starting up
+ psk_Reporter->sendReport(); // Upload any queued spots before changing band
auto f = m_dialFreq;
-
- if (m_plus2kHz)
- {
- f += 2000;
- }
- else
- {
- f -= 2000;
- }
-
+ if (m_plus2kHz) {
+ f += 2000;
+ } else {
+ f -= 2000;
+ }
m_bandEdited = true;
band_changed (f);
- }
+ }
}
void MainWindow::handle_transceiver_update (Transceiver::TransceiverState s)
@@ -3378,12 +3672,14 @@ void MainWindow::transmit (double snr)
if(m_nSubMode==2) toneSpacing=4*11025.0/4096.0;
Q_EMIT sendMessage (NUM_JT65_SYMBOLS,
4096.0*12000.0/11025.0, ui->TxFreqSpinBox->value () - m_XIT,
- m_toneSpacing, &m_soundOutput, m_config.audio_output_channel (),
+ toneSpacing, &m_soundOutput, m_config.audio_output_channel (),
true, snr);
}
- if (m_modeTx == "JT9") Q_EMIT sendMessage (NUM_JT9_SYMBOLS, m_nsps,
- ui->TxFreqSpinBox->value () - m_XIT, m_toneSpacing,
- &m_soundOutput, m_config.audio_output_channel (), true, snr);
+ if (m_modeTx == "JT9") {
+ Q_EMIT sendMessage (NUM_JT9_SYMBOLS, m_nsps,
+ ui->TxFreqSpinBox->value () - m_XIT, m_toneSpacing,
+ &m_soundOutput, m_config.audio_output_channel (), true, snr);
+ }
if (m_modeTx == "JT4") {
if(m_nSubMode==0) toneSpacing=4.375;
if(m_nSubMode==1) toneSpacing=2*4.375;
@@ -3397,6 +3693,13 @@ void MainWindow::transmit (double snr)
toneSpacing, &m_soundOutput, m_config.audio_output_channel (),
true, snr);
}
+ if (m_mode=="WSPR-2") { //### Similar code needed for WSPR-15 ###
+
+ Q_EMIT sendMessage (NUM_WSPR_SYMBOLS, 8192.0,
+ ui->TxFreqSpinBox->value()-2, m_toneSpacing,
+ &m_soundOutput, m_config.audio_output_channel(),
+ true, snr);
+ }
}
void MainWindow::on_outAttenuation_valueChanged (int a)
@@ -3512,14 +3815,12 @@ void MainWindow::pskSetLocal ()
, 1
, Qt::MatchExactly);
QString antenna_description;
- if (!matches.isEmpty ())
- {
- antenna_description = stations->index (matches.first ().row (), 2).data ().toString ();
- }
- psk_Reporter->setLocalStation(
- m_config.my_callsign ()
- , m_config.my_grid ()
- , antenna_description, QString {"WSJT-X v" + version() + " " + m_revision}.simplified ());
+ if (!matches.isEmpty ()) {
+ antenna_description = stations->index (matches.first ().row (), 2).data ().toString ();
+ }
+ psk_Reporter->setLocalStation(m_config.my_callsign (), m_config.my_grid (),
+ antenna_description, QString {"WSJT-X v" + version() + " " +
+ m_revision}.simplified ());
}
void MainWindow::transmitDisplay (bool transmitting)
@@ -3532,16 +3833,26 @@ void MainWindow::transmitDisplay (bool transmitting)
m_btxok=true;
}
- auto QSY_allowed = !transmitting || m_config.tx_QSY_allowed () || !m_config.split_mode ();
+ auto QSY_allowed = !transmitting or m_config.tx_QSY_allowed () or
+ !m_config.split_mode ();
if (ui->cbTxLock->isChecked ()) {
ui->RxFreqSpinBox->setEnabled (QSY_allowed);
ui->pbT2R->setEnabled (QSY_allowed);
}
- ui->TxFreqSpinBox->setEnabled (QSY_allowed);
- ui->pbR2T->setEnabled (QSY_allowed);
- ui->cbTxLock->setEnabled (QSY_allowed);
- // only allow +2kHz when not transmitting or if TX QSYs are allowed
+ if(m_mode=="JT4" and (m_dialFreq/1000000 >= 432)) {
+// if(m_mode=="JT4") {
+ ui->TxFreqSpinBox->setValue(1000);
+ ui->TxFreqSpinBox->setEnabled (false);
+ ui->cbTxLock->setChecked(false);
+ ui->cbTxLock->setEnabled(false);
+ } else if(m_mode!="WSPR") {
+ ui->TxFreqSpinBox->setEnabled (QSY_allowed);
+ ui->pbR2T->setEnabled (QSY_allowed);
+ ui->cbTxLock->setEnabled (QSY_allowed);
+ }
+
+ // Allow +2kHz only when not transmitting or if TX QSYs are allowed
ui->cbPlus2kHz->setEnabled (!transmitting || m_config.tx_QSY_allowed ());
// the following are always disallowed in transmit
@@ -3584,6 +3895,7 @@ void::MainWindow::VHF_controls_visible(bool b)
ui->sbDT->setVisible(b);
ui->labTol->setVisible(b);
ui->sbTol->setVisible(b);
+ ui->syncSpinBox->setVisible(b);
}
void::MainWindow::VHF_features_enabled(bool b)
@@ -3744,3 +4056,370 @@ void MainWindow::networkError (QString const& e)
m_messageClient->set_server (m_config.udp_server_name ());
}
}
+
+void MainWindow::on_syncSpinBox_valueChanged(int n)
+{
+ m_minSync=n;
+}
+
+void MainWindow::p1ReadFromStderr() //p1readFromStderr
+{
+ QByteArray t=p1.readAllStandardError();
+ msgBox(t);
+}
+
+void MainWindow::p1Error (QProcess::ProcessError e)
+{
+ if(!m_killAll) {
+ msgBox("Error starting or running\n" + m_appDir + "/wsprd");
+ qDebug() << e; // silence compiler warning
+ exit(1);
+ }
+}
+
+void MainWindow::p1ReadFromStdout() //p1readFromStdout
+{
+ QString t1;
+ while(p1.canReadLine()) {
+ QString t(p1.readLine());
+ if(t.indexOf("") >= 0) {
+ ui->DecodeButton->setChecked (false);
+ if(m_uploadSpots) {
+ float x=rand()/((double)RAND_MAX + 1.0);
+ int msdelay=20000*x;
+ uploadTimer->start(msdelay); //Upload delay
+ } else {
+ QFile f(QDir::toNativeSeparators(m_dataDir.absolutePath()) + "/wspr_spots.txt");
+ if(f.exists()) f.remove();
+ }
+ if(!m_saveAll and !m_diskData) {
+ QFile savedWav(m_fname);
+ savedWav.remove();
+ }
+/*
+ if(m_saveAll and !m_diskData) {
+ int i1=m_fname.indexOf(".wav");
+ QString sc2=m_fname.mid(0,i1) + ".c2";
+ QFile savedC2(sc2);
+ savedC2.remove();
+ }
+*/
+ m_RxLog=0;
+ m_startAnother=m_loopall;
+ m_blankLine=true;
+ return;
+ } else {
+
+ int n=t.length();
+ t=t.mid(0,n-2) + " ";
+ t.remove(QRegExp("\\s+$"));
+ QStringList rxFields = t.split(QRegExp("\\s+"));
+ QString rxLine;
+ QString grid="";
+ if ( rxFields.count() == 8 ) {
+ rxLine = QString("%1 %2 %3 %4 %5 %6 %7 %8")
+ .arg(rxFields.at(0), 4)
+ .arg(rxFields.at(1), 4)
+ .arg(rxFields.at(2), 5)
+ .arg(rxFields.at(3), 11)
+ .arg(rxFields.at(4), 4)
+ .arg(rxFields.at(5), -12)
+ .arg(rxFields.at(6), -6)
+ .arg(rxFields.at(7), 3);
+ grid = rxFields.at(6);
+ } else if ( rxFields.count() == 7 ) { // Type 2 message
+ rxLine = QString("%1 %2 %3 %4 %5 %6 %7 %8")
+ .arg(rxFields.at(0), 4)
+ .arg(rxFields.at(1), 4)
+ .arg(rxFields.at(2), 5)
+ .arg(rxFields.at(3), 11)
+ .arg(rxFields.at(4), 4)
+ .arg(rxFields.at(5), -12)
+ .arg("", -6)
+ .arg(rxFields.at(6), 3);
+ } else {
+ rxLine = t;
+ }
+ if(grid!="") {
+ double utch=0.0;
+ int nAz,nEl,nDmiles,nDkm,nHotAz,nHotABetter;
+ azdist_(const_cast (m_config.my_grid ().toLatin1().constData()),
+ const_cast (grid.toLatin1().constData()),&utch,
+ &nAz,&nEl,&nDmiles,&nDkm,&nHotAz,&nHotABetter,6,6);
+ QString t1;
+ if(m_config.miles()) {
+ t1.sprintf("%7d",nDmiles);
+ } else {
+ t1.sprintf("%7d",nDkm);
+ }
+ rxLine += t1;
+ }
+
+ if (m_config.insert_blank () && m_blankLine) {
+ QString band;
+ auto const& bands_model = m_config.bands ();
+ Frequency f=1000000.0*rxFields.at(3).toDouble()+0.5;
+ band = ' ' + bands_model->data (bands_model->find (f)).toString ();
+ ui->decodedTextBrowser->append(band.rightJustified (71, '-'));
+ m_blankLine = false;
+ }
+
+// ui->decodedTextBrowser->append(t);
+ ui->decodedTextBrowser->append(rxLine);
+ }
+ }
+}
+
+void MainWindow::uploadSpots()
+{
+ if(m_diskData) return;
+ if(m_uploading) {
+ qDebug() << "Previous upload has not completed, spots were lost";
+ return;
+ }
+ QString rfreq = QString("%1").arg(0.000001*(m_dialFreqRxWSPR + 1500), 0, 'f', 6);
+ QString tfreq = QString("%1").arg(0.000001*(m_dialFreqRxWSPR +
+ ui->TxFreqSpinBox->value()), 0, 'f', 6);
+ wsprNet->upload(m_config.my_callsign(), m_config.my_grid(), rfreq, tfreq,
+ m_mode, QString::number(ui->autoButton->isChecked() ? m_pctx : 0),
+ QString::number(m_dBm), version(),
+ QDir::toNativeSeparators(m_dataDir.absolutePath()) + "/wspr_spots.txt");
+ m_uploading = true;
+}
+
+void MainWindow::uploadResponse(QString response)
+{
+ if (response == "done") {
+ m_uploading=false;
+ } else if (response == "Upload Failed") {
+ m_uploading=false;
+ }
+// qDebug() << "uploadResponse" << response;
+}
+
+
+void MainWindow::p3ReadFromStdout() //p3readFromStdout
+{
+ QByteArray t=p3.readAllStandardOutput();
+ if(t.length()>0) {
+ msgBox("user_hardware stdout:\n\n"+t+"\n"+m_cmnd);
+ }
+}
+
+void MainWindow::p3ReadFromStderr() //p3readFromStderr
+{
+ QByteArray t=p3.readAllStandardError();
+ if(t.length()>0) {
+ msgBox("user_hardware stderr:\n\n"+t+"\n"+m_cmnd);
+ }
+}
+
+void MainWindow::p3Error(QProcess::ProcessError e) //p3rror
+{
+ msgBox("Error attempting to run user_hardware.\n\n"+m_cmnd);
+ qDebug() << e; // silence compiler warning
+}
+
+
+void MainWindow::on_TxPowerComboBox_currentIndexChanged(const QString &arg1)
+{
+ int i1=arg1.indexOf(" ");
+ m_dBm=arg1.mid(0,i1).toInt();
+}
+
+void MainWindow::on_sbTxPercent_valueChanged(int n)
+{
+ m_pctx=n;
+ m_rxavg=1.0;
+ if(m_pctx>0) {
+ m_rxavg=100.0/m_pctx - 1.0; //Average # of Rx's per Tx
+ ui->pbTxNext->setEnabled(true);
+ } else {
+ m_txNext=false;
+ ui->pbTxNext->setEnabled(false);
+ ui->pbTxNext->setChecked(false);
+ }
+}
+
+void MainWindow::on_cbUploadWSPR_Spots_toggled(bool b)
+{
+ m_uploadSpots=b;
+ if(m_uploadSpots) ui->cbUploadWSPR_Spots->setStyleSheet("");
+ if(!m_uploadSpots) ui->cbUploadWSPR_Spots->setStyleSheet(
+ "QCheckBox{background-color: yellow}");
+}
+
+void MainWindow::on_WSPRfreqSpinBox_valueChanged(int n)
+{
+ ui->TxFreqSpinBox->setValue(n);
+}
+
+void MainWindow::on_pbTxNext_clicked(bool b)
+{
+ m_txNext=b;
+}
+
+void MainWindow::on_cbBandHop_toggled(bool b)
+{
+ m_bandHopping=b;
+}
+
+void MainWindow::bandHopping()
+{
+ QString bandName[]={"160","80","60","40","30","20","17","15","12","10"};
+ QDateTime t = QDateTime::currentDateTimeUtc();
+ QString date = t.date().toString("yyyy MMM dd").trimmed();
+ QString utc = t.time().toString().trimmed();
+ int nyear=t.date().year();
+ int month=t.date().month();
+ int nday=t.date().day();
+ int nhr=t.time().hour();
+ int nmin=t.time().minute();
+ float sec=t.time().second() + 0.001*t.time().msec();
+ float uth=nhr + nmin/60.0 + sec/3600.0;
+ int isun;
+ int iband0,iband;
+ int ntxnext;
+ int i,j;
+
+ static int icall=0;
+ if(m_hopTest) uth+= 2.0*icall/60.0;
+ icall++;
+
+// Find grayline status, isun: 0=Sunrise, 1=Day, 2=Sunset, 3=Night
+ hopping_(&nyear, &month, &nday, &uth,
+ const_cast (m_config.my_grid ().toLatin1().constData()),
+ &m_grayDuration, &m_pctx, &isun, &iband0, &ntxnext, 6);
+
+/*
+ if(m_auto and ntxnext==1) {
+ m_nrx=0;
+ } else {
+ m_nrx=1;
+ }
+*/
+
+ QString bname;
+ QStringList s;
+ if(isun==0) s=m_sunriseBands;
+ if(isun==1) s=m_dayBands;
+ if(isun==2) s=m_sunsetBands;
+ if(isun==3) s=m_nightBands;
+
+ Frequency f0;
+ iband=-1;
+ for(i=0; idata (frequencies->index (i, 0));
+ auto f = frequency.value();
+ if(f==0) break;
+ if(f==f0) {
+ on_bandComboBox_activated(i); //Set new band
+// qDebug() << nhr << nmin << int(sec) << "Band selected" << i << 0.000001*f0 << 0.000001*f;
+ break;
+ }
+ }
+
+ m_cmnd="";
+ QFile f1 {m_appDir + "/user_hardware.bat"};
+ if(f1.exists()) {
+ m_cmnd=QDir::toNativeSeparators (m_appDir + "/user_hardware.bat ") + bname;
+ }
+ QFile f2 {m_appDir + "/user_hardware.cmd"};
+ if(f2.exists()) {
+ m_cmnd=QDir::toNativeSeparators (m_appDir + "/user_hardware.cmd ") + bname;
+ }
+ QFile f3 {m_appDir + "/user_hardware.exe"};
+ if(f3.exists()) {
+ m_cmnd=QDir::toNativeSeparators (m_appDir + "/user_hardware.exe ") + bname;
+ }
+ QFile f4 {m_appDir + "/user_hardware"};
+ if(f4.exists()) {
+ m_cmnd=QDir::toNativeSeparators (m_appDir + "/user_hardware ") + bname;
+ }
+ if(m_cmnd!="") p3.start(m_cmnd); // Execute user's hardware controller
+
+// Displat grayline status
+ QString dailySequence[4]={"Sunrise grayline","Day","Sunset grayline","Night"};
+ auto_tx_label->setText(dailySequence[isun]);
+
+// Produce a short tuneup signal
+ s=m_tuneBands;
+ m_tuneup=false;
+ for(int i=0; istart(2500);
+ }
+}
+
+void MainWindow::on_pushButton_clicked()
+{
+ m_hopTest=true;
+// for(int i=0; i<720; i++) {
+ bandHopping();
+// }
+ m_hopTest=false;
+}
+
+void MainWindow::on_sunriseBands_editingFinished()
+{
+ m_sunriseBands=ui->sunriseBands->text().split(" ", QString::SkipEmptyParts);
+}
+
+void MainWindow::on_dayBands_editingFinished()
+{
+ m_dayBands=ui->dayBands->text().split(" ", QString::SkipEmptyParts);
+}
+
+void MainWindow::on_sunsetBands_editingFinished()
+{
+ m_sunsetBands=ui->sunsetBands->text().split(" ", QString::SkipEmptyParts);
+}
+
+void MainWindow::on_nightBands_editingFinished()
+{
+ m_nightBands=ui->nightBands->text().split(" ", QString::SkipEmptyParts);
+}
+
+void MainWindow::on_tuneBands_editingFinished()
+{
+ m_tuneBands=ui->tuneBands->text().split(" ", QString::SkipEmptyParts);
+}
+
+void MainWindow::on_graylineDuration_editingFinished()
+{
+ m_grayDuration=ui->graylineDuration->text().toInt();
+}
diff --git a/mainwindow.h b/mainwindow.h
index f7c75a9e0..2358415ae 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -30,10 +30,12 @@
#include "Detector.hpp"
#include "Modulator.hpp"
#include "decodedtext.h"
+#include "wsprnet.h"
#define NUM_JT4_SYMBOLS 206
#define NUM_JT65_SYMBOLS 126
#define NUM_JT9_SYMBOLS 85
+#define NUM_WSPR_SYMBOLS 162
#define NUM_CW_SYMBOLS 250
#define TX_SAMPLE_RATE 48000
@@ -83,6 +85,9 @@ public slots:
void readFromStdout();
void readFromStderr();
void jt9_error(QProcess::ProcessError);
+ void p1ReadFromStdout();
+ void p1ReadFromStderr();
+ void p1Error(QProcess::ProcessError);
void setXIT(int n);
void setFreq4(int rxFreq, int txFreq);
void clrAvg();
@@ -188,6 +193,7 @@ private slots:
void band_changed (Frequency);
void monitor (bool);
void stop_tuning ();
+ void stopTuneATU();
void auto_tx_mode (bool);
void on_actionMessage_averaging_triggered();
void on_sbTol_valueChanged(int i);
@@ -203,6 +209,28 @@ private slots:
void on_cbTx6_toggled(bool b);
void networkError (QString const&);
void on_ClrAvgButton_clicked();
+ void on_actionWSPR_2_triggered();
+ void on_actionWSPR_15_triggered();
+ void on_syncSpinBox_valueChanged(int n);
+ void on_TxPowerComboBox_currentIndexChanged(const QString &arg1);
+ void on_sbTxPercent_valueChanged(int n);
+ void on_cbUploadWSPR_Spots_toggled(bool b);
+ void WSPR_config(bool b);
+ void uploadSpots();
+ void uploadResponse(QString response);
+ void p3ReadFromStdout();
+ void p3ReadFromStderr();
+ void p3Error(QProcess::ProcessError e);
+ void on_WSPRfreqSpinBox_valueChanged(int n);
+ void on_pbTxNext_clicked(bool b);
+ void on_cbBandHop_toggled(bool b);
+ void on_sunriseBands_editingFinished();
+ void on_pushButton_clicked();
+ void on_dayBands_editingFinished();
+ void on_sunsetBands_editingFinished();
+ void on_nightBands_editingFinished();
+ void on_tuneBands_editingFinished();
+ void on_graylineDuration_editingFinished();
private:
void enable_DXCC_entity (bool on);
@@ -249,6 +277,7 @@ private:
QScopedPointer m_msgAvgWidget;
Frequency m_dialFreq;
+ Frequency m_dialFreqRxWSPR;
Detector m_detector;
SoundInput m_soundInput;
@@ -263,6 +292,7 @@ private:
qint64 m_dialFreqTx;
float m_DTtol;
+ float m_rxavg;
qint32 m_waterfallAvg;
qint32 m_ntx;
@@ -274,6 +304,8 @@ private:
qint32 m_RxLog;
qint32 m_nutc0;
qint32 m_nrx;
+ qint32 m_ntr;
+ qint32 m_tx;
qint32 m_hsym;
qint32 m_TRperiod;
qint32 m_nsps;
@@ -291,6 +323,12 @@ private:
qint32 m_nclearave;
qint32 m_DopplerMethod;
qint32 m_DopplerMethod0;
+ qint32 m_minSync;
+ qint32 m_dBm;
+ qint32 m_pctx;
+ qint32 m_nseq;
+ qint32 m_grayDuration;
+ qint32 m_band00;
bool m_btxok; //True if OK to transmit
bool m_diskData;
@@ -338,6 +376,15 @@ private:
bool m_bShMsgs;
bool m_bDopplerTracking;
bool m_bDopplerTracking0;
+ bool m_uploadSpots;
+ bool m_uploading;
+ bool m_txNext;
+ bool m_grid6;
+ bool m_bandHopping;
+ bool m_hopTest;
+ bool m_tuneup;
+ bool m_bTxTime;
+ bool m_rxDone;
float m_pctZap;
@@ -347,6 +394,8 @@ private:
QLabel * last_tx_label;
QLabel * auto_tx_label;
+ QProgressBar* progressBar;
+
QMessageBox msgBox0;
QFuture* future1;
@@ -357,6 +406,10 @@ private:
QFutureWatcher* watcher3;
QProcess proc_jt9;
+ QProcess p1;
+ QProcess p3;
+
+ WSPRNet *wsprNet;
QTimer m_guiTimer;
QTimer* ptt1Timer; //StartTx delay
@@ -364,6 +417,8 @@ private:
QTimer* logQSOTimer;
QTimer* killFileTimer;
QTimer* tuneButtonTimer;
+ QTimer* uploadTimer;
+ QTimer* tuneATU_Timer;
QString m_path;
QString m_pbdecoding_style1;
@@ -389,9 +444,17 @@ private:
QString m_msgSent0;
QString m_fileToSave;
QString m_band;
+ QString m_c2name;
QStringList m_prefix;
QStringList m_suffix;
+ QStringList m_sunriseBands;
+ QStringList m_dayBands;
+ QStringList m_sunsetBands;
+ QStringList m_nightBands;
+ QStringList m_tuneBands;
+
+ QMap m_fWSPR;
QHash m_pfx;
QHash m_sfx;
@@ -444,6 +507,7 @@ private:
void replyToCQ (QTime, qint32 snr, float delta_time, quint32 delta_frequency, QString const& mode, QString const& message_text);
void replayDecodes ();
void postDecode (bool is_new, QString const& message);
+ void bandHopping();
};
extern void getfile(QString fname, int ntrperiod);
@@ -456,7 +520,7 @@ extern int ptt(int nport, int ntx, int* iptt, int* nopen);
extern "C" {
//----------------------------------------------------- C and Fortran routines
- void symspec_(int* k, int* ntrperiod, int* nsps, int* ingain,
+ void symspec_(int* k, int* ntrperiod, int* nsps, int* ingain, int* minw,
float* px, float s[], float* df3, int* nhsym, int* npts8);
void gen4_(char* msg, int* ichk, char* msgsent, int itone[],
@@ -468,6 +532,8 @@ extern "C" {
void gen65_(char* msg, int* ichk, char* msgsent, int itone[],
int* itext, int len1, int len2);
+ void genwspr_(char* msg, char* msgsent, int itone[], int len1, int len2);
+
bool stdmsg_(const char* msg, int len);
void azdist_(char* MyGrid, char* HisGrid, double* utch, int* nAz, int* nEl,
@@ -481,6 +547,12 @@ extern "C" {
int fftwf_import_wisdom_from_filename(const char *);
int fftwf_export_wisdom_to_filename(const char *);
+ void wspr_downsample_(short int d2[], int* k);
+ void savec2_(char* fname, int* m_TRseconds, double* m_dialFreq, int len1);
+
+ void hopping_(int* nyear, int* month, int* nday, float* uth, char* MyGrid,
+ int* nduration, int* npctx, int* isun, int* iband,
+ int* ntxnext, int len);
}
#endif // MAINWINDOW_H
diff --git a/mainwindow.ui b/mainwindow.ui
index 84445e435..8c62ea4ff 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -11,14 +11,14 @@
-
+
0
0
- 825
+ 0
460
@@ -38,6 +38,9 @@
-
+
+ QLayout::SetDefaultConstraint
+
-
@@ -556,58 +559,7 @@
-
-
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 60
- 0
-
-
-
- Tolerance for offset from selected Rx frequency.
-
-
- F tol 500
-
-
- Qt::AlignCenter
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 18
- 16777215
-
-
-
- 7
-
-
- 5
-
-
-
-
-
- -
+
-
-
@@ -656,7 +608,7 @@
- -
+
-
@@ -678,7 +630,7 @@
- -
+
-
<html><head/><body><p>Tx frequency tracks Rx frequency</p></body></html>
@@ -688,7 +640,7 @@
- -
+
-
5
@@ -742,23 +694,7 @@
- -
-
-
- Qt::Vertical
-
-
- QSizePolicy::Fixed
-
-
-
- 20
- 18
-
-
-
-
- -
+
-
Tolerance for expected time offset.
@@ -783,140 +719,7 @@
- -
-
-
-
-
-
- Set minimum width expected for Doppler-spread tones
-
-
- MinW A
-
-
- Qt::AlignCenter
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 18
- 16777215
-
-
-
- 0
-
-
- 6
-
-
-
-
-
- -
-
-
-
- 40
- 16777215
-
-
-
- Check to add 2.5 s to expected propagation delay.
-
-
- EME
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 48
- 16777215
-
-
-
- Set Tx frequency to Rx Frequency
-
-
- Tx<Rx
-
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 100
- 20
-
-
-
- Audio Rx frequency
-
-
- Hz
-
-
- Rx
-
-
- 200
-
-
- 5000
-
-
- 1500
-
-
-
- -
-
-
-
- 60
- 23
-
-
-
-
- 105
- 16777215
-
-
-
- Check to Tx in even minutes, uncheck for odd minutes
-
-
- Tx even
-
-
-
- -
@@ -939,6 +742,9 @@
Audio Tx frequency
+
+ Qt::AlignCenter
+
Hz
@@ -956,7 +762,7 @@
- -
+
-
false
@@ -969,7 +775,7 @@
- -
+
-
-
@@ -1008,7 +814,7 @@
- -
+
-
Qt::Horizontal
@@ -1024,6 +830,235 @@
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+ Sync
+
+
+ 10
+
+
+ 1
+
+
+
+ -
+
+
-
+
+
+ Set minimum width expected for Doppler-spread tones
+
+
+ MinW A
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 18
+ 16777215
+
+
+
+ 0
+
+
+ 6
+
+
+
+
+
+ -
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Check to add 2.5 s to expected propagation delay.
+
+
+ EME
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 48
+ 16777215
+
+
+
+ Set Tx frequency to Rx Frequency
+
+
+ Tx<Rx
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 100
+ 20
+
+
+
+ Audio Rx frequency
+
+
+ Qt::AlignCenter
+
+
+ Hz
+
+
+ Rx
+
+
+ 200
+
+
+ 5000
+
+
+ 1500
+
+
+
+ -
+
+
+
+ 60
+ 23
+
+
+
+
+ 105
+ 16777215
+
+
+
+ Check to Tx in even minutes, uncheck for odd minutes
+
+
+ Tx even
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 60
+ 0
+
+
+
+ Tolerance for offset from selected Rx frequency.
+
+
+ F tol 500
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 18
+ 16777215
+
+
+
+ 7
+
+
+ 5
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
-
@@ -1381,7 +1416,7 @@
-
-
+
0
0
@@ -1392,6 +1427,12 @@
200
+
+
+ 0
+ 0
+
+
QTabWidget::West
@@ -1399,7 +1440,7 @@
QTabWidget::Triangular
- 0
+ 1
@@ -2197,6 +2238,361 @@ list. The list can be maintained in Settings (F2).
+
+
+ 3
+
+
+
+
+ 30
+ 22
+ 200
+ 171
+
+
+
+ -
+
+
+
+ 16777215
+ 20
+
+
+
+
+ 10
+
+
+
+ WSPR Mode
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+ Hz
+
+
+ Tx
+
+
+ 1400
+
+
+ 1600
+
+
+ 1500
+
+
+
+ -
+
+
+ Upload spots
+
+
+
+ -
+
+
+ Qt::AlignCenter
+
+
+ %
+
+
+ Tx Pct
+
+
+ 100
+
+
+ 20
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 5
+ 17
+
+
+
+
+ -
+
+
+ true
+
+
+ Band hopping
+
+
+
+ -
+
+
+ QPushButton:checked {
+ background-color: red;
+ border-style: outset;
+ border-width: 1px;
+ border-radius: 5px;
+ border-color: black;
+ min-width: 5em;
+ padding: 3px;
+}
+
+
+ Tx Next
+
+
+ true
+
+
+
+ -
+
+
+
+
+
+
+
+ 4
+
+
+
+
+ 100
+ 190
+ 75
+ 23
+
+
+
+ Test
+
+
+
+
+
+ 30
+ 30
+ 211
+ 152
+
+
+
+ -
+
+
+
+ 55
+ 0
+
+
+
+
+ 60
+ 16777215
+
+
+
+ Sunrise:
+
+
+
+ -
+
+
+
+ 160
+ 16777215
+
+
+
+ 160 80 40 30 20
+
+
+
+ -
+
+
+
+ 55
+ 0
+
+
+
+
+ 60
+ 16777215
+
+
+
+ Day:
+
+
+
+ -
+
+
+
+ 160
+ 16777215
+
+
+
+ 30 20 17 15 12 10
+
+
+
+ -
+
+
+
+ 55
+ 0
+
+
+
+
+ 60
+ 16777215
+
+
+
+ Sunset:
+
+
+
+ -
+
+
+
+ 160
+ 16777215
+
+
+
+ 160 80 40 30 20
+
+
+
+ -
+
+
+
+ 55
+ 0
+
+
+
+
+ 60
+ 16777215
+
+
+
+ Night:
+
+
+
+ -
+
+
+
+ 160
+ 16777215
+
+
+
+ 160 80 40 30 20
+
+
+
+ -
+
+
+
+ 55
+ 0
+
+
+
+
+ 60
+ 16777215
+
+
+
+ Tune:
+
+
+
+ -
+
+
+
+ 160
+ 16777215
+
+
+
+ 80 40 30 20 17 15 12 10
+
+
+
+ -
+
+
+
+ 55
+ 0
+
+
+
+
+ 60
+ 16777215
+
+
+
+ Gray time:
+
+
+
+ -
+
+
+
+ 160
+ 16777215
+
+
+
+ 60
+
+
+
+
+
+
@@ -2508,6 +2904,22 @@ QLabel[oob="true"] {
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Fixed
+
+
+
+ 20
+ 1
+
+
+
+
@@ -2588,6 +3000,8 @@ QLabel[oob="true"] {
+
+
@@ -2973,6 +3387,25 @@ QLabel[oob="true"] {
Include correlation
+
+
+ true
+
+
+ WSPR-2
+
+
+
+
+ true
+
+
+ false
+
+
+ WSPR-15
+
+
@@ -2998,7 +3431,6 @@ QLabel[oob="true"] {
txFirstCheckBox
TxFreqSpinBox
rptSpinBox
- tabWidget
genStdMsgsPushButton
tx1
tx2
diff --git a/plotter.cpp b/plotter.cpp
index 95c5254e4..662937433 100644
--- a/plotter.cpp
+++ b/plotter.cpp
@@ -37,6 +37,7 @@ CPlotter::CPlotter(QWidget *parent) : //CPlotter Constructor
m_Percent2DScreen = 30; //percent of screen used for 2D display
m_txFreq=0;
m_fftBinWidth=1500.0/2048.0;
+ m_bScaleOK=false;
}
CPlotter::~CPlotter() { } // Destructor
@@ -92,8 +93,10 @@ void CPlotter::draw(float swide[], bool bScroll) //dr
int j,j0,y2;
float y;
- double gain = pow(10.0,0.02*m_plotGain);
+ double fac = sqrt(m_binsPerPixel*m_waterfallAvg/15.0);
+ double gain = fac*pow(10.0,0.02*m_plotGain);
double gain2d = pow(10.0,0.02*(m_plot2dGain));
+ qDebug() << m_binsPerPixel << m_waterfallAvg << m_plotGain << gain;
//move current data down one line (must do this before attaching a QPainter object)
if(bScroll) m_WaterfallPixmap.scroll(0,1,0,0,m_w,m_h1);
@@ -159,7 +162,7 @@ void CPlotter::draw(float swide[], bool bScroll) //dr
}
}
- if(m_bLinearAvg) { //Linear Avg
+ if(m_bLinearAvg) { //Linear Avg (yellow)
float sum=0.0;
int j=j0+m_binsPerPixel*i;
for(int k=0; k1.0e29) m_line=0;
m_line++;
if(m_line == 13) {
- UTCstr();
painter1.setPen(Qt::white);
- painter1.drawText(5,10,m_sutc);
+ QString t=QDateTime::currentDateTimeUtc().toString("hh:mm") + " " + m_rxBand;
+ painter1.drawText(5,10,t);
}
if(m_mode=="JT4") {
@@ -189,7 +192,6 @@ void CPlotter::draw(float swide[], bool bScroll) //dr
painter2D.setPen(pen3);
Font.setWeight(QFont::Bold);
painter2D.setFont(Font);
-// qDebug() << "B" << m_rxFreq;
int x1=XfromFreq(m_rxFreq);
y=0.2*m_h2;
painter2D.drawText(x1-4,y,"T");
@@ -201,22 +203,7 @@ void CPlotter::draw(float swide[], bool bScroll) //dr
painter2D.drawText(x1-4,y,"73");
}
update(); //trigger a new paintEvent
-}
-
-void CPlotter::UTCstr() //UTCstr
-{
- int ihr,imin;
- if(jt9com_.ndiskdat != 0) {
- ihr=jt9com_.nutc/100;
- imin=jt9com_.nutc % 100;
- } else {
- qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000;
- imin=ms/60000;
- ihr=imin/60;
- imin=imin % 60;
- imin=imin - (imin % (m_TRperiod/60));
- }
- sprintf(m_sutc,"%2.2d:%2.2d",ihr,imin);
+ m_bScaleOK=true;
}
void CPlotter::DrawOverlay() //DrawOverlay()
@@ -225,7 +212,6 @@ void CPlotter::DrawOverlay() //DrawOverlay()
if(m_WaterfallPixmap.isNull()) return;
int w = m_WaterfallPixmap.width();
int x,y,x1,x2;
-// int nHzDiv[11]={0,50,100,200,200,200,500,500,500,500,500};
float pixperdiv;
double df = m_binsPerPixel*m_fftBinWidth;
@@ -233,7 +219,7 @@ void CPlotter::DrawOverlay() //DrawOverlay()
{
QPainter painter(&m_OverlayPixmap);
painter.initFrom(this);
- QLinearGradient gradient(0, 0, 0 ,m_h2); //fill background with gradient
+ QLinearGradient gradient(0, 0, 0 ,m_h2); //fill background with gradient
gradient.setColorAt(1, Qt::black);
gradient.setColorAt(0, Qt::darkBlue);
painter.setBrush(gradient);
@@ -293,13 +279,13 @@ void CPlotter::DrawOverlay() //DrawOverlay()
//draw tick marks on upper scale
pixperdiv = m_freqPerDiv/df;
- for( int i=0; i const& cl) {m_ColorTbl = cl;}
void setFlatten(bool b);
void setTol(int n);
+ void setRxBand(QString band);
signals:
void freezeDecode1(int n);
@@ -88,7 +92,6 @@ protected:
private:
void MakeFrequencyStrs();
- void UTCstr();
int XfromFreq(float f);
float FreqfromX(int x);
@@ -106,6 +109,7 @@ private:
qint32 m_plot2dGain;
qint32 m_plot2dZero;
qint32 m_binsPerPixel;
+ qint32 m_waterfallAvg;
qint32 m_w;
qint32 m_Flatten;
qint32 m_nSubMode;
@@ -120,6 +124,7 @@ private:
QString m_HDivText[483];
QString m_mode;
QString m_modeTx;
+ QString m_rxBand;
bool m_Running;
bool m_paintEventBusy;
diff --git a/widegraph.cpp b/widegraph.cpp
index 23a0a892a..0f1dfd87d 100644
--- a/widegraph.cpp
+++ b/widegraph.cpp
@@ -53,8 +53,11 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) :
ui->widePlot->setFlatten(m_bFlatten);
ui->widePlot->setBreadth(m_settings->value("PlotWidth",1000).toInt());
ui->bppSpinBox->setValue(n);
+ m_nsmo=m_settings->value("SmoothYellow",1).toInt();
+ ui->smoSpinBox->setValue(m_nsmo);
m_waterfallAvg = m_settings->value("WaterfallAvg",5).toInt();
ui->waterfallAvgSpinBox->setValue(m_waterfallAvg);
+ ui->widePlot->setWaterfallAvg(m_waterfallAvg);
ui->widePlot->setCurrent(m_settings->value("Current",false).toBool());
ui->widePlot->setCumulative(m_settings->value("Cumulative",true).toBool());
ui->widePlot->setLinearAvg(m_settings->value("LinearAvg",false).toBool());
@@ -111,6 +114,7 @@ void WideGraph::saveSettings() //saveS
m_settings->setValue ("Plot2dZero", ui->widePlot->plot2dZero());
m_settings->setValue ("PlotWidth", ui->widePlot->plotWidth ());
m_settings->setValue ("BinsPerPixel", ui->bppSpinBox->value ());
+ m_settings->setValue ("SmoothYellow", ui->smoSpinBox->value ());
m_settings->setValue ("WaterfallAvg", ui->waterfallAvgSpinBox->value ());
m_settings->setValue ("Current", ui->widePlot->current());
m_settings->setValue ("Cumulative", ui->widePlot->cumulative());
@@ -176,6 +180,7 @@ void WideGraph::on_bppSpinBox_valueChanged(int n) //b
void WideGraph::on_waterfallAvgSpinBox_valueChanged(int n) //Navg
{
m_waterfallAvg = n;
+ ui->widePlot->setWaterfallAvg(n);
}
void WideGraph::keyPressEvent(QKeyEvent *e) //F11, F12
@@ -282,9 +287,15 @@ void WideGraph::on_spec2dComboBox_currentIndexChanged(const QString &arg1)
ui->widePlot->setCurrent(false);
ui->widePlot->setCumulative(false);
ui->widePlot->setLinearAvg(false);
+ ui->smoSpinBox->setEnabled(false);
+ ui->labSmooth->setEnabled(false);
if(arg1=="Current") ui->widePlot->setCurrent(true);
if(arg1=="Cumulative") ui->widePlot->setCumulative(true);
- if(arg1=="Linear Avg") ui->widePlot->setLinearAvg(true);
+ if(arg1=="Linear Avg") {
+ ui->widePlot->setLinearAvg(true);
+ ui->smoSpinBox->setEnabled(true);
+ ui->labSmooth->setEnabled(true);
+ }
}
void WideGraph::on_fSplitSpinBox_valueChanged(int n) //fSplit
@@ -309,6 +320,12 @@ void WideGraph::setDialFreq(double d) //setDialFreq
ui->widePlot->setDialFreq(d);
}
+void WideGraph::setRxBand(QString band)
+{
+ ui->widePlot->setRxBand(band);
+}
+
+
void WideGraph::on_fStartSpinBox_valueChanged(int n) //fStart
{
ui->widePlot->setStartFreq(n);
@@ -384,12 +401,13 @@ void WideGraph::on_zeroSlider_valueChanged(int value) //Zero
void WideGraph::on_gain2dSlider_valueChanged(int value) //Gain2
{
ui->widePlot->setPlot2dGain(value);
-// ui->widePlot->draw(swide);
+ if(ui->widePlot->m_bScaleOK) ui->widePlot->draw(swide,false);
}
void WideGraph::on_zero2dSlider_valueChanged(int value) //Zero2
{
ui->widePlot->setPlot2dZero(value);
+// ui->widePlot->draw(swide,false);
}
void WideGraph::setTol(int n) //setTol
@@ -398,3 +416,13 @@ void WideGraph::setTol(int n) //setTol
ui->widePlot->DrawOverlay();
ui->widePlot->update();
}
+
+void WideGraph::on_smoSpinBox_valueChanged(int n)
+{
+ m_nsmo=n;
+}
+
+int WideGraph::smoothYellow()
+{
+ return m_nsmo;
+}
diff --git a/widegraph.h b/widegraph.h
index b0f9ea533..3b8f99ac3 100644
--- a/widegraph.h
+++ b/widegraph.h
@@ -40,6 +40,8 @@ public:
void setLockTxFreq(bool b);
bool flatten();
void setTol(int n);
+ int smoothYellow();
+ void setRxBand(QString band);
signals:
void freezeDecode2(int n);
@@ -69,6 +71,7 @@ private slots:
void on_zeroSlider_valueChanged(int value);
void on_gain2dSlider_valueChanged(int value);
void on_zero2dSlider_valueChanged(int value);
+ void on_smoSpinBox_valueChanged(int n);
private:
void readPalette();
@@ -88,6 +91,7 @@ private:
qint32 m_fMin;
qint32 m_fMax;
qint32 m_nSubMode;
+ qint32 m_nsmo;
bool m_lockTxFreq;
bool m_bFlatten;
diff --git a/widegraph.ui b/widegraph.ui
index 323b3a372..315482204 100644
--- a/widegraph.ui
+++ b/widegraph.ui
@@ -95,59 +95,6 @@
- -
-
-
-
- 0
- 0
-
-
-
-
- 100
- 0
-
-
-
-
- 100
- 16777215
-
-
-
- Number of FFTs averaged (controls waterfall scrolling rate)
-
-
- N Avg
-
-
- 1
-
-
- 20
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 80
- 0
-
-
-
- Select waterfall palette
-
-
-
-
@@ -158,7 +105,7 @@
- 120
+ 100
0
@@ -191,6 +138,72 @@
+ -
+
+
+ Flatten
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 80
+ 0
+
+
+
+
+ 125
+ 16777215
+
+
+
+ Select waterfall palette
+
+
+
+ -
+
+
+
+ 80
+ 0
+
+
+
+
+ 150
+ 16777215
+
+
+
+ Spectrum gain
+
+
+ -50
+
+
+ 50
+
+
+ 0
+
+
+ Qt::Horizontal
+
+
+ QSlider::TicksAbove
+
+
+
-
@@ -201,7 +214,7 @@
- 120
+ 100
0
@@ -234,169 +247,30 @@
- -
-
-
-
- 0
- 0
-
+
-
+
+
+ false
- 100
+ 65
0
-
-
- 100
- 16777215
-
-
-
- Frequency (Hz) at left edge of waterfall
-
-
- Hz
-
-
- Start
-
-
- 5000
-
-
- 100
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
- Flatten
-
-
-
- -
-
-
-
-
-
- Palette
-
-
- Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
-
-
-
- -
-
-
- Adjust...
-
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
-
- 150
- 16777215
-
-
-
- Spectrum gain
-
-
- -50
-
-
- 50
-
-
- 0
-
-
- Qt::Horizontal
-
-
- QSlider::TicksAbove
-
-
-
- -
-
-
-
- 150
- 16777215
-
-
-
- Waterfall zero
-
-
- -50
-
-
- 50
-
-
- 0
-
-
- Qt::Horizontal
-
-
- QSlider::TicksAbove
+ Smoothing
-
+
+
+ 80
+ 0
+
+
150
@@ -423,8 +297,139 @@
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 90
+ 0
+
+
+
+
+ 100
+ 16777215
+
+
+
+ Frequency (Hz) at left edge of waterfall
+
+
+ Hz
+
+
+ Start
+
+
+ 5000
+
+
+ 100
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 90
+ 0
+
+
+
+
+ 100
+ 16777215
+
+
+
+ Number of FFTs averaged (controls waterfall scrolling rate)
+
+
+ N Avg
+
+
+ 1
+
+
+ 20
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
-
+
+
+ Palette
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+
+ -
+
+
+
+ 65
+ 16777215
+
+
+
+ Adjust...
+
+
+
+
+
-
+
+
+ 80
+ 0
+
+
150
@@ -448,6 +453,88 @@
+ -
+
+
+
+ 80
+ 0
+
+
+
+
+ 150
+ 16777215
+
+
+
+ Waterfall zero
+
+
+ -50
+
+
+ 50
+
+
+ 0
+
+
+ Qt::Horizontal
+
+
+ QSlider::TicksAbove
+
+
+
+ -
+
+
-
+
+
+ false
+
+
+
+ 65
+ 0
+
+
+
+
+ 50
+ 16777215
+
+
+
+ Smoothing of Linear Average spectrum
+
+
+ Qt::AlignCenter
+
+
+ 1
+
+
+ 7
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
diff --git a/wsjtx.pro b/wsjtx.pro
index 912a3465a..6cb7a73ae 100644
--- a/wsjtx.pro
+++ b/wsjtx.pro
@@ -83,14 +83,15 @@ SOURCES += \
soundin.cpp \
meterwidget.cpp \
signalmeter.cpp \
- WFPalette.cpp \
+ WFPalette.cpp \
plotter.cpp \
widegraph.cpp \
about.cpp \
mainwindow.cpp \
main.cpp \
- decodedtext.cpp \
- messageaveraging.cpp
+ decodedtext.cpp \
+ wsprnet.cpp \
+ messageaveraging.cpp
HEADERS += qt_helpers.hpp \
pimpl_h.hpp pimpl_impl.hpp \
@@ -102,7 +103,7 @@ HEADERS += qt_helpers.hpp \
FrequencyLineEdit.hpp AudioDevice.hpp Detector.hpp Modulator.hpp psk_reporter.h \
Transceiver.hpp TransceiverBase.hpp TransceiverFactory.hpp PollingTransceiver.hpp \
EmulateSplitTransceiver.hpp DXLabSuiteCommanderTransceiver.hpp HamlibTransceiver.hpp \
- Configuration.hpp \
+ Configuration.hpp wsprnet.h \
signalmeter.h \
meterwidget.h \
logbook/logbook.h \
diff --git a/wsprnet.cpp b/wsprnet.cpp
new file mode 100644
index 000000000..b6004684f
--- /dev/null
+++ b/wsprnet.cpp
@@ -0,0 +1,192 @@
+// Interface to WSPRnet website
+//
+// by Edson Pereira - PY2SDR
+
+#include "wsprnet.h"
+
+WSPRNet::WSPRNet(QObject *parent) :
+ QObject(parent)
+{
+ wsprNetUrl = "http://wsprnet.org/post?";
+ //wsprNetUrl = "http://127.0.0.1/post.php?";
+ networkManager = new QNetworkAccessManager(this);
+ connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkReply(QNetworkReply*)));
+
+ uploadTimer = new QTimer(this);
+ connect( uploadTimer, SIGNAL(timeout()), this, SLOT(work()));
+}
+
+void WSPRNet::upload(QString call, QString grid, QString rfreq, QString tfreq,
+ QString mode, QString tpct, QString dbm, QString version,
+ QString fileName)
+{
+ m_call = call;
+ m_grid = grid;
+ m_rfreq = rfreq;
+ m_tfreq = tfreq;
+ m_mode = mode;
+ m_tpct = tpct;
+ m_dbm = dbm;
+ m_vers = version;
+ m_file = fileName;
+
+ // Open the wsprd.out file
+ QFile wsprdOutFile(fileName);
+ if (!wsprdOutFile.open(QIODevice::ReadOnly | QIODevice::Text) ||
+ wsprdOutFile.size() == 0) {
+ urlQueue.enqueue( wsprNetUrl + urlEncodeNoSpot());
+ m_uploadType = 1;
+ uploadTimer->start(200);
+ return;
+ }
+
+ // Read the contents
+ while (!wsprdOutFile.atEnd()) {
+ QHash query;
+ if ( decodeLine(wsprdOutFile.readLine(), query) ) {
+ // Prevent reporting data ouside of the current frequency band
+ float f = fabs(m_rfreq.toFloat() - query["tqrg"].toFloat());
+ if (f < 0.0002) {
+ urlQueue.enqueue( wsprNetUrl + urlEncodeSpot(query));
+ m_uploadType = 2;
+ }
+ }
+ }
+ m_urlQueueSize = urlQueue.size();
+ uploadTimer->start(200);
+}
+
+void WSPRNet::networkReply(QNetworkReply *reply)
+{
+ QString serverResponse = reply->readAll();
+ if( m_uploadType == 2) {
+ if (!serverResponse.contains(QRegExp("spot\\(s\\) added"))) {
+ emit uploadStatus("Upload Failed");
+ urlQueue.clear();
+ uploadTimer->stop();
+ }
+ }
+
+ if (urlQueue.isEmpty()) {
+ emit uploadStatus("done");
+ QFile::remove(m_file);
+ uploadTimer->stop();
+ }
+}
+
+bool WSPRNet::decodeLine(QString line, QHash &query)
+{
+ // 130223 2256 7 -21 -0.3 14.097090 DU1MGA PK04 37 0 40 0
+ // Date Time Sync dBm DT Freq Msg
+ // 1 2 3 4 5 6 -------7------ 8 9 10
+ QRegExp rx("^(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+([+-]?\\d+)\\s+([+-]?\\d+\\.\\d+)\\s+(\\d+\\.\\d+)\\s+(.*)\\s+([+-]?\\d+)\\s+([+-]?\\d+)\\s+([+-]?\\d+)");
+ if (rx.indexIn(line) != -1) {
+ int msgType = 0;
+ QString msg = rx.cap(7);
+ msg.remove(QRegExp("\\s+$"));
+ msg.remove(QRegExp("^\\s+"));
+ QString call, grid, dbm;
+ QRegExp msgRx;
+
+ // Check for Message Type 1
+ msgRx.setPattern("^([A-Z0-9]{3,6})\\s+([A-Z]{2}\\d{2})\\s+(\\d+)");
+ if (msgRx.indexIn(msg) != -1) {
+ msgType = 1;
+ call = msgRx.cap(1);
+ grid = msgRx.cap(2);
+ dbm = msgRx.cap(3);
+ }
+
+ // Check for Message Type 2
+ msgRx.setPattern("^([A-Z0-9/]+)\\s+(\\d+)");
+ if (msgRx.indexIn(msg) != -1) {
+ msgType = 2;
+ call = msgRx.cap(1);
+ grid = "";
+ dbm = msgRx.cap(2);
+ }
+
+ // Check for Message Type 3
+ msgRx.setPattern("^<([A-Z0-9/]+)>\\s+([A-Z]{2}\\d{2}[A-Z]{2})\\s+(\\d+)");
+ if (msgRx.indexIn(msg) != -1) {
+ msgType = 3;
+ call = msgRx.cap(1);
+ grid = msgRx.cap(2);
+ dbm = msgRx.cap(3);
+ }
+
+ // Unknown message format
+ if (!msgType) {
+ return false;
+ }
+
+ query["function"] = "wspr";
+ query["date"] = rx.cap(1);
+ query["time"] = rx.cap(2);
+ query["sig"] = rx.cap(4);
+ query["dt"] = rx.cap(5);
+ query["drift"] = rx.cap(8);
+ query["tqrg"] = rx.cap(6);
+ query["tcall"] = call;
+ query["tgrid"] = grid;
+ query["dbm"] = dbm;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+QString WSPRNet::urlEncodeNoSpot()
+{
+ QString queryString;
+ queryString += "function=wsprstat&";
+ queryString += "rcall=" + m_call + "&";
+ queryString += "rgrid=" + m_grid + "&";
+ queryString += "rqrg=" + m_rfreq + "&";
+ queryString += "tpct=" + m_tpct + "&";
+ queryString += "tqrg=" + m_tfreq + "&";
+ queryString += "dbm=" + m_dbm + "&";
+ queryString += "version=" + m_vers;
+ if(m_mode=="WSPR-2") queryString += "&mode=2";
+ if(m_mode=="WSPR-15") queryString += "&mode=15";
+ return queryString;;
+}
+
+QString WSPRNet::urlEncodeSpot(QHash query)
+{
+ QString queryString;
+ queryString += "function=" + query["function"] + "&";
+ queryString += "rcall=" + m_call + "&";
+ queryString += "rgrid=" + m_grid + "&";
+ queryString += "rqrg=" + m_rfreq + "&";
+ queryString += "date=" + query["date"] + "&";
+ queryString += "time=" + query["time"] + "&";
+ queryString += "sig=" + query["sig"] + "&";
+ queryString += "dt=" + query["dt"] + "&";
+ queryString += "drift=" + query["drift"] + "&";
+ queryString += "tqrg=" + query["tqrg"] + "&";
+ queryString += "tcall=" + query["tcall"] + "&";
+ queryString += "tgrid=" + query["tgrid"] + "&";
+ queryString += "dbm=" + query["dbm"] + "&";
+ queryString += "version=" + m_vers;
+ if(m_mode=="WSPR-2") queryString += "&mode=2";
+ if(m_mode=="WSPR-15") queryString += "&mode=15";
+ return queryString;
+}
+
+void WSPRNet::work()
+{
+ if (!urlQueue.isEmpty()) {
+ QUrl url(urlQueue.dequeue());
+ QNetworkRequest request(url);
+ networkManager->get(request);
+ QString status = "Uploading Spot " + QString::number(m_urlQueueSize - urlQueue.size()) +
+ "/"+ QString::number(m_urlQueueSize);
+ emit uploadStatus(status);
+ } else {
+ uploadTimer->stop();
+ }
+}
+
+
+
diff --git a/wsprnet.h b/wsprnet.h
new file mode 100644
index 000000000..1c9379d73
--- /dev/null
+++ b/wsprnet.h
@@ -0,0 +1,37 @@
+#ifndef WSPRNET_H
+#define WSPRNET_H
+
+#include
+#include
+
+class WSPRNet : public QObject
+{
+Q_OBJECT
+public:
+ explicit WSPRNet(QObject *parent = 0);
+ void upload(QString call, QString grid, QString rfreq, QString tfreq,
+ QString mode, QString tpct, QString dbm, QString version,
+ QString fileName);
+ static bool decodeLine(QString line, QHash &query);
+
+signals:
+ void uploadStatus(QString);
+
+public slots:
+ void networkReply(QNetworkReply *);
+ void work();
+
+private:
+ QNetworkAccessManager *networkManager;
+ QString wsprNetUrl;
+ QString m_call, m_grid, m_rfreq, m_tfreq, m_mode, m_tpct, m_dbm, m_vers, m_file;
+ QQueue urlQueue;
+ QTimer *uploadTimer;
+ int m_urlQueueSize;
+ int m_uploadType;
+
+ QString urlEncodeNoSpot();
+ QString urlEncodeSpot(QHash spot);
+};
+
+#endif // WSPRNET_H