WSJT-X/lib/q65_decode.f90

356 lines
13 KiB
Fortran
Raw Normal View History

module q65_decode
2020-08-01 09:24:59 -04:00
integer nsnr0,nfreq0
real xdt0
character msg0*37,cq0*3
2021-01-12 11:28:46 -05:00
type :: q65_decoder
procedure(q65_decode_callback), pointer :: callback
2020-08-01 09:24:59 -04:00
contains
2021-01-12 11:28:46 -05:00
procedure :: decode
end type q65_decoder
2020-08-01 09:24:59 -04:00
2021-01-12 11:28:46 -05:00
abstract interface
2021-01-13 10:55:01 -05:00
subroutine q65_decode_callback (this,nutc,snr1,nsnr,dt,freq, &
decoded,idec,nused,ntrperiod)
2021-01-12 11:28:46 -05:00
import q65_decoder
implicit none
class(q65_decoder), intent(inout) :: this
integer, intent(in) :: nutc
2021-01-13 10:55:01 -05:00
real, intent(in) :: snr1
2021-01-12 11:28:46 -05:00
integer, intent(in) :: nsnr
real, intent(in) :: dt
real, intent(in) :: freq
character(len=37), intent(in) :: decoded
integer, intent(in) :: idec
integer, intent(in) :: nused
2021-01-12 11:28:46 -05:00
integer, intent(in) :: ntrperiod
end subroutine q65_decode_callback
end interface
2020-08-01 09:24:59 -04:00
contains
subroutine decode(this,callback,iwave,nqd0,nutc,ntrperiod,nsubmode,nfqso, &
ntol,ndepth,nfa0,nfb0,lclearave,single_decode,lagain,max_drift0, &
lnewdat0,emedelay,mycall,hiscall,hisgrid,nQSOprogress,ncontest, &
lapcqonly,navg0)
2020-08-01 09:24:59 -04:00
! Top-level routine that organizes the decoding of Q65 signals
2020-10-09 13:16:25 -04:00
! Input: iwave Raw data, i*2
! nutc UTC for time-tagging the decode
! ntrperiod T/R sequence length (s)
! nsubmode Tone-spacing indicator, 0-4 for A-E
! nfqso Target signal frequency (Hz)
! ntol Search range around nfqso (Hz)
! ndepth Optional decoding level
! lclearave Flag to clear the message-averaging arrays
! emedelay Sync search extended to cover EME delays
! nQSOprogress Auto-sequencing state for the present QSO
! ncontest Supported contest type
! lapcqonly Flag to use AP only for CQ calls
2020-10-09 13:16:25 -04:00
! Output: sent to the callback routine for display to user
2020-08-01 09:24:59 -04:00
use timer_module, only: timer
use packjt77
2020-08-01 09:24:59 -04:00
use, intrinsic :: iso_c_binding
use q65 !Shared variables
use prog_args
2020-10-09 13:16:25 -04:00
parameter (NMAX=300*12000) !Max TRperiod is 300 s
class(q65_decoder), intent(inout) :: this
procedure(q65_decode_callback) :: callback
2020-10-09 13:16:25 -04:00
character(len=12) :: mycall, hiscall !Used for AP decoding
2020-08-01 09:24:59 -04:00
character(len=6) :: hisgrid
2020-10-09 13:16:25 -04:00
character*37 decoded !Decoded message
character*77 c77
character*78 c78
character*6 cutc
character c6*6,c4*4,cmode*4
character*80 fmt
integer*2 iwave(NMAX) !Raw data
2020-10-17 13:16:46 -04:00
real, allocatable :: dd(:) !Raw data
integer dat4(13) !Decoded message as 12 6-bit integers
integer dgen(13)
logical lclearave,lnewdat0,lapcqonly,unpk77_success
logical single_decode,lagain
2020-10-09 13:16:25 -04:00
complex, allocatable :: c00(:) !Analytic signal, 6000 Sa/s
complex, allocatable :: c0(:) !Analytic signal, 6000 Sa/s
! Start by setting some parameters and allocating storage for large arrays
call sec0(0,tdecode)
nfa=nfa0
nfb=nfb0
nqd=nqd0
lnewdat=lnewdat0
max_drift=max_drift0
idec=-1
idf=0
idt=0
nrc=-2
2021-01-13 11:21:59 -05:00
mode_q65=2**nsubmode
npts=ntrperiod*12000
nfft1=ntrperiod*12000
nfft2=ntrperiod*6000
2021-05-20 12:57:41 -04:00
npasses=1
! Determine the T/R sequence: iseq=0 (even), or iseq=1 (odd)
n=nutc
2021-03-05 13:28:14 -05:00
if(ntrperiod.ge.60 .and. nutc.le.2359) n=100*n
write(cutc,'(i6.6)') n
read(cutc,'(3i2)') ih,im,is
nsec=3600*ih + 60*im + is
iseq=mod(nsec/ntrperiod,2)
if(lclearave) call q65_clravg
allocate(dd(npts))
allocate (c00(0:nfft1-1))
allocate (c0(0:nfft1-1))
if(lagain) then
call q65_hist(nfqso,dxcall=hiscall,dxgrid=hisgrid)
endif
nsps=1800
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
2020-10-09 14:12:34 -04:00
endif
2020-10-09 14:12:34 -04:00
baud=12000.0/nsps
this%callback => callback
2020-10-09 14:12:34 -04:00
nFadingModel=1
maxiters=33
ibwa=max(1,int(1.8*log(baud*mode_q65)) + 1)
ibwb=min(10,ibwa+2)
if(iand(ndepth,3).ge.2) then
ibwa=max(1,int(1.8*log(baud*mode_q65)) + 1)
ibwb=min(10,ibwa+5)
maxiters=67
else if(iand(ndepth,3).eq.3) then
ibwa=max(1,ibwa-1)
ibwb=min(10,ibwb+1)
maxiters=100
endif
! Generate codewords for full-AP list decoding
if(ichar(hiscall(1:1)).eq.0) hiscall=' '
if(ichar(hisgrid(1:1)).eq.0) hisgrid=' '
ncw=0
if(nqd.eq.1 .or. lagain) then
call q65_set_list(mycall,hiscall,hisgrid,codewords,ncw)
endif
2020-11-30 09:52:47 -05:00
dgen=0
call q65_enc(dgen,codewords) !Initialize the Q65 codec
nused=1
iavg=0
2021-01-13 14:34:20 -05:00
call timer('q65_dec0',0)
! Call top-level routine in q65 module: establish sync and try for a q3 decode.
call q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, &
emedelay,xdt,f0,snr1,width,dat4,snr2,idec)
2021-01-13 14:34:20 -05:00
call timer('q65_dec0',1)
if(idec.ge.0) then
dtdec=xdt !We have a list-decode result at nfqso
f0dec=f0
go to 100
2020-11-30 09:52:47 -05:00
endif
! Prepare for a single-period decode with iaptype = 0, 1, 2, or 4
jpk0=(xdt+1.0)*6000 !Index of nominal start of signal
if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !For shortest sequences
if(jpk0.lt.0) jpk0=0
call ana64(iwave,npts,c00) !Convert to complex c00() at 6000 Sa/s
call ft8apset(mycall,hiscall,ncontest,apsym0,aph10) ! Generate ap symbols
where(apsym0.eq.-1) apsym0=0
npasses=2
if(nQSOprogress.eq.5) npasses=3
if(lapcqonly) npasses=1
iaptype=0
do ipass=0,npasses !Loop over AP passes
apmask=0 !Try first with no AP information
apsymbols=0
if(ipass.ge.1) then
! Subsequent passes use AP information appropiate for nQSOprogress
call q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,iaptype, &
apsym0,apmask1,apsymbols1)
write(c78,1050) apmask1
1050 format(78i1)
read(c78,1060) apmask
1060 format(13b6.6)
write(c78,1050) apsymbols1
read(c78,1060) apsymbols
endif
call timer('q65loops',0)
2021-01-13 11:21:59 -05:00
call q65_loops(c00,npts/2,nsps/2,nsubmode,ndepth,jpk0, &
xdt,f0,iaptype,xdt1,f1,snr2,dat4,idec)
call timer('q65loops',1)
if(idec.ge.0) then
dtdec=xdt1
f0dec=f1
go to 100 !Successful decode, we're done
endif
enddo ! ipass
if(iand(ndepth,16).eq.0 .or. navg(iseq).lt.2) go to 100
2021-01-14 15:19:33 -05:00
! There was no single-transmission decode. Try for an average 'q3n' decode.
50 call timer('list_avg',0)
! Call top-level routine in q65 module: establish sync and try for a q3
! decode, this time using the cumulative 's1a' symbol spectra.
iavg=1
call q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, &
emedelay,xdt,f0,snr1,width,dat4,snr2,idec)
call timer('list_avg',1)
if(idec.ge.0) then
dtdec=xdt !We have a list-decode result from averaged data
f0dec=f0
nused=navg(iseq)
go to 100
endif
2021-01-14 15:19:33 -05:00
! There was no 'q3n' decode. Try for a 'q[0124]n' decode.
! Call top-level routine in q65 module: establish sync and try for a q[012]n
! decode, this time using the cumulative 's1a' symbol spectra.
call timer('q65_avg ',0)
iavg=2
call q65_dec0(iavg,nutc,iwave,ntrperiod,nfqso,ntol,ndepth,lclearave, &
emedelay,xdt,f0,snr1,width,dat4,snr2,idec)
call timer('q65_avg ',1)
if(idec.ge.0) then
dtdec=xdt !We have a q[012]n result
f0dec=f0
nused=navg(iseq)
endif
100 decoded=' '
if(idec.ge.0) then
! idec Meaning
! ------------------------------------------------------
! -1: No decode
! 0: Decode without AP information
! 1: Decode with AP for "CQ ? ?"
! 2: Decode with AP for "MyCall ? ?"
! 3: Decode with AP for "MyCall DxCall ?"
! Unpack decoded message for display to user
write(c77,1000) dat4(1:12),dat4(13)/2
1000 format(12b6.6,b5.5)
call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent
call q65_snr(dat4,dtdec,f0dec,mode_q65,nused,snr2)
2020-08-01 09:24:59 -04:00
nsnr=nint(snr2)
call this%callback(nutc,snr1,nsnr,dtdec,f0dec,decoded, &
idec,nused,ntrperiod)
call q65_hist(nint(f0dec),msg0=decoded)
if(iand(ndepth,128).ne.0 .and. .not.lagain .and. &
int(abs(f0dec-nfqso)).le.ntol ) call q65_clravg !AutoClrAvg
call sec0(1,tdecode)
open(22,file=trim(data_dir)//'/q65_decodes.dat',status='unknown', &
position='append',iostat=ios)
if(ios.eq.0) then
! Save decoding parameters to q65_decoded.dat, for later analysis.
write(cmode,'(i3)') ntrperiod
cmode(4:4)=char(ichar('A')+nsubmode)
c6=hiscall(1:6)
if(c6.eq.' ') c6='<b> '
c4=hisgrid(1:4)
if(c4.eq.' ') c4='<b> '
fmt='(i6.4,1x,a4,4i2,6i3,i4,f6.2,f7.1,f6.1,f7.1,f6.2,'// &
'1x,a6,1x,a6,1x,a4,1x,a)'
if(ntrperiod.le.30) fmt(5:5)='6'
if(idec.eq.3) nrc=0
write(22,fmt) nutc,cmode,nQSOprogress,idec,idfbest,idtbest,ibw, &
ndistbest,nused,icand,ncand,nrc,ndepth,xdt,f0,snr2,plog, &
tdecode,mycall(1:6),c6,c4,trim(decoded)
close(22)
endif
2020-08-01 09:24:59 -04:00
endif
navg0=1000*navg(0) + navg(1)
if(single_decode .or. lagain) go to 900
do icand=1,ncand
! Prepare for single-period candidate decodes with iaptype = 0, 1, 2, or 4
snr1=candidates(icand,1)
xdt= candidates(icand,2)
f0 = candidates(icand,3)
jpk0=(xdt+1.0)*6000 !Index of nominal start of signal
if(ntrperiod.le.30) jpk0=(xdt+0.5)*6000 !For shortest sequences
if(jpk0.lt.0) jpk0=0
call ana64(iwave,npts,c00) !Convert to complex c00() at 6000 Sa/s
call ft8apset(mycall,hiscall,ncontest,apsym0,aph10) ! Generate ap symbols
where(apsym0.eq.-1) apsym0=0
npasses=2
if(nQSOprogress.eq.5) npasses=3
if(lapcqonly) npasses=1
iaptype=0
do ipass=0,npasses !Loop over AP passes
apmask=0 !Try first with no AP information
apsymbols=0
if(ipass.ge.1) then
! Subsequent passes use AP information appropiate for nQSOprogress
call q65_ap(nQSOprogress,ipass,ncontest,lapcqonly,iaptype, &
apsym0,apmask1,apsymbols1)
write(c78,1050) apmask1
read(c78,1060) apmask
write(c78,1050) apsymbols1
read(c78,1060) apsymbols
endif
call timer('q65loops',0)
call q65_loops(c00,npts/2,nsps/2,nsubmode,ndepth,jpk0, &
xdt,f0,iaptype,xdt1,f1,snr2,dat4,idec)
call timer('q65loops',1)
if(idec.ge.0) then
dtdec=xdt1
f0dec=f1
go to 200 !Successful decode, we're done
endif
enddo ! ipass
200 decoded=' '
if(idec.ge.0) then
! Unpack decoded message for display to user
write(c77,1000) dat4(1:12),dat4(13)/2
call unpack77(c77,0,decoded,unpk77_success) !Unpack to get msgsent
call q65_snr(dat4,dtdec,f0dec,mode_q65,nused,snr2)
nsnr=nint(snr2)
call this%callback(nutc,snr1,nsnr,dtdec,f0dec,decoded, &
idec,nused,ntrperiod)
call q65_hist(nint(f0dec),msg0=decoded)
if(iand(ndepth,128).ne.0 .and. .not.lagain .and. &
int(abs(f0dec-nfqso)).le.ntol ) call q65_clravg !AutoClrAvg
call sec0(1,tdecode)
open(22,file=trim(data_dir)//'/q65_decodes.dat',status='unknown', &
position='append',iostat=ios)
if(ios.eq.0) then
! Save decoding parameters to q65_decoded.dat, for later analysis.
write(cmode,'(i3)') ntrperiod
cmode(4:4)=char(ichar('A')+nsubmode)
c6=hiscall(1:6)
if(c6.eq.' ') c6='<b> '
c4=hisgrid(1:4)
if(c4.eq.' ') c4='<b> '
fmt='(i6.4,1x,a4,4i2,6i3,i4,f6.2,f7.1,f6.1,f7.1,f6.2,'// &
'1x,a6,1x,a6,1x,a4,1x,a)'
if(ntrperiod.le.30) fmt(5:5)='6'
if(idec.eq.3) nrc=0
write(22,fmt) nutc,cmode,nQSOprogress,idec,idfbest,idtbest,ibw, &
ndistbest,nused,icand,ncand,nrc,ndepth,xdt,f0,snr2,plog, &
tdecode,mycall(1:6),c6,c4,trim(decoded)
close(22)
endif
endif
enddo ! icand
if(iavg.eq.0 .and.navg(iseq).ge.2 .and. iand(ndepth,16).ne.0) go to 50
900 return
2020-08-01 09:24:59 -04:00
end subroutine decode
end module q65_decode