Merged master 8748

This commit is contained in:
Jordan Sherer
2018-08-05 11:33:30 -04:00
parent 8f8772f1bd
commit 62899069bf
1095 changed files with 31298 additions and 367679 deletions
@@ -1,360 +0,0 @@
#include <iostream>
#include <exception>
#include <stdexcept>
#include <string>
#include <locale.h>
#include <fftw3.h>
#include <QDateTime>
#include <QApplication>
#include <QRegularExpression>
#include <QObject>
#include <QSettings>
#include <QLibraryInfo>
#include <QSysInfo>
#include <QDir>
#include <QStandardPaths>
#include <QStringList>
#include <QLockFile>
#include <QStack>
#include <QSplashScreen>
#if QT_VERSION >= 0x050200
#include <QCommandLineParser>
#include <QCommandLineOption>
#endif
#include "revision_utils.hpp"
#include "MetaDataRegistry.hpp"
#include "SettingsGroup.hpp"
#include "TraceFile.hpp"
#include "MultiSettings.hpp"
#include "mainwindow.h"
#include "commons.h"
#include "lib/init_random_seed.h"
#include "Radio.hpp"
#include "FrequencyList.hpp"
#include "SplashScreen.hpp"
#include "MessageBox.hpp" // last to avoid nasty MS macro definitions
extern "C" {
// Fortran procedures we need
void four2a_(_Complex float *, int * nfft, int * ndim, int * isign, int * iform, int len);
}
namespace
{
struct RNGSetup
{
RNGSetup ()
{
// one time seed of pseudo RNGs from current time
auto seed = QDateTime::currentMSecsSinceEpoch ();
qsrand (seed); // this is good for rand() as well
}
} seeding;
class MessageTimestamper
{
public:
MessageTimestamper ()
{
prior_handlers_.push (qInstallMessageHandler (message_handler));
}
~MessageTimestamper ()
{
if (prior_handlers_.size ()) qInstallMessageHandler (prior_handlers_.pop ());
}
private:
static void message_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg)
{
QtMessageHandler handler {prior_handlers_.top ()};
if (handler)
{
handler (type, context,
QDateTime::currentDateTimeUtc ().toString ("yy-MM-ddTHH:mm:ss.zzzZ: ") + msg);
}
}
static QStack<QtMessageHandler> prior_handlers_;
};
QStack<QtMessageHandler> MessageTimestamper::prior_handlers_;
}
int main(int argc, char *argv[])
{
// Add timestamps to all debug messages
MessageTimestamper message_timestamper;
init_random_seed ();
// make the Qt type magic happen
Radio::register_types ();
register_types ();
// Multiple instances communicate with jt9 via this
QSharedMemory mem_jt9;
QApplication a(argc, argv);
try
{
setlocale (LC_NUMERIC, "C"); // ensure number forms are in
// consistent format, do this after
// instantiating QApplication so
// that GUI has correct l18n
// Override programs executable basename as application name.
a.setApplicationName ("WSJT-X");
a.setApplicationVersion (version ());
#if QT_VERSION >= 0x050200
QCommandLineParser parser;
parser.setApplicationDescription ("\nJT65, JT9, JT4, MSK144, QRA64, ISCAT & WSPR Weak Signal Communications Program.");
auto help_option = parser.addHelpOption ();
auto version_option = parser.addVersionOption ();
// support for multiple instances running from a single installation
QCommandLineOption rig_option (QStringList {} << "r" << "rig-name"
, a.translate ("main", "Where <rig-name> is for multi-instance support.")
, a.translate ("main", "rig-name"));
parser.addOption (rig_option);
// support for start up configuration
QCommandLineOption cfg_option (QStringList {} << "c" << "config"
, a.translate ("main", "Where <configuration> is an existing one.")
, a.translate ("main", "configuration"));
parser.addOption (cfg_option);
QCommandLineOption test_option (QStringList {} << "test-mode"
, a.translate ("main", "Writable files in test location. Use with caution, for testing only."));
parser.addOption (test_option);
if (!parser.parse (a.arguments ()))
{
MessageBox::critical_message (nullptr, a.translate ("main", "Command line error"), parser.errorText ());
return -1;
}
else
{
if (parser.isSet (help_option))
{
MessageBox::information_message (nullptr, a.translate ("main", "Command line help"), parser.helpText ());
return 0;
}
else if (parser.isSet (version_option))
{
MessageBox::information_message (nullptr, a.translate ("main", "Application version"), a.applicationVersion ());
return 0;
}
}
QStandardPaths::setTestModeEnabled (parser.isSet (test_option));
// support for multiple instances running from a single installation
bool multiple {false};
if (parser.isSet (rig_option) || parser.isSet (test_option))
{
auto temp_name = parser.value (rig_option);
if (!temp_name.isEmpty ())
{
if (temp_name.contains (QRegularExpression {R"([\\/,])"}))
{
std::cerr << QObject::tr ("Invalid rig name - \\ & / not allowed").toLocal8Bit ().data () << std::endl;
parser.showHelp (-1);
}
a.setApplicationName (a.applicationName () + " - " + temp_name);
}
if (parser.isSet (test_option))
{
a.setApplicationName (a.applicationName () + " - test");
}
multiple = true;
}
// now we have the application name we can open the settings
MultiSettings multi_settings {parser.value (cfg_option)};
// find the temporary files path
QDir temp_dir {QStandardPaths::writableLocation (QStandardPaths::TempLocation)};
Q_ASSERT (temp_dir.exists ()); // sanity check
// disallow multiple instances with same instance key
QLockFile instance_lock {temp_dir.absoluteFilePath (a.applicationName () + ".lock")};
instance_lock.setStaleLockTime (0);
bool lock_ok {false};
while (!(lock_ok = instance_lock.tryLock ()))
{
if (QLockFile::LockFailedError == instance_lock.error ())
{
auto button = MessageBox::query_message (nullptr
, a.translate ("main", "Another instance may be running")
, a.translate ("main", "try to remove stale lock file?")
, QString {}
, MessageBox::Yes | MessageBox::Retry | MessageBox::No
, MessageBox::Yes);
switch (button)
{
case MessageBox::Yes:
instance_lock.removeStaleLockFile ();
break;
case MessageBox::Retry:
break;
default:
throw std::runtime_error {"Multiple instances must have unique rig names"};
}
}
}
#endif
#if WSJT_QDEBUG_TO_FILE
// Open a trace file
TraceFile trace_file {temp_dir.absoluteFilePath (a.applicationName () + "_trace.log")};
qDebug () << program_title (revision ()) + " - Program startup";
#endif
// Create a unique writeable temporary directory in a suitable location
bool temp_ok {false};
QString unique_directory {QApplication::applicationName ()};
do
{
if (!temp_dir.mkpath (unique_directory)
|| !temp_dir.cd (unique_directory))
{
MessageBox::critical_message (nullptr,
a.translate ("main", "Failed to create a temporary directory"),
a.translate ("main", "Path: \"%1\"").arg (temp_dir.absolutePath ()));
throw std::runtime_error {"Failed to create a temporary directory"};
}
if (!temp_dir.isReadable () || !(temp_ok = QTemporaryFile {temp_dir.absoluteFilePath ("test")}.open ()))
{
auto button = MessageBox::critical_message (nullptr,
a.translate ("main", "Failed to create a usable temporary directory"),
a.translate ("main", "Another application may be locking the directory"),
a.translate ("main", "Path: \"%1\"").arg (temp_dir.absolutePath ()),
MessageBox::Retry | MessageBox::Cancel);
if (MessageBox::Cancel == button)
{
throw std::runtime_error {"Failed to create a usable temporary directory"};
}
temp_dir.cdUp (); // revert to parent as this one is no good
}
}
while (!temp_ok);
SplashScreen splash;
{
// change this key if you want to force a new splash screen
// for a new version, the user will be able to re-disable it
// if they wish
QString splash_flag_name {"Splash_v1.7"};
if (multi_settings.common_value (splash_flag_name, true).toBool ())
{
QObject::connect (&splash, &SplashScreen::disabled, [&, splash_flag_name] {
multi_settings.set_common_value (splash_flag_name, false);
splash.close ();
});
splash.show ();
a.processEvents ();
}
}
int result;
do
{
#if WSJT_QDEBUG_TO_FILE
// announce to trace file and dump settings
qDebug () << "++++++++++++++++++++++++++++ Settings ++++++++++++++++++++++++++++";
for (auto const& key: multi_settings.settings ()->allKeys ())
{
auto const& value = multi_settings.settings ()->value (key);
if (value.canConvert<QVariantList> ())
{
auto const sequence = value.value<QSequentialIterable> ();
qDebug ().nospace () << key << ": ";
for (auto const& item: sequence)
{
qDebug ().nospace () << '\t' << item;
}
}
else
{
qDebug ().nospace () << key << ": " << value;
}
}
qDebug () << "---------------------------- Settings ----------------------------";
#endif
// Create and initialize shared memory segment
// Multiple instances: use rig_name as shared memory key
mem_jt9.setKey(a.applicationName ());
if(!mem_jt9.attach()) {
if (!mem_jt9.create(sizeof(struct dec_data))) {
splash.hide ();
MessageBox::critical_message (nullptr, a.translate ("main", "Shared memory error"),
a.translate ("main", "Unable to create shared memory segment"));
throw std::runtime_error {"Shared memory error"};
}
}
memset(mem_jt9.data(),0,sizeof(struct dec_data)); //Zero all decoding params in shared memory
unsigned downSampleFactor;
{
SettingsGroup {multi_settings.settings (), "Tune"};
// deal with Windows Vista and earlier input audio rate
// converter problems
downSampleFactor = multi_settings.settings ()->value ("Audio/DisableInputResampling",
#if defined (Q_OS_WIN)
// default to true for
// Windows Vista and older
QSysInfo::WV_VISTA >= QSysInfo::WindowsVersion ? true : false
#else
false
#endif
).toBool () ? 1u : 4u;
}
// run the application UI
MainWindow w(temp_dir, multiple, &multi_settings, &mem_jt9, downSampleFactor, &splash);
w.show();
splash.raise ();
QObject::connect (&a, SIGNAL (lastWindowClosed()), &a, SLOT (quit()));
result = a.exec();
}
while (!result && !multi_settings.exit ());
// clean up lazily initialized resources
{
int nfft {-1};
int ndim {1};
int isign {1};
int iform {1};
// free FFT plan resources
four2a_ (nullptr, &nfft, &ndim, &isign, &iform, 0);
}
fftwf_forget_wisdom ();
fftwf_cleanup ();
temp_dir.removeRecursively (); // clean up temp files
return result;
}
catch (std::exception const& e)
{
MessageBox::critical_message (nullptr, a.translate ("main", "Fatal error"), e.what ());
std::cerr << "Error: " << e.what () << '\n';
}
catch (...)
{
MessageBox::critical_message (nullptr, a.translate ("main", "Unexpected fatal error"));
std::cerr << "Unexpected fatal error\n";
throw; // hoping the runtime might tell us more about the exception
}
return -1;
}
@@ -1,148 +0,0 @@
subroutine sync8(dd,nfa,nfb,syncmin,nfqso,s,candidate,ncand)
include 'ft8_params.f90'
! Search over +/- 1.5s relative to 0.5s TX start time.
parameter (JZ=38)
complex cx(0:NH1)
real s(NH1,NHSYM)
real savg(NH1)
real x(NFFT1)
real sync2d(NH1,-JZ:JZ)
real red(NH1)
real candidate0(3,200)
real candidate(3,200)
real dd(NMAX)
integer jpeak(NH1)
integer indx(NH1)
integer ii(1)
integer icos7(0:6)
data icos7/2,5,6,0,4,1,3/ !Costas 7x7 tone pattern
equivalence (x,cx)
! Compute symbol spectra, stepping by NSTEP steps.
savg=0.
tstep=NSTEP/12000.0
df=12000.0/NFFT1 !3.125 Hz
fac=1.0/300.0
do j=1,NHSYM
ia=(j-1)*NSTEP + 1
ib=ia+NSPS-1
x(1:NSPS)=fac*dd(ia:ib)
x(NSPS+1:)=0.
call four2a(x,NFFT1,1,-1,0) !r2c FFT
do i=1,NH1
s(i,j)=real(cx(i))**2 + aimag(cx(i))**2
enddo
savg=savg + s(1:NH1,j) !Average spectrum
enddo
savg=savg/NHSYM
! do i=1,NH1
! write(51,3051) i*df,savg(i),db(savg(i))
!3051 format(f10.3,e12.3,f12.3)
! enddo
ia=max(1,nint(nfa/df))
ib=nint(nfb/df)
nssy=NSPS/NSTEP ! # steps per symbol
nfos=NFFT1/NSPS ! # frequency bin oversampling factor
jstrt=0.5/tstep
do i=ia,ib
do j=-JZ,+JZ
ta=0.
tb=0.
tc=0.
t0a=0.
t0b=0.
t0c=0.
do n=0,6
k=j+jstrt+nssy*n
if(k.ge.1.and.k.le.NHSYM) then
ta=ta + s(i+nfos*icos7(n),k)
t0a=t0a + sum(s(i:i+nfos*6:nfos,k))
endif
tb=tb + s(i+nfos*icos7(n),k+nssy*36)
t0b=t0b + sum(s(i:i+nfos*6:nfos,k+nssy*36))
if(k+nssy*72.le.NHSYM) then
tc=tc + s(i+nfos*icos7(n),k+nssy*72)
t0c=t0c + sum(s(i:i+nfos*6:nfos,k+nssy*72))
endif
enddo
t=ta+tb+tc
t0=t0a+t0b+t0c
t0=(t0-t)/6.0
sync_abc=t/t0
t=tb+tc
t0=t0b+t0c
t0=(t0-t)/6.0
sync_bc=t/t0
sync2d(i,j)=max(sync_abc,sync_bc)
enddo
enddo
red=0.
do i=ia,ib
ii=maxloc(sync2d(i,-JZ:JZ)) - 1 - JZ
j0=ii(1)
jpeak(i)=j0
red(i)=sync2d(i,j0)
! write(52,3052) i*df,red(i),db(red(i))
!3052 format(3f12.3)
enddo
iz=ib-ia+1
call indexx(red(ia:ib),iz,indx)
ibase=indx(nint(0.40*iz)) - 1 + ia
base=red(ibase)
red=red/base
candidate0=0.
k=0
do i=1,200
n=ia + indx(iz+1-i) - 1
if(red(n).lt.syncmin) exit
if(k.lt.200) k=k+1
candidate0(1,k)=n*df
candidate0(2,k)=(jpeak(n)-1)*tstep
candidate0(3,k)=red(n)
enddo
ncand=k
! Put nfqso at top of list, and save only the best of near-dupe freqs.
do i=1,ncand
if(abs(candidate0(1,i)-nfqso).lt.10.0) candidate0(1,i)=-candidate0(1,i)
if(i.ge.2) then
do j=1,i-1
fdiff=abs(candidate0(1,i))-abs(candidate0(1,j))
if(abs(fdiff).lt.4.0) then
if(candidate0(3,i).ge.candidate0(3,j)) candidate0(3,j)=0.
if(candidate0(3,i).lt.candidate0(3,j)) candidate0(3,i)=0.
endif
enddo
! write(*,3001) i,candidate0(1,i-1),candidate0(1,i),candidate0(3,i-1), &
! candidate0(3,i)
!3001 format(i2,4f8.1)
endif
enddo
fac=20.0/maxval(s)
s=fac*s
! Sort by sync
! call indexx(candidate0(3,1:ncand),ncand,indx)
! Sort by frequency
call indexx(candidate0(1,1:ncand),ncand,indx)
k=1
! do i=ncand,1,-1
do i=1,ncand
j=indx(i)
if( candidate0(3,j) .ge. syncmin .and. candidate0(2,j).ge.-1.5 ) then
candidate(1,k)=abs(candidate0(1,j))
candidate(2,k)=candidate0(2,j)
candidate(3,k)=candidate0(3,j)
k=k+1
endif
enddo
ncand=k-1
return
end subroutine sync8
@@ -1,41 +0,0 @@
// Status=review
image::settings-audio.png[align="center",alt="WSJT-X Audio Configuration Screen"]
Select the *Audio* tab to configure your sound system.
- _Soundcard_: Select the audio devices to be used for *Input* and
*Output*. Usually the *Mono* settings will suffice, but in special
cases you can choose *Left*, *Right*, or *Both* stereo channels.
- Be sure that your audio device is configured to sample at 48000 Hz,
16 bits.
IMPORTANT: If you select the audio output device that is also your
computer's default audio device, be sure to turn off all system sounds
to prevent inadvertently transmitting them over the air.
NOTE: Windows Vista and later may configure audio devices using
the Texas Instruments PCM2900 series CODEC for microphone input rather
line input. (This chip is used in many radios with built-in USB
CODECs, as well as various other audio interfaces.) If you are using
such a device, be sure to set the mic level in the Recording Device
Properties to 0 dB.
- _Save Directory_: _WSJT-X_ can save its received audio sequences as
`.wav` files. A default directory for these files is provided; you
can select another location if desired.
- _AzEl Directory_: A file named `azel.dat` will appear in the
specified directory. The file contains information usable by another
program for automatic tracking of the Sun or Moon, as well as
calculated Doppler shift for the specified EME path. The file is
updated once per second whenever the <<ASTRODATA,Astronomical Data>>
window is displayed.
- _Remember power settings by band_: Checking either of these will
cause _WSJT-X_ to remember the *Pwr* slider setting for that operation
on a band-by-band basis. For example, when *Tune* is checked here and
you click the *Tune* button on the main window, the power slider will
change to the most recent setting used for *Tune* on the band in use.
@@ -0,0 +1,77 @@
////
Questions:
Should be short one liners (in the .adoc file) ending with ?::
If your question is too long for one line, consider multiple questions or rephrase
Answers:
Can be bullet or paragraphs. Bullets make for easier reading.
Bullet Usage:
* = a circle bullet single intent
** = circle bullet double indent
. = should be avoided as the questions are numbered
.. = bullet a, b, c, and so on, double indent
Alternatives: Use a * Bullet, followed by .. for example, then have
a multi-section answer using the * as the section header
* Section Header 1
.. Possible Answer a
.. Possible Answer b
* Section Header 2
.. Possible Answer a
.. Possible Answer b
Link Usage:
Use the common/links.adoc for href links to maintain consistency. Try to avoid
apostrophes ` or ' as it breaks AsciiDoc syntax without special escaping
and they do not translate into other languages well.
////
[qanda]
My displayed spectrum is flatter when I do not check the *Flatten* box. What's wrong?::
_WSJT-X_ does not expect a steep filter edge within the displayed
passband. Use a wider IF filter or reduce the displayed passband by
decreasing *Bins/Pixel*, increasing *Start*, or reducing the width of
the *Wide Graph*. You might also choose to re-center the filter
passband, if such control is available.
How should I configure _WSJT-X_ to run multiple instances?::
Start _WSJT-X_ from a command-prompt window, assigning each instance a
unique identifier as in the following two-instance example. This
procedure will isolate the *Settings* file and the writable file
location for each instance of _WSJT-X_.
wsjtx --rig-name=TS2000
wsjtx --rig-name=FT847
Rig control through _OmniRig_ seems to fail when I click *Test CAT*. What can I do about it?::
_Omni-Rig_ apparently has a bug that appears when you click *Test
CAT*. Forget using *Test CAT* and just click *OK*. _Omni-Rig_ then
behaves normally.
I am using _WSJT-X_ with _Ham Radio Deluxe_. All seems well until I start HRD Logbook or DM780 running in parallel; then CAT control becomes unreliable.::
You may see delays up to 20 seconds or so in frequency changes or
other radio commands, due to a bug in HRD. HRD folks are aware of the
problem, and are working to resolve it.
I am running _WSJT-X_ under Ubuntu. The program starts, but menu bar is missing from the top of the main window and the hot-keys don't work.::
Ubuntu's new "`Unity`" desktop puts the menu for the currently active
window at the top of the primary display screen. You can restore menu
bars to their traditional locations by typing the following in a
command-prompt window:
sudo apt remove appmenu-qt5
+
Alternatively, you can disable the common menu bar for just _WSJT-X_
by starting the application with the environment variable
QT_QPA_PLATFORMTHEME set to empty (the space after the '=' character
is necessary):
QT_QPA_PLATFORMTHEME= wsjtx
@@ -1,401 +0,0 @@
subroutine bpdecode174(llr,apmask,maxiterations,decoded,cw,nharderror)
!
! A log-domain belief propagation decoder for the (174,87) code.
!
integer, parameter:: N=174, K=87, M=N-K
integer*1 codeword(N),cw(N),apmask(N)
integer colorder(N)
integer*1 decoded(K)
integer Nm(7,M) ! 5, 6, or 7 bits per check
integer Mn(3,N) ! 3 checks per bit
integer synd(M)
real tov(3,N)
real toc(7,M)
real tanhtoc(7,M)
real zn(N)
real llr(N)
real Tmn
integer nrw(M)
data colorder/ &
0, 1, 2, 3, 30, 4, 5, 6, 7, 8, 9, 10, 11, 32, 12, 40, 13, 14, 15, 16,&
17, 18, 37, 45, 29, 19, 20, 21, 41, 22, 42, 31, 33, 34, 44, 35, 47, 51, 50, 43,&
36, 52, 63, 46, 25, 55, 27, 24, 23, 53, 39, 49, 59, 38, 48, 61, 60, 57, 28, 62,&
56, 58, 65, 66, 26, 70, 64, 69, 68, 67, 74, 71, 54, 76, 72, 75, 78, 77, 80, 79,&
73, 83, 84, 81, 82, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,&
100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,&
120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,&
140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,&
160,161,162,163,164,165,166,167,168,169,170,171,172,173/
data Mn/ &
1, 25, 69, &
2, 5, 73, &
3, 32, 68, &
4, 51, 61, &
6, 63, 70, &
7, 33, 79, &
8, 50, 86, &
9, 37, 43, &
10, 41, 65, &
11, 14, 64, &
12, 75, 77, &
13, 23, 81, &
15, 16, 82, &
17, 56, 66, &
18, 53, 60, &
19, 31, 52, &
20, 67, 84, &
21, 29, 72, &
22, 24, 44, &
26, 35, 76, &
27, 36, 38, &
28, 40, 42, &
30, 54, 55, &
34, 49, 87, &
39, 57, 58, &
45, 74, 83, &
46, 62, 80, &
47, 48, 85, &
59, 71, 78, &
1, 50, 53, &
2, 47, 84, &
3, 25, 79, &
4, 6, 14, &
5, 7, 80, &
8, 34, 55, &
9, 36, 69, &
10, 43, 83, &
11, 23, 74, &
12, 17, 44, &
13, 57, 76, &
15, 27, 56, &
16, 28, 29, &
18, 19, 59, &
20, 40, 63, &
21, 35, 52, &
22, 54, 64, &
24, 62, 78, &
26, 32, 77, &
30, 72, 85, &
31, 65, 87, &
33, 39, 51, &
37, 48, 75, &
38, 70, 71, &
41, 42, 68, &
45, 67, 86, &
46, 81, 82, &
49, 66, 73, &
58, 60, 66, &
61, 65, 85, &
1, 14, 21, &
2, 13, 59, &
3, 67, 82, &
4, 32, 73, &
5, 36, 54, &
6, 43, 46, &
7, 28, 75, &
8, 33, 71, &
9, 49, 76, &
10, 58, 64, &
11, 48, 68, &
12, 19, 45, &
15, 50, 61, &
16, 22, 26, &
17, 72, 80, &
18, 40, 55, &
20, 35, 51, &
23, 25, 34, &
24, 63, 87, &
27, 39, 74, &
29, 78, 83, &
30, 70, 77, &
31, 69, 84, &
22, 37, 86, &
38, 41, 81, &
42, 44, 57, &
47, 53, 62, &
52, 56, 79, &
60, 75, 81, &
1, 39, 77, &
2, 16, 41, &
3, 31, 54, &
4, 36, 78, &
5, 45, 65, &
6, 57, 85, &
7, 14, 49, &
8, 21, 46, &
9, 15, 72, &
10, 20, 62, &
11, 17, 71, &
12, 34, 47, &
13, 68, 86, &
18, 23, 43, &
19, 64, 73, &
24, 48, 79, &
25, 70, 83, &
26, 80, 87, &
27, 32, 40, &
28, 56, 69, &
29, 63, 66, &
30, 42, 50, &
33, 37, 82, &
35, 60, 74, &
38, 55, 84, &
44, 52, 61, &
51, 53, 72, &
58, 59, 67, &
47, 56, 76, &
1, 19, 37, &
2, 61, 75, &
3, 8, 66, &
4, 60, 84, &
5, 34, 39, &
6, 26, 53, &
7, 32, 57, &
9, 52, 67, &
10, 12, 15, &
11, 51, 69, &
13, 14, 65, &
16, 31, 43, &
17, 20, 36, &
18, 80, 86, &
21, 48, 59, &
22, 40, 46, &
23, 33, 62, &
24, 30, 74, &
25, 42, 64, &
27, 49, 85, &
28, 38, 73, &
29, 44, 81, &
35, 68, 70, &
41, 63, 76, &
45, 49, 71, &
50, 58, 87, &
48, 54, 83, &
13, 55, 79, &
77, 78, 82, &
1, 2, 24, &
3, 6, 75, &
4, 56, 87, &
5, 44, 53, &
7, 50, 83, &
8, 10, 28, &
9, 55, 62, &
11, 29, 67, &
12, 33, 40, &
14, 16, 20, &
15, 35, 73, &
17, 31, 39, &
18, 36, 57, &
19, 46, 76, &
21, 42, 84, &
22, 34, 59, &
23, 26, 61, &
25, 60, 65, &
27, 64, 80, &
30, 37, 66, &
32, 45, 72, &
38, 51, 86, &
41, 77, 79, &
43, 56, 68, &
47, 74, 82, &
40, 52, 78, &
54, 61, 71, &
46, 58, 69/
data Nm/ &
1, 30, 60, 89, 118, 147, 0, &
2, 31, 61, 90, 119, 147, 0, &
3, 32, 62, 91, 120, 148, 0, &
4, 33, 63, 92, 121, 149, 0, &
2, 34, 64, 93, 122, 150, 0, &
5, 33, 65, 94, 123, 148, 0, &
6, 34, 66, 95, 124, 151, 0, &
7, 35, 67, 96, 120, 152, 0, &
8, 36, 68, 97, 125, 153, 0, &
9, 37, 69, 98, 126, 152, 0, &
10, 38, 70, 99, 127, 154, 0, &
11, 39, 71, 100, 126, 155, 0, &
12, 40, 61, 101, 128, 145, 0, &
10, 33, 60, 95, 128, 156, 0, &
13, 41, 72, 97, 126, 157, 0, &
13, 42, 73, 90, 129, 156, 0, &
14, 39, 74, 99, 130, 158, 0, &
15, 43, 75, 102, 131, 159, 0, &
16, 43, 71, 103, 118, 160, 0, &
17, 44, 76, 98, 130, 156, 0, &
18, 45, 60, 96, 132, 161, 0, &
19, 46, 73, 83, 133, 162, 0, &
12, 38, 77, 102, 134, 163, 0, &
19, 47, 78, 104, 135, 147, 0, &
1, 32, 77, 105, 136, 164, 0, &
20, 48, 73, 106, 123, 163, 0, &
21, 41, 79, 107, 137, 165, 0, &
22, 42, 66, 108, 138, 152, 0, &
18, 42, 80, 109, 139, 154, 0, &
23, 49, 81, 110, 135, 166, 0, &
16, 50, 82, 91, 129, 158, 0, &
3, 48, 63, 107, 124, 167, 0, &
6, 51, 67, 111, 134, 155, 0, &
24, 35, 77, 100, 122, 162, 0, &
20, 45, 76, 112, 140, 157, 0, &
21, 36, 64, 92, 130, 159, 0, &
8, 52, 83, 111, 118, 166, 0, &
21, 53, 84, 113, 138, 168, 0, &
25, 51, 79, 89, 122, 158, 0, &
22, 44, 75, 107, 133, 155, 172, &
9, 54, 84, 90, 141, 169, 0, &
22, 54, 85, 110, 136, 161, 0, &
8, 37, 65, 102, 129, 170, 0, &
19, 39, 85, 114, 139, 150, 0, &
26, 55, 71, 93, 142, 167, 0, &
27, 56, 65, 96, 133, 160, 174, &
28, 31, 86, 100, 117, 171, 0, &
28, 52, 70, 104, 132, 144, 0, &
24, 57, 68, 95, 137, 142, 0, &
7, 30, 72, 110, 143, 151, 0, &
4, 51, 76, 115, 127, 168, 0, &
16, 45, 87, 114, 125, 172, 0, &
15, 30, 86, 115, 123, 150, 0, &
23, 46, 64, 91, 144, 173, 0, &
23, 35, 75, 113, 145, 153, 0, &
14, 41, 87, 108, 117, 149, 170, &
25, 40, 85, 94, 124, 159, 0, &
25, 58, 69, 116, 143, 174, 0, &
29, 43, 61, 116, 132, 162, 0, &
15, 58, 88, 112, 121, 164, 0, &
4, 59, 72, 114, 119, 163, 173, &
27, 47, 86, 98, 134, 153, 0, &
5, 44, 78, 109, 141, 0, 0, &
10, 46, 69, 103, 136, 165, 0, &
9, 50, 59, 93, 128, 164, 0, &
14, 57, 58, 109, 120, 166, 0, &
17, 55, 62, 116, 125, 154, 0, &
3, 54, 70, 101, 140, 170, 0, &
1, 36, 82, 108, 127, 174, 0, &
5, 53, 81, 105, 140, 0, 0, &
29, 53, 67, 99, 142, 173, 0, &
18, 49, 74, 97, 115, 167, 0, &
2, 57, 63, 103, 138, 157, 0, &
26, 38, 79, 112, 135, 171, 0, &
11, 52, 66, 88, 119, 148, 0, &
20, 40, 68, 117, 141, 160, 0, &
11, 48, 81, 89, 146, 169, 0, &
29, 47, 80, 92, 146, 172, 0, &
6, 32, 87, 104, 145, 169, 0, &
27, 34, 74, 106, 131, 165, 0, &
12, 56, 84, 88, 139, 0, 0, &
13, 56, 62, 111, 146, 171, 0, &
26, 37, 80, 105, 144, 151, 0, &
17, 31, 82, 113, 121, 161, 0, &
28, 49, 59, 94, 137, 0, 0, &
7, 55, 83, 101, 131, 168, 0, &
24, 50, 78, 106, 143, 149, 0/
data nrw/ &
6,6,6,6,6,6,6,6,6,6, &
6,6,6,6,6,6,6,6,6,6, &
6,6,6,6,6,6,6,6,6,6, &
6,6,6,6,6,6,6,6,6,7, &
6,6,6,6,6,7,6,6,6,6, &
6,6,6,6,6,7,6,6,6,6, &
7,6,5,6,6,6,6,6,6,5, &
6,6,6,6,6,6,6,6,6,6, &
5,6,6,6,5,6,6/
ncw=3
decoded=0
toc=0
tov=0
tanhtoc=0
! initialize messages to checks
do j=1,M
do i=1,nrw(j)
toc(i,j)=llr((Nm(i,j)))
enddo
enddo
ncnt=0
do iter=0,maxiterations
! Update bit log likelihood ratios (tov=0 in iteration 0).
do i=1,N
if( apmask(i) .ne. 1 ) then
zn(i)=llr(i)+sum(tov(1:ncw,i))
else
zn(i)=llr(i)
endif
enddo
! Check to see if we have a codeword (check before we do any iteration).
cw=0
where( zn .gt. 0. ) cw=1
ncheck=0
do i=1,M
synd(i)=sum(cw(Nm(1:nrw(i),i)))
if( mod(synd(i),2) .ne. 0 ) ncheck=ncheck+1
! if( mod(synd(i),2) .ne. 0 ) write(*,*) 'check ',i,' unsatisfied'
enddo
! write(*,*) 'number of unsatisfied parity checks ',ncheck
if( ncheck .eq. 0 ) then ! we have a codeword - reorder the columns and return it
codeword=cw(colorder+1)
decoded=codeword(M+1:N)
nerr=0
do i=1,N
if( (2*cw(i)-1)*llr(i) .lt. 0.0 ) nerr=nerr+1
enddo
nharderror=nerr
return
endif
if( iter.gt.0 ) then ! this code block implements an early stopping criterion
! if( iter.gt.10000 ) then ! this code block implements an early stopping criterion
nd=ncheck-nclast
if( nd .lt. 0 ) then ! # of unsatisfied parity checks decreased
ncnt=0 ! reset counter
else
ncnt=ncnt+1
endif
! write(*,*) iter,ncheck,nd,ncnt
if( ncnt .ge. 5 .and. iter .ge. 10 .and. ncheck .gt. 15) then
nharderror=-1
return
endif
endif
nclast=ncheck
! Send messages from bits to check nodes
do j=1,M
do i=1,nrw(j)
ibj=Nm(i,j)
toc(i,j)=zn(ibj)
do kk=1,ncw ! subtract off what the bit had received from the check
if( Mn(kk,ibj) .eq. j ) then
toc(i,j)=toc(i,j)-tov(kk,ibj)
endif
enddo
enddo
enddo
! send messages from check nodes to variable nodes
do i=1,M
tanhtoc(1:7,i)=tanh(-toc(1:7,i)/2)
enddo
do j=1,N
do i=1,ncw
ichk=Mn(i,j) ! Mn(:,j) are the checks that include bit j
Tmn=product(tanhtoc(1:nrw(ichk),ichk),mask=Nm(1:nrw(ichk),ichk).ne.j)
call platanh(-Tmn,y)
! y=atanh(-Tmn)
tov(i,j)=2*y
enddo
enddo
enddo
nharderror=-1
return
end subroutine bpdecode174
@@ -1,98 +0,0 @@
program ft8sim
! Generate simulated data for a 15-second HF/6m mode using 8-FSK.
! Output is saved to a *.wav file.
use wavhdr
include 'ft8_params.f90' !Set various constants
type(hdr) h !Header for .wav file
character arg*12,fname*17
character msg*22,msgsent*22
complex c0(0:NMAX-1)
complex c(0:NMAX-1)
integer itone(NN)
integer*2 iwave(NMAX) !Generated full-length waveform
! Get command-line argument(s)
nargs=iargc()
if(nargs.ne.6) then
print*,'Usage: ft8sim "message" DT fdop del nfiles snr'
print*,'Example: ft8sim "K1ABC W9XYZ EN37" 0.0 0.1 1.0 10 -18'
go to 999
endif
call getarg(1,msg) !Message to be transmitted
call getarg(2,arg)
read(arg,*) xdt !Time offset from nominal (s)
call getarg(3,arg)
read(arg,*) fspread !Watterson frequency spread (Hz)
call getarg(4,arg)
read(arg,*) delay !Watterson delay (ms)
call getarg(5,arg)
read(arg,*) nfiles !Number of files
call getarg(6,arg)
read(arg,*) snrdb !SNR_2500
twopi=8.0*atan(1.0)
fs=12000.0 !Sample rate (Hz)
dt=1.0/fs !Sample interval (s)
tt=NSPS*dt !Duration of symbols (s)
baud=1.0/tt !Keying rate (baud)
bw=8*baud !Occupied bandwidth (Hz)
txt=NZ*dt !Transmission length (s)
bandwidth_ratio=2500.0/(fs/2.0)
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*snrdb)
if(snrdb.gt.90.0) sig=1.0
txt=NN*NSPS/12000.0
call genft8(msg,msgsent,itone) !Source-encode, then get itone()
write(*,1000) f0,xdt,txt,snrdb,bw,msgsent
1000 format('f0:',f9.3,' DT:',f6.2,' TxT:',f6.1,' SNR:',f6.1, &
' BW:',f4.1,2x,a22)
! call sgran()
c=0.
do ifile=1,nfiles
c0=0.
do isig=1,25
f0=(isig+2)*100.0
phi=0.0
k=-1 + nint(xdt+0.5/dt)
do j=1,NN !Generate complex waveform
dphi=twopi*(f0+itone(j)*baud)*dt
if(k.eq.0) phi=-dphi
do i=1,NSPS
k=k+1
phi=phi+dphi
if(phi.gt.twopi) phi=phi-twopi
xphi=phi
if(k.ge.0 .and. k.lt.NMAX) c0(k)=cmplx(cos(xphi),sin(xphi))
enddo
enddo
if(fspread.ne.0.0 .or. delay.ne.0.0) call watterson(c,NMAX,fs,delay,fspread)
c=c+c0
enddo
c=c*sig
if(snrdb.lt.90) then
do i=0,NMAX-1 !Add gaussian noise at specified SNR
xnoise=gran()
ynoise=gran()
c(i)=c(i) + cmplx(xnoise,ynoise)
enddo
endif
fac=32767.0
rms=100.0
if(snrdb.ge.90.0) iwave(1:NMAX)=nint(fac*real(c))
if(snrdb.lt.90.0) iwave(1:NMAX)=nint(rms*real(c))
h=default_header(12000,NMAX)
write(fname,1102) ifile
1102 format('000000_',i6.6,'.wav')
open(10,file=fname,status='unknown',access='stream')
write(10) h,iwave !Save to *.wav file
close(10)
write(*,1110) ifile,xdt,f0,snrdb,fname
1110 format(i4,f7.2,f8.2,f7.1,2x,a17)
enddo
999 end program ft8sim