WSJT-X/lib/qra/q65/q65sim.f90

242 lines
7.7 KiB
Fortran

program q65sim
! Generate simulated Q65 data for testing the decoder.
use wavhdr
use packjt
parameter (NMAX=300*12000) !Total samples in .wav file
type(hdr) h !Header for .wav file
integer*2 iwave(NMAX) !Generated waveform
integer itone(85) !Channel symbols (values 0-65)
integer ntone(85,10) !Channel symbols for up to 10 messages
integer y(63) !Codeword
integer istart !averaging compatible start seconds
integer imins !minutes for 15s period timestamp
integer isecs !seconds for 15s period timestamp
real*4 xnoise(NMAX) !Generated random noise
real*4 dat(NMAX) !Generated real data
complex cdat(NMAX) !Generated complex waveform
complex cspread(0:NMAX-1) !Complex amplitude for Rayleigh fading
complex z
real*8 f00,f0,dt,twopi,phi,dphi,baud,fsample,freq
character fname*17,csubmode*1,arg*12,c2*2
character*37 msg,msgsent,imsg(10)
nargs=iargc()
if(nargs.ne.11) then
print*,'Usage: q65sim "msg" A-E freq fDop DT f1 Stp TRp Nsig Nfile SNR'
print*,'Example: q65sim "K1ABC W9XYZ EN37" A 1500 0.0 0.0 0.0 1 60 1 1 -26'
print*,'Example: q65sim "ST" A 1500 0.0 0.0 0.0 1 60 1 -26'
print*,' fDop = Doppler spread'
print*,' f1 = Drift or Doppler rate (Hz/min)'
print*,' Stp = Step size (Hz)'
print*,' Stp = 0 implies no Doppler tracking'
print*,' Nsig = number of generated signals, 1 - 10'
print*,' Creates filenames which increment to permit averaging in first period'
print*,' If msg = ST program produces a single tone at freq'
go to 999
endif
call getarg(1,msg)
call getarg(2,csubmode)
mode65=2**(ichar(csubmode)-ichar('A'))
call getarg(3,arg)
read(arg,*) f00
call getarg(4,arg)
read(arg,*) fspread
call getarg(5,arg)
read(arg,*) xdt
call getarg(6,arg)
read(arg,*) f1
call getarg(7,arg)
read(arg,*) nstp
call getarg(8,arg)
read(arg,*) ntrperiod
call getarg(9,arg)
read(arg,*) nsig
call getarg(10,arg)
read(arg,*) nfiles
call getarg(11,arg)
read(arg,*) snrdb
if(ntrperiod.eq.15) then
nsps=1800
else if(ntrperiod.eq.30) then
nsps=3600
else if(ntrperiod.eq.60) then
nsps=7200
else if(ntrperiod.eq.120) then
nsps=16000
else if(ntrperiod.eq.300) then
nsps=41472
else
print*,'Invalid TR period'
go to 999
endif
rms=100.
fsample=12000.d0 !Sample rate (Hz)
npts=fsample*ntrperiod !Total samples in .wav file
nfft=npts
nh=nfft/2
dt=1.d0/fsample !Sample interval (s)
twopi=8.d0*atan(1.d0)
nsym=85 !Number of channel symbols
mode65=2**(ichar(csubmode) - ichar('A'))
imsg(1)=msg
if(nsig.ge.2) then
i0=index(msg,' ')
i0=i0 + index(msg(i0+1:),' ')-2
do i=1,nsig
c2=char(ichar('A')+i-1)//char(ichar('A')+i-1)
imsg(i)=msg(1:i0-1)//c2//msg(i0+2:)
enddo
endif
ichk=0
do i=1,nsig
msg=imsg(i)
call genq65(msg,ichk,msgsent,itone,i3,n3)
ntone(:,i)=itone
enddo
if(nsig.eq.1) then
j=0
do i=1,85
if(itone(i).gt.0) then
j=j+1
y(j)=itone(i)-1
endif
enddo
write(*,1001) y(1:13),y(1:13)
1001 format('Generated message'/'6-bit: ',13i3/'binary: ',13b6.6)
write(*,1002) y
1002 format(/'Codeword:'/(20i3))
write(*,1003) itone
1003 format(/'Channel symbols:'/(20i3))
endif
baud=12000.d0/nsps !Keying rate (6.67 baud fot 15-s sequences)
h=default_header(12000,npts)
write(*,1004)
1004 format('File TR Freq Mode S/N Dop DT f1 Stp Message'/70('-'))
do ifile=1,nfiles !Loop over requested number of files
istart = (ifile*ntrperiod*2) - (ntrperiod*2)
if(ntrperiod.lt.30) then !wdg was 60
imins=istart/60
isecs=istart-(60*imins)
write(fname,1005) imins,isecs !Construction of output filename for 15s periods with averaging
1005 format('000000_',i4.4, i2.2,'.wav')
else
write(fname,1106) istart/60 !Output filename to be compatible with averaging 30-300s periods
1106 format('000000_',i4.4,'.wav')
endif
open(10,file=trim(fname),access='stream',status='unknown')
xnoise=0.
if(snrdb.lt.90) then
do i=1,npts
xnoise(i)=gran() !Generate gaussian noise
enddo
endif
cdat=0.
bandwidth_ratio=2500.0/6000.0
sig=sqrt(2*bandwidth_ratio)*10.0**(0.05*snrdb)
if(snrdb.gt.90.0) sig=1.0
write(*,1020) ifile,ntrperiod,f00,csubmode,snrdb,fspread,xdt,f1,nstp,trim(msgsent)
1020 format(i4,i6,f7.1,2x,a1,2x,f5.1,1x,f6.2,2f6.1,i4,2x,a)
n=65.0*baud*mode65/100.0 + 0.9999
nfstep=100*n
nf1=1500 - nfstep*(nsig-1)/2
do n=1,nsig
f0=f00
if(nsig.ge.2) then
f0=nf1 + (n-1)*nfstep
itone=ntone(:,n)
endif
phi=0.d0
dphi=0.d0
k=(xdt+0.5)*12000 !Start audio at t=xdt+0.5 s (TR=15 and 30 s)
if(ntrperiod.ge.60) k=(xdt+1.0)*12000 !TR 60+ at t = xdt + 1.0 s
isym0=-99
do i=1,npts !Add this signal into cdat()
isym=i/nsps + 1
if(isym.gt.nsym) exit
if(isym.ne.isym0) then
freq_drift=f1*i*dt/60.0
if(nstp.ne.0) freq_drift=freq_drift - nstp*nint(freq_drift/nstp)
if (msg(1:2).eq.'ST') then
freq = f0 + freq_drift
else
freq = f0 + freq_drift + itone(isym)*baud*mode65
endif
dphi=twopi*freq*dt
isym0=isym
endif
phi=phi + dphi
if(phi.gt.twopi) phi=phi-twopi
xphi=phi
z=cmplx(cos(xphi),sin(xphi))
k=k+1
if(k.ge.1) cdat(k)=cdat(k) + sig*z
enddo
enddo
if(fspread.ne.0) then !Apply specified Doppler spread
df=12000.0/nfft
cspread(0)=1.0
cspread(nh)=0.
b=6.0 !Use truncated Lorenzian shape for fspread
do i=1,nh
f=i*df
x=b*f/fspread
z=0.
a=0.
if(x.lt.3.0) then !Cutoff beyond x=3
a=sqrt(1.111/(1.0+x*x)-0.1) !Lorentzian amplitude
phi1=twopi*rran() !Random phase
z=a*cmplx(cos(phi1),sin(phi1))
endif
cspread(i)=z
z=0.
if(x.lt.3.0) then !Same thing for negative freqs
phi2=twopi*rran()
z=a*cmplx(cos(phi2),sin(phi2))
endif
cspread(nfft-i)=z
enddo
call four2a(cspread,nfft,1,1,1) !Transform to time domain
sum=0.
do i=0,nfft-1
p=real(cspread(i))**2 + aimag(cspread(i))**2
sum=sum+p
enddo
avep=sum/nfft
fac=sqrt(1.0/avep)
cspread=fac*cspread !Normalize to constant avg power
cdat=cspread*cdat !Apply Rayleigh fading
! do i=0,nfft-1
! p=real(cspread(i))**2 + aimag(cspread(i))**2
! write(14,3010) i,p,cspread(i)
!3010 format(i8,3f12.6)
! enddo
endif
dat=aimag(cdat) + xnoise !Add generated AWGN noise
fac=32767.0
if(snrdb.ge.90.0) iwave(1:npts)=nint(fac*dat(1:npts))
if(snrdb.lt.90.0) iwave(1:npts)=nint(rms*dat(1:npts))
write(10) h,iwave(1:npts) !Save the .wav file
close(10)
enddo
999 end program q65sim