From f45c6174226032596d7e4e3a3893bae30f64c3e8 Mon Sep 17 00:00:00 2001 From: Joe Taylor <joe@princeton.edu> Date: Sat, 1 Aug 2020 09:24:59 -0400 Subject: [PATCH] First working QRA66 decoder. --- CMakeLists.txt | 1 + lib/qra/qra66/qra66sim.f90 | 10 ++- lib/qra66_decode.f90 | 157 +++++++++++++++++++++++++++++++++++++ lib/spec66.f90 | 44 +++++++++++ lib/sync66.f90 | 60 ++++++++++++++ 5 files changed, 269 insertions(+), 3 deletions(-) create mode 100644 lib/qra66_decode.f90 create mode 100644 lib/spec66.f90 create mode 100644 lib/sync66.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9726caee0..49cb6ad57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -565,6 +565,7 @@ set (wsjt_FSRCS lib/softsym9w.f90 lib/shell.f90 lib/spec64.f90 + lib/spec66.f90 lib/spec9f.f90 lib/stdmsg.f90 lib/subtract65.f90 diff --git a/lib/qra/qra66/qra66sim.f90 b/lib/qra/qra66/qra66sim.f90 index 934239189..a482e4cc3 100644 --- a/lib/qra/qra66/qra66sim.f90 +++ b/lib/qra/qra66/qra66sim.f90 @@ -47,12 +47,17 @@ program qra66sim nsps=960 nsym=169 endif + + ichk=66 !Flag sent to genqra64 + call genqra64(msg,ichk,msgsent,itone,itype) + write(*,1001) itone +1001 format('Channel symbols:'/(20i3)) + baud=12000.d0/nsps !Keying rate = 6.25 baud h=default_header(12000,npts) - ichk=66 !Flag sent to genqra64 write(*,1000) -1000 format('File Freq A|B S/N DT Dop Message'/60('-')) +1000 format('File Freq A|B S/N DT Dop Message'/60('-')) do ifile=1,nfiles !Loop over requested number of files write(fname,1002) ifile !Output filename @@ -70,7 +75,6 @@ program qra66sim bandwidth_ratio=2500.0/6000.0 sig=sqrt(2*bandwidth_ratio)*10.0**(0.05*snrdb) if(snrdb.gt.90.0) sig=1.0 - call genqra64(msg,ichk,msgsent,itone,itype) write(*,1020) ifile,f0,csubmode,xsnr,xdt,fspread,msgsent 1020 format(i4,f10.3,2x,a1,2x,f5.1,f6.2,f6.1,1x,a22) phi=0.d0 diff --git a/lib/qra66_decode.f90 b/lib/qra66_decode.f90 new file mode 100644 index 000000000..53243e78c --- /dev/null +++ b/lib/qra66_decode.f90 @@ -0,0 +1,157 @@ +module qra66_decode + + type :: qra66_decoder + procedure(qra66_decode_callback), pointer :: callback + contains + procedure :: decode + end type qra66_decoder + + abstract interface + subroutine qra66_decode_callback (this,nutc,sync,nsnr,dt,freq, & + decoded,nap,qual,ntrperiod,fmid,w50) + import qra66_decoder + implicit none + class(qra66_decoder), intent(inout) :: this + integer, intent(in) :: nutc + real, intent(in) :: sync + integer, intent(in) :: nsnr + real, intent(in) :: dt + real, intent(in) :: freq + character(len=37), intent(in) :: decoded + integer, intent(in) :: nap + real, intent(in) :: qual + integer, intent(in) :: ntrperiod + real, intent(in) :: fmid + real, intent(in) :: w50 + end subroutine qra66_decode_callback + end interface + +contains + + subroutine decode(this,callback,iwave,nutc,nfa,nfb,nfqso,ndepth,lapdx, & + mycall,hiscall,hisgrid) + + use timer_module, only: timer + use packjt + use, intrinsic :: iso_c_binding + parameter (NFFT1=15*12000,NFFT2=15*6000) + class(qra66_decoder), intent(inout) :: this + procedure(qra66_decode_callback) :: callback + character(len=12) :: mycall, hiscall + character(len=6) :: hisgrid + character*37 decoded + integer*2 iwave(NFFT1) !Raw data + integer ipk(1) + integer dat4(12) + logical lapdx,ltext + complex c0(0:NFFT1-1) !Spectrum, then analytic signal + real s(900) + real s3a(-64:127,63) + real s3(-64:127,63) + real a(5) + data nc1z/-1/,nc2z/-1/,ng2z/-1/,maxaptypez/-1/ + save + + this%callback => callback + nsps=1920 + baud=12000.0/nsps + df1=12000.0/NFFT1 + + if(nutc.eq.-999) print*,mycall,hiscall,hisgrid,lapdx,ndepth,nfa,nfb,nfqso + +! Prime the QRA decoder for possible use of AP + call packcall(mycall(1:6),nc1,ltext) + call packcall(hiscall(1:6),nc2,ltext) + call packgrid(hisgrid(1:4),ng2,ltext) + nSubmode=0 + b90=1.0 + nFadingModel=1 + maxaptype=4 + if(iand(ndepth,64).ne.0) maxaptype=5 + if(nc1.ne.nc1z .or. nc2.ne.nc2z .or. ng2.ne.ng2z .or. & + maxaptype.ne.maxaptypez) then + do naptype=0,maxaptype + if(naptype.eq.2 .and. maxaptype.eq.4) cycle + call qra64_dec(s3,nc1,nc2,ng2,naptype,1,nSubmode,b90, & + nFadingModel,dat4,snr2,irc) + enddo + nc1z=nc1 + nc2z=nc2 + ng2z=ng2 + maxaptypez=maxaptype + endif + naptype=maxaptype + +! Compute the full-length spectrum + fac=2.0/NFFT1 + c0=fac*iwave + call four2a(c0,NFFT1,1,-1,1) !Forward c2c FFT + + nadd=101 + nh=nadd/2 + df2=nh*df1 + iz=3000.0/df2 + do i=1,iz !Compute smoothed spectrum + s(i)=0. + j0=nh*i + do j=j0-nh,j0+nh + s(i)=s(i) + real(c0(j))**2 + aimag(c0(j))**2 + enddo + enddo + call smo121(s,iz) + ipk=maxloc(s) + f0=df2*ipk(1) - 0.5*baud !Candidate sync frequency + +! do i=1,iz +! write(51,3051) i*df2,s(i) +!3051 format(f12.6,e12.3) +! enddo + + c0(NFFT2/2+1:NFFT2)=0. !Zero the top half + c0(0)=0.5*c0(0) + call four2a(c0,nfft2,1,1,1) !Inverse c2c FFT + call sync66(c0,f0,jpk,sync) !c0 is analytic signal at 6000 S/s + xdt=jpk/6000.0 - 0.5 + + a=0. + a(1)=-(f0 + 1.5*baud) + call twkfreq(c0,c0,85*NSPS,6000.0,a) + call spec66(c0(jpk:jpk+85*NSPS-1),s3a) + s3=s3a/maxval(s3a) +! do j=1,63 +! ipk=maxloc(s3(-64:127,j)) +! write(54,3054) j,ipk(1)-65 +!3054 format(2i8) +! do i=-64,127 +! write(53,3053) i,2*s3(i,j)+j-1 +!3053 format(i8,f12.6) +! enddo +! enddo + + call qra64_dec(s3a,nc1,nc2,ng2,naptype,0,nSubmode,b90, & + nFadingModel,dat4,snr2,irc) + if(irc.gt.0) call badmsg(irc,dat4,nc1,nc2,ng2) + + decoded=' ' + if(irc.ge.0) then + call unpackmsg(dat4,decoded) !Unpack the user message + call fmtmsg(decoded,iz) + if(index(decoded,"000AAA ").ge.1) then +! Suppress a certain type of garbage decode. + decoded=' ' + irc=-1 + endif + nft=100 + irc + nsnr=nint(snr2) + else + snr2=0. + call this%callback(nutc,sYNC,nsnr,xdt,fsig,decoded, & + iaptype,qual,ntrperiod,fmid,w50) + endif +! print*,'QRA66 decoder',nutc,jpk,xdt,f0,sync,snr2,irc,decoded + print*,decoded + + return + end subroutine decode + +end module qra66_decode diff --git a/lib/spec66.f90 b/lib/spec66.f90 new file mode 100644 index 000000000..9242f6a59 --- /dev/null +++ b/lib/spec66.f90 @@ -0,0 +1,44 @@ +subroutine spec66(c0,s3) + + parameter (LL=3*64) !Frequency channels + parameter (NN=63) !Data symbols + parameter (NSPS=960) !Samples per symbol at 6000 Hz + parameter (NMAX=85*NSPS) + complex c0(0:NMAX-1) !Synchrinized complex data + complex cs(0:NSPS-1) !Complex symbol spectrum + real s3(LL,NN) !Synchronized symbol spectra + real xbase0(LL),xbase(LL) + + fac=1.0/NSPS + ja=-NSPS + do j=1,NN + ja=ja+NSPS + if(mod(ja/NSPS,4).eq.0) ja=ja+NSPS + jb=ja+NSPS-1 + cs=fac*c0(ja:jb) + call four2a(cs,NSPS,1,-1,1) !c2c FFT to frequency + do ii=1,LL + i=ii-65 + if(i.lt.0) i=i+NSPS + s3(ii,j)=real(cs(i))**2 + aimag(cs(i))**2 + enddo + enddo + + df=6000.0/NSPS + do i=1,LL + call pctile(s3(i,1:NN),NN,45,xbase0(i)) !Get baseline for passband shape + enddo + + nh=9 + xbase(1:nh-1)=sum(xbase0(1:nh-1))/(nh-1.0) + xbase(LL-nh+1:LL)=sum(xbase0(LL-nh+1:LL))/(nh-1.0) + do i=nh,LL-nh + xbase(i)=sum(xbase0(i-nh+1:i+nh))/(2*nh+1) !Smoothed passband shape + enddo + + do i=1,LL + s3(i,1:NN)=s3(i,1:NN)/(xbase(i)+0.001) !Apply frequency equalization + enddo + + return +end subroutine spec66 diff --git a/lib/sync66.f90 b/lib/sync66.f90 new file mode 100644 index 000000000..bc8b41805 --- /dev/null +++ b/lib/sync66.f90 @@ -0,0 +1,60 @@ +subroutine sync66(c0,f0,jpk,sync) + +! Use the sync vector to find xdt (and improved f0 ?) + + PARAMETER (NMAX=15*6000) + parameter (NSPS=960) !Samples per symbol at 6000 Hz + complex c0(0:NMAX-1) !Complex data at 6000 S/s + complex csync0(0:NSPS-1) + complex csync1(0:NSPS-1) + complex z + integer b11(11) !Barker 11 code + data b11/1,1,1,0,0,0,1,0,0,1,0/ !Barker 11 definition + data mode66z/-1/ + save + + twopi=8.0*atan(1.0) + baud=6000.0/NSPS + dt=1.0/6000.0 + + dphi0=twopi*f0**dt + dphi1=twopi*(f0+baud)*dt + phi0=0. + phi1=0. + + do i=0,NSPS-1 !Compute csync for f0 and f0+baud + csync0(i)=cmplx(cos(phi0),sin(phi0)) + csync1(i)=cmplx(cos(phi1),sin(phi1)) + phi0=phi0 + dphi0 + phi1=phi1 + dphi1 + if(phi0.gt.twopi) phi0=phi0-twopi + if(phi1.gt.twopi) phi1=phi1-twopi + enddo + + sqmax=0. + jstep=NSPS/8 + do j0=0,6000,jstep + sq=0. + do k=1,22 + i=k + if(i.gt.11) i=i-11 + j1=j0 + 4*(k-1)*NSPS + if(b11(i).eq.0) then + z=dot_product(c0(j1:j1+NSPS-1),csync0) + else + z=dot_product(c0(j1:j1+NSPS-1),csync1) + endif + z=0.001*z + sq=sq + real(z)**2 + aimag(z)**2 + enddo + write(52,3052) j0/6000.0,sq,j0,j1 +3052 format(f10.6,f12.3,2i8) + if(sq.gt.smax) then + smax=sq + jpk=j0 + endif + enddo + sync=smax + + return +end subroutine sync66