Merge branch 'feat-ft2' into develop

This commit is contained in:
Bill Somerville 2019-03-22 22:23:25 +00:00
commit 158f929249
72 changed files with 6369 additions and 362 deletions

View File

@ -394,6 +394,7 @@ set (wsjt_FSRCS
lib/chkhist.f90
lib/chkmsg.f90
lib/chkss2.f90
lib/ft4/clockit.f90
lib/ft8/compress.f90
lib/coord.f90
lib/db.f90
@ -455,6 +456,7 @@ set (wsjt_FSRCS
lib/ft8.f90
lib/ft8dec.f90
lib/ft8/ft8sim.f90
lib/ft8/ft8sim_gfsk.f90
lib/gen4.f90
lib/gen65.f90
lib/gen9.f90
@ -462,12 +464,16 @@ set (wsjt_FSRCS
lib/ft8/genft8.f90
lib/genmsk_128_90.f90
lib/genmsk40.f90
lib/ft4/genft4.f90
lib/ft4/gen_ft4wave.f90
lib/ft8/gen_ft8wave.f90
lib/genqra64.f90
lib/ft8/genft8refsig.f90
lib/genwspr.f90
lib/geodist.f90
lib/getlags.f90
lib/getmet4.f90
lib/ft2/gfsk_pulse.f90
lib/graycode.f90
lib/graycode65.f90
lib/grayline.f90
@ -506,6 +512,11 @@ set (wsjt_FSRCS
lib/msk144signalquality.f90
lib/msk144sim.f90
lib/mskrtd.f90
lib/nuttal_window.f90
lib/ft4/ft4sim.f90
lib/ft4/ft4sim_mult.f90
lib/ft4/ft4_decode.f90
lib/ft4/ft4_downsample.f90
lib/77bit/my_hash.f90
lib/wsprd/osdwspr.f90
lib/ft8/osd174_91.f90
@ -546,8 +557,12 @@ set (wsjt_FSRCS
lib/sync4.f90
lib/sync64.f90
lib/sync65.f90
lib/ft4/getcandidates4.f90
lib/fsk4hf/getcandidates2.f90
lib/ft4/syncft4.f90
lib/ft8/sync8.f90
lib/ft8/sync8d.f90
lib/ft4/sync4d.f90
lib/sync9.f90
lib/sync9f.f90
lib/sync9w.f90
@ -1254,9 +1269,21 @@ target_link_libraries (ft8 wsjt_fort wsjt_cxx)
add_executable (ft8sim lib/ft8/ft8sim.f90 wsjtx.rc)
target_link_libraries (ft8sim wsjt_fort wsjt_cxx)
add_executable (ft8sim_gfsk lib/ft8/ft8sim_gfsk.f90 wsjtx.rc)
target_link_libraries (ft8sim_gfsk wsjt_fort wsjt_cxx)
add_executable (msk144sim lib/msk144sim.f90 wsjtx.rc)
target_link_libraries (msk144sim wsjt_fort wsjt_cxx)
add_executable (ft4sim lib/ft4/ft4sim.f90 wsjtx.rc)
target_link_libraries (ft4sim wsjt_fort wsjt_cxx)
add_executable (ft4sim_mult lib/ft4/ft4sim_mult.f90 wsjtx.rc)
target_link_libraries (ft4sim_mult wsjt_fort wsjt_cxx)
add_executable (ft4d lib/ft4/ft4d.f90 wsjtx.rc)
target_link_libraries (ft4d wsjt_fort wsjt_cxx)
endif(WSJT_BUILD_UTILS)
# build the main application

View File

@ -209,6 +209,7 @@ namespace
|LB|NU|YT|PEI
|DC # District of Columbia
|DX # anyone else
|SCC # Slovenia Contest Club contest
)
)", QRegularExpression::CaseInsensitiveOption | QRegularExpression::ExtendedPatternSyntaxOption};
@ -630,6 +631,7 @@ private:
bool udpWindowToFront_;
bool udpWindowRestore_;
DataMode data_mode_;
bool bLowSidelobes_;
bool pwrBandTxMemory_;
bool pwrBandTuneMemory_;
@ -720,6 +722,7 @@ bool Configuration::accept_udp_requests () const {return m_->accept_udp_requests
QString Configuration::n1mm_server_name () const {return m_->n1mm_server_name_;}
auto Configuration::n1mm_server_port () const -> port_type {return m_->n1mm_server_port_;}
bool Configuration::broadcast_to_n1mm () const {return m_->broadcast_to_n1mm_;}
bool Configuration::lowSidelobes() const {return m_->bLowSidelobes_;}
bool Configuration::udpWindowToFront () const {return m_->udpWindowToFront_;}
bool Configuration::udpWindowRestore () const {return m_->udpWindowRestore_;}
Bands * Configuration::bands () {return &m_->bands_;}
@ -1289,6 +1292,8 @@ void Configuration::impl::initialize_models ()
ui_->udpWindowRestore->setChecked(udpWindowRestore_);
ui_->calibration_intercept_spin_box->setValue (calibration_.intercept);
ui_->calibration_slope_ppm_spin_box->setValue (calibration_.slope_ppm);
ui_->rbLowSidelobes->setChecked(bLowSidelobes_);
if(!bLowSidelobes_) ui_->rbMaxSensitivity->setChecked(true);
if (rig_params_.ptt_port.isEmpty ())
{
@ -1480,6 +1485,7 @@ void Configuration::impl::read_settings ()
rig_params_.audio_source = settings_->value ("TXAudioSource", QVariant::fromValue (TransceiverFactory::TX_audio_source_front)).value<TransceiverFactory::TXAudioSource> ();
rig_params_.ptt_port = settings_->value ("PTTport").toString ();
data_mode_ = settings_->value ("DataMode", QVariant::fromValue (data_mode_none)).value<Configuration::DataMode> ();
bLowSidelobes_ = settings_->value("LowSidelobes",true).toBool();
prompt_to_log_ = settings_->value ("PromptToLog", false).toBool ();
autoLog_ = settings_->value ("AutoLog", false).toBool ();
decodes_from_top_ = settings_->value ("DecodesFromTop", false).toBool ();
@ -1581,6 +1587,7 @@ void Configuration::impl::write_settings ()
settings_->setValue ("CATStopBits", QVariant::fromValue (rig_params_.stop_bits));
settings_->setValue ("CATHandshake", QVariant::fromValue (rig_params_.handshake));
settings_->setValue ("DataMode", QVariant::fromValue (data_mode_));
settings_->setValue ("LowSidelobes",bLowSidelobes_);
settings_->setValue ("PromptToLog", prompt_to_log_);
settings_->setValue ("AutoLog", autoLog_);
settings_->setValue ("DecodesFromTop", decodes_from_top_);
@ -2039,6 +2046,7 @@ void Configuration::impl::accept ()
watchdog_ = ui_->tx_watchdog_spin_box->value ();
TX_messages_ = ui_->TX_messages_check_box->isChecked ();
data_mode_ = static_cast<DataMode> (ui_->TX_mode_button_group->checkedId ());
bLowSidelobes_ = ui_->rbLowSidelobes->isChecked();
save_directory_ = ui_->save_path_display_label->text ();
azel_directory_ = ui_->azel_path_display_label->text ();
enable_VHF_features_ = ui_->enable_VHF_features_check_box->isChecked ();

View File

@ -137,6 +137,7 @@ public:
bool twoPass() const;
bool bFox() const;
bool bHound() const;
bool bLowSidelobes() const;
bool x2ToneSpacing() const;
bool x4ToneSpacing() const;
bool MyDx() const;
@ -152,6 +153,7 @@ public:
port_type n1mm_server_port () const;
bool valid_n1mm_info () const;
bool broadcast_to_n1mm() const;
bool lowSidelobes() const;
bool accept_udp_requests () const;
bool udpWindowToFront () const;
bool udpWindowRestore () const;

View File

@ -2821,6 +2821,48 @@ Right click for insert and delete options.</string>
</layout>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_7">
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="title">
<string>Waterfall spectra</string>
</property>
<widget class="QRadioButton" name="rbLowSidelobes">
<property name="geometry">
<rect>
<x>10</x>
<y>20</y>
<width>91</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Low sidelobes</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
<widget class="QRadioButton" name="rbMaxSensitivity">
<property name="geometry">
<rect>
<x>120</x>
<y>20</y>
<width>92</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Most sensitive</string>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
@ -3036,13 +3078,13 @@ Right click for insert and delete options.</string>
</connection>
</connections>
<buttongroups>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="special_op_activity_button_group"/>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="split_mode_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="CAT_handshake_button_group"/>
</buttongroups>
</ui>

View File

@ -51,10 +51,10 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
// Time according to this computer which becomes our base time
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
if (m_state != Idle)
{
stop ();
}
// qDebug() << "ModStart" << symbolsLength << framesPerSymbol
// << frequency << toneSpacing;
if(m_state != Idle) stop ();
m_quickClose = false;
@ -92,6 +92,16 @@ void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
if (synchronize && !m_tuning && !m_bFastMode) {
m_silentFrames = m_ic + m_frameRate / (1000 / delay_ms) - (mstr * (m_frameRate / 1000));
}
if((symbolsLength==103 or symbolsLength==105) and framesPerSymbol==512
and (toneSpacing==12000.0/512.0 or toneSpacing==-2.0)) {
//### FT4 parameters
delay_ms=100;
mstr=5000;
m_ic=0;
m_silentFrames=0;
}
// qDebug() << "Mod AA" << symbolsLength << framesPerSymbol << toneSpacing;
// qDebug() << "Mod AB" << delay_ms << mstr << m_ic << m_silentFrames;
initialize (QIODevice::ReadOnly, channel);
Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ?
@ -170,7 +180,8 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
case Active:
{
unsigned int isym=0;
// qDebug() << "Mod A" << m_toneSpacing << m_ic;
// qDebug() << "Mod A" << m_toneSpacing << m_frequency << m_nsps
// << m_ic << m_symbolsLength << icw[0];
if(!m_tuning) isym=m_ic/(4.0*m_nsps); // Actual fsample=48000
bool slowCwId=((isym >= m_symbolsLength) && (icw[0] > 0)) && (!m_bFastMode);
if(m_TRperiod==3) slowCwId=false;
@ -289,9 +300,12 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
if (m_ic > i1) m_amp = 0.0;
sample=qRound(m_amp*qSin(m_phi));
if(m_toneSpacing < 0) sample=qRound(m_amp*foxcom_.wave[m_ic]);
// if(m_ic < 100) qDebug() << "Mod C" << m_ic << m_amp << foxcom_.wave[m_ic] << sample;
//Here's where we transmit from a precomputed wave[] array:
if(!m_tuning and (m_toneSpacing < 0)) sample=qRound(m_amp*foxcom_.wave[m_ic]);
// if(m_ic < 10) qDebug() << "Mod Tx" << m_ic << m_amp
// << foxcom_.wave[m_ic] << sample
// << m_toneSpacing;
samples = load(postProcessSample(sample), samples);
++framesGenerated;
@ -309,6 +323,14 @@ qint64 Modulator::readData (char * data, qint64 maxSize)
m_frequency0 = m_frequency;
// done for this chunk - continue on next call
// qint64 ms1=QDateTime::currentMSecsSinceEpoch() - m_ms0;
// if(m_ic>=4*144*160) qDebug() << "Modulator finished" << m_ic << 0.001*ms1;
while (samples != end) // pad block with silence
{
samples = load (0, samples);
++framesGenerated;
}
return framesGenerated * bytesPerFrame ();
}
// fall through

View File

@ -33,6 +33,7 @@ public:
void setSpread(double s) {m_fSpread=s;}
void setTRPeriod(unsigned p) {m_period=p;}
void set_nsym(int n) {m_symbolsLength=n;}
void set_ms0(qint64 ms) {m_ms0=ms;}
Q_SLOT void start (unsigned symbolsLength, double framesPerSymbol, double frequency,
double toneSpacing, SoundOutput *, Channel = Mono,
@ -73,6 +74,7 @@ private:
double m_fSpread;
qint64 m_silentFrames;
qint64 m_ms0;
qint32 m_TRperiod;
qint16 m_ramp;

View File

@ -51,11 +51,12 @@ DecodedText::DecodedText (QString const& the_string)
QStringList DecodedText::messageWords () const
{
if (is_standard_)
{
// extract up to the first four message words
return words_re.match (message_).capturedTexts ();
}
if(is_standard_) {
// extract up to the first four message words
QString t=message_;
if(t.left(4)=="TU; ") t=message_.mid(4,-1);
return words_re.match(t).capturedTexts();
}
// simple word split for free text messages
auto words = message_.split (' ', QString::SkipEmptyParts);
// add whole message as item 0 to mimic RE capture list

View File

@ -12,18 +12,22 @@ subroutine addit(itone,nfsample,nsym,nsps,ifreq,sig,dat)
dphi=0.
iters=1
if(nsym.eq.79) iters=2
if(nsym.eq.79) iters=2 !FT8
if(nsym.eq.103) iters=5 !FT4
do iter=1,iters
f=ifreq
phi=0.
ntot=nsym*tsym/dt
k=12000 !Start audio at t = 1.0 s
t=0.
if(nsym.eq.79) k=12000 + (iter-1)*12000*30 !Special case for FT8
if(nsym.eq.79) k=12000 + (iter-1)*12000*30 !Special case for FT8
if(nsym.eq.103) k=12000 + (iter-1)*12000*10 !Special case for FT4
isym0=-1
do i=1,ntot
t=t+dt
isym=nint(t/tsym) + 1
if(isym.gt.nsym) exit
if(isym.ne.isym0) then
freq=f + itone(isym)*baud
dphi=twopi*freq*dt
@ -59,7 +63,7 @@ subroutine addcw(icw,ncw,ifreq,sig,dat)
phi=0.
k=12000 !Start audio at t = 1.0 s
t=0.
npts=60*12000
npts=59*12000
x=0.
do i=1,npts
t=t+dt

View File

@ -1,6 +1,6 @@
program allsim
! Generate simulated data for WSJT-X slow modes: JT4, JT9, JT65, QRA64,
! Generate simulated data for WSJT-X modes: JT4, JT9, JT65, FT8, FT4, QRA64,
! and WSPR. Also unmodulated carrier and 20 WPM CW.
@ -15,6 +15,7 @@ program allsim
logical*1 bcontest
real*4 dat(NMAX)
character message*22,msgsent*22,arg*8,mygrid*6
character*37 msg37,msgsent37
nargs=iargc()
if(nargs.ne.1) then
@ -60,15 +61,20 @@ program allsim
call gen4(message,0,msgsent,itone,itype)
call addit(itone,11025,206,2520,1200,sig,dat) !JT4
i3bit=0 ! ### TEMPORARY ??? ###
call genft8(message,mygrid,bcontest,i3bit,msgsent,msgbits,itone)
i3=-1
n3=-1
call genft8(message,i3,n3,msgsent,msgbits,itone)
call addit(itone,12000,79,1920,1400,sig,dat) !FT8
msg37=message//' '
call genft4(msg37,0,msgsent37,itone)
call addit(itone,12000,103,512,1600,sig,dat) !FT4
call genqra64(message,0,msgsent,itone,itype)
call addit(itone,12000,84,6912,1600,sig,dat) !QRA64
call addit(itone,12000,84,6912,1800,sig,dat) !QRA64
call gen65(message,0,msgsent,itone,itype)
call addit(itone,11025,126,4096,1800,sig,dat) !JT65
call addit(itone,11025,126,4096,2000,sig,dat) !JT65
iwave(1:npts)=nint(rms*dat(1:npts))

View File

@ -19,6 +19,7 @@ subroutine four2a(a,nfft,ndim,isign,iform)
! This version of four2a makes calls to the FFTW library to do the
! actual computations.
use fftw3
parameter (NPMAX=2100) !Max numberf of stored plans
parameter (NSMALL=16384) !Max size of "small" FFTs
complex a(nfft) !Array to be transformed
@ -29,7 +30,6 @@ subroutine four2a(a,nfft,ndim,isign,iform)
logical found_plan
data nplan/0/ !Number of stored plans
common/patience/npatience,nthreads !Patience and threads for FFTW plans
include 'fftw3.f90' !FFTW definitions
save plan,nplan,nn,ns,nf,nl
if(nfft.lt.0) go to 999
@ -107,7 +107,7 @@ subroutine four2a(a,nfft,ndim,isign,iform)
!$omp end critical(fftw)
end if
enddo
call fftwf_cleanup()
nplan=0
!$omp end critical(four2a)

12
lib/fsk4hf/ft2_params.f90 Normal file
View File

@ -0,0 +1,12 @@
! LDPC (128,90) code
parameter (KK=90) !Information bits (77 + CRC13)
parameter (ND=128) !Data symbols
parameter (NS=16) !Sync symbols (2x8)
parameter (NN=NS+ND) !Total channel symbols (144)
parameter (NSPS=160) !Samples per symbol at 12000 S/s
parameter (NZ=NSPS*NN) !Samples in full 1.92 s waveform (23040)
parameter (NMAX=2.5*12000) !Samples in iwave (36,000)
parameter (NFFT1=400, NH1=NFFT1/2) !Length of FFTs for symbol spectra
parameter (NSTEP=NSPS/4) !Rough time-sync step size
parameter (NHSYM=NMAX/NSTEP-3) !Number of symbol spectra (1/4-sym steps)
parameter (NDOWN=16) !Downsample factor

335
lib/fsk4hf/ft2d.f90 Normal file
View File

@ -0,0 +1,335 @@
program ft2d
use crc
use packjt77
include 'ft2_params.f90'
character arg*8,message*37,c77*77,infile*80,fname*16,datetime*11
character*37 decodes(100)
character*120 data_dir
character*90 dmsg
complex c2(0:NMAX/16-1) !Complex waveform
complex cb(0:NMAX/16-1)
complex cd(0:144*10-1) !Complex waveform
complex c1(0:9),c0(0:9)
complex ccor(0:1,144)
complex csum,cterm,cc0,cc1,csync1,csync2
complex csync(16),csl(0:159)
real*8 fMHz
real a(5)
real rxdata(128),llr(128) !Soft symbols
real llr2(128)
real sbits(144),sbits1(144),sbits3(144)
real ps(0:8191),psbest(0:8191)
real candidates(100,2)
real savg(NH1),sbase(NH1)
integer ihdr(11)
integer*2 iwave(NMAX) !Generated full-length waveform
integer*1 message77(77),apmask(128),cw(128)
integer*1 hbits(144),hbits1(144),hbits3(144)
integer*1 s16(16),s45(45)
logical unpk77_success
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
data s45/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,1,1,0,1,0,0,0,1,1,1,0,0/
fs=12000.0/NDOWN !Sample rate
dt=1/fs !Sample interval after downsample (s)
tt=NSPS*dt !Duration of "itone" symbols (s)
baud=1.0/tt !Keying rate for "itone" symbols (baud)
txt=NZ*dt !Transmission length (s)
twopi=8.0*atan(1.0)
h=0.800 !h=0.8 seems to be optimum for AWGN sensitivity (not for fading)
dphi=twopi/2*baud*h*dt*16 ! dt*16 is samp interval after downsample
dphi0=-1*dphi
dphi1=+1*dphi
phi0=0.0
phi1=0.0
do i=0,9
c1(i)=cmplx(cos(phi1),sin(phi1))
c0(i)=cmplx(cos(phi0),sin(phi0))
phi1=mod(phi1+dphi1,twopi)
phi0=mod(phi0+dphi0,twopi)
enddo
the=twopi*h/2.0
cc1=cmplx(cos(the),-sin(the))
cc0=cmplx(cos(the),sin(the))
k=0
do j=1,16
dphi1=(2*s16(j)-1)*dphi
phi1=0.0
do i=0,9
csl(k)=cmplx(cos(phi1),sin(phi1))
phi1=mod(phi1+dphi1,twopi)
k=k+1
enddo
enddo
nargs=iargc()
if(nargs.lt.1) then
print*,'Usage: ft2d [-a <data_dir>] [-f fMHz] file1 [file2 ...]'
go to 999
endif
iarg=1
data_dir="."
call getarg(iarg,arg)
if(arg(1:2).eq.'-a') then
call getarg(iarg+1,data_dir)
iarg=iarg+2
endif
call getarg(iarg,arg)
if(arg(1:2).eq.'-f') then
call getarg(iarg+1,arg)
read(arg,*) fMHz
iarg=iarg+2
endif
ncoh=1
do ifile=iarg,nargs
call getarg(ifile,infile)
j2=index(infile,'.wav')
open(10,file=infile,status='old',access='stream')
read(10,end=999) ihdr,iwave
read(infile(j2-4:j2-1),*) nutc
datetime=infile(j2-11:j2-1)
close(10)
candidates=0.0
ncand=0
call getcandidates2(iwave,375.0,3000.0,0.2,2200.0,100,savg,candidates,ncand,sbase)
ndecodes=0
do icand=1,ncand
f0=candidates(icand,1)
xsnr=1.0
if( f0.le.375.0 .or. f0.ge.(5000.0-375.0) ) cycle
call ft2_downsample(iwave,f0,c2) ! downsample from 160s/Symbol to 10s/Symbol
!c2=c2/sqrt(sum(abs(c2(0:NMAX/16-1))))
!ishift=-1
!rccbest=-99.
!do is=0,435
!rcc=0.0
! do id=10,10
! rcc=rcc+abs(sum(conjg(c2(is:is+159-id))*c2(is+id:is+159)*csl(0:159-id)*conjg(csl(id:159))))
! enddo
! if(rcc.gt.rccbest) then
! rccbest=rcc
! ishift=is
! endif
!write(21,*) is,rcc
!enddo
! 750 samples/second here
ibest=-1
sybest=-99.
dfbest=-1.
do if=-30,+30
df=if
a=0.
a(1)=-df
call twkfreq1(c2,NMAX/16,fs,a,cb)
do is=0,374
csync1=0.
cterm=1
do ib=1,16
! do ib=1,45
i1=(ib-1)*10+is
if(s16(ib).eq.1) then
! if(s45(ib).eq.1) then
csync1=csync1+sum(cb(i1:i1+9)*conjg(c1(0:9)))*cterm
cterm=cterm*cc1
else
csync1=csync1+sum(cb(i1:i1+9)*conjg(c0(0:9)))*cterm
cterm=cterm*cc0
endif
enddo
if(abs(csync1).gt.sybest) then
ibest=is
sybest=abs(csync1)
dfbest=df
endif
enddo
enddo
a=0.
!dfbest=1500.0-f0
a(1)=-dfbest
call twkfreq1(c2,NMAX/16,fs,a,cb)
!ibest=197
ib=ibest
cd=cb(ib:ib+144*10-1)
s2=sum(cd*conjg(cd))/(10*144)
cd=cd/sqrt(s2)
do nseq=1,4
if( nseq.eq.1 ) then ! noncoherent single-symbol detection
sbits1=0.0
do ibit=1,144
ib=(ibit-1)*10
ccor(1,ibit)=sum(cd(ib:ib+9)*conjg(c1(0:9)))
ccor(0,ibit)=sum(cd(ib:ib+9)*conjg(c0(0:9)))
sbits1(ibit)=abs(ccor(1,ibit))-abs(ccor(0,ibit))
hbits1(ibit)=0
if(sbits1(ibit).gt.0) hbits1(ibit)=1
enddo
sbits=sbits1
hbits=hbits1
sbits3=sbits1
hbits3=hbits1
elseif( nseq.ge.2 ) then
nbit=2*nseq-1
numseq=2**(nbit)
ps=0
do ibit=nbit/2+1,144-nbit/2
ps=0.0
pmax=0.0
do iseq=0,numseq-1
csum=0.0
cterm=1.0
k=1
do i=nbit-1,0,-1
ibb=iand(iseq/(2**i),1)
csum=csum+ccor(ibb,ibit-(nbit/2+1)+k)*cterm
if(ibb.eq.0) cterm=cterm*cc0
if(ibb.eq.1) cterm=cterm*cc1
k=k+1
enddo
ps(iseq)=abs(csum)
if( ps(iseq) .gt. pmax ) then
pmax=ps(iseq)
ibflag=1
endif
enddo
if( ibflag .eq. 1 ) then
psbest=ps
ibflag=0
endif
call getbitmetric(2**(nbit/2),psbest,numseq,sbits3(ibit))
hbits3(ibit)=0
if(sbits3(ibit).gt.0) hbits3(ibit)=1
enddo
sbits=sbits3
hbits=hbits3
endif
nsync_qual=count(hbits(1:16).eq.s16)
! if(nsync_qual.lt.10) exit
rxdata=sbits(17:144)
rxav=sum(rxdata(1:128))/128.0
rx2av=sum(rxdata(1:128)*rxdata(1:128))/128.0
rxsig=sqrt(rx2av-rxav*rxav)
rxdata=rxdata/rxsig
sigma=0.80
llr(1:128)=2*rxdata/(sigma*sigma)
!xllrmax=maxval(abs(llr))
!write(*,*) ifile,icand,nseq,nsync_qual
apmask=0
!apmask(1:29)=1
!llr(1:29)=xllrmax*(2*s45(17:45)-1)
max_iterations=40
do ibias=0,0
llr2=llr
if(ibias.eq.1) llr2=llr+0.4
if(ibias.eq.2) llr2=llr-0.4
call bpdecode128_90(llr2,apmask,max_iterations,message77,cw,nharderror,niterations)
if(nharderror.ge.0) exit
enddo
if(sum(message77).eq.0) cycle
if( nharderror.ge.0 ) then
write(c77,'(77i1)') message77(1:77)
call unpack77(c77,1,message,unpk77_success)
idupe=0
do i=1,ndecodes
if(decodes(i).eq.message) idupe=1
enddo
if(idupe.eq.1) goto 888
ndecodes=ndecodes+1
decodes(ndecodes)=message
nsnr=nint(xsnr)
freq=f0+dfbest
1210 format(a11,2i4,f6.2,f12.7,2x,a22,i3)
write(*,1212) datetime(8:11),nsnr,ibest/750.0,freq,message,'*',nseq,nharderror,nsync_qual
1212 format(a4,i4,2x,f5.3,f11.1,2x,a22,a1,i5,i5,i5)
goto 888
endif
enddo ! nseq
888 continue
enddo !candidate list
enddo !files
write(*,1120)
1120 format("<DecodeFinished>")
999 end program ft2d
subroutine getbitmetric(ib,ps,ns,xmet)
real ps(0:ns-1)
xm1=0
xm0=0
do i=0,ns-1
if( iand(i/ib,1) .eq. 1 .and. ps(i) .gt. xm1 ) xm1=ps(i)
if( iand(i/ib,1) .eq. 0 .and. ps(i) .gt. xm0 ) xm0=ps(i)
enddo
xmet=xm1-xm0
return
end subroutine getbitmetric
subroutine downsample2(ci,f0,co)
parameter(NI=144*160,NH=NI/2,NO=NI/16) ! downsample from 200 samples per symbol to 10
complex ci(0:NI-1),ct(0:NI-1)
complex co(0:NO-1)
fs=12000.0
df=fs/NI
ct=ci
call four2a(ct,NI,1,-1,1) !c2c FFT to freq domain
i0=nint(f0/df)
ct=cshift(ct,i0)
co=0.0
co(0)=ct(0)
b=8.0
do i=1,NO/2
arg=(i*df/b)**2
filt=exp(-arg)
co(i)=ct(i)*filt
co(NO-i)=ct(NI-i)*filt
enddo
co=co/NO
call four2a(co,NO,1,1,1) !c2c FFT back to time domain
return
end subroutine downsample2
subroutine ft2_downsample(iwave,f0,c)
! Input: i*2 data in iwave() at sample rate 12000 Hz
! Output: Complex data in c(), sampled at 1200 Hz
include 'ft2_params.f90'
parameter (NFFT2=NMAX/16)
integer*2 iwave(NMAX)
complex c(0:NMAX/16-1)
complex c1(0:NFFT2-1)
complex cx(0:NMAX/2)
real x(NMAX)
equivalence (x,cx)
BW=4.0*75
df=12000.0/NMAX
x=iwave
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
ibw=nint(BW/df)
i0=nint(f0/df)
c1=0.
c1(0)=cx(i0)
do i=1,NFFT2/2
arg=(i-1)*df/bw
win=exp(-arg*arg)
c1(i)=cx(i0+i)*win
c1(NFFT2-i)=cx(i0-i)*win
enddo
c1=c1/NFFT2
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
c=c1(0:NMAX/16-1)
return
end subroutine ft2_downsample

154
lib/fsk4hf/ft2sim.f90 Normal file
View File

@ -0,0 +1,154 @@
program ft2sim
! Generate simulated signals for experimental "FT2" mode
use wavhdr
use packjt77
include 'ft2_params.f90' !Set various constants
parameter (NWAVE=NN*NSPS)
type(hdr) h !Header for .wav file
character arg*12,fname*17
character msg37*37,msgsent37*37
character c77*77
complex c0(0:NMAX-1)
complex c(0:NMAX-1)
real wave(NMAX)
real dphi(0:NMAX-1)
real pulse(480)
integer itone(NN)
integer*1 msgbits(77)
integer*2 iwave(NMAX) !Generated full-length waveform
! Get command-line argument(s)
nargs=iargc()
if(nargs.ne.8) then
print*,'Usage: ft2sim "message" f0 DT fdop del width nfiles snr'
print*,'Examples: ft2sim "K1ABC W9XYZ EN37" 1500.0 0.0 0.1 1.0 0 10 -18'
print*,' ft2sim "WA9XYZ/R KA1ABC/R FN42" 1500.0 0.0 0.1 1.0 0 10 -18'
print*,' ft2sim "K1ABC RR73; W9XYZ <KH1/KH7Z> -11" 300 0 0 0 25 1 -10'
go to 999
endif
call getarg(1,msg37) !Message to be transmitted
call getarg(2,arg)
read(arg,*) f0 !Frequency (only used for single-signal)
call getarg(3,arg)
read(arg,*) xdt !Time offset from nominal (s)
call getarg(4,arg)
read(arg,*) fspread !Watterson frequency spread (Hz)
call getarg(5,arg)
read(arg,*) delay !Watterson delay (ms)
call getarg(6,arg)
read(arg,*) width !Filter transition width (Hz)
call getarg(7,arg)
read(arg,*) nfiles !Number of files
call getarg(8,arg)
read(arg,*) snrdb !SNR_2500
nfiles=abs(nfiles)
twopi=8.0*atan(1.0)
fs=12000.0 !Sample rate (Hz)
dt=1.0/fs !Sample interval (s)
hmod=0.800 !Modulation index (0.5 is MSK, 1.0 is FSK)
tt=NSPS*dt !Duration of symbols (s)
baud=1.0/tt !Keying rate (baud)
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
! Source-encode, then get itone()
i3=-1
n3=-1
call pack77(msg37,i3,n3,c77)
read(c77,'(77i1)') msgbits
call genft2(msg37,0,msgsent37,itone,itype)
write(*,*)
write(*,'(a9,a37,3x,a7,i1,a1,i1)') 'Message: ',msgsent37,'i3.n3: ',i3,'.',n3
write(*,1000) f0,xdt,txt,snrdb
1000 format('f0:',f9.3,' DT:',f6.2,' TxT:',f6.1,' SNR:',f6.1)
write(*,*)
if(i3.eq.1) then
write(*,*) ' mycall hiscall hisgrid'
write(*,'(28i1,1x,i1,1x,28i1,1x,i1,1x,i1,1x,15i1,1x,3i1)') msgbits(1:77)
else
write(*,'(a14)') 'Message bits: '
write(*,'(77i1)') msgbits
endif
write(*,*)
write(*,'(a17)') 'Channel symbols: '
write(*,'(79i1)') itone
write(*,*)
call sgran()
! The filtered frequency pulse
do i=1,480
tt=(i-240.5)/160.0
pulse(i)=gfsk_pulse(1.0,tt)
enddo
! Define the instantaneous frequency waveform
dphi_peak=twopi*(hmod/2.0)/real(NSPS)
dphi=0.0
do j=1,NN
ib=(j-1)*160
ie=ib+480-1
dphi(ib:ie)=dphi(ib:ie)+dphi_peak*pulse*(2*itone(j)-1)
enddo
phi=0.0
c0=0.0
dphi=dphi+twopi*f0*dt
do j=0,NMAX-1
c0(j)=cmplx(cos(phi),sin(phi))
phi=mod(phi+dphi(j),twopi)
enddo
c0(0:159)=c0(0:159)*(1.0-cos(twopi*(/(i,i=0,159)/)/320.0) )/2.0
c0(145*160:145*160+159)=c0(145*160:145*160+159)*(1.0+cos(twopi*(/(i,i=0,159)/)/320.0 ))/2.0
c0(146*160:)=0.
k=nint((xdt+0.25)/dt)
c0=cshift(c0,-k)
ia=k
do ifile=1,nfiles
c=c0
if(fspread.ne.0.0 .or. delay.ne.0.0) call watterson(c,NMAX,NWAVE,fs,delay,fspread)
c=sig*c
ib=k
wave=real(c)
peak=maxval(abs(wave(ia:ib)))
nslots=1
if(width.gt.0.0) call filt8(f0,nslots,width,wave)
if(snrdb.lt.90) then
do i=1,NMAX !Add gaussian noise at specified SNR
xnoise=gran()
wave(i)=wave(i) + xnoise
enddo
endif
gain=100.0
if(snrdb.lt.90.0) then
wave=gain*wave
else
datpk=maxval(abs(wave))
fac=32766.9/datpk
wave=fac*wave
endif
if(any(abs(wave).gt.32767.0)) print*,"Warning - data will be clipped."
iwave=nint(wave)
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 ft2sim

329
lib/fsk4hf/ft4d.f90 Normal file
View File

@ -0,0 +1,329 @@
program ft4d
use crc
use packjt77
include 'ft4_params.f90'
character arg*8,message*37,c77*77,infile*80,fname*16,datetime*11
character*37 decodes(100)
character*120 data_dir
character*90 dmsg
complex cd2(0:NMAX/16-1) !Complex waveform
complex cb(0:NMAX/16-1)
complex cd(0:76*20-1) !Complex waveform
complex csum,cterm
complex ctwk(80),ctwk2(80)
complex csymb(20)
complex cs(0:3,NN)
real s4(0:3,NN)
real*8 fMHz
real ps(0:8191),psbest(0:8191)
real bmeta(152),bmetb(152),bmetc(152)
real s(NH1,NHSYM)
real a(5)
real llr(128),llr2(128),llra(128),llrb(128),llrc(128)
real s2(0:255)
real candidate(3,100)
real savg(NH1),sbase(NH1)
integer ihdr(11)
integer icos4(0:3)
integer*2 iwave(NMAX) !Generated full-length waveform
integer*1 message77(77),apmask(128),cw(128)
integer*1 hbits(152),hbits1(152),hbits3(152)
integer*1 s12(12)
integer graymap(0:3)
integer ip(1)
logical unpk77_success
logical one(0:511,0:7) ! 256 4-symbol sequences, 8 bits
data s12/1,1,1,2,2,2,2,2,2,1,1,1/
data icos4/0,1,3,2/
data graymap/0,1,3,2/
save one
fs=12000.0/NDOWN !Sample rate
dt=1/fs !Sample interval after downsample (s)
tt=NSPS*dt !Duration of "itone" symbols (s)
baud=1.0/tt !Keying rate for "itone" symbols (baud)
txt=NZ*dt !Transmission length (s)
twopi=8.0*atan(1.0)
h=1.0 !h=0.8 seems to be optimum for AWGN sensitivity (not for fading)
one=.false.
do i=0,255
do j=0,7
if(iand(i,2**j).ne.0) one(i,j)=.true.
enddo
enddo
nargs=iargc()
if(nargs.lt.1) then
print*,'Usage: ft4d [-a <data_dir>] [-f fMHz] file1 [file2 ...]'
go to 999
endif
iarg=1
data_dir="."
call getarg(iarg,arg)
if(arg(1:2).eq.'-a') then
call getarg(iarg+1,data_dir)
iarg=iarg+2
endif
call getarg(iarg,arg)
if(arg(1:2).eq.'-f') then
call getarg(iarg+1,arg)
read(arg,*) fMHz
iarg=iarg+2
endif
ncoh=1
do ifile=iarg,nargs
call getarg(ifile,infile)
j2=index(infile,'.wav')
open(10,file=infile,status='old',access='stream')
read(10,end=999) ihdr,iwave
read(infile(j2-4:j2-1),*) nutc
datetime=infile(j2-11:j2-1)
close(10)
candidate=0.0
ncand=0
nfqso=1500
nfa=500
nfb=2700
syncmin=1.0
maxcand=100
! call syncft4(iwave,nfa,nfb,syncmin,nfqso,maxcand,s,candidate,ncand,sbase)
call getcandidates4(iwave,375.0,3000.0,0.2,2200.0,100,savg,candidate,ncand,sbase)
ndecodes=0
do icand=1,ncand
f0=candidate(1,icand)-1.5*37.5
xsnr=1.0
if( f0.le.375.0 .or. f0.ge.(5000.0-375.0) ) cycle
call ft4_downsample(iwave,f0,cd2) ! downsample from 320 Sa/Symbol to 20 Sa/Symbol
sum2=sum(cd2*conjg(cd2))/(20.0*76)
if(sum2.gt.0.0) cd2=cd2/sqrt(sum2)
! 750 samples/second here
ibest=-1
smax=-99.
dfbest=-1.
do idf=-90,+90,5
df=idf
a=0.
a(1)=df
ctwk=1.
call twkfreq1(ctwk,80,fs,a,ctwk2)
do istart=0,315
call sync4d(cd2,istart,ctwk2,1,sync)
if(sync.gt.smax) then
smax=sync
ibest=istart
dfbest=df
endif
enddo
enddo
f0=f0+dfbest
!f0=1443.75
call ft4_downsample(iwave,f0,cb) ! downsample from 320s/Symbol to 20s/Symbol
sum2=sum(abs(cb)**2)/(20.0*76)
if(sum2.gt.0.0) cb=cb/sqrt(sum2)
!ibest=208
cd=cb(ibest:ibest+76*20-1)
do k=1,NN
i1=(k-1)*20
csymb=cd(i1:i1+19)
call four2a(csymb,20,1,-1,1)
cs(0:3,k)=csymb(1:4)/1e2
s4(0:3,k)=abs(csymb(1:4))
enddo
! sync quality check
is1=0
is2=0
is3=0
do k=1,4
ip=maxloc(s4(:,k))
if(icos4(k-1).eq.(ip(1)-1)) is1=is1+1
ip=maxloc(s4(:,k+36))
if(icos4(k-1).eq.(ip(1)-1)) is2=is2+1
ip=maxloc(s4(:,k+72))
if(icos4(k-1).eq.(ip(1)-1)) is3=is3+1
enddo
! hard sync sum - max is 12
nsync=is1+is2+is3
do nseq=1,3
if(nseq.eq.1) nsym=1
if(nseq.eq.2) nsym=2
if(nseq.eq.3) nsym=4
nt=2**(2*nsym)
do ks=1,76,nsym
amax=-1.0
do i=0,nt-1
i1=i/64
i2=iand(i,63)/16
i3=iand(i,15)/4
i4=iand(i,3)
if(nsym.eq.1) then
s2(i)=abs(cs(graymap(i4),ks))
elseif(nsym.eq.2) then
s2(i)=abs(cs(graymap(i3),ks)+cs(graymap(i4),ks+1))
elseif(nsym.eq.4) then
s2(i)=abs(cs(graymap(i1),ks ) + &
cs(graymap(i2),ks+1) + &
cs(graymap(i3),ks+2) + &
cs(graymap(i4),ks+3) &
)
else
print*,"Error - nsym must be 1, 2, or 4."
endif
enddo
ipt=1+(ks-1)*2
if(nsym.eq.1) ibmax=1
if(nsym.eq.2) ibmax=3
if(nsym.eq.4) ibmax=7
do ib=0,ibmax
bm=maxval(s2(0:nt-1),one(0:nt-1,ibmax-ib)) - &
maxval(s2(0:nt-1),.not.one(0:nt-1,ibmax-ib))
if(ipt+ib .gt.152) cycle
if(nsym.eq.1) then
bmeta(ipt+ib)=bm
elseif(nsym.eq.2) then
bmetb(ipt+ib)=bm
elseif(nsym.eq.4) then
bmetc(ipt+ib)=bm
endif
enddo
enddo
enddo
call normalizebmet(bmeta,152)
call normalizebmet(bmetb,152)
call normalizebmet(bmetc,152)
hbits=0
where(bmeta.ge.0) hbits=1
ns1=count(hbits( 1: 8).eq.(/0,0,0,1,1,0,1,1/))
ns2=count(hbits( 73: 80).eq.(/0,0,0,1,1,0,1,1/))
ns3=count(hbits(145:152).eq.(/0,0,0,1,1,0,1,1/))
nsync_qual=ns1+ns2+ns3
sigma=0.7
llra(1:64)=bmeta(9:72)
llra(65:128)=bmeta(81:144)
llra=2*llra/sigma**2
llrb(1:64)=bmetb(9:72)
llrb(65:128)=bmetb(81:144)
llrb=2*llrb/sigma**2
llrc(1:64)=bmetc(9:72)
llrc(65:128)=bmetc(81:144)
llrc=2*llrc/sigma**2
do isd=1,3
if(isd.eq.1) llr=llra
if(isd.eq.2) llr=llrb
if(isd.eq.3) llr=llrc
apmask=0
max_iterations=40
do ibias=0,0
llr2=llr
if(ibias.eq.1) llr2=llr+0.4
if(ibias.eq.2) llr2=llr-0.4
call bpdecode128_90(llr2,apmask,max_iterations,message77,cw,nharderror,niterations)
if(nharderror.ge.0) exit
enddo
if(sum(message77).eq.0) cycle
if( nharderror.ge.0 ) then
write(c77,'(77i1)') message77(1:77)
call unpack77(c77,1,message,unpk77_success)
idupe=0
do i=1,ndecodes
if(decodes(i).eq.message) idupe=1
enddo
if(idupe.eq.1) cycle
ndecodes=ndecodes+1
decodes(ndecodes)=message
nsnr=nint(xsnr)
write(*,1212) datetime(8:11),nsnr,ibest/750.0,f0,message,'*',nharderror,nsync_qual,isd,niterations
1212 format(a4,i4,2x,f5.3,f11.1,2x,a22,a1,i5,i5,i5,i5)
endif
enddo ! sequence estimation
enddo !candidate list
enddo !files
write(*,1120)
1120 format("<DecodeFinished>")
999 end program ft4d
subroutine getbitmetric(ib,ps,ns,xmet)
real ps(0:ns-1)
xm1=0
xm0=0
do i=0,ns-1
if( iand(i/ib,1) .eq. 1 .and. ps(i) .gt. xm1 ) xm1=ps(i)
if( iand(i/ib,1) .eq. 0 .and. ps(i) .gt. xm0 ) xm0=ps(i)
enddo
xmet=xm1-xm0
return
end subroutine getbitmetric
subroutine downsample4(ci,f0,co)
parameter(NI=144*160,NH=NI/2,NO=NI/16) ! downsample from 200 samples per symbol to 10
complex ci(0:NI-1),ct(0:NI-1)
complex co(0:NO-1)
fs=12000.0
df=fs/NI
ct=ci
call four2a(ct,NI,1,-1,1) !c2c FFT to freq domain
i0=nint(f0/df)
ct=cshift(ct,i0)
co=0.0
co(0)=ct(0)
b=8.0
do i=1,NO/2
arg=(i*df/b)**2
filt=exp(-arg)
co(i)=ct(i)*filt
co(NO-i)=ct(NI-i)*filt
enddo
co=co/NO
call four2a(co,NO,1,1,1) !c2c FFT back to time domain
return
end subroutine downsample4
subroutine ft4_downsample(iwave,f0,c)
! Input: i*2 data in iwave() at sample rate 12000 Hz
! Output: Complex data in c(), sampled at 1200 Hz
include 'ft4_params.f90'
parameter (NFFT2=NMAX/16)
integer*2 iwave(NMAX)
complex c(0:NMAX/16-1)
complex c1(0:NFFT2-1)
complex cx(0:NMAX/2)
real x(NMAX)
equivalence (x,cx)
BW=6.0*75
df=12000.0/NMAX
x=iwave
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
ibw=nint(BW/df)
i0=nint(f0/df)
c1=0.
c1(0)=cx(i0)
do i=1,NFFT2/2
arg=(i-1)*df/bw
win=exp(-arg*arg)
c1(i)=cx(i0+i)*win
c1(NFFT2-i)=cx(i0-i)*win
enddo
c1=c1/NFFT2
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
c=c1(0:NMAX/16-1)
return
end subroutine ft4_downsample

86
lib/fsk4hf/genft2.f90 Normal file
View File

@ -0,0 +1,86 @@
subroutine genft2(msg0,ichk,msgsent,i4tone,itype)
! s8 + 48bits + s8 + 80 bits = 144 bits (72ms message duration)
!
! Encode an MSK144 message
! Input:
! - msg0 requested message to be transmitted
! - ichk if ichk=1, return only msgsent
! if ichk.ge.10000, set imsg=ichk-10000 for short msg
! - msgsent message as it will be decoded
! - i4tone array of audio tone values, 0 or 1
! - itype message type
! 1 = 77 bit message
! 7 = 16 bit message "<Call_1 Call2> Rpt"
use iso_c_binding, only: c_loc,c_size_t
use packjt77
character*37 msg0
character*37 message !Message to be generated
character*37 msgsent !Message as it will be received
character*77 c77
integer*4 i4tone(144)
integer*1 codeword(128)
integer*1 msgbits(77)
integer*1 bitseq(144) !Tone #s, data and sync (values 0-1)
integer*1 s16(16)
real*8 xi(864),xq(864),pi,twopi
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
equivalence (ihash,i1hash)
logical unpk77_success
nsym=128
pi=4.0*atan(1.0)
twopi=8.*atan(1.0)
message(1:37)=' '
itype=1
if(msg0(1:1).eq.'@') then !Generate a fixed tone
read(msg0(2:5),*,end=1,err=1) nfreq !at specified frequency
go to 2
1 nfreq=1000
2 i4tone(1)=nfreq
else
message=msg0
do i=1, 37
if(ichar(message(i:i)).eq.0) then
message(i:37)=' '
exit
endif
enddo
do i=1,37 !Strip leading blanks
if(message(1:1).ne.' ') exit
message=message(i+1:)
enddo
if(message(1:1).eq.'<') then
i2=index(message,'>')
i1=0
if(i2.gt.0) i1=index(message(1:i2),' ')
if(i1.gt.0) then
call genmsk40(message,msgsent,ichk,i4tone,itype)
if(itype.lt.0) go to 999
i4tone(41)=-40
go to 999
endif
endif
i3=-1
n3=-1
call pack77(message,i3,n3,c77)
call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent
if(ichk.eq.1) go to 999
read(c77,"(77i1)") msgbits
call encode_128_90(msgbits,codeword)
!Create 144-bit channel vector:
bitseq=0
bitseq(1:16)=s16
bitseq(17:144)=codeword
i4tone=bitseq
endif
999 return
end subroutine genft2

View File

@ -0,0 +1,63 @@
subroutine getcandidates2(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
ncand,sbase)
! For now, hardwired to find the largest peak in the average spectrum
include 'ft2_params.f90'
real s(NH1,NHSYM)
real savg(NH1),savsm(NH1)
real sbase(NH1)
real x(NFFT1)
complex cx(0:NH1)
real candidate(3,maxcand)
integer*2 id(NMAX)
integer*1 s8(8)
integer indx(NH1)
data s8/0,1,1,1,0,0,1,0/
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*id(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
savsm=0.
do i=2,NH1-1
savsm(i)=sum(savg(i-1:i+1))/3.
enddo
nfa=fa/df
nfb=fb/df
np=nfb-nfa+1
indx=0
call indexx(savsm(nfa:nfb),np,indx)
xn=savsm(nfa+indx(nint(0.3*np)))
savsm=savsm/xn
imax=-1
xmax=-99.
do i=2,NH1-1
if(savsm(i).gt.savsm(i-1).and. &
savsm(i).gt.savsm(i+1).and. &
savsm(i).gt.xmax) then
xmax=savsm(i)
imax=i
endif
enddo
f0=imax*df
if(xmax.gt.1.2) then
ncand=ncand+1
candidate(1,ncand)=f0
endif
return
end subroutine getcandidates2

View File

@ -1,194 +0,0 @@
program msksim
! Simulate characteristics of a potential "MSK10" mode using LDPC (168,84)
! code, OQPDK modulation, and 30 s T/R sequences.
! Reception and Demodulation algorithm:
! 1. Compute coarse spectrum; find fc1 = approx carrier freq
! 2. Mix from fc1 to 0; LPF at +/- 0.75*R
! 3. Square, FFT; find peaks near -R/2 and +R/2 to get fc2
! 4. Mix from fc2 to 0
! 5. Fit cb13 (central part of csync) to c -> lag, phase
! 6. Fit complex ploynomial for channel equalization
! 7. Get soft bits from equalized data
parameter (KK=84) !Information bits (72 + CRC12)
parameter (ND=168) !Data symbols: LDPC (168,84), r=1/2
parameter (NS=65) !Sync symbols (2 x 26 + Barker 13)
parameter (NR=3) !Ramp up/down
parameter (NN=NR+NS+ND) !Total symbols (236)
parameter (NSPS=1152/72) !Samples per MSK symbol (16)
parameter (N2=2*NSPS) !Samples per OQPSK symbol (32)
parameter (N13=13*N2) !Samples in central sync vector (416)
parameter (NZ=NSPS*NN) !Samples in baseband waveform (3776)
parameter (NFFT1=4*NSPS,NH1=NFFT1/2)
character*8 arg
complex cbb(0:NZ-1) !Complex baseband waveform
complex csync(0:NZ-1) !Sync symbols only, from cbb
complex cb13(0:N13-1) !Barker 13 waveform
complex c(0:NZ-1) !Complex waveform
complex c0(0:NZ-1) !Complex waveform
complex zz(NS+ND) !Complex symbol values (intermediate)
complex z
real xnoise(0:NZ-1) !Generated random noise
real ynoise(0:NZ-1) !Generated random noise
real rxdata(ND),llr(ND) !Soft symbols
real pp(2*NSPS) !Shaped pulse for OQPSK
real a(5) !For twkfreq1
real aa(20),bb(20) !Fitted polyco's
integer id(NS+ND) !NRZ values (+/-1) for Sync and Data
integer ierror(NS+ND)
integer icw(NN)
integer*1 msgbits(KK),decoded(KK),apmask(ND),cw(ND)
! integer*1 codeword(ND)
data msgbits/0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0,0,1, &
1,1,1,0,1,1,1,1,1,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,0,1,1,0,1,1, &
1,1,0,1,0,1,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0/
nargs=iargc()
if(nargs.ne.6) then
print*,'Usage: mskhfsim f0(Hz) delay(ms) fspread(Hz) maxn iters snr(dB)'
print*,'Example: mskhfsim 0 0 0 5 10 -20'
print*,'Set snr=0 to cycle through a range'
go to 999
endif
call getarg(1,arg)
read(arg,*) f0 !Generated carrier frequency
call getarg(2,arg)
read(arg,*) delay !Delta_t (ms) for Watterson model
call getarg(3,arg)
read(arg,*) fspread !Fspread (Hz) for Watterson model
call getarg(4,arg)
read(arg,*) maxn !Max nterms for polyfit
call getarg(5,arg)
read(arg,*) iters !Iterations at each SNR
call getarg(6,arg)
read(arg,*) snrdb !Specified SNR_2500
twopi=8.0*atan(1.0)
fs=12000.0/72.0 !Sample rate = 166.6666667 Hz
dt=1.0/fs !Sample interval (s)
tt=NSPS*dt !Duration of "itone" symbols (s)
ts=2*NSPS*dt !Duration of OQPSK symbols (s)
baud=1.0/tt !Keying rate for "itone" symbols (baud)
txt=NZ*dt !Transmission length (s)
bandwidth_ratio=2500.0/(fs/2.0)
write(*,1000) f0,delay,fspread,maxn,iters,baud,3*baud,txt
1000 format('f0:',f5.1,' Delay:',f4.1,' fSpread:',f5.2,' maxn:',i3, &
' Iters:',i6/'Baud:',f7.3,' BW:',f5.1,' TxT:',f5.1,f5.2/)
write(*,1004)
1004 format(/' SNR err ber fer fsigma'/37('-'))
do i=1,N2 !Half-sine pulse shape
pp(i)=sin(0.5*(i-1)*twopi/(2*NSPS))
enddo
call genmskhf(msgbits,id,icw,cbb,csync)!Generate baseband waveform and csync
cb13=csync(1680:2095) !Copy the Barker 13 waveform
a=0.
a(1)=f0
call twkfreq1(cbb,NZ,fs,a,cbb) !Mix to specified frequency
isna=-10
isnb=-30
if(snrdb.ne.0.0) then
isna=nint(snrdb)
isnb=isna
endif
do isnr=isna,isnb,-1 !Loop over SNR range
snrdb=isnr
sig=sqrt(bandwidth_ratio) * 10.0**(0.05*snrdb)
if(snrdb.gt.90.0) sig=1.0
nhard=0
nhardsync=0
nfe=0
sqf=0.
do iter=1,iters !Loop over requested iterations
c=cbb
if(delay.ne.0.0 .or. fspread.ne.0.0) then
call watterson(c,NZ,fs,delay,fspread)
endif
c=sig*c !Scale to requested SNR
if(snrdb.lt.90) then
do i=0,NZ-1 !Generate gaussian noise
xnoise(i)=gran()
ynoise(i)=gran()
enddo
c=c + cmplx(xnoise,ynoise) !Add AWGN noise
endif
call getfc1(c,fc1) !First approx for freq
call getfc2(c,csync,fc1,fc2,fc3) !Refined freq
sqf=sqf + (fc1+fc2-f0)**2
!NB: Measured performance is about equally good using fc2 or fc3 here:
a(1)=-(fc1+fc2)
a(2:5)=0.
call twkfreq1(c,NZ,fs,a,c) !Mix c down by fc1+fc2
! The following may not be necessary?
! z=sum(c(1680:2095)*cb13)/208.0 !Get phase from Barker 13 vector
! z0=z/abs(z)
! c=c*conjg(z0)
!---------------------------------------------------------------- DT
! Not presently used:
amax=0.
jpk=0
do j=-20*NSPS,20*NSPS !Get jpk
z=sum(c(1680+j:2095+j)*cb13)/208.0
if(abs(z).gt.amax) then
amax=abs(z)
jpk=j
endif
enddo
xdt=jpk/fs
nterms=maxn
c0=c
do itry=1,10
idf=itry/2
if(mod(itry,2).eq.0) idf=-idf
nhard0=0
nhardsync0=0
ifer=1
a(1)=idf*0.01
a(2:5)=0.
call twkfreq1(c0,NZ,fs,a,c) !Mix c0 into c
call cpolyfit(c,pp,id,maxn,aa,bb,zz,nhs)
call msksoftsym(zz,aa,bb,id,nterms,ierror,rxdata,nhard0,nhardsync0)
if(nhardsync0.gt.12) cycle
rxav=sum(rxdata)/ND
rx2av=sum(rxdata*rxdata)/ND
rxsig=sqrt(rx2av-rxav*rxav)
rxdata=rxdata/rxsig
ss=0.84
llr=2.0*rxdata/(ss*ss)
apmask=0
max_iterations=40
ifer=0
call bpdecode168(llr,apmask,max_iterations,decoded,niterations,cw)
nbadcrc=0
if(niterations.ge.0) call chkcrc12(decoded,nbadcrc)
if(niterations.lt.0 .or. count(msgbits.ne.decoded).gt.0 .or. &
nbadcrc.ne.0) ifer=1
! if(ifer.eq.0) write(67,1301) snrdb,itry,idf,niterations, &
! nhardsync0,nhard0
!1301 format(f6.1,5i6)
if(ifer.eq.0) exit
enddo !Freq dither loop
nhard=nhard+nhard0
nhardsync=nharsdync+nhardsync0
nfe=nfe+ifer
enddo
fsigma=sqrt(sqf/iters)
ber=float(nhard)/((NS+ND)*iters)
fer=float(nfe)/iters
write(*,1050) snrdb,nhard,ber,fer,fsigma
! write(60,1050) snrdb,nhard,ber,fer,fsigma
1050 format(f6.1,i7,f8.4,f7.3,f8.2)
enddo
999 end program msksim

View File

@ -70,14 +70,14 @@ endfunction
# M-ary PSK Block Coded Modulation," Igal Sason and Gil Weichman,
# doi: 10.1109/EEEI.2006.321097
#-------------------------------------------------------------------------------
N=174
K=75
N=128
K=90
R=K/N
delta=0.01;
[ths,fval,info,output]=fzero(@f1,[delta,pi/2-delta], optimset ("jacobian", "off"));
for ebnodb=-6:0.5:4
for ebnodb=-3:0.5:4
ebno=10^(ebnodb/10.0);
esno=ebno*R;
A=sqrt(2*esno);

19
lib/fsk4hf/spb_128_90.dat Normal file
View File

@ -0,0 +1,19 @@
N = 128
K = 90
R = 0.70312
-3.000000 0.000341
-2.500000 0.001513
-2.000000 0.006049
-1.500000 0.021280
-1.000000 0.064283
-0.500000 0.162755
0.000000 0.338430
0.500000 0.571867
1.000000 0.791634
1.500000 0.930284
2.000000 0.985385
2.500000 0.998258
3.000000 0.999893
3.500000 0.999997
4.000000 1.000000

6
lib/ft2/cdatetime.f90 Normal file
View File

@ -0,0 +1,6 @@
character*17 function cdatetime()
character cdate*8,ctime*10
call date_and_time(cdate,ctime)
cdatetime=cdate(3:8)//'_'//ctime
return
end function cdatetime

279
lib/ft2/ft2.f90 Normal file
View File

@ -0,0 +1,279 @@
program ft2
use packjt77
include 'gcom1.f90'
integer ft2audio,ptt
logical allok
character*20 pttport
character*8 arg
character*80 fname
integer*2 id2(30000)
open(12,file='all_ft2.txt',status='unknown',position='append')
nargs=iargc()
if(nargs.eq.1) then
call getarg(1,fname)
open(10,file=fname,status='old',access='stream')
read(10) id2(1:22) !Read (and ignore) the header
read(10) id2 !Read the Rx data
close(10)
call ft2_decode(fname(1:17),nfqso,id2,ndecodes,mycall,hiscall,nrx)
go to 999
endif
allok=.true.
! Get home-station details
open(10,file='ft2.ini',status='old',err=1)
go to 2
1 print*,'Cannot open ft2.ini'
allok=.false.
2 read(10,*,err=3) mycall,mygrid,ndevin,ndevout,pttport,exch
go to 4
3 print*,'Error reading ft2.ini'
allok=.false.
4 if(index(pttport,'/').lt.1) read(pttport,*) nport
hiscall=' '
hiscall_next=' '
idevin=ndevin
idevout=ndevout
call padevsub(idevin,idevout)
if(idevin.ne.ndevin .or. idevout.ne.ndevout) allok=.false.
i1=0
i1=ptt(nport,1,1,iptt)
i1=ptt(nport,1,0,iptt)
if(i1.lt.0 .and. nport.ne.0) allok=.false.
if(.not.allok) then
write(*,"('Please fix setup error(s) and restart.')")
go to 999
endif
nright=1
iwrite=0
iwave=0
nwave=NTZ
nfsample=12000
ngo=1
npabuf=1152
ntxok=0
ntransmitting=0
tx_once=.false.
snrdb=99.0
txmsg='CQ K1JT FN20'
ltx=.false.
lrx=.false.
autoseq=.false.
QSO_in_progress=.false.
ntxed=0
if(nargs.eq.3) then
call getarg(1,txmsg)
call getarg(2,arg)
read(arg,*) f0
call getarg(3,arg)
read(arg,*) snrdb
tx_once=.true.
ftx=1500.0
call transmit(-1,ftx,iptt)
snrdb=99.0
endif
! Start the audio streams
ierr=ft2audio(idevin,idevout,npabuf,nright,y1,y2,NRING,iwrite,itx, &
iwave,nwave+3*1152,nfsample,nTxOK,nTransmitting,ngo)
if(ierr.ne.0) then
print*,'Error',ierr,' starting audio input and/or output.'
endif
999 end program ft2
subroutine update(total_time,ic1,ic2)
use wavhdr
type(hdr) h
real*8 total_time
integer*8 count0,count1,clkfreq
integer ptt
integer*2 id(30000)
logical transmitted,level,ok
character*70 line
character cdatetime*17,fname*17,mode*8,band*6
include 'gcom1.f90'
data nt0/-1/,transmitted/.false./,snr/-99.0/
data level/.false./
save nt0,transmitted,level,snr,iptt
if(ic1.ne.0 .or. ic2.ne.0) then
if(ic1.eq.27 .and. ic2.eq.0) ngo=0 !ESC
if(nTxOK.eq.0 .and. ntransmitting.eq.0) then
nfunc=0
if(ic1.eq.0 .and. ic2.eq.59) nfunc=1 !F1
if(ic1.eq.0 .and. ic2.eq.60) nfunc=2 !F2
if(ic1.eq.0 .and. ic2.eq.61) nfunc=3 !F3
if(ic1.eq.0 .and. ic2.eq.62) nfunc=4 !F4
if(ic1.eq.0 .and. ic2.eq.63) nfunc=5 !F5
if(nfunc.eq.1 .or. (nfunc.ge.2 .and. hiscall.ne.' ')) then
ftx=1500.0
call transmit(nfunc,ftx,iptt)
endif
endif
if(ic1.eq.13 .and. ic2.eq.0) hiscall=hiscall_next
if((ic1.eq.97 .or. ic1.eq.65) .and. ic2.eq.0) autoseq=.not.autoseq
if((ic1.eq.108 .or. ic1.eq.76) .and. ic2.eq.0) level=.not.level
endif
if(ntransmitting.eq.1) transmitted=.true.
if(transmitted .and. ntransmitting.eq.0) then
i1=0
if(iptt.eq.1 .and. nport.gt.0) i1=ptt(nport,0,1,iptt)
if(tx_once .and. transmitted) stop
transmitted=.false.
endif
nt=2*total_time
if(nt.gt.nt0 .or. ic1.ne.0 .or. ic2.ne.0) then
if(level) then
! Measure and display the average level of signal plus noise in past 0.5 s
k=iwrite-6000
if(k.lt.1) k=k+NRING
sq=0.
do i=1,6000
k=k+1
if(k.gt.NRING) k=k-NRING
x=y1(k)
sq=sq + x*x
enddo
sigdb=0.
if(sq.gt.0.0) sigdb=db(sq/6000.0)
n=sigdb
if(n.lt.1) n=1
if(n.gt.70) n=70
line=' '
line(n:n)='*'
write(*,1030) sigdb,ntxed,autoseq,QSO_in_progress,(line(i:i),i=1,n)
1030 format(f4.1,i3,2L2,1x,70a1)
! write(*,1020) nt,total_time,iwrite,itx,ntxok,ntransmitting,ndecodes, &
! snr,sigdb,line
!1020 format(i6,f9.3,i10,i6,3i3,f6.0,f6.1,1x,a30)
endif
k=iwrite-30000
if(k.lt.1) k=k+NRING
do i=1,30000
k=k+1
if(k.gt.NRING) k=k-NRING
id(i)=y1(k)
enddo
nutc=0
nfqso=1500
ndecodes=0
if(maxval(abs(id)).gt.0) then
call system_clock(count0,clkfreq)
nrx=-1
call ft2_decode(cdatetime(),nfqso,id,ndecodes,mycall,hiscall,nrx)
call system_clock(count1,clkfreq)
! tdecode=float(count1-count0)/float(clkfreq)
if(ndecodes.ge.1) then
fMHz=7.074
mode='FT2'
nsubmode=1
ntrperiod=0
h=default_header(12000,30000)
k=0
do i=1,250
sq=0
do n=1,120
k=k+1
x=id(k)
sq=sq + x*x
enddo
write(43,3043) i,0.01*i,1.e-4*sq
3043 format(i7,f12.6,f12.3)
enddo
call set_wsjtx_wav_params(fMHz,mode,nsubmode,ntrperiod,id)
band=""
mode=""
nsubmode=-1
ntrperiod=-1
call get_wsjtx_wav_params(id,band,mode,nsubmode,ntrperiod,ok)
! write(*,1010) band,ntrperiod,mode,char(ichar('A')-1+id(3))
!1010 format('Band: ',a6,' T/R period:',i4,' Mode: ',a8,1x,a1)
fname=cdatetime()
fname(14:17)='.wav'
open(13,file=fname,status='unknown',access='stream')
write(13) h,id
close(13)
endif
if(autoseq .and.nrx.eq.2) QSO_in_progress=.true.
if(autoseq .and. QSO_in_progress .and. nrx.ge.1 .and. nrx.le.4) then
lrx(nrx)=.true.
ftx=1500.0
if(ntxed.eq.1) then
if(nrx.eq.2) then
call transmit(3,ftx,iptt)
else
call transmit(1,ftx,iptt)
endif
endif
if(ntxed.eq.2) then
if(nrx.eq.3) then
call transmit(4,ftx,iptt)
QSO_in_progress=.false.
write(*,1032)
1032 format('QSO complete: S+P side')
else
call transmit(2,ftx,iptt)
endif
endif
if(ntxed.eq.3) then
if(nrx.eq.4) then
QSO_in_progress=.false.
write(*,1034)
1034 format('QSO complete: CQ side')
else
call transmit(3,ftx,iptt)
endif
endif
endif
endif
nt0=nt
endif
return
end subroutine update
character*17 function cdatetime()
character cdate*8,ctime*10
call date_and_time(cdate,ctime)
cdatetime=cdate(3:8)//'_'//ctime
return
end function cdatetime
subroutine transmit(nfunc,ftx,iptt)
include 'gcom1.f90'
character*17 cdatetime
integer ptt
if(nTxOK.eq.1) return
if(nfunc.eq.1) txmsg='CQ '//trim(mycall)//' '//mygrid
if(nfunc.eq.2) txmsg=trim(hiscall)//' '//trim(mycall)// &
' 559 '//trim(exch)
if(nfunc.eq.3) txmsg=trim(hiscall)//' '//trim(mycall)// &
' R 559 '//trim(exch)
if(nfunc.eq.4) txmsg=trim(hiscall)//' '//trim(mycall)//' RR73'
if(nfunc.eq.5) txmsg='TNX 73 GL'
call ft2_iwave(txmsg,ftx,snrdb,iwave)
iwave(23041:)=0
i1=ptt(nport,1,1,iptt)
ntxok=1
n=len(trim(txmsg))
write(*,1010) cdatetime(),0,0.0,nint(ftx),(txmsg(i:i),i=1,n)
write(12,1010) cdatetime(),0,0.0,nint(ftx),(txmsg(i:i),i=1,n)
1010 format(a17,i4,f6.2,i5,' Tx ',37a1)
if(nfunc.ge.1 .and. nfunc.le.4) ntxed=nfunc
if(nfunc.ge.1 .and. nfunc.le.5) ltx(nfunc)=.true.
if(nfunc.eq.2 .or. nfunc.eq.3) QSO_in_progress=.true.
return
end subroutine transmit

2
lib/ft2/ft2.ini Normal file
View File

@ -0,0 +1,2 @@
K1JT FN20 1 5 0 NJ
MyCall MyGrid AudioIn AudioOut PTTport Exch

298
lib/ft2/ft2_decode.f90 Normal file
View File

@ -0,0 +1,298 @@
subroutine ft2_decode(cdatetime0,nfqso,iwave,ndecodes,mycall,hiscall,nrx,line)
use crc
use packjt77
include 'ft2_params.f90'
character message*37,c77*77
character*61 line
character*37 decodes(100)
character*120 data_dir
character*17 cdatetime0,cdatetime
character*6 mycall,hiscall,hhmmss
complex c2(0:NMAX/16-1) !Complex waveform
complex cb(0:NMAX/16-1)
complex cd(0:144*10-1) !Complex waveform
complex c1(0:9),c0(0:9)
complex ccor(0:1,144)
complex csum,cterm,cc0,cc1,csync1
real*8 fMHz
real a(5)
real rxdata(128),llr(128) !Soft symbols
real llr2(128)
real sbits(144),sbits1(144),sbits3(144)
real ps(0:8191),psbest(0:8191)
real candidate(3,100)
real savg(NH1)
integer*2 iwave(NMAX) !Generated full-length waveform
integer*1 message77(77),apmask(128),cw(128)
integer*1 hbits(144),hbits1(144),hbits3(144)
integer*1 s16(16)
logical unpk77_success
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
hhmmss=cdatetime0(8:13)
fs=12000.0/NDOWN !Sample rate
dt=1/fs !Sample interval after downsample (s)
tt=NSPS*dt !Duration of "itone" symbols (s)
baud=1.0/tt !Keying rate for "itone" symbols (baud)
txt=NZ*dt !Transmission length (s)
twopi=8.0*atan(1.0)
h=0.8 !h=0.8 seems to be optimum for AWGN sensitivity (not for fading)
dphi=twopi/2*baud*h*dt*16 ! dt*16 is samp interval after downsample
dphi0=-1*dphi
dphi1=+1*dphi
phi0=0.0
phi1=0.0
do i=0,9
c1(i)=cmplx(cos(phi1),sin(phi1))
c0(i)=cmplx(cos(phi0),sin(phi0))
phi1=mod(phi1+dphi1,twopi)
phi0=mod(phi0+dphi0,twopi)
enddo
the=twopi*h/2.0
cc1=cmplx(cos(the),-sin(the))
cc0=cmplx(cos(the),sin(the))
data_dir="."
fMHz=7.074
ncoh=1
candidate=0.0
ncand=0
fa=375.0
fb=3000.0
syncmin=0.2
maxcand=100
nfqso=-1
call getcandidates2a(iwave,fa,fb,maxcand,savg,candidate,ncand)
ndecodes=0
do icand=1,ncand
f0=candidate(1,icand)
if( f0.le.375.0 .or. f0.ge.(5000.0-375.0) ) cycle
call ft2_downsample(iwave,f0,c2) ! downsample from 160s/Symbol to 10s/Symbol
! 750 samples/second here
ibest=-1
sybest=-99.
dfbest=-1.
!### do if=-15,+15
do if=-30,30
df=if
a=0.
a(1)=-df
call twkfreq1(c2,NMAX/16,fs,a,cb)
do is=0,374 !DT search range is 0 - 0.5 s
csync1=0.
cterm=1
do ib=1,16
i1=(ib-1)*10+is
i2=i1+136*10
if(s16(ib).eq.1) then
csync1=csync1+sum(cb(i1:i1+9)*conjg(c1(0:9)))*cterm
cterm=cterm*cc1
else
csync1=csync1+sum(cb(i1:i1+9)*conjg(c0(0:9)))*cterm
cterm=cterm*cc0
endif
enddo
if(abs(csync1).gt.sybest) then
ibest=is
sybest=abs(csync1)
dfbest=df
endif
enddo
enddo
a=0.
a(1)=-dfbest
call twkfreq1(c2,NMAX/16,fs,a,cb)
ib=ibest
cd=cb(ib:ib+144*10-1)
s2=sum(real(cd*conjg(cd)))/(10*144)
cd=cd/sqrt(s2)
do nseq=1,5
if( nseq.eq.1 ) then ! noncoherent single-symbol detection
sbits1=0.0
do ibit=1,144
ib=(ibit-1)*10
ccor(1,ibit)=sum(cd(ib:ib+9)*conjg(c1(0:9)))
ccor(0,ibit)=sum(cd(ib:ib+9)*conjg(c0(0:9)))
sbits1(ibit)=abs(ccor(1,ibit))-abs(ccor(0,ibit))
hbits1(ibit)=0
if(sbits1(ibit).gt.0) hbits1(ibit)=1
enddo
sbits=sbits1
hbits=hbits1
sbits3=sbits1
hbits3=hbits1
elseif( nseq.ge.2 ) then
nbit=2*nseq-1
numseq=2**(nbit)
ps=0
do ibit=nbit/2+1,144-nbit/2
ps=0.0
pmax=0.0
do iseq=0,numseq-1
csum=0.0
cterm=1.0
k=1
do i=nbit-1,0,-1
ibb=iand(iseq/(2**i),1)
csum=csum+ccor(ibb,ibit-(nbit/2+1)+k)*cterm
if(ibb.eq.0) cterm=cterm*cc0
if(ibb.eq.1) cterm=cterm*cc1
k=k+1
enddo
ps(iseq)=abs(csum)
if( ps(iseq) .gt. pmax ) then
pmax=ps(iseq)
ibflag=1
endif
enddo
if( ibflag .eq. 1 ) then
psbest=ps
ibflag=0
endif
call getbitmetric(2**(nbit/2),psbest,numseq,sbits3(ibit))
hbits3(ibit)=0
if(sbits3(ibit).gt.0) hbits3(ibit)=1
enddo
sbits=sbits3
hbits=hbits3
endif
nsync_qual=count(hbits(1:16).eq.s16)
if(nsync_qual.lt.10) exit
rxdata=sbits(17:144)
rxav=sum(rxdata(1:128))/128.0
rx2av=sum(rxdata(1:128)*rxdata(1:128))/128.0
rxsig=sqrt(rx2av-rxav*rxav)
rxdata=rxdata/rxsig
sigma=0.80
llr(1:128)=2*rxdata/(sigma*sigma)
apmask=0
max_iterations=40
do ibias=0,0
llr2=llr
if(ibias.eq.1) llr2=llr+0.4
if(ibias.eq.2) llr2=llr-0.4
call bpdecode128_90(llr2,apmask,max_iterations,message77,cw,nharderror,niterations)
if(nharderror.ge.0) exit
enddo
nhardmin=-1
if(sum(message77).eq.0) cycle
if( nharderror.ge.0 ) then
write(c77,'(77i1)') message77(1:77)
call unpack77(c77,nrx,message,unpk77_success)
idupe=0
do i=1,ndecodes
if(decodes(i).eq.message) idupe=1
enddo
if(idupe.eq.1) exit
ndecodes=ndecodes+1
decodes(ndecodes)=message
xsnr=db(sybest*sybest) - 115.0 !### Rough estimate of S/N ###
nsnr=nint(xsnr)
freq=f0+dfbest
write(line,1000) hhmmss,nsnr,ibest/750.0,nint(freq),message
1000 format(a6,i4,f5.2,i5,' + ',1x,a37)
open(24,file='all_ft2.txt',status='unknown',position='append')
write(24,1002) cdatetime0,nsnr,ibest/750.0,nint(freq),message, &
nseq,nharderror,nhardmin
if(hhmmss.eq.' ') write(*,1002) cdatetime0,nsnr, &
ibest/750.0,nint(freq),message,nseq,nharderror,nhardmin
1002 format(a17,i4,f6.2,i5,' Rx ',a37,3i5)
close(24)
!### Temporary: assume most recent decoded message conveys "hiscall".
i0=index(message,' ')
if(i0.ge.3 .and. i0.le.7) then
hiscall=message(i0+1:i0+6)
i1=index(hiscall,' ')
if(i1.gt.0) hiscall=hiscall(1:i1)
endif
nrx=-1
if(index(message,'CQ ').eq.1) nrx=1
if((index(message,trim(mycall)//' ').eq.1) .and. &
(index(message,' '//trim(hiscall)//' ').ge.4)) then
if(index(message,' 559 ').gt.8) nrx=2
if(index(message,' R 559 ').gt.8) nrx=3
if(index(message,' RR73 ').gt.8) nrx=4
endif
!###
exit
endif
enddo ! nseq
enddo !candidate list
return
end subroutine ft2_decode
subroutine getbitmetric(ib,ps,ns,xmet)
real ps(0:ns-1)
xm1=0
xm0=0
do i=0,ns-1
if( iand(i/ib,1) .eq. 1 .and. ps(i) .gt. xm1 ) xm1=ps(i)
if( iand(i/ib,1) .eq. 0 .and. ps(i) .gt. xm0 ) xm0=ps(i)
enddo
xmet=xm1-xm0
return
end subroutine getbitmetric
subroutine downsample2(ci,f0,co)
parameter(NI=144*160,NH=NI/2,NO=NI/16) ! downsample from 200 samples per symbol to 10
complex ci(0:NI-1),ct(0:NI-1)
complex co(0:NO-1)
fs=12000.0
df=fs/NI
ct=ci
call four2a(ct,NI,1,-1,1) !c2c FFT to freq domain
i0=nint(f0/df)
ct=cshift(ct,i0)
co=0.0
co(0)=ct(0)
b=8.0
do i=1,NO/2
arg=(i*df/b)**2
filt=exp(-arg)
co(i)=ct(i)*filt
co(NO-i)=ct(NI-i)*filt
enddo
co=co/NO
call four2a(co,NO,1,1,1) !c2c FFT back to time domain
return
end subroutine downsample2
subroutine ft2_downsample(iwave,f0,c)
! Input: i*2 data in iwave() at sample rate 12000 Hz
! Output: Complex data in c(), sampled at 1200 Hz
include 'ft2_params.f90'
parameter (NFFT2=NMAX/16)
integer*2 iwave(NMAX)
complex c(0:NMAX/16-1)
complex c1(0:NFFT2-1)
complex cx(0:NMAX/2)
real x(NMAX)
equivalence (x,cx)
BW=4.0*75
df=12000.0/NMAX
x=iwave
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
ibw=nint(BW/df)
i0=nint(f0/df)
c1=0.
c1(0)=cx(i0)
do i=1,NFFT2/2
arg=(i-1)*df/bw
win=exp(-arg*arg)
c1(i)=cx(i0+i)*win
c1(NFFT2-i)=cx(i0-i)*win
enddo
c1=c1/NFFT2
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
c=c1(0:NMAX/16-1)
return
end subroutine ft2_downsample

View File

@ -0,0 +1,88 @@
subroutine ft2_gfsk_iwave(msg37,f0,snrdb,iwave)
! Generate waveform for experimental "FT2" mode
use packjt77
include 'ft2_params.f90' !Set various constants
parameter (NWAVE=(NN+2)*NSPS)
character msg37*37,msgsent37*37
real wave(NWAVE),xnoise(NWAVE)
real dphi(NWAVE)
real pulse(480)
integer itone(NN)
integer*2 iwave(NWAVE) !Generated full-length waveform
logical first
data first/.true./
save pulse
twopi=8.0*atan(1.0)
fs=12000.0 !Sample rate (Hz)
dt=1.0/fs !Sample interval (s)
hmod=0.8 !Modulation index (MSK=0.5, FSK=1.0)
tt=NSPS*dt !Duration of symbols (s)
baud=1.0/tt !Keying rate (baud)
bw=1.5*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
if(first) then
! The filtered frequency pulse
do i=1,480
tt=(i-240.5)/160.0
pulse(i)=gfsk_pulse(1.0,tt)
enddo
dphi_peak=twopi*(hmod/2.0)/real(NSPS)
first=.false.
endif
! Source-encode, then get itone():
itype=1
call genft2(msg37,0,msgsent37,itone,itype)
! Create the instantaneous frequency waveform
dphi=0.0
do j=1,NN
ib=(j-1)*160+1
ie=ib+480-1
dphi(ib:ie)=dphi(ib:ie)+dphi_peak*pulse*(2*itone(j)-1)
enddo
phi=0.0
wave=0.0
sqrt2=sqrt(2.)
dphi=dphi+twopi*f0*dt
do j=1,NWAVE
wave(j)=sqrt2*sin(phi)
sqsig=sqsig + wave(j)**2
phi=mod(phi+dphi(j),twopi)
enddo
wave(1:160)=wave(1:160)*(1.0-cos(twopi*(/(i,i=0,159)/)/320.0) )/2.0
wave(145*160+1:146*160)=wave(145*160+1:146*160)*(1.0+cos(twopi*(/(i,i=0,159)/)/320.0 ))/2.0
wave(146*160+1:)=0.
if(snrdb.gt.90.0) then
iwave=nint((32767.0/sqrt(2.0))*wave)
return
endif
sqnoise=1.e-30
if(snrdb.lt.90) then
do i=1,NWAVE !Add gaussian noise at specified SNR
xnoise(i)=gran() !Noise has rms = 1.0
enddo
endif
xnoise=xnoise*sqrt(0.5*fs/2500.0)
fac=30.0
snr_amplitude=10.0**(0.05*snrdb)
wave=fac*(snr_amplitude*wave + xnoise)
datpk=maxval(abs(wave))
print*,'A',snr_amplitude,datpk
iwave=nint((30000.0/datpk)*wave)
return
end subroutine ft2_gfsk_iwave

64
lib/ft2/ft2_iwave.f90 Normal file
View File

@ -0,0 +1,64 @@
subroutine ft2_iwave(msg37,f0,snrdb,iwave)
! Generate waveform for experimental "FT2" mode
use packjt77
include 'ft2_params.f90' !Set various constants
parameter (NWAVE=NN*NSPS)
character msg37*37,msgsent37*37
real wave(NWAVE),xnoise(NWAVE)
integer itone(NN)
integer*2 iwave(NWAVE) !Generated full-length waveform
twopi=8.0*atan(1.0)
fs=12000.0 !Sample rate (Hz)
dt=1.0/fs !Sample interval (s)
hmod=0.8 !Modulation index (MSK=0.5, FSK=1.0)
tt=NSPS*dt !Duration of symbols (s)
baud=1.0/tt !Keying rate (baud)
bw=1.5*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
! Source-encode, then get itone():
itype=1
call genft2(msg37,0,msgsent37,itone,itype)
k=0
phi=0.0
sqsig=0.
do j=1,NN !Generate real waveform
dphi=twopi*(f0*dt+(hmod/2.0)*(2*itone(j)-1)/real(NSPS))
do i=1,NSPS
k=k+1
wave(k)=sqrt(2.0)*sin(phi) !Signal has rms = 1.0
sqsig=sqsig + wave(k)**2
phi=mod(phi+dphi,twopi)
enddo
enddo
if(snrdb.gt.90.0) then
iwave=nint((32767.0/sqrt(2.0))*wave)
return
endif
sqnoise=1.e-30
if(snrdb.lt.90) then
do i=1,NWAVE !Add gaussian noise at specified SNR
xnoise(i)=gran() !Noise has rms = 1.0
enddo
endif
xnoise=xnoise*sqrt(0.5*fs/2500.0)
fac=30.0
snr_amplitude=10.0**(0.05*snrdb)
wave=fac*(snr_amplitude*wave + xnoise)
datpk=maxval(abs(wave))
print*,'A',snr_amplitude,datpk
iwave=nint((30000.0/datpk)*wave)
return
end subroutine ft2_iwave

12
lib/ft2/ft2_params.f90 Normal file
View File

@ -0,0 +1,12 @@
! LDPC (128,90) code
parameter (KK=90) !Information bits (77 + CRC13)
parameter (ND=128) !Data symbols
parameter (NS=16) !Sync symbols (2x8)
parameter (NN=NS+ND) !Total channel symbols (144)
parameter (NSPS=160) !Samples per symbol at 12000 S/s
parameter (NZ=NSPS*NN) !Samples in full 1.92 s waveform (23040)
parameter (NMAX=30000) !Samples in iwave (2.5*12000)
parameter (NFFT1=400, NH1=NFFT1/2) !Length of FFTs for symbol spectra
parameter (NSTEP=NSPS/4) !Rough time-sync step size
parameter (NHSYM=NMAX/NSTEP-3) !Number of symbol spectra (1/4-sym steps)
parameter (NDOWN=16) !Downsample factor

347
lib/ft2/ft2audio.c Normal file
View File

@ -0,0 +1,347 @@
#include <stdio.h>
#include "portaudio.h"
#include <string.h>
#include <time.h>
int iaa;
int icc;
double total_time=0.0;
// Definition of structure pointing to the audio data
typedef struct
{
int *iwrite;
int *itx;
int *TxOK;
int *Transmitting;
int *nwave;
int *nright;
int nring;
int nfs;
short *y1;
short *y2;
short *iwave;
} paTestData;
// Input callback routine:
static int
SoundIn( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
short *in = (short*)inputBuffer;
unsigned int i;
static int ia=0;
if(*data->Transmitting) return 0;
if(statusFlags!=0) printf("Status flags %d\n",(int)statusFlags);
if((statusFlags&1) == 0) {
//increment buffer pointers only if data available
ia=*data->iwrite;
if(*data->nright==0) { //Use left channel for input
for(i=0; i<framesPerBuffer; i++) {
data->y1[ia] = (*in++);
data->y2[ia] = (*in++);
ia++;
}
} else { //Use right channel
for(i=0; i<framesPerBuffer; i++) {
data->y2[ia] = (*in++);
data->y1[ia] = (*in++);
ia++;
}
}
}
if(ia >= data->nring) ia=0; //Wrap buffer pointer if necessary
*data->iwrite = ia; //Save buffer pointer
iaa=ia;
total_time += (double)framesPerBuffer/12000.0;
// printf("iwrite: %d\n",*data->iwrite);
return 0;
}
// Output callback routine:
static int
SoundOut( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
short *wptr = (short*)outputBuffer;
unsigned int i,n;
static short int n2;
static int ic=0;
static int TxOKz=0;
static clock_t tstart=-1;
static clock_t tend=-1;
static int nsent=0;
// printf("txOK: %d %d\n",TxOKz,*data->TxOK);
if(*data->TxOK && (!TxOKz)) ic=0; //Reset buffer pointer to start Tx
*data->Transmitting=*data->TxOK; //Set the "transmitting" flag
if(*data->TxOK) {
if(!TxOKz) {
// Start of a transmission
tstart=clock();
nsent=0;
// printf("Start Tx\n");
}
TxOKz=*data->TxOK;
for(i=0 ; i < framesPerBuffer; i++ ) {
n2=data->iwave[ic];
*wptr++ = n2; //left
*wptr++ = n2; //right
ic++;
if(ic > *data->nwave) {
*data->TxOK = 0;
*data->Transmitting = 0;
*data->iwrite = 0; //Reset Rx buffer pointer to 0
ic=0;
tend=clock();
double TxT=((double)(tend-tstart))/CLOCKS_PER_SEC;
// printf("End Tx, TxT = %f nSent = %d\n",TxT,nsent);
break;
}
}
nsent += framesPerBuffer;
} else {
memset((void*)outputBuffer, 0, 2*sizeof(short)*framesPerBuffer);
}
*data->itx = icc; //Save buffer pointer
icc=ic;
return 0;
}
/*******************************************************************/
int ft2audio_(int *ndevin, int *ndevout, int *npabuf, int *nright,
short y1[], short y2[], int *nring, int *iwrite,
int *itx, short iwave[], int *nwave, int *nfsample,
int *TxOK, int *Transmitting, int *ngo)
{
paTestData data;
PaStream *instream, *outstream;
PaStreamParameters inputParameters, outputParameters;
// PaStreamInfo *streamInfo;
int nfpb = *npabuf;
int nSampleRate = *nfsample;
int ndevice_in = *ndevin;
int ndevice_out = *ndevout;
double dSampleRate = (double) *nfsample;
PaError err_init, err_open_in, err_open_out, err_start_in, err_start_out;
PaError err = 0;
data.iwrite = iwrite;
data.itx = itx;
data.TxOK = TxOK;
data.Transmitting = Transmitting;
data.y1 = y1;
data.y2 = y2;
data.nring = *nring;
data.nright = nright;
data.nwave = nwave;
data.iwave = iwave;
data.nfs = nSampleRate;
err_init = Pa_Initialize(); // Initialize PortAudio
if(err_init) {
printf("Error initializing PortAudio.\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_init),
err_init);
Pa_Terminate(); // I don't think we need this but...
return(-1);
}
// printf("Opening device %d for input, %d for output...\n",
// ndevice_in,ndevice_out);
inputParameters.device = ndevice_in;
inputParameters.channelCount = 2;
inputParameters.sampleFormat = paInt16;
inputParameters.suggestedLatency = 0.2;
inputParameters.hostApiSpecificStreamInfo = NULL;
// Test if this configuration actually works, so we do not run into an
// ugly assertion
err_open_in = Pa_IsFormatSupported(&inputParameters, NULL, dSampleRate);
if (err_open_in == 0) {
err_open_in = Pa_OpenStream(
&instream, //address of stream
&inputParameters,
NULL,
dSampleRate, //Sample rate
nfpb, //Frames per buffer
paNoFlag,
(PaStreamCallback *)SoundIn, //Callback routine
(void *)&data); //address of data structure
if(err_open_in) { // We should have no error here usually
printf("Error opening input audio stream:\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_in),
err_open_in);
err = 1;
} else {
// printf("Successfully opened audio input.\n");
}
} else {
printf("Error opening input audio stream.\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_in),
err_open_in);
err = 1;
}
outputParameters.device = ndevice_out;
outputParameters.channelCount = 2;
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = 0.2;
outputParameters.hostApiSpecificStreamInfo = NULL;
// Test if this configuration actually works, so we do not run into an
// ugly assertion.
err_open_out = Pa_IsFormatSupported(NULL, &outputParameters, dSampleRate);
if (err_open_out == 0) {
err_open_out = Pa_OpenStream(
&outstream, //address of stream
NULL,
&outputParameters,
dSampleRate, //Sample rate
nfpb, //Frames per buffer
paNoFlag,
(PaStreamCallback *)SoundOut, //Callback routine
(void *)&data); //address of data structure
if(err_open_out) { // We should have no error here usually
printf("Error opening output audio stream!\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_out),
err_open_out);
err += 2;
} else {
// printf("Successfully opened audio output.\n");
}
} else {
printf("Error opening output audio stream.\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_open_out),
err_open_out);
err += 2;
}
// if there was no error in opening both streams start them
if (err == 0) {
err_start_in = Pa_StartStream(instream); //Start input stream
if(err_start_in) {
printf("Error starting input audio stream!\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_start_in),
err_start_in);
err += 4;
}
err_start_out = Pa_StartStream(outstream); //Start output stream
if(err_start_out) {
printf("Error starting output audio stream!\n");
printf("\tErrortext: %s\n\tNumber: %d\n",Pa_GetErrorText(err_start_out),
err_start_out);
err += 8;
}
}
if (err == 0) printf("Audio streams running normally.\n******************************************************************\n");
while( Pa_IsStreamActive(instream) && (*ngo != 0) && (err == 0) ) {
int ic1=0;
int ic2=0;
if(_kbhit()) ic1 = _getch();
if(_kbhit()) ic2 = _getch();
// if(ic1!=0 || ic2!=0) printf("%d %d %d\n",iaa,ic1,ic2);
update_(&total_time,&ic1,&ic2);
Pa_Sleep(100);
}
Pa_AbortStream(instream); // Abort stream
Pa_CloseStream(instream); // Close stream, we're done.
Pa_AbortStream(outstream); // Abort stream
Pa_CloseStream(outstream); // Close stream, we're done.
Pa_Terminate();
return(err);
}
int padevsub_(int *idevin, int *idevout)
{
int numdev,ndefin,ndefout;
int nchin[101], nchout[101];
int i, devIdx;
int numDevices;
const PaDeviceInfo *pdi;
PaError err;
Pa_Initialize();
numDevices = Pa_GetDeviceCount();
numdev = numDevices;
if( numDevices < 0 ) {
err = numDevices;
Pa_Terminate();
return err;
}
if ((devIdx = Pa_GetDefaultInputDevice()) > 0) {
ndefin = devIdx;
} else {
ndefin = 0;
}
if ((devIdx = Pa_GetDefaultOutputDevice()) > 0) {
ndefout = devIdx;
} else {
ndefout = 0;
}
printf("\nAudio Input Output Device Name\n");
printf("Device Channels Channels\n");
printf("------------------------------------------------------------------\n");
for( i=0; i < numDevices; i++ ) {
pdi = Pa_GetDeviceInfo(i);
// if(i == Pa_GetDefaultInputDevice()) ndefin = i;
// if(i == Pa_GetDefaultOutputDevice()) ndefout = i;
nchin[i]=pdi->maxInputChannels;
nchout[i]=pdi->maxOutputChannels;
printf(" %2d %2d %2d %s\n",i,nchin[i],nchout[i],
pdi->name);
}
printf("\nUser requested devices: Input = %2d Output = %2d\n",
*idevin,*idevout);
printf("Default devices: Input = %2d Output = %2d\n",
ndefin,ndefout);
if((*idevin<0) || (*idevin>=numdev)) *idevin=ndefin;
if((*idevout<0) || (*idevout>=numdev)) *idevout=ndefout;
if((*idevin==0) && (*idevout==0)) {
*idevin=ndefin;
*idevout=ndefout;
}
printf("Will open devices: Input = %2d Output = %2d\n",
*idevin,*idevout);
Pa_Terminate();
return 0;
}

7
lib/ft2/g4.cmd Normal file
View File

@ -0,0 +1,7 @@
gcc -c ft2audio.c
gcc -c ptt.c
gfortran -c ../77bit/packjt77.f90
gfortran -c ../wavhdr.f90
gfortran -c ../crc.f90
gfortran -o ft2 -fbounds-check -fno-second-underscore -ffpe-trap=invalid,zero -Wall -Wno-conversion -Wno-character-truncation ft2.f90 ft2_iwave.f90 ft2_decode.f90 getcandidates2.f90 ft2audio.o ptt.o /JTSDK/wsjtx-output/qt55/2.1.0/Release/build/libwsjt_fort.a /JTSDK/wsjtx-output/qt55/2.1.0/Release/build/libwsjt_cxx.a libportaudio.a ../libfftw3f_win.a -lwinmm
rm *.o *.mod

34
lib/ft2/gcom1.f90 Normal file
View File

@ -0,0 +1,34 @@
! Variable Purpose
!---------------------------------------------------------------------------
integer NRING !Length of Rx ring buffer
integer NTZ !Length of Tx waveform in samples
parameter(NRING=230400) !Ring buffer at 12000 samples/sec
parameter(NTZ=23040) !144*160
parameter(NMAX=30000) !2.5*12000
real snrdb
integer ndevin !Device# for audio input
integer ndevout !Device# for audio output
integer iwrite !Pointer to Rx ring buffer
integer itx !Pointer to Tx buffer
integer ngo !Set to 0 to terminate audio streams
integer nTransmitting !Actually transmitting?
integer nTxOK !OK to transmit?
integer nport !COM port for PTT
logical tx_once !Transmit one message, then exit
logical ltx !True if msg i has been transmitted
logical lrx !True if msg i has been received
logical autoseq
logical QSO_in_progress
integer*2 y1 !Ring buffer for audio channel 0
integer*2 y2 !Ring buffer for audio channel 1
integer*2 iwave !Data for Tx audio
character*6 mycall
character*6 hiscall
character*6 hiscall_next
character*4 mygrid
character*3 exch
character*37 txmsg
common/gcom1/snrdb,ndevin,ndevout,iwrite,itx,ngo,nTransmitting,nTxOK,nport, &
ntxed,tx_once,y1(NRING),y2(NRING),iwave(NTZ+3*1152),ltx(5),lrx(5), &
autoseq,QSO_in_progress,mycall,hiscall,hiscall_next,mygrid,exch,txmsg

86
lib/ft2/genft2.f90 Normal file
View File

@ -0,0 +1,86 @@
subroutine genft2(msg0,ichk,msgsent,i4tone,itype)
! s8 + 48bits + s8 + 80 bits = 144 bits (72ms message duration)
!
! Encode an MSK144 message
! Input:
! - msg0 requested message to be transmitted
! - ichk if ichk=1, return only msgsent
! if ichk.ge.10000, set imsg=ichk-10000 for short msg
! - msgsent message as it will be decoded
! - i4tone array of audio tone values, 0 or 1
! - itype message type
! 1 = 77 bit message
! 7 = 16 bit message "<Call_1 Call2> Rpt"
use iso_c_binding, only: c_loc,c_size_t
use packjt77
character*37 msg0
character*37 message !Message to be generated
character*37 msgsent !Message as it will be received
character*77 c77
integer*4 i4tone(144)
integer*1 codeword(128)
integer*1 msgbits(77)
integer*1 bitseq(144) !Tone #s, data and sync (values 0-1)
integer*1 s16(16)
real*8 xi(864),xq(864),pi,twopi
data s16/0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0/
equivalence (ihash,i1hash)
logical unpk77_success
nsym=128
pi=4.0*atan(1.0)
twopi=8.*atan(1.0)
message(1:37)=' '
itype=1
if(msg0(1:1).eq.'@') then !Generate a fixed tone
read(msg0(2:5),*,end=1,err=1) nfreq !at specified frequency
go to 2
1 nfreq=1000
2 i4tone(1)=nfreq
else
message=msg0
do i=1, 37
if(ichar(message(i:i)).eq.0) then
message(i:37)=' '
exit
endif
enddo
do i=1,37 !Strip leading blanks
if(message(1:1).ne.' ') exit
message=message(i+1:)
enddo
if(message(1:1).eq.'<') then
i2=index(message,'>')
i1=0
if(i2.gt.0) i1=index(message(1:i2),' ')
if(i1.gt.0) then
call genmsk40(message,msgsent,ichk,i4tone,itype)
if(itype.lt.0) go to 999
i4tone(41)=-40
go to 999
endif
endif
i3=-1
n3=-1
call pack77(message,i3,n3,c77)
call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent
if(ichk.eq.1) go to 999
read(c77,"(77i1)") msgbits
call encode_128_90(msgbits,codeword)
!Create 144-bit channel vector:
bitseq=0
bitseq(1:16)=s16
bitseq(17:144)=codeword
i4tone=bitseq
endif
999 return
end subroutine genft2

View File

@ -0,0 +1,64 @@
subroutine getcandidates2a(id,fa,fb,maxcand,savg,candidate,ncand)
! For now, hardwired to find the largest peak in the average spectrum
include 'ft2_params.f90'
real s(NH1,NHSYM)
real savg(NH1),savsm(NH1)
real x(NFFT1)
complex cx(0:NH1)
real candidate(3,100)
integer*2 id(NMAX)
integer*1 s8(8)
integer indx(NH1)
data s8/0,1,1,1,0,0,1,0/
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*id(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
savsm=0.
do i=2,NH1-1
savsm(i)=sum(savg(i-1:i+1))/3.
enddo
savsm(1)=savg(1)
savsm(NH1)=savg(NH1)
nfa=nint(fa/df)
nfb=nint(fb/df)
np=nfb-nfa+1
indx=0
call indexx(savsm(nfa:nfb),np,indx)
xn=savsm(nfa+indx(nint(0.3*np)))
if(xn.ne.0) savsm=savsm/xn
imax=-1
xmax=-99.
do i=2,NH1-1
if(savsm(i).gt.savsm(i-1).and. &
savsm(i).gt.savsm(i+1).and. &
savsm(i).gt.xmax) then
xmax=savsm(i)
imax=i
endif
enddo
f0=imax*df
if(xmax.gt.1.2) then
if(ncand.lt.maxcand) ncand=ncand+1
candidate(1,ncand)=f0
endif
return
end subroutine getcandidates2a

6
lib/ft2/gfsk_pulse.f90 Normal file
View File

@ -0,0 +1,6 @@
real function gfsk_pulse(b,t)
pi=4.*atan(1.0)
c=pi*sqrt(2.0/log(2.0))
gfsk_pulse=0.5*(erf(c*b*(t+0.5))-erf(c*b*(t-0.5)))
return
end function gfsk_pulse

BIN
lib/ft2/libportaudio.a Normal file

Binary file not shown.

BIN
lib/ft2/libwsjt_cxx.a Normal file

Binary file not shown.

BIN
lib/ft2/libwsjt_fort.a Normal file

Binary file not shown.

1123
lib/ft2/portaudio.h Normal file

File diff suppressed because it is too large Load Diff

58
lib/ft2/ptt.c Normal file
View File

@ -0,0 +1,58 @@
#include <windows.h>
#include <stdio.h>
int ptt_(int *nport, int *ntx, int *ndtr, int *iptt)
{
static HANDLE hFile;
static int open=0, nhold=0;
char s[10];
int i3,i4,i5,i6,i9,i00;
if(*nport==0) {
*iptt=*ntx;
return(0);
}
nhold=0;
if(*nport>100) nhold=1;
if(*ntx && (!open)) {
sprintf(s,"\\\\.\\COM%d",*nport%100);
hFile=CreateFile(
TEXT(s),
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if(hFile==INVALID_HANDLE_VALUE) {
printf("PTT: Cannot open COM port %d.\n",*nport%100);
return(-1);
}
open=1;
}
if(*ntx && open) {
if(*ndtr)
EscapeCommFunction(hFile,5); //set DTR
else
EscapeCommFunction(hFile,3); //set RTS
*iptt=1;
}
else {
if(*ndtr)
EscapeCommFunction(hFile,6); //clear DTR
else
EscapeCommFunction(hFile,4); //clear RTS
EscapeCommFunction(hFile,9); //clear BREAK
if(nhold==0) {
i00=CloseHandle(hFile);
open=0;
}
*iptt=0;
}
return(0);
}

341
lib/ft2/ptt_unix.c Normal file
View File

@ -0,0 +1,341 @@
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
//#include <sys/ioctl.h>
//#include <linux/ppdev.h>
//#include <linux/parport.h>
//#include <dev/ppbus/ppi.h>
//#include <dev/ppbus/ppbconf.h>
int lp_reset (int fd);
int lp_ptt (int fd, int onoff);
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
#endif
#if (defined(__unix__) || defined(unix)) && !defined(USG)
# include <sys/param.h>
#endif
#include <string.h>
/* parport functions */
int dev_is_parport(int fd);
int ptt_parallel(int fd, int *ntx, int *iptt);
int ptt_serial(int fd, int *ntx, int *iptt);
int fd=-1; /* Used for both serial and parallel */
/*
* ptt_
*
* generic unix PTT routine called from Fortran
*
* Inputs
* unused Unused, to satisfy old windows calling convention
* ptt_port device name serial or parallel
* ntx pointer to fortran command on or off
* iptt pointer to fortran command status on or off
* Returns - non 0 if error
*/
/* Tiny state machine */
#define STATE_PORT_CLOSED 0
#define STATE_PORT_OPEN_PARALLEL 1
#define STATE_PORT_OPEN_SERIAL 2
int
ptt_(int *unused, char *ptt_port, int *ntx, int *ndtr, int *iptt)
{
static int state=0;
char *p;
/* In the very unlikely event of a NULL pointer, just return.
* Yes, I realise this should not be possible in WSJT.
*/
if (ptt_port == NULL) {
*iptt = *ntx;
return (0);
}
switch (state) {
case STATE_PORT_CLOSED:
/* Remove trailing ' ' */
if ((p = strchr(ptt_port, ' ')) != NULL)
*p = '\0';
/* If all that is left is a '\0' then also just return */
if (*ptt_port == '\0') {
*iptt = *ntx;
return(0);
}
if ((fd = open(ptt_port, O_RDWR|O_NONBLOCK)) < 0) {
fprintf(stderr, "Can't open %s.\n", ptt_port);
return (1);
}
if (dev_is_parport(fd)) {
state = STATE_PORT_OPEN_PARALLEL;
lp_reset(fd);
ptt_parallel(fd, ntx, iptt);
} else {
state = STATE_PORT_OPEN_SERIAL;
ptt_serial(fd, ntx, iptt);
}
break;
case STATE_PORT_OPEN_PARALLEL:
ptt_parallel(fd, ntx, iptt);
break;
case STATE_PORT_OPEN_SERIAL:
ptt_serial(fd, ntx, iptt);
break;
default:
close(fd);
fd = -1;
state = STATE_PORT_CLOSED;
break;
}
return(0);
}
/*
* ptt_serial
*
* generic serial unix PTT routine called indirectly from Fortran
*
* fd - already opened file descriptor
* ntx - pointer to fortran command on or off
* iptt - pointer to fortran command status on or off
*/
int
ptt_serial(int fd, int *ntx, int *iptt)
{
int control = TIOCM_RTS | TIOCM_DTR;
if(*ntx) {
ioctl(fd, TIOCMBIS, &control); /* Set DTR and RTS */
*iptt = 1;
} else {
ioctl(fd, TIOCMBIC, &control);
*iptt = 0;
}
return(0);
}
/* parport functions */
/*
* dev_is_parport(fd):
*
* inputs - Already open fd
* output - 1 if parallel port, 0 if not
* side effects - Unfortunately, this is platform specific.
*/
#if defined(HAVE_LINUX_PPDEV_H) /* Linux (ppdev) */
int
dev_is_parport(int fd)
{
struct stat st;
int m;
if ((fstat(fd, &st) == -1) ||
((st.st_mode & S_IFMT) != S_IFCHR) ||
(ioctl(fd, PPGETMODE, &m) == -1))
return(0);
return(1);
}
#elif defined(HAVE_DEV_PPBUS_PPI_H) /* FreeBSD (ppbus/ppi) */
int
dev_is_parport(int fd)
{
struct stat st;
unsigned char c;
if ((fstat(fd, &st) == -1) ||
((st.st_mode & S_IFMT) != S_IFCHR) ||
(ioctl(fd, PPISSTATUS, &c) == -1))
return(0);
return(1);
}
#else /* Fallback (nothing) */
int
dev_is_parport(int fd)
{
return(0);
}
#endif
/* Linux wrapper around PPFCONTROL */
#ifdef HAVE_LINUX_PPDEV_H
static void
parport_control (int fd, unsigned char controlbits, int values)
{
struct ppdev_frob_struct frob;
frob.mask = controlbits;
frob.val = values;
if (ioctl (fd, PPFCONTROL, &frob) == -1)
{
fprintf(stderr, "Parallel port PPFCONTROL");
exit (1);
}
}
#endif
/* FreeBSD wrapper around PPISCTRL */
#ifdef HAVE_DEV_PPBUS_PPI_H
static void
parport_control (int fd, unsigned char controlbits, int values)
{
unsigned char val;
if (ioctl (fd, PPIGCTRL, &val) == -1)
{
fprintf(stderr, "Parallel port PPIGCTRL");
exit (1);
}
val &= ~controlbits;
val |= values;
if (ioctl (fd, PPISCTRL, &val) == -1)
{
fprintf(stderr, "Parallel port PPISCTRL");
exit (1);
}
}
#endif
/* Initialise a parallel port, given open fd */
int
lp_init (int fd)
{
#ifdef HAVE_LINUX_PPDEV_H
int mode;
#endif
#ifdef HAVE_LINUX_PPDEV_H
mode = PARPORT_MODE_PCSPP;
if (ioctl (fd, PPSETMODE, &mode) == -1)
{
fprintf(stderr, "Setting parallel port mode");
close (fd);
return(-1);
}
if (ioctl (fd, PPEXCL, NULL) == -1)
{
fprintf(stderr, "Parallel port is already in use.\n");
close (fd);
return(-1);
}
if (ioctl (fd, PPCLAIM, NULL) == -1)
{
fprintf(stderr, "Claiming parallel port.\n");
fprintf(stderr, "HINT: did you unload the lp kernel module?");
close (fd);
return(-1);
}
/* Enable CW & PTT - /STROBE bit (pin 1) */
parport_control (fd, PARPORT_CONTROL_STROBE, PARPORT_CONTROL_STROBE);
#endif
#ifdef HAVE_DEV_PPBUS_PPI_H
parport_control (fd, STROBE, STROBE);
#endif
lp_reset (fd);
return(0);
}
/* release ppdev and close port */
int
lp_free (int fd)
{
#ifdef HAVE_LINUX_PPDEV_H
lp_reset (fd);
/* Disable CW & PTT - /STROBE bit (pin 1) */
parport_control (fd, PARPORT_CONTROL_STROBE, 0);
ioctl (fd, PPRELEASE);
#endif
#ifdef HAVE_DEV_PPBUS_PPI_H
/* Disable CW & PTT - /STROBE bit (pin 1) */
parport_control (fd, STROBE, 0);
#endif
close (fd);
return(0);
}
/* set to a known state */
int
lp_reset (int fd)
{
#if defined (HAVE_LINUX_PPDEV_H) || defined (HAVE_DEV_PPBUS_PPI_H)
lp_ptt (fd, 0);
#endif
return(0);
}
/* SSB PTT keying - /INIT bit (pin 16) (inverted) */
int
lp_ptt (int fd, int onoff)
{
#ifdef HAVE_LINUX_PPDEV_H
if (onoff == 1)
parport_control (fd, PARPORT_CONTROL_INIT,
PARPORT_CONTROL_INIT);
else
parport_control (fd, PARPORT_CONTROL_INIT, 0);
#endif
#ifdef HAVE_DEV_PPBUS_PPI_H
if (onoff == 1)
parport_control (fd, nINIT,
nINIT);
else
parport_control (fd, nINIT, 0);
#endif
return(0);
}
/*
* ptt_parallel
*
* generic parallel unix PTT routine called indirectly from Fortran
*
* fd - already opened file descriptor
* ntx - pointer to fortran command on or off
* iptt - pointer to fortran command status on or off
*/
int
ptt_parallel(int fd, int *ntx, int *iptt)
{
if(*ntx) {
lp_ptt(fd, 1);
*iptt=1;
} else {
lp_ptt(fd, 0);
*iptt=0;
}
return(0);
}

111
lib/ft4/clockit.f90 Normal file
View File

@ -0,0 +1,111 @@
subroutine clockit(dname,k)
! Times procedure number n between a call with k=0 (tstart) and with
! k=1 (tstop). Accumulates sums of these times in array ut (user time).
! Also traces all calls (for debugging purposes) if limtrace.gt.0
character*8 dname,name(50),space,ename
character*16 sname
character*512 data_dir,fname
logical first,on(50)
real ut(50),ut0(50),dut(50),tt(2)
integer ncall(50),nlevel(50),nparent(50)
integer onlevel(0:10)
data first/.true./,eps/0.000001/,ntrace/0/
data level/0/,nmax/0/,space/' '/
data limtrace/0/,lu/29/,ntimer/1/
! data limtrace/1000000/,lu/29/,ntimer/1/
save
if(ntimer.eq.0) return
if(lu.lt.1) lu=6
if(k.gt.1) go to 40 !Check for "all done" (k>1)
onlevel(0)=0
do n=1,nmax !Check for existing name
if(name(n).eq.dname) go to 20
enddo
nmax=nmax+1 !This is a new one
n=nmax
ncall(n)=0
on(n)=.false.
ut(n)=eps
name(n)=dname
20 if(k.eq.0) then !Get start times (k=0)
if(on(n)) print*,'Error in timer: ',dname,' already on.'
level=level+1 !Increment the level
on(n)=.true.
ut0(n)=etime(tt)
ncall(n)=ncall(n)+1
if(ncall(n).gt.1.and.nlevel(n).ne.level) then
nlevel(n)=-1
else
nlevel(n)=level
endif
nparent(n)=onlevel(level-1)
onlevel(level)=n
else if(k.eq.1) then !Get stop times and accumulate sums. (k=1)
if(on(n)) then
on(n)=.false.
ut1=etime(tt)
ut(n)=ut(n)+ut1-ut0(n)
endif
level=level-1
endif
ntrace=ntrace+1
if(ntrace.lt.limtrace) write(28,1020) ntrace,dname,k,level,nparent(n)
1020 format(i8,': ',a8,3i5)
return
! Write out the timer statistics
40 open(lu,file=trim(fname),status='unknown')
write(lu,1040)
1040 format(/' name time frac dtime', &
' dfrac calls level parent'/73('-'))
if(k.gt.100) then
ndiv=k-100
do i=1,nmax
ncall(i)=ncall(i)/ndiv
ut(i)=ut(i)/ndiv
enddo
endif
total=ut(1)
sum=0.
sumf=0.
do i=1,nmax
dut(i)=ut(i)
do j=i,nmax
if(nparent(j).eq.i) dut(i)=dut(i)-ut(j)
enddo
utf=ut(i)/total
dutf=dut(i)/total
sum=sum+dut(i)
sumf=sumf+dutf
kk=nlevel(i)
sname=space(1:kk)//name(i)//space(1:8-kk)
ename=space
if(i.ge.2) ename=name(nparent(i))
write(lu,1060) float(i),sname,ut(i),utf,dut(i),dutf, &
ncall(i),nlevel(i),ename
1060 format(f4.0,a16,2(f10.2,f6.2),i7,i5,2x,a8)
enddo
write(lu,1070) sum,sumf
1070 format(/36x,f10.2,f6.2)
close(lu)
return
entry clockit2(data_dir)
l1=index(data_dir,char(0))-1
if(l1.ge.1) data_dir(l1+1:l1+1)='/'
fname=data_dir(1:l1+1)//'clockit.out'
return
end subroutine clockit

486
lib/ft4/ft4_decode.f90 Normal file
View File

@ -0,0 +1,486 @@
subroutine ft4_decode(cdatetime0,tbuf,nfa,nfb,nQSOProgress,ncontest,nfqso, &
iwave,ndecodes,mycall,hiscall,cqstr,line,data_dir)
use packjt77
include 'ft4_params.f90'
parameter (NSS=NSPS/NDOWN)
character message*37,msgsent*37,msg0*37
character c77*77
character*61 line,linex(100)
character*37 decodes(100)
character*512 data_dir,fname
character*17 cdatetime0
character*12 mycall,hiscall
character*12 mycall0,hiscall0
character*6 hhmmss
character*4 cqstr,cqstr0
complex cd2(0:NMAX/NDOWN-1) !Complex waveform
complex cb(0:NMAX/NDOWN-1)
complex cd(0:NN*NSS-1) !Complex waveform
complex ctwk(4*NSS),ctwk2(4*NSS)
complex csymb(NSS)
complex cs(0:3,NN)
real s4(0:3,NN)
real bmeta(2*NN),bmetb(2*NN),bmetc(2*NN)
real a(5)
real llr(2*ND),llra(2*ND),llrb(2*ND),llrc(2*ND),llrd(2*ND)
real s2(0:255)
real candidate(3,100)
real savg(NH1),sbase(NH1)
integer apbits(2*ND)
integer apmy_ru(28),aphis_fd(28)
integer icos4a(0:3),icos4b(0:3),icos4c(0:3),icos4d(0:3)
integer*2 iwave(NMAX) !Raw received data
integer*1 message77(77),rvec(77),apmask(2*ND),cw(2*ND)
integer*1 hbits(2*NN)
integer graymap(0:3)
integer ip(1)
integer nappasses(0:5) ! # of decoding passes for QSO States 0-5
integer naptypes(0:5,4) ! nQSOProgress, decoding pass
integer mcq(29)
integer mrrr(19),m73(19),mrr73(19)
logical nohiscall,unpk77_success
logical one(0:255,0:7) ! 256 4-symbol sequences, 8 bits
logical first
data icos4a/0,1,3,2/
data icos4b/1,0,2,3/
data icos4c/2,3,1,0/
data icos4d/3,2,0,1/
data graymap/0,1,3,2/
data msg0/' '/
data first/.true./
data mcq/0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0/
data mrrr/0,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,1/
data m73/0,1,1,1,1,1,1,0,1,0,0,1,0,1,0,0,0,0,1/
data mrr73/0,1,1,1,1,1,1,0,0,1,1,1,0,1,0,1,0,0,1/
data rvec/0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,0,1,1,0, &
1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,1,0,0,1,0,1, &
0,1,0,1,0,1,1,0,1,1,1,1,1,0,0,0,1,0,1/
save fs,dt,tt,txt,twopi,h,one,first,linex,apbits,nappasses,naptypes, &
mycall0,hiscall0,msg0,cqstr0
call clockit('ft4_deco',0)
hhmmss=cdatetime0(8:13)
if(first) then
fs=12000.0/NDOWN !Sample rate after downsampling
dt=1/fs !Sample interval after downsample (s)
tt=NSPS*dt !Duration of "itone" symbols (s)
txt=NZ*dt !Transmission length (s) without ramp up/down
twopi=8.0*atan(1.0)
h=1.0
one=.false.
do i=0,255
do j=0,7
if(iand(i,2**j).ne.0) one(i,j)=.true.
enddo
enddo
mrrr=2*mod(mrrr+rvec(59:77),2)-1
m73=2*mod(m73+rvec(59:77),2)-1
mrr73=2*mod(mrr73+rvec(59:77),2)-1
nappasses(0)=2
nappasses(1)=2
nappasses(2)=2
nappasses(3)=2
nappasses(4)=2
nappasses(5)=3
! iaptype
!------------------------
! 1 CQ ??? ??? (29 ap bits)
! 2 MyCall ??? ??? (29 ap bits)
! 3 MyCall DxCall ??? (58 ap bits)
! 4 MyCall DxCall RRR (77 ap bits)
! 5 MyCall DxCall 73 (77 ap bits)
! 6 MyCall DxCall RR73 (77 ap bits)
!********
naptypes(0,1:4)=(/1,2,0,0/) ! Tx6 selected (CQ)
naptypes(1,1:4)=(/2,3,0,0/) ! Tx1
naptypes(2,1:4)=(/2,3,0,0/) ! Tx2
naptypes(3,1:4)=(/3,6,0,0/) ! Tx3
naptypes(4,1:4)=(/3,6,0,0/) ! Tx4
naptypes(5,1:4)=(/3,1,2,0/) ! Tx5
mycall0=''
hiscall0=''
cqstr0=''
first=.false.
endif
if(cqstr.ne.cqstr0) then
i0=index(cqstr,' ')
if(i0.le.1) then
message='CQ A1AA AA01'
else
message='CQ '//cqstr(1:i0-1)//' A1AA AA01'
endif
i3=-1
n3=-1
call pack77(message,i3,n3,c77)
call unpack77(c77,1,msgsent,unpk77_success)
read(c77,'(29i1)') mcq
mcq=2*mod(mcq+rvec(1:29),2)-1
cqstr0=cqstr
endif
l1=index(mycall,char(0))
if(l1.ne.0) mycall(l1:)=" "
l1=index(hiscall,char(0))
if(l1.ne.0) hiscall(l1:)=" "
if(mycall.ne.mycall0 .or. hiscall.ne.hiscall0) then
apbits=0
apbits(1)=99
apbits(30)=99
apmy_ru=0
aphis_fd=0
if(len(trim(mycall)) .lt. 3) go to 10
nohiscall=.false.
hiscall0=hiscall
if(len(trim(hiscall0)).lt.3) then
hiscall0=mycall ! use mycall for dummy hiscall - mycall won't be hashed.
nohiscall=.true.
endif
message=trim(mycall)//' '//trim(hiscall0)//' RR73'
i3=-1
n3=-1
call pack77(message,i3,n3,c77)
call unpack77(c77,1,msgsent,unpk77_success)
if(i3.ne.1 .or. (message.ne.msgsent) .or. .not.unpk77_success) go to 10
read(c77,'(77i1)') message77
apmy_ru=2*mod(message77(1:28)+rvec(2:29),2)-1
aphis_fd=2*mod(message77(30:57)+rvec(29:56),2)-1
message77=mod(message77+rvec,2)
call encode174_91(message77,cw)
apbits=2*cw-1
if(nohiscall) apbits(30)=99
10 continue
mycall0=mycall
hiscall0=hiscall
endif
candidate=0.0
ncand=0
syncmin=1.2
maxcand=100
fa=nfa
fb=nfb
call clockit('getcand4',0)
call getcandidates4(iwave,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
ncand,sbase)
call clockit('getcand4',1)
ndecodes=0
do icand=1,ncand
f0=candidate(1,icand)
snr=candidate(3,icand)-1.0
if( f0.le.10.0 .or. f0.ge.4990.0 ) cycle
call clockit('ft4_down',0)
call ft4_downsample(iwave,f0,cd2) !Downsample from 512 to 32 Sa/Symbol
call clockit('ft4_down',1)
sum2=sum(cd2*conjg(cd2))/(real(NMAX)/real(NDOWN))
if(sum2.gt.0.0) cd2=cd2/sqrt(sum2)
! Sample rate is now 12000/16 = 750 samples/second
do isync=1,2
if(isync.eq.1) then
idfmin=-12
idfmax=12
idfstp=3
ibmin=0
ibmax=216 !Max DT = 216/750 = 0.288 s
ibstp=4
else
idfmin=idfbest-4
idfmax=idfbest+4
idfstp=1
ibmin=max(0,ibest-5)
ibmax=min(ibest+5,NMAX/NDOWN-1)
ibstp=1
endif
ibest=-1
smax=-99.
idfbest=0
do idf=idfmin,idfmax,idfstp
a=0.
a(1)=real(idf)
ctwk=1.
call clockit('twkfreq1',0)
call twkfreq1(ctwk,4*NSS,fs,a,ctwk2)
call clockit('twkfreq1',1)
call clockit('sync4d ',0)
do istart=ibmin,ibmax,ibstp
call sync4d(cd2,istart,ctwk2,1,sync,sync2) !Find sync power
if(sync.gt.smax) then
smax=sync
ibest=istart
idfbest=idf
endif
enddo
call clockit('sync4d ',1)
enddo
enddo
f0=f0+real(idfbest)
if( f0.le.10.0 .or. f0.ge.4990.0 ) cycle
call clockit('ft4down ',0)
call ft4_downsample(iwave,f0,cb) !Final downsample with corrected f0
call clockit('ft4down ',1)
sum2=sum(abs(cb)**2)/(real(NSS)*NN)
if(sum2.gt.0.0) cb=cb/sqrt(sum2)
cd=cb(ibest:ibest+NN*NSS-1)
call clockit('four2a ',0)
do k=1,NN
i1=(k-1)*NSS
csymb=cd(i1:i1+NSS-1)
call four2a(csymb,NSS,1,-1,1)
cs(0:3,k)=csymb(1:4)
s4(0:3,k)=abs(csymb(1:4))
enddo
call clockit('four2a ',1)
! Sync quality check
is1=0
is2=0
is3=0
is4=0
do k=1,4
ip=maxloc(s4(:,k))
if(icos4a(k-1).eq.(ip(1)-1)) is1=is1+1
ip=maxloc(s4(:,k+33))
if(icos4b(k-1).eq.(ip(1)-1)) is2=is2+1
ip=maxloc(s4(:,k+66))
if(icos4c(k-1).eq.(ip(1)-1)) is3=is3+1
ip=maxloc(s4(:,k+99))
if(icos4d(k-1).eq.(ip(1)-1)) is4=is4+1
enddo
nsync=is1+is2+is3+is4 !Number of hard sync errors, 0-16
if(smax .lt. 0.7 .or. nsync .lt. 8) cycle
do nseq=1,3 !Try coherent sequences of 1, 2, and 4 symbols
if(nseq.eq.1) nsym=1
if(nseq.eq.2) nsym=2
if(nseq.eq.3) nsym=4
nt=2**(2*nsym)
do ks=1,NN-nsym+1,nsym !87+16=103 symbols.
amax=-1.0
do i=0,nt-1
i1=i/64
i2=iand(i,63)/16
i3=iand(i,15)/4
i4=iand(i,3)
if(nsym.eq.1) then
s2(i)=abs(cs(graymap(i4),ks))
elseif(nsym.eq.2) then
s2(i)=abs(cs(graymap(i3),ks)+cs(graymap(i4),ks+1))
elseif(nsym.eq.4) then
s2(i)=abs(cs(graymap(i1),ks ) + &
cs(graymap(i2),ks+1) + &
cs(graymap(i3),ks+2) + &
cs(graymap(i4),ks+3) &
)
else
print*,"Error - nsym must be 1, 2, or 4."
endif
enddo
ipt=1+(ks-1)*2
if(nsym.eq.1) ibmax=1
if(nsym.eq.2) ibmax=3
if(nsym.eq.4) ibmax=7
do ib=0,ibmax
bm=maxval(s2(0:nt-1),one(0:nt-1,ibmax-ib)) - &
maxval(s2(0:nt-1),.not.one(0:nt-1,ibmax-ib))
if(ipt+ib.gt.2*NN) cycle
if(nsym.eq.1) then
bmeta(ipt+ib)=bm
elseif(nsym.eq.2) then
bmetb(ipt+ib)=bm
elseif(nsym.eq.4) then
bmetc(ipt+ib)=bm
endif
enddo
enddo
enddo
bmetb(205:206)=bmeta(205:206)
bmetc(201:204)=bmetb(201:204)
bmetc(205:206)=bmeta(205:206)
call clockit('normaliz',0)
call normalizebmet(bmeta,2*NN)
call normalizebmet(bmetb,2*NN)
call normalizebmet(bmetc,2*NN)
call clockit('normaliz',1)
hbits=0
where(bmeta.ge.0) hbits=1
ns1=count(hbits( 1: 8).eq.(/0,0,0,1,1,0,1,1/))
ns2=count(hbits( 67: 74).eq.(/0,1,0,0,1,1,1,0/))
ns3=count(hbits(133:140).eq.(/1,1,1,0,0,1,0,0/))
ns4=count(hbits(199:206).eq.(/1,0,1,1,0,0,0,1/))
nsync_qual=ns1+ns2+ns3+ns4
if(nsync_qual.lt. 20) cycle
scalefac=2.83
llra( 1: 58)=bmeta( 9: 66)
llra( 59:116)=bmeta( 75:132)
llra(117:174)=bmeta(141:198)
llra=scalefac*llra
llrb( 1: 58)=bmetb( 9: 66)
llrb( 59:116)=bmetb( 75:132)
llrb(117:174)=bmetb(141:198)
llrb=scalefac*llrb
llrc( 1: 58)=bmetc( 9: 66)
llrc( 59:116)=bmetc( 75:132)
llrc(117:174)=bmetc(141:198)
llrc=scalefac*llrc
apmag=maxval(abs(llra))*1.1
npasses=3+nappasses(nQSOProgress)
if(ncontest.ge.5) npasses=3 ! Don't support Fox and Hound
do ipass=1,npasses
if(ipass.eq.1) llr=llra
if(ipass.eq.2) llr=llrb
if(ipass.eq.3) llr=llrc
if(ipass.le.3) then
apmask=0
iaptype=0
endif
if(ipass .gt. 3) then
llrd=llrc
iaptype=naptypes(nQSOProgress,ipass-3)
! ncontest=0 : NONE
! 1 : NA_VHF
! 2 : EU_VHF
! 3 : FIELD DAY
! 4 : RTTY
! 5 : FOX
! 6 : HOUND
!
! Conditions that cause us to bail out of AP decoding
napwid=50
if(ncontest.le.4 .and. iaptype.ge.3 .and. (abs(f0-nfqso).gt.napwid) ) cycle
if(iaptype.ge.2 .and. apbits(1).gt.1) cycle ! No, or nonstandard, mycall
if(iaptype.ge.3 .and. apbits(30).gt.1) cycle ! No, or nonstandard, dxcall
if(iaptype.eq.1) then ! CQ or CQ TEST or CQ FD or CQ RU or CQ SCC
apmask=0
apmask(1:29)=1
llrd(1:29)=apmag*mcq(1:29)
endif
if(iaptype.eq.2) then ! MyCall,???,???
apmask=0
if(ncontest.eq.0.or.ncontest.eq.1) then
apmask(1:29)=1
llrd(1:29)=apmag*apbits(1:29)
else if(ncontest.eq.2) then
apmask(1:28)=1
llrd(1:28)=apmag*apbits(1:28)
else if(ncontest.eq.3) then
apmask(1:28)=1
llrd(1:28)=apmag*apbits(1:28)
else if(ncontest.eq.4) then
apmask(2:29)=1
llrd(2:29)=apmag*apmy_ru(1:28)
endif
endif
if(iaptype.eq.3) then ! MyCall,DxCall,???
apmask=0
if(ncontest.eq.0.or.ncontest.eq.1.or.ncontest.eq.2) then
apmask(1:58)=1
llrd(1:58)=apmag*apbits(1:58)
else if(ncontest.eq.3) then ! Field Day
apmask(1:56)=1
llrd(1:28)=apmag*apbits(1:28)
llrd(29:56)=apmag*aphis_fd(1:28)
else if(ncontest.eq.4) then ! RTTY RU
apmask(2:57)=1
llrd(2:29)=apmag*apmy_ru(1:28)
llrd(30:57)=apmag*apbits(30:57)
endif
endif
if(iaptype.eq.4 .or. iaptype.eq.5 .or. iaptype.eq.6) then
apmask=0
if(ncontest.le.4) then
apmask(1:91)=1 ! mycall, hiscall, RRR|73|RR73
if(iaptype.eq.6) llrd(1:91)=apmag*apbits(1:91)
endif
endif
llr=llrd
endif
max_iterations=40
message77=0
call clockit('bpdecode',0)
call bpdecode174_91(llr,apmask,max_iterations,message77, &
cw,nharderror,niterations)
call clockit('bpdecode',1)
if(sum(message77).eq.0) cycle
if( nharderror.ge.0 ) then
message77=mod(message77+rvec,2) ! remove rvec scrambling
write(c77,'(77i1)') message77(1:77)
call unpack77(c77,1,message,unpk77_success)
idupe=0
do i=1,ndecodes
if(decodes(i).eq.message) idupe=1
enddo
if(ibest.le.10 .and. message.eq.msg0) idupe=1 !Already decoded
if(idupe.eq.1) exit
ndecodes=ndecodes+1
decodes(ndecodes)=message
if(snr.gt.0.0) then
xsnr=10*log10(snr)-14.0
else
xsnr=-20.0
endif
nsnr=nint(max(-20.0,xsnr))
freq=f0
tsig=mod(tbuf + ibest/750.0,100.0)
write(line,1000) hhmmss,nsnr,tsig,nint(freq),message
1000 format(a6,i4,f5.1,i5,' + ',1x,a37)
l1=index(data_dir,char(0))-1
if(l1.ge.1) data_dir(l1+1:l1+1)="/"
fname=data_dir(1:l1+1)//'all_ft4.txt'
open(24,file=trim(fname),status='unknown',position='append')
write(24,1002) cdatetime0,nsnr,tsig,nint(freq),message, &
nharderror,nsync_qual,ipass,niterations,iaptype
if(hhmmss.eq.' ') write(*,1002) cdatetime0,nsnr, &
tsig,nint(freq),message,nharderror,nsync_qual,ipass, &
niterations,iaptype
1002 format(a17,i4,f5.1,i5,' Rx ',a37,5i5)
close(24)
linex(ndecodes)=line
if(ibest.ge.ibmax-15) msg0=message !Possible dupe candidate
exit
endif
enddo !Sequence estimation
enddo !Candidate list
call clockit('ft4_deco',1)
call clockit2(data_dir)
call clockit('ft4_deco',101)
return
entry get_ft4msg(idecode,line)
line=linex(idecode)
return
end subroutine ft4_decode

View File

@ -0,0 +1,49 @@
subroutine ft4_downsample(iwave,f0,c)
! Input: i*2 data in iwave() at sample rate 12000 Hz
! Output: Complex data in c(), sampled at 1200 Hz
include 'ft4_params.f90'
parameter (NFFT2=NMAX/16)
integer*2 iwave(NMAX)
complex c(0:NMAX/NDOWN-1)
complex c1(0:NFFT2-1)
complex cx(0:NMAX/2)
real x(NMAX), window(0:NFFT2-1)
equivalence (x,cx)
logical first
data first/.true./
save first,window
df=12000.0/NMAX
baud=12000.0/NSPS
if(first) then
bw_transition = 0.5*baud
bw_flat = 4*baud
iwt = bw_transition / df
iwf = bw_flat / df
pi=4.0*atan(1.0)
window(0:iwt-1) = 0.5*(1+cos(pi*(/(i,i=iwt-1,0,-1)/)/iwt))
window(iwt:iwt+iwf-1)=1.0
window(iwt+iwf:2*iwt+iwf-1) = 0.5*(1+cos(pi*(/(i,i=0,iwt-1)/)/iwt))
window(2*iwt+iwf:)=0.0
iws = baud / df
window=cshift(window,iws)
first=.false.
endif
x=iwave
call four2a(x,NMAX,1,-1,0) !r2c FFT to freq domain
i0=nint(f0/df)
c1=0.
c1(0)=cx(i0)
do i=1,NFFT2/2
if(i0+i.le.NMAX/2) c1(i)=cx(i0+i)
if(i0-i.ge.0) c1(NFFT2-i)=cx(i0-i)
enddo
c1=c1*window/NFFT2
call four2a(c1,NFFT2,1,1,1) !c2c FFT back to time domain
c=c1(0:NMAX/NDOWN-1)
return
end subroutine ft4_downsample

16
lib/ft4/ft4_params.f90 Normal file
View File

@ -0,0 +1,16 @@
! FT4
! LDPC(174,91) code, four 4x4 Costas arrays for Sync
parameter (KK=91) !Information bits (77 + CRC14)
parameter (ND=87) !Data symbols
parameter (NS=16) !Sync symbols
parameter (NN=NS+ND) !Sync and data symbols (103)
parameter (NN2=NS+ND+2) !Total channel symbols (105)
parameter (NSPS=512) !Samples per symbol at 12000 S/s
parameter (NZ=NSPS*NN) !Sync and Data samples (52736)
parameter (NZ2=NSPS*NN2) !Total samples in shaped waveform (53760)
parameter (NMAX=5*12000) !Samples in iwave (60,000)
parameter (NFFT1=2048, NH1=NFFT1/2) !Length of FFTs for symbol spectra
parameter (NSTEP=NSPS) !Coarse time-sync step size
parameter (NHSYM=(NMAX-NFFT1)/NSTEP) !Number of symbol spectra (1/4-sym steps)
parameter (NDOWN=16) !Downsample factor

83
lib/ft4/ft4d.f90 Normal file
View File

@ -0,0 +1,83 @@
program ft4d
include 'ft4_params.f90'
character*8 arg
character*17 cdatetime
character*512 data_dir
character*12 mycall
character*12 hiscall
character*80 infile
character*61 line
character*4 cqstr
real*8 fMHz
integer ihdr(11)
integer*2 iwave(180000) !15*12000
fs=12000.0/NDOWN !Sample rate
dt=1/fs !Sample interval after downsample (s)
tt=NSPS*dt !Duration of "itone" symbols (s)
baud=1.0/tt !Keying rate for "itone" symbols (baud)
txt=NZ*dt !Transmission length (s)
nargs=iargc()
if(nargs.lt.1) then
print*,'Usage: ft4d [-a <data_dir>] [-f fMHz] [-n nQSOProgress] file1 [file2 ...]'
go to 999
endif
iarg=1
data_dir="."
call getarg(iarg,arg)
if(arg(1:2).eq.'-a') then
call getarg(iarg+1,data_dir)
iarg=iarg+2
endif
call getarg(iarg,arg)
if(arg(1:2).eq.'-f') then
call getarg(iarg+1,arg)
read(arg,*) fMHz
iarg=iarg+2
endif
nQSOProgress=0
if(arg(1:2).eq.'-n') then
call getarg(iarg+1,arg)
read(arg,*) nQSOProgress
iarg=iarg+2
endif
nfa=10
nfb=4990
ndecodes=0
nfqso=1500
mycall="K9AN"
hiscall="K1JT"
ncontest=4
cqstr="RU "
do ifile=iarg,nargs
call getarg(ifile,infile)
j2=index(infile,'.wav')
open(10,file=infile,status='old',access='stream')
read(10) ihdr
npts=ihdr(11)/2
read(10) iwave(1:npts)
close(10)
cdatetime=infile(1:13)//'.000'
istep=3456
nsteps=(npts-52800)/istep + 1
do n=1,nsteps
i0=(n-1)*istep + 1
tbuf=(i0-1)/12000.0
call ft4_decode(cdatetime,tbuf,nfa,nfb,nQSOProgress,ncontest, &
nfqso,iwave(i0),ndecodes,mycall,hiscall,cqstr,line,data_dir)
do idecode=1,ndecodes
call get_ft4msg(idecode,line)
write(*,'(a61)') line
enddo
enddo !steps
enddo !files
call four2a(xx,-1,1,-1,1) !Destroy FFTW plans to free their memory
999 end program ft4d

155
lib/ft4/ft4sim.f90 Normal file
View File

@ -0,0 +1,155 @@
program ft4sim
! Generate simulated signals for experimental "FT4" mode
use wavhdr
use packjt77
include 'ft4_params.f90' !Set various constants
parameter (NWAVE=NN*NSPS)
type(hdr) h !Header for .wav file
character arg*12,fname*17
character msg37*37,msgsent37*37
character c77*77
complex c0(0:NMAX-1)
complex c(0:NMAX-1)
real wave(NMAX)
real dphi(0:NMAX-1)
real pulse(3*NSPS)
integer itone(NN)
integer*1 msgbits(77)
integer*2 iwave(NMAX) !Generated full-length waveform
integer icos4(4)
data icos4/0,1,3,2/
! Get command-line argument(s)
nargs=iargc()
if(nargs.ne.7) then
print*,'Usage: ft4sim "message" f0 DT fdop del nfiles snr'
print*,'Examples: ft4sim "K1ABC W9XYZ EN37" 1500.0 0.0 0.1 1.0 10 -15'
print*,' ft4sim "WA9XYZ/R KA1ABC/R FN42" 1500.0 0.0 0.1 1.0 10 -15'
print*,' ft4sim "K1ABC RR73; W9XYZ <KH1/KH7Z> -11" 300 0 0 0 1 -10'
go to 999
endif
call getarg(1,msg37) !Message to be transmitted
call getarg(2,arg)
read(arg,*) f0 !Frequency (only used for single-signal)
call getarg(3,arg)
read(arg,*) xdt !Time offset from nominal (s)
call getarg(4,arg)
read(arg,*) fspread !Watterson frequency spread (Hz)
call getarg(5,arg)
read(arg,*) delay !Watterson delay (ms)
call getarg(6,arg)
read(arg,*) nfiles !Number of files
call getarg(7,arg)
read(arg,*) snrdb !SNR_2500
nfiles=abs(nfiles)
twopi=8.0*atan(1.0)
fs=12000.0 !Sample rate (Hz)
dt=1.0/fs !Sample interval (s)
hmod=1.0 !Modulation index (0.5 is MSK, 1.0 is FSK)
tt=NSPS*dt !Duration of symbols (s)
baud=1.0/tt !Keying rate (baud)
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
! Source-encode, then get itone()
i3=-1
n3=-1
call pack77(msg37,i3,n3,c77)
read(c77,'(77i1)') msgbits
call genft4(msg37,0,msgsent37,itone)
write(*,*)
write(*,'(a9,a37,3x,a7,i1,a1,i1)') 'Message: ',msgsent37,'i3.n3: ',i3,'.',n3
write(*,1000) f0,xdt,txt,snrdb
1000 format('f0:',f9.3,' DT:',f6.2,' TxT:',f6.1,' SNR:',f6.1)
write(*,*)
if(i3.eq.1) then
write(*,*) ' mycall hiscall hisgrid'
write(*,'(28i1,1x,i1,1x,28i1,1x,i1,1x,i1,1x,15i1,1x,3i1)') msgbits(1:77)
else
write(*,'(a14)') 'Message bits: '
write(*,'(77i1)') msgbits
endif
write(*,*)
write(*,'(a17)') 'Channel symbols: '
write(*,'(76i1)') itone
write(*,*)
call sgran()
! The filtered frequency pulse
do i=1,3*NSPS
tt=(i-1.5*NSPS)/real(NSPS)
pulse(i)=gfsk_pulse(1.0,tt)
enddo
! Define the instantaneous frequency waveform
dphi_peak=twopi*hmod/real(NSPS)
dphi=0.0
do j=1,NN
ib=(j-1)*NSPS
ie=ib+3*NSPS-1
dphi(ib:ie)=dphi(ib:ie)+dphi_peak*pulse*itone(j)
enddo
phi=0.0
c0=0.0
dphi=dphi+twopi*f0*dt
do j=0,NMAX-1
c0(j)=cmplx(cos(phi),sin(phi))
phi=mod(phi+dphi(j),twopi)
enddo
c0(0:NSPS-1)=c0(0:NSPS-1)*(1.0-cos(twopi*(/(i,i=0,NSPS-1)/)/(2.0*NSPS)) )/2.0
c0((NN+1)*NSPS:(NN+2)*NSPS-1)=c0((NN+1)*NSPS:(NN+2)*NSPS-1)*(1.0+cos(twopi*(/(i,i=0,NSPS-1)/)/(2.0*NSPS) ))/2.0
c0((NN+2)*NSPS:)=0.
k=nint((xdt+0.14)/dt)
c0=cshift(c0,-k)
ia=k
do ifile=1,nfiles
c=c0
if(fspread.ne.0.0 .or. delay.ne.0.0) call watterson(c,NMAX,NWAVE,fs,delay,fspread)
c=sig*c
ib=k
wave=real(c)
peak=maxval(abs(wave(ia:ib)))
nslots=1
if(snrdb.lt.90) then
do i=1,NMAX !Add gaussian noise at specified SNR
xnoise=gran()
wave(i)=wave(i) + xnoise
enddo
endif
gain=100.0
if(snrdb.lt.90.0) then
wave=gain*wave
else
datpk=maxval(abs(wave))
fac=32766.9/datpk
wave=fac*wave
endif
if(any(abs(wave).gt.32767.0)) print*,"Warning - data will be clipped."
iwave=nint(wave)
h=default_header(12000,NMAX+2208)
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
iwave(1:2208)=0
write(10) iwave(1:2208) !Add 0.5 s of zeroes
close(10)
write(*,1110) ifile,xdt,f0,snrdb,fname
1110 format(i4,f7.2,f8.2,f7.1,2x,a17)
enddo
999 end program ft4sim

103
lib/ft4/ft4sim_mult.f90 Normal file
View File

@ -0,0 +1,103 @@
program ft4sim_mult
! Generate simulated signals for experimental "FT4" mode
use wavhdr
use packjt77
include 'ft4_params.f90' !FT4 protocol constants
parameter (NWAVE=NN*NSPS)
parameter (NZZ=15*12000) !Length of .wav file, 180,000 i*2 samples
type(hdr) h !Header for .wav file
character arg*12,fname*17,cjunk*4
character msg37*37,msgsent37*37,c77*77
real wave0((NN+2)*NSPS)
real wave(NZZ)
real tmp(NZZ)
integer itone(NN)
integer*2 iwave(NZZ) !Generated full-length waveform
integer icos4(4)
data icos4/0,1,3,2/
! Get command-line argument(s)
nargs=iargc()
if(nargs.ne.2) then
print*,'Usage: ft4sim_mult nsigs nfiles'
print*,'Example: ft4sim_mult 20 8 '
go to 999
endif
call getarg(1,arg)
read(arg,*) nsigs !Number of signals
call getarg(2,arg)
read(arg,*) nfiles !Number of files
twopi=8.0*atan(1.0)
fs=12000.0 !Sample rate (Hz)
dt=1.0/fs !Sample interval (s)
hmod=1.0 !Modulation index (0.5 is MSK, 1.0 is FSK)
tt=NSPS*dt !Duration of unsmoothed symbols (s)
baud=1.0/tt !Keying rate (baud)
txt=NZ*dt !Transmission length (s) without ramp up/down
bandwidth_ratio=2500.0/(fs/2.0)
txt=NN*NSPS/12000.0
xdtmax=10.0 - 0.086
open(10,file='messages.txt',status='old',err=998)
do ifile=1,nfiles
1 read(10,1001,end=999) cjunk,n
1001 format(a4,i2)
if(cjunk.ne.'File' .or. n.ne.ifile) go to 1
wave=0.
write(fname,1002) ifile
1002 format('000000_',i6.6,'.wav')
do isig=1,nsigs
read(10,1003,end=100) cjunk,isnr,xdt0,ifreq,msg37
1003 format(a4,30x,i3,f5.1,i5,1x,a37)
if(cjunk.eq.'File') go to 100
if(isnr.lt.-16) isnr=-16
f0=ifreq*93.75/50.0
call random_number(r)
xdt=r*xdtmax
! Source-encode, then get itone()
i3=-1
n3=-1
call pack77(msg37,i3,n3,c77)
call genft4(msg37,0,msgsent37,itone)
nwave0=(NN+2)*NSPS
call gen_ft4wave(itone,NN,NSPS,12000.0,f0,wave0,nwave0)
k0=nint(xdt/dt)
if(k0.lt.1) k0=1
tmp(:k0-1)=0.0
tmp(k0:k0+nwave0-1)=wave0
tmp(k0+nwave0:)=0.0
! Insert this signal into wave() array
sig=sqrt(2*bandwidth_ratio) * 10.0**(0.05*isnr)
wave=wave + sig*tmp
write(*,1100) fname(1:13),isig,isnr,xdt,nint(f0),msg37
1100 format(a13,i4,i5,f5.1,i6,2x,a37)
enddo ! isig
100 backspace 10
do i=1,NZZ !Add gaussian noise at specified SNR
xnoise=gran()
wave(i)=wave(i) + xnoise
enddo
gain=30.0
wave=gain*wave
if(any(abs(wave).gt.32767.0)) print*,"Warning - data will be clipped."
iwave=nint(wave)
h=default_header(12000,NZZ)
open(12,file=fname,status='unknown',access='stream')
write(12) h,iwave !Save to *.wav file
close(12)
print*,' '
enddo ! ifile
go to 999
998 print*,'Cannot open file "messages.txt"'
999 end program ft4sim_mult

52
lib/ft4/gen_ft4wave.f90 Normal file
View File

@ -0,0 +1,52 @@
subroutine gen_ft4wave(itone,nsym,nsps,fsample,f0,wave,nwave)
real wave(nwave)
real pulse(6144) !512*4*3
real dphi(0:240000-1)
integer itone(nsym)
logical first
data first/.true./
save pulse,first,twopi,dt,hmod
if(first) then
twopi=8.0*atan(1.0)
dt=1.0/fsample
hmod=1.0
! Compute the frequency-smoothing pulse
do i=1,3*nsps
tt=(i-1.5*nsps)/real(nsps)
pulse(i)=gfsk_pulse(1.0,tt)
enddo
first=.false.
endif
! Compute the smoothed frequency waveform.
! Length = (nsym+2)*nsps samples, zero-padded
dphi_peak=twopi*hmod/real(nsps)
dphi=0.0
do j=1,nsym
ib=(j-1)*nsps
ie=ib+3*nsps-1
dphi(ib:ie) = dphi(ib:ie) + dphi_peak*pulse(1:3*nsps)*itone(j)
enddo
! Calculate and insert the audio waveform
phi=0.0
dphi = dphi + twopi*f0*dt !Shift frequency up by f0
wave=0.
k=0
do j=0,nwave-1
k=k+1
wave(k)=sin(phi)
phi=mod(phi+dphi(j),twopi)
enddo
! Compute the ramp-up and ramp-down symbols
wave(1:nsps)=wave(1:nsps) * &
(1.0-cos(twopi*(/(i,i=0,nsps-1)/)/(2.0*nsps)))/2.0
k1=(nsym+1)*nsps+1
wave(k1:k1+nsps-1)=wave(k1:k1+nsps-1) * &
(1.0+cos(twopi*(/(i,i=0,nsps-1)/)/(2.0*nsps)))/2.0
return
end subroutine gen_ft4wave

82
lib/ft4/genft4.f90 Normal file
View File

@ -0,0 +1,82 @@
subroutine genft4(msg0,ichk,msgsent,i4tone)
! Encode an FT4 message
! Input:
! - msg0 requested message to be transmitted
! - ichk if ichk=1, return only msgsent
! - msgsent message as it will be decoded
! - i4tone array of audio tone values, {0,1,2,3}
! Frame structure:
! s16 + 87symbols + 2 ramp up/down = 105 total channel symbols
! r1 + s4 + d29 + s4 + d29 + s4 + d29 + s4 + r1
! Message duration: TxT = 105*512/12000 = 4.48 s
! use iso_c_binding, only: c_loc,c_size_t
use packjt77
include 'ft4_params.f90'
character*37 msg0
character*37 message !Message to be generated
character*37 msgsent !Message as it will be received
character*77 c77
integer*4 i4tone(NN),itmp(ND)
integer*1 codeword(2*ND)
integer*1 msgbits(77),rvec(77)
integer icos4a(4),icos4b(4),icos4c(4),icos4d(4)
logical unpk77_success
data icos4a/0,1,3,2/
data icos4b/1,0,2,3/
data icos4c/2,3,1,0/
data icos4d/3,2,0,1/
data rvec/0,1,0,0,1,0,1,0,0,1,0,1,1,1,1,0,1,0,0,0,1,0,0,1,1,0,1,1,0, &
1,0,0,1,0,1,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,1,0,0,1,0,1, &
0,1,0,1,0,1,1,0,1,1,1,1,1,0,0,0,1,0,1/
message=msg0
do i=1, 37
if(ichar(message(i:i)).eq.0) then
message(i:37)=' '
exit
endif
enddo
do i=1,37 !Strip leading blanks
if(message(1:1).ne.' ') exit
message=message(i+1:)
enddo
i3=-1
n3=-1
call pack77(message,i3,n3,c77)
call unpack77(c77,0,msgsent,unpk77_success) !Unpack to get msgsent
if(ichk.eq.1) go to 999
read(c77,"(77i1)") msgbits
msgbits=mod(msgbits+rvec,2)
call encode174_91(msgbits,codeword)
! Grayscale mapping:
! bits tone
! 00 0
! 01 1
! 11 2
! 10 3
do i=1,ND
is=codeword(2*i)+2*codeword(2*i-1)
if(is.le.1) itmp(i)=is
if(is.eq.2) itmp(i)=3
if(is.eq.3) itmp(i)=2
enddo
i4tone(1:4)=icos4a
i4tone(5:33)=itmp(1:29)
i4tone(34:37)=icos4b
i4tone(38:66)=itmp(30:58)
i4tone(67:70)=icos4c
i4tone(71:99)=itmp(59:87)
i4tone(100:103)=icos4d
999 return
end subroutine genft4

View File

@ -0,0 +1,73 @@
subroutine getcandidates4(id,fa,fb,syncmin,nfqso,maxcand,savg,candidate, &
ncand,sbase)
include 'ft4_params.f90'
real s(NH1,NHSYM)
real savg(NH1),savsm(NH1)
real sbase(NH1)
real x(NFFT1)
real window(NFFT1)
complex cx(0:NH1)
real candidate(3,maxcand)
integer*2 id(NMAX)
integer indx(NH1)
integer ipk(1)
equivalence (x,cx)
logical first
data first/.true./
save first,window
if(first) then
first=.false.
pi=4.0*atan(1.)
window=0.
call nuttal_window(window,NFFT1)
endif
! Compute symbol spectra, stepping by NSTEP steps.
savg=0.
tstep=NSTEP/12000.0
df=12000.0/NFFT1 !5.86 Hz
fac=1.0/300.0
do j=1,NHSYM
ia=(j-1)*NSTEP + 1
ib=ia+NFFT1-1
if(ib.gt.NMAX) exit
x=fac*id(ia:ib)*window
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
savsm=0.
do i=8,NH1-7
savsm(i)=sum(savg(i-7:i+7))/15.
enddo
nfa=fa/df
if(nfa.lt.1) nfa=1
nfb=fb/df
if(nfb.gt.nint(5000.0/df)) nfb=nint(5000.0/df)
np=nfb-nfa+1
indx=0
call indexx(savsm(nfa:nfb),np,indx)
xn=savsm(nfa+indx(nint(0.3*np)))
savsm=savsm/xn
ncand=0
f_offset = -1.5*12000/512
do i=nfa+1,nfb-1
if(savsm(i).ge.savsm(i-1) .and. savsm(i).ge.savsm(i+1) .and. savsm(i).ge.syncmin) then
del=0.5*(savsm(i-1)-savsm(i+1))/(savsm(i-1)-2*savsm(i)+savsm(i+1))
fpeak=(i+del)*df+f_offset
speak=savsm(i) - 0.25*(savsm(i-1)-savsm(i+1))*del
ncand=ncand+1
if(ncand.gt.maxcand) exit
candidate(1,ncand)=fpeak
candidate(2,ncand)=-99.99
candidate(3,ncand)=speak
endif
enddo
return
end subroutine getcandidates4

162
lib/ft4/messages.txt Normal file
View File

@ -0,0 +1,162 @@
File 1
190106_000015 7.080 Rx FT8 -15 0.2 178 N1TRK N4FKH 569 VA
190106_000015 7.080 Rx FT8 -13 -0.1 253 N1TRK KB7RUQ 539 UT
190106_000015 7.080 Rx FT8 10 0.2 389 W0ZF N3LFC RR73
190106_000015 7.080 Rx FT8 -10 0.1 450 PY4AZ KF7YED 559 MT
190106_000015 7.080 Rx FT8 -3 0.2 507 N1TRK WA4DYD 559 GA
190106_000015 7.080 Rx FT8 14 0.2 689 KB0VHA KA1YQC R 539 MA
190106_000015 7.080 Rx FT8 -5 0.1 984 W9JA N9OY RR73
190106_000015 7.080 Rx FT8 -12 0.1 1123 VA2CZ K8GNG RR73
190106_000015 7.080 Rx FT8 2 0.1 1240 CQ RU K7RL CN88
190106_000015 7.080 Rx FT8 -11 0.5 1293 K4ZMW K7VZ RR73
190106_000015 7.080 Rx FT8 2 0.1 1387 WD9IGY KX1X 73
190106_000015 7.080 Rx FT8 -10 0.1 1536 CQ RU W0FRC DM79
190106_000015 7.080 Rx FT8 -2 0.1 1635 K4SQC VE3RX 549 ON
190106_000030 7.080 Rx FT8 -11 0.2 1745 CQ RU K1LOG FN56
190106_000030 7.080 Rx FT8 3 0.8 1798 WD5DAX WS4WW 73
190106_000030 7.080 Rx FT8 12 0.1 1895 DJ6GI KG4W 73
190106_000030 7.080 Rx FT8 -14 0.2 2119 KF7YED KF5ZNQ 549 TX
190106_000030 7.080 Rx FT8 -5 0.1 336 CQ RU AB5XS EM12
190106_000030 7.080 Rx FT8 0 0.0 1415 W3KIT AA8SW RR73
190106_000030 7.080 Rx FT8 -13 0.3 1540 NI6G W7DRW 579 AZ
190106_000030 7.080 Rx FT8 -10 0.1 312 SHIFT W9JA
190106_000030 7.080 Rx FT8 -16 0.1 1447 W7BOB KJ7G 549 WA
File 2
190106_000045 7.080 Rx FT8 -9 0.1 178 N1TRK N4FKH 569 VA
190106_000045 7.080 Rx FT8 -9 -0.1 253 N1TRK KB7RUQ RR73
190106_000045 7.080 Rx FT8 -8 0.1 336 CQ RU AB5XS EM12
190106_000045 7.080 Rx FT8 16 0.3 689 KB0VHA KA1YQC R 539 MA
190106_000045 7.080 Rx FT8 -3 0.1 984 CQ RU N9OY EN43
190106_000045 7.080 Rx FT8 -10 0.1 1146 K1JT WB4HXE 559 GA
190106_000045 7.080 Rx FT8 6 0.1 1240 VE3LON K7RL R 549 WA
190106_000045 7.080 Rx FT8 -1 0.1 1386 WD9IGY KX1X 73
190106_000045 7.080 Rx FT8 -12 0.1 1536 CQ RU W0FRC DM79
190106_000045 7.080 Rx FT8 3 0.1 1635 K4SQC VE3RX RR73
190106_000045 7.080 Rx FT8 -5 0.1 1688 CQ RU W1QA FN32
190106_000045 7.080 Rx FT8 1 0.8 1797 CQ RU WS4WW FM17
190106_000045 7.080 Rx FT8 14 0.1 1895 HB9BUN KG4W R 549 VA
190106_000100 7.080 Rx FT8 -7 0.1 2002 W9TO KN3ILZ 529 PA
190106_000100 7.080 Rx FT8 -9 0.1 312 W9JA PY2APK RRR
190106_000100 7.080 Rx FT8 -12 0.1 436 NZ7P WA7JAY 589 CA
190106_000100 7.080 Rx FT8 -13 -0.1 1380 AC6BW KR9A R 559 WI
190106_000100 7.080 Rx FT8 -17 0.1 1448 W7BOB KJ7G RR73
190106_000100 7.080 Rx FT8 -7 0.3 1540 NI6G W7DRW 569 AZ
File 3
190106_000115 7.080 Rx FT8 -13 0.2 178 N1TRK N4FKH 569 VA
190106_000115 7.080 Rx FT8 -14 -0.1 253 N1TRK KB7RUQ RR73
190106_000115 7.080 Rx FT8 -5 0.1 336 CQ RU AB5XS EM12
190106_000115 7.080 Rx FT8 -7 0.1 449 NI6G KF7YED 569 MT
190106_000115 7.080 Rx FT8 14 0.2 689 KB0VHA KA1YQC 73
190106_000115 7.080 Rx FT8 11 0.4 852 W9TO N4QWF 569 VA
190106_000115 7.080 Rx FT8 -3 0.1 984 W4EMB N9OY R 529 WI
190106_000115 7.080 Rx FT8 -14 0.1 1117 WB4FAY K7PDW 549 UT
190106_000115 7.080 Rx FT8 3 0.2 1240 VE3LON K7RL R 559 WA
190106_000115 7.080 Rx FT8 -18 0.1 1380 AC6BW KR9A R 559 WI
190106_000115 7.080 Rx FT8 4 0.1 1475 K4KCL KB4S 559 VA
190106_000130 7.080 Rx FT8 -5 0.1 1612 K1JT WB4HXE RR73
190106_000115 7.080 Rx FT8 -1 0.1 1688 KW4RTR W1QA R 539 MA
190106_000115 7.080 Rx FT8 -15 0.2 1745 CQ RU K1LOG FN56
190106_000115 7.080 Rx FT8 3 0.8 1798 WA1T WS4WW R 579 VA
190106_000115 7.080 Rx FT8 14 0.1 1895 HB9BUN KG4W R 549 VA
190106_000115 7.080 Rx FT8 -9 0.2 2125 CQ RU NF3R FN20
190106_000130 7.080 Rx FT8 -8 0.4 976 AG1T K7VZ 569 AZ
190106_000130 7.080 Rx FT8 -7 0.1 1434 Z36W N4KMC 529 FL
File 4
190106_000145 7.080 Rx FT8 -8 0.1 335 CQ RU AB5XS EM12
190106_000145 7.080 Rx FT8 -11 0.2 388 W0ZF WW4LL 559 ME
190106_000145 7.080 Rx FT8 -10 0.1 449 NI6G KF7YED 569 MT
190106_000145 7.080 Rx FT8 10 0.3 689 CQ RU KA1YQC FN42
190106_000145 7.080 Rx FT8 12 0.4 852 W9TO N4QWF RR73
190106_000145 7.080 Rx FT8 -2 0.1 984 W4EMB N9OY R 529 WI
190106_000145 7.080 Rx FT8 -12 0.1 1117 WB4FAY K7PDW 549 UT
190106_000145 7.080 Rx FT8 4 0.2 1240 VE3LON K7RL 73
190106_000145 7.080 Rx FT8 -13 0.1 1348 WW5M KO9V 559 NC
190106_000145 7.080 Rx FT8 -14 0.1 1380 AC6BW KR9A R 559 WI
190106_000145 7.080 Rx FT8 2 0.1 1475 K4KCL KB4S RR73
190106_000145 7.080 Rx FT8 -9 0.1 1536 KC1HTT W0FRC 73
190106_000145 7.080 Rx FT8 1 0.1 1688 KW4RTR W1QA R 539 MA
190106_000200 7.080 Rx FT8 1 0.8 1797 WA1T WS4WW R 569 VA
190106_000200 7.080 Rx FT8 15 0.1 1895 CQ RU KG4W FM17
190106_000200 7.080 Rx FT8 -6 0.2 2125 CQ RU NF3R FN20
190106_000200 7.080 Rx FT8 -7 0.1 311 PD8DX PY2APK 539 0081
190106_000200 7.080 Rx FT8 -3 0.5 976 AG1T K7VZ RR73
190106_000200 7.080 Rx FT8 -5 0.1 1432 Z36W N4KMC 529 FL
190106_000200 7.080 Rx FT8 -7 0.3 1540 N7BT W7DRW 559 AZ
190106_000200 7.080 Rx FT8 -16 0.3 2124 N3KCR ON9COP 529 0053
190106_000200 7.080 Rx FT8 -12 0.1 1448 K2DH KJ7G 549 WA
File 5
190106_000215 7.080 Rx FT8 11 0.1 435 K1JT WV8WA 569 WV
190106_000215 7.080 Rx FT8 -4 0.1 335 CQ RU AB5XS EM12
190106_000215 7.080 Rx FT8 10 0.5 689 CQ RU KA1YQC FN42
190106_000215 7.080 Rx FT8 -10 0.1 843 CQ RU AE5JH EL07
190106_000215 7.080 Rx FT8 -3 0.1 984 W4EMB N9OY R 529 WI
190106_000215 7.080 Rx FT8 -10 0.1 1118 WB4FAY W5MO 539 TX
190106_000215 7.080 Rx FT8 -16 0.1 1170 K1JT K8GNG 569 MI
190106_000215 7.080 Rx FT8 6 0.1 1240 CQ RU K7RL CN88
190106_000215 7.080 Rx FT8 -8 0.1 1348 WW5M KO9V RR73
190106_000215 7.080 Rx FT8 -12 0.1 1432 Z36W N4KMC 529 FL
190106_000215 7.080 Rx FT8 2 0.1 1490 KA6BIM KB4S 529 VA
190106_000230 7.080 Rx FT8 -1 0.1 1688 CQ RU W1QA FN32
190106_000230 7.080 Rx FT8 15 0.1 1895 DD5ZZ KG4W R 539 VA
190106_000230 7.080 Rx FT8 -8 0.2 2125 CQ RU NF3R FN20
190106_000230 7.080 Rx FT8 -13 0.1 1380 AC6BW KR9A R 559 WI
190106_000230 7.080 Rx FT8 -4 0.1 1536 CQ RU W0FRC DM79
190106_000230 7.080 Rx FT8 -8 0.3 1540 N7BT W7DRW 559 AZ
File 6
190106_000245 7.080 Rx FT8 6 0.1 635 K1JT WV8WA RR73
190106_000245 7.080 Rx FT8 -12 0.1 335 VE3RX AB5XS R 589 TX
190106_000245 7.080 Rx FT8 -16 0.1 423 WA1T PY2APK 529 0081
190106_000245 7.080 Rx FT8 9 0.4 507 N1TRK N4QWF RR73
190106_000245 7.080 Rx FT8 -10 0.1 869 W7BOB W3KIT 539 MD
190106_000245 7.080 Rx FT8 -7 0.1 984 W4EMB N9OY R 529 WI
190106_000245 7.080 Rx FT8 -13 0.1 1170 K1JT K8GNG 569 MI
190106_000245 7.080 Rx FT8 5 0.1 1240 W6GMT K7RL R 569 WA
190106_000245 7.080 Rx FT8 2 0.1 1490 KA6BIM KB4S 529 VA
190106_000245 7.080 Rx FT8 -1 0.1 1688 CQ RU W1QA FN32
190106_000245 7.080 Rx FT8 1 0.7 1798 N3KCR WS4WW R 599 VA
190106_000245 7.080 Rx FT8 17 0.1 1895 DD5ZZ KG4W 73
190106_000300 7.080 Rx FT8 -10 0.2 2125 CQ RU NF3R FN20
190106_000300 7.080 Rx FT8 8 0.1 546 W7BOB W4DHE 539 KY
190106_000300 7.080 Rx FT8 -9 0.2 844 CQ RU AE5JH EL07
190106_000300 7.080 Rx FT8 -5 0.1 1536 W9WLX W0FRC R 549 CO
File 7
190106_000315 7.080 Rx FT8 -5 0.1 335 VE3RX AB5XS 73
190106_000315 7.080 Rx FT8 -13 0.1 424 WA1T PY2APK 529 0081
190106_000315 7.080 Rx FT8 6 0.1 545 W7BOB W4DHE 539 KY
190106_000315 7.080 Rx FT8 -7 0.4 600 K2DH K7VZ 549 AZ
190106_000315 7.080 Rx FT8 9 0.1 689 VE3LON KA1YQC 73
190106_000315 7.080 Rx FT8 -8 0.1 869 W7BOB W3KIT 539 MD
190106_000315 7.080 Rx FT8 -1 0.1 984 W4EMB N9OY R 529 WI
190106_000315 7.080 Rx FT8 -15 -0.1 1117 WB4FAY KB7RUQ 529 UT
190106_000315 7.080 Rx FT8 7 -0.2 1240 W6GMT K7RL 73
190106_000315 7.080 Rx FT8 -5 0.2 1581 VA3WW WA4DYD 559 GA
190106_000315 7.080 Rx FT8 -8 0.1 1688 DD5ZZ W1QA R 569 MA
190106_000315 7.080 Rx FT8 4 0.7 1798 N3KCR WS4WW 73
190106_000315 7.080 Rx FT8 16 0.1 1895 CQ RU KG4W FM17
190106_000315 7.080 Rx FT8 -11 0.2 2125 CQ RU NF3R FN20
190106_000330 7.080 Rx FT8 -8 0.1 1170 K1JT K8GNG RR73
190106_000330 7.080 Rx FT8 -13 -0.1 454 NZ7P KM7S R 549 CA
190106_000330 7.080 Rx FT8 1 0.1 941 KW4RTR KO9V 559 NC
190106_000330 7.080 Rx FT8 -16 0.3 1034 W9JA W9OY 539 FL
190106_000330 7.080 Rx FT8 -2 0.1 1240 K2DH KD2GXL 559 NY
190106_000330 7.080 Rx FT8 -5 0.1 1536 W9WLX W0FRC 73
190106_000330 7.080 Rx FT8 -2 -0.1 918 KF6JXM W5BT 529 TX
File 8
190106_000345 7.080 Rx FT8 -5 0.1 335 CQ RU AB5XS EM12
190106_000345 7.080 Rx FT8 -13 0.1 423 WA1T PY2APK 529 0081
190106_000345 7.080 Rx FT8 6 0.2 689 CQ RU KA1YQC FN42
190106_000345 7.080 Rx FT8 -16 0.1 852 W9TO KM4LFT RR73
190106_000345 7.080 Rx FT8 -4 -0.1 918 KF6JXM W5BT 529 TX
190106_000345 7.080 Rx FT8 2 0.1 984 W4EMB N9OY R 529 WI
190106_000345 7.080 Rx FT8 -15 0.3 1034 W9JA W9OY RR73
190106_000345 7.080 Rx FT8 -7 0.1 1175 K2DH KD2GXL 559 NY
190106_000345 7.080 Rx FT8 6 -0.1 1240 CQ RU K7RL CN88
190106_000345 7.080 Rx FT8 -10 0.1 1536 CQ RU W0FRC DM79
190106_000345 7.080 Rx FT8 -7 0.2 1581 VA3WW WA4DYD RR73
190106_000345 7.080 Rx FT8 -9 0.1 1688 DD5ZZ W1QA 73
190106_000345 7.080 Rx FT8 17 0.1 1895 CQ RU KG4W FM17
190106_000345 7.080 Rx FT8 -9 0.1 2002 K2DH KN3ILZ 549 PA
190106_000400 7.080 Rx FT8 -5 0.7 2052 CQ RU WS4WW FM17
190106_000400 7.080 Rx FT8 -9 0.2 2125 NV4G NF3R R 549 PA
190106_000400 7.080 Rx FT8 -5 0.0 941 KW4RTR KO9V RR73
190106_000400 7.080 Rx FT8 -7 0.2 1744 KC1HTT K1LOG R 569 ME

95
lib/ft4/sync4d.f90 Normal file
View File

@ -0,0 +1,95 @@
subroutine sync4d(cd0,i0,ctwk,itwk,sync,sync2)
! Compute sync power for a complex, downsampled FT4 signal.
include 'ft4_params.f90'
parameter(NP=NMAX/NDOWN,NSS=NSPS/NDOWN)
complex cd0(0:NP-1)
complex csynca(4*NSS),csyncb(4*NSS),csyncc(4*NSS),csyncd(4*NSS)
complex csync2(4*NSS)
complex ctwk(4*NSS)
complex z1,z2,z3,z4
complex zz1,zz2,zz3,zz4
logical first
integer icos4a(0:3),icos4b(0:3),icos4c(0:3),icos4d(0:3)
data icos4a/0,1,3,2/
data icos4b/1,0,2,3/
data icos4c/2,3,1,0/
data icos4d/3,2,0,1/
data first/.true./
save first,twopi,csynca,csyncb,csyncc,csyncd,fac
p(z1)=real(z1*fac)**2 + aimag(z1*fac)**2 !Statement function for power
if( first ) then
twopi=8.0*atan(1.0)
k=1
phia=0.0
phib=0.0
phic=0.0
phid=0.0
do i=0,3
dphia=twopi*icos4a(i)/real(NSS)
dphib=twopi*icos4b(i)/real(NSS)
dphic=twopi*icos4c(i)/real(NSS)
dphid=twopi*icos4d(i)/real(NSS)
do j=1,NSS
csynca(k)=cmplx(cos(phia),sin(phia))
csyncb(k)=cmplx(cos(phib),sin(phib))
csyncc(k)=cmplx(cos(phic),sin(phic))
csyncd(k)=cmplx(cos(phid),sin(phid))
phia=mod(phia+dphia,twopi)
phib=mod(phib+dphib,twopi)
phic=mod(phic+dphic,twopi)
phid=mod(phid+dphid,twopi)
k=k+1
enddo
enddo
first=.false.
fac=1.0/(4.0*NSS)
endif
i1=i0 !four Costas arrays
i2=i0+33*NSS
i3=i0+66*NSS
i4=i0+99*NSS
z1=0.
z2=0.
z3=0.
z4=0.
if(itwk.eq.1) csync2=ctwk*csynca !Tweak the frequency
if(i1.ge.0 .and. i1+4*NSS-1.le.NP-1) z1=sum(cd0(i1:i1+4*NSS-1)*conjg(csync2))
if(itwk.eq.1) csync2=ctwk*csyncb !Tweak the frequency
if(i2.ge.0 .and. i2+4*NSS-1.le.NP-1) z2=sum(cd0(i2:i2+4*NSS-1)*conjg(csync2))
if(itwk.eq.1) csync2=ctwk*csyncc !Tweak the frequency
if(i3.ge.0 .and. i3+4*NSS-1.le.NP-1) z3=sum(cd0(i3:i3+4*NSS-1)*conjg(csync2))
if(itwk.eq.1) csync2=ctwk*csyncd !Tweak the frequency
if(i4.ge.0 .and. i4+4*NSS-1.le.NP-1) z4=sum(cd0(i4:i4+4*NSS-1)*conjg(csync2))
sync = p(z1) + p(z2) + p(z3) + p(z4)
sync2=0.0
!do i=1,4
! i1=i0+(i-1)*33*NSS
! if(i.eq.1) csync2=ctwk*csynca
! if(i.eq.2) csync2=ctwk*csyncb
! if(i.eq.3) csync2=ctwk*csyncc
! if(i.eq.4) csync2=ctwk*csyncd
! z1=sum(cd0(i1 :i1+ NSS-1)*conjg(csync2( 1: NSS)))
! z2=sum(cd0(i1+ NSS:i1+2*NSS-1)*conjg(csync2( NSS+1:2*NSS)))
! z3=sum(cd0(i1+2*NSS:i1+3*NSS-1)*conjg(csync2(2*NSS+1:3*NSS)))
! z4=sum(cd0(i1+3*NSS:i1+4*NSS-1)*conjg(csync2(3*NSS+1:4*NSS)))
! sync2=sync2 + abs(z1)**2+abs(z2)**2+abs(z3)**2+abs(z4)**2+&
! 2*abs(z1*conjg(z2)+z2*conjg(z3)+z3*conjg(z4)) + &
! 2*abs(z1*conjg(z3)+z2*conjg(z4)) + &
! 2*abs(z1*conjg(z4))
!enddo
!sync2=sync2*(fac**2)
return
end subroutine sync4d

145
lib/ft4/syncft4.f90 Normal file
View File

@ -0,0 +1,145 @@
subroutine syncft4(iwave,nfa,nfb,syncmin,nfqso,maxcand,s,candidate, &
ncand,sbase)
include 'ft4_params.f90'
! Search over +/- 2.5s relative to 0.5s TX start time.
parameter (JZ=20)
complex cx(0:NH1)
real s(NH1,NHSYM)
real savg(NH1)
real sbase(NH1)
real x(NFFT1)
real sync2d(NH1,-JZ:JZ)
real red(NH1)
real candidate0(3,maxcand)
real candidate(3,maxcand)
real dd(NMAX)
integer jpeak(NH1)
integer indx(NH1)
integer ii(1)
integer*2 iwave(NMAX)
integer icos4(0:3)
data icos4/0,1,3,2/ !Costas 4x4 tone pattern
equivalence (x,cx)
dd=iwave/1e3
! Compute symbol spectra, stepping by NSTEP steps.
savg=0.
tstep=NSTEP/12000.0
df=12000.0/NFFT1
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
call baseline(savg,nfa,nfb,sbase)
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.25/tstep
candidate0=0.
k=0
do i=ia,ib
do j=-JZ,+JZ
ta=0.
tb=0.
tc=0.
t0a=0.
t0b=0.
t0c=0.
do n=0,3
m=j+jstrt+nssy*n
if(m.ge.1.and.m.le.NHSYM) then
ta=ta + s(i+nfos*icos4(n),m)
t0a=t0a + sum(s(i:i+nfos*3:nfos,m))
endif
tb=tb + s(i+nfos*icos4(n),m+nssy*36)
t0b=t0b + sum(s(i:i+nfos*3:nfos,m+nssy*36))
if(m+nssy*72.le.NHSYM) then
tc=tc + s(i+nfos*icos4(n),m+nssy*72)
t0c=t0c + sum(s(i:i+nfos*3:nfos,m+nssy*72))
endif
enddo
t=ta+tb+tc
t0=t0a+t0b+t0c
t0=(t0-t)/3.0
sync_abc=t/t0
t=tb+tc
t0=t0b+t0c
t0=(t0-t)/3.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)
enddo
iz=ib-ia+1
call indexx(red(ia:ib),iz,indx)
ibase=indx(nint(0.40*iz)) - 1 + ia
if(ibase.lt.1) ibase=1
if(ibase.gt.nh1) ibase=nh1
base=red(ibase)
red=red/base
do i=1,min(maxcand,iz)
n=ia + indx(iz+1-i) - 1
if(red(n).lt.syncmin.or.isnan(red(n)).or.k.eq.maxcand) exit
k=k+1
! candidate0(1,k)=n*df+37.5*1.5
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
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
if( candidate0(3,j) .ge. syncmin ) then
candidate(2:3,k)=candidate0(2:3,j)
candidate(1,k)=abs(candidate0(1,j))
k=k+1
endif
enddo
ncand=k-1
return
end subroutine syncft4

130
lib/ft8/ft8sim_gfsk.f90 Normal file
View File

@ -0,0 +1,130 @@
program ft8sim_gfsk
! Generate simulated "type 2" ft8 files
! Output is saved to a *.wav file.
use wavhdr
use packjt77
include 'ft8_params.f90' !Set various constants
parameter (NWAVE=NN*NSPS)
type(hdr) h !Header for .wav file
character arg*12,fname*17
character msg37*37,msgsent37*37
character c77*77
complex c0(0:NMAX-1)
complex c(0:NMAX-1)
complex cwave(0:NWAVE-1)
real wave(NMAX)
real xjunk(NWAVE)
integer itone(NN)
integer*1 msgbits(77)
integer*2 iwave(NMAX) !Generated full-length waveform
! Get command-line argument(s)
nargs=iargc()
if(nargs.ne.7) then
print*,'Usage: ft8sim "message" f0 DT fdop del nfiles snr'
print*,'Examples: ft8sim "K1ABC W9XYZ EN37" 1500.0 0.0 0.1 1.0 10 -18'
print*,' ft8sim "WA9XYZ/R KA1ABC/R FN42" 1500.0 0.0 0.1 1.0 10 -18'
print*,' ft8sim "K1ABC RR73; W9XYZ <KH1/KH7Z> -11" 300 0 0 0 25 1 -10'
go to 999
endif
call getarg(1,msg37) !Message to be transmitted
call getarg(2,arg)
read(arg,*) f0 !Frequency (only used for single-signal)
call getarg(3,arg)
read(arg,*) xdt !Time offset from nominal (s)
call getarg(4,arg)
read(arg,*) fspread !Watterson frequency spread (Hz)
call getarg(5,arg)
read(arg,*) delay !Watterson delay (ms)
call getarg(6,arg)
read(arg,*) nfiles !Number of files
call getarg(7,arg)
read(arg,*) snrdb !SNR_2500
nsig=1
if(f0.lt.100.0) then
nsig=f0
f0=1500
endif
nfiles=abs(nfiles)
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
! Source-encode, then get itone()
i3=-1
n3=-1
call pack77(msg37,i3,n3,c77)
call genft8(msg37,i3,n3,msgsent37,msgbits,itone)
call gen_ft8wave(itone,NN,NSPS,fs,f0,cwave,xjunk,1,NWAVE) !Generate complex cwave
write(*,*)
write(*,'(a23,a37,3x,a7,i1,a1,i1)') 'New Style FT8 Message: ',msgsent37,'i3.n3: ',i3,'.',n3
write(*,1000) f0,xdt,txt,snrdb,bw
1000 format('f0:',f9.3,' DT:',f6.2,' TxT:',f6.1,' SNR:',f6.1, &
' BW:',f4.1)
write(*,*)
if(i3.eq.1) then
write(*,*) ' mycall hiscall hisgrid'
write(*,'(28i1,1x,i1,1x,28i1,1x,i1,1x,i1,1x,15i1,1x,3i1)') msgbits(1:77)
else
write(*,'(a14)') 'Message bits: '
write(*,'(77i1)') msgbits
endif
write(*,*)
write(*,'(a17)') 'Channel symbols: '
write(*,'(79i1)') itone
write(*,*)
call sgran()
msg0=msg
do ifile=1,nfiles
c0=0.
c0(0:NWAVE-1)=cwave
c0=cshift(c0,-nint((xdt+0.5)/dt))
if(fspread.ne.0.0 .or. delay.ne.0.0) call watterson(c0,NMAX,NWAVE,fs,delay,fspread)
c=sig*c0
wave=imag(c)
peak=maxval(abs(wave))
nslots=1
if(snrdb.lt.90) then
do i=1,NMAX !Add gaussian noise at specified SNR
xnoise=gran()
wave(i)=wave(i) + xnoise
enddo
endif
gain=100.0
if(snrdb.lt.90.0) then
wave=gain*wave
else
datpk=maxval(abs(wave))
fac=32766.9/datpk
wave=fac*wave
endif
if(any(abs(wave).gt.32767.0)) print*,"Warning - data will be clipped."
iwave=nint(wave)
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_gfsk

74
lib/ft8/gen_ft8wave.f90 Normal file
View File

@ -0,0 +1,74 @@
subroutine gen_ft8wave(itone,nsym,nsps,fsample,f0,cwave,wave,icmplx,nwave)
!
! generate ft8 waveform using Gaussian-filtered frequency pulses.
!
parameter(MAX_SECONDS=20)
real wave(nwave)
complex cwave(nwave)
real pulse(23040)
real dphi(0:(nsym+2)*nsps-1)
integer itone(nsym)
logical first
data first/.true./
save pulse,first,twopi,dt,hmod
if(first) then
twopi=8.0*atan(1.0)
dt=1.0/fsample
hmod=1.0
bt=2.0
! Compute the frequency-smoothing pulse
do i=1,3*nsps
tt=(i-1.5*nsps)/real(nsps)
pulse(i)=gfsk_pulse(bt,tt)
enddo
first=.false.
endif
! Compute the smoothed frequency waveform.
! Length = (nsym+2)*nsps samples, first and last symbols extended
dphi_peak=twopi*hmod/real(nsps)
dphi=0.0
do j=1,nsym
ib=(j-1)*nsps
ie=ib+3*nsps-1
dphi(ib:ie) = dphi(ib:ie) + dphi_peak*pulse(1:3*nsps)*itone(j)
enddo
! Add dummy symbols at beginning and end with tone values equal to 1st and last symbol, respectively
dphi(0:2*nsps-1)=dphi(0:2*nsps-1)+dphi_peak*itone(1)*pulse(nsps+1:3*nsps)
dphi(nsym*nsps:(nsym+2)*nsps-1)=dphi(nsym*nsps:(nsym+2)*nsps-1)+dphi_peak*itone(nsym)*pulse(1:2*nsps)
! Calculate and insert the audio waveform
phi=0.0
dphi = dphi + twopi*f0*dt !Shift frequency up by f0
wave=0.
k=0
do j=nsps,nsps+nwave-1 !Don't include dummy symbols
k=k+1
if(icmplx.eq.0) then
wave(k)=sin(phi)
else
cwave(k)=cmplx(cos(phi),sin(phi))
endif
phi=mod(phi+dphi(j),twopi)
enddo
! Apply envelope shaping to the first and last symbols
nramp=nint(nsps/8.0)
if(icmplx.eq.0) then
wave(1:nramp)=wave(1:nramp) * &
(1.0-cos(twopi*(/(i,i=0,nramp-1)/)/(2.0*nramp)))/2.0
k1=nsym*nsps-nramp+1
wave(k1:k1+nramp-1)=wave(k1:k1+nramp-1) * &
(1.0+cos(twopi*(/(i,i=0,nramp-1)/)/(2.0*nramp)))/2.0
else
cwave(1:nramp)=cwave(1:nramp) * &
(1.0-cos(twopi*(/(i,i=0,nramp-1)/)/(2.0*nramp)))/2.0
k1=nsym*nsps-nramp+1
cwave(k1:k1+nramp-1)=cwave(k1:k1+nramp-1) * &
(1.0+cos(twopi*(/(i,i=0,nramp-1)/)/(2.0*nramp)))/2.0
endif
return
end subroutine gen_ft8wave

View File

@ -46,7 +46,11 @@ subroutine watterson(c,npts,nsig,fs,delay,fspread)
endif
nshift=nint(0.001*delay*fs)
c2(0:npts-1)=cshift(c(0:npts-1),nshift)
if(delay.gt.0.0) then
c2(0:npts-1)=cshift(c(0:npts-1),-nshift) !negative shifts are right shifts
else
c2(0:npts-1)=0.0
endif
sq=0.
do i=0,npts-1
if(nonzero.gt.1) then

15
lib/nuttal_window.f90 Normal file
View File

@ -0,0 +1,15 @@
subroutine nuttal_window(win,n)
real win(n)
pi=4.0*atan(1.0)
a0=0.3635819
a1=-0.4891775;
a2=0.1365995;
a3=-0.0106411;
do i=1,n
win(i)=a0+a1*cos(2*pi*(i-1)/(n))+ &
a2*cos(4*pi*(i-1)/(n))+ &
a3*cos(6*pi*(i-1)/(n))
enddo
return
end subroutine nuttal_window

View File

@ -1,13 +1,14 @@
subroutine symspec(shared_data,k,ntrperiod,nsps,ingain,nminw,pxdb,s, &
df3,ihsym,npts8,pxdbmax)
subroutine symspec(shared_data,k,ntrperiod,nsps,ingain,bLowSidelobes, &
nminw,pxdb,s,df3,ihsym,npts8,pxdbmax)
! Input:
! k pointer to the most recent new data
! ntrperiod T/R sequence length, minutes
! nsps samples per symbol, at 12000 Hz
! ndiskdat 0/1 to indicate if data from disk
! nb 0/1 status of noise blanker (off/on)
! nbslider NB setting, 0-100
! k pointer to the most recent new data
! ntrperiod T/R sequence length, minutes
! nsps samples per symbol, at 12000 Hz
! bLowSidelobes true to use windowed FFTs
! ndiskdat 0/1 to indicate if data from disk
! nb 0/1 status of noise blanker (off/on)
! nbslider NB setting, 0-100
! Output:
! pxdb raw power (0-90 dB)
@ -29,6 +30,7 @@ subroutine symspec(shared_data,k,ntrperiod,nsps,ingain,nminw,pxdb,s, &
real*4 tmp(NSMAX)
complex cx(0:MAXFFT3/2)
integer nch(7)
logical*1 bLowSidelobes
common/spectra/syellow(NSMAX),ref(0:3456),filter(0:3456)
data k0/99999999/,nfft3z/0/
@ -48,11 +50,8 @@ subroutine symspec(shared_data,k,ntrperiod,nsps,ingain,nminw,pxdb,s, &
if(nfft3.ne.nfft3z) then
! Compute new window
pi=4.0*atan(1.0)
width=0.25*nsps
do i=1,nfft3
z=(i-nfft3/2)/width
w3(i)=exp(-z*z)
enddo
w3=0
call nuttal_window(w3,nfft3)
nfft3z=nfft3
endif
@ -86,10 +85,10 @@ subroutine symspec(shared_data,k,ntrperiod,nsps,ingain,nminw,pxdb,s, &
enddo
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
if(bLowSidelobes) xc(0:nfft3-1)=w3(1:nfft3)*xc(0:nfft3-1) !Apply window
call four2a(xc,nfft3,1,-1,0) !Real-to-complex FFT
df3=12000.0/nfft3 !JT9-1: 0.732 Hz = 0.42 * tone spacing
df3=12000.0/nfft3 !JT9: 0.732 Hz = 0.42 * tone spacing
iz=min(NSMAX,nint(5000.0/df3))
fac=(1.0/nfft3)**2
do i=1,iz

View File

@ -37,17 +37,17 @@ module wavhdr
subroutine set_wsjtx_wav_params(fMHz,mode,nsubmode,ntrperiod,id2)
parameter (NBANDS=23,NMODES=11)
parameter (NBANDS=23,NMODES=13)
character*8 mode,modes(NMODES)
integer*2 id2(4)
integer iperiod(7)
integer iperiod(8)
real fband(NBANDS)
data fband/0.137,0.474,1.8,3.5,5.1,7.0,10.14,14.0,18.1,21.0,24.9, &
28.0,50.0,144.0,222.0,432.0,902.0,1296.0,2304.0,3400.0, &
5760.0,10368.0,24048.0/
data modes/'Echo','FSK441','ISCAT','JT4','JT65','JT6M','JT9', &
'JT9+JT65','JTMS','JTMSK','WSPR'/
data iperiod/5,10,15,30,60,120,900/
'JT9+JT65','JTMS','JTMSK','WSPR','FT8','FT2'/
data iperiod/5,10,15,30,60,120,900,0/
dmin=1.e30
iband=0
@ -64,7 +64,7 @@ module wavhdr
enddo
ip=0
do i=1,7
do i=1,8
if(ntrperiod.eq.iperiod(i)) ip=i
enddo
@ -78,15 +78,15 @@ module wavhdr
subroutine get_wsjtx_wav_params(id2,band,mode,nsubmode,ntrperiod,ok)
parameter (NBANDS=23,NMODES=11)
parameter (NBANDS=23,NMODES=13)
character*8 mode,modes(NMODES)
character*6 band,bands(NBANDS)
integer*2 id2(4)
integer iperiod(7)
integer iperiod(8)
logical ok
data modes/'Echo','FSK441','ISCAT','JT4','JT65','JT6M','JT9', &
'JT9+JT65','JTMS','JTMSK','WSPR'/
data iperiod/5,10,15,30,60,120,900/
'JT9+JT65','JTMS','JTMSK','WSPR','FT8','FT2'/
data iperiod/5,10,15,30,60,120,900,0/
data bands/'2190m','630m','160m','80m','60m','40m','30m','20m', &
'17m','15m','12m','10m','6m','2m','1.25m','70cm','33cm', &
'23cm','13cm','9cm','6cm','3cm','1.25cm'/
@ -95,7 +95,7 @@ module wavhdr
if(id2(1).lt.1 .or. id2(1).gt.NBANDS) ok=.false.
if(id2(2).lt.1 .or. id2(2).gt.NMODES) ok=.false.
if(id2(3).lt.1 .or. id2(3).gt.8) ok=.false.
if(id2(4).lt.1 .or. id2(4).gt.7) ok=.false.
if(id2(4).lt.1 .or. id2(4).gt.8) ok=.false.
if(ok) then
band=bands(id2(1))

View File

@ -1,11 +1,10 @@
CC = gcc
#CC = clang-3.5
FC = gfortran
CFLAGS= -I/usr/include -Wall -Wno-missing-braces -O3 -ffast-math
CFLAGS= -I/usr/include -Wall -Wno-missing-braces -Wno-unused-result -O3 -ffast-math
LDFLAGS = -L/usr/lib
FFLAGS = -O2 -Wall -Wno-conversion
LIBS = -lfftw3f -lm
FFLAGS = -O2 -Wall -Wno-conversion
LIBS = -lfftw3f -lm -lgfortran
# Default rules
%.o: %.c $(DEPS)
@ -14,21 +13,27 @@ LIBS = -lfftw3f -lm
${FC} ${FFLAGS} -c $<
%.o: %.F
${FC} ${FFLAGS} -c $<
%.o: %.f90
%.o: %.f90
${FC} ${FFLAGS} -c $<
%.o: %.F90
${FC} ${FFLAGS} -c $<
all: wsprd wsprsim wsprd_exp
all: wsprd wsprsim
DEPS = wsprsim_utils.h wsprd_utils.h fano.h jelinek.h nhash.h
OBJS1 = wsprd.o wsprsim_utils.o wsprd_utils.o tab.o fano.o jelinek.o nhash.o
indexx.o: ../indexx.f90
${FC} -o indexx.o ${FFLAGS} -c ../indexx.f90
OBJS1 = wsprd.o wsprsim_utils.o wsprd_utils.o tab.o fano.o jelinek.o nhash.o indexx.o osdwspr.o
wsprd: $(OBJS1)
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)
OBJS2 = wsprsim.o wsprsim_utils.o wsprd_utils.o tab.o fano.o nhash.o
wsprsim: $(OBJS2)
$(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) $(LIBS)
clean:
rm *.o wsprd wsprsim
$(RM) *.o wsprd wsprsim

View File

@ -91,8 +91,8 @@ namespace
int main(int argc, char *argv[])
{
// Add timestamps to all debug messages
qSetMessagePattern ("[%{time yyyyMMdd HH:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{message}");
// ### Add timestamps to all debug messages
// qSetMessagePattern ("[%{time yyyyMMdd HH:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{message}");
init_random_seed ();

View File

@ -68,6 +68,7 @@ namespace
{7038600, Modes::WSPR, IARURegions::ALL},
{7074000, Modes::FT8, IARURegions::ALL},
{7076000, Modes::JT65, IARURegions::ALL},
{7078000, Modes::FT4, IARURegions::ALL},
{7078000, Modes::JT9, IARURegions::ALL},
{10136000, Modes::FT8, IARURegions::ALL},
@ -78,6 +79,7 @@ namespace
{14095600, Modes::WSPR, IARURegions::ALL},
{14074000, Modes::FT8, IARURegions::ALL},
{14076000, Modes::JT65, IARURegions::ALL},
{14078000, Modes::FT4, IARURegions::ALL},
{14078000, Modes::JT9, IARURegions::ALL},
{18100000, Modes::FT8, IARURegions::ALL},

View File

@ -23,7 +23,8 @@ namespace
"MSK144",
"QRA64",
"FreqCal",
"FT8"
"FT8",
"FT4"
};
std::size_t constexpr mode_names_size = sizeof (mode_names) / sizeof (mode_names[0]);
}

View File

@ -49,6 +49,7 @@ public:
QRA64,
FreqCal,
FT8,
FT4,
MODES_END_SENTINAL_AND_COUNT // this must be last
};
Q_ENUM (Mode)

View File

@ -1,4 +1,5 @@
set (SAMPLE_FILES
FT4/190106_000115.wav
FT8/181201_180245.wav
ISCAT/ISCAT-A/VK7MO_110401_235515.wav
ISCAT/ISCAT-B/K0AWU_100714_115000.wav

Binary file not shown.

View File

@ -101,7 +101,7 @@ void SoundOutput::restart (QIODevice * source)
m_stream->setBufferSize (m_stream->format().bytesForDuration((m_msBuffered ? m_msBuffered : MS_BUFFERED) * 1000));
// qDebug() << "B" << m_stream->bufferSize() <<
// m_stream->periodSize() << m_stream->notifyInterval();
m_stream->setCategory ("production");
m_stream->start (source);
}

View File

@ -454,6 +454,7 @@ void DisplayText::displayDecodedText(DecodedText const& decodedText, QString con
void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 txFreq,bool bFastMode)
{
QString t1=" @ ";
if(modeTx=="FT4") t1=" + ";
if(modeTx=="FT8") t1=" ~ ";
if(modeTx=="JT4") t1=" $ ";
if(modeTx=="JT65") t1=" # ";
@ -461,7 +462,7 @@ void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 tx
QString t2;
t2.sprintf("%4d",txFreq);
QString t;
if(bFastMode or modeTx=="FT8") {
if(bFastMode or modeTx=="FT8" or modeTx=="FT4") {
t = QDateTime::currentDateTimeUtc().toString("hhmmss") + \
" Tx " + t2 + t1 + text;
} else if(modeTx.mid(0,6)=="FT8fox") {

View File

@ -86,8 +86,8 @@
extern "C" {
//----------------------------------------------------- C and Fortran routines
void symspec_(struct dec_data *, int* k, int* ntrperiod, int* nsps, int* ingain,
int* minw, float* px, float s[], float* df3, int* nhsym, int* npts8,
float *m_pxmax);
bool* bLowSidelobes, int* minw, float* px, float s[], float* df3,
int* nhsym, int* npts8, float *m_pxmax);
void hspec_(short int d2[], int* k, int* nutc0, int* ntrperiod, int* nrxfreq, int* ntol,
int* nContest, bool* bmsk144, bool* btrain, double const pcoeffs[], int* ingain,
@ -99,6 +99,15 @@ extern "C" {
void genft8_(char* msg, int* i3, int* n3, char* msgsent, char ft8msgbits[],
int itone[], fortran_charlen_t, fortran_charlen_t);
void genft4_(char* msg, int* ichk, char* msgsent, int itone[],
fortran_charlen_t, fortran_charlen_t);
void gen_ft8wave_(int itone[], int* nsym, int* nsps, float* fsample, float* f0,
float xjunk[], float wave[], int* icmplx, int* nwave);
void gen_ft4wave_(int itone[], int* nsym, int* nsps, float* fsample, float* f0,
float wave[], int* nwave);
void gen4_(char* msg, int* ichk, char* msgsent, int itone[],
int* itext, fortran_charlen_t, fortran_charlen_t);
@ -159,6 +168,14 @@ extern "C" {
void plotsave_(float swide[], int* m_w , int* m_h1, int* irow);
void chkcall_(char* w, char* basc_call, bool cok, int len1, int len2);
void ft4_decode_(char* cdatetime, float* tbuf, int* nfa, int* nfb, int* nQSOProgress,
int* nContest, int* nfqso, short int id[], int* ndecodes, char* mycall,
char* hiscall, char* cqstr, char* line, char* ddir, int len1,
int len2, int len3, int len4, int len5, int len6);
void get_ft4msg_(int* idecode, char* line, int len);
}
int volatile itone[NUM_ISCAT_SYMBOLS]; //Audio tones for all Tx symbols
@ -555,6 +572,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
on_EraseButton_clicked ();
QActionGroup* modeGroup = new QActionGroup(this);
ui->actionFT4->setActionGroup(modeGroup);
ui->actionFT8->setActionGroup(modeGroup);
ui->actionJT9->setActionGroup(modeGroup);
ui->actionJT65->setActionGroup(modeGroup);
@ -730,6 +748,13 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect(&m_guiTimer, &QTimer::timeout, this, &MainWindow::guiUpdate);
m_guiTimer.start(100); //### Don't change the 100 ms! ###
FT4_TxTimer.setSingleShot(true);
connect(&FT4_TxTimer, &QTimer::timeout, this, &MainWindow::stopTx);
FT4_WriteTxTimer.setSingleShot(true);
connect(&FT4_WriteTxTimer, &QTimer::timeout, this, &MainWindow::FT4_writeTx);
ptt0Timer.setSingleShot(true);
connect(&ptt0Timer, &QTimer::timeout, this, &MainWindow::stopTx2);
@ -783,6 +808,9 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
ui->TxPowerComboBox->addItem(t);
}
m_dateTimeRcvdRR73=QDateTime::currentDateTimeUtc();
m_dateTimeSentTx3=QDateTime::currentDateTimeUtc();
ui->labAz->setStyleSheet("border: 0px;");
ui->labAz->setText("");
auto t = "UTC dB DT Freq Message";
@ -885,6 +913,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
if(m_bFast9) m_bFastMode=true;
ui->cbFast9->setChecked(m_bFast9 or m_bFastMode);
if(m_mode=="FT4") on_actionFT4_triggered();
if(m_mode=="FT8") on_actionFT8_triggered();
if(m_mode=="JT4") on_actionJT4_triggered();
if(m_mode=="JT9") on_actionJT9_triggered();
@ -959,7 +988,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
if(QCoreApplication::applicationVersion().contains("-devel") or
QCoreApplication::applicationVersion().contains("-rc")) {
// QTimer::singleShot (0, this, SLOT (not_GA_warning_message ()));
QTimer::singleShot (0, this, SLOT (not_GA_warning_message ()));
}
if(!ui->cbMenus->isChecked()) {
@ -974,18 +1003,12 @@ void MainWindow::not_GA_warning_message ()
{
MessageBox::critical_message (this,
"<b><p align=\"center\">"
"IMPORTANT: New protocols for the FT8 and MSK144 modes "
"became the world&#8209;wide standards on December&nbsp;10,&nbsp;2018."
, "<p align=\"center\">"
"WSJT&#8209;X&nbsp;2.0 cannot communicate in these modes with other "
"stations using WSJT&#8209;X&nbsp;v1.9.1&nbsp;or&nbsp;earlier."
"<p align=\"center\">"
"Please help by urging everyone to upgrade to WSJT&#8209;X 2.0 "
"<nobr>no later than January&nbsp;1,&nbsp;2019.</nobr>");
// QDateTime now=QDateTime::currentDateTime();
// QDateTime timeout=QDateTime(QDate(2018,12,31));
// if(now.daysTo(timeout) < 0) Q_EMIT finished();
"This is a pre-release version of WSJT-X 2.1.0 made "
"available for testing purposes. It will become nonfunctional "
"after May 1, 2019.");
QDateTime now=QDateTime::currentDateTime();
QDateTime timeout=QDateTime(QDate(2019,5,1));
if(now.daysTo(timeout) < 0) Q_EMIT finished();
}
void MainWindow::initialize_fonts ()
@ -1349,14 +1372,17 @@ void MainWindow::dataSink(qint64 frames)
int nsps=m_nsps;
if(m_bFastMode) nsps=6912;
int nsmo=m_wideGraph->smoothYellow()-1;
symspec_(&dec_data,&k,&trmin,&nsps,&m_inGain,&nsmo,&m_px,s,&m_df3,&m_ihsym,&m_npts8,&m_pxmax);
bool bLowSidelobes=m_config.lowSidelobes();
symspec_(&dec_data,&k,&trmin,&nsps,&m_inGain,&bLowSidelobes,&nsmo,&m_px,s,
&m_df3,&m_ihsym,&m_npts8,&m_pxmax);
if(m_mode=="WSPR") wspr_downsample_(dec_data.d2,&k);
if(m_ihsym <=0) return;
if(ui) ui->signal_meter_widget->setValue(m_px,m_pxmax); // Update thermometer
if(m_monitoring || m_diskData) {
m_wideGraph->dataSink2(s,m_df3,m_ihsym,m_diskData);
}
if(m_mode=="MSK144") return;
if(m_mode=="FT4") ft4_rx(k);
if(m_mode=="MSK144" or m_mode=="FT4") return;
fixStop();
if (m_mode == "FreqCal"
@ -1450,7 +1476,7 @@ void MainWindow::dataSink(qint64 frames)
// the following is potential a threading hazard - not a good
// idea to pass pointer to be processed in another thread
m_saveWAVWatcher.setFuture (QtConcurrent::run (std::bind (&MainWindow::save_wave_file,
this, m_fnameWE, &dec_data.d2[0], m_TRperiod, m_config.my_callsign(),
this, m_fnameWE, &dec_data.d2[0], m_TRperiod*12000, m_config.my_callsign(),
m_config.my_grid(), m_mode, m_nSubMode, m_freqNominal, m_hisCall, m_hisGrid)));
if (m_mode=="WSPR") {
QString c2name_string {m_fnameWE + ".c2"};
@ -1509,7 +1535,7 @@ void MainWindow::startP1()
p1.start(m_cmndP1);
}
QString MainWindow::save_wave_file (QString const& name, short const * data, int seconds,
QString MainWindow::save_wave_file (QString const& name, short const * data, int samples,
QString const& my_callsign, QString const& my_grid, QString const& mode, qint32 sub_mode,
Frequency frequency, QString const& his_call, QString const& his_grid) const
{
@ -1545,7 +1571,7 @@ QString MainWindow::save_wave_file (QString const& name, short const * data, int
BWFFile wav {format, file_name, list_info};
if (!wav.open (BWFFile::WriteOnly)
|| 0 > wav.write (reinterpret_cast<char const *> (data)
, sizeof (short) * seconds * format.sampleRate ()))
, sizeof (short) * samples))
{
return file_name + ": " + wav.errorString ();
}
@ -1557,7 +1583,6 @@ void MainWindow::fastSink(qint64 frames)
{
int k (frames);
bool decodeNow=false;
if(k < m_k0) { //New sequence ?
memcpy(fast_green2,fast_green,4*703); //Copy fast_green[] to fast_green2[]
memcpy(fast_s2,fast_s,4*703*64); //Copy fast_s[] into fast_s2[]
@ -1661,7 +1686,7 @@ void MainWindow::fastSink(qint64 frames)
// the following is potential a threading hazard - not a good
// idea to pass pointer to be processed in another thread
m_saveWAVWatcher.setFuture (QtConcurrent::run (std::bind (&MainWindow::save_wave_file,
this, m_fnameWE, &dec_data.d2[0], m_TRperiod, m_config.my_callsign(),
this, m_fnameWE, &dec_data.d2[0], m_TRperiod*12000, m_config.my_callsign(),
m_config.my_grid(), m_mode, m_nSubMode, m_freqNominal, m_hisCall, m_hisGrid)));
}
if(m_mode!="MSK144") {
@ -1764,6 +1789,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
ui->actionEnable_AP_JT65->setVisible(false);
}
if(m_config.special_op_id()!=nContest0) ui->tx1->setEnabled(true);
chkFT4();
}
}
@ -1888,6 +1914,14 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
break;
case Qt::Key_F1:
if(bAltF1F5) {
if(m_mode=="FT4") {
if(e->modifiers() & Qt::ControlModifier) {
ft4_tx(1);
} else {
ft4_tx(6);
}
return;
}
auto_tx_mode(true);
on_txb6_clicked();
return;
@ -1897,6 +1931,10 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
}
case Qt::Key_F2:
if(bAltF1F5) {
if(m_mode=="FT4") {
ft4_tx(2);
return;
}
auto_tx_mode(true);
on_txb2_clicked();
return;
@ -1906,6 +1944,10 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
}
case Qt::Key_F3:
if(bAltF1F5) {
if(m_mode=="FT4") {
ft4_tx(3);
return;
}
auto_tx_mode(true);
on_txb3_clicked();
return;
@ -1915,6 +1957,10 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
}
case Qt::Key_F4:
if(bAltF1F5) {
if(m_mode=="FT4") {
ft4_tx(4);
return;
}
auto_tx_mode(true);
on_txb4_clicked();
return;
@ -1925,6 +1971,10 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
}
case Qt::Key_F5:
if(bAltF1F5) {
if(m_mode=="FT4") {
ft4_tx(5);
return;
}
auto_tx_mode(true);
on_txb5_clicked();
return;
@ -1947,7 +1997,9 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
n=11;
if(e->modifiers() & Qt::ControlModifier) n+=100;
if(e->modifiers() & Qt::ShiftModifier) {
ui->TxFreqSpinBox->setValue(ui->TxFreqSpinBox->value()-60);
int offset=60;
if(m_mode=="FT4") offset=120;
ui->TxFreqSpinBox->setValue(ui->TxFreqSpinBox->value()-offset);
} else{
bumpFqso(n);
}
@ -1961,7 +2013,9 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
n=12;
if(e->modifiers() & Qt::ControlModifier) n+=100;
if(e->modifiers() & Qt::ShiftModifier) {
ui->TxFreqSpinBox->setValue(ui->TxFreqSpinBox->value()+60);
int offset=60;
if(m_mode=="FT4") offset=120;
ui->TxFreqSpinBox->setValue(ui->TxFreqSpinBox->value()+offset);
} else {
bumpFqso(n);
}
@ -1976,7 +2030,7 @@ void MainWindow::keyPressEvent (QKeyEvent * e)
return;
case Qt::Key_X:
if(e->modifiers() & Qt::AltModifier) {
foxTest();
// foxTest();
return;
}
case Qt::Key_E:
@ -3083,7 +3137,6 @@ void MainWindow::readFromStdout() //readFromStdout
//Right (Rx Frequency) window
bool bDisplayRight=bAvgMsg;
int audioFreq=decodedtext.frequencyOffset();
if(m_mode=="FT8") {
auto const& parts = decodedtext.string().remove("<").remove(">")
.split (' ', QString::SkipEmptyParts);
@ -3377,7 +3430,7 @@ void MainWindow::guiUpdate()
double tx1=0.0;
double tx2=txDuration;
if(m_mode=="FT8") icw[0]=0; //No CW ID in FT8 mode
if(m_mode=="FT8" or m_mode=="FT4") icw[0]=0; //No CW ID in FT4 or FT8 mode
if((icw[0]>0) and (!m_bFast9)) tx2 += icw[0]*2560.0/48000.0; //Full length including CW ID
if(tx2>m_TRperiod) tx2=m_TRperiod;
@ -3535,7 +3588,7 @@ void MainWindow::guiUpdate()
Q_EMIT m_config.transceiver_ptt (true); //Assert the PTT
m_tx_when_ready = true;
}
if(!m_bTxTime and !m_tune) m_btxok=false; //Time to stop transmitting
if(!m_bTxTime and !m_tune and m_mode!="FT4") m_btxok=false; //Time to stop transmitting
}
if(m_mode.startsWith ("WSPR") and
@ -3677,10 +3730,10 @@ void MainWindow::guiUpdate()
}
}
m_currentMessage = QString::fromLatin1(msgsent);
if(m_mode!="FT4") m_currentMessage = QString::fromLatin1(msgsent);
m_bCallingCQ = CALLING == m_QSOProgress
|| m_currentMessage.contains (QRegularExpression {"^(CQ|QRZ) "});
if(m_mode=="FT8") {
if(m_mode=="FT8" or m_mode=="FT4") {
if(m_bCallingCQ && ui->cbFirst->isVisible () && ui->cbFirst->isChecked ()) {
ui->cbFirst->setStyleSheet("QCheckBox{color:red}");
} else {
@ -3787,37 +3840,38 @@ void MainWindow::guiUpdate()
}
}
if (g_iptt == 1 && m_iptt0 == 0)
{
auto const& current_message = QString::fromLatin1 (msgsent);
if(m_config.watchdog () && !m_mode.startsWith ("WSPR")
&& current_message != m_msgSent0) {
tx_watchdog (false); // in case we are auto sequencing
m_msgSent0 = current_message;
}
if (g_iptt == 1 && m_iptt0 == 0) {
auto const& current_message = QString::fromLatin1 (msgsent);
if(m_config.watchdog () && !m_mode.startsWith ("WSPR")
&& current_message != m_msgSent0) {
tx_watchdog (false); // in case we are auto sequencing
m_msgSent0 = current_message;
}
if(m_mode!="FT4") {
if(!m_tune) write_all("Tx",m_currentMessage);
if (m_config.TX_messages () && !m_tune && SpecOp::FOX!=m_config.special_op_id()) {
ui->decodedTextBrowser2->displayTransmittedText(current_message, m_modeTx,
ui->TxFreqSpinBox->value(),m_bFastMode);
ui->TxFreqSpinBox->value(),m_bFastMode);
}
switch (m_ntx)
{
case 1: m_QSOProgress = REPLYING; break;
case 2: m_QSOProgress = REPORT; break;
case 3: m_QSOProgress = ROGER_REPORT; break;
case 4: m_QSOProgress = ROGERS; break;
case 5: m_QSOProgress = SIGNOFF; break;
case 6: m_QSOProgress = CALLING; break;
default: break; // determined elsewhere
}
m_transmitting = true;
transmitDisplay (true);
statusUpdate ();
}
switch (m_ntx)
{
case 1: m_QSOProgress = REPLYING; break;
case 2: m_QSOProgress = REPORT; break;
case 3: m_QSOProgress = ROGER_REPORT; break;
case 4: m_QSOProgress = ROGERS; break;
case 5: m_QSOProgress = SIGNOFF; break;
case 6: m_QSOProgress = CALLING; break;
default: break; // determined elsewhere
}
m_transmitting = true;
transmitDisplay (true);
statusUpdate ();
}
if(!m_btxok && m_btxok0 && g_iptt==1) stopTx();
if(m_startAnother) {
@ -3831,7 +3885,7 @@ void MainWindow::guiUpdate()
}
}
if(m_mode=="FT8" or m_mode=="MSK144") {
if(m_mode=="FT8" or m_mode=="MSK144" or m_mode=="FT4") {
if(ui->txrb1->isEnabled() and
(SpecOp::NA_VHF==m_config.special_op_id() or
SpecOp::FIELD_DAY==m_config.special_op_id() or
@ -3847,6 +3901,7 @@ void MainWindow::guiUpdate()
//Once per second:
if(nsec != m_sec0) {
// qDebug() << "cc onesec" << (SpecOp::RTTY == m_config.special_op_id());
// if((!m_msgAvgWidget or (m_msgAvgWidget and !m_msgAvgWidget->isVisible()))
// and (SpecOp::NONE < m_config.special_op_id()) and (SpecOp::HOUND > m_config.special_op_id())) on_actionFox_Log_triggered();
if(m_freqNominal!=0 and m_freqNominal<50000000 and m_config.enable_VHF_features()) {
@ -3861,12 +3916,13 @@ void MainWindow::guiUpdate()
//To keep calling Fox, Hound must reactivate Enable Tx at least once every 2 minutes
if(tHound >= 120 and m_ntx==1) auto_tx_mode(false);
}
progressBar.setVisible(!(m_mode=="FT4"));
if(m_auto and m_mode=="Echo" and m_bEchoTxOK) {
progressBar.setMaximum(6);
progressBar.setValue(int(m_s6));
}
if(m_mode!="Echo") {
if(m_mode!="Echo" and m_mode!="FT4") {
if(m_monitoring or m_transmitting) {
progressBar.setMaximum(m_TRperiod);
int isec=int(fmod(tsec,m_TRperiod));
@ -3905,6 +3961,7 @@ void MainWindow::guiUpdate()
if(SpecOp::FOX==m_config.special_op_id() and ui->tabWidget->currentIndex()==2 and foxcom_.nslots==1) {
t=m_fm1.trimmed();
}
if(m_mode=="FT4") t="Tx: "+ m_currentMessage;
tx_status_label.setText(t.trimmed());
}
}
@ -3980,7 +4037,6 @@ void MainWindow::startTx2()
ui->decodedTextBrowser->appendText(t);
}
write_all("Tx",m_currentMessage);
// write_transmit_entry ("ALL_WSPR.TXT");
}
}
}
@ -4009,13 +4065,13 @@ void MainWindow::stopTx2()
on_stopTxButton_clicked ();
m_nTx73 = 0;
}
if(m_mode.startsWith ("WSPR") and m_ntr==-1 and !m_tuneup) {
if(((m_mode.startsWith("WSPR") and m_ntr==-1) or m_mode=="FT4") and
!m_tuneup) {
m_wideGraph->setWSPRtransmitted();
WSPR_scheduling ();
m_ntr=0;
}
last_tx_label.setText("Last Tx: " + m_currentMessage.trimmed());
//### if(m_mode=="FT8" and (SpecOp::HOUND == m_config.special_op_id())) auto_tx_mode(false); ###
}
void MainWindow::ba2msg(QByteArray ba, char message[]) //ba2msg()
@ -4120,6 +4176,7 @@ void MainWindow::on_txrb4_doubleClicked ()
auto const& my_callsign = m_config.my_callsign ();
auto is_compound = my_callsign != m_baseCall;
m_send_RR73 = !((is_compound && !shortList (my_callsign)) || m_send_RR73);
if(m_mode=="FT4") m_send_RR73=true;
genStdMsgs (m_rpt);
}
@ -4195,6 +4252,7 @@ void MainWindow::on_txb4_doubleClicked()
auto const& my_callsign = m_config.my_callsign ();
auto is_compound = my_callsign != m_baseCall;
m_send_RR73 = !((is_compound && !shortList (my_callsign)) || m_send_RR73);
if(m_mode=="FT4") m_send_RR73=true;
genStdMsgs (m_rpt);
}
@ -4222,6 +4280,7 @@ void MainWindow::on_txb6_clicked()
void MainWindow::doubleClickOnCall2(Qt::KeyboardModifiers modifiers)
{
if(m_mode=="FT4" and m_inQSOwith!="") return;
set_dateTimeQSO(-1); // reset our QSO start time
m_decodedText2=true;
doubleClickOnCall(modifiers);
@ -4230,10 +4289,7 @@ void MainWindow::doubleClickOnCall2(Qt::KeyboardModifiers modifiers)
void MainWindow::doubleClickOnCall(Qt::KeyboardModifiers modifiers)
{
// if(!(modifiers & Qt::AltModifier) and m_transmitting) {
// qDebug() << "aa" << "Double-click on decode is ignored while transmitting";
// return;
// }
if(m_mode=="FT4" and m_inQSOwith!="") return;
QTextCursor cursor;
if(m_mode=="ISCAT") {
MessageBox::information_message (this,
@ -4280,7 +4336,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
|| ("JT9" == m_mode && mode != "@")
|| ("MSK144" == m_mode && !("&" == mode || "^" == mode))
|| ("QRA64" == m_mode && mode.left (1) != ":")) {
return;
return; //Currently we do auto-sequencing only in FT4, FT8, and MSK144
}
//Skip the rest if no decoded text extracted
@ -4337,6 +4393,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
return;
}
// only allow automatic mode changes between JT9 and JT65, and when not transmitting
if (!m_transmitting and m_mode == "JT9+JT65") {
if (message.isJT9())
@ -4369,7 +4426,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
ui->TxFreqSpinBox->setValue(frequency);
}
if(m_mode != "JT4" && m_mode != "JT65" && !m_mode.startsWith ("JT9") &&
m_mode != "QRA64" && m_mode!="FT8") {
m_mode != "QRA64" && m_mode!="FT8" && m_mode!="FT4") {
return;
}
}
@ -4488,6 +4545,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
} else {
m_bTUmsg=false;
m_nextCall=""; //### Temporary: disable use of "TU;" message
if(SpecOp::RTTY == m_config.special_op_id() and m_nextCall!="") {
// We're in RTTY contest and have "nextCall" queued up: send a "TU; ..." message
logQSOTimer.start(0);
@ -4510,7 +4568,8 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
}
m_QSOProgress = SIGNOFF;
} else if((m_QSOProgress >= REPORT
|| (m_QSOProgress >= REPLYING && (m_mode=="MSK144" or m_mode=="FT8")))
|| (m_QSOProgress >= REPLYING &&
(m_mode=="MSK144" or m_mode=="FT8" or m_mode=="FT4")))
&& r.mid(0,1)=="R") {
m_ntx=4;
m_QSOProgress = ROGERS;
@ -4693,6 +4752,7 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
// i.e. compound version of same base call
ui->dxCallEntry->setText (hiscall);
}
if (hisgrid.contains (grid_regexp)) {
if(ui->dxGridEntry->text().mid(0,4) != hisgrid) ui->dxGridEntry->setText(hisgrid);
}
@ -4734,7 +4794,20 @@ void MainWindow::processMessage (DecodedText const& message, Qt::KeyboardModifie
}
if(m_transmitting) m_restart=true;
if (ui->cbAutoSeq->isVisible () && ui->cbAutoSeq->isChecked () && !m_bDoubleClicked) return;
if (ui->cbAutoSeq->isVisible () && ui->cbAutoSeq->isChecked ()
&& !m_bDoubleClicked && m_mode!="FT4") {
return;
}
if(m_mode=="FT4" and ui->cbAutoSeq->isChecked()) {
if((m_ntx==4 or m_ntx==5) and !m_diskData) {
save_FT4();
logQSOTimer.start(0); // Log the QSO
}
if((m_ntx==3 and ui->cbFirst->isChecked()) or m_ntx==4 or m_bDoubleClicked) {
QThread::msleep(600); //Wait a bit. ### Is this a good idea??? ###
ft4_tx(m_ntx);
}
}
if(m_config.quick_call()) auto_tx_mode(true);
m_bDoubleClicked=false;
}
@ -4747,6 +4820,7 @@ int MainWindow::setTxMsg(int n)
if(n==3) ui->txrb3->setChecked(true);
if(n==4) ui->txrb4->setChecked(true);
if(n==5) ui->txrb5->setChecked(true);
if(n==6) ui->txrb6->setChecked(true);
if(ui->tabWidget->currentIndex()==1) {
m_ntx=7; //### FIX THIS ###
m_gen_message_is_cq = false;
@ -4788,12 +4862,17 @@ void MainWindow::genCQMsg ()
}
QString t=ui->tx6->text();
if((m_mode=="FT8" or m_mode=="MSK144") and SpecOp::NONE != m_config.special_op_id() and
t.split(" ").at(1)==m_config.my_callsign() and stdCall(m_config.my_callsign())) {
if((m_mode=="FT4" or m_mode=="FT8" or m_mode=="MSK144") and
SpecOp::NONE != m_config.special_op_id() and
t.split(" ").at(1)==m_config.my_callsign() and
stdCall(m_config.my_callsign())) {
if(SpecOp::NA_VHF == m_config.special_op_id()) t="CQ TEST" + t.mid(2,-1);
if(SpecOp::EU_VHF == m_config.special_op_id()) t="CQ TEST" + t.mid(2,-1);
if(SpecOp::FIELD_DAY == m_config.special_op_id()) t="CQ FD" + t.mid(2,-1);
if(SpecOp::RTTY == m_config.special_op_id()) t="CQ RU" + t.mid(2,-1);
if(SpecOp::RTTY == m_config.special_op_id()) {
if(m_config.RTTY_Exchange()!="SCC") t="CQ RU" + t.mid(2,-1);
if(m_config.RTTY_Exchange()=="SCC") t="CQ SCC" + t.mid(2,-1);
}
ui->tx6->setText(t);
}
} else {
@ -4824,11 +4903,6 @@ bool MainWindow::stdCall(QString const& w)
void MainWindow::genStdMsgs(QString rpt, bool unconditional)
{
// Prevent abortQSO from working when a TU; message is already queued
// if(ui->tx3->text().left(4)=="TU; ") {
// return;
// }
genCQMsg ();
auto const& hisCall=ui->dxCallEntry->text();
if(!hisCall.size ()) {
@ -4875,7 +4949,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
int n=rpt.toInt();
rpt.sprintf("%+2.2d",n);
if(m_mode=="MSK144" or m_mode=="FT8") {
if(m_mode=="MSK144" or m_mode=="FT8" or m_mode=="FT4") {
QString t2,t3;
QString sent=rpt;
QString rs,rst;
@ -4912,6 +4986,15 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
msgtype(t + sent, ui->tx2);
if(sent==rpt) msgtype(t + "R" + sent, ui->tx3);
if(sent!=rpt) msgtype(t + "R " + sent, ui->tx3);
if(m_mode=="FT4" and SpecOp::RTTY==m_config.special_op_id()) {
QDateTime now=QDateTime::currentDateTimeUtc();
int sinceTx3 = m_dateTimeSentTx3.secsTo(now);
int sinceRR73 = m_dateTimeRcvdRR73.secsTo(now);
if(m_bDoubleClicked and (qAbs(sinceTx3-12) <= 3) and (sinceRR73 < 5)) {
t="TU; " + ui->tx3->text();
ui->tx3->setText(t);
}
}
}
if(m_mode=="MSK144" and m_bShMsgs) {
@ -4929,7 +5012,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
}
}
if((m_mode!="MSK144" and m_mode!="FT8")) {
if((m_mode!="MSK144" and m_mode!="FT8" and m_mode!="FT4")) {
t=t00 + rpt;
msgtype(t, ui->tx2);
t=t0 + "R" + rpt;
@ -4945,7 +5028,7 @@ void MainWindow::genStdMsgs(QString rpt, bool unconditional)
}
t=t0 + (m_send_RR73 ? "RR73" : "RRR");
if((m_mode=="MSK144" and !m_bShMsgs) or m_mode=="FT8") {
if((m_mode=="MSK144" and !m_bShMsgs) or m_mode=="FT8" or m_mode=="FT4") {
if(!bHisCall and bMyCall) t=hisCall + " <" + my_callsign + "> " + (m_send_RR73 ? "RR73" : "RRR");
if(bHisCall and !bMyCall) t="<" + hisCall + "> " + my_callsign + " " + (m_send_RR73 ? "RR73" : "RRR");
}
@ -5392,6 +5475,7 @@ void MainWindow::on_logQSOButton_clicked() //Log QSO button
m_dateTimeQSOOn, dateTimeQSOOff, m_freqNominal +
ui->TxFreqSpinBox->value(), m_noSuffix, m_xSent, m_xRcvd,
m_cabrilloLog.data ());
m_inQSOwith="";
}
void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call, QString const& grid
@ -5431,10 +5515,10 @@ void MainWindow::acceptQSO (QDateTime const& QSO_date_off, QString const& call,
if (m_config.clear_DX () and SpecOp::HOUND != m_config.special_op_id()) clearDX ();
m_dateTimeQSOOn = QDateTime {};
auto special_op = m_config.special_op_id ();
if (SpecOp::NONE < special_op && special_op < SpecOp::FOX)
{
ui->sbSerialNumber->setValue (ui->sbSerialNumber->value () + 1);
}
if (SpecOp::NONE < special_op && special_op < SpecOp::FOX &&
m_config.RTTY_Exchange()!="SCC") {
ui->sbSerialNumber->setValue(ui->sbSerialNumber->value() + 1);
}
m_xSent.clear ();
m_xRcvd.clear ();
@ -5503,12 +5587,57 @@ void MainWindow::displayWidgets(qint64 n)
j=j>>1;
}
b=SpecOp::EU_VHF==m_config.special_op_id() or (SpecOp::RTTY==m_config.special_op_id() and
(m_config.RTTY_Exchange()=="#" or m_config.RTTY_Exchange()=="DX"));
(m_config.RTTY_Exchange()=="DX" or m_config.RTTY_Exchange()=="SCC"));
ui->sbSerialNumber->setVisible(b);
m_lastCallsign.clear (); // ensures Tx5 is updated for new modes
genStdMsgs (m_rpt, true);
}
void MainWindow::on_actionFT4_triggered()
{
m_mode="FT4";
m_modeTx="FT4";
m_TRperiod=2147483647;
bool bVHF=m_config.enable_VHF_features();
m_bFast9=false;
m_bFastMode=false;
WSPR_config(false);
switch_mode (Modes::FT4);
m_nsps=6912;
m_FFTSize = m_nsps/2;
Q_EMIT FFTSize (m_FFTSize);
m_hsymStop=50;
setup_status_bar (bVHF);
m_toneSpacing=12000.0/512.0;
ui->actionFT4->setChecked(true);
m_wideGraph->setMode(m_mode);
m_wideGraph->setModeTx(m_modeTx);
m_send_RR73=true;
VHF_features_enabled(bVHF);
// ui->cbAutoSeq->setChecked(false);
m_fastGraph->hide();
m_wideGraph->show();
ui->decodedTextLabel2->setText(" UTC dB DT Freq Message");
m_wideGraph->setPeriod(m_TRperiod,m_nsps);
m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe
m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe
ui->label_7->setText("Rx Frequency");
ui->label_6->setText("Band Activity");
ui->decodedTextLabel->setText( " UTC dB DT Freq Message");
displayWidgets(nWidgets("011010000100111000010000100110001"));
ui->txrb2->setEnabled(true);
ui->txrb4->setEnabled(true);
ui->txrb5->setEnabled(true);
ui->txrb6->setEnabled(true);
ui->txb2->setEnabled(true);
ui->txb4->setEnabled(true);
ui->txb5->setEnabled(true);
ui->txb6->setEnabled(true);
ui->txFirstCheckBox->setEnabled(true);
chkFT4();
statusChanged();
}
void MainWindow::on_actionFT8_triggered()
{
m_mode="FT8";
@ -5611,8 +5740,6 @@ void MainWindow::on_actionFT8_triggered()
statusChanged();
}
void MainWindow::on_actionJT4_triggered()
{
m_mode="JT4";
@ -6180,7 +6307,7 @@ void MainWindow::on_reset_cabrillo_log_action_triggered ()
"They will be kept in the ADIF log file but will not be available "
"for export in your Cabrillo log.")))
{
ui->sbSerialNumber->setValue (1);
if(m_config.RTTY_Exchange()!="SCC") ui->sbSerialNumber->setValue(1);
if (!m_cabrilloLog) m_cabrilloLog.reset (new CabrilloLog {&m_config});
m_cabrilloLog->reset ();
}
@ -6766,7 +6893,17 @@ void MainWindow::transmit (double snr)
}
if (m_modeTx == "FT8") {
toneSpacing=12000.0/1920.0;
// toneSpacing=12000.0/1920.0;
toneSpacing=-3;
int nsym=79;
int nsps=4*1920;
float fsample=48000.0;
float f0=ui->TxFreqSpinBox->value() - m_XIT;
int icmplx=0;
int nwave=nsym*nsps;
gen_ft8wave_(const_cast<int *>(itone),&nsym,&nsps,&fsample,&f0,foxcom_.wave,
foxcom_.wave,&icmplx,&nwave);
if(m_config.x2ToneSpacing()) toneSpacing=2*12000.0/1920.0;
if(m_config.x4ToneSpacing()) toneSpacing=4*12000.0/1920.0;
if(SpecOp::FOX==m_config.special_op_id() and !m_tune) toneSpacing=-1;
@ -6776,6 +6913,15 @@ void MainWindow::transmit (double snr)
true, false, snr, m_TRperiod);
}
if (m_modeTx == "FT4") {
// toneSpacing=12000.0/512.0; //Generate Tx waveform from itone[] array
toneSpacing=-2.0; //Transmit a pre-computed, filtered waveform.
Q_EMIT sendMessage (NUM_FT4_SYMBOLS,
512.0, ui->TxFreqSpinBox->value() - m_XIT,
toneSpacing, m_soundOutput, m_config.audio_output_channel (),
true, false, snr, 2);
}
if (m_modeTx == "QRA64") {
if(m_nSubMode==0) toneSpacing=12000.0/6912.0;
if(m_nSubMode==1) toneSpacing=2*12000.0/6912.0;
@ -7145,7 +7291,8 @@ void MainWindow::replyToCQ (QTime time, qint32 snr, float delta_time, quint32 de
}
QString format_string {"%1 %2 %3 %4 %5 %6"};
auto const& time_string = time.toString ("~" == mode || "&" == mode ? "hhmmss" : "hhmm");
auto const& time_string = time.toString ("~" == mode || "&" == mode
|| "+" == mode ? "hhmmss" : "hhmm");
auto message_line = format_string
.arg (time_string)
.arg (snr, 3)
@ -7827,7 +7974,7 @@ void MainWindow::on_cbFirst_toggled(bool b)
void MainWindow::on_cbAutoSeq_toggled(bool b)
{
if(!b) ui->cbFirst->setChecked(false);
ui->cbFirst->setVisible((m_mode=="FT8") and b);
ui->cbFirst->setVisible((m_mode=="FT8" or m_mode=="FT4") and b);
}
void MainWindow::on_measure_check_box_stateChanged (int state)
@ -8489,7 +8636,7 @@ void MainWindow::write_all(QString txRx, QString message)
t.sprintf("%5d",ui->TxFreqSpinBox->value());
if(txRx=="Tx") msg=" 0 0.0" + t + " " + message;
auto time = QDateTime::currentDateTimeUtc ();
time = time.addSecs (-(time.time ().second () % m_TRperiod));
if(m_mode!="FT4") time = time.addSecs(-(time.time().second() % m_TRperiod));
t.sprintf("%10.3f ",m_freqNominal/1.e6);
if(m_diskData) {
line=m_fileDateTime + t + txRx + " " + m_mode.leftJustified(6,' ') + msg;
@ -8511,3 +8658,251 @@ void MainWindow::write_all(QString txRx, QString message)
MessageBox::warning_message(this, tr ("Log File Error"), message2); });
}
}
void MainWindow::ft4_rx(int k)
{
static int nhsec0=-1;
static bool wrapped=false;
short id[60000];
const int istep=3456;
const int k_enough=55296; //4.608 s
if(k<m_kin0) m_kin0=0;
int nhsec=k/istep;
if(nhsec0>nhsec) nhsec0=-1;
if(nhsec==nhsec0) return;
if(k<k_enough and !wrapped) return;
//Process FT4 data at intervals of istep/12000.0 = 0.288 seconds
int j=k/istep;
j=istep*j-k_enough;
if(j<0) j+=NRING;
float tbuf=j/12000.0;
for(int i=0; i<60000; i++) {
id[i]=dec_data.d2[j];
j++;
if(j>=NRING) {
j=j-NRING;
wrapped=true;
}
}
if(j>60000) wrapped=false;
if(m_saveAll and ((k-m_kin0)/12000.0 > 15.0) and !m_diskData) save_FT4();
if(k>=NRING) {
if(m_saveAll and !m_diskData) save_FT4();
//Wrap the ring buffer pointer
k=k-NRING;
dec_data.params.kin=k;
}
QByteArray ba;
if(m_diskData) {
ba=(m_fileDateTime + ".000").toLatin1();
} else {
auto time = QDateTime::currentDateTimeUtc ();
ba=time.toString("yyMMdd_hhmmss.sss").toLatin1();
}
char* cdatetime=ba.data();
strncpy(dec_data.params.mycall, (m_config.my_callsign()+" ").toLatin1(),12);
char mycall[13];
strncpy(mycall,m_config.my_callsign().toLatin1(),12);
char hiscall[13];
strncpy(hiscall,m_hisCall.toLatin1(),12);
char line[61];
int nfqso=1500;
int ndecodes=0;
int nfa=m_wideGraph->nStartFreq();
int nfb=m_wideGraph->Fmax();
int nQSOProgress = static_cast<int> ( m_QSOProgress );
int nContest = static_cast<int> (m_config.special_op_id());
QString dataDir;
dataDir = m_config.writeable_data_dir ().absolutePath ();
char ddir[512];
strncpy(ddir,dataDir.toLatin1(), sizeof (ddir) - 1);
char cqstr[4];
strncpy(cqstr," ",4);
if(SpecOp::NA_VHF == m_config.special_op_id()) strncpy(cqstr,"TEST",4);
if(SpecOp::EU_VHF == m_config.special_op_id()) strncpy(cqstr,"TEST",4);
if(SpecOp::FIELD_DAY == m_config.special_op_id()) strncpy(cqstr,"FD",2);
if(SpecOp::RTTY == m_config.special_op_id()) {
if(m_config.RTTY_Exchange()!="SCC") strncpy(cqstr,"RU",2);
if(m_config.RTTY_Exchange()=="SCC") strncpy(cqstr,"SCC",3);
}
ft4_decode_(cdatetime,&tbuf,&nfa,&nfb,&nQSOProgress,&nContest,&nfqso,id,&ndecodes,&mycall[0],&hiscall[0],
&cqstr[0],&line[0],&ddir[0],17,12,12,4,61,512);
line[60]=0;
for (int idecode=1; idecode<=ndecodes; idecode++) {
get_ft4msg_(&idecode,&line[0],61);
line[60]=0;
QString sline{QString::fromLatin1(line)};
DecodedText decodedtext {sline.replace(QChar::LineFeed,"")};
ui->decodedTextBrowser->displayDecodedText (decodedtext,m_baseCall,m_mode,
m_config.DXCC(),m_logBook,m_currentBand,m_config.ppfx());
//Right (Rx Frequency) window
// int audioFreq=decodedtext.frequencyOffset();
auto const& parts = decodedtext.string().remove("<").remove(">")
.split (' ', QString::SkipEmptyParts);
if(parts.size() > 6) {
int iFirstCall=5;
if(parts[5]=="TU;") iFirstCall=6;
auto for_us = parts[iFirstCall].contains(m_baseCall);
if(m_baseCall==m_config.my_callsign() and m_baseCall!=parts[iFirstCall]) for_us=false;
if(m_bCallingCQ && !m_bAutoReply && for_us && ui->cbFirst->isChecked()) {
m_bDoubleClicked=true;
m_bAutoReply = true;
ui->cbFirst->setStyleSheet("");
}
if(for_us) {
ui->decodedTextBrowser2->displayDecodedText(decodedtext,m_baseCall,
m_mode,m_config.DXCC(),m_logBook,m_currentBand,m_config.ppfx());
if(decodedtext.string().trimmed().contains(m_inQSOwith)) processMessage(decodedtext);
m_QSOText = decodedtext.string().trimmed ();
}
if(for_us and parts[iFirstCall+2]=="RR73") m_dateTimeRcvdRR73=QDateTime::currentDateTimeUtc();
write_all("Rx",decodedtext.string().trimmed());
}
}
nhsec0=nhsec;
if(m_diskData and (k > (dec_data.params.kin-istep))) m_startAnother=m_loopall;
if(m_bNoMoreFiles) {
MessageBox::information_message(this, tr("Just one more file to open."));
m_bNoMoreFiles=false;
}
}
void MainWindow::ft4_tx(int ntx)
{
if(g_iptt!=0) return; //Already transmitting?
static char message[38];
static char msgsent[38];
QByteArray ba;
m_ntx=ntx;
setTxMsg(m_ntx);
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();
QString msg = QString::fromLatin1(ba.data());
if(m_ntx==2 or m_ntx==3) m_inQSOwith=m_hisCall;
if(msg.trimmed().length()==0) return; //Don't transmit a blank message, or ...
if(m_diskData) return; //... in response to a decode from disk
ba2msg(ba,message);
int ichk=0;
genft4_(message, &ichk, msgsent, const_cast<int *>(itone), 37, 37);
msgsent[37]=0;
m_currentMessage = QString::fromLatin1(msgsent).trimmed();
tx_status_label.setStyleSheet("QLabel{background-color: #ffff33}");
tx_status_label.setText("TX: " + m_currentMessage);
if(m_ntx==2 or m_ntx==3) {
QStringList t=ui->tx2->text().split(' ', QString::SkipEmptyParts);
int n=t.size();
m_xSent=t.at(n-2) + " " + t.at(n-1);
}
auto_tx_mode(true); //Enable Tx
icw[0]=0;
g_iptt = 1;
setRig ();
setXIT (ui->TxFreqSpinBox->value ());
int nsym=103;
int nsps=4*512;
float fsample=48000.0;
float f0=ui->TxFreqSpinBox->value() - m_XIT;
int nwave=(nsym+2)*nsps;
gen_ft4wave_(const_cast<int *>(itone),&nsym,&nsps,&fsample,&f0,foxcom_.wave,&nwave);
if(m_ntx==3) m_dateTimeSentTx3=QDateTime::currentDateTimeUtc();
Q_EMIT m_config.transceiver_ptt (true); //Assert the PTT
m_tx_when_ready = true;
qint64 ms=QDateTime::currentMSecsSinceEpoch();
m_modulator->set_ms0(ms);
FT4_TxTimer.start(4600); //Slightly more than FT4 transmission length
if (g_iptt == 1 && m_iptt0 == 0) {
auto const& current_message = QString::fromLatin1 (msgsent);
FT4_WriteTxTimer.start(100); //Why is a delay necessary to ensure Tx after Rx in all.txt?
if (m_config.TX_messages () && !m_tune && SpecOp::FOX!=m_config.special_op_id()) {
ui->decodedTextBrowser2->displayTransmittedText(current_message, m_modeTx,
ui->TxFreqSpinBox->value(),m_bFastMode);
}
switch (m_ntx)
{
case 1: m_QSOProgress = REPLYING; break;
case 2: m_QSOProgress = REPORT; break;
case 3: m_QSOProgress = ROGER_REPORT; break;
case 4: m_QSOProgress = ROGERS; break;
case 5: m_QSOProgress = SIGNOFF; break;
case 6: m_QSOProgress = CALLING; break;
default: break; // determined elsewhere
}
m_transmitting = true;
transmitDisplay (true);
statusUpdate ();
}
m_dateTimeQSOOn=QDateTime::currentDateTimeUtc();
if(!m_btxok && m_btxok0 && g_iptt==1) stopTx();
if(m_saveAll and !m_diskData) save_FT4();
}
void MainWindow::FT4_writeTx()
{
write_all("Tx",m_currentMessage);
}
void MainWindow::save_FT4()
{
double tsec=(dec_data.params.kin - m_kin0)/12000.0;
if(tsec<4.4) return; //Saved data must be at least 4.4 seconds long.
auto time = QDateTime::currentDateTimeUtc ();
QString t=time.toString("yyMMdd_hhmmss");
m_fnameWE=m_config.save_directory().absoluteFilePath(t);
// The following is potential a threading hazard - not a good
// idea to pass pointer to be processed in another thread
int nsamples=dec_data.params.kin - m_kin0 + 1;
m_saveWAVWatcher.setFuture (QtConcurrent::run (std::bind (&MainWindow::save_wave_file,
this, m_fnameWE, &dec_data.d2[m_kin0], nsamples, m_config.my_callsign(),
m_config.my_grid(), m_mode, m_nSubMode, m_freqNominal, m_hisCall,
m_hisGrid)));
m_kin0=dec_data.params.kin;
}
void MainWindow::chkFT4()
{
if(m_mode!="FT4") return;
if(m_config.special_op_id()==SpecOp::RTTY or m_config.special_op_id()==SpecOp::NA_VHF) {
ui->cbAutoSeq->setEnabled(true);
ui->cbFirst->setEnabled(true);
ui->labDXped->setVisible(true);
} else {
ui->cbAutoSeq->setChecked(false);
ui->cbAutoSeq->setEnabled(false);
ui->cbFirst->setChecked(false);
ui->cbFirst->setEnabled(false);
ui->cbFirst->setVisible(false);
ui->labDXped->setVisible(false);
}
ui->cbFirst->setVisible(ui->cbAutoSeq->isChecked());
if (SpecOp::NONE < m_config.special_op_id () && SpecOp::FOX > m_config.special_op_id ()) {
QString t0="";
if(SpecOp::NA_VHF==m_config.special_op_id()) t0+="NA VHF";
if(SpecOp::EU_VHF==m_config.special_op_id()) t0+="EU VHF";
if(SpecOp::FIELD_DAY==m_config.special_op_id()) t0+="Field Day";
if(SpecOp::RTTY==m_config.special_op_id()) t0+="RTTY";
if(t0=="") {
ui->labDXped->setVisible(false);
} else {
ui->labDXped->setVisible(true);
ui->labDXped->setText(t0);
}
on_contest_log_action_triggered();
}
}

View File

@ -46,9 +46,11 @@
#define NUM_MSK144_SYMBOLS 144 //s8 + d48 + s8 + d80
#define NUM_QRA64_SYMBOLS 84 //63 data + 21 sync
#define NUM_FT8_SYMBOLS 79
#define NUM_FT4_SYMBOLS 103
#define NUM_CW_SYMBOLS 250
#define TX_SAMPLE_RATE 48000
#define N_WIDGETS 33
#define NRING 3456000
extern int volatile itone[NUM_ISCAT_SYMBOLS]; //Audio tones for all Tx symbols
extern int volatile icw[NUM_CW_SYMBOLS]; //Dits for CW ID
@ -200,6 +202,7 @@ private slots:
void on_actionJT65_triggered();
void on_actionJT9_JT65_triggered();
void on_actionJT4_triggered();
void on_actionFT4_triggered();
void on_actionFT8_triggered();
void on_TxFreqSpinBox_valueChanged(int arg1);
void on_actionSave_decoded_triggered();
@ -309,6 +312,8 @@ private slots:
void on_comboBoxHoundSort_activated (int index);
void not_GA_warning_message ();
void checkMSK144ContestType();
void ft4_rx(int k);
void ft4_tx(int ntx);
int setTxMsg(int n);
bool stdCall(QString const& w);
@ -343,6 +348,7 @@ private:
void hideMenus(bool b);
void foxTest();
void setColorHighlighting();
void chkFT4();
NetworkAccessManager m_network_manager;
bool m_valid;
@ -461,6 +467,7 @@ private:
qint32 m_tFoxTxSinceCQ=999; //Fox Tx cycles since most recent CQ
qint32 m_nFoxFreq; //Audio freq at which Hound received a call from Fox
qint32 m_nSentFoxRrpt=0; //Serial number for next R+rpt Hound will send to Fox
qint32 m_kin0=0;
bool m_btxok; //True if OK to transmit
bool m_diskData;
@ -576,6 +583,8 @@ private:
QTimer minuteTimer;
QTimer splashTimer;
QTimer p1Timer;
QTimer FT4_TxTimer;
QTimer FT4_WriteTxTimer;
QString m_path;
QString m_baseCall;
@ -609,6 +618,7 @@ private:
QString m_nextCall;
QString m_nextGrid;
QString m_fileDateTime;
QString m_inQSOwith;
QSet<QString> m_pfx;
QSet<QString> m_sfx;
@ -627,12 +637,23 @@ private:
QMap<QString,FoxQSO> m_foxQSO; //Key = HoundCall, value = parameters for QSO in progress
QMap<QString,QString> m_loggedByFox; //Key = HoundCall, value = logged band
struct FixupQSO //Info for fixing Fox's log from file "FoxQSO.txt"
{
QString grid; //Hound's declared locator
QString sent; //Report sent to Hound
QString rcvd; //Report received from Hound
QDateTime QSO_time;
};
QMap<QString,FixupQSO> m_fixupQSO; //Key = HoundCall, value = info for QSO in progress
QQueue<QString> m_houndQueue; //Selected Hounds available for starting a QSO
QQueue<QString> m_foxQSOinProgress; //QSOs in progress: Fox has sent a report
QQueue<qint64> m_foxRateQueue;
QDateTime m_dateTimeQSOOn;
QDateTime m_dateTimeLastTX;
QDateTime m_dateTimeSentTx3;
QDateTime m_dateTimeRcvdRR73;
QSharedMemory *mem_jt9;
QString m_QSOText;
@ -709,7 +730,7 @@ private:
QString save_wave_file (QString const& name
, short const * data
, int seconds
, int samples
, QString const& my_callsign
, QString const& my_grid
, QString const& mode
@ -743,6 +764,8 @@ private:
void foxTxSequencer();
void foxGenWaveform(int i,QString fm);
void writeFoxQSO (QString const& msg);
void FT4_writeTx();
void save_FT4();
};
extern int killbyname(const char* progName);

View File

@ -2705,6 +2705,7 @@ list. The list can be maintained in Settings (F2).</string>
<property name="title">
<string>Mode</string>
</property>
<addaction name="actionFT4"/>
<addaction name="actionFT8"/>
<addaction name="actionJT4"/>
<addaction name="actionJT9"/>
@ -2773,9 +2774,6 @@ list. The list can be maintained in Settings (F2).</string>
<property name="text">
<string>About WSJT-X</string>
</property>
<property name="shortcut">
<string>Ctrl+F1</string>
</property>
</action>
<action name="actionWide_Waterfall">
<property name="text">
@ -3338,6 +3336,14 @@ list. The list can be maintained in Settings (F2).</string>
<string>Erase WSPR hashtable</string>
</property>
</action>
<action name="actionFT4">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>FT4</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -166,7 +166,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed)
ymin=1.e30;
if(swide[0]>1.e29 and swide[0]< 1.5e30) painter1.setPen(Qt::green);
if(swide[0]>1.4e30) painter1.setPen(Qt::yellow);
if(swide[0]>1.4e30) painter1.setPen(Qt::red);
if(!m_bReplot) {
m_j=0;
int irow=-1;
@ -235,13 +235,15 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed)
if(m_bReplot) return;
if(swide[0]>1.0e29) m_line=0;
if(m_mode=="FT4" and m_line==34) m_line=0;
if(m_line == painter1.fontMetrics ().height ()) {
painter1.setPen(Qt::white);
QString t;
qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000;
int n=(ms/1000) % m_TRperiod;
if(m_mode=="FT4") n=0;
QDateTime t1=QDateTime::currentDateTimeUtc().addSecs(-n);
if(m_TRperiod < 60) {
if(m_TRperiod < 60 or m_mode=="FT4") {
t=t1.toString("hh:mm:ss") + " " + m_rxBand;
} else {
t=t1.toString("hh:mm") + " " + m_rxBand;
@ -409,7 +411,7 @@ void CPlotter::DrawOverlay() //DrawOverlay()
}
float bw=9.0*12000.0/m_nsps; //JT9
if(m_mode=="FT4") bw=3*12000.0/512.0; //FT4 ### (3x, or 4x???) ###
if(m_mode=="FT8") bw=7*12000.0/1920.0; //FT8
if(m_mode=="JT4") { //JT4
@ -484,7 +486,8 @@ void CPlotter::DrawOverlay() //DrawOverlay()
painter0.drawLine(x1,24,x1,30);
}
if(m_mode=="JT9" or m_mode=="JT65" or m_mode=="JT9+JT65" or m_mode=="QRA64" or m_mode=="FT8") {
if(m_mode=="JT9" or m_mode=="JT65" or m_mode=="JT9+JT65"
or m_mode=="QRA64" or m_mode=="FT8" or m_mode=="FT4") {
if(m_mode=="QRA64" or (m_mode=="JT65" and m_bVHF)) {
painter0.setPen(penGreen);
@ -518,7 +521,8 @@ void CPlotter::DrawOverlay() //DrawOverlay()
}
if(m_mode=="JT9" or m_mode=="JT65" or m_mode=="JT9+JT65" or
m_mode.mid(0,4)=="WSPR" or m_mode=="QRA64" or m_mode=="FT8") {
m_mode.mid(0,4)=="WSPR" or m_mode=="QRA64" or m_mode=="FT8"
or m_mode=="FT4") {
painter0.setPen(penRed);
x1=XfromFreq(m_txFreq);
x2=XfromFreq(m_txFreq+bw);

View File

@ -187,7 +187,8 @@ void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dat
// Time according to this computer
qint64 ms = QDateTime::currentMSecsSinceEpoch() % 86400000;
int ntr = (ms/1000) % m_TRperiod;
if((ndiskdata && ihsym <= m_waterfallAvg) || (!ndiskdata && ntr<m_ntr0)) {
if((ndiskdata && ihsym <= m_waterfallAvg) || (!ndiskdata &&
(ntr<m_ntr0 || (m_mode=="FT4" and m_bHaveTransmitted)))) {
float flagValue=1.0e30;
if(m_bHaveTransmitted) flagValue=2.0e30;
for(int i=0; i<MAX_SCREENSIZE; i++) {

View File

@ -108,7 +108,7 @@ private:
bool m_bFlatten;
bool m_bRef;
bool m_bHaveTransmitted; //Set true at end of a WSPR transmission
bool m_bHaveTransmitted; //Set true at end of a WSPR or FT4 transmission
QString m_rxBand;
QString m_mode;