mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2025-07-29 20:22:28 -04:00
Merge branch 'feat-ft2' into develop
This commit is contained in:
commit
158f929249
@ -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
|
||||
|
@ -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 ();
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
||||
|
@ -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
12
lib/fsk4hf/ft2_params.f90
Normal 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
335
lib/fsk4hf/ft2d.f90
Normal 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
154
lib/fsk4hf/ft2sim.f90
Normal 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
329
lib/fsk4hf/ft4d.f90
Normal 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
86
lib/fsk4hf/genft2.f90
Normal 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
|
63
lib/fsk4hf/getcandidates2.f90
Normal file
63
lib/fsk4hf/getcandidates2.f90
Normal 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
|
@ -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
|
@ -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
19
lib/fsk4hf/spb_128_90.dat
Normal 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
6
lib/ft2/cdatetime.f90
Normal 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
279
lib/ft2/ft2.f90
Normal 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
2
lib/ft2/ft2.ini
Normal 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
298
lib/ft2/ft2_decode.f90
Normal 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
|
88
lib/ft2/ft2_gfsk_iwave.f90
Normal file
88
lib/ft2/ft2_gfsk_iwave.f90
Normal 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
64
lib/ft2/ft2_iwave.f90
Normal 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
12
lib/ft2/ft2_params.f90
Normal 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
347
lib/ft2/ft2audio.c
Normal 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
7
lib/ft2/g4.cmd
Normal 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
34
lib/ft2/gcom1.f90
Normal 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
86
lib/ft2/genft2.f90
Normal 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
|
64
lib/ft2/getcandidates2a.f90
Normal file
64
lib/ft2/getcandidates2a.f90
Normal 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
6
lib/ft2/gfsk_pulse.f90
Normal 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
BIN
lib/ft2/libportaudio.a
Normal file
Binary file not shown.
BIN
lib/ft2/libwsjt_cxx.a
Normal file
BIN
lib/ft2/libwsjt_cxx.a
Normal file
Binary file not shown.
BIN
lib/ft2/libwsjt_fort.a
Normal file
BIN
lib/ft2/libwsjt_fort.a
Normal file
Binary file not shown.
1123
lib/ft2/portaudio.h
Normal file
1123
lib/ft2/portaudio.h
Normal file
File diff suppressed because it is too large
Load Diff
58
lib/ft2/ptt.c
Normal file
58
lib/ft2/ptt.c
Normal 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
341
lib/ft2/ptt_unix.c
Normal 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
111
lib/ft4/clockit.f90
Normal 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
486
lib/ft4/ft4_decode.f90
Normal 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
|
49
lib/ft4/ft4_downsample.f90
Normal file
49
lib/ft4/ft4_downsample.f90
Normal 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
16
lib/ft4/ft4_params.f90
Normal 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
83
lib/ft4/ft4d.f90
Normal 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
155
lib/ft4/ft4sim.f90
Normal 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
103
lib/ft4/ft4sim_mult.f90
Normal 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
52
lib/ft4/gen_ft4wave.f90
Normal 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
82
lib/ft4/genft4.f90
Normal 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
|
73
lib/ft4/getcandidates4.f90
Normal file
73
lib/ft4/getcandidates4.f90
Normal 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
162
lib/ft4/messages.txt
Normal 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
95
lib/ft4/sync4d.f90
Normal 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
145
lib/ft4/syncft4.f90
Normal 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
130
lib/ft8/ft8sim_gfsk.f90
Normal 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
74
lib/ft8/gen_ft8wave.f90
Normal 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
|
@ -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
15
lib/nuttal_window.f90
Normal 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
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
4
main.cpp
4
main.cpp
@ -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 ();
|
||||
|
||||
|
@ -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},
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ public:
|
||||
QRA64,
|
||||
FreqCal,
|
||||
FT8,
|
||||
FT4,
|
||||
MODES_END_SENTINAL_AND_COUNT // this must be last
|
||||
};
|
||||
Q_ENUM (Mode)
|
||||
|
@ -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
|
||||
|
BIN
samples/FT4/190106_000115.wav
Normal file
BIN
samples/FT4/190106_000115.wav
Normal file
Binary file not shown.
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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") {
|
||||
|
@ -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‑wide standards on December 10, 2018."
|
||||
, "<p align=\"center\">"
|
||||
"WSJT‑X 2.0 cannot communicate in these modes with other "
|
||||
"stations using WSJT‑X v1.9.1 or earlier."
|
||||
"<p align=\"center\">"
|
||||
"Please help by urging everyone to upgrade to WSJT‑X 2.0 "
|
||||
"<nobr>no later than January 1, 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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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++) {
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user